private void topLevelParse() {

    int fieldId = 0;
    int fieldByteBegin = start;
    int fieldByteEnd = start;

    final byte separator = this.separators[0];
    final int fieldCount = this.fieldCount;
    final int[] startPositions = this.startPositions;
    final byte[] bytes = this.bytes;
    final int end = this.end;

    /*
     * Optimize the loops by pulling special end cases and global decisions like isEscaped out!
     */
    if (!isEscaped) {
      while (fieldByteEnd < end) {
        if (bytes[fieldByteEnd] == separator) {
          startPositions[fieldId++] = fieldByteBegin;
          if (fieldId == fieldCount) {
            break;
          }
          fieldByteBegin = ++fieldByteEnd;
        } else {
          fieldByteEnd++;
        }
      }
      // End serves as final separator.
      if (fieldByteEnd == end && fieldId < fieldCount) {
        startPositions[fieldId++] = fieldByteBegin;
      }
    } else {
      final byte escapeChar = this.escapeChar;
      final int endLessOne = end - 1;
      final int[] escapeCounts = this.escapeCounts;
      int escapeCount = 0;
      // Process the bytes that can be escaped (the last one can't be).
      while (fieldByteEnd < endLessOne) {
        if (bytes[fieldByteEnd] == separator) {
          escapeCounts[fieldId] = escapeCount;
          escapeCount = 0;
          startPositions[fieldId++] = fieldByteBegin;
          if (fieldId == fieldCount) {
            break;
          }
          fieldByteBegin = ++fieldByteEnd;
        } else if (bytes[fieldByteEnd] == escapeChar) {
          // Ignore the char after escape_char
          fieldByteEnd += 2;
          escapeCount++;
        } else {
          fieldByteEnd++;
        }
      }
      // Process the last byte if necessary.
      if (fieldByteEnd == endLessOne && fieldId < fieldCount) {
        if (bytes[fieldByteEnd] == separator) {
          escapeCounts[fieldId] = escapeCount;
          escapeCount = 0;
          startPositions[fieldId++] = fieldByteBegin;
          if (fieldId <= fieldCount) {
            fieldByteBegin = ++fieldByteEnd;
          }
        } else {
          fieldByteEnd++;
        }
      }
      // End serves as final separator.
      if (fieldByteEnd == end && fieldId < fieldCount) {
        escapeCounts[fieldId] = escapeCount;
        startPositions[fieldId++] = fieldByteBegin;
      }
    }

    if (fieldId == fieldCount || fieldByteEnd == end) {
      // All fields have been parsed, or bytes have been parsed.
      // We need to set the startPositions of fields.length to ensure we
      // can use the same formula to calculate the length of each field.
      // For missing fields, their starting positions will all be the same,
      // which will make their lengths to be -1 and uncheckedGetField will
      // return these fields as NULLs.
      Arrays.fill(startPositions, fieldId, startPositions.length, fieldByteEnd + 1);
    }

    isEndOfInputReached = (fieldByteEnd == end);
  }
Parse the byte[] and fill each field.

This is an adapted version of the parse method in the LazyStruct class.
They should parse things the same way.
serde/src/java/org/apache/hadoop/hive/serde2/lazy/fast/LazySimpleDeserializeRead.java