primitives: Remove alloc requirement from HexPrimitive#5480
primitives: Remove alloc requirement from HexPrimitive#5480apoelstra merged 5 commits intorust-bitcoin:masterfrom
Conversation
|
Merged #5381. This needs rebase now. (Sorry, I think maybe the formatting PR conflicted with a ton of stuff.) |
3b8b8ad to
97b26bf
Compare
primitives/src/hex_codec.rs
Outdated
| let extra_len = if f.alternate() { 2 } else { 0 }; | ||
| let total_len = len + extra_len; |
There was a problem hiding this comment.
I already wrote it today so I won't hark on the point but these two lines are new and not a code move but are in commit titled primitives: Move hex_codec to independent module.
There was a problem hiding this comment.
I must have messed up a rebase/amend at some point. I thought that I had definitely made the last patch move-only.
The new patches move this and one other difference into the second patch. The third patch should be only a straight code move now.
|
FTR the buffer stuff looked like it should be supported by the language but from a brief look at |
|
Good effort @mpbagot |
97b26bf to
852adb5
Compare
primitives/src/lib.rs
Outdated
| pub mod witness; | ||
|
|
||
| #[cfg(feature = "hex")] | ||
| pub(crate) mod hex_codec; |
There was a problem hiding this comment.
| pub(crate) mod hex_codec; | |
| mod hex_codec; |
pub(crate) mod is equivalent to a private module (unless I'm missing something).
There was a problem hiding this comment.
You're right. It is. I tend to try and be more explicit with things than I really need to be.
There was a problem hiding this comment.
hmm, that is fair and we often do such things (e.g., our usage of ref in matching: match *self { Self::Foo(ref e)).
However uniformity is king IMO, if you want to introduce this style I rekon either raise an issue to discuss it or raise a PR that does it everywhere in the repo. If you feel super strongly about it I probably wouldn't NACK it but FWIW it doesn't seem useful to me because it is such a basic language feature. Don't let me hold you back though.
| @@ -0,0 +1,193 @@ | |||
| use core::fmt; | |||
There was a problem hiding this comment.
This file needs an SPDX licence string.
Also some module level docs would be great please. FYI the linter does not enforce this because its a private module.
There was a problem hiding this comment.
And since I'm doing a nit-picky as all hell review:
Can you re-order the file so that HexPrimitive is at the top, the error code is at the bottom, and the hex_write_with_case is below where its called (the fmt::LowerHex and fmt::UpperHex impls).
This is based on the principle that the most important thing goes at the top.
There was a problem hiding this comment.
This is one instance where an additional patch on top of the current ones is the best because it preserves the move-only nature of the existing one. Could also be a follow up PR but I don't rekon Andrew is going to get to merging this before you get back to it.
There was a problem hiding this comment.
I've done this as two new patches, since adding module/license docs is a different operation than rearranging code. If you'd rather it be squashed or reordered, I can do so. Is it acceptable for code to exist for a patch without the license string?
852adb5 to
ddec4b9
Compare
|
Thanks for humouring me and re-arranging the code. I'm aware that not all devs are as retarded as me and have to have everything exactly in the right place. |
ddec4b9 to
42deb5a
Compare
|
Rebased to fix a conflict in |
42deb5a to
7033611
Compare
|
Rebased to fix a conflict in |
7033611 to
5da334d
Compare
|
Rebased to fix a conflict in primitives/src/lib.rs, squashed license + doc patch into move patch, and fixed a bug with the no-alloc from_str that Jamil's tests caught. |
|
Needs rebase again (in the API files, ugh) |
5da334d to
6b38d69
Compare
|
Needs rebase. |
6b38d69 to
b24baea
Compare
|
In 5c22144:
|
b24baea to
0dfb450
Compare
|
Rebased on latest master and should have fixed rbmt test runs for all commits in the chain. |
0dfb450 to
1b3958b
Compare
primitives/src/block.rs
Outdated
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
| fmt::UpperHex::fmt(&crate::hex_codec::HexPrimitive(self), f) | ||
| } | ||
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { crate::hex_codec::HexPrimitive(self).fmt(f) } |
There was a problem hiding this comment.
How does the compiler resolve this call to fmt? Said another way, looking at this change I can't tell if the UpperHex::fmt method is the one being called and not for example Display::fmt?
There was a problem hiding this comment.
But it does because we have a test
// And these should yield uppercase hex
let upper_hex = lower_hex.to_ascii_uppercase();
assert_eq!(upper_hex, format!("{:X}", header));There was a problem hiding this comment.
Is the compiler using trait method function name because the implementation is a simple call through? This seems like a lot of magic. Or is it something to do with the fact that we call the method chained onto the constructor call?
|
I've reviewed this much more in depth now. For the coming suggestions feel free to ignore, use, or do follow up. While reviewing I hacked a bunch locally. |
|
I moved the logic from impl<T: Encodable + Decodable> fmt::Display for HexPrimitive<'_, T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt(f, Case::Lower) }
}In case its useful: /// Writes an Encodable object to the given formatter in the requested case.
fn fmt(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {I messed with the /// Hex encoding wrapper type for `Encodable` + `Decodable` types.
///
/// Implements `Display`, `Debug`, `LowerHex`, and `UpperHex` as well as an inherent `from_str`
/// method that returns `T`.
pub(crate) struct HexPrimitive<'a, T: Encodable + Decodable>(pub &'a T);I messed around with /// Parses a given string into an instance of the type `T`.
///
/// Since `FromStr` would return an instance of `Self` and thus a &T, this function
/// is implemented directly on the struct to return the owned instance of T.
/// Other `FromStr` implementations can directly return the result of
/// [`HexPrimitive::from_str`].
///
/// # Errors
///
/// * `OddLength` or `InvalidChar` if decode the hex string to bytes fails.
/// * `Decode` if consensus decoding the hex-decoded bytes fails.
pub(crate) fn from_str(s: &str) -> Result<T, ParsePrimitiveError<T>> {
// The iterators are ustable (exist but private in 1.0).
let iter = hex_unstable::HexToBytesIter::new(s).map_err(ParsePrimitiveError::from)?;I removed any And finally one last nit, I imported |
|
I investigated the error stuff from |
primitives/src/hex_codec.rs
Outdated
| /// [`ParsePrimitiveError::OddLengthString`] if the input string is an odd length. | ||
| /// [`ParsePrimitiveError::Decode`] if an error occurs during decoding of the object. | ||
| pub(crate) fn from_str(s: &str) -> Result<T, ParsePrimitiveError<T>> { | ||
| let iter = hex_unstable::HexToBytesIter::new(s).map_err(ParsePrimitiveError::from)?; |
There was a problem hiding this comment.
I did rust-bitcoin/hex-conservative#203 to help with this change. No need to wait though because all the unstable stuff is hidden.
The from_str implementation for parsing types from hex strings currently relies on alloc due to the use of hex::decode_to_vec. This locks Header FromStr decoding behind alloc. Implement from_str without alloc, and adjust feature gates on HeaderDecoder to match new relaxed requirements.
The Display/Debug/UpperHex/LowerHex implementations on HexPrimitive all relied on alloc due to the implementation of hex_write_with_case. This locks LowerHex and UpperHex on Header behind alloc, and led to an unnecessary custom implementation of Display. Change hex_write_with_case to remove alloc requirement, and remove corresponding feature gates. Move hex_write_with_case to inherent function on HexPrimitive, and rename to fmt_hex. Replace Display impl on Header with call through to HexPrimitive. Remove feature gates on Header Upper/LowerHex.
The hex_codec module in lib.rs of primitives now makes up 2/3rds of the file. At this scale, it is better suited to be moved into its own file. Move the hex_codec module in lib.rs to a new module file, and import it as a pub(crate) mod.
The hex_codec module was created over various patches and thus doesn't follow best practice for ordering. Errors should be near the bottom, with important types near the top. Rearrange hex_codec to put HexPrimitive at the top, and ParsePrimitiveError at the bottom.
The hex_codec module should be entirely hidden to outside the crate. While all types are pub(crate), the inner field of HexPrimitive was pub.
1b3958b to
118791e
Compare
|
I've incorporated most of your suggestions, @tcharding. I changed the |
The HexPrimitive type in primitives provides encoding and decoding functionality to/from hex for arbitrary Encodable/Decodable types in primitives. Currently, this type relies on alloc to implement this functionality, which is less than ideal for types that don't otherwise require alloc (e.g. Header).
Closes #5469