// Was testTrieRanges in ICU4C. Renamed to not conflict with ICU4J test framework. private void checkTrieRanges(String testName, String serializedName, bool withClone, int[][] setRanges, int[][] checkRanges) { string ns = #if FEATURE_TYPEEXTENSIONS_GETTYPEINFO typeof(Trie2Test).GetTypeInfo().Namespace; #else typeof(Trie2Test).Namespace; #endif // Run tests against Tries that were built by ICU4C and serialized. String fileName16 = ns + ".Trie2Test." + serializedName + ".16.tri2"; String fileName32 = ns + ".Trie2Test." + serializedName + ".32.tri2"; #if FEATURE_TYPEEXTENSIONS_GETTYPEINFO Assembly assembly = typeof(Trie2Test).GetTypeInfo().Assembly; #else Assembly assembly = typeof(Trie2Test).Assembly; #endif Stream @is = assembly.GetManifestResourceStream(fileName16); Trie2 trie16; try { trie16 = Trie2.CreateFromSerialized(ICUBinary.GetByteBufferFromStreamAndDisposeStream(@is)); } finally { @is.Dispose(); } trieGettersTest(testName, trie16, checkRanges); @is = assembly.GetManifestResourceStream(fileName32); Trie2 trie32; try { trie32 = Trie2.CreateFromSerialized(ICUBinary.GetByteBufferFromStreamAndDisposeStream(@is)); } finally { @is.Dispose(); } trieGettersTest(testName, trie32, checkRanges); // Run the same tests against locally contructed Tries. Trie2Writable trieW = genTrieFromSetRanges(setRanges); trieGettersTest(testName, trieW, checkRanges); assertEquals("", trieW, trie16); // Locally built tries must be assertEquals("", trieW, trie32); // the same as those imported from ICU4C Trie2_32 trie32a = trieW.ToTrie2_32(); trieGettersTest(testName, trie32a, checkRanges); Trie2_16 trie16a = trieW.ToTrie2_16(); trieGettersTest(testName, trie16a, checkRanges); }
internal static void Read(CollationTailoring @base, ByteBuffer inBytes, CollationTailoring tailoring) { tailoring.Version = ICUBinary.ReadHeader(inBytes, DATA_FORMAT, IS_ACCEPTABLE); if (@base != null && @base.GetUCAVersion() != tailoring.GetUCAVersion()) { throw new ICUException("Tailoring UCA version differs from base data UCA version"); } int inLength = inBytes.Remaining; if (inLength < 8) { throw new ICUException("not enough bytes"); } int indexesLength = inBytes.GetInt32(); // inIndexes[IX_INDEXES_LENGTH] if (indexesLength < 2 || inLength < indexesLength * 4) { throw new ICUException("not enough indexes"); } int[] inIndexes = new int[IX_TOTAL_SIZE + 1]; inIndexes[0] = indexesLength; for (int i = 1; i < indexesLength && i < inIndexes.Length; ++i) { inIndexes[i] = inBytes.GetInt32(); } for (int i = indexesLength; i < inIndexes.Length; ++i) { inIndexes[i] = -1; } if (indexesLength > inIndexes.Length) { ICUBinary.SkipBytes(inBytes, (indexesLength - inIndexes.Length) * 4); } // Assume that the tailoring data is in initial state, // with null pointers and 0 lengths. // Set pointers to non-empty data parts. // Do this in order of their byte offsets. (Should help porting to Java.) int index; // one of the indexes[] slots int offset; // byte offset for the index part int length; // number of bytes in the index part if (indexesLength > IX_TOTAL_SIZE) { length = inIndexes[IX_TOTAL_SIZE]; } else if (indexesLength > IX_REORDER_CODES_OFFSET) { length = inIndexes[indexesLength - 1]; } else { length = 0; // only indexes, and inLength was already checked for them } if (inLength < length) { throw new ICUException("not enough bytes"); } CollationData baseData = @base == null ? null : @base.Data; int[] reorderCodes; int reorderCodesLength; index = IX_REORDER_CODES_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; if (length >= 4) { if (baseData == null) { // We assume for collation settings that // the base data does not have a reordering. throw new ICUException("Collation base data must not reorder scripts"); } reorderCodesLength = length / 4; reorderCodes = ICUBinary.GetInts(inBytes, reorderCodesLength, length & 3); // The reorderRanges (if any) are the trailing reorderCodes entries. // Split the array at the boundary. // Script or reorder codes do not exceed 16-bit values. // Range limits are stored in the upper 16 bits, and are never 0. int reorderRangesLength = 0; while (reorderRangesLength < reorderCodesLength && (reorderCodes[reorderCodesLength - reorderRangesLength - 1] & 0xffff0000) != 0) { ++reorderRangesLength; } Debug.Assert(reorderRangesLength < reorderCodesLength); reorderCodesLength -= reorderRangesLength; } else { reorderCodes = new int[0]; reorderCodesLength = 0; ICUBinary.SkipBytes(inBytes, length); } // There should be a reorder table only if there are reorder codes. // However, when there are reorder codes the reorder table may be omitted to reduce // the data size. byte[] reorderTable = null; index = IX_REORDER_TABLE_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; if (length >= 256) { if (reorderCodesLength == 0) { throw new ICUException("Reordering table without reordering codes"); } reorderTable = new byte[256]; inBytes.Get(reorderTable); length -= 256; } else { // If we have reorder codes, then build the reorderTable at the end, // when the CollationData is otherwise complete. } ICUBinary.SkipBytes(inBytes, length); if (baseData != null && baseData.numericPrimary != (inIndexes[IX_OPTIONS] & 0xff000000L)) { throw new ICUException("Tailoring numeric primary weight differs from base data"); } CollationData data = null; // Remains null if there are no mappings. index = IX_TRIE_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; if (length >= 8) { tailoring.EnsureOwnedData(); data = tailoring.OwnedData; data.Base = baseData; data.numericPrimary = inIndexes[IX_OPTIONS] & 0xff000000L; data.trie = tailoring.Trie = Trie2_32.CreateFromSerialized(inBytes); int trieLength = data.trie.GetSerializedLength(); if (trieLength > length) { throw new ICUException("Not enough bytes for the mappings trie"); // No mappings. } length -= trieLength; } else if (baseData != null) { // Use the base data. Only the settings are tailored. tailoring.Data = baseData; } else { throw new ICUException("Missing collation data mappings"); // No mappings. } ICUBinary.SkipBytes(inBytes, length); index = IX_RESERVED8_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; ICUBinary.SkipBytes(inBytes, length); index = IX_CES_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; if (length >= 8) { if (data == null) { throw new ICUException("Tailored ces without tailored trie"); } data.ces = ICUBinary.GetLongs(inBytes, length / 8, length & 7); } else { ICUBinary.SkipBytes(inBytes, length); } index = IX_RESERVED10_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; ICUBinary.SkipBytes(inBytes, length); index = IX_CE32S_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; if (length >= 4) { if (data == null) { throw new ICUException("Tailored ce32s without tailored trie"); } data.ce32s = ICUBinary.GetInts(inBytes, length / 4, length & 3); } else { ICUBinary.SkipBytes(inBytes, length); } int jamoCE32sStart = inIndexes[IX_JAMO_CE32S_START]; if (jamoCE32sStart >= 0) { if (data == null || data.ce32s == null) { throw new ICUException("JamoCE32sStart index into non-existent ce32s[]"); } data.jamoCE32s = new int[CollationData.JAMO_CE32S_LENGTH]; // ICU4N specific - added extension method to IList<T> to handle "copy to" data.ce32s.CopyTo(jamoCE32sStart, data.jamoCE32s, 0, CollationData.JAMO_CE32S_LENGTH); } else if (data == null) { // Nothing to do. } else if (baseData != null) { data.jamoCE32s = baseData.jamoCE32s; } else { throw new ICUException("Missing Jamo CE32s for Hangul processing"); } index = IX_ROOT_ELEMENTS_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; if (length >= 4) { int rootElementsLength = length / 4; if (data == null) { throw new ICUException("Root elements but no mappings"); } if (rootElementsLength <= CollationRootElements.IX_SEC_TER_BOUNDARIES) { throw new ICUException("Root elements array too short"); } data.rootElements = new long[rootElementsLength]; for (int i = 0; i < rootElementsLength; ++i) { data.rootElements[i] = inBytes.GetInt32() & 0xffffffffL; // unsigned int -> long } long commonSecTer = data.rootElements[CollationRootElements.IX_COMMON_SEC_AND_TER_CE]; if (commonSecTer != Collation.COMMON_SEC_AND_TER_CE) { throw new ICUException("Common sec/ter weights in base data differ from the hardcoded value"); } long secTerBoundaries = data.rootElements[CollationRootElements.IX_SEC_TER_BOUNDARIES]; if ((secTerBoundaries.TripleShift(24)) < CollationKeys.SEC_COMMON_HIGH) { // [fixed last secondary common byte] is too low, // and secondary weights would collide with compressed common secondaries. throw new ICUException("[fixed last secondary common byte] is too low"); } length &= 3; } ICUBinary.SkipBytes(inBytes, length); index = IX_CONTEXTS_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; if (length >= 2) { if (data == null) { throw new ICUException("Tailored contexts without tailored trie"); } data.contexts = ICUBinary.GetString(inBytes, length / 2, length & 1); } else { ICUBinary.SkipBytes(inBytes, length); } index = IX_UNSAFE_BWD_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; if (length >= 2) { if (data == null) { throw new ICUException("Unsafe-backward-set but no mappings"); } if (baseData == null) { // Create the unsafe-backward set for the root collator. // Include all non-zero combining marks and trail surrogates. // We do this at load time, rather than at build time, // to simplify Unicode version bootstrapping: // The root data builder only needs the new FractionalUCA.txt data, // but it need not be built with a version of ICU already updated to // the corresponding new Unicode Character Database. // // The following is an optimized version of // new UnicodeSet("[[:^lccc=0:][\\udc00-\\udfff]]"). // It is faster and requires fewer code dependencies. tailoring.UnsafeBackwardSet = new UnicodeSet(0xdc00, 0xdfff); // trail surrogates data.nfcImpl.AddLcccChars(tailoring.UnsafeBackwardSet); } else { // Clone the root collator's set contents. tailoring.UnsafeBackwardSet = baseData.unsafeBackwardSet.CloneAsThawed(); } // Add the ranges from the data file to the unsafe-backward set. USerializedSet sset = new USerializedSet(); char[] unsafeData = ICUBinary.GetChars(inBytes, length / 2, length & 1); length = 0; sset.GetSet(unsafeData, 0); int count = sset.CountRanges(); int[] range = new int[2]; for (int i = 0; i < count; ++i) { sset.GetRange(i, range); tailoring.UnsafeBackwardSet.Add(range[0], range[1]); } // Mark each lead surrogate as "unsafe" // if any of its 1024 associated supplementary code points is "unsafe". int c = 0x10000; for (int lead = 0xd800; lead < 0xdc00; ++lead, c += 0x400) { if (!tailoring.UnsafeBackwardSet.ContainsNone(c, c + 0x3ff)) { tailoring.UnsafeBackwardSet.Add(lead); } } tailoring.UnsafeBackwardSet.Freeze(); data.unsafeBackwardSet = tailoring.UnsafeBackwardSet; } else if (data == null) { // Nothing to do. } else if (baseData != null) { // No tailoring-specific data: Alias the root collator's set. data.unsafeBackwardSet = baseData.unsafeBackwardSet; } else { throw new ICUException("Missing unsafe-backward-set"); } ICUBinary.SkipBytes(inBytes, length); // If the fast Latin format version is different, // or the version is set to 0 for "no fast Latin table", // then just always use the normal string comparison path. index = IX_FAST_LATIN_TABLE_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; if (data != null) { data.fastLatinTable = null; data.fastLatinTableHeader = null; if (((inIndexes[IX_OPTIONS] >> 16) & 0xff) == CollationFastLatin.VERSION) { if (length >= 2) { char header0 = inBytes.GetChar(); int headerLength = header0 & 0xff; data.fastLatinTableHeader = new char[headerLength]; data.fastLatinTableHeader[0] = header0; for (int i = 1; i < headerLength; ++i) { data.fastLatinTableHeader[i] = inBytes.GetChar(); } int tableLength = length / 2 - headerLength; data.fastLatinTable = ICUBinary.GetChars(inBytes, tableLength, length & 1); length = 0; if ((header0 >> 8) != CollationFastLatin.VERSION) { throw new ICUException("Fast-Latin table version differs from version in data header"); } } else if (baseData != null) { data.fastLatinTable = baseData.fastLatinTable; data.fastLatinTableHeader = baseData.fastLatinTableHeader; } } } ICUBinary.SkipBytes(inBytes, length); index = IX_SCRIPTS_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; if (length >= 2) { if (data == null) { throw new ICUException("Script order data but no mappings"); } int scriptsLength = length / 2; CharBuffer inChars = inBytes.AsCharBuffer(); data.numScripts = inChars.Get(); // There must be enough entries for both arrays, including more than two range starts. int scriptStartsLength = scriptsLength - (1 + data.numScripts + 16); if (scriptStartsLength <= 2) { throw new ICUException("Script order data too short"); } inChars.Get(data.scriptsIndex = new char[data.numScripts + 16]); inChars.Get(data.scriptStarts = new char[scriptStartsLength]); if (!(data.scriptStarts[0] == 0 && data.scriptStarts[1] == ((Collation.MERGE_SEPARATOR_BYTE + 1) << 8) && data.scriptStarts[scriptStartsLength - 1] == (Collation.TRAIL_WEIGHT_BYTE << 8))) { throw new ICUException("Script order data not valid"); } } else if (data == null) { // Nothing to do. } else if (baseData != null) { data.numScripts = baseData.numScripts; data.scriptsIndex = baseData.scriptsIndex; data.scriptStarts = baseData.scriptStarts; } ICUBinary.SkipBytes(inBytes, length); index = IX_COMPRESSIBLE_BYTES_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; if (length >= 256) { if (data == null) { throw new ICUException("Data for compressible primary lead bytes but no mappings"); } data.compressibleBytes = new bool[256]; for (int i = 0; i < 256; ++i) { data.compressibleBytes[i] = inBytes.Get() != 0; } length -= 256; } else if (data == null) { // Nothing to do. } else if (baseData != null) { data.compressibleBytes = baseData.compressibleBytes; } else { throw new ICUException("Missing data for compressible primary lead bytes"); } ICUBinary.SkipBytes(inBytes, length); index = IX_RESERVED18_OFFSET; offset = inIndexes[index]; length = inIndexes[index + 1] - offset; ICUBinary.SkipBytes(inBytes, length); CollationSettings ts = tailoring.Settings.ReadOnly; int options = inIndexes[IX_OPTIONS] & 0xffff; char[] fastLatinPrimaries = new char[CollationFastLatin.LATIN_LIMIT]; int fastLatinOptions = CollationFastLatin.GetOptions( tailoring.Data, ts, fastLatinPrimaries); if (options == ts.Options && ts.VariableTop != 0 && Arrays.Equals(reorderCodes, ts.ReorderCodes) && fastLatinOptions == ts.FastLatinOptions && (fastLatinOptions < 0 || Arrays.Equals(fastLatinPrimaries, ts.FastLatinPrimaries))) { return; } CollationSettings settings = tailoring.Settings.CopyOnWrite(); settings.Options = options; // Set variableTop from options and scripts data. settings.VariableTop = tailoring.Data.GetLastPrimaryForGroup( ReorderCodes.First + settings.MaxVariable); if (settings.VariableTop == 0) { throw new ICUException("The maxVariable could not be mapped to a variableTop"); } if (reorderCodesLength != 0) { settings.AliasReordering(baseData, reorderCodes, reorderCodesLength, reorderTable); } settings.FastLatinOptions = CollationFastLatin.GetOptions( tailoring.Data, settings, settings.FastLatinPrimaries); }
public void TestTrie2API() { // Trie2.createFromSerialized() // This function is well exercised by TestRanges(). // Trie2.getVersion(InputStream is, boolean anyEndianOk) // try { Trie2Writable trie = new Trie2Writable(0, 0); MemoryStream os = new MemoryStream(); trie.ToTrie2_16().Serialize(os); MemoryStream @is = new MemoryStream(os.ToArray()); assertEquals(null, 2, Trie2.GetVersion(@is, true)); } catch (IOException e) { Errln(where () + e.ToString()); } // Equals & hashCode // { Trie2Writable trieWA = new Trie2Writable(0, 0); Trie2Writable trieWB = new Trie2Writable(0, 0); Trie2 trieA = trieWA; Trie2 trieB = trieWB; assertTrue("", trieA.Equals(trieB)); assertEquals("", trieA, trieB); assertEquals("", trieA.GetHashCode(), trieB.GetHashCode()); trieWA.Set(500, 2); assertNotEquals("", trieA, trieB); // Note that the hash codes do not strictly need to be different, // but it's highly likely that something is wrong if they are the same. assertNotEquals("", trieA.GetHashCode(), trieB.GetHashCode()); trieWB.Set(500, 2); trieA = trieWA.ToTrie2_16(); assertEquals("", trieA, trieB); assertEquals("", trieA.GetHashCode(), trieB.GetHashCode()); } // // Iterator creation // { Trie2Writable trie = new Trie2Writable(17, 0); IEnumerator <Trie2Range> it; using (it = trie.GetEnumerator()) { it.MoveNext(); Trie2Range r = it.Current; assertEquals("", 0, r.StartCodePoint); assertEquals("", 0x10ffff, r.EndCodePoint); assertEquals("", 17, r.Value); assertEquals("", false, r.IsLeadSurrogate); it.MoveNext(); r = it.Current; assertEquals("", 0xd800, r.StartCodePoint); assertEquals("", 0xdbff, r.EndCodePoint); assertEquals("", 17, r.Value); assertEquals("", true, r.IsLeadSurrogate); int i = 0; foreach (Trie2Range rr in trie) { switch (i) { case 0: assertEquals("", 0, rr.StartCodePoint); assertEquals("", 0x10ffff, rr.EndCodePoint); assertEquals("", 17, rr.Value); assertEquals("", false, rr.IsLeadSurrogate); break; case 1: assertEquals("", 0xd800, rr.StartCodePoint); assertEquals("", 0xdbff, rr.EndCodePoint); assertEquals("", 17, rr.Value); assertEquals("", true, rr.IsLeadSurrogate); break; default: Errln(where () + " Unexpected iteration result"); break; } i++; } } } // Iteration with a value mapping function // { Trie2Writable trie = new Trie2Writable(0xbadfeed, 0); trie.Set(0x10123, 42); IValueMapper vm = new Trie2ValueMapper(map: (v) => { if (v == 0xbadfeed) { v = 42; } return(v); }); using (IEnumerator <Trie2Range> it2 = trie.GetEnumerator(vm)) { it2.MoveNext(); Trie2Range r = it2.Current; assertEquals("", 0, r.StartCodePoint); assertEquals("", 0x10ffff, r.EndCodePoint); assertEquals("", 42, r.Value); assertEquals("", false, r.IsLeadSurrogate); } } // Iteration over a leading surrogate range. // { Trie2Writable trie = new Trie2Writable(0xdefa17, 0); trie.Set(0x2f810, 10); using (IEnumerator <Trie2Range> it = trie.GetEnumeratorForLeadSurrogate((char)0xd87e)) { it.MoveNext(); Trie2Range r = it.Current; assertEquals("", 0x2f800, r.StartCodePoint); assertEquals("", 0x2f80f, r.EndCodePoint); assertEquals("", 0xdefa17, r.Value); assertEquals("", false, r.IsLeadSurrogate); it.MoveNext(); r = it.Current; assertEquals("", 0x2f810, r.StartCodePoint); assertEquals("", 0x2f810, r.EndCodePoint); assertEquals("", 10, r.Value); assertEquals("", false, r.IsLeadSurrogate); it.MoveNext(); r = it.Current; assertEquals("", 0x2f811, r.StartCodePoint); assertEquals("", 0x2fbff, r.EndCodePoint); assertEquals("", 0xdefa17, r.Value); assertEquals("", false, r.IsLeadSurrogate); assertFalse("", it.MoveNext()); } } // Iteration over a leading surrogate range with a ValueMapper. // { Trie2Writable trie = new Trie2Writable(0xdefa17, 0); trie.Set(0x2f810, 10); IValueMapper m = new Trie2ValueMapper(map: (@in) => { if (@in == 10) { @in = 0xdefa17; } return(@in); }); using (IEnumerator <Trie2Range> it = trie.GetEnumeratorForLeadSurrogate((char)0xd87e, m)) { it.MoveNext(); Trie2Range r = it.Current; assertEquals("", 0x2f800, r.StartCodePoint); assertEquals("", 0x2fbff, r.EndCodePoint); assertEquals("", 0xdefa17, r.Value); assertEquals("", false, r.IsLeadSurrogate); assertFalse("", it.MoveNext()); } } // Trie2.serialize() // Test the implementation in Trie2, which is used with Read Only Tries. // { Trie2Writable trie = new Trie2Writable(101, 0); trie.SetRange(0xf000, 0x3c000, 200, true); trie.Set(0xffee, 300); Trie2_16 frozen16 = trie.ToTrie2_16(); Trie2_32 frozen32 = trie.ToTrie2_32(); assertEquals("", trie, frozen16); assertEquals("", trie, frozen32); assertEquals("", frozen16, frozen32); MemoryStream os = new MemoryStream(); try { frozen16.Serialize(os); Trie2 unserialized16 = Trie2.CreateFromSerialized(ByteBuffer.Wrap(os.ToArray())); assertEquals("", trie, unserialized16); assertEquals("", typeof(Trie2_16), unserialized16.GetType()); os.Seek(0, SeekOrigin.Begin); frozen32.Serialize(os); Trie2 unserialized32 = Trie2.CreateFromSerialized(ByteBuffer.Wrap(os.ToArray())); assertEquals("", trie, unserialized32); assertEquals("", typeof(Trie2_32), unserialized32.GetType()); } catch (IOException e) { Errln(where () + " Unexpected exception: " + e); } } }
public void TestTrie2WritableAPI() { // // Trie2Writable methods. Check that all functions are present and // nominally working. Not an in-depth test. // // Trie2Writable constructor Trie2 t1 = new Trie2Writable(6, 666); // Constructor from another Trie2 Trie2 t2 = new Trie2Writable(t1); assertTrue("", t1.Equals(t2)); // Set / Get Trie2Writable t1w = new Trie2Writable(10, 666); t1w.Set(0x4567, 99); assertEquals("", 10, t1w.Get(0x4566)); assertEquals("", 99, t1w.Get(0x4567)); assertEquals("", 666, t1w.Get(-1)); assertEquals("", 666, t1w.Get(0x110000)); // SetRange t1w = new Trie2Writable(10, 666); t1w.SetRange(13 /*start*/, 6666 /*end*/, 7788 /*value*/, false /*overwrite */); t1w.SetRange(6000, 7000, 9900, true); assertEquals("", 10, t1w.Get(12)); assertEquals("", 7788, t1w.Get(13)); assertEquals("", 7788, t1w.Get(5999)); assertEquals("", 9900, t1w.Get(6000)); assertEquals("", 9900, t1w.Get(7000)); assertEquals("", 10, t1w.Get(7001)); assertEquals("", 666, t1w.Get(0x110000)); // setRange from a Trie2.Range // (Ranges are more commonly created by iterating over a Trie2, // but create one by hand here) Trie2Range r = new Trie2Range(); r.StartCodePoint = 50; r.EndCodePoint = 52; r.Value = 0x12345678; r.IsLeadSurrogate = false; t1w = new Trie2Writable(0, 0xbad); t1w.SetRange(r, true); assertEquals(null, 0, t1w.Get(49)); assertEquals("", 0x12345678, t1w.Get(50)); assertEquals("", 0x12345678, t1w.Get(52)); assertEquals("", 0, t1w.Get(53)); // setForLeadSurrogateCodeUnit / getFromU16SingleLead t1w = new Trie2Writable(10, 0xbad); assertEquals("", 10, t1w.GetFromU16SingleLead((char)0x0d801)); t1w.SetForLeadSurrogateCodeUnit((char)0xd801, 5000); t1w.Set(0xd801, 6000); assertEquals("", 5000, t1w.GetFromU16SingleLead((char)0x0d801)); assertEquals("", 6000, t1w.Get(0x0d801)); // get(). Is covered by nearly every other test. // Trie2_16 getAsFrozen_16() t1w = new Trie2Writable(10, 666); t1w.Set(42, 5555); t1w.Set(0x1ff00, 224); Trie2_16 t1_16 = t1w.ToTrie2_16(); assertTrue("", t1w.Equals(t1_16)); // alter the writable Trie2 and then re-freeze. t1w.Set(152, 129); t1_16 = t1w.ToTrie2_16(); assertTrue("", t1w.Equals(t1_16)); assertEquals("", 129, t1w.Get(152)); // Trie2_32 getAsFrozen_32() // t1w = new Trie2Writable(10, 666); t1w.Set(42, 5555); t1w.Set(0x1ff00, 224); Trie2_32 t1_32 = t1w.ToTrie2_32(); assertTrue("", t1w.Equals(t1_32)); // alter the writable Trie2 and then re-freeze. t1w.Set(152, 129); assertNotEquals("", t1_32, t1w); t1_32 = t1w.ToTrie2_32(); assertTrue("", t1w.Equals(t1_32)); assertEquals("", 129, t1w.Get(152)); // serialize(OutputStream os, ValueWidth width) // MemoryStream os = new MemoryStream(); t1w = new Trie2Writable(0, 0xbad); t1w.Set(0x41, 0x100); t1w.Set(0xc2, 0x200); t1w.Set(0x404, 0x300); t1w.Set(0xd903, 0x500); t1w.Set(0xdd29, 0x600); t1w.Set(0x1055d3, 0x700); t1w.SetForLeadSurrogateCodeUnit((char)0xda1a, 0x800); try { // Serialize to 16 bits. int serializedLen = t1w.ToTrie2_16().Serialize(os); // Fragile test. Serialized length could change with changes to compaction. // But it should not change unexpectedly. assertEquals("", 3508, serializedLen); Trie2 t1ws16 = Trie2.CreateFromSerialized(ByteBuffer.Wrap(os.ToArray())); assertEquals("", t1ws16.GetType(), typeof(Trie2_16)); assertEquals("", t1w, t1ws16); // Serialize to 32 bits os.Seek(0, SeekOrigin.Begin); serializedLen = t1w.ToTrie2_32().Serialize(os); // Fragile test. Serialized length could change with changes to compaction. // But it should not change unexpectedly. assertEquals("", 4332, serializedLen); Trie2 t1ws32 = Trie2.CreateFromSerialized(ByteBuffer.Wrap(os.ToArray())); assertEquals("", t1ws32.GetType(), typeof(Trie2_32)); assertEquals("", t1w, t1ws32); } catch (IOException e) { Errln(where () + e.ToString()); } }