File: mmapfile.go

package info (click to toggle)
golang-golang-x-tools 1%3A0.0~git20190125.d66bd3c%2Bds-4
  • links: PTS, VCS
  • area: main
  • in suites: buster, buster-backports
  • size: 8,912 kB
  • sloc: asm: 1,394; yacc: 155; makefile: 109; sh: 108; ansic: 17; xml: 11
file content (145 lines) | stat: -rw-r--r-- 3,124 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin linux

package core

import (
	"errors"
	"fmt"
	"io"
	"os"
	"syscall"
)

var errMmapClosed = errors.New("mmap: closed")

// mmapFile wraps a memory-mapped file.
type mmapFile struct {
	data     []byte
	pos      uint64
	writable bool
}

// mmapOpen opens the named file for reading.
// If writable is true, the file is also open for writing.
func mmapOpen(filename string, writable bool) (*mmapFile, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	st, err := f.Stat()
	if err != nil {
		return nil, err
	}

	size := st.Size()
	if size == 0 {
		return &mmapFile{data: []byte{}}, nil
	}
	if size < 0 {
		return nil, fmt.Errorf("mmap: file %q has negative size: %d", filename, size)
	}
	if size != int64(int(size)) {
		return nil, fmt.Errorf("mmap: file %q is too large", filename)
	}

	prot := syscall.PROT_READ
	if writable {
		prot |= syscall.PROT_WRITE
	}
	data, err := syscall.Mmap(int(f.Fd()), 0, int(size), prot, syscall.MAP_SHARED)
	if err != nil {
		return nil, err
	}
	return &mmapFile{data: data, writable: writable}, nil
}

// Size returns the size of the mapped file.
func (f *mmapFile) Size() uint64 {
	return uint64(len(f.data))
}

// Pos returns the current file pointer.
func (f *mmapFile) Pos() uint64 {
	return f.pos
}

// SeekTo sets the current file pointer relative to the start of the file.
func (f *mmapFile) SeekTo(offset uint64) {
	f.pos = offset
}

// Read implements io.Reader.
func (f *mmapFile) Read(p []byte) (int, error) {
	if f.data == nil {
		return 0, errMmapClosed
	}
	if f.pos >= f.Size() {
		return 0, io.EOF
	}
	n := copy(p, f.data[f.pos:])
	f.pos += uint64(n)
	if n < len(p) {
		return n, io.EOF
	}
	return n, nil
}

// ReadByte implements io.ByteReader.
func (f *mmapFile) ReadByte() (byte, error) {
	if f.data == nil {
		return 0, errMmapClosed
	}
	if f.pos >= f.Size() {
		return 0, io.EOF
	}
	b := f.data[f.pos]
	f.pos++
	return b, nil
}

// ReadSlice returns a slice of size n that points directly at the
// underlying mapped file. There is no copying. Fails if it cannot
// read at least n bytes.
func (f *mmapFile) ReadSlice(n uint64) ([]byte, error) {
	if f.data == nil {
		return nil, errMmapClosed
	}
	if f.pos+n >= f.Size() {
		return nil, io.EOF
	}
	first := f.pos
	f.pos += n
	return f.data[first:f.pos:f.pos], nil
}

// ReadSliceAt is like ReadSlice, but reads from a specific offset.
// The file pointer is not used or advanced.
func (f *mmapFile) ReadSliceAt(offset, n uint64) ([]byte, error) {
	if f.data == nil {
		return nil, errMmapClosed
	}
	if f.Size() < offset {
		return nil, fmt.Errorf("mmap: out-of-bounds ReadSliceAt offset %d, size is %d", offset, f.Size())
	}
	if offset+n >= f.Size() {
		return nil, io.EOF
	}
	end := offset + n
	return f.data[offset:end:end], nil
}

// Close closes the file.
func (f *mmapFile) Close() error {
	if f.data == nil {
		return nil
	}
	err := syscall.Munmap(f.data)
	f.data = nil
	f.pos = 0
	return err
}