]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | // This source code is licensed under both the GPLv2 (found in the | |
3 | // COPYING file in the root directory) and Apache 2.0 License | |
4 | // (found in the LICENSE.Apache file in the root directory). | |
7c673cae FG |
5 | |
6 | /* | |
7 | * This file defines FbsonWriterT (template) and FbsonWriter. | |
8 | * | |
9 | * FbsonWriterT is a template class which implements an FBSON serializer. | |
10 | * Users call various write functions of FbsonWriterT object to write values | |
11 | * directly to FBSON packed bytes. All write functions of value or key return | |
12 | * the number of bytes written to FBSON, or 0 if there is an error. To write an | |
13 | * object, an array, or a string, you must call writeStart[..] before writing | |
14 | * values or key, and call writeEnd[..] after finishing at the end. | |
15 | * | |
16 | * By default, an FbsonWriterT object creates an output stream buffer. | |
17 | * Alternatively, you can also pass any output stream object to a writer, as | |
18 | * long as the stream object implements some basic functions of std::ostream | |
19 | * (such as FbsonOutStream, see FbsonStream.h). | |
20 | * | |
21 | * FbsonWriter specializes FbsonWriterT with FbsonOutStream type (see | |
22 | * FbsonStream.h). So unless you want to provide own a different output stream | |
23 | * type, use FbsonParser object. | |
24 | * | |
25 | * @author Tian Xia <tianx@fb.com> | |
26 | */ | |
27 | ||
11fdf7f2 | 28 | #pragma once |
7c673cae FG |
29 | |
30 | #include <stack> | |
31 | #include "FbsonDocument.h" | |
32 | #include "FbsonStream.h" | |
33 | ||
11fdf7f2 TL |
34 | // conversion' conversion from 'type1' to 'type2', possible loss of data |
35 | // Can not restore at the header end as the warnings are emitted at the point of | |
36 | // template instantiation | |
37 | #if defined(_MSC_VER) | |
38 | #pragma warning(disable : 4244) | |
39 | #endif | |
40 | ||
7c673cae FG |
41 | namespace fbson { |
42 | ||
43 | template <class OS_TYPE> | |
44 | class FbsonWriterT { | |
45 | public: | |
46 | FbsonWriterT() | |
47 | : alloc_(true), hasHdr_(false), kvState_(WS_Value), str_pos_(0) { | |
48 | os_ = new OS_TYPE(); | |
49 | } | |
50 | ||
51 | explicit FbsonWriterT(OS_TYPE& os) | |
52 | : os_(&os), | |
53 | alloc_(false), | |
54 | hasHdr_(false), | |
55 | kvState_(WS_Value), | |
56 | str_pos_(0) {} | |
57 | ||
58 | ~FbsonWriterT() { | |
59 | if (alloc_) { | |
60 | delete os_; | |
61 | } | |
62 | } | |
63 | ||
64 | void reset() { | |
65 | os_->clear(); | |
66 | os_->seekp(0); | |
67 | hasHdr_ = false; | |
68 | kvState_ = WS_Value; | |
69 | for (; !stack_.empty(); stack_.pop()) | |
70 | ; | |
71 | } | |
72 | ||
73 | // write a key string (or key id if an external dict is provided) | |
74 | uint32_t writeKey(const char* key, | |
75 | uint8_t len, | |
76 | hDictInsert handler = nullptr) { | |
77 | if (len && !stack_.empty() && verifyKeyState()) { | |
78 | int key_id = -1; | |
79 | if (handler) { | |
80 | key_id = handler(key, len); | |
81 | } | |
82 | ||
83 | uint32_t size = sizeof(uint8_t); | |
84 | if (key_id < 0) { | |
85 | os_->put(len); | |
86 | os_->write(key, len); | |
87 | size += len; | |
88 | } else if (key_id <= FbsonKeyValue::sMaxKeyId) { | |
89 | FbsonKeyValue::keyid_type idx = key_id; | |
90 | os_->put(0); | |
91 | os_->write((char*)&idx, sizeof(FbsonKeyValue::keyid_type)); | |
92 | size += sizeof(FbsonKeyValue::keyid_type); | |
93 | } else { // key id overflow | |
94 | assert(0); | |
95 | return 0; | |
96 | } | |
97 | ||
98 | kvState_ = WS_Key; | |
99 | return size; | |
100 | } | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
105 | // write a key id | |
106 | uint32_t writeKey(FbsonKeyValue::keyid_type idx) { | |
107 | if (!stack_.empty() && verifyKeyState()) { | |
108 | os_->put(0); | |
109 | os_->write((char*)&idx, sizeof(FbsonKeyValue::keyid_type)); | |
110 | kvState_ = WS_Key; | |
111 | return sizeof(uint8_t) + sizeof(FbsonKeyValue::keyid_type); | |
112 | } | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
117 | uint32_t writeNull() { | |
118 | if (!stack_.empty() && verifyValueState()) { | |
119 | os_->put((FbsonTypeUnder)FbsonType::T_Null); | |
120 | kvState_ = WS_Value; | |
121 | return sizeof(FbsonValue); | |
122 | } | |
123 | ||
124 | return 0; | |
125 | } | |
126 | ||
127 | uint32_t writeBool(bool b) { | |
128 | if (!stack_.empty() && verifyValueState()) { | |
129 | if (b) { | |
130 | os_->put((FbsonTypeUnder)FbsonType::T_True); | |
131 | } else { | |
132 | os_->put((FbsonTypeUnder)FbsonType::T_False); | |
133 | } | |
134 | ||
135 | kvState_ = WS_Value; | |
136 | return sizeof(FbsonValue); | |
137 | } | |
138 | ||
139 | return 0; | |
140 | } | |
141 | ||
142 | uint32_t writeInt8(int8_t v) { | |
143 | if (!stack_.empty() && verifyValueState()) { | |
144 | os_->put((FbsonTypeUnder)FbsonType::T_Int8); | |
145 | os_->put(v); | |
146 | kvState_ = WS_Value; | |
147 | return sizeof(Int8Val); | |
148 | } | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
153 | uint32_t writeInt16(int16_t v) { | |
154 | if (!stack_.empty() && verifyValueState()) { | |
155 | os_->put((FbsonTypeUnder)FbsonType::T_Int16); | |
156 | os_->write((char*)&v, sizeof(int16_t)); | |
157 | kvState_ = WS_Value; | |
158 | return sizeof(Int16Val); | |
159 | } | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
164 | uint32_t writeInt32(int32_t v) { | |
165 | if (!stack_.empty() && verifyValueState()) { | |
166 | os_->put((FbsonTypeUnder)FbsonType::T_Int32); | |
167 | os_->write((char*)&v, sizeof(int32_t)); | |
168 | kvState_ = WS_Value; | |
169 | return sizeof(Int32Val); | |
170 | } | |
171 | ||
172 | return 0; | |
173 | } | |
174 | ||
175 | uint32_t writeInt64(int64_t v) { | |
176 | if (!stack_.empty() && verifyValueState()) { | |
177 | os_->put((FbsonTypeUnder)FbsonType::T_Int64); | |
178 | os_->write((char*)&v, sizeof(int64_t)); | |
179 | kvState_ = WS_Value; | |
180 | return sizeof(Int64Val); | |
181 | } | |
182 | ||
183 | return 0; | |
184 | } | |
185 | ||
186 | uint32_t writeDouble(double v) { | |
187 | if (!stack_.empty() && verifyValueState()) { | |
188 | os_->put((FbsonTypeUnder)FbsonType::T_Double); | |
189 | os_->write((char*)&v, sizeof(double)); | |
190 | kvState_ = WS_Value; | |
191 | return sizeof(DoubleVal); | |
192 | } | |
193 | ||
194 | return 0; | |
195 | } | |
196 | ||
197 | // must call writeStartString before writing a string val | |
198 | bool writeStartString() { | |
199 | if (!stack_.empty() && verifyValueState()) { | |
200 | os_->put((FbsonTypeUnder)FbsonType::T_String); | |
201 | str_pos_ = os_->tellp(); | |
202 | ||
203 | // fill the size bytes with 0 for now | |
204 | uint32_t size = 0; | |
205 | os_->write((char*)&size, sizeof(uint32_t)); | |
206 | ||
207 | kvState_ = WS_String; | |
208 | return true; | |
209 | } | |
210 | ||
211 | return false; | |
212 | } | |
213 | ||
214 | // finish writing a string val | |
215 | bool writeEndString() { | |
216 | if (kvState_ == WS_String) { | |
217 | std::streampos cur_pos = os_->tellp(); | |
218 | int32_t size = (int32_t)(cur_pos - str_pos_ - sizeof(uint32_t)); | |
219 | assert(size >= 0); | |
220 | ||
221 | os_->seekp(str_pos_); | |
222 | os_->write((char*)&size, sizeof(uint32_t)); | |
223 | os_->seekp(cur_pos); | |
224 | ||
225 | kvState_ = WS_Value; | |
226 | return true; | |
227 | } | |
228 | ||
229 | return false; | |
230 | } | |
231 | ||
232 | uint32_t writeString(const char* str, uint32_t len) { | |
233 | if (kvState_ == WS_String) { | |
234 | os_->write(str, len); | |
235 | return len; | |
236 | } | |
237 | ||
238 | return 0; | |
239 | } | |
240 | ||
241 | uint32_t writeString(char ch) { | |
242 | if (kvState_ == WS_String) { | |
243 | os_->put(ch); | |
244 | return 1; | |
245 | } | |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
250 | // must call writeStartBinary before writing a binary val | |
251 | bool writeStartBinary() { | |
252 | if (!stack_.empty() && verifyValueState()) { | |
253 | os_->put((FbsonTypeUnder)FbsonType::T_Binary); | |
254 | str_pos_ = os_->tellp(); | |
255 | ||
256 | // fill the size bytes with 0 for now | |
257 | uint32_t size = 0; | |
258 | os_->write((char*)&size, sizeof(uint32_t)); | |
259 | ||
260 | kvState_ = WS_Binary; | |
261 | return true; | |
262 | } | |
263 | ||
264 | return false; | |
265 | } | |
266 | ||
267 | // finish writing a binary val | |
268 | bool writeEndBinary() { | |
269 | if (kvState_ == WS_Binary) { | |
270 | std::streampos cur_pos = os_->tellp(); | |
271 | int32_t size = (int32_t)(cur_pos - str_pos_ - sizeof(uint32_t)); | |
272 | assert(size >= 0); | |
273 | ||
274 | os_->seekp(str_pos_); | |
275 | os_->write((char*)&size, sizeof(uint32_t)); | |
276 | os_->seekp(cur_pos); | |
277 | ||
278 | kvState_ = WS_Value; | |
279 | return true; | |
280 | } | |
281 | ||
282 | return false; | |
283 | } | |
284 | ||
285 | uint32_t writeBinary(const char* bin, uint32_t len) { | |
286 | if (kvState_ == WS_Binary) { | |
287 | os_->write(bin, len); | |
288 | return len; | |
289 | } | |
290 | ||
291 | return 0; | |
292 | } | |
293 | ||
294 | // must call writeStartObject before writing an object val | |
295 | bool writeStartObject() { | |
296 | if (stack_.empty() || verifyValueState()) { | |
297 | if (stack_.empty()) { | |
298 | // if this is a new FBSON, write the header | |
299 | if (!hasHdr_) { | |
300 | writeHeader(); | |
301 | } else | |
302 | return false; | |
303 | } | |
304 | ||
305 | os_->put((FbsonTypeUnder)FbsonType::T_Object); | |
306 | // save the size position | |
307 | stack_.push(WriteInfo({WS_Object, os_->tellp()})); | |
308 | ||
309 | // fill the size bytes with 0 for now | |
310 | uint32_t size = 0; | |
311 | os_->write((char*)&size, sizeof(uint32_t)); | |
312 | ||
313 | kvState_ = WS_Value; | |
314 | return true; | |
315 | } | |
316 | ||
317 | return false; | |
318 | } | |
319 | ||
320 | // finish writing an object val | |
321 | bool writeEndObject() { | |
322 | if (!stack_.empty() && stack_.top().state == WS_Object && | |
323 | kvState_ == WS_Value) { | |
324 | WriteInfo& ci = stack_.top(); | |
325 | std::streampos cur_pos = os_->tellp(); | |
326 | int32_t size = (int32_t)(cur_pos - ci.sz_pos - sizeof(uint32_t)); | |
327 | assert(size >= 0); | |
328 | ||
329 | os_->seekp(ci.sz_pos); | |
330 | os_->write((char*)&size, sizeof(uint32_t)); | |
331 | os_->seekp(cur_pos); | |
332 | stack_.pop(); | |
333 | ||
334 | return true; | |
335 | } | |
336 | ||
337 | return false; | |
338 | } | |
339 | ||
340 | // must call writeStartArray before writing an array val | |
341 | bool writeStartArray() { | |
342 | if (stack_.empty() || verifyValueState()) { | |
343 | if (stack_.empty()) { | |
344 | // if this is a new FBSON, write the header | |
345 | if (!hasHdr_) { | |
346 | writeHeader(); | |
347 | } else | |
348 | return false; | |
349 | } | |
350 | ||
351 | os_->put((FbsonTypeUnder)FbsonType::T_Array); | |
352 | // save the size position | |
353 | stack_.push(WriteInfo({WS_Array, os_->tellp()})); | |
354 | ||
355 | // fill the size bytes with 0 for now | |
356 | uint32_t size = 0; | |
357 | os_->write((char*)&size, sizeof(uint32_t)); | |
358 | ||
359 | kvState_ = WS_Value; | |
360 | return true; | |
361 | } | |
362 | ||
363 | return false; | |
364 | } | |
365 | ||
366 | // finish writing an array val | |
367 | bool writeEndArray() { | |
368 | if (!stack_.empty() && stack_.top().state == WS_Array && | |
369 | kvState_ == WS_Value) { | |
370 | WriteInfo& ci = stack_.top(); | |
371 | std::streampos cur_pos = os_->tellp(); | |
372 | int32_t size = (int32_t)(cur_pos - ci.sz_pos - sizeof(uint32_t)); | |
373 | assert(size >= 0); | |
374 | ||
375 | os_->seekp(ci.sz_pos); | |
376 | os_->write((char*)&size, sizeof(uint32_t)); | |
377 | os_->seekp(cur_pos); | |
378 | stack_.pop(); | |
379 | ||
380 | return true; | |
381 | } | |
382 | ||
383 | return false; | |
384 | } | |
385 | ||
386 | OS_TYPE* getOutput() { return os_; } | |
387 | ||
388 | private: | |
389 | // verify we are in the right state before writing a value | |
390 | bool verifyValueState() { | |
391 | assert(!stack_.empty()); | |
392 | return (stack_.top().state == WS_Object && kvState_ == WS_Key) || | |
393 | (stack_.top().state == WS_Array && kvState_ == WS_Value); | |
394 | } | |
395 | ||
396 | // verify we are in the right state before writing a key | |
397 | bool verifyKeyState() { | |
398 | assert(!stack_.empty()); | |
399 | return stack_.top().state == WS_Object && kvState_ == WS_Value; | |
400 | } | |
401 | ||
402 | void writeHeader() { | |
403 | os_->put(FBSON_VER); | |
404 | hasHdr_ = true; | |
405 | } | |
406 | ||
407 | private: | |
408 | enum WriteState { | |
409 | WS_NONE, | |
410 | WS_Array, | |
411 | WS_Object, | |
412 | WS_Key, | |
413 | WS_Value, | |
414 | WS_String, | |
415 | WS_Binary, | |
416 | }; | |
417 | ||
418 | struct WriteInfo { | |
419 | WriteState state; | |
420 | std::streampos sz_pos; | |
421 | }; | |
422 | ||
423 | private: | |
424 | OS_TYPE* os_; | |
425 | bool alloc_; | |
426 | bool hasHdr_; | |
427 | WriteState kvState_; // key or value state | |
428 | std::streampos str_pos_; | |
429 | std::stack<WriteInfo> stack_; | |
430 | }; | |
431 | ||
432 | typedef FbsonWriterT<FbsonOutStream> FbsonWriter; | |
433 | ||
434 | } // namespace fbson |