Skip to content

Commit 6829e3e

Browse files
Add section handles for ByteArea
1 parent 47851c0 commit 6829e3e

File tree

5 files changed

+81
-7
lines changed

5 files changed

+81
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- added example demonstrating `ByteArea` with multiple typed sections, concurrent mutations, and freezing or persisting the area
55
- added example combining Python bindings with winnow parsing
66
- added Python example demonstrating structured parsing with winnow's `view`
7+
- added `SectionHandle` for reconstructing sections from a frozen `ByteArea`
78
- added `ByteSource` support for `VecDeque<T>` when `zerocopy` is enabled and kept the deque as owner
89
- added `ByteSource` support for `Cow<'static, T>` where `T: AsRef<[u8]>`
910
- added `ByteArea` for staged file writes with `Section::freeze()` to return `Bytes`

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ To map only a portion of a file use the unsafe helper
103103

104104
### Byte Area
105105

106-
Use `ByteArea` to incrementally build immutable bytes on disk:
106+
Use `ByteArea` to incrementally build immutable bytes on disk; each section can
107+
yield a handle that reconstructs its range after the area is frozen:
107108

108109
```rust
109110
use anybytes::area::ByteArea;
@@ -112,11 +113,12 @@ let mut area = ByteArea::new().unwrap();
112113
let mut sections = area.sections();
113114
let mut section = sections.reserve::<u8>(4).unwrap();
114115
section.copy_from_slice(b"test");
116+
let handle = section.handle();
115117
let bytes = section.freeze().unwrap();
116-
assert_eq!(bytes.as_ref(), b"test".as_ref());
117118
drop(sections);
118119
let all = area.freeze().unwrap();
119-
assert_eq!(all.as_ref(), b"test".as_ref());
120+
assert_eq!(handle.bytes(&all).as_ref(), bytes.as_ref());
121+
assert_eq!(handle.view(&all).unwrap().as_ref(), b"test".as_ref());
120122
```
121123

122124
Call `area.persist(path)` to keep the temporary file instead of mapping it.

examples/byte_area.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ fn main() -> std::io::Result<()> {
1212
raw.as_mut_slice().copy_from_slice(b"test");
1313
nums.as_mut_slice().copy_from_slice(&[1, 2]);
1414

15+
// Store handles so we can later extract the sections from the frozen area.
16+
let handle_raw = raw.handle();
17+
let handle_nums = nums.handle();
18+
1519
// Freeze the sections into immutable `Bytes`.
1620
let frozen_raw: Bytes = raw.freeze()?;
1721
let frozen_nums: Bytes = nums.freeze()?;
@@ -25,8 +29,8 @@ fn main() -> std::io::Result<()> {
2529
if memory_or_file {
2630
// Freeze the whole area into immutable `Bytes`.
2731
let all: Bytes = area.freeze()?;
28-
assert_eq!(&all[..4], b"test");
29-
assert_eq!(all.slice(4..).view::<[u32]>().unwrap().as_ref(), &[1, 2]);
32+
assert_eq!(handle_raw.bytes(&all).as_ref(), frozen_raw.as_ref());
33+
assert_eq!(handle_nums.view(&all).unwrap().as_ref(), &[1, 2]);
3034
} else {
3135
// Persist the temporary file.
3236
let dir = tempdir()?;

src/area.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,19 @@
2626
//!
2727
//! let mut a = sections.reserve::<u8>(1).unwrap();
2828
//! a.as_mut_slice()[0] = 1;
29+
//! let handle_a = a.handle();
2930
//!
3031
//! let mut b = sections.reserve::<u32>(1).unwrap();
3132
//! b.as_mut_slice()[0] = 2;
33+
//! let handle_b = b.handle();
3234
//!
3335
//! let bytes_a = a.freeze().unwrap();
3436
//! let bytes_b = b.freeze().unwrap();
3537
//! drop(sections);
3638
//! let all = area.freeze().unwrap();
3739
//!
38-
//! assert_eq!(bytes_a.as_ref(), &[1]);
39-
//! assert_eq!(bytes_b.as_ref(), &2u32.to_ne_bytes());
40+
//! assert_eq!(handle_a.bytes(&all).as_ref(), &[1]);
41+
//! assert_eq!(handle_b.bytes(&all).as_ref(), &2u32.to_ne_bytes());
4042
//!
4143
//! let mut expected = Vec::new();
4244
//! expected.extend_from_slice(&[1]);
@@ -58,6 +60,9 @@ use crate::Bytes;
5860
#[cfg(feature = "zerocopy")]
5961
use zerocopy::{FromBytes, Immutable};
6062

63+
#[cfg(feature = "zerocopy")]
64+
use crate::view::{View, ViewError};
65+
6166
/// Alignment helper.
6267
fn align_up(val: usize, align: usize) -> usize {
6368
(val + align - 1) & !(align - 1)
@@ -135,6 +140,7 @@ impl<'area> SectionWriter<'area> {
135140
Ok(Section {
136141
mmap,
137142
offset,
143+
start,
138144
elems,
139145
_marker: PhantomData,
140146
})
@@ -148,6 +154,8 @@ pub struct Section<'arena, T> {
148154
mmap: memmap2::MmapMut,
149155
/// Offset from the beginning of `mmap` to the start of the buffer.
150156
offset: usize,
157+
/// Absolute start offset within the [`ByteArea`] in bytes.
158+
start: usize,
151159
/// Number of elements in the buffer.
152160
elems: usize,
153161
/// Marker tying the section to the area and element type.
@@ -176,6 +184,15 @@ where
176184
let map = self.mmap.make_read_only()?;
177185
Ok(Bytes::from_source(map).slice(offset..offset + len_bytes))
178186
}
187+
188+
/// Return a handle that can reconstruct this section from a frozen [`ByteArea`].
189+
pub fn handle(&self) -> SectionHandle<T> {
190+
SectionHandle {
191+
offset: self.start,
192+
len: self.elems * core::mem::size_of::<T>(),
193+
_type: PhantomData,
194+
}
195+
}
179196
}
180197

181198
impl<'arena, T> core::ops::Deref for Section<'arena, T>
@@ -221,3 +238,29 @@ where
221238
self
222239
}
223240
}
241+
242+
/// Handle referencing a [`Section`] within a frozen [`ByteArea`].
243+
#[derive(Clone, Copy, Debug)]
244+
pub struct SectionHandle<T> {
245+
/// Absolute byte offset from the start of the area.
246+
pub offset: usize,
247+
/// Length of the section in bytes.
248+
pub len: usize,
249+
/// Marker for the element type stored in the section.
250+
_type: PhantomData<T>,
251+
}
252+
253+
impl<T> SectionHandle<T>
254+
where
255+
T: FromBytes + Immutable,
256+
{
257+
/// Extract the raw bytes for this section from `area`.
258+
pub fn bytes<'a>(&self, area: &'a Bytes) -> Bytes {
259+
area.slice(self.offset..self.offset + self.len)
260+
}
261+
262+
/// Interpret the section as a typed [`View`].
263+
pub fn view(&self, area: &Bytes) -> Result<View<[T]>, ViewError> {
264+
self.bytes(area).view::<[T]>()
265+
}
266+
}

tests/area.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,27 @@ proptest! {
7373
prop_assert_eq!(all.as_ref(), expected.as_slice());
7474
}
7575
}
76+
77+
#[test]
78+
fn handles_reconstruct_sections() {
79+
let mut area = ByteArea::new().expect("area");
80+
let mut sections = area.sections();
81+
82+
let mut a = sections.reserve::<u8>(1).expect("reserve u8");
83+
a.as_mut_slice()[0] = 1;
84+
let handle_a = a.handle();
85+
let bytes_a = a.freeze().expect("freeze a");
86+
87+
let mut b = sections.reserve::<u32>(1).expect("reserve u32");
88+
b.as_mut_slice()[0] = 2;
89+
let handle_b = b.handle();
90+
let bytes_b = b.freeze().expect("freeze b");
91+
92+
drop(sections);
93+
let all = area.freeze().expect("freeze area");
94+
95+
assert_eq!(handle_a.bytes(&all).as_ref(), bytes_a.as_ref());
96+
assert_eq!(handle_b.bytes(&all).as_ref(), bytes_b.as_ref());
97+
assert_eq!(handle_a.view(&all).unwrap().as_ref(), &[1]);
98+
assert_eq!(handle_b.view(&all).unwrap().as_ref(), &[2]);
99+
}

0 commit comments

Comments
 (0)