| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 | /* * 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 contains managers which deal with slots on pages.FreeLogicalSlotManagerFreeLogicalSlotManager is a list manager for free logical slots. This managerobject is used by the LogicalSlotManager.FreePhysicalSlotManagerFreePhysicalSlotManager is a list manager for free physical slots. This managerobject is used by the PhysicalSlotManager.LogicalSlotManagerLogicalSlotManager is a list manager for logical slots. Logical slots are storedon translation pages which store just pointers to physical slots.PhysicalSlotManagerPhysicalSlotManager is a list manager for physical slots.*/package slottingimport (	"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")/*FreeLogicalSlotManager data structure*/type FreeLogicalSlotManager struct {	storagefile *file.StorageFile        // StorageFile which is wrapped	pager       *paging.PagedStorageFile // Pager for StorageFile	slots       []uint64                 // List of free slots}/*NewFreeLogicalSlotManager creates a new object to manage free logical slots.*/func NewFreeLogicalSlotManager(psf *paging.PagedStorageFile) *FreeLogicalSlotManager {	return &FreeLogicalSlotManager{psf.StorageFile(), psf, make([]uint64, 0)}}/*Get gets a free slot.*/func (flsm *FreeLogicalSlotManager) Get() (uint64, error) {	// Try to get entry from the slots list	if len(flsm.slots) > 0 {		freeSlot := flsm.slots[len(flsm.slots)-1]		flsm.slots = flsm.slots[:len(flsm.slots)-1]		return freeSlot, nil	}	cursor := paging.NewPageCursor(flsm.pager, view.TypeFreeLogicalSlotPage, 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 := flsm.storagefile.Get(page)		if err != nil {			return 0, err		}		flsp := pageview.NewFreeLogicalSlotPage(record)		slot := flsp.FirstAllocatedSlotInfo()		if slot != -1 {			// Return a found slot and free the free page if necessary			loc := flsp.SlotInfoLocation(uint16(slot))			// Release the slot			flsp.ReleaseSlotInfo(uint16(slot))			if flsp.FreeSlotCount() == 0 {				// Free the page if no free row id slot is left				flsm.storagefile.ReleaseInUseID(page, false)				flsm.pager.FreePage(page)			} else {				flsm.storagefile.ReleaseInUseID(page, true)			}			return loc, nil		}		flsm.storagefile.ReleaseInUseID(page, false)		page, _ = cursor.Next()	}	return 0, nil}/*Add adds a slot to the free slot set.*/func (flsm *FreeLogicalSlotManager) Add(loc uint64) {	if loc == 0 {		// The bit pattern for the 0 location is used to mark free slots		panic("Illigal free logical slot pattern: 0x0")	}	flsm.slots = append(flsm.slots, loc)}/*Flush writes all added slotinfos to FreeLogicalSlotPages.*/func (flsm *FreeLogicalSlotManager) Flush() error {	cursor := paging.NewPageCursor(flsm.pager, view.TypeFreeLogicalSlotPage, 0)	index := 0	// Go through all free logical slot 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 = flsm.doFlush(page, index)		if err != nil {			return err		}		if index >= len(flsm.slots) {			break		}		page, _ = cursor.Next()	}	// Allocate new free logical slot pages if all present ones are full	// and we have still slots to process	for index < len(flsm.slots) {		allocPage, err := flsm.pager.AllocatePage(view.TypeFreeLogicalSlotPage)		if err != nil {			return err		}		index, err = flsm.doFlush(allocPage, index)		if err != nil {			// Try to free the allocated page if there was an error			// ignore any error of the FreePage call			flsm.pager.FreePage(allocPage)			return err		}	}	// Clear lists after all slots information have been written	flsm.slots = make([]uint64, 0)	return nil}/*doFlush writes all added slotinfos to a FreeLogicalSlotPage. Stop if the page is full.*/func (flsm *FreeLogicalSlotManager) doFlush(page uint64, index int) (int, error) {	r, err := flsm.storagefile.Get(page)	if err != nil {		return index, err	}	flsp := pageview.NewFreeLogicalSlotPage(r)	// Iterate all page slots (stop if the page has no more available slots	// or we reached the end of the page)	slot := flsp.FirstFreeSlotInfo()	for ; slot != -1 && index < len(flsm.slots); index++ {		loc := flsm.slots[index]		offset := flsp.AllocateSlotInfo(uint16(slot))		flsp.SetSlotInfo(offset, util.LocationRecord(loc), util.LocationOffset(loc))		slot = flsp.FirstFreeSlotInfo()	}	flsm.storagefile.ReleaseInUseID(page, true)	return index, nil}/*Returns a string representation of this FreeLogicalSlotManager.*/func (flsm *FreeLogicalSlotManager) String() string {	return fmt.Sprintf("FreeLogicalSlotManager: %v\nIds  :%v\n",		flsm.storagefile.Name(), flsm.slots)}
 |