Skip to content

Commit 1f5a988

Browse files
committed
BOTSML-464: Fix rkyv u128 aligment issue for ARM architectures.
The `aarch64` and `x86_64` architectures have alignment differences for `u128` type: * `aarch64` has 16 bytes alignment * `x86_64` has 8 bytes aligment In order to fix the issue we're going to use `[u8; 16]` instead of `u128`, which will always have alignment of 8 bytes and have backwards compatibility with existing `x86_64` serialized data. See the following issues for more details: * rust-lang/rust#54949 * rkyv/rkyv#409 * rust-lang/rust#54341
1 parent 021afb1 commit 1f5a988

File tree

2 files changed

+53
-9
lines changed

2 files changed

+53
-9
lines changed

examples/mphf.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use entropy_map::{Mphf, DEFAULT_GAMMA};
22

33
fn main() {
4-
// Initialize MPHF with a small set of keys using `B = 32`, `S = 8` and `gamma = 2.0`.
4+
// Initialize MPHF with a small set of keys using `B = 32` (group size), `S = 8` (max seed)
5+
// and `gamma = 2.0` (speed/size tradeoff). See `README.md` for more details.
56
let keys = [1, 2, 3, 4, 5];
67
let mphf = Mphf::<32, 8>::from_slice(&keys, DEFAULT_GAMMA).expect("failed to create MPHF");
78

src/rank.rs

+51-8
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub trait RankedBitsAccess {
2424
/// This method is unsafe because `idx` must be within the bounds of the bits stored in `RankedBitsAccess`.
2525
/// An index out of bounds can lead to undefined behavior.
2626
#[inline]
27-
unsafe fn rank_impl(bits: &[u64], l12_ranks: &[u128], idx: usize) -> Option<usize> {
27+
unsafe fn rank_impl<T: L12RankAccess>(bits: &[u64], l12_ranks: &T, idx: usize) -> Option<usize> {
2828
let word_idx = idx / 64;
2929
let bit_idx = idx % 64;
3030
let word = *bits.get_unchecked(word_idx);
@@ -36,10 +36,6 @@ pub trait RankedBitsAccess {
3636
let l1_pos = idx / L1_BIT_SIZE;
3737
let l2_pos = (idx % L1_BIT_SIZE) / L2_BIT_SIZE;
3838

39-
let l12_rank = l12_ranks.get_unchecked(l1_pos);
40-
let l1_rank = (l12_rank & 0xFFFFFFFFFFF) as usize;
41-
let l2_rank = ((l12_rank >> (32 + 12 * l2_pos)) & 0xFFF) as usize;
42-
4339
let idx_within_l2 = idx % L2_BIT_SIZE;
4440
let blocks_num = idx_within_l2 / 64;
4541
let offset = (idx / L2_BIT_SIZE) * 8;
@@ -51,6 +47,7 @@ pub trait RankedBitsAccess {
5147
let word_mask = ((1u64 << (idx_within_l2 % 64)) - 1) * (idx_within_l2 > 0) as u64;
5248
let word_rank = (word & word_mask).count_ones() as usize;
5349

50+
let (l1_rank, l2_rank) = l12_ranks.l12_ranks(l1_pos, l2_pos);
5451
let total_rank = l1_rank + l2_rank + block_rank + word_rank;
5552

5653
Some(total_rank)
@@ -64,7 +61,53 @@ pub struct RankedBits {
6461
/// The bit vector represented as an array of u64 integers.
6562
bits: Box<[u64]>,
6663
/// Precomputed rank information for L1 and L2 blocks.
67-
l12_ranks: Box<[u128]>,
64+
l12_ranks: Box<[L12Rank]>,
65+
}
66+
67+
/// L12Rank represents l1 and l2 bit ranks stored inside 16 bytes (little endian).
68+
/// NB: it's important to use `[u8; 16]` instead of `u128` for `rkyv` versions 0.7.X
69+
/// because of alignment differences between `x86_64` and `aarch64` architectures.
70+
/// See https://github.com/rkyv/rkyv/issues/409 for more details.
71+
#[derive(Debug)]
72+
#[cfg_attr(feature = "rkyv_derive", derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize))]
73+
#[cfg_attr(feature = "rkyv_derive", archive_attr(derive(rkyv::CheckBytes)))]
74+
pub struct L12Rank([u8; 16]);
75+
76+
/// Trait used to access archived and non-archived L1 and L2 ranks
77+
pub trait L12RankAccess {
78+
/// Return `L12Rank` as `u128`
79+
fn l12_rank(&self, l1_pos: usize) -> u128;
80+
81+
/// Return `l1_rank` and `l2_rank`
82+
#[inline]
83+
fn l12_ranks(&self, l1_pos: usize, l2_pos: usize) -> (usize, usize) {
84+
let l12_rank = self.l12_rank(l1_pos);
85+
let l1_rank = (l12_rank & 0xFFFFFFFFFFF) as usize;
86+
let l2_rank = ((l12_rank >> (32 + 12 * l2_pos)) & 0xFFF) as usize;
87+
(l1_rank, l2_rank)
88+
}
89+
}
90+
91+
impl L12RankAccess for Box<[L12Rank]> {
92+
#[inline]
93+
fn l12_rank(&self, l1_pos: usize) -> u128 {
94+
u128::from_le_bytes(unsafe { self.get_unchecked(l1_pos).0 })
95+
}
96+
}
97+
98+
#[cfg(feature = "rkyv_derive")]
99+
impl L12RankAccess for rkyv::boxed::ArchivedBox<[ArchivedL12Rank]> {
100+
#[inline]
101+
fn l12_rank(&self, l1_pos: usize) -> u128 {
102+
u128::from_le_bytes(unsafe { self.get_unchecked(l1_pos).0 })
103+
}
104+
}
105+
106+
impl From<u128> for L12Rank {
107+
#[inline]
108+
fn from(v: u128) -> Self {
109+
L12Rank(v.to_le_bytes())
110+
}
68111
}
69112

70113
impl RankedBits {
@@ -83,7 +126,7 @@ impl RankedBits {
83126
l12_rank += (sum as u128) << (i * 12);
84127
}
85128
l12_rank = (l12_rank << 44) | l1_rank;
86-
l12_ranks.push(l12_rank);
129+
l12_ranks.push(l12_rank.into());
87130
l1_rank += sum as u128;
88131
}
89132

@@ -95,7 +138,7 @@ impl RankedBits {
95138
l12_rank += (sum as u128) << (i * 12);
96139
}
97140
l12_rank = (l12_rank << 44) | l1_rank;
98-
l12_ranks.push(l12_rank);
141+
l12_ranks.push(l12_rank.into());
99142
}
100143

101144
RankedBits { bits, l12_ranks: l12_ranks.into_boxed_slice() }

0 commit comments

Comments
 (0)