UNPKG

synopsys

Version:

Synopsys is proof of concept datastore service. It stores facts in terms of entity attribute value triples and allows clients to subscribe to _(datomic inspired)_ queries pushing updates to them when new transactions affect results.

174 lines (156 loc) 4.44 kB
import * as Digits from './digits.js' import * as Major from './major.js' import * as Base from './base.js' export const { CONSECUTIVE, EQUAL } = Digits export const { base62: base } = Digits export const MIN = Base.min(base) export const MAX = Base.max(base) export const MEDIAN = Base.median(base) /** * Type signifying a digits in the base64 character set representing a patch * component of the position. * * @typedef {Digits.Digits<Digits.B62>} Patch */ /** * @typedef {Digits.Digits<Digits.B62>} Bias */ /** * @param {Uint8Array} position * @returns {Patch} */ export const from = (position) => /** @type {Patch} */ ( position.subarray(Major.capacity(Major.from(position)) + 1) ) /** * @returns {Patch} */ export const max = () => new Uint8Array([MAX + 1]) /** * @returns {Patch} */ export const min = () => new Uint8Array([MIN - 1]) const median = new Uint8Array([MEDIAN]) /** * Returns an intermediate patch value that would sort between `lower` and * `upper`. Since patches are treated as fractions they can grow in size * which is what will happen if `lower` and `upper` are consecutive. If lower * and upper are the same function will return `null`. * * @param {Patch} low * @param {Patch} high * @param {Bias} bias * @returns {Patch|null} */ export const intermediate = (low, high, bias) => { const digits = Digits.intermediate(low, high, base.ranges) switch (digits) { case Digits.EQUAL: return null case Digits.CONSECUTIVE: // If lower and upper are consecutive, we can derive average by // appending bias to the `lower` position. This will ensure that // the patch is always greater than the `lower` and less than the // `upper` position. However if bias is not provided we may end up // with a patch that is equal to the `lower` position which is why // we append `median` in such case. return append(low, bias.length > 0 ? bias : median) default: { const head = bias[0] // If bias is empty we can return the intermediate digits as is. if (head == null) { return digits } // Otherwise we will adjust an intermediate digits with the bias. // If we have an intermediate position, only it's last digit will be // between `low` and `high` digits at the same offset. const offset = digits.length - 1 // If head of the bias fits between the low and high digits we can // override tie breaking digit. const delta = low[offset] < head && head < high[offset] ? 1 : 0 const patch = new Uint8Array(digits.length + bias.length - delta) patch.set(digits) patch.set(bias, digits.length - delta) return patch } } } /** * @param {Patch} digits * @param {Bias} bias */ export const next = (digits, bias) => intermediate(digits, max(), bias) || append(digits, bias) /** * @param {Patch} digits * @param {Patch} extra * @returns {Patch} */ const append = (digits, extra) => { const patch = new Uint8Array(digits.length + extra.length) patch.set(digits) patch.set(extra, digits.length) return patch } /** * @param {Patch} digits * @returns {Patch|null} */ const decrease = (digits) => { let offset = digits.length - 1 while (offset > 0 && digits[offset] === MIN) { offset-- } if (offset <= 0) { return null } ///* c8 ignore next 3 */ else { return digits.subarray(0, offset + 1) } } /** * * @param {Patch} patch * @param {Bias} bias * @returns {Patch} */ export const increment = (patch, bias) => { const digits = Digits.increment(patch, base.ranges) const [head] = bias if (digits == null) { return append(patch, median) } else if (head == null) { return digits } else if (head >= digits[0]) { return bias } else { return append(digits, bias) } } /** * @param {Patch} patch * @param {Bias} bias * @returns {Patch|null} */ export const decrement = (patch, bias) => { const digits = Digits.decrement(patch, base.ranges) || decrease(patch) const [head] = bias // If we can not create decremented nor shorten patch we give up. // we if (digits == null) { return null } else if (head == null) { return digits } else if (head <= digits[0]) { return bias } else { return append(digits, bias) } } /** * @param {Patch} digits * @returns {Patch} */ export const trim = (digits) => Digits.trim(digits, base.ranges)