1use ::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)]
16pub enum Technique {
25 #[default]
26 Matrix,
31 BitMatrix,
38 Schedule,
45 ScheduleCache,
52}
53
54#[derive(Debug)]
55enum TechInner {
56 Matrix(Matrix),
61 BitMatrix(Matrix, i32),
67 Schedule(Schedule),
73 ScheduleCache(ScheduleCache),
76}
77
78#[derive(Debug)]
79struct Matrix {
80 ptr: *mut c_int,
81}
82
83impl Matrix {
84 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)]
147pub enum CodingMethod {
152 ReedSolVand,
158 Cauchy,
163 Liberation,
164 Liber8tion,
165 BlaumRoth,
166}
167
168#[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 pub fn new() -> Self {
190 Self {
191 ..Default::default()
192 }
193 }
194
195 pub fn k(mut self, k: NonZeroI32) -> Self {
197 self.k = Some(k.get());
198 self
199 }
200
201 pub fn m(mut self, m: NonZeroI32) -> Self {
203 self.m = Some(m.get());
204 self
205 }
206
207 pub fn w(mut self, w: CodeWord) -> Self {
215 self.w = w;
216 self
217 }
218
219 pub fn packet_size(mut self, packet_size: NonZeroI32) -> Self {
224 self.packet_size = Some(packet_size.get());
225 self
226 }
227
228 pub fn tech(mut self, tech: Technique) -> Self {
230 self.tech = Some(tech);
231 self
232 }
233
234 pub fn coding_method(mut self, method: CodingMethod) -> Self {
236 self.coding_method = Some(method);
237 self
238 }
239
240 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 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
461pub struct ErasureCode {
466 k: i32,
467 m: i32,
468 w: CodeWord,
469 tech: TechInner,
470 method: CodingMethod,
471}
472
473impl ErasureCode {
474 pub fn k(&self) -> i32 {
476 self.k
477 }
478
479 pub fn m(&self) -> i32 {
481 self.m
482 }
483
484 pub fn w(&self) -> CodeWord {
486 self.w
487 }
488
489 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 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 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}