All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.io.pagecache.impl.CompositePageCursor Maven / Gradle / Ivy

There is a newer version: 5.26.1
Show newest version
/*
 * 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 .
 */
package org.neo4j.io.pagecache.impl;

import java.io.File;
import java.io.IOException;

import org.neo4j.io.pagecache.CursorException;
import org.neo4j.io.pagecache.PageCursor;

/**
 * A CompositePageCursor is a seamless view over parts of two other PageCursors.
 * @see #compose(PageCursor, int, PageCursor, int)
 */
public class CompositePageCursor extends PageCursor
{
    private final PageCursor first;
    private final int firstBaseOffset;
    private final int firstLength;
    private final PageCursor second;
    private final int secondBaseOffset;
    private final int secondLength;
    private int offset;
    private PageCursor byteAccessCursor;
    private boolean outOfBounds;

    // Constructed with static factory methods.
    private CompositePageCursor(
            PageCursor first, int firstBaseOffset, int firstLength,
            PageCursor second, int secondBaseOffset, int secondLength )
    {
        this.first = first;
        this.firstBaseOffset = firstBaseOffset;
        this.firstLength = firstLength;
        this.second = second;
        this.secondBaseOffset = secondBaseOffset;
        this.secondLength = secondLength;
        byteAccessCursor = new DelegatingPageCursor( this )
        {
            private int offset;
            @Override
            public int getInt()
            {
                int a = getByte( offset ) & 0xFF;
                int b = getByte( offset + 1 ) & 0xFF;
                int c = getByte( offset + 2 ) & 0xFF;
                int d = getByte( offset + 3 ) & 0xFF;
                int v = (a << 24) | (b << 16) | (c << 8) | d;
                return v;
            }

            @Override
            public int getInt( int offset )
            {
                this.offset = offset;
                return getInt();
            }

            @Override
            public short getShort()
            {
                int a = getByte( offset ) & 0xFF;
                int b = getByte( offset + 1 ) & 0xFF;
                int v = (a << 8) | b;
                return (short) v;
            }

            @Override
            public short getShort( int offset )
            {
                this.offset = offset;
                return getShort();
            }

            @Override
            public long getLong()
            {
                long a = getByte( offset ) & 0xFF;
                long b = getByte( offset + 1 ) & 0xFF;
                long c = getByte( offset + 2 ) & 0xFF;
                long d = getByte( offset + 3 ) & 0xFF;
                long e = getByte( offset + 4 ) & 0xFF;
                long f = getByte( offset + 5 ) & 0xFF;
                long g = getByte( offset + 6 ) & 0xFF;
                long h = getByte( offset + 7 ) & 0xFF;
                long v = (a << 56) | (b << 48) | (c << 40) | (d << 32) | (e << 24) | (f << 16) | (g << 8) | h;
                return v;
            }

            @Override
            public long getLong( int offset )
            {
                this.offset = offset;
                return getLong();
            }

            @Override
            public void getBytes( byte[] data )
            {
                for ( int i = 0; i < data.length; i++ )
                {
                    data[i] = getByte( offset + i );
                }
            }

            @Override
            public void putInt( int value )
            {
                putByte( offset    , (byte)  (value >> 24) );
                putByte( offset + 1, (byte) ((value >> 16) & 0xFF) );
                putByte( offset + 2, (byte) ((value >>  8) & 0xFF) );
                putByte( offset + 3, (byte) ((value      ) & 0xFF) );
            }

            @Override
            public void putInt( int offset, int value )
            {
                this.offset = offset;
                putInt( value );
            }

            @Override
            public void putShort( short value )
            {
                putByte( offset    , (byte)  (value >>  8) );
                putByte( offset + 1, (byte) ((value      ) & 0xFF) );
            }

            @Override
            public void putShort( int offset, short value )
            {
                this.offset = offset;
                putShort( value );
            }

            @Override
            public void putLong( long value )
            {
                putByte( offset    , (byte)  (value >> 56) );
                putByte( offset + 1, (byte) ((value >> 48) & 0xFF) );
                putByte( offset + 2, (byte) ((value >> 40) & 0xFF) );
                putByte( offset + 3, (byte) ((value >> 32) & 0xFF) );
                putByte( offset + 4, (byte) ((value >> 24) & 0xFF) );
                putByte( offset + 5, (byte) ((value >> 16) & 0xFF) );
                putByte( offset + 6, (byte) ((value >>  8) & 0xFF) );
                putByte( offset + 7, (byte) ((value      ) & 0xFF) );
            }

            @Override
            public void putLong( int offset, long value )
            {
                this.offset = offset;
                putLong( value );
            }

            @Override
            public void putBytes( byte[] data )
            {
                for ( int i = 0; i < data.length; i++ )
                {
                    putByte( offset + i, data[i] );
                }
            }

            @Override
            public void setOffset( int offset )
            {
                this.offset = offset;
            }
        };
    }

    private PageCursor cursor( int width )
    {
        return cursor( offset, width );
    }

    private PageCursor cursor( int offset, int width )
    {
        outOfBounds |= offset + width > firstLength + secondLength;
        if ( offset < firstLength )
        {
            return offset + width <= firstLength ? first : byteCursor( offset );
        }
        return second;

    }

    private PageCursor byteCursor( int offset )
    {
        byteAccessCursor.setOffset( offset );
        return byteAccessCursor;
    }

    private int relative( int offset )
    {
        return offset < firstLength? firstBaseOffset + offset : secondBaseOffset + (offset - firstLength);
    }

    @Override
    public byte getByte()
    {
        PageCursor cursor = cursor( Byte.BYTES );
        byte b = cursor.getByte();
        offset++;
        return b;
    }

    @Override
    public byte getByte( int offset )
    {
        return cursor( offset, Byte.BYTES ).getByte( relative( offset ) );
    }

    @Override
    public void putByte( byte value )
    {
        PageCursor cursor = cursor( Byte.BYTES );
        cursor.putByte( value );
        offset++;
    }

    @Override
    public void putByte( int offset, byte value )
    {
        cursor( offset, Byte.BYTES ).putByte( relative( offset ), value );
    }

    @Override
    public long getLong()
    {
        long l = cursor( Long.BYTES ).getLong();
        offset += Long.BYTES;
        return l;
    }

    @Override
    public long getLong( int offset )
    {
        return cursor( offset, Long.BYTES ).getLong( relative( offset ) );
    }

    @Override
    public void putLong( long value )
    {
        cursor( Long.BYTES ).putLong( value );
        offset += Long.BYTES;
    }

    @Override
    public void putLong( int offset, long value )
    {
        cursor( offset, Long.BYTES ).putLong( relative( offset ), value );
    }

    @Override
    public int getInt()
    {
        int i = cursor( Integer.BYTES ).getInt();
        offset += Integer.BYTES;
        return i;
    }

    @Override
    public int getInt( int offset )
    {
        return cursor( offset, Integer.BYTES ).getInt( relative( offset ) );
    }

    @Override
    public void putInt( int value )
    {
        PageCursor cursor = cursor( Integer.BYTES );
        cursor.putInt( value );
        offset += Integer.BYTES;
    }

    @Override
    public void putInt( int offset, int value )
    {
        cursor( offset, Integer.BYTES ).putInt( relative( offset ), value );
    }

    @Override
    public void getBytes( byte[] data )
    {
        cursor( data.length ).getBytes( data );
        offset += data.length;
    }

    @Override
    public void getBytes( byte[] data, int arrayOffset, int length )
    {
        throw new UnsupportedOperationException( "Composite page cursor does not yet support this operation" );
    }

    @Override
    public void putBytes( byte[] data )
    {
        cursor( data.length ).putBytes( data );
        offset += data.length;
    }

    @Override
    public void putBytes( byte[] data, int arrayOffset, int length )
    {
        throw new UnsupportedOperationException( "Composite page cursor does not yet support this operation" );
    }

    @Override
    public short getShort()
    {
        short s = cursor( Short.BYTES ).getShort();
        offset += Short.BYTES;
        return s;
    }

    @Override
    public short getShort( int offset )
    {
        return cursor( offset, Short.BYTES ).getShort( relative( offset ) );
    }

    @Override
    public void putShort( short value )
    {
        cursor( Short.BYTES ).putShort( value );
        offset += Short.BYTES;
    }

    @Override
    public void putShort( int offset, short value )
    {
        cursor( offset, Short.BYTES ).putShort( relative( offset ), value );
    }

    @Override
    public void setOffset( int offset )
    {
        if ( offset < firstLength )
        {
            first.setOffset( firstBaseOffset + offset );
            second.setOffset( secondBaseOffset );
        }
        else
        {
            first.setOffset( firstBaseOffset + firstLength );
            second.setOffset( secondBaseOffset + (offset - firstLength) );
        }
        this.offset = offset;
    }

    @Override
    public int getOffset()
    {
        return offset;
    }

    @Override
    public long getCurrentPageId()
    {
        return cursor( 0 ).getCurrentPageId();
    }

    @Override
    public int getCurrentPageSize()
    {
        throw new UnsupportedOperationException( "Getting current page size is not supported on compose PageCursor" );
    }

    @Override
    public File getCurrentFile()
    {
        return null;
    }

    @Override
    public void rewind()
    {
        first.setOffset( firstBaseOffset );
        second.setOffset( secondBaseOffset );
        offset = 0;
    }

    @Override
    public boolean next() throws IOException
    {
        throw unsupportedNext();
    }

    private UnsupportedOperationException unsupportedNext()
    {
        return new UnsupportedOperationException(
                "Composite cursor does not support next operation. Please operate directly on underlying cursors." );
    }

    @Override
    public boolean next( long pageId ) throws IOException
    {
        throw unsupportedNext();
    }

    @Override
    public void close()
    {
        first.close();
        second.close();
    }

    @Override
    public boolean shouldRetry() throws IOException
    {
        boolean needsRetry = first.shouldRetry() | second.shouldRetry();
        if ( needsRetry )
        {
            rewind();
            checkAndClearBoundsFlag();
        }
        return needsRetry;
    }

    @Override
    public int copyTo( int sourceOffset, PageCursor targetCursor, int targetOffset, int lengthInBytes )
    {
        throw new UnsupportedOperationException( "Composite cursor does not support copyTo functionality." );
    }

    @Override
    public boolean checkAndClearBoundsFlag()
    {
        boolean bounds = outOfBounds | first.checkAndClearBoundsFlag() | second.checkAndClearBoundsFlag();
        outOfBounds = false;
        return bounds;
    }

    @Override
    public void checkAndClearCursorException() throws CursorException
    {
        first.checkAndClearCursorException();
        second.checkAndClearCursorException();
    }

    @Override
    public void raiseOutOfBounds()
    {
        outOfBounds = true;
    }

    @Override
    public void setCursorException( String message )
    {
        cursor( 0 ).setCursorException( message );
    }

    @Override
    public void clearCursorException()
    {
        first.clearCursorException();
        second.clearCursorException();
    }

    @Override
    public PageCursor openLinkedCursor( long pageId )
    {
        throw new UnsupportedOperationException( "Linked cursors are not supported for composite cursors" );
    }

    /**
     * Build a CompositePageCursor that is a view of the first page cursor from its current offset through the given
     * length, concatenated with the second cursor from its current offset through the given length. The offsets are
     * changed as part of accessing the underlying cursors through the composite cursor. However, the size and position
     * of the view does NOT change if the offsets of the underlying cursors are changed after constructing the composite
     * cursor.
     * 

* Not all cursor operations are supported on composite cursors, notably {@link #next()} and {@link #next(long)} are * not supported. * Most things work as you would expect, though. For instance, {@link #shouldRetry()} will delegate the check to * both of the underlying cursors, as will {@link #checkAndClearBoundsFlag()}. *

* The composite cursor also has its own bounds flag built in, which will be raised if an access is made outside of * the composite view. * *

     *     offset      first length            offset     second length
     *        │              │                    │              │
     * ┌──────▼──────────────▼────────┐   ┌───────▼──────────────▼───────┐
     * │         first cursor         │   │        second cursor         │
     * └──────▼──────────────▼────────┘   └───────▼──────────────▼───────┘
     *        └──────────┐   └──────────┐┌────────┘     ┌────────┘
     *                   ▼──────────────▼▼──────────────▼
     *                   │       composite cursor       │
     *                   └──────────────────────────────┘
     *             offset = 0, page size = first length + second length
     * 
* @param first The cursor that will form the first part of this composite cursor, from its current offset. * @param firstLength The number of bytes from the first cursor that will participate in the composite view. * @param second The cursor that will form the second part of this composite cursor, from its current offset. * @param secondLength The number of bytes from the second cursor that will participate in the composite view. * @return A cursor that is a composed view of the given parts of the two given cursors. */ public static PageCursor compose( PageCursor first, int firstLength, PageCursor second, int secondLength ) { return new CompositePageCursor( first, first.getOffset(), firstLength, second, second.getOffset(), secondLength ); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy