Add Windows support.
This commit adds Windows support to Bolt. Windows memory maps return an address instead of a byte
slice so the DB.data field had to be refactored to be a pointer to a large byte array.
Ben Johnson
9 years ago
0 | package bolt | |
1 | ||
2 | // maxMapSize represents the largest mmap size supported by Bolt. | |
3 | const maxMapSize = 0xFFFFFFF // 256MB |
0 | package bolt | |
1 | ||
2 | // maxMapSize represents the largest mmap size supported by Bolt. | |
3 | const maxMapSize = 0xFFFFFFFFFFFF // 256TB |
2 | 2 | import ( |
3 | 3 | "os" |
4 | 4 | "syscall" |
5 | "unsafe" | |
5 | 6 | ) |
6 | 7 | |
7 | 8 | var odirect int |
21 | 22 | return syscall.Flock(int(f.Fd()), syscall.LOCK_UN) |
22 | 23 | } |
23 | 24 | |
24 | // mmap memory maps a file to a byte slice. | |
25 | func mmap(f *os.File, sz int) ([]byte, error) { | |
26 | return syscall.Mmap(int(f.Fd()), 0, sz, syscall.PROT_READ, syscall.MAP_SHARED) | |
25 | // mmap memory maps a DB's data file. | |
26 | func mmap(db *DB, sz int) error { | |
27 | b, err := syscall.Mmap(int(db.file.Fd()), 0, sz, syscall.PROT_READ, syscall.MAP_SHARED) | |
28 | if err != nil { | |
29 | return err | |
30 | } | |
31 | ||
32 | // Save the original byte slice and convert to a byte array pointer. | |
33 | db.dataref = b | |
34 | db.data = (*[maxMapSize]byte)(unsafe.Pointer(&b[0])) | |
35 | db.datasz = sz | |
36 | return nil | |
27 | 37 | } |
28 | 38 | |
29 | // munmap unmaps a pointer from a file. | |
30 | func munmap(b []byte) error { | |
31 | return syscall.Munmap(b) | |
39 | // munmap unmaps a DB's data file from memory. | |
40 | func munmap(db *DB) error { | |
41 | // Ignore the unmap if we have no mapped data. | |
42 | if db.dataref == nil { | |
43 | return nil | |
44 | } | |
45 | ||
46 | // Unmap using the original byte slice. | |
47 | err := syscall.Munmap(db.dataref) | |
48 | db.dataref = nil | |
49 | db.data = nil | |
50 | db.datasz = 0 | |
51 | return err | |
32 | 52 | } |
2 | 2 | import ( |
3 | 3 | "os" |
4 | 4 | "syscall" |
5 | "unsafe" | |
5 | 6 | ) |
6 | 7 | |
7 | 8 | var odirect = syscall.O_DIRECT |
21 | 22 | return syscall.Flock(int(f.Fd()), syscall.LOCK_UN) |
22 | 23 | } |
23 | 24 | |
24 | // mmap memory maps a file to a byte slice. | |
25 | func mmap(f *os.File, sz int) ([]byte, error) { | |
26 | return syscall.Mmap(int(f.Fd()), 0, sz, syscall.PROT_READ, syscall.MAP_SHARED) | |
25 | // mmap memory maps a DB's data file. | |
26 | func mmap(db *DB, sz int) error { | |
27 | b, err := syscall.Mmap(int(db.file.Fd()), 0, sz, syscall.PROT_READ, syscall.MAP_SHARED) | |
28 | if err != nil { | |
29 | return err | |
30 | } | |
31 | ||
32 | // Save the original byte slice and convert to a byte array pointer. | |
33 | db.dataref = b | |
34 | db.data = (*[maxMapSize]byte)(unsafe.Pointer(&b[0])) | |
35 | db.datasz = sz | |
36 | return nil | |
27 | 37 | } |
28 | 38 | |
29 | // munmap unmaps a pointer from a file. | |
30 | func munmap(b []byte) error { | |
31 | return syscall.Munmap(b) | |
39 | // munmap unmaps a DB's data file from memory. | |
40 | func munmap(db *DB) error { | |
41 | // Ignore the unmap if we have no mapped data. | |
42 | if db.dataref == nil { | |
43 | return nil | |
44 | } | |
45 | ||
46 | // Unmap using the original byte slice. | |
47 | err := syscall.Munmap(db.dataref) | |
48 | db.dataref = nil | |
49 | db.data = nil | |
50 | db.datasz = 0 | |
51 | return err | |
32 | 52 | } |
0 | 0 | package bolt |
1 | 1 | |
2 | 2 | import ( |
3 | "fmt" | |
3 | 4 | "os" |
4 | 5 | "syscall" |
5 | 6 | "unsafe" |
22 | 23 | return nil |
23 | 24 | } |
24 | 25 | |
25 | // mmap memory maps a file to a byte slice. | |
26 | // mmap memory maps a DB's data file. | |
26 | 27 | // Based on: https://github.com/edsrzf/mmap-go |
27 | func mmap(f *os.File, sz int) ([]byte, error) { | |
28 | func mmap(db *DB, sz int) error { | |
29 | // Truncate the database to the size of the mmap. | |
30 | if err := db.file.Truncate(int64(sz)); err != nil { | |
31 | return fmt.Errorf("truncate: %s", err) | |
32 | } | |
33 | ||
28 | 34 | // Open a file mapping handle. |
29 | sizelo, sizehi := uint32(sz>>32), uint32(sz&0xffffffff) | |
30 | h, errno := syscall.CreateFileMapping(syscall.Handle(f.Fd()), nil, syscall.PAGE_READONLY, sizehi, sizelo, nil) | |
35 | sizelo := uint32(sz >> 32) | |
36 | sizehi := uint32(sz & 0xffffffff) | |
37 | h, errno := syscall.CreateFileMapping(syscall.Handle(db.file.Fd()), nil, syscall.PAGE_READONLY, sizelo, sizehi, nil) | |
31 | 38 | if h == 0 { |
32 | return nil, os.NewSyscallError("CreateFileMapping", errno) | |
39 | return os.NewSyscallError("CreateFileMapping", errno) | |
33 | 40 | } |
34 | 41 | |
35 | 42 | // Create the memory map. |
36 | 43 | addr, errno := syscall.MapViewOfFile(h, syscall.FILE_MAP_READ, 0, 0, uintptr(sz)) |
37 | 44 | if addr == 0 { |
38 | return nil, os.NewSyscallError("MapViewOfFile", errno) | |
45 | return os.NewSyscallError("MapViewOfFile", errno) | |
39 | 46 | } |
40 | 47 | |
41 | 48 | // Close mapping handle. |
42 | 49 | if err := syscall.CloseHandle(syscall.Handle(h)); err != nil { |
43 | return nil, os.NewSyscallError("CloseHandle", err) | |
50 | return os.NewSyscallError("CloseHandle", err) | |
44 | 51 | } |
45 | 52 | |
46 | // Convert to a byte slice. | |
47 | b := ((*[0xFFFFFFF]byte)(unsafe.Pointer(addr)))[0:sz] | |
48 | return b, nil | |
53 | // Convert to a byte array. | |
54 | db.data = ((*[maxMapSize]byte)(unsafe.Pointer(addr))) | |
55 | db.datasz = sz | |
56 | ||
57 | return nil | |
49 | 58 | } |
50 | 59 | |
51 | 60 | // munmap unmaps a pointer from a file. |
52 | 61 | // Based on: https://github.com/edsrzf/mmap-go |
53 | func munmap(b []byte) error { | |
54 | addr := (uintptr)(unsafe.Pointer(&b[0])) | |
62 | func munmap(db *DB) error { | |
63 | if db.data == nil { | |
64 | return nil | |
65 | } | |
66 | ||
67 | addr := (uintptr)(unsafe.Pointer(&db.data[0])) | |
55 | 68 | if err := syscall.UnmapViewOfFile(addr); err != nil { |
56 | 69 | return os.NewSyscallError("UnmapViewOfFile", err) |
57 | 70 | } |
66 | 66 | |
67 | 67 | path string |
68 | 68 | file *os.File |
69 | data []byte | |
69 | dataref []byte | |
70 | data *[maxMapSize]byte | |
71 | datasz int | |
70 | 72 | meta0 *meta |
71 | 73 | meta1 *meta |
72 | 74 | pageSize int |
190 | 192 | } |
191 | 193 | size = db.mmapSize(size) |
192 | 194 | |
193 | // Truncate the database to the size of the mmap. | |
194 | if err := db.file.Truncate(int64(size)); err != nil { | |
195 | return fmt.Errorf("truncate: %s", err) | |
196 | } | |
197 | ||
198 | 195 | // Memory-map the data file as a byte slice. |
199 | if db.data, err = mmap(db.file, size); err != nil { | |
196 | if err := mmap(db, size); err != nil { | |
200 | 197 | return err |
201 | 198 | } |
202 | 199 | |
217 | 214 | |
218 | 215 | // munmap unmaps the data file from memory. |
219 | 216 | func (db *DB) munmap() error { |
220 | if db.data != nil { | |
221 | if err := munmap(db.data); err != nil { | |
222 | return fmt.Errorf("unmap error: " + err.Error()) | |
223 | } | |
224 | db.data = nil | |
217 | if err := munmap(db); err != nil { | |
218 | return fmt.Errorf("unmap error: " + err.Error()) | |
225 | 219 | } |
226 | 220 | return nil |
227 | 221 | } |
506 | 500 | // This is for internal access to the raw data bytes from the C cursor, use |
507 | 501 | // carefully, or not at all. |
508 | 502 | func (db *DB) Info() *Info { |
509 | return &Info{db.data, db.pageSize} | |
503 | return &Info{uintptr(unsafe.Pointer(&db.data[0])), db.pageSize} | |
510 | 504 | } |
511 | 505 | |
512 | 506 | // page retrieves a page reference from the mmap based on the current page size. |
543 | 537 | // Resize mmap() if we're at the end. |
544 | 538 | p.id = db.rwtx.meta.pgid |
545 | 539 | var minsz = int((p.id+pgid(count))+1) * db.pageSize |
546 | if minsz >= len(db.data) { | |
540 | if minsz >= db.datasz { | |
547 | 541 | if err := db.mmap(minsz); err != nil { |
548 | 542 | return nil, fmt.Errorf("mmap allocate error: %s", err) |
549 | 543 | } |
578 | 572 | } |
579 | 573 | |
580 | 574 | type Info struct { |
581 | Data []byte | |
575 | Data uintptr | |
582 | 576 | PageSize int |
583 | 577 | } |
584 | 578 |
10 | 10 | rolled back in the event of a crash. |
11 | 11 | |
12 | 12 | The design of Bolt is based on Howard Chu's LMDB database project. |
13 | ||
14 | Bolt currently works on Windows, Mac OS X, and Linux. | |
15 | ||
13 | 16 | |
14 | 17 | Basics |
15 | 18 | |
32 | 35 | will cause Go to panic. If you need to work with data returned from a Get() you |
33 | 36 | need to first copy it to a new byte slice. |
34 | 37 | |
35 | Bolt currently works on Mac OS and Linux. Windows support is coming soon. | |
36 | ||
37 | 38 | */ |
38 | 39 | package bolt |