Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement dynamic string size. #110

Merged
merged 2 commits into from
Dec 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions examples/function/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<i32,NjError> {
fn min_max(first: i32, second: i32) -> Result<i32,NjError> {
if first > second {
Err(NjError::Other("first arg is greater".to_owned()))
} else {
Expand All @@ -26,18 +26,26 @@ fn min_max(first: i32, second: i32) -> Result<i32,NjError> {
}

#[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>) -> i32 {
fn sum2(first: i32, second_arg: Option<i32>) -> i32 {
if let Some(second) = second_arg {
first + second
} else {
first
}
}

#[node_bindgen()]
fn string(first: String, second_arg: Option<String>) -> String {
if let Some(second) = second_arg {
format!("{} {}", first, second)
} else {
first
}
}
71 changes: 59 additions & 12 deletions examples/function/test.js
Original file line number Diff line number Diff line change
@@ -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 <[email protected]> (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");
return string;
}
44 changes: 27 additions & 17 deletions nj-core/src/convert.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use libc::size_t;
use std::ptr;

use crate::sys::napi_value;
use crate::val::JsEnv;
Expand All @@ -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<napi_value,NjError> ;
fn try_to_js(self,js_env: &JsEnv) -> Result<napi_value,NjError> ;

}

impl TryIntoJs for bool {
fn try_to_js(self, js_env: &JsEnv) -> Result<napi_value,NjError> {
js_env.create_boolean(self)
}
}
}

impl TryIntoJs for f64 {
Expand Down Expand Up @@ -81,15 +82,15 @@ impl TryIntoJs for napi_value {

impl <T>TryIntoJs for Vec<T> where T: TryIntoJs {
fn try_to_js(self, js_env: &JsEnv) -> Result<napi_value,NjError> {

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)?;
js_env.set_element(array, js_element, i)?;
}

Ok(array)
}
}
}


Expand All @@ -103,7 +104,7 @@ pub trait IntoJs {


/// Convert napi value to Rust value
///
///
pub trait JSValue<'a>: Sized {

fn label() -> &'static str {
Expand All @@ -130,7 +131,7 @@ impl JSValue<'_> for f64 {
}

impl JSValue<'_> for i32 {


fn convert_to_rust(env: &JsEnv,js_value: napi_value) -> Result<Self,NjError> {

Expand All @@ -147,7 +148,7 @@ impl JSValue<'_> for i32 {
}

impl JSValue<'_> for u32 {


fn convert_to_rust(env: &JsEnv,js_value: napi_value) -> Result<Self,NjError> {

Expand All @@ -165,7 +166,7 @@ impl JSValue<'_> for u32 {


impl JSValue<'_> for i64 {


fn convert_to_rust(env: &JsEnv,js_value: napi_value) -> Result<Self,NjError> {

Expand Down Expand Up @@ -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<u8> = 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<u8> = chars[0..size].into();
let my_chars: Vec<u8> = chars[0..read_size].into();

String::from_utf8(my_chars).map_err(|err| err.into())
}
Expand All @@ -226,12 +236,12 @@ impl JSValue<'_> for String {
impl <'a,T>JSValue<'a> for Vec<T> where T: JSValue<'a> {

fn convert_to_rust(env: &'a JsEnv,js_value: napi_value) -> Result<Self,NjError> {

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;
Expand All @@ -249,4 +259,4 @@ impl <'a,T>JSValue<'a> for Vec<T> where T: JSValue<'a> {

Ok(elements)
}
}
}