blob: 59af6a7bb6019f269fe394245d6505bc97441975 [file] [log] [blame]
José Fonseca299a1b32012-01-26 20:32:59 +00001/**************************************************************************
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>
José Fonseca447576d2012-01-27 14:27:13 +000035#include <stdint.h>
José Fonseca299a1b32012-01-26 20:32:59 +000036
37#include <ostream>
38#include <string>
José Fonsecaebb75cb2012-11-17 11:44:43 +000039#include <limits>
José Fonseca299a1b32012-01-26 20:32:59 +000040
41
42class PickleWriter
43{
44private:
45 std::ostream &os;
46
47 /*
48 * Python pickle opcodes. See pickle.py and pickletools.py from Python
49 * standard library for details.
50 */
51 enum Opcode {
52 MARK = '(',
53 STOP = '.',
54 POP = '0',
55 POP_MARK = '1',
56 DUP = '2',
57 FLOAT = 'F',
58 INT = 'I',
59 BININT = 'J',
60 BININT1 = 'K',
61 LONG = 'L',
62 BININT2 = 'M',
63 NONE = 'N',
64 PERSID = 'P',
65 BINPERSID = 'Q',
66 REDUCE = 'R',
67 STRING = 'S',
68 BINSTRING = 'T',
69 SHORT_BINSTRING = 'U',
70 UNICODE = 'V',
71 BINUNICODE = 'X',
72 APPEND = 'a',
73 BUILD = 'b',
74 GLOBAL = 'c',
75 DICT = 'd',
76 EMPTY_DICT = '}',
77 APPENDS = 'e',
78 GET = 'g',
79 BINGET = 'h',
80 INST = 'i',
81 LONG_BINGET = 'j',
82 LIST = 'l',
83 EMPTY_LIST = ']',
84 OBJ = 'o',
85 PUT = 'p',
86 BINPUT = 'q',
87 LONG_BINPUT = 'r',
88 SETITEM = 's',
89 TUPLE = 't',
90 EMPTY_TUPLE = ')',
91 SETITEMS = 'u',
92 BINFLOAT = 'G',
93
José Fonseca447576d2012-01-27 14:27:13 +000094 PROTO = '\x80',
95 NEWOBJ = '\x81',
96 EXT1 = '\x82',
97 EXT2 = '\x83',
98 EXT4 = '\x84',
99 TUPLE1 = '\x85',
100 TUPLE2 = '\x86',
101 TUPLE3 = '\x87',
102 NEWTRUE = '\x88',
103 NEWFALSE = '\x89',
104 LONG1 = '\x8a',
105 LONG4 = '\x8b',
José Fonseca299a1b32012-01-26 20:32:59 +0000106 };
107
108public:
José Fonseca447576d2012-01-27 14:27:13 +0000109 PickleWriter(std::ostream &_os) :
José Fonsecab16a4a82012-03-16 08:21:29 +0000110 os(_os) {
111 }
112
113 inline void begin() {
José Fonseca299a1b32012-01-26 20:32:59 +0000114 os.put(PROTO);
115 os.put(2);
116 }
117
José Fonsecab16a4a82012-03-16 08:21:29 +0000118 inline void end() {
José Fonseca299a1b32012-01-26 20:32:59 +0000119 os.put(STOP);
120 }
121
122 inline void beginDict() {
123 os.put(EMPTY_DICT);
124 os.put(BINPUT);
125 os.put(1);
126 }
127
128 inline void endDict() {
129 }
130
131 inline void beginItem() {
132 }
133
134 inline void beginItem(const char * name) {
135 writeString(name);
136 }
137
138 inline void beginItem(const std::string &name) {
139 beginItem(name.c_str());
140 }
141
142 inline void endItem(void) {
143 os.put(SETITEM);
144 }
145
146 inline void beginList() {
147 os.put(EMPTY_LIST);
148 os.put(BINPUT);
149 os.put(1);
150 os.put(MARK);
151 }
152
153 inline void endList(void) {
154 os.put(APPENDS);
155 }
156
157 inline void beginTuple() {
158 os.put(MARK);
159 }
160
161 inline void endTuple(void) {
162 os.put(TUPLE);
163 }
164
José Fonseca299a1b32012-01-26 20:32:59 +0000165 inline void writeString(const char *s, size_t length) {
166 if (!s) {
167 writeNone();
168 return;
169 }
170
171 if (length < 256) {
172 os.put(SHORT_BINSTRING);
173 os.put(length);
174 } else {
175 os.put(BINSTRING);
José Fonseca447576d2012-01-27 14:27:13 +0000176 putInt32(length);
José Fonseca299a1b32012-01-26 20:32:59 +0000177 }
178 os.write(s, length);
179
180 os.put(BINPUT);
181 os.put(1);
182 }
183
184 inline void writeString(const char *s) {
185 if (!s) {
186 writeNone();
187 return;
188 }
189
190 writeString(s, strlen(s));
191 }
192
193 inline void writeString(const std::string &s) {
194 writeString(s.c_str(), s.size());
195 }
196
197 inline void writeNone(void) {
198 os.put(NONE);
199 }
200
201 inline void writeBool(bool b) {
202 os.put(b ? NEWTRUE : NEWFALSE);
203 }
204
José Fonseca447576d2012-01-27 14:27:13 +0000205 inline void writeInt(uint8_t i) {
206 os.put(BININT1);
207 os.put(i);
José Fonseca299a1b32012-01-26 20:32:59 +0000208 }
209
José Fonseca447576d2012-01-27 14:27:13 +0000210 inline void writeInt(uint16_t i) {
211 if (i < 0x100) {
212 writeInt((uint8_t)i);
213 } else {
214 os.put(BININT2);
215 putInt16(i);
216 }
217 }
218
219 inline void writeInt(int32_t i) {
220 if (0 <= i && i < 0x10000) {
221 writeInt((uint16_t)i);
222 } else {
223 os.put(BININT);
224 putInt32(i);
225 }
226 }
227
228 inline void writeInt(uint32_t i) {
229 if (i < 0x8000000) {
230 writeInt((int32_t)i);
231 } else {
232 writeLong(i);
233 }
234 }
235
236 inline void writeInt(long long i) {
237 if (-0x8000000 <= i && i < 0x8000000) {
238 writeInt((int32_t)i);
239 } else {
240 writeLong(i);
241 }
242 }
243
244 inline void writeInt(unsigned long long i) {
245 if (i < 0x8000000) {
246 writeInt((int32_t)i);
247 } else {
248 writeLong(i);
249 }
250 }
251
252 inline void writeFloat(double f) {
253 union {
254 double f;
255 char c[8];
256 } u;
257
258 assert(sizeof u.f == sizeof u.c);
259 u.f = f;
260
261 os.put(BINFLOAT);
262 os.put(u.c[7]);
263 os.put(u.c[6]);
264 os.put(u.c[5]);
265 os.put(u.c[4]);
266 os.put(u.c[3]);
267 os.put(u.c[2]);
268 os.put(u.c[1]);
269 os.put(u.c[0]);
270 }
271
José Fonsecaeba2dec2012-03-17 16:12:22 +0000272 inline void writeByteArray(const void *buf, size_t length) {
273 os.put(GLOBAL);
274 os << "__builtin__\nbytearray\n";
275 os.put(BINPUT);
276 os.put(1);
277 writeString(static_cast<const char *>(buf), length);
278 os.put(TUPLE1);
279 os.put(REDUCE);
280 }
281
José Fonseca447576d2012-01-27 14:27:13 +0000282protected:
283 inline void putInt16(uint16_t i) {
284 os.put( i & 0xff);
285 os.put( i >> 8 );
286 }
287
288 inline void putInt32(uint32_t i) {
289 os.put( i & 0xff);
290 os.put((i >> 8) & 0xff);
291 os.put((i >> 16) & 0xff);
292 os.put( i >> 24 );
293 }
294
295 template< class T >
296 inline void writeLong(T l) {
297 os.put(LONG1);
298
299 if (l == 0) {
300 os.put(0);
301 return;
302 }
303
José Fonseca447576d2012-01-27 14:27:13 +0000304 // Same as l >> (8 * sizeof l), but without the warnings
José Fonsecaebb75cb2012-11-17 11:44:43 +0000305 T sign;
306 if (std::numeric_limits<T>::is_signed) {
307 sign = l < 0 ? ~0 : 0;
308 } else {
309 sign = 0;
310 }
José Fonsecad64e5b22012-10-05 20:55:28 +0100311
José Fonsecafbab35d2014-05-13 17:36:42 +0100312 // Count how many bytes we need to represent the long integer.
José Fonsecad64e5b22012-10-05 20:55:28 +0100313 T sl = l;
314 unsigned c = 0;
315 do {
José Fonseca447576d2012-01-27 14:27:13 +0000316 ++c;
José Fonsecafbab35d2014-05-13 17:36:42 +0100317 sl >>= 8;
318 } while (sl != sign);
José Fonsecad64e5b22012-10-05 20:55:28 +0100319
José Fonseca447576d2012-01-27 14:27:13 +0000320 // Add an extra byte if sign bit doesn't match
321 if (((l >> (8 * c - 1)) & 1) != ((l >> (8 * sizeof l - 1)) & 1)) {
322 ++c;
323 }
324 os.put(c);
325
326 for (unsigned i = 0; i < c; ++ i) {
327 os.put(l & 0xff);
328 l >>= 8;
329 }
José Fonseca299a1b32012-01-26 20:32:59 +0000330 }
331};
332
333#endif /* _Pickle_HPP_ */