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

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

Overview
Comment:{3292} finalized binary serialization refactoring with FPC support enhanced
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 92667d06d46bad8503a40df4e7666ad7bcaf1f73
User & Date: ab 2016-12-27 09:34:54
Context
2016-12-27
10:29
{3293} ensure FPC binary serialized flow matches Delphi's check-in: 354d36be1c user: ab tags: trunk
09:34
{3292} finalized binary serialization refactoring with FPC support enhanced check-in: 92667d06d4 user: ab tags: trunk
07:38
{3291} fixed [fb6a92b7dc35] about missing "raise" check-in: 5bdf4a280c user: ab tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SynCommons.pas.

20210
20211
20212
20213
20214
20215
20216
20217
20218
20219
20220
20221
20222
20223
20224
.....
20347
20348
20349
20350
20351
20352
20353

20354
20355
20356
20357
20358
20359
20360
.....
20496
20497
20498
20499
20500
20501
20502





20503
20504
20505
20506
20507
20508
20509
.....
35353
35354
35355
35356
35357
35358
35359
35360
35361
35362
35363
35364
35365
35366
35367
35368
35369
35370
35371
35372
35373
35374
35375

35376
35377











35378
35379
35380
35381
35382
35383
35384
.....
35463
35464
35465
35466
35467
35468
35469
35470
35471
35472
35473
35474
35475
35476
35477
.....
35499
35500
35501
35502
35503
35504
35505
35506

35507
35508
35509
35510
35511
35512
35513
35514
35515
35516
35517
35518
35519
35520
35521
35522
35523
35524
35525
35526
35527

35528
35529
35530
35531
35532
35533
35534
35535
35536
35537
35538
35539
35540
.....
35549
35550
35551
35552
35553
35554
35555
35556

35557
35558
35559
35560
35561
35562
35563
.....
35578
35579
35580
35581
35582
35583
35584

35585
35586
35587
35588
35589
35590
35591
35592
35593
35594
35595
35596
35597
35598
35599
35600
35601
35602
35603
35604
35605
35606
35607
35608
35609
35610
35611
35612
35613
35614
35615
35616
35617
35618
35619
35620

35621
35622
35623
35624
35625
35626
35627
35628
35629
35630
35631
35632
35633
35634
35635
35636
35637
35638
35639
35640
35641
35642
35643
35644
35645
35646
35647
35648

35649


35650
35651
35652
35653
35654
35655
35656
35657

35658
35659
35660
35661
35662
35663
35664
35665
35666
35667
35668
.....
35688
35689
35690
35691
35692
35693
35694
35695
35696


35697
35698
35699

35700
35701
35702
35703
35704
35705
35706
35707
35708
35709
35710
35711
35712
35713
35714
35715
35716
35717
35718
35719

35720

35721
35722
35723
35724
35725
35726
35727
35728
35729
35730
35731
35732
35733
35734
35735
.....
35740
35741
35742
35743
35744
35745
35746







35747
35748
35749
35750
35751
35752
35753
35754
35755
35756
35757
35758
35759
35760
35761
35762
35763
35764
35765
35766
35767
35768
35769
35770
35771
35772
35773
35774
35775
35776
35777
35778
35779
35780
35781
35782
35783
35784
35785
35786
35787
35788
35789
35790
35791
35792
35793
35794
35795
35796
35797
35798
35799
35800
35801


35802

35803
35804
35805
35806
35807
35808
35809
35810
35811
35812
35813
35814
35815
35816
35817
35818
35819
35820
35821
35822
35823
35824
35825
35826
35827
35828
35829
35830







35831
35832
35833
35834
35835
35836
35837
35838
35839
35840
35841
35842
35843
35844
35845
35846
35847
35848
35849
35850
35851
35852
35853
35854
35855
35856
35857
35858
35859
35860
35861
.....
35973
35974
35975
35976
35977
35978
35979
35980
35981
35982
35983
35984
35985
35986
35987
35988
35989
35990
.....
35996
35997
35998
35999
36000
36001
36002







36003
36004
36005
36006
36007
36008
36009
36010
36011
36012
36013
36014
36015
36016
36017
36018
36019
36020
36021
36022
36023
36024
36025
36026
36027
36028
36029
36030
.....
42996
42997
42998
42999
43000
43001
43002
43003
43004
43005
43006
43007
43008
43009
43010
43011
43012
43013
43014
43015
43016
43017
43018
43019
43020
43021
43022
43023
43024
43025
43026
43027
43028
.....
43035
43036
43037
43038
43039
43040
43041
43042
43043
43044
43045
43046
43047
43048
43049
43050
43051
43052

43053
43054
43055
43056
43057
43058
43059
.....
43645
43646
43647
43648
43649
43650
43651
43652
43653
43654
43655
43656
43657
43658
43659
.....
43750
43751
43752
43753
43754
43755
43756
43757
43758
43759
43760
43761
43762
43763
43764
.....
44091
44092
44093
44094
44095
44096
44097
44098
44099
44100
44101
44102
44103
44104
44105
44106
44107
44108
44109
44110
44111
44112
44113
44114
44115
44116
44117
44118

44119
44120
44121
44122
44123
44124
44125
.....
44275
44276
44277
44278
44279
44280
44281
44282
44283
44284
44285
44286
44287
44288
44289
.....
44300
44301
44302
44303
44304
44305
44306
44307
44308
44309
44310
44311
44312
44313
44314
.....
45213
45214
45215
45216
45217
45218
45219
45220

45221
45222
45223
45224
45225
45226
45227
.....
47348
47349
47350
47351
47352
47353
47354

47355
47356
47357
47358
47359
47360
47361
    tkSet,tkMethod,tkSString,tkLStringOld,tkLString,
    tkWString,tkVariant,tkArray,tkRecord,tkInterface,
    tkClass,tkObject,tkWChar,tkBool,tkInt64,tkQWord,
    tkDynArray,tkInterfaceRaw,tkProcVar,tkUString,tkUChar,
    tkHelper,tkFile,tkClassRef,tkPointer);

const
   // all potentially managed types
   tkManagedTypes = [tkLStringOld,tkLString,tkWstring,tkUstring,tkArray,
                     tkObject,tkRecord,tkDynArray,tkInterface,tkVariant];
   // maps record or object types
   tkRecordTypes = [tkObject,tkRecord];
   tkRecordTypeOrSet = [tkObject,tkRecord];

type
................................................................................
    TypeInfo: PTypeInfoStored;
    {$ifdef FPC}
    Offset: sizeint;
    {$else}
    Offset: PtrUInt;
    {$endif FPC}
  end;

  {$ifdef ISDELPHI2010}
  /// map the Delphi record field enhanced RTTI (available since Delphi 2010)
  TEnhancedFieldInfo = packed record
    TypeInfo: PTypeInfoStored;
    Offset: PtrUInt;
    Flags: Byte;
    NameLen: byte; // = Name[0] = length(Name)
................................................................................
  // - used to calc the beginning of memory allocation of a string
  STRRECSIZE = SizeOf(TStrRec);

function ToText(k: TTypeKind): PShortString; overload;
begin
  result := GetEnumName(TypeInfo(TTypeKind),ord(k));
end;






type
  TTypeInfoSaved = type TRawByteStringDynArray;

function TypeInfoFind(const rttitypes: TTypeInfoSaved;
  const typename: RawUTF8): pointer;
var i,len: integer;
................................................................................
{ ************ low-level RTTI types and conversion routines }


{$ifdef FPC}

function RTTIManagedSize(typeInfo: Pointer): SizeInt; inline;
begin
  case PTypeKind(typeInfo)^ of
    tkLString,tkLStringOld,tkWString,tkUString,
    tkInterface,tkDynarray:
      result := sizeof(Pointer);
    {$ifndef NOVARIANTS}
    tkVariant:
      result := sizeof(TVarData);
    {$endif}
    tkArray:
      with GetTypeInfo(typeInfo,tkArray)^ do
        result := arraySize;
        //result := (arraySize and $7FFFFFFF) * ElCount; // to be validated
    tkObject,tkRecord:
      result := GetTypeInfo(typeInfo,PTypeKind(typeInfo)^)^.recSize;
  else
    raise ESynException.CreateUTF8('RTTIManagedSize(%)',[PByte(typeInfo)^]);

  end;
end;












procedure RecordClear(var Dest; TypeInfo: pointer);
  [external name 'FPC_FINALIZE'];

procedure RecordAddRef(var Data; TypeInfo : pointer);
  [external name 'FPC_ADDREF'];

................................................................................
var i: integer;
    itemtype: PTypeInfo;
{$ifndef DELPHI5OROLDER} // do not know why Delphi 5 compiler does not like it
   DynA, DynB: TDynArray;
{$endif}
begin // info is expected to come from a DeRef() if retrieved from RTTI
  result := 0; // A^<>B^
  case info^.Kind of
  tkLString{$ifdef FPC},tkLStringOld{$endif}:
    if PAnsiString(A)^=PAnsiString(B)^ then
      result := sizeof(pointer);
  tkWString:
    if PWideString(A)^=PWideString(B)^ then
      result := sizeof(pointer);
  {$ifdef HASVARUSTRING}
................................................................................
    if PPointer(A)^=PPointer(B)^ then
      result := sizeof(pointer);
  tkArray: begin
    info := GetTypeInfo(info,tkArray);
    if (info=nil) or (info^.dimCount<>1) then
      result := -1 else begin
      itemtype := DeRef(info^.arrayType);
      if itemtype=nil then

        if CompareMem(A,B,info^.arraySize) then
          result := info^.arraySize else
          result := 0 else begin
        for i := 1 to info^.elCount do begin
          result := ManagedTypeCompare(A,B,itemtype);
          if result<=0 then
            exit;
          inc(A,result);
          inc(B,result);
        end;
        result := info^.arraySize;
      end;
    end;
  end;
  else
    result := -1;
  end;
end;

function ManagedTypeSaveLength(data: PAnsiChar; info: PTypeInfo;
  out len: integer): integer;

var DynArray: TDynArray;
    itemtype: PTypeInfo;
    itemsize,size,i: integer;
    P: PPtrUInt absolute data;
begin
  case info^.Kind of
  tkLString,tkWString{$ifdef FPC},tkLStringOld{$endif}: begin
    len := sizeof(pointer); // length stored within WideString is in bytes
    if P^=0 then
      result := 1 else
      result := ToVarUInt32LengthWithData(PStrRec(Pointer(P^-STRRECSIZE))^.length);
  end;
  {$ifdef HASVARUSTRING}
................................................................................
    result := RecordSaveLength(data^,info,@len);
  tkArray: begin
    info := GetTypeInfo(info,tkArray);
    if (info=nil) or (info^.dimCount<>1) then
      result := 0 else begin
      len := info^.arraySize;
      itemtype := DeRef(info^.arrayType);
      if itemtype=nil then

        result := len else begin
        size := 0;
        for i := 1 to info^.elCount do begin
          inc(size,ManagedTypeSaveLength(data,itemtype,itemsize));
          inc(data,itemsize);
        end;
        result := size;
................................................................................
  else
    result := 0; // invalid/unhandled record content
  end;
end;

function ManagedTypeSave(data, dest: PAnsiChar; info: PTypeInfo;
  out len: integer): PAnsiChar;

var DynArray: TDynArray;
    itemtype: PTypeInfo;
    itemsize,i: integer;
    P: PPtrUInt absolute data;
begin
  case info^.Kind of
    tkDynArray: begin
      DynArray.Init(info,data^);
      result := DynArray.SaveTo(dest);
      len := sizeof(PtrUInt); // size of tkDynArray in record
    end;
    tkLString, tkWString {$ifdef HASVARUSTRING}, tkUString{$endif}
    {$ifdef FPC}, tkLStringOld{$endif}: begin
      if P^=0 then
        itemsize := 0 else
        itemsize := PStrRec(Pointer(P^-STRRECSIZE))^.length;
      {$ifdef HASVARUSTRING} // WideString has length in bytes, UnicodeString in WideChars
      if info^.Kind=tkUString then
        itemsize := itemsize*2;
      {$endif}
      result := pointer(ToVarUInt32(itemsize,pointer(dest)));
      if itemsize>0 then begin
        MoveFast(pointer(P^)^,result^,itemsize);
        inc(result,itemsize);
      end;
      len := sizeof(PtrUInt); // size of tkLString+tkWString+tkUString in record
    end;
    tkRecord{$ifdef FPC},tkObject{$endif}:
      result := RecordSave(data^,dest,info,len);
    tkArray: begin
      info := GetTypeInfo(info,tkArray);
      if (info=nil) or (info^.dimCount<>1) then
        result := nil else begin // supports single dimension static array only
        len := info^.arraySize;
        itemtype := DeRef(info^.arrayType);
        if itemtype=nil then begin

          MoveFast(data^,dest^,len);
          result := dest+len;
        end else begin
          for i := 1 to info^.elCount do begin
            dest := ManagedTypeSave(data,dest,itemtype,itemsize);
            if dest=nil then
              break; // invalid/unhandled content
            {$ifdef FPC}
            if itemsize=-1 then begin
              result := nil;
              exit;
            end;
            {$endif}
            inc(data,itemsize)
          end;
          result := dest;
        end;
      end;
    end;
    {$ifndef NOVARIANTS}
    tkVariant: begin
      result := VariantSave(PVariant(data)^,dest);
      len := sizeof(Variant); // size of tkVariant in record
    end;
    {$endif}
    else begin
      {$ifdef FPC}
      len := -1; // FPC generates RTTI for such unmanaged fields

      result := dest;


      {$else}
      result := nil;
      {$endif}
    end;
    end;
end;

function ManagedTypeLoad(data: PAnsiChar; var source: PAnsiChar; info: PTypeInfo): integer;

var DynArray: TDynArray;
    itemtype: PTypeInfo;
    itemsize,i: integer;
begin
  case info^.Kind of
  tkDynArray: begin
    DynArray.Init(info,data^);
    source := DynArray.LoadFrom(source);
    result := sizeof(PtrUInt); // size of tkDynArray in record
  end;
  tkLString, tkWString {$ifdef HASVARUSTRING}, tkUString{$endif}
................................................................................
    inc(source,itemsize);
    result := sizeof(PtrUInt); // size of tkLString+tkWString+tkUString in record
  end;
  tkRecord{$ifdef FPC},tkObject{$endif}:
    source := RecordLoad(data^,source,info,@result);
  tkArray: begin
    info := GetTypeInfo(info,tkArray);
    if (info=nil) or (info^.dimCount<>1) then
      result := 0 else begin // supports single dimension static array only


      result := info^.arraySize;
      itemtype := DeRef(info^.arrayType);
      if itemtype=nil then begin

        MoveFast(source^,data^,result);
        inc(source,result);
      end else
        for i := 1 to info^.elCount do begin
          itemsize := ManagedTypeLoad(data,source,itemtype);
          if itemsize<=0 then begin
            result := 0;
            exit;
          end else
            inc(data,itemsize);
        end;
    end;
  end;
  {$ifndef NOVARIANTS}
  tkVariant: begin
    source := VariantLoad(PVariant(data)^,source,@JSON_OPTIONS[true]);
    result := sizeof(Variant); // size of tkVariant in record
  end;
  {$endif}
  else

    result := -1;

  end;
end;

function RecordEquals(const RecA, RecB; TypeInfo: pointer;
  PRecSize: PInteger): boolean;
var info: PTypeInfo;
    F: integer;
    Field: ^TFieldInfo;
    Diff: cardinal;
    A, B: PAnsiChar;
begin
  A := @RecA;
  B := @RecB;
  result := false;
  info := GetTypeInfo(TypeInfo,tkRecordTypeOrSet);
................................................................................
  if A=B then begin // both nil or same pointer
    result := true;
    exit;
  end;
  Field := @info^.ManagedFields[0];
  Diff := 0;
  for F := 1 to info^.ManagedCount do begin







    Diff := Field^.Offset-Diff;
    if Diff<>0 then begin
      if not CompareMem(A,B,Diff) then
        exit; // binary block not equal
      inc(A,Diff);
      inc(B,Diff);
    end;
    Diff := ManagedTypeCompare(A,B,DeRef(Field^.TypeInfo));
    if integer(Diff)<=0 then
      if Diff=0 then // A^<>B^
        exit else    // Diff=-1 for unexpected type
      {$ifdef FPC} // FPC does include RTTI for unmanaged fields! :)
        if Field^.TypeInfo^.Kind in tkManagedTypes then
          raise ESynException.CreateUTF8('RecordEquals: % is managed',
            [ToText(Field^.TypeInfo^.Kind)^]) else begin
          if F=info^.ManagedCount then
            Diff := info^.recSize-Field^.Offset else
            Diff := info^.ManagedFields[F].Offset-Field^.Offset;
          if not CompareMem(A,B,Diff) then
            exit; // binary block not equal
        end;
      {$else}
      raise ESynException.CreateUTF8('RecordEquals: % not supported',
        [ToText(Field^.TypeInfo^.Kind)^]);
      {$endif}
    inc(A,Diff);
    inc(B,Diff);
    inc(Diff,Field^.Offset);
    inc(Field);
  end;
  if CompareMem(A,B,info^.recSize-Diff) then
    result := true;
end;

function RecordSaveLength(const Rec; TypeInfo: pointer; Len: PInteger): integer;
var info: PTypeInfo;
    F, recsize,saved: integer;
    Field: ^TFieldInfo;
    R: PAnsiChar;
begin
  R := @Rec;
  info := GetTypeInfo(TypeInfo,tkRecordTypeOrSet);
  if (R=nil) or (info=nil) then begin
    result := 0; // should have been checked before
    exit;
  end;
  Field := @info^.ManagedFields[0];
  result := info^.recSize;
  if Len<>nil then
    Len^ := result;
  for F := 1 to info^.ManagedCount do begin
    saved := ManagedTypeSaveLength(R+Field^.Offset,Deref(Field^.TypeInfo),recsize);
    {$ifdef FPC}
    if saved>0 then // FPC has RTTI for unmanaged fields -> ignore
      inc(result,saved-recsize);


    {$else}

    if saved=0 then begin
      result := 0; // invalid type
      exit;
    end;
    inc(result,saved-recsize);
    {$endif}
    inc(Field);
  end;
end;

function RecordSave(const Rec; Dest: PAnsiChar; TypeInfo: pointer;
  out Len: integer): PAnsiChar; 
var info: PTypeInfo;
    F: integer;
    Diff: cardinal;
    Field: ^TFieldInfo;
    R: PAnsiChar;
begin
  R := @Rec;
  info := GetTypeInfo(TypeInfo,tkRecordTypeOrSet);
  if (R=nil) or (info=nil) then begin
    result := nil; // should have been checked before
    exit;
  end;
  Len := info^.recSize;
  Field := @info^.ManagedFields[0];
  Diff := 0;
  for F := 1 to info^.ManagedCount do begin







    Diff := Field^.Offset-Diff;
    if Diff<>0 then begin
      MoveFast(R^,Dest^,Diff);
      inc(R,Diff);
      inc(Dest,Diff);
    end;
    Dest := ManagedTypeSave(R,Dest,Deref(Field^.TypeInfo),integer(Diff));
    if Dest=nil then begin
      result := nil; // invalid/unhandled record content
      exit;
    end;
    {$ifdef FPC} // FPC does include RTTI for unmanaged fields! :)
    if integer(Diff)=-1 then begin
      if info^.Kind in tkManagedTypes then
        raise ESynException.CreateUTF8('ManagedTypeSave: % not supported',
          [ToText(info^.Kind)^]) else begin
        if F=info^.ManagedCount then
          Diff := info^.recSize-Field^.Offset else
          Diff := info^.ManagedFields[F].Offset-Field^.Offset;
        MoveFast(R^,Dest^,Diff);
        inc(Dest,Diff);
      end;
    end;
    {$endif}
    inc(R,Diff);
    inc(Diff,Field.Offset);
    inc(Field);
  end;
  Diff := info^.recSize-Diff;
  if integer(Diff)<0 then
    raise ESynException.Create('RecordSave diff<0') else
................................................................................
        jmp     System.@FinalizeArray
{$endif CPU64}
end;
{$endif FPC}

function RecordLoad(var Rec; Source: PAnsiChar; TypeInfo: pointer;
  Len: PInteger): PAnsiChar;
var info: PTypeInfo;
    F: integer;
    Diff: cardinal;
    Field: ^TFieldInfo;
    R: PAnsiChar;
begin
  result := nil; // indicates error
  R := @Rec;
  info := GetTypeInfo(TypeInfo,tkRecordTypeOrSet);
  if (R=nil) or (info=nil) then // should have been checked before
    exit;
................................................................................
      _Finalize(R+Field^.Offset,Deref(Field^.TypeInfo));
      inc(Field);
    end;
    exit;
  end;
  Diff := 0;
  for F := 1 to info^.ManagedCount do begin







    Diff := Field^.Offset-Diff;
    if Diff<>0 then begin
      MoveFast(Source^,R^,Diff);
      inc(Source,Diff);
      inc(R,Diff);
    end;
    Diff := ManagedTypeLoad(R,Source,DeRef(Field^.TypeInfo));
    {$ifdef FPC} // FPC does include RTTI for unmanaged fields! :)
    if integer(Diff)<0 then begin
      if Field^.TypeInfo^.Kind in tkManagedTypes then
        raise ESynException.CreateUTF8('RecordLoad: % is not supported',
          [ToText(Field^.TypeInfo^.Kind)^]) else begin
        if F=info^.ManagedCount then
          Diff := info^.recSize-Field^.Offset else
          Diff := info^.ManagedFields[F].Offset-Field^.Offset;
        MoveFast(Source^,R^,Diff);
        inc(Source,Diff);
      end;
    end else
    {$endif}
    if Diff=0 then
      exit; // error at loading
    inc(R,Diff);
    inc(Diff,Field^.Offset);
    inc(Field);
  end;
  Diff := info^.recSize-Diff;
  if integer(Diff)<0 then
................................................................................
    result := Dest;
    exit;
  end;
  inc(Dest,sizeof(Cardinal)); // leave space for Hash32 checksum
  result := Dest;
  // store dynamic array elements content
  P := fValue^;
  if ElemType=nil then
    if GetIsObjArray then
      raise ESynException.CreateUTF8('TDynArray.SaveTo(%) is a T*ObjArray',
        [PShortString(@PTypeInfo(ArrayType).NameLen)^]) else begin
      // binary types: store as once
      n := n*integer(ElemSize);
      MoveFast(P^,Dest^,n);
      inc(Dest,n);
    end else
    for i := 1 to n do begin
      Dest := ManagedTypeSave(P,Dest,ElemType,LenBytes);
      if Dest=nil then
        break;
      {$ifdef FPC}
      if LenBytes=-1 then begin
        result := nil;
        exit;
      end;
      {$endif}
      inc(P,LenBytes);
    end;
  // store Hash32 checksum
  if Dest<>nil then  // may be nil if RecordSave() failed
    PCardinal(result-sizeof(Cardinal))^ := Hash32(result,Dest-result);
  result := Dest;
end;
................................................................................
    result := 0;
    exit; // avoid GPF if void
  end;
  n := Count;
  result := ToVarUInt32Length(ElemSize)+ToVarUInt32Length(n)+1;
  if n=0 then
    exit;
  if ElemType=nil then
    if GetIsObjArray then
      raise ESynException.CreateUTF8('TDynArray.SaveToLength(%) is a T*ObjArray',
        [PShortString(@PTypeInfo(ArrayType).NameLen)^]) else
      inc(result,integer(ElemSize)*n) else begin
    P := fValue^;
    for i := 1 to n do begin
      L := ManagedTypeSaveLength(P,ElemType,size);
      assert(size=integer(ElemSize));
      if L=0 then
        break; // invalid record type (wrong field type)

      inc(result,L);
      inc(P,size);
    end;
  end;
  inc(result,sizeof(Cardinal)); // Hash32 checksum
end;

................................................................................
        djRawByteString:
          if not Base64MagicCheckAndDecode(Val,ValLen,PRawByteStringArray(fValue^)^[i]) then
            SetString(RawUTF8(PPointerArray(fValue^)^[i]),Val,ValLen);
        djWinAnsi:  WinAnsiConvert.UTF8BufferToAnsi(Val,ValLen,PRawByteStringArray(fValue^)^[i]);
        djString:   UTF8DecodeToString(Val,ValLen,string(PPointerArray(fValue^)^[i]));
        djWideString: UTF8ToWideString(Val,ValLen,WideString(PPointerArray(fValue^)^[i]));
        djSynUnicode: UTF8ToSynUnicode(Val,ValLen,SynUnicode(PPointerArray(fValue^)^[i]));
        djInterface: raise ESynException.Create('djInterface not readable');
        end;
      end;
    end;
    end;
  end;
  if aEndOfObject<>nil then
    aEndOfObject^ := EndOfObject;
................................................................................
    exit;
  end;
  // retrieve security checksum
  Hash := pointer(Source);
  inc(Source,sizeof(cardinal));
  // retrieve dynamic array elements content
  P := fValue^;
  if ElemType=nil then
    if GetIsObjArray then
      raise ESynException.CreateUTF8('TDynArray.LoadFrom(%) is a T*ObjArray',
        [PShortString(@PTypeInfo(ArrayType).NameLen)^]) else begin
      // binary type was stored directly
      n := n*integer(ElemSize);
      MoveFast(Source^,P^,n);
      inc(Source,n);
................................................................................
    result := fCompare(A,B)=0 else
    if ElemType=nil then
      case ElemSize of // optimized versions for arrays of common types
        1: result := byte(A)=byte(B);
        2: result := word(A)=word(B);
        4: result := cardinal(A)=cardinal(B);
        8: result := Int64(A)=Int64(B);
      else result := CompareMem(@A,@B,ElemSize); // generic comparison
      end else
    case PTypeKind(ElemType)^ of
    tkRecord{$ifdef FPC},tkObject{$endif}:
      result := RecordEquals(A,B,ElemType);
    tkLString{$ifdef FPC},tkLStringOld{$endif}:
      result := AnsiString(A)=AnsiString(B);
    tkWString:
      result := WideString(A)=WideString(B);
    {$ifdef HASVARUSTRING}
    tkUString:
      result := UnicodeString(A)=UnicodeString(B);
    {$endif}
    tkInterface:
      result := pointer(A)=pointer(B);
    {$ifndef NOVARIANTS}
    tkVariant:
      result := Variant(A)=Variant(B);
    {$endif}
    else result := false;
    end;

end;

{$ifndef DELPHI5OROLDER} // do not know why Delphi 5 compiler does not like it
function TDynArray.Equals(const B: TDynArray): boolean;
var i, n: integer;
    P1,P2: PAnsiChar;
    A1: PPointerArray absolute P1;
................................................................................
  fElemType := PTypeInfo(aTypeInfo)^.elType;
  if fElemType<>nil then begin
    {$ifndef HASDIRECTTYPEINFO}
    // FPC compatibility: if you have a GPF here at startup, your 3.1 trunk
    // revision seems older than June 2016
    // -> enable HASDIRECTTYPEINFO conditional below $ifdef VER3_1 in Synopse.inc
    // or in your project's options
    fElemType := PPointer(fElemType)^;
    {$endif}
    {$ifdef FPC}
    if not (PTypeKind(fElemType)^ in tkManagedTypes) then
      fElemType := nil; // as with Delphi
    {$endif}
  end;
  fCountP := aCountPointer;
................................................................................
  aCountPointer: PInteger=nil; aCaseInsensitive: boolean=false);
var Comp: TDynArraySortCompare;
begin
  Init(aTypeInfo,aValue,aCountPointer);
  Comp := DYNARRAY_SORTFIRSTFIELD[aCaseInsensitive,aKind];
  if @Comp=nil then
    raise ESynException.CreateUTF8('TDynArray.InitSpecific(%) wrong aKind=%',
      [PShortString(@PTypeInfo(aTypeInfo)^.NameLen)^,ord(aKind)]);
  fCompare := Comp;
  fKnownType := aKind;
  fKnownSize := KNOWNTYPE_SIZE[aKind];
end;

procedure TDynArray.UseExternalCount(var aCountPointer: Integer);
begin
................................................................................
  aKind: TDynArrayKind; aCountPointer: PInteger=nil; aCaseInsensitive: boolean=false);
var Comp: TDynArraySortCompare;
    Hasher: TDynArrayHashOne;
begin
  Comp := DYNARRAY_SORTFIRSTFIELD[aCaseInsensitive,aKind];
  Hasher := DYNARRAY_HASHFIRSTFIELD[aCaseInsensitive,aKind];
  if (@Hasher=nil) or (@Comp=nil) then
    raise ESynException.Create('TDynArrayHashed.InitSpecific wrong aKind');

  Init(aTypeInfo,aValue,Hasher,Comp,nil,aCountPointer,aCaseInsensitive);
  {$ifdef UNDIRECTDYNARRAY}with InternalDynArray do{$endif} begin
    fKnownType := aKind;
    fKnownSize := KNOWNTYPE_SIZE[aKind];
  end;
end;

................................................................................
      djWord:     AddU(PWordArray(P)^[i]);
      djInteger:  Add(PIntegerArray(P)^[i]);
      djCardinal: AddU(PCardinalArray(P)^[i]);
      djSingle:   AddSingle(PSingleArray(P)^[i]);
      djInt64:    Add(PInt64Array(P)^[i]);
      djDouble:   AddDouble(PDoubleArray(P)^[i]);
      djCurrency: AddCurr64(PInt64Array(P)^[i]);

      end;
      Add(',');
    end;
    end;
  CancelLastComma;
  Add(']');
end;






|







 







>







 







>
>
>
>
>







 







|








|
<
<



|
>


>
>
>
>
>
>
>
>
>
>
>







 







|







 







|
>






|








|





>




|
|







 







|
>







 







>




|

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


>



|







 







|
|
>
>


|
>




|
|
<

<
<









|
>
|
>





|

|







 







>
>
>
>
>
>
>







|



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










|

|













|
|
|
|
>
>
|
>




|
<






|


|












>
>
>
>
>
>
>






|




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







 







|


|







 







>
>
>
>
>
>
>






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







 







|












|
<
<
<
<
<







 







|







<


>







 







|







 







|







 







|

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







 







|







 







|







 







|
>







 







>







20210
20211
20212
20213
20214
20215
20216
20217
20218
20219
20220
20221
20222
20223
20224
.....
20347
20348
20349
20350
20351
20352
20353
20354
20355
20356
20357
20358
20359
20360
20361
.....
20497
20498
20499
20500
20501
20502
20503
20504
20505
20506
20507
20508
20509
20510
20511
20512
20513
20514
20515
.....
35359
35360
35361
35362
35363
35364
35365
35366
35367
35368
35369
35370
35371
35372
35373
35374
35375


35376
35377
35378
35379
35380
35381
35382
35383
35384
35385
35386
35387
35388
35389
35390
35391
35392
35393
35394
35395
35396
35397
35398
35399
35400
.....
35479
35480
35481
35482
35483
35484
35485
35486
35487
35488
35489
35490
35491
35492
35493
.....
35515
35516
35517
35518
35519
35520
35521
35522
35523
35524
35525
35526
35527
35528
35529
35530
35531
35532
35533
35534
35535
35536
35537
35538
35539
35540
35541
35542
35543
35544
35545
35546
35547
35548
35549
35550
35551
35552
35553
35554
35555
35556
35557
35558
.....
35567
35568
35569
35570
35571
35572
35573
35574
35575
35576
35577
35578
35579
35580
35581
35582
.....
35597
35598
35599
35600
35601
35602
35603
35604
35605
35606
35607
35608
35609
35610





35611
35612
35613
35614
35615
35616
35617
35618
35619
35620
35621
35622
35623
35624
35625
35626
35627
35628
35629
35630
35631
35632
35633
35634
35635
35636
35637
35638
35639
35640
35641
35642
35643






35644
35645
35646
35647
35648
35649
35650
35651
35652
35653
35654
35655
35656


35657
35658
35659
35660
35661
35662

35663
35664

35665
35666
35667
35668
35669
35670
35671
35672
35673
35674
35675
35676
35677
35678
.....
35698
35699
35700
35701
35702
35703
35704
35705
35706
35707
35708
35709
35710
35711
35712
35713
35714
35715
35716
35717
35718

35719


35720
35721
35722
35723
35724
35725
35726
35727
35728
35729
35730
35731
35732
35733
35734
35735
35736
35737
35738
35739
35740
35741
35742
35743
35744
35745
35746
35747
.....
35752
35753
35754
35755
35756
35757
35758
35759
35760
35761
35762
35763
35764
35765
35766
35767
35768
35769
35770
35771
35772
35773
35774
35775
35776


35777









35778

35779
35780
35781
35782
35783
35784
35785
35786
35787
35788
35789
35790
35791
35792
35793
35794
35795
35796
35797
35798
35799
35800
35801
35802
35803
35804
35805
35806
35807
35808
35809
35810
35811
35812
35813
35814
35815
35816
35817

35818
35819
35820
35821
35822
35823
35824
35825
35826
35827
35828
35829
35830
35831
35832
35833
35834
35835
35836
35837
35838
35839
35840
35841
35842
35843
35844
35845
35846
35847
35848
35849
35850
35851
35852
35853
35854
35855
35856
35857













35858
35859
35860
35861
35862
35863
35864
.....
35976
35977
35978
35979
35980
35981
35982
35983
35984
35985
35986
35987
35988
35989
35990
35991
35992
35993
.....
35999
36000
36001
36002
36003
36004
36005
36006
36007
36008
36009
36010
36011
36012
36013
36014
36015
36016
36017
36018
36019
36020













36021
36022
36023
36024
36025
36026
36027
.....
42993
42994
42995
42996
42997
42998
42999
43000
43001
43002
43003
43004
43005
43006
43007
43008
43009
43010
43011
43012
43013





43014
43015
43016
43017
43018
43019
43020
.....
43027
43028
43029
43030
43031
43032
43033
43034
43035
43036
43037
43038
43039
43040
43041

43042
43043
43044
43045
43046
43047
43048
43049
43050
43051
.....
43637
43638
43639
43640
43641
43642
43643
43644
43645
43646
43647
43648
43649
43650
43651
.....
43742
43743
43744
43745
43746
43747
43748
43749
43750
43751
43752
43753
43754
43755
43756
.....
44083
44084
44085
44086
44087
44088
44089
44090
44091
44092

44093
















44094
44095
44096
44097
44098
44099
44100
44101
.....
44251
44252
44253
44254
44255
44256
44257
44258
44259
44260
44261
44262
44263
44264
44265
.....
44276
44277
44278
44279
44280
44281
44282
44283
44284
44285
44286
44287
44288
44289
44290
.....
45189
45190
45191
45192
45193
45194
45195
45196
45197
45198
45199
45200
45201
45202
45203
45204
.....
47325
47326
47327
47328
47329
47330
47331
47332
47333
47334
47335
47336
47337
47338
47339
    tkSet,tkMethod,tkSString,tkLStringOld,tkLString,
    tkWString,tkVariant,tkArray,tkRecord,tkInterface,
    tkClass,tkObject,tkWChar,tkBool,tkInt64,tkQWord,
    tkDynArray,tkInterfaceRaw,tkProcVar,tkUString,tkUChar,
    tkHelper,tkFile,tkClassRef,tkPointer);

const
   // all potentially managed types - should match ManagedType*() functions
   tkManagedTypes = [tkLStringOld,tkLString,tkWstring,tkUstring,tkArray,
                     tkObject,tkRecord,tkDynArray,tkInterface,tkVariant];
   // maps record or object types
   tkRecordTypes = [tkObject,tkRecord];
   tkRecordTypeOrSet = [tkObject,tkRecord];

type
................................................................................
    TypeInfo: PTypeInfoStored;
    {$ifdef FPC}
    Offset: sizeint;
    {$else}
    Offset: PtrUInt;
    {$endif FPC}
  end;
  PFieldInfo = ^TFieldInfo;
  {$ifdef ISDELPHI2010}
  /// map the Delphi record field enhanced RTTI (available since Delphi 2010)
  TEnhancedFieldInfo = packed record
    TypeInfo: PTypeInfoStored;
    Offset: PtrUInt;
    Flags: Byte;
    NameLen: byte; // = Name[0] = length(Name)
................................................................................
  // - used to calc the beginning of memory allocation of a string
  STRRECSIZE = SizeOf(TStrRec);

function ToText(k: TTypeKind): PShortString; overload;
begin
  result := GetEnumName(TypeInfo(TTypeKind),ord(k));
end;

function ToText(k: TDynArrayKind): PShortString; overload; 
begin
  result := GetEnumName(TypeInfo(TDynArrayKind),ord(k));
end;

type
  TTypeInfoSaved = type TRawByteStringDynArray;

function TypeInfoFind(const rttitypes: TTypeInfoSaved;
  const typename: RawUTF8): pointer;
var i,len: integer;
................................................................................
{ ************ low-level RTTI types and conversion routines }


{$ifdef FPC}

function RTTIManagedSize(typeInfo: Pointer): SizeInt; inline;
begin
  case PTypeKind(typeInfo)^ of // match tkManagedTypes
    tkLString,tkLStringOld,tkWString,tkUString,
    tkInterface,tkDynarray:
      result := sizeof(Pointer);
    {$ifndef NOVARIANTS}
    tkVariant:
      result := sizeof(TVarData);
    {$endif}
    tkArray:
      result := GetTypeInfo(typeInfo,tkArray)^.arraySize and $7fffffff;


    tkObject,tkRecord:
      result := GetTypeInfo(typeInfo,PTypeKind(typeInfo)^)^.recSize;
  else
    raise ESynException.CreateUTF8('RTTIManagedSize unhandled % (%)',
      [ToText(PTypeKind(typeInfo)^)^,PByte(typeInfo)^]);
  end;
end;

function RTTIUnmanagedFieldSize(Index: integer; Field: PFieldInfo;
  Info: PTypeInfo): integer; inline;
begin
  if Info^.Kind in tkManagedTypes then
    raise ESynException.CreateUTF8('RTTIUnmanagedFieldSize: % not supported',
      [ToText(Info^.Kind)^]);
  if Index=Info^.ManagedCount then
    result := Info^.recSize-Field^.Offset else
    result := Info^.ManagedFields[Index].Offset-Field^.Offset;
end;

procedure RecordClear(var Dest; TypeInfo: pointer);
  [external name 'FPC_FINALIZE'];

procedure RecordAddRef(var Data; TypeInfo : pointer);
  [external name 'FPC_ADDREF'];

................................................................................
var i: integer;
    itemtype: PTypeInfo;
{$ifndef DELPHI5OROLDER} // do not know why Delphi 5 compiler does not like it
   DynA, DynB: TDynArray;
{$endif}
begin // info is expected to come from a DeRef() if retrieved from RTTI
  result := 0; // A^<>B^
  case info^.Kind of // should match tkManagedTypes
  tkLString{$ifdef FPC},tkLStringOld{$endif}:
    if PAnsiString(A)^=PAnsiString(B)^ then
      result := sizeof(pointer);
  tkWString:
    if PWideString(A)^=PWideString(B)^ then
      result := sizeof(pointer);
  {$ifdef HASVARUSTRING}
................................................................................
    if PPointer(A)^=PPointer(B)^ then
      result := sizeof(pointer);
  tkArray: begin
    info := GetTypeInfo(info,tkArray);
    if (info=nil) or (info^.dimCount<>1) then
      result := -1 else begin
      itemtype := DeRef(info^.arrayType);
      if (itemtype=nil)
        {$ifdef FPC}or not(itemtype^.Kind in tkManagedTypes){$endif} then
        if CompareMem(A,B,info^.arraySize) then
          result := info^.arraySize else
          result := 0 else begin
        for i := 1 to info^.elCount do begin
          result := ManagedTypeCompare(A,B,itemtype);
          if result<=0 then
            exit; // invalid (-1) or not equals (0)
          inc(A,result);
          inc(B,result);
        end;
        result := info^.arraySize;
      end;
    end;
  end;
  else
    result := -1; // Unhandled field
  end;
end;

function ManagedTypeSaveLength(data: PAnsiChar; info: PTypeInfo;
  out len: integer): integer;
// returns 0 on error, or saved bytes + len=data^ length
var DynArray: TDynArray;
    itemtype: PTypeInfo;
    itemsize,size,i: integer;
    P: PPtrUInt absolute data;
begin // info is expected to come from a DeRef() if retrieved from RTTI
  case info^.Kind of // should match tkManagedTypes
  tkLString,tkWString{$ifdef FPC},tkLStringOld{$endif}: begin
    len := sizeof(pointer); // length stored within WideString is in bytes
    if P^=0 then
      result := 1 else
      result := ToVarUInt32LengthWithData(PStrRec(Pointer(P^-STRRECSIZE))^.length);
  end;
  {$ifdef HASVARUSTRING}
................................................................................
    result := RecordSaveLength(data^,info,@len);
  tkArray: begin
    info := GetTypeInfo(info,tkArray);
    if (info=nil) or (info^.dimCount<>1) then
      result := 0 else begin
      len := info^.arraySize;
      itemtype := DeRef(info^.arrayType);
      if (itemtype=nil)
        {$ifdef FPC}or not(itemtype^.Kind in tkManagedTypes){$endif} then
        result := len else begin
        size := 0;
        for i := 1 to info^.elCount do begin
          inc(size,ManagedTypeSaveLength(data,itemtype,itemsize));
          inc(data,itemsize);
        end;
        result := size;
................................................................................
  else
    result := 0; // invalid/unhandled record content
  end;
end;

function ManagedTypeSave(data, dest: PAnsiChar; info: PTypeInfo;
  out len: integer): PAnsiChar;
// returns nil on error, or final dest + len=data^ length
var DynArray: TDynArray;
    itemtype: PTypeInfo;
    itemsize,i: integer;
    P: PPtrUInt absolute data;
begin // info is expected to come from a DeRef() if retrieved from RTTI
  case info^.Kind of





  tkLString, tkWString {$ifdef HASVARUSTRING}, tkUString{$endif}
  {$ifdef FPC}, tkLStringOld{$endif}: begin
    if P^=0 then
      itemsize := 0 else
      itemsize := PStrRec(Pointer(P^-STRRECSIZE))^.length;
    {$ifdef HASVARUSTRING} // WideString has length in bytes, UnicodeString in WideChars
    if info^.Kind=tkUString then
      itemsize := itemsize*2;
    {$endif}
    result := pointer(ToVarUInt32(itemsize,pointer(dest)));
    if itemsize>0 then begin
      MoveFast(pointer(P^)^,result^,itemsize);
      inc(result,itemsize);
    end;
    len := sizeof(PtrUInt); // size of tkLString+tkWString+tkUString in record
  end;
  tkRecord{$ifdef FPC},tkObject{$endif}:
    result := RecordSave(data^,dest,info,len);
  tkArray: begin
    info := GetTypeInfo(info,tkArray);
    if (info=nil) or (info^.dimCount<>1) then
      result := nil else begin // supports single dimension static array only
      len := info^.arraySize;
      itemtype := DeRef(info^.arrayType);
      if (itemtype=nil)
        {$ifdef FPC}or not(itemtype^.Kind in tkManagedTypes){$endif} then begin
        MoveFast(data^,dest^,len);
        result := dest+len;
      end else begin
        for i := 1 to info^.elCount do begin
          dest := ManagedTypeSave(data,dest,itemtype,itemsize);
          if dest=nil then
            break; // invalid/unhandled content






          inc(data,itemsize)
        end;
        result := dest;
      end;
    end;
  end;
  {$ifndef NOVARIANTS}
  tkVariant: begin
    result := VariantSave(PVariant(data)^,dest);
    len := sizeof(Variant); // size of tkVariant in record
  end;
  {$endif}
  tkDynArray: begin


    DynArray.Init(info,data^);
    result := DynArray.SaveTo(dest);
    len := sizeof(PtrUInt); // size of tkDynArray in record
  end;
  else
    result := nil; // invalid/unhandled record content

  end;
end;


function ManagedTypeLoad(data: PAnsiChar; var source: PAnsiChar; info: PTypeInfo): integer;
// returns source=nil on error, or final source + result=data^ length 
var DynArray: TDynArray;
    itemtype: PTypeInfo;
    itemsize,i: integer;
begin // info is expected to come from a DeRef() if retrieved from RTTI
  case info^.Kind of
  tkDynArray: begin
    DynArray.Init(info,data^);
    source := DynArray.LoadFrom(source);
    result := sizeof(PtrUInt); // size of tkDynArray in record
  end;
  tkLString, tkWString {$ifdef HASVARUSTRING}, tkUString{$endif}
................................................................................
    inc(source,itemsize);
    result := sizeof(PtrUInt); // size of tkLString+tkWString+tkUString in record
  end;
  tkRecord{$ifdef FPC},tkObject{$endif}:
    source := RecordLoad(data^,source,info,@result);
  tkArray: begin
    info := GetTypeInfo(info,tkArray);
    if (info=nil) or (info^.dimCount<>1) then begin
      source := nil; // supports single dimension static array only
      result := 0;
    end else begin
      result := info^.arraySize;
      itemtype := DeRef(info^.arrayType);
      if (itemtype=nil)
        {$ifdef FPC}or not(itemtype^.Kind in tkManagedTypes){$endif} then begin
        MoveFast(source^,data^,result);
        inc(source,result);
      end else
        for i := 1 to info^.elCount do begin
          inc(data,ManagedTypeLoad(data,source,itemtype));
          if source=nil then

            exit;


        end;
    end;
  end;
  {$ifndef NOVARIANTS}
  tkVariant: begin
    source := VariantLoad(PVariant(data)^,source,@JSON_OPTIONS[true]);
    result := sizeof(Variant); // size of tkVariant in record
  end;
  {$endif}
  else begin
    source := nil;
    result := 0;
  end; 
  end;
end;

function RecordEquals(const RecA, RecB; TypeInfo: pointer;
  PRecSize: PInteger): boolean;
var info,fieldinfo: PTypeInfo;
    F: integer;
    Field: PFieldInfo;
    Diff: cardinal;
    A, B: PAnsiChar;
begin
  A := @RecA;
  B := @RecB;
  result := false;
  info := GetTypeInfo(TypeInfo,tkRecordTypeOrSet);
................................................................................
  if A=B then begin // both nil or same pointer
    result := true;
    exit;
  end;
  Field := @info^.ManagedFields[0];
  Diff := 0;
  for F := 1 to info^.ManagedCount do begin
    fieldinfo := DeRef(Field^.TypeInfo);
    {$ifdef FPC} // FPC does include RTTI for unmanaged fields! :)
    if not (fieldinfo^.Kind in tkManagedTypes) then begin
      inc(Field);
      continue;
    end;
    {$endif};
    Diff := Field^.Offset-Diff;
    if Diff<>0 then begin
      if not CompareMem(A,B,Diff) then
        exit; // binary block not equal
      inc(A,Diff);
      inc(B,Diff);
    end;
    Diff := ManagedTypeCompare(A,B,fieldinfo);
    if integer(Diff)<=0 then
      if Diff=0 then // A^<>B^
        exit else    // Diff=-1 for unexpected type


        raise ESynException.CreateUTF8('RecordEquals: unexpected %',









          [ToText(fieldinfo^.Kind)^]);

    inc(A,Diff);
    inc(B,Diff);
    inc(Diff,Field^.Offset);
    inc(Field);
  end;
  if CompareMem(A,B,info^.recSize-Diff) then
    result := true;
end;

function RecordSaveLength(const Rec; TypeInfo: pointer; Len: PInteger): integer;
var info,fieldinfo: PTypeInfo;
    F, recsize,saved: integer;
    Field: PFieldInfo;
    R: PAnsiChar;
begin
  R := @Rec;
  info := GetTypeInfo(TypeInfo,tkRecordTypeOrSet);
  if (R=nil) or (info=nil) then begin
    result := 0; // should have been checked before
    exit;
  end;
  Field := @info^.ManagedFields[0];
  result := info^.recSize;
  if Len<>nil then
    Len^ := result;
  for F := 1 to info^.ManagedCount do begin
    fieldinfo := DeRef(Field^.TypeInfo);
    {$ifdef FPC} // FPC does include RTTI for unmanaged fields! :)
    if not (fieldinfo^.Kind in tkManagedTypes) then begin
      inc(Field);
      continue;
    end;
    {$endif};
    saved := ManagedTypeSaveLength(R+Field^.Offset,fieldinfo,recsize);
    if saved=0 then begin
      result := 0; // invalid type
      exit;
    end;
    inc(result,saved-recsize); // extract recsize from info^.recSize

    inc(Field);
  end;
end;

function RecordSave(const Rec; Dest: PAnsiChar; TypeInfo: pointer;
  out Len: integer): PAnsiChar; 
var info,fieldinfo: PTypeInfo;
    F: integer;
    Diff: cardinal;
    Field: PFieldInfo;
    R: PAnsiChar;
begin
  R := @Rec;
  info := GetTypeInfo(TypeInfo,tkRecordTypeOrSet);
  if (R=nil) or (info=nil) then begin
    result := nil; // should have been checked before
    exit;
  end;
  Len := info^.recSize;
  Field := @info^.ManagedFields[0];
  Diff := 0;
  for F := 1 to info^.ManagedCount do begin
    fieldinfo := DeRef(Field^.TypeInfo);
    {$ifdef FPC} // FPC does include RTTI for unmanaged fields! :)
    if not (fieldinfo^.Kind in tkManagedTypes) then begin
      inc(Field);
      continue;
    end;
    {$endif};
    Diff := Field^.Offset-Diff;
    if Diff<>0 then begin
      MoveFast(R^,Dest^,Diff);
      inc(R,Diff);
      inc(Dest,Diff);
    end;
    Dest := ManagedTypeSave(R,Dest,fieldinfo,integer(Diff));
    if Dest=nil then begin
      result := nil; // invalid/unhandled record content
      exit;
    end;













    inc(R,Diff);
    inc(Diff,Field.Offset);
    inc(Field);
  end;
  Diff := info^.recSize-Diff;
  if integer(Diff)<0 then
    raise ESynException.Create('RecordSave diff<0') else
................................................................................
        jmp     System.@FinalizeArray
{$endif CPU64}
end;
{$endif FPC}

function RecordLoad(var Rec; Source: PAnsiChar; TypeInfo: pointer;
  Len: PInteger): PAnsiChar;
var info,fieldinfo: PTypeInfo;
    F: integer;
    Diff: cardinal;
    Field: PFieldInfo;
    R: PAnsiChar;
begin
  result := nil; // indicates error
  R := @Rec;
  info := GetTypeInfo(TypeInfo,tkRecordTypeOrSet);
  if (R=nil) or (info=nil) then // should have been checked before
    exit;
................................................................................
      _Finalize(R+Field^.Offset,Deref(Field^.TypeInfo));
      inc(Field);
    end;
    exit;
  end;
  Diff := 0;
  for F := 1 to info^.ManagedCount do begin
    fieldinfo := DeRef(Field^.TypeInfo);
    {$ifdef FPC} // FPC does include RTTI for unmanaged fields! :)
    if not (fieldinfo^.Kind in tkManagedTypes) then begin
      inc(Field);
      continue;
    end;
    {$endif};
    Diff := Field^.Offset-Diff;
    if Diff<>0 then begin
      MoveFast(Source^,R^,Diff);
      inc(Source,Diff);
      inc(R,Diff);
    end;
    Diff := ManagedTypeLoad(R,Source,fieldinfo);
    if Source=nil then













      exit; // error at loading
    inc(R,Diff);
    inc(Diff,Field^.Offset);
    inc(Field);
  end;
  Diff := info^.recSize-Diff;
  if integer(Diff)<0 then
................................................................................
    result := Dest;
    exit;
  end;
  inc(Dest,sizeof(Cardinal)); // leave space for Hash32 checksum
  result := Dest;
  // store dynamic array elements content
  P := fValue^;
  if ElemType=nil then // FPC: nil also if not Kind in tkManagedTypes
    if GetIsObjArray then
      raise ESynException.CreateUTF8('TDynArray.SaveTo(%) is a T*ObjArray',
        [PShortString(@PTypeInfo(ArrayType).NameLen)^]) else begin
      // binary types: store as once
      n := n*integer(ElemSize);
      MoveFast(P^,Dest^,n);
      inc(Dest,n);
    end else
    for i := 1 to n do begin
      Dest := ManagedTypeSave(P,Dest,ElemType,LenBytes);
      if Dest=nil then
        break;
      assert(LenBytes=integer(ElemSize));





      inc(P,LenBytes);
    end;
  // store Hash32 checksum
  if Dest<>nil then  // may be nil if RecordSave() failed
    PCardinal(result-sizeof(Cardinal))^ := Hash32(result,Dest-result);
  result := Dest;
end;
................................................................................
    result := 0;
    exit; // avoid GPF if void
  end;
  n := Count;
  result := ToVarUInt32Length(ElemSize)+ToVarUInt32Length(n)+1;
  if n=0 then
    exit;
  if ElemType=nil then // FPC: nil also if not Kind in tkManagedTypes
    if GetIsObjArray then
      raise ESynException.CreateUTF8('TDynArray.SaveToLength(%) is a T*ObjArray',
        [PShortString(@PTypeInfo(ArrayType).NameLen)^]) else
      inc(result,integer(ElemSize)*n) else begin
    P := fValue^;
    for i := 1 to n do begin
      L := ManagedTypeSaveLength(P,ElemType,size);

      if L=0 then
        break; // invalid record type (wrong field type)
      assert(size=integer(ElemSize));
      inc(result,L);
      inc(P,size);
    end;
  end;
  inc(result,sizeof(Cardinal)); // Hash32 checksum
end;

................................................................................
        djRawByteString:
          if not Base64MagicCheckAndDecode(Val,ValLen,PRawByteStringArray(fValue^)^[i]) then
            SetString(RawUTF8(PPointerArray(fValue^)^[i]),Val,ValLen);
        djWinAnsi:  WinAnsiConvert.UTF8BufferToAnsi(Val,ValLen,PRawByteStringArray(fValue^)^[i]);
        djString:   UTF8DecodeToString(Val,ValLen,string(PPointerArray(fValue^)^[i]));
        djWideString: UTF8ToWideString(Val,ValLen,WideString(PPointerArray(fValue^)^[i]));
        djSynUnicode: UTF8ToSynUnicode(Val,ValLen,SynUnicode(PPointerArray(fValue^)^[i]));
        else raise ESynException.CreateUTF8('% not readable',[ToText(T)^]);
        end;
      end;
    end;
    end;
  end;
  if aEndOfObject<>nil then
    aEndOfObject^ := EndOfObject;
................................................................................
    exit;
  end;
  // retrieve security checksum
  Hash := pointer(Source);
  inc(Source,sizeof(cardinal));
  // retrieve dynamic array elements content
  P := fValue^;
  if ElemType=nil then // FPC: nil also if not Kind in tkManagedTypes
    if GetIsObjArray then
      raise ESynException.CreateUTF8('TDynArray.LoadFrom(%) is a T*ObjArray',
        [PShortString(@PTypeInfo(ArrayType).NameLen)^]) else begin
      // binary type was stored directly
      n := n*integer(ElemSize);
      MoveFast(Source^,P^,n);
      inc(Source,n);
................................................................................
    result := fCompare(A,B)=0 else
    if ElemType=nil then
      case ElemSize of // optimized versions for arrays of common types
        1: result := byte(A)=byte(B);
        2: result := word(A)=word(B);
        4: result := cardinal(A)=cardinal(B);
        8: result := Int64(A)=Int64(B);
      else result := CompareMem(@A,@B,ElemSize); // binary comparison
      end else
      if PTypeKind(ElemType)^ in tkRecordTypes then // most likely

        result := RecordEquals(A,B,ElemType) else
















        result := ManagedTypeCompare(@A,@B,ElemType)>0; // other complex types
end;

{$ifndef DELPHI5OROLDER} // do not know why Delphi 5 compiler does not like it
function TDynArray.Equals(const B: TDynArray): boolean;
var i, n: integer;
    P1,P2: PAnsiChar;
    A1: PPointerArray absolute P1;
................................................................................
  fElemType := PTypeInfo(aTypeInfo)^.elType;
  if fElemType<>nil then begin
    {$ifndef HASDIRECTTYPEINFO}
    // FPC compatibility: if you have a GPF here at startup, your 3.1 trunk
    // revision seems older than June 2016
    // -> enable HASDIRECTTYPEINFO conditional below $ifdef VER3_1 in Synopse.inc
    // or in your project's options
    fElemType := PPointer(fElemType)^; // inlined DeRef()
    {$endif}
    {$ifdef FPC}
    if not (PTypeKind(fElemType)^ in tkManagedTypes) then
      fElemType := nil; // as with Delphi
    {$endif}
  end;
  fCountP := aCountPointer;
................................................................................
  aCountPointer: PInteger=nil; aCaseInsensitive: boolean=false);
var Comp: TDynArraySortCompare;
begin
  Init(aTypeInfo,aValue,aCountPointer);
  Comp := DYNARRAY_SORTFIRSTFIELD[aCaseInsensitive,aKind];
  if @Comp=nil then
    raise ESynException.CreateUTF8('TDynArray.InitSpecific(%) wrong aKind=%',
      [PShortString(@PTypeInfo(aTypeInfo)^.NameLen)^,ToText(aKind)^]);
  fCompare := Comp;
  fKnownType := aKind;
  fKnownSize := KNOWNTYPE_SIZE[aKind];
end;

procedure TDynArray.UseExternalCount(var aCountPointer: Integer);
begin
................................................................................
  aKind: TDynArrayKind; aCountPointer: PInteger=nil; aCaseInsensitive: boolean=false);
var Comp: TDynArraySortCompare;
    Hasher: TDynArrayHashOne;
begin
  Comp := DYNARRAY_SORTFIRSTFIELD[aCaseInsensitive,aKind];
  Hasher := DYNARRAY_HASHFIRSTFIELD[aCaseInsensitive,aKind];
  if (@Hasher=nil) or (@Comp=nil) then
    raise ESynException.CreateUTF8('TDynArrayHashed.InitSpecific unsupported %',
      [ToText(aKind)^]);
  Init(aTypeInfo,aValue,Hasher,Comp,nil,aCountPointer,aCaseInsensitive);
  {$ifdef UNDIRECTDYNARRAY}with InternalDynArray do{$endif} begin
    fKnownType := aKind;
    fKnownSize := KNOWNTYPE_SIZE[aKind];
  end;
end;

................................................................................
      djWord:     AddU(PWordArray(P)^[i]);
      djInteger:  Add(PIntegerArray(P)^[i]);
      djCardinal: AddU(PCardinalArray(P)^[i]);
      djSingle:   AddSingle(PSingleArray(P)^[i]);
      djInt64:    Add(PInt64Array(P)^[i]);
      djDouble:   AddDouble(PDoubleArray(P)^[i]);
      djCurrency: AddCurr64(PInt64Array(P)^[i]);
      else raise ESynException.CreateUTF8('AddDynArrayJSON unsupported %',[ToText(T)^]);
      end;
      Add(',');
    end;
    end;
  CancelLastComma;
  Add(']');
end;

Changes to SynopseCommit.inc.

1
'1.18.3291'
|
1
'1.18.3292'