rapidhash 0.1.1.0 → 0.2.0.0
raw patch · 12 files changed
+1172/−160 lines, 12 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.Hash.RapidHash: HashViaRapidHashMicro :: a -> HashViaRapidHashMicro a
+ Data.Hash.RapidHash: RapidHashMicro :: Word64 -> RapidHashMicro
+ Data.Hash.RapidHash: newtype HashViaRapidHashMicro a
+ Data.Hash.RapidHash: newtype RapidHashMicro
+ Data.Hash.RapidHash: parseRapidHashMicroBS :: ByteString -> Either String RapidHashMicro
+ Data.Hash.RapidHash: parseRapidHashMicroText :: Text -> Either String RapidHashMicro
+ Data.Hash.RapidHash: rapidhashMicro :: RapidHashable a => a -> RapidHashMicro
+ Data.Hash.RapidHash: rapidhashMicroTextBuilder :: RapidHashMicro -> Builder
+ Data.Hash.RapidHash: rapidhashMicroWithSeed :: RapidHashable a => RapidSeed -> a -> RapidHashMicro
+ Data.Hash.RapidHash: showRapidHashMicroBS :: RapidHashMicro -> ByteString
+ Data.Hash.RapidHash: showRapidHashMicroText :: RapidHashMicro -> Text
+ Data.Hash.RapidHash.Aeson: instance Data.Aeson.Types.FromJSON.FromJSON Data.Hash.RapidHash.Types.RapidHashMicro
+ Data.Hash.RapidHash.Aeson: instance Data.Aeson.Types.FromJSON.FromJSONKey Data.Hash.RapidHash.Types.RapidHashMicro
+ Data.Hash.RapidHash.Aeson: instance Data.Aeson.Types.ToJSON.ToJSON Data.Hash.RapidHash.Types.RapidHashMicro
+ Data.Hash.RapidHash.Aeson: instance Data.Aeson.Types.ToJSON.ToJSONKey Data.Hash.RapidHash.Types.RapidHashMicro
+ Data.Hash.RapidHash.Class: HashViaRapidHashMicro :: a -> HashViaRapidHashMicro a
+ Data.Hash.RapidHash.Class: instance (Data.Hash.RapidHash.Class.RapidHashable a, GHC.Classes.Eq a) => Data.Hashable.Class.Hashable (Data.Hash.RapidHash.Class.HashViaRapidHashMicro a)
+ Data.Hash.RapidHash.Class: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Hash.RapidHash.Class.HashViaRapidHashMicro a)
+ Data.Hash.RapidHash.Class: newtype HashViaRapidHashMicro a
+ Data.Hash.RapidHash.Class: rapidhashMicro :: RapidHashable a => a -> RapidHashMicro
+ Data.Hash.RapidHash.Class: rapidhashMicroWithSeed :: RapidHashable a => RapidSeed -> a -> RapidHashMicro
+ Data.Hash.RapidHash.FFI: rapidhashMicroOffsetWithSeedFFI_ByteArray# :: ByteArray# -> COffset -> CSize -> CSeed -> IO Word64
+ Data.Hash.RapidHash.FFI: rapidhashMicroOffsetWithSeed_ByteArray# :: CSeed -> ByteArray# -> COffset -> CSize -> Word64
+ Data.Hash.RapidHash.FFI: rapidhashMicroWithSeedFFI_ByteArray# :: ByteArray# -> CSize -> CSeed -> IO Word64
+ Data.Hash.RapidHash.FFI: rapidhashMicroWithSeedFFI_Ptr :: Ptr Void -> CSize -> CSeed -> IO Word64
+ Data.Hash.RapidHash.FFI: rapidhashMicroWithSeed_ByteArray :: CSeed -> ByteArray -> Word64
+ Data.Hash.RapidHash.FFI: rapidhashMicroWithSeed_ByteArray# :: CSeed -> ByteArray# -> Word64
+ Data.Hash.RapidHash.FFI: rapidhashMicroWithSeed_ByteString :: CSeed -> ByteString -> Word64
+ Data.Hash.RapidHash.FFI: rapidhashMicroWithSeed_PrimArray :: CSeed -> PrimArray a -> Word64
+ Data.Hash.RapidHash.FFI: rapidhashMicroWithSeed_PrimitiveVector :: Prim a => CSeed -> Vector a -> Word64
+ Data.Hash.RapidHash.FFI: rapidhashMicroWithSeed_ShortByteString :: CSeed -> ShortByteString -> Word64
+ Data.Hash.RapidHash.FFI: rapidhashMicroWithSeed_StorableVector :: Storable a => CSeed -> Vector a -> Word64
+ Data.Hash.RapidHash.FFI: rapidhashMicroWithSeed_Text :: CSeed -> Text -> Word64
+ Data.Hash.RapidHash.Types: RapidHashMicro :: Word64 -> RapidHashMicro
+ Data.Hash.RapidHash.Types: instance Control.DeepSeq.NFData Data.Hash.RapidHash.Types.RapidHashMicro
+ Data.Hash.RapidHash.Types: instance Data.Binary.Class.Binary Data.Hash.RapidHash.Types.RapidHashMicro
+ Data.Hash.RapidHash.Types: instance Data.Hashable.Class.Hashable Data.Hash.RapidHash.Types.RapidHashMicro
+ Data.Hash.RapidHash.Types: instance GHC.Classes.Eq Data.Hash.RapidHash.Types.RapidHashMicro
+ Data.Hash.RapidHash.Types: instance GHC.Classes.Ord Data.Hash.RapidHash.Types.RapidHashMicro
+ Data.Hash.RapidHash.Types: instance GHC.Internal.Generics.Generic Data.Hash.RapidHash.Types.RapidHashMicro
+ Data.Hash.RapidHash.Types: instance GHC.Internal.Read.Read Data.Hash.RapidHash.Types.RapidHashMicro
+ Data.Hash.RapidHash.Types: instance GHC.Internal.Show.Show Data.Hash.RapidHash.Types.RapidHashMicro
+ Data.Hash.RapidHash.Types: newtype RapidHashMicro
+ Data.Hash.RapidHash.Types: parseRapidHashMicroBS :: ByteString -> Either String RapidHashMicro
+ Data.Hash.RapidHash.Types: parseRapidHashMicroText :: Text -> Either String RapidHashMicro
+ Data.Hash.RapidHash.Types: rapidhashMicroTextBuilder :: RapidHashMicro -> Builder
+ Data.Hash.RapidHash.Types: readsRapidHashMicro :: ReadS RapidHashMicro
+ Data.Hash.RapidHash.Types: showRapidHashMicroBS :: RapidHashMicro -> ByteString
+ Data.Hash.RapidHash.Types: showRapidHashMicroText :: RapidHashMicro -> Text
+ Data.Hash.RapidHash.Types: showsRapidHashMicro :: RapidHashMicro -> ShowS
- Data.Hash.RapidHash.Types: pattern RAPIDHASH_V3_HEX_LENGTH :: (Num a, Eq a) => a
+ Data.Hash.RapidHash.Types: pattern RAPIDHASH_MICRO_V3_PREFIX :: (IsString a, Eq a) => a
Files
- CHANGELOG.md +12/−0
- README.md +8/−4
- cbits/rapidhash_ext.h +19/−0
- rapidhash.cabal +1/−1
- src/Data/Hash/RapidHash.hs +46/−12
- src/Data/Hash/RapidHash/Aeson.hs +23/−2
- src/Data/Hash/RapidHash/Class.hs +52/−8
- src/Data/Hash/RapidHash/FFI.hs +153/−7
- src/Data/Hash/RapidHash/Types.hs +355/−114
- test/RapidHashTest.hs +99/−11
- test/pin/pin.c +20/−1
- test/pin/rapidhash-v3-pin.txt +384/−0
CHANGELOG.md view
@@ -2,6 +2,18 @@ ## Unreleased +## 0.2.0.0 - 2026-09-12++### Added++- `rapidhashMicro` is now bound++### Changed++- **Breaking**: `RapidHashable` gains a required `rapidhashMicroWithSeed` method.+ Hand-written instances must add it.+- Small README reword+ ## 0.1.1.0 - 2026-07-26 ### Added
README.md view
@@ -45,8 +45,8 @@ ## Status -First release, but I consider the API stable. Used for change detection in-[lithon](https://github.com/jtnuttall/lithon).+Package is still fresh, but I consider the API stable. Used for change detection+in [lithon](https://github.com/jtnuttall/lithon). This project follows the PVP. For stability, pin to the major (e.g., `^>= 0.1`). @@ -69,13 +69,17 @@ that stored digests get confused with the output of other hash algorithms, or of future rapidhash versions. +The rapidhashMicro variant, `RapidHashMicro`, carries its own prefix+(`rhmv3:`) and its own `Binary` tag. The two variants agree only up to 80+bytes of input, so their digests are not interchangeable.+ The convention applies to every mechanism that produces string output: `Show`/`Read`, the `Text`/`ByteString` renderers and parsers, and the `text-builder-linear` builder. (`Binary` instead uses a compact tagged 9-byte encoding.) -The `RapidHash` constructor is exported wholesale; if the tagging doesn't-fit your needs, rewrap the underlying `Word64` however you like.+All hash newtype constructors are exported wholesale; if the tagging doesn't fit+your needs, rewrap the underlying `Word64` however you like. ### Aeson
cbits/rapidhash_ext.h view
@@ -24,3 +24,22 @@ return rapidhash_internal((const uint8_t *)key + offset, len, seed, rapid_secret); }++/*+ * rapidhashMicro seeded hash function.+ *+ * @param key Buffer to be hashed.+ * @param offset Offset into the buffer, in bytes.+ * @param len @key length, in bytes.+ * @param seed 64-bit seed used to alter the hash result predictably.+ *+ * Calls rapidhashMicro_internal using provided parameters and default secrets.+ *+ * Returns a 64-bit hash.+ */+RAPIDHASH_INLINE uint64_t+rapidhashMicro_offset_withSeed(const void *key, size_t offset, size_t len,+ uint64_t seed) RAPIDHASH_NOEXCEPT {+ return rapidhashMicro_internal((const uint8_t *)key + offset, len, seed,+ rapid_secret);+}
rapidhash.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: rapidhash-version: 0.1.1.0+version: 0.2.0.0 synopsis: rapidhash v3 - very fast, high-quality, non-cryptographic hashing description: Zero-copy FFI bindings to rapidhash v3, a very fast, high-quality, non-cryptographic hash. The upstream single-header C implementation is
src/Data/Hash/RapidHash.hs view
@@ -38,41 +38,67 @@ -- * Hashes + -- ** rapidhash+ -- - -- | 'RapidHash' is a newtype that provides some affordances for ergonomic, efficient use.+ -- |+ -- The general-purpose rapidhash algorithm.+ --+ -- 'RapidHash' is a newtype that provides some affordances for ergonomic, efficient use. RapidHash (..), - -- * Serialization+ -- *** Serialization rapidHashTextBuilder, showRapidHashText, showRapidHashBS, - -- * Parsing+ -- *** Parsing parseRapidHashText, parseRapidHashBS, - -- * File hashing+ -- *** File hashing -- -- | -- These are simply useful helpers for hashing a file, assuming you don't need- -- to do anything else with the contents. They use strict 'Data.ByteString.ByteString'- -- under the hood, so beware very large files: besides residency, the hash- -- itself is a single @unsafe@ FFI call, which blocks garbage collection- -- across all capabilities for its duration (rapidhash processes tens of- -- GB/s, so this matters only for very large inputs).+ -- to do anything else with the contents. Beware large files. These helpers+ -- use strict 'Data.ByteString.ByteString' and will pause the capability and+ -- garbage collector. rapidhashFileWithSeed, rapidhashFile, - -- * DerivingVia+ -- *** DerivingVia+ HashViaRapidHash (..), + -- ** rapidhashMicro+ -- - -- | Helpers for deriving 'Data.Hashable.Hashable' and other useful classes.- HashViaRapidHash (..),+ -- |+ -- A variant of rapidhash tuned for small keys, aimed at workloads where cache+ -- misses dominate. Upstream reports it faster than rapidhash for inputs up to+ -- 512 bytes, and 15-20% slower above 1kb.+ --+ -- Digests are identical to 'rapidhash' for inputs of 80 bytes or fewer and+ -- diverge from 81 bytes on, so the two are not interchangeable in storage.+ -- Hence the separate type and tag.+ rapidhashMicro,+ RapidHashMicro (..), + -- *** Serialization+ rapidhashMicroTextBuilder,+ showRapidHashMicroBS,+ showRapidHashMicroText,++ -- *** Parsing+ parseRapidHashMicroText,+ parseRapidHashMicroBS,++ -- *** DerivingVia+ HashViaRapidHashMicro (..),+ -- * Re-exports Prim, Storable,@@ -84,18 +110,26 @@ import Data.Hash.RapidHash.Class ( HashViaRapidHash (..),+ HashViaRapidHashMicro (..), RapidHashable (..), rapidhash, rapidhashFile, rapidhashFileWithSeed,+ rapidhashMicro, ) import Data.Hash.RapidHash.Types ( RapidHash (..),+ RapidHashMicro (..), RapidSeed (..), parseRapidHashBS,+ parseRapidHashMicroBS,+ parseRapidHashMicroText, parseRapidHashText, rapidHashTextBuilder,+ rapidhashMicroTextBuilder, showRapidHashBS,+ showRapidHashMicroBS,+ showRapidHashMicroText, showRapidHashText, )
src/Data/Hash/RapidHash/Aeson.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE CPP #-} {-# OPTIONS_GHC -Wno-orphans #-} --- | Orphan aeson instances for 'Data.Hash.RapidHash.Types.RapidHash', encoding--- to and from the tagged hex form (e.g. @"rhv3:0123456789abcdef"@).+-- | Orphan aeson instances for 'Data.Hash.RapidHash.Types.RapidHash' and+-- 'Data.Hash.RapidHash.Types.RapidHashMicro', encoding to and from the tagged+-- hex form (e.g. @"rhv3:0123456789abcdef"@, @"rhmv3:0123456789abcdef"@). -- -- Opt-in with the @aeson@ flag; without it this module is empty and the -- instances do not exist.@@ -32,5 +33,25 @@ instance A.FromJSONKey RapidHash where fromJSONKey = A.FromJSONKeyTextParser \t -> either fail pure (parseRapidHashText t)+ {-# INLINEABLE fromJSONKey #-}++instance A.ToJSON RapidHashMicro where+ toJSON = A.String . showRapidHashMicroText+ {-# INLINEABLE toJSON #-}++instance A.FromJSON RapidHashMicro where+ parseJSON = A.withText "rapidhashMicrov3" \t -> case parseRapidHashMicroText t of+ Right h -> pure h+ Left err -> fail err+ {-# INLINEABLE parseJSON #-}++-- | Keys use the same tagged hex form as the value instances, so a map+-- keyed by @RapidHashMicro@ serializes to a JSON object with @rhmv3:@-prefixed keys.+instance A.ToJSONKey RapidHashMicro where+ toJSONKey = A.toJSONKeyText showRapidHashMicroText+ {-# INLINEABLE toJSONKey #-}++instance A.FromJSONKey RapidHashMicro where+ fromJSONKey = A.FromJSONKeyTextParser \t -> either fail pure (parseRapidHashMicroText t) {-# INLINEABLE fromJSONKey #-} #endif
src/Data/Hash/RapidHash/Class.hs view
@@ -16,8 +16,12 @@ rapidhashFileWithSeed, rapidhashFile, + -- * rapidhashMicro+ rapidhashMicro,+ -- * DerivingVia helpers HashViaRapidHash (..),+ HashViaRapidHashMicro (..), ) where import Control.Monad.IO.Class (MonadIO (liftIO))@@ -35,7 +39,12 @@ import Prelude (Eq, FilePath, fromIntegral, (<$>)) import Data.Hash.RapidHash.FFI-import Data.Hash.RapidHash.Types (RapidHash (RapidHash), RapidSeed (RapidSeed), defaultSeed)+import Data.Hash.RapidHash.Types (+ RapidHash (RapidHash),+ RapidHashMicro (..),+ RapidSeed (RapidSeed),+ defaultSeed,+ ) -- $setup -- >>> :set -XOverloadedStrings@@ -61,51 +70,70 @@ -- 2. Endianness - no real solution, although for common application development -- tasks you'll almost certainly know if this applies to you. class RapidHashable a where- -- | Run rapidhash with the given seed+ -- | Run rapidhash with the given seed. This is usually what you want. rapidhashWithSeed :: RapidSeed -> a -> RapidHash + -- | Run rapidhashMicro with the given seed.+ --+ -- rapidhashMicro is tuned for small keys: upstream reports it faster than+ -- rapidhash for inputs up to 512 bytes, and 15-20% slower above 1kb.+ --+ -- It has identical results to 'rapidhashWithSeed' for inputs of 80 bytes or+ -- fewer; from 81 bytes on results diverge.+ rapidhashMicroWithSeed :: RapidSeed -> a -> RapidHashMicro+ -- | This instance is stable across platforms. instance RapidHashable ByteArray where rapidhashWithSeed = coerce rapidhashWithSeed_ByteArray {-# INLINE rapidhashWithSeed #-}+ rapidhashMicroWithSeed = coerce rapidhashMicroWithSeed_ByteArray+ {-# INLINE rapidhashMicroWithSeed #-} -- | This instance is stable across platforms. instance RapidHashable BS.ByteString where rapidhashWithSeed = coerce rapidhashWithSeed_ByteString {-# INLINE rapidhashWithSeed #-}+ rapidhashMicroWithSeed = coerce rapidhashMicroWithSeed_ByteString+ {-# INLINE rapidhashMicroWithSeed #-} -- | This instance is stable across platforms. instance RapidHashable T.Text where rapidhashWithSeed = coerce rapidhashWithSeed_Text {-# INLINE rapidhashWithSeed #-}+ rapidhashMicroWithSeed = coerce rapidhashMicroWithSeed_Text+ {-# INLINE rapidhashMicroWithSeed #-} -- | This instance is stable across platforms. instance RapidHashable ShortByteString where rapidhashWithSeed = coerce rapidhashWithSeed_ShortByteString {-# INLINE rapidhashWithSeed #-}+ rapidhashMicroWithSeed = coerce rapidhashMicroWithSeed_ShortByteString+ {-# INLINE rapidhashMicroWithSeed #-} -- | This instance is __NOT__ stable across platforms. instance RapidHashable (PrimArray a) where rapidhashWithSeed = coerce rapidhashWithSeed_PrimArray {-# INLINE rapidhashWithSeed #-}+ rapidhashMicroWithSeed = coerce rapidhashMicroWithSeed_PrimArray+ {-# INLINE rapidhashMicroWithSeed #-} -- | This instance is __NOT__ stable across platforms. instance (Prim a) => RapidHashable (PrimitiveVector.Vector a) where rapidhashWithSeed = coerce rapidhashWithSeed_PrimitiveVector {-# INLINE rapidhashWithSeed #-}+ rapidhashMicroWithSeed = coerce rapidhashMicroWithSeed_PrimitiveVector+ {-# INLINE rapidhashMicroWithSeed #-} -- | This instance is __NOT__ stable across platforms. -- -- __Warning__: element memory is hashed raw, so the element type's--- 'Foreign.Storable.Storable' layout must have no padding (i.e.--- 'Foreign.Storable.poke' must write every byte of--- 'Foreign.Storable.sizeOf'). Padding bytes are uninitialized memory:--- with a padded element type, @a == b@ does not imply equal hashes —--- which also breaks lawful 'Data.Hashable.Hashable' use via--- 'Data.Hash.RapidHash.HashViaRapidHash'.+-- 'Foreign.Storable.Storable' layout must have no uninitialized bytes (padding or+-- otherwise). instance (Storable a) => RapidHashable (StorableVector.Vector a) where rapidhashWithSeed = coerce rapidhashWithSeed_StorableVector {-# INLINE rapidhashWithSeed #-}+ rapidhashMicroWithSeed = coerce rapidhashMicroWithSeed_StorableVector+ {-# INLINE rapidhashMicroWithSeed #-} -- | Run rapidhash with its default seed rapidhash :: (RapidHashable a) => a -> RapidHash@@ -123,6 +151,11 @@ rapidhashFile = rapidhashFileWithSeed defaultSeed {-# INLINE rapidhashFile #-} +-- | Run rapidhashMicro with its default seed+rapidhashMicro :: (RapidHashable a) => a -> RapidHashMicro+rapidhashMicro = rapidhashMicroWithSeed defaultSeed+{-# INLINE rapidhashMicro #-}+ -- | -- Newtype wrapper implementing 'Hashable' via 'RapidHashable' - works only for -- types that already implement 'RapidHashable'. All such types should be efficiently@@ -133,5 +166,16 @@ instance (RapidHashable a, Eq a) => Hashable (HashViaRapidHash a) where hashWithSalt salt (HashViaRapidHash a) = let RapidHash h = rapidhashWithSeed (RapidSeed (fromIntegral salt)) a+ in fromIntegral h+ {-# INLINE hashWithSalt #-}++-- |+-- Like 'HashViaRapidHash', but uses 'rapidhashMicroWithSeed'+newtype HashViaRapidHashMicro a = HashViaRapidHashMicro a+ deriving newtype (Eq)++instance (RapidHashable a, Eq a) => Hashable (HashViaRapidHashMicro a) where+ hashWithSalt salt (HashViaRapidHashMicro a) =+ let RapidHashMicro h = rapidhashMicroWithSeed (RapidSeed (fromIntegral salt)) a in fromIntegral h {-# INLINE hashWithSalt #-}
src/Data/Hash/RapidHash/FFI.hs view
@@ -16,10 +16,13 @@ -- All foreign calls in this module are @unsafe@, so they will briefly pause the capability and -- garbage collector. module Data.Hash.RapidHash.FFI (+ -- * Shared aliases COffset, CSeed, - -- * Monomorphic wrappers around FFI+ -- * rapidhash++ -- ** Monomorphic wrappers around FFI rapidhashWithSeed_Text, rapidhashWithSeed_ShortByteString, rapidhashWithSeed_ByteString,@@ -27,17 +30,39 @@ rapidhashWithSeed_PrimitiveVector, rapidhashWithSeed_StorableVector, - -- * Bare 'unsafeDupablePerformIO' wrappers around FFI+ -- ** Bare 'unsafeDupablePerformIO' wrappers around FFI rapidhashWithSeed_ByteArray, - -- ** Unlifted+ -- *** Unlifted rapidhashOffsetWithSeed_ByteArray#, rapidhashWithSeed_ByteArray#, - -- ** Raw FFI+ -- *** Raw FFI rapidhashOffsetWithSeedFFI_ByteArray#, rapidhashWithSeedFFI_ByteArray#, rapidhashWithSeedFFI_Ptr,++ -- * rapidhashMicro++ -- ** Monomorphic wrappers around FFI+ rapidhashMicroWithSeed_Text,+ rapidhashMicroWithSeed_ShortByteString,+ rapidhashMicroWithSeed_ByteString,+ rapidhashMicroWithSeed_PrimArray,+ rapidhashMicroWithSeed_PrimitiveVector,+ rapidhashMicroWithSeed_StorableVector,++ -- ** Bare 'unsafeDupablePerformIO' wrappers around FFI+ rapidhashMicroWithSeed_ByteArray,++ -- *** Unlifted+ rapidhashMicroOffsetWithSeed_ByteArray#,+ rapidhashMicroWithSeed_ByteArray#,++ -- *** Raw FFI+ rapidhashMicroOffsetWithSeedFFI_ByteArray#,+ rapidhashMicroWithSeedFFI_ByteArray#,+ rapidhashMicroWithSeedFFI_Ptr, ) where import Data.Array.Byte (ByteArray (ByteArray))@@ -126,7 +151,7 @@ -- 'ByteArray#', which lets us do a zero-copy hash. rapidhashWithSeed_Text :: CSeed -> TI.Text -> Word64 rapidhashWithSeed_Text seed (TI.Text (ByteArray ba#) off len) =- rapidhashOffsetWithSeed_ByteArray# seed ba# (CSize (fromIntegral off)) (CSize (fromIntegral len))+ rapidhashOffsetWithSeed_ByteArray# seed ba# (int2CSize off) (int2CSize len) {-# INLINE rapidhashWithSeed_Text #-} ----------------------------------------------------------------------------------------------------@@ -144,7 +169,7 @@ rapidhashWithSeed_ByteString :: CSeed -> BS.ByteString -> Word64 rapidhashWithSeed_ByteString seed bs = unsafeDupablePerformIO $ BSUnsafe.unsafeUseAsCStringLen bs \(cstr, len) ->- rapidhashWithSeedFFI_Ptr (castPtr cstr) (CSize (fromIntegral len)) seed+ rapidhashWithSeedFFI_Ptr (castPtr cstr) (int2CSize len) seed {-# INLINE rapidhashWithSeed_ByteString #-} ----------------------------------------------------------------------------------------------------@@ -173,9 +198,130 @@ :: forall a. (Storable a) => CSeed -> StorableVector.Vector a -> Word64 rapidhashWithSeed_StorableVector seed v = unsafeDupablePerformIO $ StorableVector.unsafeWith v \ptr ->- let len = CSize $ fromIntegral (StorableVector.length v * sizeOf @a (error "sizeOf evaluated"))+ let len = int2CSize $ StorableVector.length v * sizeOf @a (error "sizeOf evaluated") in rapidhashWithSeedFFI_Ptr (castPtr ptr) len seed {-# INLINE rapidhashWithSeed_StorableVector #-}++----------------------------------------------------------------------------------------------------+-- rapidhashMicro+----------------------------------------------------------------------------------------------------++----------------------------------------------------------------------------------------------------+-- FFI+----------------------------------------------------------------------------------------------------++foreign import capi unsafe "rapidhash_ext.h rapidhashMicro_offset_withSeed"+ rapidhashMicroOffsetWithSeedFFI_ByteArray#+ :: ByteArray#+ -- ^ The buffer+ -> COffset+ -- ^ Offset into buffer, in bytes+ -> CSize+ -- ^ Length of buffer after offset, in bytes+ -> CSeed+ -> IO Word64++-- |+-- Binding to @rapidhash_ext.h rapidhashMicro_offset_withSeed@.+--+-- This is a small custom shim local to this library, which allows zero-copy hashing of+-- anything wrapping a 'ByteArray#' with an offset, by offloading the offset math to C.+rapidhashMicroOffsetWithSeed_ByteArray# :: CSeed -> ByteArray# -> COffset -> CSize -> Word64+rapidhashMicroOffsetWithSeed_ByteArray# seed ba# off len =+ unsafeDupablePerformIO $+ rapidhashMicroOffsetWithSeedFFI_ByteArray# ba# off len seed+{-# INLINE rapidhashMicroOffsetWithSeed_ByteArray# #-}++foreign import capi unsafe "rapidhash.h rapidhashMicro_withSeed"+ rapidhashMicroWithSeedFFI_ByteArray# :: ByteArray# -> CSize -> CSeed -> IO Word64++-- |+-- Direct binding to @rapidhash.h rapidhashMicro_withSeed@, for anything wrapping a 'ByteArray#'+-- without an offset.+rapidhashMicroWithSeed_ByteArray# :: CSeed -> ByteArray# -> Word64+rapidhashMicroWithSeed_ByteArray# seed arr =+ unsafeDupablePerformIO $+ rapidhashMicroWithSeedFFI_ByteArray# arr (csizeofByteArray# arr) seed+{-# INLINE rapidhashMicroWithSeed_ByteArray# #-}++foreign import capi unsafe "rapidhash.h rapidhashMicro_withSeed"+ rapidhashMicroWithSeedFFI_Ptr :: Ptr Void -> CSize -> CSeed -> IO Word64++----------------------------------------------------------------------------------------------------+-- Data.Array.Byte+----------------------------------------------------------------------------------------------------++-- |+-- Lifted 'rapidhashMicroWithSeed_ByteArray#' for 'ByteArray'.+rapidhashMicroWithSeed_ByteArray :: CSeed -> ByteArray -> Word64+rapidhashMicroWithSeed_ByteArray seed (ByteArray ba#) = rapidhashMicroWithSeed_ByteArray# seed ba#+{-# INLINE rapidhashMicroWithSeed_ByteArray #-}++----------------------------------------------------------------------------------------------------+-- Data.Text+----------------------------------------------------------------------------------------------------++-- |+-- Lifted 'rapidhashMicroOffsetWithSeed_ByteArray#' for 'T.Text'.+--+-- This function reaches into 'T.Text'\'s internals to grab the offset and length into the underlying+-- 'ByteArray#', which lets us do a zero-copy hash.+rapidhashMicroWithSeed_Text :: CSeed -> TI.Text -> Word64+rapidhashMicroWithSeed_Text seed (TI.Text (ByteArray ba#) off len) =+ rapidhashMicroOffsetWithSeed_ByteArray#+ seed+ ba#+ (int2CSize off)+ (int2CSize len)+{-# INLINE rapidhashMicroWithSeed_Text #-}++----------------------------------------------------------------------------------------------------+-- Data.ByteString+----------------------------------------------------------------------------------------------------++-- |+-- Applied 'rapidhashMicroWithSeed_ByteArray' for 'SBS.ShortByteString'.+rapidhashMicroWithSeed_ShortByteString :: CSeed -> SBS.ShortByteString -> Word64+rapidhashMicroWithSeed_ShortByteString seed (SBS.ShortByteString ba) = rapidhashMicroWithSeed_ByteArray seed ba+{-# INLINE rapidhashMicroWithSeed_ShortByteString #-}++-- |+-- Applied 'rapidhashMicroWithSeedFFI_Ptr' for 'BS.ByteString'+rapidhashMicroWithSeed_ByteString :: CSeed -> BS.ByteString -> Word64+rapidhashMicroWithSeed_ByteString seed bs = unsafeDupablePerformIO $+ BSUnsafe.unsafeUseAsCStringLen bs \(cstr, len) ->+ rapidhashMicroWithSeedFFI_Ptr (castPtr cstr) (int2CSize len) seed+{-# INLINE rapidhashMicroWithSeed_ByteString #-}++----------------------------------------------------------------------------------------------------+-- Data.Primitive+----------------------------------------------------------------------------------------------------++rapidhashMicroWithSeed_PrimArray :: CSeed -> PrimArray a -> Word64+rapidhashMicroWithSeed_PrimArray seed (PrimArray ba#) = rapidhashMicroWithSeed_ByteArray# seed ba#+{-# INLINE rapidhashMicroWithSeed_PrimArray #-}++----------------------------------------------------------------------------------------------------+-- Data.Vector+----------------------------------------------------------------------------------------------------++rapidhashMicroWithSeed_PrimitiveVector+ :: forall a. (Prim a) => CSeed -> PrimitiveVector.Vector a -> Word64+rapidhashMicroWithSeed_PrimitiveVector seed (PrimitiveVector.Vector off len (ByteArray ba#)) =+ rapidhashMicroOffsetWithSeed_ByteArray#+ seed+ ba#+ (int2CSize (sizeOfType @a * off))+ (int2CSize (sizeOfType @a * len))+{-# INLINE rapidhashMicroWithSeed_PrimitiveVector #-}++rapidhashMicroWithSeed_StorableVector+ :: forall a. (Storable a) => CSeed -> StorableVector.Vector a -> Word64+rapidhashMicroWithSeed_StorableVector seed v = unsafeDupablePerformIO $+ StorableVector.unsafeWith v \ptr ->+ let len = int2CSize $ StorableVector.length v * sizeOf @a (error "sizeOf evaluated")+ in rapidhashMicroWithSeedFFI_Ptr (castPtr ptr) len seed+{-# INLINE rapidhashMicroWithSeed_StorableVector #-} ---------------------------------------------------------------------------------------------------- -- Utilities
src/Data/Hash/RapidHash/Types.hs view
@@ -1,17 +1,18 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE LinearTypes #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} module Data.Hash.RapidHash.Types (- -- * Seeds+ -- * Shared RapidSeed (..), defaultSeed,+ pattern RAPIDHASH_V3_HEX_LENGTH, - -- * Hashes+ -- * rapidhash RapidHash (..), pattern RAPIDHASH_V3_PREFIX,- pattern RAPIDHASH_V3_HEX_LENGTH, -- ** Serializing rapidHashTextBuilder,@@ -23,11 +24,27 @@ parseRapidHashText, parseRapidHashBS, readsRapidHash,++ -- * rapidhashMicro+ RapidHashMicro (..),+ pattern RAPIDHASH_MICRO_V3_PREFIX,++ -- ** Serializing+ rapidhashMicroTextBuilder,+ showRapidHashMicroText,+ showRapidHashMicroBS,+ showsRapidHashMicro,++ -- ** Deserializing+ parseRapidHashMicroText,+ parseRapidHashMicroBS,+ readsRapidHashMicro, ) where import Control.DeepSeq (NFData) import Control.Monad (when) import Control.Monad.ST.Strict (ST)+import Data.Bifunctor (first) import Data.Binary (Binary) import Data.Binary qualified as Binary import Data.Bits (Bits (shiftR, (.&.)))@@ -36,7 +53,7 @@ import Data.ByteString.Char8 qualified as BSC8 import Data.ByteString.Lex.Integral qualified as BS import Data.Char (isSpace)-import Data.Foldable (for_, length)+import Data.Foldable (for_) import Data.Hashable (Hashable) import Data.List qualified as L import Data.Maybe (maybe)@@ -48,8 +65,8 @@ import Data.Text.Builder.Linear.Buffer qualified as TBuff import Data.Text.Builder.Linear.Core qualified as TB import Data.Text.Read qualified as T-import Data.Word (Word64)-import GHC.Exts (Int (I#), (>#))+import Data.Word (Word64, Word8)+import GHC.Exts (Addr#, Int (I#), (>#)) import GHC.Generics (Generic) import Prelude ( Applicative (pure),@@ -65,10 +82,10 @@ ShowS, String, fromIntegral,+ map, otherwise, showString, ($),- (.), (<$>), (||), )@@ -86,14 +103,32 @@ defaultSeed :: RapidSeed defaultSeed = RapidSeed 0 +-- | The length of the hash part of a valid rapidhash.+--+-- Exported for convenience.+--+-- === __Total length__+--+-- >>> RAPIDHASH_V3_HEX_LENGTH + length (RAPIDHASH_V3_PREFIX :: String)+-- 21+--+-- >>> RAPIDHASH_V3_HEX_LENGTH + length (RAPIDHASH_MICRO_V3_PREFIX :: String)+-- 22+pattern RAPIDHASH_V3_HEX_LENGTH :: (Num a, Eq a) => a+pattern RAPIDHASH_V3_HEX_LENGTH = 16++pattern ZERO_CHAR :: (Num a, Eq a) => a+pattern ZERO_CHAR = 0x30++----------------------------------------------------------------------------------------------------+-- rapidhash+----------------------------------------------------------------------------------------------------+ -- | A rapidhash v3 digest. -- -- The textual form is @rhv3:@ followed by exactly 16 hex digits, always--- rendered lowercase. Parsing accepts either case (normalizing on--- re-render), but rejects any other prefix, length, or stray characters.--- The version tag makes stored digests self-describing: a future--- rapidhash version bump (which changes outputs) can never be mistaken--- for a v3 digest.+-- rendered lowercase. Parsing accepts either case, but rejects any other prefix,+-- length, or stray characters. -- -- === __Examples__ -- >>> RapidHash 0xabc@@ -113,23 +148,7 @@ -- Exported for convenience. pattern RAPIDHASH_V3_PREFIX :: (IsString a, Eq a) => a pattern RAPIDHASH_V3_PREFIX = "rhv3:"---- | The length of the hash part of a valid rapidhash.------ Exported for convenience.------ === __Total length__------ >>> RAPIDHASH_V3_HEX_LENGTH + length (RAPIDHASH_V3_PREFIX :: String)--- 21-pattern RAPIDHASH_V3_HEX_LENGTH :: (Num a, Eq a) => a-pattern RAPIDHASH_V3_HEX_LENGTH = 16--pattern ZERO_CHAR :: (Num a, Eq a) => a-pattern ZERO_CHAR = 0x30--prefixLen :: Int-prefixLen = length (RAPIDHASH_V3_PREFIX :: String)+#define RAPIDHASH_V3_PREFIX_ADDR "rhv3:"# instance Show RapidHash where showsPrec _ = showsRapidHash@@ -143,26 +162,25 @@ instance Read RapidHash where readsPrec _ = readsRapidHash +pattern RAPIDHASH_V3_MAGIC_TAG :: Word8+pattern RAPIDHASH_V3_MAGIC_TAG = 3+ -- | -- Efficient binary serialization and deserialization, with a just-good-enough tag. -- -- Encodes in 9 bytes: One byte for the tag, 8 for the 'Word64'. instance Binary RapidHash where put (RapidHash h) = do- Binary.putWord8 3+ Binary.putWord8 RAPIDHASH_V3_MAGIC_TAG Binary.put h {-# INLINE put #-} get = do tag <- Binary.getWord8 case tag of- 3 -> RapidHash <$> Binary.get- _ -> fail "did not find magic tag for rapidhash"+ RAPIDHASH_V3_MAGIC_TAG -> RapidHash <$> Binary.get+ _ -> fail $ "did not find magic tag (" <> show RAPIDHASH_V3_MAGIC_TAG <> ") for rapidhash" {-# INLINE get #-} -------------------------------------------------------------------------------------------------------- Serializing------------------------------------------------------------------------------------------------------ -- | -- Efficiently render a 'RapidHash' as a 'TB.Builder' --@@ -182,16 +200,16 @@ -- >>> RAPIDHASH_V3_PREFIX `T.isPrefixOf` showRapidHashText (RapidHash 0x0) -- True rapidHashTextBuilder :: RapidHash -> TB.Builder-rapidHashTextBuilder h = TB.Builder \b -> appendPaddedHashHexBuf (b TBuff.|># "rhv3:"#) h-{-# INLINEABLE rapidHashTextBuilder #-}+rapidHashTextBuilder (RapidHash h) = hashTextBuilder RAPIDHASH_V3_PREFIX_ADDR h+{-# INLINE rapidHashTextBuilder #-} -- | -- Efficiently render a 'RapidHash' as 'Text'. -- -- Prefer 'rapidHashTextBuilder' when composing. showRapidHashText :: RapidHash -> Text-showRapidHashText = TB.runBuilder . rapidHashTextBuilder-{-# INLINEABLE showRapidHashText #-}+showRapidHashText (RapidHash h) = showHashText RAPIDHASH_V3_PREFIX_ADDR h+{-# INLINE showRapidHashText #-} -- | -- Efficiently render a 'RapidHash' as 'ByteString'. If your text blob is utf-8, it may be worth@@ -201,55 +219,15 @@ -- -- See 'rapidHashTextBuilder' showRapidHashBS :: RapidHash -> ByteString-showRapidHashBS = TB.runBuilderBS . rapidHashTextBuilder-{-# INLINEABLE showRapidHashBS #-}+showRapidHashBS (RapidHash h) = showHashBS RAPIDHASH_V3_PREFIX_ADDR h+{-# INLINE showRapidHashBS #-} -- | -- Somewhat inefficiently render a 'RapidHash' as a 'String'. -- -- Present for debugging purposes. Goes through 'rapidHashTextBuilder' showsRapidHash :: RapidHash -> ShowS-showsRapidHash h = showString $ T.unpack (showRapidHashText h)---- |--- This is a specialized reimplementation of 'TBuff.|>&' from Andrew Lelechenko's--- text-builder-linear that additionally pads with zeros.-appendPaddedHashHexBuf :: TBuff.Buffer %1 -> RapidHash -> TBuff.Buffer-appendPaddedHashHexBuf buffer (RapidHash h) =- -- appendBounded function preallocates a given length, and takes a callback that- -- mutates the array and returns how many characters it wrote.- TB.appendBounded- RAPIDHASH_V3_HEX_LENGTH- ( \dst off -> do- endOff <- unsafeAppendHexW64 dst (off + RAPIDHASH_V3_HEX_LENGTH - 1) h-- -- Core lowering here is consistently fused, but if there's a regression I'd- -- look here first.- for_ @[] [off .. endOff] \i ->- A.unsafeWrite dst i ZERO_CHAR-- pure RAPIDHASH_V3_HEX_LENGTH- )- buffer-{-# INLINEABLE appendPaddedHashHexBuf #-}--unsafeAppendHexW64 :: A.MArray s -> Int -> Word64 -> ST s Int-unsafeAppendHexW64 marr = go- where- go !off = \case- 0 -> pure off- m -> do- let nibble = m .&. 0x0F- A.unsafeWrite marr off $ hex (fromIntegral nibble)- unsafeAppendHexW64 marr (off - 1) (m `shiftR` 4)-- -- Vendored from text-builder-linear - branchless conversion- hex n@(I# n#) = fromIntegral $ ZERO_CHAR + n + I# (n# ># 9#) * (0x60 - 0x39)-{-# INLINEABLE unsafeAppendHexW64 #-}--------------------------------------------------------------------------------------------------------- Deserializing-----------------------------------------------------------------------------------------------------+showsRapidHash (RapidHash h) = showsHash RAPIDHASH_V3_PREFIX_ADDR h -- | -- Efficiently parse a 'RapidHash' from a 'Text'.@@ -260,13 +238,13 @@ -- Right rhv3:deadbeefdeadbeef -- -- >>> parseRapidHashText "rhv3:abracadabratoomany"--- Left "rapidhash hashes should have a length of exactly 16, but found length 18"+-- Left "rapidhash hashes should have an exact, constant length per variant. Expected 16, but found length 18" -- -- >>> parseRapidHashText "rhv3:nothexnothexnoth" -- Left "input does not start with a hexadecimal digit" -- -- >>> parseRapidHashText "rhv3:tooshort"--- Left "rapidhash hashes should have a length of exactly 16, but found length 8"+-- Left "rapidhash hashes should have an exact, constant length per variant. Expected 16, but found length 8" -- -- >>> parseRapidHashText "noprefix" -- Left "missing required prefix \"rhv3:\": \"noprefix\""@@ -277,16 +255,8 @@ -- >>> parseRapidHashText "rhv3:0xadbeefdeadbeef" -- Left "input does not start with a hexadecimal digit" parseRapidHashText :: Text -> Either String RapidHash-parseRapidHashText t = do- mhash <- guardPrefix (T.splitAt prefixLen t)- guardLength (T.length mhash)- -- T.hexadecimal helpfully strips 0x/0X, which is not so helpful for our purposes- let pre = T.take 2 mhash- when (pre == "0x" || pre == "0X") $ Left "input does not start with a hexadecimal digit"- (hash, rest) <- T.hexadecimal mhash- guardLeftovers rest- pure $ RapidHash hash-{-# INLINEABLE parseRapidHashText #-}+parseRapidHashText t = RapidHash <$> parseHashText RAPIDHASH_V3_PREFIX t+{-# INLINE parseRapidHashText #-} -- | -- Efficiently parse a 'RapidHash' from a 'ByteString'.@@ -297,13 +267,13 @@ -- Right rhv3:deadbeefdeadbeef -- -- >>> parseRapidHashBS "rhv3:abracadabratoomany"--- Left "rapidhash hashes should have a length of exactly 16, but found length 18"+-- Left "rapidhash hashes should have an exact, constant length per variant. Expected 16, but found length 18" -- -- >>> parseRapidHashBS "rhv3:nothexnothexnoth" -- Left "Could not parse hexadecimal from \"nothexnothexnoth\"" -- -- >>> parseRapidHashBS "rhv3:tooshort"--- Left "rapidhash hashes should have a length of exactly 16, but found length 8"+-- Left "rapidhash hashes should have an exact, constant length per variant. Expected 16, but found length 8" -- -- >>> parseRapidHashBS "noprefix" -- Left "missing required prefix \"rhv3:\": \"noprefix\""@@ -314,15 +284,8 @@ -- >>> parseRapidHashBS "rhv3:0xadbeefdeadbeef" -- Left "leftovers after parsing hash: \"xadbeefdeadbeef\"" parseRapidHashBS :: ByteString -> Either String RapidHash-parseRapidHashBS b = do- mhash <- guardPrefix (BS.splitAt prefixLen b)- guardLength (BS.length mhash)- (hash, rest) <- maybe (badHex mhash) Right (BS.readHexadecimal mhash)- guardLeftovers rest- pure $ RapidHash hash- where- badHex mhash = Left $ "Could not parse hexadecimal from \"" <> BSC8.unpack mhash <> "\""-{-# INLINEABLE parseRapidHashBS #-}+parseRapidHashBS b = RapidHash <$> parseHashBS RAPIDHASH_V3_PREFIX b+{-# INLINE parseRapidHashBS #-} -- | -- Somewhat inefficiently parse a 'RapidHash' from a 'String'.@@ -334,16 +297,294 @@ -- >>> readsRapidHash "rhv3:0000000000000abcdagk" -- [(rhv3:0000000000000abc,"dagk")] readsRapidHash :: ReadS RapidHash-readsRapidHash s =- let (this, rest) = L.splitAt (prefixLen + RAPIDHASH_V3_HEX_LENGTH) (L.dropWhile isSpace s)- in case parseRapidHashText (T.pack this) of+readsRapidHash s = map (first RapidHash) $ readsHash RAPIDHASH_V3_PREFIX s++----------------------------------------------------------------------------------------------------+-- rapidhashmicro+----------------------------------------------------------------------------------------------------++-- | A rapidhashMicro v3 digest.+--+-- rapidhashMicro is tuned for small keys: upstream reports it faster than rapidhash+-- for inputs up to 512 bytes, and 15-20% slower above 1kb. Results are identical to+-- 'RapidHash' for inputs of 80 bytes or fewer; from 81 bytes on they diverge.+--+-- The textual form is @rhmv3:@ followed by exactly 16 hex digits, always+-- rendered lowercase. Parsing accepts either case, but rejects any other prefix,+-- length, or stray characters.+--+-- === __Examples__+-- >>> RapidHashMicro 0xabc+-- rhmv3:0000000000000abc+--+-- >>> Prelude.read "rhmv3:0000000000000abc" :: RapidHashMicro+-- rhmv3:0000000000000abc+newtype RapidHashMicro = RapidHashMicro Word64+ deriving stock (Generic)+ deriving newtype (Eq, Hashable, NFData, Ord)++-- |+-- The prefix used during serialization and deserialization. This is not a universal+-- convention, but an identifier that should help prevent confusing these hashes+-- with others.+--+-- Exported for convenience.+pattern RAPIDHASH_MICRO_V3_PREFIX :: (IsString a, Eq a) => a+pattern RAPIDHASH_MICRO_V3_PREFIX = "rhmv3:"+#define RAPIDHASH_MICRO_V3_PREFIX_ADDR "rhmv3:"#++instance Show RapidHashMicro where+ showsPrec _ = showsRapidHashMicro++-- |+--+-- === __Examples__+--+-- >>> Prelude.read (show (Prelude.Just (RapidHashMicro 0x1234))) :: Prelude.Maybe RapidHashMicro+-- Just rhmv3:0000000000001234+instance Read RapidHashMicro where+ readsPrec _ = readsRapidHashMicro++pattern RAPIDHASH_MICRO_V3_MAGIC_TAG :: Word8+pattern RAPIDHASH_MICRO_V3_MAGIC_TAG = 31++-- |+-- Efficient binary serialization and deserialization, with a just-good-enough tag.+--+-- Encodes in 9 bytes: One byte for the tag, 8 for the 'Word64'.+instance Binary RapidHashMicro where+ put (RapidHashMicro h) = do+ Binary.putWord8 RAPIDHASH_MICRO_V3_MAGIC_TAG+ Binary.put h+ {-# INLINE put #-}+ get = do+ tag <- Binary.getWord8+ case tag of+ RAPIDHASH_MICRO_V3_MAGIC_TAG -> RapidHashMicro <$> Binary.get+ _ -> fail $ "did not find magic tag (" <> show RAPIDHASH_MICRO_V3_MAGIC_TAG <> ") for rapidhashMicro"+ {-# INLINE get #-}++-- |+-- Efficiently render a 'RapidHashMicro' as a 'TB.Builder'+--+-- === __Examples__+--+-- >>> rapidhashMicroTextBuilder (RapidHashMicro 0x0)+-- "rhmv3:0000000000000000"+--+-- >>> rapidhashMicroTextBuilder (RapidHashMicro 0xabcd3)+-- "rhmv3:00000000000abcd3"+--+-- >>> rapidhashMicroTextBuilder (RapidHashMicro 0xfffff2381)+-- "rhmv3:0000000fffff2381"+--+-- === __Prefixing__+--+-- >>> RAPIDHASH_MICRO_V3_PREFIX `T.isPrefixOf` showRapidHashMicroText (RapidHashMicro 0x0)+-- True+rapidhashMicroTextBuilder :: RapidHashMicro -> TB.Builder+rapidhashMicroTextBuilder (RapidHashMicro h) = hashTextBuilder RAPIDHASH_MICRO_V3_PREFIX_ADDR h+{-# INLINE rapidhashMicroTextBuilder #-}++-- |+-- Efficiently render a 'RapidHashMicro' as 'Text'.+--+-- Prefer 'rapidhashMicroTextBuilder' when composing.+showRapidHashMicroText :: RapidHashMicro -> Text+showRapidHashMicroText (RapidHashMicro h) = showHashText RAPIDHASH_MICRO_V3_PREFIX_ADDR h+{-# INLINE showRapidHashMicroText #-}++-- |+-- Efficiently render a 'RapidHashMicro' as 'ByteString'. If your text blob is utf-8, it may be worth+-- composing using 'TB.Builder' and extracting with 'TB.runBuilderBS' yourself.+--+-- The resulting 'ByteString' is utf-8 encoded, using 'TB.runBuilderBS'+--+-- See 'rapidhashMicroTextBuilder'+showRapidHashMicroBS :: RapidHashMicro -> ByteString+showRapidHashMicroBS (RapidHashMicro h) = showHashBS RAPIDHASH_MICRO_V3_PREFIX_ADDR h+{-# INLINE showRapidHashMicroBS #-}++-- |+-- Somewhat inefficiently render a 'RapidHashMicro' as a 'String'.+--+-- Present for debugging purposes. Goes through 'rapidhashMicroTextBuilder'+showsRapidHashMicro :: RapidHashMicro -> ShowS+showsRapidHashMicro (RapidHashMicro h) = showsHash RAPIDHASH_MICRO_V3_PREFIX_ADDR h++-- |+-- Efficiently parse a 'RapidHashMicro' from a 'Text'.+--+-- == __Examples__+--+-- >>> parseRapidHashMicroText "rhmv3:deadbeefdeadbeef"+-- Right rhmv3:deadbeefdeadbeef+--+-- >>> parseRapidHashMicroText "rhmv3:abracadabratoomany"+-- Left "rapidhash hashes should have an exact, constant length per variant. Expected 16, but found length 18"+--+-- >>> parseRapidHashMicroText "rhmv3:nothexnothexnoth"+-- Left "input does not start with a hexadecimal digit"+--+-- >>> parseRapidHashMicroText "rhmv3:tooshort"+-- Left "rapidhash hashes should have an exact, constant length per variant. Expected 16, but found length 8"+--+-- >>> parseRapidHashMicroText "noprefix"+-- Left "missing required prefix \"rhmv3:\": \"noprefix\""+--+-- >>> parseRapidHashMicroText "rhmv3:abcdefabcdefabcg"+-- Left "leftovers after parsing hash: \"g\""+--+-- >>> parseRapidHashMicroText "rhmv3:0xadbeefdeadbeef"+-- Left "input does not start with a hexadecimal digit"+parseRapidHashMicroText :: Text -> Either String RapidHashMicro+parseRapidHashMicroText t = RapidHashMicro <$> parseHashText RAPIDHASH_MICRO_V3_PREFIX t+{-# INLINE parseRapidHashMicroText #-}++-- |+-- Efficiently parse a 'RapidHashMicro' from a 'ByteString'.+--+-- == __Examples__+--+-- >>> parseRapidHashMicroBS "rhmv3:deadbeefdeadbeef"+-- Right rhmv3:deadbeefdeadbeef+--+-- >>> parseRapidHashMicroBS "rhmv3:abracadabratoomany"+-- Left "rapidhash hashes should have an exact, constant length per variant. Expected 16, but found length 18"+--+-- >>> parseRapidHashMicroBS "rhmv3:nothexnothexnoth"+-- Left "Could not parse hexadecimal from \"nothexnothexnoth\""+--+-- >>> parseRapidHashMicroBS "rhmv3:tooshort"+-- Left "rapidhash hashes should have an exact, constant length per variant. Expected 16, but found length 8"+--+-- >>> parseRapidHashMicroBS "noprefix"+-- Left "missing required prefix \"rhmv3:\": \"noprefix\""+--+-- >>> parseRapidHashMicroBS "rhmv3:abcdefabcdefabcg"+-- Left "leftovers after parsing hash: \"g\""+--+-- >>> parseRapidHashMicroBS "rhmv3:0xadbeefdeadbeef"+-- Left "leftovers after parsing hash: \"xadbeefdeadbeef\""+parseRapidHashMicroBS :: ByteString -> Either String RapidHashMicro+parseRapidHashMicroBS b = RapidHashMicro <$> parseHashBS RAPIDHASH_MICRO_V3_PREFIX b+{-# INLINE parseRapidHashMicroBS #-}++-- |+-- Somewhat inefficiently parse a 'RapidHashMicro' from a 'String'.+--+-- Present for debugging purposes. Goes through 'parseRapidHashMicroText'. Unlike the other+-- parse functions, this allows trailing characters.+--+-- === __Examples__+-- >>> readsRapidHashMicro "rhmv3:0000000000000abcdagk"+-- [(rhmv3:0000000000000abc,"dagk")]+readsRapidHashMicro :: ReadS RapidHashMicro+readsRapidHashMicro s = map (first RapidHashMicro) $ readsHash RAPIDHASH_MICRO_V3_PREFIX s++----------------------------------------------------------------------------------------------------+-- Hash-agnostic+----------------------------------------------------------------------------------------------------++-- | Efficiently render a hash as a 'TB.Builder'+hashTextBuilder :: Addr# -> Word64 -> TB.Builder+hashTextBuilder prefix h = TB.Builder \b -> appendPaddedHashHexBuf (b TBuff.|># prefix) h+{-# INLINEABLE hashTextBuilder #-}++-- | Efficiently render a hash as 'Text'.+showHashText :: Addr# -> Word64 -> Text+showHashText prefix h = TB.runBuilder (hashTextBuilder prefix h)+{-# INLINEABLE showHashText #-}++-- |+-- Efficiently render a hash as 'ByteString'.+--+-- The resulting 'ByteString' is utf-8 encoded, using 'TB.runBuilderBS'+showHashBS :: Addr# -> Word64 -> ByteString+showHashBS prefix h = TB.runBuilderBS (hashTextBuilder prefix h)+{-# INLINEABLE showHashBS #-}++-- |+-- Somewhat inefficiently render a hash as a 'String'.+--+-- Present for debugging purposes. Goes through 'hashTextBuilder'+showsHash :: Addr# -> Word64 -> ShowS+showsHash prefix h = showString $ T.unpack (showHashText prefix h)++-- |+-- This is a specialized reimplementation of 'TBuff.|>&' from Andrew Lelechenko's+-- text-builder-linear that additionally pads with zeros.+appendPaddedHashHexBuf :: TBuff.Buffer %1 -> Word64 -> TBuff.Buffer+appendPaddedHashHexBuf buffer h =+ -- appendBounded function preallocates a given length, and takes a callback that+ -- mutates the array and returns how many characters it wrote.+ TB.appendBounded+ RAPIDHASH_V3_HEX_LENGTH+ ( \dst off -> do+ endOff <- unsafeAppendHexW64 dst (off + RAPIDHASH_V3_HEX_LENGTH - 1) h++ -- Core lowering here is consistently fused, but if there's a regression I'd+ -- look here first.+ for_ @[] [off .. endOff] \i ->+ A.unsafeWrite dst i ZERO_CHAR++ pure RAPIDHASH_V3_HEX_LENGTH+ )+ buffer+{-# INLINEABLE appendPaddedHashHexBuf #-}++unsafeAppendHexW64 :: A.MArray s -> Int -> Word64 -> ST s Int+unsafeAppendHexW64 marr = go+ where+ go !off = \case+ 0 -> pure off+ m -> do+ let nibble = m .&. 0x0F+ A.unsafeWrite marr off $ hex (fromIntegral nibble)+ unsafeAppendHexW64 marr (off - 1) (m `shiftR` 4)++ -- Vendored from text-builder-linear - branchless conversion+ hex n@(I# n#) = fromIntegral $ ZERO_CHAR + n + I# (n# ># 9#) * (0x60 - 0x39)+{-# INLINEABLE unsafeAppendHexW64 #-}++-- | Efficiently parse a hash from a 'Text'.+parseHashText :: Text -> Text -> Either String Word64+parseHashText prefix t = do+ mhash <- guardPrefix prefix (T.splitAt (T.length prefix) t)+ guardLength (T.length mhash)+ -- T.hexadecimal helpfully strips 0x/0X, which is not so helpful for our purposes+ let pre = T.take 2 mhash+ when (pre == "0x" || pre == "0X") $ Left "input does not start with a hexadecimal digit"+ (hash, rest) <- T.hexadecimal mhash+ guardLeftovers rest+ pure hash+{-# INLINEABLE parseHashText #-}++-- | Efficiently parse a hash from a 'ByteString'.+parseHashBS :: ByteString -> ByteString -> Either String Word64+parseHashBS prefix b = do+ mhash <- guardPrefix prefix (BS.splitAt (BS.length prefix) b)+ guardLength (BS.length mhash)+ (hash, rest) <- maybe (badHex mhash) Right (BS.readHexadecimal mhash)+ guardLeftovers rest+ pure hash+ where+ badHex mhash = Left $ "Could not parse hexadecimal from \"" <> BSC8.unpack mhash <> "\""+{-# INLINEABLE parseHashBS #-}++-- | Somewhat inefficiently parse a hash from a 'String'.+readsHash :: Text -> ReadS Word64+readsHash prefix s =+ let (this, rest) = L.splitAt (T.length prefix + RAPIDHASH_V3_HEX_LENGTH) (L.dropWhile isSpace s)+ in case parseHashText prefix (T.pack this) of Right parsed -> [(parsed, rest)] Left _ -> [] -guardPrefix :: (IsString a, Show a, Semigroup a, Eq a) => (a, a) -> Either String a-guardPrefix = \case- (RAPIDHASH_V3_PREFIX, mhash) -> Right mhash- (pre, post) -> Left $ "missing required prefix \"" <> RAPIDHASH_V3_PREFIX <> "\": " <> show (pre <> post)+guardPrefix :: (Show a, Semigroup a, Eq a) => a -> (a, a) -> Either String a+guardPrefix prefix (prefixMay, hash)+ | prefixMay == prefix = Right hash+ | otherwise = Left $ "missing required prefix " <> show prefix <> ": " <> show (prefixMay <> hash)+{-# INLINE guardPrefix #-} guardLength :: (Show a, Eq a, Num a) => a -> Either String () guardLength len@@ -351,7 +592,7 @@ | otherwise = Left badLength where badLength =- "rapidhash hashes should have a length of exactly "+ "rapidhash hashes should have an exact, constant length per variant. Expected " <> show @Int RAPIDHASH_V3_HEX_LENGTH <> ", but found length " <> show len
test/RapidHashTest.hs view
@@ -1,6 +1,7 @@ module RapidHashTest where import Control.Monad (forM_)+import Data.Binary qualified as Binary import Data.ByteString qualified as BS import Data.ByteString.Short qualified as SBS import Data.Primitive.PrimArray (primArrayFromList)@@ -17,27 +18,91 @@ import Data.Hash.RapidHash ( RapidHash (RapidHash),+ RapidHashMicro (RapidHashMicro), RapidSeed (RapidSeed), rapidhash,+ rapidhashMicro,+ rapidhashMicroWithSeed, rapidhashWithSeed, ) unit_showIsTaggedPaddedHex :: Assertion-unit_showIsTaggedPaddedHex =+unit_showIsTaggedPaddedHex = do show (RapidHash 0xabc) @?= "rhv3:0000000000000abc"+ show (RapidHashMicro 0xabc) @?= "rhmv3:0000000000000abc" unit_readRejectsForeignTags :: Assertion unit_readRejectsForeignTags = do reads @RapidHash "rhv4:0000000000000abc" @?= [] -- future reads @RapidHash "fnv1a64:0000000000000abc" @?= [] -- other hash reads @RapidHash "rhv3:abc" @?= [] -- invalid hash length+ reads @RapidHashMicro "rhmv4:0000000000000abc" @?= []+ reads @RapidHashMicro "fnv1a64:0000000000000abc" @?= []+ reads @RapidHashMicro "rhmv3:abc" @?= [] +-- | The two variants disagree above 80 bytes, so a digest of one must never+-- read back as the other — that is the whole point of the distinct tags.+unit_variantTagsDoNotCrossParse :: Assertion+unit_variantTagsDoNotCrossParse = do+ reads @RapidHash (show (RapidHashMicro 0xabc)) @?= []+ reads @RapidHashMicro (show (RapidHash 0xabc)) @?= []++-- | Same, for the compact 9-byte @Binary@ encoding: the leading magic tag must+-- keep the two variants apart.+unit_binaryTagsDoNotCrossDecode :: Assertion+unit_binaryTagsDoNotCrossDecode = do+ let encoded = Binary.encode (RapidHash 0x1234)+ encodedMicro = Binary.encode (RapidHashMicro 0x1234)+ assertBool "micro bytes rejected as RapidHash" $+ isLeft (Binary.decodeOrFail @RapidHash encodedMicro)+ assertBool "rapidhash bytes rejected as RapidHashMicro" $+ isLeft (Binary.decodeOrFail @RapidHashMicro encoded)+ Binary.decode encoded @?= RapidHash 0x1234+ Binary.decode encodedMicro @?= RapidHashMicro 0x1234+ where+ isLeft = either (const True) (const False)+ hprop_showReadRoundtrip :: Property hprop_showReadRoundtrip = property do w <- forAll (Gen.word64 Range.linearBounded) let h = RapidHash w read (show h) === h +-- | Rendering goes through the @Addr#@ prefix literal while parsing goes through+-- the prefix pattern synonym, so this property is what keeps the two definitions+-- of @rhmv3:@ in agreement.+hprop_microShowReadRoundtrip :: Property+hprop_microShowReadRoundtrip = property do+ w <- forAll (Gen.word64 Range.linearBounded)+ let h = RapidHashMicro w+ read (show h) === h++-- | rapidhashMicro's block loop only kicks in above 80 bytes, so up to and+-- including 80 it must agree with rapidhash exactly, at any seed. This pins the+-- boundary the haddocks document.+hprop_microAgreesWithRapidhashThrough80 :: Property+hprop_microAgreesWithRapidhashThrough80 = property do+ len <- forAll (Gen.int (Range.linear 0 80))+ s <- forAll (Gen.word64 Range.linearBounded)+ let bs = bufBS len+ seed = RapidSeed s+ RapidHash expected = rapidhashWithSeed seed bs+ RapidHashMicro actual = rapidhashMicroWithSeed seed bs+ actual === expected+ let RapidHash expected' = rapidhash bs+ RapidHashMicro actual' = rapidhashMicro bs+ actual' === expected'++-- | The other side of the boundary: from 81 bytes on the variants must diverge,+-- otherwise the separate type and tag would be pointless.+unit_microDivergesFrom81 :: Assertion+unit_microDivergesFrom81 =+ forM_ [81, 82, 112, 113, 256, 1024] \len -> do+ let bs = bufBS len+ RapidHash expected = rapidhash bs+ RapidHashMicro actual = rapidhashMicro bs+ assertBool ("variants diverge at length " <> show len) (actual /= expected)+ -- | The Text entry hashes the UTF-8 payload in place; it must agree with -- hashing the encoded bytes (and the ShortByteString copy of them) — at -- the default seed, at a random seed (pinning the seed plumbing of the@@ -55,6 +120,14 @@ rapidhashWithSeed seed (SBS.toShort bytes) === rapidhashWithSeed seed bytes let t' = T.drop 1 t rapidhash t' === rapidhash (TE.encodeUtf8 t')+ -- Same again for the micro variant, which has its own set of FFI wrappers and+ -- its own offset shim.+ rapidhashMicro t === rapidhashMicro bytes+ rapidhashMicro (SBS.toShort bytes) === rapidhashMicro bytes+ rapidhashMicro (BS.copy bytes) === rapidhashMicro bytes+ rapidhashMicroWithSeed seed t === rapidhashMicroWithSeed seed bytes+ rapidhashMicroWithSeed seed (SBS.toShort bytes) === rapidhashMicroWithSeed seed bytes+ rapidhashMicro t' === rapidhashMicro (TE.encodeUtf8 t') hprop_vectorInstancesAgreeWithBytes :: Property hprop_vectorInstancesAgreeWithBytes = property do@@ -69,6 +142,11 @@ rapidhash sv === rapidhash pv rapidhash (primArrayFromList ws) === rapidhash pv rapidhash pvSlice === rapidhash (pvBytes pvSlice)+ rapidhashMicro sv === rapidhashMicro (SV.unsafeCast sv :: SV.Vector Word8)+ rapidhashMicro pv === rapidhashMicro (pvBytes pv)+ rapidhashMicro sv === rapidhashMicro pv+ rapidhashMicro (primArrayFromList ws) === rapidhashMicro pv+ rapidhashMicro pvSlice === rapidhashMicro (pvBytes pvSlice) -- | Known-answer vectors generated from the vendored upstream header by -- @test/pin/pin.c@ (verify or regenerate with @scripts/rapidhash-pin.sh@@@ -84,12 +162,18 @@ ["withSeed", lenS, seedS, hashS] -> assertEqual line (RapidHash (hex hashS)) $ rapidhashWithSeed (RapidSeed (hex seedS)) (bufBS (read lenS))- ["offset", offS, lenS, seedS, hashS] -> do- let off = read offS- len = read lenS- slice = PV.drop off (PV.fromList (bufBytes (off + len)) :: PV.Vector Word8)+ ["offset", offS, lenS, seedS, hashS] -> assertEqual line (RapidHash (hex hashS)) $- rapidhashWithSeed (RapidSeed (hex seedS)) slice+ rapidhashWithSeed (RapidSeed (hex seedS)) (sliceOf (read offS) (read lenS))+ ["micro", lenS, hashS] ->+ assertEqual line (RapidHashMicro (hex hashS)) $+ rapidhashMicro (bufBS (read lenS))+ ["microWithSeed", lenS, seedS, hashS] ->+ assertEqual line (RapidHashMicro (hex hashS)) $+ rapidhashMicroWithSeed (RapidSeed (hex seedS)) (bufBS (read lenS))+ ["microOffset", offS, lenS, seedS, hashS] ->+ assertEqual line (RapidHashMicro (hex hashS)) $+ rapidhashMicroWithSeed (RapidSeed (hex seedS)) (sliceOf (read offS) (read lenS)) _ -> assertFailure ("unparseable golden line: " <> line) where hex :: String -> Word64@@ -97,9 +181,13 @@ [(v, "")] -> v _ -> error ("bad hex field in golden file: " <> s) - -- Deterministic filler; mirrors byte_at in test/pin/pin.c.- bufBytes :: Int -> [Word8]- bufBytes n = [fromIntegral (i * 167 + 13) | i <- [0 .. n - 1]]+ -- A sliced Vector, to drive the offset shim rather than a pre-cut buffer.+ sliceOf :: Int -> Int -> PV.Vector Word8+ sliceOf off len = PV.drop off (PV.fromList (bufBytes (off + len))) - bufBS :: Int -> BS.ByteString- bufBS = BS.pack . bufBytes+-- | Deterministic filler; mirrors byte_at in test/pin/pin.c.+bufBytes :: Int -> [Word8]+bufBytes n = [fromIntegral (i * 167 + 13) | i <- [0 .. n - 1]]++bufBS :: Int -> BS.ByteString+bufBS = BS.pack . bufBytes
test/pin/pin.c view
@@ -2,7 +2,8 @@ * Known-answer vector generator for the rapidhash Haskell binding. * * Compiled against the vendored upstream header (cbits/rapidhash.h) and the- * local shim (cbits/rapidhash_ext.h), it prints reference digests to stdout.+ * local shim (cbits/rapidhash_ext.h), it prints reference digests for both the+ * rapidhash and rapidhashMicro variants to stdout. * The committed golden file (test/pin/rapidhash-v3-pin.txt) is this program's * output; the Haskell test suite recomputes every line through the binding's * ByteString entrypoint (and the sliced Vector path for the offset shim).@@ -61,6 +62,24 @@ offset_lengths[li], offset_seeds[si], rapidhash_offset_withSeed(buf, offsets[oi], offset_lengths[li], offset_seeds[si]));++ for (size_t li = 0; li < COUNT(lengths); li++)+ printf("micro %zu %016" PRIx64 "\n", lengths[li],+ rapidhashMicro(buf, lengths[li]));++ for (size_t li = 0; li < COUNT(lengths); li++)+ for (size_t si = 0; si < COUNT(seeds); si++)+ printf("microWithSeed %zu %016" PRIx64 " %016" PRIx64 "\n", lengths[li],+ seeds[si], rapidhashMicro_withSeed(buf, lengths[li], seeds[si]));++ for (size_t oi = 0; oi < COUNT(offsets); oi++)+ for (size_t li = 0; li < COUNT(offset_lengths); li++)+ for (size_t si = 0; si < COUNT(offset_seeds); si++)+ printf("microOffset %zu %zu %016" PRIx64 " %016" PRIx64 "\n",+ offsets[oi], offset_lengths[li], offset_seeds[si],+ rapidhashMicro_offset_withSeed(buf, offsets[oi],+ offset_lengths[li],+ offset_seeds[si])); free(buf); return 0;
test/pin/rapidhash-v3-pin.txt view
@@ -382,3 +382,387 @@ offset 17 64 00000000deadbeef 15c05b7fa243027f offset 17 257 0000000000000000 4bd9fa46f08c671b offset 17 257 00000000deadbeef 858abb96791bc043+micro 0 0338dc4be2cecdae+micro 1 e8d3b882671125a6+micro 2 72f646f85991e6ff+micro 3 c76c8514de0eb9f0+micro 4 54ceab033b17ed48+micro 5 927048267a1e0e6c+micro 6 8a479b23d49ea8c0+micro 7 6c91739e11166758+micro 8 cb1c61491fd8ed0c+micro 9 c8c920654ad0a214+micro 10 1c197f92a46cd6a8+micro 11 ed353bd1078d750d+micro 12 a7d066f68974928b+micro 13 784066c84e806ea5+micro 14 4ae43dd7c228e5d0+micro 15 cf0671c9f0e79123+micro 16 51cf6b59dc9af20b+micro 17 d72787ae7a2963b8+micro 31 9e168ae2c0b51969+micro 32 0214629041803a0c+micro 33 c8c2c6562d2ec99b+micro 47 d86d732eb077836d+micro 48 5f31b4484305dbe9+micro 49 842be8c228f50882+micro 63 fd8fc3f2f5223ee6+micro 64 44d70d4c8b15579f+micro 65 f6bbae844d4d4181+micro 79 58026c3618a2a508+micro 80 df336fbf7f62d83b+micro 81 082807afd7b12fb2+micro 111 c647d4353ec1d7e6+micro 112 612fb9039abc2b24+micro 113 0310109732a00c86+micro 127 da335cdf507c6503+micro 128 55f26763c2d4f434+micro 129 4dd46b82a5c01d10+micro 191 0c8368fd78d8d79b+micro 192 4d48c4b4acf26329+micro 193 85b09015f8cdb7b4+micro 223 1bae47622466c8ea+micro 224 8bd5ea592b313576+micro 225 175100fa9e5867cc+micro 255 03a1de4052fad3fc+micro 256 e17dc92420e21643+micro 257 13b2f99222a3143d+micro 447 31168fbd47b630c2+micro 448 bce8c898ef125347+micro 449 fefc51f9c37a8840+micro 511 1e1480ba4aa269d6+micro 512 015ac81805fd6c96+micro 1023 4ffdb20b18464bec+micro 1024 20030530f6314046+micro 4096 8c34b5dcd4aa3e13+micro 65535 9d82472e94cd1a74+micro 65536 94258402d3266410+micro 300007 f6a75558d6bc767e+microWithSeed 0 0000000000000000 0338dc4be2cecdae+microWithSeed 0 0000000000000001 ad700ecdf353d5ca+microWithSeed 0 00000000deadbeef 842e1a82b8ea7243+microWithSeed 0 123456789abcdef0 66db63b3916becf4+microWithSeed 0 ffffffffffffffff 9a9c59147a213be8+microWithSeed 1 0000000000000000 e8d3b882671125a6+microWithSeed 1 0000000000000001 ab407ce6511467c4+microWithSeed 1 00000000deadbeef 6129164d197e499c+microWithSeed 1 123456789abcdef0 6833d7e3e8065a9f+microWithSeed 1 ffffffffffffffff af19bbe1857eda2b+microWithSeed 2 0000000000000000 72f646f85991e6ff+microWithSeed 2 0000000000000001 2eee397c4532100e+microWithSeed 2 00000000deadbeef caac963ed10e31e5+microWithSeed 2 123456789abcdef0 f22714f0fdf61dde+microWithSeed 2 ffffffffffffffff c4b1f8698f1cdd9a+microWithSeed 3 0000000000000000 c76c8514de0eb9f0+microWithSeed 3 0000000000000001 fa3c53e10185181c+microWithSeed 3 00000000deadbeef f5f25d77cd27c8c3+microWithSeed 3 123456789abcdef0 e7da94088e9d352a+microWithSeed 3 ffffffffffffffff d72c2937bcd00a14+microWithSeed 4 0000000000000000 54ceab033b17ed48+microWithSeed 4 0000000000000001 4986b01041f8f9d8+microWithSeed 4 00000000deadbeef 9de3be9cd249d4d3+microWithSeed 4 123456789abcdef0 403fbc89ba50a3e0+microWithSeed 4 ffffffffffffffff 572e595c91494030+microWithSeed 5 0000000000000000 927048267a1e0e6c+microWithSeed 5 0000000000000001 5193f513a7783e25+microWithSeed 5 00000000deadbeef b7e5650ac2a5170e+microWithSeed 5 123456789abcdef0 0787592e5c0f371e+microWithSeed 5 ffffffffffffffff f832cd04e84e96b0+microWithSeed 6 0000000000000000 8a479b23d49ea8c0+microWithSeed 6 0000000000000001 6f87e160ee3f0d00+microWithSeed 6 00000000deadbeef a522b82d7180e9f4+microWithSeed 6 123456789abcdef0 856349c2ac3dec25+microWithSeed 6 ffffffffffffffff 62a7140fe38a9f23+microWithSeed 7 0000000000000000 6c91739e11166758+microWithSeed 7 0000000000000001 11017bb55c556f75+microWithSeed 7 00000000deadbeef 5ff42368a57cfc48+microWithSeed 7 123456789abcdef0 7b321310e63b13c5+microWithSeed 7 ffffffffffffffff f57e96782b26e319+microWithSeed 8 0000000000000000 cb1c61491fd8ed0c+microWithSeed 8 0000000000000001 4221eaa066d079a0+microWithSeed 8 00000000deadbeef a43716be5754d9c8+microWithSeed 8 123456789abcdef0 69e33dd175f91269+microWithSeed 8 ffffffffffffffff 3e021c1802c19e61+microWithSeed 9 0000000000000000 c8c920654ad0a214+microWithSeed 9 0000000000000001 d3cda3b2b3fb361a+microWithSeed 9 00000000deadbeef b1606a4eb9d49c12+microWithSeed 9 123456789abcdef0 80d7ad6b0f34c372+microWithSeed 9 ffffffffffffffff 454f767d1e729858+microWithSeed 10 0000000000000000 1c197f92a46cd6a8+microWithSeed 10 0000000000000001 f4aaa18c11b1880a+microWithSeed 10 00000000deadbeef 2335184b7457b759+microWithSeed 10 123456789abcdef0 d00ed7c6ccf3b1c9+microWithSeed 10 ffffffffffffffff 51333ee955674cdb+microWithSeed 11 0000000000000000 ed353bd1078d750d+microWithSeed 11 0000000000000001 c0aaf8ec129962df+microWithSeed 11 00000000deadbeef 5e3ba7f5376e28c2+microWithSeed 11 123456789abcdef0 7316224e1217eeda+microWithSeed 11 ffffffffffffffff 1501e404ed0c8173+microWithSeed 12 0000000000000000 a7d066f68974928b+microWithSeed 12 0000000000000001 18fa41a627de8ae6+microWithSeed 12 00000000deadbeef d0df8a7a79a40a40+microWithSeed 12 123456789abcdef0 d488a65dd82b12e0+microWithSeed 12 ffffffffffffffff 451c70f6b6907acf+microWithSeed 13 0000000000000000 784066c84e806ea5+microWithSeed 13 0000000000000001 99fa1f10497a31db+microWithSeed 13 00000000deadbeef e1ec8947c089c530+microWithSeed 13 123456789abcdef0 b344d1bd2e98f286+microWithSeed 13 ffffffffffffffff d225da173586c005+microWithSeed 14 0000000000000000 4ae43dd7c228e5d0+microWithSeed 14 0000000000000001 d09aa54913bc8b20+microWithSeed 14 00000000deadbeef 1cb5455fc4da65d4+microWithSeed 14 123456789abcdef0 3958ad503597c1e4+microWithSeed 14 ffffffffffffffff b59881651749ae07+microWithSeed 15 0000000000000000 cf0671c9f0e79123+microWithSeed 15 0000000000000001 ffcd5b7587b3e03b+microWithSeed 15 00000000deadbeef e5337aa3e048c6fb+microWithSeed 15 123456789abcdef0 eea33d3c96cdbc23+microWithSeed 15 ffffffffffffffff d33d8734a2b79584+microWithSeed 16 0000000000000000 51cf6b59dc9af20b+microWithSeed 16 0000000000000001 84ab3e852806f36a+microWithSeed 16 00000000deadbeef a9762803b339f0fd+microWithSeed 16 123456789abcdef0 e4be0a162a51e769+microWithSeed 16 ffffffffffffffff 2ea6a93e9ce31a37+microWithSeed 17 0000000000000000 d72787ae7a2963b8+microWithSeed 17 0000000000000001 9093dae1bd413ea0+microWithSeed 17 00000000deadbeef d6c07c2008ef78d7+microWithSeed 17 123456789abcdef0 46a516c54320aa5c+microWithSeed 17 ffffffffffffffff b4e97bd9a73141f0+microWithSeed 31 0000000000000000 9e168ae2c0b51969+microWithSeed 31 0000000000000001 8f2391580911c9e4+microWithSeed 31 00000000deadbeef 80275fc3bfac3335+microWithSeed 31 123456789abcdef0 7634b61596605b71+microWithSeed 31 ffffffffffffffff 8603fa15b02d472d+microWithSeed 32 0000000000000000 0214629041803a0c+microWithSeed 32 0000000000000001 082341b09271e938+microWithSeed 32 00000000deadbeef 1074a288def125c8+microWithSeed 32 123456789abcdef0 c58c04deaced7e90+microWithSeed 32 ffffffffffffffff ee2301861ae63206+microWithSeed 33 0000000000000000 c8c2c6562d2ec99b+microWithSeed 33 0000000000000001 52ce883d43df49e3+microWithSeed 33 00000000deadbeef 5963dad2db66368b+microWithSeed 33 123456789abcdef0 f8d6ffc1efd49dcf+microWithSeed 33 ffffffffffffffff 70a8eea0202f83dd+microWithSeed 47 0000000000000000 d86d732eb077836d+microWithSeed 47 0000000000000001 a98da65e2ac325c6+microWithSeed 47 00000000deadbeef 65f7976f3bc306f8+microWithSeed 47 123456789abcdef0 8270fa92c75b6810+microWithSeed 47 ffffffffffffffff 144281ea7877c5f2+microWithSeed 48 0000000000000000 5f31b4484305dbe9+microWithSeed 48 0000000000000001 5a6418ef65972d4f+microWithSeed 48 00000000deadbeef e53b01b9b393c510+microWithSeed 48 123456789abcdef0 c7959121472d055a+microWithSeed 48 ffffffffffffffff d88f445b35e4715b+microWithSeed 49 0000000000000000 842be8c228f50882+microWithSeed 49 0000000000000001 1de81b8817a8f2ca+microWithSeed 49 00000000deadbeef cac1555db0979cbf+microWithSeed 49 123456789abcdef0 a809d1335aa96fef+microWithSeed 49 ffffffffffffffff d59cc76de50d9c53+microWithSeed 63 0000000000000000 fd8fc3f2f5223ee6+microWithSeed 63 0000000000000001 0318cd1daf57d224+microWithSeed 63 00000000deadbeef d2cd6e2634856170+microWithSeed 63 123456789abcdef0 644ce15944af559a+microWithSeed 63 ffffffffffffffff a2e3a95ec415bbee+microWithSeed 64 0000000000000000 44d70d4c8b15579f+microWithSeed 64 0000000000000001 7c2b184ad7a6be12+microWithSeed 64 00000000deadbeef 06abbe2cf54b915e+microWithSeed 64 123456789abcdef0 2e200f15432403d2+microWithSeed 64 ffffffffffffffff 8393fe721236a9bc+microWithSeed 65 0000000000000000 f6bbae844d4d4181+microWithSeed 65 0000000000000001 08a7b7d8e1e46e8a+microWithSeed 65 00000000deadbeef 8838e9a62419e83e+microWithSeed 65 123456789abcdef0 567fedbbdf09484e+microWithSeed 65 ffffffffffffffff 428ac41664fa5e19+microWithSeed 79 0000000000000000 58026c3618a2a508+microWithSeed 79 0000000000000001 70311e9471ead990+microWithSeed 79 00000000deadbeef ebb07db8ea173c54+microWithSeed 79 123456789abcdef0 f9a2970aed707e73+microWithSeed 79 ffffffffffffffff 2dd5b590dda181c6+microWithSeed 80 0000000000000000 df336fbf7f62d83b+microWithSeed 80 0000000000000001 a62239ae33152fd6+microWithSeed 80 00000000deadbeef 547ceac21c5de14f+microWithSeed 80 123456789abcdef0 8463e1bbfaf437d0+microWithSeed 80 ffffffffffffffff 5978ffc6cd7c56d1+microWithSeed 81 0000000000000000 082807afd7b12fb2+microWithSeed 81 0000000000000001 354c9fd7534c3c8a+microWithSeed 81 00000000deadbeef 8858c66d7a436cb9+microWithSeed 81 123456789abcdef0 59071631a5f09137+microWithSeed 81 ffffffffffffffff 77875953d208e4e1+microWithSeed 111 0000000000000000 c647d4353ec1d7e6+microWithSeed 111 0000000000000001 7c4c2c2d3fa83a76+microWithSeed 111 00000000deadbeef 222bdd3c21f0e941+microWithSeed 111 123456789abcdef0 e00c08156b240957+microWithSeed 111 ffffffffffffffff 4e067c8c54320471+microWithSeed 112 0000000000000000 612fb9039abc2b24+microWithSeed 112 0000000000000001 0f246a8e569dc4d3+microWithSeed 112 00000000deadbeef 44524c221f57506a+microWithSeed 112 123456789abcdef0 a393253de76c168f+microWithSeed 112 ffffffffffffffff fc50425a51dd18fe+microWithSeed 113 0000000000000000 0310109732a00c86+microWithSeed 113 0000000000000001 4da9861e252ae801+microWithSeed 113 00000000deadbeef 2ed0e5bdc6619928+microWithSeed 113 123456789abcdef0 7820b994d1b9b805+microWithSeed 113 ffffffffffffffff 9b2a119afe1aeb01+microWithSeed 127 0000000000000000 da335cdf507c6503+microWithSeed 127 0000000000000001 1f0ed9c24e32c48e+microWithSeed 127 00000000deadbeef 4584d77137d6b3df+microWithSeed 127 123456789abcdef0 2fa8e0c79df28ce5+microWithSeed 127 ffffffffffffffff 0d267edbd29b41db+microWithSeed 128 0000000000000000 55f26763c2d4f434+microWithSeed 128 0000000000000001 a3f412e9368d52db+microWithSeed 128 00000000deadbeef 9e04a11a1bbd7ff6+microWithSeed 128 123456789abcdef0 055220527020b661+microWithSeed 128 ffffffffffffffff 1c5e76b4a502d6fa+microWithSeed 129 0000000000000000 4dd46b82a5c01d10+microWithSeed 129 0000000000000001 c824191550708db0+microWithSeed 129 00000000deadbeef 16ea5a4a90fdfad1+microWithSeed 129 123456789abcdef0 4ceeab827796a2cf+microWithSeed 129 ffffffffffffffff 8785f66fec4f62a2+microWithSeed 191 0000000000000000 0c8368fd78d8d79b+microWithSeed 191 0000000000000001 0cdf2e678b638e97+microWithSeed 191 00000000deadbeef 6610064176919381+microWithSeed 191 123456789abcdef0 b1380fa9a0f99500+microWithSeed 191 ffffffffffffffff 6afa2ebbfaa80cc1+microWithSeed 192 0000000000000000 4d48c4b4acf26329+microWithSeed 192 0000000000000001 185dc2129ddd89a6+microWithSeed 192 00000000deadbeef f9335a1b2a6a20cb+microWithSeed 192 123456789abcdef0 dde407b46f3db6b7+microWithSeed 192 ffffffffffffffff 845b19b178bdb8d0+microWithSeed 193 0000000000000000 85b09015f8cdb7b4+microWithSeed 193 0000000000000001 ef3b4c33aa95beb1+microWithSeed 193 00000000deadbeef 278228e61b3c8d2e+microWithSeed 193 123456789abcdef0 7da960cf7a2b989b+microWithSeed 193 ffffffffffffffff a92eab4230764aa2+microWithSeed 223 0000000000000000 1bae47622466c8ea+microWithSeed 223 0000000000000001 f5057cf8956a44fc+microWithSeed 223 00000000deadbeef 233a780bb2f956c4+microWithSeed 223 123456789abcdef0 298ea5e810cc9fd5+microWithSeed 223 ffffffffffffffff 4d97a7496172296e+microWithSeed 224 0000000000000000 8bd5ea592b313576+microWithSeed 224 0000000000000001 48f1f3b59057c1af+microWithSeed 224 00000000deadbeef dcead34099eccf47+microWithSeed 224 123456789abcdef0 e00fe9d70bd404dd+microWithSeed 224 ffffffffffffffff b7c1608f4a2d3821+microWithSeed 225 0000000000000000 175100fa9e5867cc+microWithSeed 225 0000000000000001 a82831e5f4df54da+microWithSeed 225 00000000deadbeef 542319cd90a4444a+microWithSeed 225 123456789abcdef0 0c7ba34e8a1a20a5+microWithSeed 225 ffffffffffffffff c409bf1b8296e06b+microWithSeed 255 0000000000000000 03a1de4052fad3fc+microWithSeed 255 0000000000000001 92e79ded6e629341+microWithSeed 255 00000000deadbeef e4890f66b4e01b72+microWithSeed 255 123456789abcdef0 db4af919d8ff5c2b+microWithSeed 255 ffffffffffffffff 1297ae1a1dbcf3b9+microWithSeed 256 0000000000000000 e17dc92420e21643+microWithSeed 256 0000000000000001 ab7f2e74969c2fea+microWithSeed 256 00000000deadbeef fbca3edc7fc6d268+microWithSeed 256 123456789abcdef0 d411f79b64bc9376+microWithSeed 256 ffffffffffffffff 2207c0617ee89d84+microWithSeed 257 0000000000000000 13b2f99222a3143d+microWithSeed 257 0000000000000001 2a8e6290bae4b399+microWithSeed 257 00000000deadbeef 466b591f465c5e5e+microWithSeed 257 123456789abcdef0 533934608ecabf8e+microWithSeed 257 ffffffffffffffff 5f969aac99056257+microWithSeed 447 0000000000000000 31168fbd47b630c2+microWithSeed 447 0000000000000001 7781a1eea0ee0f75+microWithSeed 447 00000000deadbeef ea89d984f315370c+microWithSeed 447 123456789abcdef0 a432aaf11870d617+microWithSeed 447 ffffffffffffffff 717e22c920e018bf+microWithSeed 448 0000000000000000 bce8c898ef125347+microWithSeed 448 0000000000000001 01265d1a300b5531+microWithSeed 448 00000000deadbeef b6f4f97c1890e305+microWithSeed 448 123456789abcdef0 1b565c7b9e43a64d+microWithSeed 448 ffffffffffffffff 3a81438dc0d37e9c+microWithSeed 449 0000000000000000 fefc51f9c37a8840+microWithSeed 449 0000000000000001 a5753c3964ffeac9+microWithSeed 449 00000000deadbeef 857713cb2e4bdb59+microWithSeed 449 123456789abcdef0 5a8a4518d18a443b+microWithSeed 449 ffffffffffffffff ae67d0fcc7fa9230+microWithSeed 511 0000000000000000 1e1480ba4aa269d6+microWithSeed 511 0000000000000001 6364b390ea4ceef7+microWithSeed 511 00000000deadbeef c0f3524a5f614dec+microWithSeed 511 123456789abcdef0 eb6d693b8b4157b0+microWithSeed 511 ffffffffffffffff a141bcfd26a61242+microWithSeed 512 0000000000000000 015ac81805fd6c96+microWithSeed 512 0000000000000001 d1dbeaca2ae2cf3b+microWithSeed 512 00000000deadbeef 65f913dabf1c0799+microWithSeed 512 123456789abcdef0 35c97e2519f499d2+microWithSeed 512 ffffffffffffffff 50416c80dd62a6df+microWithSeed 1023 0000000000000000 4ffdb20b18464bec+microWithSeed 1023 0000000000000001 9515a02652597893+microWithSeed 1023 00000000deadbeef 26680c9f357f77f0+microWithSeed 1023 123456789abcdef0 e4f860e0aa2f34ca+microWithSeed 1023 ffffffffffffffff 5550c24acff28af8+microWithSeed 1024 0000000000000000 20030530f6314046+microWithSeed 1024 0000000000000001 4b44b0f7d70e6c99+microWithSeed 1024 00000000deadbeef 2375c4a0bb64b263+microWithSeed 1024 123456789abcdef0 e996fce3abe50dde+microWithSeed 1024 ffffffffffffffff 3118132114e9be18+microWithSeed 4096 0000000000000000 8c34b5dcd4aa3e13+microWithSeed 4096 0000000000000001 742724861cd01b73+microWithSeed 4096 00000000deadbeef c3998e7b15384797+microWithSeed 4096 123456789abcdef0 24747da00cf7dfe8+microWithSeed 4096 ffffffffffffffff 673b3377e214632f+microWithSeed 65535 0000000000000000 9d82472e94cd1a74+microWithSeed 65535 0000000000000001 94c43af7820805b5+microWithSeed 65535 00000000deadbeef b69360397c74f3a2+microWithSeed 65535 123456789abcdef0 6172fc82e3318070+microWithSeed 65535 ffffffffffffffff 43b6259dd190ae45+microWithSeed 65536 0000000000000000 94258402d3266410+microWithSeed 65536 0000000000000001 60ac141ec805c71d+microWithSeed 65536 00000000deadbeef d35954764df3d7bb+microWithSeed 65536 123456789abcdef0 38be0ac62addfa3f+microWithSeed 65536 ffffffffffffffff 9338701d80fd3b5f+microWithSeed 300007 0000000000000000 f6a75558d6bc767e+microWithSeed 300007 0000000000000001 d843f116cd402cf8+microWithSeed 300007 00000000deadbeef a9ab2293b1029809+microWithSeed 300007 123456789abcdef0 8f06292f653a488e+microWithSeed 300007 ffffffffffffffff da16c13a44b13331+microOffset 1 0 0000000000000000 0338dc4be2cecdae+microOffset 1 0 00000000deadbeef 842e1a82b8ea7243+microOffset 1 7 0000000000000000 f7854b11f9682cd0+microOffset 1 7 00000000deadbeef ecdfba4a43fe7737+microOffset 1 16 0000000000000000 17aeb00945b41c70+microOffset 1 16 00000000deadbeef 0d2fbc3c4cbaa6e9+microOffset 1 63 0000000000000000 1599640c41715038+microOffset 1 63 00000000deadbeef ef83c73a68443ad6+microOffset 1 64 0000000000000000 55023c50907b1303+microOffset 1 64 00000000deadbeef 39f2097c2146e40d+microOffset 1 257 0000000000000000 9ee1b579ae9f8773+microOffset 1 257 00000000deadbeef 7c387b32393994f1+microOffset 3 0 0000000000000000 0338dc4be2cecdae+microOffset 3 0 00000000deadbeef 842e1a82b8ea7243+microOffset 3 7 0000000000000000 da92d24f8013069c+microOffset 3 7 00000000deadbeef 1bd08cb2b539f027+microOffset 3 16 0000000000000000 ad13b6d5232be413+microOffset 3 16 00000000deadbeef c20ae581e6351fee+microOffset 3 63 0000000000000000 a39e7ab833156c09+microOffset 3 63 00000000deadbeef 3073dc0f20034461+microOffset 3 64 0000000000000000 c807fd33c06b1a8c+microOffset 3 64 00000000deadbeef ec371bd0069403cf+microOffset 3 257 0000000000000000 9fa2b82cca3e9468+microOffset 3 257 00000000deadbeef 4f87a47665d67b44+microOffset 8 0 0000000000000000 0338dc4be2cecdae+microOffset 8 0 00000000deadbeef 842e1a82b8ea7243+microOffset 8 7 0000000000000000 2129ff6a4225f316+microOffset 8 7 00000000deadbeef 38f3d3ad12f181ce+microOffset 8 16 0000000000000000 25ec3172c4972990+microOffset 8 16 00000000deadbeef 706eb2d1bb3e0469+microOffset 8 63 0000000000000000 705ee8c91cc34e14+microOffset 8 63 00000000deadbeef 7b109ede278365db+microOffset 8 64 0000000000000000 4b9b56e6ba4f60c9+microOffset 8 64 00000000deadbeef 149c32c6cc8d70d1+microOffset 8 257 0000000000000000 3f7fb08ee5c752c0+microOffset 8 257 00000000deadbeef dd2c9ef2bdc28e4e+microOffset 17 0 0000000000000000 0338dc4be2cecdae+microOffset 17 0 00000000deadbeef 842e1a82b8ea7243+microOffset 17 7 0000000000000000 5023d7ddd4325e35+microOffset 17 7 00000000deadbeef 704bf26ff2d3c989+microOffset 17 16 0000000000000000 e193e06a17813552+microOffset 17 16 00000000deadbeef 54d2b9ee93de7b87+microOffset 17 63 0000000000000000 4d37066d271a344d+microOffset 17 63 00000000deadbeef 25baa3dcd571ef7c+microOffset 17 64 0000000000000000 583de726b937be09+microOffset 17 64 00000000deadbeef 15c05b7fa243027f+microOffset 17 257 0000000000000000 0d90c36e754d26b3+microOffset 17 257 00000000deadbeef 57d54fe4913213dc