]> git.saurik.com Git - apple/icu.git/blame - icuSources/common/triedict.cpp
ICU-461.12.tar.gz
[apple/icu.git] / icuSources / common / triedict.cpp
CommitLineData
73c04bcf
A
1/**
2 *******************************************************************************
46f4442e
A
3 * Copyright (C) 2006-2008, International Business Machines Corporation *
4 * and others. All Rights Reserved. *
73c04bcf
A
5 *******************************************************************************
6 */
7
8#include "unicode/utypes.h"
9
10#if !UCONFIG_NO_BREAK_ITERATION
11
12#include "triedict.h"
13#include "unicode/chariter.h"
14#include "unicode/uchriter.h"
15#include "unicode/strenum.h"
16#include "unicode/uenum.h"
17#include "unicode/udata.h"
18#include "cmemory.h"
19#include "udataswp.h"
20#include "uvector.h"
21#include "uvectr32.h"
22#include "uarrsort.h"
23
24//#define DEBUG_TRIE_DICT 1
25
26#ifdef DEBUG_TRIE_DICT
27#include <sys/times.h>
28#include <limits.h>
29#include <stdio.h>
30#endif
31
32U_NAMESPACE_BEGIN
33
34/*******************************************************************
35 * TrieWordDictionary
36 */
37
38TrieWordDictionary::TrieWordDictionary() {
39}
40
41TrieWordDictionary::~TrieWordDictionary() {
42}
43
44/*******************************************************************
45 * MutableTrieDictionary
46 */
47
48// Node structure for the ternary, uncompressed trie
49struct TernaryNode : public UMemory {
50 UChar ch; // UTF-16 code unit
51 uint16_t flags; // Flag word
52 TernaryNode *low; // Less-than link
53 TernaryNode *equal; // Equal link
54 TernaryNode *high; // Greater-than link
55
56 TernaryNode(UChar uc);
57 ~TernaryNode();
58};
59
60enum MutableTrieNodeFlags {
61 kEndsWord = 0x0001 // This node marks the end of a valid word
62};
63
64inline
65TernaryNode::TernaryNode(UChar uc) {
66 ch = uc;
67 flags = 0;
68 low = NULL;
69 equal = NULL;
70 high = NULL;
71}
72
73// Not inline since it's recursive
74TernaryNode::~TernaryNode() {
75 delete low;
76 delete equal;
77 delete high;
78}
79
80MutableTrieDictionary::MutableTrieDictionary( UChar median, UErrorCode &status ) {
81 // Start the trie off with something. Having the root node already present
82 // cuts a special case out of the search/insertion functions.
83 // Making it a median character cuts the worse case for searches from
84 // 4x a balanced trie to 2x a balanced trie. It's best to choose something
85 // that starts a word that is midway in the list.
86 fTrie = new TernaryNode(median);
87 if (fTrie == NULL) {
88 status = U_MEMORY_ALLOCATION_ERROR;
89 }
90 fIter = utext_openUChars(NULL, NULL, 0, &status);
91 if (U_SUCCESS(status) && fIter == NULL) {
92 status = U_MEMORY_ALLOCATION_ERROR;
93 }
94}
95
96MutableTrieDictionary::MutableTrieDictionary( UErrorCode &status ) {
97 fTrie = NULL;
98 fIter = utext_openUChars(NULL, NULL, 0, &status);
99 if (U_SUCCESS(status) && fIter == NULL) {
100 status = U_MEMORY_ALLOCATION_ERROR;
101 }
102}
103
104MutableTrieDictionary::~MutableTrieDictionary() {
105 delete fTrie;
106 utext_close(fIter);
107}
108
109int32_t
110MutableTrieDictionary::search( UText *text,
111 int32_t maxLength,
112 int32_t *lengths,
113 int &count,
114 int limit,
115 TernaryNode *&parent,
116 UBool &pMatched ) const {
117 // TODO: current implementation works in UTF-16 space
118 const TernaryNode *up = NULL;
119 const TernaryNode *p = fTrie;
120 int mycount = 0;
121 pMatched = TRUE;
122 int i;
123
124 UChar uc = utext_current32(text);
125 for (i = 0; i < maxLength && p != NULL; ++i) {
126 while (p != NULL) {
127 if (uc < p->ch) {
128 up = p;
129 p = p->low;
130 }
131 else if (uc == p->ch) {
132 break;
133 }
134 else {
135 up = p;
136 p = p->high;
137 }
138 }
139 if (p == NULL) {
140 pMatched = FALSE;
141 break;
142 }
143 // Must be equal to get here
144 if (limit > 0 && (p->flags & kEndsWord)) {
145 lengths[mycount++] = i+1;
146 --limit;
147 }
148 up = p;
149 p = p->equal;
150 uc = utext_next32(text);
151 uc = utext_current32(text);
152 }
153
154 // Note that there is no way to reach here with up == 0 unless
155 // maxLength is 0 coming in.
156 parent = (TernaryNode *)up;
157 count = mycount;
158 return i;
159}
160
161void
162MutableTrieDictionary::addWord( const UChar *word,
163 int32_t length,
164 UErrorCode &status ) {
165#if 0
166 if (length <= 0) {
167 status = U_ILLEGAL_ARGUMENT_ERROR;
168 return;
169 }
170#endif
171 TernaryNode *parent;
172 UBool pMatched;
173 int count;
174 fIter = utext_openUChars(fIter, word, length, &status);
175
176 int matched;
177 matched = search(fIter, length, NULL, count, 0, parent, pMatched);
178
179 while (matched++ < length) {
180 UChar32 uc = utext_next32(fIter); // TODO: supplemetary support?
181 U_ASSERT(uc != U_SENTINEL);
182 TernaryNode *newNode = new TernaryNode(uc);
183 if (newNode == NULL) {
184 status = U_MEMORY_ALLOCATION_ERROR;
185 return;
186 }
187 if (pMatched) {
188 parent->equal = newNode;
189 }
190 else {
191 pMatched = TRUE;
192 if (uc < parent->ch) {
193 parent->low = newNode;
194 }
195 else {
196 parent->high = newNode;
197 }
198 }
199 parent = newNode;
200 }
201
202 parent->flags |= kEndsWord;
203}
204
205#if 0
206void
207MutableTrieDictionary::addWords( UEnumeration *words,
208 UErrorCode &status ) {
209 int32_t length;
210 const UChar *word;
211 while ((word = uenum_unext(words, &length, &status)) && U_SUCCESS(status)) {
212 addWord(word, length, status);
213 }
214}
215#endif
216
217int32_t
218MutableTrieDictionary::matches( UText *text,
219 int32_t maxLength,
220 int32_t *lengths,
221 int &count,
222 int limit ) const {
223 TernaryNode *parent;
224 UBool pMatched;
225 return search(text, maxLength, lengths, count, limit, parent, pMatched);
226}
227
228// Implementation of iteration for MutableTrieDictionary
229class MutableTrieEnumeration : public StringEnumeration {
230private:
231 UStack fNodeStack; // Stack of nodes to process
232 UVector32 fBranchStack; // Stack of which branch we are working on
233 TernaryNode *fRoot; // Root node
73c04bcf
A
234 enum StackBranch {
235 kLessThan,
236 kEqual,
237 kGreaterThan,
238 kDone
239 };
240
241public:
46f4442e
A
242 static UClassID U_EXPORT2 getStaticClassID(void);
243 virtual UClassID getDynamicClassID(void) const;
73c04bcf
A
244public:
245 MutableTrieEnumeration(TernaryNode *root, UErrorCode &status)
246 : fNodeStack(status), fBranchStack(status) {
247 fRoot = root;
248 fNodeStack.push(root, status);
249 fBranchStack.push(kLessThan, status);
250 unistr.remove();
251 }
252
253 virtual ~MutableTrieEnumeration() {
254 }
255
256 virtual StringEnumeration *clone() const {
257 UErrorCode status = U_ZERO_ERROR;
258 return new MutableTrieEnumeration(fRoot, status);
259 }
260
261 virtual const UnicodeString *snext(UErrorCode &status) {
262 if (fNodeStack.empty() || U_FAILURE(status)) {
263 return NULL;
264 }
265 TernaryNode *node = (TernaryNode *) fNodeStack.peek();
266 StackBranch where = (StackBranch) fBranchStack.peeki();
267 while (!fNodeStack.empty() && U_SUCCESS(status)) {
268 UBool emit;
269 UBool equal;
270
271 switch (where) {
272 case kLessThan:
273 if (node->low != NULL) {
274 fBranchStack.setElementAt(kEqual, fBranchStack.size()-1);
275 node = (TernaryNode *) fNodeStack.push(node->low, status);
276 where = (StackBranch) fBranchStack.push(kLessThan, status);
277 break;
278 }
279 case kEqual:
280 emit = (node->flags & kEndsWord) != 0;
281 equal = (node->equal != NULL);
282 // If this node should be part of the next emitted string, append
283 // the UChar to the string, and make sure we pop it when we come
284 // back to this node. The character should only be in the string
285 // for as long as we're traversing the equal subtree of this node
286 if (equal || emit) {
287 unistr.append(node->ch);
288 fBranchStack.setElementAt(kGreaterThan, fBranchStack.size()-1);
289 }
290 if (equal) {
291 node = (TernaryNode *) fNodeStack.push(node->equal, status);
292 where = (StackBranch) fBranchStack.push(kLessThan, status);
293 }
294 if (emit) {
295 return &unistr;
296 }
297 if (equal) {
298 break;
299 }
300 case kGreaterThan:
301 // If this node's character is in the string, remove it.
302 if (node->equal != NULL || (node->flags & kEndsWord)) {
303 unistr.truncate(unistr.length()-1);
304 }
305 if (node->high != NULL) {
306 fBranchStack.setElementAt(kDone, fBranchStack.size()-1);
307 node = (TernaryNode *) fNodeStack.push(node->high, status);
308 where = (StackBranch) fBranchStack.push(kLessThan, status);
309 break;
310 }
311 case kDone:
312 fNodeStack.pop();
313 fBranchStack.popi();
314 node = (TernaryNode *) fNodeStack.peek();
315 where = (StackBranch) fBranchStack.peeki();
316 break;
317 default:
318 return NULL;
319 }
320 }
321 return NULL;
322 }
323
324 // Very expensive, but this should never be used.
325 virtual int32_t count(UErrorCode &status) const {
326 MutableTrieEnumeration counter(fRoot, status);
327 int32_t result = 0;
328 while (counter.snext(status) != NULL && U_SUCCESS(status)) {
329 ++result;
330 }
331 return result;
332 }
333
334 virtual void reset(UErrorCode &status) {
335 fNodeStack.removeAllElements();
336 fBranchStack.removeAllElements();
337 fNodeStack.push(fRoot, status);
338 fBranchStack.push(kLessThan, status);
339 unistr.remove();
340 }
341};
342
46f4442e 343UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MutableTrieEnumeration)
73c04bcf
A
344
345StringEnumeration *
346MutableTrieDictionary::openWords( UErrorCode &status ) const {
347 if (U_FAILURE(status)) {
348 return NULL;
349 }
350 return new MutableTrieEnumeration(fTrie, status);
351}
352
353/*******************************************************************
354 * CompactTrieDictionary
355 */
356
357struct CompactTrieHeader {
358 uint32_t size; // Size of the data in bytes
359 uint32_t magic; // Magic number (including version)
360 uint16_t nodeCount; // Number of entries in offsets[]
361 uint16_t root; // Node number of the root node
362 uint32_t offsets[1]; // Offsets to nodes from start of data
363};
364
365// Note that to avoid platform-specific alignment issues, all members of the node
366// structures should be the same size, or should contain explicit padding to
367// natural alignment boundaries.
368
369// We can't use a bitfield for the flags+count field, because the layout of those
370// is not portable. 12 bits of count allows for up to 4096 entries in a node.
371struct CompactTrieNode {
372 uint16_t flagscount; // Count of sub-entries, plus flags
373};
374
375enum CompactTrieNodeFlags {
376 kVerticalNode = 0x1000, // This is a vertical node
377 kParentEndsWord = 0x2000, // The node whose equal link points to this ends a word
378 kReservedFlag1 = 0x4000,
379 kReservedFlag2 = 0x8000,
380 kCountMask = 0x0FFF, // The count portion of flagscount
381 kFlagMask = 0xF000 // The flags portion of flagscount
382};
383
384// The two node types are distinguished by the kVerticalNode flag.
385
386struct CompactTrieHorizontalEntry {
387 uint16_t ch; // UChar
388 uint16_t equal; // Equal link node index
389};
390
391// We don't use inheritance here because C++ does not guarantee that the
392// base class comes first in memory!!
393
394struct CompactTrieHorizontalNode {
395 uint16_t flagscount; // Count of sub-entries, plus flags
396 CompactTrieHorizontalEntry entries[1];
397};
398
399struct CompactTrieVerticalNode {
400 uint16_t flagscount; // Count of sub-entries, plus flags
401 uint16_t equal; // Equal link node index
402 uint16_t chars[1]; // Code units
403};
404
405// {'Dic', 1}, version 1
406#define COMPACT_TRIE_MAGIC_1 0x44696301
407
408CompactTrieDictionary::CompactTrieDictionary(UDataMemory *dataObj,
409 UErrorCode &status )
410: fUData(dataObj)
411{
412 fData = (const CompactTrieHeader *) udata_getMemory(dataObj);
413 fOwnData = FALSE;
414 if (fData->magic != COMPACT_TRIE_MAGIC_1) {
415 status = U_ILLEGAL_ARGUMENT_ERROR;
416 fData = NULL;
417 }
418}
419CompactTrieDictionary::CompactTrieDictionary( const void *data,
420 UErrorCode &status )
421: fUData(NULL)
422{
423 fData = (const CompactTrieHeader *) data;
424 fOwnData = FALSE;
425 if (fData->magic != COMPACT_TRIE_MAGIC_1) {
426 status = U_ILLEGAL_ARGUMENT_ERROR;
427 fData = NULL;
428 }
429}
430
431CompactTrieDictionary::CompactTrieDictionary( const MutableTrieDictionary &dict,
432 UErrorCode &status )
433: fUData(NULL)
434{
435 fData = compactMutableTrieDictionary(dict, status);
436 fOwnData = !U_FAILURE(status);
437}
438
439CompactTrieDictionary::~CompactTrieDictionary() {
440 if (fOwnData) {
441 uprv_free((void *)fData);
442 }
443 if (fUData) {
444 udata_close(fUData);
445 }
446}
447
448uint32_t
449CompactTrieDictionary::dataSize() const {
450 return fData->size;
451}
452
453const void *
454CompactTrieDictionary::data() const {
455 return fData;
456}
457
458// This function finds the address of a node for us, given its node ID
459static inline const CompactTrieNode *
460getCompactNode(const CompactTrieHeader *header, uint16_t node) {
461 return (const CompactTrieNode *)((const uint8_t *)header + header->offsets[node]);
462}
463
464int32_t
465CompactTrieDictionary::matches( UText *text,
466 int32_t maxLength,
467 int32_t *lengths,
468 int &count,
469 int limit ) const {
470 // TODO: current implementation works in UTF-16 space
471 const CompactTrieNode *node = getCompactNode(fData, fData->root);
472 int mycount = 0;
473
474 UChar uc = utext_current32(text);
475 int i = 0;
476
477 while (node != NULL) {
478 // Check if the node we just exited ends a word
479 if (limit > 0 && (node->flagscount & kParentEndsWord)) {
480 lengths[mycount++] = i;
481 --limit;
482 }
483 // Check that we haven't exceeded the maximum number of input characters.
484 // We have to do that here rather than in the while condition so that
485 // we can check for ending a word, above.
486 if (i >= maxLength) {
487 break;
488 }
489
490 int nodeCount = (node->flagscount & kCountMask);
491 if (nodeCount == 0) {
492 // Special terminal node; return now
493 break;
494 }
495 if (node->flagscount & kVerticalNode) {
496 // Vertical node; check all the characters in it
497 const CompactTrieVerticalNode *vnode = (const CompactTrieVerticalNode *)node;
498 for (int j = 0; j < nodeCount && i < maxLength; ++j) {
499 if (uc != vnode->chars[j]) {
500 // We hit a non-equal character; return
501 goto exit;
502 }
503 utext_next32(text);
504 uc = utext_current32(text);
505 ++i;
506 }
507 // To get here we must have come through the whole list successfully;
508 // go on to the next node. Note that a word cannot end in the middle
509 // of a vertical node.
510 node = getCompactNode(fData, vnode->equal);
511 }
512 else {
513 // Horizontal node; do binary search
514 const CompactTrieHorizontalNode *hnode = (const CompactTrieHorizontalNode *)node;
515 int low = 0;
516 int high = nodeCount-1;
517 int middle;
518 node = NULL; // If we don't find a match, we'll fall out of the loop
519 while (high >= low) {
520 middle = (high+low)/2;
521 if (uc == hnode->entries[middle].ch) {
522 // We hit a match; get the next node and next character
523 node = getCompactNode(fData, hnode->entries[middle].equal);
524 utext_next32(text);
525 uc = utext_current32(text);
526 ++i;
527 break;
528 }
529 else if (uc < hnode->entries[middle].ch) {
530 high = middle-1;
531 }
532 else {
533 low = middle+1;
534 }
535 }
536 }
537 }
538exit:
539 count = mycount;
540 return i;
541}
542
543// Implementation of iteration for CompactTrieDictionary
544class CompactTrieEnumeration : public StringEnumeration {
545private:
546 UVector32 fNodeStack; // Stack of nodes to process
547 UVector32 fIndexStack; // Stack of where in node we are
548 const CompactTrieHeader *fHeader; // Trie data
73c04bcf
A
549
550public:
46f4442e
A
551 static UClassID U_EXPORT2 getStaticClassID(void);
552 virtual UClassID getDynamicClassID(void) const;
73c04bcf
A
553public:
554 CompactTrieEnumeration(const CompactTrieHeader *header, UErrorCode &status)
555 : fNodeStack(status), fIndexStack(status) {
556 fHeader = header;
557 fNodeStack.push(header->root, status);
558 fIndexStack.push(0, status);
559 unistr.remove();
560 }
561
562 virtual ~CompactTrieEnumeration() {
563 }
564
565 virtual StringEnumeration *clone() const {
566 UErrorCode status = U_ZERO_ERROR;
567 return new CompactTrieEnumeration(fHeader, status);
568 }
569
570 virtual const UnicodeString * snext(UErrorCode &status);
571
572 // Very expensive, but this should never be used.
573 virtual int32_t count(UErrorCode &status) const {
574 CompactTrieEnumeration counter(fHeader, status);
575 int32_t result = 0;
576 while (counter.snext(status) != NULL && U_SUCCESS(status)) {
577 ++result;
578 }
579 return result;
580 }
581
582 virtual void reset(UErrorCode &status) {
583 fNodeStack.removeAllElements();
584 fIndexStack.removeAllElements();
585 fNodeStack.push(fHeader->root, status);
586 fIndexStack.push(0, status);
587 unistr.remove();
588 }
589};
590
46f4442e 591UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CompactTrieEnumeration)
73c04bcf
A
592
593const UnicodeString *
594CompactTrieEnumeration::snext(UErrorCode &status) {
595 if (fNodeStack.empty() || U_FAILURE(status)) {
596 return NULL;
597 }
598 const CompactTrieNode *node = getCompactNode(fHeader, fNodeStack.peeki());
599 int where = fIndexStack.peeki();
600 while (!fNodeStack.empty() && U_SUCCESS(status)) {
601 int nodeCount = (node->flagscount & kCountMask);
602 UBool goingDown = FALSE;
603 if (nodeCount == 0) {
604 // Terminal node; go up immediately
605 fNodeStack.popi();
606 fIndexStack.popi();
607 node = getCompactNode(fHeader, fNodeStack.peeki());
608 where = fIndexStack.peeki();
609 }
610 else if (node->flagscount & kVerticalNode) {
611 // Vertical node
612 const CompactTrieVerticalNode *vnode = (const CompactTrieVerticalNode *)node;
613 if (where == 0) {
614 // Going down
615 unistr.append((const UChar *)vnode->chars, (int32_t) nodeCount);
616 fIndexStack.setElementAt(1, fIndexStack.size()-1);
617 node = getCompactNode(fHeader, fNodeStack.push(vnode->equal, status));
618 where = fIndexStack.push(0, status);
619 goingDown = TRUE;
620 }
621 else {
622 // Going up
623 unistr.truncate(unistr.length()-nodeCount);
624 fNodeStack.popi();
625 fIndexStack.popi();
626 node = getCompactNode(fHeader, fNodeStack.peeki());
627 where = fIndexStack.peeki();
628 }
629 }
630 else {
631 // Horizontal node
632 const CompactTrieHorizontalNode *hnode = (const CompactTrieHorizontalNode *)node;
633 if (where > 0) {
634 // Pop previous char
635 unistr.truncate(unistr.length()-1);
636 }
637 if (where < nodeCount) {
638 // Push on next node
639 unistr.append((UChar)hnode->entries[where].ch);
640 fIndexStack.setElementAt(where+1, fIndexStack.size()-1);
641 node = getCompactNode(fHeader, fNodeStack.push(hnode->entries[where].equal, status));
642 where = fIndexStack.push(0, status);
643 goingDown = TRUE;
644 }
645 else {
646 // Going up
647 fNodeStack.popi();
648 fIndexStack.popi();
649 node = getCompactNode(fHeader, fNodeStack.peeki());
650 where = fIndexStack.peeki();
651 }
652 }
653 // Check if the parent of the node we've just gone down to ends a
654 // word. If so, return it.
655 if (goingDown && (node->flagscount & kParentEndsWord)) {
656 return &unistr;
657 }
658 }
659 return NULL;
660}
661
662StringEnumeration *
663CompactTrieDictionary::openWords( UErrorCode &status ) const {
664 if (U_FAILURE(status)) {
665 return NULL;
666 }
667 return new CompactTrieEnumeration(fData, status);
668}
669
670//
671// Below here is all code related to converting a ternary trie to a compact trie
672// and back again
673//
674
675// Helper classes to construct the compact trie
676class BuildCompactTrieNode: public UMemory {
677 public:
678 UBool fParentEndsWord;
679 UBool fVertical;
680 UBool fHasDuplicate;
681 int32_t fNodeID;
682 UnicodeString fChars;
683
684 public:
685 BuildCompactTrieNode(UBool parentEndsWord, UBool vertical, UStack &nodes, UErrorCode &status) {
686 fParentEndsWord = parentEndsWord;
687 fHasDuplicate = FALSE;
688 fVertical = vertical;
689 fNodeID = nodes.size();
690 nodes.push(this, status);
691 }
692
693 virtual ~BuildCompactTrieNode() {
694 }
695
696 virtual uint32_t size() {
697 return sizeof(uint16_t);
698 }
699
700 virtual void write(uint8_t *bytes, uint32_t &offset, const UVector32 &/*translate*/) {
701 // Write flag/count
702 *((uint16_t *)(bytes+offset)) = (fChars.length() & kCountMask)
703 | (fVertical ? kVerticalNode : 0) | (fParentEndsWord ? kParentEndsWord : 0 );
704 offset += sizeof(uint16_t);
705 }
706};
707
708class BuildCompactTrieHorizontalNode: public BuildCompactTrieNode {
709 public:
710 UStack fLinks;
711
712 public:
713 BuildCompactTrieHorizontalNode(UBool parentEndsWord, UStack &nodes, UErrorCode &status)
714 : BuildCompactTrieNode(parentEndsWord, FALSE, nodes, status), fLinks(status) {
715 }
716
717 virtual ~BuildCompactTrieHorizontalNode() {
718 }
719
720 virtual uint32_t size() {
721 return offsetof(CompactTrieHorizontalNode,entries) +
722 (fChars.length()*sizeof(CompactTrieHorizontalEntry));
723 }
724
725 virtual void write(uint8_t *bytes, uint32_t &offset, const UVector32 &translate) {
726 BuildCompactTrieNode::write(bytes, offset, translate);
727 int32_t count = fChars.length();
728 for (int32_t i = 0; i < count; ++i) {
729 CompactTrieHorizontalEntry *entry = (CompactTrieHorizontalEntry *)(bytes+offset);
730 entry->ch = fChars[i];
731 entry->equal = translate.elementAti(((BuildCompactTrieNode *)fLinks[i])->fNodeID);
732#ifdef DEBUG_TRIE_DICT
733 if (entry->equal == 0) {
734 fprintf(stderr, "ERROR: horizontal link %d, logical node %d maps to physical node zero\n",
735 i, ((BuildCompactTrieNode *)fLinks[i])->fNodeID);
736 }
737#endif
738 offset += sizeof(CompactTrieHorizontalEntry);
739 }
740 }
741
742 void addNode(UChar ch, BuildCompactTrieNode *link, UErrorCode &status) {
743 fChars.append(ch);
744 fLinks.push(link, status);
745 }
746};
747
748class BuildCompactTrieVerticalNode: public BuildCompactTrieNode {
749 public:
750 BuildCompactTrieNode *fEqual;
751
752 public:
753 BuildCompactTrieVerticalNode(UBool parentEndsWord, UStack &nodes, UErrorCode &status)
754 : BuildCompactTrieNode(parentEndsWord, TRUE, nodes, status) {
755 fEqual = NULL;
756 }
757
758 virtual ~BuildCompactTrieVerticalNode() {
759 }
760
761 virtual uint32_t size() {
762 return offsetof(CompactTrieVerticalNode,chars) + (fChars.length()*sizeof(uint16_t));
763 }
764
765 virtual void write(uint8_t *bytes, uint32_t &offset, const UVector32 &translate) {
766 CompactTrieVerticalNode *node = (CompactTrieVerticalNode *)(bytes+offset);
767 BuildCompactTrieNode::write(bytes, offset, translate);
768 node->equal = translate.elementAti(fEqual->fNodeID);
769 offset += sizeof(node->equal);
770#ifdef DEBUG_TRIE_DICT
771 if (node->equal == 0) {
772 fprintf(stderr, "ERROR: vertical link, logical node %d maps to physical node zero\n",
773 fEqual->fNodeID);
774 }
775#endif
776 fChars.extract(0, fChars.length(), (UChar *)node->chars);
777 offset += sizeof(uint16_t)*fChars.length();
778 }
779
780 void addChar(UChar ch) {
781 fChars.append(ch);
782 }
783
784 void setLink(BuildCompactTrieNode *node) {
785 fEqual = node;
786 }
787};
788
789// Forward declaration
790static void walkHorizontal(const TernaryNode *node,
791 BuildCompactTrieHorizontalNode *building,
792 UStack &nodes,
793 UErrorCode &status);
794
795// Convert one node. Uses recursion.
796
797static BuildCompactTrieNode *
798compactOneNode(const TernaryNode *node, UBool parentEndsWord, UStack &nodes, UErrorCode &status) {
799 if (U_FAILURE(status)) {
800 return NULL;
801 }
802 BuildCompactTrieNode *result = NULL;
803 UBool horizontal = (node->low != NULL || node->high != NULL);
804 if (horizontal) {
805 BuildCompactTrieHorizontalNode *hResult =
806 new BuildCompactTrieHorizontalNode(parentEndsWord, nodes, status);
807 if (hResult == NULL) {
808 status = U_MEMORY_ALLOCATION_ERROR;
46f4442e 809 return NULL;
73c04bcf
A
810 }
811 if (U_SUCCESS(status)) {
812 walkHorizontal(node, hResult, nodes, status);
813 result = hResult;
814 }
815 }
816 else {
817 BuildCompactTrieVerticalNode *vResult =
818 new BuildCompactTrieVerticalNode(parentEndsWord, nodes, status);
819 if (vResult == NULL) {
820 status = U_MEMORY_ALLOCATION_ERROR;
821 }
46f4442e 822 else if (U_SUCCESS(status)) {
73c04bcf
A
823 UBool endsWord = FALSE;
824 // Take up nodes until we end a word, or hit a node with < or > links
825 do {
826 vResult->addChar(node->ch);
827 endsWord = (node->flags & kEndsWord) != 0;
828 node = node->equal;
829 }
830 while(node != NULL && !endsWord && node->low == NULL && node->high == NULL);
831 if (node == NULL) {
832 if (!endsWord) {
833 status = U_ILLEGAL_ARGUMENT_ERROR; // Corrupt input trie
834 }
835 else {
836 vResult->setLink((BuildCompactTrieNode *)nodes[1]);
837 }
838 }
839 else {
840 vResult->setLink(compactOneNode(node, endsWord, nodes, status));
841 }
842 result = vResult;
843 }
844 }
845 return result;
846}
847
848// Walk the set of peers at the same level, to build a horizontal node.
849// Uses recursion.
850
851static void walkHorizontal(const TernaryNode *node,
852 BuildCompactTrieHorizontalNode *building,
853 UStack &nodes,
854 UErrorCode &status) {
855 while (U_SUCCESS(status) && node != NULL) {
856 if (node->low != NULL) {
857 walkHorizontal(node->low, building, nodes, status);
858 }
859 BuildCompactTrieNode *link = NULL;
860 if (node->equal != NULL) {
861 link = compactOneNode(node->equal, (node->flags & kEndsWord) != 0, nodes, status);
862 }
863 else if (node->flags & kEndsWord) {
864 link = (BuildCompactTrieNode *)nodes[1];
865 }
866 if (U_SUCCESS(status) && link != NULL) {
867 building->addNode(node->ch, link, status);
868 }
869 // Tail recurse manually instead of leaving it to the compiler.
870 //if (node->high != NULL) {
871 // walkHorizontal(node->high, building, nodes, status);
872 //}
873 node = node->high;
874 }
875}
876
877U_NAMESPACE_END
46f4442e 878U_NAMESPACE_USE
73c04bcf
A
879U_CDECL_BEGIN
880static int32_t U_CALLCONV
881_sortBuildNodes(const void * /*context*/, const void *voidl, const void *voidr) {
882 BuildCompactTrieNode *left = *(BuildCompactTrieNode **)voidl;
883 BuildCompactTrieNode *right = *(BuildCompactTrieNode **)voidr;
884 // Check for comparing a node to itself, to avoid spurious duplicates
885 if (left == right) {
886 return 0;
887 }
888 // Most significant is type of node. Can never coalesce.
889 if (left->fVertical != right->fVertical) {
890 return left->fVertical - right->fVertical;
891 }
892 // Next, the "parent ends word" flag. If that differs, we cannot coalesce.
893 if (left->fParentEndsWord != right->fParentEndsWord) {
894 return left->fParentEndsWord - right->fParentEndsWord;
895 }
896 // Next, the string. If that differs, we can never coalesce.
897 int32_t result = left->fChars.compare(right->fChars);
898 if (result != 0) {
899 return result;
900 }
901 // We know they're both the same node type, so branch for the two cases.
902 if (left->fVertical) {
903 result = ((BuildCompactTrieVerticalNode *)left)->fEqual->fNodeID
904 - ((BuildCompactTrieVerticalNode *)right)->fEqual->fNodeID;
905 }
906 else {
907 // We need to compare the links vectors. They should be the
908 // same size because the strings were equal.
909 // We compare the node IDs instead of the pointers, to handle
910 // coalesced nodes.
911 BuildCompactTrieHorizontalNode *hleft, *hright;
912 hleft = (BuildCompactTrieHorizontalNode *)left;
913 hright = (BuildCompactTrieHorizontalNode *)right;
914 int32_t count = hleft->fLinks.size();
915 for (int32_t i = 0; i < count && result == 0; ++i) {
916 result = ((BuildCompactTrieNode *)(hleft->fLinks[i]))->fNodeID -
917 ((BuildCompactTrieNode *)(hright->fLinks[i]))->fNodeID;
918 }
919 }
920 // If they are equal to each other, mark them (speeds coalescing)
921 if (result == 0) {
922 left->fHasDuplicate = TRUE;
923 right->fHasDuplicate = TRUE;
924 }
925 return result;
926}
927U_CDECL_END
928U_NAMESPACE_BEGIN
929
930static void coalesceDuplicates(UStack &nodes, UErrorCode &status) {
931 // We sort the array of nodes to place duplicates next to each other
932 if (U_FAILURE(status)) {
933 return;
934 }
935 int32_t size = nodes.size();
936 void **array = (void **)uprv_malloc(sizeof(void *)*size);
937 if (array == NULL) {
938 status = U_MEMORY_ALLOCATION_ERROR;
939 return;
940 }
941 (void) nodes.toArray(array);
942
943 // Now repeatedly identify duplicates until there are no more
944 int32_t dupes = 0;
945 long passCount = 0;
946#ifdef DEBUG_TRIE_DICT
947 long totalDupes = 0;
948#endif
949 do {
950 BuildCompactTrieNode *node;
951 BuildCompactTrieNode *first = NULL;
952 BuildCompactTrieNode **p;
953 BuildCompactTrieNode **pFirst = NULL;
954 int32_t counter = size - 2;
955 // Sort the array, skipping nodes 0 and 1. Use quicksort for the first
956 // pass for speed. For the second and subsequent passes, we use stable
957 // (insertion) sort for two reasons:
958 // 1. The array is already mostly ordered, so we get better performance.
959 // 2. The way we find one and only one instance of a set of duplicates is to
960 // check that the node ID equals the array index. If we used an unstable
961 // sort for the second or later passes, it's possible that none of the
962 // duplicates would wind up with a node ID equal to its array index.
963 // The sort stability guarantees that, because as we coalesce more and
964 // more groups, the first element of the resultant group will be one of
965 // the first elements of the groups being coalesced.
966 // To use quicksort for the second and subsequent passes, we would have to
967 // find the minimum of the node numbers in a group, and set all the nodes
968 // in the group to that node number.
969 uprv_sortArray(array+2, counter, sizeof(void *), _sortBuildNodes, NULL, (passCount > 0), &status);
970 dupes = 0;
971 for (p = (BuildCompactTrieNode **)array + 2; counter > 0; --counter, ++p) {
972 node = *p;
973 if (node->fHasDuplicate) {
974 if (first == NULL) {
975 first = node;
976 pFirst = p;
977 }
978 else if (_sortBuildNodes(NULL, pFirst, p) != 0) {
979 // Starting a new run of dupes
980 first = node;
981 pFirst = p;
982 }
983 else if (node->fNodeID != first->fNodeID) {
984 // Slave one to the other, note duplicate
985 node->fNodeID = first->fNodeID;
986 dupes += 1;
987 }
988 }
989 else {
990 // This node has no dupes
991 first = NULL;
992 pFirst = NULL;
993 }
994 }
995 passCount += 1;
996#ifdef DEBUG_TRIE_DICT
997 totalDupes += dupes;
998 fprintf(stderr, "Trie node dupe removal, pass %d: %d nodes tagged\n", passCount, dupes);
999#endif
1000 }
1001 while (dupes > 0);
1002#ifdef DEBUG_TRIE_DICT
1003 fprintf(stderr, "Trie node dupe removal complete: %d tagged in %d passes\n", totalDupes, passCount);
1004#endif
1005
1006 // We no longer need the temporary array, as the nodes have all been marked appropriately.
1007 uprv_free(array);
1008}
1009
1010U_NAMESPACE_END
1011U_CDECL_BEGIN
1012static void U_CALLCONV _deleteBuildNode(void *obj) {
1013 delete (BuildCompactTrieNode *) obj;
1014}
1015U_CDECL_END
1016U_NAMESPACE_BEGIN
1017
1018CompactTrieHeader *
1019CompactTrieDictionary::compactMutableTrieDictionary( const MutableTrieDictionary &dict,
1020 UErrorCode &status ) {
1021 if (U_FAILURE(status)) {
1022 return NULL;
1023 }
1024#ifdef DEBUG_TRIE_DICT
1025 struct tms timing;
1026 struct tms previous;
1027 (void) ::times(&previous);
1028#endif
1029 UStack nodes(_deleteBuildNode, NULL, status); // Index of nodes
1030
1031 // Add node 0, used as the NULL pointer/sentinel.
1032 nodes.addElement((int32_t)0, status);
1033
1034 // Start by creating the special empty node we use to indicate that the parent
1035 // terminates a word. This must be node 1, because the builder assumes
1036 // that.
1037 if (U_FAILURE(status)) {
1038 return NULL;
1039 }
1040 BuildCompactTrieNode *terminal = new BuildCompactTrieNode(TRUE, FALSE, nodes, status);
1041 if (terminal == NULL) {
1042 status = U_MEMORY_ALLOCATION_ERROR;
1043 }
1044
1045 // This call does all the work of building the new trie structure. The root
1046 // will be node 2.
1047 BuildCompactTrieNode *root = compactOneNode(dict.fTrie, FALSE, nodes, status);
1048#ifdef DEBUG_TRIE_DICT
1049 (void) ::times(&timing);
1050 fprintf(stderr, "Compact trie built, %d nodes, time user %f system %f\n",
1051 nodes.size(), (double)(timing.tms_utime-previous.tms_utime)/CLK_TCK,
1052 (double)(timing.tms_stime-previous.tms_stime)/CLK_TCK);
1053 previous = timing;
1054#endif
1055
1056 // Now coalesce all duplicate nodes.
1057 coalesceDuplicates(nodes, status);
1058#ifdef DEBUG_TRIE_DICT
1059 (void) ::times(&timing);
1060 fprintf(stderr, "Duplicates coalesced, time user %f system %f\n",
1061 (double)(timing.tms_utime-previous.tms_utime)/CLK_TCK,
1062 (double)(timing.tms_stime-previous.tms_stime)/CLK_TCK);
1063 previous = timing;
1064#endif
1065
1066 // Next, build the output trie.
1067 // First we compute all the sizes and build the node ID translation table.
1068 uint32_t totalSize = offsetof(CompactTrieHeader,offsets);
1069 int32_t count = nodes.size();
1070 int32_t nodeCount = 1; // The sentinel node we already have
1071 BuildCompactTrieNode *node;
46f4442e 1072 int32_t i;
73c04bcf
A
1073 UVector32 translate(count, status); // Should be no growth needed after this
1074 translate.push(0, status); // The sentinel node
1075
1076 if (U_FAILURE(status)) {
1077 return NULL;
1078 }
1079
46f4442e 1080 for (i = 1; i < count; ++i) {
73c04bcf
A
1081 node = (BuildCompactTrieNode *)nodes[i];
1082 if (node->fNodeID == i) {
1083 // Only one node out of each duplicate set is used
1084 if (i >= translate.size()) {
1085 // Logically extend the mapping table
1086 translate.setSize(i+1);
1087 }
1088 translate.setElementAt(nodeCount++, i);
1089 totalSize += node->size();
1090 }
1091 }
1092
1093 // Check for overflowing 16 bits worth of nodes.
1094 if (nodeCount > 0x10000) {
1095 status = U_ILLEGAL_ARGUMENT_ERROR;
1096 return NULL;
1097 }
1098
1099 // Add enough room for the offsets.
1100 totalSize += nodeCount*sizeof(uint32_t);
1101#ifdef DEBUG_TRIE_DICT
1102 (void) ::times(&timing);
1103 fprintf(stderr, "Sizes/mapping done, time user %f system %f\n",
1104 (double)(timing.tms_utime-previous.tms_utime)/CLK_TCK,
1105 (double)(timing.tms_stime-previous.tms_stime)/CLK_TCK);
1106 previous = timing;
1107 fprintf(stderr, "%d nodes, %d unique, %d bytes\n", nodes.size(), nodeCount, totalSize);
1108#endif
1109 uint8_t *bytes = (uint8_t *)uprv_malloc(totalSize);
1110 if (bytes == NULL) {
1111 status = U_MEMORY_ALLOCATION_ERROR;
1112 return NULL;
1113 }
1114
1115 CompactTrieHeader *header = (CompactTrieHeader *)bytes;
1116 header->size = totalSize;
1117 header->nodeCount = nodeCount;
1118 header->offsets[0] = 0; // Sentinel
1119 header->root = translate.elementAti(root->fNodeID);
1120#ifdef DEBUG_TRIE_DICT
1121 if (header->root == 0) {
1122 fprintf(stderr, "ERROR: root node %d translate to physical zero\n", root->fNodeID);
1123 }
1124#endif
1125 uint32_t offset = offsetof(CompactTrieHeader,offsets)+(nodeCount*sizeof(uint32_t));
1126 nodeCount = 1;
1127 // Now write the data
46f4442e 1128 for (i = 1; i < count; ++i) {
73c04bcf
A
1129 node = (BuildCompactTrieNode *)nodes[i];
1130 if (node->fNodeID == i) {
1131 header->offsets[nodeCount++] = offset;
1132 node->write(bytes, offset, translate);
1133 }
1134 }
1135#ifdef DEBUG_TRIE_DICT
1136 (void) ::times(&timing);
1137 fprintf(stderr, "Trie built, time user %f system %f\n",
1138 (double)(timing.tms_utime-previous.tms_utime)/CLK_TCK,
1139 (double)(timing.tms_stime-previous.tms_stime)/CLK_TCK);
1140 previous = timing;
1141 fprintf(stderr, "Final offset is %d\n", offset);
1142
1143 // Collect statistics on node types and sizes
1144 int hCount = 0;
1145 int vCount = 0;
1146 size_t hSize = 0;
1147 size_t vSize = 0;
1148 size_t hItemCount = 0;
1149 size_t vItemCount = 0;
1150 uint32_t previousOff = offset;
46f4442e
A
1151 for (uint16_t nodeIdx = nodeCount-1; nodeIdx >= 2; --nodeIdx) {
1152 const CompactTrieNode *node = getCompactNode(header, nodeIdx);
73c04bcf
A
1153 if (node->flagscount & kVerticalNode) {
1154 vCount += 1;
1155 vItemCount += (node->flagscount & kCountMask);
46f4442e 1156 vSize += previousOff-header->offsets[nodeIdx];
73c04bcf
A
1157 }
1158 else {
1159 hCount += 1;
1160 hItemCount += (node->flagscount & kCountMask);
46f4442e 1161 hSize += previousOff-header->offsets[nodeIdx];
73c04bcf 1162 }
46f4442e 1163 previousOff = header->offsets[nodeIdx];
73c04bcf
A
1164 }
1165 fprintf(stderr, "Horizontal nodes: %d total, average %f bytes with %f items\n", hCount,
1166 (double)hSize/hCount, (double)hItemCount/hCount);
1167 fprintf(stderr, "Vertical nodes: %d total, average %f bytes with %f items\n", vCount,
1168 (double)vSize/vCount, (double)vItemCount/vCount);
1169#endif
1170
1171 if (U_FAILURE(status)) {
1172 uprv_free(bytes);
1173 header = NULL;
1174 }
1175 else {
1176 header->magic = COMPACT_TRIE_MAGIC_1;
1177 }
1178 return header;
1179}
1180
1181// Forward declaration
1182static TernaryNode *
1183unpackOneNode( const CompactTrieHeader *header, const CompactTrieNode *node, UErrorCode &status );
1184
1185
1186// Convert a horizontal node (or subarray thereof) into a ternary subtrie
1187static TernaryNode *
1188unpackHorizontalArray( const CompactTrieHeader *header, const CompactTrieHorizontalEntry *array,
1189 int low, int high, UErrorCode &status ) {
1190 if (U_FAILURE(status) || low > high) {
1191 return NULL;
1192 }
1193 int middle = (low+high)/2;
1194 TernaryNode *result = new TernaryNode(array[middle].ch);
1195 if (result == NULL) {
1196 status = U_MEMORY_ALLOCATION_ERROR;
1197 return NULL;
1198 }
1199 const CompactTrieNode *equal = getCompactNode(header, array[middle].equal);
1200 if (equal->flagscount & kParentEndsWord) {
1201 result->flags |= kEndsWord;
1202 }
1203 result->low = unpackHorizontalArray(header, array, low, middle-1, status);
1204 result->high = unpackHorizontalArray(header, array, middle+1, high, status);
1205 result->equal = unpackOneNode(header, equal, status);
1206 return result;
1207}
1208
1209// Convert one compact trie node into a ternary subtrie
1210static TernaryNode *
1211unpackOneNode( const CompactTrieHeader *header, const CompactTrieNode *node, UErrorCode &status ) {
1212 int nodeCount = (node->flagscount & kCountMask);
1213 if (nodeCount == 0 || U_FAILURE(status)) {
1214 // Failure, or terminal node
1215 return NULL;
1216 }
1217 if (node->flagscount & kVerticalNode) {
1218 const CompactTrieVerticalNode *vnode = (const CompactTrieVerticalNode *)node;
1219 TernaryNode *head = NULL;
1220 TernaryNode *previous = NULL;
1221 TernaryNode *latest = NULL;
1222 for (int i = 0; i < nodeCount; ++i) {
1223 latest = new TernaryNode(vnode->chars[i]);
1224 if (latest == NULL) {
1225 status = U_MEMORY_ALLOCATION_ERROR;
1226 break;
1227 }
1228 if (head == NULL) {
1229 head = latest;
1230 }
1231 if (previous != NULL) {
1232 previous->equal = latest;
1233 }
1234 previous = latest;
1235 }
1236 if (latest != NULL) {
1237 const CompactTrieNode *equal = getCompactNode(header, vnode->equal);
1238 if (equal->flagscount & kParentEndsWord) {
1239 latest->flags |= kEndsWord;
1240 }
1241 latest->equal = unpackOneNode(header, equal, status);
1242 }
1243 return head;
1244 }
1245 else {
1246 // Horizontal node
1247 const CompactTrieHorizontalNode *hnode = (const CompactTrieHorizontalNode *)node;
1248 return unpackHorizontalArray(header, &hnode->entries[0], 0, nodeCount-1, status);
1249 }
1250}
1251
1252MutableTrieDictionary *
1253CompactTrieDictionary::cloneMutable( UErrorCode &status ) const {
1254 MutableTrieDictionary *result = new MutableTrieDictionary( status );
1255 if (result == NULL) {
1256 status = U_MEMORY_ALLOCATION_ERROR;
1257 return NULL;
1258 }
1259 TernaryNode *root = unpackOneNode(fData, getCompactNode(fData, fData->root), status);
1260 if (U_FAILURE(status)) {
1261 delete root; // Clean up
1262 delete result;
1263 return NULL;
1264 }
1265 result->fTrie = root;
1266 return result;
1267}
1268
1269U_NAMESPACE_END
1270
1271U_CAPI int32_t U_EXPORT2
1272triedict_swap(const UDataSwapper *ds, const void *inData, int32_t length, void *outData,
1273 UErrorCode *status) {
1274
1275 if (status == NULL || U_FAILURE(*status)) {
1276 return 0;
1277 }
1278 if(ds==NULL || inData==NULL || length<-1 || (length>0 && outData==NULL)) {
1279 *status=U_ILLEGAL_ARGUMENT_ERROR;
1280 return 0;
1281 }
1282
1283 //
1284 // Check that the data header is for for dictionary data.
1285 // (Header contents are defined in genxxx.cpp)
1286 //
1287 const UDataInfo *pInfo = (const UDataInfo *)((const uint8_t *)inData+4);
1288 if(!( pInfo->dataFormat[0]==0x54 && /* dataFormat="TrDc" */
1289 pInfo->dataFormat[1]==0x72 &&
1290 pInfo->dataFormat[2]==0x44 &&
1291 pInfo->dataFormat[3]==0x63 &&
1292 pInfo->formatVersion[0]==1 )) {
1293 udata_printError(ds, "triedict_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized\n",
1294 pInfo->dataFormat[0], pInfo->dataFormat[1],
1295 pInfo->dataFormat[2], pInfo->dataFormat[3],
1296 pInfo->formatVersion[0]);
1297 *status=U_UNSUPPORTED_ERROR;
1298 return 0;
1299 }
1300
1301 //
1302 // Swap the data header. (This is the generic ICU Data Header, not the
1303 // CompactTrieHeader). This swap also conveniently gets us
1304 // the size of the ICU d.h., which lets us locate the start
1305 // of the RBBI specific data.
1306 //
1307 int32_t headerSize=udata_swapDataHeader(ds, inData, length, outData, status);
1308
1309 //
1310 // Get the CompactTrieHeader, and check that it appears to be OK.
1311 //
1312 const uint8_t *inBytes =(const uint8_t *)inData+headerSize;
1313 const CompactTrieHeader *header = (const CompactTrieHeader *)inBytes;
1314 if (ds->readUInt32(header->magic) != COMPACT_TRIE_MAGIC_1
1315 || ds->readUInt32(header->size) < sizeof(CompactTrieHeader))
1316 {
1317 udata_printError(ds, "triedict_swap(): CompactTrieHeader is invalid.\n");
1318 *status=U_UNSUPPORTED_ERROR;
1319 return 0;
1320 }
1321
1322 //
1323 // Prefight operation? Just return the size
1324 //
1325 uint32_t totalSize = ds->readUInt32(header->size);
1326 int32_t sizeWithUData = (int32_t)totalSize + headerSize;
1327 if (length < 0) {
1328 return sizeWithUData;
1329 }
1330
1331 //
1332 // Check that length passed in is consistent with length from RBBI data header.
1333 //
1334 if (length < sizeWithUData) {
1335 udata_printError(ds, "triedict_swap(): too few bytes (%d after ICU Data header) for trie data.\n",
1336 totalSize);
1337 *status=U_INDEX_OUTOFBOUNDS_ERROR;
1338 return 0;
1339 }
1340
1341 //
1342 // Swap the Data. Do the data itself first, then the CompactTrieHeader, because
1343 // we need to reference the header to locate the data, and an
1344 // inplace swap of the header leaves it unusable.
1345 //
1346 uint8_t *outBytes = (uint8_t *)outData + headerSize;
1347 CompactTrieHeader *outputHeader = (CompactTrieHeader *)outBytes;
1348
1349#if 0
1350 //
1351 // If not swapping in place, zero out the output buffer before starting.
1352 //
1353 if (inBytes != outBytes) {
1354 uprv_memset(outBytes, 0, totalSize);
1355 }
1356
1357 // We need to loop through all the nodes in the offset table, and swap each one.
1358 uint16_t nodeCount = ds->readUInt16(header->nodeCount);
1359 // Skip node 0, which should always be 0.
1360 for (int i = 1; i < nodeCount; ++i) {
1361 uint32_t nodeOff = ds->readUInt32(header->offsets[i]);
1362 const CompactTrieNode *inNode = (const CompactTrieNode *)(inBytes + nodeOff);
1363 CompactTrieNode *outNode = (CompactTrieNode *)(outBytes + nodeOff);
1364 uint16_t flagscount = ds->readUInt16(inNode->flagscount);
1365 uint16_t itemCount = flagscount & kCountMask;
1366 ds->writeUInt16(&outNode->flagscount, flagscount);
1367 if (itemCount > 0) {
1368 if (flagscount & kVerticalNode) {
1369 ds->swapArray16(ds, inBytes+nodeOff+offsetof(CompactTrieVerticalNode,chars),
1370 itemCount*sizeof(uint16_t),
1371 outBytes+nodeOff+offsetof(CompactTrieVerticalNode,chars), status);
1372 uint16_t equal = ds->readUInt16(inBytes+nodeOff+offsetof(CompactTrieVerticalNode,equal);
1373 ds->writeUInt16(outBytes+nodeOff+offsetof(CompactTrieVerticalNode,equal));
1374 }
1375 else {
1376 const CompactTrieHorizontalNode *inHNode = (const CompactTrieHorizontalNode *)inNode;
1377 CompactTrieHorizontalNode *outHNode = (CompactTrieHorizontalNode *)outNode;
1378 for (int j = 0; j < itemCount; ++j) {
1379 uint16_t word = ds->readUInt16(inHNode->entries[j].ch);
1380 ds->writeUInt16(&outHNode->entries[j].ch, word);
1381 word = ds->readUInt16(inHNode->entries[j].equal);
1382 ds->writeUInt16(&outHNode->entries[j].equal, word);
1383 }
1384 }
1385 }
1386 }
1387#endif
1388
1389 // All the data in all the nodes consist of 16 bit items. Swap them all at once.
1390 uint16_t nodeCount = ds->readUInt16(header->nodeCount);
1391 uint32_t nodesOff = offsetof(CompactTrieHeader,offsets)+((uint32_t)nodeCount*sizeof(uint32_t));
1392 ds->swapArray16(ds, inBytes+nodesOff, totalSize-nodesOff, outBytes+nodesOff, status);
1393
1394 // Swap the header
1395 ds->writeUInt32(&outputHeader->size, totalSize);
1396 uint32_t magic = ds->readUInt32(header->magic);
1397 ds->writeUInt32(&outputHeader->magic, magic);
1398 ds->writeUInt16(&outputHeader->nodeCount, nodeCount);
1399 uint16_t root = ds->readUInt16(header->root);
1400 ds->writeUInt16(&outputHeader->root, root);
1401 ds->swapArray32(ds, inBytes+offsetof(CompactTrieHeader,offsets),
1402 sizeof(uint32_t)*(int32_t)nodeCount,
1403 outBytes+offsetof(CompactTrieHeader,offsets), status);
1404
1405 return sizeWithUData;
1406}
1407
1408#endif /* #if !UCONFIG_NO_BREAK_ITERATION */