Protocol Buffers codec for hand-written schemas
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

protobuf: rename entry points to of_* / to_*

Match the naming pattern already used elsewhere in the monorepo
(ocaml-json, ocaml-cbor, ocaml-xml): [of_*] decodes, [to_*] encodes,
and each [of_*] has an [_exn] twin that raises {!Loc.Error} on the
error path.

encode_string -> to_string
decode_string -> of_string
encode -> to_writer
decode -> of_reader

New entries:

val of_string_exn : 'a t -> string -> 'a
val of_reader_exn : 'a t -> Bytesrw.Bytes.Reader.t -> 'a

Call sites in [test/test_protobuf.ml], [test/test_wire.ml],
[test/interop/protoc/test.ml] and [fuzz/fuzz_protobuf.ml] are
mechanically updated. The old names are gone; no backwards-compat
aliases.

All 53 unit + 17 fuzz + 2 protoc interop tests pass.

+120 -86
+9 -9
fuzz/fuzz_protobuf.ml
··· 19 19 (* -- Scalar round-trip -- *) 20 20 21 21 let roundtrip_scalar codec v = 22 - let s = Protobuf.encode_string codec v in 23 - match Protobuf.decode_string codec s with Ok v' -> v' = v | Error _ -> false 22 + let s = Protobuf.to_string codec v in 23 + match Protobuf.of_string codec s with Ok v' -> v' = v | Error _ -> false 24 24 25 25 let test_i32 v = guard (roundtrip_scalar Protobuf.int32 (Int32.of_int v)) 26 26 let test_i64 v = guard (roundtrip_scalar Protobuf.int64 v) ··· 186 186 }) 187 187 188 188 let test_kitchen_roundtrip k = 189 - let s = Protobuf.encode_string kitchen_codec k in 190 - match Protobuf.decode_string kitchen_codec s with 189 + let s = Protobuf.to_string kitchen_codec k in 190 + match Protobuf.of_string kitchen_codec s with 191 191 | Error msg -> 192 192 Alcobar.pp Format.err_formatter "%s\n" (Protobuf.Error.to_string msg); 193 193 guard false ··· 200 200 or allocate unboundedly. *) 201 201 202 202 let test_decode_any_bytes input = 203 - match Protobuf.decode_string kitchen_codec input with 203 + match Protobuf.of_string kitchen_codec input with 204 204 | Ok _ | Error _ -> guard true 205 205 206 206 let test_decode_any_int32 input = 207 - match Protobuf.decode_string Protobuf.int32 input with 207 + match Protobuf.of_string Protobuf.int32 input with 208 208 | Ok _ | Error _ -> guard true 209 209 210 210 let test_decode_any_string input = 211 - match Protobuf.decode_string Protobuf.string input with 211 + match Protobuf.of_string Protobuf.string input with 212 212 | Ok _ | Error _ -> guard true 213 213 214 214 (* -- Packed-list round-trip -- ··· 225 225 return { xs }) 226 226 227 227 let test_packed_roundtrip xs = 228 - let s = Protobuf.encode_string packed_codec { xs } in 229 - match Protobuf.decode_string packed_codec s with 228 + let s = Protobuf.to_string packed_codec { xs } in 229 + match Protobuf.of_string packed_codec s with 230 230 | Error _ -> guard false 231 231 | Ok { xs = xs' } -> 232 232 check_eq ~eq:( = )
+15
lib/protobuf.ml
··· 827 827 let s = Bytesrw.Bytes.Reader.to_string r in 828 828 decode_string codec s 829 829 830 + (* -- [of_*] / [to_*] entry points -- *) 831 + 832 + let of_string = decode_string 833 + let to_string = encode_string 834 + 835 + let of_string_exn codec s = 836 + match decode_string codec s with Ok v -> v | Error e -> raise (Loc.Error e) 837 + 838 + let of_reader = decode 839 + 840 + let of_reader_exn codec r = 841 + match decode codec r with Ok v -> v | Error e -> raise (Loc.Error e) 842 + 843 + let to_writer = encode 844 + 830 845 (* -- Unknown-field preservation -- 831 846 832 847 Protobuf's wire format guarantees: a decoder ignorant of a field's
+33 -13
lib/protobuf.mli
··· 18 18 return { name; age; hobbies } |> finish 19 19 20 20 let wire = 21 - Protobuf.encode_string person 21 + Protobuf.to_string person 22 22 { name = "Ada"; age = 36l; hobbies = [ "math" ] } 23 23 ]} *) 24 24 ··· 179 179 used when a [required] field declared with this codec is absent from the 180 180 wire (typically the "empty" record for a tree-shaped type). *) 181 181 182 - (** {1 Encode / Decode} *) 182 + (** {1 Errors} *) 183 + 184 + module Error = Error 185 + (** Structured decode errors. See {!module:Error}. *) 186 + 187 + (** {1 Reading and writing} 188 + 189 + Four entry points per direction, matched on the sink/source: 183 190 184 - val encode_string : 'a t -> 'a -> string 185 - (** [encode_string c v] encodes [v] as a protobuf message body (no outer length 191 + - strings: {!of_string}, {!to_string} 192 + - {!Bytesrw} readers and writers: {!of_reader}, {!to_writer} 193 + 194 + Each [of_*] returns a [(_ , Error.t) result] and has an {!exn} twin that 195 + raises {!Loc.Error} instead. *) 196 + 197 + val of_string : 'a t -> string -> ('a, Error.t) result 198 + (** [of_string c s] decodes [s] as a protobuf message body using codec [c]. 199 + Returns [Error e] on malformed input; trailing garbage is an error. Use 200 + {!Error.to_string} to render [e] as a string. *) 201 + 202 + val of_string_exn : 'a t -> string -> 'a 203 + (** [of_string_exn c s] is {!of_string}, raising {!Loc.Error} on the error path. 204 + *) 205 + 206 + val to_string : 'a t -> 'a -> string 207 + (** [to_string c v] encodes [v] as a protobuf message body (no outer length 186 208 prefix). *) 187 209 188 - module Error = Error 189 - (** Structured decode errors extending {!Loc.Error}. *) 210 + val of_reader : 'a t -> Bytesrw.Bytes.Reader.t -> ('a, Error.t) result 211 + (** [of_reader c r] drains [r] to end-of-data and decodes the full content. *) 190 212 191 - val decode_string : 'a t -> string -> ('a, Error.t) result 192 - (** [decode_string c s] decodes the entire input as a message body. Returns 193 - [Error e] on malformed input; trailing garbage is an error. Use 194 - {!Error.to_string} to render a human-readable message. *) 213 + val of_reader_exn : 'a t -> Bytesrw.Bytes.Reader.t -> 'a 214 + (** [of_reader_exn c r] is {!of_reader}, raising on the error path. *) 195 215 196 - val encode : 'a t -> Bytesrw.Bytes.Writer.t -> 'a -> unit 197 - (** [encode c w v] encodes [v] and writes it to [w] as a single slice. Useful 216 + val to_writer : 'a t -> Bytesrw.Bytes.Writer.t -> 'a -> unit 217 + (** [to_writer c w v] encodes [v] and writes it to [w] as a single slice. Useful 198 218 for composition with other bytesrw pipelines. *) 199 219 200 220 val decode : 'a t -> Bytesrw.Bytes.Reader.t -> ('a, Error.t) result 201 - (** [decode c r] drains [r] to end-of-data and decodes the full content. *) 221 + (** Alias for {!of_reader}. *) 202 222 203 223 (** {1 Unknown field preservation} 204 224
+4 -4
test/interop/protoc/test.ml
··· 51 51 (fun (r : test1_row) -> 52 52 let expected_wire = hex_decode r.wire_hex in 53 53 (* encode direction: our codec must produce exactly what protoc did *) 54 - let our_wire = Protobuf.encode_string test1_codec { a = r.a } in 54 + let our_wire = Protobuf.to_string test1_codec { a = r.a } in 55 55 Alcotest.(check string) 56 56 (Fmt.str "encode %s" r.name) 57 57 r.wire_hex (hex_encode our_wire); 58 58 (* decode direction: our codec reads protoc's bytes into the same value *) 59 - match Protobuf.decode_string test1_codec expected_wire with 59 + match Protobuf.of_string test1_codec expected_wire with 60 60 | Error msg -> 61 61 Alcotest.failf "%s: decode failed: %a" r.name Protobuf.Error.pp msg 62 62 | Ok decoded -> ··· 164 164 List.iter 165 165 (fun (r : everything_row) -> 166 166 let expected_wire = hex_decode r.wire_hex in 167 - match Protobuf.decode_string everything_codec expected_wire with 167 + match Protobuf.of_string everything_codec expected_wire with 168 168 | Error msg -> 169 169 Alcotest.failf "%s: decode failed: %a" r.name Protobuf.Error.pp msg 170 170 | Ok decoded -> 171 - let reencoded = Protobuf.encode_string everything_codec decoded in 171 + let reencoded = Protobuf.to_string everything_codec decoded in 172 172 Alcotest.(check string) 173 173 (Fmt.str "re-encode %s matches protoc" r.name) 174 174 r.wire_hex (hex_encode reencoded))
+59 -60
test/test_protobuf.ml
··· 19 19 return { a }) 20 20 21 21 let test_spec_test1 () = 22 - let wire = Protobuf.encode_string test1_codec { a = 150l } in 22 + let wire = Protobuf.to_string test1_codec { a = 150l } in 23 23 Alcotest.(check string) "Test1 a=150" "089601" (hex wire); 24 - match Protobuf.decode_string test1_codec wire with 24 + match Protobuf.of_string test1_codec wire with 25 25 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 26 26 | Ok r -> Alcotest.(check int32) "decoded a" 150l r.a 27 27 ··· 36 36 return { b }) 37 37 38 38 let test_spec_test2 () = 39 - let wire = Protobuf.encode_string test2_codec { b = "testing" } in 39 + let wire = Protobuf.to_string test2_codec { b = "testing" } in 40 40 Alcotest.(check string) "Test2 b=testing" "120774657374696e67" (hex wire); 41 - match Protobuf.decode_string test2_codec wire with 41 + match Protobuf.of_string test2_codec wire with 42 42 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 43 43 | Ok r -> Alcotest.(check string) "decoded b" "testing" r.b 44 44 ··· 119 119 byts = "\x00\x01\x02\xff"; 120 120 } 121 121 in 122 - let wire = Protobuf.encode_string all_scalars_codec v in 123 - match Protobuf.decode_string all_scalars_codec wire with 122 + let wire = Protobuf.to_string all_scalars_codec v in 123 + match Protobuf.of_string all_scalars_codec wire with 124 124 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 125 125 | Ok r -> 126 126 Alcotest.(check int32) "i32" v.i32 r.i32; ··· 152 152 153 153 let test_optional_both_present () = 154 154 let v = { name = Some "Ada"; age = Some 36l } in 155 - let wire = Protobuf.encode_string opt_codec v in 156 - match Protobuf.decode_string opt_codec wire with 155 + let wire = Protobuf.to_string opt_codec v in 156 + match Protobuf.of_string opt_codec wire with 157 157 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 158 158 | Ok r -> 159 159 Alcotest.(check (option string)) "name" v.name r.name; ··· 161 161 162 162 let test_optional_both_absent () = 163 163 let v = { name = None; age = None } in 164 - let wire = Protobuf.encode_string opt_codec v in 164 + let wire = Protobuf.to_string opt_codec v in 165 165 Alcotest.(check int) "empty wire" 0 (String.length wire); 166 - match Protobuf.decode_string opt_codec wire with 166 + match Protobuf.of_string opt_codec wire with 167 167 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 168 168 | Ok r -> 169 169 Alcotest.(check (option string)) "name" None r.name; ··· 171 171 172 172 let test_optional_partial () = 173 173 let v = { name = Some "solo"; age = None } in 174 - let wire = Protobuf.encode_string opt_codec v in 175 - match Protobuf.decode_string opt_codec wire with 174 + let wire = Protobuf.to_string opt_codec v in 175 + match Protobuf.of_string opt_codec wire with 176 176 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 177 177 | Ok r -> 178 178 Alcotest.(check (option string)) "name" (Some "solo") r.name; ··· 190 190 191 191 let test_repeated_strings () = 192 192 let v = { tags = [ "a"; "bb"; "ccc" ] } in 193 - let wire = Protobuf.encode_string rep_str_codec v in 194 - match Protobuf.decode_string rep_str_codec wire with 193 + let wire = Protobuf.to_string rep_str_codec v in 194 + match Protobuf.of_string rep_str_codec wire with 195 195 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 196 196 | Ok r -> Alcotest.(check (list string)) "tags" v.tags r.tags 197 197 ··· 207 207 208 208 let test_packed_ints () = 209 209 let v = { nums = [ 1l; 2l; 3l; 150l ] } in 210 - let wire = Protobuf.encode_string packed_codec v in 210 + let wire = Protobuf.to_string packed_codec v in 211 211 (* Tag 1 wire type 2 (length-delim), length 5, body = 01 02 03 96 01 *) 212 212 Alcotest.(check string) "packed wire" "0a050102039601" (hex wire); 213 - match Protobuf.decode_string packed_codec wire with 213 + match Protobuf.of_string packed_codec wire with 214 214 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 215 215 | Ok r -> Alcotest.(check (list int32)) "nums" v.nums r.nums 216 216 ··· 224 224 return { nums }) 225 225 in 226 226 let unpacked_wire = 227 - Protobuf.encode_string unpacked_codec { nums = [ 1l; 2l; 3l; 150l ] } 227 + Protobuf.to_string unpacked_codec { nums = [ 1l; 2l; 3l; 150l ] } 228 228 in 229 - match Protobuf.decode_string packed_codec unpacked_wire with 229 + match Protobuf.of_string packed_codec unpacked_wire with 230 230 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 231 231 | Ok r -> Alcotest.(check (list int32)) "nums" [ 1l; 2l; 3l; 150l ] r.nums 232 232 ··· 251 251 252 252 let test_nested () = 253 253 let v = { inner = { x = 42l }; label = "hi" } in 254 - let wire = Protobuf.encode_string outer_codec v in 255 - match Protobuf.decode_string outer_codec wire with 254 + let wire = Protobuf.to_string outer_codec v in 255 + match Protobuf.of_string outer_codec wire with 256 256 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 257 257 | Ok r -> 258 258 Alcotest.(check int32) "inner.x" 42l r.inner.x; ··· 269 269 ~wire_type:Protobuf.Wire.Length_delimited; 270 270 Protobuf.Wire.write_string buf "extra-junk"; 271 271 let wire = Buffer.contents buf in 272 - match Protobuf.decode_string test1_codec wire with 272 + match Protobuf.of_string test1_codec wire with 273 273 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 274 274 | Ok r -> Alcotest.(check int32) "a decoded despite stray tag 99" 150l r.a 275 275 ··· 284 284 Protobuf.Wire.write_tag buf ~field_number:1 ~wire_type:Protobuf.Wire.Varint; 285 285 Protobuf.Wire.write_int32 buf 7l; 286 286 let wire = Buffer.contents buf in 287 - match Protobuf.decode_string all_scalars_codec wire with 287 + match Protobuf.of_string all_scalars_codec wire with 288 288 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 289 289 | Ok r -> 290 290 Alcotest.(check int32) "i32 decoded" 7l r.i32; ··· 298 298 let test_truncated_rejected () = 299 299 (* A valid tag but a truncated varint body. *) 300 300 let bad = "\x08\x80" in 301 - match Protobuf.decode_string test1_codec bad with 301 + match Protobuf.of_string test1_codec bad with 302 302 | Error _ -> () 303 303 | Ok _ -> Alcotest.fail "truncated should be rejected" 304 304 ··· 314 314 315 315 let test_map_string_int32 () = 316 316 let v = { entries = [ ("alice", 30l); ("bob", 25l); ("", 0l) ] } in 317 - let wire = Protobuf.encode_string dict_codec v in 318 - match Protobuf.decode_string dict_codec wire with 317 + let wire = Protobuf.to_string dict_codec v in 318 + match Protobuf.of_string dict_codec wire with 319 319 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 320 320 | Ok r -> 321 321 Alcotest.(check int) "entry count" 3 (List.length r.entries); ··· 345 345 (* Encode v2, decode with v1 capturing unknowns, re-encode, assert 346 346 bytes round-trip when compared to original v2. *) 347 347 let original = 348 - Protobuf.encode_string schema_v2 349 - { a = 42l; b = "hello"; c = [ 1l; 2l; 3l ] } 348 + Protobuf.to_string schema_v2 { a = 42l; b = "hello"; c = [ 1l; 2l; 3l ] } 350 349 in 351 350 match Protobuf.decode_with_unknowns_string schema_v1 original with 352 351 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) ··· 360 359 let reemitted = 361 360 Protobuf.encode_with_unknowns_string schema_v1 ~unknowns { a = v1.a } 362 361 in 363 - match Protobuf.decode_string schema_v2 reemitted with 362 + match Protobuf.of_string schema_v2 reemitted with 364 363 | Error msg -> 365 364 Alcotest.failf "v2 re-decode failed: %s" 366 365 (Protobuf.Error.to_string msg) ··· 370 369 Alcotest.(check (list int32)) "c survived" [ 1l; 2l; 3l ] v2'.c) 371 370 372 371 let test_unknowns_empty_on_match () = 373 - let wire = Protobuf.encode_string schema_v1 { a = 42l } in 372 + let wire = Protobuf.to_string schema_v1 { a = 42l } in 374 373 match Protobuf.decode_with_unknowns_string schema_v1 wire with 375 374 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 376 375 | Ok (_, unknowns) -> ··· 403 402 404 403 let test_oneof_text () = 405 404 let v = { payload = `Text "hello" } in 406 - let wire = Protobuf.encode_string msg_with_payload_codec v in 407 - match Protobuf.decode_string msg_with_payload_codec wire with 405 + let wire = Protobuf.to_string msg_with_payload_codec v in 406 + match Protobuf.of_string msg_with_payload_codec wire with 408 407 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 409 408 | Ok r -> Alcotest.(check bool) "roundtrip" true (r.payload = `Text "hello") 410 409 411 410 let test_oneof_num () = 412 411 let v = { payload = `Num 42l } in 413 - let wire = Protobuf.encode_string msg_with_payload_codec v in 414 - match Protobuf.decode_string msg_with_payload_codec wire with 412 + let wire = Protobuf.to_string msg_with_payload_codec v in 413 + match Protobuf.of_string msg_with_payload_codec wire with 415 414 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 416 415 | Ok r -> Alcotest.(check bool) "roundtrip" true (r.payload = `Num 42l) 417 416 418 417 let test_oneof_none () = 419 418 let v = { payload = `None } in 420 - let wire = Protobuf.encode_string msg_with_payload_codec v in 419 + let wire = Protobuf.to_string msg_with_payload_codec v in 421 420 Alcotest.(check int) "empty wire" 0 (String.length wire); 422 - match Protobuf.decode_string msg_with_payload_codec wire with 421 + match Protobuf.of_string msg_with_payload_codec wire with 423 422 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 424 423 | Ok r -> Alcotest.(check bool) "payload is None" true (r.payload = `None) 425 424 ··· 435 434 ~wire_type:Protobuf.Wire.Length_delimited; 436 435 Protobuf.Wire.write_string buf "winner"; 437 436 let wire = Buffer.contents buf in 438 - match Protobuf.decode_string msg_with_payload_codec wire with 437 + match Protobuf.of_string msg_with_payload_codec wire with 439 438 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 440 439 | Ok r -> Alcotest.(check bool) "last wins" true (r.payload = `Blob "winner") 441 440 442 441 let test_map_empty () = 443 442 let v = { entries = [] } in 444 - let wire = Protobuf.encode_string dict_codec v in 443 + let wire = Protobuf.to_string dict_codec v in 445 444 Alcotest.(check int) "empty wire" 0 (String.length wire); 446 - match Protobuf.decode_string dict_codec wire with 445 + match Protobuf.of_string dict_codec wire with 447 446 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 448 447 | Ok r -> Alcotest.(check (list (pair string int32))) "entries" [] r.entries 449 448 ··· 468 467 "\x72" (* tag 14, wire type 2 *) ^ "\xff\xff\xff\x7f" 469 468 (* varint 0x0FFFFFFF = 268435455 *) ^ "ab" 470 469 in 471 - match Protobuf.decode_string test1_codec bad with 470 + match Protobuf.of_string test1_codec bad with 472 471 | Error _ -> () 473 472 | Ok _ -> Alcotest.fail "CVE-2015-5237: huge length prefix must be rejected" 474 473 475 474 let test_cve5237_overlong_varint () = 476 475 (* Varint with 11 continuation bytes. 64-bit values fit in 10. *) 477 476 let bad = "\x08" ^ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01" in 478 - match Protobuf.decode_string test1_codec bad with 477 + match Protobuf.of_string test1_codec bad with 479 478 | Error _ -> () 480 479 | Ok _ -> Alcotest.fail "CVE-2015-5237: over-long varint must be rejected" 481 480 482 481 let test_cve5237_truncated_tag () = 483 - match Protobuf.decode_string test1_codec "\x80\x80\x80" with 482 + match Protobuf.of_string test1_codec "\x80\x80\x80" with 484 483 | Error _ -> () 485 484 | Ok _ -> Alcotest.fail "CVE-2015-5237: truncated tag must be rejected" 486 485 ··· 500 499 Protobuf.Wire.write_int32 buf 1l 501 500 done; 502 501 let wire = Buffer.contents buf in 503 - match Protobuf.decode_string empty_codec wire with 502 + match Protobuf.of_string empty_codec wire with 504 503 | Ok () -> () 505 504 | Error msg -> 506 505 Alcotest.failf "10k unknown fields rejected: %s" ··· 516 515 (* Emit a full [Test1] message; decode through [empty_codec] whose 517 516 schema has no fields. Every field is unknown. Decoder must not 518 517 crash. *) 519 - let wire = Protobuf.encode_string test1_codec { a = 42l } in 520 - match Protobuf.decode_string empty_codec wire with 518 + let wire = Protobuf.to_string test1_codec { a = 42l } in 519 + match Protobuf.of_string empty_codec wire with 521 520 | Ok () -> () 522 521 | Error msg -> 523 522 Alcotest.failf "empty schema, all unknowns: %s" ··· 531 530 let test_cve3171_group_wt3 () = 532 531 (* Wire type 3 is the deprecated `SGROUP` form. Modern protobuf 533 532 rejects it. *) 534 - match Protobuf.decode_string test1_codec "\x0b\x00" with 533 + match Protobuf.of_string test1_codec "\x0b\x00" with 535 534 | Error _ -> () 536 535 | Ok _ -> Alcotest.fail "CVE-2022-3171: wire type 3 must be rejected" 537 536 538 537 let test_cve3171_group_wt4 () = 539 538 (* Wire type 4 is the deprecated `EGROUP` form. Must be rejected at 540 539 tag-parse time regardless of field number. *) 541 - match Protobuf.decode_string test1_codec "\x0c" with 540 + match Protobuf.of_string test1_codec "\x0c" with 542 541 | Error _ -> () 543 542 | Ok _ -> Alcotest.fail "CVE-2022-3171: wire type 4 must be rejected" 544 543 ··· 570 569 let inner = build_wire (n - 1) in 571 570 "\x0a" ^ Leb128.encode_u63_string (String.length inner) ^ inner 572 571 in 573 - match Protobuf.decode_string nest_codec (build_wire 200) with 572 + match Protobuf.of_string nest_codec (build_wire 200) with 574 573 | Error _ -> () 575 574 | Ok () -> Alcotest.fail "CVE-2024-7254: 200-level nesting must be rejected" 576 575 ··· 589 588 in 590 589 let wire = build_wire 500 in 591 590 (* Must decode without stack overflow. *) 592 - match Protobuf.decode_string test1_codec wire with 591 + match Protobuf.of_string test1_codec wire with 593 592 | Ok _ | Error _ -> () 594 593 595 594 (* ================================================================= ··· 600 599 let test_cve47554_length_past_end () = 601 600 (* Tag 14 (str), length 100, but only 2 bytes follow. *) 602 601 let bad = "\x72\x64ab" in 603 - match Protobuf.decode_string test1_codec bad with 602 + match Protobuf.of_string test1_codec bad with 604 603 | Error _ -> () 605 604 | Ok _ -> Alcotest.fail "CVE-2024-47554: length past end must be rejected" 606 605 ··· 618 617 (* Tag 1, wire 2, length 2, then \x80\x80 (unterminated varint inside 619 618 the packed blob). *) 620 619 let bad = "\x0a\x02\x80\x80" in 621 - match Protobuf.decode_string packed_codec bad with 620 + match Protobuf.of_string packed_codec bad with 622 621 | Error _ -> () 623 622 | Ok _ -> Alcotest.fail "CVE-2024-47554: corrupt packed body must be rejected" 624 623 ··· 628 627 ================================================================= *) 629 628 630 629 let test_reserved_tag_zero () = 631 - match Protobuf.decode_string test1_codec "\x00" with 630 + match Protobuf.of_string test1_codec "\x00" with 632 631 | Error _ -> () 633 632 | Ok _ -> Alcotest.fail "tag field=0 must be rejected" 634 633 ··· 643 642 "\x0a\x00" 644 643 (* tag 1 wire type 2, length 0 *) 645 644 in 646 - match Protobuf.decode_string test1_codec bad with 645 + match Protobuf.of_string test1_codec bad with 647 646 | Error _ -> () 648 647 | Ok _ -> Alcotest.fail "wire type mismatch must be rejected" 649 648 ··· 652 651 ================================================================= *) 653 652 654 653 let test_empty_input () = 655 - match Protobuf.decode_string test1_codec "" with 654 + match Protobuf.of_string test1_codec "" with 656 655 | Error msg -> 657 656 Alcotest.failf "empty input should use defaults: %s" 658 657 (Protobuf.Error.to_string msg) ··· 664 663 ================================================================= *) 665 664 666 665 let test_overrun_rejected () = 667 - let good_prefix = Protobuf.encode_string test1_codec { a = 1l } in 666 + let good_prefix = Protobuf.to_string test1_codec { a = 1l } in 668 667 let with_trailer = 669 668 good_prefix ^ "\x80" 670 669 (* truncated continuation *) 671 670 in 672 - match Protobuf.decode_string test1_codec with_trailer with 671 + match Protobuf.of_string test1_codec with_trailer with 673 672 | Error _ -> () 674 673 | Ok _ -> Alcotest.fail "trailing truncated varint must be rejected" 675 674 ··· 690 689 let test_non_utf8_string_accepted () = 691 690 let raw = "\xff\xfe\xfd" in 692 691 (* Not valid UTF-8 *) 693 - let wire = Protobuf.encode_string with_str_codec { s = raw } in 694 - match Protobuf.decode_string with_str_codec wire with 692 + let wire = Protobuf.to_string with_str_codec { s = raw } in 693 + match Protobuf.of_string with_str_codec wire with 695 694 | Error msg -> 696 695 Alcotest.failf "non-UTF-8 string must decode: %s" 697 696 (Protobuf.Error.to_string msg) ··· 708 707 wire order in the output list; semantic last-wins is the user's 709 708 responsibility. *) 710 709 let v = { entries = [ ("k", 1l); ("k", 2l); ("x", 99l); ("k", 3l) ] } in 711 - let wire = Protobuf.encode_string dict_codec v in 712 - match Protobuf.decode_string dict_codec wire with 710 + let wire = Protobuf.to_string dict_codec v in 711 + match Protobuf.of_string dict_codec wire with 713 712 | Error e -> Alcotest.fail (Protobuf.Error.to_string e) 714 713 | Ok r -> 715 714 Alcotest.(check int) "entry count preserved" 4 (List.length r.entries) ··· 736 735 Protobuf.Wire.write_string buf "x" 737 736 done; 738 737 let wire = Buffer.contents buf in 739 - match Protobuf.decode_string rep_codec wire with 738 + match Protobuf.of_string rep_codec wire with 740 739 | Error msg -> 741 740 Alcotest.failf "many-repeated rejected: %s" (Protobuf.Error.to_string msg) 742 741 | Ok r ->