1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use sqsh_sys as ffi;
use sqsh_sys::SqshMapper;
use std::ffi::{c_int, c_void};
use std::ptr::NonNull;
pub(crate) struct SourceVtable<S> {
inner: ffi::SqshMemoryMapperImpl,
_phantom: std::marker::PhantomData<fn() -> S>,
}
impl<S: Source> SourceVtable<S> {
pub(crate) fn mapper_impl(&self) -> &ffi::SqshMemoryMapperImpl {
&self.inner
}
pub const fn new() -> Self {
let block_size_hint = S::BLOCK_SIZE_HINT;
assert!(
block_size_hint > 0,
"BLOCK_SIZE_HINT must be greater than 0"
);
let imp = ffi::SqshMemoryMapperImpl {
block_size_hint,
init: None,
map: None,
init2: Some(init2::<S>),
map2: Some(map2::<S>),
unmap: Some(unmap::<S>),
cleanup: Some(cleanup::<S>),
};
Self {
inner: imp,
_phantom: std::marker::PhantomData,
}
}
}
impl<S: Source> Default for SourceVtable<S> {
fn default() -> Self {
Self::new()
}
}
/// A trait for mapping sections of an archive into memory.
///
/// # Safety
///
/// Implementors must ensure that the `map` function returns a valid pointer to a buffer of `size`
/// bytes, and that the buffer remains valid until the `unmap` function is called.
pub unsafe trait Source {
/// The default block size if one is not passed explicitly.
///
/// Must not be 0.
const BLOCK_SIZE_HINT: usize;
/// Retrieve the size of the archive.
fn size(&mut self) -> crate::error::Result<u64>;
/// Map a section of a source into memory.
///
/// The `offset` parameter is the offset in the archive starting from the
/// beginning of the archive. That means that the mapper does not need to know about the
/// configured offset for the archive. The `size` parameter is the size of the chunk
/// that should be read. This function will return either a pointer to a buffer of `size` bytes
/// or an error. The buffer must be allocated by the implementation and the pointer must be
/// valid until the `unmap` function is called.
///
/// The size requested may _not_ be the same as the BLOCK_SIZE_HINT, and the implementation
/// must be able to handle any size requested.
///
/// # Safety
///
/// `map` must never be called for the same offset and size while the buffer is still mapped.
/// The function must return a pointer to a buffer of `size` bytes.
/// The buffer must remain valid until the `unmap` function is called.
unsafe fn map(&mut self, offset: u64, size: usize) -> crate::error::Result<*mut u8>;
/// Unmap a section of a source from memory.
///
/// The `ptr` will be a pointer which was returned by the `map` function, and `size` will be the
/// size passed to that map call.
///
/// # Safety
///
/// `ptr` must be a pointer returned by the `map` function, and `size` must be the size passed
/// to that call.
unsafe fn unmap(&mut self, ptr: *mut u8, size: usize) -> crate::error::Result<()>;
}
pub(crate) fn to_ptr<S: Source>(source: S) -> *mut c_void {
let s_ptr = if size_of::<S>() == 0 {
NonNull::dangling().as_ptr()
} else {
Box::into_raw(Box::new(source))
};
s_ptr.cast()
}
extern "C" fn init2<S: Source>(
mapper: *mut SqshMapper,
input: *const c_void,
size: *mut u64,
) -> c_int {
let input = input.cast_mut();
unsafe {
let source: &mut S = &mut *input.cast::<S>();
*size = match source.size() {
Ok(size) => size,
Err(e) => return e.to_ffi_result(),
};
ffi::sqsh_mapper_set_user_data(mapper, input);
}
0
}
extern "C" fn cleanup<S: Source>(mapper: *mut SqshMapper) -> c_int {
if size_of::<S>() != 0 {
drop(unsafe { Box::from_raw(ffi::sqsh_mapper_user_data(mapper).cast::<S>()) });
}
0
}
extern "C" fn map2<S: Source>(
mapper: *const SqshMapper,
offset: u64,
size: usize,
ptr: *mut *mut u8,
) -> c_int {
let source: &mut S = unsafe { &mut *ffi::sqsh_mapper_user_data(mapper).cast::<S>() };
let res = unsafe { source.map(offset, size) };
match res {
Ok(p) => {
unsafe { *ptr = p };
0
}
Err(e) => e.to_ffi_result(),
}
}
extern "C" fn unmap<S: Source>(mapper: *const SqshMapper, ptr: *mut u8, size: usize) -> c_int {
let source: &mut S = unsafe { &mut *ffi::sqsh_mapper_user_data(mapper).cast::<S>() };
let res = unsafe { source.unmap(ptr, size) };
match res {
Ok(()) => 0,
Err(e) => e.to_ffi_result(),
}
}