Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module.exports = {
testMatch: [
'**/__tests__/**/?(*.)+(spec|test).+(ts|tsx|js)',
'**/?(*.)+(spec|test).+(ts|tsx|js)',
'!**/dist/**/*',
],
transform: {
'^.+\\.(ts|tsx)$': 'ts-jest',
Expand Down
184 changes: 93 additions & 91 deletions src/packer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FormatTokenTuple } from './struct_type'
import { splitTokens, formatOrder } from './format'
import { splitTokens, formatOrder, Token } from './format'
import { WriteDataViewStream } from './stream'

export type Packer<T extends string> = (
Expand All @@ -17,101 +17,103 @@ export const packer = <T extends string>(format: T): Packer<T> => {
...args: FormatTokenTuple<T>
) => {
const stream = new WriteDataViewStream(buffer, bufferOffset, f.le)
tokens.forEach((token) => {
switch (token.token) {
case '?': {
for (let i = 0; i < token.count; i++) {
const v = args.shift()
if (typeof v !== 'boolean') {
throw Error('Invalid Argument type')
}
stream.writeUInt8(v ? 1 : 0)
}
break
}
case 'x': {
stream.skip(token.count)
break
}
case 'c': {
for (let i = 0; i < token.count; i++) {
const v = args.shift()
if (v instanceof Uint8Array) {
stream.writeUInt8(v[0])
} else if (typeof v === 'string') {
stream.writeUInt8(v.charCodeAt(0))
} else {
throw Error('Invalid Argument type')
}
}
break

const writeBoolean = (t: Token): void => {
for (let i = 0; i < t.count; i++) {
const v = args.shift()
if (typeof v !== 'boolean') {
throw Error('Invalid Argument type')
}
case 's': {
const v = args.shift()
if (!(v instanceof Uint8Array)) {
throw Error('Invalid Argument type')
}
for (let i = 0; i < token.count; i++) {
stream.writeUInt8(v[i] || 0)
}
break
stream.writeUInt8(v ? 1 : 0)
}
}
const skipPadding = (t: Token): void => {
stream.skip(t.count)
}

const writeChar = (t: Token): void => {
for (let i = 0; i < t.count; i++) {
const v = args.shift()
if (v instanceof Uint8Array) {
stream.writeUInt8(v[0])
} else if (typeof v === 'string') {
stream.writeUInt8(v.charCodeAt(0))
} else {
throw Error('Invalid Argument type')
}
case 'n':
case 'b':
case 'h':
case 'i':
case 'l':
case 'q': {
const size = sizeMap[token.token]
if (f.native) {
stream.aligngment(size)
}
for (let i = 0; i < token.count; i++) {
const v = args.shift()
if (typeof v !== 'number' && typeof v !== 'bigint') {
throw Error('Invalid Argument type')
}
stream.writeSInt(v, size)
}
break
}
}
const writeString = (t: Token): void => {
const v = args.shift()
if (!(v instanceof Uint8Array)) {
throw Error('Invalid Argument type')
}
for (let i = 0; i < t.count; i++) {
stream.writeUInt8(v[i] || 0)
}
}
const writeSInt = (t: Token): void => {
const size = sizeMap[t.token]
if (f.native) {
stream.aligngment(size)
}
for (let i = 0; i < t.count; i++) {
const v = args.shift()
if (typeof v !== 'number' && typeof v !== 'bigint') {
throw Error('Invalid Argument type')
}

case 'P':
case 'N':
case 'B':
case 'H':
case 'I':
case 'L':
case 'Q': {
const size = sizeMap[token.token]
if (f.native) {
stream.aligngment(size)
}
for (let i = 0; i < token.count; i++) {
const v = args.shift()
if (typeof v !== 'number' && typeof v !== 'bigint') {
throw Error('Invalid Argument')
}
stream.writeUInt(v, size)
}
break
stream.writeSInt(v, size)
}
}
const writeUInt = (t: Token): void => {
const size = sizeMap[t.token]
if (f.native) {
stream.aligngment(size)
}
for (let i = 0; i < t.count; i++) {
const v = args.shift()
if (typeof v !== 'number' && typeof v !== 'bigint') {
throw Error('Invalid Argument')
}
case 'f':
case 'd': {
const size = sizeMap[token.token]
if (f.native) {
stream.aligngment(size)
}
for (let i = 0; i < token.count; i++) {
const v = args.shift()
if (typeof v !== 'number') {
throw Error('Invalid Argument')
}
stream.writeFloat(v, size)
}
break
stream.writeUInt(v, size)
}
}
const writeFloat = (t: Token): void => {
const size = sizeMap[t.token]
if (f.native) {
stream.aligngment(size)
}
for (let i = 0; i < t.count; i++) {
const v = args.shift()
if (typeof v !== 'number') {
throw Error('Invalid Argument')
}
stream.writeFloat(v, size)
}
})
}

const tokenWriterMap: Record<Token['token'], (t: Token) => void> = {
'?': writeBoolean,
x: skipPadding,
c: writeChar,
s: writeString,
n: writeSInt,
b: writeSInt,
h: writeSInt,
i: writeSInt,
l: writeSInt,
q: writeSInt,
P: writeUInt,
N: writeUInt,
B: writeUInt,
H: writeUInt,
I: writeUInt,
L: writeUInt,
Q: writeUInt,
f: writeFloat,
d: writeFloat,
}

tokens.forEach((t) => tokenWriterMap[t.token](t))
}
}
37 changes: 13 additions & 24 deletions src/stream.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
class StreamPosition {
protected offset = 0

protected dataView: DataView
protected le: boolean
constructor(buffer: Uint8Array, offset: number, le: boolean) {
this.dataView = new DataView(buffer.buffer, buffer.byteOffset + offset)
this.le = le
}

aligngment(size: number) {
const { offset } = this
this.offset += (size - (offset % size)) % size
Expand All @@ -10,14 +17,7 @@ class StreamPosition {
}
}
export class WriteDataViewStream extends StreamPosition {
private dataView: DataView
private le: boolean
constructor(buffer: Uint8Array, offset: number, le: boolean) {
super()
this.dataView = new DataView(buffer.buffer, buffer.byteOffset + offset)
this.le = le
}
writeSInt(value: number | BigInt, size: number): void {
writeSInt(value: number | bigint, size: number): void {
const { offset, dataView, le } = this
this.offset += size
switch (size) {
Expand All @@ -32,11 +32,9 @@ export class WriteDataViewStream extends StreamPosition {
}
}
writeUInt8(value: number): void {
const { offset, dataView } = this
this.offset += 1
return dataView.setUint8(offset, value)
return this.writeUInt(value, 1)
}
writeUInt(value: number | BigInt, size: number): void {
writeUInt(value: number | bigint, size: number): void {
const { offset, dataView, le } = this
this.offset += size
switch (size) {
Expand All @@ -63,14 +61,7 @@ export class WriteDataViewStream extends StreamPosition {
}

export class ReadDataViewStream extends StreamPosition {
private dataView: DataView
private le: boolean
constructor(buffer: Uint8Array, offset: number, le: boolean) {
super()
this.dataView = new DataView(buffer.buffer, buffer.byteOffset + offset)
this.le = le
}
readSInt<S extends number>(size: S): number | BigInt {
readSInt<S extends number>(size: S): number | bigint {
const { offset, dataView, le } = this
this.offset += size
switch (size) {
Expand All @@ -86,11 +77,9 @@ export class ReadDataViewStream extends StreamPosition {
return 0
}
readUInt8(): number {
const { offset, dataView } = this
this.offset += 1
return dataView.getUint8(offset)
return Number(this.readUInt(1))
}
readUInt<S extends number>(size: S): number | BigInt {
readUInt<S extends number>(size: S): number | bigint {
const { offset, dataView, le } = this
this.offset += size
switch (size) {
Expand Down
2 changes: 1 addition & 1 deletion src/struct_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ type NativeTokenCharTypeMap = {
P: bigint
}

type TokenType = StandardTokenCharTypeMap[FormatChar]
export type TokenType = StandardTokenCharTypeMap[FormatChar]

type Digit = `${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`
type Char = Digit | FormatChar
Expand Down
Loading