diff --git a/src/lib/index.ts b/src/lib/index.ts index 3fc1630..201213a 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -2,7 +2,7 @@ import ArrayIo, { ArrayOptions } from './type/ArrayIo.js'; import StringIo, { StringOptions } from './type/StringIo.js'; import StructIo, { StructFields, StructOptions } from './type/StructIo.js'; import TlvIo, { TlvValueCallback } from './type/TlvIo.js'; -import { validateType } from './util.js'; +import { IoMode, validateType } from './util.js'; import { openStream } from './stream/util.js'; import { IoContext, IoSource, IoStream, IoType } from './types.js'; @@ -162,6 +162,7 @@ const float64le = { export { IoContext, + IoMode, IoSource, IoStream, IoType, diff --git a/src/lib/stream/FsStream.ts b/src/lib/stream/FsStream.ts index 370ae79..68b73d9 100644 --- a/src/lib/stream/FsStream.ts +++ b/src/lib/stream/FsStream.ts @@ -1,5 +1,5 @@ import fs from '../shim/fs.cjs'; -import { Endianness } from '../util.js'; +import { Endianness, IoMode } from '../util.js'; import { IoStream } from '../types.js'; class FsStream implements IoStream { @@ -12,13 +12,17 @@ class FsStream implements IoStream { #littleEndian: boolean; #offset: number; - constructor(source: string | number, endianness = Endianness.Little) { + constructor( + source: string | number, + mode = IoMode.Read, + endianness = Endianness.Little, + ) { if (fs === undefined) { throw new Error('fs api unavailable'); } if (typeof source === 'string') { - this.#fd = fs.openSync(source, 'r'); + this.#fd = fs.openSync(source, mode === IoMode.Write ? 'w+' : 'r'); this.#ownFd = true; } else if (typeof source === 'number') { this.#fd = source; diff --git a/src/lib/stream/util.ts b/src/lib/stream/util.ts index 337e6c0..e27b3fc 100644 --- a/src/lib/stream/util.ts +++ b/src/lib/stream/util.ts @@ -1,6 +1,6 @@ import ArrayBufferStream from './ArrayBufferStream.js'; import FsStream from './FsStream.js'; -import { Endianness } from '../util.js'; +import { Endianness, IoMode } from '../util.js'; import { IoSource, IoStream } from '../types.js'; const isStream = (ref: any) => { @@ -15,12 +15,13 @@ const isStream = (ref: any) => { const openStream = ( source: IoSource, + mode = IoMode.Read, endianness = Endianness.Little, ): IoStream => { if (isStream(source)) { return source as IoStream; } else if (typeof source === 'string' || typeof source === 'number') { - return new FsStream(source, endianness); + return new FsStream(source, mode, endianness); } else if (ArrayBuffer.isView(source)) { return new ArrayBufferStream( source.buffer, diff --git a/src/lib/type/ArrayIo.ts b/src/lib/type/ArrayIo.ts index 72c272e..76cb852 100644 --- a/src/lib/type/ArrayIo.ts +++ b/src/lib/type/ArrayIo.ts @@ -1,4 +1,4 @@ -import { validateType } from '../util.js'; +import { IoMode, validateType } from '../util.js'; import { openStream } from '../stream/util.js'; import { IoContext, IoSource, IoType } from '../types.js'; @@ -32,7 +32,7 @@ class ArrayIo implements IoType { } read(source: IoSource, context: IoContext = {}) { - const stream = openStream(source); + const stream = openStream(source, IoMode.Read); const value = []; const size = this.#options.size; @@ -53,7 +53,7 @@ class ArrayIo implements IoType { } write(source: IoSource, value: any[], context: IoContext = {}) { - const stream = openStream(source); + const stream = openStream(source, IoMode.Write); const size = value.length; const type = this.#type; diff --git a/src/lib/type/StringIo.ts b/src/lib/type/StringIo.ts index 57cbe1f..7b7f431 100644 --- a/src/lib/type/StringIo.ts +++ b/src/lib/type/StringIo.ts @@ -1,4 +1,4 @@ -import { resolveValue } from '../util.js'; +import { IoMode, resolveValue } from '../util.js'; import { openStream } from '../stream/util.js'; import { IoContext, IoSource, IoStream, IoType } from '../types.js'; @@ -49,7 +49,7 @@ class StringIo implements IoType { } read(source: IoSource, context: IoContext = {}) { - const stream = openStream(source); + const stream = openStream(source, IoMode.Read); const size = resolveValue(this.#options.size, context.local, context.root); const rawBytes = this.#readRawBytes(stream, size); @@ -59,7 +59,7 @@ class StringIo implements IoType { } write(source: IoSource, value: string, context: IoContext = {}) { - const stream = openStream(source); + const stream = openStream(source, IoMode.Write); const finalValue = this.#options.reverse ? value.split('').reverse().join('') diff --git a/src/lib/type/StructIo.ts b/src/lib/type/StructIo.ts index f0ff5ad..9f89cb1 100644 --- a/src/lib/type/StructIo.ts +++ b/src/lib/type/StructIo.ts @@ -1,4 +1,4 @@ -import { Endianness, validateType } from '../util.js'; +import { Endianness, IoMode, validateType } from '../util.js'; import { openStream } from '../stream/util.js'; import { IoContext, IoSource, IoType } from '../types.js'; @@ -44,7 +44,7 @@ class StructIo implements IoType { } read(source: IoSource, context: IoContext = {}) { - const stream = openStream(source, this.#options.endianness); + const stream = openStream(source, IoMode.Read, this.#options.endianness); const value: Record = {}; context.local = value; @@ -58,7 +58,7 @@ class StructIo implements IoType { } write(source: IoSource, value: object, context: IoContext = {}) { - const stream = openStream(source, this.#options.endianness); + const stream = openStream(source, IoMode.Write, this.#options.endianness); context.local = value; context.root = context.root ?? value; diff --git a/src/lib/type/TlvIo.ts b/src/lib/type/TlvIo.ts index a628ecc..e4e7b00 100644 --- a/src/lib/type/TlvIo.ts +++ b/src/lib/type/TlvIo.ts @@ -1,4 +1,4 @@ -import { Endianness } from '../util.js'; +import { Endianness, IoMode } from '../util.js'; import { openStream } from '../stream/util.js'; import { IoContext, IoSource, IoType } from '../types.js'; @@ -58,7 +58,7 @@ class TlvIo implements IoType { } read(source: IoSource, context: IoContext = {}): Tlv { - const stream = openStream(source, this.#options.endianness); + const stream = openStream(source, IoMode.Read, this.#options.endianness); context.local = null; context.root = context.root ?? null; @@ -71,7 +71,11 @@ class TlvIo implements IoType { let valueValue = valueBytes; if (valueType && valueType.read) { - const valueStream = openStream(valueBytes, this.#options.endianness); + const valueStream = openStream( + valueBytes, + IoMode.Read, + this.#options.endianness, + ); valueValue = valueType.read(valueStream, context); } @@ -83,7 +87,7 @@ class TlvIo implements IoType { } write(source: IoSource, value: Tlv, context: IoContext = {}) { - const stream = openStream(source, this.#options.endianness); + const stream = openStream(source, IoMode.Write, this.#options.endianness); context.local = null; context.root = context.root ?? null; diff --git a/src/lib/util.ts b/src/lib/util.ts index 396db7a..4c3ad26 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -5,6 +5,11 @@ enum Endianness { Big = 2, } +enum IoMode { + Read = 1, + Write = 2, +} + const validateType = (type: IoType) => { if (typeof type.getSize !== 'function') { throw new Error('Missing required function: getSize'); @@ -28,4 +33,4 @@ const resolveValue = (ref: number | string, ...objects: object[]) => { } }; -export { Endianness, validateType, resolveValue }; +export { Endianness, IoMode, validateType, resolveValue }; diff --git a/src/spec/stream/util.spec.ts b/src/spec/stream/util.spec.ts index 57917e2..f322e7f 100644 --- a/src/spec/stream/util.spec.ts +++ b/src/spec/stream/util.spec.ts @@ -2,6 +2,8 @@ import { describe, expect, test } from 'vitest'; import FsStream from '../../lib/stream/FsStream.js'; import ArrayBufferStream from '../../lib/stream/ArrayBufferStream.js'; import { openStream } from '../../lib/stream/util.js'; +import { IoMode } from '../../lib/util.js'; +import * as fs from 'fs'; describe('openStream', () => { test('returns FsStream object when given path to file', () => { @@ -9,6 +11,13 @@ describe('openStream', () => { expect(stream).toBeInstanceOf(FsStream); }); + test('returns FsStream object when given path to nonexistent file in write mode', () => { + const stream = openStream('./fixture/nonexistent.file', IoMode.Write); + expect(stream).toBeInstanceOf(FsStream); + stream.close(); + fs.rmSync('./fixture/nonexistent.file'); + }); + test('returns ArrayBufferStream object when given Uint8Array', () => { const view = new Uint8Array(10); const stream = openStream(view);