class EL_ZLIB_ROUTINES

(source code)

description

Zlib routines

note
	description: "Zlib routines"

	author: "Finnian Reilly"
	copyright: "Copyright (c) 2001-2022 Finnian Reilly"
	contact: "finnian at eiffel hyphen loop dot com"

	license: "MIT license (See: en.wikipedia.org/wiki/MIT_License)"
	date: "2022-11-15 19:56:07 GMT (Tuesday 15th November 2022)"
	revision: "9"

class
	EL_ZLIB_ROUTINES

inherit
	EL_ZLIB_API

	EL_MODULE_EXCEPTION

feature -- Conversion

	compressed (source: MANAGED_POINTER; level: INTEGER; expected_compression_ratio: DOUBLE): SPECIAL [NATURAL_8]
		require
			valid_level: (-1 |..| 9).has (level)
		do
			Result := new_compressed (source.item, source.count, level, expected_compression_ratio)
		end

	compressed_string (source: STRING; level: INTEGER; expected_compression_ratio: DOUBLE): SPECIAL [NATURAL_8]
		require
			valid_level: (-1 |..| 9).has (level)
		do
			Result := new_compressed (source.area.base_address, source.count, level, expected_compression_ratio)
		end

	decompressed (source: MANAGED_POINTER; orginal_count: INTEGER): SPECIAL [NATURAL_8]
			--
		do
			Result := new_decompressed (source.item, source.count, orginal_count)
		end

	decompressed_bytes (source: SPECIAL [NATURAL_8]; orginal_count: INTEGER): SPECIAL [NATURAL_8]
			--
		do
			Result := new_decompressed (source.base_address, source.count, orginal_count)
		end

feature -- Access

	error_message: STRING
		do
			inspect error_status
				when Z_stream_end then
					Result := "stream end"

				when Z_need_dict then
					Result := "need dict"

				when Z_stream_error then
					Result := "level parameter is invalid"

				when Z_data_error then
					Result := "input data corrupted or incomplete"

				when Z_mem_error then
					Result := "not enough memory"

				when Z_buf_error then
					Result := "not enough room in the output buffer"

				when Z_version_error then
					Result := "library version does not match header"
			else
				Result := "Unknown error"
			end
		end

	error_status: INTEGER

	last_compression_ratio: REAL

feature -- Status query

	has_error: BOOLEAN
		do
			Result := error_status > 0
		end

feature {NONE} -- Implementation

	new_compressed (source_ptr: POINTER; count, level: INTEGER; expected_compression_ratio: DOUBLE): SPECIAL [NATURAL_8]
		local
			status: INTEGER; compressed_count, upper_bound: INTEGER_64
			upper_compression_ratio, compression_ratio: DOUBLE
			done: BOOLEAN
		do
			error_status := 0
			upper_bound := c_compress_bound (count)
			upper_compression_ratio := upper_bound / count
			from
				compression_ratio := expected_compression_ratio
			until
				done or compression_ratio > upper_compression_ratio + 0.1
			loop
				compressed_count := upper_bound.min ((count * compression_ratio).rounded)

				create Result.make_filled (0, compressed_count.to_integer)
				status := c_compress2 (Result.base_address, $compressed_count, source_ptr, count, level)
				inspect status
					when Z_ok then
						Result.keep_head (compressed_count.to_integer)
						last_compression_ratio := compression_ratio.truncated_to_real
						done := True

					when Z_buf_error then
						compression_ratio := compression_ratio + 0.1

				else
					error_status := status
					done := True
				end
			end
		ensure
			compressed: Result.count > 0
		end

	new_decompressed (source_ptr: POINTER; count, orginal_count: INTEGER): SPECIAL [NATURAL_8]
		local
			status: INTEGER; decompressed_count: INTEGER_64
		do
			error_status := 0
			decompressed_count := orginal_count
			create Result.make_filled (0, decompressed_count.to_integer)
			status := c_uncompress (Result.base_address, $decompressed_count, source_ptr, count)
			inspect status
				when Z_ok then
					Result.keep_head (decompressed_count.to_integer)
					last_compression_ratio := (count / decompressed_count.to_integer).truncated_to_real
			else
				error_status := status
			end
		ensure
			same_count_as_original: orginal_count = Result.count
		end

end