ST_STRING

1 message Options
Embed this post
Permalink
Colin Paul Adams

ST_STRING

Reply Threaded More More options
Print post
Permalink
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

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
gobo-eiffel-develop mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gobo-eiffel-develop