1use std::{future::Future, pin::Pin, task::Poll};
2
3use wasm_bindgen_futures::JsFuture;
4
5#[derive(Debug, Default)]
6pub enum Op {
7 #[default]
8 Idle,
9 ReadPending(JsFuture),
10 ConsumingReadBuffer {
11 read_buffer: js_sys::Uint8Array,
12 already_read: usize,
13 },
14}
15
16#[derive(Debug)]
21pub enum Mode {
22 Byob {
24 reader: web_sys::ReadableStreamByobReader,
26
27 internal_buf: Option<js_sys::ArrayBuffer>,
32 },
33
34 Default {
36 reader: web_sys::ReadableStreamDefaultReader,
38 },
39}
40
41impl Mode {
42 pub fn release_lock(&self) {
44 match self {
45 Self::Byob { reader, .. } => reader.release_lock(),
46 Self::Default { reader } => reader.release_lock(),
47 }
48 }
49
50 pub fn cancel_with_reason(&self, reason: &wasm_bindgen::JsValue) -> js_sys::Promise {
52 match self {
53 Self::Byob { reader, .. } => reader.cancel_with_reason(reason),
54 Self::Default { reader } => reader.cancel_with_reason(reason),
55 }
56 }
57
58 pub fn closed(&self) -> js_sys::Promise {
60 match self {
61 Self::Byob { reader, .. } => reader.closed(),
62 Self::Default { reader } => reader.closed(),
63 }
64 }
65
66 fn start_read(&mut self, requested_size: u32) -> JsFuture {
74 match self {
75 Self::Byob {
76 reader,
77 internal_buf,
78 } => {
79 let internal_buf = internal_buf
80 .take()
81 .filter(|internal_buf| {
82 let actual_size = internal_buf.byte_length();
83 debug_assert!(actual_size > 0);
84 actual_size >= requested_size
85 })
86 .unwrap_or_else(|| js_sys::ArrayBuffer::new(requested_size));
87 let internal_buf_view = js_sys::Uint8Array::new_with_byte_offset_and_length(
88 &internal_buf,
89 0,
90 requested_size,
91 );
92 JsFuture::from(reader.read_with_array_buffer_view(&internal_buf_view))
97 }
98 Self::Default { reader } => JsFuture::from(reader.read()),
99 }
100 }
101
102 fn reclaim_read_buffer(&mut self, read_buffer: &js_sys::Uint8Array) {
111 if let Self::Byob { internal_buf, .. } = self {
112 *internal_buf = Some(read_buffer.buffer());
113 }
114 }
115}
116
117fn parse_read_result(
122 mode: &Mode,
123 result: Result<wasm_bindgen::JsValue, wasm_bindgen::JsValue>,
124) -> Result<Option<js_sys::Uint8Array>, ReadError> {
125 let result = result.map_err(ReadError::Read)?;
126 let result: crate::sys::ReadableStreamReaderValue = result.into();
127 match result.value() {
128 Some(read_buffer) => Ok(Some(read_buffer)),
129 None => match mode {
133 Mode::Byob { .. } => Err(ReadError::ByobReadConsumedBuffer),
134 Mode::Default { .. } => Ok(None),
135 },
136 }
137}
138
139fn consume_read_buffer(
146 mode: &mut Mode,
147 op: &mut Op,
148 read_buffer: js_sys::Uint8Array,
149 already_read: usize,
150 dest: &mut [u8],
151) -> usize {
152 let read_buffer_size = read_buffer.byte_length() as usize;
153 let remaining_size = read_buffer_size - already_read;
154 let copy_size = remaining_size.min(dest.len());
155
156 if already_read == 0 && copy_size == read_buffer_size {
161 read_buffer.copy_to(&mut dest[..copy_size]);
163 } else {
164 let source_view =
165 read_buffer.subarray(already_read as u32, (already_read + copy_size) as u32);
166 source_view.copy_to(&mut dest[..copy_size]);
167 }
168
169 if already_read + copy_size < read_buffer_size {
170 *op = Op::ConsumingReadBuffer {
172 read_buffer,
173 already_read: already_read + copy_size,
174 };
175 } else {
176 mode.reclaim_read_buffer(&read_buffer);
177 }
178
179 copy_size
180}
181
182impl From<web_sys::ReadableStreamByobReader> for Mode {
183 fn from(reader: web_sys::ReadableStreamByobReader) -> Self {
184 Self::Byob {
185 reader,
186 internal_buf: None,
187 }
188 }
189}
190
191impl From<web_sys::ReadableStreamDefaultReader> for Mode {
192 fn from(reader: web_sys::ReadableStreamDefaultReader) -> Self {
193 Self::Default { reader }
194 }
195}
196
197#[derive(Debug)]
199pub enum ReadError {
200 Read(wasm_bindgen::JsValue),
202
203 ByobReadConsumedBuffer,
206}
207
208impl From<ReadError> for std::io::Error {
209 fn from(err: ReadError) -> Self {
210 match err {
211 ReadError::Read(err) => super::js_value_to_io_error(err),
212 ReadError::ByobReadConsumedBuffer => {
213 std::io::Error::other("BYOB read consumed the buffer and did not provide a new one")
214 }
215 }
216 }
217}
218
219#[derive(Debug)]
220pub struct Reader {
221 pub inner: Mode,
222 pub op: Op,
223}
224
225impl Reader {
226 pub fn new(inner: impl Into<Mode>) -> Self {
227 Self {
228 inner: inner.into(),
229 op: Op::default(),
230 }
231 }
232
233 pub fn with_buf(
234 inner: web_sys::ReadableStreamByobReader,
235 internal_buf: js_sys::ArrayBuffer,
236 ) -> Self {
237 Self {
238 inner: Mode::Byob {
239 reader: inner,
240 internal_buf: Some(internal_buf),
241 },
242 op: Op::default(),
243 }
244 }
245
246 pub async fn read_into(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> {
255 let (read_buffer, already_read) = match std::mem::take(&mut self.op) {
258 Op::ConsumingReadBuffer {
259 read_buffer,
260 already_read,
261 } => (read_buffer, already_read),
262 op => {
263 let fut = match op {
264 Op::ReadPending(fut) => fut,
266 _ => {
268 let requested_size = buf.len().try_into().unwrap();
269 self.inner.start_read(requested_size)
270 }
271 };
272 match parse_read_result(&self.inner, fut.await)? {
273 Some(read_buffer) => (read_buffer, 0),
274 None => return Ok(0),
275 }
276 }
277 };
278
279 Ok(consume_read_buffer(
280 &mut self.inner,
281 &mut self.op,
282 read_buffer,
283 already_read,
284 buf,
285 ))
286 }
287}
288
289impl tokio::io::AsyncRead for Reader {
290 fn poll_read(
291 self: Pin<&mut Self>,
292 cx: &mut std::task::Context<'_>,
293 buf: &mut tokio::io::ReadBuf<'_>,
294 ) -> Poll<std::io::Result<()>> {
295 let this = self.get_mut();
296
297 if buf.remaining() == 0 {
300 return Poll::Ready(Ok(()));
301 }
302
303 match std::mem::take(&mut this.op) {
304 Op::ReadPending(mut fut) => {
305 let result = match Pin::new(&mut fut).poll(cx) {
306 Poll::Pending => {
307 this.op = Op::ReadPending(fut);
308 return Poll::Pending;
309 }
310 Poll::Ready(result) => result,
311 };
312
313 let Some(read_buffer) = parse_read_result(&this.inner, result)? else {
315 return Poll::Ready(Ok(()));
316 };
317
318 this.op = Op::ConsumingReadBuffer {
319 read_buffer,
320 already_read: 0,
321 };
322
323 Pin::new(this).poll_read(cx, buf)
324 }
325 Op::ConsumingReadBuffer {
326 read_buffer,
327 already_read,
328 } => {
329 let remaining_size = read_buffer.byte_length() as usize - already_read;
330 let copy_size = remaining_size.min(buf.remaining());
331
332 let write_slice = buf.initialize_unfilled_to(copy_size);
333 let copied = consume_read_buffer(
334 &mut this.inner,
335 &mut this.op,
336 read_buffer,
337 already_read,
338 write_slice,
339 );
340 buf.advance(copied);
341
342 Poll::Ready(Ok(()))
343 }
344 Op::Idle => {
345 let requested_size = buf.remaining().try_into().unwrap();
346 let fut = this.inner.start_read(requested_size);
347 this.op = Op::ReadPending(fut);
348 Pin::new(this).poll_read(cx, buf)
349 }
350 }
351 }
352}