toolbox/mmap/mmap.go
2021-03-31 10:33:16 +02:00

96 lines
1.7 KiB
Go

package mt
import (
"fmt"
"os"
"reflect"
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
// Map creates a memory map of the given file.
func Map(
prot int, // Memory protection bitmask. See syscall.PROT_* flags.
flags int, // See syscall.MAP_* flags. MAP_SHARED is usually fine.
length int64,
f *os.File,
) (
[]byte,
error,
) {
addr, _, errno := syscall.Syscall6(
syscall.SYS_MMAP,
0, // addr: 0 => allow kernel to choose
uintptr(length),
uintptr(prot),
uintptr(flags),
f.Fd(),
0) // offset: 0 => start of file
if errno != 0 {
return nil, syscall.Errno(errno)
}
var b []byte
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b))
hdr.Data = addr
hdr.Cap = int(length)
hdr.Len = 0
return b, nil
}
// Unmap unmaps the data obtained by Map.
func Unmap(data []byte) error {
_, _, errno := syscall.Syscall(
syscall.SYS_MUNMAP,
uintptr(unsafe.Pointer(&data[:1][0])),
uintptr(cap(data)),
0)
if errno != 0 {
return syscall.Errno(errno)
}
return nil
}
// Remap truncates the file to `newSize` and returns a new memory map for the
// file.
func Remap(
f *os.File,
data []byte,
newSize int64,
) ([]byte, error) {
const mRemapMayMove = 0x1
if err := f.Truncate(newSize); err != nil {
return data, err
}
addr, size, errno := unix.Syscall6(
unix.SYS_MREMAP,
uintptr(unsafe.Pointer(&data[:1][0])),
uintptr(cap(data)),
uintptr(newSize),
uintptr(mRemapMayMove),
0,
0)
if errno != 0 {
return data, syscall.Errno(errno)
}
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&data))
hdr.Data = addr
hdr.Cap = int(newSize)
hdr.Len = int(newSize)
if int64(size) != newSize {
panic(fmt.Sprintf("mremap returned incorrect size %d != %d",
size, newSize))
}
return data, nil
}