Push zunrplorkwkt #70
@@ -1,9 +1,10 @@
|
|||||||
use std::fs::{self, File, OpenOptions};
|
use std::fs::{self, OpenOptions};
|
||||||
use std::io::{self, Seek, SeekFrom, Write as _};
|
use std::io::{self, Seek, SeekFrom, Write as _};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use memmap2::{Mmap, MmapMut};
|
use memmap2::{Mmap, MmapMut};
|
||||||
|
|
||||||
|
use crate::mmap_file::{create_mmap_file, open_mmap_file};
|
||||||
use crate::reader::PersistentCompactIntVec;
|
use crate::reader::PersistentCompactIntVec;
|
||||||
use crate::views::{BitSliceIter, BitSliceView, IntSliceView};
|
use crate::views::{BitSliceIter, BitSliceView, IntSliceView};
|
||||||
|
|
||||||
@@ -33,17 +34,7 @@ pub struct PersistentBitVec {
|
|||||||
|
|
||||||
impl PersistentBitVec {
|
impl PersistentBitVec {
|
||||||
pub fn open(path: &Path) -> io::Result<Self> {
|
pub fn open(path: &Path) -> io::Result<Self> {
|
||||||
let mmap = unsafe { Mmap::map(&File::open(path)?)? };
|
let (mmap, n) = open_mmap_file(path, MAGIC, HEADER_SIZE)?;
|
||||||
if mmap.len() < HEADER_SIZE {
|
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidData,
|
|
||||||
"PBIV file too short",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if &mmap[0..4] != &MAGIC {
|
|
||||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "bad PBIV magic"));
|
|
||||||
}
|
|
||||||
let n = u64::from_le_bytes(mmap[8..16].try_into().unwrap()) as usize;
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
mmap,
|
mmap,
|
||||||
n,
|
n,
|
||||||
@@ -184,18 +175,7 @@ pub struct PersistentBitVecBuilder {
|
|||||||
impl PersistentBitVecBuilder {
|
impl PersistentBitVecBuilder {
|
||||||
pub fn new(n: usize, path: &Path) -> io::Result<Self> {
|
pub fn new(n: usize, path: &Path) -> io::Result<Self> {
|
||||||
let file_size = HEADER_SIZE + n_bytes_for_words(n);
|
let file_size = HEADER_SIZE + n_bytes_for_words(n);
|
||||||
let mut file = OpenOptions::new()
|
let mmap = create_mmap_file(path, MAGIC, [0u8; 4], n, 0, file_size)?;
|
||||||
.read(true)
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.truncate(true)
|
|
||||||
.open(path)?;
|
|
||||||
file.write_all(&MAGIC)?;
|
|
||||||
file.write_all(&[0u8; 4])?;
|
|
||||||
file.write_all(&(n as u64).to_le_bytes())?;
|
|
||||||
file.seek(SeekFrom::Start(0))?;
|
|
||||||
file.set_len(file_size as u64)?;
|
|
||||||
let mmap = unsafe { MmapMut::map_mut(&file)? };
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
mmap,
|
mmap,
|
||||||
n,
|
n,
|
||||||
@@ -205,16 +185,7 @@ impl PersistentBitVecBuilder {
|
|||||||
|
|
||||||
pub fn from_raw_bytes(bytes: &[u8], n: usize, path: &Path) -> io::Result<Self> {
|
pub fn from_raw_bytes(bytes: &[u8], n: usize, path: &Path) -> io::Result<Self> {
|
||||||
let file_size = HEADER_SIZE + n_bytes_for_words(n);
|
let file_size = HEADER_SIZE + n_bytes_for_words(n);
|
||||||
let file = OpenOptions::new()
|
let mut mmap = create_mmap_file(path, MAGIC, [0u8; 4], n, 0, file_size)?;
|
||||||
.read(true)
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.truncate(true)
|
|
||||||
.open(path)?;
|
|
||||||
file.set_len(file_size as u64)?;
|
|
||||||
let mut mmap = unsafe { MmapMut::map_mut(&file)? };
|
|
||||||
mmap[0..4].copy_from_slice(&MAGIC);
|
|
||||||
mmap[8..16].copy_from_slice(&(n as u64).to_le_bytes());
|
|
||||||
mmap[HEADER_SIZE..HEADER_SIZE + bytes.len()].copy_from_slice(bytes);
|
mmap[HEADER_SIZE..HEADER_SIZE + bytes.len()].copy_from_slice(bytes);
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
mmap,
|
mmap,
|
||||||
@@ -276,18 +247,7 @@ impl PersistentBitVecBuilder {
|
|||||||
) -> io::Result<Self> {
|
) -> io::Result<Self> {
|
||||||
let n = source.len();
|
let n = source.len();
|
||||||
let file_size = HEADER_SIZE + n_bytes_for_words(n);
|
let file_size = HEADER_SIZE + n_bytes_for_words(n);
|
||||||
let mut file = OpenOptions::new()
|
let mut mmap = create_mmap_file(path, MAGIC, [0u8; 4], n, 0, file_size)?;
|
||||||
.read(true)
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.truncate(true)
|
|
||||||
.open(path)?;
|
|
||||||
file.write_all(&MAGIC)?;
|
|
||||||
file.write_all(&[0u8; 4])?;
|
|
||||||
file.write_all(&(n as u64).to_le_bytes())?;
|
|
||||||
file.seek(SeekFrom::Start(0))?;
|
|
||||||
file.set_len(file_size as u64)?;
|
|
||||||
let mut mmap = unsafe { MmapMut::map_mut(&file)? };
|
|
||||||
{
|
{
|
||||||
let nw = n_words(n);
|
let nw = n_words(n);
|
||||||
let ptr = mmap[HEADER_SIZE..].as_mut_ptr() as *mut u64;
|
let ptr = mmap[HEADER_SIZE..].as_mut_ptr() as *mut u64;
|
||||||
|
|||||||
@@ -10,12 +10,13 @@
|
|||||||
//! values roughly uniform over a known range (e.g. genome indices, or
|
//! values roughly uniform over a known range (e.g. genome indices, or
|
||||||
//! `dict_id`s) — every value costs the same, `width` bits, by construction.
|
//! `dict_id`s) — every value costs the same, `width` bits, by construction.
|
||||||
|
|
||||||
use std::fs::{File, OpenOptions};
|
use std::io;
|
||||||
use std::io::{self, Seek, SeekFrom, Write as _};
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use memmap2::{Mmap, MmapMut};
|
use memmap2::{Mmap, MmapMut};
|
||||||
|
|
||||||
|
use crate::mmap_file::{create_mmap_file, open_mmap_file};
|
||||||
|
|
||||||
const MAGIC: [u8; 4] = *b"PFIV";
|
const MAGIC: [u8; 4] = *b"PFIV";
|
||||||
|
|
||||||
// Header: magic(4) + width(1) + _pad(3) + n(8) = 16 bytes.
|
// Header: magic(4) + width(1) + _pad(3) + n(8) = 16 bytes.
|
||||||
@@ -58,15 +59,8 @@ pub struct PersistentFixedIntVec {
|
|||||||
|
|
||||||
impl PersistentFixedIntVec {
|
impl PersistentFixedIntVec {
|
||||||
pub fn open(path: &Path) -> io::Result<Self> {
|
pub fn open(path: &Path) -> io::Result<Self> {
|
||||||
let mmap = unsafe { Mmap::map(&File::open(path)?)? };
|
let (mmap, n) = open_mmap_file(path, MAGIC, HEADER_SIZE)?;
|
||||||
if mmap.len() < HEADER_SIZE {
|
|
||||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "PFIV file too short"));
|
|
||||||
}
|
|
||||||
if mmap[0..4] != MAGIC {
|
|
||||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "bad PFIV magic"));
|
|
||||||
}
|
|
||||||
let width = mmap[4] as u32;
|
let width = mmap[4] as u32;
|
||||||
let n = u64::from_le_bytes(mmap[8..16].try_into().unwrap()) as usize;
|
|
||||||
Ok(Self { mmap, n, width, path: path.to_path_buf() })
|
Ok(Self { mmap, n, width, path: path.to_path_buf() })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,15 +138,8 @@ impl PersistentFixedIntVecBuilder {
|
|||||||
pub fn new(n: usize, width: u32, path: &Path) -> io::Result<Self> {
|
pub fn new(n: usize, width: u32, path: &Path) -> io::Result<Self> {
|
||||||
assert!(width <= 64, "width must be 0..=64, got {width}");
|
assert!(width <= 64, "width must be 0..=64, got {width}");
|
||||||
let file_size = HEADER_SIZE + n_words_for(n, width) * 8;
|
let file_size = HEADER_SIZE + n_words_for(n, width) * 8;
|
||||||
let mut file = OpenOptions::new()
|
let pad = [width as u8, 0, 0, 0];
|
||||||
.read(true).write(true).create(true).truncate(true)
|
let mmap = create_mmap_file(path, MAGIC, pad, n, 0, file_size)?;
|
||||||
.open(path)?;
|
|
||||||
file.write_all(&MAGIC)?;
|
|
||||||
file.write_all(&[width as u8, 0, 0, 0])?;
|
|
||||||
file.write_all(&(n as u64).to_le_bytes())?;
|
|
||||||
file.seek(SeekFrom::Start(0))?;
|
|
||||||
file.set_len(file_size as u64)?;
|
|
||||||
let mmap = unsafe { MmapMut::map_mut(&file)? };
|
|
||||||
Ok(Self { mmap, n, width, path: path.to_path_buf() })
|
Ok(Self { mmap, n, width, path: path.to_path_buf() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ mod rankselect;
|
|||||||
mod intmatrix;
|
mod intmatrix;
|
||||||
mod layer_meta;
|
mod layer_meta;
|
||||||
mod meta;
|
mod meta;
|
||||||
|
mod mmap_file;
|
||||||
mod reader;
|
mod reader;
|
||||||
mod storage_kind;
|
mod storage_kind;
|
||||||
mod tempbitvec;
|
mod tempbitvec;
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
//! Shared header/mmap boilerplate for this crate's `Persistent*` formats —
|
||||||
|
//! every one of them starts with `magic(4) + pad(4) + n(8)`, is created by
|
||||||
|
//! writing that header then extending the file to its final size, and is
|
||||||
|
//! read back by mapping it and validating the header. `n` at bytes `[8,
|
||||||
|
//! 16)` is a crate-wide convention, not particular to any one format.
|
||||||
|
//!
|
||||||
|
//! Deliberately narrow: only the byte-identical create/open dance is
|
||||||
|
//! extracted here. Each format keeps its own bit-level layout (word
|
||||||
|
//! indexing, any trailing index section like `PersistentRankSelectBitVec`'s
|
||||||
|
//! `block_ranks`) and its own higher-level API — those genuinely differ
|
||||||
|
//! between formats and aren't forced into a shared shape.
|
||||||
|
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::io::{self, Seek, SeekFrom, Write as _};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use memmap2::{Mmap, MmapMut};
|
||||||
|
|
||||||
|
/// Creates (or truncates) `path`, writes the standard header — `magic` +
|
||||||
|
/// `pad` (4 bytes right after `magic`; usually all-zero, but
|
||||||
|
/// `PersistentFixedIntVec` repurposes byte 4 to store its `width`) +
|
||||||
|
/// `n as u64`, followed by `extra_header_zeros` more zero bytes for a
|
||||||
|
/// caller-defined trailing header field (e.g.
|
||||||
|
/// `PersistentRankSelectBitVec`'s `total_ones`, patched in by the caller
|
||||||
|
/// after construction) — extends the file to `total_size` bytes, and maps
|
||||||
|
/// it read-write.
|
||||||
|
pub(crate) fn create_mmap_file(
|
||||||
|
path: &Path,
|
||||||
|
magic: [u8; 4],
|
||||||
|
pad: [u8; 4],
|
||||||
|
n: usize,
|
||||||
|
extra_header_zeros: usize,
|
||||||
|
total_size: usize,
|
||||||
|
) -> io::Result<MmapMut> {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(path)?;
|
||||||
|
file.write_all(&magic)?;
|
||||||
|
file.write_all(&pad)?;
|
||||||
|
file.write_all(&(n as u64).to_le_bytes())?;
|
||||||
|
if extra_header_zeros > 0 {
|
||||||
|
file.write_all(&vec![0u8; extra_header_zeros])?;
|
||||||
|
}
|
||||||
|
file.seek(SeekFrom::Start(0))?;
|
||||||
|
file.set_len(total_size as u64)?;
|
||||||
|
unsafe { MmapMut::map_mut(&file) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opens `path` read-only, validates it is at least `header_size` bytes
|
||||||
|
/// and starts with `magic`, and returns the mapping plus `n` (read from
|
||||||
|
/// bytes `[8, 16)`).
|
||||||
|
pub(crate) fn open_mmap_file(path: &Path, magic: [u8; 4], header_size: usize) -> io::Result<(Mmap, usize)> {
|
||||||
|
let mmap = unsafe { Mmap::map(&File::open(path)?)? };
|
||||||
|
let tag = String::from_utf8_lossy(&magic).into_owned();
|
||||||
|
if mmap.len() < header_size {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::InvalidData, format!("{tag} file too short")));
|
||||||
|
}
|
||||||
|
if mmap[0..4] != magic {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::InvalidData, format!("bad {tag} magic")));
|
||||||
|
}
|
||||||
|
let n = u64::from_le_bytes(mmap[8..16].try_into().unwrap()) as usize;
|
||||||
|
Ok((mmap, n))
|
||||||
|
}
|
||||||
@@ -15,13 +15,14 @@
|
|||||||
//! `common_traits::SelectInWord` locates the exact bit within the winning
|
//! `common_traits::SelectInWord` locates the exact bit within the winning
|
||||||
//! word.
|
//! word.
|
||||||
|
|
||||||
use std::fs::{File, OpenOptions};
|
use std::io;
|
||||||
use std::io::{self, Seek, SeekFrom, Write as _};
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use common_traits::SelectInWord;
|
use common_traits::SelectInWord;
|
||||||
use memmap2::{Mmap, MmapMut};
|
use memmap2::{Mmap, MmapMut};
|
||||||
|
|
||||||
|
use crate::mmap_file::{create_mmap_file, open_mmap_file};
|
||||||
|
|
||||||
const MAGIC: [u8; 4] = *b"PRSB";
|
const MAGIC: [u8; 4] = *b"PRSB";
|
||||||
|
|
||||||
/// Words per rank-sample block — 8 words = 512 bits, one cache line's
|
/// Words per rank-sample block — 8 words = 512 bits, one cache line's
|
||||||
@@ -62,14 +63,7 @@ pub struct PersistentRankSelectBitVec {
|
|||||||
|
|
||||||
impl PersistentRankSelectBitVec {
|
impl PersistentRankSelectBitVec {
|
||||||
pub fn open(path: &Path) -> io::Result<Self> {
|
pub fn open(path: &Path) -> io::Result<Self> {
|
||||||
let mmap = unsafe { Mmap::map(&File::open(path)?)? };
|
let (mmap, n) = open_mmap_file(path, MAGIC, HEADER_SIZE)?;
|
||||||
if mmap.len() < HEADER_SIZE {
|
|
||||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "PRSB file too short"));
|
|
||||||
}
|
|
||||||
if mmap[0..4] != MAGIC {
|
|
||||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "bad PRSB magic"));
|
|
||||||
}
|
|
||||||
let n = u64::from_le_bytes(mmap[8..16].try_into().unwrap()) as usize;
|
|
||||||
let total_ones = u64::from_le_bytes(mmap[16..24].try_into().unwrap());
|
let total_ones = u64::from_le_bytes(mmap[16..24].try_into().unwrap());
|
||||||
Ok(Self { mmap, n, total_ones, path: path.to_path_buf() })
|
Ok(Self { mmap, n, total_ones, path: path.to_path_buf() })
|
||||||
}
|
}
|
||||||
@@ -212,16 +206,8 @@ pub struct PersistentRankSelectBitVecBuilder {
|
|||||||
impl PersistentRankSelectBitVecBuilder {
|
impl PersistentRankSelectBitVecBuilder {
|
||||||
pub fn new(n: usize, path: &Path) -> io::Result<Self> {
|
pub fn new(n: usize, path: &Path) -> io::Result<Self> {
|
||||||
let file_size = HEADER_SIZE + bits_bytes(n) + blocks_bytes(n);
|
let file_size = HEADER_SIZE + bits_bytes(n) + blocks_bytes(n);
|
||||||
let mut file = OpenOptions::new()
|
// 8 extra header zero bytes: `total_ones`, patched in close().
|
||||||
.read(true).write(true).create(true).truncate(true)
|
let mmap = create_mmap_file(path, MAGIC, [0u8; 4], n, 8, file_size)?;
|
||||||
.open(path)?;
|
|
||||||
file.write_all(&MAGIC)?;
|
|
||||||
file.write_all(&[0u8; 4])?;
|
|
||||||
file.write_all(&(n as u64).to_le_bytes())?;
|
|
||||||
file.write_all(&0u64.to_le_bytes())?; // total_ones, patched in close()
|
|
||||||
file.seek(SeekFrom::Start(0))?;
|
|
||||||
file.set_len(file_size as u64)?;
|
|
||||||
let mmap = unsafe { MmapMut::map_mut(&file)? };
|
|
||||||
Ok(Self { mmap, n, path: path.to_path_buf() })
|
Ok(Self { mmap, n, path: path.to_path_buf() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user