com.ibm.icu.charset.CharsetMBCS Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icu4j-charset Show documentation
Show all versions of icu4j-charset Show documentation
icu4j-charset is a supplemental library for icu4j, implementing Java Charset SPI.
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2006-2016, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/
package com.ibm.icu.charset;
import java.io.IOException;
import java.io.InputStream;
import java.nio.Buffer;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.IntBuffer;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.util.Locale;
import com.ibm.icu.charset.UConverterSharedData.UConverterType;
import com.ibm.icu.impl.ICUBinary;
import com.ibm.icu.impl.ICUData;
import com.ibm.icu.impl.InvalidFormatException;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.text.UTF16;
import com.ibm.icu.text.UnicodeSet;
class CharsetMBCS extends CharsetICU {
private byte[] fromUSubstitution = null;
UConverterSharedData sharedData = null;
private static final int MAX_VERSION_LENGTH = 4;
// these variables are used in getUnicodeSet() and may be changed in future
// typedef enum UConverterSetFilter {
static final int UCNV_SET_FILTER_NONE = 1;
static final int UCNV_SET_FILTER_DBCS_ONLY = 2;
static final int UCNV_SET_FILTER_2022_CN = 3;
static final int UCNV_SET_FILTER_SJIS= 4 ;
static final int UCNV_SET_FILTER_GR94DBCS = 5;
static final int UCNV_SET_FILTER_HZ = 6;
static final int UCNV_SET_FILTER_COUNT = 7;
// } UConverterSetFilter;
/**
* Fallbacks to Unicode are stored outside the normal state table and code point structures in a vector of items of
* this type. They are sorted by offset.
*/
final static class MBCSToUFallback {
int offset;
int codePoint;
MBCSToUFallback(int off, int cp) {
offset = off;
codePoint = cp;
}
}
/**
* This is the MBCS part of the UConverterTable union (a runtime data structure). It keeps all the per-converter
* data and points into the loaded mapping tables.
*/
static final class UConverterMBCSTable {
/* toUnicode */
short countStates;
byte dbcsOnlyState;
boolean stateTableOwned;
int countToUFallbacks;
int stateTable[/* countStates */][/* 256 */];
int swapLFNLStateTable[/* countStates */][/* 256 */]; /* for swaplfnl */
char unicodeCodeUnits[/* countUnicodeResults */];
MBCSToUFallback toUFallbacks[/* countToUFallbacks */];
/* fromUnicode */
char fromUnicodeTable[]; // stage1, and for MBCS_OUTPUT_1 also contains stage2
int fromUnicodeTableInts[]; // stage1 and stage2 together as int[]
// Exactly one of the fromUnicode(Type) tables is not null,
// depending on the outputType.
byte fromUnicodeBytes[];
char fromUnicodeChars[];
int fromUnicodeInts[];
char swapLFNLFromUnicodeChars[]; /* for swaplfnl */
int fromUBytesLength;
short outputType, unicodeMask;
/* converter name for swaplfnl */
String swapLFNLName;
/* extension data */
UConverterSharedData baseSharedData;
// int extIndexes[];
ByteBuffer extIndexes; // create int[] view etc. as needed
CharBuffer mbcsIndex; /* for fast conversion from most of BMP to MBCS (utf8Friendly data) */
// char sbcsIndex[/* SBCS_FAST_LIMIT>>6 */]; /* for fast conversion from low BMP to SBCS (utf8Friendly data) */
boolean utf8Friendly; /* for utf8Friendly data */
char maxFastUChar; /* for utf8Friendly data */
/* roundtrips */
int asciiRoundtrips;
UConverterMBCSTable() {
utf8Friendly = false;
mbcsIndex = null;
}
boolean hasSupplementary() {
return (unicodeMask & UConverterConstants.HAS_SUPPLEMENTARY) != 0;
}
/*
* UConverterMBCSTable(UConverterMBCSTable t) { countStates = t.countStates; dbcsOnlyState = t.dbcsOnlyState;
* stateTableOwned = t.stateTableOwned; countToUFallbacks = t.countToUFallbacks; stateTable = t.stateTable;
* swapLFNLStateTable = t.swapLFNLStateTable; unicodeCodeUnits = t.unicodeCodeUnits; toUFallbacks =
* t.toUFallbacks; fromUnicodeTable = t.fromUnicodeTable; fromUnicodeBytes = t.fromUnicodeBytes;
* swapLFNLFromUnicodeChars = t.swapLFNLFromUnicodeChars; fromUBytesLength = t.fromUBytesLength; outputType =
* t.outputType; unicodeMask = t.unicodeMask; swapLFNLName = t.swapLFNLName; baseSharedData = t.baseSharedData;
* extIndexes = t.extIndexes; }
*/
}
/* Constants used in MBCS data header */
// enum {
static final int MBCS_OPT_LENGTH_MASK=0x3f;
static final int MBCS_OPT_NO_FROM_U=0x40;
/*
* If any of the following options bits are set,
* then the file must be rejected.
*/
static final int MBCS_OPT_INCOMPATIBLE_MASK=0xffc0;
/*
* Remove bits from this mask as more options are recognized
* by all implementations that use this constant.
*/
static final int MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK=0xff80;
// };
/* Constants for fast and UTF-8-friendly conversion. */
// enum {
static final int SBCS_FAST_MAX=0x0fff; /* maximum code point with UTF-8-friendly SBCS runtime code, see makeconv SBCS_UTF8_MAX */
static final int SBCS_FAST_LIMIT=SBCS_FAST_MAX+1; /* =0x1000 */
static final int MBCS_FAST_MAX=0xd7ff; /* maximum code point with UTF-8-friendly MBCS runtime code, see makeconv MBCS_UTF8_MAX */
static final int MBCS_FAST_LIMIT=MBCS_FAST_MAX+1; /* =0xd800 */
// };
/**
* MBCS data header. See data format description above.
*/
final static class MBCSHeader {
byte version[/* U_MAX_VERSION_LENGTH */];
int countStates, countToUFallbacks, offsetToUCodeUnits, offsetFromUTable, offsetFromUBytes;
int flags;
int fromUBytesLength;
/* new and required in version 5 */
int options;
/* new and optional in version 5; used if options&MBCS_OPT_NO_FROM_U */
int fullStage2Length; /* number of 32-bit units */
MBCSHeader() {
version = new byte[MAX_VERSION_LENGTH];
}
}
public CharsetMBCS(String icuCanonicalName, String javaCanonicalName, String[] aliases, String classPath,
ClassLoader loader) throws InvalidFormatException {
super(icuCanonicalName, javaCanonicalName, aliases);
/* See if the icuCanonicalName contains certain option information. */
if (icuCanonicalName.indexOf(UConverterConstants.OPTION_SWAP_LFNL_STRING) > -1) {
options = UConverterConstants.OPTION_SWAP_LFNL;
icuCanonicalName = icuCanonicalName.substring(0, icuCanonicalName.indexOf(UConverterConstants.OPTION_SWAP_LFNL_STRING));
super.icuCanonicalName = icuCanonicalName;
}
// now try to load the data
sharedData = loadConverter(1, icuCanonicalName, classPath, loader);
maxBytesPerChar = sharedData.staticData.maxBytesPerChar;
minBytesPerChar = sharedData.staticData.minBytesPerChar;
maxCharsPerByte = 1;
fromUSubstitution = sharedData.staticData.subChar;
subChar = sharedData.staticData.subChar;
subCharLen = sharedData.staticData.subCharLen;
subChar1 = sharedData.staticData.subChar1;
fromUSubstitution = new byte[sharedData.staticData.subCharLen];
System.arraycopy(sharedData.staticData.subChar, 0, fromUSubstitution, 0, sharedData.staticData.subCharLen);
initializeConverter(options);
}
public CharsetMBCS(String icuCanonicalName, String javaCanonicalName, String[] aliases)
throws InvalidFormatException {
this(icuCanonicalName, javaCanonicalName, aliases, ICUData.ICU_BUNDLE, null);
}
private UConverterSharedData loadConverter(int nestedLoads, String myName, String classPath, ClassLoader loader)
throws InvalidFormatException {
boolean noFromU = false;
// Read converter data from file
UConverterStaticData staticData = new UConverterStaticData();
UConverterDataReader reader = null;
try {
String itemName = myName + '.' + UConverterSharedData.DATA_TYPE;
String resourceName = classPath + '/' + itemName;
ByteBuffer b;
if (loader != null) {
@SuppressWarnings("resource") // Closed by getByteBufferFromInputStreamAndCloseStream().
InputStream i = ICUData.getRequiredStream(loader, resourceName);
b = ICUBinary.getByteBufferFromInputStreamAndCloseStream(i);
} else if (!classPath.equals(ICUData.ICU_BUNDLE)) {
@SuppressWarnings("resource") // Closed by getByteBufferFromInputStreamAndCloseStream().
InputStream i = ICUData.getRequiredStream(resourceName);
b = ICUBinary.getByteBufferFromInputStreamAndCloseStream(i);
} else {
b = ICUBinary.getRequiredData(itemName);
}
reader = new UConverterDataReader(b);
reader.readStaticData(staticData);
} catch (IOException e) {
throw new InvalidFormatException(e);
} catch (Exception e) {
throw new InvalidFormatException(e);
}
int type = staticData.conversionType;
if (type != UConverterSharedData.UConverterType.MBCS
|| staticData.structSize != UConverterStaticData.SIZE_OF_UCONVERTER_STATIC_DATA) {
throw new InvalidFormatException();
}
UConverterSharedData data = new UConverterSharedData(staticData);
// Load data
UConverterMBCSTable mbcsTable = data.mbcs;
MBCSHeader header = new MBCSHeader();
try {
reader.readMBCSHeader(header);
} catch (IOException e) {
throw new InvalidFormatException();
}
int offset;
// int[] extIndexesArray = null;
String baseNameString = null;
if (header.version[0] == 5 && header.version[1] >= 3 && (header.options & MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK) == 0) {
noFromU = ((header.options & MBCS_OPT_NO_FROM_U) != 0);
} else if (header.version[0] != 4) {
throw new InvalidFormatException();
}
mbcsTable.outputType = (byte) header.flags;
/* extension data, header version 4.2 and higher */
offset = header.flags >>> 8;
// if(offset!=0 && mbcsTable.outputType == MBCS_OUTPUT_EXT_ONLY) {
if (mbcsTable.outputType == MBCS_OUTPUT_EXT_ONLY) {
try {
baseNameString = reader.readBaseTableName();
if (offset != 0) {
// agljport:comment subtract 32 for sizeof(_MBCSHeader) and length of baseNameString and 1 null
// terminator byte all already read;
mbcsTable.extIndexes = reader.readExtIndexes(offset - reader.bytesReadAfterStaticData());
}
} catch (IOException e) {
throw new InvalidFormatException();
}
}
// agljport:add this would be unnecessary if extIndexes were memory mapped
/*
* if(mbcsTable.extIndexes != null) {
*
* try { //int nbytes = mbcsTable.extIndexes[UConverterExt.UCNV_EXT_TO_U_LENGTH]*4 +
* mbcsTable.extIndexes[UConverterExt.UCNV_EXT_TO_U_UCHARS_LENGTH]*2 +
* mbcsTable.extIndexes[UConverterExt.UCNV_EXT_FROM_U_LENGTH]*6 +
* mbcsTable.extIndexes[UConverterExt.UCNV_EXT_FROM_U_BYTES_LENGTH] +
* mbcsTable.extIndexes[UConverterExt.UCNV_EXT_FROM_U_STAGE_12_LENGTH]*2 +
* mbcsTable.extIndexes[UConverterExt.UCNV_EXT_FROM_U_STAGE_3_LENGTH]*2 +
* mbcsTable.extIndexes[UConverterExt.UCNV_EXT_FROM_U_STAGE_3B_LENGTH]*4; //int nbytes =
* mbcsTable.extIndexes[UConverterExt.UCNV_EXT_SIZE] //byte[] extTables = dataReader.readExtTables(nbytes);
* //mbcsTable.extTables = ByteBuffer.wrap(extTables); } catch(IOException e) { System.err.println("Caught
* IOException: " + e.getMessage()); pErrorCode[0] = UErrorCode.U_INVALID_FORMAT_ERROR; return; } }
*/
if (mbcsTable.outputType == MBCS_OUTPUT_EXT_ONLY) {
UConverterSharedData baseSharedData = null;
ByteBuffer extIndexes;
String baseName;
/* extension-only file, load the base table and set values appropriately */
extIndexes = mbcsTable.extIndexes;
if (extIndexes == null) {
/* extension-only file without extension */
throw new InvalidFormatException();
}
if (nestedLoads != 1) {
/* an extension table must not be loaded as a base table */
throw new InvalidFormatException();
}
/* load the base table */
baseName = baseNameString;
if (baseName.equals(staticData.name)) {
/* forbid loading this same extension-only file */
throw new InvalidFormatException();
}
// agljport:fix args.size=sizeof(UConverterLoadArgs);
baseSharedData = loadConverter(2, baseName, classPath, loader);
if (baseSharedData.staticData.conversionType != UConverterType.MBCS
|| baseSharedData.mbcs.baseSharedData != null) {
// agljport:fix ucnv_unload(baseSharedData);
throw new InvalidFormatException();
}
/* copy the base table data */
// agljport:comment deep copy in C changes mbcs through local reference mbcsTable; in java we probably don't
// need the deep copy so can just make sure mbcs and its local reference both refer to the same new object
mbcsTable = data.mbcs = baseSharedData.mbcs;
/* overwrite values with relevant ones for the extension converter */
mbcsTable.baseSharedData = baseSharedData;
mbcsTable.extIndexes = extIndexes;
/*
* It would be possible to share the swapLFNL data with a base converter, but the generated name would have
* to be different, and the memory would have to be free'd only once. It is easier to just create the data
* for the extension converter separately when it is requested.
*/
mbcsTable.swapLFNLStateTable = null;
mbcsTable.swapLFNLFromUnicodeChars = null;
mbcsTable.swapLFNLName = null;
/*
* Set a special, runtime-only outputType if the extension converter is a DBCS version of a base converter
* that also maps single bytes.
*/
if (staticData.conversionType == UConverterType.DBCS
|| (staticData.conversionType == UConverterType.MBCS && staticData.minBytesPerChar >= 2)) {
if (baseSharedData.mbcs.outputType == MBCS_OUTPUT_2_SISO) {
/* the base converter is SI/SO-stateful */
int entry;
/* get the dbcs state from the state table entry for SO=0x0e */
entry = mbcsTable.stateTable[0][0xe];
if (MBCS_ENTRY_IS_FINAL(entry) && MBCS_ENTRY_FINAL_ACTION(entry) == MBCS_STATE_CHANGE_ONLY
&& MBCS_ENTRY_FINAL_STATE(entry) != 0) {
mbcsTable.dbcsOnlyState = (byte) MBCS_ENTRY_FINAL_STATE(entry);
mbcsTable.outputType = MBCS_OUTPUT_DBCS_ONLY;
}
} else if (baseSharedData.staticData.conversionType == UConverterType.MBCS
&& baseSharedData.staticData.minBytesPerChar == 1
&& baseSharedData.staticData.maxBytesPerChar == 2 && mbcsTable.countStates <= 127) {
/* non-stateful base converter, need to modify the state table */
int newStateTable[][/* 256 */];
int state[]; // this works because java 2-D array is array of references and we can have state =
// newStateTable[i];
int i, count;
/* allocate a new state table and copy the base state table contents */
count = mbcsTable.countStates;
newStateTable = new int[(count + 1) * 1024][256];
for (i = 0; i < mbcsTable.stateTable.length; ++i)
System.arraycopy(mbcsTable.stateTable[i], 0, newStateTable[i], 0,
mbcsTable.stateTable[i].length);
/* change all final single-byte entries to go to a new all-illegal state */
state = newStateTable[0];
for (i = 0; i < 256; ++i) {
if (MBCS_ENTRY_IS_FINAL(state[i])) {
state[i] = MBCS_ENTRY_TRANSITION(count, 0);
}
}
/* build the new all-illegal state */
state = newStateTable[count];
for (i = 0; i < 256; ++i) {
state[i] = MBCS_ENTRY_FINAL(0, MBCS_STATE_ILLEGAL, 0);
}
mbcsTable.stateTable = newStateTable;
mbcsTable.countStates = (byte) (count + 1);
mbcsTable.stateTableOwned = true;
mbcsTable.outputType = MBCS_OUTPUT_DBCS_ONLY;
}
}
/*
* unlike below for files with base tables, do not get the unicodeMask from the sharedData; instead, use the
* base table's unicodeMask, which we copied in the memcpy above; this is necessary because the static data
* unicodeMask, especially the UCNV_HAS_SUPPLEMENTARY flag, is part of the base table data
*/
} else {
/* conversion file with a base table; an additional extension table is optional */
/* make sure that the output type is known */
switch (mbcsTable.outputType) {
case MBCS_OUTPUT_1:
case MBCS_OUTPUT_2:
case MBCS_OUTPUT_3:
case MBCS_OUTPUT_4:
case MBCS_OUTPUT_3_EUC:
case MBCS_OUTPUT_4_EUC:
case MBCS_OUTPUT_2_SISO:
/* OK */
break;
default:
throw new InvalidFormatException();
}
/*
* converter versions 6.1 and up contain a unicodeMask that is used here to select the most efficient
* function implementations
*/
// agljport:fix info.size=sizeof(UDataInfo);
// agljport:fix udata_getInfo((UDataMemory *)sharedData->dataMemory, &info);
if (reader.dataFormatHasUnicodeMask()) {
/* mask off possible future extensions to be safe */
mbcsTable.unicodeMask = (short) (staticData.unicodeMask & 3);
} else {
/* for older versions, assume worst case: contains anything possible (prevent over-optimizations) */
mbcsTable.unicodeMask = UConverterConstants.HAS_SUPPLEMENTARY | UConverterConstants.HAS_SURROGATES;
}
try {
reader.readMBCSTable(header, mbcsTable);
} catch (IOException e) {
throw new InvalidFormatException();
}
if (offset != 0) {
try {
// agljport:comment subtract 32 for sizeof(_MBCSHeader) and length of baseNameString and 1 null
// terminator byte all already read;
// int namelen = baseNameString != null? baseNameString.length() + 1: 0;
mbcsTable.extIndexes = reader.readExtIndexes(offset - reader.bytesReadAfterStaticData());
} catch (IOException e) {
throw new InvalidFormatException();
}
}
if (header.version[1] >= 3 && (mbcsTable.unicodeMask & UConverterConstants.HAS_SURROGATES) == 0 &&
(mbcsTable.countStates == 1 ? ((char)header.version[2] >= (SBCS_FAST_MAX>>8)) : ((char)header.version[2] >= (MBCS_FAST_MAX>>8)))) {
mbcsTable.utf8Friendly = true;
if (mbcsTable.countStates == 1) {
/*
* SBCS: Stage 3 is allocated in 64-entry blocks for U+0000..SBCS_FAST_MAX or higher.
* Build a table with indexes to each block, to be used instead of
* the regular stage 1/2 table.
*/
// sbcsIndex = new char[SBCS_FAST_LIMIT>>6];
// for (int i = 0; i < (SBCS_FAST_LIMIT>>6); ++i) {
// mbcsTable.sbcsIndex[i] = mbcsTable.fromUnicodeTable[mbcsTable.fromUnicodeTable[i>>4]+((i<<2)&0x3c)];
// }
/* set SBCS_FAST_MAX to reflect the reach of sbcsIndex[] even if header.version[2]>(SBCS_FAST_MAX>>8) */
mbcsTable.maxFastUChar = SBCS_FAST_MAX;
} else {
/*
* MBCS: Stage 3 is allocated in 64-entry blocks for U+0000..MBCS_FAST_MAX or higher.
* The .cnv file is prebuilt with an additional stage table with indexes to each block.
*/
mbcsTable.maxFastUChar = (char)((header.version[2]<<8) | 0xff);
}
}
/* calculate a bit set of 4 ASCII characters per bit that round-trip to ASCII bytes */
{
int asciiRoundtrips = 0xffffffff;
for (int i = 0; i < 0x80; ++i) {
if (mbcsTable.stateTable[0][i] != MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, i)) {
asciiRoundtrips &= ~(1 << (i >> 2));
}
}
mbcsTable.asciiRoundtrips = asciiRoundtrips;
}
// TODO: Use asciiRoundtrips to speed up conversion, like in ICU4C.
if (noFromU) {
int stage1Length = (mbcsTable.unicodeMask&UConverterConstants.HAS_SUPPLEMENTARY) != 0 ? 0x440 : 0x40;
int stage2Length = (header.offsetFromUBytes - header.offsetFromUTable)/4 - stage1Length/2;
reconstituteData(mbcsTable, stage1Length, stage2Length, header.fullStage2Length);
}
if (mbcsTable.outputType == MBCS_OUTPUT_DBCS_ONLY || mbcsTable.outputType == MBCS_OUTPUT_2_SISO) {
/*
* MBCS_OUTPUT_DBCS_ONLY: No SBCS mappings, therefore ASCII does not roundtrip.
* MBCS_OUTPUT_2_SISO: Bypass the ASCII fastpath to handle prevLength correctly.
*/
mbcsTable.asciiRoundtrips = 0;
}
}
// TODO: Use mbcsIndex to speed up UTF-16 conversion, like in ICU4C.
mbcsTable.mbcsIndex = null;
return data;
}
private static boolean writeStage3Roundtrip(UConverterMBCSTable mbcsTable, long value, int codePoints[]) {
char[] table;
byte[] bytes;
int stage2;
int p;
int c;
int i, st3;
table = mbcsTable.fromUnicodeTable;
int[] tableInts = mbcsTable.fromUnicodeTableInts;
bytes = mbcsTable.fromUnicodeBytes;
char[] chars = mbcsTable.fromUnicodeChars;
int[] ints = mbcsTable.fromUnicodeInts;
/* for EUC outputTypes, modify the value like genmbcs.c's transformEUC() */
switch(mbcsTable.outputType) {
case MBCS_OUTPUT_3_EUC:
if(value<=0xffff) {
/* short sequences are stored directly */
/* code set 0 or 1 */
} else if(value<=0x8effff) {
/* code set 2 */
value&=0x7fff;
} else /* first byte is 0x8f */ {
/* code set 3 */
value&=0xff7f;
}
break;
case MBCS_OUTPUT_4_EUC:
if(value<=0xffffff) {
/* short sequences are stored directly */
/* code set 0 or 1 */
} else if(value<=0x8effffffL) {
/* code set 2 */
value&=0x7fffff;
} else /* first byte is 0x8f */ {
/* code set 3 */
value&=0xff7fff;
}
break;
default:
break;
}
for(i=0; i<=0x1f; ++value, ++i) {
c=codePoints[i];
if(c<0) {
continue;
}
/* locate the stage 2 & 3 data */
stage2 = table[c>>10] + ((c>>4)&0x3f);
st3 = tableInts[stage2];
st3 = (char)(st3 * 16 + (c&0xf));
/* write the codepage bytes into stage 3 */
switch(mbcsTable.outputType) {
case MBCS_OUTPUT_3:
case MBCS_OUTPUT_4_EUC:
p = st3*3;
bytes[p] = (byte)(value>>16);
bytes[p+1] = (byte)(value>>8);
bytes[p+2] = (byte)value;
break;
case MBCS_OUTPUT_4:
ints[st3] = (int)value;
break;
default:
/* 2 bytes per character */
chars[st3] = (char)value;
break;
}
// Set the roundtrip flag.
int shift = 16 + (c & 0x0F);
tableInts[stage2] |= (1L << shift);
}
return true;
}
private static void reconstituteData(UConverterMBCSTable mbcsTable,
int stage1Length, int stage2Length, int fullStage2Length) {
char[] stage1 = mbcsTable.fromUnicodeTable;
// stage2 starts with unused stage1 space.
// Indexes into stage 2 count from the bottom of the fromUnicodeTable.
int numStage1Ints = stage1Length / 2; // 2 chars = 1 int
int[] stage2 = new int[numStage1Ints + fullStage2Length];
System.arraycopy(mbcsTable.fromUnicodeTableInts, numStage1Ints,
stage2, (fullStage2Length - stage2Length) + numStage1Ints,
stage2Length);
mbcsTable.fromUnicodeTableInts = stage2;
/* reconstitute the initial part of stage 2 from the mbcsIndex */
{
int stageUTF8Length=(mbcsTable.maxFastUChar+1)>>6;
int stageUTF8Index=0;
int st1, st2, st3, i;
for (st1 = 0; stageUTF8Index < stageUTF8Length; ++st1) {
st2 = stage1[st1];
if (st2 != stage1Length/2) {
/* each stage 2 block has 64 entries corresponding to 16 entries in the mbcsIndex */
for (i = 0; i < 16; ++i) {
st3 = mbcsTable.mbcsIndex.get(stageUTF8Index++);
if (st3 != 0) {
/* a stage 2 entry's index is per stage 3 16-block, not per stage 3 entry */
st3>>=4;
/*
* 4 stage 2 entries point to 4 consecutive stage 3 16-blocks which are
* allocated together as a single 64-block for access from the mbcsIndex
*/
stage2[st2++] = st3++;
stage2[st2++] = st3++;
stage2[st2++] = st3++;
stage2[st2++] = st3;
} else {
/* no stage 3 block, skip */
st2+=4;
}
}
} else {
/* no stage 2 block, skip */
stageUTF8Index+=16;
}
}
}
switch (mbcsTable.outputType) {
case CharsetMBCS.MBCS_OUTPUT_2:
case CharsetMBCS.MBCS_OUTPUT_2_SISO:
case CharsetMBCS.MBCS_OUTPUT_3_EUC:
mbcsTable.fromUnicodeChars = new char[mbcsTable.fromUBytesLength / 2];
break;
case CharsetMBCS.MBCS_OUTPUT_3:
case CharsetMBCS.MBCS_OUTPUT_4_EUC:
mbcsTable.fromUnicodeBytes = new byte[mbcsTable.fromUBytesLength];
break;
case CharsetMBCS.MBCS_OUTPUT_4:
mbcsTable.fromUnicodeInts = new int[mbcsTable.fromUBytesLength / 4];
break;
default:
// Cannot occur, caller checked already.
assert false;
}
/* reconstitute fromUnicodeBytes with roundtrips from toUnicode data */
MBCSEnumToUnicode(mbcsTable);
}
/*
* Internal function enumerating the toUnicode data of an MBCS converter.
* Currently only used for reconstituting data for a MBCS_OPT_NO_FROM_U
* table, but could also be used for a future getUnicodeSet() option
* that includes reverse fallbacks (after updating this function's implementation).
* Currently only handles roundtrip mappings.
* Does not currently handle extensions.
*/
private static void MBCSEnumToUnicode(UConverterMBCSTable mbcsTable) {
/*
* Properties for each state, to speed up the enumeration.
* Ignorable actions are unassigned/illegal/state-change-only:
* They do not lead to mappings.
*
* Bits 7..6
* 1 direct/initial state (stateful converters have multiple)
* 0 non-initial state with transitions or with nonignorable result actions
* -1 final state with only ignorable actions
*
* Bits 5..3
* The lowest byte value with non-ignorable actions is
* value<<5 (rounded down).
*
* Bits 2..0:
* The highest byte value with non-ignorable actions is
* (value<<5)&0x1f (rounded up).
*/
byte stateProps[] = new byte[MBCS_MAX_STATE_COUNT];
int state;
/* recurse from state 0 and set all stateProps */
getStateProp(mbcsTable.stateTable, stateProps, 0);
for (state = 0; state < mbcsTable.countStates; ++state) {
if (stateProps[state] >= 0x40) {
/* start from each direct state */
enumToU(mbcsTable, stateProps, state, 0, 0);
}
}
}
private static boolean enumToU(UConverterMBCSTable mbcsTable, byte stateProps[], int state, int offset, int value) {
int[] codePoints = new int[32];
int[] row;
char[] unicodeCodeUnits;
int anyCodePoints;
int b, limit;
row = mbcsTable.stateTable[state];
unicodeCodeUnits = mbcsTable.unicodeCodeUnits;
value<<=8;
anyCodePoints = -1; /* becomes non-negative if there is a mapping */
b = (stateProps[state]&0x38)<<2;
if (b == 0 && stateProps[state] >= 0x40) {
/* skip byte sequences with leading zeros because they are note stored in the fromUnicode table */
codePoints[0] = UConverterConstants.U_SENTINEL;
b = 1;
}
limit = ((stateProps[state]&7)+1)<<5;
while (b < limit) {
int entry = row[b];
if (MBCS_ENTRY_IS_TRANSITION(entry)) {
int nextState = MBCS_ENTRY_TRANSITION_STATE(entry);
if (stateProps[nextState] >= 0) {
/* recurse to a state with non-ignorable actions */
if (!enumToU(mbcsTable, stateProps, nextState, offset+MBCS_ENTRY_TRANSITION_OFFSET(entry), value|b)) {
return false;
}
}
codePoints[b&0x1f] = UConverterConstants.U_SENTINEL;
} else {
int c;
int action;
/*
* An if-else-if chain provides more reliable performance for
* the most common cases compared to a switch.
*/
action = MBCS_ENTRY_FINAL_ACTION(entry);
if (action == MBCS_STATE_VALID_DIRECT_16) {
/* output BMP code point */
c = MBCS_ENTRY_FINAL_VALUE_16(entry);
} else if (action == MBCS_STATE_VALID_16) {
int finalOffset = offset+MBCS_ENTRY_FINAL_VALUE_16(entry);
c = unicodeCodeUnits[finalOffset];
if (c < 0xfffe) {
/* output BMP code point */
} else {
c = UConverterConstants.U_SENTINEL;
}
} else if (action == MBCS_STATE_VALID_16_PAIR) {
int finalOffset = offset+MBCS_ENTRY_FINAL_VALUE_16(entry);
c = unicodeCodeUnits[finalOffset++];
if (c < 0xd800) {
/* output BMP code point below 0xd800 */
} else if (c <= 0xdbff) {
/* output roundtrip or fallback supplementary code point */
c = ((c&0x3ff)<<10)+unicodeCodeUnits[finalOffset]+(0x10000-0xdc00);
} else if (c == 0xe000) {
/* output roundtrip BMP code point above 0xd800 or fallback BMP code point */
c = unicodeCodeUnits[finalOffset];
} else {
c = UConverterConstants.U_SENTINEL;
}
} else if (action == MBCS_STATE_VALID_DIRECT_20) {
/* output supplementary code point */
c = MBCS_ENTRY_FINAL_VALUE(entry)+0x10000;
} else {
c = UConverterConstants.U_SENTINEL;
}
codePoints[b&0x1f] = c;
anyCodePoints&=c;
}
if (((++b)&0x1f) == 0) {
if(anyCodePoints>=0) {
if(!writeStage3Roundtrip(mbcsTable, value|(b-0x20), codePoints)) {
return false;
}
anyCodePoints=-1;
}
}
}
return true;
}
/*
* Only called if stateProps[state]==-1.
* A recursive call may do stateProps[state]|=0x40 if this state is the target of an
* MBCS_STATE_CHANGE_ONLY.
*/
private static byte getStateProp(int stateTable[][], byte stateProps[], int state) {
int[] row;
int min, max, entry, nextState;
row = stateTable[state];
stateProps[state] = 0;
/* find first non-ignorable state */
for (min = 0;;++min) {
entry = row[min];
nextState = MBCS_ENTRY_STATE(entry);
if (stateProps[nextState] == -1) {
getStateProp(stateTable, stateProps, nextState);
}
if (MBCS_ENTRY_IS_TRANSITION(entry)) {
if (stateProps[nextState] >- 0) {
break;
}
} else if (MBCS_ENTRY_FINAL_ACTION(entry) < MBCS_STATE_UNASSIGNED) {
break;
}
if (min == 0xff) {
stateProps[state] = -0x40; /* (byte)0xc0 */
return stateProps[state];
}
}
stateProps[state]|=(byte)((min>>5)<<3);
/* find last non-ignorable state */
for (max = 0xff; min < max; --max) {
entry = row[max];
nextState = MBCS_ENTRY_STATE(entry);
if (stateProps[nextState] == -1) {
getStateProp(stateTable, stateProps, nextState);
}
if (MBCS_ENTRY_IS_TRANSITION(entry)) {
if (stateProps[nextState] >- 0) {
break;
}
} else if (MBCS_ENTRY_FINAL_ACTION(entry) < MBCS_STATE_UNASSIGNED) {
break;
}
}
stateProps[state]|=(byte)(max>>5);
/* recurse further and collect direct-state information */
while (min <= max) {
entry = row[min];
nextState = MBCS_ENTRY_STATE(entry);
if (stateProps[nextState] == -1) {
getStateProp(stateTable, stateProps, nextState);
}
if (MBCS_ENTRY_IS_TRANSITION(entry)) {
stateProps[nextState]|=0x40;
if (MBCS_ENTRY_FINAL_ACTION(entry) <= MBCS_STATE_FALLBACK_DIRECT_20) {
stateProps[state]|=0x40;
}
}
++min;
}
return stateProps[state];
}
protected void initializeConverter(int myOptions) {
UConverterMBCSTable mbcsTable;
ByteBuffer extIndexes;
short outputType;
byte maxBytesPerUChar;
mbcsTable = sharedData.mbcs;
outputType = mbcsTable.outputType;
if (outputType == MBCS_OUTPUT_DBCS_ONLY) {
/* the swaplfnl option does not apply, remove it */
this.options = myOptions &= ~UConverterConstants.OPTION_SWAP_LFNL;
}
if ((myOptions & UConverterConstants.OPTION_SWAP_LFNL) != 0) {
/* do this because double-checked locking is broken */
boolean isCached;
// agljport:todo umtx_lock(NULL);
isCached = mbcsTable.swapLFNLStateTable != null;
// agljport:todo umtx_unlock(NULL);
if (!isCached) {
try {
if (!EBCDICSwapLFNL()) {
/* this option does not apply, remove it */
this.options = myOptions & ~UConverterConstants.OPTION_SWAP_LFNL;
}
} catch (Exception e) {
/* something went wrong. */
return;
}
}
}
String lowerCaseName = icuCanonicalName.toLowerCase(Locale.ENGLISH);
if (lowerCaseName.indexOf("gb18030") >= 0) {
/* set a flag for GB 18030 mode, which changes the callback behavior */
this.options |= MBCS_OPTION_GB18030;
} else if (lowerCaseName.indexOf("keis") >= 0) {
this.options |= MBCS_OPTION_KEIS;
} else if (lowerCaseName.indexOf("jef") >= 0) {
this.options |= MBCS_OPTION_JEF;
} else if (lowerCaseName.indexOf("jips") >= 0) {
this.options |= MBCS_OPTION_JIPS;
}
/* fix maxBytesPerUChar depending on outputType and options etc. */
if (outputType == MBCS_OUTPUT_2_SISO) {
/* changed from 3 to 4 in ICU4J only. #9205 */
maxBytesPerChar = 4; /* SO+DBCS+SI*/
}
extIndexes = mbcsTable.extIndexes;
if (extIndexes != null) {
maxBytesPerUChar = (byte) GET_MAX_BYTES_PER_UCHAR(extIndexes);
if (outputType == MBCS_OUTPUT_2_SISO) {
++maxBytesPerUChar; /* SO + multiple DBCS */
}
if (maxBytesPerUChar > maxBytesPerChar) {
maxBytesPerChar = maxBytesPerUChar;
}
}
}
/* EBCDIC swap LF<->NL--------------------------------------------------------------------------------*/
/*
* This code modifies a standard EBCDIC<->Unicode mappling table for
* OS/390 (z/OS) Unix System Services (Open Edition).
* The difference is in the mapping of Line Feed and New Line control codes:
* Standard EBDIC maps
*
* \x25 |0
* \x15 |0
*
* but OS/390 USS EBCDIC swaps the control codes for LF and NL,
* mapping
*
* \x15 |0
* \x25 |0
*
* This code modifies a loaded standard EBCDIC<->Unicode mapping table
* by copying it into allocated memory and swapping the LF and NL values.
* It allows to support the same EBCDIC charset in both version without
* duplicating the entire installed table.
*/
/* standard EBCDIC codes */
private static final short EBCDIC_LF = 0x0025;
private static final short EBCDIC_NL = 0x0015;
/* standard EBCDIC codes with roundtrip flag as stored in Unicode-to-single-byte tables */
private static final short EBCDIC_RT_LF = 0x0f25;
private static final short EBCDIC_RT_NL = 0x0f15;
/* Unicode code points */
private static final short U_LF = 0x000A;
private static final short U_NL = 0x0085;
private boolean EBCDICSwapLFNL() throws Exception {
UConverterMBCSTable mbcsTable;
char[] table;
int[][] newStateTable;
String newName;
int stage2Entry;
mbcsTable = sharedData.mbcs;
table = mbcsTable.fromUnicodeTable;
int[] tableInts = sharedData.mbcs.fromUnicodeTableInts;
char[] chars = mbcsTable.fromUnicodeChars;
char[] results = chars;
/*
* Check that this is an EBCDIC table with SBCS portion -
* SBCS or EBCDIC with standard EBCDIC LF and NL mappings.
*
* If not, ignore the option. Options are always ignored if they do not apply.
*/
if (!((mbcsTable.outputType == MBCS_OUTPUT_1 || mbcsTable.outputType == MBCS_OUTPUT_2_SISO) &&
mbcsTable.stateTable[0][EBCDIC_LF] == MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, U_LF) &&
mbcsTable.stateTable[0][EBCDIC_NL] == MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, U_NL))) {
return false;
}
if (mbcsTable.outputType == MBCS_OUTPUT_1) {
if (!(EBCDIC_RT_LF == MBCS_SINGLE_RESULT_FROM_U(table, results, U_LF) &&
EBCDIC_RT_NL == MBCS_SINGLE_RESULT_FROM_U(table, results, U_NL))) {
return false;
}
} else /* MBCS_OUTPUT_2_SISO */ {
stage2Entry = MBCS_STAGE_2_FROM_U(table, tableInts, U_LF);
if (!(MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, U_LF) &&
EBCDIC_LF == MBCS_VALUE_2_FROM_STAGE_2(chars, stage2Entry, U_LF))) {
return false;
}
stage2Entry = MBCS_STAGE_2_FROM_U(table, tableInts, U_NL);
if (!(MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, U_NL) &&
EBCDIC_NL == MBCS_VALUE_2_FROM_STAGE_2(chars, stage2Entry, U_NL))) {
return false;
}
}
if (mbcsTable.fromUBytesLength > 0) {
/*
* We _know_ the number of bytes in the fromUnicodeBytes array
* starting with header.version 4.1.
*/
// sizeofFromUBytes = mbcsTable.fromUBytesLength;
} else {
/*
* Otherwise:
* There used to be code to enumerate the fromUnicode
* trie and find the highest entry, but it was removed in ICU 3.2
* because it was not tested and caused a low code coverage number.
*/
throw new Exception("U_INVALID_FORMAT_ERROR");
}
/*
* The table has an appropriate format.
* Allocate and build
* - a modified to-Unicode state table
* - a modified from-Unicode output array
* - a converter name string with the swap option appended
*/
// size = mbcsTable.countStates * 1024 + sizeofFromUBytes + UConverterConstants.MAX_CONVERTER_NAME_LENGTH + 20;
/* copy and modify the to-Unicode state table */
newStateTable = new int[mbcsTable.stateTable.length][mbcsTable.stateTable[0].length];
for (int i = 0; i < newStateTable.length; i++) {
System.arraycopy(mbcsTable.stateTable[i], 0, newStateTable[i], 0, newStateTable[i].length);
}
newStateTable[0][EBCDIC_LF] = MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, U_NL);
newStateTable[0][EBCDIC_NL] = MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, U_LF);
/* copy and modify the from-Unicode result table */
char[] newResults = new char[chars.length];
System.arraycopy(chars, 0, newResults, 0, chars.length);
/* conveniently, the table access macros work on the left side of expressions */
if (mbcsTable.outputType == MBCS_OUTPUT_1) {
MBCS_SINGLE_RESULT_FROM_U_SET(table, newResults, U_LF, EBCDIC_RT_NL);
MBCS_SINGLE_RESULT_FROM_U_SET(table, newResults, U_NL, EBCDIC_RT_LF);
} else /* MBCS_OUTPUT_2_SISO */ {
stage2Entry = MBCS_STAGE_2_FROM_U(table, tableInts, U_LF);
MBCS_VALUE_2_FROM_STAGE_2_SET(newResults, stage2Entry, U_LF, EBCDIC_NL);
stage2Entry = MBCS_STAGE_2_FROM_U(table, tableInts, U_NL);
MBCS_VALUE_2_FROM_STAGE_2_SET(newResults, stage2Entry, U_NL, EBCDIC_LF);
}
/* set the canonical converter name */
newName = icuCanonicalName.concat(UConverterConstants.OPTION_SWAP_LFNL_STRING);
if (mbcsTable.swapLFNLStateTable == null) {
mbcsTable.swapLFNLStateTable = newStateTable;
mbcsTable.swapLFNLFromUnicodeChars = newResults;
mbcsTable.swapLFNLName = newName;
}
return true;
}
/**
* MBCS output types for conversions from Unicode. These per-converter types determine the storage method in stage 3
* of the lookup table, mostly how many bytes are stored per entry.
*/
static final int MBCS_OUTPUT_1 = 0; /* 0 */
static final int MBCS_OUTPUT_2 = MBCS_OUTPUT_1 + 1; /* 1 */
static final int MBCS_OUTPUT_3 = MBCS_OUTPUT_2 + 1; /* 2 */
static final int MBCS_OUTPUT_4 = MBCS_OUTPUT_3 + 1; /* 3 */
static final int MBCS_OUTPUT_3_EUC = 8; /* 8 */
static final int MBCS_OUTPUT_4_EUC = MBCS_OUTPUT_3_EUC + 1; /* 9 */
static final int MBCS_OUTPUT_2_SISO = 12; /* c */
static final int MBCS_OUTPUT_2_HZ = MBCS_OUTPUT_2_SISO + 1; /* d */
static final int MBCS_OUTPUT_EXT_ONLY = MBCS_OUTPUT_2_HZ + 1; /* e */
// static final int MBCS_OUTPUT_COUNT = MBCS_OUTPUT_EXT_ONLY + 1;
static final int MBCS_OUTPUT_DBCS_ONLY = 0xdb; /* runtime-only type for DBCS-only handling of SISO tables */
/* GB 18030 data ------------------------------------------------------------ */
/* helper macros for linear values for GB 18030 four-byte sequences */
private static int LINEAR_18030(int a, int b, int c, int d) {
return ((((a & 0xff) * 10 + (b & 0xff)) * 126 + (c & 0xff)) * 10 + (d & 0xff));
}
private static int LINEAR_18030_BASE = LINEAR_18030(0x81, 0x30, 0x81, 0x30);
private static int LINEAR(int x) {
return LINEAR_18030(x >>> 24, (x >>> 16) & 0xff, (x >>> 8) & 0xff, x & 0xff);
}
/*
* Some ranges of GB 18030 where both the Unicode code points and the GB four-byte sequences are contiguous and are
* handled algorithmically by the special callback functions below. The values are start & end of Unicode & GB
* codes.
*
* Note that single surrogates are not mapped by GB 18030 as of the re-released mapping tables from 2000-nov-30.
*/
private static final int gb18030Ranges[][] = new int[/* 14 */][/* 4 */] {
{ 0x10000, 0x10FFFF, LINEAR(0x90308130), LINEAR(0xE3329A35) },
{ 0x9FA6, 0xD7FF, LINEAR(0x82358F33), LINEAR(0x8336C738) },
{ 0x0452, 0x1E3E, LINEAR(0x8130D330), LINEAR(0x8135F436) },
{ 0x1E40, 0x200F, LINEAR(0x8135F438), LINEAR(0x8136A531) },
{ 0xE865, 0xF92B, LINEAR(0x8336D030), LINEAR(0x84308534) },
{ 0x2643, 0x2E80, LINEAR(0x8137A839), LINEAR(0x8138FD38) },
{ 0xFA2A, 0xFE2F, LINEAR(0x84309C38), LINEAR(0x84318537) },
{ 0x3CE1, 0x4055, LINEAR(0x8231D438), LINEAR(0x8232AF32) },
{ 0x361B, 0x3917, LINEAR(0x8230A633), LINEAR(0x8230F237) },
{ 0x49B8, 0x4C76, LINEAR(0x8234A131), LINEAR(0x8234E733) },
{ 0x4160, 0x4336, LINEAR(0x8232C937), LINEAR(0x8232F837) },
{ 0x478E, 0x4946, LINEAR(0x8233E838), LINEAR(0x82349638) },
{ 0x44D7, 0x464B, LINEAR(0x8233A339), LINEAR(0x8233C931) },
{ 0xFFE6, 0xFFFF, LINEAR(0x8431A234), LINEAR(0x8431A439) } };
/* bit flag for UConverter.options indicating GB 18030 special handling */
private static final int MBCS_OPTION_GB18030 = 0x8000;
/* bit flag for UConverter.options indicating KEIS,JEF,JIF special handling */
private static final int MBCS_OPTION_KEIS = 0x01000;
private static final int MBCS_OPTION_JEF = 0x02000;
private static final int MBCS_OPTION_JIPS = 0x04000;
private static enum SISO_Option {
SI,
SO
}
private static final byte[] KEIS_SO_CHAR = { 0x0A, 0x42 };
private static final byte[] KEIS_SI_CHAR = { 0x0A, 0x41 };
private static final byte JEF_SO_CHAR = 0x28;
private static final byte JEF_SI_CHAR = 0x29;
private static final byte[] JIPS_SO_CHAR = { 0x1A, 0x70 };
private static final byte[] JIPS_SI_CHAR = { 0x1A, 0x71 };
private static int getSISOBytes(SISO_Option option, int cnvOption, byte[] value) {
int SISOLength = 0;
switch (option) {
case SI:
if ((cnvOption&MBCS_OPTION_KEIS)!=0) {
value[0] = KEIS_SI_CHAR[0];
value[1] = KEIS_SI_CHAR[1];
SISOLength = 2;
} else if ((cnvOption&MBCS_OPTION_JEF)!=0) {
value[0] = JEF_SI_CHAR;
SISOLength = 1;
} else if ((cnvOption&MBCS_OPTION_JIPS)!=0) {
value[0] = JIPS_SI_CHAR[0];
value[1] = JIPS_SI_CHAR[1];
SISOLength = 2;
} else {
value[0] = UConverterConstants.SI;
SISOLength = 1;
}
break;
case SO:
if ((cnvOption&MBCS_OPTION_KEIS)!=0) {
value[0] = KEIS_SO_CHAR[0];
value[1] = KEIS_SO_CHAR[1];
SISOLength = 2;
} else if ((cnvOption&MBCS_OPTION_JEF)!=0) {
value[0] = JEF_SO_CHAR;
SISOLength = 1;
} else if ((cnvOption&MBCS_OPTION_JIPS)!=0) {
value[0] = JIPS_SO_CHAR[0];
value[1] = JIPS_SO_CHAR[1];
SISOLength = 2;
} else {
value[0] = UConverterConstants.SO;
SISOLength = 1;
}
break;
default:
/* Should never happen. */
break;
}
return SISOLength;
}
// enum {
static final int MBCS_MAX_STATE_COUNT = 128;
// };
/**
* MBCS action codes for conversions to Unicode. These values are in bits 23..20 of the state table entries.
*/
static final int MBCS_STATE_VALID_DIRECT_16 = 0;
static final int MBCS_STATE_VALID_DIRECT_20 = MBCS_STATE_VALID_DIRECT_16 + 1;
static final int MBCS_STATE_FALLBACK_DIRECT_16 = MBCS_STATE_VALID_DIRECT_20 + 1;
static final int MBCS_STATE_FALLBACK_DIRECT_20 = MBCS_STATE_FALLBACK_DIRECT_16 + 1;
static final int MBCS_STATE_VALID_16 = MBCS_STATE_FALLBACK_DIRECT_20 + 1;
static final int MBCS_STATE_VALID_16_PAIR = MBCS_STATE_VALID_16 + 1;
static final int MBCS_STATE_UNASSIGNED = MBCS_STATE_VALID_16_PAIR + 1;
static final int MBCS_STATE_ILLEGAL = MBCS_STATE_UNASSIGNED + 1;
static final int MBCS_STATE_CHANGE_ONLY = MBCS_STATE_ILLEGAL + 1;
static int MBCS_ENTRY_SET_STATE(int entry, int state) {
return (entry&0x80ffffff)|(state<<24L);
}
static int MBCS_ENTRY_STATE(int entry) {
return (((entry)>>24)&0x7f);
}
/* Methods for state table entries */
static int MBCS_ENTRY_TRANSITION(int state, int offset) {
return (state << 24L) | offset;
}
static int MBCS_ENTRY_FINAL(int state, int action, int value) {
return 0x80000000 | (state << 24L) | (action << 20L) | value;
}
static boolean MBCS_ENTRY_IS_TRANSITION(int entry) {
return (entry) >= 0;
}
static boolean MBCS_ENTRY_IS_FINAL(int entry) {
return (entry) < 0;
}
static int MBCS_ENTRY_TRANSITION_STATE(int entry) {
return ((entry) >>> 24);
}
static int MBCS_ENTRY_TRANSITION_OFFSET(int entry) {
return ((entry) & 0xffffff);
}
static int MBCS_ENTRY_FINAL_STATE(int entry) {
return ((entry) >>> 24) & 0x7f;
}
static boolean MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(int entry) {
return ((entry) < 0x80100000);
}
static int MBCS_ENTRY_FINAL_ACTION(int entry) {
return ((entry) >>> 20) & 0xf;
}
static int MBCS_ENTRY_FINAL_VALUE(int entry) {
return ((entry) & 0xfffff);
}
static char MBCS_ENTRY_FINAL_VALUE_16(int entry) {
return (char) (entry);
}
static boolean MBCS_IS_ASCII_ROUNDTRIP(int b, long asciiRoundtrips) {
return (((asciiRoundtrips) & (1<<((b)>>2)))!=0);
}
/**
* This macro version of _MBCSSingleSimpleGetNextUChar() gets a code point from a byte. It works for single-byte,
* single-state codepages that only map to and from BMP code points, and it always returns fallback values.
*/
static char MBCS_SINGLE_SIMPLE_GET_NEXT_BMP(UConverterMBCSTable mbcs, final int b) {
assert 0 <= b && b <= 0xff;
return MBCS_ENTRY_FINAL_VALUE_16(mbcs.stateTable[0][b]);
}
/* single-byte fromUnicode: get the 16-bit result word */
static char MBCS_SINGLE_RESULT_FROM_U(char[] table, char[] results, int c) {
int i1 = table[c >>> 10] + ((c >>> 4) & 0x3f);
int i = table[i1] + (c & 0xf);
return results[i];
}
/* single-byte fromUnicode: set the 16-bit result word with newValue*/
static void MBCS_SINGLE_RESULT_FROM_U_SET(char[] table, char[] results, int c, int newValue) {
int i1 = table[c >>> 10] + ((c >>> 4) & 0x3f);
int i = table[i1] + (c & 0xf);
results[i] = (char) newValue;
}
/* multi-byte fromUnicode: get the 32-bit stage 2 entry */
static int MBCS_STAGE_2_FROM_U(char[] table, int[] tableInts, int c) {
int i = table[(c) >>> 10] + ((c >>> 4) & 0x3f);
return tableInts[i];
}
private static boolean MBCS_FROM_U_IS_ROUNDTRIP(int stage2Entry, int c) {
return (((stage2Entry) & (1 << (16 + ((c) & 0xf)))) != 0);
}
static char MBCS_VALUE_2_FROM_STAGE_2(char[] chars, int stage2Entry, int c) {
int i = 16 * (stage2Entry & UConverterConstants.UNSIGNED_SHORT_MASK) + (c & 0xf);
return chars[i];
}
static void MBCS_VALUE_2_FROM_STAGE_2_SET(char[] chars, int stage2Entry, int c, int newValue) {
int i = 16 * (stage2Entry & UConverterConstants.UNSIGNED_SHORT_MASK) + (c & 0xf);
chars[i] = (char) newValue;
}
private static int MBCS_VALUE_4_FROM_STAGE_2(int[] ints, int stage2Entry, int c) {
int i = 16 * (stage2Entry & UConverterConstants.UNSIGNED_SHORT_MASK) + (c & 0xf);
return ints[i];
}
static int MBCS_POINTER_3_FROM_STAGE_2(byte[] bytes, int stage2Entry, int c) {
return ((16 * (stage2Entry & UConverterConstants.UNSIGNED_SHORT_MASK) + ((c) & 0xf)) * 3);
}
// ------------UConverterExt-------------------------------------------------------
static final int EXT_INDEXES_LENGTH = 0; /* 0 */
static final int EXT_TO_U_INDEX = EXT_INDEXES_LENGTH + 1; /* 1 */
static final int EXT_TO_U_LENGTH = EXT_TO_U_INDEX + 1;
static final int EXT_TO_U_UCHARS_INDEX = EXT_TO_U_LENGTH + 1;
static final int EXT_TO_U_UCHARS_LENGTH = EXT_TO_U_UCHARS_INDEX + 1;
static final int EXT_FROM_U_UCHARS_INDEX = EXT_TO_U_UCHARS_LENGTH + 1; /* 5 */
static final int EXT_FROM_U_VALUES_INDEX = EXT_FROM_U_UCHARS_INDEX + 1;
static final int EXT_FROM_U_LENGTH = EXT_FROM_U_VALUES_INDEX + 1;
static final int EXT_FROM_U_BYTES_INDEX = EXT_FROM_U_LENGTH + 1;
static final int EXT_FROM_U_BYTES_LENGTH = EXT_FROM_U_BYTES_INDEX + 1;
static final int EXT_FROM_U_STAGE_12_INDEX = EXT_FROM_U_BYTES_LENGTH + 1; /* 10 */
static final int EXT_FROM_U_STAGE_1_LENGTH = EXT_FROM_U_STAGE_12_INDEX + 1;
static final int EXT_FROM_U_STAGE_12_LENGTH = EXT_FROM_U_STAGE_1_LENGTH + 1;
static final int EXT_FROM_U_STAGE_3_INDEX = EXT_FROM_U_STAGE_12_LENGTH + 1;
static final int EXT_FROM_U_STAGE_3_LENGTH = EXT_FROM_U_STAGE_3_INDEX + 1;
static final int EXT_FROM_U_STAGE_3B_INDEX = EXT_FROM_U_STAGE_3_LENGTH + 1;
static final int EXT_FROM_U_STAGE_3B_LENGTH = EXT_FROM_U_STAGE_3B_INDEX + 1;
private static final int EXT_COUNT_BYTES = EXT_FROM_U_STAGE_3B_LENGTH + 1; /* 17 */
// private static final int EXT_COUNT_UCHARS = EXT_COUNT_BYTES + 1;
// private static final int EXT_FLAGS = EXT_COUNT_UCHARS + 1;
//
// private static final int EXT_RESERVED_INDEX = EXT_FLAGS + 1; /* 20, moves with additional indexes */
//
// private static final int EXT_SIZE=31;
// private static final int EXT_INDEXES_MIN_LENGTH=32;
static final int EXT_FROM_U_MAX_DIRECT_LENGTH = 3;
/* toUnicode helpers -------------------------------------------------------- */
private static final int TO_U_BYTE_SHIFT = 24;
private static final int TO_U_VALUE_MASK = 0xffffff;
private static final int TO_U_MIN_CODE_POINT = 0x1f0000;
private static final int TO_U_MAX_CODE_POINT = 0x2fffff;
private static final int TO_U_ROUNDTRIP_FLAG = (1 << 23);
private static final int TO_U_INDEX_MASK = 0x3ffff;
private static final int TO_U_LENGTH_SHIFT = 18;
private static final int TO_U_LENGTH_OFFSET = 12;
/* maximum number of indexed UChars */
static final int MAX_UCHARS = 19;
static int TO_U_GET_BYTE(int word) {
return word >>> TO_U_BYTE_SHIFT;
}
static int TO_U_GET_VALUE(int word) {
return word & TO_U_VALUE_MASK;
}
static boolean TO_U_IS_ROUNDTRIP(int value) {
return (value & TO_U_ROUNDTRIP_FLAG) != 0;
}
static boolean TO_U_IS_PARTIAL(int value) {
return 0 <= value && value < TO_U_MIN_CODE_POINT;
}
static int TO_U_GET_PARTIAL_INDEX(int value) {
return value;
}
static int TO_U_MASK_ROUNDTRIP(int value) {
return value & ~TO_U_ROUNDTRIP_FLAG;
}
private static int TO_U_MAKE_WORD(byte b, int value) {
// TO_U_BYTE_SHIFT == 24: safe to just shift the signed byte-as-int.
return (b << TO_U_BYTE_SHIFT) | value;
}
/* use after masking off the roundtrip flag */
static boolean TO_U_IS_CODE_POINT(int value) {
assert value >= 0;
return value <= TO_U_MAX_CODE_POINT;
}
static int TO_U_GET_CODE_POINT(int value) {
assert value >= 0;
return value - TO_U_MIN_CODE_POINT;
}
private static int TO_U_GET_INDEX(int value) {
return value & TO_U_INDEX_MASK;
}
private static int TO_U_GET_LENGTH(int value) {
return (value >>> TO_U_LENGTH_SHIFT) - TO_U_LENGTH_OFFSET;
}
/* fromUnicode helpers ------------------------------------------------------ */
/* most trie constants are shared with ucnvmbcs.h */
private static final int STAGE_2_LEFT_SHIFT = 2;
// private static final int STAGE_3_GRANULARITY = 4;
/* trie access, returns the stage 3 value=index to stage 3b; s1Index=c>>10 */
static int FROM_U(CharBuffer stage12, CharBuffer stage3, int s1Index, int c) {
return stage3.get((stage12.get((stage12.get(s1Index) + ((c >>> 4) & 0x3f))) << STAGE_2_LEFT_SHIFT)
+ (c & 0xf));
}
private static final int FROM_U_LENGTH_SHIFT = 24;
private static final int FROM_U_ROUNDTRIP_FLAG = 1 << 31;
static final int FROM_U_RESERVED_MASK = 0x60000000;
private static final int FROM_U_DATA_MASK = 0xffffff;
/* special value for "no mapping" to (impossible roundtrip to 0 bytes, value 01) */
static final int FROM_U_SUBCHAR1 = 0x80000001;
/* at most 3 bytes in the lower part of the value */
private static final int FROM_U_MAX_DIRECT_LENGTH = 3;
/* maximum number of indexed bytes */
static final int MAX_BYTES = 0x1f;
static boolean FROM_U_IS_PARTIAL(int value) {
return (value >>> FROM_U_LENGTH_SHIFT) == 0;
}
static int FROM_U_GET_PARTIAL_INDEX(int value) {
return value;
}
static boolean FROM_U_IS_ROUNDTRIP(int value) {
return (value & FROM_U_ROUNDTRIP_FLAG) != 0;
}
private static int FROM_U_MASK_ROUNDTRIP(int value) {
return value & ~FROM_U_ROUNDTRIP_FLAG;
}
/* use after masking off the roundtrip flag */
static int FROM_U_GET_LENGTH(int value) {
return (value >>> FROM_U_LENGTH_SHIFT) & MAX_BYTES;
}
/* get bytes or bytes index */
static int FROM_U_GET_DATA(int value) {
return value & FROM_U_DATA_MASK;
}
/* get the pointer to an extension array from indexes[index] */
static Buffer ARRAY(ByteBuffer indexes, int index, Class> itemType) {
int oldpos = indexes.position();
Buffer b;
// TODO: It is very inefficient to create Buffer objects for each array access.
// We should create an inner class Extensions (or sibling class CharsetMBCSExtensions)
// which has buffers for the arrays, together with the code that works with them.
indexes.position(indexes.getInt(index << 2));
if (itemType == int.class)
b = indexes.asIntBuffer();
else if (itemType == char.class)
b = indexes.asCharBuffer();
else if (itemType == short.class)
b = indexes.asShortBuffer();
else
// default or (itemType == byte.class)
b = indexes.slice();
indexes.position(oldpos);
return b;
}
private static int GET_MAX_BYTES_PER_UCHAR(ByteBuffer indexes) {
indexes.position(0);
return indexes.getInt(EXT_COUNT_BYTES) & 0xff;
}
/*
* @return index of the UChar, if found; else <0
*/
static int findFromU(CharBuffer fromUSection, int length, char u) {
int i, start, limit;
/* binary search */
start = 0;
limit = length;
for (;;) {
i = limit - start;
if (i <= 1) {
break; /* done */
}
/* startmode==0 is equivalent to firstLength==1.
*/
private static int SISO_STATE(UConverterSharedData sharedData, int mode) {
return sharedData.mbcs.outputType == MBCS_OUTPUT_2_SISO ? (byte) mode
: sharedData.mbcs.outputType == MBCS_OUTPUT_DBCS_ONLY ? 1 : -1;
}
class CharsetDecoderMBCS extends CharsetDecoderICU {
CharsetDecoderMBCS(CharsetICU cs) {
super(cs);
}
@Override
protected CoderResult decodeLoop(ByteBuffer source, CharBuffer target, IntBuffer offsets, boolean flush) {
/* Just call cnvMBCSToUnicodeWithOffsets() to remove duplicate code. */
return cnvMBCSToUnicodeWithOffsets(source, target, offsets, flush);
}
/*
* continue partial match with new input never called for simple, single-character conversion
*/
private CoderResult continueMatchToU(ByteBuffer source, CharBuffer target, IntBuffer offsets, int srcIndex,
boolean flush) {
CoderResult cr = CoderResult.UNDERFLOW;
int[] value = new int[1];
int match, length;
match = matchToU((byte) SISO_STATE(sharedData, mode), preToUArray, preToUBegin, preToULength, source,
value, isToUUseFallback(), flush);
if (match > 0) {
if (match >= preToULength) {
/* advance src pointer for the consumed input */
source.position(source.position() + match - preToULength);
preToULength = 0;
} else {
/* the match did not use all of preToU[] - keep the rest for replay */
length = preToULength - match;
System.arraycopy(preToUArray, preToUBegin + match, preToUArray, preToUBegin, length);
preToULength = (byte) -length;
}
/* write result */
cr = writeToU(value[0], target, offsets, srcIndex);
} else if (match < 0) {
/* save state for partial match */
int j, sArrayIndex;
/* just _append_ the newly consumed input to preToU[] */
sArrayIndex = source.position();
match = -match;
for (j = preToULength; j < match; ++j) {
preToUArray[j] = source.get(sArrayIndex++);
}
source.position(sArrayIndex); /* same as *src=srcLimit; because we reached the end of input */
preToULength = (byte) match;
} else /* match==0 */{
/*
* no match
*
* We need to split the previous input into two parts:
*
* 1. The first codepage character is unmappable - that's how we got into trying the extension data in
* the first place. We need to move it from the preToU buffer to the error buffer, set an error code,
* and prepare the rest of the previous input for 2.
*
* 2. The rest of the previous input must be converted once we come back from the callback for the first
* character. At that time, we have to try again from scratch to convert these input characters. The
* replay will be handled by the ucnv.c conversion code.
*/
/* move the first codepage character to the error field */
System.arraycopy(preToUArray, preToUBegin, toUBytesArray, toUBytesBegin, preToUFirstLength);
toULength = preToUFirstLength;
/* move the rest up inside the buffer */
length = preToULength - preToUFirstLength;
if (length > 0) {
System.arraycopy(preToUArray, preToUBegin + preToUFirstLength, preToUArray, preToUBegin, length);
}
/* mark preToU for replay */
preToULength = (byte) -length;
/* set the error code for unassigned */
cr = CoderResult.unmappableForLength(preToUFirstLength);
}
return cr;
}
/*
* this works like matchFromU() except - the first character is in pre - no trie is used - the returned
* matchLength is not offset by 2
*/
private int matchToU(byte sisoState, byte[] preArray, int preArrayBegin, int preLength, ByteBuffer source,
int[] pMatchValue, boolean isUseFallback, boolean flush) {
ByteBuffer cx = sharedData.mbcs.extIndexes;
IntBuffer toUTable, toUSection;
int value, matchValue, srcLength = 0;
int i, j, index, length, matchLength;
short b;
if (cx == null || cx.asIntBuffer().get(EXT_TO_U_LENGTH) <= 0) {
return 0; /* no extension data, no match */
}
/* initialize */
toUTable = (IntBuffer) ARRAY(cx, EXT_TO_U_INDEX, int.class);
index = 0;
matchValue = 0;
i = j = matchLength = 0;
if (source != null) {
srcLength = source.remaining();
}
if (sisoState == 0) {
/* SBCS state of an SI/SO stateful converter, look at only exactly 1 byte */
if (preLength > 1) {
return 0; /* no match of a DBCS sequence in SBCS mode */
} else if (preLength == 1) {
srcLength = 0;
} else /* preLength==0 */{
if (srcLength > 1) {
srcLength = 1;
}
}
flush = true;
}
/* we must not remember fallback matches when not using fallbacks */
/* match input units until there is a full match or the input is consumed */
for (;;) {
/* go to the next section */
int oldpos = toUTable.position();
toUSection = ((IntBuffer) toUTable.position(index)).slice();
toUTable.position(oldpos);
/* read first pair of the section */
value = toUSection.get();
length = TO_U_GET_BYTE(value);
value = TO_U_GET_VALUE(value);
if (value != 0 && (TO_U_IS_ROUNDTRIP(value) || isToUUseFallback(isUseFallback))
&& TO_U_VERIFY_SISO_MATCH(sisoState, i + j)) {
/* remember longest match so far */
matchValue = value;
matchLength = i + j;
}
/* match pre[] then src[] */
if (i < preLength) {
b = (short) (preArray[preArrayBegin + i++] & UConverterConstants.UNSIGNED_BYTE_MASK);
} else if (j < srcLength) {
b = (short) (source.get(source.position() + j++) & UConverterConstants.UNSIGNED_BYTE_MASK);
} else {
/* all input consumed, partial match */
if (flush || (length = (i + j)) > MAX_BYTES) {
/*
* end of the entire input stream, stop with the longest match so far or: partial match must not
* be longer than UCNV_EXT_MAX_BYTES because it must fit into state buffers
*/
break;
} else {
/* continue with more input next time */
return -length;
}
}
/* search for the current UChar */
value = findToU(toUSection, length, b);
if (value == 0) {
/* no match here, stop with the longest match so far */
break;
} else {
if (TO_U_IS_PARTIAL(value)) {
/* partial match, continue */
index = TO_U_GET_PARTIAL_INDEX(value);
} else {
if ((TO_U_IS_ROUNDTRIP(value) || isToUUseFallback(isUseFallback)) && TO_U_VERIFY_SISO_MATCH(sisoState, i + j)) {
/* full match, stop with result */
matchValue = value;
matchLength = i + j;
} else {
/* full match on fallback not taken, stop with the longest match so far */
}
break;
}
}
}
if (matchLength == 0) {
/* no match at all */
return 0;
}
/* return result */
pMatchValue[0] = TO_U_MASK_ROUNDTRIP(matchValue);
return matchLength;
}
private CoderResult writeToU(int value, CharBuffer target, IntBuffer offsets, int srcIndex) {
ByteBuffer cx = sharedData.mbcs.extIndexes;
/* output the result */
if (TO_U_IS_CODE_POINT(value)) {
/* output a single code point */
return toUWriteCodePoint(TO_U_GET_CODE_POINT(value), target, offsets, srcIndex);
} else {
/* output a string - with correct data we have resultLength>0 */
char[] a = new char[TO_U_GET_LENGTH(value)];
CharBuffer cb = ((CharBuffer) ARRAY(cx, EXT_TO_U_UCHARS_INDEX, char.class));
cb.position(TO_U_GET_INDEX(value));
cb.get(a, 0, a.length);
return toUWriteUChars(this, a, 0, a.length, target, offsets, srcIndex);
}
}
private CoderResult toUWriteCodePoint(int c, CharBuffer target, IntBuffer offsets, int sourceIndex) {
CoderResult cr = CoderResult.UNDERFLOW;
int tBeginIndex = target.position();
if (target.hasRemaining()) {
if (c <= 0xffff) {
target.put((char) c);
c = UConverterConstants.U_SENTINEL;
} else /* c is a supplementary code point */{
target.put(UTF16.getLeadSurrogate(c));
c = UTF16.getTrailSurrogate(c);
if (target.hasRemaining()) {
target.put((char) c);
c = UConverterConstants.U_SENTINEL;
}
}
/* write offsets */
if (offsets != null) {
offsets.put(sourceIndex);
if ((tBeginIndex + 1) < target.position()) {
offsets.put(sourceIndex);
}
}
}
/* write overflow from c */
if (c >= 0) {
charErrorBufferLength = UTF16.append(charErrorBufferArray, 0, c);
cr = CoderResult.OVERFLOW;
}
return cr;
}
/*
* Input sequence: cnv->toUBytes[0..length[ @return if(U_FAILURE) return the length (toULength, byteIndex) for
* the input else return 0 after output has been written to the target
*/
private int toU(int length, ByteBuffer source, CharBuffer target, IntBuffer offsets, int sourceIndex,
boolean flush, CoderResult[] cr) {
// ByteBuffer cx;
if (sharedData.mbcs.extIndexes != null
&& initialMatchToU(length, source, target, offsets, sourceIndex, flush, cr)) {
return 0; /* an extension mapping handled the input */
}
/* GB 18030 */
if (length == 4 && (options & MBCS_OPTION_GB18030) != 0) {
int[] range;
int linear;
int i;
linear = LINEAR_18030(toUBytesArray[0], toUBytesArray[1], toUBytesArray[2], toUBytesArray[3]);
for (i = 0; i < gb18030Ranges.length; ++i) {
range = gb18030Ranges[i];
if (range[2] <= linear && linear <= range[3]) {
/* found the sequence, output the Unicode code point for it */
cr[0] = CoderResult.UNDERFLOW;
/* add the linear difference between the input and start sequences to the start code point */
linear = range[0] + (linear - range[2]);
/* output this code point */
cr[0] = toUWriteCodePoint(linear, target, offsets, sourceIndex);
return 0;
}
}
}
/* no mapping */
cr[0] = CoderResult.unmappableForLength(length);
return length;
}
/*
* target 0) {
/* advance src pointer for the consumed input */
source.position(source.position() + match - firstLength);
/* write result to target */
cr[0] = writeToU(value[0], target, offsets, srcIndex);
return true;
} else if (match < 0) {
/* save state for partial match */
byte[] sArray;
int sArrayIndex;
int j;
/* copy the first code point */
sArray = toUBytesArray;
sArrayIndex = toUBytesBegin;
preToUFirstLength = (byte) firstLength;
for (j = 0; j < firstLength; ++j) {
preToUArray[j] = sArray[sArrayIndex++];
}
/* now copy the newly consumed input */
sArrayIndex = source.position();
match = -match;
for (; j < match; ++j) {
preToUArray[j] = source.get(sArrayIndex++);
}
source.position(sArrayIndex);
preToULength = (byte) match;
return true;
} else /* match==0 no match */{
return false;
}
}
private int simpleMatchToU(ByteBuffer source, boolean useFallback) {
int[] value = new int[1];
int match;
if (source.remaining() <= 0) {
return 0xffff;
}
/* try to match */
byte[] sourceArray;
int sourcePosition, sourceLimit;
if (source.isReadOnly()) {
// source.array() would throw an exception
sourcePosition = source.position(); // relative to source.array()
sourceArray = new byte[Math.min(source.remaining(), EXT_MAX_BYTES)];
source.get(sourceArray).position(sourcePosition);
sourcePosition = 0; // relative to sourceArray
sourceLimit = sourceArray.length;
} else {
sourceArray = source.array();
sourcePosition = source.position();
sourceLimit = source.limit();
}
match = matchToU((byte) -1, sourceArray, sourcePosition, sourceLimit, null, value, useFallback, true);
if (match == source.remaining()) {
/* write result for simple, single-character conversion */
if (TO_U_IS_CODE_POINT(value[0])) {
return TO_U_GET_CODE_POINT(value[0]);
}
}
/*
* return no match because - match>0 && value points to string: simple conversion cannot handle multiple
* code points - match>0 && match!=length: not all input consumed, forbidden for this function - match==0:
* no match found in the first place - match<0: partial match, not supported for simple conversion (and
* flush==true)
*/
return 0xfffe;
}
CoderResult cnvMBCSToUnicodeWithOffsets(ByteBuffer source, CharBuffer target, IntBuffer offsets, boolean flush) {
CoderResult[] cr = { CoderResult.UNDERFLOW };
int sourceArrayIndex, sourceArrayIndexStart;
int stateTable[][/* 256 */];
char[] unicodeCodeUnits;
int offset;
byte state;
int byteIndex;
byte[] bytes;
int sourceIndex, nextSourceIndex;
int entry = 0;
char c;
byte action;
if (preToULength > 0) {
/*
* pass sourceIndex=-1 because we continue from an earlier buffer in the future, this may change with
* continuous offsets
*/
cr[0] = continueMatchToU(source, target, offsets, -1, flush);
if (cr[0].isError() || preToULength < 0) {
return cr[0];
}
}
if (sharedData.mbcs.countStates == 1) {
if (!sharedData.mbcs.hasSupplementary()) {
cr[0] = cnvMBCSSingleToBMPWithOffsets(source, target, offsets, flush);
} else {
cr[0] = cnvMBCSSingleToUnicodeWithOffsets(source, target, offsets, flush);
}
return cr[0];
}
/* set up the local pointers */
sourceArrayIndex = sourceArrayIndexStart = source.position();
if ((options & UConverterConstants.OPTION_SWAP_LFNL) != 0) {
stateTable = sharedData.mbcs.swapLFNLStateTable;
} else {
stateTable = sharedData.mbcs.stateTable;
}
unicodeCodeUnits = sharedData.mbcs.unicodeCodeUnits;
/* get the converter state from UConverter */
offset = toUnicodeStatus;
byteIndex = toULength;
bytes = toUBytesArray;
/*
* if we are in the SBCS state for a DBCS-only converter, then load the DBCS state from the MBCS data
* (dbcsOnlyState==0 if it is not a DBCS-only converter)
*/
state = (byte)mode;
if (state == 0) {
state = sharedData.mbcs.dbcsOnlyState;
}
/* sourceIndex=-1 if the current character began in the previous buffer */
sourceIndex = byteIndex == 0 ? 0 : -1;
nextSourceIndex = 0;
/* conversion loop */
while (sourceArrayIndex < source.limit()) {
/*
* This following test is to see if available input would overflow the output. It does not catch output
* of more than one code unit that overflows as a result of a surrogate pair or callback output from the
* last source byte. Therefore, those situations also test for overflows and will then break the loop,
* too.
*/
if (!target.hasRemaining()) {
/* target is full */
cr[0] = CoderResult.OVERFLOW;
break;
}
if (byteIndex == 0) {
/* optimized loop for 1/2-byte input and BMP output */
// agljport:todo see ucnvmbcs.c for deleted block
do {
entry = stateTable[state][source.get(sourceArrayIndex)&UConverterConstants.UNSIGNED_BYTE_MASK];
if (MBCS_ENTRY_IS_TRANSITION(entry)) {
state = (byte)MBCS_ENTRY_TRANSITION_STATE(entry);
offset = MBCS_ENTRY_TRANSITION_OFFSET(entry);
++sourceArrayIndex;
if (sourceArrayIndex < source.limit()
&& MBCS_ENTRY_IS_FINAL(entry = stateTable[state][source.get(sourceArrayIndex)&UConverterConstants.UNSIGNED_BYTE_MASK])
&& MBCS_ENTRY_FINAL_ACTION(entry) == MBCS_STATE_VALID_16
&& (c = unicodeCodeUnits[offset + MBCS_ENTRY_FINAL_VALUE_16(entry)]) < 0xfffe) {
++sourceArrayIndex;
target.put(c);
if (offsets != null) {
offsets.put(sourceIndex);
sourceIndex = (nextSourceIndex += 2);
}
state = (byte)MBCS_ENTRY_FINAL_STATE(entry); /* typically 0 */
offset = 0;
} else {
/* set the state and leave the optimized loop */
++nextSourceIndex;
bytes[0] = source.get(sourceArrayIndex - 1);
byteIndex = 1;
break;
}
} else {
if (MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(entry)) {
/* output BMP code point */
++sourceArrayIndex;
target.put(MBCS_ENTRY_FINAL_VALUE_16(entry));
if (offsets != null) {
offsets.put(sourceIndex);
sourceIndex = ++nextSourceIndex;
}
state = (byte)MBCS_ENTRY_FINAL_STATE(entry); /* typically 0 */
} else {
/* leave the optimized loop */
break;
}
}
} while (sourceArrayIndex < source.limit() && target.hasRemaining());
/*
* these tests and break statements could be put inside the loop if C had "break outerLoop" like
* Java
*/
if (sourceArrayIndex >= source.limit()) {
break;
}
if (!target.hasRemaining()) {
/* target is full */
cr[0] = CoderResult.OVERFLOW;
break;
}
++nextSourceIndex;
bytes[byteIndex++] = source.get(sourceArrayIndex++);
} else /* byteIndex>0 */{
++nextSourceIndex;
entry = stateTable[state][(bytes[byteIndex++] = source.get(sourceArrayIndex++))
& UConverterConstants.UNSIGNED_BYTE_MASK];
}
if (MBCS_ENTRY_IS_TRANSITION(entry)) {
state = (byte)MBCS_ENTRY_TRANSITION_STATE(entry);
offset += MBCS_ENTRY_TRANSITION_OFFSET(entry);
continue;
}
/* save the previous state for proper extension mapping with SI/SO-stateful converters */
mode = state;
/* set the next state early so that we can reuse the entry variable */
state = (byte)MBCS_ENTRY_FINAL_STATE(entry); /* typically 0 */
/*
* An if-else-if chain provides more reliable performance for the most common cases compared to a
* switch.
*/
action = (byte)MBCS_ENTRY_FINAL_ACTION(entry);
if (action == MBCS_STATE_VALID_16) {
offset += MBCS_ENTRY_FINAL_VALUE_16(entry);
c = unicodeCodeUnits[offset];
if (c < 0xfffe) {
/* output BMP code point */
target.put(c);
if (offsets != null) {
offsets.put(sourceIndex);
}
byteIndex = 0;
} else if (c == 0xfffe) {
if (isFallbackUsed() && (entry = getFallback(sharedData.mbcs, offset)) != 0xfffe) {
/* output fallback BMP code point */
target.put((char)entry);
if (offsets != null) {
offsets.put(sourceIndex);
}
byteIndex = 0;
}
} else {
/* callback(illegal) */
cr[0] = CoderResult.malformedForLength(byteIndex);
}
} else if (action == MBCS_STATE_VALID_DIRECT_16) {
/* output BMP code point */
target.put(MBCS_ENTRY_FINAL_VALUE_16(entry));
if (offsets != null) {
offsets.put(sourceIndex);
}
byteIndex = 0;
} else if (action == MBCS_STATE_VALID_16_PAIR) {
offset += MBCS_ENTRY_FINAL_VALUE_16(entry);
c = unicodeCodeUnits[offset++];
if (c < 0xd800) {
/* output BMP code point below 0xd800 */
target.put(c);
if (offsets != null) {
offsets.put(sourceIndex);
}
byteIndex = 0;
} else if (isFallbackUsed() ? c <= 0xdfff : c <= 0xdbff) {
/* output roundtrip or fallback surrogate pair */
target.put((char)(c & 0xdbff));
if (offsets != null) {
offsets.put(sourceIndex);
}
byteIndex = 0;
if (target.hasRemaining()) {
target.put(unicodeCodeUnits[offset]);
if (offsets != null) {
offsets.put(sourceIndex);
}
} else {
/* target overflow */
charErrorBufferArray[0] = unicodeCodeUnits[offset];
charErrorBufferLength = 1;
cr[0] = CoderResult.OVERFLOW;
offset = 0;
break;
}
} else if (isFallbackUsed() ? (c & 0xfffe) == 0xe000 : c == 0xe000) {
/* output roundtrip BMP code point above 0xd800 or fallback BMP code point */
target.put(unicodeCodeUnits[offset]);
if (offsets != null) {
offsets.put(sourceIndex);
}
byteIndex = 0;
} else if (c == 0xffff) {
/* callback(illegal) */
cr[0] = CoderResult.malformedForLength(byteIndex);
}
} else if (action == MBCS_STATE_VALID_DIRECT_20
|| (action == MBCS_STATE_FALLBACK_DIRECT_20 && isFallbackUsed())) {
entry = MBCS_ENTRY_FINAL_VALUE(entry);
/* output surrogate pair */
target.put((char)(0xd800 | (char)(entry >> 10)));
if (offsets != null) {
offsets.put(sourceIndex);
}
byteIndex = 0;
c = (char)(0xdc00 | (char)(entry & 0x3ff));
if (target.hasRemaining()) {
target.put(c);
if (offsets != null) {
offsets.put(sourceIndex);
}
} else {
/* target overflow */
charErrorBufferArray[0] = c;
charErrorBufferLength = 1;
cr[0] = CoderResult.OVERFLOW;
offset = 0;
break;
}
} else if (action == MBCS_STATE_CHANGE_ONLY) {
/*
* This serves as a state change without any output. It is useful for reading simple stateful
* encodings, for example using just Shift-In/Shift-Out codes. The 21 unused bits may later be used
* for more sophisticated state transitions.
*/
if (sharedData.mbcs.dbcsOnlyState == 0) {
byteIndex = 0;
} else {
/* SI/SO are illegal for DBCS-only conversion */
state = (byte)(mode); /* restore the previous state */
/* callback(illegal) */
cr[0] = CoderResult.malformedForLength(byteIndex);
}
} else if (action == MBCS_STATE_FALLBACK_DIRECT_16) {
if (isFallbackUsed()) {
/* output BMP code point */
target.put(MBCS_ENTRY_FINAL_VALUE_16(entry));
if (offsets != null) {
offsets.put(sourceIndex);
}
byteIndex = 0;
}
} else if (action == MBCS_STATE_UNASSIGNED) {
/* just fall through */
} else if (action == MBCS_STATE_ILLEGAL) {
/* callback(illegal) */
cr[0] = CoderResult.malformedForLength(byteIndex);
} else {
/* reserved, must never occur */
byteIndex = 0;
}
/* end of action codes: prepare for a new character */
offset = 0;
if (byteIndex == 0) {
sourceIndex = nextSourceIndex;
} else if (cr[0].isError()) {
/* callback(illegal) */
if (byteIndex > 1) {
/*
* Ticket 5691: consistent illegal sequences:
* - We include at least the first byte in the illegal sequence.
* - If any of the non-initial bytes could be the start of a character,
* we stop the illegal sequence before the first one of those.
*/
boolean isDBCSOnly = (sharedData.mbcs.dbcsOnlyState != 0);
byte i;
for (i = 1; i < byteIndex && !isSingleOrLead(stateTable, state, isDBCSOnly, (short)(bytes[i] & UConverterConstants.UNSIGNED_BYTE_MASK)); i++) {}
if (i < byteIndex) {
byte backOutDistance = (byte)(byteIndex - i);
int bytesFromThisBuffer = sourceArrayIndex - sourceArrayIndexStart;
byteIndex = i; /* length of reported illegal byte sequence */
if (backOutDistance <= bytesFromThisBuffer) {
sourceArrayIndex -= backOutDistance;
} else {
/* Back out bytes from the previous buffer: Need to replay them. */
this.preToULength = (byte)(bytesFromThisBuffer - backOutDistance);
/* preToULength is negative! */
for (int n = 0; n < -this.preToULength; n++) {
this.preToUArray[n] = bytes[i+n];
}
sourceArrayIndex = sourceArrayIndexStart;
}
}
}
break;
} else /* unassigned sequences indicated with byteIndex>0 */{
/* try an extension mapping */
int sourceBeginIndex = sourceArrayIndex;
source.position(sourceArrayIndex);
byteIndex = toU(byteIndex, source, target, offsets, sourceIndex, flush, cr);
sourceArrayIndex = source.position();
sourceIndex = nextSourceIndex += (sourceArrayIndex - sourceBeginIndex);
if (cr[0].isError() || cr[0].isOverflow()) {
/* not mappable or buffer overflow */
break;
}
}
}
/* set the converter state back into UConverter */
toUnicodeStatus = offset;
mode = state;
toULength = byteIndex;
/* write back the updated pointers */
source.position(sourceArrayIndex);
return cr[0];
}
/*
* This version of cnvMBCSSingleToUnicodeWithOffsets() is optimized for single-byte, single-state codepages that
* only map to and from the BMP. In addition to single-byte optimizations, the offset calculations become much
* easier.
*/
private CoderResult cnvMBCSSingleToBMPWithOffsets(ByteBuffer source, CharBuffer target, IntBuffer offsets,
boolean flush) {
CoderResult[] cr = { CoderResult.UNDERFLOW };
int sourceArrayIndex, lastSource;
int targetCapacity, length;
int[][] stateTable;
int sourceIndex;
int entry;
byte action;
/* set up the local pointers */
sourceArrayIndex = source.position();
targetCapacity = target.remaining();
if ((options & UConverterConstants.OPTION_SWAP_LFNL) != 0) {
stateTable = sharedData.mbcs.swapLFNLStateTable;
} else {
stateTable = sharedData.mbcs.stateTable;
}
/* sourceIndex=-1 if the current character began in the previous buffer */
sourceIndex = 0;
lastSource = sourceArrayIndex;
/*
* since the conversion here is 1:1 UChar:uint8_t, we need only one counter for the minimum of the
* sourceLength and targetCapacity
*/
length = source.remaining();
if (length < targetCapacity) {
targetCapacity = length;
}
/* conversion loop */
while (targetCapacity > 0 && sourceArrayIndex < source.limit()) {
entry = stateTable[0][source.get(sourceArrayIndex++) & UConverterConstants.UNSIGNED_BYTE_MASK];
/* MBCS_ENTRY_IS_FINAL(entry) */
/* test the most common case first */
if (MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(entry)) {
/* output BMP code point */
target.put(MBCS_ENTRY_FINAL_VALUE_16(entry));
--targetCapacity;
continue;
}
/*
* An if-else-if chain provides more reliable performance for the most common cases compared to a
* switch.
*/
action = (byte) (MBCS_ENTRY_FINAL_ACTION(entry));
if (action == MBCS_STATE_FALLBACK_DIRECT_16) {
if (isFallbackUsed()) {
/* output BMP code point */
target.put(MBCS_ENTRY_FINAL_VALUE_16(entry));
--targetCapacity;
continue;
}
} else if (action == MBCS_STATE_UNASSIGNED) {
/* just fall through */
} else if (action == MBCS_STATE_ILLEGAL) {
/* callback(illegal) */
cr[0] = CoderResult.malformedForLength(sourceArrayIndex - lastSource);
} else {
/* reserved, must never occur */
continue;
}
/* set offsets since the start or the last extension */
if (offsets != null) {
int count = sourceArrayIndex - lastSource;
/* predecrement: do not set the offset for the callback-causing character */
while (--count > 0) {
offsets.put(sourceIndex++);
}
/* offset and sourceIndex are now set for the current character */
}
if (cr[0].isError()) {
/* callback(illegal) */
break;
} else /* unassigned sequences indicated with byteIndex>0 */{
/* try an extension mapping */
lastSource = sourceArrayIndex;
toUBytesArray[0] = source.get(sourceArrayIndex - 1);
source.position(sourceArrayIndex);
toULength = toU((byte) 1, source, target, offsets, sourceIndex, flush, cr);
sourceArrayIndex = source.position();
sourceIndex += 1 + (sourceArrayIndex - lastSource);
if (cr[0].isError()) {
/* not mappable or buffer overflow */
break;
}
/* recalculate the targetCapacity after an extension mapping */
targetCapacity = target.remaining();
length = source.remaining();
if (length < targetCapacity) {
targetCapacity = length;
}
}
}
if (!cr[0].isError() && sourceArrayIndex < source.limit() && !target.hasRemaining()) {
/* target is full */
cr[0] = CoderResult.OVERFLOW;
}
/* set offsets since the start or the last callback */
if (offsets != null) {
int count = sourceArrayIndex - lastSource;
while (count > 0) {
offsets.put(sourceIndex++);
--count;
}
}
/* write back the updated pointers */
source.position(sourceArrayIndex);
return cr[0];
}
/* This version of cnvMBCSToUnicodeWithOffsets() is optimized for single-byte, single-state codepages. */
private CoderResult cnvMBCSSingleToUnicodeWithOffsets(ByteBuffer source, CharBuffer target, IntBuffer offsets,
boolean flush) {
CoderResult[] cr = { CoderResult.UNDERFLOW };
int sourceArrayIndex;
int[][] stateTable;
int sourceIndex;
int entry;
char c;
byte action;
/* set up the local pointers */
sourceArrayIndex = source.position();
if ((options & UConverterConstants.OPTION_SWAP_LFNL) != 0) {
stateTable = sharedData.mbcs.swapLFNLStateTable;
} else {
stateTable = sharedData.mbcs.stateTable;
}
/* sourceIndex=-1 if the current character began in the previous buffer */
sourceIndex = 0;
/* conversion loop */
while (sourceArrayIndex < source.limit()) {
/*
* This following test is to see if available input would overflow the output. It does not catch output
* of more than one code unit that overflows as a result of a surrogate pair or callback output from the
* last source byte. Therefore, those situations also test for overflows and will then break the loop,
* too.
*/
if (!target.hasRemaining()) {
/* target is full */
cr[0] = CoderResult.OVERFLOW;
break;
}
entry = stateTable[0][source.get(sourceArrayIndex++) & UConverterConstants.UNSIGNED_BYTE_MASK];
/* MBCS_ENTRY_IS_FINAL(entry) */
/* test the most common case first */
if (MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(entry)) {
/* output BMP code point */
target.put(MBCS_ENTRY_FINAL_VALUE_16(entry));
if (offsets != null) {
offsets.put(sourceIndex);
}
/* normal end of action codes: prepare for a new character */
++sourceIndex;
continue;
}
/*
* An if-else-if chain provides more reliable performance for the most common cases compared to a
* switch.
*/
action = (byte) (MBCS_ENTRY_FINAL_ACTION(entry));
if (action == MBCS_STATE_VALID_DIRECT_20
|| (action == MBCS_STATE_FALLBACK_DIRECT_20 && isFallbackUsed())) {
entry = MBCS_ENTRY_FINAL_VALUE(entry);
/* output surrogate pair */
target.put((char) (0xd800 | (char) (entry >>> 10)));
if (offsets != null) {
offsets.put(sourceIndex);
}
c = (char) (0xdc00 | (char) (entry & 0x3ff));
if (target.hasRemaining()) {
target.put(c);
if (offsets != null) {
offsets.put(sourceIndex);
}
} else {
/* target overflow */
charErrorBufferArray[0] = c;
charErrorBufferLength = 1;
cr[0] = CoderResult.OVERFLOW;
break;
}
++sourceIndex;
continue;
} else if (action == MBCS_STATE_FALLBACK_DIRECT_16) {
if (isFallbackUsed()) {
/* output BMP code point */
target.put(MBCS_ENTRY_FINAL_VALUE_16(entry));
if (offsets != null) {
offsets.put(sourceIndex);
}
++sourceIndex;
continue;
}
} else if (action == MBCS_STATE_UNASSIGNED) {
/* just fall through */
} else if (action == MBCS_STATE_ILLEGAL) {
/* callback(illegal) */
cr[0] = CoderResult.malformedForLength(1);
} else {
/* reserved, must never occur */
++sourceIndex;
continue;
}
if (cr[0].isError()) {
/* callback(illegal) */
break;
} else /* unassigned sequences indicated with byteIndex>0 */{
/* try an extension mapping */
int sourceBeginIndex = sourceArrayIndex;
toUBytesArray[0] = source.get(sourceArrayIndex - 1);
source.position(sourceArrayIndex);
toULength = toU((byte) 1, source, target, offsets, sourceIndex, flush, cr);
sourceArrayIndex = source.position();
sourceIndex += 1 + (sourceArrayIndex - sourceBeginIndex);
if (cr[0].isError()) {
/* not mappable or buffer overflow */
break;
}
}
}
/* write back the updated pointers */
source.position(sourceArrayIndex);
return cr[0];
}
private int getFallback(UConverterMBCSTable mbcsTable, int offset) {
MBCSToUFallback[] toUFallbacks;
int i, start, limit;
limit = mbcsTable.countToUFallbacks;
if (limit > 0) {
/* do a binary search for the fallback mapping */
toUFallbacks = mbcsTable.toUFallbacks;
start = 0;
while (start < limit - 1) {
i = (start + limit) >>> 1;
if (offset < toUFallbacks[i].offset) {
limit = i;
} else {
start = i;
}
}
/* did we really find it? */
if (offset == toUFallbacks[start].offset) {
return toUFallbacks[start].codePoint;
}
}
return 0xfffe;
}
/**
* This is a simple version of _MBCSGetNextUChar() that is used by other converter implementations. It only
* returns an "assigned" result if it consumes the entire input. It does not use state from the converter, nor
* error codes. It does not handle the EBCDIC swaplfnl option (set in UConverter). It handles conversion
* extensions but not GB 18030.
*
* @return U+fffe unassigned U+ffff illegal otherwise the Unicode code point
*/
int simpleGetNextUChar(ByteBuffer source, boolean useFallback) {
// #if 0
// /*
// * Code disabled 2002dec09 (ICU 2.4) because it is not currently used in ICU. markus
// * TODO In future releases, verify that this function is never called for SBCS
// * conversions, i.e., that sharedData->mbcs.countStates==1 is still true.
// * Removal improves code coverage.
// */
// /* use optimized function if possible */
// if(sharedData->mbcs.countStates==1) {
// if(length==1) {
// return ucnv_MBCSSingleSimpleGetNextUChar(sharedData, (uint8_t)*source, useFallback);
// } else {
// return 0xffff; /* illegal: more than a single byte for an SBCS converter */
// }
// }
// #endif
/* set up the local pointers */
int[][] stateTable = sharedData.mbcs.stateTable;
char[] unicodeCodeUnits = sharedData.mbcs.unicodeCodeUnits;
/* converter state */
int offset = 0;
int state = sharedData.mbcs.dbcsOnlyState;
int action;
int entry;
int c;
int i = source.position();
int length = source.limit() - i;
/* conversion loop */
while (true) {
// entry=stateTable[state][(uint8_t)source[i++]];
entry = stateTable[state][source.get(i++) & UConverterConstants.UNSIGNED_BYTE_MASK];
if (MBCS_ENTRY_IS_TRANSITION(entry)) {
state = MBCS_ENTRY_TRANSITION_STATE(entry);
offset += MBCS_ENTRY_TRANSITION_OFFSET(entry);
if (i == source.limit()) {
return 0xffff; /* truncated character */
}
} else {
/*
* An if-else-if chain provides more reliable performance for the most common cases compared to a
* switch.
*/
action = MBCS_ENTRY_FINAL_ACTION(entry);
if (action == MBCS_STATE_VALID_16) {
offset += MBCS_ENTRY_FINAL_VALUE_16(entry);
c = unicodeCodeUnits[offset];
if (c != 0xfffe) {
/* done */
} else if (isToUUseFallback()) {
c = getFallback(sharedData.mbcs, offset);
}
/* else done with 0xfffe */
} else if (action == MBCS_STATE_VALID_DIRECT_16) {
// /* output BMP code point */
c = MBCS_ENTRY_FINAL_VALUE_16(entry);
} else if (action == MBCS_STATE_VALID_16_PAIR) {
offset += MBCS_ENTRY_FINAL_VALUE_16(entry);
c = unicodeCodeUnits[offset++];
if (c < 0xd800) {
/* output BMP code point below 0xd800 */
} else if (isToUUseFallback() ? c <= 0xdfff : c <= 0xdbff) {
/* output roundtrip or fallback supplementary code point */
c = (((c & 0x3ff) << 10) + unicodeCodeUnits[offset] + (0x10000 - 0xdc00));
} else if (isToUUseFallback() ? (c & 0xfffe) == 0xe000 : c == 0xe000) {
/* output roundtrip BMP code point above 0xd800 or fallback BMP code point */
c = unicodeCodeUnits[offset];
} else if (c == 0xffff) {
return 0xffff;
} else {
c = 0xfffe;
}
} else if (action == MBCS_STATE_VALID_DIRECT_20) {
/* output supplementary code point */
c = 0x10000 + MBCS_ENTRY_FINAL_VALUE(entry);
} else if (action == MBCS_STATE_FALLBACK_DIRECT_16) {
if (!isToUUseFallback(useFallback)) {
c = 0xfffe;
} else {
/* output BMP code point */
c = MBCS_ENTRY_FINAL_VALUE_16(entry);
}
} else if (action == MBCS_STATE_FALLBACK_DIRECT_20) {
if (!isToUUseFallback(useFallback)) {
c = 0xfffe;
} else {
/* output supplementary code point */
c = 0x10000 + MBCS_ENTRY_FINAL_VALUE(entry);
}
} else if (action == MBCS_STATE_UNASSIGNED) {
c = 0xfffe;
} else {
/*
* forbid MBCS_STATE_CHANGE_ONLY for this function, and MBCS_STATE_ILLEGAL and reserved action
* codes
*/
return 0xffff;
}
break;
}
}
if (i != source.limit()) {
/* illegal for this function: not all input consumed */
return 0xffff;
}
if (c == 0xfffe) {
/* try an extension mapping */
if (sharedData.mbcs.extIndexes != null) {
/* Increase the limit for proper handling. Used in LMBCS. */
if (source.limit() > i + length) {
source.limit(i + length);
}
return simpleMatchToU(source, useFallback);
}
}
return c;
}
private boolean hasValidTrailBytes(int[][] stateTable, short state) {
int[] row = stateTable[state];
int b, entry;
/* First test for final entries in this state for some commonly valid byte values. */
entry = row[0xa1];
if (!MBCS_ENTRY_IS_TRANSITION(entry) && MBCS_ENTRY_FINAL_ACTION(entry) != MBCS_STATE_ILLEGAL) {
return true;
}
entry = row[0x41];
if (!MBCS_ENTRY_IS_TRANSITION(entry) && MBCS_ENTRY_FINAL_ACTION(entry) != MBCS_STATE_ILLEGAL) {
return true;
}
/* Then test for final entries in this state. */
for (b = 0; b <= 0xff; b++) {
entry = row[b];
if (!MBCS_ENTRY_IS_TRANSITION(entry) && MBCS_ENTRY_FINAL_ACTION(entry) != MBCS_STATE_ILLEGAL) {
return true;
}
}
/* Then recurse for transition entries. */
for (b = 0; b <= 0xff; b++) {
entry = row[b];
if (MBCS_ENTRY_IS_TRANSITION(entry) &&
hasValidTrailBytes(stateTable, (short)MBCS_ENTRY_TRANSITION_STATE(entry))) {
return true;
}
}
return false;
}
private boolean isSingleOrLead(int[][] stateTable, int state, boolean isDBCSOnly, int b) {
int[] row = stateTable[state];
int entry = row[b];
if (MBCS_ENTRY_IS_TRANSITION(entry)) { /* lead byte */
return hasValidTrailBytes(stateTable, (short)MBCS_ENTRY_TRANSITION_STATE(entry));
} else {
int action = MBCS_ENTRY_FINAL_ACTION(entry);
if (action == MBCS_STATE_CHANGE_ONLY && isDBCSOnly) {
return false; /* SI/SO are illegal for DBCS-only conversion */
} else {
return (action != MBCS_STATE_ILLEGAL);
}
}
}
}
class CharsetEncoderMBCS extends CharsetEncoderICU {
private boolean allowReplacementChanges = false;
CharsetEncoderMBCS(CharsetICU cs) {
super(cs, fromUSubstitution);
allowReplacementChanges = true; // allow changes in implReplaceWith
implReset();
}
@Override
protected void implReset() {
super.implReset();
preFromUFirstCP = UConverterConstants.U_SENTINEL;
}
@Override
@SuppressWarnings("fallthrough")
protected CoderResult encodeLoop(CharBuffer source, ByteBuffer target, IntBuffer offsets, boolean flush) {
CoderResult[] cr = { CoderResult.UNDERFLOW };
// if (!source.hasRemaining() && fromUChar32 == 0)
// return cr[0];
int sourceArrayIndex;
char[] table;
byte[] pArray, bytes;
char[] chars;
int[] ints;
int pArrayIndex, outputType, c;
int prevSourceIndex, sourceIndex, nextSourceIndex;
int stage2Entry = 0, value = 0, length = 0, prevLength;
short uniMask;
// long asciiRoundtrips;
byte[] si_value = new byte[2];
byte[] so_value = new byte[2];
int si_value_length = 0, so_value_length = 0;
boolean gotoUnassigned = false;
try {
if (!flush && preFromUFirstCP >= 0) {
/*
* pass sourceIndex=-1 because we continue from an earlier buffer in the future, this may change
* with continuous offsets
*/
cr[0] = continueMatchFromU(source, target, offsets, flush, -1);
if (cr[0].isError() || preFromULength < 0) {
return cr[0];
}
}
/* use optimized function if possible */
outputType = sharedData.mbcs.outputType;
uniMask = sharedData.mbcs.unicodeMask;
if (outputType == MBCS_OUTPUT_1 && (uniMask & UConverterConstants.HAS_SURROGATES) == 0) {
if ((uniMask & UConverterConstants.HAS_SUPPLEMENTARY) == 0) {
cr[0] = cnvMBCSSingleFromBMPWithOffsets(source, target, offsets, flush);
} else {
cr[0] = cnvMBCSSingleFromUnicodeWithOffsets(source, target, offsets, flush);
}
return cr[0];
} else if (outputType == MBCS_OUTPUT_2) {
cr[0] = cnvMBCSDoubleFromUnicodeWithOffsets(source, target, offsets, flush);
return cr[0];
}
table = sharedData.mbcs.fromUnicodeTable;
int[] tableInts = sharedData.mbcs.fromUnicodeTableInts;
sourceArrayIndex = source.position();
bytes = sharedData.mbcs.fromUnicodeBytes;
ints = sharedData.mbcs.fromUnicodeInts;
if ((options & UConverterConstants.OPTION_SWAP_LFNL) != 0) {
chars = sharedData.mbcs.swapLFNLFromUnicodeChars;
} else {
chars = sharedData.mbcs.fromUnicodeChars;
}
// asciiRoundtrips = sharedData.mbcs.asciiRoundtrips;
/* get the converter state from UConverter */
c = fromUChar32;
if (outputType == MBCS_OUTPUT_2_SISO) {
prevLength = fromUnicodeStatus;
if (prevLength == 0) {
/* set the real value */
prevLength = 1;
}
} else {
/* prevent fromUnicodeStatus from being set to something non-0 */
prevLength = 0;
}
/* sourceIndex=-1 if the current character began in the previous buffer */
prevSourceIndex = -1;
sourceIndex = c == 0 ? 0 : -1;
nextSourceIndex = 0;
/* Get the SI/SO character for the converter */
si_value_length = getSISOBytes(SISO_Option.SI, options, si_value);
so_value_length = getSISOBytes(SISO_Option.SO, options, so_value);
/* conversion loop */
/*
* This is another piece of ugly code: A goto into the loop if the converter state contains a first
* surrogate from the previous function call. It saves me to check in each loop iteration a check of
* if(c==0) and duplicating the trail-surrogate-handling code in the else branch of that check. I could
* not find any other way to get around this other than using a function call for the conversion and
* callback, which would be even more inefficient.
*
* Markus Scherer 2000-jul-19
*/
boolean doloop = true;
boolean doread = true;
if (c != 0 && target.hasRemaining()) {
if (UTF16.isLeadSurrogate(c) && (uniMask & UConverterConstants.HAS_SURROGATES) == 0) {
// c is a lead surrogate, read another input
SideEffects x = new SideEffects(c, sourceArrayIndex, sourceIndex, nextSourceIndex,
prevSourceIndex, prevLength);
doloop = getTrail(source, target, uniMask, x, flush, cr);
doread = x.doread;
c = x.c;
sourceArrayIndex = x.sourceArrayIndex;
sourceIndex = x.sourceIndex;
nextSourceIndex = x.nextSourceIndex;
prevSourceIndex = x.prevSourceIndex;
prevLength = x.prevLength;
} else {
// c is not a lead surrogate, do not read another input
doread = false;
}
}
if (doloop) {
while (!doread || sourceArrayIndex < source.limit()) {
/*
* This following test is to see if available input would overflow the output. It does not catch
* output of more than one byte that overflows as a result of a multi-byte character or callback
* output from the last source character. Therefore, those situations also test for overflows
* and will then break the loop, too.
*/
if (target.hasRemaining()) {
/*
* Get a correct Unicode code point: a single UChar for a BMP code point or a matched
* surrogate pair for a "supplementary code point".
*/
if (doread) {
// doread might be false only on the first looping
c = source.get(sourceArrayIndex++);
++nextSourceIndex;
/*
* This also tests if the codepage maps single surrogates. If it does, then surrogates
* are not paired but mapped separately. Note that in this case unmatched surrogates are
* not detected.
*/
if (UTF16.isSurrogate(c)
&& (uniMask & UConverterConstants.HAS_SURROGATES) == 0) {
if (UTF16.isLeadSurrogate(c)) {
// getTrail:
SideEffects x = new SideEffects(c, sourceArrayIndex, sourceIndex,
nextSourceIndex, prevSourceIndex, prevLength);
doloop = getTrail(source, target, uniMask, x, flush, cr);
c = x.c;
sourceArrayIndex = x.sourceArrayIndex;
sourceIndex = x.sourceIndex;
nextSourceIndex = x.nextSourceIndex;
prevSourceIndex = x.prevSourceIndex;
if (x.doread) {
if (doloop)
continue;
else
break;
}
} else {
/* this is an unmatched trail code unit (2nd surrogate) */
/* callback(illegal) */
cr[0] = CoderResult.malformedForLength(1);
break;
}
}
} else {
doread = true;
}
/* convert the Unicode code point in c into codepage bytes */
/*
* The basic lookup is a triple-stage compact array (trie) lookup. For details see the
* beginning of this file.
*
* Single-byte codepages are handled with a different data structure by _MBCSSingle...
* functions.
*
* The result consists of a 32-bit value from stage 2 and a pointer to as many bytes as are
* stored per character. The pointer points to the character's bytes in stage 3. Bits 15..0
* of the stage 2 entry contain the stage 3 index for that pointer, while bits 31..16 are
* flags for which of the 16 characters in the block are roundtrip-assigned.
*
* For 2-byte and 4-byte codepages, the bytes are stored as uint16_t respectively as
* uint32_t, in the platform encoding. For 3-byte codepages, the bytes are always stored in
* big-endian order.
*
* For EUC encodings that use only either 0x8e or 0x8f as the first byte of their longest
* byte sequences, the first two bytes in this third stage indicate with their 7th bits
* whether these bytes are to be written directly or actually need to be preceded by one of
* the two Single-Shift codes. With this, the third stage stores one byte fewer per
* character than the actual maximum length of EUC byte sequences.
*
* Other than that, leading zero bytes are removed and the other bytes output. A single zero
* byte may be output if the "assigned" bit in stage 2 was on. The data structure does not
* support zero byte output as a fallback, and also does not allow output of leading zeros.
*/
stage2Entry = MBCS_STAGE_2_FROM_U(table, tableInts, c);
/* get the bytes and the length for the output */
switch (outputType) {
/* This is handled above with the method cnvMBCSDoubleFromUnicodeWithOffsets() */
/* case MBCS_OUTPUT_2:
value = MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, c);
if ((value & UConverterConstants.UNSIGNED_INT_MASK) <= 0xff) {
length = 1;
} else {
length = 2;
}
break; */
case MBCS_OUTPUT_2_SISO:
/* 1/2-byte stateful with Shift-In/Shift-Out */
/*
* Save the old state in the converter object right here, then change the local
* prevLength state variable if necessary. Then, if this character turns out to be
* unassigned or a fallback that is not taken, the callback code must not save the new
* state in the converter because the new state is for a character that is not output.
* However, the callback must still restore the state from the converter in case the
* callback function changed it for its output.
*/
fromUnicodeStatus = prevLength; /* save the old state */
value = MBCS_VALUE_2_FROM_STAGE_2(chars, stage2Entry, c);
if (value <= 0xff) {
if (value == 0 && MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c) == false) {
/* no mapping, leave value==0 */
length = 0;
} else if (prevLength <= 1) {
length = 1;
} else {
/* change from double-byte mode to single-byte */
if (si_value_length == 1) {
value|=si_value[0]<<8;
length = 2;
} else if (si_value_length == 2) {
value|=si_value[1]<<8;
value|=si_value[0]<<16;
length = 3;
}
prevLength = 1;
}
} else {
if (prevLength == 2) {
length = 2;
} else {
/* change from single-byte mode to double-byte */
if (so_value_length == 1) {
value|=so_value[0]<<16;
length = 3;
} else if (so_value_length == 2) {
value|=so_value[1]<<16;
value|=so_value[0]<<24;
length = 4;
}
prevLength = 2;
}
}
break;
case MBCS_OUTPUT_DBCS_ONLY:
/* table with single-byte results, but only DBCS mappings used */
value = MBCS_VALUE_2_FROM_STAGE_2(chars, stage2Entry, c);
if (value <= 0xff) {
/* no mapping or SBCS result, not taken for DBCS-only */
value = stage2Entry = 0; /* stage2Entry=0 to reset roundtrip flags */
length = 0;
} else {
length = 2;
}
break;
case MBCS_OUTPUT_3:
pArray = bytes;
pArrayIndex = MBCS_POINTER_3_FROM_STAGE_2(bytes, stage2Entry, c);
value = ((pArray[pArrayIndex] & UConverterConstants.UNSIGNED_BYTE_MASK) << 16)
| ((pArray[pArrayIndex + 1] & UConverterConstants.UNSIGNED_BYTE_MASK) << 8)
| (pArray[pArrayIndex + 2] & UConverterConstants.UNSIGNED_BYTE_MASK);
if (value <= 0xff) {
length = 1;
} else if (value <= 0xffff) {
length = 2;
} else {
length = 3;
}
break;
case MBCS_OUTPUT_4:
value = MBCS_VALUE_4_FROM_STAGE_2(ints, stage2Entry, c);
if (value < 0) {
// Half of the 4-byte values look negative in a signed int.
length = 4;
} else if (value <= 0xff) {
length = 1;
} else if (value <= 0xffff) {
length = 2;
} else if (value <= 0xffffff) {
length = 3;
} else {
length = 4;
}
break;
case MBCS_OUTPUT_3_EUC:
value = MBCS_VALUE_2_FROM_STAGE_2(chars, stage2Entry, c);
/* EUC 16-bit fixed-length representation */
if (value <= 0xff) {
length = 1;
} else if ((value & 0x8000) == 0) {
value |= 0x8e8000;
length = 3;
} else if ((value & 0x80) == 0) {
value |= 0x8f0080;
length = 3;
} else {
length = 2;
}
break;
case MBCS_OUTPUT_4_EUC:
pArray = bytes;
pArrayIndex = MBCS_POINTER_3_FROM_STAGE_2(bytes, stage2Entry, c);
value = ((pArray[pArrayIndex] & UConverterConstants.UNSIGNED_BYTE_MASK) << 16)
| ((pArray[pArrayIndex + 1] & UConverterConstants.UNSIGNED_BYTE_MASK) << 8)
| (pArray[pArrayIndex + 2] & UConverterConstants.UNSIGNED_BYTE_MASK);
/* EUC 16-bit fixed-length representation applied to the first two bytes */
if (value <= 0xff) {
length = 1;
} else if (value <= 0xffff) {
length = 2;
} else if ((value & 0x800000) == 0) {
value |= 0x8e800000;
length = 4;
} else if ((value & 0x8000) == 0) {
value |= 0x8f008000;
length = 4;
} else {
length = 3;
}
break;
default:
/* must not occur */
/*
* To avoid compiler warnings that value & length may be used without having been
* initialized, we set them here. In reality, this is unreachable code. Not having a
* default branch also causes warnings with some compilers.
*/
value = stage2Entry = 0; /* stage2Entry=0 to reset roundtrip flags */
length = 0;
break;
}
/* is this code point assigned, or do we use fallbacks? */
if (gotoUnassigned || (!(MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c) || (isFromUUseFallback(c) && value != 0)))) {
gotoUnassigned = false;
/*
* We allow a 0 byte output if the "assigned" bit is set for this entry. There is no way
* with this data structure for fallback output to be a zero byte.
*/
// unassigned:
SideEffects x = new SideEffects(c, sourceArrayIndex, sourceIndex, nextSourceIndex,
prevSourceIndex, prevLength);
doloop = unassigned(source, target, offsets, x, flush, cr);
c = x.c;
sourceArrayIndex = x.sourceArrayIndex;
sourceIndex = x.sourceIndex;
nextSourceIndex = x.nextSourceIndex;
prevSourceIndex = x.prevSourceIndex;
prevLength = x.prevLength;
if (doloop)
continue;
else
break;
}
/* write the output character bytes from value and length */
/* from the first if in the loop we know that targetCapacity>0 */
if (length <= target.remaining()) {
switch (length) {
/* each branch falls through to the next one */
case 4:
target.put((byte) (value >>> 24));
if (offsets != null) {
offsets.put(sourceIndex);
}
case 3:
target.put((byte) (value >>> 16));
if (offsets != null) {
offsets.put(sourceIndex);
}
case 2:
target.put((byte) (value >>> 8));
if (offsets != null) {
offsets.put(sourceIndex);
}
case 1:
target.put((byte) value);
if (offsets != null) {
offsets.put(sourceIndex);
}
default:
/* will never occur */
break;
}
} else {
int errorBufferArrayIndex;
/*
* We actually do this backwards here: In order to save an intermediate variable, we
* output first to the overflow buffer what does not fit into the regular target.
*/
/* we know that 1<=targetCapacity>> 16);
case 2:
errorBuffer[errorBufferArrayIndex++] = (byte) (value >>> 8);
case 1:
errorBuffer[errorBufferArrayIndex] = (byte) value;
default:
/* will never occur */
break;
}
errorBufferLength = (byte) length;
/* now output what fits into the regular target */
value >>>= 8 * length; /* length was reduced by targetCapacity */
switch (target.remaining()) {
/* each branch falls through to the next one */
case 3:
target.put((byte) (value >>> 16));
if (offsets != null) {
offsets.put(sourceIndex);
}
case 2:
target.put((byte) (value >>> 8));
if (offsets != null) {
offsets.put(sourceIndex);
}
case 1:
target.put((byte) value);
if (offsets != null) {
offsets.put(sourceIndex);
}
default:
/* will never occur */
break;
}
/* target overflow */
cr[0] = CoderResult.OVERFLOW;
c = 0;
break;
}
/* normal end of conversion: prepare for a new character */
c = 0;
if (offsets != null) {
prevSourceIndex = sourceIndex;
sourceIndex = nextSourceIndex;
}
continue;
} else {
/* target is full */
cr[0] = CoderResult.OVERFLOW;
break;
}
}
}
/*
* the end of the input stream and detection of truncated input are handled by the framework, but for
* EBCDIC_STATEFUL conversion we need to emit an SI at the very end
*
* conditions: successful EBCDIC_STATEFUL in DBCS mode end of input and no truncated input
*/
if (outputType == MBCS_OUTPUT_2_SISO && prevLength == 2 && flush && sourceArrayIndex >= source.limit()
&& c == 0) {
/* EBCDIC_STATEFUL ending with DBCS: emit an SI to return the output stream to SBCS */
if (target.hasRemaining()) {
target.put(si_value[0]);
if (si_value_length == 2) {
if (target.remaining() > 0) {
target.put(si_value[1]);
} else {
errorBuffer[0] = si_value[1];
errorBufferLength = 1;
cr[0] = CoderResult.OVERFLOW;
}
}
if (offsets != null) {
/* set the last source character's index (sourceIndex points at sourceLimit now) */
offsets.put(prevSourceIndex);
}
} else {
/* target is full */
errorBuffer[0] = si_value[0];
if (si_value_length == 2) {
errorBuffer[1] = si_value[1];
}
errorBufferLength = si_value_length;
cr[0] = CoderResult.OVERFLOW;
}
prevLength = 1; /* we switched into SBCS */
}
/* set the converter state back into UConverter */
fromUChar32 = c;
fromUnicodeStatus = prevLength;
source.position(sourceArrayIndex);
} catch (BufferOverflowException ex) {
cr[0] = CoderResult.OVERFLOW;
}
return cr[0];
}
/*
* This is another simple conversion function for internal use by other conversion implementations. It does not
* use the converter state nor call callbacks. It does not handle the EBCDIC swaplfnl option (set in
* UConverter). It handles conversion extensions but not GB 18030.
*
* It converts one single Unicode code point into codepage bytes, encoded as one 32-bit value. The function
* returns the number of bytes in *pValue: 1..4 the number of bytes in *pValue 0 unassigned (*pValue undefined)
* -1 illegal (currently not used, *pValue undefined)
*
* *pValue will contain the resulting bytes with the last byte in bits 7..0, the second to last byte in bits
* 15..8, etc. Currently, the function assumes but does not check that 0<=c<=0x10ffff.
*/
int fromUChar32(int c, int[] pValue, boolean isUseFallback) {
// #if 0
// /* #if 0 because this is not currently used in ICU - reduce code, increase code coverage */
// const uint8_t *p;
// #endif
char[] table;
int stage2Entry;
int value;
int length;
int p;
/* BMP-only codepages are stored without stage 1 entries for supplementary code points */
if (c <= 0xffff || sharedData.mbcs.hasSupplementary()) {
table = sharedData.mbcs.fromUnicodeTable;
/* convert the Unicode code point in c into codepage bytes (same as in _MBCSFromUnicodeWithOffsets) */
if (sharedData.mbcs.outputType == MBCS_OUTPUT_1) {
value = MBCS_SINGLE_RESULT_FROM_U(table, sharedData.mbcs.fromUnicodeChars, c);
/* is this code point assigned, or do we use fallbacks? */
if (isUseFallback ? value >= 0x800 : value >= 0xc00) {
pValue[0] = value & 0xff;
return 1;
}
} else /* outputType!=MBCS_OUTPUT_1 */{
int[] tableInts = sharedData.mbcs.fromUnicodeTableInts;
stage2Entry = MBCS_STAGE_2_FROM_U(table, tableInts, c);
/* get the bytes and the length for the output */
switch (sharedData.mbcs.outputType) {
case MBCS_OUTPUT_2:
value = MBCS_VALUE_2_FROM_STAGE_2(sharedData.mbcs.fromUnicodeChars, stage2Entry, c);
if (value <= 0xff) {
length = 1;
} else {
length = 2;
}
break;
// #if 0
// /* #if 0 because this is not currently used in ICU - reduce code, increase code coverage */
// case MBCS_OUTPUT_DBCS_ONLY:
// /* table with single-byte results, but only DBCS mappings used */
// value=MBCS_VALUE_2_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c);
// if(value<=0xff) {
// /* no mapping or SBCS result, not taken for DBCS-only */
// value=stage2Entry=0; /* stage2Entry=0 to reset roundtrip flags */
// length=0;
// } else {
// length=2;
// }
// break;
case MBCS_OUTPUT_3:
byte[] bytes = sharedData.mbcs.fromUnicodeBytes;
p = CharsetMBCS.MBCS_POINTER_3_FROM_STAGE_2(bytes, stage2Entry, c);
value = ((bytes[p] & UConverterConstants.UNSIGNED_BYTE_MASK)<<16) |
((bytes[p+1] & UConverterConstants.UNSIGNED_BYTE_MASK)<<8) |
(bytes[p+2] & UConverterConstants.UNSIGNED_BYTE_MASK);
if (value <= 0xff) {
length = 1;
} else if (value <= 0xffff) {
length = 2;
} else {
length = 3;
}
break;
// case MBCS_OUTPUT_4:
// value=MBCS_VALUE_4_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c);
// if(value<=0xff) {
// length=1;
// } else if(value<=0xffff) {
// length=2;
// } else if(value<=0xffffff) {
// length=3;
// } else {
// length=4;
// }
// break;
// case MBCS_OUTPUT_3_EUC:
// value=MBCS_VALUE_2_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c);
// /* EUC 16-bit fixed-length representation */
// if(value<=0xff) {
// length=1;
// } else if((value&0x8000)==0) {
// value|=0x8e8000;
// length=3;
// } else if((value&0x80)==0) {
// value|=0x8f0080;
// length=3;
// } else {
// length=2;
// }
// break;
// case MBCS_OUTPUT_4_EUC:
// p=MBCS_POINTER_3_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c);
// value=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2];
// /* EUC 16-bit fixed-length representation applied to the first two bytes */
// if(value<=0xff) {
// length=1;
// } else if(value<=0xffff) {
// length=2;
// } else if((value&0x800000)==0) {
// value|=0x8e800000;
// length=4;
// } else if((value&0x8000)==0) {
// value|=0x8f008000;
// length=4;
// } else {
// length=3;
// }
// break;
// #endif
default:
/* must not occur */
return -1;
}
/* is this code point assigned, or do we use fallbacks? */
if (MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c)
|| (CharsetEncoderICU.isFromUUseFallback(isUseFallback, c) && value != 0)) {
/*
* We allow a 0 byte output if the "assigned" bit is set for this entry. There is no way with
* this data structure for fallback output to be a zero byte.
*/
/* assigned */
pValue[0] = value;
return length;
}
}
}
if (sharedData.mbcs.extIndexes != null) {
length = simpleMatchFromU(c, pValue, isUseFallback);
return length >= 0 ? length : -length; /* return abs(length); */
}
/* unassigned */
return 0;
}
/*
* continue partial match with new input, requires cnv->preFromUFirstCP>=0 never called for simple,
* single-character conversion
*/
private CoderResult continueMatchFromU(CharBuffer source, ByteBuffer target, IntBuffer offsets, boolean flush,
int srcIndex) {
CoderResult cr = CoderResult.UNDERFLOW;
int[] value = new int[1];
int match;
match = matchFromU(preFromUFirstCP, preFromUArray, preFromUBegin, preFromULength, source, value, useFallback, flush);
if (match >= 2) {
match -= 2; /* remove 2 for the initial code point */
if (match >= preFromULength) {
/* advance src pointer for the consumed input */
source.position(source.position() + match - preFromULength);
preFromULength = 0;
} else {
/* the match did not use all of preFromU[] - keep the rest for replay */
int length = preFromULength - match;
System.arraycopy(preFromUArray, preFromUBegin + match, preFromUArray, preFromUBegin, length);
preFromULength = (byte) -length;
}
/* finish the partial match */
preFromUFirstCP = UConverterConstants.U_SENTINEL;
/* write result */
writeFromU(value[0], target, offsets, srcIndex);
} else if (match < 0) {
/* save state for partial match */
int sArrayIndex;
int j;
/* just _append_ the newly consumed input to preFromU[] */
sArrayIndex = source.position();
match = -match - 2; /* remove 2 for the initial code point */
for (j = preFromULength; j < match; ++j) {
preFromUArray[j] = source.get(sArrayIndex++);
}
source.position(sArrayIndex); /* same as *src=srcLimit; because we reached the end of input */
preFromULength = (byte) match;
} else { /* match==0 or 1 */
/*
* no match
*
* We need to split the previous input into two parts:
*
* 1. The first code point is unmappable - that's how we got into trying the extension data in the first
* place. We need to move it from the preFromU buffer to the error buffer, set an error code, and
* prepare the rest of the previous input for 2.
*
* 2. The rest of the previous input must be converted once we come back from the callback for the first
* code point. At that time, we have to try again from scratch to convert these input characters. The
* replay will be handled by the ucnv.c conversion code.
*/
if (match == 1) {
/* matched, no mapping but request for */
useSubChar1 = true;
}
/* move the first code point to the error field */
fromUChar32 = preFromUFirstCP;
preFromUFirstCP = UConverterConstants.U_SENTINEL;
/* mark preFromU for replay */
preFromULength = (byte) -preFromULength;
/* set the error code for unassigned */
// TODO: figure out what the unmappable length really should be
cr = CoderResult.unmappableForLength(1);
}
return cr;
}
/**
* @param cx
* pointer to extension data; if NULL, returns 0
* @param firstCP
* the first code point before all the other UChars
* @param pre
* UChars that must match; !initialMatch: partial match with them
* @param preLength
* length of pre, >=0
* @param src
* UChars that can be used to complete a match
* @param srcLength
* length of src, >=0
* @param pMatchValue
* [out] output result value for the match from the data structure
* @param useFallback
* "use fallback" flag, usually from cnv->useFallback
* @param flush
* true if the end of the input stream is reached
* @return >1: matched, return value=total match length (number of input units matched) 1: matched, no mapping
* but request for (only for the first code point) 0: no match <0: partial match, return
* value=negative total match length (partial matches are never returned for flush==true) (partial
* matches are never returned as being longer than UCNV_EXT_MAX_UCHARS) the matchLength is 2 if only
* firstCP matched, and >2 if firstCP and further code units matched
*/
// static int32_t ucnv_extMatchFromU(const int32_t *cx, UChar32 firstCP, const UChar *pre, int32_t preLength,
// const UChar *src, int32_t srcLength, uint32_t *pMatchValue, UBool useFallback, UBool flush)
private int matchFromU(int firstCP, char[] preArray, int preArrayBegin, int preLength, CharBuffer source,
int[] pMatchValue, boolean isUseFallback, boolean flush) {
ByteBuffer cx = sharedData.mbcs.extIndexes;
CharBuffer stage12, stage3;
IntBuffer stage3b;
CharBuffer fromUTableUChars, fromUSectionUChars;
IntBuffer fromUTableValues, fromUSectionValues;
int value, matchValue;
int i, j, index, length, matchLength;
char c;
if (cx == null) {
return 0; /* no extension data, no match */
}
/* trie lookup of firstCP */
index = firstCP >>> 10; /* stage 1 index */
if (index >= cx.asIntBuffer().get(EXT_FROM_U_STAGE_1_LENGTH)) {
return 0; /* the first code point is outside the trie */
}
stage12 = (CharBuffer) ARRAY(cx, EXT_FROM_U_STAGE_12_INDEX, char.class);
stage3 = (CharBuffer) ARRAY(cx, EXT_FROM_U_STAGE_3_INDEX, char.class);
index = FROM_U(stage12, stage3, index, firstCP);
stage3b = (IntBuffer) ARRAY(cx, EXT_FROM_U_STAGE_3B_INDEX, int.class);
value = stage3b.get(stage3b.position() + index);
if (value == 0) {
return 0;
}
if (TO_U_IS_PARTIAL(value)) {
/* partial match, enter the loop below */
index = FROM_U_GET_PARTIAL_INDEX(value);
/* initialize */
fromUTableUChars = (CharBuffer) ARRAY(cx, EXT_FROM_U_UCHARS_INDEX, char.class);
fromUTableValues = (IntBuffer) ARRAY(cx, EXT_FROM_U_VALUES_INDEX, int.class);
matchValue = 0;
i = j = matchLength = 0;
/* we must not remember fallback matches when not using fallbacks */
/* match input units until there is a full match or the input is consumed */
for (;;) {
/* go to the next section */
int oldpos = fromUTableUChars.position();
fromUSectionUChars = ((CharBuffer) fromUTableUChars.position(index)).slice();
fromUTableUChars.position(oldpos);
oldpos = fromUTableValues.position();
fromUSectionValues = ((IntBuffer) fromUTableValues.position(index)).slice();
fromUTableValues.position(oldpos);
/* read first pair of the section */
length = fromUSectionUChars.get();
value = fromUSectionValues.get();
if (value != 0 && (FROM_U_IS_ROUNDTRIP(value) || isFromUUseFallback(isUseFallback, firstCP))) {
/* remember longest match so far */
matchValue = value;
matchLength = 2 + i + j;
}
/* match pre[] then src[] */
if (i < preLength) {
c = preArray[preArrayBegin + i++];
} else if (source != null && j < source.remaining()) {
c = source.get(source.position() + j++);
} else {
/* all input consumed, partial match */
if (flush || (length = (i + j)) > MAX_UCHARS) {
/*
* end of the entire input stream, stop with the longest match so far or: partial match must
* not be longer than UCNV_EXT_MAX_UCHARS because it must fit into state buffers
*/
break;
} else {
/* continue with more input next time */
return -(2 + length);
}
}
/* search for the current UChar */
index = findFromU(fromUSectionUChars, length, c);
if (index < 0) {
/* no match here, stop with the longest match so far */
break;
} else {
value = fromUSectionValues.get(fromUSectionValues.position() + index);
if (FROM_U_IS_PARTIAL(value)) {
/* partial match, continue */
index = FROM_U_GET_PARTIAL_INDEX(value);
} else {
if (FROM_U_IS_ROUNDTRIP(value) || isFromUUseFallback(isUseFallback, firstCP)) {
/* full match, stop with result */
matchValue = value;
matchLength = 2 + i + j;
} else {
/* full match on fallback not taken, stop with the longest match so far */
}
break;
}
}
}
if (matchLength == 0) {
/* no match at all */
return 0;
}
} else /* result from firstCP trie lookup */{
if (FROM_U_IS_ROUNDTRIP(value) || isFromUUseFallback(isUseFallback, firstCP)) {
/* full match, stop with result */
matchValue = value;
matchLength = 2;
} else {
/* fallback not taken */
return 0;
}
}
if ((matchValue & FROM_U_RESERVED_MASK) != 0) {
/* do not interpret values with reserved bits used, for forward compatibility */
return 0;
}
/* return result */
if (matchValue == FROM_U_SUBCHAR1) {
return 1; /* assert matchLength==2 */
}
pMatchValue[0] = FROM_U_MASK_ROUNDTRIP(matchValue);
return matchLength;
}
private int simpleMatchFromU(int cp, int[] pValue, boolean isUseFallback) {
int[] value = new int[1];
int match; // signed
/* try to match */
match = matchFromU(cp, null, 0, 0, null, value, isUseFallback, true);
if (match >= 2) {
/* write result for simple, single-character conversion */
int length;
boolean isRoundtrip;
isRoundtrip = FROM_U_IS_ROUNDTRIP(value[0]);
length = FROM_U_GET_LENGTH(value[0]);
value[0] = FROM_U_GET_DATA(value[0]);
if (length <= EXT_FROM_U_MAX_DIRECT_LENGTH) {
pValue[0] = value[0];
return isRoundtrip ? length : -length;
// #if 0 /* not currently used */
// } else if(length==4) {
// /* de-serialize a 4-byte result */
// const uint8_t *result=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_BYTES_INDEX, uint8_t)+value;
// *pValue=
// ((uint32_t)result[0]<<24)|
// ((uint32_t)result[1]<<16)|
// ((uint32_t)result[2]<<8)|
// result[3];
// return isRoundtrip ? 4 : -4;
// #endif
}
}
/*
* return no match because - match>1 && resultLength>4: result too long for simple conversion - match==1: no
* match found, preferred - match==0: no match found in the first place - match<0: partial
* match, not supported for simple conversion (and flush==true)
*/
return 0;
}
@SuppressWarnings("fallthrough")
private CoderResult writeFromU(int value, ByteBuffer target, IntBuffer offsets, int srcIndex) {
ByteBuffer cx = sharedData.mbcs.extIndexes;
byte bufferArray[] = new byte[1 + MAX_BYTES];
int bufferArrayIndex = 0;
byte[] resultArray;
int resultArrayIndex;
int length, prevLength;
length = FROM_U_GET_LENGTH(value);
value = FROM_U_GET_DATA(value);
/* output the result */
if (length <= FROM_U_MAX_DIRECT_LENGTH) {
/*
* Generate a byte array and then write it below. This is not the fastest possible way, but it should be
* ok for extension mappings, and it is much simpler. Offset and overflow handling are only done once
* this way.
*/
int p = bufferArrayIndex + 1; /* reserve buffer[0] for shiftByte below */
switch (length) {
case 3:
bufferArray[p++] = (byte) (value >>> 16);
case 2:
bufferArray[p++] = (byte) (value >>> 8);
case 1:
bufferArray[p++] = (byte) value;
default:
break; /* will never occur */
}
resultArray = bufferArray;
resultArrayIndex = bufferArrayIndex + 1;
} else {
byte[] slice = new byte[length];
ByteBuffer bb = ((ByteBuffer) ARRAY(cx, EXT_FROM_U_BYTES_INDEX, byte.class));
bb.position(value);
bb.get(slice, 0, slice.length);
resultArray = slice;
resultArrayIndex = 0;
}
/* with correct data we have length>0 */
if ((prevLength = fromUnicodeStatus) != 0) {
/* handle SI/SO stateful output */
byte shiftByte;
if (prevLength > 1 && length == 1) {
/* change from double-byte mode to single-byte */
shiftByte = (byte) UConverterConstants.SI;
fromUnicodeStatus = 1;
} else if (prevLength == 1 && length > 1) {
/* change from single-byte mode to double-byte */
shiftByte = (byte) UConverterConstants.SO;
fromUnicodeStatus = 2;
} else {
shiftByte = 0;
}
if (shiftByte != 0) {
/* prepend the shift byte to the result bytes */
bufferArray[0] = shiftByte;
if (resultArray != bufferArray || resultArrayIndex != bufferArrayIndex + 1) {
System.arraycopy(resultArray, resultArrayIndex, bufferArray, bufferArrayIndex + 1, length);
}
resultArray = bufferArray;
resultArrayIndex = bufferArrayIndex;
++length;
}
}
return fromUWriteBytes(this, resultArray, resultArrayIndex, length, target, offsets, srcIndex);
}
/*
* @return if(U_FAILURE) return the code point for cnv->fromUChar32 else return 0 after output has been written
* to the target
*/
private int fromU(int cp, CharBuffer source, ByteBuffer target, IntBuffer offsets, int sourceIndex,
int length, boolean flush, CoderResult[] cr) {
// ByteBuffer cx;
useSubChar1 = false;
if (sharedData.mbcs.extIndexes != null
&& initialMatchFromU(cp, source, target, offsets, sourceIndex, flush, cr)) {
return 0; /* an extension mapping handled the input */
}
/* GB 18030 */
if ((options & MBCS_OPTION_GB18030) != 0) {
int[] range;
int i;
for (i = 0; i < gb18030Ranges.length; ++i) {
range = gb18030Ranges[i];
if (range[0] <= cp && cp <= range[1]) {
/* found the Unicode code point, output the four-byte sequence for it */
int linear;
byte bytes[] = new byte[4];
/* get the linear value of the first GB 18030 code in this range */
linear = range[2] - LINEAR_18030_BASE;
/* add the offset from the beginning of the range */
linear += (cp - range[0]);
bytes[3] = (byte) (0x30 + linear % 10);
linear /= 10;
bytes[2] = (byte) (0x81 + linear % 126);
linear /= 126;
bytes[1] = (byte) (0x30 + linear % 10);
linear /= 10;
bytes[0] = (byte) (0x81 + linear);
/* output this sequence */
cr[0] = fromUWriteBytes(this, bytes, 0, 4, target, offsets, sourceIndex);
return 0;
}
}
}
/* no mapping */
cr[0] = CoderResult.unmappableForLength(length);
return cp;
}
/*
* target= 2
&& !(FROM_U_GET_LENGTH(value[0]) == 1 && sharedData.mbcs.outputType == MBCS_OUTPUT_DBCS_ONLY)) {
/* advance src pointer for the consumed input */
source.position(source.position() + match - 2); /* remove 2 for the initial code point */
/* write result to target */
cr[0] = writeFromU(value[0], target, offsets, srcIndex);
return true;
} else if (match < 0) {
/* save state for partial match */
int sArrayIndex;
int j;
/* copy the first code point */
preFromUFirstCP = cp;
/* now copy the newly consumed input */
sArrayIndex = source.position();
match = -match - 2; /* remove 2 for the initial code point */
for (j = 0; j < match; ++j) {
preFromUArray[j] = source.get(sArrayIndex++);
}
source.position(sArrayIndex); /* same as *src=srcLimit; because we reached the end of input */
preFromULength = (byte) match;
return true;
} else if (match == 1) {
/* matched, no mapping but request for */
useSubChar1 = true;
return false;
} else /* match==0 no match */{
return false;
}
}
CoderResult cnvMBCSFromUnicodeWithOffsets(CharBuffer source, ByteBuffer target, IntBuffer offsets, boolean flush) {
// Just call encodeLoop to remove duplicate code.
return encodeLoop(source, target, offsets, flush);
}
/*
* This version of ucnv_MBCSFromUnicode() is optimized for single-byte codepages that map only to and from the
* BMP. In addition to single-byte/state optimizations, the offset calculations become much easier.
*/
private CoderResult cnvMBCSSingleFromBMPWithOffsets(CharBuffer source, ByteBuffer target, IntBuffer offsets,
boolean flush) {
CoderResult[] cr = { CoderResult.UNDERFLOW };
int sourceArrayIndex, lastSource;
int targetCapacity, length;
char[] table;
char[] results;
int c, sourceIndex;
char value, minValue;
/* set up the local pointers */
sourceArrayIndex = source.position();
targetCapacity = target.remaining();
table = sharedData.mbcs.fromUnicodeTable;
if ((options & UConverterConstants.OPTION_SWAP_LFNL) != 0) {
results = sharedData.mbcs.swapLFNLFromUnicodeChars;
} else {
results = sharedData.mbcs.fromUnicodeChars;
}
if (useFallback) {
/* use all roundtrip and fallback results */
minValue = 0x800;
} else {
/* use only roundtrips and fallbacks from private-use characters */
minValue = 0xc00;
}
/* get the converter state from UConverter */
c = fromUChar32;
/* sourceIndex=-1 if the current character began in the previous buffer */
sourceIndex = c == 0 ? 0 : -1;
lastSource = sourceArrayIndex;
/*
* since the conversion here is 1:1 UChar:uint8_t, we need only one counter for the minimum of the
* sourceLength and targetCapacity
*/
length = source.limit() - sourceArrayIndex;
if (length < targetCapacity) {
targetCapacity = length;
}
boolean doloop = true;
if (c != 0 && targetCapacity > 0) {
SideEffectsSingleBMP x = new SideEffectsSingleBMP(c, sourceArrayIndex);
doloop = getTrailSingleBMP(source, x, cr);
c = x.c;
sourceArrayIndex = x.sourceArrayIndex;
}
if (doloop) {
while (targetCapacity > 0) {
/*
* Get a correct Unicode code point: a single UChar for a BMP code point or a matched surrogate pair
* for a "supplementary code point".
*/
c = source.get(sourceArrayIndex++);
/*
* Do not immediately check for single surrogates: Assume that they are unassigned and check for
* them in that case. This speeds up the conversion of assigned characters.
*/
/* convert the Unicode code point in c into codepage bytes */
value = MBCS_SINGLE_RESULT_FROM_U(table, results, c);
/* is this code point assigned, or do we use fallbacks? */
if (value >= minValue) {
/* assigned, write the output character bytes from value and length */
/* length==1 */
/* this is easy because we know that there is enough space */
target.put((byte) value);
--targetCapacity;
/* normal end of conversion: prepare for a new character */
c = 0;
continue;
} else if (!UTF16.isSurrogate(c)) {
/* normal, unassigned BMP character */
} else if (UTF16.isLeadSurrogate(c)) {
// getTrail:
SideEffectsSingleBMP x = new SideEffectsSingleBMP(c, sourceArrayIndex);
doloop = getTrailSingleBMP(source, x, cr);
c = x.c;
sourceArrayIndex = x.sourceArrayIndex;
if (!doloop)
break;
} else {
/* this is an unmatched trail code unit (2nd surrogate) */
/* callback(illegal) */
cr[0] = CoderResult.malformedForLength(1);
break;
}
/* c does not have a mapping */
/* get the number of code units for c to correctly advance sourceIndex */
length = UTF16.getCharCount(c);
/* set offsets since the start or the last extension */
if (offsets != null) {
int count = sourceArrayIndex - lastSource;
/* do not set the offset for this character */
count -= length;
while (count > 0) {
offsets.put(sourceIndex++);
--count;
}
/* offsets and sourceIndex are now set for the current character */
}
/* try an extension mapping */
lastSource = sourceArrayIndex;
source.position(sourceArrayIndex);
c = fromU(c, source, target, offsets, sourceIndex, length, flush, cr);
sourceArrayIndex = source.position();
sourceIndex += length + (sourceArrayIndex - lastSource);
lastSource = sourceArrayIndex;
if (cr[0].isError()) {
/* not mappable or buffer overflow */
break;
} else {
/* a mapping was written to the target, continue */
/* recalculate the targetCapacity after an extension mapping */
targetCapacity = target.remaining();
length = source.limit() - sourceArrayIndex;
if (length < targetCapacity) {
targetCapacity = length;
}
}
}
}
if (sourceArrayIndex < source.limit() && !target.hasRemaining()) {
/* target is full */
cr[0] = CoderResult.OVERFLOW;
}
/* set offsets since the start or the last callback */
if (offsets != null) {
int count = sourceArrayIndex - lastSource;
while (count > 0) {
offsets.put(sourceIndex++);
--count;
}
}
/* set the converter state back into UConverter */
fromUChar32 = c;
/* write back the updated pointers */
source.position(sourceArrayIndex);
return cr[0];
}
/* This version of ucnv_MBCSFromUnicodeWithOffsets() is optimized for single-byte codepages. */
private CoderResult cnvMBCSSingleFromUnicodeWithOffsets(CharBuffer source, ByteBuffer target,
IntBuffer offsets, boolean flush) {
CoderResult[] cr = { CoderResult.UNDERFLOW };
int sourceArrayIndex;
char[] table;
char[] results;
int c;
int sourceIndex, nextSourceIndex;
char value, minValue;
/* set up the local pointers */
short uniMask;
sourceArrayIndex = source.position();
table = sharedData.mbcs.fromUnicodeTable;
if ((options & UConverterConstants.OPTION_SWAP_LFNL) != 0) {
results = sharedData.mbcs.swapLFNLFromUnicodeChars;
} else {
results = sharedData.mbcs.fromUnicodeChars;
}
if (useFallback) {
/* use all roundtrip and fallback results */
minValue = 0x800;
} else {
/* use only roundtrips and fallbacks from private-use characters */
minValue = 0xc00;
}
// agljport:comment hasSupplementary only used in getTrail block which now simply repeats the mask operation
uniMask = sharedData.mbcs.unicodeMask;
/* get the converter state from UConverter */
c = fromUChar32;
/* sourceIndex=-1 if the current character began in the previous buffer */
sourceIndex = c == 0 ? 0 : -1;
nextSourceIndex = 0;
boolean doloop = true;
boolean doread = true;
if (c != 0 && target.hasRemaining()) {
if (UTF16.isLeadSurrogate(c)) {
SideEffectsDouble x = new SideEffectsDouble(c, sourceArrayIndex, sourceIndex, nextSourceIndex);
doloop = getTrailDouble(source, target, uniMask, x, flush, cr);
doread = x.doread;
c = x.c;
sourceArrayIndex = x.sourceArrayIndex;
sourceIndex = x.sourceIndex;
nextSourceIndex = x.nextSourceIndex;
} else {
doread = false;
}
}
if (doloop) {
while (!doread || sourceArrayIndex < source.limit()) {
/*
* This following test is to see if available input would overflow the output. It does not catch
* output of more than one byte that overflows as a result of a multi-byte character or callback
* output from the last source character. Therefore, those situations also test for overflows and
* will then break the loop, too.
*/
if (target.hasRemaining()) {
/*
* Get a correct Unicode code point: a single UChar for a BMP code point or a matched surrogate
* pair for a "supplementary code point".
*/
if (doread) {
c = source.get(sourceArrayIndex++);
++nextSourceIndex;
if (UTF16.isSurrogate(c)) {
if (UTF16.isLeadSurrogate(c)) {
// getTrail:
SideEffectsDouble x = new SideEffectsDouble(c, sourceArrayIndex, sourceIndex,
nextSourceIndex);
doloop = getTrailDouble(source, target, uniMask, x, flush, cr);
c = x.c;
sourceArrayIndex = x.sourceArrayIndex;
sourceIndex = x.sourceIndex;
nextSourceIndex = x.nextSourceIndex;
if (x.doread) {
if (doloop)
continue;
else
break;
}
} else {
/* this is an unmatched trail code unit (2nd surrogate) */
/* callback(illegal) */
cr[0] = CoderResult.malformedForLength(1);
break;
}
}
} else {
doread = true;
}
/* convert the Unicode code point in c into codepage bytes */
value = MBCS_SINGLE_RESULT_FROM_U(table, results, c);
/* is this code point assigned, or do we use fallbacks? */
if (value >= minValue) {
/* assigned, write the output character bytes from value and length */
/* length==1 */
/* this is easy because we know that there is enough space */
target.put((byte) value);
if (offsets != null) {
offsets.put(sourceIndex);
}
/* normal end of conversion: prepare for a new character */
c = 0;
sourceIndex = nextSourceIndex;
} else { /* unassigned */
/* try an extension mapping */
SideEffectsDouble x = new SideEffectsDouble(c, sourceArrayIndex, sourceIndex,
nextSourceIndex);
doloop = unassignedDouble(source, target, x, flush, cr);
c = x.c;
sourceArrayIndex = x.sourceArrayIndex;
sourceIndex = x.sourceIndex;
nextSourceIndex = x.nextSourceIndex;
if (!doloop)
break;
}
} else {
/* target is full */
cr[0] = CoderResult.OVERFLOW;
break;
}
}
}
/* set the converter state back into UConverter */
fromUChar32 = c;
/* write back the updated pointers */
source.position(sourceArrayIndex);
return cr[0];
}
/* This version of ucnv_MBCSFromUnicodeWithOffsets() is optimized for double-byte codepages. */
private CoderResult cnvMBCSDoubleFromUnicodeWithOffsets(CharBuffer source, ByteBuffer target,
IntBuffer offsets, boolean flush) {
CoderResult[] cr = { CoderResult.UNDERFLOW };
int sourceArrayIndex;
char[] table;
char[] chars;
int c, sourceIndex, nextSourceIndex;
int stage2Entry;
int value;
int length;
short uniMask;
/* use optimized function if possible */
uniMask = sharedData.mbcs.unicodeMask;
/* set up the local pointers */
sourceArrayIndex = source.position();
table = sharedData.mbcs.fromUnicodeTable;
int[] tableInts = sharedData.mbcs.fromUnicodeTableInts;
if ((options & UConverterConstants.OPTION_SWAP_LFNL) != 0) {
chars = sharedData.mbcs.swapLFNLFromUnicodeChars;
} else {
chars = sharedData.mbcs.fromUnicodeChars;
}
/* get the converter state from UConverter */
c = fromUChar32;
/* sourceIndex=-1 if the current character began in the previous buffer */
sourceIndex = c == 0 ? 0 : -1;
nextSourceIndex = 0;
/* conversion loop */
boolean doloop = true;
boolean doread = true;
if (c != 0 && target.hasRemaining()) {
if (UTF16.isLeadSurrogate(c)) {
SideEffectsDouble x = new SideEffectsDouble(c, sourceArrayIndex, sourceIndex, nextSourceIndex);
doloop = getTrailDouble(source, target, uniMask, x, flush, cr);
doread = x.doread;
c = x.c;
sourceArrayIndex = x.sourceArrayIndex;
sourceIndex = x.sourceIndex;
nextSourceIndex = x.nextSourceIndex;
} else {
doread = false;
}
}
if (doloop) {
while (!doread || sourceArrayIndex < source.limit()) {
/*
* This following test is to see if available input would overflow the output. It does not catch
* output of more than one byte that overflows as a result of a multi-byte character or callback
* output from the last source character. Therefore, those situations also test for overflows and
* will then break the loop, too.
*/
if (target.hasRemaining()) {
if (doread) {
/*
* Get a correct Unicode code point: a single UChar for a BMP code point or a matched
* surrogate pair for a "supplementary code point".
*/
c = source.get(sourceArrayIndex++);
++nextSourceIndex;
/*
* This also tests if the codepage maps single surrogates. If it does, then surrogates are
* not paired but mapped separately. Note that in this case unmatched surrogates are not
* detected.
*/
if (UTF16.isSurrogate(c) && (uniMask & UConverterConstants.HAS_SURROGATES) == 0) {
if (UTF16.isLeadSurrogate(c)) {
// getTrail:
SideEffectsDouble x = new SideEffectsDouble(c, sourceArrayIndex, sourceIndex,
nextSourceIndex);
doloop = getTrailDouble(source, target, uniMask, x, flush, cr);
c = x.c;
sourceArrayIndex = x.sourceArrayIndex;
sourceIndex = x.sourceIndex;
nextSourceIndex = x.nextSourceIndex;
if (x.doread) {
if (doloop)
continue;
else
break;
}
} else {
/* this is an unmatched trail code unit (2nd surrogate) */
/* callback(illegal) */
cr[0] = CoderResult.malformedForLength(1);
break;
}
}
} else {
doread = true;
}
/* convert the Unicode code point in c into codepage bytes */
stage2Entry = MBCS_STAGE_2_FROM_U(table, tableInts, c);
/* get the bytes and the length for the output */
/* MBCS_OUTPUT_2 */
value = MBCS_VALUE_2_FROM_STAGE_2(chars, stage2Entry, c);
if (value <= 0xff) {
length = 1;
} else {
length = 2;
}
/* is this code point assigned, or do we use fallbacks? */
if (!(MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c) || (isFromUUseFallback(c) && value != 0))) {
/*
* We allow a 0 byte output if the "assigned" bit is set for this entry. There is no way
* with this data structure for fallback output to be a zero byte.
*/
// unassigned:
SideEffectsDouble x = new SideEffectsDouble(c, sourceArrayIndex, sourceIndex,
nextSourceIndex);
doloop = unassignedDouble(source, target, x, flush, cr);
c = x.c;
sourceArrayIndex = x.sourceArrayIndex;
sourceIndex = x.sourceIndex;
nextSourceIndex = x.nextSourceIndex;
if (doloop)
continue;
else
break;
}
/* write the output character bytes from value and length */
/* from the first if in the loop we know that targetCapacity>0 */
if (length == 1) {
/* this is easy because we know that there is enough space */
target.put((byte) value);
if (offsets != null) {
offsets.put(sourceIndex);
}
} else /* length==2 */{
target.put((byte) (value >>> 8));
if (2 <= target.remaining()) {
target.put((byte) value);
if (offsets != null) {
offsets.put(sourceIndex);
offsets.put(sourceIndex);
}
} else {
if (offsets != null) {
offsets.put(sourceIndex);
}
errorBuffer[0] = (byte) value;
errorBufferLength = 1;
/* target overflow */
cr[0] = CoderResult.OVERFLOW;
c = 0;
break;
}
}
/* normal end of conversion: prepare for a new character */
c = 0;
sourceIndex = nextSourceIndex;
continue;
} else {
/* target is full */
cr[0] = CoderResult.OVERFLOW;
break;
}
}
}
/* set the converter state back into UConverter */
fromUChar32 = c;
/* write back the updated pointers */
source.position(sourceArrayIndex);
return cr[0];
}
private final class SideEffectsSingleBMP {
int c, sourceArrayIndex;
SideEffectsSingleBMP(int c_, int sourceArrayIndex_) {
c = c_;
sourceArrayIndex = sourceArrayIndex_;
}
}
// function made out of block labeled getTrail in ucnv_MBCSSingleFromUnicodeWithOffsets
// assumes input c is lead surrogate
private final boolean getTrailSingleBMP(CharBuffer source, SideEffectsSingleBMP x, CoderResult[] cr) {
if (x.sourceArrayIndex < source.limit()) {
/* test the following code unit */
char trail = source.get(x.sourceArrayIndex);
if (UTF16.isTrailSurrogate(trail)) {
++x.sourceArrayIndex;
x.c = UCharacter.getCodePoint(x.c, trail);
/* this codepage does not map supplementary code points */
/* callback(unassigned) */
cr[0] = CoderResult.unmappableForLength(2);
return false;
} else {
/* this is an unmatched lead code unit (1st surrogate) */
/* callback(illegal) */
cr[0] = CoderResult.malformedForLength(1);
return false;
}
} else {
/* no more input */
return false;
}
// return true;
}
private final class SideEffects {
int c, sourceArrayIndex, sourceIndex, nextSourceIndex, prevSourceIndex, prevLength;
boolean doread = true;
SideEffects(int c_, int sourceArrayIndex_, int sourceIndex_, int nextSourceIndex_, int prevSourceIndex_,
int prevLength_) {
c = c_;
sourceArrayIndex = sourceArrayIndex_;
sourceIndex = sourceIndex_;
nextSourceIndex = nextSourceIndex_;
prevSourceIndex = prevSourceIndex_;
prevLength = prevLength_;
}
}
// function made out of block labeled getTrail in ucnv_MBCSFromUnicodeWithOffsets
// assumes input c is lead surrogate
private final boolean getTrail(CharBuffer source, ByteBuffer target, int uniMask, SideEffects x,
boolean flush, CoderResult[] cr) {
if (x.sourceArrayIndex < source.limit()) {
/* test the following code unit */
char trail = source.get(x.sourceArrayIndex);
if (UTF16.isTrailSurrogate(trail)) {
++x.sourceArrayIndex;
++x.nextSourceIndex;
/* convert this supplementary code point */
x.c = UCharacter.getCodePoint(x.c, trail);
if ((uniMask & UConverterConstants.HAS_SUPPLEMENTARY) == 0) {
/* BMP-only codepages are stored without stage 1 entries for supplementary code points */
fromUnicodeStatus = x.prevLength; /* save the old state */
/* callback(unassigned) */
x.doread = true;
return unassigned(source, target, null, x, flush, cr);
} else {
x.doread = false;
return true;
}
} else {
/* this is an unmatched lead code unit (1st surrogate) */
/* callback(illegal) */
cr[0] = CoderResult.malformedForLength(1);
return false;
}
} else {
/* no more input */
return false;
}
}
// function made out of block labeled unassigned in ucnv_MBCSFromUnicodeWithOffsets
private final boolean unassigned(CharBuffer source, ByteBuffer target, IntBuffer offsets, SideEffects x,
boolean flush, CoderResult[] cr) {
/* try an extension mapping */
int sourceBegin = x.sourceArrayIndex;
source.position(x.sourceArrayIndex);
x.c = fromU(x.c, source, target, null, x.sourceIndex, x.nextSourceIndex, flush, cr);
x.sourceArrayIndex = source.position();
x.nextSourceIndex += x.sourceArrayIndex - sourceBegin;
x.prevLength = fromUnicodeStatus;
if (cr[0].isError()) {
/* not mappable or buffer overflow */
return false;
} else {
/* a mapping was written to the target, continue */
/* recalculate the targetCapacity after an extension mapping */
// x.targetCapacity=pArgs.targetLimit-x.targetArrayIndex;
/* normal end of conversion: prepare for a new character */
if (offsets != null) {
x.prevSourceIndex = x.sourceIndex;
x.sourceIndex = x.nextSourceIndex;
}
return true;
}
}
private final class SideEffectsDouble {
int c, sourceArrayIndex, sourceIndex, nextSourceIndex;
boolean doread = true;
SideEffectsDouble(int c_, int sourceArrayIndex_, int sourceIndex_, int nextSourceIndex_) {
c = c_;
sourceArrayIndex = sourceArrayIndex_;
sourceIndex = sourceIndex_;
nextSourceIndex = nextSourceIndex_;
}
}
// function made out of block labeled getTrail in ucnv_MBCSDoubleFromUnicodeWithOffsets
// assumes input c is lead surrogate
private final boolean getTrailDouble(CharBuffer source, ByteBuffer target, int uniMask,
SideEffectsDouble x, boolean flush, CoderResult[] cr) {
if (x.sourceArrayIndex < source.limit()) {
/* test the following code unit */
char trail = source.get(x.sourceArrayIndex);
if (UTF16.isTrailSurrogate(trail)) {
++x.sourceArrayIndex;
++x.nextSourceIndex;
/* convert this supplementary code point */
x.c = UCharacter.getCodePoint(x.c, trail);
if ((uniMask & UConverterConstants.HAS_SUPPLEMENTARY) == 0) {
/* BMP-only codepages are stored without stage 1 entries for supplementary code points */
/* callback(unassigned) */
x.doread = true;
return unassignedDouble(source, target, x, flush, cr);
} else {
x.doread = false;
return true;
}
} else {
/* this is an unmatched lead code unit (1st surrogate) */
/* callback(illegal) */
cr[0] = CoderResult.malformedForLength(1);
return false;
}
} else {
/* no more input */
return false;
}
}
// function made out of block labeled unassigned in ucnv_MBCSDoubleFromUnicodeWithOffsets
private final boolean unassignedDouble(CharBuffer source, ByteBuffer target, SideEffectsDouble x,
boolean flush, CoderResult[] cr) {
/* try an extension mapping */
int sourceBegin = x.sourceArrayIndex;
source.position(x.sourceArrayIndex);
x.c = fromU(x.c, source, target, null, x.sourceIndex, x.nextSourceIndex, flush, cr);
x.sourceArrayIndex = source.position();
x.nextSourceIndex += x.sourceArrayIndex - sourceBegin;
if (cr[0].isError()) {
/* not mappable or buffer overflow */
return false;
} else {
/* a mapping was written to the target, continue */
/* recalculate the targetCapacity after an extension mapping */
// x.targetCapacity=pArgs.targetLimit-x.targetArrayIndex;
/* normal end of conversion: prepare for a new character */
x.sourceIndex = x.nextSourceIndex;
return true;
}
}
/**
* Overrides super class method
*
* @param encoder
* @param source
* @param target
* @param offsets
* @return
*/
@Override
protected CoderResult cbFromUWriteSub(CharsetEncoderICU encoder, CharBuffer source, ByteBuffer target,
IntBuffer offsets) {
CharsetMBCS cs = (CharsetMBCS) encoder.charset();
byte[] subchar;
int length;
if (cs.subChar1 != 0
&& (cs.sharedData.mbcs.extIndexes != null ? encoder.useSubChar1
: (encoder.invalidUCharBuffer[0] <= 0xff))) {
/*
* select subChar1 if it is set (not 0) and the unmappable Unicode code point is up to U+00ff (IBM MBCS
* behavior)
*/
subchar = new byte[] { cs.subChar1 };
length = 1;
} else {
/* select subChar in all other cases */
subchar = cs.subChar;
length = cs.subCharLen;
}
/* reset the selector for the next code point */
encoder.useSubChar1 = false;
if (cs.sharedData.mbcs.outputType == MBCS_OUTPUT_2_SISO) {
byte[] buffer = new byte[4];
int i = 0;
/* fromUnicodeStatus contains prevLength */
switch (length) {
case 1:
if (encoder.fromUnicodeStatus == 2) {
/* DBCS mode and SBCS sub char: change to SBCS */
encoder.fromUnicodeStatus = 1;
buffer[i++] = UConverterConstants.SI;
}
buffer[i++] = subchar[0];
break;
case 2:
if (encoder.fromUnicodeStatus <= 1) {
/* SBCS mode and DBCS sub char: change to DBCS */
encoder.fromUnicodeStatus = 2;
buffer[i++] = UConverterConstants.SO;
}
buffer[i++] = subchar[0];
buffer[i++] = subchar[1];
break;
default:
throw new IllegalArgumentException();
}
subchar = buffer;
length = i;
}
return CharsetEncoderICU.fromUWriteBytes(encoder, subchar, 0, length, target, offsets, source.position());
}
/**
* Gets called whenever CharsetEncoder.replaceWith gets called. allowReplacementChanges only allows subChar and
* subChar1 to be modified outside construction (since replaceWith is called once during construction).
*
* @param replacement
* The replacement for subchar.
*/
@Override
protected void implReplaceWith(byte[] replacement) {
if (allowReplacementChanges) {
CharsetMBCS cs = (CharsetMBCS) this.charset();
System.arraycopy(replacement, 0, cs.subChar, 0, replacement.length);
cs.subCharLen = (byte) replacement.length;
cs.subChar1 = 0;
}
}
}
@Override
public CharsetDecoder newDecoder() {
return new CharsetDecoderMBCS(this);
}
@Override
public CharsetEncoder newEncoder() {
return new CharsetEncoderMBCS(this);
}
@SuppressWarnings("fallthrough")
void MBCSGetFilteredUnicodeSetForUnicode(UConverterSharedData data, UnicodeSet setFillIn, int which, int filter){
UConverterMBCSTable mbcsTable;
char[] table;
char st1,maxStage1, st2;
int st3;
int c ;
mbcsTable = data.mbcs;
table = mbcsTable.fromUnicodeTable;
if(mbcsTable.hasSupplementary()){
maxStage1 = 0x440;
}
else{
maxStage1 = 0x40;
}
c=0; /* keep track of current code point while enumerating */
if(mbcsTable.outputType==MBCS_OUTPUT_1){
char stage2, stage3;
char minValue;
char[] results = mbcsTable.fromUnicodeChars;
if(which==ROUNDTRIP_SET) {
/* use only roundtrips */
minValue=0xf00;
} else {
/* use all roundtrip and fallback results */
minValue=0x800;
}
for(st1=0;st1maxStage1){
stage2 = st2;
for(st2=0; st2<64; ++st2){
st3 = table[stage2 + st2];
if(st3!=0){
/*read the stage 3 block */
stage3 = (char)st3;
do {
if(results[stage3++]>=minValue){
setFillIn.add(c);
}
}while((++c&0xf) !=0);
} else {
c+= 16; /*empty stage 2 block */
}
}
} else {
c+=1024; /* empty stage 2 block */
}
}
} else {
int[] tableInts = mbcsTable.fromUnicodeTableInts;
int stage2,stage3;
byte[] bytes;
int st3Multiplier;
int value;
boolean useFallBack;
bytes = mbcsTable.fromUnicodeBytes;
char[] chars = mbcsTable.fromUnicodeChars;
int[] ints = mbcsTable.fromUnicodeInts;
useFallBack = (which == ROUNDTRIP_AND_FALLBACK_SET);
switch(mbcsTable.outputType) {
case MBCS_OUTPUT_3:
case MBCS_OUTPUT_4_EUC:
st3Multiplier = 3;
break;
case MBCS_OUTPUT_4:
st3Multiplier =4;
break;
default:
st3Multiplier =2;
break;
}
for(st1=0;st1(maxStage1>>1)){
stage2 = st2 ;
for(st2=0;st2<64;++st2){
/*read the stage 3 block */
st3 = tableInts[stage2 + st2];
if(st3!=0){
//if((st3=table[stage2+st2])!=0){
stage3 = st3Multiplier*16*(st3&UConverterConstants.UNSIGNED_SHORT_MASK);
/* get the roundtrip flags for the stage 3 block */
st3>>>=16;
switch(filter) {
case UCNV_SET_FILTER_NONE:
do {
if((st3&1)!=0){
setFillIn.add(c);
}else if (useFallBack) {
int b =0;
switch(st3Multiplier) {
case 4:
b = ints[stage3 / 4];
break;
case 3:
b |= bytes[stage3] | bytes[stage3 + 1] | bytes[stage3 + 2];
break;
case 2:
b = chars[stage3 / 2];
break;
default:
break;
}
stage3+=st3Multiplier;
if(b!=0) {
setFillIn.add(c);
}
}
st3>>=1;
}while((++c&0xf)!=0);
break;
case UCNV_SET_FILTER_DBCS_ONLY:
/* Ignore single bytes results (<0x100). */
do {
if(((st3&1) != 0 || useFallBack) && chars[stage3 / 2] >= 0x100){
setFillIn.add(c);
}
st3>>=1;
stage3+=2;
}while((++c&0xf) != 0);
break;
case UCNV_SET_FILTER_2022_CN :
/* only add code points that map to CNS 11643 planes 1&2 for non-EXT ISO-2202-CN. */
do {
if(((st3&1) != 0 || useFallBack) &&
((value= (UConverterConstants.UNSIGNED_BYTE_MASK & bytes[stage3]))==0x81 || value==0x82) ){
setFillIn.add(c);
}
st3>>=1;
stage3+=3;
}while((++c&0xf)!=0);
break;
case UCNV_SET_FILTER_SJIS:
/* only add code points that map tp Shift-JIS codes corresponding to JIS X 0280. */
do{
if(((st3&1) != 0 || useFallBack) && (value=chars[stage3 / 2])>=0x8140 && value<=0xeffc){
setFillIn.add(c);
}
st3>>=1;
stage3+=2;
}while((++c&0xf)!=0);
break;
case UCNV_SET_FILTER_GR94DBCS:
/* only add code points that maps to ISO 2022 GR 94 DBCS codes*/
do {
if(((st3&1) != 0 || useFallBack) &&
(UConverterConstants.UNSIGNED_SHORT_MASK & ((value=chars[stage3 / 2])- 0xa1a1))<=(0xfefe - 0xa1a1) &&
(UConverterConstants.UNSIGNED_BYTE_MASK & (value - 0xa1)) <= (0xfe - 0xa1)){
setFillIn.add(c);
}
st3>>=1;
stage3+=2;
}while((++c&0xf)!=0);
break;
case UCNV_SET_FILTER_HZ:
/*Only add code points that are suitable for HZ DBCS*/
do {
if( ((st3&1) != 0 || useFallBack) &&
(UConverterConstants.UNSIGNED_SHORT_MASK & ((value=chars[stage3 / 2])-0xa1a1))<=(0xfdfe - 0xa1a1) &&
(UConverterConstants.UNSIGNED_BYTE_MASK & (value - 0xa1)) <= (0xfe - 0xa1)){
setFillIn.add(c);
}
st3>>=1;
stage3+=2;
}while((++c&0xf) != 0);
break;
default:
return;
}
} else {
c+=16; /* empty stage 3 block */
}
}
} else {
c+=1024; /*empty stage2 block */
}
}
}
extGetUnicodeSet(setFillIn, which, filter, data);
}
static void extGetUnicodeSetString(ByteBuffer cx,UnicodeSet setFillIn, boolean useFallback,
int minLength, int c, char s[],int length,int sectionIndex){
CharBuffer fromUSectionUChar;
IntBuffer fromUSectionValues;
fromUSectionUChar = (CharBuffer)ARRAY(cx, EXT_FROM_U_UCHARS_INDEX,char.class );
fromUSectionValues = (IntBuffer)ARRAY(cx, EXT_FROM_U_VALUES_INDEX,int.class );
int fromUSectionUCharIndex = fromUSectionUChar.position()+sectionIndex;
int fromUSectionValuesIndex = fromUSectionValues.position()+sectionIndex;
int value, i, count;
/* read first pair of the section */
count = fromUSectionUChar.get(fromUSectionUCharIndex++);
value = fromUSectionValues.get(fromUSectionValuesIndex++);
if(value!=0 && (FROM_U_IS_ROUNDTRIP(value) || useFallback) && FROM_U_GET_LENGTH(value)>=minLength) {
if(c>=0){
setFillIn.add(c);
} else {
StringBuilder normalizedStringBuilder = new StringBuilder();
for(int j=0; j=minLength) {
StringBuilder normalizedStringBuilder = new StringBuilder(); // String for composite characters
for(int j=0; j<(length+1);j++){
normalizedStringBuilder.append(s[j]);
}
setFillIn.add(normalizedStringBuilder.toString());
}
}
}
static void extGetUnicodeSet(UnicodeSet setFillIn, int which, int filter, UConverterSharedData Data){
int st1, stage1Length, st2, st3, minLength;
int ps2, ps3;
CharBuffer stage12, stage3;
int value, length;
IntBuffer stage3b;
boolean useFallback;
char s[] = new char[MAX_UCHARS];
int c;
ByteBuffer cx = Data.mbcs.extIndexes;
if(cx == null){
return;
}
stage12 = (CharBuffer)ARRAY(cx, EXT_FROM_U_STAGE_12_INDEX,char.class );
stage3 = (CharBuffer)ARRAY(cx, EXT_FROM_U_STAGE_3_INDEX,char.class );
stage3b = (IntBuffer)ARRAY(cx, EXT_FROM_U_STAGE_3B_INDEX,int.class );
stage1Length = cx.asIntBuffer().get(EXT_FROM_U_STAGE_1_LENGTH);
useFallback = (which==ROUNDTRIP_AND_FALLBACK_SET);
c = 0;
if(filter == UCNV_SET_FILTER_2022_CN) {
minLength = 3;
} else if (Data.mbcs.outputType == MBCS_OUTPUT_DBCS_ONLY || filter != UCNV_SET_FILTER_NONE) {
/* DBCS-only, ignore single-byte results */
minLength = 2;
} else {
minLength = 1;
}
for(st1=0; st1< stage1Length; ++st1){
st2 = stage12.get(st1);
if(st2>stage1Length) {
ps2 = st2;
for(st2=0;st2<64;++st2){
st3=(stage12.get(ps2+st2))<=minLength){
switch(filter) {
case UCNV_SET_FILTER_2022_CN:
if(!(FROM_U_GET_LENGTH(value)==3 && FROM_U_GET_DATA(value)<=0x82ffff)){
continue;
}
break;
case UCNV_SET_FILTER_SJIS:
if(!(FROM_U_GET_LENGTH(value)==2 && (value=FROM_U_GET_DATA(value))>=0x8140 && value<=0xeffc)){
continue;
}
break;
case UCNV_SET_FILTER_GR94DBCS:
if(!(FROM_U_GET_LENGTH(value)==2 && ((value=FROM_U_GET_DATA(value)) - 0xa1a1)<=(0xfefe - 0xa1a1)
&& (UConverterConstants.UNSIGNED_BYTE_MASK & (value - 0xa1))<= (0xfe - 0xa1))){
continue;
}
break;
case UCNV_SET_FILTER_HZ:
if(!(FROM_U_GET_LENGTH(value)==2 && ((value=FROM_U_GET_DATA(value)) - 0xa1a1)<=(0xfdfe - 0xa1a1)
&& (UConverterConstants.UNSIGNED_BYTE_MASK & (value - 0xa1))<= (0xfe - 0xa1))){
continue;
}
break;
default:
/*
* UCNV_SET_FILTER_NONE,
* or UCNV_SET_FILTER_DBCS_ONLY which is handled via minLength
*/
break;
}
setFillIn.add(c);
}
}while((++c&0xf) != 0);
} else {
c+=16; /* empty stage3 block */
}
}
} else {
c+=1024; /* empty stage 2 block*/
}
}
}
void MBCSGetUnicodeSetForUnicode(UConverterSharedData data, UnicodeSet setFillIn, int which){
MBCSGetFilteredUnicodeSetForUnicode(data, setFillIn, which,
this.sharedData.mbcs.outputType==MBCS_OUTPUT_DBCS_ONLY ? UCNV_SET_FILTER_DBCS_ONLY : UCNV_SET_FILTER_NONE );
}
@Override
void getUnicodeSetImpl( UnicodeSet setFillIn, int which){
if((options & MBCS_OPTION_GB18030)!=0){
setFillIn.add(0, 0xd7ff);
setFillIn.add(0xe000, 0x10ffff);
}
else {
this.MBCSGetUnicodeSetForUnicode(sharedData, setFillIn, which);
}
}
}