The mmap package provides efficient memory-mapped file access for Go applications. Memory mapping allows you to access file contents as if they were in memory, enabling fast random access and concurrent reading without loading the entire file into RAM.
go get golang.org/x/exp/mmapimport "golang.org/x/exp/mmap"package main
import (
"fmt"
"log"
"golang.org/x/exp/mmap"
)
func main() {
// Open and memory-map a file
reader, err := mmap.Open("data.bin")
if err != nil {
log.Fatal(err)
}
defer reader.Close()
// Get file size
size := reader.Len()
fmt.Printf("File size: %d bytes\n", size)
// Access individual bytes
firstByte := reader.At(0)
fmt.Printf("First byte: 0x%02x\n", firstByte)
// Read data from offset
buf := make([]byte, 10)
n, err := reader.ReadAt(buf, 0)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Read %d bytes: %v\n", n, buf[:n])
}Opens and memory-maps a file for reading.
func Open(filename string) (*ReaderAt, error)Opens the named file and returns a ReaderAt that can be used to access the file's contents through a memory-mapped interface. The file is memory-mapped for reading, allowing efficient random access. An error is returned if the file cannot be opened or memory-mapped.
Implements the standard io.ReaderAt interface for reading from arbitrary offsets in the memory-mapped file.
func (r *ReaderAt) ReadAt(p []byte, off int64) (int, error)Reads bytes from the memory-mapped file starting at the specified offset. It implements the io.ReaderAt interface and can be safely called concurrently from multiple goroutines. Returns the number of bytes read (which may be less than the length of p) and any error encountered.
Provides low-level access to individual bytes in the memory-mapped file.
func (r *ReaderAt) At(i int) byteReturns the byte at the specified index i in the memory-mapped file. This provides direct, efficient access to individual bytes without allocating buffers.
Returns the total size of the memory-mapped file.
func (r *ReaderAt) Len() intReturns the length in bytes of the underlying memory-mapped file. This allows you to determine the bounds of the file without external system calls.
Closes the memory-mapped file and releases associated resources.
func (r *ReaderAt) Close() errorCloses the ReaderAt and releases the memory mapping. After Close is called, subsequent calls to ReadAt, At, or Len will fail. It is not safe to call Close concurrently with any reading method; callers must ensure synchronization.
type ReaderAt struct {
// Has unexported fields.
}ReaderAt represents a memory-mapped file opened for reading. It provides efficient random access to file contents through memory mapping.
Like any io.ReaderAt, clients can execute parallel ReadAt calls, but it is not safe to call Close and reading methods concurrently. All concurrent ReadAt calls are allowed, but you must coordinate with Close() to ensure thread-safe access.
Methods:
Open(filename string) (*ReaderAt, error) - Factory function to create a new ReaderAtReadAt(p []byte, off int64) (int, error) - Read bytes from offset (implements io.ReaderAt)At(i int) byte - Get individual byte at indexLen() int - Get file size in bytesClose() error - Release resourcespackage main
import (
"encoding/binary"
"fmt"
"log"
"golang.org/x/exp/mmap"
)
func main() {
// Open a binary file
reader, err := mmap.Open("data.bin")
if err != nil {
log.Fatal(err)
}
defer reader.Close()
// Read a 32-bit integer from offset 0
buf := make([]byte, 4)
reader.ReadAt(buf, 0)
value := binary.LittleEndian.Uint32(buf)
fmt.Printf("Value at offset 0: %d\n", value)
}package main
import (
"fmt"
"log"
"golang.org/x/exp/mmap"
)
func main() {
// Open a large file efficiently
reader, err := mmap.Open("large_file.dat")
if err != nil {
log.Fatal(err)
}
defer reader.Close()
// Process file without loading it all into memory
fileSize := reader.Len()
chunkSize := 4096
for offset := int64(0); offset < int64(fileSize); offset += int64(chunkSize) {
end := offset + int64(chunkSize)
if end > int64(fileSize) {
end = int64(fileSize)
}
buf := make([]byte, end-offset)
n, err := reader.ReadAt(buf, offset)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Processed chunk at %d: %d bytes\n", offset, n)
}
}package main
import (
"fmt"
"log"
"sync"
"golang.org/x/exp/mmap"
)
func main() {
reader, err := mmap.Open("data.bin")
if err != nil {
log.Fatal(err)
}
defer reader.Close()
// Multiple goroutines can safely read concurrently
var wg sync.WaitGroup
numWorkers := 4
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
// Each worker reads from a different offset
offset := int64(workerID * 100)
buf := make([]byte, 100)
n, err := reader.ReadAt(buf, offset)
if err != nil {
log.Printf("Worker %d error: %v\n", workerID, err)
return
}
fmt.Printf("Worker %d read %d bytes from offset %d\n", workerID, n, offset)
}(i)
}
wg.Wait()
}package main
import (
"fmt"
"log"
"golang.org/x/exp/mmap"
)
func main() {
reader, err := mmap.Open("data.bin")
if err != nil {
log.Fatal(err)
}
defer reader.Close()
// Access bytes at specific positions
positions := []int64{0, 100, 500, 1000}
for _, pos := range positions {
buf := make([]byte, 8)
n, err := reader.ReadAt(buf, pos)
if err != nil {
log.Printf("Error at offset %d: %v\n", pos, err)
continue
}
fmt.Printf("Read %d bytes from offset %d\n", n, pos)
}
}package main
import (
"fmt"
"log"
"golang.org/x/exp/mmap"
)
func main() {
reader, err := mmap.Open("data.bin")
if err != nil {
log.Fatal(err)
}
defer reader.Close()
// Find pattern in file using direct byte access
pattern := byte(0xFF)
fileSize := reader.Len()
for i := 0; i < fileSize; i++ {
if reader.At(i) == pattern {
fmt.Printf("Found pattern at offset %d\n", i)
}
}
}Memory Efficiency: Memory-mapped files don't load the entire file into memory. Only accessed pages are loaded by the OS.
Concurrent Reading: Multiple goroutines can safely call ReadAt concurrently, making it ideal for parallel file processing.
Random Access: Unlike buffered I/O, you can efficiently seek to arbitrary offsets without sequential reading overhead.
Platform Specific: The underlying mmap syscall behavior may vary across operating systems (Linux, macOS, Windows).
Resource Management: Always defer Close() to ensure resources are properly released.
Common errors when using the mmap package:
ReadAt calls are safe for concurrent execution from multiple goroutinesClose is NOT safe to call concurrently with any reading method