1use fidl_constants::{
6 ALLOC_ABSENT_U32, ALLOC_ABSENT_U64, ALLOC_PRESENT_U32, ALLOC_PRESENT_U64, MAGIC_NUMBER_INITIAL,
7};
8use fidl_data_zx::{DEFAULT_CHANNEL_RIGHTS, ObjType as ObjectType, Rights};
9
10use std::collections::HashMap;
11
12use crate::error::{Error, Result};
13use crate::handle::*;
14use crate::library;
15use crate::transaction::*;
16use crate::util::*;
17use crate::value::Value;
18
19type DeferCallback<'n, 't> = dyn FnOnce(&mut EncodeBuffer<'n>, RecursionCounter) -> Result<()> + 't;
21
22fn combine_calls<'n: 't, 't>(calls: Vec<Box<DeferCallback<'n, 't>>>) -> Box<DeferCallback<'n, 't>> {
24 Box::new(move |this, counter| {
25 for call in calls {
26 call(this, counter)?;
27 }
28
29 Ok(())
30 })
31}
32
33enum HandleType<'s> {
34 ClientEnd(&'s str),
35 ServerEnd(&'s str),
36 Bare,
37}
38
39struct EncodeBuffer<'n> {
41 ns: &'n library::Namespace,
42 bytes: Vec<u8>,
43 handles: Vec<HandleDisposition>,
44}
45
46impl<'n> EncodeBuffer<'n> {
47 fn align_8(&mut self) {
49 self.bytes
50 .extend(std::iter::repeat(0u8).take(alignment_padding_for_size(self.bytes.len())));
51 }
52
53 fn encode_transaction<'n_i: 't, 't>(
54 ns: &'n_i library::Namespace,
55 txid: u32,
56 protocol_name: &str,
57 direction: Direction,
58 method_name: &str,
59 value: Value,
60 ) -> Result<(Vec<u8>, Vec<HandleDisposition>)> {
61 let mut buf = EncodeBuffer { ns, bytes: Vec::new(), handles: Vec::new() };
62
63 let protocol = match ns.lookup(protocol_name)? {
64 library::LookupResult::Protocol(i) => Ok(i),
65 _ => Err(Error::LibraryError(format!("Could not find protocol '{}'.", protocol_name))),
66 }?;
67
68 let method = protocol.methods.get(method_name).ok_or_else(|| {
69 Error::LibraryError(format!(
70 "Could not find method '{}' on protocol '{}'",
71 method_name, protocol_name
72 ))
73 })?;
74
75 let (ty, has) = match direction {
76 Direction::Request => {
77 if !method.has_response && txid != 0 {
78 return Err(Error::EncodeError(
79 "Non-zero transaction ID for one-way method.".to_owned(),
80 ));
81 }
82 (method.request.as_ref(), method.has_request)
83 }
84 Direction::Response => (method.response.as_ref(), method.has_response),
85 };
86
87 let dynamic_flags =
88 if method.strict { DynamicFlags::empty() } else { DynamicFlags::FLEXIBLE };
89
90 if !has {
91 return Err(Error::LibraryError(format!(
92 "Method '{}' on protocol '{}' has no {}",
93 method_name,
94 protocol_name,
95 direction.to_string()
96 )));
97 }
98
99 buf.bytes.extend(&txid.to_le_bytes());
100 buf.bytes.extend(&AtRestFlags::USE_V2_WIRE_FORMAT.bits().to_le_bytes());
101 buf.bytes.push(dynamic_flags.bits());
102 buf.bytes.push(MAGIC_NUMBER_INITIAL);
103 buf.bytes.extend(&method.ordinal.to_le_bytes());
104
105 if let Some(ty) = ty {
106 buf.encode_type(ty, value)?(&mut buf, RecursionCounter::new())?
107 } else if !matches!(value, Value::Null) {
108 return Err(Error::EncodeError("Value must be null.".to_owned()));
109 } else {
110 };
111 buf.align_8();
112 Ok((buf.bytes, buf.handles))
113 }
114
115 fn encode_struct_nonnull<'t>(
116 &mut self,
117 st: &'n library::Struct,
118 value: Value,
119 start_offset: usize,
120 ) -> Result<Box<DeferCallback<'n, 't>>>
121 where
122 'n: 't,
123 {
124 let start_offset = self.bytes.len() - start_offset;
125
126 let values = match value {
127 Value::Object(s) => Ok(s),
128 _ => Err(Error::EncodeError("Value is not a struct.".to_owned())),
129 }?;
130
131 let mut values = {
132 let mut map = HashMap::with_capacity(values.len());
133
134 for (k, v) in values {
135 map.insert(k, v);
136 }
137
138 map
139 };
140
141 let mut calls = Vec::new();
142
143 for member in &st.members {
144 let value = values.remove(&member.name).unwrap_or(Value::Null);
145 self.bytes.extend(
146 std::iter::repeat(0u8).take(member.offset - (self.bytes.len() - start_offset)),
147 );
148 calls.push(self.encode_type(&member.ty, value)?);
149 }
150
151 if let Some((name, _)) = values.into_iter().next() {
152 Err(Error::EncodeError(format!("Unknown struct member: {}", name)))
153 } else {
154 self.bytes
155 .extend(std::iter::repeat(0u8).take(st.size - (self.bytes.len() - start_offset)));
156 Ok(combine_calls(calls))
157 }
158 }
159
160 fn encode_type<'t>(
161 &mut self,
162 ty: &'n library::Type,
163 value: Value,
164 ) -> Result<Box<DeferCallback<'n, 't>>>
165 where
166 'n: 't,
167 {
168 use library::Type::*;
169
170 match ty {
171 Unknown(_) | UnknownString(_) => {
172 return Err(Error::LibraryError("Unknown type".to_owned()));
173 }
174 Bool => self.encode_raw(if bool::try_from(value)? { &[1u8] } else { &[0u8] }),
175 U8 => self.encode_raw(&u8::try_from(value)?.to_le_bytes()),
176 U16 => self.encode_raw(&u16::try_from(value)?.to_le_bytes()),
177 U32 => self.encode_raw(&u32::try_from(value)?.to_le_bytes()),
178 U64 => self.encode_raw(&u64::try_from(value)?.to_le_bytes()),
179 I8 => self.encode_raw(&i8::try_from(value)?.to_le_bytes()),
180 I16 => self.encode_raw(&i16::try_from(value)?.to_le_bytes()),
181 I32 => self.encode_raw(&i32::try_from(value)?.to_le_bytes()),
182 I64 => self.encode_raw(&i64::try_from(value)?.to_le_bytes()),
183 F32 => self.encode_raw(&f32::try_from(value)?.to_le_bytes()),
184 F64 => self.encode_raw(&f64::try_from(value)?.to_le_bytes()),
185 Array(ty, size) => self.encode_array(ty, *size, value),
186 Vector { ty, nullable, element_count } => {
187 self.encode_vector(ty, *nullable, value, *element_count)
188 }
189 String { nullable, byte_count } => self.encode_string(*nullable, value, *byte_count),
190 Handle { object_type, rights, nullable } => {
191 self.encode_handle(*object_type, *rights, HandleType::Bare, *nullable, value)
192 }
193 FrameworkError => self.encode_raw(&[0, 0, 0, 0]),
194 Endpoint { role, protocol, rights, nullable } => self.encode_handle(
195 ObjectType::Channel,
196 Some(rights.unwrap_or(DEFAULT_CHANNEL_RIGHTS)),
197 match role {
198 library::EndpointRole::Client => HandleType::ClientEnd(protocol),
199 library::EndpointRole::Server => HandleType::ServerEnd(protocol),
200 },
201 *nullable,
202 value,
203 ),
204 Identifier { name, nullable } => self.encode_identifier(name.clone(), *nullable, value),
205 }
206 }
207
208 fn encode_raw<'t>(&mut self, data: &[u8]) -> Result<Box<DeferCallback<'n, 't>>>
209 where
210 'n: 't,
211 {
212 self.bytes.extend(data);
213 Ok(Box::new(|_, _| Ok(())))
214 }
215
216 fn encode_array<'t>(
217 &mut self,
218 ty: &'n library::Type,
219 size: usize,
220 value: Value,
221 ) -> Result<Box<DeferCallback<'n, 't>>>
222 where
223 'n: 't,
224 {
225 let values = if let Value::List(v) = value {
226 Ok(v)
227 } else {
228 Err(Error::EncodeError("Expected a list".to_owned()))
229 }?;
230
231 if values.len() != size {
232 return Err(Error::EncodeError(format!("Expected list of length {}", size)));
233 }
234
235 let mut calls = Vec::with_capacity(size);
236
237 for value in values {
238 calls.push(self.encode_type(ty, value)?);
239 }
240
241 Ok(combine_calls(calls))
242 }
243
244 fn encode_vector<'t>(
245 &mut self,
246 ty: &'n library::Type,
247 nullable: bool,
248 value: Value,
249 element_count: Option<usize>,
250 ) -> Result<Box<DeferCallback<'n, 't>>>
251 where
252 'n: 't,
253 {
254 let values = match (value, nullable) {
255 (Value::Null, true) => Ok(None),
256 (Value::Null, false) => {
257 Err(Error::EncodeError("Got null for non-nullable list".to_owned()))
258 }
259 (Value::List(v), _) => Ok(Some(v)),
260 _ => Err(Error::EncodeError("Expected a list".to_owned())),
261 }?;
262
263 if let Some(values) = values {
264 if element_count.map(|x| x < values.len()).unwrap_or(false) {
265 return Err(Error::EncodeError("Vector exceeded max size".to_owned()));
266 }
267
268 self.bytes.extend(&(values.len() as u64).to_le_bytes());
269 self.bytes.extend(&ALLOC_PRESENT_U64.to_le_bytes());
270 Ok(Box::new(move |this, counter| {
271 let counter = counter.next()?;
272 let mut calls = Vec::with_capacity(values.len());
273
274 for value in values {
275 calls.push(this.encode_type(ty, value)?);
276 }
277
278 this.align_8();
279
280 for call in calls {
281 call(this, counter)?;
282 }
283
284 Ok(())
285 }))
286 } else {
287 self.bytes.extend(std::iter::repeat(0u8).take(16));
288 Ok(Box::new(|_, _| Ok(())))
289 }
290 }
291
292 fn encode_string<'t>(
293 &mut self,
294 nullable: bool,
295 value: Value,
296 byte_count: Option<usize>,
297 ) -> Result<Box<DeferCallback<'n, 't>>>
298 where
299 'n: 't,
300 {
301 let string = match (value, nullable) {
302 (Value::Null, true) => Ok(None),
303 (Value::Null, false) => {
304 Err(Error::EncodeError("Got null for non-nullable string".to_owned()))
305 }
306 (Value::String(s), _) => Ok(Some(s)),
307 _ => Err(Error::EncodeError("Expected a string".to_owned())),
308 }?;
309
310 if let Some(string) = string {
311 if byte_count.map(|x| x < string.len()).unwrap_or(false) {
312 return Err(Error::EncodeError("String exceeded max size".to_owned()));
313 }
314
315 self.bytes.extend(&(string.len() as u64).to_le_bytes());
316 self.bytes.extend(&ALLOC_PRESENT_U64.to_le_bytes());
317 Ok(Box::new(move |this, counter| {
318 let _counter = counter.next()?;
319 this.bytes.extend(string.as_bytes());
320 this.align_8();
321 Ok(())
322 }))
323 } else {
324 self.bytes.extend(std::iter::repeat(0u8).take(16));
325 Ok(Box::new(|_, _| Ok(())))
326 }
327 }
328
329 fn encode_handle<'t>(
330 &mut self,
331 object_type: ObjectType,
332 rights: Option<Rights>,
333 expect: HandleType<'_>,
334 nullable: bool,
335 value: Value,
336 ) -> Result<Box<DeferCallback<'n, 't>>>
337 where
338 'n: 't,
339 {
340 let (handle, handle_rights) = match (value, nullable, expect) {
341 (Value::Null, true, _) => Ok((None, None)),
342 (Value::Handle(h, _, _), true, _) if h.is_invalid() => Ok((None, None)),
343 (Value::ServerEnd(h, _, _), true, _) if h.is_invalid() => Ok((None, None)),
344 (Value::ClientEnd(h, _, _), true, _) if h.is_invalid() => Ok((None, None)),
345 (Value::Handle(h, _, _), false, _) if h.is_invalid() => {
346 Err(Error::EncodeError("Got invalid handle for non-nullable handle".to_owned()))
347 }
348 (Value::ServerEnd(h, _, _), false, _) if h.is_invalid() => {
349 Err(Error::EncodeError("Got invalid handle for non-nullable handle".to_owned()))
350 }
351 (Value::ClientEnd(h, _, _), false, _) if h.is_invalid() => {
352 Err(Error::EncodeError("Got invalid handle for non-nullable handle".to_owned()))
353 }
354 (Value::Null, false, _) => {
355 Err(Error::EncodeError("Got null for non-nullable handle".to_owned()))
356 }
357 (Value::Handle(h, s, r), _, _) => {
358 if s != object_type && s != ObjectType::None {
359 Err(Error::EncodeError(format!(
360 "Expected object type {object_type:?} got {s:?}"
361 )))
362 } else {
363 Ok((Some(h), r))
364 }
365 }
366 (Value::ServerEnd(h, s, r), _, HandleType::ServerEnd(expect))
367 | (Value::ClientEnd(h, s, r), _, HandleType::ClientEnd(expect)) => {
368 if expect != s {
369 Err(Error::EncodeError(format!(
370 "Expected endpoint for protocol {expect}, got one for {s}"
371 )))
372 } else if object_type != ObjectType::Channel {
373 Err(Error::EncodeError(format!(
374 "Expected object type {object_type:?} got channel for protocol {s}"
375 )))
376 } else {
377 Ok((Some(h.into()), r))
378 }
379 }
380 (Value::ServerEnd(_, s, _), _, HandleType::ClientEnd(expect))
381 | (Value::ClientEnd(_, s, _), _, HandleType::ServerEnd(expect)) => {
382 if expect != s {
383 Err(Error::EncodeError(format!(
384 "Expected endpoint for protocol {expect}, got one for {s}"
385 )))
386 } else if object_type != ObjectType::Channel {
387 Err(Error::EncodeError(format!(
388 "Expected object type {object_type:?} got channel for protocol {s}"
389 )))
390 } else {
391 Err(Error::EncodeError(format!("Got wrong end of channel for {expect}")))
392 }
393 }
394 (Value::ServerEnd(h, s, r), _, HandleType::Bare)
395 | (Value::ClientEnd(h, s, r), _, HandleType::Bare) => {
396 if object_type != ObjectType::Channel {
397 Err(Error::EncodeError(format!(
398 "Expected object type {object_type:?} got channel for protocol {s}"
399 )))
400 } else {
401 Ok((Some(h.into()), r))
402 }
403 }
404 _ => Err(Error::EncodeError("Expected a handle".to_owned())),
405 }?;
406
407 let encoded_rights = match (handle_rights, rights) {
408 (Some(_), Some(rights)) => rights,
409 (Some(handle_rights), None) => handle_rights,
410 (None, Some(rights)) => rights,
411 (None, None) => Rights::SAME_RIGHTS,
412 };
413
414 if let Some(handle) = handle {
415 self.bytes.extend(&ALLOC_PRESENT_U32.to_le_bytes());
416 Ok(Box::new(move |this, _| {
417 this.handles.push(HandleDisposition::move_op(handle, object_type, encoded_rights));
418 Ok(())
419 }))
420 } else {
421 self.bytes.extend(&ALLOC_ABSENT_U32.to_le_bytes());
422 Ok(Box::new(|_, _| Ok(())))
423 }
424 }
425
426 fn encode_identifier<'t>(
427 &mut self,
428 name: String,
429 nullable: bool,
430 value: Value,
431 ) -> Result<Box<DeferCallback<'n, 't>>>
432 where
433 'n: 't,
434 {
435 use library::LookupResult::*;
436 match (self.ns.lookup(&name)?, nullable) {
437 (Bits(b), false) => self.encode_bits(b, value),
438 (Enum(e), false) => self.encode_enum(e, value),
439 (Table(t), false) => self.encode_table(t, value),
440 (Struct(s), nullable) => self.encode_struct(s, nullable, value),
441 (Union(u), nullable) => self.encode_union(u, nullable, value),
442 (Protocol(_), _) => Err(Error::LibraryError(format!(
443 "Protocol names cannot be used as identifiers: {}",
444 name
445 ))),
446 _ => Err(Error::LibraryError(format!("Type {} shouldn't be nullable", name))),
447 }
448 }
449
450 fn encode_bits<'t>(
451 &mut self,
452 bits: &'n library::Bits,
453 value: Value,
454 ) -> Result<Box<DeferCallback<'n, 't>>>
455 where
456 'n: 't,
457 {
458 let value = match value {
459 Value::Bits(name, inner) => {
460 if name == bits.name {
461 *inner
462 } else {
463 return Err(Error::EncodeError(format!(
464 "Expected {}, got {}",
465 bits.name, name
466 )));
467 }
468 }
469 _ => value,
470 };
471
472 let data = u64::try_from(&value).unwrap_or(0);
475
476 if bits.strict && data & !bits.mask != 0 {
477 Err(Error::EncodeError(format!("Invalid bits set on {}", bits.name)))
478 } else {
479 self.encode_type(&bits.ty, value)
480 }
481 }
482
483 fn encode_enum<'t>(
484 &mut self,
485 en: &'n library::Enum,
486 value: Value,
487 ) -> Result<Box<DeferCallback<'n, 't>>>
488 where
489 'n: 't,
490 {
491 let value = match value {
492 Value::Enum(name, inner) => {
493 if name == en.name {
494 *inner
495 } else {
496 return Err(Error::EncodeError(format!("Expected {}, got {}", en.name, name)));
497 }
498 }
499 _ => value,
500 };
501
502 for item in &en.members {
503 if !en.strict || item.value.cast_equals(&value) {
504 return self.encode_type(&en.ty, value);
505 }
506 }
507
508 Err(Error::EncodeError("Invalid enum variant".to_owned()))
509 }
510
511 fn encode_struct<'t>(
512 &mut self,
513 st: &'n library::Struct,
514 nullable: bool,
515 value: Value,
516 ) -> Result<Box<DeferCallback<'n, 't>>>
517 where
518 'n: 't,
519 {
520 let value = match (value, nullable) {
521 (Value::Null, true) => Ok(None),
522 (Value::Null, false) => Err(Error::EncodeError("Struct can't be null".to_owned())),
523 (value, _) => Ok(Some(value)),
524 }?;
525
526 if let Some(value) = value {
527 if nullable {
528 self.bytes.extend(&ALLOC_PRESENT_U64.to_le_bytes());
529 Ok(Box::new(move |this, counter| {
530 let counter = counter.next()?;
531 let call = this.encode_struct_nonnull(st, value, 0)?;
532 this.align_8();
533 call(this, counter)
534 }))
535 } else {
536 self.encode_struct_nonnull(st, value, 0)
537 }
538 } else {
539 self.bytes.extend(&ALLOC_ABSENT_U64.to_le_bytes());
540 Ok(Box::new(|_, _| Ok(())))
541 }
542 }
543
544 fn encode_envelope<'t>(
545 &mut self,
546 ty: &'n library::Type,
547 value: Value,
548 ) -> Result<Box<DeferCallback<'n, 't>>>
549 where
550 'n: 't,
551 {
552 let header_pos = self.bytes.len();
553 self.bytes.extend(&[0u8; 8]);
554
555 if let Value::Null = value {
556 Ok(Box::new(|_, _| Ok(())))
557 } else {
558 Ok(Box::new(move |this, counter| {
559 let counter = counter.next()?;
560 let start = this.bytes.len();
561 let handle_start = this.handles.len();
562
563 let header = if ty.inline_size(this.ns)? > 4 {
564 let call = this.encode_type(ty, value)?;
565
566 this.align_8();
567 call(this, counter)?;
568 let size = (this.bytes.len() - start) as u32;
569 let handle_count = (this.handles.len() - handle_start) as u16;
570
571 debug_assert!(size > 0 || handle_count > 0);
572 let mut header = Vec::new();
573 header.extend(&size.to_le_bytes());
574 header.extend(&handle_count.to_le_bytes());
575 header.extend(&0u16.to_le_bytes());
576 header
577 } else {
578 let mut header_buf =
579 EncodeBuffer { ns: this.ns, bytes: Vec::new(), handles: Vec::new() };
580 header_buf.encode_type(ty, value)?(&mut header_buf, counter)?;
581 let EncodeBuffer { bytes: mut header, handles, .. } = header_buf;
582 header.resize(4, 0);
583 header.extend(&(handles.len() as u16).to_le_bytes());
584 header.extend(&1u16.to_le_bytes());
585 this.handles.extend(handles);
586 header
587 };
588
589 this.bytes.splice(header_pos..(header_pos + header.len()), header.into_iter());
590 Ok(())
591 }))
592 }
593 }
594
595 fn encode_union<'t>(
596 &mut self,
597 union: &'n library::TableOrUnion,
598 nullable: bool,
599 value: Value,
600 ) -> Result<Box<DeferCallback<'n, 't>>>
601 where
602 'n: 't,
603 {
604 let entry = match value {
605 Value::Null => Ok(None),
606 Value::Union(u, n, b) if *u == union.name => Ok(Some((n, *b))),
607 _ => Err(Error::EncodeError(format!("Expected {}", union.name))),
608 }?;
609
610 if let Some((variant, value)) = entry {
611 for member in union.members.values() {
612 if *member.name == *variant {
613 self.bytes.extend(&member.ordinal.to_le_bytes());
614 return self.encode_envelope(&member.ty, value);
615 }
616 }
617
618 Err(Error::EncodeError(format!("Unrecognized union variant: '{}'", variant)))
619 } else if nullable {
620 self.bytes.extend(std::iter::repeat(0u8).take(16));
621 Ok(Box::new(|_, _| Ok(())))
622 } else {
623 Err(Error::EncodeError("Got null for non-nullable Union".to_owned()))
624 }
625 }
626
627 fn encode_table<'t>(
628 &mut self,
629 table: &'n library::TableOrUnion,
630 value: Value,
631 ) -> Result<Box<DeferCallback<'n, 't>>>
632 where
633 'n: 't,
634 {
635 let values = match value {
636 Value::Object(values) => Ok(values),
637 _ => Err(Error::EncodeError(format!("Could not convert to {}", table.name))),
638 }?;
639
640 let mut values_array = Vec::new();
641 for (value_name, value) in values {
642 for (&ord, member) in &table.members {
643 let array_idx = usize::try_from(ord - 1).unwrap();
644 if values_array.len() <= array_idx {
645 values_array.resize_with(array_idx + 1, || None);
646 }
647 if *member.name == value_name {
648 values_array[array_idx] = Some((&member.ty, value));
649 break;
650 }
651 }
652 }
653
654 while values_array.last().map(|x| x.is_none()).unwrap_or(false) {
655 values_array.pop();
656 }
657
658 self.bytes.extend(&(values_array.len() as u64).to_le_bytes());
659 self.bytes.extend(&ALLOC_PRESENT_U64.to_le_bytes());
660
661 Ok(Box::new(move |this, counter| {
662 let counter = counter.next()?;
663 let mut calls = Vec::with_capacity(values_array.len());
664
665 for slot in values_array.into_iter() {
666 if let Some((ty, item)) = slot {
667 calls.push(this.encode_envelope(ty, item)?);
668 } else {
669 this.bytes.extend(&ALLOC_ABSENT_U64.to_le_bytes());
670 }
671 }
672
673 for call in calls {
674 call(this, counter)?;
675 }
676
677 Ok(())
678 }))
679 }
680}
681
682pub fn encode_request(
684 ns: &library::Namespace,
685 txid: u32,
686 protocol_name: &str,
687 method_name: &str,
688 value: Value,
689) -> Result<(Vec<u8>, Vec<HandleDisposition>)> {
690 EncodeBuffer::encode_transaction(
691 ns,
692 txid,
693 protocol_name,
694 Direction::Request,
695 method_name,
696 value,
697 )
698}
699
700pub fn encode_response(
702 ns: &library::Namespace,
703 txid: u32,
704 protocol_name: &str,
705 method_name: &str,
706 value: Value,
707) -> Result<(Vec<u8>, Vec<HandleDisposition>)> {
708 EncodeBuffer::encode_transaction(
709 ns,
710 txid,
711 protocol_name,
712 Direction::Response,
713 method_name,
714 value,
715 )
716}
717
718pub fn encode(
720 ns: &library::Namespace,
721 type_name: &str,
722 nullable: bool,
723 value: Value,
724) -> Result<(Vec<u8>, Vec<HandleDisposition>)> {
725 let mut buf = EncodeBuffer { ns, bytes: Vec::new(), handles: Vec::new() };
726 let cb = buf.encode_identifier(type_name.to_owned(), nullable, value)?;
727 buf.align_8();
728 cb(&mut buf, RecursionCounter::new()).map(|_| (buf.bytes, buf.handles))
729}