/*
 * 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.
 */
import java.lang.Override;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Set;
import org.apache.drill.common.exceptions.DrillRuntimeException;
import org.apache.drill.exec.exception.OutOfMemoryException;
import org.apache.drill.exec.memory.AllocationManager.BufferLedger;
import org.apache.drill.exec.vector.BaseDataValueVector;
import org.apache.drill.exec.vector.BaseValueVector;
import org.apache.drill.exec.vector.VariableWidthVector;

<@pp.dropOutputFile />
<#list vv.types as type>
<#list type.minor as minor>

<#assign friendlyType = (minor.friendlyType!minor.boxedType!type.boxedType) />

<#if type.major == "VarLen">
<@pp.changeOutputFile name="/org/apache/drill/exec/vector/${minor.class}Vector.java" />

<#include "/@includes/license.ftl" />

package org.apache.drill.exec.vector;

<#include "/@includes/vv_imports.ftl" />

import java.util.Iterator;
import java.nio.ByteOrder;

/**
 * ${minor.class}Vector implements a vector of variable width values.  Elements in the vector
 * are accessed by position from the logical start of the vector.  A fixed width offsetVector
 * is used to convert an element's position to it's offset from the start of the (0-based)
 * DrillBuf. Size is inferred from adjacent elements.
 * <ul>
 * <li>The width of each element is ${type.width} byte(s). Note that the actual width is
 * variable, this width is used as a guess for certain calculations.</li>
 * <li>The equivalent Java primitive is '${minor.javaType!type.javaType}'<li>
 * </ul>
 * NB: this class is automatically generated from <tt>${.template_name}</tt>
 * and <tt>ValueVectorTypes.tdd</tt> using FreeMarker.
 */

public final class ${minor.class}Vector extends BaseDataValueVector implements VariableWidthVector {

  private static final int INITIAL_BYTE_COUNT = Math.min(INITIAL_VALUE_ALLOCATION * DEFAULT_RECORD_BYTE_COUNT, MAX_BUFFER_SIZE);

  private final UInt${type.width}Vector offsetVector = new UInt${type.width}Vector(offsetsField, allocator);
  private final FieldReader reader = new ${minor.class}ReaderImpl(${minor.class}Vector.this);

  private final Accessor accessor;
  private final Mutator mutator;

  private int allocationSizeInBytes = INITIAL_BYTE_COUNT;
  private int allocationMonitor = 0;

  public ${minor.class}Vector(MaterializedField field, BufferAllocator allocator) {
    super(field, allocator);
    this.accessor = new Accessor();
    this.mutator = new Mutator();
  }

  @Override
  public FieldReader getReader(){
    return reader;
  }

  @Override
  public int getBufferSize(){
    if (getAccessor().getValueCount() == 0) {
      return 0;
    }
    return offsetVector.getBufferSize() + data.writerIndex();
  }

  @Override
  public int getAllocatedSize() {
    return offsetVector.getAllocatedSize() + data.capacity();
  }

  @Override
  public int getBufferSizeFor(int valueCount) {
    if (valueCount == 0) {
      return 0;
    }

    int idx = offsetVector.getAccessor().get(valueCount);
    return offsetVector.getBufferSizeFor(valueCount + 1) + idx;
  }

  @Override
  public int getValueCapacity(){
    return Math.max(offsetVector.getValueCapacity() - 1, 0);
  }

  @Override
  public int getByteCapacity(){
    return data.capacity();
  }

  /**
  * Return the number of bytes contained in the current var len byte vector.
  * TODO: Remove getVarByteLength with it's implementation after all client's are moved to using getCurrentSizeInBytes.
  * It's kept as is to preserve backward compatibility
  * @return
  */
  @Override
  public int getCurrentSizeInBytes() {
    return getVarByteLength();
  }

  /**
   * Return the number of bytes contained in the current var len byte vector.
   * @return
   */
  public int getVarByteLength(){
    int valueCount = getAccessor().getValueCount();
    if(valueCount == 0) {
      return 0;
    }
    return offsetVector.getAccessor().get(valueCount);
  }

  @Override
  public SerializedField getMetadata() {
    return getMetadataBuilder()
             .addChild(offsetVector.getMetadata())
             .setValueCount(getAccessor().getValueCount())
             .setBufferLength(getBufferSize())
             .build();
  }

  @Override
  public void load(SerializedField metadata, DrillBuf buffer) {
    // the bits vector is the first child (the order in which the children are added in getMetadataBuilder is significant)
    SerializedField offsetField = metadata.getChild(0);
    offsetVector.load(offsetField, buffer);

    int capacity = buffer.capacity();
    int offsetsLength = offsetField.getBufferLength();
    data = buffer.slice(offsetsLength, capacity - offsetsLength);
    data.retain();
  }

  @Override
  public void clear() {
    super.clear();
    offsetVector.clear();
  }

  @Override
  public DrillBuf[] getBuffers(boolean clear) {
    DrillBuf[] buffers = ObjectArrays.concat(offsetVector.getBuffers(false), super.getBuffers(false), DrillBuf.class);
    if (clear) {
      // does not make much sense but we have to retain buffers even when clear is set. refactor this interface.
      for (DrillBuf buffer:buffers) {
        buffer.retain(1);
      }
      clear();
    }
    return buffers;
  }

  public long getOffsetAddr(){
    return offsetVector.getBuffer().memoryAddress();
  }

  @Override
  public UInt${type.width}Vector getOffsetVector(){
    return offsetVector;
  }

  @Override
  public TransferPair getTransferPair(BufferAllocator allocator){
    return new TransferImpl(getField(), allocator);
  }

  @Override
  public TransferPair getTransferPair(String ref, BufferAllocator allocator){
    return new TransferImpl(getField().withPath(ref), allocator);
  }

  @Override
  public TransferPair makeTransferPair(ValueVector to) {
    return new TransferImpl((${minor.class}Vector) to);
  }

  public void transferTo(${minor.class}Vector target){
    target.clear();
    this.offsetVector.transferTo(target.offsetVector);
    target.data = data.transferOwnership(target.allocator).buffer;
    target.data.writerIndex(data.writerIndex());
    clear();
  }

  public void splitAndTransferTo(int startIndex, int length, ${minor.class}Vector target) {
    UInt${type.width}Vector.Accessor offsetVectorAccessor = this.offsetVector.getAccessor();
    int startPoint = offsetVectorAccessor.get(startIndex);
    int sliceLength = offsetVectorAccessor.get(startIndex + length) - startPoint;
    target.clear();
    target.offsetVector.allocateNew(length + 1);
    offsetVectorAccessor = this.offsetVector.getAccessor();
    UInt4Vector.Mutator targetOffsetVectorMutator = target.offsetVector.getMutator();
    for (int i = 0; i < length + 1; i++) {
      targetOffsetVectorMutator.set(i, offsetVectorAccessor.get(startIndex + i) - startPoint);
    }
    target.data = data.slice(startPoint, sliceLength).transferOwnership(target.allocator).buffer;
    target.getMutator().setValueCount(length);
}

  protected void copyFrom(int fromIndex, int thisIndex, ${minor.class}Vector from){
    UInt4Vector.Accessor fromOffsetVectorAccessor = from.offsetVector.getAccessor();
    int start = fromOffsetVectorAccessor.get(fromIndex);
    int end = fromOffsetVectorAccessor.get(fromIndex + 1);
    int len = end - start;

    int outputStart = offsetVector.data.get${(minor.javaType!type.javaType)?cap_first}(thisIndex * ${type.width});
    from.data.getBytes(start, data, outputStart, len);
    offsetVector.data.set${(minor.javaType!type.javaType)?cap_first}( (thisIndex+1) * ${type.width}, outputStart + len);
  }

  public void copyFromSafe(int fromIndex, int thisIndex, ${minor.class}Vector from){
    UInt${type.width}Vector.Accessor fromOffsetVectorAccessor = from.offsetVector.getAccessor();
    int start = fromOffsetVectorAccessor.get(fromIndex);
    int end =   fromOffsetVectorAccessor.get(fromIndex + 1);
    int len = end - start;
    int outputStart = offsetVector.getAccessor().get(thisIndex);

    while(data.capacity() < outputStart + len) {
      reAlloc();
    }

    offsetVector.getMutator().setSafe(thisIndex + 1, outputStart + len);
    from.data.getBytes(start, data, outputStart, len);
  }

  @Override
  public void copyEntry(int toIndex, ValueVector from, int fromIndex) {
    copyFromSafe(fromIndex, toIndex, (${minor.class}Vector) from);
  }

  @Override
  public void collectLedgers(Set<BufferLedger> ledgers) {
    offsetVector.collectLedgers(ledgers);
    super.collectLedgers(ledgers);
  }

  @Override
  public int getPayloadByteCount(int valueCount) {
    if (valueCount == 0) {
      return 0;
    }
    // If 1 or more values, then the last value is set to
    // the offset of the next value, which is the same as
    // the length of existing values.
    // In addition to the actual data bytes, we must also
    // include the "overhead" bytes: the offset vector entries
    // that accompany each column value. Thus, total payload
    // size is consumed text bytes + consumed offset vector
    // bytes.
    return offsetVector.getAccessor().get(valueCount) +
           offsetVector.getPayloadByteCount(valueCount);
  }

  private class TransferImpl implements TransferPair{
    private final ${minor.class}Vector to;

    public TransferImpl(MaterializedField field, BufferAllocator allocator){
      to = new ${minor.class}Vector(field, allocator);
    }

    public TransferImpl(${minor.class}Vector to){
      this.to = to;
    }

    @Override
    public ${minor.class}Vector 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, ${minor.class}Vector.this);
    }
  }

  @Override
  public void setInitialCapacity(int valueCount) {
    long size = 1L * valueCount * ${type.width};
    if (size > MAX_ALLOCATION_SIZE) {
      throw new OversizedAllocationException("Requested amount of memory is more than max allowed allocation size");
    }
    allocationSizeInBytes = (int) size;
    offsetVector.setInitialCapacity(valueCount + 1);
  }

  @Override
  public void allocateNew() {
    if(!allocateNewSafe()){
      throw new OutOfMemoryException("Failure while allocating buffer.");
    }
  }

  @Override
  public boolean allocateNewSafe() {
    long curAllocationSize = allocationSizeInBytes;
    if (allocationMonitor > 10) {
      curAllocationSize = Math.max(MIN_BYTE_COUNT, curAllocationSize / 2);
      allocationMonitor = 0;
    } else if (allocationMonitor < -2) {
      curAllocationSize = curAllocationSize * 2L;
      allocationMonitor = 0;
    }

    if (curAllocationSize > MAX_ALLOCATION_SIZE) {
      return false;
    }

    clear();
    /* Boolean to keep track if all the memory allocations were successful
     * Used in the case of composite vectors when we need to allocate multiple
     * buffers for multiple vectors. If one of the allocations failed we need to
     * clear all the memory that we allocated
     */
    try {
      int requestedSize = (int)curAllocationSize;
      data = allocator.buffer(requestedSize);
      allocationSizeInBytes = requestedSize;
      offsetVector.allocateNew();
    } catch (OutOfMemoryException e) {
      clear();
      return false;
    }
    data.readerIndex(0);
    offsetVector.zeroVector();
    return true;
  }

  @Override
  public void allocateNew(int totalBytes, int valueCount) {
    clear();
    assert totalBytes >= 0;
    try {
      data = allocator.buffer(totalBytes);
      offsetVector.allocateNew(valueCount + 1);
    } catch (RuntimeException e) {
      clear();
      throw e;
    }
    data.readerIndex(0);
    allocationSizeInBytes = totalBytes;
    offsetVector.zeroVector();
  }

  @Override
  public void reset() {
    allocationSizeInBytes = INITIAL_BYTE_COUNT;
    allocationMonitor = 0;
    data.readerIndex(0);
    offsetVector.zeroVector();
    super.reset();
  }

  public void reAlloc() {
    long newAllocationSize = allocationSizeInBytes*2L;

    // Some operations, such as Value Vector#exchange, can be change DrillBuf data field without corresponding allocation size changes.
    // Check that the size of the allocation is sufficient to copy the old buffer.
    while (newAllocationSize < data.capacity()) {
      newAllocationSize *= 2L;
    }

    if (newAllocationSize > MAX_ALLOCATION_SIZE)  {
      throw new OversizedAllocationException("Unable to expand the buffer. Max allowed buffer size is reached.");
    }

    reallocRaw((int) newAllocationSize);
  }

  @Override
  public DrillBuf reallocRaw(int newAllocationSize) {
    DrillBuf newBuf = allocator.buffer(newAllocationSize);
    newBuf.setBytes(0, data, 0, data.capacity());
    data.release();
    data = newBuf;
    allocationSizeInBytes = newAllocationSize;
    return data;
  }

  public void decrementAllocationMonitor() {
    if (allocationMonitor > 0) {
      allocationMonitor = 0;
    }
    --allocationMonitor;
  }

  private void incrementAllocationMonitor() {
    ++allocationMonitor;
  }

  @Override
  public Accessor getAccessor(){
    return accessor;
  }

  @Override
  public Mutator getMutator() {
    return mutator;
  }

  @Override
  public void exchange(ValueVector other) {
    super.exchange(other);
    ${minor.class}Vector target = (${minor.class}Vector) other;
    offsetVector.exchange(target.offsetVector);
  }

  @Override
  public void toNullable(ValueVector nullableVector) {
    Nullable${minor.class}Vector dest = (Nullable${minor.class}Vector) nullableVector;
    dest.getMutator().fromNotNullable(this);
  }

  public final class Accessor extends BaseValueVector.BaseAccessor implements VariableWidthAccessor {
    final UInt${type.width}Vector.Accessor oAccessor = offsetVector.getAccessor();

    public long getStartEnd(int index){
      return oAccessor.getTwoAsLong(index);
    }

    public byte[] get(int index) {
      assert index >= 0;
      int startIdx = oAccessor.get(index);
      int length = oAccessor.get(index + 1) - startIdx;
      assert length >= 0;
      byte[] dst = new byte[length];
      data.getBytes(startIdx, dst, 0, length);
      return dst;
    }

    @Override
    public int getValueLength(int index) {
      UInt${type.width}Vector.Accessor offsetVectorAccessor = offsetVector.getAccessor();
      return offsetVectorAccessor.get(index + 1) - offsetVectorAccessor.get(index);
    }

    public void get(int index, ${minor.class}Holder holder){
      holder.start = oAccessor.get(index);
      holder.end = oAccessor.get(index + 1);
      holder.buffer = data;
      <#if minor.class.contains("Decimal")>
      holder.scale = field.getScale();
      holder.precision = field.getPrecision();
      </#if>
    }

    public void get(int index, Nullable${minor.class}Holder holder){
      holder.isSet = 1;
      holder.start = oAccessor.get(index);
      holder.end = oAccessor.get(index + 1);
      holder.buffer = data;
      <#if minor.class.contains("Decimal")>
      holder.scale = field.getScale();
      holder.precision = field.getPrecision();
      </#if>
    }

    <#switch minor.class>
    <#case "VarDecimal">
    @Override
    public ${friendlyType} getObject(int index) {
      byte[] b = get(index);
      BigInteger bi = b.length == 0 ? BigInteger.ZERO : new BigInteger(b);
      BigDecimal bd = new BigDecimal(bi, getField().getScale());
      return bd;
    }
    <#break>
    <#case "VarChar">
    @Override
    public ${friendlyType} getObject(int index) {
      Text text = new Text();
      text.set(get(index));
      return text;
    }
    <#break>
    <#case "Var16Char">
    @Override
    public ${friendlyType} getObject(int index) {
      return new String(get(index), StandardCharsets.UTF_16);
    }
    <#break>
    <#default>
    @Override
    public ${friendlyType} getObject(int index) {
      return get(index);
    }
    </#switch>

    @Override
    public int getValueCount() {
      return Math.max(offsetVector.getAccessor().getValueCount()-1, 0);
    }

    @Override
    public boolean isNull(int index){
      return false;
    }

    public UInt${type.width}Vector getOffsetVector(){
      return offsetVector;
    }
  }

  /**
   * <h4>Overview</h4>
   * <p>
   * Mutable${minor.class} implements a vector of variable width values.  Elements in the vector
   * are accessed by position from the logical start of the vector.  A fixed width offsetVector
   * is used to convert an element's position to it's offset from the start of the (0-based)
   * DrillBuf.  Size is inferred by adjacent elements.
   *   The width of each element is ${type.width} byte(s)
   *   The equivalent Java primitive is '${minor.javaType!type.javaType}'
   *
   * NB: this class is automatically generated from ValueVectorTypes.tdd using FreeMarker.
   * </p>
   * <h4>Contract</h4>
   * <p>
   *   <ol>
   *     <li>
   *       <b>Supported Writes:</b> {@link VariableWidthVector}s do not support random writes. In contrast {@link org.apache.drill.exec.vector.FixedWidthVector}s do
   *       allow random writes but special care is needed.
   *       </li>
   *     <li>
   *       <b>Writing Values:</b> All set methods must be called with a consecutive sequence of indices. With a few exceptions:
   *       <ol>
   *         <li>You can update the last index you just set.</li>
   *         <li>You can reset a previous index (call it Idx), but you must assume all the data after Idx is corrupt. Also
   *         note that the memory consumed by data that came after Idx is not released.</li>
   *       </ol>
   *     </li>
   *     <li>
   *       <b>Setting Value Count:</b> Vectors aren't explicitly aware of how many values they contain. So you must keep track of the
   *       number of values you've written to the vector and once you are done writing to the vector you must call {@link Mutator#setValueCount(int)}.
   *       It is possible to trim the vector by setting the value count to be less than the number of values currently contained in the vector. Note the extra memory consumed in
   *       the data buffer is not freed when this is done.
   *     </li>
   *     <li>
   *       <b>Memory Allocation:</b> When setting a value at an index you must do one of the following to ensure you do not get an {@link IndexOutOfBoundsException}.
   *       <ol>
   *         <li>
   *           Allocate the exact amount of memory you need when using the {@link Mutator#set(int, byte[])} methods. If you do not
   *           manually allocate sufficient memory an {@link IndexOutOfBoundsException} can be thrown when the data buffer runs out of space.
   *         </li>
   *         <li>
   *           Or you can use the {@link Mutator#setSafe(int, byte[])} methods, which will automatically grow your data buffer to
   *           fit your data.
   *         </li>
   *       </ol>
   *     </li>
   *     <li>
   *       <b>Immutability:</b> Once a vector has been populated with data and {@link #setValueCount(int)} has been called, it should be considered immutable.
   *     </li>
   *   </ol>
   * </p>
   */
  public final class Mutator extends BaseValueVector.BaseMutator implements VariableWidthVector.VariableWidthMutator {

    /**
     * Set the variable length element at the specified index to the supplied byte array.
     *
     * @param index   position of the bit to set
     * @param bytes   array of bytes to write
     */
    protected void set(int index, byte[] bytes) {
      assert index >= 0;
      int currentOffset = offsetVector.getAccessor().get(index);
      offsetVector.getMutator().set(index + 1, currentOffset + bytes.length);
      data.setBytes(currentOffset, bytes, 0, bytes.length);
    }

    public void setSafe(int index, byte[] bytes) {
      assert index >= 0;

      int currentOffset = offsetVector.getAccessor().get(index);
      while (data.capacity() < currentOffset + bytes.length) {
        reAlloc();
      }
      offsetVector.getMutator().setSafe(index + 1, currentOffset + bytes.length);
      data.setBytes(currentOffset, bytes, 0, bytes.length);
    }

    /**
     * Copies the bulk input into this value vector and extends its capacity if necessary.
     * @param input bulk input
     */
    public <T extends VarLenBulkEntry> void setSafe(VarLenBulkInput<T> input) {
      setSafe(input, null);
    }

    /**
     * Copies the bulk input into this value vector and extends its capacity if necessary. The callback
     * mechanism allows decoration as caller is invoked for each bulk entry.
     *
     * @param input bulk input
     * @param callback a bulk input callback object (optional)
     */
    public <T extends VarLenBulkEntry> void setSafe(VarLenBulkInput<T> input, VarLenBulkInput.BulkInputCallback<T> callback) {
      // Let's allocate a buffered mutator to optimize memory copy performance
      BufferedMutator bufferedMutator = new BufferedMutator(input.getStartIndex(), ${minor.class}Vector.this);

      // Let's process the input
      while (input.hasNext()) {
        T entry = input.next();

        if (entry == null || entry.getNumValues() == 0) {
          break; // this could happen when handling columnar batch sizing constraints
        }
        bufferedMutator.setSafe(entry);

        if (callback != null) {
          callback.onNewBulkEntry(entry);
        }

        DrillRuntimeException.checkInterrupted(); // Ensures fast handling of query cancellation
      }

      // Flush any data not yet copied to this VL container
      bufferedMutator.flush();

      // Inform the input object we're done reading
      input.done();

      if (callback != null) {
        callback.onEndBulkInput();
      }
    }

    /**
     * Set the variable length element at the specified index to the supplied byte array.
     *
     * @param index   position of the bit to set
     * @param bytes   array of bytes to write
     * @param start   start index of bytes to write
     * @param length  length of bytes to write
     */
    protected void set(int index, byte[] bytes, int start, int length) {
      assert index >= 0;
      int currentOffset = offsetVector.getAccessor().get(index);
      offsetVector.getMutator().set(index + 1, currentOffset + length);
      data.setBytes(currentOffset, bytes, start, length);
    }

    public void setSafe(int index, ByteBuffer bytes, int start, int length) {
      assert index >= 0;

      int currentOffset = offsetVector.getAccessor().get(index);

      while (data.capacity() < currentOffset + length) {
        reAlloc();
      }
      offsetVector.getMutator().setSafe(index + 1, currentOffset + length);
      data.setBytes(currentOffset, bytes, start, length);
    }

    public void setSafe(int index, byte[] bytes, int start, int length) {
      assert index >= 0;

      int currentOffset = offsetVector.getAccessor().get(index);

      while (data.capacity() < currentOffset + length) {
        reAlloc();
      }
      offsetVector.getMutator().setSafe(index + 1, currentOffset + length);
      data.setBytes(currentOffset, bytes, start, length);
    }

    @Override
    public void setValueLengthSafe(int index, int length) {
      int offset = offsetVector.getAccessor().get(index);
      while(data.capacity() < offset + length ) {
        reAlloc();
      }
      offsetVector.getMutator().setSafe(index + 1, offsetVector.getAccessor().get(index) + length);
    }

    public void setSafe(int index, int start, int end, DrillBuf buffer) {
      int len = end - start;
      int outputStart = offsetVector.data.get${(minor.javaType!type.javaType)?cap_first}(index * ${type.width});

      while (data.capacity() < outputStart + len) {
        reAlloc();
      }
      offsetVector.getMutator().setSafe(index + 1, outputStart + len);
      buffer.getBytes(start, data, outputStart, len);
    }

    public void setSafe(int index, Nullable${minor.class}Holder holder) {
      assert holder.isSet == 1;

      int start = holder.start;
      int end =   holder.end;
      int len = end - start;

      int outputStart = offsetVector.data.get${(minor.javaType!type.javaType)?cap_first}(index * ${type.width});

      while (data.capacity() < outputStart + len) {
        reAlloc();
      }
      holder.buffer.getBytes(start, data, outputStart, len);
      offsetVector.getMutator().setSafe(index + 1, outputStart + len);
    }

    <#if minor.class == "VarDecimal">
    public void set(int index, BigDecimal value) {
      byte[] bytes = value.unscaledValue().toByteArray();
      set(index, bytes, 0, bytes.length);
    }

    public void setSafe(int index, BigDecimal value) {
      byte[] bytes = value.unscaledValue().toByteArray();
      setSafe(index, bytes, 0, bytes.length);
    }
    </#if>

    public void setSafe(int index, ${minor.class}Holder holder) {
      int start = holder.start;
      int end =   holder.end;
      int len = end - start;
      int outputStart = offsetVector.data.get${(minor.javaType!type.javaType)?cap_first}(index * ${type.width});

      while(data.capacity() < outputStart + len) {
        reAlloc();
      }
      holder.buffer.getBytes(start, data, outputStart, len);
      offsetVector.getMutator().setSafe(index + 1, outputStart + len);
    }

    /**
     * Backfill missing offsets from the given last written position up to, but
     * not including the given current write position. Used by nullable
     * vectors to allow skipping values. The <tt>set()</tt> and
     * <tt>setSafe()</tt> <b>do not</b> fill empties. See DRILL-5529.
     *
     * @param lastWrite
     *          the position of the last valid write: the offset to be copied
     *          forward
     * @param index
     *          the current write position filling occurs up to, but not
     *          including, this position
     */

    public void fillEmpties(int lastWrite, int index) {

      // If last write was 2, offsets are [0, 3, 6]
      // If next write is 4, offsets must be: [0, 3, 6, 6, 6]
      // Remember the offsets are one more than row count.

      int startWrite = lastWrite + 1;
      if (startWrite < index) {

        // Don't access the offset vector if nothing to fill.
        // This handles the special case of a zero-size batch
        // in which the 0th position of the offset vector does
        // not even exist.

        int fillOffset = offsetVector.getAccessor().get(startWrite);
        UInt4Vector.Mutator offsetMutator = offsetVector.getMutator();
        for (int i = startWrite; i < index; i++) {
          offsetMutator.setSafe(i + 1, fillOffset);
        }
      }
    }

    protected void set(int index, int start, int length, DrillBuf buffer){
      assert index >= 0;
      int currentOffset = offsetVector.getAccessor().get(index);
      offsetVector.getMutator().set(index + 1, currentOffset + length);
      DrillBuf bb = buffer.slice(start, length);
      data.setBytes(currentOffset, bb);
    }

    protected void set(int index, Nullable${minor.class}Holder holder){
      int length = holder.end - holder.start;
      int currentOffset = offsetVector.getAccessor().get(index);
      offsetVector.getMutator().set(index + 1, currentOffset + length);
      data.setBytes(currentOffset, holder.buffer, holder.start, length);
    }

    protected void set(int index, ${minor.class}Holder holder){
      int length = holder.end - holder.start;
      int currentOffset = offsetVector.getAccessor().get(index);
      offsetVector.getMutator().set(index + 1, currentOffset + length);
      data.setBytes(currentOffset, holder.buffer, holder.start, length);
    }

    /**
     * <h4>Notes on Usage</h4>
     * <p>
     * For {@link VariableWidthVector}s this method can be used in the following
     * cases:
     * <ul>
     * <li>Setting the actual number of elements currently contained in the
     * vector.</li>
     * <li>Trimming the vector to have fewer elements than it current does.</li>
     * </ul>
     * </p>
     * <h4>Caveats</h4>
     * <p>
     * It is important to note that for
     * {@link org.apache.drill.exec.vector.FixedWidthVector}s this method can
     * also be used to expand the vector. However, {@link VariableWidthVector}
     * do not support this usage and this method will throw an
     * {@link IndexOutOfBoundsException} if you attempt to use it in this way.
     * Expansion of valueCounts is not supported mainly because there is no
     * benefit, since you would still have to rely on the setSafe methods to
     * appropriately expand the data buffer and populate the vector anyway
     * (since by definition we do not know the width of elements). See
     * DRILL-6234 for details.
     * </p>
     * <h4>Method Documentation</h4> {@inheritDoc}
     */
    @Override
    public void setValueCount(int valueCount) {
      int currentByteCapacity = getByteCapacity();
      // Check if valueCount to be set is zero and current capacity is also zero. If yes then
      // we should not call get to read start index from offset vector at that value count.
      int idx = (valueCount == 0 && currentByteCapacity == 0)
        ? 0
        : offsetVector.getAccessor().get(valueCount);
      data.writerIndex(idx);
      if (valueCount > 0 && currentByteCapacity > idx * 2) {
        incrementAllocationMonitor();
      } else if (allocationMonitor > 0) {
        allocationMonitor = 0;
      }
      offsetVector.getMutator().setValueCount(valueCount == 0 ? 0 : valueCount+1);
    }

    @Override
    public void generateTestData(int size){
      boolean even = true;
      <#switch minor.class>
      <#case "Var16Char">
      java.nio.charset.Charset charset = StandardCharsets.UTF_16;
      <#break>
      <#case "VarChar">
      <#default>
      java.nio.charset.Charset charset = StandardCharsets.UTF_8;
      </#switch>
      byte[] evenValue = "aaaaa".getBytes(charset);
      byte[] oddValue = "bbbbbbbbbb".getBytes(charset);
      for(int i =0; i < size; i++, even = !even){
        set(i, even ? evenValue : oddValue);
        }
      setValueCount(size);
    }
  }

  /**
   * Helper class to buffer container mutation as a means to optimize native memory copy operations. Ideally, this
   * should be done transparently as part of the Mutator and Accessor APIs.
   *
   * NB: this class is automatically generated from ValueVectorTypes.tdd using FreeMarker.
   */
  public static final class BufferedMutator {
    /** The default buffer size */
    private static final int DEFAULT_BUFF_SZ = 1024 << 2;
    /** Byte buffer */
    private final ByteBuffer buffer;
    /** Indicator on whether to enable data buffering */
    private final boolean enableDataBuffering = false;
    /** Current offset within the data buffer */
    private int dataBuffOff;
    /** Total data length (contained within data and buffer) */
    private int totalDataLen;
    /** Parent container */
    private final ${minor.class}Vector parent;
    /** A buffered mutator to the offsets vector */
    private final UInt4Vector.BufferedMutator offsetsMutator;

    /** @see {@link #BufferedMutator(int startIdx, int buffSz, ${minor.class}Vector parent)} */
    public BufferedMutator(int startIdx, ${minor.class}Vector parent) {
      this(startIdx, DEFAULT_BUFF_SZ, parent);
    }

    /**
     * Buffered mutator to optimize bulk access to the underlying vector container
     * @param startIdx start idex of the first value to be copied
     * @param buffSz buffer length to us
     * @param parent parent container object
     */
    public BufferedMutator(int startIdx, int buffSz, ${minor.class}Vector parent) {
      if (enableDataBuffering) {
        this.buffer = ByteBuffer.allocate(buffSz);
        // set the buffer to the native byte order
        this.buffer.order(ByteOrder.nativeOrder());
      } else {
        this.buffer = null;
      }

      this.parent = parent;
      this.dataBuffOff = this.parent.offsetVector.getAccessor().get(startIdx);
      this.totalDataLen = this.dataBuffOff;
      this.offsetsMutator = new UInt4Vector.BufferedMutator(startIdx, buffSz, parent.offsetVector);

      // Forcing the offsetsMutator to operate at index+1
      this.offsetsMutator.setSafe(this.dataBuffOff);
    }

    public void setSafe(VarLenBulkEntry bulkEntry) {
      // The new entry doesn't fit in remaining space
      if (enableDataBuffering && buffer.remaining() < bulkEntry.getTotalLength()) {
        flushInternal();
      }

      // Now update the offsets vector with new information
      int[] lengths = bulkEntry.getValuesLength();
      int numValues = bulkEntry.getNumValues();

      setOffsets(lengths, numValues, bulkEntry.hasNulls());

      // Now we're able to buffer the new bulk entry
      if (enableDataBuffering && buffer.remaining() >= bulkEntry.getTotalLength() && bulkEntry.arrayBacked()) {
        buffer.put(bulkEntry.getArrayData(), bulkEntry.getDataStartOffset(), bulkEntry.getTotalLength());

      } else {
        // The new entry is larger than the buffer (note at this point we know the buffer has been flushed)
        while (parent.data.capacity() < totalDataLen) {
          parent.reAlloc();
        }

        if (bulkEntry.arrayBacked()) {
          parent.data.setBytes(dataBuffOff,
            bulkEntry.getArrayData(),
            bulkEntry.getDataStartOffset(),
            bulkEntry.getTotalLength());

        } else {
          parent.data.setBytes(dataBuffOff,
            bulkEntry.getData(),
            bulkEntry.getDataStartOffset(),
            bulkEntry.getTotalLength());
        }

        // Update the underlying DrillBuf offset
        dataBuffOff += bulkEntry.getTotalLength();
      }
    }

    public void flush() {
      flushInternal();
      offsetsMutator.flush();
    }

    private void flushInternal() {
      if (!enableDataBuffering) {
        return; // NOOP
      }
      int numElements = buffer.position();

      if (numElements == 0) {
        return; // NOOP
      }

      while (parent.data.capacity() < totalDataLen) {
        parent.reAlloc();
      }

      try {
        parent.data.setBytes(dataBuffOff, buffer.array(), 0, buffer.position());
      } catch (Exception e) {
        throw new RuntimeException(e);
      }

      // Update counters
      dataBuffOff += buffer.position();
      assert dataBuffOff == totalDataLen;

      // Reset the byte buffer
      buffer.clear();
    }

    private void setOffsets(int[] lengths, int numValues, boolean hasNulls) {
      // We need to compute source offsets using the current larget offset and the value length array.
      ByteBuffer offByteBuff = offsetsMutator.getByteBuffer();
      byte[] bufferArray = offByteBuff.array();
      int remaining = numValues;
      int srcPos = 0;

      do {
        if (offByteBuff.remaining() < 4) {
          offsetsMutator.flush();
        }

        int toCopy  = Math.min(remaining, offByteBuff.remaining() / 4);
        int tgtPos       = offByteBuff.position();

        if (!hasNulls) {
          for (int idx = 0; idx < toCopy; idx++, tgtPos += 4, srcPos++) {
            totalDataLen += lengths[srcPos];
            UInt4Vector.BufferedMutator.writeInt(totalDataLen, bufferArray, tgtPos);
          }
        } else {
          for (int idx = 0; idx < toCopy; idx++, tgtPos += 4, srcPos++) {
            int curr_len = lengths[srcPos];
            totalDataLen    += (curr_len >= 0) ? curr_len : 0;
            UInt4Vector.BufferedMutator.writeInt(totalDataLen, bufferArray, tgtPos);
          }
        }

        // Update counters
        offByteBuff.position(tgtPos);
        remaining -= toCopy;

      } while (remaining > 0);

      // We need to flush as offset data can be accessed during loading to
      // figure out current payload size.
      offsetsMutator.flush();

    }
  }
}
</#if> <#-- type.major -->
</#list>
</#list>
