TextEncoder.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import Stream, { DEFAULT_ENCODING, getEncoding } from './text_decoder_index.js'
  2. import { end_of_stream, finished, stringToCodePoints } from './text_decoder_utils.js'
  3. import { encoders } from './table.js'
  4. // 8.2 Interface TextEncoder
  5. class TextEncoder {
  6. /**
  7. * @param {string=} label The label of the encoding. NONSTANDARD.
  8. * @param {Object=} [options] NONSTANDARD.
  9. */
  10. constructor(label, options = {}) {
  11. // A TextEncoder object has an associated encoding and encoder.
  12. /** @private */
  13. this._encoding = null
  14. /** @private @type {?Encoder} */
  15. this._encoder = null
  16. // Non-standard
  17. /** @private @type {boolean} */
  18. this._do_not_flush = false
  19. /** @private @type {string} */
  20. this._fatal = options['fatal'] ? 'fatal' : 'replacement'
  21. // 2. Set enc's encoding to UTF-8's encoder.
  22. if (options['NONSTANDARD_allowLegacyEncoding']) {
  23. // NONSTANDARD behavior.
  24. label = label !== undefined ? String(label) : DEFAULT_ENCODING
  25. var encoding = getEncoding(label)
  26. if (encoding === null || encoding.name === 'replacement')
  27. throw RangeError('Unknown encoding: ' + label)
  28. if (!encoders[encoding.name]) {
  29. throw Error('Encoder not present.' +
  30. ' Did you forget to include encoding-indexes.js first?')
  31. }
  32. this._encoding = encoding
  33. } else {
  34. // Standard behavior.
  35. this._encoding = getEncoding('utf-8')
  36. if (label !== undefined && 'console' in global) {
  37. console.warn('TextEncoder constructor called with encoding label, '
  38. + 'which is ignored.')
  39. }
  40. }
  41. }
  42. get encoding() {
  43. return this._encoding.name.toLowerCase()
  44. }
  45. /**
  46. * @param {string=} opt_string The string to encode.
  47. * @param {Object=} options
  48. */
  49. encode(opt_string = '', options = {}) {
  50. // NOTE: This option is nonstandard. None of the encodings
  51. // permitted for encoding (i.e. UTF-8, UTF-16) are stateful when
  52. // the input is a USVString so streaming is not necessary.
  53. if (!this._do_not_flush)
  54. this._encoder = encoders[this._encoding.name]({
  55. fatal: this._fatal === 'fatal' })
  56. this._do_not_flush = Boolean(options['stream'])
  57. // 1. Convert input to a stream.
  58. const input = new Stream(stringToCodePoints(opt_string))
  59. // 2. Let output be a new stream
  60. const output = []
  61. /** @type {?(number|!Array.<number>)} */
  62. var result
  63. // 3. While true, run these substeps:
  64. while (true) {
  65. // 1. Let token be the result of reading from input.
  66. var token = input.read()
  67. if (token === end_of_stream)
  68. break
  69. // 2. Let result be the result of processing token for encoder,
  70. // input, output.
  71. result = this._encoder.handler(input, token)
  72. if (result === finished)
  73. break
  74. if (Array.isArray(result))
  75. output.push.apply(output, /**@type {!Array.<number>}*/(result))
  76. else
  77. output.push(result)
  78. }
  79. // TODO: Align with spec algorithm.
  80. if (!this._do_not_flush) {
  81. while (true) {
  82. result = this._encoder.handler(input, input.read())
  83. if (result === finished)
  84. break
  85. if (Array.isArray(result))
  86. output.push.apply(output, /**@type {!Array.<number>}*/(result))
  87. else
  88. output.push(result)
  89. }
  90. this._encoder = null
  91. }
  92. // 3. If result is finished, convert output into a byte sequence,
  93. // and then return a Uint8Array object wrapping an ArrayBuffer
  94. // containing output.
  95. return new Uint8Array(output)
  96. }
  97. }
  98. export {TextEncoder}