Skip to content

Commit

Permalink
working!
Browse files Browse the repository at this point in the history
  • Loading branch information
nicarq committed Aug 9, 2024
1 parent 4babfed commit 37207ec
Show file tree
Hide file tree
Showing 12 changed files with 856 additions and 111 deletions.
49 changes: 49 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion libs/shinkai-tools-runner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@ futures = "0.3.30"
nanoid = "0.4.0"
patch = "0.7.0"
reqwest = { version = "0.11.26", features = ["blocking"] }
rquickjs = { version = "0.6.2", features = ["full-async", "futures", "macro"] }
hex-simd = "0.8.0"
base64-simd = "0.8.0"
phf = { version = "0.11.2", features = ["macros"] }
rquickjs = { version = "0.6.2", features = [
"full-async",
"futures",
"macro",
"array-buffer",
] }
serde = { version = "1.0.188", features = ["derive"] }
serde_json = "1.0.117"
tokio = { version = "1.36.0", features = ["full"] }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ use rquickjs::{Ctx, Result};
mod console;
mod fetch;
mod timers;
mod text_encoder_decoder;
mod text_encoder;
mod text_decoder;
mod utils;

pub fn init_globals(ctx: &Ctx<'_>) -> Result<()> {
console::init(ctx)?;
fetch::init(ctx)?;
timers::init(ctx)?;
text_encoder_decoder::init(ctx)?;
text_encoder::init(ctx)?;
text_decoder::init(ctx)?;
Ok(())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use rquickjs::{function::Opt, Class, Ctx, Object, Result, Value};

use super::utils::{bytes::get_bytes, encoding::Encoder, object::ObjectExt, result::ResultExt};

#[rquickjs::class]
#[derive(rquickjs::class::Trace)]
pub struct TextDecoder {
#[qjs(skip_trace)]
encoder: Encoder,
fatal: bool,
ignore_bom: bool,
}

#[rquickjs::methods]
impl<'js> TextDecoder {
#[qjs(constructor)]
pub fn new(ctx: Ctx<'js>, label: Opt<String>, options: Opt<Object<'js>>) -> Result<Self> {
let encoding = label
.0
.filter(|lbl| !lbl.is_empty())
.unwrap_or_else(|| String::from("utf-8"));
let mut fatal = false;
let mut ignore_bom = false;

let encoder = Encoder::from_str(&encoding).or_throw_range(&ctx, None)?;

if let Some(options) = options.0 {
if let Some(opt) = options.get_optional("fatal")? {
fatal = opt;
}
if let Some(opt) = options.get_optional("ignoreBOM")? {
ignore_bom = opt;
}
}

Ok(TextDecoder {
encoder,
fatal,
ignore_bom,
})
}

#[qjs(get)]
fn encoding(&self) -> &str {
self.encoder.as_label()
}

#[qjs(get)]
fn fatal(&self) -> bool {
self.fatal
}

#[qjs(get, rename = "ignoreBOM")]
fn ignore_bom(&self) -> bool {
self.ignore_bom
}

pub fn decode(&self, ctx: Ctx<'js>, buffer: Value<'js>) -> Result<String> {
let bytes = get_bytes(&ctx, buffer)?;
let start_pos = if !self.ignore_bom {
match bytes.get(..3) {
Some([0xFF, 0xFE, ..]) | Some([0xFE, 0xFF, ..]) => 2,
Some([0xEF, 0xBB, 0xBF]) => 3,
_ => 0,
}
} else {
0
};

self.encoder
.encode_to_string(&bytes[start_pos..], !self.fatal)
.or_throw_type(&ctx, None)
}
}

pub fn init(ctx: &Ctx<'_>) -> Result<()> {
let globals = ctx.globals();
Class::<TextDecoder>::define(&globals)?;
Ok(())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// https://github.com/awslabs/llrt/blob/main/llrt_core/src/modules/

use rquickjs::{class::Trace, Array, Class, Ctx, Result, Value};
use rquickjs::{function::Opt, Exception, Object, TypedArray};

use super::utils::result::ResultExt;

#[derive(Trace)]
#[rquickjs::class]
pub struct TextEncoder {}

#[rquickjs::methods(rename_all = "camelCase")]
impl TextEncoder {
#[qjs(constructor)]
pub fn new() -> Self {
Self {}
}

#[qjs(get)]
fn encoding(&self) -> &str {
"utf-8"
}

pub fn encode<'js>(&self, ctx: Ctx<'js>, string: Opt<Value<'js>>) -> Result<Value<'js>> {
if let Some(string) = string.0 {
if let Some(string) = string.as_string() {
let string = string.to_string()?;
eprintln!("String to encode: {}", string);
return TypedArray::new(ctx.clone(), string.as_bytes())
.map(|m: TypedArray<'_, u8>| m.into_value());
} else if !string.is_undefined() {
eprintln!("The \"string\" argument must be a string.");
return Err(Exception::throw_message(
&ctx,
"The \"string\" argument must be a string.",
));
}
}

eprintln!("Encoding empty string");
TypedArray::new(ctx.clone(), []).map(|m: TypedArray<'_, u8>| m.into_value())
}

pub fn encode_into<'js>(
&self,
ctx: Ctx<'js>,
src: String,
dst: Value<'js>,
) -> Result<Object<'js>> {
if let Ok(typed_array) = TypedArray::<u8>::from_value(dst) {
let dst_length = typed_array.len();
let dst_offset: usize = typed_array.get("byteOffset")?;
let array_buffer = typed_array.arraybuffer()?;
let raw = array_buffer
.as_raw()
.ok_or("ArrayBuffer is detached")
.or_throw(&ctx)?;

let dst = unsafe {
std::slice::from_raw_parts_mut(raw.ptr.as_ptr().add(dst_offset), dst_length)
};

let mut written = 0;
let dst_len = dst.len();
for ch in src.chars() {
let len = ch.len_utf8();
if written + len > dst_len {
break;
}
written += len;
}
dst[..written].copy_from_slice(&src.as_bytes()[..written]);
let read: usize = src[..written].chars().map(char::len_utf16).sum();

let obj = Object::new(ctx)?;
obj.set("read", read)?;
obj.set("written", written)?;
Ok(obj)
} else {
Err(Exception::throw_type(
&ctx,
"The \"dest\" argument must be an instance of Uint8Array.",
))
}
}
}

pub fn init(ctx: &Ctx<'_>) -> Result<()> {
let globals = ctx.globals();
Class::<TextEncoder>::define(&globals)?;
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
use rquickjs::{Context, Runtime, Value};
use crate::tools::quickjs_runtime::context_globals::text_decoder;
use std::error::Error;

#[test]
fn test_text_encoder_decoder() -> std::result::Result<(), Box<dyn Error>> {
let runtime = Runtime::new()?;
let ctx = Context::full(&runtime)?;

ctx.with(|ctx| {
// Initialize the TextEncoder and TextDecoder classes
init(&ctx)?;
text_decoder::init(&ctx)?;

let result: Value<'_> = ctx.eval(
r#"
let encoder = new TextEncoder();
let decoder = new TextDecoder();
let buffer = encoder.encode('hello');
decoder.decode(buffer) == 'hello';
"#,
)?;

assert!(result.as_bool().unwrap());
Ok::<(), Box<dyn Error>>(())
})?;
Ok(())
}
}
Loading

0 comments on commit 37207ec

Please sign in to comment.