feat(napi): string latin1 support

This commit is contained in:
LongYinan 2020-09-03 19:11:30 +08:00
parent dfb7518cac
commit 371cc5eab0
No known key found for this signature in database
GPG key ID: A3FFE134A3E20881
11 changed files with 98 additions and 24 deletions

View file

@ -158,7 +158,7 @@ yarn test
| [napi_create_bigint_int64](https://nodejs.org/api/n-api.html#n_api_napi_create_bigint_int64) | 6 | v10.7.0 | ✅ |
| [napi_create_bigint_uint64](https://nodejs.org/api/n-api.html#n_api_napi_create_bigint_uint64) | 6 | v10.7.0 | ✅ |
| [napi_create_bigint_words](https://nodejs.org/api/n-api.html#n_api_napi_create_bigint_words) | 6 | v10.7.0 | ✅ |
| [napi_create_string_latin1](https://nodejs.org/api/n-api.html#n_api_napi_create_string_latin1) | 1 | v8.0.0 | ⛔️ |
| [napi_create_string_latin1](https://nodejs.org/api/n-api.html#n_api_napi_create_string_latin1) | 1 | v8.0.0 | |
| [napi_create_string_utf16](https://nodejs.org/api/n-api.html#n_api_napi_create_string_utf16) | 1 | v8.0.0 | ✅ |
| [napi_create_string_utf8](https://nodejs.org/api/n-api.html#n_api_napi_create_string_utf8) | 1 | v8.0.0 | ✅ |
@ -181,12 +181,12 @@ yarn test
| [napi_get_value_external](https://nodejs.org/api/n-api.html#n_api_napi_get_value_external) | 1 | v8.0.0 | ✅ |
| [napi_get_value_int32](https://nodejs.org/api/n-api.html#n_api_napi_get_value_int32) | 1 | v8.0.0 | ✅ |
| [napi_get_value_int64](https://nodejs.org/api/n-api.html#n_api_napi_get_value_int64) | 1 | v8.0.0 | ✅ |
| [napi_get_value_string_latin1](https://nodejs.org/api/n-api.html#n_api_napi_get_value_string_latin1) | 1 | v8.0.0 | ⛔️ |
| [napi_get_value_string_latin1](https://nodejs.org/api/n-api.html#n_api_napi_get_value_string_latin1) | 1 | v8.0.0 | |
| [napi_get_value_string_utf8](https://nodejs.org/api/n-api.html#n_api_napi_get_value_string_utf8) | 1 | v8.0.0 | ✅ |
| [napi_get_value_string_utf16](https://nodejs.org/api/n-api.html#n_api_napi_get_value_string_utf16) | 1 | v8.0.0 | ✅ |
| [napi_get_value_uint32](https://nodejs.org/api/n-api.html#n_api_napi_get_value_uint32) | 1 | v8.0.0 | ✅ |
| [napi_get_boolean](https://nodejs.org/api/n-api.html#n_api_napi_get_boolean) | 1 | v8.0.0 | ✅ |
| [napi_get_global](https://nodejs.org/api/n-api.html#n_api_napi_get_global) | 1 | v8.0.0 | ⛔️ |
| [napi_get_global](https://nodejs.org/api/n-api.html#n_api_napi_get_global) | 1 | v8.0.0 | |
| [napi_get_null](https://nodejs.org/api/n-api.html#n_api_napi_get_null) | 1 | v8.0.0 | ✅ |
| [napi_get_undefined](https://nodejs.org/api/n-api.html#n_api_napi_get_undefined) | 1 | v8.0.0 | ✅ |

View file

@ -13,10 +13,15 @@ edition = "2018"
libuv = ["futures"]
tokio_rt = ["futures", "tokio", "once_cell"]
serde-json = ["serde", "serde_json"]
latin1 = ["encoding_rs"]
[dependencies]
napi-sys = { version = "0.4", path = "../sys" }
[dependencies.encoding_rs]
version = "0.8"
optional = true
[dependencies.futures]
version = "0.3"
optional = true

View file

@ -196,6 +196,20 @@ impl Env {
Ok(JsString::from_raw_unchecked(self.0, raw_value))
}
#[inline]
pub fn create_string_latin1(&self, chars: &[u8]) -> Result<JsString> {
let mut raw_value = ptr::null_mut();
check_status(unsafe {
sys::napi_create_string_latin1(
self.0,
chars.as_ptr() as *const _,
chars.len() as u64,
&mut raw_value,
)
})?;
Ok(JsString::from_raw_unchecked(self.0, raw_value))
}
#[inline]
pub fn create_symbol_from_js_string(&self, description: JsString) -> Result<JsSymbol> {
let mut result = ptr::null_mut();

View file

@ -4,6 +4,9 @@ use std::ptr;
use std::slice;
use std::str;
#[cfg(feature = "latin1")]
use encoding_rs;
use super::Value;
use crate::error::check_status;
use crate::{sys, Error, Result, Status};
@ -13,20 +16,29 @@ pub struct JsString(pub(crate) Value);
impl JsString {
#[inline]
pub fn len(&self) -> Result<usize> {
pub fn utf8_len(&self) -> Result<usize> {
let mut length = 0;
check_status(unsafe {
sys::napi_get_value_string_utf8(self.0.env, self.0.value, ptr::null_mut(), 0, &mut length)
})?;
Ok(length as usize)
}
#[inline]
pub fn latin1_len(&self) -> Result<usize> {
let mut length = 0;
check_status(unsafe {
sys::napi_get_value_string_latin1(self.0.env, self.0.value, ptr::null_mut(), 0, &mut length)
})?;
Ok(length as usize)
}
}
impl JsString {
#[inline]
pub fn get_ref(&self) -> Result<&[u8]> {
pub fn get_utf8(&self) -> Result<&[u8]> {
let mut written_char_count: u64 = 0;
let len = self.len()? + 1;
let len = self.utf8_len()? + 1;
let mut result = Vec::with_capacity(len);
unsafe {
check_status(sys::napi_get_value_string_utf8(
@ -46,9 +58,31 @@ impl JsString {
}
#[inline]
pub fn chars(&self) -> Result<&[char]> {
pub fn get_latin1(&self) -> Result<(&[u8], usize)> {
let mut written_char_count: u64 = 0;
let len = self.len()? + 1;
let len = self.latin1_len()? + 1;
let mut result = Vec::with_capacity(len);
unsafe {
check_status(sys::napi_get_value_string_latin1(
self.0.env,
self.0.value,
result.as_mut_ptr(),
len as u64,
&mut written_char_count,
))?;
let ptr = result.as_ptr();
mem::forget(result);
Ok((
slice::from_raw_parts(ptr as *const u8, written_char_count as usize),
written_char_count as usize,
))
}
}
#[inline]
pub fn get_utf8_chars(&self) -> Result<&[char]> {
let mut written_char_count: u64 = 0;
let len = self.utf8_len()? + 1;
let mut result = Vec::with_capacity(len);
unsafe {
check_status(sys::napi_get_value_string_utf8(
@ -69,13 +103,21 @@ impl JsString {
}
pub fn as_str(&self) -> Result<&str> {
str::from_utf8(self.get_ref()?)
str::from_utf8(self.get_utf8()?)
.map_err(|e| Error::new(Status::GenericFailure, format!("{:?}", e)))
}
#[cfg(feature = "latin1")]
pub fn as_latin1_string(&self) -> Result<String> {
let (latin1_bytes, len) = self.get_latin1()?;
let mut dst_str = unsafe { String::from_utf8_unchecked(vec![0; len * 2 + 1]) };
encoding_rs::mem::convert_latin1_to_str(latin1_bytes, dst_str.as_mut_str());
Ok(dst_str)
}
pub fn get_ref_mut(&mut self) -> Result<&mut [u8]> {
let mut written_char_count: u64 = 0;
let len = self.len()? + 1;
let len = self.utf8_len()? + 1;
let mut result = Vec::with_capacity(len);
unsafe {
check_status(sys::napi_get_value_string_utf8(
@ -100,7 +142,7 @@ impl TryFrom<JsString> for Vec<u16> {
type Error = Error;
fn try_from(value: JsString) -> Result<Vec<u16>> {
let mut result = Vec::with_capacity(value.len()? + 1); // Leave room for trailing null byte
let mut result = Vec::with_capacity(value.utf8_len()? + 1); // Leave room for trailing null byte
unsafe {
let mut written_char_count = 0;

View file

@ -9,7 +9,7 @@ crate-type = ["cdylib"]
[dependencies]
futures = "0.3"
napi = { path = "../napi", features = ["libuv", "tokio_rt", "serde-json"] }
napi = { path = "../napi", features = ["libuv", "tokio_rt", "serde-json", "latin1"] }
napi-derive = { path = "../napi-derive" }
serde = "1"
serde_bytes = "0.11"

View file

@ -1,11 +0,0 @@
# Snapshot report for `test_module/__test__/string.spec.js`
The actual snapshot is saved in `string.spec.js.snap`.
Generated by [AVA](https://avajs.dev).
## should be able to concat string
> Snapshot 1
'JavaScript 🌳 你好 napi + Rust 🦀 string!'

View file

@ -6,3 +6,12 @@ test('should be able to concat string', (t) => {
const fixture = 'JavaScript 🌳 你好 napi'
t.snapshot(bindings.concatString(fixture))
})
test('should be able to concat latin1 string', (t) => {
const fixture = '涽¾DEL'
t.snapshot(bindings.concatLatin1String(fixture))
})
test('should be able to crate latin1 string', (t) => {
t.snapshot(bindings.createLatin1())
})

View file

@ -1,13 +1,28 @@
use napi::{CallContext, JsString, Module, Result};
#[js_function(1)]
pub fn concat_string(ctx: CallContext) -> Result<JsString> {
fn concat_string(ctx: CallContext) -> Result<JsString> {
let in_string = ctx.get::<JsString>(0)?;
let out_string = format!("{} + Rust 🦀 string!", in_string.as_str()?);
ctx.env.create_string_from_std(out_string)
}
#[js_function(1)]
fn concat_latin1_string(ctx: CallContext) -> Result<JsString> {
let in_string = ctx.get::<JsString>(0)?;
let out_string = format!("{} + Rust 🦀 string!", in_string.as_latin1_string()?);
ctx.env.create_string_from_std(out_string)
}
#[js_function]
fn create_latin1(ctx: CallContext) -> Result<JsString> {
let bytes = vec![169, 191];
ctx.env.create_string_latin1(bytes.as_slice())
}
pub fn register_js(module: &mut Module) -> Result<()> {
module.create_named_method("concatString", concat_string)?;
module.create_named_method("concatLatin1String", concat_latin1_string)?;
module.create_named_method("createLatin1", create_latin1)?;
Ok(())
}