base64

package module
v0.0.0-...-720dc2a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 3 Imported by: 0

README

go-ruby-base64/base64

base64 — go-ruby-base64

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of Ruby's standard-library Base64 module — the deterministic, interpreter-independent core of MRI 4.0.5's require "base64". It reproduces, byte-for-byte, what MRI computes for every method: the RFC-2045 60-column line wrapping of encode64, the lenient stream semantics of decode64, the strict variants' ArgumentError on malformed input, and the url-safe alphabet with optional padding — without any Ruby runtime.

The hot standard-alphabet encode/decode paths are built on the SIMD kernels of go-simd/base64 (go-asmgen: amd64 SSE/AVX2, arm64 NEON, ppc64le VSX, s390x vector; stdlib fallback elsewhere), so the output stays bit-identical to encoding/base64.StdEncoding while going faster on the supported arches. Only the MRI-specific framing — the 60-column wrap, the lenient unpack("m") quad/padding state machine, and the url-safe padding rules — is hand-written here.

It is the Base64 backend for go-embedded-ruby, but is a standalone, reusable module with no dependency on the Ruby runtime — a sibling of go-ruby-yaml (Psych), go-ruby-regexp (the Onigmo engine) and go-ruby-erb (the ERB compiler).

What it is — and isn't. Base64 encoding/decoding is fully deterministic and needs no interpreter, so it lives here as pure Go. This package operates on Go strings (Ruby strings are byte sequences); binding the methods to a live Ruby Base64 module — argument coercion, raising ArgumentError — is the host's job, and this library returns a Go error (ErrInvalid) the host maps to MRI's exception.

Features

Faithful port of MRI's Base64, validated against the ruby binary on every platform that has one:

  • Encode64 — RFC 2045 (Array#pack("m")): the standard +/ alphabet, a newline every 60 output characters, and a trailing newline. The empty string encodes to the empty string (no newline), as in MRI.
  • Decode64 — lenient (String#unpack1("m")): non-alphabet bytes are skipped, padding is optional, a = on a 2- or 3-char partial quad finalises it and stops decoding (trailing-padding terminates the stream) while a = on a quad boundary is ignored, and a lone orphaned final sextet is discarded.
  • StrictEncode64 / StrictDecode64pack("m0") / unpack1("m0"): no newlines on encode; decode rejects any stray byte (including the embedded newlines encoding/base64 would tolerate) with ErrInvalid.
  • UrlsafeEncode64 / UrlsafeDecode64 — the -_ alphabet (RFC 4648); padding is on by default and stripped when padding=false; decode accepts correctly-padded or unpadded input and rejects everything else.

CGO-free, 100% test coverage, gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x) on Linux, macOS, and Windows.

Install

go get github.com/go-ruby-base64/base64

Usage

package main

import (
	"fmt"

	"github.com/go-ruby-base64/base64"
)

func main() {
	fmt.Printf("%q\n", base64.Encode64("hello world"))
	// "aGVsbG8gd29ybGQ=\n"            — RFC-2045, trailing newline

	fmt.Printf("%q\n", base64.StrictEncode64("hello"))
	// "aGVsbG8="                      — no newline

	fmt.Println(base64.Decode64("aGVs bG8=\n junk")) // hello — lenient

	s, err := base64.StrictDecode64("not base64!!!")
	fmt.Printf("%q %v\n", s, err)      // "" invalid base64

	fmt.Println(base64.UrlsafeEncode64("\xfb\xff\xfe"))        // -__-
	fmt.Println(base64.UrlsafeEncode64("ab", false))           // YWI  (unpadded)
}

API

// Encode64 — RFC 2045 (pack("m")): standard alphabet, \n every 60 chars + trailing \n.
func Encode64(s string) string

// StrictEncode64 — pack("m0"): standard alphabet, no newlines.
func StrictEncode64(s string) string

// Decode64 — lenient unpack1("m"): skips stray bytes, optional padding, padding stops.
func Decode64(s string) string

// StrictDecode64 — unpack1("m0"): returns ErrInvalid on any malformed input.
func StrictDecode64(s string) (string, error)

// UrlsafeEncode64 — -_ alphabet; padding defaults to true (pass false to strip it).
func UrlsafeEncode64(s string, padding ...bool) string

// UrlsafeDecode64 — -_ alphabet, RFC 4648; accepts padded or unpadded input.
func UrlsafeDecode64(s string) (string, error)

// ErrInvalid is returned by the strict decoders, mirroring MRI's ArgumentError.
var ErrInvalid = errors.New("invalid base64")

SIMD acceleration

StrictEncode64, Encode64, StrictDecode64 and UrlsafeDecode64 route the standard-alphabet body through go-simd/base64, whose go-asmgen kernels cover the SIMD-capable 64-bit arches and fall back to encoding/base64 everywhere else, so the bytes are always identical to StdEncoding. On amd64/arm64 the encode kernel measures markedly faster than the scalar encoding/base64 reference; the package ships go test -bench benchmarks (Benchmark*SIMD vs Benchmark*Scalar) so the speedup can be measured on each target.

Tests & coverage

The suite pairs deterministic, ruby-free golden tests — captured from MRI 4.0.5, which alone hold coverage at 100% so the Windows and qemu cross-arch lanes pass the gate — with a differential MRI oracle that drives a wide corpus (every length class, a line wrap, non-ASCII bytes, and malformed/strict inputs) through the system ruby and asserts byte-equality. The oracle scripts binmode both stdin and stdout so Windows text-mode never pollutes the bytes, gate on RUBY_VERSION >= "4.0", and skip themselves where ruby is absent.

COVERPKG=$(go list ./... | paste -sd, -)
go test -race -coverpkg="$COVERPKG" -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1   # 100.0%

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-base64/base64 authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package base64 is a pure-Go (no cgo), MRI-4.0.5-faithful reimplementation of Ruby's standard-library Base64 module — the deterministic, interpreter- independent core of `require "base64"`.

It reproduces, byte-for-byte, what MRI's Base64 methods compute:

  • Encode64 — RFC 2045 (pack("m")): standard +/ alphabet, a newline every 60 output characters and a trailing newline.
  • Decode64 — lenient (unpack1("m")): every non-alphabet byte is dropped, padding is optional, an orphaned final sextet is discarded.
  • StrictEncode64 — pack("m0"): standard alphabet, no newlines.
  • StrictDecode64 — unpack1("m0"): strict, returns ErrInvalid on any byte that is not part of a well-formed padded base64 string.
  • UrlsafeEncode64 — the -_ alphabet; padding is on by default and stripped when padding=false.
  • UrlsafeDecode64 — the -_ alphabet, RFC-4648; accepts correctly-padded or unpadded input, rejects everything else.

The hot standard-alphabet encode/decode paths run on the SIMD kernels of github.com/go-simd/base64 (go-asmgen: amd64 SSE/AVX2, arm64 NEON, ppc64le VSX, s390x vector; stdlib fallback elsewhere), so the output stays bit-identical to encoding/base64.StdEncoding while going faster on the supported arches. Only the MRI-specific framing (60-column wrapping, lenient filtering, the urlsafe padding rules) is hand-written here.

It is the Base64 backend for github.com/go-embedded-ruby/ruby, but is a standalone, reusable module with no dependency on the Ruby runtime — a sibling of go-ruby-yaml/yaml, go-ruby-regexp/regexp and go-ruby-erb/erb.

Index

Constants

This section is empty.

Variables

View Source
var ErrInvalid = errors.New("invalid base64")

ErrInvalid is returned by StrictDecode64 and UrlsafeDecode64 for input that is not a well-formed base64 string. It mirrors the ArgumentError("invalid base64") MRI raises from the strict decoders.

Functions

func Decode64

func Decode64(s string) string

Decode64 decodes a base64 string leniently, matching MRI's Base64.decode64 (String#unpack1("m")). It reproduces the C unpack("m") semantics exactly: bytes outside the standard alphabet are dropped; the surviving characters are gathered into 4-char quads; a '=' that lands on a 2- or 3-char partial quad finalises that quad and stops decoding (trailing padding terminates the stream), while a '=' on a quad boundary is ignored; and a lone trailing sextet at end-of-input is discarded.

Rather than fold sextets one byte at a time (the shape of MRI's C loop, but slow in Go), it de-spaces the input with github.com/go-simd/base64's vectorised Compact kernel — a branch-free SWAR pass that copies the alphabet bytes, drops whitespace/newlines/stray bytes, and applies the very same '='-on-a-partial-quad stop rule MRI's C loop uses — then hands the packed alphabet run to that package's batched SIMD decoder. Both passes run over a single mutable copy of the input: Compact de-spaces in place and Decode decodes in place (its documented in-place contract — the write cursor always trails the read cursor), so the whole decode costs one buffer for the working copy plus the result string, matching the allocation profile of MRI's C unpack("m") instead of the extra scratch+output buffers a naive Go port needs. Only the 2/3-char tail is finished by hand. The result is byte-identical to the previous decoder, taking the lenient path from behind MRI to ahead of it (including YJIT).

func Encode64

func Encode64(s string) string

Encode64 returns the RFC-2045 base64 encoding of s, matching MRI's Base64.encode64 (Array#pack("m")): the standard +/ alphabet with a newline inserted every 60 output characters and a trailing newline. The empty string encodes to the empty string (no trailing newline), exactly as MRI.

func StrictDecode64

func StrictDecode64(s string) (string, error)

StrictDecode64 decodes a strictly-padded standard base64 string, matching MRI's Base64.strict_decode64 (String#unpack1("m0")). It returns ErrInvalid for any input that is not a well-formed padded base64 string (stray characters, whitespace, wrong padding).

func StrictEncode64

func StrictEncode64(s string) string

StrictEncode64 returns the base64 encoding of s with no newlines, matching MRI's Base64.strict_encode64 (Array#pack("m0")).

func UrlsafeDecode64

func UrlsafeDecode64(s string) (string, error)

UrlsafeDecode64 decodes a url-safe (-_) RFC-4648 base64 string, matching MRI's Base64.urlsafe_decode64: correctly-padded or unpadded input is accepted, and anything else (stray characters, wrong padding) returns ErrInvalid.

func UrlsafeEncode64

func UrlsafeEncode64(s string, padding ...bool) string

UrlsafeEncode64 returns the url-safe (-_) base64 encoding of s, matching MRI's Base64.urlsafe_encode64. Padding is included by default; when padding is false the trailing = characters are stripped. The variadic padding argument lets the Go caller mirror Ruby's `padding:` keyword (default true).

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL