You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by sc...@apache.org on 2013/04/30 18:18:26 UTC

svn commit: r1477712 [1/2] - in /avro/trunk: ./ lang/java/avro/src/main/java/org/apache/avro/generic/ lang/java/avro/src/main/java/org/apache/avro/io/ lang/java/avro/src/main/java/org/apache/avro/reflect/ lang/java/avro/src/test/java/org/apache/avro/re...

Author: scottcarey
Date: Tue Apr 30 16:18:26 2013
New Revision: 1477712

URL: http://svn.apache.org/r1477712
Log:
AVRO-1282. Java: Use sun.misc.Unsafe to improve Reflect API Performance.
    (Leo Romanoff via scottcarey)

Added:
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ArrayAccessor.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccess.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessReflect.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessUnsafe.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessor.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectionUtil.java
    avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectionUtil.java
Modified:
    avro/trunk/CHANGES.txt
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumWriter.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/EncoderFactory.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumReader.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumWriter.java
    avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java
    avro/trunk/lang/java/ipc/src/test/java/org/apache/avro/io/Perf.java

Modified: avro/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/avro/trunk/CHANGES.txt?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/CHANGES.txt (original)
+++ avro/trunk/CHANGES.txt Tue Apr 30 16:18:26 2013
@@ -39,6 +39,9 @@ Trunk (not yet released)
     AVRO-1299. Java: SpecificRecordBase implements GenericRecord.
     (Christophe Taton via cutting)
 
+    AVRO-1282. Java: Use sun.misc.Unsafe to improve Reflect API Performance.
+    (Leo Romanoff via scottcarey)
+
   BUG FIXES
 
     AVRO-1296. Python: Fix schemas retrieved from protocol types

Modified: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java (original)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java Tue Apr 30 16:18:26 2013
@@ -165,7 +165,7 @@ public class GenericDatumReader<D> imple
     default: throw new AvroRuntimeException("Unknown type: " + expected);
     }
   }
-
+  
   /** Called to read a record instance. May be overridden for alternate record
    * representations.*/
   protected Object readRecord(Object old, Schema expected, 
@@ -176,13 +176,23 @@ public class GenericDatumReader<D> imple
     for (Field f : in.readFieldOrder()) {
       int pos = f.pos();
       String name = f.name();
-      Object oldDatum = (old!=null) ? data.getField(r, name, pos, state) : null;
-      data.setField(r, name, pos, read(oldDatum, f.schema(), in), state);
+      Object oldDatum = null;
+      if (old!=null) {
+        oldDatum = data.getField(r, name, pos, state);
+      }
+      readField(r, f, oldDatum, in, state);
     }
 
     return r;
   }
   
+  /** Called to read a single field of a record. May be overridden for more 
+   * efficient or alternate implementations.*/
+  protected void readField(Object r, Field f, Object oldDatum,
+    ResolvingDecoder in, Object state) throws IOException {
+    data.setField(r, f.name(), f.pos(), read(oldDatum, f.schema(), in), state);
+  }
+  
   /** Called to read an enum value. May be overridden for alternate enum
    * representations.  By default, returns a GenericEnumSymbol. */
   protected Object readEnum(Schema expected, Decoder in) throws IOException {

Modified: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumWriter.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumWriter.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumWriter.java (original)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumWriter.java Tue Apr 30 16:18:26 2013
@@ -101,12 +101,19 @@ public class GenericDatumWriter<D> imple
     throws IOException {
     Object state = data.getRecordState(datum, schema);
     for (Field f : schema.getFields()) {
-      Object value = data.getField(datum, f.name(), f.pos(), state);
-      try {
-        write(f.schema(), value, out);
-      } catch (NullPointerException e) {
-        throw npe(e, " in field "+f.name());
-      }
+      writeField(datum, f, out, state);
+    }
+  }
+  
+  /** Called to write a single field of a record. May be overridden for more 
+   * efficient or alternate implementations.*/
+  protected void writeField(Object datum, Field f, Encoder out, Object state) 
+      throws IOException {
+    Object value = data.getField(datum, f.name(), f.pos(), state);
+    try {
+      write(f.schema(), value, out);
+    } catch (NullPointerException e) {
+      throw npe(e, " in field " + f.name());
     }
   }
   

Modified: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/EncoderFactory.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/EncoderFactory.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/EncoderFactory.java (original)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/EncoderFactory.java Tue Apr 30 16:18:26 2013
@@ -119,7 +119,7 @@ public class EncoderFactory {
       size = MIN_BLOCK_BUFFER_SIZE;
     if (size > MAX_BLOCK_BUFFER_SIZE)
       size = MAX_BLOCK_BUFFER_SIZE;
-    this.binaryBufferSize = size;
+    this.binaryBlockSize = size;
     return this;
   }
 
@@ -249,11 +249,15 @@ public class EncoderFactory {
    * @see BlockingBinaryEncoder
    * @see Encoder
    */
-  public BinaryEncoder blockingBinaryEncoder(OutputStream out, BinaryEncoder reuse) {
+  public BinaryEncoder blockingBinaryEncoder(OutputStream out,
+      BinaryEncoder reuse) {
+    int blockSize = this.binaryBlockSize;
+    int bufferSize = (blockSize * 2 >= this.binaryBufferSize) ? 32
+        : this.binaryBufferSize;
     if (null == reuse || !reuse.getClass().equals(BlockingBinaryEncoder.class)) {
-      return new BlockingBinaryEncoder(out, this.binaryBlockSize, 32);
+      return new BlockingBinaryEncoder(out, blockSize, bufferSize);
     } else {
-      return ((BlockingBinaryEncoder)reuse).configure(out, this.binaryBlockSize, 32);
+      return ((BlockingBinaryEncoder) reuse).configure(out, blockSize, bufferSize);
     }
   }
 

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ArrayAccessor.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ArrayAccessor.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ArrayAccessor.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ArrayAccessor.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,228 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.avro.io.Encoder;
+import org.apache.avro.io.ResolvingDecoder;
+
+/**
+ * Helper class to provide native array access whenever possible. It is much
+ * faster than using reflection-based operations on arrays.
+ */
+class ArrayAccessor {
+
+  static void writeArray(boolean[] data, Encoder out) throws IOException {
+    int size = data.length;
+    out.setItemCount(size);
+    for (int i = 0; i < size; i++) {
+      out.startItem();
+      out.writeBoolean(data[i]);
+    }
+  }
+
+  // short, and char arrays are upcast to avro int
+
+  static void writeArray(short[] data, Encoder out) throws IOException {
+    int size = data.length;
+    out.setItemCount(size);
+    for (int i = 0; i < size; i++) {
+      out.startItem();
+      out.writeInt(data[i]);
+    }
+  }
+
+  static void writeArray(char[] data, Encoder out) throws IOException {
+    int size = data.length;
+    out.setItemCount(size);
+    for (int i = 0; i < size; i++) {
+      out.startItem();
+      out.writeInt(data[i]);
+    }
+  }
+
+  static void writeArray(int[] data, Encoder out) throws IOException {
+    int size = data.length;
+    out.setItemCount(size);
+    for (int i = 0; i < size; i++) {
+      out.startItem();
+      out.writeInt(data[i]);
+    }
+  }
+
+  static void writeArray(long[] data, Encoder out) throws IOException {
+    int size = data.length;
+    out.setItemCount(size);
+    for (int i = 0; i < size; i++) {
+      out.startItem();
+      out.writeLong(data[i]);
+    }
+  }
+
+  static void writeArray(float[] data, Encoder out) throws IOException {
+    int size = data.length;
+    out.setItemCount(size);
+    for (int i = 0; i < size; i++) {
+      out.startItem();
+      out.writeFloat(data[i]);
+    }
+  }
+
+  static void writeArray(double[] data, Encoder out) throws IOException {
+    int size = data.length;
+    out.setItemCount(size);
+    for (int i = 0; i < size; i++) {
+      out.startItem();
+      out.writeDouble(data[i]);
+    }
+  }
+
+  static Object readArray(Object array, Class<?> elementType, long l,
+      ResolvingDecoder in) throws IOException {
+    if (elementType == int.class)
+      return readArray((int[]) array, l, in);
+    if (elementType == long.class)
+      return readArray((long[]) array, l, in);
+    if (elementType == float.class)
+      return readArray((float[]) array, l, in);
+    if (elementType == double.class)
+      return readArray((double[]) array, l, in);
+    if (elementType == boolean.class)
+      return readArray((boolean[]) array, l, in);
+    if (elementType == char.class)
+      return readArray((char[]) array, l, in);
+    if (elementType == short.class)
+      return readArray((short[]) array, l, in);
+    return null;
+  }
+
+  static boolean[] readArray(boolean[] array, long l, ResolvingDecoder in)
+      throws IOException {
+    int index = 0;
+    do {
+      int limit = index + (int) l;
+      if (array.length < limit) {
+        array = Arrays.copyOf(array, limit);
+      }
+      while (index < limit) {
+        array[index] = in.readBoolean();
+        index++;
+      }
+    } while ((l = in.arrayNext()) > 0);
+    return array;
+  }
+
+  static int[] readArray(int[] array, long l, ResolvingDecoder in)
+      throws IOException {
+    int index = 0;
+    do {
+      int limit = index + (int) l;
+      if (array.length < limit) {
+        array = Arrays.copyOf(array, limit);
+      }
+      while (index < limit) {
+        array[index] = in.readInt();
+        index++;
+      }
+    } while ((l = in.arrayNext()) > 0);
+    return array;
+  }
+
+  static short[] readArray(short[] array, long l, ResolvingDecoder in)
+      throws IOException {
+    int index = 0;
+    do {
+      int limit = index + (int) l;
+      if (array.length < limit) {
+        array = Arrays.copyOf(array, limit);
+      }
+      while (index < limit) {
+        array[index] = (short) in.readInt();
+        index++;
+      }
+    } while ((l = in.arrayNext()) > 0);
+    return array;
+  }
+
+  static char[] readArray(char[] array, long l, ResolvingDecoder in)
+      throws IOException {
+    int index = 0;
+    do {
+      int limit = index + (int) l;
+      if (array.length < limit) {
+        array = Arrays.copyOf(array, limit);
+      }
+      while (index < limit) {
+        array[index] = (char) in.readInt();
+        index++;
+      }
+    } while ((l = in.arrayNext()) > 0);
+    return array;
+  }
+
+  static long[] readArray(long[] array, long l, ResolvingDecoder in)
+      throws IOException {
+    int index = 0;
+    do {
+      int limit = index + (int) l;
+      if (array.length < limit) {
+        array = Arrays.copyOf(array, limit);
+      }
+      while (index < limit) {
+        array[index] = in.readLong();
+        index++;
+      }
+    } while ((l = in.arrayNext()) > 0);
+    return array;
+  }
+
+  static float[] readArray(float[] array, long l, ResolvingDecoder in)
+      throws IOException {
+    int index = 0;
+    do {
+      int limit = index + (int) l;
+      if (array.length < limit) {
+        array = Arrays.copyOf(array, limit);
+      }
+      while (index < limit) {
+        array[index] = in.readFloat();
+        index++;
+      }
+    } while ((l = in.arrayNext()) > 0);
+    return array;
+  }
+
+  static double[] readArray(double[] array, long l, ResolvingDecoder in)
+      throws IOException {
+    int index = 0;
+    do {
+      int limit = index + (int) l;
+      if (array.length < limit) {
+        array = Arrays.copyOf(array, limit);
+      }
+      while (index < limit) {
+        array[index] = in.readDouble();
+        index++;
+      }
+    } while ((l = in.arrayNext()) > 0);
+    return array;
+  }
+
+}

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccess.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccess.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccess.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccess.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import java.lang.reflect.Field;
+
+abstract class FieldAccess {
+  
+  protected abstract FieldAccessor getAccessor(Field field);
+
+}

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessReflect.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessReflect.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessReflect.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessReflect.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+class FieldAccessReflect extends FieldAccess {
+
+  @Override
+  protected FieldAccessor getAccessor(Field field) {
+    return new ReflectionBasedAccessor(field);
+  }
+
+  private final class ReflectionBasedAccessor extends FieldAccessor {
+    private final Field field;
+
+    public ReflectionBasedAccessor(Field field) {
+      this.field = field;
+    }
+
+    @Override
+    public String toString() {
+      return field.getName();
+    }
+
+    @Override
+    public Object get(Object object) throws IllegalAccessException {
+      return field.get(object);
+    }
+
+    @Override
+    public void set(Object object, Object value) throws IllegalAccessException,
+        IOException {
+      field.set(object, value);
+    }
+
+  }
+}

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessUnsafe.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessUnsafe.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessUnsafe.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessUnsafe.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,309 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.apache.avro.io.Decoder;
+import org.apache.avro.io.Encoder;
+
+import sun.misc.Unsafe;
+
+@SuppressWarnings("restriction")
+class FieldAccessUnsafe extends FieldAccess {
+
+  private static final Unsafe UNSAFE;
+
+  static {
+    try {
+      Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
+      theUnsafe.setAccessible(true);
+      UNSAFE = (Unsafe) theUnsafe.get(null);
+      // It seems not all Unsafe implementations implement the following method.
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
+  protected FieldAccessor getAccessor(Field field) {
+    Class<?> c = field.getType();
+    if (c == int.class)
+      return new UnsafeIntField(field);
+    else if (c == long.class)
+      return new UnsafeLongField(field);
+    else if (c == byte.class)
+      return new UnsafeByteField(field);
+    else if (c == float.class)
+      return new UnsafeFloatField(field);
+    else if (c == double.class)
+      return new UnsafeDoubleField(field);
+    else if (c == char.class)
+      return new UnsafeCharField(field);
+    else if (c == boolean.class)
+      return new UnsafeBooleanField(field);
+    else if (c == short.class)
+      return new UnsafeShortField(field);
+    else
+      return new UnsafeObjectField(field);
+  }
+
+  abstract static class UnsafeCachedField extends FieldAccessor {
+    protected final long offset;
+
+    UnsafeCachedField(long offset) {
+      this.offset = offset;
+    }
+
+    @Override
+    protected boolean supportsIO() {
+      return true;
+    }
+  }
+
+  final static class UnsafeIntField extends UnsafeCachedField {
+    UnsafeIntField(Field f) {
+      super(UNSAFE.objectFieldOffset(f));
+    }
+
+    @Override
+    protected void set(Object object, Object value) {
+      UNSAFE.putInt(object, offset, (Integer) value);
+    }
+
+    @Override
+    protected Object get(Object object) {
+      return UNSAFE.getInt(object, offset);
+    }
+
+    @Override
+    protected void read(Object object, Decoder in) throws IOException {
+      UNSAFE.putInt(object, offset, in.readInt());
+    }
+
+    @Override
+    protected void write(Object object, Encoder out) throws IOException {
+      out.writeInt(UNSAFE.getInt(object, offset));
+    }
+  }
+
+  final static class UnsafeFloatField extends UnsafeCachedField {
+    protected UnsafeFloatField(Field f) {
+      super(UNSAFE.objectFieldOffset(f));
+    }
+
+    @Override
+    protected void set(Object object, Object value) {
+      UNSAFE.putFloat(object, offset, (Float) value);
+    }
+
+    @Override
+    protected Object get(Object object) {
+      return UNSAFE.getFloat(object, offset);
+    }
+
+    @Override
+    protected void read(Object object, Decoder in) throws IOException {
+      UNSAFE.putFloat(object, offset, in.readFloat());
+    }
+
+    @Override
+    protected void write(Object object, Encoder out) throws IOException {
+      out.writeFloat(UNSAFE.getFloat(object, offset));
+    }
+  }
+
+  final static class UnsafeShortField extends UnsafeCachedField {
+    protected UnsafeShortField(Field f) {
+      super(UNSAFE.objectFieldOffset(f));
+    }
+
+    @Override
+    protected void set(Object object, Object value) {
+      UNSAFE.putShort(object, offset, (Short) value);
+    }
+
+    @Override
+    protected Object get(Object object) {
+      return UNSAFE.getShort(object, offset);
+    }
+
+    @Override
+    protected void read(Object object, Decoder in) throws IOException {
+      UNSAFE.putShort(object, offset, (short) in.readInt());
+    }
+
+    @Override
+    protected void write(Object object, Encoder out) throws IOException {
+      out.writeInt(UNSAFE.getShort(object, offset));
+    }
+  }
+
+  final static class UnsafeByteField extends UnsafeCachedField {
+    protected UnsafeByteField(Field f) {
+      super(UNSAFE.objectFieldOffset(f));
+    }
+
+    @Override
+    protected void set(Object object, Object value) {
+      UNSAFE.putByte(object, offset, (Byte) value);
+    }
+
+    @Override
+    protected Object get(Object object) {
+      return UNSAFE.getByte(object, offset);
+    }
+
+    @Override
+    protected void read(Object object, Decoder in) throws IOException {
+      UNSAFE.putByte(object, offset, (byte) in.readInt());
+    }
+
+    @Override
+    protected void write(Object object, Encoder out) throws IOException {
+      out.writeInt(UNSAFE.getByte(object, offset));
+    }
+  }
+
+  final static class UnsafeBooleanField extends UnsafeCachedField {
+    protected UnsafeBooleanField(Field f) {
+      super(UNSAFE.objectFieldOffset(f));
+    }
+
+    @Override
+    protected void set(Object object, Object value) {
+      UNSAFE.putBoolean(object, offset, (Boolean) value);
+    }
+
+    @Override
+    protected Object get(Object object) {
+      return UNSAFE.getBoolean(object, offset);
+    }
+
+    @Override
+    protected void read(Object object, Decoder in) throws IOException {
+      UNSAFE.putBoolean(object, offset, in.readBoolean());
+    }
+
+    @Override
+    protected void write(Object object, Encoder out) throws IOException {
+      out.writeBoolean(UNSAFE.getBoolean(object, offset));
+    }
+  }
+
+  final static class UnsafeCharField extends UnsafeCachedField {
+    protected UnsafeCharField(Field f) {
+      super(UNSAFE.objectFieldOffset(f));
+    }
+
+    @Override
+    protected void set(Object object, Object value) {
+      UNSAFE.putInt(object, offset, (Character) value);
+    }
+
+    @Override
+    protected Object get(Object object) {
+      return UNSAFE.getChar(object, offset);
+    }
+
+    @Override
+    protected void read(Object object, Decoder in) throws IOException {
+      UNSAFE.putChar(object, offset, (char) in.readInt());
+    }
+
+    @Override
+    protected void write(Object object, Encoder out) throws IOException {
+      out.writeInt(UNSAFE.getChar(object, offset));
+    }
+  }
+
+  final static class UnsafeLongField extends UnsafeCachedField {
+    protected UnsafeLongField(Field f) {
+      super(UNSAFE.objectFieldOffset(f));
+    }
+
+    @Override
+    protected void set(Object object, Object value) {
+      UNSAFE.putLong(object, offset, (Long) value);
+    }
+
+    @Override
+    protected Object get(Object object) {
+      return UNSAFE.getLong(object, offset);
+    }
+
+    @Override
+    protected void read(Object object, Decoder in) throws IOException {
+      UNSAFE.putLong(object, offset, in.readLong());
+    }
+
+    @Override
+    protected void write(Object object, Encoder out) throws IOException {
+      out.writeLong(UNSAFE.getLong(object, offset));
+    }
+  }
+
+  final static class UnsafeDoubleField extends UnsafeCachedField {
+    protected UnsafeDoubleField(Field f) {
+      super(UNSAFE.objectFieldOffset(f));
+    }
+
+    @Override
+    protected void set(Object object, Object value) {
+      UNSAFE.putDouble(object, offset, (Double) value);
+    }
+
+    @Override
+    protected Object get(Object object) {
+      return UNSAFE.getDouble(object, offset);
+    }
+
+    @Override
+    protected void read(Object object, Decoder in) throws IOException {
+      UNSAFE.putDouble(object, offset, in.readDouble());
+    }
+
+    @Override
+    protected void write(Object object, Encoder out) throws IOException {
+      out.writeDouble(UNSAFE.getDouble(object, offset));
+    }
+  }
+
+  final static class UnsafeObjectField extends UnsafeCachedField {
+    protected UnsafeObjectField(Field f) {
+      super(UNSAFE.objectFieldOffset(f));
+    }
+
+    @Override
+    protected void set(Object object, Object value) {
+      UNSAFE.putObject(object, offset, value);
+    }
+
+    @Override
+    protected Object get(Object object) {
+      return UNSAFE.getObject(object, offset);
+    }
+    
+    @Override
+    protected boolean supportsIO() {
+      return false;
+    }
+    
+  }
+}

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessor.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessor.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessor.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessor.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import java.io.IOException;
+
+import org.apache.avro.io.Decoder;
+import org.apache.avro.io.Encoder;
+
+abstract class FieldAccessor {
+  FieldAccessor() {
+  }
+
+  protected abstract Object get(Object object) throws IllegalAccessException;
+
+  protected abstract void set(Object object, Object value)
+      throws IllegalAccessException, IOException;
+
+  protected void read(Object object, Decoder in) throws IOException {
+  }
+
+  protected void write(Object object, Encoder out) throws IOException {
+  }
+
+  protected boolean supportsIO() {
+    return false;
+  }
+}

Modified: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java (original)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java Tue Apr 30 16:18:26 2013
@@ -17,36 +17,39 @@
  */
 package org.apache.avro.reflect;
 
+import java.io.IOException;
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
-import java.lang.reflect.Type;
 import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.GenericArrayType;
-import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
 import java.nio.ByteBuffer;
-import java.util.Collection;
-import java.util.List;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.avro.AvroRemoteException;
 import org.apache.avro.AvroRuntimeException;
 import org.apache.avro.AvroTypeException;
 import org.apache.avro.Protocol;
-import org.apache.avro.Schema;
 import org.apache.avro.Protocol.Message;
-import org.apache.avro.generic.IndexedRecord;
-import org.apache.avro.generic.GenericFixed;
+import org.apache.avro.Schema;
 import org.apache.avro.generic.GenericContainer;
-import org.apache.avro.specific.SpecificData;
-import org.apache.avro.specific.FixedSize;
+import org.apache.avro.generic.GenericFixed;
+import org.apache.avro.generic.IndexedRecord;
 import org.apache.avro.io.BinaryData;
 import org.apache.avro.io.DatumReader;
+import org.apache.avro.specific.FixedSize;
+import org.apache.avro.specific.SpecificData;
 import org.codehaus.jackson.JsonNode;
 import org.codehaus.jackson.node.NullNode;
 
@@ -55,7 +58,6 @@ import com.thoughtworks.paranamer.Parana
 
 /** Utilities to use existing Java classes and interfaces via reflection. */
 public class ReflectData extends SpecificData {
-  
   /** {@link ReflectData} implementation that permits null field values.  The
    * schema generated for each field is a union of its declared type and
    * null. */
@@ -66,6 +68,7 @@ public class ReflectData extends Specifi
     /** Return the singleton instance. */
     public static AllowNull get() { return INSTANCE; }
 
+    @Override
     protected Schema createFieldSchema(Field field, Map<String, Schema> names) {
       Schema schema = super.createFieldSchema(field, names);
       return makeNullable(schema);
@@ -98,27 +101,49 @@ public class ReflectData extends Specifi
 
   @Override
   public void setField(Object record, String name, int position, Object o) {
+    setField(record, name, position, o, null);
+  }
+
+  @Override
+  protected void setField(Object record, String name, int pos, Object o,
+    Object state) {
     if (record instanceof IndexedRecord) {
-      super.setField(record, name, position, o);
+      super.setField(record, name, pos, o);
       return;
     }
     try {
-      getField(record.getClass(), name).set(record, o);
+      getAccessorForField(record, name, pos, state).set(record, o);
     } catch (IllegalAccessException e) {
       throw new AvroRuntimeException(e);
+    } catch (IOException e) {
+      throw new AvroRuntimeException(e);
     }
   }
 
   @Override
   public Object getField(Object record, String name, int position) {
-    if (record instanceof IndexedRecord)
-      return super.getField(record, name, position);
+    return getField(record, name, position, null);
+  }
+  
+  @Override
+  protected Object getField(Object record, String name, int pos, Object state) {
+    if (record instanceof IndexedRecord) {
+      return super.getField(record, name, pos);
+    }
     try {
-      return getField(record.getClass(), name).get(record);
+      return getAccessorForField(record, name, pos, state).get(record);
     } catch (IllegalAccessException e) {
       throw new AvroRuntimeException(e);
     }
   }
+    
+  private FieldAccessor getAccessorForField(Object record, String name,
+      int pos, Object optionalState) {
+    if (optionalState != null) {
+      return ((FieldAccessor[])optionalState)[pos];
+    }
+    return getFieldAccessor(record.getClass(), name);
+  }
 
   @Override
   protected boolean isRecord(Object datum) {
@@ -166,37 +191,83 @@ public class ReflectData extends Specifi
       return super.validate(schema, datum);
     }
   }
+  
+  private static final ConcurrentHashMap<Class<?>, ClassAccessorData> 
+    ACCESSOR_CACHE = new ConcurrentHashMap<Class<?>, ClassAccessorData>();
 
-  private static final Map<Class,Map<String,Field>> FIELD_CACHE =
-    new ConcurrentHashMap<Class,Map<String,Field>>();
+  private static class ClassAccessorData {
+    private final Class<?> clazz;
+    private final Map<String, FieldAccessor> byName =
+        new HashMap<String, FieldAccessor>();
+    private final IdentityHashMap<Schema, FieldAccessor[]> bySchema =
+        new IdentityHashMap<Schema, FieldAccessor[]>();
+        
+    private ClassAccessorData(Class<?> c) {
+      clazz = c;
+      for(Field f : getFields(c, false)) {
+        FieldAccessor accessor = ReflectionUtil.getFieldAccess().getAccessor(f);
+        byName.put(f.getName(), accessor);
+      }
+    }
+    
+    /** 
+     * Return the field accessors as an array, indexed by the field
+     * index of the given schema.
+     */
+    private synchronized FieldAccessor[] getAccessorsFor(Schema schema) {
+      FieldAccessor[] result = bySchema.get(schema);
+      if (result == null) {
+        result = createAccessorsFor(schema);
+        bySchema.put(schema, result);
+      }
+      return result;
+    }
 
-  /** Return the named field of the provided class.  Implementation caches
-   * values, since this is used at runtime to get and set fields. */
-  private static Field getField(Class c, String name) {
-    Map<String,Field> fields = FIELD_CACHE.get(c);
-    if (fields == null) {
-      fields = new ConcurrentHashMap<String,Field>();
-      FIELD_CACHE.put(c, fields);
-    }
-    Field f = fields.get(name);
-    if (f == null) {
-      f = findField(c, name);
-      fields.put(name, f);
+    private FieldAccessor[] createAccessorsFor(Schema schema) {
+      List<Schema.Field> avroFields = schema.getFields();
+      FieldAccessor[] result = new FieldAccessor[avroFields.size()];
+      for(Schema.Field avroField : schema.getFields()) {
+        result[avroField.pos()] = byName.get(avroField.name());
+      }
+      return result;
     }
-    return f;
-  }
 
-  private static Field findField(Class original, String name) {
-    Class c = original;
-    do {
-      try {
-        Field f = c.getDeclaredField(name);
-        f.setAccessible(true);
-        return f;
-      } catch (NoSuchFieldException e) {}
-      c = c.getSuperclass();
-    } while (c != null);
-    throw new AvroRuntimeException("No field named "+name+" in: "+original);
+    private FieldAccessor getAccessorFor(String fieldName) {
+      FieldAccessor result = byName.get(fieldName);
+      if (result == null) {
+        throw new AvroRuntimeException(
+            "No field named " + fieldName + " in: " + clazz);
+      }
+      return result;
+    }
+  }
+  
+  private ClassAccessorData getClassAccessorData(Class<?> c) {
+    ClassAccessorData data = ACCESSOR_CACHE.get(c);
+    if(data == null && !IndexedRecord.class.isAssignableFrom(c)){
+      ClassAccessorData newData = new ClassAccessorData(c);
+      data = ACCESSOR_CACHE.putIfAbsent(c, newData);
+      if (null == data) {
+        data = newData;
+      }
+    }
+    return data;
+  }
+  
+  private FieldAccessor[] getFieldAccessors(Class<?> c, Schema s) {
+    ClassAccessorData data = getClassAccessorData(c);
+    if (data != null) {
+      return data.getAccessorsFor(s);
+    }
+    return null;
+  }
+  
+  private FieldAccessor getFieldAccessor(Class<?> c, String fieldName) {
+    ClassAccessorData data = getClassAccessorData(c);
+    if (data != null) {
+      return data.getAccessorFor(fieldName);
+    }
+    return null;
   }
 
   /** @deprecated  Replaced by {@link SpecificData#CLASS_PROP} */
@@ -209,17 +280,37 @@ public class ReflectData extends Specifi
   @Deprecated
   static final String ELEMENT_PROP = "java-element-class";
 
+  private static final Map<String,Class> CLASS_CACHE =
+               new ConcurrentHashMap<String, Class>();
+
   static Class getClassProp(Schema schema, String prop) {
     String name = schema.getProp(prop);
     if (name == null) return null;
+    Class c = CLASS_CACHE.get(name);
+    if (c != null)
+       return c;
     try {
-      return Class.forName(name);
+      c = Class.forName(name);
+      CLASS_CACHE.put(name, c);
     } catch (ClassNotFoundException e) {
       throw new AvroRuntimeException(e);
     }
+    return c;
   }
 
   private static final Class BYTES_CLASS = new byte[0].getClass();
+  private static final IdentityHashMap<Class, Class> ARRAY_CLASSES;
+  static {
+    ARRAY_CLASSES = new IdentityHashMap<Class, Class>();
+    ARRAY_CLASSES.put(byte.class, byte[].class);
+    ARRAY_CLASSES.put(char.class, char[].class);
+    ARRAY_CLASSES.put(short.class, short[].class);
+    ARRAY_CLASSES.put(int.class, int[].class);
+    ARRAY_CLASSES.put(long.class, long[].class);
+    ARRAY_CLASSES.put(float.class, float[].class);
+    ARRAY_CLASSES.put(double.class, double[].class);
+    ARRAY_CLASSES.put(boolean.class, boolean[].class);
+  }
 
   @Override
   public Class getClass(Schema schema) {
@@ -228,7 +319,13 @@ public class ReflectData extends Specifi
       Class collectionClass = getClassProp(schema, CLASS_PROP);
       if (collectionClass != null)
         return collectionClass;
-      return java.lang.reflect.Array.newInstance(getClass(schema.getElementType()),0).getClass();
+      Class elementClass = getClass(schema.getElementType());
+      if(elementClass.isPrimitive()) {
+        // avoid expensive translation to array type when primitive
+        return ARRAY_CLASSES.get(elementClass);
+      } else {
+        return java.lang.reflect.Array.newInstance(elementClass, 0).getClass();
+      }
     case STRING:
       Class stringClass = getClassProp(schema, CLASS_PROP);
       if (stringClass != null)
@@ -239,13 +336,13 @@ public class ReflectData extends Specifi
       String intClass = schema.getProp(CLASS_PROP);
       if (Byte.class.getName().equals(intClass))  return Byte.TYPE;
       if (Short.class.getName().equals(intClass)) return Short.TYPE;
+      if (Character.class.getName().equals(intClass)) return Character.TYPE;
     default:
       return super.getClass(schema);
     }
   }
 
   @Override
-  @SuppressWarnings(value="unchecked")
   protected Schema createSchema(Type type, Map<String,Schema> names) {
     if (type instanceof GenericArrayType) {                  // generic array
       Type component = ((GenericArrayType)type).getGenericComponentType();
@@ -282,6 +379,10 @@ public class ReflectData extends Specifi
       Schema result = Schema.create(Schema.Type.INT);
       result.addProp(CLASS_PROP, Short.class.getName());
       return result;
+    } else if ((type == Character.class) || (type == Character.TYPE)) {
+        Schema result = Schema.create(Schema.Type.INT);
+        result.addProp(CLASS_PROP, Character.class.getName());
+        return result;
     } else if (type instanceof Class) {                      // Class
       Class<?> c = (Class<?>)type;
       if (c.isPrimitive() ||                                 // primitives
@@ -339,7 +440,7 @@ public class ReflectData extends Specifi
           boolean error = Throwable.class.isAssignableFrom(c);
           schema = Schema.createRecord(name, null /* doc */, space, error);
           names.put(c.getName(), schema);
-          for (Field field : getFields(c))
+          for (Field field : getCachedFields(c))
             if ((field.getModifiers()&(Modifier.TRANSIENT|Modifier.STATIC))==0){
               Schema fieldSchema = createFieldSchema(field, names);
               JsonNode defaultValue = null;
@@ -349,8 +450,9 @@ public class ReflectData extends Specifi
                   defaultValue = NullNode.getInstance();
                 }
               }
-              fields.add(new Schema.Field(field.getName(),
-                  fieldSchema, null /* doc */, defaultValue));
+              Schema.Field recordField = new Schema.Field(field.getName(),
+                      fieldSchema, null /* doc */, defaultValue);
+              fields.add(recordField);
             }
           if (error)                              // add Throwable message
             fields.add(new Schema.Field("detailMessage", THROWABLE_MESSAGE,
@@ -373,7 +475,6 @@ public class ReflectData extends Specifi
 
   // if array element type is a class with a union annotation, note it
   // this is required because we cannot set a property on the union itself 
-  @SuppressWarnings(value="unchecked")
   private void setElement(Schema schema, Type element) {
     if (!(element instanceof Class)) return;
     Class<?> c = (Class<?>)element;
@@ -396,24 +497,37 @@ public class ReflectData extends Specifi
                                             schema));
   }
 
+  private static final Map<Class<?>,Field[]> FIELDS_CACHE =
+    new ConcurrentHashMap<Class<?>,Field[]>();
+  
   // Return of this class and its superclasses to serialize.
-  // Not cached, since this is only used to create schemas, which are cached.
-  private Collection<Field> getFields(Class recordClass) {
+  private static Field[] getCachedFields(Class<?> recordClass) {
+    Field[] fieldsList = FIELDS_CACHE.get(recordClass);
+    if (fieldsList != null)
+      return fieldsList;
+    fieldsList = getFields(recordClass, true);
+    FIELDS_CACHE.put(recordClass, fieldsList);
+    return fieldsList;
+  }
+
+  private static Field[] getFields(Class<?> recordClass, boolean excludeJava) {
+    Field[] fieldsList;
     Map<String,Field> fields = new LinkedHashMap<String,Field>();
-    Class c = recordClass;
+    Class<?> c = recordClass;
     do {
-      if (c.getPackage() != null
+      if (excludeJava && c.getPackage() != null
           && c.getPackage().getName().startsWith("java."))
-        break;                                    // skip java built-in classes
+        break;                                   // skip java built-in classes
       for (Field field : c.getDeclaredFields())
         if ((field.getModifiers() & (Modifier.TRANSIENT|Modifier.STATIC)) == 0)
           if (fields.put(field.getName(), field) != null)
             throw new AvroTypeException(c+" contains two fields named: "+field);
       c = c.getSuperclass();
     } while (c != null);
-    return fields.values();
+    fieldsList = fields.values().toArray(new Field[0]);
+    return fieldsList;
   }
-
+  
   /** Create a schema for a field. */
   protected Schema createFieldSchema(Field field, Map<String, Schema> names) {
     Schema schema = createSchema(field.getGenericType(), names);
@@ -487,7 +601,6 @@ public class ReflectData extends Specifi
       if (err != AvroRemoteException.class) 
         errs.add(getSchema(err, names));
     Schema errors = Schema.createUnion(errs);
-
     return protocol.createMessage(method.getName(), null /* doc */, request, response, errors);
   }
 
@@ -527,4 +640,8 @@ public class ReflectData extends Specifi
     return super.compare(o1, o2, s, equals);
   }
 
+  @Override
+  protected Object getRecordState(Object record, Schema schema) {
+    return getFieldAccessors(record.getClass(), schema);
+  }
 }

Modified: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumReader.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumReader.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumReader.java (original)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumReader.java Tue Apr 30 16:18:26 2013
@@ -18,14 +18,18 @@
 package org.apache.avro.reflect;
 
 import java.io.IOException;
-import java.util.Collection;
-import java.util.ArrayList;
 import java.lang.reflect.Array;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
 
+import org.apache.avro.AvroRuntimeException;
 import org.apache.avro.Schema;
-import org.apache.avro.specific.SpecificDatumReader;
+import org.apache.avro.Schema.Field;
 import org.apache.avro.io.Decoder;
+import org.apache.avro.io.ResolvingDecoder;
+import org.apache.avro.specific.SpecificData;
+import org.apache.avro.specific.SpecificDatumReader;
 
 /**
  * {@link org.apache.avro.io.DatumReader DatumReader} for existing classes via
@@ -63,29 +67,29 @@ public class ReflectDatumReader<T> exten
   }
 
   @Override
-  @SuppressWarnings(value="unchecked")
   protected Object newArray(Object old, int size, Schema schema) {
-    Class collectionClass =
-      ReflectData.getClassProp(schema, ReflectData.CLASS_PROP);
-    Class elementClass =
-      ReflectData.getClassProp(schema, ReflectData.ELEMENT_PROP);
+    Class<?> collectionClass =
+      ReflectData.getClassProp(schema, SpecificData.CLASS_PROP);
+    Class<?> elementClass =
+      ReflectData.getClassProp(schema, SpecificData.ELEMENT_PROP);
 
     if (collectionClass == null && elementClass == null)
       return super.newArray(old, size, schema);   // use specific/generic
 
-    ReflectData data = (ReflectData)getData();
     if (collectionClass != null && !collectionClass.isArray()) {
       if (old instanceof Collection) {
-        ((Collection)old).clear();
+        ((Collection<?>)old).clear();
         return old;
       }
       if (collectionClass.isAssignableFrom(ArrayList.class))
-        return new ArrayList();
-      return data.newInstance(collectionClass, schema);
+        return new ArrayList<Object>();
+      return SpecificData.newInstance(collectionClass, schema);
     }
 
-    if (elementClass == null)
+    if (elementClass == null) {
+      ReflectData data = (ReflectData)getData();
       elementClass = data.getClass(schema.getElementType());
+    }
     return Array.newInstance(elementClass, size);
   }
 
@@ -95,15 +99,70 @@ public class ReflectDatumReader<T> exten
   }
 
   @Override
-  @SuppressWarnings(value="unchecked")
   protected void addToArray(Object array, long pos, Object e) {
+    throw new AvroRuntimeException("reflectDatumReader does not use addToArray");
+  }
+
+  @Override
+  /** Called to read an array instance.  May be overridden for alternate array
+   * representations.*/
+  protected Object readArray(Object old, Schema expected, ResolvingDecoder in)
+      throws IOException {
+    Schema expectedType = expected.getElementType();
+    long l = in.readArrayStart();
+    if (l <= 0) {
+      return newArray(old, 0, expected);
+    }
+    Object array = newArray(old, (int) l, expected);
     if (array instanceof Collection) {
-      ((Collection)array).add(e);
+      @SuppressWarnings("unchecked")
+      Collection<Object> c = (Collection<Object>) array;
+      return readCollection(c, expectedType, l, in);
     } else {
-      Array.set(array, (int)pos, e);
+      return readJavaArray(array, expectedType, l, in);
     }
   }
 
+  private Object readJavaArray(Object array, Schema expectedType, long l,
+      ResolvingDecoder in) throws IOException {
+    Class<?> elementType = array.getClass().getComponentType();
+    if (elementType.isPrimitive()) {
+      return readPrimitiveArray(array, elementType, l, in);
+    } else {
+      return readObjectArray((Object[]) array, expectedType, l, in);
+    }
+  }
+
+  private Object readPrimitiveArray(Object array, Class<?> c, long l, ResolvingDecoder in)
+      throws IOException {
+    return ArrayAccessor.readArray(array, c, l, in);
+  }
+
+  private Object readObjectArray(Object[] array, Schema expectedType, long l,
+      ResolvingDecoder in) throws IOException {
+    int index = 0;
+    do {
+      int limit = index + (int) l;
+      while (index < limit) {
+        Object element = read(null, expectedType, in);
+        array[index] = element;
+        index++;
+      }
+    } while ((l = in.arrayNext()) > 0);
+    return array;
+  }
+
+  private Object readCollection(Collection<Object> c, Schema expectedType,
+      long l, ResolvingDecoder in) throws IOException {
+    do {
+      for (int i = 0; i < l; i++) {
+        Object element = read(null, expectedType, in);
+        c.add(element);
+      }
+    } while ((l = in.arrayNext()) > 0);
+    return c;
+  }
+
   @Override
   protected Object readString(Object old, Decoder in) throws IOException {
     return super.readString(null, in).toString();
@@ -116,7 +175,7 @@ public class ReflectDatumReader<T> exten
   protected Object readBytes(Object old, Schema s, Decoder in)
     throws IOException {
     ByteBuffer bytes = in.readBytes(null);
-    Class c = ReflectData.getClassProp(s, ReflectData.CLASS_PROP);
+    Class<?> c = ReflectData.getClassProp(s, SpecificData.CLASS_PROP);
     if (c != null && c.isArray()) {
       byte[] result = new byte[bytes.remaining()];
       bytes.get(result);
@@ -130,12 +189,27 @@ public class ReflectDatumReader<T> exten
   protected Object readInt(Object old,
                            Schema expected, Decoder in) throws IOException {
     Object value = in.readInt();
-    String intClass = expected.getProp(ReflectData.CLASS_PROP);
+    String intClass = expected.getProp(SpecificData.CLASS_PROP);
     if (Byte.class.getName().equals(intClass))
       value = ((Integer)value).byteValue();
     else if (Short.class.getName().equals(intClass))
       value = ((Integer)value).shortValue();
+    else if (Character.class.getName().equals(intClass))
+        value = ((Character)(char)(int)(Integer)value);
     return value;
   }
 
+  @Override
+  protected void readField(Object record, Field f, Object oldDatum,
+      ResolvingDecoder in, Object state) throws IOException {
+    if (state != null) {
+      FieldAccessor accessor = ((FieldAccessor[]) state)[f.pos()];
+      if (accessor != null && !Schema.Type.UNION.equals(f.schema().getType())
+          && accessor.supportsIO()) {
+        accessor.read(record, in);
+        return;
+      }
+    }
+    super.readField(record, f, oldDatum, in, state);
+  }
 }

Modified: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumWriter.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumWriter.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumWriter.java (original)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumWriter.java Tue Apr 30 16:18:26 2013
@@ -17,14 +17,14 @@
  */
 package org.apache.avro.reflect;
 
-import java.lang.reflect.Array;
 import java.io.IOException;
-import java.util.Iterator;
 import java.util.Collection;
 
+import org.apache.avro.AvroRuntimeException;
 import org.apache.avro.Schema;
-import org.apache.avro.specific.SpecificDatumWriter;
+import org.apache.avro.Schema.Field;
 import org.apache.avro.io.Encoder;
+import org.apache.avro.specific.SpecificDatumWriter;
 
 /**
  * {@link org.apache.avro.io.DatumWriter DatumWriter} for existing classes
@@ -54,29 +54,74 @@ public class ReflectDatumWriter<T> exten
   protected ReflectDatumWriter(ReflectData reflectData) {
     super(reflectData);
   }
-  
-  @Override
-  @SuppressWarnings("unchecked")
-  protected long getArraySize(Object array) {
-    if (array instanceof Collection)
-      return ((Collection)array).size();
-    return Array.getLength(array);
-  }
 
+  /** Called to write a array.  May be overridden for alternate array
+   * representations.*/
   @Override
-  @SuppressWarnings("unchecked")
-  protected Iterator<Object> getArrayElements(final Object array) {
-    if (array instanceof Collection)
-      return ((Collection<Object>)array).iterator();
-    return new Iterator<Object>() {
-      private int i = 0;
-      private final int length = Array.getLength(array);
-      public boolean hasNext() { return i < length; }
-      public Object next() { return Array.get(array, i++); }
-      public void remove() { throw new UnsupportedOperationException(); }
-    };
+  protected void writeArray(Schema schema, Object datum, Encoder out)
+    throws IOException {
+    if (datum instanceof Collection) {
+      super.writeArray(schema, datum, out);
+      return;
+    }
+    Class<?> elementClass = datum.getClass().getComponentType();
+    if (null == elementClass) {
+      // not a Collection or an Array
+      throw new AvroRuntimeException("Array data must be a Collection or Array");
+    } 
+    Schema element = schema.getElementType();
+    if (elementClass.isPrimitive()) {
+      Schema.Type type = element.getType();
+      out.writeArrayStart();
+      switch(type) {
+      case BOOLEAN:
+        if(elementClass.isPrimitive())
+        ArrayAccessor.writeArray((boolean[]) datum, out);
+        break;
+      case DOUBLE:
+        ArrayAccessor.writeArray((double[]) datum, out);
+        break;
+      case FLOAT:
+        ArrayAccessor.writeArray((float[]) datum, out);
+        break;
+      case INT:
+        if(elementClass.equals(int.class)) {
+          ArrayAccessor.writeArray((int[]) datum, out);
+        } else if(elementClass.equals(char.class)) {
+          ArrayAccessor.writeArray((char[]) datum, out);
+        } else if(elementClass.equals(short.class)) {
+          ArrayAccessor.writeArray((short[]) datum, out);
+        } else {
+          arrayError(elementClass, type);
+        }
+        break;
+      case LONG:
+        ArrayAccessor.writeArray((long[]) datum, out);
+        break;
+      default:
+        arrayError(elementClass, type);
+      }
+      out.writeArrayEnd();
+    } else {
+      out.writeArrayStart();
+      writeObjectArray(element, (Object[]) datum, out);
+      out.writeArrayEnd();
+    }
   }
-
+  
+  private void writeObjectArray(Schema element, Object[] data, Encoder out) throws IOException {
+    int size = data.length;
+    out.setItemCount(size);
+    for (int i = 0; i < size; i++) {
+      this.write(element, data[i], out);
+    }
+  }
+    
+  private void arrayError(Class<?> cl, Schema.Type type) {
+    throw new AvroRuntimeException("Error writing array with inner type " +
+      cl + " and avro type: " + type);
+  }
+  
   @Override
   protected void writeBytes(Object datum, Encoder out) throws IOException {
     if (datum instanceof byte[])
@@ -92,6 +137,8 @@ public class ReflectDatumWriter<T> exten
       datum = ((Byte)datum).intValue();
     else if (datum instanceof Short)
       datum = ((Short)datum).intValue();
+    else if (datum instanceof Character)
+        datum = (int)(char)(Character)datum;
     try {
       super.write(schema, datum, out);
     } catch (NullPointerException e) {            // improve error message
@@ -102,4 +149,17 @@ public class ReflectDatumWriter<T> exten
     }
   }
 
+  @Override
+  protected void writeField(Object record, Field f, Encoder out, Object state)
+      throws IOException {
+    if (state != null) {
+      FieldAccessor accessor = ((FieldAccessor[]) state)[f.pos()];
+      if (accessor != null && !Schema.Type.UNION.equals(f.schema().getType())
+          && accessor.supportsIO()) {
+        accessor.write(record, out);
+        return;
+      }
+    }
+    super.writeField(record, f, out, state);
+  }
 }

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectionUtil.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectionUtil.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectionUtil.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectionUtil.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,121 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import org.apache.avro.AvroRuntimeException;
+
+/**
+ * A few utility methods for using @link{java.misc.Unsafe}, mostly for private
+ * use.
+ * 
+ * Use of Unsafe on Android is forbidden, as Android provides only a very
+ * limited functionality for this class compared to the JDK version.
+ * 
+ */
+
+class ReflectionUtil {
+
+  private ReflectionUtil() {
+  }
+
+  private static final FieldAccess FIELD_ACCESS;
+  static {
+    // load only one implementation of FieldAccess
+    // so it is monomorphic and the JIT can inline
+    FieldAccess access = null;
+    try {
+      FieldAccess unsafeAccess = load(
+          "org.apache.avro.reflect.FieldAccessUnsafe", FieldAccess.class);
+      if (validate(unsafeAccess)) {
+        access = unsafeAccess;
+      }
+    } catch (Throwable ignored) {
+    }
+    if (access == null) {
+      try {
+        FieldAccess reflectAccess = load(
+            "org.apache.avro.reflect.FieldAccessReflect", FieldAccess.class);
+        if (validate(reflectAccess)) {
+          access = reflectAccess;
+        }
+      } catch (Throwable oops) {
+        throw new AvroRuntimeException(
+            "Unable to load a functional FieldAccess class!");
+      }
+    }
+    FIELD_ACCESS = access;
+  }
+
+  private static <T> T load(String name, Class<T> type) throws Exception {
+    return ReflectionUtil.class.getClassLoader().loadClass(name)
+        .asSubclass(type).newInstance();
+  }
+
+  public static FieldAccess getFieldAccess() {
+    return FIELD_ACCESS;
+  }
+
+  private static boolean validate(FieldAccess access) throws Exception {
+    return new AccessorTestClass().validate(access);
+  }
+
+  private static final class AccessorTestClass {
+    boolean b = true;
+    byte by = 0xf;
+    char c = 'c';
+    short s = 123;
+    int i = 999;
+    long l = 12345L;
+    float f = 2.2f;
+    double d = 4.4d;
+    Object o = "foo";
+    Integer i2 = 555;
+
+    private boolean validate(FieldAccess access) throws Exception {
+      boolean valid = true;
+      valid &= validField(access, "b", b, false);
+      valid &= validField(access, "by", by, (byte) 0xaf);
+      valid &= validField(access, "c", c, 'C');
+      valid &= validField(access, "s", s, (short) 321);
+      valid &= validField(access, "i", i, 111);
+      valid &= validField(access, "l", l, 54321L);
+      valid &= validField(access, "f", f, 0.2f);
+      valid &= validField(access, "d", d, 0.4d);
+      valid &= validField(access, "o", o, new Object());
+      valid &= validField(access, "i2", i2, -555);
+      return valid;
+    }
+
+    private boolean validField(FieldAccess access, String name,
+        Object original, Object toSet) throws Exception {
+      FieldAccessor a;
+      boolean valid = true;
+      a = accessor(access, name);
+      valid &= original.equals(a.get(this));
+      a.set(this, toSet);
+      valid &= !original.equals(a.get(this));
+      return valid;
+    }
+
+    private FieldAccessor accessor(FieldAccess access, String name)
+        throws Exception {
+      return access.getAccessor(this.getClass().getDeclaredField(name));
+    }
+  }
+
+}

Modified: avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java (original)
+++ avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java Tue Apr 30 16:18:26 2013
@@ -24,29 +24,29 @@ import static org.junit.Assert.assertTru
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.nio.ByteBuffer;
+import java.lang.reflect.Array;
 import java.lang.reflect.Type;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 
 import org.apache.avro.AvroRuntimeException;
 import org.apache.avro.AvroTypeException;
 import org.apache.avro.Protocol;
 import org.apache.avro.Schema;
-import org.codehaus.jackson.node.NullNode;
-
 import org.apache.avro.Schema.Field;
-import org.apache.avro.reflect.TestReflect.SampleRecord.AnotherSampleRecord;
-import org.apache.avro.io.DecoderFactory;
+import org.apache.avro.generic.GenericData;
 import org.apache.avro.io.Decoder;
+import org.apache.avro.io.DecoderFactory;
 import org.apache.avro.io.Encoder;
 import org.apache.avro.io.EncoderFactory;
-import org.apache.avro.generic.GenericData;
-
+import org.apache.avro.reflect.TestReflect.SampleRecord.AnotherSampleRecord;
+import org.codehaus.jackson.node.NullNode;
 import org.junit.Test;
 
 public class TestReflect {
@@ -104,21 +104,21 @@ public class TestReflect {
   }
 
   @Test public void testUnionWithCollection() {
-    Schema s = Schema.parse
+    Schema s = new Schema.Parser().parse
       ("[\"null\", {\"type\":\"array\",\"items\":\"float\"}]");
     GenericData data = ReflectData.get();
     assertEquals(1, data.resolveUnion(s, new ArrayList<Float>()));
   }
 
   @Test public void testUnionWithMap() {
-    Schema s = Schema.parse
+    Schema s = new Schema.Parser().parse
       ("[\"null\", {\"type\":\"map\",\"values\":\"float\"}]");
     GenericData data = ReflectData.get();
     assertEquals(1, data.resolveUnion(s, new HashMap<String,Float>()));
   }
 
   @Test public void testUnionWithBytes() {
-    Schema s = Schema.parse ("[\"null\", \"bytes\"]");
+    Schema s = new Schema.Parser().parse ("[\"null\", \"bytes\"]");
     GenericData data = ReflectData.get();
     assertEquals(1, data.resolveUnion(s, ByteBuffer.wrap(new byte[]{1})));
   }
@@ -134,6 +134,7 @@ public class TestReflect {
       listField.add("foo");
     }
     
+    @Override
     public boolean equals(Object o) {
       if (!(o instanceof R1)) return false;
       R1 that = (R1)o;
@@ -157,7 +158,7 @@ public class TestReflect {
           "{\"type\":\"array\",\"items\":\"string\""
           +",\"java-class\":\"java.util.List\"}");
   }
-
+  
   @Test public void testR1() throws Exception {
     checkReadWrite(new R1());
   }
@@ -167,6 +168,7 @@ public class TestReflect {
     private String[] arrayField;
     private Collection<String> collectionField;
     
+    @Override
     public boolean equals(Object o) {
       if (!(o instanceof R2)) return false;
       R2 that = (R2)o;
@@ -187,6 +189,7 @@ public class TestReflect {
   public static class R3 {
     private int[] intArray;
     
+    @Override
     public boolean equals(Object o) {
       if (!(o instanceof R3)) return false;
       R3 that = (R3)o;
@@ -206,6 +209,7 @@ public class TestReflect {
     public short[] shorts;
     public byte b;
     
+    @Override
     public boolean equals(Object o) {
       if (!(o instanceof R4)) return false;
       R4 that = (R4)o;
@@ -231,6 +235,7 @@ public class TestReflect {
 
   public static class R7 extends R6 {
     public int value;
+    @Override
     public boolean equals(Object o) {
       if (!(o instanceof R7)) return false;
       return this.value == ((R7)o).value;
@@ -238,6 +243,7 @@ public class TestReflect {
   }
   public static class R8 extends R6 {
     public float value;
+    @Override
     public boolean equals(Object o) {
       if (!(o instanceof R8)) return false;
       return this.value == ((R8)o).value;
@@ -247,6 +253,7 @@ public class TestReflect {
   // test arrays of union annotated class
   public static class R9  {
     public R6[] r6s;
+    @Override
     public boolean equals(Object o) {
       if (!(o instanceof R9)) return false;
       return Arrays.equals(this.r6s, ((R9)o).r6s);
@@ -296,7 +303,9 @@ public class TestReflect {
   @Stringable public static class R10 {
     private String text;
     public R10(String text) { this.text = text; }
+    @Override
     public String toString() { return text; }
+    @Override
     public boolean equals(Object o) {
       if (!(o instanceof R10)) return false;
       return this.text.equals(((R10)o).text);
@@ -313,6 +322,7 @@ public class TestReflect {
   // test Nullable annotation on field
   public static class R11 {
     @Nullable private String text;
+    @Override
     public boolean equals(Object o) {
       if (!(o instanceof R11)) return false;
       R11 that = (R11)o;
@@ -364,6 +374,7 @@ public class TestReflect {
   }
 
   // test error
+  @SuppressWarnings("serial")
   public static class E1 extends Exception {}
   public static interface P2 {
     void error() throws E1;
@@ -390,9 +401,9 @@ public class TestReflect {
   }
 
   @Test public void testNoPackage() throws Exception {
-    Class noPackage = Class.forName("NoPackage");
+    Class<?> noPackage = Class.forName("NoPackage");
     Schema s = ReflectData.get().getSchema(noPackage);
-    assertEquals(noPackage.getName(), ReflectData.get().getClassName(s));
+    assertEquals(noPackage.getName(), ReflectData.getClassName(s));
   }
 
   void checkReadWrite(Object object) throws Exception {
@@ -477,10 +488,12 @@ public class TestReflect {
     public int x = 1;
     private int y = 2;
 
+    @Override
     public int hashCode() {
       return x + y;
     }
 
+    @Override
     public boolean equals(Object obj) {
       if (this == obj)
         return true;
@@ -508,12 +521,14 @@ public class TestReflect {
         this.s = new SampleRecord();
       }
 
+      @Override
       public int hashCode() {
         int hash = (a != null ? a.hashCode() : 0);
         hash += (s != null ? s.hashCode() : 0);
         return hash;
       }
 
+      @Override
       public boolean equals(Object other) {
         if (other instanceof AnotherSampleRecord) {
           AnotherSampleRecord o = (AnotherSampleRecord) other;
@@ -592,12 +607,65 @@ public class TestReflect {
     // test that this instance can be written & re-read
     checkBinary(schema, record);
   }
+  
+  @Test
+  public void testPrimitiveArray() throws Exception {
+    testPrimitiveArrays(false);
+  }
+  
+  @Test
+  public void testPrimitiveArrayBlocking() throws Exception {
+    testPrimitiveArrays(true);
+  }
+  
+  private void testPrimitiveArrays(boolean blocking) throws Exception {
+    testPrimitiveArray(boolean.class, blocking);
+    testPrimitiveArray(byte.class, blocking);
+    testPrimitiveArray(short.class, blocking);
+    testPrimitiveArray(char.class, blocking);
+    testPrimitiveArray(int.class, blocking);
+    testPrimitiveArray(long.class, blocking);
+    testPrimitiveArray(float.class, blocking);
+    testPrimitiveArray(double.class, blocking);
+  }
+
+  private void testPrimitiveArray(Class<?> c, boolean blocking) throws Exception {
+    ReflectData data = new ReflectData();
+    Random r = new Random();
+    int size = 200;
+    Object array = Array.newInstance(c, size);
+    Schema s = data.getSchema(array.getClass());
+    for(int i = 0; i < size; i++) {
+      Array.set(array, i, randomFor(c, r));
+    }
+    checkBinary(data, s, array, false, blocking);
+  }
+
+  private Object randomFor(Class<?> c, Random r) {
+    if (c == boolean.class)
+      return r.nextBoolean();
+    if (c == int.class)
+      return r.nextInt();
+    if (c == long.class)
+      return r.nextLong();
+    if (c == byte.class)
+      return (byte)r.nextInt();
+    if (c == float.class)
+      return r.nextFloat();
+    if (c == double.class)
+      return r.nextDouble();
+    if (c == char.class)
+      return (char)r.nextInt();
+    if (c == short.class)
+      return (short)r.nextInt();
+    return null;
+  }
 
   /** Test union of null and an array. */
   @Test
   public void testNullArray() throws Exception {
     String json = "[{\"type\":\"array\", \"items\": \"long\"}, \"null\"]";
-    Schema schema = Schema.parse(json);
+    Schema schema = new Schema.Parser().parse(json);
     checkBinary(schema, null);
   }
 
@@ -610,8 +678,9 @@ public class TestReflect {
     checkStringable(java.io.File.class, "foo.bar");
   }
 
+  @SuppressWarnings({ "unchecked", "rawtypes" })
   public void checkStringable(Class c, String value) throws Exception {
-    ReflectData data = new ReflectData().get();
+    ReflectData data = new ReflectData();
     Schema schema = data.getSchema(c);
     assertEquals
       ("{\"type\":\"string\",\"java-class\":\""+c.getName()+"\"}",
@@ -659,17 +728,26 @@ public class TestReflect {
   }
 
   public static void checkBinary(ReflectData reflectData, Schema schema,
-                                 Object datum, boolean equals)
-    throws IOException {
+      Object datum, boolean equals) throws IOException {
+    checkBinary(reflectData, schema, datum, equals, false);
+  }
+  
+  private static void checkBinary(ReflectData reflectData, Schema schema,
+      Object datum, boolean equals, boolean blocking) throws IOException {
     ReflectDatumWriter<Object> writer = new ReflectDatumWriter<Object>(schema);
     ByteArrayOutputStream out = new ByteArrayOutputStream();
+    if (!blocking) {
+      writer.write(datum, EncoderFactory.get().directBinaryEncoder(out, null));
+    } else {
+      writer.write(datum, new EncoderFactory().configureBlockSize(64)
+          .blockingBinaryEncoder(out, null));
+    }
     writer.write(datum, EncoderFactory.get().directBinaryEncoder(out, null));
     byte[] data = out.toByteArray();
 
     ReflectDatumReader<Object> reader = new ReflectDatumReader<Object>(schema);
-    Object decoded =
-      reader.read(null, DecoderFactory.get().binaryDecoder(
-          data, null));
+    Object decoded = reader.read(null,
+        DecoderFactory.get().binaryDecoder(data, null));
 
     assertEquals(0, reflectData.compare(datum, decoded, schema, equals));
   }
@@ -690,5 +768,4 @@ public class TestReflect {
     }
   }
 
-
 }

Added: avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectionUtil.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectionUtil.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectionUtil.java (added)
+++ avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectionUtil.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,79 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.junit.Test;
+
+public class TestReflectionUtil {
+
+  @Test
+  public void testUnsafeUtil() {
+    new Tester().checkUnsafe();
+  }
+
+  @Test
+  public void testUnsafeWhenNotExists() throws Exception {
+    ClassLoader cl = new NoUnsafe();
+    Class<?> testerClass = cl.loadClass(Tester.class.getName());
+    testerClass.getDeclaredMethod("checkUnsafe").invoke(testerClass.newInstance());
+  }
+  
+  public static final class Tester {
+    public Tester() {}
+    public void checkUnsafe() {
+      ReflectionUtil.getFieldAccess();
+    }
+    
+  }
+
+  private static final class NoUnsafe extends ClassLoader {
+    private ClassLoader parent = TestReflectionUtil.class.getClassLoader();
+
+    @Override
+    public java.lang.Class<?> loadClass(String name)
+        throws ClassNotFoundException {
+      Class<?> clazz = findLoadedClass(name);
+      if (clazz != null) {
+        return clazz;
+      }
+      if ("sun.misc.Unsafe".equals(name)) {
+        throw new ClassNotFoundException(name);
+      }
+      if (!name.startsWith("org.apache.avro.")) {
+        return parent.loadClass(name);
+      }
+
+      InputStream data = parent.getResourceAsStream(name.replace('.', '/')
+          + ".class");
+      byte[] buf = new byte[10240]; // big enough, too lazy to loop
+      int size;
+      try {
+        size = data.read(buf);
+      } catch (IOException e) {
+        throw new ClassNotFoundException();
+      }
+      clazz = defineClass(name, buf, 0, size);
+      resolveClass(clazz);
+      return clazz;
+    }
+
+  }
+}