diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,25 @@
+See also http://pvp.haskell.org/faq
+
+# 0.3.0.0
+
+* Semantic change: Switch to `base64-bytestring-1.2.1` which provides
+  even stricter Base64 decoding semantics regarding padding; switch to
+  `base16-bytestring-1.0.1` which results in changed error messages.
+
+* Add support for encoding/decoding to/from padded `base32` &
+  `base32hex`.
+
+----
+
+# 0.2.0.0
+
+* Semantic change: Switch to `base64-bytestring-0.2.0` which
+  provides stricter Base64 decoding semantics regarding padding.
+
+* Add support for encoding/decoding to/from unpadded `base64url`.
+
+----
+
+# 0.1.0.0
+
+* First version. Released on an unsuspecting world.
diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
diff --git a/base-encoding.cabal b/base-encoding.cabal
--- a/base-encoding.cabal
+++ b/base-encoding.cabal
@@ -1,15 +1,14 @@
-cabal-version:       1.12
+cabal-version:       2.2
 name:                base-encoding
-version:             0.1.0.0
+version:             0.3.0.0
 
-synopsis:            Binary-to-text encodings (e.g. base64)
-license:             BSD3
+synopsis:            RFC4648 Binary-to-text encodings (e.g. base64)
+license:             BSD-3-Clause
 license-file:        LICENSE
 author:              Herbert Valerio Riedel
 bug-reports:         https://github.com/hvr/base-encoding/issues
 maintainer:          hvr@gnu.org
-category:            Text
-build-type:          Simple
+category:            Codec, Text
 description:
   This package provides a simple and convenient API to encode and
   decode binary data in the popular binary-to-text \"base\" encoding
@@ -17,10 +16,16 @@
   .
   Currently, the following encodings are supported:
   .
-  - base16 (RFC4648)
-  - base64 (RFC4648)
-  - base64url (RFC4648)
+  - (unpadded) @base16@ (RFC4648)
+  - (padded) @base32@ (RFC4648)
+  - (padded) @base32hex@ (RFC4648)
+  - (padded) @base64@ (RFC4648)
+  - padded @base64url@ (RFC4648)
+  - unpadded @base64url@ (RFC4648)
+  .
 
+extra-source-files: ChangeLog.md
+
 source-repository head
   type:     git
   location: https://github.com/hvr/base-encoding
@@ -30,11 +35,15 @@
 
   exposed-modules:
     Codec.Base16
+    Codec.Base32
+    Codec.Base32Hex
     Codec.Base64
     Codec.Base64Url
+    Codec.Base64Url.Unpadded
 
   other-modules:
     Internal
+    Codec.Base32.Impl
 
   default-language:    Haskell2010
   other-extensions:    CPP MultiParamTypeClasses
@@ -45,11 +54,12 @@
       ghc-options: -fno-warn-trustworthy-safe
 
   build-depends:
-      base               >=4.3      && <4.12
-    , base16-bytestring  >=0.1.1.6  && <0.2
-    , base64-bytestring  >=1.0.0.1  && <1.1
+    , base                >=4.3      && <4.15
+    , base16-bytestring  ^>=1.0.1
+    , base64-bytestring  ^>=1.2.1
 
-    , bytestring         >=0.9.1    && <0.11
-    , text               >=1.2.3    && <1.3
+    , bytestring         ^>=0.9.1 || ^>= 0.10.0
+    , bytestring-builder ^>=0.10.8.2
+    , text               ^>=1.2.3
 
   ghc-options: -Wall
diff --git a/src/Codec/Base16.hs b/src/Codec/Base16.hs
--- a/src/Codec/Base16.hs
+++ b/src/Codec/Base16.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP                   #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 
 -- | This module provides access to the \"base16\" binary-to-text encoding as defined by [RFC 4648](https://tools.ietf.org/html/rfc4648).
@@ -33,15 +32,9 @@
 import qualified Data.ByteString.Base16.Lazy  as B16.L
 
 import qualified Data.ByteString              as BS
-#if MIN_VERSION_bytestring(0,10,2)
 import qualified Data.ByteString.Builder      as BB
-#elif MIN_VERSION_bytestring(0,10,0)
-import qualified Data.ByteString.Lazy.Builder as BB
-#endif
 import qualified Data.ByteString.Lazy         as BS.L
-#if MIN_VERSION_bytestring(0,10,4)
 import qualified Data.ByteString.Short        as SBS
-#endif
 
 import qualified Data.Text                    as T (Text)
 import qualified Data.Text.Encoding           as T (decodeLatin1, encodeUtf8)
@@ -55,18 +48,10 @@
 -- primitives
 
 decodeBs2Bs :: BS.ByteString -> Either String BS.ByteString
-decodeBs2Bs txt
-  | BS.null rest = Right $! b
-  | otherwise    = Left ("invalid base16 encoding near offset " ++ show (2 * BS.length b))
-  where
-    (b,rest) = B16.decode txt
+decodeBs2Bs = B16.decode
 
 decodeBsL2BsL :: BS.L.ByteString -> Either String BS.L.ByteString
-decodeBsL2BsL txt
-  | BS.L.null rest = Right $! b
-  | otherwise      = Left ("invalid base16 encoding near offset " ++ show (2 * BS.L.length b))
-  where
-    (b,rest) = B16.L.decode txt
+decodeBsL2BsL = B16.L.decode
 
 encodeBs2Bs :: BS.ByteString -> BS.ByteString
 encodeBs2Bs = B16.encode
@@ -99,15 +84,11 @@
 instance Encode BS.L.ByteString BS.ByteString where
   encode = bsToStrict . encode
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Encode BS.L.ByteString BB.Builder where
   encode = BB.lazyByteString . encode
-#endif
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Encode BS.L.ByteString SBS.ShortByteString where
   encode = SBS.toShort . encode
-#endif
 
 instance Encode BS.L.ByteString T.Text where
   encode = T.decodeLatin1 . encode
@@ -127,15 +108,11 @@
 instance Encode BS.ByteString BS.L.ByteString where
   encode = bsFromStrict . encode
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Encode BS.ByteString BB.Builder where
   encode = BB.byteString . encode
-#endif
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Encode BS.ByteString SBS.ShortByteString where
   encode = SBS.toShort . encode
-#endif
 
 instance Encode BS.ByteString T.Text where
   encode = T.decodeLatin1 . encode
@@ -148,7 +125,6 @@
 
 ---- short BS  -> *
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Encode SBS.ShortByteString SBS.ShortByteString where
   encode = SBS.toShort . encode . SBS.fromShort
 
@@ -169,16 +145,12 @@
 
 instance Encode SBS.ShortByteString TB.Builder where
   encode = TB.fromText . encode
-#endif
 
 ---- BB  -> *
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Encode BB.Builder SBS.ShortByteString where
   encode = encode . BB.toLazyByteString
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Encode BB.Builder BB.Builder where
   encode = encode . BB.toLazyByteString
 
@@ -196,7 +168,6 @@
 
 instance Encode BB.Builder TB.Builder where
   encode = TB.fromLazyText . encode
-#endif
 
 ------------------------------------------------------------------------------
 
@@ -207,15 +178,11 @@
 instance Decode BS.ByteString BS.L.ByteString where
   decode = fmap bsFromStrict . decode
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode BS.ByteString SBS.ShortByteString where
   decode = fmap SBS.toShort . decode
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode BS.ByteString BB.Builder where
   decode = fmap BB.byteString . decode
-#endif
 
 ----
 
@@ -226,19 +193,14 @@
 instance Decode BS.L.ByteString BS.ByteString where
   decode = fmap bsToStrict . decode
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode BS.L.ByteString SBS.ShortByteString where
   decode = fmap SBS.toShort . decode
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode BS.L.ByteString BB.Builder where
   decode = fmap BB.byteString . decode
-#endif
 
 ----
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode SBS.ShortByteString SBS.ShortByteString where
   decode = fmap SBS.toShort . decode . SBS.fromShort
 
@@ -250,16 +212,12 @@
 
 instance Decode SBS.ShortByteString BB.Builder where
   decode = decode . SBS.fromShort
-#endif
 
 ----
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode BB.Builder SBS.ShortByteString where
   decode = decode . BB.toLazyByteString
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode BB.Builder BS.L.ByteString where
   decode = decode . BB.toLazyByteString
 
@@ -268,7 +226,6 @@
 
 instance Decode BB.Builder BB.Builder where
   decode = decode . BB.toLazyByteString
-#endif
 
 ----
 
@@ -278,15 +235,11 @@
 instance Decode T.Text BS.L.ByteString where
   decode = decode . T.encodeUtf8
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode T.Text SBS.ShortByteString where
   decode = decode . T.encodeUtf8
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode T.Text BB.Builder where
   decode = decode . T.encodeUtf8
-#endif
 
 ----
 
@@ -296,15 +249,11 @@
 instance Decode T.L.Text BS.L.ByteString where
   decode = decode . T.L.encodeUtf8
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode T.L.Text SBS.ShortByteString where
   decode = decode . T.L.encodeUtf8
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode T.L.Text BB.Builder where
   decode = decode . T.L.encodeUtf8
-#endif
 
 ----
 
@@ -314,12 +263,8 @@
 instance Decode TB.Builder BS.L.ByteString where
   decode = decode . TB.toLazyText
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode TB.Builder SBS.ShortByteString where
   decode = decode . TB.toLazyText
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode TB.Builder BB.Builder where
   decode = decode . TB.toLazyText
-#endif
diff --git a/src/Codec/Base32.hs b/src/Codec/Base32.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Base32.hs
@@ -0,0 +1,269 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+-- | This module provides access to the \"base32\" binary-to-text encoding as defined by [RFC 4648](https://tools.ietf.org/html/rfc4648).
+--
+-- This module is intended to be imported @qualified@, e.g.
+--
+-- > import qualified Codec.Base32 as B32
+--
+-- If you want to explictly specify which 'Encode' and 'Decode' typeclass instance is used, you can use plain Haskell2010 type-signature annotations, e.g.
+--
+-- >>> (B32.encode :: ByteString -> Text) "\x00\x00"
+-- "AAAA===="
+--
+-- >>> (B32.decode :: Text -> Either String ShortByteString) "GQ5DEMA="
+-- Right "4:20"
+--
+-- Alternatively, starting with GHC 8.0.1, you can also use the [TypeApplications language extension](https://downloads.haskell.org/~ghc/8.4.1/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications):
+--
+-- >>> B32.encode @ShortByteString @Text "\xFF\239"
+-- "77XQ===="
+--
+-- >>> B32.decode @Text @ShortByteString "77XQ===="
+-- Right "\255\239"
+--
+-- @since 0.3.0.0
+module Codec.Base32
+    ( Encode(encode)
+    , Decode(decode)
+    ) where
+
+import qualified Codec.Base32.Impl as Impl
+
+import qualified Data.ByteString              as BS
+import qualified Data.ByteString.Builder      as BB
+import qualified Data.ByteString.Lazy         as BS.L
+import qualified Data.ByteString.Short        as SBS
+
+import qualified Data.Text                    as T (Text)
+import qualified Data.Text.Encoding           as T (decodeLatin1, encodeUtf8)
+import qualified Data.Text.Lazy               as T.L (Text, fromStrict)
+import qualified Data.Text.Lazy.Builder       as TB (Builder, fromLazyText,
+                                                     fromText, toLazyText)
+import qualified Data.Text.Lazy.Encoding      as T.L (decodeLatin1, encodeUtf8)
+
+import           Internal
+
+-- primitives
+
+decodeBs2Bs :: BS.ByteString -> Either String BS.ByteString
+decodeBs2Bs = Impl.decodeBs2Bs Impl.Fmt'base32
+
+decodeBsL2BsL :: BS.L.ByteString -> Either String BS.L.ByteString
+decodeBsL2BsL = Impl.decodeBsL2BsL Impl.Fmt'base32
+
+encodeBs2Bs :: BS.ByteString -> BS.ByteString
+encodeBs2Bs = Impl.encodeBs2Bs Impl.Fmt'base32
+
+encodeBsL2BsL :: BS.L.ByteString -> BS.L.ByteString
+encodeBsL2BsL = Impl.encodeBsL2BsL Impl.Fmt'base32
+
+----------------------------------------------------------------------------
+-- exposed API
+
+-- | Typeclass representing types for which a binary-to-text @base32@ encoding is defined
+class Encode bin txt where
+  -- | Encode binary data using @base32@ text encoding
+  encode :: bin -> txt
+
+-- | Typeclass representing types for which a text-to-binary @base32@ decoding is defined
+class Decode txt bin where
+  -- | Decode binary data encoded textually as @base32@
+  decode :: txt -> Either String bin
+
+----------------------------------------------------------------------------
+-- instance matrix
+
+---- lazy BS -> *
+
+-- PRIMITIVE
+instance Encode BS.L.ByteString BS.L.ByteString where
+  encode = encodeBsL2BsL
+
+instance Encode BS.L.ByteString BS.ByteString where
+  encode = bsToStrict . encode
+
+instance Encode BS.L.ByteString BB.Builder where
+  encode = BB.lazyByteString . encode
+
+instance Encode BS.L.ByteString SBS.ShortByteString where
+  encode = SBS.toShort . encode
+
+instance Encode BS.L.ByteString T.Text where
+  encode = T.decodeLatin1 . encode
+
+instance Encode BS.L.ByteString T.L.Text where
+  encode = T.L.decodeLatin1 . encode
+
+instance Encode BS.L.ByteString TB.Builder where
+  encode = TB.fromLazyText . encode
+
+---- strict BS  -> *
+
+-- PRIMITIVE
+instance Encode BS.ByteString BS.ByteString where
+  encode = encodeBs2Bs
+
+instance Encode BS.ByteString BS.L.ByteString where
+  encode = bsFromStrict . encode
+
+instance Encode BS.ByteString BB.Builder where
+  encode = BB.byteString . encode
+
+instance Encode BS.ByteString SBS.ShortByteString where
+  encode = SBS.toShort . encode
+
+instance Encode BS.ByteString T.Text where
+  encode = T.decodeLatin1 . encode
+
+instance Encode BS.ByteString T.L.Text where
+  encode = T.L.fromStrict . T.decodeLatin1 . encode
+
+instance Encode BS.ByteString TB.Builder where
+  encode = TB.fromText . encode
+
+---- short BS  -> *
+
+instance Encode SBS.ShortByteString SBS.ShortByteString where
+  encode = SBS.toShort . encode . SBS.fromShort
+
+instance Encode SBS.ShortByteString BS.ByteString where
+  encode = encode . SBS.fromShort
+
+instance Encode SBS.ShortByteString BS.L.ByteString where
+  encode = encode . SBS.fromShort
+
+instance Encode SBS.ShortByteString BB.Builder where
+  encode = encode . SBS.fromShort
+
+instance Encode SBS.ShortByteString T.Text where
+  encode = T.decodeLatin1 . encode
+
+instance Encode SBS.ShortByteString T.L.Text where
+  encode = T.L.fromStrict . T.decodeLatin1 . encode
+
+instance Encode SBS.ShortByteString TB.Builder where
+  encode = TB.fromText . encode
+
+---- BB  -> *
+
+instance Encode BB.Builder SBS.ShortByteString where
+  encode = encode . BB.toLazyByteString
+
+instance Encode BB.Builder BB.Builder where
+  encode = encode . BB.toLazyByteString
+
+instance Encode BB.Builder BS.ByteString where
+  encode = encode . BB.toLazyByteString
+
+instance Encode BB.Builder BS.L.ByteString where
+  encode = encode . BB.toLazyByteString
+
+instance Encode BB.Builder T.Text where
+  encode = T.decodeLatin1 . encode
+
+instance Encode BB.Builder T.L.Text where
+  encode = T.L.decodeLatin1 . encode
+
+instance Encode BB.Builder TB.Builder where
+  encode = TB.fromLazyText . encode
+
+------------------------------------------------------------------------------
+
+-- PRIMITIVE
+instance Decode BS.ByteString BS.ByteString where
+  decode = decodeBs2Bs
+
+instance Decode BS.ByteString BS.L.ByteString where
+  decode = fmap bsFromStrict . decode
+
+instance Decode BS.ByteString SBS.ShortByteString where
+  decode = fmap SBS.toShort . decode
+
+instance Decode BS.ByteString BB.Builder where
+  decode = fmap BB.byteString . decode
+
+----
+
+-- PRIMITIVE
+instance Decode BS.L.ByteString BS.L.ByteString where
+  decode = decodeBsL2BsL
+
+instance Decode BS.L.ByteString BS.ByteString where
+  decode = fmap bsToStrict . decode
+
+instance Decode BS.L.ByteString SBS.ShortByteString where
+  decode = fmap SBS.toShort . decode
+
+instance Decode BS.L.ByteString BB.Builder where
+  decode = fmap BB.byteString . decode
+
+----
+
+instance Decode SBS.ShortByteString SBS.ShortByteString where
+  decode = fmap SBS.toShort . decode . SBS.fromShort
+
+instance Decode SBS.ShortByteString BS.ByteString where
+  decode = decode . SBS.fromShort
+
+instance Decode SBS.ShortByteString BS.L.ByteString where
+  decode = decode . SBS.fromShort
+
+instance Decode SBS.ShortByteString BB.Builder where
+  decode = decode . SBS.fromShort
+
+----
+
+instance Decode BB.Builder SBS.ShortByteString where
+  decode = decode . BB.toLazyByteString
+
+instance Decode BB.Builder BS.L.ByteString where
+  decode = decode . BB.toLazyByteString
+
+instance Decode BB.Builder BS.ByteString where
+  decode = decode . BB.toLazyByteString
+
+instance Decode BB.Builder BB.Builder where
+  decode = decode . BB.toLazyByteString
+
+----
+
+instance Decode T.Text BS.ByteString where
+  decode = decode . T.encodeUtf8
+
+instance Decode T.Text BS.L.ByteString where
+  decode = decode . T.encodeUtf8
+
+instance Decode T.Text SBS.ShortByteString where
+  decode = decode . T.encodeUtf8
+
+instance Decode T.Text BB.Builder where
+  decode = decode . T.encodeUtf8
+
+----
+
+instance Decode T.L.Text BS.ByteString where
+  decode = decode . T.L.encodeUtf8
+
+instance Decode T.L.Text BS.L.ByteString where
+  decode = decode . T.L.encodeUtf8
+
+instance Decode T.L.Text SBS.ShortByteString where
+  decode = decode . T.L.encodeUtf8
+
+instance Decode T.L.Text BB.Builder where
+  decode = decode . T.L.encodeUtf8
+
+----
+
+instance Decode TB.Builder BS.ByteString where
+  decode = decode . TB.toLazyText
+
+instance Decode TB.Builder BS.L.ByteString where
+  decode = decode . TB.toLazyText
+
+instance Decode TB.Builder SBS.ShortByteString where
+  decode = decode . TB.toLazyText
+
+instance Decode TB.Builder BB.Builder where
+  decode = decode . TB.toLazyText
diff --git a/src/Codec/Base32/Impl.hs b/src/Codec/Base32/Impl.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Base32/Impl.hs
@@ -0,0 +1,251 @@
+{-# LANGUAGE BangPatterns #-}
+
+-- simple unoptimized implementation of base32(hex)
+module Codec.Base32.Impl
+   ( decodeBs2Bs
+   , decodeBsL2BsL
+   , encodeBs2Bs
+   , encodeBsL2BsL
+   , Fmt(..)
+   ) where
+
+import qualified Data.ByteString         as BS
+import qualified Data.ByteString.Builder as BB
+import qualified Data.ByteString.Char8   as BS.C
+import qualified Data.ByteString.Lazy    as BS.L
+
+import           Control.Monad
+import           Data.Bits
+import           Data.Word
+
+import           Internal
+
+-- exported primitives
+
+data Fmt = Fmt'base32 | Fmt'base32hex
+
+{-# INLINE decodeBs2Bs #-}
+decodeBs2Bs :: Fmt -> BS.ByteString -> Either String BS.ByteString
+decodeBs2Bs !fmt bs0 = case runDecode fmt bs0 of
+  DR'Final chunk         -> pure $! bsToStrict $ BB.toLazyByteString chunk
+  DR'Error ofs err       -> Left $ showDecErr ofs err
+  DR'Partial chunk0 cont -> case cont mempty of
+    DR'Final chunk1  -> pure $! bsToStrict $ BB.toLazyByteString (chunk0<>chunk1) -- chunk1 ought to be empty -- TODO: maybe refactor internal API
+    DR'Partial _ _   -> error "decodeBs2Bs: the impossible just happened" -- broken invariant
+    DR'Error ofs err -> Left $ showDecErr ofs err
+
+{-# INLINE decodeBsL2BsL #-}
+decodeBsL2BsL :: Fmt -> BS.L.ByteString -> Either String BS.L.ByteString
+decodeBsL2BsL !fmt bs0 = go mempty (runDecode fmt) (filter (not . BS.null) $ BS.L.toChunks bs0)
+  where
+    go acc runDec (c:cs) = case runDec c of
+      DR'Final _            -> error "impossible"
+      DR'Error ofs err      -> Left $! showDecErr ofs err -- TODO
+      DR'Partial chunk cont -> go (acc <> chunk) cont cs
+
+    go acc runDec [] = case runDec mempty of
+      DR'Final chunk   -> pure $! BB.toLazyByteString (acc <> chunk)
+      DR'Partial _ _   -> error "decodeBsL2BsL: the impossible just happened" -- broken invariant
+      DR'Error ofs err -> Left $ showDecErr ofs err
+
+encodeBs2Bs :: Fmt -> BS.ByteString -> BS.ByteString
+encodeBs2Bs !fmt = BS.C.pack . slowEnc fmt True . BS.unpack
+
+encodeBsL2BsL :: Fmt -> BS.L.ByteString -> BS.L.ByteString
+encodeBsL2BsL !fmt = BS.L.fromChunks . go mempty . BS.L.toChunks
+  where
+    go rest [] = encodeBs2Bs fmt rest : []
+    go rest (c:cs)
+      | rclen < 5 = go rc cs -- shortcut
+      | otherwise = encodeBs2Bs fmt c' : go rest' cs
+      where
+        rlen = BS.length rest
+        clen = BS.length c
+        rc = rest <> c -- TODO: avoid copying large chunks of data
+        rclen = rlen + clen
+        rlen' = rclen `rem` 5
+        (c',rest') = BS.splitAt (rclen - rlen') rc
+
+----------------------------------------------------------------------------
+-- encoding
+
+-- | Relatively slow streaming base32 encoder
+slowEnc :: Fmt -> Bool -> [Word8] -> [Char]
+slowEnc !fmt doPad = go0
+  where
+    go0 [] = []
+    go0 (x:xs) = sym hi : go1 lo xs
+      where -- 5|3
+        (hi,lo) = x `quotRem` 0x08
+
+    go1 rest [] = sym (rest * 0x04) : pad "======"
+    go1 rest (x:xs) = sym (rest * 0x04 + hi) : sym mid : go2 lo xs
+      where -- 2|5|1
+        (hi,tmp) = x `quotRem` 0x40
+        (mid,lo) = tmp `quotRem` 0x02
+
+    go2 rest [] = sym (rest * 0x10) : pad "===="
+    go2 rest (x:xs) = sym (rest * 0x10 + hi) : go3 lo xs
+      where -- 4|4
+        (hi,lo) = x `quotRem` 0x10
+
+    go3 rest [] = sym (rest * 0x02) : pad "==="
+    go3 rest (x:xs) = sym (rest * 0x02 + hi) : sym mid : go4 lo xs
+      where -- 1|5|2
+        (hi,tmp) = x `quotRem` 0x80
+        (mid,lo) = tmp `quotRem` 0x04
+
+    go4 rest [] = sym (rest * 0x08) : pad "="
+    go4 rest (x:xs) = sym (rest * 0x08 + hi) : sym lo : go0 xs
+      where -- 3|5
+        (hi,lo) = x `quotRem` 0x20
+
+    pad x | doPad     = x
+          | otherwise = []
+
+    sym = case fmt of
+      Fmt'base32    -> sym0
+      Fmt'base32hex -> symx
+
+    -- plain base32 alphabet
+    sym0 :: Word8 -> Char
+    sym0 w
+     | w < 26 = toEnum (fromIntegral w + 0x41)
+     | w < 32 = toEnum (fromIntegral w + 24)
+     | otherwise = undefined -- sym (w `rem` 32)
+
+    -- base32hex alphabet
+    symx :: Word8 -> Char
+    symx w
+     | w < 10 = toEnum (fromIntegral w + 0x30)
+     | w < 32 = toEnum (fromIntegral w + 55)
+     | otherwise = undefined -- sym (w `rem` 32)
+
+----------------------------------------------------------------------------
+-- decoding
+
+-- internal abstraction
+data DecodeRes = DR'Final   !BB.Builder -- only emitted on empty EOF-signalling input
+               | DR'Partial !BB.Builder (BS.ByteString -> DecodeRes)
+               | DR'Error   !Word DecodeError
+
+instance Show DecodeRes where
+  show (DR'Final x)       = "DR'Final "   ++ show (BB.toLazyByteString x)
+  show (DR'Partial x _)   = "DR'Partial " ++ show (BB.toLazyByteString x) ++ " <cont>"
+  show (DR'Error ofs err) = "DR'Err "     ++ show ofs ++ " " ++ show err
+
+data DecodeError = Error'IncompleteInput
+                 | Error'InvalidChar
+                 | Error'InvalidPad
+                 deriving Show
+
+showDecErr :: Word -> DecodeError -> String
+showDecErr ofs Error'InvalidPad      = "Base32-encoded data has invalid padding at offset: " ++ show ofs
+showDecErr ofs Error'IncompleteInput = "Base32-encoded data ended prematurely at offset: " ++ show ofs
+showDecErr ofs Error'InvalidChar     = "Base32-encoded data has invalid character at offset: " ++ show ofs
+
+runDecode :: Fmt -> BS.ByteString -> DecodeRes
+runDecode !fmt = go 0 mempty
+  where
+    -- invariants:
+    --   len(buf0) < 8
+
+    go ofs0 buf0 buf1
+      | doflush, BS.null buf0 = DR'Final mempty
+      | doflush = case decodePaddedChunk fmt buf0 of
+                    Left (ofs,err) -> DR'Error (ofs0+ofs) err
+                    Right (out,rest)
+                      | BS.null rest -> DR'Final out -- actually not possible for padded input
+                      | otherwise -> error "runDecode: the impossible just happened" -- because buf0 invariant
+      | buf01len < 8 = DR'Partial mempty (go ofs0 buf01)
+      | otherwise = case decodeChunks fmt buf01 of
+                      Left (ofs,err) -> DR'Error (ofs0+ofs) err
+                      Right (chunks,rest) -> DR'Partial chunks (go (ofs0 + fromIntegral (buf01len - BS.length rest)) rest)
+      where
+        -- TODO: consider avoiding copying all of buf1 and instead copy only the prefix needed to
+        -- complete buf0 into a full chunk
+        buf01 = buf0 <> buf1
+        buf01len = BS.length buf01
+
+        doflush = BS.null buf1
+
+-- | Iterate over multiple chunks; calls 'decodeChunk' repeatedly
+--
+-- The remaining ByteString is guaranteed to be smaller than 8 octets
+decodeChunks :: Fmt -> BS.ByteString -> Either (Word,DecodeError) (BB.Builder,BS.ByteString)
+decodeChunks !fmt = go 0 mempty
+  where
+    go !ofs0 bb0 bs0
+      | BS.null bs0 = pure (bb0,mempty)
+      | otherwise = case decodePaddedChunk fmt bs0 of
+         Left (_, Error'IncompleteInput) -> pure (bb0,bs0)
+         Left (ofs1,err)                 -> Left (ofs0+ofs1, err)
+         Right (chunk, rest)             -> go (ofs0+8) (bb0 <> chunk) rest
+
+-- | Tries do decode a single 40bit base32 chunk (encoded as eight case-insensitive ASCII chars)
+--
+-- NB: An empty input leads to an incomplete input error result, as this function will either decode exactly a single
+-- chunk or report an error
+decodePaddedChunk :: Fmt -> BS.ByteString -> Either (Word,DecodeError) (BB.Builder,BS.ByteString)
+decodePaddedChunk !fmt bs0
+  | bs0len < 8 = Left (fromIntegral $ bs0len, Error'IncompleteInput)
+  | otherwise = do
+      let (digs,padding) = BS.break (== 0x3d) $ BS.take 8 bs0
+
+      let vlen = fromIntegral $ BS.length digs
+      vals <- forM (zip [0..] (BS.C.unpack digs)) $
+        \(i,c) -> maybe (Left (i,Error'InvalidChar)) pure $ desym c
+
+      olen <- case vlen of
+        0 -> Left (0, Error'InvalidPad)
+        1 -> Left (1, Error'InvalidPad)
+        2 -> pure 1
+        3 -> Left (3, Error'InvalidPad)
+        4 -> pure 2
+        5 -> pure 3
+        6 -> Left (6, Error'InvalidPad)
+        7 -> pure 4
+        8 -> pure 5
+        _ -> undefined
+
+      -- check padding trailer has no garbage
+      forM_ (zip [vlen ..] (BS.unpack padding)) $
+        \(i,c) -> unless (c == 0x3d) $ Left (i, Error'InvalidPad)
+
+      -- bit shuffle 8*5bit into 5*8bit
+      let buf64 = sum $ zipWith shiftL (map fromIntegral vals)  [35, 30 .. 0]
+          buf8  = map (fromIntegral . shiftR (buf64 :: Word64)) [32, 24 .. 0]
+
+      -- check last 5bit digit didn't have unused bits set
+      unless (all (== 0) $ drop olen buf8) $ do
+        Left (fromIntegral vlen - 1, Error'InvalidPad)
+
+      pure $ (BB.byteString $ BS.pack $ take olen buf8, BS.drop 8 bs0)
+  where
+    bs0len = BS.length bs0
+
+    desym = case fmt of
+      Fmt'base32    -> desym0
+      Fmt'base32hex -> desymx
+
+    -- plain base32 alphabet (case-insens)
+    desym0 :: Char -> Maybe Word8
+    desym0 c0 = case fromEnum c0 of
+      c | c < 0x32 -> Nothing
+        | c < 0x38 -> Just $! fromIntegral (c - 24)
+        | c < 0x41 -> Nothing
+        | c < 0x5b -> Just $! fromIntegral (c - 0x41)
+        | c < 0x61 -> Nothing
+        | c < 0x7b -> Just $! fromIntegral (c - 0x61)
+        | otherwise -> Nothing
+
+    -- base32hex alphabet (case-insens)
+    desymx :: Char -> Maybe Word8
+    desymx c0 = case fromEnum c0 of
+      c | c < 0x30 -> Nothing
+        | c < 0x3a -> Just $! fromIntegral (c - 0x30)
+        | c < 0x41 -> Nothing
+        | c < 0x57 -> Just $! fromIntegral (c - 55)
+        | c < 0x61 -> Nothing
+        | c < 0x77 -> Just $! fromIntegral (c - 87)
+        | otherwise -> Nothing
diff --git a/src/Codec/Base32Hex.hs b/src/Codec/Base32Hex.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Base32Hex.hs
@@ -0,0 +1,269 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+-- | This module provides access to the \"base32hex\" binary-to-text encoding as defined by [RFC 4648](https://tools.ietf.org/html/rfc4648).
+--
+-- This module is intended to be imported @qualified@, e.g.
+--
+-- > import qualified Codec.Base32Hex as B32
+--
+-- If you want to explictly specify which 'Encode' and 'Decode' typeclass instance is used, you can use plain Haskell2010 type-signature annotations, e.g.
+--
+-- >>> (B32.encode :: ByteString -> Text) "\x00\x00"
+-- "0000===="
+--
+-- >>> (B32.decode :: Text -> Either String ShortByteString) "6GT34C0="
+-- Right "4:20"
+--
+-- Alternatively, starting with GHC 8.0.1, you can also use the [TypeApplications language extension](https://downloads.haskell.org/~ghc/8.4.1/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications):
+--
+-- >>> B32.encode @ShortByteString @Text "\xFF\239"
+-- "VVNG===="
+--
+-- >>> B32.decode @Text @ShortByteString "VVNG===="
+-- Right "\255\239"
+--
+-- @since 0.3.0.0
+module Codec.Base32Hex
+    ( Encode(encode)
+    , Decode(decode)
+    ) where
+
+import qualified Codec.Base32.Impl as Impl
+
+import qualified Data.ByteString              as BS
+import qualified Data.ByteString.Builder      as BB
+import qualified Data.ByteString.Lazy         as BS.L
+import qualified Data.ByteString.Short        as SBS
+
+import qualified Data.Text                    as T (Text)
+import qualified Data.Text.Encoding           as T (decodeLatin1, encodeUtf8)
+import qualified Data.Text.Lazy               as T.L (Text, fromStrict)
+import qualified Data.Text.Lazy.Builder       as TB (Builder, fromLazyText,
+                                                     fromText, toLazyText)
+import qualified Data.Text.Lazy.Encoding      as T.L (decodeLatin1, encodeUtf8)
+
+import           Internal
+
+-- primitives
+
+decodeBs2Bs :: BS.ByteString -> Either String BS.ByteString
+decodeBs2Bs = Impl.decodeBs2Bs Impl.Fmt'base32hex
+
+decodeBsL2BsL :: BS.L.ByteString -> Either String BS.L.ByteString
+decodeBsL2BsL = Impl.decodeBsL2BsL Impl.Fmt'base32hex
+
+encodeBs2Bs :: BS.ByteString -> BS.ByteString
+encodeBs2Bs = Impl.encodeBs2Bs Impl.Fmt'base32hex
+
+encodeBsL2BsL :: BS.L.ByteString -> BS.L.ByteString
+encodeBsL2BsL = Impl.encodeBsL2BsL Impl.Fmt'base32hex
+
+----------------------------------------------------------------------------
+-- exposed API
+
+-- | Typeclass representing types for which a binary-to-text @base32hex@ encoding is defined
+class Encode bin txt where
+  -- | Encode binary data using @base32hex@ text encoding
+  encode :: bin -> txt
+
+-- | Typeclass representing types for which a text-to-binary @base32hex@ decoding is defined
+class Decode txt bin where
+  -- | Decode binary data encoded textually as @base32hex@
+  decode :: txt -> Either String bin
+
+----------------------------------------------------------------------------
+-- instance matrix
+
+---- lazy BS -> *
+
+-- PRIMITIVE
+instance Encode BS.L.ByteString BS.L.ByteString where
+  encode = encodeBsL2BsL
+
+instance Encode BS.L.ByteString BS.ByteString where
+  encode = bsToStrict . encode
+
+instance Encode BS.L.ByteString BB.Builder where
+  encode = BB.lazyByteString . encode
+
+instance Encode BS.L.ByteString SBS.ShortByteString where
+  encode = SBS.toShort . encode
+
+instance Encode BS.L.ByteString T.Text where
+  encode = T.decodeLatin1 . encode
+
+instance Encode BS.L.ByteString T.L.Text where
+  encode = T.L.decodeLatin1 . encode
+
+instance Encode BS.L.ByteString TB.Builder where
+  encode = TB.fromLazyText . encode
+
+---- strict BS  -> *
+
+-- PRIMITIVE
+instance Encode BS.ByteString BS.ByteString where
+  encode = encodeBs2Bs
+
+instance Encode BS.ByteString BS.L.ByteString where
+  encode = bsFromStrict . encode
+
+instance Encode BS.ByteString BB.Builder where
+  encode = BB.byteString . encode
+
+instance Encode BS.ByteString SBS.ShortByteString where
+  encode = SBS.toShort . encode
+
+instance Encode BS.ByteString T.Text where
+  encode = T.decodeLatin1 . encode
+
+instance Encode BS.ByteString T.L.Text where
+  encode = T.L.fromStrict . T.decodeLatin1 . encode
+
+instance Encode BS.ByteString TB.Builder where
+  encode = TB.fromText . encode
+
+---- short BS  -> *
+
+instance Encode SBS.ShortByteString SBS.ShortByteString where
+  encode = SBS.toShort . encode . SBS.fromShort
+
+instance Encode SBS.ShortByteString BS.ByteString where
+  encode = encode . SBS.fromShort
+
+instance Encode SBS.ShortByteString BS.L.ByteString where
+  encode = encode . SBS.fromShort
+
+instance Encode SBS.ShortByteString BB.Builder where
+  encode = encode . SBS.fromShort
+
+instance Encode SBS.ShortByteString T.Text where
+  encode = T.decodeLatin1 . encode
+
+instance Encode SBS.ShortByteString T.L.Text where
+  encode = T.L.fromStrict . T.decodeLatin1 . encode
+
+instance Encode SBS.ShortByteString TB.Builder where
+  encode = TB.fromText . encode
+
+---- BB  -> *
+
+instance Encode BB.Builder SBS.ShortByteString where
+  encode = encode . BB.toLazyByteString
+
+instance Encode BB.Builder BB.Builder where
+  encode = encode . BB.toLazyByteString
+
+instance Encode BB.Builder BS.ByteString where
+  encode = encode . BB.toLazyByteString
+
+instance Encode BB.Builder BS.L.ByteString where
+  encode = encode . BB.toLazyByteString
+
+instance Encode BB.Builder T.Text where
+  encode = T.decodeLatin1 . encode
+
+instance Encode BB.Builder T.L.Text where
+  encode = T.L.decodeLatin1 . encode
+
+instance Encode BB.Builder TB.Builder where
+  encode = TB.fromLazyText . encode
+
+------------------------------------------------------------------------------
+
+-- PRIMITIVE
+instance Decode BS.ByteString BS.ByteString where
+  decode = decodeBs2Bs
+
+instance Decode BS.ByteString BS.L.ByteString where
+  decode = fmap bsFromStrict . decode
+
+instance Decode BS.ByteString SBS.ShortByteString where
+  decode = fmap SBS.toShort . decode
+
+instance Decode BS.ByteString BB.Builder where
+  decode = fmap BB.byteString . decode
+
+----
+
+-- PRIMITIVE
+instance Decode BS.L.ByteString BS.L.ByteString where
+  decode = decodeBsL2BsL
+
+instance Decode BS.L.ByteString BS.ByteString where
+  decode = fmap bsToStrict . decode
+
+instance Decode BS.L.ByteString SBS.ShortByteString where
+  decode = fmap SBS.toShort . decode
+
+instance Decode BS.L.ByteString BB.Builder where
+  decode = fmap BB.byteString . decode
+
+----
+
+instance Decode SBS.ShortByteString SBS.ShortByteString where
+  decode = fmap SBS.toShort . decode . SBS.fromShort
+
+instance Decode SBS.ShortByteString BS.ByteString where
+  decode = decode . SBS.fromShort
+
+instance Decode SBS.ShortByteString BS.L.ByteString where
+  decode = decode . SBS.fromShort
+
+instance Decode SBS.ShortByteString BB.Builder where
+  decode = decode . SBS.fromShort
+
+----
+
+instance Decode BB.Builder SBS.ShortByteString where
+  decode = decode . BB.toLazyByteString
+
+instance Decode BB.Builder BS.L.ByteString where
+  decode = decode . BB.toLazyByteString
+
+instance Decode BB.Builder BS.ByteString where
+  decode = decode . BB.toLazyByteString
+
+instance Decode BB.Builder BB.Builder where
+  decode = decode . BB.toLazyByteString
+
+----
+
+instance Decode T.Text BS.ByteString where
+  decode = decode . T.encodeUtf8
+
+instance Decode T.Text BS.L.ByteString where
+  decode = decode . T.encodeUtf8
+
+instance Decode T.Text SBS.ShortByteString where
+  decode = decode . T.encodeUtf8
+
+instance Decode T.Text BB.Builder where
+  decode = decode . T.encodeUtf8
+
+----
+
+instance Decode T.L.Text BS.ByteString where
+  decode = decode . T.L.encodeUtf8
+
+instance Decode T.L.Text BS.L.ByteString where
+  decode = decode . T.L.encodeUtf8
+
+instance Decode T.L.Text SBS.ShortByteString where
+  decode = decode . T.L.encodeUtf8
+
+instance Decode T.L.Text BB.Builder where
+  decode = decode . T.L.encodeUtf8
+
+----
+
+instance Decode TB.Builder BS.ByteString where
+  decode = decode . TB.toLazyText
+
+instance Decode TB.Builder BS.L.ByteString where
+  decode = decode . TB.toLazyText
+
+instance Decode TB.Builder SBS.ShortByteString where
+  decode = decode . TB.toLazyText
+
+instance Decode TB.Builder BB.Builder where
+  decode = decode . TB.toLazyText
diff --git a/src/Codec/Base64.hs b/src/Codec/Base64.hs
--- a/src/Codec/Base64.hs
+++ b/src/Codec/Base64.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP                   #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 
 -- | This module provides access to the \"base64\" binary-to-text encoding as defined by [RFC 4648](https://tools.ietf.org/html/rfc4648).
@@ -33,15 +32,9 @@
 import qualified Data.ByteString.Base64.Lazy  as B64.L
 
 import qualified Data.ByteString              as BS
-#if MIN_VERSION_bytestring(0,10,2)
 import qualified Data.ByteString.Builder      as BB
-#elif MIN_VERSION_bytestring(0,10,0)
-import qualified Data.ByteString.Lazy.Builder as BB
-#endif
 import qualified Data.ByteString.Lazy         as BS.L
-#if MIN_VERSION_bytestring(0,10,4)
 import qualified Data.ByteString.Short        as SBS
-#endif
 
 import qualified Data.Text                    as T (Text)
 import qualified Data.Text.Encoding           as T (decodeLatin1, encodeUtf8)
@@ -91,15 +84,11 @@
 instance Encode BS.L.ByteString BS.ByteString where
   encode = bsToStrict . encode
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Encode BS.L.ByteString BB.Builder where
   encode = BB.lazyByteString . encode
-#endif
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Encode BS.L.ByteString SBS.ShortByteString where
   encode = SBS.toShort . encode
-#endif
 
 instance Encode BS.L.ByteString T.Text where
   encode = T.decodeLatin1 . encode
@@ -119,15 +108,11 @@
 instance Encode BS.ByteString BS.L.ByteString where
   encode = bsFromStrict . encode
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Encode BS.ByteString BB.Builder where
   encode = BB.byteString . encode
-#endif
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Encode BS.ByteString SBS.ShortByteString where
   encode = SBS.toShort . encode
-#endif
 
 instance Encode BS.ByteString T.Text where
   encode = T.decodeLatin1 . encode
@@ -140,7 +125,6 @@
 
 ---- short BS  -> *
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Encode SBS.ShortByteString SBS.ShortByteString where
   encode = SBS.toShort . encode . SBS.fromShort
 
@@ -161,16 +145,12 @@
 
 instance Encode SBS.ShortByteString TB.Builder where
   encode = TB.fromText . encode
-#endif
 
 ---- BB  -> *
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Encode BB.Builder SBS.ShortByteString where
   encode = encode . BB.toLazyByteString
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Encode BB.Builder BB.Builder where
   encode = encode . BB.toLazyByteString
 
@@ -188,7 +168,6 @@
 
 instance Encode BB.Builder TB.Builder where
   encode = TB.fromLazyText . encode
-#endif
 
 ------------------------------------------------------------------------------
 
@@ -199,15 +178,11 @@
 instance Decode BS.ByteString BS.L.ByteString where
   decode = fmap bsFromStrict . decode
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode BS.ByteString SBS.ShortByteString where
   decode = fmap SBS.toShort . decode
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode BS.ByteString BB.Builder where
   decode = fmap BB.byteString . decode
-#endif
 
 ----
 
@@ -218,19 +193,14 @@
 instance Decode BS.L.ByteString BS.ByteString where
   decode = fmap bsToStrict . decode
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode BS.L.ByteString SBS.ShortByteString where
   decode = fmap SBS.toShort . decode
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode BS.L.ByteString BB.Builder where
   decode = fmap BB.byteString . decode
-#endif
 
 ----
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode SBS.ShortByteString SBS.ShortByteString where
   decode = fmap SBS.toShort . decode . SBS.fromShort
 
@@ -242,16 +212,12 @@
 
 instance Decode SBS.ShortByteString BB.Builder where
   decode = decode . SBS.fromShort
-#endif
 
 ----
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode BB.Builder SBS.ShortByteString where
   decode = decode . BB.toLazyByteString
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode BB.Builder BS.L.ByteString where
   decode = decode . BB.toLazyByteString
 
@@ -260,7 +226,6 @@
 
 instance Decode BB.Builder BB.Builder where
   decode = decode . BB.toLazyByteString
-#endif
 
 ----
 
@@ -270,15 +235,11 @@
 instance Decode T.Text BS.L.ByteString where
   decode = decode . T.encodeUtf8
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode T.Text SBS.ShortByteString where
   decode = decode . T.encodeUtf8
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode T.Text BB.Builder where
   decode = decode . T.encodeUtf8
-#endif
 
 ----
 
@@ -288,15 +249,11 @@
 instance Decode T.L.Text BS.L.ByteString where
   decode = decode . T.L.encodeUtf8
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode T.L.Text SBS.ShortByteString where
   decode = decode . T.L.encodeUtf8
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode T.L.Text BB.Builder where
   decode = decode . T.L.encodeUtf8
-#endif
 
 ----
 
@@ -306,12 +263,8 @@
 instance Decode TB.Builder BS.L.ByteString where
   decode = decode . TB.toLazyText
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode TB.Builder SBS.ShortByteString where
   decode = decode . TB.toLazyText
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode TB.Builder BB.Builder where
   decode = decode . TB.toLazyText
-#endif
diff --git a/src/Codec/Base64Url.hs b/src/Codec/Base64Url.hs
--- a/src/Codec/Base64Url.hs
+++ b/src/Codec/Base64Url.hs
@@ -1,7 +1,6 @@
-{-# LANGUAGE CPP                   #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 
--- | This module provides access to the \"base64url\" binary-to-text encoding as defined by [RFC 4648](https://tools.ietf.org/html/rfc4648).
+-- | This module provides access to the /padded/ \"base64url\" binary-to-text encoding as defined by [RFC 4648](https://tools.ietf.org/html/rfc4648). See also "Codec.Base64Url.Unpadded" for the /unpadded/ encoding variant.
 --
 -- This module is intended to be imported @qualified@, e.g.
 --
@@ -32,34 +31,27 @@
 import qualified Data.ByteString.Base64.URL      as B64
 import qualified Data.ByteString.Base64.URL.Lazy as B64.L
 
-import qualified Data.ByteString                 as BS
-#if MIN_VERSION_bytestring(0,10,2)
-import qualified Data.ByteString.Builder         as BB
-#elif MIN_VERSION_bytestring(0,10,0)
-import qualified Data.ByteString.Lazy.Builder    as BB
-#endif
-import qualified Data.ByteString.Lazy            as BS.L
-#if MIN_VERSION_bytestring(0,10,4)
-import qualified Data.ByteString.Short           as SBS
-#endif
+import qualified Data.ByteString              as BS
+import qualified Data.ByteString.Builder      as BB
+import qualified Data.ByteString.Lazy         as BS.L
+import qualified Data.ByteString.Short        as SBS
 
-import qualified Data.Text                       as T (Text)
-import qualified Data.Text.Encoding              as T (decodeLatin1, encodeUtf8)
-import qualified Data.Text.Lazy                  as T.L (Text, fromStrict)
-import qualified Data.Text.Lazy.Builder          as TB (Builder, fromLazyText,
-                                                        fromText, toLazyText)
-import qualified Data.Text.Lazy.Encoding         as T.L (decodeLatin1,
-                                                         encodeUtf8)
+import qualified Data.Text                    as T (Text)
+import qualified Data.Text.Encoding           as T (decodeLatin1, encodeUtf8)
+import qualified Data.Text.Lazy               as T.L (Text, fromStrict)
+import qualified Data.Text.Lazy.Builder       as TB (Builder, fromLazyText,
+                                                     fromText, toLazyText)
+import qualified Data.Text.Lazy.Encoding      as T.L (decodeLatin1, encodeUtf8)
 
 import           Internal
 
 -- primitives
 
 decodeBs2Bs :: BS.ByteString -> Either String BS.ByteString
-decodeBs2Bs = B64.decode
+decodeBs2Bs = B64.decodePadded
 
 decodeBsL2BsL :: BS.L.ByteString -> Either String BS.L.ByteString
-decodeBsL2BsL = B64.L.decode
+decodeBsL2BsL = B64.L.decodePadded
 
 encodeBs2Bs :: BS.ByteString -> BS.ByteString
 encodeBs2Bs = B64.encode
@@ -70,14 +62,14 @@
 ----------------------------------------------------------------------------
 -- exposed API
 
--- | Typeclass representing types for which a binary-to-text @base64url@ encoding is defined
+-- | Typeclass representing types for which a binary-to-text /padded/ @base64url@ encoding is defined
 class Encode bin txt where
-  -- | Encode binary data using @base64url@ text encoding
+  -- | Encode binary data using /padded/ @base64url@ text encoding
   encode :: bin -> txt
 
--- | Typeclass representing types for which a text-to-binary @base64url@ decoding is defined
+-- | Typeclass representing types for which a text-to-binary /padded/ @base64url@ decoding is defined
 class Decode txt bin where
-  -- | Decode binary data encoded textually as @base64url@
+  -- | Decode binary data encoded textually as /padded/ @base64url@
   decode :: txt -> Either String bin
 
 ----------------------------------------------------------------------------
@@ -92,15 +84,11 @@
 instance Encode BS.L.ByteString BS.ByteString where
   encode = bsToStrict . encode
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Encode BS.L.ByteString BB.Builder where
   encode = BB.lazyByteString . encode
-#endif
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Encode BS.L.ByteString SBS.ShortByteString where
   encode = SBS.toShort . encode
-#endif
 
 instance Encode BS.L.ByteString T.Text where
   encode = T.decodeLatin1 . encode
@@ -120,15 +108,11 @@
 instance Encode BS.ByteString BS.L.ByteString where
   encode = bsFromStrict . encode
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Encode BS.ByteString BB.Builder where
   encode = BB.byteString . encode
-#endif
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Encode BS.ByteString SBS.ShortByteString where
   encode = SBS.toShort . encode
-#endif
 
 instance Encode BS.ByteString T.Text where
   encode = T.decodeLatin1 . encode
@@ -141,7 +125,6 @@
 
 ---- short BS  -> *
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Encode SBS.ShortByteString SBS.ShortByteString where
   encode = SBS.toShort . encode . SBS.fromShort
 
@@ -162,16 +145,12 @@
 
 instance Encode SBS.ShortByteString TB.Builder where
   encode = TB.fromText . encode
-#endif
 
 ---- BB  -> *
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Encode BB.Builder SBS.ShortByteString where
   encode = encode . BB.toLazyByteString
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Encode BB.Builder BB.Builder where
   encode = encode . BB.toLazyByteString
 
@@ -189,7 +168,6 @@
 
 instance Encode BB.Builder TB.Builder where
   encode = TB.fromLazyText . encode
-#endif
 
 ------------------------------------------------------------------------------
 
@@ -200,15 +178,11 @@
 instance Decode BS.ByteString BS.L.ByteString where
   decode = fmap bsFromStrict . decode
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode BS.ByteString SBS.ShortByteString where
   decode = fmap SBS.toShort . decode
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode BS.ByteString BB.Builder where
   decode = fmap BB.byteString . decode
-#endif
 
 ----
 
@@ -219,19 +193,14 @@
 instance Decode BS.L.ByteString BS.ByteString where
   decode = fmap bsToStrict . decode
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode BS.L.ByteString SBS.ShortByteString where
   decode = fmap SBS.toShort . decode
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode BS.L.ByteString BB.Builder where
   decode = fmap BB.byteString . decode
-#endif
 
 ----
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode SBS.ShortByteString SBS.ShortByteString where
   decode = fmap SBS.toShort . decode . SBS.fromShort
 
@@ -243,16 +212,12 @@
 
 instance Decode SBS.ShortByteString BB.Builder where
   decode = decode . SBS.fromShort
-#endif
 
 ----
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode BB.Builder SBS.ShortByteString where
   decode = decode . BB.toLazyByteString
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode BB.Builder BS.L.ByteString where
   decode = decode . BB.toLazyByteString
 
@@ -261,7 +226,6 @@
 
 instance Decode BB.Builder BB.Builder where
   decode = decode . BB.toLazyByteString
-#endif
 
 ----
 
@@ -271,15 +235,11 @@
 instance Decode T.Text BS.L.ByteString where
   decode = decode . T.encodeUtf8
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode T.Text SBS.ShortByteString where
   decode = decode . T.encodeUtf8
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode T.Text BB.Builder where
   decode = decode . T.encodeUtf8
-#endif
 
 ----
 
@@ -289,15 +249,11 @@
 instance Decode T.L.Text BS.L.ByteString where
   decode = decode . T.L.encodeUtf8
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode T.L.Text SBS.ShortByteString where
   decode = decode . T.L.encodeUtf8
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode T.L.Text BB.Builder where
   decode = decode . T.L.encodeUtf8
-#endif
 
 ----
 
@@ -307,12 +263,8 @@
 instance Decode TB.Builder BS.L.ByteString where
   decode = decode . TB.toLazyText
 
-#if MIN_VERSION_bytestring(0,10,4)
 instance Decode TB.Builder SBS.ShortByteString where
   decode = decode . TB.toLazyText
-#endif
 
-#if MIN_VERSION_bytestring(0,10,0)
 instance Decode TB.Builder BB.Builder where
   decode = decode . TB.toLazyText
-#endif
diff --git a/src/Codec/Base64Url/Unpadded.hs b/src/Codec/Base64Url/Unpadded.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Base64Url/Unpadded.hs
@@ -0,0 +1,278 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+-- | This module provides access to the /unpadded/ \"base64url\" binary-to-text encoding as defined by [RFC 4648](https://tools.ietf.org/html/rfc4648). See also "Codec.Base64Url" for the /padded/ encoding variant.
+--
+-- This module is intended to be imported @qualified@, e.g.
+--
+-- > import qualified Codec.Base64Url.Unpadded as B64
+--
+-- If you want to explictly specify which 'Encode' and 'Decode' typeclass instance is used, you can use plain Haskell2010 type-signature annotations, e.g.
+--
+-- >>> (B64.encode :: ByteString -> Text) "\x00\x00"
+-- "AAA"
+--
+-- >>> (B64.decode :: Text -> Either String ShortByteString) "NDoyMA"
+-- Right "4:20"
+--
+-- Alternatively, starting with GHC 8.0.1, you can also use the [TypeApplications language extension](https://downloads.haskell.org/~ghc/8.4.1/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications):
+--
+-- >>> B64.encode @ShortByteString @Text "\xFF\239"
+-- "_-8"
+--
+-- >>> B64.decode @Text @ShortByteString "_-8"
+-- Right "\255\239"
+--
+-- @since 0.2.0.0
+module Codec.Base64Url.Unpadded
+    ( Encode(encode)
+    , Decode(decode)
+    ) where
+
+import qualified Data.ByteString.Base64.URL      as B64
+import qualified Data.ByteString.Base64.URL.Lazy as B64.L
+
+import qualified Data.ByteString              as BS
+import qualified Data.ByteString.Builder      as BB
+import qualified Data.ByteString.Lazy         as BS.L
+import qualified Data.ByteString.Short        as SBS
+
+import qualified Data.Text                    as T (Text)
+import qualified Data.Text.Encoding           as T (decodeLatin1, encodeUtf8)
+import qualified Data.Text.Lazy               as T.L (Text, fromStrict)
+import qualified Data.Text.Lazy.Builder       as TB (Builder, fromLazyText,
+                                                     fromText, toLazyText)
+import qualified Data.Text.Lazy.Encoding      as T.L (decodeLatin1, encodeUtf8)
+
+import           Internal
+
+-- primitives
+
+decodeBs2Bs :: BS.ByteString -> Either String BS.ByteString
+decodeBs2Bs = B64.decodeUnpadded
+
+decodeBsL2BsL :: BS.L.ByteString -> Either String BS.L.ByteString
+decodeBsL2BsL = B64.L.decodeUnpadded
+
+encodeBs2Bs :: BS.ByteString -> BS.ByteString
+encodeBs2Bs = B64.encodeUnpadded
+
+encodeBsL2BsL :: BS.L.ByteString -> BS.L.ByteString
+encodeBsL2BsL = B64.L.encodeUnpadded
+
+----------------------------------------------------------------------------
+-- exposed API
+
+-- | Typeclass representing types for which a binary-to-text /unpadded/ @base64url@ encoding is defined
+--
+-- @since 0.2.0.0
+class Encode bin txt where
+  -- | Encode binary data using /unpadded/ @base64url@ text encoding
+  --
+  -- @since 0.2.0.0
+  encode :: bin -> txt
+
+-- | Typeclass representing types for which a text-to-binary /unpadded/ @base64url@ decoding is defined
+--
+-- @since 0.2.0.0
+class Decode txt bin where
+  -- | Decode binary data encoded textually as /unpadded/ @base64url@
+  --
+  -- @since 0.2.0.0
+  decode :: txt -> Either String bin
+
+----------------------------------------------------------------------------
+-- instance matrix
+
+---- lazy BS -> *
+
+-- PRIMITIVE
+instance Encode BS.L.ByteString BS.L.ByteString where
+  encode = encodeBsL2BsL
+
+instance Encode BS.L.ByteString BS.ByteString where
+  encode = bsToStrict . encode
+
+instance Encode BS.L.ByteString BB.Builder where
+  encode = BB.lazyByteString . encode
+
+instance Encode BS.L.ByteString SBS.ShortByteString where
+  encode = SBS.toShort . encode
+
+instance Encode BS.L.ByteString T.Text where
+  encode = T.decodeLatin1 . encode
+
+instance Encode BS.L.ByteString T.L.Text where
+  encode = T.L.decodeLatin1 . encode
+
+instance Encode BS.L.ByteString TB.Builder where
+  encode = TB.fromLazyText . encode
+
+---- strict BS  -> *
+
+-- PRIMITIVE
+instance Encode BS.ByteString BS.ByteString where
+  encode = encodeBs2Bs
+
+instance Encode BS.ByteString BS.L.ByteString where
+  encode = bsFromStrict . encode
+
+instance Encode BS.ByteString BB.Builder where
+  encode = BB.byteString . encode
+
+instance Encode BS.ByteString SBS.ShortByteString where
+  encode = SBS.toShort . encode
+
+instance Encode BS.ByteString T.Text where
+  encode = T.decodeLatin1 . encode
+
+instance Encode BS.ByteString T.L.Text where
+  encode = T.L.fromStrict . T.decodeLatin1 . encode
+
+instance Encode BS.ByteString TB.Builder where
+  encode = TB.fromText . encode
+
+---- short BS  -> *
+
+instance Encode SBS.ShortByteString SBS.ShortByteString where
+  encode = SBS.toShort . encode . SBS.fromShort
+
+instance Encode SBS.ShortByteString BS.ByteString where
+  encode = encode . SBS.fromShort
+
+instance Encode SBS.ShortByteString BS.L.ByteString where
+  encode = encode . SBS.fromShort
+
+instance Encode SBS.ShortByteString BB.Builder where
+  encode = encode . SBS.fromShort
+
+instance Encode SBS.ShortByteString T.Text where
+  encode = T.decodeLatin1 . encode
+
+instance Encode SBS.ShortByteString T.L.Text where
+  encode = T.L.fromStrict . T.decodeLatin1 . encode
+
+instance Encode SBS.ShortByteString TB.Builder where
+  encode = TB.fromText . encode
+
+---- BB  -> *
+
+instance Encode BB.Builder SBS.ShortByteString where
+  encode = encode . BB.toLazyByteString
+
+instance Encode BB.Builder BB.Builder where
+  encode = encode . BB.toLazyByteString
+
+instance Encode BB.Builder BS.ByteString where
+  encode = encode . BB.toLazyByteString
+
+instance Encode BB.Builder BS.L.ByteString where
+  encode = encode . BB.toLazyByteString
+
+instance Encode BB.Builder T.Text where
+  encode = T.decodeLatin1 . encode
+
+instance Encode BB.Builder T.L.Text where
+  encode = T.L.decodeLatin1 . encode
+
+instance Encode BB.Builder TB.Builder where
+  encode = TB.fromLazyText . encode
+
+------------------------------------------------------------------------------
+
+-- PRIMITIVE
+instance Decode BS.ByteString BS.ByteString where
+  decode = decodeBs2Bs
+
+instance Decode BS.ByteString BS.L.ByteString where
+  decode = fmap bsFromStrict . decode
+
+instance Decode BS.ByteString SBS.ShortByteString where
+  decode = fmap SBS.toShort . decode
+
+instance Decode BS.ByteString BB.Builder where
+  decode = fmap BB.byteString . decode
+
+----
+
+-- PRIMITIVE
+instance Decode BS.L.ByteString BS.L.ByteString where
+  decode = decodeBsL2BsL
+
+instance Decode BS.L.ByteString BS.ByteString where
+  decode = fmap bsToStrict . decode
+
+instance Decode BS.L.ByteString SBS.ShortByteString where
+  decode = fmap SBS.toShort . decode
+
+instance Decode BS.L.ByteString BB.Builder where
+  decode = fmap BB.byteString . decode
+
+----
+
+instance Decode SBS.ShortByteString SBS.ShortByteString where
+  decode = fmap SBS.toShort . decode . SBS.fromShort
+
+instance Decode SBS.ShortByteString BS.ByteString where
+  decode = decode . SBS.fromShort
+
+instance Decode SBS.ShortByteString BS.L.ByteString where
+  decode = decode . SBS.fromShort
+
+instance Decode SBS.ShortByteString BB.Builder where
+  decode = decode . SBS.fromShort
+
+----
+
+instance Decode BB.Builder SBS.ShortByteString where
+  decode = decode . BB.toLazyByteString
+
+instance Decode BB.Builder BS.L.ByteString where
+  decode = decode . BB.toLazyByteString
+
+instance Decode BB.Builder BS.ByteString where
+  decode = decode . BB.toLazyByteString
+
+instance Decode BB.Builder BB.Builder where
+  decode = decode . BB.toLazyByteString
+
+----
+
+instance Decode T.Text BS.ByteString where
+  decode = decode . T.encodeUtf8
+
+instance Decode T.Text BS.L.ByteString where
+  decode = decode . T.encodeUtf8
+
+instance Decode T.Text SBS.ShortByteString where
+  decode = decode . T.encodeUtf8
+
+instance Decode T.Text BB.Builder where
+  decode = decode . T.encodeUtf8
+
+----
+
+instance Decode T.L.Text BS.ByteString where
+  decode = decode . T.L.encodeUtf8
+
+instance Decode T.L.Text BS.L.ByteString where
+  decode = decode . T.L.encodeUtf8
+
+instance Decode T.L.Text SBS.ShortByteString where
+  decode = decode . T.L.encodeUtf8
+
+instance Decode T.L.Text BB.Builder where
+  decode = decode . T.L.encodeUtf8
+
+----
+
+instance Decode TB.Builder BS.ByteString where
+  decode = decode . TB.toLazyText
+
+instance Decode TB.Builder BS.L.ByteString where
+  decode = decode . TB.toLazyText
+
+instance Decode TB.Builder SBS.ShortByteString where
+  decode = decode . TB.toLazyText
+
+instance Decode TB.Builder BB.Builder where
+  decode = decode . TB.toLazyText
diff --git a/src/Internal.hs b/src/Internal.hs
--- a/src/Internal.hs
+++ b/src/Internal.hs
@@ -4,14 +4,25 @@
     ( bsToStrict
     , bsFromStrict
     , (<$>)
+    , pure
+    , Data.Monoid.mempty
+    , (<>)
     ) where
 
 #if !MIN_VERSION_base(4,8,0)
 import           Control.Applicative
 #endif
 
+import           Data.Monoid
 import qualified Data.ByteString      as BS
 import qualified Data.ByteString.Lazy as BS.L
+
+#if !MIN_VERSION_base(4,5,0)
+(<>) :: Monoid a => a -> a -> a
+(<>) = mappend
+
+infixr 6 <>
+#endif
 
 {-# INLINE bsToStrict #-}
 bsToStrict :: BS.L.ByteString -> BS.ByteString
