// Copyright 2017 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 "components/cbor/writer.h" #include #include "base/numerics/safe_conversions.h" #include "base/strings/string_piece.h" #include "components/cbor/constants.h" namespace cbor { Writer::~Writer() {} // static base::Optional> Writer::Write(const Value& node, size_t max_nesting_level) { std::vector cbor; Writer writer(&cbor); if (writer.EncodeCBOR(node, base::checked_cast(max_nesting_level))) return cbor; return base::nullopt; } Writer::Writer(std::vector* cbor) : encoded_cbor_(cbor) {} bool Writer::EncodeCBOR(const Value& node, int max_nesting_level) { if (max_nesting_level < 0) return false; switch (node.type()) { case Value::Type::NONE: { StartItem(Value::Type::BYTE_STRING, 0); return true; } // Represents unsigned integers. case Value::Type::UNSIGNED: { int64_t value = node.GetUnsigned(); StartItem(Value::Type::UNSIGNED, static_cast(value)); return true; } // Represents negative integers. case Value::Type::NEGATIVE: { int64_t value = node.GetNegative(); StartItem(Value::Type::NEGATIVE, static_cast(-(value + 1))); return true; } // Represents a byte string. case Value::Type::BYTE_STRING: { const Value::BinaryValue& bytes = node.GetBytestring(); StartItem(Value::Type::BYTE_STRING, base::strict_cast(bytes.size())); // Add the bytes. encoded_cbor_->insert(encoded_cbor_->end(), bytes.begin(), bytes.end()); return true; } case Value::Type::STRING: { base::StringPiece string = node.GetString(); StartItem(Value::Type::STRING, base::strict_cast(string.size())); // Add the characters. encoded_cbor_->insert(encoded_cbor_->end(), string.begin(), string.end()); return true; } // Represents an array. case Value::Type::ARRAY: { const Value::ArrayValue& array = node.GetArray(); StartItem(Value::Type::ARRAY, array.size()); for (const auto& value : array) { if (!EncodeCBOR(value, max_nesting_level - 1)) return false; } return true; } // Represents a map. case Value::Type::MAP: { const Value::MapValue& map = node.GetMap(); StartItem(Value::Type::MAP, map.size()); for (const auto& value : map) { if (!EncodeCBOR(value.first, max_nesting_level - 1)) return false; if (!EncodeCBOR(value.second, max_nesting_level - 1)) return false; } return true; } case Value::Type::TAG: NOTREACHED() << constants::kUnsupportedMajorType; return false; // Represents a simple value. case Value::Type::SIMPLE_VALUE: { const Value::SimpleValue simple_value = node.GetSimpleValue(); StartItem(Value::Type::SIMPLE_VALUE, base::checked_cast(simple_value)); return true; } } // This is needed because, otherwise, MSVC complains that not all paths return // a value. We should be able to remove it once MSVC builders are gone. NOTREACHED(); return false; } void Writer::StartItem(Value::Type type, uint64_t size) { encoded_cbor_->push_back(base::checked_cast( static_cast(type) << constants::kMajorTypeBitShift)); SetUint(size); } void Writer::SetAdditionalInformation(uint8_t additional_information) { DCHECK(!encoded_cbor_->empty()); DCHECK_EQ(additional_information & constants::kAdditionalInformationMask, additional_information); encoded_cbor_->back() |= (additional_information & constants::kAdditionalInformationMask); } void Writer::SetUint(uint64_t value) { size_t count = GetNumUintBytes(value); int shift = -1; // Values under 24 are encoded directly in the initial byte. // Otherwise, the last 5 bits of the initial byte contains the length // of unsigned integer, which is encoded in following bytes. switch (count) { case 0: SetAdditionalInformation(base::checked_cast(value)); break; case 1: SetAdditionalInformation(constants::kAdditionalInformation1Byte); shift = 0; break; case 2: SetAdditionalInformation(constants::kAdditionalInformation2Bytes); shift = 1; break; case 4: SetAdditionalInformation(constants::kAdditionalInformation4Bytes); shift = 3; break; case 8: SetAdditionalInformation(constants::kAdditionalInformation8Bytes); shift = 7; break; default: NOTREACHED(); break; } for (; shift >= 0; shift--) { encoded_cbor_->push_back(0xFF & (value >> (shift * 8))); } } size_t Writer::GetNumUintBytes(uint64_t value) { if (value < 24) { return 0; } else if (value <= 0xFF) { return 1; } else if (value <= 0xFFFF) { return 2; } else if (value <= 0xFFFFFFFF) { return 4; } return 8; } } // namespace cbor