shift (FasterCSV)

    # File lib/faster_csv.rb, line 1553
      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
          begin
            line  += @io.gets(@row_sep)
          rescue
            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]
                raise MalformedCSVError unless in_quotes
                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 on 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