-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
152 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
--- | ||
"@fleet-sdk/common": minor | ||
--- | ||
|
||
Add byte handling functions: | ||
|
||
- `hexToBytes()` - converts hex string to bytes; | ||
- `bytesToHex()` - converts bytes to hex string; | ||
- `utf8ToBytes()` - converts utf-8 string to bytes; | ||
- `bytesToUtf8()` - converts bytes to utf-8 string; | ||
- `concatBytes()` - concatenates various byte arrays; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { describe, expect, it, test } from "vitest"; | ||
import { bytesToHex, bytesToUtf8, concatBytes, hexToBytes, utf8ToBytes } from "./bytes"; | ||
|
||
const ui8 = (bytes: number[]) => Uint8Array.from(bytes); | ||
|
||
describe("Hex <> Bytes serialization", () => { | ||
it("Should convert hex to bytes", () => { | ||
expect(hexToBytes("deadbeef")).to.be.deep.equal(ui8([0xde, 0xad, 0xbe, 0xef])); | ||
expect(hexToBytes("cafe123456")).to.be.deep.equal(ui8([0xca, 0xfe, 0x12, 0x34, 0x56])); | ||
}); | ||
|
||
it("Should convert bytes to hex", () => { | ||
expect(bytesToHex(ui8([0xde, 0xad, 0xbe, 0xef]))).to.be.equal("deadbeef"); | ||
expect(bytesToHex(ui8([0xca, 0xfe, 0x12, 0x34, 0x56]))).to.be.equal("cafe123456"); | ||
}); | ||
|
||
it("Should roundtrip", () => { | ||
expect(bytesToHex(hexToBytes("deadbeef"))).to.be.deep.equal("deadbeef"); | ||
expect(hexToBytes(bytesToHex(ui8([0xca, 0xfe, 0x12, 0x34, 0x56])))).to.be.deep.equal( | ||
ui8([0xca, 0xfe, 0x12, 0x34, 0x56]) | ||
); | ||
}); | ||
|
||
test("Hex to byte with invalid inputs", () => { | ||
expect(() => hexToBytes("non hex string")).to.throw("Invalid byte sequence"); | ||
expect(() => hexToBytes("0643d437ee7")).to.throw("Invalid hex padding"); | ||
expect(() => hexToBytes(1 as unknown as string)).to.throw( | ||
"Expected an object of type 'string', got 'number'." | ||
); | ||
}); | ||
|
||
test("Bytes to hex with invalid inputs", () => { | ||
const invalidBytes = [1, -2, 2, -55] as unknown as Uint8Array; | ||
expect(() => bytesToHex(invalidBytes)).to.throw( | ||
"Expected an instance of 'Uint8Array', got 'Array'." | ||
); | ||
}); | ||
}); | ||
|
||
describe("UTF-8 <> bytes serialization", () => { | ||
it("Should roundtrip", () => { | ||
expect(bytesToUtf8(utf8ToBytes("this is a regular string"))).to.be.equal( | ||
"this is a regular string" | ||
); | ||
}); | ||
|
||
test("utf8 to bytes with invalid inputs", () => { | ||
const notAString = true as unknown as string; | ||
expect(() => utf8ToBytes(notAString)).to.throw( | ||
"Expected an object of type 'string', got 'boolean'." | ||
); | ||
}); | ||
|
||
test("bytes to utf8 with invalid inputs", () => { | ||
const invalidBytes = {} as unknown as Uint8Array; | ||
expect(() => bytesToUtf8(invalidBytes)).to.throw( | ||
"Expected an instance of 'Uint8Array', got 'Object'." | ||
); | ||
}); | ||
}); | ||
|
||
describe("Bytes concatenation", () => { | ||
it("Should concat bytes", () => { | ||
expect(concatBytes(ui8([0xde, 0xad]), ui8([0xbe, 0xef]))).to.be.deep.equal( | ||
ui8([0xde, 0xad, 0xbe, 0xef]) | ||
); | ||
|
||
expect(concatBytes(ui8([0xde, 0xad, 0xbe, 0xef]), ui8([]))).to.be.deep.equal( | ||
ui8([0xde, 0xad, 0xbe, 0xef]) | ||
); | ||
|
||
expect(concatBytes(ui8([]), ui8([0xde, 0xad, 0xbe, 0xef]))).to.be.deep.equal( | ||
ui8([0xde, 0xad, 0xbe, 0xef]) | ||
); | ||
}); | ||
|
||
it("Should fail with invalid inputs", () => { | ||
expect(() => concatBytes({} as unknown as Uint8Array, ui8([]))).to.throw( | ||
"Expected an instance of 'Uint8Array', got 'Object'." | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { assert, assertInstanceOf, assertTypeOf } from "."; | ||
|
||
export type StringEncoding = "hex" | "utf8"; | ||
|
||
const HEXES = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0")); | ||
|
||
export function hexToBytes(hex: string): Uint8Array { | ||
assertTypeOf(hex, "string"); | ||
assert(hex.length % 2 === 0, "Invalid hex padding."); | ||
|
||
const bytes = new Uint8Array(hex.length / 2); | ||
for (let i = 0; i < bytes.length; i++) { | ||
const j = i * 2; | ||
const hexByte = hex.slice(j, j + 2); | ||
const byte = parseInt(hexByte, 16); | ||
assert(!isNaN(byte) && byte >= 0, "Invalid byte sequence."); | ||
|
||
bytes[i] = byte; | ||
} | ||
|
||
return bytes; | ||
} | ||
|
||
export function bytesToHex(bytes: Uint8Array): string { | ||
assertInstanceOf(bytes, Uint8Array); | ||
|
||
let hex = ""; | ||
for (let i = 0; i < bytes.length; i++) { | ||
hex += HEXES[bytes[i]]; | ||
} | ||
|
||
return hex; | ||
} | ||
|
||
export function concatBytes(...arrays: Uint8Array[]): Uint8Array { | ||
const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0)); | ||
|
||
let pad = 0; | ||
for (const bytes of arrays) { | ||
assertInstanceOf(bytes, Uint8Array); | ||
|
||
r.set(bytes, pad); | ||
pad += bytes.length; | ||
} | ||
|
||
return r; | ||
} | ||
|
||
export function utf8ToBytes(str: string): Uint8Array { | ||
assertTypeOf(str, "string"); | ||
|
||
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809 | ||
} | ||
|
||
export function bytesToUtf8(bytes: Uint8Array): string { | ||
assertInstanceOf(bytes, Uint8Array); | ||
|
||
return new TextDecoder().decode(bytes); | ||
} |