Skip to main content

sui_package_resolver/
limits.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{Error, Result};
5
6/// Default limit on the cumulative number of layout nodes constructed or copied.
7pub const DEFAULT_MAX_LAYOUT_WORK: usize = 2_048;
8
9/// Optional configuration that imposes limits on the work that the resolver can do for each
10/// request.
11#[derive(Debug, Clone)]
12pub struct Limits {
13    /// Maximum recursion depth through type parameters.
14    pub max_type_argument_depth: usize,
15    /// Maximum number of type arguments in a single type instantiation.
16    pub max_type_argument_width: usize,
17    /// Maximum size for the resolution context.
18    pub max_type_nodes: usize,
19    /// Maximum recursion depth through struct fields.
20    pub max_move_value_depth: usize,
21    /// Maximum cumulative number of layout nodes constructed or copied during resolution.
22    pub max_layout_work: usize,
23}
24
25pub(crate) struct LayoutBudget {
26    max_nodes: Option<usize>,
27    remaining_nodes: usize,
28}
29
30pub(crate) struct LayoutMeter<'b> {
31    max_depth: Option<usize>,
32    remaining_depth: usize,
33    budget: &'b mut LayoutBudget,
34}
35
36impl Limits {
37    pub(crate) fn layout_budget(&self) -> LayoutBudget {
38        LayoutBudget {
39            max_nodes: Some(self.max_layout_work),
40            remaining_nodes: self.max_layout_work,
41        }
42    }
43}
44
45impl LayoutBudget {
46    pub(crate) fn unlimited() -> Self {
47        Self {
48            max_nodes: None,
49            remaining_nodes: usize::MAX,
50        }
51    }
52
53    fn charge_node(&mut self) -> Result<()> {
54        self.charge_nodes(1)
55    }
56
57    fn charge_nodes(&mut self, nodes: usize) -> Result<()> {
58        let Some(max_nodes) = self.max_nodes else {
59            return Ok(());
60        };
61        if nodes > self.remaining_nodes {
62            return Err(Error::TooManyTypeNodes(
63                max_nodes,
64                max_nodes
65                    .saturating_sub(self.remaining_nodes)
66                    .saturating_add(nodes),
67            ));
68        }
69
70        self.remaining_nodes = self.remaining_nodes.saturating_sub(nodes);
71        Ok(())
72    }
73}
74
75impl<'b> LayoutMeter<'b> {
76    pub(crate) fn new(max_depth: Option<usize>, budget: &'b mut LayoutBudget) -> Self {
77        Self {
78            max_depth,
79            remaining_depth: max_depth.unwrap_or(usize::MAX),
80            budget,
81        }
82    }
83
84    pub(crate) fn check_depth(&self) -> Result<()> {
85        if self.remaining_depth == 0 {
86            return Err(Error::ValueNesting(self.max_depth.unwrap_or(0)));
87        }
88        Ok(())
89    }
90
91    pub(crate) fn ensure_depth(&self, depth: usize) -> Result<()> {
92        if depth > self.remaining_depth {
93            return Err(Error::ValueNesting(self.max_depth.unwrap_or(0)));
94        }
95        Ok(())
96    }
97
98    pub(crate) fn nest(&mut self) -> Result<LayoutMeter<'_>> {
99        self.check_depth()?;
100        Ok(LayoutMeter {
101            max_depth: self.max_depth,
102            remaining_depth: self.remaining_depth.saturating_sub(1),
103            budget: self.budget,
104        })
105    }
106
107    pub(crate) fn charge_node(&mut self) -> Result<()> {
108        self.budget.charge_node()
109    }
110
111    pub(crate) fn charge_nodes(&mut self, nodes: usize) -> Result<()> {
112        self.budget.charge_nodes(nodes)
113    }
114}