freephysicalslotpage.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*
  2. * EliasDB
  3. *
  4. * Copyright 2016 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. */
  10. package pageview
  11. import (
  12. "devt.de/krotik/eliasdb/storage/file"
  13. "devt.de/krotik/eliasdb/storage/paging/view"
  14. "devt.de/krotik/eliasdb/storage/util"
  15. )
  16. /*
  17. OffsetCount is the number of free slots which are stored on this page
  18. */
  19. const OffsetCount = view.OffsetData
  20. /*
  21. OffsetData is the offset for slot information
  22. */
  23. const OffsetData = OffsetCount + file.SizeShort
  24. /*
  25. SlotInfoSize is the size of a single free slot info
  26. */
  27. const SlotInfoSize = util.LocationSize + util.SizeInfoSize
  28. /*
  29. OptimalWasteMargin is the max amount of allowed allocation waste. When searching a slot
  30. on this page we should strife to find a slot which doesn't waste more than
  31. OptimalWasteMargin bytes
  32. */
  33. const OptimalWasteMargin = 128
  34. /*
  35. FreePhysicalSlotPage data structure
  36. */
  37. type FreePhysicalSlotPage struct {
  38. *SlotInfoPage
  39. maxSlots uint16 // Max number of slots
  40. maxAcceptableWaste uint32 // Max acceptable waste for a slot allocation
  41. sizeCache []uint32 // Cache for slot sizes
  42. }
  43. /*
  44. NewFreePhysicalSlotPage creates a new page which can manage free slots.
  45. */
  46. func NewFreePhysicalSlotPage(record *file.Record) *FreePhysicalSlotPage {
  47. checkFreePhysicalSlotPageMagic(record)
  48. maxSlots := (len(record.Data()) - OffsetData) / SlotInfoSize
  49. maxAcceptableWaste := len(record.Data()) / 4
  50. return &FreePhysicalSlotPage{NewSlotInfoPage(record), uint16(maxSlots),
  51. uint32(maxAcceptableWaste), make([]uint32, maxSlots, maxSlots)}
  52. }
  53. /*
  54. checkFreePhysicalSlotPageMagic checks if the magic number at the beginning of
  55. the wrapped record is valid.
  56. */
  57. func checkFreePhysicalSlotPageMagic(record *file.Record) bool {
  58. magic := record.ReadInt16(0)
  59. if magic == view.ViewPageHeader+view.TypeFreePhysicalSlotPage {
  60. return true
  61. }
  62. panic("Unexpected header found in FreePhysicalSlotPage")
  63. }
  64. /*
  65. MaxSlots returns the maximum number of slots which can be allocated.
  66. */
  67. func (fpsp *FreePhysicalSlotPage) MaxSlots() uint16 {
  68. return fpsp.maxSlots
  69. }
  70. /*
  71. FreeSlotCount returns the number of free slots on this page.
  72. */
  73. func (fpsp *FreePhysicalSlotPage) FreeSlotCount() uint16 {
  74. return fpsp.Record.ReadUInt16(OffsetCount)
  75. }
  76. /*
  77. SlotInfoLocation returns contents of a stored slotinfo as a location. Lookup is via a
  78. given slotinfo id.
  79. */
  80. func (fpsp *FreePhysicalSlotPage) SlotInfoLocation(slotinfo uint16) uint64 {
  81. offset := fpsp.slotinfoToOffset(slotinfo)
  82. return util.PackLocation(fpsp.SlotInfoRecord(offset), fpsp.SlotInfoOffset(offset))
  83. }
  84. /*
  85. FreeSlotSize returns the size of a free slot. Lookup is via offset.
  86. */
  87. func (fpsp *FreePhysicalSlotPage) FreeSlotSize(offset uint16) uint32 {
  88. slotinfo := fpsp.offsetToSlotinfo(offset)
  89. if fpsp.sizeCache[slotinfo] == 0 {
  90. fpsp.sizeCache[slotinfo] = fpsp.Record.ReadUInt32(int(offset + util.LocationSize))
  91. }
  92. return fpsp.sizeCache[slotinfo]
  93. }
  94. /*
  95. SetFreeSlotSize sets the size of a free slot. Lookup is via offset.
  96. */
  97. func (fpsp *FreePhysicalSlotPage) SetFreeSlotSize(offset uint16, size uint32) {
  98. slotinfo := fpsp.offsetToSlotinfo(offset)
  99. fpsp.sizeCache[slotinfo] = size
  100. fpsp.Record.WriteUInt32(int(offset+util.LocationSize), size)
  101. }
  102. /*
  103. AllocateSlotInfo allocates a place for a slotinfo and returns the offset for it.
  104. */
  105. func (fpsp *FreePhysicalSlotPage) AllocateSlotInfo(slotinfo uint16) uint16 {
  106. offset := fpsp.slotinfoToOffset(slotinfo)
  107. // Set slotinfo to initial values
  108. fpsp.SetFreeSlotSize(offset, 1)
  109. fpsp.SetSlotInfo(offset, 1, 1)
  110. // Increase counter for allocated slotinfos
  111. fpsp.Record.WriteUInt16(OffsetCount, fpsp.FreeSlotCount()+1)
  112. return offset
  113. }
  114. /*
  115. ReleaseSlotInfo releases a place for a slotinfo and return its offset.
  116. */
  117. func (fpsp *FreePhysicalSlotPage) ReleaseSlotInfo(slotinfo uint16) uint16 {
  118. offset := fpsp.slotinfoToOffset(slotinfo)
  119. // Set slotinfo to empty values
  120. fpsp.SetFreeSlotSize(offset, 0)
  121. fpsp.SetSlotInfo(offset, 0, 0)
  122. // Decrease counter for allocated slotinfos
  123. fpsp.Record.WriteUInt16(OffsetCount, fpsp.FreeSlotCount()-1)
  124. return offset
  125. }
  126. /*
  127. FirstFreeSlotInfo returns the id for the first available slotinfo for allocation
  128. or -1 if nothing is available.
  129. */
  130. func (fpsp *FreePhysicalSlotPage) FirstFreeSlotInfo() int {
  131. var i uint16
  132. for i = 0; i < fpsp.maxSlots; i++ {
  133. if !fpsp.isAllocatedSlot(i) {
  134. return int(i)
  135. }
  136. }
  137. return -1
  138. }
  139. /*
  140. FindSlot finds a slot which is suitable for a given amount of data but which is also not
  141. too big to avoid wasting space.
  142. */
  143. func (fpsp *FreePhysicalSlotPage) FindSlot(minSize uint32) int {
  144. var i uint16
  145. bestSlot := -1
  146. bestSlotWaste := fpsp.maxAcceptableWaste + 1
  147. var maxSize uint32
  148. for i = 0; i < fpsp.maxSlots; i++ {
  149. slotinfoOffset := fpsp.slotinfoToOffset(i)
  150. slotinfoSize := fpsp.FreeSlotSize(slotinfoOffset)
  151. if slotinfoSize > maxSize {
  152. maxSize = slotinfoSize
  153. }
  154. // Calculate the wasted space
  155. waste := slotinfoSize - minSize
  156. // Test if the block would fit
  157. if waste >= 0 {
  158. if waste < OptimalWasteMargin {
  159. // In the ideal case we can minimise the produced waste
  160. return int(i)
  161. } else if bestSlotWaste > waste {
  162. // Too much for optimal waste margin but may still be OK if
  163. // we don't find anything better
  164. bestSlot = int(i)
  165. bestSlotWaste = waste
  166. }
  167. }
  168. }
  169. if bestSlot != -1 {
  170. // We found a block but its waste was above the optimal waste margin
  171. // check if it is still acceptable
  172. // Note: It must be below the MAX_AVAILABLE_SIZE_DIFFERENCE as a row
  173. // stores the current size as the difference to the available size.
  174. // This difference must fit in an unsigned short.
  175. if bestSlotWaste < fpsp.maxAcceptableWaste &&
  176. bestSlotWaste < util.MaxAvailableSizeDifference {
  177. return bestSlot
  178. }
  179. }
  180. return -int(maxSize)
  181. }
  182. /*
  183. isAllocatedSlot checks if a given slotinfo is allocated.
  184. */
  185. func (fpsp *FreePhysicalSlotPage) isAllocatedSlot(slotinfo uint16) bool {
  186. offset := fpsp.slotinfoToOffset(slotinfo)
  187. return fpsp.FreeSlotSize(offset) != 0
  188. }
  189. /*
  190. slotinfoToOffset converts a slotinfo number into an offset on the record.
  191. */
  192. func (fpsp *FreePhysicalSlotPage) slotinfoToOffset(slotinfo uint16) uint16 {
  193. return OffsetData + slotinfo*SlotInfoSize
  194. }
  195. /*
  196. offsetToSlotinfo converts an offset into a slotinfo number.
  197. */
  198. func (fpsp *FreePhysicalSlotPage) offsetToSlotinfo(offset uint16) uint16 {
  199. return (offset - OffsetData) / SlotInfoSize
  200. }