@aeternity/aepp-calldata
Version:
Aeternity data serialization library
196 lines (154 loc) • 4.86 kB
JavaScript
import assert from './utils/assert.js'
import FateList from './types/FateList.js'
import FateTuple from './types/FateTuple.js'
import {FateTypeTuple, FateTypeInt} from './FateTypes.js'
/* eslint-disable no-use-before-define */
// TODO types comparator
const listComparator = (a, b) => {
if (a.length === 0) {
return -1
}
if (b.length === 0) {
return 1
}
const cmp = FateComparator(a.itemsType)
for (let i = 0; i < a.length; i++) {
// second list is shorter but matches as prefix of the first one
if (typeof b.items[i] === 'undefined') {
return 1
}
// element difference
const diff = cmp(a.items[i], b.items[i])
if (diff !== 0) {
return diff
}
}
// if there is no early return from the loop above
// then the first list match a prefix of the second
// equal lists
if (a.length === b.length) {
return 0
}
// first list is shorter, thus smaller
return -1
}
const tupleComparator = (a, b) => {
if (a.size === 0) {
return -1
}
const sizeDiff = a.size - b.size
if (sizeDiff !== 0) {
return sizeDiff
}
// equal size - compare elements
for (let i = 0; i < a.size; i++) {
const valTypeA = a.valueTypes[i]
// TODO support different types ?
const diff = FateComparator(valTypeA)(a.items[i], b.items[i])
if (diff !== 0) {
return diff
}
}
// equal tuples
return 0
}
const variantComparator = (a, b) => {
const aDiff = a.arities.length - b.arities.length
if (aDiff !== 0) {
return aDiff
}
const aList = new FateList(a.aritiesType, a.arities)
const bList = new FateList(b.aritiesType, b.arities)
const aComparator = FateComparator(aList)
const lDiff = aComparator(aList, bList)
if (lDiff !== 0) {
return lDiff
}
const tDiff = a.tag - b.tag
if (tDiff !== 0) {
return tDiff
}
// equal arities and tags - compare elements
const elementsComparator = FateComparator(FateTypeTuple())
return elementsComparator(
new FateTuple(a.valueTypes, a.value),
new FateTuple(b.valueTypes, b.value)
)
}
const mapItemComparator = (type) => {
const keyComparator = FateComparator(type)
return (a, b) => keyComparator(a.key, b.key)
}
const mapComparator = (a, b) => {
const aItems = [...a.items]
const bItems = [...b.items]
aItems.sort(mapItemComparator(a.keyType))
bItems.sort(mapItemComparator(b.keyType))
const keyComparator = FateComparator(a.keyType)
const valueComparator = FateComparator(a.valueType)
for (let i = 0; i < aItems.length; i++) {
// second map is smaller (less items)
if (typeof bItems[i] === 'undefined') {
return 1
}
const aItem = aItems[i]
const bItem = bItems[i]
const kDiff = keyComparator(aItem.key, bItem.key)
if (kDiff !== 0) {
return kDiff
}
const vDiff = valueComparator(aItem.value, bItem.value)
if (vDiff !== 0) {
return vDiff
}
}
// equal number of items
if (aItems.length === bItems.length) {
return 0
}
// first map item list is shorter, thus smaller
return -1
}
const bytesComparator = (a, b) => {
const aList = new FateList(FateTypeInt(), a.valueOf())
const bList = new FateList(FateTypeInt(), b.valueOf())
return listComparator(aList, bList)
}
const stringComparator = (a, b) => {
const encoder = new TextEncoder()
const as = a.toString()
const bs = b.toString()
if (as.length === bs.length) {
return bytesComparator(encoder.encode(a), encoder.encode(b))
}
return as.length - bs.length
}
const intComparator = (a, b) => Number(BigInt(a) - BigInt(b))
const boolComparator = (a, b) => a - b
const bitsComparator = (a, b) => {
return (a < 0 || b < 0) ? -intComparator(a, b) : intComparator(a, b)
}
const comparators = {
'int': intComparator,
'bool': boolComparator,
'string': stringComparator,
'bits': bitsComparator,
// composite types
'list': listComparator,
'tuple': tupleComparator,
'variant': variantComparator,
'map': mapComparator,
// objects (bytes)
'bytes': bytesComparator,
'account_pubkey': bytesComparator,
'channel': bytesComparator,
'contract_pubkey': bytesComparator,
'oracle_query_id': bytesComparator,
'oracle_pubkey': bytesComparator,
}
const FateComparator = (type) => {
assert(type.hasOwnProperty('name'), `Cannot determine type name of ${JSON.stringify(type)}`)
assert(comparators.hasOwnProperty(type.name), `Unsupported comparator for ${type.name}`)
return comparators[type.name]
}
export default FateComparator