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