From adbe9a292ee8a663d15b9df5903afa55d99e67f2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 20 Sep 2021 18:35:23 +0800 Subject: [PATCH] Short circuit functions if empty: array, map, string. --- src/packages/array_basic.rs | 183 +++++++++++++++++++++++------ src/packages/map_basic.rs | 79 +++++++++---- src/packages/string_more.rs | 227 +++++++++++++++++++++++------------- 3 files changed, 352 insertions(+), 137 deletions(-) diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 3062f48b..f119bc0e 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -27,15 +27,29 @@ mod array_functions { } #[rhai_fn(name = "append", name = "+=")] pub fn append(array: &mut Array, y: Array) { - array.extend(y); + if !y.is_empty() { + if array.is_empty() { + *array = y; + } else { + array.extend(y); + } + } } #[rhai_fn(name = "+")] pub fn concat(mut array: Array, y: Array) -> Array { - array.extend(y); + if !y.is_empty() { + if array.is_empty() { + array = y; + } else { + array.extend(y); + } + } array } pub fn insert(array: &mut Array, position: INT, item: Dynamic) { - if position < 0 { + if array.is_empty() { + array.push(item); + } else if position < 0 { if let Some(n) = position.checked_abs() { if n as usize > array.len() { array.insert(0, item); @@ -46,7 +60,7 @@ mod array_functions { array.insert(0, item); } } else if (position as usize) >= array.len() { - push(array, item); + array.push(item); } else { array.insert(position as usize, item); } @@ -58,61 +72,78 @@ mod array_functions { len: INT, item: Dynamic, ) -> Result<(), Box> { + if len <= 0 { + return Ok(()); + } + // Check if array will be over max size limit #[cfg(not(feature = "unchecked"))] - if _ctx.engine().max_array_size() > 0 - && len > 0 - && (len as usize) > _ctx.engine().max_array_size() - { + if _ctx.engine().max_array_size() > 0 && (len as usize) > _ctx.engine().max_array_size() { return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), Position::NONE) .into(); } - if len > 0 && len as usize > array.len() { + if len as usize > array.len() { array.resize(len as usize, item); } Ok(()) } pub fn pop(array: &mut Array) -> Dynamic { - array.pop().unwrap_or_else(|| ().into()) + if array.is_empty() { + Dynamic::UNIT + } else { + array.pop().unwrap_or_else(|| Dynamic::UNIT) + } } pub fn shift(array: &mut Array) -> Dynamic { if array.is_empty() { - ().into() + Dynamic::UNIT } else { array.remove(0) } } pub fn remove(array: &mut Array, len: INT) -> Dynamic { if len < 0 || (len as usize) >= array.len() { - ().into() + Dynamic::UNIT } else { array.remove(len as usize) } } pub fn clear(array: &mut Array) { - array.clear(); + if !array.is_empty() { + array.clear(); + } } pub fn truncate(array: &mut Array, len: INT) { - if len >= 0 { - array.truncate(len as usize); - } else { - array.clear(); + if !array.is_empty() { + if len >= 0 { + array.truncate(len as usize); + } else { + array.clear(); + } } } pub fn chop(array: &mut Array, len: INT) { - if len as usize >= array.len() { - } else if len >= 0 { - array.drain(0..array.len() - len as usize); - } else { - array.clear(); + if !array.is_empty() && len as usize >= array.len() { + if len >= 0 { + array.drain(0..array.len() - len as usize); + } else { + array.clear(); + } } } pub fn reverse(array: &mut Array) { - array.reverse(); + if !array.is_empty() { + array.reverse(); + } } pub fn splice(array: &mut Array, start: INT, len: INT, replace: Array) { + if array.is_empty() { + *array = replace; + return; + } + let start = if start < 0 { let arr_len = array.len(); start @@ -136,18 +167,22 @@ mod array_functions { array.splice(start..start + len, replace.into_iter()); } pub fn extract(array: &mut Array, start: INT, len: INT) -> Array { + if array.is_empty() || len <= 0 { + return Array::new(); + } + let start = if start < 0 { let arr_len = array.len(); start .checked_abs() .map_or(0, |n| arr_len - (n as usize).min(arr_len)) } else if start as usize >= array.len() { - return Default::default(); + return Array::new(); } else { start as usize }; - let len = if len < 0 { + let len = if len <= 0 { 0 } else if len as usize > array.len() - start { array.len() - start @@ -155,17 +190,25 @@ mod array_functions { len as usize }; - array[start..start + len].to_vec() + if len == 0 { + Array::new() + } else { + array[start..start + len].to_vec() + } } #[rhai_fn(name = "extract")] pub fn extract_tail(array: &mut Array, start: INT) -> Array { + if array.is_empty() { + return Array::new(); + } + let start = if start < 0 { let arr_len = array.len(); start .checked_abs() .map_or(0, |n| arr_len - (n as usize).min(arr_len)) } else if start as usize >= array.len() { - return Default::default(); + return Array::new(); } else { start as usize }; @@ -174,7 +217,9 @@ mod array_functions { } #[rhai_fn(name = "split")] pub fn split_at(array: &mut Array, start: INT) -> Array { - if start < 0 { + if array.is_empty() { + Array::new() + } else if start < 0 { if let Some(n) = start.checked_abs() { if n as usize > array.len() { mem::take(array) @@ -187,7 +232,7 @@ mod array_functions { mem::take(array) } } else if start as usize >= array.len() { - Default::default() + Array::new() } else { let mut result = Array::new(); result.extend(array.drain(start as usize..)); @@ -200,6 +245,10 @@ mod array_functions { array: &mut Array, mapper: FnPtr, ) -> Result> { + if array.is_empty() { + return Ok(array.clone()); + } + let mut ar = Array::with_capacity(array.len()); for (i, item) in array.iter().enumerate() { @@ -233,6 +282,10 @@ mod array_functions { array: &mut Array, filter: FnPtr, ) -> Result> { + if array.is_empty() { + return Ok(array.clone()); + } + let mut ar = Array::new(); for (i, item) in array.iter().enumerate() { @@ -269,6 +322,10 @@ mod array_functions { array: &mut Array, value: Dynamic, ) -> Result> { + if array.is_empty() { + return Ok(false); + } + for item in array.iter_mut() { if ctx .call_fn_dynamic_raw(OP_EQUALS, true, &mut [item, &mut value.clone()]) @@ -300,7 +357,11 @@ mod array_functions { array: &mut Array, value: Dynamic, ) -> Result> { - index_of_starting_from(ctx, array, value, 0) + if array.is_empty() { + Ok(-1) + } else { + index_of_starting_from(ctx, array, value, 0) + } } #[rhai_fn(name = "index_of", return_raw, pure)] pub fn index_of_starting_from( @@ -309,6 +370,10 @@ mod array_functions { value: Dynamic, start: INT, ) -> Result> { + if array.is_empty() { + return Ok(-1); + } + let start = if start < 0 { let arr_len = array.len(); start @@ -351,7 +416,11 @@ mod array_functions { array: &mut Array, filter: FnPtr, ) -> Result> { - index_of_filter_starting_from(ctx, array, filter, 0) + if array.is_empty() { + Ok(-1) + } else { + index_of_filter_starting_from(ctx, array, filter, 0) + } } #[rhai_fn(name = "index_of", return_raw, pure)] pub fn index_of_filter_starting_from( @@ -360,6 +429,10 @@ mod array_functions { filter: FnPtr, start: INT, ) -> Result> { + if array.is_empty() { + return Ok(-1); + } + let start = if start < 0 { let arr_len = array.len(); start @@ -405,6 +478,10 @@ mod array_functions { array: &mut Array, filter: FnPtr, ) -> Result> { + if array.is_empty() { + return Ok(false); + } + for (i, item) in array.iter().enumerate() { if filter .call_dynamic(&ctx, None, [item.clone()]) @@ -439,6 +516,10 @@ mod array_functions { array: &mut Array, filter: FnPtr, ) -> Result> { + if array.is_empty() { + return Ok(true); + } + for (i, item) in array.iter().enumerate() { if !filter .call_dynamic(&ctx, None, [item.clone()]) @@ -473,6 +554,10 @@ mod array_functions { array: &mut Array, reducer: FnPtr, ) -> Result> { + if array.is_empty() { + return Ok(Dynamic::UNIT); + } + let mut result = Dynamic::UNIT; for (i, item) in array.iter().enumerate() { @@ -505,6 +590,10 @@ mod array_functions { reducer: FnPtr, initial: Dynamic, ) -> Result> { + if array.is_empty() { + return Ok(initial); + } + let mut result = initial; for (i, item) in array.iter().enumerate() { @@ -536,6 +625,10 @@ mod array_functions { array: &mut Array, reducer: FnPtr, ) -> Result> { + if array.is_empty() { + return Ok(Dynamic::UNIT); + } + let mut result = Dynamic::UNIT; for (i, item) in array.iter().enumerate().rev() { @@ -568,6 +661,10 @@ mod array_functions { reducer: FnPtr, initial: Dynamic, ) -> Result> { + if array.is_empty() { + return Ok(initial); + } + let mut result = initial; for (i, item) in array.iter().enumerate().rev() { @@ -599,6 +696,10 @@ mod array_functions { array: &mut Array, comparer: FnPtr, ) -> Result<(), Box> { + if array.is_empty() { + return Ok(()); + } + array.sort_by(|x, y| { comparer .call_dynamic(&ctx, None, [x.clone(), y.clone()]) @@ -621,6 +722,10 @@ mod array_functions { array: &mut Array, filter: FnPtr, ) -> Result> { + if array.is_empty() { + return Ok(Array::new()); + } + let mut drained = Array::with_capacity(array.len()); let mut i = 0; @@ -660,18 +765,22 @@ mod array_functions { } #[rhai_fn(name = "drain")] pub fn drain_range(array: &mut Array, start: INT, len: INT) -> Array { + if array.is_empty() || len <= 0 { + return Array::new(); + } + let start = if start < 0 { let arr_len = array.len(); start .checked_abs() .map_or(0, |n| arr_len - (n as usize).min(arr_len)) } else if start as usize >= array.len() { - return Default::default(); + return Array::new(); } else { start as usize }; - let len = if len < 0 { + let len = if len <= 0 { 0 } else if len as usize > array.len() - start { array.len() - start @@ -687,6 +796,10 @@ mod array_functions { array: &mut Array, filter: FnPtr, ) -> Result> { + if array.is_empty() { + return Ok(Array::new()); + } + let mut drained = Array::new(); let mut i = 0; @@ -726,6 +839,10 @@ mod array_functions { } #[rhai_fn(name = "retain")] pub fn retain_range(array: &mut Array, start: INT, len: INT) -> Array { + if array.is_empty() || len <= 0 { + return Array::new(); + } + let start = if start < 0 { let arr_len = array.len(); start diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 213500a5..b042d9e1 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -17,32 +17,56 @@ def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, { mod map_functions { #[rhai_fn(name = "has", pure)] pub fn contains(map: &mut Map, prop: ImmutableString) -> bool { - map.contains_key(prop.as_str()) + if map.is_empty() { + false + } else { + map.contains_key(prop.as_str()) + } } #[rhai_fn(pure)] pub fn len(map: &mut Map) -> INT { map.len() as INT } pub fn clear(map: &mut Map) { - map.clear(); + if !map.is_empty() { + map.clear(); + } } pub fn remove(map: &mut Map, name: ImmutableString) -> Dynamic { - map.remove(name.as_str()).unwrap_or_else(|| ().into()) + if !map.is_empty() { + map.remove(name.as_str()).unwrap_or_else(|| Dynamic::UNIT) + } else { + Dynamic::UNIT + } } #[rhai_fn(name = "mixin", name = "+=")] pub fn mixin(map: &mut Map, map2: Map) { - map.extend(map2.into_iter()); + if !map2.is_empty() { + map.extend(map2.into_iter()); + } } #[rhai_fn(name = "+")] pub fn merge(map1: Map, map2: Map) -> Map { - let mut map1 = map1; - map1.extend(map2.into_iter()); - map1 + if map2.is_empty() { + map1 + } else if map1.is_empty() { + map2 + } else { + let mut map1 = map1; + map1.extend(map2.into_iter()); + map1 + } } pub fn fill_with(map: &mut Map, map2: Map) { - map2.into_iter().for_each(|(key, value)| { - map.entry(key).or_insert(value); - }); + if !map2.is_empty() { + if map.is_empty() { + *map = map2; + } else { + map2.into_iter().for_each(|(key, value)| { + map.entry(key).or_insert(value); + }); + } + } } #[rhai_fn(name = "==", return_raw, pure)] pub fn equals( @@ -53,23 +77,22 @@ mod map_functions { if map1.len() != map2.len() { return Ok(false); } - if map1.is_empty() { - return Ok(true); - } - let mut map2 = map2; + if !map1.is_empty() { + let mut map2 = map2; - for (m1, v1) in map1.iter_mut() { - if let Some(v2) = map2.get_mut(m1) { - let equals = ctx - .call_fn_dynamic_raw(OP_EQUALS, true, &mut [v1, v2]) - .map(|v| v.as_bool().unwrap_or(false))?; + for (m1, v1) in map1.iter_mut() { + if let Some(v2) = map2.get_mut(m1) { + let equals = ctx + .call_fn_dynamic_raw(OP_EQUALS, true, &mut [v1, v2]) + .map(|v| v.as_bool().unwrap_or(false))?; - if !equals { + if !equals { + return Ok(false); + } + } else { return Ok(false); } - } else { - return Ok(false); } } @@ -88,11 +111,19 @@ mod map_functions { pub mod indexing { #[rhai_fn(pure)] pub fn keys(map: &mut Map) -> Array { - map.keys().cloned().map(Into::::into).collect() + if map.is_empty() { + Array::new() + } else { + map.keys().cloned().map(Into::::into).collect() + } } #[rhai_fn(pure)] pub fn values(map: &mut Map) -> Array { - map.values().cloned().collect() + if map.is_empty() { + Array::new() + } else { + map.values().cloned().collect() + } } } } diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 4947b620..58a94777 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -66,11 +66,19 @@ mod string_functions { #[rhai_fn(name = "len", get = "len")] pub fn len(string: &str) -> INT { - string.chars().count() as INT + if string.is_empty() { + 0 + } else { + string.chars().count() as INT + } } #[rhai_fn(name = "bytes", get = "bytes")] pub fn bytes(string: &str) -> INT { - string.len() as INT + if string.is_empty() { + 0 + } else { + string.len() as INT + } } pub fn remove(string: &mut ImmutableString, sub_string: ImmutableString) { *string -= sub_string; @@ -80,7 +88,9 @@ mod string_functions { *string -= character; } pub fn clear(string: &mut ImmutableString) { - string.make_mut().clear(); + if !string.is_empty() { + string.make_mut().clear(); + } } pub fn truncate(string: &mut ImmutableString, len: INT) { if len > 0 { @@ -88,7 +98,7 @@ mod string_functions { let copy = string.make_mut(); copy.clear(); copy.extend(chars.into_iter().take(len as usize)); - } else { + } else if !string.is_empty() { string.make_mut().clear(); } } @@ -99,11 +109,14 @@ mod string_functions { *string = trimmed.to_string().into(); } } - #[rhai_fn(return_raw)] - pub fn pop(string: &mut ImmutableString) -> Result> { - match string.make_mut().pop() { - Some(c) => Ok(c.into()), - None => EvalAltResult::ErrorStringBounds(0, 0, Position::NONE).into(), + pub fn pop(string: &mut ImmutableString) -> Dynamic { + if string.is_empty() { + Dynamic::UNIT + } else { + match string.make_mut().pop() { + Some(c) => c.into(), + None => Dynamic::UNIT, + } } } #[rhai_fn(name = "pop")] @@ -112,7 +125,7 @@ mod string_functions { string: &mut ImmutableString, len: INT, ) -> ImmutableString { - if len <= 0 { + if string.is_empty() || len <= 0 { return ctx.engine().empty_string.clone(); } @@ -128,17 +141,29 @@ mod string_functions { chars.into_iter().rev().collect::().into() } - pub fn to_upper(string: &str) -> ImmutableString { - string.to_uppercase().into() + pub fn to_upper(string: ImmutableString) -> ImmutableString { + if string.is_empty() { + string + } else { + string.to_uppercase().into() + } } pub fn make_upper(string: &mut ImmutableString) { - *string = to_upper(string); + if !string.is_empty() { + *string = string.to_uppercase().into(); + } } - pub fn to_lower(string: &str) -> ImmutableString { - string.to_lowercase().into() + pub fn to_lower(string: ImmutableString) -> ImmutableString { + if string.is_empty() { + string + } else { + string.to_lowercase().into() + } } pub fn make_lower(string: &mut ImmutableString) { - *string = to_lower(string); + if !string.is_empty() { + *string = string.to_lowercase().into(); + } } #[rhai_fn(name = "to_upper")] @@ -174,6 +199,10 @@ mod string_functions { #[rhai_fn(name = "index_of")] pub fn index_of_char_starting_from(string: &str, character: char, start: INT) -> INT { + if string.is_empty() { + return -1; + } + let start = if start < 0 { if let Some(n) = start.checked_abs() { let chars: Vec<_> = string.chars().collect(); @@ -209,13 +238,21 @@ mod string_functions { } #[rhai_fn(name = "index_of")] pub fn index_of_char(string: &str, character: char) -> INT { - string - .find(character) - .map(|index| string[0..index].chars().count() as INT) - .unwrap_or(-1 as INT) + if string.is_empty() { + -1 + } else { + string + .find(character) + .map(|index| string[0..index].chars().count() as INT) + .unwrap_or(-1 as INT) + } } #[rhai_fn(name = "index_of")] pub fn index_of_string_starting_from(string: &str, find_string: &str, start: INT) -> INT { + if string.is_empty() { + return -1; + } + let start = if start < 0 { if let Some(n) = start.checked_abs() { let chars = string.chars().collect::>(); @@ -251,10 +288,14 @@ mod string_functions { } #[rhai_fn(name = "index_of")] pub fn index_of(string: &str, find_string: &str) -> INT { - string - .find(find_string) - .map(|index| string[0..index].chars().count() as INT) - .unwrap_or(-1 as INT) + if string.is_empty() { + -1 + } else { + string + .find(find_string) + .map(|index| string[0..index].chars().count() as INT) + .unwrap_or(-1 as INT) + } } pub fn sub_string( @@ -263,6 +304,10 @@ mod string_functions { start: INT, len: INT, ) -> ImmutableString { + if string.is_empty() { + return ctx.engine().empty_string.clone(); + } + let mut chars = StaticVec::with_capacity(string.len()); let offset = if string.is_empty() || len <= 0 { @@ -308,12 +353,20 @@ mod string_functions { string: &str, start: INT, ) -> ImmutableString { - let len = string.len() as INT; - sub_string(ctx, string, start, len) + if string.is_empty() { + ctx.engine().empty_string.clone() + } else { + let len = string.len() as INT; + sub_string(ctx, string, start, len) + } } #[rhai_fn(name = "crop")] pub fn crop(string: &mut ImmutableString, start: INT, len: INT) { + if string.is_empty() { + return; + } + let mut chars = StaticVec::with_capacity(string.len()); let offset = if string.is_empty() || len <= 0 { @@ -358,7 +411,9 @@ mod string_functions { #[rhai_fn(name = "replace")] pub fn replace(string: &mut ImmutableString, find_string: &str, substitute_string: &str) { - *string = string.replace(find_string, substitute_string).into(); + if !string.is_empty() { + *string = string.replace(find_string, substitute_string).into(); + } } #[rhai_fn(name = "replace")] pub fn replace_string_with_char( @@ -366,9 +421,11 @@ mod string_functions { find_string: &str, substitute_character: char, ) { - *string = string - .replace(find_string, &substitute_character.to_string()) - .into(); + if !string.is_empty() { + *string = string + .replace(find_string, &substitute_character.to_string()) + .into(); + } } #[rhai_fn(name = "replace")] pub fn replace_char_with_string( @@ -376,9 +433,11 @@ mod string_functions { find_character: char, substitute_string: &str, ) { - *string = string - .replace(&find_character.to_string(), substitute_string) - .into(); + if !string.is_empty() { + *string = string + .replace(&find_character.to_string(), substitute_string) + .into(); + } } #[rhai_fn(name = "replace")] pub fn replace_char( @@ -386,12 +445,14 @@ mod string_functions { find_character: char, substitute_character: char, ) { - *string = string - .replace( - &find_character.to_string(), - &substitute_character.to_string(), - ) - .into(); + if !string.is_empty() { + *string = string + .replace( + &find_character.to_string(), + &substitute_character.to_string(), + ) + .into(); + } } #[rhai_fn(return_raw)] @@ -401,6 +462,10 @@ mod string_functions { len: INT, character: char, ) -> Result<(), Box> { + if len <= 0 { + return Ok(()); + } + let _ctx = ctx; // Check if string will be over max size limit @@ -413,26 +478,23 @@ mod string_functions { .into(); } - if len > 0 { - let orig_len = string.chars().count(); + let orig_len = string.chars().count(); - if len as usize > orig_len { - let p = string.make_mut(); + if len as usize > orig_len { + let p = string.make_mut(); - for _ in 0..(len as usize - orig_len) { - p.push(character); - } + for _ in 0..(len as usize - orig_len) { + p.push(character); + } - #[cfg(not(feature = "unchecked"))] - if _ctx.engine().max_string_size() > 0 - && string.len() > _ctx.engine().max_string_size() - { - return crate::EvalAltResult::ErrorDataTooLarge( - "Length of string".to_string(), - crate::Position::NONE, - ) - .into(); - } + #[cfg(not(feature = "unchecked"))] + if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size() + { + return crate::EvalAltResult::ErrorDataTooLarge( + "Length of string".to_string(), + crate::Position::NONE, + ) + .into(); } } @@ -445,6 +507,10 @@ mod string_functions { len: INT, padding: &str, ) -> Result<(), Box> { + if len <= 0 { + return Ok(()); + } + let _ctx = ctx; // Check if string will be over max size limit @@ -457,33 +523,30 @@ mod string_functions { .into(); } - if len > 0 { - let mut str_len = string.chars().count(); - let padding_len = padding.chars().count(); + let mut str_len = string.chars().count(); + let padding_len = padding.chars().count(); - if len as usize > str_len { - let p = string.make_mut(); + if len as usize > str_len { + let p = string.make_mut(); - while str_len < len as usize { - if str_len + padding_len <= len as usize { - p.push_str(padding); - str_len += padding_len; - } else { - p.extend(padding.chars().take(len as usize - str_len)); - str_len = len as usize; - } + while str_len < len as usize { + if str_len + padding_len <= len as usize { + p.push_str(padding); + str_len += padding_len; + } else { + p.extend(padding.chars().take(len as usize - str_len)); + str_len = len as usize; } + } - #[cfg(not(feature = "unchecked"))] - if _ctx.engine().max_string_size() > 0 - && string.len() > _ctx.engine().max_string_size() - { - return crate::EvalAltResult::ErrorDataTooLarge( - "Length of string".to_string(), - crate::Position::NONE, - ) - .into(); - } + #[cfg(not(feature = "unchecked"))] + if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size() + { + return crate::EvalAltResult::ErrorDataTooLarge( + "Length of string".to_string(), + crate::Position::NONE, + ) + .into(); } } @@ -496,7 +559,11 @@ mod string_functions { #[rhai_fn(name = "split")] pub fn chars(string: &str) -> Array { - string.chars().map(Into::::into).collect() + if string.is_empty() { + Array::new() + } else { + string.chars().map(Into::::into).collect() + } } #[rhai_fn(name = "split")] pub fn split_at(ctx: NativeCallContext, string: ImmutableString, start: INT) -> Array {