This is an automated email from the ASF dual-hosted git repository.

wesm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/master by this push:
     new f69e9db  ARROW-633/634: [Java] Add FixedSizeBinary support in Java and 
integration tests (Updated)
f69e9db is described below

commit f69e9dba1c610e2bce63b2b1b1cacdd7b5cd4000
Author: Jingyuan Wang <[email protected]>
AuthorDate: Wed Feb 7 17:35:24 2018 -0500

    ARROW-633/634: [Java] Add FixedSizeBinary support in Java and integration 
tests (Updated)
    
    The original PR is at https://github.com/apache/arrow/pull/1012. Due to the 
major refactoring last year, changes are big. So I created this separate PR for 
easier to review and will cancel the other one later.
    
    Changes include:
    * Arrow-633: [Java] Add support for FixedWidthBinary type
    * Arrow-634: Add integration tests for FixedSizeBinary
    
    Author: Jingyuan Wang <[email protected]>
    Author: jingyuan <[email protected]>
    
    Closes #1492 from alphalfalfa/updated-arrow-633-634 and squashes the 
following commits:
    
    c994a19f [jingyuan] create data buffer layout for FixedSizeBinary directly 
instead of using dataBuffer() method and some other changes based on PR comments
    873c5b2e [jingyuan] do not pass scale or byteWidth into 
writeValueToGenerator in JsonFileWriter.java
    4fbb67dc [jingyuan] put arbitrary values for FixSizeBinary nulls
    0c0015dc [Jingyuan Wang] add a new line
    a8a95530 [Jingyuan Wang] Pass byteWidth info for FixedSizeBinary type in 
JsonFileReader
    1a64c5cb [Jingyuan Wang] expand get() method inside getObject() method to 
remove duplicate check of isSet()
    071fb252 [Jingyuan Wang] ARROW-633/634: Add FixedSizeBinary support in Java 
and integration tests
---
 integration/integration_test.py                    |  62 +++-
 java/vector/src/main/codegen/data/ArrowTypes.tdd   |   5 +
 .../src/main/codegen/data/ValueVectorTypes.tdd     |  15 +
 .../main/codegen/templates/HolderReaderImpl.java   |   4 +
 .../apache/arrow/vector/BaseFixedWidthVector.java  |   4 +-
 .../java/org/apache/arrow/vector/BitVector.java    |   2 +-
 .../java/org/apache/arrow/vector/BufferLayout.java |   4 +-
 .../apache/arrow/vector/FixedSizeBinaryVector.java | 404 +++++++++++++++++++++
 .../java/org/apache/arrow/vector/TypeLayout.java   |   6 +
 .../arrow/vector/dictionary/DictionaryEncoder.java |   2 +-
 .../apache/arrow/vector/ipc/JsonFileReader.java    |  23 ++
 .../apache/arrow/vector/ipc/JsonFileWriter.java    |  17 +-
 .../java/org/apache/arrow/vector/types/Types.java  |  19 +
 .../org/apache/arrow/vector/util/Validator.java    |   2 +-
 .../arrow/vector/TestFixedSizeBinaryVector.java    | 261 +++++++++++++
 .../vector/complex/writer/TestComplexWriter.java   |  51 ++-
 .../org/apache/arrow/vector/ipc/TestArrowFile.java |  59 +++
 .../apache/arrow/vector/types/pojo/TestSchema.java |   4 +-
 18 files changed, 926 insertions(+), 18 deletions(-)

diff --git a/integration/integration_test.py b/integration/integration_test.py
index 79b098a..301017c 100644
--- a/integration/integration_test.py
+++ b/integration/integration_test.py
@@ -463,6 +463,46 @@ class BinaryType(PrimitiveType):
         return self.column_class(name, size, is_valid, values)
 
 
+class FixedSizeBinaryType(PrimitiveType):
+
+    def __init__(self, name, byte_width, nullable=True):
+        super(FixedSizeBinaryType, self).__init__(name, nullable=nullable)
+        self.byte_width = byte_width
+
+    @property
+    def numpy_type(self):
+        return object
+
+    @property
+    def column_class(self):
+        return FixedSizeBinaryColumn
+
+    def _get_type(self):
+        return OrderedDict([('name', 'fixedsizebinary'), ('byteWidth', 
self.byte_width)])
+
+    def _get_type_layout(self):
+        return OrderedDict([
+            ('vectors',
+             [OrderedDict([('type', 'VALIDITY'),
+                           ('typeBitWidth', 1)]),
+              OrderedDict([('type', 'DATA'),
+                           ('typeBitWidth', self.byte_width)])])])
+
+    def generate_column(self, size, name=None):
+        is_valid = self._make_is_valid(size)
+        values = []
+
+        for i in range(size):
+            draw = (np.random.randint(0, 255, size=self.byte_width)
+                    .astype(np.uint8)
+                    .tostring())
+            values.append(draw)
+
+        if name is None:
+            name = self.name
+        return self.column_class(name, size, is_valid, values)
+
+
 class StringType(BinaryType):
 
     @property
@@ -525,6 +565,22 @@ class BinaryColumn(PrimitiveColumn):
         ]
 
 
+class FixedSizeBinaryColumn(PrimitiveColumn):
+
+    def _encode_value(self, x):
+        return ''.join('{:02x}'.format(c).upper() for c in x)
+
+    def _get_buffers(self):
+        data = []
+        for i, v in enumerate(self.values):
+            data.append(self._encode_value(v))
+
+        return [
+            ('VALIDITY', [int(x) for x in self.is_valid]),
+            ('DATA', data)
+        ]
+
+
 class StringColumn(BinaryColumn):
 
     def _encode_value(self, x):
@@ -719,6 +775,9 @@ def get_field(name, type_, nullable=True):
         return BinaryType(name, nullable=nullable)
     elif type_ == 'utf8':
         return StringType(name, nullable=nullable)
+    elif type_.startswith('fixedsizebinary_'):
+        byte_width = int(type_.split('_')[1])
+        return FixedSizeBinaryType(name, byte_width=byte_width, 
nullable=nullable)
 
     dtype = np.dtype(type_)
 
@@ -751,7 +810,8 @@ def _generate_file(name, fields, batch_sizes, 
dictionaries=None):
 def generate_primitive_case(batch_sizes, name='primitive'):
     types = ['bool', 'int8', 'int16', 'int32', 'int64',
              'uint8', 'uint16', 'uint32', 'uint64',
-             'float32', 'float64', 'binary', 'utf8']
+             'float32', 'float64', 'binary', 'utf8',
+             'fixedsizebinary_19', 'fixedsizebinary_120']
 
     fields = []
 
diff --git a/java/vector/src/main/codegen/data/ArrowTypes.tdd 
b/java/vector/src/main/codegen/data/ArrowTypes.tdd
index ce92c13..63b193f 100644
--- a/java/vector/src/main/codegen/data/ArrowTypes.tdd
+++ b/java/vector/src/main/codegen/data/ArrowTypes.tdd
@@ -58,6 +58,11 @@
       complex: false
     },
     {
+      name: "FixedSizeBinary",
+      fields: [{name: "byteWidth", type: int}],
+      complex: false
+    }
+    {
       name: "Bool",
       fields: [],
       complex: false
diff --git a/java/vector/src/main/codegen/data/ValueVectorTypes.tdd 
b/java/vector/src/main/codegen/data/ValueVectorTypes.tdd
index 970d887..f294a8e 100644
--- a/java/vector/src/main/codegen/data/ValueVectorTypes.tdd
+++ b/java/vector/src/main/codegen/data/ValueVectorTypes.tdd
@@ -123,6 +123,21 @@
       ]
     },
     {
+      major: "Fixed",
+      width: -1,
+      javaType: "byte[]",
+      boxedType: "ArrowBuf",
+      minor: [
+        {
+          class: "FixedSizeBinary",
+          typeParams: [ {name: "byteWidth", type: "int"} ],
+          arrowType: 
"org.apache.arrow.vector.types.pojo.ArrowType.FixedSizeBinary",
+          friendlyType: "byte[]",
+          fields: [{name: "buffer", type: "ArrowBuf"}],
+        }
+      ]
+    },
+    {
       major: "VarLen",
       width: 4,
       javaType: "int",
diff --git a/java/vector/src/main/codegen/templates/HolderReaderImpl.java 
b/java/vector/src/main/codegen/templates/HolderReaderImpl.java
index c2aa837..f1c10d1 100644
--- a/java/vector/src/main/codegen/templates/HolderReaderImpl.java
+++ b/java/vector/src/main/codegen/templates/HolderReaderImpl.java
@@ -128,6 +128,10 @@ public class ${holderMode}${name}HolderReaderImpl extends 
AbstractFieldReader {
     holder.buffer.getBytes(holder.start, bytes, 0, ${type.width});
     ${friendlyType} value = new BigDecimal(new BigInteger(bytes), 
holder.scale);
     return value;
+  <#elseif minor.class == "FixedSizeBinary">
+    byte[] value = new byte [holder.byteWidth];
+    holder.buffer.getBytes(0, value, 0, holder.byteWidth);
+    return value;
   <#else>
     ${friendlyType} value = new ${friendlyType}(this.holder.value);
     return value;
diff --git 
a/java/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java 
b/java/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java
index 702db9f..cbc56fe 100644
--- 
a/java/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java
+++ 
b/java/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java
@@ -42,7 +42,7 @@ import org.apache.arrow.vector.util.TransferPair;
  */
 public abstract class BaseFixedWidthVector extends BaseValueVector
         implements FixedWidthVector, FieldVector, VectorDefinitionSetter {
-  private final byte typeWidth;
+  private final int typeWidth;
 
   protected int valueAllocationSizeInBytes;
   protected int validityAllocationSizeInBytes;
@@ -54,7 +54,7 @@ public abstract class BaseFixedWidthVector extends 
BaseValueVector
   protected int valueCount;
 
   public BaseFixedWidthVector(final String name, final BufferAllocator 
allocator,
-                                      FieldType fieldType, final byte 
typeWidth) {
+                                      FieldType fieldType, final int 
typeWidth) {
     super(name, allocator);
     this.typeWidth = typeWidth;
     field = new Field(name, fieldType, null);
diff --git a/java/vector/src/main/java/org/apache/arrow/vector/BitVector.java 
b/java/vector/src/main/java/org/apache/arrow/vector/BitVector.java
index 3887da4..b7b7b99 100644
--- a/java/vector/src/main/java/org/apache/arrow/vector/BitVector.java
+++ b/java/vector/src/main/java/org/apache/arrow/vector/BitVector.java
@@ -58,7 +58,7 @@ public class BitVector extends BaseFixedWidthVector {
    * @param allocator allocator for memory management.
    */
   public BitVector(String name, FieldType fieldType, BufferAllocator 
allocator) {
-    super(name, allocator, fieldType, (byte) 0);
+    super(name, allocator, fieldType, 0);
     reader = new BitReaderImpl(BitVector.this);
   }
 
diff --git 
a/java/vector/src/main/java/org/apache/arrow/vector/BufferLayout.java 
b/java/vector/src/main/java/org/apache/arrow/vector/BufferLayout.java
index f6529d8..0faab7b 100644
--- a/java/vector/src/main/java/org/apache/arrow/vector/BufferLayout.java
+++ b/java/vector/src/main/java/org/apache/arrow/vector/BufferLayout.java
@@ -70,7 +70,7 @@ public class BufferLayout {
       case 128:
         return VALUES_128;
       default:
-        throw new IllegalArgumentException("only 8, 16, 32, or 64 bits 
supported");
+        throw new IllegalArgumentException("only 8, 16, 32, 64, or 128 bits 
supported");
     }
   }
 
@@ -90,7 +90,7 @@ public class BufferLayout {
 
   private final BufferType type;
 
-  private BufferLayout(BufferType type, int typeBitWidth) {
+  BufferLayout(BufferType type, int typeBitWidth) {
     super();
     this.type = Preconditions.checkNotNull(type);
     this.typeBitWidth = (short) typeBitWidth;
diff --git 
a/java/vector/src/main/java/org/apache/arrow/vector/FixedSizeBinaryVector.java 
b/java/vector/src/main/java/org/apache/arrow/vector/FixedSizeBinaryVector.java
new file mode 100644
index 0000000..232e656
--- /dev/null
+++ 
b/java/vector/src/main/java/org/apache/arrow/vector/FixedSizeBinaryVector.java
@@ -0,0 +1,404 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.arrow.vector;
+
+import io.netty.buffer.ArrowBuf;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.vector.complex.impl.FixedSizeBinaryReaderImpl;
+import org.apache.arrow.vector.complex.reader.FieldReader;
+import org.apache.arrow.vector.holders.FixedSizeBinaryHolder;
+import org.apache.arrow.vector.holders.NullableFixedSizeBinaryHolder;
+import org.apache.arrow.vector.types.Types;
+import org.apache.arrow.vector.types.pojo.ArrowType.FixedSizeBinary;
+import org.apache.arrow.vector.types.pojo.FieldType;
+import org.apache.arrow.vector.util.TransferPair;
+
+
+/**
+ * FixedSizeBinaryVector implements a fixed width vector of
+ * binary values which could be null. A validity buffer (bit vector) is
+ * maintained to track which elements in the vector are null.
+ */
+public class FixedSizeBinaryVector extends BaseFixedWidthVector {
+  private final int byteWidth;
+  private final FieldReader reader;
+
+  /**
+   * Instantiate a FixedSizeBinaryVector. This doesn't allocate any memory for
+   * the data in vector.
+   *
+   * @param name      name of the vector
+   * @param allocator allocator for memory management.
+   * @param byteWidth byte width of the binary values
+   */
+  public FixedSizeBinaryVector(String name, BufferAllocator allocator, int 
byteWidth) {
+    this(name, FieldType.nullable(new FixedSizeBinary(byteWidth)), allocator);
+  }
+
+  /**
+   * Instantiate a FixedSizeBinaryVector. This doesn't allocate any memory for
+   * the data in vector.
+   *
+   * @param name      name of the vector
+   * @param fieldType type of Field materialized by this vector
+   * @param allocator allocator for memory management.
+   */
+  public FixedSizeBinaryVector(String name, FieldType fieldType, 
BufferAllocator allocator) {
+    super(name, allocator, fieldType, ((FixedSizeBinary) 
fieldType.getType()).getByteWidth());
+    reader = new FixedSizeBinaryReaderImpl(FixedSizeBinaryVector.this);
+    byteWidth = ((FixedSizeBinary) fieldType.getType()).getByteWidth();
+  }
+
+  /**
+   * Get a reader that supports reading values from this vector
+   *
+   * @return Field Reader for this vector
+   */
+  @Override
+  public FieldReader getReader() {
+    return reader;
+  }
+
+  /**
+   * Get minor type for this vector. The vector holds values belonging
+   * to a particular type.
+   *
+   * @return {@link org.apache.arrow.vector.types.Types.MinorType}
+   */
+  @Override
+  public Types.MinorType getMinorType() {
+    return Types.MinorType.FIXEDSIZEBINARY;
+  }
+
+
+  /******************************************************************
+   *                                                                *
+   *          vector value retrieval methods                        *
+   *                                                                *
+   ******************************************************************/
+
+  /**
+   * Get the element at the given index from the vector.
+   *
+   * @param index position of element
+   * @return element at given index
+   */
+  public byte[] get(int index) {
+    assert index >= 0;
+    if (isSet(index) == 0) {
+      throw new IllegalStateException("Value at index is null");
+    }
+    final byte[] dst = new byte[byteWidth];
+    valueBuffer.getBytes(index * byteWidth, dst, 0, byteWidth);
+    return dst;
+  }
+
+  /**
+   * Get the element at the given index from the vector and
+   * sets the state in holder. If element at given index
+   * is null, holder.isSet will be zero.
+   *
+   * @param index position of element
+   * @param holder nullable holder to carry the buffer
+   */
+  public void get(int index, NullableFixedSizeBinaryHolder holder) {
+    assert index >= 0;
+    if (isSet(index) == 0) {
+      holder.isSet = 0;
+      return;
+    }
+    holder.isSet = 1;
+    holder.buffer = valueBuffer.slice(index * byteWidth, byteWidth);
+  }
+
+  /**
+   * Same as {@link #get(int)}.
+   *
+   * @param index position of element
+   * @return element at given index
+   */
+  @Override
+  public byte[] getObject(int index) {
+    assert index >= 0;
+    if (isSet(index) == 0) {
+      return null;
+    } else {
+      final byte[] dst = new byte[byteWidth];
+      valueBuffer.getBytes(index * byteWidth, dst, 0, byteWidth);
+      return dst;
+    }
+  }
+
+  /**
+   * Copy a cell value from a particular index in source vector to a particular
+   * position in this vector
+   *
+   * @param fromIndex position to copy from in source vector
+   * @param thisIndex position to copy to in this vector
+   * @param from      source vector
+   */
+  public void copyFrom(int fromIndex, int thisIndex, FixedSizeBinaryVector 
from) {
+    BitVectorHelper.setValidityBit(validityBuffer, thisIndex, 
from.isSet(fromIndex));
+    from.valueBuffer.getBytes(fromIndex * byteWidth, valueBuffer,
+        thisIndex * byteWidth, byteWidth);
+  }
+
+  /**
+   * Same as {@link #copyFrom(int, int, FixedSizeBinaryVector)} except that
+   * it handles the case when the capacity of the vector needs to be expanded
+   * before copy.
+   *
+   * @param fromIndex position to copy from in source vector
+   * @param thisIndex position to copy to in this vector
+   * @param from      source vector
+   */
+  public void copyFromSafe(int fromIndex, int thisIndex, FixedSizeBinaryVector 
from) {
+    handleSafe(thisIndex);
+    copyFrom(fromIndex, thisIndex, from);
+  }
+
+  public int getByteWidth() {
+    return byteWidth;
+  }
+
+
+  /******************************************************************
+   *                                                                *
+   *          vector value setter methods                           *
+   *                                                                *
+   ******************************************************************/
+
+  public void set(int index, byte[] value) {
+    assert index >= 0;
+    assert byteWidth <= value.length;
+    BitVectorHelper.setValidityBitToOne(validityBuffer, index);
+    valueBuffer.setBytes(index * byteWidth, value, 0, byteWidth);
+  }
+
+  public void setSafe(int index, byte[] value) {
+    handleSafe(index);
+    set(index, value);
+  }
+
+  public void set(int index, int isSet, byte[] value) {
+    if (isSet > 0) {
+      set(index, value);
+    } else {
+      BitVectorHelper.setValidityBit(validityBuffer, index, 0);
+    }
+  }
+
+  public void setSafe(int index, int isSet, byte[] value) {
+    handleSafe(index);
+    set(index, isSet, value);
+  }
+
+  /**
+   * Set the element at the given index to the given value.
+   *
+   * @param index  position of element
+   * @param buffer ArrowBuf containing binary value.
+   */
+  public void set(int index, ArrowBuf buffer) {
+    assert index >= 0;
+    assert byteWidth <= buffer.capacity();
+    BitVectorHelper.setValidityBitToOne(validityBuffer, index);
+    valueBuffer.setBytes(index * byteWidth, buffer, 0, byteWidth);
+  }
+
+  /**
+   * Same as {@link #set(int, ArrowBuf)} except that it handles the
+   * case when index is greater than or equal to existing
+   * value capacity {@link #getValueCapacity()}.
+   *
+   * @param index  position of element
+   * @param buffer ArrowBuf containing binary value.
+   */
+  public void setSafe(int index, ArrowBuf buffer) {
+    handleSafe(index);
+    set(index, buffer);
+  }
+
+  /**
+   * Set the element at the given index to the given value.
+   *
+   * @param index  position of element
+   * @param buffer ArrowBuf containing binary value.
+   */
+  public void set(int index, int isSet, ArrowBuf buffer) {
+    if (isSet > 0) {
+      set(index, buffer);
+    } else {
+      BitVectorHelper.setValidityBit(validityBuffer, index, 0);
+    }
+  }
+
+  /**
+   * Same as {@link #set(int, ArrowBuf)} except that it handles the
+   * case when index is greater than or equal to existing
+   * value capacity {@link #getValueCapacity()}.
+   *
+   * @param index  position of element
+   * @param buffer ArrowBuf containing binary value.
+   */
+  public void setSafe(int index, int isSet, ArrowBuf buffer) {
+    handleSafe(index);
+    set(index, isSet, buffer);
+  }
+
+  /**
+   * Set the variable length element at the specified index to the data
+   * buffer supplied in the holder
+   *
+   * @param index   position of the element to set
+   * @param holder  holder that carries data buffer.
+   */
+  public void set(int index, FixedSizeBinaryHolder holder) {
+    assert holder.byteWidth == byteWidth;
+    set(index, holder.buffer);
+  }
+
+  /**
+   * Same as {@link #set(int, FixedSizeBinaryHolder)} except that it handles 
the
+   * case where index and length of new element are beyond the existing
+   * capacity of the vector.
+   *
+   * @param index   position of the element to set
+   * @param holder  holder that carries data buffer.
+   */
+  public void setSafe(int index, FixedSizeBinaryHolder holder) {
+    handleSafe(index);
+    set(index, holder);
+  }
+
+  /**
+   * Set the variable length element at the specified index to the data
+   * buffer supplied in the holder
+   *
+   * @param index   position of the element to set
+   * @param holder  holder that carries data buffer.
+   */
+  public void set(int index, NullableFixedSizeBinaryHolder holder) {
+    assert holder.byteWidth == byteWidth;
+    if (holder.isSet < 0) {
+      throw new IllegalArgumentException("holder has a negative isSet value");
+    } else if (holder.isSet > 0) {
+      set(index, holder.buffer);
+    } else {
+      BitVectorHelper.setValidityBit(validityBuffer, index, 0);
+    }
+  }
+
+  /**
+   * Same as {@link #set(int, NullableFixedSizeBinaryHolder)} except that it 
handles the
+   * case where index and length of new element are beyond the existing
+   * capacity of the vector.
+   *
+   * @param index   position of the element to set
+   * @param holder  holder that carries data buffer.
+   */
+  public void setSafe(int index, NullableFixedSizeBinaryHolder holder) {
+    handleSafe(index);
+    set(index, holder);
+  }
+
+  public void setNull(int index) {
+    handleSafe(index);
+    BitVectorHelper.setValidityBit(validityBuffer, index, 0);
+  }
+
+  /**
+   * Given a data buffer, get the value stored at a particular position
+   * in the vector.
+   *
+   * This method should not be used externally.
+   *
+   * @param buffer data buffer
+   * @param index position of the element.
+   * @return value stored at the index.
+   */
+  public static byte[] get(final ArrowBuf buffer, final int index, final int 
byteWidth) {
+    final byte[] dst = new byte[byteWidth];
+    buffer.getBytes(index * byteWidth, dst, 0, byteWidth);
+    return dst;
+  }
+
+  /******************************************************************
+   *                                                                *
+   *                      vector transfer                           *
+   *                                                                *
+   ******************************************************************/
+
+
+  /**
+   * Construct a TransferPair comprising of this and and a target vector of
+   * the same type.
+   *
+   * @param ref       name of the target vector
+   * @param allocator allocator for the target vector
+   * @return {@link TransferPair}
+   */
+  @Override
+  public TransferPair getTransferPair(String ref, BufferAllocator allocator) {
+    return new TransferImpl(ref, allocator);
+  }
+
+  /**
+   * Construct a TransferPair with a desired target vector of the same type.
+   *
+   * @param to target vector
+   * @return {@link TransferPair}
+   */
+  @Override
+  public TransferPair makeTransferPair(ValueVector to) {
+    return new TransferImpl((FixedSizeBinaryVector) to);
+  }
+
+  private class TransferImpl implements TransferPair {
+    FixedSizeBinaryVector to;
+
+    public TransferImpl(String ref, BufferAllocator allocator) {
+      to = new FixedSizeBinaryVector(ref, allocator, 
FixedSizeBinaryVector.this.byteWidth);
+    }
+
+    public TransferImpl(FixedSizeBinaryVector to) {
+      this.to = to;
+    }
+
+    @Override
+    public FixedSizeBinaryVector getTo() {
+      return to;
+    }
+
+    @Override
+    public void transfer() {
+      transferTo(to);
+    }
+
+    @Override
+    public void splitAndTransfer(int startIndex, int length) {
+      splitAndTransferTo(startIndex, length, to);
+    }
+
+    @Override
+    public void copyValueSafe(int fromIndex, int toIndex) {
+      to.copyFromSafe(fromIndex, toIndex, FixedSizeBinaryVector.this);
+    }
+  }
+}
diff --git a/java/vector/src/main/java/org/apache/arrow/vector/TypeLayout.java 
b/java/vector/src/main/java/org/apache/arrow/vector/TypeLayout.java
index d6f32b4..4c05b97 100644
--- a/java/vector/src/main/java/org/apache/arrow/vector/TypeLayout.java
+++ b/java/vector/src/main/java/org/apache/arrow/vector/TypeLayout.java
@@ -31,6 +31,7 @@ import org.apache.arrow.vector.types.pojo.ArrowType.Binary;
 import org.apache.arrow.vector.types.pojo.ArrowType.Bool;
 import org.apache.arrow.vector.types.pojo.ArrowType.Date;
 import org.apache.arrow.vector.types.pojo.ArrowType.Decimal;
+import org.apache.arrow.vector.types.pojo.ArrowType.FixedSizeBinary;
 import org.apache.arrow.vector.types.pojo.ArrowType.FixedSizeList;
 import org.apache.arrow.vector.types.pojo.ArrowType.FloatingPoint;
 import org.apache.arrow.vector.types.pojo.ArrowType.Int;
@@ -137,6 +138,11 @@ public class TypeLayout {
       }
 
       @Override
+      public TypeLayout visit(FixedSizeBinary type) {
+        return newFixedWidthTypeLayout(new BufferLayout(BufferType.DATA, 
type.getByteWidth() * 8));
+      }
+
+      @Override
       public TypeLayout visit(Bool type) {
         return newFixedWidthTypeLayout(BufferLayout.booleanVector());
       }
diff --git 
a/java/vector/src/main/java/org/apache/arrow/vector/dictionary/DictionaryEncoder.java
 
b/java/vector/src/main/java/org/apache/arrow/vector/dictionary/DictionaryEncoder.java
index 762a442..08df7b6 100644
--- 
a/java/vector/src/main/java/org/apache/arrow/vector/dictionary/DictionaryEncoder.java
+++ 
b/java/vector/src/main/java/org/apache/arrow/vector/dictionary/DictionaryEncoder.java
@@ -136,7 +136,7 @@ public class DictionaryEncoder {
   private static void validateType(MinorType type) {
     // byte arrays don't work as keys in our dictionary map - we could wrap 
them with something to
     // implement equals and hashcode if we want that functionality
-    if (type == MinorType.VARBINARY || type == MinorType.LIST || type == 
MinorType.MAP || type == MinorType.UNION) {
+    if (type == MinorType.VARBINARY || type == MinorType.FIXEDSIZEBINARY || 
type == MinorType.LIST || type == MinorType.MAP || type == MinorType.UNION) {
       throw new IllegalArgumentException("Dictionary encoding for complex 
types not implemented: type " + type);
     }
   }
diff --git 
a/java/vector/src/main/java/org/apache/arrow/vector/ipc/JsonFileReader.java 
b/java/vector/src/main/java/org/apache/arrow/vector/ipc/JsonFileReader.java
index d0a9b9e..8995716 100644
--- a/java/vector/src/main/java/org/apache/arrow/vector/ipc/JsonFileReader.java
+++ b/java/vector/src/main/java/org/apache/arrow/vector/ipc/JsonFileReader.java
@@ -329,6 +329,26 @@ public class JsonFileReader implements AutoCloseable, 
DictionaryProvider {
       }
     };
 
+    BufferReader FIXEDSIZEBINARY = new BufferReader() {
+      @Override
+      protected ArrowBuf read(BufferAllocator allocator, int count) throws 
IOException {
+        ArrayList<byte[]> values = Lists.newArrayList();
+        for (int i = 0; i < count; i++) {
+          parser.nextToken();
+          final byte[] value = decodeHexSafe(parser.readValueAs(String.class));
+          values.add(value);
+        }
+
+        int byteWidth = count > 0 ? values.get(0).length : 0;
+        ArrowBuf buf = allocator.buffer(byteWidth * count);
+        for (byte[] value : values) {
+          buf.writeBytes(value);
+        }
+
+        return buf;
+      }
+    };
+
     BufferReader VARCHAR = new BufferReader() {
       @Override
       protected ArrowBuf read(BufferAllocator allocator, int count) throws 
IOException {
@@ -428,6 +448,9 @@ public class JsonFileReader implements AutoCloseable, 
DictionaryProvider {
         case DECIMAL:
           reader = helper.DECIMAL;
           break;
+        case FIXEDSIZEBINARY:
+          reader = helper.FIXEDSIZEBINARY;
+          break;
         case VARCHAR:
           reader = helper.VARCHAR;
           break;
diff --git 
a/java/vector/src/main/java/org/apache/arrow/vector/ipc/JsonFileWriter.java 
b/java/vector/src/main/java/org/apache/arrow/vector/ipc/JsonFileWriter.java
index 6eb76a7..ad56d57 100644
--- a/java/vector/src/main/java/org/apache/arrow/vector/ipc/JsonFileWriter.java
+++ b/java/vector/src/main/java/org/apache/arrow/vector/ipc/JsonFileWriter.java
@@ -163,8 +163,7 @@ public class JsonFileWriter implements AutoCloseable {
       generator.writeObjectField("name", field.getName());
       int valueCount = vector.getValueCount();
       generator.writeObjectField("count", valueCount);
-      final int scale = (vector instanceof DecimalVector) ?
-                            ((DecimalVector) vector).getScale() : 0;
+
       for (int v = 0; v < vectorTypes.size(); v++) {
         BufferType bufferType = vectorTypes.get(v);
         ArrowBuf vectorBuffer = vectorBuffers.get(v);
@@ -173,9 +172,9 @@ public class JsonFileWriter implements AutoCloseable {
         for (int i = 0; i < bufferValueCount; i++) {
           if (bufferType.equals(DATA) && (vector.getMinorType() == 
Types.MinorType.VARCHAR ||
                   vector.getMinorType() == Types.MinorType.VARBINARY)) {
-            writeValueToGenerator(bufferType, vectorBuffer, 
vectorBuffers.get(v-1), vector, i, scale);
+            writeValueToGenerator(bufferType, vectorBuffer, 
vectorBuffers.get(v-1), vector, i);
           } else {
-            writeValueToGenerator(bufferType, vectorBuffer, null, vector, i, 
scale);
+            writeValueToGenerator(bufferType, vectorBuffer, null, vector, i);
           }
         }
         generator.writeEndArray();
@@ -200,12 +199,12 @@ public class JsonFileWriter implements AutoCloseable {
 
   private void writeValueToGenerator(BufferType bufferType, ArrowBuf buffer,
                                      ArrowBuf offsetBuffer, FieldVector vector,
-                                     final int index, final int scale) throws 
IOException {
+                                     final int index) throws IOException {
     if (bufferType.equals(TYPE)) {
       generator.writeNumber(buffer.getByte(index * TinyIntVector.TYPE_WIDTH));
     } else if (bufferType.equals(OFFSET)) {
       generator.writeNumber(buffer.getInt(index * 
BaseVariableWidthVector.OFFSET_WIDTH));
-    } else if(bufferType.equals(VALIDITY)) {
+    } else if (bufferType.equals(VALIDITY)) {
       generator.writeNumber(vector.isNull(index) ? 0 : 1);
     } else if (bufferType.equals(DATA)) {
       switch (vector.getMinorType()) {
@@ -279,6 +278,11 @@ public class JsonFileWriter implements AutoCloseable {
           generator.writeObject(hexString);
           break;
         }
+        case FIXEDSIZEBINARY:
+          int byteWidth = ((FixedSizeBinaryVector) vector).getByteWidth();
+          String fixedSizeHexString = 
Hex.encodeHexString(FixedSizeBinaryVector.get(buffer, index, byteWidth));
+          generator.writeObject(fixedSizeHexString);
+          break;
         case VARCHAR: {
           assert offsetBuffer != null;
           byte[] b = (BaseVariableWidthVector.get(buffer, offsetBuffer, 
index));
@@ -286,6 +290,7 @@ public class JsonFileWriter implements AutoCloseable {
           break;
         }
         case DECIMAL: {
+          int scale = ((DecimalVector) vector).getScale();
           BigDecimal decimalValue = 
DecimalUtility.getBigDecimalFromArrowBuf(buffer, index, scale);
           // We write the unscaled value, because the scale is stored in the 
type metadata.
           generator.writeString(decimalValue.unscaledValue().toString());
diff --git a/java/vector/src/main/java/org/apache/arrow/vector/types/Types.java 
b/java/vector/src/main/java/org/apache/arrow/vector/types/Types.java
index 3c5fd81..4adbefb 100644
--- a/java/vector/src/main/java/org/apache/arrow/vector/types/Types.java
+++ b/java/vector/src/main/java/org/apache/arrow/vector/types/Types.java
@@ -29,6 +29,7 @@ import org.apache.arrow.vector.BitVector;
 import org.apache.arrow.vector.DateDayVector;
 import org.apache.arrow.vector.DateMilliVector;
 import org.apache.arrow.vector.DecimalVector;
+import org.apache.arrow.vector.FixedSizeBinaryVector;
 import org.apache.arrow.vector.Float4Vector;
 import org.apache.arrow.vector.Float8Vector;
 import org.apache.arrow.vector.IntVector;
@@ -65,6 +66,7 @@ import org.apache.arrow.vector.complex.impl.BitWriterImpl;
 import org.apache.arrow.vector.complex.impl.DateDayWriterImpl;
 import org.apache.arrow.vector.complex.impl.DateMilliWriterImpl;
 import org.apache.arrow.vector.complex.impl.DecimalWriterImpl;
+import org.apache.arrow.vector.complex.impl.FixedSizeBinaryWriterImpl;
 import org.apache.arrow.vector.complex.impl.Float4WriterImpl;
 import org.apache.arrow.vector.complex.impl.Float8WriterImpl;
 import org.apache.arrow.vector.complex.impl.IntWriterImpl;
@@ -100,6 +102,7 @@ import org.apache.arrow.vector.types.pojo.ArrowType.Binary;
 import org.apache.arrow.vector.types.pojo.ArrowType.Bool;
 import org.apache.arrow.vector.types.pojo.ArrowType.Date;
 import org.apache.arrow.vector.types.pojo.ArrowType.Decimal;
+import org.apache.arrow.vector.types.pojo.ArrowType.FixedSizeBinary;
 import org.apache.arrow.vector.types.pojo.ArrowType.FixedSizeList;
 import org.apache.arrow.vector.types.pojo.ArrowType.FloatingPoint;
 import org.apache.arrow.vector.types.pojo.ArrowType.Int;
@@ -387,6 +390,17 @@ public class Types {
         return new DecimalWriterImpl((DecimalVector) vector);
       }
     },
+    FIXEDSIZEBINARY(null) {
+      @Override
+      public FieldVector getNewVector(String name, FieldType fieldType, 
BufferAllocator allocator, CallBack schemaChangeCallback) {
+        return new FixedSizeBinaryVector(name, fieldType, allocator);
+      }
+
+      @Override
+      public FieldWriter getNewFieldWriter(ValueVector vector) {
+        return new FixedSizeBinaryWriterImpl((FixedSizeBinaryVector) vector);
+      }
+    },
     UINT1(new Int(8, false)) {
       @Override
       public FieldVector getNewVector(String name, FieldType fieldType, 
BufferAllocator allocator, CallBack schemaChangeCallback) {
@@ -608,6 +622,11 @@ public class Types {
       }
 
       @Override
+      public MinorType visit(FixedSizeBinary type) {
+        return MinorType.FIXEDSIZEBINARY;
+      }
+
+      @Override
       public MinorType visit(Date type) {
         switch (type.getUnit()) {
           case DAY:
diff --git 
a/java/vector/src/main/java/org/apache/arrow/vector/util/Validator.java 
b/java/vector/src/main/java/org/apache/arrow/vector/util/Validator.java
index c27e5e5..c27634a 100644
--- a/java/vector/src/main/java/org/apache/arrow/vector/util/Validator.java
+++ b/java/vector/src/main/java/org/apache/arrow/vector/util/Validator.java
@@ -140,7 +140,7 @@ public class Validator {
         default:
           throw new UnsupportedOperationException("unsupported precision: " + 
fpType);
       }
-    } else if (type instanceof ArrowType.Binary) {
+    } else if (type instanceof ArrowType.Binary || type instanceof 
ArrowType.FixedSizeBinary) {
       return Arrays.equals((byte[]) o1, (byte[]) o2);
     }
 
diff --git 
a/java/vector/src/test/java/org/apache/arrow/vector/TestFixedSizeBinaryVector.java
 
b/java/vector/src/test/java/org/apache/arrow/vector/TestFixedSizeBinaryVector.java
new file mode 100644
index 0000000..9052987
--- /dev/null
+++ 
b/java/vector/src/test/java/org/apache/arrow/vector/TestFixedSizeBinaryVector.java
@@ -0,0 +1,261 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.arrow.vector;
+
+import io.netty.buffer.ArrowBuf;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.vector.holders.FixedSizeBinaryHolder;
+import org.apache.arrow.vector.holders.NullableFixedSizeBinaryHolder;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class TestFixedSizeBinaryVector {
+  private static final int numValues = 123;
+  private static final int typeWidth = 9;
+  private static final int smallDataSize = 6;
+  private static final int largeDataSize = 12;
+
+  private static byte[][] values;
+
+  static {
+    values = new byte[numValues][typeWidth];
+    for (int i = 0; i < numValues; i++) {
+      for (int j = 0; j < typeWidth; j++) {
+        values[i][j] = ((byte) i);
+      }
+    }
+  }
+
+  private ArrowBuf[] bufs = new ArrowBuf[numValues];
+  private FixedSizeBinaryHolder[] holders = new 
FixedSizeBinaryHolder[numValues];
+  private NullableFixedSizeBinaryHolder[] nullableHolders = new 
NullableFixedSizeBinaryHolder[numValues];
+
+  private static byte[] smallValue;
+
+  static {
+    smallValue = new byte[smallDataSize];
+    for (int i = 0; i < smallDataSize; i++) {
+      smallValue[i] = ((byte) i);
+    }
+  }
+
+  private ArrowBuf smallBuf;
+  private FixedSizeBinaryHolder smallHolder;
+  private NullableFixedSizeBinaryHolder smallNullableHolder;
+
+  private static byte[] largeValue;
+
+  static {
+    largeValue = new byte[largeDataSize];
+    for (int i = 0; i < largeDataSize; i++) {
+      largeValue[i] = ((byte) i);
+    }
+  }
+
+  private ArrowBuf largeBuf;
+  private FixedSizeBinaryHolder largeHolder;
+  private NullableFixedSizeBinaryHolder largeNullableHolder;
+
+  private BufferAllocator allocator;
+  private FixedSizeBinaryVector vector;
+
+  private static void failWithException(String message) throws Exception {
+    throw new Exception(message);
+  }
+
+
+  @Before
+  public void init() throws Exception {
+    allocator = new DirtyRootAllocator(Integer.MAX_VALUE, (byte) 100);
+    vector = new FixedSizeBinaryVector("fixedSizeBinary", allocator, 
typeWidth);
+    vector.allocateNew();
+
+    for (int i = 0; i < numValues; i++) {
+      bufs[i] = allocator.buffer(typeWidth);
+      bufs[i].setBytes(0, values[i]);
+
+      holders[i] = new FixedSizeBinaryHolder();
+      holders[i].byteWidth = typeWidth;
+      holders[i].buffer = bufs[i];
+
+      nullableHolders[i] = new NullableFixedSizeBinaryHolder();
+      nullableHolders[i].byteWidth = typeWidth;
+      nullableHolders[i].buffer = bufs[i];
+      nullableHolders[i].isSet = 1;
+    }
+
+    smallBuf = allocator.buffer(smallDataSize);
+    smallBuf.setBytes(0, smallValue);
+
+    smallHolder = new FixedSizeBinaryHolder();
+    smallHolder.byteWidth = smallDataSize;
+    smallHolder.buffer = smallBuf;
+
+    smallNullableHolder = new NullableFixedSizeBinaryHolder();
+    smallNullableHolder.byteWidth = smallDataSize;
+    smallNullableHolder.buffer = smallBuf;
+
+    largeBuf = allocator.buffer(largeDataSize);
+    largeBuf.setBytes(0, largeValue);
+
+    largeHolder = new FixedSizeBinaryHolder();
+    largeHolder.byteWidth = typeWidth;
+    largeHolder.buffer = largeBuf;
+
+    largeNullableHolder = new NullableFixedSizeBinaryHolder();
+    largeNullableHolder.byteWidth = typeWidth;
+    largeNullableHolder.buffer = largeBuf;
+  }
+
+  @After
+  public void terminate() throws Exception {
+    for (int i=0; i<numValues; i++) {
+      bufs[i].close();
+    }
+    smallBuf.close();
+    largeBuf.close();
+
+    vector.close();
+    allocator.close();
+  }
+
+  @Test
+  public void testSetUsingByteArray() {
+    for (int i = 0; i < numValues; i++) {
+      vector.set(i, values[i]);
+    }
+    vector.setValueCount(numValues);
+    for (int i = 0; i < numValues; i++) {
+      assertArrayEquals(values[i], vector.getObject(i));
+    }
+  }
+
+  @Test
+  public void testSetUsingHolder() {
+    for (int i = 0; i < numValues; i++) {
+      vector.set(i, holders[i]);
+    }
+    vector.setValueCount(numValues);
+    for (int i = 0; i < numValues; i++) {
+      assertArrayEquals(values[i], vector.getObject(i));
+    }
+  }
+
+  @Test
+  public void testSetUsingNullableHolder() {
+    for (int i = 0; i < numValues; i++) {
+      vector.set(i, nullableHolders[i]);
+    }
+    vector.setValueCount(numValues);
+    for (int i = 0; i < numValues; i++) {
+      assertArrayEquals(values[i], vector.getObject(i));
+    }
+  }
+
+  @Test
+  public void testGetUsingNullableHolder() {
+    for (int i = 0; i < numValues; i++) {
+      vector.set(i, holders[i]);
+    }
+    vector.setValueCount(numValues);
+    for (int i = 0; i < numValues; i++) {
+      vector.get(i, nullableHolders[i]);
+      assertEquals(typeWidth, nullableHolders[i].byteWidth);
+      assertTrue(nullableHolders[i].isSet > 0);
+      byte[] actual = new byte[typeWidth];
+      nullableHolders[i].buffer.getBytes(0, actual, 0, typeWidth);
+      assertArrayEquals(values[i], actual);
+    }
+  }
+
+  @Test
+  public void testSetWithInvalidInput() throws Exception {
+    String errorMsg = "input data needs to be at least " + typeWidth + " 
bytes";
+
+    // test small inputs, byteWidth matches but value or buffer is too small
+    try {
+      vector.set(0, smallValue);
+      failWithException(errorMsg);
+    } catch (AssertionError ignore) {
+    }
+
+    try {
+      vector.set(0, smallHolder);
+      failWithException(errorMsg);
+    } catch (AssertionError ignore) {
+    }
+
+    try {
+      vector.set(0, smallNullableHolder);
+      failWithException(errorMsg);
+    } catch (AssertionError ignore) {
+    }
+
+    try {
+      vector.set(0, smallBuf);
+      failWithException(errorMsg);
+    } catch (AssertionError ignore) {
+    }
+
+    // test large inputs, byteWidth matches but value or buffer is bigger than 
byteWidth
+    vector.set(0, largeValue);
+    vector.set(0, largeHolder);
+    vector.set(0, largeNullableHolder);
+    vector.set(0, largeBuf);
+  }
+
+  @Test
+  public void setSetSafeWithInvalidInput() throws Exception {
+    String errorMsg = "input data needs to be at least " + typeWidth + " 
bytes";
+
+    // test small inputs, byteWidth matches but value or buffer is too small
+    try {
+      vector.setSafe(0, smallValue);
+      failWithException(errorMsg);
+    } catch (AssertionError ignore) {
+    }
+
+    try {
+      vector.setSafe(0, smallHolder);
+      failWithException(errorMsg);
+    } catch (AssertionError ignore) {
+    }
+
+    try {
+      vector.setSafe(0, smallNullableHolder);
+      failWithException(errorMsg);
+    } catch (AssertionError ignore) {
+    }
+
+    try {
+      vector.setSafe(0, smallBuf);
+      failWithException(errorMsg);
+    } catch (AssertionError ignore) {
+    }
+
+    // test large inputs, byteWidth matches but value or buffer is bigger than 
byteWidth
+    vector.setSafe(0, largeValue);
+    vector.setSafe(0, largeHolder);
+    vector.setSafe(0, largeNullableHolder);
+    vector.setSafe(0, largeBuf);
+  }
+}
diff --git 
a/java/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestComplexWriter.java
 
b/java/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestComplexWriter.java
index 29d39aa..b52769e 100644
--- 
a/java/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestComplexWriter.java
+++ 
b/java/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestComplexWriter.java
@@ -618,7 +618,7 @@ public class TestComplexWriter {
     final LocalDateTime expectedSecDateTime = new LocalDateTime(2001, 2, 3, 4, 
5, 6, 0);
 
     // write
-    MapVector parent = new MapVector("parent", allocator, null);
+    MapVector parent = MapVector.empty("parent", allocator);
     ComplexWriter writer = new ComplexWriterImpl("root", parent);
     MapWriter rootWriter = writer.rootAsMap();
 
@@ -718,7 +718,7 @@ public class TestComplexWriter {
     final LocalDateTime expectedMicroDateTime = new LocalDateTime(2001, 2, 3, 
4, 5, 6, 123);
 
     // write
-    MapVector parent = new MapVector("parent", allocator, null);
+    MapVector parent = MapVector.empty("parent", allocator);
     ComplexWriter writer = new ComplexWriterImpl("root", parent);
     MapWriter rootWriter = writer.rootAsMap();
 
@@ -765,7 +765,7 @@ public class TestComplexWriter {
     final LocalDateTime expectedNanoDateTime = new LocalDateTime(2001, 2, 3, 
4, 5, 6, 123);
 
     // write
-    MapVector parent = new MapVector("parent", allocator, null);
+    MapVector parent = MapVector.empty("parent", allocator);
     ComplexWriter writer = new ComplexWriterImpl("root", parent);
     MapWriter rootWriter = writer.rootAsMap();
 
@@ -807,6 +807,51 @@ public class TestComplexWriter {
   }
 
   @Test
+  public void fixedSizeBinaryWriters() throws Exception {
+    // test values
+    int numValues = 10;
+    int byteWidth = 9;
+    byte[][] values = new byte[numValues][byteWidth];
+    for (int i = 0; i < numValues; i++) {
+      for (int j = 0; j < byteWidth; j++) {
+        values[i][j] = ((byte) i);
+      }
+    }
+    ArrowBuf[] bufs = new ArrowBuf[numValues];
+    for (int i = 0; i < numValues; i++) {
+      bufs[i] = allocator.buffer(byteWidth);
+      bufs[i].setBytes(0, values[i]);
+    }
+
+    // write
+    MapVector parent = MapVector.empty("parent", allocator);
+    ComplexWriter writer = new ComplexWriterImpl("root", parent);
+    MapWriter rootWriter = writer.rootAsMap();
+
+    String fieldName = "fixedSizeBinary";
+    FixedSizeBinaryWriter fixedSizeBinaryWriter = 
rootWriter.fixedSizeBinary(fieldName, byteWidth);
+    for (int i = 0; i < numValues; i++) {
+      fixedSizeBinaryWriter.setPosition(i);
+      fixedSizeBinaryWriter.writeFixedSizeBinary(bufs[i]);
+    }
+
+    // schema
+    List<Field> children = 
parent.getField().getChildren().get(0).getChildren();
+    Assert.assertEquals(fieldName, children.get(0).getName());
+    Assert.assertEquals(ArrowType.FixedSizeBinary.TYPE_TYPE, 
children.get(0).getType().getTypeID());
+
+    // read
+    MapReader rootReader = new SingleMapReaderImpl(parent).reader("root");
+
+    FieldReader fixedSizeBinaryReader = rootReader.reader(fieldName);
+    for (int i = 0; i < numValues; i++) {
+      fixedSizeBinaryReader.setPosition(i);
+      byte[] readValues = fixedSizeBinaryReader.readByteArray();
+      Assert.assertArrayEquals(values[i], readValues);
+    }
+  }
+
+  @Test
   public void complexCopierWithList() {
     MapVector parent = MapVector.empty("parent", allocator);
     ComplexWriter writer = new ComplexWriterImpl("root", parent);
diff --git 
a/java/vector/src/test/java/org/apache/arrow/vector/ipc/TestArrowFile.java 
b/java/vector/src/test/java/org/apache/arrow/vector/ipc/TestArrowFile.java
index 055c34e..3b809d0 100644
--- a/java/vector/src/test/java/org/apache/arrow/vector/ipc/TestArrowFile.java
+++ b/java/vector/src/test/java/org/apache/arrow/vector/ipc/TestArrowFile.java
@@ -42,6 +42,7 @@ import org.apache.arrow.vector.TinyIntVector;
 import org.apache.arrow.vector.VectorSchemaRoot;
 import org.apache.arrow.vector.VectorUnloader;
 import org.apache.arrow.vector.complex.FixedSizeListVector;
+import org.apache.arrow.vector.FixedSizeBinaryVector;
 import org.apache.arrow.vector.complex.NullableMapVector;
 import 
org.apache.arrow.vector.dictionary.DictionaryProvider.MapDictionaryProvider;
 import org.apache.arrow.vector.ipc.message.ArrowBlock;
@@ -51,6 +52,7 @@ import org.apache.arrow.vector.types.FloatingPointPrecision;
 import org.apache.arrow.vector.types.Types.MinorType;
 import org.apache.arrow.vector.types.pojo.ArrowType;
 import org.apache.arrow.vector.types.pojo.ArrowType.FixedSizeList;
+import org.apache.arrow.vector.types.pojo.ArrowType.FixedSizeBinary;
 import org.apache.arrow.vector.types.pojo.ArrowType.Int;
 import org.apache.arrow.vector.types.pojo.Field;
 import org.apache.arrow.vector.types.pojo.FieldType;
@@ -550,6 +552,63 @@ public class TestArrowFile extends BaseFileTest {
   }
 
   @Test
+  public void testWriteReadFixedSizeBinary() throws IOException {
+    File file = new File("target/mytest_fixed_size_binary.arrow");
+    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+    final int numValues = 10;
+    final int typeWidth = 11;
+    byte[][] byteValues = new byte[numValues][typeWidth];
+    for (int i=0; i<numValues; i++) {
+      for (int j=0; j<typeWidth; j++) {
+        byteValues[i][j] = ((byte) i);
+      }
+    }
+
+    // write
+    try (BufferAllocator originalVectorAllocator = 
allocator.newChildAllocator("original vectors", 0, Integer.MAX_VALUE);
+         NullableMapVector parent = NullableMapVector.empty("parent", 
originalVectorAllocator)) {
+      FixedSizeBinaryVector fixedSizeBinaryVector = 
parent.addOrGet("fixed-binary", FieldType.nullable(new 
FixedSizeBinary(typeWidth)), FixedSizeBinaryVector.class);
+      parent.allocateNew();
+      for (int i=0; i<numValues; i++) {
+        fixedSizeBinaryVector.set(i, byteValues[i]);
+      }
+      parent.setValueCount(numValues);
+      write(parent, file, stream);
+    }
+
+    // read
+    try (BufferAllocator readerAllocator = 
allocator.newChildAllocator("reader", 0, Integer.MAX_VALUE);
+         FileInputStream fileInputStream = new FileInputStream(file);
+         ArrowFileReader arrowReader = new 
ArrowFileReader(fileInputStream.getChannel(), readerAllocator)) {
+      VectorSchemaRoot root = arrowReader.getVectorSchemaRoot();
+      Schema schema = root.getSchema();
+      LOGGER.debug("reading schema: " + schema);
+
+      for (ArrowBlock rbBlock : arrowReader.getRecordBlocks()) {
+        arrowReader.loadRecordBatch(rbBlock);
+        Assert.assertEquals(numValues, root.getRowCount());
+        for (int i = 0; i < numValues; i++) {
+          Assert.assertArrayEquals(byteValues[i], ((byte[]) 
root.getVector("fixed-binary").getObject(i)));
+        }
+      }
+    }
+
+    // read from stream
+    try (BufferAllocator readerAllocator = 
allocator.newChildAllocator("reader", 0, Integer.MAX_VALUE);
+         ByteArrayInputStream input = new 
ByteArrayInputStream(stream.toByteArray());
+         ArrowStreamReader arrowReader = new ArrowStreamReader(input, 
readerAllocator)) {
+      VectorSchemaRoot root = arrowReader.getVectorSchemaRoot();
+      Schema schema = root.getSchema();
+      LOGGER.debug("reading schema: " + schema);
+      arrowReader.loadNextBatch();
+      Assert.assertEquals(numValues, root.getRowCount());
+      for (int i = 0; i < numValues; i++) {
+        Assert.assertArrayEquals(byteValues[i], ((byte[]) 
root.getVector("fixed-binary").getObject(i)));
+      }
+    }
+  }
+
+  @Test
   public void testWriteReadFixedSizeList() throws IOException {
     File file = new File("target/mytest_fixed_list.arrow");
     ByteArrayOutputStream stream = new ByteArrayOutputStream();
diff --git 
a/java/vector/src/test/java/org/apache/arrow/vector/types/pojo/TestSchema.java 
b/java/vector/src/test/java/org/apache/arrow/vector/types/pojo/TestSchema.java
index 43b0907..c6699fe 100644
--- 
a/java/vector/src/test/java/org/apache/arrow/vector/types/pojo/TestSchema.java
+++ 
b/java/vector/src/test/java/org/apache/arrow/vector/types/pojo/TestSchema.java
@@ -30,6 +30,7 @@ import org.apache.arrow.vector.types.IntervalUnit;
 import org.apache.arrow.vector.types.TimeUnit;
 import org.apache.arrow.vector.types.UnionMode;
 import org.apache.arrow.vector.types.pojo.ArrowType.Binary;
+import org.apache.arrow.vector.types.pojo.ArrowType.FixedSizeBinary;
 import org.apache.arrow.vector.types.pojo.ArrowType.Bool;
 import org.apache.arrow.vector.types.pojo.ArrowType.Date;
 import org.apache.arrow.vector.types.pojo.ArrowType.Decimal;
@@ -95,7 +96,8 @@ public class TestSchema {
         field("p", new Time(TimeUnit.NANOSECOND, 64)),
         field("q", new Timestamp(TimeUnit.MILLISECOND, "UTC")),
         field("r", new Timestamp(TimeUnit.MICROSECOND, null)),
-        field("s", new Interval(IntervalUnit.DAY_TIME))
+        field("s", new Interval(IntervalUnit.DAY_TIME)),
+        field("t", new FixedSizeBinary(100))
     ));
     roundTrip(schema);
   }

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to