aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-06 12:01:14 +0800
committerGravatar Runxi Yu2026-03-06 12:01:14 +0800
commita7df501aff793dcd55168b7440fb531d40bd75c0 (patch)
treee48a04d3634a8fbe77638547e675786de86e46de
parentformat/commitgraph: Add package-level doc comments (diff)
signatureNo signature
internal/iolimit: Add CappedCaptureWriter
-rw-r--r--internal/iolimit/capped_capture_writer.go52
-rw-r--r--internal/iolimit/capped_capture_writer_test.go45
-rw-r--r--internal/iolimit/doc.go5
-rw-r--r--internal/iolimit/expect_length_reader.go2
4 files changed, 102 insertions, 2 deletions
diff --git a/internal/iolimit/capped_capture_writer.go b/internal/iolimit/capped_capture_writer.go
new file mode 100644
index 00000000..2e69806a
--- /dev/null
+++ b/internal/iolimit/capped_capture_writer.go
@@ -0,0 +1,52 @@
+package iolimit
+
+import "bytes"
+
+// CappedCaptureWriter captures written bytes up to a fixed limit.
+//
+// Once the total written bytes would exceed the limit, capture is disabled and
+// Bytes() returns nil. Write still reports success for the full input length.
+type CappedCaptureWriter struct {
+ limit int64
+ buf bytes.Buffer
+ full bool
+}
+
+// NewCappedCaptureWriter constructs one capped capture writer.
+func NewCappedCaptureWriter(limit int64) *CappedCaptureWriter {
+ return &CappedCaptureWriter{limit: limit}
+}
+
+// Write captures up to the configured limit and always reports len(src) bytes written.
+func (writer *CappedCaptureWriter) Write(src []byte) (int, error) {
+ if writer.full {
+ return len(src), nil
+ }
+
+ room := writer.limit - int64(writer.buf.Len())
+ if room <= 0 {
+ writer.full = true
+
+ return len(src), nil
+ }
+
+ if int64(len(src)) > room {
+ _, _ = writer.buf.Write(src[:room])
+ writer.full = true
+
+ return len(src), nil
+ }
+
+ _, _ = writer.buf.Write(src)
+
+ return len(src), nil
+}
+
+// Bytes returns captured bytes, or nil when capture exceeded the limit.
+func (writer *CappedCaptureWriter) Bytes() []byte {
+ if writer.full {
+ return nil
+ }
+
+ return writer.buf.Bytes()
+}
diff --git a/internal/iolimit/capped_capture_writer_test.go b/internal/iolimit/capped_capture_writer_test.go
new file mode 100644
index 00000000..e95d06ef
--- /dev/null
+++ b/internal/iolimit/capped_capture_writer_test.go
@@ -0,0 +1,45 @@
+package iolimit_test
+
+import (
+ "bytes"
+ "testing"
+
+ "codeberg.org/lindenii/furgit/internal/iolimit"
+)
+
+func TestCappedCaptureWriterWithinLimit(t *testing.T) {
+ t.Parallel()
+
+ writer := iolimit.NewCappedCaptureWriter(8)
+
+ _, _ = writer.Write([]byte("hello"))
+ _, _ = writer.Write([]byte("!"))
+
+ if got := writer.Bytes(); !bytes.Equal(got, []byte("hello!")) {
+ t.Fatalf("Bytes() = %q, want %q", got, "hello!")
+ }
+}
+
+func TestCappedCaptureWriterExceededLimit(t *testing.T) {
+ t.Parallel()
+
+ writer := iolimit.NewCappedCaptureWriter(4)
+
+ _, _ = writer.Write([]byte("abcd"))
+ _, _ = writer.Write([]byte("x"))
+
+ if got := writer.Bytes(); got != nil {
+ t.Fatalf("Bytes() = %q, want nil after overflow", got)
+ }
+}
+
+func TestCappedCaptureWriterZeroLimit(t *testing.T) {
+ t.Parallel()
+
+ writer := iolimit.NewCappedCaptureWriter(0)
+
+ _, _ = writer.Write([]byte("x"))
+ if got := writer.Bytes(); got != nil {
+ t.Fatalf("Bytes() = %q, want nil at zero limit", got)
+ }
+}
diff --git a/internal/iolimit/doc.go b/internal/iolimit/doc.go
new file mode 100644
index 00000000..b3e81ce2
--- /dev/null
+++ b/internal/iolimit/doc.go
@@ -0,0 +1,5 @@
+// Package iolimit provides small internal I/O wrappers with bounded behavior.
+//
+// It includes helpers for both readers and writers that enforce configured
+// limits (length checks, capped capture, etc.).
+package iolimit
diff --git a/internal/iolimit/expect_length_reader.go b/internal/iolimit/expect_length_reader.go
index 288e0e62..a45ad567 100644
--- a/internal/iolimit/expect_length_reader.go
+++ b/internal/iolimit/expect_length_reader.go
@@ -1,5 +1,3 @@
-// Package iolimit provides small internal reader wrappers for length-constrained
-// stream I/O.
package iolimit
import (