1 /* Copyright (c) 2008, Nathan Sweet
4 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
7 * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8 * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
9 * disclaimer in the documentation and/or other materials provided with the distribution.
10 * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived
11 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
14 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
15 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
17 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
18 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
20 package io.atomix.utils.serializer;
22 import java.nio.ByteBuffer;
25 * A Kryo-4.0.3 ByteBufferInput adapted to deal with
26 * <a href="https://github.com/EsotericSoftware/kryo/issues/505">issue 505</a>.
28 * @author Roman Levenstein <romixlev@gmail.com>
29 * @author Robert Varga
31 public class ByteBufferInput extends com.esotericsoftware.kryo.io.ByteBufferInput {
32 public ByteBufferInput (ByteBuffer buffer) {
37 public String readString () {
38 niobuffer.position(position);
39 int available = require(1);
41 int b = niobuffer.get();
42 if ((b & 0x80) == 0) return readAscii(); // ASCII.
43 // Null, empty, or UTF8.
44 int charCount = available >= 5 ? readUtf8Length(b) : readUtf8Length_slow(b);
52 if (chars.length < charCount) chars = new char[charCount];
54 return new String(chars, 0, charCount);
57 private int readUtf8Length (int b) {
58 int result = b & 0x3F; // Mask all but first 6 bits.
59 if ((b & 0x40) != 0) { // Bit 7 means another byte, bit 8 means UTF8.
62 result |= (b & 0x7F) << 6;
63 if ((b & 0x80) != 0) {
66 result |= (b & 0x7F) << 13;
67 if ((b & 0x80) != 0) {
70 result |= (b & 0x7F) << 20;
71 if ((b & 0x80) != 0) {
74 result |= (b & 0x7F) << 27;
82 private int readUtf8Length_slow (int b) {
83 int result = b & 0x3F; // Mask all but first 6 bits.
84 if ((b & 0x40) != 0) { // Bit 7 means another byte, bit 8 means UTF8.
88 result |= (b & 0x7F) << 6;
89 if ((b & 0x80) != 0) {
93 result |= (b & 0x7F) << 13;
94 if ((b & 0x80) != 0) {
98 result |= (b & 0x7F) << 20;
99 if ((b & 0x80) != 0) {
103 result |= (b & 0x7F) << 27;
111 private void readUtf8 (int charCount) {
112 char[] chars = this.chars;
113 // Try to read 7 bit ASCII chars.
115 int count = Math.min(require(1), charCount);
116 int position = this.position;
118 while (charIndex < count) {
125 chars[charIndex++] = (char)b;
127 this.position = position;
128 // If buffer didn't hold all chars or any were not ASCII, use slow path for remainder.
129 if (charIndex < charCount) {
130 niobuffer.position(position);
131 readUtf8_slow(charCount, charIndex);
135 private void readUtf8_slow (int charCount, int charIndex) {
136 char[] chars = this.chars;
137 while (charIndex < charCount) {
138 if (position == limit) require(1);
140 int b = niobuffer.get() & 0xFF;
150 chars[charIndex] = (char)b;
154 if (position == limit) require(1);
156 chars[charIndex] = (char)((b & 0x1F) << 6 | niobuffer.get() & 0x3F);
161 int b2 = niobuffer.get();
162 int b3 = niobuffer.get();
163 chars[charIndex] = (char)((b & 0x0F) << 12 | (b2 & 0x3F) << 6 | b3 & 0x3F);
170 private String readAscii () {
173 int limit = this.limit;
176 if (end == limit) return readAscii_slow();
179 } while ((b & 0x80) == 0);
180 int count = end - start;
181 byte[] tmp = new byte[count];
182 niobuffer.position(start);
184 tmp[count - 1] &= 0x7F; // Mask end of ascii bit.
185 String value = new String(tmp, 0, 0, count);
187 niobuffer.position(position);
191 private String readAscii_slow () {
192 position--; // Re-read the first byte.
193 // Copy chars currently in buffer.
194 int charCount = limit - position;
195 if (charCount > chars.length) chars = new char[charCount * 2];
196 char[] chars = this.chars;
197 for (int i = position, ii = 0, n = limit; i < n; i++, ii++)
198 chars[ii] = (char)niobuffer.get(i);
200 // Copy additional chars one by one.
204 int b = niobuffer.get();
205 if (charCount == chars.length) {
206 char[] newChars = new char[charCount * 2];
207 System.arraycopy(chars, 0, newChars, 0, charCount);
209 this.chars = newChars;
211 if ((b & 0x80) == 0x80) {
212 chars[charCount++] = (char)(b & 0x7F);
215 chars[charCount++] = (char)b;
217 return new String(chars, 0, charCount);
221 public StringBuilder readStringBuilder () {
222 niobuffer.position(position);
223 int available = require(1);
225 int b = niobuffer.get();
226 if ((b & 0x80) == 0) return new StringBuilder(readAscii()); // ASCII.
227 // Null, empty, or UTF8.
228 int charCount = available >= 5 ? readUtf8Length(b) : readUtf8Length_slow(b);
233 return new StringBuilder("");
236 if (chars.length < charCount) chars = new char[charCount];
238 StringBuilder builder = new StringBuilder(charCount);
239 builder.append(chars, 0, charCount);