-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathJclWideStrings.pas
More file actions
2318 lines (2115 loc) · 60.4 KB
/
Copy pathJclWideStrings.pas
File metadata and controls
2318 lines (2115 loc) · 60.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{**************************************************************************************************}
{ }
{ Project JEDI Code Library (JCL) }
{ }
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
{ you may not use this file except in compliance with the License. You may obtain a copy of the }
{ License at http://www.mozilla.org/MPL/ }
{ }
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
{ ANY KIND, either express or implied. See the License for the specific language governing rights }
{ and limitations under the License. }
{ }
{ The Original Code is WStrUtils.PAS, released on 2004-01-25. }
{ }
{ The Initial Developers of the Original Code are: }
{ - Andreas Hausladen <Andreas dott Hausladen att gmx dott de> }
{ - Mike Lischke (WideQuotedStr & WideExtractQuotedStr from Unicode.pas) }
{ Portions created by Andreas Hausladen are Copyright (C) of Andreas Hausladen. }
{ All rights reserved. }
{ Portions created by Mike Lischke are Copyright (C) of Mike Lischke. All rights reserved. }
{ }
{ Contributor(s): }
{ Robert Marquardt (marquardt) }
{ Robert Rossmair (rrossmair) }
{ ZENsan }
{ Florent Ouchet (outchy) }
{ Kiriakos Vlahos }
{ }
{**************************************************************************************************}
{ }
{ This is a lightweight Unicode unit. For more features use JclUnicode. }
{ }
{ }
{**************************************************************************************************}
{ }
{ Last modified: $Date:: $ }
{ Revision: $Rev:: $ }
{ Author: $Author:: $ }
{ }
{**************************************************************************************************}
unit JclWideStrings;
{$I jcl.inc}
interface
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
{$IFDEF HAS_UNITSCOPE}
System.Classes, System.SysUtils,
{$ELSE ~HAS_UNITSCOPE}
Classes, SysUtils,
{$ENDIF ~HAS_UNITSCOPE}
JclBase;
// Exceptions
type
EJclWideStringError = class(EJclError);
const
// definitions of often used characters:
// Note: Use them only for tests of a certain character not to determine character
// classes (like white spaces) as in Unicode are often many code points defined
// being in a certain class. Hence your best option is to use the various
// UnicodeIs* functions.
WideNull = WideChar(#0);
WideTabulator = WideChar(#9);
WideSpace = WideChar(#32);
// logical line breaks
WideLF = WideChar(#10);
WideLineFeed = WideChar(#10);
WideVerticalTab = WideChar(#11);
WideFormFeed = WideChar(#12);
WideCR = WideChar(#13);
WideCarriageReturn = WideChar(#13);
WideCRLF = WideString(#13#10);
WideLineSeparator = WideChar($2028);
WideParagraphSeparator = WideChar($2029);
{$IFDEF MSWINDOWS}
WideLineBreak = WideCRLF;
{$ENDIF MSWINDOWS}
{$IFDEF UNIX}
WideLineBreak = WideLineFeed;
{$ENDIF UNIX}
BOM_LSB_FIRST = WideChar($FEFF);
BOM_MSB_FIRST = WideChar($FFFE);
type
{$IFDEF SUPPORTS_UNICODE}
TJclWideStrings = {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}Classes.TStrings;
TJclWideStringList = {$IFDEF HAS_UNITSCOPE}System.{$ENDIF}Classes.TStringList;
{$ELSE ~SUPPORTS_UNICODE}
TWideFileOptionsType =
(
foAnsiFile, // loads/writes an ANSI file
foUnicodeLB // reads/writes BOM_LSB_FIRST/BOM_MSB_FIRST
);
TWideFileOptions = set of TWideFileOptionsType;
TSearchFlag = (
sfCaseSensitive, // match letter case
sfIgnoreNonSpacing, // ignore non-spacing characters in search
sfSpaceCompress, // handle several consecutive white spaces as one white space
// (this applies to the pattern as well as the search text)
sfWholeWordOnly // match only text at end/start and/or surrounded by white spaces
);
TSearchFlags = set of TSearchFlag;
TJclWideStrings = class;
TJclWideStringList = class;
TJclWideStringListSortCompare = function(List: TJclWideStringList; Index1, Index2: Integer): Integer;
TJclWideStrings = class(TPersistent)
private
FDelimiter: WideChar;
FQuoteChar: WideChar;
FNameValueSeparator: WideChar;
FLineSeparator: WideString;
FUpdateCount: Integer;
function GetCommaText: WideString;
function GetDelimitedText: WideString;
function GetName(Index: Integer): WideString;
function GetValue(const Name: WideString): WideString;
procedure ReadData(Reader: TReader);
procedure SetCommaText(const Value: WideString);
procedure SetDelimitedText(const Value: WideString);
procedure SetValue(const Name, Value: WideString);
procedure WriteData(Writer: TWriter);
function GetValueFromIndex(Index: Integer): WideString;
procedure SetValueFromIndex(Index: Integer; const Value: WideString);
protected
procedure DefineProperties(Filer: TFiler); override;
function ExtractName(const S: WideString): WideString;
function GetP(Index: Integer): PWideString; virtual; abstract;
function Get(Index: Integer): WideString;
function GetCapacity: Integer; virtual;
function GetCount: Integer; virtual; abstract;
function GetObject(Index: Integer): TObject; virtual;
function GetTextStr: WideString; virtual;
procedure Put(Index: Integer; const S: WideString); virtual; abstract;
procedure PutObject(Index: Integer; AObject: TObject); virtual; abstract;
procedure SetCapacity(NewCapacity: Integer); virtual;
procedure SetTextStr(const Value: WideString); virtual;
procedure SetUpdateState(Updating: Boolean); virtual;
property UpdateCount: Integer read FUpdateCount;
function CompareStrings(const S1, S2: WideString): Integer; virtual;
procedure AssignTo(Dest: TPersistent); override;
public
constructor Create;
function Add(const S: WideString): Integer; virtual;
function AddObject(const S: WideString; AObject: TObject): Integer; virtual;
procedure Append(const S: WideString);
procedure AddStrings(Strings: TJclWideStrings); overload; virtual;
procedure AddStrings(Strings: TStrings); overload; virtual;
procedure Assign(Source: TPersistent); override;
function CreateAnsiStringList: TStrings;
procedure AddStringsTo(Dest: TStrings); virtual;
procedure BeginUpdate;
procedure Clear; virtual; abstract;
procedure Delete(Index: Integer); virtual; abstract;
procedure EndUpdate;
function Equals(Strings: TJclWideStrings): Boolean; {$IFDEF RTL200_UP}reintroduce; {$ENDIF RTL200_UP}overload;
function Equals(Strings: TStrings): Boolean; {$IFDEF RTL200_UP}reintroduce; {$ENDIF RTL200_UP}overload;
procedure Exchange(Index1, Index2: Integer); virtual;
function GetText: PWideChar; virtual;
function IndexOf(const S: WideString): Integer; virtual;
function IndexOfName(const Name: WideString): Integer; virtual;
function IndexOfObject(AObject: TObject): Integer; virtual;
procedure Insert(Index: Integer; const S: WideString); virtual;
procedure InsertObject(Index: Integer; const S: WideString;
AObject: TObject); virtual;
procedure LoadFromFile(const FileName: TFileName;
WideFileOptions: TWideFileOptions = []); virtual;
procedure LoadFromStream(Stream: TStream;
WideFileOptions: TWideFileOptions = []); virtual;
procedure Move(CurIndex, NewIndex: Integer); virtual;
procedure SaveToFile(const FileName: TFileName;
WideFileOptions: TWideFileOptions = []); virtual;
procedure SaveToStream(Stream: TStream;
WideFileOptions: TWideFileOptions = []); virtual;
procedure SetText(Text: PWideChar); virtual;
function GetDelimitedTextEx(ADelimiter, AQuoteChar: WideChar): WideString;
procedure SetDelimitedTextEx(ADelimiter, AQuoteChar: WideChar; const Value: WideString);
property Capacity: Integer read GetCapacity write SetCapacity;
property CommaText: WideString read GetCommaText write SetCommaText;
property Count: Integer read GetCount;
property Delimiter: WideChar read FDelimiter write FDelimiter;
property DelimitedText: WideString read GetDelimitedText write SetDelimitedText;
property Names[Index: Integer]: WideString read GetName;
property Objects[Index: Integer]: TObject read GetObject write PutObject;
property QuoteChar: WideChar read FQuoteChar write FQuoteChar;
property Values[const Name: WideString]: WideString read GetValue write SetValue;
property ValueFromIndex[Index: Integer]: WideString read GetValueFromIndex write SetValueFromIndex;
property NameValueSeparator: WideChar read FNameValueSeparator write FNameValueSeparator;
property LineSeparator: WideString read FLineSeparator write FLineSeparator;
property PStrings[Index: Integer]: PWideString read GetP;
property Strings[Index: Integer]: WideString read Get write Put; default;
property Text: WideString read GetTextStr write SetTextStr;
end;
// do not replace by JclUnicode.TWideStringList (speed and size issue)
PWStringItem = ^TWStringItem;
TWStringItem = record
FString: WideString;
FObject: TObject;
end;
TJclWideStringList = class(TJclWideStrings)
private
FList: TList;
FSorted: Boolean;
FDuplicates: TDuplicates;
FCaseSensitive: Boolean;
FOnChange: TNotifyEvent;
FOnChanging: TNotifyEvent;
procedure SetSorted(Value: Boolean);
procedure SetCaseSensitive(const Value: Boolean);
protected
function GetItem(Index: Integer): PWStringItem;
procedure Changed; virtual;
procedure Changing; virtual;
function GetP(Index: Integer): PWideString; override;
function GetCapacity: Integer; override;
function GetCount: Integer; override;
function GetObject(Index: Integer): TObject; override;
procedure Put(Index: Integer; const Value: WideString); override;
procedure PutObject(Index: Integer; AObject: TObject); override;
procedure SetCapacity(NewCapacity: Integer); override;
procedure SetUpdateState(Updating: Boolean); override;
function CompareStrings(const S1, S2: WideString): Integer; override;
public
constructor Create;
destructor Destroy; override;
function AddObject(const S: WideString; AObject: TObject): Integer; override;
procedure Clear; override;
procedure Delete(Index: Integer); override;
procedure Exchange(Index1, Index2: Integer); override;
function Find(const S: WideString; var Index: Integer): Boolean; virtual;
// Find() also works with unsorted lists
function IndexOf(const S: WideString): Integer; override;
procedure InsertObject(Index: Integer; const S: WideString;
AObject: TObject); override;
procedure Sort; virtual;
procedure CustomSort(Compare: TJclWideStringListSortCompare); virtual;
property Duplicates: TDuplicates read FDuplicates write FDuplicates;
property Sorted: Boolean read FSorted write SetSorted;
property CaseSensitive: Boolean read FCaseSensitive write SetCaseSensitive;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
end;
{$ENDIF ~SUPPORTS_UNICODE}
TWideStringList = TJclWideStringList;
TWideStrings = TJclWideStrings;
TJclUnicodeStringList = TJclWideStringList;
TJclUnicodeStrings = TJclWideStrings;
// OF deprecated?
TWStringList = TJclWideStringList;
TWStrings = TJclWideStrings;
// WideChar functions
function CharToWideChar(Ch: AnsiChar): WideChar;
function WideCharToChar(Ch: WideChar): AnsiChar;
// PWideChar functions
procedure MoveWideChar(const Source; var Dest; Count: SizeInt);
function StrLenW(const Str: PWideChar): SizeInt;
function StrEndW(const Str: PWideChar): PWideChar;
function StrMoveW(Dest: PWideChar; const Source: PWideChar; Count: SizeInt): PWideChar;
function StrCopyW(Dest: PWideChar; const Source: PWideChar): PWideChar;
function StrECopyW(Dest: PWideChar; const Source: PWideChar): PWideChar;
function StrLCopyW(Dest: PWideChar; const Source: PWideChar; MaxLen: SizeInt): PWideChar;
function StrPCopyWW(Dest: PWideChar; const Source: WideString): PWideChar;
function StrPCopyW(Dest: PWideChar; const Source: AnsiString): PWideChar;
function StrPLCopyWW(Dest: PWideChar; const Source: WideString; MaxLen: SizeInt): PWideChar;
function StrPLCopyW(Dest: PWideChar; const Source: AnsiString; MaxLen: SizeInt): PWideChar;
function StrCatW(Dest: PWideChar; const Source: PWideChar): PWideChar;
function StrLCatW(Dest: PWideChar; const Source: PWideChar; MaxLen: SizeInt): PWideChar;
function StrCompW(const Str1, Str2: PWideChar): SizeInt;
function StrICompW(const Str1, Str2: PWideChar): SizeInt;
function StrLCompW(const Str1, Str2: PWideChar; MaxLen: SizeInt): SizeInt;
function StrLICompW(const Str1, Str2: PWideChar; MaxLen: SizeInt): SizeInt;
function StrLICompW2(const Str1, Str2: PWideChar; MaxLen: SizeInt): SizeInt;
function StrNScanW(const Str1, Str2: PWideChar): SizeInt;
function StrRNScanW(const Str1, Str2: PWideChar): SizeInt;
function StrScanW(const Str: PWideChar; Ch: WideChar): PWideChar; overload;
function StrScanW(Str: PWideChar; Chr: WideChar; StrLen: SizeInt): PWideChar; overload;
function StrRScanW(const Str: PWideChar; Chr: WideChar): PWideChar;
function StrPosW(const Str, SubStr: PWideChar): PWideChar;
function StrAllocW(WideSize: SizeInt): PWideChar;
function StrBufSizeW(const Str: PWideChar): SizeInt;
function StrNewW(const Str: PWideChar): PWideChar; overload;
function StrNewW(const Str: WideString): PWideChar; overload;
procedure StrDisposeW(Str: PWideChar);
procedure StrDisposeAndNilW(var Str: PWideChar);
procedure StrSwapByteOrder(Str: PWideChar);
// WideString functions
function WidePos(const SubStr, S: WideString): SizeInt;
function WideQuotedStr(const S: WideString; Quote: WideChar): WideString;
function WideExtractQuotedStr(var Src: PWideChar; Quote: WideChar): WideString;
function WideCompareText(const S1, S2: WideString): SizeInt;
function WideCompareStr(const S1, S2: WideString): SizeInt;
function WideUpperCase(const S: WideString): WideString;
function WideLowerCase(const S: WideString): WideString;
function TrimW(const S: WideString): WideString;
function TrimLeftW(const S: WideString): WideString;
function TrimRightW(const S: WideString): WideString;
function WideReverse(const AText: Widestring): Widestring;
procedure WideReverseInPlace(var S: WideString);
function TrimLeftLengthW(const S: WideString): SizeInt;
function TrimRightLengthW(const S: WideString): SizeInt;
{$IFNDEF FPC}
function WideStartsText(const SubStr, S: WideString): Boolean;
function WideStartsStr(const SubStr, S: WideString): Boolean;
{$ENDIF ~FPC}
// MultiSz Routines
type
PWideMultiSz = PWideChar;
function StringsToMultiSz(var Dest: PWideMultiSz; const Source: TJclWideStrings): PWideMultiSz;
procedure MultiSzToStrings(const Dest: TJclWideStrings; const Source: PWideMultiSz);
function MultiSzLength(const Source: PWideMultiSz): SizeInt;
procedure AllocateMultiSz(var Dest: PWideMultiSz; Len: SizeInt);
procedure FreeMultiSz(var Dest: PWideMultiSz);
function MultiSzDup(const Source: PWideMultiSz): PWideMultiSz;
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$URL$';
Revision: '$Revision$';
Date: '$Date$';
LogPath: 'JCL\source\common';
Extra: '';
Data: nil
);
{$ENDIF UNITVERSIONING}
implementation
uses
{$IFDEF HAS_UNITSCOPE}
{$IFDEF HAS_UNIT_RTLCONSTS}
System.RTLConsts,
{$ENDIF HAS_UNIT_RTLCONSTS}
{$IFDEF MSWINDOWS}
Winapi.Windows,
{$ENDIF MSWINDOWS}
System.Math,
{$ELSE ~HAS_UNITSCOPE}
{$IFDEF HAS_UNIT_RTLCONSTS}
RTLConsts,
{$ENDIF HAS_UNIT_RTLCONSTS}
{$IFDEF MSWINDOWS}
Windows,
{$ENDIF MSWINDOWS}
Math,
{$ENDIF ~HAS_UNITSCOPE}
JclUnicode,
JclResources;
procedure SwapWordByteOrder(P: PWideChar; Len: SizeInt);
begin
while Len > 0 do
begin
Dec(Len);
P^ := WideChar((Word(P^) shr 8) or (Word(P^) shl 8));
Inc(P);
end;
end;
//=== WideChar functions =====================================================
function CharToWideChar(Ch: AnsiChar): WideChar;
var
WS: WideString;
begin
WS := WideChar(Ch);
Result := WS[1];
end;
function WideCharToChar(Ch: WideChar): AnsiChar;
var
S: WideString;
begin
S := Ch;
Result := AnsiChar(S[1]);
end;
//=== PWideChar functions ====================================================
procedure MoveWideChar(const Source; var Dest; Count: SizeInt);
begin
Move(Source, Dest, Count * SizeOf(WideChar));
end;
function StrAllocW(WideSize: SizeInt): PWideChar;
begin
WideSize := SizeOf(WideChar) * WideSize + SizeOf(SizeInt);
Result := AllocMem(WideSize);
SizeInt(Pointer(Result)^) := WideSize;
Inc(Result, SizeOf(SizeInt) div SizeOf(WideChar));
end;
function StrNewW(const Str: PWideChar): PWideChar;
// Duplicates the given string (if not nil) and returns the address of the new string.
var
Size: SizeInt;
begin
if Str = nil then
Result := nil
else
begin
Size := StrLenW(Str) + 1;
Result := StrMoveW(StrAllocW(Size), Str, Size);
end;
end;
function StrNewW(const Str: WideString): PWideChar;
begin
Result := StrNewW(PWideChar(Str));
end;
procedure StrDisposeW(Str: PWideChar);
// releases a string allocated with StrNewW or StrAllocW
begin
if Str <> nil then
begin
Dec(Str, SizeOf(SizeInt) div SizeOf(WideChar));
FreeMem(Str);
end;
end;
procedure StrDisposeAndNilW(var Str: PWideChar);
var
Buff: PWideChar;
begin
Buff := Str;
Str := nil;
StrDisposeW(Buff);
end;
const
// data used to bring UTF-16 coded strings into correct UTF-32 order for correct comparation
UTF16Fixup: array [0..31] of Word = (
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
$2000, $F800, $F800, $F800, $F800
);
function StrCompW(const Str1, Str2: PWideChar): SizeInt;
// Binary comparation of Str1 and Str2 with surrogate fix-up.
// Returns < 0 if Str1 is smaller in binary order than Str2, = 0 if both strings are
// equal and > 0 if Str1 is larger than Str2.
//
// This code is based on an idea of Markus W. Scherer (IBM).
// Note: The surrogate fix-up is necessary because some single value code points have
// larger values than surrogates which are in UTF-32 actually larger.
var
C1, C2: Word;
Run1, Run2: PWideChar;
begin
Run1 := Str1;
Run2 := Str2;
repeat
C1 := Word(Run1^);
C1 := Word(C1 or UTF16Fixup[C1 shr 11]);
C2 := Word(Run2^);
C2 := Word(C2 or UTF16Fixup[C2 shr 11]);
// now C1 and C2 are in UTF-32-compatible order
Result := SizeInt(C1) - SizeInt(C2);
if(Result <> 0) or (C1 = 0) or (C2 = 0) then
Break;
Inc(Run1);
Inc(Run2);
until False;
// If the strings have different lengths but the comparation returned equity so far
// then adjust the result so that the longer string is marked as the larger one.
if Result = 0 then
Result := (Run1 - Str1) - (Run2 - Str2);
end;
function StrLCompW(const Str1, Str2: PWideChar; MaxLen: SizeInt): SizeInt;
// compares strings up to MaxLen code points
// see also StrCompW
var
S1, S2: PWideChar;
C1, C2: Word;
begin
if MaxLen > 0 then
begin
S1 := Str1;
S2 := Str2;
repeat
C1 := Word(S1^);
C1 := Word(C1 or UTF16Fixup[C1 shr 11]);
C2 := Word(S2^);
C2 := Word(C2 or UTF16Fixup[C2 shr 11]);
// now C1 and C2 are in UTF-32-compatible order
{ TODO : surrogates take up 2 words and are counted twice here, count them only once }
Result := SizeInt(C1) - SizeInt(C2);
Dec(MaxLen);
if(Result <> 0) or (C1 = 0) or (C2 = 0) or (MaxLen = 0) then
Break;
Inc(S1);
Inc(S2);
until False;
end
else
Result := 0;
end;
function StrICompW(const Str1, Str2: PWideChar): SizeInt;
// Compares Str1 to Str2 without case sensitivity.
// See also comments in StrCompW, but keep in mind that case folding might result in
// one-to-many mappings which must be considered here.
{$IFDEF UNICODE_RTL_DATABASE}
begin
Result := AnsiStrIComp(Str1, Str2)
end;
{$ELSE ~UNICODE_RTL_DATABASE}
var
C1, C2: Word;
S1, S2: PWideChar;
Run1, Run2: PWideChar;
Folded1, Folded2: WideString;
begin
// Because of size changes of the string when doing case folding
// it is unavoidable to convert both strings completely in advance.
S1 := Str1;
S2 := Str2;
Folded1 := '';
while S1^ <> #0 do
begin
Folded1 := Folded1 + WideCaseFolding(S1^);
Inc(S1);
end;
Folded2 := '';
while S2^ <> #0 do
begin
Folded2 := Folded2 + WideCaseFolding(S2^);
Inc(S2);
end;
Run1 := PWideChar(Folded1);
Run2 := PWideChar(Folded2);
repeat
C1 := Word(Run1^);
C1 := Word(C1 or UTF16Fixup[C1 shr 11]);
C2 := Word(Run2^);
C2 := Word(C2 or UTF16Fixup[C2 shr 11]);
// now C1 and C2 are in UTF-32-compatible order
Result := SizeInt(C1) - SizeInt(C2);
if(Result <> 0) or (C1 = 0) or (C2 = 0) then
Break;
Inc(Run1);
Inc(Run2);
until False;
// If the strings have different lengths but the comparation returned equity so far
// then adjust the result so that the longer string is marked as the larger one.
if Result = 0 then
Result := (Run1 - PWideChar(Folded1)) - (Run2 - PWideChar(Folded2));
end;
{$ENDIF ~UNICODE_RTL_DATABASE}
function StrLICompW(const Str1, Str2: PWideChar; MaxLen: SizeInt): SizeInt;
// compares strings up to MaxLen code points
// see also StrICompW
{$IFDEF UNICODE_RTL_DATABASE}
begin
Result := AnsiStrIComp(Str1, Str2)
end;
{$ELSE UNICODE_RTL_DATABASE}
var
S1, S2: PWideChar;
C1, C2: Word;
Run1, Run2: PWideChar;
Folded1, Folded2: WideString;
begin
if MaxLen > 0 then
begin
// Because of size changes of the string when doing case folding
// it is unavoidable to convert both strings completely in advance.
S1 := Str1;
S2 := Str2;
Folded1 := '';
while S1^ <> #0 do
begin
Folded1 := Folded1 + WideCaseFolding(S1^);
Inc(S1);
end;
Folded2 := '';
while S2^ <> #0 do
begin
Folded2 := Folded2 + WideCaseFolding(S2^);
Inc(S2);
end;
Run1 := PWideChar(Folded1);
Run2 := PWideChar(Folded2);
repeat
C1 := Word(Run1^);
C1 := Word(C1 or UTF16Fixup[C1 shr 11]);
C2 := Word(Run2^);
C2 := Word(C2 or UTF16Fixup[C2 shr 11]);
// now C1 and C2 are in UTF-32-compatible order
{ TODO : surrogates take up 2 words and are counted twice here, count them only once }
Result := SizeInt(C1) - SizeInt(C2);
Dec(MaxLen);
if(Result <> 0) or (C1 = 0) or (C2 = 0) or (MaxLen = 0) then
Break;
Inc(Run1);
Inc(Run2);
until False;
end
else
Result := 0;
end;
{$ENDIF UNICODE_RTL_DATABASE}
function StrLICompW2(const Str1, Str2: PWideChar; MaxLen: SizeInt): SizeInt;
var
P1, P2: WideString;
begin
// faster than the JclUnicode.StrLICompW function
SetString(P1, Str1, Min(MaxLen, StrLenW(Str1)));
SetString(P2, Str2, Min(MaxLen, StrLenW(Str2)));
Result := WideCompareText(P1, P2);
end;
function StrPosW(const Str, SubStr: PWideChar): PWideChar;
var
P: PWideChar;
I: SizeInt;
begin
Result := nil;
if (Str = nil) or (SubStr = nil) or (Str^ = #0) or (SubStr^ = #0) then
Exit;
Result := Str;
while Result^ <> #0 do
begin
if Result^ <> SubStr^ then
Inc(Result)
else
begin
P := Result + 1;
I := 1;
while (P^ <> #0) and (P^ = SubStr[I]) do
begin
Inc(I);
Inc(P);
end;
if SubStr[I] = #0 then
Exit
else
Inc(Result);
end;
end;
Result := nil;
end;
function StrLenW(const Str: PWideChar): SizeInt;
begin
Result := 0;
if Str <> nil then
while Str[Result] <> #0 do
Inc(Result);
end;
function StrScanW(const Str: PWideChar; Ch: WideChar): PWideChar;
begin
Result := Str;
if Result <> nil then
begin
while (Result^ <> #0) and (Result^ <> Ch) do
Inc(Result);
if (Result^ = #0) and (Ch <> #0) then
Result := nil;
end;
end;
function StrEndW(const Str: PWideChar): PWideChar;
begin
Result := Str;
if Result <> nil then
while Result^ <> #0 do
Inc(Result);
end;
function StrCopyW(Dest: PWideChar; const Source: PWideChar): PWideChar;
var
Src: PWideChar;
begin
Result := Dest;
if Dest <> nil then
begin
Src := Source;
if Src <> nil then
while Src^ <> #0 do
begin
Dest^ := Src^;
Inc(Src);
Inc(Dest);
end;
Dest^ := #0;
end;
end;
function StrECopyW(Dest: PWideChar; const Source: PWideChar): PWideChar;
var
Src: PWideChar;
begin
if Dest <> nil then
begin
Src := Source;
if Src <> nil then
while Src^ <> #0 do
begin
Dest^ := Src^;
Inc(Src);
Inc(Dest);
end;
Dest^ := #0;
end;
Result := Dest;
end;
function StrLCopyW(Dest: PWideChar; const Source: PWideChar; MaxLen: SizeInt): PWideChar;
var
Src: PWideChar;
begin
Result := Dest;
if (Dest <> nil) and (MaxLen > 0) then
begin
Src := Source;
if Src <> nil then
while (MaxLen > 0) and (Src^ <> #0) do
begin
Dest^ := Src^;
Inc(Src);
Inc(Dest);
Dec(MaxLen);
end;
Dest^ := #0;
end;
end;
function StrCatW(Dest: PWideChar; const Source: PWideChar): PWideChar;
begin
Result := Dest;
StrCopyW(StrEndW(Dest), Source);
end;
function StrLCatW(Dest: PWideChar; const Source: PWideChar; MaxLen: SizeInt): PWideChar;
begin
Result := Dest;
StrLCopyW(StrEndW(Dest), Source, MaxLen);
end;
function StrMoveW(Dest: PWideChar; const Source: PWideChar; Count: SizeInt): PWideChar;
begin
Result := Dest;
if Count > 0 then
Move(Source^, Dest^, Count * SizeOf(WideChar));
end;
function StrPCopyWW(Dest: PWideChar; const Source: WideString): PWideChar;
begin
Result := StrLCopyW(Dest, PWideChar(Source), Length(Source));
end;
function StrPLCopyWW(Dest: PWideChar; const Source: WideString; MaxLen: SizeInt): PWideChar;
begin
Result := StrLCopyW(Dest, PWideChar(Source), MaxLen);
end;
function StrRScanW(const Str: PWideChar; Chr: WideChar): PWideChar;
var
P: PWideChar;
begin
Result := nil;
if Str <> nil then
begin
P := Str;
repeat
if P^ = Chr then
Result := P;
Inc(P);
until P^ = #0;
end;
end;
// (rom) following functions copied from JclUnicode.pas
// exchanges in each character of the given string the low order and high order
// byte to go from LSB to MSB and vice versa.
// EAX/RAX contains address of string
// stop at the first #0 character
procedure StrSwapByteOrder(Str: PWideChar);
asm
{$IFDEF CPU32}
// --> EAX Str
PUSH ESI
PUSH EDI
MOV ESI, EAX
MOV EDI, ESI
XOR EAX, EAX // clear high order byte to be able to use 32bit operand below
@@1:
LODSW
OR EAX, EAX
JZ @@2
XCHG AL, AH
STOSW
JMP @@1
@@2:
POP EDI
POP ESI
{$ENDIF CPU32}
{$IFDEF CPU64}
// --> RCX Str
XOR RAX, RAX // clear high order byte to be able to use 64bit operand below
@@1:
MOV AX, WORD PTR [RCX]
OR RAX, RAX
JZ @@2
XCHG AL, AH
MOV WORD PTR [RCX], AX
ADD ECX, 2
JMP @@1
@@2:
{$ENDIF CPU64}
end;
function StrNScanW(const Str1, Str2: PWideChar): SizeInt;
// Determines where (in Str1) the first time one of the characters of Str2 appear.
// The result is the length of a string part of Str1 where none of the characters of
// Str2 do appear (not counting the trailing #0 and starting with position 0 in Str1).
var
Run: PWideChar;
begin
Result := -1;
if (Str1 <> nil) and (Str2 <> nil) then
begin
Run := Str1;
while Run^ <> #0 do
begin
if StrScanW(Str2, Run^) <> nil then
Break;
Inc(Run);
end;
Result := Run - Str1;
end;
end;
function StrRNScanW(const Str1, Str2: PWideChar): SizeInt;
// This function does the same as StrRNScanW but uses Str1 in reverse order. This
// means Str1 points to the last character of a string, is traversed reversely
// and terminates with a starting #0. This is useful for parsing strings stored
// in reversed macro buffers etc.
var
Run: PWideChar;
begin
Result := -1;
if (Str1 <> nil) and (Str2 <> nil) then
begin
Run := Str1;
while Run^ <> #0 do
begin
if StrScanW(Str2, Run^) <> nil then
Break;
Dec(Run);
end;
Result := Str1 - Run;
end;
end;
// Returns a pointer to first occurrence of a specified character in a string
// or nil if not found.
// Note: this is just a binary search for the specified character and there's no
// check for a terminating null. Instead at most StrLen characters are
// searched. This makes this function extremly fast.
//
function StrScanW(Str: PWideChar; Chr: WideChar; StrLen: SizeInt): PWideChar;
begin
Result := Str;
while StrLen > 0 do
begin
if Result^ = Chr then
Exit;
Inc(Result);
Dec(StrLen);
end;
Result := nil;
end;
function StrBufSizeW(const Str: PWideChar): SizeInt;
// Returns max number of wide characters that can be stored in a buffer
// allocated by StrAllocW.
var
P: PWideChar;
begin
if Str <> nil then
begin
P := Str;
Dec(P, SizeOf(SizeInt) div SizeOf(WideChar));
Result := (PSizeInt(P)^ - SizeOf(SizeInt)) div SizeOf(WideChar);
end
else
Result := 0;
end;
function StrPCopyW(Dest: PWideChar; const Source: AnsiString): PWideChar;
// copies a Pascal-style string to a null-terminated wide string
begin
Result := StrPLCopyW(Dest, Source, SizeInt(Length(Source)));
Result[Length(Source)] := WideNull;
end;
function StrPLCopyW(Dest: PWideChar; const Source: AnsiString; MaxLen: SizeInt): PWideChar;
// copies characters from a Pascal-style string into a null-terminated wide string
var
P: PAnsiChar;
begin
P := PAnsiChar(Pointer(Source));
while MaxLen > 0 do
begin
Dest^ := WideChar(Ord(P^));
Inc(P);
Inc(Dest);
Dec(MaxLen);
end;
Result := Dest;
end;
//=== WideString functions ===================================================
function WidePos(const SubStr, S: WideString): SizeInt;
var
P: PWideChar;
begin
P := StrPosW(PWideChar(S), PWideChar(SubStr));
if P <> nil then
Result := P - PWideChar(S) + 1
else
Result := 0;
end;
// original code by Mike Lischke (extracted from JclUnicode.pas)
function WideQuotedStr(const S: WideString; Quote: WideChar): WideString;
var
P, Src,
Dest: PWideChar;
AddCount: SizeInt;
begin
AddCount := 0;
P := StrScanW(PWideChar(S), Quote);
while P <> nil do
begin
Inc(P);
Inc(AddCount);
P := StrScanW(P, Quote);
end;
if AddCount = 0 then
Result := Quote + S + Quote
else
begin
SetLength(Result, Length(S) + AddCount + 2);
Dest := PWideChar(Result);
Dest^ := Quote;
Inc(Dest);
Src := PWideChar(S);
P := StrScanW(Src, Quote);
repeat