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