blob: 2da912e67596d9047d29ddcd110ec24fb832def6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.uima.cas.impl;
import java.util.Arrays;
import org.apache.uima.internal.util.IntArrayUtils;
import org.apache.uima.internal.util.Misc;
/**
* the v2 CAS heap - used in modeling some binary (de)serialization
*/
public final class Heap {
private static final boolean debugLogShrink = false;
// static {
// debugLogShrink = System.getProperty("uima.debug.ihs") != null;
// }
/**
* Minimum size of the heap. Currently set to <code>1000</code>.
*/
public static final int MIN_SIZE = 1024;
/**
* Default size of the heap. Currently set to <code>500000</code>(2 MB).
*/
public static final int DEFAULT_SIZE = 1024 * 512; // 2 MB pages
private static final int MULTIPLICATION_LIMIT = 1024 * 1024 * 16;
// Initial size of the heap. This is also the size the heap will be reset to
// on a full reset.
private int initialSize;
// The array that represents the actual heap is package private and
// can be directly addressed by the LowLevelCAS.
int[] heap;
// Next free position on the heap.
private int pos;
// End of heap. In the current implementation, this is the same as
// this.heap.length at all times.
private int max;
private final int[] shrinkableCount = new int[1];
// Serialization constants. There are holes in the numbering for historical
// reasons. Keep the holes for compatibility.
private static final int SIZE_POS = 0;
private static final int TMPP_POS = 1;
private static final int TMPM_POS = 2;
private static final int PGSZ_POS = 5;
private static final int AVSZ_POS = 6;
private static final int AVST_POS = 7;
/**
* Default constructor.
*/
public Heap() {
this(DEFAULT_SIZE);
}
/**
* Constructor lets you set initial heap size. Use only if you know what you're doing.
*
* @param initialSize
* The initial heap size. If this is smaller than the {@link #MIN_SIZE MIN_SIZE}, the
* default will be used instead.
*/
public Heap(int initialSize) {
if (initialSize < MIN_SIZE) {
initialSize = MIN_SIZE;
}
this.initialSize = initialSize;
initHeap();
}
private void initHeap() {
heap = new int[initialSize];
pos = 1; // 0 is not a valid address
max = heap.length;
}
private void initHeap(int size) {
heap = new int[size];
pos = 1; // 0 is not a valid address
max = heap.length;
}
void reinit(int[] md, int[] shortHeap) {
if (md == null) {
reinitNoMetaData(shortHeap);
return;
}
// assert(md != null);
// assert(shortHeap != null);
final int heapSize = md[SIZE_POS];
pos = md[TMPP_POS];
max = md[TMPM_POS];
initialSize = md[PGSZ_POS];
// Copy the shortened version of the heap into a full version.
heap = new int[heapSize];
System.arraycopy(shortHeap, 0, heap, 0, shortHeap.length);
}
/**
* Re-init the heap without metadata. Use default values for metadata.
*
* @param shortHeap
*/
private void reinitNoMetaData(int[] shortHeap) {
initialSize = (shortHeap.length < MIN_SIZE) ? MIN_SIZE : shortHeap.length;
if (shortHeap.length >= initialSize) {
heap = shortHeap;
} else {
System.arraycopy(shortHeap, 0, heap, 0, shortHeap.length);
}
// Set position and max.
pos = shortHeap.length;
// this.max = this.initialSize;
max = heap.length; // heap could be repl by short heap
}
/**
* Re-create the heap for the given size. Just use the size of the incoming heap, unless it's
* smaller than our minimum. It is expected that the caller will then fill in the new heap up to
* newSize.
*
* @param newSize
*/
void reinitSizeOnly(int newSize) {
initialSize = (newSize < MIN_SIZE) ? MIN_SIZE : newSize;
heap = new int[initialSize];
// Set position and max.
pos = newSize;
max = initialSize;
}
/**
* Return the number of cells used (including slot 0 reserved for null)
*/
int getCellsUsed() {
return pos;
}
/**
* @return The overall size of the heap (in words) (including unused space).
*/
int getHeapSize() {
return heap.length;
}
int[] getMetaData() {
final int arSize = AVST_POS;
int[] ar = new int[arSize];
ar[SIZE_POS] = heap.length;
ar[TMPP_POS] = pos;
ar[TMPM_POS] = max;
ar[PGSZ_POS] = initialSize;
final int availablePagesSize = 0;
ar[AVSZ_POS] = availablePagesSize;
return ar;
}
// Grow the heap.
private void grow() {
final int start = heap.length;
// This will grow the heap by doubling its size if it's smaller than
// MULTIPLICATION_LIMIT, and by MULTIPLICATION_LIMIT if it's larger.
heap = IntArrayUtils.ensure_size(heap, start + initialSize, 2,
MULTIPLICATION_LIMIT);
max = heap.length;
}
static int getRoundedSize(int size) {
if (size < MULTIPLICATION_LIMIT) {
return Misc.nextHigherPowerOf2(size);
}
return size + (MULTIPLICATION_LIMIT << 1);
}
/**
* Reset the temporary heap.
*/
public void reset() {
this.reset(false);
}
/**
* Reset the temporary heap.
*
* Logic for shrinking:
*
* Based on a short history of the sizes needed to hold the larger of the previous 2 sizes (Note:
* can be overridden by calling reset() multiple times in a row) Never shrink below initialSize
*
* Shrink in exact reverse sequence of growth - using the subtraction method and then (for small
* enough sizes) the dividing method
*
* Shrink 1/2 the distance to the size needed to hold the large of the prev 2 sizes
*/
void reset(boolean doFullReset) {
if (doFullReset) {
if (debugLogShrink)
System.out.format("Debug shrink Heap full reset from %,d%n", getHeapSize());
this.initHeap();
} else {
final int curCapacity = getHeapSize();
final int curSize = getCellsUsed();
// shrink based on max of prevSize and curSize
final int newCapacity = CommonAuxHeap.computeShrunkArraySize(curCapacity, curSize, 2,
MULTIPLICATION_LIMIT, initialSize, shrinkableCount);
if (newCapacity == curCapacity) {
Arrays.fill(heap, 0, pos, 0);
} else {
if (debugLogShrink)
System.out.format("Debug shrink Heap from %,d to %,d%n", curCapacity, newCapacity);
this.initHeap(newCapacity);
}
pos = 1;
}
}
/**
* Add a structure to the heap.
*
* @param fs
* The input structure.
* @return The position where the structure was added, i.e., a pointer to the first element of the
* structure.
*/
public int add(int[] fs) {
while ((pos + fs.length) >= max) {
grow();
}
System.arraycopy(fs, 0, heap, pos, fs.length);
final int pos1 = pos;
pos += fs.length;
return pos1;
}
/**
* Reserve space for <code>len</code> items on the heap and set the first item to
* <code>val</code>. The other items are set to <code>0</code>.
*
* @param len
* The length of the new structure.
* @param val
* The value of the first cell in the new structure.
* @return The position where the structure was added, i.e., a pointer to the first element of the
* structure.
*/
public int add(int len, int val) {
while ((pos + len) >= max) {
grow();
}
final int pos1 = pos;
pos += len;
heap[pos1] = val;
return pos1;
}
public int getNextId() {
return pos;
}
public void grow(int len) {
while ((pos + len) >= max) {
grow();
}
pos += len;
}
// used by JCas to default the size the JCasHashMap
public int getInitialSize() {
return initialSize;
}
public int[] toArray() {
int[] r = new int[pos];
System.arraycopy(heap, 0, r, 0, pos);
return r;
}
}