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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
use std::{
alloc::{Layout, alloc, alloc_zeroed, dealloc},
fmt::Debug,
mem::MaybeUninit,
ptr::null_mut,
};
use crate::{
error::{Error, Result},
util::{CACHE_LINE_BYTE_SIZE, tracing_wrappers::*},
};
use super::Rect;
#[derive(Debug, Clone, Copy)]
pub(super) struct RawImageBuffer {
// Safety invariant:
// - uninit data is never written to `buf`.
// - `buf` is valid for reads for all bytes in the ranges
// `buf[i*bytes_between_rows..i*bytes_between_rows+bytes_per_row]` for all values of `i`
// from `0` to `num_rows-1`.
// - all the bytes in those ranges (and in between) are part of the same allocated object.
// - if `num_rows > 0`, then `bytes_per_row > 0`, `bytes_per_row <= bytes_between_rows`.
// - The computation `E = bytes_between_rows * (num_rows-1) + bytes_per_row` does not
// overflow and has a result that is at most `isize::MAX`, or num_rows is 0.
// We will call the bytes in the range above the "accessible bytes" of a RawImageBuffer.
// Note that wrapper structs around RawImageBuffer might have a safety invariant that declares
// that the struct has write access to the accessible bytes of a RawImageBuffer; in that case,
// that is equivalent to saying that `buf` is *also* valid for *writes*.
buf: *mut MaybeUninit<u8>,
bytes_per_row: usize,
num_rows: usize,
bytes_between_rows: usize,
}
// SAFETY: The safety invariant on RawImageBuffer enforces ownership rules on the contained data,
// there is no reason for it not to be Send.
unsafe impl Send for RawImageBuffer {}
// SAFETY: RawImageBuffer does not use any kind of interior mutability, so it is safe to share
// between threads.
unsafe impl Sync for RawImageBuffer {}
impl RawImageBuffer {
pub(super) fn check_vals(num_rows: usize, bytes_per_row: usize, bytes_between_rows: usize) {
if num_rows > 0 {
assert!(bytes_per_row > 0);
assert!(bytes_between_rows >= bytes_per_row);
assert!(
bytes_between_rows
.checked_mul(num_rows - 1)
.unwrap()
.checked_add(bytes_per_row)
.unwrap()
<= isize::MAX as usize
);
}
}
/// Checks that self.buf, self.bytes_per_row, self.bytes_between_rows are all multiple of align.
/// This guarantees that `self.get_row()` and `self.get_row_mut()` return slices aligned to
/// `align`.
#[inline(always)]
pub(super) fn is_aligned(&self, align: usize) -> bool {
self.bytes_per_row.is_multiple_of(align)
&& self.bytes_between_rows.is_multiple_of(align)
&& (self.buf as usize).is_multiple_of(align)
}
/// Creates a new RawImageBuffer from raw pointers.
/// It is guaranteed that `buf` will never be used to write uninitialized data.
///
/// # Safety
/// - `buf` must be valid for reads for all bytes in the range
/// `buf[i*bytes_between_rows..i*bytes_between_rows+bytes_per_row]` for all values of `i`
/// from `0` to `num_rows-1`.
/// - The bytes in these ranges must not be accessed as long as the returned `Self` is in scope.
/// - All the bytes in those ranges (and in between) must be part of the same allocated object.
///
/// Note: these safety requirements match those of JxlOutputBuffer::new_from_ptr, except we
/// only request validity for reads.
pub(super) unsafe fn new_from_ptr(
buf: *mut MaybeUninit<u8>,
num_rows: usize,
bytes_per_row: usize,
bytes_between_rows: usize,
) -> Self {
RawImageBuffer::check_vals(num_rows, bytes_per_row, bytes_between_rows);
// SAFETY: caller guarantees the buf-related requirements, and other safety invariants are
// checked in check_vals.
Self {
buf,
bytes_per_row,
bytes_between_rows,
num_rows,
}
}
fn empty() -> Self {
// Safety note: the safety invariant is trivially verified.
RawImageBuffer {
buf: null_mut(),
bytes_per_row: 0,
num_rows: 0,
bytes_between_rows: 0,
}
}
/// Returns the minimum size that the allocation containing the image data must have, or 0 if
/// this is an empty image.
pub(super) fn minimum_allocation_size(&self) -> usize {
if self.num_rows == 0 {
0
} else {
// Note: the safety invariant guarantees no overflow.
(self.num_rows - 1) * self.bytes_between_rows + self.bytes_per_row
}
}
#[inline]
pub(super) fn byte_size(&self) -> (usize, usize) {
(self.bytes_per_row, self.num_rows)
}
/// # Safety
/// - No uninit data must be written to the returned slice.
/// - The caller must ensure that ownership rules are respected (for example, because they
/// have exclusive access to the data).
#[inline(always)]
pub(super) unsafe fn row_mut(&mut self, row: usize) -> &mut [MaybeUninit<u8>] {
// SAFETY: The safety requirements for distinct_rows_mut match the ones for row_mut.
unsafe { self.distinct_rows_mut([row])[0] }
}
/// Note: this is quadratic in the number of rows.
/// # Safety
/// - No uninit data must be written to the returned slice.
/// - The caller must ensure that ownership rules are respected (for example, because they
/// have exclusive access to the data).
#[inline(always)]
pub(super) unsafe fn distinct_rows_mut<I: DistinctRowsIndexes>(
&mut self,
rows: I,
) -> I::Output<'_, MaybeUninit<u8>> {
// SAFETY: the safety requirements of `get_rows_mut` are the same as the ones for
// `distinct_rows_mut`.
unsafe { rows.get_rows_mut(self) }
}
/// # Safety
/// The caller must ensure that ownership and lifetime rules are respected (for example,
/// because they have shared access to the data).
#[inline(always)]
pub(super) unsafe fn row(&self, row: usize) -> &[MaybeUninit<u8>] {
assert!(row < self.num_rows);
let start = row * self.bytes_between_rows;
// SAFETY: `start` is guaranteed to be <= isize::MAX, and `self.buf + start` is guaranteed
// to fit within the same allocated object, as per safety invariants of this struct.
// We checked above that `row` and `cols` satisfy the requirements to apply the safety
// invariant.
let start = unsafe { self.buf.add(start) };
// SAFETY: due to the struct safety invariant, we know the entire slice is in a range of
// memory valid for reads. The caller guarantees aliasing rules will not be violated.
unsafe { std::slice::from_raw_parts(start, self.bytes_per_row) }
}
/// Extracts a sub-rectangle from this buffer. Rectangle coordinates are in bytes.
/// Safety note: the returned RawImageBuffer retains the same kind of access (read or write) as `self`.
pub(super) fn rect(&self, rect: Rect) -> Self {
if rect.size.0 == 0 || rect.size.1 == 0 {
return Self::empty();
}
// More helpful message in debug builds (not needed for safety).
if cfg!(debug_assertions) {
rect.check_within(self.byte_size());
}
assert!(rect.origin.1 < self.num_rows);
assert!(rect.origin.1.checked_add(rect.size.1).unwrap() <= self.num_rows);
assert!(rect.origin.0 < self.bytes_per_row);
assert!(rect.origin.0.checked_add(rect.size.0).unwrap() <= self.bytes_per_row);
// SAFETY: the safety invariant of `self`, together with the above check, guarantees that
// the calculation does not overflow and that the new pointer stays within the bounds of
// the allocation.
let start_ptr = unsafe {
self.buf
.add(rect.origin.1 * self.bytes_between_rows + rect.origin.0)
};
// SAFETY: Thanks to the check above, all the bytes accessible from `buf` at the
// appropriate ranges are a subset of the ones accessible from `self.buf` at the
// correct ranges for `self`. Thus, the safety invariant of `self` ensures that
// the safety preconditions of this call are satisfied.
unsafe { Self::new_from_ptr(start_ptr, rect.size.1, rect.size.0, self.bytes_between_rows) }
}
/// Returns zeroed memory if `uninit` is `false`, otherwise it returns uninitialized
/// memory. The returned buffer is aligned to CACHE_LINE_BYTE_SIZE bytes.
/// The returned RawImageBuffer owns the memory it references, which belongs to a single
/// allocation of size minimum_allocation_size().
pub(super) fn try_allocate(byte_size: (usize, usize), uninit: bool) -> Result<RawImageBuffer> {
let (bytes_per_row, num_rows) = byte_size;
// To simplify modular transform logic, we allow empty images, because some modular
// meta-images can have 0 bytes_per_row or num_rows (e.g. delta-palette, reference property image).
if bytes_per_row == 0 || num_rows == 0 {
return Ok(RawImageBuffer::empty());
}
// These limits let us not worry about overflows.
if bytes_per_row as u64 >= i64::MAX as u64 / 4 || num_rows as u64 >= i64::MAX as u64 / 4 {
return Err(Error::ImageSizeTooLarge(bytes_per_row, num_rows));
}
debug!("trying to allocate image");
let bytes_between_rows =
bytes_per_row.div_ceil(CACHE_LINE_BYTE_SIZE) * CACHE_LINE_BYTE_SIZE;
// Note: matches RawImageBuffer::minimum_allocation_size.
let allocation_len = (num_rows - 1)
.checked_mul(bytes_between_rows)
.unwrap()
.checked_add(bytes_per_row)
.unwrap();
assert_ne!(allocation_len, 0);
let layout = Layout::from_size_align(allocation_len, CACHE_LINE_BYTE_SIZE).unwrap();
// SAFETY: we just checked that allocation_len is not 0.
let memory = unsafe {
if uninit {
alloc(layout)
} else {
alloc_zeroed(layout)
}
};
if memory.is_null() {
return Err(Error::ImageOutOfMemory(bytes_per_row, num_rows));
}
// SAFETY: `memory` points to a contiguous array of size minimum_allocation_size(), and we
// transfer ownership so the validity requirements are satisfied.
Ok(unsafe {
RawImageBuffer::new_from_ptr(
memory as *mut MaybeUninit<u8>,
num_rows,
bytes_per_row,
bytes_between_rows,
)
})
}
/// Returns a copy of the current buffer contents in a new buffer that owns the returned data.
/// The data is allocated with `try_allocate` so that it matches the size of the current image.
///
/// This function is meant to be used when `self` is an owned buffer, and will panic if the
/// bytes between rows of a newly allocated image with the same size does not match the value
/// for `self`.
///
/// # Safety
/// The caller must ensure that the data referenced by self -- *all*
/// self.minimum_allocation_size() bytes starting from self.buf, not just the accessible bytes
/// -- can be read.
pub(super) unsafe fn try_clone(&self) -> Result<Self> {
let out = RawImageBuffer::try_allocate(self.byte_size(), true)?;
assert_eq!(self.bytes_per_row, out.bytes_per_row);
assert_eq!(self.bytes_between_rows, out.bytes_between_rows);
assert_eq!(self.num_rows, out.num_rows);
let data_len = self.minimum_allocation_size();
assert_eq!(
self.minimum_allocation_size(),
out.minimum_allocation_size()
);
if data_len != 0 {
// SAFETY: since both `self` and `out` own the allocation, which has size `data_len`, this copy
// is safe.
unsafe {
std::ptr::copy_nonoverlapping(self.buf, out.buf, data_len);
}
}
Ok(out)
}
/// Deallocates an owning buffer that was allocated by try_allocate.
///
/// # Safety
/// The data referenced by `self` must have been allocated with Self::try_allocate.
pub(super) unsafe fn deallocate(&mut self) {
if !self.buf.is_null() {
let allocation_len = self.minimum_allocation_size();
let layout = Layout::from_size_align(allocation_len, CACHE_LINE_BYTE_SIZE).unwrap();
// SAFETY: the buffer was allocated in `try_allocate` with the same layout.
unsafe {
dealloc(self.buf as *mut u8, layout);
}
}
}
}
#[allow(private_interfaces)]
pub trait DistinctRowsIndexes {
type Output<'a, T: 'static>;
/// # Safety
/// - No uninit data must be written to the returned slice.
/// - The caller must ensure that ownership rules are respected (for example, because they
/// have exclusive access to the data).
unsafe fn get_rows_mut<'a>(
&self,
image: &'a mut RawImageBuffer,
) -> Self::Output<'a, MaybeUninit<u8>>;
/// # Safety
/// - The rows are properly aligned
/// - The rows contain data that is valid for type T (and thus initialized).
unsafe fn transmute_rows<'a, T: 'static>(
rows: Self::Output<'a, MaybeUninit<u8>>,
) -> Self::Output<'a, T>;
}
#[allow(private_interfaces)]
impl<const S: usize> DistinctRowsIndexes for [usize; S] {
type Output<'a, T: 'static> = [&'a mut [T]; S];
#[inline(always)]
unsafe fn get_rows_mut<'a>(
&self,
image: &'a mut RawImageBuffer,
) -> Self::Output<'a, MaybeUninit<u8>> {
for i in 0..S {
assert!(self[i] < image.num_rows);
for j in i + 1..S {
assert_ne!(self[i], self[j]);
}
}
let start = self.map(|row| row * image.bytes_between_rows);
let start = start.map(|start| {
// SAFETY: `start` is guaranteed to be <= isize::MAX, and `self.buf + start` is guaranteed
// to fit within the same allocated object, as per safety invariants of the image struct.
// We checked above that `row` satisfies the requirements to apply the safety invariant.
unsafe { image.buf.add(start) }
});
start.map(|start| {
// SAFETY: due to the struct safety invariant, we know the entire slice is in a range of
// memory valid for writes. Moreover, the caller promises not to write uninitialized
// data in the returned slice. Finally, the caller guarantees aliasing rules will not
// be violated outside of this struct, and since we checked that all the values of
// `self` are distinct they are also not violated across the various slices returned by
unsafe { std::slice::from_raw_parts_mut(start, image.bytes_per_row) }
})
}
#[inline(always)]
unsafe fn transmute_rows<'a, T: 'static>(
rows: Self::Output<'a, MaybeUninit<u8>>,
) -> Self::Output<'a, T> {
rows.map(|row| {
// SAFETY: The caller guarantees the transmute is safe and proper alignment.
unsafe {
std::slice::from_raw_parts_mut(
row.as_mut_ptr().cast::<T>(),
row.len() / std::mem::size_of::<T>(),
)
}
})
}
}