PR# 19830 '{STRING}.left_adjust' error

Problem Report Summary
Submitter: hcater
Category: EiffelBase
Priority: Medium
Date: 2022/01/14
Class: Bug
Severity: Serious
Number: 19830
Release: 21.11
Confidential: No
Status: Closed
Responsible:
Environment: win
Synopsis: '{STRING}.left_adjust' error

Description
There is an issue with 'left_adjust' please see the screenshots of before and after.  Then what happens when I use 'twin' where the string loses the null character entirely after, because of the issue initiated originally with 'left_adjust'



To Reproduce

										
Problem Report Interactions
From:jfiat_es    Date:2022/02/02    Status: Closed    Download   
For the record, here is the answer sent by mail:

Alexander Kogtenkov from EiffelSoftware is also suggesting to replace this code:

  create string_array.make_from_special(string.as_string_32.area)
  create natural_16_array.make_filled (0, string_array.lower, string_array.upper)
  from
      i := string_array.lower
  until
      i > string_array.upper
  loop
      integer := string_array.item(i).code
      natural_16_array.put(integer.as_natural_16, i)
          -- forth
      i := i + 1
  end
  a_string := natural_16_array.to_c

with
a_string := {UTF_CONVERTER}.string_32_to_utf_16_0 (string.as_string32)

This code will make sure that a_string is 0-terminated
Unicode code points above 0xFFFF (for examples, for emoji) are correctly translated into UTF-16 surrogate pairs 
it is more efficient than the original

If string is in UTF-32, even simpler (and even more efficient code) is possible:
    a_string := {UTF_CONVERTER}.utf_32_string_to_utf_16_0 (string)

Note that in that case, th
....
Output truncated, Click download to get the full message

From:alexk_es    Date:2022/02/02    Status: Analyzed    Download   
With the provided solution sent by Jocelyn in an email message, shall we close this report?

From:jfiat_es    Date:2022/01/28    Status: Analyzed    Download   
Hi Hubert,

Sorry for the delay.

There are many other features in STRING_32 (and other variants, including STRING_8) that do not ensure that there is null at the end, keep_head as an example.
On the other hand, there is a feature to_c that makes sure that there is null at the end. I guess, users should be using to_c and do not expect that using area directly guarantees 0-terminated sequence of characters.

In addition your suggestion would slow down Eiffel code.
For C code, there is a dedicated mechanism already.

So for now, I would suggest adapting your code that interfaces with C code, to either ensure you use the feature "to_c" , and avoid using string's area directly.
Or eventually call directly   "  s.area.put ('%U', s.count)  " if you really need a null character ending C memory for the associated SPECIAL object.

From:jfiat_es    Date:2022/01/17    Status: Analyzed    Download   
Dear Hubert,

We are looking at the suggestion, and run our test suite.

From:hcater    Date:2022/01/15    Status: Open    Download   
This fixes a similar issue with 'remove_substring' in both STRING_32 and STRING_8

         remove_substring (start_index, end_index: INTEGER)
			-- Remove all characters from `start_index'
			-- to `end_index' inclusive.
		local
			l_count, nb_removed: INTEGER
		do
			nb_removed := end_index - start_index + 1
			if nb_removed > 0 then
				l_count := count
				area.overlapping_move (start_index + nb_removed - 1, start_index - 1, l_count - end_index + 1)
				area.remove_tail (nb_removed)
				count := l_count - nb_removed
				reset_hash_codes
			end
		end

From:hcater    Date:2022/01/14    Status: Open    Download   
Here are my changes which seem to resolve the issues in both 'left_adjust' and 'right_adjust' when it comes to adjusting the length and properly repositioning the null character '%U'

left_adjust
			-- Remove leading whitespace.
		local
			nb, nb_space: INTEGER
			l_area: like area
			l_prop: like character_properties
		do
			l_prop := character_properties

				-- Compute number of spaces at the left of current string.
			from
				nb := count - 1
				l_area := area
			until
				nb_space > nb or else not l_prop.is_space (l_area.item (nb_space))
			loop
				nb_space := nb_space + 1
			end

			if nb_space > 0 then
					-- Set new count value.
				nb := nb + 1 - nb_space
					-- Shift characters to the left.
				l_area.overlapping_move (nb_space, 0, nb + 1)
				l_area.remove_tail (nb_space)
					-- Set new count.
				count := nb
				reset_hash_codes
			end
		end

	right_adjust
			-- Remove trailing whitespace.
		local
			i, nb: INTEGER
			nb_space: INTEGER
			l_area: like are
....
Output truncated, Click download to get the full message

From:hcater    Date:2022/01/14    Download   
Attachments for problem report #19830

Attachment: after_left_adjust.jpg     Size:84339
Attachment: after_twin.jpg     Size:112318
Attachment: before_left_adjust.jpg     Size:84261
Attachment: before_twin.jpg     Size:95722