Binary Data (Blobs)¶
Blobs store raw binary payloads — images, meshes, buffers — under a typed
BlobLayout that records the element data type (float, int,
uchar, …) and its component count. A blob’s bytes live in a
ValueBlob; storing one in a database yields a content-addressable
ValueBlobId (the SHA-1 of layout + bytes), so identical payloads
deduplicate automatically.
When to use: reach for blobs when a value is best expressed as a packed
byte buffer rather than a structured Viper value. BlobLayout
describes the element shape; BlobEncoder packs typed elements into a
ValueBlob; BlobArray and BlobView
reinterpret those bytes as a sequence of typed elements; and the database blob
API (createBlob, blob, blobInfo) persists and retrieves them by id.
Quick Start¶
const {
BlobLayout, BlobEncoder, BlobArray, BlobArrayBuilder, ValueBlob, ValueBlobId,
Database,
} = require('@digitalsubstrate/dsviper');
// A layout: 3-component float elements (e.g. vec3 positions).
const layout = new BlobLayout('float', 3);
layout.representation(); // 'float-3'
layout.components(); // 3
layout.byteCount(); // 12 (3 * 4 bytes)
// Layouts also parse from their string form.
BlobLayout.parse('uchar-1').representation(); // 'uchar-1'
// Pack typed elements into a blob with an encoder, element by element. The Node
// BlobArray is a read-only view, so the bytes are built first, then reinterpreted.
const enc = new BlobEncoder(layout);
enc.write([1.0, 2.0, 3.0]); // one 3-component element
enc.write([4.0, 5.0, 6.0]);
const blob = enc.endEncoding(); // -> ValueBlob
// Reinterpret the bytes as a typed array; iterate to read elements back.
const array = new BlobArray(layout, blob);
const elems = [...array]; // ValueVec rows of 3 components
elems[0].at(0); // 1.0
// Or write the bytes whole, on a builder that owns them until build() seals
// them: layout and count are fixed at construction, so there is no resize.
const builder = new BlobArrayBuilder(layout, 2); // 2 * 3 floats = 24 bytes
builder.copy(Buffer.from(new Float32Array([1, 2, 3, 4, 5, 6]).buffer));
const sealed = builder.build(); // -> ValueBlob; the builder is spent
builder.isBuilt(); // true
// Raw bytes round-trip through ValueBlob directly.
const raw = new ValueBlob(Buffer.from([1, 2, 3, 4]));
raw.size(); // 4
raw.sha1(); // hex digest of the bytes
// Persist to a database: createBlob returns a content-addressable id.
const db = Database.createInMemory();
db.beginTransaction();
const blobId = db.createBlob(layout, blob);
db.commit();
const info = db.blobInfo(blobId);
Number(info.size()); // byte count (BlobInfo.size() is a BigInt)
info.blobLayout().representation(); // 'float-3'
db.blob(blobId).equals(blob); // true
db.close();
Content-addressable storage: createBlob hashes layout plus bytes,
so writing the same payload twice yields the same ValueBlobId and is
stored once. A different layout over the same bytes is a different id.
Choosing the right class¶
Need |
Class |
Example |
|---|---|---|
Raw byte payload |
|
|
Element shape (data type + components) |
|
|
Pack typed elements into bytes |
|
|
Fill the bytes of a blob, before it is a value |
|
|
Read bytes back as typed elements |
|
|
Database reference (content-addressed) |
|
|
Stored-blob metadata and stats |
|
|
Stream a large blob in chunks |
|
{tip}
The full database blob surface — createBlob, createBlobFromBuffer,
blobIds, blob, readBlob, blobInfo, blobInfos, blobStatistics, and
the blobStreamCreate/blobStreamAppend/blobStreamClose streaming API — is
documented on Database. The Python counterpart of this page is
Binary Data (Blobs).
{/tip}
Generated from the @digitalsubstrate/dsviper TypeScript declarations (index.d.ts) by TypeDoc.
Core Classes¶
Class |
Description |
|---|---|
Describes the layout of one element in a blob: a data type and a component count |
|
Reads blob as a flat array of blobLayout.dataType |
|
Fills the bytes of one typed array, then seals them into a ValueBlob |
|
Several memory regions packed into one blob, described by a BlobPackDescriptor |
|
Fills the regions a BlobPackDescriptor describes, then seals them into a ValueBlob a BlobPack reads |
|
Describes the binary regions a BlobPack will hold |
|
One region of a BlobPack - its name, the layout of its elements, how many it holds, and where it sits in the pack’s blob |
Blob I/O¶
Class |
Description |
|---|---|
The handle on a blob being written to a database past 2 GB, in chunks |
|
Builds a blob element by element: write(value) appends, endEncoding() returns the finished blob |
|
A BlobLayout together with the Viper types it reads as |
|
Interprets the bytes of a blob through its layout |
|
The bytes of one blob, with the blobId and layout that go with them |
Database Integration¶
Class |
Description |
|---|---|
Retrieves blobs from a persistence layer |
|
What a store knows about one blob without reading it: its id, layout, size, SQLite rowid, and whether it is chunked (over 2 GB) |
|
How many blobs a store holds and how big they are: count, total, smallest, largest, in bytes |
Reference¶
- class BlobLayout(dataType, components)¶
Describes the layout of one element in a blob: a data type and a component count.
Stored with a blob, the layout labels its bytes and is part of its id. Nothing checks their count against it until a BlobView or a BlobArray reads them, which refuses a size that is not a whole number of elements.
exported from
index.d- Arguments:
dataType (string | null)
components (number | null)
- BlobLayout.byteCount()¶
Return the size of an element in bytes.
- Returns:
number
- BlobLayout.compare(other)¶
Return -1, 0 or 1. Throws if
otheris not a BlobLayout.- Arguments:
other (BlobLayout)
- Returns:
number
- BlobLayout.components()¶
Return the number of components packed in one element.
- Returns:
number
- BlobLayout.dataType()¶
Return the code of the data type of one component, 0 to 10.
dataTypeRepresentation() renders it as uchar, ushort, uint, ulong, char, short, int, long, half, float or double, which is the spelling the constructor takes.
- Returns:
number
- BlobLayout.dataTypeByteCount()¶
Return the size of the data type in bytes.
- Returns:
number
- BlobLayout.dataTypeRepresentation()¶
- Return the name of the data type - uchar, ushort, uint, ulong, char, short, int,
long, half, float or double. This is what the constructor takes, where dataType() answers a code.
- Returns:
string
- BlobLayout.equals(other)¶
Return true if both are equal.
otherof another type answers false.- Arguments:
other (unknown)
- Returns:
boolean
- BlobLayout.glAttribParams()¶
- Return the gl.vertexAttribPointer parameter bundle for this layout. Throws for the
64-bit integer dataTypes (‘ulong’/’long’ - no GL vertex-attribute type).
- Returns:
GlAttribParams
- BlobLayout.representation()¶
Return a string representation.
- Returns:
string
- static BlobLayout.parse(representation)¶
Return a BlobLayout by parsing representation.
representation is ‘<dataType>-<components>’ - ‘uchar-1’, ‘float-3’, ‘double-16’. Throw if it is not two parts separated by a dash, if the data type is unknown, or if the component count is zero.
- Arguments:
representation (string)
- Returns:
BlobLayout
- class BlobArray(blobLayout, blob)¶
Reads blob as a flat array of blobLayout.dataType.
The blob must hold a whole number of elements - its size a multiple of blobLayout.byteCount() - or the constructor throws.
The binary reading of a blob: an index answers one datum, as a native scalar, and toTypedArray() hands out the whole run at once. Use BlobView to read the same bytes element by element.
Iteration walks the data as well, so it yields blobLayout.components() times what BlobView.count() reports over the same bytes.
A BlobArrayBuilder writes the bytes a blob is sealed with, and fromTypedArray reads client geometry instead.
exported from
index.d- Arguments:
blobLayout (BlobLayout)
blob (ValueBlob)
- BlobArray.[Symbol․iterator]()¶
Iterate the data lazily, as native scalars. blobView() iterates the elements.
- Returns:
Iterator<number | bigint>
- BlobArray.at(index)¶
Return the datum at the given index (out of range throws).
One datum, not one element: an element packs blobLayout.components() of them, so the index runs to the data count. A number, or a bigint for the 64-bit types, whose every bit is significant. Use blobView() to read elements as Values.
A negative index counts from the end, as
Array.prototype.atdoes:at(-1)is the last datum. Out of range still throws.- Arguments:
index (number)
- Returns:
number | bigint
- BlobArray.blob()¶
- Return the ValueBlob the array reads. The same bytes, not a copy - unlike
toTypedArray().
- Returns:
ValueBlob
- BlobArray.blobLayout()¶
- Return the BlobLayout of one element: its data type and how many components it
packs.
- Returns:
BlobLayout
- BlobArray.blobView()¶
Return a BlobView over the same bytes, indexed by element rather than by data.
- Returns:
BlobView
- BlobArray.dataCount()¶
- Return how many data the array holds: an element packs blobLayout.components()
data, and an index addresses one datum.
- Returns:
number
- BlobArray.toTypedArray()¶
- Project the whole blob as a flat TypedArray of its scalar components (copy-first,
GPU-ready). The kind follows the layout dataType. Requires a little-endian host.
- Returns:
BlobTypedArray
- static BlobArray.fromTypedArray(blobLayout, typedArray)¶
- Build a BlobArray from a TypedArray (client geometry -> Viper). The TypedArray kind
must match the layout dataType, and its byte length must be a whole number of elements (multiple of the layout stride). Requires a little-endian host.
- Arguments:
blobLayout (BlobLayout)
typedArray (BlobTypedArray)
- Returns:
BlobArray
- class BlobArrayBuilder(blobLayout, count)¶
Fills the bytes of one typed array, then seals them into a ValueBlob.
Write with copy(); build() hands the bytes over once. A blob’s bytes never change afterwards.
It is constructed in ELEMENTS and measured in DATA, which are the same number only when an element packs one datum: dataCount() is count() times blobLayout.components(), and byteCount() is dataCount() times the data type’s width. copy() takes the whole array at once, so the count a caller must get right is the TypedArray’s length - dataCount(), not count().
exported from
index.d- Arguments:
blobLayout (BlobLayout)
count (number)
- BlobArrayBuilder.blobLayout()¶
- Return the BlobLayout of one element: its data type and how many components it
packs.
- Returns:
BlobLayout
- BlobArrayBuilder.build()¶
Return the ValueBlob of the bytes written so far, and empty the builder.
The bytes are moved, not copied, so a value never shares them with a writer: every later write is refused.
- Returns:
ValueBlob
- BlobArrayBuilder.byteCount()¶
- Return how many bytes the array occupies: dataCount() times
blobLayout.dataTypeByteCount().
- Returns:
number
- BlobArrayBuilder.copy(buffer)¶
- Write buffer over the whole array: byteCount() bytes, which is a TypedArray of
dataCount() items of the layout data type, not count().
- Arguments:
buffer (BlobTypedArray | ArrayBuffer | Buffer<ArrayBufferLike>)
- BlobArrayBuilder.count()¶
Return how many elements the array holds, fixed at construction.
This is NOT how many values the array stores: an element packs blobLayout.components() data. dataCount() is that total.
- Returns:
number
- BlobArrayBuilder.dataCount()¶
Return how many data the array holds: count() times blobLayout.components().
The length the TypedArray given to copy() must have.
- Returns:
number
- BlobArrayBuilder.isBuilt()¶
Return whether build() has handed the bytes over to a blob.
- Returns:
boolean
- class BlobPack(descriptor)¶
Several memory regions packed into one blob, described by a BlobPackDescriptor.
Write with a BlobPackBuilder, whose build() answers the blob; fromBlob() reads that blob back. query() or check() then answers a BlobPackRegion, which says where its bytes sit - see its blob().
new BlobPack(descriptor) builds a pack whose regions are all zeros: the pack a BlobPackBuilder over descriptor answers before anything is written.
exported from
index.d- Arguments:
descriptor (BlobPackDescriptor)
- BlobPack.[Symbol․iterator]()¶
Iterate the region names, in registration order.
- Returns:
Iterator<string>
- BlobPack.blob()¶
Return the blob used to encode the blob pack.
- Returns:
ValueBlob
- BlobPack.check(name)¶
- Return the BlobPackRegion registered under name, or throw if the pack has no such
region.
- Arguments:
name (string)
- Returns:
BlobPackRegion
- BlobPack.query(name)¶
- Return the BlobPackRegion registered under name, or undefined if the pack has no such
region.
- Arguments:
name (string)
- Returns:
BlobPackRegion | undefined
- BlobPack.regions()¶
Return the BlobPackRegion list, in the order the descriptor registered them.
- Returns:
BlobPackRegion[]
- static BlobPack.fromBlob(blob)¶
Return a BlobPack from a blob or throw.
- Arguments:
blob (ValueBlob)
- Returns:
BlobPack
- class BlobPackBuilder(descriptor)¶
- Fills the regions a BlobPackDescriptor describes, then seals them into a
ValueBlob a BlobPack reads.
copy(name, buffer) writes one region whole; build() hands the bytes over once, and a blob’s bytes never change afterwards.
exported from
index.d- Arguments:
descriptor (BlobPackDescriptor)
- BlobPackBuilder.[Symbol․iterator]()¶
Iterate the region names, in registration order.
- Returns:
Iterator<string>
- BlobPackBuilder.build()¶
Return the ValueBlob of the regions written so far, and empty the builder.
Read it back with BlobPack.fromBlob. The bytes are moved, not copied, so a value never shares them with a writer: every later write is refused.
- Returns:
ValueBlob
- BlobPackBuilder.byteCount(name)¶
- Return how many bytes the region name occupies, or the whole pack when name is
undefined.
- Arguments:
name (string | null)
- Returns:
number
- BlobPackBuilder.copy(name, buffer)¶
Write buffer over the region name; its size must match that region.
- Arguments:
name (string)
buffer (BlobTypedArray | ArrayBuffer | Buffer<ArrayBufferLike>)
- BlobPackBuilder.isBuilt()¶
Return whether build() has handed the bytes over to a blob.
- Returns:
boolean
- class BlobPackDescriptor()¶
Describes the binary regions a BlobPack will hold.
exported from
index.d- BlobPackDescriptor.addRegion(name, blobLayout, count)¶
Register a region of count elements of blobLayout under name.
Throw if name is empty, longer than 31 bytes or already registered, or if count is zero.
- Arguments:
name (string)
blobLayout (BlobLayout)
count (number)
- class BlobPackRegion()¶
- One region of a BlobPack - its name, the layout of its elements, how many it
holds, and where it sits in the pack’s blob.
It describes a region; it does not write one. The bytes are filled before the pack is a value, on a BlobPackBuilder.
Note: Not directly instantiable.
exported from
index.d- BlobPackRegion.at(index)¶
- Return one datum of the region, as BlobArray.at does: a number, or a bigint for the
64-bit types. The index runs to dataCount(); a negative one counts from the end. Out of range throws.
- Arguments:
index (number)
- Returns:
number | bigint
- BlobPackRegion.blob()¶
Return the whole blob of the BlobPack this region belongs to.
The region is the slice of it that starts at offset() and runs byteCount() bytes; a BlobArray or a BlobView built on a ValueBlob of that slice reads it through blobLayout().
- Returns:
ValueBlob
- BlobPackRegion.blobLayout()¶
Return the BlobLayout of one element of the region.
- Returns:
BlobLayout
- BlobPackRegion.byteCount()¶
Return the size of the region in bytes.
- Returns:
number
- BlobPackRegion.count()¶
Return the number of elements the region holds.
- Returns:
number
- BlobPackRegion.dataCount()¶
- Return the number of data the region holds - count() elements of as many
components as the layout packs.
- Returns:
number
- BlobPackRegion.name()¶
Return the name the region was registered under, at most 31 bytes.
- Returns:
string
- BlobPackRegion.offset()¶
Return where the region starts in the pack blob, in bytes.
- Returns:
number
- class BlobStream()¶
The handle on a blob being written to a database past 2 GB, in chunks.
It reports where the write stands - offset, remaining, isClosed - and its blobId once the last byte is in. It moves nothing itself. The three CommitDatabase methods blobStreamCreate, blobStreamAppend and blobStreamClose do the writing.
Note: Not directly instantiable.
exported from
index.d- BlobStream.blobId()¶
Return the ValueBlobId of the written blob.
It is computed from the blob’s layout and every byte appended. Throw while the stream is open: the hash is only complete once every byte has been written.
- Returns:
ValueBlobId
- BlobStream.isClosed()¶
Return true if the stream is closed.
- Returns:
boolean
- BlobStream.offset()¶
Return the current offset.
- Returns:
number
- BlobStream.remaining()¶
Return the remaining bytes count to fill the blob.
- Returns:
number
- class BlobEncoder(blobLayout)¶
- Builds a blob element by element: write(value) appends, endEncoding()
returns the finished blob.
exported from
index.d- Arguments:
blobLayout (BlobLayout)
- BlobEncoder.blobLayout()¶
Return the layout of the blob.
- Returns:
BlobLayout
- BlobEncoder.encoderLayout()¶
- Return the BlobEncoderLayout: the blob layout together with the Viper types write()
accepts for one element and for one component.
- Returns:
BlobEncoderLayout
- BlobEncoder.endEncoding()¶
Return the encoded blob and end the encoding.
A write after this throws.
- Returns:
ValueBlob
- BlobEncoder.write(value)¶
Write one element at the end of the blob.
value is a number when the layout packs one component, and an array of blobLayout.components() of them otherwise - or a value of encoderLayout().type(). A value of another type, or an array of another length, throws.
- Arguments:
value (InputValue)
- class BlobEncoderLayout()¶
A BlobLayout together with the Viper types it reads as.
type() is the Viper type of one element - vec<float, 3> for a float-3 layout - and elementType() the type of one component, float.
Note: Not directly instantiable.
exported from
index.d- BlobEncoderLayout.blobLayout()¶
Return the layout.
- Returns:
BlobLayout
- BlobEncoderLayout.elementType()¶
Return the type of one component.
- Returns:
NumericType
- BlobEncoderLayout.type()¶
Return the type of one element.
- Returns:
TypeVec | NumericType
- class BlobView(blobLayout, blob)¶
Interprets the bytes of a blob through its layout.
at() and iteration walk ELEMENTS: one element is a native number when the layout packs one component, a ValueVec of blobLayout.components() values otherwise. BlobArray reads the same bytes one datum at a time.
The blob must hold a whole number of elements - its size a multiple of blobLayout.byteCount() - or the constructor throws.
exported from
index.d- Arguments:
blobLayout (BlobLayout)
blob (ValueBlob)
- BlobView.[Symbol․iterator]()¶
Iterate the decoded elements lazily.
- Returns:
Iterator<OutputValue>
- BlobView.at(index)¶
Return the element at the given index (out of range throws).
A negative index counts from the end, as
Array.prototype.atdoes:at(-1)is the last element. Out of range still throws.- Arguments:
index (number)
- Returns:
OutputValue
- BlobView.blob()¶
Return the ValueBlob the view reads. The bytes are shared, not copied.
- Returns:
ValueBlob
- BlobView.blobLayout()¶
- Return the BlobLayout of one element: its data type and how many components it
packs.
- Returns:
BlobLayout
- BlobView.count()¶
Return the number of elements in the blob.
- Returns:
number
- BlobView.dataCount()¶
- Return the number of data in the blob - count() elements of as many components as
the layout packs.
- Returns:
number
- BlobView.encoderLayout()¶
- Return the BlobEncoderLayout: the blob layout together with the Viper types an
element and a component decode to.
- Returns:
BlobEncoderLayout
- BlobView.toTypedArray()¶
- Project the whole blob as a flat TypedArray of its scalar components (copy-first:
a fresh, aligned buffer - safe to keep and upload to the GPU). The kind follows the layout dataType (float -> Float32Array, half -> Uint16Array, …). Requires a little-endian host.
- Returns:
BlobTypedArray
- class BlobData()¶
The bytes of one blob, with the blobId and layout that go with them.
What one database hands another when they synchronize.
Note: Not directly instantiable.
exported from
index.d- BlobData.blob()¶
Return the bytes of the blob.
- Returns:
ValueBlob
- BlobData.blobId()¶
Return the ValueBlobId, computed from the immutable content and the layout.
- Returns:
ValueBlobId
- BlobData.blobLayout()¶
Return the layout of the blob.
- Returns:
BlobLayout
- BlobData.size()¶
Return the size of the blob.
- Returns:
number
- class BlobGetting()¶
Retrieves blobs from a persistence layer.
Note: Not directly instantiable.
exported from
index.d- BlobGetting.blob(blobId)¶
Return the bytes of the blob blobId names, or undefined if it is not held here.
Throws past 2 GB, where the bytes cannot be handed over in one piece. Ask blobInfo(blobId).chunked() first, and read such a blob with readBlob.
A blob reserved and not yet sealed - createZeroBlob, then freezeBlob, on Databasing or CommitDatabasing - throws; blobInfo answers undefined for it meanwhile.
- Arguments:
blobId (ValueBlobId)
- Returns:
ValueBlob | undefined
- BlobGetting.blobIds()¶
Return the distinct blobId of every sealed blob.
A blob reserved and not yet sealed - createZeroBlob, then freezeBlob, on Databasing or CommitDatabasing - is left out.
- Returns:
ValueSet<ValueBlobId>
- BlobGetting.blobInfo(blobId)¶
Return the BlobInfo for blobId, or undefined if there is no such blob.
It gives the layout, the size, whether the blob is chunked, and the rowid that reaches it. A blob reserved and not yet sealed answers undefined - createZeroBlob, then freezeBlob, on Databasing or CommitDatabasing.
- Arguments:
blobId (ValueBlobId)
- Returns:
BlobInfo | undefined
- BlobGetting.blobInfos(blobIds)¶
Return one BlobInfo per blobId of blobIds this source holds.
An unknown blobId is left out, so the list may be shorter than blobIds.
- Arguments:
blobIds (ValueSet<ValueBlobId> | ValueBlobId[])
- Returns:
BlobInfo[]
- BlobGetting.blobStatistics()¶
Return the statistics for blobs.
- Returns:
BlobStatistics
- BlobGetting.readBlob(blobId, size, offset)¶
Return size bytes of the blob blobId names, starting at offset.
The read side of the blob stream that wrote it, and the only way to read a blob past 2 GB, which blob() refuses.
- Arguments:
blobId (ValueBlobId)
size (number | bigint)
offset (number | bigint)
- Returns:
ValueBlob
- class BlobInfo()¶
- What a store knows about one blob without reading it: its id, layout,
size, SQLite rowid, and whether it is chunked (over 2 GB).
Note: Not directly instantiable.
exported from
index.d- BlobInfo.blobId()¶
Return the identifier.
- Returns:
ValueBlobId
- BlobInfo.blobLayout()¶
Return the layout.
- Returns:
BlobLayout
- BlobInfo.chunked()¶
- Return true if the blob is stored in several chunks, which the database does above
2 GB. Such a blob is written with blobStreamCreate, blobStreamAppend and blobStreamClose, and read with readBlob; blob() refuses it.
- Returns:
boolean
- BlobInfo.rowId()¶
Return the rowid that reaches the blob in its store.
- Returns:
number
- BlobInfo.size()¶
Return the size of the blob in bytes.
- Returns:
number
- class BlobStatistics()¶
- How many blobs a store holds and how big they are: count, total, smallest,
largest, in bytes.
Note: Not directly instantiable.
exported from
index.d- BlobStatistics.count()¶
Return the number of blobs.
- Returns:
number
- BlobStatistics.maxSize()¶
Return the size of the biggest blob in bytes.
- Returns:
number
- BlobStatistics.minSize()¶
Return the size of the smallest blob in bytes.
- Returns:
number
- BlobStatistics.totalSize()¶
Return the total size in bytes.
- Returns:
number