Imported Upstream version 3.2.2
[debian/gnuradio] / pmt / src / lib / pmt_serialize.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2007 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <vector>
26 #include <pmt.h>
27 #include "pmt_int.h"
28 #include "pmt_serial_tags.h"
29
30 static pmt_t parse_pair(std::streambuf &sb);
31
32 // ----------------------------------------------------------------
33 // output primitives
34 // ----------------------------------------------------------------
35
36 static bool
37 serialize_untagged_u8(unsigned int i, std::streambuf &sb)
38 {
39   return sb.sputc((i >> 0) & 0xff) != std::streambuf::traits_type::eof();
40 }
41
42 // always writes big-endian
43 static bool
44 serialize_untagged_u16(unsigned int i, std::streambuf &sb)
45 {
46   sb.sputc((i >> 8) & 0xff);
47   return sb.sputc((i >> 0) & 0xff) != std::streambuf::traits_type::eof();
48 }
49
50 // always writes big-endian
51 static bool
52 serialize_untagged_u32(unsigned int i, std::streambuf &sb)
53 {
54   sb.sputc((i >> 24) & 0xff);
55   sb.sputc((i >> 16) & 0xff);
56   sb.sputc((i >>  8) & 0xff);
57   return sb.sputc((i >> 0) & 0xff) != std::streambuf::traits_type::eof();
58 }
59
60 #if 0
61 // always writes big-endian
62 static bool
63 serialize_untagged_u64(uint64_t i, std::streambuf &sb)
64 {
65   sb.sputc((i >> 56) & 0xff);
66   sb.sputc((i >> 48) & 0xff);
67   sb.sputc((i >> 40) & 0xff);
68   sb.sputc((i >> 32) & 0xff);
69   sb.sputc((i >> 24) & 0xff);
70   sb.sputc((i >> 16) & 0xff);
71   sb.sputc((i >>  8) & 0xff);
72   return sb.sputc((i >> 0) & 0xff) != std::streambuf::traits_type::eof();
73 }
74 #endif
75
76 // ----------------------------------------------------------------
77 // input primitives
78 // ----------------------------------------------------------------
79
80
81 // always reads big-endian
82 static bool
83 deserialize_untagged_u8(uint8_t *ip, std::streambuf &sb)
84 {
85   std::streambuf::traits_type::int_type  t;
86   int i;
87
88   t = sb.sbumpc();
89   i = t & 0xff;
90
91   *ip = i;
92   return t != std::streambuf::traits_type::eof();
93 }
94
95 // always reads big-endian
96 static bool
97 deserialize_untagged_u16(uint16_t *ip, std::streambuf &sb)
98 {
99   std::streambuf::traits_type::int_type  t;
100   int i;
101
102   t = sb.sbumpc();
103   i = t & 0xff;
104
105   t = sb.sbumpc();
106   i = (i << 8) | (t & 0xff);
107
108   *ip = i;
109   return t != std::streambuf::traits_type::eof();
110 }
111
112 // always reads big-endian
113 static bool
114 deserialize_untagged_u32(uint32_t *ip, std::streambuf &sb)
115 {
116   std::streambuf::traits_type::int_type  t;
117   int i;
118
119   t = sb.sbumpc();
120   i = t & 0xff;
121
122   t = sb.sbumpc();
123   i = (i << 8) | (t & 0xff);
124   t = sb.sbumpc();
125   i = (i << 8) | (t & 0xff);
126   t = sb.sbumpc();
127   i = (i << 8) | (t & 0xff);
128
129   *ip = i;
130   return t != std::streambuf::traits_type::eof();
131 }
132
133 #if 0
134 // always reads big-endian
135 static bool
136 deserialize_untagged_u64(uint64_t *ip, std::streambuf &sb)
137 {
138   std::streambuf::traits_type::int_type  t;
139   uint64_t i;
140
141   t = sb.sbumpc();
142   i = t & 0xff;
143
144   t = sb.sbumpc();
145   i = (i << 8) | (t & 0xff);
146   t = sb.sbumpc();
147   i = (i << 8) | (t & 0xff);
148   t = sb.sbumpc();
149   i = (i << 8) | (t & 0xff);
150   t = sb.sbumpc();
151   i = (i << 8) | (t & 0xff);
152   t = sb.sbumpc();
153   i = (i << 8) | (t & 0xff);
154   t = sb.sbumpc();
155   i = (i << 8) | (t & 0xff);
156   t = sb.sbumpc();
157   i = (i << 8) | (t & 0xff);
158
159   *ip = i;
160   return t != std::streambuf::traits_type::eof();
161 }
162 #endif
163
164 /*
165  * Write portable byte-serial representation of \p obj to \p sb
166  *
167  * N.B., Circular structures cause infinite recursion.
168  */
169 bool
170 pmt_serialize(pmt_t obj, std::streambuf &sb)
171 {
172   bool ok = true;
173
174  tail_recursion:
175
176   if (pmt_is_bool(obj)){
177     if (pmt_eq(obj, PMT_T))
178       return serialize_untagged_u8(PST_TRUE, sb);
179     else
180       return serialize_untagged_u8(PST_FALSE, sb);
181   }
182   
183   if (pmt_is_null(obj))
184     return serialize_untagged_u8(PST_NULL, sb);
185
186   if (pmt_is_symbol(obj)){
187     const std::string s = pmt_symbol_to_string(obj);
188     size_t len = s.size();
189     ok = serialize_untagged_u8(PST_SYMBOL, sb);
190     ok &= serialize_untagged_u16(len, sb);
191     for (size_t i = 0; i < len; i++)
192       ok &= serialize_untagged_u8(s[i], sb);
193     return ok;
194   }
195
196   if (pmt_is_pair(obj)){
197     ok = serialize_untagged_u8(PST_PAIR, sb);
198     ok &= pmt_serialize(pmt_car(obj), sb);
199     if (!ok)
200       return false;
201     obj = pmt_cdr(obj);
202     goto tail_recursion;
203   }
204
205   if (pmt_is_number(obj)){
206
207     if (pmt_is_integer(obj)){
208       long i = pmt_to_long(obj);
209       if (sizeof(long) > 4){
210         if (i < -2147483647 || i > 2147483647)
211           throw pmt_notimplemented("pmt_serialize (64-bit integers)", obj);
212       }
213       ok = serialize_untagged_u8(PST_INT32, sb);
214       ok &= serialize_untagged_u32(i, sb);
215       return ok;
216     }
217
218     if (pmt_is_real(obj))
219       throw pmt_notimplemented("pmt_serialize (real)", obj);
220
221     if (pmt_is_complex(obj))
222       throw pmt_notimplemented("pmt_serialize (complex)", obj);
223   }
224
225   if (pmt_is_vector(obj))
226     throw pmt_notimplemented("pmt_serialize (vector)", obj);
227
228   if (pmt_is_uniform_vector(obj))
229     throw pmt_notimplemented("pmt_serialize (uniform-vector)", obj);
230     
231   if (pmt_is_dict(obj))
232     throw pmt_notimplemented("pmt_serialize (dict)", obj);
233     
234
235   throw pmt_notimplemented("pmt_serialize (?)", obj);
236 }
237
238 /*
239  * Create obj from portable byte-serial representation
240  *
241  * Returns next obj from streambuf, or PMT_EOF at end of file.
242  * Throws exception on malformed input.
243  */
244 pmt_t
245 pmt_deserialize(std::streambuf &sb)
246 {
247   uint8_t       tag;
248   //uint8_t     u8;
249   uint16_t      u16;
250   uint32_t      u32;
251   //uint32_t    u64;
252   static char   tmpbuf[1024];
253
254   if (!deserialize_untagged_u8(&tag, sb))
255     return PMT_EOF;
256
257   switch (tag){
258   case PST_TRUE:
259     return PMT_T;
260     
261   case PST_FALSE:
262     return PMT_F;
263
264   case PST_NULL:
265     return PMT_NIL;
266
267   case PST_SYMBOL:
268     if (!deserialize_untagged_u16(&u16, sb))
269       goto error;
270     if (u16 > sizeof(tmpbuf))
271       throw pmt_notimplemented("pmt_deserialize: very long symbol",
272                                PMT_F);
273     if (sb.sgetn(tmpbuf, u16) != u16)
274       goto error;
275     return pmt_intern(std::string(tmpbuf, u16));
276
277   case PST_INT32:
278     if (!deserialize_untagged_u32(&u32, sb))
279       goto error;
280     return pmt_from_long((int32_t) u32);
281
282   case PST_PAIR:
283     return parse_pair(sb);
284
285   case PST_DOUBLE:
286   case PST_COMPLEX:
287   case PST_VECTOR:
288   case PST_DICT:
289   case PST_UNIFORM_VECTOR:
290   case PST_COMMENT:
291     throw pmt_notimplemented("pmt_deserialize: tag value = ",
292                              pmt_from_long(tag));
293     
294   default:
295     throw pmt_exception("pmt_deserialize: malformed input stream, tag value = ",
296                         pmt_from_long(tag));
297   }
298
299  error:
300   throw pmt_exception("pmt_deserialize: malformed input stream", PMT_F);
301 }
302
303 /*
304  * This is a mostly non-recursive implementation that allows us to
305  * deserialize very long lists w/o exhausting the evaluation stack.
306  *
307  * On entry we've already eaten the PST_PAIR tag.
308  */
309 pmt_t
310 parse_pair(std::streambuf &sb)
311 {
312   uint8_t tag;
313   pmt_t val, expr, lastnptr, nptr;
314
315   //
316   // Keep appending nodes until we get a non-PAIR cdr.
317   //
318   lastnptr = PMT_NIL;
319   while (1){
320     expr = pmt_deserialize(sb);         // read the car
321
322     nptr = pmt_cons(expr, PMT_NIL);     // build new cell
323     if (pmt_is_null(lastnptr))
324       val = nptr;
325     else
326       pmt_set_cdr(lastnptr, nptr);
327     lastnptr = nptr;
328
329     if (!deserialize_untagged_u8(&tag, sb))  // get tag of cdr
330       throw pmt_exception("pmt_deserialize: malformed input stream", PMT_F);
331
332     if (tag == PST_PAIR)
333       continue;                 // keep on looping...
334
335     if (tag == PST_NULL){
336       expr = PMT_NIL;
337       break;
338     }
339
340     //
341     // default: push tag back and use pmt_deserialize to get the cdr
342     //
343     sb.sungetc();
344     expr = pmt_deserialize(sb);
345     break;
346   }
347
348   //
349   // At this point, expr contains the value of the final cdr in the list.
350   //
351   pmt_set_cdr(lastnptr, expr);
352   return val;
353 }