-
Notifications
You must be signed in to change notification settings - Fork 21
/
object-view.ts
96 lines (85 loc) · 3.15 KB
/
object-view.ts
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import { Constructor } from "./utility-types.ts";
import type { ComplexView, ViewInstance, ViewLayout } from "./view-types.ts";
export class ObjectView<T extends object> extends DataView
implements ComplexView<T> {
static viewLength: number;
static layout?: ViewLayout<unknown>;
static fields: Array<unknown>;
static defaultData?: Uint8Array;
static ObjectConstructor: Constructor<unknown>;
static decode<T extends object>(view: DataView, start = 0, _?: number): T {
const layout = this.layout as ViewLayout<T>;
const fields = this.fields as Array<keyof T>;
const result = new this.ObjectConstructor() as T;
for (let i = 0; i < fields.length; i++) {
const name = fields[i];
const { View, start: fieldStart, length: fieldLength } = layout[name];
result[name] = View.decode(view, start + fieldStart, fieldLength);
}
return result;
}
static encode<T extends object>(
value: T,
view: DataView,
start = 0,
length = this.viewLength,
amend?: boolean,
): number {
if (!amend) {
new Uint8Array(view.buffer, view.byteOffset + start, length).fill(0);
}
const layout = this.layout as ViewLayout<T>;
const fields = this.fields as Array<keyof T>;
for (let i = 0; i < fields.length; i++) {
const name = fields[i];
if (Reflect.has(value, name)) {
const { View, start: fieldStart, length: fieldLength } = layout[name];
View.encode(value[name], view, start + fieldStart, fieldLength);
}
}
return length;
}
static from<T extends object, U extends ObjectView<T>>(value: T): U {
const objectView = new this<T>(this.defaultData!.buffer.slice(0));
this.encode<T>(value, objectView, 0, this.viewLength, true);
return objectView as U;
}
static getLength(): number {
return this.viewLength;
}
get<P extends keyof T>(field: P): T[P] {
const layout = (this.constructor as typeof ObjectView)
.layout as ViewLayout<T>;
const { start, View, length } = layout[field];
return View.decode(this, start, length);
}
getLength<P extends keyof T>(field: P): number {
const layout = (this.constructor as typeof ObjectView)
.layout as ViewLayout<T>;
return layout[field].length;
}
getView<P extends keyof T>(field: P): ViewInstance<T[P]> {
const layout = (this.constructor as typeof ObjectView)
.layout as ViewLayout<T>;
const { View, start, length } = layout[field];
return new View(this.buffer, this.byteOffset + start, length);
}
set<P extends keyof T>(field: P, value: T[P]) {
const layout = (this.constructor as typeof ObjectView)
.layout as ViewLayout<T>;
const { start, View, length } = layout[field];
View.encode(value, this, start, length);
}
setView<P extends keyof T>(field: P, view: DataView) {
const layout = (this.constructor as typeof ObjectView)
.layout as ViewLayout<T>;
const { start } = layout[field];
new Uint8Array(this.buffer, this.byteOffset, this.byteLength).set(
new Uint8Array(view.buffer, view.byteOffset, view.byteLength),
start,
);
}
toJSON(): T {
return (this.constructor as typeof ObjectView).decode<T>(this, 0);
}
}