elricmann / render

experimental javascript UI library (frame rate consistency, task scheduling, batching, shared workers, stack-based virtual machine with DOM opcodes, optimizing bytecode IR)
https://npmjs.com/package/librender
MIT License
28 stars 0 forks source link

Feature Proposal: Reactive Primitives in `librender` #2

Open elricmann opened 1 month ago

elricmann commented 1 month ago

Feature Proposal: Reactive Primitives in librender

Summary

This proposal introduces reactive primitives to librender that allow tracking changes to ArrayBufferView objects, such as Uint8Array. The system will use two primary concepts, references and tracking, to create a Ref instance and enable tracking changes to specific byte ranges or individual bytes. This feature will enable efficient reactivity when dealing with binary data structures.

Motivation

Efficiently tracking changes in buffer-based data is essential for handling reactive programming patterns. By introducing a mechanism that allows tracking and responding to changes in specific byte ranges or entire buffers, this system will enhance the data flow and reactivity in librender, enabling users to manage buffer changes in a streamlined and optimized way.

References are extendable but unique. Multiple sources can write to a single ref with shared ownership, i.e. SharedRef, and partial ownership that could be dropped by the GC, i.e. RefWeak (disambiguation). In many cases, the user is required to know when to use each, although it is imperative to provide explanatory guides on this.

The only data types that are covered in this proposal are individual bytes, linear buffers and strings. Representing structural data will be covered in a separate proposal but will be along the lines of representing C structs in a linear memory with instances writing to specific offsets provided by the API in said proposal. v0.2 is not expected to cover this proposal although it should be part of versions preceding 1.0, contingent on the stability of librender-rs and the C header for targeting the IR.

Proposed API

Ref<T extends ArrayBufferView>

A generic class that wraps around any ArrayBufferView type (e.g., Uint8Array, Float32Array) and provides reactive primitives for managing data:

const _ = new Ref(new Uint8Array([10, 20, 30, 40]));
const ref = new Ref(new Uint8Array([10, 20, 30, 40])).track(() => {
  console.log("buffer modified");
});

ref.value = new Uint8Array([10, 20, 35, 40]); // triggers callback
const ref = new Ref(new Uint8Array([100, 200, 150, 250, 50]))
  .trackByteRange(1, 4, () => {
    console.log("byte range modified");
  });

ref.value = new Uint8Array([100, 200, 160, 250, 50]); // triggers callback for byte range 1-4.
const ref = new Ref(new Uint8Array([5, 10, 15, 20]));
console.log(Ref.compareUint8Array(ref, new Uint8Array([5, 10, 15, 20]))); // true
console.log(Ref.compareUint8Array(ref, new Uint8Array([5, 10, 25, 30]))); // false

Usage

import * as librender from "librender";

const ref = new Ref(new Uint8Array([...librender.utils.charCodes("hello")])).track(() => {
  console.log("string changed");
});

ref.value = new Uint8Array([...librender.utils.charCodes("h-llo")]);

This is ideally represented via a CharCodesRef that would:

Implications