]> git.cworth.org Git - apitrace/blob - common/pickle.hpp
Pickle more data types in binary.
[apitrace] / common / pickle.hpp
1 /**************************************************************************
2  *
3  * Copyright 2012 Jose Fonseca
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 /*
27  * Python pickle writer
28  */
29
30 #ifndef _PICKLE_HPP_
31 #define _PICKLE_HPP_
32
33 #include <assert.h>
34 #include <stddef.h>
35 #include <stdint.h>
36
37 #include <ostream>
38 #include <string>
39
40
41 class PickleWriter
42 {
43 private:
44     std::ostream &os;
45
46     /*
47      * Python pickle opcodes.  See pickle.py and pickletools.py from Python
48      * standard library for details.
49      */
50     enum Opcode {
51         MARK            = '(',
52         STOP            = '.',
53         POP             = '0',
54         POP_MARK        = '1',
55         DUP             = '2',
56         FLOAT           = 'F',
57         INT             = 'I',
58         BININT          = 'J',
59         BININT1         = 'K',
60         LONG            = 'L',
61         BININT2         = 'M',
62         NONE            = 'N',
63         PERSID          = 'P',
64         BINPERSID       = 'Q',
65         REDUCE          = 'R',
66         STRING          = 'S',
67         BINSTRING       = 'T',
68         SHORT_BINSTRING = 'U',
69         UNICODE         = 'V',
70         BINUNICODE      = 'X',
71         APPEND          = 'a',
72         BUILD           = 'b',
73         GLOBAL          = 'c',
74         DICT            = 'd',
75         EMPTY_DICT      = '}',
76         APPENDS         = 'e',
77         GET             = 'g',
78         BINGET          = 'h',
79         INST            = 'i',
80         LONG_BINGET     = 'j',
81         LIST            = 'l',
82         EMPTY_LIST      = ']',
83         OBJ             = 'o',
84         PUT             = 'p',
85         BINPUT          = 'q',
86         LONG_BINPUT     = 'r',
87         SETITEM         = 's',
88         TUPLE           = 't',
89         EMPTY_TUPLE     = ')',
90         SETITEMS        = 'u',
91         BINFLOAT        = 'G',
92
93         PROTO           = '\x80',
94         NEWOBJ          = '\x81',
95         EXT1            = '\x82',
96         EXT2            = '\x83',
97         EXT4            = '\x84',
98         TUPLE1          = '\x85',
99         TUPLE2          = '\x86',
100         TUPLE3          = '\x87',
101         NEWTRUE         = '\x88',
102         NEWFALSE        = '\x89',
103         LONG1           = '\x8a',
104         LONG4           = '\x8b',
105     };
106
107 public:
108     PickleWriter(std::ostream &_os) :
109         os(_os)
110     {
111         os.put(PROTO);
112         os.put(2);
113     }
114
115     ~PickleWriter() {
116         os.put(STOP);
117     }
118
119     inline void beginDict() {
120         os.put(EMPTY_DICT);
121         os.put(BINPUT);
122         os.put(1);
123     }
124
125     inline void endDict() {
126     }
127
128     inline void beginItem() {
129     }
130
131     inline void beginItem(const char * name) {
132         writeString(name);
133     }
134
135     inline void beginItem(const std::string &name) {
136         beginItem(name.c_str());
137     }
138
139     inline void endItem(void) {
140         os.put(SETITEM);
141     }
142
143     inline void beginList() {
144         os.put(EMPTY_LIST);
145         os.put(BINPUT);
146         os.put(1);
147         os.put(MARK);
148     }
149
150     inline void endList(void) {
151         os.put(APPENDS);
152     }
153
154     inline void beginTuple() {
155         os.put(MARK);
156     }
157
158     inline void endTuple(void) {
159         os.put(TUPLE);
160     }
161
162     inline void writeString(const char *s, size_t length) {
163         if (!s) {
164             writeNone();
165             return;
166         }
167
168         if (length < 256) {
169             os.put(SHORT_BINSTRING);
170             os.put(length);
171         } else {
172             os.put(BINSTRING);
173             putInt32(length);
174         }
175         os.write(s, length);
176
177         os.put(BINPUT);
178         os.put(1);
179     }
180
181     inline void writeString(const char *s) {
182         if (!s) {
183             writeNone();
184             return;
185         }
186
187         writeString(s, strlen(s));
188     }
189
190     inline void writeString(const std::string &s) {
191         writeString(s.c_str(), s.size());
192     }
193
194     inline void writeNone(void) {
195         os.put(NONE);
196     }
197
198     inline void writeBool(bool b) {
199         os.put(b ? NEWTRUE : NEWFALSE);
200     }
201
202     inline void writeInt(uint8_t i) {
203         os.put(BININT1);
204         os.put(i);
205     }
206
207     inline void writeInt(uint16_t i) {
208         if (i < 0x100) {
209             writeInt((uint8_t)i);
210         } else {
211             os.put(BININT2);
212             putInt16(i);
213         }
214     }
215
216     inline void writeInt(int32_t i) {
217         if (0 <= i && i < 0x10000) {
218             writeInt((uint16_t)i);
219         } else {
220             os.put(BININT);
221             putInt32(i);
222         }
223     }
224
225     inline void writeInt(uint32_t i) {
226         if (i < 0x8000000) {
227             writeInt((int32_t)i);
228         } else {
229             writeLong(i);
230         }
231     }
232
233     inline void writeInt(long long i) {
234         if (-0x8000000 <= i && i < 0x8000000) {
235             writeInt((int32_t)i);
236         } else {
237             writeLong(i);
238         }
239     }
240
241     inline void writeInt(unsigned long long i) {
242         if (i < 0x8000000) {
243             writeInt((int32_t)i);
244         } else {
245             writeLong(i);
246         }
247     }
248
249     inline void writeFloat(double f) {
250         union {
251             double f;
252             char c[8];
253         } u;
254
255         assert(sizeof u.f == sizeof u.c);
256         u.f = f;
257
258         os.put(BINFLOAT);
259         os.put(u.c[7]);
260         os.put(u.c[6]);
261         os.put(u.c[5]);
262         os.put(u.c[4]);
263         os.put(u.c[3]);
264         os.put(u.c[2]);
265         os.put(u.c[1]);
266         os.put(u.c[0]);
267     }
268
269 protected:
270     inline void putInt16(uint16_t i) {
271         os.put( i        & 0xff);
272         os.put( i >>  8        );
273     }
274
275     inline void putInt32(uint32_t i) {
276         os.put( i        & 0xff);
277         os.put((i >>  8) & 0xff);
278         os.put((i >> 16) & 0xff);
279         os.put( i >> 24        );
280     }
281
282     template< class T >
283     inline void writeLong(T l) {
284         os.put(LONG1);
285
286         if (l == 0) {
287             os.put(0);
288             return;
289         }
290
291         unsigned c = 1;
292         // Same as l >> (8 * sizeof l), but without the warnings
293         T sign = l < 0 ? ~0 : 0;
294         while ((l >> (8 * c)) != sign) {
295             ++c;
296         }
297         // Add an extra byte if sign bit doesn't match
298         if (((l >> (8 * c - 1)) & 1) != ((l >> (8 * sizeof l - 1)) & 1)) {
299             ++c;
300         }
301         os.put(c);
302
303         for (unsigned i = 0; i < c; ++ i) {
304             os.put(l & 0xff);
305             l >>= 8;
306         }
307     }
308 };
309
310 #endif /* _Pickle_HPP_ */