note
	description: "[
					Platform specific encoding of Unicode strings. By default, UTF-8 on unix or UTF-16 on Windows.
		
					Mixed-encoding consideration
					============================
		
					Most operating systems have conventions for strings that are incompatible with Unicode.
					On UNIX, a string is just a null-terminated byte sequence, it does not follow any
					specific encoding. Usually the locale setting enables you to see the string the way
					you expect.
					On Windows, the sequence of names is made of null-terminated UTF-16 code unit sequence. Windows
					does not guarantee that the sequence is actually a valid UTF-16 sequence.
		
					In other words, when there is an invalid UTF-8 encoding on UNIX, or an invalid UTF-16 encoding
					on Windows, the string is not directly representable as a Unicode string. To make it possible
					to create and store strings in a textually representable form, the query string will create
					an encoded representation that can be then later used in make to create a NATIVE_STRING
					equivalent to the original string. The encoding is described in UTF_CONVERTER's note clause
					and is a fourth variant of the recommended practice for replacement characters in Unicode
					(see http://www.unicode.org/review/pr-121.html).
		
	]"
	date: "$Date: 2020-05-19 14:32:38 +0000 (Tue, 19 May 2020) $"
	revision: "$Revision: 104260 $"

class 
	NATIVE_STRING

create 
	make,
	make_empty,
	make_from_pointer,
	make_from_raw_string

feature {NONE} -- Initialization

	default_create
			-- Process instances of classes with no creation clause.
			-- (Default: do nothing.)
			-- (from ANY)
		do
		end

	make (a_string: READABLE_STRING_GENERAL)
			-- Initialize an instance of Current using a_string treated as a sequence
			-- of Unicode characters.
		require
			a_string_not_void: a_string /= Void
		do
			make_empty (a_string.count)
			set_string (a_string)
		end

	make_empty (a_length: INTEGER_32)
			-- Initialize an empty instance of Current that will accommodate a_length code units.
			-- The memory area is not initialized.
		require
			a_length_positive: a_length >= 0
		do
			create managed_data.make ((a_length + 1) * unit_size)
			unit_count := 0
		end

	make_from_pointer (a_pointer: POINTER)
			-- Initialize current from a_pointer, a platform system specific null-terminated string.
		require
			a_path_pointer_not_null: a_pointer /= default_pointer
		local
			l_count: INTEGER_32
		do
			l_count := pointer_length_in_bytes (a_pointer)
			create managed_data.make_from_pointer (a_pointer, l_count + unit_size)
			unit_count := l_count // unit_size
		end

	make_from_raw_string (a_raw_string: like raw_string)
			-- Initialize current from a_raw_string.
		require
			a_raw_string_not_void: a_raw_string /= Void
		local
			i: INTEGER_32
		do
			create managed_data.make (a_raw_string.count + unit_size)
			across
				a_raw_string as l_c
			loop
				managed_data.put_character (l_c.item, i)
				i := i + 1
			end
			if {PLATFORM}.is_windows then
				managed_data.put_natural_16 (0, i)
			else
				managed_data.put_natural_8 (0, i)
			end
			unit_count := (managed_data.count - unit_size) // unit_size
		ensure
			set: raw_string.same_string (a_raw_string)
		end
	
feature -- Access

	generating_type: TYPE [detachable NATIVE_STRING]
			-- Type of current object
			-- (type of which it is a direct instance)
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			generating_type_not_void: Result /= Void
		end

	generator: STRING_8
			-- Name of current object's generating class
			-- (base class of the type of which it is a direct instance)
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			generator_not_void: Result /= Void
			generator_not_empty: not Result.is_empty
		end

	item: POINTER
			-- Get pointer to allocated area.
		do
			Result := managed_data.item
		ensure
			item_not_null: Result /= default_pointer
		end

	managed_data: MANAGED_POINTER
			-- Hold data of Current.

	raw_string: STRING_8
			-- Sequence of bytes representing Current.
		local
			l_cstr: C_STRING
		do
			create l_cstr.make_shared_from_pointer_and_count (managed_data.item, managed_data.count)
			Result := l_cstr.substring_8 (1, managed_data.count - unit_size)
		end

	string: STRING_32
			-- Representation of Current up to the first null character.
		local
			u: UTF_CONVERTER
		do
			if {PLATFORM}.is_windows then
				Result := u.utf_16_0_pointer_to_escaped_string_32 (managed_data)
			else
				Result := u.utf_8_0_pointer_to_escaped_string_32 (managed_data)
			end
		end

	substring (start_pos, end_pos: INTEGER_32): STRING_32
			-- Copy of substring containing all code units at indices
			-- between start_pos and end_pos.
		require
			start_position_big_enough: start_pos >= 1
			end_position_big_enough: start_pos <= end_pos + 1
			end_position_not_too_big: end_pos <= (capacity // unit_size)
		local
			u: UTF_CONVERTER
		do
			create Result.make (end_pos - start_pos + 1)
			if {PLATFORM}.is_windows then
				u.utf_16_0_subpointer_into_escaped_string_32 (managed_data, start_pos - 1, end_pos - 1, False, Result)
			else
				Result := u.utf_8_0_pointer_to_escaped_string_32 (managed_data);
				u.utf_8_0_subpointer_into_escaped_string_32 (managed_data, start_pos - 1, end_pos - 1, False, Result)
			end
		ensure
			susbstring_not_void: Result /= Void
		end
	
feature {NATIVE_STRING_HANDLER} -- Access

	pointer_length_in_bytes (a_ptr: POINTER): INTEGER_32
			-- Length in bytes of a platform specific file name pointer, not
			-- including the null-terminating character. If size is too large
			-- to fit into an {INTEGER} instance, the size is truncated to
			-- {INTEGER_32}.max_value.
			-- (from NATIVE_STRING_HANDLER)
		require -- from NATIVE_STRING_HANDLER
			a_ptr_not_null: a_ptr /= default_pointer
		local
			l_length: NATURAL_64
		do
			l_length := c_pointer_length_in_bytes (a_ptr)
			if l_length <= {INTEGER_32}.max_value.to_natural_64 then
				Result := l_length.to_integer_32
			else
				Result := {INTEGER_32}.max_value
			end
		end
	
feature -- Measurement

	bytes_count: INTEGER_32
			-- Number of bytes used by Current not including the null terminating character.
		do
			Result := unit_count * unit_size
		ensure
			bytes_count_non_negative: Result >= 0
		end

	capacity: INTEGER_32
			-- Number of bytes in Current.
		do
			Result := managed_data.count
		end

	unit_count: INTEGER_32
			-- Number of units used by Current not including the null terminating unit.

	unit_size: INTEGER_32
			-- Size in bytes of a unit for storage.
		do
			if {PLATFORM}.is_windows then
				Result := 2
			else
				Result := 1
			end
		end
	
feature -- Comparison

	frozen deep_equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void
			-- or attached to isomorphic object structures?
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.is_deep_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			shallow_implies_deep: standard_equal (a, b) implies Result
			both_or_none_void: (a = Void) implies (Result = (b = Void))
			same_type: (Result and (a /= Void)) implies (b /= Void and then a.same_type (b))
			symmetric: Result implies deep_equal (b, a)
		end

	frozen equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void or attached
			-- to objects considered equal?
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.is_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.is_equal (b))
		end

	frozen is_deep_equal alias "≡≡≡" (other: NATIVE_STRING): BOOLEAN
			-- Are Current and other attached to isomorphic object structures?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			shallow_implies_deep: standard_is_equal (other) implies Result
			same_type: Result implies same_type (other)
			symmetric: Result implies other.is_deep_equal (Current)
		end

	is_equal (other: like Current): BOOLEAN
			-- Is content of string identical to content of string other?
		require -- from ANY
			other_not_void: other /= Void
		do
			Result := item.is_equal (other.item)
		ensure -- from ANY
			symmetric: Result implies other ~ Current
			consistent: standard_is_equal (other) implies Result
		end

	frozen standard_equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void or attached to
			-- field-by-field identical objects of the same type?
			-- Always uses default object comparison criterion.
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.standard_is_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.standard_is_equal (b))
		end

	frozen standard_is_equal alias "" (other: NATIVE_STRING): BOOLEAN
			-- Is other attached to an object of the same type
			-- as current object, and field-by-field identical to it?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			same_type: Result implies same_type (other)
			symmetric: Result implies other.standard_is_equal (Current)
		end
	
feature -- Status report

	conforms_to (other: ANY): BOOLEAN
			-- Does type of current object conform to type
			-- of other (as per Eiffel: The Language, chapter 13)?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		end

	same_type (other: ANY): BOOLEAN
			-- Is type of current object identical to type of other?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			definition: Result = (conforms_to (other) and other.conforms_to (Current))
		end
	
feature -- Element change

	set_shared_from_pointer (a_ptr: POINTER)
			-- New instance sharing a_ptr.
		require
			a_ptr_not_null: a_ptr /= default_pointer
		do
			set_shared_from_pointer_and_count (a_ptr, pointer_length_in_bytes (a_ptr))
		end

	set_shared_from_pointer_and_count (a_ptr: POINTER; a_length: INTEGER_32)
			-- New instance sharing a_ptr of a_length byte. Space for an additional
			-- null terminating code unit is added to managed_data.
		require
			a_ptr_not_null: a_ptr /= default_pointer
			a_length_non_negative: a_length >= 0
			a_length_valid: (a_length \\ unit_size) = 0
		do
			unit_count := a_length // unit_size
			if not managed_data.is_shared then
				create managed_data.share_from_pointer (a_ptr, a_length + unit_size)
			else
				managed_data.set_from_pointer (a_ptr, a_length + unit_size)
			end
		end

	set_string (a_string: READABLE_STRING_GENERAL)
			-- Set string with a_string	treated as a sequence of Unicode characters.
		require
			a_string_not_void: a_string /= Void
		do
			set_substring (a_string, 1, a_string.count)
		end

	set_substring (a_string: READABLE_STRING_GENERAL; start_pos, end_pos: INTEGER_32)
			-- Set string with a subset of a_string from a_start_pos index to end_pos index.
		require
			a_string_not_void: a_string /= Void
			start_position_big_enough: start_pos >= 1
			end_position_big_enough: start_pos <= end_pos + 1
			end_pos_small_enough: end_pos <= a_string.count
		local
			u: UTF_CONVERTER
		do
			if {PLATFORM}.is_windows then
				u.escaped_utf_32_substring_into_utf_16_0_pointer (a_string, start_pos, end_pos, managed_data, 0, Upper_cell)
			else
				u.escaped_utf_32_substring_into_utf_8_0_pointer (a_string, start_pos, end_pos, managed_data, 0, Upper_cell)
			end
			unit_count := Upper_cell.item // unit_size
		end
	
feature -- Duplication

	frozen clone (other: detachable ANY): like other
		obsolete "Use `twin' instead. [2017-05-31]"
			-- Void if other is void; otherwise new object
			-- equal to other
			--
			-- For non-void other, clone calls copy;
			-- to change copying/cloning semantics, redefine copy.
			-- (from ANY)
		do
			if other /= Void then
				Result := other.twin
			end
		ensure -- from ANY
			instance_free: class
			equal: Result ~ other
		end

	copy (other: NATIVE_STRING)
			-- Update current object using fields of object attached
			-- to other, so as to yield equal objects.
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
			type_identity: same_type (other)
		external
			"built_in"
		ensure -- from ANY
			is_equal: Current ~ other
		end

	frozen deep_clone (other: detachable ANY): like other
		obsolete "Use `deep_twin' instead. [2017-05-31]"
			-- Void if other is void: otherwise, new object structure
			-- recursively duplicated from the one attached to other
			-- (from ANY)
		do
			if other /= Void then
				Result := other.deep_twin
			end
		ensure -- from ANY
			instance_free: class
			deep_equal: deep_equal (other, Result)
		end

	frozen deep_copy (other: NATIVE_STRING)
			-- Effect equivalent to that of:
			--		copy (other . deep_twin)
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		do
			copy (other.deep_twin)
		ensure -- from ANY
			deep_equal: deep_equal (Current, other)
		end

	frozen deep_twin: NATIVE_STRING
			-- New object structure recursively duplicated from Current.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			deep_twin_not_void: Result /= Void
			deep_equal: deep_equal (Current, Result)
		end

	frozen standard_clone (other: detachable ANY): like other
		obsolete "Use `standard_twin' instead. [2017-05-31]"
			-- Void if other is void; otherwise new object
			-- field-by-field identical to other.
			-- Always uses default copying semantics.
			-- (from ANY)
		do
			if other /= Void then
				Result := other.standard_twin
			end
		ensure -- from ANY
			instance_free: class
			equal: standard_equal (Result, other)
		end

	frozen standard_copy (other: NATIVE_STRING)
			-- Copy every field of other onto corresponding field
			-- of current object.
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
			type_identity: same_type (other)
		external
			"built_in"
		ensure -- from ANY
			is_standard_equal: standard_is_equal (other)
		end

	frozen standard_twin: NATIVE_STRING
			-- New object field-by-field identical to other.
			-- Always uses default copying semantics.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			standard_twin_not_void: Result /= Void
			equal: standard_equal (Result, Current)
		end

	frozen twin: NATIVE_STRING
			-- New object equal to Current
			-- twin calls copy; to change copying/twinning semantics, redefine copy.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			twin_not_void: Result /= Void
			is_equal: Result ~ Current
		end
	
feature -- Basic operations

	frozen as_attached: attached NATIVE_STRING
		obsolete "Remove calls to this feature. [2017-05-31]"
			-- Attached version of Current.
			-- (Can be used during transitional period to convert
			-- non-void-safe classes to void-safe ones.)
			-- (from ANY)
		do
			Result := Current
		end

	frozen default: detachable NATIVE_STRING
			-- Default value of object's type
			-- (from ANY)
		do
		end

	frozen default_pointer: POINTER
			-- Default value of type POINTER
			-- (Avoid the need to write p.default for
			-- some p of type POINTER.)
			-- (from ANY)
		do
		ensure -- from ANY
			instance_free: class
		end

	default_rescue
			-- Process exception for routines with no Rescue clause.
			-- (Default: do nothing.)
			-- (from ANY)
		do
		end

	frozen do_nothing
			-- Execute a null action.
			-- (from ANY)
		do
		ensure -- from ANY
			instance_free: class
		end
	
feature {NONE} -- Implementation

	c_pointer_length_in_bytes (a_ptr: POINTER): NATURAL_64
			-- Length in bytes of a platform specific file name pointer, not
			-- including the null-terminating character.
			-- (from NATIVE_STRING_HANDLER)
		require -- from NATIVE_STRING_HANDLER
			a_ptr_not_null: a_ptr /= default_pointer
		external
			"C inline use %"eif_eiffel.h%""
		alias
			"{
			#ifdef EIF_WINDOWS
				return (EIF_NATURAL_64) wcslen($a_ptr) * sizeof(wchar_t);
			#else
				return (EIF_NATURAL_64) strlen($a_ptr) * sizeof(char);
			#endif
			}"
		end

	Platform: PLATFORM
			-- Access underlying platform info, used to satisfy invariant below.
		once
			create Result
		end

	Upper_cell: CELL [INTEGER_32]
			-- Temporary storage for byte index of last insertions when using UTF_CONVERTER routines.
		once
			create Result.put (0)
		end
	
feature -- Output

	Io: STD_FILES
			-- Handle to standard file setup
			-- (from ANY)
		once
			create Result;
			Result.set_output_default
		ensure -- from ANY
			instance_free: class
			io_not_void: Result /= Void
		end

	out: STRING_8
			-- New string containing terse printable representation
			-- of current object
			-- (from ANY)
		do
			Result := tagged_out
		ensure -- from ANY
			out_not_void: Result /= Void
		end

	print (o: detachable ANY)
			-- Write terse external representation of o
			-- on standard output.
			-- (from ANY)
		local
			s: READABLE_STRING_8
		do
			if attached o then
				s := o.out
				if attached {READABLE_STRING_32} s as s32 then
					Io.put_string_32 (s32)
				elseif attached {READABLE_STRING_8} s as s8 then
					Io.put_string (s8)
				else
					Io.put_string_32 (s.as_string_32)
				end
			end
		ensure -- from ANY
			instance_free: class
		end

	frozen tagged_out: STRING_8
			-- New string containing terse printable representation
			-- of current object
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			tagged_out_not_void: Result /= Void
		end
	
feature -- Platform

	Operating_environment: OPERATING_ENVIRONMENT
			-- Objects available from the operating system
			-- (from ANY)
		once
			create Result
		ensure -- from ANY
			instance_free: class
			operating_environment_not_void: Result /= Void
		end
	
feature {NONE} -- Retrieval

	frozen internal_correct_mismatch
			-- Called from runtime to perform a proper dynamic dispatch on correct_mismatch
			-- from MISMATCH_CORRECTOR.
			-- (from ANY)
		local
			l_msg: STRING_32
			l_exc: EXCEPTIONS
		do
			if attached {MISMATCH_CORRECTOR} Current as l_corrector then
				l_corrector.correct_mismatch
			else
				create l_msg.make_from_string ("Mismatch: ".as_string_32)
				create l_exc;
				l_msg.append (generating_type.name_32);
				l_exc.raise_retrieval_exception (l_msg)
			end
		end
	
feature -- Status Report

	is_empty: BOOLEAN
			-- Is current empty?
		do
			Result := unit_count = 0
		end
	
invariant
	little_endian_windows: {PLATFORM}.is_windows implies Platform.Is_little_endian
	even_count_on_windows: {PLATFORM}.is_windows implies managed_data.count \\ unit_size = 0

		-- from ANY
	reflexive_equality: standard_is_equal (Current)
	reflexive_conformance: conforms_to (Current)

note
	copyright: "Copyright (c) 1984-2020, 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 NATIVE_STRING

Generated by ISE EiffelStudio