Skip to content
Open
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
28 changes: 27 additions & 1 deletion src/text_with_rank_support/condensed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use crate::{
maybe_savefile::MaybeSavefile, sealed::Sealed,
};

use super::block::{Block, Block64};
use super::{
block::{Block, Block64},
prefetch_index,
};

use num_traits::{NumCast, PrimInt};
use rayon::prelude::*;
Expand All @@ -30,15 +33,18 @@ pub struct CondensedTextWithRankSupport<I, B = Block64> {
}

impl<I: IndexStorage, B: Block> CondensedTextWithRankSupport<I, B> {
#[inline(always)]
fn superblock_offset_idx(&self, symbol: u8, idx: usize) -> usize {
let superblock_size = u16::MAX as usize + 1;
(idx / superblock_size) * self.alphabet_size + symbol as usize
}

#[inline(always)]
fn block_offset_idx(&self, symbol: u8, idx: usize) -> usize {
(idx / B::NUM_BITS) * self.alphabet_size + symbol as usize
}

#[inline(always)]
fn block_range(&self, idx: usize) -> Range<usize> {
let alphabet_num_bits = ilog2_ceil_for_nonzero(self.alphabet_size);
let interleaved_blocks_start = (idx / B::NUM_BITS) * alphabet_num_bits;
Expand All @@ -56,6 +62,7 @@ impl<I: IndexStorage, B: Block> Sealed for CondensedTextWithRankSupport<I, B> {}
impl<I: IndexStorage, B: Block> super::PrivateTextWithRankSupport<I>
for CondensedTextWithRankSupport<I, B>
{
#[inline(always)]
fn construct_from_maybe_slice_compressed_text<S: SliceCompression>(
text: &[u8],
uncompressed_text_len: usize,
Expand Down Expand Up @@ -123,10 +130,12 @@ impl<I: IndexStorage, B: Block> super::PrivateTextWithRankSupport<I>
}
}

#[inline(always)]
fn _alphabet_size(&self) -> usize {
self.alphabet_size
}

#[inline(always)]
fn _text_len(&self) -> usize {
self.text_len
}
Expand Down Expand Up @@ -288,6 +297,7 @@ impl<I: IndexStorage, B: Block> super::PrivateTextWithRankSupport<I>
}

impl<I: IndexStorage, B: Block> TextWithRankSupport<I> for CondensedTextWithRankSupport<I, B> {
#[inline(always)]
unsafe fn rank_unchecked(&self, mut symbol: u8, idx: usize) -> usize {
// SAFETY: all of the index accesses are in the valid range if idx is at most text.len()
// and since the alphabet has a size of at least 2
Expand Down Expand Up @@ -340,6 +350,21 @@ impl<I: IndexStorage, B: Block> TextWithRankSupport<I> for CondensedTextWithRank
superblock_offset + block_offset + block_count
}

#[inline(always)]
fn prefetch(&self, idx: usize) {
let symbol = 0;

let superblock_offset_idx = self.superblock_offset_idx(symbol, idx);
prefetch_index(&self.interleaved_superblock_offsets, superblock_offset_idx);

let block_offset_idx = self.block_offset_idx(symbol, idx);
prefetch_index(&self.interleaved_blocks, block_offset_idx);

let block_range = self.block_range(idx);
prefetch_index(&self.interleaved_blocks, block_range.start);
}

#[inline(always)]
fn symbol_at(&self, idx: usize) -> u8 {
assert!(idx < self.text_len);

Expand Down Expand Up @@ -409,6 +434,7 @@ fn fill_superblock<I: PrimInt, B: Block, S: SliceCompression>(
}
}

#[inline(always)]
fn ilog2_ceil_for_nonzero(value: usize) -> usize {
usize::BITS as usize - value.leading_zeros() as usize - value.is_power_of_two() as usize
}
21 changes: 20 additions & 1 deletion src/text_with_rank_support/flat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::maybe_mem_dbg::MaybeMemDbg;
use crate::maybe_savefile::MaybeSavefile;
use crate::sealed::Sealed;

use super::TextWithRankSupport;
use super::block::{Block, Block64, NUM_BLOCK_OFFSET_BITS};
use super::{TextWithRankSupport, prefetch_index};

use num_traits::{NumCast, PrimInt};
use rayon::prelude::*;
Expand All @@ -33,17 +33,20 @@ pub struct FlatTextWithRankSupport<I, B = Block64> {
}

impl<I: IndexStorage, B: Block> FlatTextWithRankSupport<I, B> {
#[inline(always)]
fn superblock_offset_idx(&self, symbol: u8, idx: usize) -> usize {
let symbol_usize = symbol as usize;
(idx / self.superblock_size) * self.alphabet_size + symbol_usize
}

#[inline(always)]
fn block_idx(&self, symbol: u8, idx: usize) -> usize {
let symbol_usize = symbol as usize;
let used_bits_per_block = B::NUM_BITS - NUM_BLOCK_OFFSET_BITS;
(idx / used_bits_per_block) * self.alphabet_size + symbol_usize
}

#[inline(always)]
fn idx_in_block(idx: usize) -> usize {
let used_bits_per_block = B::NUM_BITS - NUM_BLOCK_OFFSET_BITS;
idx % used_bits_per_block
Expand All @@ -59,6 +62,7 @@ impl<I: IndexStorage, B: Block> Sealed for FlatTextWithRankSupport<I, B> {}
impl<I: IndexStorage, B: Block> super::PrivateTextWithRankSupport<I>
for FlatTextWithRankSupport<I, B>
{
#[inline(always)]
fn construct_from_maybe_slice_compressed_text<S: SliceCompression>(
text: &[u8],
uncompressed_text_len: usize,
Expand Down Expand Up @@ -121,10 +125,12 @@ impl<I: IndexStorage, B: Block> super::PrivateTextWithRankSupport<I>
}
}

#[inline(always)]
fn _alphabet_size(&self) -> usize {
self.alphabet_size
}

#[inline(always)]
fn _text_len(&self) -> usize {
self.text_len
}
Expand Down Expand Up @@ -218,6 +224,7 @@ impl<I: IndexStorage, B: Block> super::PrivateTextWithRankSupport<I>
}

impl<I: IndexStorage, B: Block> TextWithRankSupport<I> for FlatTextWithRankSupport<I, B> {
#[inline(always)]
unsafe fn rank_unchecked(&self, symbol: u8, idx: usize) -> usize {
// SAFETY: all of the index accesses are in the valid range if idx is at most text.len()
// and since the alphabet has a size of at least 2
Expand Down Expand Up @@ -245,6 +252,18 @@ impl<I: IndexStorage, B: Block> TextWithRankSupport<I> for FlatTextWithRankSuppo
superblock_offset + block_offset + block_count
}

#[inline(always)]
fn prefetch(&self, idx: usize) {
let symbol = 0;

let superblock_offset_idx = self.superblock_offset_idx(symbol, idx);
prefetch_index(&self.interleaved_superblock_offsets, superblock_offset_idx);

let block_idx = self.block_idx(symbol, idx);
prefetch_index(&self.interleaved_blocks, block_idx);
}

#[inline(always)]
fn symbol_at(&self, idx: usize) -> u8 {
assert!(idx < self.text_len);

Expand Down
31 changes: 31 additions & 0 deletions src/text_with_rank_support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub trait TextWithRankSupport<I: IndexStorage>:
/// Returns the number of occurrences of `symbol` in `text[0..idx]`.
///
/// The running time is in O(1).
#[inline(always)]
fn rank(&self, symbol: u8, idx: usize) -> usize {
let is_safe = (symbol as usize) < self.alphabet_size() && idx <= self.text_len();
assert!(is_safe);
Expand All @@ -118,20 +119,50 @@ pub trait TextWithRankSupport<I: IndexStorage>:
/// `idx` must be in the interval `[0, text.len()]` and `symbol` must be smaller than alphabet size.
unsafe fn rank_unchecked(&self, symbol: u8, idx: usize) -> usize;

/// Prefetch the data needed to answer `rank_unchecked(idx)` quickly.
fn prefetch(&self, idx: usize) {
let _ = idx;
}

/// Recoveres the symbol of the text at given index `idx`.
///
/// The running time is in O(1).
fn symbol_at(&self, idx: usize) -> u8;

#[inline(always)]
fn text_len(&self) -> usize {
self._text_len()
}

#[inline(always)]
fn alphabet_size(&self) -> usize {
self._alphabet_size()
}
}

/// Prefetch the cache line containing (the first byte of) `data[index]` into
/// all levels of the cache.
#[inline(always)]
pub fn prefetch_index<T>(data: impl AsRef<[T]>, index: usize) {
let ptr = data.as_ref().as_ptr().wrapping_add(index) as *const i8;
#[cfg(target_arch = "x86_64")]
unsafe {
std::arch::x86_64::_mm_prefetch(ptr, std::arch::x86_64::_MM_HINT_T0);
}
#[cfg(target_arch = "x86")]
unsafe {
std::arch::x86::_mm_prefetch(ptr, std::arch::x86::_MM_HINT_T0);
}
#[cfg(target_arch = "aarch64")]
unsafe {
std::arch::aarch64::_prefetch(ptr, std::arch::aarch64::_PREFETCH_LOCALITY3);
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64")))]
{
// Do nothing.
}
}

// test functions from PrivateTextWithRankSupport here
#[cfg(test)]
mod tests {
Expand Down