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/common/supplement_data.h"
16
17 #include <pw_preprocessor/compiler.h>
18
19 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
22
23 namespace bt {
24
ParseUuids(const BufferView & data,UUIDElemSize uuid_size,UuidFunction func)25 bool ParseUuids(const BufferView& data,
26 UUIDElemSize uuid_size,
27 UuidFunction func) {
28 PW_CHECK(func);
29
30 if (data.size() % uuid_size) {
31 return false;
32 }
33
34 size_t uuid_count = data.size() / uuid_size;
35 for (size_t i = 0; i < uuid_count; i++) {
36 const BufferView uuid_bytes(data.data() + (i * uuid_size), uuid_size);
37 UUID uuid;
38 if (!UUID::FromBytes(uuid_bytes, &uuid) || !func(uuid)) {
39 return false;
40 }
41 }
42
43 return true;
44 }
45
SizeForType(DataType type)46 UUIDElemSize SizeForType(DataType type) {
47 PW_MODIFY_DIAGNOSTICS_PUSH();
48 PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
49 switch (type) {
50 case DataType::kIncomplete16BitServiceUuids:
51 case DataType::kComplete16BitServiceUuids:
52 case DataType::kServiceData16Bit:
53 return UUIDElemSize::k16Bit;
54 case DataType::kIncomplete32BitServiceUuids:
55 case DataType::kComplete32BitServiceUuids:
56 case DataType::kServiceData32Bit:
57 return UUIDElemSize::k32Bit;
58 case DataType::kIncomplete128BitServiceUuids:
59 case DataType::kComplete128BitServiceUuids:
60 case DataType::kServiceData128Bit:
61 return UUIDElemSize::k128Bit;
62 default:
63 break;
64 };
65 PW_MODIFY_DIAGNOSTICS_POP();
66
67 BT_PANIC("called SizeForType with non-UUID DataType %du",
68 static_cast<uint8_t>(type));
69 return UUIDElemSize::k16Bit;
70 }
71
SupplementDataReader(const ByteBuffer & data)72 SupplementDataReader::SupplementDataReader(const ByteBuffer& data)
73 : is_valid_(true), remaining_(data) {
74 if (!remaining_.size()) {
75 is_valid_ = false;
76 return;
77 }
78
79 // Do a validity check.
80 BufferView tmp(remaining_);
81 while (tmp.size()) {
82 size_t tlv_len = tmp[0];
83
84 // A struct can have 0 as its length. In that case its valid to terminate.
85 if (!tlv_len)
86 break;
87
88 // The full struct includes the length octet itself.
89 size_t struct_size = tlv_len + 1;
90 if (struct_size > tmp.size()) {
91 is_valid_ = false;
92 break;
93 }
94
95 tmp = tmp.view(struct_size);
96 }
97 }
98
GetNextField(DataType * out_type,BufferView * out_data)99 bool SupplementDataReader::GetNextField(DataType* out_type,
100 BufferView* out_data) {
101 PW_DCHECK(out_type);
102 PW_DCHECK(out_data);
103
104 if (!HasMoreData())
105 return false;
106
107 size_t tlv_len = remaining_[0];
108 size_t cur_struct_size = tlv_len + 1;
109 PW_DCHECK(cur_struct_size <= remaining_.size());
110
111 *out_type = static_cast<DataType>(remaining_[1]);
112 *out_data = remaining_.view(2, tlv_len - 1);
113
114 // Update |remaining_|.
115 remaining_ = remaining_.view(cur_struct_size);
116 return true;
117 }
118
HasMoreData() const119 bool SupplementDataReader::HasMoreData() const {
120 if (!is_valid_ || !remaining_.size())
121 return false;
122
123 // If the buffer is valid and has remaining bytes but the length of the next
124 // segment is zero, then we terminate.
125 return !!remaining_[0];
126 }
127
SupplementDataWriter(MutableByteBuffer * buffer)128 SupplementDataWriter::SupplementDataWriter(MutableByteBuffer* buffer)
129 : buffer_(buffer), bytes_written_(0u) {
130 PW_DCHECK(buffer_);
131 }
132
WriteField(DataType type,const ByteBuffer & data)133 bool SupplementDataWriter::WriteField(DataType type, const ByteBuffer& data) {
134 size_t next_size = data.size() + 2; // 2 bytes for [length][type].
135 if (bytes_written_ + next_size > buffer_->size() || next_size > 255)
136 return false;
137
138 (*buffer_)[bytes_written_++] = static_cast<uint8_t>(next_size) - 1;
139 (*buffer_)[bytes_written_++] = static_cast<uint8_t>(type);
140
141 // Get a view into the offset we want to write to.
142 auto target = buffer_->mutable_view(bytes_written_);
143
144 // Copy the data into the view.
145 data.Copy(&target);
146
147 bytes_written_ += data.size();
148
149 return true;
150 }
151
152 } // namespace bt
153