package org.maachang.jni.io;

/**
 * 高速メモリ直接操作処理.
 * <p>このメモリ直接操作処理は、swap等リトル／ビッグエンディアンを考慮した処理は
 *    行われておりません</p>
 * <p>それらの処理でメモリ操作をおこないたい場合は、DirectMemoryIOオブジェクトを
 *    利用してください</p>
 * 
 * @version 2010/06/04
 * @author  masahito suzuki
 * @since   SeabassNativeIO-1.0.0
 */
public final class FastDirectMemoryIO {
    private FastDirectMemoryIO() {}
    
    /** sun.misc.Unsafeオブジェクト利用 **/
    private static final boolean UnsafeMode = Unsafe.UNSAFE_MODE ;
    private static final sun.misc.Unsafe unsafe = Unsafe.unsafe ;
    
    /**
     * 1バイトの情報を取得.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @return byte バイト情報が返されます.
     */
    public static final byte get( final long address,final int index ) {
        if( UnsafeMode ) {
            return unsafe.getByte( address+index ) ;
        }
        else {
            return NativeIO.getByte( address+index ) ;
        }
    }
    
    /**
     * 1バイトの情報を設定.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @param value 対象の１バイト情報を設定します.
     */
    public static final void put( final long address,final int index,final byte value ) {
        if( UnsafeMode ) {
            unsafe.putByte( address+index,value ) ;
        }
        else {
            NativeIO.putByte( address+index,value ) ;
        }
    }
    
    /**
     * binary情報を設定.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @param value 設定対象の情報を設定します.
     * @param offset 対象のオフセット値を設定します.
     * @param length 対象のデータ長を設定します.
     * @return int 設定された長さが返されます.
     */
    public static int putBinary( final long address,final int index,final byte[] value,final int offset,final int length ) {
        // binaryコピーしきい値範囲内の場合は、byte単位で処理.
        if( length <= DirectMemoryIO.BINARY_COPY_TO_PUT_BYTE ) {
            if( UnsafeMode ) {
                long j = index+address ;
                for( int i = offset ; i < length ; i ++,j ++ ) {
                    unsafe.putByte( j,value[ i ] ) ;
                }
            }
            else {
                long j = index+address ;
                for( int i = offset ; i < length ; i ++,j ++ ) {
                    NativeIO.putByte( j,value[ i ] ) ;
                }
            }
            return length ;
        }
        NativeIO.putBinary( address+index,value,offset,length ) ;
        return length ;
    }
    
    /**
     * binary情報を取得.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @param value 取得対象の情報を設定します.
     * @param offset 対象のオフセット値を設定します.
     * @param length 対象のデータ長を設定します.
     * @return int 設定された長さが返されます.
     */
    public static int getBinary( final long address,final int index,final byte[] value,final int offset,final int length ) {
        // binaryコピーしきい値範囲内の場合は、byte単位で処理.
        if( length <= DirectMemoryIO.BINARY_COPY_TO_PUT_BYTE ) {
            if( UnsafeMode ) {
                long j = index+address ;
                for( int i = offset ; i < length ; i ++,j ++ ) {
                    value[ i ] = unsafe.getByte( j ) ;
                }
            }
            else {
                long j = index+address ;
                for( int i = offset ; i < length ; i ++,j ++ ) {
                    value[ i ] = NativeIO.getByte( j ) ;
                }
            }
            return length ;
        }
        NativeIO.getBinary( address+index,value,offset,length ) ;
        return length ;
    }
    
    /**
     * boolean設定.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @param value 設定対象の情報を設定します.
     */
    public static void putBoolean( final long address,final int index,final boolean value ) {
        if( UnsafeMode ) {
            unsafe.putByte( address+index,((value)?(byte)1:(byte)0) ) ;
        }
        else {
            NativeIO.putByte( address+index,((value)?(byte)1:(byte)0) ) ;
        }
    }
    
    /**
     * boolean取得.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @return boolean 情報が返されます.
     */
    public static boolean getBoolean( final long address,final int index ) {
        if( UnsafeMode ) {
            return ( unsafe.getByte( address+index ) == 0 ) ? false : true ;
        }
        return ( NativeIO.getByte( address+index ) == 0 ) ? false : true ;
    }
    
    /**
     * char設定.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @param value 設定対象の情報を設定します.
     */
    public static void putChar( final long address,final int index,char value ) {
        if( UnsafeMode ) {
            unsafe.putChar( address+index,value ) ;
        }
        else {
            NativeIO.putChar( address+index,value ) ;
        }
    }
    
    /**
     * char取得.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @return char 情報が返されます.
     */
    public static char getChar( final long address,final int index ) {
        if( UnsafeMode ) {
            return unsafe.getChar( address+index ) ;
        }
        return NativeIO.getChar( address+index ) ;
    }
    
    
    /**
     * short設定.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @param value 設定対象の情報を設定します.
     */
    public static void putShort( final long address,final int index,final short value ) {
        if( UnsafeMode ) {
            unsafe.putShort( address+index,value ) ;
        }
        else {
            NativeIO.putShort( address+index,value ) ;
        }
    }
    
    /**
     * short取得.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @return short 情報が返されます.
     */
    public static short getShort( final long address,final int index ) {
        if( UnsafeMode ) {
            return unsafe.getShort( address+index ) ;
        }
        return NativeIO.getShort( address+index ) ;
    }
    
    /**
     * int設定.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @param value 設定対象の情報を設定します.
     */
    public static void putInt( final long address,final int index,final int value ) {
        if( UnsafeMode ) {
            unsafe.putInt( address+index,value ) ;
        }
        else {
            NativeIO.putInt( address+index,value ) ;
        }
    }
    
    /**
     * int取得.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @return int 情報が返されます.
     */
    public static int getInt( final long address,final int index ) {
        if( UnsafeMode ) {
            return unsafe.getInt( address+index ) ;
        }
        return NativeIO.getInt( address+index ) ;
    }
    
    /**
     * long設定.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @param value 設定対象の情報を設定します.
     */
    public static void putLong( final long address,final int index,final long value ) {
        if( UnsafeMode ) {
            unsafe.putLong( address+index,value ) ;
        }
        else {
            NativeIO.putLong( address+index,value ) ;
        }
    }
    
    /**
     * long取得.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @return long 情報が返されます.
     */
    public static long getLong( final long address,final int index ) {
        if( UnsafeMode ) {
            return unsafe.getLong( address+index ) ;
        }
        return NativeIO.getLong( address+index ) ;
    }
    
    /**
     * float設定.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @param value 設定対象の情報を設定します.
     */
    public static void putFloat( final long address,final int index,final float value ) {
        putInt( address,index,Float.floatToIntBits( value ) ) ;
    }
    
    /**
     * float取得.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @return float 情報が返されます.
     */
    public static float getFloat( final long address,final int index ) {
        return Float.intBitsToFloat( getInt( address,index ) ) ;
    }
    
    /**
     * double設定.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @param value 設定対象の情報を設定します.
     */
    public static void putDouble( final long address,final int index,final double value ) {
        putLong( address,index,Double.doubleToLongBits( value ) ) ;
    }
    
    /**
     * double取得.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @return double 情報が返されます.
     */
    public static double getDouble( final long address,final int index ) {
        return Double.longBitsToDouble( getLong( address,index ) ) ;
    }
}

