| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 | /* * 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 pageviewimport (	"devt.de/krotik/eliasdb/storage/file"	"devt.de/krotik/eliasdb/storage/paging/view"	"devt.de/krotik/eliasdb/storage/util")/*OffsetCount is the number of free slots which are stored on this page*/const OffsetCount = view.OffsetData/*OffsetData is the offset for slot information*/const OffsetData = OffsetCount + file.SizeShort/*SlotInfoSize is the size of a single free slot info*/const SlotInfoSize = util.LocationSize + util.SizeInfoSize/*OptimalWasteMargin is the max amount of allowed allocation waste. When searching a sloton this page we should strife to find a slot which doesn't waste more thanOptimalWasteMargin bytes*/const OptimalWasteMargin = 128/*FreePhysicalSlotPage data structure*/type FreePhysicalSlotPage struct {	*SlotInfoPage	maxSlots           uint16   // Max number of slots	maxAcceptableWaste uint32   // Max acceptable waste for a slot allocation	sizeCache          []uint32 // Cache for slot sizes}/*NewFreePhysicalSlotPage creates a new page which can manage free slots.*/func NewFreePhysicalSlotPage(record *file.Record) *FreePhysicalSlotPage {	checkFreePhysicalSlotPageMagic(record)	maxSlots := (len(record.Data()) - OffsetData) / SlotInfoSize	maxAcceptableWaste := len(record.Data()) / 4	return &FreePhysicalSlotPage{NewSlotInfoPage(record), uint16(maxSlots),		uint32(maxAcceptableWaste), make([]uint32, maxSlots, maxSlots)}}/*checkFreePhysicalSlotPageMagic checks if the magic number at the beginning ofthe wrapped record is valid.*/func checkFreePhysicalSlotPageMagic(record *file.Record) bool {	magic := record.ReadInt16(0)	if magic == view.ViewPageHeader+view.TypeFreePhysicalSlotPage {		return true	}	panic("Unexpected header found in FreePhysicalSlotPage")}/*MaxSlots returns the maximum number of slots which can be allocated.*/func (fpsp *FreePhysicalSlotPage) MaxSlots() uint16 {	return fpsp.maxSlots}/*FreeSlotCount returns the number of free slots on this page.*/func (fpsp *FreePhysicalSlotPage) FreeSlotCount() uint16 {	return fpsp.Record.ReadUInt16(OffsetCount)}/*SlotInfoLocation returns contents of a stored slotinfo as a location. Lookup is via agiven slotinfo id.*/func (fpsp *FreePhysicalSlotPage) SlotInfoLocation(slotinfo uint16) uint64 {	offset := fpsp.slotinfoToOffset(slotinfo)	return util.PackLocation(fpsp.SlotInfoRecord(offset), fpsp.SlotInfoOffset(offset))}/*FreeSlotSize returns the size of a free slot. Lookup is via offset.*/func (fpsp *FreePhysicalSlotPage) FreeSlotSize(offset uint16) uint32 {	slotinfo := fpsp.offsetToSlotinfo(offset)	if fpsp.sizeCache[slotinfo] == 0 {		fpsp.sizeCache[slotinfo] = fpsp.Record.ReadUInt32(int(offset + util.LocationSize))	}	return fpsp.sizeCache[slotinfo]}/*SetFreeSlotSize sets the size of a free slot. Lookup is via offset.*/func (fpsp *FreePhysicalSlotPage) SetFreeSlotSize(offset uint16, size uint32) {	slotinfo := fpsp.offsetToSlotinfo(offset)	fpsp.sizeCache[slotinfo] = size	fpsp.Record.WriteUInt32(int(offset+util.LocationSize), size)}/*AllocateSlotInfo allocates a place for a slotinfo and returns the offset for it.*/func (fpsp *FreePhysicalSlotPage) AllocateSlotInfo(slotinfo uint16) uint16 {	offset := fpsp.slotinfoToOffset(slotinfo)	// Set slotinfo to initial values	fpsp.SetFreeSlotSize(offset, 1)	fpsp.SetSlotInfo(offset, 1, 1)	// Increase counter for allocated slotinfos	fpsp.Record.WriteUInt16(OffsetCount, fpsp.FreeSlotCount()+1)	return offset}/*ReleaseSlotInfo releases a place for a slotinfo and return its offset.*/func (fpsp *FreePhysicalSlotPage) ReleaseSlotInfo(slotinfo uint16) uint16 {	offset := fpsp.slotinfoToOffset(slotinfo)	// Set slotinfo to empty values	fpsp.SetFreeSlotSize(offset, 0)	fpsp.SetSlotInfo(offset, 0, 0)	// Decrease counter for allocated slotinfos	fpsp.Record.WriteUInt16(OffsetCount, fpsp.FreeSlotCount()-1)	return offset}/*FirstFreeSlotInfo returns the id for the first available slotinfo for allocationor -1 if nothing is available.*/func (fpsp *FreePhysicalSlotPage) FirstFreeSlotInfo() int {	var i uint16	for i = 0; i < fpsp.maxSlots; i++ {		if !fpsp.isAllocatedSlot(i) {			return int(i)		}	}	return -1}/*FindSlot finds a slot which is suitable for a given amount of data but which is also nottoo big to avoid wasting space.*/func (fpsp *FreePhysicalSlotPage) FindSlot(minSize uint32) int {	var i uint16	bestSlot := -1	bestSlotWaste := fpsp.maxAcceptableWaste + 1	var maxSize uint32	for i = 0; i < fpsp.maxSlots; i++ {		slotinfoOffset := fpsp.slotinfoToOffset(i)		slotinfoSize := fpsp.FreeSlotSize(slotinfoOffset)		if slotinfoSize > maxSize {			maxSize = slotinfoSize		}		// Calculate the wasted space		waste := slotinfoSize - minSize		// Test if the block would fit		if waste >= 0 {			if waste < OptimalWasteMargin {				// In the ideal case we can minimise the produced waste				return int(i)			} else if bestSlotWaste > waste {				// Too much for optimal waste margin but may still be OK if				// we don't find anything better				bestSlot = int(i)				bestSlotWaste = waste			}		}	}	if bestSlot != -1 {		// We found a block but its waste was above the optimal waste margin		// check if it is still acceptable		// Note: It must be below the MAX_AVAILABLE_SIZE_DIFFERENCE as a row		// stores the current size as the difference to the available size.		// This difference must fit in an unsigned short.		if bestSlotWaste < fpsp.maxAcceptableWaste &&			bestSlotWaste < util.MaxAvailableSizeDifference {			return bestSlot		}	}	return -int(maxSize)}/*isAllocatedSlot checks if a given slotinfo is allocated.*/func (fpsp *FreePhysicalSlotPage) isAllocatedSlot(slotinfo uint16) bool {	offset := fpsp.slotinfoToOffset(slotinfo)	return fpsp.FreeSlotSize(offset) != 0}/*slotinfoToOffset converts a slotinfo number into an offset on the record.*/func (fpsp *FreePhysicalSlotPage) slotinfoToOffset(slotinfo uint16) uint16 {	return OffsetData + slotinfo*SlotInfoSize}/*offsetToSlotinfo converts an offset into a slotinfo number.*/func (fpsp *FreePhysicalSlotPage) offsetToSlotinfo(offset uint16) uint16 {	return (offset - OffsetData) / SlotInfoSize}
 |