Skip to content

Commit

Permalink
Store maps directly as objects
Browse files Browse the repository at this point in the history
  • Loading branch information
syyyr committed Sep 2, 2024
1 parent 1a5b605 commit 50ec8ae
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 112 deletions.
8 changes: 0 additions & 8 deletions deno.json

This file was deleted.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "libshv-js",
"version": "3.1.6",
"version": "3.2.0",
"description": "Typescript implementation of libshv",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
Expand Down
57 changes: 34 additions & 23 deletions src/chainpack.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {utf8ToString} from './cpon';
import {type RpcValue, type RpcValueType, type DateTime, Decimal, Double, IMap, Int, MetaMap, RpcValueWithMetaData, ShvMap, UInt, withOffset} from './rpcvalue';
import {type RpcValue, type RpcValueType, type DateTime, Decimal, Double, type IMap, Int, type MetaMap, RpcValueWithMetaData, type ShvMap, UInt, withOffset, shvMapType} from './rpcvalue';
import {UnpackContext, PackContext} from './cpcontext';

enum PackingSchema {
Expand Down Expand Up @@ -297,19 +297,24 @@ class ChainPackReader {
}

readMetaMap() {
return this.implReadMap(MetaMap);
return this.implReadMap('metamap');
}

readMap() {
return this.implReadMap(ShvMap);
return this.implReadMap('map');
}

readIMap() {
return this.implReadMap(IMap);
return this.implReadMap('imap');
}

private implReadMap<MapType extends MetaMap | ShvMap | IMap>(MapTypeCtor: new () => MapType) {
const map = new MapTypeCtor();
private implReadMap(map_type: 'map'): ShvMap;
private implReadMap(map_type: 'imap'): IMap;
private implReadMap(map_type: 'metamap'): MetaMap;
private implReadMap(map_type: 'map' | 'imap' | 'metamap') {
const map: ShvMap | IMap | MetaMap = {
[shvMapType]: map_type,
};
while (true) {
const b = this.ctx.peekByte();
if (b as PackingSchema === PackingSchema.Term) {
Expand All @@ -318,14 +323,14 @@ class ChainPackReader {
}
const key = this.read();
const val = this.read();
if (map instanceof MetaMap && typeof key === 'string') {
map.value[key] = val;
} else if (map instanceof MetaMap && key instanceof Int) {
map.value[Number(key)] = val;
} else if (map instanceof ShvMap && typeof key === 'string') {
map.value[key] = val;
} else if (map instanceof IMap && key instanceof Int) {
map.value[Number(key)] = val;
if (map[shvMapType] === 'metamap' && typeof key === 'string') {
map[key] = val;
} else if (map[shvMapType] === 'metamap' && key instanceof Int) {
map[Number(key)] = val;
} else if (map[shvMapType] === 'map' && typeof key === 'string') {
map[key] = val;
} else if (map[shvMapType] === 'imap' && key instanceof Int) {
map[Number(key)] = val;
} else {
throw new TypeError('Malformed map, invalid key');
}
Expand Down Expand Up @@ -371,15 +376,21 @@ class ChainPackWriter {
case Array.isArray(rpc_val):
this.writeList(rpc_val);
break;
case rpc_val instanceof IMap:
this.writeIMap(rpc_val);
break;
case rpc_val instanceof ShvMap:
this.writeMap(rpc_val);
break;
case rpc_val instanceof Date:
this.writeDateTime(rpc_val);
break;
case rpc_val instanceof Double:
throw new Error('writing doubles not implemented');
case typeof rpc_val === 'object':
switch (rpc_val[shvMapType]) {
case 'imap':
this.writeIMap(rpc_val);
break;
case 'map':
this.writeMap(rpc_val);
break;
}
break;
default:
console.log('Can\'t serialize', rpc_val);
throw new Error('Can\'t serialize');
Expand Down Expand Up @@ -545,19 +556,19 @@ class ChainPackWriter {
}

writeMapContent(map: MetaMap | ShvMap | IMap) {
for (const [key, value] of Object.entries(map.value)) {
for (const [key, value] of Object.entries(map)) {
if (value === undefined) {
continue;
}

if (map instanceof IMap) {
if (map[shvMapType] === 'imap') {
const int_key = Number(key);
if (Number.isNaN(int_key)) {
throw new TypeError('Invalid NaN IMap key');
}

this.writeInt(new Int(int_key));
} else if (map instanceof MetaMap) {
} else if (map[shvMapType] === 'metamap') {
const int_key = Number(key);
if (Number.isNaN(int_key)) {
this.writeJSString(key.toString());
Expand Down
59 changes: 34 additions & 25 deletions src/cpon.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {type RpcValue, type RpcValueType, type DateTime, type List, Decimal, Double, IMap, Int, MetaMap, RpcValueWithMetaData, ShvMap, UInt, withOffset} from './rpcvalue';
import {type RpcValue, type RpcValueType, type DateTime, type List, Decimal, Double, type IMap, Int, type MetaMap, RpcValueWithMetaData, type ShvMap, UInt, withOffset, shvMapType, isShvMap, isIMap} from './rpcvalue';
import {PackContext, UnpackContext} from './cpcontext';

const hexify = (byte: number) => {
Expand Down Expand Up @@ -412,15 +412,15 @@ class CponReader {
}

readMetaMap() {
return this.implReadMap(MetaMap, '>'.codePointAt(0)!);
return this.implReadMap('metamap', '>'.codePointAt(0)!);
}

readMap() {
return this.implReadMap(ShvMap, '}'.codePointAt(0)!);
return this.implReadMap('map', '}'.codePointAt(0)!);
}

readIMap() {
return this.implReadMap(IMap, '}'.codePointAt(0)!);
return this.implReadMap('imap', '}'.codePointAt(0)!);
}

readInt() {
Expand Down Expand Up @@ -550,8 +550,13 @@ class CponReader {
return new Int(is_neg ? -mantisa : mantisa);
}

private implReadMap<MapType extends MetaMap | ShvMap | IMap>(MapTypeCtor: new () => MapType, terminator: number) {
const map = new MapTypeCtor();
private implReadMap(map_type: 'map', terminator: number): ShvMap;
private implReadMap(map_type: 'imap', terminator: number): IMap;
private implReadMap(map_type: 'metamap', terminator: number): MetaMap;
private implReadMap(map_type: 'map' | 'imap' | 'metamap', terminator: number) {
const map: MetaMap | ShvMap | IMap = {
[shvMapType]: map_type,
};
this.ctx.getByte(); // eat start
while (true) {
this.skipWhitespace();
Expand All @@ -568,14 +573,14 @@ class CponReader {
this.skipWhitespace();
const val = this.read();

if (map instanceof MetaMap && typeof key === 'string') {
map.value[key] = val;
} else if (map instanceof MetaMap && (key instanceof UInt || key instanceof Int)) {
map.value[Number(key)] = val;
} else if (map instanceof ShvMap && typeof key === 'string') {
map.value[key] = val;
} else if (map instanceof IMap && (key instanceof UInt || key instanceof Int)) {
map.value[Number(key)] = val;
if (map[shvMapType] === 'metamap' && typeof key === 'string') {
map[key] = val;
} else if (map[shvMapType] === 'metamap' && (key instanceof UInt || key instanceof Int)) {
map[Number(key)] = val;
} else if (map[shvMapType] === 'map' && typeof key === 'string') {
map[key] = val;
} else if (map[shvMapType] === 'imap' && (key instanceof UInt || key instanceof Int)) {
map[Number(key)] = val;
} else {
throw new TypeError('Malformed map, invalid key');
}
Expand Down Expand Up @@ -625,15 +630,19 @@ class CponWriter {
case Array.isArray(rpc_val):
this.writeList(rpc_val);
break;
case rpc_val instanceof IMap:
this.writeIMap(rpc_val);
break;
case rpc_val instanceof ShvMap:
this.writeMap(rpc_val);
break;
case rpc_val instanceof Date:
this.writeDateTime(rpc_val);
break;
case typeof rpc_val === 'object':
switch (rpc_val[shvMapType]) {
case 'imap':
this.writeIMap(rpc_val);
break;
case 'map':
this.writeMap(rpc_val);
break;
}
break;
default:
console.log('Can\'t serialize rpc value', rpc_val);
throw new Error('Can\'t serialize rpc value');
Expand Down Expand Up @@ -761,7 +770,7 @@ class CponWriter {
this.increaseIndentIfNotOneLiner(map);
this.doIndentIfNotOneliner(map);
let first = true;
for (const [key, value] of Object.entries(map.value)) {
for (const [key, value] of Object.entries(map)) {
if (value === undefined) {
continue;
}
Expand All @@ -771,14 +780,14 @@ class CponWriter {
}
first = false;

if (map instanceof IMap) {
if (map[shvMapType] === 'imap') {
const int_key = Number(key);
if (Number.isNaN(int_key)) {
throw new TypeError('Invalid NaN IMap key');
}

this.writeInt(new Int(int_key));
} else if (map instanceof MetaMap) {
} else if (map[shvMapType] === 'metamap') {
const int_key = Number(key);
if (Number.isNaN(int_key)) {
this.ctx.putByte('"'.codePointAt(0)!);
Expand Down Expand Up @@ -881,10 +890,10 @@ class CponWriter {
const keyThreshold = Array.isArray(value) ? 10 : 5;

if (Array.isArray(value)) {
return value.length <= keyThreshold && !(value.some(x => Array.isArray(x) || x instanceof ShvMap || x instanceof IMap));
return value.length <= keyThreshold && !(value.some(x => Array.isArray(x) || isShvMap(x) || isIMap(x)));
}

return Object.keys(value.value).length <= keyThreshold && !(Object.values(value.value).some(x => Array.isArray(x) || x instanceof ShvMap || x instanceof IMap));
return Object.keys(value).length <= keyThreshold && !(Object.values(value).some(x => Array.isArray(x) || isShvMap(x) || isIMap(x)));
}

private increaseIndentIfNotOneLiner(value: MetaMap | ShvMap | IMap | List) {
Expand Down
Loading

0 comments on commit 50ec8ae

Please sign in to comment.