blob: 24fcae0e605fa5ada62c19a13fc62b918b133fe8 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use std::fs;
use std::path::Path;
const BLOB_MAGIC_NUMBER_BYTES: [u8; 4] = 1481511375_i32.to_le_bytes();
const BLOB_ENTRY_OVERHEAD: usize = 16;
const BLOB_FORMAT_VERSION: u8 = 1;
pub(crate) fn build_blob_file_bytes(rows: &[Option<&[u8]>]) -> Vec<u8> {
let mut file_bytes = Vec::new();
let mut lengths = Vec::with_capacity(rows.len());
for row in rows {
match row {
Some(payload) => {
let entry_length = payload
.len()
.checked_add(BLOB_ENTRY_OVERHEAD)
.and_then(|len| i64::try_from(len).ok())
.unwrap_or_else(|| {
panic!("Blob payload length {} exceeds test helper limits", payload.len())
});
lengths.push(entry_length);
file_bytes.extend_from_slice(&BLOB_MAGIC_NUMBER_BYTES);
file_bytes.extend_from_slice(payload);
let mut hasher = crc32fast::Hasher::new();
hasher.update(&BLOB_MAGIC_NUMBER_BYTES);
hasher.update(payload);
let entry_length_bytes = entry_length.to_le_bytes();
file_bytes.extend_from_slice(&entry_length_bytes);
hasher.update(&entry_length_bytes);
file_bytes.extend_from_slice(&hasher.finalize().to_le_bytes());
}
None => lengths.push(-1),
}
}
let index_bytes = encode_delta_varints(&lengths);
let index_length = i32::try_from(index_bytes.len()).unwrap_or_else(|_| {
panic!(
"Blob index length {} exceeds test helper limits",
index_bytes.len()
)
});
file_bytes.extend_from_slice(&index_bytes);
file_bytes.extend_from_slice(&index_length.to_le_bytes());
file_bytes.push(BLOB_FORMAT_VERSION);
file_bytes
}
pub(crate) fn write_blob_file(path: &Path, rows: &[Option<&[u8]>]) {
let file_bytes = build_blob_file_bytes(rows);
fs::write(path, file_bytes)
.unwrap_or_else(|e| panic!("Failed to write blob test file {path:?}: {e}"));
}
pub(crate) fn encode_delta_varints(values: &[i64]) -> Vec<u8> {
if values.is_empty() {
return Vec::new();
}
let mut encoded = Vec::new();
let mut previous = 0_i64;
for (idx, value) in values.iter().copied().enumerate() {
let delta = if idx == 0 { value } else { value - previous };
previous = value;
encode_varint(delta, &mut encoded);
}
encoded
}
fn encode_varint(value: i64, out: &mut Vec<u8>) {
let mut remaining = ((value << 1) ^ (value >> 63)) as u64;
while (remaining & !0x7f) != 0 {
out.push(((remaining & 0x7f) as u8) | 0x80);
remaining >>= 7;
}
out.push(remaining as u8);
}