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