Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functions to read and write string #18

Merged
merged 1 commit into from Mar 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -10,3 +10,6 @@

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# IDE files
.idea/*
4 changes: 3 additions & 1 deletion .travis.yml
Expand Up @@ -2,12 +2,14 @@ language: go

go:
- 1.12
- 1.11

os:
- osx
- linux

env:
- GO111MODULE=on

script:
- go test -race -coverprofile=coverage.txt -covermode=atomic

Expand Down
17 changes: 0 additions & 17 deletions immap.go

This file was deleted.

29 changes: 24 additions & 5 deletions mmap.go
Expand Up @@ -2,7 +2,9 @@ package mmap

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

Expand All @@ -13,8 +15,25 @@ var (
ErrIndexOutOfBound = errors.New("offset out of mapped region")
)

// Mmap provides abstraction around a memory mapped file
type Mmap struct {
// File provides an interface to a memory mapped file
type File interface {
io.ReaderAt
io.WriterAt

ReadStringAt(dest *strings.Builder, offset int64) int
WriteStringAt(src string, offset int64) int
ReadUint64At(offset int64) uint64
WriteUint64At(num uint64, offset int64)

Lock() error
Unlock() error
Advise(advice int) error
Flush(flags int) error
Unmap() error
}

// mmapFile provides abstraction around a memory mapped file
type mmapFile struct {
data []byte
length int64
}
Expand All @@ -26,21 +45,21 @@ type Mmap struct {
// then all the mapped memory is accessible
// case 2 => if file size <= memory region (offset + length)
// then from offset to file size memory region is accessible
func NewSharedFileMmap(f *os.File, offset int64, length int, prot int) (IMmap, error) {
func NewSharedFileMmap(f *os.File, offset int64, length int, prot int) (File, error) {
data, err := syscall.Mmap(int(f.Fd()), offset, length, prot, syscall.MAP_SHARED)
if err != nil {
return nil, err
}

return &Mmap{
return &mmapFile{
data: data,
length: int64(length),
}, nil
}

// Unmap unmaps the memory mapped file. An error will be returned
// if any of the functions are called on Mmap after calling Unmap
func (m *Mmap) Unmap() error {
func (m *mmapFile) Unmap() error {
err := syscall.Munmap(m.data)
m.data = nil
return err
Expand Down
84 changes: 49 additions & 35 deletions mmap_data.go
Expand Up @@ -2,72 +2,86 @@ package mmap

import (
"encoding/binary"
"strings"
"syscall"
"unsafe"
)

// ReadAt copies data to dest slice from mapped region starting at
// given offset and returns number of bytes copied to the dest slice.
// There are two possibilities -
// Case 1: len(dest) >= (len(m.data) - offset)
// => copies (len(m.data) - offset) bytes to dest from mapped region
// Case 2: len(dest) < (len(m.data) - offset)
// => copies len(dest) bytes to dest from mapped region
// err is always nil, hence, can be ignored
func (m *Mmap) ReadAt(dest []byte, offset int64) (int, error) {
func (m *mmapFile) boundaryChecks(offset, numBytes int64) {
if m.data == nil {
panic(ErrUnmappedMemory)
} else if offset >= m.length || offset < 0 {
} else if offset+numBytes > m.length || offset < 0 {
panic(ErrIndexOutOfBound)
}
}

// ReadAt copies data to dest slice from mapped region starting at
// given offset and returns number of bytes copied to the dest slice.
// There are two possibilities -
// Case 1: len(dest) >= (m.length - offset)
// => copies (m.length - offset) bytes to dest from mapped region
// Case 2: len(dest) < (m.length - offset)
// => copies len(dest) bytes to dest from mapped region
// err is always nil, hence, can be ignored
func (m *mmapFile) ReadAt(dest []byte, offset int64) (int, error) {
m.boundaryChecks(offset, 1)
return copy(dest, m.data[offset:]), nil
}

// WriteAt copies data to mapped region from the src slice starting at
// given offset and returns number of bytes copied to the mapped region.
// There are two possibilities -
// Case 1: len(src) >= (len(m.data) - offset)
// => copies (len(m.data) - offset) bytes to the mapped region from src
// Case 2: len(src) < (len(m.data) - offset)
// Case 1: len(src) >= (m.length - offset)
// => copies (m.length - offset) bytes to the mapped region from src
// Case 2: len(src) < (m.length - offset)
// => copies len(src) bytes to the mapped region from src
// err is always nil, hence, can be ignored
func (m *Mmap) WriteAt(src []byte, offset int64) (int, error) {
if m.data == nil {
panic(ErrUnmappedMemory)
} else if offset >= m.length || offset < 0 {
panic(ErrIndexOutOfBound)
}

func (m *mmapFile) WriteAt(src []byte, offset int64) (int, error) {
m.boundaryChecks(offset, 1)
return copy(m.data[offset:], src), nil
}

// ReadUint64At reads uint64 from offset
func (m *Mmap) ReadUint64At(offset int64) uint64 {
if m.data == nil {
panic(ErrUnmappedMemory)
} else if offset+8 > m.length || offset < 0 {
panic(ErrIndexOutOfBound)
// ReadStringAt copies data to dest string builder from mapped region starting at
// given offset until the min value of (length - offset) or (dest.Cap() - dest.Len())
// and returns number of bytes copied to the dest slice.
func (m *mmapFile) ReadStringAt(dest *strings.Builder, offset int64) int {
m.boundaryChecks(offset, 1)

dataLength := m.length - offset
emptySpace := int64(dest.Cap() - dest.Len())
end := m.length
if dataLength > emptySpace {
end = offset + emptySpace
}

n, _ := dest.Write(m.data[offset:end])
return n
}

// WriteStringAt copies data to mapped region from the src string starting at
// given offset and returns number of bytes copied to the mapped region.
// See github.com/grandecola/mmap/#Mmap.WriteAt for more details.
func (m *mmapFile) WriteStringAt(src string, offset int64) int {
m.boundaryChecks(offset, 1)
return copy(m.data[offset:], src)
}

// ReadUint64At reads uint64 from offset
func (m *mmapFile) ReadUint64At(offset int64) uint64 {
m.boundaryChecks(offset, 8)
return binary.LittleEndian.Uint64(m.data[offset : offset+8])
}

// WriteUint64At writes num at offset
func (m *Mmap) WriteUint64At(num uint64, offset int64) {
if m.data == nil {
panic(ErrUnmappedMemory)
} else if offset+8 > m.length || offset < 0 {
panic(ErrIndexOutOfBound)
}

func (m *mmapFile) WriteUint64At(num uint64, offset int64) {
m.boundaryChecks(offset, 8)
binary.LittleEndian.PutUint64(m.data[offset:offset+8], num)
}

// Flush flushes the memory mapped region to disk
func (m *Mmap) Flush(flags int) error {
func (m *mmapFile) Flush(flags int) error {
_, _, err := syscall.Syscall(syscall.SYS_MSYNC,
uintptr(unsafe.Pointer(&m.data[0])), uintptr(len(m.data)), uintptr(flags))
uintptr(unsafe.Pointer(&m.data[0])), uintptr(m.length), uintptr(flags))
if err != 0 {
return err
}
Expand Down
12 changes: 6 additions & 6 deletions mmap_page.go
Expand Up @@ -6,9 +6,9 @@ import (
)

// Advise provides hints to kernel regarding the use of memory mapped region
func (m *Mmap) Advise(advice int) error {
func (m *mmapFile) Advise(advice int) error {
_, _, err := syscall.Syscall(syscall.SYS_MADVISE,
uintptr(unsafe.Pointer(&m.data[0])), uintptr(len(m.data)), uintptr(advice))
uintptr(unsafe.Pointer(&m.data[0])), uintptr(m.length), uintptr(advice))
if err != 0 {
return err
}
Expand All @@ -17,9 +17,9 @@ func (m *Mmap) Advise(advice int) error {
}

// Lock locks all the mapped memory to RAM, preventing the pages from swapping out
func (m *Mmap) Lock() error {
func (m *mmapFile) Lock() error {
_, _, err := syscall.Syscall(syscall.SYS_MLOCK,
uintptr(unsafe.Pointer(&m.data[0])), uintptr(len(m.data)), 0)
uintptr(unsafe.Pointer(&m.data[0])), uintptr(m.length), 0)
if err != 0 {
return err
}
Expand All @@ -28,9 +28,9 @@ func (m *Mmap) Lock() error {
}

// Unlock unlocks the mapped memory from RAM, enabling swapping out of RAM if required
func (m *Mmap) Unlock() error {
func (m *mmapFile) Unlock() error {
_, _, err := syscall.Syscall(syscall.SYS_MUNLOCK,
uintptr(unsafe.Pointer(&m.data[0])), uintptr(len(m.data)), 0)
uintptr(unsafe.Pointer(&m.data[0])), uintptr(m.length), 0)
if err != 0 {
return err
}
Expand Down