/*
* Copyright (c) 2016, Metron, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Metron, Inc. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.metsci.glimpse.dnc.util;
import static com.google.common.base.Objects.equal;
import static com.metsci.glimpse.util.GeneralUtils.newArrayList;
import static java.lang.Integer.parseInt;
import static java.nio.channels.FileChannel.MapMode.READ_ONLY;
import static java.nio.channels.FileChannel.MapMode.READ_WRITE;
import static java.util.Arrays.fill;
import static java.util.Collections.sort;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.nio.Buffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;
import java.util.function.Function;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
public class DncMiscUtils
{
public static final long MiB = 1024 * 1024;
public static final long GiB = 1024 * MiB;
public static <T> T requireResult( Future<? extends T> future )
{
try
{
while ( true )
{
try
{
return future.get( );
}
catch ( InterruptedException e )
{ }
}
}
catch ( ExecutionException e )
{
throw new RuntimeException( e );
}
}
public static Thread startThread( String name, boolean daemon, Runnable runnable )
{
Thread thread = new Thread( runnable, name );
thread.setDaemon( daemon );
thread.start( );
return thread;
}
public static <V> V takeNewValue( BlockingQueue<V> queue, V oldValue ) throws InterruptedException
{
while ( true )
{
V value = queue.take( );
if ( !equal( value, oldValue ) )
{
return value;
}
}
}
/**
* Creates an executor with a single daemon thread and an
* unbounded job queue. Jobs submitted after the executor
* has been shutdown will be silently dropped.
*/
public static ExecutorService newWorkerDaemon( String threadNamePrefix )
{
return new ThreadPoolExecutor( 1,
1,
0, MILLISECONDS,
new LinkedBlockingQueue<Runnable>( ),
newThreadFactory( threadNamePrefix, true ),
new DiscardPolicy( ) );
}
public static ThreadFactory newThreadFactory( final String namePrefix, final boolean isDaemon )
{
return new ThreadFactory( )
{
final Object threadNumMutex = new Object( );
int nextThreadNum = 0;
int nextThreadNum( )
{
synchronized ( threadNumMutex )
{
return ( nextThreadNum++ );
}
}
public Thread newThread( Runnable runnable )
{
Thread thread = new Thread( runnable );
thread.setDaemon( isDaemon );
if ( namePrefix != null )
{
thread.setName( namePrefix + nextThreadNum( ) );
}
return thread;
}
};
}
public static abstract class ThrowingRunnable implements Runnable
{
public abstract void runThrows( ) throws Exception;
@Override
public final void run( )
{
try
{
runThrows( );
}
catch ( Exception e )
{
throw new RuntimeException( e );
}
}
}
/**
* Throws {@link IllegalArgumentException} if x is negative
*/
public static int nextPowerOfTwo( int x )
{
if ( x < 0 ) throw new IllegalArgumentException( "Argument is negative: " + x );
if ( x == 0 ) return 1;
// Copy the highest set bit into all lower bits, then add
// one -- but first, subtract one, so that it works if x
// is already a power of two
x--;
x |= ( x >> 1 );
x |= ( x >> 2 );
x |= ( x >> 4 );
x |= ( x >> 8 );
x |= ( x >> 16 );
x++;
return x;
}
public static int sum( int... xs )
{
int sum = 0;
for ( int x : xs ) sum += x;
return sum;
}
public static long timeSince_MILLIS( long start_PMILLIS )
{
return ( System.currentTimeMillis( ) - start_PMILLIS );
}
public static <V> List<V> sorted( Collection<V> values, Comparator<? super V> comparator )
{
List<V> list = newArrayList( values );
sort( list, comparator );
return list;
}
public static <T> ArrayList<T> toArrayList( Iterable<? extends T> iterable )
{
ArrayList<T> list = new ArrayList<>( );
for ( T t : iterable ) list.add( t );
return list;
}
public static <T> T last( List<T> list )
{
return list.get( list.size( ) - 1 );
}
public static boolean isFilenameCaseSensitive( File file )
{
return !equal( filenameToLowercase( file ), filenameToUppercase( file ) );
}
/**
* Lowercases the last segment of the specified path.
*/
public static File filenameToLowercase( File file )
{
return new File( file.getParent( ), file.getName( ).toLowerCase( ) );
}
/**
* Uppercases the last segment of the specified path.
*/
public static File filenameToUppercase( File file )
{
return new File( file.getParent( ), file.getName( ).toUpperCase( ) );
}
public static MappedByteBuffer memmapReadOnly( File file ) throws IOException
{
RandomAccessFile raf = null;
try
{
raf = new RandomAccessFile( file, "r" );
MappedByteBuffer mapped = raf.getChannel( ).map( READ_ONLY, 0, raf.length( ) );
mapped.order( ByteOrder.nativeOrder( ) );
return mapped;
}
finally
{
if ( raf != null ) raf.close( );
}
}
public static MappedByteBuffer memmapReadWrite( File file ) throws IOException
{
RandomAccessFile raf = null;
try
{
raf = new RandomAccessFile( file, "rw" );
MappedByteBuffer mapped = raf.getChannel( ).map( READ_WRITE, 0, raf.length( ) );
mapped.order( ByteOrder.nativeOrder( ) );
return mapped;
}
finally
{
if ( raf != null ) raf.close( );
}
}
public static MappedByteBuffer createAndMemmapReadWrite( File file, int newFileSize ) throws IOException
{
RandomAccessFile raf = null;
try
{
raf = new RandomAccessFile( file, "rw" );
raf.setLength( newFileSize );
MappedByteBuffer mapped = raf.getChannel( ).map( READ_WRITE, 0, newFileSize );
mapped.order( ByteOrder.nativeOrder( ) );
return mapped;
}
finally
{
if ( raf != null ) raf.close( );
}
}
public static void poslim( Buffer buf, int first, int count, int size )
{
buf.limit( size * ( first + count ) );
buf.position( size * ( first ) );
}
public static long packBytesIntoLong( byte[] bytes )
{
if ( bytes.length > 7 ) throw new RuntimeException( "More than 7 bytes cannot be packed into a long: num-bytes = " + bytes.length );
long packed = ( ( ( long ) bytes.length ) & 0xFF ) << 56;
for ( int i = 0; i < bytes.length; i++ )
{
int shift = 8*( 6 - i );
packed |= ( ( ( long ) bytes[ i ] ) & 0xFF ) << shift;
}
return packed;
}
public static byte[] unpackLongIntoBytes( long packed )
{
int length = ( int ) ( ( packed >> 56 ) & 0xFF );
byte[] bytes = new byte[ length ];
for ( int i = 0; i < bytes.length; i++ )
{
int shift = 8*( 6 - i );
bytes[ i ] = ( byte ) ( ( packed >> shift ) & 0xFF );
}
return bytes;
}
public static String repchar( char c, int n )
{
char[] chars = new char[ n ];
fill( chars, c );
return String.valueOf( chars );
}
public static void writeIdsMapFile( Object2IntMap<String> idsMap, File file, Charset charset ) throws IOException
{
PrintStream stream = null;
try
{
stream = new PrintStream( file, charset.name( ) );
for ( Object2IntMap.Entry<String> en : idsMap.object2IntEntrySet( ) )
{
stream.println( en.getIntValue( ) + " " + en.getKey( ) );
}
stream.flush( );
}
finally
{
if ( stream != null ) stream.close( );
}
}
public static Int2ObjectMap<String> readIdsMapFile( File file, Charset charset ) throws IOException
{
BufferedReader reader = null;
try
{
reader = new BufferedReader( new InputStreamReader( new FileInputStream( file ), charset ) );
Int2ObjectMap<String> idsMap = new Int2ObjectLinkedOpenHashMap<>( );
for ( int i = 0; true; i++ )
{
String line = reader.readLine( );
if ( line == null ) break;
String[] tokens = line.split( " " );
if ( tokens.length < 2 ) throw new IOException( "Format error in " + file.getAbsolutePath( ) + " on line " + i );
int id = parseInt( tokens[ 0 ] );
String string = tokens[ 1 ];
idsMap.put( id, string );
}
return idsMap;
}
finally
{
if ( reader != null ) reader.close( );
}
}
public static <T> Object2IntMap<T> invertIdsMap( Int2ObjectMap<T> idsMap )
{
Object2IntMap<T> inverted = new Object2IntLinkedOpenHashMap<>( );
for ( Int2ObjectMap.Entry<T> en : idsMap.int2ObjectEntrySet( ) )
{
inverted.put( en.getValue( ), en.getIntKey( ) );
}
return inverted;
}
public static <T> Object2IntMap<T> invertList( List<T> list )
{
Object2IntMap<T> inverted = new Object2IntLinkedOpenHashMap<>( );
int nextNum = 0;
for ( T value : list )
{
int num = ( nextNum++ );
inverted.put( value, num );
}
return inverted;
}
public static <F,T> Function<F,T> constFunc( final T value )
{
return new Function<F,T>( )
{
public T apply( F input )
{
return value;
}
};
}
public static Map<String,Object> newAttrsMap( Object... keysAndValues )
{
Map<String,Object> map = new LinkedHashMap<>( );
for ( int i = 0; i < keysAndValues.length; i+=2 )
{
map.put( ( String ) keysAndValues[ i ], keysAndValues[ i + 1 ] );
}
return map;
}
public static Function<String,Object> newAttrsFunc( final Map<String,Object> map )
{
return new Function<String,Object>( )
{
public Object apply( String key )
{
return map.get( key );
}
};
}
public static File createNewDir( File parentDir, String childPath )
{
return createNewDir( new File( parentDir, childPath ) );
}
public static File createNewDir( String dirPath )
{
return createNewDir( new File( dirPath ) );
}
public static File createNewDir( File dir )
{
if ( !dir.mkdirs( ) )
{
if ( dir.exists( ) )
{
throw new RuntimeException( "Directory already exists: path = " + dir.getAbsolutePath( ) );
}
else
{
throw new RuntimeException( "Failed to create directory: path = " + dir.getAbsolutePath( ) );
}
}
return dir;
}
}