#![allow(dead_code)]
use std::path::Path;
use crate::catalog::{
CHUNK_INDEX_HEADER_V1, CHUNK_PAYLOAD_CODEC_V1, ChunkIndexEntryV1, DATASET_DTYPE_TAG_V1,
FileExecutionSettingsV1, RawArrayWrite, read_tet_summary_v1, write_raw_array_file,
};
use crate::layout::mmap_file_read;
pub mod index_patch {
use super::*;
pub const CHUNK_INDEX_HDR_LEN: usize = CHUNK_INDEX_HEADER_V1.header_len;
pub const ENTRY_RAW_BYTE_LEN_OFFSET: usize = ChunkIndexEntryV1::WIRE_RAW_BYTE_LEN_OFFSET;
pub const ENTRY_STORED_BYTE_LEN_OFFSET: usize = ChunkIndexEntryV1::WIRE_STORED_BYTE_LEN_OFFSET;
pub const ENTRY_PAYLOAD_OFFSET: usize = ChunkIndexEntryV1::WIRE_PAYLOAD_OFFSET;
pub const ENTRY_CODEC_OFFSET: usize = ChunkIndexEntryV1::WIRE_CODEC_OFFSET;
pub fn first_index_field_offset(path: &Path, field_offset_in_entry: usize) -> usize {
let mmap0 = mmap_file_read(path).unwrap();
let s = read_tet_summary_v1(&mmap0).unwrap();
let idx = usize::try_from(s.superblock.chunk_index_offset).unwrap();
idx + CHUNK_INDEX_HDR_LEN + field_offset_in_entry
}
pub fn patch_first_index_entry_u64(path: &Path, field_offset_in_entry: usize, value: u64) {
let patch_at = first_index_field_offset(path, field_offset_in_entry);
let mut bytes = std::fs::read(path).unwrap();
assert!(
patch_at + 8 <= bytes.len(),
"patch offset out of range for test fixture"
);
bytes[patch_at..patch_at + 8].copy_from_slice(&value.to_le_bytes());
std::fs::write(path, &bytes).unwrap();
}
pub fn patch_first_index_entry_raw_and_stored(
path: &Path,
raw_byte_len: u64,
stored_byte_len: u64,
) {
let raw_at = first_index_field_offset(path, ENTRY_RAW_BYTE_LEN_OFFSET);
let stored_at = first_index_field_offset(path, ENTRY_STORED_BYTE_LEN_OFFSET);
let mut bytes = std::fs::read(path).unwrap();
assert!(stored_at + 8 <= bytes.len(), "patch offset out of range");
bytes[raw_at..raw_at + 8].copy_from_slice(&raw_byte_len.to_le_bytes());
bytes[stored_at..stored_at + 8].copy_from_slice(&stored_byte_len.to_le_bytes());
std::fs::write(path, &bytes).unwrap();
}
pub fn truncate_file(path: &Path, new_len: u64) {
std::fs::OpenOptions::new()
.write(true)
.open(path)
.unwrap()
.set_len(new_len)
.unwrap();
}
}
pub const SHAPE_2X3: [u64; 2] = [2, 3];
pub const CHUNK_2X2: [u64; 2] = [2, 2];
pub fn le_row_major_2x3_f32_one_to_six() -> Vec<u8> {
let mut data = vec![0u8; 24];
for (slot, n) in data.chunks_exact_mut(4).zip(1_u8..=6) {
slot.copy_from_slice(&f32::from(n).to_le_bytes());
}
data
}
pub fn le_row_major_2x3_u8_one_to_six() -> Vec<u8> {
(1_u8..=6).collect()
}
pub fn le_row_major_2x3_u16_one_to_six() -> Vec<u8> {
let mut data = vec![0u8; 12];
for (slot, n) in data.chunks_exact_mut(2).zip(1_u16..=6) {
slot.copy_from_slice(&n.to_le_bytes());
}
data
}
pub fn le_row_major_2x3_i16_one_to_six() -> Vec<u8> {
let mut data = vec![0u8; 12];
for (slot, n) in data.chunks_exact_mut(2).zip(1_i16..=6) {
slot.copy_from_slice(&n.to_le_bytes());
}
data
}
pub fn le_row_major_2x3_i32_one_to_six() -> Vec<u8> {
let mut data = vec![0u8; 24];
for (slot, n) in data.chunks_exact_mut(4).zip(1_i32..=6) {
slot.copy_from_slice(&n.to_le_bytes());
}
data
}
pub fn le_row_major_2x3_i64_one_to_six() -> Vec<u8> {
let mut data = vec![0u8; 48];
for (slot, n) in data.chunks_exact_mut(8).zip(1_i64..=6) {
slot.copy_from_slice(&n.to_le_bytes());
}
data
}
pub fn le_row_major_2x3_f64_one_to_six() -> Vec<u8> {
let mut data = vec![0u8; 48];
for (slot, n) in data.chunks_exact_mut(8).zip(1_u64..=6) {
slot.copy_from_slice(&(n as f64).to_le_bytes());
}
data
}
fn write_multichunk_2x3(
path: &Path,
dataset_name: &str,
chunk_codec: u32,
dtype: u32,
data: &[u8],
) {
write_raw_array_file(
path,
&RawArrayWrite {
name: dataset_name,
dtype,
shape: &SHAPE_2X3,
chunk_shape: &CHUNK_2X2,
chunk_codec,
data,
file_execution: None,
},
)
.unwrap();
}
fn write_multichunk_2x3_f32(path: &Path, dataset_name: &str, chunk_codec: u32, data: &[u8]) {
write_multichunk_2x3(
path,
dataset_name,
chunk_codec,
DATASET_DTYPE_TAG_V1.f32,
data,
);
}
pub fn write_multichunk_2x3_tiles(path: &Path, dataset_name: &str) {
write_multichunk_2x3_f32(
path,
dataset_name,
CHUNK_PAYLOAD_CODEC_V1.raw,
&le_row_major_2x3_f32_one_to_six(),
);
}
pub fn write_multichunk_2x3_f64_tiles(path: &Path, dataset_name: &str) {
write_multichunk_2x3_f64(
path,
dataset_name,
CHUNK_PAYLOAD_CODEC_V1.raw,
&le_row_major_2x3_f64_one_to_six(),
);
}
fn write_multichunk_2x3_f64(path: &Path, dataset_name: &str, chunk_codec: u32, data: &[u8]) {
write_multichunk_2x3(
path,
dataset_name,
chunk_codec,
DATASET_DTYPE_TAG_V1.f64,
data,
);
}
pub fn write_multichunk_2x3_u16_tiles(path: &Path, dataset_name: &str) {
write_multichunk_2x3(
path,
dataset_name,
CHUNK_PAYLOAD_CODEC_V1.raw,
DATASET_DTYPE_TAG_V1.u16,
&le_row_major_2x3_u16_one_to_six(),
);
}
pub fn le_row_major_2x3_u32_one_to_six() -> Vec<u8> {
let mut data = vec![0u8; 24];
for (slot, n) in data.chunks_exact_mut(4).zip(1_u32..=6) {
slot.copy_from_slice(&n.to_le_bytes());
}
data
}
pub fn le_row_major_2x3_u64_one_to_six() -> Vec<u8> {
let mut data = vec![0u8; 48];
for (slot, n) in data.chunks_exact_mut(8).zip(1_u64..=6) {
slot.copy_from_slice(&n.to_le_bytes());
}
data
}
pub fn le_row_major_2x3_f16_one_to_six() -> Vec<u8> {
let mut data = vec![0u8; 12];
for (slot, n) in data.chunks_exact_mut(2).zip(1_u32..=6) {
let v = half::f16::from_f32(n as f32);
slot.copy_from_slice(&v.to_bits().to_le_bytes());
}
data
}
pub fn write_multichunk_2x3_u32_tiles(path: &Path, dataset_name: &str) {
write_multichunk_2x3(
path,
dataset_name,
CHUNK_PAYLOAD_CODEC_V1.raw,
DATASET_DTYPE_TAG_V1.u32,
&le_row_major_2x3_u32_one_to_six(),
);
}
pub fn write_multichunk_2x3_u64_tiles(path: &Path, dataset_name: &str) {
write_multichunk_2x3(
path,
dataset_name,
CHUNK_PAYLOAD_CODEC_V1.raw,
DATASET_DTYPE_TAG_V1.u64,
&le_row_major_2x3_u64_one_to_six(),
);
}
pub fn write_multichunk_2x3_f16_tiles(path: &Path, dataset_name: &str) {
write_multichunk_2x3(
path,
dataset_name,
CHUNK_PAYLOAD_CODEC_V1.raw,
DATASET_DTYPE_TAG_V1.f16,
&le_row_major_2x3_f16_one_to_six(),
);
}
pub fn write_multichunk_2x3_i16_tiles(path: &Path, dataset_name: &str) {
write_multichunk_2x3(
path,
dataset_name,
CHUNK_PAYLOAD_CODEC_V1.raw,
DATASET_DTYPE_TAG_V1.i16,
&le_row_major_2x3_i16_one_to_six(),
);
}
pub fn write_multichunk_2x3_u8_tiles(path: &Path, dataset_name: &str) {
write_multichunk_2x3(
path,
dataset_name,
CHUNK_PAYLOAD_CODEC_V1.raw,
DATASET_DTYPE_TAG_V1.u8,
&le_row_major_2x3_u8_one_to_six(),
);
}
pub fn write_multichunk_2x3_i32_tiles(path: &Path, dataset_name: &str) {
write_multichunk_2x3(
path,
dataset_name,
CHUNK_PAYLOAD_CODEC_V1.raw,
DATASET_DTYPE_TAG_V1.i32,
&le_row_major_2x3_i32_one_to_six(),
);
}
pub fn write_multichunk_2x3_i64_tiles(path: &Path, dataset_name: &str) {
write_multichunk_2x3(
path,
dataset_name,
CHUNK_PAYLOAD_CODEC_V1.raw,
DATASET_DTYPE_TAG_V1.i64,
&le_row_major_2x3_i64_one_to_six(),
);
}
pub fn write_multichunk_2x3_zstd(path: &Path, dataset_name: &str, data: &[u8]) {
write_multichunk_2x3_f32(path, dataset_name, CHUNK_PAYLOAD_CODEC_V1.zstd, data);
}
pub fn write_multichunk_2x3_with_execution(
path: &Path,
dataset_name: &str,
file_execution: FileExecutionSettingsV1,
) {
write_raw_array_file(
path,
&RawArrayWrite {
name: dataset_name,
dtype: DATASET_DTYPE_TAG_V1.f32,
shape: &SHAPE_2X3,
chunk_shape: &CHUNK_2X2,
chunk_codec: CHUNK_PAYLOAD_CODEC_V1.raw,
data: &le_row_major_2x3_f32_one_to_six(),
file_execution: Some(file_execution),
},
)
.unwrap();
}
pub fn write_multichunk_2x3_zero_zstd(path: &Path, dataset_name: &str) {
write_multichunk_2x3_zstd(path, dataset_name, &vec![0u8; 24]);
}
pub const VERIFY_LARGE_SHAPE: [u64; 2] = [34, 64];
pub const VERIFY_LARGE_CHUNK: [u64; 2] = [4, 4];
pub fn write_verify_large_f32(path: &Path, dataset_name: &str) {
let ne = usize::try_from(VERIFY_LARGE_SHAPE[0] * VERIFY_LARGE_SHAPE[1]).unwrap();
let mut data = vec![0u8; ne * 4];
for (slot, i) in data.chunks_exact_mut(4).zip(0_u32..) {
slot.copy_from_slice(&(i as f32).to_le_bytes());
}
write_raw_array_file(
path,
&RawArrayWrite {
name: dataset_name,
dtype: DATASET_DTYPE_TAG_V1.f32,
shape: &VERIFY_LARGE_SHAPE,
chunk_shape: &VERIFY_LARGE_CHUNK,
chunk_codec: CHUNK_PAYLOAD_CODEC_V1.raw,
data: &data,
file_execution: None,
},
)
.unwrap();
}
fn corrupt_footer_json_bytes(data: &mut [u8]) {
const TAIL: usize = 16;
let json_len = u64::from_le_bytes(
data[data.len() - TAIL..data.len() - TAIL + 8]
.try_into()
.unwrap(),
);
let json_end = data.len() - TAIL;
let json_start = json_end - usize::try_from(json_len).unwrap();
for b in &mut data[json_start..json_end] {
*b = b'X';
}
}
pub fn write_repair_plan_fixture(path: &Path, dataset_name: &str) {
use crate::catalog::{FooterBlobV1, TetMetadataV1, write_footer_blob};
write_multichunk_2x3_tiles(path, dataset_name);
write_footer_blob(
path,
&FooterBlobV1 {
history: Vec::new(),
metadata: Some(TetMetadataV1::default()),
metadata_ref: None,
},
)
.unwrap();
let mut data = std::fs::read(path).unwrap();
corrupt_footer_json_bytes(&mut data);
std::fs::write(path, &data).unwrap();
}
pub fn write_tracked_small_tet_fixtures(dir: &Path) {
std::fs::create_dir_all(dir).unwrap();
write_multichunk_2x3_tiles(&dir.join("sample.tet"), "temperature");
write_verify_large_f32(&dir.join("large.tet"), "a");
write_repair_plan_fixture(&dir.join("plan.tet"), "a");
write_multichunk_2x3_u8_tiles(&dir.join("multichunk_u8.tet"), "a");
write_multichunk_2x3_u32_tiles(&dir.join("multichunk_u32.tet"), "a");
write_multichunk_2x3_f16_tiles(&dir.join("multichunk_f16.tet"), "a");
}
pub fn tracked_small_tet_dir() -> std::path::PathBuf {
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fixtures/small/tet")
}
pub mod query_files {
use std::path::PathBuf;
use crate::query::{QueryDocument, parse_query_json, parse_query_toml};
#[must_use]
pub fn dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fixtures/queries")
}
#[must_use]
pub fn path(stem: &str, ext: &str) -> PathBuf {
dir().join(format!("{stem}.{ext}"))
}
#[must_use]
pub fn read(stem: &str, ext: &str) -> String {
let path = path(stem, ext);
std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("{}: {e}", path.display()))
}
#[must_use]
pub fn json(stem: &str) -> QueryDocument {
parse_query_json(&read(stem, "json")).unwrap()
}
#[must_use]
pub fn toml(stem: &str) -> QueryDocument {
parse_query_toml(&read(stem, "toml")).unwrap()
}
}