123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- /*
- * 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"
- )
- /*
- LogicalSlotManager data structure
- */
- type LogicalSlotManager struct {
- storagefile *file.StorageFile // StorageFile which is wrapped
- pager *paging.PagedStorageFile // Pager for StorageFile
- freeManager *FreeLogicalSlotManager // Manager for free slots
- recordSize uint32 // Size of records
- elementsPerPage uint16 // Available elements per page
- }
- /*
- NewLogicalSlotManager creates a new object to manage logical slots. This
- factory function requires two PagedStorageFiles the first will hold the actual
- logical slots, the second is used to manage free logical slots.
- */
- func NewLogicalSlotManager(lsf *paging.PagedStorageFile,
- flsf *paging.PagedStorageFile) *LogicalSlotManager {
- sf := lsf.StorageFile()
- freeManager := NewFreeLogicalSlotManager(flsf)
- recordSize := sf.RecordSize()
- return &LogicalSlotManager{sf, lsf, freeManager,
- recordSize, uint16((recordSize - pageview.OffsetTransData) / util.LocationSize)}
- }
- /*
- ElementsPerPage returns the available elements per page.
- */
- func (lsm *LogicalSlotManager) ElementsPerPage() uint16 {
- return lsm.elementsPerPage
- }
- /*
- Insert inserts a given physical slot info and returns a logical slot for it.
- */
- func (lsm *LogicalSlotManager) Insert(location uint64) (uint64, error) {
- // Try to get a free slot from the FreeLogicalSlotManager
- slot, err := lsm.freeManager.Get()
- if err != nil {
- return 0, err
- }
- if slot == 0 {
- // Allocate a new page and give all its rows to the free manager
- allocPage, err := lsm.pager.AllocatePage(view.TypeTranslationPage)
- if err != nil {
- return 0, err
- }
- offset := uint16(pageview.OffsetTransData)
- var i uint16
- for i = 0; i < lsm.elementsPerPage; i++ {
- lsm.freeManager.Add(util.PackLocation(allocPage, offset))
- offset += util.LocationSize
- }
- err = lsm.Flush()
- if err != nil {
- // Try to clean up if something goes wrong
- // Make the freeManager forget that he received anything
- lsm.freeManager.slots = make([]uint64, 0)
- // Free the allocated page again
- lsm.pager.FreePage(allocPage)
- return 0, err
- }
- // Now get the first slot from the newly allocated page - no need for
- // error checking since we just flushed the page and all is well
- slot, _ = lsm.freeManager.Get()
- }
- // Write physical slot data to translation page
- return slot, lsm.Update(slot, location)
- }
- /*
- ForceInsert inserts a given physical slot info at a given logical slot.
- */
- func (lsm *LogicalSlotManager) ForceInsert(logicalSlot uint64, location uint64) error {
- page := lsm.pager.Last(view.TypeTranslationPage)
- targetPage := util.LocationRecord(logicalSlot)
- // If the target page hasn't been allocated yet then create new pages
- // until the target page is available and we can force insert into the
- // requested slot
- for page < targetPage {
- var err error
- page, err = lsm.pager.AllocatePage(view.TypeTranslationPage)
- if err != nil {
- return err
- }
- }
- slot, err := lsm.Fetch(logicalSlot)
- if err != nil {
- return err
- }
- if slot != 0 {
- panic(fmt.Sprintf("Cannot force insert into slot %v because it already exists",
- logicalSlot))
- }
- return lsm.Update(logicalSlot, location)
- }
- /*
- Update updates a given logical slot with a physical slot info.
- */
- func (lsm *LogicalSlotManager) Update(logicalSlot uint64, location uint64) error {
- recordID := util.LocationRecord(logicalSlot)
- record, err := lsm.storagefile.Get(recordID)
- if err != nil {
- return err
- }
- page := pageview.NewTransPage(record)
- page.SetSlotInfo(util.LocationOffset(logicalSlot), util.LocationRecord(location),
- util.LocationOffset(location))
- lsm.storagefile.ReleaseInUseID(recordID, true)
- return nil
- }
- /*
- Free frees a given logical slot. The given slot is given to the FreeLogicalSlotManager.
- */
- func (lsm *LogicalSlotManager) Free(logicalSlot uint64) error {
- recordID := util.LocationRecord(logicalSlot)
- record, err := lsm.storagefile.Get(recordID)
- if err != nil {
- return err
- }
- page := pageview.NewTransPage(record)
- page.SetSlotInfo(util.LocationOffset(logicalSlot), util.LocationRecord(0),
- util.LocationOffset(0))
- return lsm.storagefile.ReleaseInUseID(recordID, true)
- }
- /*
- Fetch looks up a physical slot using a given logical slot.
- */
- func (lsm *LogicalSlotManager) Fetch(logicalSlot uint64) (uint64, error) {
- recordID := util.LocationRecord(logicalSlot)
- offset := util.LocationOffset(logicalSlot)
- if lastPage := lsm.pager.Last(view.TypeTranslationPage); lastPage < recordID {
- // Return if the requested page doesn't exist yet
- return 0, nil
- }
- record, err := lsm.storagefile.Get(recordID)
- if err != nil {
- return 0, err
- }
- page := pageview.NewTransPage(record)
- slot := util.PackLocation(page.SlotInfoRecord(offset), page.SlotInfoOffset(offset))
- lsm.storagefile.ReleaseInUseID(recordID, false)
- return slot, nil
- }
- /*
- Flush writes all pending changes.
- */
- func (lsm *LogicalSlotManager) Flush() error {
- return lsm.freeManager.Flush()
- }
|