///|
fn rle_decode(bytes : Bytes, expected : Int32) -> Bytes raise ExrError {
  let r = reader(bytes)
  let out = sink()
  while r.pos < r.end {
    let control = r.u8()
    if control >= 128 {
      let count = 256 - control
      if count > expected - out.size() {
        raise Invalid("RLE literal exceeds chunk", r.pos)
      }
      out.raw(r.take(count))
    } else {
      let count = control + 1
      if count > expected - out.size() {
        raise Invalid("RLE run exceeds chunk", r.pos)
      }
      let value = r.u8()
      for i = 0; i < count; i = i + 1 {
        out.uint(value.to_uint64(), 1)
      }
    }
  }
  if out.size() != expected {
    raise Invalid("RLE decoded byte count", r.pos)
  }
  out.finish()
}

///|
fn rle_encode(bytes : Bytes) -> Bytes {
  let out = sink()
  let mut at = 0
  while at < bytes.length() {
    let mut end = at + 1
    while end < bytes.length() && end - at < 128 && bytes[end] == bytes[at] {
      end += 1
    }
    if end - at >= 3 {
      out.uint((end - at - 1).to_uint64(), 1)
      out.uint(bytes[at].to_uint64(), 1)
      at = end
    } else {
      let start = at
      at += 1
      while at < bytes.length() && at - start < 127 {
        if at + 2 < bytes.length() &&
          bytes[at] == bytes[at + 1] &&
          bytes[at] == bytes[at + 2] {
          break
        }
        at += 1
      }
      out.uint((256 - (at - start)).to_uint64(), 1)
      out.raw(bytes[start:at].to_owned())
    }
  }
  out.finish()
}

///|
test "RLE literal repeat boundaries and signed -128 are interoperable" {
  assert_eq(rle_decode(b"\x02a\xfeBC", 5), b"aaaBC")
  assert_eq(rle_encode(b"aaaBC"), b"\x02a\xfeBC")
  let data = Bytes::from_array(Array::makei(128, i => i.to_byte()))
  let out = sink()
  out.uint(128UL, 1)
  out.raw(data)
  assert_eq(rle_decode(out.finish(), 128), data)
  for n in [1, 2, 3, 126, 127, 128, 129, 256, 1000] {
    let repeated = Bytes::from_array(Array::make(n, b'a'))
    assert_eq(rle_decode(rle_encode(repeated), n), repeated)
    let literals = Bytes::from_array(Array::makei(n, i => i.to_byte()))
    assert_eq(rle_decode(rle_encode(literals), n), literals)
  }
}

///|
test "RLE rejects expansion mismatch truncation and trailing packets" {
  for bytes in [b"\xff", b"\x7f", b"\x7fa", b"\xffa\x00b"] {
    assert_true(
      try {
        ignore(rle_decode(bytes, 1))
        false
      } catch {
        _ => true
      },
    )
  }
}