mirror of https://github.com/perkeep/perkeep.git
106 lines
3.1 KiB
Go
106 lines
3.1 KiB
Go
/*
|
|
Copyright 2016 The go4 Authors
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
// Package writerutil contains io.Writer types.
|
|
package writerutil // import "go4.org/writerutil"
|
|
|
|
import (
|
|
"bytes"
|
|
"strconv"
|
|
)
|
|
|
|
// PrefixSuffixSaver is an io.Writer which retains the first N bytes
|
|
// and the last N bytes written to it. The Bytes method reconstructs
|
|
// it with a pretty error message.
|
|
// It is copied from os/exec/exec.go of the Go stdlib.
|
|
type PrefixSuffixSaver struct {
|
|
N int // max size of prefix or suffix
|
|
prefix []byte
|
|
suffix []byte // ring buffer once len(suffix) == N
|
|
suffixOff int // offset to write into suffix
|
|
skipped int64
|
|
|
|
// TODO(bradfitz): we could keep one large []byte and use part of it for
|
|
// the prefix, reserve space for the '... Omitting N bytes ...' message,
|
|
// then the ring buffer suffix, and just rearrange the ring buffer
|
|
// suffix when Bytes() is called, but it doesn't seem worth it for
|
|
// now just for error messages. It's only ~64KB anyway.
|
|
}
|
|
|
|
func (w *PrefixSuffixSaver) Write(p []byte) (n int, err error) {
|
|
lenp := len(p)
|
|
p = w.fill(&w.prefix, p)
|
|
|
|
// Only keep the last w.N bytes of suffix data.
|
|
if overage := len(p) - w.N; overage > 0 {
|
|
p = p[overage:]
|
|
w.skipped += int64(overage)
|
|
}
|
|
p = w.fill(&w.suffix, p)
|
|
|
|
// w.suffix is full now if p is non-empty. Overwrite it in a circle.
|
|
for len(p) > 0 { // 0, 1, or 2 iterations.
|
|
n := copy(w.suffix[w.suffixOff:], p)
|
|
p = p[n:]
|
|
w.skipped += int64(n)
|
|
w.suffixOff += n
|
|
if w.suffixOff == w.N {
|
|
w.suffixOff = 0
|
|
}
|
|
}
|
|
return lenp, nil
|
|
}
|
|
|
|
// fill appends up to len(p) bytes of p to *dst, such that *dst does not
|
|
// grow larger than w.N. It returns the un-appended suffix of p.
|
|
func (w *PrefixSuffixSaver) fill(dst *[]byte, p []byte) (pRemain []byte) {
|
|
if remain := w.N - len(*dst); remain > 0 {
|
|
add := minInt(len(p), remain)
|
|
*dst = append(*dst, p[:add]...)
|
|
p = p[add:]
|
|
}
|
|
return p
|
|
}
|
|
|
|
// Bytes returns a slice of the bytes, or a copy of the bytes, retained by w.
|
|
// If more bytes than could be retained were written to w, it returns a
|
|
// concatenation of the N first bytes, a message for how many bytes were dropped,
|
|
// and the N last bytes.
|
|
func (w *PrefixSuffixSaver) Bytes() []byte {
|
|
if w.suffix == nil {
|
|
return w.prefix
|
|
}
|
|
if w.skipped == 0 {
|
|
return append(w.prefix, w.suffix...)
|
|
}
|
|
var buf bytes.Buffer
|
|
buf.Grow(len(w.prefix) + len(w.suffix) + 50)
|
|
buf.Write(w.prefix)
|
|
buf.WriteString("\n... omitting ")
|
|
buf.WriteString(strconv.FormatInt(w.skipped, 10))
|
|
buf.WriteString(" bytes ...\n")
|
|
buf.Write(w.suffix[w.suffixOff:])
|
|
buf.Write(w.suffix[:w.suffixOff])
|
|
return buf.Bytes()
|
|
}
|
|
|
|
func minInt(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|