Skip to content

Commit 3dc7801

Browse files
committed
Replace Arc<Vec<u8>> with Storage
Storage stores small bytes inline, and spills to the heap only once that is no longer possible.
1 parent 5f86f15 commit 3dc7801

File tree

3 files changed

+113
-27
lines changed

3 files changed

+113
-27
lines changed

misc/multiaddr/src/lib.rs

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod protocol;
66
mod onion_addr;
77
mod errors;
88
mod from_url;
9+
mod storage;
910

1011
use serde::{
1112
Deserialize,
@@ -18,12 +19,13 @@ use std::{
1819
convert::TryFrom,
1920
fmt,
2021
io,
22+
hash,
2123
iter::FromIterator,
2224
net::{IpAddr, Ipv4Addr, Ipv6Addr},
2325
result::Result as StdResult,
2426
str::FromStr,
25-
sync::Arc
2627
};
28+
use storage::Storage;
2729
pub use self::errors::{Result, Error};
2830
pub use self::from_url::{FromUrlErr, from_url, from_url_lossy};
2931
pub use self::protocol::Protocol;
@@ -36,28 +38,23 @@ static_assertions::const_assert! {
3638
}
3739

3840
/// Representation of a Multiaddr.
39-
#[derive(PartialEq, Eq, Clone, Hash)]
40-
pub struct Multiaddr { bytes: Arc<Vec<u8>> }
41+
#[derive(Clone)]
42+
pub struct Multiaddr { storage: Storage }
4143

4244
impl Multiaddr {
4345
/// Create a new, empty multiaddress.
4446
pub fn empty() -> Self {
45-
Self { bytes: Arc::new(Vec::new()) }
46-
}
47-
48-
/// Create a new, empty multiaddress with the given capacity.
49-
pub fn with_capacity(n: usize) -> Self {
50-
Self { bytes: Arc::new(Vec::with_capacity(n)) }
47+
Self { storage: Storage::from_slice(&[]) }
5148
}
5249

5350
/// Return the length in bytes of this multiaddress.
5451
pub fn len(&self) -> usize {
55-
self.bytes.len()
52+
self.as_ref().len()
5653
}
5754

5855
/// Return a copy of this [`Multiaddr`]'s byte representation.
5956
pub fn to_vec(&self) -> Vec<u8> {
60-
Vec::from(&self.bytes[..])
57+
self.as_ref().to_vec()
6158
}
6259

6360
/// Adds an already-parsed address component to the end of this multiaddr.
@@ -73,9 +70,10 @@ impl Multiaddr {
7370
/// ```
7471
///
7572
pub fn push(&mut self, p: Protocol<'_>) {
76-
let mut w = io::Cursor::<&mut Vec<u8>>::new(Arc::make_mut(&mut self.bytes));
73+
let mut w = io::Cursor::new(self.to_vec());
7774
w.set_position(w.get_ref().len() as u64);
78-
p.write_bytes(&mut w).expect("Writing to a `io::Cursor<&mut Vec<u8>>` never fails.")
75+
p.write_bytes(&mut w).expect("Writing to a `io::Cursor<&mut Vec<u8>>` never fails.");
76+
self.storage = Storage::from_slice(&w.into_inner());
7977
}
8078

8179
/// Pops the last `Protocol` of this multiaddr, or `None` if the multiaddr is empty.
@@ -89,7 +87,7 @@ impl Multiaddr {
8987
/// ```
9088
///
9189
pub fn pop<'a>(&mut self) -> Option<Protocol<'a>> {
92-
let mut slice = &self.bytes[..]; // the remaining multiaddr slice
90+
let mut slice = self.as_ref(); // the remaining multiaddr slice
9391
if slice.is_empty() {
9492
return None
9593
}
@@ -100,16 +98,17 @@ impl Multiaddr {
10098
}
10199
slice = s
102100
};
103-
let remaining_len = self.bytes.len() - slice.len();
104-
Arc::make_mut(&mut self.bytes).truncate(remaining_len);
101+
let remaining_len = self.as_ref().len() - slice.len();
102+
self.storage = Storage::from_slice(&self.as_ref()[..remaining_len]);
105103
Some(protocol)
106104
}
107105

108106
/// Like [`Multiaddr::push`] but consumes `self`.
109107
pub fn with(mut self, p: Protocol<'_>) -> Self {
110-
let mut w = io::Cursor::<&mut Vec<u8>>::new(Arc::make_mut(&mut self.bytes));
108+
let mut w = io::Cursor::new(self.to_vec());
111109
w.set_position(w.get_ref().len() as u64);
112110
p.write_bytes(&mut w).expect("Writing to a `io::Cursor<&mut Vec<u8>>` never fails.");
111+
self.storage = Storage::from_slice(&w.into_inner());
113112
self
114113
}
115114

@@ -130,7 +129,7 @@ impl Multiaddr {
130129
/// ```
131130
///
132131
pub fn iter(&self) -> Iter<'_> {
133-
Iter(&self.bytes)
132+
Iter(&self.as_ref())
134133
}
135134

136135
/// Replace a [`Protocol`] at some position in this `Multiaddr`.
@@ -145,7 +144,7 @@ impl Multiaddr {
145144
where
146145
F: FnOnce(&Protocol) -> Option<Protocol<'a>>
147146
{
148-
let mut address = Multiaddr::with_capacity(self.len());
147+
let mut address = Multiaddr::empty();
149148
let mut fun = Some(by);
150149
let mut replaced = false;
151150

@@ -192,9 +191,23 @@ impl fmt::Display for Multiaddr {
192191
}
193192
}
194193

194+
impl PartialEq for Multiaddr {
195+
fn eq(&self, other: &Self) -> bool {
196+
self.as_ref() == other.as_ref()
197+
}
198+
}
199+
200+
impl Eq for Multiaddr {}
201+
202+
impl hash::Hash for Multiaddr {
203+
fn hash<H: hash::Hasher>(&self, state: &mut H) {
204+
self.as_ref().hash(state);
205+
}
206+
}
207+
195208
impl AsRef<[u8]> for Multiaddr {
196209
fn as_ref(&self) -> &[u8] {
197-
self.bytes.as_ref()
210+
self.storage.bytes()
198211
}
199212
}
200213

@@ -203,7 +216,7 @@ impl<'a> IntoIterator for &'a Multiaddr {
203216
type IntoIter = Iter<'a>;
204217

205218
fn into_iter(self) -> Iter<'a> {
206-
Iter(&self.bytes)
219+
Iter(&self.as_ref())
207220
}
208221
}
209222

@@ -216,7 +229,7 @@ impl<'a> FromIterator<Protocol<'a>> for Multiaddr {
216229
for cmp in iter {
217230
cmp.write_bytes(&mut writer).expect("Writing to a `Vec` never fails.");
218231
}
219-
Multiaddr { bytes: Arc::new(writer) }
232+
Multiaddr { storage: Storage::from_slice(&writer) }
220233
}
221234
}
222235

@@ -237,7 +250,7 @@ impl FromStr for Multiaddr {
237250
p.write_bytes(&mut writer).expect("Writing to a `Vec` never fails.");
238251
}
239252

240-
Ok(Multiaddr { bytes: Arc::new(writer) })
253+
Ok(Multiaddr { storage: Storage::from_slice(&writer) })
241254
}
242255
}
243256

@@ -264,7 +277,7 @@ impl<'a> From<Protocol<'a>> for Multiaddr {
264277
fn from(p: Protocol<'a>) -> Multiaddr {
265278
let mut w = Vec::new();
266279
p.write_bytes(&mut w).expect("Writing to a `Vec` never fails.");
267-
Multiaddr { bytes: Arc::new(w) }
280+
Multiaddr { storage: Storage::from_slice(&w) }
268281
}
269282
}
270283

@@ -299,7 +312,7 @@ impl TryFrom<Vec<u8>> for Multiaddr {
299312
let (_, s) = Protocol::from_bytes(slice)?;
300313
slice = s
301314
}
302-
Ok(Multiaddr { bytes: Arc::new(v) })
315+
Ok(Multiaddr { storage: Storage::from_slice(&v) })
303316
}
304317
}
305318

misc/multiaddr/src/storage.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use std::sync::Arc;
2+
3+
/// MAX_INLINE is the maximum size of a multiaddr that can be stored inline
4+
const MAX_INLINE: usize = 38;
5+
6+
#[derive(Clone)]
7+
pub(crate) enum Storage {
8+
/// hash is stored inline if it is smaller than MAX_INLINE
9+
Inline(u8, [u8; MAX_INLINE]),
10+
/// hash is stored on the heap. this must be only used if the hash is actually larger than
11+
/// MAX_INLINE bytes to ensure an unique representation.
12+
Heap(Arc<[u8]>),
13+
}
14+
15+
impl Storage {
16+
/// The raw bytes.
17+
pub fn bytes(&self) -> &[u8] {
18+
match self {
19+
Storage::Inline(len, bytes) => &bytes[..(*len as usize)],
20+
Storage::Heap(data) => &data,
21+
}
22+
}
23+
24+
/// creates storage from a vec. For a size up to MAX_INLINE, this will not allocate.
25+
pub fn from_slice(slice: &[u8]) -> Self {
26+
let len = slice.len();
27+
if len <= MAX_INLINE {
28+
let mut data: [u8; MAX_INLINE] = [0; MAX_INLINE];
29+
data[..len].copy_from_slice(slice);
30+
Storage::Inline(len as u8, data)
31+
} else {
32+
Storage::Heap(slice.into())
33+
}
34+
}
35+
}
36+
37+
#[cfg(test)]
38+
mod tests {
39+
use super::{Storage, MAX_INLINE};
40+
use quickcheck::quickcheck;
41+
42+
#[test]
43+
fn struct_size() {
44+
// this should be true for both 32 and 64 bit archs
45+
assert_eq!(std::mem::size_of::<Storage>(), 40);
46+
}
47+
48+
#[test]
49+
fn roundtrip() {
50+
// check that .bytes() returns whatever the storage was created with
51+
for i in 0..((MAX_INLINE + 10) as u8) {
52+
let data = (0..i).collect::<Vec<u8>>();
53+
let storage = Storage::from_slice(&data);
54+
assert_eq!(data, storage.bytes());
55+
}
56+
}
57+
58+
fn check_invariants(storage: Storage) -> bool {
59+
match storage {
60+
Storage::Inline(len, _) => len as usize <= MAX_INLINE,
61+
Storage::Heap(arc) => arc.len() > MAX_INLINE,
62+
}
63+
}
64+
65+
quickcheck! {
66+
fn roundtrip_check(data: Vec<u8>) -> bool {
67+
let storage = Storage::from_slice(&data);
68+
storage.bytes() == data.as_slice() && check_invariants(storage)
69+
}
70+
}
71+
}

misc/multiaddr/tests/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ fn push_pop_identity() {
5050
let mut b = a.clone();
5151
let q = p.clone();
5252
b.0.push(q.0);
53-
assert_ne!(a.0, b.0);
54-
Some(p.0) == b.0.pop() && a.0 == b.0
53+
let c1 = a.0 != b.0;
54+
let c2 = Some(p.0) == b.0.pop();
55+
let c3 = a.0 == b.0;
56+
c1 && c2 && c3
5557
}
5658
QuickCheck::new().quickcheck(prop as fn(Ma, Proto) -> bool)
5759
}

0 commit comments

Comments
 (0)