Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Snowbank Slices And Buffers

ASecurity

How to correctly use the Slice type and its companions (SliceReader, SliceWriter, SliceOwner) for binary data in the FoundationDB .NET client / SnowBank.Core codebase. Slice is a readonly struct (namespace System) — the logical equivalent of a ReadOnlyMemory of bytes with many helpers. Use whenever code constructs or reads a Slice, converts between bytes and other types (Slice.FromBytes/FromStringUtf8/FromInt32/FromFixed64/ToInt64/ToStringUtf8/AsSlice/ToArray), builds or parses a binary buffe...

158 stars
0 votes
0 copies
0 views
Added 9/19/2026
ai-agentsgoapi

Works with

cursorcliapi

Security Analysis

A100/100

Scanned 9/19/2026

Install to Claude Code

$npx -y skills add SnowBankSDK/foundationdb-dotnet-client --skill snowbank-slices-and-buffers --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Snowbank Slices And Buffers?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Snowbank Slices And Buffers
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/snowbanksdk-snowbank-slices-and-buffers-foundationdb-dotnet-client/badge)](https://www.skillsdirectory.com/skills/snowbanksdk-snowbank-slices-and-buffers-foundationdb-dotnet-client)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: snowbank-slices-and-buffers
description: How to correctly use the Slice type and its companions (SliceReader, SliceWriter, SliceOwner) for binary data in the FoundationDB .NET client / SnowBank.Core codebase. Slice is a readonly struct (namespace System) — the logical equivalent of a ReadOnlyMemory of bytes with many helpers. Use whenever code constructs or reads a Slice, converts between bytes and other types (Slice.FromBytes/FromStringUtf8/FromInt32/FromFixed64/ToInt64/ToStringUtf8/AsSlice/ToArray), builds or parses a binary buffer (SliceWriter/SliceReader), rents pooled buffers (SliceOwner/ArrayPool), or worries about Nil-vs-Empty, endianness, or which integer encoding to use. For the Span-of-byte (Span-first) equivalents and the low-level buffer/pool machinery, see the bundled reference files.
---

# Slice, SliceReader, SliceWriter & friends

`Slice` is the workhorse for binary data in this codebase. It is a **`readonly struct`** (in namespace `System`) that wraps a segment of a `byte[]` — its three fields are `Array` (the backing array, possibly null), `Offset`, and `Count`. It predates `Span<T>` and is the logical equivalent of **`ReadOnlyMemory<byte>`**, but with a large library of helpers for turning bytes into and out of real-world types. Keys and values in the FoundationDB binding are `Slice`s.

> **Two things to internalize first:** (1) a `Slice` is a **view**, not a copy — it shares the backing array. (2) `Slice.Nil` (no array) and `Slice.Empty` (zero-length array) are **different** and the distinction is load-bearing. Both are covered below.

For the Span-first equivalents (`SpanReader`/`SpanWriter`, `ISpanEncodable`) read [`references/span-readers-writers.md`](references/span-readers-writers.md); for pooled buffer-building (`ISliceBufferWriter`, `SlicePool`, `ValueBuffer<T>`, allocators) read [`references/buffers-and-pooling.md`](references/buffers-and-pooling.md).

## 1. Nil vs Empty — the #1 gotcha

| | `Slice.Nil` | `Slice.Empty` |
|---|---|---|
| backing array | none (null-like) | a zero-length array |
| `IsNull` | `true` | `false` |
| `IsEmpty` | `false` | `true` |
| `IsNullOrEmpty` | `true` | `true` |
| `IsPresent` | `false` | `true` |
| `GetBytes()` | returns **`null`** | returns an **empty array** |
| `ToStringUtf8()` | returns **`null`** | returns **`""`** |
| `==` | `Nil != Empty` | distinct |
| `CompareTo` | `Nil` and `Empty` compare **equal** (both sort first) |

`tr.GetAsync(key)` returns **`Slice.Nil`** for a missing key, so the canonical "does it exist?" check is `value.IsNull` (or `IsNullOrEmpty` if an empty value also counts as absent). Use `Nil` to mean *absent* and `Empty` to mean *present but zero-length*.

```csharp
var v = await tr.GetAsync(key);
if (v.IsNull) { /* key does not exist */ }
```

## 2. Slice is a view — copy when you must own it

Constructing a `Slice` from a `byte[]` does **not** copy; the `Slice` references the array, so mutations to the array are visible through the slice (and its `.Span`). When you need an independent owner, copy:

```csharp
byte[] buf = ...;
var view = buf.AsSlice();        // shares buf — buf[i] = x is visible through view
byte[] mine = view.ToArray();    // defensive copy
buf[0] = 0xFF;                   // changes `view`, not `mine`
```

## 3. Constructing a Slice

```csharp
// from arrays / spans
byte[] b = ...;
b.AsSlice();                 b.AsSlice(offset, count);
new ArraySegment<byte>(b, o, n).AsSlice();
Slice.FromBytes("abc"u8);    // copies a ReadOnlySpan<byte>

// from text
Slice.FromStringUtf8("héllo");   Slice.FromString("héllo");   // UTF-8
Slice.FromStringAscii("ABC");    // ASCII only — lossy/throws on chars > 0x7F

// well-known
Slice.Empty;   Slice.Nil;   Slice.Zero(16);   // 16 zero bytes

// guids / uuids / hex
Slice.FromGuid(g);   Slice.FromUuid128(u);   Slice.FromHexString("00ff1234");
```

### Three integer encodings — pick deliberately

This is a classic source of bugs. They are **not** interchangeable:

| Factory | Encoding | Size (int32) | Read back with |
|---|---|---|---|
| `Slice.FromInt32(v)` | minimal little-endian (leading zero bytes dropped) | 1–4 bytes | `slice.ToInt32()` |
| `Slice.FromFixed32(v)` | fixed little-endian | always 4 bytes | `slice.ToInt32()` |
| `Slice.FromVarint32(v)` | 7-bit LEB128 varint | 1–5 bytes | (via `SliceReader.ReadVarInt32`) |

Every variant has a **big-endian** twin (`FromInt32BE`, `FromFixed32BE`, …) and 16/24/64/128-bit widths, plus floats (`FromSingle`/`FromDouble`) and `FromDecimal`. Big-endian fixed encodings are what you want when a number must **sort** correctly as a key. The minimal `FromInt32` is for standalone values you read whole with `ToInt32()` — it is *not* self-delimiting, so don't use it mid-stream (in a `SliceWriter`, use the fixed-width `WriteInt32`/`WriteInt64` or `WriteVarInt*` there; see §6).

> ⚠️ **Naming differs between `Slice` and the writer/reader.** On `Slice` (standalone), `FromFixed32` = 4 bytes and `FromInt32` = minimal. On `SliceWriter`/`SliceReader` (streams), the fixed-width method is plain **`WriteInt32`/`ReadInt32`** (4 bytes LE; `*BE` for big-endian), and the varint is **`WriteVarInt32`/`ReadVarInt32`**. (`WriteFixed32`/`ReadFixed32` exist but are `[Obsolete]` — use `WriteInt32`/`ReadInt32`.)

## 4. Reading values back

```csharp
slice.ToInt64();   slice.ToInt32BE();   slice.ToGuid();   slice.ToUuid128();
slice.ToStringUtf8();    // Nil -> null, Empty -> ""
slice.ToArray();         // defensive copy to byte[]
slice.ToHexString();

// zero-copy access to the bytes
ReadOnlySpan<byte> span = slice.Span;
ReadOnlyMemory<byte> mem = slice.Memory;

// slicing (negative indices count from the end)
slice.Substring(7, 6);   slice[2..5];   slice[^1..];
```

## 5. Comparison & equality

`Slice` compares **lexicographically by raw bytes** (the same order FoundationDB sorts keys), is offset/array-independent (equal content compares equal regardless of backing array or offset), and supports `==`, `<`, `>`, `CompareTo`, `StartsWith`, `EndsWith`, `IndexOf`. For dictionaries/sorted sets, use `Slice.Comparer.Default` (an `IComparer<Slice>` + `IEqualityComparer<Slice>`).

```csharp
a.CompareTo(b) < 0;            // a sorts before b
key.StartsWith(prefix);        // prefix match
var set = new SortedSet<Slice>(Slice.Comparer.Default);
```

## 6. SliceWriter — build a buffer

`SliceWriter` is a **mutable, growable** builder (`struct`, `IBufferWriter<byte>`, `IDisposable`). Start from `default(SliceWriter)` (heap-backed, grows as needed) or `new SliceWriter(pool)` (rents from an `ArrayPool<byte>`):

```csharp
var w = new SliceWriter();
w.WriteInt32(42);                    // fixed 4 bytes LE  (self-delimiting)
w.WriteVarInt32(1000);               // LEB128            (self-delimiting)
w.WriteVarString("hello");           // length-prefixed UTF-8
w.WriteStringUtf8("raw");            // raw UTF-8, NO length prefix
w.WriteBytes(payload);               // append bytes
Slice result = w.ToSlice();          // the written region (a view into the writer's buffer)
```

- Use **self-delimiting** writes (fixed-width `WriteInt32`/`WriteInt64`/…, `WriteVarInt*`, `WriteVarString`) for anything you'll parse back sequentially. A raw `WriteStringUtf8`/`WriteBytes` has no length, so the reader must already know the length.
- `Position`, `Reset()`, `Rewind()`, `Skip(n)`, `Allocate(n)`/`AllocateSpan(n)` (reserve space to fill in place).
- **Pooling caveat:** if you pass an `ArrayPool<byte>`, you must either `Dispose()` the writer or hand the buffer off with `ToSliceOwner()` — otherwise the rented array is never returned. `ToSlice()` returns a *view into the writer's buffer*; if the writer (or its pooled buffer) is disposed/reused, that view becomes invalid — `ToArray()` or `ToSliceOwner()` it to keep it.

## 7. SliceReader — parse a buffer

`SliceReader` is a **forward cursor** over a `Slice`. Pair each read with the matching write:

```csharp
var r = result.ToSliceReader();
int n     = r.ReadInt32();           // <-> WriteInt32  (fixed 4 bytes)
uint k    = r.ReadVarInt32();        // <-> WriteVarInt32
string s  = r.ReadVarString();       // <-> WriteVarString
// raw / fixed-length string written without a prefix: read the known number of bytes
string raw = r.ReadBytes(3).ToStringUtf8();
Slice rest = r.ReadToEnd();
```

`Remaining`, `HasMore`, `Head` (bytes already read), `Tail` (bytes not yet read), and non-advancing `PeekByte()`/`PeekBytes(n)` round out the API. There is **no** `ReadStringUtf8(n)` — use `ReadBytes(n).ToStringUtf8()`.

## 8. SliceOwner — pooled, disposable Slices

`SliceOwner` is a rented `Slice` that returns its buffer to an `ArrayPool<byte>` on `Dispose` — the allocation-free analogue of `IMemoryOwner<byte>`. The contract: **you MUST `Dispose` it, and MUST NOT use its data afterward.**

```csharp
using (var owner = Slice.FromBytes(payload, ArrayPool<byte>.Shared))
{
    Slice data = owner.Data;     // valid only inside the using
    Use(data.Span);
}   // buffer returned to the pool here
```

`owner.IsValid`, `owner.Count`, `owner.Span`, `owner.Pool`; `SliceOwner.Wrap/Create/Copy` and `writer.ToSliceOwner()` produce them. Don't let an owner's `Data` escape the `using`.

## 9. Span / Memory interop & `ISpanEncodable`

`Slice` interops freely with the modern primitives: `slice.Span` (`ReadOnlySpan<byte>`), `slice.Memory` (`ReadOnlyMemory<byte>`), `byte[].AsSlice()`. Many hot types (keys, values, the writers) implement **`ISpanEncodable`** so they can be rendered into a caller's buffer with no intermediate `Slice` allocation — `TryGetSpan(out span)` / `TryGetSizeHint(out size)` / `TryEncode(dest, out written)`. That interface is how `subspace.Key(...)`/`FdbValue.*` write themselves into pooled buffers at the last moment.

For working directly over `Span<byte>` (a caller-owned, fixed buffer) instead of `Slice`, use `SpanReader`/`SpanWriter` — see [`references/span-readers-writers.md`](references/span-readers-writers.md).

## 10. Round-trip example

```csharp
// build
var w = new SliceWriter();
w.WriteInt32(order.Id);
w.WriteVarString(order.Customer);
w.WriteVarInt64(order.Total);
Slice packed = w.ToSlice();

// parse
var r = packed.ToSliceReader();
int id        = r.ReadInt32();
string cust   = r.ReadVarString();
long total    = (long) r.ReadVarInt64();
```

## 11. Self-check

- [ ] Did I use `IsNull`/`IsNullOrEmpty` (not `== Slice.Empty`) to test for a missing value?
- [ ] Am I treating `Slice` as a **view** — copying with `ToArray()`/`ToSliceOwner()` before mutating shared arrays or outliving a pooled buffer?
- [ ] Did I pick the right integer encoding (`Fixed*`/`*BE` for sortable keys; `VarInt*`/`Fixed*` for self-delimiting stream fields; `FromInt32` only for standalone whole-slice values)?
- [ ] Do my `SliceWriter` writes and `SliceReader` reads pair up (`WriteInt32`↔`ReadInt32`, `VarInt`↔`VarInt`, `VarString`↔`VarString`)?
- [ ] If I rented from an `ArrayPool` (`SliceWriter(pool)` / `SliceOwner`), did I `Dispose`/`ToSliceOwner()` so the buffer returns to the pool — and not use the data after disposal?

Attribution

SnowBankSDKSnowBankSDK
View sourceMore from SnowBankSDK →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Caveman

Ultra-compressed communication mode. Cuts token usage ~75% by speaking like caveman while keeping full technical accuracy. Supports intensity levels: lite, full (default), ultra, wenyan-lite, wenyan-full, wenyan-ultra. Use when user says "caveman mode", "talk like caveman", "use caveman", "less tokens", "be brief", or invokes /caveman. Also auto-triggers when token efficiency is requested.

1023331 votes

Hyperplan

Adversarial multi-agent planning skill. Self-orchestrates 5 hostile category members (unspecified-low, unspecified-high, deep, ultrabrain, artistry) via team-mode for ruthless cross-critique debate, distills only the defensible insights, then MANDATORILY hands the distilled insight bundle to the `plan` agent for executable plan formalization. Use when planning needs maximum rigor and surfacing of weak assumptions, blind spots, and over-engineering. Triggers: 'hyperplan', 'hpp', '/hyperplan', ...

686011 votes

Mcp Code Execution

Routes multi-tool workflows through MCP servers for large datasets and pipelines. Use when Bash tool overhead is limiting throughput on data-heavy tasks.

3331 votes

catchup

Recovers prior coding-agent session context by running `catchup <agent> --since-compact`, which extracts a clean summary of a previous Codex, Claude Code, Antigravity, OpenCode, or Pi Agent session. Use when the user says "catch up", "what did the last session do", "get me up to speed", "I switched agents", or asks to recover/summarize a previous session before continuing. Do NOT use for the current conversation, git history, or any non-agent log.

611 votes

math-skill

A comprehensive mathematical reasoning skill for AI assistants — handles arithmetic to research-level problems with rigorous step-by-step reasoning, systematic verification, and transparent uncertainty handling

381 votes
View all in ai-agents →