diff options
Diffstat (limited to 'subsystem/karuta/Translator.ml')
| -rw-r--r-- | subsystem/karuta/Translator.ml | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/subsystem/karuta/Translator.ml b/subsystem/karuta/Translator.ml new file mode 100644 index 0000000..5dec2e7 --- /dev/null +++ b/subsystem/karuta/Translator.ml @@ -0,0 +1,107 @@ +open Rnt + +module Error = struct + open Concepts.Condition + + let malformed () = + condition "malformed-beam-tuple" + "A term is not {Type, #{Name => Value}} of binaries, and integers that fit an int" empty +end + +let binary buffer s = + Buffer.add_uint8 buffer 109; + Buffer.add_int32_be buffer (Int32.of_int (String.length s)); + Buffer.add_string buffer s + +let integer buffer n = + let rec digits m = if m = 0 then [] else (m land 0xff) :: digits (m lsr 8) in + let digits = digits (abs n) in + Buffer.add_uint8 buffer 110; + Buffer.add_uint8 buffer (List.length digits); + Buffer.add_uint8 buffer (if n < 0 then 1 else 0); + List.iter (Buffer.add_uint8 buffer) digits + +let encode {Concepts.Tuple.type_; attributes} = + let buffer = Buffer.create 64 in + Buffer.add_uint8 buffer 131; + Buffer.add_uint8 buffer 104; + Buffer.add_uint8 buffer 2; + binary buffer type_; + Buffer.add_uint8 buffer 116; + Buffer.add_int32_be buffer (Int32.of_int (BatMap.String.cardinal attributes)); + BatMap.String.iter + (fun name value -> + binary buffer name; + match value with + | Concepts.Value.String s -> binary buffer s + | Concepts.Value.Integer n -> integer buffer n ) + attributes; + Buffer.contents buffer + +exception Malformed + +let read arity bytes = + let position = ref 0 in + let take n = + if !position + n > String.length bytes then raise Malformed; + let at = !position in + position := at + n; + at + in + let u8 () = String.get_uint8 bytes (take 1) in + let i32 () = Int32.to_int (String.get_int32_be bytes (take 4)) in + let u32 () = i32 () land 0xffff_ffff in + let expect byte = if u8 () <> byte then raise Malformed in + let binary () = + let n = u32 () in + String.sub bytes (take n) n + in + let big n = + let negative = u8 () = 1 in + if n > 8 then raise Malformed; + let at = take n in + let rec magnitude i m = + if i < 0 then m + else + magnitude (i - 1) + (Int64.logor (Int64.shift_left m 8) (Int64.of_int (String.get_uint8 bytes (at + i)))) + in + let m = magnitude (n - 1) 0L in + let limit = if negative then Int64.neg (Int64.of_int min_int) else Int64.of_int max_int in + if Int64.compare m 0L < 0 || Int64.compare m limit > 0 then raise Malformed; + Int64.to_int (if negative then Int64.neg m else m) + in + let value () = + match u8 () with + | 109 -> Concepts.Value.String (binary ()) + | 97 -> Concepts.Value.Integer (u8 ()) + | 98 -> Concepts.Value.Integer (i32 ()) + | 110 -> Concepts.Value.Integer (big (u8 ())) + | _ -> raise Malformed + in + let rec attributes n map = + if n = 0 then map + else begin + expect 109; + let name = binary () in + let value = value () in + attributes (n - 1) (BatMap.String.add name value map) + end + in + match + expect 131; + expect 104; + expect arity; + let names = List.init (arity - 1) (fun _ -> expect 109; binary ()) in + expect 116; + let attributes = attributes (u32 ()) BatMap.String.empty in + if !position <> String.length bytes then raise Malformed; + names, attributes + with + | read -> Some read + | exception Malformed -> None + +let decode bytes = + match read 2 bytes with + | Some ([type_], attributes) -> Ok {Concepts.Tuple.type_; attributes} + | _ -> Error (Error.malformed ()) |
