import { NONE, NOOP_SUBSCRIBER, NOOP_SUBSCRIPTION, Source, Subscriber, Subscription } from "./_core" import { Transaction } from "./_tx" import { MAX_SAFE_INTEGER } from "./_util" export abstract class Dispatcher implements Subscriber, Source { public readonly weight: number protected sink: Subscriber = NOOP_SUBSCRIBER protected subs: Subscription = NOOP_SUBSCRIPTION protected ord: number = -1 protected active: boolean = false protected msc: MulticastSubscriber = NONE constructor(protected source: Source) { this.weight = source.weight } public subscribe(subscriber: Subscriber, order: number): Subscription { if (this.msc === NONE) { return this.firstSubscribe(subscriber, order) } else { // a.k.a "multicast" return this.lateSubscribe(subscriber, order) } } public activate(subscriber: Subscriber, initialNeeded: boolean): void { if (!this.active) { this.active = true this.subs.activate(initialNeeded) } } public dispose(node: MCSNode): void { const { sink, msc } = this if (sink === msc) { const { order, next } = this.removeNode(node) this.sink = next if (order !== this.ord) { this.reorderAfterRemoval(order) } } else { const { subs } = this this.sink = NOOP_SUBSCRIBER this.subs = NOOP_SUBSCRIPTION this.msc = NONE this.ord = -1 this.active = false subs.dispose() } } public reorder(order: number): void { if (order > this.ord) { this.subs.reorder((this.ord = order)) } } public next(tx: Transaction, val: T): void { this.sink.next(tx, val) } public error(tx: Transaction, err: Error): void { this.sink.error(tx, err) } public end(tx: Transaction): void { this.sink.end(tx) } protected removeNode(node: MCSNode): { order: number; next: Subscriber } { return this.msc.removeReturnMaxOrder(node) } protected reorderAfterRemoval(order: number): void { this.subs.reorder((this.ord = order)) } protected firstSubscribe(subscriber: Subscriber, order: number): Subscription { const node = mcsNode(subscriber, order) this.msc = new MulticastSubscriber(node) this.ord = order this.sink = subscriber this.subs = this.source.subscribe(this, order) return this.makeSubs(node) } protected lateSubscribe(subscriber: Subscriber, order: number): Subscription { const { sink, msc } = this const node = mcsNode(subscriber, order) msc.add(node) if (sink !== msc) { this.sink = this.msc } this.reorder(order) return this.makeSubs(node) } private makeSubs(mcs: MCSNode): Subscription { return new MulticastSubscription(mcs, this) } } class MulticastSubscriber implements Subscriber { private h: MCSNode | null constructor(head: MCSNode) { this.h = head } public add(node: MCSNode): void { const { h } = this node.t = h h !== null && (h.h = node) this.h = node } public removeReturnMaxOrder(node: MCSNode): { order: number; next: Subscriber } { const next = this.remove(node) let head = this.h let order = -1 while (head !== null) { if (head.o > order) { order = head.o } head = head.t } return { next, order, } } public removeReturnMinOrder(node: MCSNode): { order: number; next: Subscriber } { const next = this.remove(node) let head = this.h let order = MAX_SAFE_INTEGER while (head !== null) { if (head.o < order) { order = head.o } head = head.t } return { next, order, } } public next(tx: Transaction, val: T): void { let head = this.h while (head !== null) { head.a && head.s.next(tx, val) head = head.t } } public error(tx: Transaction, err: Error): void { let head = this.h while (head !== null) { head.a && head.s.error(tx, err) head = head.t } } public end(tx: Transaction): void { let head = this.h while (head !== null) { head.a && head.s.end(tx) head = head.t } } private remove(node: MCSNode): Subscriber { node.h !== null ? (node.h.t = node.t) : (this.h = node.t) node.t !== null ? (node.t.h = node.h) : void 0 const head = this.h as MCSNode return head.t !== null ? this : head.s } } function mcsNode(subscriber: Subscriber, order: number): MCSNode { return { a: false, h: null, o: order, s: subscriber, t: null, } } export interface MCSNode { s: Subscriber // subscriber o: number // order a: boolean // active/inactive h: MCSNode | null // head t: MCSNode | null // tail } class MulticastSubscription implements Subscription { private n: MCSNode private d: Dispatcher constructor(mcs: MCSNode, dispatcher: Dispatcher) { this.n = mcs this.d = dispatcher } public activate(initialNeeded: boolean): void { this.n.a = true this.d.activate(this.n.s, initialNeeded) } public dispose(): void { this.n.a = false this.d.dispose(this.n) } public reorder(order: number): void { this.d.reorder((this.n.o = order)) } }