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}