/* * Исправление для работы kryo с Android Honeycomb * (реализация deprecated-метода String.getBytes(int, int, byte[], int) для копирования ASCII строк) * */ package nya.miku.wishmaster.lib; import java.io.OutputStream; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.io.Output; public class KryoOutputHC extends Output { public KryoOutputHC(OutputStream outputStream) { super(outputStream); } private static void getBytes(String s, int srcBegin, int srcEnd, byte dst[], int dstBegin) { byte[] bytes = s.getBytes(); if (srcBegin < 0) { throw new StringIndexOutOfBoundsException(srcBegin); } if (srcEnd > bytes.length) { throw new StringIndexOutOfBoundsException(srcEnd); } if (srcBegin > srcEnd) { throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); } int j = dstBegin; int n = srcEnd; int i = srcBegin; while (i < n) { dst[j++] = bytes[i++]; } } /** Writes the length and string, or null. Short strings are checked and if ASCII they are written more efficiently, else they * are written as UTF8. If a string is known to be ASCII, {@link #writeAscii(String)} may be used. The string can be read using * {@link Input#readString()} or {@link Input#readStringBuilder()}. * @param value May be null. */ public void writeString (String value) throws KryoException { if (value == null) { writeByte(0x80); // 0 means null, bit 8 means UTF8. return; } int charCount = value.length(); if (charCount == 0) { writeByte(1 | 0x80); // 1 means empty string, bit 8 means UTF8. return; } // Detect ASCII. boolean ascii = false; if (charCount > 1 && charCount < 64) { ascii = true; for (int i = 0; i < charCount; i++) { int c = value.charAt(i); if (c > 127) { ascii = false; break; } } } if (ascii) { if (capacity - position < charCount) writeAscii_slow(value, charCount); else { getBytes(value, 0, charCount, buffer, position); position += charCount; } buffer[position - 1] |= 0x80; } else { writeUtf8Length(charCount + 1); int charIndex = 0; if (capacity - position >= charCount) { // Try to write 8 bit chars. byte[] buffer = this.buffer; int position = this.position; for (; charIndex < charCount; charIndex++) { int c = value.charAt(charIndex); if (c > 127) break; buffer[position++] = (byte)c; } this.position = position; } if (charIndex < charCount) writeString_slow(value, charCount, charIndex); } } /** Writes the length and CharSequence as UTF8, or null. The string can be read using {@link Input#readString()} or * {@link Input#readStringBuilder()}. * @param value May be null. */ public void writeString (CharSequence value) throws KryoException { if (value == null) { writeByte(0x80); // 0 means null, bit 8 means UTF8. return; } int charCount = value.length(); if (charCount == 0) { writeByte(1 | 0x80); // 1 means empty string, bit 8 means UTF8. return; } writeUtf8Length(charCount + 1); int charIndex = 0; if (capacity - position >= charCount) { // Try to write 8 bit chars. byte[] buffer = this.buffer; int position = this.position; for (; charIndex < charCount; charIndex++) { int c = value.charAt(charIndex); if (c > 127) break; buffer[position++] = (byte)c; } this.position = position; } if (charIndex < charCount) writeString_slow(value, charCount, charIndex); } /** Writes a string that is known to contain only ASCII characters. Non-ASCII strings passed to this method will be corrupted. * Each byte is a 7 bit character with the remaining byte denoting if another character is available. This is slightly more * efficient than {@link #writeString(String)}. The string can be read using {@link Input#readString()} or * {@link Input#readStringBuilder()}. * @param value May be null. */ public void writeAscii (String value) throws KryoException { if (value == null) { writeByte(0x80); // 0 means null, bit 8 means UTF8. return; } int charCount = value.length(); switch (charCount) { case 0: writeByte(1 | 0x80); // 1 is string length + 1, bit 8 means UTF8. return; case 1: writeByte(2 | 0x80); // 2 is string length + 1, bit 8 means UTF8. writeByte(value.charAt(0)); return; } if (capacity - position < charCount) writeAscii_slow(value, charCount); else { getBytes(value, 0, charCount, buffer, position); position += charCount; } buffer[position - 1] |= 0x80; // Bit 8 means end of ASCII. } /** Writes the length of a string, which is a variable length encoded int except the first byte uses bit 8 to denote UTF8 and * bit 7 to denote if another byte is present. */ private void writeUtf8Length (int value) { if (value >>> 6 == 0) { require(1); buffer[position++] = (byte)(value | 0x80); // Set bit 8. } else if (value >>> 13 == 0) { require(2); byte[] buffer = this.buffer; buffer[position++] = (byte)(value | 0x40 | 0x80); // Set bit 7 and 8. buffer[position++] = (byte)(value >>> 6); } else if (value >>> 20 == 0) { require(3); byte[] buffer = this.buffer; buffer[position++] = (byte)(value | 0x40 | 0x80); // Set bit 7 and 8. buffer[position++] = (byte)((value >>> 6) | 0x80); // Set bit 8. buffer[position++] = (byte)(value >>> 13); } else if (value >>> 27 == 0) { require(4); byte[] buffer = this.buffer; buffer[position++] = (byte)(value | 0x40 | 0x80); // Set bit 7 and 8. buffer[position++] = (byte)((value >>> 6) | 0x80); // Set bit 8. buffer[position++] = (byte)((value >>> 13) | 0x80); // Set bit 8. buffer[position++] = (byte)(value >>> 20); } else { require(5); byte[] buffer = this.buffer; buffer[position++] = (byte)(value | 0x40 | 0x80); // Set bit 7 and 8. buffer[position++] = (byte)((value >>> 6) | 0x80); // Set bit 8. buffer[position++] = (byte)((value >>> 13) | 0x80); // Set bit 8. buffer[position++] = (byte)((value >>> 20) | 0x80); // Set bit 8. buffer[position++] = (byte)(value >>> 27); } } private void writeString_slow (CharSequence value, int charCount, int charIndex) { for (; charIndex < charCount; charIndex++) { if (position == capacity) require(Math.min(capacity, charCount - charIndex)); int c = value.charAt(charIndex); if (c <= 0x007F) { buffer[position++] = (byte)c; } else if (c > 0x07FF) { buffer[position++] = (byte)(0xE0 | c >> 12 & 0x0F); require(2); buffer[position++] = (byte)(0x80 | c >> 6 & 0x3F); buffer[position++] = (byte)(0x80 | c & 0x3F); } else { buffer[position++] = (byte)(0xC0 | c >> 6 & 0x1F); require(1); buffer[position++] = (byte)(0x80 | c & 0x3F); } } } private void writeAscii_slow (String value, int charCount) throws KryoException { byte[] buffer = this.buffer; int charIndex = 0; int charsToWrite = Math.min(charCount, capacity - position); while (charIndex < charCount) { getBytes(value, charIndex, charIndex + charsToWrite, buffer, position); charIndex += charsToWrite; position += charsToWrite; charsToWrite = Math.min(charCount - charIndex, capacity); if (require(charsToWrite)) buffer = this.buffer; } } }