-
Notifications
You must be signed in to change notification settings - Fork 21
/
object-view.ts
136 lines (124 loc) · 4.3 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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import type { Constructor } from "./utility-types.ts";
import type {
ComplexView,
ViewConstructor,
ViewInstance,
ViewLayout,
ViewSchema,
} from "./view-types.ts";
import type { View } from "./view.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);
}
static initialize<T extends object>(
schema: ViewSchema<T>,
Factory: View,
constructor?: Constructor<T>,
): ViewConstructor<T, ComplexView<T>> {
const { getDefaultConstructor, getDefaultData } = Factory
.constructor as typeof View;
const fields = Object.keys(schema.properties!) as Array<keyof T>;
const layout = {} as ViewLayout<T>;
let lastOffset = 0;
for (const property of fields) {
const field = schema.properties![property];
const fieldLayout = Factory.getFieldLayout(
field,
lastOffset,
true,
property as string,
);
lastOffset += fieldLayout.length;
layout[property] = fieldLayout;
}
const defaultData = getDefaultData(layout, lastOffset, fields);
const ObjectConstructor = constructor ||
getDefaultConstructor(fields, layout);
return class extends this<T> {
static viewLength = lastOffset;
static layout = layout;
static fields = fields;
static defaultData = defaultData;
static ObjectConstructor = ObjectConstructor;
};
}
}