basesystems 1.1.0.0 → 1.2.0.0
raw patch · 18 files changed
+825/−1023 lines, 18 filesdep +xcodecPVP ok
version bump matches the API change (PVP)
Dependencies added: xcodec
API changes (from Hackage documentation)
- Data.BaseSystem: class BaseSystem a
- Data.BaseSystem: decoder :: BaseSystem a => a -> Text -> Maybe ByteStr
- Data.BaseSystem: encoder :: BaseSystem a => a -> ByteStr -> Text
- Data.BaseSystem: instance Data.BaseSystem.BaseSystem Data.BaseSystem.DigitSystem.BitwiseDigits
- Data.BaseSystem: instance Data.BaseSystem.BaseSystem Data.BaseSystem.DigitSystem.RadixDigits
- Data.BaseSystem: type ByteStr = ShortByteString
- Data.BaseSystem: type Decoder = Text -> Maybe ByteStr
- Data.BaseSystem: type Encoder = ByteStr -> Text
- Data.BaseSystem.Alphabet: b32hexlower :: Alphabet
- Data.BaseSystem.Alphabet: b32hexupper :: Alphabet
- Data.BaseSystem.Alphabet: b32lower :: Alphabet
- Data.BaseSystem.Alphabet: b32upper :: Alphabet
- Data.BaseSystem.Alphabet: b64 :: Alphabet
- Data.BaseSystem.Alphabet: b64url :: Alphabet
- Data.BaseSystem.Alphabet: binary :: Alphabet
- Data.BaseSystem.Alphabet: btc58 :: Alphabet
- Data.BaseSystem.Alphabet: data Alphabet
- Data.BaseSystem.Alphabet: decimal :: Alphabet
- Data.BaseSystem.Alphabet: hexlower :: Alphabet
- Data.BaseSystem.Alphabet: hexupper :: Alphabet
- Data.BaseSystem.Alphabet: instance GHC.Classes.Eq Data.BaseSystem.Alphabet.Alphabet
- Data.BaseSystem.Alphabet: instance GHC.Classes.Ord Data.BaseSystem.Alphabet.Alphabet
- Data.BaseSystem.Alphabet: mkAlphabet :: Text -> Alphabet
- Data.BaseSystem.Alphabet: resolveSymbol :: Alphabet -> Value -> Maybe Symbol
- Data.BaseSystem.Alphabet: resolveValue :: Alphabet -> Symbol -> Maybe Value
- Data.BaseSystem.Alphabet: type Symbol = Char
- Data.BaseSystem.Alphabet: type Value = Word32
- Data.BaseSystem.BinaryTranscoder: append :: BinaryTranscoder bxc => bxc -> bxc -> bxc
- Data.BaseSystem.BinaryTranscoder: class (Monoid bxc) => BinaryTranscoder bxc
- Data.BaseSystem.BinaryTranscoder: dropBytes :: BinaryTranscoder bxc => Int -> bxc -> bxc
- Data.BaseSystem.BinaryTranscoder: dropBytesEnd :: BinaryTranscoder bxc => Int -> bxc -> bxc
- Data.BaseSystem.BinaryTranscoder: dropWhileBytes :: BinaryTranscoder bxc => (Word8 -> Bool) -> bxc -> bxc
- Data.BaseSystem.BinaryTranscoder: dropWhileBytesEnd :: BinaryTranscoder bxc => (Word8 -> Bool) -> bxc -> bxc
- Data.BaseSystem.BinaryTranscoder: empty :: BinaryTranscoder bxc => bxc
- Data.BaseSystem.BinaryTranscoder: instance Data.BaseSystem.BinaryTranscoder.BinaryTranscoder Data.ByteString.Internal.Type.ByteString
- Data.BaseSystem.BinaryTranscoder: instance Data.BaseSystem.BinaryTranscoder.BinaryTranscoder Data.ByteString.Lazy.Internal.LazyByteString
- Data.BaseSystem.BinaryTranscoder: instance Data.BaseSystem.BinaryTranscoder.BinaryTranscoder Data.ByteString.Short.Internal.ShortByteString
- Data.BaseSystem.BinaryTranscoder: lengthBytes :: BinaryTranscoder bxc => bxc -> Int
- Data.BaseSystem.BinaryTranscoder: null :: BinaryTranscoder bxc => bxc -> Bool
- Data.BaseSystem.BinaryTranscoder: packBytes :: BinaryTranscoder bxc => [Word8] -> bxc
- Data.BaseSystem.BinaryTranscoder: packValue :: (BinaryTranscoder bxc, Integral b, Bits b) => b -> bxc
- Data.BaseSystem.BinaryTranscoder: pushByte :: BinaryTranscoder bxc => bxc -> Word8 -> bxc
- Data.BaseSystem.BinaryTranscoder: pushByteEnd :: BinaryTranscoder bxc => bxc -> Word8 -> bxc
- Data.BaseSystem.BinaryTranscoder: replicateByte :: BinaryTranscoder bxc => Int -> Word8 -> bxc
- Data.BaseSystem.BinaryTranscoder: serializeValue :: BinaryTranscoder bxc => bxc -> Builder
- Data.BaseSystem.BinaryTranscoder: singleton :: BinaryTranscoder bxc => Word8 -> bxc
- Data.BaseSystem.BinaryTranscoder: spanBytes :: BinaryTranscoder bxc => (Word8 -> Bool) -> bxc -> (bxc, bxc)
- Data.BaseSystem.BinaryTranscoder: spanBytesEnd :: BinaryTranscoder bxc => (Word8 -> Bool) -> bxc -> (bxc, bxc)
- Data.BaseSystem.BinaryTranscoder: splitAtByte :: BinaryTranscoder bxc => Int -> bxc -> (bxc, bxc)
- Data.BaseSystem.BinaryTranscoder: takeBytes :: BinaryTranscoder bxc => Int -> bxc -> bxc
- Data.BaseSystem.BinaryTranscoder: takeBytesEnd :: BinaryTranscoder bxc => Int -> bxc -> bxc
- Data.BaseSystem.BinaryTranscoder: takeWhileBytes :: BinaryTranscoder bxc => (Word8 -> Bool) -> bxc -> bxc
- Data.BaseSystem.BinaryTranscoder: takeWhileBytesEnd :: BinaryTranscoder bxc => (Word8 -> Bool) -> bxc -> bxc
- Data.BaseSystem.BinaryTranscoder: type BitSet = Integer
- Data.BaseSystem.BinaryTranscoder: uncons :: BinaryTranscoder bxc => bxc -> Maybe (Word8, bxc)
- Data.BaseSystem.BinaryTranscoder: unpackBytes :: BinaryTranscoder bxc => bxc -> [Word8]
- Data.BaseSystem.BinaryTranscoder: unpackValue :: BinaryTranscoder bxc => bxc -> BitSet
- Data.BaseSystem.BinaryTranscoder: usnoc :: BinaryTranscoder bxc => bxc -> Maybe (bxc, Word8)
- Data.BaseSystem.DigitSystem: BitwiseDigits :: String -> Alphabet -> Int -> Int -> Int -> Maybe PaddingMethod -> BitwiseDigits
- Data.BaseSystem.DigitSystem: RadixDigits :: String -> Alphabet -> RadixDigits
- Data.BaseSystem.DigitSystem: base10 :: RadixDigits
- Data.BaseSystem.DigitSystem: base16lower :: RadixDigits
- Data.BaseSystem.DigitSystem: base16upper :: RadixDigits
- Data.BaseSystem.DigitSystem: base2 :: RadixDigits
- Data.BaseSystem.DigitSystem: base32hexlower :: BitwiseDigits
- Data.BaseSystem.DigitSystem: base32hexlowerNP :: BitwiseDigits
- Data.BaseSystem.DigitSystem: base32hexupper :: BitwiseDigits
- Data.BaseSystem.DigitSystem: base32hexupperNP :: BitwiseDigits
- Data.BaseSystem.DigitSystem: base32lower :: BitwiseDigits
- Data.BaseSystem.DigitSystem: base32lowerNP :: BitwiseDigits
- Data.BaseSystem.DigitSystem: base32upper :: BitwiseDigits
- Data.BaseSystem.DigitSystem: base32upperNP :: BitwiseDigits
- Data.BaseSystem.DigitSystem: base58btc :: RadixDigits
- Data.BaseSystem.DigitSystem: base64 :: BitwiseDigits
- Data.BaseSystem.DigitSystem: base64NP :: BitwiseDigits
- Data.BaseSystem.DigitSystem: base64url :: BitwiseDigits
- Data.BaseSystem.DigitSystem: base64urlNP :: BitwiseDigits
- Data.BaseSystem.DigitSystem: data BitwiseDigits
- Data.BaseSystem.DigitSystem: data PaddingMethod
- Data.BaseSystem.DigitSystem: data RadixDigits
- Data.BaseSystem.DigitSystem: instance GHC.Show.Show Data.BaseSystem.DigitSystem.BitwiseDigits
- Data.BaseSystem.DigitSystem: instance GHC.Show.Show Data.BaseSystem.DigitSystem.RadixDigits
- Data.BaseSystem.DigitTranscoder: class (BinaryTranscoder t) => DigitTranscoder a t
- Data.BaseSystem.DigitTranscoder: digitDecoder :: DigitTranscoder a t => a -> Text -> Maybe t
- Data.BaseSystem.DigitTranscoder: digitEncoder :: DigitTranscoder a t => a -> t -> Text
- Data.BaseSystem.DigitTranscoder: instance Data.BaseSystem.BinaryTranscoder.BinaryTranscoder t => Data.BaseSystem.DigitTranscoder.DigitTranscoder Data.BaseSystem.DigitSystem.BitwiseDigits t
- Data.BaseSystem.DigitTranscoder: instance Data.BaseSystem.BinaryTranscoder.BinaryTranscoder t => Data.BaseSystem.DigitTranscoder.DigitTranscoder Data.BaseSystem.DigitSystem.RadixDigits t
- Data.BaseSystem.DigitTranscoder: type Decoder t = Text -> Maybe t
- Data.BaseSystem.DigitTranscoder: type Encoder t = t -> Text
- Data.BaseSystem.Internal: iterateInit :: (b -> b) -> (a -> b) -> a -> [b]
- Data.BaseSystem.Internal: replaceNull :: [a] -> [a] -> [a]
- Data.BaseSystem.Lazy: class BaseSystem a
- Data.BaseSystem.Lazy: decoder :: BaseSystem a => a -> Text -> Maybe ByteStr
- Data.BaseSystem.Lazy: encoder :: BaseSystem a => a -> ByteStr -> Text
- Data.BaseSystem.Lazy: instance Data.BaseSystem.Lazy.BaseSystem Data.BaseSystem.DigitSystem.BitwiseDigits
- Data.BaseSystem.Lazy: instance Data.BaseSystem.Lazy.BaseSystem Data.BaseSystem.DigitSystem.RadixDigits
- Data.BaseSystem.Lazy: type ByteStr = LazyByteString
- Data.BaseSystem.Lazy: type Decoder = Text -> Maybe ByteStr
- Data.BaseSystem.Lazy: type Encoder = ByteStr -> Text
- Data.BaseSystem.Strict: class BaseSystem a
- Data.BaseSystem.Strict: decoder :: BaseSystem a => a -> Text -> Maybe ByteStr
- Data.BaseSystem.Strict: encoder :: BaseSystem a => a -> ByteStr -> Text
- Data.BaseSystem.Strict: instance Data.BaseSystem.Strict.BaseSystem Data.BaseSystem.DigitSystem.BitwiseDigits
- Data.BaseSystem.Strict: instance Data.BaseSystem.Strict.BaseSystem Data.BaseSystem.DigitSystem.RadixDigits
- Data.BaseSystem.Strict: type ByteStr = ByteString
- Data.BaseSystem.Strict: type Decoder = Text -> Maybe ByteStr
- Data.BaseSystem.Strict: type Encoder = ByteStr -> Text
+ Data.BaseSystems: class BaseSystem a
+ Data.BaseSystems: decoder :: BaseSystem a => a -> Text -> Maybe ByteStr
+ Data.BaseSystems: encoder :: BaseSystem a => a -> ByteStr -> Text
+ Data.BaseSystems: instance Data.BaseSystems.BaseSystem Data.BaseSystems.DigitTranscoder.BitwiseDigits
+ Data.BaseSystems: instance Data.BaseSystems.BaseSystem Data.BaseSystems.DigitTranscoder.RadixDigits
+ Data.BaseSystems: type ByteStr = ShortByteString
+ Data.BaseSystems: type Decoder = Text -> Maybe ByteStr
+ Data.BaseSystems: type Encoder = ByteStr -> Text
+ Data.BaseSystems.Alphabet: b32hexlower :: Alphabet
+ Data.BaseSystems.Alphabet: b32hexupper :: Alphabet
+ Data.BaseSystems.Alphabet: b32lower :: Alphabet
+ Data.BaseSystems.Alphabet: b32upper :: Alphabet
+ Data.BaseSystems.Alphabet: b64 :: Alphabet
+ Data.BaseSystems.Alphabet: b64url :: Alphabet
+ Data.BaseSystems.Alphabet: binary :: Alphabet
+ Data.BaseSystems.Alphabet: btc58 :: Alphabet
+ Data.BaseSystems.Alphabet: data Alphabet
+ Data.BaseSystems.Alphabet: decimal :: Alphabet
+ Data.BaseSystems.Alphabet: hexlower :: Alphabet
+ Data.BaseSystems.Alphabet: hexupper :: Alphabet
+ Data.BaseSystems.Alphabet: instance GHC.Classes.Eq Data.BaseSystems.Alphabet.Alphabet
+ Data.BaseSystems.Alphabet: instance GHC.Classes.Ord Data.BaseSystems.Alphabet.Alphabet
+ Data.BaseSystems.Alphabet: mkAlphabet :: Text -> Alphabet
+ Data.BaseSystems.Alphabet: resolveSymbol :: Alphabet -> Value -> Maybe Symbol
+ Data.BaseSystems.Alphabet: resolveValue :: Alphabet -> Symbol -> Maybe Value
+ Data.BaseSystems.Alphabet: type Symbol = Char
+ Data.BaseSystems.Alphabet: type Value = Word32
+ Data.BaseSystems.DigitSystems: base10 :: RadixDigits
+ Data.BaseSystems.DigitSystems: base16lower :: RadixDigits
+ Data.BaseSystems.DigitSystems: base16upper :: RadixDigits
+ Data.BaseSystems.DigitSystems: base2 :: RadixDigits
+ Data.BaseSystems.DigitSystems: base32hexlower :: BitwiseDigits
+ Data.BaseSystems.DigitSystems: base32hexlowerNP :: BitwiseDigits
+ Data.BaseSystems.DigitSystems: base32hexupper :: BitwiseDigits
+ Data.BaseSystems.DigitSystems: base32hexupperNP :: BitwiseDigits
+ Data.BaseSystems.DigitSystems: base32lower :: BitwiseDigits
+ Data.BaseSystems.DigitSystems: base32lowerNP :: BitwiseDigits
+ Data.BaseSystems.DigitSystems: base32upper :: BitwiseDigits
+ Data.BaseSystems.DigitSystems: base32upperNP :: BitwiseDigits
+ Data.BaseSystems.DigitSystems: base58btc :: RadixDigits
+ Data.BaseSystems.DigitSystems: base64 :: BitwiseDigits
+ Data.BaseSystems.DigitSystems: base64NP :: BitwiseDigits
+ Data.BaseSystems.DigitSystems: base64url :: BitwiseDigits
+ Data.BaseSystems.DigitSystems: base64urlNP :: BitwiseDigits
+ Data.BaseSystems.DigitTranscoder: BitwiseDigits :: String -> Alphabet -> Int -> Int -> Int -> Maybe PaddingMethod -> BitwiseDigits
+ Data.BaseSystems.DigitTranscoder: PaddingMethod :: Char -> (Int -> Text) -> PaddingMethod
+ Data.BaseSystems.DigitTranscoder: RadixDigits :: String -> Alphabet -> RadixDigits
+ Data.BaseSystems.DigitTranscoder: [paddingChar] :: PaddingMethod -> Char
+ Data.BaseSystems.DigitTranscoder: [paddingResolve] :: PaddingMethod -> Int -> Text
+ Data.BaseSystems.DigitTranscoder: class BinaryTranscoder bxc => DigitTranscoder codec bxc
+ Data.BaseSystems.DigitTranscoder: data BitwiseDigits
+ Data.BaseSystems.DigitTranscoder: data PaddingMethod
+ Data.BaseSystems.DigitTranscoder: data RadixDigits
+ Data.BaseSystems.DigitTranscoder: digitDecoder :: DigitTranscoder codec bxc => codec -> Decoder bxc
+ Data.BaseSystems.DigitTranscoder: digitEncoder :: DigitTranscoder codec bxc => codec -> Encoder bxc
+ Data.BaseSystems.DigitTranscoder: instance Data.XCodec.BinaryTranscoder.BinaryTranscoder bxc => Data.BaseSystems.DigitTranscoder.DigitTranscoder Data.BaseSystems.DigitTranscoder.BitwiseDigits bxc
+ Data.BaseSystems.DigitTranscoder: instance Data.XCodec.BinaryTranscoder.BinaryTranscoder bxc => Data.BaseSystems.DigitTranscoder.DigitTranscoder Data.BaseSystems.DigitTranscoder.RadixDigits bxc
+ Data.BaseSystems.DigitTranscoder: instance Data.XCodec.BinaryTranscoder.BinaryTranscoder bxc => Data.XCodec.StreamTranscoder.StreamTranscoder Data.BaseSystems.DigitTranscoder.BitwiseDigits bxc Data.Text.Internal.Text
+ Data.BaseSystems.DigitTranscoder: instance Data.XCodec.BinaryTranscoder.BinaryTranscoder bxc => Data.XCodec.StreamTranscoder.StreamTranscoder Data.BaseSystems.DigitTranscoder.RadixDigits bxc Data.Text.Internal.Text
+ Data.BaseSystems.DigitTranscoder: instance GHC.Show.Show Data.BaseSystems.DigitTranscoder.BitwiseDigits
+ Data.BaseSystems.DigitTranscoder: instance GHC.Show.Show Data.BaseSystems.DigitTranscoder.RadixDigits
+ Data.BaseSystems.DigitTranscoder: type Decoder bxc = Decoder bxc Text
+ Data.BaseSystems.DigitTranscoder: type Encoder bxc = Encoder bxc Text
+ Data.BaseSystems.Lazy: class BaseSystem a
+ Data.BaseSystems.Lazy: decoder :: BaseSystem a => a -> Text -> Maybe ByteStr
+ Data.BaseSystems.Lazy: encoder :: BaseSystem a => a -> ByteStr -> Text
+ Data.BaseSystems.Lazy: instance Data.BaseSystems.Lazy.BaseSystem Data.BaseSystems.DigitTranscoder.BitwiseDigits
+ Data.BaseSystems.Lazy: instance Data.BaseSystems.Lazy.BaseSystem Data.BaseSystems.DigitTranscoder.RadixDigits
+ Data.BaseSystems.Lazy: type ByteStr = LazyByteString
+ Data.BaseSystems.Lazy: type Decoder = Text -> Maybe ByteStr
+ Data.BaseSystems.Lazy: type Encoder = ByteStr -> Text
+ Data.BaseSystems.Strict: class BaseSystem a
+ Data.BaseSystems.Strict: decoder :: BaseSystem a => a -> Text -> Maybe ByteStr
+ Data.BaseSystems.Strict: encoder :: BaseSystem a => a -> ByteStr -> Text
+ Data.BaseSystems.Strict: instance Data.BaseSystems.Strict.BaseSystem Data.BaseSystems.DigitTranscoder.BitwiseDigits
+ Data.BaseSystems.Strict: instance Data.BaseSystems.Strict.BaseSystem Data.BaseSystems.DigitTranscoder.RadixDigits
+ Data.BaseSystems.Strict: type ByteStr = ByteString
+ Data.BaseSystems.Strict: type Decoder = Text -> Maybe ByteStr
+ Data.BaseSystems.Strict: type Encoder = ByteStr -> Text
Files
- CHANGELOG.md +23/−4
- README.md +11/−10
- basesystems.cabal +11/−11
- basesystems/Data/BaseSystem.hs +0/−47
- basesystems/Data/BaseSystem/Alphabet.hs +0/−177
- basesystems/Data/BaseSystem/BinaryTranscoder.hs +0/−238
- basesystems/Data/BaseSystem/DigitSystem.hs +0/−191
- basesystems/Data/BaseSystem/DigitTranscoder.hs +0/−226
- basesystems/Data/BaseSystem/Internal.hs +0/−25
- basesystems/Data/BaseSystem/Lazy.hs +0/−47
- basesystems/Data/BaseSystem/Strict.hs +0/−47
- basesystems/Data/BaseSystems.hs +51/−0
- basesystems/Data/BaseSystems/Alphabet.hs +177/−0
- basesystems/Data/BaseSystems/DigitSystems.hs +145/−0
- basesystems/Data/BaseSystems/DigitTranscoder.hs +280/−0
- basesystems/Data/BaseSystems/Internal.hs +25/−0
- basesystems/Data/BaseSystems/Lazy.hs +51/−0
- basesystems/Data/BaseSystems/Strict.hs +51/−0
CHANGELOG.md view
@@ -1,11 +1,30 @@-[`basesystems`](http://hackage-content.haskell.org/package/basesystems)-change log:-===========+[`basesystems`](http://hackage.haskell.org/package/basesystems) change log:+=========================================================================== +Major release 1.2+-----------------++## Version 1.2 (07-08-2026)++This version removes the functionality that the [xcodec](+ihttps://hackage.haskell.org/package/xcodec) package now provides. This change+replaces the `Data.BaseSystem.*Transcoder` modules, so they are no longer part+of this package.++The `Data.BaseSystem.*` modules has been renamed to `Data.BaseSystems.*` to+match the package name. The `Data.BaseSystem.DigitSystem` module has also been+renamed to `Data.BaseSystems.DigitSystems` to reflect the digit systems (plural)+that it exports.++The `Data.BaseSystems.DigitSystems` module is exported by the+`Data.BaseSystems`, `Data.BaseSystems.Lazy`, and `Data.BaseSystems.Strict`+packages, so that a single import change be used for simple use cases such as+showing number strings in a fixed basesystem.+ Major release 1.1 ----------------- -## Version 1.1.0.0 (05-17-2026)+## Version 1.1 (05-17-2026) ### Cabal - Upgrade major versions for `text` and `containers` versions.
README.md view
@@ -1,9 +1,9 @@ basesystems []( https://hackage.haskell.org/package/basesystems)[](-https://builds.sr.ht/~z0/basesystems/commits/main/basesystem-testing.yml?)-------------------------------------------------+https://builds.sr.ht/~z0/basesystems/commits/main/ipfshs-ci.yml.svg)](+https://builds.sr.ht/~z0/basesystems/commits/main/ipfshs-ci.yml?)+--------------------------------------------------------------------------- This project contains code for encoding/decoding numeric basesystems in Haskell. It's implemented in a strategy pattern style where `BaseSystem` is a type-class@@ -22,11 +22,6 @@ `decoder` are defined in `Data.BaseSystem.DigitSystem`. See the [*example*]( #example). -`basesystems` is a part of the [ipfshs](https://git.sr.ht/~z0/ipfshs) project-which contains [testing via sourcehut builds](-https://builds.sr.ht/~z0/basesystems/commits/main/basesystem-testing.yml) and a-[bug ticket tracker](https://todo.sr.ht/~z0/ipfshs).- Coverage -------- @@ -56,8 +51,7 @@ ```haskell λ> {-# LANGUAGE OverloadedStrings #-}-λ> import Data.BaseSystem (encoder, decoder)-λ> import Data.BaseSystem.DigitSystem (base2, base10, base16lower, base32lower)+λ> import Data.BaseSystem (encoder, decoder, base2, base10, base16lower, base32lower) λ> import Data.BaseSystem.BinaryTranscoder (packValue) ``` @@ -88,6 +82,13 @@ λ> encoder base10 <$> decoder base32lower "pm======" Just "123" ```++Development+-----------++Unit tests are provided on the main [ipfshs repo](https://git.sr.ht/~z0/ipfshs),+and bugs can be reported on [ipfshs ticket tracker](+https://todo.sr.ht/~z0/ipfshs). Licensing ---------
basesystems.cabal view
@@ -1,5 +1,5 @@ cabal-version: 3.0-version: 1.1.0.0+version: 1.2.0.0 name: basesystems build-type: Simple author: Zoey McBride@@ -39,6 +39,7 @@ common Depends { build-depends: base >= 4.18 && < 5,+ xcodec ^>= 1.1, array ^>= 0.5, bytestring >= 0.12 && < 0.13, containers >= 0.7 && < 8,@@ -61,19 +62,18 @@ else { import: BuildUser } exposed-modules: -- * BaseSystems for ShortByteStrings- Data.BaseSystem,+ Data.BaseSystems, -- * BaseSystems for LazyByteStrings- Data.BaseSystem.Lazy,+ Data.BaseSystems.Lazy, -- * BaseSystems for Strict ByteStrings- Data.BaseSystem.Strict,+ Data.BaseSystems.Strict, -- * Defines Alphabet type + instances- Data.BaseSystem.Alphabet,+ Data.BaseSystems.Alphabet, -- * Data instances for working with strings of digits- Data.BaseSystem.DigitSystem,+ Data.BaseSystems.DigitSystems, -- * Interface for decoding strings of numbers into binary data.- Data.BaseSystem.DigitTranscoder,- -- * Interface for transcoding bitstreams- Data.BaseSystem.BinaryTranscoder,- -- * Provides utility functions- Data.BaseSystem.Internal,+ Data.BaseSystems.DigitTranscoder,+ other-modules:+ -- * Helper functions.+ Data.BaseSystems.Internal, }
− basesystems/Data/BaseSystem.hs
@@ -1,47 +0,0 @@--- | Module : Data.BaseSystem--- Description : Implements BaseSystems for ShortByteString.--- Copyright : Zoey McBride (c) 2026--- License : BSD-3-Clause--- Maintainer : zoeymcbride@mailbox.org--- Stability : experimental-module Data.BaseSystem- ( -- * BaseSystems on ShortByteString.- BaseSystem (encoder, decoder),-- -- * Function signatures aliases.- Encoder,- Decoder,-- -- * ByteString implementation for this file.- ByteStr,- )-where--import Data.BaseSystem.DigitSystem (BitwiseDigits, RadixDigits)-import Data.BaseSystem.DigitTranscoder qualified as DXC-import Data.ByteString.Short (ShortByteString (ShortByteString))-import Data.Text (Text)---- | Sets the bytestring type to use in this file.-type ByteStr = ShortByteString---- | Alias for curried encoder method.-type Encoder = ByteStr -> Text---- | Alias for curried decoder method.-type Decoder = Text -> Maybe ByteStr---- | Configures the BaseSystem for this file's ByteType.-class BaseSystem a where- encoder :: a -> ByteStr -> Text- decoder :: a -> Text -> Maybe ByteStr---- | BaseSystem for ShortByteString.-instance BaseSystem RadixDigits where- encoder = DXC.digitEncoder- decoder = DXC.digitDecoder---- | BaseSystem for ShortByteString.-instance BaseSystem BitwiseDigits where- encoder = DXC.digitEncoder- decoder = DXC.digitDecoder
− basesystems/Data/BaseSystem/Alphabet.hs
@@ -1,177 +0,0 @@--- | Module : Data.BaseSystem.Alphabet--- Description : Functions for Alphabets on BaseSystems--- Copyright : Zoey McBride (c) 2026--- License : BSD-3-Clause--- Maintainer : zoeymcbride@mailbox.org--- Stability : experimental------ Implements digit alphabets for BaseSystems.-module Data.BaseSystem.Alphabet- ( -- * Symbolic representation of a value- Symbol,-- -- * The value that's given to each digit- Value,-- -- * Abstract Alphabet and functions- Alphabet (alphaRadix, alphaHashId),- mkAlphabet,- resolveValue,- resolveSymbol,-- -- * Available Alphabets- binary,- decimal,- hexlower,- hexupper,- btc58,- b32lower,- b32upper,- b32hexlower,- b32hexupper,- b64,- b64url,- )-where--import Data.Array.IArray qualified as Array-import Data.Array.Unboxed (UArray, (!?))-import Data.Char (ord)-import Data.Function (on)-import Data.Map.Strict (Map)-import Data.Map.Strict qualified as Map-import Data.Text (Text)-import Data.Text qualified as Text-import Data.Word (Word32)---- TODO: Another module for alphabets with symbols of multiple unicode code--- points should be made. Then a DigitSystem can be built off that for--- basesystems of multipoint symbols.---- | Represents a symbol to act as a digit in a numbering system.-type Symbol = Char---- | The value of a symbol in a number system.-type Value = Word32---- | Stores the String hash for an Alphabet. Used for comparision.-type HashId = Int---- | Represents a two-way mapping between symbol's values and values's symbols.-data Alphabet = Alphabet- { alphaRadix :: Int,- alphaHashId :: HashId,- alphaValues :: UArray Value Symbol,- alphaSymbols :: Map Symbol Value- }--instance Eq Alphabet where- (==) = (==) `on` alphaHashId--instance Ord Alphabet where- compare = compare `on` alphaHashId---- | Implements https://cp-algorithms.com/string/string-hashing.html-mkHashId :: Text -> HashId-mkHashId = snd . Text.foldr buildPolynomial (0 :: Integer, 0 :: HashId)- where- -- Should be around of characters in any given alphabet- baseprime = 53- -- Large prime to prevent most collisions (1/modprime chance)- modprime = 1_000_009- -- Produces the term for a given character- buildPolynomial ch (expvalue, polysum) =- let term = baseprime ^ expvalue * ord ch- next = polysum + term- in (expvalue + 1, next `mod` modprime)---- | Creates an Alphabet from Text using its Chars as Symbols.-mkAlphabet :: Text -> Alphabet-mkAlphabet text =- let symbols = Text.unpack text- numsymbols = Text.length text- maxdigit = fromIntegral $ numsymbols - 1- in Alphabet- { alphaRadix = numsymbols,- alphaHashId = mkHashId text,- alphaValues = Array.listArray (0, maxdigit) symbols,- alphaSymbols = Map.fromList $ zip symbols [0 ..]- }---- | /O(log n)/ Find a value from a symbol in an alphabet.-resolveValue :: Alphabet -> Symbol -> Maybe Value-resolveValue abc symbol = Map.lookup symbol $ alphaSymbols abc---- | /O(1)/ Find a symbol from a value in an alphabet.-resolveSymbol :: Alphabet -> Value -> Maybe Symbol-resolveSymbol abc value = alphaValues abc !? value---- | Constructs an Alphabet from a list of digit symbol strings.-mk :: [[Char]] -> Alphabet-mk = mkAlphabet . Text.pack . concat---- | Defines base 2.-binary :: Alphabet-binary = mk ["01"]---- | Defines base 10.-decimal :: Alphabet-decimal = mk ["0", ['1' .. '9']]---- | Defines base 16 upper-case.-hexupper :: Alphabet-hexupper = mk ["0", ['1' .. '9'], ['A' .. 'F']]---- | Defines base 16 lower-case.-hexlower :: Alphabet-hexlower = mk ["0", ['1' .. '9'], ['a' .. 'f']]---- | Defines base 58, bitcoin variant.-btc58 :: Alphabet-btc58 =- mk- [ ['1' .. '9'],- ['A' .. 'H'],- ['J' .. 'N'],- ['P' .. 'Z'],- ['a' .. 'k'],- ['m' .. 'z']- ]---- | Defines base32, upper-case.-b32upper :: Alphabet-b32upper = mk [['A' .. 'Z'], ['2' .. '7']]---- | Defines base32, lower-case.-b32lower :: Alphabet-b32lower = mk [['a' .. 'z'], ['2' .. '7']]---- | Defines base32 hexadecimal, upper-case.-b32hexupper :: Alphabet-b32hexupper = mk ["0", ['1' .. '9'], ['A' .. 'V']]---- | Defines base32 hexadecimal, lower-case.-b32hexlower :: Alphabet-b32hexlower = mk ["0", ['1' .. '9'], ['a' .. 'v']]---- | Defines default base64 with + and /.-b64 :: Alphabet-b64 =- mk- [ ['A' .. 'Z'],- ['a' .. 'z'],- "0",- ['1' .. '9'],- ['+', '/']- ]---- | Defines URL-safe base64 with - and _.-b64url :: Alphabet-b64url =- mk- [ ['A' .. 'Z'],- ['a' .. 'z'],- "0",- ['1' .. '9'],- ['-', '_']- ]
− basesystems/Data/BaseSystem/BinaryTranscoder.hs
@@ -1,238 +0,0 @@--- | Module : Data.BaseSystem.BinaryTranscoder--- Description : Interfaces for transcoding numeric binary data.--- Copyright : Zoey McBride (c) 2026--- License : BSD-3-Clause--- Maintainer : zoeymcbride@mailbox.org--- Stability : experimental-module Data.BaseSystem.BinaryTranscoder- ( -- * Interface for implementing generic binary decoders- BinaryTranscoder (..),-- -- * Converts streams of bytes into editable bits- BitSet,- )-where--import Data.BaseSystem.Internal-import Data.Bits (Bits, (.&.), (.<<.), (.>>.), (.|.))-import Data.ByteString (ByteString)-import Data.ByteString qualified as Bytes-import Data.ByteString.Builder (Builder)-import Data.ByteString.Builder qualified as Builder-import Data.ByteString.Lazy (LazyByteString)-import Data.ByteString.Lazy qualified as LBS-import Data.ByteString.Short (ShortByteString)-import Data.ByteString.Short qualified as SBS-import Data.Word (Word8)---- | Storage for data with bitwise operations.-type BitSet = Integer---- | Class of functions for working on binary transcoder streams, that is--- streams of bytes that we can easily turn into a bitset, and can partition--- into smaller streams for extracting and parsing data.-class (Monoid bxc) => BinaryTranscoder bxc where- -- This defines basic methods that all BinaryTranscoders share.- {-# MINIMAL- lengthBytes,- unpackValue,- serializeValue,- packBytes,- unpackBytes,- pushByte,- pushByteEnd,- takeBytes,- takeBytesEnd,- dropBytes,- dropBytesEnd,- splitAtByte,- spanBytes,- spanBytesEnd,- replicateByte- #-}-- -- | Gives back the length in bytes of the Transcoder data.- lengthBytes :: bxc -> Int-- -- | Unpacks transcoder data as BitSet, ie, an infinite bitarray.- unpackValue :: bxc -> BitSet-- -- | Places the contents of the transcoder into a ByteString Builder.- serializeValue :: bxc -> Builder-- -- | Packs Word8 values into transcoder data `bxc`.- packBytes :: [Word8] -> bxc-- -- | Unpacks transcoder data into a list of bytes.- unpackBytes :: bxc -> [Word8]-- -- | Prepends a byte value to the top of the transcoder data.- pushByte :: bxc -> Word8 -> bxc-- -- | Appends the byte value to the end of the transcoder data.- pushByteEnd :: bxc -> Word8 -> bxc-- -- | Takes a subsection of bytes from the beginning of the transcoder data.- takeBytes :: Int -> bxc -> bxc-- -- | Removes bytes from the beginning of transcoder data.- dropBytes :: Int -> bxc -> bxc-- -- | Removes bytes from the end of transcoder data.- takeBytesEnd :: Int -> bxc -> bxc-- -- | Removes bytes from the end of transcoder data.- dropBytesEnd :: Int -> bxc -> bxc-- -- | Parititions the binary data at the nth byte.- splitAtByte :: Int -> bxc -> (bxc, bxc)-- -- | Repeats a byte value in binary data.- replicateByte :: Int -> Word8 -> bxc-- -- | Splits byte values based on the first false result from the predicate.- spanBytes :: (Word8 -> Bool) -> bxc -> (bxc, bxc)-- -- | Implements `takeWhile` for byte data in binary transcoder.- takeWhileBytes :: (Word8 -> Bool) -> bxc -> bxc- takeWhileBytes f = fst . spanBytesEnd f-- -- | Implements `dropWhile` for byte data in binary transcoder.- dropWhileBytes :: (Word8 -> Bool) -> bxc -> bxc- dropWhileBytes f = snd . spanBytesEnd f-- -- | Splits the list while the element predicate holds staring from the left- -- side.- spanBytesEnd :: (Word8 -> Bool) -> bxc -> (bxc, bxc)-- -- | Implements `takeWhileEnd` for byte data in binary transcoder.- takeWhileBytesEnd :: (Word8 -> Bool) -> bxc -> bxc- takeWhileBytesEnd f = snd . spanBytesEnd f-- -- | Implements `dropWhileEnd` for byte data in binary transcoder.- dropWhileBytesEnd :: (Word8 -> Bool) -> bxc -> bxc- dropWhileBytesEnd f = fst . spanBytesEnd f-- -- | Represents empty binary transcoder data.- empty :: bxc- empty = mempty-- -- | Appends the contents of two binary transcoders.- append :: bxc -> bxc -> bxc- append = mappend-- -- | Creates a binary transcoder from a single byte.- {-# INLINE singleton #-}- singleton :: Word8 -> bxc- singleton w8 = packBytes [w8]-- -- | Returns True if the binary transcoder data is empty.- {-# INLINE null #-}- null :: bxc -> Bool- null bxc = lengthBytes bxc == 0-- -- | Returns the first byte removed from the transcoder data, if available.- {-# INLINE uncons #-}- uncons :: bxc -> Maybe (Word8, bxc)- uncons bxc =- let (start, rest) = splitAtByte 1 bxc- in case unpackBytes start of- [top] -> Just (top, rest)- _ -> Nothing-- -- | Returns the last byte removed from the transcoder data, if available.- {-# INLINE usnoc #-}- usnoc :: bxc -> Maybe (bxc, Word8)- usnoc bxc =- let offset = lengthBytes bxc - 1- (rest, taken) = splitAtByte offset bxc- in case unpackBytes taken of- [final] -> Just (rest, final)- _ -> Nothing-- -- | Packs a bit string value into some bytes data `bxc`.- packValue :: (Integral b, Bits b) => b -> bxc- packValue intdata =- -- This code can be optimized; if we precalculate the length of the BitSet- -- in bits we can mask from the other direction using left shifts, and- -- remove the call to reverse, maybe we can even use a list comprehension to- -- build in place for Bytes.packBytes- packBytes- . replaceNull [0]- . reverse- . map (\(_, byte) -> fromIntegral byte)- . takeWhile (\(rest, byte) -> rest > 0 || byte > 0)- $ iterateInit (\(input, _) -> extractByte input) (,0) intdata- where- -- Gets the byte from LSB in input and shifts the value 8 bits.- extractByte input = (input .>>. 8, input .&. 0xFF)---- | Implements binary decoder for Bytestrings, best for constant values that--- exist for the lifetime of the program.--- https://hackage-content.haskell.org/package/bytestring-0.12.2.0/docs/Data-ByteString.html#g:2-instance BinaryTranscoder ByteString where- lengthBytes = Bytes.length- packBytes = Bytes.pack- serializeValue = Builder.byteString- unpackBytes = Bytes.unpack- pushByte = flip Bytes.cons- pushByteEnd = Bytes.snoc- takeBytes = Bytes.take- takeBytesEnd = Bytes.takeEnd- dropBytes = Bytes.drop- dropBytesEnd = Bytes.dropEnd- spanBytes = Bytes.span- spanBytesEnd = Bytes.spanEnd- splitAtByte = Bytes.splitAt- replicateByte k = Bytes.replicate $ fromIntegral k- unpackValue = unpackValue . LBS.fromStrict---- TODO: we can improve the proformance of unpackBits by examining the current--- bytestring, and loading a range of 1-8 bytes to OR simulaneously---- | Implements binary decoder for LazyByteString, best for very large data sets--- that can be loaded in and out of memory on demand.--- https://hackage-content.haskell.org/package/bytestring-0.12.2.0/docs/Data-ByteString-Lazy.html-instance BinaryTranscoder LazyByteString where- lengthBytes = fromIntegral . LBS.length- packBytes = LBS.pack- unpackBytes = LBS.unpack- serializeValue = Builder.lazyByteString- pushByte = flip LBS.cons- pushByteEnd = LBS.snoc- takeBytes k = LBS.take $ fromIntegral k- takeBytesEnd k = LBS.takeEnd $ fromIntegral k- dropBytes k = LBS.drop $ fromIntegral k- dropBytesEnd k = LBS.dropEnd $ fromIntegral k- spanBytes = LBS.span- spanBytesEnd = LBS.spanEnd- splitAtByte k = LBS.splitAt $ fromIntegral k- replicateByte k = LBS.replicate $ fromIntegral k- unpackValue =- -- From LSB to MSB, OR the current byte and shift the total bits.- LBS.foldl' (\bits byte -> (bits .<<. 8) .|. fromIntegral byte) 0- . LBS.dropWhile (== 0)---- | Implements binary decoder for ShortByteString, best for compact data that--- doesn't exist long in memory. Notably, it packs better in memory than normal--- ByteString (prone to Heap Fragmentation).--- https://hackage-content.haskell.org/package/bytestring-0.12.2.0/docs/Data-ByteString-Short.html#g:1-instance BinaryTranscoder ShortByteString where- lengthBytes = SBS.length- packBytes = SBS.pack- unpackBytes = SBS.unpack- serializeValue = Builder.shortByteString- pushByte = flip SBS.cons- pushByteEnd = SBS.snoc- takeBytes = SBS.take- takeBytesEnd = SBS.takeEnd- dropBytes = SBS.drop- dropBytesEnd = SBS.dropEnd- spanBytes = SBS.span- spanBytesEnd = SBS.spanEnd- splitAtByte = SBS.splitAt- replicateByte k = SBS.replicate $ fromIntegral k- unpackValue =- -- From LSB to MSB, OR the current byte and shift the total bits.- SBS.foldl' (\bits byte -> (bits .<<. 8) .|. fromIntegral byte) 0- . SBS.dropWhile (== 0)
− basesystems/Data/BaseSystem/DigitSystem.hs
@@ -1,191 +0,0 @@--- | Module : Data.BaseSystem.DigitSystem--- Description : Implements DigitSystems for DigitTranscoders--- Copyright : Zoey McBride (c) 2026--- License : BSD-3-Clause--- Maintainer : zoeymcbride@mailbox.org--- Stability : experimental-module Data.BaseSystem.DigitSystem- ( -- * Abstract DigitSystems data types- RadixDigits (RadixDigits),- BitwiseDigits (BitwiseDigits),-- -- * Data member of BitwiseDigits. Determines how to post process encodings.- PaddingMethod (paddingChar, paddingResolve),-- -- * Concrete instances of DigitSystems- base2,- base10,- base16lower,- base16upper,- base32lower,- base32lowerNP,- base32upper,- base32upperNP,- base32hexlower,- base32hexlowerNP,- base32hexupper,- base32hexupperNP,- base58btc,- base64,- base64NP,- base64url,- base64urlNP,- )-where--import Data.BaseSystem.Alphabet (Alphabet)-import Data.BaseSystem.Alphabet qualified as ABCs-import Data.Text (Text)-import Data.Text qualified as Text---- | Implements BaseSystem over base radix modulus.-data RadixDigits = RadixDigits- { -- | Used to implement instance of Show.- radixShow :: String,- -- | Alphabet for the base system.- radixAlpha :: Alphabet- }--instance Show RadixDigits where- show = radixShow---- | Implements padding for encoding BitwiseDigits.-data PaddingMethod = PaddingMethod- { -- | Gives the char to use as padding.- paddingChar :: Char,- -- | Provides the callback to resolve the padding from # of bytes.- paddingResolve :: Int -> Text- }---- | Implements BaseSystem over series of bit groups.-data BitwiseDigits = BitwiseDigits- { -- | String used to implement instance of Show.- bitwiseShow :: String,- -- | Base system alphabet.- bitwiseAlpha :: Alphabet,- -- | # of bits per symbol- bitwiseSymBits :: Int,- -- | # of bytes to be processed into symbols in encoding.- bitwiseGroupBytes :: Int,- -- | # of bytes forming a group to find padding in decoding.- bitwiseGroupSymbols :: Int,- -- | Gives the method for padding.- bitwisePadMethod :: Maybe PaddingMethod- }--instance Show BitwiseDigits where- show = bitwiseShow---- | Provides methods for building a base32 system, with an option to toggle--- padding.-mkBaseSystem32 :: String -> Maybe Char -> Alphabet -> BitwiseDigits-mkBaseSystem32 name padchar abc =- BitwiseDigits name abc symbits groupbytes groupsymbols $- PaddingMethod <*> padImpl <$> padchar- where- groupbytes = 5- groupsymbols = 8- symbits = 5- padImpl padding bytestotal- | bytestotal > 5 = padImpl padding $ bytestotal `mod` symbits- | bytestotal == 4 = Text.singleton padding- | bytestotal == 3 = Text.pack (replicate 3 padding)- | bytestotal == 2 = Text.pack (replicate 4 padding)- | bytestotal == 1 = Text.pack (replicate 6 padding)- | otherwise = Text.empty---- | Provides methods for building a base64 system, with an option to toggle--- padding.-mkBaseSystem64 :: String -> Maybe Char -> Alphabet -> BitwiseDigits-mkBaseSystem64 name padchar abc =- BitwiseDigits name abc symbits groupbytes groupsymbols $- PaddingMethod <*> padImpl <$> padchar- where- groupbytes = 3- groupsymbols = 4- symbits = 6- padImpl padding bytestotal- | bytestotal > 3 = padImpl padding $ bytestotal `mod` groupbytes- | bytestotal == 2 = Text.singleton padding- | bytestotal == 1 = Text.pack [padding, padding]- | otherwise = Text.empty---- | Defines the BaseSystem for binary.-base2 :: RadixDigits-base2 = RadixDigits "base2" ABCs.binary---- | Defines base10 as RadixDigits.-base10 :: RadixDigits-base10 = RadixDigits "base10" ABCs.decimal---- | Defines Bitcoin's base58 implementation.-base58btc :: RadixDigits-base58btc = RadixDigits "base58btc" ABCs.btc58---- | Defines uppercase hexidecimal on BitwiseDigits.-base16upper :: RadixDigits-base16upper = RadixDigits "base16upper" ABCs.hexupper---- | Defines lowercase hexadecimal on BitwiseDigits.-base16lower :: RadixDigits-base16lower = RadixDigits "base16lower" ABCs.hexlower---- | Defines lowercase base32 from RFC4648, with padding.--- https://datatracker.ietf.org/doc/html/rfc4648.html#section-6-base32lower :: BitwiseDigits-base32lower = mkBaseSystem32 "base32lower" (Just '=') ABCs.b32lower---- | Defines lowercase base32 from RFC4648. No padding!--- https://datatracker.ietf.org/doc/html/rfc4648.html#section-6-base32lowerNP :: BitwiseDigits-base32lowerNP = mkBaseSystem32 "base32lowerNP" Nothing ABCs.b32lower---- | Defines uppercase base32 from RFC4648, with padding.--- https://datatracker.ietf.org/doc/html/rfc4648.html#section-6-base32upper :: BitwiseDigits-base32upper = mkBaseSystem32 "base32upper" (Just '=') ABCs.b32upper---- | Defines uppercase base32 from RFC4648. No padding!--- https://datatracker.ietf.org/doc/html/rfc4648.html#section-6-base32upperNP :: BitwiseDigits-base32upperNP = mkBaseSystem32 "base32upperNP" Nothing ABCs.b32upper---- | Defines lowercase base32 from RFC4648, extended hex version, with padding.--- https://datatracker.ietf.org/doc/html/rfc4648.html#section-7-base32hexlower :: BitwiseDigits-base32hexlower = mkBaseSystem32 "base32hexlower" (Just '=') ABCs.b32hexlower---- | Defines lowercase base32 from RFC4648, extended hex version. No padding!--- https://datatracker.ietf.org/doc/html/rfc4648.html#section-7-base32hexlowerNP :: BitwiseDigits-base32hexlowerNP = mkBaseSystem32 "base32hexlowerNP" Nothing ABCs.b32hexlower---- | Defines lowercase base32 from RFC4648, extended hex version, with padding.--- https://datatracker.ietf.org/doc/html/rfc4648.html#section-7-base32hexupper :: BitwiseDigits-base32hexupper = mkBaseSystem32 "base32hexupper" (Just '=') ABCs.b32hexupper---- | Defines uppercase base32 from RFC4648, extended hex version. No padding!--- https://datatracker.ietf.org/doc/html/rfc4648.html#section-7-base32hexupperNP :: BitwiseDigits-base32hexupperNP = mkBaseSystem32 "base32hexupperNP" Nothing ABCs.b32hexupper---- | Defines base64 from RFC4648, with padding.--- https://datatracker.ietf.org/doc/html/rfc4648.html#section-4-base64 :: BitwiseDigits-base64 = mkBaseSystem64 "base64" (Just '=') ABCs.b64---- | Defines base64 from RFC4648. No padding!--- https://datatracker.ietf.org/doc/html/rfc4648.html#section-4-base64NP :: BitwiseDigits-base64NP = mkBaseSystem64 "base64NP" Nothing ABCs.b64---- | Defines URL safe base64 from RFC4648, with padding.--- https://datatracker.ietf.org/doc/html/rfc4648.html#section-5-base64url :: BitwiseDigits-base64url = mkBaseSystem64 "base64url" (Just '=') ABCs.b64url---- | Defines URL safe base64 from RFC4648. No padding!--- https://datatracker.ietf.org/doc/html/rfc4648.html#section-5-base64urlNP :: BitwiseDigits-base64urlNP = mkBaseSystem64 "base64urlNP" Nothing ABCs.b64url
− basesystems/Data/BaseSystem/DigitTranscoder.hs
@@ -1,226 +0,0 @@--- | Module : Data.BaseSystem.DigitTranscoder--- Description : Implements BaseSystems for strict ByteStrings.--- Copyright : Zoey McBride (c) 2026--- License : BSD-3-Clause--- Maintainer : zoeymcbride@mailbox.org--- Stability : experimental-module Data.BaseSystem.DigitTranscoder- ( DigitTranscoder (digitEncoder, digitDecoder),- Encoder,- Decoder,- )-where--import Control.Monad (foldM)-import Data.BaseSystem.Alphabet (Alphabet)-import Data.BaseSystem.Alphabet qualified as Alpha-import Data.BaseSystem.BinaryTranscoder (BinaryTranscoder, BitSet)-import Data.BaseSystem.BinaryTranscoder qualified as BXC-import Data.BaseSystem.DigitSystem-import Data.BaseSystem.Internal-import Data.Bits ((.&.), (.<<.), (.>>.), (.|.))-import Data.Maybe (fromJust, fromMaybe)-import Data.Text (Text)-import Data.Text qualified as Text---- | Functor applies when in context of a BaseSystem decoder implementation that--- requires modifications to the entire bitset before converting to ByteString.-type FinalizeContext = Maybe (BitSet -> BitSet)---- | Builds symbols from a number in decoding.--- https://en.wikipedia.org/wiki/Finite-state_machine#Mathematical_model-type DeltaFunction = BitSet -> Alpha.Symbol -> Maybe BitSet---- | Using a transition function `delta`, build an BitSet into bytes data--- `bxc`, and use a final BitSet context to align the BitSet's bit contents.-{-# INLINE binaryDecoder #-}-binaryDecoder ::- (BinaryTranscoder bxc) =>- Text ->- FinalizeContext ->- DeltaFunction ->- Maybe bxc-binaryDecoder text finalize delta- | Text.null text = Nothing- | otherwise = do- -- For each symbol, build the bitvalue using the delta function if the- -- symbol is valid.- bitvalue <- foldM delta 0 $ Text.unpack text- -- Apply the finalizer to the bits and put it into a transcoder- return $ BXC.packValue (applyFinalize bitvalue)- where- -- If finalize exists, apply it, otherwise return the unchanged value.- applyFinalize value = fromMaybe value $ finalize <*> Just value---- | Type alias for curried DigitTranscoder digitEncoder function.-type Encoder t = t -> Text---- | Type alias for curried DigitTranscoder digitDecoder function.-type Decoder t = Text -> Maybe t---- | Type-class (interface) for implementing encode/decode functionality for--- some data structure w/ Alphabet `a`.-class (BinaryTranscoder t) => DigitTranscoder a t where- -- | Method signature for encoding some ByteString to String of digits.- digitEncoder :: a -> t -> Text-- -- | Method signature for decoding some String of digits to ByteString, given- -- all digits in String are valid Symbols.- digitDecoder :: a -> Text -> Maybe t---- | Implements BaseSystem for number systems built by modular arithmetic w/ the--- radix.-instance (BinaryTranscoder t) => DigitTranscoder RadixDigits t where- -- Encodes the value by converting the ByteString to Integer and repeatedly- -- applying `divMod` until the quotient is zero.- digitEncoder (RadixDigits _ abc) =- let radix = fromIntegral $ Alpha.alphaRadix abc- in Text.pack- . divModSymbols- . takeWhile divModContinue- . iterateInit (\(num, _) -> num `divMod` radix) mkDivMod- . BXC.unpackValue- where- -- Initial value to iterate on divMod.- mkDivMod numerator = (numerator, 0)- -- Predicates divMod iteration results.- divModContinue (nextinput, curresult) = nextinput > 0 || curresult > 0- -- Resolves the symbols from the result of iterating divMod.- divModSymbols =- reverse- . fromJust- . mapM (\(_, digit) -> Alpha.resolveSymbol abc $ fromIntegral digit)- . replaceNull [(undefined, 0)]-- -- Decodes the value by multiplying the radix with the current integer state- -- and adding the value. That value is decode directly, no finalization- -- needed.- digitDecoder (RadixDigits _ abc) text =- let radix = fromIntegral $ Alpha.alphaRadix abc- in binaryDecoder text Nothing $- \curvalue symbol -> do- value <- fromIntegral <$> Alpha.resolveValue abc symbol- return $- curvalue * radix + value---- | Implements encoder/decoder for a BitwiseDigits.-instance (BinaryTranscoder t) => DigitTranscoder BitwiseDigits t where- -- Encodes the input bytes by grouping into `groupsize` sized windows, then- -- batch resolving the String of Symbols from each group, then resolve the- -- correct padding chars from the number of bytes and append that to the- -- encoder result.- digitEncoder (BitwiseDigits _ abc symbits groupsize _ padmethod) input =- let -- Total # of bytes from input.- bytestotal = BXC.lengthBytes input- -- Total # of bits from input.- bitstotal = fromIntegral (8 * bytestotal) :: Double- -- Actual # of symbols for the # of bits in bytes.- numsymbols = ceiling (bitstotal / fromIntegral symbits)- -- Minimum # of symbols to put in the resulting string.- putsymbols = max 2 numsymbols- in paddingAppend (fromIntegral bytestotal)- . take putsymbols- . concatMap groupSyms- . takeWhile (\(group, _) -> BXC.lengthBytes group > 0)- . iterateInit (\(_, rest) -> BXC.splitAtByte groupsize rest) mkSplit- $ minimalBytes bytestotal- where- -- Inits the iteration for Bytes.splitAt.- mkSplit initial = (BXC.empty, initial)- -- Gives the minimal amount of bytes to produce a correct encoding. It's- -- here to maintain compatablity with other encoders.- {-# INLINE minimalBytes #-}- minimalBytes bytestotal- | bytestotal == 0 = BXC.packBytes [0, 0]- | bytestotal == 1 = BXC.pushByteEnd input 0- | bytesmodulus /= 0 = BXC.append input (BXC.replicateByte zeros 0)- | otherwise = input- where- bytesmodulus = fromIntegral (bytestotal `mod` fromIntegral groupsize)- zeros = groupsize - bytesmodulus- -- Gets the symbols for the byte group.- groupSyms (group, _) = groupSymbols abc group groupsize symbits- -- Appends the padding chars to the end of a list of symbols, when the- -- padding method exists and gives back a string for the bytestotal- paddingAppend bytestotal syms =- let padapply = paddingResolve <$> padmethod <*> Just bytestotal- in Text.append (Text.pack syms) (fromMaybe Text.empty padapply)-- -- Decodes the text by filtering out padding resolving each symbol and then- -- shifting the value by the #bits per symbol and ORing the value in place.- -- Finally, it needs to be aligned to the top of byte in memory so the value- -- is represented correctly.- digitDecoder (BitwiseDigits _ abc symbits _ groupsyms padmethod) text =- let -- Gives just padding char if not nothing.- padsym = paddingChar <$> padmethod- -- Removes the trailing padding chars from the Text of str.- notrailing = Text.dropWhileEnd (\sym -> Just sym == padsym) text- -- Gives the total available characters.- totalsyms = Text.length notrailing- -- Finds the needs # of symbols for the final padding chars.- needsyms = groupsyms - (totalsyms `mod` groupsyms)- in binaryDecoder notrailing (finalizeBits needsyms) $- \curvalue symbol ->- if Just symbol == padsym- -- When the symbol is the padding char, shift an empty value- then return (curvalue .<<. symbits)- -- Otherwise put the value in place- else do- value <- fromIntegral <$> Alpha.resolveValue abc symbol- return $ (curvalue .<<. symbits) .|. value- where- -- Finds the value for the expected amount of padding regardless if the- -- BaseSystem has a PaddingMethod or not.- {-# INLINE finalizeBits #-}- finalizeBits needsyms- -- If the # of Symbols in last group is the groupsyms size, pass- | needsyms == groupsyms = Nothing- -- Find the needed # of bits and align the output to first byte.- | otherwise = Just $ \final -> final .<<. needbits .>>. pagealign- where- -- Gives the number of bits to correct in missing final padding chars.- needbits = needsyms * symbits- -- Offset to Integer to align the bits in the final ByteString at the- -- start of a byte in memory.- pagealign = 8 * ceiling (fromIntegral needbits / 8 :: Double)---- | Resolves symbols from Alphabet for a BitwiseSystem's encoder. Partitions--- a ByteString into N sized bitgroups where N is the bitwidth of the--- Alphabet's radix. *IMPORTANT*: this function requires the groupsize to be--- a multiple of two because it generates a mask from subtracting it by 1.-groupSymbols ::- (BinaryTranscoder bxc) => Alphabet -> bxc -> Int -> Int -> [Alpha.Symbol]-groupSymbols abc grouping groupsize symbits =- let groupint =- case BXC.unpackValue grouping of- bits- | fitsBitGroup groupsize grouping bits -> fromIntegral bits- | otherwise -> error "invalid group size"- in -- Crash if the implementation isn't complete- fromJust- -- Extract the value from the shift and resolve its symbol.- . mapM (Alpha.resolveSymbol abc . valueExtract . nextInt groupint)- -- Take all non-zero shifts.- . takeWhile (>= 0)- -- Generate a list of shift values from the # bits in groupbytes.- $ iterateInit shiftValue mkBitLength grouping- where- -- Gets the next int to extract group.- nextInt groupint shift = groupint .>>. shift- -- Extracts the first group from LSB from an Int.- valueExtract int = fromIntegral $ int .&. (Alpha.alphaRadix abc - 1)- -- Finds the length in bits of a ByteString.- mkBitLength bstr = fromIntegral $ 8 * BXC.lengthBytes bstr- -- Gives the current shift value in iteration.- shiftValue bitstotal = bitstotal - symbits---- Checks if an Integral a fits within a BitwiseSystem's group.-{-# INLINE fitsBitGroup #-}-fitsBitGroup :: (BinaryTranscoder bxc, Integral i) => Int -> bxc -> i -> Bool-fitsBitGroup groupsize bxcdata groupbits =- BXC.lengthBytes bxcdata <= fromIntegral groupsize- && minInt <= groupbits- && groupbits <= maxInt- where- minInt = fromIntegral (minBound :: Int)- maxInt = fromIntegral (maxBound :: Int)
− basesystems/Data/BaseSystem/Internal.hs
@@ -1,25 +0,0 @@--- | Module : Data.BaseSystem.Internal--- Description : Provides common resources for multibase--- Copyright : Zoey McBride (c) 2026--- License : BSD-3-Clause--- Maintainer : zoeymcbride@mailbox.org--- Stability : experimental------ Common data definitions and helper functions for this library.-module Data.BaseSystem.Internal- ( -- * List utilities- replaceNull,- iterateInit,- )-where---- | Return the the first param if the second param is empty.-{-# INLINE replaceNull #-}-replaceNull :: [a] -> [a] -> [a]-replaceNull replace xs- | null xs = replace- | otherwise = xs---- | Wraps iterate to compose with an intermediary type constructor.-iterateInit :: (b -> b) -> (a -> b) -> a -> [b]-iterateInit f iterinit = drop 1 . iterate f . iterinit
− basesystems/Data/BaseSystem/Lazy.hs
@@ -1,47 +0,0 @@--- | Module : Data.BaseSystem.Lazy--- Description : Implements BaseSystems for LazyByteString.--- Copyright : Zoey McBride (c) 2026--- License : BSD-3-Clause--- Maintainer : zoeymcbride@mailbox.org--- Stability : experimental-module Data.BaseSystem.Lazy- ( -- * BaseSystems on LazyByteString.- BaseSystem (encoder, decoder),-- -- * Function signatures aliases.- Encoder,- Decoder,-- -- * ByteString implementation for this file.- ByteStr,- )-where--import Data.BaseSystem.DigitSystem (BitwiseDigits, RadixDigits)-import Data.BaseSystem.DigitTranscoder qualified as DXC-import Data.ByteString.Lazy (LazyByteString)-import Data.Text (Text)---- | Sets the bytestring type to use in this file.-type ByteStr = LazyByteString---- | Alias for curried encoder method.-type Encoder = ByteStr -> Text---- | Alias for curried decoder method.-type Decoder = Text -> Maybe ByteStr---- | Configures the BaseSystem for this file's ByteType.-class BaseSystem a where- encoder :: a -> ByteStr -> Text- decoder :: a -> Text -> Maybe ByteStr---- | BaseSystem for LazyByteString.-instance BaseSystem RadixDigits where- encoder = DXC.digitEncoder- decoder = DXC.digitDecoder---- | BaseSystem for LazyByteString.-instance BaseSystem BitwiseDigits where- encoder = DXC.digitEncoder- decoder = DXC.digitDecoder
− basesystems/Data/BaseSystem/Strict.hs
@@ -1,47 +0,0 @@--- | Module : Data.BaseSystem.Strict--- Description : Implements BaseSystems for strict ByteStrings.--- Copyright : Zoey McBride (c) 2026--- License : BSD-3-Clause--- Maintainer : zoeymcbride@mailbox.org--- Stability : experimental-module Data.BaseSystem.Strict- ( -- * BaseSystems on ByteString- BaseSystem (encoder, decoder),-- -- * Function signatures aliases.- Encoder,- Decoder,-- -- * ByteString implementation for this file- ByteStr,- )-where--import Data.BaseSystem.DigitSystem (BitwiseDigits, RadixDigits)-import Data.BaseSystem.DigitTranscoder qualified as DXC-import Data.ByteString (ByteString)-import Data.Text (Text)---- | Sets the bytestring type to use in this file.-type ByteStr = ByteString---- | Alias for curried encoder method.-type Encoder = ByteStr -> Text---- | Alias for curried decoder method.-type Decoder = Text -> Maybe ByteStr---- | Configures the BaseSystem for this file's ByteType.-class BaseSystem a where- encoder :: a -> ByteStr -> Text- decoder :: a -> Text -> Maybe ByteStr---- | BaseSystem for ByteString.-instance BaseSystem RadixDigits where- encoder = DXC.digitEncoder- decoder = DXC.digitDecoder---- | BaseSystem for ByteString.-instance BaseSystem BitwiseDigits where- encoder = DXC.digitEncoder- decoder = DXC.digitDecoder
+ basesystems/Data/BaseSystems.hs view
@@ -0,0 +1,51 @@+-- | Module : Data.BaseSystems+-- Description : Implements BaseSystems for ShortByteString.+-- Copyright : Zoey McBride (c) 2026+-- License : BSD-3-Clause+-- Maintainer : zoeymcbride@mailbox.org+-- Stability : experimental+module Data.BaseSystems+ ( -- * BaseSystems on ShortByteString.+ BaseSystem (encoder, decoder),++ -- * Function signatures aliases.+ Encoder,+ Decoder,++ -- * ByteString implementation for this file.+ ByteStr,++ -- * Exported basesystem defintions+ module Data.BaseSystems.DigitSystems,+ )+where++import Data.BaseSystems.DigitSystems+import Data.BaseSystems.DigitTranscoder (BitwiseDigits, RadixDigits)+import Data.BaseSystems.DigitTranscoder qualified as DXC+import Data.ByteString.Short (ShortByteString)+import Data.Text (Text)++-- | Sets the bytestring type to use in this file.+type ByteStr = ShortByteString++-- | Alias for curried encoder method.+type Encoder = ByteStr -> Text++-- | Alias for curried decoder method.+type Decoder = Text -> Maybe ByteStr++-- | Configures the BaseSystem for this file's ByteType.+class BaseSystem a where+ encoder :: a -> ByteStr -> Text+ decoder :: a -> Text -> Maybe ByteStr++-- | BaseSystem for ShortByteString.+instance BaseSystem RadixDigits where+ encoder = DXC.digitEncoder+ decoder = DXC.digitDecoder++-- | BaseSystem for ShortByteString.+instance BaseSystem BitwiseDigits where+ encoder = DXC.digitEncoder+ decoder = DXC.digitDecoder
+ basesystems/Data/BaseSystems/Alphabet.hs view
@@ -0,0 +1,177 @@+-- | Module : Data.BaseSystems.Alphabet+-- Description : Functions for Alphabets on BaseSystems+-- Copyright : Zoey McBride (c) 2026+-- License : BSD-3-Clause+-- Maintainer : zoeymcbride@mailbox.org+-- Stability : experimental+--+-- Implements digit alphabets for BaseSystems.+module Data.BaseSystems.Alphabet+ ( -- * Symbolic representation of a value+ Symbol,++ -- * The value that's given to each digit+ Value,++ -- * Abstract Alphabet and functions+ Alphabet (alphaRadix, alphaHashId),+ mkAlphabet,+ resolveValue,+ resolveSymbol,++ -- * Available Alphabets+ binary,+ decimal,+ hexlower,+ hexupper,+ btc58,+ b32lower,+ b32upper,+ b32hexlower,+ b32hexupper,+ b64,+ b64url,+ )+where++import Data.Array.IArray qualified as Array+import Data.Array.Unboxed (UArray, (!?))+import Data.Char (ord)+import Data.Function (on)+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Text (Text)+import Data.Text qualified as Text+import Data.Word (Word32)++-- TODO: Another module for alphabets with symbols of multiple unicode code+-- points should be made. Then a DigitSystem can be built off that for+-- basesystems of multipoint symbols.++-- | Represents a symbol to act as a digit in a numbering system.+type Symbol = Char++-- | The value of a symbol in a number system.+type Value = Word32++-- | Stores the String hash for an Alphabet. Used for comparision.+type HashId = Int++-- | Represents a two-way mapping between symbol's values and values's symbols.+data Alphabet = Alphabet+ { alphaRadix :: Int,+ alphaHashId :: HashId,+ alphaValues :: UArray Value Symbol,+ alphaSymbols :: Map Symbol Value+ }++instance Eq Alphabet where+ (==) = (==) `on` alphaHashId++instance Ord Alphabet where+ compare = compare `on` alphaHashId++-- | Implements https://cp-algorithms.com/string/string-hashing.html+mkHashId :: Text -> HashId+mkHashId = snd . Text.foldr buildPolynomial (0 :: Integer, 0 :: HashId)+ where+ -- Should be around of characters in any given alphabet+ baseprime = 53+ -- Large prime to prevent most collisions (1/modprime chance)+ modprime = 1_000_009+ -- Produces the term for a given character+ buildPolynomial ch (expvalue, polysum) =+ let term = baseprime ^ expvalue * ord ch+ next = polysum + term+ in (expvalue + 1, next `mod` modprime)++-- | Creates an Alphabet from Text using its Chars as Symbols.+mkAlphabet :: Text -> Alphabet+mkAlphabet text =+ let symbols = Text.unpack text+ numsymbols = Text.length text+ maxdigit = fromIntegral $ numsymbols - 1+ in Alphabet+ { alphaRadix = numsymbols,+ alphaHashId = mkHashId text,+ alphaValues = Array.listArray (0, maxdigit) symbols,+ alphaSymbols = Map.fromList $ zip symbols [0 ..]+ }++-- | /O(log n)/ Find a value from a symbol in an alphabet.+resolveValue :: Alphabet -> Symbol -> Maybe Value+resolveValue abc symbol = Map.lookup symbol $ alphaSymbols abc++-- | /O(1)/ Find a symbol from a value in an alphabet.+resolveSymbol :: Alphabet -> Value -> Maybe Symbol+resolveSymbol abc value = alphaValues abc !? value++-- | Constructs an Alphabet from a list of digit symbol strings.+mk :: [[Char]] -> Alphabet+mk = mkAlphabet . Text.pack . concat++-- | Defines base 2.+binary :: Alphabet+binary = mk ["01"]++-- | Defines base 10.+decimal :: Alphabet+decimal = mk ["0", ['1' .. '9']]++-- | Defines base 16 upper-case.+hexupper :: Alphabet+hexupper = mk ["0", ['1' .. '9'], ['A' .. 'F']]++-- | Defines base 16 lower-case.+hexlower :: Alphabet+hexlower = mk ["0", ['1' .. '9'], ['a' .. 'f']]++-- | Defines base 58, bitcoin variant.+btc58 :: Alphabet+btc58 =+ mk+ [ ['1' .. '9'],+ ['A' .. 'H'],+ ['J' .. 'N'],+ ['P' .. 'Z'],+ ['a' .. 'k'],+ ['m' .. 'z']+ ]++-- | Defines base32, upper-case.+b32upper :: Alphabet+b32upper = mk [['A' .. 'Z'], ['2' .. '7']]++-- | Defines base32, lower-case.+b32lower :: Alphabet+b32lower = mk [['a' .. 'z'], ['2' .. '7']]++-- | Defines base32 hexadecimal, upper-case.+b32hexupper :: Alphabet+b32hexupper = mk ["0", ['1' .. '9'], ['A' .. 'V']]++-- | Defines base32 hexadecimal, lower-case.+b32hexlower :: Alphabet+b32hexlower = mk ["0", ['1' .. '9'], ['a' .. 'v']]++-- | Defines default base64 with + and /.+b64 :: Alphabet+b64 =+ mk+ [ ['A' .. 'Z'],+ ['a' .. 'z'],+ "0",+ ['1' .. '9'],+ ['+', '/']+ ]++-- | Defines URL-safe base64 with - and _.+b64url :: Alphabet+b64url =+ mk+ [ ['A' .. 'Z'],+ ['a' .. 'z'],+ "0",+ ['1' .. '9'],+ ['-', '_']+ ]
+ basesystems/Data/BaseSystems/DigitSystems.hs view
@@ -0,0 +1,145 @@+-- | Module : Data.BaseSystems.DigitSystems+-- Description : Implements DigitSystems for DigitTranscoders+-- Copyright : Zoey McBride (c) 2026+-- License : BSD-3-Clause+-- Maintainer : zoeymcbride@mailbox.org+-- Stability : experimental+module Data.BaseSystems.DigitSystems+ ( base2,+ base10,+ base16lower,+ base16upper,+ base32lower,+ base32lowerNP,+ base32upper,+ base32upperNP,+ base32hexlower,+ base32hexlowerNP,+ base32hexupper,+ base32hexupperNP,+ base58btc,+ base64,+ base64NP,+ base64url,+ base64urlNP,+ )+where++import Data.BaseSystems.Alphabet (Alphabet)+import Data.BaseSystems.Alphabet qualified as ABCs+import Data.BaseSystems.DigitTranscoder+import Data.Text qualified as Text++-- | Provides methods for building a base32 system, with an option to toggle+-- padding.+mkBaseSystem32 :: String -> Maybe Char -> Alphabet -> BitwiseDigits+mkBaseSystem32 name padchar abc =+ BitwiseDigits name abc symbits groupbytes groupsymbols $+ PaddingMethod <*> padImpl <$> padchar+ where+ groupbytes = 5+ groupsymbols = 8+ symbits = 5+ padImpl padding bytestotal+ | bytestotal > 5 = padImpl padding $ bytestotal `mod` symbits+ | bytestotal == 4 = Text.singleton padding+ | bytestotal == 3 = Text.pack (replicate 3 padding)+ | bytestotal == 2 = Text.pack (replicate 4 padding)+ | bytestotal == 1 = Text.pack (replicate 6 padding)+ | otherwise = Text.empty++-- | Provides methods for building a base64 system, with an option to toggle+-- padding.+mkBaseSystem64 :: String -> Maybe Char -> Alphabet -> BitwiseDigits+mkBaseSystem64 name padchar abc =+ BitwiseDigits name abc symbits groupbytes groupsymbols $+ PaddingMethod <*> padImpl <$> padchar+ where+ groupbytes = 3+ groupsymbols = 4+ symbits = 6+ padImpl padding bytestotal+ | bytestotal > 3 = padImpl padding $ bytestotal `mod` groupbytes+ | bytestotal == 2 = Text.singleton padding+ | bytestotal == 1 = Text.pack [padding, padding]+ | otherwise = Text.empty++-- | Defines the BaseSystem for binary.+base2 :: RadixDigits+base2 = RadixDigits "base2" ABCs.binary++-- | Defines base10 as RadixDigits.+base10 :: RadixDigits+base10 = RadixDigits "base10" ABCs.decimal++-- | Defines Bitcoin's base58 implementation.+base58btc :: RadixDigits+base58btc = RadixDigits "base58btc" ABCs.btc58++-- | Defines uppercase hexidecimal on BitwiseDigits.+base16upper :: RadixDigits+base16upper = RadixDigits "base16upper" ABCs.hexupper++-- | Defines lowercase hexadecimal on BitwiseDigits.+base16lower :: RadixDigits+base16lower = RadixDigits "base16lower" ABCs.hexlower++-- | Defines lowercase base32 from RFC4648, with padding.+-- https://datatracker.ietf.org/doc/html/rfc4648.html#section-6+base32lower :: BitwiseDigits+base32lower = mkBaseSystem32 "base32lower" (Just '=') ABCs.b32lower++-- | Defines lowercase base32 from RFC4648. No padding!+-- https://datatracker.ietf.org/doc/html/rfc4648.html#section-6+base32lowerNP :: BitwiseDigits+base32lowerNP = mkBaseSystem32 "base32lowerNP" Nothing ABCs.b32lower++-- | Defines uppercase base32 from RFC4648, with padding.+-- https://datatracker.ietf.org/doc/html/rfc4648.html#section-6+base32upper :: BitwiseDigits+base32upper = mkBaseSystem32 "base32upper" (Just '=') ABCs.b32upper++-- | Defines uppercase base32 from RFC4648. No padding!+-- https://datatracker.ietf.org/doc/html/rfc4648.html#section-6+base32upperNP :: BitwiseDigits+base32upperNP = mkBaseSystem32 "base32upperNP" Nothing ABCs.b32upper++-- | Defines lowercase base32 from RFC4648, extended hex version, with padding.+-- https://datatracker.ietf.org/doc/html/rfc4648.html#section-7+base32hexlower :: BitwiseDigits+base32hexlower = mkBaseSystem32 "base32hexlower" (Just '=') ABCs.b32hexlower++-- | Defines lowercase base32 from RFC4648, extended hex version. No padding!+-- https://datatracker.ietf.org/doc/html/rfc4648.html#section-7+base32hexlowerNP :: BitwiseDigits+base32hexlowerNP = mkBaseSystem32 "base32hexlowerNP" Nothing ABCs.b32hexlower++-- | Defines lowercase base32 from RFC4648, extended hex version, with padding.+-- https://datatracker.ietf.org/doc/html/rfc4648.html#section-7+base32hexupper :: BitwiseDigits+base32hexupper = mkBaseSystem32 "base32hexupper" (Just '=') ABCs.b32hexupper++-- | Defines uppercase base32 from RFC4648, extended hex version. No padding!+-- https://datatracker.ietf.org/doc/html/rfc4648.html#section-7+base32hexupperNP :: BitwiseDigits+base32hexupperNP = mkBaseSystem32 "base32hexupperNP" Nothing ABCs.b32hexupper++-- | Defines base64 from RFC4648, with padding.+-- https://datatracker.ietf.org/doc/html/rfc4648.html#section-4+base64 :: BitwiseDigits+base64 = mkBaseSystem64 "base64" (Just '=') ABCs.b64++-- | Defines base64 from RFC4648. No padding!+-- https://datatracker.ietf.org/doc/html/rfc4648.html#section-4+base64NP :: BitwiseDigits+base64NP = mkBaseSystem64 "base64NP" Nothing ABCs.b64++-- | Defines URL safe base64 from RFC4648, with padding.+-- https://datatracker.ietf.org/doc/html/rfc4648.html#section-5+base64url :: BitwiseDigits+base64url = mkBaseSystem64 "base64url" (Just '=') ABCs.b64url++-- | Defines URL safe base64 from RFC4648. No padding!+-- https://datatracker.ietf.org/doc/html/rfc4648.html#section-5+base64urlNP :: BitwiseDigits+base64urlNP = mkBaseSystem64 "base64urlNP" Nothing ABCs.b64url
+ basesystems/Data/BaseSystems/DigitTranscoder.hs view
@@ -0,0 +1,280 @@+-- | Module : Data.BaseSystems.DigitTranscoder+-- Description : Implements BaseSystems for strict ByteStrings.+-- Copyright : Zoey McBride (c) 2026+-- License : BSD-3-Clause+-- Maintainer : zoeymcbride@mailbox.org+-- Stability : experimental+module Data.BaseSystems.DigitTranscoder+ ( -- * Transcoder interface for encoding/decoding digits from Text.+ DigitTranscoder (digitEncoder, digitDecoder),+ Encoder,+ Decoder,++ -- * Instances of DigitTranscoder+ RadixDigits (RadixDigits),+ BitwiseDigits (BitwiseDigits),++ -- * Strategy for adding padding for BitwiseDigits in post-processing.+ PaddingMethod (PaddingMethod, paddingChar, paddingResolve),+ )+where++import Control.Monad (foldM)+import Data.BaseSystems.Alphabet (Alphabet)+import Data.BaseSystems.Alphabet qualified as Alpha+import Data.BaseSystems.Internal+import Data.Bits ((.&.), (.<<.), (.>>.), (.|.))+import Data.Maybe (fromJust, fromMaybe)+import Data.Text (Text)+import Data.Text qualified as Text+import Data.XCodec.BinaryTranscoder (BinaryTranscoder, BitSet)+import Data.XCodec.BinaryTranscoder qualified as BXC+import Data.XCodec.StreamTranscoder (StreamTranscoder (..))+import Data.XCodec.StreamTranscoder qualified as SXC++-- | Implements BaseSystem over base radix modulus.+data RadixDigits = RadixDigits+ { -- | Used to implement instance of Show.+ radixShow :: String,+ -- | Alphabet for the base system.+ radixAlpha :: Alphabet+ }++instance Show RadixDigits where+ show = radixShow++-- | Implements padding for encoding BitwiseDigits.+data PaddingMethod = PaddingMethod+ { -- | Gives the char to use as padding.+ paddingChar :: Char,+ -- | Provides the callback to resolve the padding from # of bytes.+ paddingResolve :: Int -> Text+ }++-- | Implements BaseSystem over series of bit groups.+data BitwiseDigits = BitwiseDigits+ { -- | String used to implement instance of Show.+ bitwiseShow :: String,+ -- | Base system alphabet.+ bitwiseAlpha :: Alphabet,+ -- | # of bits per symbol+ bitwiseSymBits :: Int,+ -- | # of bytes to be processed into symbols in encoding.+ bitwiseGroupBytes :: Int,+ -- | # of bytes forming a group to find padding in decoding.+ bitwiseGroupSymbols :: Int,+ -- | Gives the method for padding.+ bitwisePadMethod :: Maybe PaddingMethod+ }++instance Show BitwiseDigits where+ show = bitwiseShow++-- | Type-alias for curried DigitTranscoder digitEncoder function.+type Encoder bxc = SXC.Encoder bxc Text++-- | Type-alias for curried DigitTranscoder digitDecoder function.+type Decoder bxc = SXC.Decoder bxc Text++-- TODO: docstrings+class (BinaryTranscoder bxc) => DigitTranscoder codec bxc where+ digitEncoder :: codec -> Encoder bxc+ digitDecoder :: codec -> Decoder bxc++-- TODO: docstrings+instance (BinaryTranscoder bxc) => DigitTranscoder RadixDigits bxc where+ digitEncoder = streamEncoder+ digitDecoder = streamDecoder++-- TODO: docstrings+instance (BinaryTranscoder bxc) => DigitTranscoder BitwiseDigits bxc where+ digitEncoder = streamEncoder+ digitDecoder = streamDecoder++-- | Functor applies when in context of a BaseSystem decoder implementation that+-- requires modifications to the entire bitset before converting to ByteString.+type FinalizeContext = Maybe (BitSet -> BitSet)++-- | Builds symbols from a number in decoding.+-- https://en.wikipedia.org/wiki/Finite-state_machine#Mathematical_model+type DeltaFunction = BitSet -> Alpha.Symbol -> Maybe BitSet++-- | Using a transition function `delta`, build an BitSet into bytes data+-- `bxc`, and use a final BitSet context to align the BitSet's bit contents.+{-# INLINE binaryDecoder #-}+binaryDecoder ::+ (BinaryTranscoder bxc) =>+ Text ->+ FinalizeContext ->+ DeltaFunction ->+ Maybe bxc+binaryDecoder text finalize delta+ | Text.null text = Nothing+ | otherwise = do+ -- For each symbol, build the bitvalue using the delta function if the+ -- symbol is valid.+ bitvalue <- foldM delta 0 $ Text.unpack text+ -- Apply the finalizer to the bits and put it into a transcoder+ -- TODO: make endianness a parameter of the decoder+ return $ BXC.packValueBE (applyFinalize bitvalue)+ where+ -- If finalize exists, apply it, otherwise return the unchanged value.+ applyFinalize value = fromMaybe value $ finalize <*> Just value++-- | Implements transcoding binary streams into radix-based number systems, such+-- as base10 or base58btc.+instance (BinaryTranscoder bxc) => StreamTranscoder RadixDigits bxc Text where+ -- Encodes the value by converting the ByteString to Integer and repeatedly+ -- applying `divMod` until the quotient is zero.+ streamEncoder (RadixDigits _ abc) =+ let radix = fromIntegral $ Alpha.alphaRadix abc+ in Text.pack+ . divModSymbols+ . takeWhile divModContinue+ . iterateInit (\(num, _) -> num `divMod` radix) mkDivMod+ . BXC.unpackValueBE+ where+ -- Initial value to iterate on divMod.+ mkDivMod numerator = (numerator, 0)+ -- Predicates divMod iteration results.+ divModContinue (nextinput, curresult) = nextinput > 0 || curresult > 0+ -- Resolves the symbols from the result of iterating divMod.+ divModSymbols =+ reverse+ . fromJust+ . mapM (\(_, digit) -> Alpha.resolveSymbol abc $ fromIntegral digit)+ . replaceNull [(undefined, 0)]++ -- Decodes the value by multiplying the radix with the current integer state+ -- and adding the value. That value is decode directly, no finalization+ -- needed.+ streamDecoder (RadixDigits _ abc) text =+ let radix = fromIntegral $ Alpha.alphaRadix abc+ in binaryDecoder text Nothing $+ \curvalue symbol -> do+ value <- fromIntegral <$> Alpha.resolveValue abc symbol+ return $+ curvalue * radix + value++-- | Implements transcoding binary streams into bitwise based number systems,+-- such as base64 and base32 that get digit symbols from grouping bits together.+instance (BinaryTranscoder bxc) => StreamTranscoder BitwiseDigits bxc Text where+ -- Encodes the input bytes by grouping into `groupsize` sized windows, then+ -- batch resolving the String of Symbols from each group, then resolve the+ -- correct padding chars from the number of bytes and append that to the+ -- encoder result.+ streamEncoder (BitwiseDigits _ abc symbits groupsize _ padmethod) input =+ let -- Total # of bytes from input.+ bytestotal = BXC.totalOctets input+ -- Total # of bits from input.+ bitstotal = fromIntegral (8 * bytestotal) :: Double+ -- Actual # of symbols for the # of bits in bytes.+ numsymbols = ceiling (bitstotal / fromIntegral symbits)+ -- Minimum # of symbols to put in the resulting string.+ putsymbols = max 2 numsymbols+ in paddingAppend (fromIntegral bytestotal)+ . take putsymbols+ . concatMap groupSyms+ . takeWhile (\(group, _) -> BXC.totalOctets group > 0)+ . iterateInit (\(_, rest) -> BXC.splitOffset groupsize rest) mkSplit+ $ minimalBytes bytestotal+ where+ -- Inits the iteration for splitOffset.+ mkSplit initial = (mempty, initial)+ -- Gives the minimal amount of bytes to produce a correct encoding. It's+ -- here to maintain compatablity with other encoders.+ {-# INLINE minimalBytes #-}+ minimalBytes bytestotal+ | bytestotal == 0 = BXC.packOctets [0, 0]+ | bytestotal == 1 = BXC.pushOctetEnd input 0+ | bytesmodulus /= 0 = input `mappend` BXC.replicateOctet numzeros 0+ | otherwise = input+ where+ bytesmodulus = fromIntegral (bytestotal `mod` fromIntegral groupsize)+ numzeros = groupsize - bytesmodulus+ -- Gets the symbols for the byte group.+ groupSyms (group, _) = groupSymbols abc group groupsize symbits+ -- Appends the padding chars to the end of a list of symbols, when the+ -- padding method exists and gives back a string for the bytestotal+ paddingAppend bytestotal syms =+ let padapply = paddingResolve <$> padmethod <*> Just bytestotal+ in Text.append (Text.pack syms) (fromMaybe Text.empty padapply)++ -- Decodes the text by filtering out padding resolving each symbol and then+ -- shifting the value by the #bits per symbol and ORing the value in place.+ -- Finally, it needs to be aligned to the top of byte in memory so the value+ -- is represented correctly.+ streamDecoder (BitwiseDigits _ abc symbits _ groupsyms padmethod) text =+ let -- Gives just padding char if not nothing.+ padsym = paddingChar <$> padmethod+ -- Removes the trailing padding chars from the Text of str.+ notrailing = Text.dropWhileEnd (\sym -> Just sym == padsym) text+ -- Gives the total available characters.+ totalsyms = Text.length notrailing+ -- Finds the needs # of symbols for the final padding chars.+ needsyms = groupsyms - (totalsyms `mod` groupsyms)+ in binaryDecoder notrailing (finalizeBits needsyms) $+ \curvalue symbol ->+ if Just symbol == padsym+ -- When the symbol is the padding char, shift an empty value+ then return (curvalue .<<. symbits)+ -- Otherwise put the value in place+ else do+ value <- fromIntegral <$> Alpha.resolveValue abc symbol+ return $ (curvalue .<<. symbits) .|. value+ where+ -- Finds the value for the expected amount of padding regardless if the+ -- BaseSystem has a PaddingMethod or not.+ {-# INLINE finalizeBits #-}+ finalizeBits needsyms+ -- If the # of Symbols in last group is the groupsyms size, pass+ | needsyms == groupsyms = Nothing+ -- Find the needed # of bits and align the output to first byte.+ | otherwise = Just $ \final -> final .<<. needbits .>>. pagealign+ where+ -- Gives the number of bits to correct in missing final padding chars.+ needbits = needsyms * symbits+ -- Offset to Integer to align the bits in the final ByteString at the+ -- start of a byte in memory.+ pagealign = 8 * ceiling (fromIntegral needbits / 8 :: Double)++-- | Resolves symbols from Alphabet for a BitwiseSystem's encoder. Partitions+-- a ByteString into N sized bitgroups where N is the bitwidth of the+-- Alphabet's radix. *IMPORTANT*: this function requires the groupsize to be+-- a multiple of two because it generates a mask from subtracting it by 1.+groupSymbols ::+ (BinaryTranscoder bxc) => Alphabet -> bxc -> Int -> Int -> [Alpha.Symbol]+groupSymbols abc grouping groupsize symbits =+ let groupint =+ case BXC.unpackValueBE grouping of+ bits+ | fitsBitGroup groupsize grouping bits -> fromIntegral bits+ | otherwise -> error "invalid group size"+ in -- Crash if the implementation isn't complete+ fromJust+ -- Extract the value from the shift and resolve its symbol.+ . mapM (Alpha.resolveSymbol abc . valueExtract . nextInt groupint)+ -- Take all non-zero shifts.+ . takeWhile (>= 0)+ -- Generate a list of shift values from the # bits in groupbytes.+ $ iterateInit shiftValue mkBitLength grouping+ where+ -- Gets the next int to extract group.+ nextInt groupint shift = groupint .>>. shift+ -- Extracts the first group from LSB from an Int.+ valueExtract int = fromIntegral $ int .&. (Alpha.alphaRadix abc - 1)+ -- Finds the length in bits of a ByteString.+ mkBitLength bstr = fromIntegral $ 8 * BXC.totalOctets bstr+ -- Gives the current shift value in iteration.+ shiftValue bitstotal = bitstotal - symbits++-- Checks if an Integral a fits within a BitwiseSystem's group.+{-# INLINE fitsBitGroup #-}+fitsBitGroup :: (BinaryTranscoder bxc, Integral i) => Int -> bxc -> i -> Bool+fitsBitGroup groupsize bxcdata groupbits =+ BXC.totalOctets bxcdata <= fromIntegral groupsize+ && minInt <= groupbits+ && groupbits <= maxInt+ where+ minInt = fromIntegral (minBound :: Int)+ maxInt = fromIntegral (maxBound :: Int)
+ basesystems/Data/BaseSystems/Internal.hs view
@@ -0,0 +1,25 @@+-- | Module : Data.BaseSystems.Internal+-- Description : Provides common resources for multibase+-- Copyright : Zoey McBride (c) 2026+-- License : BSD-3-Clause+-- Maintainer : zoeymcbride@mailbox.org+-- Stability : experimental+--+-- Common data definitions and helper functions for this library.+module Data.BaseSystems.Internal+ ( -- * List utilities+ replaceNull,+ iterateInit,+ )+where++-- | Return the the first param if the second param is empty.+{-# INLINE replaceNull #-}+replaceNull :: [a] -> [a] -> [a]+replaceNull replace xs+ | null xs = replace+ | otherwise = xs++-- | Wraps iterate to compose with an intermediary type constructor.+iterateInit :: (b -> b) -> (a -> b) -> a -> [b]+iterateInit f iterinit = drop 1 . iterate f . iterinit
+ basesystems/Data/BaseSystems/Lazy.hs view
@@ -0,0 +1,51 @@+-- | Module : Data.BaseSystems.Lazy+-- Description : Implements BaseSystems for LazyByteString.+-- Copyright : Zoey McBride (c) 2026+-- License : BSD-3-Clause+-- Maintainer : zoeymcbride@mailbox.org+-- Stability : experimental+module Data.BaseSystems.Lazy+ ( -- * BaseSystems on LazyByteString.+ BaseSystem (encoder, decoder),++ -- * Function signatures aliases.+ Encoder,+ Decoder,++ -- * ByteString implementation for this file.+ ByteStr,++ -- * Exported basesystem defintions+ module Data.BaseSystems.DigitSystems,+ )+where++import Data.BaseSystems.DigitSystems+import Data.BaseSystems.DigitTranscoder (BitwiseDigits, RadixDigits)+import Data.BaseSystems.DigitTranscoder qualified as DXC+import Data.ByteString.Lazy (LazyByteString)+import Data.Text (Text)++-- | Sets the bytestring type to use in this file.+type ByteStr = LazyByteString++-- | Alias for curried encoder method.+type Encoder = ByteStr -> Text++-- | Alias for curried decoder method.+type Decoder = Text -> Maybe ByteStr++-- | Configures the BaseSystem for this file's ByteType.+class BaseSystem a where+ encoder :: a -> ByteStr -> Text+ decoder :: a -> Text -> Maybe ByteStr++-- | BaseSystem for LazyByteString.+instance BaseSystem RadixDigits where+ encoder = DXC.digitEncoder+ decoder = DXC.digitDecoder++-- | BaseSystem for LazyByteString.+instance BaseSystem BitwiseDigits where+ encoder = DXC.digitEncoder+ decoder = DXC.digitDecoder
+ basesystems/Data/BaseSystems/Strict.hs view
@@ -0,0 +1,51 @@+-- | Module : Data.BaseSystems.Strict+-- Description : Implements BaseSystems for strict ByteStrings.+-- Copyright : Zoey McBride (c) 2026+-- License : BSD-3-Clause+-- Maintainer : zoeymcbride@mailbox.org+-- Stability : experimental+module Data.BaseSystems.Strict+ ( -- * BaseSystems on ByteString+ BaseSystem (encoder, decoder),++ -- * Function signatures aliases.+ Encoder,+ Decoder,++ -- * ByteString implementation for this file+ ByteStr,++ -- * Exported basesystem defintions+ module Data.BaseSystems.DigitSystems,+ )+where++import Data.BaseSystems.DigitSystems+import Data.BaseSystems.DigitTranscoder (BitwiseDigits, RadixDigits)+import Data.BaseSystems.DigitTranscoder qualified as DXC+import Data.ByteString (ByteString)+import Data.Text (Text)++-- | Sets the bytestring type to use in this file.+type ByteStr = ByteString++-- | Alias for curried encoder method.+type Encoder = ByteStr -> Text++-- | Alias for curried decoder method.+type Decoder = Text -> Maybe ByteStr++-- | Configures the BaseSystem for this file's ByteType.+class BaseSystem a where+ encoder :: a -> ByteStr -> Text+ decoder :: a -> Text -> Maybe ByteStr++-- | BaseSystem for ByteString.+instance BaseSystem RadixDigits where+ encoder = DXC.digitEncoder+ decoder = DXC.digitDecoder++-- | BaseSystem for ByteString.+instance BaseSystem BitwiseDigits where+ encoder = DXC.digitEncoder+ decoder = DXC.digitDecoder