Skip to content

Commit

Permalink
Implements TreeNode#pointerGen
Browse files Browse the repository at this point in the history
Used to extract generation from a read reult containing a successfully
read pointer.

Depending on if the pointer comes from node header (sibling or newGen)
or if it is a child pointer, TreeNode need to use different methods
for setting the offset before delegating to GSP.
  • Loading branch information
tinwelint committed Dec 16, 2016
1 parent cab1cec commit 019978d
Show file tree
Hide file tree
Showing 4 changed files with 268 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ class GenSafePointerPair
static final long POINTER_MASK = 0x0000FFFF_FFFFFFFFL;
static final long GEN_OFFSET_MASK = 0x0FFF0000_00000000L;
static final long GEN_OFFSET_TYPE_MASK = FLAG_ABS_OFFSET | FLAG_LOGICAL_POS;
static final long HEADER_MASK = ~POINTER_MASK;
static final long MAX_GEN_OFFSET_MASK = 0xFFF;

/**
* Reads a GSPP, returning the read pointer or a failure. Check success/failure using {@link #isSuccess(long)}
Expand Down Expand Up @@ -208,6 +210,11 @@ private static long buildSuccessfulReadResult( long slot, int logicalPos, int gs
boolean isLogicalPos = logicalPos != NO_LOGICAL_POS;
long offsetType = isLogicalPos ? FLAG_LOGICAL_POS : FLAG_ABS_OFFSET;
long genOffset = isLogicalPos ? logicalPos : gsppOffset;
if ( (genOffset & ~MAX_GEN_OFFSET_MASK) != 0 )
{
throw new IllegalArgumentException( "Illegal genOffset:" + genOffset + ", it would be too large, max is " +
MAX_GEN_OFFSET_MASK );
}
return FLAG_SUCCESS | FLAG_READ | slot | offsetType | genOffset << SHIFT_GEN_OFFSET | pointer;
}

Expand Down Expand Up @@ -526,6 +533,11 @@ static boolean isLogicalPos( long readResult )

static int genOffset( long readResult )
{
if ( (readResult & HEADER_MASK) == 0 )
{
throw new IllegalArgumentException( "Expected a header in read result, but read result was " + readResult );
}

return Math.toIntExact( (readResult & GEN_OFFSET_MASK) >>> SHIFT_GEN_OFFSET );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,20 @@ void setNewGen( PageCursor cursor, long newGenId, long stableGeneration, long un
GenSafePointerPair.assertSuccess( result );
}

long pointerGen( PageCursor cursor, long readResult )
{
if ( !GenSafePointerPair.isRead( readResult ) )
{
throw new IllegalArgumentException( "Expected read result, but got " + readResult );
}
int offset = GenSafePointerPair.genOffset( readResult );
int gsppOffset = GenSafePointerPair.isLogicalPos( readResult ) ? childOffset( offset ) : offset;
int gspOffset = GenSafePointerPair.resultIsFromSlotA( readResult ) ?
gsppOffset : gsppOffset + GenSafePointer.SIZE;
cursor.setOffset( gspOffset );
return GenSafePointer.readGeneration( cursor );
}

// BODY METHODS

KEY keyAt( PageCursor cursor, KEY into, int pos )
Expand Down Expand Up @@ -283,7 +297,7 @@ void setValueAt( PageCursor cursor, VALUE value, int pos )
long childAt( PageCursor cursor, int pos, long stableGeneration, long unstableGeneration )
{
cursor.setOffset( childOffset( pos ) );
return read( cursor, stableGeneration, unstableGeneration, NO_LOGICAL_POS );
return read( cursor, stableGeneration, unstableGeneration, pos );
}

void insertChildAt( PageCursor cursor, long child, int pos, int keyCount, byte[] tmp,
Expand Down Expand Up @@ -461,4 +475,11 @@ void writeChildren( PageCursor cursor, byte[] source, int sourcePos, int targetP
{
writeAll( cursor, source, sourcePos, targetPos, count, childOffset( 0 ), childSize() );
}

@Override
public String toString()
{
return "TreeNode[internalMax:" + internalMaxKeyCount +
", leafMax:" + leafMaxKeyCount + ", keySize:" + keySize + ", valueSize:" + valueSize + "]";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.index.gbptree;

import org.junit.Test;

import static org.junit.Assert.fail;

import static org.neo4j.index.gbptree.GenSafePointer.MIN_GENERATION;
import static org.neo4j.index.gbptree.GenSafePointerPair.MAX_GEN_OFFSET_MASK;
import static org.neo4j.index.gbptree.GenSafePointerPair.read;
import static org.neo4j.index.gbptree.GenSafePointerPair.write;
import static org.neo4j.io.ByteUnit.kibiBytes;

public class GenSafePointerPairAdditionalTest
{
@Test
public void shouldFailFastOnTooLargeGenOffset() throws Exception
{
// GIVEN
int pageSize = (int) kibiBytes( 8 );
PageAwareByteArrayCursor cursor = new PageAwareByteArrayCursor( pageSize );
cursor.next( 0 );
long firstGeneration = MIN_GENERATION;
long secondGeneration = firstGeneration + 1;
long thirdGeneration = secondGeneration + 1;
int offset = 0;
cursor.setOffset( offset );
write( cursor, 10, firstGeneration, secondGeneration );
cursor.setOffset( offset );
write( cursor, 11, secondGeneration, thirdGeneration );

try
{
// WHEN
cursor.setOffset( offset );
read( cursor, secondGeneration, thirdGeneration, (int) (MAX_GEN_OFFSET_MASK + 1) );
fail( "Should have failed" );
}
catch ( IllegalArgumentException e )
{
// THEN good
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.neo4j.index.gbptree.GenSafePointerPair.pointer;
import static org.neo4j.index.gbptree.GenSafePointerPair.resultIsFromSlotA;
import static org.neo4j.index.gbptree.TreeNode.NO_NODE_FLAG;

public class TreeNodeTest
Expand Down Expand Up @@ -590,7 +591,176 @@ public void shouldAssertPageSizeBigEnoughForAtLeastTwoKeys() throws Exception
}
}

private long remove( long[] from, int length, int position )
@Test
public void shouldReadPointerGenFromAbsoluteOffsetSlotA() throws Exception
{
// GIVEN
long gen = UNSTABLE_GENERATION;
long pointer = 12;
node.setRightSibling( cursor, pointer, STABLE_GENERATION, gen );

// WHEN
long readResult = node.rightSibling( cursor, STABLE_GENERATION, gen );
long readGen = node.pointerGen( cursor, readResult );

// THEN
assertEquals( pointer, pointer( readResult ) );
assertEquals( gen, readGen );
assertTrue( resultIsFromSlotA( readResult ) );
}

@Test
public void shouldReadPointerGenFromAbsoluteOffsetSlotB() throws Exception
{
// GIVEN
long gen = HIGH_GENERATION;
long oldPointer = 12;
long pointer = 123;
node.setRightSibling( cursor, oldPointer, STABLE_GENERATION, UNSTABLE_GENERATION );
node.setRightSibling( cursor, pointer, UNSTABLE_GENERATION, gen );

// WHEN
long readResult = node.rightSibling( cursor, UNSTABLE_GENERATION, gen );
long readGen = node.pointerGen( cursor, readResult );

// THEN
assertEquals( pointer, pointer( readResult ) );
assertEquals( gen, readGen );
assertFalse( resultIsFromSlotA( readResult ) );
}

@Test
public void shouldReadPointerGenFromLogicalPosSlotA() throws Exception
{
// GIVEN
long gen = UNSTABLE_GENERATION;
long pointer = 12;
int childPos = 2;
node.setChildAt( cursor, pointer, childPos, STABLE_GENERATION, gen );

// WHEN
long readResult = node.childAt( cursor, childPos, STABLE_GENERATION, gen );
long readGen = node.pointerGen( cursor, readResult );

// THEN
assertEquals( pointer, pointer( readResult ) );
assertEquals( gen, readGen );
assertTrue( resultIsFromSlotA( readResult ) );
}

@Test
public void shouldReadPointerGenFromLogicalPosZeroSlotA() throws Exception
{
// GIVEN
long gen = UNSTABLE_GENERATION;
long pointer = 12;
int childPos = 0;
node.setChildAt( cursor, pointer, childPos, STABLE_GENERATION, gen );

// WHEN
long readResult = node.childAt( cursor, childPos, STABLE_GENERATION, gen );
long readGen = node.pointerGen( cursor, readResult );

// THEN
assertEquals( pointer, pointer( readResult ) );
assertEquals( gen, readGen );
assertTrue( resultIsFromSlotA( readResult ) );
}

@Test
public void shouldReadPointerGenFromLogicalPosZeroSlotB() throws Exception
{
// GIVEN
long gen = HIGH_GENERATION;
long oldPointer = 13;
long pointer = 12;
int childPos = 0;
node.setChildAt( cursor, oldPointer, childPos, STABLE_GENERATION, UNSTABLE_GENERATION );
node.setChildAt( cursor, pointer, childPos, UNSTABLE_GENERATION, gen );

// WHEN
long readResult = node.childAt( cursor, childPos, UNSTABLE_GENERATION, gen );
long readGen = node.pointerGen( cursor, readResult );

// THEN
assertEquals( pointer, pointer( readResult ) );
assertEquals( gen, readGen );
assertFalse( resultIsFromSlotA( readResult ) );
}

@Test
public void shouldReadPointerGenFromLogicalPosSlotB() throws Exception
{
// GIVEN
long gen = HIGH_GENERATION;
long oldPointer = 12;
long pointer = 123;
int childPos = 2;
node.setChildAt( cursor, oldPointer, childPos, STABLE_GENERATION, UNSTABLE_GENERATION );
node.setChildAt( cursor, pointer, childPos, UNSTABLE_GENERATION, gen );

// WHEN
long readResult = node.childAt( cursor, childPos, UNSTABLE_GENERATION, gen );
long readGen = node.pointerGen( cursor, readResult );

// THEN
assertEquals( pointer, pointer( readResult ) );
assertEquals( gen, readGen );
assertFalse( resultIsFromSlotA( readResult ) );
}

@Test
public void shouldThrowIfReadingPointerGenOnWriteResult() throws Exception
{
// GIVEN
long writeResult = GenSafePointerPair.write( cursor, 123, STABLE_GENERATION, UNSTABLE_GENERATION );

try
{
// WHEN
node.pointerGen( cursor, writeResult );
fail( "Should have failed" );
}
catch ( IllegalArgumentException e )
{
// THEN good
}
}

@Test
public void shouldThrowIfReadingPointerGenOnZeroReadResultHeader() throws Exception
{
// GIVEN
long pointer = 123;

try
{
// WHEN
node.pointerGen( cursor, pointer );
fail( "Should have failed" );
}
catch ( IllegalArgumentException e )
{
// THEN good
}
}

@Test
public void shouldUseLogicalGenPosWhenReadingChild() throws Exception
{
// GIVEN
long child = 101;
int pos = 3;
node.setChildAt( cursor, child, pos, STABLE_GENERATION, UNSTABLE_GENERATION );

// WHEN
long result = node.childAt( cursor, pos, STABLE_GENERATION, UNSTABLE_GENERATION );

// THEN
assertTrue( GenSafePointerPair.isLogicalPos( result ) );
}

private static long remove( long[] from, int length, int position )
{
long result = from[position];
for ( int i = position; i < length; i++ )
Expand All @@ -600,7 +770,7 @@ private long remove( long[] from, int length, int position )
return result;
}

private void insert( long[] into, int length, long value, int position )
private static void insert( long[] into, int length, long value, int position )
{
for ( int i = length - 1; i >= position; i-- )
{
Expand Down

0 comments on commit 019978d

Please sign in to comment.