Skip to content

Commit

Permalink
Add fallback path for mmap
Browse files Browse the repository at this point in the history
Closes oschwald#163.
  • Loading branch information
database64128 committed Jan 20, 2025
1 parent 2a6b0bf commit 78367a4
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 90 deletions.
25 changes: 25 additions & 0 deletions mmap_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//go:build appengine || plan9 || js || wasip1 || wasi

package maxminddb

import (
"errors"
)

type mmapUnsupportedError struct{}

func (mmapUnsupportedError) Error() string {
return "mmap is not supported on this platform"
}

func (mmapUnsupportedError) Is(target error) bool {
return target == errors.ErrUnsupported
}

func mmap(_, _ int) (data []byte, err error) {
return nil, mmapUnsupportedError{}
}

func munmap(_ []byte) (err error) {
return mmapUnsupportedError{}
}
67 changes: 67 additions & 0 deletions reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import (
"bytes"
"errors"
"fmt"
"io"
"net/netip"
"os"
"reflect"
"runtime"
)

const dataSectionSeparatorSize = 16
Expand Down Expand Up @@ -45,6 +48,70 @@ type Metadata struct {
RecordSize uint `maxminddb:"record_size"`
}

// Open takes a string path to a MaxMind DB file and returns a Reader
// structure or an error. The database file is opened using a memory map
// on supported platforms. On platforms without memory map support, such
// as WebAssembly or Google App Engine, the database is loaded into memory.
// Use the Close method on the Reader object to return the resources to the system.
func Open(file string) (*Reader, error) {
mapFile, err := os.Open(file)
if err != nil {
return nil, err
}
defer mapFile.Close()

stats, err := mapFile.Stat()
if err != nil {
return nil, err
}

size64 := stats.Size()
size := int(size64)
if int64(size) != size64 {
return nil, errors.New("file too large")
}

data, err := mmap(int(mapFile.Fd()), size)
if err != nil {
if errors.Is(err, errors.ErrUnsupported) {
data, err = openFallback(mapFile, size)
if err != nil {
return nil, err
}
return FromBytes(data)
}
return nil, err
}

reader, err := FromBytes(data)
if err != nil {
_ = munmap(data)
return nil, err
}

reader.hasMappedFile = true
runtime.SetFinalizer(reader, (*Reader).Close)
return reader, nil
}

func openFallback(f *os.File, size int) (data []byte, err error) {
data = make([]byte, size)
_, err = io.ReadFull(f, data)
return data, err
}

// Close returns the resources used by the database to the system.
func (r *Reader) Close() error {
var err error
if r.hasMappedFile {
runtime.SetFinalizer(r, nil)
r.hasMappedFile = false
err = munmap(r.buffer)
}
r.buffer = nil
return err
}

// FromBytes takes a byte slice corresponding to a MaxMind DB file and returns
// a Reader structure or an error.
func FromBytes(buffer []byte) (*Reader, error) {
Expand Down
26 changes: 0 additions & 26 deletions reader_memory.go

This file was deleted.

64 changes: 0 additions & 64 deletions reader_mmap.go

This file was deleted.

0 comments on commit 78367a4

Please sign in to comment.