Google Protocol Buffers (AKA protobuf) is a popular data serialization scheme used for communication protocols, data storage, etc. There are implementations are available for almost every popular language. The focus points of this scheme are brevity (data is encoded in a very size-efficient manner) and extensibility (one can add keys to the structure, while keeping it readable in previous version of software).
Protobuf uses semi-self-describing encoding scheme for its
messages. It means that it is possible to parse overall structure of
the message (skipping over fields one can't understand), but to
fully understand the message, one needs a protocol definition file
(.proto
). To be specific:
.proto
file provides info on
which symbolic field names these field tags map to.sint32
vs uint32
vs some enum, or string
from bytes
), but
it's enough information to determine how many bytes to
parse. Interpretation of the value should be done according to the
type specified in .proto
file.This page hosts a formal specification of Google Protocol Buffers (protobuf) using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
use strict;
use warnings;
use IO::KaitaiStruct 0.009_000;
use VlqBase128Le;
########################################################################
package GoogleProtobuf;
our @ISA = 'IO::KaitaiStruct::Struct';
sub from_file {
my ($class, $filename) = @_;
my $fd;
open($fd, '<', $filename) or return undef;
binmode($fd);
return new($class, IO::KaitaiStruct::Stream->new($fd));
}
sub new {
my ($class, $_io, $_parent, $_root) = @_;
my $self = IO::KaitaiStruct::Struct->new($_io);
bless $self, $class;
$self->{_parent} = $_parent;
$self->{_root} = $_root || $self;;
$self->_read();
return $self;
}
sub _read {
my ($self) = @_;
$self->{pairs} = ();
while (!$self->{_io}->is_eof()) {
push @{$self->{pairs}}, GoogleProtobuf::Pair->new($self->{_io}, $self, $self->{_root});
}
}
sub pairs {
my ($self) = @_;
return $self->{pairs};
}
########################################################################
package GoogleProtobuf::Pair;
our @ISA = 'IO::KaitaiStruct::Struct';
sub from_file {
my ($class, $filename) = @_;
my $fd;
open($fd, '<', $filename) or return undef;
binmode($fd);
return new($class, IO::KaitaiStruct::Stream->new($fd));
}
our $WIRE_TYPES_VARINT = 0;
our $WIRE_TYPES_BIT_64 = 1;
our $WIRE_TYPES_LEN_DELIMITED = 2;
our $WIRE_TYPES_GROUP_START = 3;
our $WIRE_TYPES_GROUP_END = 4;
our $WIRE_TYPES_BIT_32 = 5;
sub new {
my ($class, $_io, $_parent, $_root) = @_;
my $self = IO::KaitaiStruct::Struct->new($_io);
bless $self, $class;
$self->{_parent} = $_parent;
$self->{_root} = $_root || $self;;
$self->_read();
return $self;
}
sub _read {
my ($self) = @_;
$self->{key} = VlqBase128Le->new($self->{_io});
my $_on = $self->wire_type();
if ($_on == $GoogleProtobuf::Pair::WIRE_TYPES_VARINT) {
$self->{value} = VlqBase128Le->new($self->{_io});
}
elsif ($_on == $GoogleProtobuf::Pair::WIRE_TYPES_LEN_DELIMITED) {
$self->{value} = GoogleProtobuf::DelimitedBytes->new($self->{_io}, $self, $self->{_root});
}
elsif ($_on == $GoogleProtobuf::Pair::WIRE_TYPES_BIT_64) {
$self->{value} = $self->{_io}->read_u8le();
}
elsif ($_on == $GoogleProtobuf::Pair::WIRE_TYPES_BIT_32) {
$self->{value} = $self->{_io}->read_u4le();
}
}
sub wire_type {
my ($self) = @_;
return $self->{wire_type} if ($self->{wire_type});
$self->{wire_type} = ($self->key()->value() & 7);
return $self->{wire_type};
}
sub field_tag {
my ($self) = @_;
return $self->{field_tag} if ($self->{field_tag});
$self->{field_tag} = ($self->key()->value() >> 3);
return $self->{field_tag};
}
sub key {
my ($self) = @_;
return $self->{key};
}
sub value {
my ($self) = @_;
return $self->{value};
}
########################################################################
package GoogleProtobuf::DelimitedBytes;
our @ISA = 'IO::KaitaiStruct::Struct';
sub from_file {
my ($class, $filename) = @_;
my $fd;
open($fd, '<', $filename) or return undef;
binmode($fd);
return new($class, IO::KaitaiStruct::Stream->new($fd));
}
sub new {
my ($class, $_io, $_parent, $_root) = @_;
my $self = IO::KaitaiStruct::Struct->new($_io);
bless $self, $class;
$self->{_parent} = $_parent;
$self->{_root} = $_root || $self;;
$self->_read();
return $self;
}
sub _read {
my ($self) = @_;
$self->{len} = VlqBase128Le->new($self->{_io});
$self->{body} = $self->{_io}->read_bytes($self->len()->value());
}
sub len {
my ($self) = @_;
return $self->{len};
}
sub body {
my ($self) = @_;
return $self->{body};
}
1;