use reth_trie_common::{Nibbles, ProofV2Target};

// A helper function for getting the largest prefix of the sub-trie which contains a particular
// target, based on its `min_len`.
//
// A target will only match nodes which share the target's prefix, where the target's prefix is
// the first `min_len` nibbles of its key. E.g. a target with `key` 0xabcd and `min_len` 2 will
// only match nodes with prefix 0xab.
//
// In general the target will only match within the sub-trie whose prefix is identical to the
// target's. However there is an exception:
//
// Given a trie with a node at 0xabc, there must be a branch at 0xab. A target with prefix 0xabc
// needs to match that node, but the branch at 0xab must be constructed order to know the node
// is at that path. Therefore the sub-trie prefix is the target prefix with a nibble truncated.
//
// For a target with an empty prefix (`min_len` of 0) we still use an empty sub-trie prefix;
// this will still construct the branch at the root node (if there is one). Targets with
// `min_len` of both 0 and 1 will therefore construct the root node, but only those with
// `min_len` of 0 will retain it.
#[inline]
pub(crate) fn sub_trie_prefix(target: &ProofV2Target) -> Nibbles {
    let mut sub_trie_prefix = target.key_nibbles;
    sub_trie_prefix.truncate(target.min_len.saturating_sub(1) as usize);
    sub_trie_prefix
}

// A helper function which returns the first path following a sub-trie in lexicographical order.
#[inline]
fn sub_trie_upper_bound(sub_trie_prefix: &Nibbles) -> Option<Nibbles> {
    sub_trie_prefix.next_without_prefix()
}

/// Describes a set of targets which all apply to a single sub-trie, ie a section of the overall
/// trie whose nodes all share a prefix.
pub(crate) struct SubTrieTargets<'a> {
    /// The prefix which all nodes in the sub-trie share. This is also the first node in the trie
    /// in lexicographic order.
    pub(crate) prefix: Nibbles,
    /// The targets belonging to this sub-trie. These will be sorted by their `key` field,
    /// lexicographically.
    pub(crate) targets: &'a [ProofV2Target],
    /// Will be true if at least one target in the set has a zero `min_len`.
    ///
    /// If this is true then `prefix.is_empty()`, though not necessarily vice-versa.
    pub(crate) retain_root: bool,
}

impl<'a> SubTrieTargets<'a> {
    // A helper function which returns the first path following a sub-trie in lexicographical order.
    #[inline]
    pub(crate) fn upper_bound(&self) -> Option<Nibbles>

{
        sub_trie_upper_bound(&self.prefix)
    }

}

/// Given a set of [`ProofV2Target`]s, returns an iterator over those same [`ProofV2Target`]s
/// chunked by the sub-tries they apply to within the overall trie.
pub(crate) fn iter_sub_trie_targets(
    targets: &mut [ProofV2Target],
) -> impl Iterator<Item = SubTrieTargets<'_>> {
    // First sort by the sub-trie prefix of each target, falling back to the `min_len` in cases
    // where the sub-trie prefixes are equal (to differentiate targets which match the root node and
    // those which don't).
    targets.sort_unstable_by(|a, b| {
        sub_trie_prefix(a).cmp(&sub_trie_prefix(b)).then_with(|| a.min_len.cmp(&b.min_len))
    });

    // We now chunk targets, such that each chunk contains all targets belonging to the same
    // sub-trie. We are taking advantage of the following properties:
    //
    // - The first target in the chunk has the shortest sub-trie prefix (see previous sorting step).
    //
    // - The upper bound of the first target in the chunk's sub-trie will therefore be the upper
    //   bound of the whole chunk.
    //      - For example, given a chunk with sub-trie prefixes [0x2, 0x2f, 0x2fa], the upper bounds
    //        will be [0x3, 0x3, 0x2fb]. Note that no target could match a trie node with path equal
    //        to or greater than 0x3.
    //
    // - If a target's sub-trie's prefix does not lie within the bounds of the current chunk, then
    //   that target must be the first target of the next chunk, lying in a separate sub-trie.
    //      - Example: given sub-trie prefixes of [0x2, 0x2fa, 0x4c, 0x4ce, 0x4e], we would end up
    //        with the following chunks:
    //          - [0x2, 0x2fa] w/ upper bound 0x3
    //          - [0x4c 0x4ce] w/ upper bound 0x4d
    //          - [0x4e]       w/ upper bound 0x4f
    let mut upper_bound = targets.first().and_then(|t| sub_trie_upper_bound(&sub_trie_prefix(t)));
    let target_chunks = targets.chunk_by_mut(move |_, next| {
        if let Some(some_upper_bound) = upper_bound {
            let prefix = sub_trie_prefix(next);
            let same_chunk = prefix < some_upper_bound;
            if !same_chunk {
                upper_bound = sub_trie_upper_bound(&prefix);
            }
            same_chunk
        } else {
            true
        }
    });

    // Map the chunks to the return type. Within each chunk we want targets to be sorted by their
    // key, as that will be the order they are checked by the `ProofCalculator`.
    target_chunks.map(move |targets| {
        let prefix = sub_trie_prefix(&targets[0]);
        let retain_root = targets[0].min_len == 0;
        targets.sort_unstable_by_key(|target| target.key_nibbles);
        SubTrieTargets { prefix, targets, retain_root }
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::B256;

    #[test]
    fn test_iter_sub_trie_targets() {
        // Helper to create nibbles from hex string (each character is a nibble)
        let nibbles = |hex: &str| -> Nibbles {
            if hex.is_empty() {
                return Nibbles::new();
            }
            format!("0x{}", hex).parse().expect("valid nibbles hex string")
        };

        // Test cases: (input_targets, expected_output)
        // Expected output format: Vec<(exp_prefix_hex, Vec<key_hex>)>
        let test_cases = vec![
            // Case 1: Empty targets
            (vec![], vec![]),
            // Case 2: Single target without min_len
            (
                vec![ProofV2Target::new(B256::repeat_byte(0x20))],
                vec![(
                    "",
                    vec!["2020202020202020202020202020202020202020202020202020202020202020"],
                )],
            ),
            // Case 3: Multiple targets in same sub-trie (no min_len)
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)),
                    ProofV2Target::new(B256::repeat_byte(0x21)),
                ],
                vec![(
                    "",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2121212121212121212121212121212121212121212121212121212121212121",
                    ],
                )],
            ),
            // Case 4: Multiple targets in different sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    ("2", vec!["2020202020202020202020202020202020202020202020202020202020202020"]),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 5: Three targets, two in same sub-trie, one separate
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    (
                        "2",
                        vec![
                            "2020202020202020202020202020202020202020202020202020202020202020",
                            "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                        ],
                    ),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 6: Targets with different min_len values in same sub-trie
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(3),
                ],
                vec![(
                    "2",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                    ],
                )],
            ),
            // Case 7: More complex chunking with multiple sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(4),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(3),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(4),
                    ProofV2Target::new(B256::repeat_byte(0x4e)).with_min_len(3),
                ],
                vec![
                    (
                        "2",
                        vec![
                            "2020202020202020202020202020202020202020202020202020202020202020",
                            "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                        ],
                    ),
                    (
                        "4c",
                        vec![
                            "4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c",
                            "4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c",
                        ],
                    ),
                    (
                        "4e",
                        vec!["4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e"],
                    ),
                ],
            ),
            // Case 8: Min-len 1 should result in zero-length sub-trie prefix
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(1),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(1),
                ],


(1) as usize);
    sub_trie_prefix
}

// A helper function which returns the first path following a sub-trie in lexicographical order.
#[inline]
fn sub_trie_upper_bound(sub_trie_prefix: &Nibbles) -> Option<Nibbles> {
    sub_trie_prefix.next_without_prefix()
}

/// Describes a set of targets which all apply to a single sub-trie, ie a section of the overall
/// trie whose nodes all share a prefix.
pub(crate) struct SubTrieTargets<'a> {
    /// The prefix which all nodes in the sub-trie share. This is also the first node in the trie
    /// in lexicographic order.
    pub(crate) prefix: Nibbles,
    /// The targets belonging to this sub-trie. These will be sorted by their `key` field,
    /// lexicographically.
    pub(crate) targets: &'a [ProofV2Target],
    /// Will be true if at least one target in the set has a zero `min_len`.
    ///
    /// If this is true then `prefix.is_empty()`, though not necessarily vice-versa.
    pub(crate) retain_root: bool,
}

impl<'a> SubTrieTargets<'a> {
    // A helper function which returns the first path following a sub-trie in lexicographical order.
    #[inline]
    pub(crate) fn upper_bound(&self) -> Option<Nibbles> {
        sub_trie_upper_bound(&self.prefix)
    }
}

/// Given a set of [`ProofV2Target`]s, returns an iterator over those same [`ProofV2Target`]s
/// chunked by the sub-tries they apply to within the overall trie.
pub(crate) fn iter_sub_trie_targets(
    targets: &mut [ProofV2Target],
) -> impl Iterator<Item = SubTrieTargets<'_>> {
    // First sort by the sub-trie prefix of each target, falling back to the `min_len` in cases
    // where the sub-trie prefixes are equal (to differentiate targets which match the root node and
    // those which don't).
    targets.sort_unstable_by(|a, b| {
        sub_trie_prefix(a).cmp(&sub_trie_prefix(b)).then_with(|| a.min_len.cmp(&b.min_len))
    });

    // We now chunk targets, such that each chunk contains all targets belonging to the same
    // sub-trie. We are taking advantage of the following properties:
    //
    // - The first target in the chunk has the shortest sub-trie prefix (see previous sorting step).
    //
    // - The upper bound of the first target in the chunk's sub-trie will therefore be the upper
    //   bound of the whole chunk.
    //      - For example, given a chunk with sub-trie prefixes [0x2, 0x2f, 0x2fa], the upper bounds
    //        will be [0x3, 0x3, 0x2fb]. Note that no target could match a trie node with path equal
    //        to or greater than 0x3.
    //
    // - If a target's sub-trie's prefix does not lie within the bounds of the current chunk, then
    //   that target must be the first target of the next chunk, lying in a separate sub-trie.
    //      - Example: given sub-trie prefixes of [0x2, 0x2fa, 0x4c, 0x4ce, 0x4e], we would end up
    //        with the following chunks:
    //          - [0x2, 0x2fa] w/ upper bound 0x3
    //          - [0x4c 0x4ce] w/ upper bound 0x4d
    //          - [0x4e]       w/ upper bound 0x4f
    let mut upper_bound = targets.first().and_then(|t| sub_trie_upper_bound(&sub_trie_prefix(t)));
    let target_chunks = targets.chunk_by_mut(move |_, next| {
        if let Some(some_upper_bound) = upper_bound {
            let prefix = sub_trie_prefix(next);
            let same_chunk = prefix < some_upper_bound;
            if !same_chunk {
                upper_bound = sub_trie_upper_bound(&prefix);
            }
            same_chunk
        } else {
            true
        }
    });

    // Map the chunks to the return type. Within each chunk we want targets to be sorted by their
    // key, as that will be the order they are checked by the `ProofCalculator`.
    target_chunks.map(move |targets| {
        let prefix = sub_trie_prefix(&targets[0]);
        let retain_root = targets[0].min_len == 0;
        targets.sort_unstable_by_key(|target| target.key_nibbles);
        SubTrieTargets { prefix, targets, retain_root }
    })
}

#[cfg(test)]
mod tests {


use reth_trie_common::{Nibbles, ProofV2Target};

// A helper function for getting the largest prefix of the sub-trie which contains a particular
// target, based on its `min_len`.
//
// A target will only match nodes which share the target's prefix, where the target's prefix is
// the first `min_len` nibbles of its key. E.g. a target with `key` 0xabcd and `min_len` 2 will
// only match nodes with prefix 0xab.
//
// In general the target will only match within the sub-trie whose prefix is identical to the
// target's. However there is an exception:
//
// Given a trie with a node at 0xabc, there must be a branch at 0xab. A target with prefix 0xabc
// needs to match that node, but the branch at 0xab must be constructed order to know the node
// is at that path. Therefore the sub-trie prefix is the target prefix with a nibble truncated.
//
// For a target with an empty prefix (`min_len` of 0) we still use an empty sub-trie prefix;
// this will still construct the branch at the root node (if there is one). Targets with
// `min_len` of both 0 and 1 will therefore construct the root node, but only those with
// `min_len` of 0 will retain it.
#[inline]
pub(crate) fn sub_trie_prefix(target: &ProofV2Target) -> Nibbles {
    let mut sub_trie_prefix = target.key_nibbles;
    sub_trie_prefix.truncate(target.min_len.saturating_sub(1) as usize);
    sub_trie_prefix
}

// A helper function which returns the first path following a sub-trie in lexicographical order.
#[inline]
fn sub_trie_upper_bound(sub_trie_prefix: &Nibbles) -> Option<Nibbles> {
    sub_trie_prefix.next_without_prefix()
}

/// Describes a set of targets which all apply to a single sub-trie, ie a section of the overall
/// trie whose nodes all share a prefix.
pub(crate) struct SubTrieTargets<'a> {
    /// The prefix which all nodes in the sub-trie share. This is also the first node in the trie
    /// in lexicographic order.
    pub(crate) prefix: Nibbles,
    /// The targets belonging to this sub-trie. These will be sorted by their `key` field,
    /// lexicographically.
    pub(crate) targets: &'a [ProofV2Target],
    /// Will be true if at least one target in the set has a zero `min_len`.
    ///
    /// If this is true then `prefix.is_empty()`, though not necessarily vice-versa.
    pub(crate) retain_root: bool,
}

impl<'a> SubTrieTargets<'a> {
    // A helper function which returns the first path following a sub-trie in lexicographical order.
    #[inline]
    pub(crate) fn upper_bound(&self) -> Option<Nibbles> {
        sub_trie_upper_bound(&self.prefix)
    }
}

/// Given a set of [`ProofV2Target`]s, returns an iterator over those same [`ProofV2Target`]s
/// chunked by the sub-tries they apply to within the overall trie.
pub(crate) fn iter_sub_trie_targets(
    targets: &mut [ProofV2Target],
) -> impl Iterator<Item = SubTrieTargets<'_>> {
    // First sort by the sub-trie prefix of each target, falling back to the `min_len` in cases
    // where the sub-trie prefixes are equal (to differentiate targets which match the root node and
    // those which don't).
    targets.sort_unstable_by(|a, b| {
        sub_trie_prefix(a).cmp(&sub_trie_prefix(b)).then_with(|| a.min_len.cmp(&b.min_len))
    });

    // We now chunk targets, such that each chunk contains all targets belonging to the same
    // sub-trie. We are taking advantage of the following properties:
    //
    // - The first target in the chunk has the shortest sub-trie prefix (see previous sorting step).
    //
    // - The upper bound of the first target in the chunk's sub-trie will therefore be the upper
    //   bound of the whole chunk.
    //      - For example, given a chunk with sub-trie prefixes [0x2, 0x2f, 0x2fa], the upper bounds
    //        will be [0x3, 0x3, 0x2fb]. Note that no target could match a trie node with path equal
    //        to or greater than 0x3.
    //
    // - If a target's sub-trie's prefix does not lie within the bounds of the current chunk, then
    //   that target must be the first target of the next chunk, lying in a separate sub-trie.
    //      - Example: given sub-trie prefixes of [0x2, 0x2fa, 0x4c, 0x4ce, 0x4e], we would end up
    //        with the following chunks:
    //          - [0x2, 0x2fa] w/ upper bound 0x3
    //          - [0x4c 0x4ce] w/ upper bound 0x4d
    //          - [0x4e]       w/ upper bound 0x4f
    let mut upper_bound = targets.first().and_then(|t| sub_trie_upper_bound(&sub_trie_prefix(t)));
    let target_chunks = targets.chunk_by_mut(move |_, next| {
        if let Some(some_upper_bound) = upper_bound {
            let prefix = sub_trie_prefix(next);
            let same_chunk = prefix < some_upper_bound;
            if !same_chunk {
                upper_bound = sub_trie_upper_bound(&prefix);
            }
            same_chunk
        } else {
            true
        }
    });

    // Map the chunks to the return type. Within each chunk we want targets to be sorted by their
    // key, as that will be the order they are checked by the `ProofCalculator`.
    target_chunks.map(move |targets| {
        let prefix = sub_trie_prefix(&targets[0]);
        let retain_root = targets[0].min_len == 0;
        targets.sort_unstable_by_key(|target| target.key_nibbles);
        SubTrieTargets { prefix, targets, retain_root }
    })
}

#[cfg(test)]
mod tests {
    use super::*;


//! Test helper impls.

#![allow(dead_code)]

#[cfg(any(test, feature = "file-client"))]
use crate::{bodies::test_utils::create_raw_bodies, file_codec::BlockFileCodec};

use alloy_primitives::{map::B256Map, B256};
use reth_ethereum_primitives::BlockBody;
use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
use std::ops::RangeInclusive;

mod bodies_client;
pub use bodies_client::TestBodiesClient;
use reth_primitives_traits::SealedHeader;

/// Metrics scope used for testing.
pub(crate) const TEST_SCOPE: &str = "downloaders.test";

/// Generate a set of bodies and their corresponding block hashes
pub(crate) fn generate_bodies(
    range: RangeInclusive<u64>,
) -> (Vec<SealedHeader>, B256Map<BlockBody>) {
    let mut rng = generators::rng();
    let blocks = random_block_range(
        &mut rng,
        range,
        BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..2, ..Default::default() },
    );

    let headers = blocks.iter().map(|block| block.clone_sealed_header()).collect();
    let bodies = blocks.into_iter().map(|block| (block.hash(), block.into_body())).collect();

    (headers, bodies)
}

/// Generate a set of bodies, write them to a temporary file, and return the file along with the
/// bodies and corresponding block hashes
#[cfg(any(test, feature = "file-client"))]
pub(crate) async fn generate_bodies_file(
    range: RangeInclusive<u64>,
) -> (tokio::fs::File, Vec<SealedHeader>, B256Map<BlockBody>) {
    use futures::SinkExt;
    use std::io::SeekFrom;
    use tokio::{fs::File, io::AsyncSeekExt};
    use tokio_util::codec::FramedWrite;

    let (headers, bodies) = generate_bodies(range);
    let raw_block_bodies = create_raw_bodies(headers.iter().cloned(), &mut bodies.clone());

    let file: File = tempfile::tempfile().unwrap().into();
    let mut writer = FramedWrite::new(file, BlockFileCodec::default());

    // rlp encode one after the other
    for block in raw_block_bodies {
        writer.feed(block).await.unwrap();
    }
    writer.flush().await.unwrap();

    // get the file back
    let mut file: File = writer.into_inner();
    file.seek(SeekFrom::Start(0)).await.unwrap();
    (file, headers, bodies)
}


//! Test helper impls.

#![allow(dead_code)]

#[cfg(any(test, feature = "file-client"))]
use crate::{bodies::test_utils::create_raw_bodies, file_codec::BlockFileCodec};

use reth_ethereum_primitives::BlockBody;
use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
use std::ops::RangeInclusive;

mod bodies_client;
pub use bodies_client::TestBodiesClient;
use reth_primitives_traits::SealedHeader;

/// Metrics scope used for testing.
pub(crate) const TEST_SCOPE: &str = "downloaders.test";

/// Generate a set of bodies and their corresponding block hashes
pub(crate) fn generate_bodies(
    range: RangeInclusive<u64>,
) -> (Vec<SealedHeader>, B256Map<BlockBody>) {
    let mut rng = generators::rng();
    let blocks = random_block_range(
        &mut rng,
        range,
        BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..2, ..Default::default() },
    );

    let headers = blocks.iter().map(|block| block.clone_sealed_header()).collect();
    let bodies = blocks.into_iter().map(|block| (block.hash(), block.into_body())).collect();

    (headers, bodies)
}

/// Generate a set of bodies, write them to a temporary file, and return the file along with the
/// bodies and corresponding block hashes
#[cfg(any(test, feature = "file-client"))]
pub(crate) async fn generate_bodies_file(
    range: RangeInclusive<u64>,
) -> (tokio::fs::File, Vec<SealedHeader>, B256Map<BlockBody>) {
    use futures::SinkExt;
    use std::io::SeekFrom;
    use tokio::{fs::File, io::AsyncSeekExt};
    use tokio_util::codec::FramedWrite;

    let (headers, bodies) = generate_bodies(range);
    let raw_block_bodies = create_raw_bodies(headers.iter().cloned(), &mut bodies.clone());

    let file: File = tempfile::tempfile().unwrap().into();
    let mut writer = FramedWrite::new(file, BlockFileCodec::default());

    // rlp encode one after the other
    for block in raw_block_bodies {
        writer.feed(block).await.unwrap();
    }
    writer.flush().await.unwrap();

    // get the file back
    let mut file: File = writer.into_inner();
    file.seek(SeekFrom::Start(0)).await.unwrap();
    (file, headers, bodies)
}


//! Test helper impls.

#![allow(dead_code)]

#[cfg(any(test, feature = "file-client"))]
use crate::{bodies::test_utils::create_raw_bodies, file_codec::BlockFileCodec};
use alloy_primitives::{map::B256Map, B256};

use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
use std::ops::RangeInclusive;

mod bodies_client;
pub use bodies_client::TestBodiesClient;
use reth_primitives_traits::SealedHeader;

/// Metrics scope used for testing.
pub(crate) const TEST_SCOPE: &str = "downloaders.test";

/// Generate a set of bodies and their corresponding block hashes
pub(crate) fn generate_bodies(
    range: RangeInclusive<u64>,
) -> (Vec<SealedHeader>, B256Map<BlockBody>) {
    let mut rng = generators::rng();
    let blocks = random_block_range(
        &mut rng,
        range,
        BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..2, ..Default::default() },
    );

    let headers = blocks.iter().map(|block| block.clone_sealed_header()).collect();
    let bodies = blocks.into_iter().map(|block| (block.hash(), block.into_body())).collect();

    (headers, bodies)
}

/// Generate a set of bodies, write them to a temporary file, and return the file along with the
/// bodies and corresponding block hashes
#[cfg(any(test, feature = "file-client"))]
pub(crate) async fn generate_bodies_file(
    range: RangeInclusive<u64>,
) -> (tokio::fs::File, Vec<SealedHeader>, B256Map<BlockBody>) {
    use futures::SinkExt;
    use std::io::SeekFrom;
    use tokio::{fs::File, io::AsyncSeekExt};
    use tokio_util::codec::FramedWrite;

    let (headers, bodies) = generate_bodies(range);
    let raw_block_bodies = create_raw_bodies(headers.iter().cloned(), &mut bodies.clone());

    let file: File = tempfile::tempfile().unwrap().into();
    let mut writer = FramedWrite::new(file, BlockFileCodec::default());

    // rlp encode one after the other
    for block in raw_block_bodies {
        writer.feed(block).await.unwrap();
    }
    writer.flush().await.unwrap();

    // get the file back
    let mut file: File = writer.into_inner();
    file.seek(SeekFrom::Start(0)).await.unwrap();
    (file, headers, bodies)
}


//! Test helper impls.

#![allow(dead_code)]

#[cfg(any(test, feature = "file-client"))]
use crate::{bodies::test_utils::create_raw_bodies, file_codec::BlockFileCodec};
use alloy_primitives::{map::B256Map, B256};
use reth_ethereum_primitives::BlockBody;

use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};

use std::ops::RangeInclusive;

mod bodies_client;
pub use bodies_client::TestBodiesClient;
use reth_primitives_traits::SealedHeader;

/// Metrics scope used for testing.
pub(crate) const TEST_SCOPE: &str = "downloaders.test";

/// Generate a set of bodies and their corresponding block hashes
pub(crate) fn generate_bodies(
    range: RangeInclusive<u64>,
) -> (Vec<SealedHeader>, B256Map<BlockBody>) {
    let mut rng = generators::rng();
    let blocks = random_block_range(
        &mut rng,
        range,
        BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..2, ..Default::default() },
    );

    let headers = blocks.iter().map(|block| block.clone_sealed_header()).collect();
    let bodies = blocks.into_iter().map(|block| (block.hash(), block.into_body())).collect();

    (headers, bodies)
}

/// Generate a set of bodies, write them to a temporary file, and return the file along with the
/// bodies and corresponding block hashes
#[cfg(any(test, feature = "file-client"))]
pub(crate) async fn generate_bodies_file(
    range: RangeInclusive<u64>,
) -> (tokio::fs::File, Vec<SealedHeader>, B256Map<BlockBody>) {
    use futures::SinkExt;
    use std::io::SeekFrom;
    use tokio::{fs::File, io::AsyncSeekExt};
    use tokio_util::codec::FramedWrite;

    let (headers, bodies) = generate_bodies(range);
    let raw_block_bodies = create_raw_bodies(headers.iter().cloned(), &mut bodies.clone());

    let file: File = tempfile::tempfile().unwrap().into();
    let mut writer = FramedWrite::new(file, BlockFileCodec::default());

    // rlp encode one after the other
    for block in raw_block_bodies {
        writer.feed(block).await.unwrap();
    }
    writer.flush().await.unwrap();

    // get the file back
    let mut file: File = writer.into_inner();
    file.seek(SeekFrom::Start(0)).await.unwrap();
    (file, headers, bodies)
}


//! Test helper impls.

#![allow(dead_code)]

#[cfg(any(test, feature = "file-client"))]
use crate::{bodies::test_utils::create_raw_bodies, file_codec::BlockFileCodec};
use alloy_primitives::{map::B256Map, B256};
use reth_ethereum_primitives::BlockBody;
use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};

mod bodies_client;
pub use bodies_client::TestBodiesClient;
use reth_primitives_traits::SealedHeader;

/// Metrics scope used for testing.
pub(crate) const TEST_SCOPE: &str = "downloaders.test";

/// Generate a set of bodies and their corresponding block hashes
pub(crate) fn generate_bodies(
    range: RangeInclusive<u64>,
) -> (Vec<SealedHeader>, B256Map<BlockBody>) {
    let mut rng = generators::rng();
    let blocks = random_block_range(
        &mut rng,
        range,
        BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..2, ..Default::default() },
    );

    let headers = blocks.iter().map(|block| block.clone_sealed_header()).collect();
    let bodies = blocks.into_iter().map(|block| (block.hash(), block.into_body())).collect();

    (headers, bodies)
}

/// Generate a set of bodies, write them to a temporary file, and return the file along with the
/// bodies and corresponding block hashes
#[cfg(any(test, feature = "file-client"))]
pub(crate) async fn generate_bodies_file(
    range: RangeInclusive<u64>,
) -> (tokio::fs::File, Vec<SealedHeader>, B256Map<BlockBody>) {
    use futures::SinkExt;
    use std::io::SeekFrom;
    use tokio::{fs::File, io::AsyncSeekExt};
    use tokio_util::codec::FramedWrite;

    let (headers, bodies) = generate_bodies(range);
    let raw_block_bodies = create_raw_bodies(headers.iter().cloned(), &mut bodies.clone());

    let file: File = tempfile::tempfile().unwrap().into();
    let mut writer = FramedWrite::new(file, BlockFileCodec::default());

    // rlp encode one after the other
    for block in raw_block_bodies {
        writer.feed(block).await.unwrap();
    }
    writer.flush().await.unwrap();

    // get the file back
    let mut file: File = writer.into_inner();
    file.seek(SeekFrom::Start(0)).await.unwrap();
    (file, headers, bodies)
}


//! Test helper impls.

#![allow(dead_code)]

#[cfg(any(test, feature = "file-client"))]
use crate::{bodies::test_utils::create_raw_bodies, file_codec::BlockFileCodec};
use alloy_primitives::{map::B256Map, B256};
use reth_ethereum_primitives::BlockBody;
use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
use std::ops::RangeInclusive;

pub use bodies_client::TestBodiesClient;
use reth_primitives_traits::SealedHeader;

/// Metrics scope used for testing.
pub(crate) const TEST_SCOPE: &str = "downloaders.test";

/// Generate a set of bodies and their corresponding block hashes
pub(crate) fn generate_bodies(
    range: RangeInclusive<u64>,
) -> (Vec<SealedHeader>, B256Map<BlockBody>) {
    let mut rng = generators::rng();
    let blocks = random_block_range(
        &mut rng,
        range,
        BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..2, ..Default::default() },
    );

    let headers = blocks.iter().map(|block| block.clone_sealed_header()).collect();
    let bodies = blocks.into_iter().map(|block| (block.hash(), block.into_body())).collect();

    (headers, bodies)
}

/// Generate a set of bodies, write them to a temporary file, and return the file along with the
/// bodies and corresponding block hashes
#[cfg(any(test, feature = "file-client"))]
pub(crate) async fn generate_bodies_file(
    range: RangeInclusive<u64>,
) -> (tokio::fs::File, Vec<SealedHeader>, B256Map<BlockBody>) {
    use futures::SinkExt;
    use std::io::SeekFrom;
    use tokio::{fs::File, io::AsyncSeekExt};
    use tokio_util::codec::FramedWrite;

    let (headers, bodies) = generate_bodies(range);
    let raw_block_bodies = create_raw_bodies(headers.iter().cloned(), &mut bodies.clone());

    let file: File = tempfile::tempfile().unwrap().into();
    let mut writer = FramedWrite::new(file, BlockFileCodec::default());

    // rlp encode one after the other
    for block in raw_block_bodies {
        writer.feed(block).await.unwrap();
    }
    writer.flush().await.unwrap();

    // get the file back
    let mut file: File = writer.into_inner();
    file.seek(SeekFrom::Start(0)).await.unwrap();
    (file, headers, bodies)
}


//! Test helper impls.

#![allow(dead_code)]

#[cfg(any(test, feature = "file-client"))]
use crate::{bodies::test_utils::create_raw_bodies, file_codec::BlockFileCodec};
use alloy_primitives::{map::B256Map, B256};
use reth_ethereum_primitives::BlockBody;
use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
use std::ops::RangeInclusive;

mod bodies_client;

use reth_primitives_traits::SealedHeader;

/// Metrics scope used for testing.
pub(crate) const TEST_SCOPE: &str = "downloaders.test";

/// Generate a set of bodies and their corresponding block hashes
pub(crate) fn generate_bodies(
    range: RangeInclusive<u64>,
) -> (Vec<SealedHeader>, B256Map<BlockBody>) {
    let mut rng = generators::rng();
    let blocks = random_block_range(
        &mut rng,
        range,
        BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..2, ..Default::default() },
    );

    let headers = blocks.iter().map(|block| block.clone_sealed_header()).collect();
    let bodies = blocks.into_iter().map(|block| (block.hash(), block.into_body())).collect();

    (headers, bodies)
}

/// Generate a set of bodies, write them to a temporary file, and return the file along with the
/// bodies and corresponding block hashes
#[cfg(any(test, feature = "file-client"))]
pub(crate) async fn generate_bodies_file(
    range: RangeInclusive<u64>,
) -> (tokio::fs::File, Vec<SealedHeader>, B256Map<BlockBody>) {
    use futures::SinkExt;
    use std::io::SeekFrom;
    use tokio::{fs::File, io::AsyncSeekExt};
    use tokio_util::codec::FramedWrite;

    let (headers, bodies) = generate_bodies(range);
    let raw_block_bodies = create_raw_bodies(headers.iter().cloned(), &mut bodies.clone());

    let file: File = tempfile::tempfile().unwrap().into();
    let mut writer = FramedWrite::new(file, BlockFileCodec::default());

    // rlp encode one after the other
    for block in raw_block_bodies {
        writer.feed(block).await.unwrap();
    }
    writer.flush().await.unwrap();

    // get the file back
    let mut file: File = writer.into_inner();
    file.seek(SeekFrom::Start(0)).await.unwrap();
    (file, headers, bodies)
}


//! Test helper impls.

#![allow(dead_code)]

#[cfg(any(test, feature = "file-client"))]
use crate::{bodies::test_utils::create_raw_bodies, file_codec::BlockFileCodec};
use alloy_primitives::{map::B256Map, B256};
use reth_ethereum_primitives::BlockBody;
use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
use std::ops::RangeInclusive;

mod bodies_client;
pub use bodies_client::TestBodiesClient;

/// Metrics scope used for testing.
pub(crate) const TEST_SCOPE: &str = "downloaders.test";

/// Generate a set of bodies and their corresponding block hashes
pub(crate) fn generate_bodies(
    range: RangeInclusive<u64>,
) -> (Vec<SealedHeader>, B256Map<BlockBody>) {
    let mut rng = generators::rng();
    let blocks = random_block_range(
        &mut rng,
        range,
        BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..2, ..Default::default() },
    );

    let headers = blocks.iter().map(|block| block.clone_sealed_header()).collect();
    let bodies = blocks.into_iter().map(|block| (block.hash(), block.into_body())).collect();

    (headers, bodies)
}

/// Generate a set of bodies, write them to a temporary file, and return the file along with the
/// bodies and corresponding block hashes
#[cfg(any(test, feature = "file-client"))]
pub(crate) async fn generate_bodies_file(
    range: RangeInclusive<u64>,
) -> (tokio::fs::File, Vec<SealedHeader>, B256Map<BlockBody>) {
    use futures::SinkExt;
    use std::io::SeekFrom;
    use tokio::{fs::File, io::AsyncSeekExt};
    use tokio_util::codec::FramedWrite;

    let (headers, bodies) = generate_bodies(range);
    let raw_block_bodies = create_raw_bodies(headers.iter().cloned(), &mut bodies.clone());

    let file: File = tempfile::tempfile().unwrap().into();
    let mut writer = FramedWrite::new(file, BlockFileCodec::default());

    // rlp encode one after the other
    for block in raw_block_bodies {
        writer.feed(block).await.unwrap();
    }
    writer.flush().await.unwrap();

    // get the file back
    let mut file: File = writer.into_inner();
    file.seek(SeekFrom::Start(0)).await.unwrap();
    (file, headers, bodies)
}


//! Test helper impls.

#![allow(dead_code)]

#[cfg(any(test, feature = "file-client"))]
use crate::{bodies::test_utils::create_raw_bodies, file_codec::BlockFileCodec};
use alloy_primitives::{map::B256Map, B256};
use reth_ethereum_primitives::BlockBody;
use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
use std::ops::RangeInclusive;

mod bodies_client;
pub use bodies_client::TestBodiesClient;
use reth_primitives_traits::SealedHeader;

/// Metrics scope used for testing.
pub(crate) const TEST_SCOPE: &str = "downloaders.test";

/// Generate a set of bodies and their corresponding block hashes
pub(crate) fn generate_bodies(
    range: RangeInclusive<u64>,
) -> (Vec<SealedHeader>, B256Map<BlockBody>) {
    let mut rng = generators::rng();
    let blocks = random_block_range(
        &mut rng,
        range,
        BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..2, ..Default::default() },
    );

    let headers = blocks.iter().map(|block| block.clone_sealed_header()).collect();
    let bodies = blocks.into_iter().map(|block| (block.hash(), block.into_body())).collect();

    (headers, bodies)
}

/// Generate a set of bodies, write them to a temporary file, and return the file along with the
/// bodies and corresponding block hashes
#[cfg(any(test, feature = "file-client"))]
pub(crate) async fn generate_bodies_file(
    range: RangeInclusive<u64>,
) -> (tokio::fs::File, Vec<SealedHeader>, B256Map<BlockBody>) {
    use futures::SinkExt;
    use std::io::SeekFrom;
    use tokio::{fs::File, io::AsyncSeekExt};
    use tokio_util::codec::FramedWrite;

    let (headers, bodies) = generate_bodies(range);
    let raw_block_bodies = create_raw_bodies(headers.iter().cloned(), &mut bodies.clone());

    let file: File = tempfile::tempfile().unwrap().into();
    let mut writer = FramedWrite::new(file, BlockFileCodec::default());

    // rlp encode one after the other
    for block in raw_block_bodies {
        writer.feed(block).await.unwrap();
    }
    writer.flush().await.unwrap();

    // get the file back
    let mut file: File = writer.into_inner();
    file.seek(SeekFrom::Start(0)).await.unwrap();
    (file, headers, bodies)
}


//! Test helper impls.

#![allow(dead_code)]

#[cfg(any(test, feature = "file-client"))]
use crate::{bodies::test_utils::create_raw_bodies, file_codec::BlockFileCodec};
use alloy_primitives::{map::B256Map, B256};
use reth_ethereum_primitives::BlockBody;
use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
use std::ops::RangeInclusive;

mod bodies_client;
pub use bodies_client::TestBodiesClient;
use reth_primitives_traits::SealedHeader;

/// Metrics scope used for testing.
pub(crate) const TEST_SCOPE: &str = "downloaders.test";

/// Generate a set of bodies and their corresponding block hashes
pub(crate) fn generate_bodies(
    range: RangeInclusive<u64>,
) -> (Vec<SealedHeader>, B256Map<BlockBody>) {
    let mut rng = generators::rng();
    let blocks = random_block_range(
        &mut rng,
        range,
        BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..2, ..Default::default() },
    );

    let headers = blocks.iter().map(|block| block.clone_sealed_header()).collect();
    let bodies = blocks.into_iter().map(|block| (block.hash(), block.into_body())).collect();

    (headers, bodies)
}

/// Generate a set of bodies, write them to a temporary file, and return the file along with the
/// bodies and corresponding block hashes
#[cfg(any(test, feature = "file-client"))]
pub(crate) async fn generate_bodies_file(
    range: RangeInclusive<u64>,
) -> (tokio::fs::File, Vec<SealedHeader>, B256Map<BlockBody>) {

    use std::io::SeekFrom;
    use tokio::{fs::File, io::AsyncSeekExt};
    use tokio_util::codec::FramedWrite;

    let (headers, bodies) = generate_bodies(range);
    let raw_block_bodies = create_raw_bodies(headers.iter().cloned(), &mut bodies.clone());

    let file: File = tempfile::tempfile().unwrap().into();
    let mut writer = FramedWrite::new(file, BlockFileCodec::default());

    // rlp encode one after the other
    for block in raw_block_bodies {
        writer.feed(block).await.unwrap();
    }
    writer.flush().await.unwrap();

    // get the file back
    let mut file: File = writer.into_inner();
    file.seek(SeekFrom::Start(0)).await.unwrap();
    (file, headers, bodies)
}


//! Test helper impls.

#![allow(dead_code)]

#[cfg(any(test, feature = "file-client"))]
use crate::{bodies::test_utils::create_raw_bodies, file_codec::BlockFileCodec};
use alloy_primitives::{map::B256Map, B256};
use reth_ethereum_primitives::BlockBody;
use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
use std::ops::RangeInclusive;

mod bodies_client;
pub use bodies_client::TestBodiesClient;
use reth_primitives_traits::SealedHeader;

/// Metrics scope used for testing.
pub(crate) const TEST_SCOPE: &str = "downloaders.test";

/// Generate a set of bodies and their corresponding block hashes
pub(crate) fn generate_bodies(
    range: RangeInclusive<u64>,
) -> (Vec<SealedHeader>, B256Map<BlockBody>) {
    let mut rng = generators::rng();
    let blocks = random_block_range(
        &mut rng,
        range,
        BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..2, ..Default::default() },
    );

    let headers = blocks.iter().map(|block| block.clone_sealed_header()).collect();
    let bodies = blocks.into_iter().map(|block| (block.hash(), block.into_body())).collect();

    (headers, bodies)
}

/// Generate a set of bodies, write them to a temporary file, and return the file along with the
/// bodies and corresponding block hashes
#[cfg(any(test, feature = "file-client"))]
pub(crate) async fn generate_bodies_file(
    range: RangeInclusive<u64>,
) -> (tokio::fs::File, Vec<SealedHeader>, B256Map<BlockBody>) {
    use futures::SinkExt;

    use tokio::{fs::File, io::AsyncSeekExt};
    use tokio_util::codec::FramedWrite;

    let (headers, bodies) = generate_bodies(range);
    let raw_block_bodies = create_raw_bodies(headers.iter().cloned(), &mut bodies.clone());

    let file: File = tempfile::tempfile().unwrap().into();
    let mut writer = FramedWrite::new(file, BlockFileCodec::default());

    // rlp encode one after the other
    for block in raw_block_bodies {
        writer.feed(block).await.unwrap();
    }
    writer.flush().await.unwrap();

    // get the file back
    let mut file: File = writer.into_inner();
    file.seek(SeekFrom::Start(0)).await.unwrap();
    (file, headers, bodies)
}


//! Test helper impls.

#![allow(dead_code)]

#[cfg(any(test, feature = "file-client"))]
use crate::{bodies::test_utils::create_raw_bodies, file_codec::BlockFileCodec};
use alloy_primitives::{map::B256Map, B256};
use reth_ethereum_primitives::BlockBody;
use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
use std::ops::RangeInclusive;

mod bodies_client;
pub use bodies_client::TestBodiesClient;
use reth_primitives_traits::SealedHeader;

/// Metrics scope used for testing.
pub(crate) const TEST_SCOPE: &str = "downloaders.test";

/// Generate a set of bodies and their corresponding block hashes
pub(crate) fn generate_bodies(
    range: RangeInclusive<u64>,
) -> (Vec<SealedHeader>, B256Map<BlockBody>) {
    let mut rng = generators::rng();
    let blocks = random_block_range(
        &mut rng,
        range,
        BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..2, ..Default::default() },
    );

    let headers = blocks.iter().map(|block| block.clone_sealed_header()).collect();
    let bodies = blocks.into_iter().map(|block| (block.hash(), block.into_body())).collect();

    (headers, bodies)
}

/// Generate a set of bodies, write them to a temporary file, and return the file along with the
/// bodies and corresponding block hashes
#[cfg(any(test, feature = "file-client"))]
pub(crate) async fn generate_bodies_file(
    range: RangeInclusive<u64>,
) -> (tokio::fs::File, Vec<SealedHeader>, B256Map<BlockBody>) {
    use futures::SinkExt;
    use std::io::SeekFrom;

    use tokio_util::codec::FramedWrite;

    let (headers, bodies) = generate_bodies(range);
    let raw_block_bodies = create_raw_bodies(headers.iter().cloned(), &mut bodies.clone());

    let file: File = tempfile::tempfile().unwrap().into();
    let mut writer = FramedWrite::new(file, BlockFileCodec::default());

    // rlp encode one after the other
    for block in raw_block_bodies {
        writer.feed(block).await.unwrap();
    }
    writer.flush().await.unwrap();

    // get the file back
    let mut file: File = writer.into_inner();
    file.seek(SeekFrom::Start(0)).await.unwrap();
    (file, headers, bodies)
}


//! Test helper impls.

#![allow(dead_code)]

#[cfg(any(test, feature = "file-client"))]
use crate::{bodies::test_utils::create_raw_bodies, file_codec::BlockFileCodec};
use alloy_primitives::{map::B256Map, B256};
use reth_ethereum_primitives::BlockBody;
use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
use std::ops::RangeInclusive;

mod bodies_client;
pub use bodies_client::TestBodiesClient;
use reth_primitives_traits::SealedHeader;

/// Metrics scope used for testing.
pub(crate) const TEST_SCOPE: &str = "downloaders.test";

/// Generate a set of bodies and their corresponding block hashes
pub(crate) fn generate_bodies(
    range: RangeInclusive<u64>,
) -> (Vec<SealedHeader>, B256Map<BlockBody>) {
    let mut rng = generators::rng();
    let blocks = random_block_range(
        &mut rng,
        range,
        BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..2, ..Default::default() },
    );

    let headers = blocks.iter().map(|block| block.clone_sealed_header()).collect();
    let bodies = blocks.into_iter().map(|block| (block.hash(), block.into_body())).collect();

    (headers, bodies)
}

/// Generate a set of bodies, write them to a temporary file, and return the file along with the
/// bodies and corresponding block hashes
#[cfg(any(test, feature = "file-client"))]
pub(crate) async fn generate_bodies_file(
    range: RangeInclusive<u64>,
) -> (tokio::fs::File, Vec<SealedHeader>, B256Map<BlockBody>) {
    use futures::SinkExt;
    use std::io::SeekFrom;
    use tokio::{fs::File, io::AsyncSeekExt};

    let (headers, bodies) = generate_bodies(range);
    let raw_block_bodies = create_raw_bodies(headers.iter().cloned(), &mut bodies.clone());

    let file: File = tempfile::tempfile().unwrap().into();
    let mut writer = FramedWrite::new(file, BlockFileCodec::default());

    // rlp encode one after the other
    for block in raw_block_bodies {
        writer.feed(block).await.unwrap();
    }
    writer.flush().await.unwrap();

    // get the file back
    let mut file: File = writer.into_inner();
    file.seek(SeekFrom::Start(0)).await.unwrap();
    (file, headers, bodies)
}


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};

use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
        let this = Self { nibbles, node };
        (this, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(StorageTrieEntry);

/// Account storage trie node with packed nibble encoding (storage v2).
///
/// Same as [`StorageTrieEntry`] but uses [`PackedStoredNibblesSubKey`] (33 bytes)
/// instead of [`StoredNibblesSubKey`] (65 bytes).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct PackedStorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: PackedStoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for PackedStorageTrieEntry {
    type SubKey = PackedStoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for PackedStorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = PackedStoredNibblesSubKey::from_compact(buf, 33);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 33);
        (Self { nibbles, node }, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(PackedStorageTrieEntry);


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
        let this = Self { nibbles, node };
        (this, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(StorageTrieEntry);

/// Account storage trie node with packed nibble encoding (storage v2).
///
/// Same as [`StorageTrieEntry`] but uses [`PackedStoredNibblesSubKey`] (33 bytes)
/// instead of [`StoredNibblesSubKey`] (65 bytes).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct PackedStorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: PackedStoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for PackedStorageTrieEntry {
    type SubKey = PackedStoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for PackedStorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = PackedStoredNibblesSubKey::from_compact(buf, 33);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 33);
        (Self { nibbles, node }, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(PackedStorageTrieEntry);


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};
use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
        let this = Self { nibbles, node };
        (this, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(StorageTrieEntry);

/// Account storage trie node with packed nibble encoding (storage v2).
///
/// Same as [`StorageTrieEntry`] but uses [`PackedStoredNibblesSubKey`] (33 bytes)
/// instead of [`StoredNibblesSubKey`] (65 bytes).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct PackedStorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: PackedStoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for PackedStorageTrieEntry {
    type SubKey = PackedStoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for PackedStorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = PackedStoredNibblesSubKey::from_compact(buf, 33);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 33);
        (Self { nibbles, node }, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(PackedStorageTrieEntry);


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};
use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
        let this = Self { nibbles, node };
        (this, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(StorageTrieEntry);

/// Account storage trie node with packed nibble encoding (storage v2).
///
/// Same as [`StorageTrieEntry`] but uses [`PackedStoredNibblesSubKey`] (33 bytes)
/// instead of [`StoredNibblesSubKey`] (65 bytes).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct PackedStorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: PackedStoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for PackedStorageTrieEntry {
    type SubKey = PackedStoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for PackedStorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = PackedStoredNibblesSubKey::from_compact(buf, 33);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 33);
        (Self { nibbles, node }, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(PackedStorageTrieEntry);


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};
use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};
use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
        let this = Self { nibbles, node };
        (this, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(StorageTrieEntry);

/// Account storage trie node with packed nibble encoding (storage v2).
///
/// Same as [`StorageTrieEntry`] but uses [`PackedStoredNibblesSubKey`] (33 bytes)
/// instead of [`StoredNibblesSubKey`] (65 bytes).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct PackedStorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: PackedStoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for PackedStorageTrieEntry {
    type SubKey = PackedStoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for PackedStorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = PackedStoredNibblesSubKey::from_compact(buf, 33);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 33);
        (Self { nibbles, node }, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(PackedStorageTrieEntry);


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};
use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,

{
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};
use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
        let this = Self { nibbles, node };
        (this, buf)
    }

}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(StorageTrieEntry);

/// Account storage trie node with packed nibble encoding (storage v2).
///
/// Same as [`StorageTrieEntry`] but uses [`PackedStoredNibblesSubKey`] (33 bytes)
/// instead of [`StoredNibblesSubKey`] (65 bytes).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct PackedStorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: PackedStoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for PackedStorageTrieEntry {
    type SubKey = PackedStoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for PackedStorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = PackedStoredNibblesSubKey::from_compact(buf, 33);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 33);
        (Self { nibbles, node }, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(PackedStorageTrieEntry);


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};
use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8])

{
        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
        let this = Self { nibbles, node };
        (this, buf)
    }


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};
use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
        let this = Self { nibbles, node };
        (this, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(StorageTrieEntry);

/// Account storage trie node with packed nibble encoding (storage v2).
///
/// Same as [`StorageTrieEntry`] but uses [`PackedStoredNibblesSubKey`] (33 bytes)
/// instead of [`StoredNibblesSubKey`] (65 bytes).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct PackedStorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: PackedStoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for PackedStorageTrieEntry {
    type SubKey = PackedStoredNibblesSubKey;

}

#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for PackedStorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = PackedStoredNibblesSubKey::from_compact(buf, 33);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 33);
        (Self { nibbles, node }, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(PackedStorageTrieEntry);


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};
use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
        let this = Self { nibbles, node };
        (this, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(StorageTrieEntry);

/// Account storage trie node with packed nibble encoding (storage v2).
///
/// Same as [`StorageTrieEntry`] but uses [`PackedStoredNibblesSubKey`] (33 bytes)
/// instead of [`StoredNibblesSubKey`] (65 bytes).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct PackedStorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: PackedStoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for PackedStorageTrieEntry {
    type SubKey = PackedStoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};
use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
        let this = Self { nibbles, node };
        (this, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(StorageTrieEntry);

/// Account storage trie node with packed nibble encoding (storage v2).
///
/// Same as [`StorageTrieEntry`] but uses [`PackedStoredNibblesSubKey`] (33 bytes)
/// instead of [`StoredNibblesSubKey`] (65 bytes).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct PackedStorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: PackedStoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for PackedStorageTrieEntry {
    type SubKey = PackedStoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for PackedStorageTrieEntry {

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = PackedStoredNibblesSubKey::from_compact(buf, 33);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 33);
        (Self { nibbles, node }, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(PackedStorageTrieEntry);


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};
use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
        let this = Self { nibbles, node };
        (this, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(StorageTrieEntry);

/// Account storage trie node with packed nibble encoding (storage v2).
///
/// Same as [`StorageTrieEntry`] but uses [`PackedStoredNibblesSubKey`] (33 bytes)
/// instead of [`StoredNibblesSubKey`] (65 bytes).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct PackedStorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: PackedStoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for PackedStorageTrieEntry {
    type SubKey = PackedStoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for PackedStorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};
use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
        let this = Self { nibbles, node };
        (this, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(StorageTrieEntry);

/// Account storage trie node with packed nibble encoding (storage v2).
///
/// Same as [`StorageTrieEntry`] but uses [`PackedStoredNibblesSubKey`] (33 bytes)
/// instead of [`StoredNibblesSubKey`] (65 bytes).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct PackedStorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: PackedStoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for PackedStorageTrieEntry {
    type SubKey = PackedStoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for PackedStorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = PackedStoredNibblesSubKey::from_compact(buf, 33);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 33);
        (Self { nibbles, node }, buf)
    }

}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(PackedStorageTrieEntry);


use super::{BranchNodeCompact, PackedStoredNibblesSubKey, StoredNibblesSubKey};
use reth_primitives_traits::ValueWithSubKey;

/// Account storage trie node.
///
/// `nibbles` is the subkey when used as a value in the `StorageTrie` table.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct StorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: StoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for StorageTrieEntry {
    type SubKey = StoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for StorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
        let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65);
        let this = Self { nibbles, node };
        (this, buf)
    }
}

#[cfg(any(test, feature = "reth-codec"))]
reth_codecs::impl_compression_for_compact!(StorageTrieEntry);

/// Account storage trie node with packed nibble encoding (storage v2).
///
/// Same as [`StorageTrieEntry`] but uses [`PackedStoredNibblesSubKey`] (33 bytes)
/// instead of [`StoredNibblesSubKey`] (65 bytes).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
pub struct PackedStorageTrieEntry {
    /// The nibbles of the intermediate node
    pub nibbles: PackedStoredNibblesSubKey,
    /// Encoded node.
    pub node: BranchNodeCompact,
}

impl ValueWithSubKey for PackedStorageTrieEntry {
    type SubKey = PackedStoredNibblesSubKey;

    fn get_subkey(&self) -> Self::SubKey {
        self.nibbles.clone()
    }
}

#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for PackedStorageTrieEntry {
    fn to_compact<B>(&self, buf: &mut B) -> usize
    where
        B: bytes::BufMut + AsMut<[u8]>,
    {
        let nibbles_len = self.nibbles.to_compact(buf);
        let node_len = self.node.to_compact(buf);
        nibbles_len + node_len
    }

    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8])

{
        let (nibbles, buf) = PackedStoredNibblesSubKey::from_compact(buf, 33);
        let (node, buf) = BranchNodeCompact::from_compact(buf, len - 33);
        (Self { nibbles, node }, buf)
    }


use derive_more::Deref;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{
    fs::File,
    io::{Read, Write},
    sync::Arc,
};
use tracing::*;
use zstd::bulk::Compressor;
pub use zstd::{bulk::Decompressor, dict::DecoderDictionary};

type RawDictionary = Vec<u8>;

/// Represents the state of a Zstandard compression operation.
#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum ZstdState {
    /// The compressor is pending a dictionary.
    #[default]
    PendingDictionary,
    /// The compressor is ready to perform compression.
    Ready,
}

#[cfg_attr(test, derive(PartialEq))]
#[derive(Debug, Serialize, Deserialize)]
/// Zstd compression structure. Supports a compression dictionary per column.
pub struct Zstd {
    /// State. Should be ready before compressing.
    pub(crate) state: ZstdState,
    /// Compression level. A level of `0` uses zstd's default (currently `3`).
    pub(crate) level: i32,
    /// Uses custom dictionaries to compress data.
    pub use_dict: bool,
    /// Max size of a dictionary
    pub(crate) max_dict_size: usize,
    /// List of column dictionaries.
    #[serde(with = "dictionaries_serde")]
    pub(crate) dictionaries: Option<Arc<ZstdDictionaries<'static>>>,
    /// Number of columns to compress.
    columns: usize,
}

impl Zstd {
    /// Creates new [`Zstd`].
    pub const fn new(use_dict: bool, max_dict_size: usize, columns: usize) -> Self {
        Self {
            state: if use_dict { ZstdState::PendingDictionary } else { ZstdState::Ready },
            level: 0,
            use_dict,
            max_dict_size,
            dictionaries: None,
            columns,
        }
    }

    /// Sets the compression level for the Zstd compression instance.
    pub const fn with_level(mut self, level: i32) -> Self {
        self.level = level;
        self
    }

    /// Creates a list of [`Decompressor`] if using dictionaries.
    pub fn decompressors(&self) -> Result<Vec<Decompressor<'_>>, NippyJarError> {
        if let Some(dictionaries) = &self.dictionaries {
            debug_assert!(dictionaries.len() == self.columns);
            return dictionaries.decompressors()
        }

        Ok(vec![])
    }

    /// If using dictionaries, creates a list of [`Compressor`].
    pub fn compressors(&self) -> Result<Option<Vec<Compressor<'_>>>, NippyJarError> {
        match self.state {
            ZstdState::PendingDictionary => Err(NippyJarError::CompressorNotReady),
            ZstdState::Ready => {
                if !self.use_dict {
                    return Ok(None)
                }

                if let Some(dictionaries) = &self.dictionaries {
                    debug!(target: "nippy-jar", count=?dictionaries.len(), "Generating ZSTD compressor dictionaries.");
                    return Ok(Some(dictionaries.compressors()?))
                }
                Ok(None)
            }
        }
    }

    /// Compresses a value using a dictionary. Reserves additional capacity for `buffer` if
    /// necessary.
    pub fn compress_with_dictionary(
        column_value: &[u8],
        buffer: &mut Vec<u8>,
        handle: &mut File,
        compressor: Option<&mut Compressor<'_>>,
    ) -> Result<(), NippyJarError> {
        if let Some(compressor) = compressor {
            // Compressor requires the destination buffer to be big enough to write, otherwise it
            // fails. However, we don't know how big it will be. If data is small
            // enough, the compressed buffer will actually be larger. We keep retrying.
            // If we eventually fail, it probably means it's another kind of error.
            let mut multiplier = 1;
            while let Err(err) = compressor.compress_to_buffer(column_value, buffer) {
                buffer.reserve(column_value.len() * multiplier);
                multiplier += 1;
                if multiplier == 5 {
                    return Err(NippyJarError::Disconnect(err))
                }
            }

            handle.write_all(buffer)?;
            buffer.clear();
        } else {
            handle.write_all(column_value)?;
        }

        Ok(())
    }

    /// Appends a decompressed value using a dictionary to a user provided buffer.
    pub fn decompress_with_dictionary(
        column_value: &[u8],
        output: &mut Vec<u8>,
        decompressor: &mut Decompressor<'_>,
    ) -> Result<(), NippyJarError> {
        let previous_length = output.len();

        // SAFETY: We're setting len to the existing capacity.
        unsafe {
            output.set_len(output.capacity());
        }

        match decompressor.decompress_to_buffer(column_value, &mut output[previous_length..]) {
            Ok(written) => {
                // SAFETY: `decompress_to_buffer` can only write if there's enough capacity.
                // Therefore, it shouldn't write more than our capacity.
                unsafe {
                    output.set_len(previous_length + written);
                }
                Ok(())
            }
            Err(_) => {
                // SAFETY: we are resetting it to the previous value.
                unsafe {
                    output.set_len(previous_length);
                }
                Err(NippyJarError::OutputTooSmall)
            }
        }
    }
}

impl Compression for Zstd {
    fn decompress_to(&self, value: &[u8], dest: &mut Vec<u8>) -> Result<(), NippyJarError> {
        let mut decoder = zstd::Decoder::with_dictionary(value, &[])?;
        decoder.read_to_end(dest)?;
        Ok(())
    }

    fn decompress(&self, value: &[u8]) -> Result<Vec<u8>, NippyJarError> {
        let mut decompressed = Vec::with_capacity(value.len() * 2);
        let mut decoder = zstd::Decoder::new(value)?;
        decoder.read_to_end(&mut decompressed)?;
        Ok(decompressed)
    }

    fn compress_to(&self, src: &[u8], dest: &mut Vec<u8>) -> Result<usize, NippyJarError> {
        let before = dest.len();

        let mut encoder = zstd::Encoder::new(dest, self.level)?;
        encoder.write_all(src)?;

        let dest = encoder.finish()?;

        Ok(dest.len() - before)
    }

    fn compress(&self, src: &[u8]) -> Result<Vec<u8>, NippyJarError> {
        let mut compressed = Vec::with_capacity(src.len());

        self.compress_to(src, &mut compressed)?;

        Ok(compressed)
    }

    fn is_ready(&self) -> bool {
        matches!(self.state, ZstdState::Ready)
    }

    #[cfg(test)]
    /// If using it with dictionaries, prepares a dictionary for each column.
    fn prepare_compression(
        &mut self,
        columns: Vec<impl IntoIterator<Item = Vec<u8>>>,
    ) -> Result<(), NippyJarError> {
        if !self.use_dict {
            return Ok(())
        }

        // There's a per 2GB hard limit on each column data set for training
        // REFERENCE: https://github.com/facebook/zstd/blob/dev/programs/zstd.1.md#dictionary-builder
        // ```
        // -M#, --memory=#: Limit the amount of sample data loaded for training (default: 2 GB).
        // Note that the default (2 GB) is also the maximum. This parameter can be useful in
        // situations where the training set size is not well controlled and could be potentially
        // very large. Since speed of the training process is directly correlated to the size of the
        // training sample set, a smaller sample set leads to faster training.`
        // ```

        if columns.len() != self.columns {
            return Err(NippyJarError::ColumnLenMismatch(self.columns, columns.len()))
        }

        let mut dictionaries = Vec::with_capacity(columns.len());
        for column in columns {
            // ZSTD requires all training data to be continuous in memory, alongside the size of
            // each entry
            let mut sizes = vec![];
            let data: Vec<_> = column
                .into_iter()
                .flat_map(|data| {
                    sizes.push(data.len());