pub struct SecretMemoryPool {
small_strings: Vec<String>,
medium_strings: Vec<String>,
large_strings: Vec<String>,
}
impl SecretMemoryPool {
const SMALL_SIZE: usize = 64;
const MEDIUM_SIZE: usize = 1024;
const POOL_INITIAL_CAPACITY: usize = 16;
pub fn new() -> Self {
Self {
small_strings: Vec::with_capacity(Self::POOL_INITIAL_CAPACITY),
medium_strings: Vec::with_capacity(Self::POOL_INITIAL_CAPACITY),
large_strings: Vec::with_capacity(Self::POOL_INITIAL_CAPACITY),
}
}
pub fn get_string(&mut self, size_hint: usize) -> String {
if size_hint <= Self::SMALL_SIZE {
self.small_strings
.pop()
.unwrap_or_else(|| String::with_capacity(Self::SMALL_SIZE))
} else if size_hint <= Self::MEDIUM_SIZE {
self.medium_strings
.pop()
.unwrap_or_else(|| String::with_capacity(Self::MEDIUM_SIZE))
} else {
self.large_strings
.pop()
.unwrap_or_else(|| String::with_capacity(size_hint))
}
}
pub fn return_string(&mut self, mut s: String) {
s.clear();
let capacity = s.capacity();
if capacity <= Self::SMALL_SIZE && self.small_strings.len() < Self::POOL_INITIAL_CAPACITY {
self.small_strings.push(s);
} else if capacity <= Self::MEDIUM_SIZE
&& self.medium_strings.len() < Self::POOL_INITIAL_CAPACITY
{
self.medium_strings.push(s);
} else if self.large_strings.len() < Self::POOL_INITIAL_CAPACITY {
self.large_strings.push(s);
}
}
}
impl Default for SecretMemoryPool {
fn default() -> Self {
Self::new()
}
}
pub mod binary_optimization {
use std::borrow::Cow;
use zeroize::Zeroize;
#[derive(Clone)]
pub enum OptimizedBinary {
Zeros(usize),
Ones(usize),
Repeated(u8, usize),
Small([u8; 32], usize),
Large(Vec<u8>),
}
impl OptimizedBinary {
pub fn from_slice(data: &[u8]) -> Self {
let len = data.len();
if len == 0 {
return Self::Small([0; 32], 0);
}
let first_byte = data[0];
if data.iter().all(|&b| b == 0) {
Self::Zeros(len)
} else if data.iter().all(|&b| b == 0xFF) {
Self::Ones(len)
} else if data.iter().all(|&b| b == first_byte) {
Self::Repeated(first_byte, len)
} else if len <= 32 {
let mut small = [0; 32];
small[..len].copy_from_slice(data);
Self::Small(small, len)
} else {
Self::Large(data.to_vec())
}
}
pub fn as_bytes(&self) -> Cow<'_, [u8]> {
match self {
Self::Zeros(len) => Cow::Owned(vec![0; *len]),
Self::Ones(len) => Cow::Owned(vec![0xFF; *len]),
Self::Repeated(byte, len) => Cow::Owned(vec![*byte; *len]),
Self::Small(data, len) => Cow::Borrowed(&data[..*len]),
Self::Large(data) => Cow::Borrowed(data),
}
}
pub fn len(&self) -> usize {
match self {
Self::Zeros(len) | Self::Ones(len) | Self::Repeated(_, len) => *len,
Self::Small(_, len) => *len,
Self::Large(data) => data.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl Zeroize for OptimizedBinary {
fn zeroize(&mut self) {
match self {
Self::Zeros(_) | Self::Ones(_) | Self::Repeated(_, _) => {
}
Self::Small(data, len) => {
data[..*len].zeroize();
*len = 0;
}
Self::Large(data) => {
data.zeroize();
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct MemoryStats {
pub total_secrets: usize,
pub string_secrets: usize,
pub binary_secrets: usize,
pub record_secrets: usize,
pub list_secrets: usize,
pub estimated_memory_kb: usize,
}
impl MemoryStats {
pub fn new() -> Self {
Self {
total_secrets: 0,
string_secrets: 0,
binary_secrets: 0,
record_secrets: 0,
list_secrets: 0,
estimated_memory_kb: 0,
}
}
pub fn add_string_secret(&mut self, size: usize) {
self.total_secrets += 1;
self.string_secrets += 1;
self.estimated_memory_kb += (size + std::mem::size_of::<String>()).div_ceil(1024);
}
pub fn add_binary_secret(&mut self, size: usize) {
self.total_secrets += 1;
self.binary_secrets += 1;
self.estimated_memory_kb += (size + std::mem::size_of::<Vec<u8>>()).div_ceil(1024);
}
pub fn memory_efficiency_ratio(&self) -> f64 {
if self.total_secrets == 0 {
return 1.0;
}
let baseline_kb = self.total_secrets * 64 / 1024; if baseline_kb == 0 {
1.0
} else {
baseline_kb as f64 / self.estimated_memory_kb.max(1) as f64
}
}
}
impl Default for MemoryStats {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_redacted_string_templating() {
let s1 = crate::redaction::get_cached_redacted_string(None, "string");
let s2 = crate::redaction::get_cached_redacted_string(None, "string");
assert_eq!(s1, "<redacted:string>");
assert_eq!(s2, "<redacted:string>");
assert_eq!(
crate::redaction::get_cached_redacted_string(None, "int"),
"<redacted:int>"
);
assert_eq!(
crate::redaction::get_cached_redacted_string(None, "float"),
"<redacted:float>"
);
assert_eq!(
crate::redaction::get_cached_redacted_string(None, "bool"),
"<redacted:bool>"
);
}
#[test]
fn test_binary_optimization() {
use binary_optimization::OptimizedBinary;
let zeros = vec![0; 1000];
let opt_zeros = OptimizedBinary::from_slice(&zeros);
assert!(matches!(opt_zeros, OptimizedBinary::Zeros(1000)));
assert_eq!(opt_zeros.len(), 1000);
let ones = vec![0xFF; 500];
let opt_ones = OptimizedBinary::from_slice(&ones);
assert!(matches!(opt_ones, OptimizedBinary::Ones(500)));
let repeated = vec![0xAA; 200];
let opt_repeated = OptimizedBinary::from_slice(&repeated);
assert!(matches!(opt_repeated, OptimizedBinary::Repeated(0xAA, 200)));
let small_data = vec![1, 2, 3, 4, 5];
let opt_small = OptimizedBinary::from_slice(&small_data);
assert!(matches!(opt_small, OptimizedBinary::Small(_, 5)));
}
#[test]
fn test_memory_pool() {
let mut pool = SecretMemoryPool::new();
let s1 = pool.get_string(32);
let s2 = pool.get_string(100);
let s3 = pool.get_string(2000);
assert!(s1.capacity() >= 32);
assert!(s2.capacity() >= 100);
assert!(s3.capacity() >= 2000);
pool.return_string(s1);
pool.return_string(s2);
pool.return_string(s3);
let s4 = pool.get_string(30);
assert!(s4.capacity() >= 30);
}
#[test]
fn test_memory_stats() {
let mut stats = MemoryStats::new();
assert_eq!(stats.total_secrets, 0);
stats.add_string_secret(100);
stats.add_binary_secret(200);
assert_eq!(stats.total_secrets, 2);
assert_eq!(stats.string_secrets, 1);
assert_eq!(stats.binary_secrets, 1);
assert!(stats.estimated_memory_kb > 0);
}
#[test]
fn test_configurable_redacted_string_with_value() {
use crate::config::RedactionContext;
let result = crate::redaction::get_redacted_string_with_value(
"string",
RedactionContext::Display,
Some("secret_value"),
);
assert!(result.contains("redacted"));
let result = crate::redaction::get_redacted_string_with_value::<String>(
"string",
RedactionContext::Display,
None,
);
assert!(result.contains("redacted"));
}
#[test]
fn test_configurable_redacted_string_with_generic_value() {
use crate::config::RedactionContext;
let result = crate::redaction::get_redacted_string_with_value(
"int",
RedactionContext::Display,
Some(&42),
);
assert!(result.contains("redacted"));
let result = crate::redaction::get_redacted_string_with_value(
"bool",
RedactionContext::Display,
Some(&true),
);
assert!(result.contains("redacted"));
let result = crate::redaction::get_redacted_string_with_value(
"float",
RedactionContext::Display,
Some(&2.5),
);
assert!(result.contains("redacted"));
}
#[test]
fn test_redacted_string_with_length() {
let result = crate::redaction::get_redacted_string_with_length("password", Some(12));
assert_eq!(result, "<redacted:password>");
let result = crate::redaction::get_redacted_string_with_length("token", None);
assert_eq!(result, "<redacted:token>");
}
}