fork of https://github.com/sourcegraph/zoekt
1// Copyright 2016 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package zoekt
16
17import (
18 "fmt"
19 "log"
20 "os"
21 "runtime"
22
23 // cross-platform memory-mapped file package.
24 // Benchmarks the same speed as syscall/unix Mmap
25 // see https://github.com/peterguy/benchmark-mmap
26 mmap "github.com/edsrzf/mmap-go"
27)
28
29type mmapedIndexFile struct {
30 name string
31 size uint32
32 data mmap.MMap
33}
34
35func (f *mmapedIndexFile) Read(off, sz uint32) ([]byte, error) {
36 if off > off+sz || off+sz > uint32(len(f.data)) {
37 return nil, fmt.Errorf("out of bounds: %d, len %d, name %s", off+sz, len(f.data), f.name)
38 }
39 return f.data[off : off+sz], nil
40}
41
42func (f *mmapedIndexFile) Name() string {
43 return f.name
44}
45
46func (f *mmapedIndexFile) Size() (uint32, error) {
47 return f.size, nil
48}
49
50func (f *mmapedIndexFile) Close() {
51 if err := f.data.Unmap(); err != nil {
52 log.Printf("WARN failed to memory unmap %s: %v", f.name, err)
53 }
54}
55
56func bufferSize(f *mmapedIndexFile) int {
57 // On Unix/Linux, mmap likes to allocate memory in
58 // page-sized chunks, so round up to the OS page size.
59 // mmap will zero-fill the extra bytes.
60 // On Windows, the Windows API CreateFileMapping method
61 // requires a buffer the same size as the file.
62 bsize := int(f.size)
63 if runtime.GOOS != "windows" {
64 pagesize := os.Getpagesize() - 1
65 bsize = (bsize + pagesize) &^ pagesize
66 }
67 return bsize
68}
69
70// NewIndexFile returns a new index file. The index file takes
71// ownership of the passed in file, and may close it.
72func NewIndexFile(f *os.File) (IndexFile, error) {
73 defer f.Close()
74
75 fi, err := f.Stat()
76 if err != nil {
77 return nil, err
78 }
79
80 sz := fi.Size()
81 if sz >= maxUInt32 {
82 return nil, fmt.Errorf("file %s too large: %d", f.Name(), sz)
83 }
84 r := &mmapedIndexFile{
85 name: f.Name(),
86 size: uint32(sz),
87 }
88
89 r.data, err = mmap.MapRegion(f, bufferSize(r), mmap.RDONLY, 0, 0)
90 if err != nil {
91 return nil, fmt.Errorf("NewIndexFile: unable to memory map %s: %w", f.Name(), err)
92 }
93
94 return r, nil
95}