package bare_encoding

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file bare_encoding.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
module String_map = Map.Make (String)

module type INPUT = sig
  val read_byte : unit -> char
  val read_i16 : unit -> int
  val read_i32 : unit -> int32
  val read_i64 : unit -> int64
  val read_exact : bytes -> int -> int -> unit
end

type input = (module INPUT)

let input_of_bytes ?(off = 0) ?len (b : bytes) : input =
  let off = ref off in
  let len =
    match len with
    | None -> Bytes.length b - !off
    | Some l -> l
  in
  if !off + len > Bytes.length b then invalid_arg "input_of_bytes";
  let[@inline] check_ n =
    if !off + n > len then invalid_arg "input exhausted"
  in
  let module M = struct
    let read_byte () =
      check_ 1;
      let c = Bytes.get b !off in
      incr off;
      c

    let read_i16 () =
      check_ 2;
      let r = Bytes.get_int16_le b !off in
      off := !off + 2;
      r

    let read_i32 () =
      check_ 4;
      let r = Bytes.get_int32_le b !off in
      off := !off + 4;
      r

    let read_i64 () =
      check_ 8;
      let r = Bytes.get_int64_le b !off in
      off := !off + 8;
      r

    let read_exact into i len =
      check_ len;
      Bytes.blit b !off into i len;
      off := !off + len
  end in
  (module M)

module Decode = struct
  type t = input

  let[@inline] of_input (i : input) : t = i
  let of_bytes ?off ?len b = of_input (input_of_bytes ?off ?len b)
  let of_string ?off ?len s = of_bytes ?off ?len (Bytes.unsafe_of_string s)

  type 'a dec = t -> 'a

  let uint (self : t) : int64 =
    let rec loop () =
      let c =
        let (module M) = self in
        M.read_byte ()
      in
      let c = Char.code c in
      if c land 0b1000_0000 <> 0 then (
        let rest = loop () in
        let c = Int64.of_int (c land 0b0111_1111) in
        Int64.(logor (shift_left rest 7) c)
      ) else
        Int64.of_int c (* done *)
    in
    loop ()

  let int (self : t) : int64 =
    let open Int64 in
    let i = uint self in
    let sign_bit = logand 0b1L i in
    (* true if negative *)
    let sign = equal sign_bit 0L in
    let res =
      if sign then
        shift_right_logical i 1
      else
        (* put sign back *)
        logor (shift_left 1L 63) (shift_right_logical (lognot i) 1)
    in
    res

  let i8 (self : t) : char =
    let (module M) = self in
    M.read_byte ()

  let u8 = i8

  let i16 (self : t) =
    let (module M) = self in
    M.read_i16 ()

  let u16 = i16

  let i32 (self : t) =
    let (module M) = self in
    M.read_i32 ()

  let u32 = i32

  let i64 (self : t) =
    let (module M) = self in
    M.read_i64 ()

  let u64 = i64

  let[@inline] bool self : bool =
    let c = i8 self in
    Char.code c <> 0

  let f32 (self : t) : float =
    let i = i32 self in
    Int32.float_of_bits i

  let f64 (self : t) : float =
    let i = i64 self in
    Int64.float_of_bits i

  let data_of ~size (self : t) : bytes =
    let b = Bytes.create size in
    let (module M) = self in
    M.read_exact b 0 size;
    b

  let data self : bytes =
    let size = uint self in
    if Int64.compare size (Int64.of_int Sys.max_string_length) > 0 then
      invalid_arg "Decode.data: string too large";
    let size = Int64.to_int size in
    (* fits, because of previous test *)
    data_of ~size self

  let string self : string = Bytes.unsafe_to_string (data self)

  let[@inline] optional dec self : _ option =
    let c = u8 self in
    if Char.code c = 0 then
      None
    else
      Some (dec self)
end

module type OUTPUT = sig
  val write_byte : char -> unit
  val write_i16 : int -> unit
  val write_i32 : int32 -> unit
  val write_i64 : int64 -> unit
  val write_exact : bytes -> int -> int -> unit
  val flush : unit -> unit
end

type output = (module OUTPUT)

let output_of_buffer (buf : Buffer.t) : output =
  let module M = struct
    let[@inline] write_byte c = Buffer.add_char buf c
    let[@inline] write_i16 c = Buffer.add_int16_le buf c
    let[@inline] write_i32 c = Buffer.add_int32_le buf c
    let[@inline] write_i64 c = Buffer.add_int64_le buf c
    let write_exact b i len = Buffer.add_subbytes buf b i len
    let flush _ = ()
  end in
  (module M)

module Encode = struct
  type t = output

  let[@inline] of_output (o : output) : t = o
  let[@inline] of_buffer buf : t = of_output @@ output_of_buffer buf

  type 'a enc = t -> 'a -> unit

  (* no need to check for overflow below *)
  external unsafe_chr : int -> char = "%identity"

  let uint (self : t) (i : int64) : unit =
    let module I = Int64 in
    let i = ref i in
    let continue = ref true in
    while !continue do
      let j = I.logand 0b0111_1111L !i in
      if !i = j then (
        continue := false;
        let j = I.to_int j in
        let (module M) = self in
        M.write_byte (unsafe_chr j)
      ) else (
        (* set bit 8 to [1] *)
        let lsb = I.to_int (I.logor 0b1000_0000L j) in
        let lsb = unsafe_chr lsb in
        let (module M) = self in
        M.write_byte lsb;
        i := I.shift_right_logical !i 7
      )
    done

  let[@inline] int (self : t) i =
    let open Int64 in
    let ui = logxor (shift_left i 1) (shift_right i 63) in
    uint self ui

  let[@inline] i8 (self : t) x =
    let (module M) = self in
    M.write_byte x

  let u8 = i8

  let[@inline] i16 (self : t) x =
    let (module M) = self in
    M.write_i16 x

  let u16 = i16

  let[@inline] i32 (self : t) x =
    let (module M) = self in
    M.write_i32 x

  let u32 = i32

  let[@inline] i64 (self : t) x =
    let (module M) = self in
    M.write_i64 x

  let u64 = i64

  let bool self x =
    i8 self
      (if x then
        Char.chr 1
      else
        Char.chr 0)

  let f64 (self : t) x = i64 self (Int64.bits_of_float x)

  let data_of ~size (self : t) x =
    if size <> Bytes.length x then failwith "invalid length for Encode.data_of";
    let (module M) = self in
    M.write_exact x 0 size

  let data (self : t) x =
    uint self (Int64.of_int (Bytes.length x));
    let (module M) = self in
    M.write_exact x 0 (Bytes.length x)

  let[@inline] string self x = data self (Bytes.unsafe_of_string x)

  let[@inline] optional enc self x : unit =
    match x with
    | None -> u8 self (Char.chr 0)
    | Some x ->
      u8 self (Char.chr 1);
      enc self x
end

module Pp = struct
  type 'a t = Format.formatter -> 'a -> unit
  type 'a iter = ('a -> unit) -> unit

  let unit out () = Format.pp_print_string out "()"
  let int8 out c = Format.fprintf out "%d" (Char.code c)
  let int out x = Format.fprintf out "%d" x
  let int32 out x = Format.fprintf out "%ld" x
  let int64 out x = Format.fprintf out "%Ld" x
  let float out x = Format.fprintf out "%h" x
  let bool = Format.pp_print_bool
  let string out x = Format.fprintf out "%S" x
  let data out x = string out (Bytes.unsafe_to_string x)

  let option ppelt out x =
    match x with
    | None -> Format.fprintf out "None"
    | Some x -> Format.fprintf out "(Some %a)" ppelt x

  let array ppelt out x =
    Format.fprintf out "[@[";
    Array.iteri
      (fun i x ->
        if i > 0 then Format.fprintf out ";@ ";
        ppelt out x)
      x;
    Format.fprintf out "@]]"

  let iter ppelt out xs =
    Format.fprintf out "[@[";
    let i = ref 0 in
    xs (fun x ->
        if !i > 0 then Format.fprintf out ",@ ";
        incr i;
        ppelt out x);
    Format.fprintf out "@]]"

  let list ppelt out l = iter ppelt out (fun f -> List.iter f l)
end

let to_string (e : 'a Encode.enc) (x : 'a) =
  let buf = Buffer.create 32 in
  e (Encode.of_buffer buf) x;
  Buffer.contents buf

let of_bytes_exn ?off ?len dec b =
  let i = Decode.of_bytes ?off ?len b in
  dec i

let of_bytes ?off ?len dec bs =
  try Ok (of_bytes_exn ?off ?len dec bs) with
  | Invalid_argument e | Failure e -> Error e
  | End_of_file -> Error "end of file"

let of_string_exn ?off ?len dec s =
  of_bytes_exn ?off ?len dec (Bytes.unsafe_of_string s)

let of_string ?off ?len dec s =
  of_bytes ?off ?len dec (Bytes.unsafe_of_string s)

OCaml

Innovation. Community. Security.