Skip to content

Commit

Permalink
feat: InputBuffer improvements
Browse files Browse the repository at this point in the history
- Introduced typing of the internal buffer (all types of TypedArray union are supported)
- New 'memcpy' method
- Fixed 'memset' method
- Improved naming of some methods
  • Loading branch information
yegor-pelykh committed Oct 15, 2023
1 parent 499a1d8 commit cd23324
Show file tree
Hide file tree
Showing 37 changed files with 446 additions and 369 deletions.
132 changes: 94 additions & 38 deletions src/common/input-buffer.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
/** @format */

import { LibError } from '../error/lib-error';
import { ArrayUtils } from './array-utils';
import { BitUtils } from './bit-utils';
import { StringUtils } from './string-utils';
import { TypedArray } from './typings';

export interface InputBufferInitOptions {
buffer: Uint8Array;
export interface InputBufferInitOptions<T extends TypedArray> {
buffer: T;
offset?: number;
length?: number;
bigEndian?: boolean;
Expand All @@ -14,9 +16,12 @@ export interface InputBufferInitOptions {
/**
* A buffer that can be read as a stream of bytes.
*/
export class InputBuffer {
private readonly _buffer: Uint8Array;
public get buffer(): Uint8Array {
export class InputBuffer<T extends TypedArray> {
private _buffer: T;
public set buffer(v: T) {
this._buffer = v;
}
public get buffer(): T {
return this._buffer;
}

Expand Down Expand Up @@ -70,7 +75,7 @@ export class InputBuffer {
/**
* Create a InputStream for reading from an Array<int>
*/
constructor(opt: InputBufferInitOptions) {
constructor(opt: InputBufferInitOptions<T>) {
this._buffer = opt.buffer;
this._bigEndian = opt.bigEndian ?? false;
this._offset = opt.offset ?? 0;
Expand All @@ -82,9 +87,13 @@ export class InputBuffer {
/**
* Create a copy of **other**.
*/
public static from(other: InputBuffer, offset?: number, length?: number) {
public static from<T extends TypedArray>(
other: InputBuffer<T>,
offset?: number,
length?: number
) {
const offsetFromOther = offset ?? 0;
const result = new InputBuffer({
const result = new InputBuffer<T>({
buffer: other._buffer,
bigEndian: other._bigEndian,
offset: other._offset + offsetFromOther,
Expand All @@ -108,26 +117,56 @@ export class InputBuffer {
/**
* Access the buffer relative from the current position.
*/
public getByte(index: number): number {
public get(index: number): number {
return this._buffer[this._offset + index];
}

/**
* Set a buffer element relative to the current position.
*/
public setByte(index: number, value: number) {
public set(index: number, value: number) {
return (this._buffer[this._offset + index] = value);
}

/**
* Copy data from **other** to this buffer, at **start** offset from the
* current read position, and **length** number of bytes. **offset** is
* the offset in **other** to start reading.
*/
public memcpy(
start: number,
length: number,
other: InputBuffer<T> | T,
offset: number = 0
): void {
if (other instanceof InputBuffer) {
ArrayUtils.copyRange(
other.buffer,
other.offset + offset,
this._buffer,
this.offset + start,
length
);
} else {
ArrayUtils.copyRange(
other,
offset,
this._buffer,
this.offset + start,
length
);
}
}

/**
* Set a range of bytes in this buffer to **value**, at **start** offset from the
* current read position, and **length** number of bytes.
*/
public memset(start: number, length: number, value: number): void {
this._buffer.fill(
value,
this._offset + start,
this._offset + start + length,
value
this._offset + start + length
);
}

Expand All @@ -138,10 +177,14 @@ export class InputBuffer {
* read position is used. If **length** is not specified, the remainder of this
* stream is used.
*/
public subarray(count: number, position?: number, offset = 0): InputBuffer {
public subarray(
count: number,
position?: number,
offset = 0
): InputBuffer<T> {
let pos = position !== undefined ? this._start + position : this._offset;
pos += offset;
return new InputBuffer({
return new InputBuffer<T>({
buffer: this._buffer,
bigEndian: this._bigEndian,
offset: pos,
Expand Down Expand Up @@ -169,7 +212,7 @@ export class InputBuffer {
* Read **count** bytes from an **offset** of the current read position, without
* moving the read position.
*/
public peekBytes(count: number, offset = 0): InputBuffer {
public peek(count: number, offset = 0): InputBuffer<T> {
return this.subarray(count, undefined, offset);
}

Expand All @@ -181,25 +224,28 @@ export class InputBuffer {
}

/**
* Read a single byte.
* Read a single value.
*/
public readByte(): number {
public read(): number {
return this._buffer[this._offset++];
}

public readInt8(): number {
return BitUtils.uint8ToInt8(this.readByte());
}

/**
* Read **count** bytes from the stream.
*/
public readBytes(count: number): InputBuffer {
public readRange(count: number): InputBuffer<T> {
const bytes = this.subarray(count);
this._offset += bytes.length;
return bytes;
}

/**
* Read 8-bit integer from the stream.
*/
public readInt8(): number {
return BitUtils.uint8ToInt8(this.read());
}

/**
* Read a null-terminated string, or if **length** is provided, that number of
* bytes returned as a string.
Expand All @@ -208,7 +254,7 @@ export class InputBuffer {
if (length === undefined) {
const codes: number[] = [];
while (!this.isEOS) {
const c = this.readByte();
const c = this.read();
if (c === 0) {
return String.fromCharCode(...codes);
}
Expand All @@ -217,7 +263,7 @@ export class InputBuffer {
throw new LibError('EOF reached without finding string terminator.');
}

const s = this.readBytes(length);
const s = this.readRange(length);
const bytes = s.toUint8Array();
const result = String.fromCharCode(...bytes);
return result;
Expand All @@ -229,7 +275,7 @@ export class InputBuffer {
public readStringUtf8(): string {
const codes: number[] = [];
while (!this.isEOS) {
const c = this.readByte();
const c = this.read();
if (c === 0) {
const array = new Uint8Array(codes);
return StringUtils.utf8Decoder.decode(array);
Expand Down Expand Up @@ -342,21 +388,31 @@ export class InputBuffer {
);
}

public toUint8Array(offset?: number, length?: number): Uint8Array {
const correctedOffset = offset ?? 0;
const correctedLength = length ?? this.length - correctedOffset;
return new Uint8Array(
this._buffer.buffer,
this._buffer.byteOffset + this._offset + correctedOffset,
correctedLength
public toUint8Array(offset: number = 0, length?: number): Uint8Array {
const correctedLength = length ?? this.length - offset;
if (this._buffer instanceof Uint8Array) {
return new Uint8Array(
this._buffer.buffer,
this._buffer.byteOffset + this._offset + offset,
correctedLength
);
}
return Uint8Array.from(
this._buffer.subarray(
this._offset + offset,
this._offset + offset + correctedLength
)
);
}

public toUint32Array(offset?: number): Uint32Array {
const correctedOffset = offset ?? 0;
return new Uint32Array(
this._buffer.buffer,
this._buffer.byteOffset + this._offset + correctedOffset
);
public toUint32Array(offset: number = 0): Uint32Array {
if (this._buffer instanceof Uint8Array) {
return new Uint32Array(
this._buffer.buffer,
this._buffer.byteOffset + this._offset + offset
);
}
const uint8array = this.toUint8Array();
return new Uint32Array(uint8array.buffer);
}
}
11 changes: 7 additions & 4 deletions src/exif/exif-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ export class ExifData extends IfdContainer {
}
}

private readEntry(block: InputBuffer, blockOffset: number): ExifEntry {
private readEntry(
block: InputBuffer<Uint8Array>,
blockOffset: number
): ExifEntry {
const tag = block.readUint16();
const format = block.readUint16();
const count = block.readUint32();
Expand All @@ -123,7 +126,7 @@ export class ExifData extends IfdContainer {
return entry;
}

const data = block.readBytes(size);
const data = block.readRange(size);

switch (f) {
case IfdValueType.none:
Expand Down Expand Up @@ -176,7 +179,7 @@ export class ExifData extends IfdContainer {
return new ExifData(dirs);
}

public static fromInputBuffer(input: InputBuffer) {
public static fromInputBuffer(input: InputBuffer<Uint8Array>) {
const data = new ExifData();
data.read(input);
return data;
Expand Down Expand Up @@ -309,7 +312,7 @@ export class ExifData extends IfdContainer {
out.bigEndian = saveEndian;
}

public read(block: InputBuffer): boolean {
public read(block: InputBuffer<Uint8Array>): boolean {
const saveEndian = block.bigEndian;
block.bigEndian = true;

Expand Down
2 changes: 1 addition & 1 deletion src/exif/ifd-value/ifd-ascii-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class IfdAsciiValue extends IfdValue {
}
}

public static data(data: InputBuffer, length: number) {
public static data(data: InputBuffer<Uint8Array>, length: number) {
// The final byte is a null terminator
const value = length > 0 ? data.readString(length - 1) : '';
return new IfdAsciiValue(value);
Expand Down
6 changes: 5 additions & 1 deletion src/exif/ifd-value/ifd-byte-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ export class IfdByteValue extends IfdValue {
}
}

public static data(data: InputBuffer, offset?: number, length?: number) {
public static data(
data: InputBuffer<Uint8Array>,
offset?: number,
length?: number
) {
const array = data.toUint8Array(offset, length);
return new IfdByteValue(array);
}
Expand Down
2 changes: 1 addition & 1 deletion src/exif/ifd-value/ifd-double-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class IfdDoubleValue extends IfdValue {
}
}

public static data(data: InputBuffer, length: number) {
public static data(data: InputBuffer<Uint8Array>, length: number) {
const array = new Float64Array(length);
for (let i = 0; i < length; ++i) {
array[i] = data.readFloat64();
Expand Down
2 changes: 1 addition & 1 deletion src/exif/ifd-value/ifd-long-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class IfdLongValue extends IfdValue {
}
}

public static data(data: InputBuffer, length: number) {
public static data(data: InputBuffer<Uint8Array>, length: number) {
const array = new Uint32Array(length);
for (let i = 0; i < length; ++i) {
array[i] = data.readUint32();
Expand Down
2 changes: 1 addition & 1 deletion src/exif/ifd-value/ifd-rational-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class IfdRationalValue extends IfdValue {
}
}

public static data(data: InputBuffer, length: number) {
public static data(data: InputBuffer<Uint8Array>, length: number) {
const array = new Array<Rational>();
for (let i = 0; i < length; i++) {
const r = new Rational(data.readUint32(), data.readUint32());
Expand Down
6 changes: 5 additions & 1 deletion src/exif/ifd-value/ifd-sbyte-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ export class IfdSByteValue extends IfdValue {
}
}

public static data(data: InputBuffer, offset?: number, length?: number) {
public static data(
data: InputBuffer<Uint8Array>,
offset?: number,
length?: number
) {
const array = new Int8Array(
new Int8Array(data.toUint8Array(offset, length).buffer)
);
Expand Down
2 changes: 1 addition & 1 deletion src/exif/ifd-value/ifd-short-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class IfdShortValue extends IfdValue {
}
}

public static data(data: InputBuffer, length: number) {
public static data(data: InputBuffer<Uint8Array>, length: number) {
const array = new Uint16Array(length);
for (let i = 0; i < length; ++i) {
array[i] = data.readUint16();
Expand Down
2 changes: 1 addition & 1 deletion src/exif/ifd-value/ifd-single-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class IfdSingleValue extends IfdValue {
}
}

public static data(data: InputBuffer, length: number) {
public static data(data: InputBuffer<Uint8Array>, length: number) {
const array = new Float32Array(length);
for (let i = 0; i < length; ++i) {
array[i] = data.readFloat32();
Expand Down
2 changes: 1 addition & 1 deletion src/exif/ifd-value/ifd-slong-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class IfdSLongValue extends IfdValue {
}
}

public static data(data: InputBuffer, length: number) {
public static data(data: InputBuffer<Uint8Array>, length: number) {
const array = new Int32Array(length);
for (let i = 0; i < length; ++i) {
array[i] = data.readInt32();
Expand Down
2 changes: 1 addition & 1 deletion src/exif/ifd-value/ifd-srational-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class IfdSRationalValue extends IfdValue {
}
}

public static data(data: InputBuffer, length: number) {
public static data(data: InputBuffer<Uint8Array>, length: number) {
const array = new Array<Rational>();
for (let i = 0; i < length; i++) {
const r = new Rational(data.readInt32(), data.readInt32());
Expand Down
2 changes: 1 addition & 1 deletion src/exif/ifd-value/ifd-sshort-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class IfdSShortValue extends IfdValue {
}
}

public static data(data: InputBuffer, length: number) {
public static data(data: InputBuffer<Uint8Array>, length: number) {
const array = new Int16Array(length);
for (let i = 0; i < length; ++i) {
array[i] = data.readInt16();
Expand Down
Loading

0 comments on commit cd23324

Please sign in to comment.