# File lib/faster_csv.rb, line 1460
  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 = ""

    # 
    # 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
      line  += @io.gets(@row_sep) rescue return nil
      # 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

      # 
      # shave leading empty fields if needed, because the main parser chokes 
      # on these
      # 
      csv = if parse.sub!(@parsers[:leading_fields], "")
        [nil] * ($&.length / @col_sep.length)
      else
        Array.new
      end
      # 
      # then parse the main fields with a hyper-tuned Regexp from 
      # Mastering Regular Expressions, Second Edition
      # 
      parse.gsub!(@parsers[:csv_row]) do
        csv << if $1.nil?     # we found an unquoted field
          if $2.empty?        # switch empty unquoted fields to +nil+...
            nil               # for CSV compatibility
          else
            # I decided to take a strict approach to CSV parsing...
            if $2.count("\r\n").zero?  # verify correctness of field...
              $2
            else
              # or throw an Exception
              raise MalformedCSVError, "Unquoted fields do not allow " +
                                       "\\r or \\n (line #{lineno + 1})."
            end
          end
        else                  # we found a quoted field...
          $1.gsub('""', '"')  # unescape contents
        end
        ""  # gsub!'s replacement, clear the field
      end

      # if parse is empty?(), we found all the fields on the line...
      if parse.empty?
        @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}."
      end
      # otherwise, we need to loop and pull some more data to complete the row
    end
  end