unicodedata.pas 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159
  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. unit unicodedata;
  17. {$mode delphi}
  18. {$H+}
  19. {$PACKENUM 1}
  20. {$SCOPEDENUMS ON}
  21. {$pointermath on}
  22. {$define USE_INLINE}
  23. {$warn 4056 off} //Conversion between ordinals and pointers is not portable
  24. { $define uni_debug}
  25. interface
  26. uses
  27. SysUtils;
  28. const
  29. MAX_WORD = High(Word);
  30. LOW_SURROGATE_BEGIN = Word($DC00);
  31. LOW_SURROGATE_END = Word($DFFF);
  32. HIGH_SURROGATE_BEGIN = Word($D800);
  33. HIGH_SURROGATE_END = Word($DBFF);
  34. HIGH_SURROGATE_COUNT = HIGH_SURROGATE_END - HIGH_SURROGATE_BEGIN + 1;
  35. UCS4_HALF_BASE = LongWord($10000);
  36. UCS4_HALF_MASK = Word($3FF);
  37. MAX_LEGAL_UTF32 = $10FFFF;
  38. const
  39. // Unicode General Category
  40. UGC_UppercaseLetter = 0;
  41. UGC_LowercaseLetter = 1;
  42. UGC_TitlecaseLetter = 2;
  43. UGC_ModifierLetter = 3;
  44. UGC_OtherLetter = 4;
  45. UGC_NonSpacingMark = 5;
  46. UGC_CombiningMark = 6;
  47. UGC_EnclosingMark = 7;
  48. UGC_DecimalNumber = 8;
  49. UGC_LetterNumber = 9;
  50. UGC_OtherNumber = 10;
  51. UGC_ConnectPunctuation = 11;
  52. UGC_DashPunctuation = 12;
  53. UGC_OpenPunctuation = 13;
  54. UGC_ClosePunctuation = 14;
  55. UGC_InitialPunctuation = 15;
  56. UGC_FinalPunctuation = 16;
  57. UGC_OtherPunctuation = 17;
  58. UGC_MathSymbol = 18;
  59. UGC_CurrencySymbol = 19;
  60. UGC_ModifierSymbol = 20;
  61. UGC_OtherSymbol = 21;
  62. UGC_SpaceSeparator = 22;
  63. UGC_LineSeparator = 23;
  64. UGC_ParagraphSeparator = 24;
  65. UGC_Control = 25;
  66. UGC_Format = 26;
  67. UGC_Surrogate = 27;
  68. UGC_PrivateUse = 28;
  69. UGC_Unassigned = 29;
  70. type
  71. TUInt24Rec = packed record
  72. public
  73. {$ifdef FPC_LITTLE_ENDIAN}
  74. byte0, byte1, byte2 : Byte;
  75. {$else FPC_LITTLE_ENDIAN}
  76. byte2, byte1, byte0 : Byte;
  77. {$endif FPC_LITTLE_ENDIAN}
  78. public
  79. class operator Implicit(a : TUInt24Rec) : Cardinal;{$ifdef USE_INLINE}inline;{$ENDIF}
  80. class operator Implicit(a : TUInt24Rec) : LongInt;{$ifdef USE_INLINE}inline;{$ENDIF}
  81. class operator Implicit(a : TUInt24Rec) : Word;{$ifdef USE_INLINE}inline;{$ENDIF}
  82. class operator Implicit(a : TUInt24Rec) : Byte;{$ifdef USE_INLINE}inline;{$ENDIF}
  83. class operator Implicit(a : Cardinal) : TUInt24Rec;{$ifdef USE_INLINE}inline;{$ENDIF}
  84. class operator Equal(a, b: TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  85. class operator Equal(a : TUInt24Rec; b : Cardinal): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  86. class operator Equal(a : Cardinal; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  87. class operator Equal(a : TUInt24Rec; b : LongInt): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  88. class operator Equal(a : LongInt; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  89. class operator Equal(a : TUInt24Rec; b : Word): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  90. class operator Equal(a : Word; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  91. class operator Equal(a : TUInt24Rec; b : Byte): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  92. class operator Equal(a : Byte; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  93. class operator NotEqual(a, b: TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  94. class operator NotEqual(a : TUInt24Rec; b : Cardinal): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  95. class operator NotEqual(a : Cardinal; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  96. class operator GreaterThan(a, b: TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  97. class operator GreaterThan(a : TUInt24Rec; b : Cardinal): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  98. class operator GreaterThan(a : Cardinal; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  99. class operator GreaterThanOrEqual(a, b: TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  100. class operator GreaterThanOrEqual(a : TUInt24Rec; b : Cardinal): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  101. class operator GreaterThanOrEqual(a : Cardinal; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  102. class operator LessThan(a, b: TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  103. class operator LessThan(a : TUInt24Rec; b : Cardinal): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  104. class operator LessThan(a : Cardinal; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  105. class operator LessThanOrEqual(a, b: TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  106. class operator LessThanOrEqual(a : TUInt24Rec; b : Cardinal): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  107. class operator LessThanOrEqual(a : Cardinal; b : TUInt24Rec): Boolean;{$ifdef USE_INLINE}inline;{$ENDIF}
  108. end;
  109. UInt24 = TUInt24Rec;
  110. PUInt24 = ^UInt24;
  111. const
  112. ZERO_UINT24 : UInt24 =
  113. {$ifdef FPC_LITTLE_ENDIAN}
  114. (byte0 : 0; byte1 : 0; byte2 : 0;);
  115. {$else FPC_LITTLE_ENDIAN}
  116. (byte2 : 0; byte1 : 0; byte0 : 0;);
  117. {$endif FPC_LITTLE_ENDIAN}
  118. type
  119. EUnicodeException = class(Exception)
  120. end;
  121. PUC_Prop = ^TUC_Prop;
  122. { TUC_Prop }
  123. TUC_Prop = packed record
  124. private
  125. function GetCategory : Byte;inline;
  126. procedure SetCategory(AValue : Byte);
  127. function GetWhiteSpace : Boolean;inline;
  128. procedure SetWhiteSpace(AValue : Boolean);
  129. function GetHangulSyllable : Boolean;inline;
  130. procedure SetHangulSyllable(AValue : Boolean);
  131. function GetNumericValue: Double;inline;
  132. public
  133. CategoryData : Byte;
  134. public
  135. CCC : Byte;
  136. NumericIndex : Byte;
  137. SimpleUpperCase : UInt24;
  138. SimpleLowerCase : UInt24;
  139. DecompositionID : SmallInt;
  140. public
  141. property Category : Byte read GetCategory write SetCategory;
  142. property WhiteSpace : Boolean read GetWhiteSpace write SetWhiteSpace;
  143. property HangulSyllable : Boolean read GetHangulSyllable write SetHangulSyllable;
  144. property NumericValue : Double read GetNumericValue;
  145. end;
  146. type
  147. TUCA_PropWeights = packed record
  148. Weights : array[0..2] of Word;
  149. end;
  150. PUCA_PropWeights = ^TUCA_PropWeights;
  151. TUCA_PropItemContextRec = packed record
  152. public
  153. CodePointCount : Byte;
  154. WeightCount : Byte;
  155. public
  156. function GetCodePoints() : PUInt24;inline;
  157. function GetWeights() : PUCA_PropWeights;inline;
  158. end;
  159. PUCA_PropItemContextRec = ^TUCA_PropItemContextRec;
  160. PUCA_PropItemContextTreeNodeRec = ^TUCA_PropItemContextTreeNodeRec;
  161. TUCA_PropItemContextTreeNodeRec = packed record
  162. public
  163. Left : Word;
  164. Right : Word;
  165. Data : TUCA_PropItemContextRec;
  166. public
  167. function GetLeftNode() : PUCA_PropItemContextTreeNodeRec;inline;
  168. function GetRightNode() : PUCA_PropItemContextTreeNodeRec;inline;
  169. end;
  170. { TUCA_PropItemContextTreeRec }
  171. TUCA_PropItemContextTreeRec = packed record
  172. public
  173. Size : UInt24;
  174. public
  175. function GetData:PUCA_PropItemContextTreeNodeRec;inline;
  176. property Data : PUCA_PropItemContextTreeNodeRec read GetData;
  177. function Find(
  178. const AChars : PUInt24;
  179. const ACharCount : Integer;
  180. out ANode : PUCA_PropItemContextTreeNodeRec
  181. ) : Boolean;
  182. end;
  183. PUCA_PropItemContextTreeRec = ^TUCA_PropItemContextTreeRec;
  184. { TUCA_PropItemRec }
  185. TUCA_PropItemRec = packed record
  186. private
  187. const FLAG_VALID = 0;
  188. const FLAG_CODEPOINT = 1;
  189. const FLAG_CONTEXTUAL = 2;
  190. const FLAG_DELETION = 3;
  191. const FLAG_COMPRESS_WEIGHT_1 = 6;
  192. const FLAG_COMPRESS_WEIGHT_2 = 7;
  193. private
  194. function GetCodePoint() : UInt24;inline;
  195. public
  196. WeightLength : Byte;
  197. ChildCount : Byte;
  198. Size : Word;
  199. Flags : Byte;
  200. public
  201. function HasCodePoint() : Boolean;inline;
  202. property CodePoint : UInt24 read GetCodePoint;
  203. //Weights : array[0..WeightLength] of TUCA_PropWeights;
  204. function IsValid() : Boolean;inline;
  205. //function GetWeightArray() : PUCA_PropWeights;inline;
  206. procedure GetWeightArray(ADest : PUCA_PropWeights);
  207. function GetSelfOnlySize() : Cardinal;inline;
  208. function GetContextual() : Boolean;inline;
  209. property Contextual : Boolean read GetContextual;
  210. function GetContext() : PUCA_PropItemContextTreeRec;
  211. function IsDeleted() : Boolean;inline;
  212. function IsWeightCompress_1() : Boolean;inline;
  213. function IsWeightCompress_2() : Boolean;inline;
  214. end;
  215. PUCA_PropItemRec = ^TUCA_PropItemRec;
  216. TUCA_VariableKind = (
  217. ucaShifted, ucaNonIgnorable, ucaBlanked, ucaShiftedTrimmed,
  218. ucaIgnoreSP // This one is not implemented !
  219. );
  220. TCollationName = string[128];
  221. PUCA_DataBook = ^TUCA_DataBook;
  222. TUCA_DataBook = packed record
  223. public
  224. Base : PUCA_DataBook;
  225. Version : TCollationName;
  226. CollationName : TCollationName;
  227. VariableWeight : TUCA_VariableKind;
  228. Backwards : array[0..3] of Boolean;
  229. BMP_Table1 : PByte;
  230. BMP_Table2 : PUInt24;
  231. OBMP_Table1 : PWord;
  232. OBMP_Table2 : PUInt24;
  233. PropCount : Integer;
  234. Props : PUCA_PropItemRec;
  235. VariableLowLimit : Word;
  236. VariableHighLimit : Word;
  237. public
  238. function IsVariable(const AWeight : PUCA_PropWeights) : Boolean; inline;
  239. end;
  240. TCollationField = (BackWard, VariableLowLimit, VariableHighLimit);
  241. TCollationFields = set of TCollationField;
  242. const
  243. ROOT_COLLATION_NAME = 'DUCET';
  244. procedure FromUCS4(const AValue : UCS4Char; var AHighS, ALowS : UnicodeChar);inline;
  245. function ToUCS4(const AHighS, ALowS : UnicodeChar) : UCS4Char;inline;
  246. function UnicodeIsSurrogatePair(
  247. const AHighSurrogate,
  248. ALowSurrogate : UnicodeChar
  249. ) : Boolean;inline;
  250. function UnicodeIsHighSurrogate(const AValue : UnicodeChar) : Boolean;inline;
  251. function UnicodeIsLowSurrogate(const AValue : UnicodeChar) : Boolean;inline;
  252. function GetProps(const ACodePoint : Word) : PUC_Prop;overload;inline;
  253. function GetProps(const AHighS, ALowS : UnicodeChar): PUC_Prop;overload;inline;
  254. function GetProps(const ACodePoint : Cardinal) : PUC_Prop;overload;inline;
  255. function GetPropUCA(const AHighS, ALowS : UnicodeChar; const ABook : PUCA_DataBook): PUCA_PropItemRec; inline; overload;
  256. function GetPropUCA(const AChar : UnicodeChar; const ABook : PUCA_DataBook) : PUCA_PropItemRec; inline; overload;
  257. function NormalizeNFD(const AString : UnicodeString) : UnicodeString;inline;overload;
  258. function NormalizeNFD(const AStr : PUnicodeChar; ALength : SizeInt) : UnicodeString;overload;
  259. procedure CanonicalOrder(var AString : UnicodeString);inline;overload;
  260. procedure CanonicalOrder(AStr : PUnicodeChar; const ALength : SizeInt);overload;
  261. type
  262. TUCASortKeyItem = Word;
  263. TUCASortKey = array of TUCASortKeyItem;
  264. function ComputeSortKey(
  265. const AString : UnicodeString;
  266. const ACollation : PUCA_DataBook
  267. ) : TUCASortKey;inline;overload;
  268. function ComputeSortKey(
  269. const AStr : PUnicodeChar;
  270. const ALength : SizeInt;
  271. const ACollation : PUCA_DataBook
  272. ) : TUCASortKey;overload;
  273. function CompareSortKey(const A, B : TUCASortKey) : Integer;overload;
  274. function CompareSortKey(const A : TUCASortKey; const B : array of Word) : Integer;overload;
  275. function RegisterCollation(const ACollation : PUCA_DataBook) : Boolean;
  276. function UnregisterCollation(const AName : ansistring): Boolean;
  277. function FindCollation(const AName : ansistring): PUCA_DataBook;overload;
  278. function FindCollation(const AIndex : Integer): PUCA_DataBook;overload;
  279. function GetCollationCount() : Integer;
  280. procedure PrepareCollation(
  281. ACollation : PUCA_DataBook;
  282. const ABaseName : ansistring;
  283. const AChangedFields : TCollationFields
  284. );
  285. resourcestring
  286. SCollationNotFound = 'Collation not found : "%s".';
  287. implementation
  288. uses
  289. unicodenumtable;
  290. type
  291. TCardinalRec = packed record
  292. {$ifdef FPC_LITTLE_ENDIAN}
  293. byte0, byte1, byte2, byte3 : Byte;
  294. {$else FPC_LITTLE_ENDIAN}
  295. byte3, byte2, byte1, byte0 : Byte;
  296. {$endif FPC_LITTLE_ENDIAN}
  297. end;
  298. TWordRec = packed record
  299. {$ifdef FPC_LITTLE_ENDIAN}
  300. byte0, byte1 : Byte;
  301. {$else FPC_LITTLE_ENDIAN}
  302. byte1, byte0 : Byte;
  303. {$endif FPC_LITTLE_ENDIAN}
  304. end;
  305. { TUInt24Rec }
  306. class operator TUInt24Rec.Implicit(a : TUInt24Rec) : Cardinal;
  307. begin
  308. TCardinalRec(Result).byte0 := a.byte0;
  309. TCardinalRec(Result).byte1 := a.byte1;
  310. TCardinalRec(Result).byte2 := a.byte2;
  311. TCardinalRec(Result).byte3 := 0;
  312. end;
  313. class operator TUInt24Rec.Implicit(a : TUInt24Rec) : LongInt;
  314. begin
  315. Result := Cardinal(a);
  316. end;
  317. class operator TUInt24Rec.Implicit(a : TUInt24Rec) : Word;
  318. begin
  319. {$IFOPT R+}
  320. if (a > $FFFF) then
  321. Error(reIntOverflow);
  322. {$ENDIF R+}
  323. TWordRec(Result).byte0 := a.byte0;
  324. TWordRec(Result).byte1 := a.byte1;
  325. end;
  326. class operator TUInt24Rec.Implicit(a : TUInt24Rec) : Byte;
  327. begin
  328. {$IFOPT R+}
  329. if (a > $FF) then
  330. Error(reIntOverflow);
  331. {$ENDIF R+}
  332. Result := a.byte0;
  333. end;
  334. class operator TUInt24Rec.Implicit(a : Cardinal) : TUInt24Rec;
  335. begin
  336. {$IFOPT R+}
  337. if (a > $FFFFFF) then
  338. Error(reIntOverflow);
  339. {$ENDIF R+}
  340. Result.byte0 := TCardinalRec(a).byte0;
  341. Result.byte1 := TCardinalRec(a).byte1;
  342. Result.byte2 := TCardinalRec(a).byte2;
  343. end;
  344. class operator TUInt24Rec.Equal(a, b : TUInt24Rec) : Boolean;
  345. begin
  346. Result := (a.byte0 = b.byte0) and (a.byte1 = b.byte1) and (a.byte2 = b.byte2);
  347. end;
  348. class operator TUInt24Rec.Equal(a : TUInt24Rec; b : Cardinal) : Boolean;
  349. begin
  350. Result := (TCardinalRec(b).byte3 = 0) and
  351. (a.byte0 = TCardinalRec(b).byte0) and
  352. (a.byte1 = TCardinalRec(b).byte1) and
  353. (a.byte2 = TCardinalRec(b).byte2);
  354. end;
  355. class operator TUInt24Rec.Equal(a : Cardinal; b : TUInt24Rec) : Boolean;
  356. begin
  357. Result := (b = a);
  358. end;
  359. class operator TUInt24Rec.Equal(a : TUInt24Rec; b : LongInt) : Boolean;
  360. begin
  361. Result := (LongInt(a) = b);
  362. end;
  363. class operator TUInt24Rec.Equal(a : LongInt; b : TUInt24Rec) : Boolean;
  364. begin
  365. Result := (b = a);
  366. end;
  367. class operator TUInt24Rec.Equal(a : TUInt24Rec; b : Word) : Boolean;
  368. begin
  369. Result := (a.byte2 = 0) and
  370. (a.byte0 = TWordRec(b).byte0) and
  371. (a.byte1 = TWordRec(b).byte1);
  372. end;
  373. class operator TUInt24Rec.Equal(a : Word; b : TUInt24Rec) : Boolean;
  374. begin
  375. Result := (b = a);
  376. end;
  377. class operator TUInt24Rec.Equal(a : TUInt24Rec; b : Byte) : Boolean;
  378. begin
  379. Result := (a.byte2 = 0) and
  380. (a.byte1 = 0) and
  381. (a.byte0 = b);
  382. end;
  383. class operator TUInt24Rec.Equal(a : Byte; b : TUInt24Rec) : Boolean;
  384. begin
  385. Result := (b = a);
  386. end;
  387. class operator TUInt24Rec.NotEqual(a, b : TUInt24Rec) : Boolean;
  388. begin
  389. Result := (a.byte0 <> b.byte0) or (a.byte1 <> b.byte1) or (a.byte2 <> b.byte2);
  390. end;
  391. class operator TUInt24Rec.NotEqual(a : TUInt24Rec; b : Cardinal) : Boolean;
  392. begin
  393. Result := (TCardinalRec(b).byte3 <> 0) or
  394. (a.byte0 <> TCardinalRec(b).byte0) or
  395. (a.byte1 <> TCardinalRec(b).byte1) or
  396. (a.byte2 <> TCardinalRec(b).byte2);
  397. end;
  398. class operator TUInt24Rec.NotEqual(a : Cardinal; b : TUInt24Rec) : Boolean;
  399. begin
  400. Result := (b <> a);
  401. end;
  402. class operator TUInt24Rec.GreaterThan(a, b: TUInt24Rec): Boolean;
  403. begin
  404. Result := (a.byte2 > b.byte2) or
  405. ((a.byte2 = b.byte2) and (a.byte1 > b.byte1)) or
  406. ((a.byte2 = b.byte2) and (a.byte1 = b.byte1) and (a.byte0 > b.byte0));
  407. end;
  408. class operator TUInt24Rec.GreaterThan(a: TUInt24Rec; b: Cardinal): Boolean;
  409. begin
  410. Result := Cardinal(a) > b;
  411. end;
  412. class operator TUInt24Rec.GreaterThan(a: Cardinal; b: TUInt24Rec): Boolean;
  413. begin
  414. Result := a > Cardinal(b);
  415. end;
  416. class operator TUInt24Rec.GreaterThanOrEqual(a, b: TUInt24Rec): Boolean;
  417. begin
  418. Result := (a.byte2 > b.byte2) or
  419. ((a.byte2 = b.byte2) and (a.byte1 > b.byte1)) or
  420. ((a.byte2 = b.byte2) and (a.byte1 = b.byte1) and (a.byte0 >= b.byte0));
  421. end;
  422. class operator TUInt24Rec.GreaterThanOrEqual(a: TUInt24Rec; b: Cardinal): Boolean;
  423. begin
  424. Result := Cardinal(a) >= b;
  425. end;
  426. class operator TUInt24Rec.GreaterThanOrEqual(a: Cardinal; b: TUInt24Rec): Boolean;
  427. begin
  428. Result := a >= Cardinal(b);
  429. end;
  430. class operator TUInt24Rec.LessThan(a, b: TUInt24Rec): Boolean;
  431. begin
  432. Result := (b > a);
  433. end;
  434. class operator TUInt24Rec.LessThan(a: TUInt24Rec; b: Cardinal): Boolean;
  435. begin
  436. Result := Cardinal(a) < b;
  437. end;
  438. class operator TUInt24Rec.LessThan(a: Cardinal; b: TUInt24Rec): Boolean;
  439. begin
  440. Result := a < Cardinal(b);
  441. end;
  442. class operator TUInt24Rec.LessThanOrEqual(a, b: TUInt24Rec): Boolean;
  443. begin
  444. Result := (b >= a);
  445. end;
  446. class operator TUInt24Rec.LessThanOrEqual(a: TUInt24Rec; b: Cardinal): Boolean;
  447. begin
  448. Result := Cardinal(a) <= b;
  449. end;
  450. class operator TUInt24Rec.LessThanOrEqual(a: Cardinal; b: TUInt24Rec): Boolean;
  451. begin
  452. Result := a <= Cardinal(b);
  453. end;
  454. var
  455. CollationTable : array of PUCA_DataBook;
  456. function IndexOfCollation(const AName : string) : Integer;
  457. var
  458. i, c : Integer;
  459. p : Pointer;
  460. begin
  461. c := Length(AName);
  462. p := @AName[1];
  463. for i := 0 to Length(CollationTable) - 1 do begin
  464. if (Length(CollationTable[i]^.CollationName) = c) and
  465. CompareMem(@(CollationTable[i]^.CollationName[1]),p,c)
  466. then
  467. exit(i);
  468. end;
  469. Result := -1;
  470. end;
  471. function RegisterCollation(const ACollation : PUCA_DataBook) : Boolean;
  472. var
  473. i : Integer;
  474. begin
  475. Result := (IndexOfCollation(ACollation^.CollationName) = -1);
  476. if Result then begin
  477. i := Length(CollationTable);
  478. SetLength(CollationTable,(i+1));
  479. CollationTable[i] := ACollation;
  480. end;
  481. end;
  482. function UnregisterCollation(const AName : ansistring): Boolean;
  483. var
  484. i, c : Integer;
  485. begin
  486. i := IndexOfCollation(AName);
  487. Result := (i >= 0);
  488. if Result then begin
  489. c := Length(CollationTable);
  490. if (c = 1) then begin
  491. SetLength(CollationTable,0);
  492. end else begin
  493. CollationTable[i] := CollationTable[c-1];
  494. SetLength(CollationTable,(c-1));
  495. end;
  496. end;
  497. end;
  498. function FindCollation(const AName : ansistring): PUCA_DataBook;overload;
  499. var
  500. i : Integer;
  501. begin
  502. i := IndexOfCollation(AName);
  503. if (i = -1) then
  504. Result := nil
  505. else
  506. Result := CollationTable[i];
  507. end;
  508. function GetCollationCount() : Integer;
  509. begin
  510. Result := Length(CollationTable);
  511. end;
  512. function FindCollation(const AIndex : Integer): PUCA_DataBook;overload;
  513. begin
  514. if (AIndex < 0) or (AIndex >= Length(CollationTable)) then
  515. Result := nil
  516. else
  517. Result := CollationTable[AIndex];
  518. end;
  519. procedure PrepareCollation(
  520. ACollation : PUCA_DataBook;
  521. const ABaseName : ansistring;
  522. const AChangedFields : TCollationFields
  523. );
  524. var
  525. s : ansistring;
  526. p, base : PUCA_DataBook;
  527. begin
  528. if (ABaseName <> '') then
  529. s := ABaseName
  530. else
  531. s := ROOT_COLLATION_NAME;
  532. p := ACollation;
  533. base := FindCollation(s);
  534. if (base = nil) then
  535. raise EUnicodeException.CreateFmt(SCollationNotFound,[s]);
  536. p^.Base := base;
  537. if not(TCollationField.BackWard in AChangedFields) then
  538. p^.Backwards := base^.Backwards;
  539. if not(TCollationField.VariableLowLimit in AChangedFields) then
  540. p^.VariableLowLimit := base^.VariableLowLimit;
  541. if not(TCollationField.VariableHighLimit in AChangedFields) then
  542. p^.VariableLowLimit := base^.VariableHighLimit;
  543. end;
  544. {$INCLUDE unicodedata.inc}
  545. {$IFDEF ENDIAN_LITTLE}
  546. {$INCLUDE unicodedata_le.inc}
  547. {$ENDIF ENDIAN_LITTLE}
  548. {$IFDEF ENDIAN_BIG}
  549. {$INCLUDE unicodedata_be.inc}
  550. {$ENDIF ENDIAN_BIG}
  551. procedure FromUCS4(const AValue : UCS4Char; var AHighS, ALowS : UnicodeChar);inline;
  552. begin
  553. AHighS := UnicodeChar((AValue - $10000) shr 10 + $d800);
  554. ALowS := UnicodeChar((AValue - $10000) and $3ff + $dc00);
  555. end;
  556. function ToUCS4(const AHighS, ALowS : UnicodeChar) : UCS4Char;inline;
  557. begin
  558. Result := (UCS4Char(Word(AHighS)) - HIGH_SURROGATE_BEGIN) shl 10 +
  559. (UCS4Char(Word(ALowS)) - LOW_SURROGATE_BEGIN) + UCS4_HALF_BASE;
  560. end;
  561. function UnicodeIsSurrogatePair(
  562. const AHighSurrogate,
  563. ALowSurrogate : UnicodeChar
  564. ) : Boolean;
  565. begin
  566. Result :=
  567. ( (Word(AHighSurrogate) >= HIGH_SURROGATE_BEGIN) and
  568. (Word(AHighSurrogate) <= HIGH_SURROGATE_END)
  569. ) and
  570. ( (Word(ALowSurrogate) >= LOW_SURROGATE_BEGIN) and
  571. (Word(ALowSurrogate) <= LOW_SURROGATE_END)
  572. )
  573. end;
  574. function UnicodeIsHighSurrogate(const AValue : UnicodeChar) : Boolean;
  575. begin
  576. Result := (Word(AValue) >= HIGH_SURROGATE_BEGIN) and
  577. (Word(AValue) <= HIGH_SURROGATE_END);
  578. end;
  579. function UnicodeIsLowSurrogate(const AValue : UnicodeChar) : Boolean;
  580. begin
  581. Result := (Word(AValue) >= LOW_SURROGATE_BEGIN) and
  582. (Word(AValue) <= LOW_SURROGATE_END);
  583. end;
  584. function GetProps(const ACodePoint : Word) : PUC_Prop;overload;inline;
  585. begin
  586. Result:=
  587. @UC_PROP_ARRAY[
  588. UC_TABLE_3[
  589. UC_TABLE_2[UC_TABLE_1[WordRec(ACodePoint).Hi]]
  590. [WordRec(ACodePoint).Lo shr 4]
  591. ][WordRec(ACodePoint).Lo and $F]
  592. ]; {
  593. @UC_PROP_ARRAY[
  594. UC_TABLE_2[
  595. (UC_TABLE_1[WordRec(ACodePoint).Hi] * 256) +
  596. WordRec(ACodePoint).Lo
  597. ]
  598. ];}
  599. end;
  600. function GetProps(const AHighS, ALowS : UnicodeChar): PUC_Prop;overload;inline;
  601. begin
  602. Result:=
  603. @UC_PROP_ARRAY[
  604. UCO_TABLE_3[
  605. UCO_TABLE_2[UCO_TABLE_1[Word(AHighS)-HIGH_SURROGATE_BEGIN]]
  606. [(Word(ALowS) - LOW_SURROGATE_BEGIN) div 32]
  607. ][(Word(ALowS) - LOW_SURROGATE_BEGIN) mod 32]
  608. ]; {
  609. Result:=
  610. @UC_PROP_ARRAY[
  611. UCO_TABLE_2[
  612. (UCO_TABLE_1[Word(AHighS)-HIGH_SURROGATE_BEGIN] * HIGH_SURROGATE_COUNT) +
  613. Word(ALowS) - LOW_SURROGATE_BEGIN
  614. ]
  615. ]; }
  616. end;
  617. function GetProps(const ACodePoint : Cardinal) : PUC_Prop;inline;
  618. var
  619. l, h : UnicodeChar;
  620. begin
  621. if (ACodePoint <= High(Word)) then
  622. exit(GetProps(Word(ACodePoint)));
  623. FromUCS4(ACodePoint,h,l);
  624. Result := GetProps(h,l);
  625. end;
  626. //----------------------------------------------------------------------
  627. function DecomposeHangul(const AChar : Cardinal; ABuffer : PCardinal) : Integer;
  628. const
  629. SBase = $AC00;
  630. LBase = $1100;
  631. VBase = $1161;
  632. TBase = $11A7;
  633. LCount = 19;
  634. VCount = 21;
  635. TCount = 28;
  636. NCount = VCount * TCount; // 588
  637. SCount = LCount * NCount; // 11172
  638. var
  639. SIndex, L, V, T : Integer;
  640. begin
  641. SIndex := AChar - SBase;
  642. if (SIndex < 0) or (SIndex >= SCount) then begin
  643. ABuffer^ := AChar;
  644. exit(1);
  645. end;
  646. L := LBase + SIndex div NCount;
  647. V := VBase + (SIndex mod NCount) div TCount;
  648. T := TBase + SIndex mod TCount;
  649. ABuffer[0] := L;
  650. ABuffer[1] := V;
  651. Result := 2;
  652. if (T <> TBase) then begin
  653. ABuffer[2] := T;
  654. Inc(Result);
  655. end;
  656. end;
  657. function Decompose(const ADecomposeIndex : Integer; ABuffer : PUnicodeChar) : Integer;
  658. var
  659. locStack : array[0..23] of Cardinal;
  660. locStackIdx : Integer;
  661. ResultBuffer : array[0..23] of Cardinal;
  662. ResultIdx : Integer;
  663. procedure AddCompositionToStack(const AIndex : Integer);
  664. var
  665. pdecIdx : ^TDecompositionIndexRec;
  666. k, kc : Integer;
  667. pu : ^UInt24;
  668. begin
  669. pdecIdx := @(UC_DEC_BOOK_DATA.Index[AIndex]);
  670. pu := @(UC_DEC_BOOK_DATA.CodePoints[pdecIdx^.StartPosition]);
  671. kc := pdecIdx^.Length;
  672. Inc(pu,kc);
  673. for k := 1 to kc do begin
  674. Dec(pu);
  675. locStack[locStackIdx + k] := pu^;
  676. end;
  677. locStackIdx := locStackIdx + kc;
  678. end;
  679. procedure AddResult(const AChar : Cardinal);inline;
  680. begin
  681. Inc(ResultIdx);
  682. ResultBuffer[ResultIdx] := AChar;
  683. end;
  684. function PopStack() : Cardinal;inline;
  685. begin
  686. Result := locStack[locStackIdx];
  687. Dec(locStackIdx);
  688. end;
  689. var
  690. cu : Cardinal;
  691. decIdx : SmallInt;
  692. locIsWord : Boolean;
  693. i : Integer;
  694. p : PUnicodeChar;
  695. begin
  696. ResultIdx := -1;
  697. locStackIdx := -1;
  698. AddCompositionToStack(ADecomposeIndex);
  699. while (locStackIdx >= 0) do begin
  700. cu := PopStack();
  701. locIsWord := (cu <= MAX_WORD);
  702. if locIsWord then
  703. decIdx := GetProps(Word(cu))^.DecompositionID
  704. else
  705. decIdx := GetProps(cu)^.DecompositionID;
  706. if (decIdx = -1) then
  707. AddResult(cu)
  708. else
  709. AddCompositionToStack(decIdx);
  710. end;
  711. p := ABuffer;
  712. Result := 0;
  713. for i := 0 to ResultIdx do begin
  714. cu := ResultBuffer[i];
  715. if (cu <= MAX_WORD) then begin
  716. p[0] := UnicodeChar(Word(cu));
  717. Inc(p);
  718. end else begin
  719. FromUCS4(cu,p[0],p[1]);
  720. Inc(p,2);
  721. Inc(Result);
  722. end;
  723. end;
  724. Result := Result + ResultIdx + 1;
  725. end;
  726. procedure CanonicalOrder(var AString : UnicodeString);
  727. begin
  728. CanonicalOrder(@AString[1],Length(AString));
  729. end;
  730. procedure CanonicalOrder(AStr : PUnicodeChar; const ALength : SizeInt);
  731. var
  732. i, c : SizeInt;
  733. p, q : PUnicodeChar;
  734. locIsSurrogateP, locIsSurrogateQ : Boolean;
  735. procedure Swap();
  736. var
  737. t, t1 : UnicodeChar;
  738. begin
  739. if not locIsSurrogateP then begin
  740. if not locIsSurrogateQ then begin
  741. t := p^;
  742. p^ := q^;
  743. q^ := t;
  744. exit;
  745. end;
  746. t := p^;
  747. p[0] := q[0];
  748. p[1] := q[1];
  749. q[1] := t;
  750. exit;
  751. end;
  752. if not locIsSurrogateQ then begin
  753. t := q[0];
  754. p[2] := p[1];
  755. p[1] := p[0];
  756. p[0] := t;
  757. exit;
  758. end;
  759. t := p[0];
  760. t1 := p[1];
  761. p[0] := q[0];
  762. p[1] := q[1];
  763. q[0] := t;
  764. q[1] := t1;
  765. end;
  766. var
  767. pu : PUC_Prop;
  768. cccp, cccq : Byte;
  769. begin
  770. c := ALength;
  771. if (c < 2) then
  772. exit;
  773. p := AStr;
  774. i := 1;
  775. while (i < c) do begin
  776. pu := GetProps(Word(p^));
  777. locIsSurrogateP := (pu^.Category = UGC_Surrogate);
  778. if locIsSurrogateP then begin
  779. if (i = (c - 1)) then
  780. Break;
  781. if not UnicodeIsSurrogatePair(p[0],p[1]) then begin
  782. Inc(p);
  783. Inc(i);
  784. Continue;
  785. end;
  786. pu := GetProps(p[0],p[1]);
  787. end;
  788. if (pu^.CCC > 0) then begin
  789. cccp := pu^.CCC;
  790. if locIsSurrogateP then
  791. q := p + 2
  792. else
  793. q := p + 1;
  794. pu := GetProps(Word(q^));
  795. locIsSurrogateQ := (pu^.Category = UGC_Surrogate);
  796. if locIsSurrogateQ then begin
  797. if (i = c) then
  798. Break;
  799. if not UnicodeIsSurrogatePair(q[0],q[1]) then begin
  800. Inc(p);
  801. Inc(i);
  802. Continue;
  803. end;
  804. pu := GetProps(q[0],q[1]);
  805. end;
  806. cccq := pu^.CCC;
  807. if (cccq > 0) and (cccp > cccq) then begin
  808. Swap();
  809. if (i > 1) then begin
  810. Dec(p);
  811. Dec(i);
  812. pu := GetProps(Word(p^));
  813. if (pu^.Category = UGC_Surrogate) then begin
  814. if (i > 1) then begin
  815. Dec(p);
  816. Dec(i);
  817. end;
  818. end;
  819. Continue;
  820. end;
  821. end;
  822. end;
  823. if locIsSurrogateP then begin
  824. Inc(p);
  825. Inc(i);
  826. end;
  827. Inc(p);
  828. Inc(i);
  829. end;
  830. end;
  831. //Canonical Decomposition
  832. function NormalizeNFD(const AString : UnicodeString) : UnicodeString;
  833. begin
  834. Result := NormalizeNFD(@AString[1],Length(AString));
  835. end;
  836. function NormalizeNFD(const AStr : PUnicodeChar; ALength : SizeInt) : UnicodeString;
  837. const MAX_EXPAND = 3;
  838. var
  839. i, c, kc, k : SizeInt;
  840. pp, pr : PUnicodeChar;
  841. pu : PUC_Prop;
  842. locIsSurrogate : Boolean;
  843. cpArray : array[0..7] of Cardinal;
  844. cp : Cardinal;
  845. begin
  846. c := ALength;
  847. SetLength(Result,(MAX_EXPAND*c));
  848. if (c > 0) then begin
  849. pp := AStr;
  850. pr := @Result[1];
  851. i := 1;
  852. while (i <= c) do begin
  853. pu := GetProps(Word(pp^));
  854. locIsSurrogate := (pu^.Category = UGC_Surrogate);
  855. if locIsSurrogate then begin
  856. if (i = c) then
  857. Break;
  858. if not UnicodeIsSurrogatePair(pp[0],pp[1]) then begin
  859. pr^ := pp^;
  860. Inc(pp);
  861. Inc(pr);
  862. Inc(i);
  863. Continue;
  864. end;
  865. pu := GetProps(pp[0],pp[1]);
  866. end;
  867. if pu^.HangulSyllable then begin
  868. if locIsSurrogate then begin
  869. cp := ToUCS4(pp[0],pp[1]);
  870. Inc(pp);
  871. Inc(i);
  872. end else begin
  873. cp := Word(pp^);
  874. end;
  875. kc := DecomposeHangul(cp,@cpArray[0]);
  876. for k := 0 to kc - 1 do begin
  877. if (cpArray[k] <= MAX_WORD) then begin
  878. pr^ := UnicodeChar(Word(cpArray[k]));
  879. pr := pr + 1;
  880. end else begin
  881. FromUCS4(cpArray[k],pr[0],pr[1]);
  882. pr := pr + 2;
  883. end;
  884. end;
  885. if (kc > 0) then
  886. Dec(pr);
  887. end else begin
  888. if (pu^.DecompositionID = -1) then begin
  889. pr^ := pp^;
  890. if locIsSurrogate then begin
  891. Inc(pp);
  892. Inc(pr);
  893. Inc(i);
  894. pr^ := pp^;
  895. end;
  896. end else begin
  897. k := Decompose(pu^.DecompositionID,pr);
  898. pr := pr + (k - 1);
  899. if locIsSurrogate then begin
  900. Inc(pp);
  901. Inc(i);
  902. end;
  903. end;
  904. end;
  905. Inc(pp);
  906. Inc(pr);
  907. Inc(i);
  908. end;
  909. Dec(pp);
  910. i := ((PtrUInt(pr) - PtrUInt(@Result[1])) div SizeOf(UnicodeChar));
  911. SetLength(Result,i);
  912. CanonicalOrder(@Result[1],Length(Result));
  913. end;
  914. end;
  915. type
  916. TBitOrder = 0..7;
  917. function IsBitON(const AData : Byte; const ABit : TBitOrder) : Boolean ;inline;
  918. begin
  919. Result := ( ( AData and ( 1 shl ABit ) ) <> 0 );
  920. end;
  921. procedure SetBit(var AData : Byte; const ABit : TBitOrder; const AValue : Boolean);inline;
  922. begin
  923. if AValue then
  924. AData := AData or (1 shl (ABit mod 8))
  925. else
  926. AData := AData and ( not ( 1 shl ( ABit mod 8 ) ) );
  927. end;
  928. { TUCA_PropItemContextTreeNodeRec }
  929. function TUCA_PropItemContextTreeNodeRec.GetLeftNode: PUCA_PropItemContextTreeNodeRec;
  930. begin
  931. if (Self.Left = 0) then
  932. Result := nil
  933. else
  934. Result := PUCA_PropItemContextTreeNodeRec(PtrUInt(@Self) + Self.Left);
  935. end;
  936. function TUCA_PropItemContextTreeNodeRec.GetRightNode: PUCA_PropItemContextTreeNodeRec;
  937. begin
  938. if (Self.Right = 0) then
  939. Result := nil
  940. else
  941. Result := PUCA_PropItemContextTreeNodeRec(PtrUInt(@Self) + Self.Right);
  942. end;
  943. { TUCA_PropItemContextRec }
  944. function TUCA_PropItemContextRec.GetCodePoints() : PUInt24;
  945. begin
  946. Result := PUInt24(
  947. PtrUInt(@Self) + SizeOf(Self.CodePointCount) +
  948. SizeOf(Self.WeightCount)
  949. );
  950. end;
  951. function TUCA_PropItemContextRec.GetWeights: PUCA_PropWeights;
  952. begin
  953. Result := PUCA_PropWeights(
  954. PtrUInt(@Self) +
  955. SizeOf(Self.CodePointCount) + SizeOf(Self.WeightCount) +
  956. (Self.CodePointCount*SizeOf(UInt24))
  957. );
  958. end;
  959. { TUCA_PropItemContextTreeRec }
  960. function TUCA_PropItemContextTreeRec.GetData: PUCA_PropItemContextTreeNodeRec;
  961. begin
  962. if (Size = 0) then
  963. Result := nil
  964. else
  965. Result := PUCA_PropItemContextTreeNodeRec(
  966. PtrUInt(
  967. PtrUInt(@Self) + SizeOf(UInt24){Size}
  968. )
  969. );
  970. end;
  971. function CompareCodePoints(
  972. A : PUInt24; LA : Integer;
  973. B : PUInt24; LB : Integer
  974. ) : Integer;
  975. var
  976. i, hb : Integer;
  977. begin
  978. if (A = B) then
  979. exit(0);
  980. Result := 1;
  981. hb := LB - 1;
  982. for i := 0 to LA - 1 do begin
  983. if (i > hb) then
  984. exit;
  985. if (A[i] < B[i]) then
  986. exit(-1);
  987. if (A[i] > B[i]) then
  988. exit(1);
  989. end;
  990. if (LA = LB) then
  991. exit(0);
  992. exit(-1);
  993. end;
  994. function TUCA_PropItemContextTreeRec.Find(
  995. const AChars : PUInt24;
  996. const ACharCount : Integer;
  997. out ANode : PUCA_PropItemContextTreeNodeRec
  998. ) : Boolean;
  999. var
  1000. t : PUCA_PropItemContextTreeNodeRec;
  1001. begin
  1002. t := Data;
  1003. while (t <> nil) do begin
  1004. case CompareCodePoints(AChars,ACharCount,t^.Data.GetCodePoints(),t^.Data.CodePointCount) of
  1005. 0 : Break;
  1006. -1 : t := t^.GetLeftNode();
  1007. else
  1008. t := t^.GetRightNode();
  1009. end;
  1010. end;
  1011. Result := (t <> nil);
  1012. if Result then
  1013. ANode := t;
  1014. end;
  1015. { TUC_Prop }
  1016. function TUC_Prop.GetCategory: Byte;
  1017. begin
  1018. Result := Byte((CategoryData and Byte($F8)) shr 3);
  1019. end;
  1020. function TUC_Prop.GetNumericValue: Double;
  1021. begin
  1022. Result := UC_NUMERIC_ARRAY[NumericIndex];
  1023. end;
  1024. procedure TUC_Prop.SetCategory(AValue: Byte);
  1025. begin
  1026. CategoryData := Byte(CategoryData or Byte(AValue shl 3));
  1027. end;
  1028. function TUC_Prop.GetWhiteSpace: Boolean;
  1029. begin
  1030. Result := IsBitON(CategoryData,0);
  1031. end;
  1032. procedure TUC_Prop.SetWhiteSpace(AValue: Boolean);
  1033. begin
  1034. SetBit(CategoryData,0,AValue);
  1035. end;
  1036. function TUC_Prop.GetHangulSyllable: Boolean;
  1037. begin
  1038. Result := IsBitON(CategoryData,1);
  1039. end;
  1040. procedure TUC_Prop.SetHangulSyllable(AValue: Boolean);
  1041. begin
  1042. SetBit(CategoryData,1,AValue);
  1043. end;
  1044. { TUCA_DataBook }
  1045. function TUCA_DataBook.IsVariable(const AWeight: PUCA_PropWeights): Boolean;
  1046. begin
  1047. Result := (AWeight^.Weights[0] >= Self.VariableLowLimit) and
  1048. (AWeight^.Weights[0] <= Self.VariableHighLimit);
  1049. end;
  1050. { TUCA_PropItemRec }
  1051. function TUCA_PropItemRec.IsWeightCompress_1 : Boolean;
  1052. begin
  1053. Result := IsBitON(Flags,FLAG_COMPRESS_WEIGHT_1);
  1054. end;
  1055. function TUCA_PropItemRec.IsWeightCompress_2 : Boolean;
  1056. begin
  1057. Result := IsBitON(Flags,FLAG_COMPRESS_WEIGHT_2);
  1058. end;
  1059. function TUCA_PropItemRec.GetCodePoint() : UInt24;
  1060. begin
  1061. if HasCodePoint() then begin
  1062. if Contextual then
  1063. Result := PUInt24(
  1064. PtrUInt(@Self) + Self.GetSelfOnlySize()- SizeOf(UInt24) -
  1065. Cardinal(GetContext()^.Size)
  1066. )^
  1067. else
  1068. Result := PUInt24(PtrUInt(@Self) + Self.GetSelfOnlySize() - SizeOf(UInt24))^
  1069. end else begin
  1070. {$ifdef uni_debug}
  1071. raise EUnicodeException.Create('TUCA_PropItemRec.GetCodePoint : "No code point available."');
  1072. {$else uni_debug}
  1073. Result := ZERO_UINT24;
  1074. {$endif uni_debug}
  1075. end
  1076. end;
  1077. function TUCA_PropItemRec.HasCodePoint() : Boolean;
  1078. begin
  1079. Result := IsBitON(Flags,FLAG_CODEPOINT);
  1080. end;
  1081. function TUCA_PropItemRec.IsValid() : Boolean;
  1082. begin
  1083. Result := IsBitON(Flags,FLAG_VALID);
  1084. end;
  1085. {function TUCA_PropItemRec.GetWeightArray: PUCA_PropWeights;
  1086. begin
  1087. Result := PUCA_PropWeights(PtrUInt(@Self) + SizeOf(TUCA_PropItemRec));
  1088. end;}
  1089. procedure TUCA_PropItemRec.GetWeightArray(ADest: PUCA_PropWeights);
  1090. var
  1091. c : Integer;
  1092. p : PByte;
  1093. pd : PUCA_PropWeights;
  1094. begin
  1095. c := WeightLength;
  1096. p := PByte(PtrUInt(@Self) + SizeOf(TUCA_PropItemRec));
  1097. pd := ADest;
  1098. pd^.Weights[0] := PWord(p)^;
  1099. p := p + 2;
  1100. if not IsWeightCompress_1() then begin
  1101. pd^.Weights[1] := PWord(p)^;
  1102. p := p + 2;
  1103. end else begin
  1104. pd^.Weights[1] := p^;
  1105. p := p + 1;
  1106. end;
  1107. if not IsWeightCompress_2() then begin
  1108. pd^.Weights[2] := PWord(p)^;
  1109. p := p + 2;
  1110. end else begin
  1111. pd^.Weights[2] := p^;
  1112. p := p + 1;
  1113. end;
  1114. if (c > 1) then
  1115. Move(p^, (pd+1)^, ((c-1)*SizeOf(TUCA_PropWeights)));
  1116. end;
  1117. function TUCA_PropItemRec.GetSelfOnlySize() : Cardinal;
  1118. begin
  1119. Result := SizeOf(TUCA_PropItemRec);
  1120. if (WeightLength > 0) then begin
  1121. Result := Result + (WeightLength * Sizeof(TUCA_PropWeights));
  1122. if IsWeightCompress_1() then
  1123. Result := Result - 1;
  1124. if IsWeightCompress_2() then
  1125. Result := Result - 1;
  1126. end;
  1127. if HasCodePoint() then
  1128. Result := Result + SizeOf(UInt24);
  1129. if Contextual then
  1130. Result := Result + Cardinal(GetContext()^.Size);
  1131. end;
  1132. function TUCA_PropItemRec.GetContextual: Boolean;
  1133. begin
  1134. Result := IsBitON(Flags,FLAG_CONTEXTUAL);
  1135. end;
  1136. function TUCA_PropItemRec.GetContext: PUCA_PropItemContextTreeRec;
  1137. var
  1138. p : PtrUInt;
  1139. begin
  1140. if not Contextual then
  1141. exit(nil);
  1142. p := PtrUInt(@Self) + SizeOf(TUCA_PropItemRec);
  1143. if IsBitON(Flags,FLAG_CODEPOINT) then
  1144. p := p + SizeOf(UInt24);
  1145. Result := PUCA_PropItemContextTreeRec(p);
  1146. end;
  1147. function TUCA_PropItemRec.IsDeleted() : Boolean;
  1148. begin
  1149. Result := IsBitON(Flags,FLAG_DELETION);
  1150. end;
  1151. function GetPropUCA(const AChar : UnicodeChar; const ABook : PUCA_DataBook) : PUCA_PropItemRec;
  1152. var
  1153. i : Cardinal;
  1154. begin
  1155. if (ABook^.BMP_Table2 = nil) then
  1156. exit(nil);
  1157. i := ABook^.BMP_Table2[
  1158. (ABook^.BMP_Table1[WordRec(AChar).Hi] * 256) +
  1159. WordRec(AChar).Lo
  1160. ];
  1161. if (i > 0) then
  1162. Result:= PUCA_PropItemRec(PtrUInt(ABook^.Props) + i - 1)
  1163. else
  1164. Result := nil;
  1165. end;
  1166. function GetPropUCA(const AHighS, ALowS : UnicodeChar; const ABook : PUCA_DataBook): PUCA_PropItemRec;
  1167. var
  1168. i : Cardinal;
  1169. begin
  1170. if (ABook^.OBMP_Table2 = nil) then
  1171. exit(nil);
  1172. i := ABook^.OBMP_Table2[
  1173. (ABook^.OBMP_Table1[Word(AHighS)-HIGH_SURROGATE_BEGIN] * HIGH_SURROGATE_COUNT) +
  1174. Word(ALowS) - LOW_SURROGATE_BEGIN
  1175. ];
  1176. if (i > 0) then
  1177. Result:= PUCA_PropItemRec(PtrUInt(ABook^.Props) + i - 1)
  1178. else
  1179. Result := nil;
  1180. end;
  1181. {$include weight_derivation.inc}
  1182. function CompareSortKey(const A : TUCASortKey; const B : array of Word) : Integer;
  1183. var
  1184. bb : TUCASortKey;
  1185. begin
  1186. SetLength(bb,Length(B));
  1187. if (Length(bb) > 0) then
  1188. Move(B[0],bb[0],(Length(bb)*SizeOf(B[0])));
  1189. Result := CompareSortKey(A,bb);
  1190. end;
  1191. function CompareSortKey(const A, B : TUCASortKey) : Integer;
  1192. var
  1193. i, hb : Integer;
  1194. begin
  1195. if (Pointer(A) = Pointer(B)) then
  1196. exit(0);
  1197. Result := 1;
  1198. hb := Length(B) - 1;
  1199. for i := 0 to Length(A) - 1 do begin
  1200. if (i > hb) then
  1201. exit;
  1202. if (A[i] < B[i]) then
  1203. exit(-1);
  1204. if (A[i] > B[i]) then
  1205. exit(1);
  1206. end;
  1207. if (Length(A) = Length(B)) then
  1208. exit(0);
  1209. exit(-1);
  1210. end;
  1211. type
  1212. TUCA_PropWeightsArray = array of TUCA_PropWeights;
  1213. function FormKeyBlanked(const ACEList : TUCA_PropWeightsArray; const ACollation : PUCA_DataBook) : TUCASortKey;
  1214. var
  1215. r : TUCASortKey;
  1216. i, c, k, ral, levelCount : Integer;
  1217. pce : PUCA_PropWeights;
  1218. begin
  1219. c := Length(ACEList);
  1220. if (c = 0) then
  1221. exit(nil);
  1222. levelCount := Length(ACEList[0].Weights);
  1223. SetLength(r,(levelCount*c + levelCount));
  1224. ral := 0;
  1225. for i := 0 to levelCount - 1 do begin
  1226. if not ACollation^.Backwards[i] then begin
  1227. pce := @ACEList[0];
  1228. for k := 0 to c - 1 do begin
  1229. if not(ACollation^.IsVariable(pce)) and (pce^.Weights[i] <> 0) then begin
  1230. r[ral] := pce^.Weights[i];
  1231. ral := ral + 1;
  1232. end;
  1233. pce := pce + 1;
  1234. end;
  1235. end else begin
  1236. pce := @ACEList[c-1];
  1237. for k := 0 to c - 1 do begin
  1238. if not(ACollation^.IsVariable(pce)) and (pce^.Weights[i] <> 0) then begin
  1239. r[ral] := pce^.Weights[i];
  1240. ral := ral + 1;
  1241. end;
  1242. pce := pce - 1;
  1243. end;
  1244. end;
  1245. r[ral] := 0;
  1246. ral := ral + 1;
  1247. end;
  1248. ral := ral - 1;
  1249. SetLength(r,ral);
  1250. Result := r;
  1251. end;
  1252. function FormKeyNonIgnorable(const ACEList : TUCA_PropWeightsArray; const ACollation : PUCA_DataBook) : TUCASortKey;
  1253. var
  1254. r : TUCASortKey;
  1255. i, c, k, ral, levelCount : Integer;
  1256. pce : PUCA_PropWeights;
  1257. begin
  1258. c := Length(ACEList);
  1259. if (c = 0) then
  1260. exit(nil);
  1261. levelCount := Length(ACEList[0].Weights);
  1262. SetLength(r,(levelCount*c + levelCount));
  1263. ral := 0;
  1264. for i := 0 to levelCount - 1 do begin
  1265. if not ACollation^.Backwards[i] then begin
  1266. pce := @ACEList[0];
  1267. for k := 0 to c - 1 do begin
  1268. if (pce^.Weights[i] <> 0) then begin
  1269. r[ral] := pce^.Weights[i];
  1270. ral := ral + 1;
  1271. end;
  1272. pce := pce + 1;
  1273. end;
  1274. end else begin
  1275. pce := @ACEList[c-1];
  1276. for k := 0 to c - 1 do begin
  1277. if (pce^.Weights[i] <> 0) then begin
  1278. r[ral] := pce^.Weights[i];
  1279. ral := ral + 1;
  1280. end;
  1281. pce := pce - 1;
  1282. end;
  1283. end;
  1284. r[ral] := 0;
  1285. ral := ral + 1;
  1286. end;
  1287. ral := ral - 1;
  1288. SetLength(r,ral);
  1289. Result := r;
  1290. end;
  1291. function FormKeyShifted(const ACEList : TUCA_PropWeightsArray; const ACollation : PUCA_DataBook) : TUCASortKey;
  1292. var
  1293. r : TUCASortKey;
  1294. i, c, k, ral, levelCount : Integer;
  1295. pce : PUCA_PropWeights;
  1296. variableState : Boolean;
  1297. begin
  1298. c := Length(ACEList);
  1299. if (c = 0) then
  1300. exit(nil);
  1301. levelCount := Length(ACEList[0].Weights);
  1302. SetLength(r,(levelCount*c + levelCount));
  1303. ral := 0;
  1304. for i := 0 to levelCount - 1 do begin
  1305. if not ACollation^.Backwards[i] then begin
  1306. variableState := False;
  1307. pce := @ACEList[0];
  1308. for k := 0 to c - 1 do begin
  1309. if not ACollation^.IsVariable(pce) then begin
  1310. if (pce^.Weights[0] <> 0) then
  1311. variableState := False;
  1312. if (pce^.Weights[i] <> 0) and not(variableState) then begin
  1313. r[ral] := pce^.Weights[i];
  1314. ral := ral + 1;
  1315. end;
  1316. end else begin
  1317. variableState := True;
  1318. end;
  1319. pce := pce + 1;
  1320. end;
  1321. end else begin
  1322. pce := @ACEList[c-1];
  1323. for k := 0 to c - 1 do begin
  1324. if not ACollation^.IsVariable(pce) then begin
  1325. if (pce^.Weights[0] <> 0) then
  1326. variableState := False;
  1327. if (pce^.Weights[i] <> 0) and not(variableState) then begin
  1328. r[ral] := pce^.Weights[i];
  1329. ral := ral + 1;
  1330. end;
  1331. end else begin
  1332. variableState := True;
  1333. end;
  1334. pce := pce - 1;
  1335. end;
  1336. end;
  1337. r[ral] := 0;
  1338. ral := ral + 1;
  1339. end;
  1340. ral := ral - 1;
  1341. SetLength(r,ral);
  1342. Result := r;
  1343. end;
  1344. function FormKeyShiftedTrimmed(
  1345. const ACEList : TUCA_PropWeightsArray;
  1346. const ACollation : PUCA_DataBook
  1347. ) : TUCASortKey;
  1348. var
  1349. i : Integer;
  1350. p : ^TUCASortKeyItem;
  1351. begin
  1352. Result := FormKeyShifted(ACEList,ACollation);
  1353. i := Length(Result) - 1;
  1354. if (i >= 0) then begin
  1355. p := @Result[i];
  1356. while (i >= 0) do begin
  1357. if (p^ <> $FFFF) then
  1358. Break;
  1359. Dec(i);
  1360. Dec(p);
  1361. end;
  1362. if ((i+1) < Length(Result)) then
  1363. SetLength(Result,(i+1));
  1364. end;
  1365. end;
  1366. function FindChild(
  1367. const ACodePoint : Cardinal;
  1368. const AParent : PUCA_PropItemRec
  1369. ) : PUCA_PropItemRec;inline;
  1370. var
  1371. k : Integer;
  1372. begin
  1373. Result := PUCA_PropItemRec(PtrUInt(AParent) + AParent^.GetSelfOnlySize());
  1374. for k := 0 to AParent^.ChildCount - 1 do begin
  1375. if (ACodePoint = Result^.CodePoint) then
  1376. exit;
  1377. Result := PUCA_PropItemRec(PtrUInt(Result) + Result^.Size);
  1378. end;
  1379. Result := nil;
  1380. end;
  1381. function ComputeSortKey(
  1382. const AString : UnicodeString;
  1383. const ACollation : PUCA_DataBook
  1384. ) : TUCASortKey;
  1385. begin
  1386. Result := ComputeSortKey(@AString[1],Length(AString),ACollation);
  1387. end;
  1388. function ComputeRawSortKey(
  1389. const AStr : PUnicodeChar;
  1390. const ALength : SizeInt;
  1391. const ACollation : PUCA_DataBook
  1392. ) : TUCA_PropWeightsArray;
  1393. var
  1394. r : TUCA_PropWeightsArray;
  1395. ral {used length of "r"}: Integer;
  1396. rl {capacity of "r"} : Integer;
  1397. procedure GrowKey(const AMinGrow : Integer = 0);inline;
  1398. begin
  1399. if (rl < AMinGrow) then
  1400. rl := rl + AMinGrow
  1401. else
  1402. rl := 2 * rl;
  1403. SetLength(r,rl);
  1404. end;
  1405. var
  1406. i : Integer;
  1407. s : UnicodeString;
  1408. ps : PUnicodeChar;
  1409. cp : Cardinal;
  1410. cl : PUCA_DataBook;
  1411. pp : PUCA_PropItemRec;
  1412. ppLevel : Byte;
  1413. removedCharIndex : array of DWord;
  1414. removedCharIndexLength : DWord;
  1415. locHistory : array[0..24] of record
  1416. i : Integer;
  1417. cl : PUCA_DataBook;
  1418. pp : PUCA_PropItemRec;
  1419. ppLevel : Byte;
  1420. cp : Cardinal;
  1421. removedCharIndexLength : DWord;
  1422. end;
  1423. locHistoryTop : Integer;
  1424. suppressState : record
  1425. cl : PUCA_DataBook;
  1426. CharCount : Integer;
  1427. end;
  1428. LastKeyOwner : record
  1429. Length : Integer;
  1430. Chars : array[0..24] of UInt24;
  1431. end;
  1432. procedure SaveKeyOwner();
  1433. var
  1434. k : Integer;
  1435. kppLevel : Byte;
  1436. begin
  1437. k := 0;
  1438. kppLevel := High(Byte);
  1439. while (k <= locHistoryTop) do begin
  1440. if (kppLevel <> locHistory[k].ppLevel) then begin
  1441. LastKeyOwner.Chars[k] := locHistory[k].cp;
  1442. kppLevel := locHistory[k].ppLevel;
  1443. end;
  1444. k := k + 1;
  1445. end;
  1446. if (k = 0) or (kppLevel <> ppLevel) then begin
  1447. LastKeyOwner.Chars[k] := cp;
  1448. k := k + 1;
  1449. end;
  1450. LastKeyOwner.Length := k;
  1451. end;
  1452. procedure AddWeights(AItem : PUCA_PropItemRec);inline;
  1453. begin
  1454. SaveKeyOwner();
  1455. if ((ral + AItem^.WeightLength) > rl) then
  1456. GrowKey(AItem^.WeightLength);
  1457. AItem^.GetWeightArray(@r[ral]);
  1458. ral := ral + AItem^.WeightLength;
  1459. end;
  1460. procedure AddContextWeights(AItem : PUCA_PropItemContextRec);inline;
  1461. begin
  1462. if ((ral + AItem^.WeightCount) > rl) then
  1463. GrowKey(AItem^.WeightCount);
  1464. Move(AItem^.GetWeights()^,r[ral],(AItem^.WeightCount*SizeOf(r[0])));
  1465. ral := ral + AItem^.WeightCount;
  1466. end;
  1467. procedure AddComputedWeights(ACodePoint : Cardinal);inline;
  1468. begin
  1469. SaveKeyOwner();
  1470. if ((ral + 2) > rl) then
  1471. GrowKey();
  1472. DeriveWeight(ACodePoint,@r[ral]);
  1473. ral := ral + 2;
  1474. end;
  1475. procedure RecordDeletion();inline;
  1476. begin
  1477. if pp^.IsValid() and pp^.IsDeleted() (*pp^.GetWeightLength() = 0*) then begin
  1478. if (suppressState.cl = nil) or
  1479. (suppressState.CharCount > ppLevel)
  1480. then begin
  1481. suppressState.cl := cl;
  1482. suppressState.CharCount := ppLevel;
  1483. end;
  1484. end;
  1485. end;
  1486. procedure RecordStep();inline;
  1487. begin
  1488. Inc(locHistoryTop);
  1489. locHistory[locHistoryTop].i := i;
  1490. locHistory[locHistoryTop].cl := cl;
  1491. locHistory[locHistoryTop].pp := pp;
  1492. locHistory[locHistoryTop].ppLevel := ppLevel;
  1493. locHistory[locHistoryTop].cp := cp;
  1494. locHistory[locHistoryTop].removedCharIndexLength := removedCharIndexLength;
  1495. RecordDeletion();
  1496. end;
  1497. procedure ClearHistory();inline;
  1498. begin
  1499. locHistoryTop := -1;
  1500. end;
  1501. function HasHistory() : Boolean;inline;
  1502. begin
  1503. Result := (locHistoryTop >= 0);
  1504. end;
  1505. function GetHistoryLength() : Integer;inline;
  1506. begin
  1507. Result := (locHistoryTop + 1);
  1508. end;
  1509. procedure GoBack();inline;
  1510. begin
  1511. Assert(locHistoryTop >= 0);
  1512. i := locHistory[locHistoryTop].i;
  1513. cp := locHistory[locHistoryTop].cp;
  1514. cl := locHistory[locHistoryTop].cl;
  1515. pp := locHistory[locHistoryTop].pp;
  1516. ppLevel := locHistory[locHistoryTop].ppLevel;
  1517. removedCharIndexLength := locHistory[locHistoryTop].removedCharIndexLength;
  1518. ps := @s[i];
  1519. Dec(locHistoryTop);
  1520. end;
  1521. var
  1522. c : Integer;
  1523. lastUnblockedNonstarterCCC : Byte;
  1524. function IsUnblockedNonstarter(const AStartFrom : Integer) : Boolean;
  1525. var
  1526. k : DWord;
  1527. pk : PUnicodeChar;
  1528. puk : PUC_Prop;
  1529. begin
  1530. k := AStartFrom;
  1531. if (k > c) then
  1532. exit(False);
  1533. if (removedCharIndexLength>0) and
  1534. (IndexDWord(removedCharIndex[0],removedCharIndexLength,k) >= 0)
  1535. then begin
  1536. exit(False);
  1537. end;
  1538. {if (k = (i+1)) or
  1539. ( (k = (i+2)) and UnicodeIsHighSurrogate(s[i]) )
  1540. then
  1541. lastUnblockedNonstarterCCC := 0;}
  1542. pk := @s[k];
  1543. if UnicodeIsHighSurrogate(pk^) then begin
  1544. if (k = c) then
  1545. exit(False);
  1546. if UnicodeIsLowSurrogate(pk[1]) then
  1547. puk := GetProps(pk[0],pk[1])
  1548. else
  1549. puk := GetProps(Word(pk^));
  1550. end else begin
  1551. puk := GetProps(Word(pk^));
  1552. end;
  1553. if (puk^.CCC = 0) or (lastUnblockedNonstarterCCC >= puk^.CCC) then
  1554. exit(False);
  1555. lastUnblockedNonstarterCCC := puk^.CCC;
  1556. Result := True;
  1557. end;
  1558. procedure RemoveChar(APos : Integer);inline;
  1559. begin
  1560. if (removedCharIndexLength >= Length(removedCharIndex)) then
  1561. SetLength(removedCharIndex,(2*removedCharIndexLength + 2));
  1562. removedCharIndex[removedCharIndexLength] := APos;
  1563. Inc(removedCharIndexLength);
  1564. if UnicodeIsHighSurrogate(s[APos]) and (APos < c) and UnicodeIsLowSurrogate(s[APos+1]) then begin
  1565. if (removedCharIndexLength >= Length(removedCharIndex)) then
  1566. SetLength(removedCharIndex,(2*removedCharIndexLength + 2));
  1567. removedCharIndex[removedCharIndexLength] := APos+1;
  1568. Inc(removedCharIndexLength);
  1569. end;
  1570. end;
  1571. procedure Inc_I();inline;
  1572. begin
  1573. if (removedCharIndexLength = 0) then begin
  1574. Inc(i);
  1575. Inc(ps);
  1576. exit;
  1577. end;
  1578. while True do begin
  1579. Inc(i);
  1580. Inc(ps);
  1581. if (IndexDWord(removedCharIndex[0],removedCharIndexLength,i) = -1) then
  1582. Break;
  1583. end;
  1584. end;
  1585. var
  1586. surrogateState : Boolean;
  1587. function MoveToNextChar() : Boolean;inline;
  1588. begin
  1589. Result := True;
  1590. if UnicodeIsHighSurrogate(ps[0]) then begin
  1591. if (i = c) then
  1592. exit(False);
  1593. if UnicodeIsLowSurrogate(ps[1]) then begin
  1594. surrogateState := True;
  1595. cp := ToUCS4(ps[0],ps[1]);
  1596. end else begin
  1597. surrogateState := False;
  1598. cp := Word(ps[0]);
  1599. end;
  1600. end else begin
  1601. surrogateState := False;
  1602. cp := Word(ps[0]);
  1603. end;
  1604. end;
  1605. procedure ClearPP(const AClearSuppressInfo : Boolean = True);inline;
  1606. begin
  1607. cl := nil;
  1608. pp := nil;
  1609. ppLevel := 0;
  1610. if AClearSuppressInfo then begin
  1611. suppressState.cl := nil;
  1612. suppressState.CharCount := 0;
  1613. end;
  1614. end;
  1615. function FindPropUCA() : Boolean;
  1616. var
  1617. candidateCL : PUCA_DataBook;
  1618. begin
  1619. pp := nil;
  1620. if (cl = nil) then
  1621. candidateCL := ACollation
  1622. else
  1623. candidateCL := cl;
  1624. if surrogateState then begin
  1625. while (candidateCL <> nil) do begin
  1626. pp := GetPropUCA(ps[0],ps[1],candidateCL);
  1627. if (pp <> nil) then
  1628. break;
  1629. candidateCL := candidateCL^.Base;
  1630. end;
  1631. end else begin
  1632. while (candidateCL <> nil) do begin
  1633. pp := GetPropUCA(ps[0],candidateCL);
  1634. if (pp <> nil) then
  1635. break;
  1636. candidateCL := candidateCL^.Base;
  1637. end;
  1638. end;
  1639. cl := candidateCL;
  1640. Result := (pp <> nil);
  1641. end;
  1642. procedure AddWeightsAndClear();inline;
  1643. var
  1644. ctxNode : PUCA_PropItemContextTreeNodeRec;
  1645. begin
  1646. if (pp^.WeightLength > 0) then begin
  1647. AddWeights(pp);
  1648. end else
  1649. if (LastKeyOwner.Length > 0) and pp^.Contextual and
  1650. pp^.GetContext()^.Find(@LastKeyOwner.Chars[0],LastKeyOwner.Length,ctxNode) and
  1651. (ctxNode^.Data.WeightCount > 0)
  1652. then begin
  1653. AddContextWeights(@ctxNode^.Data);
  1654. end;
  1655. //AddWeights(pp);
  1656. ClearHistory();
  1657. ClearPP();
  1658. end;
  1659. procedure StartMatch();
  1660. procedure HandleLastChar();
  1661. var
  1662. ctxNode : PUCA_PropItemContextTreeNodeRec;
  1663. begin
  1664. while True do begin
  1665. if pp^.IsValid() then begin
  1666. if (pp^.WeightLength > 0) then
  1667. AddWeights(pp)
  1668. else
  1669. if (LastKeyOwner.Length > 0) and pp^.Contextual and
  1670. pp^.GetContext()^.Find(@LastKeyOwner.Chars[0],LastKeyOwner.Length,ctxNode) and
  1671. (ctxNode^.Data.WeightCount > 0)
  1672. then
  1673. AddContextWeights(@ctxNode^.Data)
  1674. else
  1675. AddComputedWeights(cp){handle deletion of code point};
  1676. break;
  1677. end;
  1678. if (cl^.Base = nil) then begin
  1679. AddComputedWeights(cp);
  1680. break;
  1681. end;
  1682. cl := cl^.Base;
  1683. if not FindPropUCA() then begin
  1684. AddComputedWeights(cp);
  1685. break;
  1686. end;
  1687. end;
  1688. end;
  1689. var
  1690. tmpCtxNode : PUCA_PropItemContextTreeNodeRec;
  1691. begin
  1692. ppLevel := 0;
  1693. if not FindPropUCA() then begin
  1694. AddComputedWeights(cp);
  1695. ClearHistory();
  1696. ClearPP();
  1697. end else begin
  1698. if (i = c) then begin
  1699. HandleLastChar();
  1700. end else begin
  1701. if pp^.IsValid()then begin
  1702. if (pp^.ChildCount = 0) then begin
  1703. if (pp^.WeightLength > 0) then
  1704. AddWeights(pp)
  1705. else
  1706. if (LastKeyOwner.Length > 0) and pp^.Contextual and
  1707. pp^.GetContext()^.Find(@LastKeyOwner.Chars[0],LastKeyOwner.Length,tmpCtxNode) and
  1708. (tmpCtxNode^.Data.WeightCount > 0)
  1709. then
  1710. AddContextWeights(@tmpCtxNode^.Data)
  1711. else
  1712. AddComputedWeights(cp){handle deletion of code point};
  1713. ClearPP();
  1714. ClearHistory();
  1715. end else begin
  1716. RecordStep();
  1717. end
  1718. end else begin
  1719. if (pp^.ChildCount = 0) then begin
  1720. AddComputedWeights(cp);
  1721. ClearPP();
  1722. ClearHistory();
  1723. end else begin
  1724. RecordStep();
  1725. end;
  1726. end ;
  1727. end;
  1728. end;
  1729. end;
  1730. function TryPermutation() : Boolean;
  1731. var
  1732. kk : Integer;
  1733. b : Boolean;
  1734. puk : PUC_Prop;
  1735. ppk : PUCA_PropItemRec;
  1736. begin
  1737. Result := False;
  1738. puk := GetProps(cp);
  1739. if (puk^.CCC = 0) then
  1740. exit;
  1741. lastUnblockedNonstarterCCC := puk^.CCC;
  1742. if surrogateState then
  1743. kk := i + 2
  1744. else
  1745. kk := i + 1;
  1746. while IsUnblockedNonstarter(kk) do begin
  1747. b := UnicodeIsHighSurrogate(s[kk]) and (kk<c) and UnicodeIsLowSurrogate(s[kk+1]);
  1748. if b then
  1749. ppk := FindChild(ToUCS4(s[kk],s[kk+1]),pp)
  1750. else
  1751. ppk := FindChild(Word(s[kk]),pp);
  1752. if (ppk <> nil) then begin
  1753. pp := ppk;
  1754. RemoveChar(kk);
  1755. Inc(ppLevel);
  1756. RecordStep();
  1757. Result := True;
  1758. if (pp^.ChildCount = 0 ) then
  1759. Break;
  1760. end;
  1761. if b then
  1762. Inc(kk);
  1763. Inc(kk);
  1764. end;
  1765. end;
  1766. procedure AdvanceCharPos();inline;
  1767. begin
  1768. if UnicodeIsHighSurrogate(ps[0]) and (i<c) and UnicodeIsLowSurrogate(ps[1]) then begin
  1769. Inc(i);
  1770. Inc(ps);
  1771. end;
  1772. Inc_I();
  1773. end;
  1774. var
  1775. ok : Boolean;
  1776. pp1 : PUCA_PropItemRec;
  1777. cltemp : PUCA_DataBook;
  1778. ctxNode : PUCA_PropItemContextTreeNodeRec;
  1779. begin
  1780. if (ALength = 0) then
  1781. exit(nil);
  1782. c := ALength;
  1783. s := NormalizeNFD(AStr,c);
  1784. c := Length(s);
  1785. rl := 3*c;
  1786. SetLength(r,rl);
  1787. ral := 0;
  1788. ps := @s[1];
  1789. ClearPP();
  1790. locHistoryTop := -1;
  1791. removedCharIndexLength := 0;
  1792. FillByte(suppressState,SizeOf(suppressState),0);
  1793. LastKeyOwner.Length := 0;
  1794. i := 1;
  1795. while (i <= c) and MoveToNextChar() do begin
  1796. if (pp = nil) then begin // Start Matching
  1797. StartMatch();
  1798. end else begin
  1799. pp1 := FindChild(cp,pp);
  1800. if (pp1 <> nil) then begin
  1801. Inc(ppLevel);
  1802. pp := pp1;
  1803. if (pp^.ChildCount = 0) or (i = c) then begin
  1804. ok := False;
  1805. if pp^.IsValid() and (suppressState.CharCount = 0) then begin
  1806. if (pp^.WeightLength > 0) then begin
  1807. AddWeightsAndClear();
  1808. ok := True;
  1809. end else
  1810. if (LastKeyOwner.Length > 0) and pp^.Contextual and
  1811. pp^.GetContext()^.Find(@LastKeyOwner.Chars[0],LastKeyOwner.Length,ctxNode) and
  1812. (ctxNode^.Data.WeightCount > 0)
  1813. then begin
  1814. AddContextWeights(@ctxNode^.Data);
  1815. ClearHistory();
  1816. ClearPP();
  1817. ok := True;
  1818. end
  1819. end;
  1820. if not ok then begin
  1821. RecordDeletion();
  1822. ok := False;
  1823. while HasHistory() do begin
  1824. GoBack();
  1825. if pp^.IsValid() and
  1826. ( ( (cl = suppressState.cl) and (ppLevel <> suppressState.CharCount) ) or
  1827. ( (cl <> suppressState.cl) and (ppLevel < suppressState.CharCount) )
  1828. )
  1829. then begin
  1830. AddWeightsAndClear();
  1831. ok := True;
  1832. Break;
  1833. end;
  1834. end;
  1835. if not ok then begin
  1836. cltemp := cl^.Base;
  1837. if (cltemp <> nil) then begin
  1838. ClearPP(False);
  1839. cl := cltemp;
  1840. Continue;
  1841. end;
  1842. end;
  1843. if not ok then begin
  1844. AddComputedWeights(cp);
  1845. ClearHistory();
  1846. ClearPP();
  1847. end;
  1848. end;
  1849. end else begin
  1850. RecordStep();
  1851. end;
  1852. end else begin
  1853. // permutations !
  1854. ok := False;
  1855. if TryPermutation() and pp^.IsValid() then begin
  1856. if (suppressState.CharCount = 0) then begin
  1857. AddWeightsAndClear();
  1858. Continue;
  1859. end;
  1860. while True do begin
  1861. if pp^.IsValid() and
  1862. (pp^.WeightLength > 0) and
  1863. ( ( (cl = suppressState.cl) and (ppLevel <> suppressState.CharCount) ) or
  1864. ( (cl <> suppressState.cl) and (ppLevel < suppressState.CharCount) )
  1865. )
  1866. then begin
  1867. AddWeightsAndClear();
  1868. ok := True;
  1869. break;
  1870. end;
  1871. if not HasHistory() then
  1872. break;
  1873. GoBack();
  1874. if (pp = nil) then
  1875. break;
  1876. end;
  1877. end;
  1878. if not ok then begin
  1879. if pp^.IsValid() and (suppressState.CharCount = 0) then begin
  1880. if (pp^.WeightLength > 0) then begin
  1881. AddWeightsAndClear();
  1882. ok := True;
  1883. end else
  1884. if (LastKeyOwner.Length > 0) and pp^.Contextual and
  1885. pp^.GetContext()^.Find(@LastKeyOwner.Chars[0],LastKeyOwner.Length,ctxNode) and
  1886. (ctxNode^.Data.WeightCount > 0)
  1887. then begin
  1888. AddContextWeights(@ctxNode^.Data);
  1889. ClearHistory();
  1890. ClearPP();
  1891. ok := True;
  1892. end
  1893. end;
  1894. if ok then
  1895. Continue;
  1896. end;
  1897. if not ok then begin
  1898. if (cl^.Base <> nil) then begin
  1899. cltemp := cl^.Base;
  1900. while HasHistory() do
  1901. GoBack();
  1902. pp := nil;
  1903. ppLevel := 0;
  1904. cl := cltemp;
  1905. Continue;
  1906. end;
  1907. //walk back
  1908. ok := False;
  1909. while HasHistory() do begin
  1910. GoBack();
  1911. if pp^.IsValid() and
  1912. (pp^.WeightLength > 0) and
  1913. ( (suppressState.CharCount = 0) or
  1914. ( ( (cl = suppressState.cl) and (ppLevel <> suppressState.CharCount) ) or
  1915. ( (cl <> suppressState.cl) and (ppLevel < suppressState.CharCount) )
  1916. )
  1917. )
  1918. then begin
  1919. AddWeightsAndClear();
  1920. ok := True;
  1921. Break;
  1922. end;
  1923. end;
  1924. if ok then begin
  1925. AdvanceCharPos();
  1926. Continue;
  1927. end;
  1928. if (pp <> nil) then begin
  1929. AddComputedWeights(cp);
  1930. ClearHistory();
  1931. ClearPP();
  1932. end;
  1933. end;
  1934. end;
  1935. end;
  1936. if surrogateState then begin
  1937. Inc(ps);
  1938. Inc(i);
  1939. end;
  1940. //
  1941. Inc_I();
  1942. end;
  1943. SetLength(r,ral);
  1944. Result := r;
  1945. end;
  1946. function ComputeSortKey(
  1947. const AStr : PUnicodeChar;
  1948. const ALength : SizeInt;
  1949. const ACollation : PUCA_DataBook
  1950. ) : TUCASortKey;
  1951. var
  1952. r : TUCA_PropWeightsArray;
  1953. begin
  1954. r := ComputeRawSortKey(AStr,ALength,ACollation);
  1955. case ACollation^.VariableWeight of
  1956. TUCA_VariableKind.ucaShifted : Result := FormKeyShifted(r,ACollation);
  1957. TUCA_VariableKind.ucaBlanked : Result := FormKeyBlanked(r,ACollation);
  1958. TUCA_VariableKind.ucaNonIgnorable : Result := FormKeyNonIgnorable(r,ACollation);
  1959. TUCA_VariableKind.ucaShiftedTrimmed : Result := FormKeyShiftedTrimmed(r,ACollation);
  1960. else
  1961. Result := FormKeyShifted(r,ACollation);
  1962. end;
  1963. end;
  1964. end.