-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8482b57
commit f86dcc5
Showing
6 changed files
with
356 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,7 @@ | |
"dotproduct", | ||
"dsname", | ||
"dspc", | ||
"dynamicmap", | ||
"embedder", | ||
"embedders", | ||
"envfiles", | ||
|
116 changes: 116 additions & 0 deletions
116
sdk/assemblyscript/src/assembly/__tests__/dynamicmap.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/* | ||
* Copyright 2024 Hypermode Inc. | ||
* Licensed under the terms of the Apache License, Version 2.0 | ||
* See the LICENSE file that accompanied this code for further details. | ||
* | ||
* SPDX-FileCopyrightText: 2024 Hypermode Inc. <[email protected]> | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { JSON } from "json-as"; | ||
import { expect, it, run } from "as-test"; | ||
import { DynamicMap } from "../dynamicmap"; | ||
|
||
|
||
@json | ||
class Obj { | ||
foo: string = ""; | ||
} | ||
|
||
it("should set values", () => { | ||
const m = new DynamicMap(); | ||
m.set("a", 42); | ||
m.set("b", "hello"); | ||
m.set("c", [1, 2, 3]); | ||
m.set("d", true); | ||
m.set("e", null); | ||
m.set("f", 3.14); | ||
m.set("g", { foo: "bar" } as Obj); | ||
|
||
const json = JSON.stringify(m); | ||
expect(json).toBe( | ||
'{"a":42,"b":"hello","c":[1,2,3],"d":true,"e":null,"f":3.14,"g":{"foo":"bar"}}', | ||
); | ||
}); | ||
|
||
it("should get values", () => { | ||
const m = JSON.parse<DynamicMap>( | ||
'{"a":42,"b":"hello","c":[1,2,3],"d":true,"e":null,"f":3.14,"g":{"foo":"bar"}}', | ||
); | ||
expect(m.get<i32>("a")).toBe(42); | ||
expect(m.get<string>("b")).toBe("hello"); | ||
expect(m.get<i32[]>("c")).toBe([1, 2, 3]); | ||
expect(m.get<bool>("d")).toBe(true); | ||
expect(m.get<Obj | null>("e")).toBe(null); | ||
expect(m.get<f64>("f")).toBe(3.14); | ||
|
||
const obj = m.get<Obj>("g"); | ||
expect(obj.foo).toBe("bar"); | ||
}); | ||
|
||
it("should get size", () => { | ||
const m = new DynamicMap(); | ||
expect(m.size).toBe(0); | ||
m.set("a", 42); | ||
expect(m.size).toBe(1); | ||
m.set("b", "hello"); | ||
expect(m.size).toBe(2); | ||
}); | ||
|
||
it("should test existence of keys", () => { | ||
const m = new DynamicMap(); | ||
expect(m.has("a")).toBe(false); | ||
m.set("a", 42); | ||
expect(m.has("a")).toBe(true); | ||
expect(m.has("b")).toBe(false); | ||
m.set("b", "hello"); | ||
expect(m.has("b")).toBe(true); | ||
}); | ||
|
||
it("should delete keys", () => { | ||
const m = new DynamicMap(); | ||
m.set("a", 42); | ||
m.set("b", "hello"); | ||
expect(m.size).toBe(2); | ||
expect(m.has("a")).toBe(true); | ||
expect(m.has("b")).toBe(true); | ||
m.delete("a"); | ||
expect(m.size).toBe(1); | ||
expect(m.has("a")).toBe(false); | ||
expect(m.has("b")).toBe(true); | ||
m.delete("b"); | ||
expect(m.size).toBe(0); | ||
expect(m.has("a")).toBe(false); | ||
expect(m.has("b")).toBe(false); | ||
}); | ||
|
||
it("should clear", () => { | ||
const m = new DynamicMap(); | ||
m.set("a", 42); | ||
m.set("b", "hello"); | ||
expect(m.size).toBe(2); | ||
m.clear(); | ||
expect(m.size).toBe(0); | ||
expect(m.has("a")).toBe(false); | ||
expect(m.has("b")).toBe(false); | ||
}); | ||
|
||
it("should iterate keys", () => { | ||
const m = new DynamicMap(); | ||
m.set("a", 42); | ||
m.set("b", "hello"); | ||
m.set("c", [1, 2, 3]); | ||
const keys = m.keys(); | ||
expect(keys).toBe(["a", "b", "c"]); | ||
}); | ||
|
||
it("should iterate raw values", () => { | ||
const m = new DynamicMap(); | ||
m.set("a", 42); | ||
m.set("b", "hello"); | ||
m.set("c", [1, 2, 3]); | ||
const values = m.values(); | ||
expect(values).toBe(["42", '"hello"', "[1,2,3]"]); | ||
}); | ||
|
||
run(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
/* | ||
* Copyright 2024 Hypermode Inc. | ||
* Licensed under the terms of the Apache License, Version 2.0 | ||
* See the LICENSE file that accompanied this code for further details. | ||
* | ||
* SPDX-FileCopyrightText: 2024 Hypermode Inc. <[email protected]> | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { JSON } from "json-as"; | ||
|
||
export class DynamicMap { | ||
private data: Map<string, JSON.Raw> = new Map<string, JSON.Raw>(); | ||
|
||
public get size(): i32 { | ||
return this.data.size; | ||
} | ||
|
||
public has(key: string): bool { | ||
return this.data.has(key); | ||
} | ||
|
||
public get<T>(key: string): T { | ||
return JSON.parse<T>(this.data.get(key)); | ||
} | ||
|
||
public set<T>(key: string, value: T): void { | ||
if (value == null) { | ||
this.data.set(key, "null"); | ||
} else { | ||
this.data.set(key, JSON.stringify(value)); | ||
} | ||
} | ||
|
||
public delete(key: string): bool { | ||
return this.data.delete(key); | ||
} | ||
|
||
public clear(): void { | ||
this.data.clear(); | ||
} | ||
|
||
public keys(): string[] { | ||
return this.data.keys(); | ||
} | ||
|
||
public values(): JSON.Raw[] { | ||
return this.data.values(); | ||
} | ||
|
||
__INITIALIZE(): this { | ||
return this; | ||
} | ||
|
||
__SERIALIZE(): string { | ||
// This would be ideal, but doesn't work: | ||
// return JSON.stringify(this.data); | ||
// So instead, we have to do construct the JSON manually. | ||
// | ||
// TODO: Update this to use JSON.stringify once it's supported in json-as. | ||
// https://github.com/JairusSW/as-json/issues/98 | ||
|
||
const segments: string[] = []; | ||
const keys = this.data.keys(); | ||
const values = this.data.values(); | ||
|
||
for (let i = 0; i < this.data.size; i++) { | ||
const key = JSON.stringify(keys[i]); | ||
const value = values[i]; // already in JSON | ||
segments.push(`${key}:${value}`); | ||
} | ||
|
||
return `{${segments.join(",")}}`; | ||
} | ||
|
||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
__DESERIALIZE( | ||
data: string, | ||
key_start: i32, | ||
key_end: i32, | ||
value_start: i32, | ||
value_end: i32, | ||
): boolean { | ||
// This would be ideal, but doesn't work: | ||
// this.data = JSON.parse<Map<string, JSON.Raw>>(data); | ||
// So instead, we have to parse the JSON manually. | ||
// | ||
// TODO: Update this to use JSON.parse once it's supported in json-as. | ||
// https://github.com/JairusSW/as-json/issues/98 | ||
|
||
this.data = parseJsonToRawMap(data); | ||
|
||
return true; | ||
} | ||
} | ||
|
||
// Everything below this line is a workaround for JSON.parse not working with JSON.Raw values. | ||
// (It was generated via AI and may not be optimized.) | ||
|
||
class ParseResult { | ||
constructor( | ||
public result: string, | ||
public nextIndex: i32, | ||
) {} | ||
} | ||
|
||
function parseJsonToRawMap(jsonString: string): Map<string, string> { | ||
const map = new Map<string, string>(); | ||
let i: i32 = 0; | ||
const length: i32 = jsonString.length; | ||
|
||
function skipWhitespace(index: i32, input: string): i32 { | ||
while ( | ||
index < input.length && | ||
(input.charCodeAt(index) === 32 || // space | ||
input.charCodeAt(index) === 9 || // tab | ||
input.charCodeAt(index) === 10 || // newline | ||
input.charCodeAt(index) === 13) // carriage return | ||
) { | ||
index++; | ||
} | ||
return index; | ||
} | ||
|
||
function readString(index: i32, input: string): ParseResult { | ||
if (input.charCodeAt(index) !== 34) { | ||
throw new Error("Expected '\"' at position " + index.toString()); | ||
} | ||
index++; | ||
let result = ""; | ||
while (index < input.length) { | ||
const charCode = input.charCodeAt(index); | ||
if (charCode === 34) { | ||
return new ParseResult(result, index + 1); | ||
} | ||
if (charCode === 92) { | ||
index++; | ||
if (index < input.length) { | ||
result += String.fromCharCode(input.charCodeAt(index)); | ||
} | ||
} else { | ||
result += String.fromCharCode(charCode); | ||
} | ||
index++; | ||
} | ||
throw new Error("Unterminated string"); | ||
} | ||
|
||
function readValue(index: i32, input: string): ParseResult { | ||
const start: i32 = index; | ||
let braceCount: i32 = 0; | ||
let bracketCount: i32 = 0; | ||
|
||
while (index < input.length) { | ||
const char = input.charAt(index); | ||
if ( | ||
braceCount === 0 && | ||
bracketCount === 0 && | ||
(char === "," || char === "}") | ||
) { | ||
break; | ||
} | ||
if (char === "{") braceCount++; | ||
else if (char === "}") braceCount--; | ||
else if (char === "[") bracketCount++; | ||
else if (char === "]") bracketCount--; | ||
index++; | ||
} | ||
|
||
const result = input.substring(start, index).trim(); | ||
return new ParseResult(result, index); | ||
} | ||
|
||
if (jsonString.charCodeAt(i) !== 123) { | ||
throw new Error("Expected '{' at the beginning of JSON"); | ||
} | ||
i++; | ||
|
||
while (i < length) { | ||
i = skipWhitespace(i, jsonString); | ||
|
||
const keyResult = readString(i, jsonString); | ||
const key = keyResult.result; | ||
i = keyResult.nextIndex; | ||
|
||
i = skipWhitespace(i, jsonString); | ||
if (jsonString.charCodeAt(i) !== 58) { | ||
throw new Error("Expected ':' after key at position " + i.toString()); | ||
} | ||
i++; | ||
|
||
i = skipWhitespace(i, jsonString); | ||
|
||
const valueResult = readValue(i, jsonString); | ||
const value = valueResult.result; | ||
i = valueResult.nextIndex; | ||
|
||
map.set(key, value); | ||
|
||
i = skipWhitespace(i, jsonString); | ||
if (jsonString.charCodeAt(i) === 44) { | ||
i++; | ||
} else if (jsonString.charCodeAt(i) === 125) { | ||
i++; | ||
break; | ||
} else { | ||
throw new Error( | ||
"Unexpected character '" + | ||
jsonString.charAt(i) + | ||
"' at position " + | ||
i.toString(), | ||
); | ||
} | ||
} | ||
|
||
return map; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/* | ||
* Copyright 2024 Hypermode Inc. | ||
* Licensed under the terms of the Apache License, Version 2.0 | ||
* See the LICENSE file that accompanied this code for further details. | ||
* | ||
* SPDX-FileCopyrightText: 2024 Hypermode Inc. <[email protected]> | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { readFileSync } from "fs"; | ||
import { instantiate } from "../build/dynamicmap.spec.js"; | ||
const binary = readFileSync("./build/dynamicmap.spec.wasm"); | ||
const module = new WebAssembly.Module(binary); | ||
instantiate(module, { | ||
env: {}, | ||
}); |