mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-26 20:27:03 +01:00
225 lines
4.6 KiB
Go
225 lines
4.6 KiB
Go
|
package fwd
|
||
|
|
||
|
import "io"
|
||
|
|
||
|
const (
|
||
|
// DefaultWriterSize is the
|
||
|
// default write buffer size.
|
||
|
DefaultWriterSize = 2048
|
||
|
|
||
|
minWriterSize = minReaderSize
|
||
|
)
|
||
|
|
||
|
// Writer is a buffered writer
|
||
|
type Writer struct {
|
||
|
w io.Writer // writer
|
||
|
buf []byte // 0:len(buf) is bufered data
|
||
|
}
|
||
|
|
||
|
// NewWriter returns a new writer
|
||
|
// that writes to 'w' and has a buffer
|
||
|
// that is `DefaultWriterSize` bytes.
|
||
|
func NewWriter(w io.Writer) *Writer {
|
||
|
if wr, ok := w.(*Writer); ok {
|
||
|
return wr
|
||
|
}
|
||
|
return &Writer{
|
||
|
w: w,
|
||
|
buf: make([]byte, 0, DefaultWriterSize),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NewWriterSize returns a new writer
|
||
|
// that writes to 'w' and has a buffer
|
||
|
// that is 'size' bytes.
|
||
|
func NewWriterSize(w io.Writer, size int) *Writer {
|
||
|
if wr, ok := w.(*Writer); ok && cap(wr.buf) >= size {
|
||
|
return wr
|
||
|
}
|
||
|
return &Writer{
|
||
|
w: w,
|
||
|
buf: make([]byte, 0, max(size, minWriterSize)),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Buffered returns the number of buffered bytes
|
||
|
// in the reader.
|
||
|
func (w *Writer) Buffered() int { return len(w.buf) }
|
||
|
|
||
|
// BufferSize returns the maximum size of the buffer.
|
||
|
func (w *Writer) BufferSize() int { return cap(w.buf) }
|
||
|
|
||
|
// Flush flushes any buffered bytes
|
||
|
// to the underlying writer.
|
||
|
func (w *Writer) Flush() error {
|
||
|
l := len(w.buf)
|
||
|
if l > 0 {
|
||
|
n, err := w.w.Write(w.buf)
|
||
|
|
||
|
// if we didn't write the whole
|
||
|
// thing, copy the unwritten
|
||
|
// bytes to the beginnning of the
|
||
|
// buffer.
|
||
|
if n < l && n > 0 {
|
||
|
w.pushback(n)
|
||
|
if err == nil {
|
||
|
err = io.ErrShortWrite
|
||
|
}
|
||
|
}
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
w.buf = w.buf[:0]
|
||
|
return nil
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Write implements `io.Writer`
|
||
|
func (w *Writer) Write(p []byte) (int, error) {
|
||
|
c, l, ln := cap(w.buf), len(w.buf), len(p)
|
||
|
avail := c - l
|
||
|
|
||
|
// requires flush
|
||
|
if avail < ln {
|
||
|
if err := w.Flush(); err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
l = len(w.buf)
|
||
|
}
|
||
|
// too big to fit in buffer;
|
||
|
// write directly to w.w
|
||
|
if c < ln {
|
||
|
return w.w.Write(p)
|
||
|
}
|
||
|
|
||
|
// grow buf slice; copy; return
|
||
|
w.buf = w.buf[:l+ln]
|
||
|
return copy(w.buf[l:], p), nil
|
||
|
}
|
||
|
|
||
|
// WriteString is analogous to Write, but it takes a string.
|
||
|
func (w *Writer) WriteString(s string) (int, error) {
|
||
|
c, l, ln := cap(w.buf), len(w.buf), len(s)
|
||
|
avail := c - l
|
||
|
|
||
|
// requires flush
|
||
|
if avail < ln {
|
||
|
if err := w.Flush(); err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
l = len(w.buf)
|
||
|
}
|
||
|
// too big to fit in buffer;
|
||
|
// write directly to w.w
|
||
|
//
|
||
|
// yes, this is unsafe. *but*
|
||
|
// io.Writer is not allowed
|
||
|
// to mutate its input or
|
||
|
// maintain a reference to it,
|
||
|
// per the spec in package io.
|
||
|
//
|
||
|
// plus, if the string is really
|
||
|
// too big to fit in the buffer, then
|
||
|
// creating a copy to write it is
|
||
|
// expensive (and, strictly speaking,
|
||
|
// unnecessary)
|
||
|
if c < ln {
|
||
|
return w.w.Write(unsafestr(s))
|
||
|
}
|
||
|
|
||
|
// grow buf slice; copy; return
|
||
|
w.buf = w.buf[:l+ln]
|
||
|
return copy(w.buf[l:], s), nil
|
||
|
}
|
||
|
|
||
|
// WriteByte implements `io.ByteWriter`
|
||
|
func (w *Writer) WriteByte(b byte) error {
|
||
|
if len(w.buf) == cap(w.buf) {
|
||
|
if err := w.Flush(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
w.buf = append(w.buf, b)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Next returns the next 'n' free bytes
|
||
|
// in the write buffer, flushing the writer
|
||
|
// as necessary. Next will return `io.ErrShortBuffer`
|
||
|
// if 'n' is greater than the size of the write buffer.
|
||
|
// Calls to 'next' increment the write position by
|
||
|
// the size of the returned buffer.
|
||
|
func (w *Writer) Next(n int) ([]byte, error) {
|
||
|
c, l := cap(w.buf), len(w.buf)
|
||
|
if n > c {
|
||
|
return nil, io.ErrShortBuffer
|
||
|
}
|
||
|
avail := c - l
|
||
|
if avail < n {
|
||
|
if err := w.Flush(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
l = len(w.buf)
|
||
|
}
|
||
|
w.buf = w.buf[:l+n]
|
||
|
return w.buf[l:], nil
|
||
|
}
|
||
|
|
||
|
// take the bytes from w.buf[n:len(w.buf)]
|
||
|
// and put them at the beginning of w.buf,
|
||
|
// and resize to the length of the copied segment.
|
||
|
func (w *Writer) pushback(n int) {
|
||
|
w.buf = w.buf[:copy(w.buf, w.buf[n:])]
|
||
|
}
|
||
|
|
||
|
// ReadFrom implements `io.ReaderFrom`
|
||
|
func (w *Writer) ReadFrom(r io.Reader) (int64, error) {
|
||
|
// anticipatory flush
|
||
|
if err := w.Flush(); err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
|
||
|
w.buf = w.buf[0:cap(w.buf)] // expand buffer
|
||
|
|
||
|
var nn int64 // written
|
||
|
var err error // error
|
||
|
var x int // read
|
||
|
|
||
|
// 1:1 reads and writes
|
||
|
for err == nil {
|
||
|
x, err = r.Read(w.buf)
|
||
|
if x > 0 {
|
||
|
n, werr := w.w.Write(w.buf[:x])
|
||
|
nn += int64(n)
|
||
|
|
||
|
if err != nil {
|
||
|
if n < x && n > 0 {
|
||
|
w.pushback(n - x)
|
||
|
}
|
||
|
return nn, werr
|
||
|
}
|
||
|
if n < x {
|
||
|
w.pushback(n - x)
|
||
|
return nn, io.ErrShortWrite
|
||
|
}
|
||
|
} else if err == nil {
|
||
|
err = io.ErrNoProgress
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if err != io.EOF {
|
||
|
return nn, err
|
||
|
}
|
||
|
|
||
|
// we only clear here
|
||
|
// because we are sure
|
||
|
// the writes have
|
||
|
// succeeded. otherwise,
|
||
|
// we retain the data in case
|
||
|
// future writes succeed.
|
||
|
w.buf = w.buf[0:0]
|
||
|
|
||
|
return nn, nil
|
||
|
}
|