001package org.cache2k.storage; 002 003/* 004 * #%L 005 * cache2k core package 006 * %% 007 * Copyright (C) 2000 - 2015 headissue GmbH, Munich 008 * %% 009 * This program is free software: you can redistribute it and/or modify 010 * it under the terms of the GNU General Public License as 011 * published by the Free Software Foundation, either version 3 of the 012 * License, or (at your option) any later version. 013 * 014 * This program is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public 020 * License along with this program. If not, see 021 * <http://www.gnu.org/licenses/gpl-3.0.html>. 022 * #L% 023 */ 024 025import java.util.Comparator; 026import java.util.TreeSet; 027 028/** 029 * Holds areas of free space (slots). 030 * 031 * @author Jens Wilke; created: 2014-04-25 032 */ 033public class FreeSpaceMap { 034 035 long freeSpace; 036 TreeSet<Slot> freeSet; 037 TreeSet<Slot> pos2slot; 038 039 public void init() { 040 freeSpace = 0; 041 freeSet = new TreeSet<Slot>(); 042 pos2slot = new TreeSet<Slot>(new PositionOrder()); 043 } 044 045 public void put(Slot s) { 046 freeSpace += s.size; 047 freeSet.add(s); 048 pos2slot.add(s); 049 } 050 051 /** 052 * Get the slot that ends at the position exclusive or: start + size = pos 053 */ 054 public Slot reserveSlotEndingAt(long pos) { 055 reusedFreeSlotUnderLock.position = pos; 056 Slot s = pos2slot.floor(reusedFreeSlotUnderLock); 057 if (s != null && s.getNextPosition() == pos) { 058 freeSet.remove(s); 059 pos2slot.remove(s); 060 freeSpace -= s.size; 061 return s; 062 } 063 return null; 064 } 065 066 public Slot getHighestSlot() { 067 if (pos2slot.size() > 0) { 068 return pos2slot.last(); 069 } 070 return null; 071 } 072 073 final Slot reusedFreeSlotUnderLock = new Slot(0, 0); 074 075 /** 076 * Find a slot which size is greater then the needed size and 077 * remove it from the free space map. 078 */ 079 public Slot findFree(int size) { 080 reusedFreeSlotUnderLock.size = size; 081 Slot s = freeSet.ceiling(reusedFreeSlotUnderLock); 082 if (s != null) { 083 freeSet.remove(s); 084 pos2slot.remove(s); 085 freeSpace -= s.size; 086 return s; 087 } 088 return null; 089 } 090 091 public void allocateSpace(Slot s) { 092 allocateSpace(s.position, s.size); 093 } 094 095 /** 096 * Used to rebuild the free space map. Entry has already got a position. 097 * The used space will be removed of the free space map. 098 * 099 * @throws java.lang.IllegalArgumentException if space is not available 100 */ 101 public void allocateSpace(long _position, int _size) { 102 freeSpace -= _size; 103 if (freeSpace < 0) { 104 throw new IllegalArgumentException("no free space."); 105 } 106 reusedFreeSlotUnderLock.position = _position; 107 Slot s = pos2slot.floor(reusedFreeSlotUnderLock); 108 if (s == null) { 109 throw new IllegalArgumentException("no free space, no slot"); 110 } 111 pos2slot.remove(s); 112 freeSet.remove(s); 113 if (s.size < _size) { 114 throw new IllegalArgumentException("no free space, small slot"); 115 } 116 if (s.position < _position) { 117 if (s.getNextPosition() < (_position + _size)) { 118 throw new IllegalArgumentException("no free space, premature slot end"); 119 } 120 Slot _preceding = new Slot(); 121 _preceding.position = s.position; 122 _preceding.size = (int) (_position - s.position); 123 pos2slot.add(_preceding); 124 freeSet.add(_preceding); 125 s.size -= _preceding.size; 126 } 127 if (s.size > _size) { 128 s.position = _size + _position; 129 s.size -= _size; 130 pos2slot.add(s); 131 freeSet.add(s); 132 } 133 } 134 135 public void freeSpace(Slot s) { 136 freeSpace(s.position, s.size); 137 } 138 139 /** 140 * Free the space. Not just put the slot in but try to merge it. 141 */ 142 public void freeSpace(long _position, int _size) { 143 freeSpace += _size; 144 reusedFreeSlotUnderLock.position = (_size + _position); 145 Slot s = pos2slot.ceiling(reusedFreeSlotUnderLock); 146 if (s != null && s.position == reusedFreeSlotUnderLock.position) { 147 pos2slot.remove(s); 148 freeSet.remove(s); 149 s.position = _position; 150 s.size += _size; 151 } else { 152 s = new Slot(_position, _size); 153 } 154 reusedFreeSlotUnderLock.position = _position; 155 Slot s2 = pos2slot.lower(reusedFreeSlotUnderLock); 156 if (s2 != null && s2.getNextPosition() == _position) { 157 pos2slot.remove(s2); 158 freeSet.remove(s2); 159 s2.size += s.size; 160 s = s2; 161 } 162 pos2slot.add(s); 163 freeSet.add(s); 164 } 165 166 public long getFreeSpace() { 167 return freeSpace; 168 } 169 170 /** 171 * Calculate the free space for an integrity check. 172 */ 173 private long calculateFreeSpace() { 174 long s = 0; 175 Slot _prev = null; 176 for (Slot fs : pos2slot) { 177 s += fs.size; 178 _prev = fs; 179 } 180 return s; 181 } 182 183 public int getSlotCount() { 184 return freeSet.size(); 185 } 186 187 static class PositionOrder implements Comparator<Slot> { 188 @Override 189 public int compare(Slot o1, Slot o2) { 190 if (o1.position < o2.position) { 191 return -1; 192 } 193 if (o1.position > o2.position) { 194 return 1; 195 } 196 return 0; 197 } 198 } 199 200 public long getSizeOfLargestSlot() { 201 if (freeSet.size() == 0) { 202 return 0; 203 } 204 return freeSet.last().size; 205 } 206 207 public long getSizeOfSmallestSlot() { 208 if (freeSet.size() == 0) { 209 return 0; 210 } 211 return freeSet.first().size; 212 } 213 214 /** 215 * Describes an area of free space in the storage. The comparison is by size. 216 */ 217 public static class Slot implements Comparable<Slot> { 218 219 long position; 220 int size; 221 222 public Slot() { } 223 224 public Slot(long _position, int _size) { 225 this.position = _position; 226 this.size = _size; 227 } 228 229 public long getPosition() { 230 return position; 231 } 232 233 public int getSize() { 234 return size; 235 } 236 237 public long getNextPosition() { 238 return position + size; 239 } 240 241 @Override 242 public int compareTo(Slot o) { 243 int d = size - o.size; 244 if (d != 0) { 245 return d; 246 } 247 return (position < o.position) ? -1 : ((position == o.position) ? 0 : 1); 248 } 249 250 @Override 251 public String toString() { 252 return "FreeSlot{" + 253 "position=" + position + 254 ", size=" + size + 255 '}'; 256 } 257 258 } 259 260}