From c619ac4a5a4cd9778bc28f8ba5bc998f96adaa9f Mon Sep 17 00:00:00 2001 From: Borewit Date: Sat, 21 Feb 2026 16:11:14 +0100 Subject: [PATCH] Replace `text-coded` with `@exodus/bytes` for text encoding and decoding --- lib/apev2/APEv2Parser.ts | 4 ++-- lib/common/FourCC.ts | 6 +++--- lib/id3v1/ID3v1Parser.ts | 4 ++-- lib/id3v2/FrameHeader.ts | 6 +++--- lib/lyrics3/Lyrics3.ts | 4 ++-- lib/mp4/MP4Parser.ts | 8 ++++---- lib/musepack/sv7/StreamVersion7.ts | 5 ++--- lib/ogg/vorbis/VorbisDecoder.ts | 4 ++-- package.json | 2 +- test/test-util.ts | 4 ++-- yarn.lock | 14 +++++++++++++- 11 files changed, 36 insertions(+), 25 deletions(-) diff --git a/lib/apev2/APEv2Parser.ts b/lib/apev2/APEv2Parser.ts index 122f8481..c809c4dc 100644 --- a/lib/apev2/APEv2Parser.ts +++ b/lib/apev2/APEv2Parser.ts @@ -18,7 +18,7 @@ import { } from './APEv2Token.js'; import { makeUnexpectedFileContentError } from '../ParseError.js'; import type { IRandomAccessTokenizer } from 'strtok3'; -import { textDecode } from '@borewit/text-codec'; +import { TextDecoder } from '@exodus/bytes/encoding.js'; const debug = initDebug('music-metadata:parser:APEv2'); @@ -172,7 +172,7 @@ export class APEv2Parser extends BasicParser { await this.tokenizer.readBuffer(picData); zero = util.findZero(picData); - const description = textDecode(picData.subarray(0, zero), 'utf-8'); + const description = new TextDecoder('utf-8').decode(picData.subarray(0, zero)); const data = picData.subarray(zero + 1); await this.metadata.addTag(tagFormat, key, { diff --git a/lib/common/FourCC.ts b/lib/common/FourCC.ts index 2ff81da1..209e8b60 100644 --- a/lib/common/FourCC.ts +++ b/lib/common/FourCC.ts @@ -1,5 +1,5 @@ import type { IToken } from 'strtok3'; -import { textDecode, textEncode } from '@borewit/text-codec'; +import { latin1toString, latin1fromString } from '@exodus/bytes/single-byte.js'; import * as util from './Util.js'; import { InternalParserError, FieldDecodingError } from '../ParseError.js'; @@ -14,7 +14,7 @@ export const FourCcToken: IToken = { len: 4, get: (buf: Uint8Array, off: number): string => { - const id = textDecode(buf.subarray(off, off + FourCcToken.len), 'latin1'); + const id = latin1toString(buf.subarray(off, off + FourCcToken.len)); if (!id.match(validFourCC)) { throw new FieldDecodingError(`FourCC contains invalid characters: ${util.a2hex(id)} "${id}"`); } @@ -22,7 +22,7 @@ export const FourCcToken: IToken = { }, put: (buffer: Uint8Array, offset: number, id: string) => { - const str = textEncode(id, 'latin1'); + const str = latin1fromString(id); if (str.length !== 4) throw new InternalParserError('Invalid length'); buffer.set(str, offset); diff --git a/lib/id3v1/ID3v1Parser.ts b/lib/id3v1/ID3v1Parser.ts index b5a2d72f..938ad92e 100644 --- a/lib/id3v1/ID3v1Parser.ts +++ b/lib/id3v1/ID3v1Parser.ts @@ -9,7 +9,7 @@ import { APEv2Parser } from '../apev2/APEv2Parser.js'; import type { AnyTagValue, IApeHeader, IPrivateOptions } from '../type.js'; import type { INativeMetadataCollector } from '../common/MetadataCollector.js'; -import { textDecode } from '@borewit/text-codec'; +import { latin1toString } from '@exodus/bytes/single-byte.js'; const debug = initDebug('music-metadata:parser:ID3v1'); @@ -173,7 +173,7 @@ export async function hasID3v1Header(tokenizer: IRandomAccessTokenizer): Promise const position = tokenizer.position; await tokenizer.readBuffer(tag, {position: tokenizer.fileInfo.size - 128}); tokenizer.setPosition(position); // Restore tokenizer position - return textDecode(tag, 'latin1') === 'TAG'; + return latin1toString(tag) === 'TAG'; } return false; } diff --git a/lib/id3v2/FrameHeader.ts b/lib/id3v2/FrameHeader.ts index 98b72a19..b176c9e3 100644 --- a/lib/id3v2/FrameHeader.ts +++ b/lib/id3v2/FrameHeader.ts @@ -3,7 +3,7 @@ import * as Token from 'token-types'; import * as util from '../common/Util.js'; import { UINT32SYNCSAFE, type ID3v2MajorVersion } from './ID3v2Token.js'; import type { IWarningCollector } from '../common/MetadataCollector.js'; -import { textDecode } from '@borewit/text-codec'; +import { TextDecoder } from '@exodus/bytes/encoding.js'; import { Id3v2ContentError } from './FrameParser.js'; export interface IFrameFlags { @@ -77,7 +77,7 @@ export function readFrameHeader(uint8Array: Uint8Array, majorVer: ID3v2MajorVers function parseFrameHeaderV22(uint8Array: Uint8Array, majorVer: 2, warningCollector: IWarningCollector): IFrameHeader { const header: IFrameHeader = { - id: textDecode(uint8Array.subarray(0, 3), 'ascii'), + id: new TextDecoder('ascii').decode(uint8Array.subarray(0, 3)), length: Token.UINT24_BE.get(uint8Array, 3) }; @@ -90,7 +90,7 @@ function parseFrameHeaderV22(uint8Array: Uint8Array, majorVer: 2, warningCollect function parseFrameHeaderV23V24(uint8Array: Uint8Array, majorVer: 3 | 4, warningCollector: IWarningCollector): IFrameHeader { const header: IFrameHeader = { - id: textDecode(uint8Array.subarray(0, 4), 'ascii'), + id: new TextDecoder('ascii').decode(uint8Array.subarray(0, 4)), length: (majorVer === 4 ? UINT32SYNCSAFE : Token.UINT32_BE).get(uint8Array, 4), flags: readFrameFlags(uint8Array.subarray(8, 10)) }; diff --git a/lib/lyrics3/Lyrics3.ts b/lib/lyrics3/Lyrics3.ts index 9dea3622..58f04839 100644 --- a/lib/lyrics3/Lyrics3.ts +++ b/lib/lyrics3/Lyrics3.ts @@ -1,5 +1,5 @@ import type {IRandomAccessTokenizer} from 'strtok3'; -import { textDecode } from '@borewit/text-codec'; +import { latin1toString } from '@exodus/bytes/single-byte.js'; export const endTag2 = 'LYRICS200'; @@ -10,7 +10,7 @@ export async function getLyricsHeaderLength(tokenizer: IRandomAccessTokenizer): const position = tokenizer.position; await tokenizer.readBuffer(buf, {position: fileSize - 143}); tokenizer.setPosition(position); // Restore position - const txt = textDecode(buf, 'latin1'); + const txt = latin1toString(buf); const tag = txt.substring(6); if (tag === endTag2) { return Number.parseInt(txt.substring(0, 6), 10) + 15; diff --git a/lib/mp4/MP4Parser.ts b/lib/mp4/MP4Parser.ts index 05eb967b..4ca3c99f 100644 --- a/lib/mp4/MP4Parser.ts +++ b/lib/mp4/MP4Parser.ts @@ -11,7 +11,7 @@ import { type AnyTagValue, type IChapter, type ITrackInfo, TrackType } from '../ import type { IGetToken } from '@tokenizer/token'; import { uint8ArrayToHex } from 'uint8array-extras'; -import { textDecode } from '@borewit/text-codec'; +import { TextDecoder } from '@exodus/bytes/encoding.js'; const debug = initDebug('music-metadata:parser:MP4'); const tagFormat = 'iTunes'; @@ -380,7 +380,7 @@ export class MP4Parser extends BasicParser { default: { const uint8Array = await this.tokenizer.readToken(new Token.Uint8ArrayType(payLoadLength)); - this.addWarning(`Unsupported meta-item: ${tagKey}[${child.header.name}] => value=${uint8ArrayToHex(uint8Array)} ascii=${textDecode(uint8Array, 'ascii')}`); + this.addWarning(`Unsupported meta-item: ${tagKey}[${child.header.name}] => value=${uint8ArrayToHex(uint8Array)} ascii=${new TextDecoder('ascii').decode(uint8Array)}`); } } @@ -418,7 +418,7 @@ export class MP4Parser extends BasicParser { } case 'rate': { - const rate = textDecode(dataAtom.value, 'ascii'); + const rate = new TextDecoder('ascii').decode(dataAtom.value); await this.addTag(tagKey, rate); break; } @@ -430,7 +430,7 @@ export class MP4Parser extends BasicParser { case 1: // UTF-8: Without any count or NULL terminator case 18: // Unknown: Found in m4b in combination with a '©gen' tag - await this.addTag(tagKey, textDecode(dataAtom.value)); + await this.addTag(tagKey, new TextDecoder().decode(dataAtom.value)); break; case 13: // JPEG diff --git a/lib/musepack/sv7/StreamVersion7.ts b/lib/musepack/sv7/StreamVersion7.ts index 2f4c87a0..03b3ca0e 100644 --- a/lib/musepack/sv7/StreamVersion7.ts +++ b/lib/musepack/sv7/StreamVersion7.ts @@ -1,8 +1,7 @@ import * as Token from 'token-types'; import type { IGetToken } from 'strtok3'; - import * as util from '../../common/Util.js'; -import { textDecode } from '@borewit/text-codec'; +import { latin1toString } from '@exodus/bytes/single-byte.js'; /** * MusePack stream version 7 format specification @@ -45,7 +44,7 @@ export const Header: IGetToken = { const header = { // word 0 - signature: textDecode(buf.subarray(off, off + 3), 'latin1'), + signature: latin1toString(buf.subarray(off, off + 3)), // versionIndex number * 1000 (3.81 = 3810) (remember that 4-byte alignment causes this to take 4-bytes) streamMinorVersion: util.getBitAllignedNumber(buf, off + 3, 0, 4), streamMajorVersion: util.getBitAllignedNumber(buf, off + 3, 4, 4), diff --git a/lib/ogg/vorbis/VorbisDecoder.ts b/lib/ogg/vorbis/VorbisDecoder.ts index 083fee7a..94d84058 100644 --- a/lib/ogg/vorbis/VorbisDecoder.ts +++ b/lib/ogg/vorbis/VorbisDecoder.ts @@ -1,5 +1,5 @@ import * as Token from 'token-types'; -import { textDecode } from '@borewit/text-codec'; +import { TextDecoder } from '@exodus/bytes/encoding.js'; export class VorbisDecoder { private readonly data: Uint8Array; @@ -19,7 +19,7 @@ export class VorbisDecoder { public readStringUtf8(): string { const len = this.readInt32(); - const value = textDecode(this.data.subarray(this.offset, this.offset + len), 'utf-8'); + const value = new TextDecoder().decode(this.data.subarray(this.offset, this.offset + len)); this.offset += len; return value; } diff --git a/package.json b/package.json index 1ac84844..25c8d2e8 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "update-biome": "yarn add -D --exact @biomejs/biome && npx @biomejs/biome migrate --write" }, "dependencies": { - "@borewit/text-codec": "^0.2.1", + "@exodus/bytes": "^1.14.1", "@tokenizer/token": "^0.3.0", "content-type": "^1.0.5", "debug": "^4.4.3", diff --git a/test/test-util.ts b/test/test-util.ts index 07f86a1c..e2b33d2d 100644 --- a/test/test-util.ts +++ b/test/test-util.ts @@ -3,7 +3,7 @@ import { assert } from 'chai'; import * as util from '../lib/common/Util.js'; import { FourCcToken } from '../lib/common/FourCC.js'; -import { textDecode } from '@borewit/text-codec'; +import { latin1toString } from '@exodus/bytes/single-byte.js'; const t = assert; @@ -72,7 +72,7 @@ describe('shared utility functionality', () => { it('should be able to encode FourCC token', () => { const buffer = new Uint8Array(4); FourCcToken.put(buffer, 0, 'abcd'); - t.deepEqual(textDecode(buffer, 'latin1'), 'abcd'); + t.deepEqual(latin1toString(buffer), 'abcd'); }); }); diff --git a/yarn.lock b/yarn.lock index f34dd032..54d1aa61 100644 --- a/yarn.lock +++ b/yarn.lock @@ -144,6 +144,18 @@ __metadata: languageName: node linkType: hard +"@exodus/bytes@npm:^1.14.1": + version: 1.14.1 + resolution: "@exodus/bytes@npm:1.14.1" + peerDependencies: + "@noble/hashes": ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + "@noble/hashes": + optional: true + checksum: 10c0/486dad30992a8c81058b6b59341ee934c10a7e8016b440770de0f86d2e270950c5d37fc6724ea017295b8654c7564abf6a21cc49bed569d74721b545f571e416 + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -2206,7 +2218,7 @@ __metadata: resolution: "music-metadata@workspace:." dependencies: "@biomejs/biome": "npm:2.4.0" - "@borewit/text-codec": "npm:^0.2.1" + "@exodus/bytes": "npm:^1.14.1" "@tokenizer/token": "npm:^0.3.0" "@types/chai": "npm:^5.2.3" "@types/chai-as-promised": "npm:^8.0.2"