// lzma.mbt
//
// LZMA1 / LZMA2 解码内核(纯 MoonBit,零依赖)。
//
// 语义与语料来源(对拍脚本:`_research/lzma_final_proto.py`,oracle 为 Python 的 liblzma):
//
// LZMA1(7z 的 0x030101 coder):
//   props = 5 字节:首字节 = (pb * 5 + lp) * 9 + lc,其后 4 字节 = 小端字典大小。
//   裸流没有 ALONE 头、没有 EOS 标记;区间解码器读 5 字节初始化(首字节被 32 位移出,
//   等价于"跳过"),输出长度完全由容器给出的 unpack_size 决定。
//   尾部可能剩下未被消费的 flush 字节(实测:向量 1 用掉 29/34,向量 3 用掉 111/116)。
//
// LZMA2(7z 的 0x21 coder):
//   props = 1 字节字典大小编码((2 | (p & 1)) << (p / 2 + 11),p <= 40)。
//   分块头:
//     ctrl 0x00           结束标记
//     ctrl 0x01 / 0x02    未压缩块(0x02 带字典复位),后随 2 字节 size-1(大端)
//     ctrl 0x03-0x7F      保留(报错)
//     ctrl 0x80-0xFF      LZMA 块:
//         unpack-1 :2 字节大端,高 5 位在 ctrl 的 bit0-4
//         packed-1 :2 字节大端
//         ctrl >= 0xC0 时再跟 1 字节 props(lc / lp / pb)
//         reset 语义:0x80-0x9F 无复位;0xA0-0xBF state reset(无 props 字节);
//                     0xC0-0xDF state reset + 新 props;0xE0-0xFF 再叠加字典复位
//   注意:语料里 LZMA 块数据以 `1c5d` / `695d` 开头,头两个字节其实是 packed-1,
//   第三个字节 0x5d 才是 props(lc=3 / lp=0 / pb=2,与同一载荷的 LZMA1 props 相同),
//   这也是"LZMA2 块数据看起来是 2 个额外字节 + LZMA1 码流"的真正原因。
//   以上分块头布局由 `_research/probe_ctrl4.py` 用自建 range encoder 造流、
//   交给 liblzma 判决(四种布局里只有一种被接受),不是推测。
//
// 错误处理:所有越界都在读取点检查,抛 SevenZError,绝不 panic;
// 尾部多余的输入字节被容忍(合法归档里 LZMA2 结束标记可能被省略)。

///|
/// 概率模型初值(kBitModelTotal / 2 = 1024)。
const PROB_INIT : Int32 = 1024

///|
/// 概率自适应步长(kNumMoveBits)。
const MOVE_BITS : Int32 = 5

///|
/// 区间解码器归一化阈值(kTopValue = 1 << 24)。
const TOP_VALUE : Int64 = 0x1000000L

///|
/// 32 位掩码:`range` / `code` 始终保持在 [0, 2^32)。
const MASK32 : Int64 = 0xFFFFFFFFL

///|
/// LZMA 状态机状态数。
const NUM_STATES : Int32 = 12

///|
/// posSlot 位树深度。
const NUM_POS_SLOT_BITS : Int32 = 6

///|
/// "长度 → posSlot" 的状态数。
const NUM_LEN_TO_POS_STATES : Int32 = 4

///|
/// 对齐位树深度。
const NUM_ALIGN_BITS : Int32 = 4

///|
/// posSlot 中改用位树编码高位的临界值(此后走 direct bits + align)。
const END_POS_MODEL_INDEX : Int32 = 14

///|
/// 距离高位概率表长度。LZMA SDK 用 `1 + kNumFullDistances - kEndPosModelIndex` = 115,
/// 最大下标 114;这里留一点余量到 128,避免任何下标推导差错导致 panic。
const NUM_POS_MODELS : Int32 = 128

///|
/// 长度解码器概率数:choice + choice2 + low[16][8] + mid[16][8] + high[256] = 514。
const LEN_PROBS : Int32 = 514

///|
/// 字面子编码器概率数上限:0x300 << (lc + lp),lc + lp <= 4。
const LITERAL_PROBS : Int32 = 0x300 << 4

///|
/// 单次解码允许的最大输出长度(Int 在 wasm-gc 上是 32 位,超过即无法寻址)。
const MAX_UNPACK_SIZE : Int64 = 0x7FFFFFFFL

///|
/// LZMA 区间解码器。
///
/// `range` / `code` 用 Int64 承载但始终掩到 32 位:MoonBit 的 `Int` 是 32 位有符号,
/// 拿它做 LZMA 的无符号比较(`code < bound`)会在符号位上出错,因此这里统一用
/// Int64 + `& 0xFFFFFFFFL`,让算术与比较的语义和 C 里的 `uint32_t` 一致。
priv struct RangeDecoder {
  data : Bytes
  end : Int32
  mut pos : Int32
  mut range : Int64
  mut code : Int64
}

///|
/// 在 `data[pos, end)` 上建立区间解码器并读入 5 字节初始化。
///
/// LZMA1/LZMA2 都读 5 字节:第一个字节是编码器的 cache 字节,左移 32 位后自然丢弃,
/// 于是 `code` 由紧随其后的 4 字节构成。两个格式都不检查该字节是否为 0。
fn RangeDecoder::new(
  data : Bytes,
  pos : Int32,
  end : Int32,
) -> RangeDecoder raise SevenZError {
  let limit = if end < data.length() { end } else { data.length() }
  let decoder : RangeDecoder = {
    data,
    end: limit,
    pos,
    range: MASK32,
    code: 0L,
  }
  let mut i = 0
  while i < 5 {
    decoder.code = ((decoder.code << 8) | decoder.next_byte().to_int64()) &
      MASK32
    i += 1
  }
  decoder
}

///|
/// 读下一个码流字节;越过可用区间(LZMA2 里就是分块声明的 packed 边界)即报错。
fn RangeDecoder::next_byte(self : RangeDecoder) -> Int32 raise SevenZError {
  if self.pos >= self.end {
    raise LzmaCorrupt(
      reason="LZMA 码流越界:区间解码器需要第 " +
        self.pos.to_string() +
        " 字节,但可用区间只到 " +
        self.end.to_string(),
    )
  }
  let b = self.data.unsafe_get(self.pos).to_int()
  self.pos += 1
  b
}

///|
/// 归一化:`range` 掉到 2^24 以下时补入一个字节。
fn RangeDecoder::normalize(self : RangeDecoder) -> Unit raise SevenZError {
  if self.range < TOP_VALUE {
    self.range = (self.range << 8) & MASK32
    self.code = ((self.code << 8) | self.next_byte().to_int64()) & MASK32
  }
}

///|
/// 解一个自适应二进制位,并按下标更新概率。
fn RangeDecoder::bit(
  self : RangeDecoder,
  probs : Array[Int32],
  index : Int32,
) -> Int32 raise SevenZError {
  let prob = probs[index]
  let bound = (self.range >> 11) * prob.to_int64()
  if self.code < bound {
    self.range = bound
    probs[index] = prob + ((PROB_INIT * 2 - prob) >> MOVE_BITS)
    self.normalize()
    0
  } else {
    self.range = self.range - bound
    self.code = self.code - bound
    probs[index] = prob - (prob >> MOVE_BITS)
    self.normalize()
    1
  }
}

///|
/// 解 `count` 个"直写位"(概率各半,不走概率模型)。
fn RangeDecoder::direct_bits(
  self : RangeDecoder,
  count : Int32,
) -> Int32 raise SevenZError {
  let mut result = 0
  let mut i = 0
  while i < count {
    self.range = self.range >> 1
    self.code = (self.code - self.range) & MASK32
    let t = (0L - (self.code >> 31)) & MASK32
    self.code = (self.code + (self.range & t)) & MASK32
    result = (result << 1) + ((t + 1L) & 1L).to_int()
    self.normalize()
    i += 1
  }
  result
}

///|
/// 位树解码(高位在前)。
fn RangeDecoder::tree(
  self : RangeDecoder,
  probs : Array[Int32],
  base : Int32,
  bits : Int32,
) -> Int32 raise SevenZError {
  let mut symbol = 1
  let mut i = 0
  while i < bits {
    symbol = (symbol << 1) | self.bit(probs, base + symbol)
    i += 1
  }
  symbol - (1 << bits)
}

///|
/// 反向位树解码(先出的位是低位)。
fn RangeDecoder::reverse_tree(
  self : RangeDecoder,
  probs : Array[Int32],
  base : Int32,
  bits : Int32,
) -> Int32 raise SevenZError {
  let mut symbol = 1
  let mut result = 0
  let mut i = 0
  while i < bits {
    let b = self.bit(probs, base + symbol)
    symbol = (symbol << 1) | b
    result = result | (b << i)
    i += 1
  }
  result
}

///|
/// 一次 LZMA 解码的全部状态:概率模型 + 状态机 + 重复距离(reps)。
///
/// 概率数组与 LZMA SDK / xz 的 `CLzmaDec` 逐项对齐,便于出现差异时对着规范定位:
/// `is_match[state * 16 + pos_state]`、`is_rep[state]`、`is_rep_g0/g1/g2[state]`、
/// `is_rep0_long[state * 16 + pos_state]`、`pos_slot[4 * 64]`(按 (len-2) 截到 0..3 索引)、
/// `pos_special[115]`、`align[16]`、`len` / `rep_len` =
/// choice + choice2 + low[16][8] + mid[16][8] + high[256](low/mid 按 pos_state 索引)、
/// `literal[0x300 << (lc + lp)]`:每个上下文 3 个 0x100 半区
/// (0x000 普通或已失配、0x100 仍在匹配且提示位为 0、0x200 仍在匹配且提示位为 1)。
///
/// 字面值数组固定按 lc + lp <= 4 的上限分配,因此切换 props 不需要重新分配,
/// state reset 就是把所有概率填回 1024 并清空状态机。
priv struct LzmaState {
  mut lc : Int32
  mut lp : Int32
  mut pb : Int32
  mut state : Int32
  mut rep0 : Int32
  mut rep1 : Int32
  mut rep2 : Int32
  mut rep3 : Int32
  is_match : Array[Int32]
  is_rep : Array[Int32]
  is_rep_g0 : Array[Int32]
  is_rep_g1 : Array[Int32]
  is_rep_g2 : Array[Int32]
  is_rep0_long : Array[Int32]
  pos_slot : Array[Int32]
  pos_special : Array[Int32]
  align : Array[Int32]
  len : Array[Int32]
  rep_len : Array[Int32]
  literal : Array[Int32]
}

///|
/// 新建状态(概率全为 1024)。
fn LzmaState::new(lc : Int32, lp : Int32, pb : Int32) -> LzmaState {
  let state : LzmaState = {
    lc,
    lp,
    pb,
    state: 0,
    rep0: 0,
    rep1: 0,
    rep2: 0,
    rep3: 0,
    is_match: Array::make(NUM_STATES * 16, PROB_INIT),
    is_rep: Array::make(NUM_STATES, PROB_INIT),
    is_rep_g0: Array::make(NUM_STATES, PROB_INIT),
    is_rep_g1: Array::make(NUM_STATES, PROB_INIT),
    is_rep_g2: Array::make(NUM_STATES, PROB_INIT),
    is_rep0_long: Array::make(NUM_STATES * 16, PROB_INIT),
    pos_slot: Array::make(
      NUM_LEN_TO_POS_STATES * (1 << NUM_POS_SLOT_BITS),
      PROB_INIT,
    ),
    pos_special: Array::make(NUM_POS_MODELS, PROB_INIT),
    align: Array::make(1 << NUM_ALIGN_BITS, PROB_INIT),
    len: Array::make(LEN_PROBS, PROB_INIT),
    rep_len: Array::make(LEN_PROBS, PROB_INIT),
    literal: Array::make(LITERAL_PROBS, PROB_INIT),
  }
  state
}

///|
/// state reset:清空概率模型、状态机与 reps(字典内容保留,由调用方维护 dict_base)。
fn LzmaState::reset(self : LzmaState) -> Unit {
  self.is_match.fill(PROB_INIT)
  self.is_rep.fill(PROB_INIT)
  self.is_rep_g0.fill(PROB_INIT)
  self.is_rep_g1.fill(PROB_INIT)
  self.is_rep_g2.fill(PROB_INIT)
  self.is_rep0_long.fill(PROB_INIT)
  self.pos_slot.fill(PROB_INIT)
  self.pos_special.fill(PROB_INIT)
  self.align.fill(PROB_INIT)
  self.len.fill(PROB_INIT)
  self.rep_len.fill(PROB_INIT)
  self.literal.fill(PROB_INIT)
  self.state = 0
  self.rep0 = 0
  self.rep1 = 0
  self.rep2 = 0
  self.rep3 = 0
}

///|
/// 换一组 lc / lp / pb 并做 state reset。
fn LzmaState::set_props(
  self : LzmaState,
  lc : Int32,
  lp : Int32,
  pb : Int32,
) -> Unit {
  self.lc = lc
  self.lp = lp
  self.pb = pb
  self.reset()
}

///|
/// 解一个字面值,追加到 `out`。
///
/// 字面值上下文的"位置"是**自上次字典复位以来的字节数**(LZMA SDK 的 `processedPos`
/// 在字典复位时归零,xz 的 `dict.pos` 同理),前一个字节在字典复位后的第一个字面值
/// 处取 0。实测依据:`_research/probe_dictreset_pos.py`(自建流交给 liblzma 判决,
/// prev=0 被接受、prev=实际前一字节被拒)。
fn LzmaState::decode_literal(
  self : LzmaState,
  rc : RangeDecoder,
  out : Array[Byte],
  dict_base : Int32,
) -> Unit raise SevenZError {
  let produced = out.length() - dict_base
  let prev = if produced > 0 {
    out.unsafe_get(out.length() - 1).to_int()
  } else {
    0
  }
  let prev_pos = produced & ((1 << self.lp) - 1)
  let base = 0x300 * (((prev_pos << 8) + prev) >> (8 - self.lc))
  let mut symbol = 1
  if self.state >= 7 {
    // 上一个符号是匹配:用字典里距离 rep0 处的字节做上下文。
    if self.rep0 >= produced {
      raise LzmaCorrupt(
        reason="字面值的 match byte 越出字典窗口:距离 " +
          (self.rep0 + 1).to_string() +
          ",窗口 " +
          produced.to_string(),
      )
    }
    let mut match_byte = out.unsafe_get(out.length() - self.rep0 - 1).to_int()
    while symbol < 0x100 {
      let match_bit = (match_byte >> 7) & 1
      match_byte = (match_byte << 1) & 0xFF
      // 三个半区:0x100 提示位 0 / 0x200 提示位 1;分岔后改走 0x000 半区(下面的普通树)。
      let idx = base + 0x100 + (if match_bit == 1 { 0x100 } else { 0 }) + symbol
      let b = rc.bit(self.literal, idx)
      symbol = (symbol << 1) | b
      if match_bit != b {
        break
      }
    }
  }
  while symbol < 0x100 {
    symbol = (symbol << 1) | rc.bit(self.literal, base + symbol)
  }
  out.push((symbol & 0xFF).to_byte())
}

///|
/// 解一个长度符号。返回值就是拷贝字节数(>= 2,规范里已经含 kMatchMinLen,
/// 调用方不要再加 2)。
///
/// 长度解码器布局:choice / choice2 两个选择位,然后 low[16][8](长度 2..9)、
/// mid[16][8](10..17)、high[256](18..273),low / mid 按 pos_state 索引。
fn lzma_decode_len(
  rc : RangeDecoder,
  probs : Array[Int32],
  pos_state : Int32,
) -> Int32 raise SevenZError {
  if rc.bit(probs, 0) == 0 {
    return 2 + rc.tree(probs, 2 + pos_state * 8, 3)
  }
  if rc.bit(probs, 1) == 0 {
    return 10 + rc.tree(probs, 2 + 16 * 8 + pos_state * 8, 3)
  }
  18 + rc.tree(probs, 2 + 16 * 8 + 16 * 8, 8)
}

///|
/// 把字典里距离 `dist`(= rep0 + 1)处的 `length` 个字节拷到 `out`。
///
/// 逐字节拷贝天然处理重叠匹配(dist < length 时的 RLE),并且一旦到达
/// `limit` 就停下——匹配可以跨过分块边界,但输出长度以声明值为准。
fn lzma_copy_match(
  out : Array[Byte],
  dict_base : Int32,
  dist : Int32,
  length : Int32,
  limit : Int32,
) -> Unit raise SevenZError {
  let window = out.length() - dict_base
  if dist >= window {
    raise LzmaCorrupt(
      reason="匹配距离越出字典窗口:距离 " +
        (dist + 1).to_string() +
        ",窗口 " +
        window.to_string(),
    )
  }
  let src = out.length() - dist - 1
  let mut i = 0
  while i < length && out.length() < limit {
    out.push(out.unsafe_get(src + i))
    i += 1
  }
}

///|
/// 从 `rc` 解到 `out` 的长度达到 `limit` 为止。
///
/// 这是 LZMA 的主状态机:字面值 / 匹配 / 重复匹配三分支,其状态迁移与 LZMA SDK 一致。
fn LzmaState::decode_chunk(
  self : LzmaState,
  rc : RangeDecoder,
  out : Array[Byte],
  dict_base : Int32,
  limit : Int32,
) -> Unit raise SevenZError {
  while out.length() < limit {
    // pos_state 与字面值上下文一样,按"自上次字典复位以来的字节数"计(见 decode_literal)。
    let pos_state = (out.length() - dict_base) & ((1 << self.pb) - 1)
    if rc.bit(self.is_match, self.state * 16 + pos_state) == 0 {
      self.decode_literal(rc, out, dict_base)
      let st = self.state
      self.state = if st < 4 { 0 } else if st < 10 { st - 3 } else { st - 6 }
      continue
    }
    let mut length = 0
    if rc.bit(self.is_rep, self.state) != 0 {
      if rc.bit(self.is_rep_g0, self.state) == 0 {
        if rc.bit(self.is_rep0_long, self.state * 16 + pos_state) == 0 {
          // 短重复:拷 1 字节,不重排 reps。
          self.state = if self.state < 7 { 9 } else { 11 }
          lzma_copy_match(out, dict_base, self.rep0, 1, limit)
          continue
        }
      } else {
        let dist = if rc.bit(self.is_rep_g1, self.state) == 0 {
          self.rep1
        } else if rc.bit(self.is_rep_g2, self.state) == 0 {
          self.rep2
        } else {
          let d = self.rep3
          self.rep3 = self.rep2
          d
        }
        self.rep2 = self.rep1
        self.rep1 = self.rep0
        self.rep0 = dist
      }
      length = lzma_decode_len(rc, self.rep_len, pos_state)
      self.state = if self.state < 7 { 8 } else { 11 }
    } else {
      self.rep3 = self.rep2
      self.rep2 = self.rep1
      self.rep1 = self.rep0
      length = lzma_decode_len(rc, self.len, pos_state)
      self.state = if self.state < 7 { 7 } else { 10 }
      self.rep0 = self.decode_distance(rc, length)
    }
    lzma_copy_match(out, dict_base, self.rep0, length, limit)
  }
}

///|
/// 解新匹配的距离(写入并返回 rep0)。
fn LzmaState::decode_distance(
  self : LzmaState,
  rc : RangeDecoder,
  length : Int32,
) -> Int32 raise SevenZError {
  let mut len_state = length - 2
  if len_state >= NUM_LEN_TO_POS_STATES {
    len_state = NUM_LEN_TO_POS_STATES - 1
  }
  let pos_slot = rc.tree(
    self.pos_slot,
    len_state * (1 << NUM_POS_SLOT_BITS),
    NUM_POS_SLOT_BITS,
  )
  if pos_slot < 4 {
    return pos_slot
  }
  let num_direct = (pos_slot >> 1) - 1
  let base = (2 | (pos_slot & 1)) << num_direct
  if pos_slot < END_POS_MODEL_INDEX {
    base + rc.reverse_tree(self.pos_special, base - pos_slot - 1, num_direct)
  } else {
    base +
    (rc.direct_bits(num_direct - NUM_ALIGN_BITS) << NUM_ALIGN_BITS) +
    rc.reverse_tree(self.align, 0, NUM_ALIGN_BITS)
  }
}

///|
/// 校验 props 首字节给出的 lc / lp / pb。
fn lzma_check_lclppb(value : Int32, what : String) -> Unit raise SevenZError {
  let lc = value % 9
  let lp = value / 9 % 5
  let pb = value / 45
  if pb > 4 || lc + lp > 4 {
    raise LzmaCorrupt(
      reason=what +
        " 非法 props " +
        hex_byte(value) +
        "(lc=" +
        lc.to_string() +
        " lp=" +
        lp.to_string() +
        " pb=" +
        pb.to_string() +
        ")",
    )
  }
}

///|
/// 校验 unpack_size 并转成 Int。
fn lzma_check_size(unpack_size : Int64) -> Int32 raise SevenZError {
  if unpack_size < 0L {
    raise LzmaCorrupt(
      reason="声明的解压大小为负:" + unpack_size.to_string(),
    )
  }
  if unpack_size > MAX_UNPACK_SIZE {
    raise Unsupported(
      what="解压大小超过 2 GiB:" + unpack_size.to_string(),
    )
  }
  unpack_size.to_int()
}

///|
/// 解一段 LZMA1 裸流(7z 的 0x030101 coder)。
///
/// `props` 5 字节;`data` 是裸码流(无 ALONE 头、无 EOS);
/// 输出长度由 `unpack_size` 决定,尾部多余的输入字节被容忍。
pub fn lzma1_decode(
  props : Bytes,
  data : Bytes,
  unpack_size : Int64,
) -> Bytes raise SevenZError {
  if props.length() < 5 {
    raise LzmaCorrupt(
      reason="LZMA1 props 需要 5 字节,实际 " +
        props.length().to_string(),
    )
  }
  let d = props.unsafe_get(0).to_int()
  lzma_check_lclppb(d, "LZMA1")
  let limit = lzma_check_size(unpack_size)
  if limit == 0 {
    return Bytes::new(0)
  }
  let state = LzmaState::new(d % 9, d / 9 % 5, d / 45)
  let out : Array[Byte] = []
  let rc = RangeDecoder::new(data, 0, data.length())
  state.decode_chunk(rc, out, 0, limit)
  Bytes::from_array(out[:])
}

///|
/// 解一段 LZMA2 流(7z 的 0x21 coder)。
///
/// `props` 1 字节字典大小编码;`data` 是含分块头的完整流。
/// 输出达到 `unpack_size` 即返回,结尾的 0x00 结束标记与尾部多余字节都可以缺失。
pub fn lzma2_decode(
  props : Bytes,
  data : Bytes,
  unpack_size : Int64,
) -> Bytes raise SevenZError {
  if props.length() < 1 {
    raise LzmaCorrupt(reason="LZMA2 props 需要 1 字节")
  }
  let dict_code = props.unsafe_get(0).to_int()
  if dict_code > 40 {
    raise LzmaCorrupt(
      reason="LZMA2 字典大小编码非法:" + hex_byte(dict_code),
    )
  }
  let limit = lzma_check_size(unpack_size)
  let out : Array[Byte] = []
  let mut pos = 0
  let mut dict_base = 0
  let mut state : LzmaState? = None
  while out.length() < limit {
    if pos >= data.length() {
      raise LzmaCorrupt(reason="LZMA2 码流提前结束:缺少分块头")
    }
    let ctrl = data.unsafe_get(pos).to_int()
    pos += 1
    if ctrl == 0 {
      raise LzmaCorrupt(reason="LZMA2 提前出现结束标记")
    }
    if ctrl < 0x80 {
      // 未压缩块:0x01 / 0x02。
      //
      // 这两个控制值里哪个表示"复位字典"各家文档与实现并不一致,实测 liblzma 只接受
      // "首块用 0x01"(用 0x02 作首块会被拒),与常见文档相反。这里刻意不对未压缩块
      // 复位窗口:窗口只影响"是否接受跨块引用",放宽不会误拒合法归档;而真正影响
      // 解码结果的是 LZMA 块自身的字典复位(ctrl >= 0xE0,见下),它决定复位之后
      // 第一个字面值的 prev 取 0(已用 liblzma 判决实验确认)。
      if ctrl != 1 && ctrl != 2 {
        raise LzmaCorrupt(reason="LZMA2 保留控制字节 " + hex_byte(ctrl))
      }
      if pos + 2 > data.length() {
        raise LzmaCorrupt(reason="LZMA2 未压缩块头被截断")
      }
      let size = (data.unsafe_get(pos).to_int() << 8) +
        data.unsafe_get(pos + 1).to_int() +
        1
      pos += 2
      if pos + size > data.length() {
        raise LzmaCorrupt(
          reason="LZMA2 未压缩块数据被截断:需要 " +
            size.to_string() +
            " 字节,实际 " +
            (data.length() - pos).to_string(),
        )
      }
      if size > limit - out.length() {
        raise LzmaCorrupt(
          reason="LZMA2 未压缩块超出声明的解压大小",
        )
      }
      let mut i = 0
      while i < size {
        out.push(data.unsafe_get(pos + i))
        i += 1
      }
      pos += size
      continue
    }
    if pos + 4 > data.length() {
      raise LzmaCorrupt(
        reason="LZMA2 分块头被截断:剩余 " +
          (data.length() - pos).to_string() +
          " 字节(需要 4)",
      )
    }
    let unpack = ((ctrl & 0x1F) << 16) +
      (data.unsafe_get(pos).to_int() << 8) +
      data.unsafe_get(pos + 1).to_int() +
      1
    let packed = (data.unsafe_get(pos + 2).to_int() << 8) +
      data.unsafe_get(pos + 3).to_int() +
      1
    pos += 4
    if ctrl >= 0xC0 {
      if pos >= data.length() {
        raise LzmaCorrupt(reason="LZMA2 分块 props 字节缺失")
      }
      let p = data.unsafe_get(pos).to_int()
      pos += 1
      lzma_check_lclppb(p, "LZMA2 分块")
      match state {
        Some(st) => st.set_props(p % 9, p / 9 % 5, p / 45)
        None => state = Some(LzmaState::new(p % 9, p / 9 % 5, p / 45))
      }
    } else if ctrl >= 0xA0 {
      match state {
        Some(st) => st.reset()
        None =>
          raise LzmaCorrupt(
            reason="LZMA2 首个 LZMA 块既无 props 也无 state reset",
          )
      }
    }
    if ctrl >= 0xE0 {
      dict_base = out.length()
    }
    let st = match state {
      Some(st) => st
      None =>
        raise LzmaCorrupt(reason="LZMA2 首个 LZMA 块必须携带 props")
    }
    if pos + packed > data.length() {
      raise LzmaCorrupt(
        reason="LZMA2 分块数据被截断:需要 " +
          packed.to_string() +
          " 字节,实际 " +
          (data.length() - pos).to_string(),
      )
    }
    if unpack > limit - out.length() {
      raise LzmaCorrupt(
        reason="LZMA2 分块声明的解压大小超出剩余总量",
      )
    }
    let rc = RangeDecoder::new(data, pos, pos + packed)
    st.decode_chunk(rc, out, dict_base, out.length() + unpack)
    pos += packed
  }
  Bytes::from_array(out[:])
}