Skip to content

Commit 0e903dd

Browse files
committed
Add ability to read/write signed integers, and to choose Endianness. Bump library version to 0.0.4.
1 parent 05e03eb commit 0e903dd

27 files changed

+695
-88
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "byte-transcoder"
3-
version = "0.0.3"
3+
version = "0.0.4"
44
edition = "2024"
55
rust-version = "1.85"
66
authors = ["Todd Everett Griffin <tgriffin115@gmail.com>"]
@@ -36,4 +36,4 @@ pedantic = { level = "deny", priority = -1 }
3636
cargo = { level = "deny", priority = -1 }
3737

3838
[dependencies]
39-
uuid = "1.14.0"
39+
uuid = "1.16.0"

deno.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@todd/byte-transcoder",
3-
"version": "0.0.2",
3+
"version": "0.0.4",
44
"exports": {
55
".": "./src-ts/mod.ts"
66
},

examples/transcode.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use byte_transcoder::{reader::ByteReader, reader_error::ByteReaderResult};
1+
use byte_transcoder::{endian::Endian, reader::ByteReader, reader_error::ByteReaderResult};
22
use uuid::Uuid;
33

44
#[allow(dead_code)]
@@ -40,7 +40,7 @@ fn main() -> ByteReaderResult<()> {
4040
4, 84, 111, 100, 100,
4141
];
4242

43-
let mut byte_reader: ByteReader = ByteReader::new(&bytes);
43+
let mut byte_reader: ByteReader = ByteReader::new(&bytes, Endian::Little);
4444

4545
let game_id: Uuid = byte_reader.read_uuid()?;
4646
let join_code: String = byte_reader.read_string()?;

src-ts/byte-reader.ts

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import { validate } from "@std/uuid";
2+
import { Endian } from "./endian.ts";
23

34
/**
45
* Reads bytes from a Uint8Array.
56
*/
6-
export default class ByteReader {
7+
export class ByteReader {
78
private data: Uint8Array;
89
private position: number;
10+
private endian: Endian;
911

1012
/**
11-
* Wraps a Uint8Array to read from.
13+
* Reads bytes from a Uint8Array.
1214
*/
13-
constructor(data: Uint8Array) {
15+
constructor(data: Uint8Array, endian: Endian) {
1416
this.data = data;
1517
this.position = 0;
18+
this.endian = endian;
1619
}
1720

1821
/**
@@ -27,18 +30,44 @@ export default class ByteReader {
2730
}
2831

2932
/**
30-
* Reads the next two bytes as a little-endian unsigned 16-bit integer.
33+
* Reads the next byte as a signed 8-bit integer.
34+
*/
35+
public readI8(): number {
36+
if (this.position >= this.data.length) {
37+
throw new Error("End of buffer");
38+
}
39+
40+
const value = this.data[this.position++];
41+
return value > 127 ? value - 256 : value;
42+
}
43+
44+
/**
45+
* Reads the next two bytes as an unsigned 16-bit integer.
3146
*/
3247
public readU16(): number {
3348
if (this.position + 2 > this.data.length) {
3449
throw new Error("Not enough bytes to read u16");
3550
}
3651

37-
const value: number = this.data[this.position] | (this.data[this.position + 1] << 8);
52+
const value = this.endian === Endian.Little
53+
? this.data[this.position] | (this.data[this.position + 1] << 8)
54+
: (this.data[this.position] << 8) | this.data[this.position + 1];
3855
this.position += 2;
3956
return value;
4057
}
4158

59+
/**
60+
* Reads the next two bytes as a signed 16-bit integer.
61+
*/
62+
public readI16(): number {
63+
if (this.position + 2 > this.data.length) {
64+
throw new Error("Not enough bytes to read i16");
65+
}
66+
67+
const value = this.readU16();
68+
return value > 32767 ? value - 65536 : value;
69+
}
70+
4271
/**
4372
* Reads the next four bytes as an unsigned 32-bit integer.
4473
*/
@@ -47,25 +76,54 @@ export default class ByteReader {
4776
throw new Error("Not enough bytes to read u32");
4877
}
4978

50-
const value: number = this.data[this.position] |
51-
(this.data[this.position + 1] << 8) |
52-
(this.data[this.position + 2] << 16) |
53-
(this.data[this.position + 3] << 24);
79+
const value = this.endian === Endian.Little
80+
? this.data[this.position] |
81+
(this.data[this.position + 1] << 8) |
82+
(this.data[this.position + 2] << 16) |
83+
(this.data[this.position + 3] << 24)
84+
: (this.data[this.position] << 24) |
85+
(this.data[this.position + 1] << 16) |
86+
(this.data[this.position + 2] << 8) |
87+
this.data[this.position + 3];
5488
this.position += 4;
55-
return value >>> 0; // Convert to unsigned 32-bit integer
89+
return value >>> 0;
5690
}
5791

5892
/**
59-
* Reads the next eight bytes as a little-endian unsigned 64-bit integer.
93+
* Reads the next four bytes as a signed 32-bit integer.
94+
*/
95+
public readI32(): number {
96+
if (this.position + 4 > this.data.length) {
97+
throw new Error("Not enough bytes to read i32");
98+
}
99+
100+
const value = this.readU32();
101+
return value > 2147483647 ? value - 4294967296 : value;
102+
}
103+
104+
/**
105+
* Reads the next eight bytes as an unsigned 64-bit integer.
60106
*/
61107
public readU64(): bigint {
62108
if (this.position + 8 > this.data.length) {
63109
throw new Error("Not enough bytes to read u64");
64110
}
65111

66-
const low: bigint = BigInt(this.readU32());
67-
const high: bigint = BigInt(this.readU32());
68-
return (high << 32n) | low;
112+
const low = BigInt(this.readU32());
113+
const high = BigInt(this.readU32());
114+
return this.endian === Endian.Little ? (high << 32n) | low : (low << 32n) | high;
115+
}
116+
117+
/**
118+
* Reads the next eight bytes as a signed 64-bit integer.
119+
*/
120+
public readI64(): bigint {
121+
if (this.position + 8 > this.data.length) {
122+
throw new Error("Not enough bytes to read i64");
123+
}
124+
125+
const value = this.readU64();
126+
return value > 9223372036854775807n ? value - 18446744073709551616n : value;
69127
}
70128

71129
/**

src-ts/byte-writer.ts

Lines changed: 76 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import { validate } from "@std/uuid";
2+
import { Endian } from "./endian.ts";
23

34
/**
45
* Writes bytes to a Uint8Array.
56
*/
6-
export default class ByteWriter {
7+
export class ByteWriter {
78
private bytes: number[];
9+
private endian: Endian;
810

9-
constructor() {
11+
/**
12+
* Writes bytes to a Uint8Array.
13+
*/
14+
constructor(endian: Endian) {
1015
this.bytes = [];
16+
this.endian = endian;
1117
}
1218

1319
/**
@@ -22,44 +28,99 @@ export default class ByteWriter {
2228
}
2329

2430
/**
25-
* Writes an unsigned 16-bit integer as little-endian.
31+
* Writes a signed 8-bit integer.
32+
*/
33+
public writeI8(value: number): void {
34+
if (value < -128 || value > 127) {
35+
throw new Error(`Invalid i8 value: '${value}'`);
36+
}
37+
38+
this.bytes.push(value & 0xFF);
39+
}
40+
41+
/**
42+
* Writes an unsigned 16-bit integer.
2643
*/
2744
public writeU16(value: number): void {
2845
if (value < 0 || value > 65535) {
2946
throw new Error(`Invalid u16 value: '${value}'`);
3047
}
3148

32-
this.bytes.push(value & 0xFF); // least significant byte
33-
this.bytes.push((value >> 8) & 0xFF); // most significant byte
49+
const bytes: number[] = [value & 0xFF, (value >> 8) & 0xFF];
50+
this.bytes.push(...(this.endian === Endian.Little ? bytes : bytes.reverse()));
51+
}
52+
53+
/**
54+
* Writes a signed 16-bit integer.
55+
*/
56+
public writeI16(value: number): void {
57+
if (value < -32768 || value > 32767) {
58+
throw new Error(`Invalid i16 value: '${value}'`);
59+
}
60+
61+
const bytes: number[] = [value & 0xFF, (value >> 8) & 0xFF];
62+
this.bytes.push(...(this.endian === Endian.Little ? bytes : bytes.reverse()));
3463
}
3564

3665
/**
37-
* Writes an unsigned 32-bit integer as little-endian.
66+
* Writes an unsigned 32-bit integer.
3867
*/
3968
public writeU32(value: number): void {
4069
if (value < 0 || value > 4294967295) {
4170
throw new Error(`Invalid u32 value: '${value}'`);
4271
}
4372

44-
this.bytes.push(value & 0xFF); // least significant byte
45-
this.bytes.push((value >> 8) & 0xFF);
46-
this.bytes.push((value >> 16) & 0xFF);
47-
this.bytes.push((value >> 24) & 0xFF); // most significant byte
73+
const bytes: number[] = [
74+
value & 0xFF,
75+
(value >> 8) & 0xFF,
76+
(value >> 16) & 0xFF,
77+
(value >> 24) & 0xFF,
78+
];
79+
this.bytes.push(...(this.endian === Endian.Little ? bytes : bytes.reverse()));
80+
}
81+
82+
/**
83+
* Writes a signed 32-bit integer.
84+
*/
85+
public writeI32(value: number): void {
86+
if (value < -2147483648 || value > 2147483647) {
87+
throw new Error(`Invalid i32 value: '${value}'`);
88+
}
89+
90+
const bytes: number[] = [
91+
value & 0xFF,
92+
(value >> 8) & 0xFF,
93+
(value >> 16) & 0xFF,
94+
(value >> 24) & 0xFF,
95+
];
96+
this.bytes.push(...(this.endian === Endian.Little ? bytes : bytes.reverse()));
4897
}
4998

5099
/**
51-
* Writes an unsigned 64-bit integer as little-endian.
100+
* Writes an unsigned 64-bit integer.
52101
*/
53102
public writeU64(value: bigint): void {
54103
if (value < 0n || value > 18446744073709551615n) {
55104
throw new Error(`Invalid u64 value: '${value}'`);
56105
}
57106

58-
const low: number = Number(value & 0xFFFFFFFFn); // least significant byte
59-
const high: number = Number(value >> 32n); // most significant byte
107+
const bytes: number[] = Array.from({ length: 8 }, (_, i) => Number((value >> BigInt(i * 8)) & 0xFFn));
108+
this.bytes.push(...(this.endian === Endian.Little ? bytes : bytes.reverse()));
109+
}
110+
111+
/**
112+
* Writes a signed 64-bit integer.
113+
*/
114+
public writeI64(value: bigint): void {
115+
if (value < -9223372036854775808n || value > 9223372036854775807n) {
116+
throw new Error(`Invalid i64 value: '${value}'`);
117+
}
60118

61-
this.writeU32(low);
62-
this.writeU32(high);
119+
const bytes: number[] = Array.from(
120+
{ length: 8 },
121+
(_: unknown, i: number) => Number((value >> BigInt(i * 8)) & 0xFFn),
122+
);
123+
this.bytes.push(...(this.endian === Endian.Little ? bytes : bytes.reverse()));
63124
}
64125

65126
/**

src-ts/endian.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* Denotes the endianness of a byte array.
3+
*/
4+
export enum Endian {
5+
Little,
6+
Big,
7+
}

0 commit comments

Comments
 (0)