You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					122 lines
				
				3.4 KiB
			
		
		
			
		
	
	
					122 lines
				
				3.4 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								const { end_of_stream } = require('../utils');
							 | 
						||
| 
								 | 
							
								const { label_to_encoding } = require('../table');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Stream {
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * A stream represents an ordered sequence of tokens.
							 | 
						||
| 
								 | 
							
								   * @param {!(Array.<number>|Uint8Array)} tokens Array of tokens that provide
							 | 
						||
| 
								 | 
							
								   * the stream.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  constructor(tokens) {
							 | 
						||
| 
								 | 
							
								    this.tokens = [...tokens]
							 | 
						||
| 
								 | 
							
								    // Reversed as push/pop is more efficient than shift/unshift.
							 | 
						||
| 
								 | 
							
								    this.tokens.reverse()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * @returns True if end-of-stream has been hit.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  endOfStream() {
							 | 
						||
| 
								 | 
							
								    return !this.tokens.length
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * When a token is read from a stream, the first token in the
							 | 
						||
| 
								 | 
							
								   * stream must be returned and subsequently removed, and
							 | 
						||
| 
								 | 
							
								   * end-of-stream must be returned otherwise.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return Get the next token from the stream, or end_of_stream.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  read() {
							 | 
						||
| 
								 | 
							
								    if (!this.tokens.length)
							 | 
						||
| 
								 | 
							
								      return end_of_stream
							 | 
						||
| 
								 | 
							
								    return this.tokens.pop()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * When one or more tokens are prepended to a stream, those tokens
							 | 
						||
| 
								 | 
							
								   * must be inserted, in given order, before the first token in the
							 | 
						||
| 
								 | 
							
								   * stream.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param {(number|!Array.<number>)} token The token(s) to prepend to the
							 | 
						||
| 
								 | 
							
								   * stream.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  prepend(token) {
							 | 
						||
| 
								 | 
							
								    if (Array.isArray(token)) {
							 | 
						||
| 
								 | 
							
								      var tokens = /**@type {!Array.<number>}*/(token)
							 | 
						||
| 
								 | 
							
								      while (tokens.length)
							 | 
						||
| 
								 | 
							
								        this.tokens.push(tokens.pop())
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      this.tokens.push(token)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * When one or more tokens are pushed to a stream, those tokens
							 | 
						||
| 
								 | 
							
								   * must be inserted, in given order, after the last token in the
							 | 
						||
| 
								 | 
							
								   * stream.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param {(number|!Array.<number>)} token The tokens(s) to push to the
							 | 
						||
| 
								 | 
							
								   * stream.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  push(token) {
							 | 
						||
| 
								 | 
							
								    if (Array.isArray(token)) {
							 | 
						||
| 
								 | 
							
								      const tokens = /**@type {!Array.<number>}*/(token)
							 | 
						||
| 
								 | 
							
								      while (tokens.length)
							 | 
						||
| 
								 | 
							
								        this.tokens.unshift(tokens.shift())
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      this.tokens.unshift(token)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const DEFAULT_ENCODING = 'utf-8'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Returns the encoding for the label.
							 | 
						||
| 
								 | 
							
								 * @param {string} label The encoding label.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function getEncoding(label) {
							 | 
						||
| 
								 | 
							
								  // 1. Remove any leading and trailing ASCII whitespace from label.
							 | 
						||
| 
								 | 
							
								  label = String(label).trim().toLowerCase()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // 2. If label is an ASCII case-insensitive match for any of the
							 | 
						||
| 
								 | 
							
								  // labels listed in the table below, return the corresponding
							 | 
						||
| 
								 | 
							
								  // encoding, and failure otherwise.
							 | 
						||
| 
								 | 
							
								  if (Object.prototype.hasOwnProperty.call(label_to_encoding, label)) {
							 | 
						||
| 
								 | 
							
								    return label_to_encoding[label]
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return null
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// 5. Encodings
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 5.1 Encoders and decoders
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// /** @interface */
							 | 
						||
| 
								 | 
							
								// function Decoder() {}
							 | 
						||
| 
								 | 
							
								// Decoder.prototype = {
							 | 
						||
| 
								 | 
							
								//   /**
							 | 
						||
| 
								 | 
							
								//    * @param {Stream} stream The stream of bytes being decoded.
							 | 
						||
| 
								 | 
							
								//    * @param {number} bite The next byte read from the stream.
							 | 
						||
| 
								 | 
							
								//    * @return {?(number|!Array.<number>)} The next code point(s)
							 | 
						||
| 
								 | 
							
								//    *     decoded, or null if not enough data exists in the input
							 | 
						||
| 
								 | 
							
								//    *     stream to decode a complete code point, or |finished|.
							 | 
						||
| 
								 | 
							
								//    */
							 | 
						||
| 
								 | 
							
								//   handler: function(stream, bite) {},
							 | 
						||
| 
								 | 
							
								// }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// /** @interface */
							 | 
						||
| 
								 | 
							
								// function Encoder() {}
							 | 
						||
| 
								 | 
							
								// Encoder.prototype = {
							 | 
						||
| 
								 | 
							
								//   /**
							 | 
						||
| 
								 | 
							
								//    * @param {Stream} stream The stream of code points being encoded.
							 | 
						||
| 
								 | 
							
								//    * @param {number} code_point Next code point read from the stream.
							 | 
						||
| 
								 | 
							
								//    * @return {(number|!Array.<number>)} Byte(s) to emit, or |finished|.
							 | 
						||
| 
								 | 
							
								//    */
							 | 
						||
| 
								 | 
							
								//   handler: function(stream, code_point) {},
							 | 
						||
| 
								 | 
							
								// }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = Stream
							 | 
						||
| 
								 | 
							
								module.exports.DEFAULT_ENCODING = DEFAULT_ENCODING
							 | 
						||
| 
								 | 
							
								module.exports.getEncoding = getEncoding
							 |