Skip to content

JSON RGA #902

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions src/json-crdt/nodes/json/JsonNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import {AbstractRga, type Chunk} from '../rga/AbstractRga';
import {type ITimestampStruct, tick} from '../../../json-crdt-patch/clock';
import {printBinary} from 'tree-dump/lib/printBinary';
import {printTree} from 'tree-dump/lib/printTree';
import type {Model} from '../../model';
import type {JsonNode, JsonNodeView} from '..';
import type {Printable} from 'tree-dump/lib/types';


class JsonToken {}
class JsonTextToken extends JsonToken {}
class JsonControlToken extends JsonToken {}
class JsonObjectStartToken extends JsonControlToken {}
class JsonObjectEntryDividerToken extends JsonControlToken {}
class JsonObjectEntryEndToken extends JsonControlToken {}
class JsonObjectEndToken extends JsonControlToken {}
class JsonCompositeToken extends JsonToken {}

type Token = JsonTextToken | JsonObjectStartToken | JsonObjectEntryDividerToken | JsonObjectEntryEndToken | JsonObjectEndToken | JsonCompositeToken;

class JsonChunkData {
public readonly data: Token[] = [];
}

/**
* @ignore
* @category CRDT Node
*/
export class JsonChunk implements Chunk<JsonChunkData> {
public readonly id: ITimestampStruct;
public span: number;
public del: boolean;
public data: JsonChunkData | undefined;
public len: number;
public p: JsonChunk | undefined;
public l: JsonChunk | undefined;
public r: JsonChunk | undefined;
public p2: JsonChunk | undefined;
public l2: JsonChunk | undefined;
public r2: JsonChunk | undefined;
public s: JsonChunk | undefined;

constructor(id: ITimestampStruct, span: number, data: JsonChunkData | undefined) {
this.id = id;
this.span = span;
this.len = data ? span : 0;
this.del = !data;
this.p = undefined;
this.l = undefined;
this.r = undefined;
this.s = undefined;
this.data = data;
}

public merge(data: JsonChunkData) {
throw new Error('not implemented');
// this.data!.push(...data);
// this.span = this.data!.length;
}

public split(ticks: number): JsonChunk {
throw new Error('not implemented');
// const span = this.span;
// this.span = ticks;
// if (!this.del) {
// const data = this.data!;
// const rightData = data.splice(ticks);
// const chunk = new JsonChunk(tick(this.id, ticks), span - ticks, rightData);
// return chunk;
// }
// return new JsonChunk(tick(this.id, ticks), span - ticks, undefined);
}

public delete(): void {
throw new Error('not implemented');
// this.del = true;
// this.data = undefined;
}

public clone(): JsonChunk {
throw new Error('not implemented');
// return new JsonChunk(this.id, this.span, this.data ? [...this.data] : undefined);
}

public view(): JsonChunkData {
throw new Error('not implemented');
// return this.data ? [...this.data] : [];
}
}

/**
* Represents the `arr` JSON CRDT type, which is a Replicated Growable Array
* (RGA). Each element ot the array is a reference to another JSON CRDT node.
*
* @category CRDT Node
*/
export class ArrNode<Element extends JsonNode = JsonNode>
extends AbstractRga<JsonChunkData>
implements JsonNode<JsonNodeView<Element>[]>, Printable
{
constructor(
public readonly doc: Model<any>,
id: ITimestampStruct,
) {
super(id);
}

// -------------------------------------------------------------- AbstractRga

/** @ignore */
public createChunk(id: ITimestampStruct, data: JsonChunkData | undefined): JsonChunk {
throw new Error('not implemented');
// return new JsonChunk(id, data ? data.length : 0, data);
}

/** @ignore */
protected onChange(): void {}

protected toStringName(): string {
return this.name();
}

// ----------------------------------------------------------------- JsonNode

/** @ignore */
public child() {
return undefined;
}

/** @ignore */
public container(): JsonNode | undefined {
return this;
}

/** @ignore */
private _view: unknown[] = [];
public view(): JsonNodeView<Element>[] {
throw new Error('not implemented');
}

/** @ignore */
public children(callback: (node: JsonNode) => void) {
const index = this.doc.index;
for (let chunk = this.first(); chunk; chunk = this.next(chunk)) {
const data = chunk.data;
if (!data) continue;
const length = data.length;
for (let i = 0; i < length; i++) callback(index.get(data[i])!);
}
}

/** @ignore */
public api: undefined | unknown = undefined;

public name(): string {
return 'arr';
}
}
23 changes: 14 additions & 9 deletions src/json-crdt/nodes/rga/AbstractRga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@ import {printTree} from 'tree-dump/lib/printTree';
import {printBinary} from 'tree-dump/lib/printBinary';
import {printOctets} from '@jsonjoy.com/util/lib/buffers/printOctets';

export interface ChunkData<T> {
length: number;
slice: (start: number, end: number) => ChunkData<T>;
}

/**
* @category CRDT Node
*/
export interface Chunk<T> {
export interface Chunk<T extends ChunkData<T>> {
/** Unique sortable ID of this chunk and its span. */
id: ITimestampStruct;
/** Length of the logical clock interval of this chunk. */
Expand Down Expand Up @@ -56,35 +61,35 @@ export interface Chunk<T> {
/** Return a deep copy of itself. */
clone(): Chunk<T>;
/** Return the data of the chunk, if not deleted. */
view(): T & {slice: (start: number, end: number) => T};
view(): T;
}

const compareById = (c1: Chunk<unknown>, c2: Chunk<unknown>): number => {
const compareById = (c1: Chunk<ChunkData<any>>, c2: Chunk<ChunkData<any>>): number => {
const ts1 = c1.id;
const ts2 = c2.id;
return ts1.sid - ts2.sid || ts1.time - ts2.time;
};

const updateLenOne = (chunk: Chunk<unknown>): void => {
const updateLenOne = (chunk: Chunk<ChunkData<any>>): void => {
const l = chunk.l;
const r = chunk.r;
chunk.len = (chunk.del ? 0 : chunk.span) + (l ? l.len : 0) + (r ? r.len : 0);
};

const updateLenOneLive = (chunk: Chunk<unknown>): void => {
const updateLenOneLive = (chunk: Chunk<ChunkData<any>>): void => {
const l = chunk.l;
const r = chunk.r;
chunk.len = chunk.span + (l ? l.len : 0) + (r ? r.len : 0);
};

const dLen = (chunk: Chunk<unknown> | undefined, delta: number): void => {
const dLen = (chunk: Chunk<ChunkData<any>> | undefined, delta: number): void => {
while (chunk) {
chunk.len += delta;
chunk = chunk.p;
}
};

const next = <T>(curr: Chunk<T>): Chunk<T> | undefined => {
const next = <T extends ChunkData<any>>(curr: Chunk<T>): Chunk<T> | undefined => {
const r = curr.r;
if (r) {
curr = r;
Expand All @@ -100,7 +105,7 @@ const next = <T>(curr: Chunk<T>): Chunk<T> | undefined => {
return p;
};

const prev = <T>(curr: Chunk<T>): Chunk<T> | undefined => {
const prev = <T extends ChunkData<any>>(curr: Chunk<T>): Chunk<T> | undefined => {
const l = curr.l;
if (l) {
curr = l;
Expand All @@ -119,7 +124,7 @@ const prev = <T>(curr: Chunk<T>): Chunk<T> | undefined => {
/**
* @category CRDT Node
*/
export abstract class AbstractRga<T> {
export abstract class AbstractRga<T extends ChunkData<any>> {
public root: Chunk<T> | undefined = undefined;
public ids: Chunk<T> | undefined = undefined;
public count: number = 0;
Expand Down
Loading