Skip to content

Commit

Permalink
Tweak docs and loosen suffix restrictions
Browse files Browse the repository at this point in the history
  • Loading branch information
tustvold committed Jan 4, 2024
1 parent f5fcc53 commit e20b130
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 25 deletions.
55 changes: 47 additions & 8 deletions object_store/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,14 +583,7 @@ pub trait ObjectStore: std::fmt::Display + Send + Sync + Debug + 'static {
/// Return the bytes that are stored at the specified location
/// in the given byte range.
///
/// If the given range is zero-length or starts after the end of the resource,
/// an error will be returned.
/// If the given range starts in a valid location but ends after the end of the resource,
/// the valid sub-range will be returned:
/// this will start at the requested index but will be shorter than expected.
///
/// If you need to know the exact returned range, you can use [ObjectStore::get_opts] instead;
/// the [GetResult] object contains the actual range.
/// See [`GetRange::Bounded`] for more details on how `range` gets interpreted
async fn get_range(&self, location: &Path, range: Range<usize>) -> Result<Bytes> {
let options = GetOptions {
range: Some(range.into()),
Expand Down Expand Up @@ -1333,6 +1326,52 @@ mod tests {
// Should be a non-fatal error
out_of_range_result.unwrap_err();

let opts = GetOptions {
range: Some(GetRange::Bounded(2..100)),
..Default::default()
};
let result = storage.get_opts(&location, opts).await.unwrap();
assert_eq!(result.range, 2..14);
assert_eq!(result.meta.size, 14);
let bytes = result.bytes().await.unwrap();
assert_eq!(bytes, b"bitrary data".as_ref());

let opts = GetOptions {
range: Some(GetRange::Suffix(2)),
..Default::default()
};
let result = storage.get_opts(&location, opts).await.unwrap();
assert_eq!(result.range, 12..14);
assert_eq!(result.meta.size, 14);
let bytes = result.bytes().await.unwrap();
assert_eq!(bytes, b"ta".as_ref());

let opts = GetOptions {
range: Some(GetRange::Suffix(100)),
..Default::default()
};
let result = storage.get_opts(&location, opts).await.unwrap();
assert_eq!(result.range, 0..14);
assert_eq!(result.meta.size, 14);
let bytes = result.bytes().await.unwrap();
assert_eq!(bytes, b"arbitrary data".as_ref());

let opts = GetOptions {
range: Some(GetRange::Offset(3)),
..Default::default()
};
let result = storage.get_opts(&location, opts).await.unwrap();
assert_eq!(result.range, 3..14);
assert_eq!(result.meta.size, 14);
let bytes = result.bytes().await.unwrap();
assert_eq!(bytes, b"itrary data".as_ref());

let opts = GetOptions {
range: Some(GetRange::Offset(100)),
..Default::default()
};
storage.get_opts(&location, opts).await.unwrap_err();

let ranges = vec![0..1, 2..3, 0..5];
let bytes = storage.get_ranges(&location, &ranges).await.unwrap();
for (range, bytes) in ranges.iter().zip(bytes) {
Expand Down
32 changes: 15 additions & 17 deletions object_store/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ fn merge_ranges(ranges: &[Range<usize>], coalesce: usize) -> Vec<Range<usize>> {
ret
}

/// A single range in a `Range` request.
/// Request only a portion of an object's bytes
///
/// These can be created from [usize] ranges, like
///
Expand All @@ -184,23 +184,28 @@ fn merge_ranges(ranges: &[Range<usize>], coalesce: usize) -> Vec<Range<usize>> {
/// let range3: GetRange = (50..).into();
/// let range4: GetRange = (..150).into();
/// ```
///
/// Implementations may wish to inspect [`GetResult`] for the exact byte
/// range returned.
///
/// [`GetResult`]: crate::GetResult
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum GetRange {
/// A bounded byte range.
/// Request a specific range of bytes
///
/// If the given range is zero-length or starts after the end of the object,
/// an error will be returned. Additionally, if the range ends after the end
/// of the object, the entire remainder of the object will be returned.
/// Otherwise, the exact requested range will be returned.
Bounded(Range<usize>),
/// A range defined only by the first byte requested (requests all remaining bytes).
/// Request all bytes starting from a given byte offset
Offset(usize),
/// A range defined as the number of bytes at the end of the resource.
/// Request up to the last n bytes
Suffix(usize),
}

#[derive(Debug, Snafu)]
pub(crate) enum InvalidGetRange {
#[snafu(display(
"Wanted suffix of {requested} bytes, but resource was only {length} bytes long"
))]
SuffixTooLarge { requested: usize, length: usize },

#[snafu(display(
"Wanted range starting at {requested}, but resource was only {length} bytes long"
))]
Expand Down Expand Up @@ -250,14 +255,7 @@ impl GetRange {
Ok(*o..len)
}
}
Self::Suffix(n) => {
len.checked_sub(*n)
.map(|start| start..len)
.ok_or(InvalidGetRange::SuffixTooLarge {
requested: *n,
length: len,
})
}
Self::Suffix(n) => Ok(len.saturating_sub(*n)..len),
}
}
}
Expand Down

0 comments on commit e20b130

Please sign in to comment.