• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! This module extracts the common logic in filtering attributes by type +
2 //! length, used in READ_BY_TYPE_REQ and READ_BY_GROUP_TYPE_REQ
3 
4 use crate::{
5     core::uuid::Uuid,
6     gatt::server::att_database::{AttAttribute, StableAttDatabase},
7     packets::{AttAttributeDataChild, AttErrorCode, Serializable},
8 };
9 
10 use super::truncate_att_data::truncate_att_data;
11 
12 /// An attribute and the value
13 #[derive(Debug, PartialEq, Eq)]
14 pub struct AttributeWithValue {
15     /// The attribute
16     pub attr: AttAttribute,
17     pub value: AttAttributeDataChild,
18 }
19 
20 /// Takes a StableAttDatabase, a range of handles, a target type, and a
21 /// size limit.
22 ///
23 /// Returns an iterator of attributes in the range and matching the type,
24 /// with the max number of elements such that each attribute has the same
25 /// size.
26 ///
27 /// Attributes are truncated to the attr_size limit before size comparison.
28 /// If an error occurs while reading, do not output further attributes.
filter_read_attributes_by_size_type( db: &impl StableAttDatabase, attrs: impl Iterator<Item = AttAttribute>, target: Uuid, size_limit: usize, ) -> Result<impl Iterator<Item = AttributeWithValue>, AttErrorCode>29 pub async fn filter_read_attributes_by_size_type(
30     db: &impl StableAttDatabase,
31     attrs: impl Iterator<Item = AttAttribute>,
32     target: Uuid,
33     size_limit: usize,
34 ) -> Result<impl Iterator<Item = AttributeWithValue>, AttErrorCode> {
35     let target_attrs = attrs.filter(|attr| attr.type_ == target);
36 
37     let mut out = vec![];
38     let mut curr_elem_size = None;
39 
40     for attr @ AttAttribute { handle, .. } in target_attrs {
41         match db.read_attribute(handle).await {
42             Ok(value) => {
43                 let value = truncate_att_data(value, size_limit);
44                 let value_size = value.size_in_bits().unwrap_or(0);
45                 if let Some(curr_elem_size) = curr_elem_size {
46                     if curr_elem_size != value_size {
47                         // no more attributes of the same size
48                         break;
49                     }
50                 } else {
51                     curr_elem_size = Some(value_size)
52                 }
53 
54                 out.push(AttributeWithValue { attr, value });
55             }
56             Err(err) => {
57                 if out.is_empty() {
58                     return Err(err);
59                 }
60                 break;
61             }
62         }
63     }
64 
65     Ok(out.into_iter())
66 }
67 
68 #[cfg(test)]
69 mod test {
70     use super::*;
71 
72     use crate::{
73         core::uuid::Uuid,
74         gatt::{
75             ids::AttHandle,
76             server::{
77                 att_database::{AttAttribute, AttDatabase, StableAttDatabase},
78                 gatt_database::AttPermissions,
79                 test::test_att_db::TestAttDatabase,
80             },
81         },
82         packets::AttAttributeDataChild,
83     };
84 
85     const UUID: Uuid = Uuid::new(1234);
86     const ANOTHER_UUID: Uuid = Uuid::new(2345);
87 
88     #[test]
test_single_matching_attr()89     fn test_single_matching_attr() {
90         // arrange
91         let db = TestAttDatabase::new(vec![(
92             AttAttribute {
93                 handle: AttHandle(3),
94                 type_: UUID,
95                 permissions: AttPermissions::READABLE,
96             },
97             vec![4, 5],
98         )]);
99 
100         // act
101         let response = tokio_test::block_on(filter_read_attributes_by_size_type(
102             &db,
103             db.list_attributes().into_iter(),
104             UUID,
105             31,
106         ))
107         .unwrap();
108 
109         // assert
110         assert_eq!(
111             response.collect::<Vec<_>>(),
112             vec![AttributeWithValue {
113                 attr: db.find_attribute(AttHandle(3)).unwrap(),
114                 value: AttAttributeDataChild::RawData([4, 5].into())
115             }]
116         )
117     }
118 
119     #[test]
test_skip_mismatching_attrs()120     fn test_skip_mismatching_attrs() {
121         // arrange
122         let db = TestAttDatabase::new(vec![
123             (
124                 AttAttribute {
125                     handle: AttHandle(3),
126                     type_: UUID,
127                     permissions: AttPermissions::READABLE,
128                 },
129                 vec![4, 5],
130             ),
131             (
132                 AttAttribute {
133                     handle: AttHandle(5),
134                     type_: ANOTHER_UUID,
135                     permissions: AttPermissions::READABLE,
136                 },
137                 vec![5, 6],
138             ),
139             (
140                 AttAttribute {
141                     handle: AttHandle(6),
142                     type_: UUID,
143                     permissions: AttPermissions::READABLE,
144                 },
145                 vec![6, 7],
146             ),
147         ]);
148 
149         // act
150         let response = tokio_test::block_on(filter_read_attributes_by_size_type(
151             &db,
152             db.list_attributes().into_iter(),
153             UUID,
154             31,
155         ))
156         .unwrap();
157 
158         // assert
159         assert_eq!(
160             response.collect::<Vec<_>>(),
161             vec![
162                 AttributeWithValue {
163                     attr: db.find_attribute(AttHandle(3)).unwrap(),
164                     value: AttAttributeDataChild::RawData([4, 5].into())
165                 },
166                 AttributeWithValue {
167                     attr: db.find_attribute(AttHandle(6)).unwrap(),
168                     value: AttAttributeDataChild::RawData([6, 7].into())
169                 }
170             ]
171         );
172     }
173 
174     #[test]
test_stop_once_length_changes()175     fn test_stop_once_length_changes() {
176         // arrange
177         let db = TestAttDatabase::new(vec![
178             (
179                 AttAttribute {
180                     handle: AttHandle(3),
181                     type_: UUID,
182                     permissions: AttPermissions::READABLE,
183                 },
184                 vec![4, 5],
185             ),
186             (
187                 AttAttribute {
188                     handle: AttHandle(5),
189                     type_: UUID,
190                     permissions: AttPermissions::READABLE,
191                 },
192                 vec![5],
193             ),
194             (
195                 AttAttribute {
196                     handle: AttHandle(6),
197                     type_: UUID,
198                     permissions: AttPermissions::READABLE,
199                 },
200                 vec![6, 7],
201             ),
202         ]);
203 
204         // act
205         let response = tokio_test::block_on(filter_read_attributes_by_size_type(
206             &db,
207             db.list_attributes().into_iter(),
208             UUID,
209             31,
210         ))
211         .unwrap();
212 
213         // assert
214         assert_eq!(
215             response.collect::<Vec<_>>(),
216             vec![AttributeWithValue {
217                 attr: db.find_attribute(AttHandle(3)).unwrap(),
218                 value: AttAttributeDataChild::RawData([4, 5].into())
219             },]
220         );
221     }
222 
223     #[test]
test_truncate_to_mtu()224     fn test_truncate_to_mtu() {
225         // arrange: attr with data of length 3
226         let db = TestAttDatabase::new(vec![(
227             AttAttribute {
228                 handle: AttHandle(3),
229                 type_: UUID,
230                 permissions: AttPermissions::READABLE,
231             },
232             vec![4, 5, 6],
233         )]);
234 
235         // act: read the attribute with max_size = 2
236         let response = tokio_test::block_on(filter_read_attributes_by_size_type(
237             &db,
238             db.list_attributes().into_iter(),
239             UUID,
240             2,
241         ))
242         .unwrap();
243 
244         // assert: the length of the read attribute is 2
245         assert_eq!(
246             response.collect::<Vec<_>>(),
247             vec![AttributeWithValue {
248                 attr: db.find_attribute(AttHandle(3)).unwrap(),
249                 value: AttAttributeDataChild::RawData([4, 5].into())
250             },]
251         );
252     }
253 
254     #[test]
test_no_results()255     fn test_no_results() {
256         // arrange: an empty database
257         let db = TestAttDatabase::new(vec![]);
258 
259         // act
260         let response = tokio_test::block_on(filter_read_attributes_by_size_type(
261             &db,
262             db.list_attributes().into_iter(),
263             UUID,
264             31,
265         ))
266         .unwrap();
267 
268         // assert: no results
269         assert_eq!(response.count(), 0)
270     }
271 
272     #[test]
test_read_failure_on_first_attr()273     fn test_read_failure_on_first_attr() {
274         // arrange: put a non-readable attribute in the db with the right type
275         let db = TestAttDatabase::new(vec![(
276             AttAttribute {
277                 handle: AttHandle(3),
278                 type_: UUID,
279                 permissions: AttPermissions::empty(),
280             },
281             vec![4, 5, 6],
282         )]);
283 
284         // act
285         let response = tokio_test::block_on(filter_read_attributes_by_size_type(
286             &db,
287             db.list_attributes().into_iter(),
288             UUID,
289             31,
290         ));
291 
292         // assert: got READ_NOT_PERMITTED
293         assert!(matches!(response, Err(AttErrorCode::READ_NOT_PERMITTED)));
294     }
295 
296     #[test]
test_read_failure_on_subsequent_attr()297     fn test_read_failure_on_subsequent_attr() {
298         // arrange: put a non-readable attribute in the db with the right
299         // type
300         let db = TestAttDatabase::new(vec![
301             (
302                 AttAttribute {
303                     handle: AttHandle(3),
304                     type_: UUID,
305                     permissions: AttPermissions::READABLE,
306                 },
307                 vec![4, 5, 6],
308             ),
309             (
310                 AttAttribute {
311                     handle: AttHandle(4),
312                     type_: UUID,
313                     permissions: AttPermissions::empty(),
314                 },
315                 vec![5, 6, 7],
316             ),
317             (
318                 AttAttribute {
319                     handle: AttHandle(5),
320                     type_: UUID,
321                     permissions: AttPermissions::READABLE,
322                 },
323                 vec![8, 9, 10],
324             ),
325         ]);
326 
327         // act
328         let response = tokio_test::block_on(filter_read_attributes_by_size_type(
329             &db,
330             db.list_attributes().into_iter(),
331             UUID,
332             31,
333         ))
334         .unwrap();
335 
336         // assert: we reply with the first attribute, but not the second or third
337         // (since we stop on the first failure)
338         assert_eq!(
339             response.collect::<Vec<_>>(),
340             vec![AttributeWithValue {
341                 attr: db.find_attribute(AttHandle(3)).unwrap(),
342                 value: AttAttributeDataChild::RawData([4, 5, 6].into())
343             },]
344         );
345     }
346 
347     #[test]
test_skip_unreadable_mismatching_attr()348     fn test_skip_unreadable_mismatching_attr() {
349         // arrange: put a non-readable attribute in the db with the wrong type
350         // between two attributes of interest
351         let db = TestAttDatabase::new(vec![
352             (
353                 AttAttribute {
354                     handle: AttHandle(3),
355                     type_: UUID,
356                     permissions: AttPermissions::READABLE,
357                 },
358                 vec![4, 5, 6],
359             ),
360             (
361                 AttAttribute {
362                     handle: AttHandle(4),
363                     type_: ANOTHER_UUID,
364                     permissions: AttPermissions::empty(),
365                 },
366                 vec![5, 6, 7],
367             ),
368             (
369                 AttAttribute {
370                     handle: AttHandle(5),
371                     type_: UUID,
372                     permissions: AttPermissions::READABLE,
373                 },
374                 vec![6, 7, 8],
375             ),
376         ]);
377 
378         // act
379         let response = tokio_test::block_on(filter_read_attributes_by_size_type(
380             &db,
381             db.list_attributes().into_iter(),
382             UUID,
383             31,
384         ))
385         .unwrap();
386 
387         // assert: we reply with the first and third attributes, but not the second
388         assert_eq!(
389             response.collect::<Vec<_>>(),
390             vec![
391                 AttributeWithValue {
392                     attr: db.find_attribute(AttHandle(3)).unwrap(),
393                     value: AttAttributeDataChild::RawData([4, 5, 6].into())
394                 },
395                 AttributeWithValue {
396                     attr: db.find_attribute(AttHandle(5)).unwrap(),
397                     value: AttAttributeDataChild::RawData([6, 7, 8].into())
398                 }
399             ]
400         );
401     }
402 }
403