teyyare 0.2.0 → 0.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teyyare",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "High-performance resumable file transfer engine and CLI built on muttafa and raptiye",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Zero-copy generation framing and buffer extraction utilities.
3
+ * Ensures complete compatibility across all versions of Muttafa and Raptiye.
4
+ */
5
+
6
+ /**
7
+ * Extract scatter/gather buffers [metaBuf, payloadBuf] from any Muttafa generation.
8
+ * @param {object} batch
9
+ * @returns {Uint8Array[]}
10
+ */
11
+ export function extractGenerationBuffers(batch) {
12
+ if (typeof batch.buffers === 'function') {
13
+ return batch.buffers();
14
+ }
15
+
16
+ const store = batch.store || batch;
17
+ if (typeof store.buffers === 'function') {
18
+ return store.buffers();
19
+ }
20
+
21
+ // Direct extraction from ArenaStore
22
+ const rawBuf = store._allocator?.rawBuffer;
23
+ const payload = rawBuf ? rawBuf.subarray(0, store._allocator.usedBytes) : new Uint8Array(0);
24
+ const metaBuf = new Uint8Array(store.length * 25);
25
+ const view = new DataView(metaBuf.buffer, metaBuf.byteOffset, metaBuf.byteLength);
26
+
27
+ let off = 0;
28
+ const chunks = store._chunks || [];
29
+ for (let c = 0; c < chunks.length; c++) {
30
+ const chunk = chunks[c];
31
+ for (let i = 0; i < chunk.count; i++) {
32
+ view.setFloat64(off, chunk.seqs[i], true);
33
+ view.setUint8(off + 8, chunk.types[i]);
34
+ view.setUint32(off + 9, chunk.keyOffsets[i], true);
35
+ view.setUint32(off + 13, chunk.keyLengths[i], true);
36
+ view.setUint32(off + 17, chunk.valOffsets[i], true);
37
+ view.setUint32(off + 21, chunk.valLengths[i], true);
38
+ off += 25;
39
+ }
40
+ }
41
+
42
+ return [metaBuf, payload];
43
+ }
44
+
45
+ /**
46
+ * Fast zero-copy generator / unpacker for received generation buffers.
47
+ * @param {Uint8Array} metaBuf
48
+ * @param {Uint8Array} payloadBuf
49
+ * @returns {Array<{ seq: number, type: number, key: Uint8Array, val: Uint8Array }>}
50
+ */
51
+ export function unpackGeneration(metaBuf, payloadBuf) {
52
+ const entryCount = (metaBuf.byteLength / 25) | 0;
53
+ const view = new DataView(metaBuf.buffer, metaBuf.byteOffset, metaBuf.byteLength);
54
+ const entries = new Array(entryCount);
55
+
56
+ let off = 0;
57
+ for (let i = 0; i < entryCount; i++) {
58
+ const seq = view.getFloat64(off, true);
59
+ const type = view.getUint8(off + 8);
60
+ const keyOffset = view.getUint32(off + 9, true);
61
+ const keyLength = view.getUint32(off + 13, true);
62
+ const valOffset = view.getUint32(off + 17, true);
63
+ const valLength = view.getUint32(off + 21, true);
64
+ off += 25;
65
+
66
+ entries[i] = {
67
+ seq,
68
+ type,
69
+ key: payloadBuf.subarray(keyOffset, keyOffset + keyLength),
70
+ val: payloadBuf.subarray(valOffset, valOffset + valLength)
71
+ };
72
+ }
73
+
74
+ return entries;
75
+ }
76
+
77
+ /**
78
+ * Pack a single-mutation batch directly into scatter/gather wire buffers.
79
+ * @param {number} generationId
80
+ * @param {number} opcode
81
+ * @param {Uint8Array} key
82
+ * @param {Uint8Array} [val]
83
+ * @returns {object}
84
+ */
85
+ export function packSingleMutationBatch(generationId, opcode, key, val = new Uint8Array(0)) {
86
+ const metaBuf = new Uint8Array(25);
87
+ const view = new DataView(metaBuf.buffer, metaBuf.byteOffset, 25);
88
+ view.setFloat64(0, 1, true); // seq
89
+ view.setUint8(8, opcode);
90
+ view.setUint32(9, 0, true); // keyOffset
91
+ view.setUint32(13, key.byteLength, true); // keyLength
92
+ view.setUint32(17, key.byteLength, true); // valOffset
93
+ view.setUint32(21, val.byteLength, true); // valLength
94
+
95
+ const payloadBuf = new Uint8Array(key.byteLength + val.byteLength);
96
+ payloadBuf.set(key, 0);
97
+ payloadBuf.set(val, key.byteLength);
98
+
99
+ return {
100
+ generationId,
101
+ firstSequence: 1,
102
+ lastSequence: 1,
103
+ entryCount: 1,
104
+ byteLength: metaBuf.byteLength + payloadBuf.byteLength,
105
+ buffers: [metaBuf, payloadBuf]
106
+ };
107
+ }
package/src/index.js CHANGED
@@ -4,7 +4,6 @@
4
4
 
5
5
  import path from 'node:path';
6
6
  import { existsSync } from 'node:fs';
7
- import { MutableBuffer, ArenaStore, BumpAllocator, FrozenArenaStore } from 'muttafa';
8
7
  import { TCPTransport, ReplicationPipeline } from 'raptiye';
9
8
  import { SenderPipeline } from './sender/pipeline.js';
10
9
  import { ReceiverPipeline } from './receiver/pipeline.js';
@@ -12,6 +11,7 @@ import { TransferManifest } from './manifest/manifest.js';
12
11
  import { FileResumeState } from './resume/state.js';
13
12
  import { MetricsCollector } from './metrics/collector.js';
14
13
  import { ProgressReporter } from './cli/progress.js';
14
+ import { packSingleMutationBatch, unpackGeneration } from './core/generation.js';
15
15
  import {
16
16
  Opcodes,
17
17
  packChunkMeta,
@@ -39,22 +39,9 @@ export {
39
39
  };
40
40
 
41
41
  async function submitSingleMutation(replication, opcode, key, value = new Uint8Array(0)) {
42
- const store = new ArenaStore({ allocator: new BumpAllocator(16 * 1024) });
43
- const mutable = new MutableBuffer(Date.now() & 0x7fffffff, store);
44
- mutable.append(1, opcode, key, value);
45
- const frozen = mutable.freeze();
46
- frozen.markFlushing();
47
- const receipt = await replication.submitBatch({
48
- generationId: frozen.generation,
49
- firstSequence: frozen.firstSequence,
50
- lastSequence: frozen.lastSequence,
51
- entryCount: frozen.entryCount,
52
- byteLength: frozen.byteLength,
53
- buffers: frozen.buffers()
54
- });
42
+ const batch = packSingleMutationBatch(Date.now() & 0x7fffffff, opcode, key, value);
43
+ const receipt = await replication.submitBatch(batch);
55
44
  await receipt.delivered();
56
- frozen.markFlushed();
57
- frozen.release();
58
45
  }
59
46
 
60
47
  /**
@@ -102,9 +89,9 @@ export async function serve({
102
89
  replication.onBatch(async (msg) => {
103
90
  if (!msg.buffers || msg.buffers.length < 2) return;
104
91
  const [metaBuf, payloadBuf] = msg.buffers;
105
- const store = FrozenArenaStore.fromBuffers(metaBuf, payloadBuf);
106
- if (store.length > 0 && store.getType(0) === Opcodes.PULL_REQUEST) {
107
- const { remotePath } = unpackPullRequest(store.getKey(0));
92
+ const entries = unpackGeneration(metaBuf, payloadBuf);
93
+ if (entries.length > 0 && entries[0].type === Opcodes.PULL_REQUEST) {
94
+ const { remotePath } = unpackPullRequest(entries[0].key);
108
95
  let resolved = path.isAbsolute(remotePath) ? remotePath : path.resolve(destinationDir, remotePath);
109
96
  if (!existsSync(resolved)) {
110
97
  const alt = path.resolve(destinationDir, remotePath);
@@ -8,9 +8,9 @@ import fs from 'node:fs/promises';
8
8
  import { existsSync, createReadStream } from 'node:fs';
9
9
  import path from 'node:path';
10
10
  import crypto from 'node:crypto';
11
- import { FrozenArenaStore } from 'muttafa';
12
11
  import { crc32 } from 'raptiye';
13
12
  import { Opcodes, unpackChunkMeta, unpackFileBegin, unpackTransferBegin, unpackFileEnd, unpackPullError } from '../core/protocol.js';
13
+ import { unpackGeneration } from '../core/generation.js';
14
14
  import { FileResumeState } from '../resume/state.js';
15
15
 
16
16
  export class ReceiverPipeline {
@@ -70,14 +70,12 @@ export class ReceiverPipeline {
70
70
  this.metrics.generationBytes += (metaBuf.byteLength + payloadBuf.byteLength);
71
71
  }
72
72
 
73
- // Zero-copy reconstruction of Muttafa generation
74
- const store = FrozenArenaStore.fromBuffers(metaBuf, payloadBuf);
75
- const count = store.length;
73
+ // Zero-copy unpacking of generation entries
74
+ const entries = unpackGeneration(metaBuf, payloadBuf);
75
+ const count = entries.length;
76
76
 
77
77
  for (let i = 0; i < count; i++) {
78
- const type = store.getType(i);
79
- const key = store.getKey(i);
80
- const val = store.getValue(i);
78
+ const { type, key, val } = entries[i];
81
79
 
82
80
  if (this.metrics) {
83
81
  this.metrics.mutations++;
@@ -17,6 +17,8 @@ import {
17
17
  packTransferBegin,
18
18
  packTransferEnd
19
19
  } from '../core/protocol.js';
20
+ import { extractGenerationBuffers } from '../core/generation.js';
21
+ import { FileResumeState } from '../resume/state.js';
20
22
 
21
23
  class RaptiyeSink extends Sink {
22
24
  /**
@@ -36,20 +38,23 @@ class RaptiyeSink extends Sink {
36
38
  * @param {import('muttafa').ImmutableBuffer} batch
37
39
  */
38
40
  async flush(batch) {
39
- const buffers = batch.buffers();
41
+ const buffers = extractGenerationBuffers(batch);
40
42
  const t0 = Date.now();
43
+ const generationId = batch.generationId || batch.generation || 1;
44
+ const entryCount = batch.entryCount || batch.store?.length || 1;
45
+ const byteLength = batch.byteLength || (buffers[0].byteLength + buffers[1].byteLength);
41
46
 
42
47
  if (this.metrics) {
43
48
  this.metrics.generations++;
44
- this.metrics.generationBytes += batch.byteLength;
49
+ this.metrics.generationBytes += byteLength;
45
50
  }
46
51
 
47
52
  const receipt = await this.replication.submitBatch({
48
- generationId: batch.generationId,
49
- firstSequence: batch.firstSequence,
50
- lastSequence: batch.lastSequence,
51
- entryCount: batch.entryCount,
52
- byteLength: batch.byteLength,
53
+ generationId,
54
+ firstSequence: batch.firstSequence || 1,
55
+ lastSequence: batch.lastSequence || 1,
56
+ entryCount,
57
+ byteLength,
53
58
  buffers
54
59
  });
55
60