Plasma GitLab Archive
Projects Blog Knowledge

(*
 * $Id: xdr.ml 1475 2010-08-30 00:17:29Z gerd $
 *)

(* This is an XDR implementation.
 * See RFC 1014
 *)

open Rtypes;;
open Printf

exception Propagate of string;;

(**********************************************************************)

(* auxiliary stuff: *)

let aux_cmp (ha,sa) (hb,sb) =
  if ha = hb then
    compare sa sb
  else
    ha - hb
;;


let all_distinct_q l =
  (* returns whether all elements of l are distinct *)
  let a =
    Array.map (fun s -> (Hashtbl.hash s), s) (Array.of_list l) in
  Array.sort aux_cmp a;
  let distinct = ref true in
  let k = ref 0 in
  while !distinct && !k < Array.length a - 1 do
    let (ha,sa) = a.( !k ) in
    let (hb,sb) = a.( !k + 1 ) in
    distinct := (ha != hb) && (sa <> sb);
    incr k
  done;
  !distinct
;;


let all_distinct =
  function
      []
    | [_] -> true
    | [a;b] -> a <> b
    | l -> all_distinct_q l
;;


let sub_set_q l1 l2 =
  (* returns whether all elements of l1 occur in l2 *)
  let a1 =
    Array.map (fun s -> (Hashtbl.hash s), s) (Array.of_list l1) in
  let a2 =
    Array.map (fun s -> (Hashtbl.hash s), s) (Array.of_list l2) in
  Array.sort aux_cmp a1;
  Array.sort aux_cmp a2;
  let occurs = ref true in
  let k1 = ref 0 in
  let k2 = ref 0 in
  while !occurs && !k1 < Array.length a1 && !k2 < Array.length a2 do
    let (h1,s1) = a1.( !k1 ) in
    let found = ref false in
    while not !found && !k2 < Array.length a2 do
      let (h2,s2) = a2.( !k2 ) in
      found := (h1 == h2) && (s1 = s2);
      if not !found then incr k2
    done;
    occurs := !found;
    incr k1
  done;
  !occurs
;;


let sub_set l1 l2 =
  match (l1,l2) with
      ([],_) -> true
    | ([x],_) -> List.mem x l2
    | _ -> sub_set_q l1 l2
;;


(* (* currently unused! *)
let equal_sets_q l1 l2 =
  (* returns whether all elements of l1 occur in l2, and vice versa *)
  let a1 =
    Array.map (fun s -> (Hashtbl.hash s), s) (Array.of_list l1) in
  let a2 =
    Array.map (fun s -> (Hashtbl.hash s), s) (Array.of_list l2) in
  Array.sort aux_cmp a1;
  Array.sort aux_cmp a2;
  let equal = ref true in
  let k1 = ref 0 in
  let k2 = ref 0 in
  let k2_match = ref false in
      (* can only be false when !k2 = 0 *)
  while !equal && !k1 < Array.length a1 && !k2 < Array.length a2 do
    let (h1,s1) = a1.( !k1 ) in
    let (h2,s2) = a2.( !k2 ) in
    if (h1 == h2) && (s1 = s2) then (
      incr k1;
      k2_match := true;   (* a match for the k2 element has been found *)
    ) else (
      if !k2_match then (
	incr k2;
	while !k2 < Array.length a2 && (h2,s2) = a2.( !k2 ) do
	  incr k2
	done;
	if !k2 < Array.length a2 then (
	  let (h2',s2') = a2.( !k2 ) in
	  if (h1 == h2') && (s1 = s2') then (
	    incr k1;
	  )
	  else equal := false
	)
	else equal := false
      )
      else equal := false
    )
  done;
  if !equal && !k1 = Array.length a1 && !k2 < Array.length a2 && !k1 > 0 then (
    (* !k1 > 0 ==> a1 is not empty && !k2_match,
     * !k2 < length a2 ==> a2 is not empty
     *)
    let (h2,s2) = a2.( !k2 ) in
    incr k2;
    while !k2 < Array.length a2 && (h2,s2) = a2.( !k2 ) do
      incr k2
    done;
  );
  !equal && !k1 = Array.length a1 && !k2 = Array.length a2
;;


let equal_sets l1 l2 =
  match (l1,l2) with
      ([],[])    -> true
    | ([x],[y])  -> x = y
    | _ -> equal_sets_q l1 l2
;;
*)

(**********************************************************************)
(* definition of XDR types and type systems                           *)
(**********************************************************************)

(* restriction: it is not allowed to have an X_param as enumerator type
 * in union_over_enum. There must always be a real X_enum or, in a
 * type system, a resolvable X_type at this position.
 *)


type xdr_type_term =
    X_int
  | X_uint
  | X_hyper
  | X_uhyper
  | X_enum of (string * int4) list
  | X_float
  | X_double
  | X_opaque_fixed of uint4
  | X_opaque of uint4
  | X_string of uint4
  | X_mstring of string * uint4
  | X_array_fixed of xdr_type_term * uint4
  | X_array of       xdr_type_term * uint4
  | X_struct of (string * xdr_type_term) list
  | X_union_over_int of
      (int4   * xdr_type_term) list * xdr_type_term option
  | X_union_over_uint of
      (uint4  * xdr_type_term) list * xdr_type_term option
  | X_union_over_enum of
      xdr_type_term * (string * xdr_type_term) list * xdr_type_term option
  | X_void
  | X_type of string
  | X_param of string
  | X_rec of (string * xdr_type_term)      (* define a recursive type *)
  | X_refer of string                      (* refer to a recursive type *)
;;


module StringSet = Set.Make(String)
;;


type xdr_type0 =
  { mutable term   : xdr_term;
    mutable params : StringSet.t
      (* "params" is normally only non-empty in the top node *)
  }
and xdr_term =
    T_int
  | T_uint
  | T_hyper
  | T_uhyper
  | T_enum of (string * int32) array
      (* array must be sorted by ascending int32 *)
  | T_float
  | T_double
  | T_opaque_fixed of uint4
  | T_opaque of uint4
  | T_string of uint4
  | T_mstring of string * uint4
  | T_array_fixed of xdr_type0 * uint4
  | T_array of       xdr_type0 * uint4
  | T_struct of (string * xdr_type0) array
  | T_union_over_int of
      (int4, xdr_type0) Hashtbl.t * xdr_type0 option
  | T_union_over_uint of
      (uint4, xdr_type0) Hashtbl.t * xdr_type0 option
  | T_union_over_enum of
      xdr_type0 * xdr_type0 option array * xdr_type0 option
      (* The array corresponds to the T_enum array. None means that the
       * constant is not mapped.
       *)
  | T_void
  | T_param of string
  | T_rec of (string * xdr_type0)
  | T_refer of (string * xdr_type0)
;;

type xdr_type =
    xdr_type0 * xdr_type0
      (* left: includes T_rec and T_refer,
         right: does not include T_rec, T_refer
       *)


type xdr_type_term_system =
  (string * xdr_type_term) list
;;

type xdr_type_system =
  (string * xdr_type) list
  (* export xdr_type_system in an opaque manner *)


let t_name = 
  function
    | T_int -> "T_int"
    | T_uint -> "T_uint"
    | T_hyper -> "T_hyper"
    | T_uhyper -> "T_uhyper"
    | T_enum _ -> "T_enum"
    | T_float -> "T_float"
    | T_double -> "T_double"
    | T_opaque_fixed _ -> "T_opaque_fixed"
    | T_opaque _ -> "T_opaque"
    | T_string _ -> "T_string"
    | T_mstring(_,_) -> "T_mstring"
    | T_array_fixed (_,_) -> "T_array_fixed"
    | T_array (_,_) -> "T_array"
    | T_struct _ -> "T_struct"
    | T_union_over_int(_,_) -> "T_union_over_int"
    | T_union_over_uint(_,_) -> "T_union_over_uint"
    | T_union_over_enum(_,_,_) -> "T_union_over_enum"
    | T_void -> "T_void"
    | T_param _ -> "T_param"
    | T_rec _ -> "T_rec"
    | T_refer _ -> "T_refer"
	

let x_bool =
  X_enum ["FALSE", int4_of_int 0; "TRUE", int4_of_int 1]
;;


let x_optional t =
  X_union_over_enum
    (x_bool,
     ["TRUE", t; "FALSE", X_void],
     None)
;;


let x_opaque_max =
  X_opaque (mk_uint4 ('\255', '\255', '\255', '\255'));;

let x_string_max =
  X_string (mk_uint4 ('\255', '\255', '\255', '\255'));;

let x_mstring_max name =
  X_mstring (name, mk_uint4 ('\255', '\255', '\255', '\255'));;

let x_array_max t =
  X_array (t,  (mk_uint4 ('\255', '\255', '\255', '\255')));;

(**********************************************************************)
(* definition of XDR values                                           *)
(**********************************************************************)

type xdr_value =
    XV_int of int4
  | XV_uint of uint4
  | XV_hyper of int8
  | XV_uhyper of uint8
  | XV_enum of string
  | XV_float of fp4
  | XV_double of fp8
  | XV_opaque of string
  | XV_string of string
  | XV_array of xdr_value array
  | XV_struct of (string * xdr_value) list
  | XV_union_over_int of (int4 * xdr_value)
  | XV_union_over_uint of (uint4 * xdr_value)
  | XV_union_over_enum of (string * xdr_value)
  | XV_void
  | XV_enum_fast of int
  | XV_struct_fast of xdr_value array
  | XV_union_over_enum_fast of (int * xdr_value)
  | XV_array_of_string_fast of string array
  | XV_mstring of Xdr_mstring.mstring
;;

let xv_true = XV_enum_fast 1 (* "TRUE" *);;
let xv_false = XV_enum_fast 0 (*  "FALSE" *);;

let xv_none = XV_union_over_enum_fast (0,XV_void);;
let xv_some v = XV_union_over_enum_fast (1,v);;

exception Dest_failure

let dest_xv_int v =
  match v with XV_int x -> x | _ -> raise Dest_failure;;
let dest_xv_uint v =
  match v with XV_uint x -> x | _ -> raise Dest_failure;;
let dest_xv_hyper v =
  match v with XV_hyper x -> x | _ -> raise Dest_failure;;
let dest_xv_uhyper v =
  match v with XV_uhyper x -> x | _ -> raise Dest_failure;;
let dest_xv_enum v =
  match v with XV_enum x -> x | _ -> raise Dest_failure;;
let dest_xv_enum_fast v =
  match v with XV_enum_fast x -> x | _ -> raise Dest_failure;;
let dest_xv_float v =
  match v with XV_float x -> x | _ -> raise Dest_failure;;
let dest_xv_double v =
  match v with XV_double x -> x | _ -> raise Dest_failure;;
let dest_xv_opaque v =
  match v with XV_opaque x -> x | _ -> raise Dest_failure;;
let dest_xv_string v =
  match v with XV_string x -> x | _ -> raise Dest_failure;;
let dest_xv_mstring v =
  match v with XV_mstring x -> x | _ -> raise Dest_failure;;
let dest_xv_array v =
  match v with XV_array x -> x | _ -> raise Dest_failure;;
let dest_xv_array_of_string_fast v =
  match v with XV_array_of_string_fast x -> x | _ -> raise Dest_failure;;
let dest_xv_struct v =
  match v with XV_struct x -> x | _ -> raise Dest_failure;;
let dest_xv_struct_fast v =
  match v with XV_struct_fast x -> x | _ -> raise Dest_failure;;
let dest_xv_void v =
  match v with XV_void -> () | _ -> raise Dest_failure;;

let dest_xv_union_over_int v =
  match v with XV_union_over_int x -> x | _ -> raise Dest_failure;;

let dest_xv_union_over_uint v =
  match v with XV_union_over_uint x -> x | _ -> raise Dest_failure;;

let dest_xv_union_over_enum v =
  match v with XV_union_over_enum x -> x | _ -> raise Dest_failure;;

let dest_xv_union_over_enum_fast v =
  match v with XV_union_over_enum_fast x -> x | _ -> raise Dest_failure;;


let fail_map_xv_enum_fast () =
  failwith "Xdr.map_xv_enum_fast" ;;

let map_xv_enum_fast0 t v =
  match t.term with
      T_enum l ->
	let m = Array.length l in
	( match v with
	      XV_enum_fast k ->
		if k >= 0 && k < m then
		  snd(Array.unsafe_get l k)
		else
		  fail_map_xv_enum_fast()
	    | XV_enum name ->
		let k = ref 0 in
		while !k < m && (fst l.( !k ) <> name) do
		  incr k
		done;
		if !k >= m then
		  fail_map_xv_enum_fast();
		snd(l.( !k ))
	    | _ ->
		fail_map_xv_enum_fast()
	)
    | _ ->
	fail_map_xv_enum_fast()

let map_xv_enum_fast (_,t) v =
  map_xv_enum_fast0 t v



let fail_map_xv_struct_fast () =
  failwith "Xdr.map_xv_struct_fast" ;;

let map_xv_struct_fast0 t v =
  match t.term with
      T_struct decl ->
	let m = Array.length decl in
	( match v with
	      XV_struct_fast x ->
		let k = Array.length x in
		if k = m then
		  x
		else
		  fail_map_xv_struct_fast()
	    | XV_struct l ->
		( try
		    Array.map
		      (fun (name,y) -> List.assoc name l)
		      decl
		  with
		      Not_found -> fail_map_xv_struct_fast()
		)
	    | _ ->
		fail_map_xv_struct_fast()
	)
    | _ ->
	fail_map_xv_struct_fast()

let map_xv_struct_fast (_,t) v =
  map_xv_struct_fast0 t v

let fail_map_xv_union_over_enum_fast () =
  failwith "Xdr.map_xv_struct_fast" ;;

let map_xv_union_over_enum_fast0 t v =
  match t.term with
      T_union_over_enum( { term = T_enum e }, u, u_dfl ) ->
	let m = Array.length e in
	assert( m = Array.length u );
	( match v with
	      XV_union_over_enum_fast(k, x) ->
		if k >= 0 && k < m then
		  (k, (snd e.(k)), x)
		else
		  fail_map_xv_union_over_enum_fast()
	    | XV_union_over_enum(name, x) ->
		let k = ref 0 in
		while !k < m && fst(e.( !k )) <> name do
		  incr k
		done;
		if !k >= m then
		  fail_map_xv_union_over_enum_fast();
		(!k, (snd e.(!k)), x)
	    | _ ->
		fail_map_xv_union_over_enum_fast()
	)
    | _ ->
	fail_map_xv_union_over_enum_fast()

let map_xv_union_over_enum_fast (_,t) v =
  map_xv_union_over_enum_fast0 t v



exception Xdr_format of string;;
exception Xdr_format_message_too_long of xdr_value;;
(* raised in unpack_xdr_value if the byte stream does not match
 * the expected type. The string is an explanation and might be
 * useful while debugging. In the special case Xdr_format_message_too_long
 * there are more bytes than expected, but a prefix matches the type.
 * The prefix is returned as xdr_value.
 *)

let () =
  Netexn.register_printer
    (Xdr_format "")
    (function
       | Xdr_format s ->
	   sprintf "Xdr.Xdr_format(%S)" s
       | _ ->
	   assert false
    )


(**********************************************************************)
(* check if XDR types are well-formed                                 *)
(**********************************************************************)

(* TODO: check on recursions without finite fix point. *)


let rec validate_xdr_type_i1
        (r:xdr_type_term -> xdr_type0)
        (b:(string * xdr_type0) list)
        (t:xdr_type_term)
      : xdr_type0 =

  (* r: function that resolves X_type references
   * t: the xdr_type_term to validate
   * b: list of recursive bindings
   *
   * raise Not_found on any error
   *)

  let mktype tm = { term = tm; params = StringSet.empty } in

  match t with
    X_int    -> mktype T_int
  | X_uint   -> mktype T_uint
  | X_hyper  -> mktype T_hyper
  | X_uhyper -> mktype T_uhyper
  | X_float  -> mktype T_float
  | X_double -> mktype T_double
  | X_void   -> mktype T_void
  | X_enum e ->
      let e_names, e_values = List.split e in
      if all_distinct e_names && all_distinct e_values then
	let ea =
	  Array.map
	    (fun (n,i) -> (n, Rtypes.int32_of_int4 i))
	    (Array.of_list e) in
	Array.sort (fun (_,i) (_,i') -> compare i i') ea;
	mktype (T_enum ea)
      else
	raise (Propagate "Bad enumeration type: double values")
  | X_opaque_fixed n    -> mktype (T_opaque_fixed n)
  | X_opaque n          -> mktype (T_opaque n)
  | X_string n          -> mktype (T_string n)
  | X_mstring (name,n)  -> mktype (T_mstring (name,n))
  | X_array_fixed (s,n) -> mktype (T_array_fixed(validate_xdr_type_i1 r b s, n))
  | X_array (s,n)       -> mktype (T_array      (validate_xdr_type_i1 r b s, n))
  | X_struct s ->
      let s_names, s_types = List.split s in
      if all_distinct s_names then
	mktype
	  (T_struct
	     (Array.of_list
		(List.map (fun (n,x) -> n,validate_xdr_type_i1 r b x) s)))
      else
	raise (Propagate "Bad struct type: components with same names found")
  | X_union_over_int (u, default) ->
      let u_values, u_types = List.split u in
      if all_distinct u_values then begin
	let default' =
	  match default with
	    Some d -> Some (validate_xdr_type_i1 r b d)
	  | None   -> None
	in
	let htbl = Hashtbl.create(List.length u) in
	List.iter
	  (fun (n,x) ->
	     let x' = validate_xdr_type_i1 r b x in
	     Hashtbl.add htbl n x')
	  u;
	mktype(T_union_over_int(htbl, default'))
      end
      else
	raise (Propagate "Bad union_over_int type: variants found with same tags")
  | X_union_over_uint (u,default) ->
      let u_values, u_types = List.split u in
      if all_distinct u_values then begin
	let default' =
	  match default with
	    Some d -> Some (validate_xdr_type_i1 r b d)
	  | None   -> None
	in
	let htbl = Hashtbl.create(List.length u) in
	List.iter
	  (fun (n,x) ->
	     let x' = validate_xdr_type_i1 r b x in
	     Hashtbl.add htbl n x')
	  u;
	mktype(T_union_over_uint(htbl, default'))
      end
      else
	raise (Propagate "Bad union_over_uint type: variants found with same tags")
  | X_union_over_enum (e,u,default) ->
      let e' = validate_xdr_type_i1 r b e in
      let u_values, u_types = List.split u in
      let el =
	match e'.term with
	  T_enum x -> x
	| _ -> raise (Propagate "Bad union_over_enum type: discriminator is not enumerator")
      in
      let el_names, el_values = List.split (Array.to_list el) in
      if all_distinct u_values && sub_set u_values el_names then begin
	   let default' =
	     match default with
	       Some d -> Some (validate_xdr_type_i1 r b d)
	     | None   -> None
	   in
	   mktype
	     (T_union_over_enum
		(e',
		 Array.map
		   (fun (name, _) ->
		      try Some(validate_xdr_type_i1 r b (List.assoc name u))
		      with Not_found -> default'
		   )
		   el,
		 default'))
	 end
      else
	raise (Propagate "Bad union_over_enum type: variants found with identical tags")
  | X_type _ ->
      r t
  | X_param p ->
      mktype (T_param p)
  | X_rec (name, s) ->
      let node = mktype T_void in
      let t' = validate_xdr_type_i1 r ((name,node)::b) s in
      node.term <- T_rec (name, t');
      node

  | X_refer name ->
      mktype (T_refer (name, List.assoc name b))
;;


let rec find_params (t:xdr_type0) : StringSet.t =
  (* collect all parameters *)
  match t.term with
    T_param p ->
      StringSet.singleton p
  | T_array_fixed (t',n) ->
      find_params t'
  | T_array (t',n) ->
      find_params t'
  | T_struct s ->
      Array.fold_left
        (fun set (s,t') -> StringSet.union (find_params t') set)
        StringSet.empty
        s
  | T_union_over_int (htbl,def_opt) ->
      Hashtbl.fold
        (fun n t' set -> StringSet.union (find_params t') set)
        htbl
        (match def_opt with
	     None -> StringSet.empty
	   | Some def -> find_params def)
  | T_union_over_uint (htbl,def_opt) ->
      Hashtbl.fold
        (fun n t' set -> StringSet.union (find_params t') set)
        htbl
        (match def_opt with
	     None -> StringSet.empty
	   | Some def -> find_params def)
  | T_union_over_enum (e,u,def_opt) ->
      Array.fold_left (fun set t' ->
			 match t' with
			     Some t'' -> StringSet.union (find_params t'') set
			   | None     -> set)
                      (match def_opt with
			   None -> StringSet.empty
			 | Some def -> find_params def)
                      u
  | T_rec (_,t') ->
      find_params t'
  | _ ->
      StringSet.empty
;;


(* Elimination of rec/refer  *)

let map_opt f o =
  match o with
    | None -> None
    | Some x -> Some(f x)

let map_hashtbl f t =
  let acc = Hashtbl.create (Hashtbl.length t) in
  Hashtbl.iter
    (fun k v ->
       let v' = f k v in
       Hashtbl.add acc k v';  (* !!! reverses order of bindings !!! *)
    )
    t;
  acc


let rec elim_rec t = (* get rid of T_rec and T_refer *)
  match t.term with
    | T_int | T_uint | T_hyper | T_uhyper | T_enum _ | T_float
    | T_double | T_opaque_fixed _ | T_opaque _ | T_string _ 
    | T_mstring _ | T_void | T_param _ ->
	t
    | T_array_fixed(t',n) ->
	{ t with term = T_array_fixed(elim_rec t', n) }
    | T_array(t',n) ->
	{ t with term = T_array(elim_rec t', n) }
    | T_struct s ->
	let s' = 
	  Array.map
	    (fun (n,t') ->  (n, elim_rec t'))
	    s in
	{ t with term = T_struct s' }
    | T_union_over_int(ht, dt) ->
	let ht' =
	  map_hashtbl
	    (fun c t' -> elim_rec t')
	    ht in
	let dt' = map_opt elim_rec dt in
	{ t with term = T_union_over_int(ht', dt') }
    | T_union_over_uint(ht, dt) ->
	let ht' =
	  map_hashtbl
	    (fun c t' ->  elim_rec t')
	    ht in
	let dt' = map_opt elim_rec dt in
	{ t with term = T_union_over_uint(ht', dt') }
    | T_union_over_enum(et,ct,dt) ->
	let et' = elim_rec et in
	let ct' = Array.map (map_opt elim_rec) ct in
	let dt' = map_opt elim_rec dt in
	{ t with term = T_union_over_enum(et',ct',dt') }
    | T_rec(n,t') ->
	elim_rec t'
    | T_refer(n,t') ->
	t'



let rec validate_xdr_type (t:xdr_type_term) : xdr_type =
  let r n =
    raise (Propagate "Cannot resolve X_type element")
  in
  try
    let t0' = validate_xdr_type_i1 r [] t in
    let pl = find_params t0' in
    t0'.params <- pl;
    let t1' = elim_rec t0' in
    (t0', t1')
  with
    Not_found ->
      failwith "Xdr.validate_xdr_type: unspecified error"
  | Propagate s ->
      failwith ("Xdr.validate_xdr_type: " ^ s)
;;


let rec expand_X_type (s:xdr_type_system) (t:xdr_type_term) : xdr_type0 =
  match t with
    X_type n ->
      begin
	let rec r s1 s2 =
	  match s2 with
	    []       -> raise (Propagate ("Cannot resolve X_type " ^ n))
	  | (n',t') :: s2' ->
	      if n = n' then
		fst t'
	      else
		r (s1 @ [n',t']) s2'
	in
	r [] s
      end
  | _ ->
      raise (Propagate "Found X_type where it must not occur")
;;


let validate_xdr_type_system (s:xdr_type_term_system) : xdr_type_system =
  let names = List.map fst s in
  if all_distinct names then begin
    let rec r (s1:xdr_type_system) (s2:xdr_type_term_system) =
      match s2 with
	[]           -> []
      |	(n,t) :: s2' ->
	  let t2 =
	  begin
	    try
	      let t0' = validate_xdr_type_i1 (expand_X_type s1) [] t in
	      let pl = find_params t0' in
	      t0'.params <- pl;
	      let t1' = elim_rec t0' in
	      (t0',t1')
	    with
	      Not_found -> failwith "Xdr.validate_xdr_type_system: unspecified error"
	    | Propagate s -> failwith ("Xdr.validate_xdr_type_system: " ^ s)
	  end
	  in
	  (n,t2)::(r (s1 @ [n,t2]) s2')
    in
    r [] s
  end
  else
    failwith "Xdr.validate_xdr_type_system: type system has members with same names"
;;


(**********************************************************************)
(* the reverse way                                                    *)
(**********************************************************************)


let rec xdr_type_term0 (t:xdr_type0) : xdr_type_term =
  let conv_list l =
    List.map (fun (x, t') -> x, xdr_type_term0 t') l in
  let conv_htbl htbl =
    Hashtbl.fold (fun x t' l -> (x, xdr_type_term0 t') :: l) htbl [] in
  let conv_option p =
    match p with None -> None | Some t' -> Some (xdr_type_term0 t') in

  match t.term with
    T_int    -> X_int
  | T_uint   -> X_uint
  | T_hyper  -> X_hyper
  | T_uhyper -> X_uhyper
  | T_enum l -> X_enum (Array.to_list
			  (Array.map
			     (fun (n,i) -> (n,Rtypes.int4_of_int32 i))
			     l))
  | T_float  -> X_float
  | T_double -> X_double
  | T_void   -> X_void
  | T_param p        -> X_param p
  | T_opaque_fixed n -> X_opaque_fixed n
  | T_opaque n       -> X_opaque n
  | T_string n       -> X_string n
  | T_mstring(name,n)-> X_mstring(name,n)
  | T_array_fixed (t', n) -> X_array_fixed (xdr_type_term0 t',n)
  | T_array (t', n)       -> X_array       (xdr_type_term0 t',n)
  | T_struct s       -> X_struct (conv_list (Array.to_list s))
  | T_rec (n, t')    -> X_rec (n, xdr_type_term0 t')
  | T_refer (n, t')  -> X_refer n
  | T_union_over_int (u,d)  -> X_union_over_int  (conv_htbl u, conv_option d)
  | T_union_over_uint (u,d) -> X_union_over_uint (conv_htbl u, conv_option d)
  | T_union_over_enum ( { term = T_enum e } as e_term ,u,d) ->
      let u' =
	List.flatten
	  (Array.to_list
	     (Array.mapi
		(fun k t'_opt ->
		   match t'_opt with
		       Some t' ->
			 let name = fst(e.(k)) in
			 [ name, xdr_type_term0 t' ]
		     | None ->
			 []
		)
		u
	     )
	  )
      in
      X_union_over_enum (xdr_type_term0 e_term, u', conv_option d)
  | _ ->
      assert false
;;


let xdr_type_term (t:xdr_type) : xdr_type_term =
  xdr_type_term0 (fst t)


let xdr_type_term_system (s:xdr_type_system) : xdr_type_term_system =
  List.map (fun (n,t) -> n,xdr_type_term t) s
;;


(**********************************************************************)
(* expand X_type members relative to given systems                    *)
(**********************************************************************)

(* The implemantation of "expanded_xdr_type_term" repeats many phrases
 * that have been defined for "validate_xdr_type" in a very similar
 * way.
 * TODO: Currently many checks have been left out
 *)


let rec expanded_xdr_type_term (s:xdr_type_term_system) (t:xdr_type_term)
        : xdr_type_term =
  match t with
    X_array_fixed (t',n) ->
      X_array_fixed ((expanded_xdr_type_term s t'), n)
  | X_array (t',n) ->
      X_array ((expanded_xdr_type_term s t'), n)
  | X_struct st ->
      let s_names, s_types = List.split st in
      X_struct
	(List.combine
	   s_names
	   (List.map (expanded_xdr_type_term s) s_types))
  | X_union_over_int (u,default) ->
      let u_values, u_types = List.split u in
      let default' =
	match default with
	  Some d -> Some (expanded_xdr_type_term s d)
	| None   -> None
      in
      X_union_over_int
	(List.combine
	   u_values
	   (List.map (expanded_xdr_type_term s) u_types), default')
  | X_union_over_uint (u,default) ->
      let u_values, u_types = List.split u in
      let default' =
	match default with
	  Some d -> Some (expanded_xdr_type_term s d)
	| None   -> None
      in
      X_union_over_uint
	(List.combine
	   u_values
	   (List.map (expanded_xdr_type_term s) u_types), default')
  | X_union_over_enum (e,u,default) ->
      let u_values, u_types = List.split u in
      let default' =
	match default with
	  Some d -> Some (expanded_xdr_type_term s d)
	| None   -> None
      in
      X_union_over_enum
	( (expanded_xdr_type_term s e),
	 (List.combine
	    u_values
	    (List.map (expanded_xdr_type_term s) u_types)),
	 default')
  | X_type n ->
      let rec r s1 s2 =
	match s2 with
	  [] ->
	    failwith ("Xdr.expanded_xdr_type_term: cannot resolve X_type " ^ n)
	| (n',t') :: s2' ->
	      if n = n' then
		expanded_xdr_type_term s1 t'
	      else
		r (s1 @ [n',t']) s2'
      in
      r [] s
  | X_rec (n, t') ->
      X_rec (n, expanded_xdr_type_term s t')
  | _ ->
      t
;;


let expanded_xdr_type (s:xdr_type_system) (t:xdr_type_term) : xdr_type =
  try
    let t0 = validate_xdr_type_i1 (expand_X_type s) [] t in
    let t1 = elim_rec t0 in
    (t0,t1)
  with
    Not_found -> failwith "Xdr.expanded_xdr_type: unspecified error"
  | Propagate s -> failwith ("Xdr.expanded_xdr_type: " ^ s)
;;


(**********************************************************************)
(* test on compatibility                                              *)
(**********************************************************************)

let are_compatible (s1:xdr_type) (s2:xdr_type) : bool =
  (* implementation:
   * enum, struct and union members can be swapped
   *)

  failwith "Xdr.are_compatible: not implemented"

;;


(**********************************************************************)
(* common implementation of value_matches_type & pack_xdr_value       *)
(**********************************************************************)

(* pack: interestingly, two loops over the value where one loop only
   determines the size of the final buffer are _faster_ than a single
   loop over the value doing everything. Whoever understands that.
 *)


let ( ++ ) x y =
  (* pre: x >= 0 && y >= 0 *)
  let s = x + y in
  if s < 0 then raise Not_found;  (* overflow *)
  s


let pack_size
      (v:xdr_value)
      (t:xdr_type0)
      (get_param:string->xdr_type)
    : int =

  (* returned size does not include mstrings! *)

  let rec get_size v t =
    match t.term with
      | T_int ->
	  4
      | T_uint ->
	  4
      | T_hyper ->
	  8
      | T_uhyper ->
	  8
      | T_enum e ->
	  4
      | T_float ->
	  4
      | T_double ->
	  8
      | T_opaque_fixed n ->
	  int_of_uint4 n
      | T_opaque n ->
	  let x = dest_xv_opaque v in
	  get_string_size x n
      | T_string n ->
	  let x = dest_xv_string v in
	  get_string_size x n
      | T_mstring(_,n) ->
	  (* for an mstring we only count the length field plus padding *)
	  let x = dest_xv_mstring v in
	  let l = x#length in
	  get_string_decoration_size l n
      | T_array_fixed (t',n) ->
	  get_array_size v t' n (fun m n -> m=n)
      | T_array (t',n) ->
	  4 + get_array_size v t' n Rtypes.le_uint4
      | T_struct s ->
	  let v_array = map_xv_struct_fast0 t v in
	  let sum = ref 0 in
	  Array.iteri
	    (fun k v_component ->
	       sum := !sum ++ get_size v_component (snd s.(k)))
	    v_array;
	  !sum
      | T_union_over_int (u,default) ->
	  let i,x = dest_xv_union_over_int v in
	  let t' =
      	    try
	      Hashtbl.find u i
	    with
		Not_found ->
		  match default with
		      Some d -> d
		    | None   -> raise Not_found
	  in
	  4 ++ get_size x t'
      | T_union_over_uint (u,default) ->
	  let i,x = dest_xv_union_over_uint v in
	  let t' =
      	    try
	      Hashtbl.find u i
	    with
		Not_found ->
		  match default with
		      Some d -> d
		    | None   -> raise Not_found
	  in
	  4 ++ get_size x t'
      | T_union_over_enum (et,u,default) ->
	  let k,i,x = map_xv_union_over_enum_fast0 t v in
	  let t' =
	    match u.(k) with
		Some u_t -> u_t
	      | None     ->
		  ( match default with
			Some d -> d
		      | None -> raise Not_found
		  )
	  in
	  4 ++ get_size x t'
      | T_void ->
	  0
      | T_param n ->
	  let t' = get_param n in
	  get_size v (snd t')
      | T_rec (n, t') ->
	  get_size v t'
      | T_refer (n, t') ->
	  get_size v t'

  and get_array_size v t' n cmp =  (* w/o array header *)
    (* TODO: optimize arrays of types with fixed repr length *)
    match v with
      | XV_array x ->  (* generic *)
	  let m = uint4_of_int (Array.length x) in
	  if cmp m n then (
	    let s = ref 0 in
	    Array.iter
	      (fun v' -> s := !s ++ get_size v' t')
	      x;
	    !s
	  )
	  else
	    raise Not_found
      | XV_array_of_string_fast x ->
	  ( match t'.term with
	      | T_string sn ->
		  let m = uint4_of_int (Array.length x) in
		  if cmp m n then (
		    let sum = ref 0 in
		    Array.iter
		      (fun s -> sum := !sum ++ get_string_size s sn)
		      x;
		    !sum
		  )
		  else raise Not_found
	      | _ -> 
		  raise Dest_failure
	  )
      | _ ->
	  raise Dest_failure

  and get_string_size x n =
    let x_len = String.length x in
    x_len + get_string_decoration_size x_len n

  and get_string_decoration_size x_len n =
    (* header field plus padding *)
    let x_len_u = uint4_of_int x_len in
    let x_len_mod_4 = x_len land 3 in
    if Rtypes.le_uint4 x_len_u n then begin
      (if x_len_mod_4 = 0
       then 4 
       else 8 - x_len_mod_4
      )
    end
    else
      raise Not_found

  in
  get_size v t


let pack_mstring 
      (v:xdr_value)
      (t:xdr_type0)
      (get_param:string->xdr_type)
    : Xdr_mstring.mstring list =

  let size = pack_size v t get_param in
  (* all sanity checks are done here! Also, [size] does not include the
     size for mstrings (only the length field, and padding)
   *)

  let buf = String.create size in
  let buf_start = ref 0 in
  let buf_pos = ref 0 in

  let result = ref [] in
  (* The resulting mstrings in reverse order *)

  let save_buf() =
    if !buf_pos > !buf_start then (
      let x =
	Xdr_mstring.string_based_mstrings # create_from_string
	  buf !buf_start (!buf_pos - !buf_start) false in
      result := x :: !result;
      buf_start := !buf_pos
    )
  in

  let print_string_padding l =
    let n = 4-(l land 3) in
    if n < 4 then begin
      let l0 = !buf_pos in
      if n >= 1 then String.unsafe_set buf l0 '\000';
      if n >= 2 then String.unsafe_set buf (l0 + 1) '\000';
      if n >= 3 then String.unsafe_set buf (l0 + 2) '\000';
      buf_pos := l0 + n
    end
  in

  let print_string s l =
    String.unsafe_blit s 0 buf !buf_pos l;
    buf_pos := !buf_pos + l;
    print_string_padding l
  in

  let rec pack v t =
    match t.term with
	T_int ->
	  let x = dest_xv_int v in
	  Rtypes.write_int4_unsafe buf !buf_pos x;
	  buf_pos := !buf_pos + 4
      | T_uint ->
	  let x = dest_xv_uint v in
	  Rtypes.write_uint4_unsafe buf !buf_pos x;
	  buf_pos := !buf_pos + 4
      | T_hyper ->
	  let x = dest_xv_hyper v in
	  Rtypes.write_int8_unsafe buf !buf_pos x;
	  buf_pos := !buf_pos + 8
      | T_uhyper ->
	  let x = dest_xv_uhyper v in
	  Rtypes.write_uint8_unsafe buf !buf_pos x;
	  buf_pos := !buf_pos + 8
      | T_enum e ->
	  let i = map_xv_enum_fast0 t v in
	  Rtypes.write_int4_unsafe buf !buf_pos (int4_of_int32 i);
	  buf_pos := !buf_pos + 4
      | T_float ->
	  let x = dest_xv_float v in
	  let s = fp4_as_string x in
	  String.unsafe_blit s 0 buf !buf_pos 4;
	  buf_pos := !buf_pos + 4
      | T_double ->
	  let x = dest_xv_double v in
	  let s = fp8_as_string x in
	  String.unsafe_blit s 0 buf !buf_pos 8;
	  buf_pos := !buf_pos + 8
      | T_opaque_fixed n ->
	  let x = dest_xv_opaque v in
	  print_string x (String.length x)
      | T_opaque n ->
	  let x = dest_xv_opaque v in
	  let x_len = String.length x in
	  Rtypes.write_uint4_unsafe buf !buf_pos (uint4_of_int x_len);
	  buf_pos := !buf_pos + 4;
	  print_string x x_len
      | T_string n ->
	  let x = dest_xv_string v in
	  let x_len = String.length x in
	  Rtypes.write_uint4_unsafe buf !buf_pos (uint4_of_int x_len);
	  buf_pos := !buf_pos + 4;
	  print_string x x_len
      | T_mstring(_,n) ->
	  let x = dest_xv_mstring v in
	  let x_len = x#length in
	  Rtypes.write_uint4_unsafe buf !buf_pos (uint4_of_int x_len);
	  buf_pos := !buf_pos + 4;
	  save_buf();
	  result := x :: !result;
	  print_string_padding x_len
      | T_array_fixed (t',n) ->
	  pack_array v t' n false
      | T_array (t',n) ->
	  pack_array v t' n true
      | T_struct s ->
	  let v_array = map_xv_struct_fast0 t v in
	  Array.iteri
	    (fun k v_component ->
	       pack v_component (snd s.(k)))
	    v_array
      | T_union_over_int (u,default) ->
	  let i,x = dest_xv_union_over_int v in
	  let t' =
      	    try
	      Hashtbl.find u i
	    with
		Not_found ->
		  match default with
		      Some d -> d
		    | None   -> raise Not_found
	  in
	  Rtypes.write_int4_unsafe buf !buf_pos i;
	  buf_pos := !buf_pos + 4;
	  pack x t'
      | T_union_over_uint (u,default) ->
	  let i,x = dest_xv_union_over_uint v in
	  let t' =
      	    try
	      Hashtbl.find u i
	    with
		Not_found ->
		  match default with
		      Some d -> d
		    | None   -> raise Not_found
	  in
	  Rtypes.write_uint4_unsafe buf !buf_pos i;
	  buf_pos := !buf_pos + 4;
	  pack x t'
      | T_union_over_enum (et,u,default) ->
	  let k,i,x = map_xv_union_over_enum_fast0 t v in
	  let t' =
	    match u.(k) with
		Some u_t -> u_t
	      | None     ->
		  ( match default with
			Some d -> d
		      | None -> raise Not_found
		  )
	  in
	  Rtypes.write_int4_unsafe buf !buf_pos (int4_of_int32 i);
	  buf_pos := !buf_pos + 4;
	  pack x t'
      | T_void ->
	  ()
      | T_param n ->
	  let t' = get_param n in
	  pack v (snd t')
      | T_rec (n, t') ->
	  pack v t'
      | T_refer (n, t') ->
	  pack v t'

  and pack_array v t' n have_array_header =
    match v with
      | XV_array x ->  (* generic *)
	  if have_array_header then pack_array_header (Array.length x);
	  Array.iter
	    (fun v' -> pack v' t')
	    x
      | XV_array_of_string_fast x ->
	  ( match t'.term with
	      | T_string n ->
		  if have_array_header then pack_array_header (Array.length x);
		  Array.iter
		    (fun s ->
		       let s_len = String.length s in
		       Rtypes.write_uint4_unsafe
			 buf !buf_pos (uint4_of_int s_len);
		       buf_pos := !buf_pos + 4;
		       print_string s s_len
		    )
		    x
	      | _ -> raise Dest_failure
	  )
      | _ -> raise Dest_failure

  and pack_array_header x_len =
    Rtypes.write_uint4_unsafe buf !buf_pos (uint4_of_int x_len);
    buf_pos := !buf_pos + 4;
  in
  pack v t;
  save_buf();
  List.rev !result
;;


(*
let pack_buf
      ?(rm = false)
      (v:xdr_value)
      (t:xdr_type0)
      (get_param:string->xdr_type)
    : string =

  let size = 
    (if rm then 4 else 0) ++
      pack_size v t get_param in  (* all sanity checks are done here! *)

  let buf = String.create size in
  let buf_len = ref 0 in

  if rm then (
    buf.[0] <- '\000';
    buf.[1] <- '\000';
    buf.[2] <- '\000';
    buf.[3] <- '\000';
    buf_len := 4
  );

  let print_string s l =  (* assert(l=String.length s) *)
    let n = 4-(l land 3) in
    if n < 4 then begin
      String.unsafe_blit s 0 buf !buf_len l;
      let l0 = !buf_len + l in
      if n >= 1 then String.unsafe_set buf l0 '\000';
      if n >= 2 then String.unsafe_set buf (l0 + 1) '\000';
      if n >= 3 then String.unsafe_set buf (l0 + 2) '\000';
      buf_len := l0 + n
    end
    else begin
      String.unsafe_blit s 0 buf !buf_len l;
      buf_len := !buf_len + l
    end
  in

  let rec pack v t =
    match t.term with
	T_int ->
	  let x = dest_xv_int v in
	  Rtypes.write_int4_unsafe buf !buf_len x;
	  buf_len := !buf_len + 4
      | T_uint ->
	  let x = dest_xv_uint v in
	  Rtypes.write_uint4_unsafe buf !buf_len x;
	  buf_len := !buf_len + 4
      | T_hyper ->
	  let x = dest_xv_hyper v in
	  Rtypes.write_int8_unsafe buf !buf_len x;
	  buf_len := !buf_len + 8
      | T_uhyper ->
	  let x = dest_xv_uhyper v in
	  Rtypes.write_uint8_unsafe buf !buf_len x;
	  buf_len := !buf_len + 8
      | T_enum e ->
	  let i = map_xv_enum_fast0 t v in
	  Rtypes.write_int4_unsafe buf !buf_len (int4_of_int32 i);
	  buf_len := !buf_len + 4
      | T_float ->
	  let x = dest_xv_float v in
	  let s = fp4_as_string x in
	  String.unsafe_blit s 0 buf !buf_len 4;
	  buf_len := !buf_len + 4
      | T_double ->
	  let x = dest_xv_double v in
	  let s = fp8_as_string x in
	  String.unsafe_blit s 0 buf !buf_len 8;
	  buf_len := !buf_len + 8
      | T_opaque_fixed n ->
	  let x = dest_xv_opaque v in
	  print_string x (String.length x)
      | T_opaque n ->
	  let x = dest_xv_opaque v in
	  let x_len = String.length x in
	  Rtypes.write_uint4_unsafe buf !buf_len (uint4_of_int x_len);
	  buf_len := !buf_len + 4;
	  print_string x x_len
      | T_string n ->
	  let x = dest_xv_string v in
	  let x_len = String.length x in
	  Rtypes.write_uint4_unsafe buf !buf_len (uint4_of_int x_len);
	  buf_len := !buf_len + 4;
	  print_string x x_len
      | T_array_fixed (t',n) ->
	  pack_array v t' n false
      | T_array (t',n) ->
	  pack_array v t' n true
      | T_struct s ->
	  let v_array = map_xv_struct_fast0 t v in
	  Array.iteri
	    (fun k v_component ->
	       pack v_component (snd s.(k)))
	    v_array
      | T_union_over_int (u,default) ->
	  let i,x = dest_xv_union_over_int v in
	  let t' =
      	    try
	      Hashtbl.find u i
	    with
		Not_found ->
		  match default with
		      Some d -> d
		    | None   -> raise Not_found
	  in
	  Rtypes.write_int4_unsafe buf !buf_len i;
	  buf_len := !buf_len + 4;
	  pack x t'
      | T_union_over_uint (u,default) ->
	  let i,x = dest_xv_union_over_uint v in
	  let t' =
      	    try
	      Hashtbl.find u i
	    with
		Not_found ->
		  match default with
		      Some d -> d
		    | None   -> raise Not_found
	  in
	  Rtypes.write_uint4_unsafe buf !buf_len i;
	  buf_len := !buf_len + 4;
	  pack x t'
      | T_union_over_enum (et,u,default) ->
	  let k,i,x = map_xv_union_over_enum_fast0 t v in
	  let t' =
	    match u.(k) with
		Some u_t -> u_t
	      | None     ->
		  ( match default with
			Some d -> d
		      | None -> raise Not_found
		  )
	  in
	  Rtypes.write_int4_unsafe buf !buf_len (int4_of_int32 i);
	  buf_len := !buf_len + 4;
	  pack x t'
      | T_void ->
	  ()
      | T_param n ->
	  let t' = get_param n in
	  pack v (snd t')
      | T_rec (n, t') ->
	  pack v t'
      | T_refer (n, t') ->
	  pack v t'

  and pack_array v t' n have_array_header =
    match v with
      | XV_array x ->  (* generic *)
	  if have_array_header then pack_array_header (Array.length x);
	  Array.iter
	    (fun v' -> pack v' t')
	    x
      | XV_array_of_string_fast x ->
	  ( match t'.term with
	      | T_string n ->
		  if have_array_header then pack_array_header (Array.length x);
		  Array.iter
		    (fun s ->
		       let s_len = String.length s in
		       Rtypes.write_uint4_unsafe
			 buf !buf_len (uint4_of_int s_len);
		       buf_len := !buf_len + 4;
		       print_string s s_len
		    )
		    x
	      | _ -> raise Dest_failure
	  )
      | _ -> raise Dest_failure

  and pack_array_header x_len =
    Rtypes.write_uint4_unsafe buf !buf_len (uint4_of_int x_len);
    buf_len := !buf_len + 4;
  in
  pack v t;
  buf
 *)


let value_matches_type
    (v:xdr_value)
    ((_,t):xdr_type)
    (p:(string * xdr_type) list)
  : bool =
  if StringSet.for_all (fun n -> List.mem_assoc n p) t.params &&
     List.for_all (fun (n,t') -> StringSet.is_empty (fst t').params) p then
    try
      ignore(pack_size v t (fun n -> List.assoc n p));
      true
    with
      _ ->      (* we assume here that no other errors can occur *)
      	false
  else
    false
;;


(**********************************************************************)
(* pack and unpack values                                             *)
(**********************************************************************)

let pack_xdr_value
    (v:xdr_value)
    ((_,t):xdr_type)
    (p:(string * xdr_type) list)
    (print:string->unit)
  : unit =

  (* DEBUG *)
  (* List.iter (fun pn -> prerr_endline ("param " ^ pn)) t.params; *)

  if StringSet.for_all (fun n -> List.mem_assoc n p) t.params &&
     List.for_all (fun (n,t') -> StringSet.is_empty (fst t').params) p then
    try
      let mstrings = 
	pack_mstring v t (fun n -> List.assoc n p) in
      List.iter
	(fun ms ->
	   let (s,p) = ms#as_string in
	   print (String.sub s p ms#length)
	)
	mstrings
    with
      any ->
	(* DEBUG *)
	(* prerr_endline (Netexn.to_string any); *)
      	failwith "Xdr.pack_xdr_value"
  else
    failwith "Xdr.pack_xdr_value"
;;


let pack_xdr_value_as_string
    ?(rm = false)
    (v:xdr_value)
    ((_,t):xdr_type)
    (p:(string * xdr_type) list)
  : string =

  if StringSet.for_all (fun n -> List.mem_assoc n p) t.params &&
     List.for_all (fun (n,t') -> StringSet.is_empty (fst t').params) p then
    try
      let mstrings0 = 
	pack_mstring v t (fun n -> List.assoc n p) in
      let rm_prefix =
	if rm then
	  let s = "\000\000\000\000" in
	  [ Xdr_mstring.string_based_mstrings # create_from_string s 0 4 false ]
	else
	  [] in
      let mstrings = rm_prefix @ mstrings0 in
      Xdr_mstring.concat_mstrings mstrings
    with
      any ->
	(* DEBUG *)
	(* prerr_endline (Netexn.to_string any); *)
	(* Printexc.print_backtrace stderr; *)
      	failwith "Xdr.pack_xdr_value_as_string [1]"
  else
    failwith "Xdr.pack_xdr_value_as_string [2]"
;;

let pack_xdr_value_as_mstrings
    (v:xdr_value)
    ((_,t):xdr_type)
    (p:(string * xdr_type) list)
    =

  if StringSet.for_all (fun n -> List.mem_assoc n p) t.params &&
     List.for_all (fun (n,t') -> StringSet.is_empty (fst t').params) p then
    try
      pack_mstring v t (fun n -> List.assoc n p)
    with
      any ->
	(* DEBUG *)
	(* prerr_endline (Netexn.to_string any); *)
      	failwith "Xdr.pack_xdr_value_as_mstrings [1]"
  else
    failwith "Xdr.pack_xdr_value_as_mstrings [2]"
;;

(* "let rec" prevents that these functions are inlined. This is wanted here,
   because these are error cases, and for a function call less code
   is generated than for raising an exception
 *)

let rec raise_xdr_format_too_short () =
  raise (Xdr_format "message too short")

let rec raise_xdr_format_value_not_included () =
  raise (Xdr_format "value not included in enumeration")

let rec raise_xdr_format_maximum_length () =
  raise (Xdr_format "maximum length of field exceeded")

let rec raise_xdr_format_undefined_descriminator() =
  raise (Xdr_format "undefined discriminator")


let rec find_enum (e : (string * int32) array) (i : int32) =
  (* no inlining! *)
  let rec loop lb ub =
    (* The element is between lb and ub *)
    if lb > ub then raise_xdr_format_value_not_included ();
    let m = (ub + lb) lsr 1 in
    let x_m = snd(e.(m)) in
    if i = x_m then
      (* Found! *)
      m
    else if i < x_m then
      loop lb (m-1)
    else
      (* i > x_m *)
      loop (m+1) ub
  in
  loop 0 (Array.length e - 1)
;;

(* DEBUG*)
(*
let hex_dump_s s pos len =
  let b = Buffer.create 100 in
  for k = 0 to len - 1 do
    let c = s.[pos+k] in
    bprintf b "%02x " (Char.code c)
  done;
  Buffer.contents b
 *)

let read_string str k k_end n =
  let k0 = !k in
  let m = if n land 3 = 0 then n else n+4-(n land 3) in
  if k0 > k_end - m then raise_xdr_format_too_short ();
  let s = String.create n in
  String.unsafe_blit str k0 s 0 n;
  k := k0 + m;
  s

let empty_mf = Hashtbl.create 1

let unpack_term
    ?(pos = 0)
    ?len
    ?(fast = false)
    ?(prefix = false)
    ?(mstring_factories = empty_mf)
    (str:string)
    (t:xdr_type0)
    (get_param:string->xdr_type)
  : xdr_value * int =

  let len =
    match len with
	None -> String.length str - pos
      | Some l -> l
  in

  if pos < 0 || len < 0 || len > String.length str - pos then
    invalid_arg "Xdr.unpack_xdr_value";

  let k_end = pos+len in
  let k = ref pos in

  let rec read_fp4 k0 =
    if k0 + 4 > k_end then raise_xdr_format_too_short();
    k := !k + 4;
    Rtypes.read_fp4 str k0
  in

  let rec read_fp8 k0 =
    if k0 + 8 > k_end then raise_xdr_format_too_short();
    k := !k + 8;
    Rtypes.read_fp8 str k0
  in

  let rec read_enum e k0 =
    k := k0 + 4;
    if !k > k_end then raise_xdr_format_too_short();
    let i = Rtypes.int32_of_int4(Rtypes.read_int4_unsafe str k0) in
    let j = find_enum e i in   (* returns array position, or Xdr_format *)
    if fast then
      XV_enum_fast j
    else
      XV_enum(fst(e.(j)))
  in

  let rec read_string_or_opaque n k0 =
    k := k0 + 4;
    if !k > k_end then raise_xdr_format_too_short();
    let m = Rtypes.read_uint4_unsafe str k0 in
    (* Test: n < m as unsigned int32: *)
    if Rtypes.lt_uint4 n m then
      raise_xdr_format_maximum_length ();
    read_string str k k_end (int_of_uint4 m)
  in

  let rec read_mstring name n k0 =
    let factory =
      try Hashtbl.find mstring_factories name
      with Not_found -> 
	( try Hashtbl.find mstring_factories "*"
	  with Not_found ->
	    failwith "read_mstring: no such factory"
	) in
    k := k0 + 4;
    if !k > k_end then raise_xdr_format_too_short();
    let m = Rtypes.read_uint4_unsafe str k0 in
    (* Test: n < m as unsigned int32: *)
    if Rtypes.lt_uint4 n m then
      raise_xdr_format_maximum_length ();
    let m = int_of_uint4 m in
    let p = if m land 3 = 0 then m else m+4-(m land 3) in
    if !k > k_end - p then raise_xdr_format_too_short ();
    let ms = factory # create_from_string str !k m false in
    k := !k + p;
    ms
  in

  let rec unpack_array t' p =
    match t'.term with
      | T_string n ->
	  let n' = Rtypes.logical_int32_of_uint4 n in
	  let a = Array.create p "" in
	  let k' = 
	    Netsys_xdr.s_read_string_array_unsafe str !k (k_end - !k) n' a in
	  if k' = (-1) then raise_xdr_format_too_short();
	  if k' = (-2) then raise_xdr_format_maximum_length ();
	  k := k';
	  if fast then
	    XV_array_of_string_fast a
	  else
	    XV_array(Array.map (fun s -> XV_string s) a)
      | _ ->
	  let a = Array.create p XV_void in
	  for i = 0 to p-1 do
	    Array.unsafe_set a i (unpack t')
	  done;
	  XV_array a

  and unpack t =
    let k0 = !k in
(*    fprintf stderr "unpack k=%d t=%s\n%!" k0 (t_name t.term); *)
    match t.term with
      T_int ->
	k := k0 + 4;
	if !k > k_end then raise_xdr_format_too_short();
	XV_int (Rtypes.read_int4_unsafe str k0)
    | T_uint ->
	k := k0 + 4;
	if !k > k_end then raise_xdr_format_too_short();
	XV_uint (Rtypes.read_uint4_unsafe str k0)
    | T_hyper ->
	k := k0 + 8;
	if !k > k_end then raise_xdr_format_too_short();
	XV_hyper (Rtypes.read_int8_unsafe str k0)
    | T_uhyper ->
	k := !k + 8;
	if k0 > k_end then raise_xdr_format_too_short();
	XV_uhyper (read_uint8_unsafe str k0)
    | T_enum e ->
	read_enum e k0
    | T_float ->
	XV_float (read_fp4 k0)
    | T_double ->
	XV_double (read_fp8 k0)
    | T_opaque_fixed n ->
	XV_opaque (read_string str k k_end (int_of_uint4 n))
    | T_opaque n ->
	XV_opaque (read_string_or_opaque n k0)
    | T_string n ->
	XV_string (read_string_or_opaque n k0)
    | T_mstring(name,n) ->
	XV_mstring (read_mstring name n k0)
    | T_array_fixed (t',n) ->
	let p = int_of_uint4 n in
	unpack_array t' p
    | T_array (t',n) ->
	k := k0 + 4;
	let m = Rtypes.read_uint4 str k0 in
	if Rtypes.lt_uint4 n m then
	  raise_xdr_format_maximum_length ();
	unpack_array t' (int_of_uint4 m)
    | T_struct s ->
	if fast then
	  XV_struct_fast
	    ( Array.map
		(fun (name,t') -> unpack t')
		s
	    )
	else
	  XV_struct
	    (List.map
	       (fun (name,t') -> (name,unpack t'))
	       (Array.to_list s)
	    )
    | T_union_over_int (u,default) ->
	unpack_union_over_int u default k0
    | T_union_over_uint (u,default) ->
	unpack_union_over_uint u default k0
    | T_union_over_enum ( { term = T_enum e },u,default) ->
	unpack_union_over_enum e u default k0
    | T_void ->
	XV_void
    | T_param p ->
	let t' = get_param p in
	unpack (snd t')
    | T_rec (_, t')
    | T_refer (_, t') ->
	unpack t'
    | _ ->
	assert false

  and unpack_union_over_int u default k0 =
    k := k0 + 4;
    let n = Rtypes.read_int4 str k0 in
    let t' =
      try
	Hashtbl.find u n
      with
	  Not_found ->
	    match default with
		None   -> raise_xdr_format_undefined_descriminator()
	      |	Some d -> d
    in
    XV_union_over_int (n, unpack t')

  and unpack_union_over_uint u default k0 =
    k := k0 + 4;
    let n = Rtypes.read_uint4 str k0 in
    let t' =
      try
	Hashtbl.find u n
      with
	  Not_found ->
	    match default with
		None   -> raise_xdr_format_undefined_descriminator()
	      |	Some d -> d
    in
    XV_union_over_uint (n, unpack t')

  and unpack_union_over_enum e u default k0 =
    k := k0 + 4;
    let i = Rtypes.int32_of_int4 (Rtypes.read_int4 str k0) in
    let j = find_enum e i  (* returns array position, or Xdr_format *) in
    let t' =
      match u.(j) with
	  Some u_t -> u_t
	| None ->
	    ( match default with
		  Some d -> d
		| None ->
		    raise_xdr_format_undefined_descriminator()
	    )
    in
    if fast then
      XV_union_over_enum_fast(j, unpack t')
    else
      let name = fst(e.(j)) in
      XV_union_over_enum(name, unpack t')
	
  in
  try
    let v = unpack t in
    if prefix || !k = k_end then
      (v, !k - pos)
    else (
(*
      fprintf stderr "Too LONG: k=%d k_end=%d\n%!" !k k_end;
      fprintf stderr "Dump: %s\n%!" (hex_dump_s str pos (k_end-pos));
 *)
      raise (Xdr_format_message_too_long v)
    )
  with
      Cannot_represent _ ->
	raise (Xdr_format "implementation restriction")
    | Out_of_range ->
	raise (Xdr_format "message too short")
;;


let unpack_xdr_value
    ?pos ?len ?fast ?prefix ?mstring_factories
    (str:string)
    ((_,t):xdr_type)
    (p:(string * xdr_type) list)
  : xdr_value =

  if StringSet.for_all (fun n -> List.mem_assoc n p) t.params &&
     List.for_all (fun (n,t') -> StringSet.is_empty (fst t').params) p then

    fst(unpack_term 
	  ?pos ?len ?fast ?prefix ?mstring_factories
	  str t (fun n -> List.assoc n p))

  else
    failwith "Xdr.unpack_xdr_value"
;;


let unpack_xdr_value_l
    ?pos ?len ?fast ?prefix ?mstring_factories
    (str:string)
    ((_,t):xdr_type)
    (p:(string * xdr_type) list)
  : xdr_value * int =

  if StringSet.for_all (fun n -> List.mem_assoc n p) t.params &&
     List.for_all (fun (n,t') -> StringSet.is_empty (fst t').params) p then

    unpack_term
      ?pos ?len ?fast ?prefix ?mstring_factories 
      str t (fun n -> List.assoc n p)

  else
    failwith "Xdr.unpack_xdr_value"
;;


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