Skip to content

Commit

Permalink
added new base64(...) function to base64-encode binary data, along wi…
Browse files Browse the repository at this point in the history
…th a Base64Pipe <: IO type
  • Loading branch information
stevengj committed Aug 8, 2013
1 parent 3342e30 commit 227a312
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 4 deletions.
141 changes: 141 additions & 0 deletions base/base64.jl
@@ -0,0 +1,141 @@
module Base64
import Base: read, write, close
export Base64Pipe, base64

# Base64Pipe is a pipe-like IO object, which converts writes (and
# someday reads?) into base64 encoded (decoded) data send to a stream.
# (You must close the pipe to complete the encode, separate from
# closing the target stream). We also have a function base64(f,
# args...) which works like sprint except that it produces
# base64-encoded data, along with base64(args...) which is equivalent
# to base64(write, args...), to return base64 strings.

#############################################################################

type Base64Pipe <: IO
io::IO
# writing works in groups of 3, so we need to cache last two bytes written
b0::Uint8
b1::Uint8
nb::Uint8 # number of bytes in cache: 0, 1, or 2

function Base64Pipe(io::IO)
b = new(io,0,0,0)
finalizer(b, close)
return b
end
end

#############################################################################

# Based on code by Stefan Karpinski from https://github.com/hackerschool/WebSockets.jl (distributed under the same MIT license as Julia)

const b64chars = ['A':'Z','a':'z','0':'9','+','/']

function b64(x::Uint8, y::Uint8, z::Uint8)
n = int(x)<<16 | int(y)<<8 | int(z)
b64chars[(n >> 18) + 1],
b64chars[(n >> 12) & 0b111111 + 1],
b64chars[(n >> 6) & 0b111111 + 1],
b64chars[(n ) & 0b111111 + 1]
end

function b64(x::Uint8, y::Uint8)
a, b, c = b64(x, y, 0x0)
a, b, c, '='
end

function b64(x::Uint8)
a, b = b64(x, 0x0, 0x0)
a, b, '=', '='
end

#############################################################################

function write(b::Base64Pipe, x::AbstractVector{Uint8})
n = length(x)
s = 1 # starting index
# finish any cached data to write:
if b.nb == 1
if n >= 2
write(b.io, b64(b.b0, x[1], x[2])...)
s = 3
elseif n == 1
b.b1 = x[1]
b.nb = 2
return
else
return
end
elseif b.nb == 2
if n >= 1
write(b.io, b64(b.b0, b.b1, x[1])...)
s = 2
else
return
end
end
# write all groups of three bytes:
while s + 2 <= n
write(b.io, b64(x[s], x[s+1], x[s+2])...)
s += 3
end
# cache any leftover bytes:
if s + 1 == n
b.b0 = x[s]
b.b1 = x[s+1]
b.nb = 2
elseif s == n
b.b0 = x[s]
b.nb = 1
else
b.nb = 0
end
end

function write(b::Base64Pipe, x::Uint8)
if b.nb == 0
b.b0 = x
b.nb = 1
elseif b.nb == 1
b.b1 = x
b.nb = 2
else
write(b.io, b64(b.b0,b.b1,x)...)
b.nb = 0
end
end

function close(b::Base64Pipe)
try
flush(b.io)
catch
end
if b.nb > 0
# write leftover bytes + padding
if b.nb == 1
write(b.io, b64(b.b0)...)
else # b.nb == 2
write(b.io, b64(b.b0, b.b1)...)
end
b.nb = 0
end
end

# like sprint, but returns base64 string
function base64(f::Function, args...)
s = IOBuffer()
b = Base64Pipe(s)
f(b, args...)
close(b)
takebuf_string(s)
end
base64(x...) = base64(write, x...)

#############################################################################

# read(b::Base64Pipe, ::Type{Uint8}) = # TODO: decode base64

#############################################################################

end # module
2 changes: 2 additions & 0 deletions base/exports.jl
Expand Up @@ -726,6 +726,8 @@ export
# strings and text output
ascii,
base,
base64,
Base64Pipe,
beginswith,
bin,
bits,
Expand Down
2 changes: 2 additions & 0 deletions base/sysimg.jl
Expand Up @@ -62,6 +62,8 @@ include("utf8.jl")
include("iobuffer.jl")
include("string.jl")
include("regex.jl")
include("base64.jl")
importall .Base64

# system & environment
include("libc.jl")
Expand Down
12 changes: 12 additions & 0 deletions doc/helpdb.jl
Expand Up @@ -1827,6 +1827,18 @@
"),

("Text I/O","Base","base64","base64(stream, args...)
Given a \"write\"-like function \"writefunc\", which takes an I/O
stream as its first argument, \"base64(writefunc, args...)\"
calls \"writefunc\" to write \"args...\" to a base64-encoded string,
and returns the string. \"base64(args...)\" is equivalent to
\"base64(write, args...)\": it converts its arguments into bytes
using the standard \"write\" functions and returns the base64-encoded
string.
"),

("Memory-mapped I/O","Base","mmap_array","mmap_array(type, dims, stream[, offset])
Create an \"Array\" whose values are linked to a file, using
Expand Down
25 changes: 21 additions & 4 deletions doc/stdlib/base.rst
Expand Up @@ -1010,13 +1010,13 @@ I/O

.. function:: readbytes!(stream, b::Vector{Uint8}, nb=length(b))

Read at most nb bytes from the stream into b, returning the
number of bytes read (increasing the size of b as needed).
Read at most ``nb`` bytes from the stream into ``b``, returning the
number of bytes read (increasing the size of ``b`` as needed).

.. function:: readbytes(stream, nb=typemax(Int))

Read at most nb bytes from the stream, returning a
Vector{Uint8} of the bytes read.
Read at most ``nb`` bytes from the stream, returning a
``Vector{Uint8}`` of the bytes read.

.. function:: position(s)

Expand Down Expand Up @@ -1186,6 +1186,23 @@ Text I/O

Equivalent to ``writedlm`` with ``delim`` set to comma.

.. function:: Base64Pipe(ostream)

Returns a new write-only I/O stream, which converts any bytes written
to it into base64-encoded ASCII bytes written to ``ostream``. Calling
``close`` on the ``Base64Pipe`` stream is necessary to complete the
encoding (but does not close ``ostream``).

.. function:: base64(writefunc, args...), base64(args...)

Given a ``write``-like function ``writefunc``, which takes an I/O
stream as its first argument, ``base64(writefunc, args...)``
calls ``writefunc`` to write ``args...`` to a base64-encoded string,
and returns the string. ``base64(args...)`` is equivalent to
``base64(write, args...)``: it converts its arguments into bytes
using the standard ``write`` functions and returns the base64-encoded
string.

Memory-mapped I/O
-----------------

Expand Down

0 comments on commit 227a312

Please sign in to comment.