mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-07-25 14:04:50 +00:00
refactor: use standard prettier formatting
This commit is contained in:
@@ -1,14 +1,19 @@
|
||||
import {describe, it, expect} from "vitest"
|
||||
import {deserializeActionArray, serializeActionArray} from "./action-array"
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { deserializeActionArray, serializeActionArray } from "./action-array";
|
||||
|
||||
describe("action array", () => {
|
||||
it("should work with number arrays", () => {
|
||||
expect(deserializeActionArray(serializeActionArray([62, 256, 1235]))).toEqual([62, 256, 1235])
|
||||
})
|
||||
expect(
|
||||
deserializeActionArray(serializeActionArray([62, 256, 1235])),
|
||||
).toEqual([62, 256, 1235]);
|
||||
});
|
||||
|
||||
it("should work with nested arrays", () => {
|
||||
expect(deserializeActionArray(serializeActionArray([[], [[]]]))).toEqual([[], [[]]])
|
||||
})
|
||||
expect(deserializeActionArray(serializeActionArray([[], [[]]]))).toEqual([
|
||||
[],
|
||||
[[]],
|
||||
]);
|
||||
});
|
||||
|
||||
it("should compress back and forth", () => {
|
||||
expect(
|
||||
@@ -23,31 +28,37 @@ describe("action array", () => {
|
||||
[43, 746, 634],
|
||||
[34, 63],
|
||||
[332, 34],
|
||||
])
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
it("should compress a full layout", () => {
|
||||
const layout = Object.freeze([
|
||||
Object.freeze([
|
||||
0, 0, 0, 0, 0, 53, 119, 45, 103, 122, 52, 107, 118, 109, 99, 51, 114, 36, 59, 101, 50, 105, 34, 46,
|
||||
111, 49, 39, 515, 44, 117, 0, 512, 514, 513, 550, 0, 319, 318, 321, 320, 326, 315, 314, 317, 316, 0,
|
||||
0, 0, 0, 0, 54, 98, 120, 536, 113, 55, 102, 112, 104, 100, 56, 97, 296, 544, 116, 57, 108, 299, 106,
|
||||
110, 48, 121, 297, 61, 115, 0, 518, 516, 517, 553, 0, 336, 338, 335, 337, 0, 325, 322, 323, 324,
|
||||
0, 0, 0, 0, 0, 53, 119, 45, 103, 122, 52, 107, 118, 109, 99, 51, 114,
|
||||
36, 59, 101, 50, 105, 34, 46, 111, 49, 39, 515, 44, 117, 0, 512, 514,
|
||||
513, 550, 0, 319, 318, 321, 320, 326, 315, 314, 317, 316, 0, 0, 0, 0, 0,
|
||||
54, 98, 120, 536, 113, 55, 102, 112, 104, 100, 56, 97, 296, 544, 116,
|
||||
57, 108, 299, 106, 110, 48, 121, 297, 61, 115, 0, 518, 516, 517, 553, 0,
|
||||
336, 338, 335, 337, 0, 325, 322, 323, 324,
|
||||
]),
|
||||
Object.freeze([
|
||||
0, 0, 0, 0, 0, 0, 91, 0, 0, 0, 0, 53, 0, 47, 52, 0, 51, 298, 0, 50, 0, 0, 127, 0, 49, 0, 0, 515, 0, 0,
|
||||
0, 512, 514, 513, 550, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 0, 536, 0, 0, 54, 0, 92,
|
||||
55, 0, 56, 296, 544, 57, 0, 96, 299, 0, 48, 0, 0, 297, 0, 0, 0, 518, 516, 517, 553, 0, 336, 338, 335,
|
||||
337, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 91, 0, 0, 0, 0, 53, 0, 47, 52, 0, 51, 298, 0, 50, 0,
|
||||
0, 127, 0, 49, 0, 0, 515, 0, 0, 0, 512, 514, 513, 550, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 0, 536, 0, 0, 54, 0, 92, 55, 0, 56,
|
||||
296, 544, 57, 0, 96, 299, 0, 48, 0, 0, 297, 0, 0, 0, 518, 516, 517, 553,
|
||||
0, 336, 338, 335, 337, 0, 0, 0, 0, 0,
|
||||
]),
|
||||
Object.freeze([
|
||||
0, 0, 0, 0, 0, 0, 64, 95, 43, 0, 0, 126, 38, 63, 40, 0, 35, 298, 36, 123, 0, 33, 127, 37, 60, 0, 34,
|
||||
515, 0, 0, 0, 512, 514, 513, 550, 0, 333, 331, 330, 334, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 536,
|
||||
0, 0, 94, 58, 124, 41, 0, 42, 296, 544, 125, 0, 126, 299, 0, 62, 0, 0, 297, 0, 0, 0, 518, 516, 517,
|
||||
553, 0, 336, 338, 335, 337, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 64, 95, 43, 0, 0, 126, 38, 63, 40, 0, 35, 298, 36,
|
||||
123, 0, 33, 127, 37, 60, 0, 34, 515, 0, 0, 0, 512, 514, 513, 550, 0,
|
||||
333, 331, 330, 334, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 536, 0, 0,
|
||||
94, 58, 124, 41, 0, 42, 296, 544, 125, 0, 126, 299, 0, 62, 0, 0, 297, 0,
|
||||
0, 0, 518, 516, 517, 553, 0, 336, 338, 335, 337, 0, 0, 0, 0, 0,
|
||||
]),
|
||||
])
|
||||
]);
|
||||
|
||||
expect(deserializeActionArray(serializeActionArray(layout as number[][]))).toEqual(layout)
|
||||
})
|
||||
})
|
||||
expect(
|
||||
deserializeActionArray(serializeActionArray(layout as number[][])),
|
||||
).toEqual(layout);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,55 +1,63 @@
|
||||
import {compressActions, decompressActions} from "../serialization/actions"
|
||||
import {CHARA_FILE_TYPES} from "../share/share-url"
|
||||
import { compressActions, decompressActions } from "../serialization/actions";
|
||||
import { CHARA_FILE_TYPES } from "../share/share-url";
|
||||
|
||||
export type ActionArray = number[] | ActionArray[]
|
||||
export type ActionArray = number[] | ActionArray[];
|
||||
export function serializeActionArray(array: ActionArray): Uint8Array {
|
||||
let out = new Uint8Array(5)
|
||||
const writer = new DataView(out.buffer)
|
||||
writer.setUint32(0, array.length)
|
||||
let out = new Uint8Array(5);
|
||||
const writer = new DataView(out.buffer);
|
||||
writer.setUint32(0, array.length);
|
||||
|
||||
if (array.length === 0) {
|
||||
return out
|
||||
return out;
|
||||
} else if (typeof array[0] === "number") {
|
||||
writer.setUint8(4, CHARA_FILE_TYPES.indexOf("number"))
|
||||
const compressed = compressActions(array as number[])
|
||||
writer.setUint32(0, compressed.length)
|
||||
return concatUint8Arrays(out, compressed)
|
||||
writer.setUint8(4, CHARA_FILE_TYPES.indexOf("number"));
|
||||
const compressed = compressActions(array as number[]);
|
||||
writer.setUint32(0, compressed.length);
|
||||
return concatUint8Arrays(out, compressed);
|
||||
} else if (Array.isArray(array[0])) {
|
||||
writer.setUint8(4, CHARA_FILE_TYPES.indexOf("array"))
|
||||
return concatUint8Arrays(out, ...(array as ActionArray[]).map(serializeActionArray))
|
||||
writer.setUint8(4, CHARA_FILE_TYPES.indexOf("array"));
|
||||
return concatUint8Arrays(
|
||||
out,
|
||||
...(array as ActionArray[]).map(serializeActionArray),
|
||||
);
|
||||
} else {
|
||||
throw new Error("Not implemented")
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
export function deserializeActionArray(raw: Uint8Array, cursor = {pos: 0}): ActionArray {
|
||||
const reader = new DataView(raw.buffer)
|
||||
const length = reader.getUint32(cursor.pos)
|
||||
cursor.pos += 4
|
||||
const type = CHARA_FILE_TYPES[reader.getUint8(cursor.pos)]
|
||||
cursor.pos++
|
||||
export function deserializeActionArray(
|
||||
raw: Uint8Array,
|
||||
cursor = { pos: 0 },
|
||||
): ActionArray {
|
||||
const reader = new DataView(raw.buffer);
|
||||
const length = reader.getUint32(cursor.pos);
|
||||
cursor.pos += 4;
|
||||
const type = CHARA_FILE_TYPES[reader.getUint8(cursor.pos)];
|
||||
cursor.pos++;
|
||||
|
||||
if (type === "number") {
|
||||
const decompressed = decompressActions(raw.slice(cursor.pos, cursor.pos + length))
|
||||
cursor.pos += length
|
||||
return decompressed
|
||||
const decompressed = decompressActions(
|
||||
raw.slice(cursor.pos, cursor.pos + length),
|
||||
);
|
||||
cursor.pos += length;
|
||||
return decompressed;
|
||||
} else if (type === "array") {
|
||||
const out = []
|
||||
const out = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
out.push(deserializeActionArray(raw, cursor))
|
||||
out.push(deserializeActionArray(raw, cursor));
|
||||
}
|
||||
return out
|
||||
return out;
|
||||
} else {
|
||||
throw new Error("Not implemented")
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
export function concatUint8Arrays(...arrays: Uint8Array[]): Uint8Array {
|
||||
const out = new Uint8Array(arrays.reduce((a, b) => a + b.length, 0))
|
||||
let offset = 0
|
||||
const out = new Uint8Array(arrays.reduce((a, b) => a + b.length, 0));
|
||||
let offset = 0;
|
||||
for (const array of arrays) {
|
||||
out.set(array, offset)
|
||||
offset += array.length
|
||||
out.set(array, offset);
|
||||
offset += array.length;
|
||||
}
|
||||
return out
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
export interface CharaFile<T extends string> {
|
||||
charaVersion: 1
|
||||
type: T
|
||||
charaVersion: 1;
|
||||
type: T;
|
||||
}
|
||||
|
||||
export interface CharaLayoutFile extends CharaFile<"layout"> {
|
||||
device?: "ONE" | "LITE" | string
|
||||
layout: [number[], number[], number[]]
|
||||
device?: "ONE" | "LITE" | string;
|
||||
layout: [number[], number[], number[]];
|
||||
}
|
||||
|
||||
export interface CharaChordFile extends CharaFile<"chords"> {
|
||||
chords: [number[], number[]][]
|
||||
chords: [number[], number[]][];
|
||||
}
|
||||
|
||||
export interface CharaSettingsFile extends CharaFile<"settings"> {
|
||||
settings: number[]
|
||||
settings: number[];
|
||||
}
|
||||
|
||||
export interface CharaBackupFile extends CharaFile<"backup"> {
|
||||
history: [CharaChordFile, CharaLayoutFile, CharaSettingsFile][]
|
||||
history: [CharaChordFile, CharaLayoutFile, CharaSettingsFile][];
|
||||
}
|
||||
|
||||
export type CharaFiles = CharaLayoutFile | CharaChordFile | CharaSettingsFile
|
||||
export type CharaFiles = CharaLayoutFile | CharaChordFile | CharaSettingsFile;
|
||||
|
||||
+43
-28
@@ -1,13 +1,19 @@
|
||||
import type {CharaFile, CharaFiles} from "../share/chara-file"
|
||||
import type {ActionArray} from "../share/action-array"
|
||||
import {deserializeActionArray, serializeActionArray} from "../share/action-array"
|
||||
import {fromBase64, toBase64} from "../serialization/base64"
|
||||
import type { CharaFile, CharaFiles } from "../share/chara-file";
|
||||
import type { ActionArray } from "../share/action-array";
|
||||
import {
|
||||
deserializeActionArray,
|
||||
serializeActionArray,
|
||||
} from "../share/action-array";
|
||||
import { fromBase64, toBase64 } from "../serialization/base64";
|
||||
|
||||
type CharaLayoutOrder = {
|
||||
[K in CharaFiles["type"]]: Array<
|
||||
[Exclude<keyof Extract<CharaFiles, {type: K}>, keyof CharaFile<any>>, (typeof CHARA_FILE_TYPES)[number]]
|
||||
>
|
||||
}
|
||||
[
|
||||
Exclude<keyof Extract<CharaFiles, { type: K }>, keyof CharaFile<any>>,
|
||||
(typeof CHARA_FILE_TYPES)[number],
|
||||
]
|
||||
>;
|
||||
};
|
||||
|
||||
const keys: CharaLayoutOrder = {
|
||||
layout: [
|
||||
@@ -16,51 +22,60 @@ const keys: CharaLayoutOrder = {
|
||||
],
|
||||
chords: [["chords", "array"]],
|
||||
settings: [["settings", "array"]],
|
||||
}
|
||||
};
|
||||
|
||||
export const CHARA_FILE_TYPES = ["unknown", "number", "string", "array"] as const
|
||||
export const CHARA_FILE_TYPES = [
|
||||
"unknown",
|
||||
"number",
|
||||
"string",
|
||||
"array",
|
||||
] as const;
|
||||
|
||||
const sep = "\n"
|
||||
const sep = "\n";
|
||||
|
||||
export async function charaFileToUriComponent<T extends CharaFiles>(file: T): Promise<string> {
|
||||
let url = `${file.type}${sep}${file.charaVersion}`
|
||||
export async function charaFileToUriComponent<T extends CharaFiles>(
|
||||
file: T,
|
||||
): Promise<string> {
|
||||
let url = `${file.type}${sep}${file.charaVersion}`;
|
||||
|
||||
for (const [key, type] of keys[file.type]) {
|
||||
const value = file[key as keyof T]
|
||||
url += sep
|
||||
const value = file[key as keyof T];
|
||||
url += sep;
|
||||
if (type === "string") {
|
||||
url += value as string
|
||||
url += value as string;
|
||||
} else if (type === "array") {
|
||||
const stream = new Blob([serializeActionArray(value as ActionArray)])
|
||||
.stream()
|
||||
.pipeThrough(new CompressionStream("deflate"))
|
||||
url += await toBase64(await new Response(stream).blob())
|
||||
.pipeThrough(new CompressionStream("deflate"));
|
||||
url += await toBase64(await new Response(stream).blob());
|
||||
} else {
|
||||
throw new Error("Not implemented")
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
return url
|
||||
return url;
|
||||
}
|
||||
|
||||
export async function charaFileFromUriComponent<T extends CharaFiles>(
|
||||
uriComponent: string,
|
||||
fetch = window.fetch,
|
||||
): Promise<T> {
|
||||
const [fileType, version, ...values] = uriComponent.split(sep)
|
||||
const file: any = {type: fileType, charaVersion: Number(version)}
|
||||
const [fileType, version, ...values] = uriComponent.split(sep);
|
||||
const file: any = { type: fileType, charaVersion: Number(version) };
|
||||
|
||||
for (const [key, type] of keys[fileType as keyof typeof keys]) {
|
||||
const value = values.shift()!
|
||||
const value = values.shift()!;
|
||||
if (type === "string") {
|
||||
file[key] = value
|
||||
file[key] = value;
|
||||
} else if (type === "array") {
|
||||
const stream = (await fromBase64(value, fetch)).stream().pipeThrough(new DecompressionStream("deflate"))
|
||||
const actions = new Uint8Array(await new Response(stream).arrayBuffer())
|
||||
console.log(actions)
|
||||
file[key] = deserializeActionArray(actions)
|
||||
const stream = (await fromBase64(value, fetch))
|
||||
.stream()
|
||||
.pipeThrough(new DecompressionStream("deflate"));
|
||||
const actions = new Uint8Array(await new Response(stream).arrayBuffer());
|
||||
console.log(actions);
|
||||
file[key] = deserializeActionArray(actions);
|
||||
}
|
||||
}
|
||||
|
||||
return file
|
||||
return file;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user