stdlib_string_type.fypp Source File


Source Code

! SPDX-Identifier: MIT
#:include "common.fypp"

!> Implementation of a string type to hold an arbitrary sequence of characters.
!>
!> This module provides string type compatible with all Fortran instrinsic character
!> procedures as well as overloaded operators for working with character variables.
!>
!> A string type can be easily constructed by creating a new instance from a
!> character variable or literal by invoking its constructor or by assigning it
!> to a string type. Generally, the string type behaves similar to a deferred
!> length character in most regards but adds memory access safety.
!>
!> The specification of this module is available [here](../page/specs/stdlib_string_type.html).
module stdlib_string_type
    use stdlib_ascii, only: to_lower_ => to_lower, to_upper_ => to_upper, &
       & to_title_ => to_title, to_sentence_ => to_sentence, reverse_ => reverse
    use stdlib_kinds, only : int8, int16, int32, int64, lk, c_bool
    use stdlib_optval, only: optval
    implicit none
    private

    public :: string_type
    public :: len, len_trim, trim, index, scan, verify, repeat, adjustr, adjustl
    public :: lgt, lge, llt, lle, char, ichar, iachar
    public :: to_lower, to_upper, to_title, to_sentence, reverse, move
    public :: assignment(=)
    public :: operator(>), operator(>=), operator(<), operator(<=)
    public :: operator(==), operator(/=), operator(//)
    public :: write(formatted), write(unformatted)
    public :: read(formatted), read(unformatted)


    integer, parameter :: long = selected_int_kind(18)


    !> String type holding an arbitrary sequence of characters.
    type :: string_type
        ! Use the sequence statement below as a hack to prevent extending this type.
        ! It is not used for storage association.
        sequence
        private
        character(len=:), allocatable :: raw
    end type string_type

    !> Returns the length of the character sequence represented by the string.
    !>
    !> This method is elemental and returns a default integer scalar value.
    interface len
        module procedure :: len_string
    end interface len

    !> Constructor for new string instances
    interface string_type
        elemental module function new_string(string) result(new)
            character(len=*), intent(in), optional :: string
            type(string_type) :: new
        end function new_string
        #:for kind in INT_KINDS
        elemental module function new_string_from_integer_${kind}$(val) result(new)
            integer(${kind}$), intent(in) :: val
            type(string_type) :: new
        end function new_string_from_integer_${kind}$
        #:endfor
        #:for kind in LOG_KINDS
        elemental module function new_string_from_logical_${kind}$(val) result(new)
            logical(${kind}$), intent(in) :: val
            type(string_type) :: new
        end function new_string_from_logical_${kind}$
        #:endfor
    end interface string_type        

    !> Returns the length of the character sequence without trailing spaces
    !> represented by the string.
    !>
    !> This method is elemental and returns a default integer scalar value.
    interface len_trim
        module procedure :: len_trim_string
    end interface len_trim

    !> Returns the character sequence hold by the string without trailing spaces.
    !>
    !> This method is elemental and returns a scalar character value.
    interface trim
        module procedure :: trim_string
    end interface trim

    !> Left-adjust the character sequence represented by the string.
    !> The length of the character sequence remains unchanged.
    !>
    !> This method is elemental and returns a scalar character value.
    interface adjustl
        module procedure :: adjustl_string
    end interface adjustl

    !> Right-adjust the character sequence represented by the string.
    !> The length of the character sequence remains unchanged.
    !>
    !> This method is elemental and returns a scalar character value.
    interface adjustr
        module procedure :: adjustr_string
    end interface adjustr

    !> Repeats the character sequence hold by the string by the number of
    !> specified copies.
    !>
    !> This method is elemental and returns a scalar character value.
    interface repeat
        module procedure :: repeat_string
    end interface repeat

    !> Returns the lowercase version of the character sequence hold by the input string
    !>
    !> This method is Elemental and returns a new string_type instance which holds this
    !> lowercase character sequence
    interface to_lower
        module procedure :: to_lower_string
    end interface to_lower

    !> Returns the uppercase version of the character sequence hold by the input string
    !>
    !> This method is Elemental and returns a new string_type instance which holds this
    !> uppercase character sequence
    interface to_upper
        module procedure :: to_upper_string
    end interface to_upper

    !> Returns the titlecase version of the character sequence hold by the input string
    !>
    !> This method is Elemental and returns a new string_type instance which holds this
    !> titlecase character sequence
    interface to_title
        module procedure :: to_title_string
    end interface to_title

    !> Returns the sentencecase version of the character sequence hold by the input string
    !>
    !> This method is elemental and returns a new string_type instance which holds this
    !> sentencecase character sequence
    interface to_sentence
        module procedure :: to_sentence_string
    end interface to_sentence

    !> Reverses the character sequence hold by the input string
    !> 
    !> This method is elemental and returns a new string_type instance which holds this
    !> reverse character sequence
    interface reverse
        module procedure :: reverse_string
    end interface reverse

    !> Return the character sequence represented by the string.
    !>
    !> This method is elemental and returns a scalar character value.
    interface char
        module procedure :: char_string
        module procedure :: char_string_pos
        module procedure :: char_string_range
    end interface char

    !> Character-to-integer conversion function.
    !>
    !> This method is elemental and returns a default integer scalar value.
    interface ichar
        module procedure :: ichar_string
    end interface ichar

    !> Code in ASCII collating sequence.
    !>
    !> This method is elemental and returns a default integer scalar value.
    interface iachar
        module procedure :: iachar_string
    end interface iachar

    !> Position of a *substring* within a *string*.
    !>
    !> Returns the position of the start of the leftmost or rightmost occurrence
    !> of string *substring* in *string*, counting from one. If *substring* is not
    !> present in *string*, zero is returned.
    !>
    !> This method is elemental and returns a default integer scalar value.
    interface index
        module procedure :: index_string_string
        module procedure :: index_string_char
        module procedure :: index_char_string
    end interface index

    !> Scan a *string* for the presence of a *set* of characters. Scans a *string* for
    !> any of the characters in a *set* of characters.
    !>
    !> If *back* is either absent or *false*, this function returns the position
    !> of the leftmost character of *string* that is in *set*. If *back* is *true*,
    !> the rightmost position is returned. If no character of *set* is found in
    !> *string*, the result is zero.
    !>
    !> This method is elemental and returns a default integer scalar value.
    interface scan
        module procedure :: scan_string_string
        module procedure :: scan_string_char
        module procedure :: scan_char_string
    end interface scan

    !> Scan a string for the absence of a set of characters. Verifies that all
    !> the characters in string belong to the set of characters in set.
    !>
    !> If *back* is either absent or *false*, this function returns the position
    !> of the leftmost character of *string* that is not in *set*. If *back* is *true*,
    !> the rightmost position is returned. If all characters of *string* are found
    !> in *set*, the result is zero.
    !>
    !> This method is elemental and returns a default integer scalar value.
    interface verify
        module procedure :: verify_string_string
        module procedure :: verify_string_char
        module procedure :: verify_char_string
    end interface verify

    !> Version: experimental
    !>
    !> Moves the allocated character scalar from 'from' to 'to'
    !> [Specifications](../page/specs/stdlib_string_type.html#move)
    interface move
        module procedure :: move_string_string
        module procedure :: move_string_char
        module procedure :: move_char_string
        module procedure :: move_char_char
    end interface move

    !> Lexically compare the order of two character sequences being greater,
    !> The left-hand side, the right-hand side or both character sequences can
    !> be represented by a string.
    !>
    !> This method is elemental and returns a default logical scalar value.
    interface lgt
        module procedure :: lgt_string_string
        module procedure :: lgt_string_char
        module procedure :: lgt_char_string
    end interface lgt

    !> Lexically compare the order of two character sequences being less,
    !> The left-hand side, the right-hand side or both character sequences can
    !> be represented by a string.
    !>
    !> This method is elemental and returns a default logical scalar value.
    interface llt
        module procedure :: llt_string_string
        module procedure :: llt_string_char
        module procedure :: llt_char_string
    end interface llt

    !> Lexically compare the order of two character sequences being greater equal,
    !> The left-hand side, the right-hand side or both character sequences can
    !> be represented by a string.
    !>
    !> This method is elemental and returns a default logical scalar value.
    interface lge
        module procedure :: lge_string_string
        module procedure :: lge_string_char
        module procedure :: lge_char_string
    end interface lge

    !> Lexically compare the order of two character sequences being less equal,
    !> The left-hand side, the right-hand side or both character sequences can
    !> be represented by a string.
    !>
    !> This method is elemental and returns a default logical scalar value.
    interface lle
        module procedure :: lle_string_string
        module procedure :: lle_string_char
        module procedure :: lle_char_string
    end interface lle

    !> Assign a character sequence to a string.
    interface assignment(=)
        module procedure :: assign_string_char
    end interface assignment(=)

    !> Compare two character sequences for being greater, the left-hand side,
    !> the right-hand side or both character sequences can be represented by
    !> a string.
    !>
    !> This operator is elemental and returns a default logical scalar value.
    interface operator(>)
        module procedure :: gt_string_string
        module procedure :: gt_string_char
        module procedure :: gt_char_string
    end interface operator(>)

    !> Compare two character sequences for being less, the left-hand side,
    !> the right-hand side or both character sequences can be represented by
    !> a string.
    !>
    !> This operator is elemental and returns a default logical scalar value.
    interface operator(<)
        module procedure :: lt_string_string
        module procedure :: lt_string_char
        module procedure :: lt_char_string
    end interface operator(<)

    !> Compare two character sequences for being greater than, the left-hand side,
    !> the right-hand side or both character sequences can be represented by
    !> a string.
    !>
    !> This operator is elemental and returns a default logical scalar value.
    interface operator(>=)
        module procedure :: ge_string_string
        module procedure :: ge_string_char
        module procedure :: ge_char_string
    end interface operator(>=)

    !> Compare two character sequences for being less than, the left-hand side,
    !> the right-hand side or both character sequences can be represented by
    !> a string.
    !>
    !> This operator is elemental and returns a default logical scalar value.
    interface operator(<=)
        module procedure :: le_string_string
        module procedure :: le_string_char
        module procedure :: le_char_string
    end interface operator(<=)

    !> Compare two character sequences for equality, the left-hand side,
    !> the right-hand side or both character sequences can be represented by
    !> a string.
    !>
    !> This operator is elemental and returns a default logical scalar value.
    interface operator(==)
        module procedure :: eq_string_string
        module procedure :: eq_string_char
        module procedure :: eq_char_string
    end interface operator(==)

    !> Compare two character sequences for inequality, the left-hand side,
    !> the right-hand side or both character sequences can be represented by
    !> a string.
    !>
    !> This operator is elemental and returns a default logical scalar value.
    interface operator(/=)
        module procedure :: ne_string_string
        module procedure :: ne_string_char
        module procedure :: ne_char_string
    end interface operator(/=)

    !> Concatenate two character sequences, the left-hand side, the right-hand side
    !> or both character sequences can be represented by a string.
    !>
    !> This operator is elemental and returns a scalar character value.
    interface operator(//)
        module procedure :: concat_string_string
        module procedure :: concat_string_char
        module procedure :: concat_char_string
    end interface operator(//)

    !> Write the character sequence hold by the string to a connected formatted
    !> unit.
    interface write(formatted)
        module procedure :: write_formatted
    end interface

    !> Write the character sequence hold by the string to a connected unformatted
    !> unit.
    interface write(unformatted)
        module procedure :: write_unformatted
    end interface

    !> Read a character sequence from a connected unformatted unit into the string.
    interface read(formatted)
        module procedure :: read_formatted
    end interface

    !> Read a character sequence from a connected unformatted unit into the string.
    interface read(unformatted)
        module procedure :: read_unformatted
    end interface


contains

    !> Assign a character sequence to a string.
    elemental subroutine assign_string_char(lhs, rhs)
        type(string_type), intent(inout) :: lhs
        character(len=*), intent(in) :: rhs
        lhs%raw = rhs
    end subroutine assign_string_char


    !> Returns the length of the character sequence represented by the string.
    elemental function len_string(string) result(length)
        type(string_type), intent(in) :: string
        integer :: length

        if (allocated(string%raw)) then
            length = len(string%raw)
        else
            length = 0
        end if

    end function len_string


    !> Returns the length of the character sequence without trailing spaces
    !> represented by the string.
    elemental function len_trim_string(string) result(length)
        type(string_type), intent(in) :: string
        integer :: length

        length = merge(len_trim(string%raw), 0, allocated(string%raw))

    end function len_trim_string


    !> Character-to-integer conversion function.
    elemental function ichar_string(string) result(ich)
        type(string_type), intent(in) :: string
        integer :: ich

        if (allocated(string%raw) .and. len(string) > 0) then
          ich = ichar(string%raw(1:1))
        else
          ich = 0
        end if

    end function ichar_string


    !> Code in ASCII collating sequence.
    elemental function iachar_string(string) result(ich)
        type(string_type), intent(in) :: string
        integer :: ich

        if (allocated(string%raw) .and. len(string) > 0) then
          ich = iachar(string%raw(1:1))
        else
          ich = 0
        end if

    end function iachar_string


    !> Return the character sequence represented by the string.
    pure function char_string(string) result(character_string)
        type(string_type), intent(in) :: string
        character(len=len(string)) :: character_string

        character_string = maybe(string)

    end function char_string

    !> Return the character sequence represented by the string.
    elemental function char_string_pos(string, pos) result(character_string)
        type(string_type), intent(in) :: string
        integer, intent(in) :: pos
        character(len=1) :: character_string

        character_string = merge(string%raw(pos:pos), ' ', allocated(string%raw))

    end function char_string_pos

    !> Return the character sequence represented by the string.
    pure function char_string_range(string, start, last) result(character_string)
        type(string_type), intent(in) :: string
        integer, intent(in) :: start
        integer, intent(in) :: last
        character(len=last-start+1) :: character_string

        character_string = merge(string%raw(int(start, long):int(last, long)), &
            repeat(' ', int(len(character_string), long)), allocated(string%raw))

    end function char_string_range


    !> Returns the character sequence hold by the string without trailing spaces.
    elemental function trim_string(string) result(trimmed_string)
        type(string_type), intent(in) :: string
        type(string_type) :: trimmed_string

        trimmed_string = trim(maybe(string))

    end function trim_string


    !> Left-adjust the character sequence represented by the string.
    !> The length of the character sequence remains unchanged.
    elemental function adjustl_string(string) result(adjusted_string)
        type(string_type), intent(in) :: string
        type(string_type) :: adjusted_string

        adjusted_string = adjustl(maybe(string))

    end function adjustl_string


    !> Right-adjust the character sequence represented by the string.
    !> The length of the character sequence remains unchanged.
    elemental function adjustr_string(string) result(adjusted_string)
        type(string_type), intent(in) :: string
        type(string_type) :: adjusted_string

        adjusted_string = adjustr(maybe(string))

    end function adjustr_string


    !> Repeats the character sequence hold by the string by the number of
    !> specified copies.
    elemental function repeat_string(string, ncopies) result(repeated_string)
        type(string_type), intent(in) :: string
        integer, intent(in) :: ncopies
        type(string_type) :: repeated_string

        repeated_string = repeat(maybe(string), ncopies)

    end function repeat_string


    !> Convert the character sequence hold by the input string to lower case
    elemental function to_lower_string(string) result(lowercase_string)
        type(string_type), intent(in) :: string
        type(string_type) :: lowercase_string

        lowercase_string%raw = to_lower_(maybe(string))

    end function to_lower_string


    !> Convert the character sequence hold by the input string to upper case
    elemental function to_upper_string(string) result(uppercase_string)
        type(string_type), intent(in) :: string
        type(string_type) :: uppercase_string

        uppercase_string%raw = to_upper_(maybe(string))

    end function to_upper_string


    !> Convert the character sequence hold by the input string to title case
    elemental function to_title_string(string) result(titlecase_string)
        type(string_type), intent(in) :: string
        type(string_type) :: titlecase_string

        titlecase_string%raw = to_title_(maybe(string))

    end function to_title_string

    !> Convert the character sequence hold by the input string to sentence case
    elemental function to_sentence_string(string) result(sentence_string)
        type(string_type), intent(in) :: string
        type(string_type) :: sentence_string

        sentence_string%raw = to_sentence_(maybe(string))

    end function to_sentence_string


    !> Reverse the character sequence hold by the input string
    elemental function reverse_string(string) result(reversed_string)
        type(string_type), intent(in) :: string
        type(string_type) :: reversed_string

        reversed_string%raw = reverse_(maybe(string))

    end function reverse_string


    !> Position of a sequence of character within a character sequence.
    !> In this version both character sequences are represented by a string.
    elemental function index_string_string(string, substring, back) result(pos)
        type(string_type), intent(in) :: string
        type(string_type), intent(in) :: substring
        logical, intent(in), optional :: back
        integer :: pos

        pos = index(maybe(string), maybe(substring), optval(back, .false.))

    end function index_string_string

    !> Position of a sequence of character within a character sequence.
    !> In this version the main character sequence is represented by a string.
    elemental function index_string_char(string, substring, back) result(pos)
        type(string_type), intent(in) :: string
        character(len=*), intent(in) :: substring
        logical, intent(in), optional :: back
        integer :: pos

        pos = index(maybe(string), substring, optval(back, .false.))

    end function index_string_char

    !> Position of a sequence of character within a character sequence.
    !> In this version the sub character sequence is represented by a string.
    elemental function index_char_string(string, substring, back) result(pos)
        character(len=*), intent(in) :: string
        type(string_type), intent(in) :: substring
        logical, intent(in), optional :: back
        integer :: pos

        pos = index(string, maybe(substring), optval(back, .false.))

    end function index_char_string


    !> Scan a character sequence for any of the characters in a set of characters.
    !> In this version both the character sequence and the character set are
    !> represented by a string.
    elemental function scan_string_string(string, set, back) result(pos)
        type(string_type), intent(in) :: string
        type(string_type), intent(in) :: set
        logical, intent(in), optional :: back
        integer :: pos

        pos = scan(maybe(string), maybe(set), optval(back, .false.))

    end function scan_string_string

    !> Scan a character sequence for any of the characters in a set of characters.
    !> In this version the character sequences is represented by a string.
    elemental function scan_string_char(string, set, back) result(pos)
        type(string_type), intent(in) :: string
        character(len=*), intent(in) :: set
        logical, intent(in), optional :: back
        integer :: pos

        pos = scan(maybe(string), set, optval(back, .false.))

    end function scan_string_char

    !> Scan a character sequence for any of the characters in a set of characters.
    !> In this version the set of characters is represented by a string.
    elemental function scan_char_string(string, set, back) result(pos)
        character(len=*), intent(in) :: string
        type(string_type), intent(in) :: set
        logical, intent(in), optional :: back
        integer :: pos

        pos = scan(string, maybe(set), optval(back, .false.))

    end function scan_char_string


    !> Verify a character sequence for the absence any of the characters in
    !> a set of characters. In this version both the character sequence and
    !> the character set are represented by a string.
    elemental function verify_string_string(string, set, back) result(pos)
        type(string_type), intent(in) :: string
        type(string_type), intent(in) :: set
        logical, intent(in), optional :: back
        integer :: pos

        pos = verify(maybe(string), maybe(set), optval(back, .false.))

    end function verify_string_string

    !> Verify a character sequence for the absence any of the characters in
    !> a set of characters. In this version the character sequences is
    !> represented by a string.
    elemental function verify_string_char(string, set, back) result(pos)
        type(string_type), intent(in) :: string
        character(len=*), intent(in) :: set
        logical, intent(in), optional :: back
        integer :: pos

        pos = verify(maybe(string), set, optval(back, .false.))

    end function verify_string_char

    !> Verify a character sequence for the absence any of the characters in
    !> a set of characters. In this version the set of characters is
    !> represented by a string.
    elemental function verify_char_string(string, set, back) result(pos)
        character(len=*), intent(in) :: string
        type(string_type), intent(in) :: set
        logical, intent(in), optional :: back
        integer :: pos

        pos = verify(string, maybe(set), optval(back, .false.))

    end function verify_char_string

    !> Moves the allocated character scalar from 'from' to 'to'
    !> No output
    elemental subroutine move_string_string(from, to)
        type(string_type), intent(inout), target :: from
        type(string_type), intent(inout), target :: to
        type(string_type), pointer :: fromp

        fromp => from
        if (associated(fromp,to)) return
        call move_alloc(from%raw, to%raw)

    end subroutine move_string_string

    !> Moves the allocated character scalar from 'from' to 'to'
    !> No output
    pure subroutine move_string_char(from, to)
        type(string_type), intent(inout) :: from
        character(len=:), intent(out), allocatable :: to

        call move_alloc(from%raw, to)

    end subroutine move_string_char

    !> Moves the allocated character scalar from 'from' to 'to'
    !> No output
    pure subroutine move_char_string(from, to)
        character(len=:), intent(inout), allocatable :: from
        type(string_type), intent(out) :: to

        call move_alloc(from, to%raw)

    end subroutine move_char_string

    !> Moves the allocated character scalar from 'from' to 'to'
    !> No output
    pure subroutine move_char_char(from, to)
        character(len=:), intent(inout), allocatable :: from
        character(len=:), intent(out), allocatable :: to

        call move_alloc(from, to)

    end subroutine move_char_char

    !> Compare two character sequences for being greater.
    !> In this version both character sequences are by a string.
    elemental function gt_string_string(lhs, rhs) result(is_gt)
        type(string_type), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_gt

        is_gt = maybe(lhs) > maybe(rhs)

    end function gt_string_string

    !> Compare two character sequences for being greater.
    !> In this version the left-hand side character sequences is by a string.
    elemental function gt_string_char(lhs, rhs) result(is_gt)
        type(string_type), intent(in) :: lhs
        character(len=*), intent(in) :: rhs
        logical :: is_gt

        is_gt = maybe(lhs) > rhs

    end function gt_string_char

    !> Compare two character sequences for being greater.
    !> In this version the right-hand side character sequences is by a string.
    elemental function gt_char_string(lhs, rhs) result(is_gt)
        character(len=*), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_gt

        is_gt = lhs > maybe(rhs)

    end function gt_char_string


    !> Compare two character sequences for being less.
    !> In this version both character sequences are by a string.
    elemental function lt_string_string(lhs, rhs) result(is_lt)
        type(string_type), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_lt

        is_lt = rhs > lhs

    end function lt_string_string


    !> Compare two character sequences for being less.
    !> In this version the left-hand side character sequences is by a string.
    elemental function lt_string_char(lhs, rhs) result(is_lt)
        type(string_type), intent(in) :: lhs
        character(len=*), intent(in) :: rhs
        logical :: is_lt

        is_lt = rhs > lhs

    end function lt_string_char

    !> Compare two character sequences for being less.
    !> In this version the right-hand side character sequences is by a string.
    elemental function lt_char_string(lhs, rhs) result(is_lt)
        character(len=*), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_lt

        is_lt = rhs > lhs

    end function lt_char_string


    !> Compare two character sequences for being greater or equal.
    !> In this version both character sequences are by a string.
    elemental function ge_string_string(lhs, rhs) result(is_ge)
        type(string_type), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_ge

        is_ge = .not. (rhs > lhs)

    end function ge_string_string

    !> Compare two character sequences for being greater or equal.
    !> In this version the left-hand side character sequences is by a string.
    elemental function ge_string_char(lhs, rhs) result(is_ge)
        type(string_type), intent(in) :: lhs
        character(len=*), intent(in) :: rhs
        logical :: is_ge

        is_ge = .not. (rhs > lhs)

    end function ge_string_char

    !> Compare two character sequences for being greater or equal
    !> In this version the right-hand side character sequences is by a string.
    elemental function ge_char_string(lhs, rhs) result(is_ge)
        character(len=*), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_ge

        is_ge = .not. (rhs > lhs)

    end function ge_char_string


    !> Compare two character sequences for being less or equal.
    !> In this version both character sequences are by a string.
    elemental function le_string_string(lhs, rhs) result(is_le)
        type(string_type), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_le

        is_le = .not. (lhs > rhs)

    end function le_string_string

    !> Compare two character sequences for being less or equal.
    !> In this version the left-hand side character sequences is by a string.
    elemental function le_string_char(lhs, rhs) result(is_le)
        type(string_type), intent(in) :: lhs
        character(len=*), intent(in) :: rhs
        logical :: is_le

        is_le = .not. (lhs > rhs)

    end function le_string_char

    !> Compare two character sequences for being less or equal
    !> In this version the right-hand side character sequences is by a string.
    elemental function le_char_string(lhs, rhs) result(is_le)
        character(len=*), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_le

        is_le = .not. (lhs > rhs)

    end function le_char_string


    !> Compare two character sequences for equality.
    !> In this version both character sequences are by a string.
    elemental function eq_string_string(lhs, rhs) result(is_eq)
        type(string_type), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_eq

        is_eq = .not.(lhs > rhs)
        if (is_eq) then
            is_eq = .not.(rhs > lhs)
        end if

    end function eq_string_string

    !> Compare two character sequences for equality.
    !> In this version the left-hand side character sequences is by a string.
    elemental function eq_string_char(lhs, rhs) result(is_eq)
        type(string_type), intent(in) :: lhs
        character(len=*), intent(in) :: rhs
        logical :: is_eq

        is_eq = .not.(lhs > rhs)
        if (is_eq) then
            is_eq = .not.(rhs > lhs)
        end if

    end function eq_string_char

    !> Compare two character sequences for equality.
    !> In this version the right-hand side character sequences is by a string.
    elemental function eq_char_string(lhs, rhs) result(is_eq)
        character(len=*), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_eq

        is_eq = .not.(lhs > rhs)
        if (is_eq) then
            is_eq = .not.(rhs > lhs)
        end if

    end function eq_char_string


    !> Compare two character sequences for inequality.
    !> In this version both character sequences are by a string.
    elemental function ne_string_string(lhs, rhs) result(is_ne)
        type(string_type), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_ne

        is_ne = lhs > rhs
        if (.not.is_ne) then
            is_ne = rhs > lhs
        end if

    end function ne_string_string

    !> Compare two character sequences for inequality.
    !> In this version the left-hand side character sequences is by a string.
    elemental function ne_string_char(lhs, rhs) result(is_ne)
        type(string_type), intent(in) :: lhs
        character(len=*), intent(in) :: rhs
        logical :: is_ne

        is_ne = lhs > rhs
        if (.not.is_ne) then
            is_ne = rhs > lhs
        end if

    end function ne_string_char

    !> Compare two character sequences for inequality.
    !> In this version the right-hand side character sequences is by a string.
    elemental function ne_char_string(lhs, rhs) result(is_ne)
        character(len=*), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_ne

        is_ne = lhs > rhs
        if (.not.is_ne) then
            is_ne = rhs > lhs
        end if

    end function ne_char_string


    !> Lexically compare two character sequences for being greater.
    !> In this version both character sequences are by a string.
    elemental function lgt_string_string(lhs, rhs) result(is_lgt)
        type(string_type), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_lgt

        is_lgt = lgt(maybe(lhs), maybe(rhs))

    end function lgt_string_string

    !> Lexically compare two character sequences for being greater.
    !> In this version the left-hand side character sequences is by a string.
    elemental function lgt_string_char(lhs, rhs) result(is_lgt)
        type(string_type), intent(in) :: lhs
        character(len=*), intent(in) :: rhs
        logical :: is_lgt

        is_lgt = lgt(maybe(lhs), rhs)

    end function lgt_string_char

    !> Lexically compare two character sequences for being greater.
    !> In this version the right-hand side character sequences is by a string.
    elemental function lgt_char_string(lhs, rhs) result(is_lgt)
        character(len=*), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_lgt

        is_lgt = lgt(lhs, maybe(rhs))

    end function lgt_char_string


    !> Lexically compare two character sequences for being less.
    !> In this version both character sequences are by a string.
    elemental function llt_string_string(lhs, rhs) result(is_llt)
        type(string_type), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_llt

        is_llt = llt(maybe(lhs), maybe(rhs))

    end function llt_string_string

    !> Lexically compare two character sequences for being less.
    !> In this version the left-hand side character sequences is by a string.
    elemental function llt_string_char(lhs, rhs) result(is_llt)
        type(string_type), intent(in) :: lhs
        character(len=*), intent(in) :: rhs
        logical :: is_llt

        is_llt = llt(maybe(lhs), rhs)

    end function llt_string_char

    !> Lexically compare two character sequences for being less.
    !> In this version the right-hand side character sequences is by a string.
    elemental function llt_char_string(lhs, rhs) result(is_llt)
        character(len=*), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_llt

        is_llt = llt(lhs, maybe(rhs))

    end function llt_char_string


    !> Lexically compare two character sequences for being greater or equal.
    !> In this version both character sequences are by a string.
    elemental function lge_string_string(lhs, rhs) result(is_lge)
        type(string_type), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_lge

        is_lge = lge(maybe(lhs), maybe(rhs))

    end function lge_string_string

    !> Lexically compare two character sequences for being greater or equal.
    !> In this version the left-hand side character sequences is by a string.
    elemental function lge_string_char(lhs, rhs) result(is_lge)
        type(string_type), intent(in) :: lhs
        character(len=*), intent(in) :: rhs
        logical :: is_lge

        is_lge = lge(maybe(lhs), rhs)

    end function lge_string_char

    !> Lexically compare two character sequences for being greater or equal
    !> In this version the right-hand side character sequences is by a string.
    elemental function lge_char_string(lhs, rhs) result(is_lge)
        character(len=*), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_lge

        is_lge = lge(lhs, maybe(rhs))

    end function lge_char_string


    !> Lexically compare two character sequences for being less or equal.
    !> In this version both character sequences are by a string.
    elemental function lle_string_string(lhs, rhs) result(is_lle)
        type(string_type), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_lle

        is_lle = lle(maybe(lhs), maybe(rhs))

    end function lle_string_string

    !> Lexically compare two character sequences for being less or equal.
    !> In this version the left-hand side character sequences is by a string.
    elemental function lle_string_char(lhs, rhs) result(is_lle)
        type(string_type), intent(in) :: lhs
        character(len=*), intent(in) :: rhs
        logical :: is_lle

        is_lle = lle(maybe(lhs), rhs)

    end function lle_string_char

    !> Lexically compare two character sequences for being less or equal
    !> In this version the right-hand side character sequences is by a string.
    elemental function lle_char_string(lhs, rhs) result(is_lle)
        character(len=*), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        logical :: is_lle

        is_lle = lle(lhs, maybe(rhs))

    end function lle_char_string


    !> Concatenate two character sequences.
    !> In this version both character sequences are by a string.
    elemental function concat_string_string(lhs, rhs) result(string)
        type(string_type), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        type(string_type) :: string

        string%raw = maybe(lhs) // maybe(rhs)

    end function concat_string_string

    !> Concatenate two character sequences.
    !> In this version the left-hand side character sequences is by a string.
    elemental function concat_string_char(lhs, rhs) result(string)
        type(string_type), intent(in) :: lhs
        character(len=*), intent(in) :: rhs
        type(string_type) :: string

        string%raw = maybe(lhs) // rhs

    end function concat_string_char

    !> Concatenate two character sequences.
    !> In this version the right-hand side character sequences is by a string.
    elemental function concat_char_string(lhs, rhs) result(string)
        character(len=*), intent(in) :: lhs
        type(string_type), intent(in) :: rhs
        type(string_type) :: string

        string%raw = lhs // maybe(rhs)

    end function concat_char_string


    !> Write the character sequence hold by the string to a connected unformatted
    !> unit.
    subroutine write_unformatted(string, unit, iostat, iomsg)
        type(string_type), intent(in) :: string
        integer, intent(in) :: unit
        integer, intent(out) :: iostat
        character(len=*), intent(inout) :: iomsg

        write(unit, iostat=iostat, iomsg=iomsg) int(len(string), long)
        if (iostat == 0) then
            write(unit, iostat=iostat, iomsg=iomsg) maybe(string)
        end if

    end subroutine write_unformatted

    !> Write the character sequence hold by the string to a connected formatted
    !> unit.
    subroutine write_formatted(string, unit, iotype, v_list, iostat, iomsg)
        type(string_type), intent(in) :: string
        integer, intent(in) :: unit
        character(len=*), intent(in) :: iotype
        integer, intent(in) :: v_list(:)
        integer, intent(out) :: iostat
        character(len=*), intent(inout) :: iomsg

        select case(iotype)
        case("LISTDIRECTED")
            write(unit, '(a)', iostat=iostat, iomsg=iomsg) maybe(string)
        case("NAMELIST")
            error stop "[Fatal] This implementation does not support namelist output"
        case default ! DT*
            select case(size(v_list))
            case(0) ! DT
                write(unit, '(a)', iostat=iostat, iomsg=iomsg) maybe(string)
            case default
                error stop "[Fatal] This implementation does not support v_list formatters"
            end select
        end select

    end subroutine write_formatted


    !> Read a character sequence from a connected unformatted unit into the string.
    subroutine read_unformatted(string, unit, iostat, iomsg)
        type(string_type), intent(inout) :: string
        integer, intent(in)    :: unit
        integer, intent(out)   :: iostat
        character(len=*), intent(inout) :: iomsg
        character(len=:), allocatable :: buffer
        integer(long) :: chunk

        read(unit, iostat=iostat, iomsg=iomsg) chunk
        if (iostat == 0) then
            allocate(character(len=chunk) :: buffer)
            read(unit, iostat=iostat, iomsg=iomsg) buffer
            string%raw = buffer
        end if

    end subroutine read_unformatted

    !> Read a character sequence from a connected formatted unit into the string.
    subroutine read_formatted(string, unit, iotype, v_list, iostat, iomsg)
        type(string_type), intent(inout) :: string
        integer, intent(in) :: unit
        character(len=*), intent(in) :: iotype
        integer, intent(in) :: v_list(:)
        integer, intent(out) :: iostat
        character(len=*), intent(inout) :: iomsg
        character(len=:), allocatable :: line

        call unused_dummy_argument(v_list)

        select case(iotype)
        case("LISTDIRECTED")
            call read_line(unit, line, iostat, iomsg)
        case("NAMELIST")
            error stop "[Fatal] This implementation does not support namelist input"
        case default ! DT*
            error stop "[Fatal] This implementation does not support dt formatters"
        end select

        string%raw = line

    contains

        !> Internal routine to read a whole record from a formatted unit
        subroutine read_line(unit, line, iostat, iomsg)
            integer, intent(in) :: unit
            character(len=:), allocatable, intent(out) :: line
            integer, intent(out) :: iostat
            character(len=*), intent(inout) :: iomsg
            integer, parameter :: buffer_size = 512
            character(len=buffer_size) :: buffer
            integer :: chunk
            line = ''
            do
                read(unit, '(a)', iostat=iostat, iomsg=iomsg, size=chunk, advance='no') &
                    buffer
                if (iostat > 0) exit
                line = line // buffer(:chunk)
                if (iostat < 0) exit
            end do

            if (is_iostat_eor(iostat)) then
                iostat = 0
            end if
        end subroutine read_line

    end subroutine read_formatted


    !> Do nothing but mark an unused dummy argument as such to acknowledge compile
    !> time warning like:
    !>
    !>   Warning: Unused dummy argument ‘dummy’ at (1) [-Wunused-dummy-argument]
    !>
    !> We deeply trust in the compiler to inline and optimize this piece of code away.
    elemental subroutine unused_dummy_argument(dummy)
        class(*), intent(in) :: dummy
        associate(dummy => dummy); end associate
    end subroutine unused_dummy_argument


    !> Safely return the character sequences represented by the string
    pure function maybe(string) result(maybe_string)
        type(string_type), intent(in) :: string
        character(len=len(string)) :: maybe_string
        if (allocated(string%raw)) then
            maybe_string = string%raw
        else
            maybe_string = ''
        end if
    end function maybe


end module stdlib_string_type