use crate::key::{Key, Leaf, PartSink, SlotTree};
use crate::view::{Elem, STR_CHUNK_BYTES, SimdKind, str_chunk_bytes, str_chunk_ends, str_chunk_key, str_chunk_key_desc, str_compare_from, str_examined};
pub trait Record: Elem {
fn build<K: Key + ?Sized>(key: &K, idx: u32) -> Option<Self>;
fn index(&self) -> u32;
fn set_index(&mut self, i: u32);
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[repr(C)]
pub struct Rec32 {
pub key: i32,
pub idx: u32,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[repr(C)]
pub struct Rec64 {
pub key: i64,
pub idx: u32,
pub pad: u32,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[repr(transparent)]
pub struct Key64 {
pub key: i64,
}
impl Key64 {
#[inline(always)]
pub fn from_radix(r: u64) -> Self {
Key64 { key: (r ^ 0x8000_0000_0000_0000) as i64 }
}
#[inline(always)]
pub fn radix(self) -> u64 {
(self.key as u64) ^ 0x8000_0000_0000_0000
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[repr(transparent)]
pub struct Key32 {
pub key: i32,
}
impl Key32 {
#[inline(always)]
pub fn from_radix(r: u64) -> Self {
Key32 { key: (r as u32 ^ 0x8000_0000) as i32 }
}
#[inline(always)]
pub fn radix(self) -> u64 {
((self.key as u32) ^ 0x8000_0000) as u64
}
}
pub trait KeyElem: Elem {
fn from_radix(r: u64) -> Self;
fn radix(self) -> u64;
fn is_negative(self) -> bool;
}
impl KeyElem for Key64 {
#[inline(always)]
fn from_radix(r: u64) -> Self {
Key64::from_radix(r)
}
#[inline(always)]
fn radix(self) -> u64 {
Key64::radix(self)
}
#[inline(always)]
fn is_negative(self) -> bool {
self.key < 0
}
}
impl KeyElem for Key32 {
#[inline(always)]
fn from_radix(r: u64) -> Self {
Key32::from_radix(r)
}
#[inline(always)]
fn radix(self) -> u64 {
Key32::radix(self)
}
#[inline(always)]
fn is_negative(self) -> bool {
self.key < 0
}
}
impl Elem for Rec32 {
type Key = u32;
const CHUNKED: bool = false;
const SIMD: SimdKind = SimdKind::I32;
#[inline(always)]
fn less(a: Self, b: Self) -> bool {
a.key < b.key
}
#[inline(always)]
fn compare(a: Self, b: Self) -> i32 {
(a.key > b.key) as i32 - (a.key < b.key) as i32
}
#[inline(always)]
fn compare_from(a: Self, b: Self, _chunk: i32) -> i32 {
Self::compare(a, b)
}
#[inline(always)]
fn radix_key(a: Self, _chunk: i32) -> u32 {
(a.key as u32) ^ 0x8000_0000
}
#[inline(always)]
fn chunk_ends(_a: Self, _chunk: i32) -> bool {
true
}
}
impl Elem for Key64 {
type Key = u64;
const CHUNKED: bool = false;
const SIMD: SimdKind = SimdKind::K64;
#[inline(always)]
fn less(a: Self, b: Self) -> bool {
a.key < b.key
}
#[inline(always)]
fn compare(a: Self, b: Self) -> i32 {
(a.key > b.key) as i32 - (a.key < b.key) as i32
}
#[inline(always)]
fn compare_from(a: Self, b: Self, _chunk: i32) -> i32 {
Self::compare(a, b)
}
#[inline(always)]
fn radix_key(a: Self, _chunk: i32) -> u64 {
a.radix()
}
#[inline(always)]
fn chunk_ends(_a: Self, _chunk: i32) -> bool {
true
}
}
impl Elem for Key32 {
type Key = u32;
const CHUNKED: bool = false;
const SIMD: SimdKind = SimdKind::K32;
#[inline(always)]
fn less(a: Self, b: Self) -> bool {
a.key < b.key
}
#[inline(always)]
fn compare(a: Self, b: Self) -> i32 {
(a.key > b.key) as i32 - (a.key < b.key) as i32
}
#[inline(always)]
fn compare_from(a: Self, b: Self, _chunk: i32) -> i32 {
Self::compare(a, b)
}
#[inline(always)]
fn radix_key(a: Self, _chunk: i32) -> u32 {
(a.key as u32) ^ 0x8000_0000
}
#[inline(always)]
fn chunk_ends(_a: Self, _chunk: i32) -> bool {
true
}
}
impl Elem for Rec64 {
type Key = u64;
const CHUNKED: bool = false;
const SIMD: SimdKind = SimdKind::I64;
#[inline(always)]
fn less(a: Self, b: Self) -> bool {
a.key < b.key
}
#[inline(always)]
fn compare(a: Self, b: Self) -> i32 {
(a.key > b.key) as i32 - (a.key < b.key) as i32
}
#[inline(always)]
fn compare_from(a: Self, b: Self, _chunk: i32) -> i32 {
Self::compare(a, b)
}
#[inline(always)]
fn radix_key(a: Self, _chunk: i32) -> u64 {
(a.key as u64) ^ 0x8000_0000_0000_0000
}
#[inline(always)]
fn chunk_ends(_a: Self, _chunk: i32) -> bool {
true
}
}
struct Build32 {
acc: u32,
}
impl PartSink for Build32 {
#[inline(always)]
fn fixed(&mut self, radix: u64, bits: u8, desc: bool) {
let mut r = radix as u32;
if desc {
r = (if bits >= 32 { u32::MAX } else { (1u32 << bits) - 1 }) - r;
}
self.acc = if bits >= 32 { r } else { (self.acc << bits) | r };
}
#[inline(always)]
fn bytes(&mut self, _b: &[u8], _desc: bool) {}
}
struct Build64 {
acc: u64,
}
impl PartSink for Build64 {
#[inline(always)]
fn fixed(&mut self, radix: u64, bits: u8, desc: bool) {
let mut r = radix;
if desc {
r = (if bits >= 64 { u64::MAX } else { (1u64 << bits) - 1 }) - r;
}
self.acc = if bits >= 64 { r } else { (self.acc << bits) | r };
}
#[inline(always)]
fn bytes(&mut self, _b: &[u8], _desc: bool) {}
}
impl Record for Rec32 {
#[inline(always)]
fn build<K: Key + ?Sized>(key: &K, idx: u32) -> Option<Self> {
let mut b = Build32 { acc: 0 };
key.write_parts(&mut b);
Some(Rec32 { key: (b.acc ^ 0x8000_0000) as i32, idx })
}
#[inline(always)]
fn index(&self) -> u32 {
self.idx
}
#[inline(always)]
fn set_index(&mut self, i: u32) {
self.idx = i;
}
}
impl Record for Rec64 {
#[inline(always)]
fn build<K: Key + ?Sized>(key: &K, idx: u32) -> Option<Self> {
let mut b = Build64 { acc: 0 };
key.write_parts(&mut b);
Some(Rec64 { key: (b.acc ^ 0x8000_0000_0000_0000) as i64, idx, pad: 0 })
}
#[inline(always)]
fn index(&self) -> u32 {
self.idx
}
#[inline(always)]
fn set_index(&mut self, i: u32) {
self.idx = i;
}
}
#[inline(always)]
pub fn radix64_of<K: Key + ?Sized>(key: &K) -> u64 {
let mut b = Build64 { acc: 0 };
key.write_parts(&mut b);
b.acc
}
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct StrRec<const DESC: bool> {
pub ptr: *const u8,
pub len: u32,
pub idx: u32,
}
impl<const DESC: bool> Default for StrRec<DESC> {
fn default() -> Self {
StrRec { ptr: core::ptr::NonNull::dangling().as_ptr(), len: 0, idx: 0 }
}
}
impl<const DESC: bool> Elem for StrRec<DESC> {
type Key = u64;
const CHUNKED: bool = true;
const SIMD: SimdKind = SimdKind::None;
#[inline(always)]
fn less(a: Self, b: Self) -> bool {
Self::compare_from(a, b, 0) < 0
}
#[inline(always)]
fn compare(a: Self, b: Self) -> i32 {
Self::compare_from(a, b, 0)
}
#[inline(always)]
fn compare_from(a: Self, b: Self, chunk: i32) -> i32 {
let c = unsafe { str_compare_from(a.ptr, a.len, b.ptr, b.len, (chunk as u32) * STR_CHUNK_BYTES) };
if DESC { -c } else { c }
}
#[inline(always)]
fn radix_key(a: Self, chunk: i32) -> u64 {
unsafe { if DESC { str_chunk_key_desc(a.ptr, a.len, chunk) } else { str_chunk_key(a.ptr, a.len, chunk) } }
}
#[inline(always)]
fn chunk_ends(a: Self, chunk: i32) -> bool {
str_chunk_ends(a.len, chunk)
}
#[inline(always)]
fn report_key_chunk(a: &Self, chunk: i32, sink: &mut impl FnMut(*const u8, usize)) {
let off = (chunk as u32).wrapping_mul(STR_CHUNK_BYTES);
sink(a.ptr.wrapping_add(off as usize), str_chunk_bytes(a.len, chunk) as usize);
}
#[inline(always)]
fn report_key_compare(a: &Self, b: &Self, chunk: i32, sink: &mut impl FnMut(*const u8, usize)) {
let off = (chunk as u32) * STR_CHUNK_BYTES;
let ex = unsafe { str_examined(a.ptr, a.len, b.ptr, b.len, off) } as usize;
sink(a.ptr.wrapping_add(off as usize), ex);
sink(b.ptr.wrapping_add(off as usize), ex);
}
}
struct BuildStr {
ptr: *const u8,
len: usize,
}
impl PartSink for BuildStr {
#[inline(always)]
fn fixed(&mut self, _radix: u64, _bits: u8, _desc: bool) {}
#[inline(always)]
fn bytes(&mut self, b: &[u8], _desc: bool) {
self.ptr = b.as_ptr();
self.len = b.len();
}
}
impl<const DESC: bool> Record for StrRec<DESC> {
#[inline(always)]
fn build<K: Key + ?Sized>(key: &K, idx: u32) -> Option<Self> {
let mut b = BuildStr { ptr: core::ptr::NonNull::dangling().as_ptr(), len: 0 };
key.write_parts(&mut b);
if b.len > u32::MAX as usize {
return None;
}
Some(StrRec { ptr: b.ptr, len: b.len as u32, idx })
}
#[inline(always)]
fn index(&self) -> u32 {
self.idx
}
#[inline(always)]
fn set_index(&mut self, i: u32) {
self.idx = i;
}
}
#[repr(C)]
pub struct CompRec<K: Key + ?Sized> {
pub slots: K::Slots,
pub idx: u32,
pub pad: u32,
}
impl<K: Key + ?Sized> Clone for CompRec<K> {
fn clone(&self) -> Self {
*self
}
}
impl<K: Key + ?Sized> Copy for CompRec<K> {}
impl<K: Key + ?Sized> Default for CompRec<K> {
fn default() -> Self {
CompRec { slots: K::Slots::zero(), idx: 0, pad: 0 }
}
}
impl<K: Key + ?Sized> core::fmt::Debug for CompRec<K> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "CompRec(idx {})", self.idx)
}
}
impl<K: Key + ?Sized> CompRec<K> {
const N: usize = K::SHAPE.n;
#[inline(always)]
fn chunks_of(&self, p: usize) -> i32 {
match self.slots.leaf(p) {
Leaf::Fixed(_) => 1,
Leaf::Bytes(_, len) => (len / STR_CHUNK_BYTES) as i32 + 1,
}
}
#[inline(always)]
fn locate(&self, mut chunk: i32) -> (usize, i32) {
for p in 0..Self::N {
let nc = self.chunks_of(p);
if chunk < nc {
return (p, chunk);
}
chunk -= nc;
}
(Self::N, 0)
}
#[inline(always)]
fn part_chunk_key(&self, p: usize, local: i32) -> u64 {
match self.slots.leaf(p) {
Leaf::Fixed(v) => v,
Leaf::Bytes(ptr, len) => unsafe { if K::SHAPE.kind(p) == 2 { str_chunk_key_desc(ptr, len, local) } else { str_chunk_key(ptr, len, local) } },
}
}
#[inline(always)]
fn compare_part(&self, b: &Self, p: usize, off: u32) -> i32 {
match (self.slots.leaf(p), b.slots.leaf(p)) {
(Leaf::Fixed(x), Leaf::Fixed(y)) => (x > y) as i32 - (x < y) as i32,
(Leaf::Bytes(pa, la), Leaf::Bytes(pb, lb)) => {
let c = unsafe { str_compare_from(pa, la, pb, lb, off) };
if K::SHAPE.kind(p) == 2 { -c } else { c }
}
_ => 0,
}
}
}
impl<K: Key + ?Sized> Elem for CompRec<K> {
type Key = u64;
const CHUNKED: bool = true;
const SIMD: SimdKind = SimdKind::None;
#[inline(always)]
fn less(a: Self, b: Self) -> bool {
Self::compare_from(a, b, 0) < 0
}
#[inline(always)]
fn compare(a: Self, b: Self) -> i32 {
Self::compare_from(a, b, 0)
}
#[inline(always)]
fn compare_from(a: Self, b: Self, chunk: i32) -> i32 {
let (mut p, local) = a.locate(chunk);
if p >= Self::N {
return 0;
}
let mut c = a.compare_part(&b, p, (local as u32) * STR_CHUNK_BYTES);
p += 1;
while c == 0 && p < Self::N {
c = a.compare_part(&b, p, 0);
p += 1;
}
c
}
#[inline(always)]
fn radix_key(a: Self, chunk: i32) -> u64 {
let (p, local) = a.locate(chunk);
if p < Self::N { a.part_chunk_key(p, local) } else { 0 }
}
#[inline(always)]
fn chunk_ends(a: Self, chunk: i32) -> bool {
let (p, local) = a.locate(chunk);
if p + 1 >= Self::N {
if p >= Self::N {
return true;
}
return K::SHAPE.kind(p) == 0 || local == a.chunks_of(p) - 1;
}
false
}
#[inline(always)]
fn report_key_chunk(a: &Self, chunk: i32, sink: &mut impl FnMut(*const u8, usize)) {
let (p, local) = a.locate(chunk);
if p < Self::N {
if let Leaf::Bytes(ptr, len) = a.slots.leaf(p) {
let off = (local as u32) * STR_CHUNK_BYTES;
sink(ptr.wrapping_add(off as usize), str_chunk_bytes(len, local) as usize);
}
}
}
#[inline(always)]
fn report_key_compare(a: &Self, b: &Self, chunk: i32, sink: &mut impl FnMut(*const u8, usize)) {
let (mut p, local) = a.locate(chunk);
let mut off = (local as u32) * STR_CHUNK_BYTES;
while p < Self::N {
let c = a.compare_part(b, p, off);
if let (Leaf::Bytes(pa, la), Leaf::Bytes(pb, lb)) = (a.slots.leaf(p), b.slots.leaf(p)) {
let ex = unsafe { str_examined(pa, la, pb, lb, off) } as usize;
sink(pa.wrapping_add(off as usize), ex);
sink(pb.wrapping_add(off as usize), ex);
}
if c != 0 {
break;
}
p += 1;
off = 0;
}
}
}
struct BuildComp<K: Key + ?Sized> {
rec: CompRec<K>,
p: usize,
ok: bool,
}
impl<K: Key + ?Sized> PartSink for BuildComp<K> {
#[inline(always)]
fn fixed(&mut self, radix: u64, bits: u8, desc: bool) {
let mut r = radix;
if desc {
r = (if bits >= 64 { u64::MAX } else { (1u64 << bits) - 1 }) - r;
}
self.rec.slots.set_leaf(self.p, Leaf::Fixed(r));
self.p += 1;
}
#[inline(always)]
fn bytes(&mut self, b: &[u8], _desc: bool) {
if b.len() > u32::MAX as usize {
self.ok = false;
}
self.rec.slots.set_leaf(self.p, Leaf::Bytes(b.as_ptr(), b.len() as u32));
self.p += 1;
}
}
impl<K: Key + ?Sized> Record for CompRec<K> {
#[inline(always)]
fn build<K2: Key + ?Sized>(key: &K2, idx: u32) -> Option<Self> {
let mut b = BuildComp::<K> { rec: CompRec::default(), p: 0, ok: true };
key.write_parts(&mut b);
b.rec.idx = idx;
b.rec.pad = 0;
if b.ok { Some(b.rec) } else { None }
}
#[inline(always)]
fn index(&self) -> u32 {
self.idx
}
#[inline(always)]
fn set_index(&mut self, i: u32) {
self.idx = i;
}
}
unsafe impl<K: Key + ?Sized> Send for CompRec<K> {}
unsafe impl<K: Key + ?Sized> Sync for CompRec<K> {}
unsafe impl<const D: bool> Send for StrRec<D> {}
unsafe impl<const D: bool> Sync for StrRec<D> {}
const _: () = assert!(core::mem::size_of::<Rec32>() == 8 && core::mem::size_of::<Rec64>() == 16 && core::mem::size_of::<Key64>() == 8 && core::mem::size_of::<Key32>() == 4);