@@ -5,8 +5,11 @@ import (
55 "bytes"
66 "errors"
77 "fmt"
8+ "io"
89 "net/netip"
10+ "os"
911 "reflect"
12+ "runtime"
1013)
1114
1215const dataSectionSeparatorSize = 16
@@ -45,6 +48,78 @@ type Metadata struct {
4548 RecordSize uint `maxminddb:"record_size"`
4649}
4750
51+ // Open takes a string path to a MaxMind DB file and returns a Reader
52+ // structure or an error. The database file is opened using a memory map
53+ // on supported platforms. On platforms without memory map support, such
54+ // as WebAssembly or Google App Engine, or if the memory map attempt fails
55+ // due to lack of support from the filesystem, the database is loaded into memory.
56+ // Use the Close method on the Reader object to return the resources to the system.
57+ func Open (file string ) (* Reader , error ) {
58+ mapFile , err := os .Open (file )
59+ if err != nil {
60+ return nil , err
61+ }
62+ defer mapFile .Close ()
63+
64+ stats , err := mapFile .Stat ()
65+ if err != nil {
66+ return nil , err
67+ }
68+
69+ size64 := stats .Size ()
70+ // mmapping an empty file returns -EINVAL on Unix platforms,
71+ // and ERROR_FILE_INVALID on Windows.
72+ if size64 == 0 {
73+ return nil , errors .New ("file is empty" )
74+ }
75+
76+ size := int (size64 )
77+ // Check for overflow.
78+ if int64 (size ) != size64 {
79+ return nil , errors .New ("file too large" )
80+ }
81+
82+ data , err := mmap (int (mapFile .Fd ()), size )
83+ if err != nil {
84+ if errors .Is (err , errors .ErrUnsupported ) {
85+ data , err = openFallback (mapFile , size )
86+ if err != nil {
87+ return nil , err
88+ }
89+ return FromBytes (data )
90+ }
91+ return nil , err
92+ }
93+
94+ reader , err := FromBytes (data )
95+ if err != nil {
96+ _ = munmap (data )
97+ return nil , err
98+ }
99+
100+ reader .hasMappedFile = true
101+ runtime .SetFinalizer (reader , (* Reader ).Close )
102+ return reader , nil
103+ }
104+
105+ func openFallback (f * os.File , size int ) (data []byte , err error ) {
106+ data = make ([]byte , size )
107+ _ , err = io .ReadFull (f , data )
108+ return data , err
109+ }
110+
111+ // Close returns the resources used by the database to the system.
112+ func (r * Reader ) Close () error {
113+ var err error
114+ if r .hasMappedFile {
115+ runtime .SetFinalizer (r , nil )
116+ r .hasMappedFile = false
117+ err = munmap (r .buffer )
118+ }
119+ r .buffer = nil
120+ return err
121+ }
122+
48123// FromBytes takes a byte slice corresponding to a MaxMind DB file and returns
49124// a Reader structure or an error.
50125func FromBytes (buffer []byte ) (* Reader , error ) {
0 commit comments