123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- /*
- * EliasDB
- *
- * Copyright 2016 Matthias Ladkau. All rights reserved.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
- package slotting
- import (
- "fmt"
- "devt.de/krotik/eliasdb/storage/file"
- "devt.de/krotik/eliasdb/storage/paging"
- "devt.de/krotik/eliasdb/storage/paging/view"
- "devt.de/krotik/eliasdb/storage/slotting/pageview"
- "devt.de/krotik/eliasdb/storage/util"
- )
- /*
- FreePhysicalSlotManager data structure
- */
- type FreePhysicalSlotManager struct {
- storagefile *file.StorageFile // StorageFile which is wrapped
- pager *paging.PagedStorageFile // Pager for StorageFile
- onlyAppend bool // Flag for append-only mode
- lastMaxSlotSize int // Last max slot size
- slots []uint64 // List of free slots
- sizes []uint32 // List of free slot sizes
- }
- /*
- NewFreePhysicalSlotManager creates a new object to manage free physical slots.
- */
- func NewFreePhysicalSlotManager(psf *paging.PagedStorageFile, onlyAppend bool) *FreePhysicalSlotManager {
- return &FreePhysicalSlotManager{psf.StorageFile(), psf, onlyAppend, 0,
- make([]uint64, 0), make([]uint32, 0)}
- }
- /*
- Get searches for a free location with the given size.
- */
- func (fpsm *FreePhysicalSlotManager) Get(size uint32) (uint64, error) {
- // Return always nothing found if we are in only-append mode
- if fpsm.onlyAppend {
- return 0, nil
- }
- // Return nothing if all previous found pages were too small
- if fpsm.lastMaxSlotSize != 0 && int(size) > fpsm.lastMaxSlotSize {
- return 0, nil
- }
- cursor := paging.NewPageCursor(fpsm.pager, view.TypeFreePhysicalSlotPage, 0)
- // No need for error checking on cursor next since all pages will be opened
- // via Get calls in the loop.
- page, _ := cursor.Next()
- for page != 0 {
- record, err := fpsm.storagefile.Get(page)
- if err != nil {
- // Reset the lastMaxSlotSize since we didn't visit all
- // FreePhysicalSlotPages
- fpsm.lastMaxSlotSize = 0
- return 0, err
- }
- fpsp := pageview.NewFreePhysicalSlotPage(record)
- slot := fpsp.FindSlot(size)
- // If a slot was found (Important: a slot can be >= 0)
- if slot >= 0 {
- // Return a found slot and free the free page if necessary
- fpsm.lastMaxSlotSize = 0
- loc := fpsp.SlotInfoLocation(uint16(slot))
- // Release slot
- fpsp.ReleaseSlotInfo(uint16(slot))
- if fpsp.FreeSlotCount() == 0 {
- // Free the page if no free slot is stored
- fpsm.storagefile.ReleaseInUseID(page, false)
- fpsm.pager.FreePage(page)
- } else {
- fpsm.storagefile.ReleaseInUseID(page, false)
- }
- return loc, nil
- }
- if fpsm.lastMaxSlotSize < -slot {
- fpsm.lastMaxSlotSize = -slot
- }
- fpsm.storagefile.ReleaseInUseID(page, false)
- page, _ = cursor.Next()
- }
- return 0, nil
- }
- /*
- Add adds a slotinfo to the free slot set.
- */
- func (fpsm *FreePhysicalSlotManager) Add(loc uint64, size uint32) {
- if size > 0 {
- fpsm.slots = append(fpsm.slots, loc)
- fpsm.sizes = append(fpsm.sizes, size)
- }
- }
- /*
- Flush writes all added slotinfos to FreePhysicalSlotPages.
- */
- func (fpsm *FreePhysicalSlotManager) Flush() error {
- cursor := paging.NewPageCursor(fpsm.pager, view.TypeFreePhysicalSlotPage, 0)
- index := 0
- // Go through all free physical row ID pages
- // No need for error checking on cursor next since all pages will be opened
- // via Get calls in the loop.
- page, _ := cursor.Next()
- for page != 0 {
- // Need to declare err here otherwise index becomes a local for
- // the "for" block
- var err error
- index, err = fpsm.doFlush(page, index)
- if err != nil {
- return err
- }
- if index >= len(fpsm.slots) {
- break
- }
- page, _ = cursor.Next()
- }
- // Allocate new free physical slot pages if all present ones are full
- // and we have still slots to process
- for index < len(fpsm.slots) {
- allocPage, err := fpsm.pager.AllocatePage(view.TypeFreePhysicalSlotPage)
- if err != nil {
- return err
- }
- index, err = fpsm.doFlush(allocPage, index)
- if err != nil {
- // Try to free the allocated page if there was an error
- // ignore any error of the FreePage call
- fpsm.pager.FreePage(allocPage)
- return err
- }
- }
- // Clear lists after all slots information have been written
- fpsm.slots = make([]uint64, 0)
- fpsm.sizes = make([]uint32, 0)
- return nil
- }
- /*
- doFlush writes all added slotinfos to a FreePhysicalSlotPage. Stop if the page is full.
- */
- func (fpsm *FreePhysicalSlotManager) doFlush(page uint64, index int) (int, error) {
- r, err := fpsm.storagefile.Get(page)
- if err != nil {
- return index, err
- }
- fpsp := pageview.NewFreePhysicalSlotPage(r)
- // Iterate all page slots (stop if the page has no more available slots
- // or we reached the end of the page)
- slot := fpsp.FirstFreeSlotInfo()
- for ; slot != -1 && index < len(fpsm.slots); index++ {
- loc := fpsm.slots[index]
- size := fpsm.sizes[index]
- if size > 0 {
- offset := fpsp.AllocateSlotInfo(uint16(slot))
- fpsp.SetSlotInfo(offset, util.LocationRecord(loc), util.LocationOffset(loc))
- fpsp.SetFreeSlotSize(offset, size)
- slot = fpsp.FirstFreeSlotInfo()
- }
- }
- fpsm.storagefile.ReleaseInUseID(page, true)
- return index, nil
- }
- /*
- String returns a string representation of this FreePhysicalSlotManager.
- */
- func (fpsm *FreePhysicalSlotManager) String() string {
- return fmt.Sprintf("FreePhysicalSlotManager: %v (onlyAppend:%v lastMaxSlotSize:%v)\nIds :%v\nSizes:%v",
- fpsm.storagefile.Name(), fpsm.onlyAppend, fpsm.lastMaxSlotSize, fpsm.slots, fpsm.sizes)
- }
|