@hangtime/grip-connect 0.5.7 → 0.5.8

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.
package/README.md CHANGED
@@ -63,12 +63,14 @@ motherboard.notify((data) => {
63
63
 
64
64
  // Optional: Check if the device is active
65
65
  motherboard.active(
66
- (isActive) => { console.log(isActive) },
66
+ (isActive) => {
67
+ console.log(isActive)
68
+ },
67
69
  // Optionally using a weight threshold and duration
68
70
  { threshold: 2.5, duration: 1000 },
69
71
  )
70
72
 
71
- document.querySelector("#motherboard").addEventListener("click", () => {
73
+ document.querySelector("#motherboard").addEventListener("click", async () => {
72
74
  // Connect to device
73
75
  await motherboard.connect(
74
76
  async () => {
@@ -80,8 +82,8 @@ document.querySelector("#motherboard").addEventListener("click", () => {
80
82
  // await motherboard.led("red")
81
83
  // await motherboard.led()
82
84
 
83
- // Start weight streaming (for a minute) remove parameter for a continues stream
84
- await motherboard.stream(60000)
85
+ // Start weight streaming (for 30s) remove parameter for a continues stream
86
+ await motherboard.stream(30000)
85
87
 
86
88
  // Manualy tare the device when the stream is running
87
89
  // await motherboard.tare(5000)
@@ -91,14 +93,15 @@ document.querySelector("#motherboard").addEventListener("click", () => {
91
93
 
92
94
  // Download data as CSV, JSON, or XML (default: CSV) format => timestamp, frame, battery, samples, masses
93
95
  // motherboard.download('json')
96
+
97
+ // Optionally disconnect from device after we are done
98
+ motherboard.disconnect(Motherboard)
94
99
  },
95
100
  (error) => {
96
101
  // Optinal custom error handeling
97
102
  console.error(error.message)
98
103
  },
99
104
  )
100
- // Disconnect from device after we are done
101
- motherboard.disconnect(Motherboard)
102
105
  })
103
106
  ```
104
107
 
@@ -154,14 +154,12 @@ export class Entralpi extends Device {
154
154
  const value = characteristic.value;
155
155
  if (value) {
156
156
  if (value.buffer) {
157
- const buffer = value.buffer;
158
- const rawData = new DataView(buffer);
159
157
  const receivedTime = Date.now();
160
- const receivedData = (rawData.getUint16(0) / 100).toFixed(1);
158
+ const receivedData = (value.getUint16(0) / 100).toFixed(1);
161
159
  const convertedData = Number(receivedData);
162
160
  // Adjust weight by using the tare value
163
161
  // If tare is 0, use the original weight, otherwise subtract tare and invert.
164
- // This will display the romoved or 'no-hanging' weight.
162
+ // This will display the removed or 'no-hanging' weight.
165
163
  const tare = this.applyTare(convertedData);
166
164
  const numericData = tare === 0 ? convertedData : (convertedData - tare) * -1;
167
165
  // Add data to downloadable Array
@@ -183,38 +183,41 @@ export class ForceBoard extends Device {
183
183
  const value = characteristic.value;
184
184
  if (value) {
185
185
  if (value.buffer) {
186
- const buffer = value.buffer;
187
- const rawData = new DataView(buffer);
188
186
  const receivedTime = Date.now();
189
- const receivedData = rawData.getUint8(4); // Read the value at index 4
190
- // Convert from LBS to KG
191
- const convertedReceivedData = receivedData * 0.453592;
192
- // Tare correction
193
- const numericData = convertedReceivedData - this.applyTare(convertedReceivedData);
194
- // Add data to downloadable Array
195
- this.downloadPackets.push({
196
- received: receivedTime,
197
- sampleNum: this.dataPointCount,
198
- battRaw: 0,
199
- samples: [convertedReceivedData],
200
- masses: [numericData],
201
- });
202
- // Update massMax
203
- this.massMax = Math.max(Number(this.massMax), numericData).toFixed(1);
204
- // Update running sum and count
205
- const currentMassTotal = Math.max(-1000, numericData);
206
- this.massTotalSum += currentMassTotal;
207
- this.dataPointCount++;
208
- // Calculate the average dynamically
209
- this.massAverage = (this.massTotalSum / this.dataPointCount).toFixed(1);
210
- // Check if device is being used
211
- this.activityCheck(numericData);
212
- // Notify with weight data
213
- this.notifyCallback({
214
- massMax: this.massMax,
215
- massAverage: this.massAverage,
216
- massTotal: Math.max(-1000, numericData).toFixed(1),
217
- });
187
+ const dataArray = new Uint8Array(value.buffer);
188
+ // Skip the first 2 bytes, which are the command and length
189
+ // The data is sent in groups of 3 bytes
190
+ for (let i = 2; i < dataArray.length; i += 3) {
191
+ const receivedData = (dataArray[i] << 16) | (dataArray[i + 1] << 8) | dataArray[i + 2];
192
+ // Convert from LBS to KG
193
+ const convertedReceivedData = receivedData * 0.453592;
194
+ // Tare correction
195
+ const numericData = convertedReceivedData - this.applyTare(convertedReceivedData);
196
+ // Add data to downloadable Array
197
+ this.downloadPackets.push({
198
+ received: receivedTime,
199
+ sampleNum: this.dataPointCount,
200
+ battRaw: 0,
201
+ samples: [convertedReceivedData],
202
+ masses: [numericData],
203
+ });
204
+ // Update massMax
205
+ this.massMax = Math.max(Number(this.massMax), numericData).toFixed(1);
206
+ // Update running sum and count
207
+ const currentMassTotal = Math.max(-1000, numericData);
208
+ this.massTotalSum += currentMassTotal;
209
+ this.dataPointCount++;
210
+ // Calculate the average dynamically
211
+ this.massAverage = (this.massTotalSum / this.dataPointCount).toFixed(1);
212
+ // Check if device is being used
213
+ this.activityCheck(numericData);
214
+ // Notify with weight data
215
+ this.notifyCallback({
216
+ massMax: this.massMax,
217
+ massAverage: this.massAverage,
218
+ massTotal: Math.max(-1000, numericData).toFixed(1),
219
+ });
220
+ }
218
221
  }
219
222
  }
220
223
  };
@@ -1,5 +1,4 @@
1
1
  import { Device } from "../device.model";
2
- import struct from "../../helpers/struct";
3
2
  /**
4
3
  * Represents the possible responses of a Tindeq Progressor device.
5
4
  */
@@ -119,15 +118,25 @@ export class Progressor extends Device {
119
118
  const value = characteristic.value;
120
119
  if (value) {
121
120
  if (value.buffer) {
122
- const buffer = value.buffer;
123
- const rawData = new DataView(buffer);
124
121
  const receivedTime = Date.now();
125
- const [kind] = struct("<bb").unpack(rawData.buffer.slice(0, 2));
122
+ // Read the first byte of the buffer to determine the kind of message
123
+ const kind = value.getInt8(0);
124
+ // Check if the message is a weight measurement
126
125
  if (kind === ProgressorResponses.WEIGHT_MEASURE) {
127
- const iterable = struct("<fi").iter_unpack(rawData.buffer.slice(2));
128
- // eslint-disable-next-line prefer-const
129
- for (let [weight, seconds] of iterable) {
130
- if (typeof weight === "number" && !isNaN(weight) && typeof seconds === "number" && !isNaN(seconds)) {
126
+ // Start parsing data from the 3rd byte (index 2)
127
+ let offset = 2;
128
+ // Continue parsing while there's data left in the buffer
129
+ while (offset < value.byteLength) {
130
+ // Read a 32-bit float (4 bytes) for the weight, using little-endian
131
+ const weight = value.getFloat32(offset, true);
132
+ // Move the offset by 4 bytes
133
+ offset += 4;
134
+ // Read a 32-bit integer (4 bytes) for the seconds, using little-endian
135
+ const seconds = value.getInt32(offset, true);
136
+ // Move the offset by 4 bytes
137
+ offset += 4;
138
+ // Check if both weight and seconds are valid numbers
139
+ if (!isNaN(weight) && !isNaN(seconds)) {
131
140
  // Tare correction
132
141
  const numericData = weight - this.applyTare(weight);
133
142
  // Add data to downloadable Array
@@ -159,17 +168,17 @@ export class Progressor extends Device {
159
168
  else if (kind === ProgressorResponses.COMMAND_RESPONSE) {
160
169
  if (!this.writeLast)
161
170
  return;
162
- let value = "";
171
+ let output = "";
163
172
  if (this.writeLast === this.commands.GET_BATT_VLTG) {
164
- value = new DataView(rawData.buffer, 2).getUint32(0, true).toString();
173
+ output = new DataView(value.buffer, 2).getUint32(0, true).toString();
165
174
  }
166
175
  else if (this.writeLast === this.commands.GET_FW_VERSION) {
167
- value = new TextDecoder().decode(rawData.buffer.slice(2));
176
+ output = new TextDecoder().decode(new Uint8Array(value.buffer).slice(2));
168
177
  }
169
178
  else if (this.writeLast === this.commands.GET_ERR_INFO) {
170
- value = new TextDecoder().decode(rawData.buffer.slice(2));
179
+ output = new TextDecoder().decode(new Uint8Array(value.buffer.slice(2)));
171
180
  }
172
- this.writeCallback(value);
181
+ this.writeCallback(output);
173
182
  }
174
183
  else if (kind === ProgressorResponses.LOW_BATTERY_WARNING) {
175
184
  console.warn("⚠️ Low power detected. Please consider connecting to a power source.");
@@ -376,10 +376,7 @@ export class Device extends BaseModel {
376
376
  const value = characteristic.value;
377
377
  if (value) {
378
378
  if (value.buffer) {
379
- const buffer = value.buffer;
380
- console.log(new Uint8Array(buffer));
381
- const rawData = new DataView(buffer);
382
- console.log(rawData);
379
+ console.log(value);
383
380
  }
384
381
  else {
385
382
  console.log(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hangtime/grip-connect",
3
- "version": "0.5.7",
3
+ "version": "0.5.8",
4
4
  "description": "Griptonite Motherboard, Tindeq Progressor, PitchSix Force Board, WHC-06, Entralpi, Climbro, mySmartBoard: Web Bluetooth API Force-Sensing strength analysis for climbers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -161,15 +161,13 @@ export class Entralpi extends Device implements IEntralpi {
161
161
 
162
162
  if (value) {
163
163
  if (value.buffer) {
164
- const buffer: ArrayBuffer = value.buffer
165
- const rawData: DataView = new DataView(buffer)
166
164
  const receivedTime: number = Date.now()
167
- const receivedData: string = (rawData.getUint16(0) / 100).toFixed(1)
165
+ const receivedData: string = (value.getUint16(0) / 100).toFixed(1)
168
166
 
169
167
  const convertedData = Number(receivedData)
170
168
  // Adjust weight by using the tare value
171
169
  // If tare is 0, use the original weight, otherwise subtract tare and invert.
172
- // This will display the romoved or 'no-hanging' weight.
170
+ // This will display the removed or 'no-hanging' weight.
173
171
  const tare = this.applyTare(convertedData)
174
172
  const numericData = tare === 0 ? convertedData : (convertedData - tare) * -1
175
173
  // Add data to downloadable Array
@@ -187,44 +187,46 @@ export class ForceBoard extends Device implements IForceBoard {
187
187
  const value: DataView | undefined = characteristic.value
188
188
  if (value) {
189
189
  if (value.buffer) {
190
- const buffer: ArrayBuffer = value.buffer
191
- const rawData: DataView = new DataView(buffer)
192
190
  const receivedTime: number = Date.now()
191
+ const dataArray = new Uint8Array(value.buffer)
192
+ // Skip the first 2 bytes, which are the command and length
193
+ // The data is sent in groups of 3 bytes
194
+ for (let i = 2; i < dataArray.length; i += 3) {
195
+ const receivedData = (dataArray[i] << 16) | (dataArray[i + 1] << 8) | dataArray[i + 2]
196
+ // Convert from LBS to KG
197
+ const convertedReceivedData = receivedData * 0.453592
198
+ // Tare correction
199
+ const numericData = convertedReceivedData - this.applyTare(convertedReceivedData)
200
+ // Add data to downloadable Array
201
+ this.downloadPackets.push({
202
+ received: receivedTime,
203
+ sampleNum: this.dataPointCount,
204
+ battRaw: 0,
205
+ samples: [convertedReceivedData],
206
+ masses: [numericData],
207
+ })
193
208
 
194
- const receivedData = rawData.getUint8(4) // Read the value at index 4
195
- // Convert from LBS to KG
196
- const convertedReceivedData = receivedData * 0.453592
197
- // Tare correction
198
- const numericData = convertedReceivedData - this.applyTare(convertedReceivedData)
199
- // Add data to downloadable Array
200
- this.downloadPackets.push({
201
- received: receivedTime,
202
- sampleNum: this.dataPointCount,
203
- battRaw: 0,
204
- samples: [convertedReceivedData],
205
- masses: [numericData],
206
- })
209
+ // Update massMax
210
+ this.massMax = Math.max(Number(this.massMax), numericData).toFixed(1)
207
211
 
208
- // Update massMax
209
- this.massMax = Math.max(Number(this.massMax), numericData).toFixed(1)
212
+ // Update running sum and count
213
+ const currentMassTotal = Math.max(-1000, numericData)
214
+ this.massTotalSum += currentMassTotal
215
+ this.dataPointCount++
210
216
 
211
- // Update running sum and count
212
- const currentMassTotal = Math.max(-1000, numericData)
213
- this.massTotalSum += currentMassTotal
214
- this.dataPointCount++
217
+ // Calculate the average dynamically
218
+ this.massAverage = (this.massTotalSum / this.dataPointCount).toFixed(1)
215
219
 
216
- // Calculate the average dynamically
217
- this.massAverage = (this.massTotalSum / this.dataPointCount).toFixed(1)
220
+ // Check if device is being used
221
+ this.activityCheck(numericData)
218
222
 
219
- // Check if device is being used
220
- this.activityCheck(numericData)
221
-
222
- // Notify with weight data
223
- this.notifyCallback({
224
- massMax: this.massMax,
225
- massAverage: this.massAverage,
226
- massTotal: Math.max(-1000, numericData).toFixed(1),
227
- })
223
+ // Notify with weight data
224
+ this.notifyCallback({
225
+ massMax: this.massMax,
226
+ massAverage: this.massAverage,
227
+ massTotal: Math.max(-1000, numericData).toFixed(1),
228
+ })
229
+ }
228
230
  }
229
231
  }
230
232
  }
@@ -1,6 +1,5 @@
1
1
  import { Device } from "../device.model"
2
2
  import type { IProgressor } from "../../interfaces/device/progressor.interface"
3
- import struct from "../../helpers/struct"
4
3
 
5
4
  /**
6
5
  * Represents the possible responses of a Tindeq Progressor device.
@@ -129,15 +128,25 @@ export class Progressor extends Device implements IProgressor {
129
128
 
130
129
  if (value) {
131
130
  if (value.buffer) {
132
- const buffer: ArrayBuffer = value.buffer
133
- const rawData: DataView = new DataView(buffer)
134
131
  const receivedTime: number = Date.now()
135
- const [kind] = struct("<bb").unpack(rawData.buffer.slice(0, 2))
132
+ // Read the first byte of the buffer to determine the kind of message
133
+ const kind = value.getInt8(0)
134
+ // Check if the message is a weight measurement
136
135
  if (kind === ProgressorResponses.WEIGHT_MEASURE) {
137
- const iterable: IterableIterator<unknown[]> = struct("<fi").iter_unpack(rawData.buffer.slice(2))
138
- // eslint-disable-next-line prefer-const
139
- for (let [weight, seconds] of iterable) {
140
- if (typeof weight === "number" && !isNaN(weight) && typeof seconds === "number" && !isNaN(seconds)) {
136
+ // Start parsing data from the 3rd byte (index 2)
137
+ let offset = 2
138
+ // Continue parsing while there's data left in the buffer
139
+ while (offset < value.byteLength) {
140
+ // Read a 32-bit float (4 bytes) for the weight, using little-endian
141
+ const weight = value.getFloat32(offset, true)
142
+ // Move the offset by 4 bytes
143
+ offset += 4
144
+ // Read a 32-bit integer (4 bytes) for the seconds, using little-endian
145
+ const seconds = value.getInt32(offset, true)
146
+ // Move the offset by 4 bytes
147
+ offset += 4
148
+ // Check if both weight and seconds are valid numbers
149
+ if (!isNaN(weight) && !isNaN(seconds)) {
141
150
  // Tare correction
142
151
  const numericData = weight - this.applyTare(weight)
143
152
  // Add data to downloadable Array
@@ -171,16 +180,16 @@ export class Progressor extends Device implements IProgressor {
171
180
  } else if (kind === ProgressorResponses.COMMAND_RESPONSE) {
172
181
  if (!this.writeLast) return
173
182
 
174
- let value = ""
183
+ let output = ""
175
184
 
176
185
  if (this.writeLast === this.commands.GET_BATT_VLTG) {
177
- value = new DataView(rawData.buffer, 2).getUint32(0, true).toString()
186
+ output = new DataView(value.buffer, 2).getUint32(0, true).toString()
178
187
  } else if (this.writeLast === this.commands.GET_FW_VERSION) {
179
- value = new TextDecoder().decode(rawData.buffer.slice(2))
188
+ output = new TextDecoder().decode(new Uint8Array(value.buffer).slice(2))
180
189
  } else if (this.writeLast === this.commands.GET_ERR_INFO) {
181
- value = new TextDecoder().decode(rawData.buffer.slice(2))
190
+ output = new TextDecoder().decode(new Uint8Array(value.buffer.slice(2)))
182
191
  }
183
- this.writeCallback(value)
192
+ this.writeCallback(output)
184
193
  } else if (kind === ProgressorResponses.LOW_BATTERY_WARNING) {
185
194
  console.warn("⚠️ Low power detected. Please consider connecting to a power source.")
186
195
  } else {
@@ -437,10 +437,7 @@ export abstract class Device extends BaseModel implements IDevice {
437
437
 
438
438
  if (value) {
439
439
  if (value.buffer) {
440
- const buffer: ArrayBuffer = value.buffer
441
- console.log(new Uint8Array(buffer))
442
- const rawData: DataView = new DataView(buffer)
443
- console.log(rawData)
440
+ console.log(value)
444
441
  } else {
445
442
  console.log(value)
446
443
  }
@@ -1,9 +0,0 @@
1
- export default function struct(format: string): Readonly<{
2
- unpack: (arrb: ArrayBuffer) => unknown[];
3
- pack: (...values: unknown[]) => ArrayBuffer;
4
- unpack_from: (arrb: ArrayBuffer, offs: number) => unknown[];
5
- pack_into: (arrb: ArrayBuffer, offs: number, ...values: unknown[]) => void;
6
- iter_unpack: (arrb: ArrayBuffer) => IterableIterator<unknown[]>;
7
- format: string;
8
- size: number;
9
- }>;
@@ -1,203 +0,0 @@
1
- const rechk = /^([<>])?(([1-9]\d*)?([xcbB?hHiIfdsp]))*$/;
2
- const refmt = /([1-9]\d*)?([xcbB?hHiIfdsp])/g;
3
- const str = (v, o, c) => String.fromCharCode(...Array.from(new Uint8Array(v.buffer, v.byteOffset + o, c)));
4
- const rts = (v, o, c, s) => {
5
- new Uint8Array(v.buffer, v.byteOffset + o, c).set(s.split("").map((str) => str.charCodeAt(0)));
6
- };
7
- const pst = (v, o, c) => str(v, o + 1, Math.min(v.getUint8(o), c - 1));
8
- const tsp = (v, o, c, s) => {
9
- v.setUint8(o, s.length);
10
- rts(v, o + 1, c - 1, s);
11
- };
12
- const lut = (le) => ({
13
- x: (c) => [
14
- 1,
15
- c,
16
- () => ({
17
- u: () => undefined,
18
- p: () => undefined,
19
- }),
20
- ],
21
- c: (c) => [
22
- c,
23
- 1,
24
- (o) => ({
25
- u: (v) => str(v, o, 1),
26
- p: (v, s) => {
27
- rts(v, o, 1, s);
28
- },
29
- }),
30
- ],
31
- "?": (c) => [
32
- c,
33
- 1,
34
- (o) => ({
35
- u: (v) => Boolean(v.getUint8(o)),
36
- p: (v, B) => {
37
- v.setUint8(o, B ? 1 : 0);
38
- },
39
- }),
40
- ],
41
- b: (c) => [
42
- c,
43
- 1,
44
- (o) => ({
45
- u: (v) => v.getInt8(o),
46
- p: (v, b) => {
47
- v.setInt8(o, b);
48
- },
49
- }),
50
- ],
51
- B: (c) => [
52
- c,
53
- 1,
54
- (o) => ({
55
- u: (v) => v.getUint8(o),
56
- p: (v, B) => {
57
- v.setUint8(o, B);
58
- },
59
- }),
60
- ],
61
- h: (c) => [
62
- c,
63
- 2,
64
- (o) => ({
65
- u: (v) => v.getInt16(o, le),
66
- p: (v, h) => {
67
- v.setInt16(o, h, le);
68
- },
69
- }),
70
- ],
71
- H: (c) => [
72
- c,
73
- 2,
74
- (o) => ({
75
- u: (v) => v.getUint16(o, le),
76
- p: (v, H) => {
77
- v.setUint16(o, H, le);
78
- },
79
- }),
80
- ],
81
- i: (c) => [
82
- c,
83
- 4,
84
- (o) => ({
85
- u: (v) => v.getInt32(o, le),
86
- p: (v, i) => {
87
- v.setInt32(o, i, le);
88
- },
89
- }),
90
- ],
91
- I: (c) => [
92
- c,
93
- 4,
94
- (o) => ({
95
- u: (v) => v.getUint32(o, le),
96
- p: (v, I) => {
97
- v.setUint32(o, I, le);
98
- },
99
- }),
100
- ],
101
- f: (c) => [
102
- c,
103
- 4,
104
- (o) => ({
105
- u: (v) => v.getFloat32(o, le),
106
- p: (v, f) => {
107
- v.setFloat32(o, f, le);
108
- },
109
- }),
110
- ],
111
- d: (c) => [
112
- c,
113
- 8,
114
- (o) => ({
115
- u: (v) => v.getFloat64(o, le),
116
- p: (v, d) => {
117
- v.setFloat64(o, d, le);
118
- },
119
- }),
120
- ],
121
- s: (c) => [
122
- 1,
123
- c,
124
- (o) => ({
125
- u: (v) => str(v, o, c),
126
- p: (v, s) => {
127
- rts(v, o, c, s.slice(0, c));
128
- },
129
- }),
130
- ],
131
- p: (c) => [
132
- 1,
133
- c,
134
- (o) => ({
135
- u: (v) => pst(v, o, c),
136
- p: (v, s) => {
137
- tsp(v, o, c, s.slice(0, c - 1));
138
- },
139
- }),
140
- ],
141
- });
142
- const errbuf = new RangeError("Structure larger than remaining buffer");
143
- const errval = new RangeError("Not enough values for structure");
144
- export default function struct(format) {
145
- const fns = [];
146
- let size = 0;
147
- let m = rechk.exec(format);
148
- if (!m) {
149
- throw new RangeError("Invalid format string");
150
- }
151
- const t = lut("<" === m[1]);
152
- const lu = (n, c) => t[c](n ? parseInt(n, 10) : 1);
153
- while ((m = refmt.exec(format))) {
154
- ;
155
- ((r, s, f) => {
156
- for (let i = 0; i < r; ++i, size += s) {
157
- if (f) {
158
- fns.push(f(size));
159
- }
160
- }
161
- })(...lu(...m.slice(1)));
162
- }
163
- const unpack_from = (arrb, offs) => {
164
- if (arrb.byteLength < (offs | 0) + size) {
165
- throw errbuf;
166
- }
167
- const v = new DataView(arrb, offs | 0);
168
- return fns.map((f) => f.u(v));
169
- };
170
- const pack_into = (arrb, offs, ...values) => {
171
- if (values.length < fns.length) {
172
- throw errval;
173
- }
174
- if (arrb.byteLength < offs + size) {
175
- throw errbuf;
176
- }
177
- const v = new DataView(arrb, offs);
178
- new Uint8Array(arrb, offs, size).fill(0);
179
- fns.forEach((f, i) => {
180
- f.p(v, values[i]);
181
- });
182
- };
183
- const pack = (...values) => {
184
- const b = new ArrayBuffer(size);
185
- pack_into(b, 0, ...values);
186
- return b;
187
- };
188
- const unpack = (arrb) => unpack_from(arrb, 0);
189
- function* iter_unpack(arrb) {
190
- for (let offs = 0; offs + size <= arrb.byteLength; offs += size) {
191
- yield unpack_from(arrb, offs);
192
- }
193
- }
194
- return Object.freeze({
195
- unpack,
196
- pack,
197
- unpack_from,
198
- pack_into,
199
- iter_unpack,
200
- format,
201
- size,
202
- });
203
- }
@@ -1,239 +0,0 @@
1
- const rechk = /^([<>])?(([1-9]\d*)?([xcbB?hHiIfdsp]))*$/
2
- const refmt = /([1-9]\d*)?([xcbB?hHiIfdsp])/g
3
-
4
- const str = (v: DataView, o: number, c: number): string =>
5
- String.fromCharCode(...Array.from(new Uint8Array(v.buffer, v.byteOffset + o, c)))
6
-
7
- const rts = (v: DataView, o: number, c: number, s: string): void => {
8
- new Uint8Array(v.buffer, v.byteOffset + o, c).set(s.split("").map((str) => str.charCodeAt(0)))
9
- }
10
-
11
- const pst = (v: DataView, o: number, c: number): string => str(v, o + 1, Math.min(v.getUint8(o), c - 1))
12
-
13
- const tsp = (v: DataView, o: number, c: number, s: string): void => {
14
- v.setUint8(o, s.length)
15
- rts(v, o + 1, c - 1, s)
16
- }
17
-
18
- interface FormatFn {
19
- u: (v: DataView) => unknown
20
- p: (v: DataView, value: unknown) => void
21
- }
22
-
23
- type LUT = Record<string, (c: number) => [number, number, (o: number) => FormatFn]>
24
-
25
- const lut = (le: boolean): LUT => ({
26
- x: (c: number) =>
27
- [
28
- 1,
29
- c,
30
- () => ({
31
- u: () => undefined,
32
- p: () => undefined,
33
- }),
34
- ] as [number, number, (o: number) => FormatFn],
35
- c: (c: number) =>
36
- [
37
- c,
38
- 1,
39
- (o: number) => ({
40
- u: (v: DataView) => str(v, o, 1),
41
- p: (v: DataView, s: string) => {
42
- rts(v, o, 1, s)
43
- },
44
- }),
45
- ] as [number, number, (o: number) => FormatFn],
46
- "?": (c: number) =>
47
- [
48
- c,
49
- 1,
50
- (o: number) => ({
51
- u: (v: DataView) => Boolean(v.getUint8(o)),
52
- p: (v: DataView, B: boolean) => {
53
- v.setUint8(o, B ? 1 : 0)
54
- },
55
- }),
56
- ] as [number, number, (o: number) => FormatFn],
57
- b: (c: number) =>
58
- [
59
- c,
60
- 1,
61
- (o: number) => ({
62
- u: (v: DataView) => v.getInt8(o),
63
- p: (v: DataView, b: number) => {
64
- v.setInt8(o, b)
65
- },
66
- }),
67
- ] as [number, number, (o: number) => FormatFn],
68
- B: (c: number) =>
69
- [
70
- c,
71
- 1,
72
- (o: number) => ({
73
- u: (v: DataView) => v.getUint8(o),
74
- p: (v: DataView, B: number) => {
75
- v.setUint8(o, B)
76
- },
77
- }),
78
- ] as [number, number, (o: number) => FormatFn],
79
- h: (c: number) =>
80
- [
81
- c,
82
- 2,
83
- (o: number) => ({
84
- u: (v: DataView) => v.getInt16(o, le),
85
- p: (v: DataView, h: number) => {
86
- v.setInt16(o, h, le)
87
- },
88
- }),
89
- ] as [number, number, (o: number) => FormatFn],
90
- H: (c: number) =>
91
- [
92
- c,
93
- 2,
94
- (o: number) => ({
95
- u: (v: DataView) => v.getUint16(o, le),
96
- p: (v: DataView, H: number) => {
97
- v.setUint16(o, H, le)
98
- },
99
- }),
100
- ] as [number, number, (o: number) => FormatFn],
101
- i: (c: number) =>
102
- [
103
- c,
104
- 4,
105
- (o: number) => ({
106
- u: (v: DataView) => v.getInt32(o, le),
107
- p: (v: DataView, i: number) => {
108
- v.setInt32(o, i, le)
109
- },
110
- }),
111
- ] as [number, number, (o: number) => FormatFn],
112
- I: (c: number) =>
113
- [
114
- c,
115
- 4,
116
- (o: number) => ({
117
- u: (v: DataView) => v.getUint32(o, le),
118
- p: (v: DataView, I: number) => {
119
- v.setUint32(o, I, le)
120
- },
121
- }),
122
- ] as [number, number, (o: number) => FormatFn],
123
- f: (c: number) =>
124
- [
125
- c,
126
- 4,
127
- (o: number) => ({
128
- u: (v: DataView) => v.getFloat32(o, le),
129
- p: (v: DataView, f: number) => {
130
- v.setFloat32(o, f, le)
131
- },
132
- }),
133
- ] as [number, number, (o: number) => FormatFn],
134
- d: (c: number) =>
135
- [
136
- c,
137
- 8,
138
- (o: number) => ({
139
- u: (v: DataView) => v.getFloat64(o, le),
140
- p: (v: DataView, d: number) => {
141
- v.setFloat64(o, d, le)
142
- },
143
- }),
144
- ] as [number, number, (o: number) => FormatFn],
145
- s: (c: number) =>
146
- [
147
- 1,
148
- c,
149
- (o: number) => ({
150
- u: (v: DataView) => str(v, o, c),
151
- p: (v: DataView, s: string) => {
152
- rts(v, o, c, s.slice(0, c))
153
- },
154
- }),
155
- ] as [number, number, (o: number) => FormatFn],
156
- p: (c: number) =>
157
- [
158
- 1,
159
- c,
160
- (o: number) => ({
161
- u: (v: DataView) => pst(v, o, c),
162
- p: (v: DataView, s: string) => {
163
- tsp(v, o, c, s.slice(0, c - 1))
164
- },
165
- }),
166
- ] as [number, number, (o: number) => FormatFn],
167
- })
168
-
169
- const errbuf: RangeError = new RangeError("Structure larger than remaining buffer")
170
- const errval: RangeError = new RangeError("Not enough values for structure")
171
-
172
- export default function struct(format: string) {
173
- const fns: FormatFn[] = []
174
- let size = 0
175
- let m: RegExpExecArray | null = rechk.exec(format)
176
-
177
- if (!m) {
178
- throw new RangeError("Invalid format string")
179
- }
180
-
181
- const t: LUT = lut("<" === m[1])
182
- const lu = (n: string, c: string): [number, number, (o: number) => FormatFn] => t[c](n ? parseInt(n, 10) : 1)
183
-
184
- while ((m = refmt.exec(format))) {
185
- ;((r: number, s: number, f: (o: number) => FormatFn) => {
186
- for (let i = 0; i < r; ++i, size += s) {
187
- if (f) {
188
- fns.push(f(size))
189
- }
190
- }
191
- })(...lu(...(m.slice(1) as [string, string])))
192
- }
193
-
194
- const unpack_from = (arrb: ArrayBuffer, offs: number): unknown[] => {
195
- if (arrb.byteLength < (offs | 0) + size) {
196
- throw errbuf
197
- }
198
- const v = new DataView(arrb, offs | 0)
199
- return fns.map((f) => f.u(v))
200
- }
201
-
202
- const pack_into = (arrb: ArrayBuffer, offs: number, ...values: unknown[]): void => {
203
- if (values.length < fns.length) {
204
- throw errval
205
- }
206
- if (arrb.byteLength < offs + size) {
207
- throw errbuf
208
- }
209
- const v = new DataView(arrb, offs)
210
- new Uint8Array(arrb, offs, size).fill(0)
211
- fns.forEach((f, i) => {
212
- f.p(v, values[i])
213
- })
214
- }
215
-
216
- const pack = (...values: unknown[]): ArrayBuffer => {
217
- const b = new ArrayBuffer(size)
218
- pack_into(b, 0, ...values)
219
- return b
220
- }
221
-
222
- const unpack = (arrb: ArrayBuffer): unknown[] => unpack_from(arrb, 0)
223
-
224
- function* iter_unpack(arrb: ArrayBuffer): IterableIterator<unknown[]> {
225
- for (let offs = 0; offs + size <= arrb.byteLength; offs += size) {
226
- yield unpack_from(arrb, offs)
227
- }
228
- }
229
-
230
- return Object.freeze({
231
- unpack,
232
- pack,
233
- unpack_from,
234
- pack_into,
235
- iter_unpack,
236
- format,
237
- size,
238
- })
239
- }