note
	description: "Serialize/Deserialize data from a medium."
	legal: "See notice at end of class."
	status: "See notice at end of class."
	date: "$Date: 2017-09-25 15:53:33 +0000 (Mon, 25 Sep 2017) $"
	revision: "$Revision: 100789 $"

class 
	SED_MEDIUM_READER_WRITER

inherit
	SED_BINARY_READER_WRITER
		redefine
			read_header,
			write_header,
			cleanup,
			write_footer,
			is_ready_for_reading,
			is_ready_for_writing,
			medium
		end

	PLATFORM
		export
			{NONE} all
		end

create 
	make,
	make_for_reading,
	make_for_writing,
	make_with_buffer

feature {NONE} -- Initialization

	make (a_medium: IO_MEDIUM)
			-- Initialize current to read or write from a_medium.
		require
			a_medium_not_void: a_medium /= Void
			a_medium_support_storable: a_medium.support_storable
		do
			make_with_buffer (a_medium, Default_buffer_size)
		ensure
			medium_set: medium = a_medium
			buffer_size_set: buffer_size = Default_buffer_size
		end

	make_for_reading (a_medium: IO_MEDIUM)
			-- Initialize current to read from a_medium.
		require
			a_medium_not_void: a_medium /= Void
			a_medium_open_for_reading: a_medium.is_open_read
			a_medium_support_storable: a_medium.support_storable
		do
			make (a_medium)
			set_for_reading
		ensure
			medium_set: medium = a_medium
			buffer_size_set: buffer_size = Default_buffer_size
		end

	make_for_writing (a_medium: IO_MEDIUM)
			-- Initialize current to write to a_medium.
		require
			a_medium_not_void: a_medium /= Void
			a_medium_open_for_writing: a_medium.is_open_write
			a_medium_support_storable: a_medium.support_storable
		do
			make (a_medium)
			set_for_writing
		ensure
			medium_set: medium = a_medium
			buffer_size_set: buffer_size = Default_buffer_size
		end

	make_with_buffer (a_medium: IO_MEDIUM; a_buffer_size: INTEGER_32)
			-- Initialize current to read or write from a_medium using a buffer of size a_buffer_size.
			-- buffer_size will be overriden during read operation by the value of buffer_size used
			-- when writing.
		require
			a_medium_not_void: a_medium /= Void
			a_buffer_size_non_negative: a_buffer_size >= 0
			a_medium_support_storable: a_medium.support_storable
		do
			medium := a_medium
			buffer_size := a_buffer_size
			create buffer.make (a_buffer_size)
		ensure
			medium_set: medium = a_medium
			buffer_size_set: buffer_size = a_buffer_size
			buffer_size_set: buffer.count = a_buffer_size
		end
	
feature -- Header/Footer

	read_header
			-- Retrieve configuration of how data was stored.
		do
			read_buffer_from_medium
			is_little_endian_storable := read_boolean
			is_pointer_value_stored := read_boolean
			stored_pointer_bytes := read_integer_32
		end

	write_header
			-- Store configuration on how data will be stored.
		do
			buffer_size := buffer.count
			check
				enough_space: buffer.count > {PLATFORM}.integer_32_bytes * 4 + {PLATFORM}.boolean_bytes * 2
			end
			is_little_endian_storable := Is_little_endian
			write_chunk_header
			write_boolean (is_little_endian_storable)
			write_boolean (is_pointer_value_stored)
			write_integer_32 ({PLATFORM}.pointer_bytes)
		end

	write_chunk_header
			-- Store information about chunks
		do
			buffer_position := 0;
			buffer.put_integer_32_be (-1, 0)
			buffer_position := buffer_position + {PLATFORM}.integer_32_bytes;
			buffer.put_natural_32_be (Reader_writer_version_6_6, buffer_position)
			buffer_position := buffer_position + {PLATFORM}.natural_32_bytes
			buffer_position := buffer_position + {PLATFORM}.integer_32_bytes
			buffer_position := buffer_position + {PLATFORM}.boolean_bytes
		end

	write_footer
			-- Store last buffered data.
		do
			flush_buffer_to_medium (True)
		end
	
feature -- Status report

	is_ready_for_reading: BOOLEAN
			-- Is Current ready for future read operations?
		do
			Result := is_for_reading and then medium.exists and then medium.is_open_read and then medium.support_storable and then medium.readable
		end

	is_ready_for_writing: BOOLEAN
			-- Is Current ready for future write operations?
		do
			Result := not is_for_reading and then medium.exists and then medium.is_open_write and then medium.support_storable
		end

	is_last_chunk: BOOLEAN
			-- Are we reading the last chunk?

	cleanup
			-- When a reading is stopped, perform the necessary cleanup to perform
			-- another reading or writing.
		do
			from
			until
				is_last_chunk
			loop
				read_buffer_from_medium
			end
		end
	
feature -- Access

	Reader_writer_version_6_6: NATURAL_32 = 1
			-- Version of format used for reading/writing.
	
feature {NONE} -- Implementation: Access

	medium: IO_MEDIUM
			-- Medium used for read/write operations

	version_position: INTEGER_32
			-- Position where information about version is stored
		do
			Result := size_position (False) + {PLATFORM}.integer_32_bytes
		end

	size_position (a_is_new_format: BOOLEAN): INTEGER_32
			-- Position where size information for current chunk is stored.
		do
			if a_is_new_format then
				Result := version_position + {PLATFORM}.natural_32_bytes
			else
				Result := 0
			end
		end

	new_chunk_position: INTEGER_32
			-- Position where presence or not of a next chunk is stored.
		do
			Result := size_position (True) + {PLATFORM}.integer_32_bytes
		end

	chunk_header_size (a_is_new_format: BOOLEAN): INTEGER_32
			-- Size of data
		do
			Result := {PLATFORM}.integer_32_bytes
			if a_is_new_format then
				Result := Result + {PLATFORM}.natural_32_bytes + {PLATFORM}.integer_32_bytes + {PLATFORM}.boolean_bytes
			end
		end
	
feature {NONE} -- Buffer update

	check_buffer (n: INTEGER_32)
			-- If there is enough space in buffer to read n bytes, do nothing.
			-- Otherwise, read/write to medium to free some space.
		do
			if n + buffer_position > buffer_size then
				if is_for_reading then
					read_buffer_from_medium
				else
					flush_buffer_to_medium (False)
				end
			end
		end

	read_buffer_from_medium
			-- Read next chunk of data.
		require
			is_ready: is_ready_for_reading
		local
			l_version: NATURAL_32
			l_is_new_format: BOOLEAN
			l_failure: SERIALIZATION_FAILURE
		do
			medium.read_to_managed_pointer (buffer, 0, {PLATFORM}.integer_32_bytes)
			if medium.bytes_read = {PLATFORM}.integer_32_bytes then
				buffer_size := buffer.read_integer_32_be (0)
				is_last_chunk := True
				if buffer_size = -1 then
					l_is_new_format := True;
					medium.read_to_managed_pointer (buffer, version_position, {PLATFORM}.integer_32_bytes)
					l_version := buffer.read_natural_32_be (version_position)
					if l_version = Reader_writer_version_6_6 then
						medium.read_to_managed_pointer (buffer, size_position (True), {PLATFORM}.integer_32_bytes)
						buffer_size := buffer.read_integer_32_be (size_position (True));
						medium.read_to_managed_pointer (buffer, new_chunk_position, {PLATFORM}.boolean_bytes)
						is_last_chunk := buffer.read_boolean (new_chunk_position)
					end
				else
					l_is_new_format := False
				end
				buffer_position := chunk_header_size (l_is_new_format)
				if buffer_size > buffer.count then
					buffer.resize (buffer_size)
				end
				if buffer_size > {PLATFORM}.integer_32_bytes then
					medium.read_to_managed_pointer (buffer, buffer_position, buffer_size - buffer_position)
					if medium.bytes_read /= buffer_size - buffer_position then
						buffer_position := 0
						buffer_size := buffer.count
						create l_failure;
						l_failure.set_description ("Read less than expected number of bytes in buffer.");
						l_failure.raise
					end
				else
					buffer_position := 0
					buffer_size := buffer.count
					create l_failure;
					l_failure.set_description ("Read less than 4 bytes in buffer's header.");
					l_failure.raise
				end
			else
				buffer_position := 0
				buffer_size := buffer.count
				create l_failure;
				l_failure.set_description ("Cannot read buffer size from header.");
				l_failure.raise
			end
		end

	flush_buffer_to_medium (last_chunk: BOOLEAN)
			-- Write next chunk of data to medium.
		require
			is_ready: is_ready_for_writing
		do
			buffer.put_integer_32_be (buffer_position, size_position (True));
			buffer.put_boolean (last_chunk, new_chunk_position);
			medium.put_managed_pointer (buffer, 0, buffer_position)
			write_chunk_header
		end
	
invariant
	medium_not_void: medium /= Void

note
	library: "EiffelBase: Library of reusable components for Eiffel."
	copyright: "Copyright (c) 1984-2017, Eiffel Software and others"
	license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
	source: "[
		Eiffel Software
		5949 Hollister Ave., Goleta, CA 93117 USA
		Telephone 805-685-1006, Fax 805-685-6869
		Website http://www.eiffel.com
		Customer support http://support.eiffel.com
	]"

end -- class SED_MEDIUM_READER_WRITER

Generated by ISE EiffelStudio