base64

Fast Haskell base64 encoding/decoding (docs.ppad.tech/base64).
git clone git://git.ppad.tech/base64.git
Log | Files | Refs | README | LICENSE

commit 634f91042b13e9512fa8db4c2191bcf3e4a3f18c
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 16 May 2026 11:37:21 -0230

meta: initial scaffolding

Mirror ppad-base16 (master, v0.2.1) project layout: LICENSE,
.ghci, .gitignore, CHANGELOG, README, flake.nix/lock, and cabal
file. Library set up to expose Data.ByteString.Base64 with the
same llvm flag and dep bounds as ppad-base16.

Diffstat:
A.ghci | 2++
A.gitignore | 2++
ACHANGELOG | 4++++
ALICENSE | 20++++++++++++++++++++
AREADME.md | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aflake.lock | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aflake.nix | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Appad-base64.cabal | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 331 insertions(+), 0 deletions(-)

diff --git a/.ghci b/.ghci @@ -0,0 +1,2 @@ +:set -XOverloadedStrings +:set prompt "> " diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +dist-newstyle/ +report.html diff --git a/CHANGELOG b/CHANGELOG @@ -0,0 +1,4 @@ +# Changelog + +- 0.1.0 (2026-05-16) + * Initial release, supporting basic encoding/decoding. diff --git a/LICENSE b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2025 Jared Tobin + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md @@ -0,0 +1,68 @@ +# base64 + +[![](https://img.shields.io/hackage/v/ppad-base64?color=blue)](https://hackage.haskell.org/package/ppad-base64) +![](https://img.shields.io/badge/license-MIT-brightgreen) +[![](https://img.shields.io/badge/haddock-base64-lightblue)](https://docs.ppad.tech/base64) + +Pure base64 encoding & decoding on strict ByteStrings. + +## Usage + +A sample GHCi session: + +``` + > :set -XOverloadedStrings + > + > -- import qualified + > import qualified Data.ByteString.Base64 as B64 + > + > -- simple base64 encoding and decoding + > B64.encode "hello world" + "aGVsbG8gd29ybGQ=" + > + > B64.decode "aGVsbG8gd29ybGQ=" + Just "hello world" +``` + +## Documentation + +Haddocks (API documentation, etc.) are hosted at +[docs.ppad.tech/base64](https://docs.ppad.tech/base64). + +## Performance + +The aim is best-in-class performance for pure, highly-auditable Haskell +code. We could go slightly faster by using direct allocation and writes, +but we get pretty close to the best impure versions with only builders. + +Benchmark figures will be added after running the suite on the standard +VPS (use `cabal bench` to run the benchmark suite). + +## Security + +This library aims at the maximum security achievable in a +garbage-collected language under an optimizing compiler such as GHC, in +which strict constant-timeness can be challenging to achieve. + +If you discover any vulnerabilities, please disclose them via +security@ppad.tech. + +## Development + +You'll require [Nix][nixos] with [flake][flake] support enabled. Enter a +development shell with: + +``` +$ nix develop +``` + +Then do e.g.: + +``` +$ cabal repl ppad-base64 +``` + +to get a REPL for the main library. + +[nixos]: https://nixos.org/ +[flake]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html diff --git a/flake.lock b/flake.lock @@ -0,0 +1,88 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1766840161, + "narHash": "sha256-Ss/LHpJJsng8vz1Pe33RSGIWUOcqM1fjrehjUkdrWio=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3edc4a30ed3903fdf6f90c837f961fa6b49582d1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "ppad-nixpkgs": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1766932084, + "narHash": "sha256-GvVsbTfW+B7IQ9K/QP2xcXJAm1lhBin1jYZWNjOzT+o=", + "ref": "master", + "rev": "353e61763b959b960a55321a85423501e3e9ed7a", + "revCount": 2, + "type": "git", + "url": "git://git.ppad.tech/nixpkgs.git" + }, + "original": { + "ref": "master", + "type": "git", + "url": "git://git.ppad.tech/nixpkgs.git" + } + }, + "root": { + "inputs": { + "flake-utils": [ + "ppad-nixpkgs", + "flake-utils" + ], + "nixpkgs": [ + "ppad-nixpkgs", + "nixpkgs" + ], + "ppad-nixpkgs": "ppad-nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix @@ -0,0 +1,58 @@ +{ + description = "Pure Haskell base64 encoding and decoding on bytestrings."; + + inputs = { + ppad-nixpkgs = { + type = "git"; + url = "git://git.ppad.tech/nixpkgs.git"; + ref = "master"; + }; + flake-utils.follows = "ppad-nixpkgs/flake-utils"; + nixpkgs.follows = "ppad-nixpkgs/nixpkgs"; + }; + + outputs = { self, nixpkgs, flake-utils, ppad-nixpkgs }: + flake-utils.lib.eachDefaultSystem (system: + let + lib = "ppad-base64"; + + pkgs = import nixpkgs { inherit system; }; + hlib = pkgs.haskell.lib; + llvm = pkgs.llvmPackages_19.llvm; + + hpkgs = pkgs.haskell.packages.ghc910.extend (new: old: { + ${lib} = old.callCabal2nixWithOptions lib ./. "--enable-profiling" {}; + }); + + cc = pkgs.stdenv.cc; + ghc = hpkgs.ghc; + cabal = hpkgs.cabal-install; + in + { + packages.default = hpkgs.${lib}; + + devShells.default = hpkgs.shellFor { + packages = p: [ + (hlib.doBenchmark p.${lib}) + ]; + + buildInputs = [ + cabal + cc + llvm + ]; + + doBenchmark = true; + + shellHook = '' + PS1="[${lib}] \w$ " + echo "entering ${system} shell, using" + echo "cc: $(${cc}/bin/cc --version)" + echo "ghc: $(${ghc}/bin/ghc --version)" + echo "cabal: $(${cabal}/bin/cabal --version)" + echo "llc: $(${llvm}/bin/llc --version | head -2 | tail -1)" + ''; + }; + } + ); +} diff --git a/ppad-base64.cabal b/ppad-base64.cabal @@ -0,0 +1,89 @@ +cabal-version: 3.0 +name: ppad-base64 +version: 0.1.0 +synopsis: Pure base64 encoding and decoding on bytestrings. +license: MIT +license-file: LICENSE +author: Jared Tobin +maintainer: jared@ppad.tech +category: Cryptography +build-type: Simple +tested-with: GHC == { 9.10.3 } +extra-doc-files: CHANGELOG +description: + Pure base64 encoding and decoding on bytestrings. + +flag llvm + description: Use GHC's LLVM backend. + default: False + manual: True + +source-repository head + type: git + location: git.ppad.tech/base64.git + +library + default-language: Haskell2010 + hs-source-dirs: lib + ghc-options: + -Wall + if flag(llvm) + ghc-options: -fllvm -O2 + exposed-modules: + Data.ByteString.Base64 + build-depends: + base >= 4.9 && < 5 + , bytestring >= 0.9 && < 0.13 + +test-suite base64-tests + type: exitcode-stdio-1.0 + default-language: Haskell2010 + hs-source-dirs: test + main-is: Main.hs + + ghc-options: + -rtsopts -Wall -O2 + + build-depends: + base + , base64-bytestring + , bytestring + , ppad-base64 + , tasty + , tasty-hunit + , tasty-quickcheck + +benchmark base64-bench + type: exitcode-stdio-1.0 + default-language: Haskell2010 + hs-source-dirs: bench + main-is: Main.hs + + ghc-options: + -rtsopts -O2 -Wall + + build-depends: + base + , base64 + , base64-bytestring + , bytestring + , criterion + , ppad-base64 + +benchmark base64-weigh + type: exitcode-stdio-1.0 + default-language: Haskell2010 + hs-source-dirs: bench + main-is: Weight.hs + + ghc-options: + -rtsopts -O2 -Wall + + build-depends: + base + , base64 + , base64-bytestring + , bytestring + , criterion + , ppad-base64 + , weigh