# File lib/archive/zip/entry.rb, line 217
    def self.parse(io)
      # Parse the central file record and then use the information found there
      # to locate and parse the corresponding local file record.
      cfr = parse_central_file_record(io)
      next_record_position = io.pos
      io.seek(cfr.local_header_position)
      unless IOExtensions.read_exactly(io, 4) == LFH_SIGNATURE then
        raise Zip::EntryError, 'bad local file header signature'
      end
      lfr = parse_local_file_record(io, cfr.compressed_size)

      # Check to ensure that the contents of the central file record and the
      # local file record which are supposed to be duplicated are in fact the
      # same.
      compare_file_records(lfr, cfr)

      begin
        # Load the correct compression codec.
        compression_codec = Codec.create_compression_codec(
          cfr.compression_method,
          cfr.general_purpose_flags
        )
      rescue Zip::Error => e
        raise Zip::EntryError, "`#{cfr.zip_path}': #{e.message}"
      end

      begin
        # Load the correct encryption codec.
        encryption_codec = Codec.create_encryption_codec(
          cfr.general_purpose_flags
        )
      rescue Zip::Error => e
        raise Zip::EntryError, "`#{cfr.zip_path}': #{e.message}"
      end

      # Set up a data descriptor with expected values for later comparison.
      expected_data_descriptor = DataDescriptor.new(
        cfr.crc32,
        cfr.compressed_size,
        cfr.uncompressed_size
      )

      # Create the entry.
      expanded_path = expand_path(cfr.zip_path)
      io_window = IOWindow.new(io, io.pos, cfr.compressed_size)
      if cfr.zip_path[-1..-1] == '/' then
        # This is a directory entry.
        entry = Entry::Directory.new(expanded_path, io_window)
      elsif (cfr.external_file_attributes >> 16) & 0770000 == 0120000 then
        # This is a symlink entry.
        entry = Entry::Symlink.new(expanded_path, io_window)
      else
        # Anything else is a file entry.
        entry = Entry::File.new(expanded_path, io_window)
      end

      # Set the expected data descriptor so that extraction can be verified.
      entry.expected_data_descriptor = expected_data_descriptor
      # Record the compression codec.
      entry.compression_codec = compression_codec
      # Record the encryption codec.
      entry.encryption_codec = encryption_codec
      # Set some entry metadata.
      entry.mtime = cfr.mtime
      # Only set mode bits for the entry if the external file attributes are
      # Unix-compatible.
      if cfr.made_by_version & 0xFF00 == 0x0300 then
        entry.mode = cfr.external_file_attributes >> 16
      end
      entry.comment = cfr.comment
      cfr.extra_fields.each { |ef| entry.add_extra_field(ef) }
      lfr.extra_fields.each { |ef| entry.add_extra_field(ef) }

      # Return to the beginning of the next central directory record.
      io.seek(next_record_position)

      entry
    end