Name | Executed | Routines | % | Executed | Lines | % | Unexecuted |
/home/matt/eu/rds/include/std/serialize.e | 8 | 8 | 100.00% | 108 | 126 | 85.71% | 18 |
Routine | Executed | Lines | Unexecuted | |
deserialize_object() | 22 | 28 | 78.57% | 6 |
serialize() | 17 | 23 | 73.91% | 6 |
deserialize_file() | 21 | 24 | 87.50% | 3 |
deserialize() | 5 | 6 | 83.33% | 1 |
dump() | 7 | 8 | 87.50% | 1 |
load() | 6 | 7 | 85.71% | 1 |
get4() | 6 | 6 | 100.00% | 0 |
getp4() | 6 | 6 | 100.00% | 0 |
# | Executed | |
1 | -- (c) Copyright - See License.txt | |
2 | -- | |
3 | namespace serialize | |
4 | ||
5 | --**** | |
6 | -- == Serialization of Euphoria Objects | |
7 | -- | |
8 | -- < | |
9 | ||
10 | include std/convert.e | |
11 | include std/machine.e | |
12 | ||
13 | -- Serialized format of Euphoria objects | |
14 | -- | |
15 | -- First byte: | |
16 | -- 0..248 -- immediate small integer, -9 to 239 | |
17 | -- since small negative integers -9..-1 might be common | |
18 | 101 | constant I2B = 249, -- 2-byte signed integer follows |
19 | 101 | I3B = 250, -- 3-byte signed integer follows |
20 | 101 | I4B = 251, -- 4-byte signed integer follows |
21 | 101 | F4B = 252, -- 4-byte f.p. number follows |
22 | 101 | F8B = 253, -- 8-byte f.p. number follows |
23 | 101 | S1B = 254, -- sequence, 1-byte length follows, then elements |
24 | 101 | S4B = 255 -- sequence, 4-byte length follows, then elements |
25 | ||
26 | 101 | constant MIN1B = -9, |
27 | 101 | MAX1B = 239, |
28 | 101 | MIN2B = -power(2, 15), |
29 | 101 | MAX2B = power(2, 15)-1, |
30 | 101 | MIN3B = -power(2, 23), |
31 | 101 | MAX3B = power(2, 23)-1, |
32 | 101 | MIN4B = -power(2, 31) |
33 | ||
34 | atom mem0, mem1, mem2, mem3 | |
35 | 101 | mem0 = allocate(4) |
36 | 101 | mem1 = mem0 + 1 |
37 | 101 | mem2 = mem0 + 2 |
38 | 101 | mem3 = mem0 + 3 |
39 | ||
40 | 4 | |
41 | -- read 4-byte value at current position in database file | |
42 | 4 | poke(mem0, getc(fh)) |
43 | 4 | poke(mem1, getc(fh)) |
44 | 4 | poke(mem2, getc(fh)) |
45 | 4 | poke(mem3, getc(fh)) |
46 | 4 | return peek4u(mem0) |
47 | end function | |
48 | ||
49 | 187 | |
50 | -- read a serialized Euphoria object from disk | |
51 | ||
52 | sequence s | |
53 | atom len | |
54 | ||
55 | 187 | if c = 0 then |
56 | 21 | c = getc(fh) |
57 | 21 | if c < I2B then |
58 | 0 | return c + MIN1B |
59 | end if | |
60 | end if | |
61 | ||
62 | 187 | switch c with fallthru do |
63 | case I2B then | |
64 | 48 | return getc(fh) + |
65 | #100 * getc(fh) + | |
66 | MIN2B | |
67 | ||
68 | case I3B then | |
69 | 0 | return getc(fh) + |
70 | #100 * getc(fh) + | |
71 | #10000 * getc(fh) + | |
72 | MIN3B | |
73 | ||
74 | case I4B then | |
75 | 3 | return get4(fh) + MIN4B |
76 | ||
77 | case F4B then | |
78 | 0 | return float32_to_atom({getc(fh), getc(fh), |
79 | getc(fh), getc(fh)}) | |
80 | ||
81 | case F8B then | |
82 | 9 | return float64_to_atom({getc(fh), getc(fh), |
83 | getc(fh), getc(fh), | |
84 | getc(fh), getc(fh), | |
85 | getc(fh), getc(fh)}) | |
86 | ||
87 | case else | |
88 | -- sequence | |
89 | 127 | if c = S1B then |
90 | 126 | len = getc(fh) |
91 | else | |
92 | 1 | len = get4(fh) |
93 | end if | |
94 | 127 | if len < 0 or not integer(len) then |
95 | 1 | return 0 |
96 | end if | |
97 | 126 | s = repeat(0, len) |
98 | 126 | for i = 1 to len do |
99 | -- in-line small integer for greater speed on strings | |
100 | 1029 | c = getc(fh) |
101 | 1029 | if c < I2B then |
102 | 863 | s[i] = c + MIN1B |
103 | else | |
104 | 166 | s[i] = deserialize_file(fh, c) |
105 | end if | |
106 | 1029 | end for |
107 | 126 | return s |
108 | end switch | |
109 | end function | |
110 | ||
111 | 2 | |
112 | 2 | poke(mem0, sdata[pos+0]) |
113 | 2 | poke(mem1, sdata[pos+1]) |
114 | 2 | poke(mem2, sdata[pos+2]) |
115 | 2 | poke(mem3, sdata[pos+3]) |
116 | 2 | return peek4u(mem0) |
117 | end function | |
118 | ||
119 | 11 | |
120 | -- read a serialized Euphoria object from a sequence | |
121 | ||
122 | sequence s | |
123 | integer len | |
124 | ||
125 | 11 | if c = 0 then |
126 | 6 | c = sdata[pos] |
127 | 6 | pos += 1 |
128 | 6 | if c < I2B then |
129 | 0 | return {c + MIN1B, pos} |
130 | end if | |
131 | end if | |
132 | ||
133 | 11 | switch c with fallthru do |
134 | case I2B then | |
135 | 0 | return {sdata[pos] + |
136 | #100 * sdata[pos+1] + | |
137 | MIN2B, pos + 2} | |
138 | ||
139 | case I3B then | |
140 | 0 | return {sdata[pos] + |
141 | #100 * sdata[pos+1] + | |
142 | #10000 * sdata[pos+2] + | |
143 | MIN3B, pos + 3} | |
144 | ||
145 | case I4B then | |
146 | 2 | return {getp4(sdata, pos) + MIN4B, pos + 4} |
147 | ||
148 | case F4B then | |
149 | 0 | return {float32_to_atom({sdata[pos], sdata[pos+1], |
150 | sdata[pos+2], sdata[pos+3]}), pos + 4} | |
151 | ||
152 | case F8B then | |
153 | 2 | return {float64_to_atom({sdata[pos], sdata[pos+1], |
154 | sdata[pos+2], sdata[pos+3], | |
155 | sdata[pos+4], sdata[pos+5], | |
156 | sdata[pos+6], sdata[pos+7]}), pos + 8} | |
157 | ||
158 | case else | |
159 | -- sequence | |
160 | 7 | if c = S1B then |
161 | 7 | len = sdata[pos] |
162 | 7 | pos += 1 |
163 | else | |
164 | 0 | len = getp4(sdata, pos) |
165 | 0 | pos += 4 |
166 | end if | |
167 | 7 | s = repeat(0, len) |
168 | 7 | for i = 1 to len do |
169 | -- in-line small integer for greater speed on strings | |
170 | 81 | c = sdata[pos] |
171 | 81 | pos += 1 |
172 | 81 | if c < I2B then |
173 | 76 | s[i] = c + MIN1B |
174 | else | |
175 | 5 | sequence temp = deserialize_object(sdata, pos, c) |
176 | 5 | s[i] = temp[1] |
177 | 5 | pos = temp[2] |
178 | end if | |
179 | 81 | end for |
180 | 7 | return {s, pos} |
181 | end switch | |
182 | end function | |
183 | ||
184 | --**** | |
185 | -- === Routines | |
186 | ||
187 | --** | |
188 | -- Convert a serialized object in to a standard Euphoria object. | |
189 | -- | |
190 | -- Parameters: | |
191 | -- # ##sdata## : either a sequence containing one or more concatenated serialized objects or | |
192 | -- an open file handle. If this is a file handle, the current position in the | |
193 | -- file is assumed to be at a serialized object in the file. | |
194 | -- # ##pos## : optional index into ##sdata##. If omitted 1 is assumed. The index must | |
195 | -- point to the start of a serialized object. | |
196 | -- | |
197 | -- Returns: | |
198 | -- The return **value**, depends on the input type. | |
199 | -- * If ##sdata## is a file handle then | |
200 | -- this function returns a Euphoria object that had been stored in the file, and | |
201 | -- moves the current file to the first byte after the stored object. | |
202 | -- * If ##sdata## is a sequence then this returns a two-element sequence. | |
203 | -- The //first// element is the Euphoria object that corresponds to the serialized | |
204 | -- object that begins at index ##pos##, and the //second// element is the index | |
205 | -- position in the input parameter just after the serialized object. | |
206 | -- | |
207 | -- | |
208 | -- Comments: | |
209 | -- A serialized object is one that has been returned from the [[:serialize]] function. | |
210 | -- | |
211 | -- Example 1: | |
212 | -- | |
213 | -- sequence objcache | |
214 | -- objcache = serialize(FirstName) & | |
215 | -- serialize(LastName) & | |
216 | -- serialize(PhoneNumber) & | |
217 | -- serialize(Address) | |
218 | -- | |
219 | -- sequence res | |
220 | -- integer pos = 1 | |
221 | -- res = deserialize( objcache , pos) | |
222 | -- FirstName = res[1] pos = res[2] | |
223 | -- res = deserialize( objcache , pos) | |
224 | -- LastName = res[1] pos = res[2] | |
225 | -- res = deserialize( objcache , pos) | |
226 | -- PhoneNumber = res[1] pos = res[2] | |
227 | -- res = deserialize( objcache , pos) | |
228 | -- Address = res[1] pos = res[2] | |
229 | -- | |
230 | -- | |
231 | -- Example 2: | |
232 | -- | |
233 | -- sequence objcache | |
234 | -- objcache = serialize({FirstName, | |
235 | -- LastName, | |
236 | -- PhoneNumber, | |
237 | -- Address}) | |
238 | -- | |
239 | -- sequence res | |
240 | -- res = deserialize( objcache ) | |
241 | -- FirstName = res[1][1] | |
242 | -- LastName = res[1][2] | |
243 | -- PhoneNumber = res[1][3] | |
244 | -- Address = res[1][4] | |
245 | -- | |
246 | -- | |
247 | -- Example 3: | |
248 | -- | |
249 | -- integer fh | |
250 | -- fh = open("cust.dat", "wb") | |
251 | -- puts(fh, serialize(FirstName)) | |
252 | -- puts(fh, serialize(LastName)) | |
253 | -- puts(fh, serialize(PhoneNumber)) | |
254 | -- puts(fh, serialize(Address)) | |
255 | -- close(fh) | |
256 | -- | |
257 | -- fh = open("cust.dat", "rb") | |
258 | -- FirstName = deserialize(fh) | |
259 | -- LastName = deserialize(fh) | |
260 | -- PhoneNumber = deserialize(fh) | |
261 | -- Address = deserialize(fh) | |
262 | -- close(fh) | |
263 | -- | |
264 | -- | |
265 | -- Example 4: | |
266 | -- | |
267 | -- integer fh | |
268 | -- fh = open("cust.dat", "wb") | |
269 | -- puts(fh, serialize({FirstName, | |
270 | -- LastName, | |
271 | -- PhoneNumber, | |
272 | -- Address})) | |
273 | -- close(fh) | |
274 | -- | |
275 | -- sequence res | |
276 | -- fh = open("cust.dat", "rb") | |
277 | -- res = deserialize(fh) | |
278 | -- close(fh) | |
279 | -- FirstName = res[1] | |
280 | -- LastName = res[2] | |
281 | -- PhoneNumber = res[3] | |
282 | -- Address = res[4] | |
283 | -- | |
284 | -- | |
285 | ||
286 | 27 | |
287 | -- read a serialized Euphoria object | |
288 | ||
289 | 27 | if integer(sdata) then |
290 | 21 | return deserialize_file(sdata, 0) |
291 | end if | |
292 | ||
293 | 6 | if atom(sdata) then |
294 | 0 | return 0 |
295 | end if | |
296 | ||
297 | 6 | return deserialize_object(sdata, pos, 0) |
298 | ||
299 | end function | |
300 | ||
301 | --** | |
302 | -- Convert a standard Euphoria object in to a serialized version of it. | |
303 | -- | |
304 | -- Parameters: | |
305 | -- # ##euobj## : any Euphoria object. | |
306 | -- | |
307 | -- Returns: | |
308 | -- A **sequence**, this is the serialized version of the input object. | |
309 | -- | |
310 | -- Comments: | |
311 | -- A serialized object is one that has been converted to a set of byte values. This | |
312 | -- can then by written directly out to a file for storage. | |
313 | -- | |
314 | -- You can use the [[:deserialize]] function to convert it back into a standard | |
315 | -- Euphoria object. | |
316 | -- | |
317 | -- Example 1: | |
318 | -- | |
319 | -- integer fh | |
320 | -- fh = open("cust.dat", "wb") | |
321 | -- puts(fh, serialize(FirstName)) | |
322 | -- puts(fh, serialize(LastName)) | |
323 | -- puts(fh, serialize(PhoneNumber)) | |
324 | -- puts(fh, serialize(Address)) | |
325 | -- close(fh) | |
326 | -- | |
327 | -- fh = open("cust.dat", "rb") | |
328 | -- FirstName = deserialize(fh) | |
329 | -- LastName = deserialize(fh) | |
330 | -- PhoneNumber = deserialize(fh) | |
331 | -- Address = deserialize(fh) | |
332 | -- close(fh) | |
333 | -- | |
334 | -- | |
335 | -- Example 2: | |
336 | -- | |
337 | -- integer fh | |
338 | -- fh = open("cust.dat", "wb") | |
339 | -- puts(fh, serialize({FirstName, | |
340 | -- LastName, | |
341 | -- PhoneNumber, | |
342 | -- Address})) | |
343 | -- close(fh) | |
344 | -- | |
345 | -- sequence res | |
346 | -- fh = open("cust.dat", "rb") | |
347 | -- res = deserialize(fh) | |
348 | -- close(fh) | |
349 | -- FirstName = res[1] | |
350 | -- LastName = res[2] | |
351 | -- PhoneNumber = res[3] | |
352 | -- Address = res[4] | |
353 | -- | |
354 | -- | |
355 | 730 | |
356 | -- return the serialized representation of a Euphoria object | |
357 | -- as a sequence of bytes | |
358 | sequence x4, s | |
359 | ||
360 | 730 | if integer(x) then |
361 | 628 | if x >= MIN1B and x <= MAX1B then |
362 | 623 | return {x - MIN1B} |
363 | ||
364 | 5 | elsif x >= MIN2B and x <= MAX2B then |
365 | 0 | x -= MIN2B |
366 | 0 | return {I2B, and_bits(x, #FF), floor(x / #100)} |
367 | ||
368 | 5 | elsif x >= MIN3B and x <= MAX3B then |
369 | 0 | x -= MIN3B |
370 | 0 | return {I3B, and_bits(x, #FF), and_bits(floor(x / #100), #FF), floor(x / #10000)} |
371 | ||
372 | else | |
373 | 5 | return I4B & int_to_bytes(x-MIN4B) |
374 | ||
375 | end if | |
376 | ||
377 | 102 | elsif atom(x) then |
378 | -- floating point | |
379 | 11 | x4 = atom_to_float32(x) |
380 | 11 | if x = float32_to_atom(x4) then |
381 | -- can represent as 4-byte float | |
382 | 0 | return F4B & x4 |
383 | else | |
384 | 11 | return F8B & atom_to_float64(x) |
385 | end if | |
386 | ||
387 | else | |
388 | -- sequence | |
389 | 91 | if length(x) <= 255 then |
390 | 91 | s = {S1B, length(x)} |
391 | else | |
392 | 0 | s = S4B & int_to_bytes(length(x)) |
393 | end if | |
394 | 91 | for i = 1 to length(x) do |
395 | 708 | s &= serialize(x[i]) |
396 | 708 | end for |
397 | 91 | return s |
398 | end if | |
399 | end function | |
400 | ||
401 | --** | |
402 | -- Saves a Euphoria object to disk in a binary format. | |
403 | -- | |
404 | -- Parameters: | |
405 | -- # ##data## : any Euphoria object. | |
406 | -- # ##filename## : the name of the file to save it to. | |
407 | -- | |
408 | -- Returns: | |
409 | -- An **integer**, 0 if the function fails, otherwise the number of bytes in the | |
410 | -- created file. | |
411 | -- | |
412 | -- Comments: | |
413 | -- If the named file doesn't exist it is created, otherwise it is overwritten. | |
414 | -- | |
415 | -- You can use the [[:load]] function to recover the data from the file. | |
416 | -- | |
417 | -- Example : | |
418 | -- | |
419 | -- include std/serialize.e | |
420 | -- integer size = dump(myData, theFileName) | |
421 | -- if size = 0 then | |
422 | -- puts(1, "Failed to save data to file\n") | |
423 | -- else | |
424 | -- printf(1, "Saved file is %d bytes long\n", size) | |
425 | -- end if | |
426 | -- | |
427 | -- | |
428 | 1 | |
429 | integer fh | |
430 | sequence sdata | |
431 | ||
432 | 1 | fh = open(filename, "wb") |
433 | 1 | if fh < 0 then |
434 | 0 | return 0 |
435 | end if | |
436 | ||
437 | 1 | sdata = serialize(data) |
438 | 1 | puts(fh, sdata) |
439 | ||
440 | 1 | close(fh) |
441 | ||
442 | 1 | return length(sdata) -- Length is always > 0 |
443 | end function | |
444 | ||
445 | --** | |
446 | -- Restores a Euphoria object that has been saved to disk by [[:dump]]. | |
447 | -- | |
448 | -- Parameters: | |
449 | -- # ##filename## : the name of the file to restore it from. | |
450 | -- | |
451 | -- Returns: | |
452 | -- A **sequence**, the first element is the result code. If the result code is 0 | |
453 | -- then it means that the function failed, otherwise the restored data is in the | |
454 | -- second element. | |
455 | -- | |
456 | -- Comments: | |
457 | -- This is used to load back data from a file created by the [[:dump]] | |
458 | -- function. | |
459 | -- | |
460 | -- Example : | |
461 | -- | |
462 | -- include std/serialize.e | |
463 | -- sequence mydata = load(theFileName) | |
464 | -- if mydata[1] = 0 then | |
465 | -- puts(1, "Failed to load data from file\n") | |
466 | -- else | |
467 | -- mydata = mydata[2] -- Restored data is in second element. | |
468 | -- end if | |
469 | -- | |
470 | -- | |
471 | 1 | |
472 | integer fh | |
473 | sequence sdata | |
474 | ||
475 | 1 | fh = open(filename, "rb") |
476 | 1 | if fh < 0 then |
477 | 0 | return {0} |
478 | end if | |
479 | ||
480 | 1 | sdata = deserialize(fh) |
481 | ||
482 | 1 | close(fh) |
483 | 1 | return {1, sdata} |
484 | end function |