Skip to content

Commit

Permalink
Flatten RecordFormat call stacks
Browse files Browse the repository at this point in the history
and favoring composition of small methods for ability to inline instead.
  • Loading branch information
tinwelint committed Mar 3, 2016
1 parent 6bd47db commit 8d59596
Show file tree
Hide file tree
Showing 16 changed files with 417 additions and 320 deletions.
Expand Up @@ -1077,10 +1077,10 @@ public boolean next()
{
record.setId( currentId );
long pageId = pageIdForRecord( currentId );
int offset = offsetForId( currentId );
if ( pageId == pageCursor.getCurrentPageId() ||
pageCursor.next( pageIdForRecord( currentId ) ) )
pageCursor.next( pageId ) )
{
int offset = offsetForId( currentId );
do
{
prepareForReading( pageCursor, offset, record );
Expand Down
Expand Up @@ -19,15 +19,11 @@
*/
package org.neo4j.kernel.impl.store.format;

import java.io.IOException;
import java.util.function.Function;

import org.neo4j.io.pagecache.PageCursor;
import org.neo4j.io.pagecache.PagedFile;
import org.neo4j.kernel.impl.store.RecordStore;
import org.neo4j.kernel.impl.store.StoreHeader;
import org.neo4j.kernel.impl.store.record.AbstractBaseRecord;
import org.neo4j.kernel.impl.store.record.RecordLoad;

/**
* Implementation of a very common type of format where the first byte, at least one bit in it,
Expand All @@ -38,72 +34,32 @@
*/
public abstract class BaseOneByteHeaderRecordFormat<RECORD extends AbstractBaseRecord> extends BaseRecordFormat<RECORD>
{
private final int inUseBitMaskForFirstByte;

protected BaseOneByteHeaderRecordFormat( Function<StoreHeader,Integer> recordSize, int recordHeaderSize,
int inUseBitMaskForFirstByte )
{
super( recordSize, recordHeaderSize, inUseBitMaskForFirstByte );
super( recordSize, recordHeaderSize );
this.inUseBitMaskForFirstByte = inUseBitMaskForFirstByte;
}

@Override
public final void read( RECORD record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
throws IOException
protected void markFirstByteAsUnused( PageCursor cursor )
{
byte headerByte = cursor.getByte();
boolean inUse = isInUse( headerByte );
if ( mode.shouldLoad( inUse ) )
{
doRead( record, cursor, recordSize, storeFile, headerByte, inUse );
}
byte inUseByte = cursor.getByte( cursor.getOffset() );
inUseByte &= ~inUseBitMaskForFirstByte;
cursor.putByte( inUseByte );
}

/**
* Reads contents at {@code cursor} into the given record. This method is only called if the {@link RecordLoad}
* mode in {@link #read(AbstractBaseRecord, PageCursor, RecordLoad, int, PagedFile)} thinks it's OK to load the
* record, given its inUse status.
*
* @param record to put read data into, replacing any existing data in that record object.
* @param cursor {@link PageCursor} to read data from.
* See {@link RecordStore#getRecord(long, AbstractBaseRecord, RecordLoad)} for more information.
* @param recordSize size of records of this format. This is passed in like this since not all formats
* know the record size in advance, but may be read from store header when opening the store.
* @param storeFile {@link PagedFile} to get additional {@link PageCursor} from, if need be.
* @param headerByte the first byte read, in order to determine inUse status and other header flags.
* @param inUse whether or not the record is in use. Keep in mind that this method may be called
* even on an unused record, depending on {@link RecordLoad} mode.
* @throws IOException on error reading.
*/
protected abstract void doRead( RECORD record, PageCursor cursor, int recordSize, PagedFile storeFile,
long headerByte, boolean inUse ) throws IOException;

@Override
public final void write( RECORD record, PageCursor cursor, int recordSize, PagedFile storeFile )
throws IOException
public boolean isInUse( PageCursor cursor )
{
if ( record.inUse() )
{
doWrite( record, cursor, recordSize, storeFile );
}
else
{
byte inUseByte = cursor.getByte( cursor.getOffset() );
inUseByte &= ~inUseBitMaskForFirstByte;
cursor.putByte( inUseByte );
}
return isInUse( cursor.getByte( cursor.getOffset() ) );
}

/**
* Writes record contents to the {@code cursor} in the format specified by this implementation if
* the record is {@link AbstractBaseRecord#inUse()}, otherwise only the inUse bit is cleared and the
* rest of the bytes in the record left untouched.
*
* @param record containing data to write.
* @param cursor {@link PageCursor} to write the record data into.
* @param recordSize size of records of this format. This is passed in like this since not all formats
* know the record size in advance, but may be read from store header when opening the store.
* @throws IOException on error writing.
*/
protected abstract void doWrite( RECORD record, PageCursor cursor, int recordSize, PagedFile storeFile )
throws IOException;
protected boolean isInUse( byte firstByte )
{
return (firstByte & inUseBitMaskForFirstByte) != 0;
}

protected static boolean has( long headerByte, int bitMask )
{
Expand Down
Expand Up @@ -50,14 +50,11 @@ public static Function<StoreHeader,Integer> fixedRecordSize( int recordSize )

private final Function<StoreHeader,Integer> recordSize;
private final int recordHeaderSize;
protected final int inUseBitMaskForFirstByte;

protected BaseRecordFormat( Function<StoreHeader,Integer> recordSize, int recordHeaderSize,
int inUseBitMaskForFirstByte )
protected BaseRecordFormat( Function<StoreHeader,Integer> recordSize, int recordHeaderSize )
{
this.recordSize = recordSize;
this.recordHeaderSize = recordHeaderSize;
this.inUseBitMaskForFirstByte = inUseBitMaskForFirstByte;
}

@Override
Expand All @@ -72,17 +69,6 @@ public int getRecordHeaderSize()
return recordHeaderSize;
}

@Override
public boolean isInUse( PageCursor cursor )
{
return isInUse( cursor.getByte( cursor.getOffset() ) );
}

protected boolean isInUse( byte firstByte )
{
return (firstByte & inUseBitMaskForFirstByte) != 0;
}

@Override
public long getNextRecordReference( RECORD record )
{
Expand Down
Expand Up @@ -21,6 +21,7 @@

import org.neo4j.io.pagecache.PageCursor;
import org.neo4j.io.pagecache.PagedFile;
import org.neo4j.kernel.impl.store.format.BaseOneByteHeaderRecordFormat;
import org.neo4j.kernel.impl.store.format.BaseRecordFormat;
import org.neo4j.kernel.impl.store.record.DynamicRecord;
import org.neo4j.kernel.impl.store.record.Record;
Expand All @@ -30,7 +31,7 @@

import static org.neo4j.kernel.impl.store.record.DynamicRecord.NO_DATA;

public class DynamicRecordFormat extends BaseRecordFormat<DynamicRecord>
public class DynamicRecordFormat extends BaseOneByteHeaderRecordFormat<DynamicRecord>
{
// (in_use+next high)(1 byte)+nr_of_bytes(3 bytes)+next_block(int)
public static final int RECORD_HEADER_SIZE = 1 + 3 + 4; // = 8
Expand Down
Expand Up @@ -19,12 +19,15 @@
*/
package org.neo4j.kernel.impl.store.format.lowlimit;

import java.io.IOException;

import org.neo4j.io.pagecache.PageCursor;
import org.neo4j.io.pagecache.PagedFile;
import org.neo4j.kernel.impl.store.format.BaseOneByteHeaderRecordFormat;
import org.neo4j.kernel.impl.store.format.BaseRecordFormat;
import org.neo4j.kernel.impl.store.record.NodeRecord;
import org.neo4j.kernel.impl.store.record.Record;
import org.neo4j.kernel.impl.store.record.RecordLoad;

public class NodeRecordFormat extends BaseOneByteHeaderRecordFormat<NodeRecord>
{
Expand All @@ -43,51 +46,64 @@ public NodeRecord newRecord()
}

@Override
public void doRead( NodeRecord record, PageCursor cursor, int recordSize, PagedFile storeFile, long headerByte, boolean inUse )
public void read( NodeRecord record, PageCursor cursor, RecordLoad mode, int recordSize, PagedFile storeFile )
throws IOException
{
long nextRel = cursor.getUnsignedInt();
long nextProp = cursor.getUnsignedInt();
byte headerByte = cursor.getByte();
boolean inUse = isInUse( headerByte );
if ( mode.shouldLoad( inUse ) )
{
long nextRel = cursor.getUnsignedInt();
long nextProp = cursor.getUnsignedInt();

long relModifier = (headerByte & 0xEL) << 31;
long propModifier = (headerByte & 0xF0L) << 28;
long relModifier = (headerByte & 0xEL) << 31;
long propModifier = (headerByte & 0xF0L) << 28;

long lsbLabels = cursor.getUnsignedInt();
long hsbLabels = cursor.getByte() & 0xFF; // so that a negative byte won't fill the "extended" bits with ones.
long labels = lsbLabels | (hsbLabels << 32);
byte extra = cursor.getByte();
boolean dense = (extra & 0x1) > 0;
long lsbLabels = cursor.getUnsignedInt();
long hsbLabels = cursor.getByte() & 0xFF; // so that a negative byte won't fill the "extended" bits with ones.
long labels = lsbLabels | (hsbLabels << 32);
byte extra = cursor.getByte();
boolean dense = (extra & 0x1) > 0;

record.initialize( inUse,
BaseRecordFormat.longFromIntAndMod( nextProp, propModifier ), dense,
BaseRecordFormat.longFromIntAndMod( nextRel, relModifier ), labels );
record.initialize( inUse,
BaseRecordFormat.longFromIntAndMod( nextProp, propModifier ), dense,
BaseRecordFormat.longFromIntAndMod( nextRel, relModifier ), labels );
}
}

@Override
public void doWrite( NodeRecord record, PageCursor cursor, int recordSize, PagedFile storeFile )
public void write( NodeRecord record, PageCursor cursor, int recordSize, PagedFile storeFile ) throws IOException
{
long nextRel = record.getNextRel();
long nextProp = record.getNextProp();
if ( record.inUse() )
{
long nextRel = record.getNextRel();
long nextProp = record.getNextProp();

short relModifier = nextRel == Record.NO_NEXT_RELATIONSHIP.intValue() ? 0 : (short)((nextRel & 0x700000000L) >> 31);
short propModifier = nextProp == Record.NO_NEXT_PROPERTY.intValue() ? 0 : (short)((nextProp & 0xF00000000L) >> 28);
short relModifier = nextRel == Record.NO_NEXT_RELATIONSHIP.intValue() ? 0 : (short)((nextRel & 0x700000000L) >> 31);
short propModifier = nextProp == Record.NO_NEXT_PROPERTY.intValue() ? 0 : (short)((nextProp & 0xF00000000L) >> 28);

// [ , x] in use bit
// [ ,xxx ] higher bits for rel id
// [xxxx, ] higher bits for prop id
short inUseUnsignedByte = ( record.inUse() ? Record.IN_USE : Record.NOT_IN_USE ).byteValue();
inUseUnsignedByte = (short) ( inUseUnsignedByte | relModifier | propModifier );
// [ , x] in use bit
// [ ,xxx ] higher bits for rel id
// [xxxx, ] higher bits for prop id
short inUseUnsignedByte = ( record.inUse() ? Record.IN_USE : Record.NOT_IN_USE ).byteValue();
inUseUnsignedByte = (short) ( inUseUnsignedByte | relModifier | propModifier );

cursor.putByte( (byte) inUseUnsignedByte );
cursor.putInt( (int) nextRel );
cursor.putInt( (int) nextProp );
cursor.putByte( (byte) inUseUnsignedByte );
cursor.putInt( (int) nextRel );
cursor.putInt( (int) nextProp );

// lsb of labels
long labelField = record.getLabelField();
cursor.putInt( (int) labelField );
// msb of labels
cursor.putByte( (byte) ((labelField & 0xFF00000000L) >> 32) );
// lsb of labels
long labelField = record.getLabelField();
cursor.putInt( (int) labelField );
// msb of labels
cursor.putByte( (byte) ((labelField & 0xFF00000000L) >> 32) );

byte extra = record.isDense() ? (byte)1 : (byte)0;
cursor.putByte( extra );
byte extra = record.isDense() ? (byte)1 : (byte)0;
cursor.putByte( extra );
}
else
{
markFirstByteAsUnused( cursor );
}
}
}
Expand Up @@ -41,7 +41,7 @@ public class PropertyRecordFormat extends BaseRecordFormat<PropertyRecord>

public PropertyRecordFormat()
{
super( fixedRecordSize( RECORD_SIZE ), 0, 0 );
super( fixedRecordSize( RECORD_SIZE ), 0 );
}

@Override
Expand Down

0 comments on commit 8d59596

Please sign in to comment.