| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ |
| |
| use std::cell::RefCell; |
| |
| use encoding_rs::{Decoder, DecoderResult, Encoding}; |
| |
| use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer; |
| use crate::dom::bindings::error::{Error, Fallible}; |
| |
| /// The shared part of `TextDecoder` and `TextDecoderStream` |
| /// |
| /// Note that other than the three attributes defined in the `TextDecoderCommon` |
| /// interface in the WebIDL, this also performs decoding. |
| /// |
| /// <https://encoding.spec.whatwg.org/#textdecodercommon> |
| #[allow(non_snake_case)] |
| #[derive(JSTraceable, MallocSizeOf)] |
| pub(crate) struct TextDecoderCommon { |
| /// <https://encoding.spec.whatwg.org/#dom-textdecoder-encoding> |
| #[no_trace] |
| encoding: &'static Encoding, |
| |
| /// <https://encoding.spec.whatwg.org/#dom-textdecoder-fatal> |
| fatal: bool, |
| |
| /// <https://encoding.spec.whatwg.org/#dom-textdecoder-ignorebom> |
| ignoreBOM: bool, |
| |
| /// The native decoder that is used to perform decoding |
| /// |
| /// <https://encoding.spec.whatwg.org/#textdecodercommon-decoder> |
| #[ignore_malloc_size_of = "defined in encoding_rs"] |
| #[no_trace] |
| decoder: RefCell<Decoder>, |
| |
| /// <https://encoding.spec.whatwg.org/#textdecodercommon-i-o-queue> |
| io_queue: RefCell<Vec<u8>>, |
| } |
| |
| #[allow(non_snake_case)] |
| impl TextDecoderCommon { |
| pub(crate) fn new_inherited( |
| encoding: &'static Encoding, |
| fatal: bool, |
| ignoreBOM: bool, |
| ) -> TextDecoderCommon { |
| let decoder = if ignoreBOM { |
| encoding.new_decoder_without_bom_handling() |
| } else { |
| encoding.new_decoder_with_bom_removal() |
| }; |
| |
| TextDecoderCommon { |
| encoding, |
| fatal, |
| ignoreBOM, |
| decoder: RefCell::new(decoder), |
| io_queue: RefCell::new(Vec::new()), |
| } |
| } |
| |
| /// <https://encoding.spec.whatwg.org/#textdecoder-encoding> |
| pub(crate) fn encoding(&self) -> &'static Encoding { |
| self.encoding |
| } |
| |
| /// <https://encoding.spec.whatwg.org/#textdecodercommon-decoder> |
| pub(crate) fn decoder(&self) -> &RefCell<Decoder> { |
| &self.decoder |
| } |
| |
| /// <https://encoding.spec.whatwg.org/#textdecodercommon-i-o-queue> |
| pub(crate) fn io_queue(&self) -> &RefCell<Vec<u8>> { |
| &self.io_queue |
| } |
| |
| /// <https://encoding.spec.whatwg.org/#textdecoder-error-mode> |
| pub(crate) fn fatal(&self) -> bool { |
| self.fatal |
| } |
| |
| /// <https://encoding.spec.whatwg.org/#textdecoder-ignore-bom-flag> |
| pub(crate) fn ignore_bom(&self) -> bool { |
| self.ignoreBOM |
| } |
| |
| /// Shared by `TextDecoder` and `TextDecoderStream` |
| /// |
| /// <https://encoding.spec.whatwg.org/#dom-textdecoder-decode> |
| /// <https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk> |
| #[allow(unsafe_code)] |
| pub(crate) fn decode( |
| &self, |
| input: Option<&ArrayBufferViewOrArrayBuffer>, |
| last: bool, |
| ) -> Fallible<String> { |
| // <https://encoding.spec.whatwg.org/#dom-textdecoder-decode> |
| // Step 3. If input is given, then push a copy of input to this’s I/O queue. |
| // |
| // <https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk> |
| // Step 2. Push a copy of bufferSource to decoder’s I/O queue. |
| // |
| // NOTE: try to avoid this copy unless there are bytes left |
| let mut io_queue = self.io_queue.borrow_mut(); |
| let input = match &input { |
| Some(ArrayBufferViewOrArrayBuffer::ArrayBufferView(a)) => unsafe { |
| if io_queue.is_empty() { |
| a.as_slice() |
| } else { |
| io_queue.extend_from_slice(a.as_slice()); |
| &io_queue[..] |
| } |
| }, |
| Some(ArrayBufferViewOrArrayBuffer::ArrayBuffer(a)) => unsafe { |
| if io_queue.is_empty() { |
| a.as_slice() |
| } else { |
| io_queue.extend_from_slice(a.as_slice()); |
| &io_queue[..] |
| } |
| }, |
| None => &io_queue[..], |
| }; |
| |
| let mut decoder = self.decoder.borrow_mut(); |
| let (output, read) = if self.fatal { |
| // <https://encoding.spec.whatwg.org/#dom-textdecoder-decode> |
| // Step 4. Let output be the I/O queue of scalar values « end-of-queue ». |
| // |
| // <https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk> |
| // Step 3. Let output be the I/O queue of scalar values « end-of-queue ». |
| let mut output = String::with_capacity( |
| decoder |
| .max_utf8_buffer_length_without_replacement(input.len()) |
| .ok_or_else(|| { |
| Error::Type("Expected UTF8 buffer length would overflow".to_owned()) |
| })?, |
| ); |
| |
| // Note: The two algorithms below are implemented in |
| // `encoding_rs::Decoder::decode_to_string_without_replacement` |
| // |
| // <https://encoding.spec.whatwg.org/#dom-textdecoder-decode> |
| // Step 5. While true: |
| // Step 5.1 Let item be the result of reading from this’s I/O queue. |
| // Step 5.2 If item is end-of-queue and this’s do not flush is true, |
| // then return the result of running serialize I/O queue with this and output. |
| // Step 5.3 Otherwise: |
| // Step 5.3.1 Let result be the result of processing an item with item, this’s decoder, |
| // this’s I/O queue, output, and this’s error mode. |
| // |
| // <https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk> |
| // Step 4. While true: |
| // Step 4.1 Let item be the result of reading from decoder’s I/O queue. |
| // Step 4.2 If item is end-of-queue: |
| // Step 4.2.1 Let outputChunk be the result of running serialize I/O queue with decoder and output. |
| // Step 4.2.2 If outputChunk is not the empty string, then enqueue outputChunk in decoder’s transform. |
| // Step 4.2.3 Return. |
| // Step 4.3 Let result be the result of processing an item with item, decoder’s decoder, |
| // decoder’s I/O queue, output, and decoder’s error mode. |
| // Step 4.4 If result is error, then throw a TypeError. |
| let (result, read) = |
| decoder.decode_to_string_without_replacement(input, &mut output, last); |
| match result { |
| // <https://encoding.spec.whatwg.org/#dom-textdecoder-decode> |
| // Step 5.3.2 If result is finished, then return the result of running serialize I/O |
| // queue with this and output. |
| DecoderResult::InputEmpty => (output, read), |
| // <https://encoding.spec.whatwg.org/#dom-textdecoder-decode> |
| // Step 5.3.3 Otherwise, if result is error, throw a TypeError. |
| DecoderResult::Malformed(_, _) => { |
| return Err(Error::Type("Decoding failed".to_owned())); |
| }, |
| DecoderResult::OutputFull => { |
| unreachable!("output is allocated with sufficient capacity") |
| }, |
| } |
| } else { |
| // <https://encoding.spec.whatwg.org/#dom-textdecoder-decode> |
| // Step 4. Let output be the I/O queue of scalar values « end-of-queue ». |
| let mut output = |
| String::with_capacity(decoder.max_utf8_buffer_length(input.len()).ok_or_else( |
| || Error::Type("Expected UTF8 buffer length would overflow".to_owned()), |
| )?); |
| |
| // Note: The two algorithms below are implemented in |
| // `encoding_rs::Decoder::decode_to_string` |
| // |
| // <https://encoding.spec.whatwg.org/#dom-textdecoder-decode> |
| // Step 5. While true: |
| // Step 5.1 Let item be the result of reading from this’s I/O queue. |
| // Step 5.2 If item is end-of-queue and this’s do not flush is true, |
| // then return the result of running serialize I/O queue with this and output. |
| // Step 5.3 Otherwise: |
| // Step 5.3.1 Let result be the result of processing an item with item, this’s decoder, |
| // this’s I/O queue, output, and this’s error mode. |
| // |
| // <https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk> |
| // Step 4. While true: |
| // Step 4.1 Let item be the result of reading from decoder’s I/O queue. |
| // Step 4.2 If item is end-of-queue: |
| // Step 4.2.1 Let outputChunk be the result of running serialize I/O queue with decoder and output. |
| // Step 4.2.2 If outputChunk is not the empty string, then enqueue outputChunk in decoder’s transform. |
| // Step 4.2.3 Return. |
| // Step 4.3 Let result be the result of processing an item with item, decoder’s decoder, |
| // decoder’s I/O queue, output, and decoder’s error mode. |
| // Step 4.4 If result is error, then throw a TypeError. |
| let (result, read, _replaced) = decoder.decode_to_string(input, &mut output, last); |
| match result { |
| // <https://encoding.spec.whatwg.org/#dom-textdecoder-decode> |
| // Step 5.3.2 If result is finished, then return the result of running serialize I/O |
| // queue with this and output. |
| encoding_rs::CoderResult::InputEmpty => (output, read), |
| encoding_rs::CoderResult::OutputFull => { |
| unreachable!("output is allocated with sufficient capacity") |
| }, |
| } |
| }; |
| |
| let (_consumed, remaining) = input.split_at(read); |
| *io_queue = remaining.to_vec(); |
| |
| Ok(output) |
| } |
| } |