blob: 8b020116c0cf2474ff387327c5aa77f3ba4dea84 [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2017 The Chromium Authors
Kim Paulhamus6efcf4952017-09-14 22:46:272// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Adam Langleye0e46cdf2018-10-29 19:23:165#include "components/cbor/writer.h"
Kim Paulhamus6efcf4952017-09-14 22:46:276
Russ Hamilton385541b2023-07-06 00:30:387#include <cstdint>
Tom Sepezb03f3f82021-12-09 23:43:388#include <ostream>
Jun Choi0ca86672017-11-14 04:58:249#include <string>
Helmut Januschka26faf712024-05-01 20:04:4810#include <string_view>
Jun Choi0ca86672017-11-14 04:58:2411
Russ Hamilton385541b2023-07-06 00:30:3812#include "base/bit_cast.h"
Hans Wennborgdf87046c2020-04-28 11:06:2413#include "base/check_op.h"
14#include "base/notreached.h"
Kim Paulhamus6efcf4952017-09-14 22:46:2715#include "base/numerics/safe_conversions.h"
Adam Langleye0e46cdf2018-10-29 19:23:1616#include "components/cbor/constants.h"
Kim Paulhamus6efcf4952017-09-14 22:46:2717
Jun Choi9f1446c02017-12-21 23:33:2718namespace cbor {
Kim Paulhamus6efcf4952017-09-14 22:46:2719
Sorin Jianuad029cb72024-10-04 03:35:5520Writer::~Writer() = default;
Kim Paulhamus6efcf4952017-09-14 22:46:2721
22// static
Arthur Sonzognic571efb2024-01-26 20:26:1823std::optional<std::vector<uint8_t>> Writer::Write(const Value& node,
24 const Config& config) {
Kim Paulhamus6efcf4952017-09-14 22:46:2725 std::vector<uint8_t> cbor;
Adam Langleyb4f12f92018-10-26 21:00:0226 Writer writer(&cbor);
Martin Kreichgauer2344a642019-07-15 21:32:5027 if (!writer.EncodeCBOR(node, config.max_nesting_level,
28 config.allow_invalid_utf8_for_testing)) {
Arthur Sonzognic571efb2024-01-26 20:26:1829 return std::nullopt;
Martin Kreichgauer2344a642019-07-15 21:32:5030 }
31 return cbor;
32}
33
34// static
Arthur Sonzognic571efb2024-01-26 20:26:1835std::optional<std::vector<uint8_t>> Writer::Write(const Value& node,
36 size_t max_nesting_level) {
Martin Kreichgauer2344a642019-07-15 21:32:5037 Config config;
38 config.max_nesting_level = base::checked_cast<int>(max_nesting_level);
39 return Write(node, config);
Kim Paulhamus6efcf4952017-09-14 22:46:2740}
41
Adam Langleyb4f12f92018-10-26 21:00:0242Writer::Writer(std::vector<uint8_t>* cbor) : encoded_cbor_(cbor) {}
Kim Paulhamus6efcf4952017-09-14 22:46:2743
Martin Kreichgauer2344a642019-07-15 21:32:5044bool Writer::EncodeCBOR(const Value& node,
45 int max_nesting_level,
46 bool allow_invalid_utf8) {
Jun Choi0ca86672017-11-14 04:58:2447 if (max_nesting_level < 0)
48 return false;
49
Kim Paulhamus6efcf4952017-09-14 22:46:2750 switch (node.type()) {
Adam Langleyb4f12f92018-10-26 21:00:0251 case Value::Type::NONE: {
52 StartItem(Value::Type::BYTE_STRING, 0);
Jun Choi0ca86672017-11-14 04:58:2453 return true;
Kim Paulhamus6efcf4952017-09-14 22:46:2754 }
55
Martin Kreichgauer2344a642019-07-15 21:32:5056 case Value::Type::INVALID_UTF8: {
57 if (!allow_invalid_utf8) {
Peter Boström77d21352024-11-13 22:26:1158 NOTREACHED() << constants::kUnsupportedMajorType;
Martin Kreichgauer2344a642019-07-15 21:32:5059 }
60 // Encode a CBOR string with invalid UTF-8 data. This may produce invalid
61 // CBOR and is reachable in tests only. See
62 // |allow_invalid_utf8_for_testing| in Config.
63 const Value::BinaryValue& bytes = node.GetInvalidUTF8();
64 StartItem(Value::Type::STRING, base::strict_cast<uint64_t>(bytes.size()));
65 encoded_cbor_->insert(encoded_cbor_->end(), bytes.begin(), bytes.end());
66 return true;
67 }
Adam Langley08718f732019-04-22 22:21:3368
Kim Paulhamus6efcf4952017-09-14 22:46:2769 // Represents unsigned integers.
Adam Langleyb4f12f92018-10-26 21:00:0270 case Value::Type::UNSIGNED: {
Jun Choi06ae32d2017-12-21 18:52:3971 int64_t value = node.GetUnsigned();
Adam Langleyb4f12f92018-10-26 21:00:0272 StartItem(Value::Type::UNSIGNED, static_cast<uint64_t>(value));
Jun Choi06ae32d2017-12-21 18:52:3973 return true;
74 }
75
76 // Represents negative integers.
Adam Langleyb4f12f92018-10-26 21:00:0277 case Value::Type::NEGATIVE: {
Jun Choi06ae32d2017-12-21 18:52:3978 int64_t value = node.GetNegative();
Adam Langleyb4f12f92018-10-26 21:00:0279 StartItem(Value::Type::NEGATIVE, static_cast<uint64_t>(-(value + 1)));
Jun Choi0ca86672017-11-14 04:58:2480 return true;
Kim Paulhamus6efcf4952017-09-14 22:46:2781 }
82
83 // Represents a byte string.
Adam Langleyb4f12f92018-10-26 21:00:0284 case Value::Type::BYTE_STRING: {
85 const Value::BinaryValue& bytes = node.GetBytestring();
86 StartItem(Value::Type::BYTE_STRING,
Kim Paulhamus6efcf4952017-09-14 22:46:2787 base::strict_cast<uint64_t>(bytes.size()));
88 // Add the bytes.
89 encoded_cbor_->insert(encoded_cbor_->end(), bytes.begin(), bytes.end());
Jun Choi0ca86672017-11-14 04:58:2490 return true;
Kim Paulhamus6efcf4952017-09-14 22:46:2791 }
92
Adam Langleyb4f12f92018-10-26 21:00:0293 case Value::Type::STRING: {
Helmut Januschka26faf712024-05-01 20:04:4894 std::string_view string = node.GetString();
Adam Langleyb4f12f92018-10-26 21:00:0295 StartItem(Value::Type::STRING,
Kim Paulhamus6efcf4952017-09-14 22:46:2796 base::strict_cast<uint64_t>(string.size()));
97
98 // Add the characters.
99 encoded_cbor_->insert(encoded_cbor_->end(), string.begin(), string.end());
Jun Choi0ca86672017-11-14 04:58:24100 return true;
Kim Paulhamus6efcf4952017-09-14 22:46:27101 }
102
103 // Represents an array.
Adam Langleyb4f12f92018-10-26 21:00:02104 case Value::Type::ARRAY: {
105 const Value::ArrayValue& array = node.GetArray();
106 StartItem(Value::Type::ARRAY, array.size());
Kim Paulhamus6efcf4952017-09-14 22:46:27107 for (const auto& value : array) {
Martin Kreichgauer2344a642019-07-15 21:32:50108 if (!EncodeCBOR(value, max_nesting_level - 1, allow_invalid_utf8))
Jun Choi0ca86672017-11-14 04:58:24109 return false;
Kim Paulhamus6efcf4952017-09-14 22:46:27110 }
Jun Choi0ca86672017-11-14 04:58:24111 return true;
Kim Paulhamus6efcf4952017-09-14 22:46:27112 }
113
114 // Represents a map.
Adam Langleyb4f12f92018-10-26 21:00:02115 case Value::Type::MAP: {
116 const Value::MapValue& map = node.GetMap();
117 StartItem(Value::Type::MAP, map.size());
Jun Choi0ca86672017-11-14 04:58:24118
Kim Paulhamus6efcf4952017-09-14 22:46:27119 for (const auto& value : map) {
Martin Kreichgauer2344a642019-07-15 21:32:50120 if (!EncodeCBOR(value.first, max_nesting_level - 1, allow_invalid_utf8))
Jun Choi0ca86672017-11-14 04:58:24121 return false;
Martin Kreichgauer2344a642019-07-15 21:32:50122 if (!EncodeCBOR(value.second, max_nesting_level - 1,
123 allow_invalid_utf8))
Jun Choi0ca86672017-11-14 04:58:24124 return false;
Kim Paulhamus6efcf4952017-09-14 22:46:27125 }
Jun Choi0ca86672017-11-14 04:58:24126 return true;
Kim Paulhamus6efcf4952017-09-14 22:46:27127 }
Jun Choi07540c62017-12-21 02:51:43128
Adam Langleyb4f12f92018-10-26 21:00:02129 case Value::Type::TAG:
Peter Boström77d21352024-11-13 22:26:11130 NOTREACHED() << constants::kUnsupportedMajorType;
Chris Palmerbe2d8dc2018-09-14 00:31:42131
Jun Choi07540c62017-12-21 02:51:43132 // Represents a simple value.
Adam Langleyb4f12f92018-10-26 21:00:02133 case Value::Type::SIMPLE_VALUE: {
134 const Value::SimpleValue simple_value = node.GetSimpleValue();
135 StartItem(Value::Type::SIMPLE_VALUE,
Jun Choi07540c62017-12-21 02:51:43136 base::checked_cast<uint64_t>(simple_value));
137 return true;
138 }
Russ Hamilton385541b2023-07-06 00:30:38139
140 case Value::Type::FLOAT_VALUE: {
141 const double float_value = node.GetDouble();
142 encoded_cbor_->push_back(base::checked_cast<uint8_t>(
143 static_cast<unsigned>(Value::Type::SIMPLE_VALUE)
144 << constants::kMajorTypeBitShift));
145 {
146 uint16_t value_16 = EncodeHalfPrecisionFloat(float_value);
147 const double decoded_float_16 = DecodeHalfPrecisionFloat(value_16);
148 if (decoded_float_16 == float_value ||
149 (std::isnan(decoded_float_16) && std::isnan(float_value))) {
150 // We can encode it in 16 bits.
151
152 SetAdditionalInformation(constants::kAdditionalInformation2Bytes);
153 for (int shift = 1; shift >= 0; shift--) {
154 encoded_cbor_->push_back(0xFF & (value_16 >> (shift * 8)));
155 }
156 return true;
157 }
158 }
159 {
160 const float float_value_32 = float_value;
161 if (float_value == float_value_32) {
162 // We can encode it in 32 bits.
163
164 SetAdditionalInformation(constants::kAdditionalInformation4Bytes);
165 uint32_t value_32 = base::bit_cast<uint32_t>(float_value_32);
166 for (int shift = 3; shift >= 0; shift--) {
167 encoded_cbor_->push_back(0xFF & (value_32 >> (shift * 8)));
168 }
169 return true;
170 }
171 }
172 {
173 // We can always encode it in 64 bits.
174 SetAdditionalInformation(constants::kAdditionalInformation8Bytes);
175 uint64_t value_64 = base::bit_cast<uint64_t>(float_value);
176 for (int shift = 7; shift >= 0; shift--) {
177 encoded_cbor_->push_back(0xFF & (value_64 >> (shift * 8)));
178 }
179 return true;
180 }
181 }
Kim Paulhamus6efcf4952017-09-14 22:46:27182 }
Kim Paulhamus6efcf4952017-09-14 22:46:27183}
184
Adam Langleyb4f12f92018-10-26 21:00:02185void Writer::StartItem(Value::Type type, uint64_t size) {
Jun Choi6d30c4a2017-12-09 01:10:32186 encoded_cbor_->push_back(base::checked_cast<uint8_t>(
Jun Choi9f1446c02017-12-21 23:33:27187 static_cast<unsigned>(type) << constants::kMajorTypeBitShift));
Kim Paulhamus6efcf4952017-09-14 22:46:27188 SetUint(size);
189}
190
Adam Langleyb4f12f92018-10-26 21:00:02191void Writer::SetAdditionalInformation(uint8_t additional_information) {
Kim Paulhamus6efcf4952017-09-14 22:46:27192 DCHECK(!encoded_cbor_->empty());
Jun Choi9f1446c02017-12-21 23:33:27193 DCHECK_EQ(additional_information & constants::kAdditionalInformationMask,
Adam Langley31c85e72017-10-17 06:10:35194 additional_information);
Kim Paulhamus6efcf4952017-09-14 22:46:27195 encoded_cbor_->back() |=
Jun Choi9f1446c02017-12-21 23:33:27196 (additional_information & constants::kAdditionalInformationMask);
Kim Paulhamus6efcf4952017-09-14 22:46:27197}
198
Adam Langleyb4f12f92018-10-26 21:00:02199void Writer::SetUint(uint64_t value) {
Kim Paulhamus6efcf4952017-09-14 22:46:27200 size_t count = GetNumUintBytes(value);
201 int shift = -1;
202 // Values under 24 are encoded directly in the initial byte.
203 // Otherwise, the last 5 bits of the initial byte contains the length
204 // of unsigned integer, which is encoded in following bytes.
205 switch (count) {
206 case 0:
207 SetAdditionalInformation(base::checked_cast<uint8_t>(value));
208 break;
209 case 1:
Jun Choi9f1446c02017-12-21 23:33:27210 SetAdditionalInformation(constants::kAdditionalInformation1Byte);
Kim Paulhamus6efcf4952017-09-14 22:46:27211 shift = 0;
212 break;
213 case 2:
Jun Choi9f1446c02017-12-21 23:33:27214 SetAdditionalInformation(constants::kAdditionalInformation2Bytes);
Kim Paulhamus6efcf4952017-09-14 22:46:27215 shift = 1;
216 break;
217 case 4:
Jun Choi9f1446c02017-12-21 23:33:27218 SetAdditionalInformation(constants::kAdditionalInformation4Bytes);
Kim Paulhamus6efcf4952017-09-14 22:46:27219 shift = 3;
220 break;
Kim Paulhamus6efcf4952017-09-14 22:46:27221 case 8:
Jun Choi9f1446c02017-12-21 23:33:27222 SetAdditionalInformation(constants::kAdditionalInformation8Bytes);
Kim Paulhamus6efcf4952017-09-14 22:46:27223 shift = 7;
224 break;
Adam Langley31c85e72017-10-17 06:10:35225 default:
Peter Boström77d21352024-11-13 22:26:11226 NOTREACHED();
Kim Paulhamus6efcf4952017-09-14 22:46:27227 }
228 for (; shift >= 0; shift--) {
Adam Langley31c85e72017-10-17 06:10:35229 encoded_cbor_->push_back(0xFF & (value >> (shift * 8)));
Kim Paulhamus6efcf4952017-09-14 22:46:27230 }
Kim Paulhamus6efcf4952017-09-14 22:46:27231}
232
Adam Langleyb4f12f92018-10-26 21:00:02233size_t Writer::GetNumUintBytes(uint64_t value) {
Kim Paulhamus6efcf4952017-09-14 22:46:27234 if (value < 24) {
235 return 0;
236 } else if (value <= 0xFF) {
237 return 1;
238 } else if (value <= 0xFFFF) {
239 return 2;
240 } else if (value <= 0xFFFFFFFF) {
241 return 4;
242 }
243 return 8;
244}
245
Jun Choi9f1446c02017-12-21 23:33:27246} // namespace cbor