Paradise.BLOB
0.41.0
See the version list below for details.
dotnet add package Paradise.BLOB --version 0.41.0
NuGet\Install-Package Paradise.BLOB -Version 0.41.0
<PackageReference Include="Paradise.BLOB" Version="0.41.0" />
<PackageVersion Include="Paradise.BLOB" Version="0.41.0" />
<PackageReference Include="Paradise.BLOB" />
paket add Paradise.BLOB --version 0.41.0
#r "nuget: Paradise.BLOB, 0.41.0"
#:package Paradise.BLOB@0.41.0
#addin nuget:?package=Paradise.BLOB&version=0.41.0
#tool nuget:?package=Paradise.BLOB&version=0.41.0
Paradise.BLOB
Paradise.BLOB is a .NET blob builder for immutable unmanaged data layouts. It provides Unity-style blob primitives and builders that work in plain .NET code: you declare the layout as an unmanaged struct, a builder writes it as one contiguous byte array with relative offsets, and a reader hands the same struct back over those bytes with no parse and no copy.
The engine ships several asset formats on it — the collision world (Paradise.Physics), behaviour trees (Paradise.BT), mesh blobs (Paradise.Assets.Mesh) and skeletons and animation clips (Paradise.Animation) — so the conventions below are the ones those follow.
Install
dotnet add package Paradise.BLOB
Features
- Build immutable unmanaged roots with
ValueBuilder<T>andStructBuilder<T>. - Store arrays, strings, pointers, trees, sorted arrays, and dynamically typed payloads — including arrays whose elements themselves hold arrays and strings.
- Read blobs from one aligned native copy (
NativeBlobAssetReference<T>) or over a pinned managed array (ManagedBlobAssetReference<T>). - Keep offsets and alignment correct without hand-rolling binary layouts.
- Friendly to regular .NET apps and NativeAOT-oriented workflows: no reflection, no serializer.
Quick start
using Paradise.BLOB;
using System.Text;
public struct DemoBlob
{
public BlobString<UTF8Encoding> Name;
public BlobArray<int> Values;
public BlobPtr<int> MaxValue;
}
var builder = new StructBuilder<DemoBlob>();
builder.SetString(ref builder.Value.Name, "demo");
builder.SetArray(ref builder.Value.Values, new[] { 1, 2, 3 });
builder.SetPointer(ref builder.Value.MaxValue, 3);
byte[] bytes = builder.CreateBlob(); // what you store, ship or hash
using var blob = new NativeBlobAssetReference<DemoBlob>(bytes); // one aligned copy, no pinning
ref var root = ref blob.Value;
Console.WriteLine(root.Name.ToString());
Console.WriteLine(string.Join(", ", root.Values.ToArray()));
Console.WriteLine(root.MaxValue.Value);
Reading: native or managed
NativeBlobAssetReference<T>(ReadOnlySpan<byte>, int alignment = 16)copies the bytes once into aligned native memory. Prefer it at runtime: the root and every array in it are aligned however you asked, nothing is pinned in the GC heap, and the source span can be a file read or a slice of something larger. Dispose it when the data has been uploaded or is no longer needed; a finalizer frees it otherwise.ManagedBlobAssetReference<T>(byte[])pins a managed array in place and reads through the pin. Use it when the bytes must stay abyte[]you also hand elsewhere. Dispose it, or the array stays pinned.
Both hand back ref T Value — a reference into the blob, not a copy.
Nested layouts: arrays of structs that hold arrays
An element type may itself carry BlobArray<T> and BlobString<TEncoding> fields. Build such an array from one builder per element:
public struct Draw
{
public uint FirstIndex;
public uint IndexCount;
public BlobString<UTF8Encoding> Name;
}
public struct MeshBlob
{
public uint Magic;
public uint Version;
public BlobArray<float> Vertices;
public BlobArray<Draw> Draws;
}
var mesh = new StructBuilder<MeshBlob>();
mesh.Value.Magic = 0x48534D50; // "PMSH"
mesh.Value.Version = 1;
mesh.SetArray(ref mesh.Value.Vertices, vertices);
mesh.SetArray(ref mesh.Value.Draws, draws.Select(d =>
{
var draw = new StructBuilder<Draw>();
draw.Value.FirstIndex = d.First;
draw.Value.IndexCount = d.Count;
draw.SetString(ref draw.Value.Name, d.Name);
return (IBuilder<Draw>)draw;
}));
Conventions the engine's formats follow
- Magic and version first. The first two fields of a root are a
uintmagic and auintversion, so a reader can refuse a foreign or newer blob by name before touching an offset. Check them on the bytes (BitConverter.ToUInt32(bytes)) before constructing a reference. - Validate after opening. A blob is trusted memory once opened; a reader that will index into arrays checks the counts and indices it depends on (a draw that runs past the index buffer, a joint that names a node outside the tree) right after opening, and disposes the reference before throwing.
- Deterministic bytes. The same input builds the same bytes, so a blob can live in a source tree beside what it was made from and be fingerprinted by hash.
The one rule when reading: never through a copy
BlobArray<T>, BlobString<TEncoding> and BlobPtr<T> are small headers holding an offset relative to their own address. Anything that copies the header moves that address and the offset then points at the stack, not the blob — with no error, just wrong data. Three ways to copy one by accident:
// WRONG: `in` makes `blob` a readonly reference; calling a non-readonly member on
// blob.Draws forces a defensive COPY of the array header.
static void Check(in MeshBlob blob) { var n = blob.Draws[0].IndexCount; }
// WRONG: a `readonly` member on the struct does the same to every array it touches.
public readonly int VertexCount => Vertices.Length / 12;
// WRONG: passing a BlobString (or BlobArray) by value to a helper.
static string? NameOf(BlobString<UTF8Encoding> name) => name.ToString();
Do this instead:
static void Check(ref MeshBlob blob) { ref var draw = ref blob.Draws[0]; var n = draw.IndexCount; }
public int VertexCount => Vertices.Length / 12; // not readonly
var name = node.Name.ToString(); // read in place
Reach every blob member through a mutable ref, do not mark members that touch one readonly, and do not hand one to a method by value. A by-value read can pass a test by luck and fail elsewhere.
Common builders
ArrayBuilder<T>andSetArray(...)for contiguous unmanaged arrays.ArrayBuilderWithItemBuilders<T>and theSetArray(ref field, IEnumerable<IBuilder<T>>)overload for arrays of structs that hold arrays or strings.StringBuilder<TEncoding>andSetString(...)for encoded blob strings.PtrBuilderWithNewValue<T>andSetPointer(...)for blob pointers.TreeBuilder<T>andAnyTreeBuilderfor preordered trees with subtree end indices.SortedArrayBuilder<TKey, TValue>for hash-ordered key/value lookup tables.
Notes
- Blob roots and referenced values must be unmanaged.
CreateBlob()returns the raw serialized bytes for storage or transport;CreateNativeBlobAssetReference()andCreateManagedBlobAssetReference()open them directly.- Dispose every reference: a native one frees its memory, a managed one unpins its array.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages (4)
Showing the top 4 NuGet packages that depend on Paradise.BLOB:
| Package | Downloads |
|---|---|
|
Paradise.BT
Pure .NET behavior tree runtime inspired by EntitiesBT. |
|
|
Paradise.Physics
Stateless collision/physics query library for Paradise Engine: static colliders, raycasts and shape casts with no caches and no hidden state. |
|
|
Paradise.Animation
Skeletal animation runtime and offline builder in managed code, using ozz-animation's archive format: load an ozz skeleton and clips, sample a clip into local poses and model-space matrices; build, optimize and save the same archives from raw keyframes. No native code. |
|
|
Paradise.Assets.Mesh
The Paradise mesh blob: GPU-ready interleaved geometry with draw records as a Paradise.BLOB layout, read at runtime by pinning the bytes the asset pipeline extracted from a GLB. No glTF, no JSON, no images. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.60.0 | 338 | 10/3/2026 |
| 0.59.1 | 151 | 10/2/2026 |
| 0.59.0 | 161 | 9/29/2026 |
| 0.58.0 | 160 | 9/28/2026 |
| 0.57.0 | 154 | 9/28/2026 |
| 0.56.0 | 174 | 9/27/2026 |
| 0.55.0 | 161 | 9/27/2026 |
| 0.54.0 | 159 | 9/24/2026 |
| 0.53.0 | 161 | 9/24/2026 |
| 0.52.0 | 176 | 9/23/2026 |
| 0.51.0 | 157 | 9/22/2026 |
| 0.50.1 | 182 | 9/20/2026 |
| 0.50.0 | 162 | 9/19/2026 |
| 0.49.0 | 168 | 9/17/2026 |
| 0.48.1 | 162 | 9/15/2026 |
| 0.48.0 | 165 | 9/14/2026 |
| 0.47.0 | 176 | 9/10/2026 |
| 0.46.3 | 181 | 9/9/2026 |
| 0.46.2 | 180 | 9/9/2026 |
| 0.41.0 | 187 | 9/6/2026 |