This commit is contained in:
2025-01-04 00:34:03 +01:00
parent 41829408dc
commit 0ca14bbc19
18111 changed files with 1871397 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
'use strict'
const ModuleError = require('module-error')
const formats = new Set(['buffer', 'view', 'utf8'])
/**
* @template TIn, TFormat, TOut
* @abstract
*/
class Encoding {
/**
* @param {IEncoding<TIn,TFormat,TOut>} options
*/
constructor (options) {
/** @type {(data: TIn) => TFormat} */
this.encode = options.encode || this.encode
/** @type {(data: TFormat) => TOut} */
this.decode = options.decode || this.decode
/** @type {string} */
this.name = options.name || this.name
/** @type {string} */
this.format = options.format || this.format
if (typeof this.encode !== 'function') {
throw new TypeError("The 'encode' property must be a function")
}
if (typeof this.decode !== 'function') {
throw new TypeError("The 'decode' property must be a function")
}
this.encode = this.encode.bind(this)
this.decode = this.decode.bind(this)
if (typeof this.name !== 'string' || this.name === '') {
throw new TypeError("The 'name' property must be a string")
}
if (typeof this.format !== 'string' || !formats.has(this.format)) {
throw new TypeError("The 'format' property must be one of 'buffer', 'view', 'utf8'")
}
if (options.createViewTranscoder) {
this.createViewTranscoder = options.createViewTranscoder
}
if (options.createBufferTranscoder) {
this.createBufferTranscoder = options.createBufferTranscoder
}
if (options.createUTF8Transcoder) {
this.createUTF8Transcoder = options.createUTF8Transcoder
}
}
get commonName () {
return /** @type {string} */ (this.name.split('+')[0])
}
/** @return {BufferFormat<TIn,TOut>} */
createBufferTranscoder () {
throw new ModuleError(`Encoding '${this.name}' cannot be transcoded to 'buffer'`, {
code: 'LEVEL_ENCODING_NOT_SUPPORTED'
})
}
/** @return {ViewFormat<TIn,TOut>} */
createViewTranscoder () {
throw new ModuleError(`Encoding '${this.name}' cannot be transcoded to 'view'`, {
code: 'LEVEL_ENCODING_NOT_SUPPORTED'
})
}
/** @return {UTF8Format<TIn,TOut>} */
createUTF8Transcoder () {
throw new ModuleError(`Encoding '${this.name}' cannot be transcoded to 'utf8'`, {
code: 'LEVEL_ENCODING_NOT_SUPPORTED'
})
}
}
exports.Encoding = Encoding
/**
* @typedef {import('./encoding').IEncoding<TIn,TFormat,TOut>} IEncoding
* @template TIn, TFormat, TOut
*/
/**
* @typedef {import('./formats').BufferFormat<TIn,TOut>} BufferFormat
* @template TIn, TOut
*/
/**
* @typedef {import('./formats').ViewFormat<TIn,TOut>} ViewFormat
* @template TIn, TOut
*/
/**
* @typedef {import('./formats').UTF8Format<TIn,TOut>} UTF8Format
* @template TIn, TOut
*/

View File

@@ -0,0 +1,135 @@
'use strict'
const { Buffer } = require('buffer') || { Buffer: { isBuffer: () => false } }
const { textEncoder, textDecoder } = require('./text-endec')()
const { BufferFormat, ViewFormat, UTF8Format } = require('./formats')
/** @type {<T>(v: T) => v} */
const identity = (v) => v
/**
* @type {typeof import('./encodings').utf8}
*/
exports.utf8 = new UTF8Format({
encode: function (data) {
// On node 16.9.1 buffer.toString() is 5x faster than TextDecoder
return Buffer.isBuffer(data)
? data.toString('utf8')
: ArrayBuffer.isView(data)
? textDecoder.decode(data)
: String(data)
},
decode: identity,
name: 'utf8',
createViewTranscoder () {
return new ViewFormat({
encode: function (data) {
return ArrayBuffer.isView(data) ? data : textEncoder.encode(data)
},
decode: function (data) {
return textDecoder.decode(data)
},
name: `${this.name}+view`
})
},
createBufferTranscoder () {
return new BufferFormat({
encode: function (data) {
return Buffer.isBuffer(data)
? data
: ArrayBuffer.isView(data)
? Buffer.from(data.buffer, data.byteOffset, data.byteLength)
: Buffer.from(String(data), 'utf8')
},
decode: function (data) {
return data.toString('utf8')
},
name: `${this.name}+buffer`
})
}
})
/**
* @type {typeof import('./encodings').json}
*/
exports.json = new UTF8Format({
encode: JSON.stringify,
decode: JSON.parse,
name: 'json'
})
/**
* @type {typeof import('./encodings').buffer}
*/
exports.buffer = new BufferFormat({
encode: function (data) {
return Buffer.isBuffer(data)
? data
: ArrayBuffer.isView(data)
? Buffer.from(data.buffer, data.byteOffset, data.byteLength)
: Buffer.from(String(data), 'utf8')
},
decode: identity,
name: 'buffer',
createViewTranscoder () {
return new ViewFormat({
encode: function (data) {
return ArrayBuffer.isView(data) ? data : Buffer.from(String(data), 'utf8')
},
decode: function (data) {
return Buffer.from(data.buffer, data.byteOffset, data.byteLength)
},
name: `${this.name}+view`
})
}
})
/**
* @type {typeof import('./encodings').view}
*/
exports.view = new ViewFormat({
encode: function (data) {
return ArrayBuffer.isView(data) ? data : textEncoder.encode(data)
},
decode: identity,
name: 'view',
createBufferTranscoder () {
return new BufferFormat({
encode: function (data) {
return Buffer.isBuffer(data)
? data
: ArrayBuffer.isView(data)
? Buffer.from(data.buffer, data.byteOffset, data.byteLength)
: Buffer.from(String(data), 'utf8')
},
decode: identity,
name: `${this.name}+buffer`
})
}
})
/**
* @type {typeof import('./encodings').hex}
*/
exports.hex = new BufferFormat({
encode: function (data) {
return Buffer.isBuffer(data) ? data : Buffer.from(String(data), 'hex')
},
decode: function (buffer) {
return buffer.toString('hex')
},
name: 'hex'
})
/**
* @type {typeof import('./encodings').base64}
*/
exports.base64 = new BufferFormat({
encode: function (data) {
return Buffer.isBuffer(data) ? data : Buffer.from(String(data), 'base64')
},
decode: function (buffer) {
return buffer.toString('base64')
},
name: 'base64'
})

View File

@@ -0,0 +1,111 @@
'use strict'
const { Buffer } = require('buffer') || {}
const { Encoding } = require('./encoding')
const textEndec = require('./text-endec')
/**
* @template TIn, TOut
* @extends {Encoding<TIn,Buffer,TOut>}
*/
class BufferFormat extends Encoding {
/**
* @param {Omit<IEncoding<TIn, Buffer, TOut>, 'format'>} options
*/
constructor (options) {
super({ ...options, format: 'buffer' })
}
/** @override */
createViewTranscoder () {
return new ViewFormat({
encode: this.encode, // Buffer is a view (UInt8Array)
decode: (data) => this.decode(
Buffer.from(data.buffer, data.byteOffset, data.byteLength)
),
name: `${this.name}+view`
})
}
/** @override */
createBufferTranscoder () {
return this
}
}
/**
* @extends {Encoding<TIn,Uint8Array,TOut>}
* @template TIn, TOut
*/
class ViewFormat extends Encoding {
/**
* @param {Omit<IEncoding<TIn, Uint8Array, TOut>, 'format'>} options
*/
constructor (options) {
super({ ...options, format: 'view' })
}
/** @override */
createBufferTranscoder () {
return new BufferFormat({
encode: (data) => {
const view = this.encode(data)
return Buffer.from(view.buffer, view.byteOffset, view.byteLength)
},
decode: this.decode, // Buffer is a view (UInt8Array)
name: `${this.name}+buffer`
})
}
/** @override */
createViewTranscoder () {
return this
}
}
/**
* @extends {Encoding<TIn,string,TOut>}
* @template TIn, TOut
*/
class UTF8Format extends Encoding {
/**
* @param {Omit<IEncoding<TIn, string, TOut>, 'format'>} options
*/
constructor (options) {
super({ ...options, format: 'utf8' })
}
/** @override */
createBufferTranscoder () {
return new BufferFormat({
encode: (data) => Buffer.from(this.encode(data), 'utf8'),
decode: (data) => this.decode(data.toString('utf8')),
name: `${this.name}+buffer`
})
}
/** @override */
createViewTranscoder () {
const { textEncoder, textDecoder } = textEndec()
return new ViewFormat({
encode: (data) => textEncoder.encode(this.encode(data)),
decode: (data) => this.decode(textDecoder.decode(data)),
name: `${this.name}+view`
})
}
/** @override */
createUTF8Transcoder () {
return this
}
}
exports.BufferFormat = BufferFormat
exports.ViewFormat = ViewFormat
exports.UTF8Format = UTF8Format
/**
* @typedef {import('./encoding').IEncoding<TIn,TFormat,TOut>} IEncoding
* @template TIn, TFormat, TOut
*/

View File

@@ -0,0 +1,19 @@
'use strict'
/** @type {{ textEncoder: TextEncoder, textDecoder: TextDecoder }|null} */
let lazy = null
/**
* Get semi-global instances of TextEncoder and TextDecoder.
* @returns {{ textEncoder: TextEncoder, textDecoder: TextDecoder }}
*/
module.exports = function () {
if (lazy === null) {
lazy = {
textEncoder: new TextEncoder(),
textDecoder: new TextDecoder()
}
}
return lazy
}