aboutsummaryrefslogblamecommitdiffstats
path: root/subsystem/karuta/Translator.ml
blob: 5dec2e77366c2d4d1ab518e3eac98d57b99dd0a1 (plain) (tree)
1
2
3
4
5
6
7
8



                         
 
                    

                                                                                              






                                                              
























                                                                                

                   
                      














                                                                   














                                                                                               



















                                                           

                                                                         


                                                               
                     
      






                                                                       
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 ())