package bboltimport ()// The largest step that can be taken when remapping the mmap.const maxMmapStep = 1 << 30// 1GB// The data file format version.const version = 2// Represents a marker value to indicate that a file is a Bolt DB.const magic uint32 = 0xED0CDAEDconst pgidNoFreelist pgid = 0xffffffffffffffff// IgnoreNoSync specifies whether the NoSync field of a DB is ignored when// syncing changes to a file. This is required as some operating systems,// such as OpenBSD, do not have a unified buffer cache (UBC) and writes// must be synchronized using the msync(2) syscall.constIgnoreNoSync = runtime.GOOS == "openbsd"// Default values if not set in a DB instance.const (DefaultMaxBatchSizeint = 1000DefaultMaxBatchDelay = 10 * time.MillisecondDefaultAllocSize = 16 * 1024 * 1024)// default page size for db is set to the OS page size.var defaultPageSize = os.Getpagesize()// The time elapsed between consecutive file locking attempts.const flockRetryTimeout = 50 * time.Millisecond// FreelistType is the type of the freelist backendtypeFreelistTypestringconst (// FreelistArrayType indicates backend freelist type is arrayFreelistArrayType = FreelistType("array")// FreelistMapType indicates backend freelist type is hashmapFreelistMapType = FreelistType("hashmap"))// DB represents a collection of buckets persisted to a file on disk.// All data access is performed through transactions which can be obtained through the DB.// All the functions on DB will return a ErrDatabaseNotOpen if accessed before Open() is called.typeDBstruct {// When enabled, the database will perform a Check() after every commit. // A panic is issued if the database is in an inconsistent state. This // flag has a large performance impact so it should only be used for // debugging purposes. StrictMode bool// Setting the NoSync flag will cause the database to skip fsync() // calls after each commit. This can be useful when bulk loading data // into a database and you can restart the bulk load in the event of // a system failure or database corruption. Do not set this flag for // normal use. // // If the package global IgnoreNoSync constant is true, this value is // ignored. See the comment on that constant for more details. // // THIS IS UNSAFE. PLEASE USE WITH CAUTION. NoSync bool// When true, skips syncing freelist to disk. This improves the database // write performance under normal operation, but requires a full database // re-sync during recovery. NoFreelistSync bool// FreelistType sets the backend freelist type. There are two options. Array which is simple but endures // dramatic performance degradation if database is large and framentation in freelist is common. // The alternative one is using hashmap, it is faster in almost all circumstances // but it doesn't guarantee that it offers the smallest page id available. In normal case it is safe. // The default type is array FreelistType FreelistType// When true, skips the truncate call when growing the database. // Setting this to true is only safe on non-ext3/ext4 systems. // Skipping truncation avoids preallocation of hard drive space and // bypasses a truncate() and fsync() syscall on remapping. // // https://github.com/boltdb/bolt/issues/284 NoGrowSync bool// If you want to read the entire database fast, you can set MmapFlag to // syscall.MAP_POPULATE on Linux 2.6.23+ for sequential read-ahead. MmapFlags int// MaxBatchSize is the maximum size of a batch. Default value is // copied from DefaultMaxBatchSize in Open. // // If <=0, disables batching. // // Do not change concurrently with calls to Batch. MaxBatchSize int// MaxBatchDelay is the maximum delay before a batch starts. // Default value is copied from DefaultMaxBatchDelay in Open. // // If <=0, effectively disables batching. // // Do not change concurrently with calls to Batch. MaxBatchDelay time.Duration// AllocSize is the amount of space allocated when the database // needs to create new pages. This is done to amortize the cost // of truncate() and fsync() when growing the data file. AllocSize int// Mlock locks database file in memory when set to true. // It prevents major page faults, however used memory can't be reclaimed. // // Supported only on Unix via mlock/munlock syscalls. Mlock bool path string openFile func(string, int, os.FileMode) (*os.File, error) file *os.File dataref []byte// mmap'ed readonly, write throws SEGV data *[maxMapSize]byte datasz int filesz int// current on disk file size meta0 *meta meta1 *meta pageSize int opened bool rwtx *Tx txs []*Tx stats Stats freelist *freelist freelistLoad sync.Once pagePool sync.Pool batchMu sync.Mutex batch *batch rwlock sync.Mutex// Allows only one writer at a time. metalock sync.Mutex// Protects meta page access. mmaplock sync.RWMutex// Protects mmap access during remapping. statlock sync.RWMutex// Protects stats access. ops struct { writeAt func(b []byte, off int64) (n int, err error) }// Read only mode. // When true, Update() and Begin(true) return ErrDatabaseReadOnly immediately. readOnly bool}// Path returns the path to currently open database file.func ( *DB) () string {return .path}// GoString returns the Go string representation of the database.func ( *DB) () string {returnfmt.Sprintf("bolt.DB{path:%q}", .path)}// String returns the string representation of the database.func ( *DB) () string {returnfmt.Sprintf("DB<%q>", .path)}// Open creates and opens a database at the given path.// If the file does not exist then it will be created automatically.// Passing in nil options will cause Bolt to open the database with the default options.func ( string, os.FileMode, *Options) (*DB, error) { := &DB{opened: true, }// Set default options if no options are provided.if == nil { = DefaultOptions } .NoSync = .NoSync .NoGrowSync = .NoGrowSync .MmapFlags = .MmapFlags .NoFreelistSync = .NoFreelistSync .FreelistType = .FreelistType .Mlock = .Mlock// Set default values for later DB operations. .MaxBatchSize = DefaultMaxBatchSize .MaxBatchDelay = DefaultMaxBatchDelay .AllocSize = DefaultAllocSize := os.O_RDWRif .ReadOnly { = os.O_RDONLY .readOnly = true } .openFile = .OpenFileif .openFile == nil { .openFile = os.OpenFile }// Open data file and separate sync handler for metadata writes.varerrorif .file, = .openFile(, |os.O_CREATE, ); != nil { _ = .close()returnnil, } .path = .file.Name()// Lock file so that other processes using Bolt in read-write mode cannot // use the database at the same time. This would cause corruption since // the two processes would write meta pages and free pages separately. // The database file is locked exclusively (only one process can grab the lock) // if !options.ReadOnly. // The database file is locked using the shared lock (more than one process may // hold a lock at the same time) otherwise (options.ReadOnly is set).if := flock(, !.readOnly, .Timeout); != nil { _ = .close()returnnil, }// Default values for test hooks .ops.writeAt = .file.WriteAtif .pageSize = .PageSize; .pageSize == 0 {// Set the default page size to the OS page size. .pageSize = defaultPageSize }// Initialize the database if it doesn't exist.if , := .file.Stat(); != nil { _ = .close()returnnil, } elseif .Size() == 0 {// Initialize new files with meta pages.if := .init(); != nil {// clean up file descriptor on initialization fail _ = .close()returnnil, } } else {// Read the first meta page to determine the page size.var [0x1000]byte// If we can't read the page size, but can read a page, assume // it's the same as the OS or one given -- since that's how the // page size was chosen in the first place. // // If the first page is invalid and this OS uses a different // page size than what the database was created with then we // are out of luck and cannot access the database. // // TODO: scan for next pageif , := .file.ReadAt([:], 0); == nil && == len() {if := .pageInBuffer([:], 0).meta(); .validate() == nil { .pageSize = int(.pageSize) } } else { _ = .close()returnnil, ErrInvalid } }// Initialize page pool. .pagePool = sync.Pool{New: func() interface{} {returnmake([]byte, .pageSize) }, }// Memory map the data file.if := .mmap(.InitialMmapSize); != nil { _ = .close()returnnil, }if .readOnly {return , nil } .loadFreelist()// Flush freelist when transitioning from no sync to sync so // NoFreelistSync unaware boltdb can open the db later.if !.NoFreelistSync && !.hasSyncedFreelist() { , := .Begin(true)if != nil { = .Commit() }if != nil { _ = .close()returnnil, } }// Mark the database as opened and return.return , nil}// loadFreelist reads the freelist if it is synced, or reconstructs it// by scanning the DB if it is not synced. It assumes there are no// concurrent accesses being made to the freelist.func ( *DB) () { .freelistLoad.Do(func() { .freelist = newFreelist(.FreelistType)if !.hasSyncedFreelist() {// Reconstruct free list by scanning the DB. .freelist.readIDs(.freepages()) } else {// Read free list from freelist page. .freelist.read(.page(.meta().freelist)) } .stats.FreePageN = .freelist.free_count() })}func ( *DB) () bool {return .meta().freelist != pgidNoFreelist}// mmap opens the underlying memory-mapped file and initializes the meta references.// minsz is the minimum size that the new mmap can be.func ( *DB) ( int) error { .mmaplock.Lock()defer .mmaplock.Unlock() , := .file.Stat()if != nil {returnfmt.Errorf("mmap stat error: %s", ) } elseifint(.Size()) < .pageSize*2 {returnfmt.Errorf("file size too small") }// Ensure the size is at least the minimum size. := int(.Size())var = if < { = } , = .mmapSize()if != nil {return }if .Mlock {// Unlock db memoryif := .munlock(); != nil {return } }// Dereference all mmap references before unmapping.if .rwtx != nil { .rwtx.root.dereference() }// Unmap existing data before continuing.if := .munmap(); != nil {return }// Memory-map the data file as a byte slice.if := mmap(, ); != nil {return }if .Mlock {// Don't allow swapping of data fileif := .mlock(); != nil {return } }// Save references to the meta pages. .meta0 = .page(0).meta() .meta1 = .page(1).meta()// Validate the meta pages. We only return an error if both meta pages fail // validation, since meta0 failing validation means that it wasn't saved // properly -- but we can recover using meta1. And vice-versa. := .meta0.validate() := .meta1.validate()if != nil && != nil {return }returnnil}// munmap unmaps the data file from memory.func ( *DB) () error {if := munmap(); != nil {returnfmt.Errorf("unmap error: " + .Error()) }returnnil}// mmapSize determines the appropriate size for the mmap given the current size// of the database. The minimum size is 32KB and doubles until it reaches 1GB.// Returns an error if the new mmap size is greater than the max allowed.func ( *DB) ( int) (int, error) {// Double the size from 32KB until 1GB.for := uint(15); <= 30; ++ {if <= 1<< {return1 << , nil } }// Verify the requested size is not above the maximum allowed.if > maxMapSize {return0, fmt.Errorf("mmap too large") }// If larger than 1GB then grow by 1GB at a time. := int64()if := % int64(maxMmapStep); > 0 { += int64(maxMmapStep) - }// Ensure that the mmap size is a multiple of the page size. // This should always be true since we're incrementing in MBs. := int64(.pageSize)if ( % ) != 0 { = (( / ) + 1) * }// If we've exceeded the max size then only grow up to the max size.if > maxMapSize { = maxMapSize }returnint(), nil}func ( *DB) ( int) error {if := munlock(, ); != nil {returnfmt.Errorf("munlock error: " + .Error()) }returnnil}func ( *DB) ( int) error {if := mlock(, ); != nil {returnfmt.Errorf("mlock error: " + .Error()) }returnnil}func ( *DB) (, int) error {if := .munlock(); != nil {return }if := .mlock(); != nil {return }returnnil}// init creates a new database file and initializes its meta pages.func ( *DB) () error {// Create two meta pages on a buffer. := make([]byte, .pageSize*4)for := 0; < 2; ++ { := .pageInBuffer(, pgid()) .id = pgid() .flags = metaPageFlag// Initialize the meta page. := .meta() .magic = magic .version = version .pageSize = uint32(.pageSize) .freelist = 2 .root = bucket{root: 3} .pgid = 4 .txid = txid() .checksum = .sum64() }// Write an empty freelist at page 3. := .pageInBuffer(, pgid(2)) .id = pgid(2) .flags = freelistPageFlag .count = 0// Write an empty leaf page at page 4. = .pageInBuffer(, pgid(3)) .id = pgid(3) .flags = leafPageFlag .count = 0// Write the buffer to our data file.if , := .ops.writeAt(, 0); != nil {return }if := fdatasync(); != nil {return } .filesz = len()returnnil}// Close releases all database resources.// It will block waiting for any open transactions to finish// before closing the database and returning.func ( *DB) () error { .rwlock.Lock()defer .rwlock.Unlock() .metalock.Lock()defer .metalock.Unlock() .mmaplock.Lock()defer .mmaplock.Unlock()return .close()}func ( *DB) () error {if !.opened {returnnil } .opened = false .freelist = nil// Clear ops. .ops.writeAt = nil// Close the mmap.if := .munmap(); != nil {return }// Close file handles.if .file != nil {// No need to unlock read-only file.if !.readOnly {// Unlock the file.if := funlock(); != nil {log.Printf("bolt.Close(): funlock error: %s", ) } }// Close the file descriptor.if := .file.Close(); != nil {returnfmt.Errorf("db file close: %s", ) } .file = nil } .path = ""returnnil}// Begin starts a new transaction.// Multiple read-only transactions can be used concurrently but only one// write transaction can be used at a time. Starting multiple write transactions// will cause the calls to block and be serialized until the current write// transaction finishes.//// Transactions should not be dependent on one another. Opening a read// transaction and a write transaction in the same goroutine can cause the// writer to deadlock because the database periodically needs to re-mmap itself// as it grows and it cannot do that while a read transaction is open.//// If a long running read transaction (for example, a snapshot transaction) is// needed, you might want to set DB.InitialMmapSize to a large enough value// to avoid potential blocking of write transaction.//// IMPORTANT: You must close read-only transactions after you are finished or// else the database will not reclaim old pages.func ( *DB) ( bool) (*Tx, error) {if {return .beginRWTx() }return .beginTx()}func ( *DB) () (*Tx, error) {// Lock the meta pages while we initialize the transaction. We obtain // the meta lock before the mmap lock because that's the order that the // write transaction will obtain them. .metalock.Lock()// Obtain a read-only lock on the mmap. When the mmap is remapped it will // obtain a write lock so all transactions must finish before it can be // remapped. .mmaplock.RLock()// Exit if the database is not open yet.if !.opened { .mmaplock.RUnlock() .metalock.Unlock()returnnil, ErrDatabaseNotOpen }// Create a transaction associated with the database. := &Tx{} .init()// Keep track of transaction until it closes. .txs = append(.txs, ) := len(.txs)// Unlock the meta pages. .metalock.Unlock()// Update the transaction stats. .statlock.Lock() .stats.TxN++ .stats.OpenTxN = .statlock.Unlock()return , nil}func ( *DB) () (*Tx, error) {// If the database was opened with Options.ReadOnly, return an error.if .readOnly {returnnil, ErrDatabaseReadOnly }// Obtain writer lock. This is released by the transaction when it closes. // This enforces only one writer transaction at a time. .rwlock.Lock()// Once we have the writer lock then we can lock the meta pages so that // we can set up the transaction. .metalock.Lock()defer .metalock.Unlock()// Exit if the database is not open yet.if !.opened { .rwlock.Unlock()returnnil, ErrDatabaseNotOpen }// Create a transaction associated with the database. := &Tx{writable: true} .init() .rwtx = .freePages()return , nil}// freePages releases any pages associated with closed read-only transactions.func ( *DB) () {// Free all pending pages prior to earliest open transaction.sort.Sort(txsById(.txs)) := txid(0xFFFFFFFFFFFFFFFF)iflen(.txs) > 0 { = .txs[0].meta.txid }if > 0 { .freelist.release( - 1) }// Release unused txid extents.for , := range .txs { .freelist.releaseRange(, .meta.txid-1) = .meta.txid + 1 } .freelist.releaseRange(, txid(0xFFFFFFFFFFFFFFFF))// Any page both allocated and freed in an extent is safe to release.}type txsById []*Txfunc ( txsById) () int { returnlen() }func ( txsById) (, int) { [], [] = [], [] }func ( txsById) (, int) bool { return [].meta.txid < [].meta.txid }// removeTx removes a transaction from the database.func ( *DB) ( *Tx) {// Release the read lock on the mmap. .mmaplock.RUnlock()// Use the meta lock to restrict access to the DB object. .metalock.Lock()// Remove the transaction.for , := range .txs {if == { := len(.txs) - 1 .txs[] = .txs[] .txs[] = nil .txs = .txs[:]break } } := len(.txs)// Unlock the meta pages. .metalock.Unlock()// Merge statistics. .statlock.Lock() .stats.OpenTxN = .stats.TxStats.add(&.stats) .statlock.Unlock()}// Update executes a function within the context of a read-write managed transaction.// If no error is returned from the function then the transaction is committed.// If an error is returned then the entire transaction is rolled back.// Any error that is returned from the function or returned from the commit is// returned from the Update() method.//// Attempting to manually commit or rollback within the function will cause a panic.func ( *DB) ( func(*Tx) error) error { , := .Begin(true)if != nil {return }// Make sure the transaction rolls back in the event of a panic.deferfunc() {if .db != nil { .rollback() } }()// Mark as a managed tx so that the inner function cannot manually commit. .managed = true// If an error is returned from the function then rollback and return error. = () .managed = falseif != nil { _ = .Rollback()return }return .Commit()}// View executes a function within the context of a managed read-only transaction.// Any error that is returned from the function is returned from the View() method.//// Attempting to manually rollback within the function will cause a panic.func ( *DB) ( func(*Tx) error) error { , := .Begin(false)if != nil {return }// Make sure the transaction rolls back in the event of a panic.deferfunc() {if .db != nil { .rollback() } }()// Mark as a managed tx so that the inner function cannot manually rollback. .managed = true// If an error is returned from the function then pass it through. = () .managed = falseif != nil { _ = .Rollback()return }return .Rollback()}// Batch calls fn as part of a batch. It behaves similar to Update,// except://// 1. concurrent Batch calls can be combined into a single Bolt// transaction.//// 2. the function passed to Batch may be called multiple times,// regardless of whether it returns error or not.//// This means that Batch function side effects must be idempotent and// take permanent effect only after a successful return is seen in// caller.//// The maximum batch size and delay can be adjusted with DB.MaxBatchSize// and DB.MaxBatchDelay, respectively.//// Batch is only useful when there are multiple goroutines calling it.func ( *DB) ( func(*Tx) error) error { := make(chanerror, 1) .batchMu.Lock()if (.batch == nil) || (.batch != nil && len(.batch.calls) >= .MaxBatchSize) {// There is no existing batch, or the existing batch is full; start a new one. .batch = &batch{db: , } .batch.timer = time.AfterFunc(.MaxBatchDelay, .batch.trigger) } .batch.calls = append(.batch.calls, call{fn: , err: })iflen(.batch.calls) >= .MaxBatchSize {// wake up batch, it's ready to rungo .batch.trigger() } .batchMu.Unlock() := <-if == trySolo { = .Update() }return}type call struct { fn func(*Tx) error err chan<- error}type batch struct { db *DB timer *time.Timer start sync.Once calls []call}// trigger runs the batch if it hasn't already been run.func ( *batch) () { .start.Do(.run)}// run performs the transactions in the batch and communicates results// back to DB.Batch.func ( *batch) () { .db.batchMu.Lock() .timer.Stop()// Make sure no new work is added to this batch, but don't break // other batches.if .db.batch == { .db.batch = nil } .db.batchMu.Unlock():forlen(.calls) > 0 {var = -1 := .db.Update(func( *Tx) error {for , := range .calls {if := safelyCall(.fn, ); != nil { = return } }returnnil })if >= 0 {// take the failing transaction out of the batch. it's // safe to shorten b.calls here because db.batch no longer // points to us, and we hold the mutex anyway. := .calls[] .calls[], .calls = .calls[len(.calls)-1], .calls[:len(.calls)-1]// tell the submitter re-run it solo, continue with the rest of the batch .err <- trySolocontinue }// pass success, or bolt internal errors, to all callersfor , := range .calls { .err <- }break }}// trySolo is a special sentinel error value used for signaling that a// transaction function should be re-run. It should never be seen by// callers.var trySolo = errors.New("batch function returned an error and should be re-run solo")type panicked struct { reason interface{}}func ( panicked) () string {if , := .reason.(error); {return .Error() }returnfmt.Sprintf("panic: %v", .reason)}func safelyCall( func(*Tx) error, *Tx) ( error) {deferfunc() {if := recover(); != nil { = panicked{} } }()return ()}// Sync executes fdatasync() against the database file handle.//// This is not necessary under normal operation, however, if you use NoSync// then it allows you to force the database file to sync against the disk.func ( *DB) () error { returnfdatasync() }// Stats retrieves ongoing performance stats for the database.// This is only updated when a transaction closes.func ( *DB) () Stats { .statlock.RLock()defer .statlock.RUnlock()return .stats}// This is for internal access to the raw data bytes from the C cursor, use// carefully, or not at all.func ( *DB) () *Info {return &Info{uintptr(unsafe.Pointer(&.data[0])), .pageSize}}// page retrieves a page reference from the mmap based on the current page size.func ( *DB) ( pgid) *page { := * pgid(.pageSize)return (*page)(unsafe.Pointer(&.data[]))}// pageInBuffer retrieves a page reference from a given byte array based on the current page size.func ( *DB) ( []byte, pgid) *page {return (*page)(unsafe.Pointer(&[*pgid(.pageSize)]))}// meta retrieves the current meta page reference.func ( *DB) () *meta {// We have to return the meta with the highest txid which doesn't fail // validation. Otherwise, we can cause errors when in fact the database is // in a consistent state. metaA is the one with the higher txid. := .meta0 := .meta1if .meta1.txid > .meta0.txid { = .meta1 = .meta0 }// Use higher meta page if valid. Otherwise fallback to previous, if valid.if := .validate(); == nil {return } elseif := .validate(); == nil {return }// This should never be reached, because both meta1 and meta0 were validated // on mmap() and we do fsync() on every write.panic("bolt.DB.meta(): invalid meta pages")}// allocate returns a contiguous block of memory starting at a given page.func ( *DB) ( txid, int) (*page, error) {// Allocate a temporary buffer for the page.var []byteif == 1 { = .pagePool.Get().([]byte) } else { = make([]byte, *.pageSize) } := (*page)(unsafe.Pointer(&[0])) .overflow = uint32( - 1)// Use pages from the freelist if they are available.if .id = .freelist.allocate(, ); .id != 0 {return , nil }// Resize mmap() if we're at the end. .id = .rwtx.meta.pgidvar = int((.id+pgid())+1) * .pageSizeif >= .datasz {if := .mmap(); != nil {returnnil, fmt.Errorf("mmap allocate error: %s", ) } }// Move the page id high water mark. .rwtx.meta.pgid += pgid()return , nil}// grow grows the size of the database to the given sz.func ( *DB) ( int) error {// Ignore if the new size is less than available file size.if <= .filesz {returnnil }// If the data is smaller than the alloc size then only allocate what's needed. // Once it goes over the allocation size then allocate in chunks.if .datasz < .AllocSize { = .datasz } else { += .AllocSize }// Truncate and fsync to ensure file size metadata is flushed. // https://github.com/boltdb/bolt/issues/284if !.NoGrowSync && !.readOnly {ifruntime.GOOS != "windows" {if := .file.Truncate(int64()); != nil {returnfmt.Errorf("file resize error: %s", ) } }if := .file.Sync(); != nil {returnfmt.Errorf("file sync error: %s", ) }if .Mlock {// unlock old file and lock new oneif := .mrelock(.filesz, ); != nil {returnfmt.Errorf("mlock/munlock error: %s", ) } } } .filesz = returnnil}func ( *DB) () bool {return .readOnly}func ( *DB) () []pgid { , := .beginTx()deferfunc() { = .Rollback()if != nil {panic("freepages: failed to rollback tx") } }()if != nil {panic("freepages: failed to open read only tx") } := make(map[pgid]*page) := make(map[pgid]bool) := make(chanerror)gofunc() {for := range {panic(fmt.Sprintf("freepages: failed to get all reachable pages (%v)", )) } }() .checkBucket(&.root, , , )close()var []pgidfor := pgid(2); < .meta().pgid; ++ {if , := []; ! { = append(, ) } }return}// Options represents the options that can be set when opening a database.typeOptionsstruct {// Timeout is the amount of time to wait to obtain a file lock. // When set to zero it will wait indefinitely. This option is only // available on Darwin and Linux. Timeout time.Duration// Sets the DB.NoGrowSync flag before memory mapping the file. NoGrowSync bool// Do not sync freelist to disk. This improves the database write performance // under normal operation, but requires a full database re-sync during recovery. NoFreelistSync bool// FreelistType sets the backend freelist type. There are two options. Array which is simple but endures // dramatic performance degradation if database is large and framentation in freelist is common. // The alternative one is using hashmap, it is faster in almost all circumstances // but it doesn't guarantee that it offers the smallest page id available. In normal case it is safe. // The default type is array FreelistType FreelistType// Open database in read-only mode. Uses flock(..., LOCK_SH |LOCK_NB) to // grab a shared lock (UNIX). ReadOnly bool// Sets the DB.MmapFlags flag before memory mapping the file. MmapFlags int// InitialMmapSize is the initial mmap size of the database // in bytes. Read transactions won't block write transaction // if the InitialMmapSize is large enough to hold database mmap // size. (See DB.Begin for more information) // // If <=0, the initial map size is 0. // If initialMmapSize is smaller than the previous database size, // it takes no effect. InitialMmapSize int// PageSize overrides the default OS page size. PageSize int// NoSync sets the initial value of DB.NoSync. Normally this can just be // set directly on the DB itself when returned from Open(), but this option // is useful in APIs which expose Options but not the underlying DB. NoSync bool// OpenFile is used to open files. It defaults to os.OpenFile. This option // is useful for writing hermetic tests. OpenFile func(string, int, os.FileMode) (*os.File, error)// Mlock locks database file in memory when set to true. // It prevents potential page faults, however // used memory can't be reclaimed. (UNIX only) Mlock bool}// DefaultOptions represent the options used if nil options are passed into Open().// No timeout is used which will cause Bolt to wait indefinitely for a lock.varDefaultOptions = &Options{Timeout: 0,NoGrowSync: false,FreelistType: FreelistArrayType,}// Stats represents statistics about the database.typeStatsstruct {// Freelist stats FreePageN int// total number of free pages on the freelist PendingPageN int// total number of pending pages on the freelist FreeAlloc int// total bytes allocated in free pages FreelistInuse int// total bytes used by the freelist// Transaction stats TxN int// total number of started read transactions OpenTxN int// number of currently open read transactions TxStats TxStats// global, ongoing stats.}// Sub calculates and returns the difference between two sets of database stats.// This is useful when obtaining stats at two different points and time and// you need the performance counters that occurred within that time span.func ( *Stats) ( *Stats) Stats {if == nil {return * }varStats .FreePageN = .FreePageN .PendingPageN = .PendingPageN .FreeAlloc = .FreeAlloc .FreelistInuse = .FreelistInuse .TxN = .TxN - .TxN .TxStats = .TxStats.Sub(&.TxStats)return}typeInfostruct { Data uintptr PageSize int}type meta struct { magic uint32 version uint32 pageSize uint32 flags uint32 root bucket freelist pgid pgid pgid txid txid checksum uint64}// validate checks the marker bytes and version of the meta page to ensure it matches this binary.func ( *meta) () error {if .magic != magic {returnErrInvalid } elseif .version != version {returnErrVersionMismatch } elseif .checksum != 0 && .checksum != .sum64() {returnErrChecksum }returnnil}// copy copies one meta object to another.func ( *meta) ( *meta) { * = *}// write writes the meta onto a page.func ( *meta) ( *page) {if .root.root >= .pgid {panic(fmt.Sprintf("root bucket pgid (%d) above high water mark (%d)", .root.root, .pgid)) } elseif .freelist >= .pgid && .freelist != pgidNoFreelist {// TODO: reject pgidNoFreeList if !NoFreelistSyncpanic(fmt.Sprintf("freelist pgid (%d) above high water mark (%d)", .freelist, .pgid)) }// Page id is either going to be 0 or 1 which we can determine by the transaction ID. .id = pgid(.txid % 2) .flags |= metaPageFlag// Calculate the checksum. .checksum = .sum64() .copy(.meta())}// generates the checksum for the meta.func ( *meta) () uint64 {var = fnv.New64a() _, _ = .Write((*[unsafe.Offsetof(meta{}.checksum)]byte)(unsafe.Pointer())[:])return .Sum64()}// _assert will panic with a given formatted message if the given condition is false.func _assert( bool, string, ...interface{}) {if ! {panic(fmt.Sprintf("assertion failed: "+, ...)) }}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.