armature_core/
response_buffer.rs

1//! Pre-Allocated Response Buffer
2//!
3//! This module provides pre-allocated buffers for response bodies to avoid
4//! reallocations during response building.
5//!
6//! ## Performance
7//!
8//! Most HTTP responses are small (< 1KB for JSON APIs). By pre-allocating
9//! a default buffer size, we avoid the overhead of:
10//! - Initial allocation when body is first written
11//! - Reallocations as body grows
12//! - Memory fragmentation from small allocations
13//!
14//! ## Default Sizes
15//!
16//! - `DEFAULT_RESPONSE_CAPACITY`: 512 bytes (covers most small JSON responses)
17//! - `MEDIUM_RESPONSE_CAPACITY`: 4096 bytes (4KB, typical API response)
18//! - `LARGE_RESPONSE_CAPACITY`: 65536 bytes (64KB, large responses)
19//!
20//! ## Usage
21//!
22//! ```rust,ignore
23//! use armature_core::response_buffer::{ResponseBuffer, DEFAULT_RESPONSE_CAPACITY};
24//!
25//! // Pre-allocated buffer
26//! let buf = ResponseBuffer::new();
27//! buf.write(b"Hello, World!");
28//!
29//! // With custom capacity
30//! let buf = ResponseBuffer::with_capacity(4096);
31//! ```
32
33use bytes::{BufMut, Bytes, BytesMut};
34use std::ops::Deref;
35
36/// Default pre-allocated response buffer size (512 bytes).
37///
38/// This covers most small JSON API responses like:
39/// - `{"status": "ok"}`
40/// - `{"id": 123, "name": "user"}`
41/// - Simple error responses
42pub const DEFAULT_RESPONSE_CAPACITY: usize = 512;
43
44/// Medium response buffer size (4KB).
45///
46/// Good for typical API responses with moderate data.
47pub const MEDIUM_RESPONSE_CAPACITY: usize = 4096;
48
49/// Large response buffer size (64KB).
50///
51/// For large responses like paginated lists or bulk data.
52pub const LARGE_RESPONSE_CAPACITY: usize = 65536;
53
54/// A pre-allocated response buffer using `BytesMut`.
55///
56/// This provides a growable buffer that's pre-allocated to avoid
57/// reallocations for typical response sizes.
58#[derive(Debug)]
59pub struct ResponseBuffer {
60    inner: BytesMut,
61}
62
63impl ResponseBuffer {
64    /// Create a new buffer with default capacity (512 bytes).
65    #[inline]
66    pub fn new() -> Self {
67        Self {
68            inner: BytesMut::with_capacity(DEFAULT_RESPONSE_CAPACITY),
69        }
70    }
71
72    /// Create a buffer with specified capacity.
73    #[inline]
74    pub fn with_capacity(capacity: usize) -> Self {
75        Self {
76            inner: BytesMut::with_capacity(capacity),
77        }
78    }
79
80    /// Create a buffer for medium-sized responses (4KB).
81    #[inline]
82    pub fn medium() -> Self {
83        Self::with_capacity(MEDIUM_RESPONSE_CAPACITY)
84    }
85
86    /// Create a buffer for large responses (64KB).
87    #[inline]
88    pub fn large() -> Self {
89        Self::with_capacity(LARGE_RESPONSE_CAPACITY)
90    }
91
92    /// Get current length.
93    #[inline]
94    pub fn len(&self) -> usize {
95        self.inner.len()
96    }
97
98    /// Check if buffer is empty.
99    #[inline]
100    pub fn is_empty(&self) -> bool {
101        self.inner.is_empty()
102    }
103
104    /// Get remaining capacity before reallocation.
105    #[inline]
106    pub fn remaining_capacity(&self) -> usize {
107        self.inner.capacity() - self.inner.len()
108    }
109
110    /// Get total capacity.
111    #[inline]
112    pub fn capacity(&self) -> usize {
113        self.inner.capacity()
114    }
115
116    /// Write bytes to buffer.
117    #[inline]
118    pub fn write(&mut self, data: &[u8]) {
119        self.inner.extend_from_slice(data);
120    }
121
122    /// Write a string to buffer.
123    #[inline]
124    pub fn write_str(&mut self, s: &str) {
125        self.inner.extend_from_slice(s.as_bytes());
126    }
127
128    /// Write using BufMut interface.
129    #[inline]
130    pub fn put(&mut self, data: &[u8]) {
131        self.inner.put_slice(data);
132    }
133
134    /// Reserve additional capacity.
135    #[inline]
136    pub fn reserve(&mut self, additional: usize) {
137        self.inner.reserve(additional);
138    }
139
140    /// Clear the buffer (keeps capacity).
141    #[inline]
142    pub fn clear(&mut self) {
143        self.inner.clear();
144    }
145
146    /// Convert to `Bytes` (zero-copy).
147    #[inline]
148    pub fn freeze(self) -> Bytes {
149        self.inner.freeze()
150    }
151
152    /// Get as byte slice.
153    #[inline]
154    pub fn as_slice(&self) -> &[u8] {
155        &self.inner
156    }
157
158    /// Get mutable reference to inner BytesMut.
159    #[inline]
160    pub fn inner_mut(&mut self) -> &mut BytesMut {
161        &mut self.inner
162    }
163
164    /// Consume and return inner BytesMut.
165    #[inline]
166    pub fn into_inner(self) -> BytesMut {
167        self.inner
168    }
169
170    /// Split off the filled portion as Bytes.
171    #[inline]
172    pub fn split(&mut self) -> Bytes {
173        self.inner.split().freeze()
174    }
175}
176
177impl Default for ResponseBuffer {
178    #[inline]
179    fn default() -> Self {
180        Self::new()
181    }
182}
183
184impl Deref for ResponseBuffer {
185    type Target = [u8];
186
187    #[inline]
188    fn deref(&self) -> &Self::Target {
189        &self.inner
190    }
191}
192
193impl AsRef<[u8]> for ResponseBuffer {
194    #[inline]
195    fn as_ref(&self) -> &[u8] {
196        &self.inner
197    }
198}
199
200impl AsMut<BytesMut> for ResponseBuffer {
201    #[inline]
202    fn as_mut(&mut self) -> &mut BytesMut {
203        &mut self.inner
204    }
205}
206
207impl From<ResponseBuffer> for Bytes {
208    #[inline]
209    fn from(buf: ResponseBuffer) -> Self {
210        buf.freeze()
211    }
212}
213
214impl From<ResponseBuffer> for BytesMut {
215    #[inline]
216    fn from(buf: ResponseBuffer) -> Self {
217        buf.inner
218    }
219}
220
221impl std::io::Write for ResponseBuffer {
222    #[inline]
223    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
224        self.inner.extend_from_slice(buf);
225        Ok(buf.len())
226    }
227
228    #[inline]
229    fn flush(&mut self) -> std::io::Result<()> {
230        Ok(())
231    }
232}
233
234// ============================================================================
235// Response Builder with Pre-allocated Buffer
236// ============================================================================
237
238/// A response builder with pre-allocated buffer for efficient response creation.
239///
240/// This builder allocates a buffer upfront and allows building the response
241/// body incrementally without reallocations.
242///
243/// # Example
244///
245/// ```rust,ignore
246/// use armature_core::response_buffer::ResponseBuilder;
247///
248/// let response = ResponseBuilder::new()
249///     .status(200)
250///     .header("Content-Type", "application/json")
251///     .body_json(&data)?
252///     .build();
253/// ```
254#[derive(Debug)]
255pub struct ResponseBuilder {
256    status: u16,
257    headers: Vec<(String, String)>,
258    body: ResponseBuffer,
259}
260
261impl ResponseBuilder {
262    /// Create a new builder with default buffer capacity.
263    #[inline]
264    pub fn new() -> Self {
265        Self {
266            status: 200,
267            headers: Vec::with_capacity(8), // Most responses have <8 headers
268            body: ResponseBuffer::new(),
269        }
270    }
271
272    /// Create with custom buffer capacity.
273    #[inline]
274    pub fn with_capacity(capacity: usize) -> Self {
275        Self {
276            status: 200,
277            headers: Vec::with_capacity(8),
278            body: ResponseBuffer::with_capacity(capacity),
279        }
280    }
281
282    /// Set status code.
283    #[inline]
284    pub fn status(mut self, status: u16) -> Self {
285        self.status = status;
286        self
287    }
288
289    /// Add a header.
290    #[inline]
291    pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
292        self.headers.push((name.into(), value.into()));
293        self
294    }
295
296    /// Set Content-Type header.
297    #[inline]
298    pub fn content_type(self, value: impl Into<String>) -> Self {
299        self.header("Content-Type", value)
300    }
301
302    /// Set body from bytes.
303    #[inline]
304    pub fn body(mut self, data: &[u8]) -> Self {
305        self.body.write(data);
306        self
307    }
308
309    /// Set body from string.
310    #[inline]
311    pub fn body_str(mut self, s: &str) -> Self {
312        self.body.write_str(s);
313        self
314    }
315
316    /// Set body from JSON.
317    #[inline]
318    pub fn body_json<T: serde::Serialize>(mut self, value: &T) -> Result<Self, crate::Error> {
319        let json =
320            crate::json::to_vec(value).map_err(|e| crate::Error::Serialization(e.to_string()))?;
321        self.body.write(&json);
322        self.headers
323            .push(("Content-Type".to_string(), "application/json".to_string()));
324        Ok(self)
325    }
326
327    /// Get mutable reference to body buffer for direct writes.
328    #[inline]
329    pub fn body_mut(&mut self) -> &mut ResponseBuffer {
330        &mut self.body
331    }
332
333    /// Build the final HttpResponse.
334    #[inline]
335    pub fn build(self) -> crate::HttpResponse {
336        let mut response = crate::HttpResponse::new(self.status);
337        for (name, value) in self.headers {
338            response.headers.insert(name, value);
339        }
340        if !self.body.is_empty() {
341            response = response.with_bytes_body(self.body.freeze());
342        }
343        response
344    }
345}
346
347impl Default for ResponseBuilder {
348    #[inline]
349    fn default() -> Self {
350        Self::new()
351    }
352}
353
354// ============================================================================
355// Extension to HttpResponse
356// ============================================================================
357
358// Extension methods for HttpResponse are defined in http.rs
359// to access private fields. Use the builder pattern here.
360
361/// Create a response builder for more control over response creation.
362#[inline]
363pub fn response_builder() -> ResponseBuilder {
364    ResponseBuilder::new()
365}
366
367/// Create a response builder with custom buffer capacity.
368#[inline]
369pub fn response_builder_with_capacity(capacity: usize) -> ResponseBuilder {
370    ResponseBuilder::with_capacity(capacity)
371}
372
373// ============================================================================
374// Tests
375// ============================================================================
376
377#[cfg(test)]
378mod tests {
379    use super::*;
380
381    #[test]
382    fn test_response_buffer_new() {
383        let buf = ResponseBuffer::new();
384        assert_eq!(buf.capacity(), DEFAULT_RESPONSE_CAPACITY);
385        assert!(buf.is_empty());
386    }
387
388    #[test]
389    fn test_response_buffer_write() {
390        let mut buf = ResponseBuffer::new();
391        buf.write(b"Hello, ");
392        buf.write(b"World!");
393        assert_eq!(buf.as_slice(), b"Hello, World!");
394    }
395
396    #[test]
397    fn test_response_buffer_no_realloc_small() {
398        let mut buf = ResponseBuffer::new();
399        let initial_cap = buf.capacity();
400
401        // Write less than default capacity
402        buf.write(b"Small response");
403
404        // Capacity should not have grown
405        assert_eq!(buf.capacity(), initial_cap);
406    }
407
408    #[test]
409    fn test_response_buffer_freeze() {
410        let mut buf = ResponseBuffer::new();
411        buf.write(b"test data");
412        let bytes = buf.freeze();
413        assert_eq!(&*bytes, b"test data");
414    }
415
416    #[test]
417    fn test_response_buffer_medium() {
418        let buf = ResponseBuffer::medium();
419        assert_eq!(buf.capacity(), MEDIUM_RESPONSE_CAPACITY);
420    }
421
422    #[test]
423    fn test_response_buffer_large() {
424        let buf = ResponseBuffer::large();
425        assert_eq!(buf.capacity(), LARGE_RESPONSE_CAPACITY);
426    }
427
428    #[test]
429    fn test_response_builder() {
430        let response = ResponseBuilder::new()
431            .status(201)
432            .header("X-Custom", "value")
433            .body_str("Created")
434            .build();
435
436        assert_eq!(response.status, 201);
437        assert_eq!(response.headers.get("X-Custom"), Some(&"value".to_string()));
438    }
439
440    #[test]
441    fn test_response_builder_json() {
442        #[derive(serde::Serialize)]
443        struct Data {
444            message: &'static str,
445        }
446
447        let response = ResponseBuilder::new()
448            .status(200)
449            .body_json(&Data { message: "ok" })
450            .unwrap()
451            .build();
452
453        assert_eq!(response.status, 200);
454        assert!(
455            response
456                .headers
457                .get("Content-Type")
458                .unwrap()
459                .contains("json")
460        );
461    }
462
463    #[test]
464    fn test_http_response_with_capacity() {
465        let response = crate::HttpResponse::with_capacity(200, 1024);
466        assert_eq!(response.status, 200);
467        // body.capacity() is at least 1024 (may be more due to allocator)
468        assert!(response.body.capacity() >= 1024);
469    }
470
471    #[test]
472    fn test_http_response_preallocated() {
473        let response = crate::HttpResponse::ok_preallocated();
474        assert_eq!(response.status, 200);
475        assert!(response.body.capacity() >= 512);
476    }
477
478    #[test]
479    fn test_response_builder_standalone() {
480        let response = response_builder()
481            .status(404)
482            .content_type("text/plain")
483            .body_str("Not Found")
484            .build();
485
486        assert_eq!(response.status, 404);
487    }
488
489    #[test]
490    fn test_response_buffer_io_write() {
491        use std::io::Write;
492
493        let mut buf = ResponseBuffer::new();
494        write!(buf, "Hello, World!").unwrap();
495        assert_eq!(buf.as_slice(), b"Hello, World!");
496    }
497}