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 }