Data_encoding: add bounded strings and bytes

This commit is contained in:
Milo Davis 2018-05-16 08:46:01 -07:00 committed by Grégoire Henry
parent c2241c034a
commit 7fc74da1a2
9 changed files with 76 additions and 0 deletions

View File

@ -40,6 +40,7 @@ let signed_range_to_size min max : [> signed_integer ] =
(* max should be centered at zero *) (* max should be centered at zero *)
let unsigned_range_to_size max : [> unsigned_integer ] = let unsigned_range_to_size max : [> unsigned_integer ] =
assert (max >= 0) ;
if max <= 255 if max <= 255
then `Uint8 then `Uint8
else if max <= 65535 else if max <= 65535

View File

@ -37,5 +37,6 @@ val min_int: [< integer ] -> int
val max_int: [< integer ] -> int val max_int: [< integer ] -> int
val range_to_size: minimum:int -> maximum:int -> integer val range_to_size: minimum:int -> maximum:int -> integer
val unsigned_range_to_size: int -> unsigned_integer
val enum_size: 'a array -> [> unsigned_integer ] val enum_size: 'a array -> [> unsigned_integer ]

View File

@ -15,10 +15,57 @@ struct
let json = Json_encoding.assoc (Json.convert enc) in let json = Json_encoding.assoc (Json.convert enc) in
let binary = list (tup2 string enc) in let binary = list (tup2 string enc) in
raw_splitted ~json ~binary raw_splitted ~json ~binary
module Bounded = struct
let string length =
raw_splitted
~binary: begin
let kind = Binary_size.unsigned_range_to_size length in
check_size (length + Binary_size.integer_to_size kind) @@
dynamic_size ~kind Variable.string
end
~json: begin
let open Json_encoding in
conv
(fun s ->
if String.length s > length then invalid_arg "oversized string" ;
s)
(fun s ->
if String.length s > length then
raise (Cannot_destruct ([], Invalid_argument "oversized string")) ;
s)
string
end
let bytes length =
raw_splitted
~binary: begin
let kind = Binary_size.unsigned_range_to_size length in
check_size (length + Binary_size.integer_to_size kind) @@
dynamic_size ~kind Variable.bytes
end
~json: begin
let open Json_encoding in
conv
(fun s ->
if MBytes.length s > length then invalid_arg "oversized string" ;
s)
(fun s ->
if MBytes.length s > length then
raise (Cannot_destruct ([], Invalid_argument "oversized string")) ;
s)
Json.bytes_jsont
end
end
end end
include Encoding include Encoding
module Json = Json module Json = Json
module Bson = Bson module Bson = Bson
module Binary = struct module Binary = struct

View File

@ -412,6 +412,18 @@ module Encoding: sig
val list : 'a encoding -> 'a list encoding val list : 'a encoding -> 'a list encoding
end end
module Bounded : sig
(** Encoding of a string whose length does not exceed the specified length
Attempting to construct a string with a length that is too long causes
an invalid_argument exception, however the size field will use the minimum
integer that can accomidate the maximum size.
- default variable in width
- encoded as a byte sequence in binary
- encoded as a string in JSON. *)
val string : int -> string encoding
val bytes : int -> MBytes.t encoding
end
(** Mark an encoding as being of dynamic size. (** Mark an encoding as being of dynamic size.
Forces the size to be stored alongside content when needed. Forces the size to be stored alongside content when needed.
Typically used to combine two variable encodings in a same Typically used to combine two variable encodings in a same

View File

@ -52,3 +52,5 @@ val from_string : string -> (json, string) result
val from_stream : string Lwt_stream.t -> (json, string) result Lwt_stream.t val from_stream : string Lwt_stream.t -> (json, string) result Lwt_stream.t
val to_string : ?minify:bool -> json -> string val to_string : ?minify:bool -> json -> string
val pp : Format.formatter -> json -> unit val pp : Format.formatter -> json -> unit
val bytes_jsont: MBytes.t Json_encoding.encoding

View File

@ -175,8 +175,10 @@ let tests =
all_ranged_float ~-. 100. 300. @ all_ranged_float ~-. 100. 300. @
all "string.fixed" ~expected:invalid_string_length all "string.fixed" ~expected:invalid_string_length
string (Fixed.string 4) "turlututu" @ string (Fixed.string 4) "turlututu" @
all "string.bounded" string (Bounded.string 4) "turlututu" @
all "bytes.fixed" ~expected:invalid_string_length all "bytes.fixed" ~expected:invalid_string_length
bytes (Fixed.bytes 4) (MBytes.of_string "turlututu") @ bytes (Fixed.bytes 4) (MBytes.of_string "turlututu") @
all "bytes.bounded" bytes (Bounded.bytes 4) (MBytes.of_string "turlututu") @
all "unknown_case.B" ~expected:missing_case union_enc mini_union_enc (B "2") @ all "unknown_case.B" ~expected:missing_case union_enc mini_union_enc (B "2") @
all "unknown_case.E" ~expected:missing_case union_enc mini_union_enc E @ all "unknown_case.E" ~expected:missing_case union_enc mini_union_enc E @
all "enum.missing" ~expected:missing_enum enum_enc mini_enum_enc 4 @ all "enum.missing" ~expected:missing_enum enum_enc mini_enum_enc 4 @

View File

@ -163,11 +163,15 @@ let tests =
all "string" Alcotest.string string "tutu" @ all "string" Alcotest.string string "tutu" @
all "string.fixed" Alcotest.string (Fixed.string 4) "tutu" @ all "string.fixed" Alcotest.string (Fixed.string 4) "tutu" @
all "string.variable" Alcotest.string Variable.string "tutu" @ all "string.variable" Alcotest.string Variable.string "tutu" @
all "string.bounded1" Alcotest.string (Bounded.string 4) "tu" @
all "string.bounded2" Alcotest.string (Bounded.string 4) "tutu" @
all "bytes" Alcotest.bytes bytes (MBytes.of_string "titi") @ all "bytes" Alcotest.bytes bytes (MBytes.of_string "titi") @
all "bytes.fixed" Alcotest.bytes (Fixed.bytes 4) all "bytes.fixed" Alcotest.bytes (Fixed.bytes 4)
(MBytes.of_string "titi") @ (MBytes.of_string "titi") @
all "bytes.variable" Alcotest.bytes Variable.bytes all "bytes.variable" Alcotest.bytes Variable.bytes
(MBytes.of_string "titi") @ (MBytes.of_string "titi") @
all "bytes.bounded1" Alcotest.bytes (Bounded.bytes 4) (MBytes.of_string "tu") @
all "bytes.bounded2" Alcotest.bytes (Bounded.bytes 4) (MBytes.of_string "tutu") @
all "float" Alcotest.float float 42. @ all "float" Alcotest.float float 42. @
all "float.max" Alcotest.float float max_float @ all "float.max" Alcotest.float float max_float @
all "float.min" Alcotest.float float min_float @ all "float.min" Alcotest.float float min_float @

View File

@ -75,7 +75,9 @@ let tests =
all_ranged_int ~-300_000_000 300_000_000 @ all_ranged_int ~-300_000_000 300_000_000 @
all_ranged_float ~-. 100. 300. @ all_ranged_float ~-. 100. 300. @
all "string.fixed" (Fixed.string 4) "turlututu" @ all "string.fixed" (Fixed.string 4) "turlututu" @
all "string.bounded" (Bounded.string 4) "turlututu" @
all "bytes.fixed" (Fixed.bytes 4) (MBytes.of_string "turlututu") @ all "bytes.fixed" (Fixed.bytes 4) (MBytes.of_string "turlututu") @
all "bytes.bounded" (Bounded.bytes 4) (MBytes.of_string "turlututu") @
all "unknown_case.B" mini_union_enc (B "2") @ all "unknown_case.B" mini_union_enc (B "2") @
all "unknown_case.E" mini_union_enc E @ all "unknown_case.E" mini_union_enc E @
test_bounded_string_list @ test_bounded_string_list @

View File

@ -57,6 +57,11 @@ module Variable : sig
val list : 'a encoding -> 'a list encoding val list : 'a encoding -> 'a list encoding
end end
module Bounded : sig
val string : int -> string encoding
val bytes : int -> MBytes.t encoding
end
val dynamic_size : val dynamic_size :
?kind: [ `Uint30 | `Uint16 | `Uint8 ] -> ?kind: [ `Uint30 | `Uint16 | `Uint8 ] ->
'a encoding -> 'a encoding 'a encoding -> 'a encoding