/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Dec 26, 2006
*/
package com.bigdata.btree;
import java.util.UUID;
import com.bigdata.rawstore.WormAddressManager;
import com.bigdata.util.Bytes;
/**
* Tests logic to encode and decode the offsets within regions in an
* {@link IndexSegmentStore}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*
* @see IndexSegment
* @see IndexSegmentBuilder
* @see IndexSegmentStore
*/
public class TestIndexSegmentAddressManager extends AbstractBTreeTestCase {
/**
*
*/
public TestIndexSegmentAddressManager() {
}
/**
* @param name
*/
public TestIndexSegmentAddressManager(String name) {
super(name);
}
/**
* Test works through the basic operations required to encode and decode
* an address for a node and a leaf.
*
* @see IndexSegmentRegion
*/
public void test_bitMath() {
// addr:=1, shift left by one, result is (2).
assertEquals(2,1<<1);
// set low bit to true to indicate a node (vs a leaf).
assertEquals(3,2|1);
// test low bit - indicates a node.
assertTrue((3&1)==1);
// test low bit - indicates a leaf.
assertTrue((2&1)==0);
// shift right one to recover the address (low bit discarded).
assertEquals(1,2>>1);
// shift right one to recover the address (low bit discarded).
assertEquals(1,3>>1);
}
/**
* Test encoding and decoding of the region code and the offset.
*/
public void test_regionEnum_encodeDecode() {
{
final long expectedOffset = 12L;
final long encodedOffset = IndexSegmentRegion.BASE.encodeOffset(expectedOffset);
assertEquals(IndexSegmentRegion.BASE,IndexSegmentRegion.decodeRegion(encodedOffset));
assertEquals(expectedOffset,IndexSegmentRegion.decodeOffset(encodedOffset));
}
{
final long expectedOffset = 12L;
final long encodedOffset = IndexSegmentRegion.NODE.encodeOffset(expectedOffset);
assertEquals(IndexSegmentRegion.NODE,IndexSegmentRegion.decodeRegion(encodedOffset));
assertEquals(expectedOffset,IndexSegmentRegion.decodeOffset(encodedOffset));
}
{
final long expectedOffset = 12L;
final long encodedOffset = IndexSegmentRegion.BLOB.encodeOffset(expectedOffset);
assertEquals(IndexSegmentRegion.BLOB,IndexSegmentRegion.decodeRegion(encodedOffset));
assertEquals(expectedOffset,IndexSegmentRegion.decodeOffset(encodedOffset));
}
}
/**
* Unit test verifies that an offset of <code>0L</code> (not a full
* address, just an offset) is correctly encoded and decoded.
*/
public void test_encodeDecode_offsetZero() {
final long expectedOffset = 0L;
final long encodedOffset = IndexSegmentRegion.BLOB
.encodeOffset(expectedOffset);
assertEquals(IndexSegmentRegion.BLOB, IndexSegmentRegion.decodeRegion(encodedOffset));
assertEquals(expectedOffset, IndexSegmentRegion.decodeOffset(encodedOffset));
}
/**
* Test of correct decoding of addresses by the
* {@link IndexSegmentAddressManager}.
*/
public void test_addressManager_decode() {
/*
* Note: allows records up to 64M in length.
*/
final int offsetBits = WormAddressManager.SCALE_OUT_OFFSET_BITS;
/*
* Fake a checkpoint record. The only parts of this that we need are the
* offset and extent of the node and blob regions. Those offsets are
* absolute are absolute offsets into the file. The offsets of those
* regions are used to decode addresses in the non-BASE regions of the
* file.
*
* Note: the checkpoint ctor has a variety of assertions so that
* constrains how we can generate this fake checkpoint record.
*/
final IndexSegmentCheckpoint checkpoint;
final long extentLeaves = 216;
final long offsetLeaves = IndexSegmentCheckpoint.SIZE; // but base region begins at 0L!
final long offsetNodes;
final long offsetBlobs;
final long extentNodes;
final long extentBlobs;
final long length;
{
// Used to encode the addresses.
final WormAddressManager am = new WormAddressManager(offsetBits);
// final long addrLeaves = am.toAddr(sizeLeaves, IndexSegmentRegion.BASE
// .encodeOffset(offsetLeaves));
extentNodes = 123;
offsetNodes = offsetLeaves + extentLeaves;
// final long addrNodes = am.toAddr(extentNodes, IndexSegmentRegion.BASE
// .encodeOffset(offsetNodes));
// Note: only one node and it is the root node.
final long addrRoot = am.toAddr((int)extentNodes, IndexSegmentRegion.BASE
.encodeOffset(offsetNodes));
final long addrFirstLeaf = addrRoot;
final long addrLastLeaf = addrRoot;
extentBlobs = Bytes.megabyte32 * 20;
offsetBlobs = offsetNodes + extentNodes;
// final long addrBlobs = am.toAddr(extentBlobs, IndexSegmentRegion.BASE
// .encodeOffset(offsetBlobs));
final long addrBloom = 0L;
final int sizeMetadata = 712;
final long offsetMetadata = offsetBlobs + extentBlobs;
final long addrMetadata = am.toAddr(sizeMetadata, IndexSegmentRegion.BASE
.encodeOffset(offsetMetadata));
length = offsetMetadata + sizeMetadata;
final boolean compactingMerge = r.nextBoolean();
final boolean useChecksums= r.nextBoolean();
checkpoint = new IndexSegmentCheckpoint(
offsetBits,//
1, // height
2, // nleaves
1, // nnodes
29, // nentries
128,// maxNodeOrLeafLength`
offsetLeaves, extentLeaves,//
offsetNodes, extentNodes, //
offsetBlobs, extentBlobs, //
addrRoot,//
addrMetadata,//
addrBloom, //
addrFirstLeaf,//
addrLastLeaf,//
length,//
compactingMerge,//
useChecksums,//
UUID.randomUUID(),// segmentUUID,
System.currentTimeMillis()//commitTime
);
if(log.isInfoEnabled())
log.info("Checkpoint: " + checkpoint);
}
/*
* Used to decode the addresses.
*/
final IndexSegmentAddressManager am = new IndexSegmentAddressManager(checkpoint);
{
final int nbytes = 12;
final long offset = 44L;
final long offsetBase = 0L;
doRoundTripTest(IndexSegmentRegion.BASE, nbytes, offset, offsetBase
+ offset, am);
doRoundTripTest(IndexSegmentRegion.NODE, nbytes, offset,
offsetNodes + offset, am);
doRoundTripTest(IndexSegmentRegion.BLOB, nbytes, offset,
offsetBlobs + offset, am);
}
/*
* Now verify that range checks work within each region.
*/
/*
* BASE
*/
// legal.
assertEquals(0L,am.getOffset(am.toAddr(1, IndexSegmentRegion.BASE.encodeOffset(0L))));
assertEquals(0L,am.getOffset(am.toAddr((int)(length-1), IndexSegmentRegion.BASE.encodeOffset(0L))));
assertEquals(0L,am.getOffset(am.toAddr((int)length, IndexSegmentRegion.BASE.encodeOffset(0L))));
// illegal.
try {
assertEquals(0L,am.getOffset(am.toAddr((int)(length+1), IndexSegmentRegion.BASE.encodeOffset(0L))));
fail("Expecting exception");
} catch(AssertionError ex) {
log.info("Ignoring expected exception: "+ex);
}
/*
* NODES
*/
// legal.
assertEquals(offsetNodes,am.getOffset(am.toAddr(1, IndexSegmentRegion.NODE.encodeOffset(0L))));
assertEquals(offsetNodes,am.getOffset(am.toAddr((int)(extentNodes-1), IndexSegmentRegion.NODE.encodeOffset(0L))));
assertEquals(offsetNodes,am.getOffset(am.toAddr((int)extentNodes, IndexSegmentRegion.NODE.encodeOffset(0L))));
// illegal.
try {
assertEquals(offsetNodes,am.getOffset(am.toAddr((int)(extentNodes+1), IndexSegmentRegion.NODE.encodeOffset(0L))));
fail("Expecting exception");
} catch(AssertionError ex) {
log.info("Ignoring expected exception: "+ex);
}
/*
* BLOBS
*/
// legal.
assertEquals(offsetBlobs,am.getOffset(am.toAddr(1, IndexSegmentRegion.BLOB.encodeOffset(0L))));
assertEquals(offsetBlobs,am.getOffset(am.toAddr((int)(extentBlobs-1), IndexSegmentRegion.BLOB.encodeOffset(0L))));
assertEquals(offsetBlobs,am.getOffset(am.toAddr((int)extentBlobs, IndexSegmentRegion.BLOB.encodeOffset(0L))));
// illegal.
try {
assertEquals(offsetBlobs,am.getOffset(am.toAddr((int)(extentBlobs+1), IndexSegmentRegion.BLOB.encodeOffset(0L))));
fail("Expecting exception");
} catch(AssertionError ex) {
log.info("Ignoring expected exception: "+ex);
}
}
/**
* Test helper forms an encoded address whose offset is relative to the
* specified region and then attempts to decode that address.
*
* @param region
* The region.
* @param nbytes
* The #of bytes in the addressed record.
* @param offset
* The offset of the addressed record (relative to the start of
* the region).
* @param am
* The object used to decode the address.
*/
protected static void doRoundTripTest(IndexSegmentRegion region, int nbytes,
long offset, long expectedOffset, IndexSegmentAddressManager am) {
final long addr = am.toAddr(nbytes, region.encodeOffset(offset));
assertEquals("nbytes in " + region + " region", nbytes, am
.getByteCount(addr));
assertEquals("offset in " + region + " region", expectedOffset, am
.getOffset(addr));
}
}