garland

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 22, 2025 License: MIT Imports: 15 Imported by: 0

README

Garland

Garland: A rope-based text buffer library for Go, designed for text editors and applications requiring efficient document manipulation with version control.

If you use this, please support me on ko-fi: https://ko-fi.com/jeffday

ko-fi

Features

  • Rope data structure: O(log n) insert and delete operations using a balanced binary tree
  • Multiple addressing modes: Position by byte, rune (Unicode code point), or line:rune
  • Version control: Full undo/redo with branching support via forks and revisions
  • Three-tier storage: Automatic management across memory, warm (original file), and cold (external) storage
  • Decorations: Named position markers that track through edits
  • Multiple cursors: Independent cursor positions with automatic adjustment on edits
  • Search and replace: Literal and regex-based search with case sensitivity and whole-word options
  • Lazy loading: Stream large files with configurable ready thresholds
  • Transactions: Group operations into atomic revisions with rollback support
  • Memory management: Configurable soft/hard limits with LRU eviction to cold storage
  • File change detection: Monitor and handle external modifications to source files

Installation

go get github.com/phroun/garland

Requires Go 1.24 or later.

Usage

Basic Example
package main

import (
    "fmt"
    "github.com/phroun/garland"
)

func main() {
    // Initialize the library
    lib, err := garland.Init(garland.LibraryOptions{})
    if err != nil {
        panic(err)
    }

    // Create a new document
    g, err := lib.Open(garland.FileOptions{
        DataString: "Hello, World!",
    })
    if err != nil {
        panic(err)
    }
    defer g.Close()

    // Create a cursor and edit
    cursor := g.NewCursor()
    cursor.SeekByte(7)
    cursor.DeleteBytes(5, false)
    cursor.InsertString("Garland", nil, true)

    // Read the result
    cursor.SeekByte(0)
    content, _ := cursor.ReadBytes(g.ByteCount().Value)
    fmt.Println(string(content)) // "Hello, Garland!"
}
Opening Files
// From file path
g, err := lib.Open(garland.FileOptions{
    FilePath: "document.txt",
})

// From byte slice
g, err := lib.Open(garland.FileOptions{
    DataBytes: []byte("content"),
})

// From string
g, err := lib.Open(garland.FileOptions{
    DataString: "content",
})
Transactions
g.TransactionStart("Replace header")
cursor.SeekByte(0)
cursor.DeleteBytes(100, false)
cursor.InsertString("New Header\n", nil, true)
result, err := g.TransactionCommit()
// result.Revision contains the new revision number
Undo/Redo with Forks
// Undo to a previous revision
g.UndoSeek(5)

// Editing after undo creates a new fork automatically
cursor.InsertString("alternative content", nil, true)

// Switch between forks
g.ForkSeek(1, g.GetForkInfo(1).HighestRevision)
Decorations
// Add a decoration at cursor position
g.Decorate("bookmark", garland.AbsoluteAddress{
    Mode: garland.ByteMode,
    Byte: cursor.BytePos(),
})

// Query decoration position (updates as content changes)
pos, err := g.GetDecorationPosition("bookmark")

// Get decorations in a range
decorations := g.GetDecorationsInByteRange(0, 1000)
Search and Replace
// Find first occurrence
result, err := cursor.FindString("needle", garland.SearchOptions{
    CaseSensitive: false,
    WholeWord:     true,
})

// Replace all occurrences
count, err := cursor.ReplaceStringAll("old", "new", garland.SearchOptions{})

// Regex search
result, err := cursor.FindRegex(`\d{4}-\d{2}-\d{2}`, garland.SearchOptions{})
Storage Tiers
// Configure cold storage
lib, err := garland.Init(garland.LibraryOptions{
    ColdStoragePath: "/path/to/cold-storage",
})

// Use all storage tiers (memory + warm + cold)
g, err := lib.Open(garland.FileOptions{
    FilePath:     "large-file.txt",
    LoadingStyle: garland.AllStorage,
})

// Move inactive data to cold storage
g.Chill(garland.ChillInactiveForks)
Memory Management
lib, err := garland.Init(garland.LibraryOptions{
    MemorySoftLimit: 100 * 1024 * 1024, // 100 MB target
    MemoryHardLimit: 200 * 1024 * 1024, // 200 MB maximum
})

// Query memory usage
stats := g.MemoryUsage()

Architecture

Garland uses a rope data structure implemented as a balanced binary tree:

  • Leaf nodes store text data (default max 128KB per node)
  • Internal nodes aggregate metrics (byte count, rune count, line count)
  • Structural sharing enables memory-efficient versioning via copy-on-write

Each edit creates a new revision. Revisions are organized into forks, which branch when editing from a non-HEAD revision. This provides full undo history with the ability to explore alternative edit paths.

Storage Tiers

Tier Description Use Case
Memory Data held in RAM Active editing
Warm Original file on disk, verified by checksum Large files, read-heavy workloads
Cold Library-managed external storage Undo history, inactive forks

Transitions between tiers are automatic and transparent to the application.

REPL

An interactive REPL is included for testing and exploration:

go run ./cmd/garland-repl

Type help for available commands.

Testing

go test ./...

Tests are located alongside source files following Go conventions.

Benchmarking

A stress test tool is included that creates a 1GB test file and measures performance of common operations:

go run ./cmd/garland-bench

The benchmark tests:

  • File generation and opening (memory-only and tiered storage modes)
  • Seek and read operations across the file
  • Insert and delete operations at various sizes
  • Transaction cycles
  • Search operations
  • Undo/redo performance
  • Decoration operations
  • Memory management (chill to cold storage)

Expected runtime is 2-5 minutes on a modern machine (e.g., M2 MacBook). The benchmark creates temporary files that are automatically cleaned up on completion.

License

MIT License. See LICENSE for details.

Documentation

Overview

Package garland provides a rope-based data structure for efficient text/binary editing with multiple storage tiers, versioning, decorations, and lazy loading.

Index

Constants

View Source
const (
	// DefaultMaxLeafSize is the maximum bytes per leaf node.
	// Files larger than this are split into multiple leaves.
	DefaultMaxLeafSize = 128 * 1024 // 128KB

	// DefaultTargetLeafSize is the ideal leaf size (MaxLeafSize / 2).
	DefaultTargetLeafSize = 64 * 1024 // 64KB

	// DefaultMinLeafSize is the minimum leaf size before merging (MaxLeafSize / 4).
	DefaultMinLeafSize = 32 * 1024 // 32KB

	// DefaultInitialUsageWindow is the default byte range to keep in memory.
	DefaultInitialUsageWindow = 1024 * 1024 // 1MB
)

Default leaf size constants for tree building.

Variables

View Source
var (
	// ErrNotReady indicates that a position is not yet available during lazy loading.
	ErrNotReady = errors.New("position not yet available")

	// ErrInvalidPosition indicates that a position is out of bounds.
	ErrInvalidPosition = errors.New("position out of bounds")

	// ErrTimeout indicates that a blocking wait operation timed out.
	ErrTimeout = errors.New("operation timed out")

	// ErrInvalidUTF8 indicates that an operation would split a UTF-8 sequence.
	ErrInvalidUTF8 = errors.New("invalid UTF-8 sequence")

	// ErrOverlappingRanges indicates that source and destination ranges overlap
	// in an operation that doesn't allow overlap (e.g., Move).
	ErrOverlappingRanges = errors.New("source and destination ranges overlap")
)

Position errors

View Source
var (
	// ErrForkNotFound indicates that a fork ID does not exist.
	ErrForkNotFound = errors.New("fork not found")

	// ErrRevisionNotFound indicates that a revision does not exist in the current fork.
	ErrRevisionNotFound = errors.New("revision not found")
)

Versioning errors

View Source
var (
	// ErrColdStorageFailure indicates that a cold storage operation failed.
	ErrColdStorageFailure = errors.New("cold storage operation failed")

	// ErrWarmStorageMismatch indicates that the original file has changed (checksum mismatch).
	ErrWarmStorageMismatch = errors.New("warm storage checksum mismatch")

	// ErrReadOnly indicates that a region is read-only due to storage failure.
	ErrReadOnly = errors.New("region is read-only due to storage failure")

	// ErrNotFromOriginalFile indicates warm storage is not available for this node.
	ErrNotFromOriginalFile = errors.New("node is not from original file")

	// ErrMemoryPressure indicates that memory limits are exceeded and cannot be reduced.
	// This occurs when hard memory limit is set but no cold storage is configured,
	// or when cold storage is full/unavailable. The application should handle this
	// by closing unused garlands, reducing operations, or configuring cold storage.
	ErrMemoryPressure = errors.New("memory limit exceeded and cannot be reduced")
)

Storage errors

View Source
var (
	// ErrNotSupported indicates that an optional file system operation is not supported.
	ErrNotSupported = errors.New("operation not supported")

	// ErrFileNotOpen indicates that the file handle is not open.
	ErrFileNotOpen = errors.New("file not open")
)

File system errors

View Source
var (
	// ErrRegionOverlap indicates that optimized regions cannot overlap.
	ErrRegionOverlap = errors.New("optimized regions cannot overlap")

	// ErrRegionNotFound indicates that the optimized region does not exist.
	ErrRegionNotFound = errors.New("optimized region not found")
)

Region errors

View Source
var (
	// ErrTransactionPending indicates that an operation is not allowed during a transaction.
	ErrTransactionPending = errors.New("operation not allowed during transaction")

	// ErrTransactionPoisoned indicates that a transaction was poisoned by an inner rollback.
	ErrTransactionPoisoned = errors.New("transaction was poisoned by inner rollback")

	// ErrNoTransaction indicates that there is no active transaction.
	ErrNoTransaction = errors.New("no active transaction")
)

Transaction errors

View Source
var (
	// ErrNotALeaf indicates that an operation expected a leaf node but got an internal node.
	ErrNotALeaf = errors.New("expected leaf node")

	// ErrInternal indicates an internal consistency error (should not happen).
	ErrInternal = errors.New("internal error")
)

Tree structure errors

View Source
var (
	// ErrNoDataSource indicates that no data source was provided in FileOptions.
	ErrNoDataSource = errors.New("no data source provided")

	// ErrMultipleDataSources indicates that multiple data sources were provided.
	ErrMultipleDataSources = errors.New("multiple data sources provided")

	// ErrNoColdStorage indicates that cold storage is required but not configured.
	ErrNoColdStorage = errors.New("cold storage not configured")

	// ErrDataNotLoaded indicates that data is in cold/warm storage and needs to be thawed.
	ErrDataNotLoaded = errors.New("data not loaded - call Thaw() first")
)

Configuration errors

View Source
var (
	// ErrCursorNotFound indicates that the cursor does not belong to this garland.
	ErrCursorNotFound = errors.New("cursor not found")
)

Cursor errors

View Source
var (
	// ErrDecorationNotFound indicates that a decoration key does not exist.
	ErrDecorationNotFound = errors.New("decoration not found")
)

Decoration errors

Functions

This section is empty.

Types

type AbsoluteAddress

type AbsoluteAddress struct {
	Mode AddressMode

	// Byte is used when Mode is ByteMode.
	Byte int64

	// Rune is used when Mode is RuneMode.
	Rune int64

	// Line is used when Mode is LineRuneMode (0-indexed line number).
	Line int64

	// LineRune is used when Mode is LineRuneMode (0-indexed rune within line).
	LineRune int64
}

AbsoluteAddress specifies a position using one of three addressing modes.

func ByteAddress

func ByteAddress(pos int64) AbsoluteAddress

ByteAddress creates an AbsoluteAddress in byte mode.

func LineAddress

func LineAddress(line, runeInLine int64) AbsoluteAddress

LineAddress creates an AbsoluteAddress in line:rune mode.

func RuneAddress

func RuneAddress(pos int64) AbsoluteAddress

RuneAddress creates an AbsoluteAddress in rune mode.

type AddressMode

type AddressMode int

AddressMode specifies how a position is interpreted.

const (
	// ByteMode specifies an absolute byte position (0-indexed).
	ByteMode AddressMode = iota

	// RuneMode specifies an absolute rune/Unicode code point position (0-indexed).
	RuneMode

	// LineRuneMode specifies a line number and rune position within that line (both 0-indexed).
	// The newline character is considered the last character of its line.
	LineRuneMode
)

type AppendPolicy

type AppendPolicy int

AppendPolicy controls how the library handles detected appends.

const (
	// AppendPolicyAsk notifies the application and waits for a decision.
	AppendPolicyAsk AppendPolicy = iota

	// AppendPolicyIgnore ignores this append but asks again next time.
	AppendPolicyIgnore

	// AppendPolicyNever ignores all future appends permanently.
	AppendPolicyNever

	// AppendPolicyOnce loads this append but asks again next time.
	AppendPolicyOnce

	// AppendPolicyContinuous keeps loading appends automatically (tail mode).
	AppendPolicyContinuous
)

type ByteBufferRegion

type ByteBufferRegion struct {
	// contains filtered or unexported fields
}

ByteBufferRegion is a simple OptimizedRegion implementation using a byte slice. It's suitable for small to medium editing sessions.

func NewByteBufferRegion

func NewByteBufferRegion(initialContent []byte) *ByteBufferRegion

NewByteBufferRegion creates a new ByteBufferRegion with initial content.

func (*ByteBufferRegion) ByteCount

func (r *ByteBufferRegion) ByteCount() int64

ByteCount returns the number of bytes in the region.

func (*ByteBufferRegion) Content

func (r *ByteBufferRegion) Content() []byte

Content returns the full content of the region.

func (*ByteBufferRegion) DeleteBytes

func (r *ByteBufferRegion) DeleteBytes(offset, length int64) error

DeleteBytes deletes length bytes starting at offset.

func (*ByteBufferRegion) InsertBytes

func (r *ByteBufferRegion) InsertBytes(offset int64, data []byte) error

InsertBytes inserts data at the given offset.

func (*ByteBufferRegion) LineCount

func (r *ByteBufferRegion) LineCount() int64

LineCount returns the number of newlines in the region.

func (*ByteBufferRegion) ReadBytes

func (r *ByteBufferRegion) ReadBytes(offset, length int64) ([]byte, error)

ReadBytes reads length bytes starting at offset.

func (*ByteBufferRegion) RuneCount

func (r *ByteBufferRegion) RuneCount() int64

RuneCount returns the number of runes in the region.

type CacheTier

type CacheTier int

CacheTier indicates the priority level of a cached decoration.

const (
	// CacheTierWarm is for decorations seen during traversal but not actively used.
	CacheTierWarm CacheTier = iota
	// CacheTierHot is for decorations actively requested by the application.
	CacheTierHot
)

type ChangeResult

type ChangeResult struct {
	Fork     ForkID
	Revision RevisionID
}

ChangeResult contains version information after a mutation.

type ChillLevel

type ChillLevel int

ChillLevel specifies how aggressively to move data to cold storage.

const (
	// ChillInactiveForks moves data not used by the currently active fork
	// to cold storage. This is the gentlest level.
	ChillInactiveForks ChillLevel = iota

	// ChillOldHistory also moves data from older revisions in the undo
	// buffer that are more than a few steps back and not utilized by
	// any branching point.
	ChillOldHistory

	// ChillUnusedData moves all data not used at the current undo position.
	// This keeps only what's needed to display/edit the current state.
	ChillUnusedData

	// ChillEverything moves all data to cold storage. Use when switching
	// to another document or dropping to a shell. Data will be restored
	// from cold storage on access.
	ChillEverything
)

type ColdStorageInterface

type ColdStorageInterface interface {
	// Set stores data for a block within a folder.
	// Folder names are unique per loaded file.
	Set(folder, block string, data []byte) error

	// Get retrieves data for a block within a folder.
	Get(folder, block string) ([]byte, error)

	// Delete removes a block from a folder.
	Delete(folder, block string) error

	// DeleteFolder removes an empty folder.
	// Returns an error if the folder is not empty.
	DeleteFolder(folder string) error
}

ColdStorageInterface allows custom cold storage implementations.

type CopyResult

type CopyResult struct {
	ChangeResult
	DisplacedDecorations []RelativeDecoration // Decorations that were in the destination range (original positions)
}

CopyResult contains the result of a Copy operation.

type CountResult

type CountResult struct {
	Value    int64
	Complete bool // true if EOF has been reached
}

CountResult contains a count and whether it is complete.

type Cursor

type Cursor struct {
	// contains filtered or unexported fields
}

Cursor represents a position within a Garland with its own ready state. Cursors automatically update when content changes before their position.

func (*Cursor) BackDeleteBytes

func (c *Cursor) BackDeleteBytes(length int64, includeLineDecorations bool) ([]RelativeDecoration, ChangeResult, error)

BackDeleteBytes deletes `length` bytes BEFORE the cursor position. Cursor moves to the start of the deleted range (its new position). Returns decorations from the deleted range.

func (*Cursor) BackDeleteRunes

func (c *Cursor) BackDeleteRunes(length int64, includeLineDecorations bool) ([]RelativeDecoration, ChangeResult, error)

BackDeleteRunes deletes `length` runes BEFORE the cursor position. Cursor moves to the start of the deleted range (its new position). Returns decorations from the deleted range.

func (*Cursor) BeginOptimizedRegion

func (c *Cursor) BeginOptimizedRegion(startByte, endByte int64) error

BeginOptimizedRegion explicitly starts an optimized region at the specified bounds. This works regardless of cursor mode and dissolves any existing region first.

func (*Cursor) BytePos

func (c *Cursor) BytePos() int64

BytePos returns the cursor's absolute byte position.

func (*Cursor) CopyBytes

func (c *Cursor) CopyBytes(srcStart, srcEnd, dstStart, dstEnd int64, decorationsToAdd []RelativeDecoration, insertBefore bool) (CopyResult, error)

CopyBytes copies a byte range to a new location. All addresses are interpreted as positions in the original document before any changes. Source and destination ranges may overlap for Copy (source is snapshotted first). - srcStart, srcEnd: source byte range [srcStart, srcEnd) - dstStart, dstEnd: destination byte range to replace [dstStart, dstEnd) - decorationsToAdd: decorations to add to the copied content (like Insert) - insertBefore: if true, displaced decorations consolidate to end of new content Returns CopyResult with the displaced decorations from the destination range.

func (*Cursor) CountRegex

func (c *Cursor) CountRegex(pattern string, caseInsensitive bool) (int, error)

CountRegex counts regex matches in the document.

func (*Cursor) CountString

func (c *Cursor) CountString(needle string, opts SearchOptions) (int, error)

CountString counts occurrences of needle in the document.

func (*Cursor) DeleteBytes

func (c *Cursor) DeleteBytes(length int64, includeLineDecorations bool) ([]RelativeDecoration, ChangeResult, error)

DeleteBytes deletes `length` bytes starting at cursor position. Returns decorations from the deleted range. If includeLineDecorations is true, also returns (but does not move) decorations from partially affected lines.

func (*Cursor) DeleteRunes

func (c *Cursor) DeleteRunes(length int64, includeLineDecorations bool) ([]RelativeDecoration, ChangeResult, error)

DeleteRunes deletes `length` runes starting at cursor position. Returns decorations from the deleted range. If includeLineDecorations is true, also returns (but does not move) decorations from partially affected lines.

func (*Cursor) FindNext

func (c *Cursor) FindNext(needle string, opts SearchOptions) (*SearchResult, error)

FindNext finds the next occurrence and moves cursor to it. Returns the match or nil if not found.

func (*Cursor) FindNextRegex

func (c *Cursor) FindNextRegex(pattern string, opts RegexOptions) (*SearchResult, error)

FindNextRegex finds the next regex match and moves cursor to it.

func (*Cursor) FindRegex

func (c *Cursor) FindRegex(pattern string, opts RegexOptions) (*SearchResult, error)

FindRegex searches for a regex pattern starting from the cursor position. Returns the first match found, or nil if no match. The cursor is NOT moved by this operation.

func (*Cursor) FindRegexAll

func (c *Cursor) FindRegexAll(pattern string, opts RegexOptions) ([]SearchResult, error)

FindRegexAll finds all regex matches in the document.

func (*Cursor) FindString

func (c *Cursor) FindString(needle string, opts SearchOptions) (*SearchResult, error)

FindString searches for a string starting from the cursor position. Returns the first match found, or nil if no match. The cursor is NOT moved by this operation.

func (*Cursor) FindStringAll

func (c *Cursor) FindStringAll(needle string, opts SearchOptions) ([]SearchResult, error)

FindStringAll finds all occurrences of a string in the document. Returns all matches in document order (or reverse order if Backward).

func (*Cursor) HasOptimizedRegion

func (c *Cursor) HasOptimizedRegion() bool

HasOptimizedRegion returns true if the cursor has an active optimized region.

func (*Cursor) InsertBytes

func (c *Cursor) InsertBytes(data []byte, decorations []RelativeDecoration, insertBefore bool) (ChangeResult, error)

InsertBytes inserts raw bytes at the cursor position. If insertBefore is true, insertion occurs before any existing cursors/decorations at this position; otherwise after. After insertion, cursor advances to the end of the inserted content.

func (*Cursor) InsertString

func (c *Cursor) InsertString(data string, decorations []RelativeDecoration, insertBefore bool) (ChangeResult, error)

InsertString inserts a string at the cursor position. Relative decoration positions are measured in runes. If insertBefore is true, insertion occurs before any existing cursors/decorations at this position; otherwise after. After insertion, cursor advances to the end of the inserted content.

func (*Cursor) IsReady

func (c *Cursor) IsReady() bool

IsReady returns true if the read-ahead threshold has been met relative to this cursor's position.

func (*Cursor) LinePos

func (c *Cursor) LinePos() (line, runeInLine int64)

LinePos returns the cursor's line number and rune position within that line. Both values are 0-indexed.

func (*Cursor) MatchRegex

func (c *Cursor) MatchRegex(pattern string, caseInsensitive bool) (bool, *SearchResult, error)

MatchRegex checks if the regex matches at the current cursor position. Returns true if the pattern matches starting exactly at cursor position.

func (*Cursor) Mode

func (c *Cursor) Mode() CursorMode

Mode returns the cursor's current mode.

func (*Cursor) MoveBytes

func (c *Cursor) MoveBytes(srcStart, srcEnd, dstStart, dstEnd int64, insertBefore bool) (MoveResult, error)

MoveBytes moves a byte range to a new location. All addresses are interpreted as positions in the original document before any changes. Source and destination ranges cannot overlap for Move. Decorations in the source range move with the content. Decorations in the destination range are consolidated and returned. - srcStart, srcEnd: source byte range [srcStart, srcEnd) - dstStart, dstEnd: destination byte range to replace [dstStart, dstEnd) - insertBefore: if true, displaced decorations consolidate to end of new content Returns MoveResult with the displaced decorations from the destination range.

func (*Cursor) OptimizedRegionBounds

func (c *Cursor) OptimizedRegionBounds() (start, end int64, ok bool)

OptimizedRegionBounds returns the content bounds of the active region. Returns (0, 0, false) if no region is active.

func (*Cursor) OptimizedRegionGraceWindow

func (c *Cursor) OptimizedRegionGraceWindow() (start, end int64, ok bool)

OptimizedRegionGraceWindow returns the grace window bounds of the active region. Returns (0, 0, false) if no region is active.

func (*Cursor) OptimizedRegionSerial

func (c *Cursor) OptimizedRegionSerial() int64

OptimizedRegionSerial returns the serial number of the cursor's active region, or -1 if no region is active. Useful for debugging region lifecycle.

func (*Cursor) OverwriteBytes

func (c *Cursor) OverwriteBytes(length int64, newData []byte) ([]RelativeDecoration, ChangeResult, error)

OverwriteBytes replaces `length` bytes at cursor position with new data. This is more efficient than separate delete + insert for binary editing. The operation properly accounts for changes in line counts (newlines) and rune counts (UTF-8 sequences). Returns decorations that were in the overwritten range. Cursor position is not changed after the operation.

func (*Cursor) OverwriteBytesWithDecorations

func (c *Cursor) OverwriteBytesWithDecorations(length int64, newData []byte, decorationsToAdd []RelativeDecoration, insertBefore bool) ([]RelativeDecoration, ChangeResult, error)

OverwriteBytesWithDecorations replaces bytes with new data, adding decorations. - decorationsToAdd: decorations to add to the new content (relative to new content start) - insertBefore: if true, displaced decorations consolidate to end; if false, to start Returns the original decorations from the overwritten range with their original relative positions.

func (*Cursor) Position

func (c *Cursor) Position() CursorPosition

Position returns the cursor's position in all coordinate systems.

func (*Cursor) ReadBytes

func (c *Cursor) ReadBytes(length int64) ([]byte, error)

ReadBytes reads `length` bytes starting at cursor position. After reading, cursor advances past the read data.

func (*Cursor) ReadLine

func (c *Cursor) ReadLine() (string, error)

ReadLine reads the entire line the cursor is on. Note: Does NOT advance cursor (line-oriented reading is typically peek-like).

func (*Cursor) ReadString

func (c *Cursor) ReadString(length int64) (string, error)

ReadString reads `length` runes starting at cursor position as a string. After reading, cursor advances past the read data.

func (*Cursor) ReplaceRegex

func (c *Cursor) ReplaceRegex(pattern, replacement string, opts RegexOptions) (bool, ChangeResult, error)

ReplaceRegex replaces the first regex match with replacement. Replacement can include $1, $2, etc. for capture groups.

func (*Cursor) ReplaceRegexAll

func (c *Cursor) ReplaceRegexAll(pattern, replacement string, opts RegexOptions) (int, ChangeResult, error)

ReplaceRegexAll replaces all regex matches with replacement.

func (*Cursor) ReplaceRegexCount

func (c *Cursor) ReplaceRegexCount(pattern, replacement string, count int, opts RegexOptions) (int, ChangeResult, error)

ReplaceRegexCount replaces up to count regex matches with replacement.

func (*Cursor) ReplaceString

func (c *Cursor) ReplaceString(needle, replacement string, opts SearchOptions) (bool, ChangeResult, error)

ReplaceString replaces the first occurrence of needle with replacement. Search starts from cursor position. Returns the change result and whether a replacement was made.

func (*Cursor) ReplaceStringAll

func (c *Cursor) ReplaceStringAll(needle, replacement string, opts SearchOptions) (int, ChangeResult, error)

ReplaceStringAll replaces all occurrences of needle with replacement. Returns the number of replacements made.

func (*Cursor) ReplaceStringCount

func (c *Cursor) ReplaceStringCount(needle, replacement string, count int, opts SearchOptions) (int, ChangeResult, error)

ReplaceStringCount replaces up to count occurrences of needle with replacement. If count is -1, replaces all occurrences. Returns the number of replacements made.

func (*Cursor) RunePos

func (c *Cursor) RunePos() int64

RunePos returns the cursor's absolute rune position.

func (*Cursor) SeekByWord

func (c *Cursor) SeekByWord(n int) (int, error)

SeekByWord moves the cursor by n words. Positive n moves forward, negative n moves backward. A word is defined as a sequence of alphanumeric/underscore characters, or a sequence of non-whitespace non-word characters. Returns the number of words actually moved (may be less than requested at boundaries).

func (*Cursor) SeekByte

func (c *Cursor) SeekByte(pos int64) error

SeekByte moves the cursor to an absolute byte position. Blocks indefinitely until the position is available during lazy loading. Use SeekByteWithTimeout for timeout control, or check IsByteReady first for non-blocking.

func (*Cursor) SeekByteWithTimeout

func (c *Cursor) SeekByteWithTimeout(pos int64, timeout time.Duration) error

SeekByteWithTimeout moves the cursor to an absolute byte position with timeout control. If timeout is 0, returns ErrNotReady immediately if position not available. If timeout is negative, blocks indefinitely. If timeout is positive, waits up to that duration before returning ErrTimeout.

func (*Cursor) SeekLine

func (c *Cursor) SeekLine(line, runeInLine int64) error

SeekLine moves the cursor to a line and rune-within-line position. Line and rune are both 0-indexed. The newline is the last character of its line. Blocks indefinitely until the position is available during lazy loading. Use SeekLineWithTimeout for timeout control, or check IsLineReady first for non-blocking.

func (*Cursor) SeekLineEnd

func (c *Cursor) SeekLineEnd() error

SeekLineEnd moves the cursor to the end of the current line. The cursor is positioned after the last character before the newline (or at EOF).

func (*Cursor) SeekLineStart

func (c *Cursor) SeekLineStart() error

SeekLineStart moves the cursor to the beginning of the current line.

func (*Cursor) SeekLineWithTimeout

func (c *Cursor) SeekLineWithTimeout(line, runeInLine int64, timeout time.Duration) error

SeekLineWithTimeout moves the cursor to a line and rune-within-line position with timeout control. If timeout is 0, returns ErrNotReady immediately if position not available. If timeout is negative, blocks indefinitely. If timeout is positive, waits up to that duration before returning ErrTimeout.

func (*Cursor) SeekRelativeBytes

func (c *Cursor) SeekRelativeBytes(delta int64) error

SeekRelativeBytes moves the cursor relative to its current byte position. Positive delta moves forward, negative moves backward. Clamps to valid range [0, byteCount].

func (*Cursor) SeekRelativeRunes

func (c *Cursor) SeekRelativeRunes(delta int64) error

SeekRelativeRunes moves the cursor relative to its current rune position. Positive delta moves forward, negative moves backward. Clamps to valid range [0, runeCount].

func (*Cursor) SeekRune

func (c *Cursor) SeekRune(pos int64) error

SeekRune moves the cursor to an absolute rune position. Blocks indefinitely until the position is available during lazy loading. Use SeekRuneWithTimeout for timeout control, or check IsRuneReady first for non-blocking.

func (*Cursor) SeekRuneWithTimeout

func (c *Cursor) SeekRuneWithTimeout(pos int64, timeout time.Duration) error

SeekRuneWithTimeout moves the cursor to an absolute rune position with timeout control. If timeout is 0, returns ErrNotReady immediately if position not available. If timeout is negative, blocks indefinitely. If timeout is positive, waits up to that duration before returning ErrTimeout.

func (*Cursor) SetMode

func (c *Cursor) SetMode(mode CursorMode)

SetMode sets the cursor's mode. Changing mode does not affect any currently active optimized region.

func (*Cursor) TruncateToEOF

func (c *Cursor) TruncateToEOF() (ChangeResult, error)

TruncateToEOF deletes everything from cursor position to end of file.

func (*Cursor) WaitReady

func (c *Cursor) WaitReady() error

WaitReady blocks until the cursor becomes ready.

type CursorMode

type CursorMode int

CursorMode determines how a cursor interacts with optimized regions.

const (
	// CursorModeHuman automatically creates optimized regions on edit operations.
	// This is the default mode for interactive editing.
	CursorModeHuman CursorMode = iota

	// CursorModeProcess does not auto-create regions; uses transactions instead.
	// Use this mode for programmatic/scripted operations.
	CursorModeProcess
)

type CursorPosition

type CursorPosition struct {
	BytePos  int64
	RunePos  int64
	Line     int64
	LineRune int64
}

CursorPosition stores a cursor's position in all coordinate systems.

type Decoration

type Decoration struct {
	Key      string
	Position int64 // relative byte offset within the node
}

Decoration represents a decoration stored within a node.

type DecorationCacheEntry

type DecorationCacheEntry struct {
	// Last known location (hint for search)
	LastKnownFork   ForkID
	LastKnownRev    RevisionID
	LastKnownNode   NodeID // 0 means "confirmed not present at this fork/revision"
	LastKnownOffset int64

	// Cache management
	Tier       CacheTier // Hot = actively used, Warm = seen during traversal
	LastAccess time.Time
}

DecorationCacheEntry caches the last known location of a decoration. The presence of an entry indicates the key has been used at some point. The cached location is a hint - it may be stale if fork/revision differs.

type DecorationEntry

type DecorationEntry struct {
	Key     string
	Address *AbsoluteAddress // nil to delete the decoration
}

DecorationEntry represents a decoration with its absolute position.

type DivergenceDirection

type DivergenceDirection int

DivergenceDirection indicates the relationship of a fork to the current fork.

const (
	// BranchedFrom means the current fork split off from the specified fork.
	BranchedFrom DivergenceDirection = iota
	// BranchedInto means the specified fork split off from the current fork.
	BranchedInto
)

type FileHandle

type FileHandle interface{}

FileHandle represents an open file.

type FileOptions

type FileOptions struct {
	// LoadingStyle determines storage tier availability
	LoadingStyle LoadingStyle

	// Data source (exactly one must be provided)
	FilePath    string              // load from file path using default FS
	FileSystem  FileSystemInterface // custom file system (use with FilePath)
	DataBytes   []byte              // literal byte content
	DataString  string              // literal string content
	DataChannel chan []byte         // streaming input

	// Initial decorations (optional, at most one)
	Decorations      []DecorationEntry // literal list
	DecorationChan   chan DecorationEntry
	DecorationPath   string // load from dump file
	DecorationString string // parse from dump format

	// Tree structure options
	// MaxLeafSize is the maximum bytes per leaf node (default 128KB).
	// Larger files are split into a balanced tree of leaves.
	// Target leaf size is MaxLeafSize/2, minimum is MaxLeafSize/4.
	MaxLeafSize int64

	// InitialUsageStart and InitialUsageEnd define a byte range to keep in memory.
	// Nodes outside this range are immediately chilled to cold storage after loading.
	// This avoids loading a huge file fully into RAM just to chill it immediately.
	// Set InitialUsageEnd to -1 (default) to use a reasonable default window.
	// Set both to 0 to chill everything immediately (pure cold storage load).
	InitialUsageStart int64
	InitialUsageEnd   int64 // -1 means auto (defaults to 1MB or file size, whichever is smaller)

	// Ready thresholds - ALL specified (non-zero) must be met
	// Measured from beginning of file at initial load
	ReadyLines int64
	ReadyBytes int64
	ReadyRunes int64
	ReadyAll   bool

	// Lazy read-ahead - ALL specified (non-zero) must be met
	// Measured from highest seek position after any seek
	ReadAheadLines int64
	ReadAheadBytes int64
	ReadAheadRunes int64
	ReadAheadAll   bool
}

FileOptions configures how a Garland is opened.

type FileSystemInterface

type FileSystemInterface interface {
	// Required methods
	Open(name string, mode OpenMode) (FileHandle, error)
	SeekByte(handle FileHandle, pos int64) error
	ReadBytes(handle FileHandle, length int) ([]byte, error)
	IsEOF(handle FileHandle) bool
	Close(handle FileHandle) error

	// Optional methods (may return ErrNotSupported)
	HasChanged(handle FileHandle) (bool, error)
	FileSize(handle FileHandle) (int64, error)
	BlockChecksum(handle FileHandle, start, length int64) ([]byte, error)
	WriteBytes(handle FileHandle, data []byte) error
	Truncate(handle FileHandle, size int64) error

	// Convenience methods for file operations
	WriteFile(name string, data []byte) error
	ReadFile(name string) ([]byte, error)

	// Directory operations
	MkdirAll(path string) error
	Remove(name string) error
	Rmdir(path string) error // Only removes empty directories
}

FileSystemInterface abstracts file operations for custom protocols. The library provides a default implementation for local files.

type ForkDivergence

type ForkDivergence struct {
	Fork          ForkID
	DivergenceRev RevisionID          // revision at which split occurred
	Direction     DivergenceDirection // relationship to current fork
}

ForkDivergence describes where a fork split occurred.

type ForkID

type ForkID uint64

ForkID uniquely identifies a fork within a Garland.

type ForkInfo

type ForkInfo struct {
	ID              ForkID
	ParentFork      ForkID
	ParentRevision  RevisionID // revision at which this fork split from parent
	HighestRevision RevisionID
	PrunedUpTo      RevisionID // revisions < this have been pruned from this fork's view
	Deleted         bool       // true if fork is soft-deleted (data may still exist for child forks)
}

ForkInfo contains metadata about a fork.

type ForkRevision

type ForkRevision struct {
	Fork     ForkID
	Revision RevisionID
}

ForkRevision is a composite key for looking up versioned state.

type Garland

type Garland struct {
	// contains filtered or unexported fields
}

Garland is the main data structure representing an editable file.

func (*Garland) AcknowledgeSourceChange

func (g *Garland) AcknowledgeSourceChange(reload bool) error

AcknowledgeSourceChange acknowledges a detected source change. Call this after the user has been notified and made a decision.

func (*Garland) ByteCount

func (g *Garland) ByteCount() CountResult

ByteCount returns total bytes (or known bytes if still loading). For revisions created during streaming, includes the streaming remainder.

func (*Garland) ByteToLineRune

func (g *Garland) ByteToLineRune(bytePos int64) (line, runeInLine int64, err error)

ByteToLineRune converts a byte position to a line:rune position.

func (*Garland) ByteToRune

func (g *Garland) ByteToRune(bytePos int64) (int64, error)

ByteToRune converts a byte position to a rune position.

func (*Garland) CheckMemoryPressure

func (g *Garland) CheckMemoryPressure() MaintenanceStats

CheckMemoryPressure checks if memory limits are exceeded and performs appropriate maintenance. Called after mutations. Sets memoryPressure flag if hard limit exceeded and can't be reduced.

func (*Garland) CheckSourceMetadata

func (g *Garland) CheckSourceMetadata() (SourceChangeInfo, error)

CheckSourceMetadata performs a cheap metadata check on the source file. This only stats the file, it doesn't read any content.

func (*Garland) Checkpoint

func (g *Garland) Checkpoint() error

Checkpoint commits all active optimized regions across all cursors. This creates a single revision containing all pending region changes. Call this to establish an undo point after a burst of edits.

func (*Garland) Chill

func (g *Garland) Chill(level ChillLevel) error

Chill moves data to cold storage based on the specified aggressiveness level. This frees memory by storing data externally, to be reloaded on demand.

For MemoryOnly files, this is a no-op by design.

Levels:

  • ChillInactiveForks: Only chill data not used by the current fork
  • ChillOldHistory: Also chill old undo history beyond recent revisions
  • ChillUnusedData: Chill everything not used at current revision
  • ChillEverything: Chill all data (for switching documents or shells)

func (*Garland) Close

func (g *Garland) Close() error

Close releases resources associated with the Garland.

func (*Garland) CurrentFork

func (g *Garland) CurrentFork() ForkID

CurrentFork returns the current fork ID.

func (*Garland) CurrentRevision

func (g *Garland) CurrentRevision() RevisionID

CurrentRevision returns the current revision number within the current fork.

func (*Garland) Decorate

func (g *Garland) Decorate(entries []DecorationEntry) (ChangeResult, error)

Decorate adds, updates, or removes decorations at absolute positions. All changes are applied as a single revision. Pass nil Address in a DecorationEntry to delete that decoration.

func (*Garland) DeleteFork

func (g *Garland) DeleteFork(fork ForkID) error

DeleteFork soft-deletes a fork, preventing further navigation to it. The fork's data remains until no other forks depend on it. Cannot delete the current fork or the last remaining non-deleted fork.

func (*Garland) DisableSourceWatch

func (g *Garland) DisableSourceWatch()

DisableSourceWatch stops periodic monitoring of the source file.

func (*Garland) DumpDecorations

func (g *Garland) DumpDecorations(fs FileSystemInterface, path string) error

DumpDecorations writes all decorations to a file in INI-like format. If fs is nil, uses the Garland's source filesystem.

func (*Garland) EnableSourceWatch

func (g *Garland) EnableSourceWatch(interval time.Duration)

EnableSourceWatch starts periodic monitoring of the source file.

func (*Garland) FindForksBetween

func (g *Garland) FindForksBetween(revisionFirst, revisionLast RevisionID) ([]ForkDivergence, error)

FindForksBetween returns all fork divergence points between two revisions from the perspective of the current fork.

func (*Garland) ForceRebalance

func (g *Garland) ForceRebalance() MaintenanceStats

ForceRebalance performs a full tree rebalance (not incremental). Use sparingly as this can be expensive for large trees.

func (*Garland) ForkSeek

func (g *Garland) ForkSeek(fork ForkID) error

ForkSeek switches to a different fork. Retains current revision if it exists in both forks, otherwise retreats to the last common revision.

func (*Garland) GetDecorationPosition

func (g *Garland) GetDecorationPosition(key string) (AbsoluteAddress, error)

GetDecorationPosition returns the current position of a decoration by key. Uses registry-based O(1) existence check and cached location hints for fast lookup.

func (*Garland) GetDecorationsInByteRange

func (g *Garland) GetDecorationsInByteRange(start, end int64) ([]DecorationEntry, error)

GetDecorationsInByteRange returns all decorations within [start, end).

func (*Garland) GetDecorationsOnLine

func (g *Garland) GetDecorationsOnLine(line int64) ([]DecorationEntry, error)

GetDecorationsOnLine returns all decorations on the specified line.

func (*Garland) GetForkInfo

func (g *Garland) GetForkInfo(fork ForkID) (*ForkInfo, error)

GetForkInfo returns information about a specific fork.

func (*Garland) GetRevisionInfo

func (g *Garland) GetRevisionInfo(revision RevisionID) (*RevisionInfo, error)

GetRevisionInfo returns information about a specific revision.

func (*Garland) GetRevisionRange

func (g *Garland) GetRevisionRange(start, end RevisionID) ([]RevisionInfo, error)

GetRevisionRange returns info for revisions in [start, end] inclusive.

func (*Garland) GetSnapshotStats

func (g *Garland) GetSnapshotStats() SnapshotStats

GetSnapshotStats returns statistics about node snapshots. Useful for testing and diagnostics to verify garbage collection.

func (*Garland) GetTreeInfo

func (g *Garland) GetTreeInfo() *TreeNodeInfo

GetTreeInfo returns a snapshot of the current tree structure for visualization.

func (*Garland) InTransaction

func (g *Garland) InTransaction() bool

InTransaction returns true if any transaction is active.

func (*Garland) IncrementalRebalance

func (g *Garland) IncrementalRebalance(affectedPath []NodeID) MaintenanceStats

IncrementalRebalance performs budgeted tree rebalancing along a path. Called after mutations with the path of affected nodes.

func (*Garland) IsByteReady

func (g *Garland) IsByteReady(pos int64) bool

IsByteReady returns true if the given byte position is available for reading. This is a non-blocking check that can be used to guard against blocking waits. A negative position returns false. A position beyond EOF returns true only when loading is complete (at which point seeking there would return ErrInvalidPosition).

func (*Garland) IsComplete

func (g *Garland) IsComplete() bool

IsComplete returns true if EOF has been reached during loading.

func (*Garland) IsLineReady

func (g *Garland) IsLineReady(line int64) bool

IsLineReady returns true if the given line number is available for reading. This is a non-blocking check that can be used to guard against blocking waits.

func (*Garland) IsReady

func (g *Garland) IsReady() bool

IsReady returns true if initial ready threshold has been met.

func (*Garland) IsRuneReady

func (g *Garland) IsRuneReady(pos int64) bool

IsRuneReady returns true if the given rune position is available for reading. This is a non-blocking check that can be used to guard against blocking waits.

func (*Garland) LineCount

func (g *Garland) LineCount() CountResult

LineCount returns total newlines (or known newlines if still loading).

func (*Garland) LineRuneToByte

func (g *Garland) LineRuneToByte(line, runeInLine int64) (int64, error)

LineRuneToByte converts a line:rune position to a byte position.

func (*Garland) ListForks

func (g *Garland) ListForks() []ForkInfo

ListForks returns information about all forks.

func (*Garland) LoadAppendedContent

func (g *Garland) LoadAppendedContent() (int64, error)

LoadAppendedContent loads newly appended content from the source file. Only valid after VerifyBoundaryForAppend succeeds.

func (*Garland) LoadDecorations

func (g *Garland) LoadDecorations(fs FileSystemInterface, path string) error

LoadDecorations loads decorations from a file using the INI format. Unknown sections are ignored for future compatibility. Comments are lines starting with ';' or '# ' (hash followed by space). End-of-line comments start with whitespace followed by ';' or '#'.

func (*Garland) LoadDecorationsFromString

func (g *Garland) LoadDecorationsFromString(content string) error

LoadDecorationsFromString loads decorations from an INI-formatted string. Unknown sections are ignored for future compatibility. Comments are lines starting with ';' or '# ' (hash followed by space). End-of-line comments start with whitespace followed by ';' or '#'.

func (*Garland) MemoryUsage

func (g *Garland) MemoryUsage() MemoryStats

MemoryUsage returns current memory statistics for this Garland.

func (*Garland) NeedsRebalancing

func (g *Garland) NeedsRebalancing() bool

NeedsRebalancing checks if the tree is significantly unbalanced.

func (*Garland) NewCursor

func (g *Garland) NewCursor() *Cursor

NewCursor creates a new cursor at position 0.

func (*Garland) NodeManipulations added in v0.1.1

func (g *Garland) NodeManipulations() int64

NodeManipulations returns the count of node operations since the last rebalance. This is a cheap counter incremented during tree mutations.

func (*Garland) Prune

func (g *Garland) Prune(keepFromRevision RevisionID) error

Prune removes revision history before keepFromRevision in the current fork. Revisions >= keepFromRevision are kept. This sets the fork's PrunedUpTo watermark and cleans up: - RevisionInfo entries for pruned revisions - Cursor position history for pruned revisions - Node snapshots that are no longer needed by any fork

Shared revisions (inherited from parent forks) are only truly deleted when all forks that share them have pruned past that point.

func (*Garland) RefreshSourceInfo

func (g *Garland) RefreshSourceInfo() error

RefreshSourceInfo updates stored metadata after a save.

func (*Garland) RemoveCursor

func (g *Garland) RemoveCursor(c *Cursor) error

RemoveCursor removes a cursor from the Garland.

func (*Garland) RuneCount

func (g *Garland) RuneCount() CountResult

RuneCount returns total runes (or known runes if still loading).

func (*Garland) RuneToByte

func (g *Garland) RuneToByte(runePos int64) (int64, error)

RuneToByte converts a rune position to a byte position.

func (*Garland) Save

func (g *Garland) Save() error

Save overwrites the original file with the current content. Caller asserts that this replaces any warm storage source. Returns ErrNoDataSource if there is no original file path.

func (*Garland) SaveAs

func (g *Garland) SaveAs(fs FileSystemInterface, name string) error

SaveAs writes the current content to a new location. Warm storage remains pointing to the original file (if any).

func (*Garland) SetAppendPolicy

func (g *Garland) SetAppendPolicy(policy AppendPolicy)

SetAppendPolicy sets the policy for handling detected appends.

func (*Garland) SetSourceChangeHandler

func (g *Garland) SetSourceChangeHandler(handler SourceChangeHandler)

SetSourceChangeHandler sets a callback for source file changes.

func (*Garland) SetVerifyOnRead

func (g *Garland) SetVerifyOnRead(enabled bool)

SetVerifyOnRead sets whether warm storage reads should verify checksums.

func (*Garland) SourcePath

func (g *Garland) SourcePath() string

SourcePath returns the path to the source file, if any.

func (*Garland) SourceStatus

func (g *Garland) SourceStatus() SourceChangeStatus

SourceStatus returns the current source change status.

func (*Garland) Thaw

func (g *Garland) Thaw() error

Thaw restores data from cold storage to memory for the current fork. This is the inverse of Chill - it loads data back from cold storage.

func (*Garland) ThawRange

func (g *Garland) ThawRange(startByte, endByte int64) error

ThawRange restores cold data for a specific byte range at the current revision. This is RAM-safe for large files - it only thaws the nodes needed to read the specified byte range instead of the entire file.

func (*Garland) ThawRevision

func (g *Garland) ThawRevision(startRev, endRev RevisionID) error

ThawRevision restores cold data for a specific revision range in the current fork. WARNING: This thaws ALL data for the revision(s), which could be very large. For large files, prefer ThawRange() to thaw only the bytes you need.

func (*Garland) TransactionCommit

func (g *Garland) TransactionCommit() (ChangeResult, error)

TransactionCommit commits the current transaction.

func (*Garland) TransactionDepth

func (g *Garland) TransactionDepth() int

TransactionDepth returns the current nesting depth (0 = no active transaction).

func (*Garland) TransactionRollback

func (g *Garland) TransactionRollback() error

TransactionRollback discards all changes in the current transaction.

func (*Garland) TransactionStart

func (g *Garland) TransactionStart(name string) error

TransactionStart begins a new transaction with an optional descriptive name.

func (*Garland) UndoSeek

func (g *Garland) UndoSeek(revision RevisionID) error

UndoSeek navigates to a specific revision within the current fork. Cannot seek forward past the highest revision in this fork. Seeking backwards then making a change creates a new fork.

func (*Garland) VerifyBoundaryForAppend

func (g *Garland) VerifyBoundaryForAppend() error

VerifyBoundaryForAppend verifies just the boundary block to confirm an append. Returns nil if the boundary is intact (safe to treat as append).

type LeafSearchResult

type LeafSearchResult struct {
	Node          *Node         // the leaf node
	Snapshot      *NodeSnapshot // the node's snapshot at current version
	ByteOffset    int64         // byte offset from start of this leaf to target
	RuneOffset    int64         // rune offset from start of this leaf to target
	LeafByteStart int64         // absolute byte position where this leaf starts
	LeafRuneStart int64         // absolute rune position where this leaf starts
}

LeafSearchResult contains information about a leaf node found during tree traversal.

type Library

type Library struct {
	// contains filtered or unexported fields
}

Library manages garland instances and shared resources like cold storage.

func Init

func Init(options LibraryOptions) (*Library, error)

Init initializes the garland library with cold storage options. Cold storage is shared across all files opened through this library instance.

func (*Library) CheckMemoryPressureError

func (lib *Library) CheckMemoryPressureError() error

CheckMemoryPressureError returns ErrMemoryPressure if the library is under memory pressure, nil otherwise. Useful for checking before mutations.

func (*Library) ChillToTarget

func (lib *Library) ChillToTarget() MaintenanceStats

ChillToTarget performs incremental chilling until memory is below the soft limit. It respects the budget per tick and returns when either: - Memory is below soft limit - No more candidates to chill - Budget exhausted for this tick

func (*Library) Close

func (lib *Library) Close() error

Close properly shuts down a Garland, including stopping maintenance.

func (*Library) IncrementalChill

func (lib *Library) IncrementalChill(budget int) MaintenanceStats

IncrementalChill performs budgeted LRU-based chilling across all Garlands. It chills at most `budget` nodes, prioritizing least-recently-used. Returns the number of nodes chilled and bytes freed.

func (*Library) MemoryPressure

func (lib *Library) MemoryPressure() bool

MemoryPressure returns true if the library is under memory pressure (hard limit exceeded and cannot be reduced). Applications should check this before performing memory-intensive operations.

func (*Library) Open

func (lib *Library) Open(options FileOptions) (*Garland, error)

Open creates or loads a Garland from various sources.

func (*Library) StopMaintenance

func (lib *Library) StopMaintenance()

StopMaintenance stops the background maintenance worker.

func (*Library) TotalMemoryUsage

func (lib *Library) TotalMemoryUsage() int64

TotalMemoryUsage returns the total memory usage across all Garlands in the library.

type LibraryOptions

type LibraryOptions struct {
	// ColdStoragePath is a filesystem path for cold storage.
	// Either this or ColdStorageBackend must be provided (or both).
	ColdStoragePath string

	// ColdStorageBackend is a custom cold storage implementation.
	ColdStorageBackend ColdStorageInterface

	// Memory management options
	// MemorySoftLimit is the target memory usage in bytes.
	// When exceeded, background maintenance starts chilling LRU nodes.
	// 0 means disabled (default).
	MemorySoftLimit int64

	// MemoryHardLimit is the maximum memory usage in bytes.
	// When exceeded, immediate (but still budgeted) chilling occurs.
	// 0 means disabled (default).
	MemoryHardLimit int64

	// ChillBudgetPerTick is the maximum nodes to chill per maintenance tick.
	// Default is 5 if not specified.
	ChillBudgetPerTick int

	// RebalanceBudget is the maximum rotations per mutation operation.
	// Default is 2 if not specified.
	RebalanceBudget int

	// BackgroundInterval is how often the background maintenance worker runs.
	// 0 means disabled (maintenance only happens opportunistically).
	// Typical value: 100ms to 1s.
	BackgroundInterval time.Duration
}

LibraryOptions configures the garland library.

type LineSearchResult

type LineSearchResult struct {
	LeafResult    *LeafSearchResult
	LineByteStart int64 // absolute byte position where target line starts
	LineRuneStart int64 // absolute rune position where target line starts
}

LineSearchResult contains information about a line found during tree traversal.

type LineStart

type LineStart struct {
	// ByteOffset is the byte offset from the start of the node where this line begins.
	ByteOffset int64

	// RuneOffset is the rune offset from the start of the node where this line begins.
	RuneOffset int64
}

LineStart represents the starting position of a line within a leaf node.

type Loader

type Loader struct {
	// contains filtered or unexported fields
}

Loader handles background loading of data from various sources.

type LoadingStyle

type LoadingStyle int

LoadingStyle determines which storage tiers are available.

const (
	// AllStorage allows memory, warm (original file), and cold storage.
	// Warm storage requires the original file to be unchanged.
	AllStorage LoadingStyle = iota

	// ColdAndMemory prevents warm storage, only memory and cold.
	ColdAndMemory

	// MemoryOnly keeps everything in memory, no external storage.
	MemoryOnly
)

type MaintenanceStats

type MaintenanceStats struct {
	NodesChilled       int   // number of nodes moved to cold storage
	BytesChilled       int64 // bytes moved to cold storage
	RotationsPerformed int   // number of tree rotations performed
}

MaintenanceStats contains statistics from a maintenance run.

type MemoryStats

type MemoryStats struct {
	MemoryBytes      int64 // bytes of in-memory leaf data
	SoftLimit        int64 // configured soft limit (0 = disabled)
	HardLimit        int64 // configured hard limit (0 = disabled)
	InMemoryLeaves   int   // count of leaves with data in memory
	ColdStoredLeaves int   // count of leaves with data in cold storage
	WarmStoredLeaves int   // count of leaves with data in warm storage
	UnderPressure    bool  // true if hard limit exceeded and can't reduce
}

MemoryStats contains current memory usage statistics.

type MoveResult

type MoveResult struct {
	ChangeResult
	DisplacedDecorations []RelativeDecoration // Decorations that were in the destination range (original positions)
}

MoveResult contains the result of a Move operation.

type Node

type Node struct {
	// contains filtered or unexported fields
}

Node is a versioned container in the rope structure. Each node maintains a history of its state indexed by (ForkID, RevisionID).

func (*Node) ID

func (n *Node) ID() NodeID

ID returns the node's unique identifier.

type NodeID

type NodeID uint64

NodeID uniquely identifies a node within a Garland.

type NodeSnapshot

type NodeSnapshot struct {
	// contains filtered or unexported fields
}

NodeSnapshot represents the immutable state of a node at a specific version.

func (*NodeSnapshot) ByteCount

func (s *NodeSnapshot) ByteCount() int64

ByteCount returns the total bytes in this subtree.

func (*NodeSnapshot) Data

func (s *NodeSnapshot) Data() []byte

Data returns the leaf node's data. Returns nil for internal nodes.

func (*NodeSnapshot) Decorations

func (s *NodeSnapshot) Decorations() []Decoration

Decorations returns the leaf node's decorations. Returns nil for internal nodes.

func (*NodeSnapshot) IsLeaf

func (s *NodeSnapshot) IsLeaf() bool

IsLeaf returns true if this snapshot represents a leaf node.

func (*NodeSnapshot) LeftID

func (s *NodeSnapshot) LeftID() NodeID

LeftID returns the left child's ID. Only valid for internal nodes.

func (*NodeSnapshot) LineCount

func (s *NodeSnapshot) LineCount() int64

LineCount returns the total newlines in this subtree.

func (*NodeSnapshot) RightID

func (s *NodeSnapshot) RightID() NodeID

RightID returns the right child's ID. Only valid for internal nodes.

func (*NodeSnapshot) RuneCount

func (s *NodeSnapshot) RuneCount() int64

RuneCount returns the total runes in this subtree.

type OpenMode

type OpenMode int

OpenMode specifies how a file should be opened.

const (
	// OpenModeRead opens the file for reading only.
	OpenModeRead OpenMode = iota

	// OpenModeWrite opens the file for writing only.
	OpenModeWrite

	// OpenModeReadWrite opens the file for reading and writing.
	OpenModeReadWrite
)

type OptimizedRegionHandle

type OptimizedRegionHandle struct {
	// contains filtered or unexported fields
}

OptimizedRegionHandle tracks an active optimized region attached to a cursor.

func (*OptimizedRegionHandle) ByteCount

func (h *OptimizedRegionHandle) ByteCount() int64

ByteCount returns the current byte count of the region content.

func (*OptimizedRegionHandle) ContentBounds

func (h *OptimizedRegionHandle) ContentBounds() (start, end int64)

ContentBounds returns the current content bounds (document coordinates).

func (*OptimizedRegionHandle) GraceWindow

func (h *OptimizedRegionHandle) GraceWindow() (start, end int64)

GraceWindow returns the grace window bounds (document coordinates).

func (*OptimizedRegionHandle) Serial

func (h *OptimizedRegionHandle) Serial() uint64

Serial returns the unique serial number of this region for debugging.

type ReadAheadConfig

type ReadAheadConfig struct {
	Lines int64 // additional lines to read ahead (0 = ignore)
	Bytes int64 // additional bytes to read ahead (0 = ignore)
	Runes int64 // additional runes to read ahead (0 = ignore)
	All   bool  // read entire file ASAP
}

ReadAheadConfig specifies lazy read-ahead behavior.

type ReadyThreshold

type ReadyThreshold struct {
	Lines int64 // number of complete lines (0 = ignore)
	Bytes int64 // number of bytes (0 = ignore)
	Runes int64 // number of runes (0 = ignore)
	All   bool  // only ready when entire file processed
}

ReadyThreshold specifies when a garland is considered "ready".

type RegexOptions

type RegexOptions struct {
	CaseInsensitive bool // If true, regex is case-insensitive
	Backward        bool // If true, search backward from cursor
}

RegexOptions configures regex search behavior.

type RelativeDecoration

type RelativeDecoration struct {
	Key      string
	Position int64
}

RelativeDecoration specifies a decoration position relative to an insert point. Position semantics:

  • < 0 : attach after the newline preceding the insert point
  • 0 to Len : attach to the byte/rune at that offset in inserted content
  • Len + 1 : attach before the newline following the insert point
  • > Len + 1: attach after the newline following the insert point

type RevisionID

type RevisionID uint64

RevisionID identifies a revision within a fork.

type RevisionInfo

type RevisionInfo struct {
	Revision         RevisionID
	Name             string // from TransactionStart
	HasChanges       bool   // true if actual mutations occurred
	RootID           NodeID // root node ID at this revision (for UndoSeek)
	StreamKnownBytes int64  // bytes of streaming content known when revision was created (-1 if complete)
}

RevisionInfo contains metadata about a revision for undo history display.

type SearchOptions

type SearchOptions struct {
	CaseSensitive bool // If false, search is case-insensitive
	WholeWord     bool // If true, only match whole words
	Backward      bool // If true, search backward from cursor
}

SearchOptions configures string search behavior.

type SearchResult

type SearchResult struct {
	ByteStart int64  // Start position in bytes
	ByteEnd   int64  // End position in bytes (exclusive)
	Match     string // The matched text
}

SearchResult contains information about a search match.

type SnapshotStats

type SnapshotStats struct {
	TotalSnapshots int                  // Total snapshots across all nodes
	ByFork         map[ForkID]int       // Snapshots per fork
	ByForkRevision map[ForkRevision]int // Snapshots per fork/revision
}

SnapshotStats contains statistics about node snapshots.

type SourceChangeHandler

type SourceChangeHandler func(g *Garland, status SourceChangeStatus, info SourceChangeInfo)

SourceChangeHandler is called when a source file change is detected.

type SourceChangeInfo

type SourceChangeInfo struct {
	Type          SourceChangeType
	PreviousSize  int64
	CurrentSize   int64
	AppendedBytes int64 // Only valid if Type == SourceAppended
}

SourceChangeInfo contains details about a detected source file change.

type SourceChangeStatus

type SourceChangeStatus int

SourceChangeStatus indicates the current status of source file tracking.

const (
	// SourceStatusNormal indicates no changes have been detected.
	SourceStatusNormal SourceChangeStatus = iota

	// SourceStatusAppendAvailable indicates an append was detected and verified.
	SourceStatusAppendAvailable

	// SourceStatusSuspectChange indicates metadata changed but not yet verified.
	SourceStatusSuspectChange

	// SourceStatusModified indicates a checksum mismatch was confirmed.
	SourceStatusModified
)

type SourceChangeType

type SourceChangeType int

SourceChangeType indicates the type of change detected in the source file.

const (
	// SourceUnchanged indicates no change was detected.
	SourceUnchanged SourceChangeType = iota

	// SourceAppended indicates the file grew but existing content is intact.
	SourceAppended

	// SourceModified indicates existing content was altered.
	SourceModified

	// SourceTruncated indicates the file was shortened.
	SourceTruncated

	// SourceReplaced indicates the file was replaced (different inode).
	SourceReplaced

	// SourceDeleted indicates the file no longer exists.
	SourceDeleted
)

func (SourceChangeType) String

func (t SourceChangeType) String() string

String returns a human-readable description of the change type.

type StorageState

type StorageState int

StorageState indicates where a node's data is currently stored.

const (
	// StorageMemory indicates data is in memory.
	StorageMemory StorageState = iota

	// StorageWarm indicates data is in the original file (warm storage).
	StorageWarm

	// StorageCold indicates data is in cold storage.
	StorageCold

	// StoragePlaceholder indicates data was lost due to storage failure.
	StoragePlaceholder
)

type TransactionState

type TransactionState struct {
	// contains filtered or unexported fields
}

TransactionState holds the state of an active transaction.

type TreeNodeInfo

type TreeNodeInfo struct {
	NodeID       NodeID
	IsLeaf       bool
	ByteCount    int64
	RuneCount    int64
	LineCount    int64
	Storage      StorageState
	DataPreview  string // First 32 chars of leaf data (for leaves only)
	LeftChildID  NodeID // For internal nodes
	RightChildID NodeID // For internal nodes
	Children     []*TreeNodeInfo
}

TreeNodeInfo contains information about a single node in the tree.

type WarmTrustLevel

type WarmTrustLevel int

WarmTrustLevel indicates how much we trust warm storage for a given block.

const (
	// WarmTrustFull indicates no changes ever detected, fully trusted.
	WarmTrustFull WarmTrustLevel = iota

	// WarmTrustVerified indicates changes detected but block verified since.
	WarmTrustVerified

	// WarmTrustStale indicates changes detected and block not verified.
	WarmTrustStale

	// WarmTrustSuspended indicates user notified but hasn't responded.
	WarmTrustSuspended
)

Directories

Path Synopsis
cmd
garland-bench command
garland-bench is a benchmark and stress test for the Garland library.
garland-bench is a benchmark and stress test for the Garland library.
garland-repl command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL