@Override
 public void addBinary(Binary value) {
   holder.buffer = buf = buf.reallocIfNeeded(value.length());
   buf.setBytes(0, value.toByteBuffer());
   holder.start = 0;
   holder.end = value.length();
   writer.write(holder);
 }
  public static VarCharHolder getVarCharHolder(BufferAllocator a, String s) {
    VarCharHolder vch = new VarCharHolder();

    byte[] b = s.getBytes(Charsets.UTF_8);
    vch.start = 0;
    vch.end = b.length;
    vch.buffer = a.buffer(b.length); //
    vch.buffer.setBytes(0, b);
    return vch;
  }
  public static VarCharHolder getVarCharHolder(DrillBuf buf, String s) {
    VarCharHolder vch = new VarCharHolder();

    byte[] b = s.getBytes(Charsets.UTF_8);
    vch.start = 0;
    vch.end = b.length;
    vch.buffer = buf.reallocIfNeeded(b.length);
    vch.buffer.setBytes(0, b);
    return vch;
  }
 private void writeString(
     String readString, final MapOrListWriterImpl writer, String fieldName, boolean isList) {
   int length;
   byte[] strBytes;
   try {
     strBytes = readString.getBytes("UTF-8");
   } catch (UnsupportedEncodingException e) {
     throw new DrillRuntimeException("Unable to read string value for field: " + fieldName, e);
   }
   length = strBytes.length;
   ensure(length);
   workBuf.setBytes(0, strBytes);
   final VarCharHolder vh = new VarCharHolder();
   vh.buffer = workBuf;
   vh.start = 0;
   vh.end = length;
   if (isList == false) {
     writer.varChar(fieldName).write(vh);
   } else {
     writer.list.varChar().write(vh);
   }
 }
  @Override
  public void eval() {
    out.buffer = string.buffer;

    // handle invalid values; e.g. SUBSTRING(value, 0, x) or SUBSTRING(value, x, 0)
    if (offset.value == 0 || length.value <= 0) {
      out.start = 0;
      out.end = 0;
    } else {

      // start iterating over the UTF-8 buffer to find the first character of the substring
      int byteCount = 0;
      int charCount = 0;
      int byteStart = 0;
      int charStart = 0;
      int byteEnd = 0;
      byte currentByte;
      while (byteCount < string.end - string.start) {
        currentByte = string.buffer.getByte(string.start + byteCount);
        // check current position matches the (positive) start position
        if (offset.value > 0 && charCount == (int) offset.value - 1) {
          byteStart = byteCount;
          charStart = charCount;
        }

        // check current position matches the supplied length
        if (offset.value > 0 && charCount - charStart == (int) length.value) {
          byteEnd = byteCount;
          break;
        }

        if (currentByte < 128) ++charCount;
        ++byteCount;
      }

      out.start = string.start + byteStart;
      out.end = string.start + byteEnd;

      // search backwards for negative offsets
      if (offset.value < 0) {
        int endBytePos = --byteCount;
        int endCharPos = --charCount;
        while (byteCount >= 0) {
          currentByte = string.buffer.getByte(byteCount);

          if (endCharPos - charCount == -(int) offset.value) {
            // matched the negative start offset
            out.start = byteCount;
            charCount = 0;

            // search forward until we find <length> characters
            while (byteCount <= endBytePos) {
              currentByte = string.buffer.getByte(byteCount);
              if (currentByte < 128) ++charCount;
              ++byteCount;
              if (charCount == (int) length.value) {
                out.end = byteCount;
                break;
              }
            }
            break;
          }
          if (currentByte < 128) --charCount;
          --byteCount;
        }
      }

      // if length exceeds value, stop at end of value
      if (out.end == 0) {
        out.end = string.end;
      }
    }
  }
 @Override
 public void eval() {
   out.buffer = in.buffer;
   out.start = in.start;
   out.end = in.end;
 }