FMS  2025.04
Flexible Modeling System
fms_string_utils.F90
1 !***********************************************************************
2 !* Apache License 2.0
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* Licensed under the Apache License, Version 2.0 (the "License");
7 !* you may not use this file except in compliance with the License.
8 !* You may obtain a copy of the License at
9 !*
10 !* http://www.apache.org/licenses/LICENSE-2.0
11 !*
12 !* FMS is distributed in the hope that it will be useful, but WITHOUT
13 !* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied;
14 !* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15 !* PARTICULAR PURPOSE. See the License for the specific language
16 !* governing permissions and limitations under the License.
17 !***********************************************************************
18 
19 !> @defgroup fms_string_utils_mod fms_string_utils_mod
20 !> @ingroup string_utils
21 !> @brief Routines to use for string manipulation
22 
23 !> @file
24 !> @brief File for @ref fms_string_utils_mod
25 
26 !> @addtogroup fms_string_utils_mod
27 !> @{
28 module fms_string_utils_mod
29  use, intrinsic :: iso_c_binding
30  use platform_mod, only: r4_kind, r8_kind, i4_kind, i8_kind
31  use mpp_mod
32 
33  implicit none
34  private
35 
36  public :: fms_array_to_pointer
37  public :: fms_pointer_to_array
38  public :: fms_sort_this
39  public :: fms_find_my_string
40  public :: fms_find_unique
41  public :: fms_c2f_string
42  public :: fms_f2c_string
43  public :: fms_cstring2cpointer
44  public :: string
45  public :: string_copy
46  public :: stringify
47 !> @}
48 
49  interface
50  !> @brief Sorts an array of pointers (my pointer) of size (p_size) in
51  !! alphabetical order.
52  subroutine fms_sort_this(my_pointer, p_size, indices) bind(c)
53  use iso_c_binding
54 
55  type(c_ptr), intent(inout) :: my_pointer(*) !< IN: Array of c pointers to sort
56  !! OUT: Sorted array of c pointers
57  integer(kind=c_int), intent(in) :: p_size !< Size of the array
58  integer(kind=c_int), intent(inout) :: indices(*) !< IN: Array of the indices of my_pointer
59  !! OUT: Sorted array of indices
60  end subroutine fms_sort_this
61 
62  !> @brief Private c function that finds a string in a SORTED array of c pointers
63  !! @return Indices of my_pointer where the string was found as a string!!!
64  function fms_find_my_string_binding(my_pointer, p_size, string_to_find, nfound) bind(c) &
65  result(indices)
66  use iso_c_binding
67 
68  type(c_ptr), intent(in) :: my_pointer(*) !< Array of sorted c pointer
69  integer(kind=c_int), intent(in) :: p_size !< Size of the array
70  character(kind=c_char), intent(in) :: string_to_find(*) !< String to find
71  integer(kind=c_int), intent(inout) :: nfound !< Number of times the array was found
72 
73  type(c_ptr) :: indices
74  end function fms_find_my_string_binding
75 
76  !> @brief c function that finds the number of unique strings in an array of c pointers
77  !! @return number of unique strings
78  function fms_find_unique(my_pointer, p_size) bind(c)&
79  result(ntimes)
80  use iso_c_binding
81 
82  type(c_ptr), intent(in) :: my_pointer(*) !< Array of sorted c pointer
83  integer(kind=c_int), intent(in) :: p_size !< Size of the array
84  integer(kind=c_int) :: ntimes
85 
86  end function fms_find_unique
87 
88  !> @brief converts a kind=c_char to type c_ptr
89  pure function fms_cstring2cpointer (cs) result (cp) bind(c, name="cstring2cpointer")
90  import c_char, c_ptr
91  character(kind=c_char), intent(in) :: cs(*) !< C string input
92  type (c_ptr) :: cp !< C pointer
93  end function fms_cstring2cpointer
94 
95  !> @brief Finds the length of a C-string
96  integer(c_size_t) pure function c_strlen(s) bind(c,name="strlen")
97  import c_size_t, c_ptr
98  type(c_ptr), intent(in), value :: s !< A C-string whose size is desired
99  end function
100 
101  !> @brief Frees a C pointer
102  subroutine c_free(ptr) bind(c,name="free")
103  import c_ptr
104  type(c_ptr), value :: ptr !< A C-pointer to free
105  end subroutine
106 
107 end interface
108 
109 !> Converts a C string to a Fortran string
110 !> @ingroup fms_mod
111 interface fms_c2f_string
112  module procedure cstring_fortran_conversion
113  module procedure cpointer_fortran_conversion
114 end interface
115 
116 !> Converts an array of real numbers to a string
117 !> @ingroup fms_mod
118 interface stringify
119  module procedure stringify_1d_r4, stringify_1d_r8
120  module procedure stringify_2d_r4, stringify_2d_r8
121  module procedure stringify_3d_r4, stringify_3d_r8
122 end interface
123 
124 !> @addtogroup fms_string_utils_mod
125 !> @{
126 contains
127 
128  !> @brief Converts a character array to an array of c pointers!
129  !! @return An array of c pointers
130  function fms_array_to_pointer(my_array) &
131  result(my_pointer)
132  character(len=*), target :: my_array(:) !!< Array of strings to convert
133  type(c_ptr), allocatable :: my_pointer(:)
134 
135  integer :: i !< For do loops
136 
137  if (allocated(my_pointer)) call mpp_error(fatal, "The c pointer array is &
138  already allocated. Deallocated before calling fms_array_to_pointer")
139  allocate(my_pointer(size(my_array)))
140 
141  do i = 1, size(my_array)
142  my_pointer(i) = c_loc(my_array(i))
143  enddo
144  end function fms_array_to_pointer
145 
146  !> @brief Convert an array of c pointers back to a character array
147  !! @return A character array
148  function fms_pointer_to_array(my_pointer, narray) &
149  result(my_array)
150  type(c_ptr), intent(in) :: my_pointer(*) !< Array of c pointer
151  integer, intent(in) :: narray !< Length of the array
152  character(len=:), allocatable :: my_array(:)
153 
154  character(len=:), allocatable :: buffer !< Buffer to store a string
155  integer :: i !< For do loops
156 
157  allocate(character(len=255) :: my_array(narray))
158  do i = 1, narray
159  buffer = fms_c2f_string(my_pointer(i))
160  my_array(i) = buffer
161  deallocate(buffer)
162  enddo
163  end function fms_pointer_to_array
164 
165  !> @brief Searches through a SORTED array of pointers for a string
166  !! @return the indices where the array was found
167  !! If the string was not found, indices will be indices(1) = -999
168  !> <br>Example usage:
169  !! my_pointer = fms_array_to_pointer(my_array)
170  !! call fms_sort_this(my_pointer, n_array, indices)
171  !! ifind = fms_find_my_string(my_pointer, n_array, string_to_find)
172  function fms_find_my_string(my_pointer, narray, string_to_find) &
173  result(ifind)
174  type(c_ptr), intent(in) :: my_pointer(*) !< Array of c pointer
175  integer, intent(in) :: narray !< Length of the array
176  character(len=*), intent(in) :: string_to_find !< string to find
177  integer, allocatable :: ifind(:)
178 
179  integer :: nfind !< number of times the string was found
180  character(len=:), allocatable :: buffer !< buffer to read the indices into
181 
182  buffer = fms_c2f_string(&
183  fms_find_my_string_binding(my_pointer, narray, trim(string_to_find)//c_null_char, nfind))
184 
185  if (allocated(ifind)) call mpp_error(fatal, "The indices array is already allocated. &
186  Deallocate it before calling fms_find_my_string")
187 
188  if (nfind .gt. 0) then
189  allocate(ifind(nfind))
190  read(buffer,*) ifind
191  else
192  allocate(ifind(1))
193  ifind = -999
194  endif
195 
196  end function fms_find_my_string
197 
198  !> \brief Converts a C-string to a pointer and then to a Fortran string
199  function cstring_fortran_conversion (cstring) result(fstring)
200  character (kind=c_char), intent(in) :: cstring (*) !< Input C-string
201  character(len=:), allocatable :: fstring !< The fortran string returned
203  end function cstring_fortran_conversion
204 
205  !> \brief Converts a C-string returned from a TYPE(C_PTR) function to
206  !! a fortran string with type character.
207  function cpointer_fortran_conversion (cstring) result(fstring)
208  type (c_ptr), intent(in) :: cstring !< Input C-pointer
209  character(len=:), allocatable :: fstring !< The fortran string returned
210  character(len=:,kind=c_char), pointer :: string_buffer !< A temporary pointer to between C and Fortran
211  integer(c_size_t) :: length !< The string length
212 
213  length = c_strlen(cstring)
214  allocate (character(len=length, kind=c_char) :: string_buffer)
215  block
216  character(len=length,kind=c_char), pointer :: s
217  call c_f_pointer(cstring,s) ! Recovers a view of the C string
218  string_buffer = s ! Copies the string contents
219  end block
220 
221  allocate(character(len=length) :: fstring) !> Set the length of fstring
222  fstring = string_buffer
223  deallocate(string_buffer)
224  end function cpointer_fortran_conversion
225 
226 !> @brief Copies a Fortran string into a C string and puts c_null_char in any trailing spaces
227  subroutine fms_f2c_string (dest, str_in)
228  character (c_char), intent (out) :: dest (:) !< C String to be copied into
229  character (len=*), intent (in) :: str_in !< Fortran string to copy to C string
230  integer :: i !< for looping
231 !> Drop an error if the C string is not large enough to hold the input and the c_null_char at the end.
232  if (len(trim(str_in)) .ge. size(dest)) call mpp_error(fatal, &
233  "The string "//trim(str_in)//" is larger than the destination C string")
234 !> Copy c_null_char into each spot in dest
235  dest = c_null_char
236 !> Loop though and put each character of the Fortran string into the C string array
237  do i = 1, len(trim(str_in))
238  dest(i) = str_in(i:i)
239  enddo
240 end subroutine fms_f2c_string
241 
242  !> @brief Converts a number or a Boolean value to a string
243  !> @return The argument as a string
244  function string(v, fmt)
245  class(*), intent(in) :: v !< Value to be converted to a string
246  character(*), intent(in), optional :: fmt !< Optional format string for a real or integral argument
247  character(:), allocatable :: string
248 
249  select type(v)
250  type is (logical)
251  if (present(fmt)) then
252  call mpp_error(warning, "string(): Ignoring `fmt` argument for type `logical`")
253  endif
254  if (v) then
255  string = "True"
256  else
257  string = "False"
258  endif
259 
260  type is (integer(i4_kind))
261  allocate(character(32) :: string)
262  if (present(fmt)) then
263  write(string, "(" // fmt // ")") v
264  else
265  write(string, '(i0)') v
266  endif
267  string = trim(adjustl(string))
268 
269  type is (integer(i8_kind))
270  allocate(character(32) :: string)
271  if (present(fmt)) then
272  write(string, "(" // fmt // ")") v
273  else
274  write(string, '(i0)') v
275  endif
276  string = trim(adjustl(string))
277 
278  type is (real(r4_kind))
279  allocate(character(32) :: string)
280  if (present(fmt)) then
281  write(string, "(" // fmt // ")") v
282  else
283  write(string, *) v
284  endif
285  string = trim(adjustl(string))
286 
287  type is (real(r8_kind))
288  allocate(character(32) :: string)
289  if (present(fmt)) then
290  write(string, "(" // fmt // ")") v
291  else
292  write(string, *) v
293  endif
294  string = trim(adjustl(string))
295 
296  class default
297  call mpp_error(fatal, "string(): Called with incompatible argument type. Possible types &
298  &include integer(4), integer(8), real(4), real(8), or logical.")
299  end select
300  end function string
301 
302  !> @brief Safely copy a string from one buffer to another.
303  subroutine string_copy(dest, source, check_for_null)
304  character(len=*), intent(inout) :: dest !< Destination string.
305  character(len=*), intent(in) :: source !< Source string.
306  logical, intent(in), optional :: check_for_null !<Flag indicating to test for null character
307 
308  integer :: i
309  logical :: check_null
310 
311  check_null = .false.
312  if (present(check_for_null)) check_null = check_for_null
313 
314  i = 0
315  if (check_null) then
316  i = index(source, char(0)) - 1
317  endif
318 
319  if (i < 1 ) i = len_trim(source)
320 
321  if (len_trim(source(1:i)) .gt. len(dest)) then
322  call mpp_error(fatal, "The input destination string is not big enough to" &
323  //" to hold the input source string.")
324  endif
325  dest = ""
326  dest = adjustl(trim(source(1:i)))
327  end subroutine string_copy
328 
329 #include "fms_string_utils_r4.fh"
330 #include "fms_string_utils_r8.fh"
331 
332 end module fms_string_utils_mod
333 !> @}
334 ! close documentation grouping
Converts a C string to a Fortran string.
Converts an array of real numbers to a string.
character(len=:) function, allocatable cstring_fortran_conversion(cstring)
Converts a C-string to a pointer and then to a Fortran string.
character(len=:) function, dimension(:), allocatable, public fms_pointer_to_array(my_pointer, narray)
Convert an array of c pointers back to a character array.
subroutine, public string_copy(dest, source, check_for_null)
Safely copy a string from one buffer to another.
character(:) function, allocatable, public string(v, fmt)
Converts a number or a Boolean value to a string.
type(c_ptr) function, dimension(:), allocatable, public fms_array_to_pointer(my_array)
Converts a character array to an array of c pointers!
integer function, dimension(:), allocatable, public fms_find_my_string(my_pointer, narray, string_to_find)
Searches through a SORTED array of pointers for a string.
character(len=:) function, allocatable cpointer_fortran_conversion(cstring)
Converts a C-string returned from a TYPE(C_PTR) function to a fortran string with type character.
subroutine, public fms_f2c_string(dest, str_in)
Copies a Fortran string into a C string and puts c_null_char in any trailing spaces.
Finds the length of a C-string.
converts a kind=c_char to type c_ptr
Private c function that finds a string in a SORTED array of c pointers.
c function that finds the number of unique strings in an array of c pointers
Sorts an array of pointers (my pointer) of size (p_size) in alphabetical order.