Ignore:
Timestamp:
Oct 20, 2009, 11:06:34 AM (15 years ago)
Author:
george
Message:
  • Modified: Definition tree is now separated from data. This was needed for implementation of repeated fields.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • ProtocolBuffers/UProtocolBuffers.pas

    r10 r11  
    1 // http://code.google.com/intl/cs/apis/protocolbuffers/docs/overview.html
     1// Specification: http://code.google.com/intl/cs/apis/protocolbuffers/docs/overview.html
    22unit UProtocolBuffers;
    33
     
    77
    88uses
    9   Classes, SysUtils;
     9  Classes, SysUtils, Dialogs;
    1010
    1111type
    1212  TPBItemMode = (imRequired, imOptional, imRepeated);
    13   TPBItemType = (itVariant, it64bit, itLengthDelimited, itStartGroup,
    14     itEndGroup, it32bit);
     13  TPBWireType = (wtVariant, wt64bit, wtLengthDelimited, wtStartGroup,
     14    wtEndGroup, wt32bit);
     15  TPBItemType = (itInteger, itString, itMessage, itFloat, itDouble, itBlock);
    1516
    1617  TPBEnumeration = class
    1718  end;
    1819
    19   { TPBItem }
    20   TPBItem = class
     20  { TPBDefinition }
     21  TPBDefinition = class
     22  private
     23    function GetWireType: TPBWireType;
     24  public
    2125    Name: string;
    2226    Tag: Integer;
    2327    ItemType: TPBItemType;
    2428    ItemMode: TPBItemMode;
     29    Items: TList; // TList<TPBDefinition>
     30    DefaultString: string;
     31    DefaultInteger: Integer;
     32    constructor Create;
     33    destructor Destroy; override;
     34    function SearchItemByTag(Tag: Integer): Integer;
     35    property WireType: TPBWireType read GetWireType;
     36  end;
     37
     38  { TPBItemHead }
     39  TPBItemHead = record
     40    Tag: Integer;
     41    WireType: TPBWireType;
     42  end;
     43
     44  { TPBItem }
     45  TPBItem = class
    2546    procedure SaveVariantToStream(Stream: TStream; Value: Integer);
    2647    function LoadVariantFromStream(Stream: TStream): Integer;
    2748    procedure SaveLengthDelimitedToStream(Stream: TStream; Block: TStream);
    2849    procedure LoadLengthDelimitedFromStream(Stream: TStream; Block: TStream);
    29     procedure SaveHeadToStream(Stream: TStream);
    30     procedure LoadHeadFromStream(Stream: TStream);
    31     procedure SaveToStream(Stream: TStream); virtual;
    32     procedure LoadFromStream(Stream: TStream); virtual;
     50    procedure SaveHeadToStream(Stream: TStream; Definition: TPBDefinition);
     51    function LoadHeadFromStream(Stream: TStream; Definition: TPBDefinition): TPBItemHead;
     52    procedure SaveToStream(Stream: TStream; Definition: TPBDefinition); virtual;
     53    procedure LoadFromStream(Stream: TStream; Definition: TPBDefinition); virtual;
     54    procedure Clear(Definition: TPBDefinition); virtual;
     55    procedure Assign(Source: TPBItem); virtual;
    3356  end;
    3457
     
    3861  TPBStringItem = class(TPBItem)
    3962    Value: string;
    40     procedure SaveToStream(Stream: TStream); override;
    41     procedure LoadFromStream(Stream: TStream); override;
     63    procedure SaveToStream(Stream: TStream; Definition: TPBDefinition); override;
     64    procedure LoadFromStream(Stream: TStream; Definition: TPBDefinition); override;
    4265    constructor Create;
     66    procedure Assign(Source: TPBItem); override;
    4367  end;
    4468
     
    4670  TPBIntegerItem = class(TPBItem)
    4771    Value: Integer;
    48     procedure SaveToStream(Stream: TStream); override;
    49     procedure LoadFromStream(Stream: TStream); override;
     72    procedure SaveToStream(Stream: TStream; Definition: TPBDefinition); override;
     73    procedure LoadFromStream(Stream: TStream; Definition: TPBDefinition); override;
    5074    constructor Create;
     75    procedure Assign(Source: TPBItem); override;
    5176  end;
    5277
     
    5479  TPBMessageItem = class(TPBItem)
    5580    GenerateHead: Boolean;
    56     Items: TList; // TList<TPBItem>;
    57     function SearchItemByTag(Tag: Integer): TPBItem;
    58     procedure SaveToStream(Stream: TStream); override;
    59     procedure LoadFromStream(Stream: TStream); override;
     81    Items: TList; // TList<TList<TPBItem>>;
     82    procedure Clear(Definition: TPBDefinition); override;
     83    procedure SaveToStream(Stream: TStream; Definition: TPBDefinition); override;
     84    procedure LoadFromStream(Stream: TStream; Definition: TPBDefinition); override;
    6085    constructor Create;
    6186    destructor Destroy; override;
     87    procedure Assign(Source: TPBItem); override;
     88  end;
     89
     90  { TPBRepeatedItem }
     91
     92  TPBRepeatedItem = class(TPBItem)
     93    Items: TList;
     94    procedure Clear(Definition: TPBDefinition); override;
     95    procedure SaveToStream(Stream: TStream; Definition: TPBDefinition); override;
     96    procedure LoadFromStream(Stream: TStream; Definition: TPBDefinition); override;
     97    constructor Create;
     98    destructor Destroy; override;
     99    procedure Assign(Source: TPBItem); override;
    62100  end;
    63101
    64102  { TProtocolBuffer }
    65103  TProtocolBuffer = class
     104    Definition: TPBDefinition;
    66105    BaseMessage: TPBMessageItem;
    67106    procedure LoadFromStream(Stream: TStream);
     
    82121begin
    83122  BaseMessage.GenerateHead := False;
    84   BaseMessage.LoadFromStream(Stream);
     123  BaseMessage.LoadFromStream(Stream, Definition);
    85124end;
    86125
     
    88127begin
    89128  BaseMessage.GenerateHead := False;
    90   BaseMessage.SaveToStream(Stream);
     129  BaseMessage.SaveToStream(Stream, Definition);
    91130end;
    92131
     
    99138begin
    100139  BaseMessage := TPBMessageItem.Create;
     140  Definition := TPBDefinition.Create;
     141  Definition.ItemType := itMessage;
    101142end;
    102143
    103144destructor TProtocolBuffer.Destroy;
    104145begin
    105   if Assigned(BaseMessage) then BaseMessage.Free;
     146  Definition.Destroy;
     147  BaseMessage.Free;
    106148  inherited Destroy;
    107149end;
     
    109151{ TPBMessageItem }
    110152
    111 function TPBMessageItem.SearchItemByTag(Tag: Integer): TPBItem;
    112 var
    113   I: Integer;
    114 begin
    115   I := 0;
    116   while (I < Items.Count) and (TPBItem(Items[I]).Tag <> Tag) do Inc(I);
    117   if I < Items.Count then Result := Items[I]
    118     else Result := nil;
    119 end;
    120 
    121 procedure TPBMessageItem.SaveToStream(Stream: TStream);
    122 var
    123   I: Integer;
     153procedure TPBMessageItem.Clear(Definition: TPBDefinition);
     154var
     155  I: Integer;
     156  Q: Integer;
     157begin
     158  for I := 0 to Items.Count - 1 do
     159    TPBItem(Items[I]).Clear(Definition);
     160  Items.Clear;
     161  Items.Count := Definition.Items.Count;
     162  for I := 0 to Items.Count - 1 do begin
     163    if TPBDefinition(Definition.Items[I]).ItemMode = imRepeated then
     164      Items[I] := TPBRepeatedItem.Create
     165    else
     166    if TPBDefinition(Definition.Items[I]).ItemType = itInteger then begin
     167      Items[I] := TPBIntegerItem.Create;
     168      TPBIntegerItem(Items[I]).Value := TPBDefinition(Definition.Items[I]).DefaultInteger;
     169    end else
     170    if TPBDefinition(Definition.Items[I]).ItemType = itString then begin
     171      Items[I] := TPBStringItem.Create;
     172      TPBStringItem(Items[I]).Value := TPBDefinition(Definition.Items[I]).DefaultString;
     173    end else
     174    if TPBDefinition(Definition.Items[I]).ItemType = itMessage then begin
     175      Items[I] := TPBMessageItem.Create;
     176      TPBMessageItem(Items[I]).Clear(Definition.Items[I]);
     177    end;
     178  end;
     179end;
     180
     181procedure TPBMessageItem.SaveToStream(Stream: TStream; Definition: TPBDefinition);
     182var
     183  I: Integer;
     184  Q: Integer;
    124185  TempStream: TMemoryStream;
    125186begin
     
    127188  // Generate message content to temporary stream
    128189  TempStream := TMemoryStream.Create;
    129   for I := 0 to Items.Count - 1 do
    130     TPBItem(Items[I]).SaveToStream(TempStream);
    131   // if head is used than write lenght-delimited head type with block byte length
     190  if Items.Count <> Definition.Items.Count then
     191    raise Exception.Create('Definition and value items count mismatch.');
     192  for I := 0 to Definition.Items.Count - 1 do
     193    TPBItem(Items[I]).SaveToStream(TempStream, Definition.Items[I]);
     194
     195  // If head is used than write lenght-delimited head type with block byte length
    132196  if GenerateHead then begin
    133     SaveHeadToStream(Stream);
     197    SaveHeadToStream(Stream, Definition);
    134198    SaveVariantToStream(Stream, TempStream.Size);
    135199  end;
     
    139203end;
    140204
    141 procedure TPBMessageItem.LoadFromStream(Stream: TStream);
     205procedure TPBMessageItem.LoadFromStream(Stream: TStream; Definition: TPBDefinition);
    142206var
    143207  I: Integer;
    144208  TempItem: TPBItem;
    145   SearchItem: TPBItem;
     209  ItemIndex: Integer;
    146210  EndIndex: Integer;
    147211  TempStream: TMemoryStream;
     212  ItemHead: TPBItemHead;
     213  NewItem: TPBItem;
    148214begin
    149215  inherited;
     
    156222
    157223  TempItem := TPBItem.Create;
     224  Clear(Definition);
    158225  while Stream.Position < EndIndex do begin
    159     TempItem.LoadHeadFromStream(Stream);
    160     SearchItem := SearchItemByTag(TempItem.Tag);
    161     if Assigned(SearchItem) then begin
    162       if SearchItem.ItemType <> TempItem.ItemType then
    163         raise Exception.Create('Bad type for item "' + SearchItem.Name +
    164           '" with tag ' + IntToStr(SearchItem.Tag));
    165       if SearchItem is TPBIntegerItem then
    166         TPBIntegerItem(SearchItem).LoadFromStream(Stream)
    167       else if SearchItem is TPBStringItem then
    168         TPBStringItem(SearchItem).LoadFromStream(Stream)
    169       else if SearchItem is TPBMessageItem then
    170         TPBMessageItem(SearchItem).LoadFromStream(Stream);
     226    ItemHead := TempItem.LoadHeadFromStream(Stream, Definition);
     227    ItemIndex := Definition.SearchItemByTag(ItemHead.Tag);
     228    if ItemIndex <> -1 then
     229      with TPBDefinition(Definition.Items[ItemIndex]) do begin
     230        if WireType <> ItemHead.WireType then
     231          raise Exception.Create('Bad type for item "' + TPBDefinition(Definition.Items[ItemIndex]).Name +
     232            '" with tag ' + IntToStr(ItemHead.Tag));
     233        if ItemType = itInteger then begin
     234          NewItem := TPBIntegerItem.Create;
     235          TPBIntegerItem(NewItem).LoadFromStream(Stream, Definition.Items[ItemIndex]);
     236        end else
     237        if TPBDefinition(Definition.Items[ItemIndex]).ItemType = itString then begin
     238          NewItem := TPBStringItem.Create;
     239          TPBStringItem(NewItem).LoadFromStream(Stream, Definition.Items[ItemIndex])
     240        end else
     241        if TPBDefinition(Definition.Items[ItemIndex]).ItemType = itMessage then begin
     242          NewItem := TPBMessageItem.Create;
     243          TPBMessageItem(NewItem).LoadFromStream(Stream, Definition.Items[ItemIndex]);
     244        end;
     245
     246        if ItemMode = imRepeated then begin
     247          TPBRepeatedItem(Items[ItemIndex]).Items.Add(NewItem);
     248        end else begin
     249          TPBItem(Self.Items[ItemIndex]).Assign(NewItem);
     250          NewItem.Free;
     251        end;
    171252    end else begin
    172       if TempItem.ItemType = itVariant then
     253      // Skip item data
     254      if ItemHead.WireType = wtVariant then
    173255        TempItem.LoadVariantFromStream(Stream)
    174       else if TempItem.ItemType = itLengthDelimited then
     256      else if ItemHead.WireType = wtLengthDelimited then
    175257        TempItem.LoadLengthDelimitedFromStream(Stream, TempStream);
    176258    end;
     
    181263constructor TPBMessageItem.Create;
    182264begin
    183   ItemType := itLengthDelimited;
    184265  Items := TList.Create;
    185266  GenerateHead := True;
     
    194275  Items.Free;
    195276  inherited Destroy;
     277end;
     278
     279procedure TPBMessageItem.Assign(Source: TPBItem);
     280var
     281  I: Integer;
     282begin
     283  if Source is TPBMessageItem then begin
     284    GenerateHead := TPBMessageItem(Source).GenerateHead;
     285    for I := 0 to Items.Count - 1 do
     286      TPBItem(Items[I]).Assign(TPBMessageItem(Source).Items[I]);
     287  end;
    196288end;
    197289
     
    215307end;
    216308
    217 procedure TPBItem.SaveHeadToStream(Stream: TStream);
     309procedure TPBItem.SaveHeadToStream(Stream: TStream; Definition: TPBDefinition);
    218310var
    219311  ByteIndex: Byte;
     
    221313begin
    222314  with TMemoryStreamEx(Stream) do begin
    223     Data := ((Tag and $f) shl 3) or (Integer(ItemType) and $7);
     315    Data := ((Definition.Tag and $f) shl 3) or (Integer(Definition.WireType) and $7);
    224316    ByteIndex := 0;
    225     while Tag > (1 shl (ByteIndex * 7 + 4)) do begin
     317    while Definition.Tag > (1 shl (ByteIndex * 7 + 4)) do begin
    226318      WriteByte(Data or $80);
    227       Data := (Tag shr (ByteIndex * 7 + 4)) and $7f;
     319      Data := (Definition.Tag shr (ByteIndex * 7 + 4)) and $7f;
    228320      Inc(ByteIndex);
    229321    end;
     
    232324end;
    233325
    234 procedure TPBItem.LoadHeadFromStream(Stream: TStream);
     326function TPBItem.LoadHeadFromStream(Stream: TStream; Definition: TPBDefinition): TPBItemHead;
    235327var
    236328  Data: Byte;
     
    238330begin
    239331  Data := TMemoryStreamEx(Stream).ReadByte;
    240   ItemType := TPBItemType(Data and 3);
    241   Tag := (Data shr 3) and $f;
     332  Result.WireType := TPBWireType(Data and 3);
     333  Result.Tag := (Data shr 3) and $f;
    242334  ByteIndex := 0;
    243335  while Data > $7f do begin
    244336    Data := TMemoryStreamEx(Stream).ReadByte;
    245     Tag := Tag or ((Data and $7f) shl (ByteIndex * 7 + 4));
     337    Result.Tag := Result.Tag or ((Data and $7f) shl (ByteIndex * 7 + 4));
    246338    Inc(ByteIndex);
    247339  end;
    248340end;
    249341
    250 procedure TPBItem.SaveToStream(Stream: TStream);
    251 begin
    252 
    253 end;
    254 
    255 procedure TPBItem.LoadFromStream(Stream: TStream);
     342procedure TPBItem.SaveToStream(Stream: TStream; Definition: TPBDefinition);
     343begin
     344
     345end;
     346
     347procedure TPBItem.LoadFromStream(Stream: TStream; Definition: TPBDefinition);
     348begin
     349
     350end;
     351
     352procedure TPBItem.Clear(Definition: TPBDefinition);
     353begin
     354
     355end;
     356
     357procedure TPBItem.Assign(Source: TPBItem);
    256358begin
    257359
     
    291393{ TPBIntegerItem }
    292394
    293 procedure TPBIntegerItem.SaveToStream(Stream: TStream);
    294 begin
    295   inherited;
    296   SaveHeadToStream(Stream);
     395procedure TPBIntegerItem.SaveToStream(Stream: TStream; Definition: TPBDefinition);
     396begin
     397  inherited;
     398  SaveHeadToStream(Stream, Definition);
    297399  SaveVariantToStream(Stream, Value);
    298400end;
    299401
    300 procedure TPBIntegerItem.LoadFromStream(Stream: TStream);
     402procedure TPBIntegerItem.LoadFromStream(Stream: TStream; Definition: TPBDefinition);
    301403begin
    302404  inherited;
     
    306408constructor TPBIntegerItem.Create;
    307409begin
    308   ItemType := itVariant;
     410end;
     411
     412procedure TPBIntegerItem.Assign(Source: TPBItem);
     413begin
     414  if Source is TPBIntegerItem then
     415    Value := TPBIntegerItem(Source).Value;
    309416end;
    310417
    311418{ TPBStringItem }
    312419
    313 procedure TPBStringItem.SaveToStream(Stream: TStream);
    314 begin
    315   inherited;
    316   SaveHeadToStream(Stream);
     420procedure TPBStringItem.SaveToStream(Stream: TStream; Definition: TPBDefinition);
     421begin
     422  inherited;
     423  SaveHeadToStream(Stream, Definition);
    317424  SaveVariantToStream(Stream, Length(Value));
    318   TMemoryStreamEx(Stream).Write(Value[1], Length(Value));
    319 end;
    320 
    321 procedure TPBStringItem.LoadFromStream(Stream: TStream);
     425  if Length(Value) > 0 then
     426    TMemoryStreamEx(Stream).Write(Value[1], Length(Value));
     427end;
     428
     429procedure TPBStringItem.LoadFromStream(Stream: TStream; Definition: TPBDefinition);
    322430begin
    323431  inherited;
    324432  SetLength(Value, LoadVariantFromStream(Stream));
    325   TMemoryStreamEx(Stream).Read(Value[1], Length(Value));
     433  if Length(Value) > 0 then
     434    TMemoryStreamEx(Stream).Read(Value[1], Length(Value));
    326435end;
    327436
    328437constructor TPBStringItem.Create;
    329438begin
    330   ItemType := itLengthDelimited;
     439end;
     440
     441procedure TPBStringItem.Assign(Source: TPBItem);
     442begin
     443  if Source is TPBStringItem then
     444    Value := TPBStringItem(Source).Value;
     445  inherited Assign(Source);
     446end;
     447
     448{ TPBDefinition }
     449
     450function TPBDefinition.SearchItemByTag(Tag: Integer): Integer;
     451var
     452  I: Integer;
     453begin
     454  I := 0;
     455  while (I < Items.Count) and (TPBDefinition(Items[I]).Tag <> Tag) do Inc(I);
     456  if I < Items.Count then Result := I
     457    else Result := -1;
     458end;
     459
     460function TPBDefinition.GetWireType: TPBWireType;
     461begin
     462  case ItemType of
     463    itInteger: Result := wtVariant;
     464    itFloat: Result := wt64bit;
     465    itDouble: Result := wt32bit;
     466    itString: Result := wtLengthDelimited;
     467    itMessage: Result := wtLengthDelimited;
     468  end;
     469end;
     470
     471constructor TPBDefinition.Create;
     472begin
     473  Items := TList.Create;
     474end;
     475
     476destructor TPBDefinition.Destroy;
     477var
     478  I: Integer;
     479begin
     480  for I := 0 to Items.Count - 1 do
     481    TPBDefinition(Items[I]).Destroy;
     482  Items.Free;
     483  inherited Destroy;
     484end;
     485
     486{ TPBRepeatedItem }
     487
     488procedure TPBRepeatedItem.Clear(Definition: TPBDefinition);
     489var
     490  I: Integer;
     491begin
     492  for I := 0 to Items.Count - 1 do
     493    TPBItem(Items[I]).Free;
     494  inherited;
     495end;
     496
     497procedure TPBRepeatedItem.SaveToStream(Stream: TStream;
     498  Definition: TPBDefinition);
     499var
     500  I: Integer;
     501begin
     502  for I := 0 to Items.Count - 1 do begin
     503    TPBItem(Items[I]).SaveToStream(Stream, Definition);
     504  end;
     505end;
     506
     507procedure TPBRepeatedItem.LoadFromStream(Stream: TStream;
     508  Definition: TPBDefinition);
     509begin
     510  inherited LoadFromStream(Stream, Definition);
     511end;
     512
     513constructor TPBRepeatedItem.Create;
     514begin
     515  Items := TList.Create;
     516end;
     517
     518destructor TPBRepeatedItem.Destroy;
     519var
     520  I: Integer;
     521begin
     522  for I := 0 to Items.Count - 1 do
     523    TPBItem(Items[I]).Free;
     524  Items.Free;
     525  inherited Destroy;
     526end;
     527
     528procedure TPBRepeatedItem.Assign(Source: TPBItem);
     529var
     530  I: Integer;
     531begin
     532  if Source is TPBRepeatedItem then begin
     533    for I := 0 to Items.Count - 1 do
     534      TPBItem(Items[I]).Assign(TPBRepeatedItem(Source).Items[I]);
     535  end;
     536  inherited Assign(Source);
    331537end;
    332538
Note: See TracChangeset for help on using the changeset viewer.