Created
November 14, 2014 17:20
-
-
Save devinrsmith/627573047e4e2884836f to your computer and use it in GitHub Desktop.
FixedInput allows the same byte[] to be passed into Input.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.esotericsoftware.kryo.io.Input; | |
import java.io.InputStream; | |
/** | |
* Created by dsmith on 11/14/14. | |
* | |
* In reference to https://github.com/EsotericSoftware/kryo/issues/264 | |
*/ | |
public class FixedInput extends Input { | |
public FixedInput() { | |
} | |
public FixedInput(int bufferSize) { | |
super(bufferSize); | |
} | |
public FixedInput(byte[] buffer) { | |
super(buffer); | |
} | |
public FixedInput(byte[] buffer, int offset, int count) { | |
super(buffer, offset, count); | |
} | |
public FixedInput(InputStream inputStream) { | |
super(inputStream); | |
} | |
public FixedInput(InputStream inputStream, int bufferSize) { | |
super(inputStream, bufferSize); | |
} | |
@Override | |
public String readString() { | |
int available = require(1); | |
int b = buffer[position++]; | |
if ((b & 0x80) == 0) return fixedReadAscii(); // ASCII. | |
// Null, empty, or UTF8. | |
int charCount = available >= 5 ? copy_readUtf8Length(b) : copy_readUtf8Length_slow(b); | |
switch (charCount) { | |
case 0: | |
return null; | |
case 1: | |
return ""; | |
} | |
charCount--; | |
if (chars.length < charCount) chars = new char[charCount]; | |
copy_readUtf8(charCount); | |
return new String(chars, 0, charCount); | |
} | |
private String fixedReadAscii () { | |
byte[] buffer = this.buffer; | |
int end = position; | |
int start = end - 1; | |
int limit = this.limit; | |
int b; | |
do { | |
if (end == limit) return copy_readAscii_slow(); | |
b = buffer[end++]; | |
} while ((b & 0x80) == 0); | |
// if we were really cared about the extra copy here, we could do the String work of | |
// copying buffer into a char[] and call the package private constructor via reflection | |
final int L = end - start; | |
final byte[] asciiBuffer = new byte[L]; | |
System.arraycopy(buffer, start, asciiBuffer, 0, L); | |
asciiBuffer[L - 1] &= 0x7F; | |
String value = new String(asciiBuffer, 0, 0, L); | |
position = end; | |
return value; | |
} | |
// can't call private methods, so all the following copy_ methods are copied directly from Input | |
private String copy_readAscii_slow () { | |
position--; // Re-read the first byte. | |
// Copy chars currently in buffer. | |
int charCount = limit - position; | |
if (charCount > chars.length) chars = new char[charCount * 2]; | |
char[] chars = this.chars; | |
byte[] buffer = this.buffer; | |
for (int i = position, ii = 0, n = limit; i < n; i++, ii++) | |
chars[ii] = (char)buffer[i]; | |
position = limit; | |
// Copy additional chars one by one. | |
while (true) { | |
require(1); | |
int b = buffer[position++]; | |
if (charCount == chars.length) { | |
char[] newChars = new char[charCount * 2]; | |
System.arraycopy(chars, 0, newChars, 0, charCount); | |
chars = newChars; | |
this.chars = newChars; | |
} | |
if ((b & 0x80) == 0x80) { | |
chars[charCount++] = (char)(b & 0x7F); | |
break; | |
} | |
chars[charCount++] = (char)b; | |
} | |
return new String(chars, 0, charCount); | |
} | |
private int copy_readUtf8Length (int b) { | |
int result = b & 0x3F; // Mask all but first 6 bits. | |
if ((b & 0x40) != 0) { // Bit 7 means another byte, bit 8 means UTF8. | |
byte[] buffer = this.buffer; | |
b = buffer[position++]; | |
result |= (b & 0x7F) << 6; | |
if ((b & 0x80) != 0) { | |
b = buffer[position++]; | |
result |= (b & 0x7F) << 13; | |
if ((b & 0x80) != 0) { | |
b = buffer[position++]; | |
result |= (b & 0x7F) << 20; | |
if ((b & 0x80) != 0) { | |
b = buffer[position++]; | |
result |= (b & 0x7F) << 27; | |
} | |
} | |
} | |
} | |
return result; | |
} | |
private int copy_readUtf8Length_slow (int b) { | |
int result = b & 0x3F; // Mask all but first 6 bits. | |
if ((b & 0x40) != 0) { // Bit 7 means another byte, bit 8 means UTF8. | |
require(1); | |
byte[] buffer = this.buffer; | |
b = buffer[position++]; | |
result |= (b & 0x7F) << 6; | |
if ((b & 0x80) != 0) { | |
require(1); | |
b = buffer[position++]; | |
result |= (b & 0x7F) << 13; | |
if ((b & 0x80) != 0) { | |
require(1); | |
b = buffer[position++]; | |
result |= (b & 0x7F) << 20; | |
if ((b & 0x80) != 0) { | |
require(1); | |
b = buffer[position++]; | |
result |= (b & 0x7F) << 27; | |
} | |
} | |
} | |
} | |
return result; | |
} | |
private void copy_readUtf8 (int charCount) { | |
byte[] buffer = this.buffer; | |
char[] chars = this.chars; | |
// Try to read 7 bit ASCII chars. | |
int charIndex = 0; | |
int count = Math.min(require(1), charCount); | |
int position = this.position; | |
int b; | |
while (charIndex < count) { | |
b = buffer[position++]; | |
if (b < 0) { | |
position--; | |
break; | |
} | |
chars[charIndex++] = (char)b; | |
} | |
this.position = position; | |
// If buffer didn't hold all chars or any were not ASCII, use slow path for remainder. | |
if (charIndex < charCount) copy_readUtf8_slow(charCount, charIndex); | |
} | |
private void copy_readUtf8_slow (int charCount, int charIndex) { | |
char[] chars = this.chars; | |
byte[] buffer = this.buffer; | |
while (charIndex < charCount) { | |
if (position == limit) require(1); | |
int b = buffer[position++] & 0xFF; | |
switch (b >> 4) { | |
case 0: | |
case 1: | |
case 2: | |
case 3: | |
case 4: | |
case 5: | |
case 6: | |
case 7: | |
chars[charIndex] = (char)b; | |
break; | |
case 12: | |
case 13: | |
if (position == limit) require(1); | |
chars[charIndex] = (char)((b & 0x1F) << 6 | buffer[position++] & 0x3F); | |
break; | |
case 14: | |
require(2); | |
chars[charIndex] = (char)((b & 0x0F) << 12 | (buffer[position++] & 0x3F) << 6 | buffer[position++] & 0x3F); | |
break; | |
} | |
charIndex++; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment