aboutsummaryrefslogtreecommitdiffstats
path: root/node_modules/iconv-lite/encodings/internal.js
blob: 4e5c3ff25ac158bba365548767e9fdd815a1788c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
"use strict"
var Buffer = require("safer-buffer").Buffer

// Export Node.js internal encodings.

module.exports = {
  // Encodings
  utf8: { type: "_internal", bomAware: true },
  cesu8: { type: "_internal", bomAware: true },
  unicode11utf8: "utf8",

  ucs2: { type: "_internal", bomAware: true },
  utf16le: "ucs2",

  binary: { type: "_internal" },
  base64: { type: "_internal" },
  hex: { type: "_internal" },

  // Codec.
  _internal: InternalCodec
}

// ------------------------------------------------------------------------------

function InternalCodec (codecOptions, iconv) {
  this.enc = codecOptions.encodingName
  this.bomAware = codecOptions.bomAware

  if (this.enc === "base64") { this.encoder = InternalEncoderBase64 } else if (this.enc === "utf8") { this.encoder = InternalEncoderUtf8 } else if (this.enc === "cesu8") {
    this.enc = "utf8" // Use utf8 for decoding.
    this.encoder = InternalEncoderCesu8

    // Add decoder for versions of Node not supporting CESU-8
    if (Buffer.from("eda0bdedb2a9", "hex").toString() !== "💩") {
      this.decoder = InternalDecoderCesu8
      this.defaultCharUnicode = iconv.defaultCharUnicode
    }
  }
}

InternalCodec.prototype.encoder = InternalEncoder
InternalCodec.prototype.decoder = InternalDecoder

// ------------------------------------------------------------------------------

// We use node.js internal decoder. Its signature is the same as ours.
var StringDecoder = require("string_decoder").StringDecoder

function InternalDecoder (options, codec) {
  this.decoder = new StringDecoder(codec.enc)
}

InternalDecoder.prototype.write = function (buf) {
  if (!Buffer.isBuffer(buf)) {
    buf = Buffer.from(buf)
  }

  return this.decoder.write(buf)
}

InternalDecoder.prototype.end = function () {
  return this.decoder.end()
}

// ------------------------------------------------------------------------------
// Encoder is mostly trivial

function InternalEncoder (options, codec) {
  this.enc = codec.enc
}

InternalEncoder.prototype.write = function (str) {
  return Buffer.from(str, this.enc)
}

InternalEncoder.prototype.end = function () {
}

// ------------------------------------------------------------------------------
// Except base64 encoder, which must keep its state.

function InternalEncoderBase64 (options, codec) {
  this.prevStr = ""
}

InternalEncoderBase64.prototype.write = function (str) {
  str = this.prevStr + str
  var completeQuads = str.length - (str.length % 4)
  this.prevStr = str.slice(completeQuads)
  str = str.slice(0, completeQuads)

  return Buffer.from(str, "base64")
}

InternalEncoderBase64.prototype.end = function () {
  return Buffer.from(this.prevStr, "base64")
}

// ------------------------------------------------------------------------------
// CESU-8 encoder is also special.

function InternalEncoderCesu8 (options, codec) {
}

InternalEncoderCesu8.prototype.write = function (str) {
  var buf = Buffer.alloc(str.length * 3); var bufIdx = 0
  for (var i = 0; i < str.length; i++) {
    var charCode = str.charCodeAt(i)
    // Naive implementation, but it works because CESU-8 is especially easy
    // to convert from UTF-16 (which all JS strings are encoded in).
    if (charCode < 0x80) { buf[bufIdx++] = charCode } else if (charCode < 0x800) {
      buf[bufIdx++] = 0xC0 + (charCode >>> 6)
      buf[bufIdx++] = 0x80 + (charCode & 0x3f)
    } else { // charCode will always be < 0x10000 in javascript.
      buf[bufIdx++] = 0xE0 + (charCode >>> 12)
      buf[bufIdx++] = 0x80 + ((charCode >>> 6) & 0x3f)
      buf[bufIdx++] = 0x80 + (charCode & 0x3f)
    }
  }
  return buf.slice(0, bufIdx)
}

InternalEncoderCesu8.prototype.end = function () {
}

// ------------------------------------------------------------------------------
// CESU-8 decoder is not implemented in Node v4.0+

function InternalDecoderCesu8 (options, codec) {
  this.acc = 0
  this.contBytes = 0
  this.accBytes = 0
  this.defaultCharUnicode = codec.defaultCharUnicode
}

InternalDecoderCesu8.prototype.write = function (buf) {
  var acc = this.acc; var contBytes = this.contBytes; var accBytes = this.accBytes
  var res = ""
  for (var i = 0; i < buf.length; i++) {
    var curByte = buf[i]
    if ((curByte & 0xC0) !== 0x80) { // Leading byte
      if (contBytes > 0) { // Previous code is invalid
        res += this.defaultCharUnicode
        contBytes = 0
      }

      if (curByte < 0x80) { // Single-byte code
        res += String.fromCharCode(curByte)
      } else if (curByte < 0xE0) { // Two-byte code
        acc = curByte & 0x1F
        contBytes = 1; accBytes = 1
      } else if (curByte < 0xF0) { // Three-byte code
        acc = curByte & 0x0F
        contBytes = 2; accBytes = 1
      } else { // Four or more are not supported for CESU-8.
        res += this.defaultCharUnicode
      }
    } else { // Continuation byte
      if (contBytes > 0) { // We're waiting for it.
        acc = (acc << 6) | (curByte & 0x3f)
        contBytes--; accBytes++
        if (contBytes === 0) {
          // Check for overlong encoding, but support Modified UTF-8 (encoding NULL as C0 80)
          if (accBytes === 2 && acc < 0x80 && acc > 0) {
            res += this.defaultCharUnicode
          } else if (accBytes === 3 && acc < 0x800) {
            res += this.defaultCharUnicode
          } else {
            // Actually add character.
            res += String.fromCharCode(acc)
          }
        }
      } else { // Unexpected continuation byte
        res += this.defaultCharUnicode
      }
    }
  }
  this.acc = acc; this.contBytes = contBytes; this.accBytes = accBytes
  return res
}

InternalDecoderCesu8.prototype.end = function () {
  var res = 0
  if (this.contBytes > 0) { res += this.defaultCharUnicode }
  return res
}

// ------------------------------------------------------------------------------
// check the chunk boundaries for surrogate pair

function InternalEncoderUtf8 (options, codec) {
  this.highSurrogate = ""
}

InternalEncoderUtf8.prototype.write = function (str) {
  if (this.highSurrogate) {
    str = this.highSurrogate + str
    this.highSurrogate = ""
  }

  if (str.length > 0) {
    var charCode = str.charCodeAt(str.length - 1)
    if (charCode >= 0xd800 && charCode < 0xdc00) {
      this.highSurrogate = str[str.length - 1]
      str = str.slice(0, str.length - 1)
    }
  }

  return Buffer.from(str, this.enc)
}

InternalEncoderUtf8.prototype.end = function () {
  if (this.highSurrogate) {
    var str = this.highSurrogate
    this.highSurrogate = ""
    return Buffer.from(str, this.enc)
  }
}