mirror of https://github.com/etcd-io/bbolt.git
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.pull/34/head
parent
c2577db1c2
commit
1c97a490dd
|
@ -0,0 +1,4 @@
|
||||||
|
package bolt
|
||||||
|
|
||||||
|
// maxMapSize represents the largest mmap size supported by Bolt.
|
||||||
|
const maxMapSize = 0xFFFFFFF // 256MB
|
|
@ -0,0 +1,4 @@
|
||||||
|
package bolt
|
||||||
|
|
||||||
|
// maxMapSize represents the largest mmap size supported by Bolt.
|
||||||
|
const maxMapSize = 0xFFFFFFFFFFFF // 256TB
|
|
@ -3,6 +3,7 @@ package bolt
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
var odirect int
|
var odirect int
|
||||||
|
@ -22,12 +23,31 @@ func funlock(f *os.File) error {
|
||||||
return syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
|
return syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// mmap memory maps a file to a byte slice.
|
// mmap memory maps a DB's data file.
|
||||||
func mmap(f *os.File, sz int) ([]byte, error) {
|
func mmap(db *DB, sz int) error {
|
||||||
return syscall.Mmap(int(f.Fd()), 0, sz, syscall.PROT_READ, syscall.MAP_SHARED)
|
b, err := syscall.Mmap(int(db.file.Fd()), 0, sz, syscall.PROT_READ, syscall.MAP_SHARED)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// munmap unmaps a pointer from a file.
|
// Save the original byte slice and convert to a byte array pointer.
|
||||||
func munmap(b []byte) error {
|
db.dataref = b
|
||||||
return syscall.Munmap(b)
|
db.data = (*[maxMapSize]byte)(unsafe.Pointer(&b[0]))
|
||||||
|
db.datasz = sz
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// munmap unmaps a DB's data file from memory.
|
||||||
|
func munmap(db *DB) error {
|
||||||
|
// Ignore the unmap if we have no mapped data.
|
||||||
|
if db.dataref == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmap using the original byte slice.
|
||||||
|
err := syscall.Munmap(db.dataref)
|
||||||
|
db.dataref = nil
|
||||||
|
db.data = nil
|
||||||
|
db.datasz = 0
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package bolt
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
var odirect = syscall.O_DIRECT
|
var odirect = syscall.O_DIRECT
|
||||||
|
@ -22,12 +23,31 @@ func funlock(f *os.File) error {
|
||||||
return syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
|
return syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// mmap memory maps a file to a byte slice.
|
// mmap memory maps a DB's data file.
|
||||||
func mmap(f *os.File, sz int) ([]byte, error) {
|
func mmap(db *DB, sz int) error {
|
||||||
return syscall.Mmap(int(f.Fd()), 0, sz, syscall.PROT_READ, syscall.MAP_SHARED)
|
b, err := syscall.Mmap(int(db.file.Fd()), 0, sz, syscall.PROT_READ, syscall.MAP_SHARED)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// munmap unmaps a pointer from a file.
|
// Save the original byte slice and convert to a byte array pointer.
|
||||||
func munmap(b []byte) error {
|
db.dataref = b
|
||||||
return syscall.Munmap(b)
|
db.data = (*[maxMapSize]byte)(unsafe.Pointer(&b[0]))
|
||||||
|
db.datasz = sz
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// munmap unmaps a DB's data file from memory.
|
||||||
|
func munmap(db *DB) error {
|
||||||
|
// Ignore the unmap if we have no mapped data.
|
||||||
|
if db.dataref == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmap using the original byte slice.
|
||||||
|
err := syscall.Munmap(db.dataref)
|
||||||
|
db.dataref = nil
|
||||||
|
db.data = nil
|
||||||
|
db.datasz = 0
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -23,36 +24,48 @@ func funlock(f *os.File) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// mmap memory maps a file to a byte slice.
|
// mmap memory maps a DB's data file.
|
||||||
// Based on: https://github.com/edsrzf/mmap-go
|
// Based on: https://github.com/edsrzf/mmap-go
|
||||||
func mmap(f *os.File, sz int) ([]byte, error) {
|
func mmap(db *DB, sz int) error {
|
||||||
|
// Truncate the database to the size of the mmap.
|
||||||
|
if err := db.file.Truncate(int64(sz)); err != nil {
|
||||||
|
return fmt.Errorf("truncate: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Open a file mapping handle.
|
// Open a file mapping handle.
|
||||||
sizelo, sizehi := uint32(sz>>32), uint32(sz&0xffffffff)
|
sizelo := uint32(sz >> 32)
|
||||||
h, errno := syscall.CreateFileMapping(syscall.Handle(f.Fd()), nil, syscall.PAGE_READONLY, sizehi, sizelo, nil)
|
sizehi := uint32(sz & 0xffffffff)
|
||||||
|
h, errno := syscall.CreateFileMapping(syscall.Handle(db.file.Fd()), nil, syscall.PAGE_READONLY, sizelo, sizehi, nil)
|
||||||
if h == 0 {
|
if h == 0 {
|
||||||
return nil, os.NewSyscallError("CreateFileMapping", errno)
|
return os.NewSyscallError("CreateFileMapping", errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the memory map.
|
// Create the memory map.
|
||||||
addr, errno := syscall.MapViewOfFile(h, syscall.FILE_MAP_READ, 0, 0, uintptr(sz))
|
addr, errno := syscall.MapViewOfFile(h, syscall.FILE_MAP_READ, 0, 0, uintptr(sz))
|
||||||
if addr == 0 {
|
if addr == 0 {
|
||||||
return nil, os.NewSyscallError("MapViewOfFile", errno)
|
return os.NewSyscallError("MapViewOfFile", errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close mapping handle.
|
// Close mapping handle.
|
||||||
if err := syscall.CloseHandle(syscall.Handle(h)); err != nil {
|
if err := syscall.CloseHandle(syscall.Handle(h)); err != nil {
|
||||||
return nil, os.NewSyscallError("CloseHandle", err)
|
return os.NewSyscallError("CloseHandle", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to a byte slice.
|
// Convert to a byte array.
|
||||||
b := ((*[0xFFFFFFF]byte)(unsafe.Pointer(addr)))[0:sz]
|
db.data = ((*[maxMapSize]byte)(unsafe.Pointer(addr)))
|
||||||
return b, nil
|
db.datasz = sz
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// munmap unmaps a pointer from a file.
|
// munmap unmaps a pointer from a file.
|
||||||
// Based on: https://github.com/edsrzf/mmap-go
|
// Based on: https://github.com/edsrzf/mmap-go
|
||||||
func munmap(b []byte) error {
|
func munmap(db *DB) error {
|
||||||
addr := (uintptr)(unsafe.Pointer(&b[0]))
|
if db.data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := (uintptr)(unsafe.Pointer(&db.data[0]))
|
||||||
if err := syscall.UnmapViewOfFile(addr); err != nil {
|
if err := syscall.UnmapViewOfFile(addr); err != nil {
|
||||||
return os.NewSyscallError("UnmapViewOfFile", err)
|
return os.NewSyscallError("UnmapViewOfFile", err)
|
||||||
}
|
}
|
||||||
|
|
22
db.go
22
db.go
|
@ -67,7 +67,9 @@ type DB struct {
|
||||||
|
|
||||||
path string
|
path string
|
||||||
file *os.File
|
file *os.File
|
||||||
data []byte
|
dataref []byte
|
||||||
|
data *[maxMapSize]byte
|
||||||
|
datasz int
|
||||||
meta0 *meta
|
meta0 *meta
|
||||||
meta1 *meta
|
meta1 *meta
|
||||||
pageSize int
|
pageSize int
|
||||||
|
@ -191,13 +193,8 @@ func (db *DB) mmap(minsz int) error {
|
||||||
}
|
}
|
||||||
size = db.mmapSize(size)
|
size = db.mmapSize(size)
|
||||||
|
|
||||||
// Truncate the database to the size of the mmap.
|
|
||||||
if err := db.file.Truncate(int64(size)); err != nil {
|
|
||||||
return fmt.Errorf("truncate: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Memory-map the data file as a byte slice.
|
// Memory-map the data file as a byte slice.
|
||||||
if db.data, err = mmap(db.file, size); err != nil {
|
if err := mmap(db, size); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,12 +215,9 @@ func (db *DB) mmap(minsz int) error {
|
||||||
|
|
||||||
// munmap unmaps the data file from memory.
|
// munmap unmaps the data file from memory.
|
||||||
func (db *DB) munmap() error {
|
func (db *DB) munmap() error {
|
||||||
if db.data != nil {
|
if err := munmap(db); err != nil {
|
||||||
if err := munmap(db.data); err != nil {
|
|
||||||
return fmt.Errorf("unmap error: " + err.Error())
|
return fmt.Errorf("unmap error: " + err.Error())
|
||||||
}
|
}
|
||||||
db.data = nil
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -507,7 +501,7 @@ func (db *DB) Stats() Stats {
|
||||||
// This is for internal access to the raw data bytes from the C cursor, use
|
// This is for internal access to the raw data bytes from the C cursor, use
|
||||||
// carefully, or not at all.
|
// carefully, or not at all.
|
||||||
func (db *DB) Info() *Info {
|
func (db *DB) Info() *Info {
|
||||||
return &Info{db.data, db.pageSize}
|
return &Info{uintptr(unsafe.Pointer(&db.data[0])), db.pageSize}
|
||||||
}
|
}
|
||||||
|
|
||||||
// page retrieves a page reference from the mmap based on the current page size.
|
// page retrieves a page reference from the mmap based on the current page size.
|
||||||
|
@ -544,7 +538,7 @@ func (db *DB) allocate(count int) (*page, error) {
|
||||||
// Resize mmap() if we're at the end.
|
// Resize mmap() if we're at the end.
|
||||||
p.id = db.rwtx.meta.pgid
|
p.id = db.rwtx.meta.pgid
|
||||||
var minsz = int((p.id+pgid(count))+1) * db.pageSize
|
var minsz = int((p.id+pgid(count))+1) * db.pageSize
|
||||||
if minsz >= len(db.data) {
|
if minsz >= db.datasz {
|
||||||
if err := db.mmap(minsz); err != nil {
|
if err := db.mmap(minsz); err != nil {
|
||||||
return nil, fmt.Errorf("mmap allocate error: %s", err)
|
return nil, fmt.Errorf("mmap allocate error: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -579,7 +573,7 @@ func (s *Stats) add(other *Stats) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Info struct {
|
type Info struct {
|
||||||
Data []byte
|
Data uintptr
|
||||||
PageSize int
|
PageSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
5
doc.go
5
doc.go
|
@ -12,6 +12,9 @@ rolled back in the event of a crash.
|
||||||
|
|
||||||
The design of Bolt is based on Howard Chu's LMDB database project.
|
The design of Bolt is based on Howard Chu's LMDB database project.
|
||||||
|
|
||||||
|
Bolt currently works on Windows, Mac OS X, and Linux.
|
||||||
|
|
||||||
|
|
||||||
Basics
|
Basics
|
||||||
|
|
||||||
There are only a few types in Bolt: DB, Bucket, Tx, and Cursor. The DB is
|
There are only a few types in Bolt: DB, Bucket, Tx, and Cursor. The DB is
|
||||||
|
@ -33,7 +36,5 @@ values returned from Bolt cannot be changed. Writing to a read-only byte slice
|
||||||
will cause Go to panic. If you need to work with data returned from a Get() you
|
will cause Go to panic. If you need to work with data returned from a Get() you
|
||||||
need to first copy it to a new byte slice.
|
need to first copy it to a new byte slice.
|
||||||
|
|
||||||
Bolt currently works on Mac OS and Linux. Windows support is coming soon.
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package bolt
|
package bolt
|
||||||
|
|
Loading…
Reference in New Issue