///|
/// In-memory complete-subtree store. Payloads are not retained after hashing.
pub struct MemoryLog {
  priv limits : Limits
  priv levels : Array[Array[Hash32]]
  priv mut count : Int
  priv mut total_bytes : Int
}

///|
pub fn MemoryLog::new(
  limits? : Limits = { leaves: 65536, bytes: 67108864, },
) -> MemoryLog {
  { limits, levels: [[]], count: 0, total_bytes: 0, }
}

///|
pub fn MemoryLog::size(self : MemoryLog) -> UInt64 {
  self.count.to_uint64()
}

///|
pub fn MemoryLog::input_bytes(self : MemoryLog) -> Int {
  self.total_bytes
}

///|
/// Validation precedes mutation: a rejected append leaves the log unchanged.
pub fn MemoryLog::append(
  self : MemoryLog,
  leaf : Bytes,
) -> UInt64 raise AppendError {
  if self.count >= self.limits.leaves {
    raise LeafLimit
  }
  if leaf.length() > self.limits.bytes - self.total_bytes {
    raise ByteLimit
  }
  let index = self.count
  let mut current = hash_leaf(leaf)
  self.levels[0].push(current)
  let mut position = index
  let mut level = 0
  while position % 2 == 1 {
    current = hash_node(self.levels[level][position - 1], current)
    level = level + 1
    if self.levels.length() == level {
      self.levels.push([])
    }
    self.levels[level].push(current)
    position = position / 2
  }
  self.count = self.count + 1
  self.total_bytes = self.total_bytes + leaf.length()
  index.to_uint64()
}

///|
pub fn MemoryLog::snapshot(self : MemoryLog) -> Snapshot {
  let mut remainder = self.count
  let mut root : Hash32? = None
  for level in 0.. node
          Some(right) => hash_node(node, right)
        },
      )
    }
    remainder = remainder / 2
  }
  { count: self.size(), digest: root.unwrap_or(empty_root()), }
}