symbolize 1.0.1.0 → 1.0.2.0
raw patch · 5 files changed
+110/−27 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Symbolize: internUnsafe :: Textual str => str -> Symbol
+ Symbolize: internUnsafe# :: Textual str => str -> Symbol#
+ Symbolize.Textual: toShortTextUnsafe :: Textual a => a -> ShortText
Files
- CHANGELOG.md +6/−0
- src/Symbolize.hs +28/−3
- src/Symbolize/SymbolTable.hs +8/−3
- src/Symbolize/Textual.hs +67/−20
- symbolize.cabal +1/−1
CHANGELOG.md view
@@ -8,6 +8,12 @@ ## Unreleased +## 1.0.2.0++- Adds `Textual.toShortTextUnsafe` and related `internUnsafe` and `internUnsafe#`; these skip UTF-8 validity checks. Very useful when working with trusted serialized data.+ - rename `intern##` to `internUnsafe##` (the old name is marked as deprecated.)+- Cleans up `mkWeakSymbol` to be slightly less sketchy in the presence of `accursedUnutterablePerformIO`; thanks @fatho!+ ## 1.0.1.0 - Add `Data.Data` instance
src/Symbolize.hs view
@@ -123,6 +123,7 @@ ( -- * Symbol Symbol (..), intern,+ internUnsafe, unintern, lookup, @@ -134,6 +135,7 @@ -- * manipulate unlifted Symbols directly Symbol#, intern#,+ internUnsafe#, intern##, unintern#, unintern##,@@ -166,7 +168,6 @@ import Prelude hiding (lookup) import Data.Data qualified import Data.Binary qualified-import Data.Text.Short (ShortText) -- | A string-like type with O(1) equality and comparison. --@@ -293,10 +294,28 @@ {-# INLINE intern# #-} intern# !str = intern## (textualToBA# str) --- | Version of `intern` that directly works on an unlifted `ByteArray#` and returns an unlifted `Symbol#`++-- | Like `intern`, but skips any potential UTF-8 validation+internUnsafe :: (Textual str) => str -> Symbol+{-# INLINE internUnsafe #-}+internUnsafe !str = Symbol (internUnsafe# str)++-- | Like `intern#`, but skips any potential UTF-8 validation+internUnsafe# :: (Textual str) => str -> Symbol#+{-# INLINE internUnsafe# #-}+internUnsafe# !str = intern## (textualToBAUnsafe# str)++{-# DEPRECATED intern## "Renamed to `internUnsafe##`" #-} intern## :: ByteArray# -> Symbol# {-# INLINE intern## #-}-intern## ba# =+intern## = internUnsafe##++-- | Version of `intern`/`internUnsafe` that directly works on an unlifted `ByteArray#` and returns an unlifted `Symbol#`+--+-- Does not do any checking for UTF-8 validity on the `ByteArray#` beforehand.+internUnsafe## :: ByteArray# -> Symbol#+{-# INLINE internUnsafe## #-}+internUnsafe## ba# = -- SAFETY: We're actually happy with let-floating/combining -- since the result is 'outwardly pure' and doing less work is better! let !(ByteArray ba2#) = Accursed.accursedUnutterablePerformIO (SymbolTable.insertGlobal ba#)@@ -390,6 +409,12 @@ {-# INLINE textualToBA# #-} textualToBA# !str = let !(SBS ba#) = Text.Short.toShortByteString (Textual.toShortText str)+ in ba#++textualToBAUnsafe# :: (Textual str) => str -> ByteArray#+{-# INLINE textualToBAUnsafe# #-}+textualToBAUnsafe# !str =+ let !(SBS ba#) = Text.Short.toShortByteString (Textual.toShortTextUnsafe str) in ba# textualFromBA# :: (Textual str) => ByteArray# -> str
src/Symbolize/SymbolTable.hs view
@@ -78,7 +78,13 @@ insertGlobal ba# = do GlobalSymbolTable gsymtab sipkey <- globalSymbolTable let !key = calculateHash sipkey ba#- let !weak = mkWeakSymbol ba# (removeGlobal key)+ -- SAFETY: If the table IORef contested, + -- this might trigger `weak` creation for the same bytestring from multiple threads+ -- at the same time.+ -- But finalization is idempotent, and only when a thread finally wins the Compare-and-Swap+ -- will its `weak` pointer be inserted (or alternatively another previously-inserted `ba` returned).+ -- So once this function returns, we can be sure we've returned a deduplicated ByteArray+ !weak <- mkWeakSymbol ba# (removeGlobal key) IORef.atomicModifyIORef' gsymtab $ \table -> case lookup ba# sipkey table of Just ba -> (table, ba)@@ -134,7 +140,7 @@ let (SipHash.SipHash word) = SipHash.hash sipkey (ByteArray ba#) in Hash (fromIntegral word) -mkWeakSymbol :: ByteArray# -> IO () -> WeakSymbol+mkWeakSymbol :: ByteArray# -> IO () -> IO WeakSymbol {-# INLINE mkWeakSymbol #-} mkWeakSymbol ba# (IO finalizer#) = -- SAFETY: This should even be safe@@ -142,7 +148,6 @@ -- -- because the result is outwardly pure -- and the finalizer we use is idempotent- Symbolize.Accursed.accursedUnutterablePerformIO $ IO $ \s1 -> case mkWeak# ba# ba# finalizer# s1 of (# s2, weak# #) -> case makeStableName# ba# s2 of
src/Symbolize/Textual.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE InstanceSigs #-} -- NOTE: FlexibleInstances is needed to support `String` instance :-( @@ -13,10 +15,10 @@ import qualified Data.Text.Encoding as Text.Encoding import qualified Data.Text.Encoding.Error as Text.Encoding.Error import qualified Data.Text.Lazy as LText-import Data.Text.Lazy.Builder (Builder)-import qualified Data.Text.Lazy.Builder as Builder+import qualified Data.Text.Lazy.Builder as Text.Builder import Data.Text.Short (ShortText) import qualified Data.Text.Short as ShortText+import qualified Data.Text.Short.Unsafe as ShortText.Unsafe -- | Implemented by any String-like types. -- The symbol table uses `ShortText` for its internal storage, so any type which can be converted to it@@ -25,82 +27,127 @@ -- Instance should handle potential invalid UTF-8 by using the Unicode replacement character, -- c.f. `Data.Text.Encoding.Error.lenientDecode`. class Textual a where+ -- | Turns this text value into a `ShortText`, performing UTF-8 checking if necessary toShortText :: a -> ShortText++ -- | Turns this text value into a `ShortText`, potentially skipping UTF-8 checks+ toShortTextUnsafe :: a -> ShortText+ toShortTextUnsafe = toShortText++ -- | Turns a `ShortText` value into a different textual value fromShortText :: ShortText -> a -- | -- - O(0) conversion (a no-op) instance Textual ShortText where- toShortText = id+ toShortText :: ShortText -> ShortText {-# INLINE toShortText #-}- fromShortText = id+ toShortText = id++ toShortTextUnsafe :: ShortText -> ShortText+ {-# INLINE toShortTextUnsafe #-}+ toShortTextUnsafe = id++ fromShortText :: ShortText -> ShortText {-# INLINE fromShortText #-}+ fromShortText = id -- | -- - O(1) conversion instance Textual Text where- toShortText = ShortText.fromText+ toShortText :: Text -> ShortText {-# INLINE toShortText #-}- fromShortText = ShortText.toText+ toShortText = ShortText.fromText++ fromShortText :: ShortText -> Text {-# INLINE fromShortText #-}+ fromShortText = ShortText.toText -- | -- - O(n) conversion instance Textual String where- toShortText = ShortText.fromString+ toShortText :: String -> ShortText {-# INLINE toShortText #-}- fromShortText = ShortText.toString+ toShortText = ShortText.fromString++ fromShortText :: ShortText -> String {-# INLINE fromShortText #-}+ fromShortText = ShortText.toString -- | -- - O(1) conversion instance Textual LText.Text where- toShortText = ShortText.fromText . LText.toStrict+ toShortText :: LText.Text -> ShortText {-# INLINE toShortText #-}- fromShortText = LText.fromStrict . ShortText.toText+ toShortText = ShortText.fromText . LText.toStrict++ fromShortText :: ShortText -> LText.Text {-# INLINE fromShortText #-}+ fromShortText = LText.fromStrict . ShortText.toText -- | -- - toShortText: O(n). Evaluates the entire builder. -- - fromShortText: O(1)-instance Textual Builder where- toShortText = ShortText.fromText . LText.toStrict . Builder.toLazyText+instance Textual Text.Builder.Builder where+ toShortText :: Text.Builder.Builder -> ShortText {-# INLINE toShortText #-}- fromShortText = Builder.fromText . ShortText.toText+ toShortText = ShortText.fromText . LText.toStrict . Text.Builder.toLazyText++ fromShortText :: ShortText -> Text.Builder.Builder {-# INLINE fromShortText #-}+ fromShortText = Text.Builder.fromText . ShortText.toText -- | -- - toShortText: O(n). Turns invalid UTF-8 into the Unicode replacement character. -- - fromShortText: O(0) no-op instance Textual ShortByteString where+ toShortText :: ShortByteString -> ShortText+ {-# INLINE toShortText #-} toShortText byteString = byteString & ShortByteString.fromShort & Text.Encoding.decodeUtf8With Text.Encoding.Error.lenientDecode & ShortText.fromText- {-# INLINE toShortText #-} - fromShortText = ShortText.toShortByteString+ toShortTextUnsafe :: ShortByteString -> ShortText+ {-# INLINE toShortTextUnsafe #-}+ toShortTextUnsafe = ShortText.Unsafe.fromShortByteStringUnsafe++ fromShortText :: ShortText -> ShortByteString {-# INLINE fromShortText #-}+ fromShortText = ShortText.toShortByteString -- | -- - toShortText: O(n). Turns invalid UTF-8 into the Unicode replacement character. -- - fromShortText: O(n). instance Textual ByteString where+ toShortText :: ByteString -> ShortText+ {-# INLINE toShortText #-} toShortText byteString = byteString & Text.Encoding.decodeUtf8With Text.Encoding.Error.lenientDecode & ShortText.fromText- {-# INLINE toShortText #-} - fromShortText = ShortText.toByteString+ toShortTextUnsafe :: ByteString -> ShortText+ {-# INLINE toShortTextUnsafe #-}+ toShortTextUnsafe = toShortTextUnsafe . ShortByteString.toShort++ fromShortText :: ShortText -> ByteString {-# INLINE fromShortText #-}+ fromShortText = ShortText.toByteString -- | -- - toShortText: O(n). Turns invalid UTF-8 into the Unicode replacement character. -- - fromShortText: O(0) no-op instance Textual ByteArray where+ toShortText :: ByteArray -> ShortText+ {-# INLINE toShortText #-} toShortText (ByteArray ba) = toShortText (ShortByteString.SBS ba)- fromShortText short =- let !(ShortByteString.SBS ba) = fromShortText short- in ByteArray ba++ toShortTextUnsafe :: ByteArray -> ShortText+ {-# INLINE toShortTextUnsafe #-}+ toShortTextUnsafe (ByteArray ba) = toShortTextUnsafe (ShortByteString.SBS ba)++ fromShortText :: ShortText -> ByteArray+ {-# INLINE fromShortText #-}+ fromShortText (fromShortText -> ShortByteString.SBS ba) = ByteArray ba
symbolize.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: symbolize-version: 1.0.1.0+version: 1.0.2.0 synopsis: Efficient global Symbol table, with Garbage Collection. description: Symbols, also known as Atoms or Interned Strings, are a common technique to reduce memory usage and improve performance when using many small strings: