/******************************************************************************* * Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek * * Licensed 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 de.mxro.thrd.jdbm2V22.recman; /** * The data that comes at the start of a record of data. It stores * both the current size and the avaliable size for the record - the latter * can be bigger than the former, which allows the record to grow without * needing to be moved and which allows the system to put small records * in larger free spots. * <p/> * In JDBM 1.0 both values were stored as four-byte integers. This was very wastefull. * Now available size is stored in two bytes, it is compressed, so maximal value is up to 120 MB (not sure with exact number) * Current size is stored as two-byte-unsigned-short difference from Available Size. */ final class RecordHeader { // offsets private static final short O_CURRENTSIZE = 0; // int currentSize private static final short O_AVAILABLESIZE = Magic.SZ_UNSIGNED_SHORT; // int availableSize static final int SIZE = O_AVAILABLESIZE + Magic.SZ_UNSIGNED_SHORT; /** * Maximal differnece between current and available size, * Maximal value is resorved for currentSize 0, so use -1 */ static final int MAX_SIZE_SPACE = BlockIo.UNSIGNED_SHORT_MAX -1; /** Returns the current size */ static int getCurrentSize(final BlockIo block, final short pos) { int s = block.readUnsignedshort(pos + O_CURRENTSIZE); if(s == BlockIo.UNSIGNED_SHORT_MAX) return 0; return getAvailableSize(block, pos) - s; } /** Sets the current size */ static void setCurrentSize(final BlockIo block, final short pos,int value) { if(value == 0){ block.writeUnsignedShort(pos + O_CURRENTSIZE, BlockIo.UNSIGNED_SHORT_MAX); return; } int availSize = getAvailableSize(block,pos); if(value < (availSize - MAX_SIZE_SPACE) || value>availSize) throw new IllegalArgumentException("currentSize out of bounds, need to realocate "+value+ " - "+availSize); block.writeUnsignedShort(pos + O_CURRENTSIZE, availSize - value); } /** Returns the available size */ static int getAvailableSize(final BlockIo block, final short pos) { int val = block.readUnsignedshort(pos + O_AVAILABLESIZE); return deconvertAvailSize(val); } /** Sets the available size */ static void setAvailableSize(final BlockIo block, final short pos,int value) { // if(value != roundAvailableSize(value)) // throw new IllegalArgumentException("value is not rounded"); int oldCurrSize = getCurrentSize(block,pos); block.writeUnsignedShort(pos + O_AVAILABLESIZE, convertAvailSize(value)); setCurrentSize(block,pos,oldCurrSize); } private static int convertAvailSize(final int recordSize){ int multiplyer = 0; int counter = 0; if(recordSize<=base1){ multiplyer = 0; counter = recordSize / multi0; }else if(recordSize<base2){ multiplyer = 1 <<14; int val2 = recordSize -base1; counter = val2/multi1; if(val2 %multi1 != 0) counter++; }else if(recordSize<base3){ multiplyer = 2 <<14; int val2 = recordSize -base2; counter = val2/multi2; if(val2 %multi2 != 0) counter++; }else{ multiplyer = 3 <<14; int val2 = recordSize -base3; counter = val2/multi3; if(val2 %multi3 != 0) counter++; } if(counter>=(1<<14)) throw new InternalError(""+recordSize); return multiplyer + counter; } private static int deconvertAvailSize(int recordSize){ int multiplier = (recordSize & sizeMask) >>14; int counter = recordSize - (recordSize &sizeMask); switch (multiplier){ case 0: return counter * multi0; case 1: return base1 + counter * multi1; case 2: return base2 + counter * multi2; case 3: return base3 + counter * multi3; default: throw new InternalError("error deconverting: "+recordSize); } } static final int sizeMask = 1<<15 | 1<<14; static final int multi0 = 1; static final int multi1 = 1<<4; static final int multi2 = 1<<8; static final int multi3 = 1<< 13; static final int base0 = 0; static final int base1 = base0 + multi0 * ((1<<14)-2); static final int base2 = base1 + multi1 * ((1<<14)-2); static final int base3 = base2 + multi2 * ((1<<14)-2); static final int base4 = base3 + multi3 * (1<<14)-2; static final int MAX_RECORD_SIZE = roundAvailableSize(base4 - multi3 * 100); static int roundAvailableSize(int value){ if(value>MAX_RECORD_SIZE && MAX_RECORD_SIZE!=0) new InternalError("Maximal record size ("+MAX_RECORD_SIZE+") exceeded: "+value); return deconvertAvailSize(convertAvailSize(value)); } }