wip fix
This commit is contained in:
parent
bf1a6bd3a2
commit
6c579b3866
|
|
@ -1,11 +1,12 @@
|
|||
import { compress, decompress } from "wasm-gzip";
|
||||
import { apiProxy } from "../../../base/load/api/api-proxy";
|
||||
import { loadApiProxyDef } from "../../../base/load/api/api-proxy-def";
|
||||
import { dbProxy } from "../../../base/load/db/db-proxy";
|
||||
import { w } from "../../../utils/types/general";
|
||||
import { PG } from "../../ed/logic/ed-global";
|
||||
import { evalCJS } from "../../ed/logic/ed-sync";
|
||||
import { treeRebuild } from "../../ed/logic/tree/build";
|
||||
import { w } from "../../../utils/types/general";
|
||||
import { dbProxy } from "../../../base/load/db/db-proxy";
|
||||
import { apiProxy } from "../../../base/load/api/api-proxy";
|
||||
import { simpleHash } from "../utils/simple-hash";
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
export const viLoadSnapshot = async (p: PG) => {
|
||||
|
|
@ -22,7 +23,8 @@ export const viLoadSnapshot = async (p: PG) => {
|
|||
api: api.apiTypes,
|
||||
prisma: api.prismaTypes,
|
||||
});
|
||||
const hash = hashCode(zip);
|
||||
|
||||
const hash = simpleHash(zip);
|
||||
const res = await p.sync?.code.action({
|
||||
type: "check-typings",
|
||||
site_id: p.site.id,
|
||||
|
|
@ -84,11 +86,3 @@ export const applyEnv = (p: PG, src?: string) => {
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
const hashCode = function (s: string) {
|
||||
var h = 0,
|
||||
l = s.length,
|
||||
i = 0;
|
||||
if (l > 0) while (i < l) h = ((h << 5) - h + s.charCodeAt(i++)) | 0;
|
||||
return h;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
export const simpleHash = (str: string, seed = 0) => {
|
||||
let h1 = 0xdeadbeef ^ seed,
|
||||
h2 = 0x41c6ce57 ^ seed;
|
||||
for (let i = 0, ch; i < str.length; i++) {
|
||||
ch = str.charCodeAt(i);
|
||||
h1 = Math.imul(h1 ^ ch, 2654435761);
|
||||
h2 = Math.imul(h2 ^ ch, 1597334677);
|
||||
}
|
||||
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
|
||||
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
||||
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
|
||||
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
||||
|
||||
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
||||
};
|
||||
|
|
@ -13,10 +13,12 @@
|
|||
"lmdb": "^2.8.5",
|
||||
"mime": "^3.0.0",
|
||||
"pino": "^8.16.1",
|
||||
"msgpackr": "^1.10.1",
|
||||
"pino-pretty": "^10.2.3",
|
||||
"radash": "^11.0.0",
|
||||
"radix3": "^1.1.0",
|
||||
"typescript": "^5.2.2",
|
||||
"unzipper": "^0.10.14"
|
||||
"unzipper": "^0.10.14",
|
||||
"fast-myers-diff": "^3.2.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import { describe, expect, test } from "bun:test";
|
||||
import { Diff } from "./diff";
|
||||
|
||||
describe("simple diff", async () => {
|
||||
const server = await Diff.server("ini ionadi a");
|
||||
const patch = server.getPatch("new");
|
||||
|
||||
const client = await Diff.client(patch);
|
||||
|
||||
await server.update("rako12");
|
||||
|
||||
const newPatch = server.getPatch(client.ts);
|
||||
|
||||
await client.applyPatch(newPatch);
|
||||
expect(await client.data).toBe("rako12");
|
||||
});
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
import { applyPatch, calcPatch } from "fast-myers-diff";
|
||||
import { Packr } from "msgpackr";
|
||||
import { gunzip, gzip } from "zlib";
|
||||
|
||||
const MAX_HISTORY = 10;
|
||||
|
||||
const packr = new Packr({});
|
||||
|
||||
type PATCH_RESULT<T> =
|
||||
| {
|
||||
mode: "new";
|
||||
ts: string;
|
||||
data: number[];
|
||||
}
|
||||
| {
|
||||
mode: "patch";
|
||||
ts: string;
|
||||
diff: any;
|
||||
};
|
||||
|
||||
export class Diff<T> {
|
||||
ts = "";
|
||||
mode: "client" | "server" = "server";
|
||||
|
||||
private _data: number[] = [];
|
||||
private _history = {} as Record<string, number[]>;
|
||||
|
||||
constructor() {}
|
||||
|
||||
get data() {
|
||||
const _data = new Uint8Array(this._data);
|
||||
return new Promise<T>((done) => {
|
||||
if (typeof window === "undefined") {
|
||||
gunzipAsync(_data).then((result) => {
|
||||
done(packr.unpack(result));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async update(data: T) {
|
||||
if (this.mode === "server") {
|
||||
this._data = (await gzipAsync(packr.pack(data))).toJSON().data;
|
||||
this.ts =
|
||||
(Date.now() + "").substring(5) + Math.round(performance.now() * 10000);
|
||||
this._history[this.ts] = this._data;
|
||||
|
||||
let i = 0;
|
||||
for (const k of Object.keys(this._history).sort(
|
||||
(a, b) => parseInt(b) - parseInt(a)
|
||||
)) {
|
||||
if (i > MAX_HISTORY) {
|
||||
delete this._history[k];
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getPatch(ts: "new" | string): PATCH_RESULT<T> {
|
||||
if (ts !== "new") {
|
||||
const old_data = this._history[ts];
|
||||
|
||||
if (old_data) {
|
||||
const result_diff = [...calcPatch(old_data, this._data)];
|
||||
return { diff: result_diff, mode: "patch", ts: this.ts };
|
||||
}
|
||||
}
|
||||
|
||||
return { data: this._data, mode: "new", ts: this.ts };
|
||||
}
|
||||
|
||||
async applyPatch(patch: PATCH_RESULT<T>) {
|
||||
if (patch.mode === "new") {
|
||||
this.ts = patch.ts;
|
||||
if (patch.data) this._data = patch.data;
|
||||
} else {
|
||||
this.ts = patch.ts;
|
||||
const num_array = [];
|
||||
for (const num of applyPatch(this._data, patch.diff)) {
|
||||
if (Array.isArray(num)) {
|
||||
for (const n of num) {
|
||||
num_array.push(n);
|
||||
}
|
||||
} else {
|
||||
num_array.push(num);
|
||||
}
|
||||
}
|
||||
this._data = num_array;
|
||||
}
|
||||
}
|
||||
|
||||
static async server<T>(str: T) {
|
||||
const diff = new Diff<T>();
|
||||
await diff.update(str);
|
||||
return diff;
|
||||
}
|
||||
|
||||
static async client<T>(patch: PATCH_RESULT<T>) {
|
||||
const diff = new Diff<T>();
|
||||
diff.mode = "client";
|
||||
|
||||
if (patch.mode === "new") {
|
||||
diff.ts = patch.ts;
|
||||
if (patch.data) diff._data = patch.data;
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
|
||||
export const gzipAsync = (bin: Uint8Array | string) => {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
gzip(bin, (err, res) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const gunzipAsync = (bin: Uint8Array) => {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
gunzip(bin, (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
Loading…
Reference in New Issue