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  }