[comp.lang.fortran] Overlapping strings

hirchert@ncsa.uiuc.edu (Kurt Hirchert) (02/20/91)

In article <1991Feb16.202213.10167@ariel.unm.edu> scavo@cie.uoregon.edu (Tom Scavo) writes:
>So how do I fix these nonstandard routines without having
>to resort to local character variables (which would put an
>unnecessary limit on the length of each argument)?

(long FORTRAN 77 example appeared in original article)

Typically, you do it by replacing assignment statements that attempt to
do the move as one step with loops that do the move in smaller steps.
It is permissible for the same string to appear on both sides of an
assignment statement as long as the ranges do not overlap, so you can
move in chunks no longer than the offset between the starting and ending
positions.  If the offset is small, this can reduce to a series of very
small moves that may be inefficient on some machines, so you may prefer
to copy larger chunks to a local buffer and then back to the target position.
As long as you do this in a loop, the size of the local buffer in no way
limits the size of strings you can handle.  The subroutine mvleft below
illustrates the use of both approaches.  In your example, you could replace
      st(j+1:j+m) = st(i+1:n)     and
      st = st(n-m+1:n)
with
      call mvleft(st,i+1,m,i-j)     and
      call mvleft(st,n-m+1,m,n-m)
in order to get the same functionality implemented in conformance with the
FORTRAN 77 standard.

[P.S. The restriction against overlapping strings in an assignment statement
was eliminated in Fortran 90.  This change may cause the code generated for a
subroutine like mvleft to be less efficient (because the compiler may be
unable to recognize that all assignments from st to st in mvleft are made
under circumstances where there is no overlap), but you also wouldn't need
mvleft, because your original assignment statement would be standard
conforming.]



      subroutine mvleft(st,istart,len,left)

*     move the len character substring of st starting at position
*     istart to a position left positions to the left.  (if left
*     is negative, move the string to the right.)

*     the following parameter controls the size of the local copy buffer

      parameter (lenb = 16)

*     the following parameter specifies the length of strings where it
*     becomes faster to copy strings to and from the local copy buffer
*     than to do multiple direct copies of this length within st.  this
*     value should always be less than or equal to lenb/2, since two
*     copies are involved in using the local copy buffer.

      parameter (lcopy = 4)


      character*(*) st
      character*(lenb) buf


*     check for no movement case

      if (left.eq.0) return

*     check if there is no overlap - if, so use straight assignment

      if (abs(left).le.len) then
         st(istart-left:istart+len-1-left) = st(istart:istart+len-1)
         return
      endif

*     check if nonoverlapped portion is too short to be efficient

      if (abs(left) .le. lcopy) go to 100

*     do copy in chunks no larger than the nonoverlapped portion

      i = mod(len,abs(left))
      if (left .gt. 0) then
         if (i .ne. 0) 
     &      st(istart-left:istart+i-1-left) = 
     &      st(istart:istart+i-1)
         jstart = istart+i
         jend = istart+len-1
      else
         if (i .ne. 0)
     &      st(istart+len-i-left:istart+len-1-left) = 
     &      st(istart+len-i:istart+len-1)
         jstart = istart+len-i+left
         jend = istart
      endif
      do 10 i=jstart,jend,left
10    st(i-left:i+iabs(left)-1-left) = st(i:i+iabs(left)-1)
      return

*     do copy in chunks the size of the local copy buffer

100   i = mod(len,lenb)
      if (left .gt. 0) then
         if (i .ne. 0) then
            buf(1:i) = st(istart:istart+i-1)
            st(istart-left:istart+i-1-left) = buf(1:i)
         endif
         jstart = istart+i
         jend = istart+len-1
         jinc = lenb
      else
         if (i .ne. 0) then
            buf = st(istart+len-i:istart+len-1)
            st(istart+len-i-left:istart+len-1-left) = buf
         endif
         jstart = istart+len-i-lenb
         jend = istart
         jinc = -lenb
      endif
      do 20 i=jstart,jend,jinc
      buf = st(i:i+lenb-1)
20    st(i-left:i+lenb-1-left) = buf

      end
-- 
Kurt W. Hirchert     hirchert@ncsa.uiuc.edu
National Center for Supercomputing Applications