///|
fn encode_uleb128_bytes(value : UInt64) -> Bytes {
  let output : Array[Byte] = []
  for current = value {
    let payload = (current & 0x7fUL).to_byte()
    let next = current >> 7
    if next == 0UL {
      output.push(payload)
      break
    } else {
      output.push((payload.to_uint() | 0x80U).to_byte())
      continue next
    }
  }
  Bytes::from_array(output)
}

///|
fn encode_sleb128_bytes(value : Int64) -> Bytes {
  let output : Array[Byte] = []
  for current = value {
    let payload = current & 0x7fL
    let next = current >> 7
    let sign_set = (payload & 0x40L) != 0L
    let finished = if current >= 0L {
      next == 0L && !sign_set
    } else {
      next == -1L && sign_set
    }
    if finished {
      output.push(payload.to_byte())
      break
    } else {
      output.push((payload | 0x80L).to_byte())
      continue next
    }
  }
  Bytes::from_array(output)
}

///|
/// 64 位无符号 LEB128。解码严格要求最短规范表示,最多消费 10 字节。
pub fn uleb128() -> Codec[UInt64] {
  Codec::make(
    fn(decoder) {
      let start = decoder.absolute_offset()
      let encoded : Array[Byte] = []
      let mut value = 0UL
      for index in 0..<10 {
        let byte = match decoder.read_byte() {
          Err(error) => return Err(error)
          Ok(value) => value
        }
        encoded.push(byte)
        let payload = (byte.to_uint() & 0x7fU).to_uint64()
        if index == 9 && payload > 1UL {
          return Err(
            BinError::new(
              InvalidValue,
              start,
              decoder.path(),
              "ULEB128 value overflows UInt64",
            ),
          )
        }
        value = value | (payload << (index * 7))
        if (byte.to_uint() & 0x80U) == 0U {
          let actual = Bytes::from_array(encoded)
          if encode_uleb128_bytes(value) != actual {
            return Err(
              BinError::new(
                InvalidValue,
                start,
                decoder.path(),
                "ULEB128 value is not canonically encoded",
              ),
            )
          }
          return Ok(value)
        }
      }
      Err(
        BinError::new(
          LimitExceeded,
          start,
          decoder.path(),
          "ULEB128 exceeds the 10-byte UInt64 limit",
        ),
      )
    },
    fn(encoder, value) { encoder.write_bytes(encode_uleb128_bytes(value)[:]) },
    kind="uleb128",
    render=fn(value) { value.to_string() },
  )
}

///|
/// 64 位有符号 LEB128。解码严格要求最短规范表示,最多消费 10 字节。
pub fn sleb128() -> Codec[Int64] {
  Codec::make(
    fn(decoder) {
      let start = decoder.absolute_offset()
      let encoded : Array[Byte] = []
      let mut value = 0L
      for index in 0..<10 {
        let byte = match decoder.read_byte() {
          Err(error) => return Err(error)
          Ok(value) => value
        }
        encoded.push(byte)
        let payload = (byte.to_int() & 0x7f).to_int64()
        if index == 9 && payload != 0L && payload != 0x7fL {
          return Err(
            BinError::new(
              InvalidValue,
              start,
              decoder.path(),
              "SLEB128 value overflows Int64",
            ),
          )
        }
        value = value | (payload << (index * 7))
        if (byte.to_uint() & 0x80U) == 0U {
          let bits = (index + 1) * 7
          if bits < 64 && (byte.to_uint() & 0x40U) != 0U {
            value = value | (-1L << bits)
          }
          let actual = Bytes::from_array(encoded)
          if encode_sleb128_bytes(value) != actual {
            return Err(
              BinError::new(
                InvalidValue,
                start,
                decoder.path(),
                "SLEB128 value is not canonically encoded",
              ),
            )
          }
          return Ok(value)
        }
      }
      Err(
        BinError::new(
          LimitExceeded,
          start,
          decoder.path(),
          "SLEB128 exceeds the 10-byte Int64 limit",
        ),
      )
    },
    fn(encoder, value) { encoder.write_bytes(encode_sleb128_bytes(value)[:]) },
    kind="sleb128",
    render=fn(value) { value.to_string() },
  )
}