/* |
|
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
/* |
|
****************************************************************************** |
|
* Copyright (C) 1996-2014, International Business Machines Corporation and |
|
* others. All Rights Reserved. |
|
****************************************************************************** |
|
*/ |
|
package jdk.internal.icu.impl; |
|
import jdk.internal.icu.lang.UCharacter; |
|
import jdk.internal.icu.text.UTF16; |
|
import java.io.DataInputStream; |
|
import java.io.InputStream; |
|
import java.io.IOException; |
|
/** |
|
* <p>A trie is a kind of compressed, serializable table of values |
|
* associated with Unicode code points (0..0x10ffff).</p> |
|
* <p>This class defines the basic structure of a trie and provides methods |
|
* to <b>retrieve the offsets to the actual data</b>.</p> |
|
* <p>Data will be the form of an array of basic types, char or int.</p> |
|
* <p>The actual data format will have to be specified by the user in the |
|
* inner static interface com.ibm.icu.impl.Trie.DataManipulate.</p> |
|
* <p>This trie implementation is optimized for getting offset while walking |
|
* forward through a UTF-16 string. |
|
* Therefore, the simplest and fastest access macros are the |
|
* fromLead() and fromOffsetTrail() methods. |
|
* The fromBMP() method are a little more complicated; they get offsets even |
|
* for lead surrogate codepoints, while the fromLead() method get special |
|
* "folded" offsets for lead surrogate code units if there is relevant data |
|
* associated with them. |
|
* From such a folded offsets, an offset needs to be extracted to supply |
|
* to the fromOffsetTrail() methods. |
|
* To handle such supplementary codepoints, some offset information are kept |
|
* in the data.</p> |
|
* <p>Methods in com.ibm.icu.impl.Trie.DataManipulate are called to retrieve |
|
* that offset from the folded value for the lead surrogate unit.</p> |
|
* <p>For examples of use, see com.ibm.icu.impl.CharTrie or |
|
* com.ibm.icu.impl.IntTrie.</p> |
|
* @author synwee |
|
* @see com.ibm.icu.impl.CharTrie |
|
* @see com.ibm.icu.impl.IntTrie |
|
* @since release 2.1, Jan 01 2002 |
|
*/ |
|
public abstract class Trie |
|
{ |
|
// public class declaration ---------------------------------------- |
|
/** |
|
* Character data in com.ibm.impl.Trie have different user-specified format |
|
* for different purposes. |
|
* This interface specifies methods to be implemented in order for |
|
* com.ibm.impl.Trie, to surrogate offset information encapsulated within |
|
* the data. |
|
*/ |
|
public static interface DataManipulate |
|
{ |
|
/** |
|
* Called by com.ibm.icu.impl.Trie to extract from a lead surrogate's |
|
* data |
|
* the index array offset of the indexes for that lead surrogate. |
|
* @param value data value for a surrogate from the trie, including the |
|
* folding offset |
|
* @return data offset or 0 if there is no data for the lead surrogate |
|
*/ |
|
public int getFoldingOffset(int value); |
|
} |
|
// default implementation |
|
private static class DefaultGetFoldingOffset implements DataManipulate { |
|
public int getFoldingOffset(int value) { |
|
return value; |
|
} |
|
} |
|
// protected constructor ------------------------------------------- |
|
/** |
|
* Trie constructor for CharTrie use. |
|
* @param inputStream ICU data file input stream which contains the |
|
* trie |
|
* @param dataManipulate object containing the information to parse the |
|
* trie data |
|
* @throws IOException thrown when input stream does not have the |
|
* right header. |
|
*/ |
|
protected Trie(InputStream inputStream, |
|
DataManipulate dataManipulate) throws IOException |
|
{ |
|
DataInputStream input = new DataInputStream(inputStream); |
|
// Magic number to authenticate the data. |
|
int signature = input.readInt(); |
|
m_options_ = input.readInt(); |
|
if (!checkHeader(signature)) { |
|
throw new IllegalArgumentException("ICU data file error: Trie header authentication failed, please check if you have the most updated ICU data file"); |
|
} |
|
if(dataManipulate != null) { |
|
m_dataManipulate_ = dataManipulate; |
|
} else { |
|
m_dataManipulate_ = new DefaultGetFoldingOffset(); |
|
} |
|
m_isLatin1Linear_ = (m_options_ & |
|
HEADER_OPTIONS_LATIN1_IS_LINEAR_MASK_) != 0; |
|
m_dataOffset_ = input.readInt(); |
|
m_dataLength_ = input.readInt(); |
|
unserialize(inputStream); |
|
} |
|
// protected data members ------------------------------------------ |
|
/** |
|
* Lead surrogate code points' index displacement in the index array. |
|
* <pre>{@code |
|
* 0x10000-0xd800=0x2800 |
|
* 0x2800 >> INDEX_STAGE_1_SHIFT_ |
|
* }</pre> |
|
*/ |
|
protected static final int LEAD_INDEX_OFFSET_ = 0x2800 >> 5; |
|
/** |
|
* Shift size for shifting right the input index. 1..9 |
|
*/ |
|
protected static final int INDEX_STAGE_1_SHIFT_ = 5; |
|
/** |
|
* Shift size for shifting left the index array values. |
|
* Increases possible data size with 16-bit index values at the cost |
|
* of compactability. |
|
* This requires blocks of stage 2 data to be aligned by |
|
* DATA_GRANULARITY. |
|
* 0..INDEX_STAGE_1_SHIFT |
|
*/ |
|
protected static final int INDEX_STAGE_2_SHIFT_ = 2; |
|
/** |
|
* Number of data values in a stage 2 (data array) block. |
|
*/ |
|
protected static final int DATA_BLOCK_LENGTH=1<<INDEX_STAGE_1_SHIFT_; |
|
/** |
|
* Mask for getting the lower bits from the input index. |
|
* DATA_BLOCK_LENGTH - 1. |
|
*/ |
|
protected static final int INDEX_STAGE_3_MASK_ = DATA_BLOCK_LENGTH - 1; |
|
/** |
|
* Surrogate mask to use when shifting offset to retrieve supplementary |
|
* values |
|
*/ |
|
protected static final int SURROGATE_MASK_ = 0x3FF; |
|
/** |
|
* Index or UTF16 characters |
|
*/ |
|
protected char m_index_[]; |
|
/** |
|
* Internal TrieValue which handles the parsing of the data value. |
|
* This class is to be implemented by the user |
|
*/ |
|
protected DataManipulate m_dataManipulate_; |
|
/** |
|
* Start index of the data portion of the trie. CharTrie combines |
|
* index and data into a char array, so this is used to indicate the |
|
* initial offset to the data portion. |
|
* Note this index always points to the initial value. |
|
*/ |
|
protected int m_dataOffset_; |
|
/** |
|
* Length of the data array |
|
*/ |
|
protected int m_dataLength_; |
|
// protected methods ----------------------------------------------- |
|
/** |
|
* Gets the offset to the data which the surrogate pair points to. |
|
* @param lead lead surrogate |
|
* @param trail trailing surrogate |
|
* @return offset to data |
|
*/ |
|
protected abstract int getSurrogateOffset(char lead, char trail); |
|
/** |
|
* Gets the offset to the data which the index ch after variable offset |
|
* points to. |
|
* Note for locating a non-supplementary character data offset, calling |
|
* <p> |
|
* getRawOffset(0, ch); |
|
* </p> |
|
* will do. Otherwise if it is a supplementary character formed by |
|
* surrogates lead and trail. Then we would have to call getRawOffset() |
|
* with getFoldingIndexOffset(). See getSurrogateOffset(). |
|
* @param offset index offset which ch is to start from |
|
* @param ch index to be used after offset |
|
* @return offset to the data |
|
*/ |
|
protected final int getRawOffset(int offset, char ch) |
|
{ |
|
return (m_index_[offset + (ch >> INDEX_STAGE_1_SHIFT_)] |
|
<< INDEX_STAGE_2_SHIFT_) |
|
+ (ch & INDEX_STAGE_3_MASK_); |
|
} |
|
/** |
|
* Gets the offset to data which the BMP character points to |
|
* Treats a lead surrogate as a normal code point. |
|
* @param ch BMP character |
|
* @return offset to data |
|
*/ |
|
protected final int getBMPOffset(char ch) |
|
{ |
|
return (ch >= UTF16.LEAD_SURROGATE_MIN_VALUE |
|
&& ch <= UTF16.LEAD_SURROGATE_MAX_VALUE) |
|
? getRawOffset(LEAD_INDEX_OFFSET_, ch) |
|
: getRawOffset(0, ch); |
|
// using a getRawOffset(ch) makes no diff |
|
} |
|
/** |
|
* Gets the offset to the data which this lead surrogate character points |
|
* to. |
|
* Data at the returned offset may contain folding offset information for |
|
* the next trailing surrogate character. |
|
* @param ch lead surrogate character |
|
* @return offset to data |
|
*/ |
|
protected final int getLeadOffset(char ch) |
|
{ |
|
return getRawOffset(0, ch); |
|
} |
|
/** |
|
* Internal trie getter from a code point. |
|
* Could be faster(?) but longer with |
|
* {@code if((c32)<=0xd7ff) { (result)=_TRIE_GET_RAW(trie, data, 0, c32); }} |
|
* Gets the offset to data which the codepoint points to |
|
* @param ch codepoint |
|
* @return offset to data |
|
*/ |
|
protected final int getCodePointOffset(int ch) |
|
{ |
|
// if ((ch >> 16) == 0) slower |
|
if (ch < 0) { |
|
return -1; |
|
} else if (ch < UTF16.LEAD_SURROGATE_MIN_VALUE) { |
|
// fastpath for the part of the BMP below surrogates (D800) where getRawOffset() works |
|
return getRawOffset(0, (char)ch); |
|
} else if (ch < UTF16.SUPPLEMENTARY_MIN_VALUE) { |
|
// BMP codepoint |
|
return getBMPOffset((char)ch); |
|
} else if (ch <= UCharacter.MAX_VALUE) { |
|
// look at the construction of supplementary characters |
|
// trail forms the ends of it. |
|
return getSurrogateOffset(UTF16.getLeadSurrogate(ch), |
|
(char)(ch & SURROGATE_MASK_)); |
|
} else { |
|
// return -1 if there is an error, in this case we return |
|
return -1; |
|
} |
|
} |
|
/** |
|
* <p>Parses the inputstream and creates the trie index with it.</p> |
|
* <p>This is overwritten by the child classes. |
|
* @param inputStream input stream containing the trie information |
|
* @exception IOException thrown when data reading fails. |
|
*/ |
|
protected void unserialize(InputStream inputStream) throws IOException |
|
{ |
|
//indexLength is a multiple of 1024 >> INDEX_STAGE_2_SHIFT_ |
|
m_index_ = new char[m_dataOffset_]; |
|
DataInputStream input = new DataInputStream(inputStream); |
|
for (int i = 0; i < m_dataOffset_; i ++) { |
|
m_index_[i] = input.readChar(); |
|
} |
|
} |
|
/** |
|
* Determines if this is a 16 bit trie |
|
* @return true if this is a 16 bit trie |
|
*/ |
|
protected final boolean isCharTrie() |
|
{ |
|
return (m_options_ & HEADER_OPTIONS_DATA_IS_32_BIT_) == 0; |
|
} |
|
// private data members -------------------------------------------- |
|
/** |
|
* Latin 1 option mask |
|
*/ |
|
protected static final int HEADER_OPTIONS_LATIN1_IS_LINEAR_MASK_ = 0x200; |
|
/** |
|
* Constant number to authenticate the byte block |
|
*/ |
|
protected static final int HEADER_SIGNATURE_ = 0x54726965; |
|
/** |
|
* Header option formatting |
|
*/ |
|
private static final int HEADER_OPTIONS_SHIFT_MASK_ = 0xF; |
|
protected static final int HEADER_OPTIONS_INDEX_SHIFT_ = 4; |
|
protected static final int HEADER_OPTIONS_DATA_IS_32_BIT_ = 0x100; |
|
/** |
|
* Flag indicator for Latin quick access data block |
|
*/ |
|
private boolean m_isLatin1Linear_; |
|
/** |
|
* <p>Trie options field.</p> |
|
* <p>options bit field:<br> |
|
* 9 1 = Latin-1 data is stored linearly at data + DATA_BLOCK_LENGTH<br> |
|
* 8 0 = 16-bit data, 1=32-bit data<br> |
|
* 7..4 INDEX_STAGE_1_SHIFT // 0..INDEX_STAGE_2_SHIFT<br> |
|
* 3..0 INDEX_STAGE_2_SHIFT // 1..9<br> |
|
*/ |
|
private int m_options_; |
|
// private methods --------------------------------------------------- |
|
/** |
|
* Authenticates raw data header. |
|
* Checking the header information, signature and options. |
|
* @param signature This contains the options and type of a Trie |
|
* @return true if the header is authenticated valid |
|
*/ |
|
private final boolean checkHeader(int signature) |
|
{ |
|
// check the signature |
|
// Trie in big-endian US-ASCII (0x54726965). |
|
// Magic number to authenticate the data. |
|
if (signature != HEADER_SIGNATURE_) { |
|
return false; |
|
} |
|
if ((m_options_ & HEADER_OPTIONS_SHIFT_MASK_) != |
|
INDEX_STAGE_1_SHIFT_ || |
|
((m_options_ >> HEADER_OPTIONS_INDEX_SHIFT_) & |
|
HEADER_OPTIONS_SHIFT_MASK_) |
|
!= INDEX_STAGE_2_SHIFT_) { |
|
return false; |
|
} |
|
return true; |
|
} |
|
} |