@metaplex-foundation/umi-serializers
Version:
A comprehensive set of serializers for the Umi framework
87 lines (84 loc) • 2.88 kB
text/typescript
import {
BaseSerializerOptions,
mergeBytes,
Serializer,
} from '@metaplex-foundation/umi-serializers-core';
import { u32 } from '@metaplex-foundation/umi-serializers-numbers';
import { ArrayLikeSerializerSize } from './arrayLikeSerializerSize';
import { InvalidNumberOfItemsError } from './errors';
import {
getResolvedSize,
getSizeDescription,
getSizeFromChildren,
getSizePrefix,
} from './utils';
/**
* Defines the options for `Map` serializers.
* @category Serializers
*/
export type MapSerializerOptions = BaseSerializerOptions & {
/**
* The size of the map.
* @defaultValue `u32()`
*/
size?: ArrayLikeSerializerSize;
};
/**
* Creates a serializer for a map.
*
* @param key - The serializer to use for the map's keys.
* @param value - The serializer to use for the map's values.
* @param options - A set of options for the serializer.
* @category Serializers
*/
export function map<TK, TV, UK extends TK = TK, UV extends TV = TV>(
key: Serializer<TK, UK>,
value: Serializer<TV, UV>,
options: MapSerializerOptions = {}
): Serializer<Map<TK, TV>, Map<UK, UV>> {
const size = options.size ?? u32();
return {
description:
options.description ??
`map(${key.description}, ${value.description}; ${getSizeDescription(
size
)})`,
fixedSize: getSizeFromChildren(size, [key.fixedSize, value.fixedSize]),
maxSize: getSizeFromChildren(size, [key.maxSize, value.maxSize]),
serialize: (map: Map<TK, TV>) => {
if (typeof size === 'number' && map.size !== size) {
throw new InvalidNumberOfItemsError('map', size, map.size);
}
const itemBytes = Array.from(map, ([k, v]) =>
mergeBytes([key.serialize(k), value.serialize(v)])
);
return mergeBytes([getSizePrefix(size, map.size), ...itemBytes]);
},
deserialize: (bytes: Uint8Array, offset = 0) => {
const map: Map<UK, UV> = new Map();
if (typeof size === 'object' && bytes.slice(offset).length === 0) {
return [map, offset];
}
if (size === 'remainder') {
while (offset < bytes.length) {
const [deserializedKey, kOffset] = key.deserialize(bytes, offset);
offset = kOffset;
const [deserializedValue, vOffset] = value.deserialize(bytes, offset);
offset = vOffset;
map.set(deserializedKey, deserializedValue);
}
return [map, offset];
}
const [resolvedSize, newOffset] = getResolvedSize(size, bytes, offset);
offset = newOffset;
for (let i = 0; i < resolvedSize; i += 1) {
const [deserializedKey, kOffset] = key.deserialize(bytes, offset);
offset = kOffset;
const [deserializedValue, vOffset] = value.deserialize(bytes, offset);
offset = vOffset;
map.set(deserializedKey, deserializedValue);
}
return [map, offset];
},
};
}