pas

Crates.iopas
lib.rspas
version0.1.0
sourcesrc
created_at2024-03-21 18:45:23.454387
updated_at2024-03-23 22:43:02.066809
descriptionCrate for strided slice
homepagehttps://github.com/DavidPeicho/pas-rs
repositoryhttps://github.com/DavidPeicho/pas-rs
max_upload_size
id1181964
size40,670
David Peicho (DavidPeicho)

documentation

README

pas-rs

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.

Commit count: 46

cargo fmt