riboseinc/id_pack

View on GitHub
lib/id_pack/lz_string.rb

Summary

Maintainability
F
1 wk
Test Coverage
module IdPack
  module LZString
    KEY_STR_BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".freeze
    KEY_STR_URI_SAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$".freeze

    class << self

      def get_base_value(alphabet, character)
        @base_reverse_dic ||= {}

        if !@base_reverse_dic[alphabet]
          @base_reverse_dic[alphabet] = {}

          alphabet.length.times do |i|
            @base_reverse_dic[alphabet][alphabet[i]] = i
          end
        end

        @base_reverse_dic[alphabet][character]
      end

      def compress_to_base64(input)
        return "" if input.nil?

        res = _compress(input, 6) do |a|
          KEY_STR_BASE64[a]
        end

        case res.length % 4
        when 0 then res
        when 1 then "#{res}==="
        when 2 then "#{res}=="
        when 3 then "#{res}="
        end
      end

      def decompress_from_base64(input)
        return "" if input.nil?
        return nil if input == ""

        _decompress(input.length, 32) do |index|
          get_base_value(KEY_STR_BASE64, input[index])
        end
      end

      def compress_to_utf16(input)
        return "" if input.nil?

        _compress(input, 15) do |a|
          [a + 32].pack 'U'
        end + " "
      end

      def decompress_from_utf16(compressed)
        return "" if compressed.nil?
        return nil if compressed == ""

        _decompress(compressed.length, 16384) do |index|
          compressed[index].ord - 32
        end
      end

      def compress_to_uint8_array(uncompressed)
        compressed = compress(uncompressed)
        buf = []

        compressed.length.times do |i|
          current_value = compressed[i].ord
          buf[i * 2] = current_value >> 8 # TODO: >>> 8 how to do it in ruby?
          buf[i * 2 + 1] = current_value % 256
        end

        buf
      end

      def decompress_from_uint8_array(compressed)
        return decompress(compressed) if compressed.nil?

        buf = []

        (compressed.length / 2).times do |i|
          buf[i] = compressed[i * 2] * 256 + compressed[i * 2 + 1]
        end

        result = []

        buf.each do |_c|
          result.push(
            [a + 32].pack('U'),
          )
        end

        decompress(result.join(''))
      end

      def compress_to_encoded_uri_component(input)
        return "" if input.nil?

        _compress(input, 6) do |a|
          KEY_STR_URI_SAFE[a]
        end
      end

      def decompress_from_encoded_uri_component(input)
        return "" if input.nil?
        return nil if input == ""

        input.gsub!(/ /, "+")

        _decompress(input.length, 32) do |index|
          get_base_value(KEY_STR_URI_SAFE, input[index])
        end
      end

      def compress(uncompressed)
        _compress(uncompressed, 16) do |a|
          [a].pack 'U'
        end
      end

      def _compress(uncompressed, bits_per_char)
        return "" if uncompressed.nil?

        context_dictionary = {}
        context_dictionary_to_create = {}
        context_c = ""
        context_wc = ""
        context_w = ""
        context_enlarge_in = 2
        context_dict_size = 3
        context_num_bits = 2
        context_data = []
        context_data_val = 0
        context_data_position = 0

        uncompressed.length.times do |ii|
          context_c = uncompressed[ii]

          if !context_dictionary[context_c]
            context_dictionary[context_c] = context_dict_size
            context_dict_size += 1
            context_dictionary_to_create[context_c] = true
          end

          context_wc = context_w + context_c

          if context_dictionary[context_wc]
            context_w = context_wc
          else
            if context_dictionary_to_create[context_w]
              if context_w[0].ord < 256
                context_num_bits.times do |_i|
                  context_data_val = (context_data_val << 1)

                  if context_data_position == bits_per_char - 1
                    context_data_position = 0
                    context_data.push(yield(context_data_val))
                    context_data_val = 0
                  else
                    context_data_position += 1
                  end
                end

                value = context_w[0].ord

                8.times do |_i|
                  context_data_val = (context_data_val << 1) | (value & 1)

                  if context_data_position == bits_per_char - 1
                    context_data_position = 0
                    context_data.push(yield(context_data_val))
                    context_data_val = 0
                  else
                    context_data_position += 1
                  end

                  value = value >> 1
                end
              else
                value = 1

                context_num_bits.times do |_i|
                  context_data_val = (context_data_val << 1) | value

                  if context_data_position == bits_per_char - 1
                    context_data_position = 0
                    context_data.push(yield(context_data_val))
                    context_data_val = 0
                  else
                    context_data_position += 1
                  end

                  value = 0
                end

                value = context_w[0].ord

                16.times do |_i|
                  context_data_val = (context_data_val << 1) | (value & 1)

                  if context_data_position == bits_per_char - 1
                    context_data_position = 0
                    context_data.push(yield(context_data_val))
                    context_data_val = 0
                  else
                    context_data_position += 1
                  end

                  value = value >> 1
                end
              end

              context_enlarge_in -= 1

              if context_enlarge_in.zero?
                context_enlarge_in = 2**context_num_bits
                context_num_bits += 1
              end

              context_dictionary_to_create.delete(context_w)
            else
              value = context_dictionary[context_w]

              context_num_bits.times do |_i|
                context_data_val = (context_data_val << 1) | (value & 1)

                if context_data_position == bits_per_char - 1
                  context_data_position = 0
                  context_data.push(yield(context_data_val))
                  context_data_val = 0
                else
                  context_data_position += 1
                end

                value = value >> 1
              end
            end

            context_enlarge_in -= 1

            if context_enlarge_in.zero?
              context_enlarge_in = 2**context_num_bits
              context_num_bits += 1
            end

            context_dictionary[context_wc] = context_dict_size
            context_dict_size += 1
            context_w = context_c.to_s
          end
        end

        if context_w != ""
          if context_dictionary_to_create[context_w]
            if context_w[0].ord < 256
              context_num_bits.times do |_i|
                context_data_val = context_data_val << 1

                if context_data_position == bits_per_char - 1
                  context_data_position = 0
                  context_data.push(yield(context_data_val))
                  context_data_val = 0
                else
                  context_data_position += 1
                end
              end

              value = context_w[0].ord

              8.times do |_i|
                context_data_val = (context_data_val << 1) | (value & 1)

                if context_data_position == bits_per_char - 1
                  context_data_position = 0
                  context_data.push(yield(context_data_val))
                  context_data_val = 0
                else
                  context_data_position += 1
                end

                value = value >> 1
              end
            else
              value = 1

              context_num_bits.times do |_i|
                context_data_val = (context_data_val << 1) | value

                if context_data_position == bits_per_char - 1
                  context_data_position = 0
                  context_data.push(yield(context_data_val))
                  context_data_val = 0
                else
                  context_data_position += 1
                end

                value = 0
              end

              value = context_w[0].ord

              16.times do |_i|
                context_data_val = (context_data_val << 1) | (value & 1)

                if context_data_position == bits_per_char - 1
                  context_data_position = 0
                  context_data.push(yield(context_data_val))
                  context_data_val = 0
                else
                  context_data_position += 1
                end

                value = value >> 1
              end
            end

            context_enlarge_in -= 1

            if context_enlarge_in.zero?
              context_enlarge_in = 2**context_num_bits
              context_num_bits += 1
            end

            context_dictionary_to_create.delete(context_w)
          else
            value = context_dictionary[context_w]

            context_num_bits.times do |_i|
              context_data_val = (context_data_val << 1) | (value & 1)

              if context_data_position == bits_per_char - 1
                context_data_position = 0
                context_data.push(yield(context_data_val))
                context_data_val = 0
              else
                context_data_position += 1
              end

              value = value >> 1
            end
          end

          context_enlarge_in -= 1

          if context_enlarge_in.zero?
            context_enlarge_in = 2**context_num_bits
            context_num_bits += 1
          end
        end

        value = 2

        context_num_bits.times do |_i|
          context_data_val = (context_data_val << 1) | (value & 1)

          if context_data_position == bits_per_char - 1
            context_data_position = 0
            context_data.push(yield(context_data_val))
            context_data_val = 0
          else
            context_data_position += 1
          end

          value = value >> 1
        end

        loop do
          context_data_val = (context_data_val << 1)

          if context_data_position == bits_per_char - 1
            context_data.push(yield(context_data_val))
            break
          else
            context_data_position += 1
          end
        end

        context_data.join('')
      end

      def decompress(compressed)
        return "" if compressed.nil?
        return null if compressed == ""

        _decompress(compressed.length, 32768) do |index|
          compressed[index].ord
        end
      end

      def _decompress(length, reset_value)
        dictionary = []
        enlarge_in = 4
        dict_size = 4
        num_bits = 3
        entry = ""
        result = []
        data = {
          val: yield(0),
          position: reset_value,
          index: 1,
        }

        3.times do |i|
          dictionary[i] = i
        end

        bits = 0
        maxpower = 2**2
        power = 1

        while power != maxpower
          resb = data[:val] & data[:position]
          data[:position] = data[:position] >> 1

          if (data[:position]).zero?
            data[:position] = reset_value
            data[:val] = yield(data[:index])
            data[:index] += 1
          end

          bits |= (resb.positive? ? 1 : 0) * power
          power = power << 1
        end

        case bits
        when 0
          bits = 0
          maxpower = 2**8
          power = 1

          while power != maxpower
            resb = data[:val] & data[:position]
            data[:position] = data[:position] >> 1

            if (data[:position]).zero?
              data[:position] = reset_value
              data[:val] = yield(data[:index])
              data[:index] += 1
            end

            bits |= (resb.positive? ? 1 : 0) * power
            power <<= 1
          end

          c = [bits].pack 'U'
        when 1
          bits = 0
          maxpower = 2**16
          power = 1

          while power != maxpower
            resb = data[:val] & data[:position]
            data[:position] = data[:position] >> 1

            if (data[:position]).zero?
              data[:position] = reset_value
              data[:val] = yield(data[:index])
              data[:index] += 1
            end

            bits |= (resb.positive? ? 1 : 0) * power
            power <<= 1
          end

          c = [bits].pack 'U'
        when 2
          return ""
        end

        dictionary[3] = c
        w = c
        result.push(c)

        loop do
          return "" if data[:index] > length

          bits = 0
          maxpower = 2**num_bits
          power = 1

          while power != maxpower
            resb = data[:val] & data[:position]
            data[:position] = data[:position] >> 1

            if (data[:position]).zero?
              data[:position] = reset_value
              data[:val] = yield(data[:index])
              data[:index] += 1
            end

            bits |= (resb.positive? ? 1 : 0) * power
            power <<= 1
          end

          c = bits

          case bits
          when 0
            bits = 0
            maxpower = 2**8
            power = 1

            while power != maxpower
              resb = data[:val] & data[:position]
              data[:position] >>= 1

              if (data[:position]).zero?
                data[:position] = reset_value
                data[:val] = yield(data[:index])
                data[:index] += 1
              end

              bits |= (resb.positive? ? 1 : 0) * power
              power <<= 1
            end

            dictionary[dict_size] = [bits].pack 'U'
            dict_size += 1
            c = dict_size - 1
            enlarge_in -= 1
          when 1
            bits = 0
            maxpower = 2**16
            power = 1

            while power != maxpower
              resb = data[:val] & data[:position]
              data[:position] >>= 1

              if (data[:position]).zero?
                data[:position] = reset_value
                data[:val] = yield(data[:index])
                data[:index] += 1
              end

              bits |= (resb.positive? ? 1 : 0) * power
              power <<= 1
            end

            dictionary[dict_size] = [bits].pack 'U'
            dict_size += 1
            c = dict_size - 1
            enlarge_in -= 1
          when 2
            return result.join("")
          end

          if enlarge_in.zero?
            enlarge_in = 2**num_bits
            num_bits += 1
          end

          if dictionary[c]
            entry = dictionary[c]
          else
            return nil if c != dict_size

            entry = w + w[0]
          end

          result.push(entry)

          dictionary[dict_size] = w + entry[0]
          dict_size += 1
          enlarge_in -= 1

          w = entry

          if enlarge_in.zero?
            enlarge_in = 2**num_bits
            num_bits += 1
          end
        end
      end

    end

  end

end