2023-12-16 09:13:47 +00:00
|
|
|
import { is, isAnnotated } from './is';
|
|
|
|
import { Bytes } from './bytes';
|
|
|
|
import { Set, Dictionary } from './dictionary';
|
2024-03-12 16:47:38 +00:00
|
|
|
import { isEmbedded } from './embedded';
|
2023-12-16 09:13:47 +00:00
|
|
|
import { Float } from './float';
|
|
|
|
import { Value } from './values';
|
|
|
|
import { Record } from './record';
|
2024-03-12 16:47:38 +00:00
|
|
|
import type { Embeddable } from './embedded';
|
2023-12-16 09:13:47 +00:00
|
|
|
|
2024-03-12 16:47:38 +00:00
|
|
|
export function typeCode<T extends Embeddable>(v: Value<T>): number {
|
|
|
|
if (isAnnotated<T>(v)) v = v.item;
|
2023-12-16 09:13:47 +00:00
|
|
|
switch (typeof v) {
|
|
|
|
case 'boolean':
|
|
|
|
return 0;
|
|
|
|
case 'number':
|
|
|
|
case 'bigint':
|
|
|
|
return 3;
|
|
|
|
case 'string':
|
|
|
|
return 4;
|
|
|
|
case 'symbol':
|
|
|
|
return 6;
|
|
|
|
case 'object':
|
2024-01-27 13:40:37 +00:00
|
|
|
if (Float.isFloat(v)) return 2; // 1 was for single-precision floats
|
2023-12-16 09:13:47 +00:00
|
|
|
if (Bytes.isBytes(v)) return 5;
|
|
|
|
if (Array.isArray(v)) {
|
|
|
|
return ('label' in v) ? 7 : 8;
|
|
|
|
}
|
2024-03-12 16:47:38 +00:00
|
|
|
if (Set.isSet<T>(v)) return 9;
|
|
|
|
if (Dictionary.isDictionary<T>(v)) return 10;
|
2023-12-16 09:13:47 +00:00
|
|
|
if (isEmbedded(v)) return 11;
|
|
|
|
/* fall through */
|
|
|
|
default:
|
2024-03-12 16:47:38 +00:00
|
|
|
throw new Error("Invalid Value<T> in typeCode");
|
2023-12-16 09:13:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-12 16:47:38 +00:00
|
|
|
export function compare<T extends Embeddable>(
|
|
|
|
a: Value<T>,
|
|
|
|
b: Value<T>,
|
|
|
|
compare_embedded: (a: T, b: T) => number = (a, b) => is(a, b) ? 0 : a < b ? -1 : 1,
|
2023-12-16 09:13:47 +00:00
|
|
|
): number {
|
2024-03-12 16:47:38 +00:00
|
|
|
function cmp(a: Value<T>, b: Value<T>): number {
|
|
|
|
if (isAnnotated<T>(a)) a = a.item;
|
|
|
|
if (isAnnotated<T>(b)) b = b.item;
|
2023-12-16 09:13:47 +00:00
|
|
|
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;
|
|
|
|
}
|
2024-01-27 13:40:37 +00:00
|
|
|
// case 1: // was single-precision
|
2023-12-16 09:13:47 +00:00
|
|
|
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: {
|
2024-03-12 16:47:38 +00:00
|
|
|
const lr = cmp((a as Record<Value<T>, Value<T>[], T>).label,
|
|
|
|
(b as Record<Value<T>, Value<T>[], T>).label);
|
2023-12-16 09:13:47 +00:00
|
|
|
if (lr !== 0) return lr;
|
|
|
|
/* fall through */
|
|
|
|
}
|
|
|
|
case 8: {
|
2024-03-12 16:47:38 +00:00
|
|
|
const va = a as Value<T>[];
|
|
|
|
const vb = b as Value<T>[];
|
2023-12-16 09:13:47 +00:00
|
|
|
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: {
|
2024-03-12 16:47:38 +00:00
|
|
|
const va = Array.from(a as Set<T>).sort(cmp);
|
|
|
|
const vb = Array.from(b as Set<T>).sort(cmp);
|
2023-12-16 09:13:47 +00:00
|
|
|
return cmp(va, vb);
|
|
|
|
}
|
|
|
|
case 10: {
|
2024-03-27 08:13:04 +00:00
|
|
|
const va = Array.from(Dictionary.asMap<T>(a)!.entries()).sort(cmp);
|
|
|
|
const vb = Array.from(Dictionary.asMap<T>(b)!.entries()).sort(cmp);
|
2023-12-16 09:13:47 +00:00
|
|
|
return cmp(va, vb);
|
|
|
|
}
|
|
|
|
case 11:
|
2024-03-12 16:47:38 +00:00
|
|
|
return compare_embedded(a as T, b as T);
|
2023-12-16 09:13:47 +00:00
|
|
|
default:
|
|
|
|
throw new Error("Invalid typeCode: " + ta);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cmp(a, b);
|
|
|
|
}
|