Use const generics to implement Io trait for [T; N]
Frago9876543210 opened this issue · 16 comments
Also can be used for slice and together with #11
Is there a simple way to use endiannezz for [T; N]s without using const generics?
@ahmed-masud const generics already in stable rust anyway
Okay so something that produces something like this should do the trick correct? I am going to try and implement this :P wish me luck hehe
use num_traits as traits; // 0.2.14
use traits::PrimInt;
use traits::Num;
use std::convert::TryInto;
trait Primitive {
type Buf;
fn to_be_bytes(&self) -> <Self as Primitive>::Buf;
}
impl Primitive for u64 {
type Buf = u64;
fn to_be_bytes(&self) -> <Self::Buf as Primitive>::Buf {
let primitive = *self as <Self as Primitive>::Buf;
primitive.to_be()
}
}
impl<T:PrimInt, const N: usize> Primitive for [T; N]
where
T: Primitive + Sized + Num + Copy
{
type Buf = [T; N];
fn to_be_bytes(&self) -> <Self::Buf as Primitive>::Buf {
let primitive = *self as <Self as Primitive>::Buf;
let _r: Vec<T> = primitive.iter().map(|x| {
x.to_be()
}).collect();
_r.try_into()
.unwrap_or_else(|v: Vec<T>|
panic!("Expected a Vec of length {} but it was {}", N, v.len()))
}
}
fn main() {
let a: [u64; 5] = [0xdead; 5];
println!("{:?}", a.to_be_bytes());
}
@ahmed-masud collect allocates memory in heap anyway. Also don't forget about [T: Primitive; N]
and [T: Io; N]
cases. I guess it should be handled as well
I'm going to reimplement this library without depending on std::
in future
collect allocates memory in heap anyway.
That is a good point—Rust was complaining about not being able to figure out the type of _r—but I'll watch for double allocs.
Also don't forget about
[T: Primitive; N]
and[T: Io; N]
cases. I guess it should be handled as well
Thank you, that makes sense.
I am going to implement the related bits by hand first (without augmenting the derive for a simple case, I am also rather green with macros) and then see where it goes.
Why it should be implemented without const generics? They are stable since rust 1.51. The only excuse I can make against the implementation it with const generics is generating a lot of same code for different types.
Also I'd like to avoid panics and creating errors with messages using io::Error::new(_)
. See rust-lang/rust#82812 for details.
TL;DR: io::Error::new(_)
allocates memory for errors 3-d times
Why it should be implemented without const generics? They are stable since rust 1.51. The only excuse I can make against the implementation it with const generics is generating a lot of same code for different types.
Oh I AM implementing it with const generics (but before i do the derive macro code generation, i wanted to do basically write out the code by hand).
Also I'd like to avoid panics and creating errors with messages using
io::Error::new(_)
. See rust-lang/rust#82812 for details.
Yeah I wasn't planning on sticking any panics in the library, I should've clarified my code above is just "toy code" to ask you if I was on the right path :)
Would be nice to don't include any new deps such as num traits
There is however a small issue that I am currently running into, may be you can give me your thoughts on this; So I am starting by this implementation in the lib.rs
right under impl_primitives![...]
on line 221:
impl<T, const N: usize> Primitive for [T; N]
where T: Sized + Copy + Primitive
{
type Buf = [u8; mem::size_of::<T>() * N];
^^^^^^^^^^^^^^^^^^
// I take it that T here should be limited to the
// numerical primitives and I should just stick this
// template up in the macro and delegate this out?
fn to_ne_bytes(self: Self) -> Self::Buf {
todo!()
}
fn to_le_bytes(self) -> Self::Buf {
todo!()
}
fn to_be_bytes(self) -> Self::Buf {
todo!()
}
fn from_ne_bytes(bytes: Self::Buf) -> Self {
todo!()
}
fn from_le_bytes(bytes: Self::Buf) -> Self {
todo!()
}
fn from_be_bytes(bytes: Self::Buf) -> Self {
todo!()
}
}
I think the way to do it without Num (because that was what I was using to limit the T to numerical primitives, is to use a copy of the macro that you use and simply implement the const generic array types. Let me implement something (may be a bit crude to start with but we can tighten it up) ...
You can implement Io for [T; N] instead of Primitive. E.g. bool doesn't have to_ne_bytes method and it's implemented as Io
You can implement Io for [T; N] instead of Primitive. E.g. bool doesn't have to_ne_bytes method and it's implemented as Io
How would that deal with something like [u32; 5]? I would expect that to have to_le_bytes() and to_be_bytes() ... that is to say:
[u32; 5].to_le_bytes: [u32.to_le_bytes(); 5]
Hey boss,
Check out the develop branch on my fork, https://github.com/safai-labs/endiannezz/tree/develop
I have implemented const generics for all primitive types (it's not possible right now to do it for non-primitive types because of the missing features in Rust).
There were some choices about inserting some panics
and asserts
in the code, which I documented in the comments. In real life, as the code stands, that branch should NEVER trigger because sized things should always match up. I could've simply unwrapped
however, I like injecting a 'panic!' so that it can be grepped in case of internal bug.
Although the code may need some prettifying I think it is solid, and works rather nicely, and I am comfortable enough using it in production for a rather complex decentralized filesystem.
I also added the ability to "skip" attributes inside structures to allow for skipping of a field in a struct. It's skipped for write
and filled in with Default:;default()
for read.
It was necessary to create internal traits for AsRef
, AsMut
and Default
which are not visible to or interacted with by crate users. This is because these need to be implemented for [T; N]
which is always "external" to current crate.
I've added my name to the LICENSE file to enable redistribution. You can do a pull request as you see fit.
Cheers,
A
I did new branch that much simpler implements [T: Primitive; N]
support: https://github.com/Frago9876543210/endiannezz/tree/const-generics
[T: Io; N]
not supported yet. I think it was possible to do something like
#[derive(Io, Debug, PartialEq)]
#[endian(big)]
struct ParseMe {
f: Seq<CountReader, Type>,
}
but first of all need to solve the problem with specialization. <Io xor Primitive>::{read, write}
mega useful