///|
fn Content::joined(self : Content, other : Content) -> Content? {
  match (self, other) {
    (Characters(a), Characters(b)) => Some(Characters(a + b))
    (Values(a), Values(b)) =>
      Some(
        Values(
          ReadOnlyArray::from_array(
            (a.iter().to_array() + b.iter().to_array())[:],
          ),
        ),
      )
    (LegacyJson(a), LegacyJson(b)) =>
      Some(
        LegacyJson(
          ReadOnlyArray::from_array(
            (a.iter().to_array() + b.iter().to_array())[:],
          ),
        ),
      )
    (Deleted(a), Deleted(b)) if a <= 0x7fff_ffff - b => Some(Deleted(a + b))
    (Garbage(a), Garbage(b)) if a <= 0x7fff_ffff - b => Some(Garbage(a + b))
    _ => None
  }
}

///|
fn Doc::compact(self : Doc, transaction : Transaction) -> Unit {
  let clients = transaction.merge_clients
  for (client, _, _) in transaction.added {
    clients.add(client) |> ignore
  }
  for (client, _, _) in transaction.removed {
    clients.add(client) |> ignore
  }
  for client in clients {
    guard self.clients.get(client) is Some(blocks) else { continue }
    let mut index = blocks.length() - 1
    while index > 0 {
      let a = blocks[index - 1]
      let b = blocks[index]
      let end = a.id.clock + a.content.length().reinterpret_as_uint()
      if end == b.id.clock &&
        b.origin == Some({ client, clock: end - 1U, }) &&
        a.right_origin == b.right_origin &&
        a.right == Some(b.id) &&
        a.deleted == b.deleted &&
        !a.linked &&
        !b.linked &&
        a.redone is None &&
        b.redone is None &&
        a.parent == b.parent &&
        a.parent_sub == b.parent_sub &&
        a.content.joined(b.content) is Some(content) {
        if a.parent_sub is None && a.parent is Some(parent) {
          let branch = self.branch(parent)
          if branch.position_hint is Some((hint, offset)) && hint.id == b.id {
            branch.position_hint = Some(
              (a, offset - a.content.offset_length(self)),
            )
          }
        }
        a.keep = a.keep || b.keep
        a.content = content
        a.right = b.right
        if b.right is Some(right) {
          self.items[right].left = Some(a.id)
        }
        if a.parent is Some(parent) && a.parent_sub is Some(key) {
          let branch = self.branch(parent)
          if branch.values.get(key) == Some(b.id) {
            branch.values[key] = a.id
          }
          if branch.heads.get(key) == Some(b.id) {
            branch.heads[key] = a.id
          }
        }
        self.items.remove(b.id)
        blocks.remove(index) |> ignore
      }
      index -= 1
    }
  }
}