-
Notifications
You must be signed in to change notification settings - Fork 1
/
gRPC.mjs
66 lines (63 loc) · 2.06 KB
/
gRPC.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import { $app } from "./lib/app.mjs";
import { Console } from "./polyfill/Console.mjs";
import pako from "pako";
/* https://grpc.io/ */
export class gRPC {
static decode(bytesBody = new Uint8Array([])) {
Console.log("☑️ gRPC.decode");
// 先拆分gRPC校验头和protobuf数据体
const Header = bytesBody.slice(0, 5);
let body = bytesBody.slice(5);
switch (Header[0]) {
case 0: // unGzip
default:
break;
case 1: // Gzip
switch ($app) {
case "Loon":
case "Surge":
case "Egern":
body = $utils.ungzip(body);
break;
default:
body = pako.ungzip(body); // 解压缩protobuf数据体
break;
}
Header[0] = 0; // unGzip
break;
}
Console.log("✅ gRPC.decode");
return body;
}
static encode(body = new Uint8Array([]), encoding = "identity") {
Console.log("☑️ gRPC.encode");
// Header: 1位:是否校验数据 (0或者1) + 4位:校验值(数据长度)
const Header = new Uint8Array(5);
const Checksum = gRPC.#Checksum(body.length); // 校验值为未压缩情况下的数据长度, 不是压缩后的长度
Header.set(Checksum, 1); // 1-4位: 校验值(4位)
switch (encoding) {
case "gzip":
Header.set([1], 0); // 0位:Encoding类型,当为1的时候, app会校验1-4位的校验值是否正确
body = pako.gzip(body);
break;
case "identity":
case undefined:
default:
Header.set([0], 0); // 0位:Encoding类型,当为1的时候, app会校验1-4位的校验值是否正确
break;
}
const BytesBody = new Uint8Array(Header.length + body.length);
BytesBody.set(Header, 0); // 0-4位:gRPC校验头
BytesBody.set(body, 5); // 5-end位:protobuf数据
Console.log("✅ gRPC.encode");
return BytesBody;
}
// 计算校验和 (B站为数据本体字节数)
static #Checksum = (num = 0) => {
const array = new ArrayBuffer(4); // an Int32 takes 4 bytes
const view = new DataView(array);
// 首位填充计算过的新数据长度
view.setUint32(0, num, false); // byteOffset = 0; litteEndian = false
return new Uint8Array(array);
};
}