-
Notifications
You must be signed in to change notification settings - Fork 6
Description
Hello!
While working with a protobuf struct with a sint64 field, I encoded the minimum possible 64-bit signed integer into a protobuf struct. I did set int64_as_int=false while generating the OCaml code to get true 64-bit integers rather than the OCaml default 63-bit integer. That worked fine, but when I attempted to decode the struct, the number that was returned was -1, not -9223372036854775808.
I've verified that the encoding was correct by writing the encoded message to a file both manually inspecting it with xxd and using protoscope.
Below is a minimal working example of this issue. Start with a very simple protobuf file
syntax = "proto3";
message s64 {
sint64 field = 1;
}and the OCaml code generation from protoc:
protoc --ocaml_out=mwe --ocaml_opt="annot=[@@deriving show];int64_as_int=false" s64.protowhich produces this OCaml file
Generated OCaml file
(********************************************************)
(* AUTOGENERATED FILE - DO NOT EDIT! *)
(********************************************************)
(* Generated by: ocaml-protoc-plugin *)
(* https://github.com/andersfugmann/ocaml-protoc-plugin *)
(********************************************************)
(*
Source: proto/s64.proto
Syntax: proto3
Parameters:
debug=false
annot='[@@deriving show]'
opens=[]
int64_as_int=false
int32_as_int=true
fixed_as_int=false
singleton_record=false
prefix_output_with_package=false
*)
[@@@ocaml.alert "-protobuf"] (* Disable deprecation warnings for protobuf*)
(**/**)
module Runtime' = Ocaml_protoc_plugin [@@warning "-33"]
module Imported'modules = struct
end
(**/**)
module rec S64 : sig
type t = (int64) [@@deriving show]
val make: ?field:int64 -> unit -> t
(** Helper function to generate a message using default values *)
val to_proto: t -> Runtime'.Writer.t
(** Serialize the message to binary format *)
val from_proto: Runtime'.Reader.t -> (t, [> Runtime'.Result.error]) result
(** Deserialize from binary format *)
val to_json: Runtime'.Json_options.t -> t -> Runtime'.Json.t
(** Serialize to Json (compatible with Yojson.Basic.t) *)
val from_json: Runtime'.Json.t -> (t, [> Runtime'.Result.error]) result
(** Deserialize from Json (compatible with Yojson.Basic.t) *)
val name: unit -> string
(** Fully qualified protobuf name of this message *)
(**/**)
type make_t = ?field:int64 -> unit -> t
val merge: t -> t -> t
val to_proto': Runtime'.Writer.t -> t -> unit
val from_proto_exn: Runtime'.Reader.t -> t
val from_json_exn: Runtime'.Json.t -> t
(**/**)
end = struct
module This'_ = S64
let name () = ".s64"
type t = (int64) [@@deriving show]
type make_t = ?field:int64 -> unit -> t
let make ?(field = 0L) () = (field)
let merge =
let merge_field = Runtime'.Merge.merge Runtime'.Spec.( basic ((1, "field", "field"), sint64, (0L)) ) in
fun (t1_field) (t2_field) -> merge_field t1_field t2_field
let spec () = Runtime'.Spec.( basic ((1, "field", "field"), sint64, (0L)) ^:: nil )
let to_proto' =
let serialize = Runtime'.apply_lazy (fun () -> Runtime'.Serialize.serialize (spec ())) in
fun writer (field) -> serialize writer field
let to_proto t = let writer = Runtime'.Writer.init () in to_proto' writer t; writer
let from_proto_exn =
let constructor field = (field) in
Runtime'.apply_lazy (fun () -> Runtime'.Deserialize.deserialize (spec ()) constructor)
let from_proto writer = Runtime'.Result.catch (fun () -> from_proto_exn writer)
let to_json options =
let serialize = Runtime'.Serialize_json.serialize ~message_name:(name ()) (spec ()) options in
fun (field) -> serialize field
let from_json_exn =
let constructor field = (field) in
Runtime'.apply_lazy (fun () -> Runtime'.Deserialize_json.deserialize ~message_name:(name ()) (spec ()) constructor)
let from_json json = Runtime'.Result.catch (fun () -> from_json_exn json)
endThen serialize and parse Int64.min_int from the main program, saving the encoded file for examination later:
open S64
open Stdio
open Ocaml_protoc_plugin
let () =
let s64 = S64.make ~field:Int64.min_int () in
let enc = Writer.contents (S64.to_proto s64) in
let out = open_out_bin "msg.bin" in
Printf.fprintf out "%s" enc;
close_out out;
match S64.from_proto (Reader.create enc) with
| Error e ->
printf "Error parsing struct: %s\n" (Runtime'.Result.show_error e)
| Ok z ->
printf "Parsed: %s should be %s\n" (Int64.to_string z)
(Int64.to_string Int64.min_int)Running this program outputs Parsed: -1 should be -9223372036854775808
The number is correctly encoded as 64 ones, and can be verified with protoscope or by hand with xxd:
protoscope -descriptor-set s64.descriptor -message-type s64 msg.bin
1: -9223372036854775808z
xxd -b msg.bin
00000000: 00001000 11111111 11111111 11111111 11111111 11111111 ......
00000006: 11111111 11111111 11111111 11111111 00000001 .....
Which is why I suspect that the issue occurs while decoding the integer.