@@ -20,6 +20,7 @@ const { FastBuffer } = require('internal/buffer');
2020const {
2121 ERR_ENCODING_NOT_SUPPORTED ,
2222 ERR_INVALID_ARG_TYPE ,
23+ ERR_ENCODING_INVALID_ENCODED_DATA ,
2324 ERR_INVALID_THIS ,
2425 ERR_NO_ICU ,
2526} = require ( 'internal/errors' ) . codes ;
@@ -30,11 +31,11 @@ const kEncoding = Symbol('encoding');
3031const kDecoder = Symbol ( 'decoder' ) ;
3132const kChunk = Symbol ( 'chunk' ) ;
3233const kFatal = Symbol ( 'kFatal' ) ;
33- const kUTF8FastPath = Symbol ( 'kUTF8FastPath ' ) ;
34+ const kUnicode = Symbol ( 'kUnicode ' ) ;
3435const kIgnoreBOM = Symbol ( 'kIgnoreBOM' ) ;
3536
3637const { isSinglebyteEncoding, createSinglebyteDecoder } = require ( 'internal/encoding/single-byte' ) ;
37- const { unfinishedBytesUtf8 , mergePrefixUtf8 } = require ( 'internal/encoding/util' ) ;
38+ const { unfinishedBytes , mergePrefix } = require ( 'internal/encoding/util' ) ;
3839
3940const {
4041 getConstructorOf,
@@ -419,11 +420,24 @@ if (hasIntl) {
419420
420421const kBOMSeen = Symbol ( 'BOM seen' ) ;
421422
422- let StringDecoder ;
423- function lazyStringDecoder ( ) {
424- if ( StringDecoder === undefined )
425- ( { StringDecoder } = require ( 'string_decoder' ) ) ;
426- return StringDecoder ;
423+ function fixupDecodedString ( res , ignoreBom , fatal , encoding ) {
424+ if ( res . length === 0 ) return '' ;
425+ if ( ! ignoreBom && res [ 0 ] === '\ufeff' ) res = res . slice ( 1 ) ;
426+ if ( ! fatal ) return res . toWellFormed ( ) ;
427+ if ( ! res . isWellFormed ( ) ) throw new ERR_ENCODING_INVALID_ENCODED_DATA ( encoding , undefined ) ;
428+ return res ;
429+ }
430+
431+ function decodeUTF16le ( input , ignoreBom , fatal ) {
432+ return fixupDecodedString ( parseInput ( input ) . ucs2Slice ( ) , ignoreBom , fatal , 'utf-16le' ) ;
433+ }
434+
435+ function decodeUTF16be ( input , ignoreBom , fatal ) {
436+ const be = parseInput ( input )
437+ const le = new FastBuffer ( be . length )
438+ le . set ( be )
439+ le . swap16 ( )
440+ return fixupDecodedString ( le . ucs2Slice ( ) , ignoreBom , fatal , 'utf-16be' ) ;
427441}
428442
429443class TextDecoder {
@@ -446,18 +460,27 @@ class TextDecoder {
446460 this [ kEncoding ] = enc ;
447461 this [ kIgnoreBOM ] = Boolean ( options ?. ignoreBOM ) ;
448462 this [ kFatal ] = Boolean ( options ?. fatal ) ;
449- this [ kUTF8FastPath ] = false ;
463+ this [ kUnicode ] = undefined ;
450464 this [ kHandle ] = undefined ;
451465 this [ kSingleByte ] = undefined ; // Does not care about streaming or BOM
452466 this [ kChunk ] = null ; // A copy of previous streaming tail or null
453467
454468 if ( enc === 'utf-8' ) {
455- this [ kUTF8FastPath ] = true ;
469+ this [ kUnicode ] = decodeUTF8 ;
470+ this [ kBOMSeen ] = false ;
471+ } else if ( enc === 'utf-16le' ) {
472+ this [ kUnicode ] = decodeUTF16le ;
473+ this [ kBOMSeen ] = false ;
474+ } else if ( enc === 'utf-16be' ) {
475+ this [ kUnicode ] = decodeUTF16be ;
456476 this [ kBOMSeen ] = false ;
457- } else if ( isSinglebyteEncoding ( enc ) ) {
458- this [ kSingleByte ] = createSinglebyteDecoder ( enc , this [ kFatal ] ) ;
459477 } else {
460- this . #prepareConverter( ) ; // Need to throw early if we don't support the encoding
478+ this [ kUnicode ] = false ;
479+ if ( isSinglebyteEncoding ( enc ) ) {
480+ this [ kSingleByte ] = createSinglebyteDecoder ( enc , this [ kFatal ] ) ;
481+ } else {
482+ this . #prepareConverter( ) ; // Need to throw early if we don't support the encoding
483+ }
461484 }
462485 }
463486
@@ -469,10 +492,6 @@ class TextDecoder {
469492 if ( handle === undefined )
470493 throw new ERR_ENCODING_NOT_SUPPORTED ( this [ kEncoding ] ) ;
471494 this [ kHandle ] = handle ;
472- } else if ( this [ kEncoding ] === 'utf-16le' ) {
473- if ( this [ kFatal ] ) throw new ERR_NO_ICU ( '"fatal" option' ) ;
474- this [ kHandle ] = new ( lazyStringDecoder ( ) ) ( this [ kEncoding ] ) ;
475- this [ kBOMSeen ] = false ;
476495 } else {
477496 throw new ERR_ENCODING_NOT_SUPPORTED ( this [ kEncoding ] ) ;
478497 }
@@ -485,19 +504,19 @@ class TextDecoder {
485504 if ( this [ kSingleByte ] ) return this [ kSingleByte ] ( parseInput ( input ) ) ;
486505
487506 const stream = options ?. stream ;
488- if ( this [ kUTF8FastPath ] ) {
507+ if ( this [ kUnicode ] ) {
489508 const chunk = this [ kChunk ] ;
490509 const ignoreBom = this [ kIgnoreBOM ] || this [ kBOMSeen ] ;
491510 if ( ! stream ) {
492511 this [ kBOMSeen ] = false ;
493- if ( ! chunk ) return decodeUTF8 ( input , ignoreBom , this [ kFatal ] ) ;
512+ if ( ! chunk ) return this [ kUnicode ] ( input , ignoreBom , this [ kFatal ] ) ;
494513 }
495514
496515 let u = parseInput ( input ) ;
497516 if ( u . length === 0 && stream ) return '' ; // no state change
498517 let prefix ;
499518 if ( chunk ) {
500- const merged = mergePrefixUtf8 ( u , this [ kChunk ] ) ;
519+ const merged = mergePrefix ( u , this [ kChunk ] , this [ kEncoding ] ) ;
501520 if ( u . length < 3 ) {
502521 u = merged ; // Might be unfinished, but fully consumed old u
503522 } else {
@@ -510,7 +529,7 @@ class TextDecoder {
510529 }
511530
512531 if ( stream ) {
513- const trail = unfinishedBytesUtf8 ( u , u . length ) ;
532+ const trail = unfinishedBytes ( u , u . length , this [ kEncoding ] ) ;
514533 if ( trail > 0 ) {
515534 this [ kChunk ] = new FastBuffer ( u . subarray ( - trail ) ) ; // copy
516535 if ( ! prefix && trail === u . length ) return '' ; // No further state change
@@ -519,8 +538,8 @@ class TextDecoder {
519538 }
520539
521540 try {
522- const res = ( prefix ? decodeUTF8 ( prefix , ignoreBom , this [ kFatal ] ) : '' ) +
523- decodeUTF8 ( u , ignoreBom || prefix , this [ kFatal ] ) ;
541+ const res = ( prefix ? this [ kUnicode ] ( prefix , ignoreBom , this [ kFatal ] ) : '' ) +
542+ this [ kUnicode ] ( u , ignoreBom || prefix , this [ kFatal ] ) ;
524543
525544 // "BOM seen" is set on the current decode call only if it did not error,
526545 // in "serialize I/O queue" after decoding
@@ -541,22 +560,7 @@ class TextDecoder {
541560 return icuDecode ( this [ kHandle ] , input , flags , this [ kEncoding ] ) ;
542561 }
543562
544- input = parseInput ( input ) ;
545-
546- let result = stream ? this [ kHandle ] . write ( input ) : this [ kHandle ] . end ( input ) ;
547-
548- if ( result . length > 0 && ! this [ kBOMSeen ] && ! this [ kIgnoreBOM ] ) {
549- // If the very first result in the stream is a BOM, and we are not
550- // explicitly told to ignore it, then we discard it.
551- if ( result [ 0 ] === '\ufeff' ) {
552- result = StringPrototypeSlice ( result , 1 ) ;
553- }
554- this [ kBOMSeen ] = true ;
555- }
556-
557- if ( ! stream ) this [ kBOMSeen ] = false ;
558-
559- return result ;
563+ // Unreachable
560564 }
561565}
562566
0 commit comments