mirror of
https://github.com/fluencelabs/assemblyscript-json
synced 2025-04-24 22:32:20 +00:00
Move JSON encoder from test to it’s proper place
This commit is contained in:
parent
5f3f5c809c
commit
791c1d7012
@ -1,131 +1,119 @@
|
|||||||
const START_SIZE = 32;
|
|
||||||
// Growth should be aggressive as we don't free old buffer
|
|
||||||
const GROWTH_MULT = 2;
|
|
||||||
|
|
||||||
declare function logStr(str: string): void;
|
declare function logStr(str: string): void;
|
||||||
declare function logF64(val: f64): void;
|
declare function logF64(val: f64): void;
|
||||||
|
|
||||||
export class BSONEncoder {
|
export class JSONEncoder {
|
||||||
offsets: Array<i32> = new Array<i32>();
|
private isFirstKey: boolean = true
|
||||||
buffer: Uint8Array = new Uint8Array(START_SIZE)
|
private inObject: Array<boolean> = [false]
|
||||||
writeIndex: i32 = 4 // Make place for total size
|
private result: string = ""
|
||||||
|
|
||||||
serialize(): Uint8Array {
|
serialize(): Uint8Array {
|
||||||
this.writeByte(0);
|
// TODO: Write directly to UTF8 bytes
|
||||||
this.int32(this.writeIndex, 0);
|
let utf8ptr = this.result.toUTF8();
|
||||||
return this.buffer.subarray(0, this.writeIndex);
|
let buffer = new Uint8Array(this.result.lengthUTF8);
|
||||||
|
for (let i = 0; i < buffer.length; i++) {
|
||||||
|
buffer[i] = load<u8>(utf8ptr + i);
|
||||||
|
}
|
||||||
|
return buffer.subarray(0, buffer.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
setString(name: string, value: string): void {
|
setString(name: string, value: string): void {
|
||||||
this.writeByte(0x02); // BSON type: String
|
this.writeKey(name);
|
||||||
this.cstring(name);
|
this.writeString(value);
|
||||||
let startOffset = this.writeIndex;
|
|
||||||
this.writeIndex += 4;
|
|
||||||
this.cstring(value);
|
|
||||||
this.int32(this.writeIndex - startOffset - 4, startOffset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setBoolean(name: string, value: bool): void {
|
setBoolean(name: string, value: bool): void {
|
||||||
this.writeByte(0x08); // BSON type: Boolean
|
this.writeKey(name);
|
||||||
this.cstring(name);
|
this.writeBoolean(value);
|
||||||
this.writeByte(value ? 1 : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setNull(name: string): void {
|
setNull(name: string): void {
|
||||||
this.writeByte(0x0A); // BSON type: Null
|
this.writeKey(name);
|
||||||
this.cstring(name);
|
this.write("null");
|
||||||
}
|
}
|
||||||
|
|
||||||
setInteger(name: string, value: i32): void {
|
setInteger(name: string, value: i32): void {
|
||||||
this.writeByte(0x10); // BSON type: int32
|
this.writeKey(name);
|
||||||
this.cstring(name);
|
this.writeInteger(value);
|
||||||
this.int32(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setUint8Array(name: string, value: Uint8Array): void {
|
pushArray(name: string): bool {
|
||||||
this.writeByte(0x05); // BSON type: Binary data
|
this.writeKey(name);
|
||||||
this.cstring(name);
|
this.write("[");
|
||||||
this.int32(value.length);
|
this.isFirstKey = true
|
||||||
this.writeByte(0); // use generic binary subtype 0
|
this.inObject.push(false);
|
||||||
for (let i = 0; i < value.length; i++) {
|
return true;
|
||||||
this.writeByte(value[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pushArray(name: string): void {
|
|
||||||
this.writeByte(0x04); // BSON type: Array
|
|
||||||
this.cstring(name);
|
|
||||||
this.offsets.push(this.writeIndex);
|
|
||||||
this.writeIndex += 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
popArray(): void {
|
popArray(): void {
|
||||||
this.writeByte(0);
|
this.write("]");
|
||||||
let startOffset = this.offsets.pop();
|
|
||||||
this.int32(this.writeIndex - startOffset, startOffset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pushObject(name: string): void {
|
pushObject(name: string): bool {
|
||||||
this.writeByte(0x03); // BSON type: Document
|
this.writeKey(name);
|
||||||
this.cstring(name);
|
this.write("{");
|
||||||
this.offsets.push(this.writeIndex);
|
this.isFirstKey = true
|
||||||
this.writeIndex += 4;
|
this.inObject.push(true);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
popObject(): void {
|
popObject(): void {
|
||||||
this.writeByte(0);
|
this.write("}");
|
||||||
let startOffset = this.offsets.pop();
|
|
||||||
this.int32(this.writeIndex - startOffset, startOffset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private cstring(str: string): void {
|
private writeKey(str: string): void {
|
||||||
// TODO: Handle newlines properly
|
if (!this.isFirstKey ) {
|
||||||
// str = str.replace(/\r\n/g, '\n');
|
this.write(",");
|
||||||
// TODO: Maybe use AssemblyScript std Unicode conversion?
|
} else {
|
||||||
for (let i = 0, len = str.length; i < len; i++) {
|
this.isFirstKey = false;
|
||||||
let c = str.charCodeAt(i);
|
}
|
||||||
if (c < 128) {
|
if (str != null) {
|
||||||
this.writeByte(c);
|
this.writeString(str);
|
||||||
} else if (c < 2048) {
|
this.write(":");
|
||||||
this.writeByte((c >>> 6) | 192);
|
}
|
||||||
this.writeByte((c & 63) | 128);
|
}
|
||||||
} else {
|
|
||||||
this.writeByte((c >>> 12) | 224);
|
private writeString(str: string): void {
|
||||||
this.writeByte(((c >>> 6) & 63) | 128);
|
this.write('"');
|
||||||
this.writeByte((c & 63) | 128);
|
let savedIndex = 0;
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
let char = str.charCodeAt(i);
|
||||||
|
let needsEscaping = char < 0x20 || char == '"'.charCodeAt(0) || char == '\\'.charCodeAt(0);
|
||||||
|
if (needsEscaping) {
|
||||||
|
this.write(str.substring(savedIndex, i));
|
||||||
|
savedIndex = i + 1;
|
||||||
|
if (char == '"'.charCodeAt(0)) {
|
||||||
|
this.write('\\"');
|
||||||
|
} else if (char == "\\".charCodeAt(0)) {
|
||||||
|
this.write("\\\\");
|
||||||
|
} else if (char == "\b".charCodeAt(0)) {
|
||||||
|
this.write("\\b");
|
||||||
|
} else if (char == "\n".charCodeAt(0)) {
|
||||||
|
this.write("\\n");
|
||||||
|
} else if (char == "\r".charCodeAt(0)) {
|
||||||
|
this.write("\\r");
|
||||||
|
} else if (char == "\t".charCodeAt(0)) {
|
||||||
|
this.write("\\t");
|
||||||
|
} else {
|
||||||
|
// TODO: Implement encoding for other contol characters
|
||||||
|
assert(false, "Unsupported control chracter");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.writeByte(0);
|
this.write(str.substring(savedIndex, str.length));
|
||||||
|
this.write('"');
|
||||||
}
|
}
|
||||||
|
|
||||||
private int32(num: i32, offset: i32 = -1): void {
|
private writeBoolean(value: bool): void {
|
||||||
if (offset == -1) {
|
this.write(value ? "true" : "false");
|
||||||
this.growIfNeeded(4);
|
|
||||||
offset = this.writeIndex;
|
|
||||||
this.writeIndex += 4;
|
|
||||||
}
|
|
||||||
this.buffer[offset] = (num) & 0xff;
|
|
||||||
this.buffer[offset + 1] = (num >>> 8) & 0xff;
|
|
||||||
this.buffer[offset + 2] = (num >>> 16) & 0xff;
|
|
||||||
this.buffer[offset + 3] = (num >>> 24) & 0xff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private writeByte(b: u32): void {
|
private writeInteger(value: i32): void {
|
||||||
this.growIfNeeded(1);
|
// TODO: More efficient encoding
|
||||||
this.buffer[this.writeIndex++] = b;
|
let arr: Array<i32> = [value];
|
||||||
|
this.write(arr.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private growIfNeeded(numBytes: i32): void {
|
private write(str: string): void {
|
||||||
if (this.buffer.length >= this.writeIndex + numBytes) {
|
this.result += str;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let oldBuffer = this.buffer;
|
|
||||||
this.buffer = new Uint8Array(this.buffer.length * GROWTH_MULT);
|
|
||||||
for (let i = 0; i < oldBuffer.length; i++) {
|
|
||||||
this.buffer[i] = oldBuffer[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,126 +1,19 @@
|
|||||||
import "allocator/arena";
|
import "allocator/arena";
|
||||||
|
|
||||||
import { JSONDecoder, JSONHandler } from "../../assembly/decoder";
|
import { JSONDecoder } from "../../assembly/decoder";
|
||||||
|
import { JSONEncoder } from "../../assembly/encoder";
|
||||||
|
|
||||||
declare function logStr(str: string): void;
|
declare function logStr(str: string): void;
|
||||||
declare function logF64(val: f64): void;
|
declare function logF64(val: f64): void;
|
||||||
|
|
||||||
class JSONTestHandler extends JSONHandler {
|
|
||||||
isFirstKey: boolean = true
|
|
||||||
inObject: Array<boolean> = [false]
|
|
||||||
result: string = ""
|
|
||||||
|
|
||||||
setString(name: string, value: string): void {
|
|
||||||
this.writeKey(name);
|
|
||||||
this.writeString(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
setBoolean(name: string, value: bool): void {
|
|
||||||
this.writeKey(name);
|
|
||||||
this.writeBoolean(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
setNull(name: string): void {
|
|
||||||
this.writeKey(name);
|
|
||||||
this.write("null");
|
|
||||||
}
|
|
||||||
|
|
||||||
setInteger(name: string, value: i32): void {
|
|
||||||
this.writeKey(name);
|
|
||||||
this.writeInteger(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
pushArray(name: string): bool {
|
|
||||||
this.writeKey(name);
|
|
||||||
this.write("[");
|
|
||||||
this.isFirstKey = true
|
|
||||||
this.inObject.push(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
popArray(): void {
|
|
||||||
this.write("]");
|
|
||||||
}
|
|
||||||
|
|
||||||
pushObject(name: string): bool {
|
|
||||||
this.writeKey(name);
|
|
||||||
this.write("{");
|
|
||||||
this.isFirstKey = true
|
|
||||||
this.inObject.push(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
popObject(): void {
|
|
||||||
this.write("}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private writeKey(str: string): void {
|
|
||||||
if (!this.isFirstKey ) {
|
|
||||||
this.write(",");
|
|
||||||
} else {
|
|
||||||
this.isFirstKey = false;
|
|
||||||
}
|
|
||||||
if (str != null) {
|
|
||||||
this.writeString(str);
|
|
||||||
this.write(":");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private writeString(str: string): void {
|
|
||||||
this.write('"');
|
|
||||||
let savedIndex = 0;
|
|
||||||
for (let i = 0; i < str.length; i++) {
|
|
||||||
let char = str.charCodeAt(i);
|
|
||||||
let needsEscaping = char < 0x20 || char == '"'.charCodeAt(0) || char == '\\'.charCodeAt(0);
|
|
||||||
if (needsEscaping) {
|
|
||||||
this.write(str.substring(savedIndex, i));
|
|
||||||
savedIndex = i + 1;
|
|
||||||
if (char == '"'.charCodeAt(0)) {
|
|
||||||
this.write('\\"');
|
|
||||||
} else if (char == "\\".charCodeAt(0)) {
|
|
||||||
this.write("\\\\");
|
|
||||||
} else if (char == "\b".charCodeAt(0)) {
|
|
||||||
this.write("\\b");
|
|
||||||
} else if (char == "\n".charCodeAt(0)) {
|
|
||||||
this.write("\\n");
|
|
||||||
} else if (char == "\r".charCodeAt(0)) {
|
|
||||||
this.write("\\r");
|
|
||||||
} else if (char == "\t".charCodeAt(0)) {
|
|
||||||
this.write("\\t");
|
|
||||||
} else {
|
|
||||||
// TODO: Implement encoding for other contol characters
|
|
||||||
assert(false, "Unsupported control chracter");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.write(str.substring(savedIndex, str.length));
|
|
||||||
this.write('"');
|
|
||||||
}
|
|
||||||
|
|
||||||
private writeBoolean(value: bool): void {
|
|
||||||
this.write(value ? "true" : "false");
|
|
||||||
}
|
|
||||||
|
|
||||||
private writeInteger(value: i32): void {
|
|
||||||
// TODO: More efficient encoding
|
|
||||||
let arr: Array<i32> = [value];
|
|
||||||
this.write(arr.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private write(str: string): void {
|
|
||||||
this.result += str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class StringConversionTests {
|
export class StringConversionTests {
|
||||||
private static handler : JSONTestHandler = new JSONTestHandler();
|
private static handler : JSONEncoder = null;
|
||||||
|
|
||||||
static setUp(): void {
|
static setUp(): void {
|
||||||
this.handler = new JSONTestHandler();
|
this.handler = new JSONEncoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
static createDecoder(): JSONDecoder<JSONTestHandler> {
|
static createDecoder(): JSONDecoder<JSONEncoder> {
|
||||||
return new JSONDecoder(this.handler);
|
return new JSONDecoder(this.handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,24 +101,10 @@ export class StringConversionTests {
|
|||||||
buffer[i] = load<u8>(utf8ptr + i);
|
buffer[i] = load<u8>(utf8ptr + i);
|
||||||
}
|
}
|
||||||
this.createDecoder().deserialize(buffer);
|
this.createDecoder().deserialize(buffer);
|
||||||
assert(this.handler.result == expectedString,
|
let resultBuffer = this.handler.serialize();
|
||||||
"Expected:\n" + expectedString + "\n" + "Actual:\n" + this.handler.result);
|
let resultString = String.fromUTF8(resultBuffer.buffer.data, resultBuffer.length);
|
||||||
|
assert(resultString == expectedString,
|
||||||
|
"Expected:\n" + expectedString + "\n" + "Actual:\n" + resultString)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function bytes2array(typedArr: Uint8Array): Array<u8> {
|
|
||||||
let arr = new Array<u8>();
|
|
||||||
for (let i = 0; i < typedArr.length; i++) {
|
|
||||||
arr.push(typedArr[i]);
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hex2bin(hex: string): Uint8Array {
|
|
||||||
let bin = new Uint8Array(hex.length >>> 1);
|
|
||||||
for (let i = 0, len = hex.length >>> 1; i < len; i++) {
|
|
||||||
bin[i] = u32(parseInt(hex.substr(i << 1, 2), 16));
|
|
||||||
}
|
|
||||||
return bin;
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user