pub(crate) fn get_external_references() -> Vec<v8::ExternalReference> {
vec![
v8::ExternalReference {
function: v8::MapFnTo::map_fn_to(text_encoder_encode),
},
v8::ExternalReference {
function: v8::MapFnTo::map_fn_to(text_encoder_encode_into),
},
v8::ExternalReference {
function: v8::MapFnTo::map_fn_to(text_decoder_decode),
},
]
}
pub(crate) fn register_bindings(scope: &mut v8::PinScope, bindings: v8::Local<v8::Object>) {
let name = v8::String::new(scope, "textEncoderEncode").unwrap();
let value = v8::Function::new(scope, text_encoder_encode).unwrap();
bindings.set(scope, name.into(), value.into());
let name = v8::String::new(scope, "textEncoderEncodeInto").unwrap();
let value = v8::Function::new(scope, text_encoder_encode_into).unwrap();
bindings.set(scope, name.into(), value.into());
let name = v8::String::new(scope, "textDecoderDecode").unwrap();
let value = v8::Function::new(scope, text_decoder_decode).unwrap();
bindings.set(scope, name.into(), value.into());
}
fn text_encoder_encode(
scope: &mut v8::PinScope,
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
if !crate::error::check_arg_count(scope, &args, 1, "TextEncoder.encode") {
return;
}
let input = args.get(0);
let input_str = {
v8::tc_scope!(let tc, scope);
match input.to_string(tc) {
Some(s) => s.to_rust_string_lossy(tc),
None => String::new(),
}
};
let bytes = input_str.as_bytes().to_vec();
let backing_store = v8::ArrayBuffer::new_backing_store_from_vec(bytes).make_shared();
let array_buffer = v8::ArrayBuffer::with_backing_store(scope, &backing_store);
let uint8_array = v8::Uint8Array::new(scope, array_buffer, 0, backing_store.len()).unwrap();
rv.set(uint8_array.into());
}
fn text_encoder_encode_into(
scope: &mut v8::PinScope,
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
if !crate::error::check_arg_count(scope, &args, 2, "TextEncoder.encodeInto") {
return;
}
let input = args.get(0);
let destination = args.get(1);
let input_str = {
v8::tc_scope!(let tc, scope);
match input.to_string(tc) {
Some(s) => s.to_rust_string_lossy(tc),
None => String::new(),
}
};
if !destination.is_uint8_array() {
crate::error::throw_type_error(scope, "Destination must be a Uint8Array");
return;
}
let uint8_array = v8::Local::<v8::Uint8Array>::try_from(destination).unwrap();
let byte_length = uint8_array.byte_length();
let data = uint8_array.data();
let bytes = input_str.as_bytes();
let bytes_to_copy = bytes.len().min(byte_length);
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), data as *mut u8, bytes_to_copy);
}
let mut chars_read = 0;
let mut bytes_written = 0;
for ch in input_str.chars() {
let char_len = ch.len_utf8();
if bytes_written + char_len <= byte_length {
chars_read += ch.len_utf16();
bytes_written += char_len;
} else {
break;
}
}
let result = v8::Object::new(scope);
let read_key = v8::String::new(scope, "read").unwrap();
let read_value = v8::Number::new(scope, chars_read as f64);
result.set(scope, read_key.into(), read_value.into());
let written_key = v8::String::new(scope, "written").unwrap();
let written_value = v8::Number::new(scope, bytes_written as f64);
result.set(scope, written_key.into(), written_value.into());
rv.set(result.into());
}
fn text_decoder_decode(
scope: &mut v8::PinScope,
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
if args.length() == 0 {
let result = v8::String::new(scope, "").unwrap();
rv.set(result.into());
return;
}
let input = args.get(0);
if input.is_null_or_undefined() {
let result = v8::String::new(scope, "").unwrap();
rv.set(result.into());
return;
}
let bytes = if input.is_array_buffer_view() {
let Some(view) = v8::Local::<v8::ArrayBufferView>::try_from(input).ok() else {
crate::error::throw_type_error(scope, "Failed to convert to ArrayBufferView");
return;
};
let byte_length = view.byte_length();
if byte_length == 0 {
&[]
} else {
let data = view.data();
unsafe { std::slice::from_raw_parts(data as *const u8, byte_length) }
}
} else if input.is_array_buffer() {
let Some(array_buffer) = v8::Local::<v8::ArrayBuffer>::try_from(input).ok() else {
crate::error::throw_type_error(scope, "Failed to convert to ArrayBuffer");
return;
};
let backing_store = array_buffer.get_backing_store();
let byte_length = array_buffer.byte_length();
if byte_length == 0 {
&[]
} else {
match backing_store.data() {
Some(data) => unsafe {
std::slice::from_raw_parts(data.as_ptr() as *const u8, byte_length)
},
None => {
&[]
}
}
}
} else {
crate::error::throw_type_error(scope, "Input must be an ArrayBuffer or ArrayBufferView");
return;
};
let decoded = match std::str::from_utf8(bytes) {
Ok(s) => s,
Err(_) => {
let owned = String::from_utf8_lossy(bytes);
let result = v8::String::new(scope, &owned).unwrap();
rv.set(result.into());
return;
}
};
let result = v8::String::new(scope, decoded).unwrap();
rv.set(result.into());
}