docs: public final void addRow(Object... values) {
          assert(verifyTableInvariants());
          if (m_readOnly) {
              throw new IllegalStateException("Table is read-only. Make a copy before changing.");
          }
          if (m_colCount == 0) {
              throw new IllegalStateException("Table has no columns defined");
          }
          if (values.length != m_colCount) {
              throw new IllegalArgumentException(values.length + " arguments but table has " + m_colCount + " columns");
          }
  
          // memoize the start of this row in case we roll back
          final int pos = m_buffer.position();
  
          try {
              // Allow the buffer to grow to max capacity
              m_buffer.limit(m_buffer.capacity());
              // advance the row size value
              m_buffer.position(pos + 4);
  
              // where does the type bytes start
              // skip rowstart + status code + colcount
              int typePos = POS_COL_TYPES;
  
              for (int col = 0; col < m_colCount; col++) {
                  Object value = values[col];
                  VoltType columnType = VoltType.get(m_buffer.get(typePos + col));
  
                  addColumnValue(value, columnType, col);
              }
  
              //
              // Note, there is some near-identical code in both row add methods.
              // [ add(..) and addRow(..) ]
              // If you change code below here, change it in the other method too.
              // (It would be nice to re-factor, but I couldn't make a clean go at
              //  it quickly - Hugg)
              //
  
              final int rowsize = m_buffer.position() - pos - 4;
              assert(rowsize >= 0);
  
              // check for too big rows
              if (rowsize > VoltTableRow.MAX_TUPLE_LENGTH) {
                  throw new VoltOverflowException(
                          "Table row total length larger than allowed max " + VoltTableRow.MAX_TUPLE_LENGTH_STR);
              }
  
              // buffer overflow is caught and handled below.
              m_buffer.putInt(pos, rowsize);
  
              m_rowCount++;
              m_buffer.putInt(m_rowStart, m_rowCount);
          }
          catch (VoltTypeException vte) {
              // revert the row size advance and any other
              // buffer additions
              m_buffer.position(pos);
              throw vte;
          }
          catch (BufferOverflowException e) {
              m_buffer.position(pos);
              expandBuffer();
              addRow(values);
          }
          // row was too big, reset and rethrow
          catch (VoltOverflowException e) {
              m_buffer.position(pos);
              throw e;
          }
          catch (IllegalArgumentException e) {
              m_buffer.position(pos);
              // if this was thrown because of a lack of space
              // then grow the buffer
              // the number 32 was picked out of a hat ( maybe a bug if str > 32 )
              if (m_buffer.limit() - m_buffer.position() < 32) {
                  expandBuffer();
                  addRow(values);
              } else {
                  throw e;
              }
          }
          finally {
              // constrain buffer limit back to the new position
              m_buffer.limit(m_buffer.position());
          }
  
          assert(verifyTableInvariants());
      }