text: // Copyright (C) 2016 and later: Unicode, Inc. and others.
  // License & terms of use: http://www.unicode.org/copyright.html
  /*
  *******************************************************************************
  *
  *   Copyright (C) 2009-2014, International Business Machines
  *   Corporation and others.  All Rights Reserved.
  *
  *******************************************************************************
  *   file name:  normalizer2impl.cpp
  *   encoding:   US-ASCII
  *   tab size:   8 (not used)
  *   indentation:4
  *
  *   created on: 2009nov22
  *   created by: Markus W. Scherer
  */
  
  #include "unicode/utypes.h"
  
  #if !UCONFIG_NO_NORMALIZATION
  
  #include "unicode/normalizer2.h"
  #include "unicode/udata.h"
  #include "unicode/ustring.h"
  #include "unicode/utf16.h"
  #include "cmemory.h"
  #include "mutex.h"
  #include "normalizer2impl.h"
  #include "putilimp.h"
  #include "uassert.h"
  #include "uset_imp.h"
  #include "utrie2.h"
  #include "uvector.h"
  
  U_NAMESPACE_BEGIN
  
  // ReorderingBuffer -------------------------------------------------------- ***
  
  UBool ReorderingBuffer::init(int32_t destCapacity, UErrorCode &errorCode) {
      int32_t length=str.length();
      start=str.getBuffer(destCapacity);
      if(start==NULL) {
          // getBuffer() already did str.setToBogus()
          errorCode=U_MEMORY_ALLOCATION_ERROR;
          return FALSE;
      }
      limit=start+length;
      remainingCapacity=str.getCapacity()-length;
      reorderStart=start;
      if(start==limit) {
          lastCC=0;
      } else {
          setIterator();
          lastCC=previousCC();
          // Set reorderStart after the last code point with cc<=1 if there is one.
          if(lastCC>1) {
              while(previousCC()>1) {}
          }
          reorderStart=codePointLimit;
      }
      return TRUE;
  }
  
  UBool ReorderingBuffer::equals(const UChar *otherStart, const UChar *otherLimit) const {
      int32_t length=(int32_t)(limit-start);
      return
          length==(int32_t)(otherLimit-otherStart) &&
          0==u_memcmp(start, otherStart, length);
  }
  
  UBool ReorderingBuffer::appendSupplementary(UChar32 c, uint8_t cc, UErrorCode &errorCode) {
      if(remainingCapacity<2 && !resize(2, errorCode)) {
          return FALSE;
      }
      if(lastCC<=cc || cc==0) {
          limit[0]=U16_LEAD(c);
          limit[1]=U16_TRAIL(c);
          limit+=2;
          lastCC=cc;
          if(cc<=1) {
              reorderStart=limit;
          }
      } else {
          insert(c, cc);
      }
      remainingCapacity-=2;
      return TRUE;
  }
  
  UBool ReorderingBuffer::append(const UChar *s, int32_t length,
                                 uint8_t leadCC, uint8_t trailCC,
                                 UErrorCode &errorCode) {
      if(length==0) {
          return TRUE;
      }
      if(remainingCapacity<length && !resize(length, errorCode)) {
          return FALSE;
      }
      remainingCapacity-=length;
      if(lastCC<=leadCC || leadCC==0) {
          if(trailCC<=1) {
              reorderStart=limit+length;
          } else if(leadCC<=1) {
              reorderStart=limit+1;  // Ok if not a code point boundary.
          }
          const UChar *sLimit=s+length;
          do { *limit++=*s++; } while(s!=sLimit);
          lastCC=trailCC;
      } else {
          int32_t i=0;
          UChar32 c;
          U16_NEXT(s, i, length, c);
          insert(c, leadCC);  // insert first code point
          while(i<length) {
              U16_NEXT(s, i, length, c);
              if(i<length) {
                  // s must be in NFD, otherwise we need to use getCC().
                  leadCC=Normalizer2Impl::getCCFromYesOrMaybe(impl.getNorm16(c));
              } else {
                  leadCC=trailCC;
              }
              append(c, leadCC, errorCode);
          }
      }
      return TRUE;
  }
  
  UBool ReorderingBuffer::appendZeroCC(UChar32 c, UErrorCode &errorCode) {
      int32_t cpLength=U16_LENGTH(c);
      if(remainingCapacity<cpLength && !resize(cpLength, errorCode)) {
          return FALSE;
      }
      remainingCapacity-=cpLength;
      if(cpLength==1) {
          *limit++=(UChar)c;
      } else {
          limit[0]=U16_LEAD(c);
          limit[1]=U16_TRAIL(c);
          limit+=2;
      }
      lastCC=0;
      reorderStart=limit;
      return TRUE;
  }
  
  UBool ReorderingBuffer::appendZeroCC(const UChar *s, const UChar *sLimit, UErrorCode &errorCode) {
      if(s==sLimit) {
          return TRUE;
      }
      int32_t length=(int32_t)(sLimit-s);
      if(remainingCapacity<length && !resize(length, errorCode)) {
          return FALSE;
      }
      u_memcpy(limit, s, length);
      limit+=length;
      remainingCapacity-=length;
      lastCC=0;
      reorderStart=limit;
      return TRUE;
  }
  
  void ReorderingBuffer::remove() {
      reorderStart=limit=start;
      remainingCapacity=str.getCapacity();
      lastCC=0;
  }
  
  void ReorderingBuffer::removeSuffix(int32_t suffixLength) {
      if(suffixLength<(limit-start)) {
          limit-=suffixLength;
          remainingCapacity+=suffixLength;
      } else {
          limit=start;
          remainingCapacity=str.getCapacity();
      }
      lastCC=0;
      reorderStart=limit;
  }
  
  UBool ReorderingBuffer::resize(int32_t appendLength, UErrorCode &errorCode) {
      int32_t reorderStartIndex=(int32_t)(reorderStart-start);
      int32_t length=(int32_t)(limit-start);
      str.releaseBuffer(length);
      int32_t newCapacity=length+appendLength;
      int32_t doubleCapacity=2*str.getCapacity();
      if(newCapacity<doubleCapacity) {
          newCapacity=doubleCapacity;
      }
      if(newCapacity<256) {
          newCapacity=256;
      }
      start=str.getBuffer(newCapacity);
      if(start==NULL) {
          // getBuffer() already did str.setToBogus()
          errorCode=U_MEMORY_ALLOCATION_ERROR;
          return FALSE;
      }
      reorderStart=start+reorderStartIndex;
      limit=start+length;
      remainingCapacity=str.getCapacity()-length;
      return TRUE;
  }
  
  void ReorderingBuffer::skipPrevious() {
      codePointLimit=codePointStart;
      UChar c=*--codePointStart;
      if(U16_IS_TRAIL(c) && start<codePointStart && U16_IS_LEAD(*(codePointStart-1))) {
          --codePointStart;
      }
  }
  
  uint8_t ReorderingBuffer::previousCC() {
      codePointLimit=codePointStart;
      if(reorderStart>=codePointStart) {
          return 0;
      }
      UChar32 c=*--codePointStart;
      if(c<Normalizer2Impl::MIN_CCC_LCCC_CP) {
          return 0;
      }
  
      UChar c2;
      if(U16_IS_TRAIL(c) && start<codePointStart && U16_IS_LEAD(c2=*(codePointStart-1))) {
          --codePointStart;
          c=U16_GET_SUPPLEMENTARY(c2, c);
      }
      return Normalizer2Impl::getCCFromYesOrMaybe(impl.getNorm16(c));
  }
  
  // Inserts c somewhere before the last character.
  // Requires 0<cc<lastCC which implies reorderStart<limit.
  void ReorderingBuffer::insert(UChar32 c, uint8_t cc) {
      for(setIterator(), skipPrevious(); previousCC()>cc;) {}
      // insert c at codePointLimit, after the character with prevCC<=cc
      UChar *q=limit;
      UChar *r=limit+=U16_LENGTH(c);
      do {
          *--r=*--q;
      } while(codePointLimit!=q);
      writeCodePoint(q, c);
      if(cc<=1) {
          reorderStart=r;
      }
  }
  
  // Normalizer2Impl --------------------------------------------------------- ***
  
  struct CanonIterData : public UMemory {
      CanonIterData(UErrorCode &errorCode);
      ~CanonIterData();
      void addToStartSet(UChar32 origin, UChar32 decompLead, UErrorCode &errorCode);
      UTrie2 *trie;
      UVector canonStartSets;  // contains UnicodeSet *
  };
  
  Normalizer2Impl::~Normalizer2Impl() {
      delete fCanonIterData;
  }
  
  void
  Normalizer2Impl::init(const int32_t *inIndexes, const UTrie2 *inTrie,
                        const uint16_t *inExtraData, const uint8_t *inSmallFCD) {
      minDecompNoCP=inIndexes[IX_MIN_DECOMP_NO_CP];
      minCompNoMaybeCP=inIndexes[IX_MIN_COMP_NO_MAYBE_CP];
  
      minYesNo=inIndexes[IX_MIN_YES_NO];
      minYesNoMappingsOnly=inIndexes[IX_MIN_YES_NO_MAPPINGS_ONLY];
      minNoNo=inIndexes[IX_MIN_NO_NO];
      limitNoNo=inIndexes[IX_LIMIT_NO_NO];
      minMaybeYes=inIndexes[IX_MIN_MAYBE_YES];
  
      normTrie=inTrie;
  
      maybeYesCompositions=inExtraData;
      extraData=maybeYesCompositions+(MIN_NORMAL_MAYBE_YES-minMaybeYes);
  
      smallFCD=inSmallFCD;
  
      // Build tccc180[].
      // gennorm2 enforces lccc=0 for c<MIN_CCC_LCCC_CP=U+0300.
      uint8_t bits=0;
      for(UChar c=0; c<0x180; bits>>=1) {
          if((c&0xff)==0) {
              bits=smallFCD[c>>8];  // one byte per 0x100 code points
          }
          if(bits&1) {
              for(int i=0; i<0x20; ++i, ++c) {
                  tccc180[c]=(uint8_t)getFCD16FromNormData(c);
              }
          } else {
              uprv_memset(tccc180+c, 0, 0x20);
              c+=0x20;
          }
      }
  }
  
  uint8_t Normalizer2Impl::getTrailCCFromCompYesAndZeroCC(const UChar *cpStart, const UChar *cpLimit) const {
      UChar32 c;
      if(cpStart==(cpLimit-1)) {
          c=*cpStart;
      } else {
          c=U16_GET_SUPPLEMENTARY(cpStart[0], cpStart[1]);
      }
      uint16_t prevNorm16=getNorm16(c);
      if(prevNorm16<=minYesNo) {
          return 0;  // yesYes and Hangul LV/LVT have ccc=tccc=0
      } else {
          return (uint8_t)(*getMapping(prevNorm16)>>8);  // tccc from yesNo
      }
  }
  
  namespace {
  
  class LcccContext {
  public:
      LcccContext(const Normalizer2Impl &ni, UnicodeSet &s) : impl(ni), set(s) {}
  
      void handleRange(UChar32 start, UChar32 end, uint16_t norm16) {
          if(impl.isAlgorithmicNoNo(norm16)) {
              // Range of code points with same-norm16-value algorithmic decompositions.
              // They might have different non-zero FCD16 values.
              do {
                  uint16_t fcd16=impl.getFCD16(start);
                  if(fcd16>0xff) { set.add(start); }
              } while(++start<=end);
          } else {
              uint16_t fcd16=impl.getFCD16(start);
              if(fcd16>0xff) { set.add(start, end); }
          }
      }
  
  private:
      const Normalizer2Impl &impl;
      UnicodeSet &set;
  };
  
  struct PropertyStartsContext {
      PropertyStartsContext(const Normalizer2Impl &ni, const USetAdder *adder)
              : impl(ni), sa(adder) {}
  
      const Normalizer2Impl &impl;
      const USetAdder *sa;
  };
  
  }  // namespace
  
  U_CDECL_BEGIN
  
  static UBool U_CALLCONV
  enumLcccRange(const void *context, UChar32 start, UChar32 end, uint32_t value) {
      ((LcccContext *)context)->handleRange(start, end, (uint16_t)value);
      return TRUE;
  }
  
  static UBool U_CALLCONV
  enumNorm16PropertyStartsRange(const void *context, UChar32 start, UChar32 end, uint32_t value) {
      /* add the start code point to the USet */
      const PropertyStartsContext *ctx=(const PropertyStartsContext *)context;
      const USetAdder *sa=ctx->sa;
      sa->add(sa->set, start);
      if(start!=end && ctx->impl.isAlgorithmicNoNo((uint16_t)value)) {
          // Range of code points with same-norm16-value algorithmic decompositions.
          // They might have different non-zero FCD16 values.
          uint16_t prevFCD16=ctx->impl.getFCD16(start);
          while(++start<=end) {
              uint16_t fcd16=ctx->impl.getFCD16(start);
              if(fcd16!=prevFCD16) {
                  sa->add(sa->set, start);
                  prevFCD16=fcd16;
              }
          }
      }
      return TRUE;
  }
  
  static UBool U_CALLCONV
  enumPropertyStartsRange(const void *context, UChar32 start, UChar32 /*end*/, uint32_t /*value*/) {
      /* add the start code point to the USet */
      const USetAdder *sa=(const USetAdder *)context;
      sa->add(sa->set, start);
      return TRUE;
  }
  
  static uint32_t U_CALLCONV
  segmentStarterMapper(const void * /*context*/, uint32_t value) {
      return value&CANON_NOT_SEGMENT_STARTER;
  }
  
  U_CDECL_END
  
  void
  Normalizer2Impl::addLcccChars(UnicodeSet &set) const {
      /* add the start code point of each same-value range of each trie */
      LcccContext context(*this, set);
      utrie2_enum(normTrie, NULL, enumLcccRange, &context);
  }
  
  void
  Normalizer2Impl::addPropertyStarts(const USetAdder *sa, UErrorCode & /*errorCode*/) const {
      /* add the start code point of each same-value range of each trie */
      PropertyStartsContext context(*this, sa);
      utrie2_enum(normTrie, NULL, enumNorm16PropertyStartsRange, &context);
  
      /* add Hangul LV syllables and LV+1 because of skippables */
      for(UChar c=Hangul::HANGUL_BASE; c<Hangul::HANGUL_LIMIT; c+=Hangul::JAMO_T_COUNT) {
          sa->add(sa->set, c);
          sa->add(sa->set, c+1);
      }
      sa->add(sa->set, Hangul::HANGUL_LIMIT); /* add Hangul+1 to continue with other properties */
  }
  
  void
  Normalizer2Impl::addCanonIterPropertyStarts(const USetAdder *sa, UErrorCode &errorCode) const {
      /* add the start code point of each same-value range of the canonical iterator data trie */
      if(ensureCanonIterData(errorCode)) {
          // currently only used for the SEGMENT_STARTER property
          utrie2_enum(fCanonIterData->trie, segmentStarterMapper, enumPropertyStartsRange, sa);
      }
  }
  
  const UChar *
  Normalizer2Impl::copyLowPrefixFromNulTerminated(const UChar *src,
                                                  UChar32 minNeedDataCP,
                                                  ReorderingBuffer *buffer,
                                                  UErrorCode &errorCode) const {
      // Make some effort to support NUL-terminated strings reasonably.
      // Take the part of the fast quick check loop that does not look up
      // data and check the first part of the string.
      // After this prefix, determine the string length to simplify the rest
      // of the code.
      const UChar *prevSrc=src;
      UChar c;
      while((c=*src++)<minNeedDataCP && c!=0) {}
      // Back out the last character for full processing.
      // Copy this prefix.
      if(--src!=prevSrc) {
          if(buffer!=NULL) {
              buffer->appendZeroCC(prevSrc, src, errorCode);
          }
      }
      return src;
  }
  
  UnicodeString &
  Normalizer2Impl::decompose(const UnicodeString &src, UnicodeString &dest,
                             UErrorCode &errorCode) const {
      if(U_FAILURE(errorCode)) {
          dest.setToBogus();
          return dest;
      }
      const UChar *sArray=src.getBuffer();
      if(&dest==&src || sArray==NULL) {
          errorCode=U_ILLEGAL_ARGUMENT_ERROR;
          dest.setToBogus();
          return dest;
      }
      decompose(sArray, sArray+src.length(), dest, src.length(), errorCode);
      return dest;
  }
  
  void
  Normalizer2Impl::decompose(const UChar *src, const UChar *limit,
                             UnicodeString &dest,
                             int32_t destLengthEstimate,
                             UErrorCode &errorCode) const {
      if(destLengthEstimate<0 && limit!=NULL) {
          destLengthEstimate=(int32_t)(limit-src);
      }
      dest.remove();
      ReorderingBuffer buffer(*this, dest);
      if(buffer.init(destLengthEstimate, errorCode)) {
          decompose(src, limit, &buffer, errorCode);
      }
  }
  
  // Dual functionality:
  // buffer!=NULL: normalize
  // buffer==NULL: isNormalized/spanQuickCheckYes
  const UChar *
  Normalizer2Impl::decompose(const UChar *src, const UChar *limit,
                             ReorderingBuffer *buffer,
                             UErrorCode &errorCode) const {
      UChar32 minNoCP=minDecompNoCP;
      if(limit==NULL) {
          src=copyLowPrefixFromNulTerminated(src, minNoCP, buffer, errorCode);
          if(U_FAILURE(errorCode)) {
              return src;
          }
          limit=u_strchr(src, 0);
      }
  
      const UChar *prevSrc;
      UChar32 c=0;
      uint16_t norm16=0;
  
      // only for quick check
      const UChar *prevBoundary=src;
      uint8_t prevCC=0;
  
      for(;;) {
          // count code units below the minimum or with irrelevant data for the quick check
          for(prevSrc=src; src!=limit;) {
              if( (c=*src)<minNoCP ||
                  isMostDecompYesAndZeroCC(norm16=UTRIE2_GET16_FROM_U16_SINGLE_LEAD(normTrie, c))
              ) {
                  ++src;
              } else if(!U16_IS_SURROGATE(c)) {
                  break;
              } else {
                  UChar c2;
                  if(U16_IS_SURROGATE_LEAD(c)) {
                      if((src+1)!=limit && U16_IS_TRAIL(c2=src[1])) {
                          c=U16_GET_SUPPLEMENTARY(c, c2);
                      }
                  } else /* trail surrogate */ {
                      if(prevSrc<src && U16_IS_LEAD(c2=*(src-1))) {
                          --src;
                          c=U16_GET_SUPPLEMENTARY(c2, c);
                      }
                  }
                  if(isMostDecompYesAndZeroCC(norm16=getNorm16(c))) {
                      src+=U16_LENGTH(c);
                  } else {
                      break;
                  }
              }
          }
          // copy these code units all at once
          if(src!=prevSrc) {
              if(buffer!=NULL) {
                  if(!buffer->appendZeroCC(prevSrc, src, errorCode)) {
                      break;
                  }
              } else {
                  prevCC=0;
                  prevBoundary=src;
              }
          }
          if(src==limit) {
              break;
          }
  
          // Check one above-minimum, relevant code point.
          src+=U16_LENGTH(c);
          if(buffer!=NULL) {
              if(!decompose(c, norm16, *buffer, errorCode)) {
                  break;
              }
          } else {
              if(isDecompYes(norm16)) {
                  uint8_t cc=getCCFromYesOrMaybe(norm16);
                  if(prevCC<=cc || cc==0) {
                      prevCC=cc;
                      if(cc<=1) {
                          prevBoundary=src;
                      }
                      continue;
                  }
              }
              return prevBoundary;  // "no" or cc out of order
          }
      }
      return src;
  }
  
  // Decompose a short piece of text which is likely to contain characters that
  // fail the quick check loop and/or where the quick check loop's overhead
  // is unlikely to be amortized.
  // Called by the compose() and makeFCD() implementations.
  UBool Normalizer2Impl::decomposeShort(const UChar *src, const UChar *limit,
                                        ReorderingBuffer &buffer,
                                        UErrorCode &errorCode) const {
      while(src<limit) {
          UChar32 c;
          uint16_t norm16;
          UTRIE2_U16_NEXT16(normTrie, src, limit, c, norm16);
          if(!decompose(c, norm16, buffer, errorCode)) {
              return FALSE;
          }
      }
      return TRUE;
  }
  
  UBool Normalizer2Impl::decompose(UChar32 c, uint16_t norm16,
                                   ReorderingBuffer &buffer,
                                   UErrorCode &errorCode) const {
      // Only loops for 1:1 algorithmic mappings.
      for(;;) {
          // get the decomposition and the lead and trail cc's
          if(isDecompYes(norm16)) {
              // c does not decompose
              return buffer.append(c, getCCFromYesOrMaybe(norm16), errorCode);
          } else if(isHangul(norm16)) {
              // Hangul syllable: decompose algorithmically
              UChar jamos[3];
              return buffer.appendZeroCC(jamos, jamos+Hangul::decompose(c, jamos), errorCode);
          } else if(isDecompNoAlgorithmic(norm16)) {
              c=mapAlgorithmic(c, norm16);
              norm16=getNorm16(c);
          } else {
              // c decomposes, get everything from the variable-length extra data
              const uint16_t *mapping=getMapping(norm16);
              uint16_t firstUnit=*mapping;
              int32_t length=firstUnit&MAPPING_LENGTH_MASK;
              uint8_t leadCC, trailCC;
              trailCC=(uint8_t)(firstUnit>>8);
              if(firstUnit&MAPPING_HAS_CCC_LCCC_WORD) {
                  leadCC=(uint8_t)(*(mapping-1)>>8);
              } else {
                  leadCC=0;
              }
              return buffer.append((const UChar *)mapping+1, length, leadCC, trailCC, errorCode);
          }
      }
  }
  
  const UChar *
  Normalizer2Impl::getDecomposition(UChar32 c, UChar buffer[4], int32_t &length) const {
      const UChar *decomp=NULL;
      uint16_t norm16;
      for(;;) {
          if(c<minDecompNoCP || isDecompYes(norm16=getNorm16(c))) {
              // c does not decompose
              return decomp;
          } else if(isHangul(norm16)) {
              // Hangul syllable: decompose algorithmically
              length=Hangul::decompose(c, buffer);
              return buffer;
          } else if(isDecompNoAlgorithmic(norm16)) {
              c=mapAlgorithmic(c, norm16);
              decomp=buffer;
              length=0;
              U16_APPEND_UNSAFE(buffer, length, c);
          } else {
              // c decomposes, get everything from the variable-length extra data
              const uint16_t *mapping=getMapping(norm16);
              length=*mapping&MAPPING_LENGTH_MASK;
              return (const UChar *)mapping+1;
          }
      }
  }
  
  // The capacity of the buffer must be 30=MAPPING_LENGTH_MASK-1
  // so that a raw mapping fits that consists of one unit ("rm0")
  // plus all but the first two code units of the normal mapping.
  // The maximum length of a normal mapping is 31=MAPPING_LENGTH_MASK.
  const UChar *
  Normalizer2Impl::getRawDecomposition(UChar32 c, UChar buffer[30], int32_t &length) const {
      // We do not loop in this method because an algorithmic mapping itself
      // becomes a final result rather than having to be decomposed recursively.
      uint16_t norm16;
      if(c<minDecompNoCP || isDecompYes(norm16=getNorm16(c))) {
          // c does not decompose
          return NULL;
      } else if(isHangul(norm16)) {
          // Hangul syllable: decompose algorithmically
          Hangul::getRawDecomposition(c, buffer);
          length=2;
          return buffer;
      } else if(isDecompNoAlgorithmic(norm16)) {
          c=mapAlgorithmic(c, norm16);
          length=0;
          U16_APPEND_UNSAFE(buffer, length, c);
          return buffer;
      } else {
          // c decomposes, get everything from the variable-length extra data
          const uint16_t *mapping=getMapping(norm16);
          uint16_t firstUnit=*mapping;
          int32_t mLength=firstUnit&MAPPING_LENGTH_MASK;  // length of normal mapping
          if(firstUnit&MAPPING_HAS_RAW_MAPPING) {
              // Read the raw mapping from before the firstUnit and before the optional ccc/lccc word.
              // Bit 7=MAPPING_HAS_CCC_LCCC_WORD
              const uint16_t *rawMapping=mapping-((firstUnit>>7)&1)-1;
              uint16_t rm0=*rawMapping;
              if(rm0<=MAPPING_LENGTH_MASK) {
                  length=rm0;
                  return (const UChar *)rawMapping-rm0;
              } else {
                  // Copy the normal mapping and replace its first two code units with rm0.
                  buffer[0]=(UChar)rm0;
                  u_memcpy(buffer+1, (const UChar *)mapping+1+2, mLength-2);
                  length=mLength-1;
                  return buffer;
              }
          } else {
              length=mLength;
              return (const UChar *)mapping+1;
          }
      }
  }
  
  void Normalizer2Impl::decomposeAndAppend(const UChar *src, const UChar *limit,
                                           UBool doDecompose,
                                           UnicodeString &safeMiddle,
                                           ReorderingBuffer &buffer,
                                           UErrorCode &errorCode) const {
      buffer.copyReorderableSuffixTo(safeMiddle);
      if(doDecompose) {
          decompose(src, limit, &buffer, errorCode);
          return;
      }
      // Just merge the strings at the boundary.
      ForwardUTrie2StringIterator iter(normTrie, src, limit);
      uint8_t firstCC, prevCC, cc;
      firstCC=prevCC=cc=getCC(iter.next16());
      while(cc!=0) {
          prevCC=cc;
          cc=getCC(iter.next16());
      };
      if(limit==NULL) {  // appendZeroCC() needs limit!=NULL
          limit=u_strchr(iter.codePointStart, 0);
      }
  
      if (buffer.append(src, (int32_t)(iter.codePointStart-src), firstCC, prevCC, errorCode)) {
          buffer.appendZeroCC(iter.codePointStart, limit, errorCode);
      }
  }
  
  // Note: hasDecompBoundary() could be implemented as aliases to
  // hasFCDBoundaryBefore() and hasFCDBoundaryAfter()
  // at the cost of building the FCD trie for a decomposition normalizer.
  UBool Normalizer2Impl::hasDecompBoundary(UChar32 c, UBool before) const {
      for(;;) {
          if(c<minDecompNoCP) {
              return TRUE;
          }
          uint16_t norm16=getNorm16(c);
          if(isHangul(norm16) || isDecompYesAndZeroCC(norm16)) {
              return TRUE;
          } else if(norm16>MIN_NORMAL_MAYBE_YES) {
              return FALSE;  // ccc!=0
          } else if(isDecompNoAlgorithmic(norm16)) {
              c=mapAlgorithmic(c, norm16);
          } else {
              // c decomposes, get everything from the variable-length extra data
              const uint16_t *mapping=getMapping(norm16);
              uint16_t firstUnit=*mapping;
              if((firstUnit&MAPPING_LENGTH_MASK)==0) {
                  return FALSE;
              }
              if(!before) {
                  // decomp after-boundary: same as hasFCDBoundaryAfter(),
                  // fcd16<=1 || trailCC==0
                  if(firstUnit>0x1ff) {
                      return FALSE;  // trailCC>1
                  }
                  if(firstUnit<=0xff) {
                      return TRUE;  // trailCC==0
                  }
                  // if(trailCC==1) test leadCC==0, same as checking for before-boundary
              }
              // TRUE if leadCC==0 (hasFCDBoundaryBefore())
              return (firstUnit&MAPPING_HAS_CCC_LCCC_WORD)==0 || (*(mapping-1)&0xff00)==0;
          }
      }
  }
  
  /*
   * Finds the recomposition result for
   * a forward-combining "lead" character,
   * specified with a pointer to its compositions list,
   * and a backward-combining "trail" character.
   *
   * If the lead and trail characters combine, then this function returns
   * the following "compositeAndFwd" value:
   * Bits 21..1  composite character
   * Bit      0  set if the composite is a forward-combining starter
   * otherwise it returns -1.
   *
   * The compositions list has (trail, compositeAndFwd) pair entries,
   * encoded as either pairs or triples of 16-bit units.
   * The last entry has the high bit of its first unit set.
   *
   * The list is sorted by ascending trail characters (there are no duplicates).
   * A linear search is used.
   *
   * See normalizer2impl.h for a more detailed description
   * of the compositions list format.
   */
  int32_t Normalizer2Impl::combine(const uint16_t *list, UChar32 trail) {
      uint16_t key1, firstUnit;
      if(trail<COMP_1_TRAIL_LIMIT) {
          // trail character is 0..33FF
          // result entry may have 2 or 3 units
          key1=(uint16_t)(trail<<1);
          while(key1>(firstUnit=*list)) {
              list+=2+(firstUnit&COMP_1_TRIPLE);
          }
          if(key1==(firstUnit&COMP_1_TRAIL_MASK)) {
              if(firstUnit&COMP_1_TRIPLE) {
                  return ((int32_t)list[1]<<16)|list[2];
              } else {
                  return list[1];
              }
          }
      } else {
          // trail character is 3400..10FFFF
          // result entry has 3 units
          key1=(uint16_t)(COMP_1_TRAIL_LIMIT+
                          (((trail>>COMP_1_TRAIL_SHIFT))&
                            ~COMP_1_TRIPLE));
          uint16_t key2=(uint16_t)(trail<<COMP_2_TRAIL_SHIFT);
          uint16_t secondUnit;
          for(;;) {
              if(key1>(firstUnit=*list)) {
                  list+=2+(firstUnit&COMP_1_TRIPLE);
              } else if(key1==(firstUnit&COMP_1_TRAIL_MASK)) {
                  if(key2>(secondUnit=list[1])) {
                      if(firstUnit&COMP_1_LAST_TUPLE) {
                          break;
                      } else {
                          list+=3;
                      }
                  } else if(key2==(secondUnit&COMP_2_TRAIL_MASK)) {
                      return ((int32_t)(secondUnit&~COMP_2_TRAIL_MASK)<<16)|list[2];
                  } else {
                      break;
                  }
              } else {
                  break;
              }
          }
      }
      return -1;
  }
  
  /**
    * @param list some character's compositions list
    * @param set recursively receives the composites from these compositions
    */
  void Normalizer2Impl::addComposites(const uint16_t *list, UnicodeSet &set) const {
      uint16_t firstUnit;
      int32_t compositeAndFwd;
      do {
          firstUnit=*list;
          if((firstUnit&COMP_1_TRIPLE)==0) {
              compositeAndFwd=list[1];
              list+=2;
          } else {
              compositeAndFwd=(((int32_t)list[1]&~COMP_2_TRAIL_MASK)<<16)|list[2];
              list+=3;
          }
          UChar32 composite=compositeAndFwd>>1;
          if((compositeAndFwd&1)!=0) {
              addComposites(getCompositionsListForComposite(getNorm16(composite)), set);
          }
          set.add(composite);
      } while((firstUnit&COMP_1_LAST_TUPLE)==0);
  }
  
  /*
   * Recomposes the buffer text starting at recomposeStartIndex
   * (which is in NFD - decomposed and canonically ordered),
   * and truncates the buffer contents.
   *
   * Note that recomposition never lengthens the text:
   * Any character consists of either one or two code units;
   * a composition may contain at most one more code unit than the original starter,
   * while the combining mark that is removed has at least one code unit.
   */
  void Normalizer2Impl::recompose(ReorderingBuffer &buffer, int32_t recomposeStartIndex,
                                  UBool onlyContiguous) const {
      UChar *p=buffer.getStart()+recomposeStartIndex;
      UChar *limit=buffer.getLimit();
      if(p==limit) {
          return;
      }
  
      UChar *starter, *pRemove, *q, *r;
      const uint16_t *compositionsList;
      UChar32 c, compositeAndFwd;
      uint16_t norm16;
      uint8_t cc, prevCC;
      UBool starterIsSupplementary;
  
      // Some of the following variables are not used until we have a forward-combining starter
      // and are only initialized now to avoid compiler warnings.
      compositionsList=NULL;  // used as indicator for whether we have a forward-combining starter
      starter=NULL;
      starterIsSupplementary=FALSE;
      prevCC=0;
  
      for(;;) {
          UTRIE2_U16_NEXT16(normTrie, p, limit, c, norm16);
          cc=getCCFromYesOrMaybe(norm16);
          if( // this character combines backward and
              isMaybe(norm16) &&
              // we have seen a starter that combines forward and
              compositionsList!=NULL &&
              // the backward-combining character is not blocked
              (prevCC<cc || prevCC==0)
          ) {
              if(isJamoVT(norm16)) {
                  // c is a Jamo V/T, see if we can compose it with the previous character.
                  if(c<Hangul::JAMO_T_BASE) {
                      // c is a Jamo Vowel, compose with previous Jamo L and following Jamo T.
                      UChar prev=(UChar)(*starter-Hangul::JAMO_L_BASE);
                      if(prev<Hangul::JAMO_L_COUNT) {
                          pRemove=p-1;
                          UChar syllable=(UChar)
                              (Hangul::HANGUL_BASE+
                               (prev*Hangul::JAMO_V_COUNT+(c-Hangul::JAMO_V_BASE))*
                               Hangul::JAMO_T_COUNT);
                          UChar t;
                          if(p!=limit && (t=(UChar)(*p-Hangul::JAMO_T_BASE))<Hangul::JAMO_T_COUNT) {
                              ++p;
                              syllable+=t;  // The next character was a Jamo T.
                          }
                          *starter=syllable;
                          // remove the Jamo V/T
                          q=pRemove;
                          r=p;
                          while(r<limit) {
                              *q++=*r++;
                          }
                          limit=q;
                          p=pRemove;
                      }
                  }
                  /*
                   * No "else" for Jamo T:
                   * Since the input is in NFD, there are no Hangul LV syllables that
                   * a Jamo T could combine with.
                   * All Jamo Ts are combined above when handling Jamo Vs.
                   */
                  if(p==limit) {
                      break;
                  }
                  compositionsList=NULL;
                  continue;
              } else if((compositeAndFwd=combine(compositionsList, c))>=0) {
                  // The starter and the combining mark (c) do combine.
                  UChar32 composite=compositeAndFwd>>1;
  
                  // Replace the starter with the composite, remove the combining mark.
                  pRemove=p-U16_LENGTH(c);  // pRemove & p: start & limit of the combining mark
                  if(starterIsSupplementary) {
                      if(U_IS_SUPPLEMENTARY(composite)) {
                          // both are supplementary
                          starter[0]=U16_LEAD(composite);
                          starter[1]=U16_TRAIL(composite);
                      } else {
                          *starter=(UChar)composite;
                          // The composite is shorter than the starter,
                          // move the intermediate characters forward one.
                          starterIsSupplementary=FALSE;
                          q=starter+1;
                          r=q+1;
                          while(r<pRemove) {
                              *q++=*r++;
                          }
                          --pRemove;
                      }
                  } else if(U_IS_SUPPLEMENTARY(composite)) {
                      // The composite is longer than the starter,
                      // move the intermediate characters back one.
                      starterIsSupplementary=TRUE;
                      ++starter;  // temporarily increment for the loop boundary
                      q=pRemove;
                      r=++pRemove;
                      while(starter<q) {
                          *--r=*--q;
                      }
                      *starter=U16_TRAIL(composite);
                      *--starter=U16_LEAD(composite);  // undo the temporary increment
                  } else {
                      // both are on the BMP
                      *starter=(UChar)composite;
                  }
  
                  /* remove the combining mark by moving the following text over it */
                  if(pRemove<p) {
                      q=pRemove;
                      r=p;
                      while(r<limit) {
                          *q++=*r++;
                      }
                      limit=q;
                      p=pRemove;
                  }
                  // Keep prevCC because we removed the combining mark.
  
                  if(p==limit) {
                      break;
                  }
                  // Is the composite a starter that combines forward?
                  if(compositeAndFwd&1) {
                      compositionsList=
                          getCompositionsListForComposite(getNorm16(composite));
                  } else {
                      compositionsList=NULL;
                  }
  
                  // We combined; continue with looking for compositions.
                  continue;
              }
          }
  
          // no combination this time
          prevCC=cc;
          if(p==limit) {
              break;
          }
  
          // If c did not combine, then check if it is a starter.
          if(cc==0) {
              // Found a new starter.
              if((compositionsList=getCompositionsListForDecompYes(norm16))!=NULL) {
                  // It may combine with something, prepare for it.
                  if(U_IS_BMP(c)) {
                      starterIsSupplementary=FALSE;
                      starter=p-1;
                  } else {
                      starterIsSupplementary=TRUE;
                      starter=p-2;
                  }
              }
          } else if(onlyContiguous) {
              // FCC: no discontiguous compositions; any intervening character blocks.
              compositionsList=NULL;
          }
      }
      buffer.setReorderingLimit(limit);
  }
  
  UChar32
  Normalizer2Impl::composePair(UChar32 a, UChar32 b) const {
      uint16_t norm16=getNorm16(a);  // maps an out-of-range 'a' to inert norm16=0
      const uint16_t *list;
      if(isInert(norm16)) {
          return U_SENTINEL;
      } else if(norm16<minYesNoMappingsOnly) {
          if(isJamoL(norm16)) {
              b-=Hangul::JAMO_V_BASE;
              if(0<=b && b<Hangul::JAMO_V_COUNT) {
                  return
                      (Hangul::HANGUL_BASE+
                       ((a-Hangul::JAMO_L_BASE)*Hangul::JAMO_V_COUNT+b)*
                       Hangul::JAMO_T_COUNT);
              } else {
                  return U_SENTINEL;
              }
          } else if(isHangul(norm16)) {
              b-=Hangul::JAMO_T_BASE;
              if(Hangul::isHangulWithoutJamoT(a) && 0<b && b<Hangul::JAMO_T_COUNT) {  // not b==0!
                  return a+b;
              } else {
                  return U_SENTINEL;
              }
          } else {
              // 'a' has a compositions list in extraData
              list=extraData+norm16;
              if(norm16>minYesNo) {  // composite 'a' has both mapping & compositions list
                  list+=  // mapping pointer
                      1+  // +1 to skip the first unit with the mapping lenth
                      (*list&MAPPING_LENGTH_MASK);  // + mapping length
              }
          }
      } else if(norm16<minMaybeYes || MIN_NORMAL_MAYBE_YES<=norm16) {
          return U_SENTINEL;
      } else {
          list=maybeYesCompositions+norm16-minMaybeYes;
      }
      if(b<0 || 0x10ffff<b) {  // combine(list, b) requires a valid code point b
          return U_SENTINEL;
      }
  #if U_SIGNED_RIGHT_SHIFT_IS_ARITHMETIC
      return combine(list, b)>>1;
  #else
      int32_t compositeAndFwd=combine(list, b);
      return compositeAndFwd>=0 ? compositeAndFwd>>1 : U_SENTINEL;
  #endif
  }
  
  // Very similar to composeQuickCheck(): Make the same changes in both places if relevant.
  // doCompose: normalize
  // !doCompose: isNormalized (buffer must be empty and initialized)
  UBool
  Normalizer2Impl::compose(const UChar *src, const UChar *limit,
                           UBool onlyContiguous,
                           UBool doCompose,
                           ReorderingBuffer &buffer,
                           UErrorCode &errorCode) const {
      /*
       * prevBoundary points to the last character before the current one
       * that has a composition boundary before it with ccc==0 and quick check "yes".
       * Keeping track of prevBoundary saves us looking for a composition boundary
       * when we find a "no" or "maybe".
       *
       * When we back out from prevSrc back to prevBoundary,
       * then we also remove those same characters (which had been simply copied
       * or canonically-order-inserted) from the ReorderingBuffer.
       * Therefore, at all times, the [prevBoundary..prevSrc[ source units
       * must correspond 1:1 to destination units at the end of the destination buffer.
       */
      const UChar *prevBoundary=src;
      UChar32 minNoMaybeCP=minCompNoMaybeCP;
      if(limit==NULL) {
          src=copyLowPrefixFromNulTerminated(src, minNoMaybeCP,
                                             doCompose ? &buffer : NULL,
                                             errorCode);
          if(U_FAILURE(errorCode)) {
              return FALSE;
          }
          if(prevBoundary<src) {
              // Set prevBoundary to the last character in the prefix.
              prevBoundary=src-1;
          }
          limit=u_strchr(src, 0);
      }
  
      const UChar *prevSrc;
      UChar32 c=0;
      uint16_t norm16=0;
  
      // only for isNormalized
      uint8_t prevCC=0;
  
      for(;;) {
          // count code units below the minimum or with irrelevant data for the quick check
          for(prevSrc=src; src!=limit;) {
              if( (c=*src)<minNoMaybeCP ||
                  isCompYesAndZeroCC(norm16=UTRIE2_GET16_FROM_U16_SINGLE_LEAD(normTrie, c))
              ) {
                  ++src;
              } else if(!U16_IS_SURROGATE(c)) {
                  break;
              } else {
                  UChar c2;
                  if(U16_IS_SURROGATE_LEAD(c)) {
                      if((src+1)!=limit && U16_IS_TRAIL(c2=src[1])) {
                          c=U16_GET_SUPPLEMENTARY(c, c2);
                      }
                  } else /* trail surrogate */ {
                      if(prevSrc<src && U16_IS_LEAD(c2=*(src-1))) {
                          --src;
                          c=U16_GET_SUPPLEMENTARY(c2, c);
                      }
                  }
                  if(isCompYesAndZeroCC(norm16=getNorm16(c))) {
                      src+=U16_LENGTH(c);
                  } else {
                      break;
                  }
              }
          }
          // copy these code units all at once
          if(src!=prevSrc) {
              if(doCompose) {
                  if(!buffer.appendZeroCC(prevSrc, src, errorCode)) {
                      break;
                  }
              } else {
                  prevCC=0;
              }
              if(src==limit) {
                  break;
              }
              // Set prevBoundary to the last character in the quick check loop.
              prevBoundary=src-1;
              if( U16_IS_TRAIL(*prevBoundary) && prevSrc<prevBoundary &&
                  U16_IS_LEAD(*(prevBoundary-1))
              ) {
                  --prevBoundary;
              }
              // The start of the current character (c).
              prevSrc=src;
          } else if(src==limit) {
              break;
          }
  
          src+=U16_LENGTH(c);
          /*
           * isCompYesAndZeroCC(norm16) is false, that is, norm16>=minNoNo.
           * c is either a "noNo" (has a mapping) or a "maybeYes" (combines backward)
           * or has ccc!=0.
           * Check for Jamo V/T, then for regular characters.
           * c is not a Hangul syllable or Jamo L because those have "yes" properties.
           */
          if(isJamoVT(norm16) && prevBoundary!=prevSrc) {
              UChar prev=*(prevSrc-1);
              UBool needToDecompose=FALSE;
              if(c<Hangul::JAMO_T_BASE) {
                  // c is a Jamo Vowel, compose with previous Jamo L and following Jamo T.
                  prev=(UChar)(prev-Hangul::JAMO_L_BASE);
                  if(prev<Hangul::JAMO_L_COUNT) {
                      if(!doCompose) {
                          return FALSE;
                      }
                      UChar syllable=(UChar)
                          (Hangul::HANGUL_BASE+
                           (prev*Hangul::JAMO_V_COUNT+(c-Hangul::JAMO_V_BASE))*
                           Hangul::JAMO_T_COUNT);
                      UChar t;
                      if(src!=limit && (t=(UChar)(*src-Hangul::JAMO_T_BASE))<Hangul::JAMO_T_COUNT) {
                          ++src;
                          syllable+=t;  // The next character was a Jamo T.
                          prevBoundary=src;
                          buffer.setLastChar(syllable);
                          continue;
                      }
                      // If we see L+V+x where x!=T then we drop to the slow path,
                      // decompose and recompose.
                      // This is to deal with NFKC finding normal L and V but a
                      // compatibility variant of a T. We need to either fully compose that
                      // combination here (which would complicate the code and may not work
                      // with strange custom data) or use the slow path -- or else our replacing
                      // two input characters (L+V) with one output character (LV syllable)
                      // would violate the invariant that [prevBoundary..prevSrc[ has the same
                      // length as what we appended to the buffer since prevBoundary.
                      needToDecompose=TRUE;
                  }
              } else if(Hangul::isHangulWithoutJamoT(prev)) {
                  // c is a Jamo Trailing consonant,
                  // compose with previous Hangul LV that does not contain a Jamo T.
                  if(!doCompose) {
                      return FALSE;
                  }
                  buffer.setLastChar((UChar)(prev+c-Hangul::JAMO_T_BASE));
                  prevBoundary=src;
                  continue;
              }
              if(!needToDecompose) {
                  // The Jamo V/T did not compose into a Hangul syllable.
                  if(doCompose) {
                      if(!buffer.appendBMP((UChar)c, 0, errorCode)) {
                          break;
                      }
                  } else {
                      prevCC=0;
                  }
                  continue;
              }
          }
          /*
           * Source buffer pointers:
           *
           *  all done      quick check   current char  not yet
           *                "yes" but     (c)           processed
           *                may combine
           *                forward
           * [-------------[-------------[-------------[-------------[
           * |             |             |             |             |
           * orig. src     prevBoundary  prevSrc       src           limit
           *
           *
           * Destination buffer pointers inside the ReorderingBuffer:
           *
           *  all done      might take    not filled yet
           *                characters for
           *                reordering
           * [-------------[-------------[-------------[
           * |             |             |             |
           * start         reorderStart  limit         |
           *                             +remainingCap.+
           */
          if(norm16>=MIN_YES_YES_WITH_CC) {
              uint8_t cc=(uint8_t)norm16;  // cc!=0
              if( onlyContiguous &&  // FCC
                  (doCompose ? buffer.getLastCC() : prevCC)==0 &&
                  prevBoundary<prevSrc &&
                  // buffer.getLastCC()==0 && prevBoundary<prevSrc tell us that
                  // [prevBoundary..prevSrc[ (which is exactly one character under these conditions)
                  // passed the quick check "yes && ccc==0" test.
                  // Check whether the last character was a "yesYes" or a "yesNo".
                  // If a "yesNo", then we get its trailing ccc from its
                  // mapping and check for canonical order.
                  // All other cases are ok.
                  getTrailCCFromCompYesAndZeroCC(prevBoundary, prevSrc)>cc
              ) {
                  // Fails FCD test, need to decompose and contiguously recompose.
                  if(!doCompose) {
                      return FALSE;
                  }
              } else if(doCompose) {
                  if(!buffer.append(c, cc, errorCode)) {
                      break;
                  }
                  continue;
              } else if(prevCC<=cc) {
                  prevCC=cc;
                  continue;
              } else {
                  return FALSE;
              }
          } else if(!doCompose && !isMaybeOrNonZeroCC(norm16)) {
              return FALSE;
          }
  
          /*
           * Find appropriate boundaries around this character,
           * decompose the source text from between the boundaries,
           * and recompose it.
           *
           * We may need to remove the last few characters from the ReorderingBuffer
           * to account for source text that was copied or appended
           * but needs to take part in the recomposition.
           */
  
          /*
           * Find the last composition boundary in [prevBoundary..src[.
           * It is either the decomposition of the current character (at prevSrc),
           * or prevBoundary.
           */
          if(hasCompBoundaryBefore(c, norm16)) {
              prevBoundary=prevSrc;
          } else if(doCompose) {
              buffer.removeSuffix((int32_t)(prevSrc-prevBoundary));
          }
  
          // Find the next composition boundary in [src..limit[ -
          // modifies src to point to the next starter.
          src=(UChar *)findNextCompBoundary(src, limit);
  
          // Decompose [prevBoundary..src[ into the buffer and then recompose that part of it.
          int32_t recomposeStartIndex=buffer.length();
          if(!decomposeShort(prevBoundary, src, buffer, errorCode)) {
              break;
          }
          recompose(buffer, recomposeStartIndex, onlyContiguous);
          if(!doCompose) {
              if(!buffer.equals(prevBoundary, src)) {
                  return FALSE;
              }
              buffer.remove();
              prevCC=0;
          }
  
          // Move to the next starter. We never need to look back before this point again.
          prevBoundary=src;
      }
      return TRUE;
  }
  
  // Very similar to compose(): Make the same changes in both places if relevant.
  // pQCResult==NULL: spanQuickCheckYes
  // pQCResult!=NULL: quickCheck (*pQCResult must be UNORM_YES)
  const UChar *
  Normalizer2Impl::composeQuickCheck(const UChar *src, const UChar *limit,
                                     UBool onlyContiguous,
                                     UNormalizationCheckResult *pQCResult) const {
      /*
       * prevBoundary points to the last character before the current one
       * that has a composition boundary before it with ccc==0 and quick check "yes".
       */
      const UChar *prevBoundary=src;
      UChar32 minNoMaybeCP=minCompNoMaybeCP;
      if(limit==NULL) {
          UErrorCode errorCode=U_ZERO_ERROR;
          src=copyLowPrefixFromNulTerminated(src, minNoMaybeCP, NULL, errorCode);
          if(prevBoundary<src) {
              // Set prevBoundary to the last character in the prefix.
              prevBoundary=src-1;
          }
          limit=u_strchr(src, 0);
      }
  
      const UChar *prevSrc;
      UChar32 c=0;
      uint16_t norm16=0;
      uint8_t prevCC=0;
  
      for(;;) {
          // count code units below the minimum or with irrelevant data for the quick check
          for(prevSrc=src;;) {
              if(src==limit) {
                  return src;
              }
              if( (c=*src)<minNoMaybeCP ||
                  isCompYesAndZeroCC(norm16=UTRIE2_GET16_FROM_U16_SINGLE_LEAD(normTrie, c))
              ) {
                  ++src;
              } else if(!U16_IS_SURROGATE(c)) {
                  break;
              } else {
                  UChar c2;
                  if(U16_IS_SURROGATE_LEAD(c)) {
                      if((src+1)!=limit && U16_IS_TRAIL(c2=src[1])) {
                          c=U16_GET_SUPPLEMENTARY(c, c2);
                      }
                  } else /* trail surrogate */ {
                      if(prevSrc<src && U16_IS_LEAD(c2=*(src-1))) {
                          --src;
                          c=U16_GET_SUPPLEMENTARY(c2, c);
                      }
                  }
                  if(isCompYesAndZeroCC(norm16=getNorm16(c))) {
                      src+=U16_LENGTH(c);
                  } else {
                      break;
                  }
              }
          }
          if(src!=prevSrc) {
              // Set prevBoundary to the last character in the quick check loop.
              prevBoundary=src-1;
              if( U16_IS_TRAIL(*prevBoundary) && prevSrc<prevBoundary &&
                  U16_IS_LEAD(*(prevBoundary-1))
              ) {
                  --prevBoundary;
              }
              prevCC=0;
              // The start of the current character (c).
              prevSrc=src;
          }
  
          src+=U16_LENGTH(c);
          /*
           * isCompYesAndZeroCC(norm16) is false, that is, norm16>=minNoNo.
           * c is either a "noNo" (has a mapping) or a "maybeYes" (combines backward)
           * or has ccc!=0.
           */
          if(isMaybeOrNonZeroCC(norm16)) {
              uint8_t cc=getCCFromYesOrMaybe(norm16);
              if( onlyContiguous &&  // FCC
                  cc!=0 &&
                  prevCC==0 &&
                  prevBoundary<prevSrc &&
                  // prevCC==0 && prevBoundary<prevSrc tell us that
                  // [prevBoundary..prevSrc[ (which is exactly one character under these conditions)
                  // passed the quick check "yes && ccc==0" test.
                  // Check whether the last character was a "yesYes" or a "yesNo".
                  // If a "yesNo", then we get its trailing ccc from its
                  // mapping and check for canonical order.
                  // All other cases are ok.
                  getTrailCCFromCompYesAndZeroCC(prevBoundary, prevSrc)>cc
              ) {
                  // Fails FCD test.
              } else if(prevCC<=cc || cc==0) {
                  prevCC=cc;
                  if(norm16<MIN_YES_YES_WITH_CC) {
                      if(pQCResult!=NULL) {
                          *pQCResult=UNORM_MAYBE;
                      } else {
                          return prevBoundary;
                      }
                  }
                  continue;
              }
          }
          if(pQCResult!=NULL) {
              *pQCResult=UNORM_NO;
          }
          return prevBoundary;
      }
  }
  
  void Normalizer2Impl::composeAndAppend(const UChar *src, const UChar *limit,
                                         UBool doCompose,
                                         UBool onlyContiguous,
                                         UnicodeString &safeMiddle,
                                         ReorderingBuffer &buffer,
                                         UErrorCode &errorCode) const {
      if(!buffer.isEmpty()) {
          const UChar *firstStarterInSrc=findNextCompBoundary(src, limit);
          if(src!=firstStarterInSrc) {
              const UChar *lastStarterInDest=findPreviousCompBoundary(buffer.getStart(),
                                                                      buffer.getLimit());
              int32_t destSuffixLength=(int32_t)(buffer.getLimit()-lastStarterInDest);
              UnicodeString middle(lastStarterInDest, destSuffixLength);
              buffer.removeSuffix(destSuffixLength);
              safeMiddle=middle;
              middle.append(src, (int32_t)(firstStarterInSrc-src));
              const UChar *middleStart=middle.getBuffer();
              compose(middleStart, middleStart+middle.length(), onlyContiguous,
                      TRUE, buffer, errorCode);
              if(U_FAILURE(errorCode)) {
                  return;
              }
              src=firstStarterInSrc;
          }
      }
      if(doCompose) {
          compose(src, limit, onlyContiguous, TRUE, buffer, errorCode);
      } else {
          if(limit==NULL) {  // appendZeroCC() needs limit!=NULL
              limit=u_strchr(src, 0);
          }
          buffer.appendZeroCC(src, limit, errorCode);
      }
  }
  
  /**
   * Does c have a composition boundary before it?
   * True if its decomposition begins with a character that has
   * ccc=0 && NFC_QC=Yes (isCompYesAndZeroCC()).
   * As a shortcut, this is true if c itself has ccc=0 && NFC_QC=Yes
   * (isCompYesAndZeroCC()) so we need not decompose.
   */
  UBool Normalizer2Impl::hasCompBoundaryBefore(UChar32 c, uint16_t norm16) const {
      for(;;) {
          if(isCompYesAndZeroCC(norm16)) {
              return TRUE;
          } else if(isMaybeOrNonZeroCC(norm16)) {
              return FALSE;
          } else if(isDecompNoAlgorithmic(norm16)) {
              c=mapAlgorithmic(c, norm16);
              norm16=getNorm16(c);
          } else {
              // c decomposes, get everything from the variable-length extra data
              const uint16_t *mapping=getMapping(norm16);
              uint16_t firstUnit=*mapping;
              if((firstUnit&MAPPING_LENGTH_MASK)==0) {
                  return FALSE;
              }
              if((firstUnit&MAPPING_HAS_CCC_LCCC_WORD) && (*(mapping-1)&0xff00)) {
                  return FALSE;  // non-zero leadCC
              }
              int32_t i=1;  // skip over the firstUnit
              UChar32 c;
              U16_NEXT_UNSAFE(mapping, i, c);
              return isCompYesAndZeroCC(getNorm16(c));
          }
      }
  }
  
  UBool Normalizer2Impl::hasCompBoundaryAfter(UChar32 c, UBool onlyContiguous, UBool testInert) const {
      for(;;) {
          uint16_t norm16=getNorm16(c);
          if(isInert(norm16)) {
              return TRUE;
          } else if(norm16<=minYesNo) {
              // Hangul: norm16==minYesNo
              // Hangul LVT has a boundary after it.
              // Hangul LV and non-inert yesYes characters combine forward.
              return isHangul(norm16) && !Hangul::isHangulWithoutJamoT((UChar)c);
          } else if(norm16>= (testInert ? minNoNo : minMaybeYes)) {
              return FALSE;
          } else if(isDecompNoAlgorithmic(norm16)) {
              c=mapAlgorithmic(c, norm16);
          } else {
              // c decomposes, get everything from the variable-length extra data.
              // If testInert, then c must be a yesNo character which has lccc=0,
              // otherwise it could be a noNo.
              const uint16_t *mapping=getMapping(norm16);
              uint16_t firstUnit=*mapping;
              // TRUE if
              //   not MAPPING_NO_COMP_BOUNDARY_AFTER
              //     (which is set if
              //       c is not deleted, and
              //       it and its decomposition do not combine forward, and it has a starter)
              //   and if FCC then trailCC<=1
              return
                  (firstUnit&MAPPING_NO_COMP_BOUNDARY_AFTER)==0 &&
                  (!onlyContiguous || firstUnit<=0x1ff);
          }
      }
  }
  
  const UChar *Normalizer2Impl::findPreviousCompBoundary(const UChar *start, const UChar *p) const {
      BackwardUTrie2StringIterator iter(normTrie, start, p);
      uint16_t norm16;
      do {
          norm16=iter.previous16();
      } while(!hasCompBoundaryBefore(iter.codePoint, norm16));
      // We could also test hasCompBoundaryAfter() and return iter.codePointLimit,
      // but that's probably not worth the extra cost.
      return iter.codePointStart;
  }
  
  const UChar *Normalizer2Impl::findNextCompBoundary(const UChar *p, const UChar *limit) const {
      ForwardUTrie2StringIterator iter(normTrie, p, limit);
      uint16_t norm16;
      do {
          norm16=iter.next16();
      } while(!hasCompBoundaryBefore(iter.codePoint, norm16));
      return iter.codePointStart;
  }
  
  // Note: normalizer2impl.cpp r30982 (2011-nov-27)
  // still had getFCDTrie() which built and cached an FCD trie.
  // That provided faster access to FCD data than getFCD16FromNormData()
  // but required synchronization and consumed some 10kB of heap memory
  // in any process that uses FCD (e.g., via collation).
  // tccc180[] and smallFCD[] are intended to help with any loss of performance,
  // at least for Latin & CJK.
  
  // Gets the FCD value from the regular normalization data.
  uint16_t Normalizer2Impl::getFCD16FromNormData(UChar32 c) const {
      // Only loops for 1:1 algorithmic mappings.
      for(;;) {
          uint16_t norm16=getNorm16(c);
          if(norm16<=minYesNo) {
              // no decomposition or Hangul syllable, all zeros
              return 0;
          } else if(norm16>=MIN_NORMAL_MAYBE_YES) {
              // combining mark
              norm16&=0xff;
              return norm16|(norm16<<8);
          } else if(norm16>=minMaybeYes) {
              return 0;
          } else if(isDecompNoAlgorithmic(norm16)) {
              c=mapAlgorithmic(c, norm16);
          } else {
              // c decomposes, get everything from the variable-length extra data
              const uint16_t *mapping=getMapping(norm16);
              uint16_t firstUnit=*mapping;
              if((firstUnit&MAPPING_LENGTH_MASK)==0) {
                  // A character that is deleted (maps to an empty string) must
                  // get the worst-case lccc and tccc values because arbitrary
                  // characters on both sides will become adjacent.
                  return 0x1ff;
              } else {
                  norm16=firstUnit>>8;  // tccc
                  if(firstUnit&MAPPING_HAS_CCC_LCCC_WORD) {
                      norm16|=*(mapping-1)&0xff00;  // lccc
                  }
                  return norm16;
              }
          }
      }
  }
  
  // Dual functionality:
  // buffer!=NULL: normalize
  // buffer==NULL: isNormalized/quickCheck/spanQuickCheckYes
  const UChar *
  Normalizer2Impl::makeFCD(const UChar *src, const UChar *limit,
                           ReorderingBuffer *buffer,
                           UErrorCode &errorCode) const {
      // Tracks the last FCD-safe boundary, before lccc=0 or after properly-ordered tccc<=1.
      // Similar to the prevBoundary in the compose() implementation.
      const UChar *prevBoundary=src;
      int32_t prevFCD16=0;
      if(limit==NULL) {
          src=copyLowPrefixFromNulTerminated(src, MIN_CCC_LCCC_CP, buffer, errorCode);
          if(U_FAILURE(errorCode)) {
              return src;
          }
          if(prevBoundary<src) {
              prevBoundary=src;
              // We know that the previous character's lccc==0.
              // Fetching the fcd16 value was deferred for this below-U+0300 code point.
              prevFCD16=getFCD16(*(src-1));
              if(prevFCD16>1) {
                  --prevBoundary;
              }
          }
          limit=u_strchr(src, 0);
      }
  
      // Note: In this function we use buffer->appendZeroCC() because we track
      // the lead and trail combining classes here, rather than leaving it to
      // the ReorderingBuffer.
      // The exception is the call to decomposeShort() which uses the buffer
      // in the normal way.
  
      const UChar *prevSrc;
      UChar32 c=0;
      uint16_t fcd16=0;
  
      for(;;) {
          // count code units with lccc==0
          for(prevSrc=src; src!=limit;) {
              if((c=*src)<MIN_CCC_LCCC_CP) {
                  prevFCD16=~c;
                  ++src;
              } else if(!singleLeadMightHaveNonZeroFCD16(c)) {
                  prevFCD16=0;
                  ++src;
              } else {
                  if(U16_IS_SURROGATE(c)) {
                      UChar c2;
                      if(U16_IS_SURROGATE_LEAD(c)) {
                          if((src+1)!=limit && U16_IS_TRAIL(c2=src[1])) {
                              c=U16_GET_SUPPLEMENTARY(c, c2);
                          }
                      } else /* trail surrogate */ {
                          if(prevSrc<src && U16_IS_LEAD(c2=*(src-1))) {
                              --src;
                              c=U16_GET_SUPPLEMENTARY(c2, c);
                          }
                      }
                  }
                  if((fcd16=getFCD16FromNormData(c))<=0xff) {
                      prevFCD16=fcd16;
                      src+=U16_LENGTH(c);
                  } else {
                      break;
                  }
              }
          }
          // copy these code units all at once
          if(src!=prevSrc) {
              if(buffer!=NULL && !buffer->appendZeroCC(prevSrc, src, errorCode)) {
                  break;
              }
              if(src==limit) {
                  break;
              }
              prevBoundary=src;
              // We know that the previous character's lccc==0.
              if(prevFCD16<0) {
                  // Fetching the fcd16 value was deferred for this below-U+0300 code point.
                  UChar32 prev=~prevFCD16;
                  prevFCD16= prev<0x180 ? tccc180[prev] : getFCD16FromNormData(prev);
                  if(prevFCD16>1) {
                      --prevBoundary;
                  }
              } else {
                  const UChar *p=src-1;
                  if(U16_IS_TRAIL(*p) && prevSrc<p && U16_IS_LEAD(*(p-1))) {
                      --p;
                      // Need to fetch the previous character's FCD value because
                      // prevFCD16 was just for the trail surrogate code point.
                      prevFCD16=getFCD16FromNormData(U16_GET_SUPPLEMENTARY(p[0], p[1]));
                      // Still known to have lccc==0 because its lead surrogate unit had lccc==0.
                  }
                  if(prevFCD16>1) {
                      prevBoundary=p;
                  }
              }
              // The start of the current character (c).
              prevSrc=src;
          } else if(src==limit) {
              break;
          }
  
          src+=U16_LENGTH(c);
          // The current character (c) at [prevSrc..src[ has a non-zero lead combining class.
          // Check for proper order, and decompose locally if necessary.
          if((prevFCD16&0xff)<=(fcd16>>8)) {
              // proper order: prev tccc <= current lccc
              if((fcd16&0xff)<=1) {
                  prevBoundary=src;
              }
              if(buffer!=NULL && !buffer->appendZeroCC(c, errorCode)) {
                  break;
              }
              prevFCD16=fcd16;
              continue;
          } else if(buffer==NULL) {
              return prevBoundary;  // quick check "no"
          } else {
              /*
               * Back out the part of the source that we copied or appended
               * already but is now going to be decomposed.
               * prevSrc is set to after what was copied/appended.
               */
              buffer->removeSuffix((int32_t)(prevSrc-prevBoundary));
              /*
               * Find the part of the source that needs to be decomposed,
               * up to the next safe boundary.
               */
              src=findNextFCDBoundary(src, limit);
              /*
               * The source text does not fulfill the conditions for FCD.
               * Decompose and reorder a limited piece of the text.
               */
              if(!decomposeShort(prevBoundary, src, *buffer, errorCode)) {
                  break;
              }
              prevBoundary=src;
              prevFCD16=0;
          }
      }
      return src;
  }
  
  void Normalizer2Impl::makeFCDAndAppend(const UChar *src, const UChar *limit,
                                         UBool doMakeFCD,
                                         UnicodeString &safeMiddle,
                                         ReorderingBuffer &buffer,
                                         UErrorCode &errorCode) const {
      if(!buffer.isEmpty()) {
          const UChar *firstBoundaryInSrc=findNextFCDBoundary(src, limit);
          if(src!=firstBoundaryInSrc) {
              const UChar *lastBoundaryInDest=findPreviousFCDBoundary(buffer.getStart(),
                                                                      buffer.getLimit());
              int32_t destSuffixLength=(int32_t)(buffer.getLimit()-lastBoundaryInDest);
              UnicodeString middle(lastBoundaryInDest, destSuffixLength);
              buffer.removeSuffix(destSuffixLength);
              safeMiddle=middle;
              middle.append(src, (int32_t)(firstBoundaryInSrc-src));
              const UChar *middleStart=middle.getBuffer();
              makeFCD(middleStart, middleStart+middle.length(), &buffer, errorCode);
              if(U_FAILURE(errorCode)) {
                  return;
              }
              src=firstBoundaryInSrc;
          }
      }
      if(doMakeFCD) {
          makeFCD(src, limit, &buffer, errorCode);
      } else {
          if(limit==NULL) {  // appendZeroCC() needs limit!=NULL
              limit=u_strchr(src, 0);
          }
          buffer.appendZeroCC(src, limit, errorCode);
      }
  }
  
  const UChar *Normalizer2Impl::findPreviousFCDBoundary(const UChar *start, const UChar *p) const {
      while(start<p && previousFCD16(start, p)>0xff) {}
      return p;
  }
  
  const UChar *Normalizer2Impl::findNextFCDBoundary(const UChar *p, const UChar *limit) const {
      while(p<limit) {
          const UChar *codePointStart=p;
          if(nextFCD16(p, limit)<=0xff) {
              return codePointStart;
          }
      }
      return p;
  }
  
  // CanonicalIterator data -------------------------------------------------- ***
  
  CanonIterData::CanonIterData(UErrorCode &errorCode) :
          trie(utrie2_open(0, 0, &errorCode)),
          canonStartSets(uprv_deleteUObject, NULL, errorCode) {}
  
  CanonIterData::~CanonIterData() {
      utrie2_close(trie);
  }
  
  void CanonIterData::addToStartSet(UChar32 origin, UChar32 decompLead, UErrorCode &errorCode) {
      uint32_t canonValue=utrie2_get32(trie, decompLead);
      if((canonValue&(CANON_HAS_SET|CANON_VALUE_MASK))==0 && origin!=0) {
          // origin is the first character whose decomposition starts with
          // the character for which we are setting the value.
          utrie2_set32(trie, decompLead, canonValue|origin, &errorCode);
      } else {
          // origin is not the first character, or it is U+0000.
          UnicodeSet *set;
          if((canonValue&CANON_HAS_SET)==0) {
              set=new UnicodeSet;
              if(set==NULL) {
                  errorCode=U_MEMORY_ALLOCATION_ERROR;
                  return;
              }
              UChar32 firstOrigin=(UChar32)(canonValue&CANON_VALUE_MASK);
              canonValue=(canonValue&~CANON_VALUE_MASK)|CANON_HAS_SET|(uint32_t)canonStartSets.size();
              utrie2_set32(trie, decompLead, canonValue, &errorCode);
              canonStartSets.addElement(set, errorCode);
              if(firstOrigin!=0) {
                  set->add(firstOrigin);
              }
          } else {
              set=(UnicodeSet *)canonStartSets[(int32_t)(canonValue&CANON_VALUE_MASK)];
          }
          set->add(origin);
      }
  }
  
  U_CDECL_BEGIN
  
  // Call Normalizer2Impl::makeCanonIterDataFromNorm16() for a range of same-norm16 characters.
  //     context: the Normalizer2Impl
  static UBool U_CALLCONV
  enumCIDRangeHandler(const void *context, UChar32 start, UChar32 end, uint32_t value) {
      UErrorCode errorCode = U_ZERO_ERROR;
      if (value != 0) {
          Normalizer2Impl *impl = (Normalizer2Impl *)context;
          impl->makeCanonIterDataFromNorm16(
              start, end, (uint16_t)value, *impl->fCanonIterData, errorCode);
      }
      return U_SUCCESS(errorCode);
  }
  
  
  
  // UInitOnce instantiation function for CanonIterData
  
  static void U_CALLCONV 
  initCanonIterData(Normalizer2Impl *impl, UErrorCode &errorCode) {
      U_ASSERT(impl->fCanonIterData == NULL);
      impl->fCanonIterData = new CanonIterData(errorCode);
      if (impl->fCanonIterData == NULL) {
          errorCode=U_MEMORY_ALLOCATION_ERROR;
      }
      if (U_SUCCESS(errorCode)) {
          utrie2_enum(impl->getNormTrie(), NULL, enumCIDRangeHandler, impl);
          utrie2_freeze(impl->fCanonIterData->trie, UTRIE2_32_VALUE_BITS, &errorCode);
      }
      if (U_FAILURE(errorCode)) {
          delete impl->fCanonIterData;
          impl->fCanonIterData = NULL;
      }
  }
  
  U_CDECL_END
  
  void Normalizer2Impl::makeCanonIterDataFromNorm16(UChar32 start, UChar32 end, uint16_t norm16,
                                                    CanonIterData &newData,
                                                    UErrorCode &errorCode) const {
      if(norm16==0 || (minYesNo<=norm16 && norm16<minNoNo)) {
          // Inert, or 2-way mapping (including Hangul syllable).
          // We do not write a canonStartSet for any yesNo character.
          // Composites from 2-way mappings are added at runtime from the
          // starter's compositions list, and the other characters in
          // 2-way mappings get CANON_NOT_SEGMENT_STARTER set because they are
          // "maybe" characters.
          return;
      }
      for(UChar32 c=start; c<=end; ++c) {
          uint32_t oldValue=utrie2_get32(newData.trie, c);
          uint32_t newValue=oldValue;
          if(norm16>=minMaybeYes) {
              // not a segment starter if it occurs in a decomposition or has cc!=0
              newValue|=CANON_NOT_SEGMENT_STARTER;
              if(norm16<MIN_NORMAL_MAYBE_YES) {
                  newValue|=CANON_HAS_COMPOSITIONS;
              }
          } else if(norm16<minYesNo) {
              newValue|=CANON_HAS_COMPOSITIONS;
          } else {
              // c has a one-way decomposition
              UChar32 c2=c;
              uint16_t norm16_2=norm16;
              while(limitNoNo<=norm16_2 && norm16_2<minMaybeYes) {
                  c2=mapAlgorithmic(c2, norm16_2);
                  norm16_2=getNorm16(c2);
              }
              if(minYesNo<=norm16_2 && norm16_2<limitNoNo) {
                  // c decomposes, get everything from the variable-length extra data
                  const uint16_t *mapping=getMapping(norm16_2);
                  uint16_t firstUnit=*mapping;
                  int32_t length=firstUnit&MAPPING_LENGTH_MASK;
                  if((firstUnit&MAPPING_HAS_CCC_LCCC_WORD)!=0) {
                      if(c==c2 && (*(mapping-1)&0xff)!=0) {
                          newValue|=CANON_NOT_SEGMENT_STARTER;  // original c has cc!=0
                      }
                  }
                  // Skip empty mappings (no characters in the decomposition).
                  if(length!=0) {
                      ++mapping;  // skip over the firstUnit
                      // add c to first code point's start set
                      int32_t i=0;
                      U16_NEXT_UNSAFE(mapping, i, c2);
                      newData.addToStartSet(c, c2, errorCode);
                      // Set CANON_NOT_SEGMENT_STARTER for each remaining code point of a
                      // one-way mapping. A 2-way mapping is possible here after
                      // intermediate algorithmic mapping.
                      if(norm16_2>=minNoNo) {
                          while(i<length) {
                              U16_NEXT_UNSAFE(mapping, i, c2);
                              uint32_t c2Value=utrie2_get32(newData.trie, c2);
                              if((c2Value&CANON_NOT_SEGMENT_STARTER)==0) {
                                  utrie2_set32(newData.trie, c2, c2Value|CANON_NOT_SEGMENT_STARTER,
                                               &errorCode);
                              }
                          }
                      }
                  }
              } else {
                  // c decomposed to c2 algorithmically; c has cc==0
                  newData.addToStartSet(c, c2, errorCode);
              }
          }
          if(newValue!=oldValue) {
              utrie2_set32(newData.trie, c, newValue, &errorCode);
          }
      }
  }
  
  UBool Normalizer2Impl::ensureCanonIterData(UErrorCode &errorCode) const {
      // Logically const: Synchronized instantiation.
      Normalizer2Impl *me=const_cast<Normalizer2Impl *>(this);
      umtx_initOnce(me->fCanonIterDataInitOnce, &initCanonIterData, me, errorCode);
      return U_SUCCESS(errorCode);
  }
  
  int32_t Normalizer2Impl::getCanonValue(UChar32 c) const {
      return (int32_t)utrie2_get32(fCanonIterData->trie, c);
  }
  
  const UnicodeSet &Normalizer2Impl::getCanonStartSet(int32_t n) const {
      return *(const UnicodeSet *)fCanonIterData->canonStartSets[n];
  }
  
  UBool Normalizer2Impl::isCanonSegmentStarter(UChar32 c) const {
      return getCanonValue(c)>=0;
  }
  
  UBool Normalizer2Impl::getCanonStartSet(UChar32 c, UnicodeSet &set) const {
      int32_t canonValue=getCanonValue(c)&~CANON_NOT_SEGMENT_STARTER;
      if(canonValue==0) {
          return FALSE;
      }
      set.clear();
      int32_t value=canonValue&CANON_VALUE_MASK;
      if((canonValue&CANON_HAS_SET)!=0) {
          set.addAll(getCanonStartSet(value));
      } else if(value!=0) {
          set.add(value);
      }
      if((canonValue&CANON_HAS_COMPOSITIONS)!=0) {
          uint16_t norm16=getNorm16(c);
          if(norm16==JAMO_L) {
              UChar32 syllable=
                  (UChar32)(Hangul::HANGUL_BASE+(c-Hangul::JAMO_L_BASE)*Hangul::JAMO_VT_COUNT);
              set.add(syllable, syllable+Hangul::JAMO_VT_COUNT-1);
          } else {
              addComposites(getCompositionsList(norm16), set);
          }
      }
      return TRUE;
  }
  
  U_NAMESPACE_END
  
  // Normalizer2 data swapping ----------------------------------------------- ***
  
  U_NAMESPACE_USE
  
  U_CAPI int32_t U_EXPORT2
  unorm2_swap(const UDataSwapper *ds,
              const void *inData, int32_t length, void *outData,
              UErrorCode *pErrorCode) {
      const UDataInfo *pInfo;
      int32_t headerSize;
  
      const uint8_t *inBytes;
      uint8_t *outBytes;
  
      const int32_t *inIndexes;
      int32_t indexes[Normalizer2Impl::IX_MIN_MAYBE_YES+1];
  
      int32_t i, offset, nextOffset, size;
  
      /* udata_swapDataHeader checks the arguments */
      headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode);
      if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
          return 0;
      }
  
      /* check data format and format version */
      pInfo=(const UDataInfo *)((const char *)inData+4);
      if(!(
          pInfo->dataFormat[0]==0x4e &&   /* dataFormat="Nrm2" */
          pInfo->dataFormat[1]==0x72 &&
          pInfo->dataFormat[2]==0x6d &&
          pInfo->dataFormat[3]==0x32 &&
          (pInfo->formatVersion[0]==1 || pInfo->formatVersion[0]==2)
      )) {
          udata_printError(ds, "unorm2_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as Normalizer2 data\n",
                           pInfo->dataFormat[0], pInfo->dataFormat[1],
                           pInfo->dataFormat[2], pInfo->dataFormat[3],
                           pInfo->formatVersion[0]);
          *pErrorCode=U_UNSUPPORTED_ERROR;
          return 0;
      }
  
      inBytes=(const uint8_t *)inData+headerSize;
      outBytes=(uint8_t *)outData+headerSize;
  
      inIndexes=(const int32_t *)inBytes;
  
      if(length>=0) {
          length-=headerSize;
          if(length<(int32_t)sizeof(indexes)) {
              udata_printError(ds, "unorm2_swap(): too few bytes (%d after header) for Normalizer2 data\n",
                               length);
              *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
              return 0;
          }
      }
  
      /* read the first few indexes */
      for(i=0; i<=Normalizer2Impl::IX_MIN_MAYBE_YES; ++i) {
          indexes[i]=udata_readInt32(ds, inIndexes[i]);
      }
  
      /* get the total length of the data */
      size=indexes[Normalizer2Impl::IX_TOTAL_SIZE];
  
      if(length>=0) {
          if(length<size) {
              udata_printError(ds, "unorm2_swap(): too few bytes (%d after header) for all of Normalizer2 data\n",
                               length);
              *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
              return 0;
          }
  
          /* copy the data for inaccessible bytes */
          if(inBytes!=outBytes) {
              uprv_memcpy(outBytes, inBytes, size);
          }
  
          offset=0;
  
          /* swap the int32_t indexes[] */
          nextOffset=indexes[Normalizer2Impl::IX_NORM_TRIE_OFFSET];
          ds->swapArray32(ds, inBytes, nextOffset-offset, outBytes, pErrorCode);
          offset=nextOffset;
  
          /* swap the UTrie2 */
          nextOffset=indexes[Normalizer2Impl::IX_EXTRA_DATA_OFFSET];
          utrie2_swap(ds, inBytes+offset, nextOffset-offset, outBytes+offset, pErrorCode);
          offset=nextOffset;
  
          /* swap the uint16_t extraData[] */
          nextOffset=indexes[Normalizer2Impl::IX_SMALL_FCD_OFFSET];
          ds->swapArray16(ds, inBytes+offset, nextOffset-offset, outBytes+offset, pErrorCode);
          offset=nextOffset;
  
          /* no need to swap the uint8_t smallFCD[] (new in formatVersion 2) */
          nextOffset=indexes[Normalizer2Impl::IX_SMALL_FCD_OFFSET+1];
          offset=nextOffset;
  
          U_ASSERT(offset==size);
      }
  
      return headerSize+size;
  }
  
  #endif  // !UCONFIG_NO_NORMALIZATION
  