use crate::buffer_pool::{BufferSize, PooledBuffer, acquire_buffer};
use bytes::Bytes;
use lru::LruCache;
use serde::Serialize;
use std::num::NonZeroUsize;
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SerializationSize {
Tiny,
Small,
Medium,
Large,
XLarge,
}
impl SerializationSize {
#[inline]
pub const fn capacity(self) -> usize {
match self {
Self::Tiny => 256,
Self::Small => 1024,
Self::Medium => 4096,
Self::Large => 16384,
Self::XLarge => 65536,
}
}
#[inline]
pub fn to_buffer_size(self) -> BufferSize {
match self {
Self::Tiny => BufferSize::Tiny,
Self::Small => BufferSize::Small,
Self::Medium => BufferSize::Small, Self::Large => BufferSize::Medium,
Self::XLarge => BufferSize::Large,
}
}
pub fn estimate_from_hint(size_hint: usize) -> Self {
if size_hint <= 256 {
Self::Tiny
} else if size_hint <= 1024 {
Self::Small
} else if size_hint <= 4096 {
Self::Medium
} else if size_hint <= 16384 {
Self::Large
} else {
Self::XLarge
}
}
}
#[inline]
pub fn serialize_json<T: Serialize>(value: &T) -> Result<Bytes, SerializationError> {
serialize_json_with_size(value, SerializationSize::Medium)
}
pub fn serialize_json_with_size<T: Serialize>(
value: &T,
size: SerializationSize,
) -> Result<Bytes, SerializationError> {
SERIALIZATION_STATS.record_serialization();
let mut buffer = acquire_buffer(size.to_buffer_size());
let result = serde_json::to_writer(buffer.as_writer(), value);
match result {
Ok(()) => {
let len = buffer.len();
SERIALIZATION_STATS.record_bytes(len);
Ok(Bytes::copy_from_slice(&buffer[..]))
}
Err(e) => {
SERIALIZATION_STATS.record_error();
Err(SerializationError::Json(e.to_string()))
}
}
}
#[cfg(feature = "simd-json")]
pub fn serialize_json_simd<T: Serialize>(value: &T) -> Result<Bytes, SerializationError> {
serialize_json_simd_with_size(value, SerializationSize::Medium)
}
#[cfg(feature = "simd-json")]
pub fn serialize_json_simd_with_size<T: Serialize>(
value: &T,
_size: SerializationSize,
) -> Result<Bytes, SerializationError> {
SERIALIZATION_STATS.record_serialization();
let vec = crate::json::to_vec(value).map_err(|e| SerializationError::Json(e.to_string()))?;
SERIALIZATION_STATS.record_bytes(vec.len());
Ok(Bytes::from(vec))
}
pub fn serialize_bytes(data: &[u8]) -> Bytes {
SERIALIZATION_STATS.record_serialization();
SERIALIZATION_STATS.record_bytes(data.len());
Bytes::copy_from_slice(data)
}
#[inline]
pub fn serialize_bytes_from_vec(data: Vec<u8>) -> Bytes {
SERIALIZATION_STATS.record_serialization();
SERIALIZATION_STATS.record_bytes(data.len());
Bytes::from(data)
}
#[inline]
pub fn serialize_static(data: &'static [u8]) -> Bytes {
SERIALIZATION_STATS.record_serialization();
SERIALIZATION_STATS.record_bytes(data.len());
Bytes::from_static(data)
}
#[derive(Debug)]
pub struct PooledSerializer {
default_size: SerializationSize,
size_tracker: SizeTracker,
}
impl PooledSerializer {
pub fn new() -> Self {
Self {
default_size: SerializationSize::Medium,
size_tracker: SizeTracker::new(),
}
}
pub fn with_size(size: SerializationSize) -> Self {
Self {
default_size: size,
size_tracker: SizeTracker::new(),
}
}
pub fn serialize<T: Serialize>(&mut self, value: &T) -> Result<Bytes, SerializationError> {
let size = self
.size_tracker
.recommended_size()
.unwrap_or(self.default_size);
let bytes = serialize_json_with_size(value, size)?;
self.size_tracker.record_size(bytes.len());
Ok(bytes)
}
pub fn serialize_typed<T: Serialize>(
&mut self,
value: &T,
type_name: &str,
) -> Result<Bytes, SerializationError> {
let size = self
.size_tracker
.recommended_size_for_type(type_name)
.unwrap_or(self.default_size);
let bytes = serialize_json_with_size(value, size)?;
self.size_tracker
.record_size_for_type(type_name, bytes.len());
Ok(bytes)
}
pub fn stats(&self) -> &SizeTracker {
&self.size_tracker
}
}
impl Default for PooledSerializer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct SizeTracker {
recent_sizes: Vec<usize>,
index: usize,
type_sizes: LruCache<String, TypeSizeInfo>,
total_count: u64,
}
impl SizeTracker {
const HISTORY_SIZE: usize = 64;
const MAX_TYPE_ENTRIES: usize = 256;
pub fn new() -> Self {
Self {
recent_sizes: Vec::with_capacity(Self::HISTORY_SIZE),
index: 0,
type_sizes: LruCache::new(NonZeroUsize::new(Self::MAX_TYPE_ENTRIES).unwrap()),
total_count: 0,
}
}
pub fn record_size(&mut self, size: usize) {
if self.recent_sizes.len() < Self::HISTORY_SIZE {
self.recent_sizes.push(size);
} else {
self.recent_sizes[self.index] = size;
self.index = (self.index + 1) % Self::HISTORY_SIZE;
}
self.total_count += 1;
}
pub fn record_size_for_type(&mut self, type_name: &str, size: usize) {
self.record_size(size);
let info = self
.type_sizes
.get_or_insert_mut(type_name.to_string(), TypeSizeInfo::new);
info.record(size);
}
pub fn recommended_size(&self) -> Option<SerializationSize> {
if self.recent_sizes.is_empty() {
return None;
}
let mut sizes = self.recent_sizes.clone();
sizes.sort_unstable();
let p90_idx = sizes.len() * 90 / 100;
let p90_size = sizes[p90_idx];
Some(SerializationSize::estimate_from_hint(p90_size))
}
pub fn recommended_size_for_type(&self, type_name: &str) -> Option<SerializationSize> {
self.type_sizes
.peek(type_name)
.map(|info| SerializationSize::estimate_from_hint(info.p90_size()))
}
pub fn total_count(&self) -> u64 {
self.total_count
}
pub fn average_size(&self) -> usize {
if self.recent_sizes.is_empty() {
0
} else {
self.recent_sizes.iter().sum::<usize>() / self.recent_sizes.len()
}
}
}
impl Default for SizeTracker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct TypeSizeInfo {
min: usize,
max: usize,
sum: u64,
count: u64,
recent: Vec<usize>,
}
impl TypeSizeInfo {
fn new() -> Self {
Self {
min: usize::MAX,
max: 0,
sum: 0,
count: 0,
recent: Vec::with_capacity(32),
}
}
fn record(&mut self, size: usize) {
self.min = self.min.min(size);
self.max = self.max.max(size);
self.sum += size as u64;
self.count += 1;
if self.recent.len() < 32 {
self.recent.push(size);
} else {
let idx = (self.count as usize) % 32;
self.recent[idx] = size;
}
}
fn p90_size(&self) -> usize {
if self.recent.is_empty() {
return 0;
}
let mut sorted = self.recent.clone();
sorted.sort_unstable();
let idx = sorted.len() * 90 / 100;
sorted[idx]
}
pub fn average(&self) -> usize {
self.sum
.checked_div(self.count)
.map(|v| v as usize)
.unwrap_or(0)
}
pub fn min(&self) -> usize {
if self.min == usize::MAX { 0 } else { self.min }
}
pub fn max(&self) -> usize {
self.max
}
}
#[derive(Debug)]
pub struct PooledResponseBuilder {
buffer: PooledBuffer,
content_type: Option<&'static str>,
}
impl PooledResponseBuilder {
pub fn new() -> Self {
Self {
buffer: acquire_buffer(BufferSize::Small),
content_type: None,
}
}
pub fn with_size(size: SerializationSize) -> Self {
Self {
buffer: acquire_buffer(size.to_buffer_size()),
content_type: None,
}
}
pub fn content_type(mut self, content_type: &'static str) -> Self {
self.content_type = Some(content_type);
self
}
pub fn json<T: Serialize>(mut self, value: &T) -> Result<Self, SerializationError> {
serde_json::to_writer(self.buffer.as_writer(), value)
.map_err(|e| SerializationError::Json(e.to_string()))?;
self.content_type = Some("application/json");
Ok(self)
}
pub fn bytes(mut self, data: &[u8]) -> Self {
self.buffer.extend_from_slice(data);
self
}
pub fn string(mut self, s: &str) -> Self {
self.buffer.extend_from_slice(s.as_bytes());
self
}
pub fn build(self) -> Bytes {
self.buffer.freeze()
}
pub fn get_content_type(&self) -> Option<&'static str> {
self.content_type
}
}
impl Default for PooledResponseBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub enum SerializationError {
Json(String),
BufferOverflow,
Other(String),
}
impl std::fmt::Display for SerializationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Json(e) => write!(f, "JSON serialization error: {}", e),
Self::BufferOverflow => write!(f, "Buffer overflow"),
Self::Other(e) => write!(f, "Serialization error: {}", e),
}
}
}
impl std::error::Error for SerializationError {}
impl From<serde_json::Error> for SerializationError {
fn from(e: serde_json::Error) -> Self {
Self::Json(e.to_string())
}
}
#[derive(Debug, Default)]
pub struct SerializationStats {
serializations: AtomicU64,
bytes_serialized: AtomicU64,
errors: AtomicU64,
}
impl SerializationStats {
fn record_serialization(&self) {
self.serializations.fetch_add(1, Ordering::Relaxed);
}
fn record_bytes(&self, bytes: usize) {
self.bytes_serialized
.fetch_add(bytes as u64, Ordering::Relaxed);
}
fn record_error(&self) {
self.errors.fetch_add(1, Ordering::Relaxed);
}
pub fn serializations(&self) -> u64 {
self.serializations.load(Ordering::Relaxed)
}
pub fn bytes_serialized(&self) -> u64 {
self.bytes_serialized.load(Ordering::Relaxed)
}
pub fn errors(&self) -> u64 {
self.errors.load(Ordering::Relaxed)
}
pub fn average_size(&self) -> usize {
self.bytes_serialized()
.checked_div(self.serializations())
.map(|v| v as usize)
.unwrap_or(0)
}
}
static SERIALIZATION_STATS: SerializationStats = SerializationStats {
serializations: AtomicU64::new(0),
bytes_serialized: AtomicU64::new(0),
errors: AtomicU64::new(0),
};
pub fn serialization_stats() -> &'static SerializationStats {
&SERIALIZATION_STATS
}
trait PooledBufferExt {
fn as_writer(&mut self) -> PooledBufferWriter<'_>;
}
impl PooledBufferExt for PooledBuffer {
fn as_writer(&mut self) -> PooledBufferWriter<'_> {
PooledBufferWriter { buffer: self }
}
}
struct PooledBufferWriter<'a> {
buffer: &'a mut PooledBuffer,
}
impl<'a> std::io::Write for PooledBufferWriter<'a> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.buffer.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct TestUser {
name: String,
age: u32,
}
#[test]
fn test_serialization_size_capacity() {
assert_eq!(SerializationSize::Tiny.capacity(), 256);
assert_eq!(SerializationSize::Small.capacity(), 1024);
assert_eq!(SerializationSize::Medium.capacity(), 4096);
}
#[test]
fn test_serialize_json() {
let user = TestUser {
name: "John".to_string(),
age: 30,
};
let bytes = serialize_json(&user).unwrap();
assert!(!bytes.is_empty());
let parsed: TestUser = serde_json::from_slice(&bytes).unwrap();
assert_eq!(parsed, user);
}
#[test]
fn test_serialize_json_with_size() {
let user = TestUser {
name: "Jane".to_string(),
age: 25,
};
let bytes = serialize_json_with_size(&user, SerializationSize::Tiny).unwrap();
assert!(!bytes.is_empty());
}
#[test]
fn test_serialize_json_recycles_pooled_buffer() {
std::thread::spawn(|| {
let stats = crate::buffer_pool::pool_stats();
let user = TestUser {
name: "Pool".to_string(),
age: 1,
};
let first = serialize_json_with_size(&user, SerializationSize::Medium).unwrap();
let hits_before = stats.hits();
let second = serialize_json_with_size(&user, SerializationSize::Medium).unwrap();
assert!(
stats.hits() > hits_before,
"second serialization should hit the pool"
);
assert_eq!(first, second);
})
.join()
.unwrap();
}
#[test]
fn test_serialize_bytes() {
let data = b"Hello, World!";
let bytes = serialize_bytes(data);
assert_eq!(&bytes[..], data);
}
#[test]
fn test_serialize_bytes_from_vec() {
let data = vec![1, 2, 3, 4, 5];
let bytes = serialize_bytes_from_vec(data.clone());
assert_eq!(&bytes[..], &data[..]);
}
#[test]
fn test_serialize_static() {
let bytes = serialize_static(b"static data");
assert_eq!(&bytes[..], b"static data");
}
#[test]
fn test_pooled_serializer() {
let mut serializer = PooledSerializer::new();
let user = TestUser {
name: "Test".to_string(),
age: 20,
};
for _ in 0..10 {
let bytes = serializer.serialize(&user).unwrap();
assert!(!bytes.is_empty());
}
assert!(serializer.stats().total_count() >= 10);
}
#[test]
fn test_size_tracker() {
let mut tracker = SizeTracker::new();
for size in &[100, 200, 300, 400, 500] {
tracker.record_size(*size);
}
assert_eq!(tracker.total_count(), 5);
assert_eq!(tracker.average_size(), 300);
}
#[test]
fn test_size_tracker_by_type() {
let mut tracker = SizeTracker::new();
tracker.record_size_for_type("User", 100);
tracker.record_size_for_type("User", 150);
tracker.record_size_for_type("Post", 500);
let user_size = tracker.recommended_size_for_type("User");
assert!(user_size.is_some());
}
#[test]
fn test_pooled_response_builder() {
let builder = PooledResponseBuilder::new()
.content_type("text/plain")
.string("Hello, World!");
assert_eq!(builder.get_content_type(), Some("text/plain"));
let bytes = builder.build();
assert_eq!(&bytes[..], b"Hello, World!");
}
#[test]
fn test_pooled_response_builder_json() {
let user = TestUser {
name: "Builder".to_string(),
age: 42,
};
let builder = PooledResponseBuilder::new().json(&user).unwrap();
assert_eq!(builder.get_content_type(), Some("application/json"));
let bytes = builder.build();
let parsed: TestUser = serde_json::from_slice(&bytes).unwrap();
assert_eq!(parsed, user);
}
#[test]
fn test_serialization_stats() {
let stats = serialization_stats();
let _ = stats.serializations();
let _ = stats.bytes_serialized();
let _ = stats.errors();
let _ = stats.average_size();
}
#[test]
fn test_serialization_error_display() {
let err = SerializationError::Json("test error".to_string());
assert!(err.to_string().contains("JSON"));
let err = SerializationError::BufferOverflow;
assert!(err.to_string().contains("overflow"));
}
}