123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226open!CoremoduleConstants=Msgpack.Internal.ConstantsmodulePattern=struct(* The tag-byte pattern this kind matches. [Range] is used for the fix-* kinds and the
two fixint ranges; everything else is a single byte. *)typet=|Singleofint|Rangeofint*intendmoduleFormat=structtypet=|Positive_fixint|Fixmap|Fixarray|Fixstr|Nil(* 0xc1 is marked "(never used)" in the msgpack spec, i.e. reserved for future use.
See https://github.com/msgpack/msgpack/blob/master/spec.md#overview . The parser
fails when it sees this byte; the variant exists so the byte-range partition check
below stays exhaustive. *)|Reserved_c1|False_|True_|Bin8|Bin16|Bin32|Ext8|Ext16|Ext32|Float32|Float64|Uint8|Uint16|Uint32|Uint64|Int8|Int16|Int32|Int64|Fixext1|Fixext2|Fixext4|Fixext8|Fixext16|Str8|Str16|Str32|Array16|Array32|Map16|Map32|Negative_fixint[@@derivingenumerate]letpattern=function|Positive_fixint->Pattern.Range(0x00,Constants.positive_fixint_unmask)|Fixmap->Range(Constants.fixmap_mask,Constants.fixmap_masklor0xf)|Fixarray->Range(Constants.fixarray_mask,Constants.fixarray_masklor0xf)|Fixstr->Range(Constants.fixstr_mask,Constants.fixstr_masklor0x1f)|Nil->Single(Char.to_intConstants.nil)|Reserved_c1->Single0xc1|False_->Single(Char.to_intConstants.false_)|True_->Single(Char.to_intConstants.true_)|Bin8->Single(Char.to_intConstants.bin8_header)|Bin16->Single(Char.to_intConstants.bin16_header)|Bin32->Single(Char.to_intConstants.bin32_header)|Ext8->Single(Char.to_intConstants.ext8_header)|Ext16->Single(Char.to_intConstants.ext16_header)|Ext32->Single(Char.to_intConstants.ext32_header)|Float32->Single(Char.to_intConstants.float32_header)|Float64->Single(Char.to_intConstants.float64_header)|Uint8->Single(Char.to_intConstants.uint8_header)|Uint16->Single(Char.to_intConstants.uint16_header)|Uint32->Single(Char.to_intConstants.uint32_header)|Uint64->Single(Char.to_intConstants.uint64_header)|Int8->Single(Char.to_intConstants.int8_header)|Int16->Single(Char.to_intConstants.int16_header)|Int32->Single(Char.to_intConstants.int32_header)|Int64->Single(Char.to_intConstants.int64_header)|Fixext1->Single(Char.to_intConstants.fixext1_header)|Fixext2->Single(Char.to_intConstants.fixext2_header)|Fixext4->Single(Char.to_intConstants.fixext4_header)|Fixext8->Single(Char.to_intConstants.fixext8_header)|Fixext16->Single(Char.to_intConstants.fixext16_header)|Str8->Single(Char.to_intConstants.str8_header)|Str16->Single(Char.to_intConstants.str16_header)|Str32->Single(Char.to_intConstants.str32_header)|Array16->Single(Char.to_intConstants.array16_header)|Array32->Single(Char.to_intConstants.array32_header)|Map16->Single(Char.to_intConstants.map16_header)|Map32->Single(Char.to_intConstants.map32_header)|Negative_fixint->Range(Constants.negative_fixint_mask,0xff);;(* The starting byte of this kind's pattern. Used to sort the match arms in ascending
tag-byte order. *)letheadert=matchpatterntwith|Singleb->b|Range(lo,_)->lo;;(* The parser sub-expression to invoke in this kind's match arm.
For most kinds this is an identifier like ["bin8"] that returns some raw OCaml value
to be wrapped by [constructor_name]. For the kinds whose parser already returns a
[Message.t] (like [nil], [uint64]) or a raw [fail] ([Reserved_c1]), this is the full
arm body and [constructor_name] is [None]. *)letparser_name=function|Positive_fixint->"positive_fixint"|Fixmap->"fixmap msg"|Fixarray->"fixarray msg"|Fixstr->"fixstr"|Nil->"nil"|Reserved_c1->{|fail "msgpack: reserved byte 0xc1"|}|False_->"false_"|True_->"true_"|Bin8->"bin8"|Bin16->"bin16"|Bin32->"bin32"|Ext8->"ext8"|Ext16->"ext16"|Ext32->"ext32"|Float32->"float"|Float64->"double"|Uint8->"uint8"|Uint16->"uint16"|Uint32->"uint32"|Uint64->"uint64"|Int8->"int8"|Int16->"int16"|Int32->"int32"|Int64->"int64"|Fixext1->"fixext1"|Fixext2->"fixext2"|Fixext4->"fixext4"|Fixext8->"fixext8"|Fixext16->"fixext16"|Str8->"str8"|Str16->"str16"|Str32->"str32"|Array16->"array16 msg"|Array32->"array32 msg"|Map16->"map16 msg"|Map32->"map32 msg"|Negative_fixint->"negative_fixint";;(* The [Message.t] constructor used to wrap [parser_name]'s result. [None] means the arm
body is just [parser_name] as-is (the parser already returns a [Message.t], or is a
raw [fail]). *)letconstructor_name=function|Nil|Reserved_c1|False_|True_|Uint64|Int64->None|Positive_fixint|Uint8|Uint16|Uint32|Int8|Int16|Int32|Negative_fixint->Some"Int"|Float32|Float64->Some"Float"|Fixmap|Map16|Map32->Some"Map"|Fixarray|Array16|Array32->Some"Array"|Fixstr|Str8|Str16|Str32->Some"String"|Bin8|Bin16|Bin32->Some"Binary"|Ext8|Ext16|Ext32|Fixext1|Fixext2|Fixext4|Fixext8|Fixext16->Some"Ext";;endletpattern_to_string:Pattern.t->string=lethexb=Printf.sprintf"'\\x%02x'"binfunction|Singleb->hexb|Range(lo,hi)->[%string"%{hexlo} .. %{hexhi}"];;(* All formats sorted in ascending tag-byte order. This is the order we want the match
arms in. *)letsorted_formats=lazy(List.sortFormat.all~compare:(Comparable.liftInt.compare~f:Format.header));;let%expect_test"Sanity check that there are no gaps and overlaps in the parser ranges"=letcovers=List.fold(forcesorted_formats)~init:0~f:(funnextkind->letlo,hi=matchFormat.patternkindwith|Singleb->b,b|Range(lo,hi)->lo,hiinmatchInt.equalnextlowith|false->raise_s[%message"Msgpack_cinaps: kinds do not partition the byte range"~expected_next_byte:(next:int)~got:(lo:int)~pattern:(pattern_to_string(Format.patternkind):string)]|true->hi+1)inmatchInt.equalcovers0x100with|true->()|false->raise_s[%message"Msgpack_cinaps: kinds do not cover all 256 tag bytes"(covers:int)];;letprint_parser_match()=(* We print the whole function definition rather than just the match arms so the cinaps
block can live at column 0 (cinaps does not indent the generated output, so nesting
the block would leave each arm at the wrong column). The initial and trailing blank
lines separate the output from the surrounding [(*$ ... *)] and [(*$*)] markers,
matching how ocamlformat likes to format around them. *)print_endline"";print_endline"let parse_by_tag msg c =";print_endline" match c with";List.iter(forcesorted_formats)~f:(funkind->letpat=pattern_to_string(Format.patternkind)inletparser_name=Format.parser_namekindinmatchFormat.constructor_namekindwith|None->print_endline[%string" | %{pat} -> %{parser_name}"]|Someconstructor->print_endline[%string" | %{pat} ->"];print_endline[%string" let%map v = %{parser_name} in"];print_endline[%string" %{constructor} v"]);print_endline";;";print_endline"";;