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>> {
361 unsafe {
362 match self {
363 ObjectBytes::U8Array(array) => array.as_bytes(),
364 ObjectBytes::I8Array(array) => array.as_bytes(),
365 ObjectBytes::U16Array(array) => array.as_bytes(),
366 ObjectBytes::I16Array(array) => array.as_bytes(),
367 ObjectBytes::U32Array(array) => array.as_bytes(),
368 ObjectBytes::I32Array(array) => array.as_bytes(),
369 ObjectBytes::U64Array(array) => array.as_bytes(),
370 ObjectBytes::I64Array(array) => array.as_bytes(),
371 ObjectBytes::F16Array(array) => array.as_bytes(),
372 ObjectBytes::F32Array(array) => array.as_bytes(),
373 ObjectBytes::F64Array(array) => array.as_bytes(),
374 ObjectBytes::U8ClampedArray(array) => array.as_bytes(),
375 ObjectBytes::DataView(ab, offset, length) => ab.as_bytes().and_then(|bytes| {
376 let end = offset.checked_add(*length)?;
377 bytes.get(*offset..end)
378 }),
379 ObjectBytes::Vec(bytes) => Some(bytes.as_ref()),
380 }
381 }
382 .ok_or(ERROR_MSG_ARRAY_BUFFER_DETACHED.into())
383 }
384
385 pub fn into_bytes(self, ctx: &Ctx<'_>) -> Result<Vec<u8>> {
386 self.into_bytes_inner().or_throw(ctx)
387 }
388
389 fn into_bytes_inner(self) -> std::result::Result<Vec<u8>, Rc<str>> {
390 if let ObjectBytes::Vec(bytes) = self {
391 return Ok(bytes);
392 }
393 Ok(self.as_bytes_inner()?.to_vec())
394 }
395
396 pub fn from_array_buffer(obj: &Object<'js>) -> Result<Option<ObjectBytes<'js>>> {
397 if let Ok(typed_array) = TypedArray::<u8>::from_object(obj.clone()) {
399 return Ok(Some(ObjectBytes::U8Array(typed_array)));
400 }
401 if let Some(array_buffer) = ArrayBuffer::from_object(obj.clone()) {
403 let len = array_buffer.len();
404 return Ok(Some(ObjectBytes::DataView(array_buffer, 0, len)));
405 }
406
407 if let Ok(typed_array) = TypedArray::<i8>::from_object(obj.clone()) {
408 return Ok(Some(ObjectBytes::I8Array(typed_array)));
409 }
410
411 if let Ok(typed_array) = TypedArray::<u16>::from_object(obj.clone()) {
412 return Ok(Some(ObjectBytes::U16Array(typed_array)));
413 }
414
415 if let Ok(typed_array) = TypedArray::<i16>::from_object(obj.clone()) {
416 return Ok(Some(ObjectBytes::I16Array(typed_array)));
417 }
418
419 if let Ok(typed_array) = TypedArray::<u32>::from_object(obj.clone()) {
420 return Ok(Some(ObjectBytes::U32Array(typed_array)));
421 }
422
423 if let Ok(typed_array) = TypedArray::<i32>::from_object(obj.clone()) {
424 return Ok(Some(ObjectBytes::I32Array(typed_array)));
425 }
426
427 if let Ok(typed_array) = TypedArray::<u64>::from_object(obj.clone()) {
428 return Ok(Some(ObjectBytes::U64Array(typed_array)));
429 }
430
431 if let Ok(typed_array) = TypedArray::<i64>::from_object(obj.clone()) {
432 return Ok(Some(ObjectBytes::I64Array(typed_array)));
433 }
434
435 if let Ok(typed_array) = TypedArray::<f16>::from_object(obj.clone()) {
436 return Ok(Some(ObjectBytes::F16Array(typed_array)));
437 }
438
439 if let Ok(typed_array) = TypedArray::<f32>::from_object(obj.clone()) {
440 return Ok(Some(ObjectBytes::F32Array(typed_array)));
441 }
442
443 if let Ok(typed_array) = TypedArray::<f64>::from_object(obj.clone()) {
444 return Ok(Some(ObjectBytes::F64Array(typed_array)));
445 }
446
447 if let Ok(typed_array) = TypedArray::<U8Clamped>::from_object(obj.clone()) {
448 return Ok(Some(ObjectBytes::U8ClampedArray(typed_array)));
449 }
450
451 if let Ok(ab) = obj.get::<_, ArrayBuffer>("buffer") {
452 let offset: usize = obj.get("byteOffset").unwrap_or(0);
453 let length: usize = obj.get("byteLength").unwrap_or_else(|_| ab.len());
454 return Ok(Some(ObjectBytes::DataView(ab, offset, length)));
455 }
456
457 Ok(None)
458 }
459
460 pub fn get_array_buffer(&self) -> Result<Option<(ArrayBuffer<'js>, usize, usize)>> {
461 let buffer = match self {
462 ObjectBytes::U8Array(typed_array) => {
463 let byte_length = typed_array.len();
464 (
465 typed_array.arraybuffer()?,
466 byte_length,
467 typed_array.get("byteOffset")?,
468 )
469 },
470 ObjectBytes::I8Array(typed_array) => {
471 let byte_length = typed_array.len();
472 (
473 typed_array.arraybuffer()?,
474 byte_length,
475 typed_array.get("byteOffset")?,
476 )
477 },
478 ObjectBytes::U16Array(typed_array) => {
479 let byte_length = typed_array.len() * 2;
480 (
481 typed_array.arraybuffer()?,
482 byte_length,
483 typed_array.get("byteOffset")?,
484 )
485 },
486 ObjectBytes::I16Array(typed_array) => {
487 let byte_length = typed_array.len() * 2;
488 (
489 typed_array.arraybuffer()?,
490 byte_length,
491 typed_array.get("byteOffset")?,
492 )
493 },
494 ObjectBytes::U32Array(typed_array) => {
495 let byte_length = typed_array.len() * 4;
496 (
497 typed_array.arraybuffer()?,
498 byte_length,
499 typed_array.get("byteOffset")?,
500 )
501 },
502 ObjectBytes::I32Array(typed_array) => {
503 let byte_length = typed_array.len() * 4;
504 (
505 typed_array.arraybuffer()?,
506 byte_length,
507 typed_array.get("byteOffset")?,
508 )
509 },
510 ObjectBytes::U64Array(typed_array) => {
511 let byte_length = typed_array.len() * 8;
512 (
513 typed_array.arraybuffer()?,
514 byte_length,
515 typed_array.get("byteOffset")?,
516 )
517 },
518 ObjectBytes::I64Array(typed_array) => {
519 let byte_length = typed_array.len() * 8;
520 (
521 typed_array.arraybuffer()?,
522 byte_length,
523 typed_array.get("byteOffset")?,
524 )
525 },
526 ObjectBytes::F16Array(typed_array) => {
527 let byte_length = typed_array.len() * 2;
528 (
529 typed_array.arraybuffer()?,
530 byte_length,
531 typed_array.get("byteOffset")?,
532 )
533 },
534 ObjectBytes::F32Array(typed_array) => {
535 let byte_length = typed_array.len() * 4;
536 (
537 typed_array.arraybuffer()?,
538 byte_length,
539 typed_array.get("byteOffset")?,
540 )
541 },
542 ObjectBytes::F64Array(typed_array) => {
543 let byte_length = typed_array.len() * 8;
544 (
545 typed_array.arraybuffer()?,
546 byte_length,
547 typed_array.get("byteOffset")?,
548 )
549 },
550 ObjectBytes::U8ClampedArray(typed_array) => {
551 let byte_length = typed_array.len();
552 (
553 typed_array.arraybuffer()?,
554 byte_length,
555 typed_array.get("byteOffset")?,
556 )
557 },
558 ObjectBytes::DataView(array_buffer, offset, length) => {
559 (array_buffer.clone(), *length, *offset)
560 },
561 _ => return Ok(None),
562 };
563
564 Ok(Some(buffer))
565 }
566}
567
568#[cfg(test)]
569mod object_bytes_tests {
570 use super::{ObjectBytes, ERROR_MSG_ARRAY_BUFFER_DETACHED};
571 use rquickjs::{ArrayBuffer, Context, Runtime};
572
573 #[test]
574 fn data_view_ranges_are_checked() {
575 let rt = Runtime::new().unwrap();
576 let ctx = Context::full(&rt).unwrap();
577
578 ctx.with(|ctx| {
579 let buffer = ArrayBuffer::new_copy(ctx, [1_u8, 2, 3, 4]).unwrap();
580 for (offset, length) in [(3, 2), (usize::MAX, 1)] {
581 let bytes = ObjectBytes::DataView(buffer.clone(), offset, length);
582
583 assert_eq!(
584 bytes.as_bytes_inner().unwrap_err().as_ref(),
585 ERROR_MSG_ARRAY_BUFFER_DETACHED
586 );
587 }
588
589 let valid_bytes = ObjectBytes::DataView(buffer, 1, 2);
590 assert_eq!(valid_bytes.as_bytes_inner().unwrap(), &[2, 3]);
591 });
592 }
593
594 #[test]
595 fn data_view_detached_buffer_returns_error() {
596 let rt = Runtime::new().unwrap();
597 let ctx = Context::full(&rt).unwrap();
598
599 ctx.with(|ctx| {
600 let mut buffer = ArrayBuffer::new_copy(ctx, [1_u8, 2, 3, 4]).unwrap();
601 buffer.detach();
602 let bytes = ObjectBytes::DataView(buffer, 0, 4);
603
604 assert_eq!(
605 bytes.as_bytes_inner().unwrap_err().as_ref(),
606 ERROR_MSG_ARRAY_BUFFER_DETACHED
607 );
608 });
609 }
610}
611
612pub fn get_start_end_indexes(
613 source_len: usize,
614 target_len: Option<usize>,
615 offset: usize,
616) -> (usize, usize) {
617 if offset > source_len {
618 return (0, 0);
619 }
620
621 let target_len = target_len.unwrap_or(source_len - offset);
622
623 if offset + target_len > source_len {
624 return (offset, source_len);
625 }
626
627 (offset, target_len + offset)
628}
629
630pub fn get_array_bytes(
631 value: &Value<'_>,
632 offset: usize,
633 length: Option<usize>,
634) -> Result<Option<Vec<u8>>> {
635 if value.is_array() {
636 let array = value.as_array().unwrap();
637 let (start, end) = get_start_end_indexes(array.len(), length, offset);
638 let size = end - start;
639 let mut bytes: Vec<u8> = Vec::with_capacity(size);
640
641 for val in array.iter::<u8>().skip(start).take(size) {
642 let val: u8 = val?;
643 bytes.push(val);
644 }
645
646 return Ok(Some(bytes));
647 }
648 Ok(None)
649}
650
651pub fn get_coerced_string_bytes(
652 value: &Value<'_>,
653 offset: usize,
654 length: Option<usize>,
655) -> Option<Vec<u8>> {
656 if let Ok(val) = value.get::<Coerced<String>>() {
657 return Some(bytes_from_js_string(val.0, offset, length));
658 };
659 None
660}
661
662fn bytes_from_js_string(string: String, offset: usize, length: Option<usize>) -> Vec<u8> {
663 let (start, end) = get_start_end_indexes(string.len(), length, offset);
664 string.as_bytes()[start..end].to_vec()
665}
666
667#[inline]
668pub fn get_string_bytes(
669 value: &Value<'_>,
670 offset: usize,
671 length: Option<usize>,
672) -> Result<Option<Vec<u8>>> {
673 if value.is_string() {
674 let string = get_lossy_string(value.clone())?;
675 return Ok(Some(bytes_from_js_string(string, offset, length)));
676 }
677 Ok(None)
678}
679
680pub fn bytes_to_typed_array<'js>(ctx: Ctx<'js>, bytes: &[u8]) -> Result<Value<'js>> {
681 TypedArray::<u8>::new(ctx.clone(), bytes).into_js(&ctx)
682}