<commit_before>module Hiredis
  module Ruby
    class Reader

      def initialize
        @buffer = Buffer.new
        @task = Task.new(@buffer)
      end

      def feed(data)
        @buffer << data
      end

      def gets
        reply = @task.process
        @buffer.discard!
        reply
      end

    protected

      class Task

        MINUS    = "-"[0]
        PLUS     = "+"[0]
        COLON    = ":"[0]
        DOLLAR   = "$"[0]
        ASTERISK = "*"[0]

        attr_accessor :parent
        attr_accessor :multi_bulk

        def initialize(buffer, parent = nil)
          @buffer, @parent = buffer, parent
        end

        def child
          @child ||= Task.new(@buffer, self)
        end

        def root
          parent ? parent.root : self
        end

        # Set error ivar on object itself when this is the root task,
        # otherwise on the root multi bulk.
        def set_error_object(err)
          obj = parent ? root.multi_bulk : err
          if !obj.instance_variable_defined?(:@__hiredis_error)
            obj.instance_variable_set(:@__hiredis_error, err)
          end
        end

        def reset!
          @line = @type = @multi_bulk = nil
        end

        def process_error_reply
          reply = RuntimeError.new(@line)
          set_error_object(reply)
          reset!
          reply
        end

        def process_status_reply
          reply = @line
          reset!
          reply
        end

        def process_integer_reply
          reply = @line.to_i
          reset!
          reply
        end

        def process_bulk_reply
          bulk_length = @line.to_i

          if bulk_length >= 0
            reply = @buffer.read(bulk_length, 2)
            if reply.nil?
              return false
            else
              reset!
              reply
            end
          else
            reset!
            nil
          end
        end

        def process_multi_bulk_reply
          multi_bulk_length = @line.to_i

          if multi_bulk_length > 0
            @multi_bulk ||= []

            while @multi_bulk.length < multi_bulk_length
              element = child.process
              break if element == false
              @multi_bulk << element
            end

            if @multi_bulk.length == multi_bulk_length
              reply = @multi_bulk
              reset!
              reply
            else
              false
            end
          elsif multi_bulk_length == 0
            reset!
            []
          else
            reset!
            nil
          end
        end

        def process
          @line ||= @buffer.read_line
          return false if @line.nil?

          @type ||= @line.slice!(0)

          case @type
          when MINUS
            process_error_reply
          when PLUS
            process_status_reply
          when COLON
            process_integer_reply
          when DOLLAR
            process_bulk_reply
          when ASTERISK
            process_multi_bulk_reply
          else
            raise "Protocol error"
          end
        end
      end

      class Buffer

        CRLF = "\r\n".freeze

        def initialize
          @buffer = ""
          @length = @pos = 0
        end

        def <<(data)
          @length += data.length
          @buffer << data
        end

        def length<commit_msg>Reset task when returning from #process<commit_after>module Hiredis
  module Ruby
    class Reader

      def initialize
        @buffer = Buffer.new
        @task = Task.new(@buffer)
      end

      def feed(data)
        @buffer << data
      end

      def gets
        reply = @task.process
        @buffer.discard!
        reply
      end

    protected

      class Task

        MINUS    = "-"[0]
        PLUS     = "+"[0]
        COLON    = ":"[0]
        DOLLAR   = "$"[0]
        ASTERISK = "*"[0]

        attr_accessor :parent
        attr_accessor :multi_bulk

        def initialize(buffer, parent = nil)
          @buffer, @parent = buffer, parent
        end

        def child
          @child ||= Task.new(@buffer, self)
        end

        def root
          parent ? parent.root : self
        end

        # Set error ivar on object itself when this is the root task,
        # otherwise on the root multi bulk.
        def set_error_object(err)
          obj = parent ? root.multi_bulk : err
          if !obj.instance_variable_defined?(:@__hiredis_error)
            obj.instance_variable_set(:@__hiredis_error, err)
          end
          err
        end

        def reset!
          @line = @type = @multi_bulk = nil
        end

        def process_error_reply
          set_error_object RuntimeError.new(@line)
        end

        def process_status_reply
          @line
        end

        def process_integer_reply
          @line.to_i
        end

        def process_bulk_reply
          bulk_length = @line.to_i

          if bulk_length >= 0
            reply = @buffer.read(bulk_length, 2)
            if reply.nil?
              false
            else
              reply
            end
          else
            nil
          end
        end

        def process_multi_bulk_reply
          multi_bulk_length = @line.to_i

          if multi_bulk_length > 0
            @multi_bulk ||= []

            while @multi_bulk.length < multi_bulk_length
              element = child.process
              break if element == false
              @multi_bulk << element
            end

            if @multi_bulk.length == multi_bulk_length
              @multi_bulk
            else
              false
            end
          elsif multi_bulk_length == 0
            []
          else
            nil
          end
        end

        def process
          @line ||= @buffer.read_line
          return false if @line.nil?

          @type ||= @line.slice!(0)

          reply = case @type
          when MINUS
            process_error_reply
          when PLUS
            process_status_reply
          when COLON
            process_integer_reply
          when DOLLAR
            process_bulk_reply
          when ASTERISK
            process_multi_bulk_reply
          else
            raise "Protocol error"
          end

          reset! if reply != false
          reply
        end
      end

      class Buffer

        CRLF = "\r\n".freeze

        def initialize
          @buffer = ""
          @length = @pos = 0
        end

        def <<(data)
          @length += data.length
          @buffer << data
        end

        def length
{'fraction_non_alphanumeric': 0.06415726912526669, 'fraction_numerical': 0.0033526363913441025, 'mean_word_length': 1.1496888306583688, 'pattern_counts': {'":': 2, '<': 21, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '743214', 'n_tokens_mistral': 1830, 'n_tokens_neox': 1746, 'n_words': 470}