From: Colin P. A. <co...@co...> - 2008-11-15 22:05:57
|
indexing description: "[ Fast UTF-32 read-only strings. The emphasis is on cheap substring operations. Therefore `area' does not include an extra item for NULL termination (for C-strings) nor an initial extra item to avoid the cost of index translation. ]" library: "Gobo Eiffel String Library" copyright: "Copyright (c) 2008, Colin Adams and others" license: "MIT License" date: "$Date: $" revision: "$Revision: $" class ST_STRING inherit IMMUTABLE_STRING_GENERAL redefine is_equal end HASHABLE redefine is_equal end TO_SPECIAL [NATURAL_32] rename item as special_item, infix "@" as special_item_alias export {NONE} all {ST_STRING} area redefine is_equal, valid_index end ASCII export {NONE} all redefine is_equal end UC_IMPORTED_UNICODE_ROUTINES export {NONE} all redefine is_equal end ST_STRING_ROUTINES export {NONE} all redefine is_equal end ST_SHARED_STRINGS export {NONE} all redefine is_equal end ST_IMPORTED_UNICODE_FULL_CASE_MAPPING export {NONE} all redefine is_equal end ST_IMPORTED_UNICODE_NORMALIZATION_ROUTINES export {NONE} all redefine is_equal end create make, make_from_string, make_shared, make_from_codepoints, make_from_codes_list, make_empty, make_filled convert to_string: {STRING_32}, to_utf8_string: {UC_UTF8_STRING, STRING_8} feature {NONE} -- Initialization make (a_size: INTEGER) is -- Initialize to `a_size' NULLs. require a_size_non_negative: a_size >= 0 do make_area (a_size) if a_size > 0 then lower := 1 end ensure correct_count: count = a_size lower_is_zero_for_empty_string: a_size = 0 implies lower = 0 lower_is_one: a_size > 0 implies lower = 1 end make_empty is -- Initialize as empty string. do make (0) ensure empty: is_empty end make_filled (a_code: NATURAL_32; a_size: INTEGER) is -- Initialize with `a_size' characters all of codepoint `a_code'. require a_code_valid: valid_code (a_code) a_size_non_negative: a_size >= 0 local i: INTEGER do make_area (a_size) if a_size > 0 then lower := 1 end from i := 1 upper := a_size until i > upper loop area.put (a_code, i - 1) i := i + 1 end ensure correct_count: count = a_size all_characters_correct: -- not yet supported by ISE: for_all (agent a_code.is_equal) end make_from_string (a_string: READABLE_STRING_GENERAL) is -- Initialize to same characters as `a_string'. require a_string_not_void: a_string /= Void local i: INTEGER do make_area (a_string.count) if a_string.count > 0 then lower := 1 end from i := 1 upper := a_string.count until i > upper loop area.put (a_string.code (i), i - 1) i := i + 1 end ensure correct_count: count = a_string.count correct_character_sequence: for_all_with_index (agent same_item_as_string (a_string, ?, ?)) end make_from_codepoints (a_array: ARRAY [NATURAL_32]) is -- Initialize `Current' by copying codepoints from `a_array'. require a_array_not_void: a_array /= Void a_array_all_valid: a_array.for_all (agent valid_code) local i: INTEGER do make_area (a_array.count) if a_array.count > 0 then lower := 1 end from i := 1 upper := a_array.count until i > upper loop area.put (a_array.item (i), i - 1) i := i + 1 end ensure correct_count: count = a_array.count correct_character_sequence: for_all_with_index (agent same_item_as_array (a_array, ?, ?)) end make_from_codes_list (a_array: DS_ARRAYED_LIST [INTEGER_32]) is -- Initialize `Current' by copying codepoints from `a_array'. require a_array_not_void: a_array /= Void local i: INTEGER do make_area (a_array.count) if a_array.count > 0 then lower := 1 end from i := 1 upper := a_array.count until i > upper loop area.put (a_array.item (i).as_natural_32, i - 1) i := i + 1 end ensure correct_count: count = a_array.count end make_shared (a_area: like area; a_start, a_end: INTEGER) is -- Initializing sharing `a_area' from `a_start' to `a_end' inclusive. require a_area_not_void: a_area /= Void a_start_large_enough: a_start > 0 a_end_large_enough: a_end > 0 a_end_not_smaller_than_a_start: a_end >= a_start do area := a_area lower := a_start upper := a_end ensure area_set: area = a_area lower_set: lower = a_start upper_set: upper = a_end end feature -- Access lower: INTEGER -- Index of first character upper: INTEGER -- Index of last character code (a_index: INTEGER): NATURAL_32 is -- Unicode codepoint at `a_index' do Result := area.item (a_index + lower - 2) ensure then valid_code: valid_code (Result) end item, infix "@" (a_index: INTEGER): CHARACTER_32 is -- Unicode character at `a_index' require a_index_valid: valid_index (a_index) do Result := code (a_index).to_character_32 ensure valid_character: unicode.valid_non_surrogate_code (Result.code) end hash_code: INTEGER is -- Hash code value local i: INTEGER l_area: like area do Result := internal_hash_code if Result = 0 then if not is_empty then -- The magic number `8388593' below is the greatest prime lower than -- 2^23 so that this magic number shifted to the left does not exceed 2^31. from i := lower - 1 l_area := area until i = upper loop Result := ((Result \\ 8388593) |<< 8) + l_area.item (i).to_integer_32 i := i + 1 end internal_hash_code := Result end end end maximal_split (a_separators: READABLE_STRING_GENERAL): DS_LIST [ST_STRING] is -- `Current' tokenized by `a_separators'; -- Each character in `Current' matching any in `a_separators' is discarded, -- and a (possibly empty) token is extracted. -- E.g. "//my/shares/top\directory/in/path/".maximal_split ("/\") yields: -- ("","","my","shares","top","directory","in","path",""). require a_separators_not_void: a_separators /= Void do Result := internal_split (a_separators, True) ensure maximal_split_not_void: Result /= Void no_void_item: not Result.has (Void) end minimal_split (a_separators: READABLE_STRING_GENERAL): DS_LIST [ST_STRING] is -- `Current' tokenized by `a_separators'; -- Each character in `Current' matching any in `a_separators' is discarded, -- and a token is extracted. Consecutive separator characters do not yield -- additional tokens. -- E.g. "//my/shares/top\directory/in/path/".maximal_split ("/\") yields: -- ("","my","shares","top","directory","in","path",""). require a_separators_not_void: a_separators /= Void do Result := internal_split (a_separators, False) ensure minimal_split_not_void: Result /= Void no_void_item: not Result.has (Void) end trimmed: ST_STRING is -- Version of `Current' without leading or trailing XML white space characters (SPACE, TAB, LF and CR) do Result := stripped_of (" %T%N%R", True, True) ensure trimmed_not_void: Result /= Void not_longer: Result.count <= count no_leading_white_space: not Result.is_empty implies not (" %T%N%R").has_code (Result.code (1)) no_trailing_forbidden_character: not Result.is_empty implies not (" %T%N%R").has_code (Result.code (Result.count)) end stripped_of (a_forbidden: READABLE_STRING_GENERAL; a_leading_stripped, a_trailing_stripped: BOOLEAN): ST_STRING is -- Version of `Current' without any leading/trailing characters from `a_forbidden'; require a_forbidden_not_void: a_forbidden /= Void local l_leading_index, l_trailing_index: INTEGER do l_leading_index := 1 l_trailing_index := count if a_leading_stripped then from until not valid_index (l_leading_index) or else not a_forbidden.has_code (code (l_leading_index)) loop l_leading_index := l_leading_index + 1 end end if a_trailing_stripped then from until (not valid_index (l_trailing_index) or l_trailing_index = l_leading_index) or else (not a_forbidden.has_code (code (l_trailing_index))) loop l_trailing_index := l_trailing_index - 1 end end Result := substring (l_leading_index, l_trailing_index) ensure stripped_of_not_void: Result /= Void not_longer: Result.count <= count no_leading_forbidden_character: (a_leading_stripped and not Result.is_empty) implies not a_forbidden.has_code (Result.code (1)) no_trailing_forbidden_character: (a_trailing_stripped and not Result.is_empty) implies not a_forbidden.has_code (Result.code (Result.count)) end substring_index (a_other: ST_STRING; a_start_index: INTEGER): INTEGER is -- Index of first occurrence of `a_other' at or after `a_start_index'; -- 0 if none require a_other_not_void: a_other /= Void valid_a_start_index: a_start_index >= 1 and a_start_index <= count + 1 do Result := string_searcher.substring_index (Current, a_other, a_start_index, count) ensure valid_result: Result = 0 or else (a_start_index <= Result and Result <= count - a_other.count + 1) zero_if_absent: (Result = 0) = not substring (a_start_index, count).has_substring (a_other) at_this_index: Result >= a_start_index implies a_other.same_string (substring (Result, Result + a_other.count - 1)) none_before: Result > a_start_index implies not substring (a_start_index, Result + a_other.count - 2).has_substring (a_other) end feature -- Measurement count: INTEGER -- Number of characters in `Current' do if upper = 0 then Result := 0 else Result := upper - lower + 1 end end capacity: INTEGER is -- Number of characters allocated in Current do Result := count end occurrences (c: NATURAL_32): INTEGER is -- Number of times `c' appears in the string local i, l_count: INTEGER a: SPECIAL [NATURAL_32] do from l_count := count a := area until i = l_count loop if a.item (i) = c then Result := Result + 1 end i := i + 1 end ensure then zero_if_empty: count = 0 implies Result = 0 recurse_if_not_found_at_first_position: (count > 0 and then code (1) /= c) implies Result = substring (2, count).occurrences (c) recurse_if_found_at_first_position: (count > 0 and then code (1) = c) implies Result = 1 + substring (2, count).occurrences (c) end feature -- Comparison infix "<" (a_other: like Current): BOOLEAN is -- Is current object less than `other'? local l_count, l_other_count: INTEGER do if a_other /= Current then l_count := count l_other_count := a_other.count if l_count < l_other_count then Result := (leading_string_comparison (a_other, l_count) >= 0) else Result := (leading_string_comparison (a_other, l_other_count) > 0) end end end is_equal (a_other: like Current): BOOLEAN is -- Is `a_other' attached to an object of the same type -- as current object and identical to it? do Result := same_string (a_other) end same_case_insensitive (a_other: READABLE_STRING_GENERAL): BOOLEAN is -- Does `Current' represent the same character sequence (ignoring_case) as `a_other'? -- Full case folding is not yet implemented (TODO). -- Needs a more efficient implementation (TODO). -- Normalization is not considered. To check that two strings are -- equal under a given normalization form, compare normalized -- copies of the two strings. require a_other_not_void: a_other /= Void local l_string: ST_STRING do if a_other = Current then Result := True elseif a_other.count = count then l_string ?= a_other if l_string = Void then create l_string.make_from_string (a_other) end Result := to_lower.same_string (l_string.to_lower) end end feature -- Status report valid_index (i: INTEGER): BOOLEAN is -- Is `i' within the bounds of the string? do Result := i > 0 and i <= count end valid_code (a_code: like code): BOOLEAN is -- Is `a_code' a valid Unicode codepoint? do Result := unicode.valid_non_surrogate_code (a_code.as_integer_32) end is_natural, is_natural_32: BOOLEAN is -- Does `Current' represent a 32-bit unsigned integer? do Result := is_valid_integer_or_natural ({NUMERIC_INFORMATION}.type_natural_32) end is_integer, is_integer_32: BOOLEAN is -- Does `Current' represent a 32-bit signed integer? do Result := is_valid_integer_or_natural ({NUMERIC_INFORMATION}.type_integer_32) end is_integer_64: BOOLEAN is -- Does `Current' represent a 64-bit signed_integer? do Result := is_valid_integer_or_natural ({NUMERIC_INFORMATION}.type_integer_64) end is_double: BOOLEAN is -- Does `Current' represent a DOUBLE? local l_convertor: like ctor_convertor do if is_valid_as_string_8 then l_convertor := ctor_convertor l_convertor.parse_string_with_type (Current, {NUMERIC_INFORMATION}.type_double) Result := l_convertor.is_integral_double end ensure syntax_and_range: -- 'Result' is True if and only if the following two -- conditions are satisfied: -- -- 1. In the following BNF grammar, the value of -- 'Current' can be produced by "Real_literal": -- -- Real_literal = Mantissa [Exponent_part] -- Exponent_part = "E" Exponent -- | "e" Exponent -- Exponent = Integer_literal -- Mantissa = Decimal_literal -- Decimal_literal = Integer_literal ["." [Integer]] | "." Integer -- Integer_literal = [Sign] Integer -- Sign = "+" | "-" -- Integer = Digit | Digit Integer -- Digit = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9" -- -- 2. The numerical value represented by 'Current' -- is within the range that can be represented -- by an instance of type DOUBLE. end is_real: BOOLEAN is -- Does `Current' represent a REAL? do Result := is_double ensure syntax_and_range: -- 'Result' is True if and only if the following two -- conditions are satisfied: -- -- 1. In the following BNF grammar, the value of -- 'Current' can be produced by "Real_literal": -- -- Real_literal = Mantissa [Exponent_part] -- Exponent_part = "E" Exponent -- | "e" Exponent -- Exponent = Integer_literal -- Mantissa = Decimal_literal -- Decimal_literal = Integer_literal ["." [Integer]] | "." Integer -- Integer_literal = [Sign] Integer -- Sign = "+" | "-" -- Integer = Digit | Digit Integer -- Digit = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9" -- -- 2. The numerical value represented by 'Current' -- is within the range that can be represented -- by an instance of type REAL. end is_empty: BOOLEAN is -- Is structure empty? do Result := (count = 0) end is_string_8: BOOLEAN is -- Is `Current' a STRING_8? do end is_string_32: BOOLEAN is -- Is `Current' a STRING_32? do end is_valid_as_string_8: BOOLEAN is -- Is `Current' convertible to STRING_8 without information loss? do Result := for_all (agent is_character_8) end is_hexadecimal: BOOLEAN is -- Is `Current' made up of characters 0-9 or A-F or a-f? local i, l_count: INTEGER l_code: NATURAL_32 do l_count := count if l_count = 0 then Result := False else Result := True from i := 1 until i > l_count loop l_code := code (i) if (l_code < Zero.as_natural_32 or l_code > Nine.as_natural_32) and (l_code < Lower_a.as_natural_32 or l_code > Lower_f.as_natural_32) and (l_code < Upper_a.as_natural_32 or l_code > Upper_f.as_natural_32) then Result := False i := l_count + 1 -- Jump out of the loop. else i := i + 1 end end end end is_base64: BOOLEAN is -- Is `Current' made up of characters +, /, =, XML whitespace, 0-9 or A-Z or a-z? local i, l_count: INTEGER l_code: NATURAL_32 do l_count := count if l_count = 0 then Result := False else Result := True from i := 1 until i > l_count loop l_code := code (i) if (l_code < Zero.as_natural_32 or l_code > Nine.as_natural_32) and (l_code < Lower_a.as_natural_32 or l_code > Lower_z.as_natural_32) and (l_code < Upper_a.as_natural_32 or l_code > Upper_z.as_natural_32) and l_code /= Plus.as_natural_32 and l_code /= Slash.as_natural_32 and l_code /= Equal_sign.as_natural_32 and l_code /= Blank.as_natural_32 and l_code /= Tabulation.as_natural_32 and l_code /= Carriage_return.as_natural_32 and l_code /= Line_feed.as_natural_32 then Result := False i := l_count + 1 -- Jump out of the loop. else i := i + 1 end end end end has_substring (a_other: ST_STRING): BOOLEAN is -- Does `Current' contain `a_other'? require a_other_not_void: a_other /= Void do if a_other = Current then Result := True elseif a_other.count <= count then Result := substring_index (a_other, 1) > 0 end ensure false_if_too_small: count < a_other.count implies not Result true_if_initial: (count >= a_other.count and then a_other.same_string (substring (1, a_other.count))) implies Result recurse: (count >= a_other.count and then not a_other.same_string (substring (1, a_other.count))) implies (Result = substring (2, count).has_substring (a_other)) end is_nfd: BOOLEAN is -- Is `Current' in NFD normal form? do Result := normalization.is_nfd (Current) end is_nfkd: BOOLEAN is -- Is `Current' in NFKD normal form? do Result := normalization.is_nfkd (Current) end is_nfc: BOOLEAN is -- Is `Current' in NFC normal form? do Result := normalization.is_nfc (Current) end is_nfkc: BOOLEAN is -- Is `Current' in NFKC normal form? do Result := normalization.is_nfkc (Current) end feature -- New strings concatenated_before (a_other: READABLE_STRING_GENERAL): ST_STRING is -- New string formed by concatenating `Current' before `a_other' require a_other_not_void: a_other /= Void local l_builder: ST_STRING_BUILDER do create l_builder.make (count + a_other.count) l_builder.append_strings (<<Current, a_other>>) l_builder.set_built Result := l_builder.new_string ensure concatenated_with_not_void: Result /= Void correct_count: Result.count = count + a_other.count correct_prefix: Result.substring (1, count).same_string (Current) correct_suffix: Result.substring (1 + count, Result.count).same_string (a_other) end concatenated_after (a_other: READABLE_STRING_GENERAL): ST_STRING is -- New string formed by concatenating `Current' after `a_other' require a_other_not_void: a_other /= Void local l_builder: ST_STRING_BUILDER do create l_builder.make (count + a_other.count) l_builder.append_strings (<<a_other, Current>>) l_builder.set_built Result := l_builder.new_string ensure concatenated_with_not_void: Result /= Void correct_count: Result.count = count + a_other.count correct_suffix: Result.substring (1 + a_other.count, Result.count).same_string (Current) correct_prefix: Result.substring (1, a_other.count).same_string (a_other) end concatenated_before_strings (a_strings: ARRAY [READABLE_STRING_GENERAL]): ST_STRING is -- New string formed by concatenating `Current' before all of `a_strings'. require a_strings_not_void: a_strings /= Void all_strings_not_void: not a_strings.has (Void) local l_builder: ST_STRING_BUILDER do create l_builder.make (count + string_sums (a_strings)) l_builder.append_string (Current) l_builder.append_strings (a_strings) l_builder.set_built Result := l_builder.new_string ensure concatenated_before_strings_not_void: Result /= Void correct_count: Result.count = count + string_sums (a_strings) correct_prefix: Result.substring (1, count).same_string (Current) correct_suffix: True -- TODO end concatenated_before_code (a_code: NATURAL_32): ST_STRING is -- New string formed by concatenating `Current' before `a_code' local l_builder: ST_STRING_BUILDER do create l_builder.make (count + 1) l_builder.append_string (Current) l_builder.append_code (a_code) l_builder.set_built Result := l_builder.new_string ensure concatenated_with_not_void: Result /= Void correct_count: Result.count = count + 1 correct_prefix: Result.substring (1, count).same_string (Current) correct_suffix: Result.code (Result.count) = a_code end feature -- Iteration for_all (a_test: PREDICATE [ANY, TUPLE [NATURAL_32]]): BOOLEAN is -- Is `a_test' true for all codepoints? require a_test_not_void: a_test /= Void local l_tuple: TUPLE [NATURAL_32] i: INTEGER do Result := True if count > 0 then from create l_tuple i := lower until i > upper or not Result loop l_tuple.put (area.item (i - 1), 1) Result := a_test.item (l_tuple) i := i + 1 end end end for_all_with_index (a_test: PREDICATE [ANY, TUPLE [NATURAL_32, INTEGER]]): BOOLEAN is -- Is `a_test' true for all codepoints? require a_test_not_void: a_test /= Void local l_tuple: TUPLE [NATURAL_32, INTEGER] i: INTEGER do Result := True if count > 0 then from create l_tuple i := lower until i > upper or not Result loop l_tuple.put (area.item (i - 1), 1) l_tuple.put (i - lower + 1, 2) Result := a_test.item (l_tuple) i := i + 1 end end end do_all, do_forward (a_action: PROCEDURE [ANY, TUPLE [NATURAL_32]]) is -- Apply `a_action' to all codepoints. -- `a_action' must be precondition-free. require a_action_not_void: a_action /= Void local l_tuple: TUPLE [NATURAL_32] i: INTEGER do if count > 0 then from create l_tuple i := lower until i > upper loop l_tuple.put (area.item (i - 1), 1) a_action.call (l_tuple) i := i + 1 end end end do_all_with_index, do_forward_with_index (a_action: PROCEDURE [ANY, TUPLE [NATURAL_32, INTEGER]]) is -- Apply `a_action' to all codepoints, passing index relative to `lower'. -- `a_action' must be precondition-free. require a_action_not_void: a_action /= Void local l_tuple: TUPLE [NATURAL_32, INTEGER] i: INTEGER do if count > 0 then from create l_tuple i := lower until i > upper loop l_tuple.put (area.item (i - 1), 1) l_tuple.put (i - lower + 1, 2) a_action.call (l_tuple) i := i + 1 end end end feature -- Conversion to_string: STRING_32 is -- `Current' converted to STRING_32 do create Result.make_filled (('%/0/').to_character_32, count) do_all_with_index (agent Result.put_code) ensure to_string_not_void: Result /= Void correct_count: count = Result.count correct_character_sequence: for_all_with_index (agent same_item_as_string (Result, ?, ?)) end to_latin_1_string: STRING_8 is -- `Current' converted to ISO-8859-1 require latin_1_string: is_valid_as_string_8 do create Result.make_filled ('%/0/', count) do_all_with_index (agent Result.put_code) end to_utf8_string: UC_UTF8_STRING is -- `Current' converted to UTF-8 do create Result.make_from_string_general (Current) ensure to_utf_8_not_void: Result /= Void correct_count: count = Result.count correct_character_sequence: for_all_with_index (agent same_item_as_string (Result, ?, ?)) end to_natural, to_natural_32: NATURAL_32 is -- 32-bit natural value require is_natural: is_natural_32 local l_convertor: like ctoi_convertor do l_convertor := ctoi_convertor l_convertor.parse_string_with_type (Current, {NUMERIC_INFORMATION}.type_no_limitation) Result := l_convertor.parsed_natural_32 end to_integer, to_integer_32: INTEGER_32 is -- 32-bit signed integer value require is_integer: is_integer_32 local l_convertor: like ctoi_convertor do l_convertor := ctoi_convertor l_convertor.parse_string_with_type (Current, {NUMERIC_INFORMATION}.type_no_limitation) Result := l_convertor.parsed_integer end to_integer_64: INTEGER_64 is -- 64-bit integer value require is_integer_64: is_integer_64 local l_convertor: like ctoi_convertor do l_convertor := ctoi_convertor l_convertor.parse_string_with_type (Current, {NUMERIC_INFORMATION}.type_no_limitation) Result := l_convertor.parsed_integer_64 end to_double: DOUBLE is -- "Double" value; -- for example, when applied to "123.0", will yield 123.0 (double) require represents_a_double: is_double local l_convertor: like ctor_convertor do l_convertor := ctor_convertor l_convertor.parse_string_with_type (Current, {NUMERIC_INFORMATION}.type_no_limitation) Result := l_convertor.parsed_double end to_real: REAL is -- Real value; -- for example, when applied to "123.0", will yield 123.0 require represents_a_real: is_real do Result := to_double end to_lower: ST_STRING is -- New lower-case version of `Current' do Result := case_mapping.lower_string (Current) ensure to_lower_not_void: Result /= Void end to_upper: ST_STRING is -- New upper-case version of `Current' do Result := case_mapping.upper_string (Current) ensure to_upper_not_void: Result /= Void end as_nfd: ST_STRING is -- Canonical decomposition of `Current'; do Result := normalization.as_nfd_st_string (Current) ensure as_nfd_not_void: Result /= Void is_nfd: Result.is_nfd end to_nfd: ST_STRING is -- Canonical decomposition of `Current'; do Result := normalization.to_nfd_st_string (Current) ensure as_nfd_not_void: Result /= Void is_nfd: Result.is_nfd new_object: Result /= Current end as_nfkd: ST_STRING is -- Compatibility decomposition of `Current'; do Result := normalization.as_nfkd_st_string (Current) ensure as_nfkd_not_void: Result /= Void is_nfkd: Result.is_nfkd end to_nfkd: ST_STRING is -- Compatibility decomposition of `Current'; do Result := normalization.to_nfkd_st_string (Current) ensure as_nfkd_not_void: Result /= Void is_nfkd: Result.is_nfkd new_object: Result /= Current end to_nfc: ST_STRING is -- Canonical decomposition then canonical composition of `Current'; do Result := normalization.to_nfc_st_string (Current) ensure as_nfc_not_void: Result /= Void is_nfc: Result.is_nfc new_object: Result /= Current end to_nfkc: ST_STRING is -- Compatibility decomposition then canonical composition of `Current'; do Result := normalization.to_nfkc_st_string (Current) ensure as_nfkc_not_void: Result /= Void is_nfkc: Result.is_nfkc new_object: Result /= Current end feature -- Duplication substring (a_start, a_end: INTEGER): ST_STRING is -- Shared-memory substring containing all characters at indices between `a_start' and `a_end'; -- Memory is not shared if `Result' is empty string. do if a_end < a_start then Result := shared_empty_string else create Result.make_shared (area, a_start + lower - 1, a_end + lower - 1) end ensure then empty_string: a_end < a_start implies Result.is_empty lower: not Result.is_empty implies Result.lower = lower + a_start - 1 upper: not Result.is_empty implies Result.upper = a_end +lower - 1 same_area: not Result.is_empty implies Result.area = area substring_not_void: Result /= Void end feature {NONE} -- Contract support same_item_as_string (a_string: READABLE_STRING_GENERAL; a_code: NATURAL_32; a_index: INTEGER): BOOLEAN is -- Does `a_code' equal `a_string.code (a_index)'? require a_string_not_void: a_string /= Void a_index_valid: valid_index (a_index) do Result := a_code = a_string.code (a_index) ensure definition: Result = (a_code = a_string.code (a_index)) end same_item_as_array (a_array: ARRAY [NATURAL_32]; a_code: NATURAL_32; a_index: INTEGER): BOOLEAN is -- Does `a_code' equal `a_array.item (a_index)'? require a_array_not_void: a_array /= Void a_index_valid: valid_index (a_index) do Result := a_code = a_array.item (a_index) ensure definition: Result = (a_code = a_array.item (a_index)) end feature {NONE} -- Implementation leading_string_comparison (a_other: READABLE_STRING_GENERAL; a_count: INTEGER): INTEGER is -- Comparison of first `a_count' characters of `Current' with `a_other' -- 0 if equal, < 0 if `Current' < `a_other', -- Otherwise > 0 require a_other_not_void: a_other /= Void valid_count: a_count <= count and a_count <= a_other.count local i: INTEGER l_code, l_other_code: NATURAL_32 do from i := 1 until i > a_count loop l_code := code (i) l_other_code := a_other.code (i) if l_code /= l_other_code then Result := (l_code.to_integer_32 - l_other_code.to_integer_32) i := a_count -- Jump out of loop end i := i + 1 end end internal_split (a_separators: READABLE_STRING_GENERAL; a_maximal: BOOLEAN): DS_LINKED_LIST [ST_STRING] is require a_separators_not_void: a_separators /= Void local i, l_count, l_last: INTEGER l_last_separator: INTEGER l_code: NATURAL_32 l_item: ST_STRING do create Result.make l_count := count if l_count > 0 then from i := 1 l_last_separator := 0 l_last := 1 invariant last_separator: l_last_separator < i last_split: l_last <= i until i > l_count loop l_code := code (i) if a_separators.has_code (l_code) then if l_last_separator = i - 1 then if a_maximal then Result.force_last (shared_empty_string) end else if l_item = Void then l_item := substring (l_last, i - 1) else l_item := l_item.concatenated_before (substring (l_last, i - 1)) end Result.force_last (l_item) end l_item := Void l_last_separator := i l_last := i + 1 end i := i + 1 end if l_last_separator = 0 then if l_item = Void then l_item := Current else l_item := l_item.concatenated_before (substring (l_last, l_count)) end Result.force_last (l_item) elseif l_last_separator < l_count then if l_item = Void then l_item := substring (l_last, l_count) else l_item := l_item.concatenated_before (substring (l_last, l_count)) end Result.force_last (l_item) elseif a_maximal then Result.force_last (shared_empty_string) end end ensure iternal_split_not_void: Result /= Void no_void_item: not Result.has (Void) end is_valid_integer_or_natural (type: INTEGER): BOOLEAN is -- Is `Current' a valid number according to given `type'? local l_convertor: like ctoi_convertor do if is_valid_as_string_8 then l_convertor := ctoi_convertor l_convertor.reset (type) l_convertor.parse_string_with_type (Current, type) Result := l_convertor.is_integral_integer end end feature {NONE} -- Agents is_same_code (a_other: READABLE_STRING_GENERAL; a_code: NATURAL_32; a_index: INTEGER): BOOLEAN is -- Does `a_other.code (a_index)' = `a_code'? require a_other_not_void: a_other /= Void a_index_valid: valid_index (a_index) do Result := a_other.code (a_index) = a_code ensure definition: Result = (a_other.code (a_index) = a_code) end is_character_8 (a_code: NATURAL_32): BOOLEAN is -- Is `a_code' in range for for CHARACTER_8? do Result := a_code < 256 ensure definition: Result = (a_code < 256) end invariant area_not_void: area /= Void all_code_points_valid: for_all (agent valid_code) count_is_not_greater_than_area_count: count <= area.count lower_large_enough: lower >= 0 lower_small_enough: lower <= area.count + 1 upper_large_enough: upper >= 0 upper_not_larger_than_lower: upper >= lower end -- Colin Adams Preston Lancashire |