Lab
Dynamic array
// ---- Checker plumbing: you don't need to edit this. ----------------------
/**
* The track's buffer: a fixed number of slots, read and written only through
* get, set and take. The checker counts moves here: every set writes one cue
* into a slot, whether it is a copy, a shift or a new cue. take empties a slot
* and is not a move.
*/
class Slots {
static moves = 0;
private readonly cells: (string | undefined)[];
constructor(size: number) {
this.cells = new Array<string | undefined>(size).fill(undefined);
}
/** For the checker: a buffer of size slots already holding cues, not counted. */
static of(cues: string[], size: number): Slots {
const slots = new Slots(size);
slots.cells.splice(0, cues.length, ...cues);
return slots;
}
/** For the checker: the first count slots, read without a loop. */
peek(count: number): (string | undefined)[] {
return this.cells.slice(0, count);
}
get size(): number {
return this.cells.length;
}
get(index: number): string | undefined {
return this.cells[index];
}
set(index: number, cue: string | undefined): void {
if (!(index >= 0 && index < this.cells.length)) throw new RangeError(`no slot ${index} in a buffer of ${this.cells.length}`);
this.cells[index] = cue;
Slots.moves++;
}
take(index: number): string | undefined {
const cue = this.cells[index];
this.cells[index] = undefined;
return cue;
}
}
// ---- End of checker plumbing. --------------------------------------------
/**
* A caption track: the lesson's growing array, holding caption cues. Its
* buffer is a Slots (above), which counts every cue written into it.
*/
class CaptionTrack {
slots = new Slots(4);
length = 0;
get capacity(): number {
return this.slots.size;
}
/** Copies the cues into a new buffer of the given size. */
private resize(capacity: number): void {
const newSlots = new Slots(capacity);
for (let i = 0; i < this.length; i++) newSlots.set(i, this.slots.get(i));
this.slots = newSlots;
}
append(cue: string): void {
if (this.length === this.capacity) this.resize(this.capacity * 2);
this.slots.set(this.length, cue);
this.length++;
}
get(index: number): string | undefined {
return index >= 0 && index < this.length ? this.slots.get(index) : undefined;
}
remove(index: number): string | undefined {
if (index < 0 || index >= this.length) return undefined;
const removed = this.slots.take(index);
// Keep the order: move every later cue one place earlier.
for (let i = index + 1; i < this.length; i++) this.slots.set(i - 1, this.slots.take(i));
this.length--;
return removed;
}
/** Puts a cue at index, moving the cues from there one place later. */
insert(index: number, cue: string): boolean {
// TODO: a missed caption belongs between two others.
return false;
}
}