Skip to content

Commit

Permalink
feat: convert most Buffer usage to Uint8Array (#2103)
Browse files Browse the repository at this point in the history
Convert most Buffer usage to Uint8Array and `uint8array-extras` to fill functionality gaps
* Update `token-types` to version 6.0.0
* Update `strtok3`  to version 7.1.0
* Update `file-type` to version 19.1.0
* Update dependency diagram to desired Node.js free via primary entry point

Co-authored-by: Borewit <[email protected]>
  • Loading branch information
bjornstar and Borewit committed Jul 8, 2024
1 parent 06d9425 commit eb0f8e6
Show file tree
Hide file tree
Showing 55 changed files with 397 additions and 365 deletions.
36 changes: 17 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,28 +105,26 @@ import * as mm from 'music-metadata/lib/core';
Dependency diagram:
```mermaid
graph TD;
MM(music-metadata)-->S(strtok3)
MM-->TY(token-types)
MM-->FT(file-type)
MMN("music-metadata (Node.js entry point)")-->MMP
MMN-->FTN
MMP("music-metadata (primary entry point)")-->S(strtok3)
MMP-->TY(token-types)
MMP-->FTP
MMP-->UAE
FTN("file-type (Node.js entry point)")-->FTP
FTP("file-type (primary entry point)")-->S
S(strtok3)-->P(peek-readable)
S-->TO("@tokenizer/token")
TY-->TO
S(strtok3)-->TO("@tokenizer/token")
TY(token-types)-->TO
TY-->IE("ieee754")
FT-->RWNS(readable-web-to-node-stream)
FT-->S
FT-->TY
TY-->NB(node:buffer)
RWNS-->RS(readable-stream)
RS-->SD(string_decoder)
SD-->SB(safe-buffer)
RS-->UD(util-deprecate)
RS-->I(inherits)
style NB fill:#F88,stroke:#A44
style SB fill:#F88,stroke:#A44
style SD fill:#CCC,stroke:#888
FTP-->TY
NS("node:stream")
FTN-->NS
FTP-->UAE(uint8array-extras)
style NS fill:#F88,stroke:#A44
style IE fill:#CCC,stroke:#888
style UD fill:#CCC,stroke:#888
style I fill:#CCC,stroke:#888
style FTN fill:#FAA,stroke:#A44
style MMN fill:#FAA,stroke:#A44
```

Dependency list:
Expand Down
2 changes: 1 addition & 1 deletion doc/common_metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The tag mapping is strongly inspired on the [MusicBrainz Picard tag-mapping](htt
| genre | * | Genres | genre | TCO | TCON, TXXX:STYLE | TCON, TXXX:STYLE | ©GEN, GNRE | GENRE, STYLE | GENRE | WM/Genre | GNRE, IGNR | TRACK:GENRE | |
| picture | * | Embedded cover art | | PIC | APIC | APIC | COVR | METADATA_BLOCK_PICTURE | COVER ART (FRONT), COVER ART (BACK) | WM/Picture | | PICTURE | |
| composer | * | Composer | | TCM | TCOM | TCOM | ©WRT | COMPOSER | COMPOSER | WM/Composer | | | |
| lyrics | * | Lyricist | | | USLT:DESCRIPTION, SYLT | USLT:DESCRIPTION, SYLT | ©LYR | LYRICS | LYRICS | WM/Lyrics | | | |
| lyrics | * | Lyricist | | | USLT, SYLT | USLT, SYLT | ©LYR | LYRICS | LYRICS | WM/Lyrics | | | |
| albumsort | 1 | Album title, formatted for alphabetic ordering | | TSA | TSOA | TSOA | SOAL | ALBUMSORT | ALBUMSORT | WM/AlbumSortOrder | | | |
| titlesort | 1 | Track title, formatted for alphabetic ordering | | TST | TSOT | TSOT | SONM | TITLESORT | TITLESORT | WM/TitleSortOrder | | | |
| work | 1 | The canonical title of the [work](https://musicbrainz.org/doc/Work) | | | | | ©WRK | WORK | WORK | WM/Work | | | |
Expand Down
3 changes: 1 addition & 2 deletions lib/ParserFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { fileTypeFromBuffer } from 'file-type';
import ContentType from 'content-type';
import MimeType from 'media-typer';
import initDebug from 'debug';
import { Buffer } from 'node:buffer';

import { INativeMetadataCollector, MetadataCollector } from './common/MetadataCollector.js';
import { AIFFParser } from './aiff/AiffParser.js';
Expand Down Expand Up @@ -89,7 +88,7 @@ export class ParserFactory {
// Parser could not be determined on MIME-type or extension
debug('Guess parser on content...');

const buf = Buffer.alloc(4100);
const buf = new Uint8Array(4100);
await tokenizer.peekBuffer(buf, {mayBeLess: true});
if (tokenizer.fileInfo.path) {
parserId = this.getParserIdForExtension(tokenizer.fileInfo.path);
Expand Down
5 changes: 2 additions & 3 deletions lib/aiff/AiffParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,8 @@ export class AIFFParser extends BasicParser {

public async readTextChunk(header: iff.IChunkHeader): Promise<number> {
const value = await this.tokenizer.readToken(new Token.StringType(header.chunkSize, 'ascii'));
value.split('\0').map(v => v.trim()).filter(v => v && v.length > 0).forEach(v => {
this.metadata.addTag('AIFF', header.chunkID, v.trim());
});
const values = value.split('\0').map(v => v.trim()).filter(v => v && v.length);
await Promise.all(values.map(v => this.metadata.addTag('AIFF', header.chunkID, v)));
return header.chunkSize;
}

Expand Down
19 changes: 9 additions & 10 deletions lib/aiff/AiffToken.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import * as Token from 'token-types';
import { Buffer } from 'node:buffer';

import { FourCcToken } from '../common/FourCC.js';
import * as iff from '../iff/index.js';

import { IGetToken } from 'strtok3';
import type { IGetToken } from 'strtok3';

/**
* The Common Chunk.
Expand All @@ -30,27 +29,27 @@ export class Common implements IGetToken<ICommon> {
this.len = header.chunkSize;
}

public get(buf: Buffer, off: number): ICommon {
public get(buf: Uint8Array, off: number): ICommon {

// see: https://cycling74.com/forums/aiffs-80-bit-sample-rate-value
const shift = buf.readUInt16BE(off + 8) - 16398;
const baseSampleRate = buf.readUInt16BE(off + 8 + 2);
const shift = Token.UINT16_BE.get(buf, off + 8) - 16398;
const baseSampleRate = Token.UINT16_BE.get(buf, off + 8 + 2);

const res: ICommon = {
numChannels: buf.readUInt16BE(off),
numSampleFrames: buf.readUInt32BE(off + 2),
sampleSize: buf.readUInt16BE(off + 6),
numChannels: Token.UINT16_BE.get(buf, off),
numSampleFrames: Token.UINT32_BE.get(buf, off + 2),
sampleSize: Token.UINT16_BE.get(buf, off + 6),
sampleRate: shift < 0 ? baseSampleRate >> Math.abs(shift) : baseSampleRate << shift
};

if (this.isAifc) {
res.compressionType = FourCcToken.get(buf, off + 18);
if (this.len > 22) {
const strLen = buf.readInt8(off + 22);
const strLen = Token.UINT8.get(buf, off + 22);
if (strLen > 0) {
const padding = (strLen + 1) % 2;
if (23 + strLen + padding === this.len) {
res.compressionName = new Token.StringType(strLen, 'binary').get(buf, off + 23);
res.compressionName = new Token.StringType(strLen, 'latin1').get(buf, off + 23);
} else {
throw new Error('Illegal pstring length');
}
Expand Down
22 changes: 10 additions & 12 deletions lib/apev2/APEv2Parser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import initDebug from 'debug';
import * as strtok3 from 'strtok3/core';
import { StringType } from 'token-types';
import { Buffer } from 'node:buffer';
import { uint8ArrayToString } from 'uint8array-extras';

import * as util from '../common/Util.js';
import { IOptions, IRandomReader, IApeHeader } from '../type.js';
Expand Down Expand Up @@ -56,7 +56,7 @@ export class APEv2Parser extends BasicParser {
*/
public static async findApeFooterOffset(reader: IRandomReader, offset: number): Promise<IApeHeader> {
// Search for APE footer header at the end of the file
const apeBuf = Buffer.alloc(TagFooter.len);
const apeBuf = new Uint8Array(TagFooter.len);
await reader.randomRead(apeBuf, 0, TagFooter.len, offset - TagFooter.len);
const tagFooter = TagFooter.get(apeBuf, 0);
if (tagFooter.ID === 'APETAGEX') {
Expand All @@ -65,7 +65,7 @@ export class APEv2Parser extends BasicParser {
}
}

private static parseTagFooter(metadata: INativeMetadataCollector, buffer: Buffer, options: IOptions): Promise<void> {
private static parseTagFooter(metadata: INativeMetadataCollector, buffer: Uint8Array, options: IOptions): Promise<void> {
const footer = TagFooter.get(buffer, buffer.length - TagFooter.len);
if (footer.ID !== preamble) throw new Error('Unexpected APEv2 Footer ID preamble value.');
strtok3.fromBuffer(buffer);
Expand Down Expand Up @@ -95,7 +95,7 @@ export class APEv2Parser extends BasicParser {
if (this.tokenizer.fileInfo.size) {
// Try to read the APEv2 header using just the footer-header
const remaining = this.tokenizer.fileInfo.size - this.tokenizer.position; // ToDo: take ID3v1 into account
const buffer = Buffer.alloc(remaining);
const buffer = new Uint8Array(remaining);
await this.tokenizer.readBuffer(buffer);
return APEv2Parser.parseTagFooter(this.metadata, buffer, this.options);
}
Expand All @@ -117,7 +117,7 @@ export class APEv2Parser extends BasicParser {

public async parseTags(footer: IFooter): Promise<void> {

const keyBuffer = Buffer.alloc(256); // maximum tag key length
const keyBuffer = new Uint8Array(256); // maximum tag key length

let bytesRemaining = footer.size - TagFooter.len;

Expand All @@ -144,24 +144,22 @@ export class APEv2Parser extends BasicParser {
const value = await this.tokenizer.readToken<string>(new StringType(tagItemHeader.size, 'utf8'));
const values = value.split(/\x00/g);

for (const val of values) {
this.metadata.addTag(tagFormat, key, val);
}
await Promise.all(values.map(val => this.metadata.addTag(tagFormat, key, val)));
break;
}

case DataType.binary: // binary (probably artwork)
if (this.options.skipCovers) {
await this.tokenizer.ignore(tagItemHeader.size);
} else {
const picData = Buffer.alloc(tagItemHeader.size);
const picData = new Uint8Array(tagItemHeader.size);
await this.tokenizer.readBuffer(picData);

zero = util.findZero(picData, 0, picData.length);
const description = picData.toString('utf8', 0, zero);
const description = uint8ArrayToString(picData.slice(0, zero));
const data = picData.slice(zero + 1);

const data = Buffer.from(picData.slice(zero + 1));
this.metadata.addTag(tagFormat, key, {
await this.metadata.addTag(tagFormat, key, {
description,
data
});
Expand Down
3 changes: 1 addition & 2 deletions lib/apev2/APEv2Token.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as Token from 'token-types';
import { IGetToken } from 'strtok3/core';
import { Buffer } from 'node:buffer';

import { FourCcToken } from '../common/FourCC.js';

Expand Down Expand Up @@ -158,7 +157,7 @@ export const Header: IGetToken<IHeader> = {
export const TagFooter: IGetToken<IFooter> = {
len: 32,

get: (buf: Buffer, off) => {
get: (buf, off) => {
return {
// should equal 'APETAGEX'
ID: new Token.StringType(8, 'ascii').get(buf, off),
Expand Down
Loading

0 comments on commit eb0f8e6

Please sign in to comment.