unicodedata.pas 97 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481
  1. { Unicode tables unit.
  2. Copyright (c) 2013 by Inoussa OUEDRAOGO
  3. The source code is distributed under the Library GNU
  4. General Public License with the following modification:
  5. - object files and libraries linked into an application may be
  6. distributed without source code.
  7. If you didn't receive a copy of the file COPYING, contact:
  8. Free Software Foundation
  9. 675 Mass Ave
  10. Cambridge, MA 02139
  11. USA
  12. This program is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15. -------------------------------------------------------------------------------
  16. Overview of the Unicode Collation Algorithm(UCA) data layout :
  17. ============================================================
  18. The UCA data(see “TUCA_DataBook”) are organized into index data
  19. (see the “TUCA_DataBook” fields “BMP_Table1”, “BMP_Table2”,
  20. “OBMP_Table1” and “OBMP_Table2”) and actual properties data(see
  21. the “Props” field of “TUCA_DataBook”). The index is a 3 level
  22. tables designed to minimize the overhaul data size. The
  23. properties’ data contain the actual (used) UCA’s properties
  24. for the customized code points(or sequence of code points)
  25. data (see TUCA_PropItemRec).
  26. To get the properties’ record of a code point, one goes
  27. through the index data to get its offset into the “Props”
  28. serialized data, see the “GetPropUCA” procedure.
  29. The “TUCA_PropItemRec” record, that represents the actual
  30. properties, contains a fixed part and a variable part. The
  31. fixed part is directly expressed as fields of the record :
  32. “WeightLength”, “ChildCount”, “Size”, “Flags”. The
  33. variable part depends on some values of the fixed part; For
  34. example “WeightLength” specify the number of weight[1] item,
  35. it can be zero or not null; The “Flags” fields does contains
  36. some bit states to indicate for example if the record’s owner,
  37. that is the target code point, is present(it is not always
  38. necessary to store the code point as you are required to have
  39. this information in the first place in order to get the
  40. “TUCA_PropItemRec” record).
  41. The data, as it is organized now, is as follow for each code point :
  42. * the fixed part is serialized,
  43. * if there are weight item array, they are serialized
  44. (see the "WeigthLength")
  45. * the code point is serialized (if needed)
  46. * the context[2] array is serialized
  47. * The children[3] record are serialized.
  48. The “Size” represent the size of the whole record, including its
  49. children records(see [3]). The “GetSelfOnlySize” returns the size
  50. of the queried record, excluding the size of its children.
  51. Notes :
  52. [1] : A weight item is an array of 3 words. A code point/sequence of code
  53. point may have zero or multiple items.
  54. [2] : There are characters(mostly japanese ones) that do not have their
  55. own weighs; There inherit the weights of the preceding character
  56. in the string that you will be evaluating.
  57. [3] : Some unicode characters are expressed using more than one code point.
  58. In that case the properties records are serialized as a trie. The
  59. trie data structure is useful when many characters’ expression have
  60. the same starting code point(s).
  61. [4] TUCA_PropItemRec serialization :
  62. TUCA_PropItemRec :
  63. WeightLength, ChildCount, Size, Flags [weight item array]
  64. [Code Point] [Context data]
  65. [Child 0] [Child 1] .. [Child n]
  66. each [Child k] is a TUCA_PropItemRec.
  67. }
  68. unit unicodedata;
  69. {$mode delphi}
  70. {$H+}
  71. {$PACKENUM 1}
  72. {$SCOPEDENUMS ON}
  73. {$pointermath on}
  74. {$define USE_INLINE}
  75. {$warn 4056 off} //Conversion between ordinals and pointers is not portable
  76. { $define uni_debug}
  77. interface
  78. const
  79. MAX_WORD = High(Word);
  80. LOW_SURROGATE_BEGIN = Word($DC00);
  81. LOW_SURROGATE_END = Word($DFFF);
  82. HIGH_SURROGATE_BEGIN = Word($D800);
  83. HIGH_SURROGATE_END = Word($DBFF);
  84. HIGH_SURROGATE_COUNT = HIGH_SURROGATE_END - HIGH_SURROGATE_BEGIN + 1;
  85. UCS4_HALF_BASE = LongWord($10000);
  86. UCS4_HALF_MASK = Word($3FF);
  87. MAX_LEGAL_UTF32 = $10FFFF;
  88. const
  89. // Unicode General Category
  90. UGC_UppercaseLetter = 0;
  91. UGC_LowercaseLetter = 1;
  92. UGC_TitlecaseLetter = 2;
  93. UGC_ModifierLetter = 3;
  94. UGC_OtherLetter = 4;
  95. UGC_NonSpacingMark = 5;
  96. UGC_CombiningMark = 6;
  97. UGC_EnclosingMark = 7;
  98. UGC_DecimalNumber = 8;
  99. UGC_LetterNumber = 9;
  100. UGC_OtherNumber = 10;
  101. UGC_ConnectPunctuation = 11;
  102. UGC_DashPunctuation = 12;
  103. UGC_OpenPunctuation = 13;
  104. UGC_ClosePunctuation = 14;
  105. UGC_InitialPunctuation = 15;
  106. UGC_FinalPunctuation = 16;
  107. UGC_OtherPunctuation = 17;
  108. UGC_MathSymbol = 18;
  109. UGC_CurrencySymbol = 19;
  110. UGC_ModifierSymbol = 20;
  111. UGC_OtherSymbol = 21;
  112. UGC_SpaceSeparator = 22;
  113. UGC_LineSeparator = 23;
  114. UGC_ParagraphSeparator = 24;
  115. UGC_Control = 25;
  116. UGC_Format = 26;
  117. UGC_Surrogate = 27;
  118. UGC_PrivateUse = 28;
  119. UGC_Unassigned = 29;
  120. type
  121. TUInt24Rec = packed record
  122. public
  123. {$ifdef FPC_LITTLE_ENDIAN}
  124. byte0, byte1, byte2 : Byte;
  125. {$else FPC_LITTLE_ENDIAN}
  126. byte2, byte1, byte0 : Byte;
  127. {$endif FPC_LITTLE_ENDIAN}
  128. public
  129. class operator Implicit(a : TUInt24Rec) : Cardinal;{$ifdef USE_INLINE}inline;{$ENDIF}
  130. class operator Implicit(a : TUInt24Rec) : LongInt;{$ifdef USE_INLINE}inline;{$ENDIF}
  131. class operator Implicit(a : TUInt24Rec) : Word;{$ifdef USE_INLINE}inline;{$ENDIF}
  132. class operator Implicit(a : TUInt24Rec) : Byte;{$ifdef USE_INLINE}inline;{$ENDIF}
  133. class operator Implicit(a : Cardinal) : TUInt24Rec;{$ifdef USE_INLINE}inline;{$ENDIF}
  134. class operator Equal(a, b: TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  135. class operator Equal(a : TUInt24Rec; b : Cardinal): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  136. class operator Equal(a : Cardinal; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  137. class operator Equal(a : TUInt24Rec; b : LongInt): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  138. class operator Equal(a : LongInt; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  139. class operator Equal(a : TUInt24Rec; b : Word): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  140. class operator Equal(a : Word; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  141. class operator Equal(a : TUInt24Rec; b : Byte): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  142. class operator Equal(a : Byte; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  143. class operator NotEqual(a, b: TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  144. class operator NotEqual(a : TUInt24Rec; b : Cardinal): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  145. class operator NotEqual(a : Cardinal; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  146. class operator GreaterThan(a, b: TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  147. class operator GreaterThan(a : TUInt24Rec; b : Cardinal): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  148. class operator GreaterThan(a : Cardinal; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  149. class operator GreaterThanOrEqual(a, b: TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  150. class operator GreaterThanOrEqual(a : TUInt24Rec; b : Cardinal): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  151. class operator GreaterThanOrEqual(a : Cardinal; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  152. class operator LessThan(a, b: TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  153. class operator LessThan(a : TUInt24Rec; b : Cardinal): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  154. class operator LessThan(a : Cardinal; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  155. class operator LessThanOrEqual(a, b: TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  156. class operator LessThanOrEqual(a : TUInt24Rec; b : Cardinal): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  157. class operator LessThanOrEqual(a : Cardinal; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  158. end;
  159. UInt24 = TUInt24Rec;
  160. PUInt24 = ^UInt24;
  161. const
  162. ZERO_UINT24 : UInt24 =
  163. {$ifdef FPC_LITTLE_ENDIAN}
  164. (byte0 : 0; byte1 : 0; byte2 : 0;);
  165. {$else FPC_LITTLE_ENDIAN}
  166. (byte2 : 0; byte1 : 0; byte0 : 0;);
  167. {$endif FPC_LITTLE_ENDIAN}
  168. type
  169. PUC_Prop = ^TUC_Prop;
  170. { TUC_Prop }
  171. TUC_Prop = packed record
  172. private
  173. function GetCategory : Byte;inline;
  174. procedure SetCategory(AValue : Byte);
  175. function GetWhiteSpace : Boolean;inline;
  176. procedure SetWhiteSpace(AValue : Boolean);
  177. function GetHangulSyllable : Boolean;inline;
  178. procedure SetHangulSyllable(AValue : Boolean);
  179. function GetNumericValue: Double;inline;
  180. public
  181. CategoryData : Byte;
  182. public
  183. CCC : Byte;
  184. NumericIndex : Byte;
  185. SimpleUpperCase : UInt24;
  186. SimpleLowerCase : UInt24;
  187. DecompositionID : SmallInt;
  188. public
  189. property Category : Byte read GetCategory write SetCategory;
  190. property WhiteSpace : Boolean read GetWhiteSpace write SetWhiteSpace;
  191. property HangulSyllable : Boolean read GetHangulSyllable write SetHangulSyllable;
  192. property NumericValue : Double read GetNumericValue;
  193. end;
  194. type
  195. TUCA_PropWeights = packed record
  196. Weights : array[0..2] of Word;
  197. end;
  198. PUCA_PropWeights = ^TUCA_PropWeights;
  199. TUCA_PropItemContextRec = packed record
  200. public
  201. CodePointCount : Byte;
  202. WeightCount : Byte;
  203. public
  204. function GetCodePoints() : PUInt24;inline;
  205. function GetWeights() : PUCA_PropWeights;inline;
  206. end;
  207. PUCA_PropItemContextRec = ^TUCA_PropItemContextRec;
  208. PUCA_PropItemContextTreeNodeRec = ^TUCA_PropItemContextTreeNodeRec;
  209. TUCA_PropItemContextTreeNodeRec = packed record
  210. public
  211. Left : Word;
  212. Right : Word;
  213. Data : TUCA_PropItemContextRec;
  214. public
  215. function GetLeftNode() : PUCA_PropItemContextTreeNodeRec;inline;
  216. function GetRightNode() : PUCA_PropItemContextTreeNodeRec;inline;
  217. end;
  218. { TUCA_PropItemContextTreeRec }
  219. TUCA_PropItemContextTreeRec = packed record
  220. public
  221. Size : UInt24;
  222. public
  223. function GetData:PUCA_PropItemContextTreeNodeRec;inline;
  224. property Data : PUCA_PropItemContextTreeNodeRec read GetData;
  225. function Find(
  226. const AChars : PUInt24;
  227. const ACharCount : Integer;
  228. out ANode : PUCA_PropItemContextTreeNodeRec
  229. ) : Boolean;
  230. end;
  231. PUCA_PropItemContextTreeRec = ^TUCA_PropItemContextTreeRec;
  232. { TUCA_PropItemRec }
  233. TUCA_PropItemRec = packed record
  234. private
  235. const FLAG_VALID = 0;
  236. const FLAG_CODEPOINT = 1;
  237. const FLAG_CONTEXTUAL = 2;
  238. const FLAG_DELETION = 3;
  239. const FLAG_COMPRESS_WEIGHT_1 = 6;
  240. const FLAG_COMPRESS_WEIGHT_2 = 7;
  241. private
  242. function GetCodePoint() : UInt24;inline;
  243. public
  244. WeightLength : Byte;
  245. ChildCount : Byte;
  246. Size : Word;
  247. Flags : Byte;
  248. public
  249. function HasCodePoint() : Boolean;inline;
  250. property CodePoint : UInt24 read GetCodePoint;
  251. //Weights : array[0..WeightLength] of TUCA_PropWeights;
  252. function IsValid() : Boolean;inline;
  253. //function GetWeightArray() : PUCA_PropWeights;inline;
  254. procedure GetWeightArray(ADest : PUCA_PropWeights);
  255. function GetSelfOnlySize() : Cardinal;inline;
  256. function GetContextual() : Boolean;inline;
  257. property Contextual : Boolean read GetContextual;
  258. function GetContext() : PUCA_PropItemContextTreeRec;
  259. function IsDeleted() : Boolean;inline;
  260. function IsWeightCompress_1() : Boolean;inline;
  261. function IsWeightCompress_2() : Boolean;inline;
  262. end;
  263. PUCA_PropItemRec = ^TUCA_PropItemRec;
  264. TUCA_VariableKind = (
  265. ucaShifted, ucaNonIgnorable, ucaBlanked, ucaShiftedTrimmed,
  266. ucaIgnoreSP // This one is not implemented !
  267. );
  268. TCollationName = string[128];
  269. PUCA_DataBook = ^TUCA_DataBook;
  270. TUCA_DataBook = packed record
  271. public
  272. Base : PUCA_DataBook;
  273. Version : TCollationName;
  274. CollationName : TCollationName;
  275. VariableWeight : TUCA_VariableKind;
  276. Backwards : array[0..3] of Boolean;
  277. BMP_Table1 : PByte;
  278. BMP_Table2 : PUInt24;
  279. OBMP_Table1 : PWord;
  280. OBMP_Table2 : PUInt24;
  281. PropCount : Integer;
  282. Props : PUCA_PropItemRec;
  283. VariableLowLimit : Word;
  284. VariableHighLimit : Word;
  285. Dynamic : Boolean;
  286. public
  287. function IsVariable(const AWeight : PUCA_PropWeights) : Boolean; inline;
  288. end;
  289. TCollationField = (BackWard, VariableLowLimit, VariableHighLimit);
  290. TCollationFields = set of TCollationField;
  291. const
  292. ROOT_COLLATION_NAME = 'DUCET';
  293. ERROR_INVALID_CODEPOINT_SEQUENCE = 1;
  294. procedure FromUCS4(const AValue : UCS4Char; var AHighS, ALowS : UnicodeChar);inline;
  295. function ToUCS4(const AHighS, ALowS : UnicodeChar) : UCS4Char;inline;
  296. function UnicodeIsSurrogatePair(
  297. const AHighSurrogate,
  298. ALowSurrogate : UnicodeChar
  299. ) : Boolean;inline;
  300. function UnicodeIsHighSurrogate(const AValue : UnicodeChar) : Boolean;inline;
  301. function UnicodeIsLowSurrogate(const AValue : UnicodeChar) : Boolean;inline;
  302. function UnicodeToUpper(
  303. const AString : UnicodeString;
  304. const AIgnoreInvalidSequence : Boolean;
  305. out AResultString : UnicodeString
  306. ) : Integer;
  307. function UnicodeToLower(
  308. const AString : UnicodeString;
  309. const AIgnoreInvalidSequence : Boolean;
  310. out AResultString : UnicodeString
  311. ) : Integer;
  312. function GetProps(const ACodePoint : Word) : PUC_Prop;overload;inline;
  313. function GetProps(const AHighS, ALowS : UnicodeChar): PUC_Prop;overload;inline;
  314. function GetProps(const ACodePoint : Cardinal) : PUC_Prop;overload;inline;
  315. function GetPropUCA(const AHighS, ALowS : UnicodeChar; const ABook : PUCA_DataBook): PUCA_PropItemRec; inline; overload;
  316. function GetPropUCA(const AChar : UnicodeChar; const ABook : PUCA_DataBook) : PUCA_PropItemRec; inline; overload;
  317. function NormalizeNFD(const AString : UnicodeString) : UnicodeString;inline;overload;
  318. function NormalizeNFD(const AStr : PUnicodeChar; ALength : SizeInt) : UnicodeString;overload;
  319. procedure CanonicalOrder(var AString : UnicodeString);inline;overload;
  320. procedure CanonicalOrder(AStr : PUnicodeChar; const ALength : SizeInt);overload;
  321. type
  322. TUCASortKeyItem = Word;
  323. TUCASortKey = array of TUCASortKeyItem;
  324. function ComputeSortKey(
  325. const AString : UnicodeString;
  326. const ACollation : PUCA_DataBook
  327. ) : TUCASortKey;inline;overload;
  328. function ComputeSortKey(
  329. const AStr : PUnicodeChar;
  330. const ALength : SizeInt;
  331. const ACollation : PUCA_DataBook
  332. ) : TUCASortKey;overload;
  333. function CompareSortKey(const A, B : TUCASortKey) : Integer;overload;
  334. function CompareSortKey(const A : TUCASortKey; const B : array of Word) : Integer;overload;
  335. function IncrementalCompareString(
  336. const AStrA : PUnicodeChar;
  337. const ALengthA : SizeInt;
  338. const AStrB : PUnicodeChar;
  339. const ALengthB : SizeInt;
  340. const ACollation : PUCA_DataBook
  341. ) : Integer;overload;
  342. function IncrementalCompareString(
  343. const AStrA,
  344. AStrB : UnicodeString;
  345. const ACollation : PUCA_DataBook
  346. ) : Integer;inline;overload;
  347. function RegisterCollation(const ACollation : PUCA_DataBook) : Boolean;overload;
  348. function RegisterCollation(
  349. const ADirectory,
  350. ALanguage : string
  351. ) : Boolean;overload;
  352. function UnregisterCollation(const AName : ansistring): Boolean;
  353. procedure UnregisterCollations(const AFreeDynamicCollations : Boolean);
  354. function FindCollation(const AName : ansistring): PUCA_DataBook;overload;
  355. function FindCollation(const AIndex : Integer): PUCA_DataBook;overload;
  356. function GetCollationCount() : Integer;
  357. procedure PrepareCollation(
  358. ACollation : PUCA_DataBook;
  359. const ABaseName : ansistring;
  360. const AChangedFields : TCollationFields
  361. );
  362. function LoadCollation(
  363. const AData : Pointer;
  364. const ADataLength : Integer
  365. ) : PUCA_DataBook;overload;
  366. function LoadCollation(const AFileName : string) : PUCA_DataBook;overload;
  367. function LoadCollation(
  368. const ADirectory,
  369. ALanguage : string
  370. ) : PUCA_DataBook;overload;
  371. procedure FreeCollation(AItem : PUCA_DataBook);
  372. type
  373. TEndianKind = (Little, Big);
  374. const
  375. ENDIAN_SUFFIX : array[TEndianKind] of string[2] = ('le','be');
  376. {$IFDEF ENDIAN_LITTLE}
  377. ENDIAN_NATIVE = TEndianKind.Little;
  378. ENDIAN_NON_NATIVE = TEndianKind.Big;
  379. {$ENDIF ENDIAN_LITTLE}
  380. {$IFDEF ENDIAN_BIG}
  381. ENDIAN_NATIVE = TEndianKind.Big;
  382. ENDIAN_NON_NATIVE = TEndianKind.Little;
  383. {$ENDIF ENDIAN_BIG}
  384. resourcestring
  385. SCollationNotFound = 'Collation not found : "%s".';
  386. implementation
  387. uses
  388. unicodenumtable;
  389. type
  390. TCardinalRec = packed record
  391. {$ifdef FPC_LITTLE_ENDIAN}
  392. byte0, byte1, byte2, byte3 : Byte;
  393. {$else FPC_LITTLE_ENDIAN}
  394. byte3, byte2, byte1, byte0 : Byte;
  395. {$endif FPC_LITTLE_ENDIAN}
  396. end;
  397. TWordRec = packed record
  398. {$ifdef FPC_LITTLE_ENDIAN}
  399. byte0, byte1 : Byte;
  400. {$else FPC_LITTLE_ENDIAN}
  401. byte1, byte0 : Byte;
  402. {$endif FPC_LITTLE_ENDIAN}
  403. end;
  404. { TUInt24Rec }
  405. class operator TUInt24Rec.Implicit(a : TUInt24Rec) : Cardinal;
  406. begin
  407. TCardinalRec(Result).byte0 := a.byte0;
  408. TCardinalRec(Result).byte1 := a.byte1;
  409. TCardinalRec(Result).byte2 := a.byte2;
  410. TCardinalRec(Result).byte3 := 0;
  411. end;
  412. class operator TUInt24Rec.Implicit(a : TUInt24Rec) : LongInt;
  413. begin
  414. Result := Cardinal(a);
  415. end;
  416. class operator TUInt24Rec.Implicit(a : TUInt24Rec) : Word;
  417. begin
  418. {$IFOPT R+}
  419. if (a > $FFFF) then
  420. Error(reIntOverflow);
  421. {$ENDIF R+}
  422. TWordRec(Result).byte0 := a.byte0;
  423. TWordRec(Result).byte1 := a.byte1;
  424. end;
  425. class operator TUInt24Rec.Implicit(a : TUInt24Rec) : Byte;
  426. begin
  427. {$IFOPT R+}
  428. if (a > $FF) then
  429. Error(reIntOverflow);
  430. {$ENDIF R+}
  431. Result := a.byte0;
  432. end;
  433. class operator TUInt24Rec.Implicit(a : Cardinal) : TUInt24Rec;
  434. begin
  435. {$IFOPT R+}
  436. if (a > $FFFFFF) then
  437. Error(reIntOverflow);
  438. {$ENDIF R+}
  439. Result.byte0 := TCardinalRec(a).byte0;
  440. Result.byte1 := TCardinalRec(a).byte1;
  441. Result.byte2 := TCardinalRec(a).byte2;
  442. end;
  443. class operator TUInt24Rec.Equal(a, b : TUInt24Rec) : Boolean;
  444. begin
  445. Result := (a.byte0 = b.byte0) and (a.byte1 = b.byte1) and (a.byte2 = b.byte2);
  446. end;
  447. class operator TUInt24Rec.Equal(a : TUInt24Rec; b : Cardinal) : Boolean;
  448. begin
  449. Result := (TCardinalRec(b).byte3 = 0) and
  450. (a.byte0 = TCardinalRec(b).byte0) and
  451. (a.byte1 = TCardinalRec(b).byte1) and
  452. (a.byte2 = TCardinalRec(b).byte2);
  453. end;
  454. class operator TUInt24Rec.Equal(a : Cardinal; b : TUInt24Rec) : Boolean;
  455. begin
  456. Result := (b = a);
  457. end;
  458. class operator TUInt24Rec.Equal(a : TUInt24Rec; b : LongInt) : Boolean;
  459. begin
  460. Result := (LongInt(a) = b);
  461. end;
  462. class operator TUInt24Rec.Equal(a : LongInt; b : TUInt24Rec) : Boolean;
  463. begin
  464. Result := (b = a);
  465. end;
  466. class operator TUInt24Rec.Equal(a : TUInt24Rec; b : Word) : Boolean;
  467. begin
  468. Result := (a.byte2 = 0) and
  469. (a.byte0 = TWordRec(b).byte0) and
  470. (a.byte1 = TWordRec(b).byte1);
  471. end;
  472. class operator TUInt24Rec.Equal(a : Word; b : TUInt24Rec) : Boolean;
  473. begin
  474. Result := (b = a);
  475. end;
  476. class operator TUInt24Rec.Equal(a : TUInt24Rec; b : Byte) : Boolean;
  477. begin
  478. Result := (a.byte2 = 0) and
  479. (a.byte1 = 0) and
  480. (a.byte0 = b);
  481. end;
  482. class operator TUInt24Rec.Equal(a : Byte; b : TUInt24Rec) : Boolean;
  483. begin
  484. Result := (b = a);
  485. end;
  486. class operator TUInt24Rec.NotEqual(a, b : TUInt24Rec) : Boolean;
  487. begin
  488. Result := (a.byte0 <> b.byte0) or (a.byte1 <> b.byte1) or (a.byte2 <> b.byte2);
  489. end;
  490. class operator TUInt24Rec.NotEqual(a : TUInt24Rec; b : Cardinal) : Boolean;
  491. begin
  492. Result := (TCardinalRec(b).byte3 <> 0) or
  493. (a.byte0 <> TCardinalRec(b).byte0) or
  494. (a.byte1 <> TCardinalRec(b).byte1) or
  495. (a.byte2 <> TCardinalRec(b).byte2);
  496. end;
  497. class operator TUInt24Rec.NotEqual(a : Cardinal; b : TUInt24Rec) : Boolean;
  498. begin
  499. Result := (b <> a);
  500. end;
  501. class operator TUInt24Rec.GreaterThan(a, b: TUInt24Rec): Boolean;
  502. begin
  503. Result := (a.byte2 > b.byte2) or
  504. ((a.byte2 = b.byte2) and (a.byte1 > b.byte1)) or
  505. ((a.byte2 = b.byte2) and (a.byte1 = b.byte1) and (a.byte0 > b.byte0));
  506. end;
  507. class operator TUInt24Rec.GreaterThan(a: TUInt24Rec; b: Cardinal): Boolean;
  508. begin
  509. Result := Cardinal(a) > b;
  510. end;
  511. class operator TUInt24Rec.GreaterThan(a: Cardinal; b: TUInt24Rec): Boolean;
  512. begin
  513. Result := a > Cardinal(b);
  514. end;
  515. class operator TUInt24Rec.GreaterThanOrEqual(a, b: TUInt24Rec): Boolean;
  516. begin
  517. Result := (a.byte2 > b.byte2) or
  518. ((a.byte2 = b.byte2) and (a.byte1 > b.byte1)) or
  519. ((a.byte2 = b.byte2) and (a.byte1 = b.byte1) and (a.byte0 >= b.byte0));
  520. end;
  521. class operator TUInt24Rec.GreaterThanOrEqual(a: TUInt24Rec; b: Cardinal): Boolean;
  522. begin
  523. Result := Cardinal(a) >= b;
  524. end;
  525. class operator TUInt24Rec.GreaterThanOrEqual(a: Cardinal; b: TUInt24Rec): Boolean;
  526. begin
  527. Result := a >= Cardinal(b);
  528. end;
  529. class operator TUInt24Rec.LessThan(a, b: TUInt24Rec): Boolean;
  530. begin
  531. Result := (b > a);
  532. end;
  533. class operator TUInt24Rec.LessThan(a: TUInt24Rec; b: Cardinal): Boolean;
  534. begin
  535. Result := Cardinal(a) < b;
  536. end;
  537. class operator TUInt24Rec.LessThan(a: Cardinal; b: TUInt24Rec): Boolean;
  538. begin
  539. Result := a < Cardinal(b);
  540. end;
  541. class operator TUInt24Rec.LessThanOrEqual(a, b: TUInt24Rec): Boolean;
  542. begin
  543. Result := (b >= a);
  544. end;
  545. class operator TUInt24Rec.LessThanOrEqual(a: TUInt24Rec; b: Cardinal): Boolean;
  546. begin
  547. Result := Cardinal(a) <= b;
  548. end;
  549. class operator TUInt24Rec.LessThanOrEqual(a: Cardinal; b: TUInt24Rec): Boolean;
  550. begin
  551. Result := a <= Cardinal(b);
  552. end;
  553. type
  554. TBitOrder = 0..7;
  555. function IsBitON(const AData : Byte; const ABit : TBitOrder) : Boolean ;inline;
  556. begin
  557. Result := ( ( AData and ( 1 shl ABit ) ) <> 0 );
  558. end;
  559. procedure SetBit(var AData : Byte; const ABit : TBitOrder; const AValue : Boolean);inline;
  560. begin
  561. if AValue then
  562. AData := AData or (1 shl (ABit mod 8))
  563. else
  564. AData := AData and ( not ( 1 shl ( ABit mod 8 ) ) );
  565. end;
  566. var
  567. CollationTable : array of PUCA_DataBook;
  568. function IndexOfCollation(const AName : string) : Integer;
  569. var
  570. i, c : Integer;
  571. p : Pointer;
  572. begin
  573. c := Length(AName);
  574. p := @AName[1];
  575. for i := 0 to Length(CollationTable) - 1 do begin
  576. if (Length(CollationTable[i]^.CollationName) = c) and
  577. (CompareByte((CollationTable[i]^.CollationName[1]),p^,c)=0)
  578. then
  579. exit(i);
  580. end;
  581. Result := -1;
  582. end;
  583. function RegisterCollation(const ACollation : PUCA_DataBook) : Boolean;
  584. var
  585. i : Integer;
  586. begin
  587. Result := (IndexOfCollation(ACollation^.CollationName) = -1);
  588. if Result then begin
  589. i := Length(CollationTable);
  590. SetLength(CollationTable,(i+1));
  591. CollationTable[i] := ACollation;
  592. end;
  593. end;
  594. function RegisterCollation(const ADirectory, ALanguage : string) : Boolean;
  595. var
  596. cl : PUCA_DataBook;
  597. begin
  598. cl := LoadCollation(ADirectory,ALanguage);
  599. if (cl = nil) then
  600. exit(False);
  601. try
  602. Result := RegisterCollation(cl);
  603. except
  604. FreeCollation(cl);
  605. raise;
  606. end;
  607. if not Result then
  608. FreeCollation(cl);
  609. end;
  610. function UnregisterCollation(const AName : ansistring): Boolean;
  611. var
  612. i, c : Integer;
  613. begin
  614. i := IndexOfCollation(AName);
  615. Result := (i >= 0);
  616. if Result then begin
  617. c := Length(CollationTable);
  618. if (c = 1) then begin
  619. SetLength(CollationTable,0);
  620. end else begin
  621. CollationTable[i] := CollationTable[c-1];
  622. SetLength(CollationTable,(c-1));
  623. end;
  624. end;
  625. end;
  626. procedure UnregisterCollations(const AFreeDynamicCollations : Boolean);
  627. var
  628. i : Integer;
  629. cl : PUCA_DataBook;
  630. begin
  631. if AFreeDynamicCollations then begin
  632. for i := Low(CollationTable) to High(CollationTable) do begin
  633. if CollationTable[i].Dynamic then begin
  634. cl := CollationTable[i];
  635. CollationTable[i] := nil;
  636. FreeCollation(cl);
  637. end;
  638. end;
  639. end;
  640. SetLength(CollationTable,0);
  641. end;
  642. function FindCollation(const AName : ansistring): PUCA_DataBook;overload;
  643. var
  644. i : Integer;
  645. begin
  646. i := IndexOfCollation(AName);
  647. if (i = -1) then
  648. Result := nil
  649. else
  650. Result := CollationTable[i];
  651. end;
  652. function GetCollationCount() : Integer;
  653. begin
  654. Result := Length(CollationTable);
  655. end;
  656. function FindCollation(const AIndex : Integer): PUCA_DataBook;overload;
  657. begin
  658. if (AIndex < 0) or (AIndex >= Length(CollationTable)) then
  659. Result := nil
  660. else
  661. Result := CollationTable[AIndex];
  662. end;
  663. procedure PrepareCollation(
  664. ACollation : PUCA_DataBook;
  665. const ABaseName : ansistring;
  666. const AChangedFields : TCollationFields
  667. );
  668. var
  669. s : ansistring;
  670. p, base : PUCA_DataBook;
  671. begin
  672. if (ABaseName <> '') then
  673. s := ABaseName
  674. else
  675. s := ROOT_COLLATION_NAME;
  676. p := ACollation;
  677. base := FindCollation(s);
  678. if (base = nil) then
  679. Error(reCodesetConversion);
  680. p^.Base := base;
  681. if not(TCollationField.BackWard in AChangedFields) then
  682. p^.Backwards := base^.Backwards;
  683. if not(TCollationField.VariableLowLimit in AChangedFields) then
  684. p^.VariableLowLimit := base^.VariableLowLimit;
  685. if not(TCollationField.VariableHighLimit in AChangedFields) then
  686. p^.VariableLowLimit := base^.VariableHighLimit;
  687. end;
  688. type
  689. TSerializedCollationHeader = packed record
  690. Base : TCollationName;
  691. Version : TCollationName;
  692. CollationName : TCollationName;
  693. VariableWeight : Byte;
  694. Backwards : Byte;
  695. BMP_Table1Length : DWord;
  696. BMP_Table2Length : DWord;
  697. OBMP_Table1Length : DWord;
  698. OBMP_Table2Length : DWord;
  699. PropCount : DWord;
  700. VariableLowLimit : Word;
  701. VariableHighLimit : Word;
  702. ChangedFields : Byte;
  703. end;
  704. PSerializedCollationHeader = ^TSerializedCollationHeader;
  705. procedure FreeCollation(AItem : PUCA_DataBook);
  706. var
  707. h : PSerializedCollationHeader;
  708. begin
  709. if (AItem = nil) or not(AItem^.Dynamic) then
  710. exit;
  711. h := PSerializedCollationHeader(PtrUInt(AItem) + SizeOf(TUCA_DataBook));
  712. if (AItem^.BMP_Table1 <> nil) then
  713. FreeMem(AItem^.BMP_Table1,h^.BMP_Table1Length);
  714. if (AItem^.BMP_Table2 <> nil) then
  715. FreeMem(AItem^.BMP_Table2,h^.BMP_Table2Length);
  716. if (AItem^.OBMP_Table1 <> nil) then
  717. FreeMem(AItem^.OBMP_Table1,h^.OBMP_Table1Length);
  718. if (AItem^.OBMP_Table2 <> nil) then
  719. FreeMem(AItem^.OBMP_Table2,h^.OBMP_Table2Length);
  720. if (AItem^.Props <> nil) then
  721. FreeMem(AItem^.Props,h^.PropCount);
  722. FreeMem(AItem,(SizeOf(TUCA_DataBook)+SizeOf(TSerializedCollationHeader)));
  723. end;
  724. function LoadCollation(
  725. const AData : Pointer;
  726. const ADataLength : Integer
  727. ) : PUCA_DataBook;
  728. var
  729. dataPointer : PByte;
  730. readedLength : LongInt;
  731. function ReadBuffer(ADest : Pointer; ALength : LongInt) : Boolean;
  732. begin
  733. Result := (readedLength + ALength) <= ADataLength;
  734. if not result then
  735. exit;
  736. Move(dataPointer^,ADest^,ALength);
  737. Inc(dataPointer,ALength);
  738. readedLength := readedLength + ALength;
  739. end;
  740. var
  741. r : PUCA_DataBook;
  742. h : PSerializedCollationHeader;
  743. cfs : TCollationFields;
  744. i : Integer;
  745. baseName : TCollationName;
  746. begin
  747. readedLength := 0;
  748. dataPointer := AData;
  749. r := AllocMem((SizeOf(TUCA_DataBook)+SizeOf(TSerializedCollationHeader)));
  750. try
  751. h := PSerializedCollationHeader(PtrUInt(r) + SizeOf(TUCA_DataBook));
  752. if not ReadBuffer(h,SizeOf(TSerializedCollationHeader)) then
  753. exit;
  754. r^.Version := h^.Version;
  755. r^.CollationName := h^.CollationName;
  756. r^.VariableWeight := TUCA_VariableKind(h^.VariableWeight);
  757. r^.Backwards[0] := IsBitON(h^.Backwards,0);
  758. r^.Backwards[1] := IsBitON(h^.Backwards,1);
  759. r^.Backwards[2] := IsBitON(h^.Backwards,2);
  760. r^.Backwards[3] := IsBitON(h^.Backwards,3);
  761. if (h^.BMP_Table1Length > 0) then begin
  762. r^.BMP_Table1 := GetMem(h^.BMP_Table1Length);
  763. if not ReadBuffer(r^.BMP_Table1,h^.BMP_Table1Length) then
  764. exit;
  765. end;
  766. if (h^.BMP_Table2Length > 0) then begin
  767. r^.BMP_Table2 := GetMem(h^.BMP_Table2Length);
  768. if not ReadBuffer(r^.BMP_Table2,h^.BMP_Table2Length) then
  769. exit;
  770. end;
  771. if (h^.OBMP_Table1Length > 0) then begin
  772. r^.OBMP_Table1 := GetMem(h^.OBMP_Table1Length);
  773. if not ReadBuffer(r^.OBMP_Table1,h^.OBMP_Table1Length) then
  774. exit;
  775. end;
  776. if (h^.OBMP_Table2Length > 0) then begin
  777. r^.OBMP_Table2 := GetMem(h^.OBMP_Table2Length);
  778. if not ReadBuffer(r^.OBMP_Table2,h^.OBMP_Table2Length) then
  779. exit;
  780. end;
  781. r^.PropCount := h^.PropCount;
  782. if (h^.PropCount > 0) then begin
  783. r^.Props := GetMem(h^.PropCount);
  784. if not ReadBuffer(r^.Props,h^.PropCount) then
  785. exit;
  786. end;
  787. r^.VariableLowLimit := h^.VariableLowLimit;
  788. r^.VariableHighLimit := h^.VariableHighLimit;
  789. cfs := [];
  790. for i := Ord(Low(TCollationField)) to Ord(High(TCollationField)) do begin
  791. if IsBitON(h^.ChangedFields,i) then
  792. cfs := cfs + [TCollationField(i)];
  793. end;
  794. if (h^.Base <> '') then
  795. baseName := h^.Base
  796. else if (h^.CollationName <> ROOT_COLLATION_NAME) then
  797. baseName := ROOT_COLLATION_NAME
  798. else
  799. baseName := '';
  800. if (baseName <> '') then
  801. PrepareCollation(r,baseName,cfs);
  802. r^.Dynamic := True;
  803. Result := r;
  804. except
  805. FreeCollation(r);
  806. raise;
  807. end;
  808. end;
  809. {$PUSH}
  810. function LoadCollation(const AFileName : string) : PUCA_DataBook;
  811. const
  812. BLOCK_SIZE = 16*1024;
  813. var
  814. f : File of Byte;
  815. locSize, locReaded, c : LongInt;
  816. locBuffer : PByte;
  817. locBlockSize : LongInt;
  818. begin
  819. Result := nil;
  820. {$I-}
  821. if (AFileName = '') then
  822. exit;
  823. Assign(f,AFileName);
  824. Reset(f);
  825. try
  826. if (IOResult <> 0) then
  827. exit;
  828. locSize := FileSize(f);
  829. if (locSize < SizeOf(TSerializedCollationHeader)) then
  830. exit;
  831. locBuffer := GetMem(locSize);
  832. try
  833. locBlockSize := BLOCK_SIZE;
  834. locReaded := 0;
  835. while (locReaded < locSize) do begin
  836. if (locBlockSize > (locSize-locReaded)) then
  837. locBlockSize := locSize-locReaded;
  838. BlockRead(f,locBuffer[locReaded],locBlockSize,c);
  839. if (IOResult <> 0) or (c <= 0) then
  840. exit;
  841. locReaded := locReaded + c;
  842. end;
  843. Result := LoadCollation(locBuffer,locSize);
  844. finally
  845. FreeMem(locBuffer,locSize);
  846. end;
  847. finally
  848. Close(f);
  849. end;
  850. end;
  851. {$POP}
  852. function LoadCollation(const ADirectory, ALanguage : string) : PUCA_DataBook;
  853. var
  854. fileName : string;
  855. begin
  856. fileName := ADirectory;
  857. if (fileName <> '') then begin
  858. if (fileName[Length(fileName)] <> DirectorySeparator) then
  859. fileName := fileName + DirectorySeparator;
  860. end;
  861. fileName := fileName + 'collation_' + ALanguage + '_' + ENDIAN_SUFFIX[ENDIAN_NATIVE] + '.bco';
  862. Result := LoadCollation(fileName);
  863. end;
  864. {$INCLUDE unicodedata.inc}
  865. {$IFDEF ENDIAN_LITTLE}
  866. {$INCLUDE unicodedata_le.inc}
  867. {$ENDIF ENDIAN_LITTLE}
  868. {$IFDEF ENDIAN_BIG}
  869. {$INCLUDE unicodedata_be.inc}
  870. {$ENDIF ENDIAN_BIG}
  871. procedure FromUCS4(const AValue : UCS4Char; var AHighS, ALowS : UnicodeChar);inline;
  872. begin
  873. AHighS := UnicodeChar((AValue - $10000) shr 10 + $d800);
  874. ALowS := UnicodeChar((AValue - $10000) and $3ff + $dc00);
  875. end;
  876. function ToUCS4(const AHighS, ALowS : UnicodeChar) : UCS4Char;inline;
  877. begin
  878. Result := (UCS4Char(Word(AHighS)) - HIGH_SURROGATE_BEGIN) shl 10 +
  879. (UCS4Char(Word(ALowS)) - LOW_SURROGATE_BEGIN) + UCS4_HALF_BASE;
  880. end;
  881. function UnicodeIsSurrogatePair(
  882. const AHighSurrogate,
  883. ALowSurrogate : UnicodeChar
  884. ) : Boolean;
  885. begin
  886. Result :=
  887. ( (Word(AHighSurrogate) >= HIGH_SURROGATE_BEGIN) and
  888. (Word(AHighSurrogate) <= HIGH_SURROGATE_END)
  889. ) and
  890. ( (Word(ALowSurrogate) >= LOW_SURROGATE_BEGIN) and
  891. (Word(ALowSurrogate) <= LOW_SURROGATE_END)
  892. )
  893. end;
  894. function UnicodeIsHighSurrogate(const AValue : UnicodeChar) : Boolean;
  895. begin
  896. Result := (Word(AValue) >= HIGH_SURROGATE_BEGIN) and
  897. (Word(AValue) <= HIGH_SURROGATE_END);
  898. end;
  899. function UnicodeIsLowSurrogate(const AValue : UnicodeChar) : Boolean;
  900. begin
  901. Result := (Word(AValue) >= LOW_SURROGATE_BEGIN) and
  902. (Word(AValue) <= LOW_SURROGATE_END);
  903. end;
  904. function GetProps(const ACodePoint : Word) : PUC_Prop;overload;inline;
  905. begin
  906. Result:=
  907. @UC_PROP_ARRAY[
  908. UC_TABLE_3[
  909. UC_TABLE_2[UC_TABLE_1[hi(ACodePoint)]]
  910. [lo(ACodePoint) shr 4]
  911. ][lo(ACodePoint) and $F]
  912. ]; {
  913. @UC_PROP_ARRAY[
  914. UC_TABLE_2[
  915. (UC_TABLE_1[WordRec(ACodePoint).Hi] * 256) +
  916. WordRec(ACodePoint).Lo
  917. ]
  918. ];}
  919. end;
  920. function GetProps(const AHighS, ALowS : UnicodeChar): PUC_Prop;overload;inline;
  921. begin
  922. Result:=
  923. @UC_PROP_ARRAY[
  924. UCO_TABLE_3[
  925. UCO_TABLE_2[UCO_TABLE_1[Word(AHighS)-HIGH_SURROGATE_BEGIN]]
  926. [(Word(ALowS) - LOW_SURROGATE_BEGIN) div 32]
  927. ][(Word(ALowS) - LOW_SURROGATE_BEGIN) mod 32]
  928. ]; {
  929. Result:=
  930. @UC_PROP_ARRAY[
  931. UCO_TABLE_2[
  932. (UCO_TABLE_1[Word(AHighS)-HIGH_SURROGATE_BEGIN] * HIGH_SURROGATE_COUNT) +
  933. Word(ALowS) - LOW_SURROGATE_BEGIN
  934. ]
  935. ]; }
  936. end;
  937. function GetProps(const ACodePoint : Cardinal) : PUC_Prop;inline;
  938. var
  939. l, h : UnicodeChar;
  940. begin
  941. if (ACodePoint <= High(Word)) then
  942. exit(GetProps(Word(ACodePoint)));
  943. FromUCS4(ACodePoint,h,l);
  944. Result := GetProps(h,l);
  945. end;
  946. function UnicodeToUpper(
  947. const AString : UnicodeString;
  948. const AIgnoreInvalidSequence : Boolean;
  949. out AResultString : UnicodeString
  950. ) : Integer;
  951. var
  952. i, c : SizeInt;
  953. pp, pr : PUnicodeChar;
  954. pu : PUC_Prop;
  955. locIsSurrogate : Boolean;
  956. r : UnicodeString;
  957. begin
  958. c := Length(AString);
  959. SetLength(r,2*c);
  960. if (c > 0) then begin
  961. pp := @AString[1];
  962. pr := @r[1];
  963. i := 1;
  964. while (i <= c) do begin
  965. pu := GetProps(Word(pp^));
  966. locIsSurrogate := (pu^.Category = UGC_Surrogate);
  967. if locIsSurrogate then begin
  968. if (i = c) or not(UnicodeIsSurrogatePair(pp[0],pp[1])) then begin
  969. if AIgnoreInvalidSequence then begin
  970. pr^ := pp^;
  971. Inc(pp);
  972. Inc(pr);
  973. Inc(i);
  974. Continue;
  975. end;
  976. exit(ERROR_INVALID_CODEPOINT_SEQUENCE);
  977. end;
  978. pu := GetProps(pp^,AString[i+1]);
  979. end;
  980. if (pu^.SimpleUpperCase = 0) then begin
  981. pr^ := pp^;
  982. if locIsSurrogate then begin
  983. Inc(pp);
  984. Inc(pr);
  985. Inc(i);
  986. pr^ := pp^;
  987. end;
  988. end else begin
  989. if (pu^.SimpleUpperCase <= $FFFF) then begin
  990. pr^ := UnicodeChar(Word(pu^.SimpleUpperCase));
  991. end else begin
  992. FromUCS4(UCS4Char(Cardinal(pu^.SimpleUpperCase)),pr^,PUnicodeChar(PtrUInt(pr)+SizeOf(UnicodeChar))^);
  993. Inc(pr);
  994. end;
  995. if locIsSurrogate then begin
  996. Inc(pp);
  997. Inc(i);
  998. end;
  999. end;
  1000. Inc(pp);
  1001. Inc(pr);
  1002. Inc(i);
  1003. end;
  1004. Dec(pp);
  1005. i := ((PtrUInt(pr) - PtrUInt(@r[1])) div SizeOf(UnicodeChar));
  1006. SetLength(r,i);
  1007. AResultString := r;
  1008. end;
  1009. Result := 0;
  1010. end;
  1011. function UnicodeToLower(
  1012. const AString : UnicodeString;
  1013. const AIgnoreInvalidSequence : Boolean;
  1014. out AResultString : UnicodeString
  1015. ) : Integer;
  1016. var
  1017. i, c : SizeInt;
  1018. pp, pr : PUnicodeChar;
  1019. pu : PUC_Prop;
  1020. locIsSurrogate : Boolean;
  1021. r : UnicodeString;
  1022. begin
  1023. c := Length(AString);
  1024. SetLength(r,2*c);
  1025. if (c > 0) then begin
  1026. pp := @AString[1];
  1027. pr := @r[1];
  1028. i := 1;
  1029. while (i <= c) do begin
  1030. pu := GetProps(Word(pp^));
  1031. locIsSurrogate := (pu^.Category = UGC_Surrogate);
  1032. if locIsSurrogate then begin
  1033. if (i = c) or not(UnicodeIsSurrogatePair(pp[0],pp[1])) then begin
  1034. if AIgnoreInvalidSequence then begin
  1035. pr^ := pp^;
  1036. Inc(pp);
  1037. Inc(pr);
  1038. Inc(i);
  1039. Continue;
  1040. end;
  1041. exit(ERROR_INVALID_CODEPOINT_SEQUENCE);
  1042. end;
  1043. pu := GetProps(pp^,AString[i+1]);
  1044. end;
  1045. if (pu^.SimpleLowerCase = 0) then begin
  1046. pr^ := pp^;
  1047. if locIsSurrogate then begin
  1048. Inc(pp);
  1049. Inc(pr);
  1050. Inc(i);
  1051. pr^ := pp^;
  1052. end;
  1053. end else begin
  1054. if (pu^.SimpleLowerCase <= $FFFF) then begin
  1055. pr^ := UnicodeChar(Word(pu^.SimpleLowerCase));
  1056. end else begin
  1057. FromUCS4(UCS4Char(Cardinal(pu^.SimpleLowerCase)),pr^,PUnicodeChar(PtrUInt(pr)+SizeOf(UnicodeChar))^);
  1058. Inc(pr);
  1059. end;
  1060. if locIsSurrogate then begin
  1061. Inc(pp);
  1062. Inc(i);
  1063. end;
  1064. end;
  1065. Inc(pp);
  1066. Inc(pr);
  1067. Inc(i);
  1068. end;
  1069. Dec(pp);
  1070. i := ((PtrUInt(pr) - PtrUInt(@r[1])) div SizeOf(UnicodeChar));
  1071. SetLength(r,i);
  1072. AResultString := r;
  1073. end;
  1074. Result := 0;
  1075. end;
  1076. //----------------------------------------------------------------------
  1077. function DecomposeHangul(const AChar : Cardinal; ABuffer : PCardinal) : Integer;
  1078. const
  1079. SBase = $AC00;
  1080. LBase = $1100;
  1081. VBase = $1161;
  1082. TBase = $11A7;
  1083. LCount = 19;
  1084. VCount = 21;
  1085. TCount = 28;
  1086. NCount = VCount * TCount; // 588
  1087. SCount = LCount * NCount; // 11172
  1088. var
  1089. SIndex, L, V, T : Integer;
  1090. begin
  1091. SIndex := AChar - SBase;
  1092. if (SIndex < 0) or (SIndex >= SCount) then begin
  1093. ABuffer^ := AChar;
  1094. exit(1);
  1095. end;
  1096. L := LBase + SIndex div NCount;
  1097. V := VBase + (SIndex mod NCount) div TCount;
  1098. T := TBase + SIndex mod TCount;
  1099. ABuffer[0] := L;
  1100. ABuffer[1] := V;
  1101. Result := 2;
  1102. if (T <> TBase) then begin
  1103. ABuffer[2] := T;
  1104. Inc(Result);
  1105. end;
  1106. end;
  1107. function Decompose(const ADecomposeIndex : Integer; ABuffer : PUnicodeChar) : Integer;
  1108. var
  1109. locStack : array[0..23] of Cardinal;
  1110. locStackIdx : Integer;
  1111. ResultBuffer : array[0..23] of Cardinal;
  1112. ResultIdx : Integer;
  1113. procedure AddCompositionToStack(const AIndex : Integer);
  1114. var
  1115. pdecIdx : ^TDecompositionIndexRec;
  1116. k, kc : Integer;
  1117. pu : ^UInt24;
  1118. begin
  1119. pdecIdx := @(UC_DEC_BOOK_DATA.Index[AIndex]);
  1120. pu := @(UC_DEC_BOOK_DATA.CodePoints[pdecIdx^.StartPosition]);
  1121. kc := pdecIdx^.Length;
  1122. Inc(pu,kc);
  1123. for k := 1 to kc do begin
  1124. Dec(pu);
  1125. locStack[locStackIdx + k] := pu^;
  1126. end;
  1127. locStackIdx := locStackIdx + kc;
  1128. end;
  1129. procedure AddResult(const AChar : Cardinal);inline;
  1130. begin
  1131. Inc(ResultIdx);
  1132. ResultBuffer[ResultIdx] := AChar;
  1133. end;
  1134. function PopStack() : Cardinal;inline;
  1135. begin
  1136. Result := locStack[locStackIdx];
  1137. Dec(locStackIdx);
  1138. end;
  1139. var
  1140. cu : Cardinal;
  1141. decIdx : SmallInt;
  1142. locIsWord : Boolean;
  1143. i : Integer;
  1144. p : PUnicodeChar;
  1145. begin
  1146. ResultIdx := -1;
  1147. locStackIdx := -1;
  1148. AddCompositionToStack(ADecomposeIndex);
  1149. while (locStackIdx >= 0) do begin
  1150. cu := PopStack();
  1151. locIsWord := (cu <= MAX_WORD);
  1152. if locIsWord then
  1153. decIdx := GetProps(Word(cu))^.DecompositionID
  1154. else
  1155. decIdx := GetProps(cu)^.DecompositionID;
  1156. if (decIdx = -1) then
  1157. AddResult(cu)
  1158. else
  1159. AddCompositionToStack(decIdx);
  1160. end;
  1161. p := ABuffer;
  1162. Result := 0;
  1163. for i := 0 to ResultIdx do begin
  1164. cu := ResultBuffer[i];
  1165. if (cu <= MAX_WORD) then begin
  1166. p[0] := UnicodeChar(Word(cu));
  1167. Inc(p);
  1168. end else begin
  1169. FromUCS4(cu,p[0],p[1]);
  1170. Inc(p,2);
  1171. Inc(Result);
  1172. end;
  1173. end;
  1174. Result := Result + ResultIdx + 1;
  1175. end;
  1176. procedure CanonicalOrder(var AString : UnicodeString);
  1177. begin
  1178. CanonicalOrder(@AString[1],Length(AString));
  1179. end;
  1180. procedure CanonicalOrder(AStr : PUnicodeChar; const ALength : SizeInt);
  1181. var
  1182. i, c : SizeInt;
  1183. p, q : PUnicodeChar;
  1184. locIsSurrogateP, locIsSurrogateQ : Boolean;
  1185. procedure Swap();
  1186. var
  1187. t, t1 : UnicodeChar;
  1188. begin
  1189. if not locIsSurrogateP then begin
  1190. if not locIsSurrogateQ then begin
  1191. t := p^;
  1192. p^ := q^;
  1193. q^ := t;
  1194. exit;
  1195. end;
  1196. t := p^;
  1197. p[0] := q[0];
  1198. p[1] := q[1];
  1199. q[1] := t;
  1200. exit;
  1201. end;
  1202. if not locIsSurrogateQ then begin
  1203. t := q[0];
  1204. p[2] := p[1];
  1205. p[1] := p[0];
  1206. p[0] := t;
  1207. exit;
  1208. end;
  1209. t := p[0];
  1210. t1 := p[1];
  1211. p[0] := q[0];
  1212. p[1] := q[1];
  1213. q[0] := t;
  1214. q[1] := t1;
  1215. end;
  1216. var
  1217. pu : PUC_Prop;
  1218. cccp, cccq : Byte;
  1219. begin
  1220. c := ALength;
  1221. if (c < 2) then
  1222. exit;
  1223. p := AStr;
  1224. i := 1;
  1225. while (i < c) do begin
  1226. pu := GetProps(Word(p^));
  1227. locIsSurrogateP := (pu^.Category = UGC_Surrogate);
  1228. if locIsSurrogateP then begin
  1229. if (i = (c - 1)) then
  1230. Break;
  1231. if not UnicodeIsSurrogatePair(p[0],p[1]) then begin
  1232. Inc(p);
  1233. Inc(i);
  1234. Continue;
  1235. end;
  1236. pu := GetProps(p[0],p[1]);
  1237. end;
  1238. if (pu^.CCC > 0) then begin
  1239. cccp := pu^.CCC;
  1240. if locIsSurrogateP then
  1241. q := p + 2
  1242. else
  1243. q := p + 1;
  1244. pu := GetProps(Word(q^));
  1245. locIsSurrogateQ := (pu^.Category = UGC_Surrogate);
  1246. if locIsSurrogateQ then begin
  1247. if (i = c) then
  1248. Break;
  1249. if not UnicodeIsSurrogatePair(q[0],q[1]) then begin
  1250. Inc(p);
  1251. Inc(i);
  1252. Continue;
  1253. end;
  1254. pu := GetProps(q[0],q[1]);
  1255. end;
  1256. cccq := pu^.CCC;
  1257. if (cccq > 0) and (cccp > cccq) then begin
  1258. Swap();
  1259. if (i > 1) then begin
  1260. Dec(p);
  1261. Dec(i);
  1262. pu := GetProps(Word(p^));
  1263. if (pu^.Category = UGC_Surrogate) then begin
  1264. if (i > 1) then begin
  1265. Dec(p);
  1266. Dec(i);
  1267. end;
  1268. end;
  1269. Continue;
  1270. end;
  1271. end;
  1272. end;
  1273. if locIsSurrogateP then begin
  1274. Inc(p);
  1275. Inc(i);
  1276. end;
  1277. Inc(p);
  1278. Inc(i);
  1279. end;
  1280. end;
  1281. //Canonical Decomposition
  1282. function NormalizeNFD(const AString : UnicodeString) : UnicodeString;
  1283. begin
  1284. Result := NormalizeNFD(@AString[1],Length(AString));
  1285. end;
  1286. function NormalizeNFD(const AStr : PUnicodeChar; ALength : SizeInt) : UnicodeString;
  1287. const MAX_EXPAND = 3;
  1288. var
  1289. i, c, kc, k : SizeInt;
  1290. pp, pr : PUnicodeChar;
  1291. pu : PUC_Prop;
  1292. locIsSurrogate : Boolean;
  1293. cpArray : array[0..7] of Cardinal;
  1294. cp : Cardinal;
  1295. begin
  1296. c := ALength;
  1297. SetLength(Result,(MAX_EXPAND*c));
  1298. if (c > 0) then begin
  1299. pp := AStr;
  1300. pr := @Result[1];
  1301. i := 1;
  1302. while (i <= c) do begin
  1303. pu := GetProps(Word(pp^));
  1304. locIsSurrogate := (pu^.Category = UGC_Surrogate);
  1305. if locIsSurrogate then begin
  1306. if (i = c) then
  1307. Break;
  1308. if not UnicodeIsSurrogatePair(pp[0],pp[1]) then begin
  1309. pr^ := pp^;
  1310. Inc(pp);
  1311. Inc(pr);
  1312. Inc(i);
  1313. Continue;
  1314. end;
  1315. pu := GetProps(pp[0],pp[1]);
  1316. end;
  1317. if pu^.HangulSyllable then begin
  1318. if locIsSurrogate then begin
  1319. cp := ToUCS4(pp[0],pp[1]);
  1320. Inc(pp);
  1321. Inc(i);
  1322. end else begin
  1323. cp := Word(pp^);
  1324. end;
  1325. kc := DecomposeHangul(cp,@cpArray[0]);
  1326. for k := 0 to kc - 1 do begin
  1327. if (cpArray[k] <= MAX_WORD) then begin
  1328. pr^ := UnicodeChar(Word(cpArray[k]));
  1329. pr := pr + 1;
  1330. end else begin
  1331. FromUCS4(cpArray[k],pr[0],pr[1]);
  1332. pr := pr + 2;
  1333. end;
  1334. end;
  1335. if (kc > 0) then
  1336. Dec(pr);
  1337. end else begin
  1338. if (pu^.DecompositionID = -1) then begin
  1339. pr^ := pp^;
  1340. if locIsSurrogate then begin
  1341. Inc(pp);
  1342. Inc(pr);
  1343. Inc(i);
  1344. pr^ := pp^;
  1345. end;
  1346. end else begin
  1347. k := Decompose(pu^.DecompositionID,pr);
  1348. pr := pr + (k - 1);
  1349. if locIsSurrogate then begin
  1350. Inc(pp);
  1351. Inc(i);
  1352. end;
  1353. end;
  1354. end;
  1355. Inc(pp);
  1356. Inc(pr);
  1357. Inc(i);
  1358. end;
  1359. Dec(pp);
  1360. i := ((PtrUInt(pr) - PtrUInt(@Result[1])) div SizeOf(UnicodeChar));
  1361. SetLength(Result,i);
  1362. CanonicalOrder(@Result[1],Length(Result));
  1363. end;
  1364. end;
  1365. { TUCA_PropItemContextTreeNodeRec }
  1366. function TUCA_PropItemContextTreeNodeRec.GetLeftNode: PUCA_PropItemContextTreeNodeRec;
  1367. begin
  1368. if (Self.Left = 0) then
  1369. Result := nil
  1370. else
  1371. Result := PUCA_PropItemContextTreeNodeRec(PtrUInt(@Self) + Self.Left);
  1372. end;
  1373. function TUCA_PropItemContextTreeNodeRec.GetRightNode: PUCA_PropItemContextTreeNodeRec;
  1374. begin
  1375. if (Self.Right = 0) then
  1376. Result := nil
  1377. else
  1378. Result := PUCA_PropItemContextTreeNodeRec(PtrUInt(@Self) + Self.Right);
  1379. end;
  1380. { TUCA_PropItemContextRec }
  1381. function TUCA_PropItemContextRec.GetCodePoints() : PUInt24;
  1382. begin
  1383. Result := PUInt24(
  1384. PtrUInt(@Self) + SizeOf(Self.CodePointCount) +
  1385. SizeOf(Self.WeightCount)
  1386. );
  1387. end;
  1388. function TUCA_PropItemContextRec.GetWeights: PUCA_PropWeights;
  1389. begin
  1390. Result := PUCA_PropWeights(
  1391. PtrUInt(@Self) +
  1392. SizeOf(Self.CodePointCount) + SizeOf(Self.WeightCount) +
  1393. (Self.CodePointCount*SizeOf(UInt24))
  1394. );
  1395. end;
  1396. { TUCA_PropItemContextTreeRec }
  1397. function TUCA_PropItemContextTreeRec.GetData: PUCA_PropItemContextTreeNodeRec;
  1398. begin
  1399. if (Size = 0) then
  1400. Result := nil
  1401. else
  1402. Result := PUCA_PropItemContextTreeNodeRec(
  1403. PtrUInt(
  1404. PtrUInt(@Self) + SizeOf(UInt24){Size}
  1405. )
  1406. );
  1407. end;
  1408. function CompareCodePoints(
  1409. A : PUInt24; LA : Integer;
  1410. B : PUInt24; LB : Integer
  1411. ) : Integer;
  1412. var
  1413. i, hb : Integer;
  1414. begin
  1415. if (A = B) then
  1416. exit(0);
  1417. Result := 1;
  1418. hb := LB - 1;
  1419. for i := 0 to LA - 1 do begin
  1420. if (i > hb) then
  1421. exit;
  1422. if (A[i] < B[i]) then
  1423. exit(-1);
  1424. if (A[i] > B[i]) then
  1425. exit(1);
  1426. end;
  1427. if (LA = LB) then
  1428. exit(0);
  1429. exit(-1);
  1430. end;
  1431. function TUCA_PropItemContextTreeRec.Find(
  1432. const AChars : PUInt24;
  1433. const ACharCount : Integer;
  1434. out ANode : PUCA_PropItemContextTreeNodeRec
  1435. ) : Boolean;
  1436. var
  1437. t : PUCA_PropItemContextTreeNodeRec;
  1438. begin
  1439. t := Data;
  1440. while (t <> nil) do begin
  1441. case CompareCodePoints(AChars,ACharCount,t^.Data.GetCodePoints(),t^.Data.CodePointCount) of
  1442. 0 : Break;
  1443. -1 : t := t^.GetLeftNode();
  1444. else
  1445. t := t^.GetRightNode();
  1446. end;
  1447. end;
  1448. Result := (t <> nil);
  1449. if Result then
  1450. ANode := t;
  1451. end;
  1452. { TUC_Prop }
  1453. function TUC_Prop.GetCategory: Byte;
  1454. begin
  1455. Result := Byte((CategoryData and Byte($F8)) shr 3);
  1456. end;
  1457. function TUC_Prop.GetNumericValue: Double;
  1458. begin
  1459. Result := UC_NUMERIC_ARRAY[NumericIndex];
  1460. end;
  1461. procedure TUC_Prop.SetCategory(AValue: Byte);
  1462. begin
  1463. CategoryData := Byte(CategoryData or Byte(AValue shl 3));
  1464. end;
  1465. function TUC_Prop.GetWhiteSpace: Boolean;
  1466. begin
  1467. Result := IsBitON(CategoryData,0);
  1468. end;
  1469. procedure TUC_Prop.SetWhiteSpace(AValue: Boolean);
  1470. begin
  1471. SetBit(CategoryData,0,AValue);
  1472. end;
  1473. function TUC_Prop.GetHangulSyllable: Boolean;
  1474. begin
  1475. Result := IsBitON(CategoryData,1);
  1476. end;
  1477. procedure TUC_Prop.SetHangulSyllable(AValue: Boolean);
  1478. begin
  1479. SetBit(CategoryData,1,AValue);
  1480. end;
  1481. { TUCA_DataBook }
  1482. function TUCA_DataBook.IsVariable(const AWeight: PUCA_PropWeights): Boolean;
  1483. begin
  1484. Result := (AWeight^.Weights[0] >= Self.VariableLowLimit) and
  1485. (AWeight^.Weights[0] <= Self.VariableHighLimit);
  1486. end;
  1487. { TUCA_PropItemRec }
  1488. function TUCA_PropItemRec.IsWeightCompress_1 : Boolean;
  1489. begin
  1490. Result := IsBitON(Flags,FLAG_COMPRESS_WEIGHT_1);
  1491. end;
  1492. function TUCA_PropItemRec.IsWeightCompress_2 : Boolean;
  1493. begin
  1494. Result := IsBitON(Flags,FLAG_COMPRESS_WEIGHT_2);
  1495. end;
  1496. function TUCA_PropItemRec.GetCodePoint() : UInt24;
  1497. begin
  1498. if HasCodePoint() then begin
  1499. if Contextual then
  1500. Result := Unaligned(
  1501. PUInt24(
  1502. PtrUInt(@Self) + Self.GetSelfOnlySize()- SizeOf(UInt24) -
  1503. Cardinal(GetContext()^.Size)
  1504. )^
  1505. )
  1506. else
  1507. Result := Unaligned(PUInt24(PtrUInt(@Self) + Self.GetSelfOnlySize() - SizeOf(UInt24))^)
  1508. end else begin
  1509. {$ifdef uni_debug}
  1510. raise EUnicodeException.Create('TUCA_PropItemRec.GetCodePoint : "No code point available."');
  1511. {$else uni_debug}
  1512. Result := ZERO_UINT24;
  1513. {$endif uni_debug}
  1514. end
  1515. end;
  1516. function TUCA_PropItemRec.HasCodePoint() : Boolean;
  1517. begin
  1518. Result := IsBitON(Flags,FLAG_CODEPOINT);
  1519. end;
  1520. function TUCA_PropItemRec.IsValid() : Boolean;
  1521. begin
  1522. Result := IsBitON(Flags,FLAG_VALID);
  1523. end;
  1524. {function TUCA_PropItemRec.GetWeightArray: PUCA_PropWeights;
  1525. begin
  1526. Result := PUCA_PropWeights(PtrUInt(@Self) + SizeOf(TUCA_PropItemRec));
  1527. end;}
  1528. procedure TUCA_PropItemRec.GetWeightArray(ADest: PUCA_PropWeights);
  1529. var
  1530. c : Integer;
  1531. p : PByte;
  1532. pd : PUCA_PropWeights;
  1533. begin
  1534. c := WeightLength;
  1535. p := PByte(PtrUInt(@Self) + SizeOf(TUCA_PropItemRec));
  1536. pd := ADest;
  1537. pd^.Weights[0] := Unaligned(PWord(p)^);
  1538. p := p + 2;
  1539. if not IsWeightCompress_1() then begin
  1540. pd^.Weights[1] := Unaligned(PWord(p)^);
  1541. p := p + 2;
  1542. end else begin
  1543. pd^.Weights[1] := p^;
  1544. p := p + 1;
  1545. end;
  1546. if not IsWeightCompress_2() then begin
  1547. pd^.Weights[2] := Unaligned(PWord(p)^);
  1548. p := p + 2;
  1549. end else begin
  1550. pd^.Weights[2] := p^;
  1551. p := p + 1;
  1552. end;
  1553. if (c > 1) then
  1554. Move(p^, (pd+1)^, ((c-1)*SizeOf(TUCA_PropWeights)));
  1555. end;
  1556. function TUCA_PropItemRec.GetSelfOnlySize() : Cardinal;
  1557. begin
  1558. Result := SizeOf(TUCA_PropItemRec);
  1559. if (WeightLength > 0) then begin
  1560. Result := Result + (WeightLength * Sizeof(TUCA_PropWeights));
  1561. if IsWeightCompress_1() then
  1562. Result := Result - 1;
  1563. if IsWeightCompress_2() then
  1564. Result := Result - 1;
  1565. end;
  1566. if HasCodePoint() then
  1567. Result := Result + SizeOf(UInt24);
  1568. if Contextual then
  1569. Result := Result + Cardinal(GetContext()^.Size);
  1570. end;
  1571. function TUCA_PropItemRec.GetContextual: Boolean;
  1572. begin
  1573. Result := IsBitON(Flags,FLAG_CONTEXTUAL);
  1574. end;
  1575. function TUCA_PropItemRec.GetContext: PUCA_PropItemContextTreeRec;
  1576. var
  1577. p : PtrUInt;
  1578. begin
  1579. if not Contextual then
  1580. exit(nil);
  1581. p := PtrUInt(@Self) + SizeOf(TUCA_PropItemRec);
  1582. if IsBitON(Flags,FLAG_CODEPOINT) then
  1583. p := p + SizeOf(UInt24);
  1584. Result := PUCA_PropItemContextTreeRec(p);
  1585. end;
  1586. function TUCA_PropItemRec.IsDeleted() : Boolean;
  1587. begin
  1588. Result := IsBitON(Flags,FLAG_DELETION);
  1589. end;
  1590. function GetPropUCA(const AChar : UnicodeChar; const ABook : PUCA_DataBook) : PUCA_PropItemRec;
  1591. var
  1592. i : Cardinal;
  1593. begin
  1594. if (ABook^.BMP_Table2 = nil) then
  1595. exit(nil);
  1596. i := ABook^.BMP_Table2[
  1597. (ABook^.BMP_Table1[Hi(Word(AChar))] * 256) +
  1598. Lo(Word(AChar))
  1599. ];
  1600. if (i > 0) then
  1601. Result:= PUCA_PropItemRec(PtrUInt(ABook^.Props) + i - 1)
  1602. else
  1603. Result := nil;
  1604. end;
  1605. function GetPropUCA(const AHighS, ALowS : UnicodeChar; const ABook : PUCA_DataBook): PUCA_PropItemRec;
  1606. var
  1607. i : Cardinal;
  1608. begin
  1609. if (ABook^.OBMP_Table2 = nil) then
  1610. exit(nil);
  1611. i := ABook^.OBMP_Table2[
  1612. (ABook^.OBMP_Table1[Word(AHighS)-HIGH_SURROGATE_BEGIN] * HIGH_SURROGATE_COUNT) +
  1613. Word(ALowS) - LOW_SURROGATE_BEGIN
  1614. ];
  1615. if (i > 0) then
  1616. Result:= PUCA_PropItemRec(PtrUInt(ABook^.Props) + i - 1)
  1617. else
  1618. Result := nil;
  1619. end;
  1620. {$include weight_derivation.inc}
  1621. function CompareSortKey(const A : TUCASortKey; const B : array of Word) : Integer;
  1622. var
  1623. bb : TUCASortKey;
  1624. begin
  1625. SetLength(bb,Length(B));
  1626. if (Length(bb) > 0) then
  1627. Move(B[0],bb[0],(Length(bb)*SizeOf(B[0])));
  1628. Result := CompareSortKey(A,bb);
  1629. end;
  1630. function CompareSortKey(const A, B : TUCASortKey) : Integer;
  1631. var
  1632. i, hb : Integer;
  1633. begin
  1634. if (Pointer(A) = Pointer(B)) then
  1635. exit(0);
  1636. Result := 1;
  1637. hb := Length(B) - 1;
  1638. for i := 0 to Length(A) - 1 do begin
  1639. if (i > hb) then
  1640. exit;
  1641. if (A[i] < B[i]) then
  1642. exit(-1);
  1643. if (A[i] > B[i]) then
  1644. exit(1);
  1645. end;
  1646. if (Length(A) = Length(B)) then
  1647. exit(0);
  1648. exit(-1);
  1649. end;
  1650. type
  1651. TUCA_PropWeightsArray = array of TUCA_PropWeights;
  1652. function FormKeyBlanked(const ACEList : TUCA_PropWeightsArray; const ACollation : PUCA_DataBook) : TUCASortKey;
  1653. var
  1654. r : TUCASortKey;
  1655. i, c, k, ral, levelCount : Integer;
  1656. pce : PUCA_PropWeights;
  1657. begin
  1658. c := Length(ACEList);
  1659. if (c = 0) then
  1660. exit(nil);
  1661. levelCount := Length(ACEList[0].Weights);
  1662. SetLength(r,(levelCount*c + levelCount));
  1663. ral := 0;
  1664. for i := 0 to levelCount - 1 do begin
  1665. if not ACollation^.Backwards[i] then begin
  1666. pce := @ACEList[0];
  1667. for k := 0 to c - 1 do begin
  1668. if not(ACollation^.IsVariable(pce)) and (pce^.Weights[i] <> 0) then begin
  1669. r[ral] := pce^.Weights[i];
  1670. ral := ral + 1;
  1671. end;
  1672. pce := pce + 1;
  1673. end;
  1674. end else begin
  1675. pce := @ACEList[c-1];
  1676. for k := 0 to c - 1 do begin
  1677. if not(ACollation^.IsVariable(pce)) and (pce^.Weights[i] <> 0) then begin
  1678. r[ral] := pce^.Weights[i];
  1679. ral := ral + 1;
  1680. end;
  1681. pce := pce - 1;
  1682. end;
  1683. end;
  1684. r[ral] := 0;
  1685. ral := ral + 1;
  1686. end;
  1687. ral := ral - 1;
  1688. SetLength(r,ral);
  1689. Result := r;
  1690. end;
  1691. function FormKeyNonIgnorable(const ACEList : TUCA_PropWeightsArray; const ACollation : PUCA_DataBook) : TUCASortKey;
  1692. var
  1693. r : TUCASortKey;
  1694. i, c, k, ral, levelCount : Integer;
  1695. pce : PUCA_PropWeights;
  1696. begin
  1697. c := Length(ACEList);
  1698. if (c = 0) then
  1699. exit(nil);
  1700. levelCount := Length(ACEList[0].Weights);
  1701. SetLength(r,(levelCount*c + levelCount));
  1702. ral := 0;
  1703. for i := 0 to levelCount - 1 do begin
  1704. if not ACollation^.Backwards[i] then begin
  1705. pce := @ACEList[0];
  1706. for k := 0 to c - 1 do begin
  1707. if (pce^.Weights[i] <> 0) then begin
  1708. r[ral] := pce^.Weights[i];
  1709. ral := ral + 1;
  1710. end;
  1711. pce := pce + 1;
  1712. end;
  1713. end else begin
  1714. pce := @ACEList[c-1];
  1715. for k := 0 to c - 1 do begin
  1716. if (pce^.Weights[i] <> 0) then begin
  1717. r[ral] := pce^.Weights[i];
  1718. ral := ral + 1;
  1719. end;
  1720. pce := pce - 1;
  1721. end;
  1722. end;
  1723. r[ral] := 0;
  1724. ral := ral + 1;
  1725. end;
  1726. ral := ral - 1;
  1727. SetLength(r,ral);
  1728. Result := r;
  1729. end;
  1730. function FormKeyShifted(const ACEList : TUCA_PropWeightsArray; const ACollation : PUCA_DataBook) : TUCASortKey;
  1731. var
  1732. r : TUCASortKey;
  1733. i, c, k, ral, levelCount : Integer;
  1734. pce : PUCA_PropWeights;
  1735. variableState : Boolean;
  1736. begin
  1737. c := Length(ACEList);
  1738. if (c = 0) then
  1739. exit(nil);
  1740. levelCount := Length(ACEList[0].Weights);
  1741. SetLength(r,(levelCount*c + levelCount));
  1742. ral := 0;
  1743. for i := 0 to levelCount - 1 do begin
  1744. if not ACollation^.Backwards[i] then begin
  1745. variableState := False;
  1746. pce := @ACEList[0];
  1747. for k := 0 to c - 1 do begin
  1748. if not ACollation^.IsVariable(pce) then begin
  1749. if (pce^.Weights[0] <> 0) then
  1750. variableState := False;
  1751. if (pce^.Weights[i] <> 0) and not(variableState) then begin
  1752. r[ral] := pce^.Weights[i];
  1753. ral := ral + 1;
  1754. end;
  1755. end else begin
  1756. variableState := True;
  1757. end;
  1758. pce := pce + 1;
  1759. end;
  1760. end else begin
  1761. pce := @ACEList[c-1];
  1762. for k := 0 to c - 1 do begin
  1763. if not ACollation^.IsVariable(pce) then begin
  1764. if (pce^.Weights[0] <> 0) then
  1765. variableState := False;
  1766. if (pce^.Weights[i] <> 0) and not(variableState) then begin
  1767. r[ral] := pce^.Weights[i];
  1768. ral := ral + 1;
  1769. end;
  1770. end else begin
  1771. variableState := True;
  1772. end;
  1773. pce := pce - 1;
  1774. end;
  1775. end;
  1776. r[ral] := 0;
  1777. ral := ral + 1;
  1778. end;
  1779. ral := ral - 1;
  1780. SetLength(r,ral);
  1781. Result := r;
  1782. end;
  1783. function FormKeyShiftedTrimmed(
  1784. const ACEList : TUCA_PropWeightsArray;
  1785. const ACollation : PUCA_DataBook
  1786. ) : TUCASortKey;
  1787. var
  1788. i : Integer;
  1789. p : ^TUCASortKeyItem;
  1790. begin
  1791. Result := FormKeyShifted(ACEList,ACollation);
  1792. i := Length(Result) - 1;
  1793. if (i >= 0) then begin
  1794. p := @Result[i];
  1795. while (i >= 0) do begin
  1796. if (p^ <> $FFFF) then
  1797. Break;
  1798. Dec(i);
  1799. Dec(p);
  1800. end;
  1801. if ((i+1) < Length(Result)) then
  1802. SetLength(Result,(i+1));
  1803. end;
  1804. end;
  1805. function FindChild(
  1806. const ACodePoint : Cardinal;
  1807. const AParent : PUCA_PropItemRec
  1808. ) : PUCA_PropItemRec;inline;
  1809. var
  1810. k : Integer;
  1811. begin
  1812. Result := PUCA_PropItemRec(PtrUInt(AParent) + AParent^.GetSelfOnlySize());
  1813. for k := 0 to AParent^.ChildCount - 1 do begin
  1814. if (ACodePoint = Result^.CodePoint) then
  1815. exit;
  1816. Result := PUCA_PropItemRec(PtrUInt(Result) + Result^.Size);
  1817. end;
  1818. Result := nil;
  1819. end;
  1820. function ComputeSortKey(
  1821. const AString : UnicodeString;
  1822. const ACollation : PUCA_DataBook
  1823. ) : TUCASortKey;
  1824. begin
  1825. Result := ComputeSortKey(@AString[1],Length(AString),ACollation);
  1826. end;
  1827. function ComputeRawSortKey(
  1828. const AStr : PUnicodeChar;
  1829. const ALength : SizeInt;
  1830. const ACollation : PUCA_DataBook
  1831. ) : TUCA_PropWeightsArray;
  1832. var
  1833. r : TUCA_PropWeightsArray;
  1834. ral {used length of "r"}: Integer;
  1835. rl {capacity of "r"} : Integer;
  1836. procedure GrowKey(const AMinGrow : Integer = 0);inline;
  1837. begin
  1838. if (rl < AMinGrow) then
  1839. rl := rl + AMinGrow
  1840. else
  1841. rl := 2 * rl;
  1842. SetLength(r,rl);
  1843. end;
  1844. var
  1845. i : Integer;
  1846. s : UnicodeString;
  1847. ps : PUnicodeChar;
  1848. cp : Cardinal;
  1849. cl : PUCA_DataBook;
  1850. pp : PUCA_PropItemRec;
  1851. ppLevel : Byte;
  1852. removedCharIndex : array of DWord;
  1853. removedCharIndexLength : DWord;
  1854. locHistory : array[0..24] of record
  1855. i : Integer;
  1856. cl : PUCA_DataBook;
  1857. pp : PUCA_PropItemRec;
  1858. ppLevel : Byte;
  1859. cp : Cardinal;
  1860. removedCharIndexLength : DWord;
  1861. end;
  1862. locHistoryTop : Integer;
  1863. suppressState : record
  1864. cl : PUCA_DataBook;
  1865. CharCount : Integer;
  1866. end;
  1867. LastKeyOwner : record
  1868. Length : Integer;
  1869. Chars : array[0..24] of UInt24;
  1870. end;
  1871. procedure SaveKeyOwner();
  1872. var
  1873. k : Integer;
  1874. kppLevel : Byte;
  1875. begin
  1876. k := 0;
  1877. kppLevel := High(Byte);
  1878. while (k <= locHistoryTop) do begin
  1879. if (kppLevel <> locHistory[k].ppLevel) then begin
  1880. LastKeyOwner.Chars[k] := locHistory[k].cp;
  1881. kppLevel := locHistory[k].ppLevel;
  1882. end;
  1883. k := k + 1;
  1884. end;
  1885. if (k = 0) or (kppLevel <> ppLevel) then begin
  1886. LastKeyOwner.Chars[k] := cp;
  1887. k := k + 1;
  1888. end;
  1889. LastKeyOwner.Length := k;
  1890. end;
  1891. procedure AddWeights(AItem : PUCA_PropItemRec);inline;
  1892. begin
  1893. SaveKeyOwner();
  1894. if ((ral + AItem^.WeightLength) > rl) then
  1895. GrowKey(AItem^.WeightLength);
  1896. AItem^.GetWeightArray(@r[ral]);
  1897. ral := ral + AItem^.WeightLength;
  1898. end;
  1899. procedure AddContextWeights(AItem : PUCA_PropItemContextRec);inline;
  1900. begin
  1901. if ((ral + AItem^.WeightCount) > rl) then
  1902. GrowKey(AItem^.WeightCount);
  1903. Move(AItem^.GetWeights()^,r[ral],(AItem^.WeightCount*SizeOf(r[0])));
  1904. ral := ral + AItem^.WeightCount;
  1905. end;
  1906. procedure AddComputedWeights(ACodePoint : Cardinal);inline;
  1907. begin
  1908. SaveKeyOwner();
  1909. if ((ral + 2) > rl) then
  1910. GrowKey();
  1911. DeriveWeight(ACodePoint,@r[ral]);
  1912. ral := ral + 2;
  1913. end;
  1914. procedure RecordDeletion();inline;
  1915. begin
  1916. if pp^.IsValid() and pp^.IsDeleted() (*pp^.GetWeightLength() = 0*) then begin
  1917. if (suppressState.cl = nil) or
  1918. (suppressState.CharCount > ppLevel)
  1919. then begin
  1920. suppressState.cl := cl;
  1921. suppressState.CharCount := ppLevel;
  1922. end;
  1923. end;
  1924. end;
  1925. procedure RecordStep();inline;
  1926. begin
  1927. Inc(locHistoryTop);
  1928. locHistory[locHistoryTop].i := i;
  1929. locHistory[locHistoryTop].cl := cl;
  1930. locHistory[locHistoryTop].pp := pp;
  1931. locHistory[locHistoryTop].ppLevel := ppLevel;
  1932. locHistory[locHistoryTop].cp := cp;
  1933. locHistory[locHistoryTop].removedCharIndexLength := removedCharIndexLength;
  1934. RecordDeletion();
  1935. end;
  1936. procedure ClearHistory();inline;
  1937. begin
  1938. locHistoryTop := -1;
  1939. end;
  1940. function HasHistory() : Boolean;inline;
  1941. begin
  1942. Result := (locHistoryTop >= 0);
  1943. end;
  1944. function GetHistoryLength() : Integer;inline;
  1945. begin
  1946. Result := (locHistoryTop + 1);
  1947. end;
  1948. procedure GoBack();inline;
  1949. begin
  1950. Assert(locHistoryTop >= 0);
  1951. i := locHistory[locHistoryTop].i;
  1952. cp := locHistory[locHistoryTop].cp;
  1953. cl := locHistory[locHistoryTop].cl;
  1954. pp := locHistory[locHistoryTop].pp;
  1955. ppLevel := locHistory[locHistoryTop].ppLevel;
  1956. removedCharIndexLength := locHistory[locHistoryTop].removedCharIndexLength;
  1957. ps := @s[i];
  1958. Dec(locHistoryTop);
  1959. end;
  1960. var
  1961. c : Integer;
  1962. lastUnblockedNonstarterCCC : Byte;
  1963. function IsUnblockedNonstarter(const AStartFrom : Integer) : Boolean;
  1964. var
  1965. k : DWord;
  1966. pk : PUnicodeChar;
  1967. puk : PUC_Prop;
  1968. begin
  1969. k := AStartFrom;
  1970. if (k > c) then
  1971. exit(False);
  1972. if (removedCharIndexLength>0) and
  1973. (IndexDWord(removedCharIndex[0],removedCharIndexLength,k) >= 0)
  1974. then begin
  1975. exit(False);
  1976. end;
  1977. {if (k = (i+1)) or
  1978. ( (k = (i+2)) and UnicodeIsHighSurrogate(s[i]) )
  1979. then
  1980. lastUnblockedNonstarterCCC := 0;}
  1981. pk := @s[k];
  1982. if UnicodeIsHighSurrogate(pk^) then begin
  1983. if (k = c) then
  1984. exit(False);
  1985. if UnicodeIsLowSurrogate(pk[1]) then
  1986. puk := GetProps(pk[0],pk[1])
  1987. else
  1988. puk := GetProps(Word(pk^));
  1989. end else begin
  1990. puk := GetProps(Word(pk^));
  1991. end;
  1992. if (puk^.CCC = 0) or (lastUnblockedNonstarterCCC >= puk^.CCC) then
  1993. exit(False);
  1994. lastUnblockedNonstarterCCC := puk^.CCC;
  1995. Result := True;
  1996. end;
  1997. procedure RemoveChar(APos : Integer);inline;
  1998. begin
  1999. if (removedCharIndexLength >= Length(removedCharIndex)) then
  2000. SetLength(removedCharIndex,(2*removedCharIndexLength + 2));
  2001. removedCharIndex[removedCharIndexLength] := APos;
  2002. Inc(removedCharIndexLength);
  2003. if UnicodeIsHighSurrogate(s[APos]) and (APos < c) and UnicodeIsLowSurrogate(s[APos+1]) then begin
  2004. if (removedCharIndexLength >= Length(removedCharIndex)) then
  2005. SetLength(removedCharIndex,(2*removedCharIndexLength + 2));
  2006. removedCharIndex[removedCharIndexLength] := APos+1;
  2007. Inc(removedCharIndexLength);
  2008. end;
  2009. end;
  2010. procedure Inc_I();inline;
  2011. begin
  2012. if (removedCharIndexLength = 0) then begin
  2013. Inc(i);
  2014. Inc(ps);
  2015. exit;
  2016. end;
  2017. while True do begin
  2018. Inc(i);
  2019. Inc(ps);
  2020. if (IndexDWord(removedCharIndex[0],removedCharIndexLength,i) = -1) then
  2021. Break;
  2022. end;
  2023. end;
  2024. var
  2025. surrogateState : Boolean;
  2026. function MoveToNextChar() : Boolean;inline;
  2027. begin
  2028. Result := True;
  2029. if UnicodeIsHighSurrogate(ps[0]) then begin
  2030. if (i = c) then
  2031. exit(False);
  2032. if UnicodeIsLowSurrogate(ps[1]) then begin
  2033. surrogateState := True;
  2034. cp := ToUCS4(ps[0],ps[1]);
  2035. end else begin
  2036. surrogateState := False;
  2037. cp := Word(ps[0]);
  2038. end;
  2039. end else begin
  2040. surrogateState := False;
  2041. cp := Word(ps[0]);
  2042. end;
  2043. end;
  2044. procedure ClearPP(const AClearSuppressInfo : Boolean = True);inline;
  2045. begin
  2046. cl := nil;
  2047. pp := nil;
  2048. ppLevel := 0;
  2049. if AClearSuppressInfo then begin
  2050. suppressState.cl := nil;
  2051. suppressState.CharCount := 0;
  2052. end;
  2053. end;
  2054. function FindPropUCA() : Boolean;
  2055. var
  2056. candidateCL : PUCA_DataBook;
  2057. begin
  2058. pp := nil;
  2059. if (cl = nil) then
  2060. candidateCL := ACollation
  2061. else
  2062. candidateCL := cl;
  2063. if surrogateState then begin
  2064. while (candidateCL <> nil) do begin
  2065. pp := GetPropUCA(ps[0],ps[1],candidateCL);
  2066. if (pp <> nil) then
  2067. break;
  2068. candidateCL := candidateCL^.Base;
  2069. end;
  2070. end else begin
  2071. while (candidateCL <> nil) do begin
  2072. pp := GetPropUCA(ps[0],candidateCL);
  2073. if (pp <> nil) then
  2074. break;
  2075. candidateCL := candidateCL^.Base;
  2076. end;
  2077. end;
  2078. cl := candidateCL;
  2079. Result := (pp <> nil);
  2080. end;
  2081. procedure AddWeightsAndClear();inline;
  2082. var
  2083. ctxNode : PUCA_PropItemContextTreeNodeRec;
  2084. begin
  2085. if (pp^.WeightLength > 0) then begin
  2086. AddWeights(pp);
  2087. end else
  2088. if (LastKeyOwner.Length > 0) and pp^.Contextual and
  2089. pp^.GetContext()^.Find(@LastKeyOwner.Chars[0],LastKeyOwner.Length,ctxNode) and
  2090. (ctxNode^.Data.WeightCount > 0)
  2091. then begin
  2092. AddContextWeights(@ctxNode^.Data);
  2093. end;
  2094. //AddWeights(pp);
  2095. ClearHistory();
  2096. ClearPP();
  2097. end;
  2098. procedure StartMatch();
  2099. procedure HandleLastChar();
  2100. var
  2101. ctxNode : PUCA_PropItemContextTreeNodeRec;
  2102. begin
  2103. while True do begin
  2104. if pp^.IsValid() then begin
  2105. if (pp^.WeightLength > 0) then
  2106. AddWeights(pp)
  2107. else
  2108. if (LastKeyOwner.Length > 0) and pp^.Contextual and
  2109. pp^.GetContext()^.Find(@LastKeyOwner.Chars[0],LastKeyOwner.Length,ctxNode) and
  2110. (ctxNode^.Data.WeightCount > 0)
  2111. then
  2112. AddContextWeights(@ctxNode^.Data)
  2113. else
  2114. AddComputedWeights(cp){handle deletion of code point};
  2115. break;
  2116. end;
  2117. if (cl^.Base = nil) then begin
  2118. AddComputedWeights(cp);
  2119. break;
  2120. end;
  2121. cl := cl^.Base;
  2122. if not FindPropUCA() then begin
  2123. AddComputedWeights(cp);
  2124. break;
  2125. end;
  2126. end;
  2127. end;
  2128. var
  2129. tmpCtxNode : PUCA_PropItemContextTreeNodeRec;
  2130. begin
  2131. ppLevel := 0;
  2132. if not FindPropUCA() then begin
  2133. AddComputedWeights(cp);
  2134. ClearHistory();
  2135. ClearPP();
  2136. end else begin
  2137. if (i = c) then begin
  2138. HandleLastChar();
  2139. end else begin
  2140. if pp^.IsValid()then begin
  2141. if (pp^.ChildCount = 0) then begin
  2142. if (pp^.WeightLength > 0) then
  2143. AddWeights(pp)
  2144. else
  2145. if (LastKeyOwner.Length > 0) and pp^.Contextual and
  2146. pp^.GetContext()^.Find(@LastKeyOwner.Chars[0],LastKeyOwner.Length,tmpCtxNode) and
  2147. (tmpCtxNode^.Data.WeightCount > 0)
  2148. then
  2149. AddContextWeights(@tmpCtxNode^.Data)
  2150. else
  2151. AddComputedWeights(cp){handle deletion of code point};
  2152. ClearPP();
  2153. ClearHistory();
  2154. end else begin
  2155. RecordStep();
  2156. end
  2157. end else begin
  2158. if (pp^.ChildCount = 0) then begin
  2159. AddComputedWeights(cp);
  2160. ClearPP();
  2161. ClearHistory();
  2162. end else begin
  2163. RecordStep();
  2164. end;
  2165. end ;
  2166. end;
  2167. end;
  2168. end;
  2169. function TryPermutation() : Boolean;
  2170. var
  2171. kk : Integer;
  2172. b : Boolean;
  2173. puk : PUC_Prop;
  2174. ppk : PUCA_PropItemRec;
  2175. begin
  2176. Result := False;
  2177. puk := GetProps(cp);
  2178. if (puk^.CCC = 0) then
  2179. exit;
  2180. lastUnblockedNonstarterCCC := puk^.CCC;
  2181. if surrogateState then
  2182. kk := i + 2
  2183. else
  2184. kk := i + 1;
  2185. while IsUnblockedNonstarter(kk) do begin
  2186. b := UnicodeIsHighSurrogate(s[kk]) and (kk<c) and UnicodeIsLowSurrogate(s[kk+1]);
  2187. if b then
  2188. ppk := FindChild(ToUCS4(s[kk],s[kk+1]),pp)
  2189. else
  2190. ppk := FindChild(Word(s[kk]),pp);
  2191. if (ppk <> nil) then begin
  2192. pp := ppk;
  2193. RemoveChar(kk);
  2194. Inc(ppLevel);
  2195. RecordStep();
  2196. Result := True;
  2197. if (pp^.ChildCount = 0 ) then
  2198. Break;
  2199. end;
  2200. if b then
  2201. Inc(kk);
  2202. Inc(kk);
  2203. end;
  2204. end;
  2205. procedure AdvanceCharPos();inline;
  2206. begin
  2207. if UnicodeIsHighSurrogate(ps[0]) and (i<c) and UnicodeIsLowSurrogate(ps[1]) then begin
  2208. Inc(i);
  2209. Inc(ps);
  2210. end;
  2211. Inc_I();
  2212. end;
  2213. var
  2214. ok : Boolean;
  2215. pp1 : PUCA_PropItemRec;
  2216. cltemp : PUCA_DataBook;
  2217. ctxNode : PUCA_PropItemContextTreeNodeRec;
  2218. begin
  2219. if (ALength = 0) then
  2220. exit(nil);
  2221. c := ALength;
  2222. s := NormalizeNFD(AStr,c);
  2223. c := Length(s);
  2224. rl := 3*c;
  2225. SetLength(r,rl);
  2226. ral := 0;
  2227. ps := @s[1];
  2228. ClearPP();
  2229. locHistoryTop := -1;
  2230. removedCharIndexLength := 0;
  2231. FillByte(suppressState,SizeOf(suppressState),0);
  2232. LastKeyOwner.Length := 0;
  2233. i := 1;
  2234. while (i <= c) and MoveToNextChar() do begin
  2235. if (pp = nil) then begin // Start Matching
  2236. StartMatch();
  2237. end else begin
  2238. pp1 := FindChild(cp,pp);
  2239. if (pp1 <> nil) then begin
  2240. Inc(ppLevel);
  2241. pp := pp1;
  2242. if (pp^.ChildCount = 0) or (i = c) then begin
  2243. ok := False;
  2244. if pp^.IsValid() and (suppressState.CharCount = 0) then begin
  2245. if (pp^.WeightLength > 0) then begin
  2246. AddWeightsAndClear();
  2247. ok := True;
  2248. end else
  2249. if (LastKeyOwner.Length > 0) and pp^.Contextual and
  2250. pp^.GetContext()^.Find(@LastKeyOwner.Chars[0],LastKeyOwner.Length,ctxNode) and
  2251. (ctxNode^.Data.WeightCount > 0)
  2252. then begin
  2253. AddContextWeights(@ctxNode^.Data);
  2254. ClearHistory();
  2255. ClearPP();
  2256. ok := True;
  2257. end
  2258. end;
  2259. if not ok then begin
  2260. RecordDeletion();
  2261. ok := False;
  2262. while HasHistory() do begin
  2263. GoBack();
  2264. if pp^.IsValid() and
  2265. ( ( (cl = suppressState.cl) and (ppLevel <> suppressState.CharCount) ) or
  2266. ( (cl <> suppressState.cl) and (ppLevel < suppressState.CharCount) )
  2267. )
  2268. then begin
  2269. AddWeightsAndClear();
  2270. ok := True;
  2271. Break;
  2272. end;
  2273. end;
  2274. if not ok then begin
  2275. cltemp := cl^.Base;
  2276. if (cltemp <> nil) then begin
  2277. ClearPP(False);
  2278. cl := cltemp;
  2279. Continue;
  2280. end;
  2281. end;
  2282. if not ok then begin
  2283. AddComputedWeights(cp);
  2284. ClearHistory();
  2285. ClearPP();
  2286. end;
  2287. end;
  2288. end else begin
  2289. RecordStep();
  2290. end;
  2291. end else begin
  2292. // permutations !
  2293. ok := False;
  2294. if TryPermutation() and pp^.IsValid() then begin
  2295. if (suppressState.CharCount = 0) then begin
  2296. AddWeightsAndClear();
  2297. Continue;
  2298. end;
  2299. while True do begin
  2300. if pp^.IsValid() and
  2301. (pp^.WeightLength > 0) and
  2302. ( ( (cl = suppressState.cl) and (ppLevel <> suppressState.CharCount) ) or
  2303. ( (cl <> suppressState.cl) and (ppLevel < suppressState.CharCount) )
  2304. )
  2305. then begin
  2306. AddWeightsAndClear();
  2307. ok := True;
  2308. break;
  2309. end;
  2310. if not HasHistory() then
  2311. break;
  2312. GoBack();
  2313. if (pp = nil) then
  2314. break;
  2315. end;
  2316. end;
  2317. if not ok then begin
  2318. if pp^.IsValid() and (suppressState.CharCount = 0) then begin
  2319. if (pp^.WeightLength > 0) then begin
  2320. AddWeightsAndClear();
  2321. ok := True;
  2322. end else
  2323. if (LastKeyOwner.Length > 0) and pp^.Contextual and
  2324. pp^.GetContext()^.Find(@LastKeyOwner.Chars[0],LastKeyOwner.Length,ctxNode) and
  2325. (ctxNode^.Data.WeightCount > 0)
  2326. then begin
  2327. AddContextWeights(@ctxNode^.Data);
  2328. ClearHistory();
  2329. ClearPP();
  2330. ok := True;
  2331. end
  2332. end;
  2333. if ok then
  2334. Continue;
  2335. end;
  2336. if not ok then begin
  2337. if (cl^.Base <> nil) then begin
  2338. cltemp := cl^.Base;
  2339. while HasHistory() do
  2340. GoBack();
  2341. pp := nil;
  2342. ppLevel := 0;
  2343. cl := cltemp;
  2344. Continue;
  2345. end;
  2346. //walk back
  2347. ok := False;
  2348. while HasHistory() do begin
  2349. GoBack();
  2350. if pp^.IsValid() and
  2351. (pp^.WeightLength > 0) and
  2352. ( (suppressState.CharCount = 0) or
  2353. ( ( (cl = suppressState.cl) and (ppLevel <> suppressState.CharCount) ) or
  2354. ( (cl <> suppressState.cl) and (ppLevel < suppressState.CharCount) )
  2355. )
  2356. )
  2357. then begin
  2358. AddWeightsAndClear();
  2359. ok := True;
  2360. Break;
  2361. end;
  2362. end;
  2363. if ok then begin
  2364. AdvanceCharPos();
  2365. Continue;
  2366. end;
  2367. if (pp <> nil) then begin
  2368. AddComputedWeights(cp);
  2369. ClearHistory();
  2370. ClearPP();
  2371. end;
  2372. end;
  2373. end;
  2374. end;
  2375. if surrogateState then begin
  2376. Inc(ps);
  2377. Inc(i);
  2378. end;
  2379. //
  2380. Inc_I();
  2381. end;
  2382. SetLength(r,ral);
  2383. Result := r;
  2384. end;
  2385. type
  2386. TComputeKeyContext = record
  2387. Collation : PUCA_DataBook;
  2388. r : TUCA_PropWeightsArray;
  2389. ral {used length of "r"}: Integer;
  2390. rl {capacity of "r"} : Integer;
  2391. i : Integer;
  2392. s : UnicodeString;
  2393. ps : PUnicodeChar;
  2394. cp : Cardinal;
  2395. cl : PUCA_DataBook;
  2396. pp : PUCA_PropItemRec;
  2397. ppLevel : Byte;
  2398. removedCharIndex : array of DWord;
  2399. removedCharIndexLength : DWord;
  2400. locHistoryTop : Integer;
  2401. locHistory : array[0..24] of record
  2402. i : Integer;
  2403. cl : PUCA_DataBook;
  2404. pp : PUCA_PropItemRec;
  2405. ppLevel : Byte;
  2406. cp : Cardinal;
  2407. removedCharIndexLength : DWord;
  2408. end;
  2409. suppressState : record
  2410. cl : PUCA_DataBook;
  2411. CharCount : Integer;
  2412. end;
  2413. LastKeyOwner : record
  2414. Length : Integer;
  2415. Chars : array[0..24] of UInt24;
  2416. end;
  2417. c : Integer;
  2418. lastUnblockedNonstarterCCC : Byte;
  2419. surrogateState : Boolean;
  2420. Finished : Boolean;
  2421. end;
  2422. PComputeKeyContext = ^TComputeKeyContext;
  2423. procedure ClearPP(AContext : PComputeKeyContext; const AClearSuppressInfo : Boolean = True);inline;
  2424. begin
  2425. AContext^.cl := nil;
  2426. AContext^.pp := nil;
  2427. AContext^.ppLevel := 0;
  2428. if AClearSuppressInfo then begin
  2429. AContext^.suppressState.cl := nil;
  2430. AContext^.suppressState.CharCount := 0;
  2431. end;
  2432. end;
  2433. procedure InitContext(
  2434. AContext : PComputeKeyContext;
  2435. const AStr : PUnicodeChar;
  2436. const ALength : SizeInt;
  2437. const ACollation : PUCA_DataBook
  2438. );
  2439. begin
  2440. AContext^.Collation := ACollation;
  2441. AContext^.c := ALength;
  2442. AContext^.s := NormalizeNFD(AStr,AContext^.c);
  2443. AContext^.c := Length(AContext^.s);
  2444. AContext^.rl := 3*AContext^.c;
  2445. SetLength(AContext^.r,AContext^.rl);
  2446. AContext^.ral := 0;
  2447. AContext^.ps := @AContext^.s[1];
  2448. ClearPP(AContext);
  2449. AContext^.locHistoryTop := -1;
  2450. AContext^.removedCharIndexLength := 0;
  2451. FillByte(AContext^.suppressState,SizeOf(AContext^.suppressState),0);
  2452. AContext^.LastKeyOwner.Length := 0;
  2453. AContext^.i := 1;
  2454. AContext^.Finished := False;
  2455. end;
  2456. function FormKey(
  2457. const AWeightArray : TUCA_PropWeightsArray;
  2458. const ACollation : PUCA_DataBook
  2459. ) : TUCASortKey;inline;
  2460. begin
  2461. case ACollation.VariableWeight of
  2462. TUCA_VariableKind.ucaShifted : Result := FormKeyShifted(AWeightArray,ACollation);
  2463. TUCA_VariableKind.ucaBlanked : Result := FormKeyBlanked(AWeightArray,ACollation);
  2464. TUCA_VariableKind.ucaNonIgnorable : Result := FormKeyNonIgnorable(AWeightArray,ACollation);
  2465. TUCA_VariableKind.ucaShiftedTrimmed : Result := FormKeyShiftedTrimmed(AWeightArray,ACollation);
  2466. else
  2467. Result := FormKeyShifted(AWeightArray,ACollation);
  2468. end;
  2469. end;
  2470. function ComputeRawSortKeyNextItem(
  2471. const AContext : PComputeKeyContext
  2472. ) : Boolean;forward;
  2473. function IncrementalCompareString_NonIgnorable(
  2474. const AStrA : PUnicodeChar;
  2475. const ALengthA : SizeInt;
  2476. const AStrB : PUnicodeChar;
  2477. const ALengthB : SizeInt;
  2478. const ACollation : PUCA_DataBook
  2479. ) : Integer;
  2480. var
  2481. ctxA, ctxB : TComputeKeyContext;
  2482. lastKeyIndexA, keyIndexA, lengthMaxA : Integer;
  2483. keyIndexB : Integer;
  2484. keyA, keyB : TUCASortKey;
  2485. begin
  2486. if ( (ALengthA = 0) and (ALengthB = 0) ) or
  2487. ( (PtrUInt(AStrA) = PtrUInt(AStrB)) and
  2488. (ALengthA = ALengthB)
  2489. )
  2490. then
  2491. exit(0);
  2492. if (ALengthA = 0) then
  2493. exit(-1);
  2494. if (ALengthB = 0) then
  2495. exit(1);
  2496. InitContext(@ctxA,AStrA,ALengthA,ACollation);
  2497. InitContext(@ctxB,AStrB,ALengthB,ACollation);
  2498. lastKeyIndexA := -1;
  2499. keyIndexA := -1;
  2500. lengthMaxA := 0;
  2501. keyIndexB := -1;
  2502. while True do begin
  2503. if not ComputeRawSortKeyNextItem(@ctxA) then
  2504. Break;
  2505. if (ctxA.ral = lengthMaxA) then
  2506. Continue;
  2507. lengthMaxA := ctxA.ral;
  2508. keyIndexA := lastKeyIndexA + 1;
  2509. while (keyIndexA < lengthMaxA) and (ctxA.r[keyIndexA].Weights[0] = 0) do begin
  2510. Inc(keyIndexA);
  2511. end;
  2512. if (keyIndexA = lengthMaxA) then begin
  2513. lastKeyIndexA := keyIndexA-1;
  2514. Continue;
  2515. end;
  2516. while (keyIndexA < lengthMaxA) do begin
  2517. if (ctxA.r[keyIndexA].Weights[0] = 0) then begin
  2518. Inc(keyIndexA);
  2519. Continue;
  2520. end;
  2521. Inc(keyIndexB);
  2522. while (ctxB.ral <= keyIndexB) or (ctxB.r[keyIndexB].Weights[0] = 0) do begin
  2523. if (ctxB.ral <= keyIndexB) then begin
  2524. if not ComputeRawSortKeyNextItem(@ctxB) then
  2525. Break;
  2526. Continue;
  2527. end;
  2528. Inc(keyIndexB);
  2529. end;
  2530. if (ctxB.ral <= keyIndexB) then
  2531. exit(1);
  2532. if (ctxA.r[keyIndexA].Weights[0] > ctxB.r[keyIndexB].Weights[0]) then
  2533. exit(1);
  2534. if (ctxA.r[keyIndexA].Weights[0] < ctxB.r[keyIndexB].Weights[0]) then
  2535. exit(-1);
  2536. Inc(keyIndexA);
  2537. end;
  2538. lastKeyIndexA := keyIndexA - 1;
  2539. end;
  2540. //Key(A) is completed !
  2541. Inc(keyIndexB);
  2542. while (ctxB.ral <= keyIndexB) or (ctxB.r[keyIndexB].Weights[0] = 0) do begin
  2543. if (ctxB.ral <= keyIndexB) then begin
  2544. if not ComputeRawSortKeyNextItem(@ctxB) then
  2545. Break;
  2546. Continue;
  2547. end;
  2548. Inc(keyIndexB);
  2549. end;
  2550. if (ctxB.ral > keyIndexB) then begin
  2551. //B has at least one more primary weight that A
  2552. exit(-1);
  2553. end;
  2554. while ComputeRawSortKeyNextItem(@ctxB) do begin
  2555. //
  2556. end;
  2557. //Key(B) is completed !
  2558. keyA := FormKey(ctxA.r,ctxA.Collation);
  2559. keyB := FormKey(ctxB.r,ctxB.Collation);
  2560. Result := CompareSortKey(keyA,keyB);
  2561. end;
  2562. function IncrementalCompareString_Shift(
  2563. const AStrA : PUnicodeChar;
  2564. const ALengthA : SizeInt;
  2565. const AStrB : PUnicodeChar;
  2566. const ALengthB : SizeInt;
  2567. const ACollation : PUCA_DataBook
  2568. ) : Integer;
  2569. var
  2570. ctxA, ctxB : TComputeKeyContext;
  2571. lastKeyIndexA, keyIndexA, lengthMaxA : Integer;
  2572. keyIndexB : Integer;
  2573. keyA, keyB : TUCASortKey;
  2574. begin
  2575. if ( (ALengthA = 0) and (ALengthB = 0) ) or
  2576. ( (PtrUInt(AStrA) = PtrUInt(AStrB)) and
  2577. (ALengthA = ALengthB)
  2578. )
  2579. then
  2580. exit(0);
  2581. if (ALengthA = 0) then
  2582. exit(-1);
  2583. if (ALengthB = 0) then
  2584. exit(1);
  2585. InitContext(@ctxA,AStrA,ALengthA,ACollation);
  2586. InitContext(@ctxB,AStrB,ALengthB,ACollation);
  2587. lastKeyIndexA := -1;
  2588. keyIndexA := -1;
  2589. lengthMaxA := 0;
  2590. keyIndexB := -1;
  2591. while True do begin
  2592. if not ComputeRawSortKeyNextItem(@ctxA) then
  2593. Break;
  2594. if (ctxA.ral = lengthMaxA) then
  2595. Continue;
  2596. lengthMaxA := ctxA.ral;
  2597. keyIndexA := lastKeyIndexA + 1;
  2598. while (keyIndexA < lengthMaxA) and
  2599. ( (ctxA.r[keyIndexA].Weights[0] = 0) or
  2600. ctxA.Collation^.IsVariable(@ctxA.r[keyIndexA].Weights)
  2601. )
  2602. do begin
  2603. Inc(keyIndexA);
  2604. end;
  2605. if (keyIndexA = lengthMaxA) then begin
  2606. lastKeyIndexA := keyIndexA-1;
  2607. Continue;
  2608. end;
  2609. while (keyIndexA < lengthMaxA) do begin
  2610. if (ctxA.r[keyIndexA].Weights[0] = 0) or
  2611. ctxA.Collation^.IsVariable(@ctxA.r[keyIndexA].Weights)
  2612. then begin
  2613. Inc(keyIndexA);
  2614. Continue;
  2615. end;
  2616. Inc(keyIndexB);
  2617. while (ctxB.ral <= keyIndexB) or
  2618. (ctxB.r[keyIndexB].Weights[0] = 0) or
  2619. ctxB.Collation^.IsVariable(@ctxB.r[keyIndexB].Weights)
  2620. do begin
  2621. if (ctxB.ral <= keyIndexB) then begin
  2622. if not ComputeRawSortKeyNextItem(@ctxB) then
  2623. Break;
  2624. Continue;
  2625. end;
  2626. Inc(keyIndexB);
  2627. end;
  2628. if (ctxB.ral <= keyIndexB) then
  2629. exit(1);
  2630. if (ctxA.r[keyIndexA].Weights[0] > ctxB.r[keyIndexB].Weights[0]) then
  2631. exit(1);
  2632. if (ctxA.r[keyIndexA].Weights[0] < ctxB.r[keyIndexB].Weights[0]) then
  2633. exit(-1);
  2634. Inc(keyIndexA);
  2635. end;
  2636. lastKeyIndexA := keyIndexA - 1;
  2637. end;
  2638. //Key(A) is completed !
  2639. Inc(keyIndexB);
  2640. while (ctxB.ral <= keyIndexB) or
  2641. (ctxB.r[keyIndexB].Weights[0] = 0) or
  2642. ctxB.Collation^.IsVariable(@ctxB.r[keyIndexB].Weights)
  2643. do begin
  2644. if (ctxB.ral <= keyIndexB) then begin
  2645. if not ComputeRawSortKeyNextItem(@ctxB) then
  2646. Break;
  2647. Continue;
  2648. end;
  2649. Inc(keyIndexB);
  2650. end;
  2651. if (ctxB.ral > keyIndexB) then begin
  2652. //B has at least one more primary weight that A
  2653. exit(-1);
  2654. end;
  2655. while ComputeRawSortKeyNextItem(@ctxB) do begin
  2656. //
  2657. end;
  2658. //Key(B) is completed !
  2659. keyA := FormKey(ctxA.r,ctxA.Collation);
  2660. keyB := FormKey(ctxB.r,ctxB.Collation);
  2661. Result := CompareSortKey(keyA,keyB);
  2662. end;
  2663. function IncrementalCompareString(
  2664. const AStrA : PUnicodeChar;
  2665. const ALengthA : SizeInt;
  2666. const AStrB : PUnicodeChar;
  2667. const ALengthB : SizeInt;
  2668. const ACollation : PUCA_DataBook
  2669. ) : Integer;
  2670. begin
  2671. case ACollation^.VariableWeight of
  2672. TUCA_VariableKind.ucaNonIgnorable :
  2673. begin
  2674. Result := IncrementalCompareString_NonIgnorable(
  2675. AStrA,ALengthA,AStrB,ALengthB,ACollation
  2676. );
  2677. end;
  2678. TUCA_VariableKind.ucaBlanked,
  2679. TUCA_VariableKind.ucaShiftedTrimmed,
  2680. TUCA_VariableKind.ucaIgnoreSP,
  2681. TUCA_VariableKind.ucaShifted:
  2682. begin
  2683. Result := IncrementalCompareString_Shift(
  2684. AStrA,ALengthA,AStrB,ALengthB,ACollation
  2685. );
  2686. end;
  2687. else
  2688. begin
  2689. Result := IncrementalCompareString_Shift(
  2690. AStrA,ALengthA,AStrB,ALengthB,ACollation
  2691. );
  2692. end;
  2693. end;
  2694. end;
  2695. function IncrementalCompareString(
  2696. const AStrA,
  2697. AStrB : UnicodeString;
  2698. const ACollation : PUCA_DataBook
  2699. ) : Integer;
  2700. begin
  2701. Result := IncrementalCompareString(
  2702. Pointer(AStrA),Length(AStrA),Pointer(AStrB),Length(AStrB),
  2703. ACollation
  2704. );
  2705. end;
  2706. function ComputeRawSortKeyNextItem(
  2707. const AContext : PComputeKeyContext
  2708. ) : Boolean;
  2709. var
  2710. ctx : PComputeKeyContext;
  2711. procedure GrowKey(const AMinGrow : Integer = 0);inline;
  2712. begin
  2713. if (ctx^.rl < AMinGrow) then
  2714. ctx^.rl := ctx^.rl + AMinGrow
  2715. else
  2716. ctx^.rl := 2 * ctx^.rl;
  2717. SetLength(ctx^.r,ctx^.rl);
  2718. end;
  2719. procedure SaveKeyOwner();
  2720. var
  2721. k : Integer;
  2722. kppLevel : Byte;
  2723. begin
  2724. k := 0;
  2725. kppLevel := High(Byte);
  2726. while (k <= ctx^.locHistoryTop) do begin
  2727. if (kppLevel <> ctx^.locHistory[k].ppLevel) then begin
  2728. ctx^.LastKeyOwner.Chars[k] := ctx^.locHistory[k].cp;
  2729. kppLevel := ctx^.locHistory[k].ppLevel;
  2730. end;
  2731. k := k + 1;
  2732. end;
  2733. if (k = 0) or (kppLevel <> ctx^.ppLevel) then begin
  2734. ctx^.LastKeyOwner.Chars[k] := ctx^.cp;
  2735. k := k + 1;
  2736. end;
  2737. ctx^.LastKeyOwner.Length := k;
  2738. end;
  2739. procedure AddWeights(AItem : PUCA_PropItemRec);inline;
  2740. begin
  2741. SaveKeyOwner();
  2742. if ((ctx^.ral + AItem^.WeightLength) > ctx^.rl) then
  2743. GrowKey(AItem^.WeightLength);
  2744. AItem^.GetWeightArray(@ctx^.r[ctx^.ral]);
  2745. ctx^.ral := ctx^.ral + AItem^.WeightLength;
  2746. end;
  2747. procedure AddContextWeights(AItem : PUCA_PropItemContextRec);inline;
  2748. begin
  2749. if ((ctx^.ral + AItem^.WeightCount) > ctx^.rl) then
  2750. GrowKey(AItem^.WeightCount);
  2751. Move(AItem^.GetWeights()^,ctx^.r[ctx^.ral],(AItem^.WeightCount*SizeOf(ctx^.r[0])));
  2752. ctx^.ral := ctx^.ral + AItem^.WeightCount;
  2753. end;
  2754. procedure AddComputedWeights(ACodePoint : Cardinal);inline;
  2755. begin
  2756. SaveKeyOwner();
  2757. if ((ctx^.ral + 2) > ctx^.rl) then
  2758. GrowKey();
  2759. DeriveWeight(ACodePoint,@ctx^.r[ctx^.ral]);
  2760. ctx^.ral := ctx^.ral + 2;
  2761. end;
  2762. procedure RecordDeletion();inline;
  2763. begin
  2764. if ctx^.pp^.IsValid() and ctx^.pp^.IsDeleted() (*pp^.GetWeightLength() = 0*) then begin
  2765. if (ctx^.suppressState.cl = nil) or
  2766. (ctx^.suppressState.CharCount > ctx^.ppLevel)
  2767. then begin
  2768. ctx^.suppressState.cl := ctx^.cl;
  2769. ctx^.suppressState.CharCount := ctx^.ppLevel;
  2770. end;
  2771. end;
  2772. end;
  2773. procedure RecordStep();inline;
  2774. begin
  2775. Inc(ctx^.locHistoryTop);
  2776. ctx^.locHistory[ctx^.locHistoryTop].i := ctx^.i;
  2777. ctx^.locHistory[ctx^.locHistoryTop].cl := ctx^.cl;
  2778. ctx^.locHistory[ctx^.locHistoryTop].pp := ctx^.pp;
  2779. ctx^.locHistory[ctx^.locHistoryTop].ppLevel := ctx^.ppLevel;
  2780. ctx^.locHistory[ctx^.locHistoryTop].cp := ctx^.cp;
  2781. ctx^.locHistory[ctx^.locHistoryTop].removedCharIndexLength := ctx^.removedCharIndexLength;
  2782. RecordDeletion();
  2783. end;
  2784. procedure ClearHistory();inline;
  2785. begin
  2786. ctx^.locHistoryTop := -1;
  2787. end;
  2788. function HasHistory() : Boolean;inline;
  2789. begin
  2790. Result := (ctx^.locHistoryTop >= 0);
  2791. end;
  2792. function GetHistoryLength() : Integer;inline;
  2793. begin
  2794. Result := (ctx^.locHistoryTop + 1);
  2795. end;
  2796. procedure GoBack();inline;
  2797. begin
  2798. Assert(ctx^.locHistoryTop >= 0);
  2799. ctx^.i := ctx^.locHistory[ctx^.locHistoryTop].i;
  2800. ctx^.cp := ctx^.locHistory[ctx^.locHistoryTop].cp;
  2801. ctx^.cl := ctx^.locHistory[ctx^.locHistoryTop].cl;
  2802. ctx^.pp := ctx^.locHistory[ctx^.locHistoryTop].pp;
  2803. ctx^.ppLevel := ctx^.locHistory[ctx^.locHistoryTop].ppLevel;
  2804. ctx^.removedCharIndexLength := ctx^.locHistory[ctx^.locHistoryTop].removedCharIndexLength;
  2805. ctx^.ps := @ctx^.s[ctx^.i];
  2806. Dec(ctx^.locHistoryTop);
  2807. end;
  2808. function IsUnblockedNonstarter(const AStartFrom : Integer) : Boolean;
  2809. var
  2810. k : DWord;
  2811. pk : PUnicodeChar;
  2812. puk : PUC_Prop;
  2813. begin
  2814. k := AStartFrom;
  2815. if (k > ctx^.c) then
  2816. exit(False);
  2817. if (ctx^.removedCharIndexLength>0) and
  2818. (IndexDWord(ctx^.removedCharIndex[0],ctx^.removedCharIndexLength,k) >= 0)
  2819. then begin
  2820. exit(False);
  2821. end;
  2822. {if (k = (i+1)) or
  2823. ( (k = (i+2)) and UnicodeIsHighSurrogate(s[i]) )
  2824. then
  2825. lastUnblockedNonstarterCCC := 0;}
  2826. pk := @ctx^.s[k];
  2827. if UnicodeIsHighSurrogate(pk^) then begin
  2828. if (k = ctx^.c) then
  2829. exit(False);
  2830. if UnicodeIsLowSurrogate(pk[1]) then
  2831. puk := GetProps(pk[0],pk[1])
  2832. else
  2833. puk := GetProps(Word(pk^));
  2834. end else begin
  2835. puk := GetProps(Word(pk^));
  2836. end;
  2837. if (puk^.CCC = 0) or (ctx^.lastUnblockedNonstarterCCC >= puk^.CCC) then
  2838. exit(False);
  2839. ctx^.lastUnblockedNonstarterCCC := puk^.CCC;
  2840. Result := True;
  2841. end;
  2842. procedure RemoveChar(APos : Integer);inline;
  2843. begin
  2844. if (ctx^.removedCharIndexLength >= Length(ctx^.removedCharIndex)) then
  2845. SetLength(ctx^.removedCharIndex,(2*ctx^.removedCharIndexLength + 2));
  2846. ctx^.removedCharIndex[ctx^.removedCharIndexLength] := APos;
  2847. Inc(ctx^.removedCharIndexLength);
  2848. if UnicodeIsHighSurrogate(ctx^.s[APos]) and (APos < ctx^.c) and UnicodeIsLowSurrogate(ctx^.s[APos+1]) then begin
  2849. if (ctx^.removedCharIndexLength >= Length(ctx^.removedCharIndex)) then
  2850. SetLength(ctx^.removedCharIndex,(2*ctx^.removedCharIndexLength + 2));
  2851. ctx^.removedCharIndex[ctx^.removedCharIndexLength] := APos+1;
  2852. Inc(ctx^.removedCharIndexLength);
  2853. end;
  2854. end;
  2855. procedure Inc_I();inline;
  2856. begin
  2857. if (ctx^.removedCharIndexLength = 0) then begin
  2858. Inc(ctx^.i);
  2859. Inc(ctx^.ps);
  2860. exit;
  2861. end;
  2862. while True do begin
  2863. Inc(ctx^.i);
  2864. Inc(ctx^.ps);
  2865. if (IndexDWord(ctx^.removedCharIndex[0],ctx^.removedCharIndexLength,ctx^.i) = -1) then
  2866. Break;
  2867. end;
  2868. end;
  2869. function MoveToNextChar() : Boolean;inline;
  2870. begin
  2871. Result := True;
  2872. if UnicodeIsHighSurrogate(ctx^.ps[0]) then begin
  2873. if (ctx^.i = ctx^.c) then
  2874. exit(False);
  2875. if UnicodeIsLowSurrogate(ctx^.ps[1]) then begin
  2876. ctx^.surrogateState := True;
  2877. ctx^.cp := ToUCS4(ctx^.ps[0],ctx^.ps[1]);
  2878. end else begin
  2879. ctx^.surrogateState := False;
  2880. ctx^.cp := Word(ctx^.ps[0]);
  2881. end;
  2882. end else begin
  2883. ctx^.surrogateState := False;
  2884. ctx^.cp := Word(ctx^.ps[0]);
  2885. end;
  2886. end;
  2887. function FindPropUCA() : Boolean;
  2888. var
  2889. candidateCL : PUCA_DataBook;
  2890. begin
  2891. ctx^.pp := nil;
  2892. if (ctx^.cl = nil) then
  2893. candidateCL := ctx^.Collation
  2894. else
  2895. candidateCL := ctx^.cl;
  2896. if ctx^.surrogateState then begin
  2897. while (candidateCL <> nil) do begin
  2898. ctx^.pp := GetPropUCA(ctx^.ps[0],ctx^.ps[1],candidateCL);
  2899. if (ctx^.pp <> nil) then
  2900. break;
  2901. candidateCL := candidateCL^.Base;
  2902. end;
  2903. end else begin
  2904. while (candidateCL <> nil) do begin
  2905. ctx^.pp := GetPropUCA(ctx^.ps[0],candidateCL);
  2906. if (ctx^.pp <> nil) then
  2907. break;
  2908. candidateCL := candidateCL^.Base;
  2909. end;
  2910. end;
  2911. ctx^.cl := candidateCL;
  2912. Result := (ctx^.pp <> nil);
  2913. end;
  2914. procedure AddWeightsAndClear();inline;
  2915. var
  2916. ctxNode : PUCA_PropItemContextTreeNodeRec;
  2917. begin
  2918. if (ctx^.pp^.WeightLength > 0) then begin
  2919. AddWeights(ctx^.pp);
  2920. end else
  2921. if (ctx^.LastKeyOwner.Length > 0) and ctx^.pp^.Contextual and
  2922. ctx^.pp^.GetContext()^.Find(@ctx^.LastKeyOwner.Chars[0],ctx^.LastKeyOwner.Length,ctxNode) and
  2923. (ctxNode^.Data.WeightCount > 0)
  2924. then begin
  2925. AddContextWeights(@ctxNode^.Data);
  2926. end;
  2927. //AddWeights(pp);
  2928. ClearHistory();
  2929. ClearPP(ctx);
  2930. end;
  2931. function StartMatch() : Boolean;
  2932. procedure HandleLastChar();
  2933. var
  2934. ctxNode : PUCA_PropItemContextTreeNodeRec;
  2935. begin
  2936. while True do begin
  2937. if ctx^.pp^.IsValid() then begin
  2938. if (ctx^.pp^.WeightLength > 0) then
  2939. AddWeights(ctx^.pp)
  2940. else
  2941. if (ctx^.LastKeyOwner.Length > 0) and ctx^.pp^.Contextual and
  2942. ctx^.pp^.GetContext()^.Find(@ctx^.LastKeyOwner.Chars[0],ctx^.LastKeyOwner.Length,ctxNode) and
  2943. (ctxNode^.Data.WeightCount > 0)
  2944. then
  2945. AddContextWeights(@ctxNode^.Data)
  2946. else
  2947. AddComputedWeights(ctx^.cp){handle deletion of code point};
  2948. break;
  2949. end;
  2950. if (ctx^.cl^.Base = nil) then begin
  2951. AddComputedWeights(ctx^.cp);
  2952. break;
  2953. end;
  2954. ctx^.cl := ctx^.cl^.Base;
  2955. if not FindPropUCA() then begin
  2956. AddComputedWeights(ctx^.cp);
  2957. break;
  2958. end;
  2959. end;
  2960. end;
  2961. var
  2962. tmpCtxNode : PUCA_PropItemContextTreeNodeRec;
  2963. begin
  2964. Result := False;
  2965. ctx^.ppLevel := 0;
  2966. if not FindPropUCA() then begin
  2967. AddComputedWeights(ctx^.cp);
  2968. ClearHistory();
  2969. ClearPP(ctx);
  2970. Result := True;
  2971. end else begin
  2972. if (ctx^.i = ctx^.c) then begin
  2973. HandleLastChar();
  2974. Result := True;
  2975. end else begin
  2976. if ctx^.pp^.IsValid()then begin
  2977. if (ctx^.pp^.ChildCount = 0) then begin
  2978. if (ctx^.pp^.WeightLength > 0) then
  2979. AddWeights(ctx^.pp)
  2980. else
  2981. if (ctx^.LastKeyOwner.Length > 0) and ctx^.pp^.Contextual and
  2982. ctx^.pp^.GetContext()^.Find(@ctx^.LastKeyOwner.Chars[0],ctx^.LastKeyOwner.Length,tmpCtxNode) and
  2983. (tmpCtxNode^.Data.WeightCount > 0)
  2984. then
  2985. AddContextWeights(@tmpCtxNode^.Data)
  2986. else
  2987. AddComputedWeights(ctx^.cp){handle deletion of code point};
  2988. ClearPP(ctx);
  2989. ClearHistory();
  2990. Result := True;
  2991. end else begin
  2992. RecordStep();
  2993. end
  2994. end else begin
  2995. if (ctx^.pp^.ChildCount = 0) then begin
  2996. AddComputedWeights(ctx^.cp);
  2997. ClearPP(ctx);
  2998. ClearHistory();
  2999. Result := True;
  3000. end else begin
  3001. RecordStep();
  3002. end;
  3003. end;
  3004. end;
  3005. end;
  3006. end;
  3007. function TryPermutation() : Boolean;
  3008. var
  3009. kk : Integer;
  3010. b : Boolean;
  3011. puk : PUC_Prop;
  3012. ppk : PUCA_PropItemRec;
  3013. begin
  3014. Result := False;
  3015. puk := GetProps(ctx^.cp);
  3016. if (puk^.CCC = 0) then
  3017. exit;
  3018. ctx^.lastUnblockedNonstarterCCC := puk^.CCC;
  3019. if ctx^.surrogateState then
  3020. kk := ctx^.i + 2
  3021. else
  3022. kk := ctx^.i + 1;
  3023. while IsUnblockedNonstarter(kk) do begin
  3024. b := UnicodeIsHighSurrogate(ctx^.s[kk]) and (kk<ctx^.c) and UnicodeIsLowSurrogate(ctx^.s[kk+1]);
  3025. if b then
  3026. ppk := FindChild(ToUCS4(ctx^.s[kk],ctx^.s[kk+1]),ctx^.pp)
  3027. else
  3028. ppk := FindChild(Word(ctx^.s[kk]),ctx^.pp);
  3029. if (ppk <> nil) then begin
  3030. ctx^.pp := ppk;
  3031. RemoveChar(kk);
  3032. Inc(ctx^.ppLevel);
  3033. RecordStep();
  3034. Result := True;
  3035. if (ctx^.pp^.ChildCount = 0 ) then
  3036. Break;
  3037. end;
  3038. if b then
  3039. Inc(kk);
  3040. Inc(kk);
  3041. end;
  3042. end;
  3043. procedure AdvanceCharPos();inline;
  3044. begin
  3045. if UnicodeIsHighSurrogate(ctx^.ps[0]) and (ctx^.i<ctx^.c) and UnicodeIsLowSurrogate(ctx^.ps[1]) then begin
  3046. Inc(ctx^.i);
  3047. Inc(ctx^.ps);
  3048. end;
  3049. Inc_I();
  3050. end;
  3051. var
  3052. ok : Boolean;
  3053. pp1 : PUCA_PropItemRec;
  3054. cltemp : PUCA_DataBook;
  3055. ctxNode : PUCA_PropItemContextTreeNodeRec;
  3056. begin
  3057. if AContext^.Finished then
  3058. exit(False);
  3059. ctx := AContext;
  3060. while (ctx^.i <= ctx^.c) and MoveToNextChar() do begin
  3061. ok := False;
  3062. if (ctx^.pp = nil) then begin // Start Matching
  3063. ok := StartMatch();
  3064. end else begin
  3065. pp1 := FindChild(ctx^.cp,ctx^.pp);
  3066. if (pp1 <> nil) then begin
  3067. Inc(ctx^.ppLevel);
  3068. ctx^.pp := pp1;
  3069. if (ctx^.pp^.ChildCount = 0) or (ctx^.i = ctx^.c) then begin
  3070. ok := False;
  3071. if ctx^.pp^.IsValid() and (ctx^.suppressState.CharCount = 0) then begin
  3072. if (ctx^.pp^.WeightLength > 0) then begin
  3073. AddWeightsAndClear();
  3074. ok := True;
  3075. end else
  3076. if (ctx^.LastKeyOwner.Length > 0) and ctx^.pp^.Contextual and
  3077. ctx^.pp^.GetContext()^.Find(@ctx^.LastKeyOwner.Chars[0],ctx^.LastKeyOwner.Length,ctxNode) and
  3078. (ctxNode^.Data.WeightCount > 0)
  3079. then begin
  3080. AddContextWeights(@ctxNode^.Data);
  3081. ClearHistory();
  3082. ClearPP(ctx);
  3083. ok := True;
  3084. end
  3085. end;
  3086. if not ok then begin
  3087. RecordDeletion();
  3088. while HasHistory() do begin
  3089. GoBack();
  3090. if ctx^.pp^.IsValid() and
  3091. ( ( (ctx^.cl = ctx^.suppressState.cl) and (ctx^.ppLevel <> ctx^.suppressState.CharCount) ) or
  3092. ( (ctx^.cl <> ctx^.suppressState.cl) and (ctx^.ppLevel < ctx^.suppressState.CharCount) )
  3093. )
  3094. then begin
  3095. AddWeightsAndClear();
  3096. ok := True;
  3097. Break;
  3098. end;
  3099. end;
  3100. if not ok then begin
  3101. cltemp := ctx^.cl^.Base;
  3102. if (cltemp <> nil) then begin
  3103. ClearPP(ctx,False);
  3104. ctx^.cl := cltemp;
  3105. Continue;
  3106. end;
  3107. end;
  3108. if not ok then begin
  3109. AddComputedWeights(ctx^.cp);
  3110. ClearHistory();
  3111. ClearPP(ctx);
  3112. ok := True;
  3113. end;
  3114. end;
  3115. end else begin
  3116. RecordStep();
  3117. end;
  3118. end else begin
  3119. // permutations !
  3120. ok := False;
  3121. if TryPermutation() and ctx^.pp^.IsValid() then begin
  3122. if (ctx^.suppressState.CharCount = 0) then begin
  3123. AddWeightsAndClear();
  3124. ok := True;
  3125. exit(True);// Continue;
  3126. end;
  3127. while True do begin
  3128. if ctx^.pp^.IsValid() and
  3129. (ctx^.pp^.WeightLength > 0) and
  3130. ( ( (ctx^.cl = ctx^.suppressState.cl) and (ctx^.ppLevel <> ctx^.suppressState.CharCount) ) or
  3131. ( (ctx^.cl <> ctx^.suppressState.cl) and (ctx^.ppLevel < ctx^.suppressState.CharCount) )
  3132. )
  3133. then begin
  3134. AddWeightsAndClear();
  3135. ok := True;
  3136. break;
  3137. end;
  3138. if not HasHistory() then
  3139. break;
  3140. GoBack();
  3141. if (ctx^.pp = nil) then
  3142. break;
  3143. end;
  3144. end;
  3145. if not ok then begin
  3146. if ctx^.pp^.IsValid() and (ctx^.suppressState.CharCount = 0) then begin
  3147. if (ctx^.pp^.WeightLength > 0) then begin
  3148. AddWeightsAndClear();
  3149. ok := True;
  3150. end else
  3151. if (ctx^.LastKeyOwner.Length > 0) and ctx^.pp^.Contextual and
  3152. ctx^.pp^.GetContext()^.Find(@ctx^.LastKeyOwner.Chars[0],ctx^.LastKeyOwner.Length,ctxNode) and
  3153. (ctxNode^.Data.WeightCount > 0)
  3154. then begin
  3155. AddContextWeights(@ctxNode^.Data);
  3156. ClearHistory();
  3157. ClearPP(ctx);
  3158. ok := True;
  3159. end
  3160. end;
  3161. if ok then
  3162. exit(True);// Continue;
  3163. end;
  3164. if not ok then begin
  3165. if (ctx^.cl^.Base <> nil) then begin
  3166. cltemp := ctx^.cl^.Base;
  3167. while HasHistory() do
  3168. GoBack();
  3169. ctx^.pp := nil;
  3170. ctx^.ppLevel := 0;
  3171. ctx^.cl := cltemp;
  3172. Continue;
  3173. end;
  3174. //walk back
  3175. ok := False;
  3176. while HasHistory() do begin
  3177. GoBack();
  3178. if ctx^.pp^.IsValid() and
  3179. (ctx^.pp^.WeightLength > 0) and
  3180. ( (ctx^.suppressState.CharCount = 0) or
  3181. ( ( (ctx^.cl = ctx^.suppressState.cl) and (ctx^.ppLevel <> ctx^.suppressState.CharCount) ) or
  3182. ( (ctx^.cl <> ctx^.suppressState.cl) and (ctx^.ppLevel < ctx^.suppressState.CharCount) )
  3183. )
  3184. )
  3185. then begin
  3186. AddWeightsAndClear();
  3187. ok := True;
  3188. Break;
  3189. end;
  3190. end;
  3191. if ok then begin
  3192. AdvanceCharPos();
  3193. exit(True);// Continue;
  3194. end;
  3195. if (ctx^.pp <> nil) then begin
  3196. AddComputedWeights(ctx^.cp);
  3197. ClearHistory();
  3198. ClearPP(ctx);
  3199. ok := True;
  3200. end;
  3201. end;
  3202. end;
  3203. end;
  3204. if ctx^.surrogateState then begin
  3205. Inc(ctx^.ps);
  3206. Inc(ctx^.i);
  3207. end;
  3208. //
  3209. Inc_I();
  3210. if ok then
  3211. exit(True);
  3212. end;
  3213. SetLength(ctx^.r,ctx^.ral);
  3214. ctx^.Finished := True;
  3215. Result := True;
  3216. end;
  3217. function ComputeSortKey(
  3218. const AStr : PUnicodeChar;
  3219. const ALength : SizeInt;
  3220. const ACollation : PUCA_DataBook
  3221. ) : TUCASortKey;
  3222. var
  3223. r : TUCA_PropWeightsArray;
  3224. begin
  3225. r := ComputeRawSortKey(AStr,ALength,ACollation);
  3226. Result := FormKey(r,ACollation);
  3227. end;
  3228. end.