/*

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.

*/

/*!
	\file oess_1/io/h/mem_buf.hpp
	\brief Класс %oess_1::io::mem_buf_t.
*/

#if !defined( _OESS_1__IO__MEM_BUF_HPP_ )
#define _OESS_1__IO__MEM_BUF_HPP_

#include <oess_1/io/h/declspec.hpp>

#include <auto_ptr_3/h/vect_ptr.hpp>

#include <oess_1/io/h/binbuffer.hpp>

namespace oess_1 {

namespace io {

//
// mem_buf_t
//

//! Двоичный ввод/вывод с использованием неограниченого
//! буфера памяти.
/*!
	Реализация инерфейсов ibinstream_t, obinstream_t
	для неограниченого буфера, который может автоматически
	увеличивать свой размер.

	\note
	Операции копирования копируют и значение текущей
	позиции. Например:
\code
oess_1::io::mem_buf_t buf1( 1024 );
oess_1::io::mem_buf_t buf2( 2048 );

buf1.set_size( 256 );
buf1.set_pos( 26 );

buf2.set_size( 512 );
buf2.set_pos( 384 );

buf2 = buf1;
// Теперь 26 == buf2.pos()
\endcode

	\par v.1.1.1
	Сделан производным от oess_1::io::ibinbuffer_t,
	oess_1::io::obinbuffer_t.
*/
class	OESS_1__IO__TYPE	mem_buf_t :
	public ibinbuffer_t,
	public obinbuffer_t
{
	public :
		/*!
			Начальная емкость буфера определяется
			значением e_initial_capacity.
		*/
		mem_buf_t();
		mem_buf_t(
			//! Начальная емкость буфера.
			size_t capacity );
		//! Конструктор копирования.
		mem_buf_t(
			const mem_buf_t & o );
		virtual ~mem_buf_t();

		mem_buf_t &
		operator=(
			const mem_buf_t & o );

		enum {
			e_initial_capacity = 64
		};

		//! Текущая емкость буфера.
		virtual size_t
		capacity() const;

		//! Пересоздать буфер.
		/*!
			Буфер пересоздается всегда.

			Если save_contents равно 'true', то
			текущее значение буфера сохраняется,
			а новый 'size' = min( new_capacity, size )
			Текущая позиция обнуляется.
		*/
		void
		change_capacity(
			//! Новая емкость буфера.
			size_t new_capacity,
			//! Должно ли сохраняться предыдущее
			//! содержимое буфера.
			bool save_contents = true );

		//! Текущий размер буфера.
		/*!
			Объем данных, реально помещенных в буфер.
		*/
		virtual size_t
		size() const;

		//! Установить размер данных в буфере.
		/*!
			Текущая позиция обнуляется.

			Размер буфера изменяется только если
			new_size <= capacity().
		*/
		void
		change_size(
			//! Новый размер данных.
			size_t new_size );

		//! Текущая позиция, с которой будет осуществляться
		//! очередная операция чтения или записи.
		virtual size_t
		pos() const;

		//! Установить позицию, с которой будет осуществляться
		//! очередная операци чтения или записи.
		/*!
			Операция выполняется только, если pos <= size().
		*/
		void
		set_pos( size_t pos );

		//! Возвращает true, если pos() == size().
		virtual bool
		eof() const;

		//! Указатель на начало всего буфера.
		const oess_1::char_t *
		ptr() const;

		//! Указатель на текущую позицию в буфере.
		const oess_1::char_t *
		current_ptr() const;

		//! Выделение подбуфера.
		/*!
			В буфер to в позицию to_pos копируется count байт
			с позиции from_pos.

			Содержимое буфера-приемника с позиции to_pos не
			сохраняется (перезаписываются count байт в
			буфере-приемнике).

			Позиция в буфере-источнике не изменяется.

			Наличие данных не контролируется.
		*/
		void
		subbuf(
			/*! Откуда начинать брать данные. */
			size_t from_pos,
			/*! Буфер-приемник. */
			mem_buf_t & to,
			/*! В какую позицию в буфер приемник начинать запись. */
			size_t to_pos,
			/*! Количество копируемых байт. */
			size_t count ) const;

		//! Выделение подбуфера.
		/*!
			По указателю to копируется count байт
			с позиции from_pos.

			Указатель to должен указывать на блок памяти достаточного
			размера, чтобы принять count байт.

			Позиция в буфере-источнике не изменяется.

			Наличие данных не контролируется.
		*/
		void
		subbuf(
			/*! Откуда начинать брать данные. */
			size_t from_pos,
			/*! Указатель на блок памяти для приема данных. */
			void * to,
			/*! Количество копируемых байт. */
			size_t count ) const;

		//! Вставка данных.
		/*!
			Вставить count байт из from c позиции from_pos в позицию
			to_pos. Старое содержимое буфера "раздвигается".

			Текущая позиция обнуляется.

			Текущая позиция в буфере-источнике не изменяется.

			Наличие достаточного количества данных в from не
			проверяется.

			Поведение при to_pos > size() не определено.
		*/
		void
		insert(
			/*! Позиция, куда будут вставляться данные. */
			size_t to_pos,
			/*! Буфер-источник данных. */
			const mem_buf_t & from,
			/*! Позиция в буфере-источнике, откуда нужно
				брать данные. */
			size_t from_pos,
			/*! Количество копируемых байт. */
			size_t count );

		//! Вставка данных.
		/*!
			Вставить count байт из from в позицию
			to_pos. Старое содержимое буфера "раздвигается".

			Текущая позиция обнуляется.

			Предполагается, что from указывает на блок памяти в
			котором храниться count байт.

			Поведение при to_pos > size() не определено.
		*/
		void
		insert(
			size_t to_pos,
			const void * from,
			size_t count );

		//! Изъять данные из буфера.
		/*!
			Изымается count байт начиная с позиции from_offs.
			Старое содержимое буфера "сжимается".

			Поведение при from_offs > size() или при
			(from_offs + count > size()) не определено.
		*/
		void
		erase(
			size_t from_offs,
			size_t count );

		//! Обменять значения двух буферов.
		/*!
			Происходит обмен только описателями буфером.
			Копирование данных между буферами не происходит.
		*/
		void
		swap(
			mem_buf_t & o );

	protected :
		//! Реализация метода из базового класса.
		/*!
			Если нет достаточного объема данных для чтения
			хотя бы одного элемента, то порождается
			исключение.
		*/
		virtual const oess_1::char_t *
		in_reserve(
			//! Количество элементов, которые нужно
			//! прочитать.
			size_t item_count,
			//! Размер одного элемента в байтах.
			size_t item_size,
			//! Количество элементов, доступных по
			//! возвращенному указателю.
			size_t & item_available );

		//! Реализация метода из базового класса.
		virtual void
		in_shift(
			//! Количество успешно прочитанных элементов.
			size_t item_count,
			//! Размер одного элемента в байтах.
			size_t item_size );

		//! Реализация метода из базового класса.
		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 );

	private :
		//! Буфер.
		auto_ptr_3::vect_ptr_t< char >	m_buf;
		//! Текущий размер буфера.
		size_t	m_size;
		//! Текущая позиция в буфере.
		size_t	m_pos;
};

} /* namespace io */

} /* namespace oess_1 */

#endif
