github.com/jbendotnet/noms@v0.0.0-20190904222105-c43e4293ea92/go/nbs/mmap_table_reader.go (about) 1 // Copyright 2016 Attic Labs, Inc. All rights reserved. 2 // Licensed under the Apache License, version 2.0: 3 // http://www.apache.org/licenses/LICENSE-2.0 4 5 package nbs 6 7 import ( 8 "io" 9 "math" 10 "os" 11 "path/filepath" 12 "strconv" 13 "time" 14 15 "golang.org/x/sys/unix" 16 17 "github.com/attic-labs/noms/go/d" 18 ) 19 20 type mmapTableReader struct { 21 tableReader 22 fc *fdCache 23 h addr 24 } 25 26 const ( 27 fileBlockSize = 1 << 12 28 ) 29 30 var ( 31 pageSize = int64(os.Getpagesize()) 32 maxInt = int64(math.MaxInt64) 33 ) 34 35 func init() { 36 if strconv.IntSize == 32 { 37 maxInt = math.MaxInt32 38 } 39 } 40 41 func newMmapTableReader(dir string, h addr, chunkCount uint32, indexCache *indexCache, fc *fdCache) chunkSource { 42 path := filepath.Join(dir, h.String()) 43 44 var index tableIndex 45 found := false 46 if indexCache != nil { 47 indexCache.lockEntry(h) 48 defer indexCache.unlockEntry(h) 49 index, found = indexCache.get(h) 50 } 51 52 if !found { 53 f, err := fc.RefFile(path) 54 d.PanicIfError(err) 55 defer fc.UnrefFile(path) 56 57 fi, err := f.Stat() 58 d.PanicIfError(err) 59 d.PanicIfTrue(fi.Size() < 0) 60 // index. Mmap won't take an offset that's not page-aligned, so find the nearest page boundary preceding the index. 61 indexOffset := fi.Size() - int64(footerSize) - int64(indexSize(chunkCount)) 62 aligned := indexOffset / pageSize * pageSize // Thanks, integer arithmetic! 63 d.PanicIfTrue(fi.Size()-aligned > maxInt) 64 buff, err := unix.Mmap(int(f.Fd()), aligned, int(fi.Size()-aligned), unix.PROT_READ, unix.MAP_SHARED) 65 d.PanicIfError(err) 66 index = parseTableIndex(buff[indexOffset-aligned:]) 67 68 if indexCache != nil { 69 indexCache.put(h, index) 70 } 71 err = unix.Munmap(buff) 72 d.PanicIfError(err) 73 } 74 75 d.PanicIfFalse(chunkCount == index.chunkCount) 76 return &mmapTableReader{ 77 newTableReader(index, &cacheReaderAt{path, fc}, fileBlockSize), 78 fc, 79 h, 80 } 81 } 82 83 func (mmtr *mmapTableReader) hash() addr { 84 return mmtr.h 85 } 86 87 type cacheReaderAt struct { 88 path string 89 fc *fdCache 90 } 91 92 func (cra *cacheReaderAt) ReadAtWithStats(p []byte, off int64, stats *Stats) (n int, err error) { 93 var r io.ReaderAt 94 t1 := time.Now() 95 96 if r, err = cra.fc.RefFile(cra.path); err != nil { 97 return 98 } 99 defer func() { 100 stats.FileBytesPerRead.Sample(uint64(len(p))) 101 stats.FileReadLatency.SampleTimeSince(t1) 102 }() 103 104 defer cra.fc.UnrefFile(cra.path) 105 106 return r.ReadAt(p, off) 107 }