apiVersion: v1
kind: ConfigMap
metadata:
  name: lua-libs
  namespace: istio-system
data:
    moesif-core-base64.lua: local a={}local b=_G.bit32 and _G.bit32.extract;if not b then if _G.bit then local c,d,e=_G.bit.lshift,_G.bit.rshift,_G.bit.band;b=function(f,g,h)return e(d(f,g),c(1,h)-1)end elseif _G._VERSION>="Lua 5.3"then b=load[[return function( v, from, width ) return ( v >> from ) & ((1 << width) - 1) end]]()else b=function(f,g,h)local i=0;local j=2^g;for k=0,h-1 do local l=j+j;if f%l>=j then i=i+2^k end;j=l end;return i end end end;function a.makeencoder(m,n,o)local p={}for q,r in pairs{[0]='A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9',m or'+',n or'/',o or'='}do p[q]=r:byte()end;return p end;function a.makedecoder(m,n,o)local s={}for q,t in pairs(a.makeencoder(m,n,o))do s[t]=q end;return s end;local u=a.makeencoder()local v=a.makedecoder()local r,w=string.char,table.concat;function a.encode(x,p,y)p=p or u;local z,A,B={},1,#x;local C=B%3;local D={}for k=1,B-C,3 do local E,F,G=x:byte(k,k+2)local f=E*0x10000+F*0x100+G;local H;if y then H=D[f]if not H then H=r(p[b(f,18,6)],p[b(f,12,6)],p[b(f,6,6)],p[b(f,0,6)])D[f]=H end else H=r(p[b(f,18,6)],p[b(f,12,6)],p[b(f,6,6)],p[b(f,0,6)])end;z[A]=H;A=A+1 end;if C==2 then local E,F=x:byte(B-1,B)local f=E*0x10000+F*0x100;z[A]=r(p[b(f,18,6)],p[b(f,12,6)],p[b(f,6,6)],p[64])elseif C==1 then local f=x:byte(B)*0x10000;z[A]=r(p[b(f,18,6)],p[b(f,12,6)],p[64],p[64])end;return w(z)end;function a.decode(I,s,y)s=s or v;local J='[^%w%+%/%=]'if s then local m,n;for t,q in pairs(s)do if q==62 then m=t elseif q==63 then n=t end end;J=('[^%%w%%%s%%%s%%=]'):format(r(m),r(n))end;I=I:gsub(J,'')local D=y and{}local z,A={},1;local B=#I;local K=I:sub(-2)=='=='and 2 or I:sub(-1)=='='and 1 or 0;for k=1,K>0 and B-4 or B,4 do local E,F,G,L=I:byte(k,k+3)local H;if y then local M=E*0x1000000+F*0x10000+G*0x100+L;H=D[M]if not H then local f=s[E]*0x40000+s[F]*0x1000+s[G]*0x40+s[L]H=r(b(f,16,8),b(f,8,8),b(f,0,8))D[M]=H end else local f=s[E]*0x40000+s[F]*0x1000+s[G]*0x40+s[L]H=r(b(f,16,8),b(f,8,8),b(f,0,8))end;z[A]=H;A=A+1 end;if K==1 then local E,F,G=I:byte(B-3,B-1)local f=s[E]*0x40000+s[F]*0x1000+s[G]*0x40;z[A]=r(b(f,16,8),b(f,8,8))elseif K==2 then local E,F=I:byte(B-3,B-2)local f=s[E]*0x40000+s[F]*0x1000;z[A]=r(b(f,16,8))end;return w(z)end;return a
    moesif-core-lib_deflate.lua: |-
      --[[--
        LibDeflate 1.0.0-release <br>
        Pure Lua compressor and decompressor with high compression ratio using
        DEFLATE/zlib format.
        @file LibDeflate.lua
        @author Haoqian He (Github: SafeteeWoW; World of Warcraft: Safetyy-Illidan(US))
        @copyright LibDeflate <2018> Haoqian He
        @license GNU General Public License Version 3 or later
        This library is implemented according to the following specifications. <br>
        Report a bug if LibDeflate is not fully compliant with those specs. <br>
        Both compressors and decompressors have been implemented in the library.<br>
        1. RFC1950: DEFLATE Compressed Data Format Specification version 1.3 <br>
        https://tools.ietf.org/html/rfc1951 <br>
        2. RFC1951: ZLIB Compressed Data Format Specification version 3.3 <br>
        https://tools.ietf.org/html/rfc1950 <br>
        This library requires Lua 5.1/5.2/5.3 interpreter or LuaJIT v2.0+. <br>
        This library does not have any dependencies. <br>
        <br>
        This file "LibDeflate.lua" is the only source file of
        the library. <br>
        Submit suggestions or report bugs to
        https://github.com/safeteeWow/LibDeflate/issues
        ]]
        
        --[[
        This program is free software: you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        any later version.
        This program is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.
        You should have received a copy of the GNU General Public License
        along with this program.  If not, see https://www.gnu.org/licenses/.
        Credits:
        1. zlib, by Jean-loup Gailly (compression) and Mark Adler (decompression).
          http://www.zlib.net/
          Licensed under zlib License. http://www.zlib.net/zlib_license.html
          For the compression algorithm.
        2. puff, by Mark Adler. https://github.com/madler/zlib/tree/master/contrib/puff
          Licensed under zlib License. http://www.zlib.net/zlib_license.html
          For the decompression algorithm.
        3. LibCompress, by jjsheets and Galmok of European Stormrage (Horde)
          https://www.wowace.com/projects/libcompress
          Licensed under GPLv2.
          https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
          For the code to create customized codec.
        4. WeakAuras2,
          https://github.com/WeakAuras/WeakAuras2
          Licensed under GPLv2.
          For the 6bit encoding and decoding.
        ]]
        
        --[[
          Curseforge auto-packaging replacements:
          Project Date: @project-date-iso@
          Project Hash: @project-hash@
          Project Version: @project-version@
        --]]
        
        local LibDeflate
        
        do
          -- Semantic version. all lowercase.
          -- Suffix can be alpha1, alpha2, beta1, beta2, rc1, rc2, etc.
          -- NOTE: Two version numbers needs to modify.
          -- 1. On the top of LibDeflate.lua
          -- 2. HERE
          local _VERSION = "1.0.0-release"
        
          local _COPYRIGHT =
          "LibDeflate ".._VERSION
          .." Copyright (C) 2018 Haoqian He."
          .." License GPLv3+: GNU GPL version 3 or later"
        
          -- Register in the World of Warcraft library "LibStub" if detected.
          if LibStub then
            local MAJOR, MINOR = "LibDeflate", -1
            -- When MAJOR is changed, I should name it as LibDeflate2
            local lib, minor = LibStub:GetLibrary(MAJOR, true)
            if lib and minor and minor >= MINOR then -- No need to update.
              return lib
            else -- Update or first time register
              LibDeflate = LibStub:NewLibrary(MAJOR, _VERSION)
              -- NOTE: It is important that new version has implemented
              -- all exported APIs and tables in the old version,
              -- so the old library is fully garbage collected,
              -- and we 100% ensure the backward compatibility.
            end
          else -- "LibStub" is not detected.
            LibDeflate = {}
          end
        
          LibDeflate._VERSION = _VERSION
          LibDeflate._COPYRIGHT = _COPYRIGHT
        end
        
        -- localize Lua api for faster access.
        local assert = assert
        local error = error
        local pairs = pairs
        local string_byte = string.byte
        local string_char = string.char
        local string_find = string.find
        local string_gsub = string.gsub
        local string_sub = string.sub
        local table_concat = table.concat
        local table_sort = table.sort
        local tostring = tostring
        local type = type
        
        -- Converts i to 2^i, (0<=i<=32)
        -- This is used to implement bit left shift and bit right shift.
        -- "x >> y" in C:   "(x-x%_pow2[y])/_pow2[y]" in Lua
        -- "x << y" in C:   "x*_pow2[y]" in Lua
        local _pow2 = {}
        
        -- Converts any byte to a character, (0<=byte<=255)
        local _byte_to_char = {}
        
        -- _reverseBitsTbl[len][val] stores the bit reverse of
        -- the number with bit length "len" and value "val"
        -- For example, decimal number 6 with bits length 5 is binary 00110
        -- It's reverse is binary 01100,
        -- which is decimal 12 and 12 == _reverseBitsTbl[5][6]
        -- 1<=len<=9, 0<=val<=2^len-1
        -- The reason for 1<=len<=9 is that the max of min bitlen of huffman code
        -- of a huffman alphabet is 9?
        local _reverse_bits_tbl = {}
        
        -- Convert a LZ77 length (3<=len<=258) to
        -- a deflate literal/LZ77_length code (257<=code<=285)
        local _length_to_deflate_code = {}
        
        -- convert a LZ77 length (3<=len<=258) to
        -- a deflate literal/LZ77_length code extra bits.
        local _length_to_deflate_extra_bits = {}
        
        -- Convert a LZ77 length (3<=len<=258) to
        -- a deflate literal/LZ77_length code extra bit length.
        local _length_to_deflate_extra_bitlen = {}
        
        -- Convert a small LZ77 distance (1<=dist<=256) to a deflate code.
        local _dist256_to_deflate_code = {}
        
        -- Convert a small LZ77 distance (1<=dist<=256) to
        -- a deflate distance code extra bits.
        local _dist256_to_deflate_extra_bits = {}
        
        -- Convert a small LZ77 distance (1<=dist<=256) to
        -- a deflate distance code extra bit length.
        local _dist256_to_deflate_extra_bitlen = {}
        
        -- Convert a literal/LZ77_length deflate code to LZ77 base length
        -- The key of the table is (code - 256), 257<=code<=285
        local _literal_deflate_code_to_base_len = {
          3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
          35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258,
        }
        
        -- Convert a literal/LZ77_length deflate code to base LZ77 length extra bits
        -- The key of the table is (code - 256), 257<=code<=285
        local _literal_deflate_code_to_extra_bitlen = {
          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,
        }
        
        -- Convert a distance deflate code to base LZ77 distance. (0<=code<=29)
        local _dist_deflate_code_to_base_dist = {
          [0] = 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
          257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
          8193, 12289, 16385, 24577,
        }
        
        -- Convert a distance deflate code to LZ77 bits length. (0<=code<=29)
        local _dist_deflate_code_to_extra_bitlen = {
          [0] = 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,
        }
        
        -- The code order of the first huffman header in the dynamic deflate block.
        -- See the page 12 of RFC1951
        local _rle_codes_huffman_bitlen_order = {16, 17, 18,
          0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
        }
        
        -- The following tables are used by fixed deflate block.
        -- The value of these tables are assigned at the bottom of the source.
        
        -- The huffman code of the literal/LZ77_length deflate codes,
        -- in fixed deflate block.
        local _fix_block_literal_huffman_code
        
        -- Convert huffman code of the literal/LZ77_length to deflate codes,
        -- in fixed deflate block.
        local _fix_block_literal_huffman_to_deflate_code
        
        -- The bit length of the huffman code of literal/LZ77_length deflate codes,
        -- in fixed deflate block.
        local _fix_block_literal_huffman_bitlen
        
        -- The count of each bit length of the literal/LZ77_length deflate codes,
        -- in fixed deflate block.
        local _fix_block_literal_huffman_bitlen_count
        
        -- The huffman code of the distance deflate codes,
        -- in fixed deflate block.
        local _fix_block_dist_huffman_code
        
        -- Convert huffman code of the distance to deflate codes,
        -- in fixed deflate block.
        local _fix_block_dist_huffman_to_deflate_code
        
        -- The bit length of the huffman code of the distance deflate codes,
        -- in fixed deflate block.
        local _fix_block_dist_huffman_bitlen
        
        -- The count of each bit length of the huffman code of
        -- the distance deflate codes,
        -- in fixed deflate block.
        local _fix_block_dist_huffman_bitlen_count
        
        for i = 0, 255 do
          _byte_to_char[i] = string_char(i)
        end
        
        do
          local pow = 1
          for i = 0, 32 do
            _pow2[i] = pow
            pow = pow * 2
          end
        end
        
        for i = 1, 9 do
          _reverse_bits_tbl[i] = {}
          for j=0, _pow2[i+1]-1 do
            local reverse = 0
            local value = j
            for _ = 1, i do
              -- The following line is equivalent to "res | (code %2)" in C.
              reverse = reverse - reverse%2
                + (((reverse%2==1) or (value % 2) == 1) and 1 or 0)
              value = (value-value%2)/2
              reverse = reverse * 2
            end
            _reverse_bits_tbl[i][j] = (reverse-reverse%2)/2
          end
        end
        
        -- The source code is written according to the pattern in the numbers
        -- in RFC1951 Page10.
        do
          local a = 18
          local b = 16
          local c = 265
          local bitlen = 1
          for len = 3, 258 do
            if len <= 10 then
              _length_to_deflate_code[len] = len + 254
              _length_to_deflate_extra_bitlen[len] = 0
            elseif len == 258 then
              _length_to_deflate_code[len] = 285
              _length_to_deflate_extra_bitlen[len] = 0
            else
              if len > a then
                a = a + b
                b = b * 2
                c = c + 4
                bitlen = bitlen + 1
              end
              local t = len-a-1+b/2
              _length_to_deflate_code[len] = (t-(t%(b/8)))/(b/8) + c
              _length_to_deflate_extra_bitlen[len] = bitlen
              _length_to_deflate_extra_bits[len] = t % (b/8)
            end
          end
        end
        
        -- The source code is written according to the pattern in the numbers
        -- in RFC1951 Page11.
        do
          _dist256_to_deflate_code[1] = 0
          _dist256_to_deflate_code[2] = 1
          _dist256_to_deflate_extra_bitlen[1] = 0
          _dist256_to_deflate_extra_bitlen[2] = 0
        
          local a = 3
          local b = 4
          local code = 2
          local bitlen = 0
          for dist = 3, 256 do
            if dist > b then
              a = a * 2
              b = b * 2
              code = code + 2
              bitlen = bitlen + 1
            end
            _dist256_to_deflate_code[dist] = (dist <= a) and code or (code+1)
            _dist256_to_deflate_extra_bitlen[dist] = (bitlen < 0) and 0 or bitlen
            if b >= 8 then
              _dist256_to_deflate_extra_bits[dist] = (dist-b/2-1) % (b/4)
            end
          end
        end
        
        --- Calculate the Adler-32 checksum of the string. <br>
        -- See RFC1950 Page 9 https://tools.ietf.org/html/rfc1950 for the
        -- definition of Adler-32 checksum.
        -- @param str [string] the input string to calcuate its Adler-32 checksum.
        -- @return [integer] The Adler-32 checksum, which is greater or equal to 0,
        -- and less than 2^32 (4294967296).
        function LibDeflate:Adler32(str)
          -- This function is loop unrolled by better performance.
          --
          -- Here is the minimum code:
          --
          -- local a = 1
          -- local b = 0
          -- for i=1, #str do
          -- 		local s = string.byte(str, i, i)
          -- 		a = (a+s)%65521
          -- 		b = (b+a)%65521
          -- 		end
          -- return b*65536+a
          if type(str) ~= "string" then
            error(("Usage: LibDeflate:Adler32(str):"
              .." 'str' - string expected got '%s'."):format(type(str)), 2)
          end
          local strlen = #str
        
          local i = 1
          local a = 1
          local b = 0
          while i <= strlen - 15 do
            local x1, x2, x3, x4, x5, x6, x7, x8,
              x9, x10, x11, x12, x13, x14, x15, x16 = string_byte(str, i, i+15)
            b = (b+16*a+16*x1+15*x2+14*x3+13*x4+12*x5+11*x6+10*x7+9*x8+8*x9
              +7*x10+6*x11+5*x12+4*x13+3*x14+2*x15+x16)%65521
            a = (a+x1+x2+x3+x4+x5+x6+x7+x8+x9+x10+x11+x12+x13+x14+x15+x16)%65521
            i =  i + 16
          end
          while (i <= strlen) do
            local x = string_byte(str, i, i)
            a = (a + x) % 65521
            b = (b + a) % 65521
            i = i + 1
          end
          return (b*65536+a) % 4294967296
        end
        
        -- Compare adler32 checksum.
        -- adler32 should be compared with a mod to avoid sign problem
        -- 4072834167 (unsigned) is the same adler32 as -222133129
        local function IsEqualAdler32(actual, expected)
          return (actual % 4294967296) == (expected % 4294967296)
        end
        
        --- Create a preset dictionary.
        --
        -- This function is not fast, and the memory consumption of the produced
        -- dictionary is about 50 times of the input string. Therefore, it is suggestted
        -- to run this function only once in your program.
        --
        -- It is very important to know that if you do use a preset dictionary,
        -- compressors and decompressors MUST USE THE SAME dictionary. That is,
        -- dictionary must be created using the same string. If you update your program
        -- with a new dictionary, people with the old version won't be able to transmit
        -- data with people with the new version. Therefore, changing the dictionary
        -- must be very careful.
        --
        -- The parameters "strlen" and "adler32" add a layer of verification to ensure
        -- the parameter "str" is not modified unintentionally during the program
        -- development.
        --
        -- @usage local dict_str = "1234567890"
        --
        -- -- print(dict_str:len(), LibDeflate:Adler32(dict_str))
        -- -- Hardcode the print result below to verify it to avoid acciently
        -- -- modification of 'str' during the program development.
        -- -- string length: 10, Adler-32: 187433486,
        -- -- Don't calculate string length and its Adler-32 at run-time.
        --
        -- local dict = LibDeflate:CreateDictionary(dict_str, 10, 187433486)
        --
        -- @param str [string] The string used as the preset dictionary. <br>
        -- You should put stuffs that frequently appears in the dictionary
        -- string and preferablely put more frequently appeared stuffs toward the end
        -- of the string. <br>
        -- Empty string and string longer than 32768 bytes are not allowed.
        -- @param strlen [integer] The length of 'str'. Please pass in this parameter
        -- as a hardcoded constant, in order to verify the content of 'str'. The value
        -- of this parameter should be known before your program runs.
        -- @param adler32 [integer] The Adler-32 checksum of 'str'. Please pass in this
        -- parameter as a hardcoded constant, in order to verify the content of 'str'.
        -- The value of this parameter should be known before your program runs.
        -- @return  [table] The dictionary used for preset dictionary compression and
        -- decompression.
        -- @raise error if 'strlen' does not match the length of 'str',
        -- or if 'adler32' does not match the Adler-32 checksum of 'str'.
        function LibDeflate:CreateDictionary(str, strlen, adler32)
          if type(str) ~= "string" then
            error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
              .." 'str' - string expected got '%s'."):format(type(str)), 2)
          end
          if type(strlen) ~= "number" then
            error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
              .." 'strlen' - number expected got '%s'."):format(
              type(strlen)), 2)
          end
          if type(adler32) ~= "number" then
            error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
              .." 'adler32' - number expected got '%s'."):format(
              type(adler32)), 2)
          end
          if strlen ~= #str then
            error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
                .." 'strlen' does not match the actual length of 'str'."
                .." 'strlen': %u, '#str': %u ."
                .." Please check if 'str' is modified unintentionally.")
              :format(strlen, #str))
          end
          if strlen == 0 then
            error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
              .." 'str' - Empty string is not allowed."), 2)
          end
          if strlen > 32768 then
            error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
              .." 'str' - string longer than 32768 bytes is not allowed."
               .." Got %d bytes."):format(strlen), 2)
          end
          local actual_adler32 = self:Adler32(str)
          if not IsEqualAdler32(adler32, actual_adler32) then
            error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
                .." 'adler32' does not match the actual adler32 of 'str'."
                .." 'adler32': %u, 'Adler32(str)': %u ."
                .." Please check if 'str' is modified unintentionally.")
              :format(adler32, actual_adler32))
          end
        
          local dictionary = {}
          dictionary.adler32 = adler32
          dictionary.hash_tables = {}
          dictionary.string_table = {}
          dictionary.strlen = strlen
          local string_table = dictionary.string_table
          local hash_tables = dictionary.hash_tables
          string_table[1] = string_byte(str, 1, 1)
          string_table[2] = string_byte(str, 2, 2)
          if strlen >= 3 then
            local i = 1
            local hash = string_table[1]*256+string_table[2]
            while i <= strlen - 2 - 3 do
              local x1, x2, x3, x4 = string_byte(str, i+2, i+5)
              string_table[i+2] = x1
              string_table[i+3] = x2
              string_table[i+4] = x3
              string_table[i+5] = x4
              hash = (hash*256+x1)%16777216
              local t = hash_tables[hash]
              if not t then t = {}; hash_tables[hash] = t end
              t[#t+1] = i-strlen
              i = i + 1
              hash = (hash*256+x2)%16777216
              t = hash_tables[hash]
              if not t then t = {}; hash_tables[hash] = t end
              t[#t+1] = i-strlen
              i = i + 1
              hash = (hash*256+x3)%16777216
              t = hash_tables[hash]
              if not t then t = {}; hash_tables[hash] = t end
              t[#t+1] = i-strlen
              i = i + 1
              hash = (hash*256+x4)%16777216
              t = hash_tables[hash]
              if not t then t = {}; hash_tables[hash] = t end
              t[#t+1] = i-strlen
              i = i + 1
            end
            while i <= strlen - 2 do
              local x = string_byte(str, i+2)
              string_table[i+2] = x
              hash = (hash*256+x)%16777216
              local t = hash_tables[hash]
              if not t then t = {}; hash_tables[hash] = t end
              t[#t+1] = i-strlen
              i = i + 1
            end
          end
          return dictionary
        end
        
        -- Check if the dictionary is valid.
        -- @param dictionary The preset dictionary for compression and decompression.
        -- @return true if valid, false if not valid.
        -- @return if not valid, the error message.
        local function IsValidDictionary(dictionary)
          if type(dictionary) ~= "table" then
            return false, ("'dictionary' - table expected got '%s'.")
              :format(type(dictionary))
          end
          if type(dictionary.adler32) ~= "number"
            or type(dictionary.string_table) ~= "table"
            or type(dictionary.strlen) ~= "number"
            or dictionary.strlen <= 0
            or dictionary.strlen > 32768
            or dictionary.strlen ~= #dictionary.string_table
            or type(dictionary.hash_tables) ~= "table"
            then
            return false, ("'dictionary' - corrupted dictionary.")
              :format(type(dictionary))
          end
          return true, ""
        end
        
        --[[
          key of the configuration table is the compression level,
          and its value stores the compression setting.
          These numbers come from zlib source code.
          Higher compression level usually means better compression.
          (Because LibDeflate uses a simplified version of zlib algorithm,
          there is no guarantee that higher compression level does not create
          bigger file than lower level, but I can say it's 99% likely)
          Be careful with the high compression level. This is a pure lua
          implementation compressor/decompressor, which is significant slower than
          a C/C++ equivalant compressor/decompressor. Very high compression level
          costs significant more CPU time, and usually compression size won't be
          significant smaller when you increase compression level by 1, when the
          level is already very high. Benchmark yourself if you can afford it.
          See also https://github.com/madler/zlib/blob/master/doc/algorithm.txt,
          https://github.com/madler/zlib/blob/master/deflate.c for more information.
          The meaning of each field:
          @field 1 use_lazy_evaluation:
            true/false. Whether the program uses lazy evaluation.
            See what is "lazy evaluation" in the link above.
            lazy_evaluation improves ratio, but relatively slow.
          @field 2 good_prev_length:
            Only effective if lazy is set, Only use 1/4 of max_chain,
            if prev length of lazy match is above this.
          @field 3 max_insert_length/max_lazy_match:
            If not using lazy evaluation,
            insert new strings in the hash table only if the match length is not
            greater than this length.
            If using lazy evaluation, only continue lazy evaluation,
            if previous match length is strictly smaller than this value.
          @field 4 nice_length:
            Number. Don't continue to go down the hash chain,
            if match length is above this.
          @field 5 max_chain:
            Number. The maximum number of hash chains we look.
        --]]
        local _compression_level_configs = {
          [0] = {false, nil, 0, 0, 0}, -- level 0, no compression
          [1] = {false, nil, 4, 8, 4}, -- level 1, similar to zlib level 1
          [2] = {false, nil, 5, 18, 8}, -- level 2, similar to zlib level 2
          [3] = {false, nil, 6, 32, 32},	-- level 3, similar to zlib level 3
          [4] = {true, 4,	4, 16, 16},	-- level 4, similar to zlib level 4
          [5] = {true, 8,	16,	32,	32}, -- level 5, similar to zlib level 5
          [6] = {true, 8,	16,	128, 128}, -- level 6, similar to zlib level 6
          [7] = {true, 8,	32,	128, 256}, -- (SLOW) level 7, similar to zlib level 7
          [8] = {true, 32, 128, 258, 1024} , --(SLOW) level 8,similar to zlib level 8
          [9] = {true, 32, 258, 258, 4096},
            -- (VERY SLOW) level 9, similar to zlib level 9
        }
        
        -- Check if the compression/decompression arguments is valid
        -- @param str The input string.
        -- @param check_dictionary if true, check if dictionary is valid.
        -- @param dictionary The preset dictionary for compression and decompression.
        -- @param check_configs if true, check if config is valid.
        -- @param configs The compression configuration table
        -- @return true if valid, false if not valid.
        -- @return if not valid, the error message.
        local function IsValidArguments(str,
          check_dictionary, dictionary,
          check_configs, configs)
        
          if type(str) ~= "string" then
            return false,
              ("'str' - string expected got '%s'."):format(type(str))
          end
          if check_dictionary then
            local dict_valid, dict_err = IsValidDictionary(dictionary)
            if not dict_valid then
              return false, dict_err
            end
          end
          if check_configs then
            local type_configs = type(configs)
            if type_configs ~= "nil" and type_configs ~= "table" then
              return false,
              ("'configs' - nil or table expected got '%s'.")
                :format(type(configs))
            end
            if type_configs == "table" then
              for k, v in pairs(configs) do
                if k ~= "level" and k ~= "strategy" then
                  return false,
                  ("'configs' - unsupported table key in the configs: '%s'.")
                  :format(k)
                elseif k == "level" and not _compression_level_configs[v] then
                  return false,
                  ("'configs' - unsupported 'level': %s."):format(tostring(v))
                elseif k == "strategy" and v ~= "fixed" and v ~= "huffman_only"
                    and v ~= "dynamic" then
                    -- random_block_type is for testing purpose
                  return false, ("'configs' - unsupported 'strategy': '%s'.")
                    :format(tostring(v))
                end
              end
            end
          end
          return true, ""
        end
        
        
        
        --[[ --------------------------------------------------------------------------
          Compress code
        --]] --------------------------------------------------------------------------
        
        -- partial flush to save memory
        local _FLUSH_MODE_MEMORY_CLEANUP = 0
        -- full flush with partial bytes
        local _FLUSH_MODE_OUTPUT = 1
        -- write bytes to get to byte boundary
        local _FLUSH_MODE_BYTE_BOUNDARY = 2
        -- no flush, just get num of bits written so far
        local _FLUSH_MODE_NO_FLUSH = 3
        
        --[[
          Create an empty writer to easily write stuffs as the unit of bits.
          Return values:
          1. WriteBits(code, bitlen):
          2. WriteString(str):
          3. Flush(mode):
        --]]
        local function CreateWriter()
          local buffer_size = 0
          local cache = 0
          local cache_bitlen = 0
          local total_bitlen = 0
          local buffer = {}
          -- When buffer is big enough, flush into result_buffer to save memory.
          local result_buffer = {}
        
          -- Write bits with value "value" and bit length of "bitlen" into writer.
          -- @param value: The value being written
          -- @param bitlen: The bit length of "value"
          -- @return nil
          local function WriteBits(value, bitlen)
            cache = cache + value * _pow2[cache_bitlen]
            cache_bitlen = cache_bitlen + bitlen
            total_bitlen = total_bitlen + bitlen
            -- Only bulk to buffer every 4 bytes. This is quicker.
            if cache_bitlen >= 32 then
              buffer_size = buffer_size + 1
              buffer[buffer_size] =
                _byte_to_char[cache % 256]
                .._byte_to_char[((cache-cache%256)/256 % 256)]
                .._byte_to_char[((cache-cache%65536)/65536 % 256)]
                .._byte_to_char[((cache-cache%16777216)/16777216 % 256)]
              local rshift_mask = _pow2[32 - cache_bitlen + bitlen]
              cache = (value - value%rshift_mask)/rshift_mask
              cache_bitlen = cache_bitlen - 32
            end
          end
        
          -- Write the entire string into the writer.
          -- @param str The string being written
          -- @return nil
          local function WriteString(str)
            for _ = 1, cache_bitlen, 8 do
              buffer_size = buffer_size + 1
              buffer[buffer_size] = string_char(cache % 256)
              cache = (cache-cache%256)/256
            end
            cache_bitlen = 0
            buffer_size = buffer_size + 1
            buffer[buffer_size] = str
            total_bitlen = total_bitlen + #str*8
          end
        
          -- Flush current stuffs in the writer and return it.
          -- This operation will free most of the memory.
          -- @param mode See the descrtion of the constant and the source code.
          -- @return The total number of bits stored in the writer right now.
          -- for byte boundary mode, it includes the padding bits.
          -- for output mode, it does not include padding bits.
          -- @return Return the outputs if mode is output.
          local function FlushWriter(mode)
            if mode == _FLUSH_MODE_NO_FLUSH then
              return total_bitlen
            end
        
            if mode == _FLUSH_MODE_OUTPUT
              or mode == _FLUSH_MODE_BYTE_BOUNDARY then
              -- Full flush, also output cache.
              -- Need to pad some bits if cache_bitlen is not multiple of 8.
              local padding_bitlen = (8 - cache_bitlen % 8) % 8
        
              if cache_bitlen > 0 then
                -- padding with all 1 bits, mainly because "\000" is not
                -- good to be tranmitted. I do this so "\000" is a little bit
                -- less frequent.
                cache = cache - _pow2[cache_bitlen]
                  + _pow2[cache_bitlen+padding_bitlen]
                for _ = 1, cache_bitlen, 8 do
                  buffer_size = buffer_size + 1
                  buffer[buffer_size] = _byte_to_char[cache % 256]
                  cache = (cache-cache%256)/256
                end
        
                cache = 0
                cache_bitlen = 0
              end
              if mode == _FLUSH_MODE_BYTE_BOUNDARY then
                total_bitlen = total_bitlen + padding_bitlen
                return total_bitlen
              end
            end
        
            local flushed = table_concat(buffer)
            buffer = {}
            buffer_size = 0
            result_buffer[#result_buffer+1] = flushed
        
            if mode == _FLUSH_MODE_MEMORY_CLEANUP then
              return total_bitlen
            else
              return total_bitlen, table_concat(result_buffer)
            end
          end
        
          return WriteBits, WriteString, FlushWriter
        end
        
        -- Push an element into a max heap
        -- @param heap A max heap whose max element is at index 1.
        -- @param e The element to be pushed. Assume element "e" is a table
        --  and comparison is done via its first entry e[1]
        -- @param heap_size current number of elements in the heap.
        --  NOTE: There may be some garbage stored in
        --  heap[heap_size+1], heap[heap_size+2], etc..
        -- @return nil
        local function MinHeapPush(heap, e, heap_size)
          heap_size = heap_size + 1
          heap[heap_size] = e
          local value = e[1]
          local pos = heap_size
          local parent_pos = (pos-pos%2)/2
        
          while (parent_pos >= 1 and heap[parent_pos][1] > value) do
            local t = heap[parent_pos]
            heap[parent_pos] = e
            heap[pos] = t
            pos = parent_pos
            parent_pos = (parent_pos-parent_pos%2)/2
          end
        end
        
        -- Pop an element from a max heap
        -- @param heap A max heap whose max element is at index 1.
        -- @param heap_size current number of elements in the heap.
        -- @return the poped element
        -- Note: This function does not change table size of "heap" to save CPU time.
        local function MinHeapPop(heap, heap_size)
          local top = heap[1]
          local e = heap[heap_size]
          local value = e[1]
          heap[1] = e
          heap[heap_size] = top
          heap_size = heap_size - 1
        
          local pos = 1
          local left_child_pos = pos * 2
          local right_child_pos = left_child_pos + 1
        
          while (left_child_pos <= heap_size) do
            local left_child = heap[left_child_pos]
            if (right_child_pos <= heap_size
              and heap[right_child_pos][1] < left_child[1]) then
              local right_child = heap[right_child_pos]
              if right_child[1] < value then
                heap[right_child_pos] = e
                heap[pos] = right_child
                pos = right_child_pos
                left_child_pos = pos * 2
                right_child_pos = left_child_pos + 1
              else
                break
              end
            else
              if left_child[1] < value then
                heap[left_child_pos] = e
                heap[pos] = left_child
                pos = left_child_pos
                left_child_pos = pos * 2
                right_child_pos = left_child_pos + 1
              else
                break
              end
            end
          end
        
          return top
        end
        
        -- Deflate defines a special huffman tree, which is unique once the bit length
        -- of huffman code of all symbols are known.
        -- @param bitlen_count Number of symbols with a specific bitlen
        -- @param symbol_bitlen The bit length of a symbol
        -- @param max_symbol The max symbol among all symbols,
        --		which is (number of symbols - 1)
        -- @param max_bitlen The max huffman bit length among all symbols.
        -- @return The huffman code of all symbols.
        local function GetHuffmanCodeFromBitlen(bitlen_counts, symbol_bitlens
            , max_symbol, max_bitlen)
          local huffman_code = 0
          local next_codes = {}
          local symbol_huffman_codes = {}
          for bitlen = 1, max_bitlen do
            huffman_code = (huffman_code+(bitlen_counts[bitlen-1] or 0))*2
            next_codes[bitlen] = huffman_code
          end
          for symbol = 0, max_symbol do
            local bitlen = symbol_bitlens[symbol]
            if bitlen then
              huffman_code = next_codes[bitlen]
              next_codes[bitlen] = huffman_code + 1
        
              -- Reverse the bits of huffman code,
              -- because most signifant bits of huffman code
              -- is stored first into the compressed data.
              -- @see RFC1951 Page5 Section 3.1.1
              if bitlen <= 9 then -- Have cached reverse for small bitlen.
                symbol_huffman_codes[symbol] =
                  _reverse_bits_tbl[bitlen][huffman_code]
              else
                local reverse = 0
                for _ = 1, bitlen do
                  reverse = reverse - reverse%2
                    + (((reverse%2==1)
                      or (huffman_code % 2) == 1) and 1 or 0)
                  huffman_code = (huffman_code-huffman_code%2)/2
                  reverse = reverse*2
                end
                symbol_huffman_codes[symbol] = (reverse-reverse%2)/2
              end
            end
          end
          return symbol_huffman_codes
        end
        
        -- A helper function to sort heap elements
        -- a[1], b[1] is the huffman frequency
        -- a[2], b[2] is the symbol value.
        local function SortByFirstThenSecond(a, b)
          return a[1] < b[1] or
            (a[1] == b[1] and a[2] < b[2])
        end
        
        -- Calculate the huffman bit length and huffman code.
        -- @param symbol_count: A table whose table key is the symbol, and table value
        --		is the symbol frenquency (nil means 0 frequency).
        -- @param max_bitlen: See description of return value.
        -- @param max_symbol: The maximum symbol
        -- @return a table whose key is the symbol, and the value is the huffman bit
        --		bit length. We guarantee that all bit length <= max_bitlen.
        --		For 0<=symbol<=max_symbol, table value could be nil if the frequency
        --		of the symbol is 0 or nil.
        -- @return a table whose key is the symbol, and the value is the huffman code.
        -- @return a number indicating the maximum symbol whose bitlen is not 0.
        local function GetHuffmanBitlenAndCode(symbol_counts, max_bitlen, max_symbol)
          local heap_size
          local max_non_zero_bitlen_symbol = -1
          local leafs = {}
          local heap = {}
          local symbol_bitlens = {}
          local symbol_codes = {}
          local bitlen_counts = {}
        
          --[[
            tree[1]: weight, temporarily used as parent and bitLengths
            tree[2]: symbol
            tree[3]: left child
            tree[4]: right child
          --]]
          local number_unique_symbols = 0
          for symbol, count in pairs(symbol_counts) do
            number_unique_symbols = number_unique_symbols + 1
            leafs[number_unique_symbols] = {count, symbol}
          end
        
          if (number_unique_symbols == 0) then
            -- no code.
            return {}, {}, -1
          elseif (number_unique_symbols == 1) then
            -- Only one code. In this case, its huffman code
            -- needs to be assigned as 0, and bit length is 1.
            -- This is the only case that the return result
            -- represents an imcomplete huffman tree.
            local symbol = leafs[1][2]
            symbol_bitlens[symbol] = 1
            symbol_codes[symbol] = 0
            return symbol_bitlens, symbol_codes, symbol
          else
            table_sort(leafs, SortByFirstThenSecond)
            heap_size = number_unique_symbols
            for i = 1, heap_size do
              heap[i] = leafs[i]
            end
        
            while (heap_size > 1) do
              -- Note: pop does not change table size of heap
              local leftChild = MinHeapPop(heap, heap_size)
              heap_size = heap_size - 1
              local rightChild = MinHeapPop(heap, heap_size)
              heap_size = heap_size - 1
              local newNode =
                {leftChild[1]+rightChild[1], -1, leftChild, rightChild}
              MinHeapPush(heap, newNode, heap_size)
              heap_size = heap_size + 1
            end
        
            -- Number of leafs whose bit length is greater than max_len.
            local number_bitlen_overflow = 0
        
            -- Calculate bit length of all nodes
            local fifo = {heap[1], 0, 0, 0} -- preallocate some spaces.
            local fifo_size = 1
            local index = 1
            heap[1][1] = 0
            while (index <= fifo_size) do -- Breath first search
              local e = fifo[index]
              local bitlen = e[1]
              local symbol = e[2]
              local left_child = e[3]
              local right_child = e[4]
              if left_child then
                fifo_size = fifo_size + 1
                fifo[fifo_size] = left_child
                left_child[1] = bitlen + 1
              end
              if right_child then
                fifo_size = fifo_size + 1
                fifo[fifo_size] = right_child
                right_child[1] = bitlen + 1
              end
              index = index + 1
        
              if (bitlen > max_bitlen) then
                number_bitlen_overflow = number_bitlen_overflow + 1
                bitlen = max_bitlen
              end
              if symbol >= 0 then
                symbol_bitlens[symbol] = bitlen
                max_non_zero_bitlen_symbol =
                  (symbol > max_non_zero_bitlen_symbol)
                  and symbol or max_non_zero_bitlen_symbol
                bitlen_counts[bitlen] = (bitlen_counts[bitlen] or 0) + 1
              end
            end
        
            -- Resolve bit length overflow
            -- @see ZLib/trees.c:gen_bitlen(s, desc), for reference
            if (number_bitlen_overflow > 0) then
              repeat
                local bitlen = max_bitlen - 1
                while ((bitlen_counts[bitlen] or 0) == 0) do
                  bitlen = bitlen - 1
                end
                -- move one leaf down the tree
                bitlen_counts[bitlen] = bitlen_counts[bitlen] - 1
                -- move one overflow item as its brother
                bitlen_counts[bitlen+1] = (bitlen_counts[bitlen+1] or 0) + 2
                bitlen_counts[max_bitlen] = bitlen_counts[max_bitlen] - 1
                number_bitlen_overflow = number_bitlen_overflow - 2
              until (number_bitlen_overflow <= 0)
        
              index = 1
              for bitlen = max_bitlen, 1, -1 do
                local n = bitlen_counts[bitlen] or 0
                while (n > 0) do
                  local symbol = leafs[index][2]
                  symbol_bitlens[symbol] = bitlen
                  n = n - 1
                  index = index + 1
                end
              end
            end
        
            symbol_codes = GetHuffmanCodeFromBitlen(bitlen_counts, symbol_bitlens,
                max_symbol, max_bitlen)
            return symbol_bitlens, symbol_codes, max_non_zero_bitlen_symbol
          end
        end
        
        -- Calculate the first huffman header in the dynamic huffman block
        -- @see RFC1951 Page 12
        -- @param lcode_bitlen: The huffman bit length of literal/LZ77_length.
        -- @param max_non_zero_bitlen_lcode: The maximum literal/LZ77_length symbol
        --		whose huffman bit length is not zero.
        -- @param dcode_bitlen: The huffman bit length of LZ77 distance.
        -- @param max_non_zero_bitlen_dcode: The maximum LZ77 distance symbol
        --		whose huffman bit length is not zero.
        -- @return The run length encoded codes.
        -- @return The extra bits. One entry for each rle code that needs extra bits.
        --		(code == 16 or 17 or 18).
        -- @return The count of appearance of each rle codes.
        local function RunLengthEncodeHuffmanBitlen(
            lcode_bitlens,
            max_non_zero_bitlen_lcode,
            dcode_bitlens,
            max_non_zero_bitlen_dcode)
          local rle_code_tblsize = 0
          local rle_codes = {}
          local rle_code_counts = {}
          local rle_extra_bits_tblsize = 0
          local rle_extra_bits = {}
          local prev = nil
          local count = 0
        
          -- If there is no distance code, assume one distance code of bit length 0.
          -- RFC1951: One distance code of zero bits means that
          -- there are no distance codes used at all (the data is all literals).
          max_non_zero_bitlen_dcode = (max_non_zero_bitlen_dcode < 0)
              and 0 or max_non_zero_bitlen_dcode
          local max_code = max_non_zero_bitlen_lcode+max_non_zero_bitlen_dcode+1
        
          for code = 0, max_code+1 do
            local len = (code <= max_non_zero_bitlen_lcode)
              and (lcode_bitlens[code] or 0)
              or ((code <= max_code)
              and (dcode_bitlens[code-max_non_zero_bitlen_lcode-1] or 0) or nil)
            if len == prev then
              count = count + 1
              if len ~= 0 and count == 6 then
                rle_code_tblsize = rle_code_tblsize + 1
                rle_codes[rle_code_tblsize] = 16
                rle_extra_bits_tblsize = rle_extra_bits_tblsize + 1
                rle_extra_bits[rle_extra_bits_tblsize] = 3
                rle_code_counts[16] = (rle_code_counts[16] or 0) + 1
                count = 0
              elseif len == 0 and count == 138 then
                rle_code_tblsize = rle_code_tblsize + 1
                rle_codes[rle_code_tblsize] = 18
                rle_extra_bits_tblsize = rle_extra_bits_tblsize + 1
                rle_extra_bits[rle_extra_bits_tblsize] = 127
                rle_code_counts[18] = (rle_code_counts[18] or 0) + 1
                count = 0
              end
            else
              if count == 1 then
                rle_code_tblsize = rle_code_tblsize + 1
                rle_codes[rle_code_tblsize] = prev
                rle_code_counts[prev] = (rle_code_counts[prev] or 0) + 1
              elseif count == 2 then
                rle_code_tblsize = rle_code_tblsize + 1
                rle_codes[rle_code_tblsize] = prev
                rle_code_tblsize = rle_code_tblsize + 1
                rle_codes[rle_code_tblsize] = prev
                rle_code_counts[prev] = (rle_code_counts[prev] or 0) + 2
              elseif count >= 3 then
                rle_code_tblsize = rle_code_tblsize + 1
                local rleCode = (prev ~= 0) and 16 or (count <= 10 and 17 or 18)
                rle_codes[rle_code_tblsize] = rleCode
                rle_code_counts[rleCode] = (rle_code_counts[rleCode] or 0) + 1
                rle_extra_bits_tblsize = rle_extra_bits_tblsize + 1
                rle_extra_bits[rle_extra_bits_tblsize] =
                  (count <= 10) and (count - 3) or (count - 11)
              end
        
              prev = len
              if len and len ~= 0 then
                rle_code_tblsize = rle_code_tblsize + 1
                rle_codes[rle_code_tblsize] = len
                rle_code_counts[len] = (rle_code_counts[len] or 0) + 1
                count = 0
              else
                count = 1
              end
            end
          end
        
          return rle_codes, rle_extra_bits, rle_code_counts
        end
        
        -- Load the string into a table, in order to speed up LZ77.
        -- Loop unrolled 16 times to speed this function up.
        -- @param str The string to be loaded.
        -- @param t The load destination
        -- @param start str[index] will be the first character to be loaded.
        -- @param end str[index] will be the last character to be loaded
        -- @param offset str[index] will be loaded into t[index-offset]
        -- @return t
        local function LoadStringToTable(str, t, start, stop, offset)
          local i = start - offset
          while i <= stop - 15 - offset do
            t[i], t[i+1], t[i+2], t[i+3], t[i+4], t[i+5], t[i+6], t[i+7], t[i+8],
            t[i+9], t[i+10], t[i+11], t[i+12], t[i+13], t[i+14], t[i+15] =
              string_byte(str, i + offset, i + 15 + offset)
            i = i + 16
          end
          while (i <= stop - offset) do
            t[i] = string_byte(str, i + offset, i + offset)
            i = i + 1
          end
          return t
        end
        
        -- Do LZ77 process. This function uses the majority of the CPU time.
        -- @see zlib/deflate.c:deflate_fast(), zlib/deflate.c:deflate_slow()
        -- @see https://github.com/madler/zlib/blob/master/doc/algorithm.txt
        -- This function uses the algorithms used above. You should read the
        -- algorithm.txt above to understand what is the hash function and the
        -- lazy evaluation.
        --
        -- The special optimization used here is hash functions used here.
        -- The hash function is just the multiplication of the three consective
        -- characters. So if the hash matches, it guarantees 3 characters are matched.
        -- This optimization can be implemented because Lua table is a hash table.
        --
        -- @param level integer that describes compression level.
        -- @param string_table table that stores the value of string to be compressed.
        --			The index of this table starts from 1.
        --			The caller needs to make sure all values needed by this function
        --			are loaded.
        --			Assume "str" is the origin input string into the compressor
        --			str[block_start]..str[block_end+3] needs to be loaded into
        --			string_table[block_start-offset]..string_table[block_end-offset]
        --			If dictionary is presented, the last 258 bytes of the dictionary
        --			needs to be loaded into sing_table[-257..0]
        --			(See more in the description of offset.)
        -- @param hash_tables. The table key is the hash value (0<=hash<=16777216=256^3)
        --			The table value is an array0 that stores the indexes of the
        --			input data string to be compressed, such that
        --			hash == str[index]*str[index+1]*str[index+2]
        --			Indexes are ordered in this array.
        -- @param block_start The indexes of the input data string to be compressed.
        --				that starts the LZ77 block.
        -- @param block_end The indexes of the input data string to be compressed.
        --				that stores the LZ77 block.
        -- @param offset str[index] is stored in string_table[index-offset],
        --			This offset is mainly an optimization to limit the index
        --			of string_table, so lua can access this table quicker.
        -- @param dictionary See LibDeflate:CreateDictionary
        -- @return literal/LZ77_length deflate codes.
        -- @return the extra bits of literal/LZ77_length deflate codes.
        -- @return the count of each literal/LZ77 deflate code.
        -- @return LZ77 distance deflate codes.
        -- @return the extra bits of LZ77 distance deflate codes.
        -- @return the count of each LZ77 distance deflate code.
        local function GetBlockLZ77Result(level, string_table, hash_tables, block_start,
            block_end, offset, dictionary)
          local config = _compression_level_configs[level]
          local config_use_lazy
            , config_good_prev_length
            , config_max_lazy_match
            , config_nice_length
            , config_max_hash_chain =
              config[1], config[2], config[3], config[4], config[5]
        
          local config_max_insert_length = (not config_use_lazy)
            and config_max_lazy_match or 2147483646
          local config_good_hash_chain =
            (config_max_hash_chain-config_max_hash_chain%4/4)
        
          local hash
        
          local dict_hash_tables
          local dict_string_table
          local dict_string_len = 0
        
          if dictionary then
            dict_hash_tables = dictionary.hash_tables
            dict_string_table = dictionary.string_table
            dict_string_len = dictionary.strlen
            assert(block_start == 1)
            if block_end >= block_start and dict_string_len >= 2 then
              hash = dict_string_table[dict_string_len-1]*65536
                + dict_string_table[dict_string_len]*256 + string_table[1]
              local t = hash_tables[hash]
              if not t then t = {}; hash_tables[hash] = t end
              t[#t+1] = -1
            end
            if block_end >= block_start+1 and dict_string_len >= 1 then
              hash = dict_string_table[dict_string_len]*65536
                + string_table[1]*256 + string_table[2]
              local t = hash_tables[hash]
              if not t then t = {}; hash_tables[hash] = t end
              t[#t+1] = 0
            end
          end
        
          hash = (string_table[block_start-offset] or 0)*256
            + (string_table[block_start+1-offset] or 0)
        
          local lcodes = {}
          local lcode_tblsize = 0
          local lcodes_counts = {}
          local dcodes = {}
          local dcodes_tblsize = 0
          local dcodes_counts = {}
        
          local lextra_bits = {}
          local lextra_bits_tblsize = 0
          local dextra_bits = {}
          local dextra_bits_tblsize = 0
        
          local match_available = false
          local prev_len
          local prev_dist
          local cur_len = 0
          local cur_dist = 0
        
          local index = block_start
          local index_end = block_end + (config_use_lazy and 1 or 0)
        
          -- the zlib source code writes separate code for lazy evaluation and
          -- not lazy evaluation, which is easier to understand.
          -- I put them together, so it is a bit harder to understand.
          -- because I think this is easier for me to maintain it.
          while (index <= index_end) do
            local string_table_index = index - offset
            prev_len = cur_len
            prev_dist = cur_dist
            cur_len = 0
        
            hash = (hash*256+(string_table[string_table_index+2] or 0))%16777216
        
            local chain_index
            local cur_chain
            local hash_chain = hash_tables[hash]
            local chain_old_size
            if not hash_chain then
              chain_old_size = 0
              hash_chain = {}
              hash_tables[hash] = hash_chain
              if dict_hash_tables then
                cur_chain = dict_hash_tables[hash]
                chain_index = cur_chain and #cur_chain or 0
              else
                chain_index = 0
              end
            else
              chain_old_size = #hash_chain
              cur_chain = hash_chain
              chain_index = chain_old_size
            end
        
            if index <= block_end then
              hash_chain[chain_old_size+1] = index
            end
        
            if (chain_index > 0 and index + 2 <= block_end
              and (not config_use_lazy or prev_len < config_max_lazy_match)) then
        
              local depth =
                (config_use_lazy and prev_len >= config_good_prev_length)
                and config_good_hash_chain or config_max_hash_chain
        
              while chain_index >= 1 and depth > 0 do
                local prev = cur_chain[chain_index]
        
                if index - prev > 32768 then
                  break
                end
                if prev < index then
                  local j = 3
        
                  if prev >= -257 then
                    local prev_table_index = prev-offset
                    -- NOTE for author:
                    -- j < 258 and index + j <= block_end
                    -- This is the right condition
                    while (j < 258 and index + j <= block_end) do
                      if (string_table[prev_table_index+j]
                        == string_table[string_table_index+j]) then
                        j = j + 1
                      else
                        break
                      end
                    end
                  else
                    local prev_table_index = dict_string_len+prev
                    -- NOTE for author:
                    -- j < 258 and index + j <= block_end
                    -- This is the right condition
                    while (j < 258 and index + j <= block_end) do
                      if (dict_string_table[prev_table_index+j]
                        == string_table[string_table_index+j]) then
                        j = j + 1
                      else
                        break
                      end
                    end
                  end
                  if j > cur_len then
                    cur_len = j
                    cur_dist = index - prev
                  end
                  if cur_len >= config_nice_length then
                    break
                  end
                end
        
                chain_index = chain_index - 1
                depth = depth - 1
                if chain_index == 0 and prev > 0 and dict_hash_tables then
                  cur_chain = dict_hash_tables[hash]
                  chain_index = cur_chain and #cur_chain or 0
                end
              end
            end
        
            if not config_use_lazy then
              prev_len, prev_dist = cur_len, cur_dist
            end
            if ((not config_use_lazy or match_available)
              and (prev_len > 3 or (prev_len == 3 and prev_dist < 4096))
              and cur_len <= prev_len )then
              local code = _length_to_deflate_code[prev_len]
              local length_extra_bits_bitlen =
                _length_to_deflate_extra_bitlen[prev_len]
              local dist_code, dist_extra_bits_bitlen, dist_extra_bits
              if prev_dist <= 256 then -- have cached code for small distance.
                dist_code = _dist256_to_deflate_code[prev_dist]
                dist_extra_bits = _dist256_to_deflate_extra_bits[prev_dist]
                dist_extra_bits_bitlen =
                  _dist256_to_deflate_extra_bitlen[prev_dist]
              else
                dist_code = 16
                dist_extra_bits_bitlen = 7
                local a = 384
                local b = 512
        
                while true do
                  if prev_dist <= a then
                    dist_extra_bits = (prev_dist-(b/2)-1) % (b/4)
                    break
                  elseif prev_dist <= b then
                    dist_extra_bits = (prev_dist-(b/2)-1) % (b/4)
                    dist_code = dist_code + 1
                    break
                  else
                    dist_code = dist_code + 2
                    dist_extra_bits_bitlen = dist_extra_bits_bitlen + 1
                    a = a*2
                    b = b*2
                  end
                end
              end
              lcode_tblsize = lcode_tblsize + 1
              lcodes[lcode_tblsize] = code
              lcodes_counts[code] = (lcodes_counts[code] or 0) + 1
        
              dcodes_tblsize = dcodes_tblsize + 1
              dcodes[dcodes_tblsize] = dist_code
              dcodes_counts[dist_code] = (dcodes_counts[dist_code] or 0) + 1
        
              if length_extra_bits_bitlen > 0 then
                local lenExtraBits = _length_to_deflate_extra_bits[prev_len]
                lextra_bits_tblsize = lextra_bits_tblsize + 1
                lextra_bits[lextra_bits_tblsize] = lenExtraBits
              end
              if dist_extra_bits_bitlen > 0 then
                dextra_bits_tblsize = dextra_bits_tblsize + 1
                dextra_bits[dextra_bits_tblsize] = dist_extra_bits
              end
        
              for i=index+1, index+prev_len-(config_use_lazy and 2 or 1) do
                hash = (hash*256+(string_table[i-offset+2] or 0))%16777216
                if prev_len <= config_max_insert_length then
                  hash_chain = hash_tables[hash]
                  if not hash_chain then
                    hash_chain = {}
                    hash_tables[hash] = hash_chain
                  end
                  hash_chain[#hash_chain+1] = i
                end
              end
              index = index + prev_len - (config_use_lazy and 1 or 0)
              match_available = false
            elseif (not config_use_lazy) or match_available then
              local code = string_table[config_use_lazy
                and (string_table_index-1) or string_table_index]
              lcode_tblsize = lcode_tblsize + 1
              lcodes[lcode_tblsize] = code
              lcodes_counts[code] = (lcodes_counts[code] or 0) + 1
              index = index + 1
            else
              match_available = true
              index = index + 1
            end
          end
        
          -- Write "end of block" symbol
          lcode_tblsize = lcode_tblsize + 1
          lcodes[lcode_tblsize] = 256
          lcodes_counts[256] = (lcodes_counts[256] or 0) + 1
        
          return lcodes, lextra_bits, lcodes_counts, dcodes, dextra_bits
            , dcodes_counts
        end
        
        -- Get the header data of dynamic block.
        -- @param lcodes_count The count of each literal/LZ77_length codes.
        -- @param dcodes_count The count of each Lz77 distance codes.
        -- @return a lots of stuffs.
        -- @see RFC1951 Page 12
        local function GetBlockDynamicHuffmanHeader(lcodes_counts, dcodes_counts)
          local lcodes_huffman_bitlens, lcodes_huffman_codes
            , max_non_zero_bitlen_lcode =
            GetHuffmanBitlenAndCode(lcodes_counts, 15, 285)
          local dcodes_huffman_bitlens, dcodes_huffman_codes
            , max_non_zero_bitlen_dcode =
            GetHuffmanBitlenAndCode(dcodes_counts, 15, 29)
        
          local rle_deflate_codes, rle_extra_bits, rle_codes_counts =
            RunLengthEncodeHuffmanBitlen(lcodes_huffman_bitlens
            ,max_non_zero_bitlen_lcode, dcodes_huffman_bitlens
            , max_non_zero_bitlen_dcode)
        
          local rle_codes_huffman_bitlens, rle_codes_huffman_codes =
            GetHuffmanBitlenAndCode(rle_codes_counts, 7, 18)
        
          local HCLEN = 0
          for i = 1, 19 do
            local symbol = _rle_codes_huffman_bitlen_order[i]
            local length = rle_codes_huffman_bitlens[symbol] or 0
            if length ~= 0 then
              HCLEN = i
            end
          end
        
          HCLEN = HCLEN - 4
          local HLIT = max_non_zero_bitlen_lcode + 1 - 257
          local HDIST = max_non_zero_bitlen_dcode + 1 - 1
          if HDIST < 0 then HDIST = 0 end
        
          return HLIT, HDIST, HCLEN, rle_codes_huffman_bitlens
            , rle_codes_huffman_codes, rle_deflate_codes, rle_extra_bits
            , lcodes_huffman_bitlens, lcodes_huffman_codes
            , dcodes_huffman_bitlens, dcodes_huffman_codes
        end
        
        -- Get the size of dynamic block without writing any bits into the writer.
        -- @param ... Read the source code of GetBlockDynamicHuffmanHeader()
        -- @return the bit length of the dynamic block
        local function GetDynamicHuffmanBlockSize(lcodes, dcodes, HCLEN
          , rle_codes_huffman_bitlens, rle_deflate_codes
          , lcodes_huffman_bitlens, dcodes_huffman_bitlens)
        
          local block_bitlen = 17 -- 1+2+5+5+4
          block_bitlen = block_bitlen + (HCLEN+4)*3
        
          for i = 1, #rle_deflate_codes do
            local code = rle_deflate_codes[i]
            block_bitlen = block_bitlen + rle_codes_huffman_bitlens[code]
            if code >= 16 then
              block_bitlen = block_bitlen +
              ((code == 16) and 2 or (code == 17 and 3 or 7))
            end
          end
        
          local length_code_count = 0
          for i = 1, #lcodes do
            local code = lcodes[i]
            local huffman_bitlen = lcodes_huffman_bitlens[code]
            block_bitlen = block_bitlen + huffman_bitlen
            if code > 256 then -- Length code
              length_code_count = length_code_count + 1
              if code > 264 and code < 285 then -- Length code with extra bits
                local extra_bits_bitlen =
                  _literal_deflate_code_to_extra_bitlen[code-256]
                block_bitlen = block_bitlen + extra_bits_bitlen
              end
              local dist_code = dcodes[length_code_count]
              local dist_huffman_bitlen = dcodes_huffman_bitlens[dist_code]
              block_bitlen = block_bitlen + dist_huffman_bitlen
        
              if dist_code > 3 then -- dist code with extra bits
                local dist_extra_bits_bitlen = (dist_code-dist_code%2)/2 - 1
                block_bitlen = block_bitlen + dist_extra_bits_bitlen
              end
            end
          end
          return block_bitlen
        end
        
        -- Write dynamic block.
        -- @param ... Read the source code of GetBlockDynamicHuffmanHeader()
        local function CompressDynamicHuffmanBlock(WriteBits, is_last_block
            , lcodes, lextra_bits, dcodes, dextra_bits, HLIT, HDIST, HCLEN
            , rle_codes_huffman_bitlens, rle_codes_huffman_codes
            , rle_deflate_codes, rle_extra_bits
            , lcodes_huffman_bitlens, lcodes_huffman_codes
            , dcodes_huffman_bitlens, dcodes_huffman_codes)
        
          WriteBits(is_last_block and 1 or 0, 1) -- Last block identifier
          WriteBits(2, 2) -- Dynamic Huffman block identifier
        
          WriteBits(HLIT, 5)
          WriteBits(HDIST, 5)
          WriteBits(HCLEN, 4)
        
          for i = 1, HCLEN+4 do
            local symbol = _rle_codes_huffman_bitlen_order[i]
            local length = rle_codes_huffman_bitlens[symbol] or 0
            WriteBits(length, 3)
          end
        
          local rleExtraBitsIndex = 1
          for i=1, #rle_deflate_codes do
            local code = rle_deflate_codes[i]
            WriteBits(rle_codes_huffman_codes[code]
              , rle_codes_huffman_bitlens[code])
            if code >= 16 then
              local extraBits = rle_extra_bits[rleExtraBitsIndex]
              WriteBits(extraBits, (code == 16) and 2 or (code == 17 and 3 or 7))
              rleExtraBitsIndex = rleExtraBitsIndex + 1
            end
          end
        
          local length_code_count = 0
          local length_code_with_extra_count = 0
          local dist_code_with_extra_count = 0
        
          for i=1, #lcodes do
            local deflate_codee = lcodes[i]
            local huffman_code = lcodes_huffman_codes[deflate_codee]
            local huffman_bitlen = lcodes_huffman_bitlens[deflate_codee]
            WriteBits(huffman_code, huffman_bitlen)
            if deflate_codee > 256 then -- Length code
              length_code_count = length_code_count + 1
              if deflate_codee > 264 and deflate_codee < 285 then
                -- Length code with extra bits
                length_code_with_extra_count = length_code_with_extra_count + 1
                local extra_bits = lextra_bits[length_code_with_extra_count]
                local extra_bits_bitlen =
                  _literal_deflate_code_to_extra_bitlen[deflate_codee-256]
                WriteBits(extra_bits, extra_bits_bitlen)
              end
              -- Write distance code
              local dist_deflate_code = dcodes[length_code_count]
              local dist_huffman_code = dcodes_huffman_codes[dist_deflate_code]
              local dist_huffman_bitlen =
                dcodes_huffman_bitlens[dist_deflate_code]
              WriteBits(dist_huffman_code, dist_huffman_bitlen)
        
              if dist_deflate_code > 3 then -- dist code with extra bits
                dist_code_with_extra_count = dist_code_with_extra_count + 1
                local dist_extra_bits = dextra_bits[dist_code_with_extra_count]
                local dist_extra_bits_bitlen =
                  (dist_deflate_code-dist_deflate_code%2)/2 - 1
                WriteBits(dist_extra_bits, dist_extra_bits_bitlen)
              end
            end
          end
        end
        
        -- Get the size of fixed block without writing any bits into the writer.
        -- @param lcodes literal/LZ77_length deflate codes
        -- @param decodes LZ77 distance deflate codes
        -- @return the bit length of the fixed block
        local function GetFixedHuffmanBlockSize(lcodes, dcodes)
          local block_bitlen = 3
          local length_code_count = 0
          for i=1, #lcodes do
            local code = lcodes[i]
            local huffman_bitlen = _fix_block_literal_huffman_bitlen[code]
            block_bitlen = block_bitlen + huffman_bitlen
            if code > 256 then -- Length code
              length_code_count = length_code_count + 1
              if code > 264 and code < 285 then -- Length code with extra bits
                local extra_bits_bitlen =
                  _literal_deflate_code_to_extra_bitlen[code-256]
                block_bitlen = block_bitlen + extra_bits_bitlen
              end
              local dist_code = dcodes[length_code_count]
              block_bitlen = block_bitlen + 5
        
              if dist_code > 3 then -- dist code with extra bits
                local dist_extra_bits_bitlen =
                  (dist_code-dist_code%2)/2 - 1
                block_bitlen = block_bitlen + dist_extra_bits_bitlen
              end
            end
          end
          return block_bitlen
        end
        
        -- Write fixed block.
        -- @param lcodes literal/LZ77_length deflate codes
        -- @param decodes LZ77 distance deflate codes
        local function CompressFixedHuffmanBlock(WriteBits, is_last_block,
            lcodes, lextra_bits, dcodes, dextra_bits)
          WriteBits(is_last_block and 1 or 0, 1) -- Last block identifier
          WriteBits(1, 2) -- Fixed Huffman block identifier
          local length_code_count = 0
          local length_code_with_extra_count = 0
          local dist_code_with_extra_count = 0
          for i=1, #lcodes do
            local deflate_code = lcodes[i]
            local huffman_code = _fix_block_literal_huffman_code[deflate_code]
            local huffman_bitlen = _fix_block_literal_huffman_bitlen[deflate_code]
            WriteBits(huffman_code, huffman_bitlen)
            if deflate_code > 256 then -- Length code
              length_code_count = length_code_count + 1
              if deflate_code > 264 and deflate_code < 285 then
                -- Length code with extra bits
                length_code_with_extra_count = length_code_with_extra_count + 1
                local extra_bits = lextra_bits[length_code_with_extra_count]
                local extra_bits_bitlen =
                  _literal_deflate_code_to_extra_bitlen[deflate_code-256]
                WriteBits(extra_bits, extra_bits_bitlen)
              end
              -- Write distance code
              local dist_code = dcodes[length_code_count]
              local dist_huffman_code = _fix_block_dist_huffman_code[dist_code]
              WriteBits(dist_huffman_code, 5)
        
              if dist_code > 3 then -- dist code with extra bits
                dist_code_with_extra_count = dist_code_with_extra_count + 1
                local dist_extra_bits = dextra_bits[dist_code_with_extra_count]
                local dist_extra_bits_bitlen = (dist_code-dist_code%2)/2 - 1
                WriteBits(dist_extra_bits, dist_extra_bits_bitlen)
              end
            end
          end
        end
        
        -- Get the size of store block without writing any bits into the writer.
        -- @param block_start The start index of the origin input string
        -- @param block_end The end index of the origin input string
        -- @param Total bit lens had been written into the compressed result before,
        -- because store block needs to shift to byte boundary.
        -- @return the bit length of the fixed block
        local function GetStoreBlockSize(block_start, block_end, total_bitlen)
          assert(block_end-block_start+1 <= 65535)
          local block_bitlen = 3
          total_bitlen = total_bitlen + 3
          local padding_bitlen = (8-total_bitlen%8)%8
          block_bitlen = block_bitlen + padding_bitlen
          block_bitlen = block_bitlen + 32
          block_bitlen = block_bitlen + (block_end - block_start + 1) * 8
          return block_bitlen
        end
        
        -- Write the store block.
        -- @param ... lots of stuffs
        -- @return nil
        local function CompressStoreBlock(WriteBits, WriteString, is_last_block, str
          , block_start, block_end, total_bitlen)
          assert(block_end-block_start+1 <= 65535)
          WriteBits(is_last_block and 1 or 0, 1) -- Last block identifer.
          WriteBits(0, 2) -- Store block identifier.
          total_bitlen = total_bitlen + 3
          local padding_bitlen = (8-total_bitlen%8)%8
          if padding_bitlen > 0 then
            WriteBits(_pow2[padding_bitlen]-1, padding_bitlen)
          end
          local size = block_end - block_start + 1
          WriteBits(size, 16)
        
          -- Write size's one's complement
          local comp = (255 - size % 256) + (255 - (size-size%256)/256)*256
          WriteBits(comp, 16)
        
          WriteString(str:sub(block_start, block_end))
        end
        
        -- Do the deflate
        -- Currently using a simple way to determine the block size
        -- (This is why the compression ratio is little bit worse than zlib when
        -- the input size is very large
        -- The first block is 64KB, the following block is 32KB.
        -- After each block, there is a memory cleanup operation.
        -- This is not a fast operation, but it is needed to save memory usage, so
        -- the memory usage does not grow unboundly. If the data size is less than
        -- 64KB, then memory cleanup won't happen.
        -- This function determines whether to use store/fixed/dynamic blocks by
        -- calculating the block size of each block type and chooses the smallest one.
        local function Deflate(configs, WriteBits, WriteString, FlushWriter, str
          , dictionary)
          local string_table = {}
          local hash_tables = {}
          local is_last_block = nil
          local block_start
          local block_end
          local bitlen_written
          local total_bitlen = FlushWriter(_FLUSH_MODE_NO_FLUSH)
          local strlen = #str
          local offset
        
          local level
          local strategy
          if configs then
            if configs.level then
              level = configs.level
            end
            if configs.strategy then
              strategy = configs.strategy
            end
          end
        
          if not level then
            if strlen < 2048 then
              level = 7
            elseif strlen > 65536 then
              level = 3
            else
              level = 5
            end
          end
        
          while not is_last_block do
            if not block_start then
              block_start = 1
              block_end = 64*1024 - 1
              offset = 0
            else
              block_start = block_end + 1
              block_end = block_end + 32*1024
              offset = block_start - 32*1024 - 1
            end
        
            if block_end >= strlen then
              block_end = strlen
              is_last_block = true
            else
              is_last_block = false
            end
        
            local lcodes, lextra_bits, lcodes_counts, dcodes, dextra_bits
              , dcodes_counts
        
            local HLIT, HDIST, HCLEN, rle_codes_huffman_bitlens
              , rle_codes_huffman_codes, rle_deflate_codes
              , rle_extra_bits, lcodes_huffman_bitlens, lcodes_huffman_codes
              , dcodes_huffman_bitlens, dcodes_huffman_codes
        
            local dynamic_block_bitlen
            local fixed_block_bitlen
            local store_block_bitlen
        
            if level ~= 0 then
        
              -- GetBlockLZ77 needs block_start to block_end+3 to be loaded.
              LoadStringToTable(str, string_table, block_start, block_end + 3
                , offset)
              if block_start == 1 and dictionary then
                local dict_string_table = dictionary.string_table
                local dict_strlen = dictionary.strlen
                for i=0, (-dict_strlen+1)<-257
                  and -257 or (-dict_strlen+1), -1 do
                  string_table[i] = dict_string_table[dict_strlen+i]
                end
              end
        
              if strategy == "huffman_only" then
                lcodes = {}
                LoadStringToTable(str, lcodes, block_start, block_end
                  , block_start-1)
                lextra_bits = {}
                lcodes_counts = {}
                lcodes[block_end - block_start+2] = 256 -- end of block
                for i=1, block_end - block_start+2 do
                  local code = lcodes[i]
                  lcodes_counts[code] = (lcodes_counts[code] or 0) + 1
                end
                dcodes = {}
                dextra_bits = {}
                dcodes_counts = {}
              else
                lcodes, lextra_bits, lcodes_counts, dcodes, dextra_bits
                , dcodes_counts = GetBlockLZ77Result(level, string_table
                , hash_tables, block_start, block_end, offset, dictionary
                )
              end
        
              HLIT, HDIST, HCLEN, rle_codes_huffman_bitlens
                , rle_codes_huffman_codes, rle_deflate_codes
                , rle_extra_bits, lcodes_huffman_bitlens, lcodes_huffman_codes
                , dcodes_huffman_bitlens, dcodes_huffman_codes =
                GetBlockDynamicHuffmanHeader(lcodes_counts, dcodes_counts)
              dynamic_block_bitlen = GetDynamicHuffmanBlockSize(
                  lcodes, dcodes, HCLEN, rle_codes_huffman_bitlens
                  , rle_deflate_codes, lcodes_huffman_bitlens
                  , dcodes_huffman_bitlens)
              fixed_block_bitlen = GetFixedHuffmanBlockSize(lcodes, dcodes)
            end
        
            store_block_bitlen = GetStoreBlockSize(block_start, block_end
              , total_bitlen)
        
            local min_bitlen = store_block_bitlen
            min_bitlen = (fixed_block_bitlen and fixed_block_bitlen < min_bitlen)
              and fixed_block_bitlen or min_bitlen
            min_bitlen = (dynamic_block_bitlen
              and dynamic_block_bitlen < min_bitlen)
              and dynamic_block_bitlen or min_bitlen
        
            if level == 0 or (strategy ~= "fixed" and strategy ~= "dynamic" and
              store_block_bitlen == min_bitlen) then
              CompressStoreBlock(WriteBits, WriteString, is_last_block
                , str, block_start, block_end, total_bitlen)
              total_bitlen = total_bitlen + store_block_bitlen
            elseif strategy ~= "dynamic" and (
              strategy == "fixed" or fixed_block_bitlen == min_bitlen) then
              CompressFixedHuffmanBlock(WriteBits, is_last_block,
                  lcodes, lextra_bits, dcodes, dextra_bits)
              total_bitlen = total_bitlen + fixed_block_bitlen
            elseif strategy == "dynamic" or dynamic_block_bitlen == min_bitlen then
              CompressDynamicHuffmanBlock(WriteBits, is_last_block, lcodes
                , lextra_bits, dcodes, dextra_bits, HLIT, HDIST, HCLEN
                , rle_codes_huffman_bitlens, rle_codes_huffman_codes
                , rle_deflate_codes, rle_extra_bits
                , lcodes_huffman_bitlens, lcodes_huffman_codes
                , dcodes_huffman_bitlens, dcodes_huffman_codes)
              total_bitlen = total_bitlen + dynamic_block_bitlen
            end
        
            if is_last_block then
              bitlen_written = FlushWriter(_FLUSH_MODE_NO_FLUSH)
            else
              bitlen_written = FlushWriter(_FLUSH_MODE_MEMORY_CLEANUP)
            end
        
            assert(bitlen_written == total_bitlen)
        
            -- Memory clean up, so memory consumption does not always grow linearly
            -- , even if input string is > 64K.
            -- Not a very efficient operation, but this operation won't happen
            -- when the input data size is less than 64K.
            if not is_last_block then
              local j
              if dictionary and block_start == 1 then
                j = 0
                while (string_table[j]) do
                  string_table[j] = nil
                  j = j - 1
                end
              end
              dictionary = nil
              j = 1
              for i = block_end-32767, block_end do
                string_table[j] = string_table[i-offset]
                j = j + 1
              end
        
              for k, t in pairs(hash_tables) do
                local tSize = #t
                if tSize > 0 and block_end+1 - t[1] > 32768 then
                  if tSize == 1 then
                    hash_tables[k] = nil
                  else
                    local new = {}
                    local newSize = 0
                    for i = 2, tSize do
                      j = t[i]
                      if block_end+1 - j <= 32768 then
                        newSize = newSize + 1
                        new[newSize] = j
                      end
                    end
                    hash_tables[k] = new
                  end
                end
              end
            end
          end
        end
        
        --- The description to compression configuration table. <br>
        -- Any field can be nil to use its default. <br>
        -- Table with keys other than those below is an invalid table.
        -- @class table
        -- @name compression_configs
        -- @field level The compression level ranged from 0 to 9. 0 is no compression.
        -- 9 is the slowest but best compression. Use nil for default level.
        -- @field strategy The compression strategy. "fixed" to only use fixed deflate
        -- compression block. "dynamic" to only use dynamic block. "huffman_only" to
        -- do no LZ77 compression. Only do huffman compression.
        
        
        -- @see LibDeflate:CompressDeflate(str, configs)
        -- @see LibDeflate:CompressDeflateWithDict(str, dictionary, configs)
        local function CompressDeflateInternal(str, dictionary, configs)
          local WriteBits, WriteString, FlushWriter = CreateWriter()
          Deflate(configs, WriteBits, WriteString, FlushWriter, str, dictionary)
          local total_bitlen, result = FlushWriter(_FLUSH_MODE_OUTPUT)
          local padding_bitlen = (8-total_bitlen%8)%8
          return result, padding_bitlen
        end
        
        -- @see LibDeflate:CompressZlib
        -- @see LibDeflate:CompressZlibWithDict
        local function CompressZlibInternal(str, dictionary, configs)
          local WriteBits, WriteString, FlushWriter = CreateWriter()
        
          local CM = 8 -- Compression method
          local CINFO = 7 --Window Size = 32K
          local CMF = CINFO*16+CM
          WriteBits(CMF, 8)
        
          local FDIST = dictionary and 1 or 0
          local FLEVEL = 2 -- Default compression
          local FLG = FLEVEL*64+FDIST*32
          local FCHECK = (31-(CMF*256+FLG)%31)
          -- The FCHECK value must be such that CMF and FLG,
          -- when viewed as a 16-bit unsigned integer stored
          -- in MSB order (CMF*256 + FLG), is a multiple of 31.
          FLG = FLG + FCHECK
          WriteBits(FLG, 8)
        
          if FDIST == 1 then
            local adler32 = dictionary.adler32
            local byte0 = adler32 % 256
            adler32 = (adler32 - byte0) / 256
            local byte1 = adler32 % 256
            adler32 = (adler32 - byte1) / 256
            local byte2 = adler32 % 256
            adler32 = (adler32 - byte2) / 256
            local byte3 = adler32 % 256
            WriteBits(byte3, 8)
            WriteBits(byte2, 8)
            WriteBits(byte1, 8)
            WriteBits(byte0, 8)
          end
        
          Deflate(configs, WriteBits, WriteString, FlushWriter, str, dictionary)
          FlushWriter(_FLUSH_MODE_BYTE_BOUNDARY)
        
          local adler32 = LibDeflate:Adler32(str)
        
          -- Most significant byte first
          local byte3 = adler32%256
          adler32 = (adler32 - byte3) / 256
          local byte2 = adler32%256
          adler32 = (adler32 - byte2) / 256
          local byte1 = adler32%256
          adler32 = (adler32 - byte1) / 256
          local byte0 = adler32%256
        
          WriteBits(byte0, 8)
          WriteBits(byte1, 8)
          WriteBits(byte2, 8)
          WriteBits(byte3, 8)
          local total_bitlen, result = FlushWriter(_FLUSH_MODE_OUTPUT)
          local padding_bitlen = (8-total_bitlen%8)%8
          return result, padding_bitlen
        end
        
        --- Compress using the raw deflate format.
        -- @param str [string] The data to be compressed.
        -- @param configs [table/nil] The configuration table to control the compression
        -- . If nil, use the default configuration.
        -- @return [string] The compressed data.
        -- @return [integer] The number of bits padded at the end of output.
        -- 0 <= bits < 8  <br>
        -- This means the most significant "bits" of the last byte of the returned
        -- compressed data are padding bits and they don't affect decompression.
        -- You don't need to use this value unless you want to do some postprocessing
        -- to the compressed data.
        -- @see compression_configs
        -- @see LibDeflate:DecompressDeflate
        function LibDeflate:CompressDeflate(str, configs)
          local arg_valid, arg_err = IsValidArguments(str, false, nil, true, configs)
          if not arg_valid then
            error(("Usage: LibDeflate:CompressDeflate(str, configs): "
              ..arg_err), 2)
          end
          return CompressDeflateInternal(str, nil, configs)
        end
        
        --- Compress using the raw deflate format with a preset dictionary.
        -- @param str [string] The data to be compressed.
        -- @param dictionary [table] The preset dictionary produced by
        -- LibDeflate:CreateDictionary
        -- @param configs [table/nil] The configuration table to control the compression
        -- . If nil, use the default configuration.
        -- @return [string] The compressed data.
        -- @return [integer] The number of bits padded at the end of output.
        -- 0 <= bits < 8  <br>
        -- This means the most significant "bits" of the last byte of the returned
        -- compressed data are padding bits and they don't affect decompression.
        -- You don't need to use this value unless you want to do some postprocessing
        -- to the compressed data.
        -- @see compression_configs
        -- @see LibDeflate:CreateDictionary
        -- @see LibDeflate:DecompressDeflateWithDict
        function LibDeflate:CompressDeflateWithDict(str, dictionary, configs)
          local arg_valid, arg_err = IsValidArguments(str, true, dictionary
            , true, configs)
          if not arg_valid then
            error(("Usage: LibDeflate:CompressDeflateWithDict"
              .."(str, dictionary, configs): "
              ..arg_err), 2)
          end
          return CompressDeflateInternal(str, dictionary, configs)
        end
        
        --- Compress using the zlib format.
        -- @param str [string] the data to be compressed.
        -- @param configs [table/nil] The configuration table to control the compression
        -- . If nil, use the default configuration.
        -- @return [string] The compressed data.
        -- @return [integer] The number of bits padded at the end of output.
        -- Should always be 0.
        -- Zlib formatted compressed data never has padding bits at the end.
        -- @see compression_configs
        -- @see LibDeflate:DecompressZlib
        function LibDeflate:CompressZlib(str, configs)
          local arg_valid, arg_err = IsValidArguments(str, false, nil, true, configs)
          if not arg_valid then
            error(("Usage: LibDeflate:CompressZlib(str, configs): "
              ..arg_err), 2)
          end
          return CompressZlibInternal(str, nil, configs)
        end
        
        --- Compress using the zlib format with a preset dictionary.
        -- @param str [string] the data to be compressed.
        -- @param dictionary [table] A preset dictionary produced
        -- by LibDeflate:CreateDictionary()
        -- @param configs [table/nil] The configuration table to control the compression
        -- . If nil, use the default configuration.
        -- @return [string] The compressed data.
        -- @return [integer] The number of bits padded at the end of output.
        -- Should always be 0.
        -- Zlib formatted compressed data never has padding bits at the end.
        -- @see compression_configs
        -- @see LibDeflate:CreateDictionary
        -- @see LibDeflate:DecompressZlibWithDict
        function LibDeflate:CompressZlibWithDict(str, dictionary, configs)
          local arg_valid, arg_err = IsValidArguments(str, true, dictionary
            , true, configs)
          if not arg_valid then
            error(("Usage: LibDeflate:CompressZlibWithDict"
              .."(str, dictionary, configs): "
              ..arg_err), 2)
          end
          return CompressZlibInternal(str, dictionary, configs)
        end
        
        --[[ --------------------------------------------------------------------------
          Decompress code
        --]] --------------------------------------------------------------------------
        
        --[[
          Create a reader to easily reader stuffs as the unit of bits.
          Return values:
          1. ReadBits(bitlen)
          2. ReadBytes(bytelen, buffer, buffer_size)
          3. Decode(huffman_bitlen_count, huffman_symbol, min_bitlen)
          4. ReaderBitlenLeft()
          5. SkipToByteBoundary()
        --]]
        local function CreateReader(input_string)
          local input = input_string
          local input_strlen = #input_string
          local input_next_byte_pos = 1
          local cache_bitlen = 0
          local cache = 0
        
          -- Read some bits.
          -- To improve speed, this function does not
          -- check if the input has been exhausted.
          -- Use ReaderBitlenLeft() < 0 to check it.
          -- @param bitlen the number of bits to read
          -- @return the data is read.
          local function ReadBits(bitlen)
            local rshift_mask = _pow2[bitlen]
            local code
            if bitlen <= cache_bitlen then
              code = cache % rshift_mask
              cache = (cache - code) / rshift_mask
              cache_bitlen = cache_bitlen - bitlen
            else -- Whether input has been exhausted is not checked.
              local lshift_mask = _pow2[cache_bitlen]
              local byte1, byte2, byte3, byte4 = string_byte(input
                , input_next_byte_pos, input_next_byte_pos+3)
              -- This requires lua number to be at least double ()
              cache = cache + ((byte1 or 0)+(byte2 or 0)*256
                + (byte3 or 0)*65536+(byte4 or 0)*16777216)*lshift_mask
              input_next_byte_pos = input_next_byte_pos + 4
              cache_bitlen = cache_bitlen + 32 - bitlen
              code = cache % rshift_mask
              cache = (cache - code) / rshift_mask
            end
            return code
          end
        
          -- Read some bytes from the reader.
          -- Assume reader is on the byte boundary.
          -- @param bytelen The number of bytes to be read.
          -- @param buffer The byte read will be stored into this buffer.
          -- @param buffer_size The buffer will be modified starting from
          --	buffer[buffer_size+1], ending at buffer[buffer_size+bytelen-1]
          -- @return the new buffer_size
          local function ReadBytes(bytelen, buffer, buffer_size)
            assert(cache_bitlen % 8 == 0)
        
            local byte_from_cache = (cache_bitlen/8 < bytelen)
              and (cache_bitlen/8) or bytelen
            for _=1, byte_from_cache do
              local byte = cache % 256
              buffer_size = buffer_size + 1
              buffer[buffer_size] = string_char(byte)
              cache = (cache - byte) / 256
            end
            cache_bitlen = cache_bitlen - byte_from_cache*8
            bytelen = bytelen - byte_from_cache
            if (input_strlen - input_next_byte_pos - bytelen + 1) * 8
              + cache_bitlen < 0 then
              return -1 -- out of input
            end
            for i=input_next_byte_pos, input_next_byte_pos+bytelen-1 do
              buffer_size = buffer_size + 1
              buffer[buffer_size] = string_sub(input, i, i)
            end
        
            input_next_byte_pos = input_next_byte_pos + bytelen
            return buffer_size
          end
        
          -- Decode huffman code
          -- To improve speed, this function does not check
          -- if the input has been exhausted.
          -- Use ReaderBitlenLeft() < 0 to check it.
          -- Credits for Mark Adler. This code is from puff:Decode()
          -- @see puff:Decode(...)
          -- @param huffman_bitlen_count
          -- @param huffman_symbol
          -- @param min_bitlen The minimum huffman bit length of all symbols
          -- @return The decoded deflate code.
          --	Negative value is returned if decoding fails.
          local function Decode(huffman_bitlen_counts, huffman_symbols, min_bitlen)
            local code = 0
            local first = 0
            local index = 0
            local count
            if min_bitlen > 0 then
              if cache_bitlen < 15 and input then
                local lshift_mask = _pow2[cache_bitlen]
                local byte1, byte2, byte3, byte4 =
                  string_byte(input, input_next_byte_pos
                  , input_next_byte_pos+3)
                -- This requires lua number to be at least double ()
                cache = cache + ((byte1 or 0)+(byte2 or 0)*256
                  +(byte3 or 0)*65536+(byte4 or 0)*16777216)*lshift_mask
                input_next_byte_pos = input_next_byte_pos + 4
                cache_bitlen = cache_bitlen + 32
              end
        
              local rshift_mask = _pow2[min_bitlen]
              cache_bitlen = cache_bitlen - min_bitlen
              code = cache % rshift_mask
              cache = (cache - code) / rshift_mask
              -- Reverse the bits
              code = _reverse_bits_tbl[min_bitlen][code]
        
              count = huffman_bitlen_counts[min_bitlen]
              if code < count then
                return huffman_symbols[code]
              end
              index = count
              first = count * 2
              code = code * 2
            end
        
            for bitlen = min_bitlen+1, 15 do
              local bit
              bit = cache % 2
              cache = (cache - bit) / 2
              cache_bitlen = cache_bitlen - 1
        
              code = (bit==1) and (code + 1 - code % 2) or code
              count = huffman_bitlen_counts[bitlen] or 0
              local diff = code - first
              if diff < count then
                return huffman_symbols[index + diff]
              end
              index = index + count
              first = first + count
              first = first * 2
              code = code * 2
            end
            -- invalid literal/length or distance code
            -- in fixed or dynamic block (run out of code)
            return -10
          end
        
          local function ReaderBitlenLeft()
            return (input_strlen - input_next_byte_pos + 1) * 8 + cache_bitlen
          end
        
          local function SkipToByteBoundary()
            local skipped_bitlen = cache_bitlen%8
            local rshift_mask = _pow2[skipped_bitlen]
            cache_bitlen = cache_bitlen - skipped_bitlen
            cache = (cache - cache % rshift_mask) / rshift_mask
          end
        
          return ReadBits, ReadBytes, Decode, ReaderBitlenLeft, SkipToByteBoundary
        end
        
        -- Create a deflate state, so I can pass in less arguments to functions.
        -- @param str the whole string to be decompressed.
        -- @param dictionary The preset dictionary. nil if not provided.
        --		This dictionary should be produced by LibDeflate:CreateDictionary(str)
        -- @return The decomrpess state.
        local function CreateDecompressState(str, dictionary)
          local ReadBits, ReadBytes, Decode, ReaderBitlenLeft
            , SkipToByteBoundary = CreateReader(str)
          local state =
          {
            ReadBits = ReadBits,
            ReadBytes = ReadBytes,
            Decode = Decode,
            ReaderBitlenLeft = ReaderBitlenLeft,
            SkipToByteBoundary = SkipToByteBoundary,
            buffer_size = 0,
            buffer = {},
            result_buffer = {},
            dictionary = dictionary,
          }
          return state
        end
        
        -- Get the stuffs needed to decode huffman codes
        -- @see puff.c:construct(...)
        -- @param huffman_bitlen The huffman bit length of the huffman codes.
        -- @param max_symbol The maximum symbol
        -- @param max_bitlen The min huffman bit length of all codes
        -- @return zero or positive for success, negative for failure.
        -- @return The count of each huffman bit length.
        -- @return A table to convert huffman codes to deflate codes.
        -- @return The minimum huffman bit length.
        local function GetHuffmanForDecode(huffman_bitlens, max_symbol, max_bitlen)
          local huffman_bitlen_counts = {}
          local min_bitlen = max_bitlen
          for symbol = 0, max_symbol do
            local bitlen = huffman_bitlens[symbol] or 0
            min_bitlen = (bitlen > 0 and bitlen < min_bitlen)
              and bitlen or min_bitlen
            huffman_bitlen_counts[bitlen] = (huffman_bitlen_counts[bitlen] or 0)+1
          end
        
          if huffman_bitlen_counts[0] == max_symbol+1 then -- No Codes
            return 0, huffman_bitlen_counts, {}, 0 -- Complete, but decode will fail
          end
        
          local left = 1
          for len = 1, max_bitlen do
            left = left * 2
            left = left - (huffman_bitlen_counts[len] or 0)
            if left < 0 then
              return left -- Over-subscribed, return negative
            end
          end
        
          -- Generate offsets info symbol table for each length for sorting
          local offsets = {}
          offsets[1] = 0
          for len = 1, max_bitlen-1 do
            offsets[len + 1] = offsets[len] + (huffman_bitlen_counts[len] or 0)
          end
        
          local huffman_symbols = {}
          for symbol = 0, max_symbol do
            local bitlen = huffman_bitlens[symbol] or 0
            if bitlen ~= 0 then
              local offset = offsets[bitlen]
              huffman_symbols[offset] = symbol
              offsets[bitlen] = offsets[bitlen] + 1
            end
          end
        
          -- Return zero for complete set, positive for incomplete set.
          return left, huffman_bitlen_counts, huffman_symbols, min_bitlen
        end
        
        -- Decode a fixed or dynamic huffman blocks, excluding last block identifier
        -- and block type identifer.
        -- @see puff.c:codes()
        -- @param state decompression state that will be modified by this function.
        --	@see CreateDecompressState
        -- @param ... Read the source code
        -- @return 0 on success, other value on failure.
        local function DecodeUntilEndOfBlock(state, lcodes_huffman_bitlens
          , lcodes_huffman_symbols, lcodes_huffman_min_bitlen
          , dcodes_huffman_bitlens, dcodes_huffman_symbols
          , dcodes_huffman_min_bitlen)
          local buffer, buffer_size, ReadBits, Decode, ReaderBitlenLeft
            , result_buffer =
            state.buffer, state.buffer_size, state.ReadBits, state.Decode
            , state.ReaderBitlenLeft, state.result_buffer
          local dictionary = state.dictionary
          local dict_string_table
          local dict_strlen
        
          local buffer_end = 1
          if dictionary and not buffer[0] then
            -- If there is a dictionary, copy the last 258 bytes into
            -- the string_table to make the copy in the main loop quicker.
            -- This is done only once per decompression.
            dict_string_table = dictionary.string_table
            dict_strlen = dictionary.strlen
            buffer_end = -dict_strlen + 1
            for i=0, (-dict_strlen+1)<-257 and -257 or (-dict_strlen+1), -1 do
              buffer[i] = _byte_to_char[dict_string_table[dict_strlen+i]]
            end
          end
        
          repeat
            local symbol = Decode(lcodes_huffman_bitlens
              , lcodes_huffman_symbols, lcodes_huffman_min_bitlen)
            if symbol < 0 or symbol > 285 then
            -- invalid literal/length or distance code in fixed or dynamic block
              return -10
            elseif symbol < 256 then -- Literal
              buffer_size = buffer_size + 1
              buffer[buffer_size] = _byte_to_char[symbol]
            elseif symbol > 256 then -- Length code
              symbol = symbol - 256
              local bitlen = _literal_deflate_code_to_base_len[symbol]
              bitlen = (symbol >= 8)
                 and (bitlen
                 + ReadBits(_literal_deflate_code_to_extra_bitlen[symbol]))
                  or bitlen
              symbol = Decode(dcodes_huffman_bitlens, dcodes_huffman_symbols
                , dcodes_huffman_min_bitlen)
              if symbol < 0 or symbol > 29 then
              -- invalid literal/length or distance code in fixed or dynamic block
                return -10
              end
              local dist = _dist_deflate_code_to_base_dist[symbol]
              dist = (dist > 4) and (dist
                + ReadBits(_dist_deflate_code_to_extra_bitlen[symbol])) or dist
        
              local char_buffer_index = buffer_size-dist+1
              if char_buffer_index < buffer_end then
              -- distance is too far back in fixed or dynamic block
                return -11
              end
              if char_buffer_index >= -257 then
                for _=1, bitlen do
                  buffer_size = buffer_size + 1
                  buffer[buffer_size] = buffer[char_buffer_index]
                  char_buffer_index = char_buffer_index + 1
                end
              else
                char_buffer_index = dict_strlen + char_buffer_index
                for _=1, bitlen do
                  buffer_size = buffer_size + 1
                  buffer[buffer_size] =
                  _byte_to_char[dict_string_table[char_buffer_index]]
                  char_buffer_index = char_buffer_index + 1
                end
              end
            end
        
            if ReaderBitlenLeft() < 0 then
              return 2 -- available inflate data did not terminate
            end
        
            if buffer_size >= 65536 then
              result_buffer[#result_buffer+1] =
                table_concat(buffer, "", 1, 32768)
              for i=32769, buffer_size do
                buffer[i-32768] = buffer[i]
              end
              buffer_size = buffer_size - 32768
              buffer[buffer_size+1] = nil
              -- NOTE: buffer[32769..end] and buffer[-257..0] are not cleared.
              -- This is why "buffer_size" variable is needed.
            end
          until symbol == 256
        
          state.buffer_size = buffer_size
        
          return 0
        end
        
        -- Decompress a store block
        -- @param state decompression state that will be modified by this function.
        -- @return 0 if succeeds, other value if fails.
        local function DecompressStoreBlock(state)
          local buffer, buffer_size, ReadBits, ReadBytes, ReaderBitlenLeft
            , SkipToByteBoundary, result_buffer =
            state.buffer, state.buffer_size, state.ReadBits, state.ReadBytes
            , state.ReaderBitlenLeft, state.SkipToByteBoundary, state.result_buffer
        
          SkipToByteBoundary()
          local bytelen = ReadBits(16)
          if ReaderBitlenLeft() < 0 then
            return 2 -- available inflate data did not terminate
          end
          local bytelenComp = ReadBits(16)
          if ReaderBitlenLeft() < 0 then
            return 2 -- available inflate data did not terminate
          end
        
          if bytelen % 256 + bytelenComp % 256 ~= 255 then
            return -2 -- Not one's complement
          end
          if (bytelen-bytelen % 256)/256
            + (bytelenComp-bytelenComp % 256)/256 ~= 255 then
            return -2 -- Not one's complement
          end
        
          -- Note that ReadBytes will skip to the next byte boundary first.
          buffer_size = ReadBytes(bytelen, buffer, buffer_size)
          if buffer_size < 0 then
            return 2 -- available inflate data did not terminate
          end
        
          -- memory clean up when there are enough bytes in the buffer.
          if buffer_size >= 65536 then
            result_buffer[#result_buffer+1] = table_concat(buffer, "", 1, 32768)
            for i=32769, buffer_size do
              buffer[i-32768] = buffer[i]
            end
            buffer_size = buffer_size - 32768
            buffer[buffer_size+1] = nil
          end
          state.buffer_size = buffer_size
          return 0
        end
        
        -- Decompress a fixed block
        -- @param state decompression state that will be modified by this function.
        -- @return 0 if succeeds other value if fails.
        local function DecompressFixBlock(state)
          return DecodeUntilEndOfBlock(state
            , _fix_block_literal_huffman_bitlen_count
            , _fix_block_literal_huffman_to_deflate_code, 7
            , _fix_block_dist_huffman_bitlen_count
            , _fix_block_dist_huffman_to_deflate_code, 5)
        end
        
        -- Decompress a dynamic block
        -- @param state decompression state that will be modified by this function.
        -- @return 0 if success, other value if fails.
        local function DecompressDynamicBlock(state)
          local ReadBits, Decode = state.ReadBits, state.Decode
          local nlen = ReadBits(5) + 257
          local ndist = ReadBits(5) + 1
          local ncode = ReadBits(4) + 4
          if nlen > 286 or ndist > 30 then
            -- dynamic block code description: too many length or distance codes
            return -3
          end
        
          local rle_codes_huffman_bitlens = {}
        
          for i = 1, ncode do
            rle_codes_huffman_bitlens[_rle_codes_huffman_bitlen_order[i]] =
              ReadBits(3)
          end
        
          local rle_codes_err, rle_codes_huffman_bitlen_counts,
            rle_codes_huffman_symbols, rle_codes_huffman_min_bitlen =
            GetHuffmanForDecode(rle_codes_huffman_bitlens, 18, 7)
          if rle_codes_err ~= 0 then -- Require complete code set here
            -- dynamic block code description: code lengths codes incomplete
            return -4
          end
        
          local lcodes_huffman_bitlens = {}
          local dcodes_huffman_bitlens = {}
          -- Read length/literal and distance code length tables
          local index = 0
          while index < nlen + ndist do
            local symbol -- Decoded value
            local bitlen -- Last length to repeat
        
            symbol = Decode(rle_codes_huffman_bitlen_counts
              , rle_codes_huffman_symbols, rle_codes_huffman_min_bitlen)
        
            if symbol < 0 then
              return symbol -- Invalid symbol
            elseif symbol < 16 then
              if index < nlen then
                lcodes_huffman_bitlens[index] = symbol
              else
                dcodes_huffman_bitlens[index-nlen] = symbol
              end
              index = index + 1
            else
              bitlen = 0
              if symbol == 16 then
                if index == 0 then
                  -- dynamic block code description: repeat lengths
                  -- with no first length
                  return -5
                end
                if index-1 < nlen then
                  bitlen = lcodes_huffman_bitlens[index-1]
                else
                  bitlen = dcodes_huffman_bitlens[index-nlen-1]
                end
                symbol = 3 + ReadBits(2)
              elseif symbol == 17 then -- Repeat zero 3..10 times
                symbol = 3 + ReadBits(3)
              else -- == 18, repeat zero 11.138 times
                symbol = 11 + ReadBits(7)
              end
              if index + symbol > nlen + ndist then
                -- dynamic block code description:
                -- repeat more than specified lengths
                return -6
              end
              while symbol > 0 do -- Repeat last or zero symbol times
                symbol = symbol - 1
                if index < nlen then
                  lcodes_huffman_bitlens[index] = bitlen
                else
                  dcodes_huffman_bitlens[index-nlen] = bitlen
                end
                index = index + 1
              end
            end
          end
        
          if (lcodes_huffman_bitlens[256] or 0) == 0 then
            -- dynamic block code description: missing end-of-block code
            return -9
          end
        
          local lcodes_err, lcodes_huffman_bitlen_counts
            , lcodes_huffman_symbols, lcodes_huffman_min_bitlen =
            GetHuffmanForDecode(lcodes_huffman_bitlens, nlen-1, 15)
          --dynamic block code description: invalid literal/length code lengths,
          -- Incomplete code ok only for single length 1 code
          if (lcodes_err ~=0 and (lcodes_err < 0
            or nlen ~= (lcodes_huffman_bitlen_counts[0] or 0)
              +(lcodes_huffman_bitlen_counts[1] or 0))) then
            return -7
          end
        
          local dcodes_err, dcodes_huffman_bitlen_counts
            , dcodes_huffman_symbols, dcodes_huffman_min_bitlen =
            GetHuffmanForDecode(dcodes_huffman_bitlens, ndist-1, 15)
          -- dynamic block code description: invalid distance code lengths,
          -- Incomplete code ok only for single length 1 code
          if (dcodes_err ~=0 and (dcodes_err < 0
            or ndist ~= (dcodes_huffman_bitlen_counts[0] or 0)
              + (dcodes_huffman_bitlen_counts[1] or 0))) then
            return -8
          end
        
          -- Build buffman table for literal/length codes
          return DecodeUntilEndOfBlock(state, lcodes_huffman_bitlen_counts
            , lcodes_huffman_symbols, lcodes_huffman_min_bitlen
            , dcodes_huffman_bitlen_counts, dcodes_huffman_symbols
            , dcodes_huffman_min_bitlen)
        end
        
        -- Decompress a deflate stream
        -- @param state: a decompression state
        -- @return the decompressed string if succeeds. nil if fails.
        local function Inflate(state)
          local ReadBits = state.ReadBits
        
          local is_last_block
          while not is_last_block do
            is_last_block = (ReadBits(1) == 1)
            local block_type = ReadBits(2)
            local status
            if block_type == 0 then
              status = DecompressStoreBlock(state)
            elseif block_type == 1 then
              status = DecompressFixBlock(state)
            elseif block_type == 2 then
              status = DecompressDynamicBlock(state)
            else
              return nil, -1 -- invalid block type (type == 3)
            end
            if status ~= 0 then
              return nil, status
            end
          end
        
          state.result_buffer[#state.result_buffer+1] =
            table_concat(state.buffer, "", 1, state.buffer_size)
          local result = table_concat(state.result_buffer)
          return result
        end
        
        -- @see LibDeflate:DecompressDeflate(str)
        -- @see LibDeflate:DecompressDeflateWithDict(str, dictionary)
        local function DecompressDeflateInternal(str, dictionary)
          local state = CreateDecompressState(str, dictionary)
          local result, status = Inflate(state)
          if not result then
            return nil, status
          end
          local bitlen_left = state.ReaderBitlenLeft()
          local bytelen_left = (bitlen_left - bitlen_left % 8) / 8
          return result, bytelen_left
        end
        
        -- @see LibDeflate:DecompressZlib(str)
        -- @see LibDeflate:DecompressZlibWithDict(str)
        local function DecompressZlibInternal(str, dictionary)
          local state = CreateDecompressState(str, dictionary)
          local ReadBits = state.ReadBits
        
          local CMF = ReadBits(8)
          if state.ReaderBitlenLeft() < 0 then
            return nil, 2 -- available inflate data did not terminate
          end
          local CM = CMF % 16
          local CINFO = (CMF - CM) / 16
          if CM ~= 8 then
            return nil, -12 -- invalid compression method
          end
          if CINFO > 7 then
            return nil, -13 -- invalid window size
          end
        
          local FLG = ReadBits(8)
          if state.ReaderBitlenLeft() < 0 then
            return nil, 2 -- available inflate data did not terminate
          end
          if (CMF*256+FLG)%31 ~= 0 then
            return nil, -14 -- invalid header checksum
          end
        
          local FDIST = ((FLG-FLG%32)/32 % 2)
          local FLEVEL = ((FLG-FLG%64)/64 % 4) -- luacheck: ignore FLEVEL
        
          if FDIST == 1 then
            if not dictionary then
              return nil, -16 -- need dictonary, but dictionary is not provided.
            end
            local byte3 = ReadBits(8)
            local byte2 = ReadBits(8)
            local byte1 = ReadBits(8)
            local byte0 = ReadBits(8)
            local actual_adler32 = byte3*16777216+byte2*65536+byte1*256+byte0
            if state.ReaderBitlenLeft() < 0 then
              return nil, 2 -- available inflate data did not terminate
            end
            if not IsEqualAdler32(actual_adler32, dictionary.adler32) then
              return nil, -17 -- dictionary adler32 does not match
            end
          end
          local result, status = Inflate(state)
          if not result then
            return nil, status
          end
          state.SkipToByteBoundary()
        
          local adler_byte0 = ReadBits(8)
          local adler_byte1 = ReadBits(8)
          local adler_byte2 = ReadBits(8)
          local adler_byte3 = ReadBits(8)
          if state.ReaderBitlenLeft() < 0 then
            return nil, 2 -- available inflate data did not terminate
          end
        
          local adler32_expected = adler_byte0*16777216
            + adler_byte1*65536 + adler_byte2*256 + adler_byte3
          local adler32_actual = LibDeflate:Adler32(result)
          if not IsEqualAdler32(adler32_expected, adler32_actual) then
            return nil, -15 -- Adler32 checksum does not match
          end
        
          local bitlen_left = state.ReaderBitlenLeft()
          local bytelen_left = (bitlen_left - bitlen_left % 8) / 8
          return result, bytelen_left
        end
        
        --- Decompress a raw deflate compressed data.
        -- @param str [string] The data to be decompressed.
        -- @return [string/nil] If the decompression succeeds, return the decompressed
        -- data. If the decompression fails, return nil. You should check if this return
        -- value is non-nil to know if the decompression succeeds.
        -- @return [integer] If the decompression succeeds, return the number of
        -- unprocessed bytes in the input compressed data. This return value is a
        -- positive integer if the input data is a valid compressed data appended by an
        -- arbitary non-empty string. This return value is 0 if the input data does not
        -- contain any extra bytes.<br>
        -- If the decompression fails (The first return value of this function is nil),
        -- this return value is undefined.
        -- @see LibDeflate:CompressDeflate
        function LibDeflate:DecompressDeflate(str)
          local arg_valid, arg_err = IsValidArguments(str)
          if not arg_valid then
            error(("Usage: LibDeflate:DecompressDeflate(str): "
              ..arg_err), 2)
          end
          return DecompressDeflateInternal(str)
        end
        
        --- Decompress a raw deflate compressed data with a preset dictionary.
        -- @param str [string] The data to be decompressed.
        -- @param dictionary [table] The preset dictionary used by
        -- LibDeflate:CompressDeflateWithDict when the compressed data is produced.
        -- Decompression and compression must use the same dictionary.
        -- Otherwise wrong decompressed data could be produced without generating any
        -- error.
        -- @return [string/nil] If the decompression succeeds, return the decompressed
        -- data. If the decompression fails, return nil. You should check if this return
        -- value is non-nil to know if the decompression succeeds.
        -- @return [integer] If the decompression succeeds, return the number of
        -- unprocessed bytes in the input compressed data. This return value is a
        -- positive integer if the input data is a valid compressed data appended by an
        -- arbitary non-empty string. This return value is 0 if the input data does not
        -- contain any extra bytes.<br>
        -- If the decompression fails (The first return value of this function is nil),
        -- this return value is undefined.
        -- @see LibDeflate:CompressDeflateWithDict
        function LibDeflate:DecompressDeflateWithDict(str, dictionary)
          local arg_valid, arg_err = IsValidArguments(str, true, dictionary)
          if not arg_valid then
            error(("Usage: LibDeflate:DecompressDeflateWithDict(str, dictionary): "
              ..arg_err), 2)
          end
          return DecompressDeflateInternal(str, dictionary)
        end
        
        --- Decompress a zlib compressed data.
        -- @param str [string] The data to be decompressed
        -- @return [string/nil] If the decompression succeeds, return the decompressed
        -- data. If the decompression fails, return nil. You should check if this return
        -- value is non-nil to know if the decompression succeeds.
        -- @return [integer] If the decompression succeeds, return the number of
        -- unprocessed bytes in the input compressed data. This return value is a
        -- positive integer if the input data is a valid compressed data appended by an
        -- arbitary non-empty string. This return value is 0 if the input data does not
        -- contain any extra bytes.<br>
        -- If the decompression fails (The first return value of this function is nil),
        -- this return value is undefined.
        -- @see LibDeflate:CompressZlib
        function LibDeflate:DecompressZlib(str)
          local arg_valid, arg_err = IsValidArguments(str)
          if not arg_valid then
            error(("Usage: LibDeflate:DecompressZlib(str): "
              ..arg_err), 2)
          end
          return DecompressZlibInternal(str)
        end
        
        --- Decompress a zlib compressed data with a preset dictionary.
        -- @param str [string] The data to be decompressed
        -- @param dictionary [table] The preset dictionary used by
        -- LibDeflate:CompressDeflateWithDict when the compressed data is produced.
        -- Decompression and compression must use the same dictionary.
        -- Otherwise wrong decompressed data could be produced without generating any
        -- error.
        -- @return [string/nil] If the decompression succeeds, return the decompressed
        -- data. If the decompression fails, return nil. You should check if this return
        -- value is non-nil to know if the decompression succeeds.
        -- @return [integer] If the decompression succeeds, return the number of
        -- unprocessed bytes in the input compressed data. This return value is a
        -- positive integer if the input data is a valid compressed data appended by an
        -- arbitary non-empty string. This return value is 0 if the input data does not
        -- contain any extra bytes.<br>
        -- If the decompression fails (The first return value of this function is nil),
        -- this return value is undefined.
        -- @see LibDeflate:CompressZlibWithDict
        function LibDeflate:DecompressZlibWithDict(str, dictionary)
          local arg_valid, arg_err = IsValidArguments(str, true, dictionary)
          if not arg_valid then
            error(("Usage: LibDeflate:DecompressZlibWithDict(str, dictionary): "
              ..arg_err), 2)
          end
          return DecompressZlibInternal(str, dictionary)
        end
        
        -- Calculate the huffman code of fixed block
        do
          _fix_block_literal_huffman_bitlen = {}
          for sym=0, 143 do
            _fix_block_literal_huffman_bitlen[sym] = 8
          end
          for sym=144, 255 do
            _fix_block_literal_huffman_bitlen[sym] = 9
          end
          for sym=256, 279 do
              _fix_block_literal_huffman_bitlen[sym] = 7
          end
          for sym=280, 287 do
            _fix_block_literal_huffman_bitlen[sym] = 8
          end
        
          _fix_block_dist_huffman_bitlen = {}
          for dist=0, 31 do
            _fix_block_dist_huffman_bitlen[dist] = 5
          end
          local status
          status, _fix_block_literal_huffman_bitlen_count
            , _fix_block_literal_huffman_to_deflate_code =
            GetHuffmanForDecode(_fix_block_literal_huffman_bitlen, 287, 9)
          assert(status == 0)
          status, _fix_block_dist_huffman_bitlen_count,
            _fix_block_dist_huffman_to_deflate_code =
            GetHuffmanForDecode(_fix_block_dist_huffman_bitlen, 31, 5)
          assert(status == 0)
        
          _fix_block_literal_huffman_code =
            GetHuffmanCodeFromBitlen(_fix_block_literal_huffman_bitlen_count
            , _fix_block_literal_huffman_bitlen, 287, 9)
          _fix_block_dist_huffman_code =
            GetHuffmanCodeFromBitlen(_fix_block_dist_huffman_bitlen_count
            , _fix_block_dist_huffman_bitlen, 31, 5)
        end
        
        -- Encoding algorithms
        -- Prefix encoding algorithm
        -- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com
        -- From LibCompress <https://www.wowace.com/projects/libcompress>,
        -- which is licensed under GPLv2
        -- The code has been modified by the author of LibDeflate.
        ------------------------------------------------------------------------------
        
        -- to be able to match any requested byte value, the search
        -- string must be preprocessed characters to escape with %:
        -- ( ) . % + - * ? [ ] ^ $
        -- "illegal" byte values:
        -- 0 is replaces %z
        local _gsub_escape_table = {
          ["\000"] = "%z", ["("] = "%(", [")"] = "%)", ["."] = "%.",
          ["%"] = "%%", ["+"] = "%+", ["-"] = "%-", ["*"] = "%*",
          ["?"] = "%?", ["["] = "%[", ["]"] = "%]", ["^"] = "%^",
          ["$"] = "%$",
        }
        
        local function escape_for_gsub(str)
          return str:gsub("([%z%(%)%.%%%+%-%*%?%[%]%^%$])", _gsub_escape_table)
        end
        
        --- Create a custom codec with encoder and decoder. <br>
        -- This codec is used to convert an input string to make it not contain
        -- some specific bytes.
        -- This created codec and the parameters of this function do NOT take
        -- localization into account. One byte (0-255) in the string is exactly one
        -- character (0-255).
        -- Credits to LibCompress.
        -- @param reserved_chars [string] The created encoder will ensure encoded
        -- data does not contain any single character in reserved_chars. This parameter
        -- should be non-empty.
        -- @param escape_chars [string] The escape character(s) used in the created
        -- codec. The codec converts any character included in reserved\_chars /
        -- escape\_chars / map\_chars to (one escape char + one character not in
        -- reserved\_chars / escape\_chars / map\_chars).
        -- You usually only need to provide a length-1 string for this parameter.
        -- Length-2 string is only needed when
        -- reserved\_chars + escape\_chars + map\_chars is longer than 127.
        -- This parameter should be non-empty.
        -- @param map_chars [string] The created encoder will map every
        -- reserved\_chars:sub(i, i) (1 <= i <= #map\_chars) to map\_chars:sub(i, i).
        -- This parameter CAN be empty string.
        -- @return [table/nil] If the codec cannot be created, return nil.<br>
        -- If the codec can be created according to the given
        -- parameters, return the codec, which is a encode/decode table.
        -- The table contains two functions: <br>
        -- t:Encode(str) returns the encoded string. <br>
        -- t:Decode(str) returns the decoded string if succeeds. nil if fails.
        -- @return [nil/string] If the codec is successfully created, return nil.
        -- If not, return a string that describes the reason why the codec cannot be
        -- created.
        -- @usage
        -- -- Create an encoder/decoder that maps all "\000" to "\003",
        -- -- and escape "\001" (and "\002" and "\003") properly
        -- local codec = LibDeflate:CreateCodec("\000\001", "\002", "\003")
        --
        -- local encoded = codec:Encode(SOME_STRING)
        -- -- "encoded" does not contain "\000" or "\001"
        -- local decoded = codec:Decode(encoded)
        -- -- assert(decoded == SOME_STRING)
        function LibDeflate:CreateCodec(reserved_chars, escape_chars
          , map_chars)
          -- select a default escape character
          if type(reserved_chars) ~= "string"
            or type(escape_chars) ~= "string"
            or type(map_chars) ~= "string" then
              error(
                "Usage: LibDeflate:CreateCodec(reserved_chars,"
                .." escape_chars, map_chars):"
                .." All arguments must be string.", 2)
          end
        
          if escape_chars == "" then
            return nil, "No escape characters supplied."
          end
          if #reserved_chars < #map_chars then
            return nil, "The number of reserved characters must be"
              .." at least as many as the number of mapped chars."
          end
          if reserved_chars == "" then
            return nil, "No characters to encode."
          end
        
          local encode_bytes = reserved_chars..escape_chars..map_chars
          -- build list of bytes not available as a suffix to a prefix byte
          local taken = {}
          for i = 1, #encode_bytes do
            local byte = string_byte(encode_bytes, i, i)
            if taken[byte] then -- Modified by LibDeflate:
              return nil, "There must be no duplicate characters in the"
                .." concatenation of reserved_chars, escape_chars and"
                .." map_chars."
            end
            taken[byte] = true
          end
        
          -- Modified by LibDeflate:
          -- Store the patterns and replacement in tables for later use.
          -- This function is modified that loadstring() lua api is no longer used.
          local decode_patterns = {}
          local decode_repls = {}
        
          -- the encoding can be a single gsub
          -- , but the decoding can require multiple gsubs
          local encode_search = {}
          local encode_translate = {}
        
          -- map single byte to single byte
          if #map_chars > 0 then
            local decode_search = {}
            local decode_translate = {}
            for i = 1, #map_chars do
              local from = string_sub(reserved_chars, i, i)
              local to = string_sub(map_chars, i, i)
              encode_translate[from] = to
              encode_search[#encode_search+1] = from
              decode_translate[to] = from
              decode_search[#decode_search+1] = to
            end
            decode_patterns[#decode_patterns+1] =
              "([".. escape_for_gsub(table_concat(decode_search)).."])"
            decode_repls[#decode_repls+1] = decode_translate
          end
        
          local escape_char_index = 1
          local escape_char = string_sub(escape_chars
            , escape_char_index, escape_char_index)
          -- map single byte to double-byte
          local r = 0 -- suffix char value to the escapeChar
        
          local decode_search = {}
          local decode_translate = {}
          for i = 1, #encode_bytes do
            local c = string_sub(encode_bytes, i, i)
            if not encode_translate[c] then
              -- this loop will update escapeChar and r
              while r >= 256 or taken[r] do
              -- Bug in LibCompress r81
              -- while r < 256 and taken[r] do
                r = r + 1
                if r > 255 then -- switch to next escapeChar
                  decode_patterns[#decode_patterns+1] =
                    escape_for_gsub(escape_char)
                    .."(["
                    .. escape_for_gsub(table_concat(decode_search)).."])"
                  decode_repls[#decode_repls+1] = decode_translate
        
                  escape_char_index = escape_char_index + 1
                  escape_char = string_sub(escape_chars, escape_char_index
                    , escape_char_index)
                  r = 0
                  decode_search = {}
                  decode_translate = {}
        
                  -- Fixes Another bug in LibCompress r82.
                  -- LibCompress checks this error condition
                  -- right after "if r > 255 then"
                  -- This is why error case should also be tested.
                  if not escape_char or escape_char == "" then
                    -- actually I don't need to check
                    -- "not ecape_char", but what if Lua changes
                    -- the behavior of string.sub() in the future?
                    -- we are out of escape chars and we need more!
                    return nil, "Out of escape characters."
                  end
                end
              end
        
              local char_r = _byte_to_char[r]
              encode_translate[c] = escape_char..char_r
              encode_search[#encode_search+1] = c
              decode_translate[char_r] = c
              decode_search[#decode_search+1] = char_r
              r = r + 1
            end
            if i == #encode_bytes then
              decode_patterns[#decode_patterns+1] =
                escape_for_gsub(escape_char).."(["
                .. escape_for_gsub(table_concat(decode_search)).."])"
              decode_repls[#decode_repls+1] = decode_translate
            end
          end
        
          local codec = {}
        
          local encode_pattern = "(["
            .. escape_for_gsub(table_concat(encode_search)).."])"
          local encode_repl = encode_translate
        
          function codec:Encode(str)
            if type(str) ~= "string" then
              error(("Usage: codec:Encode(str):"
                .." 'str' - string expected got '%s'."):format(type(str)), 2)
            end
            return string_gsub(str, encode_pattern, encode_repl)
          end
        
          local decode_tblsize = #decode_patterns
          local decode_fail_pattern = "(["
            .. escape_for_gsub(reserved_chars).."])"
        
          function codec:Decode(str)
            if type(str) ~= "string" then
              error(("Usage: codec:Decode(str):"
                .." 'str' - string expected got '%s'."):format(type(str)), 2)
            end
            if string_find(str, decode_fail_pattern) then
              return nil
            end
            for i = 1, decode_tblsize do
              str = string_gsub(str, decode_patterns[i], decode_repls[i])
            end
            return str
          end
        
          return codec
        end
        
        local _addon_channel_codec
        
        local function GenerateWoWAddonChannelCodec()
          return LibDeflate:CreateCodec("\000", "\001", "")
        end
        
        --- Encode the string to make it ready to be transmitted in World of
        -- Warcraft addon channel. <br>
        -- The encoded string is guaranteed to contain no NULL ("\000") character.
        -- @param str [string] The string to be encoded.
        -- @return The encoded string.
        -- @see LibDeflate:DecodeForWoWAddonChannel
        function LibDeflate:EncodeForWoWAddonChannel(str)
          if type(str) ~= "string" then
            error(("Usage: LibDeflate:EncodeForWoWAddonChannel(str):"
              .." 'str' - string expected got '%s'."):format(type(str)), 2)
          end
          if not _addon_channel_codec then
            _addon_channel_codec = GenerateWoWAddonChannelCodec()
          end
          return _addon_channel_codec:Encode(str)
        end
        
        --- Decode the string produced by LibDeflate:EncodeForWoWAddonChannel
        -- @param str [string] The string to be decoded.
        -- @return [string/nil] The decoded string if succeeds. nil if fails.
        -- @see LibDeflate:EncodeForWoWAddonChannel
        function LibDeflate:DecodeForWoWAddonChannel(str)
          if type(str) ~= "string" then
            error(("Usage: LibDeflate:DecodeForWoWAddonChannel(str):"
              .." 'str' - string expected got '%s'."):format(type(str)), 2)
          end
          if not _addon_channel_codec then
            _addon_channel_codec = GenerateWoWAddonChannelCodec()
          end
          return _addon_channel_codec:Decode(str)
        end
        
        -- For World of Warcraft Chat Channel Encoding
        -- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com
        -- From LibCompress <https://www.wowace.com/projects/libcompress>,
        -- which is licensed under GPLv2
        -- The code has been modified by the author of LibDeflate.
        -- Following byte values are not allowed:
        -- \000, s, S, \010, \013, \124, %
        -- Because SendChatMessage will error
        -- if an UTF8 multibyte character is incomplete,
        -- all character values above 127 have to be encoded to avoid this.
        -- This costs quite a bit of bandwidth (about 13-14%)
        -- Also, because drunken status is unknown for the received
        -- , strings used with SendChatMessage should be terminated with
        -- an identifying byte value, after which the server MAY add "...hic!"
        -- or as much as it can fit(!).
        -- Pass the identifying byte as a reserved character to this function
        -- to ensure the encoding doesn't contain that value.
        -- or use this: local message, match = arg1:gsub("^(.*)\029.-$", "%1")
        -- arg1 is message from channel, \029 is the string terminator
        -- , but may be used in the encoded datastream as well. :-)
        -- This encoding will expand data anywhere from:
        -- 0% (average with pure ascii text)
        -- 53.5% (average with random data valued zero to 255)
        -- 100% (only encoding data that encodes to two bytes)
        local function GenerateWoWChatChannelCodec()
          local r = {}
          for i = 128, 255 do
            r[#r+1] = _byte_to_char[i]
          end
        
          local reserved_chars = "sS\000\010\013\124%"..table_concat(r)
          return LibDeflate:CreateCodec(reserved_chars
            , "\029\031", "\015\020")
        end
        
        local _chat_channel_codec
        
        --- Encode the string to make it ready to be transmitted in World of
        -- Warcraft chat channel. <br>
        -- See also https://wow.gamepedia.com/ValidChatMessageCharacters
        -- @param str [string] The string to be encoded.
        -- @return [string] The encoded string.
        -- @see LibDeflate:DecodeForWoWChatChannel
        function LibDeflate:EncodeForWoWChatChannel(str)
          if type(str) ~= "string" then
            error(("Usage: LibDeflate:EncodeForWoWChatChannel(str):"
              .." 'str' - string expected got '%s'."):format(type(str)), 2)
          end
          if not _chat_channel_codec then
            _chat_channel_codec = GenerateWoWChatChannelCodec()
          end
          return _chat_channel_codec:Encode(str)
        end
        
        --- Decode the string produced by LibDeflate:EncodeForWoWChatChannel.
        -- @param str [string] The string to be decoded.
        -- @return [string/nil] The decoded string if succeeds. nil if fails.
        -- @see LibDeflate:EncodeForWoWChatChannel
        function LibDeflate:DecodeForWoWChatChannel(str)
          if type(str) ~= "string" then
            error(("Usage: LibDeflate:DecodeForWoWChatChannel(str):"
              .." 'str' - string expected got '%s'."):format(type(str)), 2)
          end
          if not _chat_channel_codec then
            _chat_channel_codec = GenerateWoWChatChannelCodec()
          end
          return _chat_channel_codec:Decode(str)
        end
        
        -- Credits to WeakAuras <https://github.com/WeakAuras/WeakAuras2>,
        -- and Galmok (galmok@gmail.com) for the 6 bit encoding algorithm.
        -- The result of encoding will be 25% larger than the
        -- origin string, but every single byte of the encoding result will be
        -- printable characters as the following.
        local _byte_to_6bit_char = {
          [0]="a", "b", "c", "d", "e", "f", "g", "h",
          "i", "j", "k", "l", "m", "n", "o", "p",
          "q", "r", "s", "t", "u", "v", "w", "x",
          "y", "z", "A", "B", "C", "D", "E", "F",
          "G", "H", "I", "J", "K", "L", "M", "N",
          "O", "P", "Q", "R", "S", "T", "U", "V",
          "W", "X", "Y", "Z", "0", "1", "2", "3",
          "4", "5", "6", "7", "8", "9", "(", ")",
        }
        
        local _6bit_to_byte = {
          [97]=0,[98]=1,[99]=2,[100]=3,[101]=4,[102]=5,[103]=6,[104]=7,
          [105]=8,[106]=9,[107]=10,[108]=11,[109]=12,[110]=13,[111]=14,[112]=15,
          [113]=16,[114]=17,[115]=18,[116]=19,[117]=20,[118]=21,[119]=22,[120]=23,
          [121]=24,[122]=25,[65]=26,[66]=27,[67]=28,[68]=29,[69]=30,[70]=31,
          [71]=32,[72]=33,[73]=34,[74]=35,[75]=36,[76]=37,[77]=38,[78]=39,
          [79]=40,[80]=41,[81]=42,[82]=43,[83]=44,[84]=45,[85]=46,[86]=47,
          [87]=48,[88]=49,[89]=50,[90]=51,[48]=52,[49]=53,[50]=54,[51]=55,
          [52]=56,[53]=57,[54]=58,[55]=59,[56]=60,[57]=61,[40]=62,[41]=63,
        }
        
        --- Encode the string to make it printable. <br>
        --
        -- Credis to WeakAuras2, this function is equivalant to the implementation
        -- it is using right now. <br>
        -- The encoded string will be 25% larger than the origin string. However, every
        -- single byte of the encoded string will be one of 64 printable ASCII
        -- characters, which are can be easier copied, pasted and displayed.
        -- (26 lowercase letters, 26 uppercase letters, 10 numbers digits,
        -- left parenthese, or right parenthese)
        -- @param str [string] The string to be encoded.
        -- @return [string] The encoded string.
        function LibDeflate:EncodeForPrint(str)
          if type(str) ~= "string" then
            error(("Usage: LibDeflate:EncodeForPrint(str):"
              .." 'str' - string expected got '%s'."):format(type(str)), 2)
          end
          local strlen = #str
          local strlenMinus2 = strlen - 2
          local i = 1
          local buffer = {}
          local buffer_size = 0
          while i <= strlenMinus2 do
            local x1, x2, x3 = string_byte(str, i, i+2)
            i = i + 3
            local cache = x1+x2*256+x3*65536
            local b1 = cache % 64
            cache = (cache - b1) / 64
            local b2 = cache % 64
            cache = (cache - b2) / 64
            local b3 = cache % 64
            local b4 = (cache - b3) / 64
            buffer_size = buffer_size + 1
            buffer[buffer_size] =
              _byte_to_6bit_char[b1].._byte_to_6bit_char[b2]
              .._byte_to_6bit_char[b3].._byte_to_6bit_char[b4]
          end
        
          local cache = 0
          local cache_bitlen = 0
          while i <= strlen do
            local x = string_byte(str, i, i)
            cache = cache + x * _pow2[cache_bitlen]
            cache_bitlen = cache_bitlen + 8
            i = i + 1
          end
          while cache_bitlen > 0 do
            local bit6 = cache % 64
            buffer_size = buffer_size + 1
            buffer[buffer_size] = _byte_to_6bit_char[bit6]
            cache = (cache - bit6) / 64
            cache_bitlen = cache_bitlen - 6
          end
        
          return table_concat(buffer)
        end
        
        --- Decode the printable string produced by LibDeflate:EncodeForPrint.
        -- "str" will have its prefixed and trailing control characters or space
        -- removed before it is decoded, so it is easier to use if "str" comes form
        -- user copy and paste with some prefixed or trailing spaces.
        -- Then decode fails if the string contains any characters cant be produced by
        -- LibDeflate:EncodeForPrint. That means, decode fails if the string contains a
        -- characters NOT one of 26 lowercase letters, 26 uppercase letters,
        -- 10 numbers digits, left parenthese, or right parenthese.
        -- @param str [string] The string to be decoded
        -- @return [string/nil] The decoded string if succeeds. nil if fails.
        function LibDeflate:DecodeForPrint(str)
          if type(str) ~= "string" then
            error(("Usage: LibDeflate:DecodeForPrint(str):"
              .." 'str' - string expected got '%s'."):format(type(str)), 2)
          end
          str = str:gsub("^[%c ]+", "")
          str = str:gsub("[%c ]+$", "")
        
          local strlen = #str
          if strlen == 1 then
            return nil
          end
          local strlenMinus3 = strlen - 3
          local i = 1
          local buffer = {}
          local buffer_size = 0
          while i <= strlenMinus3 do
            local x1, x2, x3, x4 = string_byte(str, i, i+3)
            x1 = _6bit_to_byte[x1]
            x2 = _6bit_to_byte[x2]
            x3 = _6bit_to_byte[x3]
            x4 = _6bit_to_byte[x4]
            if not (x1 and x2 and x3 and x4) then
              return nil
            end
            i = i + 4
            local cache = x1+x2*64+x3*4096+x4*262144
            local b1 = cache % 256
            cache = (cache - b1) / 256
            local b2 = cache % 256
            local b3 = (cache - b2) / 256
            buffer_size = buffer_size + 1
            buffer[buffer_size] =
              _byte_to_char[b1].._byte_to_char[b2].._byte_to_char[b3]
          end
        
          local cache  = 0
          local cache_bitlen = 0
          while i <= strlen do
            local x = string_byte(str, i, i)
            x =  _6bit_to_byte[x]
            if not x then
              return nil
            end
            cache = cache + x * _pow2[cache_bitlen]
            cache_bitlen = cache_bitlen + 6
            i = i + 1
          end
        
          while cache_bitlen >= 8 do
            local byte = cache % 256
            buffer_size = buffer_size + 1
            buffer[buffer_size] = _byte_to_char[byte]
            cache = (cache - byte) / 256
            cache_bitlen = cache_bitlen - 8
          end
        
          return table_concat(buffer)
        end
        
        local function InternalClearCache()
          _chat_channel_codec = nil
          _addon_channel_codec = nil
        end
        
        -- For test. Don't use the functions in this table for real application.
        -- Stuffs in this table is subject to change.
        LibDeflate.internals = {
          LoadStringToTable = LoadStringToTable,
          IsValidDictionary = IsValidDictionary,
          IsEqualAdler32 = IsEqualAdler32,
          _byte_to_6bit_char = _byte_to_6bit_char,
          _6bit_to_byte = _6bit_to_byte,
          InternalClearCache = InternalClearCache,
        }
        
        --[[-- Commandline options
        @class table
        @name CommandlineOptions
        @usage lua LibDeflate.lua [OPTION] [INPUT] [OUTPUT]
        \-0    store only. no compression.
        \-1    fastest compression.
        \-9    slowest and best compression.
        \-d    do decompression instead of compression.
        \--dict <filename> specify the file that contains
        the entire preset dictionary.
        \-h    give this help.
        \--strategy <fixed/huffman_only/dynamic> specify a special compression strategy.
        \-v    print the version and copyright info.
        \--zlib  use zlib format instead of raw deflate.
        ]]
        
        -- currently no plan to support stdin and stdout.
        -- Because Lua in Windows does not set stdout with binary mode.
        if io and os and debug and _G.arg then
          local io = io
          local os = os
          local debug = debug
          local arg = _G.arg
          local debug_info = debug.getinfo(1)
          if debug_info.source == arg[0]
            or debug_info.short_src == arg[0] then
          -- We are indeed runnning THIS file from the commandline.
            local input
            local output
            local i = 1
            local status
            local is_zlib = false
            local is_decompress = false
            local level
            local strategy
            local dictionary
            while (arg[i]) do
              local a = arg[i]
              if a == "-h" then
                print(LibDeflate._COPYRIGHT
                  .."\nUsage: lua LibDeflate.lua [OPTION] [INPUT] [OUTPUT]\n"
                  .."  -0    store only. no compression.\n"
                  .."  -1    fastest compression.\n"
                  .."  -9    slowest and best compression.\n"
                  .."  -d    do decompression instead of compression.\n"
                  .."  --dict <filename> specify the file that contains"
                  .." the entire preset dictionary.\n"
                  .."  -h    give this help.\n"
                  .."  --strategy <fixed/huffman_only/dynamic>"
                  .." specify a special compression strategy.\n"
                  .."  -v    print the version and copyright info.\n"
                  .."  --zlib  use zlib format instead of raw deflate.\n")
                os.exit(0)
              elseif a == "-v" then
                print(LibDeflate._COPYRIGHT)
                os.exit(0)
              elseif a:find("^%-[0-9]$") then
                level = tonumber(a:sub(2, 2))
              elseif a == "-d" then
                is_decompress = true
              elseif a == "--dict" then
                i = i + 1
                local dict_filename = arg[i]
                if not dict_filename then
                  io.stderr:write("You must speicify the dict filename")
                  os.exit(1)
                end
                local dict_file, dict_status = io.open(dict_filename, "rb")
                if not dict_file then
                  io.stderr:write(
                  ("LibDeflate: Cannot read the dictionary file '%s': %s")
                  :format(dict_filename, dict_status))
                  os.exit(1)
                end
                local dict_str = dict_file:read("*all")
                dict_file:close()
                -- In your lua program, you should pass in adler32 as a CONSTANT
                -- , so it actually prevent you from modifying dictionary
                -- unintentionally during the program development. I do this
                -- here just because no convenient way to verify in commandline.
                dictionary = LibDeflate:CreateDictionary(dict_str,
                  #dict_str, LibDeflate:Adler32(dict_str))
              elseif a == "--strategy" then
                -- Not sure if I should check error here
                -- If I do, redudant code.
                i = i + 1
                strategy = arg[i]
              elseif a == "--zlib" then
                is_zlib = true
              elseif a:find("^%-") then
                io.stderr:write(("LibDeflate: Invalid argument: %s")
                    :format(a))
                os.exit(1)
              else
                if not input then
                  input, status = io.open(a, "rb")
                  if not input then
                    io.stderr:write(
                      ("LibDeflate: Cannot read the file '%s': %s")
                      :format(a, tostring(status)))
                    os.exit(1)
                  end
                elseif not output then
                  output, status = io.open(a, "wb")
                  if not output then
                    io.stderr:write(
                      ("LibDeflate: Cannot write the file '%s': %s")
                      :format(a, tostring(status)))
                    os.exit(1)
                  end
                end
              end
              i = i + 1
            end -- while (arg[i])
        
            if not input or not output then
              io.stderr:write("LibDeflate:"
                .." You must specify both input and output files.")
              os.exit(1)
            end
        
            local input_data = input:read("*all")
            local configs = {
              level = level,
              strategy = strategy,
            }
            local output_data
            if not is_decompress then
              if not is_zlib then
                if not dictionary then
                  output_data =
                  LibDeflate:CompressDeflate(input_data, configs)
                else
                  output_data =
                  LibDeflate:CompressDeflateWithDict(input_data, dictionary
                    , configs)
                end
              else
                if not dictionary then
                  output_data =
                  LibDeflate:CompressZlib(input_data, configs)
                else
                  output_data =
                  LibDeflate:CompressZlibWithDict(input_data, dictionary
                    , configs)
                end
              end
            else
              if not is_zlib then
                if not dictionary then
                  output_data = LibDeflate:DecompressDeflate(input_data)
                else
                  output_data = LibDeflate:DecompressDeflateWithDict(
                    input_data, dictionary)
                end
              else
                if not dictionary then
                  output_data = LibDeflate:DecompressZlib(input_data)
                else
                  output_data = LibDeflate:DecompressZlibWithDict(
                    input_data, dictionary)
                end
              end
            end
        
            if not output_data then
              io.stderr:write("LibDeflate: Decompress fails.")
              os.exit(1)
            end
        
            output:write(output_data)
            if input and input ~= io.stdin then
              input:close()
            end
            if output and output ~= io.stdout then
              output:close()
            end
        
            io.stderr:write(("Successfully writes %d bytes"):format(
              output_data:len()))
            os.exit(0)
          end
        end
        
        return LibDeflate
        
    moesif-plugins-log.lua: local a=require("moesif.core")local b={}local c={application_id=nil,batch_size=5,user_id_header=nil,company_id_header=nil,metadata={},disable_capture_request_body=false,disable_capture_response_body=false,request_header_masks={},response_header_masks={},request_body_masks={},response_body_masks={},debug=false,send_event_endpoint="/_moesif/api/v1/events/batch",get_app_config_endpoint="/_moesif/api/v1/config"}function b.set_application_id(d)if type(d)=="string"then c["application_id"]=d end end;function b.set_batch_size(e)if type(e)=="number"then c["batch_size"]=e end end;function b.set_user_id_header(f)if type(f)=="string"then c["user_id_header"]=f end end;function b.set_company_id_header(g)if type(g)=="string"then c["company_id_header"]=g end end;function b.set_metadata(h)if type(h)=="table"then c["metadata"]=h end end;function b.set_disable_capture_request_body(i)if type(i)=="boolean"then c["disable_capture_request_body"]=i end end;function b.set_disable_capture_response_body(j)if type(j)=="boolean"then c["disable_capture_response_body"]=j end end;function b.set_request_header_masks(k)if type(k)=="table"then c["request_header_masks"]=k end end;function b.set_response_header_masks(l)if type(l)=="table"then c["response_header_masks"]=l end end;function b.set_request_body_masks(m)if type(m)=="table"then c["request_body_masks"]=m end end;function b.set_response_body_masks(n)if type(n)=="table"then c["response_body_masks"]=n end end;function b.set_debug(o)if type(o)=="boolean"then c["debug"]=o end end;function b.set_send_event_endpoint(p)if type(p)=="string"then c["send_event_endpoint"]=p end end;function b.set_get_app_config_endpoint(q)if type(q)=="string"then c["get_app_config_endpoint"]=q end end;batch_events={}last_updated_time=nil;app_confg={}function build_url(r)local s=nil;local t=r:headers():get("x-forwarded-proto")if t~=nil then s=t.."://"else s="http".."://"end;local u=r:headers():get(":authority")if u~=nil then s=s..u else s=s.."localhost"end;local v=r:headers():get(":path")if v~=nil then s=s..v else s=s.."/"end;return s end;function getLocalAddress(r)return r:streamInfo():downstreamLocalAddress()end;function getRemoteAddress(r)return r:streamInfo():downstreamDirectRemoteAddress()end;function b.log_request(r)if c["application_id"]~=nil and string.lower(r:headers():get(":method"))~="connect"then local w={}local x={}x["time"]=a.helpers.get_current_time_in_ms()x["uri"]=build_url(r)x["verb"]=r:headers():get(":method")x["headers"]=a.helpers.fetch_headers(r:headers())if next(c["request_header_masks"])~=nil then x["headers"]=a.helpers.mask_headers(x["headers"],c["request_header_masks"])end;if c["disable_capture_request_body"]then x["body"],x["transfer_encoding"]=nil,nil else if c["debug"]then r:logDebug("[moesif] Fetching Raw Request Body")end;local y=a.helpers.fetch_raw_body(r,c["debug"])if y~=nil and y~=''then local z,A,B=pcall(a.helpers.parse_body,x["headers"],y,c["request_body_masks"])if z then x["body"],x["transfer_encoding"]=A,B end else if c["debug"]then r:logDebug("[moesif] Fetched Raw Request Body is nil")end end end;x["user_agent_string"]=r:headers():get("user-agent")local C,D=pcall(getRemoteAddress,r)if C then x["ip_address"]=D else local E,F=pcall(getLocalAddress,r)if E then x["ip_address"]=F end end;if c["user_id_header"]~=nil then w["user_id"]=r:headers():get(c["user_id_header"])end;if c["company_id_header"]~=nil then w["company_id"]=r:headers():get(c["company_id_header"])end;w["request"]=x;r:streamInfo():dynamicMetadata():set("context","moesif_event",w)else if c["debug"]then r:logDebug("[moesif] !!!!! Please provide Moesif application Id. Please note, Moesif will skip logging the event incase the request method is CONNECT even if application Id is already provided. !!!!!")end end end;function dump(G)if type(G)=='table'then local H='{ 'for I,J in pairs(G)do if type(I)~='number'then I='"'..I..'"'end;H=H..'['..I..'] = '..dump(J)..','end;return H..'} 'else return tostring(G)end end;function b.log_response(r)local o=c["debug"]if c["application_id"]~=nil then local K=r:streamInfo():dynamicMetadata():get("context")if K~=nil then local w=K["moesif_event"]if app_config~=nil and type(app_config)=="string"and string.sub(app_config,1,1)=="{"then local L,M=pcall(a.json.decode,a.json,app_config)if L and M~=nil then app_config=M end end;local N=math.random()*100;local O=100;if type(app_config)=="table"and next(app_config)~=nil then if app_config["user_sample_rate"]~=nil and type(app_config["user_sample_rate"])=="table"and next(app_config["user_sample_rate"])~=nil and w["user_id"]~=nil and app_config["user_sample_rate"][w["user_id"]]then O=app_config["user_sample_rate"][w["user_id"]]elseif app_config["company_sample_rate"]~=nil and type(app_config["company_sample_rate"])=="table"and next(app_config["company_sample_rate"])~=nil and w["company_id"]~=nil and app_config["company_sample_rate"][w["company_id"]]then O=app_config["company_sample_rate"][w["company_id"]]end end;if O>N then local P={}P["time"]=a.helpers.get_current_time_in_ms()P["status"]=tonumber(r:headers():get(":status"))P["headers"]=a.helpers.fetch_headers(r:headers())if next(c["response_header_masks"])~=nil then P["headers"]=a.helpers.mask_headers(P["headers"],c["response_header_masks"])end;if c["disable_capture_response_body"]then P["body"],P["transfer_encoding"]=nil,nil else if o then r:logDebug("[moesif] Fetching Raw Response Body")end;local Q=a.helpers.fetch_raw_body(r,o)if Q~=nil and Q~=''then local R,S,T=pcall(a.helpers.parse_body,P["headers"],Q,c["response_body_masks"])if R then P["body"],P["transfer_encoding"]=S,T end else if o then r:logDebug("[moesif] Fetched Raw Response Body is nil")end end end;w["response"]=P;w["direction"]="Incoming"if next(c["metadata"])~=nil then w["metadata"]=c["metadata"]end;table.insert(batch_events,w)if#batch_events>=c["batch_size"]then local U=a.json:encode(batch_events)local V;local W={[":method"]="POST",[":path"]=c["send_event_endpoint"],[":authority"]="moesifprod",["content-type"]="application/json",["x-moesif-application-id"]=c["application_id"],["user-agent"]="envoy-plugin-moesif/0.1.7"}local X,Y=pcall(a.lib_deflate["CompressDeflate"],a.lib_deflate,U)if not X then V=U else W["content-encoding"]="deflate"V=Y end;local W,Z=r:httpCall("moesifprod",W,V,5000,false)if W[":status"]~=nil and(tonumber(W[":status"])==200 or tonumber(W[":status"])==201)then if o then r:logDebug("[moesif] Events sent successfully")end;batch_events={}end;if last_updated_time==nil or os.time()>last_updated_time+300 then local _,a0=r:httpCall("moesifprod",{[":method"]="GET",[":path"]=c["get_app_config_endpoint"],[":authority"]="moesifprod",["content-type"]="application/json",["x-moesif-application-id"]=c["application_id"]},"",5000)if a0~=nil and type(a0)=="string"and string.sub(a0,1,1)=="{"then app_config=a0;if o then r:logDebug("[moesif] successfully fetched the application configuration")end else if o then r:logDebug("[moesif] Error while fetching the application configuration, response headers and body - ")r:logDebug(dump(_))r:logDebug(dump(a0))end end;last_updated_time=os.time()end end else if o then r:logDebug("[moesif] Skipped Event due to sampling percentage - "..tostring(O).." and random number - "..tostring(N))end end else if o then r:logDebug("[moesif] Request is not captured as current context is nil, skipped logging event to Moesif.")end end else if o then r:logDebug("[moesif] !!!!! Please provide Moesif application Id !!!!!")end end end;return b
    moesif-core-helpers.lua: local a={}local b=require("moesif.core.base64")local c=require("moesif.core.json")local d=require("moesif.core.zzlib")function a.fetch_headers(e)local f={}for g,h in pairs(e)do f[g]=h end;return f end;function c:onDecodeOfHTMLError(i,j,k,l)return nil end;function c:onDecodeError(i,j,k,l)return nil end;local function m(n)return type(n)=="string"and string.sub(n,1,1)=="{"or string.sub(n,1,1)=="["end;local function o(n)return b.encode(n),'base64'end;local function p()if os.clock==nil then return string.format('%.2f',0.0*1000)else return tostring(os.clock()*1000)end end;function a.get_current_time_in_ms()local q=os.time(os.date("!*t"))return os.date("!%Y-%m-%dT%H:%M:%S.",q)..string.match(p(),"%d%.(%d+)")end;function a.fetch_raw_body(r,s)local n=r:body()if n~=nil and n~=''then local t=n:length()local u=n:getBytes(0,t)if s then r:logDebug("[moesif] Fetched Raw Body - "..tostring(u))end;return tostring(u)else if s then r:logDebug("[moesif] Fetched Raw Body is nil")end;return nil end end;function process_data(v,w)local x=nil;local y=nil;if m(v)then local z=c:decode(v)if z==nil then x,y=o(v)else if next(w)==nil then x,y=z,'json'else local A,B=pcall(mask_body,z,w)if not A then x,y=z,'json'else x,y=B,'json'end end end else x,y=o(v)end;return x,y end;function decompress_body(v,w)local x=nil;local y=nil;local A,C=pcall(d.gunzip,v)if not A then x,y=o(v)else if m(C)then x,y=process_data(C,w)else x,y=o(C)end end;return x,y end;function a.parse_body(f,v,w)local x=nil;local y=nil;if f["content-type"]~=nil and string.find(f["content-type"],"json")then x,y=process_data(v,w)elseif f["content-encoding"]~=nil and type(v)=="string"and string.find(f["content-encoding"],"gzip")then x,y=decompress_body(v,w)else x,y=o(v)end;return x,y end;function mask_body(n,D)if D==nil then return n end;if n==nil then return n end;for E,F in pairs(D)do if n[F]~=nil then n[F]=nil end;for G,H in next,n do if type(H)=="table"then mask_body(H,D)end end end;return n end;function a.mask_headers(f,w)local I=nil;for J,K in pairs(w)do w[J]=K:lower()end;local A,B=pcall(mask_body,f,w)if not A then I=f else I=B end;return I end;return a
    moesif-core-zzlib.lua: |-

      -- zzlib - zlib decompression in Lua

      -- Copyright (c) 2016 Francois Galea <fgalea at free.fr>
      -- This program is free software. It comes without any warranty, to
      -- the extent permitted by applicable law. You can redistribute it
      -- and/or modify it under the terms of the Do What The Fuck You Want
      -- To Public License, Version 2, as published by Sam Hocevar. See
      -- the COPYING file or http://www.wtfpl.net/ for more details.

      local _M = {}

      local bit = bit32 or bit
      local unpack = table.unpack or unpack

      local function bitstream_init(file)
        local bs = {
          file = file,  -- the open file handle
          buf = nil,    -- character buffer
          len = nil,    -- length of character buffer
          pos = 1,      -- position in char buffer
          b = 0,        -- bit buffer
          n = 0,        -- number of bits in buffer
        }
        -- get rid of n first bits
        function bs:flushb(n)
          self.n = self.n - n
          self.b = bit.rshift(self.b,n)
        end
        -- peek a number of n bits from stream
        function bs:peekb(n)
          while self.n < n do
            if self.pos > self.len then
              self.buf = self.file:read(4096)
              self.len = self.buf:len()
              self.pos = 1
            end
            self.b = self.b + bit.lshift(self.buf:byte(self.pos),self.n)
            self.pos = self.pos + 1
            self.n = self.n + 8
          end
          return bit.band(self.b,bit.lshift(1,n)-1)
        end
        -- get a number of n bits from stream
        function bs:getb(n)
          local ret = bs:peekb(n)
          self.n = self.n - n
          self.b = bit.rshift(self.b,n)
          return ret
        end
        -- get next variable-size of maximum size=n element from stream, according to Huffman table
        function bs:getv(hufftable,n)
          local e = hufftable[bs:peekb(n)]
          local len = bit.band(e,15)
          local ret = bit.rshift(e,4)
          self.n = self.n - len
          self.b = bit.rshift(self.b,len)
          return ret
        end
        function bs:close()
          if self.file then
            self.file:close()
          end
        end
        if type(file) == "string" then
          bs.file = nil
          bs.buf = file
        else
          bs.buf = file:read(4096)
        end
        bs.len = bs.buf:len()
        return bs
      end

      local function hufftable_create(depths)
        local nvalues = #depths
        local nbits = 1
        local bl_count = {}
        local next_code = {}
        for i=1,nvalues do
          local d = depths[i]
          if d > nbits then
            nbits = d
          end
          bl_count[d] = (bl_count[d] or 0) + 1
        end
        local table = {}
        local code = 0
        bl_count[0] = 0
        for i=1,nbits do
          code = (code + (bl_count[i-1] or 0)) * 2
          next_code[i] = code
        end
        for i=1,nvalues do
          local len = depths[i] or 0
          if len > 0 then
            local e = (i-1)*16 + len
            local code = next_code[len]
            local rcode = 0
            for j=1,len do
              rcode = rcode + bit.lshift(bit.band(1,bit.rshift(code,j-1)),len-j)
            end
            for j=0,2^nbits-1,2^len do
              table[j+rcode] = e
            end
            next_code[len] = next_code[len] + 1
          end
        end
        return table,nbits
      end

      local function inflate_block_loop(out,bs,nlit,ndist,littable,disttable)
        local lit
        repeat
          lit = bs:getv(littable,nlit)
          if lit < 256 then
            table.insert(out,lit)
          elseif lit > 256 then
            local nbits = 0
            local size = 3
            local dist = 1
            if lit < 265 then
              size = size + lit - 257
            elseif lit < 285 then
              nbits = bit.rshift(lit-261,2)
              size = size + bit.lshift(bit.band(lit-261,3)+4,nbits)
            else
              size = 258
            end
            if nbits > 0 then
              size = size + bs:getb(nbits)
            end
            local v = bs:getv(disttable,ndist)
            if v < 4 then
              dist = dist + v
            else
              nbits = bit.rshift(v-2,1)
              dist = dist + bit.lshift(bit.band(v,1)+2,nbits)
              dist = dist + bs:getb(nbits)
            end
            local p = #out-dist+1
            while size > 0 do
              table.insert(out,out[p])
              p = p + 1
              size = size - 1
            end
          end
        until lit == 256
      end

      local function inflate_block_dynamic(out,bs)
        local order = { 17, 18, 19, 1, 9, 8, 10, 7, 11, 6, 12, 5, 13, 4, 14, 3, 15, 2, 16 }
        local hlit = 257 + bs:getb(5)
        local hdist = 1 + bs:getb(5)
        local hclen = 4 + bs:getb(4)
        local depths = {}
        for i=1,hclen do
          local v = bs:getb(3)
          depths[order[i]] = v
        end
        for i=hclen+1,19 do
          depths[order[i]] = 0
        end
        local lengthtable,nlen = hufftable_create(depths)
        local i=1
        while i<=hlit+hdist do
          local v = bs:getv(lengthtable,nlen)
          if v < 16 then
            depths[i] = v
            i = i + 1
          elseif v < 19 then
            local nbt = {2,3,7}
            local nb = nbt[v-15]
            local c = 0
            local n = 3 + bs:getb(nb)
            if v == 16 then
              c = depths[i-1]
            elseif v == 18 then
              n = n + 8
            end
            for j=1,n do
              depths[i] = c
              i = i + 1
            end
          else
            error("wrong entry in depth table for literal/length alphabet: "..v);
          end
        end
        local litdepths = {} for i=1,hlit do table.insert(litdepths,depths[i]) end
        local littable,nlit = hufftable_create(litdepths)
        local distdepths = {} for i=hlit+1,#depths do table.insert(distdepths,depths[i]) end
        local disttable,ndist = hufftable_create(distdepths)
        inflate_block_loop(out,bs,nlit,ndist,littable,disttable)
      end

      local function inflate_block_static(out,bs)
        local cnt = { 144, 112, 24, 8 }
        local dpt = { 8, 9, 7, 8 }
        local depths = {}
        for i=1,4 do
          local d = dpt[i]
          for j=1,cnt[i] do
            table.insert(depths,d)
          end
        end
        local littable,nlit = hufftable_create(depths)
        depths = {}
        for i=1,32 do
          depths[i] = 5
        end
        local disttable,ndist = hufftable_create(depths)
        inflate_block_loop(out,bs,nlit,ndist,littable,disttable)
      end

      local function inflate_block_uncompressed(out,bs)
        bs:flushb(bit.band(bs.n,7))
        local len = bs:getb(16)
        if bs.n > 0 then
          error("Unexpected.. should be zero remaining bits in buffer.")
        end
        local nlen = bs:getb(16)
        if bit.bxor(len,nlen) ~= 65535 then
          error("LEN and NLEN don't match")
        end
        for i=bs.pos,bs.pos+len-1 do
          table.insert(out,bs.buf:byte(i,i))
        end
        bs.pos = bs.pos + len
      end

      local function arraytostr(array)
        local tmp = {}
        local size = #array
        local pos = 1
        local imax = 1
        while size > 0 do
          local bsize = size>=2048 and 2048 or size
          local s = string.char(unpack(array,pos,pos+bsize-1))
          pos = pos + bsize
          size = size - bsize
          local i = 1
          while tmp[i] do
            s = tmp[i]..s
            tmp[i] = nil
            i = i + 1
          end
          if i > imax then
            imax = i
          end
          tmp[i] = s
        end
        local str = ""
        for i=1,imax do
          if tmp[i] then
            str = tmp[i]..str
          end
        end
        return str
      end

      local function inflate_main(bs)
        local last,type
        local output = {}
        repeat
          local block
          last = bs:getb(1)
          type = bs:getb(2)
          if type == 0 then
            inflate_block_uncompressed(output,bs)
          elseif type == 1 then
            inflate_block_static(output,bs)
          elseif type == 2 then
            inflate_block_dynamic(output,bs)
          else
            error("unsupported block type")
          end
        until last == 1
        bs:flushb(bit.band(bs.n,7))
        return arraytostr(output)
      end

      local crc32_table
      local function crc32(s,crc)
        if not crc32_table then
          crc32_table = {}
          for i=0,255 do
            local r=i
            for j=1,8 do
              r = bit.bxor(bit.rshift(r,1),bit.band(0xedb88320,bit.bnot(bit.band(r,1)-1)))
            end
            crc32_table[i] = r
          end
        end
        crc = bit.bnot(crc or 0)
        for i=1,#s do
          local c = s:byte(i)
          crc = bit.bxor(crc32_table[bit.band(bit.bxor(c,crc),0xff)],bit.rshift(crc,8))
        end
        return bit.bnot(crc)
      end

      local function inflate_gzip(bs)
        local id1,id2,cm,flg = bs.buf:byte(1,4)
        if id1 ~= 31 or id2 ~= 139 then
          error("invalid gzip header")
        end
        if cm ~= 8 then
          error("only deflate format is supported")
        end
        bs.pos=11
        if bit.band(flg,4) ~= 0 then
          local xl1,xl2 = bs.buf.byte(bs.pos,bs.pos+1)
          local xlen = xl2*256+xl1
          bs.pos = bs.pos+xlen+2
        end
        if bit.band(flg,8) ~= 0 then
          local pos = bs.buf:find("\0",bs.pos)
          bs.pos = pos+1
        end
        if bit.band(flg,16) ~= 0 then
          local pos = bs.buf:find("\0",bs.pos)
          bs.pos = pos+1
        end
        if bit.band(flg,2) ~= 0 then
          -- TODO: check header CRC16
          bs.pos = bs.pos+2
        end
        local result = inflate_main(bs)
        local crc = bs:getb(8)+256*(bs:getb(8)+256*(bs:getb(8)+256*bs:getb(8)))
        bs:close()
        if crc ~= crc32(result) then
          error("checksum verification failed")
        end
        return result
      end

      -- compute Adler-32 checksum
      local function adler32(s)
        local s1 = 1
        local s2 = 0
        for i=1,#s do
          local c = s:byte(i)
          s1 = (s1+c)%65521
          s2 = (s2+s1)%65521
        end
        return s2*65536+s1
      end

      local function inflate_zlib(bs)
        local cmf = bs.buf:byte(1)
        local flg = bs.buf:byte(2)
        if (cmf*256+flg)%31 ~= 0 then
          error("zlib header check bits are incorrect")
        end
        if bit.band(cmf,15) ~= 8 then
          error("only deflate format is supported")
        end
        if bit.rshift(cmf,4) ~= 7 then
          error("unsupported window size")
        end
        if bit.band(flg,32) ~= 0 then
          error("preset dictionary not implemented")
        end
        bs.pos=3
        local result = inflate_main(bs)
        local adler = ((bs:getb(8)*256+bs:getb(8))*256+bs:getb(8))*256+bs:getb(8)
        bs:close()
        if adler ~= adler32(result) then
          error("checksum verification failed")
        end
        return result
      end

      function _M.gunzipf(filename)
        local file,err = io.open(filename,"rb")
        if not file then
          return nil,err
        end
        return inflate_gzip(bitstream_init(file))
      end

      function _M.gunzip(str)
        return inflate_gzip(bitstream_init(str))
      end

      function _M.inflate(str)
        return inflate_zlib(bitstream_init(str))
      end

      function string.int2le(str,pos)
        local a,b = str:byte(pos,pos+1)
        return b*256+a
      end

      function string.int4le(str,pos)
        local a,b,c,d = str:byte(pos,pos+3)
        return ((d*256+c)*256+b)*256+a
      end

      function _M.unzip(buf,filename)
        local p = 1
        local quit = false
        while not quit do
          local head = buf:int4le(p)
          if head == 0x04034b50 then
            -- local file header signature
            local flag = buf:int2le(p+6)
            local method = buf:int2le(p+8)
            local crc = buf:int4le(p+14)
            local csize = buf:int4le(p+18)
            local namelen = buf:int2le(p+26)
            local extlen = buf:int2le(p+28)
            local name = buf:sub(p+30,p+29+namelen)
            if bit.band(flag,1) ~= 0 then
              error("no support for encrypted files")
            elseif method ~= 8 and method ~= 0 then
              error("unsupported compression method")
            elseif bit.band(flag,8) ~= 0 then
              error("no support for the data descriptor record")
            end
            p = p+30+namelen+extlen
            if name == filename then
              local result
              if method == 0 then
                -- no compression
                result = buf:sub(p,p+csize-1)
              else
                -- DEFLATE compression
                local bs = bitstream_init(buf)
                bs.pos = p
                result = inflate_main(bs)
              end
              if crc ~= crc32(result) then
                error("checksum verification failed")
              end
              return result
            end
            p = p+csize
          else
            -- other header: end of the list of files
            quit = true
          end
        end
      end

      return _M
    moesif-core-json.lua: |-
      -- -*- coding: utf-8 -*-
        --
        -- Simple JSON encoding and decoding in pure Lua.
        --
        -- Copyright 2010-2017 Jeffrey Friedl
        -- http://regex.info/blog/
        -- Latest version: http://regex.info/blog/lua/json
        --
        -- This code is released under a Creative Commons CC-BY "Attribution" License:
        -- http://creativecommons.org/licenses/by/3.0/deed.en_US
        --
        -- It can be used for any purpose so long as:
        --    1) the copyright notice above is maintained
        --    2) the web-page links above are maintained
        --    3) the 'AUTHOR_NOTE' string below is maintained
        --
        local VERSION = '20170927.26' -- version history at end of file
        local AUTHOR_NOTE = "-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json) version 20170927.26 ]-"
        
        --
        -- The 'AUTHOR_NOTE' variable exists so that information about the source
        -- of the package is maintained even in compiled versions. It's also
        -- included in OBJDEF below mostly to quiet warnings about unused variables.
        --
        local OBJDEF = {
           VERSION      = VERSION,
           AUTHOR_NOTE  = AUTHOR_NOTE,
        }
        
        
        --
        -- Simple JSON encoding and decoding in pure Lua.
        -- JSON definition: http://www.json.org/
        --
        --
        --   JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines
        --
        --   local lua_value = JSON:decode(raw_json_text)
        --
        --   local raw_json_text    = JSON:encode(lua_table_or_value)
        --   local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability
        --
        --
        --
        -- DECODING (from a JSON string to a Lua table)
        --
        --
        --   JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines
        --
        --   local lua_value = JSON:decode(raw_json_text)
        --
        --   If the JSON text is for an object or an array, e.g.
        --     { "what": "books", "count": 3 }
        --   or
        --     [ "Larry", "Curly", "Moe" ]
        --
        --   the result is a Lua table, e.g.
        --     { what = "books", count = 3 }
        --   or
        --     { "Larry", "Curly", "Moe" }
        --
        --
        --   The encode and decode routines accept an optional second argument,
        --   "etc", which is not used during encoding or decoding, but upon error
        --   is passed along to error handlers. It can be of any type (including nil).
        --
        --
        --
        -- ERROR HANDLING DURING DECODE
        --
        --   With most errors during decoding, this code calls
        --
        --      JSON:onDecodeError(message, text, location, etc)
        --
        --   with a message about the error, and if known, the JSON text being
        --   parsed and the byte count where the problem was discovered. You can
        --   replace the default JSON:onDecodeError() with your own function.
        --
        --   The default onDecodeError() merely augments the message with data
        --   about the text and the location (and, an 'etc' argument had been
        --   provided to decode(), its value is tacked onto the message as well),
        --   and then calls JSON.assert(), which itself defaults to Lua's built-in
        --   assert(), and can also be overridden.
        --
        --   For example, in an Adobe Lightroom plugin, you might use something like
        --
        --          function JSON:onDecodeError(message, text, location, etc)
        --             LrErrors.throwUserError("Internal Error: invalid JSON data")
        --          end
        --
        --   or even just
        --
        --          function JSON.assert(message)
        --             LrErrors.throwUserError("Internal Error: " .. message)
        --          end
        --
        --   If JSON:decode() is passed a nil, this is called instead:
        --
        --      JSON:onDecodeOfNilError(message, nil, nil, etc)
        --
        --   and if JSON:decode() is passed HTML instead of JSON, this is called:
        --
        --      JSON:onDecodeOfHTMLError(message, text, nil, etc)
        --
        --   The use of the 'etc' argument allows stronger coordination between
        --   decoding and error reporting, especially when you provide your own
        --   error-handling routines. Continuing with the the Adobe Lightroom
        --   plugin example:
        --
        --          function JSON:onDecodeError(message, text, location, etc)
        --             local note = "Internal Error: invalid JSON data"
        --             if type(etc) = 'table' and etc.photo then
        --                note = note .. " while processing for " .. etc.photo:getFormattedMetadata('fileName')
        --             end
        --             LrErrors.throwUserError(note)
        --          end
        --
        --            :
        --            :
        --
        --          for i, photo in ipairs(photosToProcess) do
        --               :             
        --               :             
        --               local data = JSON:decode(someJsonText, { photo = photo })
        --               :             
        --               :             
        --          end
        --
        --
        --
        --   If the JSON text passed to decode() has trailing garbage (e.g. as with the JSON "[123]xyzzy"),
        --   the method
        --
        --       JSON:onTrailingGarbage(json_text, location, parsed_value, etc)
        --
        --   is invoked, where:
        --
        --       'json_text' is the original JSON text being parsed,
        --       'location' is the count of bytes into 'json_text' where the garbage starts (6 in the example),
        --       'parsed_value' is the Lua result of what was successfully parsed ({123} in the example),
        --       'etc' is as above.
        --
        --   If JSON:onTrailingGarbage() does not abort, it should return the value decode() should return,
        --   or nil + an error message.
        --
        --     local new_value, error_message = JSON:onTrailingGarbage()
        --
        --   The default JSON:onTrailingGarbage() simply invokes JSON:onDecodeError("trailing garbage"...),
        --   but you can have this package ignore trailing garbage via
        --
        --      function JSON:onTrailingGarbage(json_text, location, parsed_value, etc)
        --         return parsed_value
        --      end
        --
        --
        -- DECODING AND STRICT TYPES
        --
        --   Because both JSON objects and JSON arrays are converted to Lua tables,
        --   it's not normally possible to tell which original JSON type a
        --   particular Lua table was derived from, or guarantee decode-encode
        --   round-trip equivalency.
        --
        --   However, if you enable strictTypes, e.g.
        --
        --      JSON = assert(loadfile "JSON.lua")() --load the routines
        --      JSON.strictTypes = true
        --
        --   then the Lua table resulting from the decoding of a JSON object or
        --   JSON array is marked via Lua metatable, so that when re-encoded with
        --   JSON:encode() it ends up as the appropriate JSON type.
        --
        --   (This is not the default because other routines may not work well with
        --   tables that have a metatable set, for example, Lightroom API calls.)
        --
        --
        -- ENCODING (from a lua table to a JSON string)
        --
        --   JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines
        --
        --   local raw_json_text    = JSON:encode(lua_table_or_value)
        --   local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability
        --   local custom_pretty    = JSON:encode(lua_table_or_value, etc, { pretty = true, indent = "|  ", align_keys = false })
        --
        --   On error during encoding, this code calls:
        --
        --     JSON:onEncodeError(message, etc)
        --
        --   which you can override in your local JSON object. Also see "HANDLING UNSUPPORTED VALUE TYPES" below.
        --
        --   The 'etc' in the error call is the second argument to encode() and encode_pretty(), or nil if it wasn't provided.
        --
        --
        --
        --
        -- ENCODING OPTIONS
        --
        --   An optional third argument, a table of options, can be provided to encode().
        --
        --       encode_options =  {
        --           -- options for making "pretty" human-readable JSON (see "PRETTY-PRINTING" below)
        --           pretty         = true,   -- turn pretty formatting on
        --           indent         = "   ",  -- use this indent for each level of an array/object
        --           align_keys     = false,  -- if true, align the keys in a way that sounds like it should be nice, but is actually ugly
        --           array_newline  = false,  -- if true, array elements become one to a line rather than inline
        --           
        --           -- other output-related options
        --           null           = "\0",   -- see "ENCODING JSON NULL VALUES" below
        --           stringsAreUtf8 = false,  -- see "HANDLING UNICODE LINE AND PARAGRAPH SEPARATORS FOR JAVA" below
        --       }
        --  
        --       json_string = JSON:encode(mytable, etc, encode_options)
        --
        --
        --
        -- For reference, the defaults are:
        --
        --           pretty         = false
        --           null           = nil,
        --           stringsAreUtf8 = false,
        --
        --
        --
        -- PRETTY-PRINTING
        --
        --   Enabling the 'pretty' encode option helps generate human-readable JSON.
        --
        --     pretty = JSON:encode(val, etc, {
        --                                       pretty = true,
        --                                       indent = "   ",
        --                                       align_keys = false,
        --                                     })
        --
        --   encode_pretty() is also provided: it's identical to encode() except
        --   that encode_pretty() provides a default options table if none given in the call:
        --
        --       { pretty = true, indent = "  ", align_keys = false, array_newline = false }
        --
        --   For example, if
        --
        --      JSON:encode(data)
        --
        --   produces:
        --
        --      {"city":"Kyoto","climate":{"avg_temp":16,"humidity":"high","snowfall":"minimal"},"country":"Japan","wards":11}
        --
        --   then
        --
        --      JSON:encode_pretty(data)
        --
        --   produces:
        --
        --      {
        --        "city": "Kyoto",
        --        "climate": {
        --          "avg_temp": 16,
        --          "humidity": "high",
        --          "snowfall": "minimal"
        --        },
        --        "country": "Japan",
        --        "wards": 11
        --      }
        --
        --   The following lines all return identical strings:
        --       JSON:encode_pretty(data)
        --       JSON:encode_pretty(data, nil, { pretty = true, indent = "  ", align_keys = false, array_newline = false})
        --       JSON:encode_pretty(data, nil, { pretty = true, indent = "  " })
        --       JSON:encode       (data, nil, { pretty = true, indent = "  " })
        --
        --   An example of setting your own indent string:
        --
        --     JSON:encode_pretty(data, nil, { pretty = true, indent = "|    " })
        --
        --   produces:
        --
        --      {
        --      |    "city": "Kyoto",
        --      |    "climate": {
        --      |    |    "avg_temp": 16,
        --      |    |    "humidity": "high",
        --      |    |    "snowfall": "minimal"
        --      |    },
        --      |    "country": "Japan",
        --      |    "wards": 11
        --      }
        --
        --   An example of setting align_keys to true:
        --
        --     JSON:encode_pretty(data, nil, { pretty = true, indent = "  ", align_keys = true })
        --  
        --   produces:
        --   
        --      {
        --           "city": "Kyoto",
        --        "climate": {
        --                     "avg_temp": 16,
        --                     "humidity": "high",
        --                     "snowfall": "minimal"
        --                   },
        --        "country": "Japan",
        --          "wards": 11
        --      }
        --
        --   which I must admit is kinda ugly, sorry. This was the default for
        --   encode_pretty() prior to version 20141223.14.
        --
        --
        --  HANDLING UNICODE LINE AND PARAGRAPH SEPARATORS FOR JAVA
        --
        --    If the 'stringsAreUtf8' encode option is set to true, consider Lua strings not as a sequence of bytes,
        --    but as a sequence of UTF-8 characters.
        --
        --    Currently, the only practical effect of setting this option is that Unicode LINE and PARAGRAPH
        --    separators, if found in a string, are encoded with a JSON escape instead of being dumped as is.
        --    The JSON is valid either way, but encoding this way, apparently, allows the resulting JSON
        --    to also be valid Java.
        --
        --  AMBIGUOUS SITUATIONS DURING THE ENCODING
        --
        --   During the encode, if a Lua table being encoded contains both string
        --   and numeric keys, it fits neither JSON's idea of an object, nor its
        --   idea of an array. To get around this, when any string key exists (or
        --   when non-positive numeric keys exist), numeric keys are converted to
        --   strings.
        --
        --   For example, 
        --     JSON:encode({ "one", "two", "three", SOMESTRING = "some string" }))
        --   produces the JSON object
        --     {"1":"one","2":"two","3":"three","SOMESTRING":"some string"}
        --
        --   To prohibit this conversion and instead make it an error condition, set
        --      JSON.noKeyConversion = true
        --
        --
        -- ENCODING JSON NULL VALUES
        --
        --   Lua tables completely omit keys whose value is nil, so without special handling there's
        --   no way to represent JSON object's null value in a Lua table.  For example
        --      JSON:encode({ username = "admin", password = nil })
        --
        --   produces:
        --
        --      {"username":"admin"}
        --
        --   In order to actually produce
        --
        --      {"username":"admin", "password":null}
        --
        
        --   one can include a string value for a "null" field in the options table passed to encode().... 
        --   any Lua table entry with that value becomes null in the JSON output:
        --
        --      JSON:encode({ username = "admin", password = "xyzzy" }, -- First arg is the Lua table to encode as JSON.
        --                  nil,                                        -- Second arg is the 'etc' value, ignored here
        --                  { null = "xyzzy" })                         -- Third arg is th options table
        --
        --   produces:
        --
        --      {"username":"admin", "password":null}
        --
        --   Just be sure to use a string that is otherwise unlikely to appear in your data.
        --   The string "\0" (a string with one null byte) may well be appropriate for many applications.
        --
        --   The "null" options also applies to Lua tables that become JSON arrays.
        --      JSON:encode({ "one", "two", nil, nil })
        --
        --   produces
        --
        --      ["one","two"]
        --
        --   while
        --
        --      NullPlaceholder = "\0"
        --      encode_options = { null = NullPlaceholder }
        --      JSON:encode({ "one", "two", NullPlaceholder, NullPlaceholder}, nil, encode_options)
        --   produces
        --
        --      ["one","two",null,null]
        --
        --
        --
        -- HANDLING LARGE AND/OR PRECISE NUMBERS
        --
        --
        --   Without special handling, numbers in JSON can lose precision in Lua.
        --   For example:
        --   
        --      T = JSON:decode('{  "small":12345, "big":12345678901234567890123456789, "precise":9876.67890123456789012345  }')
        --
        --      print("small:   ",  type(T.small),    T.small)
        --      print("big:     ",  type(T.big),      T.big)
        --      print("precise: ",  type(T.precise),  T.precise)
        --   
        --   produces
        --   
        --      small:          number  12345
        --      big:            number  1.2345678901235e+28
        --      precise:        number  9876.6789012346
        --
        --   Precision is lost with both 'big' and 'precise'.
        --
        --   This package offers ways to try to handle this better (for some definitions of "better")...
        --
        --   The most precise method is by setting the global:
        --   
        --      JSON.decodeNumbersAsObjects = true
        --   
        --   When this is set, numeric JSON data is encoded into Lua in a form that preserves the exact
        --   JSON numeric presentation when re-encoded back out to JSON, or accessed in Lua as a string.
        --
        --   This is done by encoding the numeric data with a Lua table/metatable that returns
        --   the possibly-imprecise numeric form when accessed numerically, but the original precise
        --   representation when accessed as a string.
        --
        --   Consider the example above, with this option turned on:
        --
        --      JSON.decodeNumbersAsObjects = true
        --      
        --      T = JSON:decode('{  "small":12345, "big":12345678901234567890123456789, "precise":9876.67890123456789012345  }')
        --
        --      print("small:   ",  type(T.small),    T.small)
        --      print("big:     ",  type(T.big),      T.big)
        --      print("precise: ",  type(T.precise),  T.precise)
        --   
        --   This now produces:
        --   
        --      small:          table   12345
        --      big:            table   12345678901234567890123456789
        --      precise:        table   9876.67890123456789012345
        --   
        --   However, within Lua you can still use the values (e.g. T.precise in the example above) in numeric
        --   contexts. In such cases you'll get the possibly-imprecise numeric version, but in string contexts
        --   and when the data finds its way to this package's encode() function, the original full-precision
        --   representation is used.
        --
        --   You can force access to the string or numeric version via
        --        JSON:forceString()
        --        JSON:forceNumber()
        --   For example,
        --        local probably_okay = JSON:forceNumber(T.small) -- 'probably_okay' is a number
        --
        --   Code the inspects the JSON-turned-Lua data using type() can run into troubles because what used to
        --   be a number can now be a table (e.g. as the small/big/precise example above shows). Update these
        --   situations to use JSON:isNumber(item), which returns nil if the item is neither a number nor one
        --   of these number objects. If it is either, it returns the number itself. For completeness there's
        --   also JSON:isString(item).
        --
        --   If you want to try to avoid the hassles of this "number as an object" kludge for all but really
        --   big numbers, you can set JSON.decodeNumbersAsObjects and then also set one or both of
        --            JSON:decodeIntegerObjectificationLength
        --            JSON:decodeDecimalObjectificationLength
        --   They refer to the length of the part of the number before and after a decimal point. If they are
        --   set and their part is at least that number of digits, objectification occurs. If both are set,
        --   objectification occurs when either length is met.
        --
        --   -----------------------
        --
        --   Even without using the JSON.decodeNumbersAsObjects option, you can encode numbers in your Lua
        --   table that retain high precision upon encoding to JSON, by using the JSON:asNumber() function:
        --
        --      T = {
        --         imprecise =                123456789123456789.123456789123456789,
        --         precise   = JSON:asNumber("123456789123456789.123456789123456789")
        --      }
        --
        --      print(JSON:encode_pretty(T))
        --
        --   This produces:
        --
        --      { 
        --         "precise": 123456789123456789.123456789123456789,
        --         "imprecise": 1.2345678912346e+17
        --      }
        --
        --
        --   -----------------------
        --
        --   A different way to handle big/precise JSON numbers is to have decode() merely return the exact
        --   string representation of the number instead of the number itself. This approach might be useful
        --   when the numbers are merely some kind of opaque object identifier and you want to work with them
        --   in Lua as strings anyway.
        --   
        --   This approach is enabled by setting
        --
        --      JSON.decodeIntegerStringificationLength = 10
        --
        --   The value is the number of digits (of the integer part of the number) at which to stringify numbers.
        --   NOTE: this setting is ignored if JSON.decodeNumbersAsObjects is true, as that takes precedence.
        --
        --   Consider our previous example with this option set to 10:
        --
        --      JSON.decodeIntegerStringificationLength = 10
        --      
        --      T = JSON:decode('{  "small":12345, "big":12345678901234567890123456789, "precise":9876.67890123456789012345  }')
        --
        --      print("small:   ",  type(T.small),    T.small)
        --      print("big:     ",  type(T.big),      T.big)
        --      print("precise: ",  type(T.precise),  T.precise)
        --
        --   This produces:
        --
        --      small:          number  12345
        --      big:            string  12345678901234567890123456789
        --      precise:        number  9876.6789012346
        --
        --   The long integer of the 'big' field is at least JSON.decodeIntegerStringificationLength digits
        --   in length, so it's converted not to a Lua integer but to a Lua string. Using a value of 0 or 1 ensures
        --   that all JSON numeric data becomes strings in Lua.
        --
        --   Note that unlike
        --      JSON.decodeNumbersAsObjects = true
        --   this stringification is simple and unintelligent: the JSON number simply becomes a Lua string, and that's the end of it.
        --   If the string is then converted back to JSON, it's still a string. After running the code above, adding
        --      print(JSON:encode(T))
        --   produces
        --      {"big":"12345678901234567890123456789","precise":9876.6789012346,"small":12345}
        --   which is unlikely to be desired.
        --
        --   There's a comparable option for the length of the decimal part of a number:
        --
        --      JSON.decodeDecimalStringificationLength
        --
        --   This can be used alone or in conjunction with
        --
        --      JSON.decodeIntegerStringificationLength
        --
        --   to trip stringification on precise numbers with at least JSON.decodeIntegerStringificationLength digits after
        --   the decimal point. (Both are ignored if JSON.decodeNumbersAsObjects is true.)
        --
        --   This example:
        --
        --      JSON.decodeIntegerStringificationLength = 10
        --      JSON.decodeDecimalStringificationLength =  5
        --
        --      T = JSON:decode('{  "small":12345, "big":12345678901234567890123456789, "precise":9876.67890123456789012345  }')
        --      
        --      print("small:   ",  type(T.small),    T.small)
        --      print("big:     ",  type(T.big),      T.big)
        --      print("precise: ",  type(T.precise),  T.precise)
        --
        --  produces:
        --
        --      small:          number  12345
        --      big:            string  12345678901234567890123456789
        --      precise:        string  9876.67890123456789012345
        --
        --
        --  HANDLING UNSUPPORTED VALUE TYPES
        --
        --   Among the encoding errors that might be raised is an attempt to convert a table value that has a type
        --   that this package hasn't accounted for: a function, userdata, or a thread. You can handle these types as table
        --   values (but not as table keys) if you supply a JSON:unsupportedTypeEncoder() method along the lines of the
        --   following example:
        --        
        --        function JSON:unsupportedTypeEncoder(value_of_unsupported_type)
        --           if type(value_of_unsupported_type) == 'function' then
        --              return "a function value"
        --           else
        --              return nil
        --           end
        --        end
        --        
        --   Your unsupportedTypeEncoder() method is actually called with a bunch of arguments:
        --
        --      self:unsupportedTypeEncoder(value, parents, etc, options, indent, for_key)
        --
        --   The 'value' is the function, thread, or userdata to be converted to JSON.
        --
        --   The 'etc' and 'options' arguments are those passed to the original encode(). The other arguments are
        --   probably of little interest; see the source code. (Note that 'for_key' is never true, as this function
        --   is invoked only on table values; table keys of these types still trigger the onEncodeError method.)
        --
        --   If your unsupportedTypeEncoder() method returns a string, it's inserted into the JSON as is.
        --   If it returns nil plus an error message, that error message is passed through to an onEncodeError invocation.
        --   If it returns only nil, processing falls through to a default onEncodeError invocation.
        --
        --   If you want to handle everything in a simple way:
        --
        --        function JSON:unsupportedTypeEncoder(value)
        --           return tostring(value)
        --        end
        --
        --
        -- SUMMARY OF METHODS YOU CAN OVERRIDE IN YOUR LOCAL LUA JSON OBJECT
        --
        --    assert
        --    onDecodeError
        --    onDecodeOfNilError
        --    onDecodeOfHTMLError
        --    onTrailingGarbage
        --    onEncodeError
        --    unsupportedTypeEncoder
        --
        --  If you want to create a separate Lua JSON object with its own error handlers,
        --  you can reload JSON.lua or use the :new() method.
        --
        ---------------------------------------------------------------------------
        
        local default_pretty_indent  = "  "
        local default_pretty_options = { pretty = true, indent = default_pretty_indent, align_keys = false, array_newline = false }
        
        local isArray  = { __tostring = function() return "JSON array"         end }  isArray.__index  = isArray
        local isObject = { __tostring = function() return "JSON object"        end }  isObject.__index = isObject
        
        function OBJDEF:newArray(tbl)
           return setmetatable(tbl or {}, isArray)
        end
        
        function OBJDEF:newObject(tbl)
           return setmetatable(tbl or {}, isObject)
        end
        
        
        
        
        local function getnum(op)
           return type(op) == 'number' and op or op.N
        end
        
        local isNumber = {
           __tostring = function(T)  return T.S        end,
           __unm      = function(op) return getnum(op) end,
        
           __concat   = function(op1, op2) return tostring(op1) .. tostring(op2) end,
           __add      = function(op1, op2) return getnum(op1)   +   getnum(op2)  end,
           __sub      = function(op1, op2) return getnum(op1)   -   getnum(op2)  end,
           __mul      = function(op1, op2) return getnum(op1)   *   getnum(op2)  end,
           __div      = function(op1, op2) return getnum(op1)   /   getnum(op2)  end,
           __mod      = function(op1, op2) return getnum(op1)   %   getnum(op2)  end,
           __pow      = function(op1, op2) return getnum(op1)   ^   getnum(op2)  end,
           __lt       = function(op1, op2) return getnum(op1)   <   getnum(op2)  end,
           __eq       = function(op1, op2) return getnum(op1)   ==  getnum(op2)  end,
           __le       = function(op1, op2) return getnum(op1)   <=  getnum(op2)  end,
        }
        isNumber.__index = isNumber
        
        function OBJDEF:asNumber(item)
        
           if getmetatable(item) == isNumber then
              -- it's already a JSON number object.
              return item
           elseif type(item) == 'table' and type(item.S) == 'string' and type(item.N) == 'number' then
              -- it's a number-object table that lost its metatable, so give it one
              return setmetatable(item, isNumber)
           else
              -- the normal situation... given a number or a string representation of a number....
              local holder = {
                 S = tostring(item), -- S is the representation of the number as a string, which remains precise
                 N = tonumber(item), -- N is the number as a Lua number.
              }
              return setmetatable(holder, isNumber)
           end
        end
        
        --
        -- Given an item that might be a normal string or number, or might be an 'isNumber' object defined above,
        -- return the string version. This shouldn't be needed often because the 'isNumber' object should autoconvert
        -- to a string in most cases, but it's here to allow it to be forced when needed.
        --
        function OBJDEF:forceString(item)
           if type(item) == 'table' and type(item.S) == 'string' then
              return item.S
           else
              return tostring(item)
           end
        end
        
        --
        -- Given an item that might be a normal string or number, or might be an 'isNumber' object defined above,
        -- return the numeric version.
        --
        function OBJDEF:forceNumber(item)
           if type(item) == 'table' and type(item.N) == 'number' then
              return item.N
           else
              return tonumber(item)
           end
        end
        
        --
        -- If the given item is a number, return it. Otherwise, return nil.
        -- This, this can be used both in a conditional and to access the number when you're not sure its form.
        --
        function OBJDEF:isNumber(item)
           if type(item) == 'number' then
              return item
           elseif type(item) == 'table' and type(item.N) == 'number' then
              return item.N
           else
              return nil
           end
        end
        
        function OBJDEF:isString(item)
           if type(item) == 'string' then
              return item
           elseif type(item) == 'table' and type(item.S) == 'string' then
              return item.S
           else
              return nil
           end
        end
        
        
        local function unicode_codepoint_as_utf8(codepoint)
           --
           -- codepoint is a number
           --
           if codepoint <= 127 then
              return string.char(codepoint)
        
           elseif codepoint <= 2047 then
              --
              -- 110yyyxx 10xxxxxx         <-- useful notation from http://en.wikipedia.org/wiki/Utf8
              --
              local highpart = math.floor(codepoint / 0x40)
              local lowpart  = codepoint - (0x40 * highpart)
              return string.char(0xC0 + highpart,
                                 0x80 + lowpart)
        
           elseif codepoint <= 65535 then
              --
              -- 1110yyyy 10yyyyxx 10xxxxxx
              --
              local highpart  = math.floor(codepoint / 0x1000)
              local remainder = codepoint - 0x1000 * highpart
              local midpart   = math.floor(remainder / 0x40)
              local lowpart   = remainder - 0x40 * midpart
        
              highpart = 0xE0 + highpart
              midpart  = 0x80 + midpart
              lowpart  = 0x80 + lowpart
        
              --
              -- Check for an invalid character (thanks Andy R. at Adobe).
              -- See table 3.7, page 93, in http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf#G28070
              --
              if ( highpart == 0xE0 and midpart < 0xA0 ) or
                 ( highpart == 0xED and midpart > 0x9F ) or
                 ( highpart == 0xF0 and midpart < 0x90 ) or
                 ( highpart == 0xF4 and midpart > 0x8F )
              then
                 return "?"
              else
                 return string.char(highpart,
                                    midpart,
                                    lowpart)
              end
        
           else
              --
              -- 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx
              --
              local highpart  = math.floor(codepoint / 0x40000)
              local remainder = codepoint - 0x40000 * highpart
              local midA      = math.floor(remainder / 0x1000)
              remainder       = remainder - 0x1000 * midA
              local midB      = math.floor(remainder / 0x40)
              local lowpart   = remainder - 0x40 * midB
        
              return string.char(0xF0 + highpart,
                                 0x80 + midA,
                                 0x80 + midB,
                                 0x80 + lowpart)
           end
        end
        
        function OBJDEF:onDecodeError(message, text, location, etc)
           if text then
              if location then
                 message = string.format("%s at byte %d of: %s", message, location, text)
              else
                 message = string.format("%s: %s", message, text)
              end
           end
        
           if etc ~= nil then
              message = message .. " (" .. OBJDEF:encode(etc) .. ")"
           end
        
           if self.assert then
              self.assert(false, message)
           else
              assert(false, message)
           end
        end
        
        function OBJDEF:onTrailingGarbage(json_text, location, parsed_value, etc)
           return self:onDecodeError("trailing garbage", json_text, location, etc)
        end
        
        OBJDEF.onDecodeOfNilError  = OBJDEF.onDecodeError
        OBJDEF.onDecodeOfHTMLError = OBJDEF.onDecodeError
        
        function OBJDEF:onEncodeError(message, etc)
           if etc ~= nil then
              message = message .. " (" .. OBJDEF:encode(etc) .. ")"
           end
        
           if self.assert then
              self.assert(false, message)
           else
              assert(false, message)
           end
        end
        
        local function grok_number(self, text, start, options)
           --
           -- Grab the integer part
           --
           local integer_part = text:match('^-?[1-9]%d*', start)
                             or text:match("^-?0",        start)
        
           if not integer_part then
              self:onDecodeError("expected number", text, start, options.etc)
              return nil, start -- in case the error method doesn't abort, return something sensible
           end
        
           local i = start + integer_part:len()
        
           --
           -- Grab an optional decimal part
           --
           local decimal_part = text:match('^%.%d+', i) or ""
        
           i = i + decimal_part:len()
        
           --
           -- Grab an optional exponential part
           --
           local exponent_part = text:match('^[eE][-+]?%d+', i) or ""
        
           i = i + exponent_part:len()
        
           local full_number_text = integer_part .. decimal_part .. exponent_part
        
           if options.decodeNumbersAsObjects then
        
              local objectify = false
        
              if not options.decodeIntegerObjectificationLength and not options.decodeDecimalObjectificationLength then
                 -- no options, so objectify
                 objectify = true
        
              elseif (options.decodeIntegerObjectificationLength
                  and
                 (integer_part:len() >= options.decodeIntegerObjectificationLength or exponent_part:len() > 0))
        
                  or
                 (options.decodeDecimalObjectificationLength 
                  and
                  (decimal_part:len() >= options.decodeDecimalObjectificationLength  or exponent_part:len() > 0))
              then
                 -- have options and they are triggered, so objectify
                 objectify = true
              end
        
              if objectify then
                 return OBJDEF:asNumber(full_number_text), i
              end
              -- else, fall through to try to return as a straight-up number
        
           else
        
              -- Not always decoding numbers as objects, so perhaps encode as strings?
        
              --
              -- If we're told to stringify only under certain conditions, so do.
              -- We punt a bit when there's an exponent by just stringifying no matter what.
              -- I suppose we should really look to see whether the exponent is actually big enough one
              -- way or the other to trip stringification, but I'll be lazy about it until someone asks.
              --
              if (options.decodeIntegerStringificationLength
                  and
                 (integer_part:len() >= options.decodeIntegerStringificationLength or exponent_part:len() > 0))
        
                  or
        
                 (options.decodeDecimalStringificationLength 
                  and
                  (decimal_part:len() >= options.decodeDecimalStringificationLength or exponent_part:len() > 0))
              then
                 return full_number_text, i -- this returns the exact string representation seen in the original JSON
              end
        
           end
        
        
           local as_number = tonumber(full_number_text)
        
           if not as_number then
              self:onDecodeError("bad number", text, start, options.etc)
              return nil, start -- in case the error method doesn't abort, return something sensible
           end
        
           return as_number, i
        end
        
        
        local function grok_string(self, text, start, options)
        
           if text:sub(start,start) ~= '"' then
              self:onDecodeError("expected string's opening quote", text, start, options.etc)
              return nil, start -- in case the error method doesn't abort, return something sensible
           end
        
           local i = start + 1 -- +1 to bypass the initial quote
           local text_len = text:len()
           local VALUE = ""
           while i <= text_len do
              local c = text:sub(i,i)
              if c == '"' then
                 return VALUE, i + 1
              end
              if c ~= '\\' then
                 VALUE = VALUE .. c
                 i = i + 1
              elseif text:match('^\\b', i) then
                 VALUE = VALUE .. "\b"
                 i = i + 2
              elseif text:match('^\\f', i) then
                 VALUE = VALUE .. "\f"
                 i = i + 2
              elseif text:match('^\\n', i) then
                 VALUE = VALUE .. "\n"
                 i = i + 2
              elseif text:match('^\\r', i) then
                 VALUE = VALUE .. "\r"
                 i = i + 2
              elseif text:match('^\\t', i) then
                 VALUE = VALUE .. "\t"
                 i = i + 2
              else
                 local hex = text:match('^\\u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i)
                 if hex then
                    i = i + 6 -- bypass what we just read
        
                    -- We have a Unicode codepoint. It could be standalone, or if in the proper range and
                    -- followed by another in a specific range, it'll be a two-code surrogate pair.
                    local codepoint = tonumber(hex, 16)
                    if codepoint >= 0xD800 and codepoint <= 0xDBFF then
                       -- it's a hi surrogate... see whether we have a following low
                       local lo_surrogate = text:match('^\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i)
                       if lo_surrogate then
                          i = i + 6 -- bypass the low surrogate we just read
                          codepoint = 0x2400 + (codepoint - 0xD800) * 0x400 + tonumber(lo_surrogate, 16)
                       else
                          -- not a proper low, so we'll just leave the first codepoint as is and spit it out.
                       end
                    end
                    VALUE = VALUE .. unicode_codepoint_as_utf8(codepoint)
        
                 else
        
                    -- just pass through what's escaped
                    VALUE = VALUE .. text:match('^\\(.)', i)
                    i = i + 2
                 end
              end
           end
        
           self:onDecodeError("unclosed string", text, start, options.etc)
           return nil, start -- in case the error method doesn't abort, return something sensible
        end
        
        local function skip_whitespace(text, start)
        
           local _, match_end = text:find("^[ \n\r\t]+", start) -- [http://www.ietf.org/rfc/rfc4627.txt] Section 2
           if match_end then
              return match_end + 1
           else
              return start
           end
        end
        
        local grok_one -- assigned later
        
        local function grok_object(self, text, start, options)
        
           if text:sub(start,start) ~= '{' then
              self:onDecodeError("expected '{'", text, start, options.etc)
              return nil, start -- in case the error method doesn't abort, return something sensible
           end
        
           local i = skip_whitespace(text, start + 1) -- +1 to skip the '{'
        
           local VALUE = self.strictTypes and self:newObject { } or { }
        
           if text:sub(i,i) == '}' then
              return VALUE, i + 1
           end
           local text_len = text:len()
           while i <= text_len do
              local key, new_i = grok_string(self, text, i, options)
        
              i = skip_whitespace(text, new_i)
        
              if text:sub(i, i) ~= ':' then
                 self:onDecodeError("expected colon", text, i, options.etc)
                 return nil, i -- in case the error method doesn't abort, return something sensible
              end
        
              i = skip_whitespace(text, i + 1)
        
              local new_val, new_i = grok_one(self, text, i, options)
        
              VALUE[key] = new_val
        
              --
              -- Expect now either '}' to end things, or a ',' to allow us to continue.
              --
              i = skip_whitespace(text, new_i)
        
              local c = text:sub(i,i)
        
              if c == '}' then
                 return VALUE, i + 1
              end
        
              if text:sub(i, i) ~= ',' then
                 self:onDecodeError("expected comma or '}'", text, i, options.etc)
                 return nil, i -- in case the error method doesn't abort, return something sensible
              end
        
              i = skip_whitespace(text, i + 1)
           end
        
           self:onDecodeError("unclosed '{'", text, start, options.etc)
           return nil, start -- in case the error method doesn't abort, return something sensible
        end
        
        local function grok_array(self, text, start, options)
           if text:sub(start,start) ~= '[' then
              self:onDecodeError("expected '['", text, start, options.etc)
              return nil, start -- in case the error method doesn't abort, return something sensible
           end
        
           local i = skip_whitespace(text, start + 1) -- +1 to skip the '['
           local VALUE = self.strictTypes and self:newArray { } or { }
           if text:sub(i,i) == ']' then
              return VALUE, i + 1
           end
        
           local VALUE_INDEX = 1
        
           local text_len = text:len()
           while i <= text_len do
              local val, new_i = grok_one(self, text, i, options)
        
              -- can't table.insert(VALUE, val) here because it's a no-op if val is nil
              VALUE[VALUE_INDEX] = val
              VALUE_INDEX = VALUE_INDEX + 1
        
              i = skip_whitespace(text, new_i)
        
              --
              -- Expect now either ']' to end things, or a ',' to allow us to continue.
              --
              local c = text:sub(i,i)
              if c == ']' then
                 return VALUE, i + 1
              end
              if text:sub(i, i) ~= ',' then
                 self:onDecodeError("expected comma or ']'", text, i, options.etc)
                 return nil, i -- in case the error method doesn't abort, return something sensible
              end
              i = skip_whitespace(text, i + 1)
           end
           self:onDecodeError("unclosed '['", text, start, options.etc)
           return nil, i -- in case the error method doesn't abort, return something sensible
        end
        
        
        grok_one = function(self, text, start, options)
           -- Skip any whitespace
           start = skip_whitespace(text, start)
        
           if start > text:len() then
              self:onDecodeError("unexpected end of string", text, nil, options.etc)
              return nil, start -- in case the error method doesn't abort, return something sensible
           end
        
           if text:find('^"', start) then
              return grok_string(self, text, start, options.etc)
        
           elseif text:find('^[-0123456789 ]', start) then
              return grok_number(self, text, start, options)
        
           elseif text:find('^%{', start) then
              return grok_object(self, text, start, options)
        
           elseif text:find('^%[', start) then
              return grok_array(self, text, start, options)
        
           elseif text:find('^true', start) then
              return true, start + 4
        
           elseif text:find('^false', start) then
              return false, start + 5
        
           elseif text:find('^null', start) then
              return options.null, start + 4
        
           else
              self:onDecodeError("can't parse JSON", text, start, options.etc)
              return nil, 1 -- in case the error method doesn't abort, return something sensible
           end
        end
        
        function OBJDEF:decode(text, etc, options)
           --
           -- If the user didn't pass in a table of decode options, make an empty one.
           --
           if type(options) ~= 'table' then
              options = {}
           end
        
           --
           -- If they passed in an 'etc' argument, stuff it into the options.
           -- (If not, any 'etc' field in the options they passed in remains to be used)
           --
           if etc ~= nil then
              options.etc = etc
           end
        
        
           if type(self) ~= 'table' or self.__index ~= OBJDEF then
              local error_message = "JSON:decode must be called in method format"
              OBJDEF:onDecodeError(error_message, nil, nil, options.etc)
              return nil, error_message -- in case the error method doesn't abort, return something sensible
           end
        
           if text == nil then
              local error_message = "nil passed to JSON:decode()"
              self:onDecodeOfNilError(error_message, nil, nil, options.etc)
              return nil, error_message -- in case the error method doesn't abort, return something sensible
        
           elseif type(text) ~= 'string' then
              local error_message = "expected string argument to JSON:decode()"
              self:onDecodeError(string.format("%s, got %s", error_message, type(text)), nil, nil, options.etc)
              return nil, error_message -- in case the error method doesn't abort, return something sensible
           end
        
           if text:match('^%s*$') then
              -- an empty string is nothing, but not an error
              return nil
           end
        
           if text:match('^%s*<') then
              -- Can't be JSON... we'll assume it's HTML
              local error_message = "HTML passed to JSON:decode()"
              self:onDecodeOfHTMLError(error_message, text, nil, options.etc)
              return nil, error_message -- in case the error method doesn't abort, return something sensible
           end
        
           --
           -- Ensure that it's not UTF-32 or UTF-16.
           -- Those are perfectly valid encodings for JSON (as per RFC 4627 section 3),
           -- but this package can't handle them.
           --
           if text:sub(1,1):byte() == 0 or (text:len() >= 2 and text:sub(2,2):byte() == 0) then
              local error_message = "JSON package groks only UTF-8, sorry"
              self:onDecodeError(error_message, text, nil, options.etc)
              return nil, error_message -- in case the error method doesn't abort, return something sensible
           end
        
           --
           -- apply global options
           --
           if options.decodeNumbersAsObjects == nil then
              options.decodeNumbersAsObjects = self.decodeNumbersAsObjects
           end
           if options.decodeIntegerObjectificationLength == nil then
              options.decodeIntegerObjectificationLength = self.decodeIntegerObjectificationLength
           end
           if options.decodeDecimalObjectificationLength == nil then
              options.decodeDecimalObjectificationLength = self.decodeDecimalObjectificationLength
           end
           if options.decodeIntegerStringificationLength == nil then
              options.decodeIntegerStringificationLength = self.decodeIntegerStringificationLength
           end
           if options.decodeDecimalStringificationLength == nil then
              options.decodeDecimalStringificationLength = self.decodeDecimalStringificationLength
           end
        
        
           --
           -- Finally, go parse it
           --
           local success, value, next_i = pcall(grok_one, self, text, 1, options)
        
           if success then
        
              local error_message = nil
              if next_i ~= #text + 1 then
                 -- something's left over after we parsed the first thing.... whitespace is allowed.
                 next_i = skip_whitespace(text, next_i)
        
                 -- if we have something left over now, it's trailing garbage
                 if next_i ~= #text + 1 then
                    value, error_message = self:onTrailingGarbage(text, next_i, value, options.etc)
                 end
              end
              return value, error_message
        
           else
        
              -- If JSON:onDecodeError() didn't abort out of the pcall, we'll have received
              -- the error message here as "value", so pass it along as an assert.
              local error_message = value
              if self.assert then
                 self.assert(false, error_message)
              else
                 assert(false, error_message)
              end
              -- ...and if we're still here (because the assert didn't throw an error),
              -- return a nil and throw the error message on as a second arg
              return nil, error_message
        
           end
        end
        
        local function backslash_replacement_function(c)
           if c == "\n" then
              return "\\n"
           elseif c == "\r" then
              return "\\r"
           elseif c == "\t" then
              return "\\t"
           elseif c == "\b" then
              return "\\b"
           elseif c == "\f" then
              return "\\f"
           elseif c == '"' then
              return '\\"'
           elseif c == '\\' then
              return '\\\\'
           else
              return string.format("\\u%04x", c:byte())
           end
        end
        
        local chars_to_be_escaped_in_JSON_string
           = '['
           ..    '"'    -- class sub-pattern to match a double quote
           ..    '%\\'  -- class sub-pattern to match a backslash
           ..    '%z'   -- class sub-pattern to match a null
           ..    '\001' .. '-' .. '\031' -- class sub-pattern to match control characters
           .. ']'
        
        
        local LINE_SEPARATOR_as_utf8      = unicode_codepoint_as_utf8(0x2028)
        local PARAGRAPH_SEPARATOR_as_utf8 = unicode_codepoint_as_utf8(0x2029)
        local function json_string_literal(value, options)
           local newval = value:gsub(chars_to_be_escaped_in_JSON_string, backslash_replacement_function)
           if options.stringsAreUtf8 then
              --
              -- This feels really ugly to just look into a string for the sequence of bytes that we know to be a particular utf8 character,
              -- but utf8 was designed purposefully to make this kind of thing possible. Still, feels dirty.
              -- I'd rather decode the byte stream into a character stream, but it's not technically needed so
              -- not technically worth it.
              --
              newval = newval:gsub(LINE_SEPARATOR_as_utf8, '\\u2028'):gsub(PARAGRAPH_SEPARATOR_as_utf8,'\\u2029')
           end
           return '"' .. newval .. '"'
        end
        
        local function object_or_array(self, T, etc)
           --
           -- We need to inspect all the keys... if there are any strings, we'll convert to a JSON
           -- object. If there are only numbers, it's a JSON array.
           --
           -- If we'll be converting to a JSON object, we'll want to sort the keys so that the
           -- end result is deterministic.
           --
           local string_keys = { }
           local number_keys = { }
           local number_keys_must_be_strings = false
           local maximum_number_key
        
           for key in pairs(T) do
              if type(key) == 'string' then
                 table.insert(string_keys, key)
              elseif type(key) == 'number' then
                 table.insert(number_keys, key)
                 if key <= 0 or key >= math.huge then
                    number_keys_must_be_strings = true
                 elseif not maximum_number_key or key > maximum_number_key then
                    maximum_number_key = key
                 end
              elseif type(key) == 'boolean' then
                 table.insert(string_keys, tostring(key))
              else
                 self:onEncodeError("can't encode table with a key of type " .. type(key), etc)
              end
           end
        
           if #string_keys == 0 and not number_keys_must_be_strings then
              --
              -- An empty table, or a numeric-only array
              --
              if #number_keys > 0 then
                 return nil, maximum_number_key -- an array
              elseif tostring(T) == "JSON array" then
                 return nil
              elseif tostring(T) == "JSON object" then
                 return { }
              else
                 -- have to guess, so we'll pick array, since empty arrays are likely more common than empty objects
                 return nil
              end
           end
        
           table.sort(string_keys)
        
           local map
           if #number_keys > 0 then
              --
              -- If we're here then we have either mixed string/number keys, or numbers inappropriate for a JSON array
              -- It's not ideal, but we'll turn the numbers into strings so that we can at least create a JSON object.
              --
        
              if self.noKeyConversion then
                 self:onEncodeError("a table with both numeric and string keys could be an object or array; aborting", etc)
              end
        
              --
              -- Have to make a shallow copy of the source table so we can remap the numeric keys to be strings
              --
              map = { }
              for key, val in pairs(T) do
                 map[key] = val
              end
        
              table.sort(number_keys)
        
              --
              -- Throw numeric keys in there as strings
              --
              for _, number_key in ipairs(number_keys) do
                 local string_key = tostring(number_key)
                 if map[string_key] == nil then
                    table.insert(string_keys , string_key)
                    map[string_key] = T[number_key]
                 else
                    self:onEncodeError("conflict converting table with mixed-type keys into a JSON object: key " .. number_key .. " exists both as a string and a number.", etc)
                 end
              end
           end
        
           return string_keys, nil, map
        end
        
        --
        -- Encode
        --
        -- 'options' is nil, or a table with possible keys:
        --
        --    pretty         -- If true, return a pretty-printed version.
        --
        --    indent         -- A string (usually of spaces) used to indent each nested level.
        --
        --    align_keys     -- If true, align all the keys when formatting a table. The result is uglier than one might at first imagine.
        --                      Results are undefined if 'align_keys' is true but 'pretty' is not.
        --
        --    array_newline  -- If true, array elements are formatted each to their own line. The default is to all fall inline.
        --                      Results are undefined if 'array_newline' is true but 'pretty' is not.
        --
        --    null           -- If this exists with a string value, table elements with this value are output as JSON null.
        --
        --    stringsAreUtf8 -- If true, consider Lua strings not as a sequence of bytes, but as a sequence of UTF-8 characters.
        --                      (Currently, the only practical effect of setting this option is that Unicode LINE and PARAGRAPH
        --                       separators, if found in a string, are encoded with a JSON escape instead of as raw UTF-8.
        --                       The JSON is valid either way, but encoding this way, apparently, allows the resulting JSON
        --                       to also be valid Java.)
        --
        --
        local function encode_value(self, value, parents, etc, options, indent, for_key)
        
           --
           -- keys in a JSON object can never be null, so we don't even consider options.null when converting a key value
           --
           if value == nil or (not for_key and options and options.null and value == options.null) then
              return 'null'
        
           elseif type(value) == 'string' then
              return json_string_literal(value, options)
        
           elseif type(value) == 'number' then
              if value ~= value then
                 --
                 -- NaN (Not a Number).
                 -- JSON has no NaN, so we have to fudge the best we can. This should really be a package option.
                 --
                 return "null"
              elseif value >= math.huge then
                 --
                 -- Positive infinity. JSON has no INF, so we have to fudge the best we can. This should
                 -- really be a package option. Note: at least with some implementations, positive infinity
                 -- is both ">= math.huge" and "<= -math.huge", which makes no sense but that's how it is.
                 -- Negative infinity is properly "<= -math.huge". So, we must be sure to check the ">="
                 -- case first.
                 --
                 return "1e+9999"
              elseif value <= -math.huge then
                 --
                 -- Negative infinity.
                 -- JSON has no INF, so we have to fudge the best we can. This should really be a package option.
                 --
                 return "-1e+9999"
              else
                 return tostring(value)
              end
        
           elseif type(value) == 'boolean' then
              return tostring(value)
        
           elseif type(value) ~= 'table' then
        
              if self.unsupportedTypeEncoder then
                 local user_value, user_error = self:unsupportedTypeEncoder(value, parents, etc, options, indent, for_key)
                 -- If the user's handler returns a string, use that. If it returns nil plus an error message, bail with that.
                 -- If only nil returned, fall through to the default error handler.
                 if type(user_value) == 'string' then
                    return user_value
                 elseif user_value ~= nil then
                    self:onEncodeError("unsupportedTypeEncoder method returned a " .. type(user_value), etc)
                 elseif user_error then
                    self:onEncodeError(tostring(user_error), etc)
                 end
              end
        
              self:onEncodeError("can't convert " .. type(value) .. " to JSON", etc)
        
           elseif getmetatable(value) == isNumber then
              return tostring(value)
           else
              --
              -- A table to be converted to either a JSON object or array.
              --
              local T = value
        
              if type(options) ~= 'table' then
                 options = {}
              end
              if type(indent) ~= 'string' then
                 indent = ""
              end
        
              if parents[T] then
                 self:onEncodeError("table " .. tostring(T) .. " is a child of itself", etc)
              else
                 parents[T] = true
              end
        
              local result_value
        
              local object_keys, maximum_number_key, map = object_or_array(self, T, etc)
              if maximum_number_key then
                 --
                 -- An array...
                 --
                 local key_indent
                 if options.array_newline then
                    key_indent = indent .. tostring(options.indent or "")
                 else
                    key_indent = indent
                 end
        
                 local ITEMS = { }
                 for i = 1, maximum_number_key do
                    table.insert(ITEMS, encode_value(self, T[i], parents, etc, options, key_indent))
                 end
        
                 if options.array_newline then
                    result_value = "[\n" .. key_indent .. table.concat(ITEMS, ",\n" .. key_indent) .. "\n" .. indent .. "]"
                 elseif options.pretty then
                    result_value = "[ " .. table.concat(ITEMS, ", ") .. " ]"
                 else
                    result_value = "["  .. table.concat(ITEMS, ",")  .. "]"
                 end
        
              elseif object_keys then
                 --
                 -- An object
                 --
                 local TT = map or T
        
                 if options.pretty then
        
                    local KEYS = { }
                    local max_key_length = 0
                    for _, key in ipairs(object_keys) do
                       local encoded = encode_value(self, tostring(key), parents, etc, options, indent, true)
                       if options.align_keys then
                          max_key_length = math.max(max_key_length, #encoded)
                       end
                       table.insert(KEYS, encoded)
                    end
                    local key_indent = indent .. tostring(options.indent or "")
                    local subtable_indent = key_indent .. string.rep(" ", max_key_length) .. (options.align_keys and "  " or "")
                    local FORMAT = "%s%" .. string.format("%d", max_key_length) .. "s: %s"
        
                    local COMBINED_PARTS = { }
                    for i, key in ipairs(object_keys) do
                       local encoded_val = encode_value(self, TT[key], parents, etc, options, subtable_indent)
                       table.insert(COMBINED_PARTS, string.format(FORMAT, key_indent, KEYS[i], encoded_val))
                    end
                    result_value = "{\n" .. table.concat(COMBINED_PARTS, ",\n") .. "\n" .. indent .. "}"
        
                 else
        
                    local PARTS = { }
                    for _, key in ipairs(object_keys) do
                       local encoded_val = encode_value(self, TT[key],       parents, etc, options, indent)
                       local encoded_key = encode_value(self, tostring(key), parents, etc, options, indent, true)
                       table.insert(PARTS, string.format("%s:%s", encoded_key, encoded_val))
                    end
                    result_value = "{" .. table.concat(PARTS, ",") .. "}"
        
                 end
              else
                 --
                 -- An empty array/object... we'll treat it as an array, though it should really be an option
                 --
                 result_value = "[]"
              end
        
              parents[T] = false
              return result_value
           end
        end
        
        local function top_level_encode(self, value, etc, options)
           local val = encode_value(self, value, {}, etc, options)
           if val == nil then
              --PRIVATE("may need to revert to the previous public verison if I can't figure out what the guy wanted")
              return val
           else
              return val
           end
        end
        
        function OBJDEF:encode(value, etc, options)
           if type(self) ~= 'table' or self.__index ~= OBJDEF then
              OBJDEF:onEncodeError("JSON:encode must be called in method format", etc)
           end
        
           --
           -- If the user didn't pass in a table of decode options, make an empty one.
           --
           if type(options) ~= 'table' then
              options = {}
           end
        
           return top_level_encode(self, value, etc, options)
        end
        
        function OBJDEF:encode_pretty(value, etc, options)
           if type(self) ~= 'table' or self.__index ~= OBJDEF then
              OBJDEF:onEncodeError("JSON:encode_pretty must be called in method format", etc)
           end
        
           --
           -- If the user didn't pass in a table of decode options, use the default pretty ones
           --
           if type(options) ~= 'table' then
              options = default_pretty_options
           end
        
           return top_level_encode(self, value, etc, options)
        end
        
        function OBJDEF.__tostring()
           return "JSON encode/decode package"
        end
        
        OBJDEF.__index = OBJDEF
        
        function OBJDEF:new(args)
           local new = { }
        
           if args then
              for key, val in pairs(args) do
                 new[key] = val
              end
           end
        
           return setmetatable(new, OBJDEF)
        end
        
        return OBJDEF:new()
        
        --
        -- Version history:
        --
        --   20170927.26   Use option.null in decoding as well. Thanks to Max Sindwani for the bump, and sorry to Oliver Hitz
        --                 whose first mention of it four years ago was completely missed by me.
        --
        --   20170823.25   Added support for JSON:unsupportedTypeEncoder().
        --                 Thanks to Chronos Phaenon Eosphoros (https://github.com/cpeosphoros) for the idea.
        --
        --   20170819.24   Added support for boolean keys in tables.
        --
        --   20170416.23   Added the "array_newline" formatting option suggested by yurenchen (http://www.yurenchen.com/)
        --
        --   20161128.22   Added:
        --                   JSON:isString()
        --                   JSON:isNumber()
        --                   JSON:decodeIntegerObjectificationLength
        --                   JSON:decodeDecimalObjectificationLength
        --
        --   20161109.21   Oops, had a small boo-boo in the previous update.
        --
        --   20161103.20   Used to silently ignore trailing garbage when decoding. Now fails via JSON:onTrailingGarbage()
        --                 http://seriot.ch/parsing_json.php
        --
        --                 Built-in error message about "expected comma or ']'" had mistakenly referred to '['
        --
        --                 Updated the built-in error reporting to refer to bytes rather than characters.
        --
        --                 The decode() method no longer assumes that error handlers abort.
        --
        --                 Made the VERSION string a string instead of a number
        --
        
        --   20160916.19   Fixed the isNumber.__index assignment (thanks to Jack Taylor)
        --   
        --   20160730.18   Added JSON:forceString() and JSON:forceNumber()
        --
        --   20160728.17   Added concatenation to the metatable for JSON:asNumber()
        --
        --   20160709.16   Could crash if not passed an options table (thanks jarno heikkinen <jarnoh@capturemonkey.com>).
        --
        --                 Made JSON:asNumber() a bit more resilient to being passed the results of itself.
        --
        --   20160526.15   Added the ability to easily encode null values in JSON, via the new "null" encoding option.
        --                 (Thanks to Adam B for bringing up the issue.)
        --
        --                 Added some support for very large numbers and precise floats via
        --                    JSON.decodeNumbersAsObjects
        --                    JSON.decodeIntegerStringificationLength
        --                    JSON.decodeDecimalStringificationLength
        --
        --                 Added the "stringsAreUtf8" encoding option. (Hat tip to http://lua-users.org/wiki/JsonModules )
        --
        --   20141223.14   The encode_pretty() routine produced fine results for small datasets, but isn't really
        --                 appropriate for anything large, so with help from Alex Aulbach I've made the encode routines
        --                 more flexible, and changed the default encode_pretty() to be more generally useful.
        --
        --                 Added a third 'options' argument to the encode() and encode_pretty() routines, to control
        --                 how the encoding takes place.
        --
        --                 Updated docs to add assert() call to the loadfile() line, just as good practice so that
        --                 if there is a problem loading JSON.lua, the appropriate error message will percolate up.
        --
        --   20140920.13   Put back (in a way that doesn't cause warnings about unused variables) the author string,
        --                 so that the source of the package, and its version number, are visible in compiled copies.
        --
        --   20140911.12   Minor lua cleanup.
        --                 Fixed internal reference to 'JSON.noKeyConversion' to reference 'self' instead of 'JSON'.
        --                 (Thanks to SmugMug's David Parry for these.)
        --
        --   20140418.11   JSON nulls embedded within an array were being ignored, such that
        --                     ["1",null,null,null,null,null,"seven"],
        --                 would return
        --                     {1,"seven"}
        --                 It's now fixed to properly return
        --                     {1, nil, nil, nil, nil, nil, "seven"}
        --                 Thanks to "haddock" for catching the error.
        --
        --   20140116.10   The user's JSON.assert() wasn't always being used. Thanks to "blue" for the heads up.
        --
        --   20131118.9    Update for Lua 5.3... it seems that tostring(2/1) produces "2.0" instead of "2",
        --                 and this caused some problems.
        --
        --   20131031.8    Unified the code for encode() and encode_pretty(); they had been stupidly separate,
        --                 and had of course diverged (encode_pretty didn't get the fixes that encode got, so
        --                 sometimes produced incorrect results; thanks to Mattie for the heads up).
        --
        --                 Handle encoding tables with non-positive numeric keys (unlikely, but possible).
        --
        --                 If a table has both numeric and string keys, or its numeric keys are inappropriate
        --                 (such as being non-positive or infinite), the numeric keys are turned into
        --                 string keys appropriate for a JSON object. So, as before,
        --                         JSON:encode({ "one", "two", "three" })
        --                 produces the array
        --                         ["one","two","three"]
        --                 but now something with mixed key types like
        --                         JSON:encode({ "one", "two", "three", SOMESTRING = "some string" }))
        --                 instead of throwing an error produces an object:
        --                         {"1":"one","2":"two","3":"three","SOMESTRING":"some string"}
        --
        --                 To maintain the prior throw-an-error semantics, set
        --                      JSON.noKeyConversion = true
        --                 
        --   20131004.7    Release under a Creative Commons CC-BY license, which I should have done from day one, sorry.
        --
        --   20130120.6    Comment update: added a link to the specific page on my blog where this code can
        --                 be found, so that folks who come across the code outside of my blog can find updates
        --                 more easily.
        --
        --   20111207.5    Added support for the 'etc' arguments, for better error reporting.
        --
        --   20110731.4    More feedback from David Kolf on how to make the tests for Nan/Infinity system independent.
        --
        --   20110730.3    Incorporated feedback from David Kolf at http://lua-users.org/wiki/JsonModules:
        --
        --                   * When encoding lua for JSON, Sparse numeric arrays are now handled by
        --                     spitting out full arrays, such that
        --                        JSON:encode({"one", "two", [10] = "ten"})
        --                     returns
        --                        ["one","two",null,null,null,null,null,null,null,"ten"]
        --
        --                     In 20100810.2 and earlier, only up to the first non-null value would have been retained.
        --
        --                   * When encoding lua for JSON, numeric value NaN gets spit out as null, and infinity as "1+e9999".
        --                     Version 20100810.2 and earlier created invalid JSON in both cases.
        --
        --                   * Unicode surrogate pairs are now detected when decoding JSON.
        --
        --   20100810.2    added some checking to ensure that an invalid Unicode character couldn't leak in to the UTF-8 encoding
        --
        --   20100731.1    initial public release
        --
        
    moesif-core.lua: return{base64=require("moesif.core.base64"),helpers=require("moesif.core.helpers"),lib_deflate=require("moesif.core.lib_deflate"),json=require("moesif.core.json"),zzlib=require("moesif.core.zzlib"),empty_tab={}}
