sportident.js 1.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 (151) hide show
  1. package/.dependency-cruiser.js +233 -0
  2. package/.editorconfig +12 -0
  3. package/.eslintignore +6 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +35 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
  6. package/.github/workflows/npm.yml +17 -0
  7. package/LICENSE +21 -0
  8. package/README.md +31 -0
  9. package/babel.config.js +21 -0
  10. package/build-docs.sh +25 -0
  11. package/docs/index.md +9 -0
  12. package/docs/typedoc/index.md +11 -0
  13. package/eslint.base.js +232 -0
  14. package/install.sh +6 -0
  15. package/jest.config.ts +49 -0
  16. package/nx.json +39 -0
  17. package/package.json +51 -0
  18. package/src/SiCard/BaseSiCard.test.ts +187 -0
  19. package/src/SiCard/BaseSiCard.ts +101 -0
  20. package/src/SiCard/IRaceResultData.ts +16 -0
  21. package/src/SiCard/ISiCard.ts +23 -0
  22. package/src/SiCard/ISiCardExamples.ts +4 -0
  23. package/src/SiCard/index.ts +2 -0
  24. package/src/SiCard/raceResultTools.test.ts +260 -0
  25. package/src/SiCard/raceResultTools.ts +150 -0
  26. package/src/SiCard/types/FCard.test.ts +19 -0
  27. package/src/SiCard/types/FCard.ts +14 -0
  28. package/src/SiCard/types/ModernSiCard.test.ts +186 -0
  29. package/src/SiCard/types/ModernSiCard.ts +241 -0
  30. package/src/SiCard/types/PCard.test.ts +19 -0
  31. package/src/SiCard/types/PCard.ts +14 -0
  32. package/src/SiCard/types/SIAC.test.ts +84 -0
  33. package/src/SiCard/types/SIAC.ts +19 -0
  34. package/src/SiCard/types/SiCard10.test.ts +85 -0
  35. package/src/SiCard/types/SiCard10.ts +17 -0
  36. package/src/SiCard/types/SiCard11.test.ts +84 -0
  37. package/src/SiCard/types/SiCard11.ts +19 -0
  38. package/src/SiCard/types/SiCard5.test.ts +149 -0
  39. package/src/SiCard/types/SiCard5.ts +129 -0
  40. package/src/SiCard/types/SiCard6.test.ts +179 -0
  41. package/src/SiCard/types/SiCard6.ts +222 -0
  42. package/src/SiCard/types/SiCard8.test.ts +137 -0
  43. package/src/SiCard/types/SiCard8.ts +129 -0
  44. package/src/SiCard/types/SiCard9.test.ts +132 -0
  45. package/src/SiCard/types/SiCard9.ts +128 -0
  46. package/src/SiCard/types/TCard.test.ts +19 -0
  47. package/src/SiCard/types/TCard.ts +14 -0
  48. package/src/SiCard/types/index.test.ts +26 -0
  49. package/src/SiCard/types/index.ts +15 -0
  50. package/src/SiCard/types/modernSiCardExamples.ts +364 -0
  51. package/src/SiCard/types/siCard5Examples.ts +73 -0
  52. package/src/SiCard/types/siCard6Examples.ts +262 -0
  53. package/src/SiCard/types/siCard8Examples.ts +152 -0
  54. package/src/SiCard/types/siCard9Examples.ts +143 -0
  55. package/src/SiDevice/INavigatorWebSerial.ts +78 -0
  56. package/src/SiDevice/INavigatorWebUsb.ts +62 -0
  57. package/src/SiDevice/ISiDevice.ts +48 -0
  58. package/src/SiDevice/ISiDeviceDriver.ts +35 -0
  59. package/src/SiDevice/README.md +13 -0
  60. package/src/SiDevice/SiDevice.test.ts +354 -0
  61. package/src/SiDevice/SiDevice.ts +132 -0
  62. package/src/SiDevice/WebSerialSiDeviceDriver.ts +146 -0
  63. package/src/SiDevice/WebUsbSiDeviceDriver.ts +343 -0
  64. package/src/SiDevice/index.ts +3 -0
  65. package/src/SiDevice/testUtils/index.ts +2 -0
  66. package/src/SiDevice/testUtils/testISiDeviceDriver.ts +63 -0
  67. package/src/SiDevice/testUtils/testISiDeviceDriverWithAutodetection.ts +72 -0
  68. package/src/SiStation/BaseSiStation.test.ts +221 -0
  69. package/src/SiStation/BaseSiStation.ts +253 -0
  70. package/src/SiStation/CoupledSiStation.test.ts +41 -0
  71. package/src/SiStation/CoupledSiStation.ts +130 -0
  72. package/src/SiStation/ISiMainStation.ts +29 -0
  73. package/src/SiStation/ISiSendTask.ts +9 -0
  74. package/src/SiStation/ISiStation.ts +88 -0
  75. package/src/SiStation/ISiTargetMultiplexer.ts +51 -0
  76. package/src/SiStation/SiMainStation.test.ts +222 -0
  77. package/src/SiStation/SiMainStation.ts +133 -0
  78. package/src/SiStation/SiSendTask.ts +50 -0
  79. package/src/SiStation/SiTargetMultiplexer.targeting.test.ts +112 -0
  80. package/src/SiStation/SiTargetMultiplexer.test.ts +605 -0
  81. package/src/SiStation/SiTargetMultiplexer.ts +241 -0
  82. package/src/SiStation/index.ts +5 -0
  83. package/src/SiStation/siStationExamples.ts +103 -0
  84. package/src/constants.test.ts +17 -0
  85. package/src/constants.ts +92 -0
  86. package/src/fakes/FakeSiCard/BaseFakeSiCard.test.ts +11 -0
  87. package/src/fakes/FakeSiCard/BaseFakeSiCard.ts +10 -0
  88. package/src/fakes/FakeSiCard/IFakeSiCard.ts +6 -0
  89. package/src/fakes/FakeSiCard/index.ts +2 -0
  90. package/src/fakes/FakeSiCard/types/FakeModernSiCard.test.ts +62 -0
  91. package/src/fakes/FakeSiCard/types/FakeModernSiCard.ts +43 -0
  92. package/src/fakes/FakeSiCard/types/FakeSIAC.ts +17 -0
  93. package/src/fakes/FakeSiCard/types/FakeSiCard10.ts +17 -0
  94. package/src/fakes/FakeSiCard/types/FakeSiCard11.ts +17 -0
  95. package/src/fakes/FakeSiCard/types/FakeSiCard5.test.ts +42 -0
  96. package/src/fakes/FakeSiCard/types/FakeSiCard5.ts +40 -0
  97. package/src/fakes/FakeSiCard/types/FakeSiCard6.test.ts +62 -0
  98. package/src/fakes/FakeSiCard/types/FakeSiCard6.ts +44 -0
  99. package/src/fakes/FakeSiCard/types/FakeSiCard8.ts +16 -0
  100. package/src/fakes/FakeSiCard/types/FakeSiCard9.ts +16 -0
  101. package/src/fakes/FakeSiCard/types/index.ts +7 -0
  102. package/src/fakes/FakeSiDeviceDriver.ts +148 -0
  103. package/src/fakes/FakeSiMainStation.test.ts +141 -0
  104. package/src/fakes/FakeSiMainStation.ts +118 -0
  105. package/src/fakes/IFakeSiMainStation.ts +15 -0
  106. package/src/fakes/index.ts +2 -0
  107. package/src/index.ts +24 -0
  108. package/src/siProtocol.test.ts +509 -0
  109. package/src/siProtocol.ts +417 -0
  110. package/src/storage/SiArray.test.ts +103 -0
  111. package/src/storage/SiArray.ts +56 -0
  112. package/src/storage/SiBool.test.ts +81 -0
  113. package/src/storage/SiBool.ts +47 -0
  114. package/src/storage/SiDataType.test.ts +81 -0
  115. package/src/storage/SiDataType.ts +60 -0
  116. package/src/storage/SiDict.test.ts +115 -0
  117. package/src/storage/SiDict.ts +60 -0
  118. package/src/storage/SiEnum.test.ts +77 -0
  119. package/src/storage/SiEnum.ts +48 -0
  120. package/src/storage/SiFieldValue.test.ts +58 -0
  121. package/src/storage/SiFieldValue.ts +23 -0
  122. package/src/storage/SiInt.test.ts +80 -0
  123. package/src/storage/SiInt.ts +84 -0
  124. package/src/storage/SiModified.test.ts +135 -0
  125. package/src/storage/SiModified.ts +59 -0
  126. package/src/storage/SiStorage.test.ts +51 -0
  127. package/src/storage/SiStorage.ts +44 -0
  128. package/src/storage/index.test.ts +222 -0
  129. package/src/storage/index.ts +12 -0
  130. package/src/storage/interfaces.ts +41 -0
  131. package/src/storage/siStringEncoding.ts +1361 -0
  132. package/src/testUtils.test.ts +266 -0
  133. package/src/testUtils.ts +75 -0
  134. package/src/utils/NumberRange.test.ts +66 -0
  135. package/src/utils/NumberRange.ts +46 -0
  136. package/src/utils/NumberRangeRegistry.test.ts +49 -0
  137. package/src/utils/NumberRangeRegistry.ts +43 -0
  138. package/src/utils/bytes.test.ts +126 -0
  139. package/src/utils/bytes.ts +69 -0
  140. package/src/utils/errors.test.ts +29 -0
  141. package/src/utils/errors.ts +20 -0
  142. package/src/utils/events.test.ts +112 -0
  143. package/src/utils/events.ts +68 -0
  144. package/src/utils/general.test.ts +139 -0
  145. package/src/utils/general.ts +69 -0
  146. package/src/utils/index.ts +8 -0
  147. package/src/utils/mixins.test.ts +40 -0
  148. package/src/utils/mixins.ts +13 -0
  149. package/src/utils/typed.ts +3 -0
  150. package/tsconfig.base.json +22 -0
  151. package/tsconfig.json +15 -0
@@ -0,0 +1,266 @@
1
+ import { describe, expect, test } from '@jest/globals';
2
+ import _ from 'lodash';
3
+ import * as testUtils from './testUtils';
4
+
5
+ testUtils.useFakeTimers();
6
+
7
+ describe('testUtils', () => {
8
+ test('runPromises resolve with fake timers', async () => {
9
+ const timeState = {
10
+ promiseResolved: false
11
+ };
12
+ Promise.resolve().then(() => {
13
+ timeState.promiseResolved = true;
14
+ });
15
+ expect(timeState).toEqual({ promiseResolved: false });
16
+ await testUtils.runPromises();
17
+ expect(timeState).toEqual({ promiseResolved: true });
18
+ });
19
+ test('runPromises reject with fake timers', async () => {
20
+ const timeState = { promiseRejected: false };
21
+ Promise.reject(new Error('test')).catch(() => {
22
+ timeState.promiseRejected = true;
23
+ });
24
+ expect(timeState).toEqual({ promiseRejected: false });
25
+ await testUtils.runPromises();
26
+ expect(timeState).toEqual({ promiseRejected: true });
27
+ });
28
+ test('advanceTimersByTime', async () => {
29
+ const timeState = {
30
+ timeout0: false,
31
+ promise0: false,
32
+ timeout1: false,
33
+ promise1: false,
34
+ timeout2: false,
35
+ promise2: false
36
+ };
37
+ setTimeout(() => {
38
+ timeState.timeout0 = true;
39
+ }, 0);
40
+ setTimeout(() => {
41
+ timeState.timeout1 = true;
42
+ }, 1);
43
+ setTimeout(() => {
44
+ timeState.timeout2 = true;
45
+ }, 2);
46
+ const promise0 = new Promise((resolve) => setTimeout(resolve, 0));
47
+ const promise1 = new Promise((resolve) => setTimeout(resolve, 1));
48
+ const promise2 = new Promise((resolve) => setTimeout(resolve, 2));
49
+ promise0.then(() => {
50
+ timeState.promise0 = true;
51
+ });
52
+ promise1.then(() => {
53
+ timeState.promise1 = true;
54
+ });
55
+ promise2.then(() => {
56
+ timeState.promise2 = true;
57
+ });
58
+ expect(timeState).toEqual({
59
+ timeout0: false,
60
+ promise0: false,
61
+ timeout1: false,
62
+ promise1: false,
63
+ timeout2: false,
64
+ promise2: false
65
+ });
66
+ await testUtils.advanceTimersByTime(0);
67
+ expect(timeState).toEqual({
68
+ timeout0: true,
69
+ promise0: true,
70
+ timeout1: false,
71
+ promise1: false,
72
+ timeout2: false,
73
+ promise2: false
74
+ });
75
+ await testUtils.advanceTimersByTime(1);
76
+ expect(timeState).toEqual({
77
+ timeout0: true,
78
+ promise0: true,
79
+ timeout1: true,
80
+ promise1: true,
81
+ timeout2: false,
82
+ promise2: false
83
+ });
84
+ await testUtils.advanceTimersByTime(1);
85
+ expect(timeState).toEqual({
86
+ timeout0: true,
87
+ promise0: true,
88
+ timeout1: true,
89
+ promise1: true,
90
+ timeout2: true,
91
+ promise2: true
92
+ });
93
+ });
94
+ test('nTimesAsync', async () => {
95
+ const timeState = {
96
+ timeout0: false,
97
+ promise0: false,
98
+ timeout1: false,
99
+ promise1: false,
100
+ timeout2: false,
101
+ promise2: false
102
+ };
103
+ setTimeout(() => {
104
+ timeState.timeout0 = true;
105
+ }, 0);
106
+ setTimeout(() => {
107
+ timeState.timeout1 = true;
108
+ }, 1);
109
+ setTimeout(() => {
110
+ timeState.timeout2 = true;
111
+ }, 2);
112
+ const promise0 = new Promise((resolve) => setTimeout(resolve, 0));
113
+ const promise1 = new Promise((resolve) => setTimeout(resolve, 1));
114
+ const promise2 = new Promise((resolve) => setTimeout(resolve, 2));
115
+ promise0.then(() => {
116
+ timeState.promise0 = true;
117
+ });
118
+ promise1.then(() => {
119
+ timeState.promise1 = true;
120
+ });
121
+ promise2.then(() => {
122
+ timeState.promise2 = true;
123
+ });
124
+ expect(timeState).toEqual({
125
+ timeout0: false,
126
+ promise0: false,
127
+ timeout1: false,
128
+ promise1: false,
129
+ timeout2: false,
130
+ promise2: false
131
+ });
132
+ await testUtils.nTimesAsync(0, () => testUtils.advanceTimersByTime(0));
133
+ expect(timeState).toEqual({
134
+ timeout0: false,
135
+ promise0: false,
136
+ timeout1: false,
137
+ promise1: false,
138
+ timeout2: false,
139
+ promise2: false
140
+ });
141
+ await testUtils.nTimesAsync(1, () => testUtils.advanceTimersByTime(0));
142
+ expect(timeState).toEqual({
143
+ timeout0: true,
144
+ promise0: true,
145
+ timeout1: false,
146
+ promise1: false,
147
+ timeout2: false,
148
+ promise2: false
149
+ });
150
+ await testUtils.nTimesAsync(2, () => testUtils.advanceTimersByTime(1));
151
+ expect(timeState).toEqual({
152
+ timeout0: true,
153
+ promise0: true,
154
+ timeout1: true,
155
+ promise1: true,
156
+ timeout2: true,
157
+ promise2: true
158
+ });
159
+ });
160
+ test('getRandomInt with 0 options', () => {
161
+ _.range(10).forEach(() => {
162
+ expect(testUtils.getRandomInt(0)).toBe(0);
163
+ });
164
+ });
165
+ test('getRandomInt with 1 options', () => {
166
+ _.range(10).forEach(() => {
167
+ expect(testUtils.getRandomInt(1)).toBe(0);
168
+ });
169
+ });
170
+ test('getRandomInt with 2 options', () => {
171
+ _.range(10).forEach(() => {
172
+ const randomInt = testUtils.getRandomInt(2);
173
+ expect(randomInt).not.toBeLessThan(0);
174
+ expect(randomInt).not.toBeGreaterThan(1);
175
+ });
176
+ });
177
+ test('getRandomByte', () => {
178
+ _.range(10).forEach(() => {
179
+ const randomByte = testUtils.getRandomByte();
180
+ expect(randomByte).not.toBeLessThan(0x00);
181
+ expect(randomByte).not.toBeGreaterThan(0xff);
182
+ expect(Math.floor(randomByte)).toBe(randomByte);
183
+ });
184
+ });
185
+ test('getRandomByteExcept one', () => {
186
+ _.range(10).forEach(() => {
187
+ const randomByte = testUtils.getRandomByteExcept([0x42]);
188
+ expect(randomByte).not.toBe(0x42);
189
+ expect(randomByte).not.toBeLessThan(0x00);
190
+ expect(randomByte).not.toBeGreaterThan(0xff);
191
+ expect(Math.floor(randomByte)).toBe(randomByte);
192
+ });
193
+ });
194
+ test('getRandomByteExcept all but one', () => {
195
+ _.range(10).forEach(() => {
196
+ const randomByte = testUtils.getRandomByteExcept(_.range(255));
197
+ expect(randomByte).toBe(0xff);
198
+ });
199
+ });
200
+ test('getRandomMessage', () => {
201
+ _.range(10).forEach(() => {
202
+ const randomMessage = testUtils.getRandomMessage({});
203
+ expect(randomMessage.command).not.toBeLessThan(0x00);
204
+ expect(randomMessage.command).not.toBeGreaterThan(0xff);
205
+ expect(randomMessage.parameters).toEqual([]);
206
+ });
207
+ });
208
+ test('getRandomMessage with numParameters', () => {
209
+ _.range(10).forEach(() => {
210
+ const numParameters = Math.floor(Math.random() * 3 + 1);
211
+ const randomMessage = testUtils.getRandomMessage({ numParameters: numParameters });
212
+ expect(randomMessage.command).not.toBeLessThan(0x00);
213
+ expect(randomMessage.command).not.toBeGreaterThan(0xff);
214
+ expect(randomMessage.parameters.length).toBe(numParameters);
215
+ expect(randomMessage.parameters[0]).not.toBeLessThan(0x00);
216
+ expect(randomMessage.parameters[0]).not.toBeGreaterThan(0xff);
217
+ });
218
+ });
219
+ test('getRandomMessage with command', () => {
220
+ _.range(10).forEach(() => {
221
+ const command = testUtils.getRandomByte();
222
+ const randomMessage = testUtils.getRandomMessage({ command: command });
223
+ expect(randomMessage.command).toBe(command);
224
+ expect(randomMessage.parameters).toEqual([]);
225
+ });
226
+ });
227
+ test('getRandomMessage with parameters', () => {
228
+ _.range(10).forEach(() => {
229
+ const numParameters = Math.floor(Math.random() * 3 + 1);
230
+ const parameters = _.range(numParameters).map(() => testUtils.getRandomByte());
231
+ const randomMessage = testUtils.getRandomMessage({ parameters: parameters });
232
+ expect(randomMessage.command).not.toBeLessThan(0x00);
233
+ expect(randomMessage.command).not.toBeGreaterThan(0xff);
234
+ expect(randomMessage.parameters).toEqual(parameters);
235
+ });
236
+ });
237
+ test('runMock', () => {
238
+ const mockObject: testUtils.Mockable<string> = {
239
+ mocks: {
240
+ existing: (index) => `existing${index}`
241
+ },
242
+ counts: {}
243
+ };
244
+ const getDefaultResult = (index: number) => `default${index}`;
245
+
246
+ const resultExisting1 = testUtils.runMock(mockObject, 'existing', getDefaultResult);
247
+ expect(resultExisting1).toBe('existing0');
248
+ expect(mockObject.counts).toEqual({ existing: 1 });
249
+
250
+ const resultNonExisting1 = testUtils.runMock(mockObject, 'nonExisting', getDefaultResult);
251
+ expect(resultNonExisting1).toBe('default0');
252
+ expect(mockObject.counts).toEqual({ existing: 1, nonExisting: 1 });
253
+
254
+ const resultExisting2 = testUtils.runMock(mockObject, 'existing', getDefaultResult);
255
+ expect(resultExisting2).toBe('existing1');
256
+ expect(mockObject.counts).toEqual({ existing: 2, nonExisting: 1 });
257
+
258
+ const resultNonExisting2 = testUtils.runMock(mockObject, 'nonExisting', getDefaultResult);
259
+ expect(resultNonExisting2).toBe('default1');
260
+ expect(mockObject.counts).toEqual({ existing: 2, nonExisting: 2 });
261
+
262
+ const resultNonExisting3 = testUtils.runMock(mockObject, 'yetAnother', getDefaultResult);
263
+ expect(resultNonExisting3).toBe('default0');
264
+ expect(mockObject.counts).toEqual({ existing: 2, nonExisting: 2, yetAnother: 1 });
265
+ });
266
+ });
@@ -0,0 +1,75 @@
1
+ /* globals jest */
2
+
3
+ import _ from 'lodash';
4
+ import type * as siProtocol from './siProtocol';
5
+ import { jest } from '@jest/globals';
6
+
7
+ export const useFakeTimers = (): void => {
8
+ jest.useFakeTimers();
9
+ };
10
+
11
+ export const runPromises = async (): Promise<void> => {
12
+ await Promise.resolve();
13
+ };
14
+
15
+ export const advanceTimersByTime = async (msToRun: number): Promise<void> => {
16
+ jest.advanceTimersByTime(msToRun);
17
+ await Promise.resolve();
18
+ };
19
+
20
+ export const nTimesAsync = async (n: number, doThing: () => Promise<unknown>): Promise<void> => {
21
+ if (n <= 0) {
22
+ return;
23
+ }
24
+ await doThing();
25
+ await nTimesAsync(n - 1, doThing);
26
+ };
27
+
28
+ export const getRandomInt = (numOptions: number): number => Math.floor(Math.random() * numOptions);
29
+
30
+ export const getRandomByte = (): number => getRandomInt(256);
31
+
32
+ export const getRandomByteExcept = (except: number[]): number => {
33
+ except.sort((a, b) => Number(a) - Number(b));
34
+ const numOptions = 256 - except.length;
35
+ let randomValue = getRandomInt(numOptions);
36
+ except.forEach((exceptedByte) => {
37
+ if (randomValue >= exceptedByte) {
38
+ randomValue += 1;
39
+ }
40
+ });
41
+ return randomValue;
42
+ };
43
+
44
+ type GetRandomMessageOptions = {
45
+ command?: number;
46
+ parameters?: number[];
47
+ numParameters?: number;
48
+ };
49
+
50
+ export const getRandomMessage = (options: GetRandomMessageOptions): siProtocol.SiMessageWithoutMode => {
51
+ let command = getRandomByte();
52
+ if (options.command !== undefined) {
53
+ command = options.command;
54
+ }
55
+ let parameters: number[] = [];
56
+ if (options.parameters !== undefined) {
57
+ parameters = options.parameters;
58
+ } else if (options.numParameters !== undefined) {
59
+ parameters = _.range(options.numParameters).map(() => getRandomByte());
60
+ }
61
+ return { command: command, parameters: parameters };
62
+ };
63
+
64
+ export interface Mockable<T> {
65
+ counts: { [key: string]: number };
66
+ mocks: { [key: string]: (count: number) => T };
67
+ }
68
+
69
+ export const runMock = <T>(that: Mockable<T>, key: string, getDefaultResult: (count: number) => T): T => {
70
+ const count = that.counts[key] || 0;
71
+ const mockFunction = that.mocks[key] || getDefaultResult;
72
+ const result = mockFunction(count);
73
+ that.counts[key] = count + 1;
74
+ return result;
75
+ };
@@ -0,0 +1,66 @@
1
+ import { describe, expect, test } from '@jest/globals';
2
+ import { NumberRange } from './NumberRange';
3
+
4
+ describe('NumberRange utils', () => {
5
+ test('instance', () => {
6
+ const numberRange = new NumberRange(0, 100);
7
+ expect(numberRange.start).toBe(0);
8
+ expect(numberRange.end).toBe(100);
9
+ });
10
+ test('null range', () => {
11
+ expect(() => new NumberRange(0, 0)).toThrow();
12
+ expect(() => new NumberRange(100, 100)).toThrow();
13
+ });
14
+ test('reverse range', () => {
15
+ expect(() => new NumberRange(100, 0)).toThrow();
16
+ });
17
+ const numberRange = new NumberRange(-3, 3);
18
+ test('toString', () => {
19
+ expect(numberRange.toString()).toBe('NumberRange(-3, 3)');
20
+ });
21
+ test('contains', () => {
22
+ expect(numberRange.contains(-4)).toBe(false);
23
+ expect(numberRange.contains(-3)).toBe(true);
24
+ expect(numberRange.contains(2)).toBe(true);
25
+ expect(numberRange.contains(3)).toBe(false);
26
+ });
27
+ test('isEntirelyAfter number', () => {
28
+ expect(numberRange.isEntirelyAfter(-4)).toBe(true);
29
+ expect(numberRange.isEntirelyAfter(-3)).toBe(false);
30
+ expect(numberRange.isEntirelyAfter(1)).toBe(false);
31
+ expect(numberRange.isEntirelyAfter(3)).toBe(false);
32
+ });
33
+ test('isEntirelyBefore number', () => {
34
+ expect(numberRange.isEntirelyBefore(-4)).toBe(false);
35
+ expect(numberRange.isEntirelyBefore(-3)).toBe(false);
36
+ expect(numberRange.isEntirelyBefore(1)).toBe(false);
37
+ expect(numberRange.isEntirelyBefore(3)).toBe(true);
38
+ });
39
+ const numberRange1 = new NumberRange(-4, -3);
40
+ const numberRange2 = new NumberRange(-3, 3);
41
+ const numberRange3 = new NumberRange(2, 4);
42
+ test('intersects', () => {
43
+ expect(numberRange1.intersects(numberRange2)).toBe(false);
44
+ expect(numberRange2.intersects(numberRange1)).toBe(false);
45
+ expect(numberRange2.intersects(numberRange3)).toBe(true);
46
+ expect(numberRange3.intersects(numberRange2)).toBe(true);
47
+ expect(numberRange1.intersects(numberRange3)).toBe(false);
48
+ expect(numberRange3.intersects(numberRange1)).toBe(false);
49
+ });
50
+ test('isEntirelyAfter range', () => {
51
+ expect(numberRange1.isEntirelyAfter(numberRange2)).toBe(false);
52
+ expect(numberRange2.isEntirelyAfter(numberRange1)).toBe(true);
53
+ expect(numberRange2.isEntirelyAfter(numberRange3)).toBe(false);
54
+ expect(numberRange3.isEntirelyAfter(numberRange2)).toBe(false);
55
+ expect(numberRange1.isEntirelyAfter(numberRange3)).toBe(false);
56
+ expect(numberRange3.isEntirelyAfter(numberRange1)).toBe(true);
57
+ });
58
+ test('isEntirelyBefore range', () => {
59
+ expect(numberRange1.isEntirelyBefore(numberRange2)).toBe(true);
60
+ expect(numberRange2.isEntirelyBefore(numberRange1)).toBe(false);
61
+ expect(numberRange2.isEntirelyBefore(numberRange3)).toBe(false);
62
+ expect(numberRange3.isEntirelyBefore(numberRange2)).toBe(false);
63
+ expect(numberRange1.isEntirelyBefore(numberRange3)).toBe(true);
64
+ expect(numberRange3.isEntirelyBefore(numberRange1)).toBe(false);
65
+ });
66
+ });
@@ -0,0 +1,46 @@
1
+ import Immutable from 'immutable';
2
+
3
+ export class NumberRange extends Immutable.Record({
4
+ start: 0,
5
+ end: 0
6
+ }) {
7
+ constructor(start: number, end: number) {
8
+ if (!(start < end)) {
9
+ throw new Error(`Invalid NumberRange(${start}, ${end})`);
10
+ }
11
+ super({
12
+ start: start,
13
+ end: end
14
+ });
15
+ }
16
+
17
+ toString(): string {
18
+ return `NumberRange(${this.start}, ${this.end})`;
19
+ }
20
+
21
+ contains(number: number): boolean {
22
+ return number >= this.start && number < this.end;
23
+ }
24
+
25
+ isEntirelyAfter(otherRangeOrNumber: NumberRange | number): boolean {
26
+ if (otherRangeOrNumber instanceof NumberRange) {
27
+ const otherRange = otherRangeOrNumber;
28
+ return otherRange.end <= this.start;
29
+ }
30
+ const otherNumber = otherRangeOrNumber;
31
+ return otherNumber < this.start;
32
+ }
33
+
34
+ isEntirelyBefore(otherRangeOrNumber: NumberRange | number): boolean {
35
+ if (otherRangeOrNumber instanceof NumberRange) {
36
+ const otherRange = otherRangeOrNumber;
37
+ return otherRange.start >= this.end;
38
+ }
39
+ const otherNumber = otherRangeOrNumber;
40
+ return otherNumber >= this.end;
41
+ }
42
+
43
+ intersects(otherRange: NumberRange): boolean {
44
+ return !this.isEntirelyAfter(otherRange) && !this.isEntirelyBefore(otherRange);
45
+ }
46
+ }
@@ -0,0 +1,49 @@
1
+ import { describe, expect, test } from '@jest/globals';
2
+ import { NumberRange } from './NumberRange';
3
+ import { NumberRangeRegistry } from './NumberRangeRegistry';
4
+
5
+ describe('NumberRangeRegistry utils', () => {
6
+ const numberRange1 = new NumberRange(-4, -3);
7
+ const numberRange2 = new NumberRange(-3, 3);
8
+ const numberRange3 = new NumberRange(3, 4);
9
+ const numberRange23 = new NumberRange(2, 4);
10
+ test('can register new range', () => {
11
+ const numberRangeRegistry = new NumberRangeRegistry();
12
+ numberRangeRegistry.register(numberRange2, 2);
13
+ expect(numberRangeRegistry.numberRanges.size).toEqual(1);
14
+ expect(numberRangeRegistry.numberRanges.get(0)).toEqual(numberRange2);
15
+ expect(numberRangeRegistry.values.size).toEqual(1);
16
+ expect(numberRangeRegistry.values.get(0)).toEqual(2);
17
+ });
18
+ test('registers ranges in right order', () => {
19
+ const numberRangeRegistry = new NumberRangeRegistry();
20
+ numberRangeRegistry.register(numberRange2, 2);
21
+ numberRangeRegistry.register(numberRange1, 1);
22
+ numberRangeRegistry.register(numberRange3, 3);
23
+ expect(numberRangeRegistry.numberRanges.size).toEqual(3);
24
+ expect(numberRangeRegistry.numberRanges.get(0)).toEqual(numberRange1);
25
+ expect(numberRangeRegistry.numberRanges.get(1)).toEqual(numberRange2);
26
+ expect(numberRangeRegistry.numberRanges.get(2)).toEqual(numberRange3);
27
+ expect(numberRangeRegistry.values.size).toEqual(3);
28
+ expect(numberRangeRegistry.values.get(0)).toEqual(1);
29
+ expect(numberRangeRegistry.values.get(1)).toEqual(2);
30
+ expect(numberRangeRegistry.values.get(2)).toEqual(3);
31
+ });
32
+ test('refuses to register overlapping range', () => {
33
+ const numberRangeRegistry = new NumberRangeRegistry();
34
+ numberRangeRegistry.register(numberRange2, 2);
35
+ expect(() => numberRangeRegistry.register(numberRange23, 1)).toThrow();
36
+ });
37
+ test('gets the value for a number', () => {
38
+ const numberRangeRegistry = new NumberRangeRegistry();
39
+ numberRangeRegistry.register(numberRange1, 1);
40
+ numberRangeRegistry.register(numberRange3, 3);
41
+ numberRangeRegistry.register(numberRange2, 2);
42
+ expect(numberRangeRegistry.getValueForNumber(-5)).toEqual(undefined);
43
+ expect(numberRangeRegistry.getValueForNumber(-4)).toEqual(1);
44
+ expect(numberRangeRegistry.getValueForNumber(-3)).toEqual(2);
45
+ expect(numberRangeRegistry.getValueForNumber(2)).toEqual(2);
46
+ expect(numberRangeRegistry.getValueForNumber(3)).toEqual(3);
47
+ expect(numberRangeRegistry.getValueForNumber(4)).toEqual(undefined);
48
+ });
49
+ });
@@ -0,0 +1,43 @@
1
+ import Immutable from 'immutable';
2
+ import * as generalUtils from './general';
3
+ import type { NumberRange } from './NumberRange';
4
+
5
+ export class NumberRangeRegistry<T> {
6
+ public numberRanges: Immutable.List<NumberRange> = Immutable.List();
7
+ public values: Immutable.List<T> = Immutable.List();
8
+
9
+ register(numberRange: NumberRange, value: T): void {
10
+ const index = generalUtils.binarySearch(this.numberRanges, numberRange, {
11
+ getLength: (numberRanges) => numberRanges.size,
12
+ getNewRange: (numberRanges, numberRangeToInsert, start, end) => {
13
+ const mid = Math.floor((start + end) / 2);
14
+ const midNumberRange = numberRanges.get(mid)!;
15
+ const isEntirelyBeforeMid = numberRangeToInsert.isEntirelyBefore(midNumberRange);
16
+ const isEntirelyAfterMid = numberRangeToInsert.isEntirelyAfter(midNumberRange);
17
+ if (!isEntirelyBeforeMid && !isEntirelyAfterMid) {
18
+ throw new Error(`Refusing to insert ${numberRangeToInsert.toString()} ` + `next to ${midNumberRange.toString()}`);
19
+ }
20
+ return isEntirelyBeforeMid ? [start, mid] : [mid + 1, end];
21
+ }
22
+ });
23
+ this.numberRanges = this.numberRanges.insert(index, numberRange);
24
+ this.values = this.values.insert(index, value);
25
+ }
26
+
27
+ getValueForNumber(number: number): T | undefined {
28
+ const index = generalUtils.binarySearch(this.numberRanges, number, {
29
+ getLength: (numberRanges) => numberRanges.size,
30
+ getNewRange: (numberRanges, number_, start, end) => {
31
+ const mid = Math.floor((start + end) / 2);
32
+ const midNumberRange = numberRanges.get(mid)!;
33
+ const isBeforeOrInMid = !midNumberRange.isEntirelyBefore(number_);
34
+ return isBeforeOrInMid ? [start, mid] : [mid + 1, end];
35
+ }
36
+ });
37
+ const numberRangeAtIndex = this.numberRanges.get(index);
38
+ if (numberRangeAtIndex !== undefined && numberRangeAtIndex.contains(number)) {
39
+ return this.values.get(index);
40
+ }
41
+ return undefined;
42
+ }
43
+ }
@@ -0,0 +1,126 @@
1
+ import { describe, expect, test } from '@jest/globals';
2
+ import * as byteUtils from './bytes';
3
+
4
+ describe('byte utils', () => {
5
+ const bytes = [0, 0x01, 0x001, 156, 0xff];
6
+ const nonBytes = [-1, 0x100, 0.5, 'asdf', '0xFF', false, true, [], {}, undefined, null];
7
+ test('isByte works', () => {
8
+ bytes.forEach((byte) => {
9
+ expect(byteUtils.isByte(byte)).toBe(true);
10
+ });
11
+ nonBytes.forEach((nonByte) => {
12
+ expect(byteUtils.isByte(nonByte)).toBe(false);
13
+ });
14
+ });
15
+ test('isByteArr works', () => {
16
+ expect(byteUtils.isByteArr([])).toBe(true);
17
+ bytes.forEach((byte) => {
18
+ expect(byteUtils.isByteArr([byte])).toBe(true);
19
+ });
20
+ nonBytes.forEach((nonByte) => {
21
+ expect(byteUtils.isByteArr([nonByte])).toBe(false);
22
+ });
23
+ });
24
+ test('assertIsByteArr works', () => {
25
+ expect(() => byteUtils.assertIsByteArr([])).not.toThrow();
26
+ bytes.forEach((byte) => {
27
+ expect(() => byteUtils.assertIsByteArr([byte])).not.toThrow();
28
+ });
29
+ nonBytes.forEach((nonByte) => {
30
+ expect(() => byteUtils.assertIsByteArr([nonByte])).toThrow();
31
+ });
32
+ });
33
+ test('isArrOfLengths works', () => {
34
+ expect(byteUtils.isArrOfLengths([], [])).toBe(false);
35
+ expect(byteUtils.isArrOfLengths([], [0])).toBe(true);
36
+ expect(byteUtils.isArrOfLengths([], [1])).toBe(false);
37
+ expect(byteUtils.isArrOfLengths([], [0, 1])).toBe(true);
38
+ expect(byteUtils.isArrOfLengths([], [1, 2])).toBe(false);
39
+ expect(byteUtils.isArrOfLengths([1], [])).toBe(false);
40
+ expect(byteUtils.isArrOfLengths([1], [0])).toBe(false);
41
+ expect(byteUtils.isArrOfLengths([1], [1])).toBe(true);
42
+ expect(byteUtils.isArrOfLengths([1], [0, 1])).toBe(true);
43
+ expect(byteUtils.isArrOfLengths([1], [1, 2])).toBe(true);
44
+ });
45
+ test('assertArrIsOfLengths works', () => {
46
+ expect(() => byteUtils.assertArrIsOfLengths([], [])).toThrow();
47
+ expect(() => byteUtils.assertArrIsOfLengths([], [0])).not.toThrow();
48
+ expect(() => byteUtils.assertArrIsOfLengths([], [1])).toThrow();
49
+ expect(() => byteUtils.assertArrIsOfLengths([], [0, 1])).not.toThrow();
50
+ expect(() => byteUtils.assertArrIsOfLengths([], [1, 2])).toThrow();
51
+ expect(() => byteUtils.assertArrIsOfLengths([1], [])).toThrow();
52
+ expect(() => byteUtils.assertArrIsOfLengths([1], [0])).toThrow();
53
+ expect(() => byteUtils.assertArrIsOfLengths([1], [1])).not.toThrow();
54
+ expect(() => byteUtils.assertArrIsOfLengths([1], [0, 1])).not.toThrow();
55
+ expect(() => byteUtils.assertArrIsOfLengths([1], [1, 2])).not.toThrow();
56
+ });
57
+ test('arr2big works', () => {
58
+ expect(byteUtils.arr2big([])).toBe(0x00);
59
+ expect(byteUtils.arr2big([0x00])).toBe(0x00);
60
+ expect(byteUtils.arr2big([0x001])).toBe(0x01);
61
+ expect(byteUtils.arr2big([0x00, 0x00])).toBe(0x0000);
62
+ expect(byteUtils.arr2big([0x12])).toBe(0x12);
63
+ expect(byteUtils.arr2big([0x12, 0x34])).toBe(0x1234);
64
+ expect(byteUtils.arr2big([0x12, 0x34, 0x56, 0xaf])).toBe(0x123456af);
65
+ expect(byteUtils.arr2big([0xff])).toBe(0xff);
66
+ expect(byteUtils.arr2big([0xff, 0xff])).toBe(0xffff);
67
+ });
68
+ test('arr2big sanitizes', () => {
69
+ expect(() => byteUtils.arr2big([0x100])).toThrow();
70
+ expect(() => byteUtils.arr2big([0x123])).toThrow();
71
+ expect(() => byteUtils.arr2big([0x123456])).toThrow();
72
+ expect(() => byteUtils.arr2big([0xff, 0x100])).toThrow();
73
+ expect(() => byteUtils.arr2big([0xff, 0x100])).toThrow();
74
+ expect(() => byteUtils.arr2big([2.5])).toThrow();
75
+ });
76
+ test('prettyHex without lineLength', () => {
77
+ expect(byteUtils.prettyHex([])).toBe('');
78
+ expect(byteUtils.prettyHex([0x00])).toBe('00');
79
+ expect(byteUtils.prettyHex([0xff])).toBe('FF');
80
+ expect(byteUtils.prettyHex([0x00, 0x00])).toBe('00 00');
81
+ expect(byteUtils.prettyHex([0x00, 0x00, 0x00])).toBe('00 00 00');
82
+ expect(byteUtils.prettyHex([0x12, 0x34, 0x00])).toBe('12 34 00');
83
+ expect(byteUtils.prettyHex([0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10])).toBe('FE DC BA 98 76 54 32 10');
84
+ expect(byteUtils.prettyHex('')).toBe('');
85
+ expect(byteUtils.prettyHex('a')).toBe('61');
86
+ expect(byteUtils.prettyHex('AA')).toBe('41 41');
87
+ expect(byteUtils.prettyHex('000')).toBe('30 30 30');
88
+ expect(byteUtils.prettyHex(' ')).toBe('20 20 20 20');
89
+ expect(byteUtils.prettyHex([0xfff])).toBe('FF');
90
+ expect(byteUtils.prettyHex([undefined])).toBe('??');
91
+ });
92
+ test('prettyHex with lineLength', () => {
93
+ expect(byteUtils.prettyHex('', 0)).toBe('');
94
+ expect(byteUtils.prettyHex('1', 0)).toBe('31');
95
+ expect(byteUtils.prettyHex('12345678', 0)).toBe('31 32 33 34 35 36 37 38');
96
+ expect(byteUtils.prettyHex('', 1)).toBe('');
97
+ expect(byteUtils.prettyHex('1', 1)).toBe('31');
98
+ expect(byteUtils.prettyHex('12345678', 1)).toBe('31\n32\n33\n34\n35\n36\n37\n38');
99
+ expect(byteUtils.prettyHex('', 4)).toBe('');
100
+ expect(byteUtils.prettyHex('1', 4)).toBe('31');
101
+ expect(byteUtils.prettyHex('123', 4)).toBe('31 32 33');
102
+ expect(byteUtils.prettyHex('1234', 4)).toBe('31 32 33 34');
103
+ expect(byteUtils.prettyHex('12345', 4)).toBe('31 32 33 34\n35');
104
+ expect(byteUtils.prettyHex('1234567', 4)).toBe('31 32 33 34\n35 36 37');
105
+ expect(byteUtils.prettyHex('12345678', 4)).toBe('31 32 33 34\n35 36 37 38');
106
+ expect(byteUtils.prettyHex('123456789', 4)).toBe('31 32 33 34\n35 36 37 38\n39');
107
+ expect(byteUtils.prettyHex([0xfff], 4)).toBe('FF');
108
+ expect(byteUtils.prettyHex([undefined], 4)).toBe('??');
109
+ });
110
+ test('unPrettyHex', () => {
111
+ expect(byteUtils.unPrettyHex('')).toEqual([]);
112
+ expect(byteUtils.unPrettyHex('31')).toEqual([0x31]);
113
+ expect(byteUtils.unPrettyHex('31 32')).toEqual([0x31, 0x32]);
114
+ expect(byteUtils.unPrettyHex('31 32 33 34\n35')).toEqual([0x31, 0x32, 0x33, 0x34, 0x35]);
115
+ expect(byteUtils.unPrettyHex('00 FF ff')).toEqual([0x00, 0xff, 0xff]);
116
+
117
+ expect(byteUtils.unPrettyHex('??')).toEqual([undefined]);
118
+ expect(byteUtils.unPrettyHex('31 ??')).toEqual([0x31, undefined]);
119
+ expect(byteUtils.unPrettyHex('?? 32')).toEqual([undefined, 0x32]);
120
+ expect(byteUtils.unPrettyHex('?? ??')).toEqual([undefined, undefined]);
121
+ expect(byteUtils.unPrettyHex('??\n??')).toEqual([undefined, undefined]);
122
+
123
+ expect(() => byteUtils.unPrettyHex('1')).toThrow();
124
+ expect(() => byteUtils.unPrettyHex('GG')).toThrow();
125
+ });
126
+ });