1#![cfg(not(feature = "unchecked"))]
3
4use super::GlobalRuntimeState;
5use crate::types::dynamic::Union;
6use crate::{Dynamic, Engine, Position, RhaiResultOf, ERR};
7use std::borrow::Borrow;
8#[cfg(feature = "no_std")]
9use std::prelude::v1::*;
10
11#[cfg(not(feature = "no_index"))]
19#[inline]
20pub fn calc_array_sizes(array: &crate::Array) -> (usize, usize, usize) {
21 let (mut ax, mut mx, mut sx) = (0, 0, 0);
22
23 for value in array {
24 ax += 1;
25
26 match value.0 {
27 Union::Array(ref a, ..) => {
28 let (a, m, s) = calc_array_sizes(a);
29 ax += a;
30 mx += m;
31 sx += s;
32 }
33 Union::Blob(ref a, ..) => ax += 1 + a.len(),
34 #[cfg(not(feature = "no_object"))]
35 Union::Map(ref m, ..) => {
36 let (a, m, s) = calc_map_sizes(m);
37 ax += a;
38 mx += m;
39 sx += s;
40 }
41 Union::Str(ref s, ..) => sx += s.len(),
42 #[cfg(not(feature = "no_closure"))]
43 Union::Shared(..) => {
44 unreachable!("shared values discovered within data")
45 }
46 _ => (),
47 }
48 }
49
50 (ax, mx, sx)
51}
52#[cfg(not(feature = "no_object"))]
60#[inline]
61pub fn calc_map_sizes(map: &crate::Map) -> (usize, usize, usize) {
62 let (mut ax, mut mx, mut sx) = (0, 0, 0);
63
64 for value in map.values() {
65 mx += 1;
66
67 match value.0 {
68 #[cfg(not(feature = "no_index"))]
69 Union::Array(ref a, ..) => {
70 let (a, m, s) = calc_array_sizes(a);
71 ax += a;
72 mx += m;
73 sx += s;
74 }
75 #[cfg(not(feature = "no_index"))]
76 Union::Blob(ref a, ..) => ax += 1 + a.len(),
77 Union::Map(ref m, ..) => {
78 let (a, m, s) = calc_map_sizes(m);
79 ax += a;
80 mx += m;
81 sx += s;
82 }
83 Union::Str(ref s, ..) => sx += s.len(),
84 #[cfg(not(feature = "no_closure"))]
85 Union::Shared(..) => {
86 unreachable!("shared values discovered within data")
87 }
88 _ => (),
89 }
90 }
91
92 (ax, mx, sx)
93}
94
95#[inline]
103pub fn calc_data_sizes(value: &Dynamic, _top: bool) -> (usize, usize, usize) {
104 match value.0 {
105 #[cfg(not(feature = "no_index"))]
106 Union::Array(ref arr, ..) => calc_array_sizes(arr),
107 #[cfg(not(feature = "no_index"))]
108 Union::Blob(ref blob, ..) => (blob.len(), 0, 0),
109 #[cfg(not(feature = "no_object"))]
110 Union::Map(ref map, ..) => calc_map_sizes(map),
111 Union::Str(ref s, ..) => (0, 0, s.len()),
112 #[cfg(not(feature = "no_closure"))]
113 Union::Shared(..) if _top => calc_data_sizes(&value.read_lock::<Dynamic>().unwrap(), true),
114 #[cfg(not(feature = "no_closure"))]
115 Union::Shared(..) => {
116 unreachable!("shared values discovered within data: {}", value)
117 }
118 _ => (0, 0, 0),
119 }
120}
121
122impl Engine {
123 #[cfg(not(feature = "unchecked"))]
128 pub(crate) fn throw_on_size(&self, (_arr, _map, s): (usize, usize, usize)) -> RhaiResultOf<()> {
129 if self.limits.string_len.map_or(false, |max| s > max.get()) {
130 return Err(
131 ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(),
132 );
133 }
134
135 #[cfg(not(feature = "no_index"))]
136 if self.limits.array_size.map_or(false, |max| _arr > max.get()) {
137 return Err(
138 ERR::ErrorDataTooLarge("Size of array/BLOB".to_string(), Position::NONE).into(),
139 );
140 }
141
142 #[cfg(not(feature = "no_object"))]
143 if self.limits.map_size.map_or(false, |max| _map > max.get()) {
144 return Err(
145 ERR::ErrorDataTooLarge("Size of object map".to_string(), Position::NONE).into(),
146 );
147 }
148
149 Ok(())
150 }
151
152 #[cfg(not(feature = "unchecked"))]
154 #[inline]
155 pub(crate) fn check_data_size<T: Borrow<Dynamic>>(
156 &self,
157 value: T,
158 pos: Position,
159 ) -> RhaiResultOf<T> {
160 if !self.has_data_size_limit() {
162 return Ok(value);
163 }
164
165 let sizes = calc_data_sizes(value.borrow(), true);
166
167 self.throw_on_size(sizes)
168 .map_err(|err| err.fill_position(pos))?;
169
170 Ok(value)
171 }
172
173 #[cfg(not(feature = "unchecked"))]
177 #[inline(always)]
178 pub fn ensure_data_size_within_limits(&self, value: &Dynamic) -> RhaiResultOf<()> {
179 self.check_data_size(value, Position::NONE).map(|_| ())
180 }
181
182 #[inline(always)]
184 pub(crate) fn track_operation(
185 &self,
186 global: &mut GlobalRuntimeState,
187 pos: Position,
188 ) -> RhaiResultOf<()> {
189 global.num_operations += 1;
190
191 #[cfg(not(feature = "unchecked"))]
193 if self.max_operations() > 0 && global.num_operations > self.max_operations() {
194 return Err(ERR::ErrorTooManyOperations(pos).into());
195 }
196
197 self.progress
198 .as_ref()
199 .and_then(|progress| {
200 progress(global.num_operations)
201 .map(|token| Err(ERR::ErrorTerminated(token, pos).into()))
202 })
203 .unwrap_or(Ok(()))
204 }
205}