package org.maachang.jni.io;

/**
 * メモリ直接操作処理.
 * 
 * @version 2010/06/04
 * @author  masahito suzuki
 * @since   SeabassNativeIO-1.0.0
 */
public final class DirectMemoryIO {
    private DirectMemoryIO() {}
    
    /** putBinaryコピーしきい値. **/
    protected static final int BINARY_COPY_TO_PUT_BYTE = 8 ;
    
    
    /**
     * malloc.
     * @param size 生成対象のメモリサイズを設定します.
     * @return long 生成された先頭アドレスが返されます.
     */
    public static final long malloc( final int size ) {
        return NativeIO.malloc( size ) ;
    }
    
    /**
     * realloc.
     * @param address 対象のメモリ先頭アドレスを設定します.
     * @param size 再生成対象のメモリサイズを設定します.
     * @return long 再生成された先頭アドレスが返されます.
     */
    public static final long realloc( final long address,final int size ) {
        return NativeIO.realloc( address,size ) ;
    }
    
    /**
     * free.
     * @param address メモリ解放対象のアドレスを設定します.
     */
    public static final void free( final long address ) {
        NativeIO.free( address ) ;
    }
    
    /**
     * memset.
     * @param address 対象のメモリ先頭アドレスを設定します.
     * @param code 指定値を設定します.
     * @param size 指定値設定のサイズを設定します.
     */
    public static final void memset( final long address,final byte code,final int size ) {
        NativeIO.memset( address,code,size ) ;
    }
    
    /**
     * memcpy.
     * @param destAddr コピー先のメモリアドレスを設定します.
     * @param srcAddr コピー元のメモリアドレスを設定します.
     * @param size コピーサイズを設定します.
     */
    public static final void memcpy( final long destAddr,final long srcAddr,final int size ) {
        NativeIO.memcpy( destAddr,srcAddr,size ) ;
    }
    
    /** sun.misc.Unsafeオブジェクト利用 **/
    private static final boolean UnsafeMode = Unsafe.UNSAFE_MODE ;
    private static final sun.misc.Unsafe unsafe = Unsafe.unsafe ;
    
    /** swap条件 **/
    protected static final boolean BIG_ENDIAN = Unsafe.BIG_ENDIAN ;
    
    /**
     * 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 <= 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( !BIG_ENDIAN ) {
            value = Unsafe.swap( 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 ) {
            if( BIG_ENDIAN ) {
                return unsafe.getChar( address+index ) ;
            }
            return Unsafe.swap( unsafe.getChar( address+index ) ) ;
        }
        if( BIG_ENDIAN ) {
            return NativeIO.getChar( address+index ) ;
        }
        return Unsafe.swap( NativeIO.getChar( address+index ) ) ;
    }
    
    /**
     * short設定.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @param value 設定対象の情報を設定します.
     */
    public static void putShort( final long address,final int index,short value ) {
        if( !BIG_ENDIAN ) {
            value = Unsafe.swap( 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 ) {
            if( BIG_ENDIAN ) {
                return unsafe.getShort( address+index ) ;
            }
            return Unsafe.swap( unsafe.getShort( address+index ) ) ;
        }
        if( BIG_ENDIAN ) {
            return NativeIO.getShort( address+index ) ;
        }
        return Unsafe.swap( NativeIO.getShort( address+index ) ) ;
    }
    
    /**
     * int設定.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @param value 設定対象の情報を設定します.
     */
    public static void putInt( final long address,final int index,int value ) {
        if( !BIG_ENDIAN ) {
            value = Unsafe.swap( 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 ) {
            if( BIG_ENDIAN ) {
                return unsafe.getInt( address+index ) ;
            }
            return Unsafe.swap( unsafe.getInt( address+index ) ) ;
        }
        if( BIG_ENDIAN ) {
            return NativeIO.getInt( address+index ) ;
        }
        return Unsafe.swap( NativeIO.getInt( address+index ) ) ;
    }
    
    /**
     * long設定.
     * @param address 対象のアドレスを設定します.
     * @param index 対象のインデックス位置を設定します.
     * @param value 設定対象の情報を設定します.
     */
    public static void putLong( final long address,final int index,long value ) {
        if( !BIG_ENDIAN ) {
            value = Unsafe.swap( 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 ) {
            if( BIG_ENDIAN ) {
                return unsafe.getLong( address+index ) ;
            }
            return Unsafe.swap( unsafe.getLong( address+index ) ) ;
        }
        if( BIG_ENDIAN ) {
            return NativeIO.getLong( address+index ) ;
        }
        return Unsafe.swap( 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 ) ) ;
    }
    
    /**
     * バイナリ情報比較.
     * @param srcAddr 比較元バイナリ先頭アドレスを設定します.
     * @param srcOff 比較元バイナリオフセット値を設定します.
     * @param destAddr 比較先バイナリ先頭アドレスを設定します.
     * @param destOff 比較先バイナリオフセット値を設定します.
     * @param length 比較するのバイナリ長を設定します.
     * @return boolean [true]の場合、バイナリ内容は一致します.
     */
    public static final boolean equals( final long srcAddr,final int srcOff,final long destAddr,final int destOff,final int length ) {
        return NativeIO.equalsBinary( srcAddr+(long)srcOff,destAddr+(long)destOff,length ) == 1 ;
    }
    
    /**
     * fnvHash計算.
     * @param address 対象のアドレスを設定します.
     * @param offset 対象のオフセット値を設定します.
     * @param length 対象の長さを設定します.
     * @return long 計算された結果が返されます.
     */
    public static final long fnv64( final long address,final int offset,final int length ) {
        return NativeIO.fnv64( address+offset,length-offset ) ;
    }
    
    /**
     * IndexOf.
     * @param address 対象のアドレスを設定します.
     * @param offset 対象のオフセット値を設定します.
     * @param length 対象の長さを設定します.
     * @param binary 操作対象のデータを設定します.
     * @return int 操作位置が返されます.
     */
    public static final int indexOf( final long address,final int index,final int length,byte[] binary ) {
        int len ;
        if( binary == null || ( len = binary.length ) <= 0 ) {
            return -1 ;
        }
        else if( index < 0 || index + len > length ) {
            return -1 ;
        }
        int ret = NativeIO.indexOf( address+index,length-index,binary,len ) ;
        if( ret == -1 ) {
            return -1 ;
        }
        return ret + index ;
    }
    
    /**
     * lastIndexOf.
     * @param address 対象のアドレスを設定します.
     * @param offset 対象のオフセット値を設定します.
     * @param length 対象の長さを設定します.
     * @param binary 操作対象のデータを設定します.
     * @return int 操作位置が返されます.
     */
    public static final int lastIndexOf( final long address,final int index,final int length,byte[] binary ) {
        int len ;
        if( binary == null || ( len = binary.length ) <= 0 ) {
            return -1 ;
        }
        else if( index < 0 || index + len > length ) {
            return -1 ;
        }
        return NativeIO.lastIndexOf( address,index,length,binary,len ) ;
    }
}

