Latest commit

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

GoRipGrep

A text search library written in pure Go, inspired by ripgrep. This project is a learning exercise in implementing text search algorithms and optimizations in Go.

⚠️ Performance Notice: This implementation is currently ~46x slower than ripgrep on typical workloads. It's a work-in-progress educational project, not a production replacement for ripgrep.

Current Performance Status

Honest Benchmarks (Pattern: \w+Sushi, local directory)

ripgrep: 20ms (0.020s)
goripgrep: 922ms (0.922s) Performance gap: 46x slower

What this means:

  • ✅ Functionally correct (finds same matches as ripgrep)
  • ❌ Not competitive with ripgrep for production use
  • ✅ Educational value for learning Go optimization techniques
  • ❌ Performance regressions occurred during development

Optimization Attempts

We attempted several optimizations but failed to achieve significant improvements:

  • Memory-mapped file reading: Implemented but limited impact
  • Basic literal string optimization: Added to engine
  • Configuration-based optimization: Added option flags
  • Result: Still significantly slower than ripgrep, no meaningful performance gains achieved

Features

✅ Working Features

  • Literal string search with basic optimizations
  • Regex pattern matching using Go's standard regexp
  • Directory traversal with file filtering
  • Gitignore support for basic patterns
  • Binary file detection and skipping
  • Context lines around matches
  • Concurrent processing with worker pools
  • Basic Unicode support (via Go's standard library)
  • Functional API with options

❌ Claims We're NOT Making

  • "2-16x faster than Go's standard regex" (No evidence for this)
  • "Sub-millisecond search times" (False for realistic workloads)
  • "DFA caching" (No actual DFA implementation)
  • "Pure Go word-level operations" (Just using standard Go)
  • "CPU feature detection" (Not implemented)

🚧 Areas for Improvement

  • Performance: Currently 46x slower than ripgrep (significant gap)
  • Memory efficiency: High allocation count vs ripgrep
  • Advanced regex features: Basic implementation only
  • SIMD optimizations: Not implemented
  • True DFA compilation: Not implemented
  • File I/O optimization: Standard library approaches vs custom optimizations

Requirements

  • Go 1.21+
  • No external dependencies (pure Go implementation)

Installation

go get github.com/localrivet/goripgrep

Quick Start

Simple Functional API

package main
import (
"fmt""log""github.com/localrivet/goripgrep"
)
funcmain() {
// Basic search (non-recursive by default)results, err:=goripgrep.Find("hello", ".")
iferr!=nil {
log.Fatal(err)
}
// Recursive search with optionsresults, err=goripgrep.Find("hello", ".", goripgrep.WithRecursive(true),
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2))
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Found %d matches in %d files\n", results.Count(), len(results.Files()))
}

Advanced Search with Options

// Search with multiple optionsresults, err:=goripgrep.Find("TODO", "/path/to/project",
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2),
goripgrep.WithFilePattern("*.go"),
goripgrep.WithMaxResults(100),
goripgrep.WithGitignore(true),
goripgrep.WithTimeout(30*time.Second),
)
iferr!=nil {
log.Fatal(err)
}
// Process results with contextfor_, match:=rangeresults.Matches {
fmt.Printf("%s:%d:%d: %s\n", match.File, match.Line, match.Column, match.Content)
// Print context lines if availablefor_, contextLine:=rangematch.Context {
fmt.Printf(" | %s\n", contextLine)
}
}

Architecture

GoRipGrep uses a straightforward architecture with these components:

Core Components

  1. Engine (engine.go)

    • Basic pattern search using Go's regexp package
    • Simple literal string optimization
    • File reading with basic buffering
  2. Search (search.go)

    • Directory traversal with filepath.WalkDir
    • Binary file detection and skipping
    • Worker pool for concurrent processing
    • Basic gitignore support
  3. API (api.go)

    • Functional options pattern
    • Simple configuration management
    • Result aggregation
  4. Types (types.go)

    • Data structures for results and configuration
    • Basic statistics tracking

Performance Analysis

Current Benchmark Results

# Run real-world performance comparison:time rg '\w+Sushi'.# ~20mstime ./goripgrep '\w+Sushi'.# ~922ms (46x slower)# Benchmark tests (if available):
go test -bench=BenchmarkSimpleComparison -benchmem

Key metrics from testing:

  • goripgrep: 922ms (0.922 seconds)
  • ripgrep: 20ms (0.020 seconds)
  • Performance gap: 46x slower

Why Is It So Much Slower?

Honest assessment of performance gaps:

  1. No SIMD optimizations - ripgrep uses assembly-optimized string search
  2. Basic regex engine - using Go's standard regexp vs ripgrep's optimized DFA
  3. Excessive allocations - thousands of allocations vs ripgrep's minimal allocation
  4. No advanced byte scanning - no memchr-style optimizations
  5. Inefficient file I/O - standard library approaches vs ripgrep's optimized I/O
  6. Suboptimal directory walking - basic implementation vs ripgrep's optimized walker
  7. No meaningful optimizations - attempted optimizations provided negligible benefits

Optimization Opportunities

Areas where significant improvements could be made:

  1. Reduce allocations - currently creating 50K+ objects per search
  2. Implement boyer-moore or similar for literal string search
  3. Add binary search optimizations for sorted pattern lists
  4. Optimize file reading with better buffering strategies
  5. Implement actual DFA compilation instead of using standard regexp

Testing

# Run all tests
go test ./...
# Run tests with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Run benchmarks (see honest performance comparison)
go test -bench=. -benchmem ./...
# Compare directly with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Configuration

Functional Options API

// Available options for Find functionresults, err:=goripgrep.Find("pattern", "/path",
goripgrep.WithContext(ctx), // Set context for cancellationgoripgrep.WithWorkers(8), // Number of concurrent workersgoripgrep.WithBufferSize(64*1024), // I/O buffer sizegoripgrep.WithMaxResults(1000), // Maximum results to returngoripgrep.WithOptimization(true), // Enable basic optimizationsgoripgrep.WithGitignore(true), // Enable gitignore filteringgoripgrep.WithIgnoreCase(), // Case-insensitive searchgoripgrep.WithRecursive(true), // Search directories recursivelygoripgrep.WithFilePattern("*.go"), // File pattern filtergoripgrep.WithContextLines(3), // Number of context linesgoripgrep.WithTimeout(30*time.Second), // Search timeout
)

Result Types

typeSearchResultsstruct {
Matches []Match// Found matchesStatsSearchStats// Basic performance statisticsQuerystring// Search pattern
}
typeMatchstruct {
Filestring// Path to the file containing the matchLineint// Line number (1-indexed)Columnint// Column number (1-indexed)Contentstring// Content of the matching lineContext []string// Context lines (if requested)
}
typeSearchStatsstruct {
FilesScannedint64// Number of files scannedFilesSkippedint64// Number of files skippedBytesScannedint64// Total bytes scannedMatchesFoundint64// Total matches foundDuration time.Duration// Search duration
}

Use Cases

Realistic use cases where this might be appropriate:

  • Learning Go text processing techniques
  • Educational projects for understanding search algorithms
  • Small codebases where 869ms search time is acceptable
  • Integration scenarios where pure Go is required
  • Prototyping before switching to production tools

Use cases where you should use ripgrep instead:

  • Production applications requiring fast search
  • Large codebases (>1000 files)
  • Interactive tools where speed matters
  • Any performance-critical scenario

Supported Features

✅ Currently Working

  • Basic literal string search
  • Regular expression patterns (via Go regexp)
  • Case-insensitive search
  • File pattern filtering (basic globs)
  • Binary file detection
  • Hidden file inclusion/exclusion
  • Basic gitignore support
  • Context lines
  • Concurrent processing
  • Unicode support (via Go standard library)
  • Timeout support
  • Result limiting

❌ Not Implemented (Despite Earlier Claims)

  • DFA caching (just uses standard Go regexp)
  • Advanced byte-level optimizations
  • SIMD instructions
  • CPU feature detection
  • Advanced Unicode character classes
  • Streaming decompression
  • Word-level scanning optimizations
  • Performance competitive with ripgrep

🚧 Could Be Improved

  • Memory efficiency (too many allocations)
  • Search speed (fundamental algorithm improvements needed)
  • Regex performance (would need custom engine)
  • File walking optimization
  • Better binary detection

Contributing

This is a learning project. Contributions are welcome, especially:

  • Performance improvements with measurable benchmarks
  • Algorithm optimizations with before/after comparisons
  • Memory allocation reductions
  • Bug fixes with test cases
  • Documentation improvements

Please include benchmark results with any performance-related PRs.

Development Setup

# Clone the repository
git clone https://github.com/localrivet/goripgrep.git
cd goripgrep
# Install dependencies
go mod download
# Run tests and benchmarks
go test ./...
go test -bench=. -benchmem ./...
# Compare with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Honest Performance Comparison

If you need fast text search, use ripgrep. This project is:

Good for: Learning, education, Go integration, small projects
Bad for: Production use, large codebases, performance-critical applications

Performance Reality:

  • ripgrep: 21ms (optimized Rust + assembly)
  • goripgrep: 869ms (educational Go implementation)
  • grep: 2.1s (basic system utility)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Inspired by ripgrep by Andrew Gallant
  • This is NOT a replacement for ripgrep, just a learning exercise
  • Thanks to the Go community for excellent tooling and documentation

GoRipGrep: An educational text search implementation in Go. Use ripgrep for production unless you need a pure go alternaive.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

GoRipGrep

A text search library written in pure Go, inspired by ripgrep. This project is a learning exercise in implementing text search algorithms and optimizations in Go.

⚠️ Performance Notice: This implementation is currently ~46x slower than ripgrep on typical workloads. It's a work-in-progress educational project, not a production replacement for ripgrep.

Current Performance Status

Honest Benchmarks (Pattern: \w+Sushi, local directory)

ripgrep: 20ms (0.020s)
goripgrep: 922ms (0.922s) Performance gap: 46x slower

What this means:

  • ✅ Functionally correct (finds same matches as ripgrep)
  • ❌ Not competitive with ripgrep for production use
  • ✅ Educational value for learning Go optimization techniques
  • ❌ Performance regressions occurred during development

Optimization Attempts

We attempted several optimizations but failed to achieve significant improvements:

  • Memory-mapped file reading: Implemented but limited impact
  • Basic literal string optimization: Added to engine
  • Configuration-based optimization: Added option flags
  • Result: Still significantly slower than ripgrep, no meaningful performance gains achieved

Features

✅ Working Features

  • Literal string search with basic optimizations
  • Regex pattern matching using Go's standard regexp
  • Directory traversal with file filtering
  • Gitignore support for basic patterns
  • Binary file detection and skipping
  • Context lines around matches
  • Concurrent processing with worker pools
  • Basic Unicode support (via Go's standard library)
  • Functional API with options

❌ Claims We're NOT Making

  • "2-16x faster than Go's standard regex" (No evidence for this)
  • "Sub-millisecond search times" (False for realistic workloads)
  • "DFA caching" (No actual DFA implementation)
  • "Pure Go word-level operations" (Just using standard Go)
  • "CPU feature detection" (Not implemented)

🚧 Areas for Improvement

  • Performance: Currently 46x slower than ripgrep (significant gap)
  • Memory efficiency: High allocation count vs ripgrep
  • Advanced regex features: Basic implementation only
  • SIMD optimizations: Not implemented
  • True DFA compilation: Not implemented
  • File I/O optimization: Standard library approaches vs custom optimizations

Requirements

  • Go 1.21+
  • No external dependencies (pure Go implementation)

Installation

go get github.com/localrivet/goripgrep

Quick Start

Simple Functional API

package main
import (
"fmt""log""github.com/localrivet/goripgrep"
)
funcmain() {
// Basic search (non-recursive by default)results, err:=goripgrep.Find("hello", ".")
iferr!=nil {
log.Fatal(err)
}
// Recursive search with optionsresults, err=goripgrep.Find("hello", ".", goripgrep.WithRecursive(true),
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2))
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Found %d matches in %d files\n", results.Count(), len(results.Files()))
}

Advanced Search with Options

// Search with multiple optionsresults, err:=goripgrep.Find("TODO", "/path/to/project",
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2),
goripgrep.WithFilePattern("*.go"),
goripgrep.WithMaxResults(100),
goripgrep.WithGitignore(true),
goripgrep.WithTimeout(30*time.Second),
)
iferr!=nil {
log.Fatal(err)
}
// Process results with contextfor_, match:=rangeresults.Matches {
fmt.Printf("%s:%d:%d: %s\n", match.File, match.Line, match.Column, match.Content)
// Print context lines if availablefor_, contextLine:=rangematch.Context {
fmt.Printf(" | %s\n", contextLine)
}
}

Architecture

GoRipGrep uses a straightforward architecture with these components:

Core Components

  1. Engine (engine.go)

    • Basic pattern search using Go's regexp package
    • Simple literal string optimization
    • File reading with basic buffering
  2. Search (search.go)

    • Directory traversal with filepath.WalkDir
    • Binary file detection and skipping
    • Worker pool for concurrent processing
    • Basic gitignore support
  3. API (api.go)

    • Functional options pattern
    • Simple configuration management
    • Result aggregation
  4. Types (types.go)

    • Data structures for results and configuration
    • Basic statistics tracking

Performance Analysis

Current Benchmark Results

# Run real-world performance comparison:time rg '\w+Sushi'.# ~20mstime ./goripgrep '\w+Sushi'.# ~922ms (46x slower)# Benchmark tests (if available):
go test -bench=BenchmarkSimpleComparison -benchmem

Key metrics from testing:

  • goripgrep: 922ms (0.922 seconds)
  • ripgrep: 20ms (0.020 seconds)
  • Performance gap: 46x slower

Why Is It So Much Slower?

Honest assessment of performance gaps:

  1. No SIMD optimizations - ripgrep uses assembly-optimized string search
  2. Basic regex engine - using Go's standard regexp vs ripgrep's optimized DFA
  3. Excessive allocations - thousands of allocations vs ripgrep's minimal allocation
  4. No advanced byte scanning - no memchr-style optimizations
  5. Inefficient file I/O - standard library approaches vs ripgrep's optimized I/O
  6. Suboptimal directory walking - basic implementation vs ripgrep's optimized walker
  7. No meaningful optimizations - attempted optimizations provided negligible benefits

Optimization Opportunities

Areas where significant improvements could be made:

  1. Reduce allocations - currently creating 50K+ objects per search
  2. Implement boyer-moore or similar for literal string search
  3. Add binary search optimizations for sorted pattern lists
  4. Optimize file reading with better buffering strategies
  5. Implement actual DFA compilation instead of using standard regexp

Testing

# Run all tests
go test ./...
# Run tests with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Run benchmarks (see honest performance comparison)
go test -bench=. -benchmem ./...
# Compare directly with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Configuration

Functional Options API

// Available options for Find functionresults, err:=goripgrep.Find("pattern", "/path",
goripgrep.WithContext(ctx), // Set context for cancellationgoripgrep.WithWorkers(8), // Number of concurrent workersgoripgrep.WithBufferSize(64*1024), // I/O buffer sizegoripgrep.WithMaxResults(1000), // Maximum results to returngoripgrep.WithOptimization(true), // Enable basic optimizationsgoripgrep.WithGitignore(true), // Enable gitignore filteringgoripgrep.WithIgnoreCase(), // Case-insensitive searchgoripgrep.WithRecursive(true), // Search directories recursivelygoripgrep.WithFilePattern("*.go"), // File pattern filtergoripgrep.WithContextLines(3), // Number of context linesgoripgrep.WithTimeout(30*time.Second), // Search timeout
)

Result Types

typeSearchResultsstruct {
Matches []Match// Found matchesStatsSearchStats// Basic performance statisticsQuerystring// Search pattern
}
typeMatchstruct {
Filestring// Path to the file containing the matchLineint// Line number (1-indexed)Columnint// Column number (1-indexed)Contentstring// Content of the matching lineContext []string// Context lines (if requested)
}
typeSearchStatsstruct {
FilesScannedint64// Number of files scannedFilesSkippedint64// Number of files skippedBytesScannedint64// Total bytes scannedMatchesFoundint64// Total matches foundDuration time.Duration// Search duration
}

Use Cases

Realistic use cases where this might be appropriate:

  • Learning Go text processing techniques
  • Educational projects for understanding search algorithms
  • Small codebases where 869ms search time is acceptable
  • Integration scenarios where pure Go is required
  • Prototyping before switching to production tools

Use cases where you should use ripgrep instead:

  • Production applications requiring fast search
  • Large codebases (>1000 files)
  • Interactive tools where speed matters
  • Any performance-critical scenario

Supported Features

✅ Currently Working

  • Basic literal string search
  • Regular expression patterns (via Go regexp)
  • Case-insensitive search
  • File pattern filtering (basic globs)
  • Binary file detection
  • Hidden file inclusion/exclusion
  • Basic gitignore support
  • Context lines
  • Concurrent processing
  • Unicode support (via Go standard library)
  • Timeout support
  • Result limiting

❌ Not Implemented (Despite Earlier Claims)

  • DFA caching (just uses standard Go regexp)
  • Advanced byte-level optimizations
  • SIMD instructions
  • CPU feature detection
  • Advanced Unicode character classes
  • Streaming decompression
  • Word-level scanning optimizations
  • Performance competitive with ripgrep

🚧 Could Be Improved

  • Memory efficiency (too many allocations)
  • Search speed (fundamental algorithm improvements needed)
  • Regex performance (would need custom engine)
  • File walking optimization
  • Better binary detection

Contributing

This is a learning project. Contributions are welcome, especially:

  • Performance improvements with measurable benchmarks
  • Algorithm optimizations with before/after comparisons
  • Memory allocation reductions
  • Bug fixes with test cases
  • Documentation improvements

Please include benchmark results with any performance-related PRs.

Development Setup

# Clone the repository
git clone https://github.com/localrivet/goripgrep.git
cd goripgrep
# Install dependencies
go mod download
# Run tests and benchmarks
go test ./...
go test -bench=. -benchmem ./...
# Compare with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Honest Performance Comparison

If you need fast text search, use ripgrep. This project is:

Good for: Learning, education, Go integration, small projects
Bad for: Production use, large codebases, performance-critical applications

Performance Reality:

  • ripgrep: 21ms (optimized Rust + assembly)
  • goripgrep: 869ms (educational Go implementation)
  • grep: 2.1s (basic system utility)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Inspired by ripgrep by Andrew Gallant
  • This is NOT a replacement for ripgrep, just a learning exercise
  • Thanks to the Go community for excellent tooling and documentation

GoRipGrep: An educational text search implementation in Go. Use ripgrep for production unless you need a pure go alternaive.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

GoRipGrep

A text search library written in pure Go, inspired by ripgrep. This project is a learning exercise in implementing text search algorithms and optimizations in Go.

⚠️ Performance Notice: This implementation is currently ~46x slower than ripgrep on typical workloads. It's a work-in-progress educational project, not a production replacement for ripgrep.

Current Performance Status

Honest Benchmarks (Pattern: \w+Sushi, local directory)

ripgrep: 20ms (0.020s)
goripgrep: 922ms (0.922s) Performance gap: 46x slower

What this means:

  • ✅ Functionally correct (finds same matches as ripgrep)
  • ❌ Not competitive with ripgrep for production use
  • ✅ Educational value for learning Go optimization techniques
  • ❌ Performance regressions occurred during development

Optimization Attempts

We attempted several optimizations but failed to achieve significant improvements:

  • Memory-mapped file reading: Implemented but limited impact
  • Basic literal string optimization: Added to engine
  • Configuration-based optimization: Added option flags
  • Result: Still significantly slower than ripgrep, no meaningful performance gains achieved

Features

✅ Working Features

  • Literal string search with basic optimizations
  • Regex pattern matching using Go's standard regexp
  • Directory traversal with file filtering
  • Gitignore support for basic patterns
  • Binary file detection and skipping
  • Context lines around matches
  • Concurrent processing with worker pools
  • Basic Unicode support (via Go's standard library)
  • Functional API with options

❌ Claims We're NOT Making

  • "2-16x faster than Go's standard regex" (No evidence for this)
  • "Sub-millisecond search times" (False for realistic workloads)
  • "DFA caching" (No actual DFA implementation)
  • "Pure Go word-level operations" (Just using standard Go)
  • "CPU feature detection" (Not implemented)

🚧 Areas for Improvement

  • Performance: Currently 46x slower than ripgrep (significant gap)
  • Memory efficiency: High allocation count vs ripgrep
  • Advanced regex features: Basic implementation only
  • SIMD optimizations: Not implemented
  • True DFA compilation: Not implemented
  • File I/O optimization: Standard library approaches vs custom optimizations

Requirements

  • Go 1.21+
  • No external dependencies (pure Go implementation)

Installation

go get github.com/localrivet/goripgrep

Quick Start

Simple Functional API

package main
import (
"fmt""log""github.com/localrivet/goripgrep"
)
funcmain() {
// Basic search (non-recursive by default)results, err:=goripgrep.Find("hello", ".")
iferr!=nil {
log.Fatal(err)
}
// Recursive search with optionsresults, err=goripgrep.Find("hello", ".", goripgrep.WithRecursive(true),
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2))
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Found %d matches in %d files\n", results.Count(), len(results.Files()))
}

Advanced Search with Options

// Search with multiple optionsresults, err:=goripgrep.Find("TODO", "/path/to/project",
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2),
goripgrep.WithFilePattern("*.go"),
goripgrep.WithMaxResults(100),
goripgrep.WithGitignore(true),
goripgrep.WithTimeout(30*time.Second),
)
iferr!=nil {
log.Fatal(err)
}
// Process results with contextfor_, match:=rangeresults.Matches {
fmt.Printf("%s:%d:%d: %s\n", match.File, match.Line, match.Column, match.Content)
// Print context lines if availablefor_, contextLine:=rangematch.Context {
fmt.Printf(" | %s\n", contextLine)
}
}

Architecture

GoRipGrep uses a straightforward architecture with these components:

Core Components

  1. Engine (engine.go)

    • Basic pattern search using Go's regexp package
    • Simple literal string optimization
    • File reading with basic buffering
  2. Search (search.go)

    • Directory traversal with filepath.WalkDir
    • Binary file detection and skipping
    • Worker pool for concurrent processing
    • Basic gitignore support
  3. API (api.go)

    • Functional options pattern
    • Simple configuration management
    • Result aggregation
  4. Types (types.go)

    • Data structures for results and configuration
    • Basic statistics tracking

Performance Analysis

Current Benchmark Results

# Run real-world performance comparison:time rg '\w+Sushi'.# ~20mstime ./goripgrep '\w+Sushi'.# ~922ms (46x slower)# Benchmark tests (if available):
go test -bench=BenchmarkSimpleComparison -benchmem

Key metrics from testing:

  • goripgrep: 922ms (0.922 seconds)
  • ripgrep: 20ms (0.020 seconds)
  • Performance gap: 46x slower

Why Is It So Much Slower?

Honest assessment of performance gaps:

  1. No SIMD optimizations - ripgrep uses assembly-optimized string search
  2. Basic regex engine - using Go's standard regexp vs ripgrep's optimized DFA
  3. Excessive allocations - thousands of allocations vs ripgrep's minimal allocation
  4. No advanced byte scanning - no memchr-style optimizations
  5. Inefficient file I/O - standard library approaches vs ripgrep's optimized I/O
  6. Suboptimal directory walking - basic implementation vs ripgrep's optimized walker
  7. No meaningful optimizations - attempted optimizations provided negligible benefits

Optimization Opportunities

Areas where significant improvements could be made:

  1. Reduce allocations - currently creating 50K+ objects per search
  2. Implement boyer-moore or similar for literal string search
  3. Add binary search optimizations for sorted pattern lists
  4. Optimize file reading with better buffering strategies
  5. Implement actual DFA compilation instead of using standard regexp

Testing

# Run all tests
go test ./...
# Run tests with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Run benchmarks (see honest performance comparison)
go test -bench=. -benchmem ./...
# Compare directly with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Configuration

Functional Options API

// Available options for Find functionresults, err:=goripgrep.Find("pattern", "/path",
goripgrep.WithContext(ctx), // Set context for cancellationgoripgrep.WithWorkers(8), // Number of concurrent workersgoripgrep.WithBufferSize(64*1024), // I/O buffer sizegoripgrep.WithMaxResults(1000), // Maximum results to returngoripgrep.WithOptimization(true), // Enable basic optimizationsgoripgrep.WithGitignore(true), // Enable gitignore filteringgoripgrep.WithIgnoreCase(), // Case-insensitive searchgoripgrep.WithRecursive(true), // Search directories recursivelygoripgrep.WithFilePattern("*.go"), // File pattern filtergoripgrep.WithContextLines(3), // Number of context linesgoripgrep.WithTimeout(30*time.Second), // Search timeout
)

Result Types

typeSearchResultsstruct {
Matches []Match// Found matchesStatsSearchStats// Basic performance statisticsQuerystring// Search pattern
}
typeMatchstruct {
Filestring// Path to the file containing the matchLineint// Line number (1-indexed)Columnint// Column number (1-indexed)Contentstring// Content of the matching lineContext []string// Context lines (if requested)
}
typeSearchStatsstruct {
FilesScannedint64// Number of files scannedFilesSkippedint64// Number of files skippedBytesScannedint64// Total bytes scannedMatchesFoundint64// Total matches foundDuration time.Duration// Search duration
}

Use Cases

Realistic use cases where this might be appropriate:

  • Learning Go text processing techniques
  • Educational projects for understanding search algorithms
  • Small codebases where 869ms search time is acceptable
  • Integration scenarios where pure Go is required
  • Prototyping before switching to production tools

Use cases where you should use ripgrep instead:

  • Production applications requiring fast search
  • Large codebases (>1000 files)
  • Interactive tools where speed matters
  • Any performance-critical scenario

Supported Features

✅ Currently Working

  • Basic literal string search
  • Regular expression patterns (via Go regexp)
  • Case-insensitive search
  • File pattern filtering (basic globs)
  • Binary file detection
  • Hidden file inclusion/exclusion
  • Basic gitignore support
  • Context lines
  • Concurrent processing
  • Unicode support (via Go standard library)
  • Timeout support
  • Result limiting

❌ Not Implemented (Despite Earlier Claims)

  • DFA caching (just uses standard Go regexp)
  • Advanced byte-level optimizations
  • SIMD instructions
  • CPU feature detection
  • Advanced Unicode character classes
  • Streaming decompression
  • Word-level scanning optimizations
  • Performance competitive with ripgrep

🚧 Could Be Improved

  • Memory efficiency (too many allocations)
  • Search speed (fundamental algorithm improvements needed)
  • Regex performance (would need custom engine)
  • File walking optimization
  • Better binary detection

Contributing

This is a learning project. Contributions are welcome, especially:

  • Performance improvements with measurable benchmarks
  • Algorithm optimizations with before/after comparisons
  • Memory allocation reductions
  • Bug fixes with test cases
  • Documentation improvements

Please include benchmark results with any performance-related PRs.

Development Setup

# Clone the repository
git clone https://github.com/localrivet/goripgrep.git
cd goripgrep
# Install dependencies
go mod download
# Run tests and benchmarks
go test ./...
go test -bench=. -benchmem ./...
# Compare with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Honest Performance Comparison

If you need fast text search, use ripgrep. This project is:

Good for: Learning, education, Go integration, small projects
Bad for: Production use, large codebases, performance-critical applications

Performance Reality:

  • ripgrep: 21ms (optimized Rust + assembly)
  • goripgrep: 869ms (educational Go implementation)
  • grep: 2.1s (basic system utility)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Inspired by ripgrep by Andrew Gallant
  • This is NOT a replacement for ripgrep, just a learning exercise
  • Thanks to the Go community for excellent tooling and documentation

GoRipGrep: An educational text search implementation in Go. Use ripgrep for production unless you need a pure go alternaive.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

GoRipGrep

A text search library written in pure Go, inspired by ripgrep. This project is a learning exercise in implementing text search algorithms and optimizations in Go.

⚠️ Performance Notice: This implementation is currently ~46x slower than ripgrep on typical workloads. It's a work-in-progress educational project, not a production replacement for ripgrep.

Current Performance Status

Honest Benchmarks (Pattern: \w+Sushi, local directory)

ripgrep: 20ms (0.020s)
goripgrep: 922ms (0.922s) Performance gap: 46x slower

What this means:

  • ✅ Functionally correct (finds same matches as ripgrep)
  • ❌ Not competitive with ripgrep for production use
  • ✅ Educational value for learning Go optimization techniques
  • ❌ Performance regressions occurred during development

Optimization Attempts

We attempted several optimizations but failed to achieve significant improvements:

  • Memory-mapped file reading: Implemented but limited impact
  • Basic literal string optimization: Added to engine
  • Configuration-based optimization: Added option flags
  • Result: Still significantly slower than ripgrep, no meaningful performance gains achieved

Features

✅ Working Features

  • Literal string search with basic optimizations
  • Regex pattern matching using Go's standard regexp
  • Directory traversal with file filtering
  • Gitignore support for basic patterns
  • Binary file detection and skipping
  • Context lines around matches
  • Concurrent processing with worker pools
  • Basic Unicode support (via Go's standard library)
  • Functional API with options

❌ Claims We're NOT Making

  • "2-16x faster than Go's standard regex" (No evidence for this)
  • "Sub-millisecond search times" (False for realistic workloads)
  • "DFA caching" (No actual DFA implementation)
  • "Pure Go word-level operations" (Just using standard Go)
  • "CPU feature detection" (Not implemented)

🚧 Areas for Improvement

  • Performance: Currently 46x slower than ripgrep (significant gap)
  • Memory efficiency: High allocation count vs ripgrep
  • Advanced regex features: Basic implementation only
  • SIMD optimizations: Not implemented
  • True DFA compilation: Not implemented
  • File I/O optimization: Standard library approaches vs custom optimizations

Requirements

  • Go 1.21+
  • No external dependencies (pure Go implementation)

Installation

go get github.com/localrivet/goripgrep

Quick Start

Simple Functional API

package main
import (
"fmt""log""github.com/localrivet/goripgrep"
)
funcmain() {
// Basic search (non-recursive by default)results, err:=goripgrep.Find("hello", ".")
iferr!=nil {
log.Fatal(err)
}
// Recursive search with optionsresults, err=goripgrep.Find("hello", ".", goripgrep.WithRecursive(true),
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2))
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Found %d matches in %d files\n", results.Count(), len(results.Files()))
}

Advanced Search with Options

// Search with multiple optionsresults, err:=goripgrep.Find("TODO", "/path/to/project",
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2),
goripgrep.WithFilePattern("*.go"),
goripgrep.WithMaxResults(100),
goripgrep.WithGitignore(true),
goripgrep.WithTimeout(30*time.Second),
)
iferr!=nil {
log.Fatal(err)
}
// Process results with contextfor_, match:=rangeresults.Matches {
fmt.Printf("%s:%d:%d: %s\n", match.File, match.Line, match.Column, match.Content)
// Print context lines if availablefor_, contextLine:=rangematch.Context {
fmt.Printf(" | %s\n", contextLine)
}
}

Architecture

GoRipGrep uses a straightforward architecture with these components:

Core Components

  1. Engine (engine.go)

    • Basic pattern search using Go's regexp package
    • Simple literal string optimization
    • File reading with basic buffering
  2. Search (search.go)

    • Directory traversal with filepath.WalkDir
    • Binary file detection and skipping
    • Worker pool for concurrent processing
    • Basic gitignore support
  3. API (api.go)

    • Functional options pattern
    • Simple configuration management
    • Result aggregation
  4. Types (types.go)

    • Data structures for results and configuration
    • Basic statistics tracking

Performance Analysis

Current Benchmark Results

# Run real-world performance comparison:time rg '\w+Sushi'.# ~20mstime ./goripgrep '\w+Sushi'.# ~922ms (46x slower)# Benchmark tests (if available):
go test -bench=BenchmarkSimpleComparison -benchmem

Key metrics from testing:

  • goripgrep: 922ms (0.922 seconds)
  • ripgrep: 20ms (0.020 seconds)
  • Performance gap: 46x slower

Why Is It So Much Slower?

Honest assessment of performance gaps:

  1. No SIMD optimizations - ripgrep uses assembly-optimized string search
  2. Basic regex engine - using Go's standard regexp vs ripgrep's optimized DFA
  3. Excessive allocations - thousands of allocations vs ripgrep's minimal allocation
  4. No advanced byte scanning - no memchr-style optimizations
  5. Inefficient file I/O - standard library approaches vs ripgrep's optimized I/O
  6. Suboptimal directory walking - basic implementation vs ripgrep's optimized walker
  7. No meaningful optimizations - attempted optimizations provided negligible benefits

Optimization Opportunities

Areas where significant improvements could be made:

  1. Reduce allocations - currently creating 50K+ objects per search
  2. Implement boyer-moore or similar for literal string search
  3. Add binary search optimizations for sorted pattern lists
  4. Optimize file reading with better buffering strategies
  5. Implement actual DFA compilation instead of using standard regexp

Testing

# Run all tests
go test ./...
# Run tests with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Run benchmarks (see honest performance comparison)
go test -bench=. -benchmem ./...
# Compare directly with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Configuration

Functional Options API

// Available options for Find functionresults, err:=goripgrep.Find("pattern", "/path",
goripgrep.WithContext(ctx), // Set context for cancellationgoripgrep.WithWorkers(8), // Number of concurrent workersgoripgrep.WithBufferSize(64*1024), // I/O buffer sizegoripgrep.WithMaxResults(1000), // Maximum results to returngoripgrep.WithOptimization(true), // Enable basic optimizationsgoripgrep.WithGitignore(true), // Enable gitignore filteringgoripgrep.WithIgnoreCase(), // Case-insensitive searchgoripgrep.WithRecursive(true), // Search directories recursivelygoripgrep.WithFilePattern("*.go"), // File pattern filtergoripgrep.WithContextLines(3), // Number of context linesgoripgrep.WithTimeout(30*time.Second), // Search timeout
)

Result Types

typeSearchResultsstruct {
Matches []Match// Found matchesStatsSearchStats// Basic performance statisticsQuerystring// Search pattern
}
typeMatchstruct {
Filestring// Path to the file containing the matchLineint// Line number (1-indexed)Columnint// Column number (1-indexed)Contentstring// Content of the matching lineContext []string// Context lines (if requested)
}
typeSearchStatsstruct {
FilesScannedint64// Number of files scannedFilesSkippedint64// Number of files skippedBytesScannedint64// Total bytes scannedMatchesFoundint64// Total matches foundDuration time.Duration// Search duration
}

Use Cases

Realistic use cases where this might be appropriate:

  • Learning Go text processing techniques
  • Educational projects for understanding search algorithms
  • Small codebases where 869ms search time is acceptable
  • Integration scenarios where pure Go is required
  • Prototyping before switching to production tools

Use cases where you should use ripgrep instead:

  • Production applications requiring fast search
  • Large codebases (>1000 files)
  • Interactive tools where speed matters
  • Any performance-critical scenario

Supported Features

✅ Currently Working

  • Basic literal string search
  • Regular expression patterns (via Go regexp)
  • Case-insensitive search
  • File pattern filtering (basic globs)
  • Binary file detection
  • Hidden file inclusion/exclusion
  • Basic gitignore support
  • Context lines
  • Concurrent processing
  • Unicode support (via Go standard library)
  • Timeout support
  • Result limiting

❌ Not Implemented (Despite Earlier Claims)

  • DFA caching (just uses standard Go regexp)
  • Advanced byte-level optimizations
  • SIMD instructions
  • CPU feature detection
  • Advanced Unicode character classes
  • Streaming decompression
  • Word-level scanning optimizations
  • Performance competitive with ripgrep

🚧 Could Be Improved

  • Memory efficiency (too many allocations)
  • Search speed (fundamental algorithm improvements needed)
  • Regex performance (would need custom engine)
  • File walking optimization
  • Better binary detection

Contributing

This is a learning project. Contributions are welcome, especially:

  • Performance improvements with measurable benchmarks
  • Algorithm optimizations with before/after comparisons
  • Memory allocation reductions
  • Bug fixes with test cases
  • Documentation improvements

Please include benchmark results with any performance-related PRs.

Development Setup

# Clone the repository
git clone https://github.com/localrivet/goripgrep.git
cd goripgrep
# Install dependencies
go mod download
# Run tests and benchmarks
go test ./...
go test -bench=. -benchmem ./...
# Compare with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Honest Performance Comparison

If you need fast text search, use ripgrep. This project is:

Good for: Learning, education, Go integration, small projects
Bad for: Production use, large codebases, performance-critical applications

Performance Reality:

  • ripgrep: 21ms (optimized Rust + assembly)
  • goripgrep: 869ms (educational Go implementation)
  • grep: 2.1s (basic system utility)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Inspired by ripgrep by Andrew Gallant
  • This is NOT a replacement for ripgrep, just a learning exercise
  • Thanks to the Go community for excellent tooling and documentation

GoRipGrep: An educational text search implementation in Go. Use ripgrep for production unless you need a pure go alternaive.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

GoRipGrep

A text search library written in pure Go, inspired by ripgrep. This project is a learning exercise in implementing text search algorithms and optimizations in Go.

⚠️ Performance Notice: This implementation is currently ~46x slower than ripgrep on typical workloads. It's a work-in-progress educational project, not a production replacement for ripgrep.

Current Performance Status

Honest Benchmarks (Pattern: \w+Sushi, local directory)

ripgrep: 20ms (0.020s)
goripgrep: 922ms (0.922s) Performance gap: 46x slower

What this means:

  • ✅ Functionally correct (finds same matches as ripgrep)
  • ❌ Not competitive with ripgrep for production use
  • ✅ Educational value for learning Go optimization techniques
  • ❌ Performance regressions occurred during development

Optimization Attempts

We attempted several optimizations but failed to achieve significant improvements:

  • Memory-mapped file reading: Implemented but limited impact
  • Basic literal string optimization: Added to engine
  • Configuration-based optimization: Added option flags
  • Result: Still significantly slower than ripgrep, no meaningful performance gains achieved

Features

✅ Working Features

  • Literal string search with basic optimizations
  • Regex pattern matching using Go's standard regexp
  • Directory traversal with file filtering
  • Gitignore support for basic patterns
  • Binary file detection and skipping
  • Context lines around matches
  • Concurrent processing with worker pools
  • Basic Unicode support (via Go's standard library)
  • Functional API with options

❌ Claims We're NOT Making

  • "2-16x faster than Go's standard regex" (No evidence for this)
  • "Sub-millisecond search times" (False for realistic workloads)
  • "DFA caching" (No actual DFA implementation)
  • "Pure Go word-level operations" (Just using standard Go)
  • "CPU feature detection" (Not implemented)

🚧 Areas for Improvement

  • Performance: Currently 46x slower than ripgrep (significant gap)
  • Memory efficiency: High allocation count vs ripgrep
  • Advanced regex features: Basic implementation only
  • SIMD optimizations: Not implemented
  • True DFA compilation: Not implemented
  • File I/O optimization: Standard library approaches vs custom optimizations

Requirements

  • Go 1.21+
  • No external dependencies (pure Go implementation)

Installation

go get github.com/localrivet/goripgrep

Quick Start

Simple Functional API

package main
import (
"fmt""log""github.com/localrivet/goripgrep"
)
funcmain() {
// Basic search (non-recursive by default)results, err:=goripgrep.Find("hello", ".")
iferr!=nil {
log.Fatal(err)
}
// Recursive search with optionsresults, err=goripgrep.Find("hello", ".", goripgrep.WithRecursive(true),
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2))
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Found %d matches in %d files\n", results.Count(), len(results.Files()))
}

Advanced Search with Options

// Search with multiple optionsresults, err:=goripgrep.Find("TODO", "/path/to/project",
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2),
goripgrep.WithFilePattern("*.go"),
goripgrep.WithMaxResults(100),
goripgrep.WithGitignore(true),
goripgrep.WithTimeout(30*time.Second),
)
iferr!=nil {
log.Fatal(err)
}
// Process results with contextfor_, match:=rangeresults.Matches {
fmt.Printf("%s:%d:%d: %s\n", match.File, match.Line, match.Column, match.Content)
// Print context lines if availablefor_, contextLine:=rangematch.Context {
fmt.Printf(" | %s\n", contextLine)
}
}

Architecture

GoRipGrep uses a straightforward architecture with these components:

Core Components

  1. Engine (engine.go)

    • Basic pattern search using Go's regexp package
    • Simple literal string optimization
    • File reading with basic buffering
  2. Search (search.go)

    • Directory traversal with filepath.WalkDir
    • Binary file detection and skipping
    • Worker pool for concurrent processing
    • Basic gitignore support
  3. API (api.go)

    • Functional options pattern
    • Simple configuration management
    • Result aggregation
  4. Types (types.go)

    • Data structures for results and configuration
    • Basic statistics tracking

Performance Analysis

Current Benchmark Results

# Run real-world performance comparison:time rg '\w+Sushi'.# ~20mstime ./goripgrep '\w+Sushi'.# ~922ms (46x slower)# Benchmark tests (if available):
go test -bench=BenchmarkSimpleComparison -benchmem

Key metrics from testing:

  • goripgrep: 922ms (0.922 seconds)
  • ripgrep: 20ms (0.020 seconds)
  • Performance gap: 46x slower

Why Is It So Much Slower?

Honest assessment of performance gaps:

  1. No SIMD optimizations - ripgrep uses assembly-optimized string search
  2. Basic regex engine - using Go's standard regexp vs ripgrep's optimized DFA
  3. Excessive allocations - thousands of allocations vs ripgrep's minimal allocation
  4. No advanced byte scanning - no memchr-style optimizations
  5. Inefficient file I/O - standard library approaches vs ripgrep's optimized I/O
  6. Suboptimal directory walking - basic implementation vs ripgrep's optimized walker
  7. No meaningful optimizations - attempted optimizations provided negligible benefits

Optimization Opportunities

Areas where significant improvements could be made:

  1. Reduce allocations - currently creating 50K+ objects per search
  2. Implement boyer-moore or similar for literal string search
  3. Add binary search optimizations for sorted pattern lists
  4. Optimize file reading with better buffering strategies
  5. Implement actual DFA compilation instead of using standard regexp

Testing

# Run all tests
go test ./...
# Run tests with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Run benchmarks (see honest performance comparison)
go test -bench=. -benchmem ./...
# Compare directly with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Configuration

Functional Options API

// Available options for Find functionresults, err:=goripgrep.Find("pattern", "/path",
goripgrep.WithContext(ctx), // Set context for cancellationgoripgrep.WithWorkers(8), // Number of concurrent workersgoripgrep.WithBufferSize(64*1024), // I/O buffer sizegoripgrep.WithMaxResults(1000), // Maximum results to returngoripgrep.WithOptimization(true), // Enable basic optimizationsgoripgrep.WithGitignore(true), // Enable gitignore filteringgoripgrep.WithIgnoreCase(), // Case-insensitive searchgoripgrep.WithRecursive(true), // Search directories recursivelygoripgrep.WithFilePattern("*.go"), // File pattern filtergoripgrep.WithContextLines(3), // Number of context linesgoripgrep.WithTimeout(30*time.Second), // Search timeout
)

Result Types

typeSearchResultsstruct {
Matches []Match// Found matchesStatsSearchStats// Basic performance statisticsQuerystring// Search pattern
}
typeMatchstruct {
Filestring// Path to the file containing the matchLineint// Line number (1-indexed)Columnint// Column number (1-indexed)Contentstring// Content of the matching lineContext []string// Context lines (if requested)
}
typeSearchStatsstruct {
FilesScannedint64// Number of files scannedFilesSkippedint64// Number of files skippedBytesScannedint64// Total bytes scannedMatchesFoundint64// Total matches foundDuration time.Duration// Search duration
}

Use Cases

Realistic use cases where this might be appropriate:

  • Learning Go text processing techniques
  • Educational projects for understanding search algorithms
  • Small codebases where 869ms search time is acceptable
  • Integration scenarios where pure Go is required
  • Prototyping before switching to production tools

Use cases where you should use ripgrep instead:

  • Production applications requiring fast search
  • Large codebases (>1000 files)
  • Interactive tools where speed matters
  • Any performance-critical scenario

Supported Features

✅ Currently Working

  • Basic literal string search
  • Regular expression patterns (via Go regexp)
  • Case-insensitive search
  • File pattern filtering (basic globs)
  • Binary file detection
  • Hidden file inclusion/exclusion
  • Basic gitignore support
  • Context lines
  • Concurrent processing
  • Unicode support (via Go standard library)
  • Timeout support
  • Result limiting

❌ Not Implemented (Despite Earlier Claims)

  • DFA caching (just uses standard Go regexp)
  • Advanced byte-level optimizations
  • SIMD instructions
  • CPU feature detection
  • Advanced Unicode character classes
  • Streaming decompression
  • Word-level scanning optimizations
  • Performance competitive with ripgrep

🚧 Could Be Improved

  • Memory efficiency (too many allocations)
  • Search speed (fundamental algorithm improvements needed)
  • Regex performance (would need custom engine)
  • File walking optimization
  • Better binary detection

Contributing

This is a learning project. Contributions are welcome, especially:

  • Performance improvements with measurable benchmarks
  • Algorithm optimizations with before/after comparisons
  • Memory allocation reductions
  • Bug fixes with test cases
  • Documentation improvements

Please include benchmark results with any performance-related PRs.

Development Setup

# Clone the repository
git clone https://github.com/localrivet/goripgrep.git
cd goripgrep
# Install dependencies
go mod download
# Run tests and benchmarks
go test ./...
go test -bench=. -benchmem ./...
# Compare with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Honest Performance Comparison

If you need fast text search, use ripgrep. This project is:

Good for: Learning, education, Go integration, small projects
Bad for: Production use, large codebases, performance-critical applications

Performance Reality:

  • ripgrep: 21ms (optimized Rust + assembly)
  • goripgrep: 869ms (educational Go implementation)
  • grep: 2.1s (basic system utility)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Inspired by ripgrep by Andrew Gallant
  • This is NOT a replacement for ripgrep, just a learning exercise
  • Thanks to the Go community for excellent tooling and documentation

GoRipGrep: An educational text search implementation in Go. Use ripgrep for production unless you need a pure go alternaive.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

GoRipGrep

A text search library written in pure Go, inspired by ripgrep. This project is a learning exercise in implementing text search algorithms and optimizations in Go.

⚠️ Performance Notice: This implementation is currently ~46x slower than ripgrep on typical workloads. It's a work-in-progress educational project, not a production replacement for ripgrep.

Current Performance Status

Honest Benchmarks (Pattern: \w+Sushi, local directory)

ripgrep: 20ms (0.020s)
goripgrep: 922ms (0.922s) Performance gap: 46x slower

What this means:

  • ✅ Functionally correct (finds same matches as ripgrep)
  • ❌ Not competitive with ripgrep for production use
  • ✅ Educational value for learning Go optimization techniques
  • ❌ Performance regressions occurred during development

Optimization Attempts

We attempted several optimizations but failed to achieve significant improvements:

  • Memory-mapped file reading: Implemented but limited impact
  • Basic literal string optimization: Added to engine
  • Configuration-based optimization: Added option flags
  • Result: Still significantly slower than ripgrep, no meaningful performance gains achieved

Features

✅ Working Features

  • Literal string search with basic optimizations
  • Regex pattern matching using Go's standard regexp
  • Directory traversal with file filtering
  • Gitignore support for basic patterns
  • Binary file detection and skipping
  • Context lines around matches
  • Concurrent processing with worker pools
  • Basic Unicode support (via Go's standard library)
  • Functional API with options

❌ Claims We're NOT Making

  • "2-16x faster than Go's standard regex" (No evidence for this)
  • "Sub-millisecond search times" (False for realistic workloads)
  • "DFA caching" (No actual DFA implementation)
  • "Pure Go word-level operations" (Just using standard Go)
  • "CPU feature detection" (Not implemented)

🚧 Areas for Improvement

  • Performance: Currently 46x slower than ripgrep (significant gap)
  • Memory efficiency: High allocation count vs ripgrep
  • Advanced regex features: Basic implementation only
  • SIMD optimizations: Not implemented
  • True DFA compilation: Not implemented
  • File I/O optimization: Standard library approaches vs custom optimizations

Requirements

  • Go 1.21+
  • No external dependencies (pure Go implementation)

Installation

go get github.com/localrivet/goripgrep

Quick Start

Simple Functional API

package main
import (
"fmt""log""github.com/localrivet/goripgrep"
)
funcmain() {
// Basic search (non-recursive by default)results, err:=goripgrep.Find("hello", ".")
iferr!=nil {
log.Fatal(err)
}
// Recursive search with optionsresults, err=goripgrep.Find("hello", ".", goripgrep.WithRecursive(true),
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2))
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Found %d matches in %d files\n", results.Count(), len(results.Files()))
}

Advanced Search with Options

// Search with multiple optionsresults, err:=goripgrep.Find("TODO", "/path/to/project",
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2),
goripgrep.WithFilePattern("*.go"),
goripgrep.WithMaxResults(100),
goripgrep.WithGitignore(true),
goripgrep.WithTimeout(30*time.Second),
)
iferr!=nil {
log.Fatal(err)
}
// Process results with contextfor_, match:=rangeresults.Matches {
fmt.Printf("%s:%d:%d: %s\n", match.File, match.Line, match.Column, match.Content)
// Print context lines if availablefor_, contextLine:=rangematch.Context {
fmt.Printf(" | %s\n", contextLine)
}
}

Architecture

GoRipGrep uses a straightforward architecture with these components:

Core Components

  1. Engine (engine.go)

    • Basic pattern search using Go's regexp package
    • Simple literal string optimization
    • File reading with basic buffering
  2. Search (search.go)

    • Directory traversal with filepath.WalkDir
    • Binary file detection and skipping
    • Worker pool for concurrent processing
    • Basic gitignore support
  3. API (api.go)

    • Functional options pattern
    • Simple configuration management
    • Result aggregation
  4. Types (types.go)

    • Data structures for results and configuration
    • Basic statistics tracking

Performance Analysis

Current Benchmark Results

# Run real-world performance comparison:time rg '\w+Sushi'.# ~20mstime ./goripgrep '\w+Sushi'.# ~922ms (46x slower)# Benchmark tests (if available):
go test -bench=BenchmarkSimpleComparison -benchmem

Key metrics from testing:

  • goripgrep: 922ms (0.922 seconds)
  • ripgrep: 20ms (0.020 seconds)
  • Performance gap: 46x slower

Why Is It So Much Slower?

Honest assessment of performance gaps:

  1. No SIMD optimizations - ripgrep uses assembly-optimized string search
  2. Basic regex engine - using Go's standard regexp vs ripgrep's optimized DFA
  3. Excessive allocations - thousands of allocations vs ripgrep's minimal allocation
  4. No advanced byte scanning - no memchr-style optimizations
  5. Inefficient file I/O - standard library approaches vs ripgrep's optimized I/O
  6. Suboptimal directory walking - basic implementation vs ripgrep's optimized walker
  7. No meaningful optimizations - attempted optimizations provided negligible benefits

Optimization Opportunities

Areas where significant improvements could be made:

  1. Reduce allocations - currently creating 50K+ objects per search
  2. Implement boyer-moore or similar for literal string search
  3. Add binary search optimizations for sorted pattern lists
  4. Optimize file reading with better buffering strategies
  5. Implement actual DFA compilation instead of using standard regexp

Testing

# Run all tests
go test ./...
# Run tests with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Run benchmarks (see honest performance comparison)
go test -bench=. -benchmem ./...
# Compare directly with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Configuration

Functional Options API

// Available options for Find functionresults, err:=goripgrep.Find("pattern", "/path",
goripgrep.WithContext(ctx), // Set context for cancellationgoripgrep.WithWorkers(8), // Number of concurrent workersgoripgrep.WithBufferSize(64*1024), // I/O buffer sizegoripgrep.WithMaxResults(1000), // Maximum results to returngoripgrep.WithOptimization(true), // Enable basic optimizationsgoripgrep.WithGitignore(true), // Enable gitignore filteringgoripgrep.WithIgnoreCase(), // Case-insensitive searchgoripgrep.WithRecursive(true), // Search directories recursivelygoripgrep.WithFilePattern("*.go"), // File pattern filtergoripgrep.WithContextLines(3), // Number of context linesgoripgrep.WithTimeout(30*time.Second), // Search timeout
)

Result Types

typeSearchResultsstruct {
Matches []Match// Found matchesStatsSearchStats// Basic performance statisticsQuerystring// Search pattern
}
typeMatchstruct {
Filestring// Path to the file containing the matchLineint// Line number (1-indexed)Columnint// Column number (1-indexed)Contentstring// Content of the matching lineContext []string// Context lines (if requested)
}
typeSearchStatsstruct {
FilesScannedint64// Number of files scannedFilesSkippedint64// Number of files skippedBytesScannedint64// Total bytes scannedMatchesFoundint64// Total matches foundDuration time.Duration// Search duration
}

Use Cases

Realistic use cases where this might be appropriate:

  • Learning Go text processing techniques
  • Educational projects for understanding search algorithms
  • Small codebases where 869ms search time is acceptable
  • Integration scenarios where pure Go is required
  • Prototyping before switching to production tools

Use cases where you should use ripgrep instead:

  • Production applications requiring fast search
  • Large codebases (>1000 files)
  • Interactive tools where speed matters
  • Any performance-critical scenario

Supported Features

✅ Currently Working

  • Basic literal string search
  • Regular expression patterns (via Go regexp)
  • Case-insensitive search
  • File pattern filtering (basic globs)
  • Binary file detection
  • Hidden file inclusion/exclusion
  • Basic gitignore support
  • Context lines
  • Concurrent processing
  • Unicode support (via Go standard library)
  • Timeout support
  • Result limiting

❌ Not Implemented (Despite Earlier Claims)

  • DFA caching (just uses standard Go regexp)
  • Advanced byte-level optimizations
  • SIMD instructions
  • CPU feature detection
  • Advanced Unicode character classes
  • Streaming decompression
  • Word-level scanning optimizations
  • Performance competitive with ripgrep

🚧 Could Be Improved

  • Memory efficiency (too many allocations)
  • Search speed (fundamental algorithm improvements needed)
  • Regex performance (would need custom engine)
  • File walking optimization
  • Better binary detection

Contributing

This is a learning project. Contributions are welcome, especially:

  • Performance improvements with measurable benchmarks
  • Algorithm optimizations with before/after comparisons
  • Memory allocation reductions
  • Bug fixes with test cases
  • Documentation improvements

Please include benchmark results with any performance-related PRs.

Development Setup

# Clone the repository
git clone https://github.com/localrivet/goripgrep.git
cd goripgrep
# Install dependencies
go mod download
# Run tests and benchmarks
go test ./...
go test -bench=. -benchmem ./...
# Compare with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Honest Performance Comparison

If you need fast text search, use ripgrep. This project is:

Good for: Learning, education, Go integration, small projects
Bad for: Production use, large codebases, performance-critical applications

Performance Reality:

  • ripgrep: 21ms (optimized Rust + assembly)
  • goripgrep: 869ms (educational Go implementation)
  • grep: 2.1s (basic system utility)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Inspired by ripgrep by Andrew Gallant
  • This is NOT a replacement for ripgrep, just a learning exercise
  • Thanks to the Go community for excellent tooling and documentation

GoRipGrep: An educational text search implementation in Go. Use ripgrep for production unless you need a pure go alternaive.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

GoRipGrep

A text search library written in pure Go, inspired by ripgrep. This project is a learning exercise in implementing text search algorithms and optimizations in Go.

⚠️ Performance Notice: This implementation is currently ~46x slower than ripgrep on typical workloads. It's a work-in-progress educational project, not a production replacement for ripgrep.

Current Performance Status

Honest Benchmarks (Pattern: \w+Sushi, local directory)

ripgrep: 20ms (0.020s)
goripgrep: 922ms (0.922s) Performance gap: 46x slower

What this means:

  • ✅ Functionally correct (finds same matches as ripgrep)
  • ❌ Not competitive with ripgrep for production use
  • ✅ Educational value for learning Go optimization techniques
  • ❌ Performance regressions occurred during development

Optimization Attempts

We attempted several optimizations but failed to achieve significant improvements:

  • Memory-mapped file reading: Implemented but limited impact
  • Basic literal string optimization: Added to engine
  • Configuration-based optimization: Added option flags
  • Result: Still significantly slower than ripgrep, no meaningful performance gains achieved

Features

✅ Working Features

  • Literal string search with basic optimizations
  • Regex pattern matching using Go's standard regexp
  • Directory traversal with file filtering
  • Gitignore support for basic patterns
  • Binary file detection and skipping
  • Context lines around matches
  • Concurrent processing with worker pools
  • Basic Unicode support (via Go's standard library)
  • Functional API with options

❌ Claims We're NOT Making

  • "2-16x faster than Go's standard regex" (No evidence for this)
  • "Sub-millisecond search times" (False for realistic workloads)
  • "DFA caching" (No actual DFA implementation)
  • "Pure Go word-level operations" (Just using standard Go)
  • "CPU feature detection" (Not implemented)

🚧 Areas for Improvement

  • Performance: Currently 46x slower than ripgrep (significant gap)
  • Memory efficiency: High allocation count vs ripgrep
  • Advanced regex features: Basic implementation only
  • SIMD optimizations: Not implemented
  • True DFA compilation: Not implemented
  • File I/O optimization: Standard library approaches vs custom optimizations

Requirements

  • Go 1.21+
  • No external dependencies (pure Go implementation)

Installation

go get github.com/localrivet/goripgrep

Quick Start

Simple Functional API

package main
import (
"fmt""log""github.com/localrivet/goripgrep"
)
funcmain() {
// Basic search (non-recursive by default)results, err:=goripgrep.Find("hello", ".")
iferr!=nil {
log.Fatal(err)
}
// Recursive search with optionsresults, err=goripgrep.Find("hello", ".", goripgrep.WithRecursive(true),
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2))
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Found %d matches in %d files\n", results.Count(), len(results.Files()))
}

Advanced Search with Options

// Search with multiple optionsresults, err:=goripgrep.Find("TODO", "/path/to/project",
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2),
goripgrep.WithFilePattern("*.go"),
goripgrep.WithMaxResults(100),
goripgrep.WithGitignore(true),
goripgrep.WithTimeout(30*time.Second),
)
iferr!=nil {
log.Fatal(err)
}
// Process results with contextfor_, match:=rangeresults.Matches {
fmt.Printf("%s:%d:%d: %s\n", match.File, match.Line, match.Column, match.Content)
// Print context lines if availablefor_, contextLine:=rangematch.Context {
fmt.Printf(" | %s\n", contextLine)
}
}

Architecture

GoRipGrep uses a straightforward architecture with these components:

Core Components

  1. Engine (engine.go)

    • Basic pattern search using Go's regexp package
    • Simple literal string optimization
    • File reading with basic buffering
  2. Search (search.go)

    • Directory traversal with filepath.WalkDir
    • Binary file detection and skipping
    • Worker pool for concurrent processing
    • Basic gitignore support
  3. API (api.go)

    • Functional options pattern
    • Simple configuration management
    • Result aggregation
  4. Types (types.go)

    • Data structures for results and configuration
    • Basic statistics tracking

Performance Analysis

Current Benchmark Results

# Run real-world performance comparison:time rg '\w+Sushi'.# ~20mstime ./goripgrep '\w+Sushi'.# ~922ms (46x slower)# Benchmark tests (if available):
go test -bench=BenchmarkSimpleComparison -benchmem

Key metrics from testing:

  • goripgrep: 922ms (0.922 seconds)
  • ripgrep: 20ms (0.020 seconds)
  • Performance gap: 46x slower

Why Is It So Much Slower?

Honest assessment of performance gaps:

  1. No SIMD optimizations - ripgrep uses assembly-optimized string search
  2. Basic regex engine - using Go's standard regexp vs ripgrep's optimized DFA
  3. Excessive allocations - thousands of allocations vs ripgrep's minimal allocation
  4. No advanced byte scanning - no memchr-style optimizations
  5. Inefficient file I/O - standard library approaches vs ripgrep's optimized I/O
  6. Suboptimal directory walking - basic implementation vs ripgrep's optimized walker
  7. No meaningful optimizations - attempted optimizations provided negligible benefits

Optimization Opportunities

Areas where significant improvements could be made:

  1. Reduce allocations - currently creating 50K+ objects per search
  2. Implement boyer-moore or similar for literal string search
  3. Add binary search optimizations for sorted pattern lists
  4. Optimize file reading with better buffering strategies
  5. Implement actual DFA compilation instead of using standard regexp

Testing

# Run all tests
go test ./...
# Run tests with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Run benchmarks (see honest performance comparison)
go test -bench=. -benchmem ./...
# Compare directly with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Configuration

Functional Options API

// Available options for Find functionresults, err:=goripgrep.Find("pattern", "/path",
goripgrep.WithContext(ctx), // Set context for cancellationgoripgrep.WithWorkers(8), // Number of concurrent workersgoripgrep.WithBufferSize(64*1024), // I/O buffer sizegoripgrep.WithMaxResults(1000), // Maximum results to returngoripgrep.WithOptimization(true), // Enable basic optimizationsgoripgrep.WithGitignore(true), // Enable gitignore filteringgoripgrep.WithIgnoreCase(), // Case-insensitive searchgoripgrep.WithRecursive(true), // Search directories recursivelygoripgrep.WithFilePattern("*.go"), // File pattern filtergoripgrep.WithContextLines(3), // Number of context linesgoripgrep.WithTimeout(30*time.Second), // Search timeout
)

Result Types

typeSearchResultsstruct {
Matches []Match// Found matchesStatsSearchStats// Basic performance statisticsQuerystring// Search pattern
}
typeMatchstruct {
Filestring// Path to the file containing the matchLineint// Line number (1-indexed)Columnint// Column number (1-indexed)Contentstring// Content of the matching lineContext []string// Context lines (if requested)
}
typeSearchStatsstruct {
FilesScannedint64// Number of files scannedFilesSkippedint64// Number of files skippedBytesScannedint64// Total bytes scannedMatchesFoundint64// Total matches foundDuration time.Duration// Search duration
}

Use Cases

Realistic use cases where this might be appropriate:

  • Learning Go text processing techniques
  • Educational projects for understanding search algorithms
  • Small codebases where 869ms search time is acceptable
  • Integration scenarios where pure Go is required
  • Prototyping before switching to production tools

Use cases where you should use ripgrep instead:

  • Production applications requiring fast search
  • Large codebases (>1000 files)
  • Interactive tools where speed matters
  • Any performance-critical scenario

Supported Features

✅ Currently Working

  • Basic literal string search
  • Regular expression patterns (via Go regexp)
  • Case-insensitive search
  • File pattern filtering (basic globs)
  • Binary file detection
  • Hidden file inclusion/exclusion
  • Basic gitignore support
  • Context lines
  • Concurrent processing
  • Unicode support (via Go standard library)
  • Timeout support
  • Result limiting

❌ Not Implemented (Despite Earlier Claims)

  • DFA caching (just uses standard Go regexp)
  • Advanced byte-level optimizations
  • SIMD instructions
  • CPU feature detection
  • Advanced Unicode character classes
  • Streaming decompression
  • Word-level scanning optimizations
  • Performance competitive with ripgrep

🚧 Could Be Improved

  • Memory efficiency (too many allocations)
  • Search speed (fundamental algorithm improvements needed)
  • Regex performance (would need custom engine)
  • File walking optimization
  • Better binary detection

Contributing

This is a learning project. Contributions are welcome, especially:

  • Performance improvements with measurable benchmarks
  • Algorithm optimizations with before/after comparisons
  • Memory allocation reductions
  • Bug fixes with test cases
  • Documentation improvements

Please include benchmark results with any performance-related PRs.

Development Setup

# Clone the repository
git clone https://github.com/localrivet/goripgrep.git
cd goripgrep
# Install dependencies
go mod download
# Run tests and benchmarks
go test ./...
go test -bench=. -benchmem ./...
# Compare with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Honest Performance Comparison

If you need fast text search, use ripgrep. This project is:

Good for: Learning, education, Go integration, small projects
Bad for: Production use, large codebases, performance-critical applications

Performance Reality:

  • ripgrep: 21ms (optimized Rust + assembly)
  • goripgrep: 869ms (educational Go implementation)
  • grep: 2.1s (basic system utility)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Inspired by ripgrep by Andrew Gallant
  • This is NOT a replacement for ripgrep, just a learning exercise
  • Thanks to the Go community for excellent tooling and documentation

GoRipGrep: An educational text search implementation in Go. Use ripgrep for production unless you need a pure go alternaive.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

GoRipGrep

A text search library written in pure Go, inspired by ripgrep. This project is a learning exercise in implementing text search algorithms and optimizations in Go.

⚠️ Performance Notice: This implementation is currently ~46x slower than ripgrep on typical workloads. It's a work-in-progress educational project, not a production replacement for ripgrep.

Current Performance Status

Honest Benchmarks (Pattern: \w+Sushi, local directory)

ripgrep: 20ms (0.020s)
goripgrep: 922ms (0.922s) Performance gap: 46x slower

What this means:

  • ✅ Functionally correct (finds same matches as ripgrep)
  • ❌ Not competitive with ripgrep for production use
  • ✅ Educational value for learning Go optimization techniques
  • ❌ Performance regressions occurred during development

Optimization Attempts

We attempted several optimizations but failed to achieve significant improvements:

  • Memory-mapped file reading: Implemented but limited impact
  • Basic literal string optimization: Added to engine
  • Configuration-based optimization: Added option flags
  • Result: Still significantly slower than ripgrep, no meaningful performance gains achieved

Features

✅ Working Features

  • Literal string search with basic optimizations
  • Regex pattern matching using Go's standard regexp
  • Directory traversal with file filtering
  • Gitignore support for basic patterns
  • Binary file detection and skipping
  • Context lines around matches
  • Concurrent processing with worker pools
  • Basic Unicode support (via Go's standard library)
  • Functional API with options

❌ Claims We're NOT Making

  • "2-16x faster than Go's standard regex" (No evidence for this)
  • "Sub-millisecond search times" (False for realistic workloads)
  • "DFA caching" (No actual DFA implementation)
  • "Pure Go word-level operations" (Just using standard Go)
  • "CPU feature detection" (Not implemented)

🚧 Areas for Improvement

  • Performance: Currently 46x slower than ripgrep (significant gap)
  • Memory efficiency: High allocation count vs ripgrep
  • Advanced regex features: Basic implementation only
  • SIMD optimizations: Not implemented
  • True DFA compilation: Not implemented
  • File I/O optimization: Standard library approaches vs custom optimizations

Requirements

  • Go 1.21+
  • No external dependencies (pure Go implementation)

Installation

go get github.com/localrivet/goripgrep

Quick Start

Simple Functional API

package main
import (
"fmt""log""github.com/localrivet/goripgrep"
)
funcmain() {
// Basic search (non-recursive by default)results, err:=goripgrep.Find("hello", ".")
iferr!=nil {
log.Fatal(err)
}
// Recursive search with optionsresults, err=goripgrep.Find("hello", ".", goripgrep.WithRecursive(true),
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2))
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Found %d matches in %d files\n", results.Count(), len(results.Files()))
}

Advanced Search with Options

// Search with multiple optionsresults, err:=goripgrep.Find("TODO", "/path/to/project",
goripgrep.WithIgnoreCase(),
goripgrep.WithContextLines(2),
goripgrep.WithFilePattern("*.go"),
goripgrep.WithMaxResults(100),
goripgrep.WithGitignore(true),
goripgrep.WithTimeout(30*time.Second),
)
iferr!=nil {
log.Fatal(err)
}
// Process results with contextfor_, match:=rangeresults.Matches {
fmt.Printf("%s:%d:%d: %s\n", match.File, match.Line, match.Column, match.Content)
// Print context lines if availablefor_, contextLine:=rangematch.Context {
fmt.Printf(" | %s\n", contextLine)
}
}

Architecture

GoRipGrep uses a straightforward architecture with these components:

Core Components

  1. Engine (engine.go)

    • Basic pattern search using Go's regexp package
    • Simple literal string optimization
    • File reading with basic buffering
  2. Search (search.go)

    • Directory traversal with filepath.WalkDir
    • Binary file detection and skipping
    • Worker pool for concurrent processing
    • Basic gitignore support
  3. API (api.go)

    • Functional options pattern
    • Simple configuration management
    • Result aggregation
  4. Types (types.go)

    • Data structures for results and configuration
    • Basic statistics tracking

Performance Analysis

Current Benchmark Results

# Run real-world performance comparison:time rg '\w+Sushi'.# ~20mstime ./goripgrep '\w+Sushi'.# ~922ms (46x slower)# Benchmark tests (if available):
go test -bench=BenchmarkSimpleComparison -benchmem

Key metrics from testing:

  • goripgrep: 922ms (0.922 seconds)
  • ripgrep: 20ms (0.020 seconds)
  • Performance gap: 46x slower

Why Is It So Much Slower?

Honest assessment of performance gaps:

  1. No SIMD optimizations - ripgrep uses assembly-optimized string search
  2. Basic regex engine - using Go's standard regexp vs ripgrep's optimized DFA
  3. Excessive allocations - thousands of allocations vs ripgrep's minimal allocation
  4. No advanced byte scanning - no memchr-style optimizations
  5. Inefficient file I/O - standard library approaches vs ripgrep's optimized I/O
  6. Suboptimal directory walking - basic implementation vs ripgrep's optimized walker
  7. No meaningful optimizations - attempted optimizations provided negligible benefits

Optimization Opportunities

Areas where significant improvements could be made:

  1. Reduce allocations - currently creating 50K+ objects per search
  2. Implement boyer-moore or similar for literal string search
  3. Add binary search optimizations for sorted pattern lists
  4. Optimize file reading with better buffering strategies
  5. Implement actual DFA compilation instead of using standard regexp

Testing

# Run all tests
go test ./...
# Run tests with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Run benchmarks (see honest performance comparison)
go test -bench=. -benchmem ./...
# Compare directly with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Configuration

Functional Options API

// Available options for Find functionresults, err:=goripgrep.Find("pattern", "/path",
goripgrep.WithContext(ctx), // Set context for cancellationgoripgrep.WithWorkers(8), // Number of concurrent workersgoripgrep.WithBufferSize(64*1024), // I/O buffer sizegoripgrep.WithMaxResults(1000), // Maximum results to returngoripgrep.WithOptimization(true), // Enable basic optimizationsgoripgrep.WithGitignore(true), // Enable gitignore filteringgoripgrep.WithIgnoreCase(), // Case-insensitive searchgoripgrep.WithRecursive(true), // Search directories recursivelygoripgrep.WithFilePattern("*.go"), // File pattern filtergoripgrep.WithContextLines(3), // Number of context linesgoripgrep.WithTimeout(30*time.Second), // Search timeout
)

Result Types

typeSearchResultsstruct {
Matches []Match// Found matchesStatsSearchStats// Basic performance statisticsQuerystring// Search pattern
}
typeMatchstruct {
Filestring// Path to the file containing the matchLineint// Line number (1-indexed)Columnint// Column number (1-indexed)Contentstring// Content of the matching lineContext []string// Context lines (if requested)
}
typeSearchStatsstruct {
FilesScannedint64// Number of files scannedFilesSkippedint64// Number of files skippedBytesScannedint64// Total bytes scannedMatchesFoundint64// Total matches foundDuration time.Duration// Search duration
}

Use Cases

Realistic use cases where this might be appropriate:

  • Learning Go text processing techniques
  • Educational projects for understanding search algorithms
  • Small codebases where 869ms search time is acceptable
  • Integration scenarios where pure Go is required
  • Prototyping before switching to production tools

Use cases where you should use ripgrep instead:

  • Production applications requiring fast search
  • Large codebases (>1000 files)
  • Interactive tools where speed matters
  • Any performance-critical scenario

Supported Features

✅ Currently Working

  • Basic literal string search
  • Regular expression patterns (via Go regexp)
  • Case-insensitive search
  • File pattern filtering (basic globs)
  • Binary file detection
  • Hidden file inclusion/exclusion
  • Basic gitignore support
  • Context lines
  • Concurrent processing
  • Unicode support (via Go standard library)
  • Timeout support
  • Result limiting

❌ Not Implemented (Despite Earlier Claims)

  • DFA caching (just uses standard Go regexp)
  • Advanced byte-level optimizations
  • SIMD instructions
  • CPU feature detection
  • Advanced Unicode character classes
  • Streaming decompression
  • Word-level scanning optimizations
  • Performance competitive with ripgrep

🚧 Could Be Improved

  • Memory efficiency (too many allocations)
  • Search speed (fundamental algorithm improvements needed)
  • Regex performance (would need custom engine)
  • File walking optimization
  • Better binary detection

Contributing

This is a learning project. Contributions are welcome, especially:

  • Performance improvements with measurable benchmarks
  • Algorithm optimizations with before/after comparisons
  • Memory allocation reductions
  • Bug fixes with test cases
  • Documentation improvements

Please include benchmark results with any performance-related PRs.

Development Setup

# Clone the repository
git clone https://github.com/localrivet/goripgrep.git
cd goripgrep
# Install dependencies
go mod download
# Run tests and benchmarks
go test ./...
go test -bench=. -benchmem ./...
# Compare with ripgreptime rg '\w+Sushi'.time ./goripgrep '\w+Sushi'.

Honest Performance Comparison

If you need fast text search, use ripgrep. This project is:

Good for: Learning, education, Go integration, small projects
Bad for: Production use, large codebases, performance-critical applications

Performance Reality:

  • ripgrep: 21ms (optimized Rust + assembly)
  • goripgrep: 869ms (educational Go implementation)
  • grep: 2.1s (basic system utility)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Inspired by ripgrep by Andrew Gallant
  • This is NOT a replacement for ripgrep, just a learning exercise
  • Thanks to the Go community for excellent tooling and documentation

GoRipGrep: An educational text search implementation in Go. Use ripgrep for production unless you need a pure go alternaive.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages