]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/rbbi.cpp
ICU-62108.0.1.tar.gz
[apple/icu.git] / icuSources / common / rbbi.cpp
index 50a9f0d6617b2853c8caebfc1428213ac45331bf..eb83a420efe3f1d0019e65e9194c68a80f79c21a 100644 (file)
@@ -1,35 +1,59 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 ***************************************************************************
-*   Copyright (C) 1999-2004 International Business Machines Corporation   *
-*   and others. All rights reserved.                                      *
+*   Copyright (C) 1999-2016 International Business Machines Corporation
+*   and others. All rights reserved.
 ***************************************************************************
 */
 //
-//  file:  rbbi.c    Contains the implementation of the rule based break iterator
+//  file:  rbbi.cpp  Contains the implementation of the rule based break iterator
 //                   runtime engine and the API implementation for
 //                   class RuleBasedBreakIterator
 //
 
+#include "utypeinfo.h"  // for 'typeid' to work
+
 #include "unicode/utypes.h"
 
 #if !UCONFIG_NO_BREAK_ITERATION
 
 #include "unicode/rbbi.h"
 #include "unicode/schriter.h"
-#include "unicode/udata.h"
+#include "unicode/uchriter.h"
 #include "unicode/uclean.h"
-#include "rbbidata.h"
-#include "rbbirb.h"
+#include "unicode/udata.h"
+
+#include "brkeng.h"
+#include "ucln_cmn.h"
 #include "cmemory.h"
 #include "cstring.h"
-
+#include "rbbidata.h"
+#include "rbbi_cache.h"
+#include "rbbirb.h"
 #include "uassert.h"
+#include "umutex.h"
+#include "uvectr32.h"
+
+// if U_LOCAL_SERVICE_HOOK is defined, then localsvc.cpp is expected to be included.
+#if U_LOCAL_SERVICE_HOOK
+#include "localsvc.h"
+#endif
+
+// Apple specific
+//#include <os/log.h>
+
+#ifdef RBBI_DEBUG
+static UBool gTrace = FALSE;
+#endif
 
 U_NAMESPACE_BEGIN
 
+// The state number of the starting state
+constexpr int32_t START_STATE = 1;
 
-static const int16_t START_STATE = 1;     // The state number of the starting state
-static const int16_t STOP_STATE  = 0;     // The state-transition value indicating "stop"
+// The state-transition value indicating "stop"
+constexpr int32_t STOP_STATE = 0;
 
 
 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedBreakIterator)
@@ -44,8 +68,9 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedBreakIterator)
  * tables object that is passed in as a parameter.
  */
 RuleBasedBreakIterator::RuleBasedBreakIterator(RBBIDataHeader* data, UErrorCode &status)
+ : fSCharIter(UnicodeString())
 {
-    init();
+    init(status);
     fData = new RBBIDataWrapper(data, status); // status checked in constructor
     if (U_FAILURE(status)) {return;}
     if(fData == 0) {
@@ -54,6 +79,37 @@ RuleBasedBreakIterator::RuleBasedBreakIterator(RBBIDataHeader* data, UErrorCode
     }
 }
 
+//
+//  Construct from precompiled binary rules (tables).  This constructor is public API,
+//  taking the rules as a (const uint8_t *) to match the type produced by getBinaryRules().
+//
+RuleBasedBreakIterator::RuleBasedBreakIterator(const uint8_t *compiledRules,
+                       uint32_t       ruleLength,
+                       UErrorCode     &status)
+ : fSCharIter(UnicodeString())
+{
+    init(status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+    if (compiledRules == NULL || ruleLength < sizeof(RBBIDataHeader)) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return;
+    }
+    const RBBIDataHeader *data = (const RBBIDataHeader *)compiledRules;
+    if (data->fLength > ruleLength) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return;
+    }
+    fData = new RBBIDataWrapper(data, RBBIDataWrapper::kDontAdopt, status);
+    if (U_FAILURE(status)) {return;}
+    if(fData == 0) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return;
+    }
+}
+
+
 //-------------------------------------------------------------------------------
 //
 //   Constructor   from a UDataMemory handle to precompiled break rules
@@ -61,8 +117,9 @@ RuleBasedBreakIterator::RuleBasedBreakIterator(RBBIDataHeader* data, UErrorCode
 //
 //-------------------------------------------------------------------------------
 RuleBasedBreakIterator::RuleBasedBreakIterator(UDataMemory* udm, UErrorCode &status)
+ : fSCharIter(UnicodeString())
 {
-    init();
+    init(status);
     fData = new RBBIDataWrapper(udm, status); // status checked in constructor
     if (U_FAILURE(status)) {return;}
     if(fData == 0) {
@@ -81,12 +138,12 @@ RuleBasedBreakIterator::RuleBasedBreakIterator(UDataMemory* udm, UErrorCode &sta
 RuleBasedBreakIterator::RuleBasedBreakIterator( const UnicodeString  &rules,
                                                 UParseError          &parseError,
                                                 UErrorCode           &status)
+ : fSCharIter(UnicodeString())
 {
-    u_init(&status);      // Just in case ICU is not yet initialized
-    init();
+    init(status);
     if (U_FAILURE(status)) {return;}
     RuleBasedBreakIterator *bi = (RuleBasedBreakIterator *)
-        RBBIRuleBuilder::createRuleBasedBreakIterator(rules, parseError, status);
+        RBBIRuleBuilder::createRuleBasedBreakIterator(rules, &parseError, status);
     // Note:  This is a bit awkward.  The RBBI ruleBuilder has a factory method that
     //        creates and returns a complete RBBI.  From here, in a constructor, we
     //        can't just return the object created by the builder factory, hence
@@ -104,8 +161,11 @@ RuleBasedBreakIterator::RuleBasedBreakIterator( const UnicodeString  &rules,
 //                           Used when creating a RuleBasedBreakIterator from a set
 //                           of rules.
 //-------------------------------------------------------------------------------
-RuleBasedBreakIterator::RuleBasedBreakIterator() {
-    init();
+RuleBasedBreakIterator::RuleBasedBreakIterator()
+ : fSCharIter(UnicodeString())
+{
+    UErrorCode status = U_ZERO_ERROR;
+    init(status);
 }
 
 
@@ -116,9 +176,11 @@ RuleBasedBreakIterator::RuleBasedBreakIterator() {
 //
 //-------------------------------------------------------------------------------
 RuleBasedBreakIterator::RuleBasedBreakIterator(const RuleBasedBreakIterator& other)
-: BreakIterator(other)
+: BreakIterator(other),
+  fSCharIter(UnicodeString())
 {
-    this->init();
+    UErrorCode status = U_ZERO_ERROR;
+    this->init(status);
     *this = other;
 }
 
@@ -127,12 +189,32 @@ RuleBasedBreakIterator::RuleBasedBreakIterator(const RuleBasedBreakIterator& oth
  * Destructor
  */
 RuleBasedBreakIterator::~RuleBasedBreakIterator() {
-    delete fText;
-    fText = NULL;
+    if (fCharIter != &fSCharIter) {
+        // fCharIter was adopted from the outside.
+        delete fCharIter;
+    }
+    fCharIter = NULL;
+
+    utext_close(&fText);
+
     if (fData != NULL) {
         fData->removeReference();
         fData = NULL;
     }
+    delete fBreakCache;
+    fBreakCache = NULL;
+
+    delete fDictionaryCache;
+    fDictionaryCache = NULL;
+
+    delete fLanguageBreakEngines;
+    fLanguageBreakEngines = NULL;
+
+    delete fUnhandledBreakEngine;
+    fUnhandledBreakEngine = NULL;
+
+    delete [] fLatin1Cat;
+    fLatin1Cat = NULL;
 }
 
 /**
@@ -144,10 +226,31 @@ RuleBasedBreakIterator::operator=(const RuleBasedBreakIterator& that) {
     if (this == &that) {
         return *this;
     }
-    delete fText;
-    fText = NULL;
-    if (that.fText != NULL) {
-        fText = that.fText->clone();
+    BreakIterator::operator=(that);
+    fLineWordOpts = that.fLineWordOpts;
+
+    if (fLanguageBreakEngines != NULL) {
+        delete fLanguageBreakEngines;
+        fLanguageBreakEngines = NULL;   // Just rebuild for now
+    }
+    // TODO: clone fLanguageBreakEngines from "that"
+    UErrorCode status = U_ZERO_ERROR;
+    utext_clone(&fText, &that.fText, FALSE, TRUE, &status);
+
+    if (fCharIter != &fSCharIter) {
+        delete fCharIter;
+    }
+    fCharIter = &fSCharIter;
+
+    if (that.fCharIter != NULL && that.fCharIter != &that.fSCharIter) {
+        // This is a little bit tricky - it will intially appear that
+        //  this->fCharIter is adopted, even if that->fCharIter was
+        //  not adopted.  That's ok.
+        fCharIter = that.fCharIter->clone();
+    }
+    fSCharIter = that.fSCharIter;
+    if (fCharIter == NULL) {
+        fCharIter = &fSCharIter;
     }
 
     if (fData != NULL) {
@@ -157,7 +260,20 @@ RuleBasedBreakIterator::operator=(const RuleBasedBreakIterator& that) {
     if (that.fData != NULL) {
         fData = that.fData->addReference();
     }
-    fTrace = that.fTrace;
+
+    delete [] fLatin1Cat;
+    fLatin1Cat = NULL;
+
+    fPosition = that.fPosition;
+    fRuleStatusIndex = that.fRuleStatusIndex;
+    fDone = that.fDone;
+
+    // TODO: both the dictionary and the main cache need to be copied.
+    //       Current position could be within a dictionary range. Trying to continue
+    //       the iteration without the caches present would go to the rules, with
+    //       the assumption that the current position is on a rule boundary.
+    fBreakCache->reset(fPosition, fRuleStatusIndex);
+    fDictionaryCache->reset();
 
     return *this;
 }
@@ -170,21 +286,41 @@ RuleBasedBreakIterator::operator=(const RuleBasedBreakIterator& that) {
 //                Initializes all fields, leaving the object in a consistent state.
 //
 //-----------------------------------------------------------------------------
-UBool RuleBasedBreakIterator::fTrace = FALSE;
-void RuleBasedBreakIterator::init() {
-
-    fText                 = NULL;
+void RuleBasedBreakIterator::init(UErrorCode &status) {
+    fCharIter             = NULL;
     fData                 = NULL;
-    fLastRuleStatusIndex  = 0;
-    fLastStatusIndexValid = TRUE;
+    fLatin1Cat            = NULL;
+    fPosition             = 0;
+    fRuleStatusIndex      = 0;
+    fDone                 = false;
     fDictionaryCharCount  = 0;
+    fLanguageBreakEngines = NULL;
+    fUnhandledBreakEngine = NULL;
+    fBreakCache           = NULL;
+    fDictionaryCache      = NULL;
+
+    // Note: IBM xlC is unable to assign or initialize member fText from UTEXT_INITIALIZER.
+    // fText                 = UTEXT_INITIALIZER;
+    static const UText initializedUText = UTEXT_INITIALIZER;
+    uprv_memcpy(&fText, &initializedUText, sizeof(UText));
+
+   if (U_FAILURE(status)) {
+        return;
+    }
+
+    utext_openUChars(&fText, NULL, 0, &status);
+    fDictionaryCache = new DictionaryCache(this, status);
+    fBreakCache      = new BreakCache(this, status);
+    if (U_SUCCESS(status) && (fDictionaryCache == NULL || fBreakCache == NULL)) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+    }
 
 #ifdef RBBI_DEBUG
     static UBool debugInitDone = FALSE;
     if (debugInitDone == FALSE) {
         char *debugEnv = getenv("U_RBBIDEBUG");
         if (debugEnv && uprv_strstr(debugEnv, "trace")) {
-            fTrace = TRUE;
+            gTrace = TRUE;
         }
         debugInitDone = TRUE;
     }
@@ -192,6 +328,12 @@ void RuleBasedBreakIterator::init() {
 }
 
 
+void RuleBasedBreakIterator::initLatin1Cat(void) {
+    fLatin1Cat = new uint16_t[256];
+    for (UChar32 c = 0; c < 256; ++c) {
+        fLatin1Cat[c] = UTRIE2_GET16(fData->fTrie, c);
+    }
+}
 
 //-----------------------------------------------------------------------------
 //
@@ -211,20 +353,41 @@ RuleBasedBreakIterator::clone(void) const {
  */
 UBool
 RuleBasedBreakIterator::operator==(const BreakIterator& that) const {
-    UBool r = FALSE;
-    if (that.getDynamicClassID() != getDynamicClassID()) {
-        return r;
+    if (typeid(*this) != typeid(that)) {
+        return FALSE;
+    }
+    if (this == &that) {
+        return TRUE;
     }
 
+    // The base class BreakIterator carries no state that participates in equality,
+    // and does not implement an equality function that would otherwise be
+    // checked at this point.
+
     const RuleBasedBreakIterator& that2 = (const RuleBasedBreakIterator&) that;
-    if (fText == that2.fText ||
-        (fText != NULL && that2.fText != NULL && *that2.fText == *fText)) {
-        if (that2.fData == fData ||
-            (fData != NULL && that2.fData != NULL && *that2.fData == *fData)) {
-            r = TRUE;
-        }
+    if (that2.fLineWordOpts != fLineWordOpts) {
+        return FALSE;
+    }
+
+    if (!utext_equals(&fText, &that2.fText)) {
+        // The two break iterators are operating on different text,
+        //   or have a different iteration position.
+        //   Note that fText's position is always the same as the break iterator's position.
+        return FALSE;
+    };
+
+    if (!(fPosition == that2.fPosition &&
+            fRuleStatusIndex == that2.fRuleStatusIndex &&
+            fDone == that2.fDone)) {
+        return FALSE;
     }
-    return r;
+
+    if (that2.fData == fData ||
+        (fData != NULL && that2.fData != NULL && *that2.fData == *fData)) {
+            // The two break iterators are using the same rules.
+            return TRUE;
+        }
+    return FALSE;
 }
 
 /**
@@ -240,48 +403,49 @@ RuleBasedBreakIterator::hashCode(void) const {
     return hash;
 }
 
-/**
- * Returns the description used to create this iterator
- */
-const UnicodeString&
-RuleBasedBreakIterator::getRules() const {
-    if (fData != NULL) {
-        return fData->getRuleSourceString();
-    } else {
-        static const UnicodeString *s;
-        if (s == NULL) {
-            // TODO:  something more elegant here.
-            //        perhaps API should return the string by value.
-            //        Note:  thread unsafe init & leak are semi-ok, better than
-            //               what was before.  Sould be cleaned up, though.
-            s = new UnicodeString;
-        }
-        return *s;
+
+void RuleBasedBreakIterator::setText(UText *ut, UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return;
     }
+    fBreakCache->reset();
+    fDictionaryCache->reset();
+    utext_clone(&fText, ut, FALSE, TRUE, &status);
+
+    // Set up a dummy CharacterIterator to be returned if anyone
+    //   calls getText().  With input from UText, there is no reasonable
+    //   way to return a characterIterator over the actual input text.
+    //   Return one over an empty string instead - this is the closest
+    //   we can come to signaling a failure.
+    //   (GetText() is obsolete, this failure is sort of OK)
+    fSCharIter.setText(UnicodeString());
+
+    if (fCharIter != &fSCharIter) {
+        // existing fCharIter was adopted from the outside.  Delete it now.
+        delete fCharIter;
+    }
+    fCharIter = &fSCharIter;
+
+    this->first();
 }
 
+
+UText *RuleBasedBreakIterator::getUText(UText *fillIn, UErrorCode &status) const {
+    UText *result = utext_clone(fillIn, &fText, FALSE, TRUE, &status);
+    return result;
+}
+
+
 //=======================================================================
 // BreakIterator overrides
 //=======================================================================
 
 /**
- * Return a CharacterIterator over the text being analyzed.  This version
- * of this method returns the actual CharacterIterator we're using internally.
- * Changing the state of this iterator can have undefined consequences.  If
- * you need to change it, clone it first.
- * @return An iterator over the text being analyzed.
+ * Return a CharacterIterator over the text being analyzed.
  */
-const CharacterIterator&
+CharacterIterator&
 RuleBasedBreakIterator::getText() const {
-    RuleBasedBreakIterator* nonConstThis = (RuleBasedBreakIterator*)this;
-
-    // The iterator is initialized pointing to no text at all, so if this
-    // function is called while we're in that state, we have to fudge an
-    // an iterator to return.
-    if (nonConstThis->fText == NULL) {
-        nonConstThis->fText = new StringCharacterIterator(UnicodeString());
-    }
-    return *nonConstThis->fText;
+    return *fCharIter;
 }
 
 /**
@@ -291,9 +455,23 @@ RuleBasedBreakIterator::getText() const {
  */
 void
 RuleBasedBreakIterator::adoptText(CharacterIterator* newText) {
-    reset();
-    delete fText;
-    fText = newText;
+    // If we are holding a CharacterIterator adopted from a
+    //   previous call to this function, delete it now.
+    if (fCharIter != &fSCharIter) {
+        delete fCharIter;
+    }
+
+    fCharIter = newText;
+    UErrorCode status = U_ZERO_ERROR;
+    fBreakCache->reset();
+    fDictionaryCache->reset();
+    if (newText==NULL || newText->startIndex() != 0) {
+        // startIndex !=0 wants to be an error, but there's no way to report it.
+        // Make the iterator text be an empty string.
+        utext_openUChars(&fText, NULL, 0, &status);
+    } else {
+        utext_openCharacterIterator(&fText, newText, &status);
+    }
     this->first();
 }
 
@@ -304,61 +482,84 @@ RuleBasedBreakIterator::adoptText(CharacterIterator* newText) {
  */
 void
 RuleBasedBreakIterator::setText(const UnicodeString& newText) {
-    reset();
-    if (fText != NULL && fText->getDynamicClassID()
-            == StringCharacterIterator::getStaticClassID()) {
-        ((StringCharacterIterator*)fText)->setText(newText);
-    }
-    else {
-        delete fText;
-        fText = new StringCharacterIterator(newText);
+    UErrorCode status = U_ZERO_ERROR;
+    fBreakCache->reset();
+    fDictionaryCache->reset();
+    utext_openConstUnicodeString(&fText, &newText, &status);
+
+    // Set up a character iterator on the string.
+    //   Needed in case someone calls getText().
+    //  Can not, unfortunately, do this lazily on the (probably never)
+    //  call to getText(), because getText is const.
+    fSCharIter.setText(newText);
+
+    if (fCharIter != &fSCharIter) {
+        // old fCharIter was adopted from the outside.  Delete it.
+        delete fCharIter;
     }
+    fCharIter = &fSCharIter;
+
     this->first();
 }
 
 
+/**
+ *  Provide a new UText for the input text.  Must reference text with contents identical
+ *  to the original.
+ *  Intended for use with text data originating in Java (garbage collected) environments
+ *  where the data may be moved in memory at arbitrary times.
+ */
+RuleBasedBreakIterator &RuleBasedBreakIterator::refreshInputText(UText *input, UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return *this;
+    }
+    if (input == NULL) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return *this;
+    }
+    int64_t pos = utext_getNativeIndex(&fText);
+    //  Shallow read-only clone of the new UText into the existing input UText
+    utext_clone(&fText, input, FALSE, TRUE, &status);
+    if (U_FAILURE(status)) {
+        return *this;
+    }
+    utext_setNativeIndex(&fText, pos);
+    if (utext_getNativeIndex(&fText) != pos) {
+        // Sanity check.  The new input utext is supposed to have the exact same
+        // contents as the old.  If we can't set to the same position, it doesn't.
+        // The contents underlying the old utext might be invalid at this point,
+        // so it's not safe to check directly.
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+    }
+    return *this;
+}
+
 
 /**
- * Sets the current iteration position to the beginning of the text.
- * (i.e., the CharacterIterator's starting offset).
- * @return The offset of the beginning of the text.
+ * Sets the current iteration position to the beginning of the text, position zero.
+ * @return The new iterator position, which is zero.
  */
 int32_t RuleBasedBreakIterator::first(void) {
-    reset();
-    fLastRuleStatusIndex  = 0;
-    fLastStatusIndexValid = TRUE;
-    if (fText == NULL)
-        return BreakIterator::DONE;
-
-    fText->first();
-    return fText->getIndex();
+    UErrorCode status = U_ZERO_ERROR;
+    if (!fBreakCache->seek(0)) {
+        fBreakCache->populateNear(0, status);
+    }
+    fBreakCache->current();
+    U_ASSERT(fPosition == 0);
+    return 0;
 }
 
 /**
  * Sets the current iteration position to the end of the text.
- * (i.e., the CharacterIterator's ending offset).
  * @return The text's past-the-end offset.
  */
 int32_t RuleBasedBreakIterator::last(void) {
-    reset();
-    if (fText == NULL) {
-        fLastRuleStatusIndex  = 0;
-        fLastStatusIndexValid = TRUE;
-        return BreakIterator::DONE;
-    }
-
-    // I'm not sure why, but t.last() returns the offset of the last character,
-    // rather than the past-the-end offset
-    //
-    //   (It's so a loop like for(p=it.last(); p!=DONE; p=it.previous()) ...
-    //     will work correctly.)
-
-
-    fLastStatusIndexValid = FALSE;
-    int32_t pos = fText->endIndex();
-    fText->setIndex(pos);
-
-    return pos;
+    int32_t endPos = (int32_t)utext_nativeLength(&fText);
+    UBool endShouldBeBoundary = isBoundary(endPos);      // Has side effect of setting iterator position.
+    (void)endShouldBeBoundary;
+    U_ASSERT(endShouldBeBoundary);
+    U_ASSERT(fPosition == endPos);
+    return endPos;
 }
 
 /**
@@ -371,14 +572,17 @@ int32_t RuleBasedBreakIterator::last(void) {
  * the current one.
  */
 int32_t RuleBasedBreakIterator::next(int32_t n) {
-    int32_t result = current();
-    while (n > 0) {
-        result = handleNext();
-        --n;
-    }
-    while (n < 0) {
-        result = previous();
-        ++n;
+    int32_t result = 0;
+    if (n > 0) {
+        for (; n > 0 && result != UBRK_DONE; --n) {
+            result = next();
+        }
+    } else if (n < 0) {
+        for (; n < 0 && result != UBRK_DONE; ++n) {
+            result = previous();
+        }
+    } else {
+        result = current();
     }
     return result;
 }
@@ -388,374 +592,314 @@ int32_t RuleBasedBreakIterator::next(int32_t n) {
  * @return The position of the first boundary after this one.
  */
 int32_t RuleBasedBreakIterator::next(void) {
-    return handleNext();
+    fBreakCache->next();
+    return fDone ? UBRK_DONE : fPosition;
 }
 
 /**
- * Advances the iterator backwards, to the last boundary preceding this one.
- * @return The position of the last boundary position preceding this one.
+ * Move the iterator backwards, to the boundary preceding the current one.
+ *
+ *         Starts from the current position within fText.
+ *         Starting position need not be on a boundary.
+ *
+ * @return The position of the boundary position immediately preceding the starting position.
  */
 int32_t RuleBasedBreakIterator::previous(void) {
-    // if we're already sitting at the beginning of the text, return DONE
-    if (fText == NULL || current() == fText->startIndex()) {
-        fLastRuleStatusIndex  = 0;
-        fLastStatusIndexValid = TRUE;
-        return BreakIterator::DONE;
-    }
-
-    if (fData->fSafeRevTable != NULL || fData->fSafeFwdTable != NULL) {
-        return handlePrevious(fData->fReverseTable);
-    }
-
-    // old rule syntax
-    // set things up.  handlePrevious() will back us up to some valid
-    // break position before the current position (we back our internal
-    // iterator up one step to prevent handlePrevious() from returning
-    // the current position), but not necessarily the last one before
-    // where we started
-
-    int32_t start = current();
-
-    fText->previous32();
-    int32_t lastResult    = handlePrevious();
-    int32_t result        = lastResult;
-    int32_t lastTag       = 0;
-    UBool   breakTagValid = FALSE;
-
-    // iterate forward from the known break position until we pass our
-    // starting point.  The last break position before the starting
-    // point is our return value
-
-    for (;;) {
-        result         = handleNext();
-        if (result == BreakIterator::DONE || result >= start) {
-            break;
-        }
-        lastResult     = result;
-        lastTag        = fLastRuleStatusIndex;
-        breakTagValid  = TRUE;
-    }
-
-    // fLastBreakTag wants to have the value for section of text preceding
-    // the result position that we are to return (in lastResult.)  If
-    // the backwards rules overshot and the above loop had to do two or more
-    // handleNext()s to move up to the desired return position, we will have a valid
-    // tag value. But, if handlePrevious() took us to exactly the correct result positon,
-    // we wont have a tag value for that position, which is only set by handleNext().
-
-    // set the current iteration position to be the last break position
-    // before where we started, and then return that value
-    fText->setIndex(lastResult);
-    fLastRuleStatusIndex  = lastTag;       // for use by getRuleStatus()
-    fLastStatusIndexValid = breakTagValid;
-    return lastResult;
+    UErrorCode status = U_ZERO_ERROR;
+    fBreakCache->previous(status);
+    return fDone ? UBRK_DONE : fPosition;
 }
 
 /**
  * Sets the iterator to refer to the first boundary position following
  * the specified position.
- * @offset The position from which to begin searching for a break position.
+ * @param startPos The position from which to begin searching for a break position.
  * @return The position of the first break after the current position.
  */
-int32_t RuleBasedBreakIterator::following(int32_t offset) {
-    // if the offset passed in is already past the end of the text,
-    // just return DONE; if it's before the beginning, return the
+int32_t RuleBasedBreakIterator::following(int32_t startPos) {
+    // if the supplied position is before the beginning, return the
     // text's starting offset
-    fLastRuleStatusIndex  = 0;
-    fLastStatusIndexValid = TRUE;
-    if (fText == NULL || offset >= fText->endIndex()) {
-        last();
-        return next();
-    }
-    else if (offset < fText->startIndex()) {
+    if (startPos < 0) {
         return first();
     }
 
-    // otherwise, set our internal iteration position (temporarily)
-    // to the position passed in.  If this is the _beginning_ position,
-    // then we can just use next() to get our return value
-
-    int32_t result = 0;
-
-    if (fData->fSafeRevTable != NULL) {
-        // new rule syntax
-        /// todo synwee
-        fText->setIndex(offset);
-        // move forward one codepoint to prepare for moving back to a
-        // safe point.
-        // this handles offset being between a supplementary character
-        fText->next32();
-        // handlePrevious will move most of the time to < 1 boundary away
-        handlePrevious(fData->fSafeRevTable);
-        int32_t result = next();
-        while (result <= offset) {
-            result = next();
-        }
-        return result;
-    }
-    if (fData->fSafeFwdTable != NULL) {
-        // backup plan if forward safe table is not available
-        fText->setIndex(offset);
-        fText->previous32();
-        // handle next will give result >= offset
-        handleNext(fData->fSafeFwdTable);
-        // previous will give result 0 or 1 boundary away from offset,
-        // most of the time
-        // we have to
-        int32_t oldresult = previous();
-        while (oldresult > offset) {
-            int32_t result = previous();
-            if (result <= offset) {
-                return oldresult;
-            }
-            oldresult = result;
-        }
-        int32_t result = next();
-        if (result <= offset) {
-            return next();
-        }
-        return result;
-    }
-    // otherwise, we have to sync up first.  Use handlePrevious() to back
-    // us up to a known break position before the specified position (if
-    // we can determine that the specified position is a break position,
-    // we don't back up at all).  This may or may not be the last break
-    // position at or before our starting position.  Advance forward
-    // from here until we've passed the starting position.  The position
-    // we stop on will be the first break position after the specified one.
-    // old rule syntax
-
-    fText->setIndex(offset);
-    if (offset == fText->startIndex()) {
-        return handleNext();
-    }
-    result = previous();
-
-    while (result != BreakIterator::DONE && result <= offset) {
-        result = next();
-    }
+    // Move requested offset to a code point start. It might be on a trail surrogate,
+    // or on a trail byte if the input is UTF-8. Or it may be beyond the end of the text.
+    utext_setNativeIndex(&fText, startPos);
+    startPos = (int32_t)utext_getNativeIndex(&fText);
 
-    return result;
+    UErrorCode status = U_ZERO_ERROR;
+    fBreakCache->following(startPos, status);
+    return fDone ? UBRK_DONE : fPosition;
 }
 
 /**
  * Sets the iterator to refer to the last boundary position before the
  * specified position.
- * @offset The position to begin searching for a break from.
+ * @param offset The position to begin searching for a break from.
  * @return The position of the last boundary before the starting position.
  */
 int32_t RuleBasedBreakIterator::preceding(int32_t offset) {
-    // if the offset passed in is already past the end of the text,
-    // just return DONE; if it's before the beginning, return the
-
-    // text's starting offset
-    if (fText == NULL || offset > fText->endIndex()) {
-        // return BreakIterator::DONE;
+    if (offset > utext_nativeLength(&fText)) {
         return last();
     }
-    else if (offset < fText->startIndex()) {
-        return first();
-    }
 
-    // if we start by updating the current iteration position to the
-    // position specified by the caller, we can just use previous()
-    // to carry out this operation
-
-    if (fData->fSafeFwdTable != NULL) {
-        /// todo synwee
-        // new rule syntax
-        fText->setIndex(offset);
-        // move backwards one codepoint to prepare for moving forwards to a
-        // safe point.
-        // this handles offset being between a supplementary character
-        // TODO:  would it be better to just check for being in the middle of a surrogate pair,
-        //        rather than adjusting the position unconditionally?
-        //        (Change would interact with safe rules.)
-        fText->previous32();
-        handleNext(fData->fSafeFwdTable);
-        int32_t result = fText->getIndex();
-        while (result >= offset) {
-            result = previous();
-        }
-        return result;
-    }
-    if (fData->fSafeRevTable != NULL) {
-        // backup plan if forward safe table is not available
-        fText->setIndex(offset);
-        fText->next32();
-        // handle previous will give result <= offset
-        handlePrevious(fData->fSafeRevTable);
-
-        // next will give result 0 or 1 boundary away from offset,
-        // most of the time
-        // we have to
-        int32_t oldresult = next();
-        while (oldresult < offset) {
-            int32_t result = next();
-            if (result >= offset) {
-                return oldresult;
-            }
-            oldresult = result;
-        }
-        int32_t result = previous();
-        if (result >= offset) {
-            return previous();
-        }
-        return result;
-    }
+    // Move requested offset to a code point start. It might be on a trail surrogate,
+    // or on a trail byte if the input is UTF-8.
 
-    // old rule syntax
-    fText->setIndex(offset);
-    return previous();
+    utext_setNativeIndex(&fText, offset);
+    int32_t adjustedOffset = utext_getNativeIndex(&fText);
+
+    UErrorCode status = U_ZERO_ERROR;
+    fBreakCache->preceding(adjustedOffset, status);
+    return fDone ? UBRK_DONE : fPosition;
 }
 
 /**
  * Returns true if the specfied position is a boundary position.  As a side
  * effect, leaves the iterator pointing to the first boundary position at
  * or after "offset".
+ *
  * @param offset the offset to check.
  * @return True if "offset" is a boundary position.
  */
 UBool RuleBasedBreakIterator::isBoundary(int32_t offset) {
-    // the beginning index of the iterator is always a boundary position by definition
-    if (fText == NULL || offset == fText->startIndex()) {
+    // out-of-range indexes are never boundary positions
+    if (offset < 0) {
         first();       // For side effects on current position, tag values.
-        return TRUE;
+        return FALSE;
     }
 
-    if (offset == fText->endIndex()) {
-        last();       // For side effects on current position, tag values.
-        return TRUE;
-    }
+    // Adjust offset to be on a code point boundary and not beyond the end of the text.
+    // Note that isBoundary() is always false for offsets that are not on code point boundaries.
+    // But we still need the side effect of leaving iteration at the following boundary.
 
-    // out-of-range indexes are never boundary positions
-    if (offset < fText->startIndex()) {
-        first();       // For side effects on current position, tag values.
-        return FALSE;
+    utext_setNativeIndex(&fText, offset);
+    int32_t adjustedOffset = utext_getNativeIndex(&fText);
+
+    bool result = false;
+    UErrorCode status = U_ZERO_ERROR;
+    if (fBreakCache->seek(adjustedOffset) || fBreakCache->populateNear(adjustedOffset, status)) {
+        result = (fBreakCache->current() == offset);
     }
 
-    if (offset > fText->endIndex()) {
-        last();        // For side effects on current position, tag values.
+    if (result && adjustedOffset < offset && utext_char32At(&fText, offset) == U_SENTINEL) {
+        // Original offset is beyond the end of the text. Return FALSE, it's not a boundary,
+        // but the iteration position remains set to the end of the text, which is a boundary.
         return FALSE;
     }
-
-    // otherwise, we can use following() on the position before the specified
-    // one and return true if the position we get back is the one the user
-    // specified
-    return following(offset - 1) == offset;
+    if (!result) {
+        // Not on a boundary. isBoundary() must leave iterator on the following boundary.
+        // Cache->seek(), above, left us on the preceding boundary, so advance one.
+        next();
+    }
+    return result;
 }
 
+
 /**
  * Returns the current iteration position.
  * @return The current iteration position.
  */
 int32_t RuleBasedBreakIterator::current(void) const {
-    return (fText != NULL) ? fText->getIndex() : BreakIterator::DONE;
+    return fPosition;
 }
 
+
 //=======================================================================
 // implementation
 //=======================================================================
 
+//
+// RBBIRunMode  -  the state machine runs an extra iteration at the beginning and end
+//                 of user text.  A variable with this enum type keeps track of where we
+//                 are.  The state machine only fetches user input while in the RUN mode.
+//
+enum RBBIRunMode {
+    RBBI_START,     // state machine processing is before first char of input
+    RBBI_RUN,       // state machine processing is in the user text
+    RBBI_END        // state machine processing is after end of user text.
+};
+
+
+// Map from look-ahead break states (corresponds to rules) to boundary positions.
+// Allows multiple lookahead break rules to be in flight at the same time.
+//
+// This is a temporary approach for ICU 57. A better fix is to make the look-ahead numbers
+// in the state table be sequential, then we can just index an array. And the
+// table could also tell us in advance how big that array needs to be.
+//
+// Before ICU 57 there was just a single simple variable for a look-ahead match that
+// was in progress. Two rules at once did not work.
+
+static const int32_t kMaxLookaheads = 8;
+struct LookAheadResults {
+    int32_t    fUsedSlotLimit;
+    int32_t    fPositions[8];
+    int16_t    fKeys[8];
+
+    LookAheadResults() : fUsedSlotLimit(0), fPositions(), fKeys() {};
+
+    int32_t getPosition(int16_t key) {
+        for (int32_t i=0; i<fUsedSlotLimit; ++i) {
+            if (fKeys[i] == key) {
+                return fPositions[i];
+            }
+        }
+        // with NLLT source rules, Latn sample and ubrk_next, we see a request for key 79 here
+        // near the end of text, when setPosition has only ever set positions for key 80 or 82.
+        //U_ASSERT(FALSE);
+        return -1;
+    }
+
+    void setPosition(int16_t key, int32_t position) {
+        int32_t i;
+        for (i=0; i<fUsedSlotLimit; ++i) {
+            if (fKeys[i] == key) {
+                fPositions[i] = position;
+                return;
+            }
+        }
+        if (i >= kMaxLookaheads) {
+            U_ASSERT(FALSE);
+            i = kMaxLookaheads - 1;
+        }
+        fKeys[i] = key;
+        fPositions[i] = position;
+        U_ASSERT(fUsedSlotLimit == i);
+        fUsedSlotLimit = i + 1;
+    }
+};
+
 
 //-----------------------------------------------------------------------------------
 //
 //  handleNext()
-//     This method is the actual implementation of the next() method.  All iteration
-//     vectors through here.  This method initializes the state machine to state 1
-//     and advances through the text character by character until we reach the end
-//     of the text or the state machine transitions to state 0.  We update our return
-//     value every time the state machine passes through an accepting state.
+//     Run the state machine to find a boundary
 //
 //-----------------------------------------------------------------------------------
+// Route handleNext calls through the following to handleNextInternal,
+// in order to handle fLineWordOpts.
 int32_t RuleBasedBreakIterator::handleNext() {
-    return handleNext(fData->fForwardTable);
+    int32_t result = handleNextInternal();
+    while (fLineWordOpts != UBRK_LINEWORD_NORMAL) {
+        UChar32 prevChr = utext_char32At(&fText, result-1);
+        UChar32 currChr = utext_char32At(&fText, result);
+        if (currChr == U_SENTINEL || prevChr == U_SENTINEL) {
+            break;
+        }
+        if (fLineWordOpts == UBRK_LINEWORD_KEEP_HANGUL) {
+            UErrorCode status = U_ZERO_ERROR;
+            if (uscript_getScript(currChr, &status) != USCRIPT_HANGUL || uscript_getScript(prevChr, &status) != USCRIPT_HANGUL) {
+                break;
+            }
+        } else {
+            if (!u_isalpha(currChr) || !u_isalpha(prevChr)) {
+                break;
+            }
+        }
+        int32_t nextResult = handleNextInternal();
+        if (nextResult <= result) {
+            break;
+        }
+        result = nextResult;
+    }
+    return result;
 }
 
-int32_t RuleBasedBreakIterator::handleNext(const RBBIStateTable *statetable) {
-    if (fTrace) {
-        RBBIDebugPuts("Handle Next   pos   char  state category");
-    }
+int32_t RuleBasedBreakIterator::handleNextInternal() {
+    int32_t             state;
+    uint16_t            category        = 0;
+    RBBIRunMode         mode;
+
+    RBBIStateTableRow  *row;
+    UChar32             c;
+    LookAheadResults    lookAheadMatches;
+    int32_t             result             = 0;
+    int32_t             initialPosition    = 0;
+    const RBBIStateTable *statetable       = fData->fForwardTable;
+    const char         *tableData          = statetable->fTableData;
+    uint32_t            tableRowLen        = statetable->fRowLen;
+    #ifdef RBBI_DEBUG
+        if (gTrace) {
+            RBBIDebugPuts("Handle Next   pos   char  state category");
+        }
+    #endif
+
+    // handleNext alway sets the break tag value.
+    // Set the default for it.
+    fRuleStatusIndex = 0;
 
-    // No matter what, handleNext alway correctly sets the break tag value.
-    fLastStatusIndexValid = TRUE;
+    fDictionaryCharCount = 0;
 
     // if we're already at the end of the text, return DONE.
-    if (fText == NULL || fData == NULL || fText->hasNext() == FALSE) {
-        fLastRuleStatusIndex = 0;
-        return BreakIterator::DONE;
+    initialPosition = fPosition;
+    UTEXT_SETNATIVEINDEX(&fText, initialPosition);
+    result          = initialPosition;
+    c               = UTEXT_NEXT32(&fText);
+    if (c==U_SENTINEL) {
+        fDone = TRUE;
+        return UBRK_DONE;
     }
 
-    int32_t initialPosition = fText->getIndex();
-    int32_t result          = initialPosition;
-    int32_t lookaheadResult = 0;
-
-    // Initialize the state machine.  Begin in state 1
-    int32_t            state           = START_STATE;
-    int16_t            category;
-    UChar32            c               = fText->current32();
-    RBBIStateTableRow *row;
-    int32_t            lookaheadStatus = 0;
-    int32_t            lookaheadTagIdx = 0;
+    //  Set the initial state for the state machine
+    state = START_STATE;
+    row = (RBBIStateTableRow *)
+            //(statetable->fTableData + (statetable->fRowLen * state));
+            (tableData + tableRowLen * state);
 
-    fLastRuleStatusIndex = 0;
 
-    row = (RBBIStateTableRow *)    // Point to starting row of state table.
-        (statetable->fTableData + (statetable->fRowLen * state));
+    mode     = RBBI_RUN;
+    if (statetable->fFlags & RBBI_BOF_REQUIRED) {
+        category = 2;
+        mode     = RBBI_START;
+    }
 
-    // Character Category fetch for starting character.
-    //    See comments on character category code within loop, below.
-    UTRIE_GET16(&fData->fTrie, c, category);
-    if ((category & 0x4000) != 0)  {
-          fDictionaryCharCount++;
-          category &= ~0x4000;
-        }
 
     // loop until we reach the end of the text or transition to state 0
+    //
     for (;;) {
-        if (c == CharacterIterator::DONE && fText->hasNext()==FALSE) {
+        if (c == U_SENTINEL) {
             // Reached end of input string.
-            //    Note: CharacterIterator::DONE is 0xffff, which is also a legal
-            //          character value.  Check for DONE first, because it's quicker,
-            //          but also need to check fText->hasNext() to be certain.
-
-            if (lookaheadResult > result) {
-                // We ran off the end of the string with a pending look-ahead match.
-                // Treat this as if the look-ahead condition had been met, and return
-                //  the match at the / position from the look-ahead rule.
-                result               = lookaheadResult;
-                fLastRuleStatusIndex = lookaheadTagIdx;
-                lookaheadStatus = 0;
-            } else if (result == initialPosition) {
-                // Ran off end, no match found.
-                // move forward one
-                fText->setIndex(initialPosition);
-                fText->next32();
-                fText->getIndex();
+            if (mode == RBBI_END) {
+                // We have already run the loop one last time with the
+                //   character set to the psueudo {eof} value.  Now it is time
+                //   to unconditionally bail out.
+                break;
             }
-            break;
+            // Run the loop one last time with the fake end-of-input character category.
+            mode = RBBI_END;
+            category = 1;
         }
-        // look up the current character's character category, which tells us
-        // which column in the state table to look at.
-        // Note:  the 16 in UTRIE_GET16 refers to the size of the data being returned,
-        //        not the size of the character going in, which is a UChar32.
-        //
-        UTRIE_GET16(&fData->fTrie, c, category);
 
-        // Check the dictionary bit in the character's category.
-        //    Counter is only used by dictionary based iterators (subclasses).
-        //    Chars that need to be handled by a dictionary have a flag bit set
-        //    in their category values.
         //
-        if ((category & 0x4000) != 0)  {
-            fDictionaryCharCount++;
-            //  And off the dictionary flag bit.
-            category &= ~0x4000;
+        // Get the char category.  An incoming category of 1 or 2 means that
+        //      we are preset for doing the beginning or end of input, and
+        //      that we shouldn't get a category from an actual text input character.
+        //
+        if (mode == RBBI_RUN) {
+            // look up the current character's character category, which tells us
+            // which column in the state table to look at.
+            // Note:  the 16 in UTRIE_GET16 refers to the size of the data being returned,
+            //        not the size of the character going in, which is a UChar32.
+            //
+            category = (fLatin1Cat!=NULL && c<0x100)? fLatin1Cat[c]: UTRIE2_GET16(fData->fTrie, c);
+
+            // Check the dictionary bit in the character's category.
+            //    Counter is only used by dictionary based iteration.
+            //    Chars that need to be handled by a dictionary have a flag bit set
+            //    in their category values.
+            //
+            if ((category & 0x4000) != 0)  {
+                fDictionaryCharCount++;
+                //  And off the dictionary flag bit.
+                category &= ~0x4000;
+            }
         }
 
-        #ifdef RBBI_DEBUG
-            if (fTrace) {
-                RBBIDebugPrintf("             %4d   ", fText->getIndex());
+       #ifdef RBBI_DEBUG
+            if (gTrace) {
+                RBBIDebugPrintf("             %4ld   ", utext_getNativeIndex(&fText));
                 if (0x20<=c && c<0x7f) {
                     RBBIDebugPrintf("\"%c\"  ", c);
                 } else {
@@ -765,63 +909,60 @@ int32_t RuleBasedBreakIterator::handleNext(const RBBIStateTable *statetable) {
             }
         #endif
 
-        // look up a state transition in the state table
-        state = row->fNextState[category];
+        // State Transition - move machine to its next state
+        //
+
+        // fNextState is a variable-length array.
+        U_ASSERT(category<fData->fHeader->fCatCount);
+        state = row->fNextState[category];  /*Not accessing beyond memory*/
         row = (RBBIStateTableRow *)
-            (statetable->fTableData + (statetable->fRowLen * state));
+            // (statetable->fTableData + (statetable->fRowLen * state));
+            (tableData + tableRowLen * state);
 
-        // Get the next character.  Doing it here positions the iterator
-        //    to the correct position for recording matches in the code that
-        //    follows.
-        c = fText->next32();
 
         if (row->fAccepting == -1) {
-            // Match found, common case, could have lookahead so we move on to check it
-            result = fText->getIndex();
-            /// added
-            fLastRuleStatusIndex = row->fTagIdx;   // Remember the break status (tag) values.
+            // Match found, common case.
+            if (mode != RBBI_START) {
+                result = (int32_t)UTEXT_GETNATIVEINDEX(&fText);
+            }
+            fRuleStatusIndex = row->fTagIdx;   // Remember the break status (tag) values.
         }
 
-        if (row->fLookAhead != 0) {
-            if (lookaheadStatus != 0
-                && row->fAccepting == lookaheadStatus) {
-                // Lookahead match is completed.  Set the result accordingly, but only
-                // if no other rule has matched further in the mean time.
-                result               = lookaheadResult;
-                fLastRuleStatusIndex = lookaheadTagIdx;
-                lookaheadStatus      = 0;
-                /// i think we have to back up to read the lookahead character again
-                /// fText->setIndex(lookaheadResult);
-                /// TODO: this is a simple hack since reverse rules only have simple
-                /// lookahead rules that we can definitely break out from.
-                /// we need to make the lookahead rules not chain eventually.
-                /// return result;
-                /// this is going to be the longest match again
-                goto continueOn;
+        int16_t completedRule = row->fAccepting;
+        if (completedRule > 0) {
+            // Lookahead match is completed.
+            int32_t lookaheadResult = lookAheadMatches.getPosition(completedRule);
+            if (lookaheadResult >= 0) {
+                fRuleStatusIndex = row->fTagIdx;
+                fPosition = lookaheadResult;
+                return lookaheadResult;
             }
-
-            int32_t  r = fText->getIndex();
-            lookaheadResult = r;
-            lookaheadStatus = row->fLookAhead;
-            lookaheadTagIdx = row->fTagIdx;
-            goto continueOn;
         }
-
-
-        if (row->fAccepting == 0) {
-            // No match, nothing of interest happening, common case.
-            goto continueOn;
+        int16_t rule = row->fLookAhead;
+        if (rule != 0) {
+            // At the position of a '/' in a look-ahead match. Record it.
+            int32_t  pos = (int32_t)UTEXT_GETNATIVEINDEX(&fText);
+            lookAheadMatches.setPosition(rule, pos);
         }
 
-        lookaheadStatus = 0;           // clear out any pending look-ahead matches.
-
-continueOn:
         if (state == STOP_STATE) {
             // This is the normal exit from the lookup state machine.
             // We have advanced through the string until it is certain that no
             //   longer match is possible, no matter what characters follow.
             break;
         }
+
+        // Advance to the next character.
+        // If this is a beginning-of-input loop iteration, don't advance
+        //    the input position.  The next iteration will be processing the
+        //    first real input character.
+        if (mode == RBBI_RUN) {
+            c = UTEXT_NEXT32(&fText);
+        } else {
+            if (mode == RBBI_START) {
+                mode = RBBI_RUN;
+            }
+        }
     }
 
     // The state machine is done.  Check whether it found a match...
@@ -830,230 +971,74 @@ continueOn:
     //   (This really indicates a defect in the break rules.  They should always match
     //    at least one character.)
     if (result == initialPosition) {
-        result = fText->setIndex(initialPosition);
-        fText ->next32();
-        result = fText->getIndex();
+        utext_setNativeIndex(&fText, initialPosition);
+        utext_next32(&fText);
+        result = (int32_t)utext_getNativeIndex(&fText);
+        fRuleStatusIndex = 0;
     }
 
     // Leave the iterator at our result position.
-    fText->setIndex(result);
-    if (fTrace) {
-        RBBIDebugPrintf("result = %d\n\n", result);
-    }
-    return result;
-}
-
-
-//----------------------------------------------------------------
-//
-//   handlePrevious(void)     This is the variant used with old style rules
-//                            (Overshoot to a safe point, then move forward)
-//
-//----------------------------------------------------------------
-int32_t RuleBasedBreakIterator::handlePrevious(void) {
-    if (fText == NULL || fData == NULL) {
-        return 0;
-    }
-    if (fData->fReverseTable == NULL) {
-        return fText->setToStart();
-    }
-
-    int32_t            state           = START_STATE;
-    int32_t            category;
-    int32_t            lastCategory    = 0;
-    int32_t            result          = fText->getIndex();
-    int32_t            lookaheadStatus = 0;
-    int32_t            lookaheadResult = 0;
-    int32_t            lookaheadTagIdx = 0;
-    UChar32            c               = fText->current32();
-    RBBIStateTableRow *row;
-
-    row = (RBBIStateTableRow *)
-        (this->fData->fReverseTable->fTableData + (state * fData->fReverseTable->fRowLen));
-    UTRIE_GET16(&fData->fTrie, c, category);
-    if ((category & 0x4000) != 0)  {
-        fDictionaryCharCount++;
-        category &= ~0x4000;
-    }
-
-    if (fTrace) {
-        RBBIDebugPuts("Handle Prev   pos   char  state category");
-    }
-
-    // loop until we reach the beginning of the text or transition to state 0
-    for (;;) {
-        if (c == CharacterIterator::DONE && fText->hasPrevious()==FALSE) {
-            break;
-        }
-
-        // save the last character's category and look up the current
-        // character's category
-        lastCategory = category;
-        UTRIE_GET16(&fData->fTrie, c, category);
-
-        // Check the dictionary bit in the character's category.
-        //    Counter is only used by dictionary based iterators.
-        //
-        if ((category & 0x4000) != 0)  {
-            fDictionaryCharCount++;
-            category &= ~0x4000;
-        }
-
-        #ifdef RBBI_DEBUG
-            if (fTrace) {
-                RBBIDebugPrintf("             %4d   ", fText->getIndex());
-                if (0x20<=c && c<0x7f) {
-                    RBBIDebugPrintf("\"%c\"  ", c);
-                } else {
-                    RBBIDebugPrintf("%5x  ", c);
-                }
-                RBBIDebugPrintf("%3d  %3d\n", state, category);
-            }
-        #endif
-
-        // look up a state transition in the backwards state table
-        state = row->fNextState[category];
-        row = (RBBIStateTableRow *)
-            (this->fData->fReverseTable->fTableData + (state * fData->fReverseTable->fRowLen));
-
-        if (row->fAccepting == 0 && row->fLookAhead == 0) {
-            // No match, nothing of interest happening, common case.
-            goto continueOn;
+    fPosition = result;
+    #ifdef RBBI_DEBUG
+        if (gTrace) {
+            RBBIDebugPrintf("result = %d\n\n", result);
         }
-
-        if (row->fAccepting == -1) {
-            // Match found, common case, no lookahead involved.
-            result = fText->getIndex();
-            lookaheadStatus = 0;     // clear out any pending look-ahead matches.
-            goto continueOn;
-        }
-
-        if (row->fAccepting == 0 && row->fLookAhead != 0) {
-            // Lookahead match point.  Remember it, but only if no other rule
-            //                         has unconditionally matched to this point.
-            // TODO:  handle case where there's a pending match from a different rule
-            //        where lookaheadStatus != 0  && lookaheadStatus != row->fLookAhead.
-            int32_t  r = fText->getIndex();
-            if (r > result) {
-                lookaheadResult = r;
-                lookaheadStatus = row->fLookAhead;
-                lookaheadTagIdx = row->fTagIdx;
-            }
-            goto continueOn;
-        }
-
-        if (row->fAccepting != 0 && row->fLookAhead != 0) {
-            // Lookahead match is completed.  Set the result accordingly, but only
-            //   if no other rule has matched further in the mean time.
-            if (lookaheadResult > result) {
-                U_ASSERT(row->fAccepting == lookaheadStatus);   // TODO:  handle this case
-                //    of overlapping lookahead matches.
-                result               = lookaheadResult;
-                fLastRuleStatusIndex = lookaheadTagIdx;
-                lookaheadStatus = 0;
-            }
-            goto continueOn;
-        }
-
-continueOn:
-        if (state == STOP_STATE) {
-            break;
-        }
-
-        // then advance one character backwards
-        c = fText->previous32();
-    }
-
-    // Note:  the result postion isn't what is returned to the user by previous(),
-    //        but where the implementation of previous() turns around and
-    //        starts iterating forward again.
-    if (c == CharacterIterator::DONE && fText->hasPrevious()==FALSE) {
-        result = fText->startIndex();
-    }
-    fText->setIndex(result);
-
+    #endif
     return result;
 }
 
 
 //-----------------------------------------------------------------------------------
 //
-//  handlePrevious()
+//  handleSafePrevious()
 //
-//      This method backs the iterator back up to a "safe position" in the text.
-//      This is a position that we know, without any context, may be any position
-//      not more than 2 breaks away. Occasionally, the position may be less than
-//      one break away.
-//      The various calling methods then iterate forward from this safe position to
-//      the appropriate position to return.
-//
-//      The logic of this function is very similar to handleNext(), above.
+//      Iterate backwards using the safe reverse rules.
+//      The logic of this function is similar to handleNext(), but simpler
+//      because the safe table does not require as many options.
 //
 //-----------------------------------------------------------------------------------
-int32_t RuleBasedBreakIterator::handlePrevious(const RBBIStateTable *statetable) {
-    if (fText == NULL || statetable == NULL) {
-        return 0;
-    }
-    // break tag is no longer valid after icu switched to exact backwards
-    // positioning.
-    fLastStatusIndexValid = FALSE;
-    if (statetable == NULL) {
-        return fText->setToStart();
-    }
-
-    int32_t            state              = START_STATE;
-    int32_t            category;
-    int32_t            lastCategory       = 0;
-    UBool              hasPassedStartText = !fText->hasPrevious();
-    UChar32            c                  = fText->previous32();
-    // previous character
-    int32_t            result             = fText->getIndex();
-    int32_t            lookaheadStatus    = 0;
-    int32_t            lookaheadResult    = 0;
-    int32_t            lookaheadTagIdx    = 0;
-    UBool              lookAheadHardBreak = (statetable->fFlags & RBBI_LOOKAHEAD_HARD_BREAK) != 0;
-
-    RBBIStateTableRow *row;
-
-    row = (RBBIStateTableRow *)
-        (statetable->fTableData + (state * statetable->fRowLen));
-    UTRIE_GET16(&fData->fTrie, c, category);
-    if ((category & 0x4000) != 0)  {
-        fDictionaryCharCount++;
-        category &= ~0x4000;
-    }
+int32_t RuleBasedBreakIterator::handleSafePrevious(int32_t fromPosition) {
+    int32_t             state;
+    uint16_t            category        = 0;
+    RBBIStateTableRow  *row;
+    UChar32             c;
+    int32_t             result          = 0;
+
+    const RBBIStateTable *stateTable = fData->fReverseTable;
+    UTEXT_SETNATIVEINDEX(&fText, fromPosition);
+    #ifdef RBBI_DEBUG
+        if (gTrace) {
+            RBBIDebugPuts("Handle Previous   pos   char  state category");
+        }
+    #endif
 
-    if (fTrace) {
-        RBBIDebugPuts("Handle Prev   pos   char  state category");
+    // if we're already at the start of the text, return DONE.
+    if (fData == NULL || UTEXT_GETNATIVEINDEX(&fText)==0) {
+        return BreakIterator::DONE;
     }
 
-    // loop until we reach the beginning of the text or transition to state 0
-    for (;;) {
-        // if (c == CharacterIterator::DONE && fText->hasPrevious()==FALSE) {
-        if (hasPassedStartText) {
-            // if we have already considered the start of the text
-            if (row->fLookAhead != 0 && lookaheadResult == 0) {
-                result = 0;
-            }
-            break;
-        }
+    //  Set the initial state for the state machine
+    c = UTEXT_PREVIOUS32(&fText);
+    state = START_STATE;
+    row = (RBBIStateTableRow *)
+            (stateTable->fTableData + (stateTable->fRowLen * state));
 
-        // save the last character's category and look up the current
-        // character's category
-        lastCategory = category;
-        UTRIE_GET16(&fData->fTrie, c, category);
+    // loop until we reach the start of the text or transition to state 0
+    //
+    for (; c != U_SENTINEL; c = UTEXT_PREVIOUS32(&fText)) {
 
-        // Check the dictionary bit in the character's category.
-        //    Counter is only used by dictionary based iterators.
+        // look up the current character's character category, which tells us
+        // which column in the state table to look at.
+        // Note:  the 16 in UTRIE_GET16 refers to the size of the data being returned,
+        //        not the size of the character going in, which is a UChar32.
         //
-        if ((category & 0x4000) != 0)  {
-            fDictionaryCharCount++;
-            category &= ~0x4000;
-        }
+        //  And off the dictionary flag bit. For reverse iteration it is not used.
+        category = UTRIE2_GET16(fData->fTrie, c);
+        category &= ~0x4000;
 
         #ifdef RBBI_DEBUG
-            if (fTrace) {
-                RBBIDebugPrintf("             %4d   ", fText->getIndex());
+            if (gTrace) {
+                RBBIDebugPrintf("             %4d   ", (int32_t)utext_getNativeIndex(&fText));
                 if (0x20<=c && c<0x7f) {
                     RBBIDebugPrintf("\"%c\"  ", c);
                 } else {
@@ -1063,90 +1048,31 @@ int32_t RuleBasedBreakIterator::handlePrevious(const RBBIStateTable *statetable)
             }
         #endif
 
-        // look up a state transition in the backwards state table
-        state = row->fNextState[category];
+        // State Transition - move machine to its next state
+        //
+        // fNextState is a variable-length array.
+        U_ASSERT(category<fData->fHeader->fCatCount);
+        state = row->fNextState[category];  /*Not accessing beyond memory*/
         row = (RBBIStateTableRow *)
-            (statetable->fTableData + (state * statetable->fRowLen));
-
-        if (row->fAccepting == -1) {
-            // Match found, common case, could have lookahead so we move on to check it
-            result = fText->getIndex();
-            /// added
-            fLastRuleStatusIndex   = row->fTagIdx;   // Remember the break status (tag) value.
-        }
-
-        if (row->fLookAhead != 0) {
-            if (lookaheadStatus != 0
-                && row->fAccepting == lookaheadStatus) {
-                // Lookahead match is completed.  Set the result accordingly, but only
-                // if no other rule has matched further in the mean time.
-                result               = lookaheadResult;
-                fLastRuleStatusIndex = lookaheadTagIdx;
-                lookaheadStatus      = 0;
-                /// i think we have to back up to read the lookahead character again
-                /// fText->setIndex(lookaheadResult);
-                /// TODO: this is a simple hack since reverse rules only have simple
-                /// lookahead rules that we can definitely break out from.
-                /// we need to make the lookahead rules not chain eventually.
-                /// return result;
-                /// this is going to be the longest match again
-
-                /// syn wee todo hard coded for line breaks stuff
-                /// needs to provide a tag in rules to ensure a stop.
-
-                if (lookAheadHardBreak) {
-                    fText->setIndex(result);
-                    return result;
-                }
-                category = lastCategory;
-                fText->setIndex(result);
-
-                goto continueOn;
-            }
-
-            int32_t    r         = fText->getIndex();
-            lookaheadResult      = r;
-            lookaheadStatus      = row->fLookAhead;
-            fLastRuleStatusIndex = row->fTagIdx;
-            goto continueOn;
-        }
+            (stateTable->fTableData + (stateTable->fRowLen * state));
 
-        // not lookahead
-        if (row->fAccepting == 0) {
-            // No match, nothing of interest happening, common case.
-            goto continueOn;
-        }
-
-        lookaheadStatus = 0;     // clear out any pending look-ahead matches.
-
-continueOn:
         if (state == STOP_STATE) {
+            // This is the normal exit from the lookup state machine.
+            // Transistion to state zero means we have found a safe point.
             break;
         }
-
-        // then advance one character backwards
-        hasPassedStartText = !fText->hasPrevious();
-        c = fText->previous32();
     }
 
-    // Note:  the result postion isn't what is returned to the user by previous(),
-    //        but where the implementation of previous() turns around and
-    //        starts iterating forward again.
-    fText->setIndex(result);
-
+    // The state machine is done.  Check whether it found a match...
+    result = (int32_t)UTEXT_GETNATIVEINDEX(&fText);
+    #ifdef RBBI_DEBUG
+        if (gTrace) {
+            RBBIDebugPrintf("result = %d\n\n", result);
+        }
+    #endif
     return result;
 }
 
-
-void
-RuleBasedBreakIterator::reset()
-{
-    // Base-class version of this function is a no-op.
-    // Subclasses may override with their own reset behavior.
-}
-
-
-
 //-------------------------------------------------------------------------------
 //
 //   getRuleStatus()   Return the break rule tag associated with the current
@@ -1154,62 +1080,27 @@ RuleBasedBreakIterator::reset()
 //                     position by iterating forwards, the value will have been
 //                     cached by the handleNext() function.
 //
-//                     If no cached status value is available, the status is
-//                     found by doing a previous() followed by a next(), which
-//                     leaves the iterator where it started, and computes the
-//                     status while doing the next().
-//
 //-------------------------------------------------------------------------------
-void RuleBasedBreakIterator::makeRuleStatusValid() {
-    if (fLastStatusIndexValid == FALSE) {
-        //  No cached status is available.
-        if (fText == NULL || current() == fText->startIndex()) {
-            //  At start of text, or there is no text.  Status is always zero.
-            fLastRuleStatusIndex = 0;
-            fLastStatusIndexValid = TRUE;
-        } else {
-            //  Not at start of text.  Find status the tedious way.
-            int32_t pa = current();
-            previous();
-            int32_t pb = next();
-            if (pa != pb) {
-                // note: the if (pa != pb) test is here only to eliminate warnings for
-                //       unused local variables on gcc.  Logically, it isn't needed.
-                U_ASSERT(pa == pb);
-            }
-        }
-    }
-    U_ASSERT(fLastStatusIndexValid == TRUE);
-    U_ASSERT(fLastRuleStatusIndex >= 0  &&  fLastRuleStatusIndex < fData->fStatusMaxIdx);
-}
-
 
 int32_t  RuleBasedBreakIterator::getRuleStatus() const {
-    RuleBasedBreakIterator *nonConstThis  = (RuleBasedBreakIterator *)this;
-    nonConstThis->makeRuleStatusValid();
 
     // fLastRuleStatusIndex indexes to the start of the appropriate status record
     //                                                 (the number of status values.)
     //   This function returns the last (largest) of the array of status values.
-    int32_t  idx = fLastRuleStatusIndex + fData->fRuleStatusTable[fLastRuleStatusIndex];
+    int32_t  idx = fRuleStatusIndex + fData->fRuleStatusTable[fRuleStatusIndex];
     int32_t  tagVal = fData->fRuleStatusTable[idx];
 
     return tagVal;
 }
 
 
-
-
 int32_t RuleBasedBreakIterator::getRuleStatusVec(
-             int32_t *fillInVec, int32_t capacity, UErrorCode &status)
-{
+             int32_t *fillInVec, int32_t capacity, UErrorCode &status) {
     if (U_FAILURE(status)) {
         return 0;
     }
 
-    RuleBasedBreakIterator *nonConstThis  = (RuleBasedBreakIterator *)this;
-    nonConstThis->makeRuleStatusValid();
-    int32_t  numVals = fData->fRuleStatusTable[fLastRuleStatusIndex];
+    int32_t  numVals = fData->fRuleStatusTable[fRuleStatusIndex];
     int32_t  numValsToCopy = numVals;
     if (numVals > capacity) {
         status = U_BUFFER_OVERFLOW_ERROR;
@@ -1217,12 +1108,70 @@ int32_t RuleBasedBreakIterator::getRuleStatusVec(
     }
     int i;
     for (i=0; i<numValsToCopy; i++) {
-        fillInVec[i] = fData->fRuleStatusTable[fLastRuleStatusIndex + i + 1];
+        fillInVec[i] = fData->fRuleStatusTable[fRuleStatusIndex + i + 1];
     }
     return numVals;
 }
 
-
+// Apple custom addition
+int32_t RuleBasedBreakIterator::tokenize(int32_t maxTokens, RuleBasedTokenRange *outTokenRanges, unsigned long *outTokenFlags)
+{
+    //os_log(OS_LOG_DEFAULT, "# tokenize 0: maxT %d; txt idx %lld, len %lld", maxTokens, utext_getNativeIndex(fText), utext_nativeLength(fText));
+    if (fDone) {
+        return 0;
+    }
+    RuleBasedTokenRange *outTokenLimit = outTokenRanges + maxTokens;
+    RuleBasedTokenRange *outTokenP = outTokenRanges;
+    int32_t lastOffset = fPosition;
+    //os_log(OS_LOG_DEFAULT, "# tokenize 1");
+    while (outTokenP < outTokenLimit) {
+        // start portion from inlining populateFollowing()
+        int32_t pos = 0;
+        int32_t ruleStatusIdx = 0;
+        int32_t startPos = fPosition;
+
+        if (fDictionaryCache->following(startPos, &pos, &ruleStatusIdx)) {
+            fPosition = pos;
+            fRuleStatusIndex = ruleStatusIdx;
+        } else {
+            pos = handleNextInternal(); // sets fRuleStatusIndex for the pos it returns, updates fPosition
+            if (pos == UBRK_DONE) {
+                // fDone = TRUE; already set by handleNextInternal
+                break;
+            }
+            // Use current result from handleNextInternal(), including fRuleStatusIndex,
+            // unless overridden by dictionary subdivisions
+            fPosition = pos;
+            if (fDictionaryCharCount > 0) {
+                // The text segment obtained from the rules includes dictionary characters.
+                // Subdivide it, with subdivided results going into the dictionary cache.
+                fDictionaryCache->populateDictionary(startPos, pos, fRuleStatusIndex, fRuleStatusIndex);
+                if (fDictionaryCache->following(startPos, &pos, &ruleStatusIdx)) {
+                    fPosition = pos;
+                    fRuleStatusIndex = ruleStatusIdx;
+                }
+            }
+        }
+        // end portion from inlining populateFollowing()
+        int32_t flagCount = fData->fRuleStatusTable[fRuleStatusIndex];
+        const int32_t* flagPtr = fData->fRuleStatusTable + fRuleStatusIndex + flagCount;
+        int32_t flagSet = *flagPtr; // if -1 then skip token
+        if (flagSet != -1) {
+            outTokenP->location = lastOffset;
+            outTokenP++->length = fPosition - lastOffset;
+            if (outTokenFlags) {
+                // flagSet should be the OR of all flags returned by getRuleStatusVec;
+                // here we collect from high-order to low-order.
+                while (--flagCount > 0) {
+                   flagSet |=  *--flagPtr;
+                }
+                *outTokenFlags++ = (unsigned long)flagSet;
+            }
+        }
+        lastOffset = fPosition;
+    }
+    return (outTokenP - outTokenRanges);
+}
 
 //-------------------------------------------------------------------------------
 //
@@ -1243,19 +1192,7 @@ const uint8_t  *RuleBasedBreakIterator::getBinaryRules(uint32_t &length) {
 }
 
 
-
-
-//-------------------------------------------------------------------------------
-//
-//  BufferClone       TODO:  In my (Andy) opinion, this function should be deprecated.
-//                    Saving one heap allocation isn't worth the trouble.
-//                    Cloning shouldn't be done in tight loops, and
-//                    making the clone copy involves other heap operations anyway.
-//                    And the application code for correctly dealing with buffer
-//                    size problems and the eventual object destruction is ugly.
-//
-//-------------------------------------------------------------------------------
-BreakIterator *  RuleBasedBreakIterator::createBufferClone(void *stackBuffer,
+BreakIterator *  RuleBasedBreakIterator::createBufferClone(void * /*stackBuffer*/,
                                    int32_t &bufferSize,
                                    UErrorCode &status)
 {
@@ -1263,83 +1200,181 @@ BreakIterator *  RuleBasedBreakIterator::createBufferClone(void *stackBuffer,
         return NULL;
     }
 
-    //
-    //  If user buffer size is zero this is a preflight operation to
-    //    obtain the needed buffer size, allowing for worst case misalignment.
-    //
     if (bufferSize == 0) {
-        bufferSize = sizeof(RuleBasedBreakIterator) + U_ALIGNMENT_OFFSET_UP(0);
+        bufferSize = 1;  // preflighting for deprecated functionality
         return NULL;
     }
 
+    BreakIterator *clonedBI = clone();
+    if (clonedBI == NULL) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+    } else {
+        status = U_SAFECLONE_ALLOCATED_WARNING;
+    }
+    return (RuleBasedBreakIterator *)clonedBI;
+}
 
-    //
-    //  Check the alignment and size of the user supplied buffer.
-    //  Allocate heap memory if the user supplied memory is insufficient.
-    //
-    char    *buf   = (char *)stackBuffer;
-    uint32_t s      = bufferSize;
+U_NAMESPACE_END
 
-    if (stackBuffer == NULL) {
-        s = 0;   // Ignore size, force allocation if user didn't give us a buffer.
-    }
-    if (U_ALIGNMENT_OFFSET(stackBuffer) != 0) {
-        uint32_t offsetUp = (uint32_t)U_ALIGNMENT_OFFSET_UP(buf);
-        s   -= offsetUp;
-        buf += offsetUp;
-    }
-    if (s < sizeof(RuleBasedBreakIterator)) {
-        buf = (char *) new RuleBasedBreakIterator;
-        if (buf == 0) {
-            status = U_MEMORY_ALLOCATION_ERROR;
-            return NULL;
+
+static icu::UStack *gLanguageBreakFactories = nullptr;
+static const icu::UnicodeString *gEmptyString = nullptr;
+static icu::UInitOnce gLanguageBreakFactoriesInitOnce = U_INITONCE_INITIALIZER;
+static icu::UInitOnce gRBBIInitOnce = U_INITONCE_INITIALIZER;
+
+/**
+ * Release all static memory held by breakiterator.
+ */
+U_CDECL_BEGIN
+static UBool U_CALLCONV rbbi_cleanup(void) {
+    delete gLanguageBreakFactories;
+    gLanguageBreakFactories = nullptr;
+    delete gEmptyString;
+    gEmptyString = nullptr;
+    gLanguageBreakFactoriesInitOnce.reset();
+    gRBBIInitOnce.reset();
+    return TRUE;
+}
+U_CDECL_END
+
+U_CDECL_BEGIN
+static void U_CALLCONV _deleteFactory(void *obj) {
+    delete (icu::LanguageBreakFactory *) obj;
+}
+U_CDECL_END
+U_NAMESPACE_BEGIN
+
+static void U_CALLCONV rbbiInit() {
+    gEmptyString = new UnicodeString();
+    ucln_common_registerCleanup(UCLN_COMMON_RBBI, rbbi_cleanup);
+}
+
+static void U_CALLCONV initLanguageFactories() {
+    UErrorCode status = U_ZERO_ERROR;
+    U_ASSERT(gLanguageBreakFactories == NULL);
+    gLanguageBreakFactories = new UStack(_deleteFactory, NULL, status);
+    if (gLanguageBreakFactories != NULL && U_SUCCESS(status)) {
+        ICULanguageBreakFactory *builtIn = new ICULanguageBreakFactory(status);
+        gLanguageBreakFactories->push(builtIn, status);
+#ifdef U_LOCAL_SERVICE_HOOK
+        LanguageBreakFactory *extra = (LanguageBreakFactory *)uprv_svc_hook("languageBreakFactory", &status);
+        if (extra != NULL) {
+            gLanguageBreakFactories->push(extra, status);
         }
-        status = U_SAFECLONE_ALLOCATED_WARNING;
+#endif
     }
+    ucln_common_registerCleanup(UCLN_COMMON_RBBI, rbbi_cleanup);
+}
 
-    //
-    //  Clone the object.
-    //    TODO:  using an overloaded operator new to directly initialize the
-    //           copy in the user's buffer would be better, but it doesn't seem
-    //           to get along with namespaces.  Investigate why.
-    //
-    //           The memcpy is only safe with an empty (default constructed)
-    //           break iterator.  Use on others can screw up reference counts
-    //           to data.  memcpy-ing objects is not really a good idea...
-    //
-    RuleBasedBreakIterator localIter;        // Empty break iterator, source for memcpy
-    RuleBasedBreakIterator *clone = (RuleBasedBreakIterator *)buf;
-    uprv_memcpy(clone, &localIter, sizeof(RuleBasedBreakIterator)); // clone = empty, but initialized, iterator.
-    *clone = *this;                          // clone = the real one we want.
-    if (status != U_SAFECLONE_ALLOCATED_WARNING) {
-        clone->fBufferClone = TRUE;
+
+static const LanguageBreakEngine*
+getLanguageBreakEngineFromFactory(UChar32 c)
+{
+    umtx_initOnce(gLanguageBreakFactoriesInitOnce, &initLanguageFactories);
+    if (gLanguageBreakFactories == NULL) {
+        return NULL;
     }
 
-    return clone;
+    int32_t i = gLanguageBreakFactories->size();
+    const LanguageBreakEngine *lbe = NULL;
+    while (--i >= 0) {
+        LanguageBreakFactory *factory = (LanguageBreakFactory *)(gLanguageBreakFactories->elementAt(i));
+        lbe = factory->getEngineFor(c);
+        if (lbe != NULL) {
+            break;
+        }
+    }
+    return lbe;
 }
 
 
-
 //-------------------------------------------------------------------------------
 //
-//  isDictionaryChar      Return true if the category lookup for this char
-//                        indicates that it is in the set of dictionary lookup
-//                        chars.
-//
-//                        This function is intended for use by dictionary based
-//                        break iterators.
+//  getLanguageBreakEngine  Find an appropriate LanguageBreakEngine for the
+//                          the character c.
 //
 //-------------------------------------------------------------------------------
-UBool RuleBasedBreakIterator::isDictionaryChar(UChar32   c) {
-    if (fData == NULL) {
-        return FALSE;
+const LanguageBreakEngine *
+RuleBasedBreakIterator::getLanguageBreakEngine(UChar32 c) {
+    const LanguageBreakEngine *lbe = NULL;
+    UErrorCode status = U_ZERO_ERROR;
+
+    if (fLanguageBreakEngines == NULL) {
+        fLanguageBreakEngines = new UStack(status);
+        if (fLanguageBreakEngines == NULL || U_FAILURE(status)) {
+            delete fLanguageBreakEngines;
+            fLanguageBreakEngines = 0;
+            return NULL;
+        }
     }
-    uint16_t category;
-    UTRIE_GET16(&fData->fTrie, c, category);
-    return (category & 0x4000) != 0;
+
+    int32_t i = fLanguageBreakEngines->size();
+    while (--i >= 0) {
+        lbe = (const LanguageBreakEngine *)(fLanguageBreakEngines->elementAt(i));
+        if (lbe->handles(c)) {
+            return lbe;
+        }
+    }
+
+    // No existing dictionary took the character. See if a factory wants to
+    // give us a new LanguageBreakEngine for this character.
+    lbe = getLanguageBreakEngineFromFactory(c);
+
+    // If we got one, use it and push it on our stack.
+    if (lbe != NULL) {
+        fLanguageBreakEngines->push((void *)lbe, status);
+        // Even if we can't remember it, we can keep looking it up, so
+        // return it even if the push fails.
+        return lbe;
+    }
+
+    // No engine is forthcoming for this character. Add it to the
+    // reject set. Create the reject break engine if needed.
+    if (fUnhandledBreakEngine == NULL) {
+        fUnhandledBreakEngine = new UnhandledEngine(status);
+        if (U_SUCCESS(status) && fUnhandledBreakEngine == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return nullptr;
+        }
+        // Put it last so that scripts for which we have an engine get tried
+        // first.
+        fLanguageBreakEngines->insertElementAt(fUnhandledBreakEngine, 0, status);
+        // If we can't insert it, or creation failed, get rid of it
+        if (U_FAILURE(status)) {
+            delete fUnhandledBreakEngine;
+            fUnhandledBreakEngine = 0;
+            return NULL;
+        }
+    }
+
+    // Tell the reject engine about the character; at its discretion, it may
+    // add more than just the one character.
+    fUnhandledBreakEngine->handleCharacter(c);
+
+    return fUnhandledBreakEngine;
 }
 
+void RuleBasedBreakIterator::dumpCache() {
+    fBreakCache->dumpCache();
+}
 
+void RuleBasedBreakIterator::dumpTables() {
+    fData->printData();
+}
+
+/**
+ * Returns the description used to create this iterator
+ */
+
+const UnicodeString&
+RuleBasedBreakIterator::getRules() const {
+    if (fData != NULL) {
+        return fData->getRuleSourceString();
+    } else {
+        umtx_initOnce(gRBBIInitOnce, &rbbiInit);
+        return *gEmptyString;
+    }
+}
 
 U_NAMESPACE_END