| 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 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")/*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)}
 |