Skip to main content

jerasure_rs/
erasure.rs

1//! The `erasure` module provides an interface for encoding and decoding data using erasure codes.
2//!
3//! This module is designed to bind with the Jerasure library, which provides efficient
4//! implementations of various erasure coding techniques.
5//!
6//! For more information, see the [jerasure documentation](https://github.com/tsuraan/Jerasure/blob/414c96ef2b9934953b6facb31d803d79b1dd1405/Manual.pdf)
7
8use ::std::os::raw::c_int;
9use std::num::NonZeroI32;
10
11use crate::{CodeWord, Error};
12
13use iter_tools::Itertools;
14
15#[derive(Debug, Clone, Copy, Default)]
16/// The `Technique` is used to represent the technique used to encode and decode the data.
17///
18/// For more information, see the [jerasure documentation](https://github.com/tsuraan/Jerasure/blob/414c96ef2b9934953b6facb31d803d79b1dd1405/Manual.pdf)
19///
20/// The Default value is `Matrix`, which is the most basic technique used in galois field.
21/// But the `BitMatrix` and `Schedule` techniques are more efficient in the most cases.
22/// And the `ScheduleCache` technique is a `Schedule` technique with a cache version,
23/// which is more efficient than the `Schedule` technique but requires more memory.
24pub enum Technique {
25    #[default]
26    /// The matrix coding technique.
27    ///
28    /// # Requires
29    /// - w must be in {8,16,32}
30    Matrix,
31    /// The bit-matrix coding technique.
32    ///
33    /// # Requires
34    /// - packet_size must be set
35    /// - w not greater than 32
36    /// - not supported for ReedSolVand
37    BitMatrix,
38    /// The schedule coding technique.
39    ///
40    /// # Requires
41    /// - packet_size must be set
42    /// - w not greater than 32
43    /// - not supported for ReedSolVand
44    Schedule,
45    /// The schedule coding technique with cache.
46    ///
47    /// # Requires
48    /// - same as `Schedule`
49    /// - m must be 2
50    /// - not supported for ReedSolVand
51    ScheduleCache,
52}
53
54#[derive(Debug)]
55enum TechInner {
56    /// The matrix coding technique.
57    ///
58    /// # Requires
59    /// - w must be in {8,16,32}
60    Matrix(Matrix),
61    /// The bit-matrix coding technique.
62    ///
63    /// # Requires
64    /// - packet_size must be set
65    /// - w not greater than 32
66    BitMatrix(Matrix, i32),
67    /// The schedule coding technique.
68    ///
69    /// # Requires
70    /// - packet_size must be set
71    /// - w not greater than 32
72    Schedule(Schedule),
73    /// # Requires
74    /// - m must be 2
75    ScheduleCache(ScheduleCache),
76}
77
78#[derive(Debug)]
79struct Matrix {
80    ptr: *mut c_int,
81}
82
83impl Matrix {
84    /// Make a malloc box from a pointer from `malloc`.
85    ///
86    /// # Safety
87    /// This function is unsafe because improper use may lead to memory problems. For example,
88    /// a double-free may occur if the function is called twice on the same raw pointer.
89    unsafe fn try_from_raw(ptr: *mut c_int) -> Option<Self> {
90        if ptr.is_null() {
91            return None;
92        }
93        Some(Self { ptr })
94    }
95
96    fn as_ptr(&self) -> *mut c_int {
97        self.ptr
98    }
99
100    fn as_mut_ptr(&mut self) -> *mut c_int {
101        self.ptr
102    }
103}
104
105impl Drop for Matrix {
106    fn drop(&mut self) {
107        unsafe {
108            jerasure_sys::jerasure::jerasure_free_matrix(self.ptr);
109        }
110    }
111}
112
113#[derive(Debug)]
114struct Schedule {
115    bmat: Matrix,
116    packet_size: i32,
117    inner: *mut *mut c_int,
118}
119
120impl Drop for Schedule {
121    fn drop(&mut self) {
122        unsafe {
123            jerasure_sys::jerasure::jerasure_free_schedule(self.inner);
124        }
125    }
126}
127
128#[derive(Debug)]
129struct ScheduleCache {
130    packet_size: i32,
131    k: i32,
132    m: i32,
133    schedule: *mut *mut c_int,
134    cache: *mut *mut *mut c_int,
135}
136
137impl Drop for ScheduleCache {
138    fn drop(&mut self) {
139        unsafe {
140            jerasure_sys::jerasure::jerasure_free_schedule(self.schedule);
141            jerasure_sys::jerasure::jerasure_free_schedule_cache(self.k, self.m, self.cache);
142        }
143    }
144}
145
146#[derive(Debug, Clone, Copy)]
147/// The `CodingMethod` is used to represent the coding method used to encode and decode the data.
148///
149/// Each method has its own matrix generation algorithm.
150/// For more information, see the [jerasure documentation](https://github.com/tsuraan/Jerasure/blob/414c96ef2b9934953b6facb31d803d79b1dd1405/Manual.pdf)
151pub enum CodingMethod {
152    /// The Reed-Solomon Vandermonde coding method.
153    ///
154    /// # Requires
155    /// - w must be in {8,16,32}
156    /// - not supported for BitMatrix, Schedule, ScheduleCache
157    ReedSolVand,
158    /// The Cauchy coding method.
159    ///
160    /// # Requires
161    /// - packet_size must be set to a multiple of the machine long size
162    Cauchy,
163    Liberation,
164    Liber8tion,
165    BlaumRoth,
166}
167
168/// The `ErasureCodeBuilder` is used to build the `ErasureCode` struct.
169///
170/// It is a builder pattern that allows you to set the parameters of the erasure code.
171#[derive(Debug, Default, Clone)]
172pub struct ErasureCodeBuilder {
173    k: Option<i32>,
174    m: Option<i32>,
175    w: CodeWord,
176    packet_size: Option<i32>,
177    tech: Option<Technique>,
178    coding_method: Option<CodingMethod>,
179}
180
181impl ErasureCodeBuilder {
182    /// Create a new `ErasureCodeBuilder` instance.
183    /// With default values:
184    /// - `k` and `m` are not set
185    /// - `w` is `CodeWord::W8`
186    /// - `packet_size` is not set
187    /// - `tech` is not set
188    /// - `coding_method` is not set
189    pub fn new() -> Self {
190        Self {
191            ..Default::default()
192        }
193    }
194
195    /// Set the number of data devices.
196    pub fn k(mut self, k: NonZeroI32) -> Self {
197        self.k = Some(k.get());
198        self
199    }
200
201    /// Set the number of parity devices.
202    pub fn m(mut self, m: NonZeroI32) -> Self {
203        self.m = Some(m.get());
204        self
205    }
206
207    /// Set the code word size.
208    ///
209    /// # Default
210    /// - `CodeWord::W8`
211    ///
212    /// # Requires
213    /// - $k + m <= 2^w$
214    pub fn w(mut self, w: CodeWord) -> Self {
215        self.w = w;
216        self
217    }
218
219    /// Set the packet size.
220    ///
221    /// # Requires
222    /// - `packet_size` must be a multiple of the machine long size
223    pub fn packet_size(mut self, packet_size: NonZeroI32) -> Self {
224        self.packet_size = Some(packet_size.get());
225        self
226    }
227
228    /// Set the implementation technique.
229    pub fn tech(mut self, tech: Technique) -> Self {
230        self.tech = Some(tech);
231        self
232    }
233
234    /// Set the coding method.
235    pub fn coding_method(mut self, method: CodingMethod) -> Self {
236        self.coding_method = Some(method);
237        self
238    }
239
240    /// Build the `ErasureCode` struct.
241    pub fn build(self) -> Result<ErasureCode, Error> {
242        let k: i32 = self
243            .k
244            .ok_or_else(|| Error::invalid_arguments("k is required"))?;
245        let m: i32 = self
246            .m
247            .ok_or_else(|| Error::invalid_arguments("m is required"))?;
248        let tech = self
249            .tech
250            .ok_or_else(|| Error::invalid_arguments("tech is required"))?;
251        let w = self.w;
252        let coding_method = self
253            .coding_method
254            .ok_or_else(|| Error::invalid_arguments("coding_method is required"))?;
255        if k <= 0 {
256            return Err(Error::invalid_arguments("k must be greater than 0"));
257        }
258        if m <= 0 {
259            return Err(Error::invalid_arguments("m must be greater than 0"));
260        }
261        if k + m > (1 << w.to_u8()) {
262            return Err(Error::invalid_arguments(format!(
263                "k + m must be less or equal than 2^w({})",
264                1 << w.to_u8()
265            )));
266        }
267        let mat = match coding_method {
268            CodingMethod::ReedSolVand => self.reed_sol_vand_mat()?,
269            CodingMethod::Cauchy => self.cauchy_mat()?,
270            _ => unimplemented!("Liber8tion, BlaumRoth are not implemented yet"),
271        };
272
273        let tech = match tech {
274            Technique::Matrix => {
275                // w must be in {8,16,32}
276                if matches!(w, CodeWord::Other(_)) {
277                    return Err(Error::not_supported("w must be in {8,16,32}"));
278                }
279                TechInner::Matrix(mat)
280            }
281            Technique::BitMatrix => {
282                if matches!(coding_method, CodingMethod::ReedSolVand) {
283                    return Err(Error::not_supported(
284                        "BitMatrix is not supported for ReedSolVand",
285                    ));
286                }
287                let bmat = self.mat_to_bitmat(mat)?;
288                TechInner::BitMatrix(bmat, self.check_packet_size()?)
289            }
290            Technique::Schedule => {
291                if matches!(coding_method, CodingMethod::ReedSolVand) {
292                    return Err(Error::not_supported(
293                        "Schedule is not supported for ReedSolVand",
294                    ));
295                }
296                let bmat = self.mat_to_bitmat(mat)?;
297                let schedule = self.bmat_to_schedule(bmat)?;
298                TechInner::Schedule(schedule)
299            }
300            Technique::ScheduleCache => {
301                if matches!(coding_method, CodingMethod::ReedSolVand) {
302                    return Err(Error::not_supported(
303                        "ScheduleCache is not supported for ReedSolVand",
304                    ));
305                }
306                if m != 2 {
307                    return Err(Error::not_supported(
308                        "ScheduleCache is only supported for m = 2",
309                    ));
310                }
311                let bmat = self.mat_to_bitmat(mat)?;
312                let schedule = self.bmat_toschedule_cache(bmat)?;
313                TechInner::ScheduleCache(schedule)
314            }
315        };
316
317        Ok(ErasureCode {
318            tech,
319            k,
320            m,
321            w,
322            method: coding_method,
323        })
324    }
325}
326
327impl ErasureCodeBuilder {
328    fn reed_sol_vand_mat(&self) -> Result<Matrix, Error> {
329        let k = self.k.unwrap();
330        let m = self.m.unwrap();
331        let w = self.w;
332
333        unsafe {
334            Matrix::try_from_raw(jerasure_sys::jerasure::reed_sol_vandermonde_coding_matrix(
335                k,
336                m,
337                w.as_cint(),
338            ))
339        }
340        .ok_or_else(|| Error::other("Failed to create reed solomon vandermonde matrix"))
341    }
342
343    fn cauchy_mat(&self) -> Result<Matrix, Error> {
344        let k = self
345            .k
346            .ok_or_else(|| Error::invalid_arguments("k is required"))?;
347        let m = self
348            .m
349            .ok_or_else(|| Error::invalid_arguments("m is required"))?;
350        let w = self.w;
351
352        unsafe {
353            Matrix::try_from_raw(jerasure_sys::jerasure::cauchy_good_general_coding_matrix(
354                k,
355                m,
356                w.as_cint(),
357            ))
358        }
359        .ok_or_else(|| Error::other("Failed to create cauchy matrix"))
360    }
361
362    fn mat_to_bitmat(&self, mut mat: Matrix) -> Result<Matrix, Error> {
363        let k = self.k.unwrap();
364        let m = self.m.unwrap();
365        let w = self.w;
366
367        unsafe {
368            Matrix::try_from_raw(jerasure_sys::jerasure::jerasure_matrix_to_bitmatrix(
369                k,
370                m,
371                w.as_cint(),
372                mat.as_mut_ptr(),
373            ))
374        }
375        .ok_or_else(|| Error::other("Failed to create bit matrix"))
376    }
377
378    fn bmat_to_schedule(&self, mut bmat: Matrix) -> Result<Schedule, Error> {
379        let k = self.k.unwrap();
380        let m = self.m.unwrap();
381        let w = self.w;
382
383        let p = unsafe {
384            jerasure_sys::jerasure::jerasure_smart_bitmatrix_to_schedule(
385                k,
386                m,
387                w.as_cint(),
388                bmat.as_mut_ptr(),
389            )
390        };
391        if p.is_null() {
392            Err(Error::other("Failed to create schedule"))
393        } else {
394            Ok(Schedule {
395                bmat,
396                packet_size: self.check_packet_size()?,
397                inner: p,
398            })
399        }
400    }
401
402    fn bmat_toschedule_cache(&self, mut bmat: Matrix) -> Result<ScheduleCache, Error> {
403        let k = self.k.unwrap();
404        let m = self.m.unwrap();
405        let w = self.w;
406
407        let schedule = unsafe {
408            jerasure_sys::jerasure::jerasure_smart_bitmatrix_to_schedule(
409                k,
410                m,
411                w.as_cint(),
412                bmat.as_mut_ptr(),
413            )
414        };
415        if schedule.is_null() {
416            return Err(Error::other("Failed to create schedule"));
417        }
418        let cache = unsafe {
419            jerasure_sys::jerasure::jerasure_generate_schedule_cache(
420                k,
421                m,
422                w.as_cint(),
423                bmat.as_mut_ptr(),
424                1,
425            )
426        };
427        if cache.is_null() {
428            unsafe { jerasure_sys::jerasure::jerasure_free_schedule(schedule) };
429            return Err(Error::other("Failed to create schedule cache"));
430        }
431        Ok(ScheduleCache {
432            packet_size: self.check_packet_size()?,
433            schedule,
434            cache,
435            k,
436            m,
437        })
438    }
439
440    fn check_packet_size(&self) -> Result<i32, Error> {
441        if self.packet_size.is_none() {
442            return Err(Error::invalid_arguments("packet_size is required"));
443        }
444        let packet_size = self.packet_size.unwrap();
445        if packet_size <= 0 {
446            return Err(Error::invalid_arguments(
447                "packet_size must be greater than 0",
448            ));
449        }
450        if packet_size % i32::try_from(crate::MACHINE_LONG_SIZE).unwrap() != 0 {
451            return Err(Error::invalid_arguments(format!(
452                "packet_size({packet_size}) must be a multiple of the machine long size({})",
453                crate::MACHINE_LONG_SIZE as i32
454            )));
455        }
456
457        Ok(packet_size)
458    }
459}
460
461/// The `ErasureCode` struct is used to encode and decode data using erasure codes.
462///
463/// It is a wrapper around the Jerasure library, which provides efficient implementations
464/// of various erasure coding techniques.
465pub struct ErasureCode {
466    k: i32,
467    m: i32,
468    w: CodeWord,
469    tech: TechInner,
470    method: CodingMethod,
471}
472
473impl ErasureCode {
474    /// Return the number of data devices.
475    pub fn k(&self) -> i32 {
476        self.k
477    }
478
479    /// Return the number of parity devices.
480    pub fn m(&self) -> i32 {
481        self.m
482    }
483
484    /// Return the code word size.
485    pub fn w(&self) -> CodeWord {
486        self.w
487    }
488
489    /// Return the coding method.
490    pub fn tech(&self) -> Technique {
491        match &self.tech {
492            TechInner::Matrix(_) => Technique::Matrix,
493            TechInner::BitMatrix(_, _) => Technique::BitMatrix,
494            TechInner::Schedule(_) => Technique::Schedule,
495            TechInner::ScheduleCache(_) => Technique::ScheduleCache,
496        }
497    }
498
499    fn _encode_parity<T: AsRef<[u8]>, U: AsMut<[u8]>>(
500        &self,
501        source: impl AsRef<[T]>,
502        mut parity: U,
503    ) -> Result<(), Error> {
504        if source.as_ref().len() != self.k as usize {
505            return Err(Error::invalid_arguments(
506                "source must be the same length as k",
507            ));
508        }
509        let parity = parity.as_mut();
510        let src = source
511            .as_ref()
512            .iter()
513            .map(|s| s.as_ref())
514            .map(|s| {
515                if s.len() % crate::MACHINE_LONG_SIZE != 0 {
516                    return Err(Error::NotAligned(s.len()));
517                }
518                if s.len() != parity.len() {
519                    Err(Error::invalid_arguments(
520                        "source and parity must be the same length",
521                    ))
522                } else {
523                    Ok(s)
524                }
525            })
526            .map_ok(|s| s.as_ptr() as *mut ::std::ffi::c_char)
527            .try_collect::<_, Vec<_>, Error>()?;
528        unsafe {
529            jerasure_sys::jerasure::jerasure_do_parity(
530                self.k,
531                src.as_ptr() as *mut *mut ::std::ffi::c_char,
532                parity.as_mut_ptr() as *mut ::std::ffi::c_char,
533                parity.len().try_into().unwrap(),
534            );
535        }
536        Ok(())
537    }
538
539    /// Encode the data and generate coding parity.
540    ///
541    /// # Arguments
542    /// * `data` - The data to encode, which must be a slice of `k` buffers of the same length.
543    /// * `code` - The buffer to store the coding parity, which must be a slice of `m` buffers of the same length.
544    ///
545    /// # Requires
546    /// * All the buffers must be aligned to the machine long size, and be the same length.
547    pub fn encode<T: AsRef<[u8]>, U: AsMut<[u8]>>(
548        &self,
549        data: impl AsRef<[T]>,
550        mut code: impl AsMut<[U]>,
551    ) -> Result<(), Error> {
552        self.check_encode_buffer(&data, &mut code)?;
553        let len = data.as_ref().first().unwrap().as_ref().len();
554        let src = data
555            .as_ref()
556            .iter()
557            .map(|s| s.as_ref())
558            .map(|s| s.as_ptr() as *mut ::std::ffi::c_char)
559            .collect::<Vec<_>>();
560        let parity = code
561            .as_mut()
562            .iter_mut()
563            .map(|s| s.as_mut())
564            .map(|s| s.as_mut_ptr() as *mut ::std::ffi::c_char)
565            .collect::<Vec<_>>();
566        let data_ptrs = src.as_ptr() as *mut *mut ::std::ffi::c_char;
567        let coding_ptrs = parity.as_ptr() as *mut *mut ::std::ffi::c_char;
568        match &self.tech {
569            TechInner::Matrix(mat) => unsafe {
570                jerasure_sys::jerasure::jerasure_matrix_encode(
571                    self.k,
572                    self.m,
573                    self.w.as_cint(),
574                    mat.as_ptr(),
575                    data_ptrs,
576                    coding_ptrs,
577                    len.try_into().unwrap(),
578                );
579            },
580            TechInner::BitMatrix(bmat, packet_size) => unsafe {
581                jerasure_sys::jerasure::jerasure_bitmatrix_encode(
582                    self.k,
583                    self.m,
584                    self.w.as_cint(),
585                    bmat.as_ptr(),
586                    data_ptrs,
587                    coding_ptrs,
588                    len.try_into().unwrap(),
589                    *packet_size,
590                );
591            },
592            TechInner::Schedule(schedule) => unsafe {
593                jerasure_sys::jerasure::jerasure_schedule_encode(
594                    self.k,
595                    self.m,
596                    self.w.as_cint(),
597                    schedule.inner,
598                    data_ptrs,
599                    coding_ptrs,
600                    len.try_into().unwrap(),
601                    schedule.packet_size,
602                );
603            },
604            TechInner::ScheduleCache(schedule) => unsafe {
605                jerasure_sys::jerasure::jerasure_schedule_encode(
606                    self.k,
607                    self.m,
608                    self.w.as_cint(),
609                    schedule.schedule,
610                    data_ptrs,
611                    coding_ptrs,
612                    len.try_into().unwrap(),
613                    schedule.packet_size,
614                );
615            },
616        }
617        Ok(())
618    }
619
620    /// Decode the data and recover the erased data.
621    ///
622    /// # Arguments
623    /// * `data` - The data devices, which must be a slice of `k` buffers of the same length.
624    /// * `code` - The coding devices, which must be a slice of `m` buffers of the same length.
625    /// * `erased` - The indices of the erased data devices, which must be a slice of integers.
626    ///   The `k` data devices are indexed 0..k, and the `m` coding devices are indexed k..k+m.
627    ///
628    /// # Requires
629    /// * All the buffers must be aligned to the machine long size, and be the same length.
630    /// * The erased indices must be in the range of 0..k+m.
631    /// * The number of erased indices must be less than or equal to `m`.
632    /// * The erased indices must be unique.
633    ///
634    /// # Note
635    /// The erased devices may not be recovered even if the number of erased devices is less than or equal to `m`.
636    /// This is because the coding matrix may not be full rank with large `k` and `m`.
637    /// In this case, the function will return an error.
638    pub fn decode<T: AsMut<[u8]>>(
639        &self,
640        mut data: impl AsMut<[T]>,
641        mut code: impl AsMut<[T]>,
642        erased: &[i32],
643    ) -> Result<(), Error> {
644        use iter_tools::prelude::*;
645        let erased: Result<Vec<_>, Error> = erased
646            .iter()
647            .dedup()
648            .map(|&i| {
649                if 0 <= i && i < self.k + self.m {
650                    Ok(i)
651                } else {
652                    Err(Error::invalid_arguments("erased index out of bounds"))
653                }
654            })
655            .chain(std::iter::once(Ok(-1)))
656            .try_collect();
657        let erased = erased?;
658        if erased.len() - 1 > self.m as usize {
659            return Err(Error::too_many_erasure(erased.len() as i32 - 1, self.m));
660        }
661        self.check_decode_buffer(data.as_mut(), code.as_mut())?;
662
663        let len = data.as_mut().first_mut().unwrap().as_mut().len();
664        let src = data
665            .as_mut()
666            .iter_mut()
667            .map(|s| s.as_mut())
668            .map(|s| s.as_mut_ptr() as *mut ::std::ffi::c_char)
669            .collect::<Vec<_>>();
670        let parity = code
671            .as_mut()
672            .iter_mut()
673            .map(|s| s.as_mut())
674            .map(|s| s.as_mut_ptr() as *mut ::std::ffi::c_char)
675            .collect::<Vec<_>>();
676
677        let row_k_ones = matches!(self.method, CodingMethod::ReedSolVand)
678            .then_some(1)
679            .unwrap_or(0);
680        let erasures_ptr = erased.as_ptr() as *mut i32;
681        let data_ptrs = src.as_ptr() as *mut *mut ::std::ffi::c_char;
682        let coding_ptrs = parity.as_ptr() as *mut *mut ::std::ffi::c_char;
683        match &self.tech {
684            TechInner::Matrix(mat) => {
685                let ret = unsafe {
686                    jerasure_sys::jerasure::jerasure_matrix_decode(
687                        self.k,
688                        self.m,
689                        self.w.as_cint(),
690                        mat.as_ptr(),
691                        row_k_ones,
692                        erasures_ptr,
693                        data_ptrs,
694                        coding_ptrs,
695                        len.try_into().unwrap(),
696                    )
697                };
698                if ret != 0 {
699                    return Err(Error::other("Failed to decode"));
700                }
701            }
702            TechInner::BitMatrix(malloc_box, packet_size) => {
703                let ret = unsafe {
704                    jerasure_sys::jerasure::jerasure_bitmatrix_decode(
705                        self.k,
706                        self.m,
707                        self.w.as_cint(),
708                        malloc_box.as_ptr(),
709                        row_k_ones,
710                        erasures_ptr,
711                        data_ptrs,
712                        coding_ptrs,
713                        len.try_into().unwrap(),
714                        *packet_size,
715                    )
716                };
717                if ret != 0 {
718                    return Err(Error::other("Failed to decode"));
719                }
720            }
721            TechInner::Schedule(schedule) => {
722                let ret = unsafe {
723                    jerasure_sys::jerasure::jerasure_schedule_decode_lazy(
724                        self.k,
725                        self.m,
726                        self.w.as_cint(),
727                        schedule.bmat.as_ptr(),
728                        erased.as_ptr() as *mut i32,
729                        src.as_ptr() as *mut *mut ::std::ffi::c_char,
730                        parity.as_ptr() as *mut *mut ::std::ffi::c_char,
731                        len.try_into().unwrap(),
732                        schedule.packet_size,
733                        1,
734                    )
735                };
736                if ret != 0 {
737                    return Err(Error::other("Failed to decode"));
738                }
739            }
740            TechInner::ScheduleCache(schedule) => {
741                let ret = unsafe {
742                    jerasure_sys::jerasure::jerasure_schedule_decode_cache(
743                        self.k,
744                        self.m,
745                        self.w.as_cint(),
746                        schedule.cache,
747                        erasures_ptr,
748                        data_ptrs,
749                        coding_ptrs,
750                        len.try_into().unwrap(),
751                        schedule.packet_size,
752                    )
753                };
754                if ret != 0 {
755                    return Err(Error::other("Failed to decode"));
756                }
757            }
758        }
759
760        Ok(())
761    }
762}
763
764impl ErasureCode {
765    fn check_encode_buffer<T: AsRef<[u8]>, U: AsMut<[u8]>>(
766        &self,
767        source: impl AsRef<[T]>,
768        mut parity: impl AsMut<[U]>,
769    ) -> Result<(), Error> {
770        let source = source.as_ref();
771        let parity = parity.as_mut();
772        if source.len() != self.k as usize {
773            return Err(Error::invalid_arguments(format!(
774                "source must have k({}) elements",
775                self.k
776            )));
777        }
778        if parity.len() != self.m as usize {
779            return Err(Error::invalid_arguments(format!(
780                "parity must have m({}) elements",
781                self.m,
782            )));
783        }
784        let len = source.first().unwrap().as_ref().len();
785        for s in source.as_ref().iter() {
786            let s = s.as_ref();
787            if s.len() % crate::MACHINE_LONG_SIZE != 0 {
788                return Err(Error::NotAligned(s.as_ref().len()));
789            }
790            if s.len() != len {
791                return Err(Error::invalid_arguments(
792                    "source and parity must be the same length",
793                ));
794            }
795        }
796        for p in parity {
797            let p = p.as_mut();
798            if p.len() % crate::MACHINE_LONG_SIZE != 0 {
799                return Err(Error::NotAligned(p.len()));
800            }
801            if p.len() != len {
802                return Err(Error::invalid_arguments(
803                    "source and parity must be the same length",
804                ));
805            }
806        }
807        Ok(())
808    }
809
810    fn check_decode_buffer<T: AsMut<[u8]>, U: AsMut<[u8]>>(
811        &self,
812        mut source: impl AsMut<[T]>,
813        mut parity: impl AsMut<[U]>,
814    ) -> Result<(), Error> {
815        let source = source.as_mut();
816        let parity = parity.as_mut();
817        if source.len() != self.k as usize {
818            return Err(Error::invalid_arguments(format!(
819                "source must have k({}) elements",
820                self.k
821            )));
822        }
823        if parity.len() != self.m as usize {
824            return Err(Error::invalid_arguments(format!(
825                "parity must have m({}) elements",
826                self.m,
827            )));
828        }
829        let len = source.first_mut().unwrap().as_mut().len();
830        for s in source.as_mut().iter_mut() {
831            let s = s.as_mut();
832            if s.len() % crate::MACHINE_LONG_SIZE != 0 {
833                return Err(Error::NotAligned(s.as_ref().len()));
834            }
835            if s.len() != len {
836                return Err(Error::invalid_arguments(
837                    "source and parity must be the same length",
838                ));
839            }
840        }
841        for p in parity {
842            let p = p.as_mut();
843            if p.len() % crate::MACHINE_LONG_SIZE != 0 {
844                return Err(Error::NotAligned(p.len()));
845            }
846            if p.len() != len {
847                return Err(Error::invalid_arguments(
848                    "source and parity must be the same length",
849                ));
850            }
851        }
852        Ok(())
853    }
854}