1use core::fmt;
4
5use super::QpackHeaderFieldRef;
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub struct QpackDynamicTableEntry {
13 offset: usize,
14 name_len: usize,
15 value_len: usize,
16}
17
18impl QpackDynamicTableEntry {
19 pub const EMPTY: Self = Self {
21 offset: 0,
22 name_len: 0,
23 value_len: 0,
24 };
25}
26
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub enum QpackDynamicTableError {
30 CapacityExceedsStorage {
32 requested: usize,
34 capacity: usize,
36 },
37 EntrySizeOverflow,
39 EntryLargerThanCapacity {
41 entry_size: usize,
43 capacity: usize,
45 },
46 InsertCountOverflow,
48 EvictionBlocked {
50 absolute_index: u64,
52 },
53 EncoderRelativeIndexUnderflow {
55 insert_count: u64,
57 index: u64,
59 },
60 FieldRelativeIndexUnderflow {
62 base: u64,
64 index: u64,
66 },
67 PostBaseIndexOverflow {
69 base: u64,
71 index: u64,
73 },
74 FutureAbsoluteIndex {
76 requested: u64,
78 insert_count: u64,
80 },
81 EvictedAbsoluteIndex {
83 requested: u64,
85 oldest_available: u64,
87 },
88 ReferenceAtOrAfterRequiredInsertCount {
90 requested: u64,
92 required_insert_count: u64,
94 },
95}
96
97impl fmt::Display for QpackDynamicTableError {
98 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
99 match self {
100 Self::CapacityExceedsStorage {
101 requested,
102 capacity,
103 } => {
104 write!(
105 formatter,
106 "requested capacity {requested} exceeds storage capacity {capacity}"
107 )
108 }
109 Self::EntrySizeOverflow => formatter.write_str("QPACK entry size overflowed usize"),
110 Self::EntryLargerThanCapacity {
111 entry_size,
112 capacity,
113 } => write!(
114 formatter,
115 "entry size {entry_size} exceeds capacity {capacity}"
116 ),
117 Self::InsertCountOverflow => formatter.write_str("QPACK insert count overflowed u64"),
118 Self::EvictionBlocked { absolute_index } => {
119 write!(
120 formatter,
121 "eviction of absolute index {absolute_index} was blocked"
122 )
123 }
124 Self::EncoderRelativeIndexUnderflow {
125 insert_count,
126 index,
127 } => write!(
128 formatter,
129 "encoder-relative index {index} exceeds insert count {insert_count}"
130 ),
131 Self::FieldRelativeIndexUnderflow { base, index } => {
132 write!(
133 formatter,
134 "field-relative index {index} exceeds base {base}"
135 )
136 }
137 Self::PostBaseIndexOverflow { base, index } => {
138 write!(formatter, "post-base index {index} overflows base {base}")
139 }
140 Self::FutureAbsoluteIndex {
141 requested,
142 insert_count,
143 } => write!(
144 formatter,
145 "absolute index {requested} is at or after insert count {insert_count}"
146 ),
147 Self::EvictedAbsoluteIndex {
148 requested,
149 oldest_available,
150 } => write!(
151 formatter,
152 "absolute index {requested} was evicted; oldest available is {oldest_available}"
153 ),
154 Self::ReferenceAtOrAfterRequiredInsertCount {
155 requested,
156 required_insert_count,
157 } => write!(
158 formatter,
159 "absolute index {requested} is at or after required insert count {required_insert_count}"
160 ),
161 }
162 }
163}
164
165impl core::error::Error for QpackDynamicTableError {}
166
167pub struct QpackDynamicTable<'storage, 'entries> {
172 storage: &'storage mut [u8],
173 entries: &'entries mut [QpackDynamicTableEntry],
174 len: usize,
175 size: usize,
176 capacity: usize,
177 storage_capacity: usize,
178 insert_count: u64,
179}
180
181impl<'storage, 'entries> QpackDynamicTable<'storage, 'entries> {
182 pub fn new(
184 storage: &'storage mut [u8],
185 entries: &'entries mut [QpackDynamicTableEntry],
186 ) -> Self {
187 let storage_capacity = Self::calculate_storage_capacity(storage.len(), entries.len());
188 Self {
189 storage,
190 entries,
191 len: 0,
192 size: 0,
193 capacity: 0,
194 storage_capacity,
195 insert_count: 0,
196 }
197 }
198
199 pub const fn storage_capacity(&self) -> usize {
201 self.storage_capacity
202 }
203
204 pub const fn capacity(&self) -> usize {
206 self.capacity
207 }
208
209 pub const fn size(&self) -> usize {
211 self.size
212 }
213
214 pub const fn len(&self) -> usize {
216 self.len
217 }
218
219 pub const fn is_empty(&self) -> bool {
221 self.len == 0
222 }
223
224 pub const fn insert_count(&self) -> u64 {
226 self.insert_count
227 }
228
229 pub fn oldest_active_absolute(&self) -> Option<u64> {
231 if self.len == 0 {
232 return None;
233 }
234 u64::try_from(self.len)
235 .ok()
236 .and_then(|len| self.insert_count.checked_sub(len))
237 }
238
239 pub fn entry_size(name: &[u8], value: &[u8]) -> Result<usize, QpackDynamicTableError> {
241 name.len()
242 .checked_add(value.len())
243 .and_then(|length| length.checked_add(32))
244 .ok_or(QpackDynamicTableError::EntrySizeOverflow)
245 }
246
247 pub fn set_capacity<F>(
249 &mut self,
250 capacity: usize,
251 mut evictable: F,
252 ) -> Result<(), QpackDynamicTableError>
253 where
254 F: FnMut(u64) -> bool,
255 {
256 if capacity > self.storage_capacity {
257 return Err(QpackDynamicTableError::CapacityExceedsStorage {
258 requested: capacity,
259 capacity: self.storage_capacity,
260 });
261 }
262
263 let (retained_len, retained_size) = self.retained_for_capacity(capacity);
264 self.confirm_evictions(retained_len, &mut evictable)?;
265 self.len = retained_len;
266 self.size = retained_size;
267 self.capacity = capacity;
268 Ok(())
269 }
270
271 pub fn insert<F>(
273 &mut self,
274 name: &[u8],
275 value: &[u8],
276 mut evictable: F,
277 ) -> Result<u64, QpackDynamicTableError>
278 where
279 F: FnMut(u64) -> bool,
280 {
281 let entry_size = Self::entry_size(name, value)?;
282 if entry_size > self.capacity {
283 return Err(QpackDynamicTableError::EntryLargerThanCapacity {
284 entry_size,
285 capacity: self.capacity,
286 });
287 }
288 let target_size = self.capacity - entry_size;
289 let (retained_len, retained_size) = self.retained_for_capacity(target_size);
290 self.confirm_evictions(retained_len, &mut evictable)?;
291 let next_insert_count = self
292 .insert_count
293 .checked_add(1)
294 .ok_or(QpackDynamicTableError::InsertCountOverflow)?;
295
296 let byte_len = name.len() + value.len();
297 let retained_bytes = self.packed_bytes(retained_len);
298 self.storage.copy_within(0..retained_bytes, byte_len);
299 if retained_len > 0 {
300 self.entries.copy_within(0..retained_len, 1);
301 for entry in &mut self.entries[1..=retained_len] {
302 entry.offset += byte_len;
303 }
304 }
305 self.storage[..name.len()].copy_from_slice(name);
306 self.storage[name.len()..byte_len].copy_from_slice(value);
307 self.entries[0] = QpackDynamicTableEntry {
308 offset: 0,
309 name_len: name.len(),
310 value_len: value.len(),
311 };
312 self.len = retained_len + 1;
313 self.size = retained_size + entry_size;
314 let absolute = self.insert_count;
315 self.insert_count = next_insert_count;
316 Ok(absolute)
317 }
318
319 pub fn get_absolute(
321 &self,
322 absolute: u64,
323 ) -> Result<QpackHeaderFieldRef<'_>, QpackDynamicTableError> {
324 if absolute >= self.insert_count {
325 return Err(QpackDynamicTableError::FutureAbsoluteIndex {
326 requested: absolute,
327 insert_count: self.insert_count,
328 });
329 }
330 let oldest_available = match self.oldest_active_absolute() {
331 Some(oldest) => oldest,
332 None => self.insert_count,
333 };
334 if absolute < oldest_available {
335 return Err(QpackDynamicTableError::EvictedAbsoluteIndex {
336 requested: absolute,
337 oldest_available,
338 });
339 }
340 let slot = (self.insert_count - absolute - 1) as usize;
341 let entry = self.entries[slot];
342 let name_end = entry.offset + entry.name_len;
343 let value_end = name_end + entry.value_len;
344 Ok(QpackHeaderFieldRef::new(
345 &self.storage[entry.offset..name_end],
346 &self.storage[name_end..value_end],
347 ))
348 }
349
350 pub fn get_encoder_relative(
352 &self,
353 relative: u64,
354 ) -> Result<QpackHeaderFieldRef<'_>, QpackDynamicTableError> {
355 let absolute = self
356 .insert_count
357 .checked_sub(relative)
358 .and_then(|count| count.checked_sub(1))
359 .ok_or(QpackDynamicTableError::EncoderRelativeIndexUnderflow {
360 insert_count: self.insert_count,
361 index: relative,
362 })?;
363 self.get_absolute(absolute)
364 }
365
366 pub fn get_field_relative(
368 &self,
369 required_insert_count: u64,
370 base: u64,
371 relative: u64,
372 ) -> Result<QpackHeaderFieldRef<'_>, QpackDynamicTableError> {
373 let absolute = base
374 .checked_sub(relative)
375 .and_then(|base| base.checked_sub(1))
376 .ok_or(QpackDynamicTableError::FieldRelativeIndexUnderflow {
377 base,
378 index: relative,
379 })?;
380 self.get_before_required_insert_count(absolute, required_insert_count)
381 }
382
383 pub fn get_post_base(
385 &self,
386 required_insert_count: u64,
387 base: u64,
388 post_base: u64,
389 ) -> Result<QpackHeaderFieldRef<'_>, QpackDynamicTableError> {
390 let absolute =
391 base.checked_add(post_base)
392 .ok_or(QpackDynamicTableError::PostBaseIndexOverflow {
393 base,
394 index: post_base,
395 })?;
396 self.get_before_required_insert_count(absolute, required_insert_count)
397 }
398
399 fn get_before_required_insert_count(
400 &self,
401 absolute: u64,
402 required_insert_count: u64,
403 ) -> Result<QpackHeaderFieldRef<'_>, QpackDynamicTableError> {
404 if absolute >= required_insert_count {
405 return Err(
406 QpackDynamicTableError::ReferenceAtOrAfterRequiredInsertCount {
407 requested: absolute,
408 required_insert_count,
409 },
410 );
411 }
412 self.get_absolute(absolute)
413 }
414
415 fn retained_for_capacity(&self, capacity: usize) -> (usize, usize) {
416 let mut len = self.len;
417 let mut size = self.size;
418 while len > 0 && size > capacity {
419 len -= 1;
420 size -= Self::entry_size_from_slot(self.entries[len]);
421 }
422 (len, size)
423 }
424
425 fn confirm_evictions<F>(
426 &self,
427 retained_len: usize,
428 evictable: &mut F,
429 ) -> Result<(), QpackDynamicTableError>
430 where
431 F: FnMut(u64) -> bool,
432 {
433 for slot in (retained_len..self.len).rev() {
434 let absolute_index = self.insert_count - slot as u64 - 1;
435 if !evictable(absolute_index) {
436 return Err(QpackDynamicTableError::EvictionBlocked { absolute_index });
437 }
438 }
439 Ok(())
440 }
441
442 fn packed_bytes(&self, len: usize) -> usize {
443 if len == 0 {
444 0
445 } else {
446 let entry = self.entries[len - 1];
447 entry.offset + entry.name_len + entry.value_len
448 }
449 }
450
451 const fn calculate_storage_capacity(storage_len: usize, entry_capacity: usize) -> usize {
452 let bytes = storage_len.saturating_add(32);
453 let entries = entry_capacity.saturating_mul(32).saturating_add(31);
454 if bytes < entries { bytes } else { entries }
455 }
456
457 const fn entry_size_from_slot(entry: QpackDynamicTableEntry) -> usize {
458 entry.name_len + entry.value_len + 32
459 }
460}