mirror of
https://github.com/nabbar/golib.git
synced 2025-12-24 11:51:02 +08:00
[file/bandwidth] - ADD documentation: add enhanced README and TESTING guidelines - ADD tests: complete test suites with benchmarks, concurrency, and edge cases [file/perm] - ADD documentation: add enhanced README and TESTING guidelines - ADD tests: complete test suites with benchmarks, concurrency, and edge cases - ADD function to parse form "rwx-wxr-x" or "-rwx-w-r-x" - ADD function to ParseFileMode to convert os.FileMode to file.Perm [file/progress] - ADD documentation: add enhanced README and TESTING guidelines - ADD tests: complete test suites with benchmarks, concurrency, and edge cases [ioutils/...] - UPDATE documentation: update enhanced README and TESTING guidelines - UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases [logger/...] - UPDATE documentation: update enhanced README and TESTING guidelines - ADD documentation: add enhanced README and TESTING guidelines for sub packages - UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases - UPDATE config: remove FileBufferSize from OptionFile (rework hookfile) - UPDATE fields: expose Store function in interface - REWORK hookfile: rework package, use aggregator to allow multi write and single file - FIX hookstderr: fix bug with NonColorable - FIX hookstdout: fix bug with NonColorable - FIX hookwriter: fix bug with NonColorable [network/protocol] - ADD function IsTCP, IsUDP, IsUnixLike to check type of protocol [runner] - FIX typo [socket] - UPDATE documentation: update enhanced README and TESTING guidelines - ADD documentation: add enhanced README and TESTING guidelines for sub packages - UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases - REWORK server: use context compatible io.reader, io.writer, io.closer instead of reader / writer - REWORK server: simplify, optimize server - REMOVE reader, writer type - ADD context: add new interface in root socket interface to expose context interface that extend context, io reader/writer/closer, dediacted function to server (IsConnected, ...)
255 lines
8.2 KiB
Go
255 lines
8.2 KiB
Go
/*
|
|
* MIT License
|
|
*
|
|
* Copyright (c) 2025 Nicolas JUHEL
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
Package nopwritecloser provides a wrapper that implements io.WriteCloser for an io.Writer
|
|
by adding a no-op Close() method.
|
|
|
|
# Design Philosophy
|
|
|
|
The nopwritecloser package is the write-equivalent of Go's standard io.NopCloser (which
|
|
works with readers). It follows these design principles:
|
|
|
|
- Simplicity: Single-purpose utility with minimal API surface - only one exported function
|
|
- Compatibility: Bridge io.Writer to io.WriteCloser without changing behavior
|
|
- Safety: Close() is always safe to call, always returns nil, never affects the underlying writer
|
|
- Zero Overhead: Thin wrapper with no performance penalty - direct delegation
|
|
- Predictability: No hidden behavior, no resource management, no state changes
|
|
|
|
The package delegates Write() calls to the underlying io.Writer and implements Close() as
|
|
a no-operation that always returns nil, allowing io.Writer implementations to satisfy the
|
|
io.WriteCloser interface without requiring actual close semantics.
|
|
|
|
# Architecture
|
|
|
|
The package consists of a simple wrapper type that holds a reference to the underlying
|
|
io.Writer. The wrapper implements both Write() and Close() methods:
|
|
|
|
- Write(p []byte) (n int, err error): Delegates to the underlying writer unchanged
|
|
- Close() error: Always returns nil, does not affect the underlying writer
|
|
|
|
Data Flow:
|
|
|
|
User Code
|
|
↓
|
|
New(writer) → Creates wrapper
|
|
↓
|
|
WriteCloser
|
|
↓
|
|
Write(data) → Delegate to writer.Write(data)
|
|
↓
|
|
Close() → Return nil (no-op)
|
|
|
|
The wrapper adds zero overhead beyond a single pointer dereference, making it suitable
|
|
for performance-critical code.
|
|
|
|
# Key Features
|
|
|
|
- Simple API: Single function New(io.Writer) io.WriteCloser
|
|
- No-Op Close: Close() always returns nil, never affects underlying writer
|
|
- Zero Dependencies: Uses only standard library interfaces
|
|
- Thread-Safe: Safe for concurrent use if underlying writer is thread-safe
|
|
- 100% Test Coverage: Comprehensive test suite with 54+ specs
|
|
- Production Ready: Battle-tested, simple implementation with no known edge cases
|
|
|
|
# Use Cases
|
|
|
|
1. API Compatibility
|
|
|
|
When a function requires io.WriteCloser but you have io.Writer that shouldn't be closed:
|
|
|
|
func processData(wc io.WriteCloser) {
|
|
defer wc.Close() // Safe with nopwritecloser
|
|
wc.Write([]byte("data"))
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
processData(nopwritecloser.New(&buf))
|
|
// buf is still usable after function returns
|
|
|
|
2. Protecting Standard Streams
|
|
|
|
Prevent accidental closure of stdout/stderr in code that expects a closeable writer:
|
|
|
|
func writeOutput(wc io.WriteCloser) {
|
|
defer wc.Close() // Would close stdout without wrapper!
|
|
wc.Write([]byte("output\n"))
|
|
}
|
|
|
|
writeOutput(nopwritecloser.New(os.Stdout))
|
|
// stdout remains open and usable
|
|
|
|
3. Testing and Inspection
|
|
|
|
Inspect output after Close() is called by test code:
|
|
|
|
func TestWriter(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
wc := nopwritecloser.New(&buf)
|
|
|
|
functionThatCloses(wc) // Calls Close() internally
|
|
|
|
// Can still inspect buffer contents
|
|
if !strings.Contains(buf.String(), "expected") {
|
|
t.Error("Missing expected output")
|
|
}
|
|
}
|
|
|
|
4. Shared Resources
|
|
|
|
Multiple components writing to the same resource without interference:
|
|
|
|
var logBuffer bytes.Buffer
|
|
wc := nopwritecloser.New(&logBuffer)
|
|
|
|
writeLog("module1", wc) // Each can call Close()
|
|
writeLog("module2", wc) // without affecting
|
|
writeLog("module3", wc) // the shared buffer
|
|
|
|
5. HTTP Response Writers
|
|
|
|
Prevent middleware from closing the response writer:
|
|
|
|
func compressHandler(w http.ResponseWriter, r *http.Request) {
|
|
gz := gzip.NewWriter(nopwritecloser.New(w))
|
|
defer gz.Close()
|
|
|
|
gz.Write([]byte("Compressed content"))
|
|
// gz.Close() won't close the ResponseWriter
|
|
}
|
|
|
|
# Performance Characteristics
|
|
|
|
The wrapper is designed for minimal overhead:
|
|
|
|
- Creation: ~5 ns (single struct allocation)
|
|
- Write(): ~0 ns overhead (direct delegation to underlying writer)
|
|
- Close(): ~0 ns (immediate nil return)
|
|
- Memory: 8 bytes (single pointer field)
|
|
- Allocations: 1 per New() call, 0 at runtime
|
|
|
|
Benchmark results show the wrapper adds negligible overhead compared to direct writes.
|
|
|
|
# Advantages
|
|
|
|
- Solves a common problem: Many APIs require io.WriteCloser but not all writers need closing
|
|
- No boilerplate: No need to create custom wrapper types for each use case
|
|
- Safe by design: Close() can never cause unintended side effects
|
|
- Compatible: Drop-in replacement anywhere io.WriteCloser is expected
|
|
- Well tested: 100% code coverage with comprehensive test suite
|
|
- Standard pattern: Mirrors io.NopCloser from the standard library
|
|
|
|
# Disadvantages and Limitations
|
|
|
|
- Single purpose: Only solves the Writer→WriteCloser adaptation problem
|
|
- No state tracking: Doesn't track whether Close() was called
|
|
- No validation: Doesn't verify if underlying writer is nil (will panic on Write)
|
|
- Not a real closer: If you need actual close semantics, use a real io.WriteCloser
|
|
- Thread safety depends on underlying writer: Not thread-safe if wrapped writer isn't
|
|
|
|
# Important Considerations
|
|
|
|
1. Use only when the underlying writer does NOT need closing:
|
|
|
|
// ✅ Good - bytes.Buffer doesn't need closing
|
|
wc := nopwritecloser.New(&buf)
|
|
|
|
// ❌ Bad - file DOES need closing
|
|
file, _ := os.Create("file.txt")
|
|
wc := nopwritecloser.New(file) // File won't be closed!
|
|
|
|
2. Thread safety is inherited from the underlying writer:
|
|
|
|
// ✅ Good - if underlying writer is thread-safe
|
|
safeWriter := &threadSafeWriter{}
|
|
wc := nopwritecloser.New(safeWriter)
|
|
|
|
// ❌ Bad - bytes.Buffer is not thread-safe
|
|
var buf bytes.Buffer
|
|
wc := nopwritecloser.New(&buf)
|
|
go wc.Write([]byte("1")) // Race condition!
|
|
go wc.Write([]byte("2"))
|
|
|
|
3. Writes work after Close() is called:
|
|
|
|
wc := nopwritecloser.New(&buf)
|
|
wc.Write([]byte("before"))
|
|
wc.Close()
|
|
wc.Write([]byte("after")) // Still works! Buffer has "beforeafter"
|
|
|
|
# Typical Usage Patterns
|
|
|
|
Basic wrapper creation:
|
|
|
|
var buf bytes.Buffer
|
|
wc := nopwritecloser.New(&buf)
|
|
wc.Write([]byte("data"))
|
|
wc.Close() // Safe, no-op
|
|
|
|
With defer pattern:
|
|
|
|
func writeData(data []byte) error {
|
|
var buf bytes.Buffer
|
|
wc := nopwritecloser.New(&buf)
|
|
defer wc.Close() // Safe, always succeeds
|
|
|
|
_, err := wc.Write(data)
|
|
return err
|
|
}
|
|
|
|
Passing to functions requiring io.WriteCloser:
|
|
|
|
func processWriter(wc io.WriteCloser) {
|
|
defer wc.Close()
|
|
// ... use wc ...
|
|
}
|
|
|
|
processWriter(nopwritecloser.New(os.Stdout))
|
|
|
|
# Package Organization
|
|
|
|
The package consists of:
|
|
|
|
- interface.go: Public API with New() function
|
|
- model.go: Internal wrapper implementation (wrp type)
|
|
- doc.go: Package documentation (this file)
|
|
- *_test.go: Comprehensive test suite
|
|
|
|
The implementation is intentionally minimal to ensure reliability and ease of maintenance.
|
|
|
|
# Related Packages
|
|
|
|
- io.NopCloser: Standard library function for Reader→ReadCloser (read equivalent)
|
|
- bufferReadCloser: sibling package with buffered read support
|
|
- ioutils: parent package with additional I/O utilities
|
|
|
|
# References
|
|
|
|
- Go io package: https://pkg.go.dev/io
|
|
- io.NopCloser: https://pkg.go.dev/io#NopCloser
|
|
- io.WriteCloser: https://pkg.go.dev/io#WriteCloser
|
|
*/
|
|
package nopwritecloser
|