/** 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 Apr 20, 2011 */ package com.bigdata.htree; import java.util.Formatter; import junit.framework.TestCase2; /** * Unit tests for {@link HTreeUtil}. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ public class TestHTreeUtil extends TestCase2 { /** * */ public TestHTreeUtil() { } /** * @param name */ public TestHTreeUtil(String name) { super(name); } /** * Unit test for {@link HTreeUtil#getMapSize(int)}. */ public void test_getMapSize() { assertEquals(1/* addressSpaceSize */, HTreeUtil.getMapSize(1)/* fanout */); assertEquals(1/* addressSpaceSize */, HTreeUtil.getMapSize(2)/* fanout */); assertEquals(2/* addressSpaceSize */, HTreeUtil.getMapSize(3)/* fanout */); assertEquals(2/* addressSpaceSize */, HTreeUtil.getMapSize(4)/* fanout */); assertEquals(3/* addressSpaceSize */, HTreeUtil.getMapSize(5)/* fanout */); assertEquals(3/* addressSpaceSize */, HTreeUtil.getMapSize(6)/* fanout */); assertEquals(3/* addressSpaceSize */, HTreeUtil.getMapSize(7)/* fanout */); assertEquals(3/* addressSpaceSize */, HTreeUtil.getMapSize(8)/* fanout */); assertEquals(4/* addressSpaceSize */, HTreeUtil.getMapSize(9)/* fanout */); assertEquals(5/* addressSpaceSize */, HTreeUtil.getMapSize(32)/* fanout */); assertEquals(10/* addressSpaceSize */, HTreeUtil.getMapSize(1024)/* fanout */); } /** Unit test for {@link HTreeUtil#isPowerOf2(int)}. */ public void test_isPowerOf2() { for (int i = 0; i < 32; i++) { final int v = 1<<i; assertTrue(HTreeUtil.isPowerOf2(v)); if (v > 1) assertFalse(HTreeUtil.isPowerOf2(v + 1)); } } /** * Prints various tables and runs consistency tests on the htree math * operations dealing with addressBits, globalDepth, localDepth, etc. * <p> * <dl> * <dt>addressBits</dt> * <dd>Size of the address space (fixed for a given hash tree).</dd> * <dt>globalDepth</dt> * <dd>The size of the address space for a buddy hash table on a directory * page.</dd> * <dt>nbuddies</dt> * <dd>The #of buddy tables (buckets) on a directory (bucket) page. This * depends solely on <i>addressBits</i> (a constant) and <i>globalDepth</i>. * </dd> * <dt>slots/buddy</dt> * <dd>The #of directory entries in a buddy hash table. This depends solely * on <i>globalDepth</i>.</dd> * <dt>npointers</dt> * <dd>The #of pointers in the parent which point to a given child (given * based on a scan of the parent).</dd> * <dt>localDepth</dt> * <dd>The #of bits in the key examined at a given level in the tree. The * range is [0:addressBits]. This depends solely on the <i>globalDepth</i> * of a directory page and the #of pointers to child (<i>npointers</i>) in * that directory page.</dd> * <dt>nbits</dt> * <dd>This is <code>(globalDepth-localDepth)</code>. It is the #of bits of * the hash code which will be used to compute the <i>buddyOffset</i>.</dd> * <dt>hashBits</dt> * <dd>The LSB <code>globalDepth-localDepth</code> bits of the hash code. * This is used to compute the <i>buddyOffset</i>.</dd> * <dt>buddyOffset</dt> * <dd>The offset of the buddy hash table or buddy bucket within the child.</dd> * </dl> */ private void doHTreeMath(final int addressBits, final StringBuilder sb) { final Formatter f = new Formatter(sb); f.format("\n%12s %12s %12s %12s %12s %12s %5s %10s %12s",// %4s %12s", // "addressBits", "globalDepth", // "nbuddies", "#slots/buddy", // "npointers", "localDepth", // "nbits", "hashBits", "buddyOffset"// // "chk", "pass"// ); int nfail = 0; for (int globalDepth = 0; globalDepth <= addressBits; globalDepth++) { // #of buddy tables on a page. final int nbuddies = (1 << addressBits) / (1 << globalDepth); // #of address slots in each buddy hash table. final int slotsPerBuddy = (1 << globalDepth); for (int npointers = (1 << globalDepth); npointers >= 1; npointers--) { if ((npointers & -npointers) != npointers) // not a power of 2 continue; final int localDepth = HTreeUtil.getLocalDepth(addressBits, globalDepth, npointers); if (localDepth < 0) { log.error("localDepth must be non-negative."); nfail++; } if (localDepth > globalDepth) { log.error("localDepth must be LTE globalDepth."); nfail++; } // cross check on localDepth by computing npointers. final int chk = 1 << (globalDepth - localDepth); if (chk != npointers) { log.error("npointers cross check failed."); nfail++; } /* * Find the buddy offset for a sequence of relevant hash bits * having a 1 in each of the bit positions to which the buddy * indexing is sensitive given the globalDepth of the parent and * the localDepth of the child. This gives us an overview of the * buddy indexing without requiring us to print off a table for * each distinct (globalDepth-localDepth) bit integer. */ // // #of buddy tables on the child page (same formula as above, // // but using the local depth of the child rather than global // // depth of the parent). // final int nChildBuddies = (1 << addressBits) / (1 << localDepth); // // #of address slots in each buddy hash table (same formula as // // above, but using the local depth of the child rather than the // // global depth of the parent). // final int slotsPerChildBuddy = (1 << localDepth); // // total #of address slots in the child page across all buddies // // on that page. // final int childSlots = nChildBuddies * slotsPerChildBuddy; final int NBITS = (globalDepth - localDepth); for (int nbits = 0; nbits <= NBITS; nbits++) { // zero for zero bits. otherwise 1<<(nbits-1). final int hashBits = nbits == 0 ? 0 : (1 << nbits - 1); final int buddyOffset = HTreeUtil.getBuddyOffset(hashBits, globalDepth, localDepth); if (buddyOffset < 0) { log.error("buddyOffset is negative."); nfail++; } if (buddyOffset >= (1 << addressBits)) { log.error("buddyOffset too large."); nfail++; } f.format( "\n%12d %12d %12d %12d %12d %12d %5d %10d %12d",// %12d %4s", // addressBits, globalDepth, nbuddies, slotsPerBuddy, npointers, localDepth,// /*childSlots,*/ nbits, hashBits, buddyOffset // chk, chk == npointers ? "yes" : "no"// ); } } } f.flush(); assertEquals("nfail", 0, nfail); } /** * Exercise the hash tree math for a 1-bit address space. This is the * absolute minimum size for an address space and the address space is only * capable of making a single bit distinction per level in the hash tree. */ public void test_htree_math_addressBits_1() { final int addressBits = 1; final StringBuilder sb = new StringBuilder(); doHTreeMath(addressBits, sb); if (log.isInfoEnabled()) log.info(sb.toString()); } /** * Exercise the hash tree math for a 2-bit address space. */ public void test_htree_math_addressBits_2() { final int addressBits = 2; final StringBuilder sb = new StringBuilder(); doHTreeMath(addressBits, sb); if (log.isInfoEnabled()) log.info(sb.toString()); } /** * Exercise the hash tree math for a 4-bit address space. */ public void test_htree_math_addressBits_4() { final int addressBits = 4; final StringBuilder sb = new StringBuilder(); doHTreeMath(addressBits, sb); if (log.isInfoEnabled()) log.info(sb.toString()); } /** * Exercise the hash tree math for an 8-bit address space. */ public void test_htree_math_addressBits_8() { final int addressBits = 8; final StringBuilder sb = new StringBuilder(); doHTreeMath(addressBits, sb); if (log.isInfoEnabled()) log.info(sb.toString()); } /** * Exercise the hash tree math for an 10-bit address space (this corresponds * to a 4k page). */ public void test_htree_math_addressBits_10() { final int addressBits = 10; final StringBuilder sb = new StringBuilder(); doHTreeMath(addressBits, sb); if (log.isInfoEnabled()) log.info(sb.toString()); } /** * Exercise the hash tree math for an 11-bit address space (this corresponds * to an 11k page). */ public void test_htree_math_addressBits_11() { final int addressBits = 11; final StringBuilder sb = new StringBuilder(); doHTreeMath(addressBits, sb); if (log.isInfoEnabled()) log.info(sb.toString()); } /** * Unit test for {@link HTreeUtil#getBuddyOffset(int, int, int)} for the * case where globalDepth:=2 and localDepth:=0. For this case, the child * will have 4 buddies and the (globalDepth-localDepth) lower bits of the * int32 hash code will be used to index into those buddies. */ public void test_getBuddyOffset_globalDepth2_localDepth0() { final int globalDepth = 2; final int localDepth = 0; assertEquals(0, HTreeUtil.getBuddyOffset(0x00/* 00 */, globalDepth, localDepth)); assertEquals(1, HTreeUtil.getBuddyOffset(0x01/* 01 */, globalDepth, localDepth)); assertEquals(2, HTreeUtil.getBuddyOffset(0x02/* 10 */, globalDepth, localDepth)); assertEquals(3, HTreeUtil.getBuddyOffset(0x03/* 11 */, globalDepth, localDepth)); } public void test_getBuddyOffset_globalDepth2_localDepth1() { final int globalDepth = 2; final int localDepth = 1; assertEquals(0, HTreeUtil.getBuddyOffset(0x00/* 00 */, globalDepth, localDepth)); assertEquals(2, HTreeUtil.getBuddyOffset(0x01/* 01 */, globalDepth, localDepth)); } }