* Serializes DataPoint or OHLCDataPoint arrays to ArrayBuffer for zero-copy transfer. * * Uses Float32Array for GPU compatibility (GPUs use Float32 precision). * * @param points - Array of data points to serialize * @returns Tuple of [ArrayBuffer, stride] where stride is 8 for DataPoint, 20 f
( points: ReadonlyArray<DataPoint> | ReadonlyArray<OHLCDataPoint> )
| 98 | * @returns Tuple of [ArrayBuffer, stride] where stride is 8 for DataPoint, 20 for OHLCDataPoint |
| 99 | */ |
| 100 | function serializeDataPoints( |
| 101 | points: ReadonlyArray<DataPoint> | ReadonlyArray<OHLCDataPoint> |
| 102 | ): [ArrayBuffer, number] { |
| 103 | if (points.length === 0) { |
| 104 | return [new ArrayBuffer(0), 0]; |
| 105 | } |
| 106 | |
| 107 | // Detect point type from first element |
| 108 | const firstPoint = points[0]!; |
| 109 | const isOHLC = Array.isArray(firstPoint) |
| 110 | ? firstPoint.length === 5 |
| 111 | : 'timestamp' in firstPoint && 'open' in firstPoint; |
| 112 | |
| 113 | if (isOHLC) { |
| 114 | // OHLCDataPoint: [timestamp, open, close, low, high] (5 × 4 bytes = 20 bytes) |
| 115 | const stride = 20; |
| 116 | const buffer = new ArrayBuffer(points.length * stride); |
| 117 | const view = new Float32Array(buffer); |
| 118 | |
| 119 | for (let i = 0, offset = 0; i < points.length; i++, offset += 5) { |
| 120 | const p = points[i] as OHLCDataPoint; |
| 121 | if (Array.isArray(p)) { |
| 122 | view[offset] = p[0]; // timestamp |
| 123 | view[offset + 1] = p[1]; // open |
| 124 | view[offset + 2] = p[2]; // close |
| 125 | view[offset + 3] = p[3]; // low |
| 126 | view[offset + 4] = p[4]; // high |
| 127 | } else { |
| 128 | // Type assertion: we know it's OHLCDataPointObject at this point |
| 129 | const ohlcObj = p as import('../config/types').OHLCDataPointObject; |
| 130 | view[offset] = ohlcObj.timestamp; |
| 131 | view[offset + 1] = ohlcObj.open; |
| 132 | view[offset + 2] = ohlcObj.close; |
| 133 | view[offset + 3] = ohlcObj.low; |
| 134 | view[offset + 4] = ohlcObj.high; |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | return [buffer, stride]; |
| 139 | } else { |
| 140 | // DataPoint: [x, y] pairs (2 × 4 bytes = 8 bytes) |
| 141 | const stride = 8; |
| 142 | const buffer = new ArrayBuffer(points.length * stride); |
| 143 | const view = new Float32Array(buffer); |
| 144 | |
| 145 | for (let i = 0, offset = 0; i < points.length; i++, offset += 2) { |
| 146 | const p = points[i] as DataPoint; |
| 147 | if (Array.isArray(p)) { |
| 148 | view[offset] = p[0]; // x |
| 149 | view[offset + 1] = p[1]; // y |
| 150 | } else { |
| 151 | // Type assertion: we know it's { x, y } at this point |
| 152 | const dataObj = p as { x: number; y: number }; |
| 153 | view[offset] = dataObj.x; |
| 154 | view[offset + 1] = dataObj.y; |
| 155 | } |
| 156 | } |
| 157 |