• 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} = primordials;
29
30const { Buffer } = require('buffer');
31const {
32  kIncompleteCharactersStart,
33  kIncompleteCharactersEnd,
34  kMissingBytes,
35  kBufferedBytes,
36  kEncodingField,
37  kSize,
38  decode,
39  flush,
40  encodings
41} = internalBinding('string_decoder');
42const internalUtil = require('internal/util');
43const {
44  ERR_INVALID_ARG_TYPE,
45  ERR_UNKNOWN_ENCODING
46} = require('internal/errors').codes;
47const isEncoding = Buffer[internalUtil.kIsEncodingSymbol];
48
49const kNativeDecoder = Symbol('kNativeDecoder');
50
51// Do not cache `Buffer.isEncoding` when checking encoding names as some
52// modules monkey-patch it to support additional encodings
53function normalizeEncoding(enc) {
54  const nenc = internalUtil.normalizeEncoding(enc);
55  if (nenc === undefined) {
56    if (Buffer.isEncoding === isEncoding || !Buffer.isEncoding(enc))
57      throw new ERR_UNKNOWN_ENCODING(enc);
58    return enc;
59  }
60  return nenc;
61}
62
63const encodingsMap = {};
64for (let i = 0; i < encodings.length; ++i)
65  encodingsMap[encodings[i]] = i;
66
67// StringDecoder provides an interface for efficiently splitting a series of
68// buffers into a series of JS strings without breaking apart multi-byte
69// characters.
70function StringDecoder(encoding) {
71  this.encoding = normalizeEncoding(encoding);
72  this[kNativeDecoder] = Buffer.alloc(kSize);
73  this[kNativeDecoder][kEncodingField] = encodingsMap[this.encoding];
74}
75
76StringDecoder.prototype.write = function write(buf) {
77  if (typeof buf === 'string')
78    return buf;
79  if (!ArrayBufferIsView(buf))
80    throw new ERR_INVALID_ARG_TYPE('buf',
81                                   ['Buffer', 'TypedArray', 'DataView'],
82                                   buf);
83  return decode(this[kNativeDecoder], buf);
84};
85
86StringDecoder.prototype.end = function end(buf) {
87  let ret = '';
88  if (buf !== undefined)
89    ret = this.write(buf);
90  if (this[kNativeDecoder][kBufferedBytes] > 0)
91    ret += flush(this[kNativeDecoder]);
92  return ret;
93};
94
95/* Everything below this line is undocumented legacy stuff. */
96StringDecoder.prototype.text = function text(buf, offset) {
97  this[kNativeDecoder][kMissingBytes] = 0;
98  this[kNativeDecoder][kBufferedBytes] = 0;
99  return this.write(buf.slice(offset));
100};
101
102ObjectDefineProperties(StringDecoder.prototype, {
103  lastChar: {
104    configurable: true,
105    enumerable: true,
106    get() {
107      return this[kNativeDecoder].subarray(kIncompleteCharactersStart,
108                                           kIncompleteCharactersEnd);
109    }
110  },
111  lastNeed: {
112    configurable: true,
113    enumerable: true,
114    get() {
115      return this[kNativeDecoder][kMissingBytes];
116    }
117  },
118  lastTotal: {
119    configurable: true,
120    enumerable: true,
121    get() {
122      return this[kNativeDecoder][kBufferedBytes] +
123             this[kNativeDecoder][kMissingBytes];
124    }
125  }
126});
127
128exports.StringDecoder = StringDecoder;
129