/* * 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; } }