@@ -5,8 +5,11 @@ import (
5
5
"bytes"
6
6
"errors"
7
7
"fmt"
8
+ "io"
8
9
"net/netip"
10
+ "os"
9
11
"reflect"
12
+ "runtime"
10
13
)
11
14
12
15
const dataSectionSeparatorSize = 16
@@ -45,6 +48,78 @@ type Metadata struct {
45
48
RecordSize uint `maxminddb:"record_size"`
46
49
}
47
50
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
+
48
123
// FromBytes takes a byte slice corresponding to a MaxMind DB file and returns
49
124
// a Reader structure or an error.
50
125
func FromBytes (buffer []byte ) (* Reader , error ) {
0 commit comments