Skip to content

Instantly share code, notes, and snippets.

@roipeker
Created August 27, 2026 12:35
Show Gist options
  • Select an option

  • Save roipeker/bb9e5b4be96fc826fd8321545ff7ac89 to your computer and use it in GitHub Desktop.

Select an option

Save roipeker/bb9e5b4be96fc826fd8321545ff7ac89 to your computer and use it in GitHub Desktop.
Graphx Buffer
abstract base class _GNumericBuffer {
_GNumericBuffer(this._length, this._growable);
int _length;
final bool _growable;
int _version = 0;
int _dirtyStart = -1;
int _dirtyEnd = -1;
/// Number of logical elements currently exposed by this buffer.
int get length => _length;
/// Number of elements available in the current typed storage.
int get capacity;
/// Whether [setLength] and capacity growth are permitted.
bool get isGrowable => _growable;
/// Monotonic change counter for content notifications and structural changes.
///
/// Direct writes through [GFloat32Buffer.data], [GUint16Buffer.data], or
/// [GUint32Buffer.data] become visible to this counter only when the caller
/// explicitly invokes [markDirty].
int get version => _version;
/// Whether a content/storage range is currently dirty.
bool get isDirty => _dirtyStart >= 0;
/// Inclusive dirty start, or `-1` when [isDirty] is false.
int get dirtyStart => _dirtyStart;
/// Exclusive dirty end, or `-1` when [isDirty] is false.
int get dirtyEnd => _dirtyEnd;
/// Changes logical length without allocating.
///
/// Fixed buffers reject length changes. Growable buffers accept values only
/// through [capacity]; callers must reserve explicitly before exceeding it.
/// Growing within capacity marks the newly exposed range dirty. Shrinking
/// changes [version] but does not rewrite storage.
void setLength(int newLength) {
if (newLength < 0 || newLength > capacity) {
throw RangeError.range(newLength, 0, capacity, 'newLength');
}
if (newLength == _length) return;
if (!_growable) {
throw StateError('Fixed buffers cannot change logical length.');
}
final oldLength = _length;
_length = newLength;
_version++;
if (newLength > oldLength) {
_mergeDirty(oldLength, newLength);
} else {
_trimDirty(newLength);
}
}
/// Marks changed logical elements in `[start, end)`.
///
/// Multiple calls merge into one retained range and never allocate.
void markDirty(int start, int end) {
if (start < 0 || start > _length) {
throw RangeError.range(start, 0, _length, 'start');
}
if (end < start || end > _length) {
throw RangeError.range(end, start, _length, 'end');
}
if (start == end) return;
_mergeDirty(start, end);
_version++;
}
/// Marks the complete logical range dirty.
void markAllDirty() {
if (_length == 0) return;
_mergeDirty(0, _length);
_version++;
}
/// Clears dirty state without changing [version].
void clearDirty() {
_dirtyStart = -1;
_dirtyEnd = -1;
}
void _validateReserve(int newCapacity) {
_validateNonNegative(newCapacity, 'newCapacity');
if (newCapacity > capacity && !_growable) {
throw StateError('Fixed buffers cannot grow capacity.');
}
}
void _didReplaceStorage() {
_version++;
if (_length > 0) _mergeDirty(0, _length);
}
void _mergeDirty(int start, int end) {
if (start == end) return;
if (_dirtyStart < 0) {
_dirtyStart = start;
_dirtyEnd = end;
return;
}
if (start < _dirtyStart) _dirtyStart = start;
if (end > _dirtyEnd) _dirtyEnd = end;
}
void _trimDirty(int newLength) {
if (_dirtyStart < 0) return;
if (_dirtyStart >= newLength) {
clearDirty();
return;
}
if (_dirtyEnd > newLength) _dirtyEnd = newLength;
}
}
void _validateInitialState(int length, int capacity) {
_validateNonNegative(capacity, 'capacity');
if (length < 0 || length > capacity) {
throw RangeError.range(length, 0, capacity, 'length');
}
}
void _validateNonNegative(int value, String name) {
if (value < 0) {
throw RangeError.range(value, 0, null, name);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment