/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.io;
import com.intellij.util.io.CharSequenceBackedByChars;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
public abstract class MessageDecoder extends Decoder {
protected int contentLength;
protected final StringBuilder builder = new StringBuilder(64);
private CharBuffer chunkedContent;
private int consumedContentByteCount = 0;
protected final int parseContentLength() {
return parseInt(builder, 0, false, 10);
}
@Nullable
protected final CharSequence readChars(@NotNull ByteBuf input) throws IOException {
int readableBytes = input.readableBytes();
if (readableBytes == 0) {
input.release();
return null;
}
int required = contentLength - consumedContentByteCount;
if (readableBytes < required) {
if (chunkedContent == null) {
chunkedContent = CharBuffer.allocate(contentLength);
}
BufferToCharsKt.readIntoCharBuffer(input, readableBytes, chunkedContent);
consumedContentByteCount += readableBytes;
input.release();
return null;
}
else {
CharBuffer charBuffer = chunkedContent;
CharSequence result;
if (charBuffer != null) {
chunkedContent = null;
consumedContentByteCount = 0;
BufferToCharsKt.readIntoCharBuffer(input, required, charBuffer);
result = new CharSequenceBackedByChars(charBuffer);
}
else {
result = input.toString(input.readerIndex(), required, StandardCharsets.UTF_8);
}
input.readerIndex(input.readerIndex() + required);
return result;
}
}
@Override
public void channelInactive(ChannelHandlerContext context) throws Exception {
try {
chunkedContent = null;
}
finally {
super.channelInactive(context);
}
}
public static boolean readUntil(char what, @NotNull ByteBuf buffer, @NotNull StringBuilder builder) {
int i = buffer.readerIndex();
//noinspection ForLoopThatDoesntUseLoopVariable
for (int n = buffer.writerIndex(); i < n; i++) {
char c = (char)buffer.getByte(i);
if (c == what) {
buffer.readerIndex(i + 1);
return true;
}
else {
builder.append(c);
}
}
buffer.readerIndex(i);
return false;
}
public static void skipWhitespace(@NotNull ByteBuf buffer) {
int i = buffer.readerIndex();
int n = buffer.writerIndex();
for (; i < n; i++) {
char c = (char)buffer.getByte(i);
if (c != ' ') {
buffer.readerIndex(i);
return;
}
}
buffer.readerIndex(n);
}
/**
* Javolution - Java(TM) Solution for Real-Time and Embedded Systems
* Copyright (C) 2006 - Javolution (http://javolution.org/)
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software is
* freely granted, provided that this notice is preserved.
*/
public static int parseInt(@NotNull CharSequence value, int start, boolean isNegative, int radix) {
final int end = value.length();
int result = 0; // Accumulates negatively (avoid MIN_VALUE overflow).
int i = start;
for (; i < end; i++) {
char c = value.charAt(i);
int digit = (c <= '9') ? c - '0'
: ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10
: ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1;
if ((digit >= 0) && (digit < radix)) {
int newResult = result * radix - digit;
if (newResult > result) {
throw new NumberFormatException("Overflow parsing " + value.subSequence(start, end));
}
result = newResult;
}
else {
break;
}
}
// Requires one valid digit character and checks for opposite overflow.
if ((result == 0) && ((end == 0) || (value.charAt(i - 1) != '0'))) {
throw new NumberFormatException("Invalid integer representation for " + value.subSequence(start, end));
}
if ((result == Integer.MIN_VALUE) && !isNegative) {
throw new NumberFormatException("Overflow parsing " + value.subSequence(start, end));
}
return isNegative ? result : -result;
}
}