v1.14.0
ADDED: * iox package * mapsx package * netx/inetcksum package
This commit is contained in:
222
iox/funcs.go
222
iox/funcs.go
@@ -1,20 +1,21 @@
|
||||
package iox
|
||||
|
||||
import (
|
||||
`context`
|
||||
`io`
|
||||
)
|
||||
|
||||
/*
|
||||
CopyBufN is a mix between io.CopyN and io.CopyBuffer.
|
||||
CopyBufN is a mix between [io.CopyN] and [io.CopyBuffer].
|
||||
|
||||
Despite what the docs may suggest, io.CopyN does NOT *read* n bytes from src AND write n bytes to dst.
|
||||
Despite what the docs may suggest, [io.CopyN] does NOT *read* n bytes from src AND write n bytes to dst.
|
||||
Instead, it always reads 32 KiB from src, and writes n bytes to dst.
|
||||
|
||||
There are, of course, cases where this is deadfully undesired.
|
||||
There are cases where this is dreadfully undesired.
|
||||
|
||||
One can, of course, use io.CopyBuffer, but this is a bit annoying since you then have to provide a buffer yourself.
|
||||
One can, of course, use [io.CopyBuffer], but this is a bit annoying since you then have to provide a buffer yourself.
|
||||
|
||||
This convenience-wraps io.CopyBuffer to have a similar signature to io.CopyN but properly uses n for both reading and writing.
|
||||
This convenience-wraps [io.CopyBuffer] to have a similar signature to [io.CopyN] but properly uses n for both reading and writing.
|
||||
*/
|
||||
func CopyBufN(dst io.Writer, src io.Reader, n int64) (written int64, err error) {
|
||||
|
||||
@@ -32,10 +33,215 @@ func CopyBufN(dst io.Writer, src io.Reader, n int64) (written int64, err error)
|
||||
return
|
||||
}
|
||||
|
||||
// CopyBufWith allows for specifying a buffer allocator function, otherwise acts as CopyBufN.
|
||||
func CopyBufWith(dst io.Writer, src io.Reader, bufFunc func() (b []byte)) (written int64, err error) {
|
||||
// CopyCtxBufN copies from `src` to `dst`, `n` bytes at a time, interruptible by `ctx`.
|
||||
func CopyCtxBufN(ctx context.Context, dst io.Writer, src io.Reader, n int64) (written int64, err error) {
|
||||
|
||||
written, err = io.CopyBuffer(dst, src, bufFunc())
|
||||
var nr int
|
||||
var nw int
|
||||
var end bool
|
||||
var buf []byte
|
||||
|
||||
if ctx == nil {
|
||||
err = ErrNilCtx
|
||||
return
|
||||
}
|
||||
if n <= 0 {
|
||||
err = ErrBufTooSmall
|
||||
return
|
||||
}
|
||||
|
||||
endCopy:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
return
|
||||
default:
|
||||
buf = make([]byte, n)
|
||||
nr, err = src.Read(buf)
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
end = true
|
||||
} else if err != nil {
|
||||
return
|
||||
}
|
||||
buf = buf[:nr]
|
||||
|
||||
if nw, err = dst.Write(buf); err != nil {
|
||||
written += int64(nw)
|
||||
return
|
||||
}
|
||||
written += int64(nw)
|
||||
if len(buf) != nw {
|
||||
err = io.ErrShortWrite
|
||||
return
|
||||
}
|
||||
if end {
|
||||
break endCopy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
CopyBufWith allows for specifying a buffer allocator function, otherwise acts as [CopyBufN].
|
||||
|
||||
bufFunc *MUST NOT* return a nil or len == 0 buffer. [ErrBufTooSmall] will be returned if it does.
|
||||
|
||||
This uses a fixed buffer size from a single call to `bufFunc`.
|
||||
If you need something with dynamic buffer sizing according to some state, use [CopyBufWithDynamic] instead.
|
||||
(Note that CopyBufWithDynamic is generally a little slower, but it should only be noticeable on very large amounts of data.)
|
||||
*/
|
||||
func CopyBufWith(dst io.Writer, src io.Reader, bufFunc func() (b []byte)) (written int64, err error) {
|
||||
|
||||
var buf []byte = bufFunc()
|
||||
|
||||
if buf == nil || len(buf) == 0 {
|
||||
err = ErrBufTooSmall
|
||||
return
|
||||
}
|
||||
|
||||
written, err = io.CopyBuffer(dst, src, buf)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
CopyBufWithDynamic is like [CopyBufWith] except it will call bufFunc after each previous buffer is written.
|
||||
|
||||
That is to say (using a particularly contrived example):
|
||||
|
||||
import time
|
||||
|
||||
func dynBuf() (b []byte) {
|
||||
|
||||
var t time.Time = time.Now()
|
||||
|
||||
b = make([]byte, t.Seconds())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
Then:
|
||||
|
||||
CopyBufWithDynamic(w, r, dynBuf)
|
||||
|
||||
will use a buffer sized to the seconds of the time it reads in/writes out the next buffer, whereas with [CopyBufWith]:
|
||||
|
||||
CopyBufWith(w, r, dynBuf)
|
||||
|
||||
would use a *fixed* buffer size of whatever the seconds was equal to at the time of the *first call* to dynBuf.
|
||||
|
||||
`src` MUST return an [io.EOF] when its end is reached, but (as per e.g. [io.CopyBuffer]) the io.EOF error will not
|
||||
be returned from CopyBufWithDynamic. (Any/all other errors encountered will be returned, however, and copying will
|
||||
immediately cease.)
|
||||
*/
|
||||
func CopyBufWithDynamic(dst io.Writer, src io.Reader, bufFunc func() (b []byte)) (written int64, err error) {
|
||||
|
||||
var nr int
|
||||
var nw int
|
||||
var end bool
|
||||
var buf []byte
|
||||
|
||||
for {
|
||||
buf = bufFunc()
|
||||
if buf == nil || len(buf) == 0 {
|
||||
err = ErrBufTooSmall
|
||||
return
|
||||
}
|
||||
nr, err = src.Read(buf)
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
end = true
|
||||
} else if err != nil {
|
||||
return
|
||||
}
|
||||
buf = buf[:nr]
|
||||
|
||||
if nw, err = dst.Write(buf); err != nil {
|
||||
written += int64(nw)
|
||||
return
|
||||
}
|
||||
written += int64(nw)
|
||||
if len(buf) != nw {
|
||||
err = ErrShortWrite
|
||||
return
|
||||
}
|
||||
if end {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NewChunker returns a [ChunkLocker] ready to use.
|
||||
func NewChunker(chunkSize uint) (c *ChunkLocker, err error) {
|
||||
|
||||
c = &ChunkLocker{}
|
||||
err = c.SetChunkLen(chunkSize)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NewCtxIO returns a [CtxIO].
|
||||
func NewCtxIO(ctx context.Context, r io.Reader, w io.Writer, chunkSize uint) (c *CtxIO, err error) {
|
||||
|
||||
if r == nil {
|
||||
err = ErrNilReader
|
||||
return
|
||||
}
|
||||
if w == nil {
|
||||
err = ErrNilWriter
|
||||
return
|
||||
}
|
||||
|
||||
if chunkSize == 0 {
|
||||
err = ErrInvalidChunkSize
|
||||
return
|
||||
}
|
||||
|
||||
if ctx == nil {
|
||||
err = ErrNilCtx
|
||||
return
|
||||
}
|
||||
|
||||
c = &CtxIO{
|
||||
r: r,
|
||||
w: w,
|
||||
l: ChunkLocker{
|
||||
chunkLen: chunkSize,
|
||||
},
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
NewXIO returns a nil [XIO].
|
||||
|
||||
A weird "feature" of Golang is that a nil XIO is perfectly fine to use;
|
||||
it's completely stateless and only has pointer receivers that only work with passed in
|
||||
values so `new(XIO)` is completely unnecessary (as is NewXCopier).
|
||||
In other words, this works fine:
|
||||
|
||||
var xc *iox.XIO
|
||||
|
||||
if n, err = xc.Copy(w, r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
This function is just to maintain cleaner-looking code if you should so need it,
|
||||
or want an XIO without declaring one:
|
||||
|
||||
if n, err = iox.NewXCopier().Copy(w, r); err != nil {
|
||||
return
|
||||
}
|
||||
*/
|
||||
func NewXIO() (x *XIO) {
|
||||
// No-op lel
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user