db.msgpack.pas
unit db.msgpack; // cxg 2024-12-12 only for delphi interface uses classes, SysUtils {$IFDEF UNICODE}, Generics.Collections{$ELSE}, Contnrs{$ENDIF} {$IFDEF MSWINDOWS}, Windows{$ENDIF} , Variants; type {$IF RTLVersion<25} IntPtr = Integer; {$IFEND IntPtr} {$IF CompilerVersion < 18} // before delphi 2007 TBytes = array of Byte; {$IFEND} TMsgPackType = (mptUnknown, mptNull, mptMap, mptArray, mptString, mptInteger, mptBoolean, mptFloat, mptSingle, mptDateTime, mptBinary); // reserved IMsgPack = interface ['{37D3E479-7A46-435A-914D-08FBDA75B50E}'] end; // copy from qmsgPack TMsgPackValue = packed record ValueType: Byte; case Integer of 0: (U8Val: Byte); 1: (I8Val: Shortint); 2: (U16Val: Word); 3: (I16Val: Smallint); 4: (U32Val: Cardinal); 5: (I32Val: Integer); 6: (U64Val: UInt64); 7: (I64Val: Int64); // 8:(F32Val:Single); // 9:(F64Val:Double); 10: (BArray: array [0 .. 16] of Byte); end; TMsgPackSetting = class(TObject) private FCaseSensitive: Boolean; public property CaseSensitive: Boolean read FCaseSensitive write FCaseSensitive; end; TMsgPack = class(TObject) private FParent: TMsgPack; FLowerName: string; FName: string; FValue: TBytes; FDataType: TMsgPackType; {$IFDEF UNICODE} FChildren: TObjectList<TMsgPack>; {$ELSE} FChildren: TObjectList; {$ENDIF} procedure InnerAddToChildren(pvDataType: TMsgPackType; obj: TMsgPack); function InnerAdd(pvDataType: TMsgPackType): TMsgPack; overload; function InnerAdd(): TMsgPack; overload; function GetCount: Integer; procedure InnerEncodeToStream(pvStream: TStream); procedure InnerParseFromStream(pvStream: TStream); procedure setName(pvName: string); private function getAsString: String; procedure setAsString(pvValue: string); function getAsInteger: Int64; procedure setAsInteger(pvValue: Int64); function GetAsBoolean: Boolean; procedure SetAsBoolean(const Value: Boolean); procedure SetAsFloat(const Value: Double); function GetAsFloat: Double; procedure SetAsDateTime(const Value: TDateTime); function GetAsDateTime: TDateTime; function GetAsVariant: Variant; procedure SetAsVariant(const Value: Variant); procedure SetAsSingle(const Value: Single); function GetAsSingle: Single; procedure SetAsBytes(const Value: TBytes); function GetAsBytes: TBytes; procedure CheckObjectDataType(ANewType: TMsgPackType); function FindObj(pvName: string): TMsgPack; function IndexOf(pvName: string): Integer; function IndexOfCaseSensitive(pvName: string): Integer; function IndexOfIgnoreSensitive(pvLowerCaseName: string): Integer; private /// <summary> /// find object index by a path /// </summary> function InnerFindPathObject(pvPath: string; var vParent: TMsgPack; var vIndex: Integer): TMsgPack; function GetO(pvPath: String): TMsgPack; procedure SetO(pvPath: String; const Value: TMsgPack); function GetS(pvPath: String): string; procedure SetS(pvPath: String; const Value: string); function GetI(pvPath: String): Int64; procedure SetI(pvPath: String; const Value: Int64); function GetB(pvPath: String): Boolean; procedure SetB(pvPath: String; const Value: Boolean); function GetD(pvPath: String): Double; procedure SetD(pvPath: String; const Value: Double); function GetItems(AIndex: Integer): TMsgPack; function GetV(pvPath: String): Variant; procedure SetV(pvPath: String; const Value: Variant); public constructor Create; destructor Destroy; override; procedure Clear; property Count: Integer read GetCount; procedure LoadStream(pvStream: TStream; pvLen: Cardinal = 0); procedure SaveStream(pvStream: TStream); procedure LoadBinaryFromFile(pvFileName: String); procedure SaveBinaryToFile(pvFileName: String); procedure ToStream(pvStream: TStream); procedure ToFile(pvFileName: string); function ToRaw: RawByteString; procedure FromStream(pvStream: TStream); procedure DecodeFromFile(pvFileName: string); procedure FromRaw(const raw: RawByteString); function ToBytes: TBytes; procedure FromBytes(pvBytes: TBytes); function Add(pvNameKey, pvValue: string): TMsgPack; overload; function Add(pvNameKey: string; pvValue: Int64): TMsgPack; overload; function Add(pvNameKey: string; pvValue: TBytes): TMsgPack; overload; function Add(pvNameKey: String): TMsgPack; overload; function Add: TMsgPack; overload; function AddArrayChild: TMsgPack; function path(pvPath: string): TMsgPack; /// <summary> /// remove and free object /// false : object is not found! /// </summary> function DeleteObject(pvPath: String): Boolean; property AsInteger: Int64 read getAsInteger write setAsInteger; property AsString: string read getAsString write setAsString; property AsBoolean: Boolean read GetAsBoolean write SetAsBoolean; property AsFloat: Double read GetAsFloat write SetAsFloat; property AsSingle: Single read GetAsSingle write SetAsSingle; property AsDateTime: TDateTime read GetAsDateTime write SetAsDateTime; property AsVariant: Variant read GetAsVariant write SetAsVariant; property AsBytes: TBytes read GetAsBytes write SetAsBytes; property O[pvPath: String]: TMsgPack read GetO write SetO; property S[pvPath: String]: string read GetS write SetS; property I[pvPath: String]: Int64 read GetI write SetI; property B[pvPath: String]: Boolean read GetB write SetB; property D[pvPath: String]: Double read GetD write SetD; property V[pvPath: String]: Variant read GetV write SetV; property Items[AIndex: Integer]: TMsgPack read GetItems; default; end; {$IFNDEF unicode} type RawByteString = AnsiString; {$ENDIF} TRawByteStringStream = class(TStream) protected fDataString: RawByteString; fPosition: Integer; procedure SetSize(NewSize: Longint); override; public constructor Create(const aString: RawByteString = ''); overload; function Read(var Buffer; Count: Longint): Longint; override; function Seek(Offset: Longint; Origin: Word): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; property DataString: RawByteString read fDataString write fDataString; end; implementation resourcestring SVariantConvertNotSupport = 'type to convert not support!。'; SCannotAddChild = 'Can''t add child in this node!'; function swap16(const V): Word; begin // FF, EE : EE->1, FF->2 PByte(@result)^ := PByte(IntPtr(@V) + 1)^; PByte(IntPtr(@result) + 1)^ := PByte(@V)^; end; function swap32(const V): Cardinal; begin // FF, EE, DD, CC : CC->1, DD->2, EE->3, FF->4 PByte(@result)^ := PByte(IntPtr(@V) + 3)^; PByte(IntPtr(@result) + 1)^ := PByte(IntPtr(@V) + 2)^; PByte(IntPtr(@result) + 2)^ := PByte(IntPtr(@V) + 1)^; PByte(IntPtr(@result) + 3)^ := PByte(@V)^; end; function swap64(const V): Int64; begin // FF, EE, DD, CC, BB, AA, 99, 88 : 88->1 ,99->2 .... PByte(@result)^ := PByte(IntPtr(@V) + 7)^; PByte(IntPtr(@result) + 1)^ := PByte(IntPtr(@V) + 6)^; PByte(IntPtr(@result) + 2)^ := PByte(IntPtr(@V) + 5)^; PByte(IntPtr(@result) + 3)^ := PByte(IntPtr(@V) + 4)^; PByte(IntPtr(@result) + 4)^ := PByte(IntPtr(@V) + 3)^; PByte(IntPtr(@result) + 5)^ := PByte(IntPtr(@V) + 2)^; PByte(IntPtr(@result) + 6)^ := PByte(IntPtr(@V) + 1)^; PByte(IntPtr(@result) + 7)^ := PByte(@V)^; end; // v and outVal is can't the same value procedure swap64Ex(const V; out outVal); begin // FF, EE, DD, CC, BB, AA, 99, 88 : 88->1 ,99->2 .... PByte(@outVal)^ := PByte(IntPtr(@V) + 7)^; PByte(IntPtr(@outVal) + 1)^ := PByte(IntPtr(@V) + 6)^; PByte(IntPtr(@outVal) + 2)^ := PByte(IntPtr(@V) + 5)^; PByte(IntPtr(@outVal) + 3)^ := PByte(IntPtr(@V) + 4)^; PByte(IntPtr(@outVal) + 4)^ := PByte(IntPtr(@V) + 3)^; PByte(IntPtr(@outVal) + 5)^ := PByte(IntPtr(@V) + 2)^; PByte(IntPtr(@outVal) + 6)^ := PByte(IntPtr(@V) + 1)^; PByte(IntPtr(@outVal) + 7)^ := PByte(@V)^; end; // v and outVal is can't the same value procedure swap32Ex(const V; out outVal); begin // FF, EE, DD, CC : CC->1, DD->2, EE->3, FF->4 PByte(@outVal)^ := PByte(IntPtr(@V) + 3)^; PByte(IntPtr(@outVal) + 1)^ := PByte(IntPtr(@V) + 2)^; PByte(IntPtr(@outVal) + 2)^ := PByte(IntPtr(@V) + 1)^; PByte(IntPtr(@outVal) + 3)^ := PByte(@V)^; end; // v and outVal is can't the same value procedure swap16Ex(const V; out outVal); begin // FF, EE : EE->1, FF->2 PByte(@outVal)^ := PByte(IntPtr(@V) + 1)^; PByte(IntPtr(@outVal) + 1)^ := PByte(@V)^; end; // overload swap, result type is integer, because single maybe NaN function swap(V: Single): Integer; overload; begin swap32Ex(V, result); end; // overload swap function swap(V: Word): Word; overload; begin swap16Ex(V, result); end; // overload swap function swap(V: Cardinal): Cardinal; overload; begin swap32Ex(V, result); end; // swap , result type is Int64, because Double maybe NaN function swap(V: Double): Int64; overload; begin swap64Ex(V, result); end; // copy from qstring function BinToHex(p: Pointer; l: Integer; ALowerCase: Boolean): string; const B2HConvert: array [0 .. 15] of Char = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); B2HConvertL: array [0 .. 15] of Char = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); var pd: PChar; pb: PByte; begin if SizeOf(Char) = 2 then begin SetLength(result, l shl 1); end else begin SetLength(result, l); end; pd := PChar(result); pb := p; if ALowerCase then begin while l > 0 do begin pd^ := B2HConvertL[pb^ shr 4]; Inc(pd); pd^ := B2HConvertL[pb^ and $0F]; Inc(pd); Inc(pb); Dec(l); end; end else begin while l > 0 do begin pd^ := B2HConvert[pb^ shr 4]; Inc(pd); pd^ := B2HConvert[pb^ and $0F]; Inc(pd); Inc(pb); Dec(l); end; end; end; function getFirst(var strPtr: PChar; splitChars: TSysCharSet): string; var oPtr: PChar; l: Cardinal; begin oPtr := strPtr; result := ''; while True do begin if (strPtr^ in splitChars) then begin l := strPtr - oPtr; if l > 0 then begin {$IFDEF UNICODE} SetLength(result, l); Move(oPtr^, PChar(result)^, l shl 1); {$ELSE} SetLength(result, l); Move(oPtr^, PChar(result)^, l); {$ENDIF} break; end; end else if (strPtr^ = #0) then begin l := strPtr - oPtr; if l > 0 then begin {$IFDEF UNICODE} SetLength(result, l); Move(oPtr^, PChar(result)^, l shl 1); {$ELSE} SetLength(result, l); Move(oPtr^, PChar(result)^, l); {$ENDIF} end; break; end; Inc(strPtr); end; end; function Utf8DecodeEx(pvValue: {$IFDEF UNICODE}TBytes{$ELSE}AnsiString{$ENDIF}; len: Cardinal): string; {$IFDEF UNICODE} var lvBytes: TBytes; {$ENDIF} begin {$IFDEF UNICODE} lvBytes := TEncoding.Convert(TEncoding.UTF8, TEncoding.Unicode, pvValue); SetLength(result, Length(lvBytes) shr 1); Move(lvBytes[0], PChar(result)^, Length(lvBytes)); {$ELSE} result := UTF8Decode(pvValue); {$ENDIF} end; function Utf8EncodeEx(pvValue: string): {$IFDEF UNICODE}TBytes{$ELSE}AnsiString{$ENDIF}; {$IFDEF UNICODE} var lvBytes: TBytes; len: Cardinal; {$ENDIF} begin {$IFDEF UNICODE} len := Length(pvValue) shl 1; SetLength(lvBytes, len); Move(PChar(pvValue)^, lvBytes[0], len); result := TEncoding.Convert(TEncoding.Unicode, TEncoding.UTF8, lvBytes); {$ELSE} result := UTF8Encode(pvValue); {$ENDIF} end; // copy from qmsgPack procedure writeString(pvValue: string; pvStream: TStream); var lvRawData: {$IFDEF UNICODE}TBytes{$ELSE}AnsiString{$ENDIF}; l: Integer; lvValue: TMsgPackValue; begin lvRawData := Utf8EncodeEx(pvValue); l := Length(lvRawData); // // fixstr stores a byte array whose length is upto 31 bytes: // +--------+========+ // |101XXXXX| data | // +--------+========+ // // str 8 stores a byte array whose length is upto (2^8)-1 bytes: // +--------+--------+========+ // | 0xd9 |YYYYYYYY| data | // +--------+--------+========+ // // str 16 stores a byte array whose length is upto (2^16)-1 bytes: // +--------+--------+--------+========+ // | 0xda |ZZZZZZZZ|ZZZZZZZZ| data | // +--------+--------+--------+========+ // // str 32 stores a byte array whose length is upto (2^32)-1 bytes: // +--------+--------+--------+--------+--------+========+ // | 0xdb |AAAAAAAA|AAAAAAAA|AAAAAAAA|AAAAAAAA| data | // +--------+--------+--------+--------+--------+========+ // // where // * XXXXX is a 5-bit unsigned integer which represents N // * YYYYYYYY is a 8-bit unsigned integer which represents N // * ZZZZZZZZ_ZZZZZZZZ is a 16-bit big-endian unsigned integer which represents N // * AAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA is a 32-bit big-endian unsigned integer which represents N // * N is the length of data if l <= 31 then begin lvValue.ValueType := $A0 + Byte(l); pvStream.WriteBuffer(lvValue.ValueType, 1); end else if l <= 255 then begin lvValue.ValueType := $D9; lvValue.U8Val := Byte(l); pvStream.WriteBuffer(lvValue, 2); end else if l <= 65535 then begin lvValue.ValueType := $DA; lvValue.U16Val := ((l shr 8) and $FF) or ((l shl 8) and $FF00); pvStream.Write(lvValue, 3); end else begin lvValue.ValueType := $DB; lvValue.BArray[0] := (l shr 24) and $FF; lvValue.BArray[1] := (l shr 16) and $FF; lvValue.BArray[2] := (l shr 8) and $FF; lvValue.BArray[3] := l and $FF; pvStream.WriteBuffer(lvValue, 5); end; pvStream.Write(PByte(lvRawData)^, l); end; procedure WriteBinary(p: PByte; l: Integer; pvStream: TStream); var lvValue: TMsgPackValue; begin if l <= 255 then begin lvValue.ValueType := $C4; lvValue.U8Val := Byte(l); pvStream.WriteBuffer(lvValue, 2); end else if l <= 65535 then begin lvValue.ValueType := $C5; lvValue.BArray[0] := (l shr 8) and $FF; lvValue.BArray[1] := l and $FF; pvStream.WriteBuffer(lvValue, 3); end else begin lvValue.ValueType := $C6; lvValue.BArray[0] := (l shr 24) and $FF; lvValue.BArray[1] := (l shr 16) and $FF; lvValue.BArray[2] := (l shr 8) and $FF; lvValue.BArray[3] := l and $FF; pvStream.WriteBuffer(lvValue, 5); end; pvStream.WriteBuffer(p^, l); end; // copy from qmsgPack procedure WriteInt(const iVal: Int64; AStream: TStream); var lvValue: TMsgPackValue; begin if iVal >= 0 then begin if iVal <= 127 then begin lvValue.U8Val := Byte(iVal); AStream.WriteBuffer(lvValue.U8Val, 1); end else if iVal <= 255 then // UInt8 begin lvValue.ValueType := $CC; lvValue.U8Val := Byte(iVal); AStream.WriteBuffer(lvValue, 2); end else if iVal <= 65535 then begin lvValue.ValueType := $CD; lvValue.BArray[0] := (iVal shr 8); lvValue.BArray[1] := (iVal and $FF); AStream.WriteBuffer(lvValue, 3); end else if iVal <= Cardinal($FFFFFFFF) then begin lvValue.ValueType := $CE; lvValue.BArray[0] := (iVal shr 24) and $FF; lvValue.BArray[1] := (iVal shr 16) and $FF; lvValue.BArray[2] := (iVal shr 8) and $FF; lvValue.BArray[3] := iVal and $FF; AStream.WriteBuffer(lvValue, 5); end else begin lvValue.ValueType := $CF; lvValue.BArray[0] := (iVal shr 56) and $FF; lvValue.BArray[1] := (iVal shr 48) and $FF; lvValue.BArray[2] := (iVal shr 40) and $FF; lvValue.BArray[3] := (iVal shr 32) and $FF; lvValue.BArray[4] := (iVal shr 24) and $FF; lvValue.BArray[5] := (iVal shr 16) and $FF; lvValue.BArray[6] := (iVal shr 8) and $FF; lvValue.BArray[7] := iVal and $FF; AStream.WriteBuffer(lvValue, 9); end; end else // <0 begin if iVal <= Low(Integer) then // -2147483648 // 64 bit begin lvValue.ValueType := $D3; lvValue.BArray[0] := (iVal shr 56) and $FF; lvValue.BArray[1] := (iVal shr 48) and $FF; lvValue.BArray[2] := (iVal shr 40) and $FF; lvValue.BArray[3] := (iVal shr 32) and $FF; lvValue.BArray[4] := (iVal shr 24) and $FF; lvValue.BArray[5] := (iVal shr 16) and $FF; lvValue.BArray[6] := (iVal shr 8) and $FF; lvValue.BArray[7] := iVal and $FF; AStream.WriteBuffer(lvValue, 9); end else if iVal <= Low(Smallint) then // -32768 // 32 bit begin lvValue.ValueType := $D2; lvValue.BArray[0] := (iVal shr 24) and $FF; lvValue.BArray[1] := (iVal shr 16) and $FF; lvValue.BArray[2] := (iVal shr 8) and $FF; lvValue.BArray[3] := iVal and $FF; AStream.WriteBuffer(lvValue, 5); end else if iVal <= -128 then begin lvValue.ValueType := $D1; lvValue.BArray[0] := (iVal shr 8); lvValue.BArray[1] := (iVal and $FF); AStream.WriteBuffer(lvValue, 3); end else if iVal < -32 then begin lvValue.ValueType := $D0; lvValue.I8Val := iVal; AStream.WriteBuffer(lvValue, 2); end else begin lvValue.I8Val := iVal; AStream.Write(lvValue.I8Val, 1); end; end; // End <0 end; procedure WriteFloat(pvVal: Double; AStream: TStream); var lvValue: TMsgPackValue; begin lvValue.I64Val := swap(pvVal); lvValue.ValueType := $CB; AStream.WriteBuffer(lvValue, 9); end; procedure WriteSingle(pvVal: Single; AStream: TStream); var lvValue: TMsgPackValue; begin lvValue.I32Val := swap(pvVal); lvValue.ValueType := $CA; AStream.WriteBuffer(lvValue, 5); end; procedure WriteNull(pvStream: TStream); var lvByte: Byte; begin lvByte := $C0; pvStream.Write(lvByte, 1); end; procedure WriteBoolean(pvValue: Boolean; pvStream: TStream); var lvByte: Byte; begin if pvValue then lvByte := $C3 else lvByte := $C2; pvStream.Write(lvByte, 1); end; /// <summary> /// copy from qmsgpack /// </summary> procedure writeArray(obj: TMsgPack; pvStream: TStream); var c, I: Integer; lvValue: TMsgPackValue; lvNode: TMsgPack; begin c := obj.Count; if c <= 15 then begin lvValue.ValueType := $90 + c; pvStream.WriteBuffer(lvValue.ValueType, 1); end else if c <= 65535 then begin lvValue.ValueType := $DC; lvValue.BArray[0] := (c shr 8) and $FF; lvValue.BArray[1] := c and $FF; pvStream.WriteBuffer(lvValue, 3); end else begin lvValue.ValueType := $DD; lvValue.BArray[0] := (c shr 24) and $FF; lvValue.BArray[1] := (c shr 16) and $FF; lvValue.BArray[2] := (c shr 8) and $FF; lvValue.BArray[3] := c and $FF; pvStream.WriteBuffer(lvValue, 5); end; for I := 0 to c - 1 do begin lvNode := TMsgPack(obj.FChildren[I]); lvNode.InnerEncodeToStream(pvStream); end; end; procedure writeMap(obj: TMsgPack; pvStream: TStream); var c, I: Integer; lvValue: TMsgPackValue; lvNode: TMsgPack; begin c := obj.Count; if c <= 15 then begin lvValue.ValueType := $80 + c; pvStream.WriteBuffer(lvValue.ValueType, 1); end else if c <= 65535 then begin lvValue.ValueType := $DE; lvValue.BArray[0] := (c shr 8) and $FF; lvValue.BArray[1] := c and $FF; pvStream.WriteBuffer(lvValue, 3); end else begin lvValue.ValueType := $DF; lvValue.BArray[0] := (c shr 24) and $FF; lvValue.BArray[1] := (c shr 16) and $FF; lvValue.BArray[2] := (c shr 8) and $FF; lvValue.BArray[3] := c and $FF; pvStream.WriteBuffer(lvValue, 5); end; for I := 0 to c - 1 do begin lvNode := TMsgPack(obj.FChildren[I]); writeString(lvNode.FName, pvStream); lvNode.InnerEncodeToStream(pvStream); end; end; function EncodeDateTime(pvVal: TDateTime): string; var AValue: TDateTime; begin AValue := pvVal; if AValue - Trunc(AValue) = 0 then // Date result := FormatDateTime('yyyy-MM-dd', AValue) else begin if Trunc(AValue) = 0 then result := FormatDateTime('hh:nn:ss.zzz', AValue) else result := FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', AValue); end; end; constructor TMsgPack.Create; begin inherited Create; {$IFDEF UNICODE} FChildren := TObjectList<TMsgPack>.Create(True); {$ELSE} FChildren := TObjectList.Create(True); {$ENDIF} end; procedure TMsgPack.FromBytes(pvBytes: TBytes); var ms: TBytesStream; begin ms := TBytesStream.Create(pvBytes); try FromStream(ms); finally ms.Free; end; end; procedure TMsgPack.DecodeFromFile(pvFileName: string); var lvFileStream: TFileStream; begin if FileExists(pvFileName) then begin lvFileStream := TFileStream.Create(pvFileName, fmOpenRead); try FromStream(lvFileStream); finally lvFileStream.Free; end; end; end; procedure TMsgPack.FromRaw(const raw: RawByteString); var rs: TRawByteStringStream; begin rs := TRawByteStringStream.Create(raw); try self.FromStream(rs); finally rs.Free; end; end; procedure TMsgPack.FromStream(pvStream: TStream); begin pvStream.Position := 0; InnerParseFromStream(pvStream); end; function TMsgPack.DeleteObject(pvPath: String): Boolean; var lvParent, lvObj: TMsgPack; j: Integer; begin lvObj := InnerFindPathObject(pvPath, lvParent, j); result := lvObj <> nil; if result then begin lvParent.FChildren.Delete(j); end; end; destructor TMsgPack.Destroy; begin FChildren.Clear; FChildren.Free; FChildren := nil; inherited Destroy; end; function TMsgPack.Add(pvNameKey, pvValue: string): TMsgPack; begin result := InnerAdd(mptMap); result.setName(pvNameKey); result.AsString := pvValue; end; function TMsgPack.Add(pvNameKey: string; pvValue: Int64): TMsgPack; begin result := InnerAdd(mptMap); result.setName(pvNameKey); result.AsInteger := pvValue; end; function TMsgPack.Add: TMsgPack; begin result := InnerAdd(mptMap); end; function TMsgPack.AddArrayChild: TMsgPack; begin if FDataType <> mptArray then begin Clear; FDataType := mptArray; end; result := InnerAdd; end; function TMsgPack.Add(pvNameKey: string; pvValue: TBytes): TMsgPack; begin result := InnerAdd(mptMap); result.setName(pvNameKey); result.FDataType := mptBinary; result.FValue := pvValue; end; function TMsgPack.Add(pvNameKey: String): TMsgPack; begin result := InnerAdd(mptMap); result.setName(pvNameKey); end; procedure TMsgPack.CheckObjectDataType(ANewType: TMsgPackType); begin if (FDataType <> ANewType) then begin FDataType := ANewType; end; end; procedure TMsgPack.Clear; begin FChildren.Clear; FDataType := mptNull; SetLength(FValue, 0); end; function TMsgPack.ToBytes: TBytes; var ms: TBytesStream; begin ms := TBytesStream.Create; try ToStream(ms); Result := ms.Bytes; finally ms.Free; end; end; procedure TMsgPack.ToFile(pvFileName: string); var lvFileStream: TFileStream; begin if FileExists(pvFileName) then lvFileStream := TFileStream.Create(pvFileName, fmOpenWrite) else lvFileStream := TFileStream.Create(pvFileName, fmCreate); try lvFileStream.size := 0; ToStream(lvFileStream); finally lvFileStream.Free; end; end; function TMsgPack.ToRaw: RawByteString; var rs: TRawByteStringStream; begin rs := TRawByteStringStream.Create; try self.ToStream(rs); result := rs.DataString; finally rs.Free; end; end; procedure TMsgPack.ToStream(pvStream: TStream); begin InnerEncodeToStream(pvStream); end; function TMsgPack.FindObj(pvName: string): TMsgPack; var I: Integer; begin I := IndexOfCaseSensitive(pvName); if I <> -1 then begin result := TMsgPack(FChildren[I]); end else begin result := nil; end; end; function TMsgPack.path(pvPath: string): TMsgPack; var lvName: string; S: string; sPtr: PChar; lvTempObj, lvParent: TMsgPack; j: Integer; begin result := nil; S := pvPath; lvParent := self; sPtr := PChar(S); while sPtr^ <> #0 do begin lvName := getFirst(sPtr, ['.', '/', '\']); if lvName = '' then begin break; end else begin if sPtr^ = #0 then begin // end j := lvParent.IndexOf(lvName); if j <> -1 then begin result := TMsgPack(lvParent.FChildren[j]); end else begin result := lvParent.Add(lvName); end; end else begin // find childrean lvTempObj := lvParent.FindObj(lvName); if lvTempObj = nil then begin lvParent := lvParent.Add(lvName); end else begin lvParent := lvTempObj; end; end; end; if sPtr^ = #0 then break; Inc(sPtr); end; end; function TMsgPack.GetAsBoolean: Boolean; begin if FDataType = mptBoolean then result := PBoolean(FValue)^ else if FDataType = mptString then result := StrToBoolDef(AsString, False) else if FDataType = mptInteger then result := (AsInteger <> 0) else if FDataType in [mptNull, mptUnknown] then result := False else result := False; end; function TMsgPack.GetAsBytes: TBytes; begin result := FValue; end; function TMsgPack.GetAsDateTime: TDateTime; begin if FDataType in [mptDateTime, mptFloat] then result := PDouble(FValue)^ else if FDataType = mptSingle then result := PSingle(FValue)^ else if FDataType = mptString then begin result := StrToDateTimeDef(getAsString, 0); end else if FDataType in [mptInteger] then result := AsInteger else result := 0; end; function TMsgPack.GetAsFloat: Double; begin if FDataType in [mptFloat, mptDateTime] then result := PDouble(FValue)^ else if FDataType = mptSingle then result := PSingle(FValue)^ else if FDataType = mptBoolean then result := Integer(AsBoolean) else if FDataType = mptString then result := StrToFloatDef(AsString, 0) else if FDataType = mptInteger then result := AsInteger else result := 0; end; function TMsgPack.getAsInteger: Int64; begin case FDataType of mptInteger: result := PInt64(FValue)^; else result := 0; end; end; function TMsgPack.GetAsSingle: Single; begin if FDataType in [mptFloat, mptDateTime] then result := PDouble(FValue)^ else if FDataType = mptSingle then result := PSingle(FValue)^ else if FDataType = mptBoolean then result := Integer(AsBoolean) else if FDataType = mptString then result := StrToFloatDef(AsString, 0) else if FDataType = mptInteger then result := AsInteger else result := 0; end; function TMsgPack.getAsString: String; var l: Cardinal; begin result := ''; if FDataType = mptString then begin l := Length(FValue); if l = 0 then begin result := ''; end else if SizeOf(Char) = 2 then begin SetLength(result, l shr 1); Move(FValue[0], PChar(result)^, l); end else begin SetLength(result, l); Move(FValue[0], PChar(result)^, l); end; end else begin case FDataType of mptUnknown, mptNull: result := ''; mptInteger: result := IntToStr(AsInteger); mptBoolean: result := BoolToStr(AsBoolean, True); mptFloat: result := FloatToStrF(AsFloat, ffGeneral, 15, 0); mptSingle: result := FloatToStrF(AsSingle, ffGeneral, 7, 0); mptBinary: result := BinToHex(@FValue[0], Length(FValue), False); mptDateTime: result := EncodeDateTime(AsDateTime); // mptArray: // Result := EncodeArray; // mptMap: // Result := EncodeMap; // mptExtended: // Result := EncodeExtended; else result := ''; end; end; // showMessage(Result); end; /// <summary> /// copy from qdac3 /// </summary> function TMsgPack.GetAsVariant: Variant; var I: Integer; procedure BytesAsVariant; var l: Integer; p: PByte; begin l := Length(FValue); result := VarArrayCreate([0, l - 1], varByte); p := VarArrayLock(result); Move(FValue[0], p^, l); VarArrayUnlock(result); end; begin case FDataType of mptNull: result := null; mptString: result := AsString; mptInteger: result := AsInteger; mptFloat: result := AsFloat; mptSingle: result := AsSingle; mptDateTime: result := AsDateTime; mptBoolean: result := AsBoolean; mptArray, mptMap: begin result := VarArrayCreate([0, Count - 1], varVariant); for I := 0 to Count - 1 do result[I] := TMsgPack(FChildren[I]).AsVariant; end; mptBinary: BytesAsVariant; else raise Exception.Create(SVariantConvertNotSupport); end; end; function TMsgPack.GetB(pvPath: String): Boolean; var lvObj: TMsgPack; begin lvObj := GetO(pvPath); if lvObj = nil then begin result := False; end else begin result := lvObj.AsBoolean; end; end; function TMsgPack.GetCount: Integer; begin result := FChildren.Count; end; function TMsgPack.GetD(pvPath: String): Double; var lvObj: TMsgPack; begin lvObj := GetO(pvPath); if lvObj = nil then begin result := 0; end else begin result := lvObj.AsFloat; end; end; function TMsgPack.GetI(pvPath: String): Int64; var lvObj: TMsgPack; begin lvObj := GetO(pvPath); if lvObj = nil then begin result := 0; end else begin result := lvObj.AsInteger; end; end; function TMsgPack.GetItems(AIndex: Integer): TMsgPack; begin result := TMsgPack(FChildren[AIndex]); end; function TMsgPack.GetO(pvPath: String): TMsgPack; var lvParent: TMsgPack; j: Integer; begin result := InnerFindPathObject(pvPath, lvParent, j); end; function TMsgPack.GetS(pvPath: String): string; var lvObj: TMsgPack; begin lvObj := GetO(pvPath); if lvObj = nil then begin result := ''; end else begin result := lvObj.AsString; end; end; function TMsgPack.GetV(pvPath: String): Variant; var lvObj: TMsgPack; begin lvObj := GetO(pvPath); if lvObj = nil then begin result := null; end else begin result := lvObj.AsVariant; end; end; function TMsgPack.IndexOf(pvName: string): Integer; begin result := IndexOfIgnoreSensitive(LowerCase(pvName)); end; function TMsgPack.IndexOfCaseSensitive(pvName: string): Integer; var I, l: Integer; lvObj: TMsgPack; begin result := -1; l := Length(pvName); if l = 0 then exit; for I := 0 to FChildren.Count - 1 do begin lvObj := TMsgPack(FChildren[I]); if Length(lvObj.FName) = l then begin if lvObj.FName = pvName then begin result := I; break; end; end; end; end; function TMsgPack.IndexOfIgnoreSensitive(pvLowerCaseName: string): Integer; var I, l: Integer; lvObj: TMsgPack; begin result := -1; l := Length(pvLowerCaseName); if l = 0 then exit; for I := 0 to FChildren.Count - 1 do begin lvObj := TMsgPack(FChildren[I]); if Length(lvObj.FLowerName) = l then begin if lvObj.FLowerName = pvLowerCaseName then begin result := I; break; end; end; end; end; function TMsgPack.InnerAdd(pvDataType: TMsgPackType): TMsgPack; begin result := TMsgPack.Create; result.FDataType := mptUnknown; InnerAddToChildren(pvDataType, result); end; function TMsgPack.InnerAdd: TMsgPack; begin if self.FDataType in [mptMap, mptArray] then begin result := TMsgPack.Create; result.FDataType := mptUnknown; result.FParent := self; FChildren.Add(result); end else begin raise Exception.Create(SCannotAddChild); end; end; procedure TMsgPack.InnerAddToChildren(pvDataType: TMsgPackType; obj: TMsgPack); begin CheckObjectDataType(pvDataType); obj.FParent := self; FChildren.Add(obj); end; procedure TMsgPack.InnerEncodeToStream(pvStream: TStream); begin case FDataType of mptUnknown, mptNull: WriteNull(pvStream); mptMap: writeMap(self, pvStream); mptArray: writeArray(self, pvStream); mptString: writeString(self.getAsString, pvStream); mptInteger: WriteInt(self.getAsInteger, pvStream); mptBoolean: WriteBoolean(self.GetAsBoolean, pvStream); mptDateTime, mptFloat: WriteFloat(GetAsFloat, pvStream); mptSingle: WriteSingle(GetAsSingle, pvStream); mptBinary: WriteBinary(PByte(@FValue[0]), Length(FValue), pvStream); end; end; function TMsgPack.InnerFindPathObject(pvPath: string; var vParent: TMsgPack; var vIndex: Integer): TMsgPack; var lvName: string; S: string; sPtr: PChar; lvTempObj, lvParent: TMsgPack; j: Integer; begin S := pvPath; result := nil; lvParent := self; sPtr := PChar(S); while sPtr^ <> #0 do begin lvName := getFirst(sPtr, ['.', '/', '\']); if lvName = '' then begin break; end else begin if sPtr^ = #0 then begin // end j := lvParent.IndexOf(lvName); if j <> -1 then begin result := TMsgPack(lvParent.FChildren[j]); vIndex := j; vParent := lvParent; end else begin break; end; end else begin // find childrean lvTempObj := lvParent.FindObj(lvName); if lvTempObj = nil then begin break; end else begin lvParent := lvTempObj; end; end; end; if sPtr^ = #0 then break; Inc(sPtr); end; end; procedure TMsgPack.InnerParseFromStream(pvStream: TStream); var lvByte: Byte; lvBData: array [0 .. 15] of Byte; lvSwapData: array [0 .. 7] of Byte; lvAnsiStr: {$IFDEF UNICODE}TBytes{$ELSE}AnsiString{$ENDIF}; l, I: Cardinal; i64: Int64; lvObj: TMsgPack; begin pvStream.Read(lvByte, 1); if lvByte in [$00 .. $7F] then // positive fixint 0xxxxxxx 0x00 - 0x7f begin // +--------+ // |0XXXXXXX| // +--------+ setAsInteger(lvByte); end else if lvByte in [$80 .. $8F] then // fixmap 1000xxxx 0x80 - 0x8f begin FDataType := mptMap; SetLength(FValue, 0); FChildren.Clear; l := lvByte - $80; if l > 0 then // check is empty ele begin for I := 0 to l - 1 do begin lvObj := InnerAdd(mptMap); // map key lvObj.InnerParseFromStream(pvStream); lvObj.setName(lvObj.getAsString); // value lvObj.InnerParseFromStream(pvStream); end; end; end else if lvByte in [$90 .. $9F] then // fixarray 1001xxxx 0x90 - 0x9f begin FDataType := mptArray; SetLength(FValue, 0); FChildren.Clear; l := lvByte - $90; if l > 0 then // check is empty ele begin for I := 0 to l - 1 do begin lvObj := InnerAdd(mptArray); // value lvObj.InnerParseFromStream(pvStream); end; end; end else if lvByte in [$A0 .. $BF] then // fixstr 101xxxxx 0xa0 - 0xbf begin l := lvByte - $A0; // str len if l > 0 then begin SetLength(lvAnsiStr, l); pvStream.Read(PByte(lvAnsiStr)^, l); setAsString(Utf8DecodeEx(lvAnsiStr, l)); // SetLength(lvBytes, l + 1); // lvBytes[l] := 0; // pvStream.Read(lvBytes[0], l); // setAsString(UTF8Decode(PAnsiChar(@lvBytes[0]))); end else begin setAsString(''); end; end else if lvByte in [$E0 .. $FF] then begin // negative fixnum stores 5-bit negative integer // +--------+ // |111YYYYY| // +--------+ setAsInteger(Shortint(lvByte)); end else begin case lvByte of $C0: // null begin FDataType := mptNull; SetLength(FValue, 0); end; $C1: // (never used) raise Exception.Create('(never used) type $c1'); $C2: // False begin SetAsBoolean(False); end; $C3: // True begin SetAsBoolean(True); end; $C4: // 短二进制,最长255字节 begin FDataType := mptBinary; l := 0; // fill zero pvStream.Read(l, 1); SetLength(FValue, l); pvStream.Read(FValue[0], l); end; $C5: // 二进制,16位,最长65535B begin FDataType := mptBinary; l := 0; // fill zero pvStream.Read(l, 2); l := swap16(l); SetLength(FValue, l); pvStream.Read(FValue[0], l); end; $C6: // 二进制,32位,最长2^32-1 begin FDataType := mptBinary; l := 0; // fill zero pvStream.Read(l, 4); l := swap32(l); SetLength(FValue, l); pvStream.Read(FValue[0], l); end; $C7, $C8, $C9: // ext 8 11000111 0xc7, ext 16 11001000 0xc8, ext 32 11001001 0xc9 begin raise Exception.Create('(ext8,ext16,ex32) type $c7,$c8,$c9'); end; $CA: // float 32 begin pvStream.Read(lvBData[0], 4); swap32Ex(lvBData[0], lvSwapData[0]); AsSingle := PSingle(@lvSwapData[0])^; end; $CB: // Float 64 begin pvStream.Read(lvBData[0], 8); // swap to int64, and lvBData is not valid double value (for IEEE) i64 := swap64(lvBData[0]); // AsFloat := PDouble(@i64)^; // AsFloat := swap(PDouble(@lvBData[0])^); end; $CC: // UInt8 begin // uint 8 stores a 8-bit unsigned integer // +--------+--------+ // | 0xcc |ZZZZZZZZ| // +--------+--------+ l := 0; pvStream.Read(l, 1); setAsInteger(l); end; $CD: begin // uint 16 stores a 16-bit big-endian unsigned integer // +--------+--------+--------+ // | 0xcd |ZZZZZZZZ|ZZZZZZZZ| // +--------+--------+--------+ l := 0; pvStream.Read(l, 2); l := swap16(l); setAsInteger(Word(l)); end; $CE: begin // uint 32 stores a 32-bit big-endian unsigned integer // +--------+--------+--------+--------+--------+ // | 0xce |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ // +--------+--------+--------+--------+--------+ l := 0; pvStream.Read(l, 4); l := swap32(l); setAsInteger(Cardinal(l)); end; $CF: begin // uint 64 stores a 64-bit big-endian unsigned integer // +--------+--------+--------+--------+--------+--------+--------+--------+--------+ // | 0xcf |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| // +--------+--------+--------+--------+--------+--------+--------+--------+--------+ i64 := 0; pvStream.Read(i64, 8); i64 := swap64(i64); setAsInteger(i64); end; $DC: // array 16 begin // +--------+--------+--------+~~~~~~~~~~~~~~~~~+ // | 0xdc |YYYYYYYY|YYYYYYYY| N objects | // +--------+--------+--------+~~~~~~~~~~~~~~~~~+ FDataType := mptArray; SetLength(FValue, 0); FChildren.Clear; l := 0; // fill zero pvStream.Read(l, 2); l := swap16(l); if l > 0 then // check is empty ele begin for I := 0 to l - 1 do begin lvObj := InnerAdd(mptArray); // value lvObj.InnerParseFromStream(pvStream); end; end; end; $DD: // Array 32 begin // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+ // | 0xdd |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| N objects | // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+ FDataType := mptArray; SetLength(FValue, 0); FChildren.Clear; l := 0; // fill zero pvStream.Read(l, 4); l := swap32(l); if l > 0 then // check is empty ele begin for I := 0 to l - 1 do begin lvObj := InnerAdd(mptArray); // value lvObj.InnerParseFromStream(pvStream); end; end; end; $D9: // str 8 , 255 begin // str 8 stores a byte array whose length is upto (2^8)-1 bytes: // +--------+--------+========+ // | 0xd9 |YYYYYYYY| data | // +--------+--------+========+ l := 0; pvStream.Read(l, 1); if l > 0 then // check is empty ele begin SetLength(lvAnsiStr, l); pvStream.Read(PByte(lvAnsiStr)^, l); setAsString(Utf8DecodeEx(lvAnsiStr, l)); end else begin setAsString(''); end; // SetLength(lvBytes, l + 1); // lvBytes[l] := 0; // pvStream.Read(lvBytes[0], l); // setAsString(UTF8Decode(PAnsiChar(@lvBytes[0]))); end; $DE: // Object map 16 begin // +--------+--------+--------+~~~~~~~~~~~~~~~~~+ // | 0xde |YYYYYYYY|YYYYYYYY| N*2 objects | // +--------+--------+--------+~~~~~~~~~~~~~~~~~+ FDataType := mptMap; SetLength(FValue, 0); FChildren.Clear; l := 0; // fill zero pvStream.Read(l, 2); l := swap16(l); if l > 0 then // check is empty ele begin for I := 0 to l - 1 do begin lvObj := InnerAdd(mptMap); // map key lvObj.InnerParseFromStream(pvStream); lvObj.setName(lvObj.getAsString); // value lvObj.InnerParseFromStream(pvStream); end; end; end; $DF: // Object map 32 begin // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+ // | 0xdf |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| N*2 objects | // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+ FDataType := mptMap; SetLength(FValue, 0); FChildren.Clear; l := 0; // fill zero pvStream.Read(l, 4); l := swap32(l); if l > 0 then // check is empty ele begin for I := 0 to l - 1 do begin lvObj := InnerAdd(mptMap); // map key lvObj.InnerParseFromStream(pvStream); lvObj.setName(lvObj.getAsString); // value lvObj.InnerParseFromStream(pvStream); end; end; end; $DA: // str 16 begin // str 16 stores a byte array whose length is upto (2^16)-1 bytes: // +--------+--------+--------+========+ // | 0xda |ZZZZZZZZ|ZZZZZZZZ| data | // +--------+--------+--------+========+ l := 0; // fill zero pvStream.Read(l, 2); l := swap16(l); if l > 0 then // check is empty ele begin SetLength(lvAnsiStr, l); pvStream.Read(PByte(lvAnsiStr)^, l); setAsString(Utf8DecodeEx(lvAnsiStr, l)); end else begin setAsString(''); end; // SetLength(lvBytes, l + 1); // lvBytes[l] := 0; // pvStream.Read(lvBytes[0], l); // setAsString(UTF8Decode(PAnsiChar(@lvBytes[0]))); end; $DB: // str 16 begin // str 32 stores a byte array whose length is upto (2^32)-1 bytes: // +--------+--------+--------+--------+--------+========+ // | 0xdb |AAAAAAAA|AAAAAAAA|AAAAAAAA|AAAAAAAA| data | // +--------+--------+--------+--------+--------+========+ l := 0; // fill zero pvStream.Read(l, 4); l := swap32(l); if l > 0 then // check is empty ele begin SetLength(lvAnsiStr, l); pvStream.Read(PByte(lvAnsiStr)^, l); setAsString(Utf8DecodeEx(lvAnsiStr, l)); end else begin setAsString(''); end; // SetLength(lvBytes, l + 1); // lvBytes[l] := 0; // pvStream.Read(lvBytes[0], l); // setAsString(UTF8Decode(PAnsiChar(@lvBytes[0]))); end; $D0: // int 8 begin // int 8 stores a 8-bit signed integer // +--------+--------+ // | 0xd0 |ZZZZZZZZ| // +--------+--------+ l := 0; pvStream.Read(l, 1); setAsInteger(Shortint(l)); end; $D1: begin // int 16 stores a 16-bit big-endian signed integer // +--------+--------+--------+ // | 0xd1 |ZZZZZZZZ|ZZZZZZZZ| // +--------+--------+--------+ l := 0; pvStream.Read(l, 2); l := swap16(l); setAsInteger(Smallint(l)); end; $D2: begin // int 32 stores a 32-bit big-endian signed integer // +--------+--------+--------+--------+--------+ // | 0xd2 |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| // +--------+--------+--------+--------+--------+ l := 0; pvStream.Read(l, 4); l := swap32(l); setAsInteger(Integer(l)); end; $D3: begin // int 64 stores a 64-bit big-endian signed integer // +--------+--------+--------+--------+--------+--------+--------+--------+--------+ // | 0xd3 |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| // +--------+--------+--------+--------+--------+--------+--------+--------+--------+ i64 := 0; pvStream.Read(i64, 8); i64 := swap64(i64); setAsInteger(Int64(i64)); end; end; end; end; procedure TMsgPack.LoadBinaryFromFile(pvFileName: String); var lvFileStream: TFileStream; begin if FileExists(pvFileName) then begin lvFileStream := TFileStream.Create(pvFileName, fmOpenRead); try LoadStream(lvFileStream); finally lvFileStream.Free; end; end; end; procedure TMsgPack.LoadStream(pvStream: TStream; pvLen: Cardinal = 0); begin FDataType := mptBinary; if pvLen = 0 then begin pvStream.Position := 0; SetLength(FValue, pvStream.size); pvStream.Read(FValue[0], pvStream.size); end else begin SetLength(FValue, pvLen); pvStream.ReadBuffer(FValue[0], pvLen); end; end; procedure TMsgPack.SaveBinaryToFile(pvFileName: String); var lvFileStream: TFileStream; begin if FileExists(pvFileName) then begin if not DeleteFile(PChar(pvFileName)) then RaiseLastOSError; end; lvFileStream := TFileStream.Create(pvFileName, fmCreate); try lvFileStream.WriteBuffer(FValue[0], Length(FValue)); finally lvFileStream.Free; end; end; procedure TMsgPack.SaveStream(pvStream: TStream); begin pvStream.WriteBuffer(FValue[0], Length(FValue)); pvStream.Position := 0; end; procedure TMsgPack.SetAsBoolean(const Value: Boolean); begin FDataType := mptBoolean; SetLength(FValue, 1); PBoolean(@FValue[0])^ := Value; end; procedure TMsgPack.SetAsBytes(const Value: TBytes); begin FDataType := mptBinary; FValue := Value; end; procedure TMsgPack.SetAsDateTime(const Value: TDateTime); begin FDataType := mptDateTime; SetLength(FValue, SizeOf(TDateTime)); PDouble(@FValue[0])^ := Value; end; procedure TMsgPack.SetAsFloat(const Value: Double); begin FDataType := mptFloat; SetLength(FValue, SizeOf(Double)); PDouble(@FValue[0])^ := Value; end; procedure TMsgPack.setAsInteger(pvValue: Int64); begin FDataType := mptInteger; SetLength(FValue, SizeOf(Int64)); PInt64(@FValue[0])^ := pvValue; end; procedure TMsgPack.SetAsSingle(const Value: Single); begin FDataType := mptSingle; SetLength(FValue, SizeOf(Single)); PSingle(FValue)^ := Value; end; procedure TMsgPack.setAsString(pvValue: string); begin FDataType := mptString; if SizeOf(Char) = 2 then begin SetLength(FValue, Length(pvValue) shl 1); Move(PChar(pvValue)^, FValue[0], Length(FValue)); end else begin SetLength(FValue, Length(pvValue)); Move(PChar(pvValue)^, FValue[0], Length(FValue)); end; end; /// <summary> /// copy from qdac3 /// </summary> procedure TMsgPack.SetAsVariant(const Value: Variant); var I: Integer; AType: TVarType; procedure VarAsBytes; var l: Integer; p: PByte; begin FDataType := mptBinary; l := VarArrayHighBound(Value, 1) + 1; SetLength(FValue, l); p := VarArrayLock(Value); Move(p^, FValue[0], l); VarArrayUnlock(Value); end; begin if VarIsArray(Value) then begin AType := VarType(Value); if (AType and varTypeMask) = varByte then VarAsBytes else begin CheckObjectDataType(mptArray); FChildren.Clear; for I := VarArrayLowBound(Value, VarArrayDimCount(Value)) to VarArrayHighBound(Value, VarArrayDimCount(Value)) do Add.AsVariant := Value[I]; end; end else begin case VarType(Value) of varSmallInt, varInteger, varByte, varShortInt, varWord, varLongWord, varInt64: AsInteger := Value; varSingle, varDouble, varCurrency: AsFloat := Value; varDate: AsDateTime := Value; varOleStr, varString{$IFDEF UNICODE}, varUString{$ENDIF}: AsString := Value; varBoolean: AsBoolean := Value; varNull, varEmpty, varUnknown: begin FDataType := mptNull; SetLength(FValue, 0); end; {$IF RtlVersion>=26} varUInt64: AsInteger := Value; {$IFEND} else // null ; // raise Exception.Create(SVariantConvertNotSupport); end; end; end; procedure TMsgPack.SetB(pvPath: String; const Value: Boolean); var lvObj: TMsgPack; begin lvObj := path(pvPath); lvObj.AsBoolean := Value; end; procedure TMsgPack.SetD(pvPath: String; const Value: Double); var lvObj: TMsgPack; begin lvObj := path(pvPath); lvObj.AsFloat := Value; end; procedure TMsgPack.SetI(pvPath: String; const Value: Int64); var lvObj: TMsgPack; begin lvObj := path(pvPath); lvObj.AsInteger := Value; end; procedure TMsgPack.setName(pvName: string); begin FName := pvName; FLowerName := LowerCase(FName); end; procedure TMsgPack.SetO(pvPath: String; const Value: TMsgPack); var lvName: String; S: String; sPtr: PChar; lvTempObj, lvParent: TMsgPack; j: Integer; begin S := pvPath; lvParent := self; sPtr := PChar(S); while sPtr^ <> #0 do begin lvName := getFirst(sPtr, ['.', '/', '\']); if lvName = '' then begin break; end else begin if sPtr^ = #0 then begin // end j := lvParent.IndexOf(lvName); if j <> -1 then begin lvTempObj := TMsgPack(lvParent.FChildren[j]); lvParent.FChildren[j] := Value; lvTempObj.Free; // free old end else begin Value.setName(lvName); lvParent.InnerAddToChildren(mptMap, Value); end; end else begin // find childrean lvTempObj := lvParent.FindObj(lvName); if lvTempObj = nil then begin lvParent := lvParent.Add(lvName); end else begin lvParent := lvTempObj; end; end; end; if sPtr^ = #0 then break; Inc(sPtr); end; end; procedure TMsgPack.SetS(pvPath: String; const Value: string); var lvObj: TMsgPack; begin lvObj := path(pvPath); lvObj.AsString := Value; end; procedure TMsgPack.SetV(pvPath: String; const Value: Variant); var lvObj: TMsgPack; begin lvObj := path(pvPath); lvObj.AsVariant := Value; end; { TRawByteStringStream } constructor TRawByteStringStream.Create(const aString: RawByteString); begin fDataString := aString; end; function TRawByteStringStream.Read(var Buffer; Count: Integer): Longint; begin if Count <= 0 then result := 0 else begin result := Length(fDataString) - fPosition; if result > Count then result := Count; Move(PByteArray(fDataString)[fPosition], Buffer, result); Inc(fPosition, result); end; end; function TRawByteStringStream.Seek(Offset: Integer; Origin: Word): Longint; begin case Origin of soFromBeginning: fPosition := Offset; soFromCurrent: fPosition := fPosition + Offset; soFromEnd: fPosition := Length(fDataString) - Offset; end; if fPosition > Length(fDataString) then fPosition := Length(fDataString) else if fPosition < 0 then fPosition := 0; result := fPosition; end; procedure TRawByteStringStream.SetSize(NewSize: Integer); begin SetLength(fDataString, NewSize); if fPosition > NewSize then fPosition := NewSize; end; function TRawByteStringStream.Write(const Buffer; Count: Integer): Longint; begin if Count <= 0 then result := 0 else begin result := Count; SetLength(fDataString, fPosition + result); Move(Buffer, PByteArray(fDataString)[fPosition], result); Inc(fPosition, result); end; end; end.
标签:function,begin,msgpack,end,pvStream,db,pas,result,TMsgPack From: https://www.cnblogs.com/hnxxcxg/p/18616886