diff --git a/examples/function/src/lib.rs b/examples/function/src/lib.rs index 1a6cd3c6..e50f9196 100644 --- a/examples/function/src/lib.rs +++ b/examples/function/src/lib.rs @@ -4,20 +4,20 @@ use node_bindgen::core::NjError; #[node_bindgen()] -fn hello(count: i32) -> String { +fn hello(count: i32) -> String { format!("hello world {}", count) } #[node_bindgen] -fn sum(first: i32, second: i32) -> i32 { +fn sum(first: i32, second: i32) -> i32 { first + second } // throw error if first > second, otherwise return sum #[node_bindgen] -fn min_max(first: i32, second: i32) -> Result { +fn min_max(first: i32, second: i32) -> Result { if first > second { Err(NjError::Other("first arg is greater".to_owned())) } else { @@ -26,14 +26,14 @@ fn min_max(first: i32, second: i32) -> Result { } #[node_bindgen(name="multiply")] -fn mul(first: i32,second: i32) -> i32 { - first * second +fn mul(first: i32,second: i32) -> i32 { + first * second } -/// add second if supplied +/// add second if supplied #[node_bindgen()] -fn sum2(first: i32, second_arg: Option) -> i32 { +fn sum2(first: i32, second_arg: Option) -> i32 { if let Some(second) = second_arg { first + second } else { @@ -41,3 +41,11 @@ fn sum2(first: i32, second_arg: Option) -> i32 { } } +#[node_bindgen()] +fn string(first: String, second_arg: Option) -> String { + if let Some(second) = second_arg { + format!("{} {}", first, second) + } else { + first + } +} diff --git a/examples/function/test.js b/examples/function/test.js index 3c033f02..79e73716 100644 --- a/examples/function/test.js +++ b/examples/function/test.js @@ -1,30 +1,77 @@ +const crypto = require('crypto'); + let addon =require('./dist'); const assert = require('assert'); -assert.equal(addon.hello(2),"hello world 2"); - +assert.strictEqual(addon.hello(2),"hello world 2"); assert.throws( () => addon.hello("hello"),{ message: 'invalid type, expected: number, actual: string' }); - - assert.throws(() => addon.hello(),{ message: 'expected argument of type: i32' -}); +}); -assert.equal(addon.sum(1,2),3); +assert.strictEqual(addon.sum(1,2),3); assert.throws( () => addon.minMax(10,0),{ - message: 'first arg is greater', + message: 'first arg is greater', }); -assert.equal(addon.minMax(1,2),3); +assert.strictEqual(addon.minMax(1,2),3); + +assert.strictEqual(addon.multiply(2,5),10); + +assert.strictEqual(addon.sum2(10),10); +assert.strictEqual(addon.sum2(5,100),105); + +const stringShort = _generateForCustomCharacters(5); +const stringMedium = _generateForCustomCharacters(100); +const stringLong = _generateForCustomCharacters(2000); +const strings = new Set([stringShort, stringMedium, stringLong]); + +assert.strictEqual(addon.string(stringShort), stringShort); +assert.strictEqual(addon.string(stringMedium), stringMedium); +assert.strictEqual(addon.string(stringLong), stringLong); + +for(const string1 in strings) { + for(const string2 in strings) { + assert.strictEqual(addon.string(string1), string2); + } +} + +console.log("function tests succeed"); + +/* + * attribution: https://github.com/sindresorhus/crypto-random-string + * MIT License + * Copyright (c) Sindre Sorhus (sindresorhus.com) + */ +function _generateForCustomCharacters(length, characters) { + characters = characters || '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'.split(''); + // Generating entropy is faster than complex math operations, so we use the simplest way + const characterCount = characters.length; + const maxValidSelector = (Math.floor(0x10000 / characterCount) * characterCount) - 1; // Using values above this will ruin distribution when using modular division + const entropyLength = 2 * Math.ceil(1.1 * length); // Generating a bit more than required so chances we need more than one pass will be really low + let string = ''; + let stringLength = 0; + + while (stringLength < length) { // In case we had many bad values, which may happen for character sets of size above 0x8000 but close to it + const entropy = crypto.randomBytes(entropyLength); + let entropyPosition = 0; -assert.equal(addon.multiply(2,5),10); + while (entropyPosition < entropyLength && stringLength < length) { + const entropyValue = entropy.readUInt16LE(entropyPosition); + entropyPosition += 2; + if (entropyValue > maxValidSelector) { // Skip values which will ruin distribution when using modular division + continue; + } -assert.equal(addon.sum2(10),10); -assert.equal(addon.sum2(5,100),105); + string += characters[entropyValue % characterCount]; + stringLength++; + } + } -console.log("function tests succeed"); \ No newline at end of file + return string; +} diff --git a/nj-core/src/convert.rs b/nj-core/src/convert.rs index f8062466..f420d222 100644 --- a/nj-core/src/convert.rs +++ b/nj-core/src/convert.rs @@ -1,4 +1,5 @@ use libc::size_t; +use std::ptr; use crate::sys::napi_value; use crate::val::JsEnv; @@ -10,14 +11,14 @@ use crate::napi_call_result; /// convert to JS object pub trait TryIntoJs { - fn try_to_js(self,js_env: &JsEnv) -> Result ; - + fn try_to_js(self,js_env: &JsEnv) -> Result ; + } impl TryIntoJs for bool { fn try_to_js(self, js_env: &JsEnv) -> Result { js_env.create_boolean(self) - } + } } impl TryIntoJs for f64 { @@ -81,7 +82,7 @@ impl TryIntoJs for napi_value { impl TryIntoJs for Vec where T: TryIntoJs { fn try_to_js(self, js_env: &JsEnv) -> Result { - + let array = js_env.create_array_with_len(self.len())?; for (i,element) in self.into_iter().enumerate() { let js_element = element.try_to_js(js_env)?; @@ -89,7 +90,7 @@ impl TryIntoJs for Vec where T: TryIntoJs { } Ok(array) - } + } } @@ -103,7 +104,7 @@ pub trait IntoJs { /// Convert napi value to Rust value -/// +/// pub trait JSValue<'a>: Sized { fn label() -> &'static str { @@ -130,7 +131,7 @@ impl JSValue<'_> for f64 { } impl JSValue<'_> for i32 { - + fn convert_to_rust(env: &JsEnv,js_value: napi_value) -> Result { @@ -147,7 +148,7 @@ impl JSValue<'_> for i32 { } impl JSValue<'_> for u32 { - + fn convert_to_rust(env: &JsEnv,js_value: napi_value) -> Result { @@ -165,7 +166,7 @@ impl JSValue<'_> for u32 { impl JSValue<'_> for i64 { - + fn convert_to_rust(env: &JsEnv,js_value: napi_value) -> Result { @@ -208,14 +209,23 @@ impl JSValue<'_> for String { use crate::sys::napi_get_value_string_utf8; - let mut chars: [u8; 1024] = [0;1024]; - let mut size: size_t = 0; + let mut string_size: size_t = 0; + + napi_call_result!( + napi_get_value_string_utf8(env.inner(),js_value,ptr::null_mut(),0,&mut string_size) + )?; + + string_size += 1; + + let chars_vec: Vec = vec![0; string_size]; + let mut chars: Box<[u8]> = chars_vec.into_boxed_slice(); + let mut read_size: size_t = 0; napi_call_result!( - napi_get_value_string_utf8(env.inner(),js_value,chars.as_mut_ptr() as *mut i8,1024,&mut size) + napi_get_value_string_utf8(env.inner(),js_value,chars.as_mut_ptr() as *mut i8,string_size,&mut read_size) )?; - let my_chars: Vec = chars[0..size].into(); + let my_chars: Vec = chars[0..read_size].into(); String::from_utf8(my_chars).map_err(|err| err.into()) } @@ -226,12 +236,12 @@ impl JSValue<'_> for String { impl <'a,T>JSValue<'a> for Vec where T: JSValue<'a> { fn convert_to_rust(env: &'a JsEnv,js_value: napi_value) -> Result { - + if !env.is_array(js_value)? { return Err(NjError::Other("not array".to_owned())); } - - + + use crate::sys::napi_get_array_length; let mut length: u32 = 0; @@ -249,4 +259,4 @@ impl <'a,T>JSValue<'a> for Vec where T: JSValue<'a> { Ok(elements) } -} \ No newline at end of file +}