client should release buffer as soon as possible
[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     int readableBytes = input.readableBytes();
29     if (readableBytes == 0) {
30       input.release();
31       return null;
32     }
33
34     int required = contentLength - consumedContentByteCount;
35     if (readableBytes < required) {
36       if (chunkedContent == null) {
37         chunkedContent = CharBuffer.allocate((int)((float)contentLength * charsetDecoder.maxCharsPerByte()));
38       }
39
40       ChannelBufferToString.readIntoCharBuffer(charsetDecoder, input, readableBytes, chunkedContent);
41       consumedContentByteCount += readableBytes;
42       input.release();
43       return null;
44     }
45     else {
46       CharBuffer charBuffer = chunkedContent;
47       if (charBuffer != null) {
48         chunkedContent = null;
49         consumedContentByteCount = 0;
50       }
51       return new ChannelBufferToString.MyCharArrayCharSequence(ChannelBufferToString.readIntoCharBuffer(charsetDecoder, input, required, charBuffer));
52     }
53   }
54
55   @Override
56   public void channelInactive(ChannelHandlerContext context) throws Exception {
57     try {
58       chunkedContent = null;
59     }
60     finally {
61       super.channelInactive(context);
62     }
63   }
64
65   public static boolean readUntil(char what, @NotNull ByteBuf buffer, @NotNull StringBuilder builder) {
66     int i = buffer.readerIndex();
67     //noinspection ForLoopThatDoesntUseLoopVariable
68     for (int n = buffer.writerIndex(); i < n; i++) {
69       char c = (char)buffer.getByte(i);
70       if (c == what) {
71         buffer.readerIndex(i + 1);
72         return true;
73       }
74       else {
75         builder.append(c);
76       }
77     }
78     buffer.readerIndex(i);
79     return false;
80   }
81
82   public static void skipWhitespace(@NotNull ByteBuf buffer) {
83     int i = buffer.readerIndex();
84     int n = buffer.writerIndex();
85     for (; i < n; i++) {
86       char c = (char)buffer.getByte(i);
87       if (c != ' ') {
88         buffer.readerIndex(i);
89         return;
90       }
91     }
92     buffer.readerIndex(n);
93   }
94
95   /**
96    * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
97    * Copyright (C) 2006 - Javolution (http://javolution.org/)
98    * All rights reserved.
99    *
100    * Permission to use, copy, modify, and distribute this software is
101    * freely granted, provided that this notice is preserved.
102    */
103   public static int parseInt(@NotNull CharSequence value, int start, boolean isNegative, int radix) {
104     final int end = value.length();
105     int result = 0; // Accumulates negatively (avoid MIN_VALUE overflow).
106     int i = start;
107     for (; i < end; i++) {
108       char c = value.charAt(i);
109       int digit = (c <= '9') ? c - '0'
110                              : ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10
111                                                           : ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1;
112       if ((digit >= 0) && (digit < radix)) {
113         int newResult = result * radix - digit;
114         if (newResult > result) {
115           throw new NumberFormatException("Overflow parsing " + value.subSequence(start, end));
116         }
117         result = newResult;
118       }
119       else {
120         break;
121       }
122     }
123     // Requires one valid digit character and checks for opposite overflow.
124     if ((result == 0) && ((end == 0) || (value.charAt(i - 1) != '0'))) {
125       throw new NumberFormatException("Invalid integer representation for " + value.subSequence(start, end));
126     }
127     if ((result == Integer.MIN_VALUE) && !isNegative) {
128       throw new NumberFormatException("Overflow parsing " + value.subSequence(start, end));
129     }
130     return isNegative ? result : -result;
131   }
132 }