oxirs_chat/memory_optimization/
pooling.rs1use anyhow::{anyhow, Result};
4use std::collections::VecDeque;
5
6pub struct MemoryPool {
8 max_size: usize,
9 current_usage: usize,
10 buffers: VecDeque<Vec<u8>>,
11}
12
13impl MemoryPool {
14 pub fn new(max_size: usize) -> Self {
15 Self {
16 max_size,
17 current_usage: 0,
18 buffers: VecDeque::new(),
19 }
20 }
21
22 pub fn allocate(&mut self, size: usize) -> Result<PooledBuffer> {
24 if let Some(buffer) = self.find_suitable_buffer(size) {
26 return Ok(PooledBuffer::Pooled { data: buffer });
27 }
28
29 if self.current_usage + size > self.max_size {
31 return Err(anyhow!("Pool exhausted"));
32 }
33
34 let buffer = vec![0u8; size];
36 self.current_usage += size;
37
38 Ok(PooledBuffer::Pooled { data: buffer })
39 }
40
41 pub fn deallocate(&mut self, buffer: Vec<u8>) {
43 let size = buffer.len();
44
45 if self.buffers.len() < 100 {
47 self.buffers.push_back(buffer);
49 } else {
50 self.current_usage = self.current_usage.saturating_sub(size);
51 }
52 }
53
54 fn find_suitable_buffer(&mut self, size: usize) -> Option<Vec<u8>> {
56 for i in 0..self.buffers.len() {
58 if self.buffers[i].len() >= size {
59 return self.buffers.remove(i);
60 }
61 }
62 None
63 }
64
65 pub fn current_usage(&self) -> usize {
67 self.current_usage
68 }
69
70 pub fn available(&self) -> usize {
72 self.max_size.saturating_sub(self.current_usage)
73 }
74}
75
76pub enum PooledBuffer {
78 Pooled { data: Vec<u8> },
79 Heap { data: Vec<u8> },
80}
81
82impl PooledBuffer {
83 pub fn new_heap(size: usize) -> Result<Self> {
84 Ok(Self::Heap {
85 data: vec![0u8; size],
86 })
87 }
88
89 pub fn len(&self) -> usize {
90 match self {
91 Self::Pooled { data } => data.len(),
92 Self::Heap { data } => data.len(),
93 }
94 }
95
96 pub fn is_empty(&self) -> bool {
97 self.len() == 0
98 }
99
100 pub fn as_slice(&self) -> &[u8] {
101 match self {
102 Self::Pooled { data } => data,
103 Self::Heap { data } => data,
104 }
105 }
106
107 pub fn as_mut_slice(&mut self) -> &mut [u8] {
108 match self {
109 Self::Pooled { data } => data,
110 Self::Heap { data } => data,
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_pool_allocation() {
121 let mut pool = MemoryPool::new(10240);
122
123 let buffer = pool.allocate(1024).expect("should succeed");
124 assert_eq!(buffer.len(), 1024);
125 assert_eq!(pool.current_usage(), 1024);
126 }
127
128 #[test]
129 fn test_pool_reuse() {
130 let mut pool = MemoryPool::new(10240);
131
132 let buffer1 = pool.allocate(1024).expect("should succeed");
133 let data = match buffer1 {
134 PooledBuffer::Pooled { data } => data,
135 PooledBuffer::Heap { data } => data,
136 };
137
138 pool.deallocate(data);
139
140 let buffer2 = pool.allocate(1024).expect("should succeed");
141 assert_eq!(buffer2.len(), 1024);
142 }
143
144 #[test]
145 fn test_pool_exhaustion() {
146 let mut pool = MemoryPool::new(1024);
147
148 let _b1 = pool.allocate(512).expect("should succeed");
149 let _b2 = pool.allocate(512).expect("should succeed");
150
151 let result = pool.allocate(1);
153 assert!(result.is_err());
154 }
155
156 #[test]
157 fn test_pool_available() {
158 let mut pool = MemoryPool::new(10240);
159
160 assert_eq!(pool.available(), 10240);
161
162 let _buffer = pool.allocate(1024).expect("should succeed");
163 assert_eq!(pool.available(), 9216);
164 }
165}