• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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