Skip to content
Snippets Groups Projects
pugixml.cpp 289 KiB
Newer Older
		if (length < 0) return status_io_error;
		size_t result = static_cast<size_t>(length);

		if (static_cast<length_type>(result) != length) return status_out_of_memory;

		// finalize
		out_result = result;

		return status_ok;
	}

	PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) 
	{
		// We only need to zero-terminate if encoding conversion does not do it for us
	#ifdef PUGIXML_WCHAR_MODE
		xml_encoding wchar_encoding = get_wchar_encoding();

		if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding))
		{
			size_t length = size / sizeof(char_t);

			static_cast<char_t*>(buffer)[length] = 0;
			return (length + 1) * sizeof(char_t);
		}
	#else
		if (encoding == encoding_utf8)
		{
			static_cast<char*>(buffer)[size] = 0;
			return size + 1;
		}
	#endif

		return size;
	}

	PUGI__FN xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding)
	{
		if (!file) return make_parse_result(status_file_not_found);

		// get file size (can result in I/O errors)
		size_t size = 0;
		xml_parse_status size_status = get_file_size(file, size);

		if (size_status != status_ok)
			return make_parse_result(size_status);
		size_t max_suffix_size = sizeof(char_t);

		char* contents = static_cast<char*>(xml_memory::allocate(size + max_suffix_size));
		{
			fclose(file);
			return make_parse_result(status_out_of_memory);
		}

		// read file in memory
		size_t read_size = fread(contents, 1, size, file);
			return make_parse_result(status_io_error);
		}

		xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size);
		return doc.load_buffer_inplace_own(contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding);
#ifndef PUGIXML_NO_STL
	template <typename T> struct xml_stream_chunk
	{
		static xml_stream_chunk* create()
		{
			void* memory = xml_memory::allocate(sizeof(xml_stream_chunk));
			
			return new (memory) xml_stream_chunk();
		}

		static void destroy(void* ptr)
		{
			xml_stream_chunk* chunk = static_cast<xml_stream_chunk*>(ptr);

			// free chunk chain
			while (chunk)
			{
Arseny Kapoulkine's avatar
Arseny Kapoulkine committed
				xml_stream_chunk* next_ = chunk->next;

				xml_memory::deallocate(chunk);
Arseny Kapoulkine's avatar
Arseny Kapoulkine committed

				chunk = next_;
			}
		}

		xml_stream_chunk(): next(0), size(0)
		{
		}

		xml_stream_chunk* next;
		size_t size;

		T data[xml_memory_page_size / sizeof(T)];
	};
	template <typename T> PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
	{
		buffer_holder chunks(0, xml_stream_chunk<T>::destroy);
		// read file to a chunk list
		size_t total = 0;
		xml_stream_chunk<T>* last = 0;
		while (!stream.eof())
		{
			// allocate new chunk
			xml_stream_chunk<T>* chunk = xml_stream_chunk<T>::create();
			if (!chunk) return status_out_of_memory;
			// append chunk to list
			if (last) last = last->next = chunk;
			else chunks.data = last = chunk;
			// read data to chunk
			stream.read(chunk->data, static_cast<std::streamsize>(sizeof(chunk->data) / sizeof(T)));
			chunk->size = static_cast<size_t>(stream.gcount()) * sizeof(T);
			// read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors
			if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;
			// guard against huge files (chunk size is small enough to make this overflow check work)
			if (total + chunk->size < total) return status_out_of_memory;
			total += chunk->size;
		}
		size_t max_suffix_size = sizeof(char_t);

		// copy chunk list to a contiguous buffer
		char* buffer = static_cast<char*>(xml_memory::allocate(total + max_suffix_size));
		if (!buffer) return status_out_of_memory;
		for (xml_stream_chunk<T>* chunk = static_cast<xml_stream_chunk<T>*>(chunks.data); chunk; chunk = chunk->next)
		{
			assert(write + chunk->size <= buffer + total);
			memcpy(write, chunk->data, chunk->size);
			write += chunk->size;
		}
		assert(write == buffer + total);
		// return buffer
		*out_buffer = buffer;
		*out_size = total;
	template <typename T> PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
		// get length of remaining data in stream
		typename std::basic_istream<T>::pos_type pos = stream.tellg();
		stream.seekg(0, std::ios::end);
		std::streamoff length = stream.tellg() - pos;
		if (stream.fail() || pos < 0) return status_io_error;
		size_t read_length = static_cast<size_t>(length);

		if (static_cast<std::streamsize>(read_length) != length || length < 0) return status_out_of_memory;
		size_t max_suffix_size = sizeof(char_t);

		// read stream data into memory (guard against stream exceptions with buffer holder)
		buffer_holder buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate);
		if (!buffer.data) return status_out_of_memory;
		stream.read(static_cast<T*>(buffer.data), static_cast<std::streamsize>(read_length));
		// read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors
		if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;
		size_t actual_length = static_cast<size_t>(stream.gcount());
		assert(actual_length <= read_length);
		*out_buffer = buffer.release();
		*out_size = actual_length * sizeof(T);
	template <typename T> PUGI__FN xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream<T>& stream, unsigned int options, xml_encoding encoding)
		void* buffer = 0;
		size_t size = 0;
		xml_parse_status status = status_ok;

		// if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits)
		if (stream.fail()) return make_parse_result(status_io_error);
		// load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory)
		if (stream.tellg() < 0)
		{
			stream.clear(); // clear error flags that could be set by a failing tellg
			status = load_stream_data_noseek(stream, &buffer, &size);
		}
		else
			status = load_stream_data_seek(stream, &buffer, &size);

		if (status != status_ok) return make_parse_result(status);
		xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size);
		
		return doc.load_buffer_inplace_own(buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding);
#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && !defined(__STRICT_ANSI__))
	PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
	PUGI__FN char* convert_path_heap(const wchar_t* str)
	{
		assert(str);

		// first pass: get length in utf8 characters
		size_t size = as_utf8_begin(str, length);
		char* result = static_cast<char*>(xml_memory::allocate(size + 1));
		if (!result) return 0;

		// second pass: convert to utf8
		as_utf8_end(result, size, str, length);
	PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
	{
		// there is no standard function to open wide paths, so our best bet is to try utf8 path
		char* path_utf8 = convert_path_heap(path);
		if (!path_utf8) return 0;

		// convert mode to ASCII (we mirror _wfopen interface)
		char mode_ascii[4] = {0};
		for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast<char>(mode[i]);

		// try to open the utf8 path
		FILE* result = fopen(path_utf8, mode_ascii);

		// free dummy buffer

	PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding)
	{
		if (!file) return false;

		xml_writer_file writer(file);
		doc.save(writer, indent, flags, encoding);


	PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer)
	{
		// check input buffer
		assert(contents || size == 0);

		// get actual encoding
		xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size);

		// get private buffer
		char_t* buffer = 0;
		size_t length = 0;

		if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory);
		
		// delete original buffer if we performed a conversion
		if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);

		// store buffer for offset_debug
		doc->buffer = buffer;

		// parse
		xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options);

		// remember encoding
		res.encoding = buffer_encoding;

		// grab onto buffer if it's our buffer, user is responsible for deallocating contents himself
		if (own || buffer != contents) *out_buffer = buffer;

		return res;
	}
	PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_)
	PUGI__FN void xml_writer_file::write(const void* data, size_t size)
		size_t result = fwrite(data, 1, size, static_cast<FILE*>(file));
		(void)!result; // unfortunately we can't do proper error handling here
	PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream): narrow_stream(&stream), wide_stream(0)
	PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream): narrow_stream(0), wide_stream(&stream)
	PUGI__FN void xml_writer_stream::write(const void* data, size_t size)
	{
		if (narrow_stream)
		{
			assert(!wide_stream);
			narrow_stream->write(reinterpret_cast<const char*>(data), static_cast<std::streamsize>(size));
		}
		else
		{
			assert(wide_stream);
			assert(size % sizeof(wchar_t) == 0);

			wide_stream->write(reinterpret_cast<const wchar_t*>(data), static_cast<std::streamsize>(size / sizeof(wchar_t)));
		}
	}
#endif

	PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0)
	PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr)
	PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***)
	{
	}
	PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const
		return _attr ? unspecified_bool_xml_attribute : 0;
	}
	PUGI__FN bool xml_attribute::operator!() const
	{
		return !_attr;
	}
	PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const
	PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const
	PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const
	PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const
	PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const
	PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const
	PUGI__FN xml_attribute xml_attribute::next_attribute() const
	{
		return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute();
	}
	PUGI__FN xml_attribute xml_attribute::previous_attribute() const
	{
		return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute();
	}
	PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const
		return (_attr && _attr->value) ? _attr->value : def;
		return impl::get_value_int(_attr ? _attr->value : 0, def);
	PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const
		return impl::get_value_uint(_attr ? _attr->value : 0, def);
	PUGI__FN double xml_attribute::as_double(double def) const
		return impl::get_value_double(_attr ? _attr->value : 0, def);
	PUGI__FN float xml_attribute::as_float(float def) const
		return impl::get_value_float(_attr ? _attr->value : 0, def);
		return impl::get_value_bool(_attr ? _attr->value : 0, def);
#ifdef PUGIXML_HAS_LONG_LONG
	PUGI__FN long long xml_attribute::as_llong(long long def) const
	{
		return impl::get_value_llong(_attr ? _attr->value : 0, def);
	}

	PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const
	{
		return impl::get_value_ullong(_attr ? _attr->value : 0, def);
	}
#endif

	PUGI__FN const char_t* xml_attribute::name() const
	{
		return (_attr && _attr->name) ? _attr->name : PUGIXML_TEXT("");
	}

	PUGI__FN const char_t* xml_attribute::value() const
	{
		return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT("");
	}

	PUGI__FN size_t xml_attribute::hash_value() const
	{
		return static_cast<size_t>(reinterpret_cast<uintptr_t>(_attr) / sizeof(xml_attribute_struct));
	}
	PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const
	PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs)
	PUGI__FN xml_attribute& xml_attribute::operator=(int rhs)
	PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs)
	PUGI__FN xml_attribute& xml_attribute::operator=(double rhs)
	PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs)
#ifdef PUGIXML_HAS_LONG_LONG
	PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs)
	{
		set_value(rhs);
		return *this;
	}

	PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs)
	{
		set_value(rhs);
		return *this;
	}
#endif

	PUGI__FN bool xml_attribute::set_name(const char_t* rhs)
		return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs);
	PUGI__FN bool xml_attribute::set_value(const char_t* rhs)
		return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
	PUGI__FN bool xml_attribute::set_value(unsigned int rhs)
		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
	PUGI__FN bool xml_attribute::set_value(double rhs)
		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
#ifdef PUGIXML_HAS_LONG_LONG
	PUGI__FN bool xml_attribute::set_value(long long rhs)
	{
		if (!_attr) return false;

		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
	}

	PUGI__FN bool xml_attribute::set_value(unsigned long long rhs)
	{
		if (!_attr) return false;

		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
	}
#endif

	PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs)
	PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs)
	PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p)
	PUGI__FN static void unspecified_bool_xml_node(xml_node***)
	{
	}
	PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const
		return _root ? unspecified_bool_xml_node : 0;
	}
	PUGI__FN bool xml_node::operator!() const
	{
		return !_root;
	}
	PUGI__FN xml_node::iterator xml_node::begin() const
	{
		return iterator(_root ? _root->first_child : 0, _root);
	}

	PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const
	{
		return attribute_iterator(_root ? _root->first_attribute : 0, _root);
	}

	PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const
	{
		return attribute_iterator(0, _root);
	}
	
	PUGI__FN xml_object_range<xml_node_iterator> xml_node::children() const
	{
		return xml_object_range<xml_node_iterator>(begin(), end());
	}
	PUGI__FN xml_object_range<xml_named_node_iterator> xml_node::children(const char_t* name_) const
	{
		return xml_object_range<xml_named_node_iterator>(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_));
	PUGI__FN xml_object_range<xml_attribute_iterator> xml_node::attributes() const
	{
		return xml_object_range<xml_attribute_iterator>(attributes_begin(), attributes_end());
	}
	PUGI__FN bool xml_node::operator==(const xml_node& r) const
	PUGI__FN bool xml_node::operator!=(const xml_node& r) const
	PUGI__FN bool xml_node::operator<(const xml_node& r) const
	PUGI__FN bool xml_node::operator>(const xml_node& r) const
	PUGI__FN bool xml_node::operator<=(const xml_node& r) const
	PUGI__FN bool xml_node::operator>=(const xml_node& r) const
	{
		return (_root && _root->name) ? _root->name : PUGIXML_TEXT("");
	}

		return _root ? PUGI__NODETYPE(_root) : node_null;
	{
		return (_root && _root->value) ? _root->value : PUGIXML_TEXT("");
	}
	
	PUGI__FN xml_node xml_node::child(const char_t* name_) const
	{
		if (!_root) return xml_node();

		for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
			if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
	PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const
	{
		if (!_root) return xml_attribute();

		for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)
				return xml_attribute(i);
		
		return xml_attribute();
	}
	
	PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const
	{
		if (!_root) return xml_node();
		
		for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)
			if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
		return _root ? xml_node(_root->next_sibling) : xml_node();
	PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const
	{
		if (!_root) return xml_node();
		
		for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c)
			if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
	PUGI__FN xml_node xml_node::previous_sibling() const
	{
		if (!_root) return xml_node();
		
		if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c);
		else return xml_node();
	}

	{
		return _root ? xml_node(_root->parent) : xml_node();
	}

		return _root ? xml_node(&impl::get_document(_root)) : xml_node();
	PUGI__FN xml_text xml_node::text() const
	{
		return xml_text(_root);
	}
	PUGI__FN const char_t* xml_node::child_value() const
	{
		if (!_root) return PUGIXML_TEXT("");
		
		for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
			if (i->value && impl::is_text_node(i))
				return i->value;

		return PUGIXML_TEXT("");
	}

	PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const
		return child(name_).child_value();
	PUGI__FN xml_attribute xml_node::first_attribute() const
	{
		return _root ? xml_attribute(_root->first_attribute) : xml_attribute();
	}

	PUGI__FN xml_attribute xml_node::last_attribute() const
	{
		return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute();
	}

	{
		return _root ? xml_node(_root->first_child) : xml_node();
	}

	{
		return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node();
	}

	PUGI__FN bool xml_node::set_name(const char_t* rhs)
	{
		switch (type())
		{
		case node_pi:
		case node_declaration:
		case node_element:
			return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs);
	PUGI__FN bool xml_node::set_value(const char_t* rhs)
	{
		switch (type())
		{
		case node_pi:
		case node_cdata:
		case node_pcdata:
		case node_comment:
			return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs);
	PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_)
	{
		if (type() != node_element && type() != node_declaration) return xml_attribute();
		
		xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
		if (!a) return xml_attribute();

		impl::append_attribute(a._attr, _root);
	PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_)
	{
		if (type() != node_element && type() != node_declaration) return xml_attribute();
		
		xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
		impl::prepend_attribute(a._attr, _root);
	PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr)
		if (type() != node_element && type() != node_declaration) return xml_attribute();
		if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
		xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
		if (!a) return xml_attribute();

		impl::insert_attribute_after(a._attr, attr._attr, _root);
	PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr)
		if (type() != node_element && type() != node_declaration) return xml_attribute();
		if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
		xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
		if (!a) return xml_attribute();

		impl::insert_attribute_before(a._attr, attr._attr, _root);
	PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto)
	{
		if (!proto) return xml_attribute();

		xml_attribute result = append_attribute(proto.name());
		result.set_value(proto.value());

		return result;
	}

	PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto)
	{
		if (!proto) return xml_attribute();

		xml_attribute result = prepend_attribute(proto.name());
		result.set_value(proto.value());

		return result;
	}

	PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr)
	{
		if (!proto) return xml_attribute();

		xml_attribute result = insert_attribute_after(proto.name(), attr);
		result.set_value(proto.value());

		return result;
	}

	PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr)
	{
		if (!proto) return xml_attribute();

		xml_attribute result = insert_attribute_before(proto.name(), attr);
		result.set_value(proto.value());

		return result;
	}

	PUGI__FN xml_node xml_node::append_child(xml_node_type type_)
		if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
		xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
		if (!n) return xml_node();

		impl::append_node(n._root, _root);
		if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
	PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_)
		if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
		xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
		if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
	PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node)
		if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
		if (!node._root || node._root->parent != _root) return xml_node();
	
		xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));