Clash.Meta/hub/updater/limitedreader.go

68 lines
1.3 KiB
Go
Raw Normal View History

package updater
import (
"fmt"
"io"
"golang.org/x/exp/constraints"
)
// LimitReachedError records the limit and the operation that caused it.
type LimitReachedError struct {
Limit int64
}
// Error implements the [error] interface for *LimitReachedError.
//
// TODO(a.garipov): Think about error string format.
func (lre *LimitReachedError) Error() string {
return fmt.Sprintf("attempted to read more than %d bytes", lre.Limit)
}
// limitedReader is a wrapper for [io.Reader] limiting the input and dealing
// with errors package.
type limitedReader struct {
r io.Reader
limit int64
n int64
}
// Read implements the [io.Reader] interface.
func (lr *limitedReader) Read(p []byte) (n int, err error) {
if lr.n == 0 {
return 0, &LimitReachedError{
Limit: lr.limit,
}
}
p = p[:Min(lr.n, int64(len(p)))]
n, err = lr.r.Read(p)
lr.n -= int64(n)
return n, err
}
// LimitReader wraps Reader to make it's Reader stop with ErrLimitReached after
// n bytes read.
func LimitReader(r io.Reader, n int64) (limited io.Reader, err error) {
if n < 0 {
return nil, &updateError{Message: "limit must be non-negative"}
}
return &limitedReader{
r: r,
limit: n,
n: n,
}, nil
}
// Min returns the smaller of x or y.
func Min[T constraints.Integer | ~string](x, y T) (res T) {
if x < y {
return x
}
return y
}