Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add reed solomon encoding #190

Merged
merged 4 commits into from
Dec 20, 2024
Merged

add reed solomon encoding #190

merged 4 commits into from
Dec 20, 2024

Conversation

greywolve
Copy link
Contributor

Add reed solomon encoding by wrapping the rust reed-solomon-simd crate and exposing it via FFI.

Part of #141

@greywolve greywolve force-pushed the feat/reed-solomon branch 2 times, most recently from e504f0c to eb316e5 Compare December 18, 2024 15:24
Copy link
Member

@asmie asmie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Rust part.

Very good job! I like the copying slice into slice part, using copy_from_slice was the good choice.

The general remark is that it would be nice to add some tests to check if the interface is good. Just several simple cases showing that we cannot easily trigger undefined behaviour.

Example:

#[cfg(test)]
mod tests {
    use super::*;
    use std::ptr;

    #[test]
    fn test_decode_success() {
        // Mock data setup
        let original_shards_count = 2;
        let recovery_shards_count = 1;
        let shard_size = 4;

        let original_shards: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
        let original_shards_indexes: [usize; 2] = [0, 1];

        let recovery_shards: [u8; 4] = [9, 10, 11, 12];
        let recovery_shards_indexes: [usize; 1] = [2];

        let mut recovered_shards_out: [u8; 4] = [0; 4];
        let mut recovered_shards_indexes_out: [usize; 1] = [0; 1];

        unsafe {
            let result = reed_solomon_decode(
                original_shards_count,
                recovery_shards_count,
                shard_size,
                original_shards.as_ptr(),
                original_shards.len(),
                original_shards_indexes.as_ptr(),
                recovery_shards.as_ptr(),
                recovery_shards.len(),
                recovery_shards_indexes.as_ptr(),
                recovered_shards_out.as_mut_ptr(),
                recovered_shards_indexes_out.as_mut_ptr(),
            );

            assert_eq!(result, 0); // Success
            assert_eq!(recovered_shards_out, [9, 10, 11, 12]);
            assert_eq!(recovered_shards_indexes_out, [2]);
        }
    }

    #[test]
    fn test_decode_invalid_shard_size() {
        let original_shards_count = 2;
        let recovery_shards_count = 1;
        let shard_size = 4;

        let original_shards: [u8; 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
        let original_shards_indexes: [usize; 2] = [0, 1];

        let recovery_shards: [u8; 4] = [9, 10, 11, 12];
        let recovery_shards_indexes: [usize; 1] = [2];

        let mut recovered_shards_out: [u8; 4] = [0; 4];
        let mut recovered_shards_indexes_out: [usize; 1] = [0; 1];

        unsafe {
            let result = reed_solomon_decode(
                original_shards_count,
                recovery_shards_count,
                shard_size,
                original_shards.as_ptr(),
                original_shards.len(),
                original_shards_indexes.as_ptr(),
                recovery_shards.as_ptr(),
                recovery_shards.len(),
                recovery_shards_indexes.as_ptr(),
                recovered_shards_out.as_mut_ptr(),
                recovered_shards_indexes_out.as_mut_ptr(),
            );

            assert_eq!(result, -1); // Failure due to invalid shard size
        }
    }

    #[test]
    fn test_decode_mismatched_shard_counts() {
        let original_shards_count = 3; // Incorrect count
        let recovery_shards_count = 1;
        let shard_size = 4;

        let original_shards: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
        let original_shards_indexes: [usize; 2] = [0, 1];

        let recovery_shards: [u8; 4] = [9, 10, 11, 12];
        let recovery_shards_indexes: [usize; 1] = [2];

        let mut recovered_shards_out: [u8; 4] = [0; 4];
        let mut recovered_shards_indexes_out: [usize; 1] = [0; 1];

        unsafe {
            let result = reed_solomon_decode(
                original_shards_count,
                recovery_shards_count,
                shard_size,
                original_shards.as_ptr(),
                original_shards.len(),
                original_shards_indexes.as_ptr(),
                recovery_shards.as_ptr(),
                recovery_shards.len(),
                recovery_shards_indexes.as_ptr(),
                recovered_shards_out.as_mut_ptr(),
                recovered_shards_indexes_out.as_mut_ptr(),
            );

            assert_eq!(result, -1); // Failure due to mismatched shard count
        }
    }

    #[test]
    fn test_decode_minimum_shards() {
        let original_shards_count = 1;
        let recovery_shards_count = 1;
        let shard_size = 4;

        let original_shards: [u8; 4] = [1, 2, 3, 4];
        let original_shards_indexes: [usize; 1] = [0];

        let recovery_shards: [u8; 4] = [5, 6, 7, 8];
        let recovery_shards_indexes: [usize; 1] = [1];

        let mut recovered_shards_out: [u8; 4] = [0; 4];
        let mut recovered_shards_indexes_out: [usize; 1] = [0; 1];

        unsafe {
            let result = reed_solomon_decode(
                original_shards_count,
                recovery_shards_count,
                shard_size,
                original_shards.as_ptr(),
                original_shards.len(),
                original_shards_indexes.as_ptr(),
                recovery_shards.as_ptr(),
                recovery_shards.len(),
                recovery_shards_indexes.as_ptr(),
                recovered_shards_out.as_mut_ptr(),
                recovered_shards_indexes_out.as_mut_ptr(),
            );

            assert_eq!(result, 0); // Success
            assert_eq!(recovered_shards_out, [5, 6, 7, 8]);
            assert_eq!(recovered_shards_indexes_out, [1]);
        }
    }

#[test]
fn test_decode_null_pointer() {
    let original_shards_count = 2;
    let recovery_shards_count = 1;
    let shard_size = 4;

    let original_shards: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
    let original_shards_indexes: [usize; 2] = [0, 1];

    let recovery_shards: [u8; 4] = [9, 10, 11, 12];
    let recovery_shards_indexes: [usize; 1] = [2];

    let mut recovered_shards_out: [u8; 4] = [0; 4];
    let mut recovered_shards_indexes_out: [usize; 1] = [0; 1];

    unsafe {
        // Test null pointer for original_shards
        let result = reed_solomon_decode(
            original_shards_count,
            recovery_shards_count,
            shard_size,
            ptr::null(),
            original_shards.len(),
            original_shards_indexes.as_ptr(),
            recovery_shards.as_ptr(),
            recovery_shards.len(),
            recovery_shards_indexes.as_ptr(),
            recovered_shards_out.as_mut_ptr(),
            recovered_shards_indexes_out.as_mut_ptr(),
        );
        assert_eq!(result, -1); // Failure due to null pointer

        // Test null pointer for recovery_shards
        let result = reed_solomon_decode(
            original_shards_count,
            recovery_shards_count,
            shard_size,
            original_shards.as_ptr(),
            original_shards.len(),
            original_shards_indexes.as_ptr(),
            ptr::null(),
            recovery_shards.len(),
            recovery_shards_indexes.as_ptr(),
            recovered_shards_out.as_mut_ptr(),
            recovered_shards_indexes_out.as_mut_ptr(),
        );
        assert_eq!(result, -1); // Failure due to null pointer

        // Test null pointer for recovered_shards_out
        let result = reed_solomon_decode(
            original_shards_count,
            recovery_shards_count,
            shard_size,
            original_shards.as_ptr(),
            original_shards.len(),
            original_shards_indexes.as_ptr(),
            recovery_shards.as_ptr(),
            recovery_shards.len(),
            recovery_shards_indexes.as_ptr(),
            ptr::null_mut(),
            recovered_shards_indexes_out.as_mut_ptr(),
        );
        assert_eq!(result, -1); // Failure due to null pointer

        // Test null pointer for recovered_shards_indexes_out
        let result = reed_solomon_decode(
            original_shards_count,
            recovery_shards_count,
            shard_size,
            original_shards.as_ptr(),
            original_shards.len(),
            original_shards_indexes.as_ptr(),
            recovery_shards.as_ptr(),
            recovery_shards.len(),
            recovery_shards_indexes.as_ptr(),
            recovered_shards_out.as_mut_ptr(),
            ptr::null_mut(),
        );
        assert_eq!(result, -1); // Failure due to null pointer
    }
}
}

erasurecoding/src/lib.rs Outdated Show resolved Hide resolved
erasurecoding/src/lib.rs Outdated Show resolved Hide resolved
erasurecoding/src/lib.rs Outdated Show resolved Hide resolved
erasurecoding/src/lib.rs Outdated Show resolved Hide resolved
erasurecoding/src/lib.rs Show resolved Hide resolved
erasurecoding/src/lib.rs Outdated Show resolved Hide resolved
erasurecoding/src/lib.rs Show resolved Hide resolved
erasurecoding/src/lib.rs Outdated Show resolved Hide resolved
@greywolve
Copy link
Contributor Author

@asmie thanks again for the detailed Rust review. :)

asmie
asmie previously approved these changes Dec 19, 2024
Copy link
Member

@asmie asmie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job!
Only some nitpicks left - feel free to use them or not 😄

Just a random thought: if we can distinguish what went wrong maybe we could use more error consts than only -1 - but if we just want to indicate error, we can even join those ifs together, as all of the error conditions returns exactly the same value 👍

Overall, LGTM, merge whenever Go side is approved 👍

erasurecoding/src/lib.rs Outdated Show resolved Hide resolved
erasurecoding/src/lib.rs Outdated Show resolved Hide resolved
erasurecoding/src/lib.rs Outdated Show resolved Hide resolved
erasurecoding/src/lib.rs Outdated Show resolved Hide resolved
@greywolve greywolve force-pushed the feat/reed-solomon branch 2 times, most recently from 23d962c to ec492c7 Compare December 20, 2024 14:55
Add reed solomon encoding by wrapping the rust reed-solomon-simd crate
and exposing it via FFI.
@greywolve
Copy link
Contributor Author

Thanks for all the feedback. :) Addressed it all. Added some limitations for total shards and shard size, that should prevent any overflows too.

bamzedev
bamzedev previously approved these changes Dec 20, 2024
@greywolve greywolve merged commit d3e3cbd into main Dec 20, 2024
3 checks passed
@greywolve greywolve deleted the feat/reed-solomon branch December 20, 2024 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants