152f62de93a5715608ec498268951aa91bdb0796
[idea/community.git] / platform / platform-impl / src / org / jetbrains / io / MessageDecoder.java
1 package org.jetbrains.io;
2
3 import io.netty.buffer.ByteBuf;
4 import io.netty.channel.ChannelHandlerContext;
5 import io.netty.util.CharsetUtil;
6 import org.jetbrains.annotations.NotNull;
7 import org.jetbrains.annotations.Nullable;
8
9 import java.nio.CharBuffer;
10 import java.nio.charset.CharacterCodingException;
11 import java.nio.charset.CharsetDecoder;
12
13 public abstract class MessageDecoder extends Decoder {
14   protected int contentLength;
15   protected final StringBuilder builder = new StringBuilder(64);
16
17   private CharBuffer chunkedContent;
18   private int consumedContentByteCount = 0;
19
20   private final CharsetDecoder charsetDecoder = CharsetUtil.getDecoder(CharsetUtil.UTF_8);
21
22   protected final int parseContentLength() {
23     return parseInt(builder, 0, false, 10);
24   }
25
26   @Nullable
27   protected final CharSequence readChars(@NotNull ByteBuf input) throws CharacterCodingException {
28     if (!input.isReadable()) {
29       return null;
30     }
31
32     int required = contentLength - consumedContentByteCount;
33     if (input.readableBytes() < required) {
34       if (chunkedContent == null) {
35         chunkedContent = CharBuffer.allocate((int)((float)contentLength * charsetDecoder.maxCharsPerByte()));
36       }
37
38       int count = input.readableBytes();
39       ChannelBufferToString.readIntoCharBuffer(charsetDecoder, input, count, chunkedContent);
40       consumedContentByteCount += count;
41       return null;
42     }
43     else {
44       CharBuffer charBuffer = chunkedContent;
45       if (charBuffer != null) {
46         chunkedContent = null;
47         consumedContentByteCount = 0;
48       }
49       return new ChannelBufferToString.MyCharArrayCharSequence(ChannelBufferToString.readIntoCharBuffer(charsetDecoder, input, required, charBuffer));
50     }
51   }
52
53   @Override
54   public void channelInactive(ChannelHandlerContext context) throws Exception {
55     try {
56       chunkedContent = null;
57     }
58     finally {
59       super.channelInactive(context);
60     }
61   }
62
63   public static boolean readUntil(char what, @NotNull ByteBuf buffer, @NotNull StringBuilder builder) {
64     int i = buffer.readerIndex();
65     //noinspection ForLoopThatDoesntUseLoopVariable
66     for (int n = buffer.writerIndex(); i < n; i++) {
67       char c = (char)buffer.getByte(i);
68       if (c == what) {
69         buffer.readerIndex(i + 1);
70         return true;
71       }
72       else {
73         builder.append(c);
74       }
75     }
76     buffer.readerIndex(i);
77     return false;
78   }
79
80   public static void skipWhitespace(@NotNull ByteBuf buffer) {
81     int i = buffer.readerIndex();
82     int n = buffer.writerIndex();
83     for (; i < n; i++) {
84       char c = (char)buffer.getByte(i);
85       if (c != ' ') {
86         buffer.readerIndex(i);
87         return;
88       }
89     }
90     buffer.readerIndex(n);
91   }
92
93   /**
94    * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
95    * Copyright (C) 2006 - Javolution (http://javolution.org/)
96    * All rights reserved.
97    *
98    * Permission to use, copy, modify, and distribute this software is
99    * freely granted, provided that this notice is preserved.
100    */
101   public static int parseInt(@NotNull CharSequence value, int start, boolean isNegative, int radix) {
102     final int end = value.length();
103     int result = 0; // Accumulates negatively (avoid MIN_VALUE overflow).
104     int i = start;
105     for (; i < end; i++) {
106       char c = value.charAt(i);
107       int digit = (c <= '9') ? c - '0'
108                              : ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10
109                                                           : ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1;
110       if ((digit >= 0) && (digit < radix)) {
111         int newResult = result * radix - digit;
112         if (newResult > result) {
113           throw new NumberFormatException("Overflow parsing " + value.subSequence(start, end));
114         }
115         result = newResult;
116       }
117       else {
118         break;
119       }
120     }
121     // Requires one valid digit character and checks for opposite overflow.
122     if ((result == 0) && ((end == 0) || (value.charAt(i - 1) != '0'))) {
123       throw new NumberFormatException("Invalid integer representation for " + value.subSequence(start, end));
124     }
125     if ((result == Integer.MIN_VALUE) && !isNegative) {
126       throw new NumberFormatException("Overflow parsing " + value.subSequence(start, end));
127     }
128     return isNegative ? result : -result;
129   }
130 }