base64_decode_bytes Subroutine

private subroutine base64_decode_bytes(input, bytes, nbytes)

Core base64 decoder

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: input
integer(kind=int8), intent(out), allocatable :: bytes(:)
integer, intent(out) :: nbytes

Source Code

  subroutine base64_decode_bytes(input, bytes, nbytes)
    !! Core base64 decoder
    use iso_fortran_env, only: int8
    implicit none
    character(*), intent(in) :: input
    integer(int8), allocatable, intent(out) :: bytes(:)
    integer, intent(out) :: nbytes

    character(64), parameter :: b64 = &
         'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    integer :: i, j, in_len, ngroups, pad
    integer :: v0, v1, v2, v3

    in_len = len_trim(input)
    if(in_len .eq. 0)then
       allocate(bytes(0))
       nbytes = 0
       return
    end if

    ! Count padding
    pad = 0
    if(input(in_len:in_len) .eq. '=') pad = pad + 1
    if(in_len .ge. 2 .and. input(in_len-1:in_len-1) .eq. '=') pad = pad + 1

    ngroups = in_len / 4
    nbytes = ngroups * 3 - pad
    allocate(bytes(nbytes))

    j = 1
    do i = 1, in_len, 4
       v0 = index(b64, input(i:i)) - 1
       v1 = index(b64, input(i+1:i+1)) - 1
       if(input(i+2:i+2) .ne. '=')then
          v2 = index(b64, input(i+2:i+2)) - 1
       else
          v2 = 0
       end if
       if(input(i+3:i+3) .ne. '=')then
          v3 = index(b64, input(i+3:i+3)) - 1
       else
          v3 = 0
       end if

       if(j .le. nbytes) &
            bytes(j) = int(ior(ishft(v0, 2), ishft(v1, -4)), int8)
       if(j + 1 .le. nbytes) &
            bytes(j+1) = int(ior(ishft(iand(v1, 15), 4), ishft(v2, -2)), int8)
       if(j + 2 .le. nbytes) &
            bytes(j+2) = int(ior(ishft(iand(v2, 3), 6), v3), int8)
       j = j + 3
    end do

  end subroutine base64_decode_bytes