Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: add new types of CodecErrors #135

Merged
merged 3 commits into from
Jan 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- Add `array:codec::{InvalidBytesLengthError,InvalidArrayShapeError,InvalidNumberOfElementsError,SubsetOutOfBoundsError}`
- Add `ArraySubset::inbounds_shape()` (matches the old `ArraySubset::inbounds` behaviour)

### Changed
- **Breaking**: change `ArraySubset::inbounds` to take another subset rather than a shape
- **Breaking**: `CodecError` enum changes:
- Change `CodecError::UnexpectedChunkDecodedSize` to an `InvalidBytesLengthError`
- Add `CodecError::{InvalidArrayShape,InvalidNumberOfElements,SubsetOutOfBounds}`

## [0.19.1] - 2025-01-19

Expand Down
5 changes: 1 addition & 4 deletions zarrs/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -910,10 +910,7 @@
) -> Result<ndarray::ArrayD<T>, ArrayError> {
let length = elements.len();
ndarray::ArrayD::<T>::from_shape_vec(iter_u64_to_usize(shape.iter()), elements).map_err(|_| {
ArrayError::CodecError(codec::CodecError::UnexpectedChunkDecodedSize(
length * size_of::<T>(),
shape.iter().product::<u64>() * size_of::<T>() as u64,
))
ArrayError::CodecError(codec::InvalidArrayShapeError::new(shape.to_vec(), length).into())

Check warning on line 913 in zarrs/src/array.rs

View check run for this annotation

Codecov / codecov/patch

zarrs/src/array.rs#L913

Added line #L913 was not covered by tests
})
}

Expand Down
21 changes: 11 additions & 10 deletions zarrs/src/array/array_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
metadata::v3::array::data_type::DataTypeSize,
};

use super::{codec::CodecError, ravel_indices, ArraySize, DataType, FillValue};
use super::{
codec::{CodecError, InvalidBytesLengthError},
ravel_indices, ArraySize, DataType, FillValue,
};

/// Array element bytes.
///
Expand Down Expand Up @@ -217,14 +220,11 @@
}

/// Validate fixed length array bytes for a given array size.
fn validate_bytes_flen(bytes: &RawBytes, array_size: u64) -> Result<(), CodecError> {
if bytes.len() as u64 == array_size {
fn validate_bytes_flen(bytes: &RawBytes, array_size: usize) -> Result<(), InvalidBytesLengthError> {
if bytes.len() == array_size {
Ok(())
} else {
Err(CodecError::UnexpectedChunkDecodedSize(
bytes.len(),
array_size,
))
Err(InvalidBytesLengthError::new(bytes.len(), array_size))

Check warning on line 227 in zarrs/src/array/array_bytes.rs

View check run for this annotation

Codecov / codecov/patch

zarrs/src/array/array_bytes.rs#L227

Added line #L227 was not covered by tests
}
}

Expand Down Expand Up @@ -259,9 +259,10 @@
data_type_size: DataTypeSize,
) -> Result<(), CodecError> {
match (bytes, data_type_size) {
(ArrayBytes::Fixed(bytes), DataTypeSize::Fixed(data_type_size)) => {
validate_bytes_flen(bytes, num_elements * data_type_size as u64)
}
(ArrayBytes::Fixed(bytes), DataTypeSize::Fixed(data_type_size)) => Ok(validate_bytes_flen(
bytes,
usize::try_from(num_elements * data_type_size as u64).unwrap(),
)?),
(ArrayBytes::Variable(bytes, offsets), DataTypeSize::Variable) => {
validate_bytes_vlen(bytes, offsets, num_elements)
}
Expand Down
85 changes: 83 additions & 2 deletions zarrs/src/array/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
pub mod metadata_options;
pub mod options;

use derive_more::derive::Display;
pub use metadata_options::CodecMetadataOptions;
pub use options::{CodecOptions, CodecOptionsBuilder};

Expand Down Expand Up @@ -79,6 +80,7 @@

mod bytes_partial_encoder_default;
pub use bytes_partial_encoder_default::BytesPartialEncoderDefault;
use zarrs_metadata::ArrayShape;

use crate::storage::{StoreKeyOffsetValue, WritableStorage};
use crate::{
Expand Down Expand Up @@ -964,6 +966,76 @@
}
}

/// An error indicating the length of bytes does not match the expected length.
#[derive(Debug, Error, Display)]
#[display("Invalid bytes len {len}, expected {expected_len}")]
pub struct InvalidBytesLengthError {
len: usize,
expected_len: usize,
}

impl InvalidBytesLengthError {
/// Create a new [`InvalidBytesLengthError`].
#[must_use]
pub fn new(len: usize, expected_len: usize) -> Self {
Self { len, expected_len }
}

Check warning on line 982 in zarrs/src/array/codec.rs

View check run for this annotation

Codecov / codecov/patch

zarrs/src/array/codec.rs#L980-L982

Added lines #L980 - L982 were not covered by tests
}

/// An error indicating the shape is not compatible with the expected number of elements.
#[derive(Debug, Error, Display)]
#[display("Invalid shape {shape:?} for number of elements {expected_num_elements}")]
pub struct InvalidArrayShapeError {
shape: ArrayShape,
expected_num_elements: usize,
}

impl InvalidArrayShapeError {
/// Create a new [`InvalidArrayShapeError`].
#[must_use]
pub fn new(shape: ArrayShape, expected_num_elements: usize) -> Self {
Self {
shape,
expected_num_elements,
}
}

Check warning on line 1001 in zarrs/src/array/codec.rs

View check run for this annotation

Codecov / codecov/patch

zarrs/src/array/codec.rs#L996-L1001

Added lines #L996 - L1001 were not covered by tests
}

/// An error indicating the length of elements does not match the expected length.
#[derive(Debug, Error, Display)]
#[display("Invalid number of elements {num}, expected {expected}")]
pub struct InvalidNumberOfElementsError {
num: u64,
expected: u64,
}

impl InvalidNumberOfElementsError {
/// Create a new [`InvalidNumberOfElementsError`].
#[must_use]
pub fn new(num: u64, expected: u64) -> Self {
Self { num, expected }
}

Check warning on line 1017 in zarrs/src/array/codec.rs

View check run for this annotation

Codecov / codecov/patch

zarrs/src/array/codec.rs#L1015-L1017

Added lines #L1015 - L1017 were not covered by tests
}

/// An array subset is out of bounds.
#[derive(Debug, Error, Display)]
#[display("Subset {subset} is out of bounds of {must_be_within}")]
pub struct SubsetOutOfBoundsError {
subset: ArraySubset,
must_be_within: ArraySubset,
}

impl SubsetOutOfBoundsError {
/// Create a new [`InvalidNumberOfElementsError`].
#[must_use]
pub fn new(subset: ArraySubset, must_be_within: ArraySubset) -> Self {
Self {
subset,
must_be_within,
}
}

Check warning on line 1036 in zarrs/src/array/codec.rs

View check run for this annotation

Codecov / codecov/patch

zarrs/src/array/codec.rs#L1031-L1036

Added lines #L1031 - L1036 were not covered by tests
}

/// A codec error.
#[derive(Debug, Error)]
pub enum CodecError {
Expand All @@ -980,8 +1052,8 @@
#[error("the array subset {_0} has the wrong dimensionality, expected {_1}")]
InvalidArraySubsetDimensionalityError(ArraySubset, usize),
/// The decoded size of a chunk did not match what was expected.
#[error("the size of a decoded chunk is {_0}, expected {_1}")]
UnexpectedChunkDecodedSize(usize, u64),
#[error("the size of a decoded chunk is {}, expected {}", _0.len, _0.expected_len)]
UnexpectedChunkDecodedSize(#[from] InvalidBytesLengthError),
/// An embedded checksum does not match the decoded value.
#[error("the checksum is invalid")]
InvalidChecksum,
Expand All @@ -1006,6 +1078,15 @@
/// Expected variable length bytes.
#[error("Expected variable length array bytes")]
ExpectedVariableLengthBytes,
/// Invalid array shape.
#[error(transparent)]
InvalidArrayShape(#[from] InvalidArrayShapeError),
/// Invalid number of elements.
#[error(transparent)]
InvalidNumberOfElements(#[from] InvalidNumberOfElementsError),
/// Subset out of bounds.
#[error(transparent)]
SubsetOutOfBounds(#[from] SubsetOutOfBoundsError),
}

impl From<&str> for CodecError {
Expand Down
13 changes: 6 additions & 7 deletions zarrs/src/array/codec/array_to_bytes/bytes/bytes_codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
ArrayCodecTraits, ArrayPartialDecoderTraits, ArrayPartialEncoderDefault,
ArrayPartialEncoderTraits, ArrayToBytesCodecTraits, BytesPartialDecoderTraits,
BytesPartialEncoderTraits, CodecError, CodecMetadataOptions, CodecOptions, CodecTraits,
RecommendedConcurrency,
InvalidBytesLengthError, RecommendedConcurrency,
},
ArrayBytes, BytesRepresentation, ChunkRepresentation, DataTypeSize, RawBytes,
},
Expand Down Expand Up @@ -76,12 +76,11 @@
));
}
DataTypeSize::Fixed(data_type_size) => {
let array_size = decoded_representation.num_elements() * data_type_size as u64;
if value.len() as u64 != array_size {
return Err(CodecError::UnexpectedChunkDecodedSize(
value.len(),
array_size,
));
let array_size =
usize::try_from(decoded_representation.num_elements() * data_type_size as u64)
.unwrap();
if value.len() != array_size {
return Err(InvalidBytesLengthError::new(value.len(), array_size).into());

Check warning on line 83 in zarrs/src/array/codec/array_to_bytes/bytes/bytes_codec.rs

View check run for this annotation

Codecov / codecov/patch

zarrs/src/array/codec/array_to_bytes/bytes/bytes_codec.rs#L83

Added line #L83 was not covered by tests
} else if data_type_size > 1 && self.endian.is_none() {
return Err(CodecError::Other(format!(
"tried to encode an array with element size {data_type_size} with endianness None"
Expand Down
7 changes: 2 additions & 5 deletions zarrs/src/array/codec/array_to_bytes/vlen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
};
use crate::{
array::{
codec::{ArrayToBytesCodecTraits, CodecError, CodecOptions},
codec::{ArrayToBytesCodecTraits, CodecError, CodecOptions, InvalidBytesLengthError},
convert_from_bytes_slice, ChunkRepresentation, CodecChain, DataType, Endianness, FillValue,
RawBytes,
},
Expand Down Expand Up @@ -62,10 +62,7 @@
) -> Result<(Vec<u8>, Vec<usize>), CodecError> {
// Get the index length and data start
if bytes.len() < size_of::<u64>() {
return Err(CodecError::UnexpectedChunkDecodedSize(
bytes.len(),
size_of::<u64>() as u64,
));
return Err(InvalidBytesLengthError::new(bytes.len(), size_of::<u64>()).into());

Check warning on line 65 in zarrs/src/array/codec/array_to_bytes/vlen.rs

View check run for this annotation

Codecov / codecov/patch

zarrs/src/array/codec/array_to_bytes/vlen.rs#L65

Added line #L65 was not covered by tests
}
let index_len = u64::from_le_bytes(bytes[0..size_of::<u64>()].try_into().unwrap());
let index_len = usize::try_from(index_len)
Expand Down
10 changes: 5 additions & 5 deletions zarrs/src/array/codec/array_to_bytes/vlen_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
pub(crate) const IDENTIFIER: &str = "vlen_v2";
// pub use vlen_v2::IDENTIFIER;

use crate::array::{codec::CodecError, RawBytes};
use crate::array::{
codec::{CodecError, InvalidBytesLengthError},
RawBytes,
};

pub(crate) use vlen_v2_codec::VlenV2Codec;

Expand Down Expand Up @@ -67,10 +70,7 @@
// Validate the bytes is long enough to contain header and element lengths
let header_length = size_of::<u32>() * (1 + num_elements);
if bytes.len() < header_length {
return Err(CodecError::UnexpectedChunkDecodedSize(
bytes.len(),
header_length as u64,
));
return Err(InvalidBytesLengthError::new(bytes.len(), header_length).into());

Check warning on line 73 in zarrs/src/array/codec/array_to_bytes/vlen_v2.rs

View check run for this annotation

Codecov / codecov/patch

zarrs/src/array/codec/array_to_bytes/vlen_v2.rs#L73

Added line #L73 was not covered by tests
}

// Validate the number of elements from the header
Expand Down
16 changes: 9 additions & 7 deletions zarrs/src/array/codec/bytes_to_bytes/gdeflate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

use crate::{
array::{
codec::{Codec, CodecError, CodecPlugin},
codec::{Codec, CodecError, CodecPlugin, InvalidBytesLengthError},
RawBytes,
},
metadata::v3::{array::codec::gdeflate, MetadataV3},
Expand Down Expand Up @@ -61,10 +61,11 @@

fn gdeflate_decode(encoded_value: &RawBytes<'_>) -> Result<Vec<u8>, CodecError> {
if encoded_value.len() < GDEFLATE_STATIC_HEADER_LENGTH {
return Err(CodecError::UnexpectedChunkDecodedSize(
return Err(InvalidBytesLengthError::new(

Check warning on line 64 in zarrs/src/array/codec/bytes_to_bytes/gdeflate.rs

View check run for this annotation

Codecov / codecov/patch

zarrs/src/array/codec/bytes_to_bytes/gdeflate.rs#L64

Added line #L64 was not covered by tests
encoded_value.len(),
GDEFLATE_STATIC_HEADER_LENGTH as u64,
));
GDEFLATE_STATIC_HEADER_LENGTH,
)
.into());

Check warning on line 68 in zarrs/src/array/codec/bytes_to_bytes/gdeflate.rs

View check run for this annotation

Codecov / codecov/patch

zarrs/src/array/codec/bytes_to_bytes/gdeflate.rs#L66-L68

Added lines #L66 - L68 were not covered by tests
}

// Decode the static header
Expand All @@ -77,10 +78,11 @@
// Check length of dynamic header
let dynamic_header_length = num_pages * size_of::<u64>();
if encoded_value.len() < GDEFLATE_STATIC_HEADER_LENGTH + dynamic_header_length {
return Err(CodecError::UnexpectedChunkDecodedSize(
return Err(InvalidBytesLengthError::new(

Check warning on line 81 in zarrs/src/array/codec/bytes_to_bytes/gdeflate.rs

View check run for this annotation

Codecov / codecov/patch

zarrs/src/array/codec/bytes_to_bytes/gdeflate.rs#L81

Added line #L81 was not covered by tests
encoded_value.len(),
(GDEFLATE_STATIC_HEADER_LENGTH + dynamic_header_length) as u64,
));
GDEFLATE_STATIC_HEADER_LENGTH + dynamic_header_length,
)
.into());

Check warning on line 85 in zarrs/src/array/codec/bytes_to_bytes/gdeflate.rs

View check run for this annotation

Codecov / codecov/patch

zarrs/src/array/codec/bytes_to_bytes/gdeflate.rs#L83-L85

Added lines #L83 - L85 were not covered by tests
}

// Decode the pages
Expand Down
Loading