Skip to content

Buffer ​

The Buffer class represents a resource that stores homogeneous quantities (tokens). Unlike Store which holds distinct objects, Buffer tracks a numerical quantity of identical tokens. Perfect for modeling fuel tanks, money, raw materials, bandwidth, inventory levels, etc.

Constructor ​

typescript
new Buffer(sim: Simulation, capacity: number, options?: BufferOptions)

Parameters:

  • sim - Simulation instance
  • capacity - Maximum capacity of the buffer (must be > 0)
  • options - Optional configuration object
    • name - Buffer name for statistics and debugging
    • initialLevel - Starting quantity in buffer (default: 0)

Example:

typescript
import { Simulation, Buffer } from 'discrete-sim';

const sim = new Simulation();

// Empty fuel tank with 1000 gallon capacity
const fuelTank = new Buffer(sim, 1000, {
  name: 'Fuel Tank',
  initialLevel: 0
});

// Bank account starting with $10,000
const account = new Buffer(sim, 1000000, {
  name: 'Account',
  initialLevel: 10000
});

Validation:

  • capacity must be > 0 and finite
  • initialLevel must be >= 0 and <= capacity
  • Throws ValidationError for invalid parameters

Properties ​

capacity ​

typescript
buffer.capacity: number

Maximum capacity of the buffer.

Example:

typescript
const tank = new Buffer(sim, 1000);
console.log(tank.capacity);  // 1000

Read-only - Cannot be changed after creation.

level ​

typescript
buffer.level: number

Current quantity of tokens in the buffer.

Example:

typescript
console.log(`Tank has ${fuelTank.level} gallons`);

Range: 0 to capacity

available ​

typescript
buffer.available: number

Available space remaining in the buffer.

Example:

typescript
if (tank.available >= 100) {
  console.log('Room for 100 more gallons');
}

Relation: available = capacity - level

putQueueLength ​

typescript
buffer.putQueueLength: number

Number of processes waiting to put tokens into the buffer.

Example:

typescript
console.log(`${tank.putQueueLength} trucks waiting to deliver`);

getQueueLength ​

typescript
buffer.getQueueLength: number

Number of processes waiting to get tokens from the buffer.

Example:

typescript
console.log(`${tank.getQueueLength} customers waiting for fuel`);

name ​

typescript
buffer.name: string

Name of the buffer (for statistics and debugging).

stats ​

typescript
buffer.stats: BufferStatistics

Comprehensive statistics for this buffer.

Returns:

typescript
interface BufferStatistics {
  totalPuts: number;           // Total put operations
  totalGets: number;           // Total get operations
  totalAmountPut: number;      // Total tokens added
  totalAmountGot: number;      // Total tokens removed
  averagePutWaitTime: number;  // Average time waiting to put
  averageGetWaitTime: number;  // Average time waiting to get
  averageLevel: number;        // Time-weighted average level
  averagePutQueueLength: number;
  averageGetQueueLength: number;
}

Example:

typescript
const stats = fuelTank.stats;
console.log(`Average fuel level: ${stats.averageLevel.toFixed(2)} gallons`);
console.log(`Total fuel delivered: ${stats.totalAmountPut} gallons`);
console.log(`Total fuel sold: ${stats.totalAmountGot} gallons`);

Methods ​

put() ​

typescript
yield buffer.put(amount: number): void

Request to put tokens into the buffer. Waits if insufficient space.

Parameters:

  • amount - Quantity of tokens to add (must be > 0 and <= capacity)

Returns: Token to yield in generator function

Example:

typescript
function* deliverFuel(amount: number) {
  console.log(`Delivering ${amount} gallons`);

  yield fuelTank.put(amount);

  console.log(`Delivery complete at ${sim.now}`);
}

Behavior:

  • If space available: Tokens added immediately, process continues
  • If insufficient space: Process queued until space becomes available
  • Must use yield (not yield*) when putting
  • Throws error if amount > capacity

Queue Discipline: FIFO (First-In-First-Out)

get() ​

typescript
yield buffer.get(amount: number): void

Request to get tokens from the buffer. Waits if insufficient tokens.

Parameters:

  • amount - Quantity of tokens to remove (must be > 0)

Returns: Token to yield in generator function

Example:

typescript
function* refuelCar() {
  console.log('Refueling car');

  yield fuelTank.get(15);  // Get 15 gallons

  console.log(`Refueling complete at ${sim.now}`);
}

Behavior:

  • If tokens available: Tokens removed immediately, process continues
  • If insufficient tokens: Process queued until tokens become available
  • Must use yield (not yield*) when getting

Queue Discipline: FIFO (First-In-First-Out)

Examples ​

Example: Gas Station Fuel Tank ​

typescript
import { Simulation, Buffer, timeout } from 'discrete-sim';

const sim = new Simulation();
const fuelTank = new Buffer(sim, 1000, {
  name: 'Fuel Tank',
  initialLevel: 500  // Start half full
});

// Customer refueling
function* customer(id: number, amount: number) {
  console.log(`[${sim.now}] Customer ${id} arrives, needs ${amount} gallons`);

  // Get fuel from tank
  yield fuelTank.get(amount);

  console.log(`[${sim.now}] Customer ${id} refueling...`);
  yield* timeout(5);  // Refueling time

  console.log(`[${sim.now}] Customer ${id} done`);
}

// Tanker delivery
function* tankerDelivery(amount: number) {
  while (true) {
    yield* timeout(120);  // Delivery every 120 time units

    console.log(`[${sim.now}] Tanker delivering ${amount} gallons`);
    yield fuelTank.put(amount);

    console.log(`[${sim.now}] Delivery complete. Tank level: ${fuelTank.level}`);
  }
}

// Start simulation
sim.process(() => tankerDelivery(800));

// Customers arrive
sim.process(() => customer(1, 15));
sim.process(() => customer(2, 20));
sim.schedule(10, () => sim.process(() => customer(3, 25)));

sim.run(200);

// Statistics
const stats = fuelTank.stats;
console.log(`\nFuel Tank Statistics:`);
console.log(`Average level: ${stats.averageLevel.toFixed(2)} gallons`);
console.log(`Total delivered: ${stats.totalAmountPut} gallons`);
console.log(`Total sold: ${stats.totalAmountGot} gallons`);
console.log(`Average customer wait: ${stats.averageGetWaitTime.toFixed(2)}`);

Example: Bank Account ​

typescript
function* deposit(amount: number) {
  console.log(`[${sim.now}] Depositing $${amount}`);
  yield account.put(amount);
  console.log(`[${sim.now}] Deposit complete. Balance: $${account.level}`);
}

function* withdrawal(amount: number) {
  console.log(`[${sim.now}] Withdrawing $${amount}`);

  // Check if funds available (optional)
  if (account.level < amount) {
    console.log(`[${sim.now}] Waiting for funds...`);
  }

  yield account.get(amount);
  console.log(`[${sim.now}] Withdrawal complete. Balance: $${account.level}`);
}

const account = new Buffer(sim, 1000000, {
  name: 'Account',
  initialLevel: 1000
});

sim.process(() => withdrawal(500));
sim.process(() => withdrawal(600));  // Must wait for deposit
sim.schedule(10, () => sim.process(() => deposit(500)));

sim.run();

Example: Production Inventory ​

typescript
function* producer(rng: Random) {
  while (sim.now < 480) {  // 8 hour shift
    // Produce items
    const batchSize = 50;
    yield* timeout(rng.exponential(20));

    console.log(`[${sim.now}] Produced ${batchSize} units`);
    yield inventory.put(batchSize);
  }
}

function* consumer(id: number, rng: Random) {
  while (sim.now < 480) {
    yield* timeout(rng.exponential(30));

    const needed = 20;
    console.log(`[${sim.now}] Consumer ${id} needs ${needed} units`);

    yield inventory.get(needed);
    console.log(`[${sim.now}] Consumer ${id} got ${needed} units`);
  }
}

const inventory = new Buffer(sim, 500, {
  name: 'Inventory',
  initialLevel: 100
});

const rng = new Random(42);

sim.process(() => producer(rng));
sim.process(() => consumer(1, rng));
sim.process(() => consumer(2, rng));

sim.run(480);

console.log(`Final inventory: ${inventory.level} units`);
console.log(`Average inventory: ${inventory.stats.averageLevel.toFixed(2)} units`);

Common Patterns ​

Pattern: Bounded Production-Consumption ​

typescript
// Producer
function* produce(rate: number) {
  while (true) {
    yield* timeout(1 / rate);

    const amount = 10;
    yield buffer.put(amount);  // Blocks if buffer full
  }
}

// Consumer
function* consume(rate: number) {
  while (true) {
    yield* timeout(1 / rate);

    const amount = 10;
    yield buffer.get(amount);  // Blocks if buffer empty
  }
}

Pattern: Check Before Request ​

typescript
function* smartConsumer(needed: number) {
  // Check current level
  if (buffer.level < needed) {
    console.log(`Waiting for ${needed - buffer.level} more units`);
  }

  yield buffer.get(needed);
}

Pattern: Coordinated Buffer and Resource ​

typescript
function* fillTruck(truck: Resource, fuel: Buffer) {
  yield truck.request();  // Get truck
  yield fuel.get(100);    // Get fuel

  yield* timeout(10);     // Fill truck

  truck.release();
  // Fuel is consumed, not returned
}

Best Practices ​

1. Set Appropriate Capacity ​

typescript
// Good - realistic capacity
const tank = new Buffer(sim, 1000, { name: 'Tank' });

// Avoid - infinite capacity (use large number instead)
const unlimited = new Buffer(sim, Number.MAX_SAFE_INTEGER);

2. Use Initial Levels for Steady State ​

typescript
// Start with typical operating level
const inventory = new Buffer(sim, 1000, {
  name: 'Inventory',
  initialLevel: 500  // Half full
});

3. Monitor Queue Lengths ​

typescript
function* monitor() {
  while (true) {
    if (buffer.putQueueLength > 5) {
      console.log('Warning: Many producers waiting');
    }
    if (buffer.getQueueLength > 5) {
      console.log('Warning: Many consumers waiting');
    }
    yield* timeout(10);
  }
}

4. Track Statistics ​

typescript
// Always name buffers for statistics
const tank = new Buffer(sim, 1000, { name: 'Fuel Tank' });

// After simulation
const stats = tank.stats;
console.log(`Utilization: ${(stats.averageLevel / tank.capacity * 100).toFixed(1)}%`);

Differences from Store ​

FeatureBufferStore
ContentsHomogeneous tokens (numbers)Distinct objects
Operationsput(amount), get(amount)put(item), get(filter?)
RetrievalBy quantityFIFO or filtered
Use CasesFuel, money, materialsParts, vehicles, jobs
Level TrackingNumeric levelItem count

See Also ​

Released under the MIT License.