Files
qb/atree/io.go
2026-05-10 00:59:47 +00:00

374 lines
7.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package atree
import (
"errors"
"fmt"
"io/fs"
"math"
"os"
"gordenko.dev/dima/qb"
"gordenko.dev/dima/qb/bin"
)
// fix - додати ID, щоб потім перемістити із тимчасового буфера в pages, або звільнити
type AllocatedPage struct {
PageNo uint32
Data []byte
IsReused bool
}
type readResult struct {
Data []byte
Err error
}
// INDEX PAGES
func (s *Atree) DeletePages(pageNumbers []uint32) {
s.mutex.Lock()
for _, pageNo := range pageNumbers {
delete(s.pages, pageNo)
}
s.mutex.Unlock()
}
func (s *Atree) fetchIndexPage(pageNo uint32) ([]byte, error) {
buf, err := s.fetchPage(pageNo)
if err != nil {
return nil, err
}
if buf[pageTypeIdx] != PageTypeIndex {
return nil, fmt.Errorf("wrong pageType %d instead of %d", buf[pageTypeIdx], PageTypeIndex)
}
return buf, nil
}
func (s *Atree) fetchDataPage(pageNo uint32) ([]byte, error) {
buf, err := s.fetchPage(pageNo)
if err != nil {
return nil, err
}
if buf[pageTypeIdx] != PageTypeIndex {
return nil, fmt.Errorf("wrong pageType %d instead of %d", buf[pageTypeIdx], PageTypeIndex)
}
return buf, nil
}
func (s *Atree) fetchPage(pageNo uint32) ([]byte, error) {
s.mutex.Lock()
p, ok := s.pages[pageNo]
if ok {
p.ReferenceCount++
s.mutex.Unlock()
return p.Buf, nil
}
resultCh := make(chan readResult, 1)
s.pageWaits[pageNo] = append(s.pageWaits[pageNo], resultCh)
if len(s.pageWaits[pageNo]) == 1 {
s.pagesToRead = append(s.pagesToRead, pageNo)
s.mutex.Unlock()
select {
case s.readSignalCh <- struct{}{}:
default:
}
} else {
s.mutex.Unlock()
}
result := <-resultCh
if result.Err == nil {
result.Err = s.verifyCRC(result.Data, PageSize)
}
return result.Data, result.Err
}
func (s *Atree) releasePage(pageNo uint32) {
s.mutex.Lock()
defer s.mutex.Unlock()
p, ok := s.pages[pageNo]
if ok {
if p.ReferenceCount > 0 {
p.ReferenceCount--
return
} else {
qb.Abort(
qb.ReferenceCountBug,
fmt.Errorf("call releasePage on page %d with reference count = %d",
pageNo, p.ReferenceCount),
)
}
}
}
func (s *Atree) allocPageNumber() (uint32, bool) {
// fix
return 0, true
}
func (s *Atree) allocPage() AllocatedPage {
var (
allocated = AllocatedPage{
Data: make([]byte, PageSize),
}
)
//allocated.PageNo = s.freelist.ReservePage()
s.mutex.Lock()
// if allocated.PageNo > 0 {
// allocated.IsReused = true
// } else {
// if s.allocatedPagesQty == math.MaxUint32 {
// qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
// }
// s.allocatedPagesQty++
// allocated.PageNo = s.allocatedPagesQty
// }
// s.pages[allocated.PageNo] = &_page{
// // fix pageType
// PageNo: allocated.PageNo,
// Buf: allocated.Data,
// ReferenceCount: 1,
// }
s.mutex.Unlock()
return allocated
}
// // fix - без freelist
// func (s *Atree) allocIndexPage() AllocatedPage {
// var (
// allocated = AllocatedPage{
// Data: make([]byte, PageSize),
// }
// )
// allocated.PageNo = s.freelist.ReservePage()
// s.mutex.Lock()
// if allocated.PageNo > 0 {
// allocated.IsReused = true
// } else {
// if s.allocatedPagesQty == math.MaxUint32 {
// qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
// }
// s.allocatedPagesQty++
// allocated.PageNo = s.allocatedPagesQty
// }
// s.pages[allocated.PageNo] = &_page{
// // fix pageType
// PageNo: allocated.PageNo,
// Buf: allocated.Data,
// ReferenceCount: 1,
// }
// s.mutex.Unlock()
// return allocated
// }
// func (s *Atree) allocDataPage() AllocatedPage {
// var (
// allocated = AllocatedPage{
// Data: make([]byte, PageSize),
// }
// )
// allocated.PageNo = s.freelist.ReservePage()
// s.mutex.Lock()
// if allocated.PageNo > 0 {
// allocated.IsReused = true
// } else {
// if s.allocatedPagesQty == math.MaxUint32 {
// qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
// }
// s.allocatedPagesQty++
// allocated.PageNo = s.allocatedPagesQty
// }
// s.pages[allocated.PageNo] = &_page{
// // fix pageType
// PageNo: allocated.PageNo,
// Buf: allocated.Data,
// ReferenceCount: 1,
// }
// s.mutex.Unlock()
// return allocated
// }
// DATA PAGES
// READ
func (s *Atree) pageReader() {
for {
select {
case <-s.readSignalCh:
s.readPages()
}
}
}
func (s *Atree) readPages() {
s.mutex.Lock()
if len(s.pagesToRead) == 0 {
s.mutex.Unlock()
return
}
pagesToRead := s.pagesToRead
s.pagesToRead = nil
s.mutex.Unlock()
for _, pageNo := range pagesToRead {
buf := make([]byte, PageSize)
off := (pageNo - 1) * PageSize
n, err := s.file.ReadAt(buf, int64(off))
if n != PageSize {
err = fmt.Errorf("read %d instead of %d", n, PageSize)
}
s.mutex.Lock()
resultChannels := s.pageWaits[pageNo]
delete(s.pageWaits, pageNo)
if err != nil {
s.mutex.Unlock()
for _, resultCh := range resultChannels {
resultCh <- readResult{
Err: err,
}
}
} else {
s.pages[pageNo] = &_page{
// fix - page type
PageNo: pageNo,
Buf: buf,
ReferenceCount: len(resultChannels),
}
s.mutex.Unlock()
for _, resultCh := range resultChannels {
resultCh <- readResult{
Data: buf,
}
}
}
}
}
// WRITE
// func (s *Atree) pageWriter() {
// for {
// select {
// case <-s.writeSignalCh:
// err := s.writeTasks()
// if err != nil {
// qb.Abort(qb.WriteToAtreeFailed, err)
// }
// }
// }
// }
// type WriteTask struct {
// WaitCh chan struct{}
// Pages []PageToWrite
// }
// func (s *Atree) appendWriteTaskToQueue(task WriteTask) {
// s.mutex.Lock()
// s.writeTasksQueue = append(s.writeTasksQueue, task)
// s.mutex.Unlock()
// select {
// case s.writeSignalCh <- struct{}{}:
// default:
// }
// }
// func (s *Atree) writeTasks() error {
// s.mutex.Lock()
// tasks := s.writeTasksQueue
// s.writeTasksQueue = nil
// s.mutex.Unlock()
// for _, task := range tasks {
// for _, p := range task.Pages {
// if len(p.Data) != PageSize {
// return fmt.Errorf("wrong page %d size: %d",
// p.PageNo, len(p.Data))
// }
// bin.PutUint32(p.Data[crc32Idx:], calcChecksum(p.Data[:crc32Idx]))
// off := (p.PageNo - 1) * PageSize
// n, err := s.file.WriteAt(p.Data, int64(off))
// if err != nil {
// return err
// }
// if n != len(p.Data) {
// return fmt.Errorf("write %d instead of %d", n, len(p.Data))
// }
// }
// close(task.WaitCh)
// }
// return nil
// }
// IO
func isFileExist(fileName string) (bool, error) {
_, err := os.Stat(fileName)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return false, nil
} else {
return false, err
}
} else {
return true, nil
}
}
func openFile(fileName string, pageSize int) (_ *os.File, _ uint32, err error) {
file, err := os.OpenFile(fileName, os.O_RDWR, filePerm)
if err != nil {
return
}
fi, err := file.Stat()
if err != nil {
return
}
fileSize := fi.Size()
if (fileSize % int64(pageSize)) > 0 {
err = fmt.Errorf("the file size %d is not a multiple of the page size %d",
fileSize, pageSize)
return
}
allocatedPagesQty := fileSize / int64(pageSize)
if allocatedPagesQty > math.MaxUint32 {
err = fmt.Errorf("allocated pages %d is > max pages %d",
allocatedPagesQty, math.MaxUint32)
return
}
return file, uint32(allocatedPagesQty), nil
}
// func (s *Atree) ApplyREDO(task WriteTask) {
// s.appendWriteTaskToQueue(task)
// }
func (s *Atree) verifyCRC(data []byte, pageSize int) error {
var (
pos = pageSize - 4
calculatedCRC = calcChecksum(data[:pos])
storedCRC = bin.GetUint32(data[pos:])
)
if calculatedCRC != storedCRC {
return fmt.Errorf("calculatedCRC %d not equal storedCRC %d",
calculatedCRC, storedCRC)
}
return nil
}