it - Buffered Iterator Utility for Go
The it package provides a powerful and flexible iterator for Go, enabling buffered iteration over any iter.Seq[T] sequence with advanced features like peeking, seeking, rewinding, and configurable buffer management. It supports various data sources (slices, channels, maps, and IO readers) and is optimized for performance and memory efficiency.
Features
- Buffered Iteration: Store elements for efficient navigation and rewinding.
- Peek Ahead: Inspect the next element without advancing the iterator.
- Relative and Absolute Navigation: Use
Seekto move relative positions orSeekTofor absolute positions. - Rewind Capability: Reset to the initial state while preserving the buffer.
- Configurable Buffer Limits: Set initial and maximum buffer sizes for memory control.
- Multiple Source Adapters: Iterate over slices, channels, map keys/values, and IO streams (lines, bytes, runes).
- Context Support: Integrate cancellation for IO operations.
- Efficient Memory Management: Minimal allocations with optimized buffer growth.
- Transform Functions: Apply transformations during iteration or dumping.
Installation
go get github.com/olekukonko/itQuick Start
Here’s a simple example using a Fibonacci sequence to demonstrate the it package’s core features:
package main
import (
"fmt"
"github.com/olekukonko/it"
"iter"
)
// Fibonacci generator
func fibonacci() iter.Seq[int] {
return func(yield func(int) bool) {
a, b := 0, 1
for {
if !yield(a) {
return
}
a, b = b, a+b
}
}
}
func main() {
// Create iterator from Fibonacci sequence
it, err := it.Wrap(fibonacci())
if err != nil {
fmt.Println("Error:", err)
return
}
// Iterate over first 5 numbers
for i := 0; i < 5; i++ {
v, ok := it.Next()
if !ok {
break
}
fmt.Printf("Fibonacci[%d]: %d\n", i, v)
}
// Peek at the next number
if v, ok := it.Peek(); ok {
fmt.Println("Next Fibonacci:", v)
}
// Rewind and iterate again
it.Rewind()
for i := 0; i < 3; i++ {
v, ok := it.Next()
if !ok {
break
}
fmt.Printf("Rewound Fibonacci[%d]: %d\n", i, v)
}
}Output:
Fibonacci[0]: 0
Fibonacci[1]: 1
Fibonacci[2]: 1
Fibonacci[3]: 2
Fibonacci[4]: 3
Next Fibonacci: 5
Rewound Fibonacci[0]: 0
Rewound Fibonacci[1]: 1
Rewound Fibonacci[2]: 1
Key Features and Examples
Buffered Iteration
Create an iterator from a slice and navigate through elements:
it, err := it.Slice([]string{"a", "b", "c"})
if err != nil {
fmt.Println("Error:", err)
return
}
for v, ok := it.Next(); ok; v, ok = it.Next() {
fmt.Println(v)
}Seeking and Navigation
Move relative to the current position with Seek or to an absolute position with SeekTo:
it, err := it.Slice([]string{"a", "b", "c", "d"})
if err != nil {
fmt.Println("Error:", err)
return
}
it.Next() // Move to "a"
if v, ok := it.Seek(1); ok { // Skip 1 element, move to "c"
fmt.Println("After Seek(1):", v) // Output: c
}
if v, ok := it.SeekTo(1); ok { // Move to position 1 ("b")
fmt.Println("After SeekTo(1):", v) // Output: b
}IO Operations
Read lines, bytes, or runes from an IO source with configurable buffer sizes:
import "strings"
reader := strings.NewReader("line 1\nline 2\nline 3")
cfg := it.Config().BufferSize(1024).MaxLineLength(8192)
lines, err := cfg.Lines(reader)
if err != nil {
fmt.Println("Error:", err)
return
}
for line, ok := lines.Next(); ok; line, ok = lines.Next() {
fmt.Println(line)
}Transform Functions
Apply transformations when dumping all elements:
it, err := it.Slice([]int{1, 2, 3})
if err != nil {
fmt.Println("Error:", err)
return
}
doubled, err := it.Dump(func(n int) int { return n * 2 })
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(doubled) // Output: [2 4 6]API Reference
Core Methods
Next() (T, bool): Advances to the next element, returning its value and whether it exists.Peek() (T, bool): Returns the next element without advancing.Seek(n int) (T, bool): Movesnelements forward (or backward if negative), returning the element at the new position.SeekTo(pos int) (T, bool): Jumps to the absolute positionpos.Rewind(): Resets the iterator to the initial state.Buffer() []T: Returns a copy of buffered elements.Dump(...func(T) T) ([]T, error): Consumes the iterator and returns all elements, optionally applying transformations.Position() int: Returns the current position.BufferedLen() int: Returns the number of buffered elements.Cap() int: Returns the current buffer capacity.
Factory Functions
New[T](src Seq[T], initialCap, maxCap int) (*Iter[T], error): Creates an iterator with custom buffer settings.Wrap[T](src Seq[T]) (*Iter[T], error): Creates an iterator with default settings (initialCap=64, maxCap=0).Must[T](src Seq[T]) *Iter[T]: Creates an iterator or panics on error.Slice[T](s []T) (*Iter[T], error): Creates an iterator from a slice.Chan[T](c *C, ch <-chan T) (*Iter[T], error): Creates an iterator from a channel.Keys[K, V](m map[K]V) (*Iter[K], error): Creates an iterator over map keys.Vals[K, V](m map[K]V) (*Iter[V], error): Creates an iterator over map values.Pairs[K, V](m map[K]V) (*Iter[Pair[K, V]], error): Creates an iterator over map key-value pairs.Tea(sources ...io.Reader) (*Iter[string], error): Creates an iterator over lines from multiple readers.C.Lines(r io.Reader) (*Iter[string], error): Creates an iterator over lines from a reader.C.Bytes(r io.Reader) (*Iter[[]byte], error): Creates an iterator over byte chunks.C.Runes(r io.Reader) (*Iter[rune], error): Creates an iterator over runes.
Configuration
Customize iterator behavior with the Config builder:
cfg := it.Config().
InitialCap(128). // Initial buffer capacity
MaxCap(1024). // Maximum buffer capacity
BufferSize(8192). // IO buffer size
MaxLineLength(128 * 1024). // Maximum line length
WithContext(ctx) // Context for cancellation
it, err := cfg.Lines(reader)Testing and Reliability
The it package is thoroughly tested to ensure reliability across various use cases, including:
- Basic iteration (
TestNext,TestSeq) - Peeking and seeking (
TestPeek,TestSeek,TestSeekTo) - Buffer management (
TestBuffer) - IO operations (
TestLines,TestBytes,TestRunes) - Edge cases (
TestEmptyIterator,TestSingleElement)
Run the tests to verify:
go test -vPerformance
- Minimal Allocations: Optimized buffer growth reduces memory overhead.
- Configurable Limits: Set
maxCapto control memory usage. - Efficient Navigation: Buffered elements enable fast seeking and rewinding.
- Concurrent Safety: Channel-based iteration ensures safe sequence consumption.
Contributing
Contributions are welcome! Please submit issues or pull requests to the GitHub repository: github.com/olekukonko/it.
License
MIT License