mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 20:52:27 -06:00 
			
		
		
		
	- codeberg.org/gruf/go-ffmpreg: v0.6.9 -> v0.6.10
- github.com/ncruces/go-sqlite3: v0.27.1 -> v0.28.0
- github.com/stretchr/testify: v1.10.0 -> v1.11.1
- github.com/tdewolff/minify/v2 v2.23.11 -> v2.24.2
- go.opentelemetry.io/otel{,/*}: v1.37.0 -> v1.38.0
- go.opentelemetry.io/contrib/*: v0.62.0 -> v0.63.0
Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4406
Co-authored-by: kim <grufwub@gmail.com>
Co-committed-by: kim <grufwub@gmail.com>
		
	
			
		
			
				
	
	
		
			307 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			307 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package memdb
 | 
						|
 | 
						|
import (
 | 
						|
	"io"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/ncruces/go-sqlite3"
 | 
						|
	"github.com/ncruces/go-sqlite3/util/vfsutil"
 | 
						|
	"github.com/ncruces/go-sqlite3/vfs"
 | 
						|
)
 | 
						|
 | 
						|
const sectorSize = 65536
 | 
						|
 | 
						|
type memVFS struct{}
 | 
						|
 | 
						|
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.
 | 
						|
	// 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 {
 | 
						|
		// notest // OPEN_MEMORY
 | 
						|
		return nil, flags, sqlite3.CANTOPEN
 | 
						|
	}
 | 
						|
 | 
						|
	// A shared database has a name that begins with "/".
 | 
						|
	shared := strings.HasPrefix(name, "/")
 | 
						|
 | 
						|
	var db *memDB
 | 
						|
	if shared {
 | 
						|
		name = name[1:]
 | 
						|
		memoryMtx.Lock()
 | 
						|
		defer memoryMtx.Unlock()
 | 
						|
		db = memoryDBs[name]
 | 
						|
	}
 | 
						|
	if db == nil {
 | 
						|
		if flags&vfs.OPEN_CREATE == 0 {
 | 
						|
			return nil, flags, sqlite3.CANTOPEN
 | 
						|
		}
 | 
						|
		db = &memDB{name: name}
 | 
						|
	}
 | 
						|
	if shared {
 | 
						|
		db.refs++ // +checklocksforce: memoryMtx is held
 | 
						|
		memoryDBs[name] = db
 | 
						|
	}
 | 
						|
 | 
						|
	return &memFile{
 | 
						|
		memDB:    db,
 | 
						|
		readOnly: flags&vfs.OPEN_READONLY != 0,
 | 
						|
	}, flags | vfs.OPEN_MEMORY, nil
 | 
						|
}
 | 
						|
 | 
						|
func (memVFS) Delete(name string, dirSync bool) error {
 | 
						|
	return sqlite3.IOERR_DELETE_NOENT // used to delete journals
 | 
						|
}
 | 
						|
 | 
						|
func (memVFS) Access(name string, flag vfs.AccessFlag) (bool, error) {
 | 
						|
	return false, nil // used to check for journals
 | 
						|
}
 | 
						|
 | 
						|
func (memVFS) FullPathname(name string) (string, error) {
 | 
						|
	return name, nil
 | 
						|
}
 | 
						|
 | 
						|
type memDB struct {
 | 
						|
	name string
 | 
						|
 | 
						|
	// +checklocks:lockMtx
 | 
						|
	waiter *sync.Cond
 | 
						|
	// +checklocks:dataMtx
 | 
						|
	data []*[sectorSize]byte
 | 
						|
 | 
						|
	size     int64 // +checklocks:dataMtx
 | 
						|
	refs     int32 // +checklocks:memoryMtx
 | 
						|
	shared   int32 // +checklocks:lockMtx
 | 
						|
	pending  bool  // +checklocks:lockMtx
 | 
						|
	reserved bool  // +checklocks:lockMtx
 | 
						|
 | 
						|
	lockMtx sync.Mutex
 | 
						|
	dataMtx sync.RWMutex
 | 
						|
}
 | 
						|
 | 
						|
func (m *memDB) release() {
 | 
						|
	memoryMtx.Lock()
 | 
						|
	defer memoryMtx.Unlock()
 | 
						|
	if m.refs--; m.refs == 0 && m == memoryDBs[m.name] {
 | 
						|
		delete(memoryDBs, m.name)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type memFile struct {
 | 
						|
	*memDB
 | 
						|
	lock     vfs.LockLevel
 | 
						|
	readOnly bool
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	// Ensure these interfaces are implemented:
 | 
						|
	_ vfs.FileLockState = &memFile{}
 | 
						|
	_ vfs.FileSizeHint  = &memFile{}
 | 
						|
)
 | 
						|
 | 
						|
func (m *memFile) Close() error {
 | 
						|
	m.release()
 | 
						|
	return m.Unlock(vfs.LOCK_NONE)
 | 
						|
}
 | 
						|
 | 
						|
func (m *memFile) ReadAt(b []byte, off int64) (n int, err error) {
 | 
						|
	m.dataMtx.RLock()
 | 
						|
	defer m.dataMtx.RUnlock()
 | 
						|
 | 
						|
	if off >= m.size {
 | 
						|
		return 0, io.EOF
 | 
						|
	}
 | 
						|
 | 
						|
	base := off / sectorSize
 | 
						|
	rest := off % sectorSize
 | 
						|
	have := int64(sectorSize)
 | 
						|
	if m.size < off+int64(len(b)) {
 | 
						|
		have = modRoundUp(m.size, sectorSize)
 | 
						|
	}
 | 
						|
	n = copy(b, (*m.data[base])[rest:have])
 | 
						|
	if n < len(b) {
 | 
						|
		// notest // assume reads are page aligned
 | 
						|
		return 0, io.ErrNoProgress
 | 
						|
	}
 | 
						|
	return n, nil
 | 
						|
}
 | 
						|
 | 
						|
func (m *memFile) WriteAt(b []byte, off int64) (n int, err error) {
 | 
						|
	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)
 | 
						|
	if size := off + int64(n); size > m.size {
 | 
						|
		m.size = size
 | 
						|
	}
 | 
						|
	if n < len(b) {
 | 
						|
		// notest // assume writes are page aligned
 | 
						|
		return n, io.ErrShortWrite
 | 
						|
	}
 | 
						|
	return n, nil
 | 
						|
}
 | 
						|
 | 
						|
func (m *memFile) Size() (int64, error) {
 | 
						|
	m.dataMtx.RLock()
 | 
						|
	defer m.dataMtx.RUnlock()
 | 
						|
	return m.size, nil
 | 
						|
}
 | 
						|
 | 
						|
func (m *memFile) Truncate(size int64) error {
 | 
						|
	m.dataMtx.Lock()
 | 
						|
	defer m.dataMtx.Unlock()
 | 
						|
	return m.truncate(size)
 | 
						|
}
 | 
						|
 | 
						|
func (m *memFile) SizeHint(size int64) error {
 | 
						|
	m.dataMtx.Lock()
 | 
						|
	defer m.dataMtx.Unlock()
 | 
						|
	if size > m.size {
 | 
						|
		return m.truncate(size)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// +checklocks:m.dataMtx
 | 
						|
func (m *memFile) truncate(size int64) error {
 | 
						|
	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
 | 
						|
}
 | 
						|
 | 
						|
func (m *memFile) Lock(lock vfs.LockLevel) error {
 | 
						|
	if m.lock >= lock {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	if m.readOnly && lock >= vfs.LOCK_RESERVED {
 | 
						|
		return sqlite3.IOERR_LOCK
 | 
						|
	}
 | 
						|
 | 
						|
	m.lockMtx.Lock()
 | 
						|
	defer m.lockMtx.Unlock()
 | 
						|
 | 
						|
	switch lock {
 | 
						|
	case vfs.LOCK_SHARED:
 | 
						|
		if m.pending {
 | 
						|
			return sqlite3.BUSY
 | 
						|
		}
 | 
						|
		m.shared++
 | 
						|
 | 
						|
	case vfs.LOCK_RESERVED:
 | 
						|
		if m.reserved {
 | 
						|
			return sqlite3.BUSY
 | 
						|
		}
 | 
						|
		m.reserved = true
 | 
						|
 | 
						|
	case vfs.LOCK_EXCLUSIVE:
 | 
						|
		if m.lock < vfs.LOCK_PENDING {
 | 
						|
			m.lock = vfs.LOCK_PENDING
 | 
						|
			m.pending = true
 | 
						|
		}
 | 
						|
 | 
						|
		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()
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	m.lock = lock
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (m *memFile) Unlock(lock vfs.LockLevel) error {
 | 
						|
	if m.lock <= lock {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	m.lockMtx.Lock()
 | 
						|
	defer m.lockMtx.Unlock()
 | 
						|
 | 
						|
	if m.lock >= vfs.LOCK_RESERVED {
 | 
						|
		m.reserved = false
 | 
						|
	}
 | 
						|
	if m.lock >= vfs.LOCK_PENDING {
 | 
						|
		m.pending = false
 | 
						|
	}
 | 
						|
	if lock < vfs.LOCK_SHARED {
 | 
						|
		if m.shared--; m.pending && m.shared <= 1 && m.waiter != nil {
 | 
						|
			m.waiter.Broadcast()
 | 
						|
		}
 | 
						|
	}
 | 
						|
	m.lock = lock
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (m *memFile) CheckReservedLock() (bool, error) {
 | 
						|
	// notest // OPEN_MEMORY
 | 
						|
	if m.lock >= vfs.LOCK_RESERVED {
 | 
						|
		return true, nil
 | 
						|
	}
 | 
						|
	m.lockMtx.Lock()
 | 
						|
	defer m.lockMtx.Unlock()
 | 
						|
	return m.reserved, nil
 | 
						|
}
 | 
						|
 | 
						|
func (m *memFile) LockState() vfs.LockLevel {
 | 
						|
	return m.lock
 | 
						|
}
 | 
						|
 | 
						|
func (*memFile) Sync(flag vfs.SyncFlag) error { return nil }
 | 
						|
 | 
						|
func (*memFile) SectorSize() int {
 | 
						|
	// notest // IOCAP_POWERSAFE_OVERWRITE
 | 
						|
	return sectorSize
 | 
						|
}
 | 
						|
 | 
						|
func (*memFile) DeviceCharacteristics() vfs.DeviceCharacteristic {
 | 
						|
	return vfs.IOCAP_ATOMIC |
 | 
						|
		vfs.IOCAP_SEQUENTIAL |
 | 
						|
		vfs.IOCAP_SAFE_APPEND |
 | 
						|
		vfs.IOCAP_POWERSAFE_OVERWRITE
 | 
						|
}
 | 
						|
 | 
						|
func divRoundUp(a, b int64) int64 {
 | 
						|
	return (a + b - 1) / b
 | 
						|
}
 | 
						|
 | 
						|
func modRoundUp(a, b int64) int64 {
 | 
						|
	return b - (b-a%b)%b
 | 
						|
}
 |