Skip to content

Commit

Permalink
VFS API refactor.
Browse files Browse the repository at this point in the history
  • Loading branch information
ncruces committed Oct 21, 2024
1 parent 9eec439 commit 21de004
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 144 deletions.
104 changes: 63 additions & 41 deletions util/vfsutil/wrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,28 @@ import (
"github.com/ncruces/go-sqlite3/vfs"
)

// WrapSharedMemory helps wrap [vfs.FileSharedMemory].
func WrapSharedMemory(f vfs.File) vfs.SharedMemory {
if f, ok := f.(vfs.FileSharedMemory); ok {
return f.SharedMemory()
}
return nil
}

// WrapChunkSize helps wrap [vfs.FileChunkSize].
func WrapChunkSize(f vfs.File, size int) {
if f, ok := f.(vfs.FileChunkSize); ok {
f.ChunkSize(size)
// UnwrapFile unwraps a [vfs.File],
// possibly implementing [vfs.FileUnwrap],
// to a concrete type.
func UnwrapFile[T vfs.File](f vfs.File) (_ T, _ bool) {
for {
switch t := f.(type) {
default:
return
case T:
return t, true
case vfs.FileUnwrap:
f = t.Unwrap()
}
}
}

// WrapSizeHint helps wrap [vfs.FileSizeHint].
func WrapSizeHint(f vfs.File, size int64) error {
if f, ok := f.(vfs.FileSizeHint); ok {
return f.SizeHint(size)
// WrapLockState helps wrap [vfs.FileLockState].
func WrapLockState(f vfs.File) vfs.LockLevel {
if f, ok := f.(vfs.FileLockState); ok {
return f.LockState()
}
return sqlite3.NOTFOUND
}

// WrapHasMoved helps wrap [vfs.FileHasMoved].
func WrapHasMoved(f vfs.File) (bool, error) {
if f, ok := f.(vfs.FileHasMoved); ok {
return f.HasMoved()
}
return false, sqlite3.NOTFOUND
}

// WrapOverwrite helps wrap [vfs.FileOverwrite].
func WrapOverwrite(f vfs.File) error {
if f, ok := f.(vfs.FileOverwrite); ok {
return f.Overwrite()
}
return sqlite3.NOTFOUND
return vfs.LOCK_EXCLUSIVE + 1 // UNKNOWN_LOCK
}

// WrapPersistentWAL helps wrap [vfs.FilePersistentWAL].
Expand Down Expand Up @@ -75,6 +60,37 @@ func WrapSetPowersafeOverwrite(f vfs.File, psow bool) {
}
}

// WrapChunkSize helps wrap [vfs.FileChunkSize].
func WrapChunkSize(f vfs.File, size int) {
if f, ok := f.(vfs.FileChunkSize); ok {
f.ChunkSize(size)
}
}

// WrapSizeHint helps wrap [vfs.FileSizeHint].
func WrapSizeHint(f vfs.File, size int64) error {
if f, ok := f.(vfs.FileSizeHint); ok {
return f.SizeHint(size)
}
return sqlite3.NOTFOUND
}

// WrapHasMoved helps wrap [vfs.FileHasMoved].
func WrapHasMoved(f vfs.File) (bool, error) {
if f, ok := f.(vfs.FileHasMoved); ok {
return f.HasMoved()
}
return false, sqlite3.NOTFOUND
}

// WrapOverwrite helps wrap [vfs.FileOverwrite].
func WrapOverwrite(f vfs.File) error {
if f, ok := f.(vfs.FileOverwrite); ok {
return f.Overwrite()
}
return sqlite3.NOTFOUND
}

// WrapCommitPhaseTwo helps wrap [vfs.FileCommitPhaseTwo].
func WrapCommitPhaseTwo(f vfs.File) error {
if f, ok := f.(vfs.FileCommitPhaseTwo); ok {
Expand Down Expand Up @@ -107,20 +123,18 @@ func WrapRollbackAtomicWrite(f vfs.File) error {
return sqlite3.NOTFOUND
}

// WrapCheckpointDone helps wrap [vfs.FileCheckpoint].
func WrapCheckpointDone(f vfs.File) error {
// WrapCheckpointStart helps wrap [vfs.FileCheckpoint].
func WrapCheckpointStart(f vfs.File) {
if f, ok := f.(vfs.FileCheckpoint); ok {
return f.CheckpointDone()
f.CheckpointStart()
}
return sqlite3.NOTFOUND
}

// WrapCheckpointStart helps wrap [vfs.FileCheckpoint].
func WrapCheckpointStart(f vfs.File) error {
// WrapCheckpointDone helps wrap [vfs.FileCheckpoint].
func WrapCheckpointDone(f vfs.File) {
if f, ok := f.(vfs.FileCheckpoint); ok {
return f.CheckpointStart()
f.CheckpointDone()
}
return sqlite3.NOTFOUND
}

// WrapPragma helps wrap [vfs.FilePragma].
Expand All @@ -130,3 +144,11 @@ func WrapPragma(f vfs.File, name, value string) (string, error) {
}
return "", sqlite3.NOTFOUND
}

// WrapSharedMemory helps wrap [vfs.FileSharedMemory].
func WrapSharedMemory(f vfs.File) vfs.SharedMemory {
if f, ok := f.(vfs.FileSharedMemory); ok {
return f.SharedMemory()
}
return nil
}
76 changes: 47 additions & 29 deletions vfs/adiantum/hbsh.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (h *hbshVFS) OpenFilename(name *vfs.Filename, flags vfs.OpenFlag) (file vfs
}

var hbsh *hbsh.HBSH
if f, ok := name.DatabaseFile().(*hbshFile); ok {
if f, ok := vfsutil.UnwrapFile[*hbshFile](name.DatabaseFile()); ok {
hbsh = f.hbsh
} else {
var key []byte
Expand Down Expand Up @@ -71,6 +71,17 @@ const (
blockSize = 4096
)

// Ensure blockSize is a power of two.
var _ [0]struct{} = [blockSize & (blockSize - 1)]struct{}{}

func roundDown(i int64) int64 {
return i &^ (blockSize - 1)
}

func roundUp[T int | int64](i T) T {
return (i + (blockSize - 1)) &^ (blockSize - 1)
}

type hbshFile struct {
vfs.File
init HBSHCreator
Expand Down Expand Up @@ -111,8 +122,8 @@ func (h *hbshFile) ReadAt(p []byte, off int64) (n int, err error) {
return 0, sqlite3.CANTOPEN
}

min := (off) &^ (blockSize - 1) // round down
max := (off + int64(len(p)) + (blockSize - 1)) &^ (blockSize - 1) // round up
min := roundDown(off)
max := roundUp(off + int64(len(p)))

// Read one block at a time.
for ; min < max; min += blockSize {
Expand Down Expand Up @@ -141,8 +152,8 @@ func (h *hbshFile) WriteAt(p []byte, off int64) (n int, err error) {
return 0, sqlite3.READONLY
}

min := (off) &^ (blockSize - 1) // round down
max := (off + int64(len(p)) + (blockSize - 1)) &^ (blockSize - 1) // round up
min := roundDown(off)
max := roundUp(off + int64(len(p)))

// Write one block at a time.
for ; min < max; min += blockSize {
Expand Down Expand Up @@ -187,45 +198,44 @@ func (h *hbshFile) WriteAt(p []byte, off int64) (n int, err error) {
return n, nil
}

func (h *hbshFile) Truncate(size int64) error {
size = (size + (blockSize - 1)) &^ (blockSize - 1) // round up
return h.File.Truncate(size)
}

func (h *hbshFile) SectorSize() int {
return util.LCM(h.File.SectorSize(), blockSize)
}

func (h *hbshFile) DeviceCharacteristics() vfs.DeviceCharacteristic {
var _ [0]struct{} = [blockSize - 4096]struct{}{} // Ensure blockSize is 4K.
return h.File.DeviceCharacteristics() & (0 |
// The only safe flags are these:
vfs.IOCAP_ATOMIC4K |
vfs.IOCAP_UNDELETABLE_WHEN_OPEN |
vfs.IOCAP_IMMUTABLE |
vfs.IOCAP_BATCH_ATOMIC)
}

// Wrap optional methods.
func (h *hbshFile) SectorSize() int {
return util.LCM(h.File.SectorSize(), blockSize)
}

func (h *hbshFile) SharedMemory() vfs.SharedMemory {
return vfsutil.WrapSharedMemory(h.File)
func (h *hbshFile) Truncate(size int64) error {
return h.File.Truncate(roundUp(size))
}

func (h *hbshFile) ChunkSize(size int) {
size = (size + (blockSize - 1)) &^ (blockSize - 1) // round up
vfsutil.WrapChunkSize(h.File, size)
vfsutil.WrapChunkSize(h.File, roundUp(size))
}

func (h *hbshFile) SizeHint(size int64) error {
size = (size + (blockSize - 1)) &^ (blockSize - 1) // round up
return vfsutil.WrapSizeHint(h.File, size)
return vfsutil.WrapSizeHint(h.File, roundUp(size))
}

func (h *hbshFile) HasMoved() (bool, error) {
return vfsutil.WrapHasMoved(h.File) // notest
func (h *hbshFile) Unwrap() vfs.File {
return h.File
}

func (h *hbshFile) Overwrite() error {
return vfsutil.WrapOverwrite(h.File) // notest
func (h *hbshFile) SharedMemory() vfs.SharedMemory {
return vfsutil.WrapSharedMemory(h.File)
}

// Wrap optional methods.

func (h *hbshFile) LockState() vfs.LockLevel {
return vfsutil.WrapLockState(h.File) // notest
}

func (h *hbshFile) PersistentWAL() bool {
Expand All @@ -236,6 +246,14 @@ func (h *hbshFile) SetPersistentWAL(keepWAL bool) {
vfsutil.WrapSetPersistentWAL(h.File, keepWAL) // notest
}

func (h *hbshFile) HasMoved() (bool, error) {
return vfsutil.WrapHasMoved(h.File) // notest
}

func (h *hbshFile) Overwrite() error {
return vfsutil.WrapOverwrite(h.File) // notest
}

func (h *hbshFile) CommitPhaseTwo() error {
return vfsutil.WrapCommitPhaseTwo(h.File) // notest
}
Expand All @@ -252,10 +270,10 @@ func (h *hbshFile) RollbackAtomicWrite() error {
return vfsutil.WrapRollbackAtomicWrite(h.File) // notest
}

func (h *hbshFile) CheckpointDone() error {
return vfsutil.WrapCheckpointDone(h.File) // notest
func (h *hbshFile) CheckpointStart() {
vfsutil.WrapCheckpointStart(h.File) // notest
}

func (h *hbshFile) CheckpointStart() error {
return vfsutil.WrapCheckpointStart(h.File) // notest
func (h *hbshFile) CheckpointDone() {
vfsutil.WrapCheckpointDone(h.File) // notest
}
69 changes: 38 additions & 31 deletions vfs/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ type File interface {
DeviceCharacteristics() DeviceCharacteristic
}

// FileUnwrap should be implemented by a File
// that wraps another File implementation.
type FileUnwrap interface {
File
Unwrap() File
}

// FileLockState extends File to implement the
// SQLITE_FCNTL_LOCKSTATE file control opcode.
//
Expand All @@ -58,6 +65,26 @@ type FileLockState interface {
LockState() LockLevel
}

// FilePersistentWAL extends File to implement the
// SQLITE_FCNTL_PERSIST_WAL file control opcode.
//
// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpersistwal
type FilePersistentWAL interface {
File
PersistentWAL() bool
SetPersistentWAL(bool)
}

// FilePowersafeOverwrite extends File to implement the
// SQLITE_FCNTL_POWERSAFE_OVERWRITE file control opcode.
//
// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpowersafeoverwrite
type FilePowersafeOverwrite interface {
File
PowersafeOverwrite() bool
SetPowersafeOverwrite(bool)
}

// FileChunkSize extends File to implement the
// SQLITE_FCNTL_CHUNK_SIZE file control opcode.
//
Expand Down Expand Up @@ -94,26 +121,6 @@ type FileOverwrite interface {
Overwrite() error
}

// FilePersistentWAL extends File to implement the
// SQLITE_FCNTL_PERSIST_WAL file control opcode.
//
// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpersistwal
type FilePersistentWAL interface {
File
PersistentWAL() bool
SetPersistentWAL(bool)
}

// FilePowersafeOverwrite extends File to implement the
// SQLITE_FCNTL_POWERSAFE_OVERWRITE file control opcode.
//
// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpowersafeoverwrite
type FilePowersafeOverwrite interface {
File
PowersafeOverwrite() bool
SetPowersafeOverwrite(bool)
}

// FileCommitPhaseTwo extends File to implement the
// SQLITE_FCNTL_COMMIT_PHASETWO file control opcode.
//
Expand All @@ -135,24 +142,24 @@ type FileBatchAtomicWrite interface {
RollbackAtomicWrite() error
}

// FilePragma extends File to implement the
// SQLITE_FCNTL_PRAGMA file control opcode.
//
// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpragma
type FilePragma interface {
File
Pragma(name, value string) (string, error)
}

// FileCheckpoint extends File to implement the
// SQLITE_FCNTL_CKPT_START and SQLITE_FCNTL_CKPT_DONE
// file control opcodes.
//
// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlckptstart
type FileCheckpoint interface {
File
CheckpointDone() error
CheckpointStart() error
CheckpointStart()
CheckpointDone()
}

// FilePragma extends File to implement the
// SQLITE_FCNTL_PRAGMA file control opcode.
//
// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpragma
type FilePragma interface {
File
Pragma(name, value string) (string, error)
}

// FileSharedMemory extends File to possibly implement
Expand Down
Loading

0 comments on commit 21de004

Please sign in to comment.