|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
// (c) 2018 and later: Unicode, Inc. and others. |
|
// License & terms of use: http://www.unicode.org/copyright.html#License |
|
|
|
// created: 2018may04 Markus W. Scherer |
|
|
|
package jdk.internal.icu.util; |
|
|
|
import jdk.internal.icu.impl.ICUBinary; |
|
|
|
import java.io.DataOutputStream; |
|
import java.io.IOException; |
|
import java.io.UncheckedIOException; |
|
import java.io.OutputStream; |
|
import java.nio.ByteBuffer; |
|
import java.nio.ByteOrder; |
|
|
|
import static jdk.internal.icu.impl.NormalizerImpl.UTF16Plus; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("deprecation") |
|
public abstract class CodePointTrie extends CodePointMap { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public enum Type { |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
FAST, |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
SMALL |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public enum ValueWidth { |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
BITS_16, |
|
|
|
|
|
|
|
|
|
*/ |
|
BITS_32, |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
BITS_8 |
|
} |
|
|
|
private CodePointTrie(char[] index, Data data, int highStart, |
|
int index3NullOffset, int dataNullOffset) { |
|
this.ascii = new int[ASCII_LIMIT]; |
|
this.index = index; |
|
this.data = data; |
|
this.dataLength = data.getDataLength(); |
|
this.highStart = highStart; |
|
this.index3NullOffset = index3NullOffset; |
|
this.dataNullOffset = dataNullOffset; |
|
|
|
for (int c = 0; c < ASCII_LIMIT; ++c) { |
|
ascii[c] = data.getFromIndex(c); |
|
} |
|
|
|
int nullValueOffset = dataNullOffset; |
|
if (nullValueOffset >= dataLength) { |
|
nullValueOffset = dataLength - HIGH_VALUE_NEG_DATA_OFFSET; |
|
} |
|
nullValue = data.getFromIndex(nullValueOffset); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static CodePointTrie fromBinary(Type type, ValueWidth valueWidth, ByteBuffer bytes) { |
|
ByteOrder outerByteOrder = bytes.order(); |
|
try { |
|
|
|
if (bytes.remaining() < 16 ) { |
|
throw new InternalError("Buffer too short for a CodePointTrie header"); |
|
} |
|
|
|
// struct UCPTrieHeader |
|
|
|
int signature = bytes.getInt(); |
|
|
|
|
|
switch (signature) { |
|
case 0x54726933: |
|
|
|
break; |
|
case 0x33697254: |
|
|
|
boolean isBigEndian = outerByteOrder == ByteOrder.BIG_ENDIAN; |
|
bytes.order(isBigEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); |
|
signature = 0x54726933; |
|
break; |
|
default: |
|
throw new InternalError("Buffer does not contain a serialized CodePointTrie"); |
|
} |
|
|
|
// struct UCPTrieHeader continued |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
int options = bytes.getChar(); |
|
|
|
|
|
int indexLength = bytes.getChar(); |
|
|
|
|
|
int dataLength = bytes.getChar(); |
|
|
|
|
|
int index3NullOffset = bytes.getChar(); |
|
|
|
|
|
int dataNullOffset = bytes.getChar(); |
|
|
|
|
|
|
|
|
|
*/ |
|
int shiftedHighStart = bytes.getChar(); |
|
// struct UCPTrieHeader end |
|
|
|
int typeInt = (options >> 6) & 3; |
|
Type actualType; |
|
switch (typeInt) { |
|
case 0: actualType = Type.FAST; break; |
|
case 1: actualType = Type.SMALL; break; |
|
default: |
|
throw new InternalError("CodePointTrie data header has an unsupported type"); |
|
} |
|
|
|
int valueWidthInt = options & OPTIONS_VALUE_BITS_MASK; |
|
ValueWidth actualValueWidth; |
|
switch (valueWidthInt) { |
|
case 0: actualValueWidth = ValueWidth.BITS_16; break; |
|
case 1: actualValueWidth = ValueWidth.BITS_32; break; |
|
case 2: actualValueWidth = ValueWidth.BITS_8; break; |
|
default: |
|
throw new InternalError("CodePointTrie data header has an unsupported value width"); |
|
} |
|
|
|
if ((options & OPTIONS_RESERVED_MASK) != 0) { |
|
throw new InternalError("CodePointTrie data header has unsupported options"); |
|
} |
|
|
|
if (type == null) { |
|
type = actualType; |
|
} |
|
if (valueWidth == null) { |
|
valueWidth = actualValueWidth; |
|
} |
|
if (type != actualType || valueWidth != actualValueWidth) { |
|
throw new InternalError("CodePointTrie data header has a different type or value width than required"); |
|
} |
|
|
|
|
|
dataLength |= ((options & OPTIONS_DATA_LENGTH_MASK) << 4); |
|
dataNullOffset |= ((options & OPTIONS_DATA_NULL_OFFSET_MASK) << 8); |
|
|
|
int highStart = shiftedHighStart << SHIFT_2; |
|
|
|
|
|
int actualLength = indexLength * 2; |
|
if (valueWidth == ValueWidth.BITS_16) { |
|
actualLength += dataLength * 2; |
|
} else if (valueWidth == ValueWidth.BITS_32) { |
|
actualLength += dataLength * 4; |
|
} else { |
|
actualLength += dataLength; |
|
} |
|
if (bytes.remaining() < actualLength) { |
|
throw new InternalError("Buffer too short for the CodePointTrie data"); |
|
} |
|
|
|
char[] index = ICUBinary.getChars(bytes, indexLength, 0); |
|
switch (valueWidth) { |
|
case BITS_16: { |
|
char[] data16 = ICUBinary.getChars(bytes, dataLength, 0); |
|
return type == Type.FAST ? |
|
new Fast16(index, data16, highStart, index3NullOffset, dataNullOffset) : |
|
new Small16(index, data16, highStart, index3NullOffset, dataNullOffset); |
|
} |
|
case BITS_32: { |
|
int[] data32 = ICUBinary.getInts(bytes, dataLength, 0); |
|
return type == Type.FAST ? |
|
new Fast32(index, data32, highStart, index3NullOffset, dataNullOffset) : |
|
new Small32(index, data32, highStart, index3NullOffset, dataNullOffset); |
|
} |
|
case BITS_8: { |
|
byte[] data8 = ICUBinary.getBytes(bytes, dataLength, 0); |
|
return type == Type.FAST ? |
|
new Fast8(index, data8, highStart, index3NullOffset, dataNullOffset) : |
|
new Small8(index, data8, highStart, index3NullOffset, dataNullOffset); |
|
} |
|
default: |
|
throw new AssertionError("should be unreachable"); |
|
} |
|
} finally { |
|
bytes.order(outerByteOrder); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public abstract Type getType(); |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final ValueWidth getValueWidth() { return data.getValueWidth(); } |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public int get(int c) { |
|
return data.getFromIndex(cpIndex(c)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final int asciiGet(int c) { |
|
return ascii[c]; |
|
} |
|
|
|
private static final int MAX_UNICODE = 0x10ffff; |
|
|
|
private static final int ASCII_LIMIT = 0x80; |
|
|
|
private static final int maybeFilterValue(int value, int trieNullValue, int nullValue, |
|
ValueFilter filter) { |
|
if (value == trieNullValue) { |
|
value = nullValue; |
|
} else if (filter != null) { |
|
value = filter.apply(value); |
|
} |
|
return value; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final boolean getRange(int start, ValueFilter filter, Range range) { |
|
if (start < 0 || MAX_UNICODE < start) { |
|
return false; |
|
} |
|
if (start >= highStart) { |
|
int di = dataLength - HIGH_VALUE_NEG_DATA_OFFSET; |
|
int value = data.getFromIndex(di); |
|
if (filter != null) { value = filter.apply(value); } |
|
range.set(start, MAX_UNICODE, value); |
|
return true; |
|
} |
|
|
|
int nullValue = this.nullValue; |
|
if (filter != null) { nullValue = filter.apply(nullValue); } |
|
Type type = getType(); |
|
|
|
int prevI3Block = -1; |
|
int prevBlock = -1; |
|
int c = start; |
|
|
|
int trieValue = 0, value = 0; |
|
boolean haveValue = false; |
|
do { |
|
int i3Block; |
|
int i3; |
|
int i3BlockLength; |
|
int dataBlockLength; |
|
if (c <= 0xffff && (type == Type.FAST || c <= SMALL_MAX)) { |
|
i3Block = 0; |
|
i3 = c >> FAST_SHIFT; |
|
i3BlockLength = type == Type.FAST ? BMP_INDEX_LENGTH : SMALL_INDEX_LENGTH; |
|
dataBlockLength = FAST_DATA_BLOCK_LENGTH; |
|
} else { |
|
|
|
int i1 = c >> SHIFT_1; |
|
if (type == Type.FAST) { |
|
assert(0xffff < c && c < highStart); |
|
i1 += BMP_INDEX_LENGTH - OMITTED_BMP_INDEX_1_LENGTH; |
|
} else { |
|
assert(c < highStart && highStart > SMALL_LIMIT); |
|
i1 += SMALL_INDEX_LENGTH; |
|
} |
|
i3Block = index[index[i1] + ((c >> SHIFT_2) & INDEX_2_MASK)]; |
|
if (i3Block == prevI3Block && (c - start) >= CP_PER_INDEX_2_ENTRY) { |
|
|
|
assert((c & (CP_PER_INDEX_2_ENTRY - 1)) == 0); |
|
c += CP_PER_INDEX_2_ENTRY; |
|
continue; |
|
} |
|
prevI3Block = i3Block; |
|
if (i3Block == index3NullOffset) { |
|
|
|
if (haveValue) { |
|
if (nullValue != value) { |
|
range.set(start, c - 1, value); |
|
return true; |
|
} |
|
} else { |
|
trieValue = this.nullValue; |
|
value = nullValue; |
|
haveValue = true; |
|
} |
|
prevBlock = dataNullOffset; |
|
c = (c + CP_PER_INDEX_2_ENTRY) & ~(CP_PER_INDEX_2_ENTRY - 1); |
|
continue; |
|
} |
|
i3 = (c >> SHIFT_3) & INDEX_3_MASK; |
|
i3BlockLength = INDEX_3_BLOCK_LENGTH; |
|
dataBlockLength = SMALL_DATA_BLOCK_LENGTH; |
|
} |
|
|
|
do { |
|
int block; |
|
if ((i3Block & 0x8000) == 0) { |
|
block = index[i3Block + i3]; |
|
} else { |
|
|
|
int group = (i3Block & 0x7fff) + (i3 & ~7) + (i3 >> 3); |
|
int gi = i3 & 7; |
|
block = (index[group++] << (2 + (2 * gi))) & 0x30000; |
|
block |= index[group + gi]; |
|
} |
|
if (block == prevBlock && (c - start) >= dataBlockLength) { |
|
|
|
assert((c & (dataBlockLength - 1)) == 0); |
|
c += dataBlockLength; |
|
} else { |
|
int dataMask = dataBlockLength - 1; |
|
prevBlock = block; |
|
if (block == dataNullOffset) { |
|
|
|
if (haveValue) { |
|
if (nullValue != value) { |
|
range.set(start, c - 1, value); |
|
return true; |
|
} |
|
} else { |
|
trieValue = this.nullValue; |
|
value = nullValue; |
|
haveValue = true; |
|
} |
|
c = (c + dataBlockLength) & ~dataMask; |
|
} else { |
|
int di = block + (c & dataMask); |
|
int trieValue2 = data.getFromIndex(di); |
|
if (haveValue) { |
|
if (trieValue2 != trieValue) { |
|
if (filter == null || |
|
maybeFilterValue(trieValue2, this.nullValue, nullValue, |
|
filter) != value) { |
|
range.set(start, c - 1, value); |
|
return true; |
|
} |
|
trieValue = trieValue2; |
|
} |
|
} else { |
|
trieValue = trieValue2; |
|
value = maybeFilterValue(trieValue2, this.nullValue, nullValue, filter); |
|
haveValue = true; |
|
} |
|
while ((++c & dataMask) != 0) { |
|
trieValue2 = data.getFromIndex(++di); |
|
if (trieValue2 != trieValue) { |
|
if (filter == null || |
|
maybeFilterValue(trieValue2, this.nullValue, nullValue, |
|
filter) != value) { |
|
range.set(start, c - 1, value); |
|
return true; |
|
} |
|
trieValue = trieValue2; |
|
} |
|
} |
|
} |
|
} |
|
} while (++i3 < i3BlockLength); |
|
} while (c < highStart); |
|
assert(haveValue); |
|
int di = dataLength - HIGH_VALUE_NEG_DATA_OFFSET; |
|
int highValue = data.getFromIndex(di); |
|
if (maybeFilterValue(highValue, this.nullValue, nullValue, filter) != value) { |
|
--c; |
|
} else { |
|
c = MAX_UNICODE; |
|
} |
|
range.set(start, c, value); |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final int toBinary(OutputStream os) { |
|
try { |
|
DataOutputStream dos = new DataOutputStream(os); |
|
|
|
// Write the UCPTrieHeader |
|
dos.writeInt(0x54726933); |
|
dos.writeChar( |
|
((dataLength & 0xf0000) >> 4) | |
|
((dataNullOffset & 0xf0000) >> 8) | |
|
(getType().ordinal() << 6) | |
|
getValueWidth().ordinal()); |
|
dos.writeChar(index.length); |
|
dos.writeChar(dataLength); |
|
dos.writeChar(index3NullOffset); |
|
dos.writeChar(dataNullOffset); |
|
dos.writeChar(highStart >> SHIFT_2); |
|
int length = 16; |
|
|
|
for (char i : index) { dos.writeChar(i); } |
|
length += index.length * 2; |
|
length += data.write(dos); |
|
return length; |
|
} catch (IOException e) { |
|
throw new UncheckedIOException(e); |
|
} |
|
} |
|
|
|
|
|
static final int FAST_SHIFT = 6; |
|
|
|
|
|
static final int FAST_DATA_BLOCK_LENGTH = 1 << FAST_SHIFT; |
|
|
|
|
|
private static final int FAST_DATA_MASK = FAST_DATA_BLOCK_LENGTH - 1; |
|
|
|
|
|
private static final int SMALL_MAX = 0xfff; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static final int ERROR_VALUE_NEG_DATA_OFFSET = 1; |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final int HIGH_VALUE_NEG_DATA_OFFSET = 2; |
|
|
|
// ucptrie_impl.h |
|
|
|
|
|
private static final int BMP_INDEX_LENGTH = 0x10000 >> FAST_SHIFT; |
|
|
|
static final int SMALL_LIMIT = 0x1000; |
|
private static final int SMALL_INDEX_LENGTH = SMALL_LIMIT >> FAST_SHIFT; |
|
|
|
|
|
static final int SHIFT_3 = 4; |
|
|
|
|
|
private static final int SHIFT_2 = 5 + SHIFT_3; |
|
|
|
|
|
private static final int SHIFT_1 = 5 + SHIFT_2; |
|
|
|
|
|
|
|
|
|
*/ |
|
static final int SHIFT_2_3 = SHIFT_2 - SHIFT_3; |
|
|
|
|
|
|
|
|
|
*/ |
|
static final int SHIFT_1_2 = SHIFT_1 - SHIFT_2; |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final int OMITTED_BMP_INDEX_1_LENGTH = 0x10000 >> SHIFT_1; |
|
|
|
|
|
static final int INDEX_2_BLOCK_LENGTH = 1 << SHIFT_1_2; |
|
|
|
|
|
static final int INDEX_2_MASK = INDEX_2_BLOCK_LENGTH - 1; |
|
|
|
|
|
static final int CP_PER_INDEX_2_ENTRY = 1 << SHIFT_2; |
|
|
|
|
|
static final int INDEX_3_BLOCK_LENGTH = 1 << SHIFT_2_3; |
|
|
|
|
|
private static final int INDEX_3_MASK = INDEX_3_BLOCK_LENGTH - 1; |
|
|
|
|
|
static final int SMALL_DATA_BLOCK_LENGTH = 1 << SHIFT_3; |
|
|
|
|
|
static final int SMALL_DATA_MASK = SMALL_DATA_BLOCK_LENGTH - 1; |
|
|
|
|
|
private static final int OPTIONS_DATA_LENGTH_MASK = 0xf000; |
|
private static final int OPTIONS_DATA_NULL_OFFSET_MASK = 0xf00; |
|
private static final int OPTIONS_RESERVED_MASK = 0x38; |
|
private static final int OPTIONS_VALUE_BITS_MASK = 7; |
|
|
|
|
|
|
|
|
|
*/ |
|
static final int NO_INDEX3_NULL_OFFSET = 0x7fff; |
|
static final int NO_DATA_NULL_OFFSET = 0xfffff; |
|
|
|
private static abstract class Data { |
|
abstract ValueWidth getValueWidth(); |
|
abstract int getDataLength(); |
|
abstract int getFromIndex(int index); |
|
abstract int write(DataOutputStream dos) throws IOException; |
|
} |
|
|
|
private static final class Data16 extends Data { |
|
char[] array; |
|
Data16(char[] a) { array = a; } |
|
@Override ValueWidth getValueWidth() { return ValueWidth.BITS_16; } |
|
@Override int getDataLength() { return array.length; } |
|
@Override int getFromIndex(int index) { return array[index]; } |
|
@Override int write(DataOutputStream dos) throws IOException { |
|
for (char v : array) { dos.writeChar(v); } |
|
return array.length * 2; |
|
} |
|
} |
|
|
|
private static final class Data32 extends Data { |
|
int[] array; |
|
Data32(int[] a) { array = a; } |
|
@Override ValueWidth getValueWidth() { return ValueWidth.BITS_32; } |
|
@Override int getDataLength() { return array.length; } |
|
@Override int getFromIndex(int index) { return array[index]; } |
|
@Override int write(DataOutputStream dos) throws IOException { |
|
for (int v : array) { dos.writeInt(v); } |
|
return array.length * 4; |
|
} |
|
} |
|
|
|
private static final class Data8 extends Data { |
|
byte[] array; |
|
Data8(byte[] a) { array = a; } |
|
@Override ValueWidth getValueWidth() { return ValueWidth.BITS_8; } |
|
@Override int getDataLength() { return array.length; } |
|
@Override int getFromIndex(int index) { return array[index] & 0xff; } |
|
@Override int write(DataOutputStream dos) throws IOException { |
|
for (byte v : array) { dos.writeByte(v); } |
|
return array.length; |
|
} |
|
} |
|
|
|
|
|
private final int[] ascii; |
|
|
|
|
|
private final char[] index; |
|
|
|
|
|
|
|
|
|
*/ |
|
@Deprecated |
|
protected final Data data; |
|
|
|
|
|
|
|
*/ |
|
@Deprecated |
|
protected final int dataLength; |
|
|
|
|
|
|
|
|
|
*/ |
|
@Deprecated |
|
protected final int highStart; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private final int index3NullOffset; |
|
|
|
|
|
|
|
|
|
*/ |
|
private final int dataNullOffset; |
|
|
|
private final int nullValue; |
|
|
|
|
|
|
|
|
|
*/ |
|
@Deprecated |
|
protected final int fastIndex(int c) { |
|
return index[c >> FAST_SHIFT] + (c & FAST_DATA_MASK); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Deprecated |
|
protected final int smallIndex(Type type, int c) { |
|
// Split into two methods to make this part inline-friendly. |
|
|
|
if (c >= highStart) { |
|
return dataLength - HIGH_VALUE_NEG_DATA_OFFSET; |
|
} |
|
return internalSmallIndex(type, c); |
|
} |
|
|
|
private final int internalSmallIndex(Type type, int c) { |
|
int i1 = c >> SHIFT_1; |
|
if (type == Type.FAST) { |
|
assert(0xffff < c && c < highStart); |
|
i1 += BMP_INDEX_LENGTH - OMITTED_BMP_INDEX_1_LENGTH; |
|
} else { |
|
assert(0 <= c && c < highStart && highStart > SMALL_LIMIT); |
|
i1 += SMALL_INDEX_LENGTH; |
|
} |
|
int i3Block = index[index[i1] + ((c >> SHIFT_2) & INDEX_2_MASK)]; |
|
int i3 = (c >> SHIFT_3) & INDEX_3_MASK; |
|
int dataBlock; |
|
if ((i3Block & 0x8000) == 0) { |
|
|
|
dataBlock = index[i3Block + i3]; |
|
} else { |
|
|
|
i3Block = (i3Block & 0x7fff) + (i3 & ~7) + (i3 >> 3); |
|
i3 &= 7; |
|
dataBlock = (index[i3Block++] << (2 + (2 * i3))) & 0x30000; |
|
dataBlock |= index[i3Block + i3]; |
|
} |
|
return dataBlock + (c & SMALL_DATA_MASK); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Deprecated |
|
protected abstract int cpIndex(int c); |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static abstract class Fast extends CodePointTrie { |
|
private Fast(char[] index, Data data, int highStart, |
|
int index3NullOffset, int dataNullOffset) { |
|
super(index, data, highStart, index3NullOffset, dataNullOffset); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Fast fromBinary(ValueWidth valueWidth, ByteBuffer bytes) { |
|
return (Fast) CodePointTrie.fromBinary(Type.FAST, valueWidth, bytes); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final Type getType() { return Type.FAST; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public abstract int bmpGet(int c); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public abstract int suppGet(int c); |
|
|
|
|
|
|
|
|
|
*/ |
|
@Deprecated |
|
@Override |
|
protected final int cpIndex(int c) { |
|
if (c >= 0) { |
|
if (c <= 0xffff) { |
|
return fastIndex(c); |
|
} else if (c <= 0x10ffff) { |
|
return smallIndex(Type.FAST, c); |
|
} |
|
} |
|
return dataLength - ERROR_VALUE_NEG_DATA_OFFSET; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final StringIterator stringIterator(CharSequence s, int sIndex) { |
|
return new FastStringIterator(s, sIndex); |
|
} |
|
|
|
private final class FastStringIterator extends StringIterator { |
|
private FastStringIterator(CharSequence s, int sIndex) { |
|
super(s, sIndex); |
|
} |
|
|
|
@Override |
|
public boolean next() { |
|
if (sIndex >= s.length()) { |
|
return false; |
|
} |
|
char lead = s.charAt(sIndex++); |
|
c = lead; |
|
int dataIndex; |
|
if (!Character.isSurrogate(lead)) { |
|
dataIndex = fastIndex(c); |
|
} else { |
|
char trail; |
|
if (UTF16Plus.isSurrogateLead(lead) && sIndex < s.length() && |
|
Character.isLowSurrogate(trail = s.charAt(sIndex))) { |
|
++sIndex; |
|
c = Character.toCodePoint(lead, trail); |
|
dataIndex = smallIndex(Type.FAST, c); |
|
} else { |
|
dataIndex = dataLength - ERROR_VALUE_NEG_DATA_OFFSET; |
|
} |
|
} |
|
value = data.getFromIndex(dataIndex); |
|
return true; |
|
} |
|
|
|
@Override |
|
public boolean previous() { |
|
if (sIndex <= 0) { |
|
return false; |
|
} |
|
char trail = s.charAt(--sIndex); |
|
c = trail; |
|
int dataIndex; |
|
if (!Character.isSurrogate(trail)) { |
|
dataIndex = fastIndex(c); |
|
} else { |
|
char lead; |
|
if (!UTF16Plus.isSurrogateLead(trail) && sIndex > 0 && |
|
Character.isHighSurrogate(lead = s.charAt(sIndex - 1))) { |
|
--sIndex; |
|
c = Character.toCodePoint(lead, trail); |
|
dataIndex = smallIndex(Type.FAST, c); |
|
} else { |
|
dataIndex = dataLength - ERROR_VALUE_NEG_DATA_OFFSET; |
|
} |
|
} |
|
value = data.getFromIndex(dataIndex); |
|
return true; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static abstract class Small extends CodePointTrie { |
|
private Small(char[] index, Data data, int highStart, |
|
int index3NullOffset, int dataNullOffset) { |
|
super(index, data, highStart, index3NullOffset, dataNullOffset); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Small fromBinary(ValueWidth valueWidth, ByteBuffer bytes) { |
|
return (Small) CodePointTrie.fromBinary(Type.SMALL, valueWidth, bytes); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final Type getType() { return Type.SMALL; } |
|
|
|
|
|
|
|
|
|
*/ |
|
@Deprecated |
|
@Override |
|
protected final int cpIndex(int c) { |
|
if (c >= 0) { |
|
if (c <= SMALL_MAX) { |
|
return fastIndex(c); |
|
} else if (c <= 0x10ffff) { |
|
return smallIndex(Type.SMALL, c); |
|
} |
|
} |
|
return dataLength - ERROR_VALUE_NEG_DATA_OFFSET; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final StringIterator stringIterator(CharSequence s, int sIndex) { |
|
return new SmallStringIterator(s, sIndex); |
|
} |
|
|
|
private final class SmallStringIterator extends StringIterator { |
|
private SmallStringIterator(CharSequence s, int sIndex) { |
|
super(s, sIndex); |
|
} |
|
|
|
@Override |
|
public boolean next() { |
|
if (sIndex >= s.length()) { |
|
return false; |
|
} |
|
char lead = s.charAt(sIndex++); |
|
c = lead; |
|
int dataIndex; |
|
if (!Character.isSurrogate(lead)) { |
|
dataIndex = cpIndex(c); |
|
} else { |
|
char trail; |
|
if (UTF16Plus.isSurrogateLead(lead) && sIndex < s.length() && |
|
Character.isLowSurrogate(trail = s.charAt(sIndex))) { |
|
++sIndex; |
|
c = Character.toCodePoint(lead, trail); |
|
dataIndex = smallIndex(Type.SMALL, c); |
|
} else { |
|
dataIndex = dataLength - ERROR_VALUE_NEG_DATA_OFFSET; |
|
} |
|
} |
|
value = data.getFromIndex(dataIndex); |
|
return true; |
|
} |
|
|
|
@Override |
|
public boolean previous() { |
|
if (sIndex <= 0) { |
|
return false; |
|
} |
|
char trail = s.charAt(--sIndex); |
|
c = trail; |
|
int dataIndex; |
|
if (!Character.isSurrogate(trail)) { |
|
dataIndex = cpIndex(c); |
|
} else { |
|
char lead; |
|
if (!UTF16Plus.isSurrogateLead(trail) && sIndex > 0 && |
|
Character.isHighSurrogate(lead = s.charAt(sIndex - 1))) { |
|
--sIndex; |
|
c = Character.toCodePoint(lead, trail); |
|
dataIndex = smallIndex(Type.SMALL, c); |
|
} else { |
|
dataIndex = dataLength - ERROR_VALUE_NEG_DATA_OFFSET; |
|
} |
|
} |
|
value = data.getFromIndex(dataIndex); |
|
return true; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static final class Fast16 extends Fast { |
|
private final char[] dataArray; |
|
|
|
Fast16(char[] index, char[] data16, int highStart, |
|
int index3NullOffset, int dataNullOffset) { |
|
super(index, new Data16(data16), highStart, index3NullOffset, dataNullOffset); |
|
this.dataArray = data16; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Fast16 fromBinary(ByteBuffer bytes) { |
|
return (Fast16) CodePointTrie.fromBinary(Type.FAST, ValueWidth.BITS_16, bytes); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final int get(int c) { |
|
return dataArray[cpIndex(c)]; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final int bmpGet(int c) { |
|
assert 0 <= c && c <= 0xffff; |
|
return dataArray[fastIndex(c)]; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final int suppGet(int c) { |
|
assert 0x10000 <= c && c <= 0x10ffff; |
|
return dataArray[smallIndex(Type.FAST, c)]; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static final class Fast32 extends Fast { |
|
private final int[] dataArray; |
|
|
|
Fast32(char[] index, int[] data32, int highStart, |
|
int index3NullOffset, int dataNullOffset) { |
|
super(index, new Data32(data32), highStart, index3NullOffset, dataNullOffset); |
|
this.dataArray = data32; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Fast32 fromBinary(ByteBuffer bytes) { |
|
return (Fast32) CodePointTrie.fromBinary(Type.FAST, ValueWidth.BITS_32, bytes); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final int get(int c) { |
|
return dataArray[cpIndex(c)]; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final int bmpGet(int c) { |
|
assert 0 <= c && c <= 0xffff; |
|
return dataArray[fastIndex(c)]; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final int suppGet(int c) { |
|
assert 0x10000 <= c && c <= 0x10ffff; |
|
return dataArray[smallIndex(Type.FAST, c)]; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static final class Fast8 extends Fast { |
|
private final byte[] dataArray; |
|
|
|
Fast8(char[] index, byte[] data8, int highStart, |
|
int index3NullOffset, int dataNullOffset) { |
|
super(index, new Data8(data8), highStart, index3NullOffset, dataNullOffset); |
|
this.dataArray = data8; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Fast8 fromBinary(ByteBuffer bytes) { |
|
return (Fast8) CodePointTrie.fromBinary(Type.FAST, ValueWidth.BITS_8, bytes); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final int get(int c) { |
|
return dataArray[cpIndex(c)] & 0xff; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final int bmpGet(int c) { |
|
assert 0 <= c && c <= 0xffff; |
|
return dataArray[fastIndex(c)] & 0xff; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public final int suppGet(int c) { |
|
assert 0x10000 <= c && c <= 0x10ffff; |
|
return dataArray[smallIndex(Type.FAST, c)] & 0xff; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static final class Small16 extends Small { |
|
Small16(char[] index, char[] data16, int highStart, |
|
int index3NullOffset, int dataNullOffset) { |
|
super(index, new Data16(data16), highStart, index3NullOffset, dataNullOffset); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Small16 fromBinary(ByteBuffer bytes) { |
|
return (Small16) CodePointTrie.fromBinary(Type.SMALL, ValueWidth.BITS_16, bytes); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static final class Small32 extends Small { |
|
Small32(char[] index, int[] data32, int highStart, |
|
int index3NullOffset, int dataNullOffset) { |
|
super(index, new Data32(data32), highStart, index3NullOffset, dataNullOffset); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Small32 fromBinary(ByteBuffer bytes) { |
|
return (Small32) CodePointTrie.fromBinary(Type.SMALL, ValueWidth.BITS_32, bytes); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static final class Small8 extends Small { |
|
Small8(char[] index, byte[] data8, int highStart, |
|
int index3NullOffset, int dataNullOffset) { |
|
super(index, new Data8(data8), highStart, index3NullOffset, dataNullOffset); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Small8 fromBinary(ByteBuffer bytes) { |
|
return (Small8) CodePointTrie.fromBinary(Type.SMALL, ValueWidth.BITS_8, bytes); |
|
} |
|
} |
|
} |