1#[cfg(feature = "payload-builder")]
11pub mod builder;
12#[cfg(test)]
13mod tests;
14
15use crate::EXTERNAL_ARGS_BUFFER_SIZE;
16use ab_contracts_common::MAX_TOTAL_METHOD_ARGS;
17use ab_contracts_common::env::{MethodContext, PreparedMethod};
18use ab_contracts_common::method::MethodFingerprint;
19use ab_core_primitives::address::Address;
20use ab_io_type::MAX_ALIGNMENT;
21use ab_io_type::trivial_type::TrivialType;
22use core::ffi::c_void;
23use core::mem::{MaybeUninit, offset_of};
24use core::num::{NonZeroU8, NonZeroUsize};
25use core::ops::{Deref, DerefMut};
26use core::ptr::NonNull;
27use core::{hint, ptr, slice};
28
29#[derive(Copy, Clone)]
30#[repr(C)]
31struct FfiDataSizeCapacityRo {
32 data_ptr: NonNull<u8>,
33 size: u32,
34 capacity: u32,
35}
36
37#[derive(Copy, Clone)]
38#[repr(C)]
39struct FfiDataSizeCapacityRw {
40 data_ptr: *mut u8,
41 size: u32,
42 capacity: u32,
43}
44
45#[derive(Debug, Copy, Clone, Eq, PartialEq, TrivialType)]
46#[repr(u8)]
47pub enum TransactionMethodContext {
48 Null,
50 Wallet,
52}
53
54const impl TryFrom<u8> for TransactionMethodContext {
55 type Error = ();
56
57 #[inline(always)]
58 fn try_from(value: u8) -> Result<Self, Self::Error> {
59 Ok(match value {
60 0 => Self::Null,
61 1 => Self::Wallet,
62 _ => return Err(()),
63 })
64 }
65}
66
67#[derive(Debug, Copy, Eq, PartialEq, Clone)]
68pub enum TransactionInputType {
69 Value { alignment_power: u8 },
70 OutputIndex { output_index: u8 },
71}
72
73#[derive(Debug, Copy, Eq, PartialEq, Clone)]
74pub enum TransactionSlotType {
75 Address,
76 OutputIndex { output_index: u8 },
77}
78
79#[derive(Debug, Copy, Clone)]
88pub struct TransactionSlot(TransactionSlotType);
89
90impl TransactionSlot {
91 #[inline(always)]
93 pub const fn new_address() -> Self {
94 Self(TransactionSlotType::Address)
95 }
96
97 #[inline(always)]
101 pub const fn new_output_index(output_index: u8) -> Option<Self> {
102 if output_index > 0b0111_1111 {
103 return None;
104 }
105
106 Some(Self(TransactionSlotType::OutputIndex { output_index }))
107 }
108
109 #[inline(always)]
111 pub const fn from_u8(n: u8) -> Self {
112 if n & 0b1000_0000 == 0 {
114 Self(TransactionSlotType::OutputIndex { output_index: n })
115 } else {
116 Self(TransactionSlotType::Address)
117 }
118 }
119
120 #[inline(always)]
122 pub const fn into_u8(self) -> u8 {
123 match self.0 {
125 TransactionSlotType::Address => 0b1000_0000,
126 TransactionSlotType::OutputIndex { output_index } => output_index,
127 }
128 }
129
130 #[inline(always)]
132 pub const fn slot_type(self) -> TransactionSlotType {
133 self.0
134 }
135}
136
137#[derive(Debug, Copy, Clone)]
146pub struct TransactionInput(TransactionInputType);
147
148impl TransactionInput {
149 #[inline(always)]
153 pub const fn new_value(alignment: NonZeroU8) -> Option<Self> {
154 match alignment.get() {
155 1 | 2 | 4 | 8 | 16 => Some(Self(TransactionInputType::Value {
156 alignment_power: alignment.ilog2() as u8,
157 })),
158 _ => None,
159 }
160 }
161
162 #[inline(always)]
166 pub const fn new_output_index(output_index: u8) -> Option<Self> {
167 if output_index > 0b0111_1111 {
168 return None;
169 }
170
171 Some(Self(TransactionInputType::OutputIndex { output_index }))
172 }
173
174 #[inline(always)]
176 pub const fn from_u8(n: u8) -> Self {
177 if n & 0b1000_0000 == 0 {
179 Self(TransactionInputType::OutputIndex { output_index: n })
180 } else {
181 Self(TransactionInputType::Value {
182 alignment_power: n & 0b0111_1111,
183 })
184 }
185 }
186
187 #[inline(always)]
189 pub const fn into_u8(self) -> u8 {
190 match self.0 {
192 TransactionInputType::Value { alignment_power } => 0b1000_0000 | alignment_power,
193 TransactionInputType::OutputIndex { output_index } => output_index,
194 }
195 }
196
197 #[inline(always)]
199 pub const fn input_type(self) -> TransactionInputType {
200 self.0
201 }
202}
203
204#[derive(Debug, thiserror::Error)]
206pub enum TransactionPayloadDecoderError {
207 #[error("Payload too small")]
209 PayloadTooSmall,
210 #[error("Too many arguments")]
212 TooManyArguments(u8),
213 #[error("`ExternalArgs` buffer too small")]
215 ExternalArgsBufferTooSmall,
216 #[error("Output index not found: {0}")]
218 OutputIndexNotFound(u8),
219 #[error("Invalid output index {output_index} size for slot: {size}")]
221 InvalidSlotOutputIndexSize { output_index: u8, size: u32 },
222 #[error("Invalid output index {output_index} alignment for slot: {alignment}")]
224 InvalidSlotOutputIndexAlign { output_index: u8, alignment: u32 },
225 #[error("Alignment power is too large: {0}")]
227 AlignmentPowerTooLarge(u8),
228 #[error("Invalid method context: {0}")]
230 InvalidMethodContext(u8),
231 #[error("Output buffer too small")]
233 OutputBufferTooSmall,
234 #[error("Output buffer offsets too small")]
236 OutputBufferOffsetsTooSmall,
237}
238
239#[derive(Debug, Copy, Clone)]
240pub struct OutputBufferDetails {
241 output_offset: u32,
243 size_or_external_args_offset: u32,
249}
250
251#[derive(Debug, Copy, Clone)]
252struct OutputBufferOffsetsCursor {
253 before_last: usize,
256 current: usize,
258}
259
260#[derive(Debug)]
262pub struct TransactionPayloadDecoder<'a> {
263 payload: &'a [u8],
264 external_args_buffer: &'a mut [*mut c_void; EXTERNAL_ARGS_BUFFER_SIZE],
265 output_buffer: &'a mut [MaybeUninit<u128>],
268 output_buffer_cursor: usize,
269 output_buffer_details: &'a mut [MaybeUninit<OutputBufferDetails>],
270 output_buffer_offsets_cursor: OutputBufferOffsetsCursor,
271 map_context: fn(TransactionMethodContext) -> MethodContext,
272}
273
274impl<'a> TransactionPayloadDecoder<'a> {
275 #[inline]
288 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
289 pub fn new(
290 payload: &'a [u128],
291 external_args_buffer: &'a mut [*mut c_void; EXTERNAL_ARGS_BUFFER_SIZE],
292 output_buffer: &'a mut [MaybeUninit<u128>],
293 output_buffer_details: &'a mut [MaybeUninit<OutputBufferDetails>],
294 map_context: fn(TransactionMethodContext) -> MethodContext,
295 ) -> Self {
296 debug_assert_eq!(align_of_val(payload), usize::from(MAX_ALIGNMENT));
297 debug_assert_eq!(align_of_val(output_buffer), usize::from(MAX_ALIGNMENT));
298
299 let payload =
301 unsafe { slice::from_raw_parts(payload.as_ptr().cast::<u8>(), size_of_val(payload)) };
302
303 Self {
304 payload,
305 external_args_buffer,
306 output_buffer,
307 output_buffer_cursor: 0,
308 output_buffer_details,
309 output_buffer_offsets_cursor: OutputBufferOffsetsCursor {
310 before_last: 0,
311 current: 0,
312 },
313 map_context,
314 }
315 }
316
317 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
319 pub fn decode_next_method(
320 &mut self,
321 ) -> Result<Option<PreparedMethod<'_>>, TransactionPayloadDecoderError> {
322 TransactionPayloadDecoderInternal::<true>(self).decode_next_method()
323 }
324
325 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
331 pub unsafe fn decode_next_method_unchecked(&mut self) -> Option<PreparedMethod<'_>> {
332 TransactionPayloadDecoderInternal::<false>(self)
333 .decode_next_method()
334 .expect("No decoding errors are possible with trusted input; qed")
335 }
336}
337
338#[inline(always)]
344unsafe fn write_external_args<T>(external_args: &mut NonNull<c_void>, value: T) {
345 unsafe {
347 external_args.cast::<T>().write(value);
348 *external_args = external_args.byte_add(size_of::<T>());
349 }
350}
351
352struct TransactionPayloadDecoderInternal<'tmp, 'decoder, const VERIFY: bool>(
356 &'tmp mut TransactionPayloadDecoder<'decoder>,
357);
358
359impl<'decoder, const VERIFY: bool> Deref
360 for TransactionPayloadDecoderInternal<'_, 'decoder, VERIFY>
361{
362 type Target = TransactionPayloadDecoder<'decoder>;
363
364 #[inline(always)]
365 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
366 fn deref(&self) -> &Self::Target {
367 self.0
368 }
369}
370
371impl<const VERIFY: bool> DerefMut for TransactionPayloadDecoderInternal<'_, '_, VERIFY> {
372 #[inline(always)]
373 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
374 fn deref_mut(&mut self) -> &mut Self::Target {
375 self.0
376 }
377}
378
379impl<'tmp, 'decoder, const VERIFY: bool> TransactionPayloadDecoderInternal<'tmp, 'decoder, VERIFY> {
380 #[inline(always)]
381 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
382 fn decode_next_method(
383 mut self,
384 ) -> Result<Option<PreparedMethod<'tmp>>, TransactionPayloadDecoderError> {
385 if self.payload.len() <= usize::from(MAX_ALIGNMENT) {
386 return Ok(None);
387 }
388
389 self.update_output_buffer_details();
390
391 let contract = self.get_trivial_type::<Address>()?;
392 let method_fingerprint = self.get_trivial_type::<MethodFingerprint>()?;
393 let method_context = (self.map_context)(self.read_method_context()?);
394
395 let mut transaction_slots_inputs =
396 [MaybeUninit::<u8>::uninit(); MAX_TOTAL_METHOD_ARGS as usize];
397
398 let num_slot_arguments = self.read_u8()?;
399 for transaction_slot in transaction_slots_inputs
400 .iter_mut()
401 .take(usize::from(num_slot_arguments))
402 {
403 transaction_slot.write(self.read_u8()?);
404 }
405
406 let num_input_arguments = self.read_u8()?;
407 for transaction_input in transaction_slots_inputs
408 .iter_mut()
409 .skip(usize::from(num_slot_arguments))
410 .take(usize::from(num_input_arguments))
411 {
412 transaction_input.write(self.read_u8()?);
413 }
414
415 let num_output_arguments = self.read_u8()?;
416
417 let number_of_arguments = u16::from(num_slot_arguments)
420 + u16::from(num_input_arguments)
421 + u16::from(num_output_arguments);
422 if VERIFY {
423 if number_of_arguments > u16::from(MAX_TOTAL_METHOD_ARGS) {
424 return Err(TransactionPayloadDecoderError::TooManyArguments(
425 u8::try_from(number_of_arguments).unwrap_or(u8::MAX),
426 ));
427 }
428 } else {
429 unsafe {
431 hint::assert_unchecked(number_of_arguments <= u16::from(MAX_TOTAL_METHOD_ARGS));
432 }
433 }
434
435 let (transaction_slots, transaction_inputs) = transaction_slots_inputs
436 .split_at_checked(usize::from(num_slot_arguments))
437 .expect("Total number of arguments was checked to fit above; qed");
438 let transaction_inputs = transaction_inputs
439 .get(..usize::from(num_input_arguments))
440 .expect("Total number of arguments was checked to fit above; qed");
441 let (transaction_slots, transaction_inputs) = unsafe {
443 (
444 transaction_slots.assume_init_ref(),
445 transaction_inputs.assume_init_ref(),
446 )
447 };
448
449 let external_args = NonNull::new(self.external_args_buffer.as_mut_ptr())
450 .expect("Not null; qed")
451 .cast::<c_void>();
452 {
453 let external_args_cursor = &mut external_args.clone();
454
455 for &transaction_slot in transaction_slots {
456 let address = match TransactionSlot::from_u8(transaction_slot).slot_type() {
457 TransactionSlotType::Address => self.get_trivial_type::<Address>()?,
458 TransactionSlotType::OutputIndex { output_index } => {
459 let (bytes, size) = self.get_from_output_buffer(output_index)?;
460
461 if VERIFY && size != Address::SIZE {
462 return Err(
463 TransactionPayloadDecoderError::InvalidSlotOutputIndexSize {
464 output_index,
465 size,
466 },
467 );
468 }
469
470 let maybe_address = unsafe { Address::from_bytes(bytes) };
472
473 if VERIFY {
474 let Some(address) = maybe_address else {
475 let error =
476 TransactionPayloadDecoderError::InvalidSlotOutputIndexAlign {
477 output_index,
478 alignment: align_of_val(bytes) as u32,
479 };
480 return Err(error);
481 };
482
483 address
484 } else {
485 unsafe { maybe_address.unwrap_unchecked() }
487 }
488 }
489 };
490
491 unsafe {
494 write_external_args(external_args_cursor, ptr::from_ref(address));
495 }
496 }
497
498 for &transaction_input in transaction_inputs {
499 let (bytes, size) = match TransactionInput::from_u8(transaction_input).input_type()
500 {
501 TransactionInputType::Value { alignment_power } => {
502 let alignment = Self::alignment_from_power(alignment_power)?;
503
504 let size = *self.get_trivial_type::<u32>()?;
505 let bytes = self.get_bytes(size, alignment)?;
506
507 (bytes, size)
508 }
509 TransactionInputType::OutputIndex { output_index } => {
510 self.get_from_output_buffer(output_index)?
511 }
512 };
513
514 unsafe {
517 write_external_args(
518 external_args_cursor,
519 FfiDataSizeCapacityRo {
520 data_ptr: NonNull::from_ref(bytes).as_non_null_ptr(),
521 size,
522 capacity: size,
523 },
524 );
525 }
526 }
527
528 for _ in 0..num_output_arguments {
529 let recommended_capacity = *self.get_trivial_type::<u32>()?;
530 let alignment_power = *self.get_trivial_type::<u8>()?;
531 let alignment = Self::alignment_from_power(alignment_power)?;
532
533 let external_args_size_offset = unsafe {
536 external_args_cursor
537 .byte_add(offset_of!(FfiDataSizeCapacityRw, size))
538 .byte_offset_from_unsigned(external_args)
539 };
540
541 let data = self.allocate_output_buffer(
542 recommended_capacity,
543 alignment,
544 external_args_size_offset as u32,
545 )?;
546
547 unsafe {
550 write_external_args(
551 external_args_cursor,
552 FfiDataSizeCapacityRw {
553 data_ptr: data.as_ptr(),
554 size: 0,
555 capacity: recommended_capacity,
556 },
557 );
558 }
559 }
560 }
561
562 let decoder = self.0;
563 Ok(Some(PreparedMethod::new_untyped(
564 *contract,
565 *method_fingerprint,
566 &mut *decoder.external_args_buffer,
567 method_context,
568 )))
569 }
570
571 #[inline(always)]
574 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
575 fn alignment_from_power(
576 alignment_power: u8,
577 ) -> Result<NonZeroUsize, TransactionPayloadDecoderError> {
578 if VERIFY {
579 if u32::from(alignment_power) > MAX_ALIGNMENT.ilog2() {
580 return Err(TransactionPayloadDecoderError::AlignmentPowerTooLarge(
581 alignment_power,
582 ));
583 }
584 } else {
585 unsafe {
587 hint::assert_unchecked(u32::from(alignment_power) <= MAX_ALIGNMENT.ilog2());
588 }
589 }
590
591 Ok(NonZeroUsize::new(1 << alignment_power).expect("Power of two is not zero; qed"))
592 }
593
594 #[inline(always)]
596 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
597 fn get_trivial_type<T>(&mut self) -> Result<&'decoder T, TransactionPayloadDecoderError>
598 where
599 T: TrivialType,
600 {
601 self.ensure_alignment(NonZeroUsize::new(align_of::<T>()).expect("Not zero; qed"))?;
602
603 let bytes;
604 if VERIFY {
605 (bytes, self.payload) = self
606 .payload
607 .split_at_checked(size_of::<T>())
608 .ok_or(TransactionPayloadDecoderError::PayloadTooSmall)?;
609 } else {
610 (bytes, self.payload) = unsafe { self.payload.split_at_unchecked(size_of::<T>()) };
612 }
613
614 let value_ref = unsafe { bytes.as_ptr().cast::<T>().as_ref().expect("Not null; qed") };
616
617 Ok(value_ref)
618 }
619
620 #[inline(always)]
622 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
623 fn get_bytes(
624 &mut self,
625 size: u32,
626 alignment: NonZeroUsize,
627 ) -> Result<&'decoder [u8], TransactionPayloadDecoderError> {
628 self.ensure_alignment(alignment)?;
629
630 let bytes;
631 if VERIFY {
632 (bytes, self.payload) = self
633 .payload
634 .split_at_checked(size as usize)
635 .ok_or(TransactionPayloadDecoderError::PayloadTooSmall)?;
636 } else {
637 (bytes, self.payload) = unsafe { self.payload.split_at_unchecked(size as usize) };
639 }
640
641 Ok(bytes)
642 }
643
644 #[inline(always)]
647 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
648 fn read_method_context(
649 &mut self,
650 ) -> Result<TransactionMethodContext, TransactionPayloadDecoderError> {
651 let value = self.read_u8()?;
652
653 if VERIFY {
654 TransactionMethodContext::try_from(value)
655 .map_err(|()| TransactionPayloadDecoderError::InvalidMethodContext(value))
656 } else {
657 Ok(unsafe { TransactionMethodContext::try_from(value).unwrap_unchecked() })
659 }
660 }
661
662 #[inline(always)]
663 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
664 fn read_u8(&mut self) -> Result<u8, TransactionPayloadDecoderError> {
665 let value;
666 if VERIFY {
667 (value, self.payload) = self
668 .payload
669 .split_first()
670 .ok_or(TransactionPayloadDecoderError::PayloadTooSmall)?;
671 } else {
672 (value, self.payload) = unsafe { self.payload.split_first().unwrap_unchecked() };
674 }
675
676 Ok(*value)
677 }
678
679 #[inline(always)]
680 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
681 fn ensure_alignment(
682 &mut self,
683 alignment: NonZeroUsize,
684 ) -> Result<(), TransactionPayloadDecoderError> {
685 let alignment = alignment.get();
686 debug_assert!(alignment <= usize::from(MAX_ALIGNMENT));
687
688 let unaligned_by = self.payload.as_ptr().addr() & (alignment - 1);
691 if unaligned_by > 0 {
692 let padding_bytes = unsafe { alignment.unchecked_sub(unaligned_by) };
694 if VERIFY {
695 self.payload = self
696 .payload
697 .split_off(padding_bytes..)
698 .ok_or(TransactionPayloadDecoderError::PayloadTooSmall)?;
699 } else {
700 self.payload = unsafe { self.payload.get_unchecked(padding_bytes..) };
702 }
703 }
704 Ok(())
705 }
706
707 #[inline(always)]
709 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
710 fn allocate_output_buffer(
711 &mut self,
712 capacity: u32,
713 output_alignment: NonZeroUsize,
714 external_args_size_offset: u32,
715 ) -> Result<NonNull<u8>, TransactionPayloadDecoderError> {
716 if VERIFY && self.output_buffer_details.len() == self.output_buffer_offsets_cursor.current {
717 return Err(TransactionPayloadDecoderError::OutputBufferOffsetsTooSmall);
718 }
719
720 let (output_offset, output_ptr) = self
721 .allocate_output_buffer_ptr(output_alignment, capacity as usize)
722 .ok_or(TransactionPayloadDecoderError::OutputBufferTooSmall)?;
723
724 let output_buffer_details = unsafe {
726 let output_buffer_offsets_cursor = self.output_buffer_offsets_cursor.current;
728 self.output_buffer_details
729 .get_unchecked_mut(output_buffer_offsets_cursor)
730 };
731 output_buffer_details.write(OutputBufferDetails {
732 output_offset: output_offset as u32,
733 size_or_external_args_offset: external_args_size_offset,
734 });
735 self.output_buffer_offsets_cursor.current += 1;
736
737 Ok(output_ptr)
738 }
739
740 #[inline(always)]
742 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
743 fn allocate_output_buffer_ptr<T>(
744 &mut self,
745 alignment: NonZeroUsize,
746 size: usize,
747 ) -> Option<(usize, NonNull<T>)> {
748 let alignment = alignment.get();
749 debug_assert!(alignment <= usize::from(MAX_ALIGNMENT));
750
751 let unaligned_by = self.output_buffer_cursor & (alignment - 1);
754 let padding_bytes = unsafe { alignment.unchecked_sub(unaligned_by) };
756
757 let (offset, new_output_buffer_cursor) = if VERIFY {
758 let offset = self.output_buffer_cursor.checked_add(padding_bytes)?;
759 let new_output_buffer_cursor = offset.checked_add(size)?;
760
761 if new_output_buffer_cursor > size_of_val(self.output_buffer) {
762 return None;
763 }
764
765 (offset, new_output_buffer_cursor)
766 } else {
767 unsafe {
769 let offset = self.output_buffer_cursor.unchecked_add(padding_bytes);
770 (offset, offset.unchecked_add(size))
771 }
772 };
773
774 let buffer_ptr = unsafe {
776 NonNull::from_mut(&mut *self.output_buffer)
777 .cast::<T>()
778 .byte_add(offset)
779 };
780 self.output_buffer_cursor = new_output_buffer_cursor;
781
782 Some((offset, buffer_ptr))
783 }
784
785 #[inline(always)]
788 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
789 fn update_output_buffer_details(&mut self) {
790 #[expect(
791 clippy::rest_pattern_accessible_field,
792 reason = "Do not need other fields"
793 )]
794 let TransactionPayloadDecoder {
795 external_args_buffer,
796 output_buffer_details,
797 output_buffer_offsets_cursor,
798 ..
799 } = &mut **self;
800
801 for output_buffer_details in unsafe {
803 output_buffer_details
804 .get_unchecked_mut(
805 output_buffer_offsets_cursor.before_last..output_buffer_offsets_cursor.current,
806 )
807 .assume_init_mut()
808 } {
809 output_buffer_details.size_or_external_args_offset = unsafe {
811 external_args_buffer
812 .as_ptr()
813 .cast::<u32>()
814 .byte_add(output_buffer_details.size_or_external_args_offset as usize)
815 .read()
816 };
817 }
818
819 output_buffer_offsets_cursor.before_last = output_buffer_offsets_cursor.current;
820 }
821
822 #[inline(always)]
823 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
824 fn get_from_output_buffer(
825 &self,
826 output_index: u8,
827 ) -> Result<(&[u8], u32), TransactionPayloadDecoderError> {
828 let OutputBufferDetails {
829 output_offset,
830 size_or_external_args_offset: size,
831 } = if VERIFY {
832 if usize::from(output_index) < self.output_buffer_offsets_cursor.current {
833 unsafe {
835 self.output_buffer_details
836 .get_unchecked(usize::from(output_index))
837 .assume_init()
838 }
839 } else {
840 return Err(TransactionPayloadDecoderError::OutputIndexNotFound(
841 output_index,
842 ));
843 }
844 } else {
845 unsafe {
847 self.output_buffer_details
848 .get_unchecked(usize::from(output_index))
849 .assume_init()
850 }
851 };
852
853 let bytes = unsafe {
856 let bytes_ptr = self
857 .output_buffer
858 .as_ptr()
859 .cast::<u8>()
860 .add(output_offset as usize);
861
862 slice::from_raw_parts(bytes_ptr, size as usize)
863 };
864
865 Ok((bytes, size))
866 }
867}