aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2025-01-23 12:36:19 -0300
committerEuAndreh <eu@euandre.org>2025-01-23 12:36:19 -0300
commitc13c4625dae8ff64f540caf08df425729eaf804a (patch)
treef6521318fba3b651a3863c3e35a4fbcef63f4c87
parentsrc/dedo.go: Instantiate &DB{} only after opening the file; simplify Open() (diff)
downloaddedo-c13c4625dae8ff64f540caf08df425729eaf804a.tar.gz
dedo-c13c4625dae8ff64f540caf08df425729eaf804a.tar.xz
src/dedo.go: Simplify initDB()
-rw-r--r--src/dedo.go172
-rw-r--r--tests/dedo.go67
2 files changed, 129 insertions, 110 deletions
diff --git a/src/dedo.go b/src/dedo.go
index 416d575..a7bc629 100644
--- a/src/dedo.go
+++ b/src/dedo.go
@@ -1729,16 +1729,15 @@ func (db *DB) String() string {
}
func newDB(path string, file *os.File) *DB {
- db := &DB{
+ return &DB{
MaxBatchSize: DefaultMaxBatchSize,
MaxBatchDelay: DefaultMaxBatchDelay,
AllocSize: DefaultAllocSize,
opened: true,
path: path,
file: file,
- ops: dbops{file.WriteAt},
+ ops: dbops{writeAt: file.WriteAt},
}
- return db
}
func openFile(path string) (*os.File, error) {
@@ -1760,9 +1759,82 @@ func openFile(path string) (*os.File, error) {
return os.OpenFile(path, flagsRO, fileMode)
}
+func readPageSize(db *DB) (int, error) {
+ // Read the first meta page to determine the page size.
+ const size4KiB = 0x1000
+ buf := [0x1000]byte{}
+ _, err := db.file.ReadAt(buf[:], 0)
+ if err != nil {
+ return 0, err
+ }
+
+ m := pageInBuffer(db, buf[:], 0).meta()
+ err = m.validate()
+ if err != nil {
+ return 0, err
+ }
+
+ return int(m.pageSize), nil
+}
+
+// init creates a new database file and initializes its meta pages.
+func initDB(db *DB, size int64) error {
+ if size != 0 {
+ pageSize, err := readPageSize(db)
+ if err != nil {
+ return err
+ }
+
+ db.pageSize = pageSize
+ return nil
+ }
+ // Set the page size to the OS page size.
+ db.pageSize = os.Getpagesize()
+
+ // Create two meta pages on a buffer.
+ buf := make([]byte, db.pageSize*4)
+ for i := 0; i < 2; i++ {
+ p := pageInBuffer(db, buf[:], pgid(i))
+ p.id = pgid(i)
+ p.flags = metaPageFlag
+
+ // Initialize the meta page.
+ m := p.meta()
+ m.magic = magic
+ m.version = version
+ m.pageSize = uint32(db.pageSize)
+ m.freelist = 2
+ m.root = bucket{root: 3}
+ m.pgid = 4
+ m.txid = txid(i)
+ m.checksum = m.sum64()
+ }
+
+ // Write an empty freelist at page 3.
+ p := pageInBuffer(db, buf[:], pgid(2))
+ p.id = pgid(2)
+ p.flags = freelistPageFlag
+ p.count = 0
+
+ // Write an empty leaf page at page 4.
+ p = pageInBuffer(db, buf[:], pgid(3))
+ p.id = pgid(3)
+ p.flags = leafPageFlag
+ p.count = 0
+
+ // Write the buffer to our data file.
+ if _, err := db.ops.writeAt(buf, 0); err != nil {
+ return err
+ }
+ if err := fdatasync(db); err != nil {
+ return err
+ }
+
+ return nil
+}
+
// 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 Open(path string) (*DB, error) {
file, err := openFile(path)
if err != nil {
@@ -1776,14 +1848,9 @@ func Open(path string) (*DB, error) {
}
db := newDB(path, file)
- // 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 err := flock(db); err != nil {
+
+ err = flock(db)
+ if err != nil {
_ = db.close()
return nil, err
}
@@ -1801,7 +1868,8 @@ func Open(path string) (*DB, error) {
}
// Memory map the data file.
- if err := db.mmap(0); err != nil {
+ err = db.mmap(0)
+ if err != nil {
_ = db.close()
return nil, err
}
@@ -1913,80 +1981,6 @@ func (db *DB) mmapSize(size int) (int, error) {
return int(sz), nil
}
-func readPageSize(db *DB) (int, error) {
- // Read the first meta page to determine the page size.
- var buf [0x1000]byte
- _, err := db.file.ReadAt(buf[:], 0)
- if err != nil {
- return 0, err
- }
-
- m := db.pageInBuffer(buf[:], 0).meta()
- err = m.validate()
- if err != nil {
- // return 0, err
- return os.Getpagesize(), nil
- }
-
- return int(m.pageSize), nil
-}
-
-// init creates a new database file and initializes its meta pages.
-func initDB(db *DB, size int64) error {
- if size != 0 {
- pageSize, err := readPageSize(db)
- if err != nil {
- return err
- }
-
- db.pageSize = pageSize
- return nil
- }
- // Set the page size to the OS page size.
- db.pageSize = os.Getpagesize()
-
- // Create two meta pages on a buffer.
- buf := make([]byte, db.pageSize*4)
- for i := 0; i < 2; i++ {
- p := db.pageInBuffer(buf[:], pgid(i))
- p.id = pgid(i)
- p.flags = metaPageFlag
-
- // Initialize the meta page.
- m := p.meta()
- m.magic = magic
- m.version = version
- m.pageSize = uint32(db.pageSize)
- m.freelist = 2
- m.root = bucket{root: 3}
- m.pgid = 4
- m.txid = txid(i)
- m.checksum = m.sum64()
- }
-
- // Write an empty freelist at page 3.
- p := db.pageInBuffer(buf[:], pgid(2))
- p.id = pgid(2)
- p.flags = freelistPageFlag
- p.count = 0
-
- // Write an empty leaf page at page 4.
- p = db.pageInBuffer(buf[:], pgid(3))
- p.id = pgid(3)
- p.flags = leafPageFlag
- p.count = 0
-
- // Write the buffer to our data file.
- if _, err := db.ops.writeAt(buf, 0); err != nil {
- return err
- }
- if err := fdatasync(db); err != nil {
- return err
- }
-
- return nil
-}
-
// Close releases all database resources.
// All transactions must be closed before closing the database.
func (db *DB) Close() error {
@@ -2356,7 +2350,7 @@ func (db *DB) page(id pgid) *page {
}
// pageInBuffer retrieves a page reference from a given byte array based on the current page size.
-func (db *DB) pageInBuffer(b []byte, id pgid) *page {
+func pageInBuffer(db *DB, b []byte, id pgid) *page {
return (*page)(unsafe.Pointer(&b[id*pgid(db.pageSize)]))
}
@@ -4003,7 +3997,7 @@ func (tx *Tx) write() error {
func (tx *Tx) writeMeta() error {
// Create a temporary buffer for the meta page.
buf := make([]byte, tx.db.pageSize)
- p := tx.db.pageInBuffer(buf, 0)
+ p := pageInBuffer(tx.db, buf, 0)
tx.meta.write(p)
// Write the meta page to file.
diff --git a/tests/dedo.go b/tests/dedo.go
index 0def91d..1ab2b61 100644
--- a/tests/dedo.go
+++ b/tests/dedo.go
@@ -34,6 +34,25 @@ import (
func test_newDB() {
g.TestStart("newDB()")
+ g.Testing("different path are given back", func() {
+ db1 := newDB("path1", nil)
+ db2 := newDB("path2", nil)
+
+ g.TAssertEqual(db1.path, "path1")
+ g.TAssertEqual(db2.path, "path2")
+ })
+
+ g.Testing("diferent *os.File pointers", func() {
+ f1 := new(os.File)
+ f2 := new(os.File)
+
+ db1 := newDB("path", f1)
+ db2 := newDB("path", f2)
+
+ g.TAssertEqual(db1.ops.writeAt == nil, false)
+ g.TAssertEqual(db2.ops.writeAt == nil, false)
+ g.TAssertEqual(db1 == db2, false)
+ })
}
func test_openFile() {
@@ -81,6 +100,29 @@ func test_openFile() {
})
}
+func test_readPageSize() {
+ g.TestStart("readPageSize()")
+
+ g.Testing("empty page gives us EOF", func() {
+ })
+ g.Testing("page smaller than 4KiB also gives us EOF", func() { })
+ g.Testing("zeroed 4KiB page gives us ErrInvalid", func() {
+ })
+}
+
+func test_initDB() {
+ g.TestStart("initDB()")
+}
+
+func test_Open() {
+ g.TestStart("Open()")
+}
+
+// Ensure that opening a file that is not a Bolt database returns ErrInvalid.
+func TestOpen_ErrInvalid(t *testing.T) {
+ return
+}
+
// Ensure that a bucket that gets a non-existent key returns nil.
func TestBucket_Get_NonExistent(t *testing.T) {
db := MustOpenDB()
@@ -2884,27 +2926,6 @@ func TestOpen_ErrNotExists(t *testing.T) {
}
}
-// Ensure that opening a file that is not a Bolt database returns ErrInvalid.
-func TestOpen_ErrInvalid(t *testing.T) {
- path := tempfile()
- defer os.Remove(path)
-
- f, err := os.Create(path)
- if err != nil {
- t.Fatal(err)
- }
- if _, err := fmt.Fprintln(f, "this is not a bolt database"); err != nil {
- t.Fatal(err)
- }
- if err := f.Close(); err != nil {
- t.Fatal(err)
- }
-
- if _, err := Open(path); err != ErrInvalid {
- t.Fatalf("unexpected error: %s", err)
- }
-}
-
// Ensure that opening a file with two invalid versions returns ErrVersionMismatch.
func TestOpen_ErrVersionMismatch(t *testing.T) {
if pageSize != os.Getpagesize() {
@@ -6264,7 +6285,11 @@ func walkBucket(parent *Bucket, k []byte, v []byte, w io.Writer) error {
func MainTest() {
tempdir = getTempdir()
+ test_newDB()
test_openFile()
+ test_readPageSize()
+ test_initDB()
+ test_Open()
tests := []testing.InternalTest{
{ "TestBucket_Get_NonExistent", TestBucket_Get_NonExistent },