File: deriving_Dump.ml

package info (click to toggle)
ocaml-deriving-ocsigen 0.6.2-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 628 kB
  • ctags: 1,159
  • sloc: ml: 6,334; makefile: 63; sh: 18
file content (256 lines) | stat: -rw-r--r-- 7,732 bytes parent folder | download | duplicates (2)
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
(** Dump **)

(* Copyright Jeremy Yallop 2007.
   This file is free software, distributed under the MIT license.
   See the file COPYING for details.
*)

(* TODO: we could have an additional debugging deserialisation method. *)
module type Dump = sig
  type a
    val to_buffer : Buffer.t -> a -> unit
    val to_string : a -> string
    val to_channel : out_channel -> a -> unit
    val from_stream : char Stream.t -> a
    val from_string : string -> a
    val from_channel : in_channel -> a
end

module type SimpleDump = sig
  type a
  val to_buffer : Buffer.t -> a -> unit
  val from_stream : char Stream.t -> a
end

exception Dump_error of string

let bad_tag tag stream typename =
  raise (Dump_error
           (Printf.sprintf 
              "Dump: failure during %s deserialisation at character %d; unexpected tag %d" 
              typename (Stream.count stream) tag))

module Defaults (P : sig   
			  type a
			  val to_buffer : Buffer.t -> a -> unit
			  val from_stream : char Stream.t -> a
			end) : Dump with type a = P.a = 
struct
  include P

 (* is there a reasonable value to use here? *)
  let buffer_size = 128

  let to_string obj = 
    let buffer = Buffer.create buffer_size in
      P.to_buffer buffer obj;
      Buffer.contents buffer
      (* should we explicitly deallocate the buffer? *)
  and from_string string = P.from_stream (Stream.of_string string)
  and from_channel in_channel = 
    from_stream (Stream.of_channel in_channel)
  and to_channel out_channel obj = 
    let buffer = Buffer.create buffer_size in
      P.to_buffer buffer obj;
      Buffer.output_buffer out_channel buffer
end


(* Generic int dumper.  This should work for any (fixed-size) integer
   type with suitable operations. *)
module Dump_intN (P : sig
                      type t
                      val zero : t
                      val logand : t -> t -> t
                      val logor : t -> t -> t
                      val lognot : t -> t
                      val shift_right_logical : t -> int -> t
                      val shift_left : t -> int -> t
                      val of_int : int -> t
                      val to_int : t -> int
                    end) = Defaults (
  struct
    type a = P.t
	(* Format an integer using the following scheme:
	   
	   The lower 7 bits of each byte are used to store successive 7-bit
	   chunks of the integer.
	   
	   The highest bit of each byte is used as a flag to indicate
	   whether the next byte is present.
	*)
    open Buffer
    open Char
    open P

    let to_buffer buffer =
      let rec aux int =
        (* are there more than 7 bits? *)
        if logand int (lognot (of_int 0x7f)) <> zero
        (* if there are, write the lowest 7 bite plus a high bit (to
           indicate that there's more).  Then recurse, shifting the value
           7 bits right *)
        then begin
          add_char buffer (chr (to_int (logor (of_int 0x80) (logand int (of_int 0x7f)))));
	  aux (shift_right_logical int 7)
        end
          (* otherwise, write the bottom 7 bits only *)
        else add_char buffer (chr (to_int int))
      in aux

    and from_stream stream = 
      let rec aux (int : t) shift = 
        let c = of_int (code (Stream.next stream)) in
        let int = logor int (shift_left (logand c (of_int 0x7f)) shift) in
          if logand c (of_int 0x80) <> zero then aux int (shift + 7)
          else int 
      in aux zero 0
  end
)

module Dump_int32 = Dump_intN (Int32)
module Dump_int64 = Dump_intN (Int64)
module Dump_nativeint = Dump_intN (Nativeint)
module Dump_int = Defaults (
  struct
    type a = int
    let to_buffer buffer int = Dump_nativeint.to_buffer buffer (Nativeint.of_int int)
    and from_stream stream = Nativeint.to_int (Dump_nativeint.from_stream stream)
  end
)

module Dump_char = Defaults (
  struct
    type a = char
    let to_buffer = Buffer.add_char
    and from_stream = Stream.next
  end
)

(* This is questionable; it doesn't preserve sharing *)
module Dump_string = Defaults (
  struct
    type a = string
    let to_buffer buffer string = 
      begin
        Dump_int.to_buffer buffer (String.length string);
        Buffer.add_string buffer string
      end
    and from_stream stream = 
      let len = Dump_int.from_stream stream in
      let s = String.create len in
        for i = 0 to len - 1 do
          String.set s i (Stream.next stream) (* could use String.unsafe_set here *)
        done;
        s
  end
)

module Dump_float = Defaults (
  struct
    type a = float
    let to_buffer buffer f = Dump_int64.to_buffer buffer (Int64.bits_of_float f)
    and from_stream stream = Int64.float_of_bits (Dump_int64.from_stream stream)
  end
)

(* This should end up a bit more compact than the derived version *)
module Dump_list (P : SimpleDump) = Defaults (
  (* This could perhaps be more efficient by serialising the list in
     reverse: this would result in only one traversal being needed
     during serialisation, and no "reverse" being needed during
     deserialisation.  (However, dumping would no longer be
     tail-recursive) *)
  struct
    type a = P.a list
    let to_buffer buffer items = 
      begin
        Dump_int.to_buffer buffer (List.length items);
        List.iter (P.to_buffer buffer) items
      end
    and from_stream stream = 
      let rec aux items = function
        | 0 -> items
        | n -> aux (P.from_stream stream :: items) (n-1)
      in List.rev (aux [] (Dump_int.from_stream stream))
  end
)

(* Dump_ref and Dump_array cannot preserve sharing, so we don't
   provide implementations *)

module Dump_option (P : SimpleDump) = Defaults (
  struct
    type a = P.a option
    let to_buffer buffer = function
      | None   -> Dump_int.to_buffer buffer 0
      | Some s -> 
          begin
            Dump_int.to_buffer buffer 1;
            P.to_buffer buffer s
          end
    and from_stream stream = 
      match Dump_int.from_stream stream with
        | 0 -> None
        | 1 -> Some (P.from_stream stream)
        | i      -> bad_tag i stream "option"
  end
)


module Dump_bool = Defaults (
  struct
    type a = bool
    let to_buffer buffer = function
      | false -> Buffer.add_char buffer '\000'
      | true  -> Buffer.add_char buffer '\001'
    and from_stream stream =
      match Stream.next stream with
        | '\000' -> false
        | '\001' -> true
        | c      -> bad_tag (Char.code c) stream "bool"
  end
)

module Dump_unit = Defaults (
  struct
    type a = unit
    let to_buffer _ () = ()
    and from_stream _ = ()
  end
)

module Dump_alpha(P: sig type a end) = Defaults(struct
  type a = P.a
  let to_buffer _ _ = assert false
  let from_stream _ = assert false
end)

module Dump_undumpable (P : sig type a val tname : string end) = Defaults ( 
  struct 
    type a = P.a
    let to_buffer _ _ = failwith ("Dump: attempt to serialise a value of unserialisable type : " ^ P.tname)
    let from_stream _ = failwith ("Dump: attempt to deserialise a value of unserialisable type : " ^ P.tname)
  end
)

(* Uses Marshal to serialise the values that the parse-the-declarations
   technique can't reach. *)
module Dump_via_marshal (P : sig type a end) = Defaults (
(* Rather inefficient. *)
  struct
    include P
    let to_buffer buffer obj = Buffer.add_string buffer (Marshal.to_string obj [Marshal.Closures])
    let from_stream stream = 
      let readn n = 
        let s = String.create n in
          for i = 0 to n - 1 do
            String.set s i (Stream.next stream)
          done;
          s
      in
      let header = readn Marshal.header_size in
      let datasize = Marshal.data_size header 0 in
      let datapart = readn datasize in
        Marshal.from_string (header ^ datapart) 0
  end)