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