But I need to modify the slice directly, such as calling read and split_first on it. In this case I don't know the range of the modified slice in raw data.
FYI, pinning a vec most likely doesn't have the effect you indended.
I'd second the advice to spend some effort on avoiding unsafe; it's not woth the hassle to deal with the potential effects of unexpected UB. Try not to use unsafe, especially when you're not that familiar with the more subtile corners of Rust yet; don't use unsafe if there's any equivalently good safe alternative, a safe abstraction in a popular crate, or even a slightly worse (performance-wise) safe alternative (when your code isn't performance-critical).
You can get a subslice's range in the original slice like this:
use std::ops::Range;
fn get_indexes(big_slice: &[u8], subslice: &[u8]) -> Range<usize> {
let range_start = subslice.as_ptr() as usize;
let buffer_start = big_slice.as_ptr() as usize;
let offset = range_start - buffer_start;
let range = offset .. offset+subslice.len();
assert!(big_slice.get(range.clone()).is_some());
range
}
fn main() {
let slice = &[1, 2, 3, 4];
let subslice = &slice[1..2];
println!("{:?}", get_indexes(slice, subslice));
}
split_first doesn't modify the slice. read takes &mut &[u8], reading on it obtained from the Deref impl doesn't magically modify the backing SharedSlice directly. If you want to make two sequential .read() call from the same SharedSlice yield different data, you need to impl Read for SharedSlice anyway.
Yes I know that, it's my fault I didn't speak it correctly. I mean, I want to replace the old data with modified data. In other words, I need replace the original slice with its subslice.
use std::ops::Deref;
use std::rc::Rc;
use ouroboros::self_referencing;
#[self_referencing]
struct SharedSliceInternals {
template: Rc<Vec<u8>>,
#[borrows(template)]
slice: &'this [u8],
}
pub struct SharedSlice(SharedSliceInternals);
impl SharedSlice {
pub fn new(data: Vec<u8>) -> Self {
Self({
SharedSliceInternalsBuilder {
template: Rc::new(data),
slice_builder: |template| template,
}
.build()
})
}
pub fn new_slice(&self, data: &[u8]) -> Option<SharedSlice> {
let template = self.0.borrow_template();
let template_start = template as &[u8] as *const [u8] as *const u8 as usize;
let slice_start = data as &[u8] as *const [u8] as *const u8 as usize;
if slice_start < template_start {
return None;
}
let offset = slice_start - template_start;
dbg!(offset);
if template.len() < offset + data.len() {
return None;
}
let range = offset..offset + data.len();
Some(Self({
SharedSliceInternalsBuilder {
template: Rc::clone(template),
slice_builder: |template| &template[range],
}
.build()
}))
}
// can be used e.g. to call `.read()` on the slice
pub fn with_mut<'a, F, R>(&'a mut self, f: F) -> R
where
F: FnOnce(&'a mut &[u8]) -> R,
{
self.0.with_slice_mut(f)
}
}
impl Deref for SharedSlice {
type Target = [u8];
fn deref(&self) -> &[u8] {
self.0.borrow_slice()
}
}
fn main() {
let x = vec![1, 2, 3, 4, 5, 6, 7];
let mut s = SharedSlice::new(x);
let mut t = s.new_slice(&s[2..]).unwrap();
let mut buf = vec![];
use std::io::Read;
s.with_mut(|s| s.read_to_end(&mut buf)).unwrap();
buf.push(0);
t.with_mut(|t| t.read_to_end(&mut buf)).unwrap();
assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 0, 3, 4, 5, 6, 7]);
}
actually, after writing this, I realize that (almost1) the same API should be possible with a non-self-referencing index-based approach, too, in particular a function like with_mut. You just create a slice on the stack, pass a mutable reference to it to the callback, and after the callback finishes, you update the indices accordingly.
1only difference would be that that approach could never store any slices, e.g. from a &'static [u8] literal, that don't point into the Rc-owned data anymore, while the code above supports such a modification. (It would lead to subsequent as_slice(&self[....]) calls returning None though.)