package cbor
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file CBOR.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(** CBOR encoder/decoder, RFC 7049 *) open Printf module BE = EndianBytes.BigEndian_unsafe module SE = EndianString.BigEndian_unsafe exception Error of string let (@@) f x = f x let (|>) x f = f x let list_iteri f l = let i = ref 0 in List.iter (fun x -> f !i x; incr i) l let fail fmt = ksprintf (fun s -> raise (Error s)) fmt module Encode = struct let start () = Buffer.create 10 let init b ~maj add = assert (maj >= 0 && maj < 8); assert (add >= 0 && add < 32); Buffer.add_char b @@ char_of_int @@ (maj lsl 5) lor add let put_n b n f x = let s = Bytes.create n in f s 0 x; Buffer.add_string b (Bytes.unsafe_to_string s) let max_uint32 = match Sys.word_size with | 32 -> max_int (* max signed int, but on 32-bit this is enough *) | _ -> int_of_string "0xFF_FF_FF_FF" (* so that it compiles on 32-bit *) let put b ~maj n = assert (n >= 0); if n < 24 then init b ~maj n else if n < 256 then begin init b ~maj 24; Buffer.add_char b @@ char_of_int n end else if n < 65536 then begin init b ~maj 25; put_n b 2 BE.set_int16 n end else if n <= max_uint32 then begin init b ~maj 26; put_n b 4 BE.set_int32 @@ Int32.of_int n end else begin init b ~maj 27; put_n b 8 BE.set_int64 @@ Int64.of_int n end let int b n = let (maj,n) = if n < 0 then 1, -1 - n else 0, n in put b ~maj n let hex_char x = assert (x >= 0 && x < 16); if x <= 9 then Char.chr @@ Char.code '0' + x else Char.chr @@ Char.code 'a' + x - 10 let to_hex s = let r = Bytes.create (String.length s * 2) in for i = 0 to String.length s - 1 do Bytes.set r (i*2) @@ hex_char @@ Char.code s.[i] lsr 4; Bytes.set r (i*2+1) @@ hex_char @@ Char.code s.[i] land 0b1111; done; Bytes.to_string r end module Simple = struct type t = [ `Null | `Undefined | `Simple of int | `Bool of bool | `Int of int | `Float of float | `Bytes of string | `Text of string | `Array of t list | `Map of (t * t) list | `Tag of int * t ] let encode item = let open Encode in let b = start () in let rec write = function | `Null -> put b ~maj:7 22; | `Undefined -> put b ~maj:7 23; | `Bool false -> put b ~maj:7 20; | `Bool true -> put b ~maj:7 21; | `Simple n when (n >= 0 && n <= 23) || (n >= 32 && n <= 255) -> put b ~maj:7 n | `Simple n -> fail "encode: simple(%d)" n | `Int n -> int b n | `Float f -> init b ~maj:7 27; put_n b 8 BE.set_double f | `Bytes s -> put b ~maj:2 (String.length s); Buffer.add_string b s | `Text s -> put b ~maj:3 (String.length s); Buffer.add_string b s | `Array l -> put b ~maj:4 (List.length l); List.iter write l | `Map m -> put b ~maj:5 (List.length m); List.iter (fun (a,b) -> write a; write b) m | `Tag (t, v) -> put b ~maj:6 t; write v in write item; Buffer.contents b let need (s,i) n = if n > String.length s || !i + n > String.length s then fail "truncated: len %d pos %d need %d" (String.length s) !i n; let j = !i in i := !i + n; j let get_byte (s,_ as r) = int_of_char @@ s.[need r 1] let get_n (s,_ as r) n f = f s @@ need r n let get_s (s,_ as r) n = String.sub s (need r n) n let get_additional byte1 = byte1 land 0b11111 let is_indefinite byte1 = get_additional byte1 = 31 let int64_max_int = Int64.of_int max_int let two_min_int32 = 2 * Int32.to_int Int32.min_int let extract_number byte1 r = match get_additional byte1 with | n when n < 24 -> n | 24 -> get_byte r | 25 -> get_n r 2 SE.get_uint16 | 26 -> let n = Int32.to_int @@ get_n r 4 SE.get_int32 in if n < 0 then n - two_min_int32 else n | 27 -> let n = get_n r 8 SE.get_int64 in if n > int64_max_int || n < 0L then fail "extract_number: %Lu" n; Int64.to_int n | n -> fail "bad additional %d" n let get_float16 s i = let half = Char.code s.[i] lsl 8 + Char.code s.[i+1] in let mant = half land 0x3ff in let value = match (half lsr 10) land 0x1f with (* exp *) | 31 when mant = 0 -> infinity | 31 -> nan | 0 -> ldexp (float mant) ~-24 | exp -> ldexp (float @@ mant + 1024) (exp - 25) in if half land 0x8000 = 0 then value else ~-. value exception Break let extract_list byte1 r f = if is_indefinite byte1 then let l = ref [] in try while true do l := f r :: !l done; assert false with Break -> List.rev !l else let n = extract_number byte1 r in Array.to_list @@ Array.init n (fun _ -> f r) let rec extract_pair r = let a = extract r in let b = try extract r with Break -> fail "extract_pair: unexpected break" in a,b and extract_string byte1 r f = if is_indefinite byte1 then let b = Buffer.create 10 in try while true do Buffer.add_string b (f @@ extract r) done; assert false with Break -> Buffer.contents b else let n = extract_number byte1 r in get_s r n and extract r = let byte1 = get_byte r in match byte1 lsr 5 with | 0 -> `Int (extract_number byte1 r) | 1 -> `Int (-1 - extract_number byte1 r) | 2 -> `Bytes (extract_string byte1 r (function `Bytes s -> s | _ -> fail "extract: not a bytes chunk")) | 3 -> `Text (extract_string byte1 r (function `Text s -> s | _ -> fail "extract: not a text chunk")) | 4 -> `Array (extract_list byte1 r extract) | 5 -> `Map (extract_list byte1 r extract_pair) | 6 -> let tag = extract_number byte1 r in let v = extract r in `Tag (tag, v) | 7 -> begin match get_additional byte1 with | n when n < 20 -> `Simple n | 20 -> `Bool false | 21 -> `Bool true | 22 -> `Null | 23 -> `Undefined | 24 -> `Simple (get_byte r) | 25 -> `Float (get_n r 2 get_float16) | 26 -> `Float (get_n r 4 SE.get_float) | 27 -> `Float (get_n r 8 SE.get_double) | 31 -> raise Break | a -> fail "extract: (7,%d)" a end | _ -> assert false let decode_partial s = let i = ref 0 in let x = try extract (s,i) with Break -> fail "decode: unexpected break" in x, String.sub s !i (String.length s - !i) let decode s : t = let x, rest = decode_partial s in if rest = "" then x else fail "decode: extra data: len %d pos %d" (String.length s) (String.length s - String.length rest) let to_diagnostic item = let b = Buffer.create 10 in let put = Buffer.add_string b in let rec write = function | `Null -> put "null" | `Bool false -> put "false" | `Bool true -> put "true" | `Simple n -> bprintf b "simple(%d)" n | `Undefined -> put "undefined" | `Int n -> bprintf b "%d" n | `Float f -> begin match classify_float f with | FP_nan -> put "NaN" | FP_infinite -> put (if f < 0. then "-Infinity" else "Infinity") | FP_zero | FP_normal | FP_subnormal -> put (Float.to_string f) end | `Bytes s -> bprintf b "h'%s'" (Encode.to_hex s) | `Text s -> bprintf b "\"%s\"" s | `Array l -> put "["; l |> list_iteri (fun i x -> if i <> 0 then put ", "; write x); put "]" | `Map m -> put "{"; m |> list_iteri (fun i (k,v) -> if i <> 0 then put ", "; write k; put ": "; write v); put "}" | `Tag (t, v) -> bprintf b "%i(" t; write v; put ")" in write item; Buffer.contents b end (* Simple *)