Language model search engine built on a vector database and an anything key/value store.
Last updated Jul 6, 2026
577
Stars
41
Forks
4
Issues
0
Stars/day
Attention Score
88
Topics
Language breakdown
C# 99.5%
Batchfile 0.5%
βΈ Files
click to expand
README
Resin
Introduction
This is a reboot of the old Resin search engine/machine learning project. Resin is a vector space search engine, a vector database and an anything key/value store. It powers efficient string processing, vector operations, and custom storage primitives designed for speed and simplicity. It can produce large language models out of strings and large anything models out of byte arrays.Highlights
- Fast key/value storage with page/column readers and writers
- Practical text analysis utilities for strings, bags of words/chars, and vectors
- Commandline tools for building and validating lexicons and comparing strings
- Clean, dependency light design that is easy to extend
Key/Value Column Semantics
ColumnWriter
- TryPut(TKey key, ReadOnlySpan
value)
false when the key already exists; otherwise writes to the current page.
- Triggers page serialization when the page becomes full.
- PutOrAppend(TKey key, ReadOnlySpan
value)
LinkedAddressNode) written to the value stream.
- Tail-appending order: the original value remains first, followed by each appended value in insertion order. Address entry for the key points to the list head when linking is active.
- If the key does not exist in the column snapshot, operates at the page level (insert/append within the current page) and may serialize when full.
ColumnReader
- Get(TKey key)
key. If the keyβs address entry points to a linked-list head, returns the concatenated bytes of all linked values.
- Returns ReadOnlySpan<byte>.Empty when the key does not exist.
- GetMany(TKey key, out int count)
ReadOnlySpan<byte> of all values linked for key and outputs the number of items via count.
- When the key points to a single raw value, returns that value and count = 1. If the key does not exist, returns empty and count = 0.
TKey Restrictions for ColumnWriter/ColumnReader
When working withTKey, please adhere to the following restrictions to ensure proper functionality:
TKeymust be a value type (struct) and implement bothIEquatable<TKey>andIComparable<TKey>.
- Ordering and equality must be stable across sessions. The column-wide key snapshot uses
BinarySearch/sorting, soCompareTomust define a strict total order consistent withEquals.
- Page-level storage operates on
longkeys. For primitive numeric keys:
double and float are stored via their IEEE bit representations.
- int and long are stored directly.
- Other TKey types are hashed via GetHashCode() to a long for page-level operations.
- Recommendation: Use numeric primitives (
double,float,int,long) for deterministic ordering and lookup. If using a custom struct, ensure:
Equals and CompareTo are consistent and deterministic.
- GetHashCode() is stable and evenly distributed; collisions affect page-level operations since non-primitive keys are hashed to long.
- Keys must be comparable across the entire column; duplicate detection relies on the column snapshot and
BinarySearchover sorted keys.
Column model and set operations
- Each column stores any given
TKeyat most once in its column-wide snapshot (duplicate keys are prevented by bothTryPutandPutOrAppend). This makes columns effectively sets of keys, enabling set operations such as union, intersection, and joins across columns. Linked values (viaPutOrAppend) attach additional data to the existing key without introducing duplicates.
Storage artefacts: .key, .adr, *.val
- .key (Key stream)
TKey representations per page/column. Keys are written in fixed-size slots (sizeof(long) per entry for page-level storage) and serialized in page batches. The column-wide snapshot is built by reading and sorting this stream.
- .adr (Address stream)
Address structs aligned with .key entries. Each Address contains Offset and Length:
- For raw values: points directly into .val (Offset = start of value, Length = byte length).
- For linked values: points to a LinkedAddressNode head in .val (Length equals node size). The node chain yields multiple values for a single key.
- .val (Value stream)
LinkedAddressNode headers used for linking. Values are appended at the end of the stream; LinkedAddressNodes are also written into .val to form singly linked lists via absolute offsets.
Immutability of value files
- The
.valstream is treated as append-only:
LinkedAddressNode headers are appended and previous nodeβs NextOffset is patched by writing a new node and updating pointers via .adr alignment.
- Benefits:
Usage
- Use
Resin.KeyValuefor fast on disk structures and efficient read/write key/value sessions.
- Use
Resin.TextAnalysisforStringAnalyzer,VectorOperations, and similarity tooling.
- Use
Resin.WikipediaCommandLinefor commandline tools to build/validate lexicons. See detailed CLI usage and setup inResin.WikipediaCommandLine/README.md.
Contributing
Contributions are welcome! Please open an issue or pull request with clear motivation, tests when applicable, and concise changes.License
This project is licensed under the MIT License.Get help or report issues
Report Issuesπ More in this category