Files

308 lines
6.1 KiB
Go
Raw Permalink Normal View History

2023-06-01 18:11:37 +01:00
package memdb
2023-05-31 15:47:28 +01:00
import (
"io"
2025-07-22 22:41:29 +01:00
"strings"
2023-05-31 15:47:28 +01:00
"sync"
"time"
"github.com/ncruces/go-sqlite3"
2025-07-22 22:41:29 +01:00
"github.com/ncruces/go-sqlite3/util/vfsutil"
2023-06-01 18:11:37 +01:00
"github.com/ncruces/go-sqlite3/vfs"
2023-05-31 15:47:28 +01:00
)
const sectorSize = 65536
2023-06-01 18:11:37 +01:00
type memVFS struct{}
2023-05-31 15:47:28 +01:00
2023-06-01 18:11:37 +01:00
func (memVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) {
// For simplicity, we do not support reading or writing data
// across "sector" boundaries.
2025-07-22 22:41:29 +01:00
// This is not a problem for SQLite database files.
const databases = vfs.OPEN_MAIN_DB | vfs.OPEN_TEMP_DB | vfs.OPEN_TRANSIENT_DB
// Temp journals, as used by the sorter, use SliceFile.
if flags&vfs.OPEN_TEMP_JOURNAL != 0 {
return &vfsutil.SliceFile{}, flags | vfs.OPEN_MEMORY, nil
}
// Refuse to open all other file types.
// Returning OPEN_MEMORY means SQLite won't ask us to.
if flags&databases == 0 {
2024-07-20 12:52:25 +01:00
// notest // OPEN_MEMORY
2023-05-31 15:47:28 +01:00
return nil, flags, sqlite3.CANTOPEN
}
2024-05-20 01:04:53 +01:00
// A shared database has a name that begins with "/".
2025-07-22 22:41:29 +01:00
shared := strings.HasPrefix(name, "/")
2023-05-31 15:47:28 +01:00
2024-05-20 01:04:53 +01:00
var db *memDB
2023-05-31 15:47:28 +01:00
if shared {
2024-05-20 01:04:53 +01:00
name = name[1:]
2023-05-31 15:47:28 +01:00
memoryMtx.Lock()
defer memoryMtx.Unlock()
2024-05-20 01:04:53 +01:00
db = memoryDBs[name]
2023-05-31 15:47:28 +01:00
}
if db == nil {
2023-06-01 18:11:37 +01:00
if flags&vfs.OPEN_CREATE == 0 {
2023-05-31 15:47:28 +01:00
return nil, flags, sqlite3.CANTOPEN
}
2024-05-20 01:04:53 +01:00
db = &memDB{name: name}
2023-05-31 15:47:28 +01:00
}
if shared {
2024-05-20 01:04:53 +01:00
db.refs++ // +checklocksforce: memoryMtx is held
memoryDBs[name] = db
2023-05-31 15:47:28 +01:00
}
2023-06-01 18:11:37 +01:00
return &memFile{
memDB: db,
readOnly: flags&vfs.OPEN_READONLY != 0,
}, flags | vfs.OPEN_MEMORY, nil
2023-05-31 15:47:28 +01:00
}
2023-06-01 18:11:37 +01:00
func (memVFS) Delete(name string, dirSync bool) error {
2025-01-08 16:36:41 +00:00
return sqlite3.IOERR_DELETE_NOENT // used to delete journals
2023-05-31 15:47:28 +01:00
}
2023-06-01 18:11:37 +01:00
func (memVFS) Access(name string, flag vfs.AccessFlag) (bool, error) {
2025-01-08 16:36:41 +00:00
return false, nil // used to check for journals
2023-05-31 15:47:28 +01:00
}
2023-06-01 18:11:37 +01:00
func (memVFS) FullPathname(name string) (string, error) {
2023-05-31 15:47:28 +01:00
return name, nil
}
2023-06-01 18:11:37 +01:00
type memDB struct {
2024-05-20 01:04:53 +01:00
name string
2025-07-21 13:20:30 +01:00
// +checklocks:lockMtx
waiter *sync.Cond
2023-05-31 15:47:28 +01:00
// +checklocks:dataMtx
data []*[sectorSize]byte
2024-10-28 13:25:09 +00:00
2025-07-21 13:20:30 +01:00
size int64 // +checklocks:dataMtx
refs int32 // +checklocks:memoryMtx
shared int32 // +checklocks:lockMtx
pending bool // +checklocks:lockMtx
reserved bool // +checklocks:lockMtx
2024-05-20 01:04:53 +01:00
2023-05-31 15:47:28 +01:00
lockMtx sync.Mutex
dataMtx sync.RWMutex
}
2024-05-20 01:04:53 +01:00
func (m *memDB) release() {
memoryMtx.Lock()
if m.refs--; m.refs == 0 && m == memoryDBs[m.name] {
delete(memoryDBs, m.name)
}
2025-12-19 16:37:47 +00:00
memoryMtx.Unlock()
2024-05-20 01:04:53 +01:00
}
2023-06-01 18:11:37 +01:00
type memFile struct {
*memDB
lock vfs.LockLevel
2023-05-31 15:47:28 +01:00
readOnly bool
}
var (
// Ensure these interfaces are implemented:
2023-06-01 18:11:37 +01:00
_ vfs.FileLockState = &memFile{}
_ vfs.FileSizeHint = &memFile{}
2023-05-31 15:47:28 +01:00
)
2023-06-01 18:11:37 +01:00
func (m *memFile) Close() error {
2024-05-20 01:04:53 +01:00
m.release()
2023-06-01 18:11:37 +01:00
return m.Unlock(vfs.LOCK_NONE)
2023-05-31 15:47:28 +01:00
}
2023-06-01 18:11:37 +01:00
func (m *memFile) ReadAt(b []byte, off int64) (n int, err error) {
2023-05-31 15:47:28 +01:00
m.dataMtx.RLock()
defer m.dataMtx.RUnlock()
if off >= m.size {
return 0, io.EOF
}
base := off / sectorSize
rest := off % sectorSize
have := int64(sectorSize)
2025-07-21 13:20:30 +01:00
if m.size < off+int64(len(b)) {
2023-05-31 15:47:28 +01:00
have = modRoundUp(m.size, sectorSize)
}
n = copy(b, (*m.data[base])[rest:have])
if n < len(b) {
2024-07-20 01:42:50 +01:00
// notest // assume reads are page aligned
2023-05-31 15:47:28 +01:00
return 0, io.ErrNoProgress
}
return n, nil
}
2023-06-01 18:11:37 +01:00
func (m *memFile) WriteAt(b []byte, off int64) (n int, err error) {
2023-05-31 15:47:28 +01:00
m.dataMtx.Lock()
defer m.dataMtx.Unlock()
base := off / sectorSize
rest := off % sectorSize
for base >= int64(len(m.data)) {
m.data = append(m.data, new([sectorSize]byte))
}
n = copy((*m.data[base])[rest:], b)
2025-07-21 13:20:30 +01:00
if size := off + int64(n); size > m.size {
m.size = size
}
2023-05-31 15:47:28 +01:00
if n < len(b) {
2024-07-20 01:42:50 +01:00
// notest // assume writes are page aligned
2023-09-20 15:07:07 +01:00
return n, io.ErrShortWrite
2023-05-31 15:47:28 +01:00
}
return n, nil
}
2025-07-22 22:41:29 +01:00
func (m *memFile) Size() (int64, error) {
m.dataMtx.RLock()
defer m.dataMtx.RUnlock()
return m.size, nil
}
2023-06-01 18:11:37 +01:00
func (m *memFile) Truncate(size int64) error {
2023-05-31 15:47:28 +01:00
m.dataMtx.Lock()
defer m.dataMtx.Unlock()
return m.truncate(size)
}
2025-07-22 22:41:29 +01:00
func (m *memFile) SizeHint(size int64) error {
m.dataMtx.Lock()
defer m.dataMtx.Unlock()
if size > m.size {
return m.truncate(size)
}
return nil
}
2023-05-31 15:47:28 +01:00
// +checklocks:m.dataMtx
2023-06-01 18:11:37 +01:00
func (m *memFile) truncate(size int64) error {
2023-05-31 15:47:28 +01:00
if size < m.size {
base := size / sectorSize
rest := size % sectorSize
if rest != 0 {
clear((*m.data[base])[rest:])
}
}
sectors := divRoundUp(size, sectorSize)
for sectors > int64(len(m.data)) {
m.data = append(m.data, new([sectorSize]byte))
}
clear(m.data[sectors:])
m.data = m.data[:sectors]
m.size = size
return nil
}
2023-06-01 18:11:37 +01:00
func (m *memFile) Lock(lock vfs.LockLevel) error {
2023-05-31 15:47:28 +01:00
if m.lock >= lock {
return nil
}
2023-06-01 18:11:37 +01:00
if m.readOnly && lock >= vfs.LOCK_RESERVED {
2023-05-31 15:47:28 +01:00
return sqlite3.IOERR_LOCK
}
m.lockMtx.Lock()
defer m.lockMtx.Unlock()
switch lock {
2023-06-01 18:11:37 +01:00
case vfs.LOCK_SHARED:
2024-06-11 22:57:29 +01:00
if m.pending {
2023-06-12 13:04:37 +01:00
return sqlite3.BUSY
2023-05-31 15:47:28 +01:00
}
m.shared++
2023-06-01 18:11:37 +01:00
case vfs.LOCK_RESERVED:
2024-06-11 22:57:29 +01:00
if m.reserved {
2023-05-31 15:47:28 +01:00
return sqlite3.BUSY
}
2024-06-11 22:57:29 +01:00
m.reserved = true
2023-05-31 15:47:28 +01:00
2023-06-01 18:11:37 +01:00
case vfs.LOCK_EXCLUSIVE:
2025-11-28 11:21:26 +00:00
if m.lock == vfs.LOCK_RESERVED {
2023-06-01 18:11:37 +01:00
m.lock = vfs.LOCK_PENDING
2024-06-11 22:57:29 +01:00
m.pending = true
2023-05-31 15:47:28 +01:00
}
2025-07-15 18:58:29 +01:00
if m.shared > 1 {
before := time.Now()
if m.waiter == nil {
m.waiter = sync.NewCond(&m.lockMtx)
}
defer time.AfterFunc(time.Millisecond, m.waiter.Broadcast).Stop()
for m.shared > 1 {
if time.Since(before) > time.Millisecond {
return sqlite3.BUSY
}
m.waiter.Wait()
2023-05-31 15:47:28 +01:00
}
}
}
m.lock = lock
return nil
}
2023-06-01 18:11:37 +01:00
func (m *memFile) Unlock(lock vfs.LockLevel) error {
2023-05-31 15:47:28 +01:00
if m.lock <= lock {
return nil
}
m.lockMtx.Lock()
defer m.lockMtx.Unlock()
2024-10-28 13:25:09 +00:00
if m.lock >= vfs.LOCK_RESERVED {
2024-06-11 22:57:29 +01:00
m.reserved = false
2023-05-31 15:47:28 +01:00
}
2024-10-28 13:25:09 +00:00
if m.lock >= vfs.LOCK_PENDING {
m.pending = false
}
2023-06-01 18:11:37 +01:00
if lock < vfs.LOCK_SHARED {
2025-07-15 18:58:29 +01:00
if m.shared--; m.pending && m.shared <= 1 && m.waiter != nil {
m.waiter.Broadcast()
}
2023-05-31 15:47:28 +01:00
}
m.lock = lock
return nil
}
2023-06-01 18:11:37 +01:00
func (m *memFile) CheckReservedLock() (bool, error) {
2024-07-20 12:52:25 +01:00
// notest // OPEN_MEMORY
2023-06-01 18:11:37 +01:00
if m.lock >= vfs.LOCK_RESERVED {
2023-05-31 15:47:28 +01:00
return true, nil
}
m.lockMtx.Lock()
defer m.lockMtx.Unlock()
2024-06-11 22:57:29 +01:00
return m.reserved, nil
2023-05-31 15:47:28 +01:00
}
2025-07-22 22:41:29 +01:00
func (m *memFile) LockState() vfs.LockLevel {
return m.lock
}
func (*memFile) Sync(flag vfs.SyncFlag) error { return nil }
func (*memFile) SectorSize() int {
2024-07-20 12:52:25 +01:00
// notest // IOCAP_POWERSAFE_OVERWRITE
2023-05-31 15:47:28 +01:00
return sectorSize
}
2025-07-22 22:41:29 +01:00
func (*memFile) DeviceCharacteristics() vfs.DeviceCharacteristic {
2023-06-01 18:11:37 +01:00
return vfs.IOCAP_ATOMIC |
vfs.IOCAP_SEQUENTIAL |
vfs.IOCAP_SAFE_APPEND |
vfs.IOCAP_POWERSAFE_OVERWRITE
2023-05-31 15:47:28 +01:00
}
func divRoundUp(a, b int64) int64 {
return (a + b - 1) / b
}
func modRoundUp(a, b int64) int64 {
return b - (b-a%b)%b
}