/** * Copyright (c) 2002-2011 "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 Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.kernel.impl.util; import java.io.IOException; import java.lang.reflect.Array; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.util.HashMap; import java.util.Map; public abstract class IoPrimitiveUtils { public static String readLengthAndString( ReadableByteChannel channel, ByteBuffer buffer ) throws IOException { Integer length = readInt( channel, buffer ); String result = length != null ? readString( channel, buffer, length ) : null; return result; } public static String readString( ReadableByteChannel channel, ByteBuffer buffer, int length ) throws IOException { char[] chars = new char[length]; chars = readCharArray( channel, buffer, chars ); return chars == null ? null : new String( chars ); } private static char[] readCharArray( ReadableByteChannel channel, ByteBuffer buffer, char[] charArray ) throws IOException { buffer.clear(); int charsLeft = charArray.length; int maxSize = buffer.capacity() / 2; int offset = 0; // offset in chars while ( charsLeft > 0 ) { if ( charsLeft > maxSize ) { buffer.limit( maxSize * 2 ); charsLeft -= maxSize; } else { buffer.limit( charsLeft * 2 ); charsLeft = 0; } if ( channel.read( buffer ) != buffer.limit() ) { return null; } buffer.flip(); int length = buffer.limit() / 2; buffer.asCharBuffer().get( charArray, offset, length ); offset += length; buffer.clear(); } return charArray; } private static boolean readAndFlip( ReadableByteChannel channel, ByteBuffer buffer, int bytes ) throws IOException { buffer.clear(); buffer.limit( bytes ); int read = channel.read( buffer ); if ( read < bytes ) { return false; } buffer.flip(); return true; } public static Integer readInt( ReadableByteChannel channel, ByteBuffer buffer ) throws IOException { return readAndFlip( channel, buffer, 4 ) ? buffer.getInt() : null; } public static Long readLong( ReadableByteChannel channel, ByteBuffer buffer ) throws IOException { return readAndFlip( channel, buffer, 8 ) ? buffer.getLong() : null; } public static Float readFloat( ReadableByteChannel channel, ByteBuffer buffer ) throws IOException { return readAndFlip( channel, buffer, 4 ) ? buffer.getFloat() : null; } public static Double readDouble( ReadableByteChannel channel, ByteBuffer buffer ) throws IOException { return readAndFlip( channel, buffer, 8 ) ? buffer.getDouble() : null; } public static byte[] readBytes( ReadableByteChannel channel, byte[] array ) throws IOException { return readBytes( channel, array, array.length ); } public static byte[] readBytes( ReadableByteChannel channel, byte[] array, int bytes ) throws IOException { return readAndFlip( channel, ByteBuffer.wrap( array ), bytes ) ? array : null; } public static Map<String, String> readMap( ReadableByteChannel channel, ByteBuffer buffer ) throws IOException { int size = readInt( channel, buffer ); Map<String, String> map = new HashMap<String, String>(); for ( int i = 0; i < size; i++ ) { String key = readLengthAndString( channel, buffer ); String value = readLengthAndString( channel, buffer ); if ( key == null || value == null ) { return null; } map.put( key, value ); } return map; } public static void writeLengthAndString( FileChannel channel, ByteBuffer buffer, String value ) throws IOException { char[] chars = value.toCharArray(); int length = chars.length; writeInt( channel, buffer, length ); writeChars( channel, buffer, chars ); } private static void writeChars( FileChannel channel, ByteBuffer buffer, char[] chars ) throws IOException { int position = 0; do { buffer.clear(); int leftToWrite = chars.length - position; if ( leftToWrite * 2 < buffer.capacity() ) { buffer.asCharBuffer().put( chars, position, leftToWrite ); buffer.limit( leftToWrite * 2); channel.write( buffer ); position += leftToWrite; } else { int length = buffer.capacity() / 2; buffer.asCharBuffer().put( chars, position, length ); buffer.limit( length * 2 ); channel.write( buffer ); position += length; } } while ( position < chars.length ); } public static void writeInt( FileChannel channel, ByteBuffer buffer, int value ) throws IOException { buffer.clear(); buffer.putInt( value ); buffer.flip(); channel.write( buffer ); } public static void writeMap( FileChannel channel, ByteBuffer buffer, Map<String, String> map ) throws IOException { writeInt( channel, buffer, map.size() ); for ( Map.Entry<String, String> entry : map.entrySet() ) { writeLengthAndString( channel, buffer, entry.getKey() ); writeLengthAndString( channel, buffer, entry.getValue() ); } } public static Object[] asArray( Object propertyValue ) { if ( propertyValue.getClass().isArray() ) { int length = Array.getLength( propertyValue ); Object[] result = new Object[ length ]; for ( int i = 0; i < length; i++ ) { result[ i ] = Array.get( propertyValue, i ); } return result; } else { return new Object[] { propertyValue }; } } }