Skip to content

Rust strided slice implementation. Slice anything, even struct!

License

Notifications You must be signed in to change notification settings

DavidPeicho/pas-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pas-rs

crates.io

pas (\pa\), meaning "step" in French, is a crate for slicing stuff, especially strided data.

pas allows you to:

  • Get a slice with a custom stride
  • Slice only a part of a struct

⚠️ This crate relies on casting between different data types ⚠️

  • This operation is endian dependant
  • No mechanism to encode/decode to/from big endian is provided

Examples

Macros

With Type Inference

Using slice_attr! to slice in a struct and automatically infer the type:

use pas::{slice, slice_attr};

#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct Vertex {
    pub position: [f32; 3],
    pub uv: [f32; 2],
}

fn main() {
    let vertices = [
        Vertex {position: [1.0, 0.5, 1.0], uv: [1.0, 1.0]},
        Vertex {position: [1.0, 1.0, 0.5], uv: [0.0, 1.0]},
    ];

    // Start slice at first vertex, pointing at `position`.
    let positions = slice_attr!(vertices, [0].position);
    println!("{:?}", positions); // [[1.0, 0.5, 1.0], [1.0, 1.0, 0.5]]

    // Start slice at second vertex, pointing at `uv`.
    let uvs = slice_attr!(vertices, [1].uv);
    println!("{:?}", uvs); // [[0.0, 1.0]]
}

Without Type Inference

It can be useful to slice at a struct attribute, but with a smaller type:

let x_positions: Slice<f32> = slice!(vertices, [0].position[0]);
println!("{:?}", x_positions); // [1.0, 1.0]

let y_positions: Slice<f32> = slice!(vertices, [0].position[1]);
println!("{:?}", y_positions); // [0.5, 1.0]

let z_positions: Slice<f32> = slice!(vertices, [0].position[2]);
println!("{:?}", z_positions); // [1.0, 0.5]

Slice and SliceMut

When slicing an array whose type information is known only at runtime, you can use Slice/SliceMut:

let uv_byte_offset = std::mem::size_of::<Vertex>() + std::mem::size_of::<[f32; 3]>();

// Slice starting at the byte offset `32`, with a stride of 1 element.
let uvs: Slice<[f32; 3]> = Slice::new(&vertices, uv_byte_offset);
println!("{:?}", uvs); // [[0.0, 1.0]]

Custom Stride

It's possible to use a custom stride, in elements count:

use pas::{slice_attr, Slice};

let data: [u32; 5] = [0, 1, 2, 3, 4];

// Using the macro, the stride appears first
let slice = slice_attr!(2, data, [0]);
println!("{:?}", slice); // [0, 2, 4]

// Specified as the last argument when using `Slice`/`SliceMut`
let slice: Slice<u32> = Slice::strided(&data, 0, 3);
println!("{:?}", slice); // [0, 3]

The stride must always be at least the size of the attribute. This example will panic:

use pas::{slice_attr, Slice};

let data: [u32; 5] = [0, 1, 2, 3, 4];
// Default stride is `std::mem::size_of::<u32>()` here, attribute
// size is `std::mem::size_of::<[u32; 3]>()`.
let _: Slice<[u32; 3]> = Slice::new(&data, 0);

Safety

While this crate makes use of unsafe and transmute, it's (mostly) safe to use and comes with runtime checks preventing you to run into undefined behaviors:

  • Ensure that reads are aligned
  • Check size of read compared to stride

This crate requires your types to implement the Pod trait from the bytemuck crate, improving safety with alignment rules, and illegal bit patterns.

About

Rust strided slice implementation. Slice anything, even struct!

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages