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

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

Overview
Comment:{2256} introducing HASCODEPAGE conditional to support FPC_HAS_CPSTRING feature available since FPC 2.7+
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: a4999ede0d8921a866a2bdd9aea354da1514df33
User & Date: ab 2016-01-13 18:14:11
Context
2016-01-13
18:15
{2257} updated documentation about how to use properly the ISQLDBRows interface check-in: e3848c4a9d user: ab tags: trunk
18:14
{2256} introducing HASCODEPAGE conditional to support FPC_HAS_CPSTRING feature available since FPC 2.7+ check-in: a4999ede0d user: ab tags: trunk
15:10
{2255} includes regression tests for latest commit feature check-in: f6da482791 user: ab tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SQLite3/DDD/infra/dddInfraEmail.pas.

312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
  result := fValidationServerRoot+fValidationMethodName+'/'+
    GetWithSalt(aLogonName,aEmail,fValidationSalt)+
    BinToBase64URI(pointer(result),length(result));
end;

procedure TDDDEmailValidationService.EmailValidate(
  Ctxt: TSQLRestServerURIContext);
var code: RawByteString;
    logon,email,signature: RawUTF8;
    EmailValidation: TSQLRecordEmailValidation;
begin
  signature := Copy(Ctxt.URIBlobFieldName,1,SHA256DIGESTSTRLEN);
  if length(signature)<>SHA256DIGESTSTRLEN then
    exit;
  code := Copy(Ctxt.URIBlobFieldName,SHA256DIGESTSTRLEN+1,200);






|







312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
  result := fValidationServerRoot+fValidationMethodName+'/'+
    GetWithSalt(aLogonName,aEmail,fValidationSalt)+
    BinToBase64URI(pointer(result),length(result));
end;

procedure TDDDEmailValidationService.EmailValidate(
  Ctxt: TSQLRestServerURIContext);
var code: RawUTF8;
    logon,email,signature: RawUTF8;
    EmailValidation: TSQLRecordEmailValidation;
begin
  signature := Copy(Ctxt.URIBlobFieldName,1,SHA256DIGESTSTRLEN);
  if length(signature)<>SHA256DIGESTSTRLEN then
    exit;
  code := Copy(Ctxt.URIBlobFieldName,SHA256DIGESTSTRLEN+1,200);

Changes to SQLite3/DDD/infra/dddInfraSettings.pas.

740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
begin
  if self=nil then
    exit;
  fOwner := aOwner;
  if fInitialJsonContent='' then
    exit;
  tmp := fInitialJsonContent;
  UniqueString(AnsiString(tmp));
  RemoveCommentsFromJSON(pointer(tmp));
  JSONToObject(fOwner,pointer(tmp),result);
  if not result then
    fInitialJsonContent := '';
end;

procedure TDDDAppSettingsStorageAbstract.Store(const aJSON: RawUTF8);
begin






<
|







740
741
742
743
744
745
746

747
748
749
750
751
752
753
754
begin
  if self=nil then
    exit;
  fOwner := aOwner;
  if fInitialJsonContent='' then
    exit;
  tmp := fInitialJsonContent;

  RemoveCommentsFromJSON(UniqueRawUTF8(tmp));
  JSONToObject(fOwner,pointer(tmp),result);
  if not result then
    fInitialJsonContent := '';
end;

procedure TDDDAppSettingsStorageAbstract.Store(const aJSON: RawUTF8);
begin

Changes to SQLite3/mORMot.pas.

2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
....
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
....
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
....
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
....
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
....
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
.....
19248
19249
19250
19251
19252
19253
19254
19255
19256
19257
19258
19259
19260
19261
19262
.....
20557
20558
20559
20560
20561
20562
20563
20564
20565
20566
20567
20568
20569
20570
20571
.....
20670
20671
20672
20673
20674
20675
20676
20677
20678
20679
20680
20681
20682
20683
20684
.....
25625
25626
25627
25628
25629
25630
25631
25632
25633
25634
25635
25636
25637
25638
25639
.....
25832
25833
25834
25835
25836
25837
25838
25839
25840
25841
25842
25843
25844
25845
25846
25847
25848
.....
25862
25863
25864
25865
25866
25867
25868
25869
25870
25871
25872
25873
25874
25875
25876
25877
25878
25879
.....
25885
25886
25887
25888
25889
25890
25891
25892
25893
25894
25895
25896
25897
25898
25899
.....
25916
25917
25918
25919
25920
25921
25922

25923
25924
25925
25926
25927
25928
25929
25930
.....
25951
25952
25953
25954
25955
25956
25957
25958
25959
25960
25961
25962
25963
25964
25965
.....
25997
25998
25999
26000
26001
26002
26003
26004
26005
26006
26007
26008
26009
26010
26011
26012
26013
26014
26015
26016
26017
26018
26019
26020
26021

26022
26023
26024
26025
26026
26027
26028
26029
26030
26031
26032
26033
26034
26035
26036
26037
26038
26039
26040
26041
26042
26043
26044
26045
.....
26241
26242
26243
26244
26245
26246
26247
26248
26249
26250
26251
26252
26253
26254
26255
.....
26452
26453
26454
26455
26456
26457
26458
26459
26460
26461
26462
26463
26464
26465
26466
26467
26468
26469
26470
26471
26472
26473
26474
26475
26476
.....
26748
26749
26750
26751
26752
26753
26754
26755
26756
26757
26758
26759
26760
26761
26762
.....
26797
26798
26799
26800
26801
26802
26803
26804
26805
26806
26807
26808
26809
26810
26811
.....
27253
27254
27255
27256
27257
27258
27259
27260
27261
27262
27263
27264
27265
27266
27267
.....
27331
27332
27333
27334
27335
27336
27337
27338
27339
27340
27341
27342
27343
27344
27345
.....
42745
42746
42747
42748
42749
42750
42751
42752
42753
42754
42755
42756
42757
42758
42759
42760
42761
42762
42763
.....
43597
43598
43599
43600
43601
43602
43603
43604
43605
43606
43607
43608
43609
43610
43611
.....
43736
43737
43738
43739
43740
43741
43742
43743
43744
43745
43746
43747
43748
43749
43750
.....
45625
45626
45627
45628
45629
45630
45631
45632
45633
45634
45635
45636
45637
45638
45639
.....
45840
45841
45842
45843
45844
45845
45846
45847
45848
45849
45850
45851
45852
45853
45854
.....
48379
48380
48381
48382
48383
48384
48385




48386
48387
48388
48389
48390
48391
48392
48393
48394
48395
48396
48397
    {$ifdef UNICODE}, tkUString{$endif});
const
  // maps record or object types
  tkRecordTypes = [tkRecord];
{$endif}
  // maps long string types
  tkStringTypes =
    [tkLString,tkWString{$ifdef UNICODE},tkUString{$endif}{$ifdef FPC},tkAString{$endif}];
  // maps 1, 8, 16, 32 and 64 bit ordinal types
  tkOrdinalTypes =
    [tkInteger, tkChar, tkWChar, tkEnumeration, tkSet, tkInt64
     {$ifdef FPC},tkBool,tkQWord{$endif}];

type
  /// specify ordinal (tkInteger and tkEnumeration) storage size and sign
................................................................................
    function DynArrayItemSize: integer; {$ifdef HASINLINE}inline;{$endif}
    /// recognize most used string types, returning their code page
    // - will recognize TSQLRawBlob as the fake CP_SQLRAWBLOB code page
    // - will return the exact code page since Delphi 2009, from RTTI
    // - for non Unicode versions of Delphi, will recognize WinAnsiString as
    // CODEPAGE_US, RawUnicode as CP_UTF16, RawByteString as CP_RAWBYTESTRING,
    // AnsiString as 0, and any other type as RawUTF8
    function AnsiStringCodePage: integer; {$ifdef UNICODE}inline;{$endif}
    /// get the TGUID of a given interface type information
    // - returns nil if this type is not an interface
    function InterfaceGUID: PGUID;
    /// get the unit name of a given interface type information
    // - returns '' if this type is not an interface
    function InterfaceUnitName: PShortString;
    /// get the ancestor/parent of a given interface type information
................................................................................
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    procedure CopyLongStrProp(Source,Dest: TObject);
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    procedure GetWideStrProp(Instance: TObject; var Value: WideString);
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    procedure SetWideStrProp(Instance: TObject; const Value: WideString);
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    {$ifdef UNICODE}
    function GetUnicodeStrProp(Instance: TObject): UnicodeString;
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    procedure SetUnicodeStrProp(Instance: TObject; const Value: UnicodeString);
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    {$endif}
    function GetCurrencyProp(Instance: TObject): currency;
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    procedure SetCurrencyProp(Instance: TObject; const Value: Currency);
     {$ifdef HASINLINE}inline;{$endif}
    function GetDoubleProp(Instance: TObject): double;
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    procedure SetDoubleProp(Instance: TObject; Value: Double);
................................................................................
    procedure SetGenericStringValue(Instance: TObject; const Value: string);
    /// low-level getter of the long string property value of a given instance
    // - uses the generic string type: to be used within the VCL
    // - this method will check if the corresponding property is a Long String,
    // or an UnicodeString (for Delphi 2009+),and will return '' if it's
    // not the case
    function GetGenericStringValue(Instance: TObject): string;
{$ifdef UNICODE}
    /// low-level setter of the Unicode string property value of a given instance
    // - this method will check if the corresponding property is a Unicode String
    procedure SetUnicodeStrValue(Instance: TObject; const Value: UnicodeString);
    /// low-level getter of the Unicode string property value of a given instance
    // - this method will check if the corresponding property is a Unicode String
    function GetUnicodeStrValue(Instance: TObject): UnicodeString;
{$endif}
    /// low-level getter of a dynamic array wrapper
    // - this method will NOT check if the property is a dynamic array: caller
    // must have already checked that PropType^^.Kind=tkDynArray
    function GetDynArray(Instance: TObject): TDynArray; overload;
      {$ifdef HASINLINE}inline;{$endif}
    /// low-level getter of a dynamic array wrapper
    // - this method will NOT check if the property is a dynamic array: caller
................................................................................
    procedure GetBinary(Instance: TObject; W: TFileBufferWriter); override;
    function SetBinary(Instance: TObject; P: PAnsiChar): PAnsiChar; override;
    procedure GetJSONValues(Instance: TObject; W: TJSONSerializer); override;
    function CompareValue(Item1,Item2: TObject; CaseInsensitive: boolean): PtrInt; override;
    function GetHash(Instance: TObject; CaseInsensitive: boolean): cardinal; override;
  end;

{$ifdef UNICODE}
  /// information about a UnicodeString published property
  TSQLPropInfoRTTIUnicode = class(TSQLPropInfoRTTI)
  protected
    procedure CopySameClassProp(Source: TObject; DestInfo: TSQLPropInfo; Dest: TObject); override;
  public
    procedure SetValue(Instance: TObject; Value: PUTF8Char; wasString: boolean); override;
    procedure SetValueVar(Instance: TObject; const Value: RawUTF8; wasString: boolean); override;
................................................................................
    procedure GetJSONValues(Instance: TObject; W: TJSONSerializer); override;
    procedure GetFieldSQLVar(Instance: TObject; var aValue: TSQLVar;
      var temp: RawByteString); override;
    function SetFieldSQLVar(Instance: TObject; const aValue: TSQLVar): boolean; override;
    function CompareValue(Item1,Item2: TObject; CaseInsensitive: boolean): PtrInt; override;
    function GetHash(Instance: TObject; CaseInsensitive: boolean): cardinal; override;
  end;
{$endif}

  /// information about a dynamic array published property
  TSQLPropInfoRTTIDynArray = class(TSQLPropInfoRTTI)
  protected
    fObjArray: PClassInstance;
    function GetDynArray(Instance: TObject): TDynArray; overload;
      {$ifdef HASINLINE}inline;{$endif}
................................................................................
            C := TSQLPropInfoRTTIDouble;
        tkLString {$ifdef FPC},tkAString{$endif}:
          case aType^.AnsiStringCodePage of // recognize optimized UTF-8/UTF-16
            CP_UTF8:  C := TSQLPropInfoRTTIRawUTF8;
            CP_UTF16: C := TSQLPropInfoRTTIRawUnicode;
            else C := TSQLPropInfoRTTIAnsi; // will use the right TSynAnsiConvert
          end;
        {$ifdef UNICODE}
        tkUString:
          C := TSQLPropInfoRTTIUnicode;
        {$endif}
        tkWString:
          C := TSQLPropInfoRTTIWide;
      end;
    end;
................................................................................
begin
  if Value<>nil then
    UTF8ToWideString(Value,StrLen(Value),Wide);
  fPropInfo.SetWideStrProp(Instance,Wide);
end;


{$ifdef UNICODE}

{ TSQLPropInfoRTTIUnicode }

procedure TSQLPropInfoRTTIUnicode.CopySameClassProp(Source: TObject;
  DestInfo: TSQLPropInfo; Dest: TObject);
begin
  TSQLPropInfoRTTIUnicode(DestInfo).fPropInfo.SetUnicodeStrProp(Dest,
................................................................................
  var temp: RawByteString);
begin
  temp := UnicodeStringToUtf8(fPropInfo.GetUnicodeStrProp(Instance));
  aValue.VType := ftUTF8;
  aValue.VText := Pointer(temp);
end;

{$endif UNICODE}


{ TObjArraySerializer}

type
  TObjArraySerializer = class(TPointerClassHashed)
  public
................................................................................
        {$ifdef FPC}tkBool,{$endif}
        tkEnumeration, tkInteger, tkSet: begin
          V := P^.GetOrdProp(Value);
          if V<>P^.Default then
            Add('%%=%'#13,[SubCompName,P^.Name,V]);
        end;
        {$ifdef FPC}tkAString,{$endif} tkLString, tkWString
        {$ifdef UNICODE},tkUString{$endif}: begin
          P^.GetLongStrValue(Value,tmp);
          Add('%%=%'#13,[SubCompName,P^.Name,tmp]);
        end;
        tkFloat: begin
          VT[0] := AnsiChar(ExtendedToString(VT,P^.GetFloatProp(Value),DOUBLE_PRECISION));
          Add('%%=%'#13,[SubCompName,P^.Name,VT]);
        end;
................................................................................
      case cp of
        CP_UTF8:       result := tmp;
        CP_SQLRAWBLOB: result := TSQLRawBlobToBlob(TSQLRawBlob(tmp));
        else           result := TSynAnsiConvert.Engine(cp).AnsiToUTF8(tmp);
      end;
    end;
  end;
  {$ifdef UNICODE}
  tkUString:
    StringToUTF8(GetUnicodeStrProp(Instance),result);
  {$endif}
  tkWString: begin
    GetWideStrProp(Instance,tmpWS);
    RawUnicodeToUtf8(pointer(tmpWS),length(tmpWS),result);
  end;
  else result := '';
  end
................................................................................
  var tmp: RawByteString;
  begin
    if cp=CP_SQLRAWBLOB then
      tmp := BlobToTSQLRawBlob(Value) else
      tmp := TSynAnsiConvert.Engine(cp).UTF8ToAnsi(Value);
    SetLongStrProp(Instance,tmp);
  end;
  {$ifdef UNICODE}
  procedure HandleUnicode(Instance: TObject; const Value: RawUTF8);
  begin
    SetUnicodeStrProp(Instance,UTF8ToString(Value));
  end;
  {$endif}
  procedure HandleWideString(Instance: TObject; const Value: RawUTF8);
  begin
    SetWideStrProp(Instance,UTF8ToWideString(Value));
  end;
var cp: integer;
................................................................................
      cp := PropType^.AnsiStringCodePage;
      if cp=CP_UTF8 then
        SetLongStrProp(Instance,Value) else
        HandleAnsiString(Instance,Value,cp);
    end else
      SetLongStrProp(Instance,'');
  end;
  {$ifdef UNICODE}
  tkUString:
    HandleUnicode(Instance,Value);
  {$endif}
  tkWString:
    HandleWideString(Instance,Value);
  end;
end;
................................................................................
      i := PropType^.EnumBaseType^.GetEnumNameValue(pointer(u),length(u));
      if i>=0 then
        SetOrdProp(Instance,i)
    end;
  tkInt64{$ifdef FPC},tkQWord{$endif}:
    if VariantToInt64(Value,i64) then
      SetInt64Prop(Instance,i64);

  {$ifdef UNICODE}tkUString,{$endif}tkLString,tkWString{$ifdef FPC},tkAString{$endif}:
    if VariantToUTF8(Value,u) then
      SetLongStrValue(Instance,u);
  tkFloat:
    if VariantToDouble(Value,d) then
      SetFloatProp(Instance,d);
  tkVariant:
    SetVariantProp(Instance,Value);
................................................................................
  case PropType^.Kind of
  tkInteger,tkEnumeration,tkSet,tkChar,tkWChar{$ifdef FPC},tkBool{$endif}:
    SetOrdProp(Instance,0);
  tkInt64{$ifdef FPC},tkQWord{$endif}:
    SetInt64Prop(Instance,0);
  tkLString{$ifdef FPC},tkAString{$endif}:
    SetLongStrProp(Instance,'');
  {$ifdef UNICODE}
  tkUString:
    SetUnicodeStrProp(Instance,'');
  {$endif}
  tkWString:
    SetWideStrProp(Instance,'');
  tkFloat:
    SetFloatProp(Instance,0);
................................................................................
  if (Instance=nil) or (@self=nil) then
    result := '' else
    case PropType^.Kind of
      {$ifdef FPC}tkAString,{$endif} tkLString, tkWString: begin
        GetLongStrValue(Instance,tmp);
        result := UTF8ToString(tmp);
      end;
{$ifdef UNICODE}
      tkUString:
        result := GetUnicodeStrProp(Instance);
{$endif}else result := '';
     end;
end;

procedure TPropInfo.SetGenericStringValue(Instance: TObject; const Value: string);
begin
  if (Instance<>nil) and (@self<>nil) then
    case PropType^.Kind of
      {$ifdef FPC}tkAString,{$endif}tkLString, tkWString:
         SetLongStrValue(Instance,StringToUtf8(Value));
{$ifdef UNICODE}
       tkUString:
         SetUnicodeStrProp(Instance,Value);
{$endif}end;
end;


{$ifdef UNICODE}

function TPropInfo.GetUnicodeStrValue(Instance: TObject): UnicodeString;
begin
  if (Instance<>nil) and (@self<>nil) and
     (PropType^^.Kind=tkUString) then
    result := GetUnicodeStrProp(Instance);
end;

procedure TPropInfo.SetUnicodeStrValue(Instance: TObject; const Value: UnicodeString);
begin
  if (Instance<>nil) and (@self<>nil) and
     (PropType^^.Kind=tkUString) then
    SetUnicodeStrProp(Instance,Value);
end;
{$endif}

procedure TPropInfo.SetOrdValue(Instance: TObject; Value: PtrInt);
begin
  if (Instance<>nil) and (@self<>nil) and
     (PropType^.Kind in [
       tkInteger,tkEnumeration,tkSet,{$ifdef FPC}tkBool,{$endif}tkClass]) then
    SetOrdProp(Instance,Value);
................................................................................
        GetLongStrProp(Source,Value);
        DestInfo.SetLongStrProp(Dest,Value);
      end else
str:  if kD in tkStringTypes then begin
        GetLongStrValue(Source,RawUTF8(Value));
        DestInfo.SetLongStrValue(Dest,RawUTF8(Value));
      end;
    {$ifdef UNICODE}
    tkUString:
      if kD=tkUString then
        DestInfo.SetUnicodeStrProp(Dest,GetUnicodeStrProp(Source)) else
        goto str;
    {$endif}
    tkWString:
      if kD=tkWString then begin
................................................................................
end;

procedure TPropInfo.SetWideStrProp(Instance: TObject; const Value: WideString);
begin
  TypInfo.SetWideStrProp(Instance,@self,Value);
end;

{$ifdef UNICODE}
function TPropInfo.GetUnicodeStrProp(Instance: TObject): UnicodeString;
begin
  Value := TypInfo.GetUnicodeStrProp(Instance,@self);
end;

procedure TPropInfo.SetUnicodeStrProp(Instance: TObject; const Value: UnicodeString);
begin
  TypInfo.SetUnicodeStrProp(Instance,@self,Value);
end;
{$endif}

function TPropInfo.GetCurrencyProp(Instance: TObject): currency;
begin
  if GetterIsField then
    result := PCurrency(GetterAddr(Instance))^ else
    result := TypInfo.GetFloatProp(Instance,@self);
end;
................................................................................
    M.Data := Instance;
    if Index=NO_INDEX then  // no index
      TUStringSetProc(M)(Value) else
      TUStringIndexedSetProc(M)(Index, Value);
  end;
end;

{$ifdef UNICODE}
function TPropInfo.GetUnicodeStrProp(Instance: TObject): UnicodeString;
type
  TUStringGetProc = function: UnicodeString of object;
  TUStringIndexedGetProc = function(Index: Integer): UnicodeString of object;
var M: TMethod;
begin // caller must check that PropType^.Kind = tkUString
  if PropWrap(GetProc).Kind=$FF then
................................................................................
      M.Code := Pointer(SetProc);
    M.Data := Instance;
    if Index=NO_INDEX then // no index
      TUStringSetProc(M)(Value) else
      TUStringIndexedSetProc(M)(Index, Value);
  end;
end;
{$endif}

function TPropInfo.GetCurrencyProp(Instance: TObject): currency;
type // function(Instance: TObject) trick does not work with CPU64 :(
  TGetProc = function: currency of object;
  TIndexedGetProc = function(Index: Integer): currency of object;
var P: Pointer;
    Call: TMethod;
................................................................................
      if @self=TypeInfo(WinAnsiString) then begin
        result := sftAnsiText;
        exit;
      end else begin
        result := sftUTF8Text; // CP_UTF8,CP_UTF16 and any other to UTF-8 text
        exit; 
      end;
    {$ifdef UNICODE}tkUString,{$endif} tkChar, tkWChar, tkWString: begin
      result := sftUTF8Text;
      exit;
    end;
    tkDynArray: begin
      result := sftBlobDynArray;
      exit;
    end;
................................................................................
  if @self=nil then
    result := 0 else
    DynArrayTypeInfoToRecordInfo(@self,@result);
end;

function TTypeInfo.AnsiStringCodePage: integer;
begin
  {$ifdef UNICODE}
  if @self=TypeInfo(TSQLRawBlob) then
    result := CP_SQLRAWBLOB else
    if Kind in [{$ifdef FPC}tkAString,{$endif} tkLString] then
      result := PWord(AlignToPtr(@Name[ord(Name[0])+1]))^ else // from RTTI
  {$else}
  if @self=TypeInfo(RawUTF8) then
    result := CP_UTF8 else
................................................................................
          Int64ToUtf8(P^.GetInt64Prop(Value)));
      {$ifdef FPC}tkBool,{$endif} tkEnumeration, tkSet, tkInteger: begin
        V := P^.GetOrdProp(Value);
        //if V<>P^.Default then NO DEFAULT: update INI -> must override previous
        UpdateIniEntry(IniContent,Section,SubCompName+ToUTF8(P^.Name),
          Int32ToUtf8(V));
      end;
      {$ifdef UNICODE}tkUString,{$endif} {$ifdef FPC}tkAString,{$endif}
      tkLString, tkWString: begin
        P^.GetLongStrValue(Value,tmp);
        UpdateIniEntry(IniContent,Section,SubCompName+ToUTF8(P^.Name),tmp);
        end;
      tkClass:
      if Section='' then begin // recursive call works only as plain object
        Obj := P^.GetObjProp(Value);
        if (Obj<>nil) and Obj.InheritsFrom(TPersistent) then
          WriteObject(Value,IniContent,Section,SubCompName+ToUTF8(P^.Name)+'.');
      end;
      // tkString (shortstring) and tkInterface are not handled
................................................................................
        end;
      {$ifdef FPC}tkAString,{$endif} tkLString: 
        if wasString or (j2oIgnoreStringType in Options) then begin
          SetString(U,PAnsiChar(PropValue),StrLen(PropValue));
          P^.SetLongStrValue(Value,U);
        end else
          exit;
      {$ifdef UNICODE}
      tkUString:
        if wasString or (j2oIgnoreStringType in Options) then
          P^.SetUnicodeStrProp(Value,
            UTF8DecodeToUnicodeString(PropValue,StrLen(PropValue))) else
          exit;
      {$endif}
      tkWString:
................................................................................
          if err=0 then
            P^.SetFloatProp(Value,E);
        end;
      {$ifdef FPC}tkAString,{$endif} tkLString:
        P^.SetLongStrValue(Value,U);
      tkWString:
         P^.SetWideStrProp(Value,UTF8ToWideString(U));
      {$ifdef UNICODE}
      tkUString:
         P^.SetUnicodeStrProp(Value,UTF8ToString(U));
      {$endif}
      tkDynArray:
        P^.GetDynArray(Value).LoadFrom(pointer(BlobToTSQLRawBlob(U)));
{$ifdef PUBLISHRECORD}
      tkRecord{$ifdef FPC},tkObject{$endif}:
................................................................................
    Table: TSQLTable absolute Value;
    aClassType: TClass;
    Kind: TTypeKind;
    UtfP: PPUtf8CharArray;
    IsObj: TJSONObject;
    IsObjCustomIndex: integer;
    WS: WideString;
    {$ifdef UNICODE}
    US: UnicodeString;
    {$endif}
    tmp: RawByteString;
    dyn: TDynArray;
    dynObjArray: PClassInstance;
    {$ifndef NOVARIANTS}
    VVariant: variant;
................................................................................
            AddDateTime(P^.GetDoubleProp(Value));
            if woDateTimeWithZSuffix in Options then
              Add('Z');
            Add('"');
          end else
            Add(P^.GetFloatProp(Value),DOUBLE_PRECISION);
        end;
        {$ifdef UNICODE}
        tkUString: begin // write converted to UTF-8
          US := P^.GetUnicodeStrProp(Value);
          if (US<>'') or not (woDontStoreEmptyString in Options) then begin
            HR(P);
            Add('"');
            AddJSONEscapeW(pointer(US));
            Add('"');
................................................................................
      result := smvRawJSON else
    if P=TypeInfo(RawByteString) then
      result := smvRawByteString else
  {$ifndef UNICODE}
    if P=TypeInfo(AnsiString) then
      result := smvString else
      result := smvRawUTF8; // UTF-8 by default




  {$else UNICODE}
      result := smvRawUTF8;
  tkUString:
    result := smvString;
  {$endif}
  tkWString:
    result := smvWideString;
  tkClass:
    with P^.ClassType^ do
    if ClassHasPublishedFields(ClassType) or
       (JSONObject(ClassType,IsObjCustomIndex,[cpRead,cpWrite]) in
         [{$ifndef LVCL}oCollection,{$endif}oObjectList,oUtfs,oStrings,






|







 







|







 







|




|







 







|






|







 







|







 







|







 







|







 







|







 







|







 







|







 







|

|







 







|


|







 







|







 







>
|







 







|







 







|

|
|









|

|
|
|
>

|
<



|






|


|







 







|







 







|


|






|







 







|







 







|







 







|







 







|







 







|



|







 







|







 







|







 







|







 







|







 







>
>
>
>




|







2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
....
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
....
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
....
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
....
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
....
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
.....
19248
19249
19250
19251
19252
19253
19254
19255
19256
19257
19258
19259
19260
19261
19262
.....
20557
20558
20559
20560
20561
20562
20563
20564
20565
20566
20567
20568
20569
20570
20571
.....
20670
20671
20672
20673
20674
20675
20676
20677
20678
20679
20680
20681
20682
20683
20684
.....
25625
25626
25627
25628
25629
25630
25631
25632
25633
25634
25635
25636
25637
25638
25639
.....
25832
25833
25834
25835
25836
25837
25838
25839
25840
25841
25842
25843
25844
25845
25846
25847
25848
.....
25862
25863
25864
25865
25866
25867
25868
25869
25870
25871
25872
25873
25874
25875
25876
25877
25878
25879
.....
25885
25886
25887
25888
25889
25890
25891
25892
25893
25894
25895
25896
25897
25898
25899
.....
25916
25917
25918
25919
25920
25921
25922
25923
25924
25925
25926
25927
25928
25929
25930
25931
.....
25952
25953
25954
25955
25956
25957
25958
25959
25960
25961
25962
25963
25964
25965
25966
.....
25998
25999
26000
26001
26002
26003
26004
26005
26006
26007
26008
26009
26010
26011
26012
26013
26014
26015
26016
26017
26018
26019
26020
26021
26022
26023
26024
26025

26026
26027
26028
26029
26030
26031
26032
26033
26034
26035
26036
26037
26038
26039
26040
26041
26042
26043
26044
26045
26046
.....
26242
26243
26244
26245
26246
26247
26248
26249
26250
26251
26252
26253
26254
26255
26256
.....
26453
26454
26455
26456
26457
26458
26459
26460
26461
26462
26463
26464
26465
26466
26467
26468
26469
26470
26471
26472
26473
26474
26475
26476
26477
.....
26749
26750
26751
26752
26753
26754
26755
26756
26757
26758
26759
26760
26761
26762
26763
.....
26798
26799
26800
26801
26802
26803
26804
26805
26806
26807
26808
26809
26810
26811
26812
.....
27254
27255
27256
27257
27258
27259
27260
27261
27262
27263
27264
27265
27266
27267
27268
.....
27332
27333
27334
27335
27336
27337
27338
27339
27340
27341
27342
27343
27344
27345
27346
.....
42746
42747
42748
42749
42750
42751
42752
42753
42754
42755
42756
42757
42758
42759
42760
42761
42762
42763
42764
.....
43598
43599
43600
43601
43602
43603
43604
43605
43606
43607
43608
43609
43610
43611
43612
.....
43737
43738
43739
43740
43741
43742
43743
43744
43745
43746
43747
43748
43749
43750
43751
.....
45626
45627
45628
45629
45630
45631
45632
45633
45634
45635
45636
45637
45638
45639
45640
.....
45841
45842
45843
45844
45845
45846
45847
45848
45849
45850
45851
45852
45853
45854
45855
.....
48380
48381
48382
48383
48384
48385
48386
48387
48388
48389
48390
48391
48392
48393
48394
48395
48396
48397
48398
48399
48400
48401
48402
    {$ifdef UNICODE}, tkUString{$endif});
const
  // maps record or object types
  tkRecordTypes = [tkRecord];
{$endif}
  // maps long string types
  tkStringTypes =
    [tkLString,tkWString{$ifdef HASVARUSTRING},tkUString{$endif}{$ifdef FPC},tkAString{$endif}];
  // maps 1, 8, 16, 32 and 64 bit ordinal types
  tkOrdinalTypes =
    [tkInteger, tkChar, tkWChar, tkEnumeration, tkSet, tkInt64
     {$ifdef FPC},tkBool,tkQWord{$endif}];

type
  /// specify ordinal (tkInteger and tkEnumeration) storage size and sign
................................................................................
    function DynArrayItemSize: integer; {$ifdef HASINLINE}inline;{$endif}
    /// recognize most used string types, returning their code page
    // - will recognize TSQLRawBlob as the fake CP_SQLRAWBLOB code page
    // - will return the exact code page since Delphi 2009, from RTTI
    // - for non Unicode versions of Delphi, will recognize WinAnsiString as
    // CODEPAGE_US, RawUnicode as CP_UTF16, RawByteString as CP_RAWBYTESTRING,
    // AnsiString as 0, and any other type as RawUTF8
    function AnsiStringCodePage: integer; {$ifdef HASCODEPAGE}inline;{$endif}
    /// get the TGUID of a given interface type information
    // - returns nil if this type is not an interface
    function InterfaceGUID: PGUID;
    /// get the unit name of a given interface type information
    // - returns '' if this type is not an interface
    function InterfaceUnitName: PShortString;
    /// get the ancestor/parent of a given interface type information
................................................................................
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    procedure CopyLongStrProp(Source,Dest: TObject);
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    procedure GetWideStrProp(Instance: TObject; var Value: WideString);
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    procedure SetWideStrProp(Instance: TObject; const Value: WideString);
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    {$ifdef HASVARUSTRING}
    function GetUnicodeStrProp(Instance: TObject): UnicodeString;
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    procedure SetUnicodeStrProp(Instance: TObject; const Value: UnicodeString);
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    {$endif HASVARUSTRING}
    function GetCurrencyProp(Instance: TObject): currency;
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    procedure SetCurrencyProp(Instance: TObject; const Value: Currency);
     {$ifdef HASINLINE}inline;{$endif}
    function GetDoubleProp(Instance: TObject): double;
     {$ifdef USETYPEINFO}{$ifdef HASINLINE}inline;{$endif}{$endif}
    procedure SetDoubleProp(Instance: TObject; Value: Double);
................................................................................
    procedure SetGenericStringValue(Instance: TObject; const Value: string);
    /// low-level getter of the long string property value of a given instance
    // - uses the generic string type: to be used within the VCL
    // - this method will check if the corresponding property is a Long String,
    // or an UnicodeString (for Delphi 2009+),and will return '' if it's
    // not the case
    function GetGenericStringValue(Instance: TObject): string;
    {$ifdef HASVARUSTRING}
    /// low-level setter of the Unicode string property value of a given instance
    // - this method will check if the corresponding property is a Unicode String
    procedure SetUnicodeStrValue(Instance: TObject; const Value: UnicodeString);
    /// low-level getter of the Unicode string property value of a given instance
    // - this method will check if the corresponding property is a Unicode String
    function GetUnicodeStrValue(Instance: TObject): UnicodeString;
    {$endif}
    /// low-level getter of a dynamic array wrapper
    // - this method will NOT check if the property is a dynamic array: caller
    // must have already checked that PropType^^.Kind=tkDynArray
    function GetDynArray(Instance: TObject): TDynArray; overload;
      {$ifdef HASINLINE}inline;{$endif}
    /// low-level getter of a dynamic array wrapper
    // - this method will NOT check if the property is a dynamic array: caller
................................................................................
    procedure GetBinary(Instance: TObject; W: TFileBufferWriter); override;
    function SetBinary(Instance: TObject; P: PAnsiChar): PAnsiChar; override;
    procedure GetJSONValues(Instance: TObject; W: TJSONSerializer); override;
    function CompareValue(Item1,Item2: TObject; CaseInsensitive: boolean): PtrInt; override;
    function GetHash(Instance: TObject; CaseInsensitive: boolean): cardinal; override;
  end;

  {$ifdef HASVARUSTRING}
  /// information about a UnicodeString published property
  TSQLPropInfoRTTIUnicode = class(TSQLPropInfoRTTI)
  protected
    procedure CopySameClassProp(Source: TObject; DestInfo: TSQLPropInfo; Dest: TObject); override;
  public
    procedure SetValue(Instance: TObject; Value: PUTF8Char; wasString: boolean); override;
    procedure SetValueVar(Instance: TObject; const Value: RawUTF8; wasString: boolean); override;
................................................................................
    procedure GetJSONValues(Instance: TObject; W: TJSONSerializer); override;
    procedure GetFieldSQLVar(Instance: TObject; var aValue: TSQLVar;
      var temp: RawByteString); override;
    function SetFieldSQLVar(Instance: TObject; const aValue: TSQLVar): boolean; override;
    function CompareValue(Item1,Item2: TObject; CaseInsensitive: boolean): PtrInt; override;
    function GetHash(Instance: TObject; CaseInsensitive: boolean): cardinal; override;
  end;
  {$endif HASVARUSTRING}

  /// information about a dynamic array published property
  TSQLPropInfoRTTIDynArray = class(TSQLPropInfoRTTI)
  protected
    fObjArray: PClassInstance;
    function GetDynArray(Instance: TObject): TDynArray; overload;
      {$ifdef HASINLINE}inline;{$endif}
................................................................................
            C := TSQLPropInfoRTTIDouble;
        tkLString {$ifdef FPC},tkAString{$endif}:
          case aType^.AnsiStringCodePage of // recognize optimized UTF-8/UTF-16
            CP_UTF8:  C := TSQLPropInfoRTTIRawUTF8;
            CP_UTF16: C := TSQLPropInfoRTTIRawUnicode;
            else C := TSQLPropInfoRTTIAnsi; // will use the right TSynAnsiConvert
          end;
        {$ifdef HASVARUSTRING}
        tkUString:
          C := TSQLPropInfoRTTIUnicode;
        {$endif}
        tkWString:
          C := TSQLPropInfoRTTIWide;
      end;
    end;
................................................................................
begin
  if Value<>nil then
    UTF8ToWideString(Value,StrLen(Value),Wide);
  fPropInfo.SetWideStrProp(Instance,Wide);
end;


{$ifdef HASVARUSTRING}

{ TSQLPropInfoRTTIUnicode }

procedure TSQLPropInfoRTTIUnicode.CopySameClassProp(Source: TObject;
  DestInfo: TSQLPropInfo; Dest: TObject);
begin
  TSQLPropInfoRTTIUnicode(DestInfo).fPropInfo.SetUnicodeStrProp(Dest,
................................................................................
  var temp: RawByteString);
begin
  temp := UnicodeStringToUtf8(fPropInfo.GetUnicodeStrProp(Instance));
  aValue.VType := ftUTF8;
  aValue.VText := Pointer(temp);
end;

{$endif HASVARUSTRING}


{ TObjArraySerializer}

type
  TObjArraySerializer = class(TPointerClassHashed)
  public
................................................................................
        {$ifdef FPC}tkBool,{$endif}
        tkEnumeration, tkInteger, tkSet: begin
          V := P^.GetOrdProp(Value);
          if V<>P^.Default then
            Add('%%=%'#13,[SubCompName,P^.Name,V]);
        end;
        {$ifdef FPC}tkAString,{$endif} tkLString, tkWString
        {$ifdef HASVARUSTRING},tkUString{$endif}: begin
          P^.GetLongStrValue(Value,tmp);
          Add('%%=%'#13,[SubCompName,P^.Name,tmp]);
        end;
        tkFloat: begin
          VT[0] := AnsiChar(ExtendedToString(VT,P^.GetFloatProp(Value),DOUBLE_PRECISION));
          Add('%%=%'#13,[SubCompName,P^.Name,VT]);
        end;
................................................................................
      case cp of
        CP_UTF8:       result := tmp;
        CP_SQLRAWBLOB: result := TSQLRawBlobToBlob(TSQLRawBlob(tmp));
        else           result := TSynAnsiConvert.Engine(cp).AnsiToUTF8(tmp);
      end;
    end;
  end;
  {$ifdef HASVARUSTRING}
  tkUString:
    result := UnicodeStringToUTF8(GetUnicodeStrProp(Instance));
  {$endif}
  tkWString: begin
    GetWideStrProp(Instance,tmpWS);
    RawUnicodeToUtf8(pointer(tmpWS),length(tmpWS),result);
  end;
  else result := '';
  end
................................................................................
  var tmp: RawByteString;
  begin
    if cp=CP_SQLRAWBLOB then
      tmp := BlobToTSQLRawBlob(Value) else
      tmp := TSynAnsiConvert.Engine(cp).UTF8ToAnsi(Value);
    SetLongStrProp(Instance,tmp);
  end;
  {$ifdef HASVARUSTRING}
  procedure HandleUnicode(Instance: TObject; const Value: RawUTF8);
  begin
    SetUnicodeStrProp(Instance,UTF8DecodeToUnicodeString(Value));
  end;
  {$endif}
  procedure HandleWideString(Instance: TObject; const Value: RawUTF8);
  begin
    SetWideStrProp(Instance,UTF8ToWideString(Value));
  end;
var cp: integer;
................................................................................
      cp := PropType^.AnsiStringCodePage;
      if cp=CP_UTF8 then
        SetLongStrProp(Instance,Value) else
        HandleAnsiString(Instance,Value,cp);
    end else
      SetLongStrProp(Instance,'');
  end;
  {$ifdef HASVARUSTRING}
  tkUString:
    HandleUnicode(Instance,Value);
  {$endif}
  tkWString:
    HandleWideString(Instance,Value);
  end;
end;
................................................................................
      i := PropType^.EnumBaseType^.GetEnumNameValue(pointer(u),length(u));
      if i>=0 then
        SetOrdProp(Instance,i)
    end;
  tkInt64{$ifdef FPC},tkQWord{$endif}:
    if VariantToInt64(Value,i64) then
      SetInt64Prop(Instance,i64);
  {$ifdef HASVARUSTRING}tkUString,{$endif}
  tkLString, tkWString {$ifdef FPC},tkAString{$endif}:
    if VariantToUTF8(Value,u) then
      SetLongStrValue(Instance,u);
  tkFloat:
    if VariantToDouble(Value,d) then
      SetFloatProp(Instance,d);
  tkVariant:
    SetVariantProp(Instance,Value);
................................................................................
  case PropType^.Kind of
  tkInteger,tkEnumeration,tkSet,tkChar,tkWChar{$ifdef FPC},tkBool{$endif}:
    SetOrdProp(Instance,0);
  tkInt64{$ifdef FPC},tkQWord{$endif}:
    SetInt64Prop(Instance,0);
  tkLString{$ifdef FPC},tkAString{$endif}:
    SetLongStrProp(Instance,'');
  {$ifdef HASVARUSTRING}
  tkUString:
    SetUnicodeStrProp(Instance,'');
  {$endif}
  tkWString:
    SetWideStrProp(Instance,'');
  tkFloat:
    SetFloatProp(Instance,0);
................................................................................
  if (Instance=nil) or (@self=nil) then
    result := '' else
    case PropType^.Kind of
      {$ifdef FPC}tkAString,{$endif} tkLString, tkWString: begin
        GetLongStrValue(Instance,tmp);
        result := UTF8ToString(tmp);
      end;
      {$ifdef HASVARUSTRING}
      tkUString:
        result := string(GetUnicodeStrProp(Instance));
      {$endif}else result := '';
     end;
end;

procedure TPropInfo.SetGenericStringValue(Instance: TObject; const Value: string);
begin
  if (Instance<>nil) and (@self<>nil) then
    case PropType^.Kind of
      {$ifdef FPC}tkAString,{$endif}tkLString, tkWString:
         SetLongStrValue(Instance,StringToUtf8(Value));
      {$ifdef HASVARUSTRING}
       tkUString:
         SetUnicodeStrProp(Instance,UnicodeString(Value));
      {$endif}
    end;
end;

{$ifdef HASVARUSTRING}

function TPropInfo.GetUnicodeStrValue(Instance: TObject): UnicodeString;
begin
  if (Instance<>nil) and (@self<>nil) and
     (PropType^.Kind=tkUString) then
    result := GetUnicodeStrProp(Instance);
end;

procedure TPropInfo.SetUnicodeStrValue(Instance: TObject; const Value: UnicodeString);
begin
  if (Instance<>nil) and (@self<>nil) and
     (PropType^.Kind=tkUString) then
    SetUnicodeStrProp(Instance,Value);
end;
{$endif HASVARUSTRING}

procedure TPropInfo.SetOrdValue(Instance: TObject; Value: PtrInt);
begin
  if (Instance<>nil) and (@self<>nil) and
     (PropType^.Kind in [
       tkInteger,tkEnumeration,tkSet,{$ifdef FPC}tkBool,{$endif}tkClass]) then
    SetOrdProp(Instance,Value);
................................................................................
        GetLongStrProp(Source,Value);
        DestInfo.SetLongStrProp(Dest,Value);
      end else
str:  if kD in tkStringTypes then begin
        GetLongStrValue(Source,RawUTF8(Value));
        DestInfo.SetLongStrValue(Dest,RawUTF8(Value));
      end;
    {$ifdef HASVARUSTRING}
    tkUString:
      if kD=tkUString then
        DestInfo.SetUnicodeStrProp(Dest,GetUnicodeStrProp(Source)) else
        goto str;
    {$endif}
    tkWString:
      if kD=tkWString then begin
................................................................................
end;

procedure TPropInfo.SetWideStrProp(Instance: TObject; const Value: WideString);
begin
  TypInfo.SetWideStrProp(Instance,@self,Value);
end;

{$ifdef HASVARUSTRING}
function TPropInfo.GetUnicodeStrProp(Instance: TObject): UnicodeString;
begin
  result := TypInfo.GetUnicodeStrProp(Instance,@self);
end;

procedure TPropInfo.SetUnicodeStrProp(Instance: TObject; const Value: UnicodeString);
begin
  TypInfo.SetUnicodeStrProp(Instance,@self,Value);
end;
{$endif HASVARUSTRING}

function TPropInfo.GetCurrencyProp(Instance: TObject): currency;
begin
  if GetterIsField then
    result := PCurrency(GetterAddr(Instance))^ else
    result := TypInfo.GetFloatProp(Instance,@self);
end;
................................................................................
    M.Data := Instance;
    if Index=NO_INDEX then  // no index
      TUStringSetProc(M)(Value) else
      TUStringIndexedSetProc(M)(Index, Value);
  end;
end;

{$ifdef HASVARUSTRING}
function TPropInfo.GetUnicodeStrProp(Instance: TObject): UnicodeString;
type
  TUStringGetProc = function: UnicodeString of object;
  TUStringIndexedGetProc = function(Index: Integer): UnicodeString of object;
var M: TMethod;
begin // caller must check that PropType^.Kind = tkUString
  if PropWrap(GetProc).Kind=$FF then
................................................................................
      M.Code := Pointer(SetProc);
    M.Data := Instance;
    if Index=NO_INDEX then // no index
      TUStringSetProc(M)(Value) else
      TUStringIndexedSetProc(M)(Index, Value);
  end;
end;
{$endif HASVARUSTRING}

function TPropInfo.GetCurrencyProp(Instance: TObject): currency;
type // function(Instance: TObject) trick does not work with CPU64 :(
  TGetProc = function: currency of object;
  TIndexedGetProc = function(Index: Integer): currency of object;
var P: Pointer;
    Call: TMethod;
................................................................................
      if @self=TypeInfo(WinAnsiString) then begin
        result := sftAnsiText;
        exit;
      end else begin
        result := sftUTF8Text; // CP_UTF8,CP_UTF16 and any other to UTF-8 text
        exit; 
      end;
    {$ifdef HASVARUSTRING}tkUString,{$endif} tkChar, tkWChar, tkWString: begin
      result := sftUTF8Text;
      exit;
    end;
    tkDynArray: begin
      result := sftBlobDynArray;
      exit;
    end;
................................................................................
  if @self=nil then
    result := 0 else
    DynArrayTypeInfoToRecordInfo(@self,@result);
end;

function TTypeInfo.AnsiStringCodePage: integer;
begin
  {$ifdef HASCODEPAGE}
  if @self=TypeInfo(TSQLRawBlob) then
    result := CP_SQLRAWBLOB else
    if Kind in [{$ifdef FPC}tkAString,{$endif} tkLString] then
      result := PWord(AlignToPtr(@Name[ord(Name[0])+1]))^ else // from RTTI
  {$else}
  if @self=TypeInfo(RawUTF8) then
    result := CP_UTF8 else
................................................................................
          Int64ToUtf8(P^.GetInt64Prop(Value)));
      {$ifdef FPC}tkBool,{$endif} tkEnumeration, tkSet, tkInteger: begin
        V := P^.GetOrdProp(Value);
        //if V<>P^.Default then NO DEFAULT: update INI -> must override previous
        UpdateIniEntry(IniContent,Section,SubCompName+ToUTF8(P^.Name),
          Int32ToUtf8(V));
      end;
      {$ifdef HASVARUSTRING}tkUString,{$endif} {$ifdef FPC}tkAString,{$endif}
      tkLString, tkWString: begin
        P^.GetLongStrValue(Value,tmp);
        UpdateIniEntry(IniContent,Section,SubCompName+ToUTF8(P^.Name),tmp);
      end;
      tkClass:
      if Section='' then begin // recursive call works only as plain object
        Obj := P^.GetObjProp(Value);
        if (Obj<>nil) and Obj.InheritsFrom(TPersistent) then
          WriteObject(Value,IniContent,Section,SubCompName+ToUTF8(P^.Name)+'.');
      end;
      // tkString (shortstring) and tkInterface are not handled
................................................................................
        end;
      {$ifdef FPC}tkAString,{$endif} tkLString: 
        if wasString or (j2oIgnoreStringType in Options) then begin
          SetString(U,PAnsiChar(PropValue),StrLen(PropValue));
          P^.SetLongStrValue(Value,U);
        end else
          exit;
      {$ifdef HASVARUSTRING}
      tkUString:
        if wasString or (j2oIgnoreStringType in Options) then
          P^.SetUnicodeStrProp(Value,
            UTF8DecodeToUnicodeString(PropValue,StrLen(PropValue))) else
          exit;
      {$endif}
      tkWString:
................................................................................
          if err=0 then
            P^.SetFloatProp(Value,E);
        end;
      {$ifdef FPC}tkAString,{$endif} tkLString:
        P^.SetLongStrValue(Value,U);
      tkWString:
         P^.SetWideStrProp(Value,UTF8ToWideString(U));
      {$ifdef HASVARUSTRING}
      tkUString:
         P^.SetUnicodeStrProp(Value,UTF8ToString(U));
      {$endif}
      tkDynArray:
        P^.GetDynArray(Value).LoadFrom(pointer(BlobToTSQLRawBlob(U)));
{$ifdef PUBLISHRECORD}
      tkRecord{$ifdef FPC},tkObject{$endif}:
................................................................................
    Table: TSQLTable absolute Value;
    aClassType: TClass;
    Kind: TTypeKind;
    UtfP: PPUtf8CharArray;
    IsObj: TJSONObject;
    IsObjCustomIndex: integer;
    WS: WideString;
    {$ifdef HASVARUSTRING}
    US: UnicodeString;
    {$endif}
    tmp: RawByteString;
    dyn: TDynArray;
    dynObjArray: PClassInstance;
    {$ifndef NOVARIANTS}
    VVariant: variant;
................................................................................
            AddDateTime(P^.GetDoubleProp(Value));
            if woDateTimeWithZSuffix in Options then
              Add('Z');
            Add('"');
          end else
            Add(P^.GetFloatProp(Value),DOUBLE_PRECISION);
        end;
        {$ifdef HASVARUSTRING}
        tkUString: begin // write converted to UTF-8
          US := P^.GetUnicodeStrProp(Value);
          if (US<>'') or not (woDontStoreEmptyString in Options) then begin
            HR(P);
            Add('"');
            AddJSONEscapeW(pointer(US));
            Add('"');
................................................................................
      result := smvRawJSON else
    if P=TypeInfo(RawByteString) then
      result := smvRawByteString else
  {$ifndef UNICODE}
    if P=TypeInfo(AnsiString) then
      result := smvString else
      result := smvRawUTF8; // UTF-8 by default
  {$ifdef HASVARUSTRING}
  tkUString:
    result := smvRawUTF8;
  {$endif}
  {$else UNICODE}
      result := smvRawUTF8;
  tkUString:
    result := smvString;
  {$endif UNICODE}
  tkWString:
    result := smvWideString;
  tkClass:
    with P^.ClassType^ do
    if ClassHasPublishedFields(ClassType) or
       (JSONObject(ClassType,IsObjCustomIndex,[cpRead,cpWrite]) in
         [{$ifndef LVCL}oCollection,{$endif}oObjectList,oUtfs,oStrings,

Changes to SQLite3/mORMotUIOptions.pas.

289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
...
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
          end;
        tkLString: begin
            CLE := TLabeledEdit.Create(Scroll);
            if P^.PropType^=TypeInfo(RawUTF8) then
              CLE.Text := U2S(P^.GetLongStrValue(Obj)) else
              CLE.Text := P^.GetGenericStringValue(Obj);
          end;
        {$ifdef UNICODE}
        tkUString: begin
            CLE := TLabeledEdit.Create(Scroll);
            CLE.Text := P^.GetUnicodeStrValue(Obj);
          end;
        {$endif}
        tkClass: begin
          O := pointer(P^.GetOrdValue(Obj));
................................................................................
          Application.ProcessMessages;
          CNE.SetFocus;                   // focus corresponding field
          ShowMessage(CNE.EditLabel.Caption+':'#13+E.Message,true);
          exit;
        end;
      end else
      if C.InheritsFrom(TLabeledEdit) then
        {$ifdef UNICODE}
        if P^.PropType^^.Kind=tkUString then
          P^.SetUnicodeStrValue(Obj,CLE.Text) else
        {$endif}
          if P^.PropType^=TypeInfo(RawUTF8) then
            P^.SetLongStrValue(Obj,S2U(CLE.Text)) else
            P^.SetGenericStringValue(Obj,CLE.Text) else
      if C.InheritsFrom(TCheckBox) then






|







 







|







289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
...
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
          end;
        tkLString: begin
            CLE := TLabeledEdit.Create(Scroll);
            if P^.PropType^=TypeInfo(RawUTF8) then
              CLE.Text := U2S(P^.GetLongStrValue(Obj)) else
              CLE.Text := P^.GetGenericStringValue(Obj);
          end;
        {$ifdef HASVARUSTRING}
        tkUString: begin
            CLE := TLabeledEdit.Create(Scroll);
            CLE.Text := P^.GetUnicodeStrValue(Obj);
          end;
        {$endif}
        tkClass: begin
          O := pointer(P^.GetOrdValue(Obj));
................................................................................
          Application.ProcessMessages;
          CNE.SetFocus;                   // focus corresponding field
          ShowMessage(CNE.EditLabel.Caption+':'#13+E.Message,true);
          exit;
        end;
      end else
      if C.InheritsFrom(TLabeledEdit) then
        {$ifdef HASVARUSTRING}
        if P^.PropType^^.Kind=tkUString then
          P^.SetUnicodeStrValue(Obj,CLE.Text) else
        {$endif}
          if P^.PropType^=TypeInfo(RawUTF8) then
            P^.SetLongStrValue(Obj,S2U(CLE.Text)) else
            P^.SetGenericStringValue(Obj,CLE.Text) else
      if C.InheritsFrom(TCheckBox) then

Changes to SynBidirSock.pas.

1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
          raise ESynBidirSocket.CreateUTF8('%.GetFrame: received %, expected %',
            [self,OpcodeText(opcode)^,OpcodeText(Frame.opcode)^]);
      GetData(data);
      Frame.payload := Frame.payload+data;
    end;
    if (fProtocol<>nil) and (Frame.payload<>'') then
      fProtocol.AfterGetFrame(Frame);
    {$ifdef UNICODE}
    if opcode=focText then
      SetCodePage(Frame.payload,CP_UTF8,false); // identify text value as UTF-8
    {$endif}
    Log(frame,'GetFrame');
    SetLastPingTicks;
    result := true;
  finally






|







1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
          raise ESynBidirSocket.CreateUTF8('%.GetFrame: received %, expected %',
            [self,OpcodeText(opcode)^,OpcodeText(Frame.opcode)^]);
      GetData(data);
      Frame.payload := Frame.payload+data;
    end;
    if (fProtocol<>nil) and (Frame.payload<>'') then
      fProtocol.AfterGetFrame(Frame);
    {$ifdef HASCODEPAGE}
    if opcode=focText then
      SetCodePage(Frame.payload,CP_UTF8,false); // identify text value as UTF-8
    {$endif}
    Log(frame,'GetFrame');
    SetLastPingTicks;
    result := true;
  finally

Changes to SynCommons.pas.

937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952

953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982





983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
....
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
....
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
....
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
....
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
....
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
....
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
....
9326
9327
9328
9329
9330
9331
9332
9333
9334
9335
9336
9337
9338
9339
9340
9341
9342
9343
9344
9345
9346
9347
9348
9349
9350
9351
9352
9353
9354
9355
9356
9357
9358
9359
9360
9361
9362
9363
9364
9365
9366
9367
.....
14860
14861
14862
14863
14864
14865
14866
14867
14868
14869
14870
14871
14872
14873
14874
.....
14903
14904
14905
14906
14907
14908
14909
14910
14911
14912
14913
14914
14915
14916
14917
.....
14936
14937
14938
14939
14940
14941
14942



14943
14944
14945
14946
14947
14948
14949
.....
15346
15347
15348
15349
15350
15351
15352



15353
15354
15355
15356
15357



15358
15359
15360
15361
15362
15363
15364
.....
15480
15481
15482
15483
15484
15485
15486
15487
15488
15489
15490
15491
15492
15493
15494
15495
15496
15497
15498
.....
16161
16162
16163
16164
16165
16166
16167
16168
16169
16170
16171
16172
16173
16174
16175
16176
16177
.....
16185
16186
16187
16188
16189
16190
16191
16192
16193
16194
16195
16196
16197
16198
16199
.....
16398
16399
16400
16401
16402
16403
16404
16405
16406
16407
16408
16409
16410
16411
16412
.....
16497
16498
16499
16500
16501
16502
16503
16504
16505
16506
16507
16508
16509
16510
16511
16512
16513
.....
17976
17977
17978
17979
17980
17981
17982
17983
17984
17985
17986
17987
17988
17989
17990
.....
17999
18000
18001
18002
18003
18004
18005
18006
18007
18008
18009
18010
18011
18012
18013
.....
18310
18311
18312
18313
18314
18315
18316
18317
18318
18319
18320
18321
18322
18323
18324
.....
18407
18408
18409
18410
18411
18412
18413
18414
18415
18416
18417
18418
18419
18420
18421
.....
19644
19645
19646
19647
19648
19649
19650
19651
19652
19653
19654
19655
19656
19657
19658
19659
19660
19661
19662
19663
19664
19665
19666
19667
19668
19669
19670
19671
19672
19673
19674
19675
19676
19677
19678
19679
19680
19681
19682
19683
19684
19685
19686
19687
19688
19689
19690
19691
19692
19693
19694
.....
21795
21796
21797
21798
21799
21800
21801
21802
21803
21804
21805
21806
21807
21808
21809
21810
21811
21812
21813
21814
21815
21816
21817
21818
21819
21820
21821
21822
21823
21824
21825
21826
21827
21828
21829
.....
21837
21838
21839
21840
21841
21842
21843
21844
21845
21846
21847
21848
21849
21850
21851
.....
21864
21865
21866
21867
21868
21869
21870
21871
21872
21873
21874
21875
21876
21877
21878
21879
21880
21881
21882
21883
21884
21885
21886
21887
21888
21889
21890
21891
21892
21893
21894
21895
21896
.....
23415
23416
23417
23418
23419
23420
23421
23422
23423
23424
23425
23426
23427
23428
23429
.....
29155
29156
29157
29158
29159
29160
29161
29162
29163
29164
29165
29166
29167
29168
29169
.....
30183
30184
30185
30186
30187
30188
30189
30190
30191
30192
30193
30194
30195
30196
30197
.....
30302
30303
30304
30305
30306
30307
30308
30309
30310
30311
30312
30313
30314
30315
30316
.....
30384
30385
30386
30387
30388
30389
30390
30391
30392
30393
30394
30395
30396
30397
30398
.....
30457
30458
30459
30460
30461
30462
30463
30464
30465
30466
30467
30468
30469
30470
30471
30472
30473
30474
30475
30476
.....
30537
30538
30539
30540
30541
30542
30543
30544
30545
30546
30547
30548
30549
30550
30551
.....
30558
30559
30560
30561
30562
30563
30564

30565
30566
30567
30568
30569
30570
30571
30572
30573
30574
30575
30576
30577
30578
30579
.....
30664
30665
30666
30667
30668
30669
30670
30671
30672
30673
30674
30675
30676
30677
30678
30679
30680
30681
30682
30683
30684
30685
30686
30687
30688
30689
30690
30691
30692
30693
.....
33609
33610
33611
33612
33613
33614
33615
33616
33617
33618
33619
33620
33621
33622
33623
33624
33625
33626
33627
.....
33632
33633
33634
33635
33636
33637
33638
33639
33640
33641
33642
33643
33644
33645
33646
33647
33648
33649
33650
.....
33970
33971
33972
33973
33974
33975
33976
33977
33978
33979
33980
33981
33982
33983
33984
.....
37167
37168
37169
37170
37171
37172
37173
37174
37175
37176
37177
37178
37179
37180
37181
37182
37183
37184
37185
37186
37187
.....
37257
37258
37259
37260
37261
37262
37263
37264
37265
37266
37267
37268
37269
37270
37271
.....
37920
37921
37922
37923
37924
37925
37926
37927
37928
37929
37930
37931
37932
37933
37934
37935
37936
37937
37938
37939
37940
37941
37942
37943
37944
37945
37946
37947
37948
37949
37950
.....
38303
38304
38305
38306
38307
38308
38309
38310
38311
38312
38313
38314
38315
38316
38317
.....
38359
38360
38361
38362
38363
38364
38365
38366
38367
38368
38369
38370
38371
38372
38373
.....
38436
38437
38438
38439
38440
38441
38442
38443
38444
38445
38446
38447
38448
38449
38450
.....
38765
38766
38767
38768
38769
38770
38771
38772
38773
38774
38775
38776
38777
38778
38779
.....
38801
38802
38803
38804
38805
38806
38807
38808
38809
38810
38811
38812
38813
38814
38815
.....
38850
38851
38852
38853
38854
38855
38856
38857
38858
38859
38860
38861
38862
38863
38864
38865
38866
38867
38868
38869
38870
38871
38872
38873
.....
38880
38881
38882
38883
38884
38885
38886
38887
38888
38889
38890
38891
38892
38893
38894
.....
38910
38911
38912
38913
38914
38915
38916
38917
38918
38919
38920
38921
38922
38923
38924
38925
38926
38927
38928
38929
38930
38931
38932
38933
38934
38935
38936
38937
38938
38939
38940
.....
39217
39218
39219
39220
39221
39222
39223
39224
39225
39226
39227
39228
39229
39230
39231
39232
39233
.....
39236
39237
39238
39239
39240
39241
39242
39243
39244
39245
39246
39247
39248
39249
39250
39251
39252
39253
39254
.....
39255
39256
39257
39258
39259
39260
39261
39262
39263
39264
39265
39266
39267
39268
39269
39270
39271
39272
39273
39274
39275
39276
39277
39278
39279
39280
39281
39282
39283
39284
39285
39286
39287
39288
.....
40906
40907
40908
40909
40910
40911
40912
40913
40914
40915
40916
40917
40918
40919
40920
.....
40927
40928
40929
40930
40931
40932
40933
40934
40935
40936
40937
40938
40939
40940
40941
.....
41839
41840
41841
41842
41843
41844
41845
41846
41847
41848
41849
41850
41851
41852
41853
.....
42084
42085
42086
42087
42088
42089
42090
42091
42092
42093
42094
42095
42096
42097
42098
42099
42100
42101
42102
42103
42104
42105
42106
42107
.....
42145
42146
42147
42148
42149
42150
42151
42152
42153
42154
42155
42156
42157
42158
42159
.....
42549
42550
42551
42552
42553
42554
42555
42556



42557

42558
42559
42560
42561
42562
42563
42564
  /// a pointer to a variant array
  PVariantArray = ^TVariantArray;

  /// a dynamic array of variant values
  TVariantDynArray = array of variant;
{$endif}

/// RawUnicode is an Unicode String stored in an AnsiString
// - faster than WideString, which are allocated in Global heap (for COM)
// - an AnsiChar(#0) is added at the end, for having a true WideChar(#0) at ending
// - length(RawUnicode) returns memory bytes count: use (length(RawUnicode) shr 1)
// for WideChar count (that's why the definition of this type since Delphi 2009
// is AnsiString(1200) and not UnicodeString)
// - pointer(RawUnicode) is compatible with Win32 'Wide' API call
// - mimic Delphi 2009 UnicodeString, without the WideString or Ansi conversion overhead
// - all conversion to/from AnsiString or RawUTF8 must be explicit

  {$ifdef UNICODE}
  RawUnicode = type AnsiString(CP_UTF16); // Codepage for an UnicodeString
  {$else}
  RawUnicode = type AnsiString;
  {$endif}

  /// RawUTF8 is an UTF-8 String stored in an AnsiString
  // - use this type instead of System.UTF8String, which behavior changed
  // between Delphi 2009 compiler and previous versions: our implementation
  // is consistent and compatible with all versions of Delphi compiler
  // - mimic Delphi 2009 UTF8String, without the charset conversion overhead
  // - all conversion to/from AnsiString or RawUnicode must be explicit
  {$ifdef UNICODE}
  RawUTF8 = type AnsiString(CP_UTF8); // Codepage for an UTF8 string
  {$else}
  RawUTF8 = type AnsiString;
  {$endif}

  /// WinAnsiString is a WinAnsi-encoded AnsiString (code page 1252)
  // - use this type instead of System.String, which behavior changed
  // between Delphi 2009 compiler and previous versions: our implementation
  // is consistent and compatible with all versions of Delphi compiler
  // - all conversion to/from RawUTF8 or RawUnicode must be explicit
  {$ifdef UNICODE}
  WinAnsiString = type AnsiString(1252); // WinAnsi Codepage
  {$else}
  WinAnsiString = type AnsiString;
  {$endif}

{$ifndef UNICODE}





  /// define RawByteString, as it does exist in Delphi 2009+
  // - to be used for byte storage into an AnsiString
  // - use this type if you don't want the Delphi compiler not to do any
  // code page conversions when you assign a typed AnsiString to a RawByteString,
  // i.e. a RawUTF8 or a WinAnsiString
  RawByteString = type AnsiString;
  /// pointer to a RawByteString
  PRawByteString = ^RawByteString;
{$endif}

  /// RawJSON will indicate that this variable content would stay in raw JSON
  // - i.e. won't be serialized into values
  // - could be any JSON content: number, string, object or array
  // - e.g. interface-based service will use it for efficient and AJAX-ready
  // transmission of TSQLTableJSON result
  RawJSON = type RawUTF8;
................................................................................
function UTF8DecodeToUnicodeString(const S: RawUTF8): UnicodeString; overload; inline;

/// convert our UTF-8 encoded buffer into a Delphi 2009+ Unicode string
// - this function is the same as direct assignment, since RawUTF8=AnsiString(CP_UTF8),
// but is faster, since use no Win32 API call
procedure UTF8DecodeToUnicodeString(P: PUTF8Char; L: integer; var result: UnicodeString); overload;

{$endif HASVARUSTRING}

{$ifdef UNICODE}

/// convert a Delphi 2009+ Unicode string into a WinAnsi (code page 1252) string
function UnicodeStringToWinAnsi(const S: string): WinAnsiString; inline;

/// convert our UTF-8 encoded buffer into a Delphi 2009+ Unicode string
// - this function is the same as direct assignment, since RawUTF8=AnsiString(CP_UTF8),
// but is faster, since use no Win32 API call
function UTF8DecodeToUnicodeString(P: PUTF8Char; L: integer): UnicodeString; overload; inline;
................................................................................
// - this function is faster than default RTL, since use no Win32 API call
function WinAnsiToUnicodeString(WinAnsi: PAnsiChar; WinAnsiLen: integer): UnicodeString; overload;

/// convert a Win-Ansi string into a Delphi 2009+ Unicode string
// - this function is faster than default RTL, since use no Win32 API call
function WinAnsiToUnicodeString(const WinAnsi: WinAnsiString): UnicodeString; inline; overload;

{$endif UNICODE}

/// convert any generic VCL Text into an UTF-8 encoded String
// - it's prefered to use TLanguageFile.StringToUTF8() method in mORMoti18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
................................................................................
  {$ifdef UNICODE}inline;{$endif}

/// convert any UTF-8 encoded buffer into a generic VCL Text
procedure UTF8DecodeToString(P: PUTF8Char; L: integer; var result: string); overload;

/// convert any UTF-8 encoded String into a generic WideString Text
function UTF8ToWideString(const Text: RawUTF8): WideString; overload;
  {$ifdef UNICODE}inline;{$endif}

/// convert any UTF-8 encoded String into a generic WideString Text
procedure UTF8ToWideString(const Text: RawUTF8; var result: WideString); overload;
  {$ifdef UNICODE}inline;{$endif}

/// convert any UTF-8 encoded String into a generic WideString Text
procedure UTF8ToWideString(Text: PUTF8Char; Len: integer; var result: WideString); overload;

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

................................................................................
// - just a wrapper around PosEx(substr,str,1)
function Pos(const substr, str: RawUTF8): Integer; overload; inline;
{$endif UNICODE}

/// use our fast RawUTF8 version of IntToStr()
// - without any slow UnicodeString=String->AnsiString conversion for Delphi 2009
// - only useful if our Enhanced Runtime (or LVCL) library is not installed
function Int64ToUtf8(Value: Int64): RawByteString; overload;
  {$ifdef PUREPASCAL}{$ifdef HASINLINE}inline;{$endif}{$endif}

/// use our fast RawUTF8 version of IntToStr()
// - without any slow UnicodeString=String->AnsiString conversion for Delphi 2009
// - only useful if our Enhanced Runtime (or LVCL) library is not installed
function Int32ToUtf8(Value: integer): RawByteString; overload;
  {$ifdef PUREPASCAL}{$ifdef HASINLINE}inline;{$endif}{$endif}

/// use our fast RawUTF8 version of IntToStr()
// - without any slow UnicodeString=String->AnsiString conversion for Delphi 2009
// - result as var parameter saves a local assignment and a try..finally
procedure Int32ToUTF8(Value: integer; var result: RawUTF8); overload;
  {$ifdef HASINLINE}inline;{$endif}
................................................................................
/// use our fast RawUTF8 version of IntToStr()
// - without any slow UnicodeString=String->AnsiString conversion for Delphi 2009
// - result as var parameter saves a local assignment and a try..finally
procedure Int64ToUtf8(Value: Int64; var result: RawUTF8); overload;
  {$ifdef HASINLINE}inline;{$endif}

/// use our fast RawUTF8 version of IntToStr()
function ToUTF8(Value: PtrInt): RawByteString; overload;
  {$ifdef HASINLINE}inline;{$endif}

{$ifndef CPU64}
/// use our fast RawUTF8 version of IntToStr()
function ToUTF8(Value: Int64): RawByteString; overload;
  {$ifdef PUREPASCAL}{$ifdef HASINLINE}inline;{$endif}{$endif}
{$endif}

/// optimized conversion of a cardinal into RawUTF8
function UInt32ToUtf8(Value: cardinal): RawByteString; overload;
  {$ifdef HASINLINE}inline;{$endif}

/// optimized conversion of a cardinal into RawUTF8
procedure UInt32ToUtf8(Value: cardinal; var result: RawUTF8); overload;
  {$ifdef HASINLINE}inline;{$endif}

/// faster version than default SysUtils.IntToStr implementation
................................................................................
// - warning: will encode generic string fields as AnsiString (one byte per char)
// prior to Delphi 2009, and as UnicodeString (two bytes per char) since Delphi
// 2009: if you want to use this function between UNICODE and NOT UNICODE
// versions of Delphi, you should use some explicit types like RawUTF8,
// WinAnsiString, SynUnicode or even RawUnicode/WideString
function RecordSave(const Rec; Dest: PAnsiChar; TypeInfo: pointer): PAnsiChar; overload;

/// save a record content into a Base-64 encoded RawByteString content
// - will use RecordSave() format, with a left-sided binary CRC
function RecordSaveBase64(const Rec; TypeInfo: pointer; UriCompatible: boolean=false): RawByteString;

/// compute the number of bytes needed to save a record content
// using the RecordSave() function
// - will return 0 in case of an invalid (not handled) record type (e.g. if
// it contains an unknown variant)
function RecordSaveLength(const Rec; TypeInfo: pointer): integer;

................................................................................
function HexDisplayToBin(Hex: PAnsiChar; Bin: PByte; BinBytes: integer): boolean;

/// fast conversion from hexa chars into a cardinal
function HexDisplayToCardinal(Hex: PAnsiChar; out aValue: cardinal): boolean;
    {$ifndef FPC}{$ifdef HASINLINE}inline;{$endif}{$endif}
    // inline gives an error under release conditions with FPC

/// 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-like URI-compatible encoded text
// - will trim any right-sided '=' unsignificant characters, and replace
// '+' or '/' by '_' or '-'
function BinToBase64URI(Bin: PAnsiChar; BinBytes: integer): RawByteString;

/// conversion from any Base64 encoded value into URI-compatible encoded text
// - will trim any right-sided '=' unsignificant characters, and replace
// '+' or '/' by '_' or '-'
procedure Base64ToURI(var base64: RawByteString);

/// conversion from URI-compatible encoded text into its original Base64 value
// - will add any right-sided '=' unsignificant characters, and replace back
// '_' or '-' by '+' or '/'
procedure Base64FromURI(var base64: RawByteString);

/// fast conversion from binary data into Base64 encoded text
// with JSON_BASE64_MAGIC prefix (UTF-8 encoded \uFFF0 special code)
function BinToBase64WithMagic(const s: RawByteString): RawByteString; overload;

/// fast conversion from binary data into Base64 encoded text
// with JSON_BASE64_MAGIC prefix (UTF-8 encoded \uFFF0 special code)
function BinToBase64WithMagic(Data: pointer; DataLen: integer): RawByteString; overload;

/// fast conversion from Base64 encoded text into binary data
function Base64ToBin(const s: RawByteString): RawByteString; overload;
  {$ifdef HASINLINE}inline;{$endif}

/// fast conversion from Base64 encoded text into binary data
function Base64ToBin(sp: PAnsiChar; len: PtrInt): RawByteString; overload;
................................................................................
    result := '' else begin
    if SourceChars<SizeOf(tmpA)shr fAnsiCharShift then
      SetString(result,tmpA,Utf8BufferToAnsi(tmpA,Source,SourceChars)-tmpA) else begin
      Getmem(A,(SourceChars+1) shl fAnsiCharShift);
      SetString(result,A,Utf8BufferToAnsi(A,Source,SourceChars)-A);
      FreeMem(A);
    end;
    {$ifdef UNICODE}
    SetCodePage(result,fCodePage,false);
    {$endif}
  end;
end;

function TSynAnsiConvert.UTF8ToAnsi(const UTF8: RawUTF8): RawByteString;
begin
................................................................................
    result := '' else begin
    if SourceChars<SizeOf(tmpA)shr fAnsiCharShift then
      SetString(result,tmpA,UnicodeBufferToAnsi(tmpA,Source,SourceChars)-tmpA) else begin
      Getmem(A,(SourceChars+1) shl fAnsiCharShift);
      SetString(result,A,UnicodeBufferToAnsi(A,Source,SourceChars)-A);
      FreeMem(A);
    end;
    {$ifdef UNICODE}
    SetCodePage(result,fCodePage,false);
    {$endif}
  end;
end;

function TSynAnsiConvert.RawUnicodeToAnsi(const Source: RawUnicode): RawByteString;
begin
................................................................................
  if SourceChars<sizeof(tmpU) shr 1 then
    result := UnicodeBufferToAnsi(tmpU,
      (PtrUInt(From.AnsiBufferToUnicode(tmpU,Source,SourceChars))-PtrUInt(@tmpU))shr 1) else begin
    GetMem(U,SourceChars*2+2);
    result := UnicodeBufferToAnsi(U,From.AnsiBufferToUnicode(U,Source,SourceChars)-U);
    FreeMem(U);
  end;



end;


{ TSynAnsiFixedWidth }

function TSynAnsiFixedWidth.AnsiBufferToUnicode(Dest: PWideChar;
  Source: PAnsiChar; SourceChars: Cardinal): PWideChar;
................................................................................
begin
  SetString(Result,Source,SourceChars);
end;

function TSynAnsiUTF8.UTF8ToAnsi(const UTF8: RawUTF8): RawByteString;
begin
  result := UTF8;



end;

function TSynAnsiUTF8.AnsiToUTF8(const AnsiText: RawByteString): RawUTF8;
begin
  result := AnsiText;



end;

function TSynAnsiUTF8.AnsiBufferToRawUTF8(Source: PAnsiChar; SourceChars: Cardinal): RawUTF8;
begin
  SetString(Result,Source,SourceChars);
end;

................................................................................
    Dest[j] := AnsiChar((ucs4 and $3f)+$80);
    ucs4 := ucs4 shr 6;
  end;
  Dest^ := AnsiChar(ToByte(ucs4) or UTF8_FIRSTBYTE[result]);
end;

procedure AnyAnsiToUTF8(const s: RawByteString; var result: RawUTF8);
{$ifdef UNICODE}var CodePage: Cardinal;{$endif}
begin
  if s='' then
    result := '' else begin
    {$ifdef UNICODE}
    CodePage := StringCodePage(s);
    if (CodePage=CP_UTF8) or (CodePage=CP_RAWBYTESTRING) then
      result := s else
      result := TSynAnsiConvert.Engine(CodePage).
    {$else}
    result := CurrentAnsiConvert.
    {$endif}
................................................................................
  if L<sizeof(short)div 3 then
    SetString(result,short,UTF8ToWideChar(short,P,L) shr 1) else begin
    GetMem(U,L*3+2); // maximum posible unicode size (if all <#128)
    SetString(result,U,UTF8ToWideChar(U,P,L) shr 1);
    FreeMem(U);
  end;
end;
{$endif}

{$ifdef UNICODE}
function UnicodeStringToWinAnsi(const S: string): WinAnsiString;
begin
  result := RawUnicodeToWinAnsi(pointer(S),length(S));
end;

function UTF8DecodeToUnicodeString(P: PUTF8Char; L: integer): UnicodeString;
begin
................................................................................
end;

function WinAnsiToUnicodeString(const WinAnsi: WinAnsiString): UnicodeString;
begin
  result := WinAnsiToUnicodeString(pointer(WinAnsi),length(WinAnsi));
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
................................................................................
    end;
    vtAnsiString: begin // expect UTF-8 content
      Res.Text := pointer(V.VAnsiString);
      Res.Len := length(RawUTF8(V.VAnsiString));
      result := Res.Len;
      exit;
    end;
    {$ifdef UNICODE}
    vtUnicodeString:
      RawUnicodeToUtf8(V.VPWideChar,length(UnicodeString(V.VUnicodeString)),tmpStr);
    {$endif}
    vtWideString:
      RawUnicodeToUtf8(V.VPWideChar,length(WideString(V.VWideString)),tmpStr);
    vtPChar: begin
      Res.Text := V.VPointer;
................................................................................
  isString := not (V.VType in [vtBoolean,vtInteger,vtInt64,vtCurrency,vtExtended]);
  with V do
  case V.VType of
    vtString:
      SetRawUTF8(result,@VString^[1],ord(VString^[0]));
    vtAnsiString:
      result := RawUTF8(VAnsiString); // expect UTF-8 content
    {$ifdef UNICODE}
    vtUnicodeString:
      result := UnicodeStringToUtf8(string(VUnicodeString));
    {$endif}
    vtWideString:
      RawUnicodeToUtf8(VWideString,length(WideString(VWideString)),result);
    vtPChar:
      SetRawUTF8(result,VPChar,StrLen(VPChar));
    vtChar:
      SetRawUTF8(result,PAnsiChar(@VChar),1);
................................................................................
    Curr64ToStr(VInt64,result);
  varDate: begin
    wasString := true;
    DateTimeToIso8601TextVar(VDate,'T',result);
  end;
  varString: begin
    wasString := true;
  {$ifdef UNICODE}
    AnyAnsiToUTF8(RawByteString(VString),result);
  {$else}
    result := RawUTF8(VString);
  {$endif}
  end;
  {$ifdef HASVARUSTRING}
  varUString: begin
................................................................................
  else
  if SetVariantUnRefSimpleValue(V,tmp) then
    VariantToUTF8(Variant(tmp),result,wasString) else
  if VType=varVariant or varByRef then // complex varByRef
    VariantToUTF8(PVariant(VPointer)^,result,wasString) else
  if VType=varByRef or varString then begin
    wasString := true;
    {$ifdef UNICODE}
      AnyAnsiToUTF8(PRawByteString(VString)^,result);
    {$else}
      result := PRawUTF8(VString)^;
    {$endif}
  end else
  if VType=varByRef or varOleStr then begin
    wasString := true;
................................................................................

{$ifndef PUREPASCAL} { these functions are implemented in asm }
{$ifndef LVCL} { don't define these functions twice }
{$ifndef FPC}  { these asm function use some low-level system.pas calls }

{$define OWNI2S}

function Int32ToUTF8(Value : integer): RawByteString; // 3x faster than SysUtils.IntToStr
// from IntToStr32_JOH_IA32_6_a, adapted for Delphi 2009+
asm // eax=Value, edx=@result
  push   ebx
  push   edi
  push   esi
  mov    ebx,eax                {Value}
  sar    ebx,31                 {0 for +ve Value or -1 for -ve Value}
................................................................................
  mov    [ecx],ax                {Save Final 2 Digits}
  ret
@@LastDigit:
  or     al,'0'                  {Ascii Adjustment}
  mov    [ecx],al                {Save Final Digit}
end;

function Int64ToUTF8(Value: Int64): RawByteString;
// from IntToStr64_JOH_IA32_6_b, adapted for Delphi 2009+
asm
  push   ebx
  mov    ecx, [ebp+8]            {Low Integer of Value}
  mov    edx, [ebp+12]           {High Integer of Value}
  xor    ebp, ebp                {Clear Sign Flag (EBP Already Pushed)}
  mov    ebx, ecx                {Low Integer of Value}
................................................................................
  SetLength(result,Gen-pointer(result));
  inc(maxParam);
end;


{$ifndef OWNI2S}

function Int32ToUTF8(Value : integer): RawByteString; // faster than SysUtils.IntToStr
var tmp: array[0..15] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrInt32(@tmp[15],Value);
  SetString(result,P,@tmp[15]-P);
end;

function Int64ToUtf8(Value: Int64): RawByteString; // faster than SysUtils.IntToStr
var tmp: array[0..23] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrInt64(@tmp[23],Value);
  SetString(result,P,@tmp[23]-P);
end;

{$endif}

{$ifndef CPU64} // already implemented by ToUTF8(Value: PtrInt) below
function ToUTF8(Value: Int64): RawByteString;
var tmp: array[0..23] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrInt64(@tmp[23],Value);
  SetString(result,P,@tmp[23]-P);
end;
{$endif}

function ToUTF8(Value: PtrInt): RawByteString;
var tmp: array[0..15] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrInt32(@tmp[15],Value);
  SetString(result,P,@tmp[15]-P);
end;

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

................................................................................
end;

function BinToBase64Length(len: PtrUInt): PtrUInt;
begin
  result := ((len+2)div 3)*4;
end;

function BinToBase64(const s: RawByteString): RawByteString;
var len: integer;
begin
  result := '';
  len := length(s);
  if len=0 then
    exit;
  SetLength(result,BinToBase64Length(len));
  Base64Encode(pointer(result),pointer(s),len);
end;

function BinToBase64(Bin: PAnsiChar; BinBytes: integer): RawByteString;
begin
  result := '';
  if BinBytes=0 then
    exit;
  SetLength(result,BinToBase64Length(BinBytes));
  Base64Encode(pointer(result),Bin,BinBytes);
end;

procedure Base64ToURI(var base64: RawByteString);
var P: PUTF8Char;
begin
  {$ifdef FPC}
  UniqueString(base64); // @base64[1] won't call UniqueString() under FPC :(
  {$endif}
  P := @base64[1];
  if P<>nil then
................................................................................
        break;
      end;
      end;
      inc(P);
    until false;
end;

procedure Base64FromURI(var base64: RawByteString);
var P: PUTF8Char;
    len,i,append: integer;
begin
  len := length(base64);
  if len=0 then
    exit;
  {$ifdef FPC}
................................................................................
  if append<>4 then begin // add unsignificant trailing '=' characters
    SetLength(base64,len+append);
    for i := len+1 to len+append do
      base64[i] := '=';
  end;
end;

function BinToBase64URI(Bin: PAnsiChar; BinBytes: integer): RawByteString;
begin
  result := BinToBase64(Bin,BinBytes);
  Base64ToURI(result);
end;

function BinToBase64WithMagic(const s: RawByteString): RawByteString;
var len: integer;
begin
  result:='';
  len := length(s);
  if len=0 then
    exit;
  SetLength(result,((len+2) div 3)*4+3);
  PInteger(pointer(result))^ := JSON_BASE64_MAGIC;
  Base64Encode(PAnsiChar(pointer(result))+3,pointer(s),len);
end;

function BinToBase64WithMagic(Data: pointer; DataLen: integer): RawByteString; overload;
begin
  result:='';
  if DataLen<=0 then
    exit;
  SetLength(result,((DataLen+2) div 3)*4+3);
  PInteger(pointer(result))^ := JSON_BASE64_MAGIC;
  Base64Encode(PAnsiChar(pointer(result))+3,Data,DataLen);
................................................................................
    while S[L]<=' ' do dec(L);
    result := Copy(S,I,L-I+1);
  end;
end;
{$endif}

{$IFDEF PUREPASCAL}
{$IFDEF UNICODE}
function Trim(const S: RawUTF8): RawUTF8;
var I,L: Integer;
begin
  L := Length(S);
  I := 1;
  while (I<=L) and (S[I]<=' ') do inc(I);
  if I>L then
................................................................................
     j := PosEx(endboundary,Body,i); // try last boundary
     if j=0 then
      exit;
    end;
    part.Content := copy(Body,i,j-i-2); // -2 to ignore latest #13#10
    if (part.ContentType='') or (PosEx('-8',part.ContentType)>0) then begin
      part.ContentType := TEXT_CONTENT_TYPE;
      {$ifdef UNICODE}
      SetCodePage(part.Content,CP_UTF8,false); // ensure raw field value is UTF-8
      {$endif}
    end else
    if IdemPropNameU(part.Encoding,'base64') then
      part.Content := Base64ToBin(part.Content);
    // note: "quoted-printable" not yet handled here
    SetLength(MultiPart,length(MultiPart)+1);
................................................................................
procedure FromVarString(var Source: PByte; var Value: RawByteString; CodePage: integer);
var Len: PtrUInt;
begin
  Len := FromVarUInt32(Source);
  if Len=0 then
    exit;
  SetString(Value,PAnsiChar(Source),Len);
  {$ifdef UNICODE}
  SetCodePage(Value,CodePage,false);
  {$endif}
  inc(Source,Len);
end;

function FromVarBlob(Data: PByte): TValueResult;
begin
................................................................................
        if PAnsiString(A)^=PAnsiString(B)^ then
          Diff := sizeof(pointer) else
          exit;
      tkWString:
        if PWideString(A)^=PWideString(B)^ then
          Diff := sizeof(pointer) else
          exit;
      {$ifdef UNICODE}
      tkUString:
        if PUnicodeString(A)^=PUnicodeString(B)^ then
          Diff := sizeof(pointer) else
          exit;
      {$endif}
      tkRecord{$ifdef FPC},tkObject{$endif}:
        if RecordEquals(A^,B^,Field^.TypeInfo{$ifndef FPC}^{$endif}) then
................................................................................
        inc(result,DynArray.SaveToLength-sizeof(PtrUInt));
      end;
      tkLString,tkWString{$ifdef FPC},tkLStringOld{$endif}:
        // length stored within WideString is in bytes
        if P^=0 then
          dec(result,sizeof(PtrUInt)-1) else
          inc(result,ToVarUInt32LengthWithData(PStrRec(Pointer(P^-STRRECSIZE))^.length)-sizeof(PtrUInt));
      {$ifdef UNICODE}
      tkUString:
        if P^=0 then
          dec(result,sizeof(PtrUInt)-1) else
          inc(result,ToVarUInt32LengthWithData(PStrRec(Pointer(P^-STRRECSIZE))^.length*2)-sizeof(PtrUInt));
      {$endif}
      tkRecord{$ifdef FPC},tkObject{$endif}: begin
        Len := RecordSaveLength(P^,Field.TypeInfo{$ifndef FPC}^{$endif});
................................................................................
    Kind := Field.TypeInfo^.Kind;
    case Kind of
    tkDynArray: begin
      DynArray.Init(Field.TypeInfo{$ifndef FPC}^{$endif},R^);
      Dest := DynArray.SaveTo(Dest);
      Diff := sizeof(PtrUInt); // size of tkDynArray in record
    end;
    tkLString, tkWString {$ifdef UNICODE}, tkUString{$endif}
    {$ifdef FPC}, tkLStringOld{$endif}: begin
      if PPtrUInt(R)^=0 then
        LenBytes := 0 else
        LenBytes := PStrRec(Pointer(PPtrUInt(R)^-STRRECSIZE))^.length;
      {$ifdef UNICODE} // WideString has length in bytes, UnicodeString in WideChars
      if Kind=tkUString then
        LenBytes := LenBytes*2;
      {$endif}
      Dest := pointer(ToVarUInt32(LenBytes,pointer(Dest)));
      if LenBytes>0 then begin
        MoveFast(pointer(PPtrUInt(R)^)^,Dest^,LenBytes);
        inc(Dest,LenBytes);
................................................................................
begin
  Len := RecordSaveLength(Rec,TypeInfo);
  SetString(result,nil,Len);
  if Len<>0 then
    RecordSave(Rec,pointer(result),TypeInfo);
end;

function RecordSaveBase64(const Rec; TypeInfo: pointer; UriCompatible: boolean): RawByteString;
var len: integer;
    data: RawByteString;
    dat: PAnsiChar;
begin
  result := '';
  len := RecordSaveLength(Rec,TypeInfo);
  if len=0 then
................................................................................
  if UriCompatible then
    Base64ToURI(result);
end;

function RecordLoadBase64(Source: PAnsiChar; Len: integer; var Rec;
  TypeInfo: pointer; UriCompatible: boolean): boolean;
var data: RawByteString;

begin
  result := false;
  if Len<=6 then
    exit;
  if UriCompatible then begin
    SetString(data,Source,Len);
    Base64FromURI(data);
    data := Base64ToBin(data);
  end else
    data := Base64ToBin(Source,Len);
  Len := length(data);
  if Len<=4 then
    exit;
  Source := PAnsiChar(pointer(data))+4;
  if crc32c(0,Source,Len-4)=PCardinal(data)^ then
................................................................................
    Kind := Field.TypeInfo^.Kind;
    case Kind of
    tkDynArray: begin
      DynArray.Init(Field.TypeInfo{$ifndef FPC}^{$endif},R^);
      Source := DynArray.LoadFrom(Source);
      Diff := sizeof(PtrUInt); // size of tkDynArray in record
    end;
    tkLString, tkWString {$ifdef UNICODE}, tkUString{$endif}
    {$ifdef FPC}, tkLStringOld{$endif}: begin
      LenBytes := FromVarUInt32(PByte(Source));
      case Kind of
        tkLString{$ifdef FPC},tkLStringOld{$endif}: begin
          SetString(PRawByteString(R)^,Source,LenBytes);
          {$ifdef UNICODE}
          { Delphi 2009+: set Code page for this AnsiString }
          if LenBytes<>0 then
            SetCodePage(PRawByteString(R)^,PWord(PtrUInt(Field.TypeInfo^)+
              Field.TypeInfo^^.NameLen+2)^,false);
          {$endif}
        end;
        tkWString:
          SetString(PWideString(R)^,PWideChar(Source),LenBytes shr 1);
        {$ifdef UNICODE}
        tkUString:
          SetString(PString(R)^,PWideChar(Source),LenBytes shr 1);
        {$endif}
      end;
      inc(Source,LenBytes);
      Diff := sizeof(PtrUInt); // size of tkLString+tkWString+tkUString in record
    end;
................................................................................
      if VType and VTYPE_STATIC<>0 then
        VarClear(Value);
      VType := varString;
      VAny := nil; // avoid GPF below when assigning a string variable to VAny
      if Txt='' then
        exit;
    end;
    {$ifdef UNICODE}
    if (PByte(Txt)<>nil) and (PWord(PByte(Txt)-12)^=CP_RAWBYTESTRING) then
      PWord(PByte(Txt)-12)^ := CP_UTF8; // force explicit UTF-8
    {$endif}
    RawByteString(VAny) := Txt;
  end;
end;

function RawUTF8ToVariant(const Txt: RawUTF8): variant;
begin
  RawUTF8ToVariant(Txt,result);
end;
................................................................................
  if Value.VType and VTYPE_STATIC<>0 then
    VarClear(variant(Value));
  Value.VType := ExpectedValueType;
  Value.VAny := nil; // avoid GPF below
  if Txt<>'' then
  case ExpectedValueType of
    varString: begin
      {$ifdef UNICODE}
      if PWord(PByte(Txt)-12)^=CP_RAWBYTESTRING then
        PWord(PByte(Txt)-12)^ := CP_UTF8; // force explicit UTF-8
      {$endif}
      RawByteString(Value.VAny) := Txt;
    end;
    varOleStr:
      UTF8ToWideString(Txt,WideString(Value.VAny));
    {$ifdef HASVARUSTRING}
    varUString:
      UTF8DecodeToUnicodeString(pointer(Txt),length(Txt),UnicodeString(Value.VAny));
    {$endif}
................................................................................
    vtVariant:
      result := V.VVariant^;
    vtAnsiString: begin
      VType := varString;
      VAny := nil;
      RawByteString(VAny) := RawByteString(V.VAnsiString);
    end;
    vtString, {$ifdef UNICODE}vtUnicodeString,{$endif}
    vtPChar, vtChar, vtWideChar, vtWideString, vtClass: begin
      VType := varString;
      VAny := nil; // avoid GPF on next line
      VarRecToUTF8(V,RawUTF8(VAny));
    end;
    vtObject: // class instance will be serialized as a TDocVariant
      ObjectToVariant(V.VObject,result);
................................................................................
        [PShortString(@PTypeInfo(ArrayType).NameLen)^]) else begin
      // binary types: store as once
      n := n*integer(ElemSize);
      MoveFast(P^,Dest^,n);
      inc(Dest,n);
    end else
    case PTypeKind(ElemType)^ of
    tkLString, tkWString {$ifdef UNICODE}, tkUString{$endif}
    {$ifdef FPC}, tkLStringOld{$endif}: begin
      for i := 1 to n do begin
        if PPtrUInt(P)^=0 then
          LenBytes := 0 else begin
          LenBytes := PStrRec(Pointer(PPtrUInt(P)^-STRRECSIZE))^.length;
          {$ifdef UNICODE} // WideString length in bytes, UnicodeString in WideChars
          if PTypeKind(ElemType)^=tkUString then
            LenBytes := LenBytes*2;
          {$endif}
        end;
        Dest := pointer(ToVarUInt32(LenBytes,pointer(Dest)));
        if LenBytes>0 then begin
          MoveFast(pointer(PPtrUInt(P)^)^,Dest^,LenBytes);
................................................................................
    tkLString, tkWString{$ifdef FPC}, tkLStringOld{$endif}:
      for i := 1 to n do begin
        if PPtrUInt(P)^=0 then
          inc(result) else
          inc(result,ToVarUInt32LengthWithData(PStrRec(Pointer(PPtrUInt(P)^-STRRECSIZE))^.length));
        inc(P,sizeof(PtrUInt));
      end;
    {$ifdef UNICODE}
    tkUString: // WideString has length in bytes, UnicodeString in WideChars
      for i := 1 to n do begin
        if PPtrUInt(P)^=0 then
          inc(result) else
          inc(result,ToVarUInt32LengthWithData(PStrRec(Pointer(PPtrUInt(P)^-STRRECSIZE))^.length*2));
        inc(P,sizeof(PtrUInt));
      end;
................................................................................
      [PShortString(@PTypeInfo(ArrayType).NameLen)^]) else begin
    // binary type was stored as once
    n := n*integer(ElemSize);
    MoveFast(Source^,P^,n);
    inc(Source,n);
  end else
  case PTypeKind(ElemType)^ of
    tkLString, tkWString {$ifdef UNICODE}, tkUString{$endif}
    {$ifdef FPC}, tkLStringOld{$endif}:
    for i := 1 to n do begin
      LenBytes := FromVarUInt32(PByte(Source));
      case PTypeKind(ElemType)^ of
      tkLString{$ifdef FPC},tkLStringOld{$endif}: begin
        SetString(PRawByteString(P)^,Source,LenBytes);
        {$ifdef UNICODE}
        { Delphi 2009+: set Code page for this AnsiString }
        if LenBytes<>0 then
          SetCodePage(PRawByteString(P)^,PWord(PtrUInt(ElemType)+
            PTypeInfo(ElemType)^.NameLen+2)^,false);
        {$endif}
      end;
      tkWString:
        SetString(PWideString(P)^,PWideChar(Source),LenBytes shr 1);
      {$ifdef UNICODE}
      tkUString:
        SetString(PString(P)^,PWideChar(Source),LenBytes shr 1);
      {$endif}
      end;
      inc(Source,LenBytes);
      inc(P,sizeof(PtrUInt));
    end;
................................................................................
    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 UNICODE}
    tkUString:
      result := UnicodeString(A)=UnicodeString(B);
    {$endif}
    tkInterface:
      result := pointer(A)=pointer(B);
    {$ifndef NOVARIANTS}
    tkVariant:
................................................................................
    for i := 0 to n-1 do
      if AnsiString(A1^[i])<>AnsiString(A2^[i]) then
        exit;
  tkWString:
    for i := 0 to n-1 do
      if WideString(A1^[i])<>WideString(A2^[i]) then
        exit;
  {$ifdef UNICODE}
  tkUString:
    for i := 0 to n-1 do
      if UnicodeString(A1^[i])<>UnicodeString(A2^[i]) then
        exit;
  {$endif}
  tkInterface:
    if not CompareMem(P1,P2,SizeOf(pointer)*cardinal(n)) then
................................................................................
  case PTypeKind(ElemType)^ of
  tkLString{$ifdef FPC},tkLStringOld{$endif}:
    for result := 0 to max do
      if AnsiString(PPtrIntArray(P)^[result])=AnsiString(Elem) then exit;
  tkWString:
    for result := 0 to max do
      if WideString(PPtrIntArray(P)^[result])=WideString(Elem) then exit;
  {$ifdef UNICODE}
  tkUString:
    for result := 0 to max do
      if UnicodeString(PPtrIntArray(P)^[result])=UnicodeString(Elem) then exit;
  {$endif}
  {$ifndef NOVARIANTS}
  tkVariant:
    for result := 0 to max do
................................................................................
    case PTypeKind(ElemType)^ of // release reference counted
      tkLString{$ifdef FPC},tkLStringOld{$endif}:
        RawByteString(Elem) := '';
      tkWString:
        WideString(Elem) := '';
      tkInterface:
        IUnknown(Elem) := nil;
      {$ifdef UNICODE}
      tkUString:
        UnicodeString(Elem) := '';
      {$endif}
      tkRecord{$ifdef FPC},tkObject{$endif}:
        RecordClear(Elem,ElemType);
      tkDynArray:
        _DynArrayClear(pointer(Elem),ElemType);
................................................................................
        WideString(B) := WideString(A);
        exit;
      end;
      tkInterface: begin
        IUnknown(B) := IUnknown(A);
        exit;
      end;
      {$ifdef UNICODE}
      tkUString: begin
        UnicodeString(B) := UnicodeString(A);
        exit;
      end;
      {$endif}
      tkRecord{$ifdef FPC},tkObject{$endif}: begin
        RecordCopy(B,A,ElemType);
................................................................................
  if Source=nil then
    exit; // avoid GPF
  if ElemType=nil then
    MoveFast(Source^,Elem,ElemSize) else
    case PTypeKind(ElemType)^ of
    tkLString{$ifdef FPC},tkLStringOld{$endif}: begin
      SetString(RawByteString(Elem),Source+4,PInteger(Source)^);
      {$ifdef UNICODE}
      { Delphi 2009+: set Code page for this AnsiString }
      if PPtrUInt(@Elem)^<>0 then
        SetCodePage(RawByteString(Elem),PWord(PtrUInt(ElemType)+
          PTypeInfo(ElemType)^.NameLen+2)^,false);
      {$endif}
    end;
    tkWString: // WideString internal length is in bytes
      SetString(WideString(Elem),PWideChar(Source+4),PInteger(Source)^ shr 1);
    {$ifdef UNICODE}
    tkUString:
      SetString(UnicodeString(Elem),PWideChar(Source+4),PInteger(Source)^);
    {$endif}
    {$ifndef NOVARIANTS}
    tkVariant:
      VariantLoad(variant(Elem),Source,@JSON_OPTIONS[true]);
    {$endif}
................................................................................
begin
  if (ElemType<>nil) and (length(ElemLoaded)=integer(ElemSize)) then
  case PTypeKind(ElemType)^ of
    tkLString{$ifdef FPC},tkLStringOld{$endif}:
      PRawByteString(pointer(ElemLoaded))^ := '';
    tkWString:
      PWideString(pointer(ElemLoaded))^ := '';
    {$ifdef UNICODE}
    tkUString:
      PUnicodeString(pointer(ElemLoaded))^ := '';
    {$endif}
    {$ifndef NOVARIANTS}
    tkVariant:
      VarClear(PVariant(pointer(ElemLoaded))^);
    {$endif}
................................................................................
      tkLString, tkWString, tkLStringOld:
        if PPtrInt(@Elem)^=0 then
          SetString(result,PAnsiChar(@Elem),4) else begin
          LenBytes := PStrRec(Pointer(PPtrInt(@Elem)^-STRRECSIZE))^.length;
          SetString(result,PAnsiChar(PPtrInt(@Elem)^-sizeof(integer)),LenBytes+sizeof(integer));
          PInteger(result)^ := LenBytes;
        end;
      {$ifdef UNICODE}
      tkUString:
        if PPtrInt(@Elem)^=0 then
          SetString(result,PAnsiChar(@Elem),4) else begin
          LenBytes := PStrRec(Pointer(PPtrInt(@Elem)^-STRRECSIZE))^.length;
          SetString(result,PAnsiChar(PPtrInt(@Elem)^-sizeof(integer)),LenBytes*2+sizeof(integer));
          PInteger(result)^ := LenBytes;
        end;
      end;
      {$endif}
      {$else FPC}
      tkLString, tkWString: // WideString internal length is in bytes
        if PPtrInt(@Elem)^=0 then
          SetString(result,PAnsiChar(@Elem),4) else
          SetString(result,PAnsiChar(PPtrInt(@Elem)^-sizeof(integer)),
            PInteger(PPtrInt(@Elem)^-sizeof(integer))^+sizeof(integer));
      {$ifdef UNICODE}
      tkUString:
        if PPtrInt(@Elem)^=0 then
          SetString(result,PAnsiChar(@Elem),4) else
          SetString(result,PAnsiChar(PPtrInt(@Elem)^-sizeof(integer)),
            PInteger(PPtrInt(@Elem)^-sizeof(integer))^*2+sizeof(integer));
      {$endif}
      {$endif FPC}
................................................................................

{$ifdef UNICODE}

function HashUnicodeString(const Elem; Hasher: THasher): cardinal;
begin
  if PtrUInt(Elem)=0 then
    result := HASH_ONVOIDCOLISION else
    result := Hasher(0,Pointer(PtrUInt(Elem)),
      {$ifdef FPC}PStrRec(Pointer(PtrUInt(Elem)-STRRECSIZE))^.length
      {$else}PInteger(PtrUInt(Elem)-sizeof(integer))^{$endif}*2);
end;

function HashUnicodeStringI(const Elem; Hasher: THasher): cardinal;
var tmp: array[byte] of AnsiChar; // avoid slow heap allocation
begin
  if PtrUInt(Elem)=0 then
    result := HASH_ONVOIDCOLISION else
................................................................................

{$endif UNICODE}

function HashSynUnicode(const Elem; Hasher: THasher): cardinal;
begin
  if PtrUInt(Elem)=0 then
    result := HASH_ONVOIDCOLISION else
    result := Hasher(0,Pointer(PtrUInt(Elem)),
      {$ifdef FPC}PStrRec(Pointer(PtrUInt(Elem)-STRRECSIZE))^.length
      {$else}PInteger(PtrUInt(Elem)-sizeof(integer))^{$endif}
      {$ifdef UNICODE}*sizeof(WideChar){$endif});
      // WideString internal size is in bytes, UnicodeString is in WideChars
end;

function HashSynUnicodeI(const Elem; Hasher: THasher): cardinal;
var tmp: array[byte] of AnsiChar; // avoid slow heap allocation
begin
  if PtrUInt(Elem)=0 then
    result := HASH_ONVOIDCOLISION else
................................................................................
    result := Hasher(0,tmp,UpperCopy255W(tmp,SynUnicode(Elem))-tmp);
end;

function HashWideString(const Elem; Hasher: THasher): cardinal;
begin // WideString internal size is in bytes, not WideChar
  if PtrUInt(Elem)=0 then
    result := HASH_ONVOIDCOLISION else
    result := Hasher(0,Pointer(PtrUInt(Elem)),
      {$ifdef FPC}PStrRec(Pointer(PtrUInt(Elem)-STRRECSIZE))^.length
      {$else}PInteger(PtrUInt(Elem)-sizeof(integer))^{$endif});
end;

function HashWideStringI(const Elem; Hasher: THasher): cardinal;
var tmp: array[byte] of AnsiChar; // avoid slow heap allocation
begin
  if PtrUInt(Elem)=0 then
    result := HASH_ONVOIDCOLISION else
    result := Hasher(0,tmp,UpperCopy255W(tmp,pointer(Elem),Length(WideString(Elem)))-tmp);
end;

function HashPtrUInt(const Elem; Hasher: THasher): cardinal;
begin
{$ifdef CPU64}
  result := Hasher(0,@Elem,sizeof(PtrUInt));
{$else}
  result := (PtrUInt(Elem) shr 4)+1; // naive but optimal for TDynArrayHashed
{$endif}
end;

function HashPointer(const Elem; Hasher: THasher): cardinal;
begin
  result := Hasher(0,@Elem,sizeof(pointer));
end;

................................................................................
  varDate:     AddDateTime(@VDate,'T','"');
  varCurrency: AddCurr64(VInt64);
  varBoolean:  Add(VBoolean);
  varVariant:  AddVariant(PVariant(VPointer)^,Escape,ForcedSerializeAsNonExtendedJson);
  varString: begin
    if Escape=twJSONEscape then
      Add('"');
    {$ifdef UNICODE}
    AddAnyAnsiString(RawByteString(VString),Escape);
    {$else}  // VString is expected to be a RawUTF8
    Add(VAny,length(RawUTF8(VAny)),Escape);
    {$endif}
    if Escape=twJSONEscape then
      Add('"');
  end;
................................................................................
  end;
  else
  if VType=varVariant or varByRef then
    AddVariant(PVariant(VPointer)^,Escape,ForcedSerializeAsNonExtendedJson) else
  if VType=varByRef or varString then begin
    if Escape=twJSONEscape then
      Add('"');
    {$ifdef UNICODE}
    AddAnyAnsiString(PRawByteString(VAny)^,Escape);
    {$else}  // VString is expected to be a RawUTF8
    Add(PPointer(VAny)^,length(PRawUTF8(VAny)^),Escape);
    {$endif}
    if Escape=twJSONEscape then
      Add('"');
  end else
................................................................................
  if L=0 then
    exit;
  if PInteger(s)^ and $ffffff=JSON_BASE64_MAGIC then begin
    AddNoJSONEscape(pointer(s),L); // identified as a BLOB content
    exit;
  end;
  if CodePage<0 then
    {$ifdef UNICODE}
    CodePage := StringCodePage(s);
    {$else}
    CodePage := 0; // TSynAnsiConvert.Engine(0)=CurrentAnsiConvert
    {$endif}
  AddAnyAnsiBuffer(pointer(s),L,Escape,CodePage);
end;

................................................................................
end;

procedure TTextWriter.AddJSONEscape(const V: TVarRec);
begin
  with V do
  case VType of
    vtPointer: AddShort('null');
    vtString, vtAnsiString,{$ifdef UNICODE}vtUnicodeString,{$endif}
    vtPChar, vtChar, vtWideChar, vtWideString, vtClass: begin
      Add('"');
      case VType of
        vtString:     AddJSONEscape(@VString^[1],ord(VString^[0]));
        vtAnsiString: AddJSONEscape(pointer(RawUTF8(VAnsiString)));
    {$ifdef UNICODE}
        vtUnicodeString: AddJSONEscapeW(
          pointer(string(VUnicodeString)),length(string(VUnicodeString)));
    {$endif}
        vtPChar:      AddJSONEscape(VPChar);
        vtChar:       AddJSONEscape(@VChar,1);
        vtWideChar:   AddJSONEscapeW(@VWideChar,1);
        vtWideString: AddJSONEscapeW(VWideString);
        vtClass:      AddClassName(VClass);
      end;
      Add('"');
................................................................................
      AddW(VWideString,length(WideString(VWideString)),Escape);
  vtInt64:
    Add(VInt64^);
  {$ifndef NOVARIANTS}
  vtVariant:
    AddVariant(VVariant^,Escape);
  {$endif}
  {$ifdef UNICODE}
  vtUnicodeString:
    if VUnicodeString<>nil then // convert to UTF-8
      AddW(VUnicodeString,length(UnicodeString(VUnicodeString)),Escape);
  {$endif} end;
end;

{$ifndef NOVARIANTS}
................................................................................
begin
  FlushFinal;
  Len := fTotalFileSize-fInitialStreamPosition;
  if Len=0 then
    result := '' else
  if fStream.InheritsFrom(TRawByteStringStream) then
    with TRawByteStringStream(fStream) do
    if fInitialStreamPosition=0 then



      result := DataString else

      SetRawUTF8(result,PAnsiChar(pointer(DataString))+fInitialStreamPosition,Len) else
  if fStream.InheritsFrom(TCustomMemoryStream) then
    with TCustomMemoryStream(fStream) do
    SetRawUTF8(result,PAnsiChar(Memory)+fInitialStreamPosition,Len) else begin
    FastNewRawUTF8(result,Len);
    fStream.Seek(fInitialStreamPosition,soBeginning);
    fStream.Read(pointer(result)^,Len);






|
|
|
|
|
|
|
|
|
>
|











|










|
|




|
>
>
>
>
>








|







 







<
<
<
<







 







|







 







|



|







 







|





|







 







|




|




|







 







|

|







 







|
|

|
|




|




|




|

|

|

|

|







 







|







 







|







 







>
>
>







 







>
>
>





>
>
>







 







|



|







 







<

<







 







|







 







|







 







|

|







 







|







 







|







 







|







 







|







 







|







|










|








|







|







 







|










|








|







 







|







 







|





|











|







 







|







 







|







 







|







 







|







 







|







 







|




|







 







|







 







>





|
|
|







 







|





|


|
|




|







 







|
|
|
|
|







 







|
|
|
|
|







 







|







 







|





|







 







|







 







|






|








|







 







|







 







|







 







|







 







|







 







|







 







|








|







 







|







 







|







<







|







 







|
<
<







 







|
<
<
<
<







 







|
<
<












|

|

|







 







|







 







|







 







|







 







|





|

|
|







 







|







 







|
>
>
>
|
>







937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
....
1764
1765
1766
1767
1768
1769
1770




1771
1772
1773
1774
1775
1776
1777
....
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
....
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
....
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
....
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
....
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
....
9328
9329
9330
9331
9332
9333
9334
9335
9336
9337
9338
9339
9340
9341
9342
9343
9344
9345
9346
9347
9348
9349
9350
9351
9352
9353
9354
9355
9356
9357
9358
9359
9360
9361
9362
9363
9364
9365
9366
9367
9368
9369
.....
14862
14863
14864
14865
14866
14867
14868
14869
14870
14871
14872
14873
14874
14875
14876
.....
14905
14906
14907
14908
14909
14910
14911
14912
14913
14914
14915
14916
14917
14918
14919
.....
14938
14939
14940
14941
14942
14943
14944
14945
14946
14947
14948
14949
14950
14951
14952
14953
14954
.....
15351
15352
15353
15354
15355
15356
15357
15358
15359
15360
15361
15362
15363
15364
15365
15366
15367
15368
15369
15370
15371
15372
15373
15374
15375
.....
15491
15492
15493
15494
15495
15496
15497
15498
15499
15500
15501
15502
15503
15504
15505
15506
15507
15508
15509
.....
16172
16173
16174
16175
16176
16177
16178

16179

16180
16181
16182
16183
16184
16185
16186
.....
16194
16195
16196
16197
16198
16199
16200
16201
16202
16203
16204
16205
16206
16207
16208
.....
16407
16408
16409
16410
16411
16412
16413
16414
16415
16416
16417
16418
16419
16420
16421
.....
16506
16507
16508
16509
16510
16511
16512
16513
16514
16515
16516
16517
16518
16519
16520
16521
16522
.....
17985
17986
17987
17988
17989
17990
17991
17992
17993
17994
17995
17996
17997
17998
17999
.....
18008
18009
18010
18011
18012
18013
18014
18015
18016
18017
18018
18019
18020
18021
18022
.....
18319
18320
18321
18322
18323
18324
18325
18326
18327
18328
18329
18330
18331
18332
18333
.....
18416
18417
18418
18419
18420
18421
18422
18423
18424
18425
18426
18427
18428
18429
18430
.....
19653
19654
19655
19656
19657
19658
19659
19660
19661
19662
19663
19664
19665
19666
19667
19668
19669
19670
19671
19672
19673
19674
19675
19676
19677
19678
19679
19680
19681
19682
19683
19684
19685
19686
19687
19688
19689
19690
19691
19692
19693
19694
19695
19696
19697
19698
19699
19700
19701
19702
19703
.....
21804
21805
21806
21807
21808
21809
21810
21811
21812
21813
21814
21815
21816
21817
21818
21819
21820
21821
21822
21823
21824
21825
21826
21827
21828
21829
21830
21831
21832
21833
21834
21835
21836
21837
21838
.....
21846
21847
21848
21849
21850
21851
21852
21853
21854
21855
21856
21857
21858
21859
21860
.....
21873
21874
21875
21876
21877
21878
21879
21880
21881
21882
21883
21884
21885
21886
21887
21888
21889
21890
21891
21892
21893
21894
21895
21896
21897
21898
21899
21900
21901
21902
21903
21904
21905
.....
23424
23425
23426
23427
23428
23429
23430
23431
23432
23433
23434
23435
23436
23437
23438
.....
29164
29165
29166
29167
29168
29169
29170
29171
29172
29173
29174
29175
29176
29177
29178
.....
30192
30193
30194
30195
30196
30197
30198
30199
30200
30201
30202
30203
30204
30205
30206
.....
30311
30312
30313
30314
30315
30316
30317
30318
30319
30320
30321
30322
30323
30324
30325
.....
30393
30394
30395
30396
30397
30398
30399
30400
30401
30402
30403
30404
30405
30406
30407
.....
30466
30467
30468
30469
30470
30471
30472
30473
30474
30475
30476
30477
30478
30479
30480
30481
30482
30483
30484
30485
.....
30546
30547
30548
30549
30550
30551
30552
30553
30554
30555
30556
30557
30558
30559
30560
.....
30567
30568
30569
30570
30571
30572
30573
30574
30575
30576
30577
30578
30579
30580
30581
30582
30583
30584
30585
30586
30587
30588
30589
.....
30674
30675
30676
30677
30678
30679
30680
30681
30682
30683
30684
30685
30686
30687
30688
30689
30690
30691
30692
30693
30694
30695
30696
30697
30698
30699
30700
30701
30702
30703
.....
33619
33620
33621
33622
33623
33624
33625
33626
33627
33628
33629
33630
33631
33632
33633
33634
33635
33636
33637
.....
33642
33643
33644
33645
33646
33647
33648
33649
33650
33651
33652
33653
33654
33655
33656
33657
33658
33659
33660
.....
33980
33981
33982
33983
33984
33985
33986
33987
33988
33989
33990
33991
33992
33993
33994
.....
37177
37178
37179
37180
37181
37182
37183
37184
37185
37186
37187
37188
37189
37190
37191
37192
37193
37194
37195
37196
37197
.....
37267
37268
37269
37270
37271
37272
37273
37274
37275
37276
37277
37278
37279
37280
37281
.....
37930
37931
37932
37933
37934
37935
37936
37937
37938
37939
37940
37941
37942
37943
37944
37945
37946
37947
37948
37949
37950
37951
37952
37953
37954
37955
37956
37957
37958
37959
37960
.....
38313
38314
38315
38316
38317
38318
38319
38320
38321
38322
38323
38324
38325
38326
38327
.....
38369
38370
38371
38372
38373
38374
38375
38376
38377
38378
38379
38380
38381
38382
38383
.....
38446
38447
38448
38449
38450
38451
38452
38453
38454
38455
38456
38457
38458
38459
38460
.....
38775
38776
38777
38778
38779
38780
38781
38782
38783
38784
38785
38786
38787
38788
38789
.....
38811
38812
38813
38814
38815
38816
38817
38818
38819
38820
38821
38822
38823
38824
38825
.....
38860
38861
38862
38863
38864
38865
38866
38867
38868
38869
38870
38871
38872
38873
38874
38875
38876
38877
38878
38879
38880
38881
38882
38883
.....
38890
38891
38892
38893
38894
38895
38896
38897
38898
38899
38900
38901
38902
38903
38904
.....
38920
38921
38922
38923
38924
38925
38926
38927
38928
38929
38930
38931
38932
38933
38934

38935
38936
38937
38938
38939
38940
38941
38942
38943
38944
38945
38946
38947
38948
38949
.....
39226
39227
39228
39229
39230
39231
39232
39233


39234
39235
39236
39237
39238
39239
39240
.....
39243
39244
39245
39246
39247
39248
39249
39250




39251
39252
39253
39254
39255
39256
39257
.....
39258
39259
39260
39261
39262
39263
39264
39265


39266
39267
39268
39269
39270
39271
39272
39273
39274
39275
39276
39277
39278
39279
39280
39281
39282
39283
39284
39285
39286
39287
39288
39289
.....
40907
40908
40909
40910
40911
40912
40913
40914
40915
40916
40917
40918
40919
40920
40921
.....
40928
40929
40930
40931
40932
40933
40934
40935
40936
40937
40938
40939
40940
40941
40942
.....
41840
41841
41842
41843
41844
41845
41846
41847
41848
41849
41850
41851
41852
41853
41854
.....
42085
42086
42087
42088
42089
42090
42091
42092
42093
42094
42095
42096
42097
42098
42099
42100
42101
42102
42103
42104
42105
42106
42107
42108
.....
42146
42147
42148
42149
42150
42151
42152
42153
42154
42155
42156
42157
42158
42159
42160
.....
42550
42551
42552
42553
42554
42555
42556
42557
42558
42559
42560
42561
42562
42563
42564
42565
42566
42567
42568
42569
  /// a pointer to a variant array
  PVariantArray = ^TVariantArray;

  /// a dynamic array of variant values
  TVariantDynArray = array of variant;
{$endif}

  /// RawUnicode is an Unicode String stored in an AnsiString
  // - faster than WideString, which are allocated in Global heap (for COM)
  // - an AnsiChar(#0) is added at the end, for having a true WideChar(#0) at ending
  // - length(RawUnicode) returns memory bytes count: use (length(RawUnicode) shr 1)
  // for WideChar count (that's why the definition of this type since Delphi 2009
  // is AnsiString(1200) and not UnicodeString)
  // - pointer(RawUnicode) is compatible with Win32 'Wide' API call
  // - mimic Delphi 2009 UnicodeString, without the WideString or Ansi conversion overhead
  // - all conversion to/from AnsiString or RawUTF8 must be explicit: the
  // compiler is not able to make valid implicit conversion on CP_UTF16
  {$ifdef HASCODEPAGE}
  RawUnicode = type AnsiString(CP_UTF16); // Codepage for an UnicodeString
  {$else}
  RawUnicode = type AnsiString;
  {$endif}

  /// RawUTF8 is an UTF-8 String stored in an AnsiString
  // - use this type instead of System.UTF8String, which behavior changed
  // between Delphi 2009 compiler and previous versions: our implementation
  // is consistent and compatible with all versions of Delphi compiler
  // - mimic Delphi 2009 UTF8String, without the charset conversion overhead
  // - all conversion to/from AnsiString or RawUnicode must be explicit
  {$ifdef HASCODEPAGE}
  RawUTF8 = type AnsiString(CP_UTF8); // Codepage for an UTF8 string
  {$else}
  RawUTF8 = type AnsiString;
  {$endif}

  /// WinAnsiString is a WinAnsi-encoded AnsiString (code page 1252)
  // - use this type instead of System.String, which behavior changed
  // between Delphi 2009 compiler and previous versions: our implementation
  // is consistent and compatible with all versions of Delphi compiler
  // - all conversion to/from RawUTF8 or RawUnicode must be explicit
  {$ifdef HASCODEPAGE}
  WinAnsiString = type AnsiString(CODEPAGE_US); // WinAnsi Codepage
  {$else}
  WinAnsiString = type AnsiString;
  {$endif}

  {$ifdef HASCODEPAGE}
  {$ifdef FPC}
  // missing declaration
  PRawByteString = ^RawByteString;
  {$endif}
  {$else}
  /// define RawByteString, as it does exist in Delphi 2009+
  // - to be used for byte storage into an AnsiString
  // - use this type if you don't want the Delphi compiler not to do any
  // code page conversions when you assign a typed AnsiString to a RawByteString,
  // i.e. a RawUTF8 or a WinAnsiString
  RawByteString = type AnsiString;
  /// pointer to a RawByteString
  PRawByteString = ^RawByteString;
  {$endif}

  /// RawJSON will indicate that this variable content would stay in raw JSON
  // - i.e. won't be serialized into values
  // - could be any JSON content: number, string, object or array
  // - e.g. interface-based service will use it for efficient and AJAX-ready
  // transmission of TSQLTableJSON result
  RawJSON = type RawUTF8;
................................................................................
function UTF8DecodeToUnicodeString(const S: RawUTF8): UnicodeString; overload; inline;

/// convert our UTF-8 encoded buffer into a Delphi 2009+ Unicode string
// - this function is the same as direct assignment, since RawUTF8=AnsiString(CP_UTF8),
// but is faster, since use no Win32 API call
procedure UTF8DecodeToUnicodeString(P: PUTF8Char; L: integer; var result: UnicodeString); overload;





/// convert a Delphi 2009+ Unicode string into a WinAnsi (code page 1252) string
function UnicodeStringToWinAnsi(const S: string): WinAnsiString; inline;

/// convert our UTF-8 encoded buffer into a Delphi 2009+ Unicode string
// - this function is the same as direct assignment, since RawUTF8=AnsiString(CP_UTF8),
// but is faster, since use no Win32 API call
function UTF8DecodeToUnicodeString(P: PUTF8Char; L: integer): UnicodeString; overload; inline;
................................................................................
// - this function is faster than default RTL, since use no Win32 API call
function WinAnsiToUnicodeString(WinAnsi: PAnsiChar; WinAnsiLen: integer): UnicodeString; overload;

/// convert a Win-Ansi string into a Delphi 2009+ Unicode string
// - this function is faster than default RTL, since use no Win32 API call
function WinAnsiToUnicodeString(const WinAnsi: WinAnsiString): UnicodeString; inline; overload;

{$endif HASVARUSTRING}

/// convert any generic VCL Text into an UTF-8 encoded String
// - it's prefered to use TLanguageFile.StringToUTF8() method in mORMoti18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
................................................................................
  {$ifdef UNICODE}inline;{$endif}

/// convert any UTF-8 encoded buffer into a generic VCL Text
procedure UTF8DecodeToString(P: PUTF8Char; L: integer; var result: string); overload;

/// convert any UTF-8 encoded String into a generic WideString Text
function UTF8ToWideString(const Text: RawUTF8): WideString; overload;
  {$ifdef HASINLINE}inline;{$endif}

/// convert any UTF-8 encoded String into a generic WideString Text
procedure UTF8ToWideString(const Text: RawUTF8; var result: WideString); overload;
  {$ifdef HASINLINE}inline;{$endif}

/// convert any UTF-8 encoded String into a generic WideString Text
procedure UTF8ToWideString(Text: PUTF8Char; Len: integer; var result: WideString); overload;

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

................................................................................
// - just a wrapper around PosEx(substr,str,1)
function Pos(const substr, str: RawUTF8): Integer; overload; inline;
{$endif UNICODE}

/// use our fast RawUTF8 version of IntToStr()
// - without any slow UnicodeString=String->AnsiString conversion for Delphi 2009
// - only useful if our Enhanced Runtime (or LVCL) library is not installed
function Int64ToUtf8(Value: Int64): RawUTF8; overload;
  {$ifdef PUREPASCAL}{$ifdef HASINLINE}inline;{$endif}{$endif}

/// use our fast RawUTF8 version of IntToStr()
// - without any slow UnicodeString=String->AnsiString conversion for Delphi 2009
// - only useful if our Enhanced Runtime (or LVCL) library is not installed
function Int32ToUtf8(Value: integer): RawUTF8; overload;
  {$ifdef PUREPASCAL}{$ifdef HASINLINE}inline;{$endif}{$endif}

/// use our fast RawUTF8 version of IntToStr()
// - without any slow UnicodeString=String->AnsiString conversion for Delphi 2009
// - result as var parameter saves a local assignment and a try..finally
procedure Int32ToUTF8(Value: integer; var result: RawUTF8); overload;
  {$ifdef HASINLINE}inline;{$endif}
................................................................................
/// use our fast RawUTF8 version of IntToStr()
// - without any slow UnicodeString=String->AnsiString conversion for Delphi 2009
// - result as var parameter saves a local assignment and a try..finally
procedure Int64ToUtf8(Value: Int64; var result: RawUTF8); overload;
  {$ifdef HASINLINE}inline;{$endif}

/// use our fast RawUTF8 version of IntToStr()
function ToUTF8(Value: PtrInt): RawUTF8; overload;
  {$ifdef HASINLINE}inline;{$endif}

{$ifndef CPU64}
/// use our fast RawUTF8 version of IntToStr()
function ToUTF8(Value: Int64): RawUTF8; overload;
  {$ifdef PUREPASCAL}{$ifdef HASINLINE}inline;{$endif}{$endif}
{$endif}

/// optimized conversion of a cardinal into RawUTF8
function UInt32ToUtf8(Value: cardinal): RawUTF8; overload;
  {$ifdef HASINLINE}inline;{$endif}

/// optimized conversion of a cardinal into RawUTF8
procedure UInt32ToUtf8(Value: cardinal; var result: RawUTF8); overload;
  {$ifdef HASINLINE}inline;{$endif}

/// faster version than default SysUtils.IntToStr implementation
................................................................................
// - warning: will encode generic string fields as AnsiString (one byte per char)
// prior to Delphi 2009, and as UnicodeString (two bytes per char) since Delphi
// 2009: if you want to use this function between UNICODE and NOT UNICODE
// versions of Delphi, you should use some explicit types like RawUTF8,
// WinAnsiString, SynUnicode or even RawUnicode/WideString
function RecordSave(const Rec; Dest: PAnsiChar; TypeInfo: pointer): PAnsiChar; overload;

/// save a record content into a Base-64 encoded UTF-8 text content
// - will use RecordSave() format, with a left-sided binary CRC
function RecordSaveBase64(const Rec; TypeInfo: pointer; UriCompatible: boolean=false): RawUTF8;

/// compute the number of bytes needed to save a record content
// using the RecordSave() function
// - will return 0 in case of an invalid (not handled) record type (e.g. if
// it contains an unknown variant)
function RecordSaveLength(const Rec; TypeInfo: pointer): integer;

................................................................................
function HexDisplayToBin(Hex: PAnsiChar; Bin: PByte; BinBytes: integer): boolean;

/// fast conversion from hexa chars into a cardinal
function HexDisplayToCardinal(Hex: PAnsiChar; out aValue: cardinal): boolean;
    {$ifndef FPC}{$ifdef HASINLINE}inline;{$endif}{$endif}
    // inline gives an error under release conditions with FPC

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

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

/// fast conversion from binary data into Base64-like URI-compatible encoded text
// - will trim any right-sided '=' unsignificant characters, and replace
// '+' or '/' by '_' or '-'
function BinToBase64URI(Bin: PAnsiChar; BinBytes: integer): RawUTF8;

/// conversion from any Base64 encoded value into URI-compatible encoded text
// - will trim any right-sided '=' unsignificant characters, and replace
// '+' or '/' by '_' or '-'
procedure Base64ToURI(var base64: RawUTF8);

/// conversion from URI-compatible encoded text into its original Base64 value
// - will add any right-sided '=' unsignificant characters, and replace back
// '_' or '-' by '+' or '/'
procedure Base64FromURI(var base64: RawUTF8);

/// fast conversion from binary data into Base64 encoded UTF-8 text
// with JSON_BASE64_MAGIC prefix (UTF-8 encoded \uFFF0 special code)
function BinToBase64WithMagic(const s: RawByteString): RawUTF8; overload;

/// fast conversion from binary data into Base64 encoded UTF-8 text
// with JSON_BASE64_MAGIC prefix (UTF-8 encoded \uFFF0 special code)
function BinToBase64WithMagic(Data: pointer; DataLen: integer): RawUTF8; overload;

/// fast conversion from Base64 encoded text into binary data
function Base64ToBin(const s: RawByteString): RawByteString; overload;
  {$ifdef HASINLINE}inline;{$endif}

/// fast conversion from Base64 encoded text into binary data
function Base64ToBin(sp: PAnsiChar; len: PtrInt): RawByteString; overload;
................................................................................
    result := '' else begin
    if SourceChars<SizeOf(tmpA)shr fAnsiCharShift then
      SetString(result,tmpA,Utf8BufferToAnsi(tmpA,Source,SourceChars)-tmpA) else begin
      Getmem(A,(SourceChars+1) shl fAnsiCharShift);
      SetString(result,A,Utf8BufferToAnsi(A,Source,SourceChars)-A);
      FreeMem(A);
    end;
    {$ifdef HASCODEPAGE}
    SetCodePage(result,fCodePage,false);
    {$endif}
  end;
end;

function TSynAnsiConvert.UTF8ToAnsi(const UTF8: RawUTF8): RawByteString;
begin
................................................................................
    result := '' else begin
    if SourceChars<SizeOf(tmpA)shr fAnsiCharShift then
      SetString(result,tmpA,UnicodeBufferToAnsi(tmpA,Source,SourceChars)-tmpA) else begin
      Getmem(A,(SourceChars+1) shl fAnsiCharShift);
      SetString(result,A,UnicodeBufferToAnsi(A,Source,SourceChars)-A);
      FreeMem(A);
    end;
    {$ifdef HASCODEPAGE}
    SetCodePage(result,fCodePage,false);
    {$endif}
  end;
end;

function TSynAnsiConvert.RawUnicodeToAnsi(const Source: RawUnicode): RawByteString;
begin
................................................................................
  if SourceChars<sizeof(tmpU) shr 1 then
    result := UnicodeBufferToAnsi(tmpU,
      (PtrUInt(From.AnsiBufferToUnicode(tmpU,Source,SourceChars))-PtrUInt(@tmpU))shr 1) else begin
    GetMem(U,SourceChars*2+2);
    result := UnicodeBufferToAnsi(U,From.AnsiBufferToUnicode(U,Source,SourceChars)-U);
    FreeMem(U);
  end;
  {$ifdef HASCODEPAGE}
  SetCodePage(result,fCodePage,false);
  {$endif}
end;


{ TSynAnsiFixedWidth }

function TSynAnsiFixedWidth.AnsiBufferToUnicode(Dest: PWideChar;
  Source: PAnsiChar; SourceChars: Cardinal): PWideChar;
................................................................................
begin
  SetString(Result,Source,SourceChars);
end;

function TSynAnsiUTF8.UTF8ToAnsi(const UTF8: RawUTF8): RawByteString;
begin
  result := UTF8;
  {$ifdef HASCODEPAGE}
  SetCodePage(result,CP_UTF8,false);
  {$endif}
end;

function TSynAnsiUTF8.AnsiToUTF8(const AnsiText: RawByteString): RawUTF8;
begin
  result := AnsiText;
  {$ifdef HASCODEPAGE}
  SetCodePage(RawByteString(result),CP_UTF8,false);
  {$endif}
end;

function TSynAnsiUTF8.AnsiBufferToRawUTF8(Source: PAnsiChar; SourceChars: Cardinal): RawUTF8;
begin
  SetString(Result,Source,SourceChars);
end;

................................................................................
    Dest[j] := AnsiChar((ucs4 and $3f)+$80);
    ucs4 := ucs4 shr 6;
  end;
  Dest^ := AnsiChar(ToByte(ucs4) or UTF8_FIRSTBYTE[result]);
end;

procedure AnyAnsiToUTF8(const s: RawByteString; var result: RawUTF8);
{$ifdef HASCODEPAGE}var CodePage: Cardinal;{$endif}
begin
  if s='' then
    result := '' else begin
    {$ifdef HASCODEPAGE}
    CodePage := StringCodePage(s);
    if (CodePage=CP_UTF8) or (CodePage=CP_RAWBYTESTRING) then
      result := s else
      result := TSynAnsiConvert.Engine(CodePage).
    {$else}
    result := CurrentAnsiConvert.
    {$endif}
................................................................................
  if L<sizeof(short)div 3 then
    SetString(result,short,UTF8ToWideChar(short,P,L) shr 1) else begin
    GetMem(U,L*3+2); // maximum posible unicode size (if all <#128)
    SetString(result,U,UTF8ToWideChar(U,P,L) shr 1);
    FreeMem(U);
  end;
end;



function UnicodeStringToWinAnsi(const S: string): WinAnsiString;
begin
  result := RawUnicodeToWinAnsi(pointer(S),length(S));
end;

function UTF8DecodeToUnicodeString(P: PUTF8Char; L: integer): UnicodeString;
begin
................................................................................
end;

function WinAnsiToUnicodeString(const WinAnsi: WinAnsiString): UnicodeString;
begin
  result := WinAnsiToUnicodeString(pointer(WinAnsi),length(WinAnsi));
end;

{$endif HASVARUSTRING}

{$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
................................................................................
    end;
    vtAnsiString: begin // expect UTF-8 content
      Res.Text := pointer(V.VAnsiString);
      Res.Len := length(RawUTF8(V.VAnsiString));
      result := Res.Len;
      exit;
    end;
    {$ifdef HASVARUSTRING}
    vtUnicodeString:
      RawUnicodeToUtf8(V.VPWideChar,length(UnicodeString(V.VUnicodeString)),tmpStr);
    {$endif}
    vtWideString:
      RawUnicodeToUtf8(V.VPWideChar,length(WideString(V.VWideString)),tmpStr);
    vtPChar: begin
      Res.Text := V.VPointer;
................................................................................
  isString := not (V.VType in [vtBoolean,vtInteger,vtInt64,vtCurrency,vtExtended]);
  with V do
  case V.VType of
    vtString:
      SetRawUTF8(result,@VString^[1],ord(VString^[0]));
    vtAnsiString:
      result := RawUTF8(VAnsiString); // expect UTF-8 content
    {$ifdef HASVARUSTRING}
    vtUnicodeString:
      result := UnicodeStringToUtf8(UnicodeString(VUnicodeString));
    {$endif}
    vtWideString:
      RawUnicodeToUtf8(VWideString,length(WideString(VWideString)),result);
    vtPChar:
      SetRawUTF8(result,VPChar,StrLen(VPChar));
    vtChar:
      SetRawUTF8(result,PAnsiChar(@VChar),1);
................................................................................
    Curr64ToStr(VInt64,result);
  varDate: begin
    wasString := true;
    DateTimeToIso8601TextVar(VDate,'T',result);
  end;
  varString: begin
    wasString := true;
  {$ifdef HASCODEPAGE}
    AnyAnsiToUTF8(RawByteString(VString),result);
  {$else}
    result := RawUTF8(VString);
  {$endif}
  end;
  {$ifdef HASVARUSTRING}
  varUString: begin
................................................................................
  else
  if SetVariantUnRefSimpleValue(V,tmp) then
    VariantToUTF8(Variant(tmp),result,wasString) else
  if VType=varVariant or varByRef then // complex varByRef
    VariantToUTF8(PVariant(VPointer)^,result,wasString) else
  if VType=varByRef or varString then begin
    wasString := true;
    {$ifdef HASCODEPAGE}
      AnyAnsiToUTF8(PRawByteString(VString)^,result);
    {$else}
      result := PRawUTF8(VString)^;
    {$endif}
  end else
  if VType=varByRef or varOleStr then begin
    wasString := true;
................................................................................

{$ifndef PUREPASCAL} { these functions are implemented in asm }
{$ifndef LVCL} { don't define these functions twice }
{$ifndef FPC}  { these asm function use some low-level system.pas calls }

{$define OWNI2S}

function Int32ToUTF8(Value : integer): RawUtf8; // 3x faster than SysUtils.IntToStr
// from IntToStr32_JOH_IA32_6_a, adapted for Delphi 2009+
asm // eax=Value, edx=@result
  push   ebx
  push   edi
  push   esi
  mov    ebx,eax                {Value}
  sar    ebx,31                 {0 for +ve Value or -1 for -ve Value}
................................................................................
  mov    [ecx],ax                {Save Final 2 Digits}
  ret
@@LastDigit:
  or     al,'0'                  {Ascii Adjustment}
  mov    [ecx],al                {Save Final Digit}
end;

function Int64ToUTF8(Value: Int64): RawUtf8;
// from IntToStr64_JOH_IA32_6_b, adapted for Delphi 2009+
asm
  push   ebx
  mov    ecx, [ebp+8]            {Low Integer of Value}
  mov    edx, [ebp+12]           {High Integer of Value}
  xor    ebp, ebp                {Clear Sign Flag (EBP Already Pushed)}
  mov    ebx, ecx                {Low Integer of Value}
................................................................................
  SetLength(result,Gen-pointer(result));
  inc(maxParam);
end;


{$ifndef OWNI2S}

function Int32ToUTF8(Value : integer): RawUTF8; // faster than SysUtils.IntToStr
var tmp: array[0..15] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrInt32(@tmp[15],Value);
  SetString(result,P,@tmp[15]-P);
end;

function Int64ToUtf8(Value: Int64): RawUTF8; // faster than SysUtils.IntToStr
var tmp: array[0..23] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrInt64(@tmp[23],Value);
  SetString(result,P,@tmp[23]-P);
end;

{$endif}

{$ifndef CPU64} // already implemented by ToUTF8(Value: PtrInt) below
function ToUTF8(Value: Int64): RawUTF8;
var tmp: array[0..23] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrInt64(@tmp[23],Value);
  SetString(result,P,@tmp[23]-P);
end;
{$endif}

function ToUTF8(Value: PtrInt): RawUTF8;
var tmp: array[0..15] of AnsiChar;
    P: PAnsiChar;
begin
  P := StrInt32(@tmp[15],Value);
  SetString(result,P,@tmp[15]-P);
end;

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

................................................................................
end;

function BinToBase64Length(len: PtrUInt): PtrUInt;
begin
  result := ((len+2)div 3)*4;
end;

function BinToBase64(const s: RawByteString): RawUTF8;
var len: integer;
begin
  result := '';
  len := length(s);
  if len=0 then
    exit;
  SetLength(result,BinToBase64Length(len));
  Base64Encode(pointer(result),pointer(s),len);
end;

function BinToBase64(Bin: PAnsiChar; BinBytes: integer): RawUTF8;
begin
  result := '';
  if BinBytes=0 then
    exit;
  SetLength(result,BinToBase64Length(BinBytes));
  Base64Encode(pointer(result),Bin,BinBytes);
end;

procedure Base64ToURI(var base64: RawUTF8);
var P: PUTF8Char;
begin
  {$ifdef FPC}
  UniqueString(base64); // @base64[1] won't call UniqueString() under FPC :(
  {$endif}
  P := @base64[1];
  if P<>nil then
................................................................................
        break;
      end;
      end;
      inc(P);
    until false;
end;

procedure Base64FromURI(var base64: RawUTF8);
var P: PUTF8Char;
    len,i,append: integer;
begin
  len := length(base64);
  if len=0 then
    exit;
  {$ifdef FPC}
................................................................................
  if append<>4 then begin // add unsignificant trailing '=' characters
    SetLength(base64,len+append);
    for i := len+1 to len+append do
      base64[i] := '=';
  end;
end;

function BinToBase64URI(Bin: PAnsiChar; BinBytes: integer): RawUTF8;
begin
  result := BinToBase64(Bin,BinBytes);
  Base64ToURI(result);
end;

function BinToBase64WithMagic(const s: RawByteString): RawUTF8;
var len: integer;
begin
  result:='';
  len := length(s);
  if len=0 then
    exit;
  SetLength(result,((len+2) div 3)*4+3);
  PInteger(pointer(result))^ := JSON_BASE64_MAGIC;
  Base64Encode(PAnsiChar(pointer(result))+3,pointer(s),len);
end;

function BinToBase64WithMagic(Data: pointer; DataLen: integer): RawUTF8; overload;
begin
  result:='';
  if DataLen<=0 then
    exit;
  SetLength(result,((DataLen+2) div 3)*4+3);
  PInteger(pointer(result))^ := JSON_BASE64_MAGIC;
  Base64Encode(PAnsiChar(pointer(result))+3,Data,DataLen);
................................................................................
    while S[L]<=' ' do dec(L);
    result := Copy(S,I,L-I+1);
  end;
end;
{$endif}

{$IFDEF PUREPASCAL}
{$IFDEF HASCODEPAGE}
function Trim(const S: RawUTF8): RawUTF8;
var I,L: Integer;
begin
  L := Length(S);
  I := 1;
  while (I<=L) and (S[I]<=' ') do inc(I);
  if I>L then
................................................................................
     j := PosEx(endboundary,Body,i); // try last boundary
     if j=0 then
      exit;
    end;
    part.Content := copy(Body,i,j-i-2); // -2 to ignore latest #13#10
    if (part.ContentType='') or (PosEx('-8',part.ContentType)>0) then begin
      part.ContentType := TEXT_CONTENT_TYPE;
      {$ifdef HASCODEPAGE}
      SetCodePage(part.Content,CP_UTF8,false); // ensure raw field value is UTF-8
      {$endif}
    end else
    if IdemPropNameU(part.Encoding,'base64') then
      part.Content := Base64ToBin(part.Content);
    // note: "quoted-printable" not yet handled here
    SetLength(MultiPart,length(MultiPart)+1);
................................................................................
procedure FromVarString(var Source: PByte; var Value: RawByteString; CodePage: integer);
var Len: PtrUInt;
begin
  Len := FromVarUInt32(Source);
  if Len=0 then
    exit;
  SetString(Value,PAnsiChar(Source),Len);
  {$ifdef HASCODEPAGE}
  SetCodePage(Value,CodePage,false);
  {$endif}
  inc(Source,Len);
end;

function FromVarBlob(Data: PByte): TValueResult;
begin
................................................................................
        if PAnsiString(A)^=PAnsiString(B)^ then
          Diff := sizeof(pointer) else
          exit;
      tkWString:
        if PWideString(A)^=PWideString(B)^ then
          Diff := sizeof(pointer) else
          exit;
      {$ifdef HASVARUSTRING}
      tkUString:
        if PUnicodeString(A)^=PUnicodeString(B)^ then
          Diff := sizeof(pointer) else
          exit;
      {$endif}
      tkRecord{$ifdef FPC},tkObject{$endif}:
        if RecordEquals(A^,B^,Field^.TypeInfo{$ifndef FPC}^{$endif}) then
................................................................................
        inc(result,DynArray.SaveToLength-sizeof(PtrUInt));
      end;
      tkLString,tkWString{$ifdef FPC},tkLStringOld{$endif}:
        // length stored within WideString is in bytes
        if P^=0 then
          dec(result,sizeof(PtrUInt)-1) else
          inc(result,ToVarUInt32LengthWithData(PStrRec(Pointer(P^-STRRECSIZE))^.length)-sizeof(PtrUInt));
      {$ifdef HASVARUSTRING}
      tkUString:
        if P^=0 then
          dec(result,sizeof(PtrUInt)-1) else
          inc(result,ToVarUInt32LengthWithData(PStrRec(Pointer(P^-STRRECSIZE))^.length*2)-sizeof(PtrUInt));
      {$endif}
      tkRecord{$ifdef FPC},tkObject{$endif}: begin
        Len := RecordSaveLength(P^,Field.TypeInfo{$ifndef FPC}^{$endif});
................................................................................
    Kind := Field.TypeInfo^.Kind;
    case Kind of
    tkDynArray: begin
      DynArray.Init(Field.TypeInfo{$ifndef FPC}^{$endif},R^);
      Dest := DynArray.SaveTo(Dest);
      Diff := sizeof(PtrUInt); // size of tkDynArray in record
    end;
    tkLString, tkWString {$ifdef HASVARUSTRING}, tkUString{$endif}
    {$ifdef FPC}, tkLStringOld{$endif}: begin
      if PPtrUInt(R)^=0 then
        LenBytes := 0 else
        LenBytes := PStrRec(Pointer(PPtrUInt(R)^-STRRECSIZE))^.length;
      {$ifdef HASVARUSTRING} // WideString has length in bytes, UnicodeString in WideChars
      if Kind=tkUString then
        LenBytes := LenBytes*2;
      {$endif}
      Dest := pointer(ToVarUInt32(LenBytes,pointer(Dest)));
      if LenBytes>0 then begin
        MoveFast(pointer(PPtrUInt(R)^)^,Dest^,LenBytes);
        inc(Dest,LenBytes);
................................................................................
begin
  Len := RecordSaveLength(Rec,TypeInfo);
  SetString(result,nil,Len);
  if Len<>0 then
    RecordSave(Rec,pointer(result),TypeInfo);
end;

function RecordSaveBase64(const Rec; TypeInfo: pointer; UriCompatible: boolean): RawUTF8;
var len: integer;
    data: RawByteString;
    dat: PAnsiChar;
begin
  result := '';
  len := RecordSaveLength(Rec,TypeInfo);
  if len=0 then
................................................................................
  if UriCompatible then
    Base64ToURI(result);
end;

function RecordLoadBase64(Source: PAnsiChar; Len: integer; var Rec;
  TypeInfo: pointer; UriCompatible: boolean): boolean;
var data: RawByteString;
    uri: RawUTF8;
begin
  result := false;
  if Len<=6 then
    exit;
  if UriCompatible then begin
    SetString(uri,Source,Len);
    Base64FromURI(uri);
    data := Base64ToBin(uri);
  end else
    data := Base64ToBin(Source,Len);
  Len := length(data);
  if Len<=4 then
    exit;
  Source := PAnsiChar(pointer(data))+4;
  if crc32c(0,Source,Len-4)=PCardinal(data)^ then
................................................................................
    Kind := Field.TypeInfo^.Kind;
    case Kind of
    tkDynArray: begin
      DynArray.Init(Field.TypeInfo{$ifndef FPC}^{$endif},R^);
      Source := DynArray.LoadFrom(Source);
      Diff := sizeof(PtrUInt); // size of tkDynArray in record
    end;
    tkLString, tkWString {$ifdef HASVARUSTRING}, tkUString{$endif}
    {$ifdef FPC}, tkLStringOld{$endif}: begin
      LenBytes := FromVarUInt32(PByte(Source));
      case Kind of
        tkLString{$ifdef FPC},tkLStringOld{$endif}: begin
          SetString(PRawByteString(R)^,Source,LenBytes);
          {$ifdef HASCODEPAGE}
          { Delphi 2009+: set Code page for this AnsiString }
          if LenBytes<>0 then
            SetCodePage(PRawByteString(R)^,PWord(PtrUInt(Field.TypeInfo{$ifndef FPC}^{$endif})+
              Field.TypeInfo{$ifndef FPC}^{$endif}^.NameLen+2)^,false);
          {$endif}
        end;
        tkWString:
          SetString(PWideString(R)^,PWideChar(Source),LenBytes shr 1);
        {$ifdef HASVARUSTRING}
        tkUString:
          SetString(PString(R)^,PWideChar(Source),LenBytes shr 1);
        {$endif}
      end;
      inc(Source,LenBytes);
      Diff := sizeof(PtrUInt); // size of tkLString+tkWString+tkUString in record
    end;
................................................................................
      if VType and VTYPE_STATIC<>0 then
        VarClear(Value);
      VType := varString;
      VAny := nil; // avoid GPF below when assigning a string variable to VAny
      if Txt='' then
        exit;
    end;
    RawByteString(VAny) := Txt;
    {$ifdef HASCODEPAGE}
    if (Txt<>'') and  (StringCodePage(Txt)=CP_RAWBYTESTRING) then
      SetCodePage(RawByteString(VAny),CP_UTF8,false); // force explicit UTF-8
    {$endif}
  end;
end;

function RawUTF8ToVariant(const Txt: RawUTF8): variant;
begin
  RawUTF8ToVariant(Txt,result);
end;
................................................................................
  if Value.VType and VTYPE_STATIC<>0 then
    VarClear(variant(Value));
  Value.VType := ExpectedValueType;
  Value.VAny := nil; // avoid GPF below
  if Txt<>'' then
  case ExpectedValueType of
    varString: begin
      RawByteString(Value.VAny) := Txt;
      {$ifdef HASCODEPAGE}
      if (Txt<>'') and  (StringCodePage(Txt)=CP_RAWBYTESTRING) then
        SetCodePage(RawByteString(Value.VAny),CP_UTF8,false); // force explicit UTF-8
      {$endif}
    end;
    varOleStr:
      UTF8ToWideString(Txt,WideString(Value.VAny));
    {$ifdef HASVARUSTRING}
    varUString:
      UTF8DecodeToUnicodeString(pointer(Txt),length(Txt),UnicodeString(Value.VAny));
    {$endif}
................................................................................
    vtVariant:
      result := V.VVariant^;
    vtAnsiString: begin
      VType := varString;
      VAny := nil;
      RawByteString(VAny) := RawByteString(V.VAnsiString);
    end;
    vtString, {$ifdef HASVARUSTRING}vtUnicodeString,{$endif}
    vtPChar, vtChar, vtWideChar, vtWideString, vtClass: begin
      VType := varString;
      VAny := nil; // avoid GPF on next line
      VarRecToUTF8(V,RawUTF8(VAny));
    end;
    vtObject: // class instance will be serialized as a TDocVariant
      ObjectToVariant(V.VObject,result);
................................................................................
        [PShortString(@PTypeInfo(ArrayType).NameLen)^]) else begin
      // binary types: store as once
      n := n*integer(ElemSize);
      MoveFast(P^,Dest^,n);
      inc(Dest,n);
    end else
    case PTypeKind(ElemType)^ of
    tkLString, tkWString {$ifdef HASVARUSTRING}, tkUString{$endif}
    {$ifdef FPC}, tkLStringOld{$endif}: begin
      for i := 1 to n do begin
        if PPtrUInt(P)^=0 then
          LenBytes := 0 else begin
          LenBytes := PStrRec(Pointer(PPtrUInt(P)^-STRRECSIZE))^.length;
          {$ifdef HASVARUSTRING} // WideString length in bytes, UnicodeString in WideChars
          if PTypeKind(ElemType)^=tkUString then
            LenBytes := LenBytes*2;
          {$endif}
        end;
        Dest := pointer(ToVarUInt32(LenBytes,pointer(Dest)));
        if LenBytes>0 then begin
          MoveFast(pointer(PPtrUInt(P)^)^,Dest^,LenBytes);
................................................................................
    tkLString, tkWString{$ifdef FPC}, tkLStringOld{$endif}:
      for i := 1 to n do begin
        if PPtrUInt(P)^=0 then
          inc(result) else
          inc(result,ToVarUInt32LengthWithData(PStrRec(Pointer(PPtrUInt(P)^-STRRECSIZE))^.length));
        inc(P,sizeof(PtrUInt));
      end;
    {$ifdef HASVARUSTRING}
    tkUString: // WideString has length in bytes, UnicodeString in WideChars
      for i := 1 to n do begin
        if PPtrUInt(P)^=0 then
          inc(result) else
          inc(result,ToVarUInt32LengthWithData(PStrRec(Pointer(PPtrUInt(P)^-STRRECSIZE))^.length*2));
        inc(P,sizeof(PtrUInt));
      end;
................................................................................
      [PShortString(@PTypeInfo(ArrayType).NameLen)^]) else begin
    // binary type was stored as once
    n := n*integer(ElemSize);
    MoveFast(Source^,P^,n);
    inc(Source,n);
  end else
  case PTypeKind(ElemType)^ of
    tkLString, tkWString {$ifdef HASVARUSTRING}, tkUString{$endif}
    {$ifdef FPC}, tkLStringOld{$endif}:
    for i := 1 to n do begin
      LenBytes := FromVarUInt32(PByte(Source));
      case PTypeKind(ElemType)^ of
      tkLString{$ifdef FPC},tkLStringOld{$endif}: begin
        SetString(PRawByteString(P)^,Source,LenBytes);
        {$ifdef HASCODEPAGE}
        { Delphi 2009+: set Code page for this AnsiString }
        if LenBytes<>0 then
          SetCodePage(PRawByteString(P)^,PWord(PtrUInt(ElemType)+
            PTypeInfo(ElemType)^.NameLen+2)^,false);
        {$endif}
      end;
      tkWString:
        SetString(PWideString(P)^,PWideChar(Source),LenBytes shr 1);
      {$ifdef HASVARUSTRING}
      tkUString:
        SetString(PString(P)^,PWideChar(Source),LenBytes shr 1);
      {$endif}
      end;
      inc(Source,LenBytes);
      inc(P,sizeof(PtrUInt));
    end;
................................................................................
    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:
................................................................................
    for i := 0 to n-1 do
      if AnsiString(A1^[i])<>AnsiString(A2^[i]) then
        exit;
  tkWString:
    for i := 0 to n-1 do
      if WideString(A1^[i])<>WideString(A2^[i]) then
        exit;
  {$ifdef HASVARUSTRING}
  tkUString:
    for i := 0 to n-1 do
      if UnicodeString(A1^[i])<>UnicodeString(A2^[i]) then
        exit;
  {$endif}
  tkInterface:
    if not CompareMem(P1,P2,SizeOf(pointer)*cardinal(n)) then
................................................................................
  case PTypeKind(ElemType)^ of
  tkLString{$ifdef FPC},tkLStringOld{$endif}:
    for result := 0 to max do
      if AnsiString(PPtrIntArray(P)^[result])=AnsiString(Elem) then exit;
  tkWString:
    for result := 0 to max do
      if WideString(PPtrIntArray(P)^[result])=WideString(Elem) then exit;
  {$ifdef HASVARUSTRING}
  tkUString:
    for result := 0 to max do
      if UnicodeString(PPtrIntArray(P)^[result])=UnicodeString(Elem) then exit;
  {$endif}
  {$ifndef NOVARIANTS}
  tkVariant:
    for result := 0 to max do
................................................................................
    case PTypeKind(ElemType)^ of // release reference counted
      tkLString{$ifdef FPC},tkLStringOld{$endif}:
        RawByteString(Elem) := '';
      tkWString:
        WideString(Elem) := '';
      tkInterface:
        IUnknown(Elem) := nil;
      {$ifdef HASVARUSTRING}
      tkUString:
        UnicodeString(Elem) := '';
      {$endif}
      tkRecord{$ifdef FPC},tkObject{$endif}:
        RecordClear(Elem,ElemType);
      tkDynArray:
        _DynArrayClear(pointer(Elem),ElemType);
................................................................................
        WideString(B) := WideString(A);
        exit;
      end;
      tkInterface: begin
        IUnknown(B) := IUnknown(A);
        exit;
      end;
      {$ifdef HASVARUSTRING}
      tkUString: begin
        UnicodeString(B) := UnicodeString(A);
        exit;
      end;
      {$endif}
      tkRecord{$ifdef FPC},tkObject{$endif}: begin
        RecordCopy(B,A,ElemType);
................................................................................
  if Source=nil then
    exit; // avoid GPF
  if ElemType=nil then
    MoveFast(Source^,Elem,ElemSize) else
    case PTypeKind(ElemType)^ of
    tkLString{$ifdef FPC},tkLStringOld{$endif}: begin
      SetString(RawByteString(Elem),Source+4,PInteger(Source)^);
      {$ifdef HASCODEPAGE}
      { Delphi 2009+: set Code page for this AnsiString }
      if PPtrUInt(@Elem)^<>0 then
        SetCodePage(RawByteString(Elem),PWord(PtrUInt(ElemType)+
          PTypeInfo(ElemType)^.NameLen+2)^,false);
      {$endif}
    end;
    tkWString: // WideString internal length is in bytes
      SetString(WideString(Elem),PWideChar(Source+4),PInteger(Source)^ shr 1);
    {$ifdef HASVARUSTRING}
    tkUString:
      SetString(UnicodeString(Elem),PWideChar(Source+4),PInteger(Source)^);
    {$endif}
    {$ifndef NOVARIANTS}
    tkVariant:
      VariantLoad(variant(Elem),Source,@JSON_OPTIONS[true]);
    {$endif}
................................................................................
begin
  if (ElemType<>nil) and (length(ElemLoaded)=integer(ElemSize)) then
  case PTypeKind(ElemType)^ of
    tkLString{$ifdef FPC},tkLStringOld{$endif}:
      PRawByteString(pointer(ElemLoaded))^ := '';
    tkWString:
      PWideString(pointer(ElemLoaded))^ := '';
    {$ifdef HASVARUSTRING}
    tkUString:
      PUnicodeString(pointer(ElemLoaded))^ := '';
    {$endif}
    {$ifndef NOVARIANTS}
    tkVariant:
      VarClear(PVariant(pointer(ElemLoaded))^);
    {$endif}
................................................................................
      tkLString, tkWString, tkLStringOld:
        if PPtrInt(@Elem)^=0 then
          SetString(result,PAnsiChar(@Elem),4) else begin
          LenBytes := PStrRec(Pointer(PPtrInt(@Elem)^-STRRECSIZE))^.length;
          SetString(result,PAnsiChar(PPtrInt(@Elem)^-sizeof(integer)),LenBytes+sizeof(integer));
          PInteger(result)^ := LenBytes;
        end;
      {$ifdef HASVARUSTRING}
      tkUString:
        if PPtrInt(@Elem)^=0 then
          SetString(result,PAnsiChar(@Elem),4) else begin
          LenBytes := PStrRec(Pointer(PPtrInt(@Elem)^-STRRECSIZE))^.length;
          SetString(result,PAnsiChar(PPtrInt(@Elem)^-sizeof(integer)),LenBytes*2+sizeof(integer));
          PInteger(result)^ := LenBytes;
        end;

      {$endif}
      {$else FPC}
      tkLString, tkWString: // WideString internal length is in bytes
        if PPtrInt(@Elem)^=0 then
          SetString(result,PAnsiChar(@Elem),4) else
          SetString(result,PAnsiChar(PPtrInt(@Elem)^-sizeof(integer)),
            PInteger(PPtrInt(@Elem)^-sizeof(integer))^+sizeof(integer));
      {$ifdef HASVARUSTRING}
      tkUString:
        if PPtrInt(@Elem)^=0 then
          SetString(result,PAnsiChar(@Elem),4) else
          SetString(result,PAnsiChar(PPtrInt(@Elem)^-sizeof(integer)),
            PInteger(PPtrInt(@Elem)^-sizeof(integer))^*2+sizeof(integer));
      {$endif}
      {$endif FPC}
................................................................................

{$ifdef UNICODE}

function HashUnicodeString(const Elem; Hasher: THasher): cardinal;
begin
  if PtrUInt(Elem)=0 then
    result := HASH_ONVOIDCOLISION else
    result := Hasher(0,Pointer(Elem),length(UnicodeString(Elem))*2);


end;

function HashUnicodeStringI(const Elem; Hasher: THasher): cardinal;
var tmp: array[byte] of AnsiChar; // avoid slow heap allocation
begin
  if PtrUInt(Elem)=0 then
    result := HASH_ONVOIDCOLISION else
................................................................................

{$endif UNICODE}

function HashSynUnicode(const Elem; Hasher: THasher): cardinal;
begin
  if PtrUInt(Elem)=0 then
    result := HASH_ONVOIDCOLISION else
    result := Hasher(0,Pointer(Elem),length(SynUnicode(Elem))*2);




end;

function HashSynUnicodeI(const Elem; Hasher: THasher): cardinal;
var tmp: array[byte] of AnsiChar; // avoid slow heap allocation
begin
  if PtrUInt(Elem)=0 then
    result := HASH_ONVOIDCOLISION else
................................................................................
    result := Hasher(0,tmp,UpperCopy255W(tmp,SynUnicode(Elem))-tmp);
end;

function HashWideString(const Elem; Hasher: THasher): cardinal;
begin // WideString internal size is in bytes, not WideChar
  if PtrUInt(Elem)=0 then
    result := HASH_ONVOIDCOLISION else
    result := Hasher(0,Pointer(Elem),Length(WideString(Elem))*2);


end;

function HashWideStringI(const Elem; Hasher: THasher): cardinal;
var tmp: array[byte] of AnsiChar; // avoid slow heap allocation
begin
  if PtrUInt(Elem)=0 then
    result := HASH_ONVOIDCOLISION else
    result := Hasher(0,tmp,UpperCopy255W(tmp,pointer(Elem),Length(WideString(Elem)))-tmp);
end;

function HashPtrUInt(const Elem; Hasher: THasher): cardinal;
begin
  {$ifdef CPU64}
  result := Hasher(0,@Elem,sizeof(PtrUInt));
  {$else}
  result := (PtrUInt(Elem) shr 4)+1; // naive but optimal for TDynArrayHashed
  {$endif}
end;

function HashPointer(const Elem; Hasher: THasher): cardinal;
begin
  result := Hasher(0,@Elem,sizeof(pointer));
end;

................................................................................
  varDate:     AddDateTime(@VDate,'T','"');
  varCurrency: AddCurr64(VInt64);
  varBoolean:  Add(VBoolean);
  varVariant:  AddVariant(PVariant(VPointer)^,Escape,ForcedSerializeAsNonExtendedJson);
  varString: begin
    if Escape=twJSONEscape then
      Add('"');
    {$ifdef HASCODEPAGE}
    AddAnyAnsiString(RawByteString(VString),Escape);
    {$else}  // VString is expected to be a RawUTF8
    Add(VAny,length(RawUTF8(VAny)),Escape);
    {$endif}
    if Escape=twJSONEscape then
      Add('"');
  end;
................................................................................
  end;
  else
  if VType=varVariant or varByRef then
    AddVariant(PVariant(VPointer)^,Escape,ForcedSerializeAsNonExtendedJson) else
  if VType=varByRef or varString then begin
    if Escape=twJSONEscape then
      Add('"');
    {$ifdef HASCODEPAGE}
    AddAnyAnsiString(PRawByteString(VAny)^,Escape);
    {$else}  // VString is expected to be a RawUTF8
    Add(PPointer(VAny)^,length(PRawUTF8(VAny)^),Escape);
    {$endif}
    if Escape=twJSONEscape then
      Add('"');
  end else
................................................................................
  if L=0 then
    exit;
  if PInteger(s)^ and $ffffff=JSON_BASE64_MAGIC then begin
    AddNoJSONEscape(pointer(s),L); // identified as a BLOB content
    exit;
  end;
  if CodePage<0 then
    {$ifdef HASCODEPAGE}
    CodePage := StringCodePage(s);
    {$else}
    CodePage := 0; // TSynAnsiConvert.Engine(0)=CurrentAnsiConvert
    {$endif}
  AddAnyAnsiBuffer(pointer(s),L,Escape,CodePage);
end;

................................................................................
end;

procedure TTextWriter.AddJSONEscape(const V: TVarRec);
begin
  with V do
  case VType of
    vtPointer: AddShort('null');
    vtString, vtAnsiString,{$ifdef HASVARUSTRING}vtUnicodeString,{$endif}
    vtPChar, vtChar, vtWideChar, vtWideString, vtClass: begin
      Add('"');
      case VType of
        vtString:     AddJSONEscape(@VString^[1],ord(VString^[0]));
        vtAnsiString: AddJSONEscape(pointer(RawUTF8(VAnsiString)));
        {$ifdef HASVARUSTRING}
        vtUnicodeString: AddJSONEscapeW(
          pointer(UnicodeString(VUnicodeString)),length(UnicodeString(VUnicodeString)));
        {$endif}
        vtPChar:      AddJSONEscape(VPChar);
        vtChar:       AddJSONEscape(@VChar,1);
        vtWideChar:   AddJSONEscapeW(@VWideChar,1);
        vtWideString: AddJSONEscapeW(VWideString);
        vtClass:      AddClassName(VClass);
      end;
      Add('"');
................................................................................
      AddW(VWideString,length(WideString(VWideString)),Escape);
  vtInt64:
    Add(VInt64^);
  {$ifndef NOVARIANTS}
  vtVariant:
    AddVariant(VVariant^,Escape);
  {$endif}
  {$ifdef HASVARUSTRING}
  vtUnicodeString:
    if VUnicodeString<>nil then // convert to UTF-8
      AddW(VUnicodeString,length(UnicodeString(VUnicodeString)),Escape);
  {$endif} end;
end;

{$ifndef NOVARIANTS}
................................................................................
begin
  FlushFinal;
  Len := fTotalFileSize-fInitialStreamPosition;
  if Len=0 then
    result := '' else
  if fStream.InheritsFrom(TRawByteStringStream) then
    with TRawByteStringStream(fStream) do
    if fInitialStreamPosition=0 then begin
      {$ifdef HASCODEPAGE} // FPC expects this
      SetCodePage(fDataString,CP_UTF8,false);
      {$endif}
      result := fDataString;
    end else
      SetRawUTF8(result,PAnsiChar(pointer(DataString))+fInitialStreamPosition,Len) else
  if fStream.InheritsFrom(TCustomMemoryStream) then
    with TCustomMemoryStream(fStream) do
    SetRawUTF8(result,PAnsiChar(Memory)+fInitialStreamPosition,Len) else begin
    FastNewRawUTF8(result,Len);
    fStream.Seek(fInitialStreamPosition,soBeginning);
    fStream.Read(pointer(result)^,Len);

Changes to SynCrtSock.pas.

300
301
302
303
304
305
306



307
308

309
310
311
312
313
314
315
....
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
....
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
....
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
....
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
....
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
....
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
  /// define the fastest Unicode string type of the compiler
  SynUnicode = UnicodeString;
  /// define a raw storage string type, used for data buffer management
  SockString = type RawByteString;
{$else}
  /// define the fastest Unicode string type of the compiler
  SynUnicode = WideString;



  /// define a raw storage string type, used for data buffer management
  SockString = type AnsiString;

{$endif}

{$ifndef CONDITIONALEXPRESSIONS}
  // not defined in Delphi 5 or older
  PPointer = ^Pointer;
  TTextLineBreakStyle = (tlbsLF, tlbsCRLF);
  UTF8String = AnsiString;
................................................................................
      result := nil;
      exit;
    end;
    Inc(result);
  end;
end;

{$ifdef UNICODE}
// rewrite some functions to avoid unattempted ansi<->unicode conversion

function Trim(const S: SockString): SockString;
{$ifdef PUREPASCAL}
var I, L: Integer;
begin
  L := Length(S);
  I := 1;
  while (I<=L) and (S[i]<=' ') do Inc(I);
  if I>L then
    Result := '' else
................................................................................

function UpperCase(const S: SockString): SockString;
procedure Upper(Source, Dest: PAnsiChar; L: cardinal);
var Ch: AnsiChar; // this sub-call is shorter and faster than 1 plain proc
begin
  repeat
    Ch := Source^;
    if (Ch >= 'a') and (Ch <= 'z') then
      dec(Ch, 32);
    Dest^ := Ch;
    dec(L);
    inc(Source);
    inc(Dest);
  until L=0;
end;
var L: cardinal;
................................................................................
  L := Length(S);
  if L=0 then
    exit;
  SetLength(result, L);
  Upper(pointer(S),pointer(result),L);
end;

{$endif}

function GetCardinal(P: PAnsiChar): cardinal; overload;
var c: cardinal;
begin
  if P=nil then begin
    result := 0;
    exit;
................................................................................

constructor TCrtSocket.Bind(const aPort: SockString; aLayer: TCrtSocketLayer=cslTCP);
var s,p: SockString;
    i: integer;
begin
  // on Linux, Accept() blocks even after Shutdown() -> use 0.5 second timeout
  Create({$ifdef LINUX}500{$else}5000{$endif});
  i := pos({$ifdef UNICODE}SockString{$endif}(':'),aPort);
  if i=0 then begin
    s := '0.0.0.0';
    p := aPort;
  end else begin
    s := Copy(aPort,1,i-1);
    p := Copy(aPort,i+1,10);
  end;
................................................................................
  for i := 0 to high(Values) do
  with Values[i] do
  case VType of
    vtString:
      SockSend(@VString^[1],pByte(VString)^);
    vtAnsiString:
      SockSend(VAnsiString,length(SockString(VAnsiString)));
{$ifdef UNICODE}
    vtUnicodeString: begin
      tmp := ShortString(UnicodeString(VUnicodeString)); // convert into ansi
      SockSend(@tmp[1],length(tmp));
    end;
{$endif}
    vtPChar:
      SockSend(VPChar,StrLen(VPChar));
    vtChar:
      SockSend(@VChar,1);
    vtWideChar:
      SockSend(@VWideChar,1); // only ansi part of the character
    vtInteger: begin
................................................................................
    end else
      Exec('HELO '+Server,'25');
    writeln(TCP.SockOut^,'MAIL FROM:<',From,'>'); Expect('250');
    ToList := 'To: ';
    repeat
      rec := trim(GetNextItem(P));
      if rec='' then continue;
      if pos({$ifdef UNICODE}SockString{$endif}('<'),rec)=0 then
        rec := '<'+rec+'>';
      Exec('RCPT TO:'+rec,'25');
      ToList := ToList+rec+', ';
    until P=nil;
    Exec('DATA','354');
    writeln(TCP.SockOut^,'Subject: ',Subject,#13#10,
      ToList,#13#10'Content-Type: text/plain; charset=',TextCharSet,






>
>
>


>







 







|



|







 







|
|







 







|







 







|







 







|




|







 







|







300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
....
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
....
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
....
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
....
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
....
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
....
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
  /// define the fastest Unicode string type of the compiler
  SynUnicode = UnicodeString;
  /// define a raw storage string type, used for data buffer management
  SockString = type RawByteString;
{$else}
  /// define the fastest Unicode string type of the compiler
  SynUnicode = WideString;
  {$ifdef HASCODEPAGE} // FPC expects a CP, e.g. to compare to string constants
  SockString = type AnsiString(CP_UTF8);
  {$else}
  /// define a raw storage string type, used for data buffer management
  SockString = type AnsiString;
  {$endif}
{$endif}

{$ifndef CONDITIONALEXPRESSIONS}
  // not defined in Delphi 5 or older
  PPointer = ^Pointer;
  TTextLineBreakStyle = (tlbsLF, tlbsCRLF);
  UTF8String = AnsiString;
................................................................................
      result := nil;
      exit;
    end;
    Inc(result);
  end;
end;

{$ifdef HASCODEPAGE}
// rewrite some functions to avoid unattempted ansi<->unicode conversion

function Trim(const S: SockString): SockString;
{$ifdef FPC_OR_PUREPASCAL}
var I, L: Integer;
begin
  L := Length(S);
  I := 1;
  while (I<=L) and (S[i]<=' ') do Inc(I);
  if I>L then
    Result := '' else
................................................................................

function UpperCase(const S: SockString): SockString;
procedure Upper(Source, Dest: PAnsiChar; L: cardinal);
var Ch: AnsiChar; // this sub-call is shorter and faster than 1 plain proc
begin
  repeat
    Ch := Source^;
    if (Ch>='a') and (Ch<='z') then
      dec(Ch,32);
    Dest^ := Ch;
    dec(L);
    inc(Source);
    inc(Dest);
  until L=0;
end;
var L: cardinal;
................................................................................
  L := Length(S);
  if L=0 then
    exit;
  SetLength(result, L);
  Upper(pointer(S),pointer(result),L);
end;

{$endif HASCODEPAGE}

function GetCardinal(P: PAnsiChar): cardinal; overload;
var c: cardinal;
begin
  if P=nil then begin
    result := 0;
    exit;
................................................................................

constructor TCrtSocket.Bind(const aPort: SockString; aLayer: TCrtSocketLayer=cslTCP);
var s,p: SockString;
    i: integer;
begin
  // on Linux, Accept() blocks even after Shutdown() -> use 0.5 second timeout
  Create({$ifdef LINUX}500{$else}5000{$endif});
  i := pos({$ifdef HASCODEPAGE}SockString{$endif}(':'),aPort);
  if i=0 then begin
    s := '0.0.0.0';
    p := aPort;
  end else begin
    s := Copy(aPort,1,i-1);
    p := Copy(aPort,i+1,10);
  end;
................................................................................
  for i := 0 to high(Values) do
  with Values[i] do
  case VType of
    vtString:
      SockSend(@VString^[1],pByte(VString)^);
    vtAnsiString:
      SockSend(VAnsiString,length(SockString(VAnsiString)));
    {$ifdef HASVARUSTRING}
    vtUnicodeString: begin
      tmp := ShortString(UnicodeString(VUnicodeString)); // convert into ansi
      SockSend(@tmp[1],length(tmp));
    end;
    {$endif}
    vtPChar:
      SockSend(VPChar,StrLen(VPChar));
    vtChar:
      SockSend(@VChar,1);
    vtWideChar:
      SockSend(@VWideChar,1); // only ansi part of the character
    vtInteger: begin
................................................................................
    end else
      Exec('HELO '+Server,'25');
    writeln(TCP.SockOut^,'MAIL FROM:<',From,'>'); Expect('250');
    ToList := 'To: ';
    repeat
      rec := trim(GetNextItem(P));
      if rec='' then continue;
      if pos({$ifdef HASCODEPAGE}SockString{$endif}('<'),rec)=0 then
        rec := '<'+rec+'>';
      Exec('RCPT TO:'+rec,'25');
      ToList := ToList+rec+', ';
    until P=nil;
    Exec('DATA','354');
    writeln(TCP.SockOut^,'Subject: ',Subject,#13#10,
      ToList,#13#10'Content-Type: text/plain; charset=',TextCharSet,

Changes to SynDB.pas.

6388
6389
6390
6391
6392
6393
6394



6395

6396
6397
6398
6399
6400
6401

6402
6403


6404

6405
6406
6407
6408
6409
6410
6411
....
6473
6474
6475
6476
6477
6478
6479
6480
6481
6482
6483
6484
6485
6486
6487
....
7214
7215
7216
7217
7218
7219
7220
7221
7222
7223
7224
7225
7226
7227
7228
7229
7230
        BindTextU(i,'',IO) else begin
        c := PInteger(VAnsiString)^ and $00ffffff;
        if c=JSON_BASE64_MAGIC then
          BindBlob(i,Base64ToBin(PAnsiChar(VAnsiString)+3,length(RawUTF8(VAnsiString))-3)) else
        if c=JSON_SQLDATE_MAGIC then
          BindDateTime(i,Iso8601ToDateTimePUTF8Char(PUTF8Char(VAnsiString)+3,length(RawUTF8(VAnsiString))-3)) else
          // expect UTF-8 content only for AnsiString, i.e. RawUTF8 variables



          BindTextU(i,RawUTF8(VAnsiString),IO);

      end;
    vtPChar:      BindTextP(i,PUTF8Char(VPChar),IO);
    vtChar:       BindTextU(i,RawUTF8(VChar),IO);
    vtWideChar:   BindTextU(i,RawUnicodeToUtf8(@VWideChar,1),IO);
    vtPWideChar:  BindTextU(i,RawUnicodeToUtf8(VPWideChar,StrLenW(VPWideChar)),IO);
    vtWideString: BindTextW(i,WideString(VWideString),IO);

{$ifdef UNICODE}
    vtUnicodeString: BindTextS(i,string(VUnicodeString),IO);


{$endif}

    vtBoolean:    Bind(i,integer(VBoolean),IO);
    vtInteger:    Bind(i,VInteger,IO);
    vtInt64:      Bind(i,VInt64^,IO);
    vtCurrency:   BindCurrency(i,VCurrency^,IO);
    vtExtended:   Bind(i,VExtended^,IO);
    vtPointer:
      if VPointer=nil then
................................................................................
      if DataIsBlob then
        if (VAny<>nil) and (PInteger(VAny)^ and $00ffffff=JSON_BASE64_MAGIC) then
          // recognized as Base64 encoded text
          BindBlob(Param,Base64ToBin(PAnsiChar(VAny)+3,length(RawByteString(VAny))-3)) else
          // no conversion if was set via TQuery.AsBlob property e.g.
          BindBlob(Param,RawByteString(VAny),IO) else
        // direct bind of AnsiString as UTF-8 value
        {$ifdef UNICODE}
        BindTextU(Param,AnyAnsiToUTF8(RawByteString(VAny)),IO);
        {$else} // on older Delphi, we assume AnsiString = RawUTF8
        BindTextU(Param,RawUTF8(VAny),IO);
        {$endif}
    else
    if VType=varByRef or varVariant then
      BindVariant(Param,PVariant(VPointer)^,DataIsBlob,IO) else
................................................................................
              dec(L); // avoid return of invalid UTF-8 buffer
            if L=0 then
              L := MaxCharCount;
            SetString(result,PAnsiChar(VAny),L);
          end else
            result := RawUTF8(VAny);
        end;
        {$ifdef UNICODE}
        varUString: begin
          L := length(string(VAny));
          if L>MaxCharCount then begin
            Truncated := true;
            L := MaxCharCount;
          end;
          RawUnicodeToUtf8(VAny,L,result);
        end;
        {$endif}






>
>
>

>






>
|

>
>
|
>







 







|







 







|

|







6388
6389
6390
6391
6392
6393
6394
6395
6396
6397
6398
6399
6400
6401
6402
6403
6404
6405
6406
6407
6408
6409
6410
6411
6412
6413
6414
6415
6416
6417
6418
6419
....
6481
6482
6483
6484
6485
6486
6487
6488
6489
6490
6491
6492
6493
6494
6495
....
7222
7223
7224
7225
7226
7227
7228
7229
7230
7231
7232
7233
7234
7235
7236
7237
7238
        BindTextU(i,'',IO) else begin
        c := PInteger(VAnsiString)^ and $00ffffff;
        if c=JSON_BASE64_MAGIC then
          BindBlob(i,Base64ToBin(PAnsiChar(VAnsiString)+3,length(RawUTF8(VAnsiString))-3)) else
        if c=JSON_SQLDATE_MAGIC then
          BindDateTime(i,Iso8601ToDateTimePUTF8Char(PUTF8Char(VAnsiString)+3,length(RawUTF8(VAnsiString))-3)) else
          // expect UTF-8 content only for AnsiString, i.e. RawUTF8 variables
          {$ifdef HASCODEPAGE}
          BindTextU(i,AnyAnsiToUTF8(RawByteString(VAnsiString)),IO);
          {$else}
          BindTextU(i,RawUTF8(VAnsiString),IO);
          {$endif}
      end;
    vtPChar:      BindTextP(i,PUTF8Char(VPChar),IO);
    vtChar:       BindTextU(i,RawUTF8(VChar),IO);
    vtWideChar:   BindTextU(i,RawUnicodeToUtf8(@VWideChar,1),IO);
    vtPWideChar:  BindTextU(i,RawUnicodeToUtf8(VPWideChar,StrLenW(VPWideChar)),IO);
    vtWideString: BindTextW(i,WideString(VWideString),IO);
    {$ifdef HASVARUSTRING}
    {$ifdef UNICODE}
    vtUnicodeString: BindTextS(i,string(VUnicodeString),IO);
    {$else}
    vtUnicodeString: BindTextU(i,UnicodeStringToUtf8(UnicodeString(VUnicodeString)),IO);
    {$endif}
    {$endif}
    vtBoolean:    Bind(i,integer(VBoolean),IO);
    vtInteger:    Bind(i,VInteger,IO);
    vtInt64:      Bind(i,VInt64^,IO);
    vtCurrency:   BindCurrency(i,VCurrency^,IO);
    vtExtended:   Bind(i,VExtended^,IO);
    vtPointer:
      if VPointer=nil then
................................................................................
      if DataIsBlob then
        if (VAny<>nil) and (PInteger(VAny)^ and $00ffffff=JSON_BASE64_MAGIC) then
          // recognized as Base64 encoded text
          BindBlob(Param,Base64ToBin(PAnsiChar(VAny)+3,length(RawByteString(VAny))-3)) else
          // no conversion if was set via TQuery.AsBlob property e.g.
          BindBlob(Param,RawByteString(VAny),IO) else
        // direct bind of AnsiString as UTF-8 value
        {$ifdef HASCODEPAGE}
        BindTextU(Param,AnyAnsiToUTF8(RawByteString(VAny)),IO);
        {$else} // on older Delphi, we assume AnsiString = RawUTF8
        BindTextU(Param,RawUTF8(VAny),IO);
        {$endif}
    else
    if VType=varByRef or varVariant then
      BindVariant(Param,PVariant(VPointer)^,DataIsBlob,IO) else
................................................................................
              dec(L); // avoid return of invalid UTF-8 buffer
            if L=0 then
              L := MaxCharCount;
            SetString(result,PAnsiChar(VAny),L);
          end else
            result := RawUTF8(VAny);
        end;
        {$ifdef HASVARUSTRING}
        varUString: begin
          L := length(UnicodeString(VAny));
          if L>MaxCharCount then begin
            Truncated := true;
            L := MaxCharCount;
          end;
          RawUnicodeToUtf8(VAny,L,result);
        end;
        {$endif}

Changes to SynDBZeos.pas.

959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
...
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
    end;
  end;
  inc(len,startlen+finlen);//add { and }
  SetLength(result,len);
  P := pointer(result);

  if startlen>0 then begin
    Move(pointer(start)^,P^,startlen);
    inc(P,startlen);
  end;

  i := 0;
  repeat
    L := length(Values[i]);
    if L>0 then begin
................................................................................
          j := 0;
          while k+j<l do begin
            case Values[i][k+j] of
              '"': break;
              else inc(j);
            end;
          end;
          move(pointer(@Values[i][k])^,P^,j);
          inc(P,j);
          inc(k,j);
          case Values[i][k] of
            '"': begin
              move(pointer(dQuoteRepl)^,P^,dQuoteRepllen);
              inc(P,dQuoteRepllen);
              inc(k);
            end;
          end;
        end;
        P^ := '"';
        inc(p);
      end else begin
        move(pointer(Values[i])^,P^,L);
        inc(P,L);
      end;
    end;

    if i=high(Values) then
      Break;
    if seplen>0 then begin
      Move(pointer(Sep)^,P^,seplen);
      inc(P,seplen);
    end;
    inc(i);
  until false;

  if finlen>0 then begin
    Move(pointer(fin)^,P^,finlen);
    inc(P,finlen);
  end;
  Assert(P-pointer(result)=len);
end;

procedure TSQLDBZEOSStatement.ExecutePrepared;
var i,n: integer;






|







 







|




|








|







|






|







959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
...
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
    end;
  end;
  inc(len,startlen+finlen);//add { and }
  SetLength(result,len);
  P := pointer(result);

  if startlen>0 then begin
    MoveFast(pointer(start)^,P^,startlen);
    inc(P,startlen);
  end;

  i := 0;
  repeat
    L := length(Values[i]);
    if L>0 then begin
................................................................................
          j := 0;
          while k+j<l do begin
            case Values[i][k+j] of
              '"': break;
              else inc(j);
            end;
          end;
          MoveFast(pointer(@Values[i][k])^,P^,j);
          inc(P,j);
          inc(k,j);
          case Values[i][k] of
            '"': begin
              MoveFast(pointer(dQuoteRepl)^,P^,dQuoteRepllen);
              inc(P,dQuoteRepllen);
              inc(k);
            end;
          end;
        end;
        P^ := '"';
        inc(p);
      end else begin
        MoveFast(pointer(Values[i])^,P^,L);
        inc(P,L);
      end;
    end;

    if i=high(Values) then
      Break;
    if seplen>0 then begin
      MoveFast(pointer(Sep)^,P^,seplen);
      inc(P,seplen);
    end;
    inc(i);
  until false;

  if finlen>0 then begin
    MoveFast(pointer(fin)^,P^,finlen);
    inc(P,finlen);
  end;
  Assert(P-pointer(result)=len);
end;

procedure TSQLDBZEOSStatement.ExecutePrepared;
var i,n: integer;

Changes to SynGdiPlus.pas.

648
649
650
651
652
653
654

655
656
657
658
659
660
661
662
663
664
665
666
....
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603

1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
/// helper to save a specified graphic into GIF/PNG/JPG/TIFF format
// - CompressionQuality is only used for gptJPG format saving
// and is expected to be from 0 to 100
// - if MaxPixelsForBiggestSide is set to something else than 0, the resulting
// picture biggest side won't exceed this pixel number
procedure SaveAsRawByteString(Graphic: TPersistent;

  out DataRawByteString; Format: TGDIPPictureType; CompressionQuality: integer=80;
  MaxPixelsForBiggestSide: cardinal=0; BitmapSetResolution: single=0);

/// helper to load a specified graphic from GIF/PNG/JPG/TIFF format content
function LoadFromRawByteString(const Picture: {$ifdef UNICODE}RawByteString{$else}AnsiString{$endif}): TBitmap;

/// helper function to create a bitmap from any GIF/PNG/JPG/TIFF/EMF/WMF file
// - if file extension if .EMF, the file is drawn with a special antialiased
// GDI+ drawing method (if the global Gdip var is a TGDIPlusFull instance)
function LoadFrom(const FileName: TFileName): TBitmap; overload;

/// helper function to create a bitmap from any EMF content
................................................................................
    SaveAs(Graphic,Stream,Format,CompressionQuality,MaxPixelsForBiggestSide,
      BitmapSetResolution);
  finally
    Stream.Free;
  end;
end;

{$ifndef UNICODE}
type RawByteString = AnsiString;
{$endif}

procedure SaveAsRawByteString(Graphic: TPersistent;

  out DataRawByteString; Format: TGDIPPictureType; CompressionQuality: integer=80;
  MaxPixelsForBiggestSide: cardinal=0; BitmapSetResolution: single=0); overload;
var Stream: TMemoryStream;
begin
  Stream := TMemoryStream.Create;
  try
    SaveAs(Graphic,Stream,Format,CompressionQuality,MaxPixelsForBiggestSide,
      BitmapSetResolution);
    SetString(RawByteString(DataRawByteString),PAnsiChar(Stream.Memory),Stream.Seek(0,soFromCurrent));
  finally
    Stream.Free;
  end;
end;

function LoadFromRawByteString(const Picture: {$ifdef UNICODE}RawByteString{$else}AnsiString{$endif}): TBitmap;
var ST: TStringStream;
begin
  Result := nil;
  if Picture='' then
    exit;
  ST := TStringStream.Create(Picture);
  try






>
|



|







 







|




>
|













|







648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
....
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
/// helper to save a specified graphic into GIF/PNG/JPG/TIFF format
// - CompressionQuality is only used for gptJPG format saving
// and is expected to be from 0 to 100
// - if MaxPixelsForBiggestSide is set to something else than 0, the resulting
// picture biggest side won't exceed this pixel number
procedure SaveAsRawByteString(Graphic: TPersistent;
  out DataRawByteString{$ifdef HASCODEPAGE}: RawByteString{$endif};
  Format: TGDIPPictureType; CompressionQuality: integer=80;
  MaxPixelsForBiggestSide: cardinal=0; BitmapSetResolution: single=0);

/// helper to load a specified graphic from GIF/PNG/JPG/TIFF format content
function LoadFromRawByteString(const Picture: {$ifdef HASCODEPAGE}RawByteString{$else}AnsiString{$endif}): TBitmap;

/// helper function to create a bitmap from any GIF/PNG/JPG/TIFF/EMF/WMF file
// - if file extension if .EMF, the file is drawn with a special antialiased
// GDI+ drawing method (if the global Gdip var is a TGDIPlusFull instance)
function LoadFrom(const FileName: TFileName): TBitmap; overload;

/// helper function to create a bitmap from any EMF content
................................................................................
    SaveAs(Graphic,Stream,Format,CompressionQuality,MaxPixelsForBiggestSide,
      BitmapSetResolution);
  finally
    Stream.Free;
  end;
end;

{$ifndef HASCODEPAGE}
type RawByteString = AnsiString;
{$endif}

procedure SaveAsRawByteString(Graphic: TPersistent;
  out DataRawByteString{$ifdef HASCODEPAGE}: RawByteString{$endif};
  Format: TGDIPPictureType; CompressionQuality: integer=80;
  MaxPixelsForBiggestSide: cardinal=0; BitmapSetResolution: single=0); overload;
var Stream: TMemoryStream;
begin
  Stream := TMemoryStream.Create;
  try
    SaveAs(Graphic,Stream,Format,CompressionQuality,MaxPixelsForBiggestSide,
      BitmapSetResolution);
    SetString(RawByteString(DataRawByteString),PAnsiChar(Stream.Memory),Stream.Seek(0,soFromCurrent));
  finally
    Stream.Free;
  end;
end;

function LoadFromRawByteString(const Picture: RawByteString): TBitmap;
var ST: TStringStream;
begin
  Result := nil;
  if Picture='' then
    exit;
  ST := TStringStream.Create(Picture);
  try

Changes to SynMongoDB.pas.

3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
  case value.VType of
    vtBoolean:  BSONWrite(name,value.VBoolean);
    vtInteger:  BSONWrite(name,value.VInteger);
    vtInt64:    BSONWrite(name,value.VInt64^);
    vtCurrency: BSONWrite(name,value.VCurrency^);
    vtExtended: BSONWrite(name,value.VExtended^);
    vtVariant:  BSONWriteVariant(name,value.VVariant^);
    vtString, vtAnsiString, {$ifdef UNICODE}vtUnicodeString,{$endif}
    vtPChar, vtChar, vtWideChar, vtWideString: begin
      VarRecToUTF8(value,tmp);
      BSONWrite(name,tmp);
    end;
    else raise EBSONException.CreateUtf8(
      '%.BSONWrite(TVarRec.VType=%)',[self,value.VType]);
  end;






|







3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
  case value.VType of
    vtBoolean:  BSONWrite(name,value.VBoolean);
    vtInteger:  BSONWrite(name,value.VInteger);
    vtInt64:    BSONWrite(name,value.VInt64^);
    vtCurrency: BSONWrite(name,value.VCurrency^);
    vtExtended: BSONWrite(name,value.VExtended^);
    vtVariant:  BSONWriteVariant(name,value.VVariant^);
    vtString, vtAnsiString, {$ifdef HASVARUSTRING}vtUnicodeString,{$endif}
    vtPChar, vtChar, vtWideChar, vtWideString: begin
      VarRecToUTF8(value,tmp);
      BSONWrite(name,tmp);
    end;
    else raise EBSONException.CreateUtf8(
      '%.BSONWrite(TVarRec.VType=%)',[self,value.VType]);
  end;

Changes to SynPdf.pas.

709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
....
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998

1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
....
6961
6962
6963
6964
6965
6966
6967
6968
6969
6970
6971
6972
6973
6974
6975
    fCodePage: integer;
    fAddGlyphFont: (fNone, fMain, fFallBack);
    fDoc: TPdfDocument;
    Tmp: array[0..511] of AnsiChar;
    /// internal Ansi->Unicode conversion, using the CodePage used in Create()
    // - caller must release the returned memory via FreeMem()
    function ToWideChar(const Ansi: PDFString; out DLen: Integer): PWideChar;
{$ifdef USE_UNISCRIBE}
    /// internal method using the Windows Uniscribe API
    // - return FALSE if PW was not appened to the PDF content, TRUE if OK
    function AddUnicodeHexTextUniScribe(PW: PWideChar; WinAnsiTTF: TPdfFontTrueType;
      NextLine: boolean; Canvas: TPdfCanvas): boolean;
{$endif}
    /// internal method NOT using the Windows Uniscribe API
    procedure AddUnicodeHexTextNoUniScribe(PW: PWideChar; TTF: TPdfFontTrueType;
      NextLine: boolean; Canvas: TPdfCanvas);
    /// internal methods handling font fall-back
    procedure AddGlyphFromChar(Char: WideChar; Canvas: TPdfCanvas;
      TTF: TPdfFontTrueType; NextLine: PBoolean);
    procedure AddGlyphFlush(Canvas: TPdfCanvas; TTF: TPdfFontTrueType; NextLine: PBoolean);
................................................................................
    // line by (tx ,ty)
    // - tx and ty are numbers expressed in unscaled text space units
    procedure MoveTextPoint(tx, ty: Single); {$ifdef HASINLINE}inline;{$endif} {  Td  }
    /// set the Text Matrix to a,b,c,d and the text line Matrix x,y
    procedure SetTextMatrix(a, b, c, d, x, y: Single);           {  Tm  }
    /// Move to the start of the next line
    procedure MoveToNextLine;                                    {  T*  }
{$ifdef UNICODE}
    /// Show a text string
    // - text is expected to be Unicode encoded
    // - if NextLine is TRUE, moves to the next line and show a text string;
    // in this case, method as the same effect as MoveToNextLine; ShowText(s);
    procedure ShowText(const text: UnicodeString; NextLine: boolean=false); overload; inline; {  Tj  or ' }

    /// Show a text string
    // - text is expected to be Ansi-Encoded, in the current CharSet; if
    // some Unicode or MBCS conversion is necessary, it will be notified to the
    // corresponding
    // - if NextLine is TRUE, moves to the next line and show a text string;
    // in this case, method as the same effect as MoveToNextLine; ShowText(s);
    procedure ShowText(const text: PDFString; NextLine: boolean=false); overload; {  Tj  or ' }
{$else}
    /// Show a text string
    // - text is expected to be Ansi-Encoded, in the current CharSet; if
    // some Unicode or MBCS conversion is necessary, it will be notified to the
    // corresponding
    // - if NextLine is TRUE, moves to the next line and show a text string;
    // in this case, method as the same effect as MoveToNextLine; ShowText(s);
    procedure ShowText(const text: PDFString; NextLine: boolean=false); overload; {  Tj  or ' }
{$endif}
    /// Show an Unicode Text string
    // - if NextLine is TRUE, moves to the next line and show a text string;
    // in this case, method as the same effect as MoveToNextLine; ShowText(s);
    procedure ShowText(PW: PWideChar; NextLine: boolean=false); overload;
      {$ifdef HASINLINE}inline;{$endif}
    /// Show an Unicode Text string, encoded as Glyphs or the current font
    // - PW must follow the ETO_GLYPH_INDEX layout, i.e. refers to an array as
................................................................................

procedure TPdfCanvas.MoveToNextLine;
begin
  if FContents<>nil then
    FContents.Writer.Add('T*'#10);
end;

{$ifdef UNICODE}

procedure TPdfCanvas.ShowText(const text: UnicodeString; NextLine: boolean);
begin // direct call of the unicode text drawing method below
  ShowText(pointer(text),NextLine);
end;

{$endif}






|




|







 







|





>







<
<
<
<
<
<
<
<
<







 







|







709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
....
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006









2007
2008
2009
2010
2011
2012
2013
....
6953
6954
6955
6956
6957
6958
6959
6960
6961
6962
6963
6964
6965
6966
6967
    fCodePage: integer;
    fAddGlyphFont: (fNone, fMain, fFallBack);
    fDoc: TPdfDocument;
    Tmp: array[0..511] of AnsiChar;
    /// internal Ansi->Unicode conversion, using the CodePage used in Create()
    // - caller must release the returned memory via FreeMem()
    function ToWideChar(const Ansi: PDFString; out DLen: Integer): PWideChar;
    {$ifdef USE_UNISCRIBE}
    /// internal method using the Windows Uniscribe API
    // - return FALSE if PW was not appened to the PDF content, TRUE if OK
    function AddUnicodeHexTextUniScribe(PW: PWideChar; WinAnsiTTF: TPdfFontTrueType;
      NextLine: boolean; Canvas: TPdfCanvas): boolean;
    {$endif}
    /// internal method NOT using the Windows Uniscribe API
    procedure AddUnicodeHexTextNoUniScribe(PW: PWideChar; TTF: TPdfFontTrueType;
      NextLine: boolean; Canvas: TPdfCanvas);
    /// internal methods handling font fall-back
    procedure AddGlyphFromChar(Char: WideChar; Canvas: TPdfCanvas;
      TTF: TPdfFontTrueType; NextLine: PBoolean);
    procedure AddGlyphFlush(Canvas: TPdfCanvas; TTF: TPdfFontTrueType; NextLine: PBoolean);
................................................................................
    // line by (tx ,ty)
    // - tx and ty are numbers expressed in unscaled text space units
    procedure MoveTextPoint(tx, ty: Single); {$ifdef HASINLINE}inline;{$endif} {  Td  }
    /// set the Text Matrix to a,b,c,d and the text line Matrix x,y
    procedure SetTextMatrix(a, b, c, d, x, y: Single);           {  Tm  }
    /// Move to the start of the next line
    procedure MoveToNextLine;                                    {  T*  }
    {$ifdef HASVARUSTRING}
    /// Show a text string
    // - text is expected to be Unicode encoded
    // - if NextLine is TRUE, moves to the next line and show a text string;
    // in this case, method as the same effect as MoveToNextLine; ShowText(s);
    procedure ShowText(const text: UnicodeString; NextLine: boolean=false); overload; inline; {  Tj  or ' }
    {$endif}
    /// Show a text string
    // - text is expected to be Ansi-Encoded, in the current CharSet; if
    // some Unicode or MBCS conversion is necessary, it will be notified to the
    // corresponding
    // - if NextLine is TRUE, moves to the next line and show a text string;
    // in this case, method as the same effect as MoveToNextLine; ShowText(s);
    procedure ShowText(const text: PDFString; NextLine: boolean=false); overload; {  Tj  or ' }









    /// Show an Unicode Text string
    // - if NextLine is TRUE, moves to the next line and show a text string;
    // in this case, method as the same effect as MoveToNextLine; ShowText(s);
    procedure ShowText(PW: PWideChar; NextLine: boolean=false); overload;
      {$ifdef HASINLINE}inline;{$endif}
    /// Show an Unicode Text string, encoded as Glyphs or the current font
    // - PW must follow the ETO_GLYPH_INDEX layout, i.e. refers to an array as
................................................................................

procedure TPdfCanvas.MoveToNextLine;
begin
  if FContents<>nil then
    FContents.Writer.Add('T*'#10);
end;

{$ifdef HASVARUSTRING}

procedure TPdfCanvas.ShowText(const text: UnicodeString; NextLine: boolean);
begin // direct call of the unicode text drawing method below
  ShowText(pointer(text),NextLine);
end;

{$endif}

Changes to SynSM.pas.

1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
      FValue := DOUBLE_TO_JSVAL(V.VExtended^);
    vtVariant:
      SetVariant(cx,V.VVariant^);
    vtWideString:
      SetWideString(cx,WideString(V.VPointer));
    vtAnsiString:
      SetAnsiChar(cx,V.VPointer,length(RawByteString(V.VAnsiString)),
{$ifndef UNICODE}     CP_UTF8);
{$else}               StringCodePage(RawByteString(V.VAnsiString)));
    vtUnicodeString:
      SetSynUnicode(cx,UnicodeString(V.VPointer));
{$endif}
    vtString:
      SetAnsiChar(cx,PAnsiChar(@V.VString^[1]),ord(V.VString^[0]),0);
    vtPChar:






|







1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
      FValue := DOUBLE_TO_JSVAL(V.VExtended^);
    vtVariant:
      SetVariant(cx,V.VVariant^);
    vtWideString:
      SetWideString(cx,WideString(V.VPointer));
    vtAnsiString:
      SetAnsiChar(cx,V.VPointer,length(RawByteString(V.VAnsiString)),
{$ifndef HASCODEPAGE} CP_UTF8);
{$else}               StringCodePage(RawByteString(V.VAnsiString)));
    vtUnicodeString:
      SetSynUnicode(cx,UnicodeString(V.VPointer));
{$endif}
    vtString:
      SetAnsiChar(cx,PAnsiChar(@V.VString^[1]),ord(V.VString^[0]),0);
    vtPChar:

Changes to SynSelfTests.pas.

1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
....
2078
2079
2080
2081
2082
2083
2084
2085

2086
2087
2088
2089
2090
2091
2092
....
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
....
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
....
2967
2968
2969
2970
2971
2972
2973


2974
2975
2976

2977

















2978

2979
2980
2981
2982
2983
2984
2985
....
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
....
3036
3037
3038
3039
3040
3041
3042


3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053

3054
3055
3056
3057
3058
3059
3060
....
4863
4864
4865
4866
4867
4868
4869

4870
4871
4872
4873
4874
4875
4876
....
5518
5519
5520
5521
5522
5523
5524
5525


5526
5527


5528
5529
5530
5531
5532
5533
5534
....
7958
7959
7960
7961
7962
7963
7964
7965
7966

7967
7968
7969
7970
7971
7972
7973
procedure TTestLowLevelCommon._TDynArray;
var AI, AI2: TIntegerDynArray;
    AU: TRawUTF8DynArray;
    AR: TRecs;
    AF: TFVs;
    AF2: TFV2s;
    i,j,k,Len, count,AIcount: integer;
    U: RawUTF8;
    P: PUTF8Char;
    PI: PIntegerArray;
    R: TRec;
    F, F1: TFV;
    F2: TFV2;
    City: TCity;
    Province: TProvince;
................................................................................
  end;
  W.CancelAll;
  W.AddDynArrayJSON(ARP);
  U := W.Text;
  Check(Hash32(U)={$ifdef CPU64}$9F98936D{$else}$54659D65{$endif});
  P := pointer(U);
  JSON_BASE64_MAGIC_UTF8 := RawUnicodeToUtf8(@MAGIC,2);
  Check(U='['+JSON_BASE64_MAGIC_UTF8+BinToBase64(ARP.SaveTo)+'"]');

  ARP.Clear;
  Check(ARP.LoadFromJSON(pointer(U))<>nil);
  if not CheckFailed(ARP.Count=1001) then
    for i := 0 to 1000 do
    with AR[i] do begin
      Check(A=i);
      Check(B=byte(i+1));
................................................................................
  {Check(A.Dyn[0]=0) bug in original VCL?}
  Check(C.Dyn[0]=10);
end;

procedure TTestLowLevelCommon.UrlEncoding;
var i: integer;
    s: RawByteString;
    name,value: RawUTF8;
    P: PUTF8Char;
    GUID2: TGUID;
    U: TURI;
const GUID: TGUID = '{c9a646d3-9c61-4cb7-bfcd-ee2522c8f633}';
procedure Test(const decoded,encoded: RawUTF8);
begin
  Check(UrlEncode(decoded)=encoded);
................................................................................
  Check(P^=#0);
  Check(name='name,complex');
  Check(value='value');
  for i := 0 to 100 do begin
    s := RandomString(i*5);
    Check(UrlDecode(UrlEncode(s))=s,string(s));
  end;
  s := BinToBase64URI(@GUID,sizeof(GUID));
  Check(s='00amyWGct0y_ze4lIsj2Mw');
  Base64FromURI(s);
  Check(Base64ToBinLength(pointer(s),length(s))=sizeof(GUID2));
  fillchar(GUID2,sizeof(GUID2),0);
  SynCommons.Base64Decode(Pointer(s),@GUID2,SizeOf(GUID2));
  Check(IsEqualGUID(GUID2,GUID));
  Check(U.From('toto.com'));
  Check(U.URI='http://toto.com/');
  Check(U.From('toto.com:123'));
  Check(U.URI='http://toto.com:123/');
  Check(U.From('https://toto.com:123/tata/titi'));
  Check(U.URI='https://toto.com:123/tata/titi');
................................................................................
  end;
end;

procedure TTestLowLevelCommon._UTF8;
procedure Test(CP: cardinal; const W: WinAnsiString);
var C: TSynAnsiConvert;
    L: integer;


    tmpA: array[0..127] of AnsiChar;
begin
  C := TSynAnsiConvert.Engine(CP);

  Check(C.UTF8ToAnsi(C.AnsiToUTF8(W))=W);

















  Check(C.RawUnicodeToAnsi(C.AnsiToRawUnicode(W))=W);

  FillChar(tmpA,SizeOf(tmpA),1);
  if CP=CP_UTF16 then
    exit;
  L := C.Utf8ToAnsiBuffer(RawByteString(W),tmpA,sizeof(tmpA));
  Check(L=StrLen(@tmpA));
  if L<sizeof(tmpA)-1 then
    Check(L=Length(W)) else
................................................................................
var i, CP, L: integer;
    W: WinAnsiString;
    WS: WideString;
    SU: SynUnicode;
    U, res, Up,Up2: RawUTF8;
    arr: TRawUTF8DynArray;
    PB: PByte;
{$ifndef DELPHI5OROLDER}
    q: RawUTF8;
{$endif}
    Unic: RawUnicode;
    WA: Boolean;
begin
  res := 'one,two,three';
  Check(StrLen(nil)=0);
  for i := length(res)+1 downto 1 do
    Check(StrLen(Pointer(@res[i]))=length(res)-i+1);
................................................................................
    Test(CP_UTF8,W);
    L := Length(W);
    if L and 1<>0 then
      SetLength(W,L-1); // force exact UTF-16 buffer length
    Test(CP_UTF16,W);
    W := WinAnsiString(RandomString(i*5));
    U := WinAnsiToUtf8(W);


    Check(Utf8ToWinAnsi(U)=W);
    Check(WinAnsiConvert.UTF8ToAnsi(WinAnsiConvert.AnsiToUTF8(W))=W);
    Check(WinAnsiConvert.RawUnicodeToAnsi(WinAnsiConvert.AnsiToRawUnicode(W))=W);
    if CurrentAnsiConvert.InheritsFrom(TSynAnsiFixedWidth) then begin
      Check(CurrentAnsiConvert.UTF8ToAnsi(CurrentAnsiConvert.AnsiToUTF8(W))=W);
      Check(CurrentAnsiConvert.RawUnicodeToAnsi(CurrentAnsiConvert.AnsiToRawUnicode(W))=W);
    end;
    Unic := Utf8DecodeToRawUnicode(U);
    res := RawUnicodeToUtf8(Unic);
    Check(res=U);
    Check(RawUnicodeToWinAnsi(Unic)=W);

    WS := UTF8ToWideString(U);
    Check(length(WS)=length(Unic)shr 1);
    if WS<>'' then
      Check(CompareMem(pointer(WS),pointer(Unic),length(WS)*sizeof(WideChar)));
    Check(integer(Utf8ToUnicodeLength(Pointer(U)))=length(WS));
    SU := UTF8ToSynUnicode(U);
    Check(length(SU)=length(Unic)shr 1);
................................................................................
    Trans: TTestCustomJSON2;
    Disco: TTestCustomDiscogs;
    Cache: TSQLRestCacheEntryValue;
{$ifndef DELPHI5OROLDER}
    peop: TSQLRecordPeople;
    K,U2: RawUTF8;
    Valid: boolean;

{$ifndef LVCL}
    Instance: TClassInstance;
    Coll, C2: TCollTst;
    MyItem: TCollTest;
    Comp: TComplexNumber;
    DA: TDynArray;
    F: TFV;
................................................................................
    Check(not IsString(V[2]));
    Check(not IsStringJSON(V[2]));
    Check(GetInteger(V[2])=a);
    Check(V[3]=nil);
    J := BinToBase64WithMagic(U);
    check(PInteger(J)^ and $00ffffff=JSON_BASE64_MAGIC);
{$ifndef DELPHI5OROLDER}
    check(BlobToTSQLRawBlob(pointer(J))=U);


    Base64MagicToBlob(@J[4],K);
    check(BlobToTSQLRawBlob(pointer(K))=U);


{    J := TSQLRestServer.JSONEncodeResult([r]);
    Check(SameValue(GetExtended(pointer(JSONDecode(J)),err),r)); }
    {$ifndef NOVARIANTS}
    with TTextWriter.CreateOwnedStream do
    try
      AddVariant(a);
      Add(',');
................................................................................
procedure TTestCryptographicRoutines._Adler32;
begin
  Check(Adler32SelfTest);
end;

procedure TTestCryptographicRoutines._Base64;
const
  Value64: RawByteString = 'SGVsbG8gL2Mn6XRhaXQg5+Ar';
var tmp, b64: RawByteString;

    Value: WinAnsiString;
    i, L: Integer;
begin
  Value := 'Hello /c''0tait 67+';
  Value[10] := #$E9;
  Value[16] := #$E7;
  Value[17] := #$E0;






|







 







|
>







 







|







 







|
|
|
|

|







 







>
>



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

>







 







|

|







 







>
>







<



>







 







>







 







|
>
>

|
>
>







 







|
|
>







1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
....
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
....
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
....
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
....
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
....
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
....
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073

3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
....
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
....
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
....
7987
7988
7989
7990
7991
7992
7993
7994
7995
7996
7997
7998
7999
8000
8001
8002
8003
procedure TTestLowLevelCommon._TDynArray;
var AI, AI2: TIntegerDynArray;
    AU: TRawUTF8DynArray;
    AR: TRecs;
    AF: TFVs;
    AF2: TFV2s;
    i,j,k,Len, count,AIcount: integer;
    U,U2: RawUTF8;
    P: PUTF8Char;
    PI: PIntegerArray;
    R: TRec;
    F, F1: TFV;
    F2: TFV2;
    City: TCity;
    Province: TProvince;
................................................................................
  end;
  W.CancelAll;
  W.AddDynArrayJSON(ARP);
  U := W.Text;
  Check(Hash32(U)={$ifdef CPU64}$9F98936D{$else}$54659D65{$endif});
  P := pointer(U);
  JSON_BASE64_MAGIC_UTF8 := RawUnicodeToUtf8(@MAGIC,2);
  U2 := RawUTF8('[')+JSON_BASE64_MAGIC_UTF8+RawUTF8(BinToBase64(ARP.SaveTo))+RawUTF8('"]');
  Check(U=U2);
  ARP.Clear;
  Check(ARP.LoadFromJSON(pointer(U))<>nil);
  if not CheckFailed(ARP.Count=1001) then
    for i := 0 to 1000 do
    with AR[i] do begin
      Check(A=i);
      Check(B=byte(i+1));
................................................................................
  {Check(A.Dyn[0]=0) bug in original VCL?}
  Check(C.Dyn[0]=10);
end;

procedure TTestLowLevelCommon.UrlEncoding;
var i: integer;
    s: RawByteString;
    name,value,utf: RawUTF8;
    P: PUTF8Char;
    GUID2: TGUID;
    U: TURI;
const GUID: TGUID = '{c9a646d3-9c61-4cb7-bfcd-ee2522c8f633}';
procedure Test(const decoded,encoded: RawUTF8);
begin
  Check(UrlEncode(decoded)=encoded);
................................................................................
  Check(P^=#0);
  Check(name='name,complex');
  Check(value='value');
  for i := 0 to 100 do begin
    s := RandomString(i*5);
    Check(UrlDecode(UrlEncode(s))=s,string(s));
  end;
  utf := BinToBase64URI(@GUID,sizeof(GUID));
  Check(utf='00amyWGct0y_ze4lIsj2Mw');
  Base64FromURI(utf);
  Check(Base64ToBinLength(pointer(utf),length(utf))=sizeof(GUID2));
  fillchar(GUID2,sizeof(GUID2),0);
  SynCommons.Base64Decode(Pointer(utf),@GUID2,SizeOf(GUID2));
  Check(IsEqualGUID(GUID2,GUID));
  Check(U.From('toto.com'));
  Check(U.URI='http://toto.com/');
  Check(U.From('toto.com:123'));
  Check(U.URI='http://toto.com:123/');
  Check(U.From('https://toto.com:123/tata/titi'));
  Check(U.URI='https://toto.com:123/tata/titi');
................................................................................
  end;
end;

procedure TTestLowLevelCommon._UTF8;
procedure Test(CP: cardinal; const W: WinAnsiString);
var C: TSynAnsiConvert;
    L: integer;
    A: RawByteString;
    U: RawUTF8;
    tmpA: array[0..127] of AnsiChar;
begin
  C := TSynAnsiConvert.Engine(CP);
  Check(C.CodePage=CP);
  U := C.AnsiToUTF8(W);
  A := C.UTF8ToAnsi(U);
  Check(length(W)=length(A));
  if W='' then
    exit;
  {$ifdef HASCODEPAGE}
  {$ifndef FPC}
  Check(StringCodePage(W)=1252);
  {$endif}
  CP := StringCodePage(A);
  Check(CP=C.CodePage);
  {$endif}
  {$ifdef FPC}
  if CP=CP_UTF16 then
    exit;
  Check(CompareMem(pointer(W),pointer(A),length(W)));
  {$else}
  Check(A=W);
  Check(C.RawUnicodeToAnsi(C.AnsiToRawUnicode(W))=W);
  {$endif}
  FillChar(tmpA,SizeOf(tmpA),1);
  if CP=CP_UTF16 then
    exit;
  L := C.Utf8ToAnsiBuffer(RawByteString(W),tmpA,sizeof(tmpA));
  Check(L=StrLen(@tmpA));
  if L<sizeof(tmpA)-1 then
    Check(L=Length(W)) else
................................................................................
var i, CP, L: integer;
    W: WinAnsiString;
    WS: WideString;
    SU: SynUnicode;
    U, res, Up,Up2: RawUTF8;
    arr: TRawUTF8DynArray;
    PB: PByte;
    {$ifndef DELPHI5OROLDER}
    q: RawUTF8;
    {$endif}
    Unic: RawUnicode;
    WA: Boolean;
begin
  res := 'one,two,three';
  Check(StrLen(nil)=0);
  for i := length(res)+1 downto 1 do
    Check(StrLen(Pointer(@res[i]))=length(res)-i+1);
................................................................................
    Test(CP_UTF8,W);
    L := Length(W);
    if L and 1<>0 then
      SetLength(W,L-1); // force exact UTF-16 buffer length
    Test(CP_UTF16,W);
    W := WinAnsiString(RandomString(i*5));
    U := WinAnsiToUtf8(W);
    Unic := Utf8DecodeToRawUnicode(U);
    {$ifndef FPC_HAS_CPSTRING} // buggy FPC
    Check(Utf8ToWinAnsi(U)=W);
    Check(WinAnsiConvert.UTF8ToAnsi(WinAnsiConvert.AnsiToUTF8(W))=W);
    Check(WinAnsiConvert.RawUnicodeToAnsi(WinAnsiConvert.AnsiToRawUnicode(W))=W);
    if CurrentAnsiConvert.InheritsFrom(TSynAnsiFixedWidth) then begin
      Check(CurrentAnsiConvert.UTF8ToAnsi(CurrentAnsiConvert.AnsiToUTF8(W))=W);
      Check(CurrentAnsiConvert.RawUnicodeToAnsi(CurrentAnsiConvert.AnsiToRawUnicode(W))=W);
    end;

    res := RawUnicodeToUtf8(Unic);
    Check(res=U);
    Check(RawUnicodeToWinAnsi(Unic)=W);
    {$endif FPC_HAS_CPSTRING}
    WS := UTF8ToWideString(U);
    Check(length(WS)=length(Unic)shr 1);
    if WS<>'' then
      Check(CompareMem(pointer(WS),pointer(Unic),length(WS)*sizeof(WideChar)));
    Check(integer(Utf8ToUnicodeLength(Pointer(U)))=length(WS));
    SU := UTF8ToSynUnicode(U);
    Check(length(SU)=length(Unic)shr 1);
................................................................................
    Trans: TTestCustomJSON2;
    Disco: TTestCustomDiscogs;
    Cache: TSQLRestCacheEntryValue;
{$ifndef DELPHI5OROLDER}
    peop: TSQLRecordPeople;
    K,U2: RawUTF8;
    Valid: boolean;
    RB: TSQLRawBlob;
{$ifndef LVCL}
    Instance: TClassInstance;
    Coll, C2: TCollTst;
    MyItem: TCollTest;
    Comp: TComplexNumber;
    DA: TDynArray;
    F: TFV;
................................................................................
    Check(not IsString(V[2]));
    Check(not IsStringJSON(V[2]));
    Check(GetInteger(V[2])=a);
    Check(V[3]=nil);
    J := BinToBase64WithMagic(U);
    check(PInteger(J)^ and $00ffffff=JSON_BASE64_MAGIC);
{$ifndef DELPHI5OROLDER}
    RB := BlobToTSQLRawBlob(pointer(J));
    check(length(RB)=length(U)); // RB=U is buggy under FPC :(
    check(CompareMem(pointer(RB),pointer(U),length(U)));
    Base64MagicToBlob(@J[4],K);
    RB := BlobToTSQLRawBlob(pointer(K));
    check(length(RB)=length(U)); // RB=U is buggy under FPC :(
    check(CompareMem(pointer(RB),pointer(U),length(U)));
{    J := TSQLRestServer.JSONEncodeResult([r]);
    Check(SameValue(GetExtended(pointer(JSONDecode(J)),err),r)); }
    {$ifndef NOVARIANTS}
    with TTextWriter.CreateOwnedStream do
    try
      AddVariant(a);
      Add(',');
................................................................................
procedure TTestCryptographicRoutines._Adler32;
begin
  Check(Adler32SelfTest);
end;

procedure TTestCryptographicRoutines._Base64;
const
  Value64: RawUTF8 = 'SGVsbG8gL2Mn6XRhaXQg5+Ar';
var tmp: RawByteString;
    b64: RawUTF8;
    Value: WinAnsiString;
    i, L: Integer;
begin
  Value := 'Hello /c''0tait 67+';
  Value[10] := #$E9;
  Value[16] := #$E7;
  Value[17] := #$E0;

Changes to SynZip.pas.

238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
// - by default, will use the deflate/.zip header-less format, but you may set
// ZlibFormat=true to add an header, as expected by zlib (and pdf)
function UnCompressStream(src: pointer; srcLen: integer; aStream: TStream;
  checkCRC: PCardinal; ZlibFormat: Boolean=false): cardinal;


type
{$ifdef UNICODE}
  /// define a raw storage string type, used for data buffer management
  ZipString = type RawByteString;
{$else}
  /// define a raw storage string type, used for data buffer management
  ZipString = type AnsiString;
  /// as available in newer Delphi versions
  NativeUInt = cardinal;






|







238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
// - by default, will use the deflate/.zip header-less format, but you may set
// ZlibFormat=true to add an header, as expected by zlib (and pdf)
function UnCompressStream(src: pointer; srcLen: integer; aStream: TStream;
  checkCRC: PCardinal; ZlibFormat: Boolean=false): cardinal;


type
{$ifdef HASCODEPAGE}
  /// define a raw storage string type, used for data buffer management
  ZipString = type RawByteString;
{$else}
  /// define a raw storage string type, used for data buffer management
  ZipString = type AnsiString;
  /// as available in newer Delphi versions
  NativeUInt = cardinal;

Changes to Synopse.inc.

135
136
137
138
139
140
141

142
143
144
145
146
147
148
...
181
182
183
184
185
186
187

188
189
190
191
192
193
194
...
232
233
234
235
236
237
238

239
240
241
242
243
244
245
246
247



248
249
250
251
252
253
254
  // LVCL does not support variants
  {$define NOVARIANTS}
{$endif}

{$ifdef UNICODE}
  {$undef ENHANCEDRTL} // Delphi 2009 and up don't have our Enhanced Runtime library
  {$define HASVARUSTRING}

  { due to a bug in Delphi 2009+, we need to fake inheritance of record,
    since TDynArrayHashed = object(TDynArray) fails to initialize
    http://blog.synopse.info/post/2011/01/29/record-and-object-issue-in-Delphi-2010 }
  {$define UNDIRECTDYNARRAY}
{$endif}


................................................................................
{$ifdef FPC}

  {$MODE DELPHI} // e.g. for asm syntax - disabled for FPC 2.6 compatibility
  {$INLINE ON}
  {$MINENUMSIZE 1}
  {$PACKSET 1}
  {$PACKENUM 1}


  {$undef ENHANCEDRTL}    // there is no version of our Enhanced RTL for FPC
  {$undef DOPATCHTRTL}
  {$define USETYPEINFO}  // will use SynFPCTypInfo.pas wrapper
  {$define HASINLINE}
  {$define NODELPHIASM}   // ignore low-level System.@LStrFromPCharLen calls
  {$define HASAESNI}
................................................................................
  {$endif}

  {$define FPC_OR_PUREPASCAL}
  {$define FPC_OR_KYLIX}
  // exceptions interception code in FPC differs from Delphi
  {$define NOEXCEPTIONINTERCEPT}


  {$ifdef VER2_7}
    {$define ISFPC27}
  {$endif}
  {$ifdef VER3_0}
    {$define ISFPC27}
  {$endif}
  {$ifdef VER3_1}
    {$define ISFPC27}
  {$endif}



  {$ifdef ISFPC27}
    {$define ISFPC271}
    {$define HASVARUSTRING}
    {$define HASVARUSTRARG}
    // defined if the http://mantis.freepascal.org/view.php?id=26773 bug is fixed
    // you should use 2.7.1/trunk branch in revision 28995 from 2014-11-05T22:17:54
    // => this will change the TInvokeableVariantType.SetProperty() signature






>







 







>







 







>









>
>
>







135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
...
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
  // LVCL does not support variants
  {$define NOVARIANTS}
{$endif}

{$ifdef UNICODE}
  {$undef ENHANCEDRTL} // Delphi 2009 and up don't have our Enhanced Runtime library
  {$define HASVARUSTRING}
  {$define HASCODEPAGE}
  { due to a bug in Delphi 2009+, we need to fake inheritance of record,
    since TDynArrayHashed = object(TDynArray) fails to initialize
    http://blog.synopse.info/post/2011/01/29/record-and-object-issue-in-Delphi-2010 }
  {$define UNDIRECTDYNARRAY}
{$endif}


................................................................................
{$ifdef FPC}

  {$MODE DELPHI} // e.g. for asm syntax - disabled for FPC 2.6 compatibility
  {$INLINE ON}
  {$MINENUMSIZE 1}
  {$PACKSET 1}
  {$PACKENUM 1}
  {$CODEPAGE UTF8} // otherwise unexpected behavior occurs in most cases

  {$undef ENHANCEDRTL}    // there is no version of our Enhanced RTL for FPC
  {$undef DOPATCHTRTL}
  {$define USETYPEINFO}  // will use SynFPCTypInfo.pas wrapper
  {$define HASINLINE}
  {$define NODELPHIASM}   // ignore low-level System.@LStrFromPCharLen calls
  {$define HASAESNI}
................................................................................
  {$endif}

  {$define FPC_OR_PUREPASCAL}
  {$define FPC_OR_KYLIX}
  // exceptions interception code in FPC differs from Delphi
  {$define NOEXCEPTIONINTERCEPT}

  // {$if FPC_FULLVERSION>20700} does not compile under Delphi 6-7 :(
  {$ifdef VER2_7}
    {$define ISFPC27}
  {$endif}
  {$ifdef VER3_0}
    {$define ISFPC27}
  {$endif}
  {$ifdef VER3_1}
    {$define ISFPC27}
  {$endif}
  {$ifdef FPC_HAS_CPSTRING}
    {$define HASCODEPAGE} // UNICODE means {$mode delphiunicode}
  {$endif}
  {$ifdef ISFPC27}
    {$define ISFPC271}
    {$define HASVARUSTRING}
    {$define HASVARUSTRARG}
    // defined if the http://mantis.freepascal.org/view.php?id=26773 bug is fixed
    // you should use 2.7.1/trunk branch in revision 28995 from 2014-11-05T22:17:54
    // => this will change the TInvokeableVariantType.SetProperty() signature

Changes to SynopseCommit.inc.

1
'1.18.2255'
|
1
'1.18.2256'