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

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

Overview
Comment:
  • added function GetJSONFieldOrObjectOrArray() in unit's interface section
  • fix Delphi 5 compilation issue
  • some minor code refactoring about low-level Delphi RTL functions patching
  • fix typo in documentation
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 9014c28bd559c9c87287c625c7c7e653cefab8b4
User & Date: abouchez 2014-01-20 08:11:44
Context
2014-01-20
13:34
added SQLITE_MEMORY_DATABASE_NAME constant as alias to ':memory:' check-in: 18be73251a user: abouchez tags: trunk
08:11
  • added function GetJSONFieldOrObjectOrArray() in unit's interface section
  • fix Delphi 5 compilation issue
  • some minor code refactoring about low-level Delphi RTL functions patching
  • fix typo in documentation
check-in: 9014c28bd5 user: abouchez tags: trunk
2014-01-19
14:52
  • TBSONElement has now some methods for proper OOP process (better than some global functions)
  • first step to integrate our TDocVariant / TBSONVariant custom variants to the main variant process of SynCommons and mORMot - goal is to let those kind of variants be handled natively everywhere in the framework, e.g. as published properties of any TSQLRecord; so it will help layering TSQLRecord to work with MongoDB document oriented area at full power, i.e. with some fixed fields (e.g. for indexing), then with any nested tree of properties - so letting data sharding be as native as possible :)
check-in: 35702a3019 user: User tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SQLite3/Documentation/Synopse SQLite3 Framework.pro.

3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
!end;
!
!destructor TSQLServer.Destroy;
!begin
!  FHttpServer.Free;
!  inherited;
!end;
You will need to specify also no the client side that those {\f1\fs20 TSQLValue1} and {\f1\fs20 TSQLValue2} tables are virtual.
You have several possibilities:
- Inherit each table not from {\f1\fs20 TSQLRecord}, but from {\f1\fs20 @**TSQLRecordVirtualTableAutoID@}, as was stated above as standard procedure for virtual tables - see @76@;
- If your tables are defined as {\f1\fs20 TSQLRecord}, ensure that the Client side set the table property of its own model to {\f1\fs20 rCustomAutoID};
- If your tables are defined as {\f1\fs20 TSQLRecord}, ensure that both Client and Server set the table property of its own model to {\f1\fs20 rCustomAutoID}.
First option could be done as such:
!type
!!  TSQLValue1 = class(TSQLRecordVirtualTableAutoID)






|







3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
!end;
!
!destructor TSQLServer.Destroy;
!begin
!  FHttpServer.Free;
!  inherited;
!end;
You will need to specify also on the client side that those {\f1\fs20 TSQLValue1} and {\f1\fs20 TSQLValue2} tables are virtual.
You have several possibilities:
- Inherit each table not from {\f1\fs20 TSQLRecord}, but from {\f1\fs20 @**TSQLRecordVirtualTableAutoID@}, as was stated above as standard procedure for virtual tables - see @76@;
- If your tables are defined as {\f1\fs20 TSQLRecord}, ensure that the Client side set the table property of its own model to {\f1\fs20 rCustomAutoID};
- If your tables are defined as {\f1\fs20 TSQLRecord}, ensure that both Client and Server set the table property of its own model to {\f1\fs20 rCustomAutoID}.
First option could be done as such:
!type
!!  TSQLValue1 = class(TSQLRecordVirtualTableAutoID)

Changes to SynCommons.pas.

439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
...
528
529
530
531
532
533
534


535
536
537
538
539
540
541
....
5366
5367
5368
5369
5370
5371
5372












5373
5374
5375
5376
5377
5378
5379
....
6620
6621
6622
6623
6624
6625
6626

6627

6628
6629
6630
6631
6632
6633
6634
6635
6636
6637
6638
6639
6640
6641
6642
6643
6644
6645
6646
6647
6648
6649
6650
6651
6652
.....
11789
11790
11791
11792
11793
11794
11795
11796
11797
11798
11799
11800
11801
11802
11803
11804
11805
11806
11807
11808
11809
11810
11811
11812
.....
21964
21965
21966
21967
21968
21969
21970
21971


21972

21973



21974
21975
21976
21977
21978
21979
21980
.....
22107
22108
22109
22110
22111
22112
22113
22114
22115
22116
22117
22118
22119
22120
22121
22122
22123
22124
22125
22126
22127
22128
22129
22130
22131
22132
.....
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
23230
23231
23232
23233
23234
23235
.....
23554
23555
23556
23557
23558
23559
23560
23561
23562
23563
23564
23565
23566
23567
23568
23569
23570
23571
23572
23573
.....
23580
23581
23582
23583
23584
23585
23586
23587
23588
23589

23590
23591
23592
23593
23594
23595
23596
23597
23598
23599
23600
23601
23602
23603
23604
23605
23606
23607
23608
23609
23610
23611
23612


23613
23614
23615
23616
23617
23618
23619

23620



23621
23622
23623
23624
23625
23626
23627
.....
23632
23633
23634
23635
23636
23637
23638
23639
23640
23641
23642
23643
23644
23645
23646
.....
28387
28388
28389
28390
28391
28392
28393






















28394
28395
28396
28397
28398
28399
28400
.....
37171
37172
37173
37174
37175
37176
37177
37178
37179
37180
37181
37182
37183
37184
37185
37186
37187
37188
37189
37190
37191
37192
37193
37194
37195
37196
37197
37198
37199
37200
37201
37202
37203
37204
37205
.....
37250
37251
37252
37253
37254
37255
37256
37257
37258
37259
37260
37261
37262
  - added Base64MagicDecode() and SQLToDateTime() functions 
  - added IsEqual(const A,B: TSQLFieldBits): boolean function
  - enhanced FPC/Lazarus Win32/Win64 compilation
  - TDynArrayHashed is now a record with Delphi 2009+, due to a bug in latest
    version of Delphi compiler when using TDynArrayHashed = object(TDynArray)
  - fixed [7658da5529] unexpected hash collision in TDynArrayHashed
  - fixed unexpected GPF in TSynCache.Find() e.g. when cache is disabled
  - fixed function GetJSONField() to properly decode JSON number with exponent
  - handle variant serialization in/from JSON using new VariantLoadJSON(),
    VariantSaveJSON() functions and TTextWriter.AddVariantJSON() method
  - handle variant serialization in/from our binary custom format, using new
    VariantLoad(), VariantSaveLength() and VariantSave() functions
  - added VariantToUTF8() overloaded functions for fast conversion
  - added VariantToInteger()/VariantToIntegerDef() functions for direct process
    of numerical variants (e.g. array indexes)
................................................................................
  - confusing-named RoundTo2Digits() function renamed into Trunc2ToDigit()
  - added simple, non banker rounding SimpleRoundTo2Digits() function
  - fixed potential comparison error in TSynTableFieldProperties.SortCompare()
    when sorting UTF8 Field with tfoCaseInsensitive in Options
  - speedup of QuotedStr() function and TDynArrayHashed hashing process
  - several speedup in GetJSONField() and JSON parsing: it will now expect true,
    false or null to be in lowercase only (as in json.org specifications)


  - function GotoNextJSONField() renamed GotoNextJSONItem(), and fixed to
    handle nested JSON array or objects in addition to string/numbers
  - added function JSONRetrieveIDField() for fast retrieval of a "ID":.. value
  - added function JSONRetrieveStringField() for retrieval of a string field
    name or value from JSON buffer
  - added TextColor() and TextBackground() functions - will initialize internal
    console process after any manual AllocConsole call
................................................................................
// - any integer value is left as its ascii representation
// - wasString is set to true if the JSON value was a "string"
// - works for both field names or values (e.g. '"FieldName":' or 'Value,')
// - EndOfObject (if not nil) is set to the JSON value char (',' ':' or '}' e.g.)
function GetJSONField(P: PUTF8Char; out PDest: PUTF8Char;
  wasString: PBoolean=nil; EndOfObject: PUTF8Char=nil): PUTF8Char;













/// test if the supplied buffer is a "string" value or a numerical value
// (floating point or integer), according to the characters within
// - this version will recognize null/false/true as strings
// - e.g. IsString('0')=false, IsString('abc')=true, IsString('null')=true
function IsString(P: PUTF8Char): boolean;

/// test if the supplied buffer is a "string" value or a numerical value
................................................................................

{/ delete the window resources used to receive GDI messages
  - must be called for each CreateInternalWindow() function
  - both parameter values are then reset to ''/0 }
function ReleaseInternalWindow(var aWindowName: string; var aWindow: HWND): boolean;

type

  TPatchCode = array[0..4] of byte;

  PPatchCode = ^TPatchCode;

/// Self-modifying code - change some memory buffer in the code segment
// - if Backup is not nil, it should point to a Size array of bytes, ready
// to contain the overriden code buffer, for further hook disabling
procedure PatchCode(Old,New: pointer; Size: integer; Backup: pointer=nil;
  LeaveUnprotected: boolean=false);

/// Self-modifying code - change one PtrUInt in the code segment
procedure PatchCodePtrUInt(Code: PPtrUInt; Value: PtrUInt;
  LeaveUnprotected: boolean=false);

/// Self-modifying code - add an asm JUMP to a redirected function
// - if Backup is not nil, it should point to a Size array of bytes, ready
// to contain the overriden code buffer, for further hook disabling
procedure RedirectCode(Func, RedirectFunc: Pointer; Backup: PPatchCode=nil);

/// Self-modifying code - restore a code from its RedirectCode() backup
procedure RedirectCodeRestore(Func: pointer; const Backup: TPatchCode);

{$else}

/// compatibility function, to be implemented according to the running OS
// - expect more or less the same result as the homonymous Win32 API function
function GetTickCount: Cardinal;
................................................................................
asm
  bswap eax
end;
{$endif}
{$endif}

{$ifndef FPC}
procedure FillCharInvoke;
asm
  call System.@FillChar
end;

procedure MoveInvoke;
asm
  call System.Move
end;


{$ifdef CPU64}
{$ifndef NOX64PATCHRTL}

{ Some notes about MOVNTI opcode use below:
  - Delphi inline assembler is not able to compile the instruction -> so we
    had to write some manual DB $... values instead :(
................................................................................
asm // same params than _CopyRecord{ dest, source, typeInfo: Pointer }
{$ifdef CPUX64}
  .NOFRAME
{$endif}
  jmp System.@CopyRecord
end;
{$else PUREPASCAL}
procedure RecordCopyInvoke;


asm

  call System.@CopyRecord



end;

procedure RecordCopy(var Dest; const Source; TypeInfo: pointer);
asm  // faster version of _CopyRecord{dest, source, typeInfo: Pointer} by AB
        { ->    EAX pointer to dest             }
        {       EDX pointer to source           }
        {       ECX pointer to typeInfo         }
................................................................................
end;
{$endif PUREPASCAL}
{$endif FPC}
{$endif ENHANCEDRTL}
{$endif DELPHI5OROLDER}
{$endif USEPACKAGES}

/// used internally to retrieve hidden System.pas function address from asm stub
function GetAddressFromCall(AStub: Pointer): Pointer;
begin
  if AStub=nil then
    result := AStub else
  if PBYTE(AStub)^ = $E8 then begin
    Inc(PtrInt(AStub));
    Result := Pointer(PtrInt(AStub)+SizeOf(integer)+PInteger(AStub)^);
  end else
    Result := nil;
end;


{ ************  Custom record / dynamic array JSON serialization }

type
  /// information about one customized JSON serialization
  TJSONCustomParserRegistration = record
    RecordTypeName: RawUTF8;
................................................................................
      exit;
    end;
    end;
  end;
  result := Source;
end;

function GetJSONFieldOrObjectOrArray(var P: PUTF8Char; wasString: PBoolean=nil;
  EndOfObject: PUTF8Char=nil; HandleValuesAsObjectOrArray: Boolean=false): PUTF8Char;
var Value: PUTF8Char;
begin
  result := nil;
  while ord(P^) in [1..32] do inc(P);
  if HandleValuesAsObjectOrArray and (P^ in ['{','[']) then begin
    Value := P;
    P := GotoNextJSONObjectOrArray(P);
    if P=nil then
      exit; // invalid content
    if wasString<>nil then
      wasString^ := false; // was object or array
    if EndOfObject<>nil then
     EndOfObject^ := P^;
    P^ := #0; // so Values[] will be a valid ASCIIZ string
    inc(P);
    result := Value;
  end else
    result := GetJSONField(P,P,wasString,EndOfObject);
end;

function VariantLoadJSON(var Value: variant; JSON: PUTF8Char;
  ForceStringAs: TReturnedStringType;
  EndOfObject: PUTF8Char; HandleObjectArray: PDocVariantOptions): PUTF8Char;
var wasString: boolean;
    Val: PUTF8Char;
begin
  result := JSON;
................................................................................

var
  LastDispInvokeType: TSynInvokeableVariantType;

procedure SynVarDispProc(Result: PVarData; const Instance: TVarData;
      CallDesc: PCallDesc; Params: Pointer); cdecl;
const DO_PROP = 1; GET_PROP = 2; SET_PROP = 4;
var i: integer;
    Value: TVarData;
    Handler: TCustomVariantType;
    CacheDispInvokeType: TSynInvokeableVariantType; // to be thread-safe
begin
  if Instance.VType=varByRef or varVariant then // handle By Ref variants 
    SynVarDispProc(Result,PVarData(Instance.VPointer)^,CallDesc,Params) else begin
    if Result<>nil then
      VarClear(Variant(Result^));
    case Instance.VType of
    varDispatch, varDispatch or varByRef,
    varUnknown, varUnknown or varByRef, varAny:
      // process Ole Automation variants
................................................................................
        CacheDispInvokeType := LastDispInvokeType; 
        if (CacheDispInvokeType<>nil) and
           (CacheDispInvokeType.VarType=TVarData(Instance).VType) and
           (CallDesc^.CallType in [GET_PROP, DO_PROP]) and
           (Result<>nil) and (CallDesc^.ArgCount=0) then begin
          CacheDispInvokeType.IntGet(Result^,Instance,@CallDesc^.ArgTypes[0]);
          exit;
        end else
        // handle our custom types
        for i := 0 to SynVariantTypes.Count-1 do

          with TSynInvokeableVariantType(SynVariantTypes.List[i]) do
          if VarType=TVarData(Instance).VType then
          case CallDesc^.CallType of
          GET_PROP, DO_PROP:
            if (Result<>nil) and (CallDesc^.ArgCount=0) then begin
              IntGet(Result^,Instance,@CallDesc^.ArgTypes[0]);
              LastDispInvokeType := SynVariantTypes.List[i]; // speed up in loop
              exit;
            end;
          SET_PROP:
            if (Result=nil) and (CallDesc^.ArgCount=1) then begin
              ParseParamPointer(@Params,CallDesc^.ArgTypes[0],Value);
              IntSet(Instance,Value,@CallDesc^.ArgTypes[1]);
              exit;
            end;
          end;
      end;
      // here we call the default code handling custom types
      if FindCustomVariantType(Instance.VType,Handler) then
        TSynInvokeableVariantType(Handler).DispInvoke(
          {$ifdef DELPHI6OROLDER}Result^{$else}Result{$endif},
          Instance,CallDesc,@Params)
      else raise EInvalidOp.Create('Invalid variant invoke');


    end;
    end;
  end;
end;

procedure VariantsDispInvoke;
asm

  call Variants.@DispInvoke;



end;

{$endif FPC}
{$endif ISDELPHIXE2}

function SynRegisterCustomVariantType(aClass: TSynInvokeableVariantTypeClass): TSynInvokeableVariantType;
{$ifdef DELPHI6OROLDER}
................................................................................
{$ifdef DELPHI6OROLDER}
    GetVariantManager(VarMgr);
    VarMgr.DispInvoke := @SynVarDispProc;
    SetVariantManager(VarMgr);
{$else}
  {$ifndef FPC}
  {$ifndef ISDELPHIXE2} // Delphi XE2 just does not like our performance trick :(
    RedirectCode(GetAddressFromCall(@VariantsDispInvoke),@SynVarDispProc);
  {$endif}
  {$endif}
{$endif}
    GarbageCollectorFreeAndNil(SynVariantTypes,TObjectList.Create);
  end;
  result :=  aClass.Create; // register variant type
  SynVariantTypes.Add(result);
................................................................................
  if EndOfObject<>nil then
    EndOfObject^ := P^;
  P^ := #0; // make zero-terminated
  PDest := @P[1];
  if P[1]=#0 then
    PDest := nil;
end;























function IsString(P: PUTF8Char): boolean;  // test if P^ is a "string" value
begin
  if P=nil then begin
    result := false;
    exit;
  end;
................................................................................
{$endif PUREPASCAL}
{$else}
begin
{$endif DELPHI5OROLDER}
  {$ifndef FPC}
   {$ifdef CPU64}
   {$ifndef NOX64PATCHRTL}
   RedirectCode(GetAddressFromCall(@FillCharInvoke),@FillChar);
   RedirectCode(GetAddressFromCall(@MoveInvoke),@Move);
   {$endif NOX64PATCHRTL}
   {$endif CPU64}
   {$ifndef ENHANCEDRTL}
    {$ifndef PUREPASCAL}
     {$ifndef DELPHI5OROLDER}
      {$ifndef USEPACKAGES}
       {$ifdef DOPATCHTRTL}
  RedirectCode(GetAddressFromCall(@RecordCopyInvoke),@RecordCopy);
       {$endif DOPATCHTRTL}
      {$endif USEPACKAGES}
     {$endif DELPHI5OROLDER}
     {$ifndef LVCL}
      {$ifndef DELPHI5OROLDER}
  if not SupportsSSE2 then // back to default X87 code for older CPUs
    PatchCode(@FillChar,@FillCharX87,FILLCHAR_SIZE);
      {$endif DELPHI5OROLDER}
      {$ifndef ISDELPHI2007ANDUP} // use faster FillChar/Move for older Delphi
  RedirectCode(GetAddressFromCall(@FillCharInvoke),@FillChar);
  RedirectCode(GetAddressFromCall(@MoveInvoke),@Move);
      {$endif ISDELPHI2007ANDUP}
     {$endif LVCL}
    {$endif PUREPASCAL}
   {$endif ENHANCEDRTL}
  {$endif FPC}
end;

................................................................................
  {$ifndef NOVARIANTS}
  Assert(SizeOf(TSynTableData)=sizeof(TVarData));
  {$endif NOVARIANTS}

finalization
  GarbageCollectorFree;
end.












<







 







>
>







 







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







 







>

>


|





|



|
|



|







 







|
|
|
|
|
|
|
|
|
<







 







<
>
>

>
|
>
>
>







 







<
<
<
<
<
<
<
<
<
<
<
<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<
|
|


|







 







|
|
|
>
|
<

|

|
|


|


|



<
|
<
<
|

<
>
>





|

>
|
>
>
>







 







|







 







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







 







|
|







|









|
|







 








<
<
<
<
<
439
440
441
442
443
444
445

446
447
448
449
450
451
452
...
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
....
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
....
6633
6634
6635
6636
6637
6638
6639
6640
6641
6642
6643
6644
6645
6646
6647
6648
6649
6650
6651
6652
6653
6654
6655
6656
6657
6658
6659
6660
6661
6662
6663
6664
6665
6666
6667
.....
11804
11805
11806
11807
11808
11809
11810
11811
11812
11813
11814
11815
11816
11817
11818
11819

11820
11821
11822
11823
11824
11825
11826
.....
21978
21979
21980
21981
21982
21983
21984

21985
21986
21987
21988
21989
21990
21991
21992
21993
21994
21995
21996
21997
21998
21999
.....
22126
22127
22128
22129
22130
22131
22132












22133
22134
22135
22136
22137
22138
22139
.....
23207
23208
23209
23210
23211
23212
23213






















23214
23215
23216
23217
23218
23219
23220
.....
23539
23540
23541
23542
23543
23544
23545

23546
23547
23548
23549
23550
23551
23552
23553
23554
23555
23556
23557
.....
23564
23565
23566
23567
23568
23569
23570
23571
23572
23573
23574
23575

23576
23577
23578
23579
23580
23581
23582
23583
23584
23585
23586
23587
23588
23589

23590


23591
23592

23593
23594
23595
23596
23597
23598
23599
23600
23601
23602
23603
23604
23605
23606
23607
23608
23609
23610
23611
23612
23613
.....
23618
23619
23620
23621
23622
23623
23624
23625
23626
23627
23628
23629
23630
23631
23632
.....
28373
28374
28375
28376
28377
28378
28379
28380
28381
28382
28383
28384
28385
28386
28387
28388
28389
28390
28391
28392
28393
28394
28395
28396
28397
28398
28399
28400
28401
28402
28403
28404
28405
28406
28407
28408
.....
37179
37180
37181
37182
37183
37184
37185
37186
37187
37188
37189
37190
37191
37192
37193
37194
37195
37196
37197
37198
37199
37200
37201
37202
37203
37204
37205
37206
37207
37208
37209
37210
37211
37212
37213
.....
37258
37259
37260
37261
37262
37263
37264
37265





  - added Base64MagicDecode() and SQLToDateTime() functions 
  - added IsEqual(const A,B: TSQLFieldBits): boolean function
  - enhanced FPC/Lazarus Win32/Win64 compilation
  - TDynArrayHashed is now a record with Delphi 2009+, due to a bug in latest
    version of Delphi compiler when using TDynArrayHashed = object(TDynArray)
  - fixed [7658da5529] unexpected hash collision in TDynArrayHashed
  - fixed unexpected GPF in TSynCache.Find() e.g. when cache is disabled

  - handle variant serialization in/from JSON using new VariantLoadJSON(),
    VariantSaveJSON() functions and TTextWriter.AddVariantJSON() method
  - handle variant serialization in/from our binary custom format, using new
    VariantLoad(), VariantSaveLength() and VariantSave() functions
  - added VariantToUTF8() overloaded functions for fast conversion
  - added VariantToInteger()/VariantToIntegerDef() functions for direct process
    of numerical variants (e.g. array indexes)
................................................................................
  - confusing-named RoundTo2Digits() function renamed into Trunc2ToDigit()
  - added simple, non banker rounding SimpleRoundTo2Digits() function
  - fixed potential comparison error in TSynTableFieldProperties.SortCompare()
    when sorting UTF8 Field with tfoCaseInsensitive in Options
  - speedup of QuotedStr() function and TDynArrayHashed hashing process
  - several speedup in GetJSONField() and JSON parsing: it will now expect true,
    false or null to be in lowercase only (as in json.org specifications)
  - fixed function GetJSONField() to properly decode JSON number with exponent
  - added function GetJSONFieldOrObjectOrArray() in unit's interface section  
  - function GotoNextJSONField() renamed GotoNextJSONItem(), and fixed to
    handle nested JSON array or objects in addition to string/numbers
  - added function JSONRetrieveIDField() for fast retrieval of a "ID":.. value
  - added function JSONRetrieveStringField() for retrieval of a string field
    name or value from JSON buffer
  - added TextColor() and TextBackground() functions - will initialize internal
    console process after any manual AllocConsole call
................................................................................
// - any integer value is left as its ascii representation
// - wasString is set to true if the JSON value was a "string"
// - works for both field names or values (e.g. '"FieldName":' or 'Value,')
// - EndOfObject (if not nil) is set to the JSON value char (',' ':' or '}' e.g.)
function GetJSONField(P: PUTF8Char; out PDest: PUTF8Char;
  wasString: PBoolean=nil; EndOfObject: PUTF8Char=nil): PUTF8Char;

/// decode a JSON content in an UTF-8 encoded buffer
// - GetJSONField() will only handle JSON "strings" or numbers - if
// HandleValuesAsObjectOrArray is TRUE, this function will process JSON {
// objects } or [ arrays ] and add a #0 at the end of it
// - this function decodes in the P^ buffer memory itself (no memory allocation
// or copy), for faster process - so take care that it is an unique string
// - PDest points to the next field to be decoded, or nil on any unexpected end
// - wasString is set to true if the JSON value was a "string"
// - EndOfObject (if not nil) is set to the JSON value char (',' ':' or '}' e.g.)
function GetJSONFieldOrObjectOrArray(var P: PUTF8Char; wasString: PBoolean=nil;
  EndOfObject: PUTF8Char=nil; HandleValuesAsObjectOrArray: Boolean=false): PUTF8Char;

/// test if the supplied buffer is a "string" value or a numerical value
// (floating point or integer), according to the characters within
// - this version will recognize null/false/true as strings
// - e.g. IsString('0')=false, IsString('abc')=true, IsString('null')=true
function IsString(P: PUTF8Char): boolean;

/// test if the supplied buffer is a "string" value or a numerical value
................................................................................

{/ delete the window resources used to receive GDI messages
  - must be called for each CreateInternalWindow() function
  - both parameter values are then reset to ''/0 }
function ReleaseInternalWindow(var aWindowName: string; var aWindow: HWND): boolean;

type
  /// small memory buffer used to backup a RedirectCode() redirection hook
  TPatchCode = array[0..4] of byte;
  /// pointer to a small memory buffer used to backup a RedirectCode() hook
  PPatchCode = ^TPatchCode;

/// self-modifying code - change some memory buffer in the code segment
// - if Backup is not nil, it should point to a Size array of bytes, ready
// to contain the overriden code buffer, for further hook disabling
procedure PatchCode(Old,New: pointer; Size: integer; Backup: pointer=nil;
  LeaveUnprotected: boolean=false);

/// self-modifying code - change one PtrUInt in the code segment
procedure PatchCodePtrUInt(Code: PPtrUInt; Value: PtrUInt;
  LeaveUnprotected: boolean=false);

/// self-modifying code - add an asm JUMP to a redirected function
// - if Backup is not nil, it should point to a TPatchCode buffer, ready
// to contain the overriden code buffer, for further hook disabling
procedure RedirectCode(Func, RedirectFunc: Pointer; Backup: PPatchCode=nil);

/// self-modifying code - restore a code from its RedirectCode() backup
procedure RedirectCodeRestore(Func: pointer; const Backup: TPatchCode);

{$else}

/// compatibility function, to be implemented according to the running OS
// - expect more or less the same result as the homonymous Win32 API function
function GetTickCount: Cardinal;
................................................................................
asm
  bswap eax
end;
{$endif}
{$endif}

{$ifndef FPC}

function SystemFillCharAddress: Pointer;
asm
  {$ifdef CPU64}
  mov rax,offset System.@FillChar
  {$else}
  mov eax,offset System.@FillChar
  {$endif}
end;


{$ifdef CPU64}
{$ifndef NOX64PATCHRTL}

{ Some notes about MOVNTI opcode use below:
  - Delphi inline assembler is not able to compile the instruction -> so we
    had to write some manual DB $... values instead :(
................................................................................
asm // same params than _CopyRecord{ dest, source, typeInfo: Pointer }
{$ifdef CPUX64}
  .NOFRAME
{$endif}
  jmp System.@CopyRecord
end;
{$else PUREPASCAL}


function SystemRecordCopyAddress: Pointer;
asm
  {$ifdef CPU64}
  mov rax,offset System.@CopyRecord
  {$else}
  mov eax,offset System.@CopyRecord
  {$endif}
end;

procedure RecordCopy(var Dest; const Source; TypeInfo: pointer);
asm  // faster version of _CopyRecord{dest, source, typeInfo: Pointer} by AB
        { ->    EAX pointer to dest             }
        {       EDX pointer to source           }
        {       ECX pointer to typeInfo         }
................................................................................
end;
{$endif PUREPASCAL}
{$endif FPC}
{$endif ENHANCEDRTL}
{$endif DELPHI5OROLDER}
{$endif USEPACKAGES}














{ ************  Custom record / dynamic array JSON serialization }

type
  /// information about one customized JSON serialization
  TJSONCustomParserRegistration = record
    RecordTypeName: RawUTF8;
................................................................................
      exit;
    end;
    end;
  end;
  result := Source;
end;























function VariantLoadJSON(var Value: variant; JSON: PUTF8Char;
  ForceStringAs: TReturnedStringType;
  EndOfObject: PUTF8Char; HandleObjectArray: PDocVariantOptions): PUTF8Char;
var wasString: boolean;
    Val: PUTF8Char;
begin
  result := JSON;
................................................................................

var
  LastDispInvokeType: TSynInvokeableVariantType;

procedure SynVarDispProc(Result: PVarData; const Instance: TVarData;
      CallDesc: PCallDesc; Params: Pointer); cdecl;
const DO_PROP = 1; GET_PROP = 2; SET_PROP = 4;

var Value: TVarData;
    Handler: TSynInvokeableVariantType;
    CacheDispInvokeType: TSynInvokeableVariantType; // to be thread-safe
begin
  if Instance.VType=varByRef or varVariant then // handle By Ref variants
    SynVarDispProc(Result,PVarData(Instance.VPointer)^,CallDesc,Params) else begin
    if Result<>nil then
      VarClear(Variant(Result^));
    case Instance.VType of
    varDispatch, varDispatch or varByRef,
    varUnknown, varUnknown or varByRef, varAny:
      // process Ole Automation variants
................................................................................
        CacheDispInvokeType := LastDispInvokeType; 
        if (CacheDispInvokeType<>nil) and
           (CacheDispInvokeType.VarType=TVarData(Instance).VType) and
           (CallDesc^.CallType in [GET_PROP, DO_PROP]) and
           (Result<>nil) and (CallDesc^.ArgCount=0) then begin
          CacheDispInvokeType.IntGet(Result^,Instance,@CallDesc^.ArgTypes[0]);
          exit;
        end;
      end;
      // handle any custom variant type
      if FindCustomVariantType(Instance.VType,TCustomVariantType(Handler)) then begin
        if Handler.InheritsFrom(TSynInvokeableVariantType) then

          case CallDesc^.CallType of
          GET_PROP, DO_PROP: // fast direct call of our IntGet() virtual method
            if (Result<>nil) and (CallDesc^.ArgCount=0) then begin
              Handler.IntGet(Result^,Instance,@CallDesc^.ArgTypes[0]);
              LastDispInvokeType := Handler; // speed up in loop
              exit;
            end;
          SET_PROP: // fast direct call of our IntSet() virtual method
            if (Result=nil) and (CallDesc^.ArgCount=1) then begin
              ParseParamPointer(@Params,CallDesc^.ArgTypes[0],Value);
              Handler.IntSet(Instance,Value,@CallDesc^.ArgTypes[1]);
              exit;
            end;
          end;

        // here we call the default code handling custom types


        Handler.DispInvoke({$ifdef DELPHI6OROLDER}Result^{$else}Result{$endif},
          Instance,CallDesc,@Params)

      end else
        raise EInvalidOp.CreateFmt('Invalid variant type %d invoke',[Instance.VType]);
    end;
    end;
  end;
end;

function VariantsDispInvokeAddress: pointer;
asm
  {$ifdef CPU64}
  mov rax,offset Variants.@DispInvoke
  {$else}
  mov eax,offset Variants.@DispInvoke
  {$endif}
end;

{$endif FPC}
{$endif ISDELPHIXE2}

function SynRegisterCustomVariantType(aClass: TSynInvokeableVariantTypeClass): TSynInvokeableVariantType;
{$ifdef DELPHI6OROLDER}
................................................................................
{$ifdef DELPHI6OROLDER}
    GetVariantManager(VarMgr);
    VarMgr.DispInvoke := @SynVarDispProc;
    SetVariantManager(VarMgr);
{$else}
  {$ifndef FPC}
  {$ifndef ISDELPHIXE2} // Delphi XE2 just does not like our performance trick :(
    RedirectCode(VariantsDispInvokeAddress,@SynVarDispProc);
  {$endif}
  {$endif}
{$endif}
    GarbageCollectorFreeAndNil(SynVariantTypes,TObjectList.Create);
  end;
  result :=  aClass.Create; // register variant type
  SynVariantTypes.Add(result);
................................................................................
  if EndOfObject<>nil then
    EndOfObject^ := P^;
  P^ := #0; // make zero-terminated
  PDest := @P[1];
  if P[1]=#0 then
    PDest := nil;
end;

function GetJSONFieldOrObjectOrArray(var P: PUTF8Char; wasString: PBoolean=nil;
  EndOfObject: PUTF8Char=nil; HandleValuesAsObjectOrArray: Boolean=false): PUTF8Char;
var Value: PUTF8Char;
begin
  result := nil;
  while ord(P^) in [1..32] do inc(P);
  if HandleValuesAsObjectOrArray and (P^ in ['{','[']) then begin
    Value := P;
    P := GotoNextJSONObjectOrArray(P);
    if P=nil then
      exit; // invalid content
    if wasString<>nil then
      wasString^ := false; // was object or array
    if EndOfObject<>nil then
     EndOfObject^ := P^;
    P^ := #0; // so Values[] will be a valid ASCIIZ string
    inc(P);
    result := Value;
  end else
    result := GetJSONField(P,P,wasString,EndOfObject);
end;

function IsString(P: PUTF8Char): boolean;  // test if P^ is a "string" value
begin
  if P=nil then begin
    result := false;
    exit;
  end;
................................................................................
{$endif PUREPASCAL}
{$else}
begin
{$endif DELPHI5OROLDER}
  {$ifndef FPC}
   {$ifdef CPU64}
   {$ifndef NOX64PATCHRTL}
   RedirectCode(SystemFillCharAddress,@FillChar);
   RedirectCode(@System.Move,@Move);
   {$endif NOX64PATCHRTL}
   {$endif CPU64}
   {$ifndef ENHANCEDRTL}
    {$ifndef PUREPASCAL}
     {$ifndef DELPHI5OROLDER}
      {$ifndef USEPACKAGES}
       {$ifdef DOPATCHTRTL}
  RedirectCode(SystemRecordCopyAddress,@RecordCopy);
       {$endif DOPATCHTRTL}
      {$endif USEPACKAGES}
     {$endif DELPHI5OROLDER}
     {$ifndef LVCL}
      {$ifndef DELPHI5OROLDER}
  if not SupportsSSE2 then // back to default X87 code for older CPUs
    PatchCode(@FillChar,@FillCharX87,FILLCHAR_SIZE);
      {$endif DELPHI5OROLDER}
      {$ifndef ISDELPHI2007ANDUP} // use faster FillChar/Move for older Delphi
  RedirectCode(SystemFillCharAddress,@FillChar);
  RedirectCode(@System.Move,@Move);
      {$endif ISDELPHI2007ANDUP}
     {$endif LVCL}
    {$endif PUREPASCAL}
   {$endif ENHANCEDRTL}
  {$endif FPC}
end;

................................................................................
  {$ifndef NOVARIANTS}
  Assert(SizeOf(TSynTableData)=sizeof(TVarData));
  {$endif NOVARIANTS}

finalization
  GarbageCollectorFree;
end.






Changes to SynMongoDB.pas.

1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
end;


{ TBSONVariant }

function TBSONVariant.TryJSONToVariant(var JSON: PUTF8Char;
  var Value: variant; EndOfObject: PUTF8Char): boolean;
// warning: this code should NOT modify the JSON buffer in-place !
  procedure Return(kind: TBSONElementType; P: PUTF8Char; GotoEndOfObject: AnsiChar='}');
  begin
    if GotoEndOfObject<>#0 then
      while P^<>GotoEndOfObject do if P^=#0 then exit else inc(P);
    P := GotoNextNotSpace(P+1);
    if EndOfObject<>nil then
      EndOfObject^ := P^;






|







1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
end;


{ TBSONVariant }

function TBSONVariant.TryJSONToVariant(var JSON: PUTF8Char;
  var Value: variant; EndOfObject: PUTF8Char): boolean;
// warning: code should NOT modify JSON buffer in-place, unless it returns true
  procedure Return(kind: TBSONElementType; P: PUTF8Char; GotoEndOfObject: AnsiChar='}');
  begin
    if GotoEndOfObject<>#0 then
      while P^<>GotoEndOfObject do if P^=#0 then exit else inc(P);
    P := GotoNextNotSpace(P+1);
    if EndOfObject<>nil then
      EndOfObject^ := P^;