nodejs/stdlib/
string_decoder.rs1use crate::host::{with_host, JsObj};
11use fusevm::Value;
12use indexmap::IndexMap;
13
14pub fn construct(args: &[Value]) -> Result<Value, String> {
16 let enc = if args.is_empty() {
17 "utf8".to_string()
18 } else {
19 super::arg_str(args, 0)
20 };
21 Ok(with_host(|h| {
22 let mut m = IndexMap::new();
23 m.insert("@@native".into(), h.new_str("StringDecoder"));
24 m.insert("encoding".into(), h.new_str(enc.to_ascii_lowercase()));
25 let empty = h.new_array(Vec::new());
27 m.insert("@@pending".into(), empty);
28 h.new_object(m)
29 }))
30}
31
32fn bytes_of(v: &Value) -> Vec<u8> {
34 with_host(|h| match h.get(v) {
35 Some(JsObj::Object(p)) => {
36 let field = p.get("@@bytes").or_else(|| p.get("@@elems"));
37 match field.and_then(|a| h.get(a)) {
38 Some(JsObj::Array(items)) => items.iter().map(|x| h.to_number(x) as u8).collect(),
39 _ => Vec::new(),
40 }
41 }
42 Some(JsObj::Array(items)) => items.iter().map(|x| h.to_number(x) as u8).collect(),
43 _ => Vec::new(),
44 })
45}
46
47fn encoding_of(recv: &Value) -> String {
48 with_host(|h| match h.get(recv) {
49 Some(JsObj::Object(p)) => p
50 .get("encoding")
51 .map(|v| h.str_of(v))
52 .unwrap_or_else(|| "utf8".into()),
53 _ => "utf8".into(),
54 })
55}
56
57fn pending_of(recv: &Value) -> Vec<u8> {
58 with_host(|h| match h.get(recv) {
59 Some(JsObj::Object(p)) => match p.get("@@pending").and_then(|a| h.get(a)) {
60 Some(JsObj::Array(items)) => items.iter().map(|x| h.to_number(x) as u8).collect(),
61 _ => Vec::new(),
62 },
63 _ => Vec::new(),
64 })
65}
66
67fn set_pending(recv: &Value, bytes: &[u8]) {
68 with_host(|h| {
69 let arr = h.new_array(bytes.iter().map(|b| Value::Float(*b as f64)).collect());
70 if let Some(JsObj::Object(p)) = h.get_mut(recv) {
71 p.insert("@@pending".into(), arr);
72 }
73 });
74}
75
76pub fn instance_call(recv: &Value, method: &str, args: &[Value]) -> Result<Value, String> {
77 let enc = encoding_of(recv);
78 match method {
79 "write" => {
80 let mut buf = pending_of(recv);
81 buf.extend(bytes_of(&args.first().cloned().unwrap_or(Value::Undef)));
82 let (decoded, tail) = decode(&enc, &buf);
83 set_pending(recv, &tail);
84 Ok(with_host(|h| h.new_str(decoded)))
85 }
86 "end" => {
87 let mut buf = pending_of(recv);
88 if let Some(v) = args.first() {
89 buf.extend(bytes_of(v));
90 }
91 set_pending(recv, &[]);
92 let (mut decoded, tail) = decode(&enc, &buf);
96 if !tail.is_empty() {
97 decoded.push('\u{FFFD}');
98 }
99 Ok(with_host(|h| h.new_str(decoded)))
100 }
101 _ => Err(crate::host::type_error(&format!(
102 "{method} is not a function"
103 ))),
104 }
105}
106
107fn decode(enc: &str, buf: &[u8]) -> (String, Vec<u8>) {
111 match enc {
112 "ascii" | "latin1" | "binary" => (buf.iter().map(|b| *b as char).collect(), Vec::new()),
113 "hex" => (super::to_hex(buf), Vec::new()),
114 "base64" | "base64url" => (super::to_base64(buf), Vec::new()),
115 _ => {
117 let split = incomplete_utf8_tail(buf);
118 let (head, tail) = buf.split_at(buf.len() - split);
119 (String::from_utf8_lossy(head).into_owned(), tail.to_vec())
120 }
121 }
122}
123
124fn incomplete_utf8_tail(buf: &[u8]) -> usize {
126 let mut i = buf.len();
128 let mut cont = 0;
129 while i > 0 && buf[i - 1] & 0b1100_0000 == 0b1000_0000 && cont < 3 {
130 i -= 1;
131 cont += 1;
132 }
133 if i == 0 {
134 return 0;
135 }
136 let lead = buf[i - 1];
137 let needed = if lead & 0b1000_0000 == 0 {
138 1
139 } else if lead & 0b1110_0000 == 0b1100_0000 {
140 2
141 } else if lead & 0b1111_0000 == 0b1110_0000 {
142 3
143 } else if lead & 0b1111_1000 == 0b1111_0000 {
144 4
145 } else {
146 1
147 };
148 if cont + 1 >= needed {
150 0
151 } else {
152 cont + 1
153 }
154}