diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index 1e9e4a7f..2b031bcd 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -9,6 +9,60 @@ use std::borrow::Borrow; use std::prelude::v1::*; impl Dynamic { + /// Recursively calculate the sizes of an array. + /// + /// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`. + /// + /// # Panics + /// + /// Panics if any interior data is shared (should never happen). + #[cfg(not(feature = "no_index"))] + #[inline] + pub(crate) fn calc_array_sizes(array: &crate::Array, _top: bool) -> (usize, usize, usize) { + array + .iter() + .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { + Union::Array(..) => { + let (a, m, s) = value.calc_data_sizes(false); + (ax + a + 1, mx + m, sx + s) + } + Union::Blob(ref a, ..) => (ax + 1 + a.len(), mx, sx), + #[cfg(not(feature = "no_object"))] + Union::Map(..) => { + let (a, m, s) = value.calc_data_sizes(false); + (ax + a + 1, mx + m, sx + s) + } + Union::Str(ref s, ..) => (ax + 1, mx, sx + s.len()), + _ => (ax + 1, mx, sx), + }) + } + /// Recursively calculate the sizes of a map. + /// + /// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`. + /// + /// # Panics + /// + /// Panics if any interior data is shared (should never happen). + #[cfg(not(feature = "no_object"))] + #[inline] + pub(crate) fn calc_map_sizes(map: &crate::Map, _top: bool) -> (usize, usize, usize) { + map.values() + .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { + #[cfg(not(feature = "no_index"))] + Union::Array(..) => { + let (a, m, s) = value.calc_data_sizes(false); + (ax + a, mx + m + 1, sx + s) + } + #[cfg(not(feature = "no_index"))] + Union::Blob(ref a, ..) => (ax + a.len(), mx, sx), + Union::Map(..) => { + let (a, m, s) = value.calc_data_sizes(false); + (ax + a, mx + m + 1, sx + s) + } + Union::Str(ref s, ..) => (ax, mx + 1, sx + s.len()), + _ => (ax, mx + 1, sx), + }) + } /// Recursively calculate the sizes of a value. /// /// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`. @@ -16,47 +70,15 @@ impl Dynamic { /// # Panics /// /// Panics if any interior data is shared (should never happen). + #[inline] pub(crate) fn calc_data_sizes(&self, _top: bool) -> (usize, usize, usize) { match self.0 { #[cfg(not(feature = "no_index"))] - Union::Array(ref arr, ..) => { - arr.iter() - .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { - Union::Array(..) => { - let (a, m, s) = value.calc_data_sizes(false); - (ax + a + 1, mx + m, sx + s) - } - Union::Blob(ref a, ..) => (ax + 1 + a.len(), mx, sx), - #[cfg(not(feature = "no_object"))] - Union::Map(..) => { - let (a, m, s) = value.calc_data_sizes(false); - (ax + a + 1, mx + m, sx + s) - } - Union::Str(ref s, ..) => (ax + 1, mx, sx + s.len()), - _ => (ax + 1, mx, sx), - }) - } + Union::Array(ref arr, ..) => Self::calc_array_sizes(&**arr, _top), #[cfg(not(feature = "no_index"))] Union::Blob(ref blob, ..) => (blob.len(), 0, 0), #[cfg(not(feature = "no_object"))] - Union::Map(ref map, ..) => { - map.values() - .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { - #[cfg(not(feature = "no_index"))] - Union::Array(..) => { - let (a, m, s) = value.calc_data_sizes(false); - (ax + a, mx + m + 1, sx + s) - } - #[cfg(not(feature = "no_index"))] - Union::Blob(ref a, ..) => (ax + a.len(), mx, sx), - Union::Map(..) => { - let (a, m, s) = value.calc_data_sizes(false); - (ax + a, mx + m + 1, sx + s) - } - Union::Str(ref s, ..) => (ax, mx + 1, sx + s.len()), - _ => (ax, mx + 1, sx), - }) - } + Union::Map(ref map, ..) => Self::calc_map_sizes(&**map, _top), Union::Str(ref s, ..) => (0, 0, s.len()), #[cfg(not(feature = "no_closure"))] Union::Shared(..) if _top => self.read_lock::().unwrap().calc_data_sizes(true), diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index c1502d4a..b8396991 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -235,52 +235,15 @@ pub mod array_functions { // Check if array will be over max size limit #[cfg(not(feature = "unchecked"))] - { - use crate::types::dynamic::Union; + if _ctx.engine().max_array_size() > 0 { + let pad = len - array.len(); + let (a, m, s) = Dynamic::calc_array_sizes(array, true); + let (ax, mx, sx) = item.calc_data_sizes(true); - if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { - return Err( - ERR::ErrorDataTooLarge("Size of array".to_string(), Position::NONE).into(), - ); - } - - let check_sizes = match item.0 { - Union::Str(..) => true, - Union::Array(..) => true, - #[cfg(not(feature = "no_object"))] - Union::Map(..) => true, - _ => false, - }; - - if check_sizes { - let mut arr_len = array.len(); - let mut arr = Dynamic::from_array(mem::take(array)); - - let (mut a1, mut m1, mut s1) = arr.calc_data_sizes(true); - let (a2, m2, s2) = item.calc_data_sizes(true); - - { - let mut guard = arr.write_lock::().unwrap(); - - while arr_len < len { - a1 += a2; - m1 += m2; - s1 += s2; - - _ctx.engine().throw_on_size((a1, m1, s1))?; - - guard.push(item.clone()); - arr_len += 1; - } - } - - *array = arr.into_array().unwrap(); - } else { - array.resize(len, item); - } + _ctx.engine() + .throw_on_size((a + pad + ax * pad, m + mx * pad, s + sx * pad))?; } - #[cfg(feature = "unchecked")] array.resize(len, item); Ok(())