Skip to main content

cadmpeg_core/decode/
collect.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Charged growth of collections owned by a decode session.
3
4use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
5use std::fmt::{self, Write};
6use std::hash::Hash;
7
8use crate::CodecError;
9
10use super::{
11    u64_from_index, BoundedCount, DecodeContext, ResourceDimension, ResourceLimit,
12    ScopedReservation,
13};
14
15/// A vector that must contain exactly a count proven against input.
16#[derive(Debug)]
17pub struct ExactVec<T> {
18    values: Vec<T>,
19    capacity: usize,
20}
21
22impl<T> ExactVec<T> {
23    /// Charges and allocates storage for a count bounded by the input window.
24    pub fn new(
25        ctx: &DecodeContext<'_>,
26        count: BoundedCount,
27        operation: &'static str,
28    ) -> Result<Self, CodecError> {
29        let capacity = count.get();
30        let values = ctx.collection_vec(capacity, operation)?;
31        Ok(Self { values, capacity })
32    }
33
34    /// Appends one value without exceeding the bounded count.
35    pub fn push(&mut self, value: T) -> Result<(), CodecError> {
36        if self.values.len() == self.capacity {
37            return Err(CodecError::Malformed(
38                "fixed-capacity vector overflow".to_owned(),
39            ));
40        }
41        self.values.push(value);
42        Ok(())
43    }
44
45    /// Returns the values if the bounded count was filled exactly.
46    pub fn finish(self) -> Result<Vec<T>, CodecError> {
47        if self.values.len() == self.capacity {
48            Ok(self.values)
49        } else {
50            Err(CodecError::malformed(format_args!(
51                "fixed-capacity vector contains {} of {} values",
52                self.values.len(),
53                self.capacity
54            )))
55        }
56    }
57}
58
59#[derive(Clone, Copy)]
60enum VecGrowth {
61    Exact,
62    Amortized,
63}
64
65impl DecodeContext<'_> {
66    /// Reserve backing storage without admitting values not yet inserted.
67    pub fn reserve_capacity_limit<T>(
68        &self,
69        values: &mut Vec<T>,
70        count: usize,
71        operation: &'static str,
72    ) -> Result<(), ResourceLimit> {
73        self.reserve_retained_vec_storage(values, count, VecGrowth::Amortized, None, operation)
74    }
75
76    fn reserve_retained_vec_storage<T>(
77        &self,
78        values: &mut Vec<T>,
79        count: usize,
80        growth: VecGrowth,
81        items: Option<usize>,
82        operation: &'static str,
83    ) -> Result<(), ResourceLimit> {
84        let required = values
85            .len()
86            .checked_add(count)
87            .ok_or_else(|| self.retained_size_overflow_limit(operation))?;
88        let capacity = if std::mem::size_of::<T>() == 0 || required <= values.capacity() {
89            values.capacity()
90        } else if matches!(growth, VecGrowth::Exact) {
91            required
92        } else if values.capacity() == 0 {
93            let minimum = match std::mem::size_of::<T>() {
94                1 => 8,
95                2..=1024 => 4,
96                _ => 1,
97            };
98            required.max(minimum)
99        } else {
100            match values.capacity().checked_mul(2) {
101                Some(grown) if grown > required => grown,
102                _ => required,
103            }
104        };
105        let added = capacity - values.capacity();
106        let bytes = added
107            .checked_mul(std::mem::size_of::<T>())
108            .ok_or_else(|| self.retained_size_overflow_limit(operation))?;
109        let maximum = usize::try_from(isize::MAX)
110            .map_err(|_| self.retained_size_overflow_limit(operation))?;
111        if bytes > maximum {
112            return Err(self
113                .budget
114                .retained_allocation_failed_limit(u64_from_index(bytes), operation));
115        }
116        self.charge_retained_limit(u64_from_index(bytes), operation)?;
117        if let Some(items) = items {
118            self.charge_collection_items_limit(u64_from_index(items), operation)?;
119        }
120        if added != 0 {
121            values
122                .try_reserve_exact(capacity - values.len())
123                .map_err(|_| {
124                    self.budget
125                        .retained_allocation_failed_limit(u64_from_index(bytes), operation)
126                })?;
127        }
128        Ok(())
129    }
130
131    /// Retains vector storage whose slots were admitted in aggregate.
132    pub fn reserve_capacity<T>(
133        &self,
134        values: &mut Vec<T>,
135        count: usize,
136        operation: &'static str,
137    ) -> Result<(), CodecError> {
138        self.reserve_retained_vec_storage(values, count, VecGrowth::Amortized, None, operation)
139            .map_err(Into::into)
140    }
141
142    /// Creates retained storage whose collection slots are admitted separately.
143    pub fn vector_storage<T>(
144        &self,
145        count: usize,
146        operation: &'static str,
147    ) -> Result<Vec<T>, CodecError> {
148        let mut values = Vec::new();
149        self.reserve_retained_vec_storage(&mut values, count, VecGrowth::Exact, None, operation)?;
150        Ok(values)
151    }
152
153    /// Admits scoped collection slots whose storage is allocated by a later operation.
154    pub fn reserve_scoped_collection<T>(
155        &self,
156        count: usize,
157        operation: &'static str,
158    ) -> Result<ScopedReservation<'_>, CodecError> {
159        self.charge_collection_items(u64_from_index(count), operation)?;
160        let bytes = count
161            .checked_mul(std::mem::size_of::<T>())
162            .ok_or_else(|| self.budget.scoped_size_overflow_limit(operation))?;
163        self.reserve_scoped(u64_from_index(bytes), operation)
164    }
165
166    /// Creates scoped storage whose collection slots are admitted separately.
167    pub fn scoped_vector_storage<T>(
168        &self,
169        count: usize,
170        operation: &'static str,
171    ) -> Result<(Vec<T>, ScopedReservation<'_>), CodecError> {
172        let mut reservation = self.reserve_scoped(0, operation)?;
173        let values = reservation.with_storage(|| self.vector_storage(count, operation))?;
174        Ok((values, reservation))
175    }
176
177    /// Grows scoped storage and admits additional vector slots.
178    pub fn reserve_scoped_vec<T>(
179        &self,
180        reservation: &mut ScopedReservation<'_>,
181        values: &mut Vec<T>,
182        count: usize,
183        operation: &'static str,
184    ) -> Result<(), CodecError> {
185        self.reserve_scoped_vec_limit(reservation, values, count, operation)
186            .map_err(Into::into)
187    }
188
189    /// Reserves storage with a resource-only refusal channel.
190    pub fn reserve_scoped_vec_limit<T>(
191        &self,
192        reservation: &mut ScopedReservation<'_>,
193        values: &mut Vec<T>,
194        count: usize,
195        operation: &'static str,
196    ) -> Result<(), ResourceLimit> {
197        reservation.with_storage_limit(|| self.reserve_vec_limit(values, count, operation))
198    }
199
200    /// Appends one item with scoped storage and a charged collection slot.
201    pub fn push_scoped_vec<T>(
202        &self,
203        reservation: &mut ScopedReservation<'_>,
204        values: &mut Vec<T>,
205        value: T,
206        operation: &'static str,
207    ) -> Result<(), CodecError> {
208        self.reserve_scoped_vec(reservation, values, 1, operation)?;
209        values.push(value);
210        Ok(())
211    }
212
213    /// Reserves temporary vector storage and returns its live byte reservation.
214    pub fn reserve_temporary_vec<T>(
215        &self,
216        values: &mut Vec<T>,
217        count: usize,
218        operation: &'static str,
219    ) -> Result<ScopedReservation<'_>, ResourceLimit> {
220        let mut reservation = self.reserve_scoped_limit(0, operation)?;
221        reservation.with_storage_limit(|| {
222            self.reserve_retained_vec_storage(
223                values,
224                count,
225                VecGrowth::Exact,
226                Some(count),
227                operation,
228            )
229        })?;
230        Ok(reservation)
231    }
232
233    /// Copies a slice into scoped storage and returns its live byte reservation.
234    pub fn copy_temporary_slice<T: Copy>(
235        &self,
236        values: &[T],
237        operation: &'static str,
238    ) -> Result<(Vec<T>, ScopedReservation<'_>), ResourceLimit> {
239        let mut copy = Vec::new();
240        let reservation = self.reserve_temporary_vec(&mut copy, values.len(), operation)?;
241        self.charge_work_limit(u64_from_index(values.len()), operation)?;
242        copy.extend_from_slice(values);
243        Ok((copy, reservation))
244    }
245
246    /// Collects retained text and charges both vector storage and text bytes.
247    pub fn collect_retained_texts<'text>(
248        &self,
249        values: impl IntoIterator<Item = &'text str>,
250        operation: &'static str,
251    ) -> Result<Vec<String>, CodecError> {
252        self.try_collect_retained_with(values, operation, |value| {
253            self.copy_retained_text(value, operation)
254        })
255    }
256
257    /// Copies scoped text values and returns the reservation for their storage.
258    pub fn collect_scoped_texts<'text>(
259        &self,
260        values: impl IntoIterator<Item = &'text str>,
261        operation: &'static str,
262    ) -> Result<(Vec<String>, ScopedReservation<'_>), CodecError> {
263        let mut reservation = self.reserve_scoped(0, operation)?;
264        let copies = reservation.with_storage(|| self.collect_retained_texts(values, operation))?;
265        Ok((copies, reservation))
266    }
267
268    /// Formats text in an existing scope after charging its byte count as work.
269    pub fn format_scoped_text(
270        &self,
271        reservation: &mut ScopedReservation<'_>,
272        args: fmt::Arguments<'_>,
273        operation: &'static str,
274    ) -> Result<String, CodecError> {
275        reservation.with_storage(|| self.format_retained(args, operation))
276    }
277
278    /// Admits records, retains their storage and additional text, and reserves slots.
279    pub fn reserve_record_vec<T>(
280        &self,
281        records: &mut Vec<T>,
282        count: usize,
283        text_bytes: u64,
284        operation: &'static str,
285    ) -> Result<(), CodecError> {
286        self.charge_entities(u64_from_index(count), operation)?;
287        self.charge_retained(text_bytes, operation)?;
288        self.reserve_vec(records, count, operation)
289    }
290
291    /// Collects retained slots before adding each value.
292    pub fn collect_retained_vec<T>(
293        &self,
294        values: impl IntoIterator<Item = T>,
295        operation: &'static str,
296    ) -> Result<Vec<T>, CodecError> {
297        self.try_collect_retained_with(values, operation, Ok::<T, CodecError>)
298    }
299
300    /// Reserves text bytes charged by aggregate admission.
301    pub(crate) fn reserve_admitted_string(
302        text: &mut String,
303        additional: usize,
304        operation: &'static str,
305    ) -> Result<(), CodecError> {
306        text.try_reserve_exact(additional).map_err(|_| {
307            CodecError::ResourceLimit(ResourceLimit::allocation_failed(
308                ResourceDimension::RetainedBytes,
309                u64::MAX,
310                u64_from_index(additional),
311                operation,
312            ))
313        })
314    }
315
316    /// Admits the comparison bound for one standard-library B-tree lookup.
317    pub fn charge_btree_lookup(
318        &self,
319        len: usize,
320        operation: &'static str,
321    ) -> Result<(), CodecError> {
322        // Nodes have at most eleven keys. A height-h tree has at least
323        // 2 * 6^h - 1 keys: the root has two children and other internal
324        // nodes have at least six. A lookup compares at most eleven keys
325        // per level, and never more keys than the whole tree contains.
326        let mut levels = 1_u64;
327        let mut minimum_subtree = len.div_ceil(2);
328        while minimum_subtree >= 6 {
329            minimum_subtree /= 6;
330            levels += 1;
331        }
332        self.charge_work(u64_from_index(len).min(11 * levels), operation)
333    }
334
335    /// Inserts a new scoped tree key after charging lookup work and node storage.
336    pub fn insert_scoped_btree_set<T: Ord>(
337        &self,
338        reservation: &mut ScopedReservation<'_>,
339        values: &mut BTreeSet<T>,
340        value: T,
341        work_operation: &'static str,
342        operation: &'static str,
343    ) -> Result<bool, CodecError> {
344        self.charge_btree_lookup(values.len(), work_operation)?;
345        if values.contains(&value) {
346            return Ok(false);
347        }
348        // Insertion searches the tree again after storage admission.
349        self.charge_btree_lookup(values.len(), work_operation)?;
350        reservation.with_storage(|| {
351            self.charge_retained(
352                self.tree_growth_bytes::<T, ()>(values.len(), operation)?,
353                operation,
354            )?;
355            self.charge_collection_items(1, operation)?;
356            Ok(values.insert(value))
357        })
358    }
359
360    /// Inserts a vacant scoped tree entry after charging lookup work and node storage.
361    pub fn insert_scoped_btree_map_if_vacant<K: Ord, V>(
362        &self,
363        reservation: &mut ScopedReservation<'_>,
364        values: &mut BTreeMap<K, V>,
365        key: K,
366        value: V,
367        work_operation: &'static str,
368        operation: &'static str,
369    ) -> Result<bool, CodecError> {
370        self.charge_work(u64_from_index(values.len()), work_operation)?;
371        reservation.with_storage(|| {
372            if values.contains_key(&key) {
373                return Ok(false);
374            }
375            self.insert_btree_map(values, key, value, operation)?;
376            Ok(true)
377        })
378    }
379
380    /// Appends a lazily built value to a scoped group after aggregate admission.
381    pub fn push_scoped_btree_group<K: Ord, V>(
382        &self,
383        reservation: &mut ScopedReservation<'_>,
384        groups: &mut BTreeMap<K, Vec<V>>,
385        key: K,
386        value: impl FnOnce() -> V,
387        owned_bytes: usize,
388        operation: &'static str,
389    ) -> Result<(), CodecError> {
390        self.charge_work(u64_from_index(groups.len()) + 1, operation)?;
391        reservation.with_storage(|| {
392            if let Some(values) = groups.get_mut(&key) {
393                self.reserve_vec(values, 1, operation)?;
394                self.charge_retained(u64_from_index(owned_bytes), operation)?;
395                values.push(value());
396                return Ok(());
397            }
398            self.admit_btree_entry(groups, &key, operation)?;
399            let mut values = self.collection_vec(1, operation)?;
400            self.charge_retained(u64_from_index(owned_bytes), operation)?;
401            values.push(value());
402            groups.insert(key, values);
403            Ok(())
404        })
405    }
406
407    /// Admits tree-record storage, owned bytes, one slot and one work unit.
408    ///
409    /// Without the current tree length, each record admits one backing node.
410    pub fn admit_retained_btree_record<K, V>(
411        &self,
412        owned_bytes: usize,
413        operation: &'static str,
414    ) -> Result<(), CodecError> {
415        let bytes = self
416            .tree_growth_bytes::<K, V>(0, operation)?
417            .checked_add(u64_from_index(owned_bytes))
418            .ok_or_else(|| self.retained_size_overflow_limit(operation))?;
419        self.charge_collection_items(1, operation)?;
420        self.charge_retained(bytes, operation)?;
421        self.charge_work(1, operation)
422    }
423
424    /// Collects scoped groups in input order within each key.
425    pub fn collect_scoped_btree_groups<'ctx, K: Ord, V>(
426        &'ctx self,
427        values: impl IntoIterator<Item = (K, V)>,
428        operation: &'static str,
429    ) -> Result<(BTreeMap<K, Vec<V>>, ScopedReservation<'ctx>), CodecError> {
430        let mut groups = BTreeMap::new();
431        let mut reservation = self.reserve_scoped(0, operation)?;
432        for (key, value) in values {
433            self.push_scoped_btree_group(
434                &mut reservation,
435                &mut groups,
436                key,
437                || value,
438                0,
439                operation,
440            )?;
441        }
442        Ok((groups, reservation))
443    }
444
445    /// Collects scoped entries, keeping the last value for each key.
446    pub fn collect_scoped_btree_map<'ctx, K: Ord, V>(
447        &'ctx self,
448        values: impl IntoIterator<Item = (K, V)>,
449        operation: &'static str,
450    ) -> Result<(BTreeMap<K, V>, ScopedReservation<'ctx>), CodecError> {
451        let mut entries = BTreeMap::new();
452        let mut reservation = self.reserve_scoped(0, operation)?;
453        reservation.with_storage(|| {
454            for (key, value) in values {
455                self.charge_work(u64_from_index(entries.len()) + 1, operation)?;
456                self.insert_btree_map(&mut entries, key, value, operation)?;
457            }
458            Ok::<(), CodecError>(())
459        })?;
460        Ok((entries, reservation))
461    }
462
463    /// Grows a scoped string after admitting its additional byte storage.
464    pub fn reserve_scoped_string(
465        &self,
466        reservation: &mut ScopedReservation<'_>,
467        text: &mut String,
468        additional: usize,
469        operation: &'static str,
470    ) -> Result<(), CodecError> {
471        reservation.with_storage(|| self.try_reserve_retained_text(text, additional, operation))
472    }
473
474    /// Creates empty scoped text with fallible storage admission.
475    pub fn scoped_string(
476        &self,
477        length: usize,
478        operation: &'static str,
479    ) -> Result<(String, ScopedReservation<'_>), CodecError> {
480        let mut reservation = self.reserve_scoped(0, operation)?;
481        let mut text = String::new();
482        self.reserve_scoped_string(&mut reservation, &mut text, length, operation)?;
483        Ok((text, reservation))
484    }
485
486    fn allocation_failed(
487        &self,
488        dimension: ResourceDimension,
489        count: usize,
490        operation: &'static str,
491    ) -> CodecError {
492        match dimension {
493            ResourceDimension::CollectionItems => {
494                self.collection_allocation_failed(count, operation)
495            }
496            ResourceDimension::MaterializedBytes => self
497                .budget
498                .scoped_allocation_failed(u64_from_index(count), operation),
499            _ => self
500                .budget
501                .retained_allocation_failed(u64_from_index(count), operation),
502        }
503    }
504
505    /// Allocates a vector for `count` admitted items.
506    pub fn collection_vec<T>(
507        &self,
508        count: usize,
509        operation: &'static str,
510    ) -> Result<Vec<T>, CodecError> {
511        let mut values = Vec::new();
512        self.reserve_retained_vec_storage(
513            &mut values,
514            count,
515            VecGrowth::Exact,
516            Some(count),
517            operation,
518        )?;
519        Ok(values)
520    }
521
522    /// Reserves additional vector items and charges their admission.
523    pub fn reserve_vec<T>(
524        &self,
525        values: &mut Vec<T>,
526        additional: usize,
527        operation: &'static str,
528    ) -> Result<(), CodecError> {
529        self.reserve_vec_limit(values, additional, operation)
530            .map_err(Into::into)
531    }
532
533    /// Reserves storage with a resource-only refusal channel.
534    pub fn reserve_vec_limit<T>(
535        &self,
536        values: &mut Vec<T>,
537        additional: usize,
538        operation: &'static str,
539    ) -> Result<(), ResourceLimit> {
540        self.reserve_retained_vec_storage(
541            values,
542            additional,
543            VecGrowth::Amortized,
544            Some(additional),
545            operation,
546        )
547    }
548
549    /// Builds a vector of indexed values after charging all slots.
550    pub fn collect_indexed_vec<T>(
551        &self,
552        count: usize,
553        operation: &'static str,
554        mut value_at: impl FnMut(usize) -> Result<T, CodecError>,
555    ) -> Result<Vec<T>, CodecError> {
556        let mut values = self.collection_vec(count, operation)?;
557        for index in 0..count {
558            values.push(value_at(index)?);
559        }
560        Ok(values)
561    }
562
563    /// Adds one vector item after charging its slot.
564    pub fn push_vec<T>(
565        &self,
566        values: &mut Vec<T>,
567        value: T,
568        operation: &'static str,
569    ) -> Result<(), CodecError> {
570        self.reserve_vec(values, 1, operation)?;
571        values.push(value);
572        Ok(())
573    }
574
575    /// Adds a formatted retained note after charging its collection slot.
576    pub fn push_formatted_retained(
577        &self,
578        values: &mut Vec<String>,
579        args: fmt::Arguments<'_>,
580        slots_operation: &'static str,
581        text_operation: &'static str,
582    ) -> Result<(), CodecError> {
583        self.reserve_vec(values, 1, slots_operation)?;
584        values.push(self.format_retained(args, text_operation)?);
585        Ok(())
586    }
587
588    /// Moves all source items into a vector after charging their slots.
589    pub fn append_vec<T>(
590        &self,
591        target: &mut Vec<T>,
592        source: &mut Vec<T>,
593        operation: &'static str,
594    ) -> Result<(), CodecError> {
595        self.reserve_vec(target, source.len(), operation)?;
596        target.append(source);
597        Ok(())
598    }
599
600    /// Moves owned items into a vector after charging their slots.
601    pub fn extend_vec<T>(
602        &self,
603        target: &mut Vec<T>,
604        mut source: Vec<T>,
605        operation: &'static str,
606    ) -> Result<(), CodecError> {
607        self.append_vec(target, &mut source, operation)
608    }
609
610    /// Collects iterator values with a charged slot for each value.
611    pub fn collect_vec<T>(
612        &self,
613        values: impl IntoIterator<Item = T>,
614        operation: &'static str,
615    ) -> Result<Vec<T>, CodecError> {
616        let mut out = Vec::new();
617        for value in values {
618            self.push_vec(&mut out, value, operation)?;
619        }
620        Ok(out)
621    }
622
623    /// Collects fallible iterator values with a charged slot for each success.
624    pub fn try_collect_vec<T, E: From<CodecError>>(
625        &self,
626        values: impl IntoIterator<Item = Result<T, E>>,
627        operation: &'static str,
628    ) -> Result<Vec<T>, E> {
629        let mut out = Vec::new();
630        for value in values {
631            self.push_vec(&mut out, value?, operation)
632                .map_err(E::from)?;
633        }
634        Ok(out)
635    }
636
637    /// Inserts a unique tree value after admitting its scoped value storage.
638    pub fn insert_scoped_btree_value<T: Ord>(
639        &self,
640        reservation: &mut ScopedReservation<'_>,
641        values: &mut BTreeSet<T>,
642        value: T,
643        operation: &'static str,
644    ) -> Result<bool, CodecError> {
645        reservation.with_storage(|| self.insert_btree_set(values, value, operation))
646    }
647
648    /// Inserts a new set item after charging its slot.
649    pub fn insert_hash_set<T: Eq + Hash>(
650        &self,
651        values: &mut HashSet<T>,
652        value: T,
653        operation: &'static str,
654    ) -> Result<bool, CodecError> {
655        if values.contains(&value) {
656            return Ok(false);
657        }
658        self.reserve_set(values, 1, operation)?;
659        Ok(values.insert(value))
660    }
661
662    /// Inserts a copied string into a set if the value is new.
663    pub fn insert_string_set(
664        &self,
665        values: &mut HashSet<String>,
666        value: &str,
667        operation: &'static str,
668    ) -> Result<bool, CodecError> {
669        if values.contains(value) {
670            return Ok(false);
671        }
672        self.reserve_set(values, 1, operation)?;
673        let owned = self.copy_retained_text(value, operation)?;
674        Ok(values.insert(owned))
675    }
676
677    /// Extends a set with one charged slot for each distinct new value.
678    pub fn extend_hash_set<T: Eq + Hash>(
679        &self,
680        values: &mut HashSet<T>,
681        additions: impl IntoIterator<Item = T>,
682        operation: &'static str,
683    ) -> Result<(), CodecError> {
684        for value in additions {
685            self.insert_hash_set(values, value, operation)?;
686        }
687        Ok(())
688    }
689
690    /// Collects distinct values into a charged hash set.
691    pub fn collect_hash_set<T: Eq + Hash>(
692        &self,
693        values: impl IntoIterator<Item = T>,
694        operation: &'static str,
695    ) -> Result<HashSet<T>, CodecError> {
696        let mut out = HashSet::new();
697        for value in values {
698            self.insert_hash_set(&mut out, value, operation)?;
699        }
700        Ok(out)
701    }
702
703    /// Reserves a map slot only when the key is new.
704    pub fn admit_hash_map_entry<K: Eq + Hash, V>(
705        &self,
706        values: &mut HashMap<K, V>,
707        key: &K,
708        operation: &'static str,
709    ) -> Result<(), CodecError> {
710        if !values.contains_key(key) {
711            self.reserve_map(values, 1, operation)?;
712        }
713        Ok(())
714    }
715
716    /// Appends a grouped value after admitting both new index and value slots.
717    pub fn push_hash_group<K: Eq + Hash, V>(
718        &self,
719        values: &mut HashMap<K, Vec<V>>,
720        key: K,
721        value: V,
722        index_operation: &'static str,
723        value_operation: &'static str,
724    ) -> Result<(), CodecError> {
725        if let Some(group) = values.get_mut(&key) {
726            return self.push_vec(group, value, value_operation);
727        }
728        self.charge_collection_items(1, index_operation)?;
729        self.charge_collection_items(1, value_operation)?;
730        let mut group = self.vector_storage(1, value_operation)?;
731        let bytes = self.charge_hash_growth::<(K, Vec<V>)>(
732            values.len(),
733            values.capacity(),
734            1,
735            index_operation,
736        )?;
737        values.try_reserve(1).map_err(|_| {
738            self.budget
739                .retained_allocation_failed(u64_from_index(bytes), index_operation)
740        })?;
741        group.push(value);
742        values.insert(key, group);
743        Ok(())
744    }
745
746    /// Inserts a map entry after admitting a new key, if needed.
747    pub fn insert_hash_map<K: Eq + Hash, V>(
748        &self,
749        values: &mut HashMap<K, V>,
750        key: K,
751        value: V,
752        operation: &'static str,
753    ) -> Result<Option<V>, CodecError> {
754        self.admit_hash_map_entry(values, &key, operation)?;
755        Ok(values.insert(key, value))
756    }
757
758    /// Collects entries into a charged hash map.
759    pub fn collect_hash_map<K: Eq + Hash, V>(
760        &self,
761        values: impl IntoIterator<Item = (K, V)>,
762        operation: &'static str,
763    ) -> Result<HashMap<K, V>, CodecError> {
764        let mut out = HashMap::new();
765        for (key, value) in values {
766            self.insert_hash_map(&mut out, key, value, operation)?;
767        }
768        Ok(out)
769    }
770
771    /// Copies temporary text while its scoped reservation remains live.
772    pub fn copy_scoped_text(
773        &self,
774        text: &str,
775        reservation: &mut ScopedReservation<'_>,
776        operation: &'static str,
777    ) -> Result<String, CodecError> {
778        reservation.with_storage(|| self.copy_retained_text(text, operation))
779    }
780
781    fn linear_growth<T>(
782        &self,
783        len: usize,
784        capacity: usize,
785        count: usize,
786        operation: &'static str,
787    ) -> Result<(usize, usize), CodecError> {
788        let required = len
789            .checked_add(count)
790            .ok_or_else(|| self.retained_size_overflow_limit(operation))?;
791        if std::mem::size_of::<T>() == 0 || required <= capacity {
792            return Ok((0, 0));
793        }
794        let minimum = match std::mem::size_of::<T>() {
795            1 => 8,
796            2..=1024 => 4,
797            _ => 1,
798        };
799        let target = capacity
800            .checked_mul(2)
801            .ok_or_else(|| self.retained_size_overflow_limit(operation))?
802            .max(required)
803            .max(minimum);
804        let bytes = (target - capacity)
805            .checked_mul(std::mem::size_of::<T>())
806            .ok_or_else(|| self.retained_size_overflow_limit(operation))?;
807        self.charge_retained(u64_from_index(bytes), operation)?;
808        Ok((target - len, bytes))
809    }
810
811    /// Appends a deque item after charging its slot.
812    pub fn push_back<T>(
813        &self,
814        values: &mut VecDeque<T>,
815        value: T,
816        operation: &'static str,
817    ) -> Result<(), CodecError> {
818        let (additional, bytes) =
819            self.linear_growth::<T>(values.len(), values.capacity(), 1, operation)?;
820        self.charge_collection_items(1, operation)?;
821        values.try_reserve_exact(additional).map_err(|_| {
822            self.budget
823                .retained_allocation_failed(u64_from_index(bytes), operation)
824        })?;
825        values.push_back(value);
826        Ok(())
827    }
828
829    /// Prepends a deque item after charging its slot.
830    pub fn push_front<T>(
831        &self,
832        values: &mut VecDeque<T>,
833        value: T,
834        operation: &'static str,
835    ) -> Result<(), CodecError> {
836        let (additional, bytes) =
837            self.linear_growth::<T>(values.len(), values.capacity(), 1, operation)?;
838        self.charge_collection_items(1, operation)?;
839        values.try_reserve_exact(additional).map_err(|_| {
840            self.budget
841                .retained_allocation_failed(u64_from_index(bytes), operation)
842        })?;
843        values.push_front(value);
844        Ok(())
845    }
846
847    /// Reserves slots in a binary heap after charging them.
848    pub fn reserve_heap<T: Ord>(
849        &self,
850        values: &mut BinaryHeap<T>,
851        count: usize,
852        operation: &'static str,
853    ) -> Result<(), CodecError> {
854        let (additional, bytes) =
855            self.linear_growth::<T>(values.len(), values.capacity(), count, operation)?;
856        self.charge_collection_items(u64_from_index(count), operation)?;
857        values.try_reserve_exact(additional).map_err(|_| {
858            self.budget
859                .retained_allocation_failed(u64_from_index(bytes), operation)
860        })
861    }
862
863    /// Creates a charged vector only when the source is present.
864    pub fn optional_collection_vec<T>(
865        &self,
866        present: bool,
867        count: usize,
868        operation: &'static str,
869    ) -> Result<Option<Vec<T>>, CodecError> {
870        if present {
871            Ok(Some(self.collection_vec(count, operation)?))
872        } else {
873            Ok(None)
874        }
875    }
876
877    /// Copies a flat lane into rows after admitting every row and element slot.
878    pub fn copy_rows<T: Copy>(
879        &self,
880        values: &[T],
881        row_len: usize,
882        row_operation: &'static str,
883        item_operation: &'static str,
884    ) -> Result<Vec<Vec<T>>, CodecError> {
885        if row_len == 0 {
886            return Err(CodecError::malformed("row width must be nonzero"));
887        }
888        self.try_collect_retained_with(values.chunks(row_len), row_operation, |row| {
889            self.copy_slice(row, item_operation)
890        })
891    }
892
893    /// Collects fallible values and admits the storage of each output slot.
894    pub fn try_collect_retained_with<I, T, E: From<CodecError>>(
895        &self,
896        values: impl IntoIterator<Item = I>,
897        operation: &'static str,
898        mut map: impl FnMut(I) -> Result<T, E>,
899    ) -> Result<Vec<T>, E> {
900        let values = values.into_iter();
901        let (minimum, maximum) = values.size_hint();
902        let mut collected = Vec::new();
903        for (index, value) in values.enumerate() {
904            self.charge_work(1, operation)?;
905            if index == 0 && maximum == Some(minimum) {
906                self.reserve_retained_vec_storage(
907                    &mut collected,
908                    minimum.max(1),
909                    VecGrowth::Exact,
910                    Some(1),
911                    operation,
912                )
913                .map_err(CodecError::from)?;
914            } else {
915                self.reserve_retained_vec_storage(
916                    &mut collected,
917                    1,
918                    VecGrowth::Amortized,
919                    Some(1),
920                    operation,
921                )
922                .map_err(CodecError::from)?;
923            }
924            collected.push(map(value)?);
925        }
926        Ok(collected)
927    }
928
929    /// Copies a slice after charging its collection slots.
930    pub fn copy_slice<T: Copy>(
931        &self,
932        values: &[T],
933        operation: &'static str,
934    ) -> Result<Vec<T>, CodecError> {
935        self.charge_work(u64_from_index(values.len()), operation)?;
936        let mut copy = self.collection_vec(values.len(), operation)?;
937        copy.extend_from_slice(values);
938        Ok(copy)
939    }
940
941    /// Collects optional values, stopping at the first absent value.
942    pub fn collect_options<T>(
943        &self,
944        values: impl IntoIterator<Item = Option<T>>,
945        operation: &'static str,
946    ) -> Result<Option<Vec<T>>, CodecError> {
947        let mut collected = Vec::new();
948        for value in values {
949            let Some(value) = value else { return Ok(None) };
950            self.push_vec(&mut collected, value, operation)?;
951        }
952        Ok(Some(collected))
953    }
954
955    /// Collects fallible optional values, stopping at the first absent value.
956    pub fn collect_fallible_options<T, E: Into<CodecError>>(
957        &self,
958        values: impl IntoIterator<Item = Result<Option<T>, E>>,
959        operation: &'static str,
960    ) -> Result<Option<Vec<T>>, CodecError> {
961        let mut collected = Vec::new();
962        for value in values {
963            let Some(value) = value.map_err(Into::into)? else {
964                return Ok(None);
965            };
966            self.push_vec(&mut collected, value, operation)?;
967        }
968        Ok(Some(collected))
969    }
970
971    /// Collects retained strings into a charged hash set.
972    pub fn collect_string_set<'a>(
973        &self,
974        values: impl IntoIterator<Item = &'a str>,
975        operation: &'static str,
976    ) -> Result<HashSet<String>, CodecError> {
977        let mut collected = HashSet::new();
978        for value in values {
979            self.insert_string_set(&mut collected, value, operation)?;
980        }
981        Ok(collected)
982    }
983
984    /// Copies retained rows and charges row and item slots.
985    pub fn copy_retained_rows<T: Copy>(
986        &self,
987        rows: &[Vec<T>],
988        row_operation: &'static str,
989        item_operation: &'static str,
990    ) -> Result<Vec<Vec<T>>, CodecError> {
991        self.try_collect_retained_with(rows, row_operation, |row| {
992            self.copy_slice(row, item_operation)
993        })
994    }
995
996    /// Copies a retained set after charging storage and entries.
997    pub fn copy_retained_set<T: Copy + Eq + Hash>(
998        &self,
999        values: &HashSet<T>,
1000        operation: &'static str,
1001    ) -> Result<HashSet<T>, CodecError> {
1002        self.charge_work(u64_from_index(values.len()), operation)?;
1003        let mut copy = HashSet::new();
1004        self.reserve_set(&mut copy, values.len(), operation)?;
1005        copy.extend(values.iter().copied());
1006        Ok(copy)
1007    }
1008
1009    // SwissTable has a maximum 7/8 load, power-of-two bucket storage, and
1010    // at most 16 trailing control bytes. Small elements require 16 bytes
1011    // of bucket storage. Alignment padding is bounded by max(align, 16)-1.
1012    fn hash_storage_bytes<T>(
1013        &self,
1014        capacity: usize,
1015        operation: &'static str,
1016    ) -> Result<usize, ResourceLimit> {
1017        if capacity == 0 {
1018            return Ok(0);
1019        }
1020        let buckets = if capacity < 4 {
1021            Some(4)
1022        } else if capacity < 8 {
1023            Some(8)
1024        } else {
1025            capacity
1026                .checked_mul(8)
1027                .and_then(|slots| (slots / 7).checked_next_power_of_two())
1028        }
1029        .ok_or_else(|| self.retained_size_overflow_limit(operation))?;
1030        let alignment = std::mem::align_of::<T>().max(16);
1031        buckets
1032            .checked_mul(std::mem::size_of::<T>())
1033            .and_then(|bytes| bytes.checked_add(alignment - 1))
1034            .and_then(|bytes| bytes.checked_add(buckets))
1035            .and_then(|bytes| bytes.checked_add(16))
1036            .ok_or_else(|| self.retained_size_overflow_limit(operation))
1037    }
1038
1039    fn charge_hash_growth<T>(
1040        &self,
1041        len: usize,
1042        capacity: usize,
1043        count: usize,
1044        operation: &'static str,
1045    ) -> Result<usize, ResourceLimit> {
1046        let required = len
1047            .checked_add(count)
1048            .ok_or_else(|| self.retained_size_overflow_limit(operation))?;
1049        if required <= capacity {
1050            return Ok(0);
1051        }
1052        let minimum = match std::mem::size_of::<T>() {
1053            0..=1 => 14,
1054            2..=3 => 7,
1055            _ => 3,
1056        };
1057        let bytes = self.hash_storage_bytes::<T>(required.max(minimum), operation)?
1058            - self.hash_storage_bytes::<T>(capacity, operation)?;
1059        self.charge_retained_limit(u64_from_index(bytes), operation)?;
1060        Ok(bytes)
1061    }
1062
1063    /// Reserves hash set entries after charging their slots.
1064    pub fn reserve_set<T: Eq + Hash>(
1065        &self,
1066        values: &mut HashSet<T>,
1067        count: usize,
1068        operation: &'static str,
1069    ) -> Result<(), CodecError> {
1070        let bytes =
1071            self.charge_hash_growth::<T>(values.len(), values.capacity(), count, operation)?;
1072        self.charge_collection_items(u64_from_index(count), operation)?;
1073        values.try_reserve(count).map_err(|_| {
1074            self.budget
1075                .retained_allocation_failed(u64_from_index(bytes), operation)
1076        })
1077    }
1078
1079    /// Reserves hash map entries after charging their slots.
1080    pub fn reserve_map<K: Eq + Hash, V>(
1081        &self,
1082        values: &mut HashMap<K, V>,
1083        count: usize,
1084        operation: &'static str,
1085    ) -> Result<(), CodecError> {
1086        let bytes =
1087            self.charge_hash_growth::<(K, V)>(values.len(), values.capacity(), count, operation)?;
1088        self.charge_collection_items(u64_from_index(count), operation)?;
1089        values.try_reserve(count).map_err(|_| {
1090            self.budget
1091                .retained_allocation_failed(u64_from_index(bytes), operation)
1092        })
1093    }
1094
1095    // A B-tree node stores at most 11 key/value lanes, 12 child pointers,
1096    // and a parent pointer plus length/index metadata. The bound includes
1097    // four pointer-width metadata slots and padding for both lane arrays.
1098    // A tree built by insertion has at most (n - 1) / 5 + 1 nodes for n > 0,
1099    // and none when empty: every node except the root keeps at least five keys.
1100    // Admit the increase of that bound before each insertion. Since insertion
1101    // frees no nodes, cumulative admission bounds the live node storage.
1102    fn tree_growth_bytes<K, V>(
1103        &self,
1104        len: usize,
1105        operation: &'static str,
1106    ) -> Result<u64, ResourceLimit> {
1107        let alignment = std::mem::align_of::<K>()
1108            .max(std::mem::align_of::<V>())
1109            .max(std::mem::align_of::<usize>());
1110        let next = len
1111            .checked_add(1)
1112            .ok_or_else(|| self.retained_size_overflow_limit(operation))?;
1113        let before = if len == 0 { 0 } else { (len - 1) / 5 + 1 };
1114        let nodes = (next - 1) / 5 + 1 - before;
1115        std::mem::size_of::<K>()
1116            .checked_add(std::mem::size_of::<V>())
1117            .and_then(|bytes| bytes.checked_mul(11))
1118            .and_then(|bytes| bytes.checked_add(16 * std::mem::size_of::<usize>()))
1119            .and_then(|bytes| bytes.checked_add(2 * alignment))
1120            .and_then(|bytes| bytes.checked_mul(nodes))
1121            .map(u64_from_index)
1122            .ok_or_else(|| self.retained_size_overflow_limit(operation))
1123    }
1124
1125    /// Admits the backing nodes for one ordered entry whose slot is already charged.
1126    pub fn admit_btree_node_storage<K, V>(
1127        &self,
1128        len: usize,
1129        operation: &'static str,
1130    ) -> Result<(), CodecError> {
1131        self.charge_retained(self.tree_growth_bytes::<K, V>(len, operation)?, operation)
1132    }
1133
1134    /// Charges a new B-tree map key before insertion.
1135    pub fn admit_btree_entry<K: Ord, V>(
1136        &self,
1137        values: &BTreeMap<K, V>,
1138        key: &K,
1139        operation: &'static str,
1140    ) -> Result<(), CodecError> {
1141        if !values.contains_key(key) {
1142            self.admit_btree_node_storage::<K, V>(values.len(), operation)?;
1143            self.charge_collection_items(1, operation)?;
1144        }
1145        Ok(())
1146    }
1147
1148    /// Inserts a B-tree map entry after charging a new key.
1149    pub fn insert_btree_map<K: Ord, V>(
1150        &self,
1151        values: &mut BTreeMap<K, V>,
1152        key: K,
1153        value: V,
1154        operation: &'static str,
1155    ) -> Result<Option<V>, CodecError> {
1156        self.admit_btree_entry(values, &key, operation)?;
1157        Ok(values.insert(key, value))
1158    }
1159
1160    /// Inserts a B-tree set item after charging a new value.
1161    pub fn insert_btree_set<T: Ord>(
1162        &self,
1163        values: &mut BTreeSet<T>,
1164        value: T,
1165        operation: &'static str,
1166    ) -> Result<bool, CodecError> {
1167        if values.contains(&value) {
1168            return Ok(false);
1169        }
1170        self.charge_retained(
1171            self.tree_growth_bytes::<T, ()>(values.len(), operation)?,
1172            operation,
1173        )?;
1174        self.charge_collection_items(1, operation)?;
1175        Ok(values.insert(value))
1176    }
1177
1178    /// Collects distinct ordered values after admitting each new entry.
1179    pub fn collect_btree_set<T: Ord>(
1180        &self,
1181        values: impl IntoIterator<Item = T>,
1182        operation: &'static str,
1183    ) -> Result<BTreeSet<T>, CodecError> {
1184        let mut out = BTreeSet::new();
1185        for value in values {
1186            self.insert_btree_set(&mut out, value, operation)?;
1187        }
1188        Ok(out)
1189    }
1190
1191    /// Appends an ordered group member after admitting its group and slot.
1192    pub fn push_btree_group<K: Ord, V>(
1193        &self,
1194        groups: &mut BTreeMap<K, Vec<V>>,
1195        key: K,
1196        value: V,
1197        group_operation: &'static str,
1198        item_operation: &'static str,
1199    ) -> Result<(), CodecError> {
1200        if let Some(values) = groups.get_mut(&key) {
1201            return self.push_vec(values, value, item_operation);
1202        }
1203        self.admit_btree_entry(groups, &key, group_operation)?;
1204        let mut values = self.collection_vec(1, item_operation)?;
1205        values.push(value);
1206        groups.insert(key, values);
1207        Ok(())
1208    }
1209
1210    /// Inserts an ordered group member after admitting its group and value.
1211    pub fn insert_btree_group_set<K: Ord, V: Ord>(
1212        &self,
1213        groups: &mut BTreeMap<K, BTreeSet<V>>,
1214        key: K,
1215        value: V,
1216        group_operation: &'static str,
1217        item_operation: &'static str,
1218    ) -> Result<(), CodecError> {
1219        if let Some(values) = groups.get_mut(&key) {
1220            self.insert_btree_set(values, value, item_operation)?;
1221            return Ok(());
1222        }
1223        self.admit_btree_entry(groups, &key, group_operation)?;
1224        let mut values = BTreeSet::new();
1225        self.insert_btree_set(&mut values, value, item_operation)?;
1226        groups.insert(key, values);
1227        Ok(())
1228    }
1229
1230    /// Resizes a byte buffer after admitting additional retained storage.
1231    pub fn resize_retained_bytes(
1232        &self,
1233        values: &mut Vec<u8>,
1234        length: usize,
1235        fill: u8,
1236        operation: &'static str,
1237    ) -> Result<(), CodecError> {
1238        if let Some(additional) = length.checked_sub(values.len()) {
1239            self.reserve_capacity(values, additional, operation)?;
1240            for _ in 0..additional {
1241                values.push(fill);
1242            }
1243        } else {
1244            values.truncate(length);
1245        }
1246        Ok(())
1247    }
1248
1249    /// Extends retained bytes after charging both their slots and storage.
1250    pub fn extend_retained_bytes(
1251        &self,
1252        target: &mut Vec<u8>,
1253        source: &[u8],
1254        operation: &'static str,
1255    ) -> Result<(), CodecError> {
1256        self.reserve_vec(target, source.len(), operation)?;
1257        target.extend_from_slice(source);
1258        Ok(())
1259    }
1260
1261    /// Reserves a scoped vector and returns its live reservation.
1262    pub fn temporary_vec<T>(
1263        &self,
1264        count: usize,
1265        operation: &'static str,
1266    ) -> Result<(Vec<T>, ScopedReservation<'_>), CodecError> {
1267        let mut reservation = self.reserve_scoped(0, operation)?;
1268        let values = reservation.with_storage(|| self.collection_vec(count, operation))?;
1269        Ok((values, reservation))
1270    }
1271
1272    /// Reserves a scoped hash set and returns its live reservation.
1273    pub fn temporary_set<T: Eq + Hash>(
1274        &self,
1275        count: usize,
1276        operation: &'static str,
1277    ) -> Result<(HashSet<T>, ScopedReservation<'_>), CodecError> {
1278        self.temporary_set_limit(count, operation)
1279            .map_err(Into::into)
1280    }
1281
1282    /// Reserves a scoped hash set with a typed resource error.
1283    pub fn temporary_set_limit<T: Eq + Hash>(
1284        &self,
1285        count: usize,
1286        operation: &'static str,
1287    ) -> Result<(HashSet<T>, ScopedReservation<'_>), ResourceLimit> {
1288        let mut reservation = self.reserve_scoped_limit(0, operation)?;
1289        let mut values = HashSet::new();
1290        reservation.with_storage_limit(|| {
1291            let bytes = self.charge_hash_growth::<T>(0, 0, count, operation)?;
1292            self.charge_collection_items_limit(u64_from_index(count), operation)?;
1293            values.try_reserve(count).map_err(|_| {
1294                self.budget
1295                    .retained_allocation_failed_limit(u64_from_index(bytes), operation)
1296            })
1297        })?;
1298        Ok((values, reservation))
1299    }
1300
1301    /// Collects a borrowed string index with scoped hash storage.
1302    pub fn collect_scoped_string_set<'text>(
1303        &self,
1304        count: usize,
1305        values: impl IntoIterator<Item = &'text str>,
1306        operation: &'static str,
1307    ) -> Result<(HashSet<&'text str>, ScopedReservation<'_>), CodecError> {
1308        let (mut output, mut reservation) = self.temporary_set(count, operation)?;
1309        let mut remaining = count;
1310        reservation.with_storage(|| {
1311            for value in values {
1312                self.charge_work(
1313                    u64_from_index(value.len())
1314                        .checked_mul(2)
1315                        .and_then(|work| work.checked_add(1))
1316                        .ok_or_else(|| self.refuse_codec_limit(operation, u64::MAX, u64::MAX))?,
1317                    operation,
1318                )?;
1319                if !output.contains(value) {
1320                    if remaining == 0 {
1321                        self.reserve_set(&mut output, 1, operation)?;
1322                    } else {
1323                        remaining -= 1;
1324                    }
1325                    output.insert(value);
1326                }
1327            }
1328            Ok::<(), CodecError>(())
1329        })?;
1330        Ok((output, reservation))
1331    }
1332
1333    /// Collects a borrowed string-keyed index with scoped hash storage.
1334    pub fn collect_scoped_string_map<'text, V>(
1335        &self,
1336        count: usize,
1337        values: impl IntoIterator<Item = (&'text str, V)>,
1338        operation: &'static str,
1339    ) -> Result<(HashMap<&'text str, V>, ScopedReservation<'_>), CodecError> {
1340        let mut reservation = self.reserve_scoped(0, operation)?;
1341        let mut output = HashMap::new();
1342        reservation.with_storage(|| {
1343            self.reserve_map(&mut output, count, operation)?;
1344            let mut remaining = count;
1345            for (key, value) in values {
1346                self.charge_work(
1347                    u64_from_index(key.len())
1348                        .checked_mul(2)
1349                        .and_then(|work| work.checked_add(1))
1350                        .ok_or_else(|| self.refuse_codec_limit(operation, u64::MAX, u64::MAX))?,
1351                    operation,
1352                )?;
1353                if !output.contains_key(key) {
1354                    if remaining == 0 {
1355                        self.reserve_map(&mut output, 1, operation)?;
1356                    } else {
1357                        remaining -= 1;
1358                    }
1359                }
1360                output.insert(key, value);
1361            }
1362            Ok::<(), CodecError>(())
1363        })?;
1364        Ok((output, reservation))
1365    }
1366
1367    /// Reserves a scoped deque and returns its live reservation.
1368    pub fn temporary_queue<T>(
1369        &self,
1370        count: usize,
1371        operation: &'static str,
1372    ) -> Result<(VecDeque<T>, ScopedReservation<'_>), CodecError> {
1373        let mut reservation = self.reserve_scoped(0, operation)?;
1374        let mut values = VecDeque::new();
1375        reservation.with_storage(|| {
1376            let bytes = count
1377                .checked_mul(std::mem::size_of::<T>())
1378                .ok_or_else(|| self.retained_size_overflow_limit(operation))?;
1379            self.charge_retained(u64_from_index(bytes), operation)?;
1380            self.charge_collection_items(u64_from_index(count), operation)?;
1381            values.try_reserve_exact(count).map_err(|_| {
1382                self.budget
1383                    .retained_allocation_failed(u64_from_index(bytes), operation)
1384            })
1385        })?;
1386        Ok((values, reservation))
1387    }
1388
1389    /// Charges retained text produced by a later aggregate materialization.
1390    pub fn charge_formatted_retained(
1391        &self,
1392        args: fmt::Arguments<'_>,
1393        operation: &'static str,
1394    ) -> Result<(), CodecError> {
1395        let length = self.formatted_length(args, operation)?;
1396        self.charge_retained(u64_from_index(length), operation)
1397    }
1398
1399    /// Formats a retained string after charging its exact byte count.
1400    pub fn format_retained(
1401        &self,
1402        args: fmt::Arguments<'_>,
1403        operation: &'static str,
1404    ) -> Result<String, CodecError> {
1405        let mut text = String::new();
1406        self.append_formatted_retained(&mut text, args, operation)?;
1407        Ok(text)
1408    }
1409
1410    /// Formats a temporary string and returns its live reservation.
1411    pub fn format_scoped(
1412        &self,
1413        args: fmt::Arguments<'_>,
1414        operation: &'static str,
1415    ) -> Result<(String, ScopedReservation<'_>), CodecError> {
1416        let mut reservation = self.reserve_scoped(0, operation)?;
1417        let text = self.format_scoped_text(&mut reservation, args, operation)?;
1418        Ok((text, reservation))
1419    }
1420
1421    /// Appends retained text after charging its bytes.
1422    pub fn append_retained(
1423        &self,
1424        output: &mut String,
1425        suffix: &str,
1426        operation: &'static str,
1427    ) -> Result<(), CodecError> {
1428        self.charge_work(u64_from_index(suffix.len()), operation)?;
1429        self.try_reserve_retained_text(output, suffix.len(), operation)?;
1430        output.push_str(suffix);
1431        Ok(())
1432    }
1433
1434    /// Appends formatted text after admitting its exact retained byte count.
1435    pub fn append_formatted_retained(
1436        &self,
1437        output: &mut String,
1438        args: fmt::Arguments<'_>,
1439        operation: &'static str,
1440    ) -> Result<(), CodecError> {
1441        struct Output<'ctx, 'arena, 'text> {
1442            ctx: &'ctx DecodeContext<'arena>,
1443            text: &'text mut String,
1444            operation: &'static str,
1445            refusal: Option<CodecError>,
1446        }
1447        impl Write for Output<'_, '_, '_> {
1448            fn write_str(&mut self, fragment: &str) -> fmt::Result {
1449                if let Err(error) = self
1450                    .ctx
1451                    .append_retained(self.text, fragment, self.operation)
1452                {
1453                    self.refusal = Some(error);
1454                    return Err(fmt::Error);
1455                }
1456                Ok(())
1457            }
1458        }
1459        let length = self.formatted_length(args, operation)?;
1460        self.try_reserve_retained_text(output, length, operation)?;
1461        let mut writer = Output {
1462            ctx: self,
1463            text: output,
1464            operation,
1465            refusal: None,
1466        };
1467        match fmt::write(&mut writer, args) {
1468            Ok(()) => Ok(()),
1469            Err(error) => Err(match writer.refusal {
1470                Some(refusal) => refusal,
1471                None => CodecError::malformed(error),
1472            }),
1473        }
1474    }
1475
1476    /// Reserves an empty retained string after charging its declared length.
1477    pub fn retained_string(
1478        &self,
1479        length: usize,
1480        operation: &'static str,
1481    ) -> Result<String, CodecError> {
1482        self.charge_retained(u64_from_index(length), operation)?;
1483        let mut value = String::new();
1484        value.try_reserve_exact(length).map_err(|_| {
1485            self.allocation_failed(ResourceDimension::RetainedBytes, length, operation)
1486        })?;
1487        Ok(value)
1488    }
1489
1490    /// Copies retained text and appends a retained suffix.
1491    pub fn retained_suffix(
1492        &self,
1493        value: &str,
1494        suffix: &str,
1495        operation: &'static str,
1496    ) -> Result<String, CodecError> {
1497        let mut output = self.copy_retained_text(value, operation)?;
1498        self.append_retained(&mut output, suffix, operation)?;
1499        Ok(output)
1500    }
1501
1502    /// Copies a slice of retained strings after charging collection slots.
1503    pub fn copy_retained_strings(
1504        &self,
1505        values: &[String],
1506        operation: &'static str,
1507    ) -> Result<Vec<String>, CodecError> {
1508        let mut copies = self.collection_vec(values.len(), operation)?;
1509        for value in values {
1510            copies.push(self.copy_retained_text(value, operation)?);
1511        }
1512        Ok(copies)
1513    }
1514
1515    /// Joins retained text after measuring and charging the exact byte count.
1516    pub fn join_retained<S: AsRef<str>>(
1517        &self,
1518        parts: &[S],
1519        separator: &str,
1520        operation: &'static str,
1521    ) -> Result<String, CodecError> {
1522        let mut count = 0_usize;
1523        for part in parts {
1524            count = count
1525                .checked_add(part.as_ref().len())
1526                .ok_or_else(|| self.refuse_codec_limit(operation, u64::MAX, u64::MAX))?;
1527        }
1528        let gaps = if parts.is_empty() { 0 } else { parts.len() - 1 };
1529        count = separator
1530            .len()
1531            .checked_mul(gaps)
1532            .and_then(|separators| count.checked_add(separators))
1533            .ok_or_else(|| self.refuse_codec_limit(operation, u64::MAX, u64::MAX))?;
1534        self.charge_retained(u64_from_index(count), operation)?;
1535        let mut output = String::new();
1536        output.try_reserve_exact(count).map_err(|_| {
1537            self.allocation_failed(ResourceDimension::RetainedBytes, count, operation)
1538        })?;
1539        for (index, part) in parts.iter().enumerate() {
1540            if index != 0 {
1541                output.push_str(separator);
1542            }
1543            output.push_str(part.as_ref());
1544        }
1545        Ok(output)
1546    }
1547
1548    /// Joins displayed values while charging each retained text fragment.
1549    pub fn join_display_retained<T: fmt::Display>(
1550        &self,
1551        values: impl IntoIterator<Item = T>,
1552        separator: &str,
1553        operation: &'static str,
1554    ) -> Result<String, CodecError> {
1555        struct ChargedText<'a, 'arena> {
1556            ctx: &'a DecodeContext<'arena>,
1557            operation: &'static str,
1558            text: String,
1559            refusal: Option<CodecError>,
1560        }
1561        impl Write for ChargedText<'_, '_> {
1562            fn write_str(&mut self, fragment: &str) -> fmt::Result {
1563                if let Err(error) =
1564                    self.ctx
1565                        .append_retained(&mut self.text, fragment, self.operation)
1566                {
1567                    self.refusal = Some(error);
1568                    return Err(fmt::Error);
1569                }
1570                Ok(())
1571            }
1572        }
1573        let mut output = ChargedText {
1574            ctx: self,
1575            operation,
1576            text: String::new(),
1577            refusal: None,
1578        };
1579        for (index, value) in values.into_iter().enumerate() {
1580            let written = if index == 0 {
1581                output.write_fmt(format_args!("{value}"))
1582            } else {
1583                output
1584                    .write_str(separator)
1585                    .and_then(|()| output.write_fmt(format_args!("{value}")))
1586            };
1587            if written.is_err() {
1588                return Err(match output.refusal {
1589                    Some(error) => error,
1590                    None => self.refuse_codec_limit(operation, 0, 1),
1591                });
1592            }
1593        }
1594        Ok(output.text)
1595    }
1596
1597    fn formatted_length(
1598        &self,
1599        args: fmt::Arguments<'_>,
1600        operation: &'static str,
1601    ) -> Result<usize, CodecError> {
1602        struct Count<'ctx, 'arena> {
1603            ctx: &'ctx DecodeContext<'arena>,
1604            operation: &'static str,
1605            length: Option<usize>,
1606            refusal: Option<CodecError>,
1607        }
1608        impl Write for Count<'_, '_> {
1609            fn write_str(&mut self, value: &str) -> fmt::Result {
1610                if let Err(error) = self
1611                    .ctx
1612                    .charge_work(u64_from_index(value.len()), self.operation)
1613                {
1614                    self.refusal = Some(error);
1615                    return Err(fmt::Error);
1616                }
1617                self.length = self.length.and_then(|total| total.checked_add(value.len()));
1618                self.length.map(|_| ()).ok_or(fmt::Error)
1619            }
1620        }
1621        let mut count = Count {
1622            ctx: self,
1623            operation,
1624            length: Some(0),
1625            refusal: None,
1626        };
1627        if fmt::write(&mut count, args).is_err() {
1628            return Err(match count.refusal {
1629                Some(error) => error,
1630                None => self.refuse_codec_limit(operation, u64::MAX, u64::MAX),
1631            });
1632        }
1633        count
1634            .length
1635            .ok_or_else(|| self.refuse_codec_limit(operation, u64::MAX, u64::MAX))
1636    }
1637}
1638
1639#[cfg(test)]
1640mod tests;
1641
1642#[cfg(test)]
1643mod temporary_capacity_tests {
1644    #[test]
1645    fn temporary_reserve_charges_no_storage_with_available_capacity() {
1646        use super::DecodeContext;
1647        let arena = super::super::DecodeArena::new();
1648        let mut policy = super::super::DecodePolicy::service();
1649        policy.limits.max_materialized_bytes = 0;
1650        let (ctx, _) =
1651            DecodeContext::from_root_bytes(&[], &arena, &policy).expect("admitted test operation");
1652        let mut values = Vec::<u64>::with_capacity(8);
1653        let capacity = values.capacity();
1654        let storage = ctx
1655            .reserve_temporary_vec(&mut values, 3, "existing temporary capacity")
1656            .expect("admitted test operation");
1657        assert_eq!(values.capacity(), capacity);
1658        drop(storage);
1659    }
1660}
1661
1662#[cfg(test)]
1663mod tree_work_tests {
1664    use std::cell::Cell;
1665    use std::cmp::Ordering;
1666    use std::collections::BTreeSet;
1667
1668    use super::DecodeContext;
1669    use crate::decode::{DecodeArena, DecodePolicy};
1670
1671    thread_local! {
1672        static COMPARISONS: Cell<u64> = const { Cell::new(0) };
1673    }
1674
1675    #[derive(Debug)]
1676    struct CountedKey {
1677        value: usize,
1678    }
1679
1680    impl PartialEq for CountedKey {
1681        fn eq(&self, other: &Self) -> bool {
1682            self.value == other.value
1683        }
1684    }
1685
1686    impl Eq for CountedKey {}
1687
1688    impl PartialOrd for CountedKey {
1689        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1690            Some(self.cmp(other))
1691        }
1692    }
1693
1694    impl Ord for CountedKey {
1695        fn cmp(&self, other: &Self) -> Ordering {
1696            COMPARISONS.with(|count| count.set(count.get() + 1));
1697            self.value.cmp(&other.value)
1698        }
1699    }
1700
1701    #[test]
1702    fn scoped_tree_work_refuses_before_comparing_or_inserting() {
1703        let arena = DecodeArena::new();
1704        let mut policy = DecodePolicy::service();
1705        policy.limits.max_work_units = 0;
1706        let ctx = DecodeContext::new(&arena, &policy, false);
1707        COMPARISONS.with(|count| count.set(0));
1708        let mut values = BTreeSet::from([CountedKey { value: 0 }]);
1709        let mut storage = ctx.reserve_scoped(0, "tree test storage").expect("scope");
1710        let error = ctx
1711            .insert_scoped_btree_set(
1712                &mut storage,
1713                &mut values,
1714                CountedKey { value: 1 },
1715                "tree test comparisons",
1716                "tree test storage",
1717            )
1718            .expect_err("lookup exceeds work allowance");
1719        assert!(matches!(error, crate::CodecError::ResourceLimit(limit)
1720            if limit.operation == "tree test comparisons"));
1721        assert_eq!(COMPARISONS.with(Cell::get), 0);
1722        assert_eq!(values.len(), 1);
1723    }
1724
1725    #[test]
1726    fn scoped_tree_work_bounds_comparisons_without_billing_full_scans() {
1727        const COUNT: usize = 16_384;
1728        for order in 0..3 {
1729            let arena = DecodeArena::new();
1730            let mut policy = DecodePolicy::service();
1731            // The old insertion charges alone cost COUNT * (COUNT - 1) / 2.
1732            policy.limits.max_work_units = 3_000_000;
1733            let ctx = DecodeContext::new(&arena, &policy, false);
1734            COMPARISONS.with(|count| count.set(0));
1735            let mut values = BTreeSet::new();
1736            let mut storage = ctx.reserve_scoped(0, "tree test storage").expect("scope");
1737            for index in 0..COUNT {
1738                let value = match order {
1739                    0 => index,
1740                    1 => COUNT - index - 1,
1741                    _ => (index * 8191) % COUNT,
1742                };
1743                assert!(ctx
1744                    .insert_scoped_btree_set(
1745                        &mut storage,
1746                        &mut values,
1747                        CountedKey { value },
1748                        "tree test comparisons",
1749                        "tree test storage",
1750                    )
1751                    .expect("indexed insertion fits work allowance"));
1752            }
1753            for value in 0..=COUNT {
1754                ctx.charge_btree_lookup(values.len(), "tree test membership")
1755                    .expect("indexed membership fits work allowance");
1756                assert_eq!(values.contains(&CountedKey { value }), value < COUNT);
1757            }
1758            assert!(!ctx
1759                .insert_scoped_btree_set(
1760                    &mut storage,
1761                    &mut values,
1762                    CountedKey { value: COUNT / 2 },
1763                    "tree test comparisons",
1764                    "tree test storage",
1765                )
1766                .expect("duplicate fits work allowance"));
1767            assert_eq!(values.len(), COUNT);
1768            let refusal = ctx
1769                .charge_work_limit(u64::MAX, "read tree test work")
1770                .expect_err("measurement exceeds the work limit");
1771            assert!(refusal.used >= COMPARISONS.with(Cell::get));
1772            assert!(refusal.used < policy.limits.max_work_units);
1773        }
1774    }
1775}