use crate::build_config::get_build_seed;
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
#[cfg(not(feature = "std"))]
use alloc::string::String;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[inline(never)] pub fn decrypt_string(encrypted: &[u8], string_id: u64) -> String {
let seed = get_build_seed();
let mut decrypted = Vec::with_capacity(encrypted.len());
for (i, &byte) in encrypted.iter().enumerate() {
let key_byte = derive_key_byte(&seed, string_id, i as u64);
decrypted.push(byte ^ key_byte);
}
String::from_utf8(decrypted).unwrap_or_else(|_| String::new())
}
#[inline(never)]
pub fn decrypt_static(encrypted: &'static [u8], string_id: u64) -> &'static str {
let decrypted = decrypt_string(encrypted, string_id);
Box::leak(decrypted.into_boxed_str())
}
#[inline(always)]
fn derive_key_byte(seed: &[u8; 32], string_id: u64, position: u64) -> u8 {
let mut hash = 0xcbf29ce484222325u64;
for &byte in seed {
hash ^= byte as u64;
hash = hash.wrapping_mul(0x100000001b3);
}
for &byte in &string_id.to_le_bytes() {
hash ^= byte as u64;
hash = hash.wrapping_mul(0x100000001b3);
}
for &byte in &position.to_le_bytes() {
hash ^= byte as u64;
hash = hash.wrapping_mul(0x100000001b3);
}
(hash & 0xFF) as u8
}
#[cfg(feature = "std")]
pub mod cached {
#[allow(unused_imports)]
use super::{decrypt_string, decrypt_static};
use std::sync::OnceLock;
use std::collections::HashMap;
static STRING_CACHE: OnceLock<std::sync::RwLock<HashMap<u64, &'static str>>> = OnceLock::new();
pub fn get_or_decrypt(encrypted: &'static [u8], string_id: u64) -> &'static str {
let cache = STRING_CACHE.get_or_init(|| std::sync::RwLock::new(HashMap::new()));
{
let read = cache.read().unwrap();
if let Some(&s) = read.get(&string_id) {
return s;
}
}
let decrypted = super::decrypt_static(encrypted, string_id);
{
let mut write = cache.write().unwrap();
write.insert(string_id, decrypted);
}
decrypted
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decrypt_roundtrip() {
let original = "Hello, World!";
let string_id = 0x12345678u64;
let seed = get_build_seed();
let encrypted: Vec<u8> = original
.bytes()
.enumerate()
.map(|(i, b)| b ^ derive_key_byte(&seed, string_id, i as u64))
.collect();
let decrypted = decrypt_string(&encrypted, string_id);
assert_eq!(original, decrypted);
}
#[test]
fn test_different_ids_different_keys() {
let seed = get_build_seed();
let key1 = derive_key_byte(&seed, 1, 0);
let key2 = derive_key_byte(&seed, 2, 0);
assert_ne!(key1, key2);
}
#[test]
fn test_different_positions_different_keys() {
let seed = get_build_seed();
let key1 = derive_key_byte(&seed, 1, 0);
let key2 = derive_key_byte(&seed, 1, 1);
assert_ne!(key1, key2);
}
}