| // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
| // This source code is licensed under both the GPLv2 (found in the |
| // COPYING file in the root directory) and Apache 2.0 License |
| // (found in the LICENSE.Apache file in the root directory). |
| |
| /* |
| * This file defines FbsonWriterT (template) and FbsonWriter. |
| * |
| * FbsonWriterT is a template class which implements an FBSON serializer. |
| * Users call various write functions of FbsonWriterT object to write values |
| * directly to FBSON packed bytes. All write functions of value or key return |
| * the number of bytes written to FBSON, or 0 if there is an error. To write an |
| * object, an array, or a string, you must call writeStart[..] before writing |
| * values or key, and call writeEnd[..] after finishing at the end. |
| * |
| * By default, an FbsonWriterT object creates an output stream buffer. |
| * Alternatively, you can also pass any output stream object to a writer, as |
| * long as the stream object implements some basic functions of std::ostream |
| * (such as FbsonOutStream, see FbsonStream.h). |
| * |
| * FbsonWriter specializes FbsonWriterT with FbsonOutStream type (see |
| * FbsonStream.h). So unless you want to provide own a different output stream |
| * type, use FbsonParser object. |
| * |
| * @author Tian Xia <tianx@fb.com> |
| */ |
| |
| #ifndef FBSON_FBSONWRITER_H |
| #define FBSON_FBSONWRITER_H |
| |
| #include <stack> |
| #include "FbsonDocument.h" |
| #include "FbsonStream.h" |
| |
| namespace fbson { |
| |
| template <class OS_TYPE> |
| class FbsonWriterT { |
| public: |
| FbsonWriterT() |
| : alloc_(true), hasHdr_(false), kvState_(WS_Value), str_pos_(0) { |
| os_ = new OS_TYPE(); |
| } |
| |
| explicit FbsonWriterT(OS_TYPE& os) |
| : os_(&os), |
| alloc_(false), |
| hasHdr_(false), |
| kvState_(WS_Value), |
| str_pos_(0) {} |
| |
| ~FbsonWriterT() { |
| if (alloc_) { |
| delete os_; |
| } |
| } |
| |
| void reset() { |
| os_->clear(); |
| os_->seekp(0); |
| hasHdr_ = false; |
| kvState_ = WS_Value; |
| for (; !stack_.empty(); stack_.pop()) |
| ; |
| } |
| |
| // write a key string (or key id if an external dict is provided) |
| uint32_t writeKey(const char* key, |
| uint8_t len, |
| hDictInsert handler = nullptr) { |
| if (len && !stack_.empty() && verifyKeyState()) { |
| int key_id = -1; |
| if (handler) { |
| key_id = handler(key, len); |
| } |
| |
| uint32_t size = sizeof(uint8_t); |
| if (key_id < 0) { |
| os_->put(len); |
| os_->write(key, len); |
| size += len; |
| } else if (key_id <= FbsonKeyValue::sMaxKeyId) { |
| FbsonKeyValue::keyid_type idx = key_id; |
| os_->put(0); |
| os_->write((char*)&idx, sizeof(FbsonKeyValue::keyid_type)); |
| size += sizeof(FbsonKeyValue::keyid_type); |
| } else { // key id overflow |
| assert(0); |
| return 0; |
| } |
| |
| kvState_ = WS_Key; |
| return size; |
| } |
| |
| return 0; |
| } |
| |
| // write a key id |
| uint32_t writeKey(FbsonKeyValue::keyid_type idx) { |
| if (!stack_.empty() && verifyKeyState()) { |
| os_->put(0); |
| os_->write((char*)&idx, sizeof(FbsonKeyValue::keyid_type)); |
| kvState_ = WS_Key; |
| return sizeof(uint8_t) + sizeof(FbsonKeyValue::keyid_type); |
| } |
| |
| return 0; |
| } |
| |
| uint32_t writeNull() { |
| if (!stack_.empty() && verifyValueState()) { |
| os_->put((FbsonTypeUnder)FbsonType::T_Null); |
| kvState_ = WS_Value; |
| return sizeof(FbsonValue); |
| } |
| |
| return 0; |
| } |
| |
| uint32_t writeBool(bool b) { |
| if (!stack_.empty() && verifyValueState()) { |
| if (b) { |
| os_->put((FbsonTypeUnder)FbsonType::T_True); |
| } else { |
| os_->put((FbsonTypeUnder)FbsonType::T_False); |
| } |
| |
| kvState_ = WS_Value; |
| return sizeof(FbsonValue); |
| } |
| |
| return 0; |
| } |
| |
| uint32_t writeInt8(int8_t v) { |
| if (!stack_.empty() && verifyValueState()) { |
| os_->put((FbsonTypeUnder)FbsonType::T_Int8); |
| os_->put(v); |
| kvState_ = WS_Value; |
| return sizeof(Int8Val); |
| } |
| |
| return 0; |
| } |
| |
| uint32_t writeInt16(int16_t v) { |
| if (!stack_.empty() && verifyValueState()) { |
| os_->put((FbsonTypeUnder)FbsonType::T_Int16); |
| os_->write((char*)&v, sizeof(int16_t)); |
| kvState_ = WS_Value; |
| return sizeof(Int16Val); |
| } |
| |
| return 0; |
| } |
| |
| uint32_t writeInt32(int32_t v) { |
| if (!stack_.empty() && verifyValueState()) { |
| os_->put((FbsonTypeUnder)FbsonType::T_Int32); |
| os_->write((char*)&v, sizeof(int32_t)); |
| kvState_ = WS_Value; |
| return sizeof(Int32Val); |
| } |
| |
| return 0; |
| } |
| |
| uint32_t writeInt64(int64_t v) { |
| if (!stack_.empty() && verifyValueState()) { |
| os_->put((FbsonTypeUnder)FbsonType::T_Int64); |
| os_->write((char*)&v, sizeof(int64_t)); |
| kvState_ = WS_Value; |
| return sizeof(Int64Val); |
| } |
| |
| return 0; |
| } |
| |
| uint32_t writeDouble(double v) { |
| if (!stack_.empty() && verifyValueState()) { |
| os_->put((FbsonTypeUnder)FbsonType::T_Double); |
| os_->write((char*)&v, sizeof(double)); |
| kvState_ = WS_Value; |
| return sizeof(DoubleVal); |
| } |
| |
| return 0; |
| } |
| |
| // must call writeStartString before writing a string val |
| bool writeStartString() { |
| if (!stack_.empty() && verifyValueState()) { |
| os_->put((FbsonTypeUnder)FbsonType::T_String); |
| str_pos_ = os_->tellp(); |
| |
| // fill the size bytes with 0 for now |
| uint32_t size = 0; |
| os_->write((char*)&size, sizeof(uint32_t)); |
| |
| kvState_ = WS_String; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // finish writing a string val |
| bool writeEndString() { |
| if (kvState_ == WS_String) { |
| std::streampos cur_pos = os_->tellp(); |
| int32_t size = (int32_t)(cur_pos - str_pos_ - sizeof(uint32_t)); |
| assert(size >= 0); |
| |
| os_->seekp(str_pos_); |
| os_->write((char*)&size, sizeof(uint32_t)); |
| os_->seekp(cur_pos); |
| |
| kvState_ = WS_Value; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| uint32_t writeString(const char* str, uint32_t len) { |
| if (kvState_ == WS_String) { |
| os_->write(str, len); |
| return len; |
| } |
| |
| return 0; |
| } |
| |
| uint32_t writeString(char ch) { |
| if (kvState_ == WS_String) { |
| os_->put(ch); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| // must call writeStartBinary before writing a binary val |
| bool writeStartBinary() { |
| if (!stack_.empty() && verifyValueState()) { |
| os_->put((FbsonTypeUnder)FbsonType::T_Binary); |
| str_pos_ = os_->tellp(); |
| |
| // fill the size bytes with 0 for now |
| uint32_t size = 0; |
| os_->write((char*)&size, sizeof(uint32_t)); |
| |
| kvState_ = WS_Binary; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // finish writing a binary val |
| bool writeEndBinary() { |
| if (kvState_ == WS_Binary) { |
| std::streampos cur_pos = os_->tellp(); |
| int32_t size = (int32_t)(cur_pos - str_pos_ - sizeof(uint32_t)); |
| assert(size >= 0); |
| |
| os_->seekp(str_pos_); |
| os_->write((char*)&size, sizeof(uint32_t)); |
| os_->seekp(cur_pos); |
| |
| kvState_ = WS_Value; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| uint32_t writeBinary(const char* bin, uint32_t len) { |
| if (kvState_ == WS_Binary) { |
| os_->write(bin, len); |
| return len; |
| } |
| |
| return 0; |
| } |
| |
| // must call writeStartObject before writing an object val |
| bool writeStartObject() { |
| if (stack_.empty() || verifyValueState()) { |
| if (stack_.empty()) { |
| // if this is a new FBSON, write the header |
| if (!hasHdr_) { |
| writeHeader(); |
| } else |
| return false; |
| } |
| |
| os_->put((FbsonTypeUnder)FbsonType::T_Object); |
| // save the size position |
| stack_.push(WriteInfo({WS_Object, os_->tellp()})); |
| |
| // fill the size bytes with 0 for now |
| uint32_t size = 0; |
| os_->write((char*)&size, sizeof(uint32_t)); |
| |
| kvState_ = WS_Value; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // finish writing an object val |
| bool writeEndObject() { |
| if (!stack_.empty() && stack_.top().state == WS_Object && |
| kvState_ == WS_Value) { |
| WriteInfo& ci = stack_.top(); |
| std::streampos cur_pos = os_->tellp(); |
| int32_t size = (int32_t)(cur_pos - ci.sz_pos - sizeof(uint32_t)); |
| assert(size >= 0); |
| |
| os_->seekp(ci.sz_pos); |
| os_->write((char*)&size, sizeof(uint32_t)); |
| os_->seekp(cur_pos); |
| stack_.pop(); |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // must call writeStartArray before writing an array val |
| bool writeStartArray() { |
| if (stack_.empty() || verifyValueState()) { |
| if (stack_.empty()) { |
| // if this is a new FBSON, write the header |
| if (!hasHdr_) { |
| writeHeader(); |
| } else |
| return false; |
| } |
| |
| os_->put((FbsonTypeUnder)FbsonType::T_Array); |
| // save the size position |
| stack_.push(WriteInfo({WS_Array, os_->tellp()})); |
| |
| // fill the size bytes with 0 for now |
| uint32_t size = 0; |
| os_->write((char*)&size, sizeof(uint32_t)); |
| |
| kvState_ = WS_Value; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // finish writing an array val |
| bool writeEndArray() { |
| if (!stack_.empty() && stack_.top().state == WS_Array && |
| kvState_ == WS_Value) { |
| WriteInfo& ci = stack_.top(); |
| std::streampos cur_pos = os_->tellp(); |
| int32_t size = (int32_t)(cur_pos - ci.sz_pos - sizeof(uint32_t)); |
| assert(size >= 0); |
| |
| os_->seekp(ci.sz_pos); |
| os_->write((char*)&size, sizeof(uint32_t)); |
| os_->seekp(cur_pos); |
| stack_.pop(); |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| OS_TYPE* getOutput() { return os_; } |
| |
| private: |
| // verify we are in the right state before writing a value |
| bool verifyValueState() { |
| assert(!stack_.empty()); |
| return (stack_.top().state == WS_Object && kvState_ == WS_Key) || |
| (stack_.top().state == WS_Array && kvState_ == WS_Value); |
| } |
| |
| // verify we are in the right state before writing a key |
| bool verifyKeyState() { |
| assert(!stack_.empty()); |
| return stack_.top().state == WS_Object && kvState_ == WS_Value; |
| } |
| |
| void writeHeader() { |
| os_->put(FBSON_VER); |
| hasHdr_ = true; |
| } |
| |
| private: |
| enum WriteState { |
| WS_NONE, |
| WS_Array, |
| WS_Object, |
| WS_Key, |
| WS_Value, |
| WS_String, |
| WS_Binary, |
| }; |
| |
| struct WriteInfo { |
| WriteState state; |
| std::streampos sz_pos; |
| }; |
| |
| private: |
| OS_TYPE* os_; |
| bool alloc_; |
| bool hasHdr_; |
| WriteState kvState_; // key or value state |
| std::streampos str_pos_; |
| std::stack<WriteInfo> stack_; |
| }; |
| |
| typedef FbsonWriterT<FbsonOutStream> FbsonWriter; |
| |
| } // namespace fbson |
| |
| #endif // FBSON_FBSONWRITER_H |