alist/pkg/utils/io.go

72 lines
1.8 KiB
Go
Raw Normal View History

2022-06-25 07:14:03 +00:00
package utils
import (
"context"
"io"
)
// here is some syntaxic sugar inspired by the Tomas Senart's video,
// it allows me to inline the Reader interface
type readerFunc func(p []byte) (n int, err error)
func (rf readerFunc) Read(p []byte) (n int, err error) { return rf(p) }
// CopyWithCtx slightly modified function signature:
// - context has been added in order to propagate cancelation
// - I do not return the number of bytes written, has it is not useful in my use case
2022-08-31 14:41:27 +00:00
func CopyWithCtx(ctx context.Context, out io.Writer, in io.Reader, size int64, progress func(percentage int)) error {
2022-06-25 07:14:03 +00:00
// Copy will call the Reader and Writer interface multiple time, in order
// to copy by chunk (avoiding loading the whole file in memory).
// I insert the ability to cancel before read time as it is the earliest
// possible in the call process.
2022-08-31 14:41:27 +00:00
var finish int64 = 0
s := size / 100
2022-06-25 07:14:03 +00:00
_, err := io.Copy(out, readerFunc(func(p []byte) (int, error) {
// golang non-blocking channel: https://gobyexample.com/non-blocking-channel-operations
select {
// if context has been canceled
case <-ctx.Done():
// stop process and propagate "context canceled" error
return 0, ctx.Err()
default:
// otherwise just run default io.Reader implementation
2022-08-31 14:41:27 +00:00
n, err := in.Read(p)
2022-09-03 11:32:44 +00:00
if s > 0 && (err == nil || err == io.EOF) {
2022-08-31 14:41:27 +00:00
finish += int64(n)
progress(int(finish / s))
}
return n, err
2022-06-25 07:14:03 +00:00
}
}))
return err
}
2022-09-12 09:10:02 +00:00
type limitWriter struct {
w io.Writer
count int64
limit int64
}
func (l limitWriter) Write(p []byte) (n int, err error) {
wn := int(l.limit - l.count)
if wn > len(p) {
wn = len(p)
}
if wn > 0 {
if n, err = l.w.Write(p[:wn]); err != nil {
return
}
if n < wn {
err = io.ErrShortWrite
}
}
if err == nil {
n = len(p)
}
return
}
func LimitWriter(w io.Writer, size int64) io.Writer {
return &limitWriter{w: w, limit: size}
}