blob: 138abbcfa15bc11ad3f5c5310d41445c613600cd [file] [log] [blame]
/**
*******************************************************************************
* Copyright (C) 1996-2001, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
* $Source: /xsrl/Nsvn/icu/unicodetools/com/ibm/text/utility/UTF32.java,v $
* $Date: 2004/04/17 18:21:38 $
* $Revision: 1.3 $
*
*******************************************************************************
*/
package com.ibm.text.utility;
/**
* Utility class for demonstrating UTF16 character conversions and indexing conversions.
* Ideally, these methods would be on existing classes in Java, but they can also be used
* in a stand-alone utility class like this one.
* <p>Code that uses strings alone rarely need modification.
* By design, UTF-16 does not allow overlap, so searching for strings is a safe operation.
* Similarly, concatenation is always safe. Substringing is safe if the start and end are both
* on UTF32 boundaries. In normal code, the values for start and end are on those boundaries,
* since they arose from operations like searching.
* If not, the nearest UTF-32 boundaries can be determined using <code>bounds32()</code>.
* <p>Here is a summary of the methods:
* <ul><li>
* <code>char32At()</code>, <code>count32()</code>, and <code>append32()</code>
* are most important methods for most programs.
* They are used for iteration, filtering and copying. See the examples below.
* </li><li>
* <code>bounds32()</code> is useful for finding the nearest UTF-32 boundaries.
* However, in most circumstances it is better to use
* <a <a href="http://java.sun.com/products/jdk/1.2/docs/api/java/text/BreakIterator.html#getCharacterInstance(java.util.Locale)">
* BreakIterator.getCharacterInstance(Locale)</a> to find character boundaries
* that are closer to end-user expectations.
* </li><li>
* <code>valueOf32()</code> is occasionally convenient for producing a string containing a UTF-32 value.
* </li><li>
* <code>findOffset16()</code> and <code>findOffset32()</code> are generally not needed,
* except when interfacing to specifications that use UTF-32 indices (such as XSL).
* </li><li>
* <code>isLegal()</code> can be used to test whether UTF-16 or UTF-32 values are valid.
* </li><li>
* <code>isLeadSurrogate()</code>, <code>isSurrogate()</code>, and <code>isTrailSurrogate()</code>
* test the type of a char. They are useful for lower-level code.
* </li><li>
* <code>getChar32()</code>, <code>getLead()</code>, and <code>getTrail()</code>
* are sometimes useful for putting together and taking apart UTF-32 values.
* </li></ul>
* <strong>Examples:</strong>
* <p>The following examples illustrate use of some of these methods.
<pre>
// iteration forwards: Original
for (int i = 0; i < s.length(); ++i) {
    char ch = s.charAt(i);
    doSomethingWith(ch);
}
// iteration forwards: Changes for UTF-32
int ch;
for (int i = 0; i < s.length(); i+=UTF32.count16(ch)) {
    ch = UTF32.char32At(s,i);
    doSomethingWith(ch);
}
// iteration backwards: Original
for (int i = s.length()-1; i >= 0; --i) {
    char ch = s.charAt(i);
    doSomethingWith(ch);
}
// iteration backwards: Changes for UTF-32
int ch;
for (int i = s.length()-1; i > 0; i-=UTF32.count16(ch)) {
    ch = UTF32.char32At(s,i);
    doSomethingWith(ch);
}
* </pre>
* <strong>Notes:</strong>
* <ul><li>
* <strong>Naming:</strong> For clarity, High and Low surrogates are called <code>Lead</code> and <code>Trail</code> in the API,
* which gives a better sense of their ordering in a string. <code>offset16</code> and <code>offset32</code> are used to distinguish
* offsets to UTF-16 boundaries vs offsets to UTF-32 boundaries.
* <code>int char32</code> is used to contain UTF-32 characters, as opposed to <code>char</code>, which is a UTF-16 code unit.
* </li><li>
* <strong>Roundtripping Offsets:</strong> You can always roundtrip
* from a UTF-32 offset to a UTF-16 offset and back.
* Because of the difference in structure, you can roundtrip
* from a UTF-16 offset to a UTF-32 offset and back if and only if <code>bounds(string, offset16) != TRAIL</code>.
* </li><li>
* <strong>Exceptions:</strong> The error checking will throw an exception if indices are out of bounds.
* Other than than that, all methods will behave reasonably,
* even if unmatched surrogates or out-of-bounds UTF-32 values are present.
* <code>isLegal()</code> can be used to check for validity if desired.
* </li><li>
* <strong>Unmatched Surrogates:</strong> If the string contains unmatched surrogates, then these are
* counted as one UTF-32 value. This matches their iteration behavior, which is vital.
* It also matches common display practice as
* missing glyphs (see the Unicode Standard Section 5.4, 5.5).
* </li><li>
* <strong>Out-of-bounds UTF-32 values:</strong> If a <code>char32</code> contains an out-of-bounds UTF-32 value,
* then it is treated as REPLACEMENT_CHAR for consistency across the API.
* </li><li>
* <strong>Optimization:</strong> The method implementations may need optimization if the compiler doesn't fold static final methods.
* Since surrogate pairs will form an exceeding small percentage of all the text in the world,
* the singleton case should always be optimized for.
* </li></ul>
* @author Mark Davis, with help from Markus Scherer
*/
public final class UTF32 {
// =========================================================
// UTILITIES
// =========================================================
/**
* Unicode value used when translating into Unicode encoding form
* and there is no existing character.
*/
public static final char REPLACEMENT_CHAR = '\uFFFD';
/**
* Value returned in <code><a href="#bounds32(java.lang.String, int)">bounds32()</a></code>.
*/
public static final int SINGLE = 1, LEAD = 2, TRAIL = 5;
/**
* Determines how many chars this char32 requires.
* If a validity check is required, use <code><a href="#isLegal(char)">isLegal()</a></code>
* on char32 before calling.
* <p><i>If this were integrated into the Java API, it could be a static method of either Character or String.</i>
* @return 2 if is in surrogate space, otherwise 1.
* @param ch the input character.
*/
public static int count16(int char32) {
if (char32 < MIN_SUPPLEMENTARY) return 1;
return 2;
}
/**
* Extract a single UTF-32 value from a string.
* Used when iterating forwards or backwards (with <code>count16()</code>, as well as random access.
* If a validity check is required, use <code><a href="#isLegal(char)">isLegal()</a></code> on the return value.
* <p><i>If this were integrated into the Java API, it could be a method of String, StringBuffer and possibly CharacterIterator.</i>
* @return UTF-32 value for the UTF-32 value that contains the char at offset16.
* The boundaries of that codepoint are the same as in <code>bounds32()</code>.
* @param source array of UTF-16 chars
* @param offset16 UTF-16 offset to the start of the character.
*/
public static int char32At(String source, int offset16) {
char single = source.charAt(offset16);
if (!isSurrogate(single)) return single;
try { // use exception to catch out-of-bounds
// Convert the UTF-16 surrogate pair if necessary.
// For simplicity in usage, and because the frequency of pairs is low,
// look both directions.
if (isLeadSurrogate(single)) {
char trail = source.charAt(++offset16);
if (isTrailSurrogate(trail)) {
return ((int)single << SURROGATE_SHIFT) + trail + SURROGATE_OFFSET;
}
} else { // isTrailSurrogate(single), so
char lead = source.charAt(--offset16);
if (isLeadSurrogate(lead)) {
return ((int)lead << SURROGATE_SHIFT) + single + SURROGATE_OFFSET;
}
}
} catch (StringIndexOutOfBoundsException e) {}
return single; // return unmatched surrogate
}
public static int char32At(StringBuffer source, int offset16) {
char single = source.charAt(offset16);
if (!isSurrogate(single)) return single;
try { // use exception to catch out-of-bounds
// Convert the UTF-16 surrogate pair if necessary.
// For simplicity in usage, and because the frequency of pairs is low,
// look both directions.
if (isLeadSurrogate(single)) {
char trail = source.charAt(++offset16);
if (isTrailSurrogate(trail)) {
return ((int)single << SURROGATE_SHIFT) + trail + SURROGATE_OFFSET;
}
} else { // isTrailSurrogate(single), so
char lead = source.charAt(--offset16);
if (isLeadSurrogate(lead)) {
return ((int)lead << SURROGATE_SHIFT) + single + SURROGATE_OFFSET;
}
}
} catch (StringIndexOutOfBoundsException e) {}
return single; // return unmatched surrogate
}
public static int char32At(char[] source, int start16, int end16, int offset16) {
if (offset16 < start16 || offset16 >= end16) {
throw new ArrayIndexOutOfBoundsException(offset16);
}
char single = source[offset16];
if (!isSurrogate(single)) return single;
try { // use exception to catch out-of-bounds
// Convert the UTF-16 surrogate pair if necessary.
// For simplicity in usage, and because the frequency of pairs is low,
// look both directions.
if (isLeadSurrogate(single)) {
++offset16;
if (offset16 >= end16) return single;
char trail = source[offset16];
if (isTrailSurrogate(trail)) {
return ((int)single << SURROGATE_SHIFT) + trail + SURROGATE_OFFSET;
}
} else { // isTrailSurrogate(single), so
char lead = source[--offset16];
if (isLeadSurrogate(lead)) {
return ((int)lead << SURROGATE_SHIFT) + single + SURROGATE_OFFSET;
}
}
} catch (ArrayIndexOutOfBoundsException e) {}
return single; // return unmatched surrogate
}
// moral equivalent of valueOf32(charAt32(x)), but no memory alloc
public static String getCodePointSubstring(String s, int offset16) {
switch(bounds32(s,offset16)) {
default: return s.substring(offset16,offset16+1);
case LEAD: return s.substring(offset16,offset16+2);
case TRAIL: return s.substring(offset16-1,offset16+1);
}
}
// moral equivalent of valueOf32(charAt32(x)), but no memory alloc
public static String getCodePointSubstring(StringBuffer s, int offset16) {
switch(bounds32(s,offset16)) {
default: return s.substring(offset16,offset16+1);
case LEAD: return s.substring(offset16,offset16+2);
case TRAIL: return s.substring(offset16-1,offset16+1);
}
}
public static int append32(char[] output, int oPosition, int oEnd, int cp) {
if (oPosition >= oEnd) throw new ArrayIndexOutOfBoundsException(oPosition);
output[oPosition++] = UTF32.getLead(cp);
if (UTF32.count16(cp) != 1) {
if (oPosition >= oEnd) throw new ArrayIndexOutOfBoundsException(oPosition);
output[oPosition++] = UTF32.getTrail(cp);
}
return oPosition;
}
public static void setChar32At(StringBuffer b, int position, int codePoint) {
int type = bounds32(b, position);
// handle simple cases: #chars at position match #chars in codePoint
int end = position;
switch (type) {
case SINGLE:
if (isSupplementary(codePoint)) break;
b.setCharAt(position, (char)codePoint);
return;
case LEAD:
if (!isSupplementary(codePoint)) {
++end;
break;
}
b.setCharAt(position++, (char)getLead(codePoint));
b.setCharAt(position, (char)getTrail(codePoint));
return;
case TRAIL:
if (!isSupplementary(codePoint)) {
--position;
break;
}
b.setCharAt(position++, (char)getLead(codePoint));
b.setCharAt(position, (char)getTrail(codePoint));
return;
}
// mismatch, just use long form
b.replace(position, end+1, valueOf32(codePoint));
}
/**
* See if a char value is legal. It can't be:
* <ul><li>Not-a-character (either \\uFFFF or\\uFFFE).
* The datatype char itself prevents out of bounds errors.
* </li></ul>
* Note: legal does not mean that it is assigned in this version of Unicode.
* <p><i>If this were integrated into the Java API, it could be a static method of String or Character.</i>
* @param UTF-32 value to test
* @return true iff legal.
*/
public static boolean isLegal(char char16) {
return (char16 < 0xFFFE);
}
/**
* See if a UTF32 value is legal. It can't be:
* <ul>
* <li>Out of bounds (less than 0 or greater than MAX_UNICODE)</li>
* <li>A surrogate value (00D800 to 00DCFF)</li>
* <li>Not-a-character (of the form xxFFFF or xxFFFE)</li>
* </ul>
* Note: legal does not mean that it is assigned in this version of Unicode.
* <p><i>If this were integrated into the Java API, it could be a static method of String or Character.</i>
* @param char32 UTF-32 value to test
* @return true iff legal.
*/
public static boolean isLegal(int char32) {
if (char32 < 0) return false;
//if (char32 < SURROGATE_BASE) return true;
//if (char32 < SURROGATE_LIMIT) return false;
if ((char32 & PLANE_MASK) >= NON_CHARACTER_BASE) return false;
return (char32 <= MAX_UNICODE);
}
/**
* Determines whether the code unit OR code point is a surrogate.
* <p><i>If this were integrated into the Java API, it could be a static method of String or Character.</i>
* @return true iff the input character is a surrogate.
* @param ch the input character.
*/
public static boolean isSurrogate(int char32) {
return (SURROGATE_BASE <= char32 && char32 < SURROGATE_LIMIT);
}
/**
* Determines whether the code point is a supplementary.
* <p><i>If this were integrated into the Java API, it could be a static method of String or Character.</i>
* @return true iff the input character is a surrogate.
* @param ch the input character.
*/
public static boolean isSupplementary(int char32) {
return (char32 >= MIN_SUPPLEMENTARY && char32 <= MAX_UNICODE);
}
/**
* Determines whether the code point is a supplementary.
* <p><i>If this were integrated into the Java API, it could be a static method of String or Character.</i>
* @return true iff the input character is a surrogate.
* @param ch the input character.
*/
public static boolean isBasic(int char32) {
return (char32 >= 0 && char32 < MIN_SUPPLEMENTARY);
}
/**
* Determines whether the character is a trail surrogate.
* <p><i>If this were integrated into the Java API, it could be a static method of String or Character.</i>
* @return true iff the input character is a trail surrogate.
* @param ch the input character.
*/
public static boolean isTrailSurrogate(char ch) {
return (TRAIL_BASE <= ch && ch < TRAIL_LIMIT);
}
/**
* Determines whether the character is a lead surrogate.
* <p><i>If this were integrated into the Java API, it could be a static method of String or Character.</i>
* @return true iff the input character is a lead surrogate.
* @param ch the input character.
*/
public static boolean isLeadSurrogate(char ch) {
return (LEAD_BASE <= ch && ch < LEAD_LIMIT);
}
/**
* Returns the lead surrogate.
* If a validity check is required, use <code><a href="#isLegal(char)">isLegal()</a></code> on char32 before calling.
* <p><i>If this were integrated into the Java API, it could be a static method of String or Character.</i>
* @return lead surrogate if the count16(ch) is 2;
* <br>otherwise the character itself
* @param char32 the input character.
*/
public static char getLead(int char32) {
if (char32 >= MIN_SUPPLEMENTARY) {
return (char)(LEAD_BASE_OFFSET + (char32 >> SURROGATE_SHIFT));
}
return (char)char32;
}
/**
* Returns the trail surrogate.
* If a validity check is required, use <code><a href="#isLegal(char)">isLegal()</a></code> on char32 before calling.
* <p><i>If this were integrated into the Java API, it could be a static method of String or Character.</i>
* @return the trail surrogate if the count16(ch) is 2;
* <br>and 0 otherwise (note: 0 is not a valid lead surrogate).
* @param char32 the input character.
*/
public static char getTrail(int char32) {
if (char32 >= MIN_SUPPLEMENTARY) {
return (char)(TRAIL_BASE + (char32 & TRAIL_MASK));
}
return '\u0000';
}
/**
* Convenience method corresponding to String.valueOf(char). It returns a one or two char string containing
* the UTF-32 value. If the input value can't be converted, it substitutes REPLACEMENT_CHAR.
* If a validity check is required, use <code><a href="#isLegal(char)">isLegal()</a></code> before calling.
* <p><i>If this were integrated into the Java API, it could be a static method of String.</i>
* @return string value of char32
* @param ch the input character.
*/
public static String valueOf32(int char32) {
if (char32 < 0 || MAX_UNICODE < char32) return String.valueOf(REPLACEMENT_CHAR);
if (char32 < MIN_SUPPLEMENTARY) return String.valueOf((char)char32);
synchronized (buf2) { // saves allocations
buf2[0] = (char)(LEAD_BASE_OFFSET + (char32 >> SURROGATE_SHIFT));
buf2[1] = (char)(TRAIL_BASE + (char32 & TRAIL_MASK));
return String.valueOf(buf2);
}
}
private static char[] buf2 = new char[2]; // used to avoid allocations
/**
* Returns the UTF-32 character corresponding to the two chars.
* If a validity check is required, check the arguments with
* <code>isLeadSurrogate()</code> and <code>isTrailSurrogate()</code>, respectively before calling.
* <p><i>If this were integrated into the Java API, it could be a static method of String or Character.</i>
* @return the UTF-32 character, or REPLACEMENT_CHAR if invalid.
* @param lead the lead char
* @param lead the trail char
*/
public static int getChar32(char lead, char trail) {
if (isLeadSurrogate(lead) && isTrailSurrogate(trail)) {
return (lead <<= SURROGATE_SHIFT) + trail + SURROGATE_OFFSET;
}
return REPLACEMENT_CHAR;
}
/**
* Returns the type of the UTF32 boundaries around the char at offset16.
* Used for random access.
* <p><i>If this were integrated into the Java API, it could be a method of String, StringBuffer and possibly CharacterIterator.</i>
* @return SINGLE, FIRST, or SECOND:
* <ul><li>
* SINGLE: a single char; the bounds are [offset16, offset16+1]
* </li><li>
* LEAD: a surrogate pair starting at offset16; the bounds are [offset16, offset16+2]
* </li><li>
* TRAIL: a surrogate pair starting at offset16-1; the bounds are [offset16-1, offset16+1]
* </ul>
* For bit-twiddlers, the return values for these are chosen so that the boundaries can be gotten by:
* [offset16 - (value>>2), offset16 + (value&3)].
* @param source text to analyse
* @param offset16 UTF-16 offset
* @exception StringIndexOutOfBoundsException if offset16 is out of bounds.
*/
public static int bounds32(String source, int offset16) {
char ch = source.charAt(offset16);
if (isSurrogate(ch)) {
if (isLeadSurrogate(ch)) {
if (++offset16 < source.length()
&& isTrailSurrogate(source.charAt(offset16))) return LEAD;
} else { // isTrailSurrogate(ch), so
if (--offset16 >= 0
&& isLeadSurrogate(source.charAt(offset16))) return TRAIL;
}
}
return SINGLE;
}
public static int bounds32(StringBuffer source, int offset16) {
char ch = source.charAt(offset16);
if (isSurrogate(ch)) {
if (isLeadSurrogate(ch)) {
if (++offset16 < source.length()
&& isTrailSurrogate(source.charAt(offset16))) return LEAD;
} else { // isTrailSurrogate(ch), so
if (--offset16 >= 0
&& isLeadSurrogate(source.charAt(offset16))) return TRAIL;
}
}
return SINGLE;
}
// should be renamed bounds
public static int bounds32(char[] source, int oStart, int oEnd, int offset16) {
if (offset16 < oStart || offset16 >= oEnd) {
throw new ArrayIndexOutOfBoundsException(offset16);
}
char ch = source[offset16];
if (isSurrogate(ch)) {
if (isLeadSurrogate(ch)) {
if (++offset16 < oEnd
&& isTrailSurrogate(source[offset16])) return LEAD;
} else { // isTrailSurrogate(ch), so
if (--offset16 >= oStart
&& isLeadSurrogate(source[offset16])) return TRAIL;
}
}
return SINGLE;
}
/**
* Returns the UTF-16 offset that corresponds to a UTF-32 offset.
* Used for random access. See the <a name="_top_">class description</a>
* for notes on roundtripping.
* <p><i>If this were integrated into the Java API, it could be a method of String, StringBuffer and possibly CharacterIterator.</i>
* @return UTF-16 offset
* @param offset32 UTF-32 offset
* @param source text to analyse
* @exception StringIndexOutOfBoundsException if offset32 is out of bounds.
*/
public static int findOffset16(String source, int offset32) {
int remaining = offset32; // for decrementing
boolean hadLeadSurrogate = false;
int i;
for (i = 0; remaining > 0 && i < source.length(); ++i) {
char ch = source.charAt(i);
if (hadLeadSurrogate && isTrailSurrogate(ch)) {
hadLeadSurrogate = false; // count valid trail as zero
} else {
hadLeadSurrogate = isLeadSurrogate(ch);
--remaining; // count others as 1
}
}
// if we didn't use up all of remaining (or if we started < 0)
// then it is beyond the bounds
if (remaining != 0) throw new StringIndexOutOfBoundsException(offset32);
// special check for last surrogate if needed, for consistency with
// other situations
if (hadLeadSurrogate && i < source.length() && isTrailSurrogate(source.charAt(i))) {
++i; // grab extra unicode
}
return i;
}
/**
* Returns the UTF-32 offset corresponding to the first UTF-32 boundary at or after the given UTF-16 offset.
* Used for random access. See the <a name="_top_">class description</a>
* for notes on roundtripping.
* <i>Note: If the UTF-16 offset is into the middle of a surrogate pair, then
* the UTF-32 offset of the <strong>end</strong> of the pair is returned.</i>
* <p>To find the UTF-32 length of a string, use:
* <pre>
* len32 = getOffset32(source, source.length());
* </pre>
* <p><i>If this were integrated into the Java API, it could be a methods of String, StringBuffer and possibly CharacterIterator.</i>
* @return UTF-32 offset
* @param source text to analyse
* @param offset16 UTF-16 offset
* @exception StringIndexOutOfBoundsException if offset16 is out of bounds.
*/
public static int findOffset32(String source, int offset16) {
int result = 0;
boolean hadLeadSurrogate = false;
for (int i = 0; i < offset16; ++i) {
char ch = source.charAt(i);
if (hadLeadSurrogate && isTrailSurrogate(ch)) {
hadLeadSurrogate = false; // count valid trail as zero
} else {
hadLeadSurrogate = isLeadSurrogate(ch);
++result; // count others as 1
}
}
return result;
}
public static int length32(String source) {
return findOffset32(source, source.length());
}
/**
* Append a single UTF-32 value to the end of a StringBuffer.
* If a validity check is required, use <code><a href="#isLegal(char)">isLegal()</a></code> on char32 before calling.
* <p><i>If this were integrated into the Java API, it could be a method of StringBuffer.</i>
* @param char32 value to append. If out of bounds, substitutes REPLACEMENT_CHAR.
* @param target string to add to
*/
public static void append32(StringBuffer target, int char32) {
// Check for irregular values
if (char32 < 0 || char32 > MAX_UNICODE) char32 = REPLACEMENT_CHAR;
// Write the UTF-16 values
if (char32 >= MIN_SUPPLEMENTARY) {
target.append((char)(LEAD_BASE_OFFSET + (char32 >> SURROGATE_SHIFT)));
target.append((char)(TRAIL_BASE + (char32 & TRAIL_MASK)));
} else {
target.append((char)char32);
}
}
/**
* Compare strings using Unicode code point order, instead of UTF-16 code unit order.
*/
public static final class StringComparator implements java.util.Comparator {
/**
* Standard String compare. Only one small section is different, marked in the code.
*/
public int compare(Object a, Object b) {
if (a == b) {
return 0;
}
if (a == null) {
return -1;
} else if (b == null) {
return 1;
}
String sa = (String) a;
String sb = (String) b;
int lena = sa.length();
int lenb = sb.length();
int len = lena;
if (len > lenb) len = lenb;
for (int i = 0; i < len; ++i) {
char ca = sa.charAt(i);
char cb = sb.charAt(i);
if (ca == cb) continue; // skip remap if equal
// start of only different section
if (ca >= 0xD800) { // reshuffle to get right codepoint order
ca += (ca < 0xE000) ? 0x2000 : -0x800;
}
if (cb >= 0xD800) { // reshuffle to get right codepoint order
cb += (cb < 0xE000) ? 0x2000 : -0x800;
}
// end of only different section
if (ca < cb) return -1;
return 1; // wasn't equal, so return 1
}
if (lena < lenb) return -1;
if (lena > lenb) return 1;
return 0;
}
}
// ===========================================================
// PRIVATES
// ===========================================================
/**
* Prevent instance from being created.
*/
private UTF32() {}
/**
* Maximum code point values for UTF-32.
*/
private static final int MAX_UNICODE = 0x10FFFF;
/**
* Maximum values for Basic code points (BMP).
*/
private static final int MAX_BASIC = 0xFFFF;
/**
* Minimum value for Supplementary code points (SMP).
*/
private static final int MIN_SUPPLEMENTARY = 0x10000;
/**
* Used to mask off single plane in checking for NON_CHARACTER
*/
private static final int PLANE_MASK = 0xFFFF;
/**
* Range of non-characters in each plane
*/
private static final int
NON_CHARACTER_BASE = 0xFFFE,
NON_CHARACTER_END = 0xFFFF;
// useful statics and tables for fast lookup
/**
* Values for surrogate detection. X is a surrogate iff X & SURROGATE_MASK == SURROGATE_MASK.
*/
static final int SURROGATE_MASK = 0xD800;
/**
* Bottom 10 bits for use in surrogates.
*/
private static final int TRAIL_MASK = 0x3FF;
/**
* Shift value for surrogates.
*/
private static final int SURROGATE_SHIFT = 10;
/**
* Lead surrogates go from LEAD_BASE up to LEAD_LIMIT-1.
*/
private static final int LEAD_BASE = 0xD800, LEAD_LIMIT = 0xDC00;
/**
* Trail surrogates go from TRAIL_BASE up to TRAIL_LIMIT-1.
*/
private static final int TRAIL_BASE = 0xDC00, TRAIL_LIMIT = 0xE000;
/**
* Surrogates go from SURROGATE_BASE up to SURROGATE_LIMIT-1.
*/
private static final int SURROGATE_BASE = 0xD800, SURROGATE_LIMIT = 0xE000;
/**
* Any codepoint at or greater than SURROGATE_SPACE_BASE requires 2 16-bit code units.
*/
//private static final int SURROGATE_SPACE_BASE = 0x10000;
/**
* Offset to add to combined surrogate pair to avoid masking.
*/
private static final int SURROGATE_OFFSET = MIN_SUPPLEMENTARY
- (LEAD_BASE << SURROGATE_SHIFT) - TRAIL_BASE;
private static final int LEAD_BASE_OFFSET = LEAD_BASE - (MIN_SUPPLEMENTARY >> SURROGATE_SHIFT);
};