Skip to content

Commit 02cf114

Browse files
committed
Explicity store the bytes size
We can afford it since the average hash is 34 bytes, and we want this thing to be a multiple of 8 large.
1 parent 0559bed commit 02cf114

File tree

3 files changed

+25
-44
lines changed

3 files changed

+25
-44
lines changed

src/lib.rs

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,7 @@ impl Multihash {
212212

213213
/// Returns the bytes representation of this multihash.
214214
pub fn as_bytes(&self) -> &[u8] {
215-
let bytes = self.storage.bytes();
216-
let size = multihash_size(bytes).expect("storage contains a valid multihash");
217-
&bytes[..size]
215+
self.storage.bytes()
218216
}
219217

220218
/// Builds a `MultihashRef` corresponding to this `Multihash`.
@@ -261,30 +259,6 @@ pub struct MultihashRef<'a> {
261259
bytes: &'a [u8],
262260
}
263261

264-
/// Given a buffer starting with a valid multihash, returns the size of the multihash
265-
fn multihash_size(input: &[u8]) -> Result<usize, DecodeError> {
266-
if input.is_empty() {
267-
return Err(DecodeError::BadInputLength);
268-
}
269-
let mut res = 0usize;
270-
271-
// Ensure `Hash::code` returns a `u16` so that our `decode::u16` here is correct.
272-
std::convert::identity::<fn(Hash) -> u16>(Hash::code);
273-
let (code, bytes) = decode::u16(&input).map_err(|_| DecodeError::BadInputLength)?;
274-
275-
// Very convoluted way to get the size of the code
276-
let mut tmp = [0u8; 3];
277-
res += unsigned_varint::encode::u16(code, &mut tmp).len();
278-
279-
let (hash_len, _) = decode::u32(&bytes).map_err(|_| DecodeError::BadInputLength)?;
280-
281-
// Very convoluted way to get the size of the hash_len
282-
let mut tmp = [0u8; 5];
283-
res += unsigned_varint::encode::u32(hash_len, &mut tmp).len();
284-
res += hash_len as usize;
285-
Ok(res)
286-
}
287-
288262
impl<'a> MultihashRef<'a> {
289263
/// Creates a `MultihashRef` from the given `input`.
290264
pub fn from_slice(input: &'a [u8]) -> Result<Self, DecodeError> {

src/storage.rs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
11
use std::sync::Arc;
22

3-
const MAX_INLINE: usize = 39;
3+
const MAX_INLINE: usize = 38;
44

55
#[derive(Clone)]
66
pub enum Storage {
7-
/// hash is stored inline. if it is smaller than 39 bytes it should be padded with 0u8
8-
Inline([u8; MAX_INLINE]),
7+
/// hash is stored inline if it is smaller than MAX_INLINE
8+
Inline(u8, [u8; MAX_INLINE]),
99
/// hash is stored on the heap. this must be only used if the hash is actually larger than
10-
/// 39 bytes to ensure an unique representation.
10+
/// MAX_INLINE bytes to ensure an unique representation.
1111
Heap(Arc<[u8]>),
1212
}
1313

1414
impl Storage {
15-
/// The raw bytes. Note that this can be longer than the data this storage has been created from.
15+
/// The raw bytes.
1616
pub fn bytes(&self) -> &[u8] {
1717
match self {
18-
Storage::Inline(bytes) => bytes,
18+
Storage::Inline(len, bytes) => &bytes[..(*len as usize)],
1919
Storage::Heap(data) => &data,
2020
}
2121
}
2222

23-
/// creates storage from a vec. Note that this will not preserve the size.
23+
/// creates storage from a vec. For a size up to MAX_INLINE, this will not allocate.
2424
pub fn from_slice(slice: &[u8]) -> Self {
25-
if slice.len() <= MAX_INLINE {
25+
let len = slice.len();
26+
if len <= MAX_INLINE {
2627
let mut data: [u8; MAX_INLINE] = [0; MAX_INLINE];
27-
data[..slice.len()].copy_from_slice(slice);
28-
Storage::Inline(data)
28+
data[..len].copy_from_slice(slice);
29+
Storage::Inline(len as u8, data)
2930
} else {
3031
Storage::Heap(slice.into())
3132
}
@@ -34,10 +35,21 @@ impl Storage {
3435

3536
#[cfg(test)]
3637
mod tests {
37-
use super::Storage;
38+
use super::{Storage, MAX_INLINE};
3839

3940
#[test]
40-
fn test_size() {
41+
fn struct_size() {
42+
// this should be true for both 32 and 64 bit archs
4143
assert_eq!(std::mem::size_of::<Storage>(), 40);
4244
}
45+
46+
#[test]
47+
fn roundtrip() {
48+
// check that .bytes() returns whatever the storage was created with
49+
for i in 0..((MAX_INLINE + 10) as u8) {
50+
let data = (0..i).collect::<Vec<u8>>();
51+
let storage = Storage::from_slice(&data);
52+
assert_eq!(data, storage.bytes());
53+
}
54+
}
4355
}

tests/lib.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,3 @@ fn multihash_ref_errors() {
276276
"Should error on wrong hash length"
277277
);
278278
}
279-
280-
#[test]
281-
fn multihash_size() {
282-
assert_eq!(std::mem::size_of::<Multihash>(), 40);
283-
}

0 commit comments

Comments
 (0)