SatCat5
io_cbor.cc
1 // Copyright 2024-2025 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <satcat5/io_cbor.h>
7 #include <satcat5/log.h>
8 
9 #if SATCAT5_CBOR_ENABLE
10 
15 
16 // Required due to G++ bug: https://stackoverflow.com/questions/25594644
17 namespace satcat5 {
18  namespace cbor {
20  Writeable* dst, QCBOREncodeContext* encode,
21  u8* buff, unsigned size, bool automap)
22  : cbor(encode)
23  , m_dst(dst)
24  , m_encoded(nullptr)
25  , m_encoded_len(0)
26  , m_auto_close(automap ? QCBOR_TYPE_MAP : QCBOR_TYPE_NONE)
27  , m_read(buff, 0)
28  {
29  // Initialize the encoder state, QCBOR saves buffer ptr and size.
30  QCBOREncode_Init(cbor, {buff, size});
31  if (automap) { QCBOREncode_OpenMap(cbor); } // Open a Map
32  }
33 
35  // Finish the CBOR object, validate, and write if successful.
36  // Note: This only applies to automap, so it's always a Map.
37  switch (m_auto_close) {
38  case QCBOR_TYPE_MAP: QCBOREncode_CloseMap(cbor); break;
39  case QCBOR_TYPE_ARRAY: QCBOREncode_CloseArray(cbor); break;
40  }
41  UsefulBufC encoded; // Should be const pointer to m_buff
42  QCBORError error = QCBOREncode_Finish(cbor, &encoded);
43  if (error != QCBOR_SUCCESS) { return false; }
44  m_encoded = (const u8*) encoded.ptr;
45  m_encoded_len = encoded.len;
46  if (m_dst) { m_dst->write_bytes(encoded.len, encoded.ptr); }
47  m_read.read_reset(encoded.len);
48  return true;
49  }
50 
51  UsefulBufC CborWriter::get_encoded() {
52  if (!m_encoded) close();
53  return { m_encoded, m_encoded_len };
54  }
55 
57  if (!m_encoded) close();
58  return &m_read;
59  }
60 
61  // Template specialization for key writing
62  template <>
63  void MapWriter<s64>::add_key(s64 key) const {
64  QCBOREncode_AddInt64(cbor, key);
65  }
66 
67  // Template specialization for key writing
68  template <>
69  void MapWriter<const char*>::add_key(const char* key) const {
70  QCBOREncode_AddSZString(cbor, key);
71  }
72 
73  // Write a bool array
74  void ListWriter::add_array(u32 len, const bool* value) const {
75  QCBOREncode_OpenArray(cbor);
76  for (unsigned a = 0; a < len; ++a)
77  { QCBOREncode_AddBool(cbor, value[a]); }
78  QCBOREncode_CloseArray(cbor);
79  }
80 
81  template <typename KEYTYPE>
82  void MapWriter<KEYTYPE>::add_array(KEYTYPE key, u32 len, const bool* value) const {
83  add_key(key);
84  QCBOREncode_OpenArray(cbor);
85  for (unsigned a = 0; a < len; ++a)
86  { QCBOREncode_AddBool(cbor, value[a]); }
87  QCBOREncode_CloseArray(cbor);
88  }
89 
90  // Template for writing types handled by QCBOREncode_AddUInt64
91  template <typename T>
92  void CborWriter::add_unsigned_array(unsigned len, const T* value) const {
93  QCBOREncode_OpenArray(cbor);
94  for (unsigned a = 0; a < len; ++a)
95  { QCBOREncode_AddUInt64(cbor, value[a]); }
96  QCBOREncode_CloseArray(cbor);
97  }
98 
99  // Template for writing types handled by QCBOREncode_AddInt64
100  template <typename T>
101  void CborWriter::add_signed_array(unsigned len, const T* value) const {
102  QCBOREncode_OpenArray(cbor);
103  for (unsigned a = 0; a < len; ++a)
104  { QCBOREncode_AddInt64(cbor, value[a]); }
105  QCBOREncode_CloseArray(cbor);
106  }
107 
108  // Write a float array
109  void ListWriter::add_array(u32 len, const float* value) const {
110  QCBOREncode_OpenArray(cbor);
111  for (unsigned a = 0; a < len; ++a)
112  { QCBOREncode_AddFloat(cbor, value[a]); }
113  QCBOREncode_CloseArray(cbor);
114  }
115 
116  template <typename KEYTYPE>
117  void MapWriter<KEYTYPE>::add_array(KEYTYPE key, u32 len, const float* value) const {
118  add_key(key);
119  QCBOREncode_OpenArray(cbor);
120  for (unsigned a = 0; a < len; ++a)
121  { QCBOREncode_AddFloat(cbor, value[a]); }
122  QCBOREncode_CloseArray(cbor);
123  }
124 
125  // Write a double array
126  void ListWriter::add_array(u32 len, const double* value) const {
127  QCBOREncode_OpenArray(cbor);
128  for (unsigned a = 0; a < len; ++a)
129  { QCBOREncode_AddFloat(cbor, value[a]); }
130  QCBOREncode_CloseArray(cbor);
131  }
132 
133  template <typename KEYTYPE>
134  void MapWriter<KEYTYPE>::add_array(KEYTYPE key, u32 len, const double* value) const {
135  add_key(key);
136  QCBOREncode_OpenArray(cbor);
137  for (unsigned a = 0; a < len; ++a)
138  { QCBOREncode_AddDouble(cbor, value[a]); }
139  QCBOREncode_CloseArray(cbor);
140  }
141 
142  // Write a nested array/list
143  QCBOREncodeContext* ListWriter::open_list() {
144  QCBOREncode_OpenArray(cbor);
145  return cbor;
146  }
147 
148  template <>
149  QCBOREncodeContext* MapWriter<s64>::open_list(s64 key) {
150  QCBOREncode_OpenArrayInMapN(cbor, key);
151  return cbor;
152  }
153 
154  template <>
155  QCBOREncodeContext* MapWriter<const char*>::open_list(const char* key) {
156  QCBOREncode_OpenArrayInMap(cbor, key);
157  return cbor;
158  }
159 
160  // Write a nested dictionary
161  QCBOREncodeContext* ListWriter::open_map() {
162  QCBOREncode_OpenMap(cbor);
163  return cbor;
164  }
165 
166  template <>
167  QCBOREncodeContext* MapWriter<s64>::open_map(s64 key) {
168  QCBOREncode_OpenMapInMapN(cbor, key);
169  return cbor;
170  }
171 
172  template <>
173  QCBOREncodeContext* MapWriter<const char*>::open_map(const char* key) {
174  QCBOREncode_OpenMapInMap(cbor, key);
175  return cbor;
176  }
177 
178  // Open the Decoder from a Readable and copy into the qcbor buff
179  CborReader::CborReader(
180  Readable* src, QCBORDecodeContext* decode,
181  u8* buff, unsigned size)
182  : cbor(decode)
183  {
184  unsigned src_len = size;
185  if (src) {
186  src_len = src->get_read_ready();
187  if (src_len > size || !src->read_bytes(src_len, buff))
188  { cbor->uLastError = QCBOR_ERR_BUFFER_TOO_SMALL; return; }
189  src->read_finalize();
190  }
191  QCBORDecode_Init(cbor, {buff, src_len}, QCBOR_DECODE_MODE_NORMAL);
192  }
193 
194  bool CborReader::copy_item(QCBOREncodeContext* dst) {
195  // Consume the next item, noting before/after parsing position.
196  // (For arrays and maps, this method consumes the entire structure.)
197  QCBORItem item;
198  size_t idx_dat = UsefulInputBuf_Tell(&cbor->InBuf);
199  QCBORDecode_VGetNextConsume(cbor, &item);
200  QCBORError err = QCBORDecode_GetError(cbor);
201  if (err != QCBOR_SUCCESS) {
202  if (err == QCBOR_ERR_NO_MORE_ITEMS)
203  { QCBORDecode_GetAndResetError(cbor); }
204  return false;
205  }
206  size_t idx_end = UsefulInputBuf_Tell(&cbor->InBuf);
207  // Convert offsets to start/end pointers.
208  const u8* ptr_dat = (const u8*)cbor->InBuf.UB.ptr + idx_dat;
209  const u8* ptr_end = (const u8*)cbor->InBuf.UB.ptr + idx_end;
210  // If a label is present, consume that information first.
211  // (QCBOR labels can only have integer or string types.)
212  if (item.uLabelType == QCBOR_TYPE_INT64 ||
213  item.uLabelType == QCBOR_TYPE_UINT64) {
214  size_t len_key = peek_integer_len(ptr_dat);
215  QCBOREncode_AddEncoded(dst, UsefulBufC{ptr_dat, len_key});
216  ptr_dat += len_key;
217  } else if (item.uLabelType == QCBOR_TYPE_TEXT_STRING) {
218  const u8* ptr_mid = (const u8*)item.label.string.ptr + item.label.string.len;
219  QCBOREncode_AddEncoded(dst, UsefulBufC{ptr_dat, size_t(ptr_mid - ptr_dat)});
220  ptr_dat = ptr_mid;
221  }
222  // Copy remaining data as a single "item", potentially nested.
223  QCBOREncode_AddEncoded(dst, UsefulBufC{ptr_dat, size_t(ptr_end - ptr_dat)});
224  return true;
225  }
226 
227  unsigned CborReader::copy_all(QCBOREncodeContext* dst) {
228  unsigned count = 0;
229  while (copy_item(dst)) {
230  ++count;
231  }
232  if (QCBORDecode_GetError(cbor) == QCBOR_ERR_NO_MORE_ITEMS)
233  { QCBORDecode_GetAndResetError(cbor); } // GCOVR_EXCL_LINE
234  return count;
235  }
236 
237  unsigned CborReader::peek_integer_len(const u8* rdptr) const {
238  // See IETF RFC8949, Appendix B: Jump Table for Initial Byte.
239  // https://www.rfc-editor.org/rfc/rfc8949.html#name-jump-table-for-initial-byte
240  const u8 tmp = (*rdptr) & 0xDF; // Ignore sign bit
241  if (tmp >= 0x1C) return 0; // Not a valid integer
242  if (tmp == 0x1B) return 9; // Header + u64/s64
243  if (tmp == 0x1A) return 5; // Header + u32/s32
244  if (tmp == 0x19) return 3; // Header + u16/s16
245  if (tmp == 0x18) return 2; // Header + u8/s8
246  return 1; // Header only
247  }
248 
249  // Templates for detecting if a key was found in a map.
250  // * If the value was successfully decoded, return it.
251  // * If the value was not found or the requested type was incorrect,
252  // clear the error and return empty.
253  // * If another error was thrown, return empty and leave the error.
254  bool key_found(QCBORDecodeContext* cbor) {
255  switch (QCBORDecode_GetError(cbor)) {
256  case QCBOR_SUCCESS:
257  return true;
258  case QCBOR_ERR_LABEL_NOT_FOUND:
259  case QCBOR_ERR_UNEXPECTED_TYPE:
260  QCBORDecode_GetAndResetError(cbor);
261  return false;
262  default:
263  return false;
264  }
265  }
266 
267  template <typename T>
268  inline optional<T> val_if_found(QCBORDecodeContext* cbor, T val) {
269  return key_found(cbor) ? val : optional<T>();
270  }
271 
272  // Constructor
274  Readable* src, QCBORDecodeContext* decode,
275  u8* buff, unsigned size)
276  : CborReader(src, decode, buff, size)
277  {
278  QCBORDecode_EnterArray(cbor, &m_item);
279  }
280 
281  // Constructor
282  template <typename KEYTYPE>
284  Readable* src, QCBORDecodeContext* decode,
285  u8* buff, unsigned size)
286  : CborReader(src, decode, buff, size)
287  {
288  QCBORDecode_EnterMap(cbor, &m_item);
289  }
290 
291  // Get a single item (polymorphic)
293  QCBORItem item;
294  QCBORDecode_GetNext(cbor, &item);
295  return val_if_found(cbor, item);
296  }
297 
298  // Get bool by key.
300  bool val;
301  QCBORDecode_GetBool(cbor, &val);
302  return val_if_found(cbor, val);
303  }
304 
305  template <>
307  bool val;
308  QCBORDecode_GetBoolInMapN(cbor, key, &val);
309  return val_if_found(cbor, val);
310  }
311 
312  template <>
313  optional<bool> MapReader<const char*>::get_bool(const char* key) const {
314  bool val;
315  QCBORDecode_GetBoolInMapSZ(cbor, key, &val);
316  return val_if_found(cbor, val);
317  }
318 
319  // Get signed/unsigned ints by key.
320  optional<s64> ListReader::get_int() const {
321  s64 val;
322  QCBORDecode_GetInt64(cbor, &val);
323  return val_if_found(cbor, val);
324  }
325 
326  template <>
327  optional<s64> MapReader<s64>::get_int(s64 key) const {
328  s64 val;
329  QCBORDecode_GetInt64InMapN(cbor, key, &val);
330  return val_if_found(cbor, val);
331  }
332 
333  template <>
334  optional<s64> MapReader<const char*>::get_int(const char* key) const {
335  s64 val;
336  QCBORDecode_GetInt64InMapSZ(cbor, key, &val);
337  return val_if_found(cbor, val);
338  }
339 
340  optional<u64> ListReader::get_uint() const {
341  u64 val;
342  QCBORDecode_GetUInt64(cbor, &val);
343  return val_if_found(cbor, val);
344  }
345 
346  template <>
347  optional<u64> MapReader<s64>::get_uint(s64 key) const {
348  u64 val;
349  QCBORDecode_GetUInt64InMapN(cbor, key, &val);
350  return val_if_found(cbor, val);
351  }
352 
353  template <>
355  u64 val;
356  QCBORDecode_GetUInt64InMapSZ(cbor, key, &val);
357  return val_if_found(cbor, val);
358  }
359 
360  // Get floating point value by key, returned as a double.
361  optional<double> ListReader::get_double() const {
362  double val;
363  QCBORDecode_GetDouble(cbor, &val);
364  return val_if_found(cbor, val);
365  }
366 
367  template <>
369  double val;
370  QCBORDecode_GetDoubleInMapN(cbor, key, &val);
371  return val_if_found(cbor, val);
372  }
373 
374  template <>
376  double val;
377  QCBORDecode_GetDoubleInMapSZ(cbor, key, &val);
378  return val_if_found(cbor, val);
379  }
380 
381  // Check if a value exists and is NULL.
382  template <>
383  bool MapReader<s64>::is_null(s64 key) const {
384  QCBORDecode_GetNullInMapN(cbor, key);
385  return val_if_found(cbor, true).has_value();
386  }
387 
388  template <>
389  bool MapReader<const char*>::is_null(const char* key) const {
390  QCBORDecode_GetNullInMapSZ(cbor, key);
391  return val_if_found(cbor, true).has_value();
392  }
393 
394  // Copy a sequence of bytes or a text string.
395  optional<ArrayRead> ListReader::get_string() const {
396  UsefulBufC buf;
397  QCBORDecode_GetTextString(cbor, &buf);
398  return val_if_found(cbor, ArrayRead(buf.ptr, buf.len));
399  }
400 
401  template <>
403  UsefulBufC buf;
404  QCBORDecode_GetTextStringInMapN(cbor, key, &buf);
405  return val_if_found(cbor, ArrayRead(buf.ptr, buf.len));
406  }
407 
408  template <>
410  UsefulBufC buf;
411  QCBORDecode_GetTextStringInMapSZ(cbor, key, &buf);
412  return val_if_found(cbor, ArrayRead(buf.ptr, buf.len));
413  }
414 
415  optional<ArrayRead> ListReader::get_bytes() const {
416  UsefulBufC buf;
417  QCBORDecode_GetByteString(cbor, &buf);
418  return val_if_found(cbor, ArrayRead(buf.ptr, buf.len));
419  }
420 
421  template <>
423  UsefulBufC buf;
424  QCBORDecode_GetByteStringInMapN(cbor, key, &buf);
425  return val_if_found(cbor, ArrayRead(buf.ptr, buf.len));
426  }
427 
428  template <>
430  UsefulBufC buf;
431  QCBORDecode_GetByteStringInMapSZ(cbor, key, &buf);
432  return val_if_found(cbor, ArrayRead(buf.ptr, buf.len));
433  }
434 
435  QCBORDecodeContext* ListReader::open_list() const {
436  QCBORDecode_EnterArray(cbor, nullptr);
437  return key_found(cbor) ? cbor : nullptr;
438  }
439 
440  template <>
441  QCBORDecodeContext* MapReader<s64>::open_list(s64 key) const {
442  QCBORDecode_EnterArrayFromMapN(cbor, key);
443  return key_found(cbor) ? cbor : nullptr;
444  }
445 
446  template <>
447  QCBORDecodeContext* MapReader<const char*>::open_list(const char* key) const {
448  QCBORDecode_EnterArrayFromMapSZ(cbor, key);
449  return key_found(cbor) ? cbor : nullptr;
450  }
451 
452  QCBORDecodeContext* ListReader::open_map() const {
453  QCBORDecode_EnterMap(cbor, nullptr);
454  return key_found(cbor) ? cbor : nullptr;
455  }
456 
457  template <>
458  QCBORDecodeContext* MapReader<s64>::open_map(s64 key) const {
459  QCBORDecode_EnterMapFromMapN(cbor, key);
460  return key_found(cbor) ? cbor : nullptr;
461  }
462 
463  template <>
464  QCBORDecodeContext* MapReader<const char*>::open_map(const char* key) const {
465  QCBORDecode_EnterMapFromMapSZ(cbor, key);
466  return key_found(cbor) ? cbor : nullptr;
467  }
468 
469  int ListReader::get_bool_array(Writeable& dst) const {
470  QCBORDecode_EnterArray(cbor, nullptr);
471  return get_array_internal(dst, QCBOR_TYPE_FALSE, 1);
472  }
473 
474  template <>
475  int MapReader<s64>::get_bool_array(s64 key, Writeable& dst) const {
476  QCBORDecode_EnterArrayFromMapN(cbor, key);
477  return get_array_internal(dst, QCBOR_TYPE_FALSE, 1);
478  }
479 
480  template <>
481  int MapReader<const char*>::get_bool_array(const char* key, Writeable& dst) const {
482  QCBORDecode_EnterArrayFromMapSZ(cbor, key);
483  return get_array_internal(dst, QCBOR_TYPE_FALSE, 1);
484  }
485 
486  int ListReader::get_s64_array(Writeable& dst) const {
487  QCBORDecode_EnterArray(cbor, nullptr);
488  return get_array_internal(dst, QCBOR_TYPE_INT64, sizeof(u64));
489  }
490 
491  template <>
492  int MapReader<s64>::get_s64_array(s64 key, Writeable& dst) const {
493  QCBORDecode_EnterArrayFromMapN(cbor, key);
494  return get_array_internal(dst, QCBOR_TYPE_INT64, sizeof(u64));
495  }
496 
497  template <>
498  int MapReader<const char*>::get_s64_array(const char* key, Writeable& dst) const {
499  QCBORDecode_EnterArrayFromMapSZ(cbor, key);
500  return get_array_internal(dst, QCBOR_TYPE_INT64, sizeof(u64));
501  }
502 
503  int ListReader::get_double_array(Writeable& dst) const {
504  QCBORDecode_EnterArray(cbor, nullptr);
505  return get_array_internal(dst, QCBOR_TYPE_FLOAT, sizeof(double));
506  }
507 
508  template <>
509  int MapReader<s64>::get_double_array(s64 key, Writeable& dst) const {
510  QCBORDecode_EnterArrayFromMapN(cbor, key);
511  return get_array_internal(dst, QCBOR_TYPE_FLOAT, sizeof(double));
512  }
513 
514  template <>
515  int MapReader<const char*>::get_double_array(const char* key, Writeable& dst) const {
516  QCBORDecode_EnterArrayFromMapSZ(cbor, key);
517  return get_array_internal(dst, QCBOR_TYPE_FLOAT, sizeof(double));
518  }
519 
520  int CborReader::get_array_internal(Writeable& dst, u8 qcbor_type, u8 type_size) const {
521  // Check key lookup was successful and decoder is not errored.
522  QCBORError err = QCBORDecode_GetError(cbor);
523  if (err == QCBOR_ERR_LABEL_NOT_FOUND)
524  { QCBORDecode_GetAndResetError(cbor); return ERR_NOT_FOUND; }
525  if (err == QCBOR_ERR_UNEXPECTED_TYPE)
526  { QCBORDecode_GetAndResetError(cbor); return ERR_BAD_TYPE; }
527  if (err != QCBOR_SUCCESS) { return ERR_QCBOR_INT; } // Unknown.
528 
529  // Many QCBOR types are "paired" and have adjacent numbers. Ex:
530  // Int: 2, UInt: 3, True: 20, False: 21, Float: 26, Double: 27
531  // Since these decode to the same types, leverage this and bitmask
532  // out the LSB for currently supported decoder types.
533  // Note: This WILL be a problem for other future types (date).
534  const u8 type_mask = 0xFE; // Paired types (true = 20, false = 21).
535  qcbor_type &= type_mask;
536 
537  // In-order array traversal until it is out of items.
538  QCBORItem item;
539  QCBORDecode_GetNext(cbor, &item);
540  int num_elems = 0;
541  while (QCBORDecode_GetError(cbor) == QCBOR_SUCCESS &&
542  item.uDataType != QCBOR_TYPE_NONE) {
543 
544  // Check for destination buffer and type errors.
545  if (dst.get_write_space() < type_size) {
546  dst.write_abort();
547  QCBORDecode_ExitArray(cbor);
548  return ERR_OVERFLOW;
549  }
550  if ((item.uDataType & type_mask) != qcbor_type) {
551  dst.write_abort();
552  QCBORDecode_ExitArray(cbor);
553  return ERR_BAD_TYPE;
554  }
555 
556  // Booleans - Write true/false indicated by type.
557  // All others: Write blindly to the destination.
558  if (qcbor_type == QCBOR_TYPE_FALSE) {
559  dst.write_u8(item.uDataType == QCBOR_TYPE_TRUE ? true : false);
560  } else {
561  dst.write_bytes(type_size, &item.val);
562  }
563 
564  // Advance to next element and log number of elements.
565  num_elems++;
566  QCBORDecode_GetNext(cbor, &item);
567  }
568 
569  // If reached the end of the array - exit and reset error state.
570  QCBORDecode_ExitArray(cbor);
571  if (!dst.write_finalize()) { return ERR_OVERFLOW; }
572  return (QCBORDecode_GetError(cbor) == QCBOR_SUCCESS) ?
573  num_elems : ERR_QCBOR_INT;
574  }
575 
576  // Array return types - these need to be in .cc due to template.
577  const int CborReader::ERR_NOT_FOUND = -1;
578  const int CborReader::ERR_OVERFLOW = -2;
579  const int CborReader::ERR_BAD_TYPE = -3;
580  const int CborReader::ERR_QCBOR_INT = -4;
581 
582  // Template instantiations for QCBOR's supported key types.
583  template class MapWriter<s64>;
584  template class MapWriter<const char*>;
585  template class MapReader<s64>;
586  template class MapReader<const char*>;
587  template void CborWriter::add_unsigned_array<u8>(unsigned len, const u8* value) const;
588  template void CborWriter::add_unsigned_array<u16>(unsigned len, const u16* value) const;
589  template void CborWriter::add_unsigned_array<u32>(unsigned len, const u32* value) const;
590  template void CborWriter::add_unsigned_array<u64>(unsigned len, const u64* value) const;
591  template void CborWriter::add_signed_array<s8>(unsigned len, const s8* value) const;
592  template void CborWriter::add_signed_array<s16>(unsigned len, const s16* value) const;
593  template void CborWriter::add_signed_array<s32>(unsigned len, const s32* value) const;
594  template void CborWriter::add_signed_array<s64>(unsigned len, const s64* value) const;
595 
596  void Logger::log_to(satcat5::log::LogBuffer& wr) const {
597  // Always lead with prefix.
598  wr.wr_str(" = ");
599 
600  // Print label if applicable.
601  if (m_item.uLabelType == QCBOR_TYPE_INT64) {
602  wr.wr_s64(m_item.label.int64);
603  wr.wr_str(" / ");
604  } else if (m_item.uLabelType == QCBOR_TYPE_BYTE_STRING
605  || m_item.uLabelType == QCBOR_TYPE_TEXT_STRING) {
606  wr.wr_fix((const char*)m_item.label.string.ptr, m_item.label.string.len);
607  wr.wr_str(" / ");
608  }
609 
610  // Print primary value, if we understand the format.
611  if (m_item.uDataType == QCBOR_TYPE_INT64) {
612  wr.wr_s64(m_item.val.int64);
613  } else if (m_item.uDataType == QCBOR_TYPE_UINT64) {
614  wr.wr_d64(m_item.val.uint64);
615  } else if (m_item.uDataType == QCBOR_TYPE_ARRAY) {
616  wr.wr_str("[Array]");
617  } else if (m_item.uDataType == QCBOR_TYPE_MAP) {
618  wr.wr_str("[Map]");
619  } else if (m_item.uDataType == QCBOR_TYPE_BYTE_STRING) {
620  const u8* data = (const u8*)m_item.val.string.ptr;
621  wr.wr_str("0x");
622  for (unsigned a = 0 ; a < m_item.val.string.len ; ++a) {
623  wr.wr_h32(data[a], 2);
624  }
625  } else if (m_item.uDataType == QCBOR_TYPE_TEXT_STRING
626  || m_item.uDataType == QCBOR_TYPE_URI) {
627  wr.wr_str("\"");
628  wr.wr_fix((const char*)m_item.val.string.ptr, m_item.val.string.len);
629  wr.wr_str("\"");
630  } else if (m_item.uDataType == QCBOR_TYPE_FALSE) {
631  wr.wr_str("False");
632  } else if (m_item.uDataType == QCBOR_TYPE_TRUE) {
633  wr.wr_str("True");
634  } else if (m_item.uDataType == QCBOR_TYPE_NULL) {
635  wr.wr_str("Null");
636  } else {
637  wr.wr_str("[Unknown]");
638  }
639  }
640  } // namespace io
641 } // namespace satcat5
642 
643 #endif // SATCAT5_CBOR_ENABLE
Creates an ephemeral reader for the Concise Binary Object Representation (CBOR, IETF RFC8949),...
Definition: io_cbor.h:465
unsigned peek_integer_len(const u8 *rdptr) const
Predict the length of the next integer value.
Definition: io_cbor.cc:237
bool copy_item(QCBOREncodeContext *dst)
Copy the next CBOR item to the specified destination.
Definition: io_cbor.cc:194
QCBORDecodeContext *const cbor
QCBOR context pointer.
Definition: io_cbor.h:507
unsigned copy_all(QCBOREncodeContext *dst)
Copy all remaining CBOR item(s) to the specified destination.
Definition: io_cbor.cc:227
QCBORItem m_item
QCBOR Decoder item.
Definition: io_cbor.h:542
unsigned m_encoded_len
Encoded length.
Definition: io_cbor.h:173
bool close()
Close the QCBOR encoder by calling QCBOREncode_Finish and copy the resulting bytes to m_dst,...
Definition: io_cbor.cc:34
CborWriter(satcat5::io::Writeable *dst, QCBOREncodeContext *encode, u8 *buff, unsigned size, bool automap=false)
Constructor requires child class to provide a working buffer.
Definition: io_cbor.cc:19
satcat5::io::ArrayRead m_read
Reads working buffer.
Definition: io_cbor.h:175
const u8 * m_encoded
Encoded data.
Definition: io_cbor.h:172
UsefulBufC get_encoded()
Get encoded data as a UsefulBuf, useful if no dst was given.
Definition: io_cbor.cc:51
satcat5::io::Readable * get_buffer()
Get encoded data as a Readable, useful if no dst was given.
Definition: io_cbor.cc:56
u8 m_auto_close
Auto-close QCBOR Type.
Definition: io_cbor.h:174
QCBOREncodeContext *const cbor
QCBOR context pointer.
Definition: io_cbor.h:124
satcat5::io::Writeable *const m_dst
Writeable sink.
Definition: io_cbor.h:171
satcat5::util::optional< QCBORItem > get_item() const
Read the next scalar value from the List.
Definition: io_cbor.cc:292
satcat5::util::optional< bool > get_bool() const
Read the next scalar value from the List.
Definition: io_cbor.cc:299
constexpr ListReader(QCBORDecodeContext *decode)
Create a ListReader from an existing decoder context.
Definition: io_cbor.h:557
Reads a series of key-value pairs from a CBOR Map.
Definition: io_cbor.h:678
satcat5::util::optional< satcat5::io::ArrayRead > get_bytes(const char * key) const
Read a byte sequence from the Map.
int get_double_array(const char * key, satcat5::io::Writeable &dst) const
Read an array of floating-point values from the Map, always written as doubles to cover any precision...
QCBORDecodeContext * open_map(const char * key) const
Read a nested dictionary from the Map.
satcat5::util::optional< s64 > get_int(const char * key) const
QCBORDecodeContext * open_list(const char * key) const
Read a nested list from the Map.
satcat5::util::optional< bool > get_bool(KEYTYPE key) const
Read a scalar value from the Map.
int get_bool_array(const char * key, satcat5::io::Writeable &dst) const
Read an array of boolean values from the Map, always written as u8 values regardless of platform size...
constexpr MapReader(QCBORDecodeContext *decode)
Create a MapReader from an existing decoder context.
Definition: io_cbor.h:685
satcat5::util::optional< double > get_double(const char * key) const
int get_s64_array(const char * key, satcat5::io::Writeable &dst) const
Read an array of integer values from the Map, always written as 64-bit length signed values (s64) to ...
bool is_null(const char * key) const
Check if a key in the Map exists and is NULL.
satcat5::util::optional< satcat5::io::ArrayRead > get_string(const char * key) const
Read a string from the Map.
Write a series of key-value pairs to a CBOR Map.
Definition: io_cbor.h:302
void add_key(KEYTYPE key) const
Templated function to write the correct key type to the Map.
QCBOREncodeContext * open_list(const char * key)
Add a list to the Map.
QCBOREncodeContext * open_map(const char * key)
Add a nested dictionary to the Map.
Ephemeral Readable interface for a simple array.
Definition: io_readable.h:206
void read_reset(unsigned len)
Reset read position to the start of the backing array, and set the readable length to the specified v...
Definition: io_readable.cc:277
Abstract API for reading byte-streams and packets.
Definition: io_readable.h:68
virtual bool read_bytes(unsigned nbytes, void *dst)
Read 0 or more bytes into a buffer.
Definition: io_readable.cc:190
virtual void read_finalize()
Consume any remaining bytes in this frame, if applicable.
Definition: io_readable.cc:270
virtual unsigned get_read_ready() const =0
How many bytes can be read without blocking?
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
virtual unsigned get_write_space() const =0
How many bytes can be written without blocking?
virtual void write_bytes(unsigned nbytes, const void *src)
Write 0 or more bytes from a buffer.
virtual void write_abort()
If possible, abort the current partially-written packet.
void write_u8(u8 data)
One of many functions for writing integer/floating point values, see details.
Definition: io_writeable.cc:20
virtual bool write_finalize()
Mark end of frame and release temporary working data.
Internal buffer used by the Log class.
Definition: log.h:134
void wr_d64(u64 val, unsigned zpad=0)
Write an unsigned integer (u64) in decimal format.
Definition: log.cc:323
void wr_s64(s64 val, unsigned zpad=0)
Write a signed integer (s64) in decimal format.
Definition: log.cc:334
void wr_h32(u32 val, unsigned nhex=8)
Write an integer (u32) in hexadecimal format.
Definition: log.cc:303
void wr_fix(const char *str, unsigned len)
Write a fixed-length UTF-8 string.
Definition: log.cc:290
void wr_str(const char *str)
Write a null-terminated UTF-8 string.
Definition: log.cc:297
CBOR (IETF RFC8949) Readable/Writeable interface.
Diagnostic logging to UART and/or Ethernet ports.
An optional field that may be filled or empty.
Definition: utils.h:53