text: class _meta_table_enum_wrapper {
  public:
  	_meta_table_enum_wrapper(file_info & p_info) : m_info(p_info) {}
  	template<typename t_values>
  	void operator() (const char * p_name,const t_values & p_values) {
  		t_size index = ~0;
  		for(t_values::const_iterator iter = p_values.first(); iter.is_valid(); ++iter) {
  			if (index == ~0) index = m_info.__meta_add_unsafe(p_name,*iter);
  			else m_info.meta_add_value(index,*iter);
  		}
  	}
  private:
  	file_info & m_info;
  };
  
  class _meta_table_enum_wrapper_RG {
  public:
  	_meta_table_enum_wrapper_RG(file_info & p_info) : m_info(p_info) {}
  	template<typename t_values>
  	void operator() (const char * p_name,const t_values & p_values) {
  		if (p_values.get_count() > 0) {
  			if (!m_info.info_set_replaygain(p_name, *p_values.first())) {
  				t_size index = ~0;
  				for(t_values::const_iterator iter = p_values.first(); iter.is_valid(); ++iter) {
  					if (index == ~0) index = m_info.__meta_add_unsafe(p_name,*iter);
  					else m_info.meta_add_value(index,*iter);
  				}
  			}
  		}
  	}
  private:
  	file_info & m_info;
  };
  
  //! Purpose: building a file_info metadata table from loose input without search-for-existing-entry bottleneck
  class meta_table_builder {
  public:
  	typedef pfc::chain_list_v2_t<pfc::string8> t_entry;
  	typedef pfc::map_t<pfc::string8,t_entry,file_info::field_name_comparator> t_content;
  
  	t_content & content() {return m_data;}
  	t_content const & content() const {return m_data;}
  
  	void add(const char * p_name,const char * p_value,t_size p_value_len = ~0) {
  		if (file_info::g_is_valid_field_name(p_name)) {
  			_add(p_name).insert_last()->set_string(p_value,p_value_len);
  		}
  	}
  
  	void remove(const char * p_name) {
  		m_data.remove(p_name);
  	}
  	void set(const char * p_name,const char * p_value,t_size p_value_len = ~0) {
  		if (file_info::g_is_valid_field_name(p_name)) {
  			t_entry & entry = _add(p_name);
  			entry.remove_all();
  			entry.insert_last()->set_string(p_value,p_value_len);
  		}
  	}
  	t_entry & add(const char * p_name) {
  		if (!file_info::g_is_valid_field_name(p_name)) throw pfc::exception_bug_check_v2();//we return a reference, nothing smarter to do
  		return _add(p_name);
  	}
  	void finalize(file_info & p_info) const {
  		p_info.meta_remove_all();
  		m_data.enumerate(_meta_table_enum_wrapper(p_info));
  	}
  	void finalize_withRG(file_info & p_info) const {
  		p_info.meta_remove_all(); p_info.set_replaygain(replaygain_info_invalid);
  		m_data.enumerate(_meta_table_enum_wrapper_RG(p_info));
  	}
  
  	void from_info(const file_info & p_info) {
  		m_data.remove_all();
  		from_info_overwrite(p_info);
  	}
  	void from_info_withRG(const file_info & p_info) {
  		m_data.remove_all();
  		from_info_overwrite(p_info);
  		from_RG_overwrite(p_info.get_replaygain());
  	}
  	void from_RG_overwrite(replaygain_info info) {
  		replaygain_info::t_text_buffer buffer;
  		if (info.format_album_gain(buffer)) set("replaygain_album_gain", buffer);
  		if (info.format_track_gain(buffer)) set("replaygain_track_gain", buffer);
  		if (info.format_album_peak(buffer)) set("replaygain_album_peak", buffer);
  		if (info.format_track_peak(buffer)) set("replaygain_track_peak", buffer);
  	}
  	void from_info_overwrite(const file_info & p_info) {
  		for(t_size metawalk = 0, metacount = p_info.meta_get_count(); metawalk < metacount; ++metawalk ) {
  			const t_size valuecount = p_info.meta_enum_value_count(metawalk);
  			if (valuecount > 0) {
  				t_entry & entry = add(p_info.meta_enum_name(metawalk));
  				entry.remove_all();
  				for(t_size valuewalk = 0; valuewalk < valuecount; ++valuewalk) {
  					entry.insert_last(p_info.meta_enum_value(metawalk,valuewalk));
  				}
  			}
  		}
  	}
  	void reset() {m_data.remove_all();}
  private:
  
  	t_entry & _add(const char * p_name) {
  		return m_data.find_or_add(p_name);
  	}
  
  	t_content m_data;
  };
  