• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 ***************************************************************************
3 *   Copyright (C) 1999-2014 International Business Machines Corporation
4 *   and others. All rights reserved.
5 ***************************************************************************
6 */
7 //
8 //  file:  rbbi.c    Contains the implementation of the rule based break iterator
9 //                   runtime engine and the API implementation for
10 //                   class RuleBasedBreakIterator
11 //
12 
13 #include "utypeinfo.h"  // for 'typeid' to work
14 
15 #include "unicode/utypes.h"
16 
17 #if !UCONFIG_NO_BREAK_ITERATION
18 
19 #include "unicode/rbbi.h"
20 #include "unicode/schriter.h"
21 #include "unicode/uchriter.h"
22 #include "unicode/udata.h"
23 #include "unicode/uclean.h"
24 #include "rbbidata.h"
25 #include "rbbirb.h"
26 #include "cmemory.h"
27 #include "cstring.h"
28 #include "umutex.h"
29 #include "ucln_cmn.h"
30 #include "brkeng.h"
31 
32 #include "uassert.h"
33 #include "uvector.h"
34 
35 // if U_LOCAL_SERVICE_HOOK is defined, then localsvc.cpp is expected to be included.
36 #if U_LOCAL_SERVICE_HOOK
37 #include "localsvc.h"
38 #endif
39 
40 #ifdef RBBI_DEBUG
41 static UBool fTrace = FALSE;
42 #endif
43 
44 U_NAMESPACE_BEGIN
45 
46 // The state number of the starting state
47 #define START_STATE 1
48 
49 // The state-transition value indicating "stop"
50 #define STOP_STATE  0
51 
52 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedBreakIterator)53 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedBreakIterator)
54 
55 
56 //=======================================================================
57 // constructors
58 //=======================================================================
59 
60 /**
61  * Constructs a RuleBasedBreakIterator that uses the already-created
62  * tables object that is passed in as a parameter.
63  */
64 RuleBasedBreakIterator::RuleBasedBreakIterator(RBBIDataHeader* data, UErrorCode &status)
65 {
66     init();
67     fData = new RBBIDataWrapper(data, status); // status checked in constructor
68     if (U_FAILURE(status)) {return;}
69     if(fData == 0) {
70         status = U_MEMORY_ALLOCATION_ERROR;
71         return;
72     }
73 }
74 
75 /**
76  * Same as above but does not adopt memory
77  */
RuleBasedBreakIterator(const RBBIDataHeader * data,enum EDontAdopt,UErrorCode & status)78 RuleBasedBreakIterator::RuleBasedBreakIterator(const RBBIDataHeader* data, enum EDontAdopt, UErrorCode &status)
79 {
80     init();
81     fData = new RBBIDataWrapper(data, RBBIDataWrapper::kDontAdopt, status); // status checked in constructor
82     if (U_FAILURE(status)) {return;}
83     if(fData == 0) {
84         status = U_MEMORY_ALLOCATION_ERROR;
85         return;
86     }
87 }
88 
89 
90 //
91 //  Construct from precompiled binary rules (tables).  This constructor is public API,
92 //  taking the rules as a (const uint8_t *) to match the type produced by getBinaryRules().
93 //
RuleBasedBreakIterator(const uint8_t * compiledRules,uint32_t ruleLength,UErrorCode & status)94 RuleBasedBreakIterator::RuleBasedBreakIterator(const uint8_t *compiledRules,
95                        uint32_t       ruleLength,
96                        UErrorCode     &status) {
97     init();
98     if (U_FAILURE(status)) {
99         return;
100     }
101     if (compiledRules == NULL || ruleLength < sizeof(RBBIDataHeader)) {
102         status = U_ILLEGAL_ARGUMENT_ERROR;
103         return;
104     }
105     const RBBIDataHeader *data = (const RBBIDataHeader *)compiledRules;
106     if (data->fLength > ruleLength) {
107         status = U_ILLEGAL_ARGUMENT_ERROR;
108         return;
109     }
110     fData = new RBBIDataWrapper(data, RBBIDataWrapper::kDontAdopt, status);
111     if (U_FAILURE(status)) {return;}
112     if(fData == 0) {
113         status = U_MEMORY_ALLOCATION_ERROR;
114         return;
115     }
116 }
117 
118 
119 //-------------------------------------------------------------------------------
120 //
121 //   Constructor   from a UDataMemory handle to precompiled break rules
122 //                 stored in an ICU data file.
123 //
124 //-------------------------------------------------------------------------------
RuleBasedBreakIterator(UDataMemory * udm,UErrorCode & status)125 RuleBasedBreakIterator::RuleBasedBreakIterator(UDataMemory* udm, UErrorCode &status)
126 {
127     init();
128     fData = new RBBIDataWrapper(udm, status); // status checked in constructor
129     if (U_FAILURE(status)) {return;}
130     if(fData == 0) {
131         status = U_MEMORY_ALLOCATION_ERROR;
132         return;
133     }
134 }
135 
136 
137 
138 //-------------------------------------------------------------------------------
139 //
140 //   Constructor       from a set of rules supplied as a string.
141 //
142 //-------------------------------------------------------------------------------
RuleBasedBreakIterator(const UnicodeString & rules,UParseError & parseError,UErrorCode & status)143 RuleBasedBreakIterator::RuleBasedBreakIterator( const UnicodeString  &rules,
144                                                 UParseError          &parseError,
145                                                 UErrorCode           &status)
146 {
147     init();
148     if (U_FAILURE(status)) {return;}
149     RuleBasedBreakIterator *bi = (RuleBasedBreakIterator *)
150         RBBIRuleBuilder::createRuleBasedBreakIterator(rules, &parseError, status);
151     // Note:  This is a bit awkward.  The RBBI ruleBuilder has a factory method that
152     //        creates and returns a complete RBBI.  From here, in a constructor, we
153     //        can't just return the object created by the builder factory, hence
154     //        the assignment of the factory created object to "this".
155     if (U_SUCCESS(status)) {
156         *this = *bi;
157         delete bi;
158     }
159 }
160 
161 
162 //-------------------------------------------------------------------------------
163 //
164 // Default Constructor.      Create an empty shell that can be set up later.
165 //                           Used when creating a RuleBasedBreakIterator from a set
166 //                           of rules.
167 //-------------------------------------------------------------------------------
RuleBasedBreakIterator()168 RuleBasedBreakIterator::RuleBasedBreakIterator() {
169     init();
170 }
171 
172 
173 //-------------------------------------------------------------------------------
174 //
175 //   Copy constructor.  Will produce a break iterator with the same behavior,
176 //                      and which iterates over the same text, as the one passed in.
177 //
178 //-------------------------------------------------------------------------------
RuleBasedBreakIterator(const RuleBasedBreakIterator & other)179 RuleBasedBreakIterator::RuleBasedBreakIterator(const RuleBasedBreakIterator& other)
180 : BreakIterator(other)
181 {
182     this->init();
183     *this = other;
184 }
185 
186 
187 /**
188  * Destructor
189  */
~RuleBasedBreakIterator()190 RuleBasedBreakIterator::~RuleBasedBreakIterator() {
191     if (fCharIter!=fSCharIter && fCharIter!=fDCharIter) {
192         // fCharIter was adopted from the outside.
193         delete fCharIter;
194     }
195     fCharIter = NULL;
196     delete fSCharIter;
197     fCharIter = NULL;
198     delete fDCharIter;
199     fDCharIter = NULL;
200 
201     utext_close(fText);
202 
203     if (fData != NULL) {
204         fData->removeReference();
205         fData = NULL;
206     }
207     if (fCachedBreakPositions) {
208         uprv_free(fCachedBreakPositions);
209         fCachedBreakPositions = NULL;
210     }
211     if (fLanguageBreakEngines) {
212         delete fLanguageBreakEngines;
213         fLanguageBreakEngines = NULL;
214     }
215     if (fUnhandledBreakEngine) {
216         delete fUnhandledBreakEngine;
217         fUnhandledBreakEngine = NULL;
218     }
219 }
220 
221 /**
222  * Assignment operator.  Sets this iterator to have the same behavior,
223  * and iterate over the same text, as the one passed in.
224  */
225 RuleBasedBreakIterator&
operator =(const RuleBasedBreakIterator & that)226 RuleBasedBreakIterator::operator=(const RuleBasedBreakIterator& that) {
227     if (this == &that) {
228         return *this;
229     }
230     reset();    // Delete break cache information
231     fBreakType = that.fBreakType;
232     if (fLanguageBreakEngines != NULL) {
233         delete fLanguageBreakEngines;
234         fLanguageBreakEngines = NULL;   // Just rebuild for now
235     }
236     // TODO: clone fLanguageBreakEngines from "that"
237     UErrorCode status = U_ZERO_ERROR;
238     fText = utext_clone(fText, that.fText, FALSE, TRUE, &status);
239 
240     if (fCharIter!=fSCharIter && fCharIter!=fDCharIter) {
241         delete fCharIter;
242     }
243     fCharIter = NULL;
244 
245     if (that.fCharIter != NULL ) {
246         // This is a little bit tricky - it will intially appear that
247         //  this->fCharIter is adopted, even if that->fCharIter was
248         //  not adopted.  That's ok.
249         fCharIter = that.fCharIter->clone();
250     }
251 
252     if (fData != NULL) {
253         fData->removeReference();
254         fData = NULL;
255     }
256     if (that.fData != NULL) {
257         fData = that.fData->addReference();
258     }
259 
260     return *this;
261 }
262 
263 
264 
265 //-----------------------------------------------------------------------------
266 //
267 //    init()      Shared initialization routine.   Used by all the constructors.
268 //                Initializes all fields, leaving the object in a consistent state.
269 //
270 //-----------------------------------------------------------------------------
init()271 void RuleBasedBreakIterator::init() {
272     UErrorCode  status    = U_ZERO_ERROR;
273     fText                 = utext_openUChars(NULL, NULL, 0, &status);
274     fCharIter             = NULL;
275     fSCharIter            = NULL;
276     fDCharIter            = NULL;
277     fData                 = NULL;
278     fLastRuleStatusIndex  = 0;
279     fLastStatusIndexValid = TRUE;
280     fDictionaryCharCount  = 0;
281     fBreakType            = UBRK_WORD;  // Defaulting BreakType to word gives reasonable
282                                         //   dictionary behavior for Break Iterators that are
283                                         //   built from rules.  Even better would be the ability to
284                                         //   declare the type in the rules.
285 
286     fCachedBreakPositions    = NULL;
287     fLanguageBreakEngines    = NULL;
288     fUnhandledBreakEngine    = NULL;
289     fNumCachedBreakPositions = 0;
290     fPositionInCache         = 0;
291 
292 #ifdef RBBI_DEBUG
293     static UBool debugInitDone = FALSE;
294     if (debugInitDone == FALSE) {
295         char *debugEnv = getenv("U_RBBIDEBUG");
296         if (debugEnv && uprv_strstr(debugEnv, "trace")) {
297             fTrace = TRUE;
298         }
299         debugInitDone = TRUE;
300     }
301 #endif
302 }
303 
304 
305 
306 //-----------------------------------------------------------------------------
307 //
308 //    clone - Returns a newly-constructed RuleBasedBreakIterator with the same
309 //            behavior, and iterating over the same text, as this one.
310 //            Virtual function: does the right thing with subclasses.
311 //
312 //-----------------------------------------------------------------------------
313 BreakIterator*
clone(void) const314 RuleBasedBreakIterator::clone(void) const {
315     return new RuleBasedBreakIterator(*this);
316 }
317 
318 /**
319  * Equality operator.  Returns TRUE if both BreakIterators are of the
320  * same class, have the same behavior, and iterate over the same text.
321  */
322 UBool
operator ==(const BreakIterator & that) const323 RuleBasedBreakIterator::operator==(const BreakIterator& that) const {
324     if (typeid(*this) != typeid(that)) {
325         return FALSE;
326     }
327 
328     const RuleBasedBreakIterator& that2 = (const RuleBasedBreakIterator&) that;
329 
330     if (!utext_equals(fText, that2.fText)) {
331         // The two break iterators are operating on different text,
332         //   or have a different interation position.
333         return FALSE;
334     };
335 
336     // TODO:  need a check for when in a dictionary region at different offsets.
337 
338     if (that2.fData == fData ||
339         (fData != NULL && that2.fData != NULL && *that2.fData == *fData)) {
340             // The two break iterators are using the same rules.
341             return TRUE;
342         }
343     return FALSE;
344 }
345 
346 /**
347  * Compute a hash code for this BreakIterator
348  * @return A hash code
349  */
350 int32_t
hashCode(void) const351 RuleBasedBreakIterator::hashCode(void) const {
352     int32_t   hash = 0;
353     if (fData != NULL) {
354         hash = fData->hashCode();
355     }
356     return hash;
357 }
358 
359 
setText(UText * ut,UErrorCode & status)360 void RuleBasedBreakIterator::setText(UText *ut, UErrorCode &status) {
361     if (U_FAILURE(status)) {
362         return;
363     }
364     reset();
365     fText = utext_clone(fText, ut, FALSE, TRUE, &status);
366 
367     // Set up a dummy CharacterIterator to be returned if anyone
368     //   calls getText().  With input from UText, there is no reasonable
369     //   way to return a characterIterator over the actual input text.
370     //   Return one over an empty string instead - this is the closest
371     //   we can come to signaling a failure.
372     //   (GetText() is obsolete, this failure is sort of OK)
373     if (fDCharIter == NULL) {
374         static const UChar c = 0;
375         fDCharIter = new UCharCharacterIterator(&c, 0);
376         if (fDCharIter == NULL) {
377             status = U_MEMORY_ALLOCATION_ERROR;
378             return;
379         }
380     }
381 
382     if (fCharIter!=fSCharIter && fCharIter!=fDCharIter) {
383         // existing fCharIter was adopted from the outside.  Delete it now.
384         delete fCharIter;
385     }
386     fCharIter = fDCharIter;
387 
388     this->first();
389 }
390 
391 
getUText(UText * fillIn,UErrorCode & status) const392 UText *RuleBasedBreakIterator::getUText(UText *fillIn, UErrorCode &status) const {
393     UText *result = utext_clone(fillIn, fText, FALSE, TRUE, &status);
394     return result;
395 }
396 
397 
398 
399 /**
400  * Returns the description used to create this iterator
401  */
402 const UnicodeString&
getRules() const403 RuleBasedBreakIterator::getRules() const {
404     if (fData != NULL) {
405         return fData->getRuleSourceString();
406     } else {
407         static const UnicodeString *s;
408         if (s == NULL) {
409             // TODO:  something more elegant here.
410             //        perhaps API should return the string by value.
411             //        Note:  thread unsafe init & leak are semi-ok, better than
412             //               what was before.  Sould be cleaned up, though.
413             s = new UnicodeString;
414         }
415         return *s;
416     }
417 }
418 
419 //=======================================================================
420 // BreakIterator overrides
421 //=======================================================================
422 
423 /**
424  * Return a CharacterIterator over the text being analyzed.
425  */
426 CharacterIterator&
getText() const427 RuleBasedBreakIterator::getText() const {
428     return *fCharIter;
429 }
430 
431 /**
432  * Set the iterator to analyze a new piece of text.  This function resets
433  * the current iteration position to the beginning of the text.
434  * @param newText An iterator over the text to analyze.
435  */
436 void
adoptText(CharacterIterator * newText)437 RuleBasedBreakIterator::adoptText(CharacterIterator* newText) {
438     // If we are holding a CharacterIterator adopted from a
439     //   previous call to this function, delete it now.
440     if (fCharIter!=fSCharIter && fCharIter!=fDCharIter) {
441         delete fCharIter;
442     }
443 
444     fCharIter = newText;
445     UErrorCode status = U_ZERO_ERROR;
446     reset();
447     if (newText==NULL || newText->startIndex() != 0) {
448         // startIndex !=0 wants to be an error, but there's no way to report it.
449         // Make the iterator text be an empty string.
450         fText = utext_openUChars(fText, NULL, 0, &status);
451     } else {
452         fText = utext_openCharacterIterator(fText, newText, &status);
453     }
454     this->first();
455 }
456 
457 /**
458  * Set the iterator to analyze a new piece of text.  This function resets
459  * the current iteration position to the beginning of the text.
460  * @param newText An iterator over the text to analyze.
461  */
462 void
setText(const UnicodeString & newText)463 RuleBasedBreakIterator::setText(const UnicodeString& newText) {
464     UErrorCode status = U_ZERO_ERROR;
465     reset();
466     fText = utext_openConstUnicodeString(fText, &newText, &status);
467 
468     // Set up a character iterator on the string.
469     //   Needed in case someone calls getText().
470     //  Can not, unfortunately, do this lazily on the (probably never)
471     //  call to getText(), because getText is const.
472     if (fSCharIter == NULL) {
473         fSCharIter = new StringCharacterIterator(newText);
474     } else {
475         fSCharIter->setText(newText);
476     }
477 
478     if (fCharIter!=fSCharIter && fCharIter!=fDCharIter) {
479         // old fCharIter was adopted from the outside.  Delete it.
480         delete fCharIter;
481     }
482     fCharIter = fSCharIter;
483 
484     this->first();
485 }
486 
487 
488 /**
489  *  Provide a new UText for the input text.  Must reference text with contents identical
490  *  to the original.
491  *  Intended for use with text data originating in Java (garbage collected) environments
492  *  where the data may be moved in memory at arbitrary times.
493  */
refreshInputText(UText * input,UErrorCode & status)494 RuleBasedBreakIterator &RuleBasedBreakIterator::refreshInputText(UText *input, UErrorCode &status) {
495     if (U_FAILURE(status)) {
496         return *this;
497     }
498     if (input == NULL) {
499         status = U_ILLEGAL_ARGUMENT_ERROR;
500         return *this;
501     }
502     int64_t pos = utext_getNativeIndex(fText);
503     //  Shallow read-only clone of the new UText into the existing input UText
504     fText = utext_clone(fText, input, FALSE, TRUE, &status);
505     if (U_FAILURE(status)) {
506         return *this;
507     }
508     utext_setNativeIndex(fText, pos);
509     if (utext_getNativeIndex(fText) != pos) {
510         // Sanity check.  The new input utext is supposed to have the exact same
511         // contents as the old.  If we can't set to the same position, it doesn't.
512         // The contents underlying the old utext might be invalid at this point,
513         // so it's not safe to check directly.
514         status = U_ILLEGAL_ARGUMENT_ERROR;
515     }
516     return *this;
517 }
518 
519 
520 /**
521  * Sets the current iteration position to the beginning of the text.
522  * @return The offset of the beginning of the text.
523  */
first(void)524 int32_t RuleBasedBreakIterator::first(void) {
525     reset();
526     fLastRuleStatusIndex  = 0;
527     fLastStatusIndexValid = TRUE;
528     //if (fText == NULL)
529     //    return BreakIterator::DONE;
530 
531     utext_setNativeIndex(fText, 0);
532     return 0;
533 }
534 
535 /**
536  * Sets the current iteration position to the end of the text.
537  * @return The text's past-the-end offset.
538  */
last(void)539 int32_t RuleBasedBreakIterator::last(void) {
540     reset();
541     if (fText == NULL) {
542         fLastRuleStatusIndex  = 0;
543         fLastStatusIndexValid = TRUE;
544         return BreakIterator::DONE;
545     }
546 
547     fLastStatusIndexValid = FALSE;
548     int32_t pos = (int32_t)utext_nativeLength(fText);
549     utext_setNativeIndex(fText, pos);
550     return pos;
551 }
552 
553 /**
554  * Advances the iterator either forward or backward the specified number of steps.
555  * Negative values move backward, and positive values move forward.  This is
556  * equivalent to repeatedly calling next() or previous().
557  * @param n The number of steps to move.  The sign indicates the direction
558  * (negative is backwards, and positive is forwards).
559  * @return The character offset of the boundary position n boundaries away from
560  * the current one.
561  */
next(int32_t n)562 int32_t RuleBasedBreakIterator::next(int32_t n) {
563     int32_t result = current();
564     while (n > 0) {
565         result = next();
566         --n;
567     }
568     while (n < 0) {
569         result = previous();
570         ++n;
571     }
572     return result;
573 }
574 
575 /**
576  * Advances the iterator to the next boundary position.
577  * @return The position of the first boundary after this one.
578  */
next(void)579 int32_t RuleBasedBreakIterator::next(void) {
580     // if we have cached break positions and we're still in the range
581     // covered by them, just move one step forward in the cache
582     if (fCachedBreakPositions != NULL) {
583         if (fPositionInCache < fNumCachedBreakPositions - 1) {
584             ++fPositionInCache;
585             int32_t pos = fCachedBreakPositions[fPositionInCache];
586             utext_setNativeIndex(fText, pos);
587             return pos;
588         }
589         else {
590             reset();
591         }
592     }
593 
594     int32_t startPos = current();
595     fDictionaryCharCount = 0;
596     int32_t result = handleNext(fData->fForwardTable);
597     if (fDictionaryCharCount > 0) {
598         result = checkDictionary(startPos, result, FALSE);
599     }
600     return result;
601 }
602 
603 /**
604  * Advances the iterator backwards, to the last boundary preceding this one.
605  * @return The position of the last boundary position preceding this one.
606  */
previous(void)607 int32_t RuleBasedBreakIterator::previous(void) {
608     int32_t result;
609     int32_t startPos;
610 
611     // if we have cached break positions and we're still in the range
612     // covered by them, just move one step backward in the cache
613     if (fCachedBreakPositions != NULL) {
614         if (fPositionInCache > 0) {
615             --fPositionInCache;
616             // If we're at the beginning of the cache, need to reevaluate the
617             // rule status
618             if (fPositionInCache <= 0) {
619                 fLastStatusIndexValid = FALSE;
620             }
621             int32_t pos = fCachedBreakPositions[fPositionInCache];
622             utext_setNativeIndex(fText, pos);
623             return pos;
624         }
625         else {
626             reset();
627         }
628     }
629 
630     // if we're already sitting at the beginning of the text, return DONE
631     if (fText == NULL || (startPos = current()) == 0) {
632         fLastRuleStatusIndex  = 0;
633         fLastStatusIndexValid = TRUE;
634         return BreakIterator::DONE;
635     }
636 
637     if (fData->fSafeRevTable != NULL || fData->fSafeFwdTable != NULL) {
638         result = handlePrevious(fData->fReverseTable);
639         if (fDictionaryCharCount > 0) {
640             result = checkDictionary(result, startPos, TRUE);
641         }
642         return result;
643     }
644 
645     // old rule syntax
646     // set things up.  handlePrevious() will back us up to some valid
647     // break position before the current position (we back our internal
648     // iterator up one step to prevent handlePrevious() from returning
649     // the current position), but not necessarily the last one before
650     // where we started
651 
652     int32_t start = current();
653 
654     (void)UTEXT_PREVIOUS32(fText);
655     int32_t lastResult    = handlePrevious(fData->fReverseTable);
656     if (lastResult == UBRK_DONE) {
657         lastResult = 0;
658         utext_setNativeIndex(fText, 0);
659     }
660     result = lastResult;
661     int32_t lastTag       = 0;
662     UBool   breakTagValid = FALSE;
663 
664     // iterate forward from the known break position until we pass our
665     // starting point.  The last break position before the starting
666     // point is our return value
667 
668     for (;;) {
669         result         = next();
670         if (result == BreakIterator::DONE || result >= start) {
671             break;
672         }
673         lastResult     = result;
674         lastTag        = fLastRuleStatusIndex;
675         breakTagValid  = TRUE;
676     }
677 
678     // fLastBreakTag wants to have the value for section of text preceding
679     // the result position that we are to return (in lastResult.)  If
680     // the backwards rules overshot and the above loop had to do two or more
681     // next()s to move up to the desired return position, we will have a valid
682     // tag value. But, if handlePrevious() took us to exactly the correct result position,
683     // we wont have a tag value for that position, which is only set by handleNext().
684 
685     // Set the current iteration position to be the last break position
686     // before where we started, and then return that value.
687     utext_setNativeIndex(fText, lastResult);
688     fLastRuleStatusIndex  = lastTag;       // for use by getRuleStatus()
689     fLastStatusIndexValid = breakTagValid;
690 
691     // No need to check the dictionary; it will have been handled by
692     // next()
693 
694     return lastResult;
695 }
696 
697 /**
698  * Sets the iterator to refer to the first boundary position following
699  * the specified position.
700  * @offset The position from which to begin searching for a break position.
701  * @return The position of the first break after the current position.
702  */
following(int32_t offset)703 int32_t RuleBasedBreakIterator::following(int32_t offset) {
704     // if we have cached break positions and offset is in the range
705     // covered by them, use them
706     // TODO: could use binary search
707     // TODO: what if offset is outside range, but break is not?
708     if (fCachedBreakPositions != NULL) {
709         if (offset >= fCachedBreakPositions[0]
710                 && offset < fCachedBreakPositions[fNumCachedBreakPositions - 1]) {
711             fPositionInCache = 0;
712             // We are guaranteed not to leave the array due to range test above
713             while (offset >= fCachedBreakPositions[fPositionInCache]) {
714                 ++fPositionInCache;
715             }
716             int32_t pos = fCachedBreakPositions[fPositionInCache];
717             utext_setNativeIndex(fText, pos);
718             return pos;
719         }
720         else {
721             reset();
722         }
723     }
724 
725     // if the offset passed in is already past the end of the text,
726     // just return DONE; if it's before the beginning, return the
727     // text's starting offset
728     fLastRuleStatusIndex  = 0;
729     fLastStatusIndexValid = TRUE;
730     if (fText == NULL || offset >= utext_nativeLength(fText)) {
731         last();
732         return next();
733     }
734     else if (offset < 0) {
735         return first();
736     }
737 
738     // otherwise, set our internal iteration position (temporarily)
739     // to the position passed in.  If this is the _beginning_ position,
740     // then we can just use next() to get our return value
741 
742     int32_t result = 0;
743 
744     if (fData->fSafeRevTable != NULL) {
745         // new rule syntax
746         utext_setNativeIndex(fText, offset);
747         // move forward one codepoint to prepare for moving back to a
748         // safe point.
749         // this handles offset being between a supplementary character
750         (void)UTEXT_NEXT32(fText);
751         // handlePrevious will move most of the time to < 1 boundary away
752         handlePrevious(fData->fSafeRevTable);
753         int32_t result = next();
754         while (result <= offset) {
755             result = next();
756         }
757         return result;
758     }
759     if (fData->fSafeFwdTable != NULL) {
760         // backup plan if forward safe table is not available
761         utext_setNativeIndex(fText, offset);
762         (void)UTEXT_PREVIOUS32(fText);
763         // handle next will give result >= offset
764         handleNext(fData->fSafeFwdTable);
765         // previous will give result 0 or 1 boundary away from offset,
766         // most of the time
767         // we have to
768         int32_t oldresult = previous();
769         while (oldresult > offset) {
770             int32_t result = previous();
771             if (result <= offset) {
772                 return oldresult;
773             }
774             oldresult = result;
775         }
776         int32_t result = next();
777         if (result <= offset) {
778             return next();
779         }
780         return result;
781     }
782     // otherwise, we have to sync up first.  Use handlePrevious() to back
783     // up to a known break position before the specified position (if
784     // we can determine that the specified position is a break position,
785     // we don't back up at all).  This may or may not be the last break
786     // position at or before our starting position.  Advance forward
787     // from here until we've passed the starting position.  The position
788     // we stop on will be the first break position after the specified one.
789     // old rule syntax
790 
791     utext_setNativeIndex(fText, offset);
792     if (offset==0 ||
793         (offset==1  && utext_getNativeIndex(fText)==0)) {
794         return next();
795     }
796     result = previous();
797 
798     while (result != BreakIterator::DONE && result <= offset) {
799         result = next();
800     }
801 
802     return result;
803 }
804 
805 /**
806  * Sets the iterator to refer to the last boundary position before the
807  * specified position.
808  * @offset The position to begin searching for a break from.
809  * @return The position of the last boundary before the starting position.
810  */
preceding(int32_t offset)811 int32_t RuleBasedBreakIterator::preceding(int32_t offset) {
812     // if we have cached break positions and offset is in the range
813     // covered by them, use them
814     if (fCachedBreakPositions != NULL) {
815         // TODO: binary search?
816         // TODO: What if offset is outside range, but break is not?
817         if (offset > fCachedBreakPositions[0]
818                 && offset <= fCachedBreakPositions[fNumCachedBreakPositions - 1]) {
819             fPositionInCache = 0;
820             while (fPositionInCache < fNumCachedBreakPositions
821                    && offset > fCachedBreakPositions[fPositionInCache])
822                 ++fPositionInCache;
823             --fPositionInCache;
824             // If we're at the beginning of the cache, need to reevaluate the
825             // rule status
826             if (fPositionInCache <= 0) {
827                 fLastStatusIndexValid = FALSE;
828             }
829             utext_setNativeIndex(fText, fCachedBreakPositions[fPositionInCache]);
830             return fCachedBreakPositions[fPositionInCache];
831         }
832         else {
833             reset();
834         }
835     }
836 
837     // if the offset passed in is already past the end of the text,
838     // just return DONE; if it's before the beginning, return the
839     // text's starting offset
840     if (fText == NULL || offset > utext_nativeLength(fText)) {
841         // return BreakIterator::DONE;
842         return last();
843     }
844     else if (offset < 0) {
845         return first();
846     }
847 
848     // if we start by updating the current iteration position to the
849     // position specified by the caller, we can just use previous()
850     // to carry out this operation
851 
852     if (fData->fSafeFwdTable != NULL) {
853         // new rule syntax
854         utext_setNativeIndex(fText, offset);
855         int32_t newOffset = (int32_t)UTEXT_GETNATIVEINDEX(fText);
856         if (newOffset != offset) {
857             // Will come here if specified offset was not a code point boundary AND
858             //   the underlying implmentation is using UText, which snaps any non-code-point-boundary
859             //   indices to the containing code point.
860             // For breakitereator::preceding only, these non-code-point indices need to be moved
861             //   up to refer to the following codepoint.
862             (void)UTEXT_NEXT32(fText);
863             offset = (int32_t)UTEXT_GETNATIVEINDEX(fText);
864         }
865 
866         // TODO:  (synwee) would it be better to just check for being in the middle of a surrogate pair,
867         //        rather than adjusting the position unconditionally?
868         //        (Change would interact with safe rules.)
869         // TODO:  change RBBI behavior for off-boundary indices to match that of UText?
870         //        affects only preceding(), seems cleaner, but is slightly different.
871         (void)UTEXT_PREVIOUS32(fText);
872         handleNext(fData->fSafeFwdTable);
873         int32_t result = (int32_t)UTEXT_GETNATIVEINDEX(fText);
874         while (result >= offset) {
875             result = previous();
876         }
877         return result;
878     }
879     if (fData->fSafeRevTable != NULL) {
880         // backup plan if forward safe table is not available
881         //  TODO:  check whether this path can be discarded
882         //         It's probably OK to say that rules must supply both safe tables
883         //            if they use safe tables at all.  We have certainly never described
884         //            to anyone how to work with just one safe table.
885         utext_setNativeIndex(fText, offset);
886         (void)UTEXT_NEXT32(fText);
887 
888         // handle previous will give result <= offset
889         handlePrevious(fData->fSafeRevTable);
890 
891         // next will give result 0 or 1 boundary away from offset,
892         // most of the time
893         // we have to
894         int32_t oldresult = next();
895         while (oldresult < offset) {
896             int32_t result = next();
897             if (result >= offset) {
898                 return oldresult;
899             }
900             oldresult = result;
901         }
902         int32_t result = previous();
903         if (result >= offset) {
904             return previous();
905         }
906         return result;
907     }
908 
909     // old rule syntax
910     utext_setNativeIndex(fText, offset);
911     return previous();
912 }
913 
914 /**
915  * Returns true if the specfied position is a boundary position.  As a side
916  * effect, leaves the iterator pointing to the first boundary position at
917  * or after "offset".
918  * @param offset the offset to check.
919  * @return True if "offset" is a boundary position.
920  */
isBoundary(int32_t offset)921 UBool RuleBasedBreakIterator::isBoundary(int32_t offset) {
922     // the beginning index of the iterator is always a boundary position by definition
923     if (offset == 0) {
924         first();       // For side effects on current position, tag values.
925         return TRUE;
926     }
927 
928     if (offset == (int32_t)utext_nativeLength(fText)) {
929         last();       // For side effects on current position, tag values.
930         return TRUE;
931     }
932 
933     // out-of-range indexes are never boundary positions
934     if (offset < 0) {
935         first();       // For side effects on current position, tag values.
936         return FALSE;
937     }
938 
939     if (offset > utext_nativeLength(fText)) {
940         last();        // For side effects on current position, tag values.
941         return FALSE;
942     }
943 
944     // otherwise, we can use following() on the position before the specified
945     // one and return true if the position we get back is the one the user
946     // specified
947     utext_previous32From(fText, offset);
948     int32_t backOne = (int32_t)UTEXT_GETNATIVEINDEX(fText);
949     UBool    result  = following(backOne) == offset;
950     return result;
951 }
952 
953 /**
954  * Returns the current iteration position.
955  * @return The current iteration position.
956  */
current(void) const957 int32_t RuleBasedBreakIterator::current(void) const {
958     int32_t  pos = (int32_t)UTEXT_GETNATIVEINDEX(fText);
959     return pos;
960 }
961 
962 //=======================================================================
963 // implementation
964 //=======================================================================
965 
966 //
967 // RBBIRunMode  -  the state machine runs an extra iteration at the beginning and end
968 //                 of user text.  A variable with this enum type keeps track of where we
969 //                 are.  The state machine only fetches user input while in the RUN mode.
970 //
971 enum RBBIRunMode {
972     RBBI_START,     // state machine processing is before first char of input
973     RBBI_RUN,       // state machine processing is in the user text
974     RBBI_END        // state machine processing is after end of user text.
975 };
976 
977 
978 //-----------------------------------------------------------------------------------
979 //
980 //  handleNext(stateTable)
981 //     This method is the actual implementation of the rbbi next() method.
982 //     This method initializes the state machine to state 1
983 //     and advances through the text character by character until we reach the end
984 //     of the text or the state machine transitions to state 0.  We update our return
985 //     value every time the state machine passes through an accepting state.
986 //
987 //-----------------------------------------------------------------------------------
handleNext(const RBBIStateTable * statetable)988 int32_t RuleBasedBreakIterator::handleNext(const RBBIStateTable *statetable) {
989     int32_t             state;
990     uint16_t            category        = 0;
991     RBBIRunMode         mode;
992 
993     RBBIStateTableRow  *row;
994     UChar32             c;
995     int32_t             lookaheadStatus = 0;
996     int32_t             lookaheadTagIdx = 0;
997     int32_t             result          = 0;
998     int32_t             initialPosition = 0;
999     int32_t             lookaheadResult = 0;
1000     UBool               lookAheadHardBreak = (statetable->fFlags & RBBI_LOOKAHEAD_HARD_BREAK) != 0;
1001     const char         *tableData       = statetable->fTableData;
1002     uint32_t            tableRowLen     = statetable->fRowLen;
1003 
1004     #ifdef RBBI_DEBUG
1005         if (fTrace) {
1006             RBBIDebugPuts("Handle Next   pos   char  state category");
1007         }
1008     #endif
1009 
1010     // No matter what, handleNext alway correctly sets the break tag value.
1011     fLastStatusIndexValid = TRUE;
1012     fLastRuleStatusIndex = 0;
1013 
1014     // if we're already at the end of the text, return DONE.
1015     initialPosition = (int32_t)UTEXT_GETNATIVEINDEX(fText);
1016     result          = initialPosition;
1017     c               = UTEXT_NEXT32(fText);
1018     if (fData == NULL || c==U_SENTINEL) {
1019         return BreakIterator::DONE;
1020     }
1021 
1022     //  Set the initial state for the state machine
1023     state = START_STATE;
1024     row = (RBBIStateTableRow *)
1025             //(statetable->fTableData + (statetable->fRowLen * state));
1026             (tableData + tableRowLen * state);
1027 
1028 
1029     mode     = RBBI_RUN;
1030     if (statetable->fFlags & RBBI_BOF_REQUIRED) {
1031         category = 2;
1032         mode     = RBBI_START;
1033     }
1034 
1035 
1036     // loop until we reach the end of the text or transition to state 0
1037     //
1038     for (;;) {
1039         if (c == U_SENTINEL) {
1040             // Reached end of input string.
1041             if (mode == RBBI_END) {
1042                 // We have already run the loop one last time with the
1043                 //   character set to the psueudo {eof} value.  Now it is time
1044                 //   to unconditionally bail out.
1045                 if (lookaheadResult > result) {
1046                     // We ran off the end of the string with a pending look-ahead match.
1047                     // Treat this as if the look-ahead condition had been met, and return
1048                     //  the match at the / position from the look-ahead rule.
1049                     result               = lookaheadResult;
1050                     fLastRuleStatusIndex = lookaheadTagIdx;
1051                     lookaheadStatus = 0;
1052                 }
1053                 break;
1054             }
1055             // Run the loop one last time with the fake end-of-input character category.
1056             mode = RBBI_END;
1057             category = 1;
1058         }
1059 
1060         //
1061         // Get the char category.  An incoming category of 1 or 2 means that
1062         //      we are preset for doing the beginning or end of input, and
1063         //      that we shouldn't get a category from an actual text input character.
1064         //
1065         if (mode == RBBI_RUN) {
1066             // look up the current character's character category, which tells us
1067             // which column in the state table to look at.
1068             // Note:  the 16 in UTRIE_GET16 refers to the size of the data being returned,
1069             //        not the size of the character going in, which is a UChar32.
1070             //
1071             UTRIE_GET16(&fData->fTrie, c, category);
1072 
1073             // Check the dictionary bit in the character's category.
1074             //    Counter is only used by dictionary based iterators (subclasses).
1075             //    Chars that need to be handled by a dictionary have a flag bit set
1076             //    in their category values.
1077             //
1078             if ((category & 0x4000) != 0)  {
1079                 fDictionaryCharCount++;
1080                 //  And off the dictionary flag bit.
1081                 category &= ~0x4000;
1082             }
1083         }
1084 
1085        #ifdef RBBI_DEBUG
1086             if (fTrace) {
1087                 RBBIDebugPrintf("             %4ld   ", utext_getNativeIndex(fText));
1088                 if (0x20<=c && c<0x7f) {
1089                     RBBIDebugPrintf("\"%c\"  ", c);
1090                 } else {
1091                     RBBIDebugPrintf("%5x  ", c);
1092                 }
1093                 RBBIDebugPrintf("%3d  %3d\n", state, category);
1094             }
1095         #endif
1096 
1097         // State Transition - move machine to its next state
1098         //
1099 
1100         // Note: fNextState is defined as uint16_t[2], but we are casting
1101         // a generated RBBI table to RBBIStateTableRow and some tables
1102         // actually have more than 2 categories.
1103         U_ASSERT(category<fData->fHeader->fCatCount);
1104         state = row->fNextState[category];  /*Not accessing beyond memory*/
1105         row = (RBBIStateTableRow *)
1106             // (statetable->fTableData + (statetable->fRowLen * state));
1107             (tableData + tableRowLen * state);
1108 
1109 
1110         if (row->fAccepting == -1) {
1111             // Match found, common case.
1112             if (mode != RBBI_START) {
1113                 result = (int32_t)UTEXT_GETNATIVEINDEX(fText);
1114             }
1115             fLastRuleStatusIndex = row->fTagIdx;   // Remember the break status (tag) values.
1116         }
1117 
1118         if (row->fLookAhead != 0) {
1119             if (lookaheadStatus != 0
1120                 && row->fAccepting == lookaheadStatus) {
1121                 // Lookahead match is completed.
1122                 result               = lookaheadResult;
1123                 fLastRuleStatusIndex = lookaheadTagIdx;
1124                 lookaheadStatus      = 0;
1125                 // TODO:  make a standalone hard break in a rule work.
1126                 if (lookAheadHardBreak) {
1127                     UTEXT_SETNATIVEINDEX(fText, result);
1128                     return result;
1129                 }
1130                 // Look-ahead completed, but other rules may match further.  Continue on
1131                 //  TODO:  junk this feature?  I don't think it's used anywhwere.
1132                 goto continueOn;
1133             }
1134 
1135             int32_t  r = (int32_t)UTEXT_GETNATIVEINDEX(fText);
1136             lookaheadResult = r;
1137             lookaheadStatus = row->fLookAhead;
1138             lookaheadTagIdx = row->fTagIdx;
1139             goto continueOn;
1140         }
1141 
1142 
1143         if (row->fAccepting != 0) {
1144             // Because this is an accepting state, any in-progress look-ahead match
1145             //   is no longer relavant.  Clear out the pending lookahead status.
1146             lookaheadStatus = 0;           // clear out any pending look-ahead match.
1147         }
1148 
1149 continueOn:
1150         if (state == STOP_STATE) {
1151             // This is the normal exit from the lookup state machine.
1152             // We have advanced through the string until it is certain that no
1153             //   longer match is possible, no matter what characters follow.
1154             break;
1155         }
1156 
1157         // Advance to the next character.
1158         // If this is a beginning-of-input loop iteration, don't advance
1159         //    the input position.  The next iteration will be processing the
1160         //    first real input character.
1161         if (mode == RBBI_RUN) {
1162             c = UTEXT_NEXT32(fText);
1163         } else {
1164             if (mode == RBBI_START) {
1165                 mode = RBBI_RUN;
1166             }
1167         }
1168 
1169 
1170     }
1171 
1172     // The state machine is done.  Check whether it found a match...
1173 
1174     // If the iterator failed to advance in the match engine, force it ahead by one.
1175     //   (This really indicates a defect in the break rules.  They should always match
1176     //    at least one character.)
1177     if (result == initialPosition) {
1178         UTEXT_SETNATIVEINDEX(fText, initialPosition);
1179         UTEXT_NEXT32(fText);
1180         result = (int32_t)UTEXT_GETNATIVEINDEX(fText);
1181     }
1182 
1183     // Leave the iterator at our result position.
1184     UTEXT_SETNATIVEINDEX(fText, result);
1185     #ifdef RBBI_DEBUG
1186         if (fTrace) {
1187             RBBIDebugPrintf("result = %d\n\n", result);
1188         }
1189     #endif
1190     return result;
1191 }
1192 
1193 
1194 
1195 //-----------------------------------------------------------------------------------
1196 //
1197 //  handlePrevious()
1198 //
1199 //      Iterate backwards, according to the logic of the reverse rules.
1200 //      This version handles the exact style backwards rules.
1201 //
1202 //      The logic of this function is very similar to handleNext(), above.
1203 //
1204 //-----------------------------------------------------------------------------------
handlePrevious(const RBBIStateTable * statetable)1205 int32_t RuleBasedBreakIterator::handlePrevious(const RBBIStateTable *statetable) {
1206     int32_t             state;
1207     uint16_t            category        = 0;
1208     RBBIRunMode         mode;
1209     RBBIStateTableRow  *row;
1210     UChar32             c;
1211     int32_t             lookaheadStatus = 0;
1212     int32_t             result          = 0;
1213     int32_t             initialPosition = 0;
1214     int32_t             lookaheadResult = 0;
1215     UBool               lookAheadHardBreak = (statetable->fFlags & RBBI_LOOKAHEAD_HARD_BREAK) != 0;
1216 
1217     #ifdef RBBI_DEBUG
1218         if (fTrace) {
1219             RBBIDebugPuts("Handle Previous   pos   char  state category");
1220         }
1221     #endif
1222 
1223     // handlePrevious() never gets the rule status.
1224     // Flag the status as invalid; if the user ever asks for status, we will need
1225     // to back up, then re-find the break position using handleNext(), which does
1226     // get the status value.
1227     fLastStatusIndexValid = FALSE;
1228     fLastRuleStatusIndex = 0;
1229 
1230     // if we're already at the start of the text, return DONE.
1231     if (fText == NULL || fData == NULL || UTEXT_GETNATIVEINDEX(fText)==0) {
1232         return BreakIterator::DONE;
1233     }
1234 
1235     //  Set up the starting char.
1236     initialPosition = (int32_t)UTEXT_GETNATIVEINDEX(fText);
1237     result          = initialPosition;
1238     c               = UTEXT_PREVIOUS32(fText);
1239 
1240     //  Set the initial state for the state machine
1241     state = START_STATE;
1242     row = (RBBIStateTableRow *)
1243             (statetable->fTableData + (statetable->fRowLen * state));
1244     category = 3;
1245     mode     = RBBI_RUN;
1246     if (statetable->fFlags & RBBI_BOF_REQUIRED) {
1247         category = 2;
1248         mode     = RBBI_START;
1249     }
1250 
1251 
1252     // loop until we reach the start of the text or transition to state 0
1253     //
1254     for (;;) {
1255         if (c == U_SENTINEL) {
1256             // Reached end of input string.
1257             if (mode == RBBI_END) {
1258                 // We have already run the loop one last time with the
1259                 //   character set to the psueudo {eof} value.  Now it is time
1260                 //   to unconditionally bail out.
1261                 if (lookaheadResult < result) {
1262                     // We ran off the end of the string with a pending look-ahead match.
1263                     // Treat this as if the look-ahead condition had been met, and return
1264                     //  the match at the / position from the look-ahead rule.
1265                     result               = lookaheadResult;
1266                     lookaheadStatus = 0;
1267                 } else if (result == initialPosition) {
1268                     // Ran off start, no match found.
1269                     // move one index one (towards the start, since we are doing a previous())
1270                     UTEXT_SETNATIVEINDEX(fText, initialPosition);
1271                     (void)UTEXT_PREVIOUS32(fText);   // TODO:  shouldn't be necessary.  We're already at beginning.  Check.
1272                 }
1273                 break;
1274             }
1275             // Run the loop one last time with the fake end-of-input character category.
1276             mode = RBBI_END;
1277             category = 1;
1278         }
1279 
1280         //
1281         // Get the char category.  An incoming category of 1 or 2 means that
1282         //      we are preset for doing the beginning or end of input, and
1283         //      that we shouldn't get a category from an actual text input character.
1284         //
1285         if (mode == RBBI_RUN) {
1286             // look up the current character's character category, which tells us
1287             // which column in the state table to look at.
1288             // Note:  the 16 in UTRIE_GET16 refers to the size of the data being returned,
1289             //        not the size of the character going in, which is a UChar32.
1290             //
1291             UTRIE_GET16(&fData->fTrie, c, category);
1292 
1293             // Check the dictionary bit in the character's category.
1294             //    Counter is only used by dictionary based iterators (subclasses).
1295             //    Chars that need to be handled by a dictionary have a flag bit set
1296             //    in their category values.
1297             //
1298             if ((category & 0x4000) != 0)  {
1299                 fDictionaryCharCount++;
1300                 //  And off the dictionary flag bit.
1301                 category &= ~0x4000;
1302             }
1303         }
1304 
1305         #ifdef RBBI_DEBUG
1306             if (fTrace) {
1307                 RBBIDebugPrintf("             %4d   ", (int32_t)utext_getNativeIndex(fText));
1308                 if (0x20<=c && c<0x7f) {
1309                     RBBIDebugPrintf("\"%c\"  ", c);
1310                 } else {
1311                     RBBIDebugPrintf("%5x  ", c);
1312                 }
1313                 RBBIDebugPrintf("%3d  %3d\n", state, category);
1314             }
1315         #endif
1316 
1317         // State Transition - move machine to its next state
1318         //
1319 
1320         // Note: fNextState is defined as uint16_t[2], but we are casting
1321         // a generated RBBI table to RBBIStateTableRow and some tables
1322         // actually have more than 2 categories.
1323         U_ASSERT(category<fData->fHeader->fCatCount);
1324         state = row->fNextState[category];  /*Not accessing beyond memory*/
1325         row = (RBBIStateTableRow *)
1326             (statetable->fTableData + (statetable->fRowLen * state));
1327 
1328         if (row->fAccepting == -1) {
1329             // Match found, common case.
1330             result = (int32_t)UTEXT_GETNATIVEINDEX(fText);
1331         }
1332 
1333         if (row->fLookAhead != 0) {
1334             if (lookaheadStatus != 0
1335                 && row->fAccepting == lookaheadStatus) {
1336                 // Lookahead match is completed.
1337                 result               = lookaheadResult;
1338                 lookaheadStatus      = 0;
1339                 // TODO:  make a standalone hard break in a rule work.
1340                 if (lookAheadHardBreak) {
1341                     UTEXT_SETNATIVEINDEX(fText, result);
1342                     return result;
1343                 }
1344                 // Look-ahead completed, but other rules may match further.  Continue on
1345                 //  TODO:  junk this feature?  I don't think it's used anywhwere.
1346                 goto continueOn;
1347             }
1348 
1349             int32_t  r = (int32_t)UTEXT_GETNATIVEINDEX(fText);
1350             lookaheadResult = r;
1351             lookaheadStatus = row->fLookAhead;
1352             goto continueOn;
1353         }
1354 
1355 
1356         if (row->fAccepting != 0) {
1357             // Because this is an accepting state, any in-progress look-ahead match
1358             //   is no longer relavant.  Clear out the pending lookahead status.
1359             lookaheadStatus = 0;
1360         }
1361 
1362 continueOn:
1363         if (state == STOP_STATE) {
1364             // This is the normal exit from the lookup state machine.
1365             // We have advanced through the string until it is certain that no
1366             //   longer match is possible, no matter what characters follow.
1367             break;
1368         }
1369 
1370         // Move (backwards) to the next character to process.
1371         // If this is a beginning-of-input loop iteration, don't advance
1372         //    the input position.  The next iteration will be processing the
1373         //    first real input character.
1374         if (mode == RBBI_RUN) {
1375             c = UTEXT_PREVIOUS32(fText);
1376         } else {
1377             if (mode == RBBI_START) {
1378                 mode = RBBI_RUN;
1379             }
1380         }
1381     }
1382 
1383     // The state machine is done.  Check whether it found a match...
1384 
1385     // If the iterator failed to advance in the match engine, force it ahead by one.
1386     //   (This really indicates a defect in the break rules.  They should always match
1387     //    at least one character.)
1388     if (result == initialPosition) {
1389         UTEXT_SETNATIVEINDEX(fText, initialPosition);
1390         UTEXT_PREVIOUS32(fText);
1391         result = (int32_t)UTEXT_GETNATIVEINDEX(fText);
1392     }
1393 
1394     // Leave the iterator at our result position.
1395     UTEXT_SETNATIVEINDEX(fText, result);
1396     #ifdef RBBI_DEBUG
1397         if (fTrace) {
1398             RBBIDebugPrintf("result = %d\n\n", result);
1399         }
1400     #endif
1401     return result;
1402 }
1403 
1404 
1405 void
reset()1406 RuleBasedBreakIterator::reset()
1407 {
1408     if (fCachedBreakPositions) {
1409         uprv_free(fCachedBreakPositions);
1410     }
1411     fCachedBreakPositions = NULL;
1412     fNumCachedBreakPositions = 0;
1413     fDictionaryCharCount = 0;
1414     fPositionInCache = 0;
1415 }
1416 
1417 
1418 
1419 //-------------------------------------------------------------------------------
1420 //
1421 //   getRuleStatus()   Return the break rule tag associated with the current
1422 //                     iterator position.  If the iterator arrived at its current
1423 //                     position by iterating forwards, the value will have been
1424 //                     cached by the handleNext() function.
1425 //
1426 //                     If no cached status value is available, the status is
1427 //                     found by doing a previous() followed by a next(), which
1428 //                     leaves the iterator where it started, and computes the
1429 //                     status while doing the next().
1430 //
1431 //-------------------------------------------------------------------------------
makeRuleStatusValid()1432 void RuleBasedBreakIterator::makeRuleStatusValid() {
1433     if (fLastStatusIndexValid == FALSE) {
1434         //  No cached status is available.
1435         if (fText == NULL || current() == 0) {
1436             //  At start of text, or there is no text.  Status is always zero.
1437             fLastRuleStatusIndex = 0;
1438             fLastStatusIndexValid = TRUE;
1439         } else {
1440             //  Not at start of text.  Find status the tedious way.
1441             int32_t pa = current();
1442             previous();
1443             if (fNumCachedBreakPositions > 0) {
1444                 reset();                // Blow off the dictionary cache
1445             }
1446             int32_t pb = next();
1447             if (pa != pb) {
1448                 // note: the if (pa != pb) test is here only to eliminate warnings for
1449                 //       unused local variables on gcc.  Logically, it isn't needed.
1450                 U_ASSERT(pa == pb);
1451             }
1452         }
1453     }
1454     U_ASSERT(fLastRuleStatusIndex >= 0  &&  fLastRuleStatusIndex < fData->fStatusMaxIdx);
1455 }
1456 
1457 
getRuleStatus() const1458 int32_t  RuleBasedBreakIterator::getRuleStatus() const {
1459     RuleBasedBreakIterator *nonConstThis  = (RuleBasedBreakIterator *)this;
1460     nonConstThis->makeRuleStatusValid();
1461 
1462     // fLastRuleStatusIndex indexes to the start of the appropriate status record
1463     //                                                 (the number of status values.)
1464     //   This function returns the last (largest) of the array of status values.
1465     int32_t  idx = fLastRuleStatusIndex + fData->fRuleStatusTable[fLastRuleStatusIndex];
1466     int32_t  tagVal = fData->fRuleStatusTable[idx];
1467 
1468     return tagVal;
1469 }
1470 
1471 
1472 
1473 
getRuleStatusVec(int32_t * fillInVec,int32_t capacity,UErrorCode & status)1474 int32_t RuleBasedBreakIterator::getRuleStatusVec(
1475              int32_t *fillInVec, int32_t capacity, UErrorCode &status)
1476 {
1477     if (U_FAILURE(status)) {
1478         return 0;
1479     }
1480 
1481     RuleBasedBreakIterator *nonConstThis  = (RuleBasedBreakIterator *)this;
1482     nonConstThis->makeRuleStatusValid();
1483     int32_t  numVals = fData->fRuleStatusTable[fLastRuleStatusIndex];
1484     int32_t  numValsToCopy = numVals;
1485     if (numVals > capacity) {
1486         status = U_BUFFER_OVERFLOW_ERROR;
1487         numValsToCopy = capacity;
1488     }
1489     int i;
1490     for (i=0; i<numValsToCopy; i++) {
1491         fillInVec[i] = fData->fRuleStatusTable[fLastRuleStatusIndex + i + 1];
1492     }
1493     return numVals;
1494 }
1495 
1496 
1497 
1498 //-------------------------------------------------------------------------------
1499 //
1500 //   getBinaryRules        Access to the compiled form of the rules,
1501 //                         for use by build system tools that save the data
1502 //                         for standard iterator types.
1503 //
1504 //-------------------------------------------------------------------------------
getBinaryRules(uint32_t & length)1505 const uint8_t  *RuleBasedBreakIterator::getBinaryRules(uint32_t &length) {
1506     const uint8_t  *retPtr = NULL;
1507     length = 0;
1508 
1509     if (fData != NULL) {
1510         retPtr = (const uint8_t *)fData->fHeader;
1511         length = fData->fHeader->fLength;
1512     }
1513     return retPtr;
1514 }
1515 
1516 
createBufferClone(void *,int32_t & bufferSize,UErrorCode & status)1517 BreakIterator *  RuleBasedBreakIterator::createBufferClone(void * /*stackBuffer*/,
1518                                    int32_t &bufferSize,
1519                                    UErrorCode &status)
1520 {
1521     if (U_FAILURE(status)){
1522         return NULL;
1523     }
1524 
1525     if (bufferSize == 0) {
1526         bufferSize = 1;  // preflighting for deprecated functionality
1527         return NULL;
1528     }
1529 
1530     BreakIterator *clonedBI = clone();
1531     if (clonedBI == NULL) {
1532         status = U_MEMORY_ALLOCATION_ERROR;
1533     } else {
1534         status = U_SAFECLONE_ALLOCATED_WARNING;
1535     }
1536     return (RuleBasedBreakIterator *)clonedBI;
1537 }
1538 
1539 
1540 //-------------------------------------------------------------------------------
1541 //
1542 //  isDictionaryChar      Return true if the category lookup for this char
1543 //                        indicates that it is in the set of dictionary lookup
1544 //                        chars.
1545 //
1546 //                        This function is intended for use by dictionary based
1547 //                        break iterators.
1548 //
1549 //-------------------------------------------------------------------------------
1550 /*UBool RuleBasedBreakIterator::isDictionaryChar(UChar32   c) {
1551     if (fData == NULL) {
1552         return FALSE;
1553     }
1554     uint16_t category;
1555     UTRIE_GET16(&fData->fTrie, c, category);
1556     return (category & 0x4000) != 0;
1557 }*/
1558 
1559 
1560 //-------------------------------------------------------------------------------
1561 //
1562 //  checkDictionary       This function handles all processing of characters in
1563 //                        the "dictionary" set. It will determine the appropriate
1564 //                        course of action, and possibly set up a cache in the
1565 //                        process.
1566 //
1567 //-------------------------------------------------------------------------------
checkDictionary(int32_t startPos,int32_t endPos,UBool reverse)1568 int32_t RuleBasedBreakIterator::checkDictionary(int32_t startPos,
1569                             int32_t endPos,
1570                             UBool reverse) {
1571     // Reset the old break cache first.
1572     reset();
1573 
1574     // note: code segment below assumes that dictionary chars are in the
1575     // startPos-endPos range
1576     // value returned should be next character in sequence
1577     if ((endPos - startPos) <= 1) {
1578         return (reverse ? startPos : endPos);
1579     }
1580 
1581     // Bug 5532.  The dictionary code will crash if the input text is UTF-8
1582     //      because native indexes are different from UTF-16 indexes.
1583     //      Temporary hack: skip dictionary lookup for UTF-8 encoded text.
1584     //      It wont give the right breaks, but it's better than a crash.
1585     //
1586     //      Check the type of the UText by checking its pFuncs field, which
1587     //      is UText's function dispatch table.  It will be the same for all
1588     //      UTF-8 UTexts and different for any other UText type.
1589     //
1590     //      We have no other type of UText available with non-UTF-16 native indexing.
1591     //      This whole check will go away once the dictionary code is fixed.
1592     static const void *utext_utf8Funcs;
1593     if (utext_utf8Funcs == NULL) {
1594         // Cache the UTF-8 UText function pointer value.
1595         UErrorCode status = U_ZERO_ERROR;
1596         UText tempUText = UTEXT_INITIALIZER;
1597         utext_openUTF8(&tempUText, NULL, 0, &status);
1598         utext_utf8Funcs = tempUText.pFuncs;
1599         utext_close(&tempUText);
1600     }
1601     if (fText->pFuncs == utext_utf8Funcs) {
1602         return (reverse ? startPos : endPos);
1603     }
1604 
1605     // Starting from the starting point, scan towards the proposed result,
1606     // looking for the first dictionary character (which may be the one
1607     // we're on, if we're starting in the middle of a range).
1608     utext_setNativeIndex(fText, reverse ? endPos : startPos);
1609     if (reverse) {
1610         UTEXT_PREVIOUS32(fText);
1611     }
1612 
1613     int32_t rangeStart = startPos;
1614     int32_t rangeEnd = endPos;
1615 
1616     uint16_t    category;
1617     int32_t     current;
1618     UErrorCode  status = U_ZERO_ERROR;
1619     UStack      breaks(status);
1620     int32_t     foundBreakCount = 0;
1621     UChar32     c = utext_current32(fText);
1622 
1623     UTRIE_GET16(&fData->fTrie, c, category);
1624 
1625     // Is the character we're starting on a dictionary character? If so, we
1626     // need to back up to include the entire run; otherwise the results of
1627     // the break algorithm will differ depending on where we start. Since
1628     // the result is cached and there is typically a non-dictionary break
1629     // within a small number of words, there should be little performance impact.
1630     if (category & 0x4000) {
1631         if (reverse) {
1632             do {
1633                 utext_next32(fText);          // TODO:  recast to work directly with postincrement.
1634                 c = utext_current32(fText);
1635                 UTRIE_GET16(&fData->fTrie, c, category);
1636             } while (c != U_SENTINEL && (category & 0x4000));
1637             // Back up to the last dictionary character
1638             rangeEnd = (int32_t)UTEXT_GETNATIVEINDEX(fText);
1639             if (c == U_SENTINEL) {
1640                 // c = fText->last32();
1641                 //   TODO:  why was this if needed?
1642                 c = UTEXT_PREVIOUS32(fText);
1643             }
1644             else {
1645                 c = UTEXT_PREVIOUS32(fText);
1646             }
1647         }
1648         else {
1649             do {
1650                 c = UTEXT_PREVIOUS32(fText);
1651                 UTRIE_GET16(&fData->fTrie, c, category);
1652             }
1653             while (c != U_SENTINEL && (category & 0x4000));
1654             // Back up to the last dictionary character
1655             if (c == U_SENTINEL) {
1656                 // c = fText->first32();
1657                 c = utext_current32(fText);
1658             }
1659             else {
1660                 utext_next32(fText);
1661                 c = utext_current32(fText);
1662             }
1663             rangeStart = (int32_t)UTEXT_GETNATIVEINDEX(fText);;
1664         }
1665         UTRIE_GET16(&fData->fTrie, c, category);
1666     }
1667 
1668     // Loop through the text, looking for ranges of dictionary characters.
1669     // For each span, find the appropriate break engine, and ask it to find
1670     // any breaks within the span.
1671     // Note: we always do this in the forward direction, so that the break
1672     // cache is built in the right order.
1673     if (reverse) {
1674         utext_setNativeIndex(fText, rangeStart);
1675         c = utext_current32(fText);
1676         UTRIE_GET16(&fData->fTrie, c, category);
1677     }
1678     while(U_SUCCESS(status)) {
1679         while((current = (int32_t)UTEXT_GETNATIVEINDEX(fText)) < rangeEnd && (category & 0x4000) == 0) {
1680             utext_next32(fText);           // TODO:  tweak for post-increment operation
1681             c = utext_current32(fText);
1682             UTRIE_GET16(&fData->fTrie, c, category);
1683         }
1684         if (current >= rangeEnd) {
1685             break;
1686         }
1687 
1688         // We now have a dictionary character. Get the appropriate language object
1689         // to deal with it.
1690         const LanguageBreakEngine *lbe = getLanguageBreakEngine(c);
1691 
1692         // Ask the language object if there are any breaks. It will leave the text
1693         // pointer on the other side of its range, ready to search for the next one.
1694         if (lbe != NULL) {
1695             foundBreakCount += lbe->findBreaks(fText, rangeStart, rangeEnd, FALSE, fBreakType, breaks);
1696         }
1697 
1698         // Reload the loop variables for the next go-round
1699         c = utext_current32(fText);
1700         UTRIE_GET16(&fData->fTrie, c, category);
1701     }
1702 
1703     // If we found breaks, build a new break cache. The first and last entries must
1704     // be the original starting and ending position.
1705     if (foundBreakCount > 0) {
1706         U_ASSERT(foundBreakCount == breaks.size());
1707         int32_t totalBreaks = foundBreakCount;
1708         if (startPos < breaks.elementAti(0)) {
1709             totalBreaks += 1;
1710         }
1711         if (endPos > breaks.peeki()) {
1712             totalBreaks += 1;
1713         }
1714         fCachedBreakPositions = (int32_t *)uprv_malloc(totalBreaks * sizeof(int32_t));
1715         if (fCachedBreakPositions != NULL) {
1716             int32_t out = 0;
1717             fNumCachedBreakPositions = totalBreaks;
1718             if (startPos < breaks.elementAti(0)) {
1719                 fCachedBreakPositions[out++] = startPos;
1720             }
1721             for (int32_t i = 0; i < foundBreakCount; ++i) {
1722                 fCachedBreakPositions[out++] = breaks.elementAti(i);
1723             }
1724             if (endPos > fCachedBreakPositions[out-1]) {
1725                 fCachedBreakPositions[out] = endPos;
1726             }
1727             // If there are breaks, then by definition, we are replacing the original
1728             // proposed break by one of the breaks we found. Use following() and
1729             // preceding() to do the work. They should never recurse in this case.
1730             if (reverse) {
1731                 return preceding(endPos);
1732             }
1733             else {
1734                 return following(startPos);
1735             }
1736         }
1737         // If the allocation failed, just fall through to the "no breaks found" case.
1738     }
1739 
1740     // If we get here, there were no language-based breaks. Set the text pointer
1741     // to the original proposed break.
1742     utext_setNativeIndex(fText, reverse ? startPos : endPos);
1743     return (reverse ? startPos : endPos);
1744 }
1745 
1746 // defined in ucln_cmn.h
1747 
1748 U_NAMESPACE_END
1749 
1750 
1751 static icu::UStack *gLanguageBreakFactories = NULL;
1752 static icu::UInitOnce gLanguageBreakFactoriesInitOnce = U_INITONCE_INITIALIZER;
1753 
1754 /**
1755  * Release all static memory held by breakiterator.
1756  */
1757 U_CDECL_BEGIN
breakiterator_cleanup_dict(void)1758 static UBool U_CALLCONV breakiterator_cleanup_dict(void) {
1759     if (gLanguageBreakFactories) {
1760         delete gLanguageBreakFactories;
1761         gLanguageBreakFactories = NULL;
1762     }
1763     gLanguageBreakFactoriesInitOnce.reset();
1764     return TRUE;
1765 }
1766 U_CDECL_END
1767 
1768 U_CDECL_BEGIN
_deleteFactory(void * obj)1769 static void U_CALLCONV _deleteFactory(void *obj) {
1770     delete (icu::LanguageBreakFactory *) obj;
1771 }
1772 U_CDECL_END
1773 U_NAMESPACE_BEGIN
1774 
initLanguageFactories()1775 static void U_CALLCONV initLanguageFactories() {
1776     UErrorCode status = U_ZERO_ERROR;
1777     U_ASSERT(gLanguageBreakFactories == NULL);
1778     gLanguageBreakFactories = new UStack(_deleteFactory, NULL, status);
1779     if (gLanguageBreakFactories != NULL && U_SUCCESS(status)) {
1780         ICULanguageBreakFactory *builtIn = new ICULanguageBreakFactory(status);
1781         gLanguageBreakFactories->push(builtIn, status);
1782 #ifdef U_LOCAL_SERVICE_HOOK
1783         LanguageBreakFactory *extra = (LanguageBreakFactory *)uprv_svc_hook("languageBreakFactory", &status);
1784         if (extra != NULL) {
1785             gLanguageBreakFactories->push(extra, status);
1786         }
1787 #endif
1788     }
1789     ucln_common_registerCleanup(UCLN_COMMON_BREAKITERATOR_DICT, breakiterator_cleanup_dict);
1790 }
1791 
1792 
1793 static const LanguageBreakEngine*
getLanguageBreakEngineFromFactory(UChar32 c,int32_t breakType)1794 getLanguageBreakEngineFromFactory(UChar32 c, int32_t breakType)
1795 {
1796     umtx_initOnce(gLanguageBreakFactoriesInitOnce, &initLanguageFactories);
1797     if (gLanguageBreakFactories == NULL) {
1798         return NULL;
1799     }
1800 
1801     int32_t i = gLanguageBreakFactories->size();
1802     const LanguageBreakEngine *lbe = NULL;
1803     while (--i >= 0) {
1804         LanguageBreakFactory *factory = (LanguageBreakFactory *)(gLanguageBreakFactories->elementAt(i));
1805         lbe = factory->getEngineFor(c, breakType);
1806         if (lbe != NULL) {
1807             break;
1808         }
1809     }
1810     return lbe;
1811 }
1812 
1813 
1814 //-------------------------------------------------------------------------------
1815 //
1816 //  getLanguageBreakEngine  Find an appropriate LanguageBreakEngine for the
1817 //                          the character c.
1818 //
1819 //-------------------------------------------------------------------------------
1820 const LanguageBreakEngine *
getLanguageBreakEngine(UChar32 c)1821 RuleBasedBreakIterator::getLanguageBreakEngine(UChar32 c) {
1822     const LanguageBreakEngine *lbe = NULL;
1823     UErrorCode status = U_ZERO_ERROR;
1824 
1825     if (fLanguageBreakEngines == NULL) {
1826         fLanguageBreakEngines = new UStack(status);
1827         if (fLanguageBreakEngines == NULL || U_FAILURE(status)) {
1828             delete fLanguageBreakEngines;
1829             fLanguageBreakEngines = 0;
1830             return NULL;
1831         }
1832     }
1833 
1834     int32_t i = fLanguageBreakEngines->size();
1835     while (--i >= 0) {
1836         lbe = (const LanguageBreakEngine *)(fLanguageBreakEngines->elementAt(i));
1837         if (lbe->handles(c, fBreakType)) {
1838             return lbe;
1839         }
1840     }
1841 
1842     // No existing dictionary took the character. See if a factory wants to
1843     // give us a new LanguageBreakEngine for this character.
1844     lbe = getLanguageBreakEngineFromFactory(c, fBreakType);
1845 
1846     // If we got one, use it and push it on our stack.
1847     if (lbe != NULL) {
1848         fLanguageBreakEngines->push((void *)lbe, status);
1849         // Even if we can't remember it, we can keep looking it up, so
1850         // return it even if the push fails.
1851         return lbe;
1852     }
1853 
1854     // No engine is forthcoming for this character. Add it to the
1855     // reject set. Create the reject break engine if needed.
1856     if (fUnhandledBreakEngine == NULL) {
1857         fUnhandledBreakEngine = new UnhandledEngine(status);
1858         if (U_SUCCESS(status) && fUnhandledBreakEngine == NULL) {
1859             status = U_MEMORY_ALLOCATION_ERROR;
1860         }
1861         // Put it last so that scripts for which we have an engine get tried
1862         // first.
1863         fLanguageBreakEngines->insertElementAt(fUnhandledBreakEngine, 0, status);
1864         // If we can't insert it, or creation failed, get rid of it
1865         if (U_FAILURE(status)) {
1866             delete fUnhandledBreakEngine;
1867             fUnhandledBreakEngine = 0;
1868             return NULL;
1869         }
1870     }
1871 
1872     // Tell the reject engine about the character; at its discretion, it may
1873     // add more than just the one character.
1874     fUnhandledBreakEngine->handleCharacter(c, fBreakType);
1875 
1876     return fUnhandledBreakEngine;
1877 }
1878 
1879 
1880 
1881 /*int32_t RuleBasedBreakIterator::getBreakType() const {
1882     return fBreakType;
1883 }*/
1884 
setBreakType(int32_t type)1885 void RuleBasedBreakIterator::setBreakType(int32_t type) {
1886     fBreakType = type;
1887     reset();
1888 }
1889 
1890 U_NAMESPACE_END
1891 
1892 #endif /* #if !UCONFIG_NO_BREAK_ITERATION */
1893