13 Reader::Reader(
const std::string &data) : Reader(
std::string(data)) {}
19 Reader::Reader(std::string &&data) :
20 data(
std::make_shared<
std::string>(
std::forward<
std::string>(data))),
22 slice_length(this->data->size())
25 throw TREE_RUNTIME_ERROR(
"invalid CBOR: zero-size object");
33 Reader::Reader(
const Reader &parent,
size_t offs,
size_t len) :
35 slice_offset(parent.slice_offset + offs),
38 if (slice_offset + slice_length > parent.slice_offset + parent.slice_length) {
39 throw TREE_RUNTIME_ERROR(
"invalid CBOR: trying to slice past extents of current slice");
41 if (this->slice_length == 0) {
42 throw TREE_RUNTIME_ERROR(
"invalid CBOR: trying to make an empty slice");
46 uint8_t initial = read_at(0);
47 uint8_t type = initial >> 5u;
50 read_intlike(initial & 0x1Fu, tag_len);
51 slice_offset += tag_len;
52 slice_length -= tag_len;
53 if (this->slice_length == 0) {
54 throw TREE_RUNTIME_ERROR(
"invalid CBOR: semantic tag has no value");
62 Reader Reader::slice(
size_t offset,
size_t length)
const {
63 return Reader(*
this, offset, length);
69 uint8_t Reader::read_at(
size_t offset)
const {
70 if (offset >= slice_length) {
71 throw TREE_RUNTIME_ERROR(
"invalid CBOR: trying to read past extents of current slice");
73 return data->at(this->slice_offset + offset);
82 uint64_t Reader::read_intlike(uint8_t info,
size_t &offset)
const {
85 if (info < 24u)
return info;
89 uint64_t value = read_at(offset++);
90 if (info < 25u)
return value;
91 value <<= 8u; value |= read_at(offset++);
92 if (info < 26u)
return value;
93 value <<= 8u; value |= read_at(offset++);
94 value <<= 8u; value |= read_at(offset++);
95 if (info < 27u)
return value;
96 value <<= 8u; value |= read_at(offset++);
97 value <<= 8u; value |= read_at(offset++);
98 value <<= 8u; value |= read_at(offset++);
99 value <<= 8u; value |= read_at(offset++);
100 if (info < 28u)
return value;
104 throw TREE_RUNTIME_ERROR(
"invalid CBOR: illegal additional info for integer or object length");
114 void Reader::read_stringlike(
size_t &offset, std::ostream &s)
const {
115 uint8_t info = read_at(offset++) & 0x1Fu;
121 while (read_at(offset) != 0xFF) {
122 read_stringlike(offset, s);
129 uint64_t length = read_intlike(info, offset);
130 if (length + offset > this->slice_length) {
131 throw TREE_RUNTIME_ERROR(
"Invalid CBOR: string read past end of slice");
133 s.write(data->data() + this->slice_offset + offset, length);
143 void Reader::check_and_seek(
size_t &offset)
const {
146 uint8_t initial = read_at(offset++);
147 uint8_t type = initial >> 5u;
148 uint8_t info = initial & 0x1Fu;
154 read_intlike(info, offset);
166 while ((sub_initial = read_at(offset++)) != 0xFF) {
167 uint8_t sub_type = sub_initial >> 5u;
168 uint8_t sub_info = sub_initial & 0x1Fu;
169 if (sub_type != type) {
170 throw TREE_RUNTIME_ERROR(
"invalid CBOR: illegal indefinite-length string component");
175 offset += read_intlike(sub_info, offset);
184 offset += read_intlike(info, offset);
194 while (read_at(offset) != 0xFF) {
195 if (type == 5) check_and_seek(offset);
196 check_and_seek(offset);
207 for (uint64_t size = read_intlike(info, offset); size--;) {
208 if (type == 5) check_and_seek(offset);
209 check_and_seek(offset);
217 read_intlike(info, offset);
218 check_and_seek(offset);
236 throw TREE_RUNTIME_ERROR(
"invalid CBOR: undefined value is not supported");
239 throw TREE_RUNTIME_ERROR(
"invalid CBOR: half-precision float is not supported");
242 throw TREE_RUNTIME_ERROR(
"invalid CBOR: single-precision float is not supported");
249 throw TREE_RUNTIME_ERROR(
"invalid CBOR: unexpected break");
255 throw TREE_RUNTIME_ERROR(
"invalid CBOR: unknown type code");
262 void Reader::check()
const {
264 check_and_seek(offset);
265 if (offset != this->slice_length) {
266 throw TREE_RUNTIME_ERROR(
"invalid CBOR: garbage at end of outer object or multiple objects");
283 const char *Reader::get_type_name()
const {
284 uint8_t initial = read_at(0);
285 uint8_t type = initial >> 5u;
286 uint8_t info = initial & 0x1Fu;
288 case 0:
case 1:
return "integer";
289 case 2:
return "binary string";
290 case 3:
return "UTF8 string";
291 case 4:
return "array";
292 case 5:
return "map";
295 case 20:
case 21:
return "boolean";
296 case 22:
return "null";
297 case 27:
return "float";
302 return "unknown type";
308 bool Reader::is_null()
const {
309 return read_at(0) == 0xF6;
316 void Reader::as_null()
const {
318 throw TREE_RUNTIME_ERROR(
319 "unexpected CBOR structure: expected null but found " 320 + std::string(get_type_name()));
327 bool Reader::is_bool()
const {
328 return (read_at(0) & 0xFEu) == 0xF4;
335 bool Reader::as_bool()
const {
336 switch (read_at(0)) {
337 case 0xF4:
return false;
338 case 0xF5:
return true;
340 throw TREE_RUNTIME_ERROR(
341 "unexpected CBOR structure: expected boolean but found " 342 + std::string(get_type_name()));
348 bool Reader::is_int()
const {
349 return (read_at(0) & 0xC0u) == 0;
357 int64_t Reader::as_int()
const {
358 uint8_t initial = read_at(0);
359 uint8_t type = initial >> 5u;
361 throw TREE_RUNTIME_ERROR(
362 "unexpected CBOR structure: expected integer but found " 363 + std::string(get_type_name()));
365 uint8_t info = initial & 0x1Fu;
367 uint64_t value = read_intlike(info, offset);
368 if (value >= 0x8000000000000000ull) {
369 throw TREE_RUNTIME_ERROR(
"CBOR integer out of int64 range");
372 return (int64_t)value;
374 return -1 - (int64_t)value;
382 bool Reader::is_float()
const {
383 return read_at(0) == 0xFBu;
391 double Reader::as_float()
const {
393 throw TREE_RUNTIME_ERROR(
394 "unexpected CBOR structure: expected float but found " 395 + std::string(get_type_name()));
398 uint64_t value = read_intlike(27, offset);
400 memcpy(&retval, &value,
sizeof(retval));
407 bool Reader::is_string()
const {
408 return (read_at(0) & 0xE0u) == 0x60u;
416 std::string Reader::as_string()
const {
418 throw TREE_RUNTIME_ERROR(
419 "unexpected CBOR structure: expected UTF8 string but found " 420 + std::string(get_type_name()));
422 std::ostringstream ss;
424 read_stringlike(offset, ss);
431 bool Reader::is_binary()
const {
432 return (read_at(0) & 0xE0u) == 0x40u;
440 std::string Reader::as_binary()
const {
442 throw TREE_RUNTIME_ERROR(
443 "unexpected CBOR structure: expected binary string but found " 444 + std::string(get_type_name()));
446 std::ostringstream ss;
448 read_stringlike(offset, ss);
455 bool Reader::is_array()
const {
456 return (read_at(0) & 0xE0u) == 0x80u;
463 void Reader::read_array_item(
size_t &offset, ArrayReader &ar)
const {
464 size_t start = offset;
465 check_and_seek(offset);
466 ar.push_back(slice(start, offset - start));
473 ArrayReader Reader::as_array()
const {
475 throw TREE_RUNTIME_ERROR(
476 "unexpected CBOR structure: expected array but found " 477 + std::string(get_type_name()));
480 uint8_t info = read_at(0) & 0x1Fu;
487 while (read_at(offset) != 0xFF) {
488 read_array_item(offset, ar);
495 for (uint64_t size = read_intlike(info, offset); size--;) {
496 read_array_item(offset, ar);
507 bool Reader::is_map()
const {
508 return (read_at(0) & 0xE0u) == 0xA0u;
515 void Reader::read_map_item(
size_t &offset, MapReader &map)
const {
516 size_t key_start = offset;
517 check_and_seek(offset);
518 size_t data_start = offset;
519 check_and_seek(offset);
520 map.insert(std::make_pair(
521 slice(key_start, data_start - key_start).as_string(),
522 slice(data_start, offset - data_start)));
529 MapReader Reader::as_map()
const {
531 throw TREE_RUNTIME_ERROR(
532 "unexpected CBOR structure: expected map but found " 533 + std::string(get_type_name()));
536 uint8_t info = read_at(0) & 0x1Fu;
543 while (read_at(offset) != 0xFFu) {
544 read_map_item(offset, map);
551 for (uint64_t size = read_intlike(info, offset); size--;) {
552 read_map_item(offset, map);
563 std::string Reader::get_contents()
const {
564 return data->substr(slice_offset, slice_length);
570 StructureWriter::StructureWriter(Writer &writer) :
572 id(writer.id_counter)
574 writer.stack.push(
id);
582 std::ostream &StructureWriter::stream() {
583 if (!writer || writer->stack.empty() || writer->stack.top() != id) {
584 throw TREE_RUNTIME_ERROR(
"Attempt to write to CBOR object using inactive writer");
586 return writer->stream;
592 void StructureWriter::write_null() {
594 stream().write(reinterpret_cast<char*>(&data), 1);
600 void StructureWriter::write_bool(
bool value) {
601 uint8_t data = value ? 0xF5 : 0xF4;
602 stream().write(reinterpret_cast<char*>(&data), 1);
610 void StructureWriter::write_int(int64_t int_value, uint8_t major) {
614 value = -1 - int_value;
619 data[0] = major << 5u;
622 stream().write(reinterpret_cast<char*>(&data), 1);
623 }
else if (value < 0x100ll) {
625 data[1] =
static_cast<uint8_t
>(value);
626 stream().write(reinterpret_cast<char*>(&data), 2);
627 }
else if (value < 0x10000ll) {
629 data[1] =
static_cast<uint8_t
>(value >> 8u);
630 data[2] =
static_cast<uint8_t
>(value);
631 stream().write(reinterpret_cast<char*>(&data), 3);
632 }
else if (value < 0x100000000ll) {
634 data[1] =
static_cast<uint8_t
>(value >> 24u);
635 data[2] =
static_cast<uint8_t
>(value >> 16u);
636 data[3] =
static_cast<uint8_t
>(value >> 8u);
637 data[4] =
static_cast<uint8_t
>(value);
638 stream().write(reinterpret_cast<char*>(&data), 5);
641 data[1] =
static_cast<uint8_t
>(value >> 56u);
642 data[2] =
static_cast<uint8_t
>(value >> 48u);
643 data[3] =
static_cast<uint8_t
>(value >> 40u);
644 data[4] =
static_cast<uint8_t
>(value >> 32u);
645 data[5] =
static_cast<uint8_t
>(value >> 24u);
646 data[6] =
static_cast<uint8_t
>(value >> 16u);
647 data[7] =
static_cast<uint8_t
>(value >> 8u);
648 data[8] =
static_cast<uint8_t
>(value);
649 stream().write(reinterpret_cast<char*>(&data), 9);
656 void StructureWriter::write_float(
double value) {
659 std::memcpy(data + 1, &value, 8);
660 std::swap(data[1], data[8]);
661 std::swap(data[2], data[7]);
662 std::swap(data[3], data[6]);
663 std::swap(data[4], data[5]);
664 stream().write(reinterpret_cast<char*>(&data), 9);
670 void StructureWriter::write_string(
const std::string &value) {
671 write_int(value.size(), 3);
672 stream().write(value.data(), value.size());
678 void StructureWriter::write_binary(
const std::string &value) {
679 write_int(value.size(), 2);
680 stream().write(value.data(), value.size());
688 ArrayWriter StructureWriter::write_array() {
691 return ArrayWriter(*writer);
699 MapWriter StructureWriter::write_map() {
702 return MapWriter(*writer);
709 StructureWriter::~StructureWriter() {
710 if (writer && !writer->stack.empty() && writer->stack.top() == id) {
718 StructureWriter::StructureWriter(StructureWriter &&src) : writer(src.writer), id(src.id) {
719 src.writer =
nullptr;
726 StructureWriter &StructureWriter::operator=(StructureWriter &&src) {
727 if (writer && !writer->stack.empty() && writer->stack.top() == id) {
732 src.writer =
nullptr;
741 void StructureWriter::close() {
743 stream().write(reinterpret_cast<char*>(&data), 1);
751 ArrayWriter::ArrayWriter(Writer &writer) : StructureWriter(writer) {
753 stream().write(reinterpret_cast<char*>(&data), 1);
759 void ArrayWriter::append_null() {
766 void ArrayWriter::append_bool(
bool value) {
773 void ArrayWriter::append_int(int64_t value) {
780 void ArrayWriter::append_float(
double value) {
787 void ArrayWriter::append_string(
const std::string &value) {
794 void ArrayWriter::append_binary(
const std::string &value) {
803 ArrayWriter ArrayWriter::append_array() {
804 return write_array();
812 MapWriter ArrayWriter::append_map() {
820 MapWriter::MapWriter(Writer &writer) : StructureWriter(writer) {
822 stream().write(reinterpret_cast<char*>(&data), 1);
828 void MapWriter::append_null(
const std::string &key) {
836 void MapWriter::append_bool(
const std::string &key,
bool value) {
844 void MapWriter::append_int(
const std::string &key, int64_t value) {
853 void MapWriter::append_float(
const std::string &key,
double value) {
861 void MapWriter::append_string(
const std::string &key,
const std::string &value) {
869 void MapWriter::append_binary(
const std::string &key,
const std::string &value) {
880 ArrayWriter MapWriter::append_array(
const std::string &key) {
882 return write_array();
891 MapWriter MapWriter::append_map(
const std::string &key) {
899 Writer::Writer(std::ostream &stream) : stream(stream), id_counter(1) {
908 MapWriter Writer::start() {
909 if (!stack.empty()) {
910 throw TREE_RUNTIME_ERROR(
"Writing of this CBOR object has already started");
912 return MapWriter(*
this);