# File lib/faster_csv.rb, line 1578
    def shift
      #########################################################################
      ### This method is purposefully kept a bit long as simple conditional ###
      ### checks are faster than numerous (expensive) method calls.         ###
      #########################################################################

      # handle headers not based on document content
      if header_row? and @return_headers and
         [Array, String].include? @use_headers.class
        if @unconverted_fields
          return add_unconverted_fields(parse_headers, Array.new)
        else
          return parse_headers
        end
      end

      # begin with a blank line, so we can always add to it
      line = String.new

      # 
      # it can take multiple calls to <tt>@io.gets()</tt> to get a full line,
      # because of \r and/or \n characters embedded in quoted fields
      # 
      loop do
        # add another read to the line
        if read_line = @io.gets(@row_sep)
         line += read_line
        else
         return nil
        end
        # copy the line so we can chop it up in parsing
        parse =  line.dup
        parse.sub!(@parsers[:line_end], "")

        # 
        # I believe a blank line should be an <tt>Array.new</tt>, not 
        # CSV's <tt>[nil]</tt>
        # 
        if parse.empty?
          @lineno += 1
          if @skip_blanks
            line = ""
            next
          elsif @unconverted_fields
            return add_unconverted_fields(Array.new, Array.new)
          elsif @use_headers
            return FasterCSV::Row.new(Array.new, Array.new)
          else
            return Array.new
          end
        end

        # parse the fields with a mix of String#split and regular expressions
        csv           = Array.new
        current_field = String.new
        field_quotes  = 0
        parse.split(@col_sep, -1).each do |match|
          if current_field.empty? && match.count(@quote_and_newlines).zero?
            csv           << (match.empty? ? nil : match)
          elsif (current_field.empty? ? match[0] : current_field[0]) ==
                @quote_char[0]
            current_field << match
            field_quotes += match.count(@quote_char)
            if field_quotes % 2 == 0
              in_quotes = current_field[@parsers[:quoted_field], 1]
              if !in_quotes || in_quotes[@parsers[:stray_quote]]
                raise MalformedCSVError,
                      "Missing or stray quote in line #{lineno + 1}"
              end
              current_field = in_quotes
              current_field.gsub!(@quote_char * 2, @quote_char) # unescape contents
              csv           << current_field
              current_field =  String.new
              field_quotes  =  0
            else # we found a quoted field that spans multiple lines
              current_field << @col_sep
            end
          elsif match.count("\r\n").zero?
            raise MalformedCSVError, "Illegal quoting in line #{lineno + 1}."
          else
            raise MalformedCSVError, "Unquoted fields do not allow " +
                                     "\\r or \\n (line #{lineno + 1})."
          end
        end

        # if parse is empty?(), we found all the fields on the line...
        if field_quotes % 2 == 0
          @lineno += 1

          # save fields unconverted fields, if needed...
          unconverted = csv.dup if @unconverted_fields

          # convert fields, if needed...
          csv = convert_fields(csv) unless @use_headers or @converters.empty?
          # parse out header rows and handle FasterCSV::Row conversions...
          csv = parse_headers(csv)  if     @use_headers

          # inject unconverted fields and accessor, if requested...
          if @unconverted_fields and not csv.respond_to? :unconverted_fields
            add_unconverted_fields(csv, unconverted)
          end

          # return the results
          break csv
        end
        # if we're not empty?() but at eof?(), a quoted field wasn't closed...
        if @io.eof?
          raise MalformedCSVError, "Unclosed quoted field on line #{lineno + 1}."
        elsif @field_size_limit and current_field.size >= @field_size_limit
          raise MalformedCSVError, "Field size exceeded on line #{lineno + 1}."
        end
        # otherwise, we need to loop and pull some more data to complete the row
      end
    end