function decodeValueColumns(columns, colIndex, actorIds, result) {
  const { columnId, columnName, decoder } = columns[colIndex]
  if (
    columnId % 8 === COLUMN_TYPE.VALUE_LEN &&
    colIndex + 1 < columns.length &&
    columns[colIndex + 1].columnId === columnId + 1
  ) {
    const sizeTag = decoder.readValue()
    const rawValue = columns[colIndex + 1].decoder.readRawBytes(sizeTag >> 4)
    const { value, datatype } = decodeValue(sizeTag, rawValue)
    result[columnName] = value
    if (datatype) result[columnName + "_datatype"] = datatype
    return 2
  } else if (columnId % 8 === COLUMN_TYPE.ACTOR_ID) {
    const actorNum = decoder.readValue()
    if (actorNum === null) {
      result[columnName] = null
    } else {
      if (!actorIds[actorNum])
        throw new RangeError(`No actor index ${actorNum}`)
      result[columnName] = actorIds[actorNum]
    }
  } else {
    result[columnName] = decoder.readValue()
  }
  return 1
}
Reads one value from the column `columns[colIndex]` and interprets it based
on the column type. `actorIds` is a list of actors that appear in the change;
`actorIds[0]` is the actorId of the change's author. Mutates the `result`
object with the value, and returns the number of columns processed (this is 2
in the case of a pair of VALUE_LEN and VALUE_RAW columns, which are processed
in one go).