• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37446

pending completion
#37446

push

local

web-flow
do lazy reallocation after take!(iobuffer) (#48676)

closes #27741. closes #48651

30 of 30 new or added lines in 3 files covered. (100.0%)

69389 of 80609 relevant lines covered (86.08%)

34717409.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.09
/stdlib/Base64/src/encode.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
# Generate encode table.
4
const BASE64_ENCODE = [UInt8(x) for x in append!(['A':'Z'; 'a':'z'; '0':'9'], ['+', '/'])]
5
encode(x::UInt8) = @inbounds return BASE64_ENCODE[(x & 0x3f) + 1]
199,698✔
6
encodepadding()  = UInt8('=')
×
7

8
"""
9
    Base64EncodePipe(ostream)
10

11
Return a new write-only I/O stream, which converts any bytes written to it into
12
base64-encoded ASCII bytes written to `ostream`.  Calling [`close`](@ref) on the
13
`Base64EncodePipe` stream is necessary to complete the encoding (but does not
14
close `ostream`).
15

16
# Examples
17
```jldoctest
18
julia> io = IOBuffer();
19

20
julia> iob64_encode = Base64EncodePipe(io);
21

22
julia> write(iob64_encode, "Hello!")
23
6
24

25
julia> close(iob64_encode);
26

27
julia> str = String(take!(io))
28
"SGVsbG8h"
29

30
julia> String(base64decode(str))
31
"Hello!"
32
```
33
"""
34
struct Base64EncodePipe <: IO
35
    io::IO
36
    buffer::Buffer
37

38
    function Base64EncodePipe(io::IO)
1,007✔
39
        # The buffer size must be at least 3.
40
        buffer = Buffer(512)
1,007✔
41
        pipe = new(io, buffer)
1,007✔
42
        finalizer(_ -> close(pipe), buffer)
2,014✔
43
        return pipe
1,007✔
44
    end
45
end
46

47
Base.isreadable(::Base64EncodePipe) = false
2✔
48
Base.iswritable(pipe::Base64EncodePipe) = iswritable(pipe.io)
2✔
49

50
function Base.unsafe_write(pipe::Base64EncodePipe, ptr::Ptr{UInt8}, n::UInt)::Int
1,095✔
51
    buffer = pipe.buffer
1,095✔
52
    m = buffer.size
1,095✔
53
    b1, b2, b3, k = loadtriplet!(buffer, ptr, n)
1,095✔
54
    @assert k ≥ m
1,095✔
55
    p = ptr + k - m
1,095✔
56
    if k < 3
1,095✔
57
        if k == 1
5✔
58
            buffer[1] = b1
×
59
            buffer.size = 1
×
60
        elseif k == 2
5✔
61
            buffer[1] = b1
4✔
62
            buffer[2] = b2
4✔
63
            buffer.size = 2
4✔
64
        end
65
        return p - ptr
5✔
66
    end
67
    @assert buffer.size == 0
1,090✔
68

69
    i = 0
1,090✔
70
    p_end = ptr + n
1,090✔
71
    while true
49,518✔
72
        buffer[i+1] = encode(b1 >> 2          )
49,518✔
73
        buffer[i+2] = encode(b1 << 4 | b2 >> 4)
49,518✔
74
        buffer[i+3] = encode(b2 << 2 | b3 >> 6)
49,518✔
75
        buffer[i+4] = encode(          b3     )
49,518✔
76
        i += 4
49,518✔
77
        if p + 2 < p_end
49,518✔
78
            b1 = unsafe_load(p, 1)
48,428✔
79
            b2 = unsafe_load(p, 2)
48,428✔
80
            b3 = unsafe_load(p, 3)
48,428✔
81
            p += 3
48,428✔
82
        else
83
            break
1,090✔
84
        end
85
        if i + 4 > capacity(buffer)
48,428✔
86
            unsafe_write(pipe.io, pointer(buffer), i)
×
87
            i = 0
×
88
        end
89
    end
48,428✔
90
    if i > 0
1,090✔
91
        unsafe_write(pipe.io, pointer(buffer), i)
1,090✔
92
    end
93

94
    while p < p_end
2,054✔
95
        buffer[buffer.size+=1] = unsafe_load(p)
965✔
96
        p += 1
965✔
97
    end
965✔
98
    return p - ptr
1,089✔
99
end
100

101
function Base.write(pipe::Base64EncodePipe, x::UInt8)
269✔
102
    buffer = pipe.buffer
269✔
103
    buffer[buffer.size+=1] = x
269✔
104
    if buffer.size == 3
269✔
105
        unsafe_write(pipe, C_NULL, 0)
89✔
106
    end
107
    return 1
269✔
108
end
109

110
function Base.close(pipe::Base64EncodePipe)
2,014✔
111
    b1, b2, b3, k = loadtriplet!(pipe.buffer, Ptr{UInt8}(C_NULL), UInt(0))
2,014✔
112
    if k == 0
2,014✔
113
        # no leftover and padding
114
    elseif k == 1
651✔
115
        write(pipe.io,
327✔
116
              encode(b1 >> 2),
117
              encode(b1 << 4),
118
              encodepadding(),
119
              encodepadding())
120
    elseif k == 2
324✔
121
        write(pipe.io,
324✔
122
              encode(          b1 >> 2),
123
              encode(b1 << 4 | b2 >> 4),
124
              encode(b2 << 2          ),
125
              encodepadding())
126
    else
127
        @assert k == 3
×
128
        write(pipe.io,
×
129
              encode(b1 >> 2          ),
130
              encode(b1 << 4 | b2 >> 4),
131
              encode(b2 << 2 | b3 >> 6),
132
              encode(          b3     ))
133
    end
134
    return nothing
2,014✔
135
end
136

137
# Load three bytes from buffer and ptr.
138
function loadtriplet!(buffer::Buffer, ptr::Ptr{UInt8}, n::UInt)
3,109✔
139
    b1 = b2 = b3 = 0x00
3,109✔
140
    if buffer.size == 0
3,109✔
141
        if n == 0
2,369✔
142
            k = 0
1,364✔
143
        elseif n == 1
1,005✔
144
            b1 = unsafe_load(ptr, 1)
×
145
            k = 1
×
146
        elseif n == 2
1,005✔
147
            b1 = unsafe_load(ptr, 1)
4✔
148
            b2 = unsafe_load(ptr, 2)
4✔
149
            k = 2
4✔
150
        else
151
            b1 = unsafe_load(ptr, 1)
1,001✔
152
            b2 = unsafe_load(ptr, 2)
1,001✔
153
            b3 = unsafe_load(ptr, 3)
1,001✔
154
            k = 3
3,370✔
155
        end
156
    elseif buffer.size == 1
740✔
157
        b1 = buffer[1]
327✔
158
        if n == 0
327✔
159
            k = 1
327✔
160
        elseif n == 1
×
161
            b2 = unsafe_load(ptr, 1)
×
162
            k = 2
×
163
        else
164
            b2 = unsafe_load(ptr, 1)
×
165
            b3 = unsafe_load(ptr, 2)
×
166
            k = 3
327✔
167
        end
168
    elseif buffer.size == 2
413✔
169
        b1 = buffer[1]
324✔
170
        b2 = buffer[2]
324✔
171
        if n == 0
324✔
172
            k = 2
324✔
173
        else
174
            b3 = unsafe_load(ptr, 1)
×
175
            k = 3
324✔
176
        end
177
    else
178
        @assert buffer.size == 3
89✔
179
        b1 = buffer[1]
89✔
180
        b2 = buffer[2]
89✔
181
        b3 = buffer[3]
89✔
182
        k = 3
89✔
183
    end
184
    empty!(buffer)
3,109✔
185
    return b1, b2, b3, k
3,109✔
186
end
187

188
"""
189
    base64encode(writefunc, args...; context=nothing)
190
    base64encode(args...; context=nothing)
191

192
Given a [`write`](@ref)-like function `writefunc`, which takes an I/O stream as
193
its first argument, `base64encode(writefunc, args...)` calls `writefunc` to
194
write `args...` to a base64-encoded string, and returns the string.
195
`base64encode(args...)` is equivalent to `base64encode(write, args...)`: it
196
converts its arguments into bytes using the standard [`write`](@ref) functions
197
and returns the base64-encoded string.
198

199
The optional keyword argument `context` can be set to `:key=>value` pair
200
or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O
201
stream passed to `writefunc` or `write`.
202

203
See also [`base64decode`](@ref).
204
"""
205
function base64encode(f::Function, args...; context=nothing)
2,008✔
206
    s = IOBuffer()
1,004✔
207
    b = Base64EncodePipe(s)
1,004✔
208
    if context === nothing
1,004✔
209
        f(b, args...)
1,004✔
210
    else
211
        f(IOContext(b, context), args...)
×
212
    end
213
    close(b)
1,004✔
214
    return String(take!(s))
1,004✔
215
end
216
base64encode(args...; context=nothing) = base64encode(write, args...; context=context)
2,002✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc