blob: 28c886509d4b422109197a09cbd117ec5c5a3fbb [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use std::io;
use std::sync::atomic::{AtomicU64, Ordering};
pub struct ReadRequest<'a> {
pub pos: u64,
pub buf: &'a mut [u8],
}
impl<'a> ReadRequest<'a> {
pub fn new(pos: u64, buf: &'a mut [u8]) -> Self {
Self { pos, buf }
}
}
/// Positional reader shared by Tantivy file handles.
///
/// Implementations must be safe to call concurrently. Each call may contain one
/// or more disjoint read requests, and callers may issue multiple `pread` calls
/// at the same time through shared references.
pub trait SeekRead: Send + Sync {
fn pread(&self, ranges: &mut [ReadRequest<'_>]) -> io::Result<()>;
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct FullTextReadMetrics {
pub pread_calls: u64,
pub pread_ranges: u64,
pub pread_bytes: u64,
pub cache_hits: u64,
pub cache_misses: u64,
pub cache_evictions: u64,
pub cached_blocks: u64,
}
#[derive(Default)]
pub(crate) struct ReadMetrics {
pread_calls: AtomicU64,
pread_ranges: AtomicU64,
pread_bytes: AtomicU64,
cache_hits: AtomicU64,
cache_misses: AtomicU64,
cache_evictions: AtomicU64,
cached_blocks: AtomicU64,
}
impl ReadMetrics {
pub(crate) fn record_pread(&self, ranges: &[ReadRequest<'_>]) {
self.pread_calls.fetch_add(1, Ordering::Relaxed);
self.pread_ranges
.fetch_add(ranges.len() as u64, Ordering::Relaxed);
self.pread_bytes.fetch_add(
ranges.iter().map(|range| range.buf.len() as u64).sum(),
Ordering::Relaxed,
);
}
pub(crate) fn record_cache_hit(&self) {
self.cache_hits.fetch_add(1, Ordering::Relaxed);
}
pub(crate) fn record_cache_miss(&self) {
self.cache_misses.fetch_add(1, Ordering::Relaxed);
}
pub(crate) fn record_cache_insert(&self) {
self.cached_blocks.fetch_add(1, Ordering::Relaxed);
}
pub(crate) fn record_cache_eviction(&self) {
self.cache_evictions.fetch_add(1, Ordering::Relaxed);
self.cached_blocks.fetch_sub(1, Ordering::Relaxed);
}
pub(crate) fn snapshot(&self) -> FullTextReadMetrics {
FullTextReadMetrics {
pread_calls: self.pread_calls.load(Ordering::Relaxed),
pread_ranges: self.pread_ranges.load(Ordering::Relaxed),
pread_bytes: self.pread_bytes.load(Ordering::Relaxed),
cache_hits: self.cache_hits.load(Ordering::Relaxed),
cache_misses: self.cache_misses.load(Ordering::Relaxed),
cache_evictions: self.cache_evictions.load(Ordering::Relaxed),
cached_blocks: self.cached_blocks.load(Ordering::Relaxed),
}
}
}
pub trait SeekWrite: Send {
fn write_all(&mut self, buf: &[u8]) -> io::Result<()>;
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
pub struct PosWriter<W> {
inner: W,
}
impl<W> PosWriter<W> {
pub fn new(inner: W) -> Self {
Self { inner }
}
pub fn into_inner(self) -> W {
self.inner
}
}
impl<W: io::Write + Send> SeekWrite for PosWriter<W> {
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
io::Write::write_all(&mut self.inner, buf)
}
fn flush(&mut self) -> io::Result<()> {
io::Write::flush(&mut self.inner)
}
}
pub struct SliceReader {
data: Vec<u8>,
}
impl SliceReader {
pub fn new(data: Vec<u8>) -> Self {
Self { data }
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
impl SeekRead for SliceReader {
fn pread(&self, ranges: &mut [ReadRequest<'_>]) -> io::Result<()> {
for range in ranges {
let start = usize::try_from(range.pos)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "offset overflow"))?;
let end = start
.checked_add(range.buf.len())
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "range overflow"))?;
if end > self.data.len() {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"read past end of slice",
));
}
range.buf.copy_from_slice(&self.data[start..end]);
}
Ok(())
}
}