///|
fn read_update_fields(
  reader : UpdateReader,
) -> (Array[RawItem], IdSet, Bool) raise DecodeError {
  fn id(column : Int32) -> Id raise DecodeError {
    { client: reader.field(1), clock: codec_clock(reader.field(column)), }
  }
  fn length_count() -> Int32 raise DecodeError {
    let n = codec_clock(reader.field(8))
    guard n.to_uint64() <= reader.budget.to_uint64() else {
      raise InvalidData("update exceeds decode item budget")
    }
    reader.budget -= n.reinterpret_as_int()
    n.reinterpret_as_int()
  }
  let items = []
  let clients = reader.count()
  for _ in 0.. Some(Root(reader.string()))
          0UL => Some(Nested(id(2)))
          _ => raise InvalidData("invalid parent discriminator")
        }
      } else {
        None
      }
      let parent_sub = if parent is Some(_) && (info & 32) != 0 {
        Some(reader.string())
      } else {
        None
      }
      // Deleted runs carry no per-clock payload. Keep each internal block within
      // signed indexing bounds while accepting the full unsigned wire range.
      if kind == 0 || kind == 1 {
        let length = codec_clock(reader.field(8))
        guard clock.to_uint64() + length.to_uint64() <= 0xffff_ffffUL else {
          raise InvalidData("clock overflow")
        }
        let end = clock + length
        let mut left = origin
        while clock < end {
          let count = (end - clock).min(0x7fff_ffffU)
          items.push({
            id: { client, clock, },
            origin: if kind == 0 {
              None
            } else {
              left
            },
            right_origin,
            parent,
            parent_sub,
            content: if kind == 0 {
              Garbage(count.reinterpret_as_int())
            } else {
              Deleted(count.reinterpret_as_int())
            },
          })
          clock += count
          left = Some({ client, clock: clock - 1U, })
        }
        continue
      }
      let content = match kind {
        2 => {
          let values = []
          let count = length_count()
          for _ in 0.. raise InvalidData("invalid legacy JSON")
                  },
                )
              },
            )
          }
          LegacyJson(ReadOnlyArray::from_array(values[:]))
        }
        9 => Subdocument(decode_subdocument(reader.string(), reader.raw.any()))
        5 => Embed(reader.json())
        6 => Format(reader.key(), reader.json())
        7 =>
          Shared(
            match codec_byte(reader.field(7)).to_int() {
              0 => SharedArray
              1 => SharedMap
              2 => SharedText
              3 => SharedXmlElement(reader.key())
              4 => SharedXmlFragment
              6 => SharedXmlText
              7 => SharedWeak(LinkSource::read(reader))
              5 | 9 | 15 as reference => SharedOpaque(reference)
              _ => raise InvalidData("unsupported shared type")
            },
          )
        3 => Binary(reader.raw.buffer())
        8 => {
          let values = []
          let count = length_count()
          for _ in 0.. Characters(reader.string())
        _ => raise InvalidData("unsupported item content")
      }
      let length = content.length()
      guard clock.to_uint64() + length.to_uint64() <= 0xffff_ffffUL else {
        raise InvalidData("clock overflow")
      }
      if length > 0 && kind != 10 {
        items.push({
          id: { client, clock, },
          origin,
          right_origin,
          parent,
          parent_sub,
          content,
        })
      }
      clock += length.reinterpret_as_uint()
    }
  }
  let deletion_ranges = []
  for _ in 0.. 0)
}