From 324e6dabf0eb51240a3006c68ce2544794f305c5 Mon Sep 17 00:00:00 2001 From: Benny Siegert Date: Tue, 21 Feb 2012 11:25:19 +1100 Subject: [PATCH] go.image/tiff: optimize reading uncompressed files from a tiff.buffer. In case the image is read via a tiff.buffer, avoid copying the data strip before decoding it. Remove corresponding TODO. Speeds up reading uncompressed images (which is the common case) and uses much less memory. benchmark old ns/op new ns/op delta BenchmarkDecodeCompressed 4619438 4630774 +0.25% BenchmarkDecodeUncompressed 260809 219875 -15.70% R=nigeltao CC=golang-dev https://golang.org/cl/5683050 --- tiff/buffer.go | 33 ++++++++++++++++++++++++--------- tiff/reader.go | 9 ++++++--- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/tiff/buffer.go b/tiff/buffer.go index 27533c6..d1801be 100644 --- a/tiff/buffer.go +++ b/tiff/buffer.go @@ -12,13 +12,8 @@ type buffer struct { buf []byte } -func (b *buffer) ReadAt(p []byte, off int64) (int, error) { - o := int(off) - end := o + len(p) - if int64(end) != off+int64(len(p)) { - return 0, io.ErrUnexpectedEOF - } - +// fill reads data from b.r until the buffer contains at least end bytes. +func (b *buffer) fill(end int) error { m := len(b.buf) if end > m { if end > cap(b.buf) { @@ -35,11 +30,31 @@ func (b *buffer) ReadAt(p []byte, off int64) (int, error) { if n, err := io.ReadFull(b.r, b.buf[m:end]); err != nil { end = m + n b.buf = b.buf[:end] - return copy(p, b.buf[o:end]), err + return err } } + return nil +} - return copy(p, b.buf[o:end]), nil +func (b *buffer) ReadAt(p []byte, off int64) (int, error) { + o := int(off) + end := o + len(p) + if int64(end) != off+int64(len(p)) { + return 0, io.ErrUnexpectedEOF + } + + err := b.fill(end) + return copy(p, b.buf[o:end]), err +} + +// Slice returns a slice of the underlying buffer. The slice contains +// n bytes starting at offset off. +func (b *buffer) Slice(off, n int) ([]byte, error) { + end := off + n + if err := b.fill(end); err != nil { + return nil, err + } + return b.buf[off:end], nil } // newReaderAt converts an io.Reader into an io.ReaderAt. diff --git a/tiff/reader.go b/tiff/reader.go index dc5a87a..0603b1e 100644 --- a/tiff/reader.go +++ b/tiff/reader.go @@ -397,9 +397,12 @@ func Decode(r io.Reader) (img image.Image, err error) { n := int64(d.features[tStripByteCounts][i]) switch d.firstVal(tCompression) { case cNone: - // TODO(bsiegert): Avoid copy if r is a tiff.buffer. - d.buf = make([]byte, n) - _, err = d.r.ReadAt(d.buf, offset) + if b, ok := d.r.(*buffer); ok { + d.buf, err = b.Slice(int(offset), int(n)) + } else { + d.buf = make([]byte, n) + _, err = d.r.ReadAt(d.buf, offset) + } case cLZW: r := lzw.NewReader(io.NewSectionReader(d.r, offset, n), lzw.MSB, 8) d.buf, err = ioutil.ReadAll(r)