{# This template is generated by gen_cbor_templates.py. #} // Generated by lib/CBOR_cpp.template. // Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include {% for namespace in config.protocol.namespace %} namespace {{namespace}} { {% endfor %} // ===== encoding/cbor.cc ===== using namespace cbor; namespace { // See RFC 7049 Section 2.3, Table 2. static constexpr uint8_t kEncodedTrue = EncodeInitialByte(MajorType::SIMPLE_VALUE, 21); static constexpr uint8_t kEncodedFalse = EncodeInitialByte(MajorType::SIMPLE_VALUE, 20); static constexpr uint8_t kEncodedNull = EncodeInitialByte(MajorType::SIMPLE_VALUE, 22); static constexpr uint8_t kInitialByteForDouble = EncodeInitialByte(MajorType::SIMPLE_VALUE, 27); } // namespace uint8_t EncodeTrue() { return kEncodedTrue; } uint8_t EncodeFalse() { return kEncodedFalse; } uint8_t EncodeNull() { return kEncodedNull; } uint8_t EncodeIndefiniteLengthArrayStart() { return kInitialByteIndefiniteLengthArray; } uint8_t EncodeIndefiniteLengthMapStart() { return kInitialByteIndefiniteLengthMap; } uint8_t EncodeStop() { return kStopByte; } namespace { // See RFC 7049 Table 3 and Section 2.4.4.2. This is used as a prefix for // arbitrary binary data encoded as BYTE_STRING. static constexpr uint8_t kExpectedConversionToBase64Tag = EncodeInitialByte(MajorType::TAG, 22); // When parsing CBOR, we limit recursion depth for objects and arrays // to this constant. static constexpr int kStackLimit = 1000; // Writes the bytes for |v| to |out|, starting with the most significant byte. // See also: https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html template void WriteBytesMostSignificantByteFirst(T v, std::vector* out) { for (int shift_bytes = sizeof(T) - 1; shift_bytes >= 0; --shift_bytes) out->push_back(0xff & (v >> (shift_bytes * 8))); } } // namespace namespace cbor_internals { // Writes the start of a token with |type|. The |value| may indicate the size, // or it may be the payload if the value is an unsigned integer. void WriteTokenStart(MajorType type, uint64_t value, std::vector* encoded) { if (value < 24) { // Values 0-23 are encoded directly into the additional info of the // initial byte. encoded->push_back(EncodeInitialByte(type, /*additional_info=*/value)); return; } if (value <= std::numeric_limits::max()) { // Values 24-255 are encoded with one initial byte, followed by the value. encoded->push_back(EncodeInitialByte(type, kAdditionalInformation1Byte)); encoded->push_back(value); return; } if (value <= std::numeric_limits::max()) { // Values 256-65535: 1 initial byte + 2 bytes payload. encoded->push_back(EncodeInitialByte(type, kAdditionalInformation2Bytes)); WriteBytesMostSignificantByteFirst(value, encoded); return; } if (value <= std::numeric_limits::max()) { // 32 bit uint: 1 initial byte + 4 bytes payload. encoded->push_back(EncodeInitialByte(type, kAdditionalInformation4Bytes)); WriteBytesMostSignificantByteFirst(static_cast(value), encoded); return; } // 64 bit uint: 1 initial byte + 8 bytes payload. encoded->push_back(EncodeInitialByte(type, kAdditionalInformation8Bytes)); WriteBytesMostSignificantByteFirst(value, encoded); } } // namespace cbor_internals namespace { // Extracts sizeof(T) bytes from |in| to extract a value of type T // (e.g. uint64_t, uint32_t, ...), most significant byte first. // See also: https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html template T ReadBytesMostSignificantByteFirst(span in) { assert(static_cast(in.size()) >= sizeof(T)); T result = 0; for (std::size_t shift_bytes = 0; shift_bytes < sizeof(T); ++shift_bytes) result |= T(in[sizeof(T) - 1 - shift_bytes]) << (shift_bytes * 8); return result; } } // namespace namespace cbor_internals { int8_t ReadTokenStart(span bytes, MajorType* type, uint64_t* value) { if (bytes.empty()) return -1; uint8_t initial_byte = bytes[0]; *type = MajorType((initial_byte & kMajorTypeMask) >> kMajorTypeBitShift); uint8_t additional_information = initial_byte & kAdditionalInformationMask; if (additional_information < 24) { // Values 0-23 are encoded directly into the additional info of the // initial byte. *value = additional_information; return 1; } if (additional_information == kAdditionalInformation1Byte) { // Values 24-255 are encoded with one initial byte, followed by the value. if (bytes.size() < 2) return -1; *value = ReadBytesMostSignificantByteFirst(bytes.subspan(1)); return 2; } if (additional_information == kAdditionalInformation2Bytes) { // Values 256-65535: 1 initial byte + 2 bytes payload. if (static_cast(bytes.size()) < 1 + sizeof(uint16_t)) return -1; *value = ReadBytesMostSignificantByteFirst(bytes.subspan(1)); return 3; } if (additional_information == kAdditionalInformation4Bytes) { // 32 bit uint: 1 initial byte + 4 bytes payload. if (static_cast(bytes.size()) < 1 + sizeof(uint32_t)) return -1; *value = ReadBytesMostSignificantByteFirst(bytes.subspan(1)); return 5; } if (additional_information == kAdditionalInformation8Bytes) { // 64 bit uint: 1 initial byte + 8 bytes payload. if (static_cast(bytes.size()) < 1 + sizeof(uint64_t)) return -1; *value = ReadBytesMostSignificantByteFirst(bytes.subspan(1)); return 9; } return -1; } } // namespace cbor_internals using cbor_internals::WriteTokenStart; using cbor_internals::ReadTokenStart; void EncodeInt32(int32_t value, std::vector* out) { if (value >= 0) { WriteTokenStart(MajorType::UNSIGNED, value, out); } else { uint64_t representation = static_cast(-(value + 1)); WriteTokenStart(MajorType::NEGATIVE, representation, out); } } void EncodeString16(span in, std::vector* out) { uint64_t byte_length = static_cast(in.size_bytes()); WriteTokenStart(MajorType::BYTE_STRING, byte_length, out); // When emitting UTF16 characters, we always write the least significant byte // first; this is because it's the native representation for X86. // TODO(johannes): Implement a more efficient thing here later, e.g. // casting *iff* the machine has this byte order. // The wire format for UTF16 chars will probably remain the same // (least significant byte first) since this way we can have // golden files, unittests, etc. that port easily and universally. // See also: // https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html for (const uint16_t two_bytes : in) { out->push_back(two_bytes); out->push_back(two_bytes >> 8); } } void EncodeString8(span in, std::vector* out) { WriteTokenStart(MajorType::STRING, static_cast(in.size_bytes()), out); out->insert(out->end(), in.begin(), in.end()); } void EncodeBinary(span in, std::vector* out) { out->push_back(kExpectedConversionToBase64Tag); uint64_t byte_length = static_cast(in.size_bytes()); WriteTokenStart(MajorType::BYTE_STRING, byte_length, out); out->insert(out->end(), in.begin(), in.end()); } // A double is encoded with a specific initial byte // (kInitialByteForDouble) plus the 64 bits of payload for its value. constexpr std::ptrdiff_t kEncodedDoubleSize = 1 + sizeof(uint64_t); // An envelope is encoded with a specific initial byte // (kInitialByteForEnvelope), plus the start byte for a BYTE_STRING with a 32 // bit wide length, plus a 32 bit length for that string. constexpr std::ptrdiff_t kEncodedEnvelopeHeaderSize = 1 + 1 + sizeof(uint32_t); void EncodeDouble(double value, std::vector* out) { // The additional_info=27 indicates 64 bits for the double follow. // See RFC 7049 Section 2.3, Table 1. out->push_back(kInitialByteForDouble); union { double from_double; uint64_t to_uint64; } reinterpret; reinterpret.from_double = value; WriteBytesMostSignificantByteFirst(reinterpret.to_uint64, out); } void EnvelopeEncoder::EncodeStart(std::vector* out) { assert(byte_size_pos_ == 0); out->push_back(kInitialByteForEnvelope); out->push_back(kInitialByteFor32BitLengthByteString); byte_size_pos_ = out->size(); out->resize(out->size() + sizeof(uint32_t)); } bool EnvelopeEncoder::EncodeStop(std::vector* out) { assert(byte_size_pos_ != 0); // The byte size is the size of the payload, that is, all the // bytes that were written past the byte size position itself. uint64_t byte_size = out->size() - (byte_size_pos_ + sizeof(uint32_t)); // We store exactly 4 bytes, so at most INT32MAX, with most significant // byte first. if (byte_size > std::numeric_limits::max()) return false; for (int shift_bytes = sizeof(uint32_t) - 1; shift_bytes >= 0; --shift_bytes) { (*out)[byte_size_pos_++] = 0xff & (byte_size >> (shift_bytes * 8)); } return true; } namespace { class JSONToCBOREncoder : public JSONParserHandler { public: JSONToCBOREncoder(std::vector* out, Status* status) : out_(out), status_(status) { *status_ = Status(); } void HandleObjectBegin() override { envelopes_.emplace_back(); envelopes_.back().EncodeStart(out_); out_->push_back(kInitialByteIndefiniteLengthMap); } void HandleObjectEnd() override { out_->push_back(kStopByte); assert(!envelopes_.empty()); envelopes_.back().EncodeStop(out_); envelopes_.pop_back(); } void HandleArrayBegin() override { envelopes_.emplace_back(); envelopes_.back().EncodeStart(out_); out_->push_back(kInitialByteIndefiniteLengthArray); } void HandleArrayEnd() override { out_->push_back(kStopByte); assert(!envelopes_.empty()); envelopes_.back().EncodeStop(out_); envelopes_.pop_back(); } void HandleString16(std::vector chars) override { for (uint16_t ch : chars) { if (ch >= 0x7f) { // If there's at least one non-7bit character, we encode as UTF16. EncodeString16(span(chars.data(), chars.size()), out_); return; } } std::vector sevenbit_chars(chars.begin(), chars.end()); EncodeString8(span(sevenbit_chars.data(), sevenbit_chars.size()), out_); } void HandleBinary(std::vector bytes) override { EncodeBinary(span(bytes.data(), bytes.size()), out_); } void HandleDouble(double value) override { EncodeDouble(value, out_); } void HandleInt32(int32_t value) override { EncodeInt32(value, out_); } void HandleBool(bool value) override { // See RFC 7049 Section 2.3, Table 2. out_->push_back(value ? kEncodedTrue : kEncodedFalse); } void HandleNull() override { // See RFC 7049 Section 2.3, Table 2. out_->push_back(kEncodedNull); } void HandleError(Status error) override { assert(!error.ok()); *status_ = error; out_->clear(); } private: std::vector* out_; std::vector envelopes_; Status* status_; }; } // namespace std::unique_ptr NewJSONToCBOREncoder( std::vector* out, Status* status) { return std::unique_ptr(new JSONToCBOREncoder(out, status)); } namespace { // Below are three parsing routines for CBOR, which cover enough // to roundtrip JSON messages. bool ParseMap(int32_t stack_depth, CBORTokenizer* tokenizer, JSONParserHandler* out); bool ParseArray(int32_t stack_depth, CBORTokenizer* tokenizer, JSONParserHandler* out); bool ParseValue(int32_t stack_depth, CBORTokenizer* tokenizer, JSONParserHandler* out); void ParseUTF16String(CBORTokenizer* tokenizer, JSONParserHandler* out) { std::vector value; span rep = tokenizer->GetString16WireRep(); for (std::ptrdiff_t ii = 0; ii < rep.size(); ii += 2) value.push_back((rep[ii + 1] << 8) | rep[ii]); out->HandleString16(std::move(value)); tokenizer->Next(); } // For now this method only covers US-ASCII. Later, we may allow UTF8. bool ParseASCIIString(CBORTokenizer* tokenizer, JSONParserHandler* out) { assert(tokenizer->TokenTag() == CBORTokenTag::STRING8); std::vector value16; for (uint8_t ch : tokenizer->GetString8()) { // We only accept us-ascii (7 bit) strings here. Other strings must // be encoded with 16 bit (the BYTE_STRING case). if (ch >= 0x7f) { out->HandleError( Status{Error::CBOR_STRING8_MUST_BE_7BIT, tokenizer->Status().pos}); return false; } value16.push_back(ch); } out->HandleString16(std::move(value16)); tokenizer->Next(); return true; } bool ParseValue(int32_t stack_depth, CBORTokenizer* tokenizer, JSONParserHandler* out) { if (stack_depth > kStackLimit) { out->HandleError( Status{Error::CBOR_STACK_LIMIT_EXCEEDED, tokenizer->Status().pos}); return false; } // Skip past the envelope to get to what's inside. if (tokenizer->TokenTag() == CBORTokenTag::ENVELOPE) tokenizer->EnterEnvelope(); switch (tokenizer->TokenTag()) { case CBORTokenTag::ERROR_VALUE: out->HandleError(tokenizer->Status()); return false; case CBORTokenTag::DONE: out->HandleError(Status{Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE, tokenizer->Status().pos}); return false; case CBORTokenTag::TRUE_VALUE: out->HandleBool(true); tokenizer->Next(); return true; case CBORTokenTag::FALSE_VALUE: out->HandleBool(false); tokenizer->Next(); return true; case CBORTokenTag::NULL_VALUE: out->HandleNull(); tokenizer->Next(); return true; case CBORTokenTag::INT32: out->HandleInt32(tokenizer->GetInt32()); tokenizer->Next(); return true; case CBORTokenTag::DOUBLE: out->HandleDouble(tokenizer->GetDouble()); tokenizer->Next(); return true; case CBORTokenTag::STRING8: return ParseASCIIString(tokenizer, out); case CBORTokenTag::STRING16: ParseUTF16String(tokenizer, out); return true; case CBORTokenTag::BINARY: { span binary = tokenizer->GetBinary(); out->HandleBinary(std::vector(binary.begin(), binary.end())); tokenizer->Next(); return true; } case CBORTokenTag::MAP_START: return ParseMap(stack_depth + 1, tokenizer, out); case CBORTokenTag::ARRAY_START: return ParseArray(stack_depth + 1, tokenizer, out); default: out->HandleError( Status{Error::CBOR_UNSUPPORTED_VALUE, tokenizer->Status().pos}); return false; } } // |bytes| must start with the indefinite length array byte, so basically, // ParseArray may only be called after an indefinite length array has been // detected. bool ParseArray(int32_t stack_depth, CBORTokenizer* tokenizer, JSONParserHandler* out) { assert(tokenizer->TokenTag() == CBORTokenTag::ARRAY_START); tokenizer->Next(); out->HandleArrayBegin(); while (tokenizer->TokenTag() != CBORTokenTag::STOP) { if (tokenizer->TokenTag() == CBORTokenTag::DONE) { out->HandleError( Status{Error::CBOR_UNEXPECTED_EOF_IN_ARRAY, tokenizer->Status().pos}); return false; } if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) { out->HandleError(tokenizer->Status()); return false; } // Parse value. if (!ParseValue(stack_depth, tokenizer, out)) return false; } out->HandleArrayEnd(); tokenizer->Next(); return true; } // |bytes| must start with the indefinite length array byte, so basically, // ParseArray may only be called after an indefinite length array has been // detected. bool ParseMap(int32_t stack_depth, CBORTokenizer* tokenizer, JSONParserHandler* out) { assert(tokenizer->TokenTag() == CBORTokenTag::MAP_START); out->HandleObjectBegin(); tokenizer->Next(); while (tokenizer->TokenTag() != CBORTokenTag::STOP) { if (tokenizer->TokenTag() == CBORTokenTag::DONE) { out->HandleError( Status{Error::CBOR_UNEXPECTED_EOF_IN_MAP, tokenizer->Status().pos}); return false; } if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) { out->HandleError(tokenizer->Status()); return false; } // Parse key. if (tokenizer->TokenTag() == CBORTokenTag::STRING8) { if (!ParseASCIIString(tokenizer, out)) return false; } else if (tokenizer->TokenTag() == CBORTokenTag::STRING16) { ParseUTF16String(tokenizer, out); } else { out->HandleError( Status{Error::CBOR_INVALID_MAP_KEY, tokenizer->Status().pos}); return false; } // Parse value. if (!ParseValue(stack_depth, tokenizer, out)) return false; } out->HandleObjectEnd(); tokenizer->Next(); return true; } } // namespace void ParseCBOR(span bytes, JSONParserHandler* json_out) { if (bytes.empty()) { json_out->HandleError(Status{Error::CBOR_NO_INPUT, 0}); return; } if (bytes[0] != kInitialByteForEnvelope) { json_out->HandleError(Status{Error::CBOR_INVALID_START_BYTE, 0}); return; } CBORTokenizer tokenizer(bytes); if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) { json_out->HandleError(tokenizer.Status()); return; } // We checked for the envelope start byte above, so the tokenizer // must agree here, since it's not an error. assert(tokenizer.TokenTag() == CBORTokenTag::ENVELOPE); tokenizer.EnterEnvelope(); if (tokenizer.TokenTag() != CBORTokenTag::MAP_START) { json_out->HandleError( Status{Error::CBOR_MAP_START_EXPECTED, tokenizer.Status().pos}); return; } if (!ParseMap(/*stack_depth=*/1, &tokenizer, json_out)) return; if (tokenizer.TokenTag() == CBORTokenTag::DONE) return; if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) { json_out->HandleError(tokenizer.Status()); return; } json_out->HandleError( Status{Error::CBOR_TRAILING_JUNK, tokenizer.Status().pos}); } CBORTokenizer::CBORTokenizer(span bytes) : bytes_(bytes) { ReadNextToken(/*enter_envelope=*/false); } CBORTokenizer::~CBORTokenizer() {} CBORTokenTag CBORTokenizer::TokenTag() const { return token_tag_; } void CBORTokenizer::Next() { if (token_tag_ == CBORTokenTag::ERROR_VALUE || token_tag_ == CBORTokenTag::DONE) return; ReadNextToken(/*enter_envelope=*/false); } void CBORTokenizer::EnterEnvelope() { assert(token_tag_ == CBORTokenTag::ENVELOPE); ReadNextToken(/*enter_envelope=*/true); } Status CBORTokenizer::Status() const { return status_; } int32_t CBORTokenizer::GetInt32() const { assert(token_tag_ == CBORTokenTag::INT32); // The range checks happen in ::ReadNextToken(). return static_cast( token_start_type_ == MajorType::UNSIGNED ? token_start_internal_value_ : -static_cast(token_start_internal_value_) - 1); } double CBORTokenizer::GetDouble() const { assert(token_tag_ == CBORTokenTag::DOUBLE); union { uint64_t from_uint64; double to_double; } reinterpret; reinterpret.from_uint64 = ReadBytesMostSignificantByteFirst( bytes_.subspan(status_.pos + 1)); return reinterpret.to_double; } span CBORTokenizer::GetString8() const { assert(token_tag_ == CBORTokenTag::STRING8); auto length = static_cast(token_start_internal_value_); return bytes_.subspan(status_.pos + (token_byte_length_ - length), length); } span CBORTokenizer::GetString16WireRep() const { assert(token_tag_ == CBORTokenTag::STRING16); auto length = static_cast(token_start_internal_value_); return bytes_.subspan(status_.pos + (token_byte_length_ - length), length); } span CBORTokenizer::GetBinary() const { assert(token_tag_ == CBORTokenTag::BINARY); auto length = static_cast(token_start_internal_value_); return bytes_.subspan(status_.pos + (token_byte_length_ - length), length); } void CBORTokenizer::ReadNextToken(bool enter_envelope) { if (enter_envelope) { status_.pos += kEncodedEnvelopeHeaderSize; } else { status_.pos = status_.pos == Status::npos() ? 0 : status_.pos + token_byte_length_; } status_.error = Error::OK; if (status_.pos >= bytes_.size()) { token_tag_ = CBORTokenTag::DONE; return; } switch (bytes_[status_.pos]) { case kStopByte: SetToken(CBORTokenTag::STOP, 1); return; case kInitialByteIndefiniteLengthMap: SetToken(CBORTokenTag::MAP_START, 1); return; case kInitialByteIndefiniteLengthArray: SetToken(CBORTokenTag::ARRAY_START, 1); return; case kEncodedTrue: SetToken(CBORTokenTag::TRUE_VALUE, 1); return; case kEncodedFalse: SetToken(CBORTokenTag::FALSE_VALUE, 1); return; case kEncodedNull: SetToken(CBORTokenTag::NULL_VALUE, 1); return; case kExpectedConversionToBase64Tag: { // BINARY int8_t bytes_read = ReadTokenStart(bytes_.subspan(status_.pos + 1), &token_start_type_, &token_start_internal_value_); int64_t token_byte_length = 1 + bytes_read + token_start_internal_value_; if (-1 == bytes_read || token_start_type_ != MajorType::BYTE_STRING || status_.pos + token_byte_length > bytes_.size()) { SetError(Error::CBOR_INVALID_BINARY); return; } SetToken(CBORTokenTag::BINARY, static_cast(token_byte_length)); return; } case kInitialByteForDouble: { // DOUBLE if (status_.pos + kEncodedDoubleSize > bytes_.size()) { SetError(Error::CBOR_INVALID_DOUBLE); return; } SetToken(CBORTokenTag::DOUBLE, kEncodedDoubleSize); return; } case kInitialByteForEnvelope: { // ENVELOPE if (status_.pos + kEncodedEnvelopeHeaderSize > bytes_.size()) { SetError(Error::CBOR_INVALID_ENVELOPE); return; } // The envelope must be a byte string with 32 bit length. if (bytes_[status_.pos + 1] != kInitialByteFor32BitLengthByteString) { SetError(Error::CBOR_INVALID_ENVELOPE); return; } // Read the length of the byte string. token_start_internal_value_ = ReadBytesMostSignificantByteFirst( bytes_.subspan(status_.pos + 2)); // Make sure the payload is contained within the message. if (token_start_internal_value_ + kEncodedEnvelopeHeaderSize + status_.pos > static_cast(bytes_.size())) { SetError(Error::CBOR_INVALID_ENVELOPE); return; } auto length = static_cast(token_start_internal_value_); SetToken(CBORTokenTag::ENVELOPE, kEncodedEnvelopeHeaderSize + length); return; } default: { span remainder = bytes_.subspan(status_.pos, bytes_.size() - status_.pos); assert(!remainder.empty()); int8_t token_start_length = ReadTokenStart(remainder, &token_start_type_, &token_start_internal_value_); bool success = token_start_length != -1; switch (token_start_type_) { case MajorType::UNSIGNED: // INT32. if (!success || std::numeric_limits::max() < token_start_internal_value_) { SetError(Error::CBOR_INVALID_INT32); return; } SetToken(CBORTokenTag::INT32, token_start_length); return; case MajorType::NEGATIVE: // INT32. if (!success || std::numeric_limits::min() > -static_cast(token_start_internal_value_) - 1) { SetError(Error::CBOR_INVALID_INT32); return; } SetToken(CBORTokenTag::INT32, token_start_length); return; case MajorType::STRING: { // STRING8. if (!success || remainder.size() < static_cast( token_start_internal_value_)) { SetError(Error::CBOR_INVALID_STRING8); return; } auto length = static_cast(token_start_internal_value_); SetToken(CBORTokenTag::STRING8, token_start_length + length); return; } case MajorType::BYTE_STRING: { // STRING16. if (!success || remainder.size() < static_cast(token_start_internal_value_) || // Must be divisible by 2 since UTF16 is 2 bytes per character. token_start_internal_value_ & 1) { SetError(Error::CBOR_INVALID_STRING16); return; } auto length = static_cast(token_start_internal_value_); SetToken(CBORTokenTag::STRING16, token_start_length + length); return; } case MajorType::ARRAY: case MajorType::MAP: case MajorType::TAG: case MajorType::SIMPLE_VALUE: SetError(Error::CBOR_UNSUPPORTED_VALUE); return; } } } } void CBORTokenizer::SetToken(CBORTokenTag token_tag, std::ptrdiff_t token_byte_length) { token_tag_ = token_tag; token_byte_length_ = token_byte_length; } void CBORTokenizer::SetError(Error error) { token_tag_ = CBORTokenTag::ERROR_VALUE; status_.error = error; } #if 0 void DumpCBOR(span cbor) { std::string indent; CBORTokenizer tokenizer(cbor); while (true) { fprintf(stderr, "%s", indent.c_str()); switch (tokenizer.TokenTag()) { case CBORTokenTag::ERROR_VALUE: fprintf(stderr, "ERROR {status.error=%d, status.pos=%ld}\n", tokenizer.Status().error, tokenizer.Status().pos); return; case CBORTokenTag::DONE: fprintf(stderr, "DONE\n"); return; case CBORTokenTag::TRUE_VALUE: fprintf(stderr, "TRUE_VALUE\n"); break; case CBORTokenTag::FALSE_VALUE: fprintf(stderr, "FALSE_VALUE\n"); break; case CBORTokenTag::NULL_VALUE: fprintf(stderr, "NULL_VALUE\n"); break; case CBORTokenTag::INT32: fprintf(stderr, "INT32 [%d]\n", tokenizer.GetInt32()); break; case CBORTokenTag::DOUBLE: fprintf(stderr, "DOUBLE [%lf]\n", tokenizer.GetDouble()); break; case CBORTokenTag::STRING8: { span v = tokenizer.GetString8(); std::string t(v.begin(), v.end()); fprintf(stderr, "STRING8 [%s]\n", t.c_str()); break; } case CBORTokenTag::STRING16: { span v = tokenizer.GetString16WireRep(); std::string t(v.begin(), v.end()); fprintf(stderr, "STRING16 [%s]\n", t.c_str()); break; } case CBORTokenTag::BINARY: { span v = tokenizer.GetBinary(); std::string t(v.begin(), v.end()); fprintf(stderr, "BINARY [%s]\n", t.c_str()); break; } case CBORTokenTag::MAP_START: fprintf(stderr, "MAP_START\n"); indent += " "; break; case CBORTokenTag::ARRAY_START: fprintf(stderr, "ARRAY_START\n"); indent += " "; break; case CBORTokenTag::STOP: fprintf(stderr, "STOP\n"); indent.erase(0, 2); break; case CBORTokenTag::ENVELOPE: fprintf(stderr, "ENVELOPE\n"); tokenizer.EnterEnvelope(); continue; } tokenizer.Next(); } } #endif {% for namespace in config.protocol.namespace %} } // namespace {{namespace}} {% endfor %}