Skip to content

Commit

Permalink
Add functions to read and write string data
Browse files Browse the repository at this point in the history
  • Loading branch information
mangalaman93 committed Mar 20, 2019
1 parent 1931b7a commit ced96f4
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 69 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit ced96f4

Please sign in to comment.