///|
/// Only wire values, not generated classes. Unknown fields remain bounded.
priv enum Wire {
Var(UInt64)
Data(Bytes)
Fixed
}
///|
priv struct Field {
id : Int32
wire : Wire
}
///|
fn parse_proto(raw : Bytes, limits : Limits) -> Array[Field] raise OrcError {
if raw.length() > limits.max_decoded_bytes {
raise Limit("protobuf bytes")
}
let c = input(raw)
let out : Array[Field] = []
while c.pos < c.end {
if out.length() >= limits.max_fields {
raise Limit("protobuf fields")
}
let tag = host(c.varint())
if tag >> 3 == 0 {
raise Invalid("zero protobuf field", c.pos)
}
let wire = match tag & 7 {
0 => Var(c.varint())
1 => {
ignore(c.take(8))
Fixed
}
2 => {
let n = host(c.varint())
Data(c.take(n))
}
5 => {
ignore(c.take(4))
Fixed
}
_ => raise Unsupported("protobuf wire groups/type")
}
out.push({ id: tag >> 3, wire, })
}
out
}
///|
fn one(fields : Array[Field], id : Int32) -> Wire? raise OrcError {
let mut found : Wire? = None
for f in fields {
if f.id == id {
if found is Some(_) {
raise Invalid("duplicate singular protobuf field", id)
}
found = Some(f.wire)
}
}
found
}
///|
fn number(
fields : Array[Field],
id : Int32,
fallback : UInt64,
) -> UInt64 raise OrcError {
match one(fields, id) {
None => fallback
Some(Var(n)) => n
_ => raise Invalid("expected protobuf integer", id)
}
}
///|
fn blob(
fields : Array[Field],
id : Int32,
fallback : Bytes,
) -> Bytes raise OrcError {
match one(fields, id) {
None => fallback
Some(Data(n)) => n
_ => raise Invalid("expected protobuf message", id)
}
}
///|
fn blobs(fields : Array[Field], id : Int32) -> Array[Bytes] raise OrcError {
let out : Array[Bytes] = []
for f in fields {
if f.id == id {
match f.wire {
Data(b) => out.push(b)
_ => raise Invalid("expected repeated message", id)
}
}
}
out
}
///|
fn integers(fields : Array[Field], id : Int32) -> Array[Int32] raise OrcError {
let out : Array[Int32] = []
for f in fields {
if f.id == id {
match f.wire {
Var(n) => out.push(host(n))
Data(b) => {
let c = input(b)
while c.pos < c.end {
out.push(host(c.varint()))
}
}
Fixed => raise Invalid("expected packed integers", id)
}
}
}
out
}
///|
fn Output::field(self : Output, id : Int32, n : UInt64) -> Unit {
self.varint((id * 8).to_uint64())
self.varint(n)
}
///|
fn Output::message(self : Output, id : Int32, raw : Bytes) -> Unit {
self.varint((id * 8 + 2).to_uint64())
self.varint(raw.length().to_uint64())
self.raw(raw)
}
///|
fn decode_text(raw : Bytes) -> String raise OrcError {
@utf8.decode(raw, ignore_bom=false) catch {
_ => raise Invalid("invalid UTF8", 0)
}
}
///|
test "protobuf accepts packed fields and skips unfamiliar typed values" {
let b = output()
b.field(1, 5UL)
b.message(2, b"\x01\x7f")
let fields = parse_proto(b.finish(), Limits::default())
assert_eq(number(fields, 1, 0UL), 5UL)
assert_eq(integers(fields, 2), [1, 127])
assert_true(
try {
ignore(parse_proto(b"\x00", Limits::default()))
false
} catch {
_ => true
},
)
}