11! SPDX-Identifier: MIT
2+ #:include "common.fypp"
23
34!> Base64 encoding and decoding algorithms
45!>
1011!> may be subject to change in future releases of `stdlib`.
1112
1213module stdlib_base64
13- use stdlib_base64_encode, only: base64_encode, base64_encode_into
14- use stdlib_base64_decode, only: base64_decode, base64_decode_into
15-
14+ use stdlib_kinds, only: sp, dp, xdp, qp, int8, int16, int32, int64, lk
15+ use, intrinsic :: iso_c_binding, only: c_size_t, c_bool
16+ use stdlib_ascii, only: base64_alphabet
17+ use stdlib_error, only: state_type
18+
1619 implicit none
1720 private
1821
1922 public :: base64_encode, base64_encode_into
2023 public :: base64_decode, base64_decode_into
2124
25+ ! Branchless RFC 4648 decode map: byte -> 6-bit value, invalid -> -1.
26+ ! The main loop OR-reduces values and checks once at the end.
27+ integer(int8), parameter :: DT(0:255) = int( [ &
28+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, &
29+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, &
30+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, &
31+ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, &
32+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, &
33+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, &
34+ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, &
35+ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, &
36+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, &
37+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, &
38+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, &
39+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, &
40+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, &
41+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, &
42+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, &
43+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 &
44+ ], int8)
45+
46+ ! Branchless stream-compaction mask for despace step: keep(1) / drop(0).
47+ integer(int32), parameter :: IS_VAL(0:255) = int([ &
48+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &
49+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &
50+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, &
51+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, &
52+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, &
53+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, &
54+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, &
55+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, &
56+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, &
57+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, &
58+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, &
59+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, &
60+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, &
61+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, &
62+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, &
63+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 &
64+ ], int32)
65+
66+ !> Encodes intrinsic arrays into Base64 text.
67+ !>
68+ !> This is the ergonomic API for encoding. It allocates and returns the
69+ !> output string automatically.
70+ !>
71+ !> Use this form when convenience matters more than avoiding allocations.
72+ !>
73+ !> Example:
74+ !>```fortran
75+ !> character(len=:), allocatable :: encoded
76+ !> integer(int8) :: bytes(3) = [77_int8, 97_int8, 110_int8]
77+ !> encoded = base64_encode(bytes) ! "TWFu"
78+ !>```
79+ interface base64_encode
80+ #:for k1, t1, _, _ in REAL_KINDS_TYPES
81+ module procedure :: base64_encode_real_${k1}$
82+ #:endfor
83+ #:for k1, t1, _, _ in INT_KINDS_TYPES
84+ module procedure :: base64_encode_int_${k1}$
85+ #:endfor
86+ #:for k1, t1, _ in CMPLX_KINDS_TYPES
87+ module procedure :: base64_encode_cmplx_${k1}$
88+ #:endfor
89+ #:for k1, t1 in LOG_KINDS_TYPES
90+ module procedure :: base64_encode_logical_${k1}$
91+ #:endfor
92+ end interface base64_encode
93+
94+ interface
95+ #:for k1, t1, _, _ in REAL_KINDS_TYPES
96+ module function base64_encode_real_${k1}$(data) result(str)
97+ ${t1}$, intent(in), target, contiguous :: data(..)
98+ character(len=:), allocatable :: str
99+ end function base64_encode_real_${k1}$
100+ #:endfor
101+
102+ #:for k1, t1, _, _ in INT_KINDS_TYPES
103+ module function base64_encode_int_${k1}$(data) result(str)
104+ ${t1}$, intent(in), target, contiguous :: data(..)
105+ character(len=:), allocatable :: str
106+ end function base64_encode_int_${k1}$
107+ #:endfor
108+
109+ #:for k1, t1, _ in CMPLX_KINDS_TYPES
110+ module function base64_encode_cmplx_${k1}$(data) result(str)
111+ ${t1}$, intent(in), target, contiguous :: data(..)
112+ character(len=:), allocatable :: str
113+ end function base64_encode_cmplx_${k1}$
114+ #:endfor
115+
116+ #:for k1, t1 in LOG_KINDS_TYPES
117+ module function base64_encode_logical_${k1}$(data) result(str)
118+ ${t1}$, intent(in), target, contiguous :: data(..)
119+ character(len=:), allocatable :: str
120+ end function base64_encode_logical_${k1}$
121+ #:endfor
122+
123+ module function base64_encode_bytes(bytes) result(str)
124+ integer(int8), intent(in), target, contiguous :: bytes(:)
125+ character(len=:), allocatable, target :: str
126+ end function base64_encode_bytes
127+
128+ !> Encodes bytes into a caller-provided output buffer.
129+ !>
130+ !> This is the preallocated API for throughput-sensitive workflows.
131+ !> It does not allocate and reports status through `err_state`.
132+ !>
133+ !> On success, `err_state%ok()` is `.true.` and `encoded_len` is the
134+ !> number of meaningful characters written into `str`.
135+ pure module subroutine base64_encode_into(bytes, str, encoded_len, err_state)
136+ integer(int8), intent(in), target, contiguous :: bytes(:)
137+ character(len=*), intent(out), target :: str
138+ integer, intent(out) :: encoded_len
139+ type(state_type), intent(out) :: err_state
140+ end subroutine base64_encode_into
141+
142+ !> Decodes Base64 text into a caller-provided output buffer.
143+ !>
144+ !> This is the preallocated API for throughput-sensitive workflows.
145+ !> It does not allocate and reports status through `err_state`.
146+ !>
147+ !> The optional `skip_despace` input can be used when the input is
148+ !> already whitespace-free.
149+ pure module subroutine base64_decode_into(str, res, decoded_len, err_state, skip_despace)
150+ character(len=*), intent(in) :: str
151+ character(len=*), intent(out) :: res
152+ integer, intent(out) :: decoded_len
153+ type(state_type), intent(out) :: err_state
154+ logical, intent(in), optional :: skip_despace
155+ end subroutine base64_decode_into
156+
157+ !> Decodes Base64 text and returns an allocated byte-string.
158+ !>
159+ !> This is the ergonomic API for decoding. It allocates and returns
160+ !> the result automatically.
161+ !>
162+ !> On error, an empty result is returned. If `err_state` is present,
163+ !> details are stored there.
164+ !>
165+ !> Example:
166+ !>```fortran
167+ !> character(len=:), allocatable :: decoded
168+ !> decoded = base64_decode("TWFu") ! "Man"
169+ !>```
170+ module function base64_decode(str, err_state) result(res)
171+ character(len=*), intent(in) :: str
172+ type(state_type), intent(out), optional :: err_state
173+ character(len=:), allocatable :: res
174+ end function base64_decode
175+ end interface
176+
22177end module stdlib_base64
0 commit comments