diff options
Diffstat (limited to 'libgo/go/io')
-rw-r--r-- | libgo/go/io/io.go | 369 | ||||
-rw-r--r-- | libgo/go/io/io_test.go | 156 | ||||
-rw-r--r-- | libgo/go/io/ioutil/ioutil.go | 90 | ||||
-rw-r--r-- | libgo/go/io/ioutil/ioutil_test.go | 92 | ||||
-rw-r--r-- | libgo/go/io/ioutil/tempfile.go | 60 | ||||
-rw-r--r-- | libgo/go/io/ioutil/tempfile_test.go | 33 | ||||
-rw-r--r-- | libgo/go/io/multi.go | 60 | ||||
-rw-r--r-- | libgo/go/io/multi_test.go | 88 | ||||
-rw-r--r-- | libgo/go/io/pipe.go | 305 | ||||
-rw-r--r-- | libgo/go/io/pipe_test.go | 271 |
10 files changed, 1524 insertions, 0 deletions
diff --git a/libgo/go/io/io.go b/libgo/go/io/io.go new file mode 100644 index 000000000..1a6eca95a --- /dev/null +++ b/libgo/go/io/io.go @@ -0,0 +1,369 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This package provides basic interfaces to I/O primitives. +// Its primary job is to wrap existing implementations of such primitives, +// such as those in package os, into shared public interfaces that +// abstract the functionality, plus some other related primitives. +package io + +import "os" + +// Error represents an unexpected I/O behavior. +type Error struct { + os.ErrorString +} + +// ErrShortWrite means that a write accepted fewer bytes than requested +// but failed to return an explicit error. +var ErrShortWrite os.Error = &Error{"short write"} + +// ErrShortBuffer means that a read required a longer buffer than was provided. +var ErrShortBuffer os.Error = &Error{"short buffer"} + +// ErrUnexpectedEOF means that os.EOF was encountered in the +// middle of reading a fixed-size block or data structure. +var ErrUnexpectedEOF os.Error = &Error{"unexpected EOF"} + +// Reader is the interface that wraps the basic Read method. +// +// Read reads up to len(p) bytes into p. It returns the number of bytes +// read (0 <= n <= len(p)) and any error encountered. +// Even if Read returns n < len(p), +// it may use all of p as scratch space during the call. +// If some data is available but not len(p) bytes, Read conventionally +// returns what is available rather than block waiting for more. +// +// At the end of the input stream, Read returns 0, os.EOF. +// Read may return a non-zero number of bytes with a non-nil err. +// In particular, a Read that exhausts the input may return n > 0, os.EOF. +type Reader interface { + Read(p []byte) (n int, err os.Error) +} + +// Writer is the interface that wraps the basic Write method. +// +// Write writes len(p) bytes from p to the underlying data stream. +// It returns the number of bytes written from p (0 <= n <= len(p)) +// and any error encountered that caused the write to stop early. +// Write must return a non-nil error if it returns n < len(p). +type Writer interface { + Write(p []byte) (n int, err os.Error) +} + +// Closer is the interface that wraps the basic Close method. +type Closer interface { + Close() os.Error +} + +// Seeker is the interface that wraps the basic Seek method. +// +// Seek sets the offset for the next Read or Write to offset, +// interpreted according to whence: 0 means relative to the origin of +// the file, 1 means relative to the current offset, and 2 means +// relative to the end. Seek returns the new offset and an Error, if +// any. +type Seeker interface { + Seek(offset int64, whence int) (ret int64, err os.Error) +} + +// ReadWriter is the interface that groups the basic Read and Write methods. +type ReadWriter interface { + Reader + Writer +} + +// ReadCloser is the interface that groups the basic Read and Close methods. +type ReadCloser interface { + Reader + Closer +} + +// WriteCloser is the interface that groups the basic Write and Close methods. +type WriteCloser interface { + Writer + Closer +} + +// ReadWriteCloser is the interface that groups the basic Read, Write and Close methods. +type ReadWriteCloser interface { + Reader + Writer + Closer +} + +// ReadSeeker is the interface that groups the basic Read and Seek methods. +type ReadSeeker interface { + Reader + Seeker +} + +// WriteSeeker is the interface that groups the basic Write and Seek methods. +type WriteSeeker interface { + Writer + Seeker +} + +// ReadWriteSeeker is the interface that groups the basic Read, Write and Seek methods. +type ReadWriteSeeker interface { + Reader + Writer + Seeker +} + +// ReaderFrom is the interface that wraps the ReadFrom method. +type ReaderFrom interface { + ReadFrom(r Reader) (n int64, err os.Error) +} + +// WriterTo is the interface that wraps the WriteTo method. +type WriterTo interface { + WriteTo(w Writer) (n int64, err os.Error) +} + +// ReaderAt is the interface that wraps the basic ReadAt method. +// +// ReadAt reads len(p) bytes into p starting at offset off in the +// underlying data stream. It returns the number of bytes +// read (0 <= n <= len(p)) and any error encountered. +// +// Even if ReadAt returns n < len(p), +// it may use all of p as scratch space during the call. +// If some data is available but not len(p) bytes, ReadAt blocks +// until either all the data is available or an error occurs. +// +// At the end of the input stream, ReadAt returns 0, os.EOF. +// ReadAt may return a non-zero number of bytes with a non-nil err. +// In particular, a ReadAt that exhausts the input may return n > 0, os.EOF. +type ReaderAt interface { + ReadAt(p []byte, off int64) (n int, err os.Error) +} + +// WriterAt is the interface that wraps the basic WriteAt method. +// +// WriteAt writes len(p) bytes from p to the underlying data stream +// at offset off. It returns the number of bytes written from p (0 <= n <= len(p)) +// and any error encountered that caused the write to stop early. +// WriteAt must return a non-nil error if it returns n < len(p). +type WriterAt interface { + WriteAt(p []byte, off int64) (n int, err os.Error) +} + +// ReadByter is the interface that wraps the ReadByte method. +// +// ReadByte reads and returns the next byte from the input. +// If no byte is available, err will be set. +type ReadByter interface { + ReadByte() (c byte, err os.Error) +} + +// WriteString writes the contents of the string s to w, which accepts an array of bytes. +func WriteString(w Writer, s string) (n int, err os.Error) { + return w.Write([]byte(s)) +} + +// ReadAtLeast reads from r into buf until it has read at least min bytes. +// It returns the number of bytes copied and an error if fewer bytes were read. +// The error is os.EOF only if no bytes were read. +// If an EOF happens after reading fewer than min bytes, +// ReadAtLeast returns ErrUnexpectedEOF. +// If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer. +func ReadAtLeast(r Reader, buf []byte, min int) (n int, err os.Error) { + if len(buf) < min { + return 0, ErrShortBuffer + } + for n < min { + nn, e := r.Read(buf[n:]) + if nn > 0 { + n += nn + } + if e != nil { + if e == os.EOF && n > 0 { + e = ErrUnexpectedEOF + } + return n, e + } + } + return +} + +// ReadFull reads exactly len(buf) bytes from r into buf. +// It returns the number of bytes copied and an error if fewer bytes were read. +// The error is os.EOF only if no bytes were read. +// If an EOF happens after reading some but not all the bytes, +// ReadFull returns ErrUnexpectedEOF. +func ReadFull(r Reader, buf []byte) (n int, err os.Error) { + return ReadAtLeast(r, buf, len(buf)) +} + +// Copyn copies n bytes (or until an error) from src to dst. +// It returns the number of bytes copied and the error, if any. +// +// If dst implements the ReaderFrom interface, +// the copy is implemented by calling dst.ReadFrom(src). +func Copyn(dst Writer, src Reader, n int64) (written int64, err os.Error) { + // If the writer has a ReadFrom method, use it to do the copy. + // Avoids a buffer allocation and a copy. + if rt, ok := dst.(ReaderFrom); ok { + written, err = rt.ReadFrom(LimitReader(src, n)) + if written < n && err == nil { + // rt stopped early; must have been EOF. + err = os.EOF + } + return + } + buf := make([]byte, 32*1024) + for written < n { + l := len(buf) + if d := n - written; d < int64(l) { + l = int(d) + } + nr, er := src.Read(buf[0:l]) + if nr > 0 { + nw, ew := dst.Write(buf[0:nr]) + if nw > 0 { + written += int64(nw) + } + if ew != nil { + err = ew + break + } + if nr != nw { + err = ErrShortWrite + break + } + } + if er != nil { + err = er + break + } + } + return written, err +} + +// Copy copies from src to dst until either EOF is reached +// on src or an error occurs. It returns the number of bytes +// copied and the error, if any. +// +// If dst implements the ReaderFrom interface, +// the copy is implemented by calling dst.ReadFrom(src). +// Otherwise, if src implements the WriterTo interface, +// the copy is implemented by calling src.WriteTo(dst). +func Copy(dst Writer, src Reader) (written int64, err os.Error) { + // If the writer has a ReadFrom method, use it to do the copy. + // Avoids an allocation and a copy. + if rt, ok := dst.(ReaderFrom); ok { + return rt.ReadFrom(src) + } + // Similarly, if the reader has a WriteTo method, use it to do the copy. + if wt, ok := src.(WriterTo); ok { + return wt.WriteTo(dst) + } + buf := make([]byte, 32*1024) + for { + nr, er := src.Read(buf) + if nr > 0 { + nw, ew := dst.Write(buf[0:nr]) + if nw > 0 { + written += int64(nw) + } + if ew != nil { + err = ew + break + } + if nr != nw { + err = ErrShortWrite + break + } + } + if er == os.EOF { + break + } + if er != nil { + err = er + break + } + } + return written, err +} + +// LimitReader returns a Reader that reads from r +// but stops with os.EOF after n bytes. +func LimitReader(r Reader, n int64) Reader { return &limitedReader{r, n} } + +type limitedReader struct { + r Reader + n int64 +} + +func (l *limitedReader) Read(p []byte) (n int, err os.Error) { + if l.n <= 0 { + return 0, os.EOF + } + if int64(len(p)) > l.n { + p = p[0:l.n] + } + n, err = l.r.Read(p) + l.n -= int64(n) + return +} + +// NewSectionReader returns a SectionReader that reads from r +// starting at offset off and stops with os.EOF after n bytes. +func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader { + return &SectionReader{r, off, off, off + n} +} + +// SectionReader implements Read, Seek, and ReadAt on a section +// of an underlying ReaderAt. +type SectionReader struct { + r ReaderAt + base int64 + off int64 + limit int64 +} + +func (s *SectionReader) Read(p []byte) (n int, err os.Error) { + if s.off >= s.limit { + return 0, os.EOF + } + if max := s.limit - s.off; int64(len(p)) > max { + p = p[0:max] + } + n, err = s.r.ReadAt(p, s.off) + s.off += int64(n) + return +} + +func (s *SectionReader) Seek(offset int64, whence int) (ret int64, err os.Error) { + switch whence { + default: + return 0, os.EINVAL + case 0: + offset += s.base + case 1: + offset += s.off + case 2: + offset += s.limit + } + if offset < s.base || offset > s.limit { + return 0, os.EINVAL + } + s.off = offset + return offset - s.base, nil +} + +func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err os.Error) { + if off < 0 || off >= s.limit-s.base { + return 0, os.EOF + } + off += s.base + if max := s.limit - off; int64(len(p)) > max { + p = p[0:max] + } + return s.r.ReadAt(p, off) +} + +// Size returns the size of the section in bytes. +func (s *SectionReader) Size() int64 { return s.limit - s.base } diff --git a/libgo/go/io/io_test.go b/libgo/go/io/io_test.go new file mode 100644 index 000000000..4fcd85e69 --- /dev/null +++ b/libgo/go/io/io_test.go @@ -0,0 +1,156 @@ +// Copyright 2009 The Go 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 io_test + +import ( + "bytes" + . "io" + "os" + "strings" + "testing" +) + +// An version of bytes.Buffer without ReadFrom and WriteTo +type Buffer struct { + bytes.Buffer + ReaderFrom // conflicts with and hides bytes.Buffer's ReaderFrom. + WriterTo // conflicts with and hides bytes.Buffer's WriterTo. +} + +// Simple tests, primarily to verify the ReadFrom and WriteTo callouts inside Copy and Copyn. + +func TestCopy(t *testing.T) { + rb := new(Buffer) + wb := new(Buffer) + rb.WriteString("hello, world.") + Copy(wb, rb) + if wb.String() != "hello, world." { + t.Errorf("Copy did not work properly") + } +} + +func TestCopyReadFrom(t *testing.T) { + rb := new(Buffer) + wb := new(bytes.Buffer) // implements ReadFrom. + rb.WriteString("hello, world.") + Copy(wb, rb) + if wb.String() != "hello, world." { + t.Errorf("Copy did not work properly") + } +} + +func TestCopyWriteTo(t *testing.T) { + rb := new(bytes.Buffer) // implements WriteTo. + wb := new(Buffer) + rb.WriteString("hello, world.") + Copy(wb, rb) + if wb.String() != "hello, world." { + t.Errorf("Copy did not work properly") + } +} + +func TestCopyn(t *testing.T) { + rb := new(Buffer) + wb := new(Buffer) + rb.WriteString("hello, world.") + Copyn(wb, rb, 5) + if wb.String() != "hello" { + t.Errorf("Copyn did not work properly") + } +} + +func TestCopynReadFrom(t *testing.T) { + rb := new(Buffer) + wb := new(bytes.Buffer) // implements ReadFrom. + rb.WriteString("hello") + Copyn(wb, rb, 5) + if wb.String() != "hello" { + t.Errorf("Copyn did not work properly") + } +} + +func TestCopynWriteTo(t *testing.T) { + rb := new(bytes.Buffer) // implements WriteTo. + wb := new(Buffer) + rb.WriteString("hello, world.") + Copyn(wb, rb, 5) + if wb.String() != "hello" { + t.Errorf("Copyn did not work properly") + } +} + +type noReadFrom struct { + w Writer +} + +func (w *noReadFrom) Write(p []byte) (n int, err os.Error) { + return w.w.Write(p) +} + +func TestCopynEOF(t *testing.T) { + // Test that EOF behavior is the same regardless of whether + // argument to Copyn has ReadFrom. + + b := new(bytes.Buffer) + + n, err := Copyn(&noReadFrom{b}, strings.NewReader("foo"), 3) + if n != 3 || err != nil { + t.Errorf("Copyn(noReadFrom, foo, 3) = %d, %v; want 3, nil", n, err) + } + + n, err = Copyn(&noReadFrom{b}, strings.NewReader("foo"), 4) + if n != 3 || err != os.EOF { + t.Errorf("Copyn(noReadFrom, foo, 4) = %d, %v; want 3, EOF", n, err) + } + + n, err = Copyn(b, strings.NewReader("foo"), 3) // b has read from + if n != 3 || err != nil { + t.Errorf("Copyn(bytes.Buffer, foo, 3) = %d, %v; want 3, nil", n, err) + } + + n, err = Copyn(b, strings.NewReader("foo"), 4) // b has read from + if n != 3 || err != os.EOF { + t.Errorf("Copyn(bytes.Buffer, foo, 4) = %d, %v; want 3, EOF", n, err) + } +} + +func TestReadAtLeast(t *testing.T) { + var rb bytes.Buffer + rb.Write([]byte("0123")) + buf := make([]byte, 2) + n, err := ReadAtLeast(&rb, buf, 2) + if err != nil { + t.Error(err) + } + n, err = ReadAtLeast(&rb, buf, 4) + if err != ErrShortBuffer { + t.Errorf("expected ErrShortBuffer got %v", err) + } + if n != 0 { + t.Errorf("expected to have read 0 bytes, got %v", n) + } + n, err = ReadAtLeast(&rb, buf, 1) + if err != nil { + t.Error(err) + } + if n != 2 { + t.Errorf("expected to have read 2 bytes, got %v", n) + } + n, err = ReadAtLeast(&rb, buf, 2) + if err != os.EOF { + t.Errorf("expected EOF, got %v", err) + } + if n != 0 { + t.Errorf("expected to have read 0 bytes, got %v", n) + } + rb.Write([]byte("4")) + n, err = ReadAtLeast(&rb, buf, 2) + if err != ErrUnexpectedEOF { + t.Errorf("expected ErrUnexpectedEOF, got %v", err) + } + if n != 1 { + t.Errorf("expected to have read 1 bytes, got %v", n) + } +} diff --git a/libgo/go/io/ioutil/ioutil.go b/libgo/go/io/ioutil/ioutil.go new file mode 100644 index 000000000..fb3fdcda1 --- /dev/null +++ b/libgo/go/io/ioutil/ioutil.go @@ -0,0 +1,90 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Utility functions. + +package ioutil + +import ( + "bytes" + "io" + "os" + "sort" +) + +// ReadAll reads from r until an error or EOF and returns the data it read. +func ReadAll(r io.Reader) ([]byte, os.Error) { + var buf bytes.Buffer + _, err := io.Copy(&buf, r) + return buf.Bytes(), err +} + +// ReadFile reads the file named by filename and returns the contents. +func ReadFile(filename string) ([]byte, os.Error) { + f, err := os.Open(filename, os.O_RDONLY, 0) + if err != nil { + return nil, err + } + defer f.Close() + // It's a good but not certain bet that FileInfo will tell us exactly how much to + // read, so let's try it but be prepared for the answer to be wrong. + fi, err := f.Stat() + var n int64 + if err == nil && fi.Size < 2e9 { // Don't preallocate a huge buffer, just in case. + n = fi.Size + } + // Add a little extra in case Size is zero, and to avoid another allocation after + // Read has filled the buffer. + n += bytes.MinRead + // Pre-allocate the correct size of buffer, then set its size to zero. The + // Buffer will read into the allocated space cheaply. If the size was wrong, + // we'll either waste some space off the end or reallocate as needed, but + // in the overwhelmingly common case we'll get it just right. + buf := bytes.NewBuffer(make([]byte, 0, n)) + _, err = buf.ReadFrom(f) + return buf.Bytes(), err +} + +// WriteFile writes data to a file named by filename. +// If the file does not exist, WriteFile creates it with permissions perm; +// otherwise WriteFile truncates it before writing. +func WriteFile(filename string, data []byte, perm uint32) os.Error { + f, err := os.Open(filename, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, perm) + if err != nil { + return err + } + n, err := f.Write(data) + f.Close() + if err == nil && n < len(data) { + err = io.ErrShortWrite + } + return err +} + +// A dirList implements sort.Interface. +type fileInfoList []*os.FileInfo + +func (f fileInfoList) Len() int { return len(f) } +func (f fileInfoList) Less(i, j int) bool { return f[i].Name < f[j].Name } +func (f fileInfoList) Swap(i, j int) { f[i], f[j] = f[j], f[i] } + +// ReadDir reads the directory named by dirname and returns +// a list of sorted directory entries. +func ReadDir(dirname string) ([]*os.FileInfo, os.Error) { + f, err := os.Open(dirname, os.O_RDONLY, 0) + if err != nil { + return nil, err + } + list, err := f.Readdir(-1) + f.Close() + if err != nil { + return nil, err + } + fi := make(fileInfoList, len(list)) + for i := range list { + fi[i] = &list[i] + } + sort.Sort(fi) + return fi, nil +} diff --git a/libgo/go/io/ioutil/ioutil_test.go b/libgo/go/io/ioutil/ioutil_test.go new file mode 100644 index 000000000..150ee6d63 --- /dev/null +++ b/libgo/go/io/ioutil/ioutil_test.go @@ -0,0 +1,92 @@ +// Copyright 2009 The Go 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 ioutil_test + +import ( + . "io/ioutil" + "os" + "testing" +) + +func checkSize(t *testing.T, path string, size int64) { + dir, err := os.Stat(path) + if err != nil { + t.Fatalf("Stat %q (looking for size %d): %s", path, size, err) + } + if dir.Size != size { + t.Errorf("Stat %q: size %d want %d", path, dir.Size, size) + } +} + +func TestReadFile(t *testing.T) { + filename := "rumpelstilzchen" + contents, err := ReadFile(filename) + if err == nil { + t.Fatalf("ReadFile %s: error expected, none found", filename) + } + + filename = "ioutil_test.go" + contents, err = ReadFile(filename) + if err != nil { + t.Fatalf("ReadFile %s: %v", filename, err) + } + + checkSize(t, filename, int64(len(contents))) +} + +func TestWriteFile(t *testing.T) { + filename := "_test/rumpelstilzchen" + data := "Programming today is a race between software engineers striving to " + + "build bigger and better idiot-proof programs, and the Universe trying " + + "to produce bigger and better idiots. So far, the Universe is winning." + + if err := WriteFile(filename, []byte(data), 0644); err != nil { + t.Fatalf("WriteFile %s: %v", filename, err) + } + + contents, err := ReadFile(filename) + if err != nil { + t.Fatalf("ReadFile %s: %v", filename, err) + } + + if string(contents) != data { + t.Fatalf("contents = %q\nexpected = %q", string(contents), data) + } + + // cleanup + os.Remove(filename) // ignore error +} + + +func TestReadDir(t *testing.T) { + dirname := "rumpelstilzchen" + _, err := ReadDir(dirname) + if err == nil { + t.Fatalf("ReadDir %s: error expected, none found", dirname) + } + + dirname = "." + list, err := ReadDir(dirname) + if err != nil { + t.Fatalf("ReadDir %s: %v", dirname, err) + } + + foundTest := false + foundTestDir := false + for _, dir := range list { + switch { + case dir.IsRegular() && dir.Name == "ioutil_test.go": + foundTest = true + case dir.IsDirectory() && dir.Name == "_test": + foundTestDir = true + } + } + if !foundTest { + t.Fatalf("ReadDir %s: test file not found", dirname) + } + if !foundTestDir { + t.Fatalf("ReadDir %s: _test directory not found", dirname) + } +} diff --git a/libgo/go/io/ioutil/tempfile.go b/libgo/go/io/ioutil/tempfile.go new file mode 100644 index 000000000..114eca2b5 --- /dev/null +++ b/libgo/go/io/ioutil/tempfile.go @@ -0,0 +1,60 @@ +// Copyright 2010 The Go 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 ioutil + +import ( + "os" + "strconv" +) + +// Random number state, accessed without lock; racy but harmless. +// We generate random temporary file names so that there's a good +// chance the file doesn't exist yet - keeps the number of tries in +// TempFile to a minimum. +var rand uint32 + +func reseed() uint32 { + sec, nsec, _ := os.Time() + return uint32(sec*1e9 + nsec + int64(os.Getpid())) +} + +func nextSuffix() string { + r := rand + if r == 0 { + r = reseed() + } + r = r*1664525 + 1013904223 // constants from Numerical Recipes + rand = r + return strconv.Itoa(int(1e9 + r%1e9))[1:] +} + +// TempFile creates a new temporary file in the directory dir +// with a name beginning with prefix, opens the file for reading +// and writing, and returns the resulting *os.File. +// If dir is the empty string, TempFile uses the default directory +// for temporary files (see os.TempDir). +// Multiple programs calling TempFile simultaneously +// will not choose the same file. The caller can use f.Name() +// to find the name of the file. It is the caller's responsibility to +// remove the file when no longer needed. +func TempFile(dir, prefix string) (f *os.File, err os.Error) { + if dir == "" { + dir = os.TempDir() + } + + nconflict := 0 + for i := 0; i < 10000; i++ { + name := dir + "/" + prefix + nextSuffix() + f, err = os.Open(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) + if pe, ok := err.(*os.PathError); ok && pe.Error == os.EEXIST { + if nconflict++; nconflict > 10 { + rand = reseed() + } + continue + } + break + } + return +} diff --git a/libgo/go/io/ioutil/tempfile_test.go b/libgo/go/io/ioutil/tempfile_test.go new file mode 100644 index 000000000..d949a86cf --- /dev/null +++ b/libgo/go/io/ioutil/tempfile_test.go @@ -0,0 +1,33 @@ +// Copyright 2010 The Go 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 ioutil_test + +import ( + . "io/ioutil" + "os" + "regexp" + "testing" +) + +func TestTempFile(t *testing.T) { + f, err := TempFile("/_not_exists_", "foo") + if f != nil || err == nil { + t.Errorf("TempFile(`/_not_exists_`, `foo`) = %v, %v", f, err) + } + + dir := os.TempDir() + f, err = TempFile(dir, "ioutil_test") + if f == nil || err != nil { + t.Errorf("TempFile(dir, `ioutil_test`) = %v, %v", f, err) + } + if f != nil { + re := regexp.MustCompile("^" + regexp.QuoteMeta(dir) + "/ioutil_test[0-9]+$") + if !re.MatchString(f.Name()) { + t.Errorf("TempFile(`"+dir+"`, `ioutil_test`) created bad name %s", f.Name()) + } + os.Remove(f.Name()) + } + f.Close() +} diff --git a/libgo/go/io/multi.go b/libgo/go/io/multi.go new file mode 100644 index 000000000..88e4f1b76 --- /dev/null +++ b/libgo/go/io/multi.go @@ -0,0 +1,60 @@ +// Copyright 2010 The Go 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 io + +import "os" + +type multiReader struct { + readers []Reader +} + +func (mr *multiReader) Read(p []byte) (n int, err os.Error) { + for len(mr.readers) > 0 { + n, err = mr.readers[0].Read(p) + if n > 0 || err != os.EOF { + if err == os.EOF { + // This shouldn't happen. + // Well-behaved Readers should never + // return non-zero bytes read with an + // EOF. But if so, we clean it. + err = nil + } + return + } + mr.readers = mr.readers[1:] + } + return 0, os.EOF +} + +// MultiReader returns a Reader that's the logical concatenation of +// the provided input readers. They're read sequentially. Once all +// inputs are drained, Read will return os.EOF. +func MultiReader(readers ...Reader) Reader { + return &multiReader{readers} +} + +type multiWriter struct { + writers []Writer +} + +func (t *multiWriter) Write(p []byte) (n int, err os.Error) { + for _, w := range t.writers { + n, err = w.Write(p) + if err != nil { + return + } + if n != len(p) { + err = ErrShortWrite + return + } + } + return len(p), nil +} + +// MultiWriter creates a writer that duplicates its writes to all the +// provided writers, similar to the Unix tee(1) command. +func MultiWriter(writers ...Writer) Writer { + return &multiWriter{writers} +} diff --git a/libgo/go/io/multi_test.go b/libgo/go/io/multi_test.go new file mode 100644 index 000000000..3ecb7c75d --- /dev/null +++ b/libgo/go/io/multi_test.go @@ -0,0 +1,88 @@ +// Copyright 2010 The Go 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 io_test + +import ( + . "io" + "bytes" + "crypto/sha1" + "fmt" + "os" + "strings" + "testing" +) + +func TestMultiReader(t *testing.T) { + var mr Reader + var buf []byte + nread := 0 + withFooBar := func(tests func()) { + r1 := strings.NewReader("foo ") + r2 := strings.NewReader("bar") + mr = MultiReader(r1, r2) + buf = make([]byte, 20) + tests() + } + expectRead := func(size int, expected string, eerr os.Error) { + nread++ + n, gerr := mr.Read(buf[0:size]) + if n != len(expected) { + t.Errorf("#%d, expected %d bytes; got %d", + nread, len(expected), n) + } + got := string(buf[0:n]) + if got != expected { + t.Errorf("#%d, expected %q; got %q", + nread, expected, got) + } + if gerr != eerr { + t.Errorf("#%d, expected error %v; got %v", + nread, eerr, gerr) + } + buf = buf[n:] + } + withFooBar(func() { + expectRead(2, "fo", nil) + expectRead(5, "o ", nil) + expectRead(5, "bar", nil) + expectRead(5, "", os.EOF) + }) + withFooBar(func() { + expectRead(4, "foo ", nil) + expectRead(1, "b", nil) + expectRead(3, "ar", nil) + expectRead(1, "", os.EOF) + }) + withFooBar(func() { + expectRead(5, "foo ", nil) + }) +} + +func TestMultiWriter(t *testing.T) { + sha1 := sha1.New() + sink := new(bytes.Buffer) + mw := MultiWriter(sha1, sink) + + sourceString := "My input text." + source := strings.NewReader(sourceString) + written, err := Copy(mw, source) + + if written != int64(len(sourceString)) { + t.Errorf("short write of %d, not %d", written, len(sourceString)) + } + + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + sha1hex := fmt.Sprintf("%x", sha1.Sum()) + if sha1hex != "01cb303fa8c30a64123067c5aa6284ba7ec2d31b" { + t.Error("incorrect sha1 value") + } + + if sink.String() != sourceString { + t.Errorf("expected %q; got %q", sourceString, sink.String()) + } +} diff --git a/libgo/go/io/pipe.go b/libgo/go/io/pipe.go new file mode 100644 index 000000000..df76418b9 --- /dev/null +++ b/libgo/go/io/pipe.go @@ -0,0 +1,305 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Pipe adapter to connect code expecting an io.Reader +// with code expecting an io.Writer. + +package io + +import ( + "os" + "runtime" + "sync" +) + +type pipeResult struct { + n int + err os.Error +} + +// Shared pipe structure. +type pipe struct { + // Reader sends on cr1, receives on cr2. + // Writer does the same on cw1, cw2. + r1, w1 chan []byte + r2, w2 chan pipeResult + + rclose chan os.Error // read close; error to return to writers + wclose chan os.Error // write close; error to return to readers + + done chan int // read or write half is done +} + +func (p *pipe) run() { + var ( + rb []byte // pending Read + wb []byte // pending Write + wn int // amount written so far from wb + rerr os.Error // if read end is closed, error to send to writers + werr os.Error // if write end is closed, error to send to readers + r1 chan []byte // p.cr1 or nil depending on whether Read is ok + w1 chan []byte // p.cw1 or nil depending on whether Write is ok + ndone int + ) + + // Read and Write are enabled at the start. + r1 = p.r1 + w1 = p.w1 + + for { + select { + case <-p.done: + if ndone++; ndone == 2 { + // both reader and writer are gone + // close out any existing i/o + if r1 == nil { + p.r2 <- pipeResult{0, os.EINVAL} + } + if w1 == nil { + p.w2 <- pipeResult{0, os.EINVAL} + } + return + } + continue + case rerr = <-p.rclose: + if w1 == nil { + // finish pending Write + p.w2 <- pipeResult{wn, rerr} + wn = 0 + w1 = p.w1 // allow another Write + } + if r1 == nil { + // Close of read side during Read. + // finish pending Read with os.EINVAL. + p.r2 <- pipeResult{0, os.EINVAL} + r1 = p.r1 // allow another Read + } + continue + case werr = <-p.wclose: + if r1 == nil { + // finish pending Read + p.r2 <- pipeResult{0, werr} + r1 = p.r1 // allow another Read + } + if w1 == nil { + // Close of write side during Write. + // finish pending Write with os.EINVAL. + p.w2 <- pipeResult{wn, os.EINVAL} + wn = 0 + w1 = p.w1 // allow another Write + } + continue + case rb = <-r1: + if werr != nil { + // write end is closed + p.r2 <- pipeResult{0, werr} + continue + } + if rerr != nil { + // read end is closed + p.r2 <- pipeResult{0, os.EINVAL} + continue + } + r1 = nil // disable Read until this one is done + case wb = <-w1: + if rerr != nil { + // read end is closed + p.w2 <- pipeResult{0, rerr} + continue + } + if werr != nil { + // write end is closed + p.w2 <- pipeResult{0, os.EINVAL} + continue + } + w1 = nil // disable Write until this one is done + } + + if r1 == nil && w1 == nil { + // Have rb and wb. Execute. + n := copy(rb, wb) + wn += n + wb = wb[n:] + + // Finish Read. + p.r2 <- pipeResult{n, nil} + r1 = p.r1 // allow another Read + + // Maybe finish Write. + if len(wb) == 0 { + p.w2 <- pipeResult{wn, nil} + wn = 0 + w1 = p.w1 // allow another Write + } + } + } +} + +// Read/write halves of the pipe. +// They are separate structures for two reasons: +// 1. If one end becomes garbage without being Closed, +// its finalizer can Close so that the other end +// does not hang indefinitely. +// 2. Clients cannot use interface conversions on the +// read end to find the Write method, and vice versa. + +type pipeHalf struct { + c1 chan []byte + c2 chan pipeResult + cclose chan os.Error + done chan int + + lock sync.Mutex + closed bool + + io sync.Mutex + ioclosed bool +} + +func (p *pipeHalf) rw(data []byte) (n int, err os.Error) { + // Run i/o operation. + // Check ioclosed flag under lock to make sure we're still allowed to do i/o. + p.io.Lock() + if p.ioclosed { + p.io.Unlock() + return 0, os.EINVAL + } + p.io.Unlock() + p.c1 <- data + res := <-p.c2 + return res.n, res.err +} + +func (p *pipeHalf) close(err os.Error) os.Error { + // Close pipe half. + // Only first call to close does anything. + p.lock.Lock() + if p.closed { + p.lock.Unlock() + return os.EINVAL + } + p.closed = true + p.lock.Unlock() + + // First, send the close notification. + p.cclose <- err + + // Runner is now responding to rw operations + // with os.EINVAL. Cut off future rw operations + // by setting ioclosed flag. + p.io.Lock() + p.ioclosed = true + p.io.Unlock() + + // With ioclosed set, there will be no more rw operations + // working on the channels. + // Tell the runner we won't be bothering it anymore. + p.done <- 1 + + // Successfully torn down; can disable finalizer. + runtime.SetFinalizer(p, nil) + + return nil +} + +func (p *pipeHalf) finalizer() { + p.close(os.EINVAL) +} + + +// A PipeReader is the read half of a pipe. +type PipeReader struct { + pipeHalf +} + +// Read implements the standard Read interface: +// it reads data from the pipe, blocking until a writer +// arrives or the write end is closed. +// If the write end is closed with an error, that error is +// returned as err; otherwise err is nil. +func (r *PipeReader) Read(data []byte) (n int, err os.Error) { + return r.rw(data) +} + +// Close closes the reader; subsequent writes to the +// write half of the pipe will return the error os.EPIPE. +func (r *PipeReader) Close() os.Error { + return r.CloseWithError(nil) +} + +// CloseWithError closes the reader; subsequent writes +// to the write half of the pipe will return the error err. +func (r *PipeReader) CloseWithError(err os.Error) os.Error { + if err == nil { + err = os.EPIPE + } + return r.close(err) +} + +// A PipeWriter is the write half of a pipe. +type PipeWriter struct { + pipeHalf +} + +// Write implements the standard Write interface: +// it writes data to the pipe, blocking until readers +// have consumed all the data or the read end is closed. +// If the read end is closed with an error, that err is +// returned as err; otherwise err is os.EPIPE. +func (w *PipeWriter) Write(data []byte) (n int, err os.Error) { + return w.rw(data) +} + +// Close closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and os.EOF. +func (w *PipeWriter) Close() os.Error { + return w.CloseWithError(nil) +} + +// CloseWithError closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and the error err. +func (w *PipeWriter) CloseWithError(err os.Error) os.Error { + if err == nil { + err = os.EOF + } + return w.close(err) +} + +// Pipe creates a synchronous in-memory pipe. +// It can be used to connect code expecting an io.Reader +// with code expecting an io.Writer. +// Reads on one end are matched with writes on the other, +// copying data directly between the two; there is no internal buffering. +func Pipe() (*PipeReader, *PipeWriter) { + p := &pipe{ + r1: make(chan []byte), + r2: make(chan pipeResult), + w1: make(chan []byte), + w2: make(chan pipeResult), + rclose: make(chan os.Error), + wclose: make(chan os.Error), + done: make(chan int), + } + go p.run() + + // NOTE: Cannot use composite literal here: + // pipeHalf{c1: p.cr1, c2: p.cr2, cclose: p.crclose, cdone: p.cdone} + // because this implicitly copies the pipeHalf, which copies the inner mutex. + + r := new(PipeReader) + r.c1 = p.r1 + r.c2 = p.r2 + r.cclose = p.rclose + r.done = p.done + runtime.SetFinalizer(r, (*PipeReader).finalizer) + + w := new(PipeWriter) + w.c1 = p.w1 + w.c2 = p.w2 + w.cclose = p.wclose + w.done = p.done + runtime.SetFinalizer(w, (*PipeWriter).finalizer) + + return r, w +} diff --git a/libgo/go/io/pipe_test.go b/libgo/go/io/pipe_test.go new file mode 100644 index 000000000..bd4b94f0a --- /dev/null +++ b/libgo/go/io/pipe_test.go @@ -0,0 +1,271 @@ +// Copyright 2009 The Go 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 io_test + +import ( + "fmt" + . "io" + "os" + "testing" + "time" +) + +func checkWrite(t *testing.T, w Writer, data []byte, c chan int) { + n, err := w.Write(data) + if err != nil { + t.Errorf("write: %v", err) + } + if n != len(data) { + t.Errorf("short write: %d != %d", n, len(data)) + } + c <- 0 +} + +// Test a single read/write pair. +func TestPipe1(t *testing.T) { + c := make(chan int) + r, w := Pipe() + var buf = make([]byte, 64) + go checkWrite(t, w, []byte("hello, world"), c) + n, err := r.Read(buf) + if err != nil { + t.Errorf("read: %v", err) + } else if n != 12 || string(buf[0:12]) != "hello, world" { + t.Errorf("bad read: got %q", buf[0:n]) + } + <-c + r.Close() + w.Close() +} + +func reader(t *testing.T, r Reader, c chan int) { + var buf = make([]byte, 64) + for { + n, err := r.Read(buf) + if err == os.EOF { + c <- 0 + break + } + if err != nil { + t.Errorf("read: %v", err) + } + c <- n + } +} + +// Test a sequence of read/write pairs. +func TestPipe2(t *testing.T) { + c := make(chan int) + r, w := Pipe() + go reader(t, r, c) + var buf = make([]byte, 64) + for i := 0; i < 5; i++ { + p := buf[0 : 5+i*10] + n, err := w.Write(p) + if n != len(p) { + t.Errorf("wrote %d, got %d", len(p), n) + } + if err != nil { + t.Errorf("write: %v", err) + } + nn := <-c + if nn != n { + t.Errorf("wrote %d, read got %d", n, nn) + } + } + w.Close() + nn := <-c + if nn != 0 { + t.Errorf("final read got %d", nn) + } +} + +type pipeReturn struct { + n int + err os.Error +} + +// Test a large write that requires multiple reads to satisfy. +func writer(w WriteCloser, buf []byte, c chan pipeReturn) { + n, err := w.Write(buf) + w.Close() + c <- pipeReturn{n, err} +} + +func TestPipe3(t *testing.T) { + c := make(chan pipeReturn) + r, w := Pipe() + var wdat = make([]byte, 128) + for i := 0; i < len(wdat); i++ { + wdat[i] = byte(i) + } + go writer(w, wdat, c) + var rdat = make([]byte, 1024) + tot := 0 + for n := 1; n <= 256; n *= 2 { + nn, err := r.Read(rdat[tot : tot+n]) + if err != nil && err != os.EOF { + t.Fatalf("read: %v", err) + } + + // only final two reads should be short - 1 byte, then 0 + expect := n + if n == 128 { + expect = 1 + } else if n == 256 { + expect = 0 + if err != os.EOF { + t.Fatalf("read at end: %v", err) + } + } + if nn != expect { + t.Fatalf("read %d, expected %d, got %d", n, expect, nn) + } + tot += nn + } + pr := <-c + if pr.n != 128 || pr.err != nil { + t.Fatalf("write 128: %d, %v", pr.n, pr.err) + } + if tot != 128 { + t.Fatalf("total read %d != 128", tot) + } + for i := 0; i < 128; i++ { + if rdat[i] != byte(i) { + t.Fatalf("rdat[%d] = %d", i, rdat[i]) + } + } +} + +// Test read after/before writer close. + +type closer interface { + CloseWithError(os.Error) os.Error + Close() os.Error +} + +type pipeTest struct { + async bool + err os.Error + closeWithError bool +} + +func (p pipeTest) String() string { + return fmt.Sprintf("async=%v err=%v closeWithError=%v", p.async, p.err, p.closeWithError) +} + +var pipeTests = []pipeTest{ + {true, nil, false}, + {true, nil, true}, + {true, ErrShortWrite, true}, + {false, nil, false}, + {false, nil, true}, + {false, ErrShortWrite, true}, +} + +func delayClose(t *testing.T, cl closer, ch chan int, tt pipeTest) { + time.Sleep(1e6) // 1 ms + var err os.Error + if tt.closeWithError { + err = cl.CloseWithError(tt.err) + } else { + err = cl.Close() + } + if err != nil { + t.Errorf("delayClose: %v", err) + } + ch <- 0 +} + +func TestPipeReadClose(t *testing.T) { + for _, tt := range pipeTests { + c := make(chan int, 1) + r, w := Pipe() + if tt.async { + go delayClose(t, w, c, tt) + } else { + delayClose(t, w, c, tt) + } + var buf = make([]byte, 64) + n, err := r.Read(buf) + <-c + want := tt.err + if want == nil { + want = os.EOF + } + if err != want { + t.Errorf("read from closed pipe: %v want %v", err, want) + } + if n != 0 { + t.Errorf("read on closed pipe returned %d", n) + } + if err = r.Close(); err != nil { + t.Errorf("r.Close: %v", err) + } + } +} + +// Test close on Read side during Read. +func TestPipeReadClose2(t *testing.T) { + c := make(chan int, 1) + r, _ := Pipe() + go delayClose(t, r, c, pipeTest{}) + n, err := r.Read(make([]byte, 64)) + <-c + if n != 0 || err != os.EINVAL { + t.Errorf("read from closed pipe: %v, %v want %v, %v", n, err, 0, os.EINVAL) + } +} + +// Test write after/before reader close. + +func TestPipeWriteClose(t *testing.T) { + for _, tt := range pipeTests { + c := make(chan int, 1) + r, w := Pipe() + if tt.async { + go delayClose(t, r, c, tt) + } else { + delayClose(t, r, c, tt) + } + n, err := WriteString(w, "hello, world") + <-c + expect := tt.err + if expect == nil { + expect = os.EPIPE + } + if err != expect { + t.Errorf("write on closed pipe: %v want %v", err, expect) + } + if n != 0 { + t.Errorf("write on closed pipe returned %d", n) + } + if err = w.Close(); err != nil { + t.Errorf("w.Close: %v", err) + } + } +} + +func TestWriteEmpty(t *testing.T) { + r, w := Pipe() + go func() { + w.Write([]byte{}) + w.Close() + }() + var b [2]byte + ReadFull(r, b[0:2]) + r.Close() +} + +func TestWriteNil(t *testing.T) { + r, w := Pipe() + go func() { + w.Write(nil) + w.Close() + }() + var b [2]byte + ReadFull(r, b[0:2]) + r.Close() +} |