github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/internal/traceparser/filebuf/filebuf.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package filebuf implements io.SeekReader for os files. 6 // This is useful only for very large files with lots of 7 // seeking. (otherwise use ioutil.ReadFile or bufio) 8 package filebuf 9 10 import ( 11 "fmt" 12 "io" 13 "os" 14 ) 15 16 // Buf is the implemented interface 17 type Buf interface { 18 io.ReadCloser 19 io.Seeker 20 Size() int64 21 Stats() Stat 22 } 23 24 // Buflen is the size of the internal buffer. 25 // The code is designed to never need to reread unnecessarily 26 const Buflen = 1 << 20 27 28 // fbuf is a buffered file with seeking. 29 // fixed is an internal buffer. buf is the slice fixed[:fixedLen]. bufloc is the file 30 // location of the beginning of fixed (and buf). The seek pointer is at bufloc+bufpos, 31 // so the file's contents there start with buf[bufpos:] 32 type fbuf struct { 33 Name string 34 fd *os.File 35 size int64 // file size 36 bufloc int64 // file loc of beginning of fixed 37 bufpos int32 // seekptr is at bufloc+bufpos. bufpos <= Buflen, fixedLen 38 fixed [Buflen]byte // backing store for buf 39 fixedlen int // how much of fixed is valid file contents 40 buf []byte // buf is fixed[0:fixedlen] 41 // statistics 42 seeks int // number of calls to fd.Seek 43 reads int // number of calls to fd.Read 44 bytes int64 // number of bytes read by fd.Read 45 } 46 47 // Stat returns the number of underlying seeks and reads, and bytes read 48 type Stat struct { 49 Seeks int 50 Reads int 51 Bytes int64 52 } 53 54 // Stats returns the stats so far 55 func (fb *fbuf) Stats() Stat { 56 return Stat{fb.seeks, fb.reads, fb.bytes} 57 } 58 59 // Size returns the file size 60 func (fb *fbuf) Size() int64 { 61 return fb.size 62 } 63 64 // New returns an initialized *fbuf or an error 65 func New(fname string) (Buf, error) { 66 fd, err := os.Open(fname) 67 if err != nil { 68 return nil, err 69 } 70 fi, err := fd.Stat() 71 if err != nil || fi.Mode().IsDir() { 72 return nil, fmt.Errorf("not readable: %s", fname) 73 } 74 return &fbuf{Name: fname, fd: fd, size: fi.Size()}, nil 75 } 76 77 // Read implements io.Reader. It may return a positive 78 // number of bytes read with io.EOF 79 func (fb *fbuf) Read(p []byte) (int, error) { 80 // If there are enough valid bytes remaining in buf, just use them 81 if len(fb.buf[fb.bufpos:]) >= len(p) { 82 copy(p, fb.buf[fb.bufpos:]) 83 fb.bufpos += int32(len(p)) 84 return len(p), nil 85 } 86 done := 0 // done counts how many bytes have been transferred 87 // If there are any valid bytes left in buf, use them first 88 if len(fb.buf[fb.bufpos:]) > 0 { 89 m := copy(p, fb.buf[fb.bufpos:]) 90 done = m 91 fb.bufpos += int32(done) // at end of the valid bytes in buf 92 } 93 // used up buffered data. logical seek pointer is at bufloc+bufpos. 94 // loop until p has been filled up or EOF 95 for done < len(p) { 96 loc, err := fb.fd.Seek(0, io.SeekCurrent) // make sure of the os's file location 97 if loc != fb.bufloc+int64(fb.bufpos) { 98 panic(fmt.Sprintf("%v loc=%d bufloc=%d bufpos=%d", err, loc, 99 fb.bufloc, fb.bufpos)) 100 } 101 fb.seeks++ // did a file system seek 102 if loc >= fb.size { 103 // at EOF 104 fb.bufpos = int32(len(fb.buf)) 105 fb.bufloc = loc - int64(fb.fixedlen) 106 return done, io.EOF 107 } 108 n, err := fb.fd.Read(fb.fixed[:]) 109 if n != 0 { 110 fb.fixedlen = n 111 } 112 fb.reads++ // did a file system read 113 m := copy(p[done:], fb.fixed[:n]) 114 done += m 115 if err != nil { 116 if err == io.EOF { 117 fb.bufpos = int32(len(fb.buf)) 118 fb.bufloc = loc - int64(fb.fixedlen) 119 return done, io.EOF 120 } 121 return 0, err 122 } 123 fb.bytes += int64(n) 124 fb.bufpos = int32(m) // used m byes of the buffer 125 fb.bufloc = loc 126 fb.buf = fb.fixed[:n] 127 } 128 return len(p), nil 129 } 130 131 // Seek implements io.Seeker. (<unchanged>, io.EOF) is returned for seeks off the end. 132 func (fb *fbuf) Seek(offset int64, whence int) (int64, error) { 133 seekpos := offset 134 switch whence { 135 case io.SeekCurrent: 136 seekpos += fb.bufloc + int64(fb.bufpos) 137 case io.SeekEnd: 138 seekpos += fb.size 139 } 140 if seekpos < 0 || seekpos > fb.size { 141 return fb.bufloc + int64(fb.bufpos), io.EOF 142 } 143 // if seekpos is inside fixed, just adjust buf and bufpos 144 if seekpos >= fb.bufloc && seekpos <= int64(fb.fixedlen)+fb.bufloc { 145 fb.bufpos = int32(seekpos - fb.bufloc) 146 return seekpos, nil 147 } 148 // need to refresh the internal buffer. Seek does no reading, mark buf 149 // as empty, set bufpos and bufloc. 150 fb.buf, fb.bufpos, fb.bufloc = nil, 0, seekpos 151 n, err := fb.fd.Seek(seekpos, io.SeekStart) 152 fb.seeks++ 153 if n != seekpos || err != nil { 154 return -1, fmt.Errorf("seek failed (%d!= %d) %v", n, seekpos, err) 155 } 156 return seekpos, nil 157 } 158 159 // Close closes the underlying file 160 func (fb *fbuf) Close() error { 161 if fb.fd != nil { 162 return fb.fd.Close() 163 } 164 return nil 165 }