source: ProtocolBuffers/UProtocolBuffers.pas

Last change on this file was 13, checked in by george, 15 years ago
  • Added: Support for Single and Double float data types.
File size: 19.1 KB
Line 
1// Specification: http://code.google.com/intl/cs/apis/protocolbuffers/docs/overview.html
2unit UProtocolBuffers;
3
4{$mode delphi}
5
6interface
7
8uses
9 Classes, SysUtils, Dialogs;
10
11type
12 TPBItemMode = (imRequired, imOptional, imRepeated);
13 TPBWireType = (wtVariant, wt64bit, wtLengthDelimited, wtStartGroup,
14 wtEndGroup, wt32bit);
15 TPBItemType = (itInteger, itString, itMessage, itFloat, itDouble, itBlock);
16
17 TPBEnumeration = class
18 end;
19
20 { TPBDefinition }
21 TPBDefinition = class
22 private
23 function GetWireType: TPBWireType;
24 public
25 Name: string;
26 Tag: Integer;
27 ItemType: TPBItemType;
28 ItemMode: TPBItemMode;
29 Items: TList; // TList<TPBDefinition>
30 DefaultString: string;
31 DefaultInteger: Integer;
32 DefaultDouble: Double;
33 constructor Create;
34 destructor Destroy; override;
35 function SearchItemByTag(Tag: Integer): Integer;
36 property WireType: TPBWireType read GetWireType;
37 end;
38
39 { TPBItemHead }
40 TPBItemHead = record
41 Tag: Integer;
42 WireType: TPBWireType;
43 end;
44
45 { TPBItem }
46 TPBItem = class
47 procedure SaveVariantToStream(Stream: TStream; Value: Integer);
48 function LoadVariantFromStream(Stream: TStream): Integer;
49 procedure SaveFixed32ToStream(Stream: TStream; Value: Cardinal);
50 function LoadFixed32FromStream(Stream: TStream): Cardinal;
51 procedure SaveFixed64ToStream(Stream: TStream; Value: QWord);
52 function LoadFixed64FromStream(Stream: TStream): QWord;
53 procedure SaveLengthDelimitedToStream(Stream: TStream; Block: TStream);
54 procedure LoadLengthDelimitedFromStream(Stream: TStream; Block: TStream);
55 procedure SaveHeadToStream(Stream: TStream; Definition: TPBDefinition);
56 function LoadHeadFromStream(Stream: TStream; Definition: TPBDefinition): TPBItemHead;
57 procedure SaveToStream(Stream: TStream; Definition: TPBDefinition); virtual;
58 procedure LoadFromStream(Stream: TStream; Definition: TPBDefinition); virtual;
59 procedure Clear(Definition: TPBDefinition); virtual;
60 procedure Assign(Source: TPBItem); virtual;
61 end;
62
63 TPBMessageItem = class;
64
65 { TPBStringItem }
66 TPBStringItem = class(TPBItem)
67 Value: string;
68 procedure SaveToStream(Stream: TStream; Definition: TPBDefinition); override;
69 procedure LoadFromStream(Stream: TStream; Definition: TPBDefinition); override;
70 constructor Create;
71 procedure Assign(Source: TPBItem); override;
72 end;
73
74 { TPBIntegerItem }
75 TPBIntegerItem = class(TPBItem)
76 Value: Integer;
77 procedure SaveToStream(Stream: TStream; Definition: TPBDefinition); override;
78 procedure LoadFromStream(Stream: TStream; Definition: TPBDefinition); override;
79 constructor Create;
80 procedure Assign(Source: TPBItem); override;
81 end;
82
83 { TPBFloatItem }
84 TPBFloatItem = class(TPBItem)
85 Value: Single;
86 procedure SaveToStream(Stream: TStream; Definition: TPBDefinition); override;
87 procedure LoadFromStream(Stream: TStream; Definition: TPBDefinition); override;
88 constructor Create;
89 procedure Assign(Source: TPBItem); override;
90 end;
91
92 { TPBDoubleItem }
93 TPBDoubleItem = class(TPBItem)
94 Value: Double;
95 procedure SaveToStream(Stream: TStream; Definition: TPBDefinition); override;
96 procedure LoadFromStream(Stream: TStream; Definition: TPBDefinition); override;
97 constructor Create;
98 procedure Assign(Source: TPBItem); override;
99 end;
100
101 { TPBMessageItem }
102 TPBMessageItem = class(TPBItem)
103 GenerateHead: Boolean;
104 Items: TList; // TList<TList<TPBItem>>;
105 procedure Clear(Definition: TPBDefinition); override;
106 procedure SaveToStream(Stream: TStream; Definition: TPBDefinition); override;
107 procedure LoadFromStream(Stream: TStream; Definition: TPBDefinition); override;
108 constructor Create;
109 destructor Destroy; override;
110 procedure Assign(Source: TPBItem); override;
111 end;
112
113 { TPBRepeatedItem }
114
115 TPBRepeatedItem = class(TPBItem)
116 Items: TList;
117 procedure Clear(Definition: TPBDefinition); override;
118 procedure SaveToStream(Stream: TStream; Definition: TPBDefinition); override;
119 procedure LoadFromStream(Stream: TStream; Definition: TPBDefinition); override;
120 constructor Create;
121 destructor Destroy; override;
122 procedure Assign(Source: TPBItem); override;
123 end;
124
125 { TProtocolBuffer }
126 TProtocolBuffer = class
127 Definition: TPBDefinition;
128 BaseMessage: TPBMessageItem;
129 procedure LoadFromStream(Stream: TStream);
130 procedure SaveToStream(Stream: TStream);
131 procedure LoadFromProto(Source: TStringList);
132 constructor Create;
133 destructor Destroy; override;
134 end;
135
136implementation
137
138uses
139 UMemoryStreamEx;
140
141{ TProtocolBuffer }
142
143procedure TProtocolBuffer.LoadFromStream(Stream: TStream);
144begin
145 BaseMessage.GenerateHead := False;
146 BaseMessage.LoadFromStream(Stream, Definition);
147end;
148
149procedure TProtocolBuffer.SaveToStream(Stream: TStream);
150begin
151 BaseMessage.GenerateHead := False;
152 BaseMessage.SaveToStream(Stream, Definition);
153end;
154
155procedure TProtocolBuffer.LoadFromProto(Source: TStringList);
156begin
157
158end;
159
160constructor TProtocolBuffer.Create;
161begin
162 BaseMessage := TPBMessageItem.Create;
163 Definition := TPBDefinition.Create;
164 Definition.ItemType := itMessage;
165end;
166
167destructor TProtocolBuffer.Destroy;
168begin
169 Definition.Destroy;
170 BaseMessage.Free;
171 inherited Destroy;
172end;
173
174{ TPBMessageItem }
175
176procedure TPBMessageItem.Clear(Definition: TPBDefinition);
177var
178 I: Integer;
179 Q: Integer;
180begin
181 for I := 0 to Items.Count - 1 do
182 TPBItem(Items[I]).Clear(Definition);
183 Items.Clear;
184 Items.Count := Definition.Items.Count;
185 for I := 0 to Items.Count - 1 do begin
186 if TPBDefinition(Definition.Items[I]).ItemMode = imRepeated then
187 Items[I] := TPBRepeatedItem.Create
188 else
189 if TPBDefinition(Definition.Items[I]).ItemType = itInteger then begin
190 Items[I] := TPBIntegerItem.Create;
191 TPBIntegerItem(Items[I]).Value := TPBDefinition(Definition.Items[I]).DefaultInteger;
192 end else
193 if TPBDefinition(Definition.Items[I]).ItemType = itFloat then begin
194 Items[I] := TPBFloatItem.Create;
195 TPBFloatItem(Items[I]).Value := TPBDefinition(Definition.Items[I]).DefaultDouble;
196 end else
197 if TPBDefinition(Definition.Items[I]).ItemType = itDouble then begin
198 Items[I] := TPBDoubleItem.Create;
199 TPBDoubleItem(Items[I]).Value := TPBDefinition(Definition.Items[I]).DefaultDouble;
200 end else
201 if TPBDefinition(Definition.Items[I]).ItemType = itString then begin
202 Items[I] := TPBStringItem.Create;
203 TPBStringItem(Items[I]).Value := TPBDefinition(Definition.Items[I]).DefaultString;
204 end else
205 if TPBDefinition(Definition.Items[I]).ItemType = itMessage then begin
206 Items[I] := TPBMessageItem.Create;
207 TPBMessageItem(Items[I]).Clear(Definition.Items[I]);
208 end;
209 end;
210end;
211
212procedure TPBMessageItem.SaveToStream(Stream: TStream; Definition: TPBDefinition);
213var
214 I: Integer;
215 Q: Integer;
216 TempStream: TMemoryStream;
217begin
218 inherited;
219 // Generate message content to temporary stream
220 TempStream := TMemoryStream.Create;
221 if Items.Count <> Definition.Items.Count then
222 raise Exception.Create('Definition and value items count mismatch.');
223 for I := 0 to Definition.Items.Count - 1 do
224 TPBItem(Items[I]).SaveToStream(TempStream, Definition.Items[I]);
225
226 // If head is used than write lenght-delimited head type with block byte length
227 if GenerateHead then begin
228 SaveHeadToStream(Stream, Definition);
229 SaveVariantToStream(Stream, TempStream.Size);
230 end;
231 TempStream.Position := 0;
232 TempStream.SaveToStream(Stream);
233 TempStream.Free;
234end;
235
236procedure TPBMessageItem.LoadFromStream(Stream: TStream; Definition: TPBDefinition);
237var
238 I: Integer;
239 TempItem: TPBItem;
240 ItemIndex: Integer;
241 EndIndex: Integer;
242 TempStream: TMemoryStream;
243 ItemHead: TPBItemHead;
244 NewItem: TPBItem;
245begin
246 inherited;
247 TempStream := TMemoryStream.Create;
248
249 if GenerateHead then begin
250 I := LoadVariantFromStream(Stream);
251 EndIndex := Stream.Position + I;
252 end else EndIndex := Stream.Size;
253
254 TempItem := TPBItem.Create;
255 Clear(Definition);
256 while Stream.Position < EndIndex do begin
257 ItemHead := TempItem.LoadHeadFromStream(Stream, Definition);
258 ItemIndex := Definition.SearchItemByTag(ItemHead.Tag);
259 if ItemIndex <> -1 then
260 with TPBDefinition(Definition.Items[ItemIndex]) do begin
261 if WireType <> ItemHead.WireType then
262 raise Exception.Create('Bad type for item "' + TPBDefinition(Definition.Items[ItemIndex]).Name +
263 '" with tag ' + IntToStr(ItemHead.Tag));
264 if ItemType = itInteger then begin
265 NewItem := TPBIntegerItem.Create;
266 TPBIntegerItem(NewItem).LoadFromStream(Stream, Definition.Items[ItemIndex]);
267 end else
268 if TPBDefinition(Definition.Items[ItemIndex]).ItemType = itString then begin
269 NewItem := TPBStringItem.Create;
270 TPBStringItem(NewItem).LoadFromStream(Stream, Definition.Items[ItemIndex])
271 end else
272 if TPBDefinition(Definition.Items[ItemIndex]).ItemType = itFloat then begin
273 NewItem := TPBFloatItem.Create;
274 TPBFloatItem(NewItem).LoadFromStream(Stream, Definition.Items[ItemIndex])
275 end else
276 if TPBDefinition(Definition.Items[ItemIndex]).ItemType = itDouble then begin
277 NewItem := TPBDoubleItem.Create;
278 TPBDoubleItem(NewItem).LoadFromStream(Stream, Definition.Items[ItemIndex])
279 end else
280 if TPBDefinition(Definition.Items[ItemIndex]).ItemType = itMessage then begin
281 NewItem := TPBMessageItem.Create;
282 TPBMessageItem(NewItem).LoadFromStream(Stream, Definition.Items[ItemIndex]);
283 end;
284
285 if ItemMode = imRepeated then begin
286 TPBRepeatedItem(Self.Items[ItemIndex]).Items.Add(NewItem);
287 end else begin
288 TPBItem(Self.Items[ItemIndex]).Assign(NewItem);
289 NewItem.Free;
290 end;
291 end else begin
292 // Skip item data
293 if ItemHead.WireType = wtVariant then
294 TempItem.LoadVariantFromStream(Stream)
295 else if ItemHead.WireType = wt32bit then
296 TempItem.LoadFixed32FromStream(Stream)
297 else if ItemHead.WireType = wt64bit then
298 TempItem.LoadFixed64FromStream(Stream)
299 else if ItemHead.WireType = wtLengthDelimited then
300 TempItem.LoadLengthDelimitedFromStream(Stream, TempStream);
301 end;
302 end;
303 TempStream.Free;
304end;
305
306constructor TPBMessageItem.Create;
307begin
308 Items := TList.Create;
309 GenerateHead := True;
310end;
311
312destructor TPBMessageItem.Destroy;
313var
314 I: Integer;
315begin
316 for I := 0 to Items.Count - 1 do
317 TPBItem(Items[I]).Free;
318 Items.Free;
319 inherited Destroy;
320end;
321
322procedure TPBMessageItem.Assign(Source: TPBItem);
323var
324 I: Integer;
325begin
326 if Source is TPBMessageItem then begin
327 GenerateHead := TPBMessageItem(Source).GenerateHead;
328 for I := 0 to Items.Count - 1 do
329 TPBItem(Items[I]).Assign(TPBMessageItem(Source).Items[I]);
330 end;
331end;
332
333{ TPBItem }
334
335procedure TPBItem.SaveVariantToStream(Stream: TStream; Value: Integer);
336var
337 ByteIndex: Byte;
338 Data: Byte;
339begin
340 with TMemoryStreamEx(Stream) do begin
341 Data := Value and $7f;
342 ByteIndex := 1;
343 while Value > (1 shl (ByteIndex * 7)) do begin
344 WriteByte(Data or $80);
345 Data := (Value shr (ByteIndex * 7)) and $7f;
346 Inc(ByteIndex);
347 end;
348 WriteByte(Data);
349 end
350end;
351
352procedure TPBItem.SaveHeadToStream(Stream: TStream; Definition: TPBDefinition);
353var
354 ByteIndex: Byte;
355 Data: Byte;
356begin
357 with TMemoryStreamEx(Stream) do begin
358 Data := ((Definition.Tag and $f) shl 3) or (Integer(Definition.WireType) and $7);
359 ByteIndex := 0;
360 while Definition.Tag > (1 shl (ByteIndex * 7 + 4)) do begin
361 WriteByte(Data or $80);
362 Data := (Definition.Tag shr (ByteIndex * 7 + 4)) and $7f;
363 Inc(ByteIndex);
364 end;
365 WriteByte(Data);
366 end
367end;
368
369function TPBItem.LoadHeadFromStream(Stream: TStream; Definition: TPBDefinition): TPBItemHead;
370var
371 Data: Byte;
372 ByteIndex: Byte;
373begin
374 Data := TMemoryStreamEx(Stream).ReadByte;
375 Result.WireType := TPBWireType(Data and 3);
376 Result.Tag := (Data shr 3) and $f;
377 ByteIndex := 0;
378 while Data > $7f do begin
379 Data := TMemoryStreamEx(Stream).ReadByte;
380 Result.Tag := Result.Tag or ((Data and $7f) shl (ByteIndex * 7 + 4));
381 Inc(ByteIndex);
382 end;
383end;
384
385procedure TPBItem.SaveToStream(Stream: TStream; Definition: TPBDefinition);
386begin
387
388end;
389
390procedure TPBItem.LoadFromStream(Stream: TStream; Definition: TPBDefinition);
391begin
392
393end;
394
395procedure TPBItem.Clear(Definition: TPBDefinition);
396begin
397
398end;
399
400procedure TPBItem.Assign(Source: TPBItem);
401begin
402
403end;
404
405function TPBItem.LoadVariantFromStream(Stream: TStream): Integer;
406var
407 Data: Byte;
408 ByteIndex: Byte;
409begin
410 Data := TMemoryStreamEx(Stream).ReadByte;
411 Result := Data and $7f;
412 ByteIndex := 1;
413 while Data > $7f do begin
414 Data := TMemoryStreamEx(Stream).ReadByte;
415 Result := Result or ((Data and $7f) shl (ByteIndex * 7));
416 Inc(ByteIndex);
417 end;
418end;
419
420procedure TPBItem.SaveFixed32ToStream(Stream: TStream; Value: Cardinal);
421begin
422 TMemoryStreamEx(Stream).WriteCardinal(Value);
423end;
424
425function TPBItem.LoadFixed32FromStream(Stream: TStream): Cardinal;
426begin
427 Result := TMemoryStreamEx(Stream).ReadCardinal;
428end;
429
430procedure TPBItem.SaveFixed64ToStream(Stream: TStream; Value: QWord);
431begin
432 TMemoryStreamEx(Stream).WriteInt64(Value);
433end;
434
435function TPBItem.LoadFixed64FromStream(Stream: TStream): QWord;
436begin
437 Result := TMemoryStreamEx(Stream).ReadInt64;
438end;
439
440procedure TPBItem.SaveLengthDelimitedToStream(Stream: TStream; Block: TStream);
441begin
442 SaveVariantToStream(Stream, Block.Size);
443 Block.Position := 0;
444 TMemoryStreamEx(Block).ReadStream(Stream, Block.Size);
445end;
446
447procedure TPBItem.LoadLengthDelimitedFromStream(Stream: TStream; Block: TStream
448 );
449var
450 Size: Integer;
451begin
452 Size := LoadVariantFromStream(Stream);
453 TMemoryStreamEx(Stream).ReadStream(Block, Size);
454end;
455
456{ TPBIntegerItem }
457
458procedure TPBIntegerItem.SaveToStream(Stream: TStream; Definition: TPBDefinition);
459begin
460 inherited;
461 SaveHeadToStream(Stream, Definition);
462 SaveVariantToStream(Stream, Value);
463end;
464
465procedure TPBIntegerItem.LoadFromStream(Stream: TStream; Definition: TPBDefinition);
466begin
467 inherited;
468 Value := LoadVariantFromStream(Stream);
469end;
470
471constructor TPBIntegerItem.Create;
472begin
473end;
474
475procedure TPBIntegerItem.Assign(Source: TPBItem);
476begin
477 if Source is TPBIntegerItem then
478 Value := TPBIntegerItem(Source).Value;
479end;
480
481{ TPBStringItem }
482
483procedure TPBStringItem.SaveToStream(Stream: TStream; Definition: TPBDefinition);
484begin
485 inherited;
486 SaveHeadToStream(Stream, Definition);
487 SaveVariantToStream(Stream, Length(Value));
488 if Length(Value) > 0 then
489 TMemoryStreamEx(Stream).Write(Value[1], Length(Value));
490end;
491
492procedure TPBStringItem.LoadFromStream(Stream: TStream; Definition: TPBDefinition);
493begin
494 inherited;
495 SetLength(Value, LoadVariantFromStream(Stream));
496 if Length(Value) > 0 then
497 TMemoryStreamEx(Stream).Read(Value[1], Length(Value));
498end;
499
500constructor TPBStringItem.Create;
501begin
502end;
503
504procedure TPBStringItem.Assign(Source: TPBItem);
505begin
506 if Source is TPBStringItem then
507 Value := TPBStringItem(Source).Value;
508 inherited Assign(Source);
509end;
510
511{ TPBDefinition }
512
513function TPBDefinition.SearchItemByTag(Tag: Integer): Integer;
514var
515 I: Integer;
516begin
517 I := 0;
518 while (I < Items.Count) and (TPBDefinition(Items[I]).Tag <> Tag) do Inc(I);
519 if I < Items.Count then Result := I
520 else Result := -1;
521end;
522
523function TPBDefinition.GetWireType: TPBWireType;
524begin
525 case ItemType of
526 itInteger: Result := wtVariant;
527 itFloat: Result := wt32bit;
528 itDouble: Result := wt64bit;
529 itString: Result := wtLengthDelimited;
530 itMessage: Result := wtLengthDelimited;
531 end;
532end;
533
534constructor TPBDefinition.Create;
535begin
536 Items := TList.Create;
537end;
538
539destructor TPBDefinition.Destroy;
540var
541 I: Integer;
542begin
543 for I := 0 to Items.Count - 1 do
544 TPBDefinition(Items[I]).Destroy;
545 Items.Free;
546 inherited Destroy;
547end;
548
549{ TPBRepeatedItem }
550
551procedure TPBRepeatedItem.Clear(Definition: TPBDefinition);
552var
553 I: Integer;
554begin
555 for I := 0 to Items.Count - 1 do begin
556 TPBItem(Items[I]).Free;
557 if Definition.ItemType = itInteger then begin
558 Items[I] := TPBIntegerItem.Create;
559 TPBIntegerItem(Items[I]).Value := Definition.DefaultInteger;
560 end else
561 if Definition.ItemType = itFloat then begin
562 Items[I] := TPBFloatItem.Create;
563 TPBFloatItem(Items[I]).Value := Definition.DefaultDouble;
564 end else
565 if Definition.ItemType = itDouble then begin
566 Items[I] := TPBDoubleItem.Create;
567 TPBDoubleItem(Items[I]).Value := Definition.DefaultDouble;
568 end else
569 if Definition.ItemType = itString then begin
570 Items[I] := TPBStringItem.Create;
571 TPBStringItem(Items[I]).Value := Definition.DefaultString;
572 end else
573 if Definition.ItemType = itMessage then begin
574 Items[I] := TPBMessageItem.Create;
575 TPBMessageItem(Items[I]).Clear(Definition);
576 end;
577 end;
578 inherited;
579end;
580
581procedure TPBRepeatedItem.SaveToStream(Stream: TStream;
582 Definition: TPBDefinition);
583var
584 I: Integer;
585begin
586 for I := 0 to Items.Count - 1 do begin
587 TPBItem(Items[I]).SaveToStream(Stream, Definition);
588 end;
589end;
590
591procedure TPBRepeatedItem.LoadFromStream(Stream: TStream;
592 Definition: TPBDefinition);
593begin
594 inherited LoadFromStream(Stream, Definition);
595end;
596
597constructor TPBRepeatedItem.Create;
598begin
599 Items := TList.Create;
600end;
601
602destructor TPBRepeatedItem.Destroy;
603var
604 I: Integer;
605begin
606 for I := 0 to Items.Count - 1 do
607 TPBItem(Items[I]).Free;
608 Items.Free;
609 inherited Destroy;
610end;
611
612procedure TPBRepeatedItem.Assign(Source: TPBItem);
613var
614 I: Integer;
615begin
616 if Source is TPBRepeatedItem then begin
617 for I := 0 to Items.Count - 1 do
618 TPBItem(Items[I]).Assign(TPBRepeatedItem(Source).Items[I]);
619 end;
620 inherited Assign(Source);
621end;
622
623{ TPBFloatItem }
624
625procedure TPBFloatItem.SaveToStream(Stream: TStream; Definition: TPBDefinition
626 );
627begin
628 SaveHeadToStream(Stream, Definition);
629 SaveFixed32ToStream(Stream, Cardinal(Value));
630end;
631
632procedure TPBFloatItem.LoadFromStream(Stream: TStream; Definition: TPBDefinition
633 );
634begin
635 inherited;
636 Value := Single(LoadFixed32FromStream(Stream));
637end;
638
639constructor TPBFloatItem.Create;
640begin
641
642end;
643
644procedure TPBFloatItem.Assign(Source: TPBItem);
645begin
646 if Source is TPBFloatItem then
647 Value := TPBFloatItem(Source).Value;
648end;
649
650{ TPBDoubleItem }
651
652procedure TPBDoubleItem.SaveToStream(Stream: TStream; Definition: TPBDefinition
653 );
654begin
655 SaveHeadToStream(Stream, Definition);
656 SaveFixed64ToStream(Stream, QWord(Value));
657end;
658
659procedure TPBDoubleItem.LoadFromStream(Stream: TStream;
660 Definition: TPBDefinition);
661begin
662 inherited;
663 Value := Double(LoadFixed64FromStream(Stream));
664end;
665
666constructor TPBDoubleItem.Create;
667begin
668
669end;
670
671procedure TPBDoubleItem.Assign(Source: TPBItem);
672begin
673 if Source is TPBDoubleItem then
674 Value := TPBDoubleItem(Source).Value;
675end;
676
677end.
678
Note: See TracBrowser for help on using the repository browser.