3 (* Copyright Jeremy Yallop 2007.
4 This file is free software, distributed under the MIT license.
5 See the file COPYING for details.
8 (* TODO: we could have an additional debugging deserialisation method. *)
11 val to_buffer
: Buffer.t
-> a
-> unit
12 val to_string
: a
-> string
13 val to_channel
: out_channel
-> a
-> unit
14 val from_stream
: char
Stream.t
-> a
15 val from_string
: string -> a
16 val from_channel
: in_channel
-> a
19 module type SimpleDump
= sig
21 val to_buffer
: Buffer.t
-> a
-> unit
22 val from_stream
: char
Stream.t
-> a
25 exception Dump_error
of string
27 let bad_tag tag stream typename
=
30 "Dump: failure during %s deserialisation at character %d; unexpected tag %d"
31 typename
(Stream.count stream
) tag
))
33 module Defaults
(P
: sig
35 val to_buffer
: Buffer.t
-> a
-> unit
36 val from_stream
: char
Stream.t
-> a
37 end) : Dump
with type a
= P.a
=
41 (* is there a reasonable value to use here? *)
45 let buffer = Buffer.create
buffer_size in
46 P.to_buffer
buffer obj
;
47 Buffer.contents
buffer
48 (* should we explicitly deallocate the buffer? *)
49 and from_string
string = P.from_stream
(Stream.of_string
string)
50 and from_channel in_channel
=
51 from_stream
(Stream.of_channel in_channel
)
52 and to_channel out_channel obj
=
53 let buffer = Buffer.create
buffer_size in
54 P.to_buffer
buffer obj
;
55 Buffer.output_buffer out_channel
buffer
59 (* Generic int dumper. This should work for any (fixed-size) integer
60 type with suitable operations. *)
61 module Dump_intN
(P
: sig
64 val logand
: t
-> t
-> t
65 val logor
: t
-> t
-> t
67 val shift_right_logical
: t
-> int -> t
68 val shift_left
: t
-> int -> t
74 (* Format an integer using the following scheme:
76 The lower 7 bits of each byte are used to store successive 7-bit
77 chunks of the integer.
79 The highest bit of each byte is used as a flag to indicate
80 whether the next byte is present.
86 let to_buffer buffer =
88 (* are there more than 7 bits? *)
89 if logand
int (lognot
(of_int
0x7f)) <> zero
90 (* if there are, write the lowest 7 bite plus a high bit (to
91 indicate that there's more). Then recurse, shifting the value
94 add_char
buffer (chr
(to_int
(logor
(of_int
0x80) (logand
int (of_int
0x7f)))));
95 aux (shift_right_logical
int 7)
97 (* otherwise, write the bottom 7 bits only *)
98 else add_char
buffer (chr
(to_int
int))
101 and from_stream stream
=
102 let rec aux (int : t
) shift
=
103 let c = of_int
(code
(Stream.next stream
)) in
104 let int = logor
int (shift_left
(logand
c (of_int
0x7f)) shift
) in
105 if logand
c (of_int
0x80) <> zero
then aux int (shift
+ 7)
111 module Dump_int32
= Dump_intN
(Int32
)
112 module Dump_int64
= Dump_intN
(Int64
)
113 module Dump_nativeint
= Dump_intN
(Nativeint
)
114 module Dump_int
= Defaults
(
117 let to_buffer buffer int = Dump_nativeint.to_buffer buffer (Nativeint.of_int
int)
118 and from_stream stream
= Nativeint.to_int
(Dump_nativeint.from_stream stream
)
122 module Dump_char
= Defaults
(
125 let to_buffer = Buffer.add_char
126 and from_stream
= Stream.next
130 (* This is questionable; it doesn't preserve sharing *)
131 module Dump_string
= Defaults
(
134 let to_buffer buffer string =
136 Dump_int.to_buffer buffer (String.length
string);
137 Buffer.add_string
buffer string
139 and from_stream stream
=
140 let len = Dump_int.from_stream stream
in
141 let s = String.create
len in
142 for i
= 0 to len - 1 do
143 String.set
s i
(Stream.next stream
) (* could use String.unsafe_set here *)
149 module Dump_float
= Defaults
(
152 let to_buffer buffer f
= Dump_int64.to_buffer buffer (Int64.bits_of_float f
)
153 and from_stream stream
= Int64.float_of_bits
(Dump_int64.from_stream stream
)
157 (* This should end up a bit more compact than the derived version *)
158 module Dump_list
(P
: SimpleDump
) = Defaults
(
159 (* This could perhaps be more efficient by serialising the list in
160 reverse: this would result in only one traversal being needed
161 during serialisation, and no "reverse" being needed during
162 deserialisation. (However, dumping would no longer be
166 let to_buffer buffer items
=
168 Dump_int.to_buffer buffer (List.length items
);
169 List.iter
(P.to_buffer buffer) items
171 and from_stream stream
=
172 let rec aux items
= function
174 | n
-> aux (P.from_stream stream
:: items
) (n
-1)
175 in List.rev
(aux [] (Dump_int.from_stream stream
))
179 (* Dump_ref and Dump_array cannot preserve sharing, so we don't
180 provide implementations *)
182 module Dump_option
(P
: SimpleDump
) = Defaults
(
185 let to_buffer buffer = function
186 | None
-> Dump_int.to_buffer buffer 0
189 Dump_int.to_buffer buffer 1;
192 and from_stream stream
=
193 match Dump_int.from_stream stream
with
195 | 1 -> Some
(P.from_stream stream
)
196 | i
-> bad_tag i stream
"option"
201 module Dump_bool
= Defaults
(
204 let to_buffer buffer = function
205 | false -> Buffer.add_char
buffer '
\000'
206 | true -> Buffer.add_char
buffer '
\001'
207 and from_stream stream
=
208 match Stream.next stream
with
211 | c -> bad_tag (Char.code
c) stream
"bool"
215 module Dump_unit
= Defaults
(
218 let to_buffer _
() = ()
219 and from_stream _
= ()
223 module Dump_num
= Defaults
(
225 (* TODO: a less wasteful dumper for nums. A good start would be
226 using half a byte per decimal-coded digit, instead of a whole
229 let to_buffer buffer n
= Dump_string.to_buffer buffer (Num.string_of_num n
)
230 and from_stream stream
= Num.num_of_string
(Dump_string.from_stream stream
)
234 module Dump_undumpable
(P
: sig type a
val tname
: string end) = Defaults
(
237 let to_buffer _ _
= failwith
("Dump: attempt to serialise a value of unserialisable type : " ^
P.tname
)
238 let from_stream _
= failwith
("Dump: attempt to deserialise a value of unserialisable type : " ^
P.tname
)
242 (* Uses Marshal to serialise the values that the parse-the-declarations
243 technique can't reach. *)
244 module Dump_via_marshal
(P
: sig type a
end) = Defaults
(
245 (* Rather inefficient. *)
248 let to_buffer buffer obj
= Buffer.add_string
buffer (Marshal.to_string obj
[Marshal.Closures
])
249 let from_stream stream
=
251 let s = String.create n
in
252 for i
= 0 to n
- 1 do
253 String.set
s i
(Stream.next stream
)
257 let header = readn Marshal.header_size
in
258 let datasize = Marshal.data_size
header 0 in
259 let datapart = readn datasize in
260 Marshal.from_string
(header ^
datapart) 0