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 pageview
- import (
- "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 slot
- on this page we should strife to find a slot which doesn't waste more than
- OptimalWasteMargin 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 of
- the 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 a
- given 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 allocation
- or -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 not
- too 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
- }
|