Plasma GitLab Archive
Projects Blog Knowledge

(* $Id: netamqp_rtypes.ml 53347 2011-03-01 00:38:28Z gerd $ *)

open Netamqp_types

type uint2 = int

type ('table_field,'table) table_field_standard =
    [ `Sint4 of Rtypes.int4
    | `Decimal of int * Rtypes.uint4
    | `Longstr of string  (* up to 4G chars *)
    | `Timestamp of float  (* only int precision *)
    | `Table of 'table
    | `Null
    ]

type ('table_field,'table) table_field_ok =
    [ `Bool of bool
    | `Sint1 of int
    | `Float of float   (* single precision only *)
    | `Double of float
    ]

type ('table_field,'table) table_field_problematic =
    [ `Uint1 of int
    | `Sint2 of int
    | `Uint2 of int
    | `Uint4 of Rtypes.uint4
    | `Sint8 of Rtypes.int8
    | `Uint8 of Rtypes.uint8
    | `Shortstr of string (* up to 255 chars *)
    | `Array of 'table_field list
    ]

type table_field =
    [ (table_field,table) table_field_standard
    | (table_field,table) table_field_ok
    (* | table_field_problematic *)
    ]

and table =
    (string * table_field) list


let read_uint2_unsafe s p =
  let c1 = String.unsafe_get s p in
  let c0 = String.unsafe_get s (p+1) in
  ((Char.code c1) lsl 8) lor (Char.code c0)


let read_uint2 s p =
  let l = String.length s in
  if p < 0 || p > l-2 then
    invalid_arg "Netamqp_rtypes.read_uint2";
  read_uint2_unsafe s p


let write_uint2_unsafe s p x =
  String.unsafe_set s p (Char.unsafe_chr ((x lsr 8) land 0xff));
  String.unsafe_set s (p+1) (Char.unsafe_chr (x land 0xff))

let write_uint2 s p x =
  let l = String.length s in
  if p < 0 || p > l-2 || x < 0 || x > 65535 then
    invalid_arg "Netamqp_rtypes.write_uint2";
  write_uint2_unsafe s p x

let uint2_as_string x =
  let s = String.create 2 in
  write_uint2 s 0 x;
  s


let decode_shortstr s c l =
  assert(String.length s >= l);
  if !c >= l then raise(Decode_error "Message too short");
  let n = Char.code(String.unsafe_get s !c) in
  if !c >= l - n then raise(Decode_error "Message too short");
  let u = String.sub s (!c+1) n in
  c := !c + n + 1;
  u


let encode_shortstr s =
  let n = String.length s in
  if n > 255 then raise(Encode_error "String too long (shortstr)");
  let p = String.make 1 (Char.unsafe_chr n) in
  ( [s; p], n+1 )

let encode_shortstr_straight s =
  let n = String.length s in
  if n > 255 then raise(Encode_error "String too long (shortstr)");
  let p = String.make 1 (Char.unsafe_chr n) in
  ( [p; s] (* ! *), n+1 )

let encode_shortstr_for_field s =
  let n = String.length s in
  if n > 255 then raise(Encode_error "String too long (shortstr)");
  let p = String.create 2 in
  String.unsafe_set p 0 's';
  String.unsafe_set p 1 (Char.unsafe_chr n);
  ( [p; s], n+2 )


let decode_longstr_nocopy s c l =
  assert(String.length s >= l);
  if !c >= l - 3 then raise(Decode_error "Message too short");
  let n_rt = Rtypes.read_uint4_unsafe s !c in
  let n =
    try Rtypes.int_of_uint4 n_rt
    with Rtypes.Cannot_represent _ ->
      raise(Decode_error "Cannot represent field because it is too long") in
  if !c >= l - n - 3 then raise(Decode_error "Message too short");
  let p = !c+4 in
  c := !c + n + 4;
  (p, n)


let decode_longstr s c l =
  let (p,n) = decode_longstr_nocopy s c l in
  String.sub s p n


let encode_longstr s =
  let n = String.length s in
  let n_rt =
    try Rtypes.uint4_of_int n
    with _ -> raise(Encode_error "String too long (longstr)") in
  let p = Rtypes.uint4_as_string n_rt in
  ( [s; p], n+4 )


let encode_longstr_for_field s =
  let n = String.length s in
  let n_rt =
    try Rtypes.uint4_of_int n
    with _ -> raise(Encode_error "String too long (longstr)") in
  let p = String.create 5 in
  String.unsafe_set p 0 'S';
  Rtypes.write_uint4_unsafe p 1 n_rt;
  ( [p; s], n+5 )


let rec parse_table s c l =
  let rec next_field() =
    if !c < l then (
      let name = decode_shortstr s c l in
      let v = decode_field s c l in
      (name, v) :: next_field()
    )
    else
      [] 
  in
  next_field()


and decode_field s c l : table_field =
  let expect n =
    if !c >= l - n + 1 then
      raise(Decode_error "Message too short") in

  expect 1;
  let t = s.[ !c ] in
  incr c;
  match t with
    | 't' ->
	expect 1;
	let v = `Bool(s.[!c] <> '\000') in
	incr c;
	v
    | 'b' ->
	expect 1;
	let x = Char.code s.[!c] in
	let v = if x >= 128 then `Sint1(x - 256) else `Sint1 x in
	incr c;
	v
(*
    | 'B' ->
	expect 1;
	let x = Char.code s.[!c] in
	let v = `Uint1 x in
	incr c;
	v
    | 'U' ->
	expect 2;
	let x = read_uint2_unsafe s !c in
	let v = if x >= 32768 then `Sint2(x - 65536) else `Sint2 x in
	c := !c + 2;
	v
    | 'u' ->
	expect 2;
	let x = read_uint2_unsafe s !c in
	let v = `Uint2 x in
	c := !c + 2;
	v
 *)
    | 'I' ->
	expect 4;
	let x = Rtypes.read_int4_unsafe s !c in
	let v = `Sint4 x in
	c := !c + 4;
	v
(*
    | 'i' ->
	expect 4;
	let x = Rtypes.read_uint4_unsafe s !c in
	let v = `Uint4 x in
	c := !c + 4;
	v
    | 'L' ->
	expect 8;
	let x = Rtypes.read_int8_unsafe s !c in
	let v = `Sint8 x in
	c := !c + 8;
	v
    | 'l' ->
	expect 8;
	let x = Rtypes.read_uint8_unsafe s !c in
	let v = `Uint8 x in
	c := !c + 8;
	v
 *)
    | 'f' ->
	expect 4;
	let x = Rtypes.float_of_fp4(Rtypes.read_fp4 s !c) in
	let v = `Float x in
	c := !c + 4;
	v
    | 'd' ->
	expect 8;
	let x = Rtypes.float_of_fp8(Rtypes.read_fp8 s !c) in
	let v = `Double x in
	c := !c + 8;
	v
    | 'D' ->
	expect 5;
	let scale = Char.code s.[!c] in
	let x = Rtypes.read_uint4_unsafe s (!c+1) in
	let v = `Decimal(scale,x) in
	c := !c + 5;
	v
(*
    | 's' ->
	let x = decode_shortstr s c l in
	let v = `Shortstr x in
	v
 *)
    | 'S' -> 
	let x = decode_longstr s c l in
	let v = `Longstr x in
	v
(*
    | 'A' ->
	let x = decode_array s c l in
	let v = `Array x in
	v
 *)
    | 'T' ->
	expect 8;
	let x = Rtypes.read_uint8_unsafe s !c in
	let t =
	  try Int64.to_float(Rtypes.int64_of_uint8 x)
	  with _ ->
	    raise(Decode_error "Timestamp out of supported range") in
	let v = `Timestamp t in
	c := !c + 8;
	v
    | 'F' ->
	let x = decode_table s c l in
	let v = `Table x in
	v
    | 'V' ->
	`Null
    | _ ->
	raise(Decode_error "Bad field type in table")
	  
and decode_array s c l =
  let (p,n) = decode_longstr_nocopy s c l in
  let c' = ref p in
  let acc = ref [] in
  while !c' < !c do
    let v = decode_field s c' !c in
    acc := v :: !acc
  done;
  List.rev !acc

and decode_table s c l =
  let (p,n) = decode_longstr_nocopy s c l in
  let c' = ref p in
  let t = parse_table s c' !c in
  if !c <> !c' then
    raise(Decode_error "Table does not fit into field");
  t


let rec encode_field field =
  (* Note: the list is built in the right order! *)
  match field with
    | `Bool b ->
	(if b then ["t\001"] else ["t\000"]), 2
    | `Sint1 x ->
	if x < (-128) || x > 127 then
	  raise(Encode_error "Value out of range (Sint1)");
	let s = String.create 2 in
	String.unsafe_set s 0 'b';
	String.unsafe_set s 1
	  (Char.unsafe_chr (if x < 0 then x+256 else x));
	([s], 2)
(*
    | `Uint1 x ->
	if x < 0 || x > 255 then
	  raise(Encode_error "Value out of range (Uint1)");
	let s = String.create 2 in
	String.unsafe_set s 0 'B';
	String.unsafe_set s 1 (Char.unsafe_chr x);
	([s], 2)
    | `Sint2 x ->
	if x < (-32768) || x > 32767 then
	  raise(Encode_error "Value out of range (Sint2)");
	let s = String.create 3 in
	String.unsafe_set s 0 'U';
	write_uint2_unsafe s 1 (if x < 0 then x + 65536 else x);
	([s], 3)
    | `Uint2 x ->
	if x < 0 || x > 65535 then
	  raise(Encode_error "Value out of range (Uint2)");
	let s = String.create 3 in
	String.unsafe_set s 0 'u';
	write_uint2_unsafe s 1 x;
	([s], 3)
 *)
     | `Sint4 x ->
	let s = String.create 5 in
	String.unsafe_set s 0 'I';
	Rtypes.write_int4_unsafe s 1 x;
	([s], 5)
(*
    | `Uint4 x ->
	let s = String.create 5 in
	String.unsafe_set s 0 'i';
	Rtypes.write_uint4_unsafe s 1 x;
	([s], 5)
    | `Sint8 x ->
	let s = String.create 9 in
	String.unsafe_set s 0 'L';
	Rtypes.write_int8_unsafe s 1 x;
	([s], 9)
    | `Uint8 x ->
	let s = String.create 9 in
	String.unsafe_set s 0 'l';
	Rtypes.write_uint8_unsafe s 1 x;
	([s], 9)
 *)
    | `Float x ->
	let s = "f" ^ Rtypes.fp4_as_string (Rtypes.fp4_of_float x) in
	([s], 5)
    | `Double x ->
	let s = "d" ^ Rtypes.fp8_as_string (Rtypes.fp8_of_float x) in
	([s], 9)
    | `Decimal(scale, x) ->
	if scale < 0 || scale > 255 then
	  raise(Encode_error "Value out of range (Decimal)");
	let s = String.create 6 in
	String.unsafe_set s 0 'D';
	String.unsafe_set s 1 (Char.unsafe_chr scale);
	Rtypes.write_uint4_unsafe s 2 x;
	([s], 6)
(*
    | `Shortstr x ->
	encode_shortstr_for_field x
 *)
    | `Longstr x ->
	encode_longstr_for_field x
(*
    | `Array x ->
	let len = ref 0 in
	let x' = 
	  List.flatten
	    (List.map 
	       (fun xe ->
		  let (l,n) = encode_field xe in
		  len := !len + n;
		  l
	       )
	       x
	    ) in
	let s = String.create 5 in
	String.unsafe_set s 0 'A';
	Rtypes.write_uint4_unsafe s 1 (Rtypes.uint4_of_int !len);
	( s :: x', !len + 5 )
 *)
    | `Timestamp x ->
	let s = String.create 9 in
	String.unsafe_set s 0 'T';
	Rtypes.write_uint8_unsafe s 1
	  ( try
	      (Rtypes.uint8_of_int64 (Int64.of_float x))
	    with
	      | _ -> raise(Encode_error "Cannot represent timestamp")
	  );
	([s], 9)
    | `Table x ->
	let (l, n) = encode_table_straight x in
	("F" :: l, n+1)
    | `Null ->
	(["V"], 1)

and encode_table_straight x =
  let n = ref 0 in
  let l =
    List.flatten
      (List.map
	 (fun (name, xe) ->
	    let (l1, n1) = encode_shortstr_straight name in
	    let (l2, n2) = encode_field xe in
	    n := !n + n1 + n2;
	    l1 @ l2
	 )
	 x 
      ) in
  let p = Rtypes.uint4_as_string (Rtypes.uint4_of_int !n) in
  (p :: l, !n + 4)


let encode_table x =
  let (l,n) = encode_table_straight x in
  (List.rev l, n)


let mk_mstring s =
  Xdr_mstring.string_based_mstrings # create_from_string
    s 0 (String.length s) false


let unsafe_rev_concat l n =
  let s = String.create n in
  let k = ref n in
  List.iter
    (fun x ->
       let p = String.length x in
       k := !k - p;
       String.unsafe_blit x 0 s !k p
    )
    l;
  assert(!k = 0);
  s

This web site is published by Informatikbüro Gerd Stolpmann
Powered by Caml