/* * Copyright (c) 2013-2014, Parallel Universe Software Co. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 3.0 * as published by the Free Software Foundation. */ package co.paralleluniverse.data.record; import co.paralleluniverse.common.util.UtilUnsafe; import java.util.Arrays; import sun.misc.Unsafe; /** * * @author pron */ class SimpleRecord<R> extends AbstractRecord<R> implements Record<R>, Cloneable { final int[] offsets; private final Object[] oa; private final byte[] ba; public SimpleRecord(RecordType<R> recordType) { super(recordType); this.offsets = recordType.getOffsets(); this.oa = recordType.getObjectIndex() > 0 ? new Object[recordType.getObjectOffset()] : null; this.ba = recordType.getPrimitiveIndex() > 0 ? new byte[recordType.getPrimitiveOffset()] : null; } /** * Used by SimpleRecordArray * @param fieldSet * @param offsets * @param oa * @param ba */ SimpleRecord(RecordType<R> recordType, int[] offsets, Object[] oa, byte[] ba) { super(recordType); this.offsets = offsets; this.oa = oa; this.ba = ba; } private SimpleRecord(SimpleRecord<R> other) { super(other.type); this.offsets = other.offsets; this.oa = other.oa != null ? Arrays.copyOf(other.oa, other.oa.length) : null; this.ba = other.ba != null ? Arrays.copyOf(other.ba, other.ba.length) : null; } @Override protected SimpleRecord<R> clone() { return new SimpleRecord<R>(this); } int fieldOffset(Field<? super R, ?> field) { try { return offsets[field.id]; } catch (IndexOutOfBoundsException e) { throw new FieldNotFoundException(field, this); } } @Override public <V> V get(Field.ObjectField<? super R, V> field) { return (V) oa[fieldOffset(field)]; } @Override public <V> void set(Field.ObjectField<? super R, V> field, V value) { oa[fieldOffset(field)] = value; } @Override public <V> V get(Field.ObjectArrayField<? super R, V> field, int index) { if (index < 0 || index > field.length) throw new ArrayIndexOutOfBoundsException(index); return (V) oa[fieldOffset(field) + index]; } @Override public <V> void set(Field.ObjectArrayField<? super R, V> field, int index, V value) { if (index < 0 || index > field.length) throw new ArrayIndexOutOfBoundsException(index); oa[fieldOffset(field) + index] = value; } @Override public <V> void get(Field.ObjectArrayField<? super R, V> field, V[] target, int offset) { System.arraycopy(oa, fieldOffset(field), target, offset, field.length); } @Override public <V> void set(Field.ObjectArrayField<? super R, V> field, V[] source, int offset) { System.arraycopy(source, offset, oa, fieldOffset(field), field.length); } @Override public <S, V> void set(Field.ObjectArrayField<? super R, V> field, Record<S> source, Field.ObjectArrayField<? super S, V> sourceField) { boundsCheck(field); Object array = null; if (source instanceof SimpleRecord) System.arraycopy(((SimpleRecord) source).oa, ((SimpleRecord<S>) source).fieldOffset(sourceField), oa, fieldOffset(field), field.length); else if (source instanceof DynamicRecord && (array = ((DynamicRecord) source).get(sourceField)) != null) System.arraycopy(array, 0, oa, fieldOffset(field), field.length); else { for (int i = 0; i < field.length; i++) oa[fieldOffset(field) + i] = source.get(sourceField, i); } for (int i = 0; i < field.length; i++) oa[fieldOffset(field) + i] = source.get(sourceField, i); } private void boundsCheck(Field field) { // if (field.id < 0) // throw new IllegalArgumentException("Incompatible field"); // if (fieldOffset(field) + field.size() > ba.length) // throw new IllegalArgumentException("Incompatible field"); // if (field.id < 0) // throw new IllegalArgumentException("Incompatible field"); // if (fieldOffset(field) + field.size() > ba.length) // throw new IllegalArgumentException("Incompatible field"); } private void boundsCheck(Field.ArrayField field, int index) { boundsCheck(field); if (index < 0 || index >= field.length) throw new ArrayIndexOutOfBoundsException(index); } @Override public boolean get(Field.BooleanField<? super R> field) { boundsCheck(field); return getBoolean(ba, fieldOffset(field)); } @Override public void set(Field.BooleanField<? super R> field, boolean value) { boundsCheck(field); setBoolean(ba, fieldOffset(field), value); } @Override public byte get(Field.ByteField<? super R> field) { boundsCheck(field); return getByte(ba, fieldOffset(field)); } @Override public void set(Field.ByteField<? super R> field, byte value) { boundsCheck(field); setByte(ba, fieldOffset(field), value); } @Override public short get(Field.ShortField<? super R> field) { boundsCheck(field); return getShort(ba, fieldOffset(field)); } @Override public void set(Field.ShortField<? super R> field, short value) { boundsCheck(field); setShort(ba, fieldOffset(field), value); } @Override public int get(Field.IntField<? super R> field) { boundsCheck(field); return getInt(ba, fieldOffset(field)); } @Override public void set(Field.IntField<? super R> field, int value) { boundsCheck(field); setInt(ba, fieldOffset(field), value); } @Override public long get(Field.LongField<? super R> field) { boundsCheck(field); return getLong(ba, fieldOffset(field)); } @Override public void set(Field.LongField<? super R> field, long value) { boundsCheck(field); setLong(ba, fieldOffset(field), value); } @Override public float get(Field.FloatField<? super R> field) { boundsCheck(field); return getFloat(ba, fieldOffset(field)); } @Override public void set(Field.FloatField<? super R> field, float value) { boundsCheck(field); setFloat(ba, fieldOffset(field), value); } @Override public double get(Field.DoubleField<? super R> field) { boundsCheck(field); return getDouble(ba, fieldOffset(field)); } @Override public void set(Field.DoubleField<? super R> field, double value) { boundsCheck(field); setDouble(ba, fieldOffset(field), value); } @Override public char get(Field.CharField<? super R> field) { boundsCheck(field); return getChar(ba, fieldOffset(field)); } @Override public void set(Field.CharField<? super R> field, char value) { boundsCheck(field); setChar(ba, fieldOffset(field), value); } @Override public boolean get(Field.BooleanArrayField<? super R> field, int index) { boundsCheck(field, index); return getBoolean(ba, fieldOffset(field) + offset(index, BOOLEAN_SHIFT)); } @Override public void set(Field.BooleanArrayField<? super R> field, int index, boolean value) { boundsCheck(field, index); setBoolean(ba, fieldOffset(field) + offset(index, BOOLEAN_SHIFT), value); } @Override public void get(Field.BooleanArrayField<? super R> field, boolean[] target, int offset) { boundsCheck(field); getArray(ba, fieldOffset(field), target, offset, field.length, BOOLEAN_SHIFT); } @Override public void set(Field.BooleanArrayField<? super R> field, boolean[] source, int offset) { boundsCheck(field); setArray(ba, fieldOffset(field), source, offset, field.length, BOOLEAN_SHIFT); } @Override public <S> void set(Field.BooleanArrayField<? super R> field, Record<S> source, Field.BooleanArrayField<? super S> sourceField) { boundsCheck(field); Object array = null; if (source instanceof SimpleRecord) System.arraycopy(((SimpleRecord<S>) source).ba, ((SimpleRecord<S>) source).fieldOffset(sourceField), ba, fieldOffset(field), field.length << BOOLEAN_SHIFT); else if (source instanceof DynamicRecord && (array = ((DynamicRecord) source).get(sourceField)) != null) setArray(ba, fieldOffset(field), array, 0, field.length, BOOLEAN_SHIFT); else { for (int i = 0; i < field.length; i++) setBoolean(ba, fieldOffset(field) + offset(i, BOOLEAN_SHIFT), source.get(sourceField, i)); } } @Override public byte get(Field.ByteArrayField<? super R> field, int index) { boundsCheck(field, index); return getByte(ba, fieldOffset(field) + offset(index, BYTE_SHIFT)); } @Override public void set(Field.ByteArrayField<? super R> field, int index, byte value) { boundsCheck(field, index); setByte(ba, fieldOffset(field) + offset(index, BYTE_SHIFT), value); } @Override public void get(Field.ByteArrayField<? super R> field, byte[] target, int offset) { boundsCheck(field); getArray(ba, fieldOffset(field), target, offset, field.length, BYTE_SHIFT); } @Override public void set(Field.ByteArrayField<? super R> field, byte[] source, int offset) { boundsCheck(field); setArray(ba, fieldOffset(field), source, offset, field.length, BYTE_SHIFT); } @Override public <S> void set(Field.ByteArrayField<? super R> field, Record<S> source, Field.ByteArrayField<? super S> sourceField) { boundsCheck(field); Object array = null; if (source instanceof SimpleRecord) System.arraycopy(((SimpleRecord<S>) source).ba, ((SimpleRecord<S>) source).fieldOffset(sourceField), ba, fieldOffset(field), field.length << BYTE_SHIFT); else if (source instanceof DynamicRecord && (array = ((DynamicRecord) source).get(sourceField)) != null) setArray(ba, fieldOffset(field), array, 0, field.length, BYTE_SHIFT); else { for (int i = 0; i < field.length; i++) setByte(ba, fieldOffset(field) + offset(i, BYTE_SHIFT), source.get(sourceField, i)); } } @Override public short get(Field.ShortArrayField<? super R> field, int index) { boundsCheck(field, index); return getShort(ba, fieldOffset(field) + offset(index, SHORT_SHIFT)); } @Override public void set(Field.ShortArrayField<? super R> field, int index, short value) { boundsCheck(field, index); setShort(ba, fieldOffset(field) + offset(index, SHORT_SHIFT), value); } @Override public void get(Field.ShortArrayField<? super R> field, short[] target, int offset) { boundsCheck(field); getArray(ba, fieldOffset(field), target, offset, field.length, SHORT_SHIFT); } @Override public void set(Field.ShortArrayField<? super R> field, short[] source, int offset) { boundsCheck(field); setArray(ba, fieldOffset(field), source, offset, field.length, SHORT_SHIFT); } @Override public <S> void set(Field.ShortArrayField<? super R> field, Record<S> source, Field.ShortArrayField<? super S> sourceField) { boundsCheck(field); Object array = null; if (source instanceof SimpleRecord) System.arraycopy(((SimpleRecord<S>) source).ba, ((SimpleRecord<S>) source).fieldOffset(sourceField), ba, fieldOffset(field), field.length << SHORT_SHIFT); else if (source instanceof DynamicRecord && (array = ((DynamicRecord) source).get(sourceField)) != null) setArray(ba, fieldOffset(field), array, 0, field.length, SHORT_SHIFT); else { for (int i = 0; i < field.length; i++) setShort(ba, fieldOffset(field) + offset(i, SHORT_SHIFT), source.get(sourceField, i)); } } @Override public int get(Field.IntArrayField<? super R> field, int index) { boundsCheck(field, index); return getInt(ba, fieldOffset(field) + offset(index, INT_SHIFT)); } @Override public void set(Field.IntArrayField<? super R> field, int index, int value) { boundsCheck(field, index); setInt(ba, fieldOffset(field) + offset(index, INT_SHIFT), value); } @Override public void get(Field.IntArrayField<? super R> field, int[] target, int offset) { boundsCheck(field); getArray(ba, fieldOffset(field), target, offset, field.length, INT_SHIFT); } @Override public void set(Field.IntArrayField<? super R> field, int[] source, int offset) { boundsCheck(field); setArray(ba, fieldOffset(field), source, offset, field.length, INT_SHIFT); } @Override public <S> void set(Field.IntArrayField<? super R> field, Record<S> source, Field.IntArrayField<? super S> sourceField) { boundsCheck(field); Object array = null; if (source instanceof SimpleRecord) System.arraycopy(((SimpleRecord<S>) source).ba, ((SimpleRecord<S>) source).fieldOffset(sourceField), ba, fieldOffset(field), field.length << INT_SHIFT); else if (source instanceof DynamicRecord && (array = ((DynamicRecord) source).get(sourceField)) != null) setArray(ba, fieldOffset(field), array, 0, field.length, INT_SHIFT); else { for (int i = 0; i < field.length; i++) setInt(ba, fieldOffset(field) + offset(i, INT_SHIFT), source.get(sourceField, i)); } } @Override public long get(Field.LongArrayField<? super R> field, int index) { boundsCheck(field, index); return getLong(ba, fieldOffset(field) + offset(index, LONG_SHIFT)); } @Override public void set(Field.LongArrayField<? super R> field, int index, long value) { boundsCheck(field, index); setLong(ba, fieldOffset(field) + offset(index, LONG_SHIFT), value); } @Override public void get(Field.LongArrayField<? super R> field, long[] target, int offset) { boundsCheck(field); getArrayLong(ba, fieldOffset(field), target, offset, field.length, LONG_SHIFT); } @Override public void set(Field.LongArrayField<? super R> field, long[] source, int offset) { boundsCheck(field); setArrayLong(ba, fieldOffset(field), source, offset, field.length, LONG_SHIFT); } @Override public <S> void set(Field.LongArrayField<? super R> field, Record<S> source, Field.LongArrayField<? super S> sourceField) { boundsCheck(field); Object array = null; if (source instanceof SimpleRecord) System.arraycopy(((SimpleRecord<S>) source).ba, ((SimpleRecord<S>) source).fieldOffset(sourceField), ba, fieldOffset(field), field.length << LONG_SHIFT); else if (source instanceof DynamicRecord && (array = ((DynamicRecord) source).get(sourceField)) != null) setArrayLong(ba, fieldOffset(field), array, 0, field.length, LONG_SHIFT); else { for (int i = 0; i < field.length; i++) setLong(ba, fieldOffset(field) + offset(i, LONG_SHIFT), source.get(sourceField, i)); } } @Override public float get(Field.FloatArrayField<? super R> field, int index) { boundsCheck(field, index); return getFloat(ba, fieldOffset(field) + offset(index, FLOAT_SHIFT)); } @Override public void set(Field.FloatArrayField<? super R> field, int index, float value) { boundsCheck(field, index); setFloat(ba, fieldOffset(field) + offset(index, FLOAT_SHIFT), value); } @Override public void get(Field.FloatArrayField<? super R> field, float[] target, int offset) { boundsCheck(field); getArray(ba, fieldOffset(field), target, offset, field.length, FLOAT_SHIFT); } @Override public void set(Field.FloatArrayField<? super R> field, float[] source, int offset) { boundsCheck(field); setArray(ba, fieldOffset(field), source, offset, field.length, FLOAT_SHIFT); } @Override public <S> void set(Field.FloatArrayField<? super R> field, Record<S> source, Field.FloatArrayField<? super S> sourceField) { boundsCheck(field); Object array = null; if (source instanceof SimpleRecord) System.arraycopy(((SimpleRecord<S>) source).ba, ((SimpleRecord<S>) source).fieldOffset(sourceField), ba, fieldOffset(field), field.length << FLOAT_SHIFT); else if (source instanceof DynamicRecord && (array = ((DynamicRecord) source).get(sourceField)) != null) setArray(ba, fieldOffset(field), array, 0, field.length, FLOAT_SHIFT); else { for (int i = 0; i < field.length; i++) setFloat(ba, fieldOffset(field) + offset(i, FLOAT_SHIFT), source.get(sourceField, i)); } } @Override public double get(Field.DoubleArrayField<? super R> field, int index) { boundsCheck(field, index); return getDouble(ba, fieldOffset(field) + offset(index, DOUBLE_SHIFT)); } @Override public void set(Field.DoubleArrayField<? super R> field, int index, double value) { boundsCheck(field, index); setDouble(ba, fieldOffset(field) + offset(index, DOUBLE_SHIFT), value); } @Override public void get(Field.DoubleArrayField<? super R> field, double[] target, int offset) { boundsCheck(field); getArrayLong(ba, fieldOffset(field), target, offset, field.length, DOUBLE_SHIFT); } @Override public void set(Field.DoubleArrayField<? super R> field, double[] source, int offset) { boundsCheck(field); setArrayLong(ba, fieldOffset(field), source, offset, field.length, DOUBLE_SHIFT); } @Override public <S> void set(Field.DoubleArrayField<? super R> field, Record<S> source, Field.DoubleArrayField<? super S> sourceField) { boundsCheck(field); Object array = null; if (source instanceof SimpleRecord) System.arraycopy(((SimpleRecord<S>) source).ba, ((SimpleRecord<S>) source).fieldOffset(sourceField), ba, fieldOffset(field), field.length << DOUBLE_SHIFT); else if (source instanceof DynamicRecord && (array = ((DynamicRecord) source).get(sourceField)) != null) setArrayLong(ba, fieldOffset(field), array, 0, field.length, DOUBLE_SHIFT); else { for (int i = 0; i < field.length; i++) setDouble(ba, fieldOffset(field) + offset(i, DOUBLE_SHIFT), source.get(sourceField, i)); } } @Override public char get(Field.CharArrayField<? super R> field, int index) { boundsCheck(field, index); return getChar(ba, fieldOffset(field) + offset(index, CHAR_SHIFT)); } @Override public void set(Field.CharArrayField<? super R> field, int index, char value) { boundsCheck(field, index); setChar(ba, fieldOffset(field) + offset(index, CHAR_SHIFT), value); } @Override public void get(Field.CharArrayField<? super R> field, char[] target, int offset) { boundsCheck(field); getArray(ba, fieldOffset(field), target, offset, field.length, CHAR_SHIFT); } @Override public void set(Field.CharArrayField<? super R> field, char[] source, int offset) { boundsCheck(field); setArray(ba, fieldOffset(field), source, offset, field.length, CHAR_SHIFT); } @Override public <S> void set(Field.CharArrayField<? super R> field, Record<S> source, Field.CharArrayField<? super S> sourceField) { boundsCheck(field); Object array = null; if (source instanceof SimpleRecord) System.arraycopy(((SimpleRecord<S>) source).ba, ((SimpleRecord<S>) source).fieldOffset(sourceField), ba, fieldOffset(field), field.length << CHAR_SHIFT); else if (source instanceof DynamicRecord && (array = ((DynamicRecord) source).get(sourceField)) != null) setArray(ba, fieldOffset(field), array, 0, field.length, CHAR_SHIFT); else { for (int i = 0; i < field.length; i++) setChar(ba, fieldOffset(field) + offset(i, CHAR_SHIFT), source.get(sourceField, i)); } } static final Unsafe unsafe = UtilUnsafe.getUnsafe(); private static final int base; private static final int baseLong; private static final int shift; static { try { if (unsafe.arrayIndexScale(boolean[].class) != 1) throw new AssertionError("Strange boolean array scale: " + unsafe.arrayIndexScale(boolean[].class)); if (unsafe.arrayIndexScale(byte[].class) != 1) throw new AssertionError("Strange byte array scale: " + unsafe.arrayIndexScale(byte[].class)); if (unsafe.arrayIndexScale(short[].class) != 2) throw new AssertionError("Strange short array scale: " + unsafe.arrayIndexScale(short[].class)); if (unsafe.arrayIndexScale(char[].class) != 2) throw new AssertionError("Strange char array scale: " + unsafe.arrayIndexScale(char[].class)); if (unsafe.arrayIndexScale(int[].class) != 4) throw new AssertionError("Strange int array scale: " + unsafe.arrayIndexScale(int[].class)); if (unsafe.arrayIndexScale(float[].class) != 4) throw new AssertionError("Strange float array scale: " + unsafe.arrayIndexScale(float[].class)); if (unsafe.arrayIndexScale(long[].class) != 8) throw new AssertionError("Strange long array scale: " + unsafe.arrayIndexScale(long[].class)); if (unsafe.arrayIndexScale(double[].class) != 8) throw new AssertionError("Strange double array scale: " + unsafe.arrayIndexScale(double[].class)); base = unsafe.arrayBaseOffset(byte[].class); baseLong = unsafe.arrayBaseOffset(long[].class); if (unsafe.arrayBaseOffset(boolean[].class) != base) throw new AssertionError("different array base"); if (unsafe.arrayBaseOffset(short[].class) != base) throw new AssertionError("different array base"); if (unsafe.arrayBaseOffset(char[].class) != base) throw new AssertionError("different array base"); if (unsafe.arrayBaseOffset(int[].class) != base) throw new AssertionError("different array base"); if (unsafe.arrayBaseOffset(float[].class) != base) throw new AssertionError("different array base"); if (unsafe.arrayBaseOffset(long[].class) != baseLong) throw new AssertionError("different array base"); if (unsafe.arrayBaseOffset(double[].class) != baseLong) throw new AssertionError("different array base"); int scale = unsafe.arrayIndexScale(byte[].class); if ((scale & (scale - 1)) != 0) throw new Error("data type scale not a power of two"); shift = 31 - Integer.numberOfLeadingZeros(scale); if (scale != 1 || shift != 0) throw new AssertionError("Strange byte array alignment"); } catch (Exception ex) { throw new Error(ex); } } private static long byteOffset(long i) { return base + i; } private static long offset(long index, int typeSizeShift) { return index << typeSizeShift; } private static boolean getBoolean(byte[] array, long i) { return unsafe.getBoolean(array, byteOffset(i)); } private static void setBoolean(byte[] array, long i, boolean value) { unsafe.putBoolean(array, byteOffset(i), value); } private static byte getByte(byte[] array, long i) { return unsafe.getByte(array, byteOffset(i)); } private static void setByte(byte[] array, long i, byte value) { unsafe.putByte(array, byteOffset(i), value); } private static short getShort(byte[] array, long i) { return unsafe.getShort(array, byteOffset(i)); } private static void setShort(byte[] array, long i, short value) { unsafe.putShort(array, byteOffset(i), value); } private static int getInt(byte[] array, long i) { return unsafe.getInt(array, byteOffset(i)); } private static void setInt(byte[] array, long i, int value) { unsafe.putInt(array, byteOffset(i), value); } private static long getLong(byte[] array, long i) { return unsafe.getLong(array, byteOffset(i)); } private static void setLong(byte[] array, long i, long value) { unsafe.putLong(array, byteOffset(i), value); } private static char getChar(byte[] array, long i) { return unsafe.getChar(array, byteOffset(i)); } private static void setChar(byte[] array, long i, char value) { unsafe.putChar(array, byteOffset(i), value); } private static float getFloat(byte[] array, long i) { return unsafe.getFloat(array, byteOffset(i)); } private static void setFloat(byte[] array, long i, float value) { unsafe.putFloat(array, byteOffset(i), value); } private static double getDouble(byte[] array, long i) { return unsafe.getDouble(array, byteOffset(i)); } private static void setDouble(byte[] array, long i, double value) { unsafe.putDouble(array, byteOffset(i), value); } private static void getArray(byte[] array, long i, Object target, int offset, int length, int shift) { unsafe.copyMemory(array, byteOffset(i), target, base + (offset << shift), length << shift); } private static void setArray(byte[] array, long i, Object source, int offset, int length, int shift) { unsafe.copyMemory(source, base + (offset << shift), array, byteOffset(i), length << shift); } private static void getArrayLong(byte[] array, long i, Object target, int offset, int length, int shift) { unsafe.copyMemory(array, byteOffset(i), target, baseLong + (offset << shift), length << shift); } private static void setArrayLong(byte[] array, long i, Object source, int offset, int length, int shift) { unsafe.copyMemory(source, baseLong + (offset << shift), array, byteOffset(i), length << shift); } private static final int BOOLEAN_SHIFT = 0; private static final int BYTE_SHIFT = 0; private static final int SHORT_SHIFT = 1; private static final int CHAR_SHIFT = 1; private static final int INT_SHIFT = 2; private static final int FLOAT_SHIFT = 2; private static final int LONG_SHIFT = 3; private static final int DOUBLE_SHIFT = 3; }