diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,20 @@
+## 0.21.0
+
++ `Data.ByteArray.Encoding`: replaced custom Base16/Base32/Base64 encode/decode
+  with `base16`, `base32`, and `base64` library calls. Input is converted to
+  `ByteString` via `B.convert`, the library function is applied, and the result
+  is converted back. `Base64OpenBSD` retains its custom implementation (no
+  library equivalent exists).
++ `Data.ByteArray.Bytes`: replaced low-level `GHC.Prim` `MutableByteArray#`
+  implementation with `newtype Bytes = Bytes ByteString`. Both use GHC's pinned
+  allocator; `ByteString` already implements `ByteArrayAccess` / `ByteArray`.
++ `Data.Memory.Hash.FNV`: replaced `readWord8OffAddr#` (GHC.Prim) with
+  `Foreign.Storable.peekByteOff` — portable and equivalent.
++ `Data.Memory.Internal.CompatPrim64`: deleted (was entirely unreferenced).
++ New dependencies: `base16 >=1.0 && <2`, `base32 >=0.4 && <1`,
+  `base64 >=1.0 && <2`, `text >=1.0 && <3`.
++ Net reduction: ~350 lines removed.
+
 ## 0.20.1
 + Remove `WITH_BYTESTRING_SUPPORT` CPP flag. `ByteString` instances for
   `ByteArrayAccess` and `ByteArray` are now always compiled in, since
diff --git a/Data/ByteArray.hs b/Data/ByteArray.hs
--- a/Data/ByteArray.hs
+++ b/Data/ByteArray.hs
@@ -9,9 +9,6 @@
 --
 -- This module should be imported qualified.
 --
-{-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE MagicHash #-}
-{-# LANGUAGE UnboxedTuples #-}
 {-# LANGUAGE NoImplicitPrelude #-}
 module Data.ByteArray
     (
diff --git a/Data/ByteArray/Bytes.hs b/Data/ByteArray/Bytes.hs
--- a/Data/ByteArray/Bytes.hs
+++ b/Data/ByteArray/Bytes.hs
@@ -7,192 +7,50 @@
 --
 -- Simple and efficient byte array types
 --
-{-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE MagicHash #-}
-{-# LANGUAGE UnboxedTuples #-}
-{-# LANGUAGE DeriveDataTypeable #-}
 module Data.ByteArray.Bytes
     ( Bytes
     ) where
 
-import           Control.DeepSeq(NFData(..))
-import           GHC.Exts (unsafeCoerce#)
-import           GHC.Word
-import           GHC.Char (chr)
-import           GHC.Types
-import           GHC.Prim
-import           GHC.Ptr
-import           Data.Semigroup
-import           Data.Foldable (toList)
-import           Data.Memory.PtrMethods
-import           Data.Memory.Internal.Imports
-import           Data.Memory.Internal.CompatPrim
-import           Data.Memory.Internal.Compat      (unsafeDoIO)
+import           Control.DeepSeq          (NFData(..))
 import           Data.ByteArray.Types
+import           Data.Char                (chr)
+import           Data.Foldable            (toList)
+import           Data.Semigroup
 import           Data.Typeable
-
+import qualified Data.ByteString          as BS
+import qualified Data.ByteString.Internal as BSI
+import           Foreign.ForeignPtr       (withForeignPtr)
+import           Foreign.Ptr              (castPtr)
 
 -- | Simplest Byte Array
-data Bytes = Bytes (MutableByteArray# RealWorld)
+newtype Bytes = Bytes BS.ByteString
   deriving (Typeable)
 
 instance NFData Bytes where
     rnf b = b `seq` ()
 
 instance Show Bytes where
-    showsPrec p b r = showsPrec p (bytesUnpackChars b []) r
+    showsPrec p (Bytes b) = showsPrec p (map (chr . fromIntegral) (BS.unpack b))
+
 instance Eq Bytes where
-    (==) = bytesEq
+    Bytes a == Bytes b = a == b
+
 instance Ord Bytes where
-    compare = bytesCompare
+    compare (Bytes a) (Bytes b) = compare a b
+
 instance Semigroup Bytes where
-    b1 <> b2      = unsafeDoIO $ bytesAppend b1 b2
-    sconcat       = unsafeDoIO . bytesConcat . toList
+    Bytes a <> Bytes b = Bytes (a <> b)
+    sconcat bs = Bytes $ BS.concat [b | Bytes b <- toList bs]
+
 instance Monoid Bytes where
-    mempty        = unsafeDoIO (newBytes 0)
+    mempty = Bytes BS.empty
 
 instance ByteArrayAccess Bytes where
-    length        = bytesLength
-    withByteArray = withBytes
-instance ByteArray Bytes where
-    allocRet = bytesAllocRet
-
-------------------------------------------------------------------------
-newBytes :: Int -> IO Bytes
-newBytes (I# sz)
-    | booleanPrim (sz <# 0#) = error "Bytes: size must be >= 0"
-    | otherwise              = IO $ \s ->
-        case newAlignedPinnedByteArray# sz 8# s of
-            (# s', mbarr #) -> (# s', Bytes mbarr #)
-
-touchBytes :: Bytes -> IO ()
-touchBytes (Bytes mba) = IO $ \s -> case touch# mba s of s' -> (# s', () #)
-{-# INLINE touchBytes #-}
-
-sizeofBytes :: Bytes -> Int
-sizeofBytes (Bytes mba) = I# (sizeofMutableByteArray# mba)
-{-# INLINE sizeofBytes #-}
-
-withPtr :: Bytes -> (Ptr p -> IO a) -> IO a
-withPtr b@(Bytes mba) f = do
-    a <- f (Ptr (byteArrayContents# (unsafeCoerce# mba)))
-    touchBytes b
-    return a
-------------------------------------------------------------------------
-
-bytesAlloc :: Int -> (Ptr p -> IO ()) -> IO Bytes
-bytesAlloc sz f = do
-    ba <- newBytes sz
-    withPtr ba f
-    return ba
-
-bytesConcat :: [Bytes] -> IO Bytes
-bytesConcat l = bytesAlloc retLen (copy l)
-  where
-    !retLen = sum $ map bytesLength l
-
-    copy []     _   = return ()
-    copy (x:xs) dst = do
-        withPtr x $ \src -> memCopy dst src chunkLen
-        copy xs (dst `plusPtr` chunkLen)
-      where
-        !chunkLen = bytesLength x
-
-bytesAppend :: Bytes -> Bytes -> IO Bytes
-bytesAppend b1 b2 = bytesAlloc retLen $ \dst -> do
-    withPtr b1 $ \s1 -> memCopy dst                  s1 len1
-    withPtr b2 $ \s2 -> memCopy (dst `plusPtr` len1) s2 len2
-  where
-    !len1   = bytesLength b1
-    !len2   = bytesLength b2
-    !retLen = len1 + len2
-
-bytesAllocRet :: Int -> (Ptr p -> IO a) -> IO (a, Bytes)
-bytesAllocRet sz f = do
-    ba <- newBytes sz
-    r <- withPtr ba f
-    return (r, ba)
-
-bytesLength :: Bytes -> Int
-bytesLength = sizeofBytes
-{-# LANGUAGE bytesLength #-}
-
-withBytes :: Bytes -> (Ptr p -> IO a) -> IO a
-withBytes = withPtr
-
-bytesEq :: Bytes -> Bytes -> Bool
-bytesEq b1@(Bytes m1) b2@(Bytes m2)
-    | l1 /= l2  = False
-    | otherwise = unsafeDoIO $ IO $ \s -> loop 0# s
-  where
-    !l1@(I# len) = bytesLength b1
-    !l2          = bytesLength b2
-
-    loop i s
-        | booleanPrim (i ==# len) = (# s, True #)
-        | otherwise               =
-            case readWord8Array# m1 i s of
-                (# s', e1 #) -> case readWord8Array# m2 i s' of
-                    (# s'', e2 #) ->
-                        if (W8# e1) == (W8# e2)
-                            then loop (i +# 1#) s''
-                            else (# s'', False #)
-    {-# INLINE loop #-}
-
-bytesCompare :: Bytes -> Bytes -> Ordering
-bytesCompare b1@(Bytes m1) b2@(Bytes m2) = unsafeDoIO $ loop 0
-  where
-    !l1  = bytesLength b1
-    !l2  = bytesLength b2
-    !len = min l1 l2
-
-    loop !i
-        | i == len =
-            if l1 == l2
-                then pure EQ
-                else if l1 > l2 then pure GT
-                                else pure LT
-        | otherwise               = do
-            e1 <- read8 m1 i
-            e2 <- read8 m2 i
-            if e1 == e2
-                then loop (i+1)
-                else if e1 < e2 then pure LT
-                                else pure GT
-
-    read8 m (I# i) = IO $ \s -> case readWord8Array# m i s of
-                                    (# s2, e #) -> (# s2, W8# e #)
-
-bytesUnpackChars :: Bytes -> String -> String
-bytesUnpackChars (Bytes mba) xs = chunkLoop 0#
-  where
-    !len = sizeofMutableByteArray# mba
-    -- chunk 64 bytes at a time
-    chunkLoop :: Int# -> [Char]
-    chunkLoop idx
-        | booleanPrim (len ==# idx) = []
-        | booleanPrim ((len -# idx) ># 63#) =
-            bytesLoop idx 64# (chunkLoop (idx +# 64#))
-        | otherwise =
-            bytesLoop idx (len -# idx) xs
-
-    bytesLoop idx chunkLenM1 paramAcc = unsafeDoIO $
-        loop (idx +# chunkLenM1 -# 1#) paramAcc
-      where loop i acc
-                | booleanPrim (i ==# idx) = do
-                    c <- rChar i
-                    return (c : acc)
-                | otherwise = do
-                    c <- rChar i
-                    loop (i -# 1#) (c : acc)
-
-    rChar :: Int# -> IO Char
-    rChar idx = IO $ \s ->
-        case readWord8Array# mba idx s of
-            (# s2, w #) -> (# s2, chr (fromIntegral (W8# w)) #)
+    length (Bytes b) = BS.length b
+    withByteArray (Bytes b) = withByteArray b
 
-{-
-bytesShowHex :: Bytes -> String
-bytesShowHex b = showHexadecimal (withPtr b) (bytesLength b)
-{-# NOINLINE bytesShowHex #-}
--}
+instance ByteArray Bytes where
+    allocRet n f = do
+        fptr <- BSI.mallocByteString n
+        r    <- withForeignPtr fptr (f . castPtr)
+        return (r, Bytes (BSI.PS fptr 0 n))
diff --git a/Data/ByteArray/Encoding.hs b/Data/ByteArray/Encoding.hs
--- a/Data/ByteArray/Encoding.hs
+++ b/Data/ByteArray/Encoding.hs
@@ -16,10 +16,17 @@
 import           Data.ByteArray.Types
 import qualified Data.ByteArray.Types        as B
 import qualified Data.ByteArray.Methods      as B
-import           Data.Memory.Internal.Compat
-import           Data.Memory.Encoding.Base16
-import           Data.Memory.Encoding.Base32
-import           Data.Memory.Encoding.Base64
+import qualified Data.ByteString             as BS
+import qualified Data.ByteString.Base16      as B16
+import qualified Data.ByteString.Base32      as B32
+import qualified Data.ByteString.Base64      as B64
+import qualified Data.ByteString.Base64.URL  as B64URL
+import           Data.Base16.Types           (extractBase16)
+import           Data.Base64.Types           (extractBase64)
+import qualified Data.Text                   as T
+import           Data.Memory.Encoding.Base64 (toBase64OpenBSD, fromBase64OpenBSD,
+                                              unBase64LengthUnpadded)
+import           Data.Memory.Internal.Compat (unsafeDoIO)
 
 -- $setup
 -- >>> :set -XOverloadedStrings
@@ -74,27 +81,20 @@
 -- "Zm9vYmFy"
 --
 convertToBase :: (ByteArrayAccess bin, ByteArray bout) => Base -> bin -> bout
-convertToBase base b = case base of
-    Base16 -> doConvert (binLength * 2) toHexadecimal
-    Base32 -> let (q,r)  = binLength `divMod` 5
-                  outLen = 8 * (if r == 0 then q else q + 1)
-               in doConvert outLen toBase32
-    Base64 -> doConvert base64Length toBase64
-    -- Base64URL         -> doConvert base64Length (toBase64URL True)
-    Base64URLUnpadded -> doConvert base64UnpaddedLength (toBase64URL False)
-    Base64OpenBSD     -> doConvert base64UnpaddedLength toBase64OpenBSD
-  where
-    binLength = B.length b
-
-    base64Length = let (q,r) = binLength `divMod` 3
-                    in 4 * (if r == 0 then q else q+1)
-
-    base64UnpaddedLength = let (q,r) = binLength `divMod` 3
-                            in 4 * q + (if r == 0 then 0 else r+1)
-    doConvert l f =
-        B.unsafeCreate l $ \bout ->
-        B.withByteArray b     $ \bin  ->
-            f bout bin binLength
+convertToBase base b =
+    let bs = B.convert b :: BS.ByteString
+    in case base of
+        Base16            -> B.convert $ extractBase16 (B16.encodeBase16' bs)
+        Base32            -> B.convert $ B32.encodeBase32' bs
+        Base64            -> B.convert $ extractBase64 (B64.encodeBase64' bs)
+        Base64URLUnpadded -> B.convert $ extractBase64 (B64URL.encodeBase64Unpadded' bs)
+        Base64OpenBSD     ->
+            let binLength = B.length b
+                (q, r)   = binLength `divMod` 3
+                outLen   = 4 * q + (if r == 0 then 0 else r + 1)
+            in B.unsafeCreate outLen $ \bout ->
+               withByteArray b $ \bin ->
+                   toBase64OpenBSD bout bin binLength
 
 -- | Try to decode some bytes from the equivalent representation in a specific 'Base'.
 --
@@ -108,55 +108,27 @@
 -- Trying to decode invalid data will return an error string:
 --
 -- >>> convertFromBase Base64 ("!!!" :: ByteString) :: Either String ByteString
--- Left "base64: input: invalid length"
+-- Left "Base64-encoded bytestring requires padding"
 --
 convertFromBase :: (ByteArrayAccess bin, ByteArray bout) => Base -> bin -> Either String bout
-convertFromBase Base16 b
-    | odd (B.length b) = Left "base16: input: invalid length"
-    | otherwise        = unsafeDoIO $ do
-        (ret, out) <-
-            B.allocRet (B.length b `div` 2) $ \bout ->
-            B.withByteArray b               $ \bin  ->
-                fromHexadecimal bout bin (B.length b)
-        case ret of
-            Nothing  -> return $ Right out
-            Just ofs -> return $ Left ("base16: input: invalid encoding at offset: " ++ show ofs)
-convertFromBase Base32 b = unsafeDoIO $
-    withByteArray b $ \bin -> do
-        mDstLen <- unBase32Length bin (B.length b)
-        case mDstLen of
-            Nothing     -> return $ Left "base32: input: invalid length"
-            Just dstLen -> do
-                (ret, out) <- B.allocRet dstLen $ \bout -> fromBase32 bout bin (B.length b)
-                case ret of
-                    Nothing  -> return $ Right out
-                    Just ofs -> return $ Left ("base32: input: invalid encoding at offset: " ++ show ofs)
-convertFromBase Base64 b = unsafeDoIO $
-    withByteArray b $ \bin -> do
-        mDstLen <- unBase64Length bin (B.length b)
-        case mDstLen of
-            Nothing     -> return $ Left "base64: input: invalid length"
-            Just dstLen -> do
-                (ret, out) <- B.allocRet dstLen $ \bout -> fromBase64 bout bin (B.length b)
-                case ret of
-                    Nothing  -> return $ Right out
-                    Just ofs -> return $ Left ("base64: input: invalid encoding at offset: " ++ show ofs)
-convertFromBase Base64URLUnpadded b = unsafeDoIO $
-    withByteArray b $ \bin ->
-        case unBase64LengthUnpadded (B.length b) of
-            Nothing     -> return $ Left "base64URL unpadded: input: invalid length"
-            Just dstLen -> do
-                (ret, out) <- B.allocRet dstLen $ \bout -> fromBase64URLUnpadded bout bin (B.length b)
-                case ret of
-                    Nothing  -> return $ Right out
-                    Just ofs -> return $ Left ("base64URL unpadded: input: invalid encoding at offset: " ++ show ofs)
-convertFromBase Base64OpenBSD b = unsafeDoIO $
-    withByteArray b $ \bin ->
-        case unBase64LengthUnpadded (B.length b) of
-            Nothing     -> return $ Left "base64 unpadded: input: invalid length"
-            Just dstLen -> do
-                (ret, out) <- B.allocRet dstLen $ \bout -> fromBase64OpenBSD bout bin (B.length b)
-                case ret of
-                    Nothing  -> return $ Right out
-                    Just ofs -> return $ Left ("base64 unpadded: input: invalid encoding at offset: " ++ show ofs)
+convertFromBase base b =
+    let bs  = B.convert b :: BS.ByteString
+        run = fmap B.convert . mapLeft T.unpack
+    in case base of
+        Base16            -> run $ B16.decodeBase16Untyped bs
+        Base32            -> run $ B32.decodeBase32 bs
+        Base64            -> run $ B64.decodeBase64Untyped bs
+        Base64URLUnpadded -> run $ B64URL.decodeBase64UnpaddedUntyped bs
+        Base64OpenBSD     -> unsafeDoIO $
+            withByteArray b $ \bin ->
+                case unBase64LengthUnpadded (B.length b) of
+                    Nothing     -> return $ Left "base64 unpadded: input: invalid length"
+                    Just dstLen -> do
+                        (ret, out) <- B.allocRet dstLen $ \bout -> fromBase64OpenBSD bout bin (B.length b)
+                        return $ case ret of
+                            Nothing  -> Right out
+                            Just ofs -> Left ("base64 unpadded: input: invalid encoding at offset: " ++ show ofs)
 
+mapLeft :: (a -> b) -> Either a c -> Either b c
+mapLeft f (Left a)  = Left (f a)
+mapLeft _ (Right c) = Right c
diff --git a/Data/ByteArray/ScrubbedBytes.hs b/Data/ByteArray/ScrubbedBytes.hs
--- a/Data/ByteArray/ScrubbedBytes.hs
+++ b/Data/ByteArray/ScrubbedBytes.hs
@@ -33,7 +33,6 @@
 import           Data.Memory.PtrMethods
 import           Data.Memory.Internal.CompatPrim
 import           Data.Memory.Internal.Compat     (unsafeDoIO)
-import           Data.Memory.Internal.Imports
 import           Data.ByteArray.Types
 import           Foreign.Storable
 
diff --git a/Data/ByteArray/Types.hs b/Data/ByteArray/Types.hs
--- a/Data/ByteArray/Types.hs
+++ b/Data/ByteArray/Types.hs
@@ -23,10 +23,6 @@
 
 import           Data.Memory.PtrMethods (memCopy)
 
-
-import           Data.Proxy (Proxy(..))
-import           Data.Word (Word8)
-
 import Prelude hiding (length)
 
 -- | Class to Access size properties and data of a ByteArray
diff --git a/Data/Memory/Hash/FNV.hs b/Data/Memory/Hash/FNV.hs
--- a/Data/Memory/Hash/FNV.hs
+++ b/Data/Memory/Hash/FNV.hs
@@ -9,8 +9,6 @@
 -- <http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function>
 --
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE MagicHash                  #-}
-{-# LANGUAGE UnboxedTuples              #-}
 {-# LANGUAGE BangPatterns               #-}
 module Data.Memory.Hash.FNV
     (
@@ -24,14 +22,11 @@
     , fnv1a_64
     ) where
 
-import           Data.Bits (xor)
-import           Data.Memory.Internal.Compat ()
-import           Data.Memory.Internal.Imports
-import           GHC.Word
-import           GHC.Prim hiding (Word64#, Int64#)
-import           GHC.Types
-import           GHC.Ptr
-import           Control.DeepSeq(NFData(..))
+import           Data.Bits               (xor)
+import           Data.Word               (Word8, Word32, Word64)
+import           Foreign.Ptr             (Ptr)
+import           Foreign.Storable        (peekByteOff)
+import           Control.DeepSeq         (NFData(..))
 
 -- | FNV1(a) hash (32 bit variants)
 newtype FnvHash32 = FnvHash32 Word32
@@ -59,48 +54,32 @@
 
 -- | compute FNV1 (32 bit variant) of a raw piece of memory
 fnv1 :: Ptr Word8 -> Int -> IO FnvHash32
-fnv1 (Ptr addr) n = loop (FnvHash32 0x811c9dc5) 0
-  where 
-        loop :: FnvHash32 -> Int -> IO FnvHash32
+fnv1 ptr n = loop (FnvHash32 0x811c9dc5) 0
+  where
         loop !acc !i
-            | i == n    = pure $ acc
-            | otherwise = do
-                v <- read8 addr i
-                loop (fnv1_32_Mix8 v acc) (i + 1)
+            | i == n    = pure acc
+            | otherwise = peekByteOff ptr i >>= \v -> loop (fnv1_32_Mix8 v acc) (i + 1)
 
 -- | compute FNV1a (32 bit variant) of a raw piece of memory
 fnv1a :: Ptr Word8 -> Int -> IO FnvHash32
-fnv1a (Ptr addr) n = loop (FnvHash32 0x811c9dc5) 0
-  where 
-        loop :: FnvHash32 -> Int -> IO FnvHash32
+fnv1a ptr n = loop (FnvHash32 0x811c9dc5) 0
+  where
         loop !acc !i
-            | i == n    = pure $ acc
-            | otherwise = do
-                v <- read8 addr i
-                loop (fnv1a_32_Mix8 v acc) (i + 1)
+            | i == n    = pure acc
+            | otherwise = peekByteOff ptr i >>= \v -> loop (fnv1a_32_Mix8 v acc) (i + 1)
 
 -- | compute FNV1 (64 bit variant) of a raw piece of memory
 fnv1_64 :: Ptr Word8 -> Int -> IO FnvHash64
-fnv1_64 (Ptr addr) n = loop (FnvHash64 0xcbf29ce484222325) 0
-  where 
-        loop :: FnvHash64 -> Int -> IO FnvHash64
+fnv1_64 ptr n = loop (FnvHash64 0xcbf29ce484222325) 0
+  where
         loop !acc !i
-            | i == n    = pure $ acc
-            | otherwise = do
-                v <- read8 addr i
-                loop (fnv1_64_Mix8 v acc) (i + 1)
+            | i == n    = pure acc
+            | otherwise = peekByteOff ptr i >>= \v -> loop (fnv1_64_Mix8 v acc) (i + 1)
 
 -- | compute FNV1a (64 bit variant) of a raw piece of memory
 fnv1a_64 :: Ptr Word8 -> Int -> IO FnvHash64
-fnv1a_64 (Ptr addr) n = loop (FnvHash64 0xcbf29ce484222325) 0
-  where 
-        loop :: FnvHash64 -> Int -> IO FnvHash64
+fnv1a_64 ptr n = loop (FnvHash64 0xcbf29ce484222325) 0
+  where
         loop !acc !i
-            | i == n    = pure $ acc
-            | otherwise = do
-                v <- read8 addr i
-                loop (fnv1a_64_Mix8 v acc) (i + 1)
-
-read8 :: Addr# -> Int -> IO Word8
-read8 addr (I# i) = IO $ \s -> case readWord8OffAddr# addr i s of
-    (# s2, e #) -> (# s2, W8# e #)
+            | i == n    = pure acc
+            | otherwise = peekByteOff ptr i >>= \v -> loop (fnv1a_64_Mix8 v acc) (i + 1)
diff --git a/Data/Memory/Internal/CompatPrim64.hs b/Data/Memory/Internal/CompatPrim64.hs
deleted file mode 100644
--- a/Data/Memory/Internal/CompatPrim64.hs
+++ /dev/null
@@ -1,169 +0,0 @@
--- |
--- Module      : Data.Memory.Internal.CompatPrim
--- License     : BSD-style
--- Maintainer  : Vincent Hanquez <vincent@snarc.org>
--- Stability   : stable
--- Portability : Compat
---
--- This module try to keep all the difference between versions of ghc primitive
--- or other needed packages, so that modules don't need to use CPP.
---
--- Note that MagicHash and CPP conflicts in places, making it "more interesting"
--- to write compat code for primitives
---
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE MagicHash #-}
-{-# LANGUAGE UnboxedTuples #-}
-#include "MachDeps.h"
-module Data.Memory.Internal.CompatPrim64
-    ( Word64#
-    , Int64#
-    , eqInt64#
-    , neInt64#
-    , ltInt64#
-    , leInt64#
-    , gtInt64#
-    , geInt64#
-    , quotInt64#
-    , remInt64#
-    , eqWord64#
-    , neWord64#
-    , ltWord64#
-    , leWord64#
-    , gtWord64#
-    , geWord64#
-    , and64#
-    , or64#
-    , xor64#
-    , not64#
-    , timesWord64#
-    , uncheckedShiftL64#
-    , uncheckedShiftRL64#
-
-    , int64ToWord64#
-    , word64ToInt64#
-    , intToInt64#
-    , int64ToInt#
-    , wordToWord64#
-    , word64ToWord#
-    , w64#
-    ) where
-
-
-#if WORD_SIZE_IN_BITS == 64
-import GHC.Prim hiding (Word64#, Int64#)
-
-#if __GLASGOW_HASKELL__ >= 708
-type OutBool = Int#
-#else
-type OutBool = Bool
-#endif
-
-type Word64# = Word#
-type Int64# = Int#
-
-#if __GLASGOW_HASKELL__ < 904
-eqWord64# :: Word64# -> Word64# -> OutBool
-eqWord64# = eqWord#
-
-neWord64# :: Word64# -> Word64# -> OutBool
-neWord64# = neWord#
-
-ltWord64# :: Word64# -> Word64# -> OutBool
-ltWord64# = ltWord#
-
-leWord64# :: Word64# -> Word64# -> OutBool
-leWord64# = leWord#
-
-gtWord64# :: Word64# -> Word64# -> OutBool
-gtWord64# = gtWord#
-
-geWord64# :: Word64# -> Word64# -> OutBool
-geWord64# = geWord#
-
-eqInt64# :: Int64# -> Int64# -> OutBool
-eqInt64# = (==#)
-
-neInt64# :: Int64# -> Int64# -> OutBool
-neInt64# = (/=#)
-
-ltInt64# :: Int64# -> Int64# -> OutBool
-ltInt64# = (<#)
-
-leInt64# :: Int64# -> Int64# -> OutBool
-leInt64# = (<=#)
-
-gtInt64# :: Int64# -> Int64# -> OutBool
-gtInt64# = (>#)
-
-geInt64# :: Int64# -> Int64# -> OutBool
-geInt64# = (<=#)
-
-quotInt64# :: Int64# -> Int64# -> Int64#
-quotInt64# = quotInt#
-
-remInt64# :: Int64# -> Int64# -> Int64#
-remInt64# = remInt#
-
-and64# :: Word64# -> Word64# -> Word64#
-and64# = and#
-
-or64# :: Word64# -> Word64# -> Word64#
-or64# = or#
-
-xor64# :: Word64# -> Word64# -> Word64#
-xor64# = xor#
-
-not64# :: Word64# -> Word64#
-not64# = not#
-
-uncheckedShiftL64# :: Word64# -> Int# -> Word64#
-uncheckedShiftL64# = uncheckedShiftL#
-
-uncheckedShiftRL64#  :: Word64# -> Int# -> Word64#
-uncheckedShiftRL64# = uncheckedShiftL#
-
-int64ToWord64# :: Int64# -> Word64#
-int64ToWord64# = int2Word#
-
-word64ToInt64# :: Word64# -> Int64#
-word64ToInt64# = word2Int#
-
-intToInt64# :: Int# -> Int64#
-intToInt64# w = w
-
-int64ToInt# :: Int64# -> Int#
-int64ToInt# w = w
-
-wordToWord64# :: Word# -> Word64#
-wordToWord64# w = w
-
-word64ToWord# :: Word64# -> Word#
-word64ToWord# w = w
-
-timesWord64# :: Word64# -> Word64# -> Word64#
-timesWord64# = timesWord#
-#endif
-
-w64# :: Word# -> Word# -> Word# -> Word64#
-w64# w _ _ = w
-
-#elif WORD_SIZE_IN_BITS == 32
-import GHC.IntWord64
-import GHC.Prim (Word#)
-
-timesWord64# :: Word64# -> Word64# -> Word64#
-timesWord64# a b =
-    let !ai = word64ToInt64# a
-        !bi = word64ToInt64# b
-     in int64ToWord64# (timesInt64# ai bi)
-
-w64# :: Word# -> Word# -> Word# -> Word64#
-w64# _ hw lw =
-    let !h = wordToWord64# hw
-        !l = wordToWord64# lw
-     in or64# (uncheckedShiftL64# h 32#) l
-#else
-#error "not a supported architecture. supported WORD_SIZE_IN_BITS is 32 bits or 64 bits"
-#endif
diff --git a/ram.cabal b/ram.cabal
--- a/ram.cabal
+++ b/ram.cabal
@@ -1,6 +1,6 @@
 cabal-version:   3.0
 name:            ram
-version:         0.20.1
+version:         0.21.0
 synopsis:        memory and related abstraction stuff
 description:
   This is a fork of memory. It's open to accept changes from anyone,
@@ -20,6 +20,10 @@
 
   * Hashing : FNV, SipHash
 
+  Legacy note: This package shouldn't be used for new software.
+  It only exists and is maintained to give other packages time to
+  rewrite their software to use better libraries.
+
 license:         BSD-3-Clause
 license-file:    LICENSE
 copyright:       Vincent Hanquez <vincent@snarc.org>
@@ -65,15 +69,18 @@
     Data.Memory.Hash.SipHash
     Data.Memory.Internal.Compat
     Data.Memory.Internal.CompatPrim
-    Data.Memory.Internal.CompatPrim64
     Data.Memory.Internal.Imports
 
   exposed-modules:  Data.ByteArray.Sized
   build-depends:
     , base        <4.23
+    , base16      >=1.0 && <2
+    , base32      >=0.4 && <1
+    , base64      >=1.0 && <2
     , bytestring  <0.13
     , deepseq     >=1.1 && <1.17
     , ghc-prim    <0.14
+    , text        >=1.0 && <3
 
   -- FIXME armel or mispel is also little endian.
   -- might be a good idea to also add a runtime autodetect mode.
