blob: ccc0026b2971a3690a7325e92c0e86cda318dff3 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"bytes"
"io"
"log"
"sync"
)
const sinkLimit = 1 * 1024 * 1024 // 1 MiB limit on unread output.
// stdioSink will sink Write calls into a local buffer, and will provide only
// EOF for Read calls. This makes it suitable for running non-interactive
// programs and capturing their output. stdioSink implements io.ReadCloser and
// io.WriteCloser.
// Unlike bytes.Buffer, it is thread-safe and has a capped size after which
// writes will be silently ignored.
type stdioSink struct {
x sync.Mutex
b bytes.Buffer
}
func (s *stdioSink) Close() error { return nil }
func (s *stdioSink) Write(p []byte) (int, error) {
s.x.Lock()
defer s.x.Unlock()
if s.b.Len() >= sinkLimit {
log.Print("stdioSink is full, silently discarding extra data")
return len(p), nil
}
var n int
var err error
if s.b.Len()+len(p) > sinkLimit {
log.Print("stdioSink doesn't have enough spare capacity, silently truncating extra data")
n, err = s.b.Write(p[:sinkLimit-s.b.Len()])
if s.b.Len() == sinkLimit {
// Pretend that we didn't truncate our buffer.
n = len(p)
}
} else {
n, err = s.b.Write(p)
}
return n, err
}
func (s *stdioSink) Read(p []byte) (n int, err error) { return 0, io.EOF }
func (s *stdioSink) String() string {
s.x.Lock()
defer s.x.Unlock()
return s.b.String()
}
// ReadString returns new items since the last ReadString
// Non-blocking i.e. if nothing new will return "".
// Thread-safe i.e. concurrent Read will not return overlapping data.
func (s *stdioSink) ReadString() string {
s.x.Lock()
defer s.x.Unlock()
data := string(s.b.Next(sinkLimit))
s.b.Reset()
return data
}