Object store

The objects plugin stores binary payloads in the database file and names them in a tree of files and directories. Enable it at open with plugins {objects {}}. It has no settings. See plugin selection for the open options. Calls without it report the unavailable status.

The typed wrappers use plugin_call or plugin_stream with plugin objects. Request members use lowerCamelCase, such as expectedRevision.

Payloads

A payload is an immutable byte sequence. Its digest is the SHA-256 of all its bytes. Write a payload through an upload stream: write appends bytes in order and finish publishes the payload atomically. Equal bytes publish one payload: a second finish of the same bytes reports deduplicated true and stores nothing new. The database splits payloads into content-defined chunks of 256 KiB to 4 MiB, so edits to a large payload share most chunks with earlier versions. manifest lists the chunks, and an open stream reads any byte range: seek moves its position and read copies from it.

Published payloads are never deleted. Removing a file or pointing it at another payload keeps the earlier payload, and history keeps every earlier version of the file. An upload that does not finish publishes nothing. Closing the stream abandons it, and the next open removes what an interrupted upload left behind.

Each upload holds a working buffer of up to 4 MiB from the database memory budget until it finishes or closes. An upload that cannot fit reports out_of_memory. Calls on one stream are serialized; separate streams can run at the same time.

Digests cross the API as 64 hexadecimal digits in either case. Results show them as {#sha256 <hex>} values.

Files and directories

Every node lives under the root directory, entity 0. A directory holds named children; a file names one published payload. Names are non-empty UTF-8 text without / or NUL, and are not . or ... Names are unique within a directory and hold at most 4096 bytes.

A record describes one node:

MemberValue
entityThe node's entity.
parentThe parent directory's entity, 0 for the root.
nameThe node's name in its parent.
kindfile or directory.
revisionThe transaction that last changed the node.
sizeA file's payload size in bytes.
digestA file's payload digest.

create creates up to 4096 nodes in one commit. A node names its parent by entity, or with under by the position of an earlier directory in the same request:

dust136 B
[
  {name notes}
  {name   hello.txt
   under  1
   digest {#sha256 95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb}}]

A file can name only a published payload. A merge patch can also write stardust/object/digest on an entity; the commit then needs a published payload too.

Revisions

Each write commits atomically through the commit queue. A refused write changes nothing. update, move and remove take an expected revision. Pass the revision of a record you read to refuse the write when another writer changed the node since: the call reports conflict. Pass 0 to skip the check. An update to the current payload, or a move to the current name and parent, changes nothing and keeps the revision.

With replace true, a move can replace a destination node of the same kind. A file replaces a file. A directory replaces only an empty directory. Remove deletes a file or an empty directory.

Reads

Every tree read uses one snapshot of the database. Concurrent writes do not change a read in progress. children lists a directory in name order. path returns the names from the root to a node. files finds the files that hold a payload. A stream read verifies every chunk it reads against its digest and reports evaluation_failed when stored bytes do not match.

Without the plugin

On a database that does not enable objects, a write that sets stardust/object/digest fails with the unavailable status and the code plugin_required. Payloads and files that the plugin stored stay in the database file and return when the plugin is enabled again.

Operations

OperationReturnsSummary
objects_getdocumentobjects.get(entity) returns the owned record document of an object file or directory.
objects_lookupdocumentobjects.lookup(parent, name) returns the owned record document of a directory's child.
objects_childrendocumentobjects.children(parent) returns an owned array document of a directory's child records in name order.
objects_pathdocumentobjects.path(entity) returns an owned array document of the names from the root to an object node.
objects_filesdocumentobjects.files(digest) returns an owned ascending array document of the file entities that hold a payload.
objects_manifestdocumentobjects.manifest(digest) returns a published payload's owned manifest document.
objects_createdocumentobjects.create(nodes) creates files and directories in one commit and returns an owned array document of their records.
objects_updatedocumentobjects.update(entity, digest, expected_revision) points a file at another published payload and returns its owned record document.
objects_movedocumentobjects.move(entity, parent, name, expected_revision, replace) renames or moves a node and returns its owned record document.
objects_removenoneobjects.remove(entity, expected_revision) removes a file or an empty directory.
objects_uploadplugin_streamobjects.upload() opens an owned plugin stream that uploads one payload.
objects_openplugin_streamobjects.open(digest) opens an owned plugin stream that reads a published payload.

objects_get

odin118 B
objects_get :: proc(
	db: Database,
	entity: builtin.i64,
	allocator := context.allocator,
) -> (
	Document,
	Error,
)

Parameters

ParameterTypeOwnershipDefaultMeaning
dbDatabaseBorrowed for the callRequiredThe objects owner for this operation. Keep it alive during the call.
entitybuiltin.i64ValueRequiredThe node's entity.
allocatormem.AllocatorValuecontext.allocatorAllocator for copied return values and diagnostics. The caller releases them with the same allocator.

Errors

StatusWhen
.Invalid_Argumententity is not a user entity
.Not_Foundno object node has the entity
.Evaluation_Failedstored tree metadata or the manifest of a named payload is corrupted
.Unavailablethe database does not enable the objects plugin
.Closedthe database is closed
.Out_Of_Memorynative memory is exhausted

Examples

Read the record of a file

odin581 B
{
	upload := stardust.objects_upload(db) or_return
	defer stardust.close(&upload)
	_ = stardust.write(upload, transmute([]u8)string("Hello, objects")) or_return
	published := stardust.finish(upload) or_return
	stardust.close(&published)
}
{
	nodes := stardust.parse(`[{name hello.txt digest {#sha256 "95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb"}}]`, .DUST) or_return
	defer stardust.close(&nodes)
	created := stardust.objects_create(db, nodes) or_return
	stardust.close(&created)
}
result := stardust.objects_get(db, 1) or_return
defer stardust.close(&result)
Result / 169 B
{entity   1
 parent   0
 revision 2
 name     hello.txt
 kind     file
 size     14
 digest   {#sha256 95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb}}

C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release

objects_lookup

odin136 B
objects_lookup :: proc(
	db: Database,
	parent: builtin.i64,
	name: string,
	allocator := context.allocator,
) -> (
	Document,
	Error,
)

Parameters

ParameterTypeOwnershipDefaultMeaning
dbDatabaseBorrowed for the callRequiredThe objects owner for this operation. Keep it alive during the call.
parentbuiltin.i64ValueRequiredThe directory's entity. 0 selects the root.
namestringValueRequiredThe child's name.
allocatormem.AllocatorValuecontext.allocatorAllocator for copied return values and diagnostics. The caller releases them with the same allocator.

Errors

StatusWhen
.Invalid_Argumentthe parent or the name is invalid, or the parent is a file
.Not_Foundthe parent has no child with the name, or the parent does not exist
.Evaluation_Failedstored tree metadata or the manifest of a named payload is corrupted
.Unavailablethe database does not enable the objects plugin
.Closedthe database is closed
.Out_Of_Memorynative memory is exhausted

Examples

Find a child of the root by name

odin301 B
{
	nodes := stardust.parse(`[{name notes}]`, .DUST) or_return
	defer stardust.close(&nodes)
	created := stardust.objects_create(db, nodes) or_return
	stardust.close(&created)
}
// Entity 0 is the root directory.
result := stardust.objects_lookup(db, 0, "notes") or_return
defer stardust.close(&result)
Result / 72 B
{entity   1
 parent   0
 revision 2
 name     notes
 kind     directory}

C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release

objects_children

odin123 B
objects_children :: proc(
	db: Database,
	parent: builtin.i64,
	allocator := context.allocator,
) -> (
	Document,
	Error,
)

Parameters

ParameterTypeOwnershipDefaultMeaning
dbDatabaseBorrowed for the callRequiredThe objects owner for this operation. Keep it alive during the call.
parentbuiltin.i64ValueRequiredThe directory's entity. 0 selects the root.
allocatormem.AllocatorValuecontext.allocatorAllocator for copied return values and diagnostics. The caller releases them with the same allocator.

Errors

StatusWhen
.Invalid_Argumentthe parent is invalid or is a file
.Not_Foundthe parent does not exist
.Evaluation_Failedstored tree metadata or the manifest of a named payload is corrupted
.Unavailablethe database does not enable the objects plugin
.Closedthe database is closed
.Out_Of_Memorynative memory is exhausted

Examples

List the root directory

odin273 B
{
	nodes := stardust.parse(`[{name notes} {name drafts}]`, .DUST) or_return
	defer stardust.close(&nodes)
	created := stardust.objects_create(db, nodes) or_return
	stardust.close(&created)
}
result := stardust.objects_children(db, 0) or_return
defer stardust.close(&result)
Result / 157 B
[{entity   2
  parent   0
  revision 3
  name     drafts
  kind     directory}
 {entity   1
  parent   0
  revision 3
  name     notes
  kind     directory}]

C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release

objects_path

odin119 B
objects_path :: proc(
	db: Database,
	entity: builtin.i64,
	allocator := context.allocator,
) -> (
	Document,
	Error,
)

Parameters

ParameterTypeOwnershipDefaultMeaning
dbDatabaseBorrowed for the callRequiredThe objects owner for this operation. Keep it alive during the call.
entitybuiltin.i64ValueRequiredThe node's entity. 0 selects the root, whose path is empty.
allocatormem.AllocatorValuecontext.allocatorAllocator for copied return values and diagnostics. The caller releases them with the same allocator.

Errors

StatusWhen
.Invalid_Argumententity is not a user entity
.Not_Foundthe node or one of its ancestors does not exist
.Evaluation_Failedstored tree metadata or the manifest of a named payload is corrupted
.Unavailablethe database does not enable the objects plugin
.Closedthe database is closed
.Out_Of_Memorynative memory is exhausted

Examples

Read the path of a nested directory

odin277 B
{
	nodes := stardust.parse(`[{name notes} {name drafts under 1}]`, .DUST) or_return
	defer stardust.close(&nodes)
	created := stardust.objects_create(db, nodes) or_return
	stardust.close(&created)
}
result := stardust.objects_path(db, 2) or_return
defer stardust.close(&result)
Result / 14 B
[notes drafts]

C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release

objects_files

odin115 B
objects_files :: proc(
	db: Database,
	digest: string,
	allocator := context.allocator,
) -> (
	Document,
	Error,
)

Parameters

ParameterTypeOwnershipDefaultMeaning
dbDatabaseBorrowed for the callRequiredThe objects owner for this operation. Keep it alive during the call.
digeststringValueRequiredThe payload's SHA-256 digest as 64 hexadecimal digits.
allocatormem.AllocatorValuecontext.allocatorAllocator for copied return values and diagnostics. The caller releases them with the same allocator.

Errors

StatusWhen
.Invalid_Argumentthe digest is not 64 hexadecimal digits
.Unavailablethe database does not enable the objects plugin
.Closedthe database is closed
.Out_Of_Memorynative memory is exhausted

Examples

Find the files that hold a payload

odin741 B
{
	upload := stardust.objects_upload(db) or_return
	defer stardust.close(&upload)
	_ = stardust.write(upload, transmute([]u8)string("Hello, objects")) or_return
	published := stardust.finish(upload) or_return
	stardust.close(&published)
}
{
	nodes := stardust.parse(`[{name a.txt digest {#sha256 "95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb"}} {name b.txt digest {#sha256 "95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb"}}]`, .DUST) or_return
	defer stardust.close(&nodes)
	created := stardust.objects_create(db, nodes) or_return
	stardust.close(&created)
}
result := stardust.objects_files(db, "95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb") or_return
defer stardust.close(&result)
Result / 5 B
[1 2]

C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release

objects_manifest

odin118 B
objects_manifest :: proc(
	db: Database,
	digest: string,
	allocator := context.allocator,
) -> (
	Document,
	Error,
)

Parameters

ParameterTypeOwnershipDefaultMeaning
dbDatabaseBorrowed for the callRequiredThe objects owner for this operation. Keep it alive during the call.
digeststringValueRequiredThe payload's SHA-256 digest as 64 hexadecimal digits.
allocatormem.AllocatorValuecontext.allocatorAllocator for copied return values and diagnostics. The caller releases them with the same allocator.

Errors

StatusWhen
.Invalid_Argumentthe digest is not 64 hexadecimal digits
.Not_Foundno payload with the digest is published
.Evaluation_Failedthe stored manifest is corrupted
.Unavailablethe database does not enable the objects plugin
.Closedthe database is closed
.Out_Of_Memorynative memory is exhausted

Notes

The manifest is {digest size chunks}. chunks lists {digest size} in byte order and is omitted for an empty payload.

Examples

Read the manifest of a published payload

odin386 B
{
	upload := stardust.objects_upload(db) or_return
	defer stardust.close(&upload)
	_ = stardust.write(upload, transmute([]u8)string("Hello, objects")) or_return
	published := stardust.finish(upload) or_return
	stardust.close(&published)
}
result := stardust.objects_manifest(db, "95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb") or_return
defer stardust.close(&result)
Result / 208 B
{digest {#sha256 95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb}
 size   14
 chunks [{digest {#sha256 95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb}
          size   14}]}

C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release

objects_create

odin117 B
objects_create :: proc(
	db: Database,
	nodes: Document,
	allocator := context.allocator,
) -> (
	Document,
	Error,
)

Parameters

ParameterTypeOwnershipDefaultMeaning
dbDatabaseBorrowed for the callRequiredThe objects owner for this operation. Keep it alive during the call.
nodesDocumentBorrowed for the callRequiredA borrowed array of at most 4096 nodes. Each has name, and parent (entity, default 0) or under (one-based position of an earlier directory). A file also has digest {#sha256 hex}. Without digest, the node is a directory.
allocatormem.AllocatorValuecontext.allocatorAllocator for copied return values and diagnostics. The caller releases them with the same allocator.

Errors

StatusWhen
.Invalid_Argumentthe array is empty or malformed, a name is invalid, or a parent is a file
.Not_Founda parent does not exist or a digest is not published
.Conflicta name is already taken in its parent or appears twice, or the database holds writes
.Unavailablethe database does not enable the objects plugin
.Closedthe database is closed
.Out_Of_Memorynative memory is exhausted

Notes

Nodes are created in order. A node names its parent by entity, or by the one-based position of an earlier directory in the same array with under.

Examples

Create a directory and a file in one commit

odin582 B
{
	upload := stardust.objects_upload(db) or_return
	defer stardust.close(&upload)
	_ = stardust.write(upload, transmute([]u8)string("Hello, objects")) or_return
	published := stardust.finish(upload) or_return
	stardust.close(&published)
}
// under 1 places hello.txt in the first node of the request.
nodes := stardust.parse(`[{name notes} {name hello.txt under 1 digest {#sha256 "95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb"}}]`, .DUST) or_return
defer stardust.close(&nodes)
result := stardust.objects_create(db, nodes) or_return
defer stardust.close(&result)
Result / 255 B
[{entity   1
  parent   0
  revision 3
  name     notes
  kind     directory}
 {entity   2
  parent   1
  revision 3
  name     hello.txt
  kind     file
  size     14
  digest   {#sha256 95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb}}]

C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release

objects_update

odin171 B
objects_update :: proc(
	db: Database,
	entity: builtin.i64,
	digest: string,
	expected_revision: builtin.i64,
	allocator := context.allocator,
) -> (
	Document,
	Error,
)

Parameters

ParameterTypeOwnershipDefaultMeaning
dbDatabaseBorrowed for the callRequiredThe objects owner for this operation. Keep it alive during the call.
entitybuiltin.i64ValueRequiredThe file's entity.
digeststringValueRequiredThe payload's SHA-256 digest as 64 hexadecimal digits.
expected_revisionbuiltin.i64ValueRequiredThe transaction that last touched the node, from its record. 0 skips the check.
allocatormem.AllocatorValuecontext.allocatorAllocator for copied return values and diagnostics. The caller releases them with the same allocator.

Errors

StatusWhen
.Invalid_Argumentthe entity is a directory or invalid, the digest is malformed, or expected_revision is negative
.Not_Foundthe file does not exist or the digest is not published
.Conflictexpected_revision is stale, or the database holds writes
.Unavailablethe database does not enable the objects plugin
.Closedthe database is closed
.Out_Of_Memorynative memory is exhausted

Examples

Point a file at a new payload

odin774 B
for text in ([]string{"Hello, objects", "Revised notes"}) {
	upload := stardust.objects_upload(db) or_return
	defer stardust.close(&upload)
	_ = stardust.write(upload, transmute([]u8)text) or_return
	published := stardust.finish(upload) or_return
	stardust.close(&published)
}
{
	nodes := stardust.parse(`[{name hello.txt digest {#sha256 "95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb"}}]`, .DUST) or_return
	defer stardust.close(&nodes)
	created := stardust.objects_create(db, nodes) or_return
	stardust.close(&created)
}
// 0 skips the revision check; pass a record's revision to refuse a stale write.
result := stardust.objects_update(db, 1, "2b6d3a4299dac5900171112d4fd569b48e54711d8937f6ff5ca00528d47afad2", 0) or_return
defer stardust.close(&result)
Result / 169 B
{entity   1
 parent   0
 revision 3
 name     hello.txt
 kind     file
 size     13
 digest   {#sha256 2b6d3a4299dac5900171112d4fd569b48e54711d8937f6ff5ca00528d47afad2}}

C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release

objects_move

odin205 B
objects_move :: proc(
	db: Database,
	entity: builtin.i64,
	parent: builtin.i64,
	name: string,
	expected_revision: builtin.i64,
	replace: bool,
	allocator := context.allocator,
) -> (
	Document,
	Error,
)

Parameters

ParameterTypeOwnershipDefaultMeaning
dbDatabaseBorrowed for the callRequiredThe objects owner for this operation. Keep it alive during the call.
entitybuiltin.i64ValueRequiredThe node to move.
parentbuiltin.i64ValueRequiredThe destination directory. 0 selects the root.
namestringValueRequiredThe node's new name.
expected_revisionbuiltin.i64ValueRequiredThe transaction that last touched the node, from its record. 0 skips the check.
replaceboolValueRequiredReplace an existing node of the same kind at the destination. False reports conflict for an occupied name.
allocatormem.AllocatorValuecontext.allocatorAllocator for copied return values and diagnostics. The caller releases them with the same allocator.

Errors

StatusWhen
.Invalid_Argumentthe request is malformed, the destination is a file or inside the node, the kinds differ, or a replaced directory is not empty
.Not_Foundthe node or the destination does not exist
.Conflictthe name is occupied without replace, expected_revision is stale, or the database holds writes
.Unavailablethe database does not enable the objects plugin
.Closedthe database is closed
.Out_Of_Memorynative memory is exhausted

Notes

With replace, a node of the same kind at the destination is replaced; a directory only when it is empty. A move into the node's own subtree is invalid.

Examples

Move a directory into another

odin355 B
{
	nodes := stardust.parse(`[{name notes} {name drafts}]`, .DUST) or_return
	defer stardust.close(&nodes)
	created := stardust.objects_create(db, nodes) or_return
	stardust.close(&created)
}
// false refuses to replace a node that already has the name.
result := stardust.objects_move(db, 2, 1, "archive", 0, false) or_return
defer stardust.close(&result)
Result / 74 B
{entity   2
 parent   1
 revision 4
 name     archive
 kind     directory}

C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release

objects_remove

odin137 B
objects_remove :: proc(
	db: Database,
	entity: builtin.i64,
	expected_revision: builtin.i64,
	allocator := context.allocator,
) -> Error

Parameters

ParameterTypeOwnershipDefaultMeaning
dbDatabaseBorrowed for the callRequiredThe objects owner for this operation. Keep it alive during the call.
entitybuiltin.i64ValueRequiredThe node to remove.
expected_revisionbuiltin.i64ValueRequiredThe transaction that last touched the node, from its record. 0 skips the check.
allocatormem.AllocatorValuecontext.allocatorAllocator for copied return values and diagnostics. The caller releases them with the same allocator.

Errors

StatusWhen
.Invalid_Argumentthe entity is invalid, the directory is not empty, or expected_revision is negative
.Not_Foundthe node does not exist
.Conflictexpected_revision is stale, or the database holds writes
.Unavailablethe database does not enable the objects plugin
.Closedthe database is closed
.Out_Of_Memorynative memory is exhausted

Notes

The payload stays published.

Examples

Remove an empty directory

odin221 B
{
	nodes := stardust.parse(`[{name drafts}]`, .DUST) or_return
	defer stardust.close(&nodes)
	created := stardust.objects_create(db, nodes) or_return
	stardust.close(&created)
}
stardust.objects_remove(db, 1, 0) or_return

C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release

objects_upload

odin104 B
objects_upload :: proc(
	db: Database,
	allocator := context.allocator,
) -> (
	Plugin_Stream,
	Error,
)

Parameters

ParameterTypeOwnershipDefaultMeaning
dbDatabaseBorrowed for the callRequiredThe objects owner for this operation. Keep it alive during the call.
allocatormem.AllocatorValuecontext.allocatorAllocator for copied return values and diagnostics. The caller releases them with the same allocator.

Errors

StatusWhen
.Unavailablethe database does not enable the objects plugin
.Conflictthe database holds writes
.Closedthe database is closed
.Out_Of_Memorynative memory is exhausted

Notes

The stream holds a working buffer of up to 4 MiB from the database memory budget until it finishes or closes. Write appends bytes; finish publishes the payload and returns {digest size deduplicated}.

Examples

Upload a payload and publish it

odin344 B
upload := stardust.objects_upload(db) or_return
// Closing abandons the upload unless it finished.
defer stardust.close(&upload)
_ = stardust.write(upload, transmute([]u8)string("Hello, ")) or_return
_ = stardust.write(upload, transmute([]u8)string("objects")) or_return
result := stardust.finish(upload) or_return
defer stardust.close(&result)
Result / 126 B
{digest       {#sha256 95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb}
 size         14
 deduplicated false}

C exports: stardust_db_plugin_stream_open, stardust_result_error, stardust_result_release

objects_open

odin119 B
objects_open :: proc(
	db: Database,
	digest: string,
	allocator := context.allocator,
) -> (
	Plugin_Stream,
	Error,
)

Parameters

ParameterTypeOwnershipDefaultMeaning
dbDatabaseBorrowed for the callRequiredThe objects owner for this operation. Keep it alive during the call.
digeststringValueRequiredThe payload's SHA-256 digest as 64 hexadecimal digits.
allocatormem.AllocatorValuecontext.allocatorAllocator for copied return values and diagnostics. The caller releases them with the same allocator.

Errors

StatusWhen
.Invalid_Argumentthe digest is not 64 hexadecimal digits
.Not_Foundno payload with the digest is published
.Evaluation_Failedthe stored manifest is corrupted
.Unavailablethe database does not enable the objects plugin
.Closedthe database is closed
.Out_Of_Memorynative memory is exhausted

Notes

Read copies bytes from the position and verifies each chunk it touches; seek moves the position, from the end with the payload size. The reader charges its state to the database memory budget until it is released or the database closes.

Examples

Read a range of a payload

odin517 B
{
	upload := stardust.objects_upload(db) or_return
	defer stardust.close(&upload)
	_ = stardust.write(upload, transmute([]u8)string("Hello, objects")) or_return
	published := stardust.finish(upload) or_return
	stardust.close(&published)
}
reader := stardust.objects_open(db, "95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb") or_return
defer stardust.close(&reader)
_ = stardust.seek(reader, 7, 0) or_return
data: [7]u8
// data holds "objects".
result := i64(stardust.read(reader, data[:]) or_return)
Result / 1 B
7

C exports: stardust_db_plugin_stream_open, stardust_result_error, stardust_result_release