1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/sdp/data_element.h"
16
17 #include <cpp-string/string_printf.h>
18 #include <pw_assert/check.h>
19 #include <pw_bytes/endian.h>
20
21 #include <algorithm>
22 #include <cinttypes>
23 #include <set>
24 #include <vector>
25
26 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
27
28 // Returns true if |url| is a valid URI.
IsValidUrl(const std::string & url)29 bool IsValidUrl(const std::string& url) {
30 // Pulled from [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986).
31 // See Section 2.2 for the set of reserved characters.
32 // See Section 2.3 for the set of unreserved characters.
33 constexpr char kValidUrlChars[] =
34 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!#$&'("
35 ")*+,/:;=?@[]";
36 return url.find_first_not_of(kValidUrlChars) == std::string::npos;
37 }
38
39 namespace bt::sdp {
40
41 namespace {
42
43 // Size Descriptor occupies the lowest 3 bits of the header byte.
44 // v5.0, Vol 3, Part B, Sec 3.3.
45 constexpr uint8_t kDataElementSizeTypeMask = 0x07;
46
SizeToSizeType(size_t size)47 DataElement::Size SizeToSizeType(size_t size) {
48 switch (size) {
49 case 1:
50 return DataElement::Size::kOneByte;
51 case 2:
52 return DataElement::Size::kTwoBytes;
53 case 4:
54 return DataElement::Size::kFourBytes;
55 case 8:
56 return DataElement::Size::kEightBytes;
57 case 16:
58 return DataElement::Size::kSixteenBytes;
59 default:
60 PW_CRASH("invalid data element size: %zu", size);
61 }
62 return DataElement::Size::kNextFour;
63 }
64
AggregateSize(const std::vector<DataElement> & aggregate)65 size_t AggregateSize(const std::vector<DataElement>& aggregate) {
66 size_t total_size = 0;
67 for (const auto& elem : aggregate) {
68 total_size += elem.WriteSize();
69 }
70 return total_size;
71 }
72
WriteLength(MutableByteBuffer * buf,size_t length)73 size_t WriteLength(MutableByteBuffer* buf, size_t length) {
74 if (length <= std::numeric_limits<uint8_t>::max()) {
75 uint8_t val = static_cast<uint8_t>(length);
76 buf->Write(&val, sizeof(val));
77 return sizeof(uint8_t);
78 }
79
80 if (length <= std::numeric_limits<uint16_t>::max()) {
81 buf->WriteObj(pw::bytes::ConvertOrderTo(cpp20::endian::big,
82 static_cast<uint16_t>(length)));
83 return sizeof(uint16_t);
84 }
85
86 if (length <= std::numeric_limits<uint32_t>::max()) {
87 buf->WriteObj(pw::bytes::ConvertOrderTo(cpp20::endian::big,
88 static_cast<uint32_t>(length)));
89 return sizeof(uint32_t);
90 }
91
92 return 0;
93 }
94
95 } // namespace
96
DataElement()97 DataElement::DataElement() : type_(Type::kNull), size_(Size::kOneByte) {}
98
DataElement(const DataElement & other)99 DataElement::DataElement(const DataElement& other)
100 : type_(other.type_), size_(other.size_) {
101 switch (type_) {
102 case Type::kNull:
103 return;
104 case Type::kUnsignedInt:
105 uint_value_ = other.uint_value_;
106 return;
107 case Type::kBoolean:
108 case Type::kSignedInt:
109 int_value_ = other.int_value_;
110 return;
111 case Type::kUuid:
112 uuid_ = other.uuid_;
113 return;
114 case Type::kString:
115 case Type::kUrl:
116 bytes_ = DynamicByteBuffer(other.bytes_);
117 return;
118 case Type::kSequence:
119 case Type::kAlternative:
120 for (const auto& it : other.aggregate_) {
121 aggregate_.emplace_back(DataElement(it));
122 }
123 return;
124 }
125 }
126
127 template <>
Set(uint8_t value)128 void DataElement::Set<uint8_t>(uint8_t value) {
129 type_ = Type::kUnsignedInt;
130 size_ = SizeToSizeType(sizeof(uint8_t));
131 uint_value_ = value;
132 }
133
134 template <>
Set(uint16_t value)135 void DataElement::Set<uint16_t>(uint16_t value) {
136 type_ = Type::kUnsignedInt;
137 size_ = SizeToSizeType(sizeof(uint16_t));
138 uint_value_ = value;
139 }
140
141 template <>
Set(uint32_t value)142 void DataElement::Set<uint32_t>(uint32_t value) {
143 type_ = Type::kUnsignedInt;
144 size_ = SizeToSizeType(sizeof(uint32_t));
145 uint_value_ = value;
146 }
147
148 template <>
Set(uint64_t value)149 void DataElement::Set<uint64_t>(uint64_t value) {
150 type_ = Type::kUnsignedInt;
151 size_ = SizeToSizeType(sizeof(uint64_t));
152 uint_value_ = value;
153 }
154
155 template <>
Set(int8_t value)156 void DataElement::Set<int8_t>(int8_t value) {
157 type_ = Type::kSignedInt;
158 size_ = SizeToSizeType(sizeof(int8_t));
159 int_value_ = value;
160 }
161
162 template <>
Set(int16_t value)163 void DataElement::Set<int16_t>(int16_t value) {
164 type_ = Type::kSignedInt;
165 size_ = SizeToSizeType(sizeof(int16_t));
166 int_value_ = value;
167 }
168
169 template <>
Set(int32_t value)170 void DataElement::Set<int32_t>(int32_t value) {
171 type_ = Type::kSignedInt;
172 size_ = SizeToSizeType(sizeof(int32_t));
173 int_value_ = value;
174 }
175
176 template <>
Set(int64_t value)177 void DataElement::Set<int64_t>(int64_t value) {
178 type_ = Type::kSignedInt;
179 size_ = SizeToSizeType(sizeof(int64_t));
180 int_value_ = value;
181 }
182
183 template <>
Set(bool value)184 void DataElement::Set<bool>(bool value) {
185 type_ = Type::kBoolean;
186 size_ = Size::kOneByte;
187 int_value_ = (value ? 1 : 0);
188 }
189
190 template <>
Set(std::nullptr_t)191 void DataElement::Set<std::nullptr_t>(std::nullptr_t) {
192 type_ = Type::kNull;
193 size_ = Size::kOneByte;
194 }
195
196 template <>
Set(UUID value)197 void DataElement::Set<UUID>(UUID value) {
198 type_ = Type::kUuid;
199 size_ = SizeToSizeType(value.CompactSize());
200 uuid_ = value;
201 }
202
Set(const bt::DynamicByteBuffer & value)203 void DataElement::Set(const bt::DynamicByteBuffer& value) {
204 type_ = Type::kString;
205 SetVariableSize(value.size());
206 bytes_ = DynamicByteBuffer(value);
207 }
208
Set(const std::string & value)209 void DataElement::Set(const std::string& value) {
210 type_ = Type::kString;
211 SetVariableSize(value.size());
212 bytes_ = DynamicByteBuffer(value);
213 }
214
Set(std::vector<DataElement> && value)215 void DataElement::Set(std::vector<DataElement>&& value) {
216 type_ = Type::kSequence;
217 aggregate_ = std::move(value);
218 SetVariableSize(AggregateSize(aggregate_));
219 }
220
SetUrl(const std::string & url)221 void DataElement::SetUrl(const std::string& url) {
222 if (!IsValidUrl(url)) {
223 bt_log(WARN, "sdp", "Invalid URL in SetUrl: %s", url.c_str());
224 return;
225 }
226
227 type_ = Type::kUrl;
228 SetVariableSize(url.size());
229 bytes_ = DynamicByteBuffer(url);
230 }
231
SetAlternative(std::vector<DataElement> && items)232 void DataElement::SetAlternative(std::vector<DataElement>&& items) {
233 type_ = Type::kAlternative;
234 aggregate_ = std::move(items);
235 SetVariableSize(AggregateSize(aggregate_));
236 }
237
238 template <>
Get() const239 std::optional<uint8_t> DataElement::Get<uint8_t>() const {
240 if (type_ == Type::kUnsignedInt && size_ == SizeToSizeType(sizeof(uint8_t))) {
241 return static_cast<uint8_t>(uint_value_);
242 }
243
244 return std::nullopt;
245 }
246
247 template <>
Get() const248 std::optional<uint16_t> DataElement::Get<uint16_t>() const {
249 if (type_ == Type::kUnsignedInt &&
250 size_ == SizeToSizeType(sizeof(uint16_t))) {
251 return static_cast<uint16_t>(uint_value_);
252 }
253
254 return std::nullopt;
255 }
256
257 template <>
Get() const258 std::optional<uint32_t> DataElement::Get<uint32_t>() const {
259 if (type_ == Type::kUnsignedInt &&
260 size_ == SizeToSizeType(sizeof(uint32_t))) {
261 return static_cast<uint32_t>(uint_value_);
262 }
263
264 return std::nullopt;
265 }
266
267 template <>
Get() const268 std::optional<uint64_t> DataElement::Get<uint64_t>() const {
269 if (type_ == Type::kUnsignedInt &&
270 size_ == SizeToSizeType(sizeof(uint64_t))) {
271 return uint_value_;
272 }
273
274 return std::nullopt;
275 }
276
277 template <>
Get() const278 std::optional<int8_t> DataElement::Get<int8_t>() const {
279 if (type_ == Type::kSignedInt && size_ == SizeToSizeType(sizeof(int8_t))) {
280 return static_cast<int8_t>(int_value_);
281 }
282
283 return std::nullopt;
284 }
285
286 template <>
Get() const287 std::optional<int16_t> DataElement::Get<int16_t>() const {
288 if (type_ == Type::kSignedInt && size_ == SizeToSizeType(sizeof(int16_t))) {
289 return static_cast<int16_t>(int_value_);
290 }
291
292 return std::nullopt;
293 }
294
295 template <>
Get() const296 std::optional<int32_t> DataElement::Get<int32_t>() const {
297 if (type_ == Type::kSignedInt && size_ == SizeToSizeType(sizeof(int32_t))) {
298 return static_cast<int32_t>(int_value_);
299 }
300
301 return std::nullopt;
302 ;
303 }
304
305 template <>
Get() const306 std::optional<int64_t> DataElement::Get<int64_t>() const {
307 if (type_ == Type::kSignedInt && size_ == SizeToSizeType(sizeof(int64_t))) {
308 return static_cast<int64_t>(int_value_);
309 }
310
311 return std::nullopt;
312 }
313
314 template <>
Get() const315 std::optional<bool> DataElement::Get<bool>() const {
316 if (type_ != Type::kBoolean) {
317 return std::nullopt;
318 }
319
320 return (int_value_ == 1);
321 }
322
323 template <>
Get() const324 std::optional<std::nullptr_t> DataElement::Get<std::nullptr_t>() const {
325 if (type_ != Type::kNull) {
326 return std::nullopt;
327 }
328
329 return nullptr;
330 }
331
332 template <>
Get() const333 std::optional<bt::DynamicByteBuffer> DataElement::Get<bt::DynamicByteBuffer>()
334 const {
335 if (type_ != Type::kString) {
336 return std::nullopt;
337 }
338
339 return DynamicByteBuffer(bytes_);
340 }
341
342 template <>
Get() const343 std::optional<std::string> DataElement::Get<std::string>() const {
344 if (type_ != Type::kString) {
345 return std::nullopt;
346 }
347
348 return std::string(reinterpret_cast<const char*>(bytes_.data()),
349 bytes_.size());
350 }
351
352 template <>
Get() const353 std::optional<UUID> DataElement::Get<UUID>() const {
354 if (type_ != Type::kUuid) {
355 return std::nullopt;
356 }
357
358 return uuid_;
359 }
360
361 template <>
362 std::optional<std::vector<DataElement>>
Get() const363 DataElement::Get<std::vector<DataElement>>() const {
364 if ((type_ != Type::kSequence) && (type_ != Type::kAlternative)) {
365 return std::nullopt;
366 }
367
368 std::vector<DataElement> aggregate_copy;
369 for (const auto& it : aggregate_) {
370 aggregate_copy.emplace_back(it.Clone());
371 }
372
373 return aggregate_copy;
374 }
375
GetUrl() const376 std::optional<std::string> DataElement::GetUrl() const {
377 if (type_ != Type::kUrl) {
378 return std::nullopt;
379 }
380
381 return std::string(reinterpret_cast<const char*>(bytes_.data()),
382 bytes_.size());
383 }
384
SetVariableSize(size_t length)385 void DataElement::SetVariableSize(size_t length) {
386 if (length <= std::numeric_limits<uint8_t>::max()) {
387 size_ = Size::kNextOne;
388 } else if (length <= std::numeric_limits<uint16_t>::max()) {
389 size_ = Size::kNextTwo;
390 } else {
391 size_ = Size::kNextFour;
392 }
393 }
394
Read(DataElement * elem,const ByteBuffer & buffer)395 size_t DataElement::Read(DataElement* elem, const ByteBuffer& buffer) {
396 if (buffer.size() == 0) {
397 return 0;
398 }
399 Type type_desc = static_cast<Type>(buffer[0] & kTypeMask);
400 Size size_desc = static_cast<Size>(buffer[0] & kDataElementSizeTypeMask);
401 size_t data_bytes = 0;
402 size_t bytes_read = 1;
403 BufferView cursor = buffer.view(bytes_read);
404 switch (size_desc) {
405 case DataElement::Size::kOneByte:
406 case DataElement::Size::kTwoBytes:
407 case DataElement::Size::kFourBytes:
408 case DataElement::Size::kEightBytes:
409 case DataElement::Size::kSixteenBytes:
410 if (type_desc != Type::kNull) {
411 data_bytes = (1 << static_cast<uint8_t>(size_desc));
412 } else {
413 data_bytes = 0;
414 }
415 break;
416 case DataElement::Size::kNextOne: {
417 if (cursor.size() < sizeof(uint8_t)) {
418 return 0;
419 }
420 data_bytes = cursor.To<uint8_t>();
421 bytes_read += sizeof(uint8_t);
422 break;
423 }
424 case DataElement::Size::kNextTwo: {
425 if (cursor.size() < sizeof(uint16_t)) {
426 return 0;
427 }
428 data_bytes = pw::bytes::ConvertOrderFrom(cpp20::endian::big,
429 cursor.To<uint16_t>());
430 bytes_read += sizeof(uint16_t);
431 break;
432 }
433 case DataElement::Size::kNextFour: {
434 if (cursor.size() < sizeof(uint32_t)) {
435 return 0;
436 }
437 data_bytes = pw::bytes::ConvertOrderFrom(cpp20::endian::big,
438 cursor.To<uint32_t>());
439 bytes_read += sizeof(uint32_t);
440 break;
441 }
442 }
443 cursor = buffer.view(bytes_read);
444 if (cursor.size() < data_bytes) {
445 return 0;
446 }
447
448 switch (type_desc) {
449 case Type::kNull: {
450 if (size_desc != Size::kOneByte) {
451 return 0;
452 }
453 elem->Set(nullptr);
454 return bytes_read + data_bytes;
455 }
456 case Type::kBoolean: {
457 if (size_desc != Size::kOneByte) {
458 return 0;
459 }
460 elem->Set(cursor.To<uint8_t>() != 0);
461 return bytes_read + data_bytes;
462 }
463 case Type::kUnsignedInt: {
464 if (size_desc == Size::kOneByte) {
465 elem->Set(cursor.To<uint8_t>());
466 } else if (size_desc == Size::kTwoBytes) {
467 elem->Set(pw::bytes::ConvertOrderFrom(cpp20::endian::big,
468 cursor.To<uint16_t>()));
469 } else if (size_desc == Size::kFourBytes) {
470 elem->Set(pw::bytes::ConvertOrderFrom(cpp20::endian::big,
471 cursor.To<uint32_t>()));
472 } else if (size_desc == Size::kEightBytes) {
473 elem->Set(pw::bytes::ConvertOrderFrom(cpp20::endian::big,
474 cursor.To<uint64_t>()));
475 } else {
476 // TODO(fxbug.dev/42078670): support 128-bit uints
477 // Invalid size.
478 return 0;
479 }
480 return bytes_read + data_bytes;
481 }
482 case Type::kSignedInt: {
483 if (size_desc == Size::kOneByte) {
484 elem->Set(cursor.To<int8_t>());
485 } else if (size_desc == Size::kTwoBytes) {
486 elem->Set(pw::bytes::ConvertOrderFrom(cpp20::endian::big,
487 cursor.To<int16_t>()));
488 } else if (size_desc == Size::kFourBytes) {
489 elem->Set(pw::bytes::ConvertOrderFrom(cpp20::endian::big,
490 cursor.To<int32_t>()));
491 } else if (size_desc == Size::kEightBytes) {
492 elem->Set(pw::bytes::ConvertOrderFrom(cpp20::endian::big,
493 cursor.To<int64_t>()));
494 } else {
495 // TODO(fxbug.dev/42078670): support 128-bit uints
496 // Invalid size.
497 return 0;
498 }
499 return bytes_read + data_bytes;
500 }
501 case Type::kUuid: {
502 if (size_desc == Size::kTwoBytes) {
503 elem->Set(UUID(pw::bytes::ConvertOrderFrom(cpp20::endian::big,
504 cursor.To<uint16_t>())));
505 } else if (size_desc == Size::kFourBytes) {
506 elem->Set(UUID(pw::bytes::ConvertOrderFrom(cpp20::endian::big,
507 cursor.To<uint32_t>())));
508 } else if (size_desc == Size::kSixteenBytes) {
509 StaticByteBuffer<16> uuid_bytes;
510 // UUID expects these to be in little-endian order.
511 cursor.Copy(&uuid_bytes, 0, 16);
512 std::reverse(uuid_bytes.mutable_data(), uuid_bytes.mutable_data() + 16);
513 UUID uuid(uuid_bytes);
514 elem->Set(uuid);
515 } else {
516 return 0;
517 }
518 return bytes_read + data_bytes;
519 }
520 case Type::kString: {
521 if (static_cast<uint8_t>(size_desc) < 5) {
522 return 0;
523 }
524 bt::DynamicByteBuffer str(data_bytes);
525 str.Write(cursor.data(), data_bytes);
526 elem->Set(str);
527 return bytes_read + data_bytes;
528 }
529 case Type::kSequence:
530 case Type::kAlternative: {
531 if (static_cast<uint8_t>(size_desc) < 5) {
532 return 0;
533 }
534 BufferView sequence_buf = cursor.view(0, data_bytes);
535 size_t remaining = data_bytes;
536 PW_DCHECK(sequence_buf.size() == data_bytes);
537
538 std::vector<DataElement> seq;
539 while (remaining > 0) {
540 DataElement next;
541 size_t used = Read(&next, sequence_buf.view(data_bytes - remaining));
542 if (used == 0 || used > remaining) {
543 return 0;
544 }
545 seq.push_back(std::move(next));
546 remaining -= used;
547 }
548 PW_DCHECK(remaining == 0);
549 if (type_desc == Type::kAlternative) {
550 elem->SetAlternative(std::move(seq));
551 } else {
552 elem->Set(std::move(seq));
553 }
554 return bytes_read + data_bytes;
555 }
556 case Type::kUrl: {
557 if (static_cast<uint8_t>(size_desc) < 5) {
558 return 0;
559 }
560 std::string str(cursor.data(), cursor.data() + data_bytes);
561 elem->SetUrl(str);
562 return bytes_read + data_bytes;
563 }
564 }
565 return 0;
566 }
567
WriteSize() const568 size_t DataElement::WriteSize() const {
569 switch (type_) {
570 case Type::kNull:
571 return 1;
572 case Type::kBoolean:
573 return 2;
574 case Type::kUnsignedInt:
575 case Type::kSignedInt:
576 case Type::kUuid:
577 return 1 + (1 << static_cast<uint8_t>(size_));
578 case Type::kString:
579 case Type::kUrl:
580 return 1 + (1 << (static_cast<uint8_t>(size_) - 5)) + bytes_.size();
581 case Type::kSequence:
582 case Type::kAlternative:
583 return 1 + (1 << (static_cast<uint8_t>(size_) - 5)) +
584 AggregateSize(aggregate_);
585 }
586 }
587
Write(MutableByteBuffer * buffer) const588 size_t DataElement::Write(MutableByteBuffer* buffer) const {
589 if (buffer->size() < WriteSize()) {
590 bt_log(TRACE,
591 "sdp",
592 "not enough space in buffer (%zu < %zu)",
593 buffer->size(),
594 WriteSize());
595 return 0;
596 }
597
598 uint8_t type_and_size =
599 static_cast<uint8_t>(type_) | static_cast<uint8_t>(size_);
600 buffer->Write(&type_and_size, 1);
601 size_t pos = 1;
602
603 MutableBufferView cursor = buffer->mutable_view(pos);
604
605 switch (type_) {
606 case Type::kNull: {
607 return pos;
608 }
609 case Type::kBoolean: {
610 uint8_t val = int_value_ != 0 ? 1 : 0;
611 cursor.Write(&val, sizeof(val));
612 pos += 1;
613 return pos;
614 }
615 case Type::kUnsignedInt: {
616 if (size_ == Size::kOneByte) {
617 uint8_t val = static_cast<uint8_t>(uint_value_);
618 cursor.Write(reinterpret_cast<uint8_t*>(&val), sizeof(val));
619 pos += sizeof(val);
620 } else if (size_ == Size::kTwoBytes) {
621 cursor.WriteObj(pw::bytes::ConvertOrderTo(
622 cpp20::endian::big, static_cast<uint16_t>(uint_value_)));
623 pos += sizeof(uint16_t);
624 } else if (size_ == Size::kFourBytes) {
625 cursor.WriteObj(pw::bytes::ConvertOrderTo(
626 cpp20::endian::big, static_cast<uint32_t>(uint_value_)));
627 pos += sizeof(uint32_t);
628 } else if (size_ == Size::kEightBytes) {
629 uint64_t val =
630 pw::bytes::ConvertOrderTo(cpp20::endian::big, uint_value_);
631 cursor.Write(reinterpret_cast<uint8_t*>(&val), sizeof(val));
632 pos += sizeof(val);
633 }
634 return pos;
635 }
636 case Type::kSignedInt: {
637 if (size_ == Size::kOneByte) {
638 int8_t val = static_cast<int8_t>(int_value_);
639 cursor.Write(reinterpret_cast<uint8_t*>(&val), sizeof(val));
640 pos += sizeof(val);
641 } else if (size_ == Size::kTwoBytes) {
642 cursor.WriteObj(pw::bytes::ConvertOrderTo(
643 cpp20::endian::big, static_cast<int16_t>(int_value_)));
644 pos += sizeof(uint16_t);
645 } else if (size_ == Size::kFourBytes) {
646 cursor.WriteObj(pw::bytes::ConvertOrderTo(
647 cpp20::endian::big, static_cast<int32_t>(int_value_)));
648 pos += sizeof(uint32_t);
649 } else if (size_ == Size::kEightBytes) {
650 int64_t val = pw::bytes::ConvertOrderTo(
651 cpp20::endian::big, static_cast<int64_t>(int_value_));
652 cursor.Write(reinterpret_cast<uint8_t*>(&val), sizeof(val));
653 pos += sizeof(val);
654 }
655 return pos;
656 }
657 case Type::kUuid: {
658 size_t written = uuid_.ToBytes(&cursor);
659 PW_DCHECK(written);
660 // SDP is big-endian, so reverse.
661 std::reverse(cursor.mutable_data(), cursor.mutable_data() + written);
662 pos += written;
663 return pos;
664 }
665 case Type::kString:
666 case Type::kUrl: {
667 size_t used = WriteLength(&cursor, bytes_.size());
668 PW_DCHECK(used);
669 pos += used;
670 cursor.Write(bytes_.data(), bytes_.size(), used);
671 pos += bytes_.size();
672 return pos;
673 }
674 case Type::kSequence:
675 case Type::kAlternative: {
676 size_t used = WriteLength(&cursor, AggregateSize(aggregate_));
677 PW_DCHECK(used);
678 pos += used;
679 cursor = cursor.mutable_view(used);
680 for (const auto& elem : aggregate_) {
681 used = elem.Write(&cursor);
682 PW_DCHECK(used);
683 pos += used;
684 cursor = cursor.mutable_view(used);
685 }
686 return pos;
687 }
688 }
689 return 0;
690 }
691
At(size_t idx) const692 const DataElement* DataElement::At(size_t idx) const {
693 if ((type_ != Type::kSequence && type_ != Type::kAlternative) ||
694 (idx >= aggregate_.size())) {
695 return nullptr;
696 }
697 return &aggregate_[idx];
698 }
699
ToString() const700 std::string DataElement::ToString() const {
701 switch (type_) {
702 case Type::kNull:
703 return std::string("Null");
704 case Type::kBoolean:
705 return bt_lib_cpp_string::StringPrintf("Boolean(%s)",
706 int_value_ ? "true" : "false");
707 case Type::kUnsignedInt:
708 return bt_lib_cpp_string::StringPrintf(
709 "UnsignedInt:%zu(%" PRIu64 ")", WriteSize() - 1, uint_value_);
710 case Type::kSignedInt:
711 return bt_lib_cpp_string::StringPrintf(
712 "SignedInt:%zu(%" PRId64 ")", WriteSize() - 1, int_value_);
713 case Type::kUuid:
714 return bt_lib_cpp_string::StringPrintf("UUID(%s)",
715 uuid_.ToString().c_str());
716 case Type::kString:
717 return bt_lib_cpp_string::StringPrintf(
718 "String(%s)", bytes_.Printable(0, bytes_.size()).c_str());
719 case Type::kUrl:
720 return bt_lib_cpp_string::StringPrintf(
721 "Url(%s)", bytes_.Printable(0, bytes_.size()).c_str());
722 case Type::kSequence: {
723 std::string str;
724 for (const auto& it : aggregate_) {
725 str += it.ToString() + " ";
726 }
727 return bt_lib_cpp_string::StringPrintf("Sequence { %s}", str.c_str());
728 }
729 case Type::kAlternative: {
730 std::string str;
731 for (const auto& it : aggregate_) {
732 str += it.ToString() + " ";
733 }
734 return bt_lib_cpp_string::StringPrintf("Alternatives { %s}", str.c_str());
735 }
736 default:
737 bt_log(TRACE,
738 "sdp",
739 "unhandled type (%hhu) in ToString()",
740 static_cast<unsigned char>(type_));
741 // Fallthrough to unknown.
742 }
743
744 return "(unknown)";
745 }
746 } // namespace bt::sdp
747