prefer to use standard netty bytebuf.toString since now it use cache under the hood
[idea/community.git] / platform / platform-impl / src / org / jetbrains / io / MessageDecoder.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.io;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelHandlerContext;
20 import org.jetbrains.annotations.NotNull;
21 import org.jetbrains.annotations.Nullable;
22
23 import java.io.IOException;
24 import java.nio.CharBuffer;
25 import java.nio.charset.StandardCharsets;
26
27 public abstract class MessageDecoder extends Decoder {
28   protected int contentLength;
29   protected final StringBuilder builder = new StringBuilder(64);
30
31   private CharBuffer chunkedContent;
32   private int consumedContentByteCount = 0;
33
34   protected final int parseContentLength() {
35     return parseInt(builder, 0, false, 10);
36   }
37
38   @Nullable
39   protected final CharSequence readChars(@NotNull ByteBuf input) throws IOException {
40     int readableBytes = input.readableBytes();
41     if (readableBytes == 0) {
42       input.release();
43       return null;
44     }
45
46     int required = contentLength - consumedContentByteCount;
47     if (readableBytes < required) {
48       if (chunkedContent == null) {
49         chunkedContent = CharBuffer.allocate(contentLength);
50       }
51
52       ChannelBufferToStringKt.readIntoCharBuffer(input, readableBytes, chunkedContent);
53       consumedContentByteCount += readableBytes;
54       input.release();
55       return null;
56     }
57     else {
58       CharBuffer charBuffer = chunkedContent;
59       if (charBuffer == null) {
60         return input.toString(input.readerIndex(), required, StandardCharsets.UTF_8);
61       }
62
63       chunkedContent = null;
64       consumedContentByteCount = 0;
65       ChannelBufferToStringKt.readIntoCharBuffer(input, required, charBuffer);
66       return new CharSequenceBackedByChars(charBuffer);
67     }
68   }
69
70   @Override
71   public void channelInactive(ChannelHandlerContext context) throws Exception {
72     try {
73       chunkedContent = null;
74     }
75     finally {
76       super.channelInactive(context);
77     }
78   }
79
80   public static boolean readUntil(char what, @NotNull ByteBuf buffer, @NotNull StringBuilder builder) {
81     int i = buffer.readerIndex();
82     //noinspection ForLoopThatDoesntUseLoopVariable
83     for (int n = buffer.writerIndex(); i < n; i++) {
84       char c = (char)buffer.getByte(i);
85       if (c == what) {
86         buffer.readerIndex(i + 1);
87         return true;
88       }
89       else {
90         builder.append(c);
91       }
92     }
93     buffer.readerIndex(i);
94     return false;
95   }
96
97   public static void skipWhitespace(@NotNull ByteBuf buffer) {
98     int i = buffer.readerIndex();
99     int n = buffer.writerIndex();
100     for (; i < n; i++) {
101       char c = (char)buffer.getByte(i);
102       if (c != ' ') {
103         buffer.readerIndex(i);
104         return;
105       }
106     }
107     buffer.readerIndex(n);
108   }
109
110   /**
111    * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
112    * Copyright (C) 2006 - Javolution (http://javolution.org/)
113    * All rights reserved.
114    *
115    * Permission to use, copy, modify, and distribute this software is
116    * freely granted, provided that this notice is preserved.
117    */
118   public static int parseInt(@NotNull CharSequence value, int start, boolean isNegative, int radix) {
119     final int end = value.length();
120     int result = 0; // Accumulates negatively (avoid MIN_VALUE overflow).
121     int i = start;
122     for (; i < end; i++) {
123       char c = value.charAt(i);
124       int digit = (c <= '9') ? c - '0'
125                              : ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10
126                                                           : ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1;
127       if ((digit >= 0) && (digit < radix)) {
128         int newResult = result * radix - digit;
129         if (newResult > result) {
130           throw new NumberFormatException("Overflow parsing " + value.subSequence(start, end));
131         }
132         result = newResult;
133       }
134       else {
135         break;
136       }
137     }
138     // Requires one valid digit character and checks for opposite overflow.
139     if ((result == 0) && ((end == 0) || (value.charAt(i - 1) != '0'))) {
140       throw new NumberFormatException("Invalid integer representation for " + value.subSequence(start, end));
141     }
142     if ((result == Integer.MIN_VALUE) && !isNegative) {
143       throw new NumberFormatException("Overflow parsing " + value.subSequence(start, end));
144     }
145     return isNegative ? result : -result;
146   }
147 }