/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2025. All rights reserved.
 */

/**
 * @file
 *
 * This is a class library that implements compression functions.
 * Provides streaming compression interfaces such as deflateInit, deflate, deflateEnd, deflateBound, setGzipHeader, and setDictionary.
 */

package cangjie_tpc::zlib4cj

/* Some constants */
let LENGTH_CODES: UInt32 = 29
let LITERALS: UInt32 = 256
let L_CODES: UInt32 = LITERALS + 1 + LENGTH_CODES
let D_CODES: UInt32 = 30
let BL_CODES: UInt32 = 19
let HEAP_SIZE: UInt32 = 2 * L_CODES + 1
let MAX_BITS: UInt32 = 15
let BUF_SIZE: UInt32 = 16
let TOO_FAR: UInt32 = 4096

let MIN_LOOKAHEAD: UInt32 = MAX_MATCH + MIN_MATCH + 1
let MAX_STORED: UInt32 = 65535

let MAX_BL_BITS: UInt32 = 7
let END_BLOCK: Int64 = 256
let REP_3_6: Int64 = 16
let REPZ_3_10: Int64 = 17
let REPZ_11_138: Int64 = 18

let SMALLEST: Int64 = 1

/* Compressed state */
let INIT_STATE: UInt32    = 42
let GZIP_STATE: UInt32    = 57
let EXTRA_STATE: UInt32   = 69
let NAME_STATE: UInt32    = 73
let COMMENT_STATE: UInt32 = 91
let HCRC_STATE: UInt32    = 103
let BUSY_STATE: UInt32    = 113
let FINISH_STATE: UInt32  = 666

/*
 * Compressed Block Status
 * return value of the deflateStored deflateFast deflateSlow function
 */
let NEED_MORE: UInt32      = 201
let BLOCK_DONE: UInt32     = 202
let FINISH_STARTED: UInt32 = 203
let FINISH_DONE: UInt32    = 204

let freq_code = 0
let dad_len = 1

type CTData = Array<UInt16>

class StaticTreeDesc {
    @Frozen
    StaticTreeDesc(var static_tree: Array<CTData>, var extra_bits: Array<UInt8>, var extra_base: UInt32, var max_element: UInt32, var max_length: UInt32) {}
}

class TreeDescription {
    @Frozen
    TreeDescription(var dyn_tree: Array<CTData>, var max_code: UInt32, var static_desc: StaticTreeDesc) {}
}

let good_length = 0
let max_lazy = 1
let nice_length = 2
let max_chain = 3
let deflate_func = 4

/* Static Huffman compression code table of literal length */
let static_ltree: Array<CTData> = [
    [12,8],[140,8],[76,8],[204,8],[44,8],[172,8],[108,8],[236,8],
    [28,8],[156,8],[92,8],[220,8],[60,8],[188,8],[124,8],[252,8],
    [2,8],[130,8],[66,8],[194,8],[34,8],[162,8],[98,8],[226,8],
    [18,8],[146,8],[82,8],[210,8],[50,8],[178,8],[114,8],[242,8],
    [10,8],[138,8],[74,8],[202,8],[42,8],[170,8],[106,8],[234,8],
    [26,8],[154,8],[90,8],[218,8],[58,8],[186,8],[122,8],[250,8],
    [6,8],[134,8],[70,8],[198,8],[38,8],[166,8],[102,8],[230,8],
    [22,8],[150,8],[86,8],[214,8],[54,8],[182,8],[118,8],[246,8],
    [14,8],[142,8],[78,8],[206,8],[46,8],[174,8],[110,8],[238,8],
    [30,8],[158,8],[94,8],[222,8],[62,8],[190,8],[126,8],[254,8],
    [1,8],[129,8],[65,8],[193,8],[33,8],[161,8],[97,8],[225,8],
    [17,8],[145,8],[81,8],[209,8],[49,8],[177,8],[113,8],[241,8],
    [9,8],[137,8],[73,8],[201,8],[41,8],[169,8],[105,8],[233,8],
    [25,8],[153,8],[89,8],[217,8],[57,8],[185,8],[121,8],[249,8],
    [5,8],[133,8],[69,8],[197,8],[37,8],[165,8],[101,8],[229,8],
    [21,8],[149,8],[85,8],[213,8],[53,8],[181,8],[117,8],[245,8],
    [13,8],[141,8],[77,8],[205,8],[45,8],[173,8],[109,8],[237,8],
    [29,8],[157,8],[93,8],[221,8],[61,8],[189,8],[125,8],[253,8],
    [19,9],[275,9],[147,9],[403,9],[83,9],[339,9],[211,9],[467,9],
    [51,9],[307,9],[179,9],[435,9],[115,9],[371,9],[243,9],[499,9],
    [11,9],[267,9],[139,9],[395,9],[75,9],[331,9],[203,9],[459,9],
    [43,9],[299,9],[171,9],[427,9],[107,9],[363,9],[235,9],[491,9],
    [27,9],[283,9],[155,9],[411,9],[91,9],[347,9],[219,9],[475,9],
    [59,9],[315,9],[187,9],[443,9],[123,9],[379,9],[251,9],[507,9],
    [7,9],[263,9],[135,9],[391,9],[71,9],[327,9],[199,9],[455,9],
    [39,9],[295,9],[167,9],[423,9],[103,9],[359,9],[231,9],[487,9],
    [23,9],[279,9],[151,9],[407,9],[87,9],[343,9],[215,9],[471,9],
    [55,9],[311,9],[183,9],[439,9],[119,9],[375,9],[247,9],[503,9],
    [15,9],[271,9],[143,9],[399,9],[79,9],[335,9],[207,9],[463,9],
    [47,9],[303,9],[175,9],[431,9],[111,9],[367,9],[239,9],[495,9],
    [31,9],[287,9],[159,9],[415,9],[95,9],[351,9],[223,9],[479,9],
    [63,9],[319,9],[191,9],[447,9],[127,9],[383,9],[255,9],[511,9],
    [0,7],[64,7],[32,7],[96,7],[16,7],[80,7],[48,7],[112,7],
    [8,7],[72,7],[40,7],[104,7],[24,7],[88,7],[56,7],[120,7],
    [4,7],[68,7],[36,7],[100,7],[20,7],[84,7],[52,7],[116,7],
    [3,8],[131,8],[67,8],[195,8],[35,8],[163,8],[99,8],[227,8]
]

/*
 * Compressed code table of the distance interval number in the static huffman policy
 * Fixed 5 bits indicate the range number from 0 to 29. The code value is in reverse order.
 */
let static_dtree: Array<CTData> = [
    [0,5],[16,5],[8,5],[24,5],[4,5],
    [20,5],[12,5],[28,5],[2,5],[18,5],
    [10,5],[26,5],[6,5],[22,5],[14,5],
    [30,5],[1,5],[17,5],[9,5],[25,5],
    [5,5],[21,5],[13,5],[29,5],[3,5],
    [19,5],[11,5],[27,5],[7,5],[23,5]
]

/* Mapping between distance values and ranges */
let dist_code: Array<UInt8> = [
    0,  1,  2,  3,  4,  4,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8,
    8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9, 10, 10, 10, 10, 10, 10, 10, 10,
    10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
    11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
    12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
    13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  0,  0, 16, 17,
    18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
    23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
    24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
    26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
    26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
    27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
    27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
    28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
    28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
    28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
    29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
    29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
    29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
]

/* Mapping between literal length values and ranges */
let length_code: Array<UInt8> = [
    0,  1,  2,  3,  4,  5,  6,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 12, 12,
    13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
    17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
    19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
    22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
    23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
    24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
    26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
    26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
    27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
]

/* Mapping between literal length ranges and basic values */
let base_length: Array<UInt32> = [
    0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0
]

/* Mapping between distance range numbers and basic values */
let base_dist: Array<UInt32> = [
    0,     1,     2,     3,     4,     6,     8,    12,    16,    24,
    32,    48,    64,    96,   128,   192,   256,   384,   512,   768,
    1024,  1536,  2048,  3072,  4096,  6144,  8192, 12288, 16384, 24576
]

/* Mapping between the range number and the number of extended bits */
let extra_lbits: Array<UInt8> = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0]
let extra_dbits: Array<UInt8> = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13]
let extra_blbits: Array<UInt8> = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7]

let static_l_desc = StaticTreeDesc(static_ltree, extra_lbits,  LITERALS + UInt32(1), L_CODES,  MAX_BITS)
let static_d_desc = StaticTreeDesc(static_dtree, extra_dbits,  0,                    D_CODES,  MAX_BITS)
let static_bl_desc = StaticTreeDesc(NULL_ARR_CTData,          extra_blbits, 0,                    BL_CODES, MAX_BL_BITS)

/**
 * This class Deflate implements the compression function.
 *
 * @since 0.24.2
 */
public class Deflate <: Stream {

    /* Compressed state */
    private var status: UInt32 = INIT_STATE

    private var lit_bufsize: Int64 = 1 << (DEF_MEM_LEVEL + 6)

    /* Wait for the buffer to save the compressed data */
    private var pending_buf: Array<UInt8> = NULL_ARR_UINT8
    private var pending_buf_size: Int64 = lit_bufsize * 4

    /* Read position of pending_buf */
    private var pending_start: Int64 = 0

    /* Number of bytes in pending_buf that are not read to the output buffer */
    private var pending: Int64 = 0

    /* Compressed data outer wrapper type: ZLIB format, GZIP format, or DEFLATE */
    private var wrap: Int64 = ZLIB

    /* Header information in GZIP format */
    private var gzhead: GZIPHeader = GZIPHeader()
    private var gzindex: Int64 = 0

    /* compression algorithm: Only one DEFLATE = 8 */
    private var method: UInt32 = METHOD_DEFLATE
    private var last_flush: Int32 = -2

    /* Controls the size of the sliding window and the size of the prev array */
    private var w_bits: Int64 = 15

    /* 1 << w_bits */
    private var w_size: UInt32 = 1 << w_bits

    /* Mask = w_size - 1 */
    private var w_mask: UInt32 = w_size - 1

    /* Sliding window, compression table */
    private var window: Array<UInt8> = NULL_ARR_UINT8

    /* Sliding window size = w_size * 2 */
    private var window_size: UInt32 = w_size * 2

    /* prev [current position] = previous position (hash value is the same as current position) */
    private var prev: Array<UInt16> = NULL_ARR_UINT16

    /* head [hash value] = position */
    private var head: Array<UInt16> = NULL_ARR_UINT16

    /* Hash value */
    private var ins_h: UInt16 = 0

    /* Number of bits of the hash value */
    private var hash_bits: UInt32 = DEF_MEM_LEVEL + 7

    /* 1 << hash_bits */
    private var hash_size: UInt32 = 1 << hash_bits

    /* hash_mask = hash_size - 1 */
    private var hash_mask: UInt32 = hash_size - 1

    /* Number of left scrolling bits of the rolling hash */
    private var hash_shift: UInt32 = ((hash_bits + MIN_MATCH - 1) / MIN_MATCH)

    /* Uncompressed data in the sliding window */
    private var lookahead: UInt32 = 0
    private var block_start: Int64 = 0

    /* Current compression position (in sliding window) */
    private var strstart: UInt32 = 0
    private var match_start: Int64 = 0
    private var match_length: UInt32 = MIN_MATCH - 1
    private var prev_start: UInt32 = 0
    private var prev_length: UInt32 = MIN_MATCH - 1
    private var match_available: Bool = false


    static let configuration_table: Array<(UInt32,UInt32,UInt32,UInt32,(Deflate, Int32) -> UInt32)> = [
        /*      good lazy nice chain */
        /* 0 */ (0,  0,   0,   0,    deflateStored),  /* store only */
        /* 1 */ (4,  4,   8,   4,    deflateFast), /* max speed, no lazy matches */
        /* 2 */ (4,  5,   16,  8,    deflateFast),
        /* 3 */ (4,  6,   32,  32,   deflateFast),
        /* 4 */ (4,  4,   16,  16,   deflateSlow),/* lazy matches */
        /* 5 */ (8,  16,  32,  32,   deflateSlow),
        /* 6 */ (8,  16,  128, 128,  deflateSlow),
        /* 7 */ (8,  32,  128, 256,  deflateSlow),
        /* 8 */ (32, 128, 258, 1024, deflateSlow),
        /* 9 */ (32, 258, 258, 4096, deflateSlow)/* max compression */
        ]

    /* Compression level 0 - 9 */
    private var level: Int64 = 6

    /* Number of forward search times in the longest match (following the prev chain) */
    private var max_chain_length: UInt32 = configuration_table[Int64(level)][3]

    /* The value is the same as max_chain_length. This parameter is used when level is less than 4. */
    private var max_insert_length: UInt32 = max_chain_length

    /* Maximum number of times that a lazy match moves backwards */
    private var max_lazy_match: UInt32 = configuration_table[Int64(level)][1]

    /*
     * Good matching length.
     * If the length is greater than or equal to this length, the current chain_length is reduced.
     */
    private var good_length: UInt32 = configuration_table[Int64(level)][0]

    /*
     * Very good match length.
     * If the length is greater than or equal to this length, the longest match ends.
     */
    private var nice_length: UInt32 = configuration_table[Int64(level)][2]

    /* Compression Policy */
    private var strategy: UInt32 = Z_DEFAULT_STRATEGY

    private var dyn_ltree: Array<CTData> = NULL_ARR_CTData
    private var dyn_dtree: Array<CTData> = NULL_ARR_CTData
    private var bl_tree: Array<CTData> = NULL_ARR_CTData

    private var l_desc: TreeDescription = TreeDescription(dyn_ltree, 0, static_l_desc)
    private var d_desc: TreeDescription = TreeDescription(dyn_dtree, 0, static_d_desc)
    private var bl_desc: TreeDescription = TreeDescription(bl_tree,  0, static_bl_desc)

    private var bl_count: Array<UInt16> = NULL_ARR_UINT16

    private var heap: Array<UInt32> = NULL_ARR_UINT32
    private var heap_len: UInt32 = 0
    private var heap_max: Int64 = Int64(HEAP_SIZE)
    private var depth: Array<UInt8> = NULL_ARR_UINT8

    private var sym_buf: Array<UInt8> = pending_buf
    private var sym_next: Int64 = lit_bufsize
    private var sym_end: Int64 = (lit_bufsize - 1) * 3 + lit_bufsize

    private var opt_len: Int64 = 0
    private var static_len: Int64 = 0
    private var insert: UInt32 = 0

    private var bits_buf: UInt16 = 0
    private var bits_valid: UInt32 = 0
    private var high_water: UInt64 = 0

    private var max_dist: UInt32 = w_size - MIN_LOOKAHEAD

    private var stored_len: UInt32 = 0
    private var stored_state: UInt32 = 0

    var finished: Bool = false

    @Frozen
    public init() {}

    @Frozen
    public func deflateInit(level: Int64): Int32 {
        return this.deflateInit2(level, 15, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY)
    }

    @Frozen
    public func deflateInit(level: Int64, wbits: Int64): Int32 {
        return this.deflateInit2(level, wbits, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY)
    }

    @Frozen
    public func deflateInit2(lev: Int64, wbits: Int64, memLevel: UInt32, strategy: UInt32): Int32 {
        var level = lev
        var wrap = 1
        if (level == Z_DEFAULT_COMPRESSION) {
            level = 6
        }
        var windowBits = wbits
        if (windowBits < 0) { /* suppress zlib wrapper */
            wrap = 0
            if (windowBits < -15) {
                return Z_STREAM_ERROR
            }
            windowBits = -windowBits;
        } else if (windowBits > 15) { /* write gzip wrapper instead */
            wrap = 2
            windowBits -= 16;
        }
        if (memLevel < 1 || memLevel > MAX_MEM_LEVEL ||
            windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
            strategy > Z_FIXED || (windowBits == 8 && wrap != 1)) {
            return Z_STREAM_ERROR;
        }
        if(windowBits == 8) {
            windowBits = 9
        }
        this.status = INIT_STATE
        this.wrap = wrap
        // s->gzhead = Z_NULL;
        this.w_bits = windowBits
        this.w_size = 1 << windowBits
        this.w_mask = this.w_size - 1
        this.hash_bits = memLevel + 7
        this.hash_size = 1 << hash_bits
        this.hash_mask = hash_size - 1
        this.hash_shift = ((hash_bits + MIN_MATCH - 1) / MIN_MATCH)
        this.window_size = w_size * 2
        try {
            this.window = Array<UInt8>(Int64(window_size), repeat: 0)
            this.prev = Array<UInt16>(Int64(w_size), repeat: 0)
            this.head = Array<UInt16>(Int64(hash_size), repeat: 0)
            this.high_water = 0
            this.lit_bufsize = 1 << (memLevel + 6)
            this.pending_buf = Array<UInt8>(this.lit_bufsize * 4, repeat: 0)
            this.pending_buf_size = this.lit_bufsize * 4
            this.bl_count = Array<UInt16>(Int64(MAX_BITS + 1), repeat: 0)
            this.bl_tree = Array<CTData>(Int64(2 * BL_CODES + 1), {_ => [0,0]})
            this.dyn_dtree = Array<CTData>(Int64(2 * D_CODES + 1), {_ => [0,0]})
            this.dyn_ltree = Array<CTData>(Int64(HEAP_SIZE), {_ => [0,0]})
            this.heap = Array<UInt32>(Int64(HEAP_SIZE), repeat: 0)
            this.depth = Array<UInt8>(Int64(HEAP_SIZE), repeat: 0)
        } catch (e: Exception) {
            this.status = FINISH_STATE
            this.message = erroMsg(Z_MEM_ERROR)
            deflateEnd()
            return Z_MEM_ERROR
        }
        this.sym_buf = this.pending_buf //TODO + this.lit_bufsize

        this.sym_end = (this.lit_bufsize - 1) * 3;
        this.level = Int64(level)
        this.strategy = UInt32(strategy)
        this.bl_desc = TreeDescription(bl_tree, 0, static_bl_desc)
        this.d_desc = TreeDescription(dyn_dtree, 0, static_d_desc)
        this.l_desc = TreeDescription(dyn_ltree, 0, static_l_desc)
        treeInit()
        return deflateReset()
    }

    @Frozen
    func deflateReset(): Int32 {
        var ret = deflateResetKeep()
        if (ret == Z_OK) {
            return ret
        }
        return ret
    }


    @Frozen
    func deflateResetKeep(): Int32 {
        this.total_in = 0
        this.total_out = 0
        this.data_type = 2  // Z_UNKNOWN
        this.pending = 0
        // this.pending_out = this.pending_buf
        if(this.wrap < 0) {
            this.wrap = -this.wrap
        }
        this.status = if (this.wrap == 2) {
            GZIP_STATE
        } else {
            INIT_STATE
        }
        this.adler = if (this.wrap == 2) {
            DEFAULT_CRC32
        } else {
            DEFAULT_ADLER32
        }
        this.last_flush = -2
        return Z_OK
    }


    @Frozen
    func hcrcUpdate(beg: Int64) {
        if (gzhead.hcrc != 0 && pending > beg) {
            adler = crc32(adler, pending_buf, beg, pending_start + pending - beg)
        }
    }

    /**
     * Compressing data.
     *
     * @param flush Compressing data
     * @return UInt32 Returns a value of the UInt32 type.
     *
     * @since 0.24.2
     */
    @Frozen
    public func deflate(flush: Int32): Int32 {
        if (deflateStateCheck() == 1 || flush > Z_BLOCK || flush < Z_NO_FLUSH) {
            return Z_STREAM_ERROR
        }
        if (avail_out == 0 ||
            (avail_in != 0 && next_in.size == 0) ||
            (status == FINISH_STATE && flush != Z_FINISH)) {
            message = erroMsg(Z_STREAM_ERROR)
            return Z_STREAM_ERROR
        }
        if (avail_out == 0) {
            message = erroMsg(Z_BUF_ERROR)
            return Z_BUF_ERROR
        }
        let old_flush: Int32 = last_flush
        last_flush = flush
        if (pending != 0) {
            flushPending()
            if (avail_out == 0) {
                last_flush = Z_UNKNOWN_FLUSH
                return Z_OK
            }
        }
        /*
         else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) &&
               flush != Z_FINISH) {
         */
        else if (avail_in == 0 && flush != Z_FINISH) {
            /* to do: Repeated useless Z_FINISH calls return Z_STREAM_END */
            message = erroMsg(Z_BUF_ERROR)
            return Z_BUF_ERROR
        }
        if (status == FINISH_STATE && avail_in != 0) {
            message = erroMsg(Z_BUF_ERROR)
            return Z_BUF_ERROR
        }
        if (status == INIT_STATE && this.wrap == 0) {
            status = BUSY_STATE
        }
        if (status == INIT_STATE) {
            var header: UInt32 = (method + UInt32((w_bits - 8) << 4)) << 8
            var level_flags: UInt32
            if (strategy >= Z_HUFFMAN_ONLY || level < 2) {
                level_flags = 0
            } else if (level < 6) {
                level_flags = 1
            } else if (level == 6) {
                level_flags = 2
            } else {
                level_flags = 3
            }
            header |= (level_flags << 6)
            if (strstart != 0) {
                header |= PRESET_DICT
            }
            header += 31 - (header % 31)
            putShortMSB(UInt16(header))
            if (strstart != 0) {
                putShortMSB(UInt16(adler >> 16))
                putShortMSB(UInt16(adler & 0xFFFF))
            }
            adler = DEFAULT_ADLER32
            status = BUSY_STATE
            flushPending()
            if (pending != 0) {
                last_flush = Z_UNKNOWN_FLUSH
                return Z_OK
            }
        }
        // start GZIP
        if (status == GZIP_STATE) {
            adler = DEFAULT_CRC32
            /* to do: Currently, the done flag is used. Then, check whether the done flag has other functions. */
            putByte(31)
            putByte(139)
            putByte(8)
            if (gzhead.done == 0) {
                putByte(0)
                putByte(0)
                putByte(0)
                putByte(0)
                putByte(0)
                let uu: UInt8= if (level == 9) {
                    2
                } else if (strategy >= Z_HUFFMAN_ONLY || level < 2) {
                    4
                } else {
                    0
                }
                putByte(uu)
                putByte(UInt8(OS_CODE))
                status = BUSY_STATE
                flushPending()
                if (pending != 0) {
                    last_flush = Z_UNKNOWN_FLUSH
                    return Z_OK
                }
            } else {
                putByte(if (gzhead.text != 0) { 1 } else { 0 } +
                        if (gzhead.hcrc != 0) { 2 } else { 0 } +
                        if (let Some(v) <- gzhead.extra) { 0 } else { 4 } +
                        if (let Some(v) <- gzhead.name) { 0 } else { 8 } +
                        if (let Some(v) <- gzhead.comment) { 0 } else { 16 })
                putByte(UInt8(gzhead.time & 0xFF))
                putByte(UInt8((gzhead.time >> 8) & 0xFF))
                putByte(UInt8((gzhead.time >> 16) & 0xFF))
                putByte(UInt8((gzhead.time >> 24) & 0xFF))
                putByte(if (level == 9) { 2 } else if (strategy != Z_FILTERED || level < 2) { 4 } else { 0 })
                putByte(UInt8(gzhead.os & 0xFF))
                if (let Some(gzhead_extra) <- gzhead.extra) {
                    putByte(UInt8(gzhead.extra_len & 0xFF))
                    putByte(UInt8((gzhead.extra_len >> 8) & 0xFF))
                }
                if (gzhead.hcrc != 0) {
                    adler = crc32(adler, pending_buf, pending_start, pending)
                }
                gzindex = 0
                status = EXTRA_STATE
            }
        }
        if (status == EXTRA_STATE) {
            if (let Some(gzhead_extra) <- gzhead.extra) {
                var beging: Int64 = pending_start + pending
                var left: Int64 = (gzhead_extra.size & 0xFFFF) - gzindex
                while (pending_start + pending + left > pending_buf_size) {
                    var copy: Int64 = pending_buf_size - pending
                    for (i in 0..copy) {
                        pending_buf[pending_start + pending] = gzhead_extra[gzindex]
                        gzindex++
                        pending++ // SEE
                    }
                    // if (gzhead.hcrc != 0 && pending_start + pending > beging) {
                    //     adler = crc32(adler, pending_buf, beging, pending_start + pending - beging)
                    // }
                    this.hcrcUpdate(beging)
                    flushPending()
                    if (pending != 0) {
                        last_flush = Z_UNKNOWN_FLUSH
                        return Z_OK
                    }
                    beging = 0
                    left -= copy
                }
                for (i in 0..left) {
                    pending_buf[pending_start + pending] = gzhead_extra[gzindex]
                    pending++
                    gzindex++
                }
                // if (gzhead.hcrc != 0 && pending > beging) {
                //     adler = crc32(adler, pending_buf, beging, pending_start + pending - beging)
                // }
                this.hcrcUpdate(beging)
                gzindex = 0
            }
            status = NAME_STATE
        }
        if (status == NAME_STATE) {
            var gzhead_name: Array<Byte> = NULL_ARR_UINT8
            if (let Some(v) <- gzhead.name) {
                gzhead_name = v
            }
            if (gzhead_name.size > 0) {
                var begin: Int64 = pending_start + pending
                var pos: Int64
                do {
                    pos = pending_start + pending
                    if (pos == pending_buf_size) {
                        // if (gzhead.hcrc != 0 && pos > begin) {
                        //     adler = crc32(adler, pending_buf, begin, pos - begin)
                        // }
                        this.hcrcUpdate(begin)
                        flushPending()
                        if (pending != 0) {
                            last_flush = Z_UNKNOWN_FLUSH
                            return Z_OK
                        }
                        begin = 0
                    }
                    putByte(gzhead_name[Int64(gzindex)])
                    gzindex++
                } while (gzindex < gzhead_name.size)
                // putByte(0)
                this.hcrcUpdate(begin)
                gzindex = 0
                // pos = pending_start + pending
                // if (gzhead.hcrc != 0 && pos > begin) {
                    // adler = crc32(adler, pending_buf, begin, pos - begin)
                // }
            }
            status = COMMENT_STATE
        }
        if (status == COMMENT_STATE) {
            var gzhead_comment: Array<Byte> = NULL_ARR_UINT8
            if (let Some(v) <- gzhead.comment) {
                gzhead_comment = v
            }
            if (gzhead_comment.size > 0) {
                var begin: Int64 = pending_start + pending
                var pos: Int64
                do {
                    pos = pending_start + pending
                    if (pos == pending_buf_size) {
                        // if (gzhead.hcrc != 0 && pos > begin) {
                        //     adler = crc32(adler, pending_buf, begin, pos - begin)
                        // }
                        this.hcrcUpdate(begin)
                        flushPending()
                        if (pending != 0) {
                            last_flush = Z_UNKNOWN_FLUSH
                            return Z_OK
                        }
                        begin = 0
                    }
                    putByte(gzhead_comment[Int64(gzindex)])
                    gzindex++
                } while (gzindex < gzhead_comment.size)
                this.hcrcUpdate(begin)
                // putByte(0)
                // gzindex = 0
                // pos = pending_start + pending
                // if (gzhead.hcrc != 0 && pos > begin) {
                //     adler = crc32(adler, pending_buf, begin, pos - begin)
                // }
            }
            status = HCRC_STATE
        }
        if (status == HCRC_STATE) {
            if (gzhead.hcrc != 0) {
                if (pending_start + pending + 2 > pending_buf_size) {
                    flushPending()
                    if (pending != 0) {
                        last_flush = Z_UNKNOWN_FLUSH
                        return Z_OK
                    }
                }
                putByte(UInt8(adler & 0xFF))
                putByte(UInt8((adler >> 8) & 0xFF))
                adler = DEFAULT_CRC32
            }
            status = BUSY_STATE
            flushPending()
            if (pending != 0) {
                last_flush = Z_UNKNOWN_FLUSH
                return Z_OK
            }
        }
        // end GZIP
        if (avail_in != 0 || lookahead != 0 || (flush != Z_NO_FLUSH && status != FINISH_STATE)) {
            var bstate: UInt32
            if (level == 0) {
                bstate = deflateStored(this, flush)
            } else if (strategy == Z_HUFFMAN_ONLY) {
                bstate = deflateHuffman(flush)
            } else if (strategy == Z_RLE) {
                bstate = deflateRLE(flush)
            } else {
                bstate = configuration_table[Int64(level)][4](this, flush)
            }

            if (bstate == FINISH_STARTED || bstate == FINISH_DONE) {
                status = FINISH_STATE
            }
            if (bstate == NEED_MORE || bstate == FINISH_STARTED) {
                if (avail_out == 0) {
                    last_flush = Z_UNKNOWN_FLUSH
                }
                return Z_OK
            }
            if (bstate == BLOCK_DONE) {
                if (flush == Z_PARTIAL_FLUSH) {
                    flushEmptyStaticBlock()
                } else if (flush != Z_BLOCK) {
                    storedBlock(0, 0, false)
                    if (flush == Z_FULL_FLUSH) {
                        for (i in 0..head.size) {
                            head[i] = 0
                        }
                        if (lookahead == 0) {
                            strstart = 0
                            block_start = 0
                            insert = 0
                        }
                    }
                }
                flushPending()
                if (avail_out == 0) {
                    last_flush = Z_UNKNOWN_FLUSH
                    return Z_OK
                }
            }
        }
        if (flush != Z_FINISH) {
            return Z_OK
        }
        if (this.wrap <= 0) {
            message = erroMsg(Z_STREAM_END)
            this.finished = true
            return Z_STREAM_END
        }
        match (wrap) {
            /* to do: wrap */
            case 0 =>
                this.finished = true
                return Z_STREAM_END
            case 2 =>
                putByte(UInt8(adler & 0xFF))
                putByte(UInt8((adler >> 8) & 0xFF))
                putByte(UInt8((adler >> 16) & 0xFF))
                putByte(UInt8((adler >> 24) & 0xFF))
                putByte(UInt8(total_in & 0xFF))
                putByte(UInt8((total_in >> 8) & 0xFF))
                putByte(UInt8((total_in >> 16) & 0xFF))
                putByte(UInt8((total_in >> 24) & 0xFF))
            case _ =>
                putShortMSB(UInt16(adler >> 16))
                putShortMSB(UInt16(adler & 0xFFFF))
        }
        flushPending()
        /* to do: wrap */
        if (this.wrap > 0) {
            this.wrap = -this.wrap
        }
        if (pending != 0) {
            return Z_OK
        }
        message = erroMsg(Z_STREAM_END)
        this.finished = true
        return Z_STREAM_END
    }

    /**
     * This command is invoked after the compression is complete to check the compression status.
     *
     * @return UInt32 Returns a value of the UInt32 type.
     *
     * @since 0.24.2
     */
    @Frozen
    public func deflateEnd(): Int32 { // TODO
        if (deflateStateCheck() != 0) {
            message = erroMsg(Z_STREAM_ERROR)
            return Z_STREAM_ERROR
        }
        if (status == BUSY_STATE) {
            message = erroMsg(Z_DATA_ERROR)
            return Z_DATA_ERROR
        }
        this.finished = true
        return Z_OK
    }

    func deflateStateCheck(): UInt32 {
        if (this.status != INIT_STATE &&
            this.status != GZIP_STATE &&
            this.status != EXTRA_STATE &&
            this.status != NAME_STATE &&
            this.status != COMMENT_STATE &&
            this.status != HCRC_STATE &&
            this.status != BUSY_STATE &&
            this.status != FINISH_STATE){
                return 1
        }
        return 0
    }

    /**
     * Calculate the maximum size of compressed data based on the source data size.
     *
     * @param sourceLen Source data size
     * @return Int64 Returns a value of the Int64 type.
     *
     * @since 0.24.2
     */
    @Frozen
    public func deflateBound(sourceLen: Int64): Int64 {
        var fixedlen = sourceLen + (sourceLen >> 3) + (sourceLen >> 8) + (sourceLen >> 9) + 4
        var storelen = sourceLen + (sourceLen >> 5) + (sourceLen >> 7) + (sourceLen >> 11) + 7
        if (deflateStateCheck() != 0) {
            return if(fixedlen > storelen) {
                fixedlen
            } else {
                storelen
            } + 6
        }
        // var complen: Int64 = sourceLen + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5
        var wraplen: Int64 = 0
        match (wrap) {
            case 0 => wraplen = 0
            case 1    => wraplen = 6 + if (strstart > 0) { 4 } else { 0 }
            case 2    =>
                wraplen = 18

                if (let Some(gzhead_extra) <- gzhead.extra) {
                    wraplen += 2 + Int64(gzhead.extra_len)
                }
                if (let Some(gzhead_name) <- gzhead.name) {
                    wraplen += gzhead_name.size
                }
                if (let Some(gzhead_comment) <- gzhead.comment) {
                    wraplen += gzhead_comment.size
                }

                if (gzhead.hcrc != 0) {
                    wraplen += 2
                }
            case _ => wraplen = 6
        }
        if (w_bits != 15 || hash_bits != 8 + 7) {
            return if (w_bits <= Int64(hash_bits) && level != 0) {
                fixedlen
            } else {
                storelen
            } + wraplen
        }
        return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13 - 6 + wraplen
    }

    /**
     * After initialization is complete and before compression starts, call this function to set the dictionary.
     * The zlib file format supports the dictionary.
     *
     * @param dict Set dictionary data
     * @return UInt32 Returns a value of the UInt32 type.
     *
     * @since 0.24.2
     */
    @Frozen
    public func setDictionary(dict: Array<UInt8>): Int32 {
        if (status < INIT_STATE || status > FINISH_STATE || dict.size == 0) {
            return Z_STREAM_ERROR
        }
        var wrap: Int64 = this.wrap
        match (wrap) {
            case 2 =>
                return Z_STREAM_ERROR
            case 1 =>
                if (status != INIT_STATE || lookahead > 0) {
                    return Z_STREAM_ERROR
                }
                adler = adler32(adler, dict, 0, dict.size)
            case 0 =>
                if (lookahead > 0) {
                    return Z_STREAM_ERROR
                }
            case _ => ()
        }
        this.wrap = 0
        var start: Int64 = 0
        if (dict.size >= Int64(w_size)) {
            if (wrap == 0) {
                for (i in 0..head.size) {
                    head[i] = 0
                }
                strstart = 0
                block_start = 0
                insert = 0
            }
            start = dict.size - Int64(w_size)
        }

        var avail: Int64 = avail_in
        var posin: Int64 = pos_in
        var next: Array<UInt8> = next_in
        avail_in = dict.size - start
        pos_in = start
        next_in = dict
        fillWindow()
        var str: UInt32
        var n: UInt32
        while (lookahead >= MIN_MATCH) {
            str = strstart
            n = lookahead - (MIN_MATCH - 1)
            do {
                updateHash(ins_h, window[Int64(str + MIN_MATCH - 1)])
                prev[Int64(str & w_mask)] = head[Int64(ins_h)]
                head[Int64(ins_h)] = UInt16(str)
                str++
                n--
            } while (n > 0)
            strstart = str
            lookahead = MIN_MATCH - 1
            fillWindow()
        }
        strstart += lookahead
        block_start = Int64(strstart)
        insert = lookahead
        lookahead = 0
        match_length = MIN_MATCH - 1
        prev_length = MIN_MATCH - 1
        match_available = false
        next_in = next
        avail_in = avail
        pos_in = posin
        this.wrap = wrap
        return Z_OK
    }

    /**
     * This command is used to set the header information in the gzip format when the compressed file is in the gzip format.
     *
     * @param gzhead Header information in gzip format
     * @return UInt32 Returns a value of the UInt32 type.
     *
     * @since 0.24.2
     */
    @Frozen
    public func deflateSetHeader(gzhead: GZIPHeader): Int32 {
        if (deflateStateCheck() != 0 || this.wrap != 2) {
            return Z_STREAM_ERROR
        }
        this.gzhead = gzhead
        return Z_OK
    }

    @Frozen
    private func putByte(data: UInt8): Unit {
        pending_buf[Int64(pending_start + pending)] = data
        pending++
    }

    @Frozen
    private func putShortMSB(data: UInt16): Unit {
        putByte(UInt8(data >> 8))
        putByte(UInt8(data & 0xFF))
    }

    @Frozen
    private func putShort(data: UInt16): Unit {
        putByte(UInt8(data & 0xFF))
        putByte(UInt8(data >> 8))
    }

    @Frozen
    private func sendBits(value: UInt32, length: UInt32): Unit {
        if (length > BUF_SIZE - bits_valid) {
            bits_buf |= UInt16((value << bits_valid) & 0xFFFF)
            putShort(bits_buf)
            bits_buf = UInt16((value >> (BUF_SIZE - bits_valid)) & 0xFFFF)
            bits_valid = bits_valid + length - BUF_SIZE
        } else {
            bits_buf |= UInt16((value << bits_valid) & 0xFFFF)
            bits_valid += length
        }
    }

    @Frozen
    private func flushPending(): Unit {
        bitsFlush()
        var len: Int64 = pending
        if (len > Int64(avail_out)) {
            len = avail_out
        }
        if (len == 0) {
            return
        }
        for (i in 0..len) {
            next_out[pos_out] = pending_buf[pending_start]
            pos_out++
            pending_start++
        }
        avail_out -= len
        total_out += len
        pending -= Int64(len)
        if (pending == 0) {
            pending_start = 0
        }
    }

    @Frozen
    private func tallyDistance(distance: UInt32, length: UInt32): Bool {
        var dist: UInt32 = distance
        pending_buf[sym_next] = UInt8(dist & 0xFF)
        sym_next++
        pending_buf[sym_next] = UInt8((dist >> 8) & 0xFF)
        sym_next++
        pending_buf[sym_next] = UInt8(length & 0xFF)
        sym_next++
        dist--
        dyn_ltree[Int64(length_code[Int64(length)]) + Int64(LITERALS) + 1][freq_code]++
        if (dist < 256) {
            dyn_dtree[Int64(dist_code[Int64(dist)])][freq_code]++
        } else {
            dyn_dtree[Int64(dist_code[256 + Int64(dist >> 7)])][freq_code]++
        }
        return sym_next == sym_end + lit_bufsize
    }

    @Frozen
    private func tallyLiteral(c: UInt8): Bool {
        sym_buf[sym_next] = 0
        sym_next++
        sym_buf[sym_next] = 0
        sym_next++
        sym_buf[sym_next] = c
        sym_next++
        dyn_ltree[Int64(c)][freq_code]++
        return sym_next == sym_end + lit_bufsize
    }

    // configuration_table
    @Frozen
    private static func deflateStored(_: Deflate, _: Int32): UInt32 {
        return FINISH_DONE
    }

    @Frozen
    private func deflateHuffman(flush: Int32): UInt32 {
        var bflush: Bool
        while (true) {
            if (lookahead == 0) {
                fillWindow()
                if (lookahead == 0) {
                    if (flush == Z_NO_FLUSH) {
                        return NEED_MORE
                    }
                    break
                }
            }
            match_length = 0
            bflush = tallyLiteral(window[Int64(strstart)])
            lookahead--
            strstart++
            if (bflush) {
                flushBlock(block_start, Int64(strstart) - block_start, false)
                block_start = Int64(strstart)
                flushPending()
                if (avail_out == 0) {
                    return NEED_MORE
                }
            }
        }
        insert = 0
        if (flush == Z_FINISH) {
            flushBlock(block_start, Int64(strstart) - block_start, true)
            block_start = Int64(strstart)
            flushPending()
            if (avail_out == 0) {
                return FINISH_STARTED
            }
            return FINISH_DONE
        }
        if (sym_next == sym_end) {
            flushBlock(block_start, Int64(strstart) - block_start, false)
            block_start = Int64(strstart)
            flushPending()
            if (avail_out == 0) {
                return NEED_MORE
            }
            return BLOCK_DONE
        }
        return NEED_MORE
    }

    @Frozen
    private func deflateRLE(flush: Int32): UInt32 {
        var bflush: Bool
        var prev: UInt8
        var scanPos: Int64
        var scanEnd: Int64
        while (true) {
            if (lookahead <= MAX_MATCH) {
                fillWindow()
                if (lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) {
                    return NEED_MORE
                }
                if (lookahead == 0) {
                    break
                }
            }
            match_length = 0
            if (lookahead >= MIN_MATCH && strstart > 0) {
                scanPos = Int64(strstart) - 1
                prev = window[Int64(scanPos)]
                if (lookahead > MAX_MATCH) {
                    scanEnd = Int64(strstart + MAX_MATCH)
                } else {
                    scanEnd = Int64(strstart + lookahead)
                }
                while (scanPos < scanEnd) {
                    if (prev != window[scanPos]) {
                        break
                    }
                    scanPos++
                }
                match_length = UInt32(scanPos) - strstart
            }
            if (match_length >= MIN_MATCH) {
                bflush = tallyDistance(1, match_length - MIN_MATCH)
                lookahead -= match_length
                strstart += match_length
                match_length = 0
            } else {
                bflush = tallyLiteral(window[Int64(strstart)])
                lookahead--
                strstart++
            }
            if (bflush) {
                flushBlock(block_start, Int64(strstart) - block_start, false)
                block_start = Int64(strstart)
                flushPending()
                if (avail_out == 0) {
                    return NEED_MORE
                }
            }
        }
        insert = 0
        if (flush == Z_FINISH) {
            flushBlock(block_start, Int64(strstart) - block_start, true)
            block_start = Int64(strstart)
            flushPending()
            if (avail_out == 0) {
                return FINISH_STARTED
            }
            return FINISH_DONE
        }
        if (sym_next == sym_end) {
            flushBlock(block_start, Int64(strstart) - block_start, false)
            block_start = Int64(strstart)
            flushPending()
            if (avail_out == 0) {
                return NEED_MORE
            }
            return BLOCK_DONE
        }
        return NEED_MORE
    }

    @Frozen
    private func deflateQuick(_: Int32): UInt32 {
        return FINISH_DONE
    }

    // configuration_table
    @Frozen
    private static func deflateFast(that: Deflate, flush: Int32): UInt32 {
        var hash_head: UInt32 = 0
        var bflush: Bool
        while (true) {
            if (that.lookahead < MIN_LOOKAHEAD) {
                that.fillWindow()
                if (that.lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
                    return NEED_MORE
                }
                if (that.lookahead == 0) {
                    break
                }
            }
            hash_head = 0
            if (that.lookahead >= MIN_MATCH) {
                hash_head = that.insertString(that.strstart)
            }
            if (hash_head != 0 && (that.strstart - hash_head) <= that.max_dist) {
                that.match_length = that.longestMatch(hash_head)
            }
            if (that.match_length >= MIN_MATCH) {
                bflush = that.tallyDistance(UInt32(Int64(that.strstart) - that.match_start), that.match_length - MIN_MATCH)
                that.lookahead -= that.match_length
                if (that.match_length <= that.max_insert_length && that.lookahead >= MIN_MATCH) {
                    that.match_length--
                    do {
                        that.strstart++
                        that.insertString(that.strstart)
                        that.match_length--
                    } while (that.match_length != 0)
                    that.strstart++
                } else {
                    that.strstart += that.match_length
                    that.match_length = 0
                    that.ins_h = UInt16(that.window[Int64(that.strstart)])
                    that.updateHash(that.ins_h, that.window[Int64(that.strstart) + 1])
                }
            } else {
                bflush = that.tallyLiteral(that.window[Int64(that.strstart)])
                that.lookahead--
                that.strstart++
            }
            if (bflush) {
                that.flushBlock(that.block_start, Int64(that.strstart) - that.block_start, false)
                that.block_start = Int64(that.strstart)
                that.flushPending()
                if (that.avail_out == 0) {
                    return NEED_MORE
                }
            }
        }
        that.insert = if (that.strstart < MIN_MATCH - 1) { that.strstart } else { MIN_MATCH - 1 }
        if (flush == Z_FINISH) {
            that.flushBlock(that.block_start, Int64(that.strstart) - that.block_start, true)
            that.block_start = Int64(that.strstart)
            that.flushPending()
            if (that.avail_out == 0) {
                return FINISH_STARTED
            }
            return FINISH_DONE
        }
        /* to do: Compress the block into one block only when it is full. Check whether Z_FIXED has a bug. */
        if (that.sym_next == that.sym_end) {
            that.flushBlock(that.block_start, Int64(that.strstart) - that.block_start, false)
            that.block_start = Int64(that.strstart)
            that.flushPending()
            if (that.avail_out == 0) {
                return NEED_MORE
            }
            return BLOCK_DONE
        }
        return NEED_MORE
    }

    @Frozen
    private func deflateMedium(_: UInt32): UInt32 {
        return FINISH_DONE
    }

    // configuration_table
    @Frozen
    private static func deflateSlow(that: Deflate, flush: Int32): UInt32 {
        var hash_head: UInt32 = 0
        var bflush: Bool = false
        while (true) {
            if (that.lookahead < MIN_LOOKAHEAD) {
                that.fillWindow()
                if (that.lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
                    return NEED_MORE
                }
                if (that.lookahead == 0) {
                    break
                }
            }
            hash_head = 0
            if (that.lookahead >= MIN_MATCH) {
                hash_head = that.insertString(that.strstart)
            }

            that.prev_length = that.match_length
            that.prev_start = if (that.match_start > 0) { UInt32(that.match_start) } else { 0 }
            that.match_length = MIN_MATCH - 1
            if (hash_head != 0 && that.prev_length < that.max_lazy_match && that.strstart - hash_head <= that.max_dist) {
                that.match_length = that.longestMatch(hash_head)
                /* to do: I don't know why. */
                if ((that.strategy == Z_FILTERED && that.match_length <= 5) ||
                    (that.match_length == MIN_MATCH && UInt32(Int64(that.strstart) - that.match_start) > TOO_FAR)) {
                    that.match_length = MIN_MATCH - 1
                }
            }
            if (that.prev_length >= MIN_MATCH && that.match_length <= that.prev_length) {
                var max_insert: UInt32 = that.strstart + that.lookahead - MIN_MATCH

                bflush = that.tallyDistance(that.strstart - 1 - that.prev_start, that.prev_length - MIN_MATCH)
                that.lookahead -= that.prev_length - 1
                that.prev_length -= 2
                do {
                    that.strstart++
                    if (that.strstart <= max_insert) {
                        that.updateHash(that.ins_h, that.window[Int64(that.strstart) + 2])
                        that.prev[Int64(that.strstart & that.w_mask)] = that.head[Int64(that.ins_h)]
                        that.head[Int64(that.ins_h)] = UInt16(that.strstart)
                    }
                    that.prev_length--
                } while (that.prev_length != 0)
                that.match_available = false
                that.match_length = MIN_MATCH - 1
                that.strstart++
                if (bflush) {
                    that.flushBlock(that.block_start, Int64(that.strstart) - that.block_start, false)
                    that.block_start = Int64(that.strstart)
                    that.flushPending()
                    if (that.avail_out == 0) {
                        return NEED_MORE
                    }
                }
            } else if (that.match_available) {
                bflush = that.tallyLiteral(that.window[Int64(that.strstart) - 1])
                if (bflush) {
                    that.flushBlock(that.block_start, Int64(that.strstart) - that.block_start, false)
                    that.block_start = Int64(that.strstart)
                    that.flushPending()
                }
                that.strstart++
                that.lookahead--
                if (that.avail_out == 0) {
                    return NEED_MORE
                }
            } else {
                that.match_available = true
                that.strstart++
                that.lookahead--
            }
        }
        if (that.match_available) {
            bflush = that.tallyLiteral(that.window[Int64(that.strstart) - 1])
            that.match_available = false
        }
        that.insert = if (that.strstart < MIN_MATCH - 1) { that.strstart } else { MIN_MATCH - 1 }
        if (flush == Z_FINISH) {
            that.flushBlock(that.block_start, Int64(that.strstart) - that.block_start, true)
            that.block_start = Int64(that.strstart)
            that.flushPending()
            if (that.avail_out == 0) {
                return FINISH_STARTED
            }
            return FINISH_DONE
        }
        if (that.sym_next == that.sym_end) {
            that.flushBlock(that.block_start, Int64(that.strstart) - that.block_start, false)
            that.block_start = Int64(that.strstart)
            that.flushPending()
            if (that.avail_out == 0) {
                return NEED_MORE
            }
            return BLOCK_DONE
        }
        return NEED_MORE
    }

    @Frozen
    private func fillWindow(): Unit {
        var n: UInt32
        var more: UInt32
        var offset: UInt32
        do {
            more = window_size - lookahead - strstart
            if (strstart >= window_size - MIN_LOOKAHEAD) {
                offset = w_size - more
                for (i in 0..Int64(offset)) {
                    window[i] = window[i + Int64(w_size)]
                }
                match_start -= Int64(w_size)
                strstart -= w_size
                block_start -= Int64(w_size)
                if (insert > strstart) {
                    insert = strstart
                }
                let off = UInt16(w_size)
                for (i in 0..head.size) {
                    let j = head[i]
                    head[i] = if (j > off) { j - off } else { 0 }
                }
                for (i in 0..prev.size) {
                    let j = prev[i]
                    prev[i] = if (j > off) { j - off } else { 0 }
                }
                more += w_size
            }
            if (avail_in == 0) {
                break
            }
            n = readBuf(window, strstart + lookahead, more)
            lookahead += n
            /* to do: */
            if (lookahead + insert >= MIN_MATCH) {
                var str: Int64 = Int64(strstart - insert)
                updateHash(UInt16(window[str]), window[str + 1])
                while (insert != 0) {
                    updateHash(ins_h, window[str + Int64(MIN_MATCH) - 1])
                    prev[str & Int64(w_mask)] = head[Int64(ins_h)]
                    head[Int64(ins_h)] = UInt16(str)
                    str++
                    insert--
                    if (lookahead + insert < MIN_MATCH) {
                        break
                    }
                }
            }
        } while (lookahead < MIN_LOOKAHEAD && avail_in != 0)
    }

    @Frozen
    private func updateHash(hash: UInt16, c: UInt8): Unit {
        ins_h = ((hash << hash_shift) ^ UInt16(c)) & UInt16(hash_mask)
    }

    @Frozen
    private func insertString(pos: UInt32): UInt32 {
        updateHash(ins_h, window[Int64(pos) + 2])
        let prev_pos: UInt32 = UInt32(head[Int64(ins_h)])
        prev[Int64(pos & w_mask)] = UInt16(prev_pos)
        head[Int64(ins_h)] = UInt16(pos)
        return prev_pos
    }

    @Frozen
    private func readBuf(buf: Array<UInt8>, start: UInt32, size: UInt32): UInt32 {
        var len: Int64 = avail_in
        if (len > Int64(size)) {
            len = Int64(size)
        }
        if (len == 0) {
            return 0
        }
        avail_in -= len
        for (i in 0..len) {
            buf[Int64(start) + i] = next_in[pos_in]
            pos_in++
        }
        match (wrap) {
            case 2 => adler = crc32(adler, buf, Int64(start), len)
            case 1 => adler = adler32(adler, buf, Int64(start), len)
            case _ => ()
        }
        total_in += len
        return UInt32(len)
    }

    @Frozen
    private func longestMatch(curmatch: UInt32): UInt32 {
        var len: UInt32
        var cur_match: UInt32 = curmatch
        var chain_length: UInt32 = max_chain_length
        var best_len: UInt32 = prev_length
        var nice_len: UInt32 = nice_length
        var limit: UInt32 = if (strstart > max_dist) {strstart - max_dist} else { 0 }
        var cur_pos: UInt32 = strstart
        var max_pos: UInt32 = strstart + if (lookahead >= MAX_MATCH) { MAX_MATCH } else { lookahead }
        if (prev_length >= good_length) {
            chain_length >>= 2
        }
        if (nice_len > lookahead) {
            nice_len = lookahead
        }
        do {
            var match_pos: UInt32 = cur_match
            if (window[Int64(match_pos + best_len)] != window[Int64(cur_pos + best_len)] || 
                window[Int64(match_pos + best_len - 1)] != window[Int64(cur_pos + best_len - 1)] || 
                window[Int64(match_pos)] != window[Int64(cur_pos)] || 
                window[Int64(match_pos + 1)] != window[Int64(cur_pos + 1)]) {
                cur_match = UInt32(prev[Int64(cur_match & w_mask)])
                chain_length--
                /* The break code is added to the language bug. To do: The code is deleted after this problem is solved. */
                if (cur_match > limit && chain_length != 0) {
                    continue
                }
                break
            }
            match_pos += 2
            cur_pos += 2
            while (cur_pos < max_pos) {
                if (window[Int64(match_pos)] != window[Int64(cur_pos)]) {
                    break
                }
                cur_pos++
                match_pos++
            }
            len = cur_pos - strstart
            cur_pos = strstart
            if (len > best_len) {
                match_start = Int64(cur_match)
                best_len = len
                if (len >= nice_len) {
                    break
                }
            }
            cur_match = UInt32(prev[Int64(cur_match & w_mask)])
            chain_length--
        } while (cur_match > limit && chain_length != 0)
        if (best_len > lookahead) {
            return lookahead
        }
        return best_len
    }

    @Frozen
    private func treeInit(): Unit {
        initStaticTree()
        l_desc.dyn_tree = dyn_ltree
        l_desc.static_desc = static_l_desc
        d_desc.dyn_tree = dyn_dtree
        d_desc.static_desc = static_d_desc
        bl_desc.dyn_tree = bl_tree
        bl_desc.static_desc = static_bl_desc
        bits_buf = 0
        bits_valid = 0
        initBlock()
    }

    /* to do */
    @Frozen
    private func initStaticTree() {}

    @Frozen
    private func initBlock(): Unit {
        for (i in 0..Int64(L_CODES)) {
            dyn_ltree[i][freq_code] = 0
        }
        for (i in 0..Int64(D_CODES)) {
            dyn_dtree[i][freq_code] = 0
        }
        for (i in 0..Int64(BL_CODES)) {
            bl_tree[i][freq_code] = 0
        }
        dyn_ltree[END_BLOCK][freq_code] = 1
        opt_len = 0
        static_len = 0
        sym_next = lit_bufsize
    }

    @Frozen
    private func bitsReverse(code: UInt32, len: Int32): UInt32 {
        var result: UInt32 = 0
        var val: UInt32 = code
        var length: Int32 = len
        do {
            result |= val & 1
            val >>= 1
            result <<= 1
            length -= 1
        } while (length > 0)
        return result >> 1
    }

    @Frozen
    private func bitsFlush(): Unit {
        if (bits_valid == 16) {
            putShort(bits_buf)
            bits_buf = 0
            bits_valid = 0
        } else if (bits_valid >= 8) {
            putByte(UInt8(bits_buf & 0xFF))
            bits_buf >>= 8
            bits_valid -= 8
        }
    }

    @Frozen
    private func bitsFlushAlign(): Unit {
        if (bits_valid > 8) {
            putShort(bits_buf)
        } else if (bits_valid > 0) {
            putByte(UInt8(bits_buf & 0xFF))
        }
        bits_buf = 0
        bits_valid = 0
    }

    @Frozen
    private func flushBlock(start: Int64, stored_lenb: Int64, last: Bool): Unit {
        var opt_lenb: UInt32 = 0
        var static_lenb: UInt32 = 0
        var max_bindex: UInt32 = 0
        if (level > 0) {
            match (data_type) {
                case 0 => data_type = detectDataType()
                case _ => ()
            }
            buildTree(l_desc)
            buildTree(d_desc)
            max_bindex = buildBLTree()
            opt_lenb = UInt32((opt_len + 3 + 7) >> 3)
            static_lenb = UInt32((static_len + 3 + 7) >> 3)
            if (static_lenb <= opt_lenb) {
                opt_lenb = static_lenb
            }
        } else {
            static_lenb = UInt32(stored_lenb) + 5
            opt_lenb = static_lenb
        }
        var last_val: UInt32 = if (last) { 1 } else { 0 }
        if (stored_lenb + 4 <= Int64(opt_lenb) && start >= 0) {
            storedBlock(UInt32(start), UInt32(stored_lenb), last)
        } else if (strategy == Z_FIXED || static_lenb == opt_lenb) {
            sendBits((STATIC_TREES << 1) + last_val, 3)
            compressBlock(static_ltree, static_dtree)
        } else {
            sendBits((DYN_TREES << 1) + last_val, 3)
            sendAllTrees(l_desc.max_code + 1, d_desc.max_code + 1, max_bindex + 1)
            compressBlock(dyn_ltree, dyn_dtree)
        }
        initBlock()
        if (last) {
            bitsFlushAlign()
        }
    }

    @Frozen
    private func flushEmptyStaticBlock(): Unit {
        sendBits(STATIC_TREES << 1, 3)
        sendBits(UInt32(static_ltree[END_BLOCK][freq_code]), UInt32(static_ltree[END_BLOCK][dad_len]))
        bitsFlush()
    }

    @Frozen
    private func detectDataType(): Int32 {
        var black_mask: UInt32 = 0xF3FFC07F
        for (i in 0..=31) {
            if ((black_mask & 1) != 0 && dyn_ltree[i][freq_code] != 0) {
                return Z_BINARY
            }
        }
        if (dyn_ltree[9][freq_code] != 0 || dyn_ltree[10][freq_code] != 0 || dyn_ltree[13][freq_code] != 0) {
            return Z_TEXT
        }
        for (i in 32..Int64(LITERALS)) {
            if (dyn_ltree[i][freq_code] != 0) {
                return Z_TEXT
            }
        }
        return Z_BINARY
    }

    @Frozen
    private func downHeap(tree: Array<CTData>, pos: Int64): Unit {
        var k: Int64 = pos
        var v: Int64 = Int64(heap[k])
        var j: Int64 = Int64(k << 1)
        var n: Int64
        var m: Int64
        while (j <= Int64(heap_len)) {
            n = Int64(heap[j + 1])
            m = Int64(heap[j])
            if (j < Int64(heap_len) && (tree[n][freq_code] < tree[m][freq_code] ||
               (tree[n][freq_code] == tree[m][freq_code] && depth[n] <= depth[m]))) {
                j++
            }
            n = v
            m = Int64(heap[j])
            if (tree[n][freq_code] < tree[m][freq_code] ||
               (tree[n][freq_code] == tree[m][freq_code] && depth[n] <= depth[m])) {
                break
            }
            heap[k] = heap[j]
            k = j
            j <<= 1
        }
        heap[k] = UInt32(v)
    }

    @Frozen
    private func genBitLength(desc: TreeDescription): Unit {
        var tree: Array<CTData> = desc.dyn_tree
        var max_code: Int64 = Int64(desc.max_code)
        var stree: Array<CTData> = desc.static_desc.static_tree
        var extra: Array<UInt8> = desc.static_desc.extra_bits
        var base: Int64 = Int64(desc.static_desc.extra_base)
        var max_length: Int64 = Int64(desc.static_desc.max_length)
        for (i in 0..=Int64(MAX_BITS)) {
            bl_count[i] = 0
        }
        tree[Int64(heap[heap_max])][dad_len] = 0
        var n: Int64
        var m: Int64
        var bits: Int64
        var xbits: UInt32
        var overflow: Int32 = 0
        var freq: UInt16
        var start: Int64 = heap_max + 1
        for (i in start..Int64(HEAP_SIZE)) {
            n = Int64(heap[i])
            bits = Int64(tree[Int64(tree[n][dad_len])][dad_len] + 1)
            if (bits > max_length) {
                bits = max_length
                overflow++
            }
            tree[n][dad_len] = UInt16(bits)

            if (n > max_code) {
                continue
            }
            bl_count[bits]++
            xbits = 0
            if (n >= base) {
                xbits = UInt32(extra[n - base])
            }
            freq = tree[n][freq_code]
            opt_len += Int64(freq) * (Int64(bits) + Int64(xbits))
            if (stree.size > 0) {
                static_len += Int64(freq) * (Int64(stree[n][dad_len]) + Int64(xbits))
            }
        }
        if (overflow == 0) {
            return
        }
        do {
            bits = max_length - 1
            while (bl_count[bits] == 0) {
                bits--
            }
            bl_count[bits]--
            bl_count[bits + 1] = bl_count[bits + 1] + 2
            bl_count[max_length]--
            overflow -= 2
        } while (overflow > 0)
        var h: Int64 = Int64(HEAP_SIZE)
        bits = max_length
        while (bits != 0) {
            n = Int64(bl_count[bits])
            while (n != 0) {
                h--
                m = Int64(heap[h])
                if (m > max_code) {
                    continue
                }
                if (tree[m][dad_len] != UInt16(bits)) {
                    if (UInt32(bits) >= UInt32(tree[m][dad_len])) {
                        opt_len += (Int64(bits) - Int64(tree[m][dad_len])) * Int64(tree[m][freq_code])
                    } else {
                        opt_len += (Int64(tree[m][dad_len]) - Int64(bits)) * Int64(tree[m][freq_code])
                    }
                    tree[m][dad_len] = UInt16(bits)
                }
                n--
            }
            bits--
        }
    }

    @Frozen
    private func genCodes(tree: Array<CTData>, max_code: UInt32, bl_count: Array<UInt16>): Unit {
        var code: UInt32 = 0
        var max_bits: Int64 = Int64(MAX_BITS)
        var next_code: Array<UInt16> = Array<UInt16>(max_bits + 1, { i => 0 })
        for (i in 1..=max_bits) {
            code = (code + UInt32(bl_count[i - 1])) << 1
            next_code[i] = UInt16(code)
        }
        var len: UInt16
        for (i in 0..=Int64(max_code)) {
            len = tree[i][dad_len]
            if (len == 0) {
                continue
            }
            tree[i][freq_code] = UInt16(bitsReverse(UInt32(next_code[Int64(len)]), Int32(len)))
            next_code[Int64(len)] = next_code[Int64(len)] + 1
        }
    }

    @Frozen
    private func buildTree(desc: TreeDescription): Unit {
        var tree: Array<CTData> = desc.dyn_tree
        var stree: Array<CTData> = desc.static_desc.static_tree
        var element: Int64 = Int64(desc.static_desc.max_element)
        var max_code: Int32 = -1
        var node: Int64
        heap_len = 0
        heap_max = Int64(HEAP_SIZE)
        for (i in 0..element) {
            if (tree[i][freq_code] != 0) {
                heap_len++
                heap[Int64(heap_len)] = UInt32(i)
                max_code = Int32(i)
                depth[i] = 0
            } else {
                tree[i][dad_len] = 0
            }
        }
        while (heap_len < 2) {
            heap_len++
            if (max_code < 2) {
                max_code++
                heap[Int64(heap_len)] = UInt32(max_code)
                node = Int64(max_code)
            } else {
                heap[Int64(heap_len)] = 0
                node = 0
            }
            tree[node][freq_code] = 1
            depth[node] = 0
            opt_len--
            if (stree.size > 0) {
                static_len -= Int64(stree[node][dad_len])
            }
        }
        desc.max_code = UInt32(max_code)
        for (i in Int64(heap_len/2)..=1:-1) {
            downHeap(tree, i)
        }
        node = element
        var top1: Int64
        var top2: Int64
        do {
            top1 = Int64(heap[SMALLEST])
            heap[SMALLEST] = heap[Int64(heap_len)]
            heap_len--
            downHeap(tree, SMALLEST)
            top2 = Int64(heap[SMALLEST])
            heap_max--
            heap[heap_max] = UInt32(top1)
            heap_max--
            heap[heap_max] = UInt32(top2)
            tree[node][freq_code] = tree[top1][freq_code] + tree[top2][freq_code]
            depth[node] = UInt8(if (depth[top1] >= depth[top2]) { depth[top1] + 1 } else { depth[top2] + 1 })
            tree[top1][dad_len] = UInt16(node)
            tree[top2][dad_len] = UInt16(node)
            heap[SMALLEST] = UInt32(node)
            node++
            downHeap(tree, SMALLEST)
        } while (heap_len >= 2)
        heap_max--
        heap[heap_max] = heap[SMALLEST]
        genBitLength(desc)
        genCodes(tree, UInt32(max_code), bl_count)
    }

    @Frozen
    private func scanTree(tree: Array<CTData>, max_code: UInt32): Unit {
        var prevlen: Int32 = -1
        var curlen: Int32
        var nextlen: Int32 = Int32(tree[0][dad_len])
        var count: Int32 = 0
        var max_count: Int32 = 7
        var min_count: Int32 = 4
        if (nextlen == 0) {
            max_count = 138
            min_count = 3
        }
        tree[Int64(max_code) + 1][dad_len] = UInt16(0xFFFF)
        for (n in 0..=Int64(max_code)) {
            curlen = nextlen
            nextlen = Int32(tree[n + 1][dad_len])
            count++
            if (count < max_count && curlen == nextlen) {
                continue
            } else if (count < min_count) {
                bl_tree[Int64(curlen)][freq_code] += UInt16(count)
            } else if (curlen != 0) {
                if (curlen != prevlen) {
                    bl_tree[Int64(curlen)][freq_code]++
                }
                bl_tree[REP_3_6][freq_code]++
            } else if (count <= 10) {
                bl_tree[REPZ_3_10][freq_code]++
            } else {
                bl_tree[REPZ_11_138][freq_code]++
            }
            count = 0
            prevlen = curlen
            if (nextlen == 0) {
                max_count = 138
                min_count = 3
            } else if (curlen == nextlen) {
                max_count = 6
                min_count = 3
            } else {
                max_count = 7
                min_count = 4
            }
        }
    }

    @Frozen
    private func buildBLTree(): UInt32 {
        scanTree(dyn_ltree, l_desc.max_code)
        scanTree(dyn_dtree, d_desc.max_code)
        buildTree(bl_desc)
        var max_bindex: UInt32 = BL_CODES - 1
        while (max_bindex >= 3) {
            if (bl_tree[bl_order[Int64(max_bindex)]][dad_len] != 0) {
                break
            }
            max_bindex--
        }
        opt_len += Int64(3 * (max_bindex + 1) + 5 + 5 + 4)
        return max_bindex
    }

    @Frozen
    private func storedBlock(s: UInt32, l: UInt32, las: Bool): Unit {
        var start = Int64(s)
        var len = UInt16(l)
        var last_val: UInt32 = if (las) { 1 } else { 0 }
        sendBits((STORED_BLOCK << 1) + last_val, 3)
        bitsFlushAlign()
        putShort(len & 0xFFFF)
        putShort((!len) & 0xFFFF) // TODO put_short(s, (ush)~stored_len);
        if (len > 0) {
            for (i in 0..len) {
                pending_buf[Int64(pending_start + pending)] = window[start]
                pending++
                start++
            }
        }
    }

    @Frozen
    private func compressBlock(ltree: Array<CTData>, dtree: Array<CTData>): Unit {
        var dist: UInt32
        var sx: Int64 = lit_bufsize

        var lc: Int64
        var code: Int64
        var extra: UInt32
        var index: Int64
        if (sym_next != lit_bufsize) {
            do {
                dist = UInt32(sym_buf[sx]) & 0xFF
                sx++
                dist += (UInt32(sym_buf[sx]) & 0xFF) << 8
                sx++
                lc = Int64(sym_buf[sx])
                sx++
                if (dist == 0) {
                    sendBits(UInt32(ltree[lc][freq_code]), UInt32(ltree[lc][dad_len]))
                } else {
                    code = Int64(length_code[lc])
                    index = code + Int64(LITERALS) + 1
                    sendBits(UInt32(ltree[index][freq_code]), UInt32(ltree[index][dad_len]))
                    extra = UInt32(extra_lbits[code])
                    if (extra != 0) {
                        lc -= Int64(base_length[code])
                        sendBits(UInt32(lc), extra)
                    }
                    dist--
                    if (dist < 256) {
                        code = Int64(dist_code[Int64(dist)])
                    } else {
                        code = Int64(dist_code[256 + (Int64(dist) >> 7)])
                    }
                    sendBits(UInt32(dtree[code][freq_code]), UInt32(dtree[code][dad_len]))
                    extra = UInt32(extra_dbits[code])
                    if (extra != 0) {
                        dist -= base_dist[code]
                        sendBits(dist, extra)
                    }
                }
            } while (sx < sym_next)
        }
        sendBits(UInt32(ltree[END_BLOCK][freq_code]), UInt32(ltree[END_BLOCK][dad_len]))
    }

    @Frozen
    private func sendTree(tree: Array<CTData>, max_code: UInt32): Unit {
        var max_count: UInt32 = 7
        var min_count: UInt32 = 4
        var curlen: Int64
        var nextlen: Int64 = Int64(tree[0][dad_len])
        var count: UInt32 = 0
        var prevlen: Int64 = -1
        if (nextlen == 0) {
            max_count = 138
            min_count = 3
        }
        for (n in 0..=Int64(max_code)) {
            curlen = nextlen
            nextlen = Int64(tree[n + 1][dad_len])
            count++
            if (count < max_count && curlen == nextlen) {
                continue
            } else if (count < min_count) {
                do {
                    sendBits(UInt32(bl_tree[curlen][freq_code]), UInt32(bl_tree[curlen][dad_len]))
                    count--
                } while (count != 0)
            } else if (curlen != 0) {
                if (curlen != prevlen) {
                    sendBits(UInt32(bl_tree[curlen][freq_code]), UInt32(bl_tree[curlen][dad_len]))
                    count--
                }
                sendBits(UInt32(bl_tree[REP_3_6][freq_code]), UInt32(bl_tree[REP_3_6][dad_len]))
                sendBits(count - 3, 2)
            } else if (count <= 10) {
                sendBits(UInt32(bl_tree[REPZ_3_10][freq_code]), UInt32(bl_tree[REPZ_3_10][dad_len]))
                sendBits(count - 3, 3)
            } else {
                sendBits(UInt32(bl_tree[REPZ_11_138][freq_code]), UInt32(bl_tree[REPZ_11_138][dad_len]))
                sendBits(count - 11, 7)
            }
            count = 0
            prevlen = curlen
            if (nextlen == 0) {
                max_count = 138
                min_count = 3
            } else if (curlen == nextlen) {
                max_count = 6
                min_count = 3
            } else {
                max_count = 7
                min_count = 4
            }
        }
    }

    @Frozen
    private func sendAllTrees(lcodes: UInt32, dcodes: UInt32, blcodes: UInt32): Unit {
        sendBits(lcodes - 257, 5)
        sendBits(dcodes - 1, 5)
        sendBits(blcodes - 4, 4)
        for (i in 0..Int64(blcodes)) {
            sendBits(UInt32(bl_tree[bl_order[i]][dad_len]), 3)
        }
        sendTree(dyn_ltree, lcodes - 1)
        sendTree(dyn_dtree, dcodes - 1)
    }
}