1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//! Thread-safe memory pool for Snappy FrameEncoder/FrameDecoder scratch buffers.
//!
//! Amortises the repeated allocation cost of per-encode/decode chunk buffers
//! across many streaming calls.
//!
//! # Design
//!
//! There are two distinct buffer types:
//!
//! - **Encoder scratch** (`Vec<u8>`, `ENCODER_SCRATCH_CAP` bytes): the input staging
//! buffer inside [`FrameEncoder`](crate::frame::FrameEncoder) that accumulates up to
//! 64 KiB before being flushed as a chunk. Sized at `65536 + 256` to leave room
//! for small overruns during extend-from-slice before flush.
//!
//! - **Decoder scratch** (`Vec<u8>`, `MAX_UNCOMPRESSED_CHUNK_SIZE` bytes): the
//! temporary read buffer inside [`FrameDecoder`](crate::frame::FrameDecoder) used to
//! hold raw chunk data read from the stream before decoding.
//!
//! Each bucket is a `Mutex`-guarded `Vec<Vec<u8>>`. Acquiring a buffer either pops a
//! cached one (a "hit") or allocates fresh memory (a "miss"). On drop, the buffer is
//! returned to the correct bucket, subject to the per-bucket capacity cap. Excess
//! buffers (over cap) are silently dropped (freed).
//!
//! The pool is `Clone` because it wraps an `Arc<PoolInner>`; all clones share the
//! same underlying buckets.
//!
//! # Example
//!
//! ```rust
//! use oxiarc_snappy::{SnappyPool, FrameEncoder, FrameDecoder};
//! use std::io::{Write, Read};
//!
//! let pool = SnappyPool::new();
//!
//! // First call allocates fresh buffers.
//! let mut compressed = Vec::new();
//! {
//! let mut enc = FrameEncoder::with_pool(&mut compressed, &pool);
//! enc.write_all(b"hello world").unwrap();
//! enc.finish().unwrap();
//! }
//!
//! // Second call reuses the buffers returned from the first.
//! let mut compressed2 = Vec::new();
//! {
//! let mut enc = FrameEncoder::with_pool(&mut compressed2, &pool);
//! enc.write_all(b"hello world").unwrap();
//! enc.finish().unwrap();
//! }
//! assert!(pool.stats().encoder_scratch_hits >= 1);
//! ```
use ;
// ─────────────────────────────────────────────────────────────────────────────
// Capacity constants
// ─────────────────────────────────────────────────────────────────────────────
/// Maximum uncompressed chunk size matches the framing spec (64 KiB).
pub const MAX_UNCOMPRESSED_CHUNK_SIZE: usize = 65536;
/// Pre-allocated capacity for encoder scratch buffers: 64 KiB + overhead.
pub const ENCODER_SCRATCH_CAP: usize = MAX_UNCOMPRESSED_CHUNK_SIZE + 256;
// ─────────────────────────────────────────────────────────────────────────────
// Internal state
// ─────────────────────────────────────────────────────────────────────────────
pub
// ─────────────────────────────────────────────────────────────────────────────
// Public API: SnappyPool
// ─────────────────────────────────────────────────────────────────────────────
/// Thread-safe pool of reusable buffers for Snappy frame encode/decode.
///
/// Amortises the cost of allocating chunk staging buffers each time a
/// [`FrameEncoder`](crate::frame::FrameEncoder) or
/// [`FrameDecoder`](crate::frame::FrameDecoder) processes a 64 KiB chunk.
///
/// The pool is `Clone`: all clones share the same internal buckets.
///
/// See [module-level documentation](self) for a worked example.
// SAFETY: All mutable state is guarded by `Mutex`.
unsafe
unsafe
// ─────────────────────────────────────────────────────────────────────────────
// Public API: PoolStats
// ─────────────────────────────────────────────────────────────────────────────
/// Statistics returned by [`SnappyPool::stats`].