mORMot and Open Source friends
Check-in [d65d009416]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:TSynLog and TSynMapFile now working all together as expected:
  • changed the exception handling process from windows API to pure Delphi scheme
  • stack trace now working as expected
  • a lot of fixes and enhancements
  • introducing new .mab file, i.e. binary representation of .map files
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: d65d00941685dd5ac704fd6e3e69d7e9b6975163
User & Date: ab 2011-04-05 18:37:55
Context
2011-04-05
20:11
fixed compilation for Delphi 2009/2010/XE (incoherent .map file generation) check-in: 614cb71122 user: ab tags: trunk
18:37
TSynLog and TSynMapFile now working all together as expected:
  • changed the exception handling process from windows API to pure Delphi scheme
  • stack trace now working as expected
  • a lot of fixes and enhancements
  • introducing new .mab file, i.e. binary representation of .map files
check-in: d65d009416 user: ab tags: trunk
2011-04-03
09:00
TDynArrayHashed now working as expected, with some full regression tests - resulting speed sounds very good check-in: 8195c840e1 user: ab tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SynBigTable.pas.

972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
....
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
....
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
....
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
....
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
....
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
    St: TStream;
    tmp: TIntegerDynArray;
    ok: boolean;
begin
  result := false;
  SetLength(Deleted,Random(100)+10);
  fillchar(Deleted[0],length(Deleted)*4,0);
  start('Deleting '+IntToStr(length(Deleted))+' elements');
  for i := 0 to high(Deleted) do
  repeat
    repeat
      id := Random(n)+1;
    until (T.IDToIndex(id)>=0) and
      (IntegerScanIndex(pointer(Deleted),length(Deleted),id)<0); // no dup
    Deleted[i] := id;
................................................................................
    for i := 1 to n do
      if T.Get(i,Data) and not TestString(i,Data) then
        exit else
      if withString then
      if TS.Get(Int32ToUTF8(i),Data) and not TestString(i,Data) then
        exit;
  nu := Random(100)+10;
  start('Updating '+IntToStr(nu)+' elements');
  for i := 0 to nu-1 do
  repeat
    repeat
      id := Random(n)+1;
    until (T.IDToIndex(id)>=0) and
      (FastFindIntegerSorted(pointer(Updated),UpdatedCount-1,id)<0); // no dup
    AddSortedInteger(Updated,UpdatedCount,id);
................................................................................
//  RandSeed := 1000;
  start('');
  Finalize(By8);
  n := Random(50)*50+1500;
  UpdatedCount := 0;
  FN := ChangeFileExt(paramstr(0),'.syn');
  DeleteFile(FN);
  start('Creating a TSynBigTable with '+IntToStr(n)+' elements');
  T := TSynBigTable.Create(FN);
  if true then
  try
    for i := 1 to n do
      if T.Add(TTestBigTable.CreateString(i))<>i then
        exit else
      if T.CurrentInMemoryDataSize>10 shl 20 then // write on disk every 10 MB
................................................................................
  start('');
  dec(n,1000);
  FN := FN+'2';
  UpdatedCount := 0;
  DeleteFile(FN);
  TS := TSynBigTableString.Create(FN);
  try
    start('Creating a GUID-indexed TSynBigTableString with '+IntToStr(Length(FileN))+' * 4MB elements');
    for i := 0 to high(FileN) do
      repeat // avoid duplicates (Add=0 if FileN[i] was already there)
        guid.D1 := Random(maxInt);
        guid.D2 := Random(65535);
        guid.D3 := Random(65535);
        Int64(guid.D4) := Int64(Random(maxInt))*Random(maxInt);
        FileN[i] := RawUTF8(GuidToString(guid));
................................................................................
    if not TS.Get(FileN[id],data) or not TestString(777,Data) then
      exit;
    st := stats(T);
    start('Clear');
    TS.Clear;
    UpdatedCount := 0;
    start(st+#13#10#10'Creating a string-indexed TSynBigTableString with '+
      IntToStr(n)+' elements');
    for i := 1 to n do
      if TS.Add(TTestBigTable.CreateString(i),Int32ToUTF8(i))<>i then
        exit else
      if TS.CurrentInMemoryDataSize>10 shl 20 then // write on disk every 10 MB
        TS.UpdateToFile;
    if TS.Count<>n then
      exit;
................................................................................
    WriteBuffer.WriteVarUInt32Array(fDeleted,fDeletedCount,wkSorted);
    WriteBuffer.WriteVarUInt32Array(fAliasSource,fAliasCount,wkSorted);
    WriteBuffer.WriteVarUInt32Array(fAliasReal,fAliasCount,wkVarUInt32);
    UseOffset64 := pointer(fOffset64)<>nil;
    WriteBuffer.Write(@UseOffset64,1);
    if UseOffset64 then
      WriteBuffer.WriteVarUInt64DynArray(fOffset64,fCount,true) else
      WriteBuffer.WriteVarUInt32Array(fOffset32,fCount,wkOffset);
    Offs := WriteBuffer.TotalWritten-Offs+8;
    //assert(Offs<maxInt);
    WriteBuffer.Write(@Offs,4);
    WriteBuffer.Write(@magic,4);
    WriteBuffer.Flush;
    SetEndOfFile(fFile); // always force truncate at end of header
    if forceFlushOnDisk then






|







 







|







 







|







 







|







 







|







 







|







972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
....
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
....
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
....
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
....
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
....
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
    St: TStream;
    tmp: TIntegerDynArray;
    ok: boolean;
begin
  result := false;
  SetLength(Deleted,Random(100)+10);
  fillchar(Deleted[0],length(Deleted)*4,0);
  start('Deleting '+IntToString(length(Deleted))+' elements');
  for i := 0 to high(Deleted) do
  repeat
    repeat
      id := Random(n)+1;
    until (T.IDToIndex(id)>=0) and
      (IntegerScanIndex(pointer(Deleted),length(Deleted),id)<0); // no dup
    Deleted[i] := id;
................................................................................
    for i := 1 to n do
      if T.Get(i,Data) and not TestString(i,Data) then
        exit else
      if withString then
      if TS.Get(Int32ToUTF8(i),Data) and not TestString(i,Data) then
        exit;
  nu := Random(100)+10;
  start('Updating '+IntToString(nu)+' elements');
  for i := 0 to nu-1 do
  repeat
    repeat
      id := Random(n)+1;
    until (T.IDToIndex(id)>=0) and
      (FastFindIntegerSorted(pointer(Updated),UpdatedCount-1,id)<0); // no dup
    AddSortedInteger(Updated,UpdatedCount,id);
................................................................................
//  RandSeed := 1000;
  start('');
  Finalize(By8);
  n := Random(50)*50+1500;
  UpdatedCount := 0;
  FN := ChangeFileExt(paramstr(0),'.syn');
  DeleteFile(FN);
  start('Creating a TSynBigTable with '+IntToString(n)+' elements');
  T := TSynBigTable.Create(FN);
  if true then
  try
    for i := 1 to n do
      if T.Add(TTestBigTable.CreateString(i))<>i then
        exit else
      if T.CurrentInMemoryDataSize>10 shl 20 then // write on disk every 10 MB
................................................................................
  start('');
  dec(n,1000);
  FN := FN+'2';
  UpdatedCount := 0;
  DeleteFile(FN);
  TS := TSynBigTableString.Create(FN);
  try
    start('Creating a GUID-indexed TSynBigTableString with '+IntToString(Length(FileN))+' * 4MB elements');
    for i := 0 to high(FileN) do
      repeat // avoid duplicates (Add=0 if FileN[i] was already there)
        guid.D1 := Random(maxInt);
        guid.D2 := Random(65535);
        guid.D3 := Random(65535);
        Int64(guid.D4) := Int64(Random(maxInt))*Random(maxInt);
        FileN[i] := RawUTF8(GuidToString(guid));
................................................................................
    if not TS.Get(FileN[id],data) or not TestString(777,Data) then
      exit;
    st := stats(T);
    start('Clear');
    TS.Clear;
    UpdatedCount := 0;
    start(st+#13#10#10'Creating a string-indexed TSynBigTableString with '+
      IntToString(n)+' elements');
    for i := 1 to n do
      if TS.Add(TTestBigTable.CreateString(i),Int32ToUTF8(i))<>i then
        exit else
      if TS.CurrentInMemoryDataSize>10 shl 20 then // write on disk every 10 MB
        TS.UpdateToFile;
    if TS.Count<>n then
      exit;
................................................................................
    WriteBuffer.WriteVarUInt32Array(fDeleted,fDeletedCount,wkSorted);
    WriteBuffer.WriteVarUInt32Array(fAliasSource,fAliasCount,wkSorted);
    WriteBuffer.WriteVarUInt32Array(fAliasReal,fAliasCount,wkVarUInt32);
    UseOffset64 := pointer(fOffset64)<>nil;
    WriteBuffer.Write(@UseOffset64,1);
    if UseOffset64 then
      WriteBuffer.WriteVarUInt64DynArray(fOffset64,fCount,true) else
      WriteBuffer.WriteVarUInt32Array(fOffset32,fCount,wkOffsetU);
    Offs := WriteBuffer.TotalWritten-Offs+8;
    //assert(Offs<maxInt);
    WriteBuffer.Write(@Offs,4);
    WriteBuffer.Write(@magic,4);
    WriteBuffer.Flush;
    SetEndOfFile(fFile); // always force truncate at end of header
    if forceFlushOnDisk then

Changes to SynCommons.pas.

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148




149
150
151
152
153
154
155
...
613
614
615
616
617
618
619
620




621
622
623
624
625
626
627
...
649
650
651
652
653
654
655









656
657
658
659
660
661
662
....
1733
1734
1735
1736
1737
1738
1739



1740
1741
1742
1743
1744
1745
1746
....
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578

2579
2580
2581
2582
2583
2584
2585
....
2671
2672
2673
2674
2675
2676
2677



2678
2679
2680
2681
2682
2683
2684
....
2691
2692
2693
2694
2695
2696
2697



2698
2699
2700
2701
2702
2703
2704
....
3146
3147
3148
3149
3150
3151
3152



3153
3154
3155
3156
3157
3158
3159
....
4703
4704
4705
4706
4707
4708
4709












































































4710
4711
4712
4713
4714
4715
4716
....
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771




4772
4773
4774
4775
4776
4777
4778
....
4800
4801
4802
4803
4804
4805
4806


4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818



4819
4820
4821
4822
4823
4824
4825
....
4845
4846
4847
4848
4849
4850
4851




4852
4853
4854
4855
4856
4857
4858

4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871


4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884

4885
4886
4887
4888
4889
4890
4891
....
4932
4933
4934
4935
4936
4937
4938











4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
....
4961
4962
4963
4964
4965
4966
4967











4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
....
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
....
5019
5020
5021
5022
5023
5024
5025
5026




5027
5028
5029
5030
5031
5032
5033
....
5784
5785
5786
5787
5788
5789
5790
5791
5792
5793
5794
5795
5796
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819















5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830
5831
5832
....
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
....
6530
6531
6532
6533
6534
6535
6536
























































6537
6538
6539
6540
6541
6542
6543
....
8272
8273
8274
8275
8276
8277
8278





















8279
8280
8281
8282
8283
8284
8285
....
9234
9235
9236
9237
9238
9239
9240
9241
9242
9243
9244
9245
9246
9247
9248
....
9255
9256
9257
9258
9259
9260
9261
9262
9263
9264
9265
9266
9267
9268
9269
.....
11821
11822
11823
11824
11825
11826
11827
11828
11829
11830
11831
11832
11833
11834
11835
11836
11837
11838
11839
11840
11841
11842
11843
11844
11845
11846
11847
11848
11849
11850
11851
11852
11853
11854
11855
11856
11857
11858
11859
11860
.....
12141
12142
12143
12144
12145
12146
12147
12148
12149
12150
12151
12152
12153
12154
12155
12156
12157
12158
12159
12160
12161
12162
12163
12164
12165
12166
12167
12168
.....
12958
12959
12960
12961
12962
12963
12964
12965
12966
12967
12968
12969
12970
12971
12972
12973
.....
13696
13697
13698
13699
13700
13701
13702








13703
13704
13705
13706
13707
13708
13709
.....
14772
14773
14774
14775
14776
14777
14778

14779
14780
14781
14782
14783
14784
14785
.....
14789
14790
14791
14792
14793
14794
14795
14796
14797
14798
14799
14800
14801
14802
14803
14804




14805
14806
14807
14808
14809
14810
14811
14812
14813
14814
14815
14816
14817
14818
14819
14820
14821
14822
.....
14829
14830
14831
14832
14833
14834
14835




14836
14837
14838
14839
14840
14841
14842
.....
14872
14873
14874
14875
14876
14877
14878
14879
14880
14881
14882
14883
14884
14885
14886
.....
14889
14890
14891
14892
14893
14894
14895
14896
14897
14898
14899
14900
14901
14902
14903
.....
14907
14908
14909
14910
14911
14912
14913




14914
14915
14916
14917
14918
14919
14920
.....
16930
16931
16932
16933
16934
16935
16936
16937
16938
16939
16940
16941
16942
16943
16944
16945
.....
17456
17457
17458
17459
17460
17461
17462
17463
17464
17465
17466
17467
17468
17469
17470
17471
17472
17473
17474
17475
17476
17477
17478
17479
17480
17481
17482
17483
17484
17485
.....
17513
17514
17515
17516
17517
17518
17519
17520
17521
17522
17523
17524
17525
17526
17527
17528
.....
17741
17742
17743
17744
17745
17746
17747
17748
17749
17750
17751
17752
17753
17754
17755
17756
.....
18048
18049
18050
18051
18052
18053
18054
18055
18056
18057
18058
18059
18060
18061
18062
18063
18064
18065
18066
18067
18068
18069
18070
18071
18072
18073
18074
18075
18076
18077
18078
18079
.....
18268
18269
18270
18271
18272
18273
18274

18275
18276
18277
18278
18279
18280
18281
.....
19289
19290
19291
19292
19293
19294
19295
19296
19297
19298
19299
19300
19301
19302
19303
.....
19325
19326
19327
19328
19329
19330
19331
19332
19333
19334
19335
19336
19337
19338
19339
.....
19346
19347
19348
19349
19350
19351
19352
19353
19354
19355
19356
19357
19358
19359








19360
19361
19362
19363
19364
19365
19366
.....
19471
19472
19473
19474
19475
19476
19477









19478
19479
19480
19481
19482
19483
19484
.....
19499
19500
19501
19502
19503
19504
19505
19506
19507
19508
19509
19510
19511
19512
19513
.....
19538
19539
19540
19541
19542
19543
19544
19545
19546
19547
19548
19549
19550
19551
19552
.....
19563
19564
19565
19566
19567
19568
19569
19570
19571
19572
19573
19574
19575
19576
19577
.....
19584
19585
19586
19587
19588
19589
19590
19591
19592
19593
19594
19595
19596
19597
19598
19599
19600
19601
19602
19603
19604

















19605
19606
19607
19608
19609
19610
19611
.....
19698
19699
19700
19701
19702
19703
19704
19705
19706
19707
19708
19709
19710
19711
19712
.....
19730
19731
19732
19733
19734
19735
19736
19737
19738
19739









19740
19741
19742
19743
19744
19745
19746
.....
19800
19801
19802
19803
19804
19805
19806
19807
19808
19809
19810
19811
19812
19813
19814
.....
19866
19867
19868
19869
19870
19871
19872
19873
19874
19875
19876
19877
19878
19879
19880
19881
19882
19883

19884
19885
19886
19887
19888
19889
19890
19891
19892
.....
20605
20606
20607
20608
20609
20610
20611
20612
20613
20614
20615
20616
20617
20618
20619
.....
21288
21289
21290
21291
21292
21293
21294
21295
21296
21297
21298
21299
21300
21301
21302
.....
22110
22111
22112
22113
22114
22115
22116

22117

















































































































































































































































































































































































































































































22118






22119
22120
22121
22122
22123
22124
22125
22126
22127



22128
22129
22130
22131
22132



































22133











22134
22135
22136
22137
22138
22139
22140
.....
22146
22147
22148
22149
22150
22151
22152
22153
22154
22155
22156
22157
22158
22159
22160
22161
22162
22163
22164
22165
22166
22167
22168
22169
22170
22171
22172
22173
22174
22175
22176
22177
22178
22179
22180
22181
22182
22183
22184
22185
22186




22187





22188
22189

22190
22191

22192
22193
22194
22195
22196


22197
22198
22199
22200
22201
22202

22203
22204





























































22205


22206
22207

22208




22209
22210
22211
22212
22213





















22214








22215
22216
22217
22218










22219








22220
22221

22222









22223



































22224
22225
22226



22227
22228
22229
22230
22231


22232
22233

22234
22235
22236




22237
22238
22239
22240
22241
22242
22243
22244
22245
22246
22247
22248
22249


22250
22251
22252
22253
22254
22255
22256
.....
22266
22267
22268
22269
22270
22271
22272
22273
22274
22275
22276
22277
22278
22279
22280
22281
22282
22283
22284
22285
22286
22287
22288
22289
22290
22291
22292
22293
22294
22295
22296
22297
22298
22299
22300
22301
22302
22303
22304
22305
22306
22307

22308
22309
22310
22311
22312
22313
22314
.....
22315
22316
22317
22318
22319
22320
22321
22322
22323
22324
22325
22326
22327
22328
22329
.....
22334
22335
22336
22337
22338
22339
22340
22341






22342

22343
22344
22345
22346
22347
22348
22349
.....
22386
22387
22388
22389
22390
22391
22392

22393





22394
22395
22396
22397
22398
22399
22400
22401
22402

22403
22404
22405
22406
22407
22408
22409
22410
22411

22412





22413
22414
22415
22416
22417
22418
22419
.....
22421
22422
22423
22424
22425
22426
22427

22428
22429
22430
22431
22432
22433
22434
.....
22496
22497
22498
22499
22500
22501
22502
















22503
22504
22505
22506
22507
22508
22509
.....
22648
22649
22650
22651
22652
22653
22654
22655
22656
22657
22658
22659
22660
22661
22662
.....
22689
22690
22691
22692
22693
22694
22695

22696

22697
22698
22699
22700
22701
22702
22703
    TSynValidateTableUniqueField instance is created if tfoUnique is in Options)
  - dedicated TSynTableFieldProperties.Filter method for filtering (using
    common TSynFilter classes, working at UTF-8 Text content)
  - faster implementation of Move() for Delphi versions with no FastCode inside
  - new ConvertCaseUTF8(), UpperCaseU(), LowerCaseU(), Int64ToUInt32(),
    GetCardinalDef(), IsValidEmail, IsValidIP4Address(), PatchCodePtrUInt(),
    GetCaptionFromClass(), GetDisplayNameFromClass(), DateTimeToIso8601Text()
    StrUInt32(), StringBufferToUtf8(), IsZero(), AddPrefixToCSV()
    procedures or functions (with associated tests)
  - new grep-like IsMatch() function for basic pattern matching
  - new BinToBase64, Base64ToBin and IsBase64 *fast* conversion functions
    (with optimized assembler version, using CPU pipelining and lookup table)
  - introducing the GarbageCollector TObjectList for handling a global garbage
    collector for instances which must live during the whole executable process
    (used e.g. to avoid a memory leak for "class var" or such variables)




  - great performance improvement in TSynTableFieldProperties for update process
  - now TTextWriter can have a custom internal buffer size (default 1024 bytes)
  - fixed issue in TSynTable.Data() method: ID was not set as expected
  - fixed issue in TSynTableFieldProperties: wrong constraint evaluation and
    index refresh at records update

}
................................................................................
function UTF8ToWideString(const Text: RawUTF8): WideString;

/// convert any UTF-8 encoded String into a generic SynUnicode Text
function UTF8ToSynUnicode(const Text: RawUTF8): SynUnicode;

/// convert any Ansi 7 bit encoded String into a generic VCL Text
// - the Text content must contain only 7 bit pure ASCII characters
function Ansi7ToString(const Text: RawByteString): string;





/// convert any generic VCL Text into Ansi 7 bit encoded String 
// - the Text content must contain only 7 bit pure ASCII characters
function StringToAnsi7(const Text: string): RawByteString;

/// fast Format() function replacement, optimized for RawUTF8
// - only supported token is %, which works only for integer and string type of
................................................................................

{$ifndef PURE_PASCAL}
{$ifndef ISDELPHI2007ANDUP}
/// faster implementation of Move() for Delphi versions with no FastCode inside
procedure Move(const Source; var Dest; Count: Integer);
{$endif}
{$endif}










{$ifndef FPC}  { these asm function use some low-level system.pas calls }

/// use our fast asm RawUTF8 version of Trim()
function Trim(const S: RawUTF8): RawUTF8;

/// use our fast asm version of CompareMem()
................................................................................
    // - returns the index of the added element in the dynamic array
    // - note that because of dynamic array internal memory managment, adding
    // will be a bit slower than e.g. with a TList: the list is reallocated
    // every time a record is added - but in practice, with FastMM4 or
    // SynScaleMM, there is no big speed penalty - for even better speed, you
    // can also specify an external count variable in Init(...,@Count) method 
    function Add(const Elem): integer;



    /// add elements from a given dynamic array
    // - the supplied source DynArray MUST be of the same exact type as the
    // current used for this TDynArray
    // - you can specify the start index and the number of items to take from
    // the source dynamic array (leave as -1 to add till the end)
    procedure AddArray(const DynArray; aStartIndex: integer=0; aCount: integer=-1);
    /// add an element to the dynamic array at the position specified by Index
................................................................................
  // - wkVarUInt32 will write the content using our 32-bit variable-length integer
  // encoding
  // - wkVarInt32 will write the content using our 32-bit variable-length integer
  // encoding and the by-two complement (0=0,1=1,2=-1,3=2,4=-2...)
  // - wkSorted will write an increasing array of integers, handling the special
  // case of a difference of 1 between two values (therefore is very optimized
  // to store an array of IDs)
  // - wkOffset will write the difference between two successive values, handling
  // constant difference in an optimized manner
  TFileBufferWriterKind = (wkUInt32, wkVarUInt32, wkVarInt32, wkSorted, wkOffset);


  PFileBufferWriter = ^TFileBufferWriter;

  /// this structure can be used to speed up writing to a file
  // - big speed up if data is written in small blocks
  // - also handle optimized storage of any dynamic array of Integer/Int64/RawUTF8
  // - is defined either as an object either as a record, due to a bug
................................................................................
    // used by ReadChunk() method
    fBufTemp: RawByteString;
    /// get Isize + buffer from current memory map or fBufTemp into (P,PEnd)
    procedure ReadChunk(var P, PEnd: PByte);
  public
    /// initialize the buffer, and specify a file to use for reading
    procedure Open(aFile: THandle);



    /// close all internal mapped files
    // - call Open() again to use the Read() methods
    procedure Close;
    {$ifndef CPU64}
    /// change the current reading position, from the beginning of the file
    // - returns TRUE if success, or FALSE if Offset is out of range
    function Seek(Offset: Int64): boolean; overload;
................................................................................
    function Read(Data: pointer; DataLen: integer): integer; overload;
    /// read some UTF-8 encoded text at the current position
    // - returns the resulting text length, in bytes
    function Read(out Text: RawUTF8): integer; overload;
    /// read one byte
    // - if reached end of file, don't raise any error, but returns 0
    function ReadByte: PtrUInt; {$ifdef HASINLINE}inline;{$endif}



    /// read one cardinal value encoded using our 32-bit variable-length integer
    function ReadVarUInt32: PtrUInt;
    /// read one integer value encoded using our 32-bit variable-length integer,
    // and the by-two complement
    function ReadVarInt32: PtrInt;
    /// read one UInt64 value encoded using our 64-bit variable-length integer
    function ReadVarUInt64: QWord;
................................................................................
// in big-endian order (most-signignifican byte first): use it for display
procedure BinToHexDisplay(Bin, Hex: PAnsiChar; BinBytes: integer);

/// fast conversion from a pointer data into hexa chars, ready to be displayed
// - use internally BinToHexDisplay()
function PointerToHex(aPointer: Pointer): RawUTF8;




/// fast conversion from binary data into Base64 encoded text
function BinToBase64(const s: RawByteString): RawByteString; overload;

/// fast conversion from binary data into Base64 encoded text
function BinToBase64(Bin: PAnsiChar; BinBytes: integer): RawByteString; overload;

/// fast conversion from binary data into Base64 encoded text
................................................................................
function GetDelphiCompilerVersion: RawUTF8;



{ ************ Logging classes and functions }

type












































































  /// the available logging events, as handled by TSynLog
  // - sllError will log errors
  // - sllInfo will log general information events
  // - sllDebug will log detailed debugging information
  // - sllLastError will log the GetLastError OS message
  // - sllException will log all exception raised - available since Windows XP
  // - sllExceptionOS will log all OS low-level exceptions (EDivByZero,
................................................................................
      Instance: TObject=nil); overload;
    /// call this method to add some information to the log at a specified level
    // - if Instance is set and Text is not '', it will log the corresponding
    // class name and address (to be used e.g. if you didn't call TSynLog.Enter()
    // method first)
    // - if Instance is set and Text is '', will behave the same as
    // Log(Level,Instance), i.e. write the Instance as JSON content
    procedure Log(Level: TSynLogInfo; const Text: RawUTF8='';
      Instance: TObject=nil); overload;
    /// call this method to add the content of an object to the log at a
    // specified level
    // - TSynLog will write the class and hexa address - TSQLLog will write the
    // object JSON content
    procedure Log(Level: TSynLogInfo; Instance: TObject); overload;
    /// call this method to add the content of most low-level types to the log
    // at a specified level
    // - TSynLog will handle enumerations and dynamic array; TSQLLog will be
    // able to write TObject/TSQLRecord and sets content as JSON
    procedure Log(Level: TSynLogInfo; aName: PWinAnsiChar;
      aTypeInfo: pointer; var aValue); overload;




    /// retrieve the associated logging instance
    function Instance: TSynLog;
  end;

  {{ regroup several logs under an unique family name
   - you should usualy use one family per application or per architectural
     module: e.g. a server application may want to log in separate files the
................................................................................
    fIdent: integer;
    fDestinationPath: TFileName;
    fBufferSize: integer;
    fHRTimeStamp: boolean;
    fWithUnitName: boolean;
    fAutoFlush: cardinal;
    fHandleExceptions: boolean;


    function CreateSynLog: TSynLog;
    procedure SetAutoFlush(TimeOut: cardinal);
    procedure SetLevel(const aLevel: TSynLogInfos);
  public
    /// intialize for a TSynLog class family
    // - add it in the global SynLogFileFamily[] list
    constructor Create(aSynLog: TSynLogClass);
    /// release associated memory
    destructor Destroy; override;
    /// retrieve the corresponding log file of this thread and family
    // - creates the TSynLog if not already existing for this current thread
    function SynLog: TSynLog;



  published
    /// the associated TSynLog class
    property SynLogClass: TSynLogClass read fSynLogClass;
    /// index in global SynLogFileFamily[] and threadvar SynLogFileIndex[] lists
    property Ident: integer read fIdent;
    /// the current level of logging information for this family
    property Level: TSynLogInfos read fLevel write SetLevel;
................................................................................
    // disk, whatever the current content size is
    // - by default, the log file will be written for every 4 KB of log - this
    // will ensure that the main application won't be slow down by logging
    // - in order not to loose any log, a background thread can be created
    // and will be responsible of flushing all pending log content every
    // period of time (e.g. every 10 seconds)
    property AutoFlushTimeOut: cardinal read fAutoFlush write SetAutoFlush;




  end;
  
  /// used to store the identification of one recursivity level
  TSynLogCurrentIdent = packed record
    Instance: TObject;
    ClassType: TClass;
    Method: PUTF8Char;

  end;
  PSynLogCurrentIdent = ^TSynLogCurrentIdent;
  /// used to store the identification of all recursivity levels
  TSynLogCurrentIdents = array[0..maxInt div sizeof(TSynLogCurrentIdent)-1] of TSynLogCurrentIdent;
  PSynLogCurrentIdents = ^TSynLogCurrentIdents;

  /// a per-family and/or per-thread log file content
  // - you should create a sub class per kind of log file
  // ! TSynLogDB = class(TSynLog);
  // - the TSynLog instance won't be allocated in heap, but will share a
  // per-thread (if Family.PerThreadLog=TRUE) or global private log file instance
  // - was very optimized for speed, if no logging is written, and even during
  // log write (using an internal TTextWriter)


  TSynLog = class(TObject, ISynLog)
  protected
    fWriter: TTextWriter;
    fFamily: TSynLogFamily;
    fWriterStream: TStream;
    fHeaderWritten: boolean;
    fLockCS: PRTLCriticalSection;
    fStartTimeStamp: Int64;
    fFileName: TFileName;
    /// set by Enter() method
    fRecursionCount: integer;
    fRecursionMax: integer;
    fRecursion: PSynLogCurrentIdents;

    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    class function FamilyCreate: TSynLogFamily;
    procedure DoEnterLeave(aLevel: TSynLogInfo);
    procedure CreateLogWriter; virtual;
    procedure LogInternal(Level: TSynLogInfo; TextFmt: PWinAnsiChar;
................................................................................
     - if you just need to access the log inside the method block, you may
     not need any ISynLog interface:
     ! procedure TMyDB.SQLFlush;
     ! begin
     !   TSynLogDB.Enter(self,'SQLFlush');
     !   // do some stuff
     ! end;











     - Enter() will write the class name (and the unit name for classes with
     published properties, if TSynLogFamily.WithUnitName is true) for both
     enter (+) and leave (-) events:
      $ 20110325 19325801  +    MyDBUnit.TMyDB(004E11F4).SQLExecute
      $ 20110325 19325801 info   SQL=SELECT * FROM Table;
      $ 20110325 19325801  -    MyDBUnit.TMyDB(004E11F4).SQLExecute }
    class function Enter(aInstance: TObject; aMethodName: PUTF8Char): ISynLog; overload;
    {{ to be called and assigned to a ISynLog interface at the beginning
     of a method
     - this is the main method to be called within a class method:
     ! class function TMyDB.SQLValidate(const SQL: RawUTF8): boolean;
     ! var ILog: ISynLog;
     ! begin
     !   ILog := TSynLogDB.Enter(self,'SQLValidate');
................................................................................
     - if you just need to access the log inside the method block, you may
     not need any ISynLog interface:
     ! class procedure TMyDB.SQLFlush;
     ! begin
     !   TSynLogDB.Enter(self,'SQLFlush');
     !   // do some stuff
     ! end;











     - Enter() will write the class name (and the unit name for classes with
     published properties, if TSynLogFamily.WithUnitName is true) for both
     enter (+) and leave (-) events:
      $ 20110325 19325801  +    MyDBUnit.TMyDB.SQLValidate
      $ 20110325 19325801 info   SQL=SELECT * FROM Table returned 1;
      $ 20110325 19325801  -    MyDBUnit.TMyDB.SQLValidate }
    class function Enter(aClassType: TClass; aMethodName: PUTF8Char): ISynLog; overload;
    /// retrieve the current instance of this TSynLog class
    // - to be used for direct logging, without any Enter/Leave:
    // ! TSynLogDB.Add.Log(llError,'The % statement didn't work',[SQL]);
    // - is just a wrapper around Family.SynLog - the same code will work:
    // ! TSynLogDB.Family.SynLog.Log(llError,'The % statement didn't work',[SQL]);
    class function Add: TSynLog;
      {$ifdef HASINLINE}inline;{$endif}
................................................................................
    // class name and address (to be used e.g. if you didn't call TSynLog.Enter()
    // method first) - for instance
    // ! TSQLLog.Add.Log(sllDebug,'GarbageCollector',GarbageCollector);
    // will append this line to the log:
    // $ 0000000000002DB9 debug TObjectList(00425E68) GarbageCollector
    // - if Instance is set and Text is '', will behave the same as
    // Log(Level,Instance), i.e. write the Instance as JSON content
    procedure Log(Level: TSynLogInfo; const Text: RawUTF8='';
      aInstance: TObject=nil); overload;
    /// call this method to add the content of an object to the log at a
    // specified level
    // - this default implementation will just write the class name and its hexa
    // pointer value, and handle TList, TCollections and TStrings - for instance:
    // ! TSynLog.Add.Log(sllDebug,GarbageCollector);
    // will append this line to the log:
................................................................................
    /// call this method to add the content of most low-level types to the log
    // at a specified level
    // - this overriden implementation will write the value content,
    // written as human readable JSON: handle dynamic arrays and enumerations
    // - TSQLLog from SQLite3Commons unit will be able to write
    // TObject/TSQLRecord and sets content as JSON
    procedure Log(Level: TSynLogInfo; aName: PWinAnsiChar;
      aTypeInfo: pointer; var aValue); overload; 




  published
    /// the associated logging family
    property GenericFamily: TSynLogFamily read fFamily;
    /// the associated file name containing the log
    // - this is accurate only with the default implementation of the class:
    // any child may override it with a custom logging mechanism
    property FileName: TFileName read fFileName;
................................................................................
var L2: integer;
begin
  result := ''; // somewhat faster if result is freed before any SetLength()
  if L=0 then
    L := StrLen(P);
  if L=0 then
    exit;
  SetLength(result,L); // maximum posible unicode size (if all <#128)
  L2 := UTF8ToWideChar(pointer(result),P,L) shr 1;
  if L2<>L then
    SetLength(result,L2);
end;

function WinAnsiToUnicodeString(WinAnsi: PAnsiChar; WinAnsiLen: integer): UnicodeString;
var i: integer;
begin
  SetLength(result,WinAnsiLen);
  for i := 0 to WinAnsiLen-1 do
    PWordArray(result)[i] := WinAnsiTableA[WinAnsi[i]]; // very fast conversion
end;
{$endif}

{$ifdef UNICODE}
function Ansi7ToString(const Text: RawByteString): string;
var i: integer;
begin
  SetLength(result,length(Text));
  for i := 0 to length(Text)-1 do
    PWordArray(result)[i] := PByteArray(Text)[i]; // no conversion for 7 bit Ansi
end;
{$else}
function Ansi7ToString(const Text: RawByteString): string;
begin
  result := Text; // if we are SURE this text is 7 bit Ansi -> direct assign
end;
{$endif}
















{$ifdef UNICODE}
function StringToAnsi7(const Text: string): RawByteString;
var i: integer;
begin
  SetLength(result,length(Text));
  for i := 0 to length(Text)-1 do
    PByteArray(result)[i] := PWordArray(Text)[i]; // no conversion for 7 bit Ansi
end;
{$else}
function StringToAnsi7(const Text: string): RawByteString;
begin
  result := Text; // if we are SURE this text is 7 bit Ansi -> direct assign
................................................................................
  SetString(result,PAnsiChar(pointer(S)),length(S)*2+1); // +1 for last wide #0
end;
{$else}
function StringToRawUnicode(const S: string): RawUnicode;
var i, L: integer;
begin
  L := length(S);
  SetLength(result,L*2+1); // +1 for last wide #0
  if GetACP<>1252 then begin
    // low-level MBCS RTL function including last widechar #0
    SetLength(result,MultiByteToWideChar(GetACP, 0, Pointer(s), L, pointer(result), L));
  end else
    // fast WinAnsi conversion
    for i := 0 to L do // includes S[L]=#0 -> last widechar #0
      PWordArray(result)[i] := WinAnsiTable[PByteArray(S)[i]];
................................................................................
// this Pos() is seldom used, it was decided to only define it under
// Delphi 2009/2010/XE (which expect such a RawUTF8 specific overloaded version)

function Pos(const substr, str: RawUTF8): Integer; overload;
begin
  Result := PosEx(substr, str, 1);
end;

























































{$endif UNICODE}

{$ifndef ISDELPHI2007ANDUP}
/// faster implementation of Move() for Delphi versions with no FastCode inside
procedure Move(const Source; var Dest; Count: Integer);
asm // eax=source edx=dest ecx=count
................................................................................
      if byte(U^) and $80<>0 then break; // 7 bits char check only
    until false;
{$endif}
    // find beginning of next word
    U := FindNextUTF8WordBegin(U);
  until U=nil;
end;






















function HexToBin(Hex: PAnsiChar; Bin: PByte; BinBytes: Integer): boolean;
var I: Integer;
    B,C: byte;
begin
  result := false; // return false if any invalid char
  if Pointer(ConvertHexToBin)=nil then
................................................................................
var F: THandle;
    Size: integer;
begin
  result := '';
  if FileName='' then
    exit;
  F := FileOpen(FileName,fmOpenRead or fmShareDenyNone);
  if F<>THandle(-1) then begin
{$ifdef LINUX}
    Size := FileSeek(F,0,soFromEnd);
    FileSeek(F,0,soFromBeginning);
{$else}
    Size := GetFileSize(F,nil);
{$endif}
    SetLength(result,Size);
................................................................................
function FileFromString(const Content: RawByteString; const FileName: TFileName;
  FlushOnDisk: boolean=false): boolean;
var F: THandle;
    L: integer;
begin
  result := false;
  F := FileCreate(FileName);
  if F=THandle(-1) then
    exit;
  if pointer(Content)<>nil then
    L := FileWrite(F,pointer(Content)^,length(Content)) else
    L := 0;
  result := (L=length(Content));
{$ifdef MSWINDOWS}
  if FlushOnDisk then
................................................................................
    SetString(result,tmp,15);
  end;
end;

function DateToIso8601(Date: TDateTime; Expanded: boolean): RawUTF8;
// we use YYYYMMDDTdate format
begin
  result := '';
  SetLength(result,8+2*integer(Expanded));
{$ifdef UNICODE2} // not needed: SetLength() did already set the codepage
  PWord(PtrUInt(result)-12)^ := CP_UTF8; // use only SetLength() -> force set code page
{$endif}
  DateToIso8601PChar(Date,pointer(result),Expanded);
end;

/// basic Date conversion into ISO-8601
// - use 'YYYYMMDD' format if not Expanded
// - use 'YYYY-MM-DD' format if Expanded
function DateToIso8601(Y,M,D: cardinal; Expanded: boolean): RawUTF8; overload;
begin
  result := '';
  SetLength(result,8+2*integer(Expanded));
{$ifdef UNICODE2} // not needed: SetLength() did already set the codepage
  PWord(PtrUInt(result)-12)^ := CP_UTF8; // use only SetLength() -> force set code page
{$endif}
  DateToIso8601PChar(pointer(result),Expanded,Y,M,D);
end;

function TimeToIso8601(Time: TDateTime; Expanded: boolean): RawUTF8;
// we use Thhmmss format
begin
  result := '';
  SetLength(result,7+2*integer(Expanded));
{$ifdef UNICODE2} // not needed: SetLength() did already set the codepage
  PWord(PtrUInt(result)-12)^ := CP_UTF8; // use only SetLength() -> force set code page
{$endif}
  TimeToIso8601PChar(Time,pointer(result),Expanded);
end;

function DateTimeToIso8601Text(DT: TDateTime): RawUTF8;
................................................................................
{$ifdef MSWINDOWS}
    Now: TSystemTime; {$else}
    D: TDateTime;     {$endif}
begin
  if aFileName='' then
    exit;
  F := FileOpen(aFileName,fmOpenWrite);
  if F=THandle(-1) then begin
    F := FileCreate(aFileName);
    if F=THandle(-1) then
      exit;
  end;
   // append to end of file
  if FileSeek(F,0,soFromEnd)>MAXLOGSIZE then begin
    // rotate log file if too big
    FileClose(F);
    Old := aFileName+'.bak'; // '.log.bak'
    DeleteFile(Old); // rotate once
    RenameFile(aFileName,Old);
    F := FileCreate(aFileName);
    if F=THandle(-1) then
      exit;
  end;
  PWord(@Date)^ := 13+10 shl 8; // first go to next line
{$ifdef MSWINDOWS}
  GetLocalTime(Now); // windows dedicated function
  DateToIso8601PChar(@Date[3],true,Now.wYear,Now.wMonth,Now.wDay);
  TimeToIso8601PChar(@Date[13],true,Now.wHour,Now.wMinute,Now.wSecond,' ');
................................................................................
          BuildYear := SystemTime.wYear;
        end;
      end;
    finally
      Freemem(Pt);
    end;
  end;
  Main := SysUtils.IntToStr(Major) + '.' + SysUtils.IntToStr(Minor);
  Detailed := Main+ '.' + SysUtils.IntToStr(Release) + '.' + SysUtils.IntToStr(Build);
  if BuildDateTime=0 then // get build date from file age
    BuildDateTime := FileAgeToDateTime(FileName);
end;

function TFileVersion.Version32: integer;
begin
  result := Major shl 16+Minor shl 8+Release;
................................................................................
    exit; // avoid GPF if void
  SetCount(result+1);
  P := pointer(PtrUInt(Value^)+PtrUInt(result)*ElemSize);
  if ElemType=nil then
    move(Elem,P^,ElemSize) else
    CopyArray(P,@Elem,ElemType,1);
end;









procedure TDynArray.Insert(Index: Integer; const Elem);
var n: integer;
    P: PByteArray;
begin
  if Value=nil then
    exit; // avoid GPF if void
................................................................................
    result := Hasher(0,Pointer(PtrUInt(Elem)),PInteger(PtrUInt(Elem)-4)^*2);
end;

procedure TDynArrayHashed.Init(aTypeInfo: pointer; var aValue;
      aHashElement: TDynArrayHashOne=nil; aCompare: TDynArraySortCompare=nil;
      aHasher: THasher=nil; aCountPointer: PInteger=nil);
var FieldTable: PFieldTable;

begin
  inherited Init(aTypeInfo,aValue,aCountPointer);
  if @aHasher=nil then begin
    if @DefaultHasher=nil then
      DefaultHasher := @kr32;
     fHasher := DefaultHasher;
  end else
................................................................................
    case PByte(ElemType)^ of
      tkLString:
        aHashElement := HashAnsiString;
      tkWString{$ifdef UNICODE}, tkUString{$endif}:
        aHashElement := HashUnicodeString;
      tkRecord: begin
        FieldTable := ElemType;
        inc(PtrUInt(FieldTable),ord(FieldTable^.Name[0]));
        if FieldTable^.Count>0 then
        with FieldTable^.Fields[0] do
          if Offset=0 then
          case TypeInfo^^.Kind of
            tkLString:
              aHashElement := HashAnsiString;
            tkWString{$ifdef UNICODE}, tkUString{$endif}:
              aHashElement := HashUnicodeString;




          end;
      end;
    end;
  fHashElement := aHashElement;
  if @aCompare=nil then begin
    aCompare := SORTFIRSTFIELD[TypeInfoToKnown(aTypeInfo)];
    if @aCompare=nil then
      // will match default HashOne algo, i.e. hash first field of record
      if (ElemType<>nil) and (PByte(ElemType)^=tkRecord) then begin
        FieldTable := ElemType;
        inc(PtrUInt(FieldTable),ord(FieldTable^.Name[0]));
        if FieldTable^.Count=0 then
        case ElemSize of
          1: aCompare := @SortDynArrayByte;
          2: aCompare := @SortDynArrayWord;
          4: aCompare := @SortDynArrayCardinal;
          8: aCompare := @SortDynArrayInt64;
        end else
................................................................................
            8: aCompare := @SortDynArrayInt64;
          end else
          case TypeInfo^^.Kind of
          tkLString:
            aCompare := @SortDynArrayAnsiString;
          tkWString{$ifdef UNICODE}, tkUString{$endif}:
            aCompare := @SortDynArrayUnicodeString;




          end;
      end;
  end;
  fCompare := aCompare;
  ReHash;
end;

................................................................................
      end;
  until false;
  result := -1; // mark not found
end;

function TDynArrayHashed.HashOne(const Elem): cardinal;
var FieldTable: PFieldTable;
label Bin, LStr, WStr;
begin
  if @fHashElement<>nil then
    result := fHashElement(Elem,fHasher) else
  if ElemType=nil then
    goto Bin else
    case PByte(ElemType)^ of
    tkLString:
................................................................................
        result := fHasher(0,Pointer(PtrUInt(Elem)),PInteger(PtrUInt(Elem)-4)^);
    tkWString{$ifdef UNICODE}, tkUString{$endif}:
WStr: if PtrUInt(Elem)=0 then
        result := 0 else
        result := fHasher(0,Pointer(PtrUInt(Elem)),PInteger(PtrUInt(Elem)-4)^*2);
    tkRecord: begin
      FieldTable := ElemType;
      inc(PtrUInt(FieldTable),ord(FieldTable^.Name[0]));
      if FieldTable^.Count=0 then
        // only binary content -> hash full content
Bin:    case ElemSize of 
          1: result := byte(Elem);
          2: result := word(Elem);
          4: result := cardinal(Elem);
          8: result := Int64Rec(Elem).Lo xor Int64Rec(Elem).Hi;
................................................................................
      if Offset<>0 then
        // hash whole starting binary content of record
        result := fHasher(0,@Elem,Offset) else
        // hash first string field of record
        case TypeInfo^^.Kind of
        tkLString: goto LStr;
        tkWString{$ifdef UNICODE}, tkUString{$endif}: goto WStr;




        else result := 0; // unhandled type 
        end;
    end;
    else result := 0;
    end;
  if result=0 then
    result := 1; // never return 0 (indicates a free position in fHashs[])
................................................................................
  fOwner := Owner;
end;

function TSynTestCase.RandomString(CharCount: Integer): RawByteString;
var V: cardinal;
    P: PAnsiChar;
begin
  Result := '';
  SetLength(Result,CharCount);
  P := pointer(Result);
  while CharCount>0 do begin
    if CharCount>5 then begin
      V := Random(maxInt); // fast: one random compute per 5 chars
      P[0] := AnsiChar(32+V and 127); V := V shr 7;
      P[1] := AnsiChar(32+V and 127); V := V shr 7;
      P[2] := AnsiChar(32+V and 127); V := V shr 7;
................................................................................
    Check(Cities[i].Name=''); // ForAddind = not added
    Cities[i] := City;
  end;
  Check(ACities.Count=3);
  Check(City.Name='Iasi');
  Check(ACities.FindHashed(City)>=0);
  for i := 1 to 2000 do begin
    City.Name := IntToStr(i);
    City.Latitude := i*3.14;
    City.Longitude := i*6.13;
    Check(ACities.FindHashedAndUpdate(City,true)=i+2,'multiple ReHash');
    Check(ACities.FindHashed(City)=i+2);
  end;
  ACities.Capacity := CITIES_MAX+3; // make it as fast as possible
  for i := 2001 to CITIES_MAX do begin
    City.Name := IntToStr(i);
    City.Latitude := i*3.14;
    City.Longitude := i*6.13;
    Check(ACities.FindHashedAndUpdate(City,true)=i+2,'use Capacity: no ReHash');
    Check(ACities.FindHashed(City.Name)=i+2);
  end;
  for i := 1 to CITIES_MAX do begin
    N := IntToStr(i);
    j := ACities.FindHashed(N);
    Check(j=i+2,'hashing with string not City.Name');
    Check(Cities[j].Name=N);
    CheckSame(Cities[j].Latitude,i*3.14);
    CheckSame(Cities[j].Longitude,i*6.13);
  end;
end;
................................................................................
    JSON_BASE64_MAGIC_UTF8: RawUTF8;
procedure Fill(var F: TFileVersion; i: integer);
begin
  F.Major := i;
  F.Minor := i+1;
  F.Release := i+2;
  F.Build := i+3;
  F.Main := IntToStr(i+1000);
  F.Detailed := IntToStr(2000-i);
  F.BuildDateTime := 36215.12;
  F.BuildYear := i+2011;
end;
begin
  W := TTextWriter.CreateOwnedStream;
  // validate TIntegerDynArray 
  AIP.Init(TypeInfo(TIntegerDynArray),AI);
................................................................................
  Check(RecordEquals(F,AF[100],TypeInfo(TFileVersion)));
  for i := 0 to 1000 do
    with AF[i] do begin
      Check(Major=i);
      Check(Minor=i+1);
      Check(Release=i+2);
      Check(Build=i+3);
      Check(Main=IntToStr(i+1000));
      Check(Detailed=IntToStr(2000-i));
      CheckSame(BuildDateTime,36215.12);
      Check(BuildYear=i+2011);
    end;
  for i := 0 to 1000 do begin
    Fill(F,i);
    Check(AFP.IndexOf(F)=i);
  end;
................................................................................
{$endif}
  for i := 1 to 10000 do begin
    j := Random(maxInt)-Random(maxInt);
    str(j,a);
    s := RawUTF8(a);
    Check(kr32(0,pointer(s),length(s))=kr32pas(pointer(s),length(s)));
    u := string(a);
    Check(IntToStr(j)=u);
    Check(Int32ToUtf8(j)=s);
    Check(format('%d',[j])=u);
    Check(GetInteger(pointer(s))=j);
    Check(FormatUTF8('%',[j])=s);
    k := Int64(j)*Random(MaxInt);
    str(k,a);
    s := RawUTF8(a);
    u := string(a);
    Check(IntToStr(k)=u);
    Check(Int64ToUtf8(k)=s);
    Check(format('%d',[k])=u);
    Check(FormatUTF8('%',[k])=s);
    err := 1;
    l := GetInt64(pointer(s),err);
    Check((err=0)and(l=k));
    str(j,a);
    Check(IntToStr(j)=string(a));
    Check(format('%d',[j])=string(a));
    Check(format('%.8x',[j])=IntToHex(j,8));
    d := Random*1E-17-Random*1E-9;
    str(d,a);
    s := RawUTF8(a);
    e := GetExtended(Pointer(s),err);
    Check(SameValue(e,d)); // test str()
................................................................................
    Check(T.AddField('double',tftDouble)<>nil);
    Check(T.AddField('varint',tftVarUInt32)<>nil);
    Check(T.AddField('text',tftUTF8,[tfoUnique])<>nil);
    Check(T.AddField('ansi',tftWinAnsi,[])<>nil);
    Check(T.AddField('currency',tftCurrency)<>nil);
    Test;
    FN := ChangeFileExt(paramstr(0),'.syntable');

    f := FileCreate(FN); // manual storage of TSynTable header
    W.AssignToFile(f);
    T.SaveTo(W);
    W.Flush;
    FileClose(f);
    T.Free;
    f := FileOpen(FN,fmOpenRead);
................................................................................
  WriteVarUInt32(ValuesCount);
  if ValuesCount=0 then
    exit;
  PI := pointer(Values);
  PByteArray(fBuf)^[fPos] := ord(DataLayout);
  inc(fPos);
  inc(fTotalWritten);
  if DataLayout=wkOffset then begin
    pos := fPos;
    fPos := PtrUInt(ToVarUInt32(PI^[0],@PByteArray(fBuf)^[fPos]))-PtrUInt(fBuf);
    diff := PI^[1]-PI^[0];
    inc(PtrUInt(PI),4);
    dec(ValuesCount);
    if ValuesCount=0 then begin
      inc(fTotalWritten,PtrUInt(fPos-pos));
................................................................................
      wkUInt32: begin
        n := (fBufLen-fPos)shr 2;
        if ValuesCount<n then
          n := ValuesCount;
        Move(PI^,P^,n*4);
        inc(P,n*4);
      end;
      wkVarInt32, wkVarUInt32,wkOffset: begin
        PBeg := PAnsiChar(P); // leave space for chunk size
        inc(P,4);
        n := ValuesCount;
        case DataLayout of
        wkVarInt32:
          for i := 0 to ValuesCount-1 do begin
            P := ToVarInt32(PI^[i],P);
................................................................................
          for i := 0 to ValuesCount-1 do begin
            P := ToVarUInt32(PI^[i],P);
            if PtrUInt(P)>=PtrUInt(PEnd) then begin
              n := i+1;
              break; // avoid buffer overflow
            end;
          end;
        wkOffset:
          for i := 0 to ValuesCount-1 do begin
            P := ToVarUInt32(PI^[i]-PI^[i-1],P);
            if PtrUInt(P)>=PtrUInt(PEnd) then begin
              n := i+1;
              break; // avoid buffer overflow
            end;








          end;
        end;
        PInteger(PBeg)^ := PAnsiChar(P)-PBeg-4; // format: Isize+varUInt32s
      end;
      wkSorted: begin
        PBeg := PAnsiChar(P)+4; // leave space for chunk size
        P := PByte(CleverStoreInteger(pointer(PI),PBeg,PEnd,ValuesCount,n));
................................................................................
  end;
end;

procedure TFileBufferReader.ErrorInvalidContent;
begin
  raise Exception.Create('TFileBufferReader: invalid content');
end;










procedure TFileBufferReader.Open(aFile: THandle);
begin
  fCurrentPos := 0;
  fBuf := nil;
  fMap := 0;
  fFile := aFile;
................................................................................
  end;
end;

function TFileBufferReader.Read(Data: pointer; DataLen: integer): integer;
var len: integer;
begin
  if DataLen>0 then
    if fMap<>0 then begin
      // file up to 2 GB: use fast memory map
      len := fBufSize-fCurrentPos;
      if len>DataLen then
        len := DataLen;
      move(fBuf[fCurrentPos],Data^,len);
      inc(fCurrentPos,len);
      result := len;
................................................................................
    ErrorInvalidContent;
  PEnd := pointer(PtrUInt(P)+PtrUInt(len));
end;

function TFileBufferReader.ReadPointer(DataLen: PtrUInt;
  var aTempData: RawByteString): pointer;
begin
  if fMap=0 then begin
    // read from file
    if DataLen>PtrUInt(Length(aTempData)) then begin
      aTempData := ''; // so no move() call in SetLength() below
      SetLength(aTempData,DataLen);
    end;
    if PtrUInt(FileRead(fFile,pointer(aTempData)^,DataLen))<>DataLen then
      result := nil else // invalid content
................................................................................

{$ifndef LVCL}
function TFileBufferReader.ReadStream: TCustomMemoryStream;
var DataLen: PtrUInt;
begin
  DataLen := ReadVarUInt32;
  if DataLen<>0 then
    if fMap=0 then begin
      // use temporary TMemoryStream only if not in memory map buffer
      result := TMemoryStream.Create;
      result.Size := DataLen;
      if PtrUInt(FileRead(fFile,result.Memory^,DataLen))<>DataLen then
        FreeAndNil(result); // invalid content
    end else
    if DataLen+fCurrentPos>fBufSize then
................................................................................
    // DataLen=0 -> invalid content
    result := nil;
end;
{$endif}

function TFileBufferReader.ReadByte: PtrUInt;
begin
  if fMap<>0 then
    if fCurrentPos>=fBufSize then
      // invalid request
      result := 0 else begin
      // read one byte from current memory map
      result := ord(fBuf[fCurrentPos]);
      inc(fCurrentPos);
    end else begin
      // read from file if >= 2 GB (slow, but works)
      result := 0;
      if FileRead(fFile,result,1)<>1 then
        result := 0;
    end;
end;


















function TFileBufferReader.ReadVarUInt32: PtrUInt;
var c, n: PtrUInt;
begin
  result := ReadByte;
  if result>$7f then begin
    n := 0;
................................................................................
  if result=0 then
    exit;
  count := result;
  if count>length(Values) then // only set length is not big enough
    SetLength(Values,count);
  PI := pointer(Values);
  DataLayout := TFileBufferWriterKind(ReadByte);
  if DataLayout=wkOffset then begin
    PI^ := ReadVarUInt32;
    dec(count);
    diff := ReadVarUInt32;
    if diff<>0 then begin
      for i := 0 to count-1 do
        PIA^[i+1] := PIA^[i]+diff;
      exit;
................................................................................
          inc(PI);
        end;
      wkSorted: begin
        n := CleverReadInteger(pointer(P),pointer(PEnd),PI);
        dec(count,n);
        inc(PtrUInt(PI),n*4);
      end;
      wkOffset: begin
        while (count>0) and (PtrUInt(P)<PtrUInt(PEnd)) do begin
          PIA^[1] := PIA^[0]+integer(FromVarUInt32(P)); 









          dec(count);
          inc(PI);
        end;
        if count<=0 then
          inc(PI); // make sure PI=@Values[result]
      end;
      else
................................................................................
  assert((count=0)and(PI=@Values[result]));
end;

function TFileBufferReader.ReadRawUTF8List(List: TRawUTF8List): boolean;
var i: integer;
    StoreObjectsAsVarUInt32: Boolean;
begin
  if (fMap<>0) and (List<>nil) then
  with List do begin
    BeginUpdate;
    try
      Capacity := 0; // finalize both fObjects[] and fList[]
      fCount := ReadVarRawUTF8DynArray(List.fList);
      result := true;
      if fCount=0 then
................................................................................
end;

{$ifndef CPU64}
function TFileBufferReader.Seek(Offset: Int64): boolean;
begin
  if (Offset<0) or (Offset>fFileSize) then
    result := False else
  if fMap=0 then
    result := FileSeek64(fFile,Offset,soFromBeginning)=Offset else begin
    fCurrentPos := Int64Rec(Offset).Lo;
    result := true;
  end;
end;
{$endif CPU64}

function TFileBufferReader.Seek(Offset: PtrInt): boolean;
begin
  // we don't need to handle fMap=0 here

  if fMap=0 then Result := FileSeek(fFile,Offset,0)=Offset else
  if (fMap<>0) and (PtrUInt(Offset)<PPtrUInt(@fFileSize)^) then begin
    fCurrentPos := Offset;
    result := true;
  end else
    result := false;
end;


................................................................................
    inc(P,Len);
    inc(TotalLen,Len);
    Lens[F] := Len;
  end else
    inc(TotalLen,fDefaultFieldLength);
  // create new record content
  P := RecordBuffer;
  Setlength(Result,TotalLen);
  Dest := pointer(Result);
  for F := 0 to fField.Count-1 do
  with TSynTableFieldProperties(fField.List[F]) do
    if F in AvailableFields then begin
      Len := Lens[F];
      move(P^,Dest^,Len);
      inc(P,Len);
................................................................................
    Head: integer;
begin
  if FieldType<>tftBlobInternal then
    result := '' else
  if (Value=nil) or (ValueLen=0) then
    result := #0 else begin // inlined ToSBFStr() code
    Head := PAnsiChar(ToVarUInt32(ValueLen,@tmp))-tmp;
    SetLength(result,ValueLen+Head);
    Move(tmp,PByteArray(Result)[0],Head);
    Move(Value^,PByteArray(Result)[Head],ValueLen);
  end;
end;

function TSynTableFieldProperties.SBFFloat(const Value: Double): TSBFString;
begin
................................................................................
function TSynTableData.ValidateSBFValue(RecordIndex: integer): string;
begin
  CheckVTableInitialized;
  Result := VTable.Validate(Pointer(VValue),RecordIndex);
end;





















































































































































































































































































































































































































































































{ TSynLogFamily }







type
  TExceptProc = procedure(Obj: TObject; Addr: Pointer);

var
  /// internal list of registered TSynLogFamily
  // - up to MAX_SYNLOGFAMILY+1 families may be defined
  SynLogFamily: TObjectList = nil;




threadvar
  CurrentHandleExceptionSynLog: TSynLog;

var
  SynLogExceptionEnabled: boolean = false;















































type
  PExceptionRecord = ^TExceptionRecord;
  TExceptionRecord = packed record
    ExceptionCode        : LongWord;
    ExceptionFlags       : LongWord;
    OuterException       : PExceptionRecord;
    ExceptionAddress     : Pointer;
................................................................................
  PExceptionInfo = ^TExceptionInfo;
  TExceptionInfo = packed record
    ExceptionRecord: PExceptionRecord;
    ContextRecord: pointer;
  end;
  GetExceptionClass = function(P: PExceptionRecord): ExceptClass;

function GetHandleExceptionSynLog: TSynLog;
var Index: ^TSynLogFileIndex;
    i: integer;
    ndx, n: cardinal;
begin
  result := CurrentHandleExceptionSynLog;
  if result<>nil then
    exit;
  Index := @SynLogFileIndex;
  if SynLogFile<>nil then begin
    n := SynLogFile.Count;
    for i := 0 to high(Index^) do begin
      ndx := Index^[i]-1;
      if ndx<=n then begin
        result := TSynLog(SynLogFile.List^[ndx]);
        if result.fFamily.fHandleExceptions then
          exit;
      end;
    end;
    for i := 0 to n-1 do begin
      result := TSynLog(SynLogFile.List^[i]);
      if result.fFamily.fHandleExceptions then
        exit;
    end;
  end;
  result := nil;
end;

function SynLogVectoredHandler(ExceptionInfo : PExceptionInfo): PtrInt; stdcall;
const
  EXCEPTION_CONTINUE_SEARCH = 0;
var SynLog: TSynLog;
    Level: TSynLogInfo;
    E: ExceptClass;




begin





  // guess if was a Delphi or OS exception
  E := nil;

  Level := sllException;
  if SynLogExceptionEnabled and

     (ExceptionInfo<>nil) and (ExceptionInfo^.ExceptionRecord<>nil) then
    with ExceptionInfo^.ExceptionRecord^ do
      if (ExceptObject<>nil) and ExceptObject.InheritsFrom(Exception) then
        // Delphi exception
        E := PPointer(ExceptObject)^ else begin


        // OS exception -> translate into a Delphi Exception class
        if Assigned(ExceptClsProc) then
          E := GetExceptionClass(ExceptClsProc)(ExceptionInfo^.ExceptionRecord);
        if E=nil then
          E := EExternalException;
        Level := sllExceptionOS;

      end;
  // log corresponding message if necessary





























































  if E<>nil then begin


    SynLog := GetHandleExceptionSynLog;
    if SynLog<>nil then

      case Level of




      sllException:
        if sllException in SynLog.fFamily.Level then
          with ExceptionInfo^.ExceptionRecord^ do
            SynLog.LogInternal(sllException,'% at % ("%")',
              [E,ExceptAddr,ExceptObject.Message],nil);





















      sllExceptionOS:








        if SynLog.fFamily.fHandleExceptions then
          with ExceptionInfo^.ExceptionRecord^ do
            SynLog.LogInternal(sllExceptionOS,'% (%) at %',
              [E,pointer(ExceptionCode),ExceptionAddress],nil);










      end;








  end;
  result := EXCEPTION_CONTINUE_SEARCH;

end;













































procedure TSynLogFamily.SetLevel(const aLevel: TSynLogInfos);
var AddVectoredExceptionHandler: function(FirstHandler: cardinal;
  VectoredHandler: pointer): PtrInt; stdcall;



begin
  fLevel := aLevel;
  fHandleExceptions := (sllExceptionOS in aLevel) or (sllException in aLevel);
  if not SynLogExceptionEnabled and fHandleExceptions then begin
    SynLogExceptionEnabled := true;


    AddVectoredExceptionHandler :=
      GetProcAddress(GetModuleHandle(kernel32),'AddVectoredExceptionHandler');

    if Assigned(AddVectoredExceptionHandler) then
      // available since Windows XP
      AddVectoredExceptionHandler(0,@SynLogVectoredHandler);




  end;
end;

constructor TSynLogFamily.Create(aSynLog: TSynLogClass);
begin
  fSynLogClass := aSynLog;
  if SynLogFamily=nil then begin
    SynLogFamily := TObjectList.Create;
    GarbageCollector.Add(SynLogFamily);
  end;
  fIdent := SynLogFamily.Add(self);
  fDestinationPath := ExtractFilePath(paramstr(0));
  fBufferSize := 4096;


end;

function TSynLogFamily.CreateSynLog: TSynLog;
begin
  result := fSynLogClass.Create(self);
  if fPerThreadLog then begin
    if SynLogFile=nil then begin
................................................................................
  AutoFlushThread: THandle = 0;
  AutoFlushSecondElapsed: cardinal;

procedure AutoFlushProc(P: pointer); stdcall;  // TThread not needed here
var i: integer;
begin
  repeat
    Sleep(1000);
    if SynLogFile=nil then
      continue;
    inc(AutoFlushSecondElapsed);
    for i := 0 to SynLogFile.Count-1 do
    with TSynLog(SynLogFile.List^[i]) do
      if (fFamily.fAutoFlush<>0) and (fWriter<>nil) and
         (AutoFlushSecondElapsed mod fFamily.fAutoFlush=0) then
        if fWriter.B-fWriter.fTempBuf>1 then begin
          if not IsMultiThread and
             not fWriterStream.InheritsFrom(TFileStream) then
            IsMultiThread := true; // only TFileStream is thread-safe
          Flush(false);
        end;
  until false;
end;

procedure TSynLogFamily.SetAutoFlush(TimeOut: cardinal);
var ID: cardinal;
begin
  SynLog.CreateLocker;
  fAutoFlush := TimeOut;
  if AutoFlushThread=0 then begin
    AutoFlushSecondElapsed := 0;
    AutoFlushThread := CreateThread(nil,0,@AutoFlushProc,nil,0,ID);
  end;
end;

destructor TSynLogFamily.Destroy;
begin
  FreeAndNil(fGlobalLog);
  if AutoFlushThread<>0 then begin
    CloseHandle(AutoFlushThread); // release background thread once for all
    AutoFlushThread := 0;
  end;

  inherited;
end;

function TSynLogFamily.SynLog: TSynLog;
var ndx: integer;
begin
  if fPerThreadLog then begin
................................................................................
    ndx := SynLogFileIndex[fIdent]-1;
    if ndx>=0 then
      result := SynLogFile.List^[ndx] else
      result := CreateSynLog;
  end else
    if fGlobalLog<>nil then
      result := fGlobalLog else
      result := CreateSynLog; 
  if fHandleExceptions then
    CurrentHandleExceptionSynLog := result;
end;


{ TSynLog }

................................................................................
  inc(fRecursionCount);
  result := fRecursionCount;
end;

function TSynLog._Release: Integer;
begin
  dec(fRecursionCount);
  if sllLeave in fFamily.Level then






    DoEnterLeave(sllLeave);

  result := fRecursionCount;
end;

constructor TSynLog.Create(aFamily: TSynLogFamily);
begin
  fFamily := aFamily;
end;
................................................................................
begin
  inc(fRecursionMax,256+fRecursionMax shr 3);
  ReallocMem(fRecursion,fRecursionMax*sizeof(fRecursion^[0]));
end;

class function TSynLog.Enter(aClassType: TClass; aMethodName: PUTF8Char): ISynLog;
var aSynLog: TSynLog;

begin





  aSynLog := Family.SynLog;
  with aSynLog do begin
    // recursively store parameters
    if fRecursionCount=fRecursionMax then
      RecursionGrow;
    with fRecursion^[fRecursionCount] do begin
      Instance := nil;
      ClassType := aClassType;
      Method := aMethodName;

    end;
  end;
  // copy to ISynLog interface -> will call TSynLog._AddRef
  result := aSynLog;
end;

class function TSynLog.Enter(aInstance: TObject; aMethodName: PUTF8Char): ISynLog;
var aSynLog: TSynLog;
    aFamily: TSynLogFamily;

begin





  // inlined aSynLog := Family.SynLog
  aFamily := PPointer(PtrInt(Self)+vmtAutoTable)^;
  if aFamily=nil then
    aSynLog := FamilyCreate.SynLog else
    aSynLog := aFamily.SynLog;
  // recursively store parameters
  with aSynLog do begin
................................................................................
      RecursionGrow;
    with fRecursion^[fRecursionCount] do begin
      Instance := aInstance;
      if aInstance=nil then
        ClassType := pointer(aInstance) else
        ClassType := PPointer(aInstance)^;
      Method := aMethodName;

    end;
  end;
  // copy to ISynLog interface -> will call TSynLog._AddRef
  result := aSynLog;
end;

class function TSynLog.FamilyCreate: TSynLogFamily;
................................................................................

procedure TSynLog.Log(Level: TSynLogInfo; aName: PWinAnsiChar;
  aTypeInfo: pointer; var aValue);
begin
  if Level in fFamily.fLevel then
    LogInternal(Level,aName,aTypeInfo,aValue);
end;

















procedure TSynLog.LogFileHeader;
var Freq: Int64;
begin
  QueryPerformanceFrequency(Freq);
  ExeVersionRetrieve;
  with ExeVersion, SystemInfo, OSVersionInfo do
................................................................................
procedure TSynLog.CreateLogWriter;
begin
  if fWriterStream=nil then begin
    ExeVersionRetrieve;
    fFileName := string(ExeVersion.ProgramName)+' '+GetCaptionFromClass(ClassType)+' '+
      TFileName(NowToString(false));
    if fFamily.PerThreadLog then
      fFileName := fFileName+' '+IntToStr(GetCurrentThreadId);
    fFileName := fFamily.fDestinationPath+fFileName+'.log';
    fWriterStream := TFileStream.Create(fFileName,fmCreate)
  end;
  if fWriter=nil then
    fWriter := TTextWriter.Create(fWriterStream,fFamily.BufferSize);
end;

................................................................................
      if Instance<>nil then begin
        fWriter.Add('(');
        fWriter.AddPointer(PtrUInt(Instance));
        fWriter.Add(')');
      end;
      fWriter.Add('.');
    end;

    fWriter.AddNoJSONEscape(Method);

  end;
  fWriter.AddCR;
end;

procedure TSynLog.DoEnterLeave(aLevel: TSynLogInfo);
begin
  LogHeaderLock(aLevel);






|







>
>
>
>







 







|
>
>
>
>







 







>
>
>
>
>
>
>
>
>







 







>
>
>







 







|
|
|
>







 







>
>
>







 







>
>
>







 







>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|












>
>
>
>







 







>
>












>
>
>







 







>
>
>
>

|





>













>
>













>







 







>
>
>
>
>
>
>
>
>
>
>






|







 







>
>
>
>
>
>
>
>
>
>
>






|







 







|







 







|
>
>
>
>







 







|








|









|









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





|







 







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







 







|







 







<
|











<
|









<
|







 







|

|










|







 







|
|







 







>
>
>
>
>
>
>
>







 







>







 







|








>
>
>
>










|







 







>
>
>
>







 







|







 







|







 







>
>
>
>







 







<
|







 







|







|






|







 







|
|







 







|
|







 







|








|







|







 







>







 







|







 







|







 







|






>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>







 







|







 







|







 







|







 







|













>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







 







|

|
>
>
>
>
>
>
>
>
>







 







|







 







|










>
|
|







 







|







 







|







 







>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>









>
>
>



<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>







 







|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







>
>
>
>

>
>
>
>
>


>

<
>
|

|

|
>
>






>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
|
|
>
|
>
>
>
>
|
|
<
<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
|
<
<
<
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
|
<
>
|
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<
<
>
>
>





>
>
|
<
>



>
>
>
>













>
>







 







|











|









|












>







 







|







 







|
>
>
>
>
>
>

>







 







>

>
>
>
>
>









>









>

>
>
>
>
>







 







>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







 







>
|
>







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
...
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
...
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
....
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
....
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
....
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
....
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
....
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
....
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
....
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
....
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
....
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
....
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
....
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
....
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
....
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
....
5933
5934
5935
5936
5937
5938
5939
5940
5941
5942
5943
5944
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
5976
5977
5978
5979
5980
5981
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
....
6047
6048
6049
6050
6051
6052
6053
6054
6055
6056
6057
6058
6059
6060
6061
....
6694
6695
6696
6697
6698
6699
6700
6701
6702
6703
6704
6705
6706
6707
6708
6709
6710
6711
6712
6713
6714
6715
6716
6717
6718
6719
6720
6721
6722
6723
6724
6725
6726
6727
6728
6729
6730
6731
6732
6733
6734
6735
6736
6737
6738
6739
6740
6741
6742
6743
6744
6745
6746
6747
6748
6749
6750
6751
6752
6753
6754
6755
6756
6757
6758
6759
6760
6761
6762
6763
....
8492
8493
8494
8495
8496
8497
8498
8499
8500
8501
8502
8503
8504
8505
8506
8507
8508
8509
8510
8511
8512
8513
8514
8515
8516
8517
8518
8519
8520
8521
8522
8523
8524
8525
8526
....
9475
9476
9477
9478
9479
9480
9481
9482
9483
9484
9485
9486
9487
9488
9489
....
9496
9497
9498
9499
9500
9501
9502
9503
9504
9505
9506
9507
9508
9509
9510
.....
12062
12063
12064
12065
12066
12067
12068

12069
12070
12071
12072
12073
12074
12075
12076
12077
12078
12079
12080

12081
12082
12083
12084
12085
12086
12087
12088
12089
12090

12091
12092
12093
12094
12095
12096
12097
12098
.....
12379
12380
12381
12382
12383
12384
12385
12386
12387
12388
12389
12390
12391
12392
12393
12394
12395
12396
12397
12398
12399
12400
12401
12402
12403
12404
12405
12406
.....
13196
13197
13198
13199
13200
13201
13202
13203
13204
13205
13206
13207
13208
13209
13210
13211
.....
13934
13935
13936
13937
13938
13939
13940
13941
13942
13943
13944
13945
13946
13947
13948
13949
13950
13951
13952
13953
13954
13955
.....
15018
15019
15020
15021
15022
15023
15024
15025
15026
15027
15028
15029
15030
15031
15032
.....
15036
15037
15038
15039
15040
15041
15042
15043
15044
15045
15046
15047
15048
15049
15050
15051
15052
15053
15054
15055
15056
15057
15058
15059
15060
15061
15062
15063
15064
15065
15066
15067
15068
15069
15070
15071
15072
15073
.....
15080
15081
15082
15083
15084
15085
15086
15087
15088
15089
15090
15091
15092
15093
15094
15095
15096
15097
.....
15127
15128
15129
15130
15131
15132
15133
15134
15135
15136
15137
15138
15139
15140
15141
.....
15144
15145
15146
15147
15148
15149
15150
15151
15152
15153
15154
15155
15156
15157
15158
.....
15162
15163
15164
15165
15166
15167
15168
15169
15170
15171
15172
15173
15174
15175
15176
15177
15178
15179
.....
17189
17190
17191
17192
17193
17194
17195

17196
17197
17198
17199
17200
17201
17202
17203
.....
17714
17715
17716
17717
17718
17719
17720
17721
17722
17723
17724
17725
17726
17727
17728
17729
17730
17731
17732
17733
17734
17735
17736
17737
17738
17739
17740
17741
17742
17743
.....
17771
17772
17773
17774
17775
17776
17777
17778
17779
17780
17781
17782
17783
17784
17785
17786
.....
17999
18000
18001
18002
18003
18004
18005
18006
18007
18008
18009
18010
18011
18012
18013
18014
.....
18306
18307
18308
18309
18310
18311
18312
18313
18314
18315
18316
18317
18318
18319
18320
18321
18322
18323
18324
18325
18326
18327
18328
18329
18330
18331
18332
18333
18334
18335
18336
18337
.....
18526
18527
18528
18529
18530
18531
18532
18533
18534
18535
18536
18537
18538
18539
18540
.....
19548
19549
19550
19551
19552
19553
19554
19555
19556
19557
19558
19559
19560
19561
19562
.....
19584
19585
19586
19587
19588
19589
19590
19591
19592
19593
19594
19595
19596
19597
19598
.....
19605
19606
19607
19608
19609
19610
19611
19612
19613
19614
19615
19616
19617
19618
19619
19620
19621
19622
19623
19624
19625
19626
19627
19628
19629
19630
19631
19632
19633
.....
19738
19739
19740
19741
19742
19743
19744
19745
19746
19747
19748
19749
19750
19751
19752
19753
19754
19755
19756
19757
19758
19759
19760
.....
19775
19776
19777
19778
19779
19780
19781
19782
19783
19784
19785
19786
19787
19788
19789
.....
19814
19815
19816
19817
19818
19819
19820
19821
19822
19823
19824
19825
19826
19827
19828
.....
19839
19840
19841
19842
19843
19844
19845
19846
19847
19848
19849
19850
19851
19852
19853
.....
19860
19861
19862
19863
19864
19865
19866
19867
19868
19869
19870
19871
19872
19873
19874
19875
19876
19877
19878
19879
19880
19881
19882
19883
19884
19885
19886
19887
19888
19889
19890
19891
19892
19893
19894
19895
19896
19897
19898
19899
19900
19901
19902
19903
19904
.....
19991
19992
19993
19994
19995
19996
19997
19998
19999
20000
20001
20002
20003
20004
20005
.....
20023
20024
20025
20026
20027
20028
20029
20030
20031
20032
20033
20034
20035
20036
20037
20038
20039
20040
20041
20042
20043
20044
20045
20046
20047
20048
.....
20102
20103
20104
20105
20106
20107
20108
20109
20110
20111
20112
20113
20114
20115
20116
.....
20168
20169
20170
20171
20172
20173
20174
20175
20176
20177
20178
20179
20180
20181
20182
20183
20184
20185
20186
20187
20188
20189
20190
20191
20192
20193
20194
20195
.....
20908
20909
20910
20911
20912
20913
20914
20915
20916
20917
20918
20919
20920
20921
20922
.....
21591
21592
21593
21594
21595
21596
21597
21598
21599
21600
21601
21602
21603
21604
21605
.....
22413
22414
22415
22416
22417
22418
22419
22420
22421
22422
22423
22424
22425
22426
22427
22428
22429
22430
22431
22432
22433
22434
22435
22436
22437
22438
22439
22440
22441
22442
22443
22444
22445
22446
22447
22448
22449
22450
22451
22452
22453
22454
22455
22456
22457
22458
22459
22460
22461
22462
22463
22464
22465
22466
22467
22468
22469
22470
22471
22472
22473
22474
22475
22476
22477
22478
22479
22480
22481
22482
22483
22484
22485
22486
22487
22488
22489
22490
22491
22492
22493
22494
22495
22496
22497
22498
22499
22500
22501
22502
22503
22504
22505
22506
22507
22508
22509
22510
22511
22512
22513
22514
22515
22516
22517
22518
22519
22520
22521
22522
22523
22524
22525
22526
22527
22528
22529
22530
22531
22532
22533
22534
22535
22536
22537
22538
22539
22540
22541
22542
22543
22544
22545
22546
22547
22548
22549
22550
22551
22552
22553
22554
22555
22556
22557
22558
22559
22560
22561
22562
22563
22564
22565
22566
22567
22568
22569
22570
22571
22572
22573
22574
22575
22576
22577
22578
22579
22580
22581
22582
22583
22584
22585
22586
22587
22588
22589
22590
22591
22592
22593
22594
22595
22596
22597
22598
22599
22600
22601
22602
22603
22604
22605
22606
22607
22608
22609
22610
22611
22612
22613
22614
22615
22616
22617
22618
22619
22620
22621
22622
22623
22624
22625
22626
22627
22628
22629
22630
22631
22632
22633
22634
22635
22636
22637
22638
22639
22640
22641
22642
22643
22644
22645
22646
22647
22648
22649
22650
22651
22652
22653
22654
22655
22656
22657
22658
22659
22660
22661
22662
22663
22664
22665
22666
22667
22668
22669
22670
22671
22672
22673
22674
22675
22676
22677
22678
22679
22680
22681
22682
22683
22684
22685
22686
22687
22688
22689
22690
22691
22692
22693
22694
22695
22696
22697
22698
22699
22700
22701
22702
22703
22704
22705
22706
22707
22708
22709
22710
22711
22712
22713
22714
22715
22716
22717
22718
22719
22720
22721
22722
22723
22724
22725
22726
22727
22728
22729
22730
22731
22732
22733
22734
22735
22736
22737
22738
22739
22740
22741
22742
22743
22744
22745
22746
22747
22748
22749
22750
22751
22752
22753
22754
22755
22756
22757
22758
22759
22760
22761
22762
22763
22764
22765
22766
22767
22768
22769
22770
22771
22772
22773
22774
22775
22776
22777
22778
22779
22780
22781
22782
22783
22784
22785
22786
22787
22788
22789
22790
22791
22792
22793
22794
22795
22796
22797
22798
22799
22800
22801
22802
22803
22804
22805
22806
22807
22808
22809
22810
22811
22812
22813
22814
22815
22816
22817
22818
22819
22820
22821
22822
22823
22824
22825
22826
22827
22828
22829
22830
22831
22832
22833
22834
22835
22836
22837
22838
22839
22840
22841
22842
22843
22844
22845
22846
22847
22848
22849
22850
22851
22852
22853
22854
22855
22856
22857
22858
22859
22860
22861
22862
22863
22864
22865
22866
22867
22868
22869
22870
22871
22872
22873
22874
22875
22876
22877
22878
22879
22880
22881
22882
22883
22884
22885
22886
22887
22888
22889
22890
22891
22892
22893
22894
22895
22896
22897
22898
22899
22900
22901
22902
22903
22904
22905
22906
22907
22908


22909
22910
22911
22912
22913
22914
22915
22916
22917
22918
22919
22920
22921
22922
22923
22924
22925
22926
22927
22928
22929
22930
22931
22932
22933
22934
22935
22936
22937
22938
22939
22940
22941
22942
22943
22944
22945
22946
22947
22948
22949
22950
22951
22952
22953
22954
22955
22956
22957
22958
22959
22960
22961
22962
.....
22968
22969
22970
22971
22972
22973
22974
22975
22976
22977
























22978
22979
22980
22981
22982
22983
22984
22985
22986
22987
22988
22989
22990
22991
22992
22993
22994
22995
22996
22997
22998

22999
23000
23001
23002
23003
23004
23005
23006
23007
23008
23009
23010
23011
23012
23013
23014
23015
23016
23017
23018
23019
23020
23021
23022
23023
23024
23025
23026
23027
23028
23029
23030
23031
23032
23033
23034
23035
23036
23037
23038
23039
23040
23041
23042
23043
23044
23045
23046
23047
23048
23049
23050
23051
23052
23053
23054
23055
23056
23057
23058
23059
23060
23061
23062
23063
23064
23065
23066
23067
23068
23069
23070
23071
23072
23073
23074
23075
23076
23077
23078
23079
23080
23081
23082
23083
23084
23085
23086
23087
23088
23089



23090
23091
23092
23093
23094
23095
23096
23097
23098
23099
23100
23101
23102
23103
23104
23105
23106
23107
23108
23109
23110
23111
23112
23113
23114
23115
23116
23117
23118
23119
23120



23121
23122
23123
23124
23125
23126
23127
23128
23129
23130
23131
23132
23133
23134
23135
23136
23137
23138
23139
23140

23141
23142
23143
23144
23145
23146
23147
23148
23149
23150
23151
23152
23153
23154
23155
23156
23157
23158
23159
23160
23161
23162
23163
23164
23165
23166
23167
23168
23169
23170
23171
23172
23173
23174
23175
23176
23177
23178
23179
23180
23181
23182
23183
23184
23185
23186
23187
23188


23189
23190
23191
23192
23193
23194
23195
23196
23197
23198
23199

23200
23201
23202
23203
23204
23205
23206
23207
23208
23209
23210
23211
23212
23213
23214
23215
23216
23217
23218
23219
23220
23221
23222
23223
23224
23225
23226
23227
23228
23229
.....
23239
23240
23241
23242
23243
23244
23245
23246
23247
23248
23249
23250
23251
23252
23253
23254
23255
23256
23257
23258
23259
23260
23261
23262
23263
23264
23265
23266
23267
23268
23269
23270
23271
23272
23273
23274
23275
23276
23277
23278
23279
23280
23281
23282
23283
23284
23285
23286
23287
23288
.....
23289
23290
23291
23292
23293
23294
23295
23296
23297
23298
23299
23300
23301
23302
23303
.....
23308
23309
23310
23311
23312
23313
23314
23315
23316
23317
23318
23319
23320
23321
23322
23323
23324
23325
23326
23327
23328
23329
23330
.....
23367
23368
23369
23370
23371
23372
23373
23374
23375
23376
23377
23378
23379
23380
23381
23382
23383
23384
23385
23386
23387
23388
23389
23390
23391
23392
23393
23394
23395
23396
23397
23398
23399
23400
23401
23402
23403
23404
23405
23406
23407
23408
23409
23410
23411
23412
23413
.....
23415
23416
23417
23418
23419
23420
23421
23422
23423
23424
23425
23426
23427
23428
23429
.....
23491
23492
23493
23494
23495
23496
23497
23498
23499
23500
23501
23502
23503
23504
23505
23506
23507
23508
23509
23510
23511
23512
23513
23514
23515
23516
23517
23518
23519
23520
.....
23659
23660
23661
23662
23663
23664
23665
23666
23667
23668
23669
23670
23671
23672
23673
.....
23700
23701
23702
23703
23704
23705
23706
23707
23708
23709
23710
23711
23712
23713
23714
23715
23716
    TSynValidateTableUniqueField instance is created if tfoUnique is in Options)
  - dedicated TSynTableFieldProperties.Filter method for filtering (using
    common TSynFilter classes, working at UTF-8 Text content)
  - faster implementation of Move() for Delphi versions with no FastCode inside
  - new ConvertCaseUTF8(), UpperCaseU(), LowerCaseU(), Int64ToUInt32(),
    GetCardinalDef(), IsValidEmail, IsValidIP4Address(), PatchCodePtrUInt(),
    GetCaptionFromClass(), GetDisplayNameFromClass(), DateTimeToIso8601Text()
    StrUInt32(), StringBufferToUtf8(), IsZero(), AddPrefixToCSV(), IntToString()
    procedures or functions (with associated tests)
  - new grep-like IsMatch() function for basic pattern matching
  - new BinToBase64, Base64ToBin and IsBase64 *fast* conversion functions
    (with optimized assembler version, using CPU pipelining and lookup table)
  - introducing the GarbageCollector TObjectList for handling a global garbage
    collector for instances which must live during the whole executable process
    (used e.g. to avoid a memory leak for "class var" or such variables)
  - new TSynLog class to handle enhanced logging to any application, with
    exception handling (+stack trace) and customer-side performance profiling
  - new TSynMapFile class to retrieve debugging information from .map file (and
    able to save and read smaller .mab files) - used by TSynLog if available 
  - great performance improvement in TSynTableFieldProperties for update process
  - now TTextWriter can have a custom internal buffer size (default 1024 bytes)
  - fixed issue in TSynTable.Data() method: ID was not set as expected
  - fixed issue in TSynTableFieldProperties: wrong constraint evaluation and
    index refresh at records update

}
................................................................................
function UTF8ToWideString(const Text: RawUTF8): WideString;

/// convert any UTF-8 encoded String into a generic SynUnicode Text
function UTF8ToSynUnicode(const Text: RawUTF8): SynUnicode;

/// convert any Ansi 7 bit encoded String into a generic VCL Text
// - the Text content must contain only 7 bit pure ASCII characters
function Ansi7ToString(const Text: RawByteString): string; overload;

/// convert any Ansi 7 bit encoded String into a generic VCL Text
// - the Text content must contain only 7 bit pure ASCII characters
function Ansi7ToString(Text: PWinAnsiChar; Len: integer): string; overload;

/// convert any generic VCL Text into Ansi 7 bit encoded String 
// - the Text content must contain only 7 bit pure ASCII characters
function StringToAnsi7(const Text: string): RawByteString;

/// fast Format() function replacement, optimized for RawUTF8
// - only supported token is %, which works only for integer and string type of
................................................................................

{$ifndef PURE_PASCAL}
{$ifndef ISDELPHI2007ANDUP}
/// faster implementation of Move() for Delphi versions with no FastCode inside
procedure Move(const Source; var Dest; Count: Integer);
{$endif}
{$endif}

/// faster version than default SysUtils.IntToStr implementation
function IntToString(Value: integer): string; overload;

/// faster version than default SysUtils.IntToStr implementation
function IntToString(Value: cardinal): string; overload;

/// faster version than default SysUtils.IntToStr implementation
function IntToString(Value: Int64): string; overload;

{$ifndef FPC}  { these asm function use some low-level system.pas calls }

/// use our fast asm RawUTF8 version of Trim()
function Trim(const S: RawUTF8): RawUTF8;

/// use our fast asm version of CompareMem()
................................................................................
    // - returns the index of the added element in the dynamic array
    // - note that because of dynamic array internal memory managment, adding
    // will be a bit slower than e.g. with a TList: the list is reallocated
    // every time a record is added - but in practice, with FastMM4 or
    // SynScaleMM, there is no big speed penalty - for even better speed, you
    // can also specify an external count variable in Init(...,@Count) method 
    function Add(const Elem): integer;
    /// add an element to the dynamic array
    // - this version add a void element to the array, and returns its index
    function New: integer;
    /// add elements from a given dynamic array
    // - the supplied source DynArray MUST be of the same exact type as the
    // current used for this TDynArray
    // - you can specify the start index and the number of items to take from
    // the source dynamic array (leave as -1 to add till the end)
    procedure AddArray(const DynArray; aStartIndex: integer=0; aCount: integer=-1);
    /// add an element to the dynamic array at the position specified by Index
................................................................................
  // - wkVarUInt32 will write the content using our 32-bit variable-length integer
  // encoding
  // - wkVarInt32 will write the content using our 32-bit variable-length integer
  // encoding and the by-two complement (0=0,1=1,2=-1,3=2,4=-2...)
  // - wkSorted will write an increasing array of integers, handling the special
  // case of a difference of 1 between two values (therefore is very optimized
  // to store an array of IDs)
  // - wkOffsetU and wkOffsetI will write the difference between two successive
  // values, handling constant difference (Unsigned or Integer) in an optimized manner
  TFileBufferWriterKind = (wkUInt32, wkVarUInt32, wkVarInt32, wkSorted,
    wkOffsetU, wkOffsetI);

  PFileBufferWriter = ^TFileBufferWriter;

  /// this structure can be used to speed up writing to a file
  // - big speed up if data is written in small blocks
  // - also handle optimized storage of any dynamic array of Integer/Int64/RawUTF8
  // - is defined either as an object either as a record, due to a bug
................................................................................
    // used by ReadChunk() method
    fBufTemp: RawByteString;
    /// get Isize + buffer from current memory map or fBufTemp into (P,PEnd)
    procedure ReadChunk(var P, PEnd: PByte);
  public
    /// initialize the buffer, and specify a file to use for reading
    procedure Open(aFile: THandle);
    /// initialize the buffer from an already existing memory block
    // - may be e.g. a resource
    procedure OpenFrom(aBuffer: pointer; aBufferSize: cardinal);
    /// close all internal mapped files
    // - call Open() again to use the Read() methods
    procedure Close;
    {$ifndef CPU64}
    /// change the current reading position, from the beginning of the file
    // - returns TRUE if success, or FALSE if Offset is out of range
    function Seek(Offset: Int64): boolean; overload;
................................................................................
    function Read(Data: pointer; DataLen: integer): integer; overload;
    /// read some UTF-8 encoded text at the current position
    // - returns the resulting text length, in bytes
    function Read(out Text: RawUTF8): integer; overload;
    /// read one byte
    // - if reached end of file, don't raise any error, but returns 0
    function ReadByte: PtrUInt; {$ifdef HASINLINE}inline;{$endif}
    /// read one cardinal, which was written as fixed length
    // - if reached end of file, don't raise any error, but returns 0
    function ReadCardinal: cardinal;
    /// read one cardinal value encoded using our 32-bit variable-length integer
    function ReadVarUInt32: PtrUInt;
    /// read one integer value encoded using our 32-bit variable-length integer,
    // and the by-two complement
    function ReadVarInt32: PtrInt;
    /// read one UInt64 value encoded using our 64-bit variable-length integer
    function ReadVarUInt64: QWord;
................................................................................
// in big-endian order (most-signignifican byte first): use it for display
procedure BinToHexDisplay(Bin, Hex: PAnsiChar; BinBytes: integer);

/// fast conversion from a pointer data into hexa chars, ready to be displayed
// - use internally BinToHexDisplay()
function PointerToHex(aPointer: Pointer): RawUTF8;

/// fast conversion from hexa chars into a pointer
function HexDisplayToBin(Hex: PAnsiChar; Bin: PByte; BinBytes: integer): boolean;

/// fast conversion from binary data into Base64 encoded text
function BinToBase64(const s: RawByteString): RawByteString; overload;

/// fast conversion from binary data into Base64 encoded text
function BinToBase64(Bin: PAnsiChar; BinBytes: integer): RawByteString; overload;

/// fast conversion from binary data into Base64 encoded text
................................................................................
function GetDelphiCompilerVersion: RawUTF8;



{ ************ Logging classes and functions }

type
  /// a debugger symbol, as decoded by TSynMapFile from a .map file
  TSynMapSymbol = record
    /// symbol internal name
    Name: RawUTF8;
    /// starting offset of this symbol in the executable
    Start: cardinal;
    /// end offset of this symbol in the executable
    Stop: cardinal;
  end;
  PSynMapSymbol = ^TSynMapSymbol;
  /// a dynamic array of symbols, as decoded by TSynMapFile from a .map file
  TSynMapSymbolDynArray = array of TSynMapSymbol;

  /// a debugger unit, as decoded by TSynMapFile from a .map file
  TSynMapUnit = record
    /// Name, Start and Stop of this Unit
    Symbol: TSynMapSymbol;
    /// list of all mapped source code lines of this unit
    Line: TIntegerDynArray;
    /// start code address of each source code lin
    Addr: TIntegerDynArray;
  end;
  /// a dynamic array of units, as decoded by TSynMapFile from a .map file
  TSynMapUnitDynArray = array of TSynMapUnit;

  /// retrieve a .map file content, to be used e.g. with TSynLog to provide
  // additional debugging information
  // - original .map content can be saved as .mab file in a more optimized format
  TSynMapFile = class
  protected
    fMapFile: TFileName;
    fSymbol: TSynMapSymbolDynArray;
    fUnit: TSynMapUnitDynArray;
    fSymbols: TDynArray;
    fUnits: TDynArrayHashed;
    fGetModuleHandle: PtrUInt;
    fHasDebugInfo: boolean;
  public
    /// get the available debugging information
    // - will first search for a .map file in the .exe directory: if found,
    // will be read to retrieve all necessary debugging information - a .mab
    // file will be also created in the same directory (if MabCreate is TRUE)
    // - if .map is not not available, will search for the .mab file in the
    // .exe directory
    // - if no .mab is available, will search for a .mab appended to the exe 
    // - if nothing is available, will log as hexadecimal pointers, without
    // debugging information
    // - if aExeName is not specified, will use the current process executable
    constructor Create(const aExeName: TFileName=''; MabCreate: boolean=true);
    /// save all debugging information in the .mab custom binary format
    // - if no file name is specified, it will be saved as ExeName.mab
    // - this file content can be appended to the executable via SaveToExe method
    // - this function returns the created file name
    function SaveToFile(const aFileName: TFileName=''): TFileName;
    /// append all debugging information to an executable
    // - the executable name must be specified, because it's impossible to
    // write to the executable of a running process
    procedure SaveToExe(const aExeName: TFileName);
    /// add some debugging information according to the specified memory address
    // - will create a global TSynMapFile instance for the current process, if
    // necessary
    // - if no debugging information is available (.map or .mab), will write
    // the address as hexadecimal
    class procedure Log(W: TTextWriter; Addr: PtrUInt);
    /// retrieve a symbol according to an absolute code address
    function FindSymbol(aAddr: cardinal): integer;
    /// retrieve an unit and source line, according to an absolute code address
    function FindUnit(aAddr: cardinal; out LineNumber: integer): integer;
    /// all symbols associated to the executable
    property Symbols: TSynMapSymbolDynArray read fSymbol;
    /// all units, including line numbers, associated to the executable
    property Units: TSynMapUnitDynArray read fUnit;
    /// equals true if a .map or .mab debugging information has been loaded
    property HasDebugInfo: boolean read fHasDebugInfo;
  end;
  
  /// the available logging events, as handled by TSynLog
  // - sllError will log errors
  // - sllInfo will log general information events
  // - sllDebug will log detailed debugging information
  // - sllLastError will log the GetLastError OS message
  // - sllException will log all exception raised - available since Windows XP
  // - sllExceptionOS will log all OS low-level exceptions (EDivByZero,
................................................................................
      Instance: TObject=nil); overload;
    /// call this method to add some information to the log at a specified level
    // - if Instance is set and Text is not '', it will log the corresponding
    // class name and address (to be used e.g. if you didn't call TSynLog.Enter()
    // method first)
    // - if Instance is set and Text is '', will behave the same as
    // Log(Level,Instance), i.e. write the Instance as JSON content
    procedure Log(Level: TSynLogInfo; const Text: RawUTF8;
      Instance: TObject=nil); overload;
    /// call this method to add the content of an object to the log at a
    // specified level
    // - TSynLog will write the class and hexa address - TSQLLog will write the
    // object JSON content
    procedure Log(Level: TSynLogInfo; Instance: TObject); overload;
    /// call this method to add the content of most low-level types to the log
    // at a specified level
    // - TSynLog will handle enumerations and dynamic array; TSQLLog will be
    // able to write TObject/TSQLRecord and sets content as JSON
    procedure Log(Level: TSynLogInfo; aName: PWinAnsiChar;
      aTypeInfo: pointer; var aValue); overload;
    /// call this method to add the caller address to the log at the specified level
    // - if the debugging info is available from TSynMapFile, will log the
    // unit name, associated symbol and source code line
    procedure Log(Level: TSynLogInfo); overload;
    /// retrieve the associated logging instance
    function Instance: TSynLog;
  end;

  {{ regroup several logs under an unique family name
   - you should usualy use one family per application or per architectural
     module: e.g. a server application may want to log in separate files the
................................................................................
    fIdent: integer;
    fDestinationPath: TFileName;
    fBufferSize: integer;
    fHRTimeStamp: boolean;
    fWithUnitName: boolean;
    fAutoFlush: cardinal;
    fHandleExceptions: boolean;
    fStackTraceLevel: cardinal;
    fExceptionIgnore: TList;
    function CreateSynLog: TSynLog;
    procedure SetAutoFlush(TimeOut: cardinal);
    procedure SetLevel(const aLevel: TSynLogInfos);
  public
    /// intialize for a TSynLog class family
    // - add it in the global SynLogFileFamily[] list
    constructor Create(aSynLog: TSynLogClass);
    /// release associated memory
    destructor Destroy; override;
    /// retrieve the corresponding log file of this thread and family
    // - creates the TSynLog if not already existing for this current thread
    function SynLog: TSynLog;
    /// you can add some exceptions to be ignored to this list
    // - for instance, EConvertError may be added to the list
    property ExceptionIgnore: TList read fExceptionIgnore;
  published
    /// the associated TSynLog class
    property SynLogClass: TSynLogClass read fSynLogClass;
    /// index in global SynLogFileFamily[] and threadvar SynLogFileIndex[] lists
    property Ident: integer read fIdent;
    /// the current level of logging information for this family
    property Level: TSynLogInfos read fLevel write SetLevel;
................................................................................
    // disk, whatever the current content size is
    // - by default, the log file will be written for every 4 KB of log - this
    // will ensure that the main application won't be slow down by logging
    // - in order not to loose any log, a background thread can be created
    // and will be responsible of flushing all pending log content every
    // period of time (e.g. every 10 seconds)
    property AutoFlushTimeOut: cardinal read fAutoFlush write SetAutoFlush;
    /// the recursive depth of stack trace symbol to write
    // - used only if exceptions are handled
    // - default value is 20 
    property StackTraceLevel: cardinal read fStackTraceLevel write fStackTraceLevel;
  end;

  /// used to store the identification of one recursivity level
  TSynLogCurrentIdent = packed record
    Instance: TObject;
    ClassType: TClass;
    Method: PUTF8Char;
    Caller: PtrUInt;
  end;
  PSynLogCurrentIdent = ^TSynLogCurrentIdent;
  /// used to store the identification of all recursivity levels
  TSynLogCurrentIdents = array[0..maxInt div sizeof(TSynLogCurrentIdent)-1] of TSynLogCurrentIdent;
  PSynLogCurrentIdents = ^TSynLogCurrentIdents;

  /// a per-family and/or per-thread log file content
  // - you should create a sub class per kind of log file
  // ! TSynLogDB = class(TSynLog);
  // - the TSynLog instance won't be allocated in heap, but will share a
  // per-thread (if Family.PerThreadLog=TRUE) or global private log file instance
  // - was very optimized for speed, if no logging is written, and even during
  // log write (using an internal TTextWriter)
  // - can use available debugging information via the TSynMapFile class, for
  // stack trace logging for exceptions, and Enter/Leave labelling
  TSynLog = class(TObject, ISynLog)
  protected
    fWriter: TTextWriter;
    fFamily: TSynLogFamily;
    fWriterStream: TStream;
    fHeaderWritten: boolean;
    fLockCS: PRTLCriticalSection;
    fStartTimeStamp: Int64;
    fFileName: TFileName;
    /// set by Enter() method
    fRecursionCount: integer;
    fRecursionMax: integer;
    fRecursion: PSynLogCurrentIdents;
    fRecursionCaller: PtrUInt;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    class function FamilyCreate: TSynLogFamily;
    procedure DoEnterLeave(aLevel: TSynLogInfo);
    procedure CreateLogWriter; virtual;
    procedure LogInternal(Level: TSynLogInfo; TextFmt: PWinAnsiChar;
................................................................................
     - if you just need to access the log inside the method block, you may
     not need any ISynLog interface:
     ! procedure TMyDB.SQLFlush;
     ! begin
     !   TSynLogDB.Enter(self,'SQLFlush');
     !   // do some stuff
     ! end;
     - if no Method name is supplied, it will use the caller address, and
     will write it as hexa and with full unit and symbol name, if the debugging
     information is available (i.e. if TSynMapFile retrieved the .map content):
     ! procedure TMyDB.SQLFlush;
     ! begin
     !   TSynLogDB.Enter(self);
     !   // do some stuff
     ! end;
     - note that supplying a method name is faster than using the .map content:
     if you want accurate profiling, it's better to use a method name or not to
     use a .map file
     - Enter() will write the class name (and the unit name for classes with
     published properties, if TSynLogFamily.WithUnitName is true) for both
     enter (+) and leave (-) events:
      $ 20110325 19325801  +    MyDBUnit.TMyDB(004E11F4).SQLExecute
      $ 20110325 19325801 info   SQL=SELECT * FROM Table;
      $ 20110325 19325801  -    MyDBUnit.TMyDB(004E11F4).SQLExecute }
    class function Enter(aInstance: TObject=nil; aMethodName: PUTF8Char=nil): ISynLog; overload;
    {{ to be called and assigned to a ISynLog interface at the beginning
     of a method
     - this is the main method to be called within a class method:
     ! class function TMyDB.SQLValidate(const SQL: RawUTF8): boolean;
     ! var ILog: ISynLog;
     ! begin
     !   ILog := TSynLogDB.Enter(self,'SQLValidate');
................................................................................
     - if you just need to access the log inside the method block, you may
     not need any ISynLog interface:
     ! class procedure TMyDB.SQLFlush;
     ! begin
     !   TSynLogDB.Enter(self,'SQLFlush');
     !   // do some stuff
     ! end;
     - if no Method name is supplied, it will use the caller address, and
     will write it as hexa and with full unit and symbol name, if the debugging
     information is available (i.e. if TSynMapFile retrieved the .map content):
     ! class procedure TMyDB.SQLFlush;
     ! begin
     !   TSynLogDB.Enter(self);
     !   // do some stuff
     ! end;
     - note that supplying a method name is faster than using the .map content:
     if you want accurate profiling, it's better to use a method name or not to
     use a .map file
     - Enter() will write the class name (and the unit name for classes with
     published properties, if TSynLogFamily.WithUnitName is true) for both
     enter (+) and leave (-) events:
      $ 20110325 19325801  +    MyDBUnit.TMyDB.SQLValidate
      $ 20110325 19325801 info   SQL=SELECT * FROM Table returned 1;
      $ 20110325 19325801  -    MyDBUnit.TMyDB.SQLValidate }
    class function Enter(aClassType: TClass; aMethodName: PUTF8Char=nil): ISynLog; overload;
    /// retrieve the current instance of this TSynLog class
    // - to be used for direct logging, without any Enter/Leave:
    // ! TSynLogDB.Add.Log(llError,'The % statement didn't work',[SQL]);
    // - is just a wrapper around Family.SynLog - the same code will work:
    // ! TSynLogDB.Family.SynLog.Log(llError,'The % statement didn't work',[SQL]);
    class function Add: TSynLog;
      {$ifdef HASINLINE}inline;{$endif}
................................................................................
    // class name and address (to be used e.g. if you didn't call TSynLog.Enter()
    // method first) - for instance
    // ! TSQLLog.Add.Log(sllDebug,'GarbageCollector',GarbageCollector);
    // will append this line to the log:
    // $ 0000000000002DB9 debug TObjectList(00425E68) GarbageCollector
    // - if Instance is set and Text is '', will behave the same as
    // Log(Level,Instance), i.e. write the Instance as JSON content
    procedure Log(Level: TSynLogInfo; const Text: RawUTF8;
      aInstance: TObject=nil); overload;
    /// call this method to add the content of an object to the log at a
    // specified level
    // - this default implementation will just write the class name and its hexa
    // pointer value, and handle TList, TCollections and TStrings - for instance:
    // ! TSynLog.Add.Log(sllDebug,GarbageCollector);
    // will append this line to the log:
................................................................................
    /// call this method to add the content of most low-level types to the log
    // at a specified level
    // - this overriden implementation will write the value content,
    // written as human readable JSON: handle dynamic arrays and enumerations
    // - TSQLLog from SQLite3Commons unit will be able to write
    // TObject/TSQLRecord and sets content as JSON
    procedure Log(Level: TSynLogInfo; aName: PWinAnsiChar;
      aTypeInfo: pointer; var aValue); overload;
    /// call this method to add the caller address to the log at the specified level
    // - if the debugging info is available from TSynMapFile, will log the
    // unit name, associated symbol and source code line
    procedure Log(Level: TSynLogInfo); overload;
  published
    /// the associated logging family
    property GenericFamily: TSynLogFamily read fFamily;
    /// the associated file name containing the log
    // - this is accurate only with the default implementation of the class:
    // any child may override it with a custom logging mechanism
    property FileName: TFileName read fFileName;
................................................................................
var L2: integer;
begin
  result := ''; // somewhat faster if result is freed before any SetLength()
  if L=0 then
    L := StrLen(P);
  if L=0 then
    exit;
  SetString(result,nil,L); // maximum posible unicode size (if all <#128)
  L2 := UTF8ToWideChar(pointer(result),P,L) shr 1;
  if L2<>L then
    SetLength(result,L2);
end;

function WinAnsiToUnicodeString(WinAnsi: PAnsiChar; WinAnsiLen: integer): UnicodeString;
var i: integer;
begin
  SetString(result,nil,WinAnsiLen);
  for i := 0 to WinAnsiLen-1 do
    PWordArray(result)[i] := WinAnsiTableA[WinAnsi[i]]; // very fast conversion
end;
{$endif}

{$ifdef UNICODE}
function Ansi7ToString(const Text: RawByteString): string;
var i: integer;
begin
  SetString(result,nil,length(Text));
  for i := 0 to length(Text)-1 do
    PWordArray(result)[i] := PByteArray(Text)[i]; // no conversion for 7 bit Ansi
end;
{$else}
function Ansi7ToString(const Text: RawByteString): string;
begin
  result := Text; // if we are SURE this text is 7 bit Ansi -> direct assign
end;
{$endif}

{$ifdef UNICODE}
function Ansi7ToString(Text: PWinAnsiChar; Len: integer): string;
var i: integer;
begin
  SetString(result,nil,Len);
  for i := 0 to Len-1 do
    PWordArray(result)[i] := PByteArray(Text)[i]; // no conversion for 7 bit Ansi
end;
{$else}
function Ansi7ToString(Text: PWinAnsiChar; Len: integer): string;
begin
  SetString(result,PAnsiChar(Text),Len);
end;
{$endif}

{$ifdef UNICODE}
function StringToAnsi7(const Text: string): RawByteString;
var i: integer;
begin
  SetString(result,nil,length(Text));
  for i := 0 to length(Text)-1 do
    PByteArray(result)[i] := PWordArray(Text)[i]; // no conversion for 7 bit Ansi
end;
{$else}
function StringToAnsi7(const Text: string): RawByteString;
begin
  result := Text; // if we are SURE this text is 7 bit Ansi -> direct assign
................................................................................
  SetString(result,PAnsiChar(pointer(S)),length(S)*2+1); // +1 for last wide #0
end;
{$else}
function StringToRawUnicode(const S: string): RawUnicode;
var i, L: integer;
begin
  L := length(S);
  SetString(result,nil,L*2+1); // +1 for last wide #0
  if GetACP<>1252 then begin
    // low-level MBCS RTL function including last widechar #0
    SetLength(result,MultiByteToWideChar(GetACP, 0, Pointer(s), L, pointer(result), L));
  end else
    // fast WinAnsi conversion
    for i := 0 to L do // includes S[L]=#0 -> last widechar #0
      PWordArray(result)[i] := WinAnsiTable[PByteArray(S)[i]];
................................................................................
// this Pos() is seldom used, it was decided to only define it under
// Delphi 2009/2010/XE (which expect such a RawUTF8 specific overloaded version)

function Pos(const substr, str: RawUTF8): Integer; overload;
begin
  Result := PosEx(substr, str, 1);
end;

function IntToString(Value: integer): string;
var tmp: array[0..15] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrInt32(@tmp[15],Value);
  result := Ansi7ToString(P,@tmp[15]-P);
end;

function IntToString(Value: cardinal): string;
var tmp: array[0..15] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrUInt32(@tmp[15],Value);
  result := Ansi7ToString(P,@tmp[15]-P);
end;

function IntToString(Value: Int64): string;
var tmp: array[0..31] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrInt64(@tmp[31],Value);
  result := Ansi7ToString(P,@tmp[31]-P);
end;

{$else UNICODE}

function IntToString(Value: integer): string;
{$ifdef PUREPASCAL}
var tmp: array[0..15] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrInt32(@tmp[15],Value);
  SetString(result,P,@tmp[15]-P);
end;
{$else}
asm
  jmp Int32ToUTF8
end;
{$endif}

function IntToString(Value: cardinal): string;
var tmp: array[0..15] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrUInt32(@tmp[15],Value);
  SetString(result,P,@tmp[15]-P);
end;

function IntToString(Value: Int64): string;
var tmp: array[0..31] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrInt64(@tmp[31],Value);
  SetString(result,P,@tmp[31]-P);
end;

{$endif UNICODE}

{$ifndef ISDELPHI2007ANDUP}
/// faster implementation of Move() for Delphi versions with no FastCode inside
procedure Move(const Source; var Dest; Count: Integer);
asm // eax=source edx=dest ecx=count
................................................................................
      if byte(U^) and $80<>0 then break; // 7 bits char check only
    until false;
{$endif}
    // find beginning of next word
    U := FindNextUTF8WordBegin(U);
  until U=nil;
end;

function HexDisplayToBin(Hex: PAnsiChar; Bin: PByte; BinBytes: integer): boolean;
var B,C: byte;
    i: integer;
begin
  result := false; // return false if any invalid char
  if Pointer(ConvertHexToBin)=nil then
    InitConvertHexToBin;
  inc(Bin,BinBytes-1);
  for i := 1 to BinBytes do begin
    B := ConvertHexToBin[Ord(Hex^)];
    inc(Hex);
    if B>15 then exit;
    C := ConvertHexToBin[Ord(Hex^)];
    Inc(Hex);
    if C>15 then exit;
    Bin^ := B shl 4+C;
    Dec(Bin);
  end;
  result := true; // correct content in Hex
end;

function HexToBin(Hex: PAnsiChar; Bin: PByte; BinBytes: Integer): boolean;
var I: Integer;
    B,C: byte;
begin
  result := false; // return false if any invalid char
  if Pointer(ConvertHexToBin)=nil then
................................................................................
var F: THandle;
    Size: integer;
begin
  result := '';
  if FileName='' then
    exit;
  F := FileOpen(FileName,fmOpenRead or fmShareDenyNone);
  if PtrInt(F)>=0 then begin
{$ifdef LINUX}
    Size := FileSeek(F,0,soFromEnd);
    FileSeek(F,0,soFromBeginning);
{$else}
    Size := GetFileSize(F,nil);
{$endif}
    SetLength(result,Size);
................................................................................
function FileFromString(const Content: RawByteString; const FileName: TFileName;
  FlushOnDisk: boolean=false): boolean;
var F: THandle;
    L: integer;
begin
  result := false;
  F := FileCreate(FileName);
  if PtrInt(F)<0 then
    exit;
  if pointer(Content)<>nil then
    L := FileWrite(F,pointer(Content)^,length(Content)) else
    L := 0;
  result := (L=length(Content));
{$ifdef MSWINDOWS}
  if FlushOnDisk then
................................................................................
    SetString(result,tmp,15);
  end;
end;

function DateToIso8601(Date: TDateTime; Expanded: boolean): RawUTF8;
// we use YYYYMMDDTdate format
begin

  SetString(result,nil,8+2*integer(Expanded));
{$ifdef UNICODE2} // not needed: SetLength() did already set the codepage
  PWord(PtrUInt(result)-12)^ := CP_UTF8; // use only SetLength() -> force set code page
{$endif}
  DateToIso8601PChar(Date,pointer(result),Expanded);
end;

/// basic Date conversion into ISO-8601
// - use 'YYYYMMDD' format if not Expanded
// - use 'YYYY-MM-DD' format if Expanded
function DateToIso8601(Y,M,D: cardinal; Expanded: boolean): RawUTF8; overload;
begin

  SetString(result,nil,8+2*integer(Expanded));
{$ifdef UNICODE2} // not needed: SetLength() did already set the codepage
  PWord(PtrUInt(result)-12)^ := CP_UTF8; // use only SetLength() -> force set code page
{$endif}
  DateToIso8601PChar(pointer(result),Expanded,Y,M,D);
end;

function TimeToIso8601(Time: TDateTime; Expanded: boolean): RawUTF8;
// we use Thhmmss format
begin

  SetString(result,nil,7+2*integer(Expanded));
{$ifdef UNICODE2} // not needed: SetLength() did already set the codepage
  PWord(PtrUInt(result)-12)^ := CP_UTF8; // use only SetLength() -> force set code page
{$endif}
  TimeToIso8601PChar(Time,pointer(result),Expanded);
end;

function DateTimeToIso8601Text(DT: TDateTime): RawUTF8;
................................................................................
{$ifdef MSWINDOWS}
    Now: TSystemTime; {$else}
    D: TDateTime;     {$endif}
begin
  if aFileName='' then
    exit;
  F := FileOpen(aFileName,fmOpenWrite);
  if PtrInt(F)<0 then begin
    F := FileCreate(aFileName);
    if PtrInt(F)<0 then
      exit;
  end;
   // append to end of file
  if FileSeek(F,0,soFromEnd)>MAXLOGSIZE then begin
    // rotate log file if too big
    FileClose(F);
    Old := aFileName+'.bak'; // '.log.bak'
    DeleteFile(Old); // rotate once
    RenameFile(aFileName,Old);
    F := FileCreate(aFileName);
    if PtrInt(F)<0 then
      exit;
  end;
  PWord(@Date)^ := 13+10 shl 8; // first go to next line
{$ifdef MSWINDOWS}
  GetLocalTime(Now); // windows dedicated function
  DateToIso8601PChar(@Date[3],true,Now.wYear,Now.wMonth,Now.wDay);
  TimeToIso8601PChar(@Date[13],true,Now.wHour,Now.wMinute,Now.wSecond,' ');
................................................................................
          BuildYear := SystemTime.wYear;
        end;
      end;
    finally
      Freemem(Pt);
    end;
  end;
  Main := IntToString(Major)+'.'+IntToString(Minor);
  Detailed := Main+ '.'+IntToString(Release)+'.'+IntToString(Build);
  if BuildDateTime=0 then // get build date from file age
    BuildDateTime := FileAgeToDateTime(FileName);
end;

function TFileVersion.Version32: integer;
begin
  result := Major shl 16+Minor shl 8+Release;
................................................................................
    exit; // avoid GPF if void
  SetCount(result+1);
  P := pointer(PtrUInt(Value^)+PtrUInt(result)*ElemSize);
  if ElemType=nil then
    move(Elem,P^,ElemSize) else
    CopyArray(P,@Elem,ElemType,1);
end;

function TDynArray.New: integer;
begin
  result := Count;
  if Value=nil then
    exit; // avoid GPF if void
  SetCount(result+1);
end;

procedure TDynArray.Insert(Index: Integer; const Elem);
var n: integer;
    P: PByteArray;
begin
  if Value=nil then
    exit; // avoid GPF if void
................................................................................
    result := Hasher(0,Pointer(PtrUInt(Elem)),PInteger(PtrUInt(Elem)-4)^*2);
end;

procedure TDynArrayHashed.Init(aTypeInfo: pointer; var aValue;
      aHashElement: TDynArrayHashOne=nil; aCompare: TDynArraySortCompare=nil;
      aHasher: THasher=nil; aCountPointer: PInteger=nil);
var FieldTable: PFieldTable;
label RecH, RecC;
begin
  inherited Init(aTypeInfo,aValue,aCountPointer);
  if @aHasher=nil then begin
    if @DefaultHasher=nil then
      DefaultHasher := @kr32;
     fHasher := DefaultHasher;
  end else
................................................................................
    case PByte(ElemType)^ of
      tkLString:
        aHashElement := HashAnsiString;
      tkWString{$ifdef UNICODE}, tkUString{$endif}:
        aHashElement := HashUnicodeString;
      tkRecord: begin
        FieldTable := ElemType;
RecH:   inc(PtrUInt(FieldTable),ord(FieldTable^.Name[0]));
        if FieldTable^.Count>0 then
        with FieldTable^.Fields[0] do
          if Offset=0 then
          case TypeInfo^^.Kind of
            tkLString:
              aHashElement := HashAnsiString;
            tkWString{$ifdef UNICODE}, tkUString{$endif}:
              aHashElement := HashUnicodeString;
            tkRecord: begin
              FieldTable := pointer(TypeInfo^);
              goto RecH;
            end;
          end;
      end;
    end;
  fHashElement := aHashElement;
  if @aCompare=nil then begin
    aCompare := SORTFIRSTFIELD[TypeInfoToKnown(aTypeInfo)];
    if @aCompare=nil then
      // will match default HashOne algo, i.e. hash first field of record
      if (ElemType<>nil) and (PByte(ElemType)^=tkRecord) then begin
        FieldTable := ElemType;
RecC:   inc(PtrUInt(FieldTable),ord(FieldTable^.Name[0]));
        if FieldTable^.Count=0 then
        case ElemSize of
          1: aCompare := @SortDynArrayByte;
          2: aCompare := @SortDynArrayWord;
          4: aCompare := @SortDynArrayCardinal;
          8: aCompare := @SortDynArrayInt64;
        end else
................................................................................
            8: aCompare := @SortDynArrayInt64;
          end else
          case TypeInfo^^.Kind of
          tkLString:
            aCompare := @SortDynArrayAnsiString;
          tkWString{$ifdef UNICODE}, tkUString{$endif}:
            aCompare := @SortDynArrayUnicodeString;
          tkRecord: begin
            FieldTable := pointer(TypeInfo^);
            goto RecC;
          end;
          end;
      end;
  end;
  fCompare := aCompare;
  ReHash;
end;

................................................................................
      end;
  until false;
  result := -1; // mark not found
end;

function TDynArrayHashed.HashOne(const Elem): cardinal;
var FieldTable: PFieldTable;
label Bin, LStr, WStr, Rec;
begin
  if @fHashElement<>nil then
    result := fHashElement(Elem,fHasher) else
  if ElemType=nil then
    goto Bin else
    case PByte(ElemType)^ of
    tkLString:
................................................................................
        result := fHasher(0,Pointer(PtrUInt(Elem)),PInteger(PtrUInt(Elem)-4)^);
    tkWString{$ifdef UNICODE}, tkUString{$endif}:
WStr: if PtrUInt(Elem)=0 then
        result := 0 else
        result := fHasher(0,Pointer(PtrUInt(Elem)),PInteger(PtrUInt(Elem)-4)^*2);
    tkRecord: begin
      FieldTable := ElemType;
Rec:  inc(PtrUInt(FieldTable),ord(FieldTable^.Name[0]));
      if FieldTable^.Count=0 then
        // only binary content -> hash full content
Bin:    case ElemSize of 
          1: result := byte(Elem);
          2: result := word(Elem);
          4: result := cardinal(Elem);
          8: result := Int64Rec(Elem).Lo xor Int64Rec(Elem).Hi;
................................................................................
      if Offset<>0 then
        // hash whole starting binary content of record
        result := fHasher(0,@Elem,Offset) else
        // hash first string field of record
        case TypeInfo^^.Kind of
        tkLString: goto LStr;
        tkWString{$ifdef UNICODE}, tkUString{$endif}: goto WStr;
        tkRecord: begin
          FieldTable := pointer(TypeInfo^);
          goto Rec;
        end;
        else result := 0; // unhandled type 
        end;
    end;
    else result := 0;
    end;
  if result=0 then
    result := 1; // never return 0 (indicates a free position in fHashs[])
................................................................................
  fOwner := Owner;
end;

function TSynTestCase.RandomString(CharCount: Integer): RawByteString;
var V: cardinal;
    P: PAnsiChar;
begin

  SetString(result,nil,CharCount);
  P := pointer(Result);
  while CharCount>0 do begin
    if CharCount>5 then begin
      V := Random(maxInt); // fast: one random compute per 5 chars
      P[0] := AnsiChar(32+V and 127); V := V shr 7;
      P[1] := AnsiChar(32+V and 127); V := V shr 7;
      P[2] := AnsiChar(32+V and 127); V := V shr 7;
................................................................................
    Check(Cities[i].Name=''); // ForAddind = not added
    Cities[i] := City;
  end;
  Check(ACities.Count=3);
  Check(City.Name='Iasi');
  Check(ACities.FindHashed(City)>=0);
  for i := 1 to 2000 do begin
    City.Name := IntToString(i);
    City.Latitude := i*3.14;
    City.Longitude := i*6.13;
    Check(ACities.FindHashedAndUpdate(City,true)=i+2,'multiple ReHash');
    Check(ACities.FindHashed(City)=i+2);
  end;
  ACities.Capacity := CITIES_MAX+3; // make it as fast as possible
  for i := 2001 to CITIES_MAX do begin
    City.Name := IntToString(i);
    City.Latitude := i*3.14;
    City.Longitude := i*6.13;
    Check(ACities.FindHashedAndUpdate(City,true)=i+2,'use Capacity: no ReHash');
    Check(ACities.FindHashed(City.Name)=i+2);
  end;
  for i := 1 to CITIES_MAX do begin
    N := IntToString(i);
    j := ACities.FindHashed(N);
    Check(j=i+2,'hashing with string not City.Name');
    Check(Cities[j].Name=N);
    CheckSame(Cities[j].Latitude,i*3.14);
    CheckSame(Cities[j].Longitude,i*6.13);
  end;
end;
................................................................................
    JSON_BASE64_MAGIC_UTF8: RawUTF8;
procedure Fill(var F: TFileVersion; i: integer);
begin
  F.Major := i;
  F.Minor := i+1;
  F.Release := i+2;
  F.Build := i+3;
  F.Main := IntToString(i+1000);
  F.Detailed := IntToString(2000-i);
  F.BuildDateTime := 36215.12;
  F.BuildYear := i+2011;
end;
begin
  W := TTextWriter.CreateOwnedStream;
  // validate TIntegerDynArray 
  AIP.Init(TypeInfo(TIntegerDynArray),AI);
................................................................................
  Check(RecordEquals(F,AF[100],TypeInfo(TFileVersion)));
  for i := 0 to 1000 do
    with AF[i] do begin
      Check(Major=i);
      Check(Minor=i+1);
      Check(Release=i+2);
      Check(Build=i+3);
      Check(Main=IntToString(i+1000));
      Check(Detailed=IntToString(2000-i));
      CheckSame(BuildDateTime,36215.12);
      Check(BuildYear=i+2011);
    end;
  for i := 0 to 1000 do begin
    Fill(F,i);
    Check(AFP.IndexOf(F)=i);
  end;
................................................................................
{$endif}
  for i := 1 to 10000 do begin
    j := Random(maxInt)-Random(maxInt);
    str(j,a);
    s := RawUTF8(a);
    Check(kr32(0,pointer(s),length(s))=kr32pas(pointer(s),length(s)));
    u := string(a);
    Check(SysUtils.IntToStr(j)=u);
    Check(Int32ToUtf8(j)=s);
    Check(format('%d',[j])=u);
    Check(GetInteger(pointer(s))=j);
    Check(FormatUTF8('%',[j])=s);
    k := Int64(j)*Random(MaxInt);
    str(k,a);
    s := RawUTF8(a);
    u := string(a);
    Check(SysUtils.IntToStr(k)=u);
    Check(Int64ToUtf8(k)=s);
    Check(format('%d',[k])=u);
    Check(FormatUTF8('%',[k])=s);
    err := 1;
    l := GetInt64(pointer(s),err);
    Check((err=0)and(l=k));
    str(j,a);
    Check(SysUtils.IntToStr(j)=string(a));
    Check(format('%d',[j])=string(a));
    Check(format('%.8x',[j])=IntToHex(j,8));
    d := Random*1E-17-Random*1E-9;
    str(d,a);
    s := RawUTF8(a);
    e := GetExtended(Pointer(s),err);
    Check(SameValue(e,d)); // test str()
................................................................................
    Check(T.AddField('double',tftDouble)<>nil);
    Check(T.AddField('varint',tftVarUInt32)<>nil);
    Check(T.AddField('text',tftUTF8,[tfoUnique])<>nil);
    Check(T.AddField('ansi',tftWinAnsi,[])<>nil);
    Check(T.AddField('currency',tftCurrency)<>nil);
    Test;
    FN := ChangeFileExt(paramstr(0),'.syntable');
    DeleteFile(FN);
    f := FileCreate(FN); // manual storage of TSynTable header
    W.AssignToFile(f);
    T.SaveTo(W);
    W.Flush;
    FileClose(f);
    T.Free;
    f := FileOpen(FN,fmOpenRead);
................................................................................
  WriteVarUInt32(ValuesCount);
  if ValuesCount=0 then
    exit;
  PI := pointer(Values);
  PByteArray(fBuf)^[fPos] := ord(DataLayout);
  inc(fPos);
  inc(fTotalWritten);
  if DataLayout in [wkOffsetU, wkOffsetI] then begin
    pos := fPos;
    fPos := PtrUInt(ToVarUInt32(PI^[0],@PByteArray(fBuf)^[fPos]))-PtrUInt(fBuf);
    diff := PI^[1]-PI^[0];
    inc(PtrUInt(PI),4);
    dec(ValuesCount);
    if ValuesCount=0 then begin
      inc(fTotalWritten,PtrUInt(fPos-pos));
................................................................................
      wkUInt32: begin
        n := (fBufLen-fPos)shr 2;
        if ValuesCount<n then
          n := ValuesCount;
        Move(PI^,P^,n*4);
        inc(P,n*4);
      end;
      wkVarInt32, wkVarUInt32, wkOffsetU, wkOffsetI: begin
        PBeg := PAnsiChar(P); // leave space for chunk size
        inc(P,4);
        n := ValuesCount;
        case DataLayout of
        wkVarInt32:
          for i := 0 to ValuesCount-1 do begin
            P := ToVarInt32(PI^[i],P);
................................................................................
          for i := 0 to ValuesCount-1 do begin
            P := ToVarUInt32(PI^[i],P);
            if PtrUInt(P)>=PtrUInt(PEnd) then begin
              n := i+1;
              break; // avoid buffer overflow
            end;
          end;
        wkOffsetU:
          for i := 0 to ValuesCount-1 do begin
            P := ToVarUInt32(PI^[i]-PI^[i-1],P);
            if PtrUInt(P)>=PtrUInt(PEnd) then begin
              n := i+1;
              break; // avoid buffer overflow
            end;
          end;
        wkOffsetI:
          for i := 0 to ValuesCount-1 do begin
            P := ToVarInt32(PI^[i]-PI^[i-1],P);
            if PtrUInt(P)>=PtrUInt(PEnd) then begin
              n := i+1;
              break; // avoid buffer overflow
            end;
          end;
        end;
        PInteger(PBeg)^ := PAnsiChar(P)-PBeg-4; // format: Isize+varUInt32s
      end;
      wkSorted: begin
        PBeg := PAnsiChar(P)+4; // leave space for chunk size
        P := PByte(CleverStoreInteger(pointer(PI),PBeg,PEnd,ValuesCount,n));
................................................................................
  end;
end;

procedure TFileBufferReader.ErrorInvalidContent;
begin
  raise Exception.Create('TFileBufferReader: invalid content');
end;

procedure TFileBufferReader.OpenFrom(aBuffer: pointer; aBufferSize: cardinal);
begin
  fCurrentPos := 0;
  fBuf := aBuffer;
  fBufSize := aBufferSize;
  fFileSize := aBufferSize;
  fMap := 0;
end;

procedure TFileBufferReader.Open(aFile: THandle);
begin
  fCurrentPos := 0;
  fBuf := nil;
  fMap := 0;
  fFile := aFile;
................................................................................
  end;
end;

function TFileBufferReader.Read(Data: pointer; DataLen: integer): integer;
var len: integer;
begin
  if DataLen>0 then
    if fBuf<>nil then begin
      // file up to 2 GB: use fast memory map
      len := fBufSize-fCurrentPos;
      if len>DataLen then
        len := DataLen;
      move(fBuf[fCurrentPos],Data^,len);
      inc(fCurrentPos,len);
      result := len;
................................................................................
    ErrorInvalidContent;
  PEnd := pointer(PtrUInt(P)+PtrUInt(len));
end;

function TFileBufferReader.ReadPointer(DataLen: PtrUInt;
  var aTempData: RawByteString): pointer;
begin
  if fBuf=nil then begin
    // read from file
    if DataLen>PtrUInt(Length(aTempData)) then begin
      aTempData := ''; // so no move() call in SetLength() below
      SetLength(aTempData,DataLen);
    end;
    if PtrUInt(FileRead(fFile,pointer(aTempData)^,DataLen))<>DataLen then
      result := nil else // invalid content
................................................................................

{$ifndef LVCL}
function TFileBufferReader.ReadStream: TCustomMemoryStream;
var DataLen: PtrUInt;
begin
  DataLen := ReadVarUInt32;
  if DataLen<>0 then
    if fBuf=nil then begin
      // use temporary TMemoryStream only if not in memory map buffer
      result := TMemoryStream.Create;
      result.Size := DataLen;
      if PtrUInt(FileRead(fFile,result.Memory^,DataLen))<>DataLen then
        FreeAndNil(result); // invalid content
    end else
    if DataLen+fCurrentPos>fBufSize then
................................................................................
    // DataLen=0 -> invalid content
    result := nil;
end;
{$endif}

function TFileBufferReader.ReadByte: PtrUInt;
begin
  if fBuf<>nil then
    if fCurrentPos>=fBufSize then
      // invalid request
      result := 0 else begin
      // read one byte from current memory map
      result := ord(fBuf[fCurrentPos]);
      inc(fCurrentPos);
    end else begin
      // read from file if >= 2 GB (slow, but works)
      result := 0;
      if FileRead(fFile,result,1)<>1 then
        result := 0;
    end;
end;

function TFileBufferReader.ReadCardinal: cardinal;
begin
  if fBuf<>nil then
    if fCurrentPos+3>=fBufSize then
      // invalid request
      result := 0 else begin
      // read one byte from current memory map
      result := PCardinal(fBuf+fCurrentPos)^;
      inc(fCurrentPos,4);
    end else begin
      // read from file if >= 2 GB (slow, but works)
      result := 0;
      if FileRead(fFile,result,4)<>4 then
        result := 0;
    end;
end;

function TFileBufferReader.ReadVarUInt32: PtrUInt;
var c, n: PtrUInt;
begin
  result := ReadByte;
  if result>$7f then begin
    n := 0;
................................................................................
  if result=0 then
    exit;
  count := result;
  if count>length(Values) then // only set length is not big enough
    SetLength(Values,count);
  PI := pointer(Values);
  DataLayout := TFileBufferWriterKind(ReadByte);
  if DataLayout in [wkOffsetU, wkOffsetI] then begin
    PI^ := ReadVarUInt32;
    dec(count);
    diff := ReadVarUInt32;
    if diff<>0 then begin
      for i := 0 to count-1 do
        PIA^[i+1] := PIA^[i]+diff;
      exit;
................................................................................
          inc(PI);
        end;
      wkSorted: begin
        n := CleverReadInteger(pointer(P),pointer(PEnd),PI);
        dec(count,n);
        inc(PtrUInt(PI),n*4);
      end;
      wkOffsetU: begin
        while (count>0) and (PtrUInt(P)<PtrUInt(PEnd)) do begin
          PIA^[1] := PIA^[0]+integer(FromVarUInt32(P));
          dec(count);
          inc(PI);
        end;
        if count<=0 then
          inc(PI); // make sure PI=@Values[result]
      end;
      wkOffsetI: begin
        while (count>0) and (PtrUInt(P)<PtrUInt(PEnd)) do begin
          PIA^[1] := PIA^[0]+FromVarInt32(P);
          dec(count);
          inc(PI);
        end;
        if count<=0 then
          inc(PI); // make sure PI=@Values[result]
      end;
      else
................................................................................
  assert((count=0)and(PI=@Values[result]));
end;

function TFileBufferReader.ReadRawUTF8List(List: TRawUTF8List): boolean;
var i: integer;
    StoreObjectsAsVarUInt32: Boolean;
begin
  if (fBuf<>nil) and (List<>nil) then
  with List do begin
    BeginUpdate;
    try
      Capacity := 0; // finalize both fObjects[] and fList[]
      fCount := ReadVarRawUTF8DynArray(List.fList);
      result := true;
      if fCount=0 then
................................................................................
end;

{$ifndef CPU64}
function TFileBufferReader.Seek(Offset: Int64): boolean;
begin
  if (Offset<0) or (Offset>fFileSize) then
    result := False else
  if fBuf=nil then
    result := FileSeek64(fFile,Offset,soFromBeginning)=Offset else begin
    fCurrentPos := Int64Rec(Offset).Lo;
    result := true;
  end;
end;
{$endif CPU64}

function TFileBufferReader.Seek(Offset: PtrInt): boolean;
begin
  // we don't need to handle fMap=0 here
  if fBuf=nil then
    Result := FileSeek(fFile,Offset,0)=Offset else
  if (fBuf<>nil) and (PtrUInt(Offset)<PPtrUInt(@fFileSize)^) then begin
    fCurrentPos := Offset;
    result := true;
  end else
    result := false;
end;


................................................................................
    inc(P,Len);
    inc(TotalLen,Len);
    Lens[F] := Len;
  end else
    inc(TotalLen,fDefaultFieldLength);
  // create new record content
  P := RecordBuffer;
  SetString(Result,nil,TotalLen);
  Dest := pointer(Result);
  for F := 0 to fField.Count-1 do
  with TSynTableFieldProperties(fField.List[F]) do
    if F in AvailableFields then begin
      Len := Lens[F];
      move(P^,Dest^,Len);
      inc(P,Len);
................................................................................
    Head: integer;
begin
  if FieldType<>tftBlobInternal then
    result := '' else
  if (Value=nil) or (ValueLen=0) then
    result := #0 else begin // inlined ToSBFStr() code
    Head := PAnsiChar(ToVarUInt32(ValueLen,@tmp))-tmp;
    SetString(Result,nil,ValueLen+Head);
    Move(tmp,PByteArray(Result)[0],Head);
    Move(Value^,PByteArray(Result)[Head],ValueLen);
  end;
end;

function TSynTableFieldProperties.SBFFloat(const Value: Double): TSBFString;
begin
................................................................................
function TSynTableData.ValidateSBFValue(RecordIndex: integer): string;
begin
  CheckVTableInitialized;
  Result := VTable.Validate(Pointer(VValue),RecordIndex);
end;


{ TSynMapFile }

var
  ExeMapFile: TSynMapFile = nil;

const
  MAB_MAGIC: cardinal = $A5A5A5A5;

function MatchPattern(P,PEnd,Up: PUTF8Char): boolean;
begin
  result := false;
  repeat
    while P^=' ' do inc(P);
    while NormToUpperAnsi7[P^]=Up^ do begin
      inc(P);
      if P>PEnd then exit;
      inc(Up);
      if (Up^=' ') and (P^=' ') then begin // ignore multiple spaces in P^
        while (P<PEnd) and (P^=' ') do inc(P);
        inc(Up);
      end;
    end;
    if Up^=#0 then // all chars of Up^ found in P^
      break else
    if Up^<>' ' then // P^ and Up^ didn't match
      exit;
    inc(Up);
  until false;
  result := true;
end;

procedure ReadSymbol(var R: TFileBufferReader; const A: TDynArray);
var i, n, L: integer;
    S: PSynMapSymbol;
    Addr: cardinal;
    P: PByte;
begin
  n := R.ReadVarUInt32;
  A.Count := n;
  if n=0 then
    exit;
  P := @R.fBuf[R.fCurrentPos];
  S := A.Value^;
  for i := 0 to n-1 do begin
    L := FromVarUInt32(P); // inlined R.Read(S^.Name)
    SetString(S^.Name,PAnsiChar(P),L);
    inc(P,L);
    inc(PtrUInt(S),A.ElemSize);
  end;
  S := A.Value^;
  Addr := FromVarUInt32(P);
  S^.Start := Addr;
  for i := 1 to n-1 do begin
    inc(Addr,FromVarUInt32(P));
    S^.Stop := Addr-1;
    inc(PtrUInt(S),A.ElemSize);
    S^.Start := Addr;
  end;
  S^.Stop := Addr+FromVarUInt32(P);
  R.fCurrentPos := PtrUInt(P)-PtrUInt(R.fBuf);
end;

constructor TSynMapFile.Create(const aExeName: TFileName=''; MabCreate: boolean=true);

  procedure LoadMap;
    var P, PEnd: PUTF8Char;
    procedure NextLine;
    begin
      while (P<PEnd) and (P^>=' ') do inc(P);
      if (P<PEnd) and (P^=#13) then inc(P);
      if (P<PEnd) and (P^=#10) then inc(P);
    end;
    function GetCode(var Ptr: cardinal): boolean;
    begin
      while (P<PEnd) and (P^=' ') do inc(P);
      result := false;
      if (P+10<PEnd) and
         (PInteger(P)^=ord('0')+ord('0')shl 8+ord('0')shl 16+ord('1')shl 24) and
         (P[4]=':') then begin
        if not HexDisplayToBin(PAnsiChar(P)+5,@Ptr,sizeof(Ptr)) then exit;
        while (P<PEnd) and (P^>' ') do inc(P);
        while (P<PEnd) and (P^=' ') do inc(P);
        if P<PEnd then
          result := true;
      end;
    end;
    procedure ReadSegments;
    var Beg: PAnsiChar;
        U: TSynMapUnit;
    begin
      NextLine;
      NextLine;
      while (P<PEnd) and (P^<' ') do inc(P);
      while (P+10<PEnd) and (P^>=' ') do begin
        if GetCode(U.Symbol.Start) and
           HexDisplayToBin(PAnsiChar(P),@U.Symbol.Stop,4) then begin
          while PWord(P)^<>ord('M')+ord('=')shl 8 do
            if P+10>PEnd then exit else inc(P);
          Beg := pointer(P+2);
          while (P<PEnd) and (P^>' ') do inc(P);
          SetString(U.Symbol.Name,Beg,P-Beg);
          inc(U.Symbol.Stop,U.Symbol.Start);
          if U.Symbol.Start<>0 then
            dec(U.Symbol.Stop); // fix incoherency in .map for 1st segment
          if U.Symbol.Name<>'' then
            fUnits.FindHashedAndUpdate(U,true); // true for adding
        end;
        NextLine;
      end;
    end;
    procedure ReadSymbols;
    var Beg: PAnsiChar;
        Sym: TSynMapSymbol;
    begin
      NextLine;
      NextLine;
      while (P+10<PEnd) and (P^>=' ') do begin
        if GetCode(Sym.Start) then begin
          while (P<PEnd) and (P^=' ') do inc(P);
          Beg := pointer(P);
          while (P<PEnd) and (P^>' ') do inc(P);
          SetString(Sym.Name,Beg,P-Beg);
          if Sym.Name<>'' then
            fSymbols.Add(Sym);
        end;
        NextLine;
      end;
    end;
    procedure ReadLines;
    var Beg: PAnsiChar;
        i, Count: integer;
        aName: RawUTF8;
        added: boolean;
    begin
      inc(P,17);
      Beg := pointer(P);
      while P^<>'(' do if P=PEnd then exit else inc(P);
      SetString(aName,Beg,P-Beg);
    {  inc(P); Beg := pointer(P); // we don't need the filename by now
      while P^<>')' do if P=PEnd then exit else inc(P);
      SetString(aFileName,Beg,P-Beg); }
      if aName='' then
        exit;
      i := fUnits.FindHashedForAdding(aName,added);
      if added then
        fUnit[i].Symbol.Name := aName; // should not occur, but who knows...
      NextLine;
      NextLine;
      Count := 0;
      with fUnit[i] do begin
        // FileName := aFileName;
        while (P+10<PEnd) and (P^>=' ') do begin
          while (P<PEnd) and (P^=' ') do inc(P);
          repeat
            if Count=length(Line) then begin
              SetLength(Line,Count+256);
              SetLength(Addr,Count+256);
            end;
            Line[Count] := GetNextItemCardinal(P,' ');
            if not GetCode(cardinal(Addr[Count])) then
              break;
            inc(Count);
          until (P>=PEnd) or (P^<' ');
          NextLine;
        end;
        SetLength(Line,Count);
        SetLength(Addr,Count);
      end;
    end;
  var i, s,u: integer;
  begin // LoadMap
    fSymbols.Capacity := 5000;
    with TMemoryStream.Create do
    try
      LoadFromFile(fMapFile);
      P := Memory;
      PEnd := P+Size;
      while P<PEnd do
        if MatchPattern(P,PEnd,'DETAILED MAP OF SEGMENTS') then
          ReadSegments else
        if MatchPattern(P,PEnd,'ADDRESS PUBLICS BY VALUE') then
          ReadSymbols else
        if MatchPattern(P,PEnd,'LINE NUMBERS FOR') then
          ReadLines else
         NextLine;
      // now we should have read all .map content
      u := fUnits.Count;
      s := fSymbols.Count;
      for i := 1 to u-1 do
         assert(fUnit[i].Symbol.Start>fUnit[i-1].Symbol.Stop);
      for i := 0 to s-2 do
        fSymbol[i].Stop := fSymbol[i+1].Start-1;
      if (u>0) and (s>0) then
        fSymbol[s-1].Stop := fUnit[u-1].Symbol.Stop;
    finally
      Free;
    end;
  end;

  procedure LoadMab(const aMabFile: TFileName);
  var tmp: cardinal;
      F: THandle;
      R: TFileBufferReader;
      i: integer;
  begin
    fMapFile := aMabFile;
    F := FileOpen(fMapFile,fmOpenRead or fmShareDenyNone);
    if PtrInt(F)>=0 then
    try
      R.Open(F);
      R.Seek(R.FileSize-8);
      if R.ReadCardinal<>MAB_MAGIC then exit;
      R.Read(@tmp,4);
      if tmp>R.FileSize then
        exit;
      R.Seek(R.FileSize-tmp);
      if R.ReadCardinal<>MAB_MAGIC then exit;
      ReadSymbol(R,fSymbols);
      ReadSymbol(R,fUnits);
      for i := 0 to fUnits.Count-1 do
      with fUnit[i] do begin
        R.ReadVarUInt32Array(Line);
        R.ReadVarUInt32Array(Addr);
      end;
      if R.ReadCardinal=MAB_MAGIC then
        MabCreate := false else
        fUnits.Count := 0; // invalid content
    finally
      FileClose(F);
    end;
  end;

var SymCount, UnitCount, i: integer;
    MabFile: TFileName;
begin
  fSymbols.Init(TypeInfo(TSynMapSymbolDynArray),fSymbol,@SymCount);
  fUnits.Init(TypeInfo(TSynMapUnitDynArray),fUnit,nil,nil,nil,@UnitCount);
  // 1. search for a .map file in the .exe directory
  if aExeName='' then
    fMapFile := paramstr(0) else
    fMapFile := aExeName;
  fMapFile := ChangeFileExt(fMapFile,'.map');
  MabFile := ChangeFileExt(fMapFile,'.mab');
  if FileExists(fMapFile) and
     // no slow read of .map if faster-to-load .mab is available and accurate 
     (FileAge(MabFile)<FileAge(fMapFile)) then
    LoadMap;
  // 2. search for a .mab file in the .exe directory
  if SymCount=0 then
    LoadMab(MabFile);
  // 3. search for an embedded compressed .mab file appended to the .exe
  if SymCount=0 then
    LoadMab(ChangeFileExt(fMapFile,'.exe'));
  // finalize symbols
  if SymCount>0 then begin
    for i := 1 to SymCount-1 do
      assert(fSymbol[i].Start>fSymbol[i-1].Stop);
    SetLength(fSymbol,SymCount);
    SetLength(fUnit,UnitCount);
    fSymbols.fCountP := nil;
    fUnits.fCountP := nil;
    if MabCreate then
      SaveToFile(''); // if just created from .map -> create .mab file 
    fHasDebugInfo := true;
  end;
end;

procedure WriteSymbol(var W: TFileBufferWriter; const A: TDynArray);
var i, n: integer;
    Diff: cardinal;
    S: PSynMapSymbol;
    P: PByte;
    Beg: PtrUInt;
begin
  n := A.Count;
  W.WriteVarUInt32(n);
  if n=0 then exit;
  S := A.Value^;
  for i := 0 to n-1 do begin
    W.Write(S^.Name);
    inc(PtrUInt(S),A.ElemSize);
  end;
  S := A.Value^;
  Diff := S^.Start;
  W.WriteVarUInt32(Diff);
  if W.fPos+n*5>W.fBufLen then
    W.Flush;
  with W do
    if fPos+n*5>fBufLen then
      raise Exception.CreateFmt('too big %s',[PDynArrayTypeInfo(A.TypeInfo).Name]) else
      P := @PByteArray(fBuf)^[fPos];
  Beg := PtrUInt(P);
  for i := 1 to n-1 do begin
    inc(PtrUInt(S),A.ElemSize);
    P := ToVarUInt32(S^.Start-Diff,P);
    Diff := S^.Start;
  end;
  P := ToVarUInt32(S^.Stop-Diff,P);
  Beg := PtrUInt(P)-Beg;
  inc(W.fPos,Beg);
  inc(W.fTotalWritten,Beg);
end;

function TSynMapFile.SaveToFile(const aFileName: TFileName=''): TFileName;
var W: TFileBufferWriter;
    F: THandle;
    i, Len: integer;
begin
  if aFileName='' then
    result := ChangeFileExt(paramstr(0),'.mab') else
    result := aFileName;
  DeleteFile(result);
  F := FileCreate(result);
  if PtrInt(F)>=0 then
  try
    W.AssignToFile(F,1 shl 17);
    W.Write(@MAB_MAGIC,sizeof(MAB_MAGIC));
    WriteSymbol(W,fSymbols);
    WriteSymbol(W,fUnits);
    for i := 0 to high(fUnit) do
    with fUnit[i] do begin
      W.WriteVarUInt32Array(Line,length(Line),wkOffsetI); // not always increasing
      W.WriteVarUInt32Array(Addr,length(Addr),wkOffsetU); // always increasing
    end;
    W.Write(@MAB_MAGIC,4);
    Len := W.TotalWritten+4;
    W.Write(@Len,4);
    W.Flush;
  finally
    FileClose(F);
  end;
end;

procedure TSynMapFile.SaveToExe(const aExeName: TFileName);
var FN: TFileName;
    MS, MAB: TMemoryStream;
    Len, LenMAB: PtrUInt;
    P: PAnsiChar;
begin
  if not FileExists(aExeName) then
    exit;
  FN := SaveToFile(ChangeFileExt(aExeName,'.mab'));
  try
    MS := TMemoryStream.Create;
    MAB := TMemoryStream.Create;
    try
      // load both files
      MAB.LoadFromFile(FN);
      LenMAB := MAB.Size;
      MS.LoadFromFile(aExeName);
      Len := MS.Size;
      if Len<16 then
        exit;
      P := MS.Memory;
      inc(P,Len);
      if PCardinal(P-8)^=MAB_MAGIC then
        // trim existing mab content
        dec(Len,PCardinal(P-4)^);
      // append mab content to exe
      MS.Size := Len+LenMAB;
      move(MAB.Memory^,PAnsiChar(MS.Memory)[Len],LenMAB);
      MS.SaveToFile(aExeName);
    finally
      MAB.Free;
      MS.Free;
    end;
  finally
    DeleteFile(FN);
  end;
end;

function TSynMapFile.FindSymbol(aAddr: cardinal): integer;
var L,R: integer;
begin
  R := high(fSymbol);
  L := 0;
  if (R>=0) and (aAddr>=fSymbol[0].Start) and (aAddr<=fSymbol[R].Stop) then
  repeat
    result := (L+R)shr 1;
    with fSymbol[result] do
      if aAddr<Start then
        R := result-1 else
      if aAddr>Stop then
        L := result+1 else
        exit;
  until L>R;
  result := -1;
end;

function TSynMapFile.FindUnit(aAddr: cardinal; out LineNumber: integer): integer;
var L,R,n,max: integer;
begin
  LineNumber := 0;
  R := high(fUnit);
  L := 0;
  if (R>=0) and
     (aAddr>=fUnit[0].Symbol.Start) and (aAddr<=fUnit[R].Symbol.Stop) then
  repeat
    result := (L+R) shr 1;
    with fUnit[result] do
      if aAddr<Symbol.Start then
        R := result-1 else
      if aAddr>Symbol.Stop then
        L := result+1 else begin
        // unit found -> search line number
        L := 0;
        max := high(Addr);
        R := max;
        if R>=0 then
        repeat
          n := (L+R) shr 1;
          if aAddr<cardinal(Addr[n]) then
            R := n-1 else
          if (n<max) and (aAddr>=cardinal(Addr[n+1])) then
            L := n+1 else begin
            LineNumber := Line[n];
            exit;
          end;
        until L>R;
        exit;
      end;
  until L>R;
  result := -1;
end;

const
  /// Delphi linker starts the code section at this fixed offset
  CodeSection = $1000;

class procedure TSynMapFile.Log(W: TTextWriter; Addr: PtrUInt);
var u, s, Line: integer;
begin
  if (W=nil) or (Addr=0) then
    exit;
  if ExeMapFile=nil then begin
    ExeMapFile := TSynMapFile.Create;
    GarbageCollector.Add(ExeMapFile);
    ExeMapFile.fGetModuleHandle := GetModuleHandle(nil)+CodeSection;
  end;
  W.AddPointer(Addr);
  W.Add(' ');
  with ExeMapFile do
  if HasDebugInfo then begin
    dec(Addr,fGetModuleHandle);
    s := FindSymbol(Addr);
    u := FindUnit(Addr,Line);
    if (s<0) and (u<0) then
      exit;
    if u>=0 then begin
      W.AddString(Units[u].Symbol.Name);
      if s>=0 then
        if Symbols[s].Name=Units[u].Symbol.Name then
          s := -1 else
          W.Add('.');
    end;
    if s>=0 then
      W.AddString(Symbols[s].Name);
    W.Add(' ');
    if Line>0 then begin
      W.Add('(');
      W.Add(Line);
      W.Add(')',' ');
    end;
  end;
end;


{ TSynLogFamily }

/// if defined, will use AddVectoredExceptionHandler() API call
// - this one does not produce accurate stack trace by now, and is supported
// only since Windows XP
// - so default method using RTLUnwindProc should be prefered
{.$define WITH_VECTOREXCEPT}

type
  TExceptProc = procedure(Obj: TObject; Addr: Pointer);

var
  /// internal list of registered TSynLogFamily
  // - up to MAX_SYNLOGFAMILY+1 families may be defined
  SynLogFamily: TObjectList = nil;

var
  SynLogExceptionEnabled: boolean = false;

threadvar
  CurrentHandleExceptionSynLog: TSynLog;



function GetHandleExceptionSynLog: TSynLog;
var Index: ^TSynLogFileIndex;
    i: integer;
    ndx, n: cardinal;
begin
  result := CurrentHandleExceptionSynLog;
  if (result<>nil) and result.fFamily.fHandleExceptions then
    exit;
  Index := @SynLogFileIndex;
  if SynLogFile=nil then begin
    // we are here is no previous log content was trigerred
    for i := 0 to SynLogFamily.Count-1 do
      with TSynLogFamily(SynLogFamily.List^[i]) do
      if fHandleExceptions then begin
        result := SynLog;
        exit;
      end;
  end else begin
    n := SynLogFile.Count;
    for i := 0 to high(Index^) do begin
      ndx := Index^[i]-1;
      if ndx<=n then begin
        result := TSynLog(SynLogFile.List^[ndx]);
        if result.fFamily.fHandleExceptions then
          exit;
      end;
    end;
    for i := 0 to n-1 do begin
      result := TSynLog(SynLogFile.List^[i]);
      if result.fFamily.fHandleExceptions then
        exit;
    end;
  end;
  result := nil;
end;

function check2(xret: PtrUInt): Boolean;
var i: PtrUInt;
begin
  result := true;
  for i := 2 to 7 do
    if PWord(xret-i)^ and $38FF=$10FF then
      exit;
  result := false;
end;

{$ifdef WITH_VECTOREXCEPT}
type
  PExceptionRecord = ^TExceptionRecord;
  TExceptionRecord = packed record
    ExceptionCode        : LongWord;
    ExceptionFlags       : LongWord;
    OuterException       : PExceptionRecord;
    ExceptionAddress     : Pointer;
................................................................................
  PExceptionInfo = ^TExceptionInfo;
  TExceptionInfo = packed record
    ExceptionRecord: PExceptionRecord;
    ContextRecord: pointer;
  end;
  GetExceptionClass = function(P: PExceptionRecord): ExceptClass;

var
  AddVectoredExceptionHandler: function(FirstHandler: cardinal;
    VectoredHandler: pointer): PtrInt; stdcall;

























function SynLogVectoredHandler(ExceptionInfo : PExceptionInfo): PtrInt; stdcall;
const
  EXCEPTION_CONTINUE_SEARCH = 0;
var SynLog: TSynLog;
    Level: TSynLogInfo;
    E: ExceptClass;
    EAddr: pointer;
    LastError: DWORD;
    curr_stack: PPtrUInt;
    stack, max_stack, min_stack, depth: PtrUInt;
begin
  if not SynLogExceptionEnabled then begin
    result := EXCEPTION_CONTINUE_SEARCH; // if called within function
    exit;
  end;
  LastError := GetLastError;
  // guess if was a Delphi or OS exception
  E := nil;
  EAddr := nil;
  Level := sllException;

  SynLog := GetHandleExceptionSynLog;
  if (SynLog<>nil) and (ExceptionInfo<>nil) and (ExceptionInfo^.ExceptionRecord<>nil) then
    with ExceptionInfo^.ExceptionRecord^ do
      if (ExceptObject<>nil) and ExceptObject.InheritsFrom(Exception) then begin
        // Delphi exception
        E := PPointer(ExceptObject)^;
        EAddr := ExceptAddr;
      end else begin
        // OS exception -> translate into a Delphi Exception class
        if Assigned(ExceptClsProc) then
          E := GetExceptionClass(ExceptClsProc)(ExceptionInfo^.ExceptionRecord);
        if E=nil then
          E := EExternalException;
        Level := sllExceptionOS;
        EAddr := ExceptionAddress;
      end;
  // log corresponding message if necessary
  if (E<>nil) and
     ((Level<>sllException) or (sllException in SynLog.fFamily.Level)) then begin
    SynLog.LogHeaderLock(Level);
    with SynLog.fWriter, ExceptionInfo^.ExceptionRecord^ do
      if Level=sllException then
        Add('% ("%") at ',[E,ExceptObject.Message]) else
        Add('% (%) at ',[E,pointer(ExceptionCode)]);
    TSynMapFile.Log(SynLog.fWriter,PtrUInt(EAddr));
    depth := SynLog.fFamily.fStackTraceLevel;
    if depth>0 then begin
      SynLog.fWriter.AddShort('stack trace ');
      SynLogExceptionEnabled := false; // for IsBadCodePtr
      try
        asm
          mov eax,[ebp] // push ebp; mov ebp,esp done at begin level above
          mov curr_stack,eax
          mov eax,fs:[18h]
          mov ecx,dword ptr [eax+4]
          mov max_stack,ecx
          mov ecx,dword ptr [eax+8]
          mov min_stack,ecx
        end;
        while (PtrUInt(curr_stack)<max_stack) and
          (curr_stack^<>PtrUInt(EAddr)) do inc(curr_stack);
        while PtrUInt(curr_stack)<max_stack do begin
          stack := curr_stack^;
          if ((stack>max_stack) or (stack<min_stack)) and
             not IsBadReadPtr(pointer(stack-8),12) and
             ((pByte(stack-5)^=$E8) or check2(stack)) then begin
            TSynMapFile.Log(SynLog.fWriter,stack);
            dec(depth);
            if depth=0 then break;
          end;
          inc(curr_stack);
        end;
      finally
        SynLogExceptionEnabled := true;
      end;
    end;
    SynLog.fWriter.AddCR;
    SynLog.UnLock;
  end;
  SetLastError(LastError); // code above could have changed this
  result := EXCEPTION_CONTINUE_SEARCH;
end;

{$else WITH_VECTOREXCEPT}

var
  oldUnWindProc: pointer;

const
  cDelphiExcept = $0EEDFAE0;
  cDelphiException = $0EEDFADE;

procedure LogExcept(stack: PPtrUInt; EAddr, ECode: PtrUInt; EObject: TObject); stdcall;
var SynLog: TSynLog;
    Level: TSynLogInfo;
    E: ExceptClass;
    LastError: DWORD;
    st, max_stack, min_stack, depth: PtrUInt;
begin
  if not SynLogExceptionEnabled then
    exit;
  SynLog := GetHandleExceptionSynLog;
  if SynLog=nil then
    exit;
  Level := sllError;
  LastError := GetLastError;
  if (ECode=cDelphiException) and (EObject<>nil) then begin
    if EObject.InheritsFrom(Exception) then
      E := PPointer(EObject)^ else
      E := EExternalException;
    if sllException in SynLog.fFamily.Level then



      Level := sllException;
  end else begin
    case ECode of
      STATUS_INTEGER_DIVIDE_BY_ZERO,
      STATUS_FLOAT_DIVIDE_BY_ZERO:   E := EDivByZero;
      STATUS_ARRAY_BOUNDS_EXCEEDED:  E := ERangeError;
      STATUS_INTEGER_OVERFLOW:       E := EIntOverflow;
      STATUS_FLOAT_INEXACT_RESULT,
      STATUS_FLOAT_INVALID_OPERATION,
      STATUS_FLOAT_STACK_CHECK:      E := EInvalidOp;
      STATUS_FLOAT_OVERFLOW:         E := EOverflow;
      STATUS_FLOAT_UNDERFLOW,
      STATUS_FLOAT_DENORMAL_OPERAND: E := EUnderflow;
      STATUS_ACCESS_VIOLATION:       E := EAccessViolation;
      STATUS_PRIVILEGED_INSTRUCTION: E := EPrivilege;
      STATUS_CONTROL_C_EXIT:         E := EControlC;
{$WARN SYMBOL_DEPRECATED OFF}
      STATUS_STACK_OVERFLOW:         E := EStackOverflow;
{$WARN SYMBOL_DEPRECATED ON}
      else E := EExternal;
    end;
    Level := sllExceptionOS;
  end;
  if (Level<>SllError) and
     (SynLog.fFamily.ExceptionIgnore.IndexOf(E)<0) then begin
    SynLog.LogHeaderLock(Level);
    if (Level=sllException) and (E<>EExternalException) then
      SynLog.fWriter.Add('% ("%") at ',[E,Exception(EObject).Message]) else
      SynLog.fWriter.Add('% (%) at ',[E,pointer(ECode)]);
    TSynMapFile.Log(SynLog.fWriter,EAddr);
    depth := SynLog.fFamily.fStackTraceLevel;



    if depth>0 then begin
      SynLog.fWriter.AddShort('stack trace ');
      SynLogExceptionEnabled := false; // for IsBadCodePtr
      try
        asm
          mov eax,fs:[18h]
          mov ecx,dword ptr [eax+4]
          mov max_stack,ecx
          mov ecx,dword ptr [eax+8]
          mov min_stack,ecx
        end;
        while PtrUInt(stack)<max_stack do begin
          st := stack^;
          if ((st>max_stack) or (st<min_stack)) and
             not IsBadReadPtr(pointer(st-8),12) and
             ((pByte(st-5)^=$E8) or check2(st)) then begin
            TSynMapFile.Log(SynLog.fWriter,st);
            dec(depth);
            if depth=0 then break;
          end;

          inc(stack);
        end;
      finally
        SynLogExceptionEnabled := true;
      end;
    end;
    SynLog.fWriter.AddCR;
    SynLog.UnLock;
  end;
  SetLastError(LastError); // code above could have changed this
end;

procedure SynRtlUnwind(TargetFrame, TargetIp, ExceptionRecord, ReturnValue: Pointer); stdcall;
asm
  pushad
  cmp  byte ptr SynLogExceptionEnabled,0
  jz   @oldproc
  mov  eax,[ebp+8]
  or   eax,eax
  jz   @oldproc
  mov  ebx,[eax+8]
  mov  eax,[ebp+16]
  push dword ptr [eax+24]
  push dword ptr [eax]
  mov  ecx,[eax]
  cmp  ecx,cDelphiException
  je   @delphi_exception
  cmp  ecx,cDelphiExcept
  je   @delphi_exception
  mov  eax,[eax+12]
  push eax
  push ebx
  call LogExcept
  jmp  @oldproc
@delphi_exception:
  mov  eax,[eax+20]
  push eax
  push ebx
  call LogExcept
@oldproc:
  popad
  pop  ebp
  jmp  oldUnWindProc
end;

{$endif WITH_VECTOREXCEPT}

procedure TSynLogFamily.SetLevel(const aLevel: TSynLogInfos);


{$ifdef WITH_VECTOREXCEPT}
var K32: HMODULE;
{$endif}
begin
  fLevel := aLevel;
  fHandleExceptions := (sllExceptionOS in aLevel) or (sllException in aLevel);
  if not SynLogExceptionEnabled and fHandleExceptions then begin
    SynLogExceptionEnabled := true;
{$ifdef WITH_VECTOREXCEPT}
    K32 := GetModuleHandle(kernel32);
    AddVectoredExceptionHandler := GetProcAddress(K32,'AddVectoredExceptionHandler');

    // RemoveVectoredContinueHandler() is available under 64 bit editions only
    if Assigned(AddVectoredExceptionHandler) then
      // available since Windows XP
      AddVectoredExceptionHandler(0,@SynLogVectoredHandler);
{$else WITH_VECTOREXCEPT}
    oldUnWindProc := RTLUnwindProc;
    RTLUnwindProc := @SynRtlUnwind;
{$endif WITH_VECTOREXCEPT}
  end;
end;

constructor TSynLogFamily.Create(aSynLog: TSynLogClass);
begin
  fSynLogClass := aSynLog;
  if SynLogFamily=nil then begin
    SynLogFamily := TObjectList.Create;
    GarbageCollector.Add(SynLogFamily);
  end;
  fIdent := SynLogFamily.Add(self);
  fDestinationPath := ExtractFilePath(paramstr(0));
  fBufferSize := 4096;
  fStackTraceLevel := 20;
  fExceptionIgnore := TList.Create;
end;

function TSynLogFamily.CreateSynLog: TSynLog;
begin
  result := fSynLogClass.Create(self);
  if fPerThreadLog then begin
    if SynLogFile=nil then begin
................................................................................
  AutoFlushThread: THandle = 0;
  AutoFlushSecondElapsed: cardinal;

procedure AutoFlushProc(P: pointer); stdcall;  // TThread not needed here
var i: integer;
begin
  repeat
    Sleep(1000); // thread will awake every second to check of pending data 
    if SynLogFile=nil then
      continue;
    inc(AutoFlushSecondElapsed);
    for i := 0 to SynLogFile.Count-1 do
    with TSynLog(SynLogFile.List^[i]) do
      if (fFamily.fAutoFlush<>0) and (fWriter<>nil) and
         (AutoFlushSecondElapsed mod fFamily.fAutoFlush=0) then
        if fWriter.B-fWriter.fTempBuf>1 then begin
          if not IsMultiThread and
             not fWriterStream.InheritsFrom(TFileStream) then
            IsMultiThread := true; // only TFileStream is thread-safe
          Flush(false); // write pending data
        end;
  until false;
end;

procedure TSynLogFamily.SetAutoFlush(TimeOut: cardinal);
var ID: cardinal;
begin
  SynLog.CreateLocker;
  fAutoFlush := TimeOut;
  if (AutoFlushThread=0) and (TimeOut<>0) then begin
    AutoFlushSecondElapsed := 0;
    AutoFlushThread := CreateThread(nil,0,@AutoFlushProc,nil,0,ID);
  end;
end;

destructor TSynLogFamily.Destroy;
begin
  FreeAndNil(fGlobalLog);
  if AutoFlushThread<>0 then begin
    CloseHandle(AutoFlushThread); // release background thread once for all
    AutoFlushThread := 0;
  end;
  FreeAndNil(fExceptionIgnore);
  inherited;
end;

function TSynLogFamily.SynLog: TSynLog;
var ndx: integer;
begin
  if fPerThreadLog then begin
................................................................................
    ndx := SynLogFileIndex[fIdent]-1;
    if ndx>=0 then
      result := SynLogFile.List^[ndx] else
      result := CreateSynLog;
  end else
    if fGlobalLog<>nil then
      result := fGlobalLog else
      result := CreateSynLog;
  if fHandleExceptions then
    CurrentHandleExceptionSynLog := result;
end;


{ TSynLog }

................................................................................
  inc(fRecursionCount);
  result := fRecursionCount;
end;

function TSynLog._Release: Integer;
begin
  dec(fRecursionCount);
  if sllLeave in fFamily.Level then begin
    asm
      mov eax,self
      mov ecx,[ebp+16]
      mov TSynLog.fRecursionCaller[eax],ecx
    end;
    fRecursion^[fRecursionCount].Caller := fRecursionCaller;
    DoEnterLeave(sllLeave);
  end;
  result := fRecursionCount;
end;

constructor TSynLog.Create(aFamily: TSynLogFamily);
begin
  fFamily := aFamily;
end;
................................................................................
begin
  inc(fRecursionMax,256+fRecursionMax shr 3);
  ReallocMem(fRecursion,fRecursionMax*sizeof(fRecursion^[0]));
end;

class function TSynLog.Enter(aClassType: TClass; aMethodName: PUTF8Char): ISynLog;
var aSynLog: TSynLog;
    aStackFrame: PtrUInt;
begin
  asm
    mov eax,[ebp+4]  // retrieve caller EIP from push ebp; mov ebp,esp
    sub eax,5        // ignore call TSynLog.Enter op codes
    mov aStackFrame,eax
  end;
  aSynLog := Family.SynLog;
  with aSynLog do begin
    // recursively store parameters
    if fRecursionCount=fRecursionMax then
      RecursionGrow;
    with fRecursion^[fRecursionCount] do begin
      Instance := nil;
      ClassType := aClassType;
      Method := aMethodName;
      Caller := aStackFrame;
    end;
  end;
  // copy to ISynLog interface -> will call TSynLog._AddRef
  result := aSynLog;
end;

class function TSynLog.Enter(aInstance: TObject; aMethodName: PUTF8Char): ISynLog;
var aSynLog: TSynLog;
    aFamily: TSynLogFamily;
    aStackFrame: PtrUInt;
begin
  asm
    mov eax,[ebp+4]  // retrieve caller EIP from push ebp; mov ebp,esp
    sub eax,5        // ignore call TSynLog.Enter op codes
    mov aStackFrame,eax
  end;
  // inlined aSynLog := Family.SynLog
  aFamily := PPointer(PtrInt(Self)+vmtAutoTable)^;
  if aFamily=nil then
    aSynLog := FamilyCreate.SynLog else
    aSynLog := aFamily.SynLog;
  // recursively store parameters
  with aSynLog do begin
................................................................................
      RecursionGrow;
    with fRecursion^[fRecursionCount] do begin
      Instance := aInstance;
      if aInstance=nil then
        ClassType := pointer(aInstance) else
        ClassType := PPointer(aInstance)^;
      Method := aMethodName;
      Caller := aStackFrame;
    end;
  end;
  // copy to ISynLog interface -> will call TSynLog._AddRef
  result := aSynLog;
end;

class function TSynLog.FamilyCreate: TSynLogFamily;
................................................................................

procedure TSynLog.Log(Level: TSynLogInfo; aName: PWinAnsiChar;
  aTypeInfo: pointer; var aValue);
begin
  if Level in fFamily.fLevel then
    LogInternal(Level,aName,aTypeInfo,aValue);
end;

procedure TSynLog.Log(Level: TSynLogInfo);
var aCaller: PtrUInt;
begin
  if Level in fFamily.fLevel then begin
    LogHeaderLock(Level);
    asm
      mov eax,[ebp+4]  // retrieve caller EIP from push ebp; mov ebp,esp
      sub eax,5        // ignore call TSynLog.Enter op codes
      mov aCaller,eax
    end;
    TSynMapFile.Log(fWriter,aCaller);
    fWriter.AddCR;
    UnLock;
  end;
end;

procedure TSynLog.LogFileHeader;
var Freq: Int64;
begin
  QueryPerformanceFrequency(Freq);
  ExeVersionRetrieve;
  with ExeVersion, SystemInfo, OSVersionInfo do
................................................................................
procedure TSynLog.CreateLogWriter;
begin
  if fWriterStream=nil then begin
    ExeVersionRetrieve;
    fFileName := string(ExeVersion.ProgramName)+' '+GetCaptionFromClass(ClassType)+' '+
      TFileName(NowToString(false));
    if fFamily.PerThreadLog then
      fFileName := fFileName+' '+IntToString(GetCurrentThreadId);
    fFileName := fFamily.fDestinationPath+fFileName+'.log';
    fWriterStream := TFileStream.Create(fFileName,fmCreate)
  end;
  if fWriter=nil then
    fWriter := TTextWriter.Create(fWriterStream,fFamily.BufferSize);
end;

................................................................................
      if Instance<>nil then begin
        fWriter.Add('(');
        fWriter.AddPointer(PtrUInt(Instance));
        fWriter.Add(')');
      end;
      fWriter.Add('.');
    end;
    if Method<>nil then
      fWriter.AddNoJSONEscape(Method) else
      TSynMapFile.Log(fWriter,Caller);
  end;
  fWriter.AddCR;
end;

procedure TSynLog.DoEnterLeave(aLevel: TSynLogInfo);
begin
  LogHeaderLock(aLevel);

Changes to SynSelfTests.pas.

367
368
369
370
371
372
373
374
375

376
377
378
379
380
381
382
383








384
385
386
387
388
389
390
...
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
...
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
var
  TestLevel: TOrdType = high(TOrdType);

procedure TTestCompression.TestLog;
var ILog: ISynLog;
    S: TSQLFieldTypes;
begin
  ILog := TSQLLog.Enter(self,'TestLog');
  // do some stuff

  ILog.Log(sllInfo,'TestLevel',TypeInfo(TOrdType),TestLevel);
  ILog.Log(sllInfo,'set',TypeInfo(TSQLFieldTypes),S);
  ILog.Log(sllDebug,ILog.Instance);
  if TestLevel=low(TOrdType) then
    TTestCompression(nil).ClassName; // will raise an access violation
  dec(TestLevel);
  TestLog;
end;










{ TTestCryptographicRoutines }

procedure TTestCryptographicRoutines.Adler32;
begin
  Check(Adler32SelfTest);
................................................................................
      StandardFontsReplace := embed;
      AddPage;
      Canvas.SetFont('arial',10,[]);
      Check(Canvas.Page.Font.Name=Name[embed]);
      y := 800;
      for i := 1 to 30 do begin
        Canvas.SetFont('Arial',9+i,[]);
        Canvas.TextOut(100,y,WinAnsiString('Texte accentu'#233' n�'+IntToStr(i)));
        dec(y,9+i);
      end;
      SaveToStream(MS,Date);
      // MS.SaveToFile(ChangeFileExt(paramstr(0),'.pdf'));
      Check(Hash32(MS.Memory,MS.Position)=Hash[embed]);
      if not embed then begin
        NewDoc;
................................................................................
  finally
    Free;
    MS.Free;
  end;
end;
{$endif}





begin










  if false then begin










    AllocConsole;
    with TSQLLog.Family do begin
      Level := LOG_VERBOSE;
      //Level := [sllException];
      PerThreadLog := true;
      HighResolutionTimeStamp := true;
      AutoFlushTimeOut := 5;
    end;
    with TTestCompression.Create(nil) do
    try
      try
        TestLog;

      except
        on Exception do; // just ignore now

      end;
    finally
      Free;
    end;
    TSQLLog.Add.Log(sllDebug,'GarbageCollector',GarbageCollector);
    TSQLLog.Add.Log(sllDebug,GarbageCollector);
    TSynLog.Family.Level := [sllDebug];
    TSynLog.Add.Log(sllDebug,GarbageCollector);



    try
      raise ECrtSocket.Create('Test exception',-1024);


    except
      on Exception do; // just ignore now
    end;



















    halt;
  end;




end.






|

>








>
>
>
>
>
>
>
>







 







|







 







<
>
>
>
|
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
|
|
<
|
<
<
>
|
|
>
|
<
<
<
<
<
<
<
>
>
>
|
<
>
>



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>
>
>
>

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
...
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
...
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
var
  TestLevel: TOrdType = high(TOrdType);

procedure TTestCompression.TestLog;
var ILog: ISynLog;
    S: TSQLFieldTypes;
begin
  ILog := TSQLLog.Enter(self);
  // do some stuff
  ILog.Log(sllCustom1);
  ILog.Log(sllInfo,'TestLevel',TypeInfo(TOrdType),TestLevel);
  ILog.Log(sllInfo,'set',TypeInfo(TSQLFieldTypes),S);
  ILog.Log(sllDebug,ILog.Instance);
  if TestLevel=low(TOrdType) then
    TTestCompression(nil).ClassName; // will raise an access violation
  dec(TestLevel);
  TestLog;
end;

procedure TestLogProc;
var ILog: ISynLog;
begin
  ILog := TSQLLog.Enter;
  ILog.Log(sllDebug,'GarbageCollector',GarbageCollector);
  ILog.Log(sllDebug,GarbageCollector);
end;


{ TTestCryptographicRoutines }

procedure TTestCryptographicRoutines.Adler32;
begin
  Check(Adler32SelfTest);
................................................................................
      StandardFontsReplace := embed;
      AddPage;
      Canvas.SetFont('arial',10,[]);
      Check(Canvas.Page.Font.Name=Name[embed]);
      y := 800;
      for i := 1 to 30 do begin
        Canvas.SetFont('Arial',9+i,[]);
        Canvas.TextOut(100,y,WinAnsiString('Texte accentu'#233' n�'+IntToString(i)));
        dec(y,9+i);
      end;
      SaveToStream(MS,Date);
      // MS.SaveToFile(ChangeFileExt(paramstr(0),'.pdf'));
      Check(Hash32(MS.Memory,MS.Position)=Hash[embed]);
      if not embed then begin
        NewDoc;
................................................................................
  finally
    Free;
    MS.Free;
  end;
end;
{$endif}


procedure TestsLog;
  procedure Proc2(n1, n2: Integer); forward;
  procedure Proc1(n1, n2: Integer);
  begin
    if n1 = 0 then
    try
      asm xor eax,eax; mov [eax],eax; end; // AV
    except
      on e: exception do
        TSQLLog.Add.Log(sllInfo,'^^^^^^^^ recursicve, Proc1 function',e);
    end else
    Proc2(n1 - 1, n2);
  end;
  procedure Proc2(n1, n2: Integer);
  begin
    if n2 = 0 then
      try
        asm xor eax,eax; mov [eax],eax; end; // AV
      except
        on e: exception do
          TSQLLog.Add.Log(sllInfo,'^^^^^^^^ recursicve, Proc2 function',e);
      end else
      Proc1(n1, n2 - 1);
  end;
begin
  AllocConsole;
  with TSQLLog.Family do begin
    Level := LOG_VERBOSE;
    //Level := [sllException];
    PerThreadLog := true;
    HighResolutionTimeStamp := true;
    //AutoFlushTimeOut := 5;
  end;

  try


    writeln(IOResult div IOResult); // will raise EDivByZero
  except
    on e: exception do
      TSQLLog.Add.Log(sllInfo,'^^^^^^^^ the first sample, divide by 0',e);
  end;







  Proc1(5, 7);
  Proc2(7, 5);
  with TTestCompression.Create(nil) do
  try

    try
      TestLog;
    except
      on Exception do; // just ignore now
    end;
  finally
    Free;
  end;
  TestLogProc;
  TSynLog.Family.Level := [sllDebug];
  TSynLog.Add.Log(sllDebug,GarbageCollector);
  try
    raise ECrtSocket.Create('Test exception',-1024); // logged to TSQLLog
  except
    on e: Exception do
      TSQLLog.Add.Log(sllInfo,'^^^^^^^^  custom exception type',e);
  end;
  TSQLLog.Family.ExceptionIgnore.Add(ECrtSocket);
  try
    raise ECrtSocket.Create('Test exception',-1024);
  except
    on e: Exception do
      TSQLLog.Add.Log(sllInfo,'^^^^^^^^  nothing should be logged just above',e);
  end;
  halt;
end;

begin
  if false then
    TestsLog;
end.