Skip to content

Commit 10009e3

Browse files
committed
Polish decay based interfaces
In particular we expect these to be infallible by default, the check on length is redundant if implementations perform according to the trait requirements. Thus, we design the APIs around returning a definitive result. This allow using decay as an unobstructive mechanism for 'do not care about layout' through the fact that everything decays to `Bytes`.
1 parent e7e3702 commit 10009e3

File tree

6 files changed

+121
-20
lines changed

6 files changed

+121
-20
lines changed

texel/src/image.rs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,16 @@ impl<L: Layout> Image<L> {
247247
self.inner.decay().into()
248248
}
249249

250+
/// Like [`Self::decay`]` but returns `None` rather than panicking. While this is strictly
251+
/// speaking a violation of the trait contract, you may want to handle this yourself.
252+
pub fn checked_decay<M>(self) -> Option<Image<M>>
253+
where
254+
M: Decay<L>,
255+
M: Layout,
256+
{
257+
Some(self.inner.checked_decay()?.into())
258+
}
259+
250260
/// Move the buffer into a new image.
251261
pub fn take(&mut self) -> Image<L>
252262
where
@@ -550,7 +560,20 @@ impl<'data, L> ImageRef<'data, L> {
550560
/// Decay into a image with less specific layout.
551561
///
552562
/// See [`Image::decay`].
553-
pub fn decay<M>(self) -> Option<ImageRef<'data, M>>
563+
pub fn decay<M>(self) -> ImageRef<'data, M>
564+
where
565+
M: Decay<L>,
566+
M: Layout,
567+
{
568+
self.inner
569+
.checked_decay()
570+
.unwrap_or_else(decay_failed)
571+
.into()
572+
}
573+
574+
/// Like [`Self::decay`]` but returns `None` rather than panicking. While this is strictly
575+
/// speaking a violation of the trait contract, you may want to handle this yourself.
576+
pub fn checked_decay<M>(self) -> Option<ImageRef<'data, M>>
554577
where
555578
M: Decay<L>,
556579
M: Layout,
@@ -828,7 +851,20 @@ impl<'data, L> ImageMut<'data, L> {
828851
/// Decay into a image with less specific layout.
829852
///
830853
/// See [`Image::decay`].
831-
pub fn decay<M>(self) -> Option<ImageMut<'data, M>>
854+
pub fn decay<M>(self) -> ImageMut<'data, M>
855+
where
856+
M: Decay<L>,
857+
M: Layout,
858+
{
859+
self.inner
860+
.checked_decay()
861+
.unwrap_or_else(decay_failed)
862+
.into()
863+
}
864+
865+
/// Like [`Self::decay`]` but returns `None` rather than panicking. While this is strictly
866+
/// speaking a violation of the trait contract, you may want to handle this yourself.
867+
pub fn checked_decay<M>(self) -> Option<ImageMut<'data, M>>
832868
where
833869
M: Decay<L>,
834870
M: Layout,
@@ -1072,6 +1108,15 @@ impl<'data, L> ImageMut<'data, L> {
10721108
}
10731109
}
10741110

1111+
fn decay_failed<T>() -> T {
1112+
#[cold]
1113+
fn decay_failed_inner() -> ! {
1114+
panic!("decayed layout is incompatible with the original layout");
1115+
}
1116+
1117+
decay_failed_inner()
1118+
}
1119+
10751120
impl IntoPlanesError {
10761121
pub(crate) fn from_array<L, Buffer, const N: usize>(
10771122
items: [(Option<L>, Buffer); N],

texel/src/image/atomic.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,19 @@ impl<L: Layout> AtomicImage<L> {
112112
/// ```
113113
///
114114
/// [`Decay`]: ../layout/trait.Decay.html
115+
pub fn decay<M>(self) -> AtomicImage<M>
116+
where
117+
M: Decay<L>,
118+
M: Layout,
119+
{
120+
self.inner
121+
.checked_decay()
122+
.unwrap_or_else(super::decay_failed)
123+
.into()
124+
}
125+
126+
/// Like [`Self::decay`]` but returns `None` rather than panicking. While this is strictly
127+
/// speaking a violation of the trait contract, you may want to handle this yourself.
115128
pub fn checked_decay<M>(self) -> Option<AtomicImage<M>>
116129
where
117130
M: Decay<L>,
@@ -374,6 +387,20 @@ impl<'data, L> AtomicImageRef<'data, L> {
374387
Some(self.inner.try_reinterpret(layout).ok()?.into())
375388
}
376389

390+
/// Decay into a image with less specific layout.
391+
///
392+
/// See [`AtomicImage::decay`].
393+
pub fn decay<M>(self) -> AtomicImageRef<'data, M>
394+
where
395+
M: Decay<L>,
396+
M: Layout,
397+
{
398+
self.inner
399+
.checked_decay()
400+
.unwrap_or_else(super::decay_failed)
401+
.into()
402+
}
403+
377404
/// Decay into a image with less specific layout.
378405
///
379406
/// See [`AtomicImage::checked_decay`].

texel/src/image/cell.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ impl<L: Layout> CellImage<L> {
8181
/// let image: CellImage<layout::Matrix<u8>> = CellImage::new(matrix);
8282
///
8383
/// // to turn hide the `u8` type but keep width, height, texel layout
84-
/// let as_bytes: CellImage<layout::MatrixBytes> = image.clone().checked_decay().unwrap();
84+
/// let as_bytes: CellImage<layout::MatrixBytes> = image.clone().decay();
8585
/// assert_eq!(as_bytes.layout().width(), 400);
8686
/// assert_eq!(as_bytes.layout().height(), 400);
8787
/// ```
@@ -97,11 +97,24 @@ impl<L: Layout> CellImage<L> {
9797
/// let matrix = Matrix::<u8>::width_and_height(400, 400).unwrap();
9898
///
9999
/// // Can always decay to a byte buffer.
100-
/// let bytes: CellImage = CellImage::new(matrix).checked_decay().unwrap();
100+
/// let bytes: CellImage = CellImage::new(matrix).decay();
101101
/// let _: &layout::Bytes = bytes.layout();
102102
/// ```
103103
///
104104
/// [`Decay`]: ../layout/trait.Decay.html
105+
pub fn decay<M>(self) -> CellImage<M>
106+
where
107+
M: Decay<L>,
108+
M: Layout,
109+
{
110+
self.inner
111+
.checked_decay()
112+
.unwrap_or_else(super::decay_failed)
113+
.into()
114+
}
115+
116+
/// Like [`Self::decay`]` but returns `None` rather than panicking. While this is strictly
117+
/// speaking a violation of the trait contract, you may want to handle this yourself.
105118
pub fn checked_decay<M>(self) -> Option<CellImage<M>>
106119
where
107120
M: Decay<L>,
@@ -357,6 +370,20 @@ impl<'data, L> CellImageRef<'data, L> {
357370
Some(self.inner.try_reinterpret(layout).ok()?.into())
358371
}
359372

373+
/// Decay into a image with less specific layout.
374+
///
375+
/// See [`CellImage::decay`].
376+
pub fn decay<M>(self) -> CellImageRef<'data, M>
377+
where
378+
M: Decay<L>,
379+
M: Layout,
380+
{
381+
self.inner
382+
.checked_decay()
383+
.unwrap_or_else(super::decay_failed)
384+
.into()
385+
}
386+
360387
/// Decay into a image with less specific layout.
361388
///
362389
/// See [`CellImage::checked_decay`].

texel/src/image/unaligned.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
//! Design issues:
99
//! - Many methods completely disregard some of the layouts. When we write into an `Image` for
1010
//! instance, we treat it just as a container for bytes under the _input_ layout. We do not
11-
//! interact with the target's layout at all.
11+
//! interact with the target's layout at all. In these cases we request a `Bytes` layout as an
12+
//! explicit opt-in. You can [`decay`][`Image::decay`] all buffer types to conveniently do so at
13+
//! the call site.
1214
//! - Atomic inputs are probably valuable but we can not command users to use our own atomic type.
1315
//! Since atomic sizes must not mix, these kinds of buffers require different copy methods for
1416
//! each single kind of underlying atomic! Not providing these however risks users doing unsound
@@ -21,7 +23,6 @@
2123
//! `Image` by interpreting it as `Image<Relocated<Plane>>`. This however is still wrong. When we
2224
//! write of course the aliasing of other planes is not considered. And we never read the
2325
//! relocation offset, again just disregarding any existing layout data. This is a big trap.
24-
//!
2526
//! - To expand, of course it is also improper to expect that the input buffer contains enough data
2627
//! for the whole image, instead it is probably just that single plane. This messes with the
2728
//! expectations and types involved in the 'copy engine' implementation detail. What is missing
@@ -32,7 +33,6 @@
3233
//! - Do we stack strategies, and how? In particular when copying an array of planes each of which
3334
//! are matrices we want to copy everything row-by-row but right now this would require a
3435
//! specialized engine for `impl Planar<impl MatrixLayout>` instead of being able to compose.
35-
//!
3636
//! - Think of blitting. When we write multiple planes, the input data can contain them completely
3737
//! unaligned but this can not be expressed with properly planar layouts. We not a 'packed
3838
//! planes' type or so that does not implement `PlaneOf` in terms of `Relocated<T>`.
@@ -507,7 +507,7 @@ impl<E: LayoutEngine> AsCopySource<'_, E> {
507507
///
508508
/// Reallocates the image buffer when necessary to ensure that the allocated buffer fits the
509509
/// new data's layout.
510-
pub fn write_to_image(mut self, buffer: Image<impl Layout>) -> Image<E::Layout> {
510+
pub fn write_to_image(mut self, buffer: Image<Bytes>) -> Image<E::Layout> {
511511
let mut buffer = buffer.with_layout(self.engine.consume_layout());
512512
self.engine_to_buf_at(buffer.as_capacity_buf_mut());
513513
buffer
@@ -519,7 +519,7 @@ impl<E: LayoutEngine> AsCopySource<'_, E> {
519519
/// reference to the target buffer that is using the data's layout. Otherwise, returns `None`.
520520
pub fn write_to_mut<'data>(
521521
mut self,
522-
buffer: ImageMut<'data, impl Layout>,
522+
buffer: ImageMut<'data, Bytes>,
523523
) -> Option<ImageMut<'data, E::Layout>> {
524524
let mut buffer = buffer.with_layout(self.engine.consume_layout())?;
525525
self.engine_to_buf_at(buffer.as_mut_buf());
@@ -531,7 +531,7 @@ impl<E: LayoutEngine> AsCopySource<'_, E> {
531531
/// Fails when allocated buffer does not fits the new data's layout.
532532
pub fn write_to_cell_image(
533533
mut self,
534-
buffer: CellImage<impl Layout>,
534+
buffer: CellImage<Bytes>,
535535
) -> Option<CellImage<E::Layout>>
536536
where
537537
E::Layout: Clone + Layout,
@@ -547,7 +547,7 @@ impl<E: LayoutEngine> AsCopySource<'_, E> {
547547
/// reference to the target buffer that is using the data's layout. Otherwise, returns `None`.
548548
pub fn write_to_cell_ref<'data>(
549549
mut self,
550-
buffer: CellImageRef<'data, impl Layout>,
550+
buffer: CellImageRef<'data, Bytes>,
551551
) -> Option<CellImageRef<'data, E::Layout>> {
552552
let buffer = buffer.checked_with_layout(self.engine.consume_layout())?;
553553
self.engine_to_buf_at(buffer.as_cell_buf());
@@ -559,7 +559,7 @@ impl<E: LayoutEngine> AsCopySource<'_, E> {
559559
/// Fails when allocated buffer does not fits the new data's layout.
560560
pub fn write_to_atomic_image(
561561
mut self,
562-
buffer: AtomicImage<impl Layout>,
562+
buffer: AtomicImage<Bytes>,
563563
) -> Option<AtomicImage<E::Layout>>
564564
where
565565
E::Layout: Clone + Layout,
@@ -575,7 +575,7 @@ impl<E: LayoutEngine> AsCopySource<'_, E> {
575575
/// reference to the target buffer that is using the data's layout. Otherwise, returns `None`.
576576
pub fn write_to_atomic_ref<'data>(
577577
mut self,
578-
buffer: AtomicImageRef<'data, impl Layout>,
578+
buffer: AtomicImageRef<'data, Bytes>,
579579
) -> Option<AtomicImageRef<'data, E::Layout>>
580580
where
581581
E::Layout: Clone + Layout,
@@ -615,23 +615,23 @@ impl<E: LayoutEngine> AsCopyTarget<'_, E> {
615615
///
616616
/// This reads data up to our layout. It does not interpret the data with the layout of
617617
/// the argument buffer.
618-
pub fn read_from_ref(&mut self, buffer: ImageRef<'_, impl Layout>) {
618+
pub fn read_from_ref(&mut self, buffer: ImageRef<'_, Bytes>) {
619619
self.engine_from_buf_at(buffer.as_buf());
620620
}
621621

622622
/// Read out data from a borrowed buffer.
623623
///
624624
/// This reads data up to our layout. It does not interpret the data with the layout of
625625
/// the argument buffer.
626-
pub fn read_from_cell_ref(&mut self, buffer: CellImageRef<'_, impl Layout>) {
626+
pub fn read_from_cell_ref(&mut self, buffer: CellImageRef<'_, Bytes>) {
627627
self.engine_from_buf_at(buffer.as_cell_buf());
628628
}
629629

630630
/// Read out data from a borrowed buffer.
631631
///
632632
/// This reads data up to our layout. It does not interpret the data with the layout of
633633
/// the argument buffer.
634-
pub fn read_from_atomic_ref(&mut self, buffer: CellImageRef<'_, impl Layout>) {
634+
pub fn read_from_atomic_ref(&mut self, buffer: CellImageRef<'_, Bytes>) {
635635
self.engine_from_buf_at(buffer.as_cell_buf());
636636
}
637637
}

texel/src/layout.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ impl dyn Layout + '_ {
9292
///
9393
/// In general, a layout `L` should implement `Decay<T>` if any image with layouts of type `T` is
9494
/// also valid for some layout of type `L`. A common example would be if a crate strictly adds more
95-
/// information to a predefined layout, then it should also decay to that layout.
95+
/// information to a predefined layout, then it should also decay to that layout. It is invalid to
96+
/// decay to a layout that somehow expands outside the initial layout, you must weaken the buffer
97+
/// required.
9698
///
9799
/// Also note that this trait is not reflexive, in contrast to `From` and `Into` which are. This
98100
/// avoids collisions in impls. In particular, it allows adding blanket impls of the form

texel/tests/data_transfer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ fn planar_io() {
3030
let target = Image::new(layout.clone());
3131

3232
let data = DataRef::with_layout_at(input.data, input.layout, 0).unwrap();
33-
let reinterpreted = data.as_source().write_to_image(target);
33+
let reinterpreted = data.as_source().write_to_image(target.decay());
3434

3535
// Layout must match, as must the bytes within the layout.
3636
assert_eq!(*reinterpreted.layout(), input.layout);
@@ -45,11 +45,11 @@ fn planar_io() {
4545
assert_ne!(small.as_buf().as_bytes(), input.data);
4646
assert_ne!(post.as_buf().as_bytes(), input.data);
4747

48-
assert!(data.as_source().write_to_mut(small).is_none());
48+
assert!(data.as_source().write_to_mut(small.decay()).is_none());
4949

5050
let post = data
5151
.as_source()
52-
.write_to_mut(post)
52+
.write_to_mut(post.decay())
5353
.expect("that plane was large enough");
5454
// Modify that other independent plane for good measure.
5555
pre.as_mut_buf().fill(0);

0 commit comments

Comments
 (0)