| @@ -90,6 +90,41 @@ func AfterReadClosedOnce(closer io.ReadCloser, callback func(closer io.ReadClose | |||
| } | |||
| } | |||
| type afterEOF struct { | |||
| inner io.ReadCloser | |||
| callback func(str io.ReadCloser, err error) | |||
| } | |||
| func (hook *afterEOF) Read(buf []byte) (n int, err error) { | |||
| n, err = hook.inner.Read(buf) | |||
| if hook.callback != nil { | |||
| if err == io.EOF { | |||
| hook.callback(hook.inner, nil) | |||
| hook.callback = nil | |||
| } else if err != nil { | |||
| hook.callback(hook.inner, err) | |||
| hook.callback = nil | |||
| } | |||
| } | |||
| return n, err | |||
| } | |||
| func (hook *afterEOF) Close() error { | |||
| err := hook.inner.Close() | |||
| if hook.callback != nil { | |||
| hook.callback(hook.inner, io.ErrClosedPipe) | |||
| hook.callback = nil | |||
| } | |||
| return err | |||
| } | |||
| func AfterEOF(str io.ReadCloser, callback func(str io.ReadCloser, err error)) io.ReadCloser { | |||
| return &afterEOF{ | |||
| inner: str, | |||
| callback: callback, | |||
| } | |||
| } | |||
| type readerWithCloser struct { | |||
| reader io.Reader | |||
| closer func(reader io.Reader) error | |||
| @@ -152,3 +187,15 @@ func ToReaders(strs []io.ReadCloser) ([]io.Reader, func()) { | |||
| } | |||
| } | |||
| } | |||
| func DropWithBuf(str io.Reader, buf []byte) error { | |||
| for { | |||
| _, err := str.Read(buf) | |||
| if err == io.EOF { | |||
| return nil | |||
| } | |||
| if err != nil { | |||
| return err | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,79 @@ | |||
| package io2 | |||
| import ( | |||
| "io" | |||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||
| ) | |||
| type rng struct { | |||
| offset int64 | |||
| length *int64 | |||
| inner io.ReadCloser | |||
| err error | |||
| } | |||
| func (r *rng) Read(p []byte) (n int, err error) { | |||
| if r.err != nil { | |||
| return 0, r.err | |||
| } | |||
| if r.offset > 0 { | |||
| buf := make([]byte, 1024*16) | |||
| for r.offset > 0 { | |||
| need := math2.Min(r.offset, int64(len(buf))) | |||
| rd, err := r.inner.Read(buf[:need]) | |||
| if err != nil { | |||
| r.err = err | |||
| return 0, err | |||
| } | |||
| r.offset -= int64(rd) | |||
| } | |||
| } | |||
| if r.length == nil { | |||
| return r.inner.Read(p) | |||
| } | |||
| need := math2.Min(*r.length, int64(len(p))) | |||
| rd, err := r.inner.Read(p[:need]) | |||
| if err != nil { | |||
| r.err = err | |||
| return rd, io.EOF | |||
| } | |||
| *r.length -= int64(rd) | |||
| if *r.length == 0 { | |||
| r.err = io.EOF | |||
| } | |||
| return rd, nil | |||
| } | |||
| func (r *rng) Close() error { | |||
| r.err = io.ErrClosedPipe | |||
| return r.inner.Close() | |||
| } | |||
| func NewRange(inner io.ReadCloser, offset int64, length *int64) io.ReadCloser { | |||
| return &rng{ | |||
| offset: offset, | |||
| length: length, | |||
| inner: inner, | |||
| } | |||
| } | |||
| func Ranged(inner io.ReadCloser, offset int64, length int64) io.ReadCloser { | |||
| return &rng{ | |||
| offset: offset, | |||
| length: &length, | |||
| inner: inner, | |||
| } | |||
| } | |||
| func Offset(inner io.ReadCloser, offset int64) io.ReadCloser { | |||
| return &rng{ | |||
| offset: offset, | |||
| inner: inner, | |||
| } | |||
| } | |||
| @@ -17,3 +17,19 @@ func Min[T constraints.Ordered](v1, v2 T) T { | |||
| return v2 | |||
| } | |||
| func Ceil[T constraints.Integer](v T, div T) T { | |||
| return (v + div - 1) / div * div | |||
| } | |||
| func Floor[T constraints.Integer](v T, div T) T { | |||
| return v / div * div | |||
| } | |||
| func CeilDiv[T constraints.Integer](v T, div T) T { | |||
| return (v + div - 1) / div | |||
| } | |||
| func FloorDiv[T constraints.Integer](v T, div T) T { | |||
| return v / div | |||
| } | |||