Skip to content

Commit 6e9a317

Browse files
committed
First commit
0 parents  commit 6e9a317

File tree

5 files changed

+2014
-0
lines changed

5 files changed

+2014
-0
lines changed

constants.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* Field type codes.
3+
* https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/wire_format_lite.h#L110
4+
*/
5+
export enum FieldType {
6+
INVALID,
7+
DOUBLE,
8+
FLOAT,
9+
INT64,
10+
UINT64,
11+
INT32,
12+
FIXED64,
13+
FIXED32,
14+
BOOL,
15+
STRING,
16+
GROUP,
17+
MESSAGE,
18+
BYTES,
19+
UINT32,
20+
ENUM,
21+
SFIXED32,
22+
SFIXED64,
23+
SINT32,
24+
SINT64
25+
}
26+
27+
/**
28+
* Wire-format type codes.
29+
* https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/wire_format_lite.h#L101
30+
*/
31+
export enum WireType {
32+
VARINT,
33+
FIXED64,
34+
DELIMITED,
35+
START_GROUP,
36+
END_GROUP,
37+
FIXED32
38+
}
39+
40+
/**
41+
* The largest finite float32 value.
42+
*/
43+
export const FLOAT32_MAX = 3.40282e38;
44+
45+
/**
46+
* The largest finite float64 value.
47+
*/
48+
export const FLOAT64_MAX = 1.79769e308;
49+
50+
/**
51+
* Convenience constant equal to 2^23.
52+
*/
53+
export const TWO_TO_23 = 8388608;
54+
55+
/**
56+
* Convenience constant equal to 2^31.
57+
*/
58+
export const TWO_TO_31 = 2147483648;
59+
60+
/**
61+
* Convenience constant equal to 2^32.
62+
*/
63+
export const TWO_TO_32 = 4294967296;
64+
65+
/**
66+
* Convenience constant equal to 2^52.
67+
*/
68+
export const TWO_TO_52 = 4503599627370496;
69+
70+
/**
71+
* Convenience constant equal to 2^63.
72+
*/
73+
export const TWO_TO_63 = 9223372036854775808n;
74+
75+
/**
76+
* Convenience constant equal to 2^64.
77+
*/
78+
export const TWO_TO_64 = 18446744073709551616n;

encoder.ts

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/**
2+
* BinaryEncoder implements encoders for all the wire types specified in
3+
* https://developers.google.com/protocol-buffers/docs/encoding.
4+
*/
5+
export class BinaryEncoder {
6+
private buffer: number[] = [];
7+
8+
get Length() {
9+
return this.buffer.length;
10+
}
11+
12+
get Buffer() {
13+
return this.buffer;
14+
}
15+
16+
/**
17+
* Encodes a 32-bit unsigned integer into its wire-format varint representation
18+
* and stores it in the buffer.
19+
*/
20+
UnsignedVarint32(value: number) {
21+
while (value > 127) {
22+
this.buffer.push((value & 0x7f) | 0x80);
23+
value = value >>> 7;
24+
}
25+
this.buffer.push(value);
26+
}
27+
28+
/**
29+
* Encodes a 32-bit signed integer into its wire-format varint representation
30+
* and stores it in the buffer.
31+
*/
32+
Varint32(v: number) {
33+
// Use the unsigned version if the value is not negative.
34+
if (v >= 0) {
35+
this.UnsignedVarint32(v);
36+
return;
37+
}
38+
39+
// Write nine bytes with a _signed_ right shift so we preserve the sign bit.
40+
for (var i = 0; i < 4; i++) {
41+
this.buffer.push((v & 0x7f) | 0x80);
42+
v = v >> 7;
43+
}
44+
45+
// The above loop writes out 28 bits, so the last byte is always the sign bit
46+
// which is always set for negative numbers.
47+
this.buffer.push(v);
48+
this.buffer.push(1);
49+
}
50+
51+
/**
52+
* Encodes a 64-bit unsigned integer into its wire-format varint representation
53+
* and stores it in the buffer. Integers that are not representable in 64 bits
54+
* will be truncated.
55+
*/
56+
UnsignedVarint(v: bigint) {
57+
while (v > 127) {
58+
this.buffer.push(Number((v & 0x7fn) | 0x80n));
59+
v = v >> 7n;
60+
}
61+
this.buffer.push(Number(v));
62+
}
63+
64+
/**
65+
* Encodes a 64-bit signed integer into its wire-format varint representation
66+
* and stores it in the buffer. Integers that are not representable in 64 bits
67+
* will be truncated.
68+
*/
69+
Varint(v: bigint) {
70+
// Use the unsigned version if the value is not negative.
71+
if (v >= 0n) {
72+
this.UnsignedVarint(v);
73+
return;
74+
}
75+
// Write nine bytes with a _signed_ right shift so we preserve the sign bit.
76+
for (var i = 0; i < 9; i++) {
77+
this.buffer.push(Number((v & 0x7fn) | 0x80n));
78+
v = v >> 7n;
79+
}
80+
// The above loop writes out 28 bits, so the last byte is always the sign bit
81+
// which is always set for negative numbers.
82+
this.buffer.push(1);
83+
}
84+
85+
/**
86+
* Encodes a JavaScript integer into its wire-format, zigzag-encoded varint
87+
* representation and stores it in the buffer.
88+
*/
89+
Zigzag32(value: number) {
90+
this.UnsignedVarint32(((value << 1) ^ (value >> 31)) >>> 0);
91+
}
92+
93+
/**
94+
* Encodes a JavaScript integer into its wire-format, zigzag-encoded varint
95+
* representation and stores it in the buffer. Integers not representable in 64
96+
* bits will be truncated.
97+
*/
98+
Zigzag64(x: bigint) {
99+
this.Varint((x << 1n) ^ (x >> 63n));
100+
}
101+
102+
/**
103+
* Writes a unsigned 32-bit integer to the buffer.
104+
*/
105+
Fixed32(value: number) {
106+
this.buffer.push((value >>> 0) & 0xff);
107+
this.buffer.push((value >>> 8) & 0xff);
108+
this.buffer.push((value >>> 16) & 0xff);
109+
this.buffer.push((value >>> 24) & 0xff);
110+
}
111+
112+
/**
113+
* Writes a unsigned 64-bit integer to the buffer.
114+
*/
115+
Fixed64(value: bigint) {
116+
this.buffer.push(Number(value >> 0n));
117+
this.buffer.push(Number(value >> 8n));
118+
this.buffer.push(Number(value >> 16n));
119+
this.buffer.push(Number(value >> 24n));
120+
this.buffer.push(Number(value >> 32n));
121+
this.buffer.push(Number(value >> 40n));
122+
this.buffer.push(Number(value >> 48n));
123+
this.buffer.push(Number(value >> 56n));
124+
}
125+
126+
/**
127+
* Writes a single-precision floating point value to the buffer. Numbers
128+
* requiring more than 32 bits of precision will be truncated.
129+
*/
130+
Float(value: number) {
131+
const frame = new ArrayBuffer(4);
132+
new DataView(frame).setFloat32(0, value, true);
133+
this.buffer.push(...new Uint8Array(frame));
134+
}
135+
136+
/**
137+
* Writes a double-precision floating point value to the buffer. As this is
138+
* the native format used by JavaScript, no precision will be lost.
139+
*/
140+
Double(value: number) {
141+
const frame = new ArrayBuffer(8);
142+
new DataView(frame).setFloat64(0, value, true);
143+
this.buffer.push(...new Uint8Array(frame));
144+
}
145+
146+
/**
147+
* Writes a boolean value to the buffer as a varint.
148+
*/
149+
Bool(value: boolean) {
150+
this.buffer.push(value ? 1 : 0);
151+
}
152+
153+
/**
154+
* Writes an enum value to the buffer as a varint.
155+
*/
156+
Enum(value: number) {
157+
this.Varint32(value);
158+
}
159+
160+
/**
161+
* Writes an arbitrary byte array to the buffer.
162+
*/
163+
Bytes(bytes: Uint8Array) {
164+
this.buffer.push.apply(bytes);
165+
}
166+
167+
/**
168+
* Writes a UTF16 Javascript string to the buffer encoded as UTF8.
169+
* Returns length of encoded string.
170+
*/
171+
String(str: string): number {
172+
const oLength = this.buffer.length;
173+
// https://gist.github.com/joni/3760795#file-toutf8array-js
174+
for (var i = 0; i < str.length; i++) {
175+
let charcode = str.charCodeAt(i);
176+
if (charcode < 0x80) this.buffer.push(charcode);
177+
else if (charcode < 0x800) {
178+
this.buffer.push(0xc0 | (charcode >> 6), 0x80 | (charcode & 0x3f));
179+
} else if (charcode < 0xd800 || charcode >= 0xe000) {
180+
this.buffer.push(
181+
0xe0 | (charcode >> 12),
182+
0x80 | ((charcode >> 6) & 0x3f),
183+
0x80 | (charcode & 0x3f)
184+
);
185+
}
186+
// surrogate pair
187+
else {
188+
i++;
189+
// UTF-16 encodes 0x10000-0x10FFFF by
190+
// subtracting 0x10000 and splitting the
191+
// 20 bits of 0x0-0xFFFFF into two halves
192+
charcode = 0x10000 + (((charcode & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
193+
this.buffer.push(
194+
0xf0 | (charcode >> 18),
195+
0x80 | ((charcode >> 12) & 0x3f),
196+
0x80 | ((charcode >> 6) & 0x3f),
197+
0x80 | (charcode & 0x3f)
198+
);
199+
}
200+
}
201+
202+
return this.buffer.length - oLength;
203+
}
204+
}

index.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { BinaryReader } from './reader';
2+
import { BinaryWriter } from './writer';
3+
export { BinaryReader, BinaryWriter };
4+
/**
5+
* Base class for all TsPb messages.
6+
*/
7+
export abstract class Message {
8+
/**
9+
* Serializes the message to binary data (in protobuf wire format).
10+
*/
11+
serializeBinary(): any[] {
12+
const writer = new BinaryWriter();
13+
this.serializeBinaryToWriter(writer);
14+
return writer.ResultBuffer;
15+
}
16+
/**
17+
* Deserializes binary data (in protobuf wire format).
18+
*/
19+
deserializeBinary(bytes: Uint8Array): this {
20+
const reader = new BinaryReader(bytes);
21+
return this.deserializeBinaryFromReader(reader);
22+
}
23+
/**
24+
* Serializes the given message to binary data (in protobuf wire
25+
* format), writing to the given BinaryWriter.
26+
*/
27+
abstract serializeBinaryToWriter(writer: BinaryWriter): void;
28+
/**
29+
* Deserializes binary data (in protobuf wire format) from the
30+
* given BinaryReader.
31+
*/
32+
abstract deserializeBinaryFromReader(reader: BinaryReader): this;
33+
/**
34+
* Unary request.
35+
* Unary response.
36+
* Deserializes binary data from protobuf wire format.
37+
*/
38+
Unary(bytes: Uint8Array): this {
39+
const reader = new BinaryReader(bytes);
40+
reader.Header();
41+
return this.deserializeBinaryFromReader(reader);
42+
}
43+
/**
44+
* Unary request.
45+
* Streaming response.
46+
* Deserializes binary data from protobuf wire format.
47+
*/
48+
Stream(bytes: Uint8Array, arr: this[]) {
49+
const reader = new BinaryReader(bytes);
50+
while (reader.Header()) {
51+
// TODO: I would like to do a reset on the object,
52+
// either delete all the properties,
53+
// or set all properties to undefined.
54+
const tmp = Object.create(
55+
Object.getPrototypeOf(this),
56+
Object.getOwnPropertyDescriptors(this)
57+
);
58+
arr.push(tmp.deserializeBinaryFromReader(reader));
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)