or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

buffer-writing.mdindex.mdindexed-reading.mdindexing.mditeration.mdreading.mdwriting.md
tile.json

buffer-writing.mddocs/

0

# Buffer Writing (Node.js)

1

2

Synchronous buffer-based CAR writing for performance-critical scenarios where the final CAR size is known in advance. Provides direct buffer manipulation with precise memory control and no async overhead. **This functionality is only available in Node.js.**

3

4

## Capabilities

5

6

### CarBufferWriter Class

7

8

Synchronous CAR writer that writes directly to a pre-allocated buffer.

9

10

```typescript { .api }

11

/**

12

* Synchronous CAR writer for pre-allocated buffers

13

* Provides precise memory control and no async overhead

14

* Suitable when final CAR size is known in advance

15

* Node.js only - not available in browser environments

16

*/

17

interface CarBufferWriter {

18

/** Buffer being written to */

19

readonly bytes: Uint8Array;

20

/** Current root CIDs */

21

readonly roots: CID[];

22

23

/** Add a root CID to the header, optionally resizing header space */

24

addRoot(root: CID, options?: { resize?: boolean }): CarBufferWriter;

25

26

/** Write a block to the buffer, throws if insufficient space */

27

write(block: Block): CarBufferWriter;

28

29

/** Finalize CAR and return final byte array, optionally resizing */

30

close(options?: { resize?: boolean }): Uint8Array;

31

}

32

33

/**

34

* Options for creating a buffer writer

35

*/

36

interface CarBufferWriterOptions {

37

/** Initial root CIDs (default: []) */

38

roots?: CID[];

39

/** Byte offset in buffer to start writing (default: 0) */

40

byteOffset?: number;

41

/** Number of bytes to use from buffer (default: buffer.byteLength) */

42

byteLength?: number;

43

/** Reserved space for header (default: calculated from roots) */

44

headerSize?: number;

45

}

46

```

47

48

### Creating Buffer Writers

49

50

Create buffer writers with precise memory allocation.

51

52

```typescript { .api }

53

/**

54

* Create buffer writer from ArrayBuffer with optional configuration

55

* @param buffer - ArrayBuffer to write CAR data into

56

* @param options - Configuration options

57

* @returns CarBufferWriter instance

58

*/

59

function createWriter(buffer: ArrayBuffer, options?: CarBufferWriterOptions): CarBufferWriter;

60

```

61

62

**Usage Examples:**

63

64

```typescript

65

import * as CarBufferWriter from "@ipld/car/buffer-writer";

66

67

// Create buffer with estimated size

68

const estimatedSize = 1024 * 1024; // 1MB

69

const buffer = new ArrayBuffer(estimatedSize);

70

71

// Create writer with known roots

72

const writer = CarBufferWriter.createWriter(buffer, {

73

roots: [rootCid1, rootCid2],

74

headerSize: CarBufferWriter.headerLength({ roots: [rootCid1, rootCid2] })

75

});

76

77

// Write blocks

78

writer

79

.write({ cid: blockCid1, bytes: blockData1 })

80

.write({ cid: blockCid2, bytes: blockData2 })

81

.write({ cid: blockCid3, bytes: blockData3 });

82

83

// Finalize and get result

84

const carBytes = writer.close();

85

console.log(`Created CAR: ${carBytes.length} bytes`);

86

```

87

88

### Size Calculation Functions

89

90

Calculate buffer sizes and header requirements.

91

92

```typescript { .api }

93

/**

94

* Calculate bytes needed for storing a block in CAR format

95

* @param block - Block to calculate size for

96

* @returns Number of bytes needed

97

*/

98

function blockLength(block: Block): number;

99

100

/**

101

* Calculate header length for given roots

102

* @param options - Object containing roots array

103

* @returns Header length in bytes

104

*/

105

function headerLength(options: { roots: CID[] }): number;

106

107

/**

108

* Calculate header length from root CID byte lengths

109

* @param rootLengths - Array of root CID byte lengths

110

* @returns Header length in bytes

111

*/

112

function calculateHeaderLength(rootLengths: number[]): number;

113

114

/**

115

* Estimate header length for a given number of roots

116

* @param rootCount - Number of root CIDs

117

* @param rootByteLength - Expected bytes per root CID (default: 36)

118

* @returns Estimated header length in bytes

119

*/

120

function estimateHeaderLength(rootCount: number, rootByteLength?: number): number;

121

```

122

123

**Usage Examples:**

124

125

```typescript

126

import * as CarBufferWriter from "@ipld/car/buffer-writer";

127

128

// Calculate exact buffer size needed

129

const blocks = [

130

{ cid: cid1, bytes: data1 },

131

{ cid: cid2, bytes: data2 },

132

{ cid: cid3, bytes: data3 }

133

];

134

135

const roots = [cid1];

136

137

// Calculate total size needed

138

const headerSize = CarBufferWriter.headerLength({ roots });

139

const blockSizes = blocks.reduce(

140

(total, block) => total + CarBufferWriter.blockLength(block),

141

0

142

);

143

const totalSize = headerSize + blockSizes;

144

145

// Create perfectly sized buffer

146

const buffer = new ArrayBuffer(totalSize);

147

const writer = CarBufferWriter.createWriter(buffer, {

148

roots,

149

headerSize

150

});

151

152

// Write all blocks

153

for (const block of blocks) {

154

writer.write(block);

155

}

156

157

const carBytes = writer.close();

158

console.log(`Perfect fit: ${carBytes.length} === ${totalSize}`);

159

```

160

161

### Dynamic Header Resizing

162

163

Handle scenarios where root count changes during writing.

164

165

```typescript

166

import * as CarBufferWriter from "@ipld/car/buffer-writer";

167

168

// Start with estimated header size

169

const buffer = new ArrayBuffer(1024 * 1024);

170

const estimatedHeaderSize = CarBufferWriter.estimateHeaderLength(5); // Expect ~5 roots

171

172

const writer = CarBufferWriter.createWriter(buffer, {

173

headerSize: estimatedHeaderSize

174

});

175

176

// Add roots dynamically with resize option

177

try {

178

writer.addRoot(root1);

179

writer.addRoot(root2);

180

writer.addRoot(root3);

181

182

// This might exceed estimated header size

183

writer.addRoot(root4, { resize: true }); // Automatically resize header

184

writer.addRoot(root5, { resize: true });

185

186

} catch (error) {

187

if (error instanceof RangeError && error.message.includes('resize')) {

188

console.log('Use { resize: true } to expand header');

189

}

190

}

191

192

// Write blocks and close with resize

193

writer

194

.write(block1)

195

.write(block2);

196

197

const carBytes = writer.close({ resize: true }); // Resize to actual header size

198

```

199

200

### Advanced Buffer Management

201

202

Manage buffer allocation and reuse patterns.

203

204

```typescript

205

import * as CarBufferWriter from "@ipld/car/buffer-writer";

206

207

// Pattern 1: Buffer pools for repeated CAR creation

208

class CarBufferPool {

209

constructor(bufferSize = 1024 * 1024) {

210

this.bufferSize = bufferSize;

211

this.availableBuffers = [];

212

}

213

214

getBuffer() {

215

return this.availableBuffers.pop() || new ArrayBuffer(this.bufferSize);

216

}

217

218

returnBuffer(buffer) {

219

if (buffer.byteLength === this.bufferSize) {

220

this.availableBuffers.push(buffer);

221

}

222

}

223

224

async createCar(roots, blocks) {

225

const buffer = this.getBuffer();

226

227

try {

228

const writer = CarBufferWriter.createWriter(buffer, { roots });

229

230

for (const block of blocks) {

231

writer.write(block);

232

}

233

234

return writer.close({ resize: true });

235

} finally {

236

this.returnBuffer(buffer);

237

}

238

}

239

}

240

241

// Pattern 2: Precise allocation for known data sets

242

function createOptimalCar(roots, blocks) {

243

// Calculate exact size needed

244

const headerSize = CarBufferWriter.headerLength({ roots });

245

const dataSize = blocks.reduce(

246

(total, block) => total + CarBufferWriter.blockLength(block),

247

0

248

);

249

250

// Create exactly sized buffer

251

const buffer = new ArrayBuffer(headerSize + dataSize);

252

const writer = CarBufferWriter.createWriter(buffer, {

253

roots,

254

headerSize

255

});

256

257

// Write all data

258

blocks.forEach(block => writer.write(block));

259

260

// No resize needed - should be perfect fit

261

return writer.close();

262

}

263

```

264

265

### Error Handling

266

267

Handle buffer overflow and sizing errors.

268

269

```typescript

270

import * as CarBufferWriter from "@ipld/car/buffer-writer";

271

272

// Buffer capacity errors

273

const smallBuffer = new ArrayBuffer(1024); // Too small

274

const writer = CarBufferWriter.createWriter(smallBuffer);

275

276

try {

277

writer.write(largeBlock); // Might exceed buffer capacity

278

} catch (error) {

279

if (error instanceof RangeError && error.message.includes('capacity')) {

280

console.log('Buffer too small for block');

281

282

// Calculate needed size and recreate

283

const neededSize = CarBufferWriter.blockLength(largeBlock);

284

console.log(`Need at least ${neededSize} bytes`);

285

}

286

}

287

288

// Header sizing errors

289

try {

290

writer.addRoot(newRoot); // Might exceed header space

291

} catch (error) {

292

if (error.message.includes('resize')) {

293

console.log('Header space exceeded - use resize option');

294

writer.addRoot(newRoot, { resize: true });

295

}

296

}

297

298

// Close sizing errors

299

try {

300

const carBytes = writer.close();

301

} catch (error) {

302

if (error instanceof RangeError && error.message.includes('overestimated')) {

303

console.log('Header was overestimated - use resize option');

304

const carBytes = writer.close({ resize: true });

305

}

306

}

307

```

308

309

### Performance Optimization

310

311

Optimize for different performance scenarios.

312

313

```typescript

314

import * as CarBufferWriter from "@ipld/car/buffer-writer";

315

316

// High-throughput CAR creation

317

class HighThroughputCarWriter {

318

constructor() {

319

// Pre-calculate common header sizes

320

this.headerSizeCache = new Map();

321

322

// Reuse buffers for similar-sized outputs

323

this.bufferPool = new Map(); // size -> [buffers]

324

}

325

326

precalculateHeaderSize(rootCount) {

327

if (!this.headerSizeCache.has(rootCount)) {

328

const size = CarBufferWriter.estimateHeaderLength(rootCount);

329

this.headerSizeCache.set(rootCount, size);

330

}

331

return this.headerSizeCache.get(rootCount);

332

}

333

334

getPooledBuffer(size) {

335

const roundedSize = Math.ceil(size / 1024) * 1024; // Round to KB

336

const pool = this.bufferPool.get(roundedSize) || [];

337

338

if (pool.length > 0) {

339

return pool.pop();

340

}

341

342

return new ArrayBuffer(roundedSize);

343

}

344

345

returnBuffer(buffer) {

346

const size = buffer.byteLength;

347

const pool = this.bufferPool.get(size) || [];

348

349

if (pool.length < 10) { // Limit pool size

350

pool.push(buffer);

351

this.bufferPool.set(size, pool);

352

}

353

}

354

355

createCar(roots, blocks) {

356

// Fast path calculations

357

const headerSize = this.precalculateHeaderSize(roots.length);

358

const dataSize = blocks.reduce(

359

(total, block) => total + CarBufferWriter.blockLength(block),

360

0

361

);

362

363

const buffer = this.getPooledBuffer(headerSize + dataSize);

364

365

try {

366

const writer = CarBufferWriter.createWriter(buffer, {

367

roots,

368

headerSize

369

});

370

371

// Batch write blocks

372

blocks.forEach(block => writer.write(block));

373

374

return writer.close({ resize: true });

375

} finally {

376

this.returnBuffer(buffer);

377

}

378

}

379

}

380

```

381

382

## Performance Considerations

383

384

### Memory Efficiency

385

- **Pre-allocation**: Most efficient when buffer size is known in advance

386

- **No Async Overhead**: Synchronous operations avoid Promise/callback overhead

387

- **Buffer Reuse**: Reuse buffers across multiple CAR creations

388

389

### Speed Optimization

390

- **Batch Operations**: Group multiple `write()` calls when possible

391

- **Size Calculation**: Pre-calculate sizes to avoid resizing

392

- **Header Estimation**: Use accurate header size estimates

393

394

### Use Cases

395

- **High-Performance Scenarios**: When async overhead is problematic

396

- **Embedded Systems**: Precise memory control requirements

397

- **Batch Processing**: Creating many small CAR files efficiently

398

- **Memory-Constrained Environments**: When streaming isn't suitable