/** * Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>. */ package org.neo4j.kernel.impl.nioneo.store; import java.lang.reflect.Array; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import org.neo4j.kernel.IdGeneratorFactory; import org.neo4j.kernel.IdType; import org.neo4j.kernel.impl.util.Bits; /** * Dynamic store that stores strings. */ public class DynamicArrayStore extends AbstractDynamicStore { // store version, each store ends with this string (byte encoded) static final String VERSION = "ArrayPropertyStore v0.A.0"; public static final String TYPE_DESCRIPTOR = "ArrayPropertyStore"; public DynamicArrayStore( String fileName, Map<?,?> config, IdType idType ) { super( fileName, config, idType ); } @Override public String getTypeDescriptor() { return TYPE_DESCRIPTOR; } public static void createStore( String fileName, int blockSize, IdGeneratorFactory idGeneratorFactory ) { createEmptyStore( fileName, blockSize, VERSION, idGeneratorFactory, IdType.ARRAY_BLOCK ); } private Collection<DynamicRecord> allocateFromNumbers( long startBlock, Object array ) { ShortArray type = ShortArray.typeOf( array ); if (type == null) { throw new IllegalArgumentException( array + " not a valid array type." ); } int arrayLength = Array.getLength( array ); int requiredBits = type.calculateRequiredBitsForArray( array ); int totalBits = requiredBits*arrayLength; int bytes = (totalBits-1)/8+1; int bitsUsedInLastByte = totalBits%8; bitsUsedInLastByte = bitsUsedInLastByte == 0 ? 8 : bitsUsedInLastByte; bytes += 3; // type + rest + requiredBits header. TODO no need to use full bytes Bits bits = Bits.bits( bytes ); bits.put( (byte)type.intValue() ); bits.put( (byte)bitsUsedInLastByte ); bits.put( (byte)requiredBits ); int length = arrayLength; for ( int i = 0; i < length; i++ ) { type.put( Array.get( array, i ), bits, requiredBits ); } return allocateRecords( startBlock, bits.asBytes() ); } private Collection<DynamicRecord> allocateFromString( long startBlock, String[] array ) { List<byte[]> stringsAsBytes = new ArrayList<byte[]>(); int totalBytesRequired = 1+4; // 1b type + 3b array length for ( String string : array ) { byte[] bytes = PropertyStore.encodeString( string ); stringsAsBytes.add( bytes ); totalBytesRequired += 4/*byte[].length*/ + bytes.length; } ByteBuffer buf = ByteBuffer.allocate( totalBytesRequired ); buf.put( PropertyType.STRING.byteValue() ); buf.putInt( array.length ); for ( byte[] stringAsBytes : stringsAsBytes ) { buf.putInt( stringAsBytes.length ); buf.put( stringAsBytes ); } return allocateRecords( startBlock, buf.array() ); } public Collection<DynamicRecord> allocateRecords( long startBlock, Object array ) { if ( !array.getClass().isArray() ) { throw new IllegalArgumentException( array + " not an array" ); } Class<?> type = array.getClass().getComponentType(); if ( type.equals( String.class ) ) { return allocateFromString( startBlock, (String[]) array ); } else { return allocateFromNumbers( startBlock, array ); } } public Object getRightArray( byte[] bArray ) { byte typeId = bArray[0]; if ( typeId == PropertyType.STRING.intValue() ) { ByteBuffer buf = ByteBuffer.wrap( bArray ); buf.get(); // Get rid of the type byte that we've already read int arrayLength = buf.getInt(); String[] result = new String[arrayLength]; for ( int i = 0; i < arrayLength; i++ ) { int byteLength = buf.getInt(); byte[] stringByteArray = new byte[byteLength]; buf.get( stringByteArray ); result[i] = (String) PropertyStore.getStringFor( stringByteArray ); } return result; } else { ShortArray type = ShortArray.typeOf( typeId ); Bits bits = Bits.bitsFromBytes( bArray ); bits.getByte(); // type, we already got it int bitsUsedInLastByte = bits.getByte(); int requiredBits = bits.getByte(); if ( requiredBits == 0 ) return type.createArray( 0 ); int length = ((bArray.length-3)*8-(8-bitsUsedInLastByte))/requiredBits; Object result = type.createArray( length ); for ( int i = 0; i < length; i++ ) { type.get( result, i, bits, requiredBits ); } return result; } } }