UNPKG

dmclc

Version:

Dolphin Minecraft Launcher Core

442 lines (441 loc) 16.7 kB
/* * Ported from Fabric Loader. * Copyright 2016 FabricMC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import assert from "assert"; import { SemanticVersion } from "./SemanticVersion.js"; import { VersionInterval } from "./VersionInterval.js"; class VersionIntervalImpl extends VersionInterval { static INFINITE = new VersionIntervalImpl(undefined, false, undefined, false); min; minInclusive; max; maxInclusive; constructor(min, minInclusive, max, maxInclusive) { super(); this.min = min; this.minInclusive = min != undefined ? minInclusive : false; this.max = max; this.maxInclusive = max != undefined ? maxInclusive : false; assert(min != undefined || !minInclusive); assert(max != undefined || !maxInclusive); assert(min == undefined || min instanceof SemanticVersion || minInclusive); assert(max == undefined || max instanceof SemanticVersion || maxInclusive); assert(min == undefined || max == undefined || min instanceof SemanticVersion && max instanceof SemanticVersion || min.equals(max)); } isSemantic() { return (this.min == undefined || this.min instanceof SemanticVersion) && (this.max == undefined || this.max instanceof SemanticVersion); } getMin() { return this.min; } isMinInclusive() { return this.minInclusive; } getMax() { return this.max; } isMaxInclusive() { return this.maxInclusive; } equals(obj) { if (obj instanceof VersionInterval) { return (this.min?.equals(obj.getMin()) && this.minInclusive === obj.isMinInclusive() && this.max?.equals(obj.getMax()) && this.maxInclusive === obj.isMaxInclusive()) ? true : false; } else { return false; } } toString() { if (this.min == undefined) { if (this.max == undefined) { return "(-∞,∞)"; } else { return "(-∞," + this.max + this.maxInclusive ? "]" : ")"; } } else if (this.max == undefined) { return "%c%s" + this.minInclusive ? "[" : "(" + this.min + ",∞)"; } else { return this.minInclusive ? "[" : "(" + this.min + "," + this.max + this.maxInclusive ? "]" : ")"; } } static andOne(a, b) { if (a == undefined || b == undefined) return undefined; if (!a.isSemantic() || !b.isSemantic()) { return this.andPlain(a, b); } else { return this.andSemantic(a, b); } } static andPlain(a, b) { const aMin = a.getMin(); const aMax = a.getMax(); const bMin = b.getMin(); const bMax = b.getMax(); if (aMin != undefined) { // -> min must be aMin or invalid if (bMin != undefined && !aMin.equals(bMin) || bMax != undefined && !aMin.equals(bMax)) { return undefined; } if (aMax != undefined || bMax == undefined) { assert(aMax?.equals(bMax) || bMax == undefined); return a; } else { return new VersionIntervalImpl(aMin, true, bMax, b.isMaxInclusive()); } } else if (aMax != undefined) { // -> min must be bMin, max must be aMax or invalid if (bMin != undefined && !aMax.equals(bMin) || bMax != undefined && !aMax.equals(bMax)) { return undefined; } if (bMin == undefined) { return a; } else if (bMax != undefined) { return b; } else { return new VersionIntervalImpl(bMin, true, aMax, true); } } else { return b; } } static andSemantic(a, b) { const minCmp = VersionIntervalImpl.compareMin(a, b); const maxCmp = VersionIntervalImpl.compareMax(a, b); if (minCmp == 0) { // aMin == bMin if (maxCmp == 0) { // aMax == bMax -> a == b -> a/b return a; } else { // aMax != bMax -> a/b..min(a,b) return maxCmp < 0 ? a : b; } } else if (maxCmp == 0) { // aMax == bMax, aMin != bMin -> max(a,b)..a/b return minCmp < 0 ? b : a; } else if (minCmp < 0) { // aMin < bMin, aMax != bMax -> b..min(a,b) if (maxCmp > 0) return b; // a > b -> b const aMax = a.getMax(); const bMin = b.getMin(); const cmp = bMin.compareTo(aMax); if (cmp < 0 || cmp == 0 && b.isMinInclusive() && a.isMaxInclusive()) { return new VersionIntervalImpl(bMin, b.isMinInclusive(), aMax, a.isMaxInclusive()); } else { return undefined; } } else { // aMin > bMin, aMax != bMax -> a..min(a,b) if (maxCmp < 0) return a; // a < b -> a const aMin = a.getMin(); const bMax = b.getMax(); const cmp = aMin.compareTo(bMax); if (cmp < 0 || cmp == 0 && a.isMinInclusive() && b.isMaxInclusive()) { return new VersionIntervalImpl(aMin, a.isMinInclusive(), bMax, b.isMaxInclusive()); } else { return undefined; } } } static and(a, b) { if (a.length === 0 || b.length === 0) return []; if (a.length == 1 && b.length == 1) { const merged = VersionIntervalImpl.andOne(a[0], b[0]); return merged != undefined ? [merged] : []; } // (a0 || a1 || a2) && (b0 || b1 || b2) == a0 && b0 && b1 && b2 || a1 && b0 && b1 && b2 || a2 && b0 && b1 && b2 const allMerged = []; for (const intervalA of a) { for (const intervalB of b) { const merged = VersionIntervalImpl.andOne(intervalA, intervalB); if (merged != undefined) allMerged.push(merged); } } if (allMerged.length === 0) return []; if (allMerged.length == 1) return allMerged; const ret = new Array(allMerged.length); for (const v of allMerged) { VersionIntervalImpl.merge(v, ret); } return ret; } static or(a, b) { if (a.length === 0) { if (b == undefined) { return []; } else { return [b]; } } const ret = new Array(a.length + 1); for (const v of a) { VersionIntervalImpl.merge(v, ret); } VersionIntervalImpl.merge(b, ret); return ret; } static merge(a, out) { if (a == undefined) return; if (out.length === 0) { out.push(a); return; } if (out.length == 1) { const e = out[0]; if (e.getMin() == undefined && e.getMax() == undefined) { return; } } if (!a.isSemantic()) { VersionIntervalImpl.mergePlain(a, out); } else { VersionIntervalImpl.mergeSemantic(a, out); } } static mergePlain(a, out) { const aMin = a.getMin(); const aMax = a.getMax(); const v = aMin != undefined ? aMin : aMax; assert(v != undefined); for (let i = 0; i < out.length; i++) { const c = out[i]; if (v.equals(c.getMin())) { if (aMin == undefined) { assert(aMax?.equals(c.getMin())); out.length = 0; out.push(VersionIntervalImpl.INFINITE); } else if (aMax == undefined && c.getMax() != undefined) { out[i] = a; } return; } else if (v.equals(c.getMax())) { assert(c.getMin() == undefined); if (aMax == undefined) { assert(aMin?.equals(c.getMax())); out.length = 0; out.push(VersionIntervalImpl.INFINITE); } return; } } out.push(a); } static mergeSemantic(a, out) { const aMin = a.getMin(); const aMax = a.getMax(); if (aMin == undefined && aMax == undefined) { out.length = 0; out.push(VersionIntervalImpl.INFINITE); return; } for (let i = 0; i < out.length; i++) { const c = out[i]; if (!c.isSemantic()) continue; const cMin = c.getMin(); const cMax = c.getMax(); let cmp; if (aMin == undefined) { // ..a..] if (cMax == undefined) { // ..a..] [..c.. cmp = aMax.compareTo(cMin); if (cmp < 0 || cmp == 0 && !a.isMaxInclusive() && !c.isMinInclusive()) { // ..a..]..[..c.. or ..a..)(..c.. out.splice(i, 0, a); } else { // ..a..|..c.. or ..a.[..].c.. out.length = 0; out.push(VersionIntervalImpl.INFINITE); } return; } else { // ..a..] [..c..] cmp = VersionIntervalImpl.compareMax(a, c); if (cmp >= 0) { // a encompasses c out.splice(i, 1); i--; } else if (cMin == undefined) { // c encompasses a return; } else { // aMax < cMax cmp = aMax.compareTo(cMin); if (cmp < 0 || cmp == 0 && !a.isMaxInclusive() && !c.isMinInclusive()) { // ..a..]..[..c..] or ..a..)(..c..] out.splice(i, 0, a); } else { // c extends a to the right out[i] = new VersionIntervalImpl(undefined, false, cMax, c.isMaxInclusive()); } return; } } } else if (cMax == undefined) { // [..c.. cmp = VersionIntervalImpl.compareMin(a, c); if (cmp >= 0) { // c encompasses a // no-op } else if (aMax == undefined) { // a encompasses c while (out.length > i) out.splice(i, 1); out.push(a); } else { // aMin < cMin cmp = aMax.compareTo(cMin); if (cmp < 0 || cmp == 0 && !a.isMaxInclusive() && !c.isMinInclusive()) { // [..a..]..[..c.. or [..a..)(..c.. out.splice(i, 0, a); } else { // a extends c to the left out[i] = new VersionIntervalImpl(aMin, a.isMinInclusive(), undefined, false); } } return; } else if ((cmp = aMin.compareTo(cMax)) < 0 || cmp == 0 && (a.isMinInclusive() || c.isMaxInclusive())) { let cmp2; if (aMax == undefined || cMin == undefined || (cmp2 = aMax.compareTo(cMin)) > 0 || cmp2 == 0 && (a.isMaxInclusive() || c.isMinInclusive())) { const cmpMin = VersionIntervalImpl.compareMin(a, c); const cmpMax = VersionIntervalImpl.compareMax(a, c); if (cmpMax <= 0) { // aMax <= cMax if (cmpMin < 0) { // aMin < cMin out[i] = new VersionIntervalImpl(aMin, a.isMinInclusive(), cMax, c.isMaxInclusive()); } return; } else if (cmpMin > 0) { // aMin > cMin, aMax > cMax a = new VersionIntervalImpl(cMin, c.isMinInclusive(), aMax, a.isMaxInclusive()); } out.splice(i, 1); i--; } else { out.splice(i, 0, a); return; } } } out.push(a); } static compareMin(a, b) { const aMin = a.getMin(); const bMin = b.getMin(); let cmp; if (aMin == undefined) { // a <= b if (bMin == undefined) { // a == b == -inf return 0; } else { // bMin != undefined -> a < b return -1; } } else if (bMin == undefined || (cmp = aMin.compareTo(bMin)) > 0 || cmp == 0 && !a.isMinInclusive() && b.isMinInclusive()) { // a > b return 1; } else if (cmp < 0 || a.isMinInclusive() && !b.isMinInclusive()) { // a < b return -1; } else { // cmp == 0 && a.minInclusive() == b.minInclusive() -> a == b return 0; } } static compareMax(a, b) { const aMax = a.getMax(); const bMax = b.getMax(); let cmp; if (aMax == undefined) { // a >= b if (bMax == undefined) { // a == b == inf return 0; } else { // bMax != undefined -> a > b return 1; } } else if (bMax == undefined || (cmp = aMax.compareTo(bMax)) < 0 || cmp == 0 && !a.isMaxInclusive() && b.isMaxInclusive()) { // a < b return -1; } else if (cmp > 0 || a.isMaxInclusive() && !b.isMaxInclusive()) { // a > b return 1; } else { // cmp == 0 && a.maxInclusive() == b.maxInclusive() -> a == b return 0; } } static notOne(interval) { if (interval == undefined) { // () = empty interval -> infinite return [VersionIntervalImpl.INFINITE]; } else if (interval.getMin() == undefined) { // (-∞, = at least half-open towards min if (interval.getMax() == undefined) { // (-∞,∞) = infinite -> empty return []; } else { // (-∞,x = left open towards min -> half open towards max return [new VersionIntervalImpl(interval.getMax(), !interval.isMaxInclusive(), undefined, false)]; } } else if (interval.getMax() == undefined) { // x,∞) = half open towards max -> half open towards min return [new VersionIntervalImpl(undefined, false, interval.getMin(), !interval.isMinInclusive())]; } else if (interval.getMin()?.equals(interval.getMax()) && !interval.isMinInclusive() && !interval.isMaxInclusive()) { // (x,x) = effectively empty interval -> infinite return [VersionIntervalImpl.INFINITE]; } else { // closed interval -> 2 half open intervals on each side const ret = new Array(2); ret.push(new VersionIntervalImpl(undefined, false, interval.getMin(), !interval.isMinInclusive())); ret.push(new VersionIntervalImpl(interval.getMax(), !interval.isMaxInclusive(), undefined, false)); return ret; } } static not(intervals) { if (intervals.length === 0) return [VersionIntervalImpl.INFINITE]; if (intervals.length == 1) return this.notOne(intervals[0]); // !(i0 || i1 || i2) == !i0 && !i1 && !i2 let ret = undefined; for (const v of intervals) { const inverted = this.notOne(v); if (!ret) { ret = inverted; } else { ret = this.and(ret, inverted); } if (ret.length === 0) break; } return ret; } } export { VersionIntervalImpl };