void completeTree() {
      assert ranges != null : "Validator was not prepared()";

      if (range != null) range.addHash(EMPTY_ROW);
      while (ranges.hasNext()) {
        range = ranges.next();
        range.addHash(EMPTY_ROW);
      }
    }
    /**
     * Called (in order) for every row present in the CF. Hashes the row, and adds it to the tree
     * being built.
     *
     * <p>There are four possible cases: 1. Token is greater than range.right (we haven't generated
     * a range for it yet), 2. Token is less than/equal to range.left (the range was valid), 3.
     * Token is contained in the range (the range is in progress), 4. No more invalid ranges exist.
     *
     * <p>TODO: Because we only validate completely empty trees at the moment, we do not bother
     * dealing with case 2 and case 4 should result in an error.
     *
     * <p>Additionally, there is a special case for the minimum token, because although it sorts
     * first, it is contained in the last possible range.
     *
     * @param row The row.
     */
    public void add(AbstractCompactedRow row) {
      assert request.range.contains(row.key.token)
          : row.key.token + " is not contained in " + request.range;
      assert lastKey == null || lastKey.compareTo(row.key) < 0
          : "row " + row.key + " received out of order wrt " + lastKey;
      lastKey = row.key;

      if (range == null) range = ranges.next();

      // generate new ranges as long as case 1 is true
      while (!range.contains(row.key.token)) {
        // add the empty hash, and move to the next range
        range.addHash(EMPTY_ROW);
        range = ranges.next();
      }

      // case 3 must be true: mix in the hashed row
      range.addHash(rowHash(row));
    }