-
Notifications
You must be signed in to change notification settings - Fork 182
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
Converting to VarULE #1179
Comments
So a problem with the Appendable thing is that you often need to know the size of the buffer beforehand, for example when calling I'm not sure if |
This will be explained a bit better in the EncodeAsVarULE docs once I get to them :) |
What is the use case you had in mind where the slice-of-slices has more than one element? |
Zibi needs this. something like: struct Foo<'a> {
x: u32,
y: char,
vec: ZeroVec<'a, u32>
}
struct FooULE {
x: u32::ULE,
y: char::ULE,
vec: [u32::ULE]
}
fn encode_as_ule(...) {
cb(&[x.as_byte_slice().as_unaligned(), y.as_unaligned().as_byte_slice(), vec.as_bytes()])
} The idea is that you do this once per field, and since there's a constant number of fields this will work without allocation. Note that The only time you should have to allocate for an |
Okay. So this is basically a variadic function, and using a slice seems to be Rust convention for that. Works for me |
Yeah should have opened with that 😄 . I actually considered trying to make this work with generic tuples instead (because then you could just feed it ULE types directly) but then you need an extra trait and you have to manually implement it on all kinds of tuples. |
I'm reopening this issue because we haven't fully litigated the pros and cons of An unfortunate side-effect of |
Only for If it's that concerning we could add a second |
I think I'm in favor of a second method on the trait, like |
Hmmm. So an important thing to note is that due to the use of generics it's getting inlined anyway, and most likely optimized; using a defaulted function can be a helpful way to hint (and reduces duplication) but I'd rather not require people to implement it manually. |
We still haven't discussed the pros and cons of unsafe trait WriteToBufferAsVarULE<U: ?Sized> {
fn encode_var_ule<'a, E>(
&self,
get_buffer: impl FnOnce(usize) -> &'a mut [u8]
) -> Result<&'a U, E>;
} |
Discussion from today: we solve almost all of our known problems with unsafe trait EncodeAsVarULE<U: ?Sized> {
fn encode_var_ule<'a, E>(
&self,
get_buffer: impl FnOnce(usize) -> &'a mut [u8]
) -> Result<&'a U, E>;
fn encode_len(&self) -> usize;
fn encode_write<'a, E>(&self, &'a mut [u8]) -> Result<&'a U, E>;
} where |
#1199 addresses that issue. |
Part of #1082
Converting From VarULE
From VarULE is fairly easy.
From
works fine when the target type allocates memory. If we want to support cases where the target type borrows from the VarULE,FromVarULE
can work (playground):Converting To VarULE
The other direction is more complicated because VarULE is unsized.
Box-based solutions
A simple trait would be something like the following, but we should not consider it because it requires allocation (playground):
An interesting option would be a
Box
-based solution that uses a custom allocator such that it does not need to actually allocate memory. A trait definition such as the following would be cool, but I can't get this to compile becauseMaybeUninit
requires aSized
type (which seems silly to me, since the main reason you allocate in Rust is for unsized things):A workaround would be to pass an uninitialized buffer into the function. This results in a safe trait (I think), although implementing it requires unsafe code and unstable features (playground):
Buffer-based solutions
@Manishearth proposed the following in #1173:
The advantage of this type of approach, which I'll call a "buffer-based approach" since it returns a
[u8]
instead of aU
, is that it is easy to reason about and doesn't require unstable features or additional memory allocations. The disadvantage is that it requires the trait to be unsafe.An issue with the above trait is that it requires creating the
&[&[u8]]
, which is easy if the outer slice has only a single element, but may require allocating if multiple slices are required (such as aVec<String>
). An alternative would be something such as:The above solution could also use
std::io::Write
, but that trait requires thestd
feature.We could also pre-allocate the whole buffer:
An advantage of
WriteToBufferAsVarULE
is that the safety constraint is a bit simpler: the only requirement is that the pointer returned byencode_var_ule
is equal in in location, size, and alignment to the pointer returned byget_buffer
. Validating that the buffer is a validVarULE
is done internally in the function.CC @zbraniecki
The text was updated successfully, but these errors were encountered: