Crates.io | uintx |
lib.rs | uintx |
version | 0.1.0 |
source | src |
created_at | 2024-04-02 10:09:59.326381 |
updated_at | 2024-05-16 08:20:54.088667 |
description | Unaligned unsigned integers with exact size in memory and arithmetic operations for them |
homepage | |
repository | https://github.com/AlexanderSchuetz97/uintx |
max_upload_size | |
id | 1193386 |
size | 220,114 |
Unaligned integer types in rust with arithmetic operations
Each of these types implement and provide all the functionality of u32. In addition to that they also overload all operators for the next largest aligned number. Example:
#[test]
fn test() {
let mut num : u24 = u24::from(12u32);
num += 1u32;
num = num + 1u32;
num += u24::from(4);
num = num + u24::from(4);
assert_eq!(num, 22)
}
Enabling this feature causes all types of this crate to implement the PrimInt trait and all required super traits from the num_traits crate.
Enabling this feature enables Into and From conversions for all numeric types provided by the ux crate. This can be useful if you want to read unaligned data from a block of memory but need to perform signed math on it. It is also useful if your codebase already uses the ux crate.
Conversions from uintx to ux and vice versa never fail. They always clamp the values. So converting an u24 into an ux::u12 will never fail but all bits beyond bit #12 are discarded. The same goes for converting an ux::u28 into an u24. This will discard all bits beyond bit #24.
Enabling this feature enables Into and From conversions for all numeric types provided by the intx crate. This is mainly useful if you need signed numbers with an accurate sizeof.
Beware that the intx crate does not implement any arithmetic operations so this is probably only useful if you already use intx in your codebase.
This feature provides some functions for every type to perform arithmetic operations using less fetch instructions (and fewer instructions overall) compares to the memory safe std::ops. It all boils down to how the CPU fetches the data from memory, so your mileage may vary depending on the CPU architecture. I am mainly targeting x86_64 with this feature.
A normal add operation of 2 u24 ints would compile into 2 fetches+1 shift for each u24, then an add operation, then to store the result 2 stores+1 shift. This is memory safe but not the fastest.
The function: u24::unsafe_add_with_aligned_into_aligned(u24, u32) -> u32
would compile into 1 fetch+1 and for the u24 and 1 fetch for the u32, then an add operation, then 1 store. This is faster than the above. Keep in mind that this function implicitly transforms the output to an u32. There are variants of this function that output an u24 too or accept 2 u24s as input. They require a bit more instructions but still fewer than the memory save "+" operator.
Why are these unsafe functions not memory safe?
You probably know already that the CPU has no instruction to fetch just 3 bytes from memory, The unsafe functions always fetch the next largest possible type. In u24 this would be u32. So this function will read 1 more byte than a buffer (slice/vec) may have. The additional byte does not have an influence on the computation result, but it is read nonetheless. This may cause your application to segfault or cause other unintended consequences (memory mapped io for example may do something funny) This is pretty much exclusively intended to operate on buffers that are padded to have a size dividable by the next largest aligned integer. So for u24 the buffer (slice/vec) should have a size in bytes dividable by 4 to safely use the unsafe operations. I would not recommend on using this on stack variables. (The layout of the stack is hard/impossible to predict). If you don't want to deal with this then I recommend you do not use the unsafe functions and stick to the safe std::ops. I also recommend verification of the compiled code using a disassembler when using the unsafe functions. Some Integers (depending on size/architecture) are not faster when using the unsafe functions. CPU Architectures that do not permit unaligned memory access for all primitive types will probably cause a SIGBUS if this feature is used. (Again I recommend to use this feature with x86_64 only)
As mentioned in the features the intx and ux library provide similar functionality than this crate.
My personal reasons for not using them are as follows
This library was developed to assist in dealing with a lot of RGB,BGR, CMYKOG, CMYKOGV,... pixel buffers in esoteric "Pixel" formats where arithmetic operations have to be performed before the pixel represents an actual valid value. Doing this with many &| and shift operations leads to very unreadable code.