Skip to content

Commit b3ed35e

Browse files
authored
feat: auto load btrie files if available (#8218)
1 parent ce2ca9f commit b3ed35e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+494
-217
lines changed

cspell-dict.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
alexiosc
33
backreference
44
bitjson
5+
btrie
56
cfile
67
cheatsheets
78
codecov

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"pub-grad": "pnpm run pre-pub && lerna publish --conventional-commits --conventional-graduate --changelog-preset conventionalcommits --sync-workspace-lock",
4545
"pub-next": "pnpm run pre-pub && lerna publish --conventional-commits --conventional-prerelease --changelog-preset conventionalcommits --sync-workspace-lock --dist-tag next",
4646
"test-watch": "pnpm -r run --parallel test-watch",
47-
"test": "pnpm -r --stream --workspace-concurrency=2 run test && pnpm run test-schema",
47+
"test": "pnpm test:prep && pnpm -r --stream --workspace-concurrency=2 run test && pnpm run test-schema",
4848
"test:perf": "pnpm -r --stream --workspace-concurrency=2 run test:perf",
4949
"test:update-snapshots": "pnpm run -r test:update-snapshot",
5050
"test:bin": "pnpm test:bin-spell && pnpm test:bin-spell-cache-content && pnpm test:bin-spell-cache-metadata && pnpm test:bin-trace && pnpm test:bin-check && pnpm test:bin-spell-yarn",
@@ -54,6 +54,7 @@
5454
"test:bin-spell-cache-content": "node ./bin.mjs -c cspell.test.json --cache --cache-strategy content --cache-location temp/.cspellcache-content",
5555
"test:bin-spell-cache-metadata": "node ./bin.mjs -c cspell.test.json --cache --cache-strategy metadata --cache-location temp/.cspellcache-meta",
5656
"test:bin-trace": "node ./bin.mjs trace test",
57+
"test:prep": "cspell-tools btrie node_modules/@cspell/dict-en_us/en_US.trie.gz",
5758
"test-build-docs": "cd website && pnpm run build:site",
5859
"test-integrations": "cd ./integration-tests && pnpm run integration-tests",
5960
"test-schema": "node ./test-packages/cspell-types/validate-schema/validate-schema.mjs",
@@ -101,6 +102,8 @@
101102
"node": ">=20.0.0"
102103
},
103104
"devDependencies": {
105+
"@cspell/cspell-tools": "workspace:*",
106+
"@cspell/dict-en_us": "^4.4.26",
104107
"@cspell/eslint-plugin": "workspace:*",
105108
"@eslint/js": "^9.39.2",
106109
"@internal/scripts": "workspace:*",

packages/cspell-io/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@
2323
"!**/*.map"
2424
],
2525
"scripts": {
26-
"build": "tsdown",
26+
"build": "tsdown && pnpm build:tsc",
27+
"build:tsc": "tsc -p .",
2728
"build:watch": "tsdown --watch",
2829
"watch": "pnpm build:watch",
2930
"clean": "shx rm -rf dist temp coverage \"*.tsbuildInfo\"",
3031
"clean-build": "pnpm run clean && pnpm run build",
3132
"coverage": "vitest run --coverage --pool=forks",
3233
"test-watch": "vitest",
33-
"test:build": "tsc -p .",
34-
"test": "pnpm test:build && vitest run --pool=forks"
34+
"test": "vitest run --pool=forks"
3535
},
3636
"repository": {
3737
"type": "git",

packages/cspell-io/src/CSpellIONode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class CSpellIONode implements CSpellIO {
5757
}
5858
return res.value;
5959
}
60-
writeFile(urlOrFilename: UrlOrReference, content: string | ArrayBufferView): Promise<FileReference> {
60+
writeFile(urlOrFilename: UrlOrReference, content: string | Uint8Array<ArrayBuffer>): Promise<FileReference> {
6161
const ref = toFileReference(urlOrFilename);
6262
const fileResource = CFileResource.from(ref, content);
6363
const res = this.serviceBus.dispatch(RequestFsWriteFile.create(fileResource));

packages/cspell-io/src/VirtualFs.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe('VirtualFs', () => {
2222

2323
beforeEach(() => {
2424
virtualFs = createVirtualFS();
25-
mockConsoleLog = vi.spyOn(console, 'log');
25+
mockConsoleLog = vi.spyOn(console, 'log').mockImplementation(() => {});
2626
});
2727

2828
afterEach(() => {

packages/cspell-io/src/common/CFileResource.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { Buffer } from 'node:buffer';
2+
import { promisify } from 'node:util';
3+
import zlib from 'node:zlib';
24

35
import { describe, expect, test } from 'vitest';
46

57
import { CFileResource, fromFileResource } from './CFileResource.js';
68

9+
const gzip = promisify(zlib.gzip);
10+
711
describe('CFileResource', () => {
812
describe('from', () => {
913
test('should create a CFileResource from a FileResource', () => {
@@ -86,6 +90,32 @@ describe('CFileResource', () => {
8690
expect(cFileResource.baseFilename).toBe('file.txt');
8791
expect(cFileResource.gz).toEqual(gz);
8892
});
93+
94+
test('binary data', async () => {
95+
const text = 'Hello, world!';
96+
const encoder = new TextEncoder();
97+
const content = encoder.encode(text);
98+
const decoder = new TextDecoder();
99+
100+
const cFileResource = CFileResource.from(new URL('https://example.com/file.txt'), content);
101+
102+
const fileData = await cFileResource.getBytes();
103+
expect([...fileData]).toEqual([...content]);
104+
expect(decoder.decode(fileData)).toBe(text);
105+
});
106+
107+
test('binary data compressed', async () => {
108+
const text = 'Hello, world!';
109+
const data = Buffer.from(text, 'utf8');
110+
const content = await gzip(data);
111+
const decoder = new TextDecoder();
112+
113+
const cFileResource = CFileResource.from(new URL('https://example.com/file.txt.gz'), content);
114+
115+
const fileData = await cFileResource.getBytes();
116+
expect([...fileData]).toEqual([...data]);
117+
expect(decoder.decode(fileData)).toBe(text);
118+
});
89119
});
90120

91121
describe('fromFileResource', () => {

packages/cspell-io/src/common/CFileResource.ts

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { assert } from '../errors/assert.js';
22
import type { BufferEncoding } from '../models/BufferEncoding.js';
33
import type { FileReference, FileResource, TextFileResource } from '../models/FileResource.js';
4-
import { decode, encodeString, isGZipped } from './encode-decode.js';
4+
import { decode, decompress, encodeString, isGZipped } from './encode-decode.js';
55

66
export interface CFileResourceJson {
77
url: string;
@@ -12,41 +12,53 @@ export interface CFileResourceJson {
1212
}
1313

1414
export class CFileResource implements TextFileResource {
15-
private _text?: string;
1615
readonly baseFilename?: string | undefined;
17-
private _gz?: boolean | undefined;
16+
readonly url: URL;
17+
readonly content: string | Uint8Array<ArrayBuffer>;
18+
readonly encoding: BufferEncoding | undefined;
19+
#gz?: boolean | undefined;
20+
#text?: string;
21+
#data?: Uint8Array<ArrayBuffer>;
1822

1923
constructor(
20-
readonly url: URL,
21-
readonly content: string | ArrayBufferView,
22-
readonly encoding: BufferEncoding | undefined,
24+
url: URL,
25+
content: string | Uint8Array<ArrayBuffer>,
26+
encoding: BufferEncoding | undefined,
2327
baseFilename: string | undefined,
2428
gz: boolean | undefined,
2529
) {
30+
this.url = url;
31+
this.content = content;
32+
this.encoding = encoding;
2633
this.baseFilename = baseFilename ?? ((url.protocol !== 'data:' && url.pathname.split('/').pop()) || undefined);
27-
this._gz = gz;
34+
this.#gz = gz;
2835
}
2936

3037
get gz(): boolean {
31-
if (this._gz !== undefined) return this._gz;
38+
if (this.#gz !== undefined) return this.#gz;
3239
if (this.url.pathname.endsWith('.gz')) return true;
3340
if (typeof this.content === 'string') return false;
3441
return isGZipped(this.content);
3542
}
3643

3744
getText(encoding?: BufferEncoding): string {
38-
if (this._text !== undefined) return this._text;
45+
if (this.#text !== undefined) return this.#text;
3946
const text = typeof this.content === 'string' ? this.content : decode(this.content, encoding ?? this.encoding);
40-
this._text = text;
47+
this.#text = text;
4148
return text;
4249
}
4350

44-
getBytes(): Uint8Array {
45-
const arrayBufferview =
46-
typeof this.content === 'string' ? encodeString(this.content, this.encoding) : this.content;
47-
return arrayBufferview instanceof Uint8Array
48-
? arrayBufferview
49-
: new Uint8Array(arrayBufferview.buffer, arrayBufferview.byteOffset, arrayBufferview.byteLength);
51+
async getBytes(unzip?: boolean): Promise<Uint8Array<ArrayBuffer>> {
52+
if (unzip !== false && this.#data !== undefined) return this.#data;
53+
if (typeof this.content === 'string') {
54+
this.#data = encodeString(this.content, this.encoding);
55+
return this.#data;
56+
}
57+
if (unzip ?? isGZipped(this.content)) {
58+
this.#data = await decompress(this.content, 'gzip');
59+
return this.#data;
60+
}
61+
return this.content;
5062
}
5163

5264
public toJson(): CFileResourceJson {
@@ -64,18 +76,18 @@ export class CFileResource implements TextFileResource {
6476
}
6577

6678
static from(fileResource: FileResource): CFileResource;
67-
static from(fileReference: FileReference, content: string | ArrayBufferView): CFileResource;
68-
static from(fileReference: FileReference | URL, content: string | ArrayBufferView): CFileResource;
79+
static from(fileReference: FileReference, content: string | Uint8Array<ArrayBuffer>): CFileResource;
80+
static from(fileReference: FileReference | URL, content: string | Uint8Array<ArrayBuffer>): CFileResource;
6981
static from(
7082
url: URL,
71-
content: string | ArrayBufferView,
83+
content: string | Uint8Array<ArrayBuffer>,
7284
encoding?: BufferEncoding | undefined,
7385
baseFilename?: string | undefined,
7486
gz?: boolean,
7587
): CFileResource;
7688
static from(
7789
urlOrFileResource: FileResource | FileReference | URL,
78-
content?: string | ArrayBufferView,
90+
content?: string | Uint8Array<ArrayBuffer>,
7991
encoding?: BufferEncoding,
8092
baseFilename?: string | undefined,
8193
gz?: boolean,

packages/cspell-io/src/common/arrayBuffers.test.ts

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,14 @@ import { Buffer } from 'node:buffer';
22

33
import { describe, expect, test } from 'vitest';
44

5-
import {
6-
arrayBufferViewToBuffer,
7-
asUint8Array,
8-
copyArrayBufferView,
9-
sliceView,
10-
swap16,
11-
swap16Poly,
12-
} from './arrayBuffers.js';
5+
import { arrayBufferViewToBuffer, swap16, swap16Poly, toUint8Array } from './arrayBuffers.js';
136

147
const sampleText = 'This is a bit of text to test things with.';
158

169
describe('arrayBuffers', () => {
1710
test('asUint8Array', () => {
1811
const buf = Buffer.from(sampleText);
19-
const u8 = asUint8Array(buf);
12+
const u8 = toUint8Array(buf);
2013
expect(u8[0]).toBe(sampleText.codePointAt(0));
2114
expect(buf[0]).toBe(sampleText.codePointAt(0));
2215
u8[0] = 32;
@@ -44,38 +37,12 @@ describe('arrayBuffers', () => {
4437
expect(buf).toEqual(Buffer.from([2, 1, 4, 3]));
4538
});
4639

47-
test('sliceView', () => {
48-
const src = [1, 2, 3, 4, 5, 6, 7, 8];
49-
const buf = Buffer.from(src);
50-
const view = sliceView(buf, 2, 4);
51-
expect(view.buffer).toBe(buf.buffer);
52-
const u8 = asUint8Array(view);
53-
expect(u8.buffer).toBe(buf.buffer);
54-
const u8From = Uint8Array.from(src);
55-
const cView = copyArrayBufferView(view);
56-
expect(cView.buffer).not.toBe(buf.buffer);
57-
expect(cView).toEqual(Uint8Array.from([3, 4, 5, 6]));
58-
expect(u8).toEqual(Uint8Array.from([3, 4, 5, 6]));
59-
expect(asUint8Array(sliceView(u8From, 2, 4))).toEqual(Uint8Array.from([3, 4, 5, 6]));
60-
});
61-
62-
test('sliceView bounds', () => {
63-
const src = [1, 2, 3, 4, 5, 6, 7, 8];
64-
const buf = Uint8Array.from(src);
65-
const view = sliceView(buf, 2, 100);
66-
expect(view.buffer).toBe(buf.buffer);
67-
expect(asUint8Array(view)).toEqual(Uint8Array.from([3, 4, 5, 6, 7, 8]));
68-
const view2 = sliceView(view, 2);
69-
expect(view2.buffer).toBe(buf.buffer);
70-
expect(asUint8Array(view2)).toEqual(Uint8Array.from([5, 6, 7, 8]));
71-
});
72-
7340
test('arrayBufferViewToBuffer', () => {
7441
const src = [1, 2, 3, 4, 5, 6, 7, 8];
7542
const buf = Buffer.from(src);
7643
expect(arrayBufferViewToBuffer(buf)).toBe(buf);
7744

78-
const view = sliceView(buf, 2, 4);
45+
const view = buf.subarray(2, 6);
7946
const bufView = arrayBufferViewToBuffer(view);
8047
expect(bufView).not.toBe(buf);
8148
expect(bufView.buffer).toBe(buf.buffer);
Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,51 @@
11
import { Buffer } from 'node:buffer';
22

3+
import type { TArrayBufferView } from '../types.js';
4+
35
/**
4-
* Treat a ArrayBufferView as a Uint8Array.
6+
* Treat a TArrayBufferView as a Uint8Array.
57
* The Uint8Array will share the same underlying ArrayBuffer.
68
* @param data - source data
79
* @returns Uint8Array
810
*/
9-
export function asUint8Array(data: ArrayBufferView): Uint8Array {
11+
export function toUint8Array(data: TArrayBufferView): Uint8Array<ArrayBuffer>;
12+
export function toUint8Array(data: ArrayBufferView): Uint8Array;
13+
export function toUint8Array(data: ArrayBufferView): Uint8Array {
14+
if (data instanceof Uint8Array) {
15+
return data;
16+
}
1017
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
1118
}
1219

20+
export function arrayBufferViewToBuffer(data: TArrayBufferView): Buffer<ArrayBuffer>;
21+
export function arrayBufferViewToBuffer(data: ArrayBufferView): Buffer;
1322
export function arrayBufferViewToBuffer(data: ArrayBufferView): Buffer {
1423
if (data instanceof Buffer) {
1524
return data;
1625
}
17-
const buf = Buffer.from(data.buffer);
18-
if (data.byteOffset === 0 && data.byteLength === data.buffer.byteLength) {
19-
return buf;
20-
}
21-
return buf.subarray(data.byteOffset, data.byteOffset + data.byteLength);
26+
const buf = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
27+
return buf;
2228
}
2329

2430
/**
2531
* Copy the data buffer.
2632
* @param data - source data
2733
* @returns A copy of the data
2834
*/
29-
export function copyArrayBufferView(data: ArrayBufferView): ArrayBufferView {
35+
export function copyArrayBufferView(data: TArrayBufferView): Uint8Array<ArrayBuffer>;
36+
export function copyArrayBufferView(data: ArrayBufferView): Uint8Array;
37+
export function copyArrayBufferView(data: ArrayBufferView): Uint8Array {
3038
return new Uint8Array(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
3139
}
3240

33-
/**
34-
* Slice an existing data view. Returns a new view using the same underlying data.
35-
* @param data - data view
36-
* @param byteOffset - offset from the beginning of the view.
37-
* @param byteLength - optional length
38-
*/
39-
export function sliceView(data: ArrayBufferView, byteOffset: number, byteLength?: number): ArrayBufferView {
40-
const currentEnd = data.byteOffset + data.byteLength;
41-
const start = Math.min(data.byteOffset + byteOffset, currentEnd);
42-
const end = byteLength ? Math.min(currentEnd, start + byteLength) : currentEnd;
43-
return {
44-
buffer: data.buffer,
45-
byteOffset: start,
46-
byteLength: end - start,
47-
};
48-
}
49-
5041
/**
5142
* Swap the bytes in a buffer.
5243
* @param data - data to swap
5344
* @returns data
5445
*/
55-
export function swap16Poly(data: ArrayBufferView): ArrayBufferView {
46+
export function swap16Poly<T extends TArrayBufferView>(data: T): T;
47+
export function swap16Poly<T extends ArrayBufferView>(data: T): T;
48+
export function swap16Poly(data: TArrayBufferView): TArrayBufferView {
5649
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
5750
for (let i = 0; i < view.byteLength; i += 2) {
5851
view.setUint16(i, view.getUint16(i, false), true);
@@ -65,11 +58,15 @@ export function swap16Poly(data: ArrayBufferView): ArrayBufferView {
6558
* @param data - data to swap
6659
* @returns data
6760
*/
68-
export function swap16(data: ArrayBufferView): ArrayBufferView {
69-
return arrayBufferViewToBuffer(data).swap16();
61+
export function swap16<T extends TArrayBufferView>(data: T): T;
62+
export function swap16<T extends ArrayBufferView>(data: T): T;
63+
64+
export function swap16<T extends ArrayBufferView>(data: T): T {
65+
arrayBufferViewToBuffer(data).swap16();
66+
return data;
7067
}
7168

72-
export function swapBytes(data: ArrayBufferView): ArrayBufferView {
69+
export function swapBytes(data: TArrayBufferView): Uint8Array<ArrayBuffer> {
7370
const buf = copyArrayBufferView(data);
7471
return swap16(buf);
7572
}

0 commit comments

Comments
 (0)