preserves/implementations/javascript/packages/core/src/order.ts

104 lines
3.6 KiB
TypeScript

import { is, isAnnotated } from './is';
import { Bytes } from './bytes';
import { Set, Dictionary } from './dictionary';
import { Embedded, isEmbedded } from './embedded';
import { Float } from './float';
import { Value } from './values';
import { Record } from './record';
export function typeCode<V>(v: Value<V>): number {
if (isAnnotated<V>(v)) v = v.item;
switch (typeof v) {
case 'boolean':
return 0;
case 'number':
case 'bigint':
return 3;
case 'string':
return 4;
case 'symbol':
return 6;
case 'object':
if (Float.isFloat(v)) return 2; // 1 was for single-precision floats
if (Bytes.isBytes(v)) return 5;
if (Array.isArray(v)) {
return ('label' in v) ? 7 : 8;
}
if (Set.isSet<V>(v)) return 9;
if (Dictionary.isDictionary<V>(v)) return 10;
if (isEmbedded(v)) return 11;
/* fall through */
default:
throw new Error("Invalid Value<V> in typeCode");
}
}
export function compare<V>(
a: Value<V>,
b: Value<V>,
compare_embedded: (a: V, b: V) => number = (a, b) => is(a, b) ? 0 : a < b ? -1 : 1,
): number {
function cmp(a: Value<V>, b: Value<V>): number {
if (isAnnotated<V>(a)) a = a.item;
if (isAnnotated<V>(b)) b = b.item;
const ta = typeCode(a);
const tb = typeCode(b);
if (ta < tb) return -1;
if (ta > tb) return 1;
switch (ta) {
case 0:
case 3:
case 4: {
const va = a as any;
const vb = b as any;
return va < vb ? -1 : va > vb ? 1 : 0;
}
// case 1: // was single-precision
case 2: {
const va = (a as Float).value;
const vb = (b as Float).value;
return va < vb ? -1 : va > vb ? 1 : 0;
}
case 5:
return Bytes.compare(a as Bytes, b as Bytes);
case 6: {
const va = (a as symbol).description!;
const vb = (b as symbol).description!;
return va < vb ? -1 : va > vb ? 1 : 0;
}
case 7: {
const lr = cmp((a as Record<Value<V>, Value<V>[], V>).label,
(b as Record<Value<V>, Value<V>[], V>).label);
if (lr !== 0) return lr;
/* fall through */
}
case 8: {
const va = a as Value<V>[];
const vb = b as Value<V>[];
const l = Math.min(va.length, vb.length)
for (let i = 0; i < l; i++) {
const c = cmp(va[i], vb[i]);
if (c !== 0) return c;
}
return va.length < vb.length ? -1 : va.length > vb.length ? 1 : 0;
}
case 9: {
const va = Array.from(a as Set<V>).sort(cmp);
const vb = Array.from(b as Set<V>).sort(cmp);
return cmp(va, vb);
}
case 10: {
const va = Array.from(a as Dictionary<V>).sort(cmp);
const vb = Array.from(b as Dictionary<V>).sort(cmp);
return cmp(va, vb);
}
case 11:
return compare_embedded((a as Embedded<V>).embeddedValue,
(b as Embedded<V>).embeddedValue);
default:
throw new Error("Invalid typeCode: " + ta);
}
}
return cmp(a, b);
}