123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- import {createHash} from "crypto";
- import {Delta} from "./delta";
- export class CompositeDelta implements Delta {
- readonly deltas: Array<Delta>;
- readonly dependencies: Array<CompositeDelta>;
- readonly typedDependencies: Array<[CompositeDelta, string]>;
- readonly conflicts: Array<CompositeDelta>;
- readonly hash: Buffer;
- readonly description: string;
- // Do not call constructor directly. Instead, use CompositeLevel.createComposite
- constructor(deltas: Array<Delta>, dependencies: Array<CompositeDelta>, typedDependencies: Array<[CompositeDelta,string]>, conflicts: Array<CompositeDelta>, hash: Buffer, description: string) {
- this.deltas = deltas;
- this.dependencies = dependencies;
- this.typedDependencies = typedDependencies;
- this.conflicts = conflicts;
- this.hash = hash;
- this.description = description;
- }
- getDependencies(): Array<CompositeDelta> {
- return this.dependencies;
- }
- getTypedDependencies(): Array<[CompositeDelta, string]> {
- return this.typedDependencies;
- }
- getConflicts(): Array<CompositeDelta> {
- return this.conflicts;
- }
- getHash(): Buffer {
- return this.hash;
- }
- getDescription(): string {
- return this.description;
- }
- serialize(): any {
- return {
- type: "CompositeDelta",
- deltas: this.deltas.map(d => d.serialize()),
- }
- }
- *iterPrimitiveDeltas(): Iterable<Delta> {
- for (const d of this.deltas) {
- yield* d.iterPrimitiveDeltas();
- }
- }
- }
- // A "registry" of composite deltas.
- // When creating a new CompositeDelta, it will figure out what other CompositeDeltas to depend on, and to conflict with.
- export class CompositeLevel {
- containedBy: Map<Delta, CompositeDelta> = new Map();
- createComposite(deltas: Array<Delta>, description: string = deltas.map(d=>d.getDescription()).join(",")): CompositeDelta {
- const dependencies: Array<CompositeDelta> = [];
- const typedDependencies: Array<[CompositeDelta,string]> = [];
- const conflicts: Array<CompositeDelta> = [];
- for (const delta of deltas) {
- if (this.containedBy.has(delta)) {
- throw new Error("Assertion failed: delta " + delta.getDescription() + " already part of another composite");
- }
- for (const [dependency, dependencyType] of delta.getTypedDependencies()) {
- if (!deltas.includes(dependency)) {
- // We got ourselves an inter-composite dependency.
- const compositeDependency = this.containedBy.get(dependency);
- if (compositeDependency === undefined) {
- throw new Error("Assertion failed: delta " + delta.getDescription() + " depends on " + dependency.getDescription() + " but this dependency could not be found in a composite.");
- }
- const existingDependency = typedDependencies.find(([dep,_]) => dep === compositeDependency);
- if (existingDependency !== undefined) {
- // existingDependency[1] += ","+dependencyType;
- } else {
- dependencies.push(compositeDependency);
- // typedDependencies.push([compositeDependency, dependencyType]);
- typedDependencies.push([compositeDependency, ""]);
- }
- }
- }
- for (const conflict of delta.getConflicts()) {
- if (deltas.includes(conflict)) {
- console.log("Conflict between", conflict, "and", delta);
- throw new Error("Cannot create a composite delta out of conflicting deltas");
- }
- const compositeConflict = this.containedBy.get(conflict);
- if (compositeConflict === undefined) {
- // We used to treat this as an error, however, it's possible that a conflicting delta simply isn't part yet of a composite delta...
- // throw new Error("Assertion failed: cannot find composite of " + conflict.getDescription());
- } else {
- if (!conflicts.includes(compositeConflict)) {
- conflicts.push(compositeConflict);
- }
- }
- }
- }
- if (dependencies.some(dependency => conflicts.includes(dependency))) {
- throw new Error("Assertion failed: Attempted to create a composite delta that conflicts with one of its dependencies.");
- }
- const hash = createHash('sha256');
- for (const delta of deltas) {
- hash.update(delta.getHash());
- }
- const composite = new CompositeDelta(deltas, dependencies, typedDependencies, conflicts, hash.digest(), description);
- for (const delta of deltas) {
- this.containedBy.set(delta, composite);
- }
- for (const compositeConflict of conflicts) {
- // Conflicts are symmetric, so newly created conflicts are also added to the other:
- compositeConflict.conflicts.push(composite);
- }
- return composite;
- }
- }
|