Skip to content

Commit

Permalink
Add serializeNumber/deserializeNumber
Browse files Browse the repository at this point in the history
  • Loading branch information
allevo committed Dec 18, 2023
1 parent 6ea14f7 commit 75d1fd9
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 18 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ This library exports the following functions:
- `des.deserializeBoolean()`: deserializes a boolean value.
- `ser.serializeUInt32(uint32)`: serializes a 32-bit unsigned integer.
- `des.deserializeUInt32(uint32)`: deserializes a 32-bit unsigned integer.
- `ser.serializeNumber(n)`: serializes a 32-bit unsigned integer or signed integer or float.
- `des.deserializeNumber(n)`: deserializes a 32-bit unsigned integer or signed integer or float.
- `ser.serializeString(string)`: serializes a string.
- `des.deserializeString()`: deserializes a string.
- `ser.serializeArray(array, (ser, item) => { ... })`: serializes an array.
Expand Down
36 changes: 36 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@

type StrictArrayBuffer = ArrayBuffer & { buffer?: undefined }

const TYPE_FLOAT = 0
const TYPE_UINT32 = 1
const TYPE_INT32 = 2
const POW_2_32 = 2 ** 32

export interface Ser {
index: number
buffer: ArrayBuffer
Expand All @@ -11,6 +16,7 @@ export interface Ser {
serializeBoolean: (b: boolean) => void
serializeUInt32: (n: number) => void
serializeFloat32: (n: number) => void
serializeNumber: (n: number) => void
serializeString: (str: string) => void
serializeArray: <T>(arr: T[], serialize: (ser: Ser, t: T) => void) => void
serializeIterable: <T>(iterable: Iterable<T>, serialize: (ser: Ser, t: T) => void) => void
Expand All @@ -26,6 +32,7 @@ export interface Des {
deserializeBoolean: () => boolean
deserializeUInt32: () => number
deserializeFloat32: () => number
deserializeNumber: () => number
deserializeString: () => string
deserializeArray: <T>(deserialize: (des: Des) => T) => T[]
deserializeIterable: <T>(deserialize: (des: Des) => T) => Iterable<T>
Expand All @@ -52,6 +59,7 @@ export function createSer ({ bufferSize }: CreateSerOption = {}): Ser {
serializeBoolean,
serializeUInt32,
serializeFloat32,
serializeNumber,
serializeString,
serializeArray,
serializeIterable,
Expand Down Expand Up @@ -90,6 +98,7 @@ export function createDes (buffer: StrictArrayBuffer): Des {
deserializeBoolean,
deserializeUInt32,
deserializeFloat32,
deserializeNumber,
deserializeString,
deserializeArray,
deserializeIterable,
Expand Down Expand Up @@ -117,6 +126,33 @@ function serializeFloat32 (this: Ser, n: number): void {
function deserializeFloat32 (this: Des): number {
return this.float32Array[this.index++]
}
function serializeNumber (this: Ser, n: number): void {
// If it's not an integer
if (n % 1 !== 0) {
this.uint32Array[this.index++] = TYPE_FLOAT
this.serializeFloat32(n)
} else {
if (n >= 0) {
this.uint32Array[this.index++] = TYPE_UINT32
this.serializeUInt32(n)
} else {
this.uint32Array[this.index++] = TYPE_INT32
this.uint32Array[this.index++] = POW_2_32 + n
}
}
}
function deserializeNumber (this: Des): number {
const type = this.uint32Array[this.index++]
if (type === TYPE_FLOAT) {
return this.deserializeFloat32()
} else if (type === TYPE_UINT32) {
return this.deserializeUInt32()
} else if (type === TYPE_INT32) {
return this.uint32Array[this.index++] - POW_2_32
} else {
throw new Error('Unknown type')
}
}

const textEncoder = new TextEncoder()
function serializeString (this: Ser, str: string): void {
Expand Down
73 changes: 55 additions & 18 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ await t.test('boolean', async t => {

await t.test('uint32', async t => {
const numbers = [
0, 1, 10000
0, 1, 5, 123, 42, 10000
]

for (const expected of numbers) {
Expand All @@ -59,22 +59,55 @@ await t.test('uint32', async t => {

await t.test('serialize multiple numbers', () => {
const ser = createSer()
ser.serializeUInt32(1)
ser.serializeUInt32(5)
ser.serializeUInt32(123)
ser.serializeUInt32(42)
for (const n of numbers) {
ser.serializeUInt32(n)
}

const des = createDes(ser.getBuffer())

const n1 = des.deserializeUInt32()
const n2 = des.deserializeUInt32()
const n3 = des.deserializeUInt32()
const n4 = des.deserializeUInt32()
for (let i = 0; i < numbers.length; i++) {
assert.equal(des.deserializeUInt32(), numbers[i])
}
})
})

await t.test('number', async t => {
const numbers = [
0, 0.0, -0, -0.0, 42, -42, 42.42, -42.42
]

for (const expected of numbers) {
await t.test(`serialize ${expected}`, () => {
const ser = createSer()
ser.serializeNumber(expected)

const des = createDes(ser.getBuffer())

const actual = des.deserializeNumber()

if (isFloat(expected)) {
assertFloat32Equals(actual, expected)
} else {
assert.equal(actual, expected)
}
})
}

assert.equal(n1, 1)
assert.equal(n2, 5)
assert.equal(n3, 123)
assert.equal(n4, 42)
await t.test('serialize multiple numbers', () => {
const ser = createSer()
for (const n of numbers) {
ser.serializeNumber(n)
}

const des = createDes(ser.getBuffer())

for (let i = 0; i < numbers.length; i++) {
if (isFloat(numbers[i])) {
assertFloat32Equals(des.deserializeNumber(), numbers[i])
} else {
assert.equal(des.deserializeNumber(), numbers[i])
}
}
})
})

Expand All @@ -91,7 +124,7 @@ await t.test('float32', async t => {
const des = createDes(ser.getBuffer())

const actual = des.deserializeFloat32()
assert.ok(Math.abs(actual - expected) < 0.0001)
assertFloat32Equals(actual, expected)
})
}

Expand All @@ -109,10 +142,10 @@ await t.test('float32', async t => {
const n3 = des.deserializeFloat32()
const n4 = des.deserializeFloat32()

assert.ok(Math.abs(n1 - -3) < 0.0001)
assert.ok(Math.abs(n2 - 55.5) < 0.0001)
assert.ok(Math.abs(n3 - 42.42) < 0.0001)
assert.ok(Math.abs(n4 - 33.3) < 0.0001)
assertFloat32Equals(n1, -3)
assertFloat32Equals(n2, 55.5)
assertFloat32Equals(n3, 42.42)
assertFloat32Equals(n4, 33.3)
})
})

Expand Down Expand Up @@ -466,3 +499,7 @@ function permutator (inputArr: any): any[][] {
function isFloat (n: number): boolean {
return Number(n) === n && n % 1 !== 0
}

function assertFloat32Equals (a: number, b: number): void {
assert.ok(Math.abs(a - b) < 0.0001)
}

0 comments on commit 75d1fd9

Please sign in to comment.