@colyseus/schema 3.0.0-alpha.9 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/README.md +148 -62
  2. package/bin/schema-debug +94 -0
  3. package/build/cjs/index.js +2222 -1513
  4. package/build/cjs/index.js.map +1 -1
  5. package/build/esm/index.mjs +2223 -1516
  6. package/build/esm/index.mjs.map +1 -1
  7. package/build/umd/index.js +2225 -1516
  8. package/lib/Metadata.d.ts +21 -9
  9. package/lib/Metadata.js +169 -32
  10. package/lib/Metadata.js.map +1 -1
  11. package/lib/Reflection.d.ts +19 -4
  12. package/lib/Reflection.js +66 -31
  13. package/lib/Reflection.js.map +1 -1
  14. package/lib/Schema.d.ts +12 -5
  15. package/lib/Schema.js +57 -56
  16. package/lib/Schema.js.map +1 -1
  17. package/lib/annotations.d.ts +31 -34
  18. package/lib/annotations.js +110 -160
  19. package/lib/annotations.js.map +1 -1
  20. package/lib/bench_encode.d.ts +1 -0
  21. package/lib/bench_encode.js +130 -0
  22. package/lib/bench_encode.js.map +1 -0
  23. package/lib/codegen/api.js +1 -2
  24. package/lib/codegen/api.js.map +1 -1
  25. package/lib/codegen/languages/cpp.js +1 -2
  26. package/lib/codegen/languages/cpp.js.map +1 -1
  27. package/lib/codegen/languages/csharp.js +9 -46
  28. package/lib/codegen/languages/csharp.js.map +1 -1
  29. package/lib/codegen/languages/haxe.js +4 -2
  30. package/lib/codegen/languages/haxe.js.map +1 -1
  31. package/lib/codegen/languages/java.js +1 -2
  32. package/lib/codegen/languages/java.js.map +1 -1
  33. package/lib/codegen/languages/js.js +1 -2
  34. package/lib/codegen/languages/js.js.map +1 -1
  35. package/lib/codegen/languages/lua.js +23 -25
  36. package/lib/codegen/languages/lua.js.map +1 -1
  37. package/lib/codegen/languages/ts.js +1 -2
  38. package/lib/codegen/languages/ts.js.map +1 -1
  39. package/lib/codegen/parser.js +85 -3
  40. package/lib/codegen/parser.js.map +1 -1
  41. package/lib/codegen/types.js +6 -3
  42. package/lib/codegen/types.js.map +1 -1
  43. package/lib/debug.d.ts +1 -0
  44. package/lib/debug.js +51 -0
  45. package/lib/debug.js.map +1 -0
  46. package/lib/decoder/DecodeOperation.d.ts +3 -4
  47. package/lib/decoder/DecodeOperation.js +35 -17
  48. package/lib/decoder/DecodeOperation.js.map +1 -1
  49. package/lib/decoder/Decoder.d.ts +5 -6
  50. package/lib/decoder/Decoder.js +10 -10
  51. package/lib/decoder/Decoder.js.map +1 -1
  52. package/lib/decoder/ReferenceTracker.js +4 -2
  53. package/lib/decoder/ReferenceTracker.js.map +1 -1
  54. package/lib/decoder/strategy/RawChanges.js +1 -2
  55. package/lib/decoder/strategy/RawChanges.js.map +1 -1
  56. package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
  57. package/lib/decoder/strategy/StateCallbacks.js +74 -64
  58. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  59. package/lib/encoder/ChangeTree.d.ts +28 -20
  60. package/lib/encoder/ChangeTree.js +242 -188
  61. package/lib/encoder/ChangeTree.js.map +1 -1
  62. package/lib/encoder/EncodeOperation.d.ts +3 -6
  63. package/lib/encoder/EncodeOperation.js +51 -65
  64. package/lib/encoder/EncodeOperation.js.map +1 -1
  65. package/lib/encoder/Encoder.d.ts +8 -7
  66. package/lib/encoder/Encoder.js +128 -79
  67. package/lib/encoder/Encoder.js.map +1 -1
  68. package/lib/encoder/Root.d.ts +22 -0
  69. package/lib/encoder/Root.js +81 -0
  70. package/lib/encoder/Root.js.map +1 -0
  71. package/lib/encoder/StateView.d.ts +7 -7
  72. package/lib/encoder/StateView.js +72 -74
  73. package/lib/encoder/StateView.js.map +1 -1
  74. package/lib/encoding/assert.d.ts +7 -6
  75. package/lib/encoding/assert.js +13 -5
  76. package/lib/encoding/assert.js.map +1 -1
  77. package/lib/encoding/decode.d.ts +36 -19
  78. package/lib/encoding/decode.js +54 -84
  79. package/lib/encoding/decode.js.map +1 -1
  80. package/lib/encoding/encode.d.ts +36 -18
  81. package/lib/encoding/encode.js +61 -48
  82. package/lib/encoding/encode.js.map +1 -1
  83. package/lib/encoding/spec.d.ts +4 -5
  84. package/lib/encoding/spec.js +1 -2
  85. package/lib/encoding/spec.js.map +1 -1
  86. package/lib/index.d.ts +10 -9
  87. package/lib/index.js +24 -17
  88. package/lib/index.js.map +1 -1
  89. package/lib/types/HelperTypes.d.ts +34 -2
  90. package/lib/types/HelperTypes.js.map +1 -1
  91. package/lib/types/TypeContext.d.ts +29 -0
  92. package/lib/types/TypeContext.js +151 -0
  93. package/lib/types/TypeContext.js.map +1 -0
  94. package/lib/types/custom/ArraySchema.d.ts +2 -2
  95. package/lib/types/custom/ArraySchema.js +33 -22
  96. package/lib/types/custom/ArraySchema.js.map +1 -1
  97. package/lib/types/custom/CollectionSchema.d.ts +2 -2
  98. package/lib/types/custom/CollectionSchema.js +1 -0
  99. package/lib/types/custom/CollectionSchema.js.map +1 -1
  100. package/lib/types/custom/MapSchema.d.ts +18 -16
  101. package/lib/types/custom/MapSchema.js +12 -4
  102. package/lib/types/custom/MapSchema.js.map +1 -1
  103. package/lib/types/custom/SetSchema.d.ts +2 -2
  104. package/lib/types/custom/SetSchema.js +1 -0
  105. package/lib/types/custom/SetSchema.js.map +1 -1
  106. package/lib/types/registry.d.ts +8 -1
  107. package/lib/types/registry.js +23 -6
  108. package/lib/types/registry.js.map +1 -1
  109. package/lib/types/symbols.d.ts +8 -5
  110. package/lib/types/symbols.js +9 -6
  111. package/lib/types/symbols.js.map +1 -1
  112. package/lib/types/utils.js +1 -2
  113. package/lib/types/utils.js.map +1 -1
  114. package/lib/utils.js +9 -7
  115. package/lib/utils.js.map +1 -1
  116. package/package.json +19 -18
  117. package/src/Metadata.ts +190 -42
  118. package/src/Reflection.ts +76 -38
  119. package/src/Schema.ts +72 -70
  120. package/src/annotations.ts +156 -202
  121. package/src/bench_encode.ts +108 -0
  122. package/src/codegen/languages/csharp.ts +8 -47
  123. package/src/codegen/languages/haxe.ts +4 -0
  124. package/src/codegen/languages/lua.ts +19 -27
  125. package/src/codegen/parser.ts +107 -0
  126. package/src/codegen/types.ts +1 -0
  127. package/src/debug.ts +55 -0
  128. package/src/decoder/DecodeOperation.ts +43 -15
  129. package/src/decoder/Decoder.ts +12 -10
  130. package/src/decoder/ReferenceTracker.ts +5 -3
  131. package/src/decoder/strategy/StateCallbacks.ts +152 -81
  132. package/src/encoder/ChangeTree.ts +282 -209
  133. package/src/encoder/EncodeOperation.ts +78 -78
  134. package/src/encoder/Encoder.ts +152 -87
  135. package/src/encoder/Root.ts +93 -0
  136. package/src/encoder/StateView.ts +80 -88
  137. package/src/encoding/assert.ts +17 -8
  138. package/src/encoding/decode.ts +73 -93
  139. package/src/encoding/encode.ts +76 -45
  140. package/src/encoding/spec.ts +3 -5
  141. package/src/index.ts +12 -20
  142. package/src/types/HelperTypes.ts +54 -2
  143. package/src/types/TypeContext.ts +175 -0
  144. package/src/types/custom/ArraySchema.ts +49 -19
  145. package/src/types/custom/CollectionSchema.ts +1 -0
  146. package/src/types/custom/MapSchema.ts +30 -17
  147. package/src/types/custom/SetSchema.ts +1 -0
  148. package/src/types/registry.ts +22 -3
  149. package/src/types/symbols.ts +10 -7
  150. package/src/utils.ts +7 -3
@@ -0,0 +1,93 @@
1
+ import { OPERATION } from "../encoding/spec";
2
+ import { TypeContext } from "../types/TypeContext";
3
+ import { spliceOne } from "../types/utils";
4
+ import { ChangeTree, setOperationAtIndex } from "./ChangeTree";
5
+
6
+ export class Root {
7
+ protected nextUniqueId: number = 0;
8
+
9
+ refCount: {[id: number]: number} = {};
10
+ changeTrees: {[refId: number]: ChangeTree} = {};
11
+
12
+ // all changes
13
+ allChanges: ChangeTree[] = [];
14
+ allFilteredChanges: ChangeTree[] = [];// TODO: do not initialize it if filters are not used
15
+
16
+ // pending changes to be encoded
17
+ changes: ChangeTree[] = [];
18
+ filteredChanges: ChangeTree[] = [];// TODO: do not initialize it if filters are not used
19
+
20
+ constructor(public types: TypeContext) { }
21
+
22
+ getNextUniqueId() {
23
+ return this.nextUniqueId++;
24
+ }
25
+
26
+ add(changeTree: ChangeTree) {
27
+ // FIXME: move implementation of `ensureRefId` to `Root` class
28
+ changeTree.ensureRefId();
29
+
30
+ const isNewChangeTree = (this.changeTrees[changeTree.refId] === undefined);
31
+ if (isNewChangeTree) { this.changeTrees[changeTree.refId] = changeTree; }
32
+
33
+ const previousRefCount = this.refCount[changeTree.refId];
34
+ if (previousRefCount === 0) {
35
+ //
36
+ // When a ChangeTree is re-added, it means that it was previously removed.
37
+ // We need to re-add all changes to the `changes` map.
38
+ //
39
+ const ops = changeTree.allChanges.operations;
40
+ let len = ops.length;
41
+ while (len--) {
42
+ changeTree.indexedOperations[ops[len]] = OPERATION.ADD;
43
+ setOperationAtIndex(changeTree.changes, len);
44
+ }
45
+ }
46
+
47
+ this.refCount[changeTree.refId] = (previousRefCount || 0) + 1;
48
+
49
+ return isNewChangeTree;
50
+ }
51
+
52
+ remove(changeTree: ChangeTree) {
53
+ const refCount = (this.refCount[changeTree.refId]) - 1;
54
+
55
+ if (refCount <= 0) {
56
+ //
57
+ // Only remove "root" reference if it's the last reference
58
+ //
59
+ changeTree.root = undefined;
60
+ delete this.changeTrees[changeTree.refId];
61
+
62
+ this.removeChangeFromChangeSet("allChanges", changeTree);
63
+ this.removeChangeFromChangeSet("changes", changeTree);
64
+
65
+ if (changeTree.filteredChanges) {
66
+ this.removeChangeFromChangeSet("allFilteredChanges", changeTree);
67
+ this.removeChangeFromChangeSet("filteredChanges", changeTree);
68
+ }
69
+
70
+ this.refCount[changeTree.refId] = 0;
71
+
72
+ } else {
73
+ this.refCount[changeTree.refId] = refCount;
74
+ }
75
+
76
+ changeTree.forEachChild((child, _) => this.remove(child));
77
+
78
+ return refCount;
79
+ }
80
+
81
+ removeChangeFromChangeSet(changeSetName: "allChanges" | "changes" | "filteredChanges" | "allFilteredChanges", changeTree: ChangeTree) {
82
+ const changeSet = this[changeSetName];
83
+ const index = changeSet.indexOf(changeTree);
84
+ if (index !== -1) {
85
+ spliceOne(changeSet, index);
86
+ // changeSet[index] = undefined;
87
+ }
88
+ }
89
+
90
+ clear() {
91
+ this.changes.length = 0;
92
+ }
93
+ }
@@ -1,11 +1,11 @@
1
- import { ChangeTree, Ref } from "./ChangeTree";
2
- import { $changes } from "../types/symbols";
1
+ import { ChangeSet, ChangeTree, IndexedOperations, Ref } from "./ChangeTree";
2
+ import { $changes, $fieldIndexesByViewTag, $viewFieldIndexes } from "../types/symbols";
3
3
  import { DEFAULT_VIEW_TAG } from "../annotations";
4
4
  import { OPERATION } from "../encoding/spec";
5
5
  import { Metadata } from "../Metadata";
6
- import type { Schema } from "../Schema";
7
6
 
8
- export function createView(root: Schema) {
7
+ export function createView() {
8
+ return new StateView();
9
9
  }
10
10
 
11
11
  export class StateView {
@@ -25,37 +25,35 @@ export class StateView {
25
25
  * Manual "ADD" operations for changes per ChangeTree, specific to this view.
26
26
  * (This is used to force encoding a property, even if it was not changed)
27
27
  */
28
- changes = new Map<ChangeTree, Map<number, OPERATION>>();
28
+ changes: { [refId: number]: IndexedOperations } = {};
29
29
 
30
30
  // TODO: allow to set multiple tags at once
31
- add(obj: Ref, tag: number = DEFAULT_VIEW_TAG) {
31
+ add(obj: Ref, tag: number = DEFAULT_VIEW_TAG, checkIncludeParent: boolean = true) {
32
32
  if (!obj[$changes]) {
33
33
  console.warn("StateView#add(), invalid object:", obj);
34
34
  return this;
35
35
  }
36
36
 
37
- let changeTree: ChangeTree = obj[$changes];
38
- this.items.add(changeTree);
39
-
40
- // Add children of this ChangeTree to this view
41
- changeTree.forEachChild((change, _) =>
42
- this.add(change.ref, tag));
43
-
44
- // FIXME: ArraySchema/MapSchema does not have metadata
37
+ // FIXME: ArraySchema/MapSchema do not have metadata
45
38
  const metadata: Metadata = obj.constructor[Symbol.metadata];
39
+ const changeTree: ChangeTree = obj[$changes];
40
+ this.items.add(changeTree);
46
41
 
47
- // add parent ChangeTree's, if they are invisible to this view
48
- // TODO: REFACTOR addParent()
49
- this.addParent(changeTree, tag);
42
+ // add parent ChangeTree's
43
+ // - if it was invisible to this view
44
+ // - if it were previously filtered out
45
+ if (checkIncludeParent && changeTree.parent) {
46
+ this.addParent(changeTree.parent[$changes], changeTree.parentIndex, tag);
47
+ }
50
48
 
51
49
  //
52
50
  // TODO: when adding an item of a MapSchema, the changes may not
53
51
  // be set (only the parent's changes are set)
54
52
  //
55
- let changes = this.changes.get(changeTree);
53
+ let changes = this.changes[changeTree.refId];
56
54
  if (changes === undefined) {
57
- changes = new Map<number, OPERATION>();
58
- this.changes.set(changeTree, changes)
55
+ changes = {};
56
+ this.changes[changeTree.refId] = changes;
59
57
  }
60
58
 
61
59
  // set tag
@@ -72,99 +70,93 @@ export class StateView {
72
70
  }
73
71
  tags.add(tag);
74
72
 
75
- // console.log("BY TAG:", tag);
76
-
77
73
  // Ref: add tagged properties
78
- metadata?.[-3]?.[tag]?.forEach((index) => {
74
+ metadata?.[$fieldIndexesByViewTag]?.[tag]?.forEach((index) => {
79
75
  if (changeTree.getChange(index) !== OPERATION.DELETE) {
80
- changes.set(index, OPERATION.ADD)
76
+ changes[index] = OPERATION.ADD;
81
77
  }
82
78
  });
83
79
 
84
80
  } else {
85
-
86
- // console.log("DEFAULT TAG", changeTree.allChanges);
87
-
88
- // // add default tag properties
89
- // metadata?.[-3]?.[DEFAULT_VIEW_TAG]?.forEach((index) => {
90
- // if (changeTree.getChange(index) !== OPERATION.DELETE) {
91
- // changes.set(index, OPERATION.ADD);
92
- // }
93
- // });
94
-
95
- const allChangesSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
81
+ const isInvisible = this.invisible.has(changeTree);
82
+ const changeSet = (changeTree.filteredChanges !== undefined)
96
83
  ? changeTree.allFilteredChanges
97
84
  : changeTree.allChanges;
98
- const it = allChangesSet.keys();
99
- const isInvisible = this.invisible.has(changeTree);
100
85
 
101
- for (const index of it) {
86
+ for (let i = 0, len = changeSet.operations.length; i < len; i++) {
87
+ const index = changeSet.operations[i];
88
+ if (index === undefined) { continue; } // skip "undefined" indexes
89
+
90
+ const op = changeTree.indexedOperations[index] ?? OPERATION.ADD;
91
+ const tagAtIndex = metadata?.[index].tag;
102
92
  if (
103
- (isInvisible || metadata?.[metadata?.[index]].tag === tag) &&
104
- changeTree.getChange(index) !== OPERATION.DELETE
93
+ (
94
+ isInvisible || // if "invisible", include all
95
+ tagAtIndex === undefined || // "all change" with no tag
96
+ tagAtIndex === tag // tagged property
97
+ ) &&
98
+ op !== OPERATION.DELETE
105
99
  ) {
106
- changes.set(index, OPERATION.ADD);
100
+ changes[index] = op;
107
101
  }
108
102
  }
109
103
  }
110
104
 
111
- // TODO: avoid unnecessary iteration here
112
- while (
113
- changeTree.parent &&
114
- (changeTree = changeTree.parent[$changes]) &&
115
- (changeTree.isFiltered || changeTree.isPartiallyFiltered)
116
- ) {
117
- this.items.add(changeTree);
118
- }
105
+ // Add children of this ChangeTree to this view
106
+ changeTree.forEachChild((change, index) => {
107
+ // Do not ADD children that don't have the same tag
108
+ if (
109
+ metadata &&
110
+ metadata[index].tag !== undefined &&
111
+ metadata[index].tag !== tag
112
+ ) {
113
+ return;
114
+ }
115
+ this.add(change.ref, tag, false);
116
+ });
119
117
 
120
118
  return this;
121
119
  }
122
120
 
123
- protected addParent(changeTree: ChangeTree, tag: number) {
124
- const parentRef = changeTree.parent;
125
- if (!parentRef) { return; }
121
+ protected addParent(changeTree: ChangeTree, parentIndex: number, tag: number) {
122
+ // view must have all "changeTree" parent tree
123
+ this.items.add(changeTree);
126
124
 
127
- const parentChangeTree = parentRef[$changes];
128
- const parentIndex = changeTree.parentIndex;
125
+ // add parent's parent
126
+ const parentChangeTree: ChangeTree = changeTree.parent?.[$changes];
127
+ if (parentChangeTree && (parentChangeTree.filteredChanges !== undefined)) {
128
+ this.addParent(parentChangeTree, changeTree.parentIndex, tag);
129
+ }
129
130
 
130
- if (!this.invisible.has(parentChangeTree)) {
131
- // parent is already available, no need to add it!
131
+ // parent is already available, no need to add it!
132
+ if (!this.invisible.has(changeTree)) {
132
133
  return;
133
134
  }
134
135
 
135
- this.addParent(parentChangeTree, tag);
136
-
137
136
  // add parent's tag properties
138
- if (parentChangeTree.getChange(parentIndex) !== OPERATION.DELETE) {
137
+ if (changeTree.getChange(parentIndex) !== OPERATION.DELETE) {
139
138
 
140
- let parentChanges = this.changes.get(parentChangeTree);
141
- if (parentChanges === undefined) {
142
- parentChanges = new Map<number, OPERATION>();
143
- this.changes.set(parentChangeTree, parentChanges);
139
+ let changes = this.changes[changeTree.refId];
140
+ if (changes === undefined) {
141
+ changes = {};
142
+ this.changes[changeTree.refId] = changes;
144
143
  }
145
144
 
146
- // console.log("add parent change", {
147
- // parentIndex,
148
- // parentChanges,
149
- // parentChange: (
150
- // parentChangeTree.getChange(parentIndex) &&
151
- // OPERATION[parentChangeTree.getChange(parentIndex)]
152
- // ),
153
- // })
145
+ if (!this.tags) {
146
+ this.tags = new WeakMap<ChangeTree, Set<number>>();
147
+ }
154
148
 
155
- if (!this.tags) { this.tags = new WeakMap<ChangeTree, Set<number>>(); }
156
149
  let tags: Set<number>;
157
- if (!this.tags.has(parentChangeTree)) {
150
+ if (!this.tags.has(changeTree)) {
158
151
  tags = new Set<number>();
159
- this.tags.set(parentChangeTree, tags);
152
+ this.tags.set(changeTree, tags);
160
153
  } else {
161
- tags = this.tags.get(parentChangeTree);
154
+ tags = this.tags.get(changeTree);
162
155
  }
163
156
  tags.add(tag);
164
157
 
165
- parentChanges.set(parentIndex, OPERATION.ADD);
158
+ changes[parentIndex] = OPERATION.ADD;
166
159
  }
167
-
168
160
  }
169
161
 
170
162
  remove(obj: Ref, tag: number = DEFAULT_VIEW_TAG) {
@@ -179,10 +171,10 @@ export class StateView {
179
171
  const ref = changeTree.ref;
180
172
  const metadata: Metadata = ref.constructor[Symbol.metadata];
181
173
 
182
- let changes = this.changes.get(changeTree);
174
+ let changes = this.changes[changeTree.refId];
183
175
  if (changes === undefined) {
184
- changes = new Map<number, OPERATION>();
185
- this.changes.set(changeTree, changes)
176
+ changes = {};
177
+ this.changes[changeTree.refId] = changes;
186
178
  }
187
179
 
188
180
  if (tag === DEFAULT_VIEW_TAG) {
@@ -190,25 +182,25 @@ export class StateView {
190
182
  const parent = changeTree.parent;
191
183
  if (!Metadata.isValidInstance(parent)) {
192
184
  const parentChangeTree = parent[$changes];
193
- let changes = this.changes.get(parentChangeTree);
185
+ let changes = this.changes[parentChangeTree.refId];
194
186
  if (changes === undefined) {
195
- changes = new Map<number, OPERATION>();
196
- this.changes.set(parentChangeTree, changes)
187
+ changes = {};
188
+ this.changes[parentChangeTree.refId] = changes;
197
189
  }
198
190
  // DELETE / DELETE BY REF ID
199
- changes.set(changeTree.parentIndex, OPERATION.DELETE);
191
+ changes[changeTree.parentIndex] = OPERATION.DELETE;
200
192
 
201
193
  } else {
202
194
  // delete all "tagged" properties.
203
- metadata[-2].forEach((index) =>
204
- changes.set(index, OPERATION.DELETE));
195
+ metadata[$viewFieldIndexes].forEach((index) =>
196
+ changes[index] = OPERATION.DELETE);
205
197
  }
206
198
 
207
199
 
208
200
  } else {
209
201
  // delete only tagged properties
210
- metadata[-3][tag].forEach((index) =>
211
- changes.set(index, OPERATION.DELETE));
202
+ metadata[$fieldIndexesByViewTag][tag].forEach((index) =>
203
+ changes[index] = OPERATION.DELETE);
212
204
  }
213
205
 
214
206
  // remove tag
@@ -1,8 +1,9 @@
1
- import { Schema } from "../Schema";
2
- import { CollectionSchema } from "../types/custom/CollectionSchema";
3
- import { MapSchema } from "../types/custom/MapSchema";
4
- import { SetSchema } from "../types/custom/SetSchema";
5
- import { ArraySchema } from "../types/custom/ArraySchema";
1
+ import type { Schema } from "../Schema";
2
+ import type { CollectionSchema } from "../types/custom/CollectionSchema";
3
+ import type { MapSchema } from "../types/custom/MapSchema";
4
+ import type { SetSchema } from "../types/custom/SetSchema";
5
+ import type { ArraySchema } from "../types/custom/ArraySchema";
6
+ import type { Ref } from "../encoder/ChangeTree";
6
7
 
7
8
  export class EncodeSchemaError extends Error {}
8
9
 
@@ -27,6 +28,10 @@ export function assertType(value: any, type: string, klass: Schema, field: strin
27
28
  console.log(`trying to encode "NaN" in ${klass.constructor.name}#${field}`);
28
29
  }
29
30
  break;
31
+ case "bigint64":
32
+ case "biguint64":
33
+ typeofTarget = "bigint";
34
+ break;
30
35
  case "string":
31
36
  typeofTarget = "string";
32
37
  allowNull = true;
@@ -34,6 +39,10 @@ export function assertType(value: any, type: string, klass: Schema, field: strin
34
39
  case "boolean":
35
40
  // boolean is always encoded as true/false based on truthiness
36
41
  return;
42
+ default:
43
+ // skip assertion for custom types
44
+ // TODO: allow custom types to define their own assertions
45
+ return;
37
46
  }
38
47
 
39
48
  if (typeof (value) !== typeofTarget && (!allowNull || (allowNull && value !== null))) {
@@ -43,16 +52,16 @@ export function assertType(value: any, type: string, klass: Schema, field: strin
43
52
  }
44
53
 
45
54
  export function assertInstanceType(
46
- value: Schema,
55
+ value: Ref,
47
56
  type: typeof Schema
48
57
  | typeof ArraySchema
49
58
  | typeof MapSchema
50
59
  | typeof CollectionSchema
51
60
  | typeof SetSchema,
52
- klass: Schema,
61
+ instance: Ref,
53
62
  field: string | number,
54
63
  ) {
55
64
  if (!(value instanceof type)) {
56
- throw new EncodeSchemaError(`a '${type.name}' was expected, but '${value && (value as any).constructor.name}' was provided in ${klass.constructor.name}#${field}`);
65
+ throw new EncodeSchemaError(`a '${type.name}' was expected, but '${value && (value as any).constructor.name}' was provided in ${instance.constructor.name}#${field}`);
57
66
  }
58
67
  }
@@ -21,7 +21,6 @@
21
21
  * SOFTWARE
22
22
  */
23
23
 
24
- import { SWITCH_TO_STRUCTURE } from "./spec";
25
24
  import type { BufferLike } from "./encode";
26
25
 
27
26
  /**
@@ -31,7 +30,17 @@ import type { BufferLike } from "./encode";
31
30
 
32
31
  export interface Iterator { offset: number; }
33
32
 
34
- export function utf8Read(bytes: BufferLike, it: Iterator, length: number) {
33
+ // force little endian to facilitate decoding on multiple implementations
34
+ const _isLittleEndian = true; // new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1;
35
+ const _convoBuffer = new ArrayBuffer(8);
36
+
37
+ const _int32 = new Int32Array(_convoBuffer);
38
+ const _float32 = new Float32Array(_convoBuffer);
39
+ const _float64 = new Float64Array(_convoBuffer);
40
+ const _uint64 = new BigUint64Array(_convoBuffer);
41
+ const _int64 = new BigInt64Array(_convoBuffer);
42
+
43
+ function utf8Read(bytes: BufferLike, it: Iterator, length: number) {
35
44
  var string = '', chr = 0;
36
45
  for (var i = it.offset, end = it.offset + length; i < end; i++) {
37
46
  var byte = bytes[i];
@@ -76,72 +85,70 @@ export function utf8Read(bytes: BufferLike, it: Iterator, length: number) {
76
85
  return string;
77
86
  }
78
87
 
79
- export function int8 (bytes: BufferLike, it: Iterator) {
88
+ function int8 (bytes: BufferLike, it: Iterator) {
80
89
  return uint8(bytes, it) << 24 >> 24;
81
90
  };
82
91
 
83
- export function uint8 (bytes: BufferLike, it: Iterator) {
92
+ function uint8 (bytes: BufferLike, it: Iterator) {
84
93
  return bytes[it.offset++];
85
94
  };
86
95
 
87
- export function int16 (bytes: BufferLike, it: Iterator) {
96
+ function int16 (bytes: BufferLike, it: Iterator) {
88
97
  return uint16(bytes, it) << 16 >> 16;
89
98
  };
90
99
 
91
- export function uint16 (bytes: BufferLike, it: Iterator) {
100
+ function uint16 (bytes: BufferLike, it: Iterator) {
92
101
  return bytes[it.offset++] | bytes[it.offset++] << 8;
93
102
  };
94
103
 
95
- export function int32 (bytes: BufferLike, it: Iterator) {
104
+ function int32 (bytes: BufferLike, it: Iterator) {
96
105
  return bytes[it.offset++] | bytes[it.offset++] << 8 | bytes[it.offset++] << 16 | bytes[it.offset++] << 24;
97
106
  };
98
107
 
99
- export function uint32 (bytes: BufferLike, it: Iterator) {
108
+ function uint32 (bytes: BufferLike, it: Iterator) {
100
109
  return int32(bytes, it) >>> 0;
101
110
  };
102
111
 
103
- export function float32(bytes: BufferLike, it: Iterator) {
104
- return readFloat32(bytes, it);
105
- }
112
+ function float32 (bytes: BufferLike, it: Iterator) {
113
+ _int32[0] = int32(bytes, it);
114
+ return _float32[0];
115
+ };
106
116
 
107
- export function float64(bytes: BufferLike, it: Iterator) {
108
- return readFloat64(bytes, it);
109
- }
117
+ function float64 (bytes: BufferLike, it: Iterator) {
118
+ _int32[_isLittleEndian ? 0 : 1] = int32(bytes, it);
119
+ _int32[_isLittleEndian ? 1 : 0] = int32(bytes, it);
120
+ return _float64[0];
121
+ };
110
122
 
111
- export function int64(bytes: BufferLike, it: Iterator) {
123
+ function int64(bytes: BufferLike, it: Iterator) {
112
124
  const low = uint32(bytes, it);
113
125
  const high = int32(bytes, it) * Math.pow(2, 32);
114
126
  return high + low;
115
127
  };
116
128
 
117
- export function uint64(bytes: BufferLike, it: Iterator) {
118
- const low = uint32(bytes, it);
119
- const high = uint32(bytes, it) * Math.pow(2, 32);
120
- return high + low;
129
+ function uint64(bytes: BufferLike, it: Iterator) {
130
+ const low = uint32(bytes, it);
131
+ const high = uint32(bytes, it) * Math.pow(2, 32);
132
+ return high + low;
121
133
  };
122
134
 
123
- // force little endian to facilitate decoding on multiple implementations
124
- const _isLittleEndian = true; // new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1;
125
- const _int32 = new Int32Array(2);
126
- const _float32 = new Float32Array(_int32.buffer);
127
- const _float64 = new Float64Array(_int32.buffer);
128
-
129
- export function readFloat32 (bytes: BufferLike, it: Iterator) {
135
+ function bigint64(bytes: BufferLike, it: Iterator) {
130
136
  _int32[0] = int32(bytes, it);
131
- return _float32[0];
132
- };
137
+ _int32[1] = int32(bytes, it);
138
+ return _int64[0];
139
+ }
133
140
 
134
- export function readFloat64 (bytes: BufferLike, it: Iterator) {
135
- _int32[_isLittleEndian ? 0 : 1] = int32(bytes, it);
136
- _int32[_isLittleEndian ? 1 : 0] = int32(bytes, it);
137
- return _float64[0];
138
- };
141
+ function biguint64(bytes: BufferLike, it: Iterator) {
142
+ _int32[0] = int32(bytes, it);
143
+ _int32[1] = int32(bytes, it);
144
+ return _uint64[0];
145
+ }
139
146
 
140
- export function boolean (bytes: BufferLike, it: Iterator) {
147
+ function boolean (bytes: BufferLike, it: Iterator) {
141
148
  return uint8(bytes, it) > 0;
142
149
  };
143
150
 
144
- export function string (bytes: BufferLike, it: Iterator) {
151
+ function string (bytes: BufferLike, it: Iterator) {
145
152
  const prefix = bytes[it.offset++];
146
153
  let length: number;
147
154
 
@@ -162,21 +169,7 @@ export function string (bytes: BufferLike, it: Iterator) {
162
169
  return utf8Read(bytes, it, length);
163
170
  }
164
171
 
165
- export function stringCheck(bytes: BufferLike, it: Iterator) {
166
- const prefix = bytes[it.offset];
167
- return (
168
- // fixstr
169
- (prefix < 0xc0 && prefix > 0xa0) ||
170
- // str 8
171
- prefix === 0xd9 ||
172
- // str 16
173
- prefix === 0xda ||
174
- // str 32
175
- prefix === 0xdb
176
- );
177
- }
178
-
179
- export function number (bytes: BufferLike, it: Iterator) {
172
+ function number (bytes: BufferLike, it: Iterator) {
180
173
  const prefix = bytes[it.offset++];
181
174
 
182
175
  if (prefix < 0x80) {
@@ -185,11 +178,11 @@ export function number (bytes: BufferLike, it: Iterator) {
185
178
 
186
179
  } else if (prefix === 0xca) {
187
180
  // float 32
188
- return readFloat32(bytes, it);
181
+ return float32(bytes, it);
189
182
 
190
183
  } else if (prefix === 0xcb) {
191
184
  // float 64
192
- return readFloat64(bytes, it);
185
+ return float64(bytes, it);
193
186
 
194
187
  } else if (prefix === 0xcc) {
195
188
  // uint 8
@@ -229,49 +222,36 @@ export function number (bytes: BufferLike, it: Iterator) {
229
222
  }
230
223
  };
231
224
 
232
- export function numberCheck (bytes: BufferLike, it: Iterator) {
225
+ export function stringCheck(bytes: BufferLike, it: Iterator) {
233
226
  const prefix = bytes[it.offset];
234
- // positive fixint - 0x00 - 0x7f
235
- // float 32 - 0xca
236
- // float 64 - 0xcb
237
- // uint 8 - 0xcc
238
- // uint 16 - 0xcd
239
- // uint 32 - 0xce
240
- // uint 64 - 0xcf
241
- // int 8 - 0xd0
242
- // int 16 - 0xd1
243
- // int 32 - 0xd2
244
- // int 64 - 0xd3
245
227
  return (
246
- prefix < 0x80 ||
247
- (prefix >= 0xca && prefix <= 0xd3)
228
+ // fixstr
229
+ (prefix < 0xc0 && prefix > 0xa0) ||
230
+ // str 8
231
+ prefix === 0xd9 ||
232
+ // str 16
233
+ prefix === 0xda ||
234
+ // str 32
235
+ prefix === 0xdb
248
236
  );
249
237
  }
250
238
 
251
- export function arrayCheck (bytes: BufferLike, it: Iterator) {
252
- return bytes[it.offset] < 0xa0;
253
-
254
- // const prefix = bytes[it.offset] ;
255
-
256
- // if (prefix < 0xa0) {
257
- // return prefix;
258
-
259
- // // array
260
- // } else if (prefix === 0xdc) {
261
- // it.offset += 2;
262
-
263
- // } else if (0xdd) {
264
- // it.offset += 4;
265
- // }
266
-
267
- // return prefix;
268
- }
269
-
270
- export function switchStructureCheck(bytes: BufferLike, it: Iterator) {
271
- return (
272
- // previous byte should be `SWITCH_TO_STRUCTURE`
273
- bytes[it.offset - 1] === SWITCH_TO_STRUCTURE &&
274
- // next byte should be a number
275
- (bytes[it.offset] < 0x80 || (bytes[it.offset] >= 0xca && bytes[it.offset] <= 0xd3))
276
- );
277
- }
239
+ export const decode = {
240
+ utf8Read,
241
+ int8,
242
+ uint8,
243
+ int16,
244
+ uint16,
245
+ int32,
246
+ uint32,
247
+ float32,
248
+ float64,
249
+ int64,
250
+ uint64,
251
+ bigint64,
252
+ biguint64,
253
+ boolean,
254
+ string,
255
+ number,
256
+ stringCheck,
257
+ };