15
15
* limitations under the License.
16
16
*/
17
17
18
- use crate :: data_model:: { core:: DataModel , objects:: * } ;
19
- use crate :: interaction_model:: core:: OpCode ;
20
- use crate :: tlv:: FromTLV ;
21
- use crate :: transport:: packet:: Packet ;
22
- use crate :: transport:: proto_demux:: ResponseRequired ;
23
18
use crate :: {
24
19
acl:: { AccessReq , Accessor } ,
20
+ data_model:: { core:: DataModel , objects:: * } ,
25
21
error:: * ,
26
22
interaction_model:: {
27
- core:: IMStatusCode ,
23
+ core:: { IMStatusCode , OpCode } ,
28
24
messages:: {
29
25
ib:: { self , DataVersionFilter } ,
30
26
msg:: { self , ReadReq , ReportDataTag :: MoreChunkedMsgs , ReportDataTag :: SupressResponse } ,
31
27
GenericPath ,
32
28
} ,
33
29
Transaction ,
34
30
} ,
35
- tlv:: { self , TLVArray , TLVWriter , TagType , ToTLV } ,
31
+ tlv:: { self , FromTLV , TLVArray , TLVWriter , TagType , ToTLV } ,
32
+ transport:: { packet:: Packet , proto_demux:: ResponseRequired } ,
33
+ utils:: writebuf:: WriteBuf ,
34
+ wb_shrink, wb_unshrink,
36
35
} ;
37
-
36
+ use log :: error ;
38
37
/// Encoder for generating a response to a read request
39
38
pub struct AttrReadEncoder < ' a , ' b , ' c > {
40
39
tw : & ' a mut TLVWriter < ' b , ' c > ,
@@ -205,15 +204,27 @@ impl DataModel {
205
204
206
205
/// Process an array of Attribute Read Requests
207
206
///
208
- /// This API returns whether the read response is chunked or not
207
+ /// When the API returns the chunked read is on, if *resume_from is Some(x) otherwise
208
+ /// the read is complete
209
209
pub ( super ) fn handle_read_attr_array (
210
210
& self ,
211
211
read_req : & ReadReq ,
212
212
trans : & mut Transaction ,
213
- tw : & mut TLVWriter ,
213
+ old_tw : & mut TLVWriter ,
214
214
resume_from : & mut Option < GenericPath > ,
215
215
) -> Result < ( ) , Error > {
216
- let mut attr_encoder = AttrReadEncoder :: new ( tw) ;
216
+ let old_wb = old_tw. get_buf ( ) ;
217
+ // Note, this function may be called from multiple places: a) an actual read
218
+ // request, a b) resumed read request, c) subscribe request or d) resumed subscribe
219
+ // request. Hopefully 18 is sufficient to address all those scenarios.
220
+ //
221
+ // This is the amount of space we reserve for other things to be attached towards
222
+ // the end
223
+ const RESERVE_SIZE : usize = 18 ;
224
+ let mut new_wb = wb_shrink ! ( old_wb, RESERVE_SIZE ) ;
225
+ let mut tw = TLVWriter :: new ( & mut new_wb) ;
226
+
227
+ let mut attr_encoder = AttrReadEncoder :: new ( & mut tw) ;
217
228
if let Some ( filters) = & read_req. dataver_filters {
218
229
attr_encoder. set_data_ver_filters ( filters) ;
219
230
}
@@ -238,20 +249,54 @@ impl DataModel {
238
249
& mut attr_details,
239
250
resume_from,
240
251
) ;
252
+ if result. is_err ( ) {
253
+ break ;
254
+ }
241
255
}
242
- tw. end_container ( ) ?;
256
+ // Now that all the read reports are captured, let's use the old_tw that is
257
+ // the full writebuf, and hopefully as all the necessary space to store this
258
+ wb_unshrink ! ( old_wb, new_wb) ;
259
+ old_tw. end_container ( ) ?; // Finish the AttrReports
260
+
243
261
if result. is_err ( ) {
244
262
// If there was an error, indicate chunking. The resume_read_req would have been
245
- // already populated from in the loop above.
246
- tw. bool ( TagType :: Context ( MoreChunkedMsgs as u8 ) , true ) ?;
247
- return Ok ( ( ) ) ;
263
+ // already populated in the loop above.
264
+ old_tw. bool ( TagType :: Context ( MoreChunkedMsgs as u8 ) , true ) ?;
265
+ } else {
266
+ // A None resume_from indicates no chunking
267
+ * resume_from = None ;
248
268
}
249
269
}
250
- // A None resume_from indicates no chunking
251
- * resume_from = None ;
252
270
Ok ( ( ) )
253
271
}
254
272
273
+ /// Handle a read request
274
+ ///
275
+ /// This could be called from an actual read request or a resumed read request. Subscription
276
+ /// requests do not come to this function.
277
+ /// When the API returns the chunked read is on, if *resume_from is Some(x) otherwise
278
+ /// the read is complete
279
+ pub fn handle_read_req (
280
+ & self ,
281
+ read_req : & ReadReq ,
282
+ trans : & mut Transaction ,
283
+ tw : & mut TLVWriter ,
284
+ resume_from : & mut Option < GenericPath > ,
285
+ ) -> Result < ( OpCode , ResponseRequired ) , Error > {
286
+ tw. start_struct ( TagType :: Anonymous ) ?;
287
+
288
+ self . handle_read_attr_array ( read_req, trans, tw, resume_from) ?;
289
+
290
+ if resume_from. is_none ( ) {
291
+ tw. bool ( TagType :: Context ( SupressResponse as u8 ) , true ) ?;
292
+ // Mark transaction complete, if not chunked
293
+ trans. complete ( ) ;
294
+ }
295
+ tw. end_container ( ) ?;
296
+ Ok ( ( OpCode :: ReportData , ResponseRequired :: Yes ) )
297
+ }
298
+
299
+ /// Handle a resumed read request
255
300
pub fn handle_resume_read (
256
301
& self ,
257
302
resume_read_req : & mut ResumeReadReq ,
@@ -263,16 +308,11 @@ impl DataModel {
263
308
let root = tlv:: get_root_node ( rx_buf) ?;
264
309
let req = ReadReq :: from_tlv ( & root) ?;
265
310
266
- tw. start_struct ( TagType :: Anonymous ) ?;
267
- self . handle_read_attr_array ( & req, trans, tw, & mut resume_read_req. resume_from ) ?;
268
-
269
- if resume_read_req. resume_from . is_none ( ) {
270
- tw. bool ( TagType :: Context ( SupressResponse as u8 ) , true ) ?;
271
- // Mark transaction complete, if not chunked
272
- trans. complete ( ) ;
273
- }
274
- tw. end_container ( ) ?;
311
+ self . handle_read_req ( & req, trans, tw, & mut resume_read_req. resume_from )
312
+ } else {
313
+ // No pending req, is that even possible?
314
+ error ! ( "This shouldn't have happened" ) ;
315
+ Ok ( ( OpCode :: Reserved , ResponseRequired :: No ) )
275
316
}
276
- Ok ( ( OpCode :: ReportData , ResponseRequired :: Yes ) )
277
317
}
278
318
}
0 commit comments