1/* Part of SWI-Prolog 2 3 Author: Jeffrey Rosenwald, extended by Peter Ludemann 4 E-mail: jeffrose@acm.org, peter.ludemann@gmail.com 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2010-2013, Jeffrey Rosenwald 7 All rights reserved. 8 9 Redistribution and use in source and binary forms, with or without 10 modification, are permitted provided that the following conditions 11 are met: 12 13 1. Redistributions of source code must retain the above copyright 14 notice, this list of conditions and the following disclaimer. 15 16 2. Redistributions in binary form must reproduce the above copyright 17 notice, this list of conditions and the following disclaimer in 18 the documentation and/or other materials provided with the 19 distribution. 20 21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 POSSIBILITY OF SUCH DAMAGE. 33*/ 34 35:- module(protobufs, 36 [ protobuf_message/2, % ?Template ?Codes 37 protobuf_message/3, % ?Template ?Codes ?Rest 38 protobuf_parse_from_codes/3, % +WireCodes, +MessageType, -Term 39 protobuf_serialize_to_codes/3, % +Term, +MessageType, -WireCodes 40 protobuf_field_is_map/2, % +MessageType, +FieldName 41 protobuf_map_pairs/3 % ?ProtobufTermList, ?DictTag, ?Pairs 42 43 % TODO: Restore the following to the public interface, if 44 % someone needs them. For now, the tests directly specify 45 % them using, e.g. protobufs:uint32_codes(..., ...). 46 % 47 % protobuf_segment_message/2, % ?Segments ?Codes 48 % protobuf_segment_convert/2, % +Form1 ?Form2 49 % uint32_codes/2, 50 % int32_codes/2, 51 % float32_codes/2, 52 % uint64_codes/2, 53 % int64_codes/2, 54 % float64_codes/2, 55 % int64_zigzag/2, 56 % uint32_int32/2, 57 % uint64_int64/2, 58 % uint32_codes_when/2, 59 % int32_codes_when/2, % TODO: unused 60 % float32_codes_when/2, 61 % uint64_codes_when/2, 62 % int64_codes_when/2, % TODO: unused 63 % float64_codes_when/2, 64 % int64_zigzag_when/2, 65 % uint32_int32_when/2, 66 % uint64_int64_when/2, 67 % int64_float64_when/2, 68 % int32_float32_when/2, 69 % protobuf_var_int//1, 70 % protobuf_tag_type//2 71 ]). 72 73:- use_module(library(apply_macros)). % autoload(library(apply), [maplist/3, foldl/4]). 74:- autoload(library(error), [must_be/2, domain_error/2, existence_error/2]). 75:- autoload(library(lists), [append/3]). 76:- autoload(library(utf8), [utf8_codes//1]). 77:- autoload(library(dif), [dif/2]). 78:- autoload(library(dcg/high_order), [sequence//2]). 79:- autoload(library(when), [when/2]). 80:- autoload(library(debug), [assertion/1]). % TODO: remove 81 82:- set_prolog_flag(optimise, true). % For arithmetic using is/2.
162:- use_foreign_library(foreign(protobufs)).
MessageType
), creating a Prolog term.
Protoc
must have been run (with the --swipl_out=
option and the resulting
top-level _pb.pl
file loaded. For more details, see the "protoc" section of the
overview documentation.
Fails if the message can't be parsed or if the appropriate meta-data from protoc
hasn't been loaded.
All fields that are omitted from the WireCodes
are set to their
default values (typically the empty string or 0, depending on the
type; or []
for repeated groups). There is no way of testing
whether a value was specified in WireCodes
or given its default
value (that is, there is no equivalent of the Python
implementation's =HasField`). Optional embedded messages and groups
do not have any default value -- you must check their existence by
using get_dict/3 or similar. If a field is part of a "oneof" set,
then none of the other fields is set. You can determine which field
had a value by using get_dict/3.
219protobuf_parse_from_codes(WireCodes, MessageType0, Term) :- 220 verify_version, 221 must_be(ground, MessageType0), 222 ( proto_meta_normalize(MessageType0, MessageType) 223 -> true 224 ; existence_error(protobuf_package, MessageType0) 225 ), 226 protobuf_segment_message(Segments, WireCodes), 227 % protobuf_segment_message/2 can leave choicepoints, backtracking 228 % through all the possibilities would have combinatoric explosion; 229 % instead use segment_to_term/3 call protobuf_segment_convert/2 to 230 % change segments that were guessed incorrectly. 231 !, 232 maplist(segment_to_term(MessageType), Segments, MsgFields), 233 !, % TODO: remove 234 combine_fields(MsgFields, MessageType{}, Term), 235 !. % TODO: remove? - but proto_meta might have left choicepoints if loaded twice 236 237verify_version :- 238 ( protoc_gen_swipl_version(Module, Version), 239 Version @< [0,9,1] % This must be sync-ed with changes to protoc-gen-swipl 240 -> throw(error(version_error(Module-Version), _)) 241 ; true 242 ).
MessageType
).
Protoc
must have been run (with the --swipl_out=
option and the resulting
top-level _pb.pl
file loaded. For more details, see the "protoc" section of the
overview documentation.
Fails if the term isn't of an appropriate form or if the appropriate
meta-data from protoc
hasn't been loaded, or if a field name is incorrect
(and therefore nothing in the meta-data matches it).
277protobuf_serialize_to_codes(Term, MessageType0, WireCodes) :- 278 verify_version, 279 must_be(ground, MessageType0), 280 ( proto_meta_normalize(MessageType0, MessageType) 281 -> true 282 ; existence_error(protobuf_package, MessageType0) 283 ), 284 term_to_segments(Term, MessageType, Segments), 285 !, % TODO: remove 286 protobuf_segment_message(Segments, WireCodes), 287 !. % TODO: remove? - but proto_meta might have left choicepoints if loaded twice 288 289% 290% Map wire type (atom) to its encoding (an int) 291% 292wire_type(varint, 0). % for int32, int64, uint32, uint64, sint32, sint64, bool, enum 293wire_type(fixed64, 1). % for fixed64, sfixed64, double 294wire_type(length_delimited, 2). % for string, bytes, embedded messages, packed repeated fields 295wire_type(start_group, 3). % for groups (deprecated) 296wire_type(end_group, 4). % for groups (deprecated) 297wire_type(fixed32, 5). % for fixed32, sfixed32, float 298 299% 300% basic wire-type processing handled by C-support code in DCG-form 301% 302 303fixed_uint32(X, [A0, A1, A2, A3 | Rest], Rest) :- 304 uint32_codes_when(X, [A0, A1, A2, A3]). 305/* equivalent to: 306fixed_uint32_(X) --> 307 [ A0,A1,A2,A3 ], 308 { uint32_codes_when(X, [A0,A1,A2,A3]) }. 309*/ 310 311fixed_uint64(X, [A0, A1, A2, A3, A4, A5, A6, A7 | Rest], Rest) :- 312 uint64_codes_when(X, [A0, A1, A2, A3, A4, A5, A6, A7]). 313 314fixed_float64(X, [A0, A1, A2, A3, A4, A5, A6, A7 | Rest], Rest) :- 315 float64_codes_when(X, [A0, A1, A2, A3, A4, A5, A6, A7]). 316 317fixed_float32(X, [A0, A1, A2, A3 | Rest], Rest) :- 318 float32_codes_when(X, [A0, A1, A2, A3]). 319 320% 321% Start of the DCG 322% 323 324code_string(N, Codes, Rest, Rest1) :- 325 length(Codes, N), 326 append(Codes, Rest1, Rest), 327 !. 328/* 329code_string(N, Codes) --> 330 { length(Codes, N) }, 331 Codes, !. 332*/ 333 334% 335% deal with Google's method of packing unsigned integers in variable 336% length, modulo 128 strings. 337% 338% protobuf_var_int//1 and protobuf_tag_type//2 productions were rewritten in straight 339% Prolog for speed's sake. 340%
A
is negative.
This is a low-level predicate; normally, you should use
template_message/2 and the appropriate template term.
e.g. phrase(protobuf_var_int(300), S)
=> S = [172,2]
phrase(protobuf_var_int(A), [172,2])
-> A = 300350protobuf_var_int(A, [A | Rest], Rest) :- 351 A < 128, 352 !. 353protobuf_var_int(X, [A | Rest], Rest1) :- 354 nonvar(X), 355 X1 is X >> 7, 356 A is 128 + (X /\ 0x7f), 357 protobuf_var_int(X1, Rest, Rest1), 358 !. 359protobuf_var_int(X, [A | Rest], Rest1) :- 360 protobuf_var_int(X1, Rest, Rest1), 361 X is (X1 << 7) + A - 128, 362 !.
371protobuf_tag_type(Tag, WireType, Rest, Rest1) :- 372 nonvar(Tag), nonvar(WireType), 373 wire_type(WireType, WireTypeEncoding), 374 A is Tag << 3 \/ WireTypeEncoding, 375 protobuf_var_int(A, Rest, Rest1), 376 !. 377protobuf_tag_type(Tag, WireType, Rest, Rest1) :- 378 protobuf_var_int(A, Rest, Rest1), 379 WireTypeEncoding is A /\ 0x07, 380 wire_type(WireType, WireTypeEncoding), 381 Tag is A >> 3.
389prolog_type(Tag, double) --> protobuf_tag_type(Tag, fixed64). 390prolog_type(Tag, integer64) --> protobuf_tag_type(Tag, fixed64). 391prolog_type(Tag, unsigned64) --> protobuf_tag_type(Tag, fixed64). 392prolog_type(Tag, float) --> protobuf_tag_type(Tag, fixed32). 393prolog_type(Tag, integer32) --> protobuf_tag_type(Tag, fixed32). 394prolog_type(Tag, unsigned32) --> protobuf_tag_type(Tag, fixed32). 395prolog_type(Tag, integer) --> protobuf_tag_type(Tag, varint). 396prolog_type(Tag, unsigned) --> protobuf_tag_type(Tag, varint). 397prolog_type(Tag, signed32) --> protobuf_tag_type(Tag, varint). 398prolog_type(Tag, signed64) --> protobuf_tag_type(Tag, varint). 399prolog_type(Tag, boolean) --> protobuf_tag_type(Tag, varint). 400prolog_type(Tag, enum) --> protobuf_tag_type(Tag, varint). 401prolog_type(Tag, atom) --> protobuf_tag_type(Tag, length_delimited). 402prolog_type(Tag, codes) --> protobuf_tag_type(Tag, length_delimited). 403prolog_type(Tag, utf8_codes) --> protobuf_tag_type(Tag, length_delimited). 404prolog_type(Tag, string) --> protobuf_tag_type(Tag, length_delimited). 405prolog_type(Tag, embedded) --> protobuf_tag_type(Tag, length_delimited). 406prolog_type(Tag, packed) --> protobuf_tag_type(Tag, length_delimited). 407 408% 409% The protobuf-2.1.0 grammar allows negative values in enums. 410% But they are encoded as unsigned in the golden message. 411% As such, they use the maximum length of a varint, so it is 412% recommended that they be non-negative. However, that's controlled 413% by the =|.proto|= file. 414% 415:- meta_predicate enumeration( , , ). 416 417enumeration(Type) --> 418 { call(Type, Value) }, 419 payload(signed64, Value).
Payload
, according to PrologType
TODO
: payload//2 "mode" is sometimes module-sensitive, sometimes not.
payload(enum, A)
// has A as a callable
all other uses of payload//2, the 2nd arg is not callable.
vector_demo.pl
, which defines commands/2)429payload(enum, Payload) --> 430 enumeration(Payload). 431payload(double, Payload) --> 432 fixed_float64(Payload). 433payload(integer64, Payload) --> 434 { uint64_int64_when(Payload0, Payload) }, 435 fixed_uint64(Payload0). 436payload(unsigned64, Payload) --> 437 fixed_uint64(Payload). 438payload(float, Payload) --> 439 fixed_float32(Payload). 440payload(integer32, Payload) --> 441 { uint32_int32_when(Payload0, Payload) }, 442 fixed_uint32(Payload0). 443payload(unsigned32, Payload) --> 444 fixed_uint32(Payload). 445payload(integer, Payload) --> 446 { nonvar(Payload), int64_zigzag(Payload, X) }, % TODO: int64_zigzag_when/2 447 !, 448 protobuf_var_int(X). 449payload(integer, Payload) --> 450 protobuf_var_int(X), 451 { int64_zigzag(Payload, X) }. % TODO: int64_zigzag_when/2 452payload(unsigned, Payload) --> 453 protobuf_var_int(Payload), 454 { Payload >= 0 }. 455payload(signed32, Payload) --> % signed32 is not defined by prolog_type//2 456 % for wire-stream compatibility reasons. 457 % signed32 ought to write 5 bytes for negative numbers, but both 458 % the C++ and Python implementations write 10 bytes. For 459 % wire-stream compatibility, we follow C++ and Python, even though 460 % protoc decode appears to work just fine with 5 bytes -- 461 % presumably there are some issues with decoding 5 bytes and 462 % getting the sign extension correct with some 32/64-bit integer 463 % models. See CodedOutputStream::WriteVarint32SignExtended(int32 464 % value) in google/protobuf/io/coded_stream.h. 465 payload(signed64, Payload). 466payload(signed64, Payload) --> 467 % protobuf_var_int//1 cannot handle negative numbers (note that 468 % zig-zag encoding always results in a positive number), so 469 % compute the 64-bit 2s complement, which is what is produced 470 % form C++ and Python. 471 { nonvar(Payload) }, 472 !, 473 { uint64_int64(X, Payload) }, % TODO: uint64_int64_when 474 protobuf_var_int(X). 475payload(signed64, Payload) --> 476 % See comment in previous clause about negative numbers. 477 protobuf_var_int(X), 478 { uint64_int64(X, Payload) }. % TODO: uint64_int64_when 479payload(codes, Payload) --> 480 { nonvar(Payload), 481 !, 482 length(Payload, Len) 483 }, 484 protobuf_var_int(Len), 485 code_string(Len, Payload). 486payload(codes, Payload) --> 487 protobuf_var_int(Len), 488 code_string(Len, Payload). 489payload(utf8_codes, Payload) --> 490 { nonvar(Payload), % TODO: use freeze/2 or when/2 491 !, 492 phrase(utf8_codes(Payload), B) 493 }, 494 payload(codes, B). 495payload(utf8_codes, Payload) --> 496 payload(codes, B), 497 { phrase(utf8_codes(Payload), B) }. 498payload(atom, Payload) --> 499 { nonvar(Payload), 500 atom_codes(Payload, Codes) 501 }, 502 payload(utf8_codes, Codes), 503 !. 504payload(atom, Payload) --> 505 payload(utf8_codes, Codes), 506 { atom_codes(Payload, Codes) }. 507payload(boolean, true) --> 508 payload(unsigned, 1). 509payload(boolean, false) --> 510 payload(unsigned, 0). 511payload(string, Payload) --> 512 { nonvar(Payload) 513 -> string_codes(Payload, Codes) 514 ; true 515 }, 516 % string_codes produces a list of unicode, not bytes 517 payload(utf8_codes, Codes), 518 { string_codes(Payload, Codes) }. 519payload(embedded, protobuf(PayloadSeq)) --> 520 { ground(PayloadSeq), 521 phrase(protobuf(PayloadSeq), Codes) 522 }, 523 payload(codes, Codes), 524 !. 525payload(embedded, protobuf(PayloadSeq)) --> 526 payload(codes, Codes), 527 { phrase(protobuf(PayloadSeq), Codes) }. 528payload(packed, TypedPayloadSeq) --> 529 { TypedPayloadSeq =.. [PrologType, PayloadSeq], % TypedPayloadSeq = PrologType(PayloadSeq) 530 ground(PayloadSeq), 531 phrase(packed_payload(PrologType, PayloadSeq), Codes) 532 }, 533 payload(codes, Codes), 534 !. 535payload(packed, enum(EnumSeq)) --> 536 !, 537 % TODO: combine with next clause 538 % TODO: replace =.. with a predicate that gives all the possibilities - see detag/6. 539 { EnumSeq =.. [ Enum, Values ] }, % EnumSeq = Enum(Values) 540 payload(codes, Codes), 541 { phrase(packed_enum(Enum, Values), Codes) }. 542payload(packed, TypedPayloadSeq) --> 543 payload(codes, Codes), 544 % TODO: replace =.. with a predicate that gives all the possibilities - see detag/6. 545 { TypedPayloadSeq =.. [PrologType, PayloadSeq] }, % TypedPayloadSeq = PrologType(PayloadSeq) 546 { phrase(packed_payload(PrologType, PayloadSeq), Codes) }. 547 548packed_payload(enum, EnumSeq) --> 549 { ground(EnumSeq) }, !, 550 { EnumSeq =.. [EnumType, Values] }, % EnumSeq = EnumType(Values) 551 packed_enum(EnumType, Values). 552packed_payload(PrologType, PayloadSeq) --> 553 sequence_payload(PrologType, PayloadSeq). 554 555% sequence_payload//2 (because sequence//2 isn't compile-time expanded) 556sequence_payload(PrologType, PayloadSeq) --> 557 sequence_payload_(PayloadSeq, PrologType). 558 559sequence_payload_([], _PrologType) --> [ ]. 560sequence_payload_([Payload|PayloadSeq], PrologType) --> 561 payload(PrologType, Payload), 562 sequence_payload_(PayloadSeq, PrologType). 563 564packed_enum(Enum, [ A | As ]) --> 565 % TODO: replace =.. with a predicate that gives all the possibilities - see detag/6. 566 { E =.. [Enum, A] }, 567 payload(enum, E), 568 packed_enum(Enum, As). 569packed_enum(_, []) --> [ ]. 570 571start_group(Tag) --> protobuf_tag_type(Tag, start_group). 572 573end_group(Tag) --> protobuf_tag_type(Tag, end_group). 574% 575% 576nothing([]) --> [], !. 577 578protobuf([Field | Fields]) --> 579 % TODO: don't use =.. -- move logic to single_message 580 ( { Field = repeated_embedded(Tag, protobuf(EmbeddedFields), Items) } 581 -> repeated_embedded_messages(Tag, EmbeddedFields, Items) 582 ; { Field =.. [ PrologType, Tag, Payload] }, % Field = PrologType(Tag, Payload) 583 single_message(PrologType, Tag, Payload), 584 ( protobuf(Fields) 585 ; nothing(Fields) 586 ) 587 ), 588 !. 589 590repeated_message(repeated_enum, Tag, Type, [A | B]) --> 591 % TODO: replace =.. with a predicate that gives all the possibilities - see detag/6. 592 { TypedPayload =.. [Type, A] }, % TypedPayload = Type(A) 593 single_message(enum, Tag, TypedPayload), 594 ( repeated_message(repeated_enum, Tag, Type, B) 595 ; nothing(B) 596 ). 597repeated_message(Type, Tag, [A | B]) --> 598 { Type \= repeated_enum }, 599 single_message(Type, Tag, A), 600 repeated_message(Type, Tag, B). 601repeated_message(_Type, _Tag, A) --> 602 nothing(A). 603 604repeated_embedded_messages(Tag, EmbeddedFields, [protobuf(A) | B]) --> 605 { copy_term(EmbeddedFields, A) }, 606 single_message(embedded, Tag, protobuf(A)), !, 607 repeated_embedded_messages(Tag, EmbeddedFields, B). 608repeated_embedded_messages(_Tag, _EmbeddedFields, []) --> 609 [ ].
protobuf([...])
.
The PrologType, Tag, Payload are from Field =.. [PrologType, Tag, Payload]
in the caller615single_message(repeated, Tag, enum(EnumSeq)) --> 616 !, 617 { EnumSeq =.. [EnumType, Values] }, % EnumSeq = EnumType(Values) 618 repeated_message(repeated_enum, Tag, EnumType, Values). 619single_message(repeated, Tag, Payload) --> 620 !, 621 % TODO: replace =.. with a predicate that gives all the possibilities - see detag/6. 622 { Payload =.. [PrologType, A] }, % Payload = PrologType(A) 623 { PrologType \= enum }, 624 repeated_message(PrologType, Tag, A). 625single_message(group, Tag, A) --> 626 !, 627 start_group(Tag), 628 protobuf(A), 629 end_group(Tag). 630single_message(PrologType, Tag, Payload) --> 631 { PrologType \= repeated, PrologType \= group }, 632 prolog_type(Tag, PrologType), 633 payload(PrologType, Payload).
658protobuf_message(protobuf(TemplateList), WireStream) :- 659 must_be(list, TemplateList), 660 phrase(protobuf(TemplateList), WireStream), 661 !. 662 663protobuf_message(protobuf(TemplateList), WireStream, Residue) :- 664 must_be(list, TemplateList), 665 phrase(protobuf(TemplateList), WireStream, Residue).
.proto
description, similar to
the processing done by protoc --decode_raw
. This means that
field names aren't shown, only field numbers.
For unmarshalling, a simple heuristic is used on length-delimited segments: first interpret it as a message; if that fails, try to interpret as a UTF8 string; otherwise, leave it as a "blob" (if the heuristic was wrong, you can convert to a string or a blob by using protobuf_segment_convert/2). 32-bit and 64-bit numbers are left as codes because they could be either integers or floating point (use int32_codes_when/2, float32_codes_when/2, int64_codes_when/2, uint32_codes_when/2, uint64_codes_when/2, float64_codes_when/2 as appropriate); variable-length numbers ("varint" in the Protocol Buffers encoding documentation), might require "zigzag" conversion, int64_zigzag_when/2.
For marshalling, use the predicates int32_codes_when/2, float32_codes_when/2, int64_codes_when/2, uint32_codes_when/2, uint64_codes_when/2, float64_codes_when/2, int64_zigzag_when/2 to put integer and floating point values into the appropriate form.
738protobuf_segment_message(Segments, WireStream) :- 739 phrase(segment_message(Segments), WireStream). 740 741segment_message(Segments) --> 742 sequence_segment(Segments). 743 744% sequence_segment//1 (because sequence//2 isn't compile-time expanded) 745sequence_segment([]) --> [ ]. 746sequence_segment([Segment|Segments]) --> 747 segment(Segment), 748 sequence_segment(Segments). 749 750segment(Segment) --> 751 { nonvar(Segment) }, 752 !, 753 % repeated(List) can be created by field_segment_scalar_or_repeated/7 754 ( { Segment = repeated(Segments) } 755 -> sequence_segment(Segments) 756 ; { segment_type_tag(Segment, Type, Tag) }, 757 protobuf_tag_type(Tag, Type), 758 segment(Type, Tag, Segment) 759 ). 760segment(Segment) --> 761 % { var(Segment) }, 762 protobuf_tag_type(Tag, Type), 763 segment(Type, Tag, Segment). 764 765segment(varint, Tag, varint(Tag,Value)) --> 766 protobuf_var_int(Value). 767segment(fixed64, Tag, fixed64(Tag, Int64)) --> 768 payload(integer64, Int64). 769segment(fixed32, Tag, fixed32(Tag, Int32)) --> 770 payload(integer32, Int32). 771segment(start_group, Tag, group(Tag, Segments)) --> 772 segment_message(Segments), 773 protobuf_tag_type(Tag, end_group). 774segment(length_delimited, Tag, Result) --> 775 segment_length_delimited(Tag, Result). 776 777segment_length_delimited(Tag, Result) --> 778 { nonvar(Result) }, 779 !, 780 { length_delimited_segment(Result, Tag, Codes) }, 781 { length(Codes, CodesLen) }, 782 protobuf_var_int(CodesLen), 783 code_string(CodesLen, Codes). 784segment_length_delimited(Tag, Result) --> 785 % { var(Result) }, 786 protobuf_var_int(CodesLen), 787 code_string(CodesLen, Codes), 788 { length_delimited_segment(Result, Tag, Codes) }. 789 790length_delimited_segment(message(Tag,Segments), Tag, Codes) :- 791 protobuf_segment_message(Segments, Codes). 792length_delimited_segment(group(Tag,Segments), Tag, Codes) :- 793 phrase(segment_group(Tag, Segments), Codes). 794length_delimited_segment(string(Tag,String), Tag, Codes) :- 795 ( nonvar(String) 796 -> string_codes(String, StringCodes), 797 phrase(utf8_codes(StringCodes), Codes) 798 ; phrase(utf8_codes(StringCodes), Codes), 799 string_codes(String, StringCodes) 800 ). 801length_delimited_segment(packed(Tag,Payload), Tag, Codes) :- 802 % We don't know the type of the fields, so we try the 3 803 % possibilities. This has a problem: an even number of fixed32 804 % items can't be distinguished from half the number of fixed64 805 % items; but it's all we can do. The good news is that usually 806 % varint (possibly with zig-zag encoding) is more common because 807 % it's more compact (I don't know whether 32-bit or 64-bit is more 808 % common for floating point). 809 packed_option(Type, Items, Payload), 810 phrase(sequence_payload(Type, Items), Codes). 811length_delimited_segment(length_delimited(Tag,Codes), Tag, Codes). 812 813segment_group(Tag, Segments) --> 814 start_group(Tag), 815 segment_message(Segments), 816 end_group(Tag). 817 818% See also prolog_type//2. Note that this doesn't handle repeated(List), 819% which is used internally (see field_segment_scalar_or_repeated/7). 820segment_type_tag(varint(Tag,_Value), varint, Tag). 821segment_type_tag(fixed64(Tag,_Value), fixed64, Tag). 822segment_type_tag(group(Tag,_Segments), start_group, Tag). 823segment_type_tag(fixed32(Tag,_Value), fixed32, Tag). 824segment_type_tag(length_delimited(Tag,_Codes), length_delimited, Tag). 825segment_type_tag(message(Tag,_Segments), length_delimited, Tag). 826segment_type_tag(packed(Tag,_Payload), length_delimited, Tag). 827segment_type_tag(string(Tag,_String), length_delimited, Tag).
Compound
or the form Name(Tag,Value)
and create a
new CompoundWithList
that replaces Value
with List
. This is
used by packed_list/2 to transform [varint(1,0),varint(1,1)]
to
varint(1,[0,1])
.
Some of Compound
items are impossible for packed
with the
current protobuf spec, but they don't do any harm.
837detag(varint(Tag,Value), varint, Tag, Value, List, varint(List)). 838detag(fixed64(Tag,Value), fixed64, Tag, Value, List, fixed64(List)). 839detag(fixed32(Tag,Value), fixed32, Tag, Value, List, fixed32(List)). 840detag(length_delimited(Tag,Codes), length_delimited, Tag, Codes, List, length_delimited(List)). 841detag(message(Tag,Segments), message, Tag, Segments, List, message(List)). 842detag(packed(Tag,Payload), packed, Tag, Payload, List, packed(List)). % TODO: delete? 843detag(string(Tag,String), string, Tag, String, List, string(List)). 844 845% See also prolog_type//2, but pick only one for each wirestream type 846% For varint(Items), use one that doesn't do zigzag 847packed_option(integer64, Items, fixed64(Items)). 848packed_option(integer32, Items, fixed32(Items)). 849packed_option(unsigned, Items, varint(Items)). 850% packed_option(integer, Items, varint(Items)). 851% packed_option(double, Items, fixed64(Items)). 852% packed_option(float, Items, fixed32(Items)). 853% packed_option(signed64, Items, varint(Items)). 854% packed_option(boolean, Items, varint(Items)). 855% packed_option(enum, Items, varint(Items)).
Form1
is converted back to the original wire stream, then the
predicate non-deterimisticly attempts to convert the wire stream to
a string
or length_delimited
term (or both: the lattter
always succeeds).
The possible conversions are:
message(Tag,Segments)
=> string(Tag,String)
message(Tag,Segments)
=> length_delimited(Tag,Codes)
string(Tag,String)
=> length_delimited(Tag,Codes)
length_delimited(Tag,Codes)
=> length_delimited(Tag,Codes)
Note that for fixed32, fixed64, only the signed integer forms are given; if you want the floating point forms, then you need to do use int64_float64_when/2 and int32_float32_when/2.
For example:
~~~{.pl}
?- protobuf_segment_convert(
message(10,[fixed64(13,7309475598860382318)]),
string(10,"inputType"))
.
?- protobuf_segment_convert(
message(10,[fixed64(13,7309475598860382318)]),
length_delimited(10,[105,110,112,117,116,84,121,112,101]))
.
?- protobuf_segment_convert(
string(10, "inputType"),
length_delimited(10,[105,110,112,117,116,84,121,112,101]))
.
?- forall(protobuf_segment_convert(string(1999,"\x1\\x0\\x0\\x0\\x2\\x0\\x0\\x0\"),Z), writeln(Z))
.
string(1999,
890
Google's Protocol Buffers ("protobufs")
Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data -- think XML, but smaller, faster, and simpler. You define how you want your data to be structured once. This takes the form of a template that describes the data structure. You use this template to encode and decode your data structure into wire-streams that may be sent-to or read-from your peers. The underlying wire stream is platform independent, lossless, and may be used to interwork with a variety of languages and systems regardless of word size or endianness. Techniques exist to safely extend your data structure without breaking deployed programs that are compiled against the "old" format.
The idea behind Google's Protocol Buffers is that you define your structured messages using a domain-specific language and tool set. Further documentation on this is at https://developers.google.com/protocol-buffers.
There are two ways you can use protobufs in Prolog:
.proto
file: protobuf_parse_from_codes/3 and protobuf_serialize_to_codes/3.The protobuf_parse_from_codes/3 and protobuf_serialize_to_codes/3 interface translates between a "wire stream" and a Prolog term. This interface takes advantage of SWI-Prolog's dict. There is a
protoc
plugin (protoc-gen-swipl
) that generates a Prolog file of meta-information that captures the.proto
file's definition in theprotobufs
module:proto_meta_normalize(Unnormalized, Normalized)
proto_meta_package(Package, FileName, Options)
proto_meta_message_type( Fqn, Package, Name)
proto_meta_message_type_map_entry( Fqn)
proto_meta_field_name( Fqn, FieldNumber, FieldName, FqnName)
proto_meta_field_json_name( FqnName, JsonName)
proto_meta_field_label( FqnName, LabelRepeatOptional) % 'LABEL_OPTIONAL', 'LABEL_REQUIRED', 'LABEL_REPEATED'
proto_meta_field_type( FqnName, Type) % 'TYPE_INT32', 'TYPE_MESSAGE', etc
proto_meta_field_type_name( FqnName, TypeName)
proto_meta_field_default_value( FqnName, DefaultValue)
proto_meta_field_option_packed( FqnName)
proto_meta_enum_type( FqnName, Fqn, Name)
proto_meta_enum_value( FqnName, Name, Number)
proto_meta_field_oneof_index( FqnName, Index)
proto_meta_oneof( FqnName, Index, Name)
The protobuf_message/2 interface allows you to define your message template as a list of predefined Prolog terms that correspond to production rules in the Definite Clause Grammar (DCG) that realizes the interpreter. Each production rule has an equivalent rule in the protobuf grammar. The process is not unlike specifiying the format of a regular expression. To encode a template to a wire-stream, you pass a grounded template,
X
, and variable,Y
, to protobuf_message/2. To decode a wire-stream,Y
, you pass an ungrounded template,X
, along with a grounded wire-stream,Y
, to protobuf_message/2. The interpreter will unify the unbound variables in the template with values decoded from the wire-stream.For an overview and tutorial with examples, see
library(protobufs)
: Google's Protocol Buffers Examples of usage may also be found by inspecting test_protobufs.pl and the demo directory, or by looking at the "addressbook" example that is typically installed at /usr/lib/swi-prolog/doc/packages/examples/protobufs/interop/addressbook.pl