• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_bluetooth_sapphire/internal/host/common/assert.h"
18 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
19 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
20 
21 #pragma clang diagnostic ignored "-Wswitch-enum"
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   BT_ASSERT(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   switch (type) {
48     case DataType::kIncomplete16BitServiceUuids:
49     case DataType::kComplete16BitServiceUuids:
50     case DataType::kServiceData16Bit:
51       return UUIDElemSize::k16Bit;
52     case DataType::kIncomplete32BitServiceUuids:
53     case DataType::kComplete32BitServiceUuids:
54     case DataType::kServiceData32Bit:
55       return UUIDElemSize::k32Bit;
56     case DataType::kIncomplete128BitServiceUuids:
57     case DataType::kComplete128BitServiceUuids:
58     case DataType::kServiceData128Bit:
59       return UUIDElemSize::k128Bit;
60     default:
61       break;
62   };
63 
64   BT_PANIC("called SizeForType with non-UUID DataType %du",
65            static_cast<uint8_t>(type));
66   return UUIDElemSize::k16Bit;
67 }
68 
SupplementDataReader(const ByteBuffer & data)69 SupplementDataReader::SupplementDataReader(const ByteBuffer& data)
70     : is_valid_(true), remaining_(data) {
71   if (!remaining_.size()) {
72     is_valid_ = false;
73     return;
74   }
75 
76   // Do a validity check.
77   BufferView tmp(remaining_);
78   while (tmp.size()) {
79     size_t tlv_len = tmp[0];
80 
81     // A struct can have 0 as its length. In that case its valid to terminate.
82     if (!tlv_len)
83       break;
84 
85     // The full struct includes the length octet itself.
86     size_t struct_size = tlv_len + 1;
87     if (struct_size > tmp.size()) {
88       is_valid_ = false;
89       break;
90     }
91 
92     tmp = tmp.view(struct_size);
93   }
94 }
95 
GetNextField(DataType * out_type,BufferView * out_data)96 bool SupplementDataReader::GetNextField(DataType* out_type,
97                                         BufferView* out_data) {
98   BT_DEBUG_ASSERT(out_type);
99   BT_DEBUG_ASSERT(out_data);
100 
101   if (!HasMoreData())
102     return false;
103 
104   size_t tlv_len = remaining_[0];
105   size_t cur_struct_size = tlv_len + 1;
106   BT_DEBUG_ASSERT(cur_struct_size <= remaining_.size());
107 
108   *out_type = static_cast<DataType>(remaining_[1]);
109   *out_data = remaining_.view(2, tlv_len - 1);
110 
111   // Update |remaining_|.
112   remaining_ = remaining_.view(cur_struct_size);
113   return true;
114 }
115 
HasMoreData() const116 bool SupplementDataReader::HasMoreData() const {
117   if (!is_valid_ || !remaining_.size())
118     return false;
119 
120   // If the buffer is valid and has remaining bytes but the length of the next
121   // segment is zero, then we terminate.
122   return !!remaining_[0];
123 }
124 
SupplementDataWriter(MutableByteBuffer * buffer)125 SupplementDataWriter::SupplementDataWriter(MutableByteBuffer* buffer)
126     : buffer_(buffer), bytes_written_(0u) {
127   BT_DEBUG_ASSERT(buffer_);
128 }
129 
WriteField(DataType type,const ByteBuffer & data)130 bool SupplementDataWriter::WriteField(DataType type, const ByteBuffer& data) {
131   size_t next_size = data.size() + 2;  // 2 bytes for [length][type].
132   if (bytes_written_ + next_size > buffer_->size() || next_size > 255)
133     return false;
134 
135   (*buffer_)[bytes_written_++] = static_cast<uint8_t>(next_size) - 1;
136   (*buffer_)[bytes_written_++] = static_cast<uint8_t>(type);
137 
138   // Get a view into the offset we want to write to.
139   auto target = buffer_->mutable_view(bytes_written_);
140 
141   // Copy the data into the view.
142   data.Copy(&target);
143 
144   bytes_written_ += data.size();
145 
146   return true;
147 }
148 
149 }  // namespace bt
150