std.encoding

import std.encoding

encoding.base64_encode(data)        # data: bytes, or a string's UTF-8 bytes
encoding.base64_decode(s)           # -> bytes
encoding.base64_decode_text(s)      # -> string; errors on invalid UTF-8

encoding.hex_encode(data)
encoding.hex_decode(s)              # -> bytes
encoding.hex_decode_text(s)

encoding.url_encode(s)              # text only
encoding.url_decode(s)

Decoders return bytes

base64_decode and hex_decode return bytes, not a string. Decoding produces binary data, and the older string-returning behaviour silently corrupted anything that was not valid UTF-8 - which is most of what gets base64-encoded.

The *_decode_text forms are the explicit "this is text" variants: identical on valid UTF-8, and a loud error otherwise.

img = encoding.base64_decode(payload)          # bytes, correct for a PNG
name = encoding.base64_decode_text(payload)    # string, errors if not UTF-8

Choosing between them is choosing what you believe the data is. That is the point: the old behaviour made the choice for you and got it wrong quietly.

Migrating

ecko fix --migrate-bytes src/

Rewrites *_decode to *_decode_text and net.recv to net.recv_text for code written against the older behaviour.

URL encoding is text only

url_encode and url_decode work on strings, because percent-encoding is defined over text. For binary in a URL, base64-encode first, then url-encode - or use the url-safe token from std.random, which needs neither.

Note that url_encode encodes a component, not a whole URL: it escapes / and ?. To build a URL, use the url package, which handles the parts separately.

Not encryption, and not a checksum

Base64 and hex are encodings. They provide no confidentiality and no integrity

  • anyone can decode them. For a digest use std.hash; for

confidentiality use TLS.