1use std::{rc::Rc, slice};
4
5use half::f16;
6use rquickjs::{
7 atom::PredefinedAtom,
8 class::{Trace, Tracer},
9 function::Constructor,
10 ArrayBuffer, Coerced, Ctx, Error, Exception, FromJs, IntoJs, JsLifetime, Object, Result,
11 TypedArray, U8Clamped, Value,
12};
13
14pub fn get_lossy_string(string_value: Value) -> Result<String> {
21 let js_str = string_value.into_string().ok_or_else(|| Error::FromJs {
22 from: "Value",
23 to: "JSString",
24 message: Some("Value is not a string".into()),
25 })?;
26 let cstr = js_str.to_cstring()?;
27 let bytes = unsafe { slice::from_raw_parts(cstr.as_ptr() as *const u8, cstr.len()) };
28
29 let first = match memchr::memchr(0xED, bytes) {
30 None => return Ok(unsafe { String::from_utf8_unchecked(bytes.to_vec()) }),
31 Some(idx) => idx,
32 };
33 let mut result = String::with_capacity(bytes.len());
34 result.push_str(unsafe { std::str::from_utf8_unchecked(&bytes[..first]) });
35 qjs_substitute_into(&bytes[first..], &mut result);
36 Ok(result)
37}
38
39fn qjs_substitute_into(bytes: &[u8], result: &mut String) {
40 let mut start = 0;
41 while start < bytes.len() {
42 let next_ed = match memchr::memchr(0xED, &bytes[start..]) {
43 None => {
44 result.push_str(unsafe { std::str::from_utf8_unchecked(&bytes[start..]) });
45 return;
46 },
47 Some(rel) => start + rel,
48 };
49 if next_ed > start {
50 result.push_str(unsafe { std::str::from_utf8_unchecked(&bytes[start..next_ed]) });
51 }
52 if next_ed + 3 > bytes.len() {
53 replace_invalid_utf8_and_utf16_into(&bytes[next_ed..], result);
54 return;
55 }
56 let b1 = bytes[next_ed + 1];
57 let b2 = bytes[next_ed + 2];
58 if (b1 & 0xC0) != 0x80 || (b2 & 0xC0) != 0x80 {
59 replace_invalid_utf8_and_utf16_into(&bytes[next_ed..], result);
60 return;
61 }
62 if (b1 & 0xE0) == 0xA0 {
63 result.push('\u{FFFD}');
64 } else {
65 result.push_str(unsafe { std::str::from_utf8_unchecked(&bytes[next_ed..next_ed + 3]) });
66 }
67 start = next_ed + 3;
68 }
69}
70
71#[doc(hidden)]
72pub fn replace_invalid_utf8_and_utf16(bytes: &[u8]) -> String {
73 let err = match simdutf8::compat::from_utf8(bytes) {
74 Ok(s) => return s.to_owned(),
75 Err(e) => e,
76 };
77 let valid_up_to = err.valid_up_to();
78 let mut result = String::with_capacity(bytes.len());
79 result.push_str(unsafe { std::str::from_utf8_unchecked(&bytes[..valid_up_to]) });
80 replace_invalid_utf8_and_utf16_into(&bytes[valid_up_to..], &mut result);
81 result
82}
83
84fn replace_invalid_utf8_and_utf16_into(bytes: &[u8], result: &mut String) {
85 let mut i = 0;
86
87 while i < bytes.len() {
88 let current = bytes[i];
89 match current {
90 0x00..=0x7F => {
91 result.push(current as char);
92 i += 1;
93 },
94 0xC0..=0xDF if i + 1 < bytes.len() => {
95 let next = bytes[i + 1];
96 if (next & 0xC0) == 0x80 {
97 let code_point = ((current as u32 & 0x1F) << 6) | (next as u32 & 0x3F);
98 result.push(char::from_u32(code_point).unwrap_or('\u{FFFD}'));
99 i += 2;
100 } else {
101 result.push('\u{FFFD}');
102 i += 1;
103 }
104 },
105 0xE0..=0xEF if i + 2 < bytes.len() => {
106 let next1 = bytes[i + 1];
107 let next2 = bytes[i + 2];
108 if (next1 & 0xC0) == 0x80 && (next2 & 0xC0) == 0x80 {
109 let code_point = ((current as u32 & 0x0F) << 12)
110 | ((next1 as u32 & 0x3F) << 6)
111 | (next2 as u32 & 0x3F);
112 result.push(char::from_u32(code_point).unwrap_or('\u{FFFD}'));
113 i += 3;
114 } else {
115 result.push('\u{FFFD}');
116 i += 1;
117 }
118 },
119 0xF0..=0xF7 if i + 3 < bytes.len() => {
120 let next1 = bytes[i + 1];
121 let next2 = bytes[i + 2];
122 let next3 = bytes[i + 3];
123 if (next1 & 0xC0) == 0x80 && (next2 & 0xC0) == 0x80 && (next3 & 0xC0) == 0x80 {
124 let code_point = ((current as u32 & 0x07) << 18)
125 | ((next1 as u32 & 0x3F) << 12)
126 | ((next2 as u32 & 0x3F) << 6)
127 | (next3 as u32 & 0x3F);
128 result.push(char::from_u32(code_point).unwrap_or('\u{FFFD}'));
129 i += 4;
130 } else {
131 result.push('\u{FFFD}');
132 i += 1;
133 }
134 },
135 _ => {
136 result.push('\u{FFFD}');
137 i += 1;
138 },
139 }
140 }
141}
142
143#[cfg(test)]
144mod replace_invalid_utf8_tests {
145 use super::replace_invalid_utf8_and_utf16;
146
147 fn cases() -> Vec<(&'static str, Vec<u8>, &'static str)> {
148 vec![
149 ("empty", vec![], ""),
150 ("ascii", b"hello world".to_vec(), "hello world"),
151 (
152 "ascii_with_control",
153 vec![b'a', 0x00, b'b', 0x7f, b'c'],
154 "a\u{0}b\u{7f}c",
155 ),
156 ("two_byte_latin1", vec![0xC3, 0xA9], "\u{00E9}"),
157 ("three_byte_cjk", vec![0xE4, 0xB8, 0x96], "\u{4e16}"),
158 ("four_byte_emoji", vec![0xF0, 0x9F, 0xA6, 0x80], "\u{1f980}"),
159 ("lone_high_surrogate", vec![0xED, 0xA0, 0xBD], "\u{FFFD}"),
160 ("lone_low_surrogate", vec![0xED, 0xB0, 0x80], "\u{FFFD}"),
161 (
162 "surrogate_pair_in_wtf8",
163 vec![0xED, 0xA0, 0xBD, 0xED, 0xB2, 0xA9],
164 "\u{FFFD}\u{FFFD}",
165 ),
166 ("stray_continuation", vec![0x80], "\u{FFFD}"),
167 ("truncated_two_byte", vec![0xC3], "\u{FFFD}"),
168 ("truncated_three_byte", vec![0xE0, 0xA0], "\u{FFFD}\u{FFFD}"),
169 (
170 "truncated_four_byte",
171 vec![0xF0, 0x9F, 0xA6],
172 "\u{FFFD}\u{FFFD}\u{FFFD}",
173 ),
174 (
175 "two_byte_bad_continuation",
176 vec![0xC3, 0x20, b'a'],
177 "\u{FFFD} a",
178 ),
179 (
180 "three_byte_bad_continuation",
181 vec![0xE4, 0xB8, 0x20, b'a'],
182 "\u{FFFD}\u{FFFD} a",
183 ),
184 ("high_byte_above_f7", vec![0xF8, b'a'], "\u{FFFD}a"),
185 (
186 "mixed_valid_and_invalid",
187 {
188 let mut v = b"hello ".to_vec();
189 v.extend_from_slice(&[0xED, 0xA0, 0xBD]);
190 v.extend_from_slice(" world".as_bytes());
191 v
192 },
193 "hello \u{FFFD} world",
194 ),
195 (
196 "long_ascii",
197 b"the quick brown fox jumps over the lazy dog".repeat(20),
198 &*Box::leak(
199 "the quick brown fox jumps over the lazy dog"
200 .repeat(20)
201 .into_boxed_str(),
202 ),
203 ),
204 ]
205 }
206
207 #[test]
208 fn matches_contract() {
209 for (name, input, expected) in cases() {
210 let got = replace_invalid_utf8_and_utf16(&input);
211 assert_eq!(
212 got, expected,
213 "case `{}`: got {:?}, expected {:?}",
214 name, got, expected
215 );
216 }
217 }
218}
219
220use crate::utils::{error_messages::ERROR_MSG_ARRAY_BUFFER_DETACHED, result::ResultExt};
221
222#[derive(Clone, PartialEq)]
223pub enum ObjectBytes<'js> {
224 U8Array(TypedArray<'js, u8>),
225 I8Array(TypedArray<'js, i8>),
226 U16Array(TypedArray<'js, u16>),
227 I16Array(TypedArray<'js, i16>),
228 U32Array(TypedArray<'js, u32>),
229 I32Array(TypedArray<'js, i32>),
230 U64Array(TypedArray<'js, u64>),
231 I64Array(TypedArray<'js, i64>),
232 F16Array(TypedArray<'js, f16>),
233 F32Array(TypedArray<'js, f32>),
234 F64Array(TypedArray<'js, f64>),
235 U8ClampedArray(TypedArray<'js, U8Clamped>),
236 DataView(ArrayBuffer<'js>, usize, usize), Vec(Vec<u8>),
238}
239
240unsafe impl<'js> JsLifetime<'js> for ObjectBytes<'js> {
242 type Changed<'to> = ObjectBytes<'to>;
243}
244
245impl<'js> Trace<'js> for ObjectBytes<'js> {
246 fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
247 match self {
248 ObjectBytes::U8Array(a) => a.trace(tracer),
249 ObjectBytes::I8Array(a) => a.trace(tracer),
250 ObjectBytes::U16Array(a) => a.trace(tracer),
251 ObjectBytes::I16Array(a) => a.trace(tracer),
252 ObjectBytes::U32Array(a) => a.trace(tracer),
253 ObjectBytes::I32Array(a) => a.trace(tracer),
254 ObjectBytes::U64Array(a) => a.trace(tracer),
255 ObjectBytes::I64Array(a) => a.trace(tracer),
256 ObjectBytes::F16Array(a) => a.trace(tracer),
257 ObjectBytes::F32Array(a) => a.trace(tracer),
258 ObjectBytes::F64Array(a) => a.trace(tracer),
259 ObjectBytes::U8ClampedArray(a) => a.trace(tracer),
260 ObjectBytes::DataView(ab, _, _) => ab.trace(tracer),
261 ObjectBytes::Vec(v) => v.trace(tracer),
262 }
263 }
264}
265
266impl<'js> IntoJs<'js> for ObjectBytes<'js> {
267 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
268 match self {
269 ObjectBytes::U8Array(a) => a.into_js(ctx),
270 ObjectBytes::I8Array(a) => a.into_js(ctx),
271 ObjectBytes::U16Array(a) => a.into_js(ctx),
272 ObjectBytes::I16Array(a) => a.into_js(ctx),
273 ObjectBytes::U32Array(a) => a.into_js(ctx),
274 ObjectBytes::I32Array(a) => a.into_js(ctx),
275 ObjectBytes::U64Array(a) => a.into_js(ctx),
276 ObjectBytes::I64Array(a) => a.into_js(ctx),
277 ObjectBytes::F16Array(a) => a.into_js(ctx),
278 ObjectBytes::F32Array(a) => a.into_js(ctx),
279 ObjectBytes::F64Array(a) => a.into_js(ctx),
280 ObjectBytes::U8ClampedArray(a) => a.into_js(ctx),
281 ObjectBytes::DataView(ab, _, _) => {
282 let ctor: Constructor = ctx.globals().get(PredefinedAtom::DataView)?;
283 ctor.construct((ab,))
284 },
285 ObjectBytes::Vec(v) => v.into_js(ctx),
286 }
287 }
288}
289
290impl<'js> TryFrom<ObjectBytes<'js>> for Vec<u8> {
291 type Error = Rc<str>;
292 fn try_from(value: ObjectBytes<'js>) -> std::result::Result<Self, Self::Error> {
293 value.into_bytes_inner()
294 }
295}
296
297impl<'a, 'js> TryFrom<&'a ObjectBytes<'js>> for &'a [u8] {
298 type Error = Rc<str>;
299 fn try_from(value: &'a ObjectBytes<'js>) -> std::result::Result<Self, Self::Error> {
300 value.as_bytes_inner()
301 }
302}
303
304impl<'js> FromJs<'js> for ObjectBytes<'js> {
305 fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
306 Self::from_offset(ctx, &value, 0, None)
307 }
308}
309
310impl<'js> ObjectBytes<'js> {
311 pub fn from(ctx: &Ctx<'js>, value: &Value<'js>) -> Result<Self> {
312 Self::from_offset(ctx, value, 0, None)
313 }
314
315 pub fn from_offset(
316 ctx: &Ctx<'js>,
317 value: &Value<'js>,
318 offset: usize,
319 length: Option<usize>,
320 ) -> Result<Self> {
321 if value.is_undefined() {
322 return Ok(ObjectBytes::Vec(vec![]));
323 }
324 if let Some(bytes) = get_string_bytes(value, offset, length)? {
325 return Ok(ObjectBytes::Vec(bytes));
326 }
327 if let Some(bytes) = get_array_bytes(value, offset, length)? {
328 return Ok(ObjectBytes::Vec(bytes));
329 }
330
331 if let Some(obj) = value.as_object() {
332 if let Some(bytes) = Self::from_array_buffer(obj)? {
333 return Ok(bytes);
334 }
335 }
336
337 if let Some(bytes) = get_coerced_string_bytes(value, offset, length) {
338 return Ok(ObjectBytes::Vec(bytes));
339 }
340
341 Err(Exception::throw_message(
342 ctx,
343 "value must be typed DataView, Buffer, ArrayBuffer, Uint8Array or interpretable as string",
344 ))
345 }
346
347 pub fn as_bytes(&self, ctx: &Ctx<'js>) -> Result<&[u8]> {
348 self.as_bytes_inner().or_throw(ctx)
349 }
350
351 pub fn as_bytes_opt(&self) -> Option<&[u8]> {
355 self.as_bytes_inner().ok()
356 }
357
358 fn as_bytes_inner(&self) -> std::result::Result<&[u8], Rc<str>> {
363 unsafe {
365 match self {
366 ObjectBytes::U8Array(array) => array.as_bytes(),
367 ObjectBytes::I8Array(array) => array.as_bytes(),
368 ObjectBytes::U16Array(array) => array.as_bytes(),
369 ObjectBytes::I16Array(array) => array.as_bytes(),
370 ObjectBytes::U32Array(array) => array.as_bytes(),
371 ObjectBytes::I32Array(array) => array.as_bytes(),
372 ObjectBytes::U64Array(array) => array.as_bytes(),
373 ObjectBytes::I64Array(array) => array.as_bytes(),
374 ObjectBytes::F16Array(array) => array.as_bytes(),
375 ObjectBytes::F32Array(array) => array.as_bytes(),
376 ObjectBytes::F64Array(array) => array.as_bytes(),
377 ObjectBytes::U8ClampedArray(array) => array.as_bytes(),
378 ObjectBytes::DataView(ab, offset, length) => ab.as_bytes().and_then(|bytes| {
379 let end = offset.checked_add(*length)?;
380 bytes.get(*offset..end)
381 }),
382 ObjectBytes::Vec(bytes) => Some(bytes.as_ref()),
383 }
384 }
385 .ok_or(ERROR_MSG_ARRAY_BUFFER_DETACHED.into())
386 }
387
388 pub fn into_bytes(self, ctx: &Ctx<'_>) -> Result<Vec<u8>> {
389 self.into_bytes_inner().or_throw(ctx)
390 }
391
392 fn into_bytes_inner(self) -> std::result::Result<Vec<u8>, Rc<str>> {
393 if let ObjectBytes::Vec(bytes) = self {
394 return Ok(bytes);
395 }
396 Ok(self.as_bytes_inner()?.to_vec())
397 }
398
399 pub fn from_array_buffer(obj: &Object<'js>) -> Result<Option<ObjectBytes<'js>>> {
400 if let Ok(typed_array) = TypedArray::<u8>::from_object(obj.clone()) {
402 return Ok(Some(ObjectBytes::U8Array(typed_array)));
403 }
404 if let Some(array_buffer) = ArrayBuffer::from_object(obj.clone()) {
406 let len = array_buffer.len();
407 return Ok(Some(ObjectBytes::DataView(array_buffer, 0, len)));
408 }
409
410 if let Ok(typed_array) = TypedArray::<i8>::from_object(obj.clone()) {
411 return Ok(Some(ObjectBytes::I8Array(typed_array)));
412 }
413
414 if let Ok(typed_array) = TypedArray::<u16>::from_object(obj.clone()) {
415 return Ok(Some(ObjectBytes::U16Array(typed_array)));
416 }
417
418 if let Ok(typed_array) = TypedArray::<i16>::from_object(obj.clone()) {
419 return Ok(Some(ObjectBytes::I16Array(typed_array)));
420 }
421
422 if let Ok(typed_array) = TypedArray::<u32>::from_object(obj.clone()) {
423 return Ok(Some(ObjectBytes::U32Array(typed_array)));
424 }
425
426 if let Ok(typed_array) = TypedArray::<i32>::from_object(obj.clone()) {
427 return Ok(Some(ObjectBytes::I32Array(typed_array)));
428 }
429
430 if let Ok(typed_array) = TypedArray::<u64>::from_object(obj.clone()) {
431 return Ok(Some(ObjectBytes::U64Array(typed_array)));
432 }
433
434 if let Ok(typed_array) = TypedArray::<i64>::from_object(obj.clone()) {
435 return Ok(Some(ObjectBytes::I64Array(typed_array)));
436 }
437
438 if let Ok(typed_array) = TypedArray::<f16>::from_object(obj.clone()) {
439 return Ok(Some(ObjectBytes::F16Array(typed_array)));
440 }
441
442 if let Ok(typed_array) = TypedArray::<f32>::from_object(obj.clone()) {
443 return Ok(Some(ObjectBytes::F32Array(typed_array)));
444 }
445
446 if let Ok(typed_array) = TypedArray::<f64>::from_object(obj.clone()) {
447 return Ok(Some(ObjectBytes::F64Array(typed_array)));
448 }
449
450 if let Ok(typed_array) = TypedArray::<U8Clamped>::from_object(obj.clone()) {
451 return Ok(Some(ObjectBytes::U8ClampedArray(typed_array)));
452 }
453
454 if let Ok(ab) = obj.get::<_, ArrayBuffer>("buffer") {
455 let offset: usize = obj.get("byteOffset").unwrap_or(0);
456 let length: usize = obj.get("byteLength").unwrap_or_else(|_| ab.len());
457 return Ok(Some(ObjectBytes::DataView(ab, offset, length)));
458 }
459
460 Ok(None)
461 }
462
463 pub fn get_array_buffer(&self) -> Result<Option<(ArrayBuffer<'js>, usize, usize)>> {
464 let buffer = match self {
465 ObjectBytes::U8Array(typed_array) => {
466 let byte_length = typed_array.len();
467 (
468 typed_array.arraybuffer()?,
469 byte_length,
470 typed_array.get("byteOffset")?,
471 )
472 },
473 ObjectBytes::I8Array(typed_array) => {
474 let byte_length = typed_array.len();
475 (
476 typed_array.arraybuffer()?,
477 byte_length,
478 typed_array.get("byteOffset")?,
479 )
480 },
481 ObjectBytes::U16Array(typed_array) => {
482 let byte_length = typed_array.len() * 2;
483 (
484 typed_array.arraybuffer()?,
485 byte_length,
486 typed_array.get("byteOffset")?,
487 )
488 },
489 ObjectBytes::I16Array(typed_array) => {
490 let byte_length = typed_array.len() * 2;
491 (
492 typed_array.arraybuffer()?,
493 byte_length,
494 typed_array.get("byteOffset")?,
495 )
496 },
497 ObjectBytes::U32Array(typed_array) => {
498 let byte_length = typed_array.len() * 4;
499 (
500 typed_array.arraybuffer()?,
501 byte_length,
502 typed_array.get("byteOffset")?,
503 )
504 },
505 ObjectBytes::I32Array(typed_array) => {
506 let byte_length = typed_array.len() * 4;
507 (
508 typed_array.arraybuffer()?,
509 byte_length,
510 typed_array.get("byteOffset")?,
511 )
512 },
513 ObjectBytes::U64Array(typed_array) => {
514 let byte_length = typed_array.len() * 8;
515 (
516 typed_array.arraybuffer()?,
517 byte_length,
518 typed_array.get("byteOffset")?,
519 )
520 },
521 ObjectBytes::I64Array(typed_array) => {
522 let byte_length = typed_array.len() * 8;
523 (
524 typed_array.arraybuffer()?,
525 byte_length,
526 typed_array.get("byteOffset")?,
527 )
528 },
529 ObjectBytes::F16Array(typed_array) => {
530 let byte_length = typed_array.len() * 2;
531 (
532 typed_array.arraybuffer()?,
533 byte_length,
534 typed_array.get("byteOffset")?,
535 )
536 },
537 ObjectBytes::F32Array(typed_array) => {
538 let byte_length = typed_array.len() * 4;
539 (
540 typed_array.arraybuffer()?,
541 byte_length,
542 typed_array.get("byteOffset")?,
543 )
544 },
545 ObjectBytes::F64Array(typed_array) => {
546 let byte_length = typed_array.len() * 8;
547 (
548 typed_array.arraybuffer()?,
549 byte_length,
550 typed_array.get("byteOffset")?,
551 )
552 },
553 ObjectBytes::U8ClampedArray(typed_array) => {
554 let byte_length = typed_array.len();
555 (
556 typed_array.arraybuffer()?,
557 byte_length,
558 typed_array.get("byteOffset")?,
559 )
560 },
561 ObjectBytes::DataView(array_buffer, offset, length) => {
562 (array_buffer.clone(), *length, *offset)
563 },
564 _ => return Ok(None),
565 };
566
567 Ok(Some(buffer))
568 }
569}
570
571#[cfg(test)]
572mod object_bytes_tests {
573 use super::{ObjectBytes, ERROR_MSG_ARRAY_BUFFER_DETACHED};
574 use rquickjs::{ArrayBuffer, Context, Runtime};
575
576 #[test]
577 fn data_view_ranges_are_checked() {
578 let rt = Runtime::new().unwrap();
579 let ctx = Context::full(&rt).unwrap();
580
581 ctx.with(|ctx| {
582 let buffer = ArrayBuffer::new_copy(ctx, [1_u8, 2, 3, 4]).unwrap();
583 for (offset, length) in [(3, 2), (usize::MAX, 1)] {
584 let bytes = ObjectBytes::DataView(buffer.clone(), offset, length);
585
586 assert_eq!(
587 bytes.as_bytes_inner().unwrap_err().as_ref(),
588 ERROR_MSG_ARRAY_BUFFER_DETACHED
589 );
590 }
591
592 let valid_bytes = ObjectBytes::DataView(buffer, 1, 2);
593 assert_eq!(valid_bytes.as_bytes_inner().unwrap(), &[2, 3]);
594 });
595 }
596
597 #[test]
598 fn data_view_detached_buffer_returns_error() {
599 let rt = Runtime::new().unwrap();
600 let ctx = Context::full(&rt).unwrap();
601
602 ctx.with(|ctx| {
603 let mut buffer = ArrayBuffer::new_copy(ctx, [1_u8, 2, 3, 4]).unwrap();
604 buffer.detach();
605 let bytes = ObjectBytes::DataView(buffer, 0, 4);
606
607 assert_eq!(
608 bytes.as_bytes_inner().unwrap_err().as_ref(),
609 ERROR_MSG_ARRAY_BUFFER_DETACHED
610 );
611 });
612 }
613}
614
615pub fn get_start_end_indexes(
616 source_len: usize,
617 target_len: Option<usize>,
618 offset: usize,
619) -> (usize, usize) {
620 if offset > source_len {
621 return (0, 0);
622 }
623
624 let target_len = target_len.unwrap_or(source_len - offset);
625
626 if offset + target_len > source_len {
627 return (offset, source_len);
628 }
629
630 (offset, target_len + offset)
631}
632
633pub fn get_array_bytes(
634 value: &Value<'_>,
635 offset: usize,
636 length: Option<usize>,
637) -> Result<Option<Vec<u8>>> {
638 if value.is_array() {
639 let array = value.as_array().unwrap();
640 let (start, end) = get_start_end_indexes(array.len(), length, offset);
641 let size = end - start;
642 let mut bytes: Vec<u8> = Vec::with_capacity(size);
643
644 for val in array.iter::<u8>().skip(start).take(size) {
645 let val: u8 = val?;
646 bytes.push(val);
647 }
648
649 return Ok(Some(bytes));
650 }
651 Ok(None)
652}
653
654pub fn get_coerced_string_bytes(
655 value: &Value<'_>,
656 offset: usize,
657 length: Option<usize>,
658) -> Option<Vec<u8>> {
659 if let Ok(val) = value.get::<Coerced<String>>() {
660 return Some(bytes_from_js_string(val.0, offset, length));
661 };
662 None
663}
664
665fn bytes_from_js_string(string: String, offset: usize, length: Option<usize>) -> Vec<u8> {
666 let (start, end) = get_start_end_indexes(string.len(), length, offset);
667 string.as_bytes()[start..end].to_vec()
668}
669
670#[inline]
671pub fn get_string_bytes(
672 value: &Value<'_>,
673 offset: usize,
674 length: Option<usize>,
675) -> Result<Option<Vec<u8>>> {
676 if value.is_string() {
677 let string = get_lossy_string(value.clone())?;
678 return Ok(Some(bytes_from_js_string(string, offset, length)));
679 }
680 Ok(None)
681}
682
683pub fn bytes_to_typed_array<'js>(ctx: Ctx<'js>, bytes: &[u8]) -> Result<Value<'js>> {
684 TypedArray::<u8>::new(ctx.clone(), bytes).into_js(&ctx)
685}