/*

Copyright (c) 2002-2005, Yauheni Akhotnikau
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

- The name of the author may not be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.

*/

/*!
	\since v.1.4.0
	\brief Стандарная реализация интерфейса %trace_stream_t.
*/

#include <vector>
#include <algorithm>
#include <functional>
#include <stdexcept>
#include <cstring>

#include <zlib.h>

#include <cpp_util_2/h/lexcast.hpp>
#include <cpp_util_2/lexcasts/h/util.hpp>

#include <oess_1/defs/h/ex.hpp>

#include <oess_1/io/h/mem_buf.hpp>
#include <oess_1/io/h/fixed_mem_buf.hpp>
#include <oess_1/io/h/bin_data_size.hpp>
#include <oess_1/file/h/file.hpp>

#include <oess_1/stdsn/h/ent_std.hpp>

#include <oess_1/db/storage/h/errno.hpp>

#include <oess_1/db/storage/trx/h/find_all_trace_files.hpp>
#include <oess_1/db/storage/trx/h/std_trace_stream.hpp>

#include <oess_1/debug/h/time_meter.hpp>

#define OPT_COUT(stream_ptr) !(stream_ptr) ? std::cout : *(stream_ptr)

namespace oess_1 {

namespace db {

namespace storage {

namespace trx {

namespace impl
{

/*! \name Константы, которые определяют расположение отдельных
	полей в заголовке страницы. */
//! \{

//! Смещение, с которого начинается контрольная сумма.
const oess_1::uint_t	checksum_field_offset = 0;
//! Длина контрольной суммы.
const oess_1::uint_t checksum_field_size = 
	oess_1::io::bin_data_size_t< oess_1::uint_t >::image_size;

//! Смещение, с которого начинается сам заголовок.
const oess_1::uint_t header_field_offset = checksum_field_size;
//! Длина заголовка.
const oess_1::uint_t header_field_size =
	oess_1::io::bin_data_size_t< oess_1::uint_t >::image_size * 7;
	
//! Смещение, с которого начинаются данные на странице.
const oess_1::uint_t data_field_offset =
	header_field_offset + header_field_size;
//! \}

//
// calc_checksum
//

/*!
	\since v.1.4.0
	\brief Вспомогательная функция для вычисления контрольной суммы.

	Контрольная сумма вычисляется по алгоритму adler32.

	\return значение контрольной суммы.
*/
oess_1::uint_t
calc_checksum(
	//! Данные, над которыми нужно проводить вычисления.
	const oess_1::char_t * what,
	//! Объем данных.
	oess_1::uint_t size )
	{
		uLong v = adler32( 0L, Z_NULL, 0 );
		v = adler32( v,
			reinterpret_cast< const Bytef * >( what ), size );

		return static_cast< oess_1::uint_t >( v );
	}

//
// create_trace_file
//

/*!
	\since v.1.4.0
	\brief Создает новый, пустой trace-файл.
*/
void
create_trace_file(
	//! Физической имя БД для которой нужно определить имя trace-файла.
	const std::string & physic_name,
	//! Порядковый номер trace-файла.
	oess_1::uint_t file_ordinal )
	{
		oess_1::file::file_t file;
		file.create( oess_1::db::storage::trx::create_trace_file_name(
				physic_name, file_ordinal ) );
	}

//
// destroy_trace_files
//

/*!
	\since v.1.4.0
	\brief Удаляет указанные trace-файлы.

	Исключения, производные от oess_1::ex_t наружу не выпускаются,
	но их описания накапливаются в \a errors.
*/
void
destroy_trace_files(
	//! Физическое имя БД с которой происходит работа.
	const std::string & physic_name,
	//! Номера trace-файлов для удаления.
	const trace_file_number_set_t & all_traces,
	//! Приемник описаний перехваченных в процессе работы исключений.
	std::string & errors )
	{
		for( trace_file_number_set_t::const_iterator
				it = all_traces.begin(), it_end = all_traces.end();
				it != it_end; ++it )
			{
				// Сейчас игнорируем все ошибки, т.к. пока мы
				// начинаем цикл кто-то уже мог удалить часть
				// trace-файлов.
				try
					{
						oess_1::file::file_t::remove(
								oess_1::db::storage::trx::
										create_trace_file_name(
												physic_name, *it ) );
					}
				catch( const oess_1::ex_t & x )
					{
						errors += std::string( x.what() ) + ";";
					}
			}
	}

//
// check_trace_file_count
//

/*!
	\since v.1.4.0
	\brief Проверяет состав trace-файлов для указанного режима.

	\par Режим trace_all
	Должно быть не менее одного trace-файла.

	\par Режим trace_last
	Должен быть только один trace-файл с номером 0.

	\return 0, если все нормально. Код ошибки, если количество или состав
		trace-файлов не соответствуют \a trace_mode.
*/
err_code_t
check_trace_file_count(
	//! Режим сохранения восстановочной информации для проверяемой БД.
	const trace_mode_t & trace_mode,
	//! Состав trace-файлов для этой БД.
	const trace_file_number_set_t & traces )
	{
		if( trace_all == trace_mode )
			{
				if( 0 == traces.size() )
					{
						return oess_1_make_err(
								oess_1::db::storage::err::c_trace_file_not_found,
								"must be at least one trace file in "
										"trace_all mode" );
					}
			}
		else if( trace_last == trace_mode )
			{
				if( 1 != traces.size() || 0 != *( traces.begin() ) )
					{
						return oess_1_make_err(
								oess_1::db::storage::err::c_trace_file_not_found,
								"must be only one trace file in "
										"trace_last mode" );
					}
			}

		return err_code_t( __FILE__, __LINE__ );
	}


//
// chain_ostream_t
//

/*!
	\since v.1.4.0
	\brief Выходной поток, который скрывает в себе детали распределения
		образа транзакции по страницам trace-файла.

	Если при выталкивании образа требуется завершить текущий trace-файл и
	начать новый, то chain_ostream_t выполняет это автоматически.
	Номер созданного trace-файла помещается в переданное в конструкторе
	множество номеров trace-файлов. В результате, после завершения работы
	объекта этого класса получается новое множество номеров trace-файлов,
	в котором находятся номера созданных trace-файлов.

	Если при открытии текущего trace-файла выясняется, что его размер не
	кратен размеру страницы, то trace-файл автоматически выравнивается
	на границу страницы.

	Для сохранения образа транзакции необходимо вызывать метод
	append.

	\note В работе класса используется предположение, что указатель всегда
	будет находиться в конце файла. Поэтому не производится позиционирование
	указателя перед выполнением записи в файл.

	\par Формат заголовка одной страницы.
\code
// Значение контрольной суммы всей страницы.
oess_1::uint_t	m_checksum;
// Порядковый номер этой страницы в образе всей транзакции.
oess_1::uint_t	m_chain_ordinal;
// Общее количество страниц в образе транзакции.
oess_1::uint_t m_chain_size;
// Длина исходного образа транзакции в байтах.
oess_1::uint_t m_image_len;
// Контрольная сумма исходного образа.
oess_1::uint_t m_image_checksum;
oess_1::uint_t m_reserved[ 3 ];
\endcode
*/
class	chain_ostream_t
	:	protected oess_1::io::obinstream_t
	{
	public :
		//! Инициализирующий конструктор.
		/*!
			Сразу открывает текущий trace-файл и, если нужно, обеспечивает
			его выравнивание.
		*/
		chain_ostream_t(
			//! Физическое имя БД, с которой происходит работа.
			const std::string & physic_name,
			//! Множество номеров существующих trace-файлов.
			//! Не должно быть пустым.
			trace_file_number_set_t & trace_file_numbers,
			//! Размер одной страницы trace-файла в байтах.
			oess_1::uint_t trace_file_page_size,
			//! Максимальная длина одного trace-файла в байтах.
			oess_1::uint_t trace_file_size_limit,
			//! Количество страниц, которые могут быть буферизированны
			//! в памяти перед записью на диск.
			oess_1::uint_t trace_stream_buffered_pages );
		virtual ~chain_ostream_t();

		/*!
			Сохранить описание транзакции в файле.
		*/
		virtual void
		append(
			const oess_1::char_t * image,
			oess_1::uint_t image_size );

	protected :
	//! \name Реализация унаследованных методов.
	//! \{
		virtual oess_1::char_t *
		out_reserve(
			size_t item_count,
			size_t item_size,
			size_t & item_available );

		virtual void
		out_shift(
			size_t item_count,
			size_t item_size );
	//! \}

	protected :
		//! Физическое имя БД, для которой используется trace-файл.
		std::string	m_physic_name;
		//! Множество номеров существующих trace-файлов.
		//! Не должно быть пустым.
		trace_file_number_set_t & m_trace_file_numbers;
		//! Размер одной страницы trace-файла в байтах.
		oess_1::uint_t	m_trace_file_page_size;
		//! Максимальный размер одного trace-файла в байтах.
		const oess_1::uint_t	m_trace_file_size_limit;
		//! Текущий trace-файл, с которым идет работа.
		oess_1::file::file_t	m_file;

	/*! \name Параметры транзакции.

		Значения, которые остаются корректными и актуальными
		только во время работы метода append().

		\{ */

		//! Длина образа транзакции.
		oess_1::uint_t m_image_len;
		//! Контрольная сумма образа транзакции.
		oess_1::uint_t m_image_checksum;

		//! Номер текущей страницы внутри транзакции.
		oess_1::uint_t	m_chain_page_ordinal;
		//! Количество всех страниц trace-файла для образа транзакции.
		oess_1::uint_t	m_chain_page_count;

	/*! \} */

		//! Образ текущей страницы.
		/*!
			В процессе формирования страницы не содержит заголовка.
			Заголовок добавляется только перед вытеснением страницы
			в файл.
		*/
		std::vector< oess_1::char_t >	m_buf;
		//! Количество занятых байт в образе страницы.
		/*!
			Учитывает так же поля checksum и header.
		*/
		oess_1::uint_t	m_buf_size;

		//! Буфер для промежуточного сохранения страниц перед записью.
		oess_1::io::mem_buf_t	m_buffered_pages;

		//! Определить размер одной страницы.
		oess_1::uint_t
		page_size() const
			{
				return m_trace_file_page_size;
			}

		//! Открыть файл для дописывания.
		/*!
			Если размер открытого файла не кратен размеру страницы,
			то производит выравнивание путем дописывания в конец
			файла необходимого количества нулей.
		*/
		void
		open_file_to_append(
			//! Порядковый номер trace-файла.
			oess_1::uint_t file_ordinal );

		//! Выполнить подготовку к сохранению новой транзакции.
		/*!
			Формирует значения полей m_image_len, m_image_checksum.

			Вычисляет значение m_chain_page_ordinal.

			Обнуляет m_chain_page_ordinal.
		*/
		void
		prepare_new_transaction(
			const oess_1::char_t * image,
			oess_1::uint_t image_size );

		//! Вытолкнуть текущую страницу в файл.
		/*!
			Заполняет m_buf заголовком и checksum, после чего записывает
			страницу в конец текущего trace-файла. Если же текущий
			trace-файл уже достиг максимального размера, то
			автоматически начинает новый trace-файл.
		*/
		void
		push_out_current_page();

		//! Выполнят заполнение текущей страницы.
		/*!
			Выставляет поля заголовка и вычисляет checksum.

			Увеличивает значение m_chain_page_ordinal на единицу.
		*/
		void
		complete_current_page();

		//! Запись буферизированных страниц на диск.
		/*!
			Если текущий trace-файл оказывается заполненым, то осуществляется
			автоматический переход к следующему файлу.
		*/
		void
		flush_buffered_pages();

		//! Перейти к следующему trace-файлу.
		/*!
			Объявлен виртуальным методом для того, чтобы дать возможность
			производным классам выполнить какие-то действия при переходе
			к следующему trace-файлу.
		*/
		virtual void
		switch_to_next_trace_file();

		//! Проинициализировать m_buf для новой страницы.
		/*!
			Обнуляет m_buf и выставляет m_buf_size так, что
			страница содержит только заголовок и checksum.
		*/
		void
		start_new_page()
			{
				std::memset( &m_buf[ 0 ], 0, m_buf.size() );
				m_buf_size = data_field_offset;
			}

		//! Получить номер файла, который должен использоваться
		//! для дописывания новой транзакции в конец trace-stream.
		/*!
			\return максимальный элемент из m_trace_file_numbers.
		*/
		oess_1::uint_t
		current_file_to_append() const
			{
				if( !m_trace_file_numbers.size() )
					OESS_THROW_PHYSIC(
							oess_1::db::storage::err::c_trace_file_not_found,
							"no trace files for: " << m_physic_name )

				return *( m_trace_file_numbers.rbegin() );
			}
	};

chain_ostream_t::chain_ostream_t(
	const std::string & physic_name,
	trace_file_number_set_t & trace_file_numbers,
	oess_1::uint_t trace_file_page_size,
	oess_1::uint_t trace_file_size_limit,
	oess_1::uint_t trace_stream_buffered_pages )
	:	m_physic_name( physic_name )
	,	m_trace_file_numbers( trace_file_numbers )
	,	m_trace_file_page_size( trace_file_page_size )
	,	m_trace_file_size_limit( trace_file_size_limit )
	,	m_image_len( 0 )
	,	m_image_checksum( 0 )
	,	m_chain_page_ordinal( 0 )
	,	m_chain_page_count( 0 )
	,	m_buf( trace_file_page_size, '\0' )
	,	m_buf_size( 0 )
	,	m_buffered_pages( trace_file_page_size * trace_stream_buffered_pages )
	{
		start_new_page();

		open_file_to_append( current_file_to_append() );
	}

chain_ostream_t::~chain_ostream_t()
	{}

void
chain_ostream_t::append(
	const oess_1::char_t * image,
	oess_1::uint_t image_len )
	{
		prepare_new_transaction( image, image_len );

		// Помещаем образ в файл.
		write( image, image_len );

		// Флушируем текущее содержимое.
		push_out_current_page();
		flush_buffered_pages();
	}

oess_1::char_t *
chain_ostream_t::out_reserve(
	size_t item_count,
	size_t item_size,
	size_t & item_available )
	{
		do
			{
				item_available = ( page_size() - m_buf_size ) / item_size;
				if( !item_available )
					// Место освободиться после выталкивания текущей страницы.
					push_out_current_page();
			}
		while( !item_available );

		return &m_buf[ m_buf_size ];
	}

void
chain_ostream_t::out_shift(
	size_t item_count,
	size_t item_size )
	{
		m_buf_size += item_count * item_size;
	}

void
chain_ostream_t::open_file_to_append(
	oess_1::uint_t file_ordinal )
	{
		m_file.open(
				oess_1::db::storage::trx::create_trace_file_name(
						m_physic_name, file_ordinal ),
				false );

		m_file.seek( m_file.length() );

		oess_1::uint_t tail_size = m_file.length() % page_size();
		if( tail_size )
			{
				// Нужно выравнивать.
				std::vector< oess_1::char_t >	padding(
					page_size() - tail_size, '\0' );
				m_file.write( &padding[ 0 ], padding.size() );
			}
	}

void
chain_ostream_t::prepare_new_transaction(
	const oess_1::char_t * image,
	oess_1::uint_t image_size )
	{
		// Нужно сохранить длину и контрольную сумму образа транзакции.
		m_image_len = image_size;
		m_image_checksum = calc_checksum( image, image_size );

		// Для новой транзакции страницы в цепочке должны нумероваться с нуля.
		m_chain_page_ordinal = 0;

		// Определям количество страниц, которые потребуются для сохранения
		// всей транзакции.
		oess_1::uint_t data_size_on_page = page_size() - data_field_offset;
		m_chain_page_count = m_image_len / data_size_on_page +
				( m_image_len % data_size_on_page ? 1 : 0 );
	}

void
chain_ostream_t::push_out_current_page()
	{
		complete_current_page();

		if( m_buffered_pages.size() == m_buffered_pages.capacity() )
			flush_buffered_pages();

		m_buffered_pages.write( &m_buf[ 0 ], page_size() );

		start_new_page();
	}

void
chain_ostream_t::complete_current_page()
	{
		oess_1::io::ofixed_mem_buf_t ostream( &m_buf[ 0 ], page_size() );

		oess_1::uint_t checksum_value = 0;

		//optimization: более выгодно записывать весь заголовок одним
		//обращением к write.
		oess_1::uint_t all_header[] =
			{
				checksum_value, m_chain_page_ordinal, m_chain_page_count,
				m_image_len, m_image_checksum
			};
		ostream.write( all_header,
				sizeof( all_header ) / sizeof( all_header[ 0 ] ) );

		ostream.set_pos( header_field_offset );
		checksum_value = calc_checksum(
			ostream.current_ptr(), page_size() - header_field_offset );

		ostream.set_pos( checksum_field_offset );
		ostream << checksum_value;

		++m_chain_page_ordinal;
	}

void
chain_ostream_t::flush_buffered_pages()
	{
		oess_1::uint_t remaining = m_buffered_pages.size();
		const oess_1::char_t * page = m_buffered_pages.ptr();

		while( remaining )
			{
				if( m_file.length() >= m_trace_file_size_limit )
					{
						// Нужно переходить к следующему trace-файлу.
						switch_to_next_trace_file();
					}
				else
					{
						oess_1::uint_t portion_size = m_trace_file_size_limit -
								m_file.length();
						if( portion_size > remaining )
							portion_size = remaining;

						m_file.write( page, portion_size );

						page += portion_size;
						remaining -= portion_size;
					}
			}

		m_buffered_pages.change_size( 0 );
	}

void
chain_ostream_t::switch_to_next_trace_file()
	{
		oess_1::uint_t file = current_file_to_append() + 1;
		create_trace_file( m_physic_name, file );
      
		// Этот номер должен быть сохранен на будущее.
		m_trace_file_numbers.insert( file );
      
		m_file.close();
		open_file_to_append( file );
	}

//
// last_trx_only_chain_ostream_t
//

/*!
	\since v.1.4.0
	\brief Выходной поток, который сохраняет в себе только описание
		последней транзакции.

	Данный класс предназначен для поддержки режима сохранения
	восстановочной информации oess_1::db::storage::trace_last.

	Формат trace-stream остается точно таким же, как и в случае с
	chain_ostream_t. Различие состоит в том, что при фиксации новой
	транзакции:

	- текущий trace-файл усекается до нулевой длины (т.е. все предыдущее
	содержимое выбрасывается);
	- транзакция сохраняется в текущий trace-файл. Новые trace-файлы не
	создаются, т.к. для текущего trace-файла нет органичений на длину.
*/
class	last_trx_only_chain_ostream_t
	:	public chain_ostream_t
	{
		typedef chain_ostream_t base_type_t;

	public :
		//! Инициализирующий конструктор.
		/*!
			В trace_file_numbers должен быть всего один элемент
			и этот элемент должен быть нулевым. В противном случае
			порождается исключение.
		*/
		last_trx_only_chain_ostream_t(
			//! Физическое имя БД, с которой происходит работа.
			const std::string & physic_name,
			//! Множество номеров существующих trace-файлов.
			//! Не должно быть пустым.
			trace_file_number_set_t & trace_file_numbers,
			//! Размер одной страницы trace-файла в байтах.
			oess_1::uint_t trace_file_page_size,
			//! Количество страниц, которые могут быть буферизированны
			//! в памяти перед записью на диск.
			oess_1::uint_t trace_stream_buffered_pages );
		virtual ~last_trx_only_chain_ostream_t();

		/*!
			Сохранить описание транзакции в файле.
		*/
		virtual void
		append(
			const oess_1::char_t * image,
			oess_1::uint_t image_size );

	protected :
		//! Обработка попытки перейти к следующему trace-файлу.
		/*!
			Порождает исключение, т.к. в этом режиме нельзя иметь более
			одного trace-файла.
		*/
		virtual void
		switch_to_next_trace_file();

		//! Урезать размер текущего trace-файла до нуля.
		void
		truncate_trace_file();
	};

last_trx_only_chain_ostream_t::last_trx_only_chain_ostream_t(
	const std::string & physic_name,
	trace_file_number_set_t & trace_file_numbers,
	oess_1::uint_t trace_file_page_size,
	oess_1::uint_t trace_stream_buffered_pages )
	:	base_type_t(
			physic_name, trace_file_numbers,
			trace_file_page_size,
			static_cast< oess_1::uint_t >( -1 ),
			trace_stream_buffered_pages )
	{
		err_code_t error = check_trace_file_count( trace_last,
				trace_file_numbers );

		if( error )
			OESS_THROW_PHYSIC( error.m_code, error.m_desc )
	}

last_trx_only_chain_ostream_t::~last_trx_only_chain_ostream_t()
	{}

void
last_trx_only_chain_ostream_t::append(
	const oess_1::char_t * image,
	oess_1::uint_t image_len )
	{
		truncate_trace_file();

		// Все остальное сделает базовый класс.
		base_type_t::append( image, image_len );
	}

void
last_trx_only_chain_ostream_t::switch_to_next_trace_file()
	{
		OESS_THROW_PHYSIC(
				oess_1::db::storage::err::c_trace_stream_full,
				"all transaction must fit into one trace file in "
				"trace_last mode" )
	}

void
last_trx_only_chain_ostream_t::truncate_trace_file()
	{
		m_file.seek( 0 );
		m_file.chsize( 0 );
	}

//
// reverse_loader_t
//

/*!
	\since v.1.4.0
	\brief Класс для чтения транзакций из trace-stream в обратном
		порядке.
*/
class reverse_loader_t
	:	private cpp_util_2::nocopy_t
	{
	public :
		//! Инициализирующий конструктор.
		reverse_loader_t(
			//! Физическое имя БД с которой нужно работать.
			const std::string & physic_name,
			//! Размер страницы trace-файла в байтах.
			oess_1::uint_t page_size,
			//! Множество номеров существующих trace-файлов.
			const trace_file_number_set_t & all_traces,
			//! Необязательны поток вывода информации о ходе
			//! выполнения обратного просмотра.
			std::ostream * verbose_stream = 0 );
		virtual ~reverse_loader_t();

		//! Прочитать очередную транзакцию.
		/*!
			При первом вызове пытается прочитать последнюю
			существующую транзакцию из trace-файла. При следующем	
			вызове -- предпоследнюю и т.д., пока не будет достигнуто
			начало trace-stream.	

			\attention Возвращаемый в \a image указатель остается
			действительным до следующего вызова next() и до
			разрушения объекта reverse_loader_t.
		*/
		virtual bool
		next(
			//! Приемник указателя на образ транзакции.
			const oess_1::char_t * & image,
			//! Приемник размера транзакции.
			oess_1::uint_t & image_size,
			//! Приемник номеров trace-файлов из которых была
			//! извлечена актуальная информация. Фактически, это номера
			//! trace-файлов, в которых находился образ транзакции.
			trace_file_number_set_t & used_traces );

	private :
		//! Физическое имя БД с которой нужно работать.
		std::string	m_physic_name;
		//! Размер страницы trace-файла.
		oess_1::uint_t	m_page_size;

		//! Необязательны поток вывода информации о ходе
		//! выполнения обратного просмотра.
		std::ostream * m_verbose_stream;

		//! Множество номеров файлов, которые составляют оставшуюся
		//! часть trace-stream.
		/*!
			В самом начале это множество имеет значение параметра	
			\a all_traces конструктора. Затем, по мере продвижения по
			trace-stream из него изымаются номера уже обработанных
			файлов. В результате, это множество должно оказаться
			пустым.
		*/
		trace_file_number_set_t	m_all_traces;

		//! Признак того, что текущий файл открыт.
		bool	m_is_current_file_open;
		//! Номер текущего trace-файла.
		/*!
			Содержит актуальное значение только, если
			m_is_current_file_open == true.
		*/
		trace_file_number_t	m_current_file_number;
		//! Номер текущей страницы в trace-файле.
		/*!
			Содержит актуальное значение только, если
			m_is_current_file_open == true.
		*/
		oess_1::uint_t	m_current_file_page;
		/*!
			Содержимое текущего файла.

			Содержит актуальное значение только, если
			m_is_current_file_open == true.
		*/
		std::vector< oess_1::char_t >	m_current_file_content;

	//! \name Информация, которая оносится к текущей транзакции.
	//! \{
		//! Буфер, в котором накапливается образ транзакции.
		/*!
			Размер этого буфера устанавливается в нужное значение
			при обнаружении первой страницы транзакции (точнее, когда
			при обратном просмотре будет обнаружена последняя
			страница образа транзакции).
		*/
		std::vector< oess_1::char_t >	m_image;

		//! Порядковый номер текущей страницы в цепочке.
		/*!
			Устанавливается при нахождении первой страницы очередной
			транзакции, затем уменьшается на 1 при переходе
			к следующей странице.

			Отличается от m_current_ordinal тем, что m_current_ordinal
			содерживает порядковый номер, извлеченный из m_current_page.
			А значение m_chain_page_ordinal вычисляется самим
			классом reverse_loader_t.
		*/
		oess_1::uint_t	m_chain_page_ordinal;

		//! Длина образа транзакции.
		/*!
			Содержит корректное значение только после того,
			как будет найдена первая страница очередной транзакции.
		*/
		oess_1::uint_t	m_image_len;

		//! Контрольная сумма образа транзакции.
		/*!
			Содержит корректное значение только после того,
			как будет найдена первая страница очередной транзакции.
		*/
		oess_1::uint_t	m_image_checksum;

		/*!
			Множество номеров trace-файлов, из которых информация
			попала в образ текущей транзакции.

			Переинициализируется при обнаружении очередной транзакции.
		*/
		trace_file_number_set_t	m_used_traces;
	//! \}

	//! \name Информация, которая актуальна только для текущей страницы.
	//! \{
		//! Порядковый номер страницы в транзакции.
		oess_1::uint_t	m_current_ordinal;

		//! Общее количество страниц в транзакции.
		oess_1::uint_t m_current_count;

		//! Длина образа транзакции.
		oess_1::uint_t	m_current_image_len;

		//! Контрольная сумма образа транзакции.
		oess_1::uint_t	m_current_image_checksum;
	//! \}

		//! Попробовать открыть очередной trace-файл.
		/*!
			Перебирает множество m_all_traces в обратном порядке и
			пробует открыть очередной файл. После каждой попытке
			соответствующий номер trace-файла из m_all_traces
			удаляется.

			Отсутствующие файлы просто пропускаются (без попрождения
			исключений), а их номера вычеркиваются из m_all_traces.

			Номер первого успешно открытого файла помещается в
			m_current_file_number.

			Если множество m_all_traces оказывается пустым, то
			порождается исключение end_of_trace_ex_t.

			Если файл успешно открывается, то все его содержимое
			загружается в m_current_file_content.
		*/
		void
		try_open_next_file();

		//! Найти очередную транзакцию.
		/*!
			Если транзакция найдена, то все ее значение, включая image_type,
			находится в m_image.

			\return true, если транзакция найдена.
				false, если все trace-файлы были обработаны и нет
				больше транзакций.
		*/
		void
		find_trx();

		//! Переинициализировать переменные для начала нового поиска.
		void
		reinit_search();

		//! Найти первую страницу транзакции.
		/*!
			Если первая страница найдена, то инициализируются все
			атрибуты, относящиеся к текущей транзакции.

			Содержимое первой страницы транзакции поднимается в m_image.

			Возврат из функции происходит только, если найдено
			начало очередной транзакции. В проивном случае из
			функции происходит по исключению.
		*/
		void
		find_first_trx_page();

		//! Поднять всю транзакцию.
		/*!
			Должна вызываться после успешного возврата из
			find_first_trx_page().

			Возврат из функции происходит по извлечении всех страниц
			транзакции, либо по исключению при возникновении ошибок.
		*/
		void
		pop_up_trx();

		//! Поднять очередную страницу хранилища и проверить ее целостность.
		/*!
			\throw trace_file::broken_page_ex_t, если страница имеет меньший
				размер или у нее не совпадает контрольная сумма.
		*/
		void
		pop_up_and_check_page();

		//! Переход к следущей странице в trace-файле.
		/*!
			Увеличивается значение m_trx_current с учетом возможного
			завершения текущего файла. Если текущий файл исчерпан, но
			есть следующий trace-файл, то осуществляется переход к
			следующему trace-файлу.

			При исчерпании всех trace-файлов порождается исключение
			end_of_trace_ex_t.
		*/
		void
		go_to_next_page();

		//! Переход к следующей странице без исключения в конце trace.
		/*!
			Если в go_to_next_page обнаруживается конец всего trace-потока,
			то исключение end_of_trace_ex_t не порождается.
		*/
		void
		go_to_next_page_no_throw_at_end();

		//! Проверка того, что вся транзакция загружена в %m_image.
		bool
		is_all_trx_loaded() const;

		//! Проверка корректности содержимого %m_image.	
		/*!
			Проверяются размеры и контрольные суммы.

			\throw broken_trx_t, если образ транзакции в m_image
				не корректен.
		*/
		void
		check_trx_integrity() const;

		//! Проверка того, что текущая страница принадлежит
		//! текущей транзакции.
		bool
		is_current_trx_page() const;

		//! Сохранить данные из текущей страницы в %m_image.
		/*!
			Поскольку эти данные попадают в транзакцию, то номер
			текущего файла помещается в m_used_traces.
		*/
		void
		store_page_data();

		//! Возвратить размер одной страницы trace-файла.
		oess_1::uint_t
		page_size() const;
	};

//! \name Исключения для класса reverse_loader_t.
//! \{

//
// Поскольку эти исключения из reverse_loader_t не выпускаются,
// то они производятся от std::logic_error (для простоты), а
// не от oess_1::ex_t.
//

//! Достигнут конец всего trace-потока.
class	end_of_trace_ex_t
	:	public std::logic_error
	{
	typedef std::logic_error base_type_t;
	public :
		end_of_trace_ex_t()
			:	base_type_t( "end of all trace stream" )
			{}	
	};

//! Из trace-файла извлечена поврежденная страница.
class	broken_page_ex_t
	:	public std::logic_error
	{
	typedef std::logic_error base_type_t;
	public :
		broken_page_ex_t()
			:	base_type_t( "broken page extracted" )
			{}	
	};

//! Из trace-потока извлечена поврежденная транзакция.
class	broken_trx_ex_t
	:	public std::logic_error
	{
	typedef std::logic_error base_type_t;
	public :
		broken_trx_ex_t()
			:	base_type_t( "broken trx extracted" )
			{}	
	};

//! \}

//
// reverse_loader_t
//

reverse_loader_t::reverse_loader_t(
	const std::string & physic_name,
	oess_1::uint_t page_size,
	const trace_file_number_set_t & all_traces,
	std::ostream * verbose_stream )
	:	m_physic_name( physic_name )
	,	m_page_size( page_size )
	,	m_all_traces( all_traces )
	,	m_verbose_stream( verbose_stream )
	,	m_is_current_file_open( false )
	{
	}

reverse_loader_t::~reverse_loader_t()
	{}

bool
reverse_loader_t::next(
	const oess_1::char_t * & image,
	oess_1::uint_t & image_size,
	trace_file_number_set_t & used_traces )
	{
		try
			{
				if( !m_is_current_file_open )
					try_open_next_file();

				find_trx();

				image_size = m_image_len;
				image = &m_image[ 0 ];
				used_traces = m_used_traces;

				return true;
			}
		catch( const end_of_trace_ex_t & )
			{}

		return false;
	}

void
reverse_loader_t::try_open_next_file()
	{
		while( !m_is_current_file_open && m_all_traces.size() )
			{
				trace_file_number_t number = *( m_all_traces.rbegin() );

				std::string file_name = oess_1::db::storage::trx::
						create_trace_file_name( m_physic_name, number );
				if( oess_1::file::file_t::is_file_exists( file_name ) )
					{
						oess_1::file::file_t file;
						file.open( file_name, true );
						// После открытия файла мы должны:
						// Загрузить весь файл в ОП...
						m_current_file_content.resize( file.length(), 0 );
						file.read( &m_current_file_content[ 0 ],
								m_current_file_content.size() );

						// ...сделать текущей самую последнюю целую страницу.
						m_current_file_page =
								m_current_file_content.size() / page_size();
						if( m_current_file_page &&
								!( m_current_file_content.size() % page_size() ) )
							// Это как раз тот случай, когда файл содержит
							// хотя бы одну страницу и конец файла попадает
							// на границу страницы. В этом случае полученный
							// номер будет указывать за пределы файла. Поэтому
							// нужно уменьшить номер страницы на 1.
							--m_current_file_page;

						m_current_file_number = number;
						m_is_current_file_open = true;

						OPT_COUT( m_verbose_stream )
								<< file_name << ": trace-file open; "
								<< "current_file_page: " << m_current_file_page
								<< std::endl;
					}
				else
					OPT_COUT( m_verbose_stream )
							<< file_name << ": not exists; ignored"
							<< std::endl;

				m_all_traces.erase( number );
			}

		if( !m_is_current_file_open )
			{
				// Раз нет больше файлов, значит конец потока достигнут.
				throw end_of_trace_ex_t();
			}
	}

void
reverse_loader_t::find_trx()
	{
		// Перебираем все страницы в цикле.
		// Игнорируем битые страницы и транзакции.
		// Вылетаем только по исключениям.
		while( true )
			{
				// Поиск должен начинаться заново.
				reinit_search();

				try
					{
						find_first_trx_page();
						pop_up_trx();

						return;
					}
				// Эти исключения в данном контексте вполне уместны.
				// На следующей итерации мы просто начнем поиск заново
				// полностью проигнорировав все, что уже успели найти.
				catch( const broken_page_ex_t & )
					{
						// Ошибочную страницу нужно пропустить, чтобы
						// не инициировать бесконечный цикл.
						go_to_next_page();
					}
				catch( const broken_trx_ex_t & )
					{
					}
			}
	}

void
reverse_loader_t::reinit_search()
	{
		m_used_traces.clear();
	}

void
reverse_loader_t::find_first_trx_page()
	{
		// Просматриваем все страницы, пока не будет найдена
		// страница, у которой порядковый номер на единицу меньше
		// общего количества страниц транзакции -- это
		// и есть начало транзакции для обратного просмотра.
		while( true )
			{
				pop_up_and_check_page();
				if( m_current_ordinal + 1 == m_current_count )
					{
						m_image_len = m_current_image_len;
						m_image_checksum = m_current_image_checksum;

						// Отсчет цепочки должен начаться заново.
						m_chain_page_ordinal = m_current_ordinal;

						// В буфере должно быть место для всей транзакции.
						if( m_image.size() < m_image_len )
							m_image.resize( m_image_len, 0 );

						// Нам нужно извлечь значение этой страницы,
						// т.к. pop_up_trx перейдет к следующей странице.
						store_page_data();

						OPT_COUT( m_verbose_stream )
								<< m_physic_name << ":"
								<< m_current_file_number << ":"
								<< m_current_file_page << ": "
								<< "start of transaction found: "
								<< "page count: " << m_current_count
								<< ", image length: " << m_image_len
								<< ", image checksum: 0x"
								<< std::hex << m_image_checksum
								<< std::dec << std::endl;

						return;
					}
				else
					{
						OPT_COUT( m_verbose_stream )
								<< m_physic_name << ":"
								<< m_current_file_number << ":"
								<< m_current_file_page << ": "
								<< "part of broken transaction; "
								<< "page ordinal: " << m_current_ordinal
								<< "page count: " << m_current_count
								<< ", image length: " << m_image_len
								<< ", image checksum: 0x"
								<< std::hex << m_image_checksum
								<< std::dec << std::endl;

						go_to_next_page();
					}
			}
	}

void
reverse_loader_t::pop_up_trx()
	{
		// Просматриваем все страницы, пока транзакция не будет
		// загружена в m_image целиком.
		while( true )
			{
				if( is_all_trx_loaded() )
					{
						// Мы пока стоим на последней странице нашей
						// транзакции. А нужно перейти к следущей странице.
						go_to_next_page_no_throw_at_end();

						check_trx_integrity();

						// Все, транзакция успешно извлечена.
						return;
					}
				else
					{
						// Пора переходить к следующей странице.
						go_to_next_page();

						// Ожидаем, что у нее будет такой порядковый номер.
						if( m_chain_page_ordinal )
							--m_chain_page_ordinal;
						else
							// Транзакция оказалась битой, т.к. мы должны
							// были стоять на самой первой странице транзакции
							// и двигаться уже некуда.
							throw broken_trx_ex_t();

						pop_up_and_check_page();
						if( is_current_trx_page() )
							store_page_data();
						else
							// Транзакция оказалась битой, т.к. мы нашли
							// страницу, не принадлежащую нашей транзакции.
							throw broken_trx_ex_t();
					}
			}
	}

void
reverse_loader_t::pop_up_and_check_page()
	{
		// Из-за метода go_to_next_page_no_throw_at_end() может
		// оказаться, что конец потока уже достигнут.
		if( !m_is_current_file_open )
			throw end_of_trace_ex_t();

		oess_1::uint_t page_offset = m_current_file_page * page_size();

		if( page_offset + page_size() <= m_current_file_content.size() )
			{
				// Страницу можно прочитать полностью.
				// Извлекаем поля из заголовка страницы.
				oess_1::io::ifixed_mem_buf_t istream(
						&m_current_file_content[ page_offset ], page_size() );
      
				// Нужно проверить контрольную сумму.
				oess_1::uint_t page_checksum;
				istream >> page_checksum;
      
				oess_1::uint_t calculated_checksum = calc_checksum(
						istream.current_ptr(),
						page_size() - checksum_field_size );
				if( page_checksum != calculated_checksum )
					{
						OPT_COUT( m_verbose_stream ) << m_physic_name
								<< ":" << m_current_file_number
								<< ":" << m_current_file_page
								<< ": page checksum mismatch; "
								<< "header checksum: 0x"
								<< std::hex << page_checksum << std::dec
								<< ", real checksum: 0x"
								<< std::hex << calculated_checksum << std::dec
								<< std::endl;
						throw broken_page_ex_t();
					}
      
				// Страница корректна, поэтому извлекаем все поля из заголовка.
				istream >> m_current_ordinal
					>> m_current_count
					>> m_current_image_len
					>> m_current_image_checksum;
			}
		else
			{
				OPT_COUT( m_verbose_stream ) << m_physic_name
						<< ":" << m_current_file_number
						<< ":" << m_current_file_page
						<< ": not all page in file; "
						<< "page offset: " << page_offset
						<< ", file length: " << m_current_file_content.size()
						<< std::endl;

				// Эта страница некорректна, т.к. ее нельзя прочитать полностью.
				throw broken_page_ex_t();
			}
	}

void
reverse_loader_t::go_to_next_page()
	{
		// Из-за метода go_to_next_page_no_throw_at_end() может
		// оказаться, что конец потока уже достигнут.
		if( !m_is_current_file_open )
			throw end_of_trace_ex_t();

		if( !m_current_file_page )
			{
				// Нужно переходить к следующему файлу.
				m_is_current_file_open = false;

				try_open_next_file();
			}
		else
			// Страница есть в текущем файле.
			--m_current_file_page;
	}

void
reverse_loader_t::go_to_next_page_no_throw_at_end()
	{
		try
			{
				go_to_next_page();
			}
		catch( const end_of_trace_ex_t & )
			{}
	}

bool
reverse_loader_t::is_all_trx_loaded() const
	{
		return ( 0 == m_current_ordinal );
	}

void
reverse_loader_t::check_trx_integrity() const
	{
		oess_1::uint_t image_calculated_checksum = calc_checksum(
				&m_image[ 0 ], m_image_len );
		if( m_image_checksum != image_calculated_checksum )
			{
				OPT_COUT( m_verbose_stream ) << m_physic_name
						<< ":" << m_current_file_number
						<< ":" << m_current_file_page
						<< ": image checksum mismatch; "
						<< "header checksum: 0x"
						<< std::hex << m_image_checksum << std::dec
						<< ", real checksum: 0x"
						<< std::hex << image_calculated_checksum << std::dec
						<< std::endl;

				throw broken_trx_ex_t();
			}
	}

bool
reverse_loader_t::is_current_trx_page() const
	{
		if( m_chain_page_ordinal == m_current_ordinal )
			if( m_image_len == m_current_image_len &&
					m_image_checksum == m_current_image_checksum )
				return true;

		OPT_COUT( m_verbose_stream ) << m_physic_name
				<< ":" << m_current_file_number
				<< ":" << m_current_file_page
				<< ": is not a part of current transaction; "
				<< " expected ordinal: " << m_chain_page_ordinal
				<< ", page ordinal: " << m_current_ordinal
				<< ", page count: " << m_current_count
				<< ", expected image length: " << m_image_len
				<< ", image length: " << m_current_image_len
				<< ", expected image checksum: 0x"
				<< std::hex << m_image_checksum << std::dec
				<< ", image checksum: 0x"
				<< std::hex << m_current_image_checksum << std::dec
				<< std::endl;
		
		return false;
	}

void
reverse_loader_t::store_page_data()
	{
		oess_1::uint_t page_offset = m_current_file_page * page_size();

		oess_1::uint_t data_bytes_on_page = ( page_size() -
				data_field_offset );
		oess_1::uint_t pos = m_current_ordinal * data_bytes_on_page;

		if( pos + data_bytes_on_page > m_image_len )
			data_bytes_on_page = m_image_len - pos;

		std::copy(
				&m_current_file_content[
						page_offset + data_field_offset ],
				&m_current_file_content[
						page_offset + data_field_offset +
								data_bytes_on_page ],
				&m_image[ 0 ] + pos );

		// Поскольку из данного файла что-то в транзакцию попало,
		// то этот файл должен быть учен как актуальный.
		m_used_traces.insert( m_current_file_number );
	}

oess_1::uint_t
reverse_loader_t::page_size() const
	{
		return m_page_size;
	}

//FIX ME: здесь есть большой задел для увеличения производительности.  В
//методах serialize_item, deserialize_item создаются временные буфера для
//компрессии/декомпрессии данных. Причем создаются для каждой транзакции.
//Можно было бы создавать их при выполнении первой транзакции и сохранять в
//каком-то контексте. Это контекст должен затем сохраняться в
//std_trace_stream_t. Тогда получиться, что после стабилизации размеров
//временных буферов не будет расходываться время на их создание/удаление.

//
// initialize_compression
//

/*!
	Проверить, какой режим компрессии должен использоваться и, если
	необходимо выполнить инициализацию соответствующей библиотеки.
*/
void
initialize_compression( const config_t & config )
	{
	}

//
// use_zlib_compression
//
 
void
use_zlib_compression(
	//! Что архивировать.
	const oess_1::char_t * what,
	//! Исходный размер.
	oess_1::uint_t uncompressed_size,
	//! Приемник результата.
	std::vector< char_t > & compressed_data,
	//! Приемник размера результата.
	oess_1::uint_t & compressed_size )
	{
		compressed_data.reserve( compressBound( uncompressed_size ) );
		uLong dest_length = compressed_data.capacity();
		int z_result;
		z_result = compress2(
				reinterpret_cast< Bytef * >( &compressed_data.front() ),
				&dest_length,
				reinterpret_cast< const Bytef * >( what ),
				uncompressed_size,
				Z_BEST_SPEED );
		if( Z_OK != z_result )
			OESS_THROW_PHYSIC(
					oess_1::db::storage::err::c_trx_zlib_failure,
					"try to compress: " << uncompressed_size
					<< " bytes; compress() return code: " << z_result )

		compressed_size = static_cast< oess_1::uint_t >( dest_length );
	}

//
// serialize_item
//

/*!
	\brief Сериализовать объект %trace_stream_image_t и
		получить двоичный образ транзакции для сохранения
		в trace-stream.

	Осуществляется попытка применения метода компрессии
	all_zlib_compressed. Если результат получается меньше
	длины исходного образа, то сериализуется сжатый образ.
	В противном случае сериализуется исходный образ.
*/
void
serialize_item(
	//! Образ транзакции.
	const trace_stream_item_t & item,
	//! Режим компрессии, который нужно использовать.
	const trace_compression_mode_t & required_compression_mode,
	//! Приемник результирующего образа.
	oess_1::io::mem_buf_t & result_receiver )
	{
		// Сначала получаем образ item для того, чтобы попробовать
		// его заархивировать.
		oess_1::io::mem_buf_t item_image(
				static_cast< oess_1::uint_t >(
						1.2 * item.summary_length() ) );
		oess_1::stdsn::oent_std_t item_ent( item_image );
		item_ent << item;

		image_header_t::compression_mode_t compression_mode =
				trace_no_compression;
		oess_1::uint_t uncompressed_size = item_image.size();
		oess_1::uint_t compressed_size = uncompressed_size;
		const oess_1::char_t * image_to_store = item_image.ptr();

		std::vector< char_t > compressed_data;

		// Если заказано архивирование, то попробуем его применить.
		if( trace_no_compression != required_compression_mode )
			{
				if( trace_zlib_compression == required_compression_mode )
					use_zlib_compression( item_image.ptr(), uncompressed_size,
							compressed_data, compressed_size );
				else
					OESS_THROW_PHYSIC(
							oess_1::db::storage::err::c_trx_unknown_compression_mode,
							static_cast< oess_1::uint_t >( required_compression_mode )
							<< ": compression mode unknown or not implemented yet" )

				// Мы не обязательно могли получить выигрыш в объеме.
				// Если выигрыш есть, то используем заархивированный образ.
				if( compressed_size < uncompressed_size )
					{
						// В образ транзакции уйдет заархивированное значение.
						compression_mode = required_compression_mode;
						image_to_store = reinterpret_cast< const oess_1::char_t * >(
								&compressed_data[ 0 ] );
					}
				else
					// Используем неупакованное значение, т.к. это даст
					// больший выигрыш и сейчас и при воспроизведении.
					compressed_size = uncompressed_size;
			}

		image_header_t header( compression_mode, uncompressed_size,
				compressed_size );

		result_receiver.change_capacity( 32 /* приблизительный 
				размер образа header */
				+ compressed_size );
		oess_1::stdsn::oent_std_t result_ent( result_receiver );
		result_ent << header;
		result_receiver.write( image_to_store, compressed_size );
	}

//
// unpack_all_zlib_compressed
//

/*!
	\brief Десериализовать объект %trace_stream_image_t из
		заархивированного в режиме %all_zlib_compressed образа.
*/
void
unpack_all_zlib_compressed(
	//! Начало заархивированного представления.
	const oess_1::char_t * image,
	//! Длина заархивированного представления.
	oess_1::uint_t compressed_size,
	//! Длина исходного представления.
	oess_1::uint_t uncompressed_size,
	//! Приемник десериализованного представления.
	trace_stream_item_t & item )
	{
		std::vector< Bytef > unpacked_data( uncompressed_size );
		uLong dest_length = uncompressed_size;
		int z_result = uncompress( &unpacked_data.front(), &dest_length,
				reinterpret_cast< const Bytef * >( image ),
				compressed_size );
		if( Z_OK != z_result )
			OESS_THROW_PHYSIC(
					oess_1::db::storage::err::c_trx_zlib_failure,
					"try to uncompress: " << compressed_size
					<< " bytes, awaiting uncompressed size: "
					<< uncompressed_size
					<< "; uncompress() return code: " << z_result )
		if( uncompressed_size != dest_length )
			OESS_THROW_PHYSIC(
					oess_1::db::storage::err::c_unexpected,
					"data length mismatch after decompression; "
					"awaiting uncompressed_size: "
					<< uncompressed_size
					<< ", zlib uncompressed length: "
					<< dest_length )

		// Теперь можно выполнять десериализацию.
		oess_1::io::ifixed_mem_buf_t stream(
				reinterpret_cast< const oess_1::char_t * >(
						&unpacked_data.front() ),
				uncompressed_size );
		oess_1::stdsn::ient_std_t ient( stream );
		ient >> item;
	}

//
// deserialize_item
//

/*!
	\breif Десериализовать объект %trace_stream_image_t из
		поднятого из trace-stream образа транзакции.
*/
void
deserialize_item(
	//! Образ транзакции.
	const oess_1::char_t * image,
	//! Длина образа транзакции.
	oess_1::uint_t image_len,
	//! Приемник десериализованного представления.
	trace_stream_item_t & item )
	{
		oess_1::io::ifixed_mem_buf_t stream( image, image_len );
		oess_1::stdsn::ient_std_t ient( stream );

		// Сначала извлекаем заголовок, чтобы понять, что нужно
		// делать дальше.
		image_header_t image_header;
		ient >> image_header;

		if( trace_no_compression == image_header.compression_mode() )
			// Образ транзакции не заархивирован, поэтому его
			// можно просто поднять из оставшейся части буфера.
			ient >> item;
		else if( trace_zlib_compression == image_header.compression_mode() )
			unpack_all_zlib_compressed(
					stream.current_ptr(),
					image_header.compressed_size(),
					image_header.uncompressed_size(),
					item );
		else
			OESS_THROW_PHYSIC(
					oess_1::db::storage::err::c_trx_unknown_compression_mode,
					"compression mode: " << static_cast< oess_1::uint_t >(
							image_header.compression_mode() ) )
	}

//
// replay_completion_map_t
//

/*!
	\brief Битовая карта для определения уже восстановленных
		фрагментов основного файла.

	\attention Данная реализация жестко завязана на то, что	
		в одной странице никогда не будет более 8 блоков, изменения в
		которых восстанавливаются независимо.  
	\todo В принципе, можно разрешить, чтобы количество блоков
		на странице достигало 32. Этого можно достичь с помощью
		шаблонов, которые будут определять тип значения для
		m_masks.

	\note Для повышения быстродействия корректность параметров,
		которые передаются в методы is_* не проверяются.
*/
class	replay_completion_map_t
	:	public cpp_util_2::nocopy_t
	{
	public :
		//! Инициализирующий конструктор.
		/*!
			\throw oess_1::physic_ex_t, если page_size / block_size > 8.
		*/
		replay_completion_map_t(
			//! Количество страниц основного файла.
			oess_1::uint_t page_count,
			//! Размер одной страницы в байтах.
			oess_1::uint_t page_size,
			//! Размер одного блока в байтах.
			oess_1::uint_t block_size )
			:	m_all_block_count( page_count * ( page_size / block_size ) )
			,	m_completed_blocks( 0 )
			,	m_page_block_count( page_size / block_size )
			{
				// Значение маски, которая соответствует полностью
				// восстановленной страницы, зависит от количества
				// блоков на странице.
				switch( m_page_block_count )
					{
						case 1 : m_completed_page_mask = 0x1; break;
						case 2 : m_completed_page_mask = 0x3; break;
						case 3 : m_completed_page_mask = 0x7; break;
						case 4 : m_completed_page_mask = 0xf; break;
						case 5 : m_completed_page_mask = 0x1f; break;
						case 6 : m_completed_page_mask = 0x3f; break;
						case 7 : m_completed_page_mask = 0x7f; break;
						case 8 : m_completed_page_mask = 0xff; break;
						default :
							OESS_THROW_PHYSIC(
									oess_1::db::storage::err::
											c_invalid_page_block_count,
									m_page_block_count
									<< ": page_block_count must >= 1 and <= 8" )
					}

				m_masks.resize( page_count, 0 );
			}

		//! Нужно ли продолжать восстановление.
		/*!
			\return false, если все страницы хранилища восстановлены.
		*/
		bool
		is_need_continue() const
			{
				return ( m_all_block_count != m_completed_blocks );
			}

		//! Нужно ли продолжать восстановление указаной страницы.
		/*!
			\return false, если значение страницы уже полностью
				восстановлено.
		*/
		bool
		is_page_need_completion(
			//! Порядковый номер страницы в файле.
			oess_1::uint_t page_ordinal ) const
			{
				return ( m_completed_page_mask !=
						( m_completed_page_mask & m_masks[ page_ordinal ] ) );
			}

		//! Нужно ли восстанавливать значение этого фрагмента на
		//! указанной странице.
		/*!
			\return false, если значение фрагмента уже восстановлено.
		*/
		bool
		is_page_fragment_need_value(
			//! Порядковый номер страницы в файле.
			oess_1::uint_t page_ordinal,
			//! Индекс блока на странице.
			oess_1::uint_t block_index ) const
			{
				oess_1::uchar_t bit = ( 0x1 << block_index );
				return !( bit & m_masks[ page_ordinal ] );
			}

		//! Установить признак того, что значение указанного
		//! фрагмента на указанной странице восстановлено.
		void
		page_fragment_defined(
			//! Порядковый номер страницы в файле.
			oess_1::uint_t page_ordinal,
			//! Индекс блока на странице.
			oess_1::uint_t block_index )
			{
				oess_1::uchar_t bit = ( 0x1 << block_index );
				m_masks[ page_ordinal ] |= bit;

				//! Увеличиваем количество уже восстановленных блоков.
				++m_completed_blocks;
			}

		//! Отобразить текущее состояние в указанный поток.
		void
		dump( std::ostream & to );

	private :
		//! Общее количество блоков в основном файле.
		oess_1::uint_t	m_all_block_count;
		//! Количество установленных в 1 битов.
		oess_1::uint_t	m_completed_blocks;
		//! Количество блоков на одной странице.
		oess_1::uint_t	m_page_block_count;
		//! Маска, которая соответствует полностью восстановленной странице.
		/*! Значение маски определяется количеством блоков на странице. */
		oess_1::uchar_t m_completed_page_mask;

		//! Хранилище битовых масок для всех страниц.
		std::vector< oess_1::uchar_t >	m_masks;
	};

void
replay_completion_map_t::dump( std::ostream & to )
	{
		to << "replay_completion_map status: "
			<< "\tall_block_count: " << m_all_block_count << "\n"
			<< "\tcompleted_blocks: " << m_completed_blocks << "\n"
			<< "\tpage_block_count: " << m_page_block_count << "\n"
			<< "\tcompleted_page_mask: 0x" << std::hex
				<< static_cast< oess_1::ushort_t >( m_completed_page_mask )
				<< std::dec << "\n";

		for( oess_1::uint_t i = 0,
				i_max = m_all_block_count / m_page_block_count;
				i != i_max; )
			{
				char sz[ 32 ];
				std::sprintf( sz, "%08X", i );
				to << sz << ": ";

				for( oess_1::uint_t j = 0; j != 16 && i != i_max; ++j, ++i )
					{
						char hex_mask[ 16 ];
						std::sprintf( hex_mask, "%02x ",
								static_cast< unsigned int >( m_masks[ i ] ) );
						to << hex_mask;
					}

				to << "\n";
			}

		to << std::flush;
	}

//
// replay_context_t
//

/*!
	\brief Контекст, в котором осуществляется обратный просмотр.
*/
class	replay_context_t
	:	private cpp_util_2::nocopy_t
	{
	private :
		//! Объект, который физически будет восстанавливать
		//! основной файл.
		committer_t & 	m_committer;
		//! Размер одной страницы хранилища в байтах.
		oess_1::uint_t	m_page_size;
		//! Размер одного блока на странице в байтах.
		oess_1::uint_t m_block_size;
		//! Необязательный поток для выдачи информации о
		//! ходе восстановления.
		std::ostream * m_verbose_stream;

		//! Маска обработанных страниц.
		replay_completion_map_t	m_completion;

	public :
		//! Инициализирующий конструктор.
		replay_context_t(
			//! Объект, который физически будет восстанавливать
			//! основной файл.
			committer_t & committer,
			//! Общее количество страниц в основном файле.
			oess_1::uint_t total_page_count,
			//! Размер одной страницы в байтах.
			oess_1::uint_t page_size,
			//! Размер одного блока в байтах.
			oess_1::uint_t block_size,
			//! Необязательный поток для выдачи информации о
			//! ходе восстановления.
			std::ostream * verbose_stream )
			:	m_committer( committer )
			,	m_page_size( page_size )
			,	m_block_size( block_size )
			,	m_verbose_stream( verbose_stream )
			,	m_completion( total_page_count, page_size, block_size )
			{}

		//! Получить размер одной страницы хранилища в байтах.
		oess_1::uint_t
		page_size() const
			{
				return m_page_size;
			}

		//! Получить размер одного блока на странице хранилища в байтах.
		oess_1::uint_t
		block_size() const
			{
				return m_block_size;
			}

		//! Нужно ли продолжать восстановление.
		/*!
			\return false, если все страницы хранилища восстановлены.
		*/
		bool
		is_need_continue() const
			{
				return m_completion.is_need_continue();
			}

		//! Нужно ли продолжать восстановление указаной страницы.
		/*!
			\return false, если значение страницы уже полностью
				восстановлено.
		*/
		bool
		is_page_need_completion(
			//! Порядковый номер страницы в файле.
			oess_1::uint_t page_ordinal ) const
			{
				return m_completion.is_page_need_completion( page_ordinal );
			}

		//! Нужно ли восстанавливать значение этого фрагмента на
		//! указанной странице.
		/*!
			\return false, если значение фрагмента уже восстановлено.
		*/
		bool
		is_page_fragment_need_value(
			//! Порядковый номер страницы в файле.
			oess_1::uint_t page_ordinal,
			//! Индекс блока на странице.
			oess_1::uint_t block_index ) const
			{
				return m_completion.is_page_fragment_need_value(
						page_ordinal, block_index );
			}

		//! Установить признак того, что значение указанного
		//! фрагмента на указанной странице восстановлено.
		void
		page_fragment_defined(
			//! Порядковый номер страницы в файле.
			oess_1::uint_t page_ordinal,
			//! Индекс блока на странице.
			oess_1::uint_t block_index )
			{
				m_completion.page_fragment_defined(
						page_ordinal, block_index );
			}

		//! Получение доступа к объекту-коммитеру.
		committer_t &
		committer()
			{
				return m_committer;
			}

		//! Отобразить текущее состояние в отладочный поток.
		void
		dump()
			{
				if( m_verbose_stream )
					m_completion.dump( *m_verbose_stream );
			}
	};

//
// trx_page_delta_processor_t
//

/*!
	Предикат для std::for_each.

	Занимается обработкой очередного несовпадающего фрагмента
	на странице транзакции.
*/
class	trx_page_delta_processor_t
	:	public std::unary_function< const delta_info_shptr_t &, void >
	{
		//! Контекст на котором происходит работа.
		replay_context_t & m_ctx;
		//! Сюда будет помещено значение true, если хотя бы
		//! один фрагмент страницы был восстановлен.
		bool & m_is_applied;
		//! Порядковый номер восстанавливаемой страницы.
		oess_1::uint_t	m_page_ordinal;

	public :
		trx_page_delta_processor_t(
			replay_context_t & ctx,
			bool & is_applied,
			oess_1::uint_t page_ordinal )
			:	m_ctx( ctx )
			,	m_is_applied( is_applied )
			,	m_page_ordinal( page_ordinal )
			{}

		result_type
		operator()( argument_type a )
			{
				const std::string & delta = a->value();
				oess_1::uint_t block_count = delta.size() /
						m_ctx.block_size();

				for( oess_1::uint_t block_index = a->index(), i = 0;
						i != block_count;
						++block_index, ++i )
					if( m_ctx.is_page_fragment_need_value(
							m_page_ordinal, block_index ) )
						{
							m_ctx.committer().store_block(
									m_page_ordinal,
									block_index,
									delta.substr( i * m_ctx.block_size(),
											m_ctx.block_size() ) );
							m_ctx.page_fragment_defined( m_page_ordinal,
									block_index );
							m_is_applied = true;
						}
			}
	};

//
// trx_page_processor_t
//

/*!
	Предикат для std::for_each.

	Занимается обработкой очередной страницы транзакции.
*/
class	trx_page_processor_t
	:	public std::unary_function< const page_info_t &, void >
	{
		//! Контекст на котором происходит работа.
		replay_context_t & m_ctx;
		//! Сюда будет помещено значение true, если хотя бы
		//! один фрагмент страницы был восстановлен.
		bool & m_is_applied;

	public :
		trx_page_processor_t(
			replay_context_t & ctx,
			bool & is_applied )
			:	m_ctx( ctx )
			,	m_is_applied( is_applied )
			{}

		result_type
		operator()( argument_type a )
			{
				if( m_ctx.is_page_need_completion( a.ordinal() ) )
					{
						std::for_each( a.deltas().begin(), a.deltas().end(),
								trx_page_delta_processor_t(
										m_ctx, m_is_applied, a.ordinal() ) );
					}
			}
	};

//
// process_trx
//

/*!
	\brief Обработать очередную транзакцию.

	\return true, если при обработке этой транзакции что-то
		было восстановлено.
*/
static bool
process_trx(
	//! Описание транзакции, которую нужно обработать.
	const trace_stream_item_t & trx,
	//! Контекст, на котором происходит работа.
	replay_context_t & ctx )
	{
		bool is_applied = false;

		std::for_each( trx.pages().begin(), trx.pages().end(),
				trx_page_processor_t( ctx, is_applied ) );

		return is_applied;
	}

//
// calc_used_unused_traces
//

/*!
	\brief Формирует можества актуальных и не актуальных
		trace-файлов.
*/
static void
calc_used_unused_traces(
	//! Множество номеров всех trace-файлов в trace-stream.
	const trace_file_number_set_t & all_traces,
	//! Множество trace-файлов, которые были использованы
	//! при восстановлении БД.
	const trace_file_number_set_t & all_used_traces,
	//! Необязательный приемник множества номеров trace-файлов,
	//! которые реально были использованы при восстановлении БД.
	trace_file_number_set_t * used_traces,
	//! Нeобязательный приемник множества номеров trace-файлов,
	//! которые оказались либо не актуальными, либо вовсе не
	//! использовались.
	trace_file_number_set_t * unused_traces )
	{
		if( used_traces )
			*used_traces = all_used_traces;

		if( unused_traces )
			{
				unused_traces->clear();
				std::set_difference(
						all_traces.begin(), all_traces.end(),
						all_used_traces.begin(), all_used_traces.end(),
						std::inserter( *unused_traces,
								unused_traces->begin() ) );
			}
	}

//
// do_reverse_replay
//

/*!
	\brief Реализация восстановления основного файла БД путем
		обратного воспроизведения trace-stream.
*/
static void
do_reverse_replay(
	//! Физическое имя БД с которой производится работа.
	const std::string & physic_name,
	//! Конфигурация БД.
	const config_t & config,
	//! Множество номеров всех trace-файлов в trace-stream.
	const trace_file_number_set_t & all_traces,
	//! Объект, который физически будет восстанавливать основной файл.
	committer_t & committer,
	//! Необязательный приемник множества номеров trace-файлов,
	//! которые реально были использованы при восстановлении БД.
	trace_file_number_set_t * used_traces,
	//! Нeобязательный приемник множества номеров trace-файлов,
	//! которые оказались либо не актуальными, либо вовсе не
	//! использовались.
	trace_file_number_set_t * unused_traces,
	//! Необязательный поток для выдачи информации по ходу
	//! восстановления.
	std::ostream * verbose_stream )
	{
		// Пытаемся выделить только актуальные номера trace-файлов.
		trace_file_number_set_t actual_traces;
		committer.reduce_traces_set( all_traces, actual_traces );
		if( actual_traces.empty() )
			OESS_THROW_PHYSIC(
					oess_1::db::storage::err::c_insufficent_trace_info,
					"actual_traces.size() cannot be 0; physic_name: "
					<< physic_name << ", all_traces.size(): "
					<< all_traces.size() )

		reverse_loader_t loader( physic_name,
				config.trace_file_page_size(),
				actual_traces, verbose_stream );

		trace_file_number_set_t all_used_traces;
		trace_file_number_set_t trx_used_traces;

		// Нужно найти самую последнюю транзакцию в trace-stream,
		// чтобы определить параметры восстанавливаемого файла.
		const oess_1::char_t * image;
		oess_1::uint_t image_len;
		if( !loader.next( image, image_len, trx_used_traces ) )
			// БД невозможно восстановить, т.к. она даже не была
			// полностью создана.
			OESS_THROW_PHYSIC(
					oess_1::db::storage::err::c_partially_created_db,
					physic_name << ": unable to recover partially "
							"created db; no transaction found "
							"in trace-stream" )

		bool trx_found = true;

		trace_stream_item_t item;
		deserialize_item( image, image_len, item );

		// Главный параметр сейчас -- это общее количество страниц
		// в восстанавливаемом файле.
		oess_1::uint_t total_page_count =
				item.main_file_length() / config.page_size();

		replay_context_t ctx(
				committer,
				total_page_count,
				config.page_size(),
				config.trx_delta_block_size(),
				verbose_stream );

		// Начинаем восстановление основного файла.
		committer.initialize(
				total_page_count, ctx.page_size(), ctx.block_size() );
		do
			{
				if( process_trx( item, ctx ) )
					// Что-то из транзакции было взято. Значит номера
					// использованных trace-файлов должны быть сохранены.
					std::copy( trx_used_traces.begin(), trx_used_traces.end(),
							std::inserter(
									all_used_traces,
									all_used_traces.begin() ) );

				if( ctx.is_need_continue() )
					{
						// Обработка не завершена, нужно искать следующую
						// транзакцию.
						trx_found = loader.next( image, image_len,
								trx_used_traces );
						if( trx_found )
							deserialize_item( image, image_len, item );
					}
			}
		while( ctx.is_need_continue() && trx_found );

		if( ctx.is_need_continue() &&
				!committer.is_insufficient_trace_info_allowed() )
			{
				ctx.dump();

				OESS_THROW_PHYSIC(
						oess_1::db::storage::err::c_insufficent_trace_info,
						physic_name << ": insufficent amount of information "
								"in trace-stream" )
			}

		committer.finalize();

		calc_used_unused_traces(
				all_traces, all_used_traces,
				used_traces, unused_traces );
	}

} /* namespace impl */

//
// std_trace_stream_t
//

std_trace_stream_t::std_trace_stream_t()
	:	m_is_open( false )
	,	m_is_read_only( false )
	{}

std_trace_stream_t::~std_trace_stream_t()
	{}

void
std_trace_stream_t::open(
	const std::string & physic_name,
	const config_t & config,
	bool is_read_only )
	{
		if( m_is_open )
			OESS_THROW_LOGIC(
					oess_1::db::storage::err::c_trace_stream_open,
					"trace-stream already open for: " << m_physic_name
					<< ", new physic-name: " << physic_name )

		// Должен существовать хотя бы один trace-файл.
		trace_file_number_set_t all_traces(
				find_all_trace_files( physic_name ) );
		err_code_t rc = impl::check_trace_file_count(
				config.trace_mode(), all_traces );
		if( rc )
			OESS_THROW_PHYSIC( rc.m_code, rc.m_desc )

		impl::initialize_compression( config );

		m_all_traces = all_traces;
		m_physic_name = physic_name;
		m_config = config;
		m_is_open = true;
		m_is_read_only = is_read_only;
	}

void
std_trace_stream_t::create(
	const std::string & physic_name,
	const config_t & config )
	{
		if( m_is_open )
			OESS_THROW_LOGIC(
					oess_1::db::storage::err::c_trace_stream_open,
					"trace-stream already open for: " << m_physic_name
					<< ", new physic-name: " << physic_name )

		// Не должно существовать ни одного файла.
		trace_file_number_set_t all_traces(
				find_all_trace_files( physic_name ) );
		if( all_traces.size() )
			OESS_THROW_PHYSIC(
					oess_1::db::storage::err::c_trace_file_found,
					"some trace-files already exists for: " << physic_name )

		impl::initialize_compression( config );

		// Создаем пустой trace-файл с нулевым номером. Это и будет
		// началом всего trace-stream.
		trace_file_number_t first = 0;
		oess_1::file::file_t first_file;
		first_file.create( create_trace_file_name( physic_name, first ) );

		m_all_traces.insert( first );
		m_physic_name = physic_name;
		m_config = config;
		m_is_open = true;
		m_is_read_only = false;
	}

void
std_trace_stream_t::destroy(
	const std::string & physic_name )
	{
		std::string errors;
		impl::destroy_trace_files(
				physic_name,
				find_all_trace_files( physic_name ),
				errors );

		// После этого не должно было остаться ни одного файла.
		if( find_all_trace_files( physic_name ).size() )
			OESS_THROW_PHYSIC(
					oess_1::db::storage::err::c_trace_file_found,
					"unable to delete all trace files for: "
					<< physic_name << "; all errors: "
					<< errors )
	}

bool
std_trace_stream_t::is_exists(
	const std::string & physic_name )
	{
		// Должен существовать хотя бы один trace-файл.
		return find_all_trace_files( physic_name ).size();
	}

void
std_trace_stream_t::append(
	const trace_stream_item_t & item )
	{
		if( !m_is_open )
			OESS_THROW_LOGIC(
					oess_1::db::storage::err::c_trace_stream_not_open,
					"trace-stream not open" )
		if( m_is_read_only )
			OESS_THROW_LOGIC(
					oess_1::db::storage::err::c_read_only_trace_stream,
					"read-only trace-stream for: " << m_physic_name )

		// Преобразуем изменения в один блок двоичных данных и
		// передадим его на сохранение.
//FIX ME: здесь есть возможность для оптимизации:
//сейчас на каждую транзакцию создается буфер для получения двоичного
//образа транзакции. Но можно использовать один буфер для всех транзакций
//и ограничения на его размер.
		oess_1::io::mem_buf_t buffer;
		impl::serialize_item( item, m_config.trace_compression(), buffer );

		// Теперь осталось записать образ транзакции в trace-файлы.
		if( !m_chain_ostream.get() )
			m_chain_ostream = create_chain_ostream();

		m_chain_ostream->append( buffer.ptr(), buffer.size() );
	}

void
std_trace_stream_t::reverse_replay(
	committer_t & committer,
	trace_file_number_set_t * used_files,
	trace_file_number_set_t * unused_files,
	std::ostream * verbose_stream )
	{
		if( !m_is_open )
			OESS_THROW_LOGIC(
					oess_1::db::storage::err::c_trace_stream_not_open,
					"trace-stream not open" )
		
		impl::do_reverse_replay(
				m_physic_name, m_config, m_all_traces,
				committer, used_files, unused_files,
				verbose_stream );
	}

void
std_trace_stream_t::close()
	{
		m_all_traces.clear();
		m_is_open = false;
	}

std::auto_ptr< impl::chain_ostream_t >
std_trace_stream_t::create_chain_ostream()
	{
		return std::auto_ptr< impl::chain_ostream_t >(
				new impl::chain_ostream_t(
						physic_name(), all_traces(),
						config().trace_file_page_size(),
						config().trace_file_size_limit(),
						config().trace_stream_buffered_pages() ) );
	}

bool
std_trace_stream_t::is_open() const
	{
		return m_is_open;
	}

const std::string &
std_trace_stream_t::physic_name() const
	{
		return m_physic_name;
	}

const config_t &
std_trace_stream_t::config() const
	{
		return m_config;
	}

trace_file_number_set_t &
std_trace_stream_t::all_traces()
	{
		return m_all_traces;
	}

//
// last_trx_only_trace_stream_t
//

last_trx_only_trace_stream_t::last_trx_only_trace_stream_t()
	{}

last_trx_only_trace_stream_t::~last_trx_only_trace_stream_t()
	{}

std::auto_ptr< impl::chain_ostream_t >
last_trx_only_trace_stream_t::create_chain_ostream()
	{
		return std::auto_ptr< impl::chain_ostream_t >(
				new impl::last_trx_only_chain_ostream_t(
						physic_name(), all_traces(),
						config().trace_file_page_size(),
						config().trace_stream_buffered_pages() ) );
	}

//
// pseudo_trace_stream_t
//

pseudo_trace_stream_t::pseudo_trace_stream_t()
	{}

pseudo_trace_stream_t::~pseudo_trace_stream_t()
	{}

void
pseudo_trace_stream_t::open(
	const std::string & physic_name,
	const config_t & config,
	bool is_read_only )
	{}

void
pseudo_trace_stream_t::create(
	const std::string & physic_name,
	const config_t & config )
	{}

void
pseudo_trace_stream_t::destroy(
	const std::string & physic_name )
	{}

bool
pseudo_trace_stream_t::is_exists(
	const std::string & physic_name )
	{
		return true;
	}

void
pseudo_trace_stream_t::append(
	const trace_stream_item_t & item )
	{}

void
pseudo_trace_stream_t::reverse_replay(
	committer_t & committer,
	trace_file_number_set_t * used_files,
	trace_file_number_set_t * unused_files,
	std::ostream * verbose_stream )
	{
		OESS_THROW_PHYSIC(
				oess_1::db::storage::err::c_trace_file_not_found,
				"storage cannot be repaired if trace_mode == trace_no" )
	}

void
pseudo_trace_stream_t::close()
	{}

//
// create_std_trace_stream
//

OESS_1__DB__FUNC(std::auto_ptr< trace_stream_t >)
create_std_trace_stream(
	trace_mode_t trace_mode )
	{
		if( trace_no == trace_mode )
			return std::auto_ptr< trace_stream_t >( 
					new pseudo_trace_stream_t() );

		if( trace_last == trace_mode )
			return std::auto_ptr< trace_stream_t >(
					new last_trx_only_trace_stream_t() );

		if( trace_all != trace_mode )
			OESS_THROW_LOGIC(
					oess_1::db::storage::err::c_invalid_trace_mode,
					trace_mode << ": unknown or unsupported trace mode" )

		return std::auto_ptr< trace_stream_t >(
				new std_trace_stream_t() );
	}

} /* namespace trx */

} /* namespace storage */

} /* namespace db */

} /* namespace oess */

