1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23 24const { 25 ArrayBufferIsView, 26 ObjectDefineProperties, 27 Symbol, 28 TypedArrayPrototypeSubarray, 29} = primordials; 30 31const { Buffer } = require('buffer'); 32const { 33 kIncompleteCharactersStart, 34 kIncompleteCharactersEnd, 35 kMissingBytes, 36 kBufferedBytes, 37 kEncodingField, 38 kSize, 39 decode, 40 flush, 41 encodings 42} = internalBinding('string_decoder'); 43const internalUtil = require('internal/util'); 44const { 45 ERR_INVALID_ARG_TYPE, 46 ERR_UNKNOWN_ENCODING 47} = require('internal/errors').codes; 48const isEncoding = Buffer[internalUtil.kIsEncodingSymbol]; 49 50const kNativeDecoder = Symbol('kNativeDecoder'); 51 52// Do not cache `Buffer.isEncoding` when checking encoding names as some 53// modules monkey-patch it to support additional encodings 54function normalizeEncoding(enc) { 55 const nenc = internalUtil.normalizeEncoding(enc); 56 if (nenc === undefined) { 57 if (Buffer.isEncoding === isEncoding || !Buffer.isEncoding(enc)) 58 throw new ERR_UNKNOWN_ENCODING(enc); 59 return enc; 60 } 61 return nenc; 62} 63 64const encodingsMap = {}; 65for (let i = 0; i < encodings.length; ++i) 66 encodingsMap[encodings[i]] = i; 67 68// StringDecoder provides an interface for efficiently splitting a series of 69// buffers into a series of JS strings without breaking apart multi-byte 70// characters. 71function StringDecoder(encoding) { 72 this.encoding = normalizeEncoding(encoding); 73 this[kNativeDecoder] = Buffer.alloc(kSize); 74 this[kNativeDecoder][kEncodingField] = encodingsMap[this.encoding]; 75} 76 77StringDecoder.prototype.write = function write(buf) { 78 if (typeof buf === 'string') 79 return buf; 80 if (!ArrayBufferIsView(buf)) 81 throw new ERR_INVALID_ARG_TYPE('buf', 82 ['Buffer', 'TypedArray', 'DataView'], 83 buf); 84 return decode(this[kNativeDecoder], buf); 85}; 86 87StringDecoder.prototype.end = function end(buf) { 88 let ret = ''; 89 if (buf !== undefined) 90 ret = this.write(buf); 91 if (this[kNativeDecoder][kBufferedBytes] > 0) 92 ret += flush(this[kNativeDecoder]); 93 return ret; 94}; 95 96/* Everything below this line is undocumented legacy stuff. */ 97StringDecoder.prototype.text = function text(buf, offset) { 98 this[kNativeDecoder][kMissingBytes] = 0; 99 this[kNativeDecoder][kBufferedBytes] = 0; 100 return this.write(buf.slice(offset)); 101}; 102 103ObjectDefineProperties(StringDecoder.prototype, { 104 lastChar: { 105 configurable: true, 106 enumerable: true, 107 get() { 108 return TypedArrayPrototypeSubarray(this[kNativeDecoder], 109 kIncompleteCharactersStart, 110 kIncompleteCharactersEnd); 111 } 112 }, 113 lastNeed: { 114 configurable: true, 115 enumerable: true, 116 get() { 117 return this[kNativeDecoder][kMissingBytes]; 118 } 119 }, 120 lastTotal: { 121 configurable: true, 122 enumerable: true, 123 get() { 124 return this[kNativeDecoder][kBufferedBytes] + 125 this[kNativeDecoder][kMissingBytes]; 126 } 127 } 128}); 129 130exports.StringDecoder = StringDecoder; 131