• bosn.pas


    bosn.pas

    unit bson;
    {$IFDEF FPC}
    {$MODE DELPHI}
    {$ENDIF}
    interface
    
    uses
      SysUtils,
      Classes,
      Contnrs;
    {
      BSON element format
      <type:byte> <c-str> <data>
      <data> below
    }
    
    const
      BSON_EOF = $00;
      BSON_FLOAT = $01; //double 8-byte float
      BSON_STRING = $02; //UTF-8 string
      BSON_DOC = $03; //embedded document
      BSON_ARRAY = $04; //bson document but using integer string for key
      BSON_BINARY = $05; //
      BSON_UNDEFINED = $06; //deprecated
      BSON_OBJECTID = $07; //
      BSON_BOOLEAN = $08; //false:$00, true:$01
      BSON_DATETIME = $09;
      BSON_NULL = $0A;
      BSON_REGEX = $0B; //
      BSON_DBPTR = $0C; //deprecated
      BSON_JS = $0D;
      BSON_SYMBOL = $0E;
      BSON_JSSCOPE = $0F;
      BSON_INT32 = $10;
      BSON_TIMESTAMP = $11;
      BSON_INT64 = $12;
      BSON_MINKEY = $FF;
      BSON_MAXKEY = $7F;
      {subtype}
      BSON_SUBTYPE_FUNC = $01;
      BSON_SUBTYPE_BINARY = $02;
      BSON_SUBTYPE_UUID = $03;
      BSON_SUBTYPE_MD5 = $05;
      BSON_SUBTYPE_USER = $80;
      {boolean constant}
      BSON_BOOL_FALSE = $00;
      BSON_BOOL_TRUE = $01;
    
    const
      nullterm: AnsiChar = #0;
    
    type
      EBSONException = class(Exception);
      TBSONObjectID = array[0..11] of byte;
      TBSONDocument = class;
      TBSONItem = class
      protected
        eltype: byte;
        elname: string;
        fnull: boolean;
    
        procedure WriteDouble(Value: real); virtual;
        procedure WriteInteger(Value: integer); virtual;
        procedure WriteInt64(Value: Int64); virtual;
        procedure WriteBoolean(Value: Boolean); virtual;
        procedure WriteString(Value: string); virtual;
        procedure WriteOID(Value: TBSONObjectID); virtual;
        procedure WriteDocument(Value: TBSONDocument); virtual;
        procedure WriteItem(idx: integer; Value: TBSONItem); virtual;
    
        function ReadDouble: real; virtual;
        function ReadInteger: integer; virtual;
        function ReadInt64: Int64; virtual;
        function ReadBoolean: Boolean; virtual;
        function ReadString: string; virtual;
        function ReadOID: TBSONObjectID; virtual;
        function ReadDocument: TBSONDocument; virtual;
        function ReadItem(idx: integer): TBSONItem; virtual;
      public
        constructor Create(etype: byte = BSON_NULL);
    
        procedure WriteStream(F: TStream); virtual;
        procedure ReadStream(F: TStream); virtual;
    
        function GetSize: longint; virtual;
        function ToString: string; virtual;
    
        function Clone: TBSONItem; virtual;
    
        function IsNull: boolean;
        property AsObjectID: TBSONObjectID read ReadOID write WriteOID;
        property AsInteger: integer read ReadInteger write WriteInteger;
        property AsDouble: real read ReadDouble write WriteDouble;
        property AsInt64: int64 read ReadInt64 write WriteInt64;
        property AsString: string read ReadString write WriteString;
        property AsBoolean: Boolean read ReadBoolean write WriteBoolean;
        property Items[idx: integer]: TBSONItem read ReadItem write WriteItem;
        property Name: string read elname;
      end;
    
      TBSONDocument = class
        FItems: TObjectList;
        function GetItem(i: integer): TBSONItem;
        function GetValue(name: string): TBSONItem;
        procedure SetValue(Name: string; Value: TBSONItem);
        function GetCount: integer;
      public
        constructor Create;
        destructor Destroy; override;
    
        procedure Clear;
        procedure ReadStream(F: TStream);
        procedure WriteStream(F: TStream);
    
        procedure LoadFromFile(filename: string);
        procedure SaveToFile(filename: string);
    
        function IndexOf(name: string): integer;
        function GetSize: longint;
        function Clone: TBSONDocument;
    
        function ToString: string;
        function HasItem(itemname: string): Boolean;
    
        property Items[idx: integer]: TBSONItem read GetItem;
        property Values[Name: string]: TBSONItem read GetValue write SetValue;
        property Count: integer read GetCount;
      end;
    
      TBSONDoubleItem = class(TBSONItem)
        FData: real;
    
        procedure WriteDouble(AValue: real); override;
        function ReadDouble: real; override;
      public
        constructor Create(AValue: real = 0.0);
        function GetSize: longint; override;
    
        function ToString: string; override;
        function Clone: TBSONItem; override;
    
        procedure ReadStream(F: TStream); override;
        procedure WriteStream(F: TStream); override;
      end;
    
      TBSONIntItem = class(TBSONItem)
        FData: integer;
    
        procedure WriteInteger(AValue: integer); override;
        function ReadInteger: integer; override;
      public
        constructor Create(AValue: integer = 0);
        function GetSize: longint; override;
    
        function ToString: string; override;
        function Clone: TBSONItem; override;
    
        procedure ReadStream(F: TStream); override;
        procedure WriteStream(F: TStream); override;
      end;
    
      TBSONStringItem = class(TBSONItem)
      protected
        FData: string;
    
        procedure WriteString(AValue: string); override;
        function ReadString: string; override;
      public
        constructor Create(AValue: string = '');
        function GetSize: longint; override;
    
        function ToString: string; override;
        function Clone: TBSONItem; override;
    
        procedure ReadStream(F: TStream); override;
        procedure WriteStream(F: TStream); override;
      end;
    
      TBSONJSItem = class(TBSONStringItem)
      public
        constructor Create(AValue: string = '');
    
        function Clone: TBSONItem; override;
      end;
    
      TBSONSymbolItem = class(TBSONStringItem)
      public
        constructor Create(AValue: string = '');
    
        function Clone: TBSONItem; override;
      end;
    
      TBSONInt64Item = class(TBSONItem)
        FData: Int64;
    
        procedure WriteInt64(AValue: int64); override;
        function ReadInt64: Int64; override;
      public
        constructor Create(AValue: Int64 = 0);
        function GetSize: longint; override;
    
        function ToString: string; override;
        function Clone: TBSONItem; override;
    
        procedure ReadStream(F: TStream); override;
        procedure WriteStream(F: TStream); override;
      end;
    
      TBSONBooleanItem = class(TBSONItem)
        FData: Boolean;
    
        procedure WriteBoolean(AValue: Boolean); override;
        function ReadBoolean: Boolean; override;
      public
        constructor Create(AValue: Boolean = false);
        function GetSize: longint; override;
    
        function ToString: string; override;
        function Clone: TBSONItem; override;
    
        procedure ReadStream(F: TStream); override;
        procedure WriteStream(F: TStream); override;
      end;
    
      TBSONDocumentItem = class(TBSONItem)
        FData: TBSONDocument;
      public
        constructor Create;
        destructor Destroy; override;
        function GetSize: longint; override;
    
        function ToString: string; override;
        function Clone: TBSONItem; override;
    
        procedure ReadStream(F: TStream); override;
        procedure WriteStream(F: TStream); override;
      end;
    
      TBSONArrayItem = class(TBSONItem)
        FData: TBSONDocument;
    
        procedure WriteItem(idx: integer; item: TBSONItem); override;
        function ReadItem(idx: integer): TBSONItem; override;
      public
        constructor Create;
        destructor Destroy; override;
        function GetSize: longint; override;
    
        function ToString: string; override;
        function Clone: TBSONItem; override;
    
        procedure ReadStream(F: TStream); override;
        procedure WriteStream(F: TStream); override;
      end;
    
      TBSONDatetimeItem = class(TBSONItem)
        FData: TDatetime;
      public
        constructor Create(AValue: TDateTime);
        function GetSize: longint; override;
    
        function ToString: string; override;
        function Clone: TBSONItem; override;
    
        procedure ReadStream(F: TStream); override;
        procedure WriteStream(F: TStream); override;
      end;
    
      TBSONBinaryItem = class(TBSONItem)
        FLen: integer;
        FSubtype: byte;
        FData: Pointer;
      public
        constructor Create;
        destructor Destroy; override;
        function GetSize: longint; override;
    
        function ToString: string; override;
        function Clone: TBSONItem; override;
    
        procedure ReadStream(F: TStream); override;
        procedure WriteStream(F: TStream); override;
      end;
    
      TBSONObjectIDItem = class(TBSONItem)
        FData: TBSONObjectID;
    
        procedure WriteOID(AValue: TBSONObjectID); override;
        function ReadOID: TBSONObjectID; override;
      public
        constructor Create(AValue: string = '000000000000');
        function GetSize: longint; override;
    
        function ToString: string; override;
        function Clone: TBSONItem; override;
    
        procedure ReadStream(F: TStream); override;
        procedure WriteStream(F: TStream); override;
      end;
    
      TBSONDBRefItem = class(TBSONStringItem)
        FValue: TBSONObjectID;
        procedure WriteOID(AValue: TBSONObjectID); override;
        function ReadOID: TBSONObjectID; override;
      public
        constructor Create(AValue: string = ''; AData: string = '');
        function GetSize: longint; override;
    
        function ToString: string; override;
        function Clone: TBSONItem; override;
    
        procedure ReadStream(F: TStream); override;
        procedure WriteStream(F: TStream); override;
      end;
    
      TBSONRegExItem = class(TBSONItem)
        FPattern, FOptions: string;
      public
        constructor Create(APattern: string = ''; AOptions: string = '');
        function GetSize: longint; override;
    
        function ToString: string; override;
        function Clone: TBSONItem; override;
    
        procedure ReadStream(F: TStream); override;
        procedure WriteStream(F: TStream); override;
      end;
    
      TBSONScopedJSItem = class(TBSONItem)
        FLen: integer;
        FCode: string;
        FScope: TBSONDocument;
      public
        constructor Create;
        destructor Destroy; override;
        function GetSize: longint; override;
    
        function ToString: string; override;
        function Clone: TBSONItem; override;
    
        procedure ReadStream(F: TStream); override;
        procedure WriteStream(F: TStream); override;
      end;
    
    function _ReadString(F: TStream): string;
    
    implementation
    
    uses
      DateUtils;
    
    var
      buf: array[0..65535] of AnsiChar;
      nullitem: TBSONItem;
    
    function _ReadString(F: TStream): string;
    var
      i: integer;
      c: Ansichar;
    begin
      i := 0;
      repeat
        f.read(c, sizeof(char));
        buf[i] := c;
        inc(i);
      until c = nullterm;
      result := strpas(buf);
    end;
    
    { TBSONDocument }
    
    procedure TBSONDocument.Clear;
    begin
      FItems.Clear;
    end;
    
    function TBSONDocument.Clone: TBSONDocument;
    var
      i: integer;
    begin
      Result := TBSONDocument.Create;
      for i := 0 to FItems.Count - 1 do begin
        Result.FItems.Add((FItems[i] as TBSONItem).Clone);
      end;
    end;
    
    constructor TBSONDocument.Create;
    begin
      FItems := TObjectlist.Create(true);
    end;
    
    destructor TBSONDocument.Destroy;
    begin
      FItems.Free;
    
      inherited Destroy;
    end;
    
    function TBSONDocument.GetCount: integer;
    begin
      Result := FItems.Count;
    end;
    
    function TBSONDocument.GetItem(i: integer): TBSONItem;
    begin
      if i in [0..(FItems.Count - 1)] then
        Result := (FItems[i] as TBSONItem)
      else
        Result := nullitem;
    end;
    
    function TBSONDocument.GetSize: longint;
    var
      i: integer;
    begin
      Result := 5;
      for i := 0 to FItems.Count - 1 do begin
        Result := Result + (FItems[i] as TBSONItem).GetSize;
      end;
    end;
    
    function TBSONDocument.GetValue(name: string): TBSONItem;
    var
      i: integer;
    begin
      result := nullitem;
      i := IndexOf(name);
      if i <> -1 then Result := (FItems[i] as TBSONItem);
    end;
    
    function TBSONDocument.HasItem(itemname: string): Boolean;
    var
      i: integer;
    begin
      Result := False;
      for i := 0 to FItems.Count - 1 do begin
        if (FItems[i] as TBSONItem).elname = itemname then begin
          Result := True;
          break;
        end;
      end;
    end;
    
    function TBSONDocument.IndexOf(name: string): integer;
    var
      i: integer;
    begin
      Result := -1;
      for i := 0 to FItems.Count - 1 do begin
        if (FItems[i] as TBSONItem).elname = name then begin
          Result := i;
          break;
        end;
      end;
    end;
    
    procedure TBSONDocument.LoadFromFile(filename: string);
    var
      f: TFileStream;
    begin
      f := TFileStream.Create(filename, fmOpenRead);
      try
        ReadStream(f);
      finally
        f.Free;
      end;
    end;
    
    procedure TBSONDocument.ReadStream(F: TStream);
    var
      len: integer;
      elmtype: byte;
      elmname: string;
      lastItem: TBSONItem;
    begin
      Clear;
      f.Read(len, sizeof(len));
      f.Read(elmtype, sizeof(byte));
      while elmtype <> BSON_EOF do begin
        elmname := _ReadString(f);
        case elmtype of
          BSON_ARRAY: lastItem := TBSONArrayItem.Create;
          BSON_BINARY: lastItem := TBSONBinaryItem.Create;
          BSON_DBPTR: lastItem := TBSONDBRefItem.Create;
          BSON_FLOAT: lastItem := TBSONDoubleItem.Create;
          BSON_INT32: lastItem := TBSONIntItem.Create;
          BSON_INT64: lastItem := TBSONInt64Item.Create;
          BSON_BOOLEAN: lastItem := TBSONBooleanItem.Create;
          BSON_STRING: lastItem := TBSONStringItem.Create;
          BSON_DOC: lastItem := TBSONDocumentItem.Create;
          BSON_JS: lastItem := TBSONJSItem.Create;
          BSON_JSSCOPE: lastItem := TBSONScopedJSItem.Create;
          BSON_OBJECTID: lastItem := TBSONObjectIDItem.Create;
          BSON_MINKEY: lastItem := TBSONItem.Create(BSON_MINKEY);
          BSON_MAXKEY: lastItem := TBSONItem.Create(BSON_MAXKEY);
          BSON_REGEX: lastItem := TBSONRegExItem.Create;
          BSON_SYMBOL: lastItem := TBSONSymbolItem.Create;
          BSON_DATETIME: lastItem := TBSONDateTimeItem.Create(0);
        else
          raise EBSONException.Create('unimplemented element handler ' + inttostr(elmtype));
        end;
        with lastItem do begin
          elname := elmname;
          ReadStream(f);
        end;
        FItems.Add(lastItem);
        f.Read(elmtype, sizeof(byte));
      end;
    end;
    
    procedure TBSONDocument.SaveToFile(filename: string);
    var
      f: TFileStream;
    begin
    {$IFDEF FPC}
      f := TFileStream.Create(filename, fmOpenWrite);
    {$ELSE}
      f := TFileStream.Create(FileCreate(filename));
    {$ENDIF}
      try
        WriteStream(f);
      finally
        f.Free;
      end;
    end;
    
    procedure TBSONDocument.SetValue(Name: string; Value: TBSONItem);
    var
      item: TBSONItem;
      idx: integer;
    begin
      idx := IndexOf(name);
      if idx = -1 then begin
        Value.elname := Name;
        FItems.Add(Value)
      end
      else begin
        item := FItems[idx] as TBSONItem;
        if (item.eltype <> value.eltype) then begin
          FItems[idx] := Value;
          item.Free;
        end;
      end;
    end;
    
    function TBSONDocument.ToString: string;
    var
      i, n: integer;
    begin
      Result := '{';
      n := FItems.Count - 1;
      for i := 0 to n do begin
        Result := Result + (FItems[i] as TBSONItem).ToString;
        if i < n then Result := Result + ', ';
      end;
      Result := Result + '}';
    end;
    
    procedure TBSONDocument.WriteStream(F: TStream);
    var
      dummy: integer;
      i: integer;
    begin
      dummy := GetSize;
      f.write(dummy, sizeof(dummy));
      for i := 0 to FItems.Count - 1 do begin
        (FItems[i] as TBSONItem).WriteStream(f);
      end;
      f.Write(nullterm, sizeof(nullterm));
    end;
    
    { TBSONDoubleItem }
    
    function TBSONDoubleItem.Clone: TBSONItem;
    begin
      Result := TBSONDoubleItem.Create(FData);
    end;
    
    constructor TBSONDoubleItem.Create(AValue: real);
    begin
      eltype := BSON_FLOAT;
      FData := AValue;
    end;
    
    function TBSONDoubleItem.GetSize: longint;
    begin
      Result := 2 + length(elname) + sizeof(FData);
    end;
    
    function TBSONDoubleItem.ReadDouble: real;
    begin
      Result := FData;
    end;
    
    procedure TBSONDoubleItem.ReadStream(F: TStream);
    begin
      f.Read(FData, sizeof(FData));
    end;
    
    function TBSONDoubleItem.ToString: string;
    begin
      Result := format('"%s" : %f', [elname, FData]);
    end;
    
    procedure TBSONDoubleItem.WriteDouble(AValue: real);
    begin
      FData := AValue;
    end;
    
    procedure TBSONDoubleItem.WriteStream(F: TStream);
    begin
      f.Write(eltype, sizeof(byte));
      f.Write(elname[1], length(elname));
      f.Write(nullterm, sizeof(nullterm));
      f.Write(FData, sizeof(FData));
    end;
    
    { TBSONIntItem }
    
    function TBSONIntItem.Clone: TBSONItem;
    begin
      Result := TBSONIntItem.Create(FData);
    end;
    
    constructor TBSONIntItem.Create(AValue: integer);
    begin
      eltype := BSON_INT32;
      FData := AValue;
    end;
    
    function TBSONIntItem.GetSize: longint;
    begin
      Result := 2 + length(elname) + sizeof(FData);
    end;
    
    function TBSONIntItem.ReadInteger: integer;
    begin
      result := FData;
    end;
    
    procedure TBSONIntItem.ReadStream(F: TStream);
    begin
      f.Read(fdata, sizeof(integer));
    end;
    
    function TBSONIntItem.ToString: string;
    begin
      Result := format('"%s" : %d', [elname, FData]);
    end;
    
    procedure TBSONIntItem.WriteInteger(AValue: integer);
    begin
      FData := AValue;
    end;
    
    procedure TBSONIntItem.WriteStream(F: TStream);
    begin
      f.Write(eltype, sizeof(byte));
      f.Write(elname[1], length(elname));
      f.Write(nullterm, sizeof(nullterm));
      f.Write(FData, sizeof(FData));
    end;
    
    { TBSONStringItem }
    
    function TBSONStringItem.Clone: TBSONItem;
    begin
      Result := TBSONStringItem.Create(FData);
    end;
    
    constructor TBSONStringItem.Create(AValue: string);
    begin
      eltype := BSON_STRING;
      FData := AValue;
    end;
    
    function TBSONStringItem.GetSize: longint;
    begin
      Result := 7 + length(elname) + length(fdata);
    end;
    
    procedure TBSONStringItem.ReadStream(F: TStream);
    var
      len: integer;
    begin
      f.Read(len, sizeof(integer));
      FData := _ReadString(F);
    end;
    
    function TBSONStringItem.ReadString: string;
    begin
      Result := FData;
    end;
    
    function TBSONStringItem.ToString: string;
    begin
      Result := format('"%s" : "%s"', [elname, FData]);
    end;
    
    procedure TBSONStringItem.WriteStream(F: TStream);
    var
      len: integer;
    begin
      len := length(FData) + 1;
      f.Write(eltype, sizeof(byte));
      f.Write(elname[1], length(elname));
      f.Write(nullterm, sizeof(nullterm));
      f.Write(len, sizeof(integer));
      f.Write(FData[1], length(FData));
      f.Write(nullterm, sizeof(nullterm));
    end;
    
    procedure TBSONStringItem.WriteString(AValue: string);
    begin
      FData := AValue;
    end;
    
    { TBSONInt64Item }
    
    function TBSONInt64Item.Clone: TBSONItem;
    begin
      Result := TBSONInt64Item.Create(FData);
    end;
    
    constructor TBSONInt64Item.Create(AValue: Int64);
    begin
      eltype := BSON_INT64;
      FData := AValue;
    end;
    
    function TBSONInt64Item.GetSize: longint;
    begin
      Result := 2 + length(elname) + sizeof(fdata);
    end;
    
    function TBSONInt64Item.ReadInt64: Int64;
    begin
      Result := FData;
    end;
    
    procedure TBSONInt64Item.ReadStream(F: TStream);
    begin
      f.Read(FData, sizeof(FData));
    end;
    
    function TBSONInt64Item.ToString: string;
    begin
      Result := format('"%s" : %d', [elname, FData]);
    end;
    
    procedure TBSONInt64Item.WriteInt64(AValue: int64);
    begin
      FData := AValue;
    end;
    
    procedure TBSONInt64Item.WriteStream(F: TStream);
    begin
      f.Write(eltype, sizeof(byte));
      f.Write(elname[1], length(elname));
      f.Write(nullterm, sizeof(nullterm));
      f.Write(FData, sizeof(FData));
    end;
    
    { TBSONBooleanItem }
    
    function TBSONBooleanItem.Clone: TBSONItem;
    begin
      Result := TBSONBooleanItem.Create(FData);
    end;
    
    constructor TBSONBooleanItem.Create(AValue: Boolean);
    begin
      eltype := BSON_BOOLEAN;
      FData := AValue;
    end;
    
    function TBSONBooleanItem.GetSize: longint;
    begin
      Result := 3 + length(elname);
    end;
    
    function TBSONBooleanItem.ReadBoolean: Boolean;
    begin
      Result := FData;
    end;
    
    procedure TBSONBooleanItem.ReadStream(F: TStream);
    var
      b: Byte;
    begin
      f.Read(b, sizeof(byte));
      FData := b = BSON_BOOL_TRUE
    end;
    
    function TBSONBooleanItem.ToString: string;
    begin
      Result := format('"%s" : %s', [elname, BoolToStr(FData, true)]);
    end;
    
    procedure TBSONBooleanItem.WriteBoolean(AValue: Boolean);
    begin
      FData := AValue;
    end;
    
    procedure TBSONBooleanItem.WriteStream(F: TStream);
    var
      boolb: byte;
    begin
      f.Write(eltype, sizeof(byte));
      f.Write(elname[1], length(elname));
      f.Write(nullterm, sizeof(nullterm));
      if FData then
        boolb := BSON_BOOL_TRUE
      else
        boolb := BSON_BOOL_FALSE;
      f.Write(boolb, sizeof(byte));
    end;
    
    { TBSONItem }
    
    function TBSONItem.Clone: TBSONItem;
    begin
      Result := TBSONItem.Create(eltype);
    end;
    
    constructor TBSONItem.Create(etype: byte);
    begin
      fnull := true;
      eltype := etype;
    end;
    
    function TBSONItem.GetSize: longint;
    begin
      Result := 0;
      if FNull then
        Result := 2 + Length(elName);
    end;
    
    function TBSONItem.IsNull: boolean;
    begin
      Result := FNull;
    end;
    
    function TBSONItem.ReadBoolean: Boolean;
    begin
      Result := False;
    end;
    
    function TBSONItem.ReadDocument: TBSONDocument;
    begin
      Result := nil;
    end;
    
    function TBSONItem.ReadDouble: real;
    begin
      Result := 0;
    end;
    
    function TBSONItem.ReadInt64: Int64;
    begin
      Result := 0;
    end;
    
    function TBSONItem.ReadInteger: integer;
    begin
      Result := 0;
    end;
    
    function TBSONItem.ReadItem(idx: integer): TBSONItem;
    begin
      Result := nullitem;
    end;
    
    function TBSONItem.ReadOID: TBSONObjectID;
    begin
      Result := Result;
    end;
    
    procedure TBSONItem.ReadStream(F: TStream);
    begin
    
    end;
    
    function TBSONItem.ReadString: string;
    begin
      Result := '';
    end;
    
    function TBSONItem.ToString: string;
    begin
      Result := elname + ' : null';
    end;
    
    procedure TBSONItem.WriteBoolean(Value: Boolean);
    begin
    
    end;
    
    procedure TBSONItem.WriteDocument(Value: TBSONDocument);
    begin
    
    end;
    
    procedure TBSONItem.WriteDouble(Value: real);
    begin
    
    end;
    
    procedure TBSONItem.WriteInt64(Value: Int64);
    begin
    
    end;
    
    procedure TBSONItem.WriteInteger(Value: integer);
    begin
    
    end;
    
    procedure TBSONItem.WriteItem(idx: integer; Value: TBSONItem);
    begin
    
    end;
    
    procedure TBSONItem.WriteOID(Value: TBSONObjectID);
    begin
    
    end;
    
    procedure TBSONItem.WriteStream(F: TStream);
    begin
      if FNull then begin
        f.Write(eltype, sizeof(byte));
        f.Write(elname[1], length(elname));
        f.Write(nullterm, sizeof(nullterm));
      end;
    end;
    
    procedure TBSONItem.WriteString(Value: string);
    begin
    
    end;
    
    { TBSONDocumentItem }
    
    function TBSONDocumentItem.Clone: TBSONItem;
    var
      item: TBSONDocumentItem;
    begin
      item := TBSONDocumentItem.Create;
      item.FData.Free;
      item.FData := FData.Clone;
      Result := item;
    end;
    
    constructor TBSONDocumentItem.Create;
    begin
      FData := TBSONDocument.Create;
    end;
    
    destructor TBSONDocumentItem.Destroy;
    begin
      FData.Free;
    
      inherited Destroy;
    end;
    
    function TBSONDocumentItem.GetSize: longint;
    begin
      Result := 2 + length(elname) + FData.GetSize;
    end;
    
    procedure TBSONDocumentItem.ReadStream(F: TStream);
    begin
      inherited;
      FData.ReadStream(f);
    end;
    
    function TBSONDocumentItem.ToString: string;
    begin
      Result := format('"%s" : %s', [elname, FData.ToString]);
    end;
    
    procedure TBSONDocumentItem.WriteStream(F: TStream);
    begin
      f.Write(eltype, sizeof(byte));
      f.Write(elname[1], length(elname));
      f.Write(nullterm, sizeof(nullterm));
      FData.WriteStream(f);
    end;
    
    { TBSONArrayItem }
    
    function TBSONArrayItem.Clone: TBSONItem;
    var
      item: TBSONArrayItem;
    begin
      item := TBSONArrayItem.Create;
      item.FData.Free;
      item.FData := FData.Clone;
      Result := Item;
    end;
    
    constructor TBSONArrayItem.Create;
    begin
      eltype := BSON_ARRAY;
      FData := TBSONDocument.Create;
    end;
    
    destructor TBSONArrayItem.Destroy;
    begin
      FData.Free;
    
      inherited Destroy;
    end;
    
    function TBSONArrayItem.GetSize: longint;
    begin
      Result := 2 + length(elname) + FData.GetSize;
    end;
    
    function TBSONArrayItem.ReadItem(idx: integer): TBSONItem;
    begin
      Result := FData.Items[idx];
    end;
    
    procedure TBSONArrayItem.ReadStream(F: TStream);
    begin
      FData.ReadStream(F);
    end;
    
    function TBSONArrayItem.ToString: string;
    var
      i, n: integer;
      tmp, t2: string;
    begin
      tmp := '';
      n := FData.Count - 1;
      for i := 0 to n do begin
        t2 := FData.Items[i].ToString;
        tmp := tmp + Copy(t2, Pos(':', t2) + 1, length(t2));
        if i < n then tmp := tmp + ', ';
      end;
      Result := format('"%s" : [%s]', [elname, tmp]);
    end;
    
    procedure TBSONArrayItem.WriteItem(idx: integer; item: TBSONItem);
    begin
      inherited;
      FData.SetValue(IntToStr(idx), item);
    end;
    
    procedure TBSONArrayItem.WriteStream(F: TStream);
    begin
      f.Write(eltype, sizeof(byte));
      f.Write(elname[1], length(elname));
      f.Write(nullterm, sizeof(nullterm));
      FData.WriteStream(f);
    end;
    
    { TBSONDatetimeItem }
    
    function TBSONDatetimeItem.Clone: TBSONItem;
    begin
      Result := TBSONDateTimeItem.Create(FData);
    end;
    
    constructor TBSONDatetimeItem.Create(AValue: TDateTime);
    begin
      eltype := BSON_DATETIME;
      FData := AValue;
    end;
    
    function TBSONDatetimeItem.GetSize: longint;
    begin
      result := 2 + length(elname) + sizeof(int64);
    end;
    
    procedure TBSONDatetimeItem.ReadStream(F: TStream);
    var
      data: int64;
    begin
      f.Read(data, sizeof(int64));
      FData := UnixToDateTime(data);
    end;
    
    function TBSONDatetimeItem.ToString: string;
    begin
      Result := format('"%s" : %s', [elname, DateTimeToStr(FData)]);
    end;
    
    procedure TBSONDatetimeItem.WriteStream(F: TStream);
    var
      data: Int64;
    begin
      data := DateTimeToUnix(FData);
      f.Write(eltype, sizeof(byte));
      f.Write(elname[1], length(elname));
      f.Write(nullterm, sizeof(nullterm));
      f.Write(Data, sizeof(int64));
    end;
    
    { TBSONJSItem }
    
    function TBSONJSItem.Clone: TBSONItem;
    begin
      Result := TBSONJSItem.Create(FData);
    end;
    
    constructor TBSONJSItem.Create(AValue: string);
    begin
      inherited Create(AValue);
      eltype := BSON_JS;
    end;
    
    { TBSONObjectIDItem }
    
    function TBSONObjectIDItem.Clone: TBSONItem;
    begin
      Result := TBSONObjectIDItem.Create;
      Result.AsObjectID := FData;
    end;
    
    constructor TBSONObjectIDItem.Create(AValue: string);
    var
      i: integer;
    begin
      eltype := BSON_OBJECTID;
      if length(AValue) = 12 then
        for i := 0 to 11 do
          FData[i] := StrToInt(AValue[i + 1]);
    end;
    
    function TBSONObjectIDItem.GetSize: longint;
    begin
      result := 2 + length(elname) + 12;
    end;
    
    function TBSONObjectIDItem.ReadOID: TBSONObjectID;
    begin
      Result := FData;
    end;
    
    procedure TBSONObjectIDItem.ReadStream(F: TStream);
    begin
      f.Read(FData[0], 12);
    end;
    
    function TBSONObjectIDItem.ToString: string;
    begin
      Result := format('"%s" : ObjectID("%s%s%s%s%s%s%s%s%s%s%s%s")', [elname,
        IntToHex(FData[0], 2),
          IntToHex(FData[1], 2),
          IntToHex(FData[2], 2),
          IntToHex(FData[3], 2),
          IntToHex(FData[4], 2),
          IntToHex(FData[5], 2),
          IntToHex(FData[6], 2),
          IntToHex(FData[7], 2),
          IntToHex(FData[8], 2),
          IntToHex(FData[9], 2),
          IntToHex(FData[10], 2),
          IntToHex(FData[11], 2)
          ]);
    end;
    
    procedure TBSONObjectIDItem.WriteOID(AValue: TBSONObjectID);
    begin
      FData := AValue;
    end;
    
    procedure TBSONObjectIDItem.WriteStream(F: TStream);
    begin
      f.Write(eltype, sizeof(byte));
      f.Write(elname[1], length(elname));
      f.Write(nullterm, sizeof(nullterm));
      f.Write(FData[0], 12);
    end;
    
    { TBSONRegExItem }
    
    function TBSONRegExItem.Clone: TBSONItem;
    begin
      Result := TBSONRegExItem.Create(FPattern, FOptions);
    end;
    
    constructor TBSONRegExItem.Create(APattern, AOptions: string);
    begin
      FPattern := APattern;
      FOptions := AOptions;
      eltype := BSON_REGEX;
    end;
    
    function TBSONRegExItem.GetSize: longint;
    begin
      result := 2 + length(elname) + 1 + length(FPattern) + 1 + length(FOptions);
    end;
    
    procedure TBSONRegExItem.ReadStream(F: TStream);
    begin
      FPattern := _ReadString(f);
      FOptions := _ReadString(f);
    end;
    
    function TBSONRegExItem.ToString: string;
    begin
      Result := format('"%s" : "%s" "%s"', [elname, FPattern, FOptions]);
    end;
    
    procedure TBSONRegExItem.WriteStream(F: TStream);
    begin
      f.Write(eltype, sizeof(byte));
      f.Write(elname[1], length(elname));
      f.Write(nullterm, sizeof(nullterm));
      f.Write(FPattern[1], length(FPattern));
      f.Write(nullterm, sizeof(nullterm));
      f.Write(FOptions[1], length(FOptions));
      f.Write(nullterm, sizeof(nullterm));
    end;
    
    { TBSONBinaryItem }
    
    function TBSONBinaryItem.Clone: TBSONItem;
    var
      ms: TMemoryStream;
    begin
      Result := TBSONBinaryItem.Create;
      ms := TMemoryStream.Create;
      try
        WriteStream(ms);
        ms.Seek(0, soFromBeginning);
        Result.ReadStream(ms);
      finally
        ms.Free;
      end;
    end;
    
    constructor TBSONBinaryItem.Create;
    begin
      FLen := 0;
      FData := nil;
      FSubtype := BSON_SUBTYPE_USER;
      eltype := BSON_BINARY;
    end;
    
    destructor TBSONBinaryItem.Destroy;
    begin
      if FLen <> 0 then
        FreeMem(FData);
    
      inherited Destroy;
    end;
    
    function TBSONBinaryItem.GetSize: longint;
    begin
      result := 2 + length(elname) + 4 + 1 + FLen;
    end;
    
    procedure TBSONBinaryItem.ReadStream(F: TStream);
    begin
      f.Read(FLen, sizeof(integer));
      f.Read(FSubtype, sizeof(byte));
      GetMem(FData, FLen);
      f.Read(FData^, Flen);
    end;
    
    function TBSONBinaryItem.ToString: string;
    begin
      Result := format('"%s" : %s', [elname, 'Binary']);
    end;
    
    procedure TBSONBinaryItem.WriteStream(F: TStream);
    begin
      f.Write(eltype, sizeof(byte));
      f.Write(elname[1], length(elname));
      f.Write(nullterm, sizeof(nullterm));
    
      f.Write(FLen, sizeof(integer));
      f.Write(FSubtype, sizeof(byte));
      f.Write(FData^, FLen);
    end;
    
    
    { TBSONScopedJSItem }
    
    function TBSONScopedJSItem.Clone: TBSONItem;
    var
      item: TBSONScopedJSItem;
    begin
      item := TBSONScopedJSItem.Create;
      item.FCode := FCode;
      item.FLen := FLen;
      item.FScope.Free;
      item.FScope := FScope.Clone;
      Result := item;
    end;
    
    constructor TBSONScopedJSItem.Create;
    begin
      eltype := BSON_JSSCOPE;
      FScope := TBSONDocument.Create;
    end;
    
    destructor TBSONScopedJSItem.Destroy;
    begin
      FScope.Free;
    
      inherited Destroy;
    end;
    
    function TBSONScopedJSItem.GetSize: longint;
    begin
      result := 2 + length(elname) + 4 + length(fcode) + 1 + FScope.GetSize;
    end;
    
    procedure TBSONScopedJSItem.ReadStream(F: TStream);
    begin
      f.Read(Flen, sizeof(integer));
      FCode := _ReadString(f);
      FScope.ReadStream(f);
    end;
    
    function TBSONScopedJSItem.ToString: string;
    begin
      Result := format('"%s" : "%s" %s', [elname, FCode, FScope.ToString]);
    end;
    
    procedure TBSONScopedJSItem.WriteStream(F: TStream);
    begin
      f.Write(eltype, sizeof(byte));
      f.Write(elname[1], length(elname));
      f.Write(nullterm, sizeof(nullterm));
      FLen := FScope.GetSize + 5 + length(FCode);
      f.Write(FLen, sizeof(integer));
      f.Write(FCode[1], length(FCode));
      f.Write(nullterm, sizeof(nullterm));
      FScope.WriteStream(f);
    end;
    
    { TBSONSymbolItem }
    
    function TBSONSymbolItem.Clone: TBSONItem;
    begin
      Result := TBSONSymbolItem.Create(FData);
    end;
    
    constructor TBSONSymbolItem.Create(AValue: string);
    begin
      eltype := BSON_SYMBOL;
    end;
    
    { TBSONDBRefItem }
    
    function TBSONDBRefItem.Clone: TBSONItem;
    begin
      Result := TBSONDBRefItem.Create(FData);
      Result.AsObjectID := FValue;
    end;
    
    constructor TBSONDBRefItem.Create(AValue, AData: string);
    var
      i: integer;
    begin
      inherited Create(AValue);
      eltype := BSON_DBPTR;
      if length(AData) = 12 then
        for i := 0 to 11 do
          FValue[i] := StrToInt(AData[1 + i]);
    end;
    
    function TBSONDBRefItem.GetSize: longint;
    begin
      result := 3 + length(elname) + length(FData) + 12;
    end;
    
    function TBSONDBRefItem.ReadOID: TBSONObjectID;
    begin
      Result := FValue;
    end;
    
    procedure TBSONDBRefItem.ReadStream(F: TStream);
    begin
      inherited;
      f.Read(FValue[0], 12);
    end;
    
    function TBSONDBRefItem.ToString: string;
    begin
      Result := format('"%s" : DBRef("%s", "%s%s%s%s%s%s%s%s%s%s%s%s")', [elname,
        FData,
          IntToHex(FValue[0], 2),
          IntToHex(FValue[1], 2),
          IntToHex(FValue[2], 2),
          IntToHex(FValue[3], 2),
          IntToHex(FValue[4], 2),
          IntToHex(FValue[5], 2),
          IntToHex(FValue[6], 2),
          IntToHex(FValue[7], 2),
          IntToHex(FValue[8], 2),
          IntToHex(FValue[9], 2),
          IntToHex(FValue[10], 2),
          IntToHex(FValue[11], 2)
          ]);
    end;
    
    procedure TBSONDBRefItem.WriteOID(AValue: TBSONObjectID);
    begin
      FValue := AValue;
    end;
    
    procedure TBSONDBRefItem.WriteStream(F: TStream);
    begin
      inherited;
      f.Write(FValue[0], 12);
    end;
    
    initialization
      nullitem := TBSONItem.Create;
    finalization
      nullitem.Free;
    end.
    

      调用:

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      var doc: TBSONDocument := TBSONDocument.Create;
      var item: TBSONStringItem := TBSONStringItem.Create('test');
      doc.SetValue('str', item);
      var item2: TBSONDatetimeItem := TBSONDatetimeItem.Create(now);
      doc.setvalue('datetime', item2);
      memo1.Text := doc.ToString;
    end;
    

      

  • 相关阅读:
    如何加速JavaScript 代码
    以Kafka Connect作为实时数据集成平台的基础架构有什么优势?
    Java多线程开发系列之一:走进多线程
    java运行环境和运行机制
    C#先序遍历2叉树(非递归)
    Java 之 List<T> 接口的实现:ArrayList
    string.split() 解读---------->从java 和C#的角度剖析
    究竟什么是语法糖呢
    Eclipse 恢复删除的文件
    Notepad++自动刷新文本
  • 原文地址:https://www.cnblogs.com/hnxxcxg/p/13635108.html
Copyright © 2020-2023  润新知