// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
fn decode_frame_into(
src : Bytes,
start : Int,
out : Array[Byte],
) -> Int raise ZstdError {
decode_frame_into_with_history(src, start, out, b"")
}
///|
fn decode_frame_into_with_history(
src : Bytes,
start : Int,
out : Array[Byte],
history : Bytes,
) -> Int raise ZstdError {
decode_frame_into_with_dictionary_state(
src,
start,
out,
raw_dictionary_state(history),
)
}
///|
fn decode_frame_into_with_dictionary(
src : Bytes,
start : Int,
out : Array[Byte],
dictionary : Bytes,
) -> Int raise ZstdError {
decode_frame_into_with_dictionary_state(
src,
start,
out,
parse_dictionary_state(dictionary),
)
}
///|
fn decode_frame_into_with_dictionary_state(
src : Bytes,
start : Int,
out : Array[Byte],
dictionary_state : DictionaryState,
) -> Int raise ZstdError {
let src_len = src.length()
ensure_range(src_len, start, 4)
let magic = read_u32_le(src, start)
if (magic & skippable_magic_mask) == skippable_magic_start {
ensure_range(src_len, start, 8)
let user_size = read_u32_le(src, start + 4).to_uint64()
let frame_size = user_size + (8 : UInt64)
let remaining = (src_len - start).to_uint64()
if frame_size > remaining {
raise SrcSizeWrong
}
return start + frame_size.to_int()
}
if magic != zstd_magic_number {
raise CorruptionDetected
}
ensure_range(src_len, start, 5)
let descriptor = src[start + 4].to_uint()
if ((descriptor >> 3) & 1) == 1 {
raise CorruptionDetected
}
let frame_content_size_flag = descriptor >> 6
let single_segment = ((descriptor >> 5) & 1) == 1
let checksum_flag = ((descriptor >> 2) & 1) == 1
let dict_id_flag = descriptor & 0x3
let mut pos = start + 5
let mut frame_window_size = 0
if !single_segment {
ensure_range(src_len, pos, 1)
let window_size_u = window_size_from_descriptor(src[pos].to_uint())
if window_size_u == 0 || window_size_u > (1 : UInt64) << 62 {
raise CorruptionDetected
}
frame_window_size = window_size_u.to_int()
pos = pos + 1
}
let dict_size = dict_id_size(dict_id_flag)
ensure_range(src_len, pos, dict_size)
let frame_dict_id = if dict_size == 0 {
(0 : UInt)
} else if dict_size == 1 {
src[pos].to_uint()
} else if dict_size == 2 {
src[pos].to_uint() + (src[pos + 1].to_uint() << 8)
} else if dict_size == 4 {
read_u32_le(src, pos)
} else {
raise CorruptionDetected
}
pos = pos + dict_size
if dict_size > 0 {
if !dictionary_state.has_dictionary {
raise DictionaryRequired(frame_dict_id)
}
if dictionary_state.dict_id != 0 &&
dictionary_state.dict_id != frame_dict_id {
raise DictionaryRequired(frame_dict_id)
}
}
let fcs_size = frame_content_size_size(
frame_content_size_flag, single_segment,
)
ensure_range(src_len, pos, fcs_size)
let mut has_content_size = false
let mut content_size : UInt64 = 0
match fcs_size {
0 => ()
1 => {
has_content_size = true
content_size = src[pos].to_uint().to_uint64()
}
2 => {
has_content_size = true
content_size = src[pos].to_uint().to_uint64() +
(src[pos + 1].to_uint().to_uint64() << 8) +
(256 : UInt64)
}
4 => {
has_content_size = true
content_size = read_u32_le(src, pos).to_uint64()
}
8 => {
has_content_size = true
content_size = read_u64_le(src, pos)
}
_ => raise CorruptionDetected
}
pos = pos + fcs_size
let mut decoded_size : UInt64 = 0
let mut last_block = false
let mut rep1 = dictionary_state.rep1
let mut rep2 = dictionary_state.rep2
let mut rep3 = dictionary_state.rep3
let frame_out_start = out.length()
let prev_huf_valid : Ref[Bool] = { val: dictionary_state.huf_valid }
let prev_huf_max_bits : Ref[Int] = { val: dictionary_state.huf_max_bits }
let prev_huf_left : Ref[Array[Int]] = { val: dictionary_state.huf_left }
let prev_huf_right : Ref[Array[Int]] = { val: dictionary_state.huf_right }
let prev_huf_symbol : Ref[Array[Int]] = { val: dictionary_state.huf_symbol }
let prev_ll_valid : Ref[Bool] = { val: dictionary_state.ll_valid }
let prev_ll_kind : Ref[Int] = { val: dictionary_state.ll_kind }
let prev_off_valid : Ref[Bool] = { val: dictionary_state.off_valid }
let prev_off_kind : Ref[Int] = { val: dictionary_state.off_kind }
let prev_ml_valid : Ref[Bool] = { val: dictionary_state.ml_valid }
let prev_ml_kind : Ref[Int] = { val: dictionary_state.ml_kind }
let prev_ll_code : Ref[UInt] = { val: dictionary_state.ll_code }
let prev_off_code : Ref[UInt] = { val: dictionary_state.off_code }
let prev_ml_code : Ref[UInt] = { val: dictionary_state.ml_code }
let prev_ll_table_log : Ref[Int] = { val: dictionary_state.ll_table_log }
let prev_off_table_log : Ref[Int] = { val: dictionary_state.off_table_log }
let prev_ml_table_log : Ref[Int] = { val: dictionary_state.ml_table_log }
let prev_ll_table_next_state : Ref[Array[Int]] = {
val: dictionary_state.ll_table_next_state,
}
let prev_ll_table_nb_add_bits : Ref[Array[Int]] = {
val: dictionary_state.ll_table_nb_add_bits,
}
let prev_ll_table_nb_bits : Ref[Array[Int]] = {
val: dictionary_state.ll_table_nb_bits,
}
let prev_ll_table_base_values : Ref[Array[Int]] = {
val: dictionary_state.ll_table_base_values,
}
let prev_off_table_next_state : Ref[Array[Int]] = {
val: dictionary_state.off_table_next_state,
}
let prev_off_table_nb_add_bits : Ref[Array[Int]] = {
val: dictionary_state.off_table_nb_add_bits,
}
let prev_off_table_nb_bits : Ref[Array[Int]] = {
val: dictionary_state.off_table_nb_bits,
}
let prev_off_table_base_values : Ref[Array[Int]] = {
val: dictionary_state.off_table_base_values,
}
let prev_ml_table_next_state : Ref[Array[Int]] = {
val: dictionary_state.ml_table_next_state,
}
let prev_ml_table_nb_add_bits : Ref[Array[Int]] = {
val: dictionary_state.ml_table_nb_add_bits,
}
let prev_ml_table_nb_bits : Ref[Array[Int]] = {
val: dictionary_state.ml_table_nb_bits,
}
let prev_ml_table_base_values : Ref[Array[Int]] = {
val: dictionary_state.ml_table_base_values,
}
while !last_block {
ensure_range(src_len, pos, 3)
let block_header : UInt = src[pos].to_uint() +
(src[pos + 1].to_uint() << 8) +
(src[pos + 2].to_uint() << 16)
pos = pos + 3
last_block = (block_header & 1) == 1
let block_type = (block_header >> 1) & 0x3
let block_size_u = (block_header >> 3).to_uint64()
if block_size_u > zstd_block_size_max {
raise CorruptionDetected
}
let block_size = block_size_u.to_int()
match block_type {
0 => {
ensure_range(src_len, pos, block_size)
let mut i = 0
while i < block_size {
out.push(src[pos + i])
i = i + 1
}
pos = pos + block_size
decoded_size = decoded_size + block_size_u
}
1 => {
ensure_range(src_len, pos, 1)
let value = src[pos]
pos = pos + 1
let mut i = 0
while i < block_size {
out.push(value)
i = i + 1
}
decoded_size = decoded_size + block_size_u
}
2 => {
let (next_pos, produced, next_rep1, next_rep2, next_rep3) = decode_compressed_block_minimal(
src,
pos,
block_size,
out,
frame_out_start,
dictionary_state.history,
rep1,
rep2,
rep3,
prev_huf_valid,
prev_huf_max_bits,
prev_huf_left,
prev_huf_right,
prev_huf_symbol,
prev_ll_valid,
prev_ll_kind,
prev_ll_code,
prev_ll_table_log,
prev_ll_table_next_state,
prev_ll_table_nb_add_bits,
prev_ll_table_nb_bits,
prev_ll_table_base_values,
prev_off_valid,
prev_off_kind,
prev_off_code,
prev_off_table_log,
prev_off_table_next_state,
prev_off_table_nb_add_bits,
prev_off_table_nb_bits,
prev_off_table_base_values,
prev_ml_valid,
prev_ml_kind,
prev_ml_code,
prev_ml_table_log,
prev_ml_table_next_state,
prev_ml_table_nb_add_bits,
prev_ml_table_nb_bits,
prev_ml_table_base_values,
window_size=frame_window_size,
)
pos = next_pos
decoded_size = decoded_size + produced
rep1 = next_rep1
rep2 = next_rep2
rep3 = next_rep3
}
_ => raise CorruptionDetected
}
}
if checksum_flag {
ensure_range(src_len, pos, 4)
let expected_checksum = read_u32_le(src, pos)
let frame_bytes = Bytes::from_array(out)[frame_out_start:].to_owned()
let actual_checksum = (xxh64(frame_bytes) & (0xFFFF_FFFF : UInt64)).to_uint()
if actual_checksum != expected_checksum {
raise CorruptionDetected
}
pos = pos + 4
}
if has_content_size && decoded_size != content_size {
raise CorruptionDetected
}
pos
}