function encodeContainer(chunkType, encodeContentsCallback) {
  const CHECKSUM_SIZE = 4 // checksum is first 4 bytes of SHA-256 hash of the rest of the data
  const HEADER_SPACE = MAGIC_BYTES.byteLength + CHECKSUM_SIZE + 1 + 5 // 1 byte type + 5 bytes length
  const body = new Encoder()
  // Make space for the header at the beginning of the body buffer. We will
  // copy the header in here later. This is cheaper than copying the body since
  // the body is likely to be much larger than the header.
  body.appendRawBytes(new Uint8Array(HEADER_SPACE))
  encodeContentsCallback(body)

  const bodyBuf = body.buffer
  const header = new Encoder()
  header.appendByte(chunkType)
  header.appendUint53(bodyBuf.byteLength - HEADER_SPACE)

  // Compute the hash over chunkType, length, and body
  const headerBuf = header.buffer
  const sha256 = new Hash()
  sha256.update(headerBuf)
  sha256.update(bodyBuf.subarray(HEADER_SPACE))
  const hash = sha256.digest(),
    checksum = hash.subarray(0, CHECKSUM_SIZE)

  // Copy header into the body buffer so that they are contiguous
  bodyBuf.set(
    MAGIC_BYTES,
    HEADER_SPACE -
      headerBuf.byteLength -
      CHECKSUM_SIZE -
      MAGIC_BYTES.byteLength,
  )
  bodyBuf.set(checksum, HEADER_SPACE - headerBuf.byteLength - CHECKSUM_SIZE)
  bodyBuf.set(headerBuf, HEADER_SPACE - headerBuf.byteLength)
  return {
    hash,
    bytes: bodyBuf.subarray(
      HEADER_SPACE -
        headerBuf.byteLength -
        CHECKSUM_SIZE -
        MAGIC_BYTES.byteLength,
    ),
  }
}
Assembles a chunk of encoded data containing a checksum, headers, and a
series of encoded columns. Calls `encodeHeaderCallback` with an encoder that
should be used to add the headers. The columns should be given as `columns`.