base62 0.1.0.2 → 0.1.0.3
raw patch · 3 files changed
+55/−47 lines, 3 filesdep −word-compatdep ~base
Dependencies removed: word-compat
Dependency ranges changed: base
Files
- CHANGELOG.md +4/−0
- base62.cabal +3/−4
- src/Data/Word/Base62.hs +48/−43
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for base62 +## 0.1.0.3 -- 2023-07-25++* Support GHC 9.4. Drop support for all versions of GHC before 9.4.+ ## 0.1.0.2 -- 2022-12-07 * Support GHC 9.2.
base62.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: base62-version: 0.1.0.2+version: 0.1.0.3 synopsis: Base62 encoding and decoding description: Encode and decode using the base62 encoding scheme.@@ -18,13 +18,12 @@ exposed-modules: Data.Word.Base62 build-depends:- , base >=4.12 && <5+ , base >=4.17 && <5 , byteslice >=0.2 && <0.3 , natural-arithmetic >=0.1 && <0.2- , primitive >=0.7 && <0.8+ , primitive >=0.7 && <0.10 , bytebuild >=0.3.4 && <0.4 , wide-word >=0.1.0.8 && <0.2- , word-compat >=0.0.4 hs-source-dirs: src default-language: Haskell2010 ghc-options: -Wall -O2
src/Data/Word/Base62.hs view
@@ -34,13 +34,12 @@ import Data.Primitive (ByteArray(..),readByteArray,writeByteArray) import Data.Primitive (MutableByteArray(MutableByteArray)) import Data.WideWord.Word128 (Word128(Word128))-import GHC.Exts (Char(C#),quotRemWord#,indexCharArray#)-import GHC.Exts (ByteArray#,Int#,Int(I#),Word#,(+#),(-#))+import GHC.Exts (Char(C#),Word64#,Word8#,quotRemWord#,indexCharArray#)+import GHC.Exts (ByteArray#,Int#,Int(I#),(+#),(-#)) import GHC.Exts (isTrue#,(>#))-import GHC.Prim.Compat (writeWord8Array#)+import GHC.Exts (wordToWord64#,word64ToWord#) import GHC.ST (ST(ST))-import GHC.Word (Word64(W64#),Word8,Word(W#))-import GHC.Word.Compat (pattern W8#)+import GHC.Word (Word64(W64#),Word8(W8#)) import qualified Arithmetic.Nat as Nat import qualified Data.Bytes as Bytes@@ -79,41 +78,45 @@ {-# inline builder128 #-} builder128 (Word128 (W64# a) (W64# b)) = builder128# a b -builder64# :: Word# -> Builder 11+builder64# :: Word64# -> Builder 11 {-# noinline builder64# #-} builder64# w0 = Builder- (\marr off0 s0 -> case w0 of- 0## -> case writeWord8Array# marr off0 48## s0 of+ (\marr off0 s0 -> case word64ToWord# w0 of+ 0## -> case Exts.writeWord8Array# marr off0 (Exts.wordToWord8# 48##) s0 of s1 -> (# s1, off0 +# 1# #)- _ -> let go ix w s1 = case w of+ _ -> let go ix w s1 = case word64ToWord# w of 0## -> case reverseBytes (MutableByteArray marr) (I# off0) (I# (ix -# 1# )) of ST f -> case f s1 of (# s2, (_ :: ()) #) -> (# s2, ix #) _ ->- let !(# q, r #) = quotRemWord# w 62##- in case writeWord8Array# marr ix (unW8 (encodeByte (W# r))) s1 of+ let !(# q0, r0 #) = quotRemWord# (word64ToWord# w) 62##+ !q = wordToWord64# q0+ !r = wordToWord64# r0+ in case Exts.writeWord8Array# marr ix (unW8 (encodeByte (W64# r))) s1 of s2 -> go (ix +# 1#) q s2 in go off0 w0 s0 ) -- Always outputs exactly ten digits. They do not need to be reversed.-builder62pow10# :: Word# -> Builder 10+builder62pow10# :: Word64# -> Builder 10 {-# noinline builder62pow10# #-} builder62pow10# w0 = Builder (\marr off0 s0 -> let go ix d w s1 = case d of 0# -> (# s1, ix +# 11# #) _ ->- let !(# q, r #) = quotRemWord# w 62##- in case writeWord8Array# marr ix (unW8 (encodeByte (W# r))) s1 of+ let !(# q0, r0 #) = quotRemWord# (word64ToWord# w) 62##+ !q = wordToWord64# q0+ !r = wordToWord64# r0+ in case Exts.writeWord8Array# marr ix (unW8 (encodeByte (W64# r))) s1 of s2 -> go (ix -# 1# ) (d -# 1# ) q s2 in go (off0 +# 9# ) 10# w0 s0 ) -builder128# :: Word# -> Word# -> Builder 22+builder128# :: Word64# -> Word64# -> Builder 22 {-# noinline builder128# #-} builder128# wa wb = Builder- (\marr off0 s0 -> case wa of+ (\marr off0 s0 -> case word64ToWord# wa of 0## -> case builder64# wb of Builder f -> f marr off0 s0 _ -> case quotRem (Word128 (W64# wa) (W64# wb)) (Word128 0 n62pow10) of (upper@(Word128 upperHi (W64# upperLo)), (Word128 shouldBeZeroA (W64# lower))) -> case shouldBeZeroA of@@ -123,9 +126,9 @@ (Word128 shouldBeZeroB (W64# x),Word128 shouldBeZeroC (W64# y)) -> case shouldBeZeroB of 0 -> case shouldBeZeroC of 0 -> case builder64# x `Builder.append` (builder62pow10# y `Builder.append` builder62pow10# lower) of Builder f -> f marr off0 s0- _ -> error "Data.Word.Base62: logical error c"- _ -> error "Data.Word.Base62: logical error b"- _ -> error "Data.Word.Base62: logical error a"+ _ -> errorWithoutStackTrace "Data.Word.Base62: logical error c"+ _ -> errorWithoutStackTrace "Data.Word.Base62: logical error b"+ _ -> errorWithoutStackTrace "Data.Word.Base62: logical error a" ) -- Reverse the bytes in the designated slice. This takes@@ -143,7 +146,7 @@ else pure () -- Precondition: argument is less than 62.-encodeByte :: Word -> Word8+encodeByte :: Word64 -> Word8 encodeByte w | w < 10 = unsafeW8 (c2w '0' + w) | w < 36 = unsafeW8 ((c2w 'A' - 10) + w)@@ -151,26 +154,23 @@ -- We use Char here since it produces more readable Core. -- Performance is not impacted in any way.-decodeByte :: Char -> Maybe Word+decodeByte :: Char -> Maybe Word64 decodeByte w | w >= '0' && w <= '9' = Just (c2w w - c2w '0') | w >= 'A' && w <= 'Z' = Just (c2w w - (c2w 'A' - 10)) | w >= 'a' && w <= 'z' = Just (c2w w - (c2w 'a' - 36)) | otherwise = Nothing -c2w :: Char -> Word+c2w :: Char -> Word64 c2w = fromIntegral . ord -- Precondition: the argument is less than 256-unsafeW8 :: Word -> Word8-unsafeW8 (W# w) = W8# w+unsafeW8 :: Word64 -> Word8+unsafeW8 (W64# w) = W8# (Exts.wordToWord8# (word64ToWord# w)) -unW8 :: Word8 -> Word#+unW8 :: Word8 -> Word8# unW8 (W8# w) = w -unW :: Word -> Word#-unW (W# w) = w- -- | Decode a base62-encoded 64-bit word. This rejects the empty -- string rather than decoding it as zero. This also rejects encoded -- numbers greater than or equal to @2^64@.@@ -190,9 +190,9 @@ -- Worker-wrapper will turn this into good code as long as -- we do not put a noinline pragma on it. It is recursive, -- so it cannot inline anywhere.-decode64# :: Word -> Bytes -> (# (# #) | Word# #)-decode64# !acc b@(Bytes arr off len) = case len of- 0 -> (# | unW acc #)+decode64# :: Word64 -> Bytes -> (# (# #) | Word64# #)+decode64# !acc@(W64# acc# ) b@(Bytes arr off len) = case len of+ 0 -> (# | acc# #) _ -> case decodeByte (indexAsciiArray arr off) of Nothing -> (# (# #) | #) Just w ->@@ -215,14 +215,14 @@ -- position in the bytearray, @d@ tracks the number of digits -- consumed by this particular function. The caller should always -- set @d@ to 0.-unsafeDecode62pow10# :: Word -> ByteArray -> Int -> Int -> (# (# #) | Word# #)-unsafeDecode62pow10# !acc !arr !off !d = if d < 10+unsafeDecode62pow10# :: Word64 -> ByteArray -> Int -> Int -> (# (# #) | Word64# #)+unsafeDecode62pow10# !acc@(W64# acc# ) !arr !off !d = if d < 10 then case decodeByte (indexAsciiArray arr off) of Nothing -> (# (# #) | #) Just w -> let acc' = acc * 62 + w in unsafeDecode62pow10# acc' arr (off + 1) (d + 1)- else (# | unW acc #)+ else (# | acc# #) -- | Decode a base62-encoded 128-bit word. This rejects the empty -- string rather than decoding it as zero. This also rejects encoded@@ -241,7 +241,7 @@ (# (# #) | #) -> Nothing (# | (# a, b #) #) -> Just (Word128 (W64# a) (W64# b)) -decode128# :: ByteArray# -> Int# -> Int# -> (# (# #) | (# Word#, Word# #) #)+decode128# :: ByteArray# -> Int# -> Int# -> (# (# #) | (# Word64#, Word64# #) #) {-# noinline decode128# #-} decode128# arr off len | isTrue# (len ># 22# ) = (# (# #) | #) -- always overflows@@ -269,15 +269,20 @@ Word128 (W64# x) (W64# y) -> (# | (# x, y #) #) | otherwise = case decode64# 0 (Bytes (ByteArray arr) (I# off) (I# len)) of (# (# #) | #) -> (# (# #) | #)- (# | w #) -> (# | (# 0##, w #) #)+ (# | w #) -> (# | (# wordToWord64# 0##, w #) #) indexAsciiArray :: ByteArray -> Int -> Char indexAsciiArray (ByteArray arr) (I# i) = C# (indexCharArray# arr i) -unsignedPushBase62 :: Word -> Word -> (Bool,Word)-unsignedPushBase62 (W# a) (W# b) = - let !(# ca, r0 #) = Exts.timesWord2# a 62##- !r1 = Exts.plusWord# r0 b- !cb = Exts.int2Word# (Exts.ltWord# r1 r0)- !c = ca `Exts.or#` cb- in (case c of { 0## -> False; _ -> True }, W# r1)+unsignedPushBase62 :: Word64 -> Word64 -> (Bool,Word64)+{-# inline unsignedPushBase62 #-}+unsignedPushBase62 (W64# a) (W64# b) = + let !(# ca, r0 #) = Exts.timesWord2# (word64ToWord# a) 62##+ in case ca of+ 0## ->+ let !r0' = wordToWord64# r0+ !r1 = Exts.plusWord64# r0' b+ in case Exts.ltWord64# r1 r0' of+ 1# -> (True,0)+ _ -> (False,W64# r1)+ _ -> (True,0)