Skip to content

Commit

Permalink
Export more file classes
Browse files Browse the repository at this point in the history
Marked some classes as external
Exported more backends/FSes
Fixed encode and decode (hopefully)
  • Loading branch information
james-pre committed Mar 22, 2024
1 parent 9fc77d2 commit f442965
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 23 deletions.
3 changes: 2 additions & 1 deletion src/backends/AsyncMirror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ type AsyncOperation = {

/**
* We define our own file to interpose on syncSync() for mirroring purposes.
* @internal
*/
class MirrorFile extends PreloadFile<AsyncMirrorFS> {
export class MirrorFile extends PreloadFile<AsyncMirrorFS> {
constructor(fs: AsyncMirrorFS, path: string, flag: FileFlag, stat: Stats, data: Uint8Array) {
super(fs, path, flag, stat, data);
}
Expand Down
2 changes: 2 additions & 0 deletions src/backends/AsyncStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export interface AsyncRWTransaction extends AsyncROTransaction {

/**
* Async preload file for usage with AsyncStore
* @internal
*/
export class AsyncFile extends PreloadFile<AsyncStoreFS> {
constructor(_fs: AsyncStoreFS, _path: string, _flag: FileFlag, _stat: Stats, contents?: Uint8Array) {
Expand Down Expand Up @@ -164,6 +165,7 @@ export interface AsyncStoreOptions {
/**
* An asynchronous file system which uses an async store to store its data.
* @see AsyncStore
* @internal
*/
export class AsyncStoreFS extends Async(FileSystem) {
protected store: AsyncStore;
Expand Down
1 change: 1 addition & 0 deletions src/backends/InMemory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Ino } from '../inode.js';
import type { Backend } from './backend.js';
import { SyncStore, SimpleSyncStore, SimpleSyncRWTransaction, SyncRWTransaction, SyncStoreFS } from './SyncStore.js';

/**
* A simple in-memory store
*/
Expand Down
1 change: 1 addition & 0 deletions src/backends/Locked.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { Stats } from '../stats.js';
* are not executed in a single atomic step. OverlayFS uses this
* LockedFS to avoid having to reason about the correctness of
* multiple requests interleaving.
* @internal
*/
export class LockedFS<FS extends FileSystem> implements FileSystem {
private _mu: Mutex = new Mutex();
Expand Down
4 changes: 3 additions & 1 deletion src/backends/Overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ const deletionLogPath = '/.deleted';

/**
* Overlays a RO file to make it writable.
* @internal
*/
class OverlayFile extends PreloadFile<UnlockedOverlayFS> implements File {
export class OverlayFile extends PreloadFile<UnlockedOverlayFS> implements File {
constructor(fs: UnlockedOverlayFS, path: string, flag: FileFlag, stats: Stats, data: Uint8Array) {
super(fs, path, flag, stats, data);
}
Expand Down Expand Up @@ -568,6 +569,7 @@ export class UnlockedOverlayFS extends FileSystem {
* OverlayFS makes a read-only filesystem writable by storing writes on a second,
* writable file system. Deletes are persisted via metadata stored on the writable
* file system.
* @internal
*/
export class OverlayFS extends LockedFS<UnlockedOverlayFS> {
public async ready() {
Expand Down
14 changes: 9 additions & 5 deletions src/backends/SyncStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ export interface SyncStoreOptions {
store: SyncStore;
}

/**
* File backend by a SyncStoreFS
* @internal
*/
export class SyncStoreFile extends PreloadFile<SyncStoreFS> {
constructor(_fs: SyncStoreFS, _path: string, _flag: FileFlag, _stat: Stats, contents?: Uint8Array) {
super(_fs, _path, _flag, _stat, contents);
Expand All @@ -191,13 +195,12 @@ export class SyncStoreFile extends PreloadFile<SyncStoreFS> {
}

/**
* A "Synchronous key-value file system". Stores data to/retrieves data from an
* underlying key-value store.
* A synchronous key-value file system. Uses a SyncStore to store the data.
*
* We use a unique ID for each node in the file system. The root node has a
* fixed ID.
* We use a unique ID for each node in the file system. The root node has a fixed ID.
* @todo Introduce Node ID caching.
* @todo Check modes.
* @internal
*/
export class SyncStoreFS extends Sync(FileSystem) {
protected store: SyncStore;
Expand Down Expand Up @@ -483,7 +486,8 @@ export class SyncStoreFS extends Sync(FileSystem) {
if (!data) {
throw ApiError.ENOENT(p);
}
return new Inode(data.buffer);
const inode = new Inode(data.buffer);
return inode;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ export async function configure(config: Configuration): Promise<void> {
}

export type { Backend, BackendConfig } from './backends/backend.js';
export * from './backends/AsyncMirror.js';
export * from './backends/AsyncStore.js';
export * from './backends/InMemory.js';
export * from './backends/Locked.js';
export * from './backends/Overlay.js';
export * from './backends/SyncStore.js';
export * from './ApiError.js';
export * from './cred.js';
Expand Down
53 changes: 37 additions & 16 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@ import { FileSystem } from './filesystem.js';
import { ApiError, ErrorCode } from './ApiError.js';
import { dirname } from './emulation/path.js';
import { Cred } from './cred.js';
import type { TextDecoder as _TextDecoder, TextEncoder as _TextEncoder } from 'node:util';

declare global {
function atob(data: string): string;
function btoa(data: string): string;
}

declare const globalThis: {
TextDecoder?: typeof _TextDecoder;
TextEncoder?: typeof _TextEncoder;
setImmediate?: (callback: () => unknown) => void;
};

Expand Down Expand Up @@ -130,21 +127,36 @@ export const setImmediate = typeof globalThis.setImmediate == 'function' ? globa
* @internal
*/
export function encode(input: string, encoding: BufferEncoding = 'utf8'): Uint8Array {
if (typeof input != 'string') {
throw new ApiError(ErrorCode.EINVAL, 'Can not encode a non-string');
}
switch (encoding) {
case 'ascii':
return new globalThis.TextEncoder().encode(input).map(v => v & 0x7f);
case 'latin1':
case 'binary':
return new Uint8Array(
Array.from(input).flatMap(char => {
const code = char.charCodeAt(0);
return code > 0x7f ? [0x7f, code & 0x7f] : code;
})
);
case 'utf8':
case 'utf-8':
case 'latin1':
case 'binary':
return new Uint8Array(Array.from(input).map(char => char.charCodeAt(0)));
case 'base64':
return encode(atob(input), 'utf-8');
case 'base64url':
return encode(input.replace('_', '/').replace('-', '+'), 'base64');
case 'hex':
return new globalThis.TextEncoder().encode(input);
return new Uint8Array(input.match(/.{1,2}/g).map(e => parseInt(e, 16)));
case 'utf16le':
case 'ucs2':
case 'ucs-2':
return new globalThis.TextEncoder().encode(input).slice(0, -1);
const u16 = new Uint16Array(new ArrayBuffer(input.length * 2));
for (let i = 0; i < input.length; i++) {
u16[i] = input.charCodeAt(i);
}
return new Uint8Array(u16.buffer);
default:
throw new ApiError(ErrorCode.EINVAL, 'Invalid encoding: ' + encoding);
}
Expand All @@ -155,14 +167,27 @@ export function encode(input: string, encoding: BufferEncoding = 'utf8'): Uint8A
* @internal
*/
export function decode(input?: Uint8Array, encoding: BufferEncoding = 'utf8'): string {
if (!(input instanceof Uint8Array)) {
throw new ApiError(ErrorCode.EINVAL, 'Can not decode a non-Uint8Array');
}
switch (encoding) {
case 'ascii':
let asciiString = '';
for (let i = 0; i < input.length; i++) {
let code = input[i];
if (code == 0x7f) {
code += input[++i];
}
asciiString += String.fromCharCode(code);
}
return asciiString;
case 'utf8':
case 'utf-8':
return new globalThis.TextDecoder().decode(input);
case 'latin1':
case 'binary':
return new globalThis.TextDecoder('latin1').decode(input);
return Array.from(input)
.map(char => String.fromCharCode(char))
.join('');
case 'utf16le':
case 'ucs2':
case 'ucs-2':
Expand All @@ -173,16 +198,12 @@ export function decode(input?: Uint8Array, encoding: BufferEncoding = 'utf8'): s
}
return utf16leString;
case 'base64':
return btoa(
Array.from(input)
.map(v => String.fromCharCode(v))
.join('')
);
return btoa(decode(input, 'utf-8'));
case 'base64url':
return decode(input, 'base64').replace('/', '_').replace('+', '-');
case 'hex':
return Array.from(input)
.map(e => e.toString(16))
.map(e => e.toString(16).padStart(2, '0'))
.join('');
default:
throw new ApiError(ErrorCode.EINVAL, 'Invalid encoding: ' + encoding);
Expand Down

0 comments on commit f442965

Please sign in to comment.