diff --git a/Cargo.toml b/Cargo.toml index eabbcbc1a..6f31a1ae2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,6 @@ iai-callgrind = "0.14.0" itertools = "0.13.0" num-traits = "0.2.15" num_cpus = "1.16.0" -once_cell = "1.19.0" opentelemetry = "0.30.0" opentelemetry_sdk = "0.30.0" paste = "1.0.15" diff --git a/diskann-disk/benches/benchmarks/aligned_file_reader_bench.rs b/diskann-disk/benches/benchmarks/aligned_file_reader_bench.rs index d301af5a0..e8099d40d 100644 --- a/diskann-disk/benches/benchmarks/aligned_file_reader_bench.rs +++ b/diskann-disk/benches/benchmarks/aligned_file_reader_bench.rs @@ -9,7 +9,7 @@ use diskann_disk::utils::aligned_file_reader::{ traits::{AlignedFileReader, AlignedReaderFactory}, AlignedFileReaderFactory, AlignedRead, }; -use diskann_quantization::{alloc::aligned_slice, num::PowerOfTwo}; +use diskann_quantization::alloc::{AlignedAllocator, Poly}; pub const TEST_INDEX_PATH: &str = "../test_data/disk_index_misc/disk_index_siftsmall_learn_256pts_R4_L50_A1.2_aligned_reader_test.index"; @@ -34,7 +34,7 @@ pub fn benchmark_aligned_file_reader(c: &mut Criterion) { let read_length = 512; let num_read = MAX_IO_CONCURRENCY * 100; // The LinuxAlignedFileReader batches reads according to MAX_IO_CONCURRENCY. Make sure we have many batches to handle. let mut aligned_mem = - aligned_slice::(read_length * num_read, PowerOfTwo::new(512).unwrap()).unwrap(); + Poly::broadcast(0u8, read_length * num_read, AlignedAllocator::A512).unwrap(); // create and add AlignedReads to the vector let mut mem_slices: Vec<&mut [u8]> = aligned_mem.chunks_mut(read_length).collect(); diff --git a/diskann-disk/benches/benchmarks_iai/aligned_file_reader_bench_iai.rs b/diskann-disk/benches/benchmarks_iai/aligned_file_reader_bench_iai.rs index 8ff50f167..8c9c1f880 100644 --- a/diskann-disk/benches/benchmarks_iai/aligned_file_reader_bench_iai.rs +++ b/diskann-disk/benches/benchmarks_iai/aligned_file_reader_bench_iai.rs @@ -7,7 +7,7 @@ use diskann_disk::utils::aligned_file_reader::{ traits::{AlignedFileReader, AlignedReaderFactory}, AlignedFileReaderFactory, AlignedRead, }; -use diskann_quantization::{alloc::aligned_slice, num::PowerOfTwo}; +use diskann_quantization::alloc::{AlignedAllocator, Poly}; pub const TEST_INDEX_PATH: &str = "../test_data/disk_index_misc/disk_index_siftsmall_learn_256pts_R4_L50_A1.2_aligned_reader_test.index"; @@ -25,7 +25,7 @@ pub fn benchmark_aligned_file_reader_iai() { let read_length = 512; let num_read = MAX_IO_CONCURRENCY * 100; // The LinuxAlignedFileReader batches reads according to MAX_IO_CONCURRENCY. Make sure we have many batches to handle. let mut aligned_mem = - aligned_slice::(read_length * num_read, PowerOfTwo::new(512).unwrap()).unwrap(); + Poly::broadcast(0u8, read_length * num_read, AlignedAllocator::A512).unwrap(); // create and add AlignedReads to the vector let mut mem_slices: Vec<&mut [u8]> = aligned_mem.chunks_mut(read_length).collect(); diff --git a/diskann-disk/src/search/pq/pq_scratch.rs b/diskann-disk/src/search/pq/pq_scratch.rs index bd7a8dfc3..0707fae3a 100644 --- a/diskann-disk/src/search/pq/pq_scratch.rs +++ b/diskann-disk/src/search/pq/pq_scratch.rs @@ -6,25 +6,22 @@ use diskann::{error::IntoANNResult, utils::VectorRepr, ANNError, ANNResult}; -use diskann_quantization::{ - alloc::{aligned_slice, AlignedSlice}, - num::PowerOfTwo, -}; +use diskann_quantization::alloc::{AlignedAllocator, Poly}; #[derive(Debug)] /// PQ scratch pub struct PQScratch { /// Aligned pq table distance scratch, the length must be at least [256 * NCHUNKS]. 256 is the number of PQ centroids. /// This is used to store the distance between each chunk in the query vector to each centroid, which is why the length is num of centroids * num of chunks - pub aligned_pqtable_dist_scratch: AlignedSlice, + pub aligned_pqtable_dist_scratch: Poly<[f32], AlignedAllocator>, /// Aligned dist scratch, must be at least diskann MAX_DEGREE /// This is used to temporarily save the pq distance between query vector to the candidate vectors. - pub aligned_dist_scratch: AlignedSlice, + pub aligned_dist_scratch: Poly<[f32], AlignedAllocator>, /// Aligned pq coord scratch, must be at least [N_CHUNKS * MAX_DEGREE] /// This is used to store the pq coordinates of the candidate vectors. - pub aligned_pq_coord_scratch: AlignedSlice, + pub aligned_pq_coord_scratch: Poly<[u8], AlignedAllocator>, /// Query scratch buffer stored as `f32`. `set` initializes it by copying/converting the /// raw query values; `PQTable.PreprocessQuery` can then rotate or otherwise preprocess it. @@ -32,12 +29,6 @@ pub struct PQScratch { } impl PQScratch { - /// 128 bytes alignment to optimize for the L2 Adjacent Cache Line Prefetcher. - const ALIGNED_ALLOC_128: PowerOfTwo = match PowerOfTwo::new(128) { - Ok(v) => v, - Err(_) => unreachable!(), - }; - /// Create a new pq scratch pub fn new( graph_degree: usize, @@ -46,12 +37,12 @@ impl PQScratch { num_centers: usize, ) -> ANNResult { let aligned_pq_coord_scratch = - aligned_slice(graph_degree * num_pq_chunks, PQScratch::ALIGNED_ALLOC_128) + Poly::broadcast(0u8, graph_degree * num_pq_chunks, AlignedAllocator::A128) .map_err(ANNError::log_index_error)?; let aligned_pqtable_dist_scratch = - aligned_slice(num_centers * num_pq_chunks, PQScratch::ALIGNED_ALLOC_128) + Poly::broadcast(0f32, num_centers * num_pq_chunks, AlignedAllocator::A128) .map_err(ANNError::log_index_error)?; - let aligned_dist_scratch = aligned_slice(graph_degree, PQScratch::ALIGNED_ALLOC_128) + let aligned_dist_scratch = Poly::broadcast(0f32, graph_degree, AlignedAllocator::A128) .map_err(ANNError::log_index_error)?; let rotated_query = vec![0.0f32; dim]; @@ -94,6 +85,7 @@ impl PQScratch { #[cfg(test)] mod tests { + use diskann_quantization::num::PowerOfTwo; use rstest::rstest; use super::PQScratch; @@ -110,20 +102,16 @@ mod tests { let mut pq_scratch: PQScratch = PQScratch::new(graph_degree, dim, num_pq_chunks, num_centers).unwrap(); - // Check alignment of the remaining AlignedSlice buffers. assert_eq!( - (pq_scratch.aligned_pqtable_dist_scratch.as_ptr() as usize) - % PQScratch::ALIGNED_ALLOC_128.raw(), + (pq_scratch.aligned_pqtable_dist_scratch.as_ptr() as usize) % PowerOfTwo::V128.raw(), 0 ); assert_eq!( - (pq_scratch.aligned_dist_scratch.as_ptr() as usize) - % PQScratch::ALIGNED_ALLOC_128.raw(), + (pq_scratch.aligned_dist_scratch.as_ptr() as usize) % PowerOfTwo::V128.raw(), 0 ); assert_eq!( - (pq_scratch.aligned_pq_coord_scratch.as_ptr() as usize) - % PQScratch::ALIGNED_ALLOC_128.raw(), + (pq_scratch.aligned_pq_coord_scratch.as_ptr() as usize) % PowerOfTwo::V128.raw(), 0 ); diff --git a/diskann-disk/src/search/provider/disk_sector_graph.rs b/diskann-disk/src/search/provider/disk_sector_graph.rs index cd3412b57..def525712 100644 --- a/diskann-disk/src/search/provider/disk_sector_graph.rs +++ b/diskann-disk/src/search/provider/disk_sector_graph.rs @@ -9,7 +9,7 @@ use std::ops::Deref; use diskann::{ANNError, ANNResult}; use diskann_quantization::{ - alloc::{aligned_slice, AlignedSlice}, + alloc::{AlignedAllocator, Poly}, num::PowerOfTwo, }; @@ -37,7 +37,7 @@ pub struct DiskSectorGraph { /// index info for multi-sector nodes /// node `i` is in sector: [i * max_node_len.div_ceil(block_size)] /// offset in sector: [0] - sectors_data: AlignedSlice, + sectors_data: Poly<[u8], AlignedAllocator>, /// Current sector index into which the next read reads data cur_sector_idx: u64, @@ -76,9 +76,12 @@ impl DiskSectorGraph { Ok(Self { sector_reader, - sectors_data: aligned_slice( + sectors_data: Poly::broadcast( + 0u8, max_n_batch_sector_read * num_sectors_per_node * block_size, - PowerOfTwo::new(block_size).map_err(ANNError::log_index_error)?, + AlignedAllocator::new( + PowerOfTwo::new(block_size).map_err(ANNError::log_index_error)?, + ), ) .map_err(ANNError::log_index_error)?, cur_sector_idx: 0, @@ -94,9 +97,12 @@ impl DiskSectorGraph { pub fn reconfigure(&mut self, max_n_batch_sector_read: usize) -> ANNResult<()> { if max_n_batch_sector_read > self.max_n_batch_sector_read { self.max_n_batch_sector_read = max_n_batch_sector_read; - self.sectors_data = aligned_slice( + self.sectors_data = Poly::broadcast( + 0u8, max_n_batch_sector_read * self.num_sectors_per_node * self.block_size, - PowerOfTwo::new(self.block_size).map_err(ANNError::log_index_error)?, + AlignedAllocator::new( + PowerOfTwo::new(self.block_size).map_err(ANNError::log_index_error)?, + ), ) .map_err(ANNError::log_index_error)?; } @@ -220,7 +226,7 @@ mod disk_sector_graph_test { ) -> DiskSectorGraph<::AlignedReaderType> { DiskSectorGraph { - sectors_data: aligned_slice(512, PowerOfTwo::new(512).unwrap()).unwrap(), + sectors_data: Poly::broadcast(0u8, 512, AlignedAllocator::A512).unwrap(), sector_reader, cur_sector_idx: 0, num_nodes_per_sector, diff --git a/diskann-disk/src/search/provider/disk_vertex_provider_factory.rs b/diskann-disk/src/search/provider/disk_vertex_provider_factory.rs index 7b5647b87..e5567c373 100644 --- a/diskann-disk/src/search/provider/disk_vertex_provider_factory.rs +++ b/diskann-disk/src/search/provider/disk_vertex_provider_factory.rs @@ -6,7 +6,10 @@ use std::{cmp::min, collections::VecDeque, sync::Arc, time::Instant}; use crate::data_model::GraphDataType; use diskann::{graph::AdjacencyList, utils::TryIntoVectorId, ANNError, ANNResult}; -use diskann_quantization::{alloc::aligned_slice, num::PowerOfTwo}; +use diskann_quantization::{ + alloc::{AlignedAllocator, Poly}, + num::PowerOfTwo, +}; use hashbrown::HashSet; use tracing::info; @@ -52,9 +55,10 @@ where // since this is the implementation for the disk vertex provider, there're only two kinds of sector lengths: 4096 and 512. // it's okay to hardcoded at this place. let buffer_len = GraphHeader::get_size().next_multiple_of(DEFAULT_DISK_SECTOR_LEN); - let mut read_buf = aligned_slice::( + let mut read_buf = Poly::broadcast( + 0u8, buffer_len, - PowerOfTwo::new(buffer_len).map_err(ANNError::log_index_error)?, + AlignedAllocator::new(PowerOfTwo::new(buffer_len).map_err(ANNError::log_index_error)?), ) .map_err(ANNError::log_index_error)?; let aligned_read = AlignedRead::new(0_u64, &mut read_buf)?; diff --git a/diskann-disk/src/utils/aligned_file_reader/linux_aligned_file_reader.rs b/diskann-disk/src/utils/aligned_file_reader/linux_aligned_file_reader.rs index 9ad5dba16..b9c649b7c 100644 --- a/diskann-disk/src/utils/aligned_file_reader/linux_aligned_file_reader.rs +++ b/diskann-disk/src/utils/aligned_file_reader/linux_aligned_file_reader.rs @@ -135,7 +135,7 @@ mod tests { use serde::{Deserialize, Serialize}; use super::*; - use diskann_quantization::{alloc::aligned_slice, num::PowerOfTwo}; + use diskann_quantization::alloc::{AlignedAllocator, Poly}; pub const TEST_INDEX_PATH: &str = "../test_data/disk_index_misc/disk_index_siftsmall_learn_256pts_R4_L50_A1.2_aligned_reader_test.index"; pub const TRUTH_NODE_DATA_PATH: &str = @@ -171,7 +171,7 @@ mod tests { let read_length = 512; // adjust according to your logic let num_read = 10; let mut aligned_mem = - aligned_slice::(read_length * num_read, PowerOfTwo::new(512).unwrap()).unwrap(); + Poly::broadcast(0u8, read_length * num_read, AlignedAllocator::A512).unwrap(); // create and add AlignedReads to the vector let mut mem_slices: Vec<&mut [u8]> = aligned_mem.chunks_mut(read_length).collect(); @@ -215,7 +215,7 @@ mod tests { let read_length = 512; let num_read = MAX_IO_CONCURRENCY * 100; // The LinuxAlignedFileReader batches reads according to MAX_IO_CONCURRENCY. Make sure we have many batches to handle. let mut aligned_mem = - aligned_slice::(read_length * num_read, PowerOfTwo::new(512).unwrap()).unwrap(); + Poly::broadcast(0u8, read_length * num_read, AlignedAllocator::A512).unwrap(); // create and add AlignedReads to the vector let mut mem_slices: Vec<&mut [u8]> = aligned_mem.chunks_mut(read_length).collect(); @@ -255,7 +255,7 @@ mod tests { let read_length = 512; // adjust according to your logic let num_sector = 10; let mut aligned_mem = - aligned_slice::(read_length * num_sector, PowerOfTwo::new(512).unwrap()).unwrap(); + Poly::broadcast(0u8, read_length * num_sector, AlignedAllocator::A512).unwrap(); // Each slice will be used as the buffer for a read request of a sector. let mut mem_slices: Vec<&mut [u8]> = aligned_mem.chunks_mut(read_length).collect(); diff --git a/diskann-disk/src/utils/aligned_file_reader/storage_provider_aligned_file_reader.rs b/diskann-disk/src/utils/aligned_file_reader/storage_provider_aligned_file_reader.rs index 2e296eaf7..8bc6a7e54 100644 --- a/diskann-disk/src/utils/aligned_file_reader/storage_provider_aligned_file_reader.rs +++ b/diskann-disk/src/utils/aligned_file_reader/storage_provider_aligned_file_reader.rs @@ -54,7 +54,7 @@ mod tests { use diskann_utils::test_data_root; use super::*; - use diskann_quantization::{alloc::aligned_slice, num::PowerOfTwo}; + use diskann_quantization::alloc::{AlignedAllocator, Poly}; fn test_index_path() -> String { "/disk_index_misc/disk_index_siftsmall_learn_256pts_R4_L50_A1.2_aligned_reader_test.index" @@ -79,7 +79,7 @@ mod tests { let read_length = 512; let num_read = 10; let mut aligned_mem = - aligned_slice::(read_length * num_read, PowerOfTwo::new(512).unwrap()).unwrap(); + Poly::broadcast(0u8, read_length * num_read, AlignedAllocator::A512).unwrap(); // create and add AlignedReads to the vector let mut mem_slices: Vec<&mut [u8]> = aligned_mem.chunks_mut(read_length).collect(); diff --git a/diskann-disk/src/utils/aligned_file_reader/windows_aligned_file_reader.rs b/diskann-disk/src/utils/aligned_file_reader/windows_aligned_file_reader.rs index 0ac6887f5..468d8a1ff 100644 --- a/diskann-disk/src/utils/aligned_file_reader/windows_aligned_file_reader.rs +++ b/diskann-disk/src/utils/aligned_file_reader/windows_aligned_file_reader.rs @@ -123,7 +123,7 @@ mod tests { use super::*; use crate::utils::aligned_file_reader::AlignedRead; - use diskann_quantization::{alloc::aligned_slice, num::PowerOfTwo}; + use diskann_quantization::alloc::{AlignedAllocator, Poly}; fn test_index_path() -> String { test_data_root() @@ -170,7 +170,7 @@ mod tests { let read_length = 512; // adjust according to your logic let num_read = 10; let mut aligned_mem = - aligned_slice::(read_length * num_read, PowerOfTwo::new(512).unwrap()).unwrap(); + Poly::broadcast(0u8, read_length * num_read, AlignedAllocator::A512).unwrap(); // create and add AlignedReads to the vector let mut mem_slices: Vec<&mut [u8]> = aligned_mem.chunks_mut(read_length).collect(); @@ -209,7 +209,7 @@ mod tests { let read_length = DEFAULT_DISK_SECTOR_LEN; // adjust according to your logic let num_sector = 10; let mut aligned_mem = - aligned_slice::(read_length * num_sector, PowerOfTwo::new(512).unwrap()).unwrap(); + Poly::broadcast(0u8, read_length * num_sector, AlignedAllocator::A512).unwrap(); // Each slice will be used as the buffer for a read request of a sector. let mut mem_slices: Vec<&mut [u8]> = aligned_mem.chunks_mut(read_length).collect(); diff --git a/diskann-quantization/src/alloc/aligned.rs b/diskann-quantization/src/alloc/aligned.rs index 3a3d3f6fd..40dea03ab 100644 --- a/diskann-quantization/src/alloc/aligned.rs +++ b/diskann-quantization/src/alloc/aligned.rs @@ -20,6 +20,19 @@ pub struct AlignedAllocator { } impl AlignedAllocator { + /// Aligned allocators for commonly specified boundaries in the codebase (4..4096) + pub const A4: Self = Self::new(PowerOfTwo::V4); + pub const A8: Self = Self::new(PowerOfTwo::V8); + pub const A16: Self = Self::new(PowerOfTwo::V16); + pub const A32: Self = Self::new(PowerOfTwo::V32); + pub const A64: Self = Self::new(PowerOfTwo::V64); + pub const A128: Self = Self::new(PowerOfTwo::V128); + pub const A256: Self = Self::new(PowerOfTwo::V256); + pub const A512: Self = Self::new(PowerOfTwo::V512); + pub const A1024: Self = Self::new(PowerOfTwo::V1024); + pub const A2048: Self = Self::new(PowerOfTwo::V2048); + pub const A4096: Self = Self::new(PowerOfTwo::V4096); + /// Construct a new allocator that uses the given alignment. #[inline] pub const fn new(alignment: PowerOfTwo) -> Self { diff --git a/diskann-quantization/src/alloc/aligned_slice.rs b/diskann-quantization/src/alloc/aligned_slice.rs deleted file mode 100644 index 4e15f114f..000000000 --- a/diskann-quantization/src/alloc/aligned_slice.rs +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT license. - */ - -use super::Poly; -use crate::alloc::{AlignedAllocator, AllocatorError}; - -use crate::num::PowerOfTwo; - -/// Type alias for an aligned, heap-allocated slice -/// -/// Shorthand for `Poly<[T], AlignedAllocator>` which is intended -/// for allocations requiring specific alignment (e.g. cache-line or disk-sector alignment) -pub type AlignedSlice = Poly<[T], AlignedAllocator>; - -/// Create a new [`AlignedSlice`] with the given capacity and alignment -/// initialized to `T::default()` -pub fn aligned_slice( - capacity: usize, - alignment: PowerOfTwo, -) -> Result, AllocatorError> { - let allocator = AlignedAllocator::new(alignment); - Poly::from_iter((0..capacity).map(|_| T::default()), allocator) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn aligned_slice_alignment_32() { - let data = aligned_slice::(1000, PowerOfTwo::new(32).unwrap()).unwrap(); - assert_eq!(data.len(), 1000); - assert_eq!(data.as_ptr() as usize % 32, 0); - } - - #[test] - fn aligned_slice_alignment_256() { - let data = aligned_slice::(500, PowerOfTwo::new(256).unwrap()).unwrap(); - assert_eq!(data.len(), 500); - assert_eq!(data.as_ptr() as usize % 256, 0); - } - - #[test] - fn aligned_slice_alignment_512() { - let data = aligned_slice::(4096, PowerOfTwo::new(512).unwrap()).unwrap(); - assert_eq!(data.len(), 4096); - assert_eq!(data.as_ptr() as usize % 512, 0); - } - - #[test] - fn aligned_slice_zero_length() { - // Zero-length aligned slices should succeed: `Poly::from_iter` - // special-cases empty iterators and returns an empty slice. - let data = aligned_slice::(0, PowerOfTwo::new(16).unwrap()).unwrap(); - assert!(data.is_empty()); - assert_eq!(data.len(), 0); - } - - #[test] - fn aligned_slice_default_initialized() { - let data = aligned_slice::(100, PowerOfTwo::new(64).unwrap()).unwrap(); - for &val in data.iter() { - assert_eq!(val, 0.0); - } - } - - #[test] - fn aligned_slice_deref_mut() { - let mut data = aligned_slice::(4, PowerOfTwo::new(32).unwrap()).unwrap(); - data[0] = 1.0; - data[1] = 2.0; - assert_eq!(&data[..2], &[1.0, 2.0]); - } -} diff --git a/diskann-quantization/src/alloc/mod.rs b/diskann-quantization/src/alloc/mod.rs index e4aaae317..da58f8b77 100644 --- a/diskann-quantization/src/alloc/mod.rs +++ b/diskann-quantization/src/alloc/mod.rs @@ -6,13 +6,11 @@ use std::{alloc::Layout, ptr::NonNull}; mod aligned; -mod aligned_slice; mod bump; mod poly; mod traits; pub use aligned::{AlignedAllocator, NotPowerOfTwo}; -pub use aligned_slice::{AlignedSlice, aligned_slice}; pub use bump::BumpAllocator; pub use poly::{CompoundError, Poly, TrustedIter, poly}; pub use traits::{Allocator, AllocatorCore, AllocatorError}; diff --git a/diskann-quantization/src/num.rs b/diskann-quantization/src/num.rs index 17a632ac1..7881c45e7 100644 --- a/diskann-quantization/src/num.rs +++ b/diskann-quantization/src/num.rs @@ -60,6 +60,95 @@ pub struct PowerOfTwo(NonZeroUsize); #[non_exhaustive] pub struct NotPowerOfTwo(usize); +macro_rules! constants { + (($pow:ident, $value:ident) => $shift:literal) => { + pub const $pow: Self = match Self::new(1 << $shift) { + Ok(v) => v, + Err(_) => panic!("not a power of two"), + }; + + pub const $value: Self = Self::$pow; + }; + ($(($pow:ident, $value:ident) => $shift:literal),+ $(,)?) => { + $(constants!(($pow, $value) => $shift);)+ + }; +} + +impl PowerOfTwo { + constants! { + (P0, V1) => 0, + (P1, V2) => 1, + (P2, V4) => 2, + (P3, V8) => 3, + (P4, V16) => 4, + (P5, V32) => 5, + (P6, V64) => 6, + (P7, V128) => 7, + (P8, V256) => 8, + (P9, V512) => 9, + (P10, V1024) => 10, + (P11, V2048) => 11, + (P12, V4096) => 12, + (P13, V8192) => 13, + (P14, V16384) => 14, + (P15, V32768) => 15, + (P16, V65536) => 16, + (P17, V131072) => 17, + (P18, V262144) => 18, + (P19, V524288) => 19, + (P20, V1048576) => 20, + (P21, V2097152) => 21, + (P22, V4194304) => 22, + (P23, V8388608) => 23, + (P24, V16777216) => 24, + (P25, V33554432) => 25, + (P26, V67108864) => 26, + (P27, V134217728) => 27, + (P28, V268435456) => 28, + (P29, V536870912) => 29, + (P30, V1073741824) => 30, + (P31, V2147483648) => 31, + } +} + +#[cfg(target_pointer_width = "64")] +impl PowerOfTwo { + constants! { + (P32, V4294967296) => 32, + (P33, V8589934592) => 33, + (P34, V17179869184) => 34, + (P35, V34359738368) => 35, + (P36, V68719476736) => 36, + (P37, V137438953472) => 37, + (P38, V274877906944) => 38, + (P39, V549755813888) => 39, + (P40, V1099511627776) => 40, + (P41, V2199023255552) => 41, + (P42, V4398046511104) => 42, + (P43, V8796093022208) => 43, + (P44, V17592186044416) => 44, + (P45, V35184372088832) => 45, + (P46, V70368744177664) => 46, + (P47, V140737488355328) => 47, + (P48, V281474976710656) => 48, + (P49, V562949953421312) => 49, + (P50, V1125899906842624) => 50, + (P51, V2251799813685248) => 51, + (P52, V4503599627370496) => 52, + (P53, V9007199254740992) => 53, + (P54, V18014398509481984) => 54, + (P55, V36028797018963968) => 55, + (P56, V72057594037927936) => 56, + (P57, V144115188075855872) => 57, + (P58, V288230376151711744) => 58, + (P59, V576460752303423488) => 59, + (P60, V1152921504606846976) => 60, + (P61, V2305843009213693952) => 61, + (P62, V4611686018427387904) => 62, + (P63, V9223372036854775808) => 63, + } +} + impl PowerOfTwo { /// Create a new `PowerOfTwo` if the given value is greater a power of two. pub const fn new(value: usize) -> Result {