键值对序列

发布时间 2023-06-15 16:48:08作者: delphi中间件

键值对序列

支持delphi和lazarus。

/// <author>cxg 2022-4-25</author>
/// 支持 delphi and fpc.
/// 支持 linux and windows.
/// 键-值 数据序列: keyLen(integer)+keyName(rawbytestring)+valueLen(integer)+value
/// 测试: d7~d11,lazarus
unit keyValue.serialize;

interface

uses
  FmtBcd, DateUtils, Variants, Classes, SysUtils;

const
  pieceSize = 65536;

type
{$IFNDEF fpc}
{$IF RTLVersion<25}
  IntPtr = integer;
{$IFEND IntPtr}
{$IF CompilerVersion < 18}// before delphi 2007

  TBytes = array of byte;
{$IFEND}
{$IFNDEF UNICODE}

  RawByteString = AnsiString;

  PRawByteString = ^RawByteString;
{$ENDIF}
{$ENDIF}

  TSerialize = class
  private
    fValue: TBytes;
    fKey: rawbytestring;
    fList: tlist;
  private
    procedure setInt(const keyName: rawbytestring; const Value: integer);
    procedure setStr(const keyName: rawbytestring; const Value: rawbytestring);
    procedure setVariant(const keyName: rawbytestring; const Value: variant);
    procedure setBytes(const keyName: rawbytestring; const Value: TBytes);
    procedure setDateTime(const keyName: rawbytestring; const Value: TDateTime);
    procedure setBool(const keyName: rawbytestring; const Value: boolean);
    procedure setSingle(const keyName: rawbytestring; const Value: single);
    procedure setDouble(const keyName: rawbytestring; const Value: double);
    procedure setByte(const keyName: rawbytestring; const Value: byte);
    procedure setInt64(const keyName: rawbytestring; const Value: int64);
    procedure setCurrency(const keyName: rawbytestring; const Value: Currency);
    procedure setStream(const keyName: rawbytestring; const Value: tstream);
    procedure setWord(const keyName: rawbytestring; const Value: word);
    procedure setExtended(const keyName: rawbytestring; const Value: Extended);
    procedure setLongWord(const keyName: rawbytestring; const Value: LongWord);
    procedure setShortint(const keyName: rawbytestring; const Value: Shortint);
    procedure setSmallint(const keyName: rawbytestring; const Value: Smallint);
    procedure setBCD(const keyName: rawbytestring; const Value: tbcd);
  private
    function getInt(const keyName: rawbytestring): integer;
    function getStr(const keyName: rawbytestring): rawbytestring;
    function getVariant(const keyName: rawbytestring): variant;
    function getBytes(const keyName: rawbytestring): TBytes;
    function getDateTime(const keyName: rawbytestring): TDateTime;
    function getBool(const keyName: rawbytestring): boolean;
    function getSingle(const keyName: rawbytestring): single;
    function getDouble(const keyName: rawbytestring): double;
    function getByte(const keyName: rawbytestring): byte;
    function getInt64(const keyName: rawbytestring): int64;
    function getCurrency(const keyName: rawbytestring): Currency;
    function getStream(const keyName: rawbytestring): tstream;
    function getWord(const keyName: rawbytestring): word;
    function getExtended(const keyName: rawbytestring): Extended;
    function getLongWord(const keyName: rawbytestring): LongWord;
    function getShortint(const keyName: rawbytestring): Shortint;
    function getSmallint(const keyName: rawbytestring): Smallint;
    function getBCD(const keyName: rawbytestring): tbcd;
  private
    function getCount: integer;
  public
    constructor Create;
    destructor Destroy; override;
  public
    function key(const keyName: rawbytestring): TSerialize;
    procedure Clear;
    function Delete(const keyName: rawbytestring): boolean;
  public
    procedure marshal(stream: TStream);
    function marshal2: TBytes;
    function marshal3: RawByteString;
    function marshal5: OleVariant;
  public
    procedure unMarshal(stream: TStream); overload;
    procedure unMarshal(bytes: TBytes); overload;
    procedure unMarshal(raw: RawByteString); overload;
    procedure unMarshal(ole: OleVariant); overload;
  public
    property asInt[const keyName: rawbytestring]: integer read getInt write setInt;
    property asStr[const keyName: rawbytestring]: rawbytestring read getStr write setStr;
    property AsVariant[const keyName: rawbytestring]: variant read getVariant write setVariant;
    property asBytes[const keyName: rawbytestring]: TBytes read getBytes write setBytes;
    property AsDateTime[const keyName: rawbytestring]: TDateTime read getDateTime write setDateTime;
    property asBool[const keyName: rawbytestring]: boolean read getBool write setBool;
    property asSingle[const keyName: rawbytestring]: single read getSingle write setSingle;
    property asDouble[const keyName: rawbytestring]: double read getDouble write setDouble;
    property asByte[const keyName: rawbytestring]: byte read getByte write setByte;
    property asInt64[const keyName: rawbytestring]: int64 read getInt64 write setInt64;
    property asCurrency[const keyName: rawbytestring]: Currency read getCurrency write setCurrency;
    property asStream[const keyName: rawbytestring]: tstream read getStream write setStream;
    property asWord[const keyName: rawbytestring]: word read getword write setword;
    property asExtended[const keyName: rawbytestring]: Extended read getExtended write setExtended;
    property asLongWord[const keyName: rawbytestring]: LongWord read getLongWord write setLongWord;
    property asShortint[const keyName: rawbytestring]: Shortint read getShortint write setShortint;
    property asSmallint[const keyName: rawbytestring]: Smallint read getSmallint write setSmallint;
    property asBCD[const keyName: rawbytestring]: tbcd read getBCD write setBCD;
    property asFloat[const keyName: rawbytestring]: double read getDouble write setDouble;
    property asCardinal[const keyName: rawbytestring]: LongWord read getLongWord write setLongWord;
  public
    property Count: integer read getCount;
  end;

implementation

function TSerialize.key(const keyName: rawbytestring): TSerialize;
var
  i: integer;
  found: boolean;
begin
  Result := nil;
  found := False;
  for i := 0 to fList.Count - 1 do
  begin
    if keyName = TSerialize(fList[i]).fKey then
    begin
      Result := TSerialize(fList[i]);
      exit;
    end;
  end;
  if not found then
  begin
    Result := TSerialize.Create;
    Result.fKey := keyName;
    fList.Add(Result);
  end;
end;

function TSerialize.getInt(const keyName: rawbytestring): integer;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := PInteger(s.fValue)^;
end;

function TSerialize.getStr(const keyName: rawbytestring): rawbytestring;
var
  l: integer;
  s: TSerialize;
begin
  s := key(keyName);
  l := Length(s.fValue);
  if l = 0 then
    Result := ''
  else
  begin
    SetLength(Result, l);
    Move(s.FValue[0], PRawByteString(Result)^, l);
  end;
end;

function TSerialize.getStream(const keyName: rawbytestring): tstream;
var
  s: TSerialize;
  l, at, bufLen, remainLen: Integer;
  p: pbyte;
begin
  s := key(keyName);
  l := Length(s.fValue);
  Result := TMemoryStream.Create;
  Result.Size := l;

  at := 0;
  remainLen := l;
  while remainLen > 0 do
  begin
    if remainLen > PieceSize then
      bufLen := PieceSize
    else
      bufLen := remainLen;
    p := TMemoryStream(Result).Memory;
    Move(s.fValue[at], p[at], bufLen);
    Inc(at, bufLen);
    Dec(remainLen, bufLen);
  end;
  Result.Position := 0;
end;

function TSerialize.getVariant(const keyName: rawbytestring): variant;
var
  p: pbyte;
  l: integer;
  s: TSerialize;
begin
  s := key(keyName);
  l := Length(s.fValue);
  Result := VarArrayCreate([0, l - 1], varByte);
  p := VarArrayLock(Result);
  Move(s.fValue[0], p^, l);
  VarArrayUnlock(Result);
end;

function TSerialize.getWord(const keyName: rawbytestring): word;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := pword(s.fValue)^;
end;

function TSerialize.getBytes(const keyName: rawbytestring): TBytes;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := s.fValue;
end;

function TSerialize.getByte(const keyName: rawbytestring): byte;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := pbyte(s.fValue)^;
end;

function TSerialize.getInt64(const keyName: rawbytestring): int64;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := PInt64(s.fValue)^;
end;

function TSerialize.getLongWord(const keyName: rawbytestring): LongWord;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := PLongWord(s.fValue)^;
end;

function TSerialize.getShortint(const keyName: rawbytestring): Shortint;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := PShortint(s.fValue)^;
end;

function TSerialize.getSingle(const keyName: rawbytestring): single;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := PSingle(s.fValue)^;
end;

function TSerialize.getSmallint(const keyName: rawbytestring): Smallint;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := PSmallint(s.fValue)^;
end;

function TSerialize.getDateTime(const keyName: rawbytestring): TDateTime;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := PDateTime(s.fValue)^;
end;

function TSerialize.getBool(const keyName: rawbytestring): boolean;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := PBoolean(s.fValue)^;
end;

function TSerialize.getDouble(const keyName: rawbytestring): double;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := PDouble(s.fValue)^;
end;

function TSerialize.getExtended(const keyName: rawbytestring): Extended;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := PExtended(s.fValue)^;
end;

function TSerialize.getCurrency(const keyName: rawbytestring): Currency;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := PCurrency(s.fValue)^;
end;

procedure TSerialize.setDouble(const keyName: rawbytestring; const Value: double);
var
  s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(double));
  PDouble(s.fValue)^ := Value;
end;

procedure TSerialize.setExtended(const keyName: rawbytestring; const Value: Extended);
var
  s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(Extended));
  PExtended(s.fValue)^ := Value;
end;

procedure TSerialize.setInt(const keyName: rawbytestring; const Value: integer);
var
  s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(integer));
  PInteger(s.fValue)^ := Value;
end;

procedure TSerialize.setShortint(const keyName: rawbytestring; const Value: Shortint);
var
  s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(Shortint));
  PShortint(s.fValue)^ := Value;
end;

procedure TSerialize.setSingle(const keyName: rawbytestring; const Value: single);
var
  s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(single));
  PSingle(s.fValue)^ := Value;
end;

procedure TSerialize.setSmallint(const keyName: rawbytestring; const Value: Smallint);
var
  s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(Smallint));
  PSmallint(s.fValue)^ := Value;
end;

procedure TSerialize.setStr(const keyName: rawbytestring; const Value: rawbytestring);
var
  l: integer;
  s: TSerialize;
begin
  s := key(keyName);
  l := Length(Value);
  SetLength(s.fValue, l);
  if l > 0 then
    Move(PRawByteString(Value)^, s.fValue[0], l);
end;

procedure TSerialize.setStream(const keyName: rawbytestring; const Value: tstream);
var
  s: TSerialize;
  at, bufLen, remainLen: Integer;
  p: pbyte;
begin
  s := key(keyName);
  SetLength(s.fValue, Value.Size);
  Value.Position := 0;

  at := 0;
  remainLen := Value.Size;
  while remainLen > 0 do
  begin
    if remainLen > PieceSize then
      bufLen := PieceSize
    else
      bufLen := remainLen;
    p := TMemoryStream(Value).Memory;
    Move(p[at], s.fValue[at], bufLen); // value
    Inc(at, bufLen);
    Dec(remainLen, bufLen);
  end;
end;

procedure TSerialize.setVariant(const keyName: rawbytestring; const Value: variant);
var
  p: pbyte;
  l: integer;
  s: TSerialize;
begin
  s := key(keyName);
  l := VarArrayHighBound(Value, 1) - VarArrayLowBound(Value, 1) + 1;
  p := VarArrayLock(Value);
  SetLength(s.fValue, l);
  Move(p^, s.fValue[0], l);
  VarArrayUnlock(Value);
end;

procedure TSerialize.setWord(const keyName: rawbytestring; const Value: word);
var
  s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(word));
  PWord(s.fValue)^ := Value;
end;

procedure TSerialize.setBytes(const keyName: rawbytestring; const Value: TBytes);
var
  s: TSerialize;
begin
  s := key(keyName);
  s.fValue := Value;
end;

procedure TSerialize.setCurrency(const keyName: rawbytestring; const Value: Currency);
var
  s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(Currency));
  pCurrency(s.fValue)^ := Value;
end;

procedure TSerialize.setByte(const keyName: rawbytestring; const Value: byte);
var
  s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(byte));
  pbyte(s.fValue)^ := Value;
end;

procedure TSerialize.setInt64(const keyName: rawbytestring; const Value: int64);
var
  s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(int64));
  PInt64(s.fValue)^ := Value;
end;

procedure TSerialize.setLongWord(const keyName: rawbytestring; const Value: LongWord);
var
  s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(LongWord));
  PLongWord(s.fValue)^ := Value;
end;

procedure TSerialize.setDateTime(const keyName: rawbytestring; const Value: TDateTime);
var
  s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(TDateTime));
  PDateTime(s.fValue)^ := Value;
end;

procedure TSerialize.setBool(const keyName: rawbytestring; const Value: boolean);
var
  s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(boolean));
  PBoolean(s.fValue)^ := Value;
end;

procedure TSerialize.setBCD(const keyName: rawbytestring; const Value: tbcd);
var s: TSerialize;
begin
  s := key(keyName);
  SetLength(s.fValue, SizeOf(TBcd));
  PBcd(s.fValue)^ := Value;
end;

procedure TSerialize.unMarshal(stream: TStream);
var
  keyLen, valueLen, remainLen, at, bufLen: integer;
  keyName: rawbytestring;
  Value: TBytes;
  s: TSerialize;
begin
  stream.Position := 0;
  while stream.Position < stream.Size do
  begin
    stream.Read(keyLen, SizeOf(Integer)); // key len
    SetLength(keyName, keyLen);
    stream.Read(PRawByteString(keyName)^, keyLen); // key
    stream.Read(valueLen, SizeOf(Integer)); // value len
    SetLength(Value, valueLen); // value

    remainLen := valueLen;
    at := 0;
    while remainLen > 0 do
    begin
      if remainLen > PieceSize then
        bufLen := PieceSize
      else
        bufLen := remainLen;
      stream.read(Value[at], bufLen); // value
      Inc(at, bufLen);
      Dec(remainLen, bufLen);
    end;

    s := TSerialize.Create;
    s.fValue := Value;
    s.fKey := keyName;
    fList.Add(s);
  end;
end;

procedure TSerialize.unMarshal(ole: OleVariant);
var
  keyLen, valueLen, at, at2, remainLen, BufLen, l: integer;
  keyName: rawbytestring;
  Value: TBytes;
  s: TSerialize;
  p: pbyte;
begin
  at := 0;
  l := VarArrayHighBound(ole, 1) - VarArrayLowBound(ole, 1) + 1;
  p := VarArrayLock(ole);
  while at < l do
  begin
    Move(p[at], keyLen, SizeOf(Integer)); // key len
    inc(at, SizeOf(Integer));
    SetLength(keyName, keyLen);
    Move(p[at], PRawByteString(keyName)^, keyLen); // key
    inc(at, keyLen);
    Move(p[at], valueLen, SizeOf(Integer)); // value len
    inc(at, SizeOf(Integer));
    SetLength(Value, valueLen); // value

    at2 := 0;
    remainLen := valueLen;
    while remainLen > 0 do
    begin
      if remainLen > PieceSize then
        BufLen := PieceSize
      else
        BufLen := remainLen;
      Move(p[at], Value[at2], BufLen); // value
      Inc(at, BufLen);
      Inc(at2, BufLen);
      Dec(remainLen, BufLen);
    end;

    s := TSerialize.Create;
    s.fValue := Value;
    s.fKey := keyName;
    fList.Add(s);
  end;
  VarArrayUnlock(ole);
end;

procedure TSerialize.unMarshal(bytes: TBytes);
var
  keyLen, valueLen, at, at2, remainLen, BufLen: integer;
  keyName: rawbytestring;
  Value: TBytes;
  s: TSerialize;
begin
  at := 0;
  while at < Length(bytes) do
  begin
    Move(bytes[at], keyLen, SizeOf(Integer)); // key len
    inc(at, SizeOf(Integer));
    SetLength(keyName, keyLen);
    Move(bytes[at], PRawByteString(keyName)^, keyLen); // key
    inc(at, keyLen);
    Move(bytes[at], valueLen, SizeOf(Integer)); // value len
    inc(at, SizeOf(Integer));
    SetLength(Value, valueLen); // value

    at2 := 0;
    remainLen := valueLen;
    while remainLen > 0 do
    begin
      if remainLen > PieceSize then
        BufLen := PieceSize
      else
        BufLen := remainLen;
      Move(bytes[at], Value[at2], BufLen); // value
      Inc(at, BufLen);
      Inc(at2, BufLen);
      Dec(remainLen, BufLen);
    end;

    s := TSerialize.Create;
    s.fValue := Value;
    s.fKey := keyName;
    fList.Add(s);
  end;
end;

procedure TSerialize.unMarshal(raw: RawByteString);
var
  keyLen, valueLen, at, at2, remainLen, BufLen: integer;
  keyName: rawbytestring;
  Value: TBytes;
  s: TSerialize;
begin
  at := 0;
  while at < Length(raw) do
  begin
    if at = 0 then
      at := 1;
    Move(raw[at], keyLen, SizeOf(Integer)); // key len
    inc(at, SizeOf(Integer));
    SetLength(keyName, keyLen);
    Move(raw[at], PRawByteString(keyName)^, keyLen); // key
    inc(at, keyLen);
    Move(raw[at], valueLen, SizeOf(Integer)); // value len
    inc(at, SizeOf(Integer));
    SetLength(Value, valueLen); // value

    at2 := 0;
    remainLen := valueLen;
    while remainLen > 0 do
    begin
      if remainLen > PieceSize then
        BufLen := PieceSize
      else
        BufLen := remainLen;
      Move(raw[at], Value[at2], BufLen); // value
      Inc(at, BufLen);
      Inc(at2, BufLen);
      Dec(remainLen, BufLen);
    end;

    s := TSerialize.Create;
    s.fValue := Value;
    s.fKey := keyName;
    fList.Add(s);
  end;
end;

procedure TSerialize.marshal(stream: TStream);
var
  i: integer;
  s: TSerialize;
  keyLen, valueLen, bufLen, remainLen, at: integer;
begin
  stream.Position := 0;
  for i := 0 to fList.Count - 1 do
  begin
    s := TSerialize(fList[i]);
    keyLen := Length(s.fKey);
    stream.Write(keyLen, SizeOf(Integer)); // key len
    stream.Write(prawbytestring(s.fKey)^, keyLen); // key
    valueLen := Length(s.fValue); // value len
    stream.Write(valueLen, SizeOf(Integer));

    remainLen := valueLen;
    at := 0;
    while remainLen > 0 do
    begin
      if remainLen > PieceSize then
        bufLen := PieceSize
      else
        bufLen := remainLen;
      stream.Write(s.fValue[at], bufLen); // value
      Inc(at, bufLen);
      Dec(remainLen, bufLen);
    end;
  end;
end;

function TSerialize.marshal2: TBytes;
var
  i, at, at2, l: integer;
  s: TSerialize;
  keyLen, valueLen, remainLen, bufLen: integer;
begin
  l := 0;
  for i := 0 to fList.Count - 1 do
  begin
    s := TSerialize(fList[i]);
    keyLen := Length(s.fKey);
    valueLen := Length(s.fValue);
    inc(l, SizeOf(Integer) + keyLen + SizeOf(Integer) + valueLen);
  end;
  SetLength(Result, l);

  at := 0;
  for i := 0 to fList.Count - 1 do
  begin
    s := TSerialize(fList[i]);
    keyLen := Length(s.fKey);
    valueLen := Length(s.fValue);
    Move(keyLen, Result[at], SizeOf(Integer)); // keyLen
    inc(at, SizeOf(Integer));
    Move(prawbytestring(s.fKey)^, Result[at], keyLen);
    inc(at, keyLen);
    Move(valueLen, Result[at], SizeOf(Integer)); // valueLen
    inc(at, SizeOf(Integer));

    at2 := 0;
    remainLen := valueLen;
    while remainLen > 0 do
    begin
      if remainLen > PieceSize then
        bufLen := PieceSize
      else
        bufLen := remainLen;
      Move(s.fValue[at2], Result[at], bufLen); // value
      Inc(at, bufLen);
      Inc(at2, bufLen);
      Dec(remainLen, bufLen);
    end;
  end;
end;

function TSerialize.marshal5: OleVariant;
var
  l, keyLen, valueLen, i, at, at2, remainLen, bufLen: Integer;
  s: TSerialize;
  p: pbyte;
begin
  l := 0;
  for i := 0 to fList.Count - 1 do
  begin
    s := TSerialize(fList[i]);
    keyLen := Length(s.fKey);
    valueLen := Length(s.fValue);
    inc(l, SizeOf(Integer) + keyLen + SizeOf(Integer) + valueLen);
  end;
  Result := VarArrayCreate([0, l - 1], varByte);
  at := 0;
  p := VarArrayLock(Result);
  for i := 0 to fList.Count - 1 do
  begin
    s := TSerialize(fList[i]);
    keyLen := Length(s.fKey);
    valueLen := Length(s.fValue);
    Move(keyLen, p[at], SizeOf(Integer));
    inc(at, SizeOf(Integer));
    Move(prawbytestring(s.fKey)^, p[at], keyLen);
    inc(at, keyLen);
    Move(valueLen, p[at], SizeOf(Integer));
    inc(at, SizeOf(Integer));

    at2 := 0;
    remainLen := valueLen;
    while remainLen > 0 do
    begin
      if remainLen > PieceSize then
        bufLen := PieceSize
      else
        bufLen := remainLen;
      Move(s.fValue[at2], p[at], bufLen);
      Inc(at, bufLen);
      Inc(at2, bufLen);
      Dec(remainLen, bufLen);
    end;
  end;
  VarArrayUnlock(Result);
end;

function TSerialize.marshal3: RawByteString;
var
  i, at, at2, bufLen, remainLen, l: integer;
  s: TSerialize;
  keyLen, valueLen: integer;
begin
  l := 0;
  for i := 0 to fList.Count - 1 do
  begin
    s := TSerialize(fList[i]);
    keyLen := Length(s.fKey);
    valueLen := Length(s.fValue);
    inc(l, SizeOf(Integer) + keyLen + SizeOf(Integer) + valueLen);
  end;
  SetLength(Result, l);

  at := 0;
  for i := 0 to fList.Count - 1 do
  begin
    s := TSerialize(fList[i]);
    keyLen := Length(s.fKey);
    valueLen := Length(s.fValue);
    if at = 0 then
      at := 1;
    Move(keyLen, Result[at], SizeOf(Integer)); // keyLen
    inc(at, SizeOf(Integer));
    Move(PRawByteString(s.fKey)^, Result[at], keyLen); // key
    inc(at, keyLen);
    Move(valueLen, Result[at], SizeOf(Integer)); // valueLen
    inc(at, SizeOf(Integer));

    at2 := 0;
    remainLen := valueLen;
    while remainLen > 0 do
    begin
      if remainLen > PieceSize then
        bufLen := PieceSize
      else
        bufLen := remainLen;
      Move(s.fValue[at2], Result[at], bufLen); // value
      Inc(at, bufLen);
      Inc(at2, bufLen);
      Dec(remainLen, bufLen);
    end;
  end;
end;

procedure TSerialize.Clear;
var
  i: integer;
begin
  for i := fList.Count - 1 downto 0 do
  begin
    TSerialize(fList[i]).Free;
    fList.Delete(i);
  end;
end;

constructor TSerialize.Create;
begin
  fList := TList.Create;
end;

function TSerialize.Delete(const keyName: rawbytestring): boolean;
var
  i: integer;
begin
  Result := False;
  for i := fList.Count - 1 downto 0 do
  begin
    if TSerialize(fList[i]).fKey = keyName then
    begin
      TSerialize(fList[i]).Free;
      fList.Delete(i);
      Result := True;
    end;
  end;
end;

destructor TSerialize.Destroy;
begin
  Self.Clear;
  fList.Free;
  inherited;
end;

function TSerialize.getCount: integer;
begin
  Result := fList.Count;
end;

function TSerialize.getBCD(const keyName: rawbytestring): tbcd;
var
  s: TSerialize;
begin
  s := key(keyName);
  Result := pbcd(s.fValue)^;
end;

end.