Files
golib/ioutils
nabbar 344498a7d8 Improvements, test & documentatons (2025-11 #3)
[root]
- UPDATE documentation: enhanced README and TESTING guidelines
- UPDATE security md file: fix minimal go version needed
- ADD script: add coverage_report.sh script (see TESTING for info)

[ioutils/aggregator]
- ADD package: add new package to simplify aggregation of multiple write
  to a unique writer function
- ADD documentation: add enhanced README and TESTING guidelines
- ADD tests: complete test suites with benchmarks, concurrency, and edge cases

[router]
- UPDATE documentation

[semaphore]
- FIX bug if given context is nil or have error trigger

[shell]
- UPDATE package & sub-package: fix bugs and optimize code
- ADD sub-package tty: allow to backup and restore tty setting
- ADD documentation: add enhanced README and TESTING guidelines
- ADD tests: complete test suites with benchmarks, concurrency, and edge cases

[socket]
- UPDATE package & sub-package: rename function Handler to HandlerFunc
- UPDATE package & sub-package: add new interface Handler to expose a
  socket compatible handler function

[Other]
- UPDATE go.mod: bump dependencies
2025-11-22 18:04:16 +01:00
..

IOUtils Package

Go Version License

Production-ready I/O utilities collection providing specialized tools for stream processing, resource management, progress tracking, and concurrent I/O operations with comprehensive testing and thread-safe implementations.


Table of Contents


Overview

The ioutils package is a comprehensive collection of I/O utilities for Go applications, providing 10 specialized subpackages plus root-level helper functions. Each subpackage addresses specific I/O challenges encountered in production environments: concurrent write aggregation, stream multiplexing, progress tracking, resource lifecycle management, and more.

Design Philosophy

  1. Standard Interface Compliance: All implementations conform to Go's io package interfaces (Reader, Writer, Closer, ReadCloser, WriteCloser)
  2. Thread Safety First: Atomic operations, proper locking, and race-free implementations verified with -race detector
  3. Streaming-Oriented: Designed for continuous data flow with constant memory usage, not batch processing
  4. Resource Management: Proper cleanup with defer, context cancellation support, and automatic lifecycle handling
  5. Zero External Dependencies: Only standard library and internal golib packages
  6. Production Hardened: 772 test specs, 90.7% average coverage, extensive documentation

Key Features

Thread-Safe Operations: All subpackages safe for concurrent use
Context Integration: Cancellation and deadline propagation throughout
Comprehensive Testing: 772 specs, zero race conditions
Rich Subpackage Ecosystem: 10 specialized packages for different I/O needs
High Performance: Benchmarked and optimized for throughput and latency
Well Documented: GoDoc comments, examples, README per subpackage


Architecture

Package Organization

ioutils/
├── tools.go                    Root-level utilities (PathCheckCreate)
│
├── aggregator/                 Concurrent write aggregation
│   └── [115 specs, 86.0% coverage]
│
├── bufferReadCloser/           Buffered readers with closer
│   └── [44 specs, 100% coverage]
│
├── delim/                      Delimiter-based stream processing
│   └── [95 specs, 100% coverage]
│
├── fileDescriptor/             File descriptor management
│   └── [28 specs, 85.7% coverage]
│
├── ioprogress/                 Progress tracking wrappers
│   └── [54 specs, 84.7% coverage]
│
├── iowrapper/                  Generic I/O wrappers
│   └── [88 specs, 100% coverage]
│
├── mapCloser/                  Multiple closer management
│   └── [82 specs, 77.5% coverage]
│
├── maxstdio/                   Stdio limit management
│   └── [No specs - utility package]
│
├── multi/                      Write multiplexing
│   └── [112 specs, 81.7% coverage]
│
└── nopwritecloser/             No-op writer closers
    └── [54 specs, 100% coverage]

Total: 772 test specs, 90.7% average coverage

Dependency Graph

                     ┌──────────────────────┐
                     │   ioutils (root)     │
                     │   PathCheckCreate    │
                     └──────────────────────┘
                               │
         ┌─────────────────────┼─────────────────────┐
         │                     │                     │
         ▼                     ▼                     ▼
┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐
│   aggregator    │   │   ioprogress    │   │      multi      │
│  (concurrent)   │   │   (tracking)    │   │ (multiplexing)  │
└─────────────────┘   └─────────────────┘   └─────────────────┘
         │                     │                     │
         └─────────────────────┴─────────────────────┘
                               │
         ┌─────────────────────┼─────────────────────┐
         │                     │                     │
         ▼                     ▼                     ▼
┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐
│  iowrapper      │   │   mapCloser     │   │ nopwritecloser  │
│  (generic)      │   │   (lifecycle)   │   │   (no-op)       │
└─────────────────┘   └─────────────────┘   └─────────────────┘
         │                     │                     │
         └─────────────────────┴─────────────────────┘
                               │
         ┌─────────────────────┼─────────────────────┐
         │                     │                     │
         ▼                     ▼                     ▼
┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐
│bufferReadCloser │   │     delim       │   │ fileDescriptor  │
│   (buffering)   │   │  (delimiter)    │   │    (limits)     │
└─────────────────┘   └─────────────────┘   └─────────────────┘

Note: Arrows represent conceptual grouping by complexity, not actual import dependencies.


Performance

Benchmark Summary

Based on actual test execution (772 specs, ~33 seconds total):

Package Specs Coverage Execution Time Notable Metrics
aggregator 115 86.0% ~30.8s Start: 10.7ms, Throughput: 5000-10000/s
bufferReadCloser 44 100% ~0.03s Read: <1ms, Buffer: configurable
delim 95 100% ~0.19s Read: <500µs, Scan: <1ms
fileDescriptor 28 85.7% ~0.01s Limit check: <1µs
ioprogress 54 84.7% ~0.02s Callback: <10µs overhead
iowrapper 88 100% ~0.08s Wrap: <1µs, Pass-through
mapCloser 82 77.5% ~0.02s Add/Remove: <1µs, Close all: <1ms
multi 112 81.7% ~0.15s Write to N: O(N), Copy: <100µs
nopwritecloser 54 100% ~0.24s No-op: <1ns
ioutils (root) - 88.2% ~0.02s PathCheckCreate: varies

Aggregate Performance:

  • Total execution: ~33 seconds (including setup/teardown)
  • Average spec time: ~43ms (dominated by aggregator's timing tests)
  • Zero race conditions: All tests pass with -race detector
  • Memory efficiency: Constant memory for streaming operations

Coverage Statistics

Overall Coverage:       90.7% (weighted average)
Packages at 100%:       5/10 (bufferReadCloser, delim, iowrapper, nopwritecloser, root)
Packages >85%:          8/10
Lowest Coverage:        77.5% (mapCloser - primarily error paths)

Coverage Breakdown:

Coverage Range Count Packages
100% 5 bufferReadCloser, delim, iowrapper, nopwritecloser, root
85-99% 3 aggregator (86%), fileDescriptor (85.7%), ioprogress (84.7%)
75-84% 2 mapCloser (77.5%), multi (81.7%)

Subpackages

aggregator

Purpose: Thread-safe write aggregator that serializes concurrent write operations to a single output function.

Key Features:

  • Concurrent writes from multiple goroutines
  • Configurable buffer for backpressure handling
  • Optional periodic callbacks (async/sync)
  • Real-time metrics (count and size based)
  • Context integration for lifecycle management

Performance:

  • Throughput: 5,000-10,000 writes/second (depends on FctWriter)
  • Latency: Start 10.7ms, Stop 12.1ms, Write <1ms
  • Metrics read: <5µs

Use Case: Socket server logging, database write pooling, network stream multiplexing

Documentation: aggregator/README.md


bufferReadCloser

Purpose: Buffered reader implementation with proper closer interface.

Key Features:

  • Wraps any io.Reader with buffering
  • Implements io.ReadCloser
  • Configurable buffer size
  • Proper resource cleanup

Performance:

  • Read overhead: <1ms
  • Buffer size: configurable (default 4KB)
  • Zero-copy when possible

Use Case: Network streams, file reading with buffering, pipe wrapping

Documentation: bufferReadCloser/README.md


delim

Purpose: High-performance delimiter-based stream processing.

Key Features:

  • Read until any delimiter character
  • Buffered scanning for efficiency
  • Handles any byte delimiter
  • Line and token reading

Performance:

  • Read latency: <500µs
  • Scan latency: <1ms
  • Memory: constant per buffer

Use Case: CSV parsing, log file processing, protocol parsing

Documentation: delim/README.md


fileDescriptor

Purpose: File descriptor limit management and validation.

Key Features:

  • Check system-wide FD limits
  • Validate current usage
  • Preemptive resource checks
  • Cross-platform support

Performance:

  • Limit check: <1µs
  • No overhead on I/O operations

Use Case: High-concurrency servers, connection pooling, resource planning

Documentation: fileDescriptor/README.md


ioprogress

Purpose: Thread-safe I/O progress tracking wrappers.

Key Features:

  • Reader/Writer progress callbacks
  • Byte count tracking
  • Real-time notification
  • Progress bar integration

Performance:

  • Callback overhead: <10µs per operation
  • Thread-safe atomic counters
  • Minimal allocation

Use Case: File uploads/downloads, progress bars, bandwidth monitoring, ETL pipelines

Documentation: ioprogress/README.md


iowrapper

Purpose: Generic I/O wrappers for extending Reader/Writer functionality.

Key Features:

  • Wrap readers and writers
  • Add custom behavior
  • Preserve interface semantics
  • Zero-allocation pass-through

Performance:

  • Wrap overhead: <1µs
  • Pass-through: no measurable impact

Use Case: Logging wrappers, compression, encryption, transformation

Documentation: iowrapper/README.md


mapCloser

Purpose: Thread-safe, context-aware manager for multiple io.Closer instances.

Key Features:

  • Manage multiple closers
  • Automatic cleanup on context cancellation
  • Error aggregation
  • Add/remove closers dynamically

Performance:

  • Add/Remove: <1µs
  • Close all: <1ms (for moderate counts)
  • Thread-safe operations

Use Case: Resource pools, connection management, cleanup coordination

Documentation: mapCloser/README.md


maxstdio

Purpose: Standard I/O (stdin/stdout/stderr) limit enforcement.

Key Features:

  • Protect against excessive stdio usage
  • Redirect overflow to alternatives
  • Configurable thresholds

Use Case: Daemon processes, service wrappers, log management

Documentation: maxstdio/README.md


multi

Purpose: Thread-safe I/O multiplexer for broadcasting writes to multiple destinations.

Key Features:

  • Write to multiple writers atomically
  • Dynamic writer addition/removal
  • Error handling per writer
  • Zero-allocation for single writer
  • Copy to multiple outputs

Performance:

  • Write to N writers: O(N) time
  • Copy operation: <100µs
  • Atomic operations

Use Case: Logging to multiple files, network fanout, tee operations

Documentation: multi/README.md


nopwritecloser

Purpose: No-op writer closer for testing and interface satisfaction.

Key Features:

  • Implements io.WriteCloser
  • Write discards all data
  • Close is always successful
  • Useful for testing

Performance:

  • Write: <1ns (no-op)
  • Close: <1ns (no-op)
  • Zero allocation

Use Case: Testing, benchmarking, interface mocking, discard sinks

Documentation: nopwritecloser/README.md


Root-Level Utilities

PathCheckCreate

Function: PathCheckCreate(isFile bool, path string, permFile os.FileMode, permDir os.FileMode) error

Purpose: Ensures a file or directory exists with correct permissions.

Features:

  • Creates files or directories as needed
  • Creates parent directories automatically
  • Validates and updates permissions
  • Type checking (file vs directory)
  • Atomic creation with proper error handling

Example:

// Ensure config directory exists
err := ioutils.PathCheckCreate(false, "/etc/app/config", 0644, 0755)

// Ensure log file exists
err := ioutils.PathCheckCreate(true, "/var/log/app.log", 0644, 0755)

Use Case: Application initialization, config file setup, log directory creation


Quick Start

Installation

# Install entire package
go get github.com/nabbar/golib/ioutils

# Import specific subpackages as needed
import (
    "github.com/nabbar/golib/ioutils"
    "github.com/nabbar/golib/ioutils/aggregator"
    "github.com/nabbar/golib/ioutils/multi"
    "github.com/nabbar/golib/ioutils/ioprogress"
)

Basic Examples

Path Management:

import "github.com/nabbar/golib/ioutils"

// Create config directory
if err := ioutils.PathCheckCreate(false, "/etc/myapp", 0644, 0755); err != nil {
    log.Fatal(err)
}

// Create log file
if err := ioutils.PathCheckCreate(true, "/var/log/myapp.log", 0644, 0755); err != nil {
    log.Fatal(err)
}

Write Aggregation (aggregator):

import "github.com/nabbar/golib/ioutils/aggregator"

cfg := aggregator.Config{
    BufWriter: 100,
    FctWriter: func(p []byte) (int, error) {
        return logFile.Write(p)
    },
}

agg, _ := aggregator.New(ctx, cfg, logger)
agg.Start(ctx)
defer agg.Close()

// Write from multiple goroutines safely
for i := 0; i < 10; i++ {
    go func(id int) {
        agg.Write([]byte(fmt.Sprintf("Log from goroutine %d\n", id)))
    }(i)
}

Write Multiplexing (multi):

import "github.com/nabbar/golib/ioutils/multi"

// Create multi-writer
mw := multi.New()

// Add multiple destinations
file1, _ := os.Create("output1.txt")
file2, _ := os.Create("output2.txt")

mw.AddWriter(file1)
mw.AddWriter(file2)

// Write to both files at once
mw.Write([]byte("This goes to both files\n"))

Progress Tracking (ioprogress):

import "github.com/nabbar/golib/ioutils/ioprogress"

// Wrap reader with progress callback
reader := ioprogress.NewReader(file, func(bytes int64) {
    fmt.Printf("Read %d bytes so far\n", bytes)
})

// Read operations trigger callbacks
io.Copy(dest, reader)

Delimiter Reading (delim):

import "github.com/nabbar/golib/ioutils/delim"

scanner := delim.NewScanner(file, '\n')

for scanner.Scan() {
    line := scanner.Text()
    fmt.Println(line)
}

Use Cases

1. High-Concurrency Logging

Problem: Multiple goroutines writing to a single log file (filesystem doesn't support concurrent writes).

Solution: Use aggregator to serialize writes.

logFile, _ := os.Create("app.log")
agg, _ := aggregator.New(ctx, aggregator.Config{
    BufWriter: 1000,
    FctWriter: func(p []byte) (int, error) {
        return logFile.Write(p)
    },
}, logger)

// All goroutines write through aggregator
for i := 0; i < 100; i++ {
    go func(id int) {
        agg.Write([]byte(fmt.Sprintf("[%d] Log message\n", id)))
    }(i)
}

2. Fan-Out Data Broadcasting

Problem: Send data to multiple destinations (files, network, stdout).

Solution: Use multi for write multiplexing.

mw := multi.New()
mw.AddWriter(os.Stdout)
mw.AddWriter(logFile)
mw.AddWriter(networkConn)

// One write reaches all destinations
mw.Write([]byte("Broadcast message\n"))

3. Upload Progress Tracking

Problem: Show upload progress to user during file transfer.

Solution: Use ioprogress wrapper.

file, _ := os.Open("large-file.dat")
progressReader := ioprogress.NewReader(file, func(bytes int64) {
    percent := float64(bytes) / float64(fileSize) * 100
    fmt.Printf("Uploaded: %.1f%%\r", percent)
})

http.Post(url, "application/octet-stream", progressReader)

4. Resource Management

Problem: Manage multiple connections/files with automatic cleanup.

Solution: Use mapCloser.

closer := mapcloser.New(ctx)

// Add resources
conn1, _ := net.Dial("tcp", "host1:port")
closer.Add("conn1", conn1)

conn2, _ := net.Dial("tcp", "host2:port")
closer.Add("conn2", conn2)

// Automatic cleanup on context cancel or explicit close
defer closer.Close()

5. Protocol Parsing

Problem: Parse delimited protocol messages efficiently.

Solution: Use delim scanner.

conn, _ := net.Dial("tcp", "server:port")
scanner := delim.NewScanner(conn, '\n')

for scanner.Scan() {
    message := scanner.Text()
    processMessage(message)
}

Best Practices

DO

Choose the Right Subpackage:

// For concurrent writes → aggregator
// For broadcast writes → multi
// For progress tracking → ioprogress
// For resource management → mapCloser
// For delimiter parsing → delim

Proper Resource Cleanup:

// Always close resources
agg, _ := aggregator.New(ctx, cfg, logger)
defer agg.Close()

// Use context for lifecycle
ctx, cancel := context.WithCancel(parent)
defer cancel()

Buffer Sizing:

// Size buffers based on workload
cfg := aggregator.Config{
    BufWriter: writeRate * maxLatency * 1.5,  // Safety margin
    // ...
}

Error Handling:

// Check errors from I/O operations
if n, err := writer.Write(data); err != nil {
    log.Error("Write failed:", err)
    return err
}

Thread Safety:

// All subpackages are thread-safe
for i := 0; i < 10; i++ {
    go func() {
        agg.Write(data)  // Safe concurrent access
    }()
}

DON'T

Don't Ignore Buffer Limits:

// ❌ BAD: No buffer with slow writer
cfg := aggregator.Config{
    BufWriter: 1,  // Will block immediately
    FctWriter: slowWriter,
}

// ✅ GOOD: Sized for throughput
cfg := aggregator.Config{
    BufWriter: 1000,
    FctWriter: slowWriter,
}

Don't Mix Interfaces:

// ❌ BAD: Writing directly bypasses aggregator
agg.Start(ctx)
logFile.Write(data)  // Bypasses serialization

// ✅ GOOD: All writes through aggregator
agg.Write(data)

Don't Forget Context:

// ❌ BAD: No cancellation
agg, _ := aggregator.New(context.Background(), cfg, logger)

// ✅ GOOD: Cancellable context
ctx, cancel := context.WithCancel(parent)
defer cancel()
agg, _ := aggregator.New(ctx, cfg, logger)

Don't Leave Resources Open:

// ❌ BAD: No cleanup
closer := mapcloser.New(ctx)
closer.Add("conn", conn)
// Program exits, resources leak

// ✅ GOOD: Explicit cleanup
defer closer.Close()

Testing

Comprehensive test suite with 772 specs across all subpackages.

Quick Test:

# Run all tests
go test ./...

# With race detector
CGO_ENABLED=1 go test -race ./...

# Coverage report
go test -cover ./...

Expected Results:

  • 772 specs passed
  • 90.7% average coverage
  • Zero race conditions
  • ~33 seconds execution time

See TESTING.md for detailed testing documentation including:

  • Running tests (standard, race, coverage, profiling)
  • Performance benchmarks per subpackage
  • Writing new tests
  • CI integration examples

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Code Quality

    • Follow Go best practices and idioms
    • Maintain or improve code coverage (target: >85%)
    • Pass all tests including race detector
    • Use gofmt and golint
  2. AI Usage Policy

    • Do NOT use AI for implementing package functionality or core logic
    • AI may assist with:
      • Writing and improving tests
      • Documentation and comments
      • Debugging and troubleshooting
    • All AI-assisted contributions must be reviewed and validated by humans
  3. Testing

    • Add tests for new features
    • Use Ginkgo v2 / Gomega for test framework
    • Ensure zero race conditions
    • Maintain coverage above 85%
  4. Documentation

    • Update GoDoc comments for public APIs
    • Add examples for new features
    • Update README.md if adding subpackages
    • Update TESTING.md if changing test structure
  5. Pull Request Process

    • Fork the repository
    • Create a feature branch
    • Write clear commit messages
    • Ensure all tests pass
    • Update documentation
    • Submit PR with description of changes

Future Enhancements

Potential improvements for consideration:

  1. New Subpackages

    • iozip: Streaming compression/decompression wrappers
    • iocrypto: Encryption/decryption stream wrappers
    • ioratelimit: Bandwidth throttling and rate limiting
    • iocache: Write-through/write-back caching layers
  2. Performance Optimizations

    • SIMD-accelerated delimiter scanning (delim)
    • Lock-free queues for aggregator
    • Memory pool for buffer allocation
    • Zero-copy operations where possible
  3. Monitoring Enhancements

    • Prometheus metrics integration
    • OpenTelemetry tracing
    • Structured logging throughout
    • Performance profiling hooks
  4. Advanced Features

    • Async I/O support (io_uring on Linux)
    • Adaptive buffer sizing based on load
    • Priority queuing in aggregator
    • Circuit breaker patterns for reliability

These are suggestions only. Actual implementation depends on real-world usage feedback and community needs.


Resources

Internal Documentation

External References


AI Transparency

In compliance with EU AI Act Article 50.4: AI assistance was used for testing, documentation, and bug resolution under human supervision. All core functionality is human-designed and validated.


License

MIT License - See LICENSE file for details.

Copyright (c) 2021-2024 Nicolas JUHEL


Maintained by: Nicolas JUHEL
Package: github.com/nabbar/golib/ioutils
Version: See releases for versioning