packages feed

alfred-margaret 2.1.0.2 → 2.1.1.0

raw patch · 12 files changed

+202/−180 lines, 12 filesdep ~containers

Dependency ranges changed: containers

Files

+ CHANGELOG.md view
@@ -0,0 +1,76 @@+# Changelog++## v2.1.1.0 - "Generically Primitive" (2026-04-24)++Tested on GHC 9.14.1.++- Derive `Generic` for the exposed data types in `Data.Text.AhoCorasick.*`,+  `Data.Text.BoyerMoore.*`, `Data.Text.BoyerMooreCI.*` and+  `Data.Text.CaseSensitivity` ([#65](https://github.com/channable/alfred-margaret/pull/65)).+- Replace the internal `Data.TypedByteArray` wrapper with `PrimArray` from+  `Data.Primitive`; introduces the new internal module `Data.Primitive.Extended`.+- Widen `containers` upper bound to include `0.8`.+- Toolchain: switch development environment and CI to GHC 9.14 and LLVM 19;+  add `direnv` support; bump pinned `nixpkgs` and Nix to 2.24.12.+- Update `tested-with` to GHC 9.14.1.++## v2.1.0.2 - "All the bounds!" (2024-09-06)++Publishing requires `cabal` to know the version bounds as well.++## v2.1.0.1 - "Higher bounds!" (2024-09-05)++Tested on GHC 9.6.6.++- Revise dependency bounds ([#62](https://github.com/channable/alfred-margaret/issues/62) thanks @Bodigrim)+- Allow using primitive < 0.9 and vector < 0.14 ([#59](https://github.com/channable/alfred-margaret/pull/59) thanks @rampion)++## v2.1.0.0 - "All The Cases!" (2022-08-31)++- Added a case-insensitive variant of the Boyer-Moore algorithm in the `Data.Text.BoyerMooreCI.*` modules. ([#47](https://github.com/channable/alfred-margaret/pull/47))+- Fixed a bug in the case-insensitive Aho-Corasick replacer where it would+  replace the wrong section of the haystack when the needle had a different+  byte-length than the matching part of the haystack. ([#47](https://github.com/channable/alfred-margaret/pull/47))+- Allow mapping the payloads of Aho-Corasick automatons. ([#46](https://github.com/channable/alfred-margaret/pull/46))++## v2.0.0.0 - "So Long Surrogates" (2022-05-02)++Switched to text-2.0 which uses UTF-8 encoding internally.++- Removed `Data.Text.Utf8.*` modules+- Replaced `Data.Text.AhoCorasick.*` and `Data.Text.BoyerMoore.*` (previously using UTF-16) with the UTF-8 implementation++## v1.1.2.0 - "ByteArray Boogaloo" (2022-04-21)++Added UTF-8 implementations on a mock `Text` type (in `Data.Text.Utf8`).++- Added `Data.Text.Utf8*` modules+- Moved `CaseSensitivity` to its own `Data.Text.CaseSensitivity` module.+- Added the private module `Data.TypedByteArray` which contains thin wrappers over `ByteArray` and `MutableByteArray`.+- Replaced uses of `Data.Vector.Unboxed.Vector` by `TypedByteArray`.++## v1.1.0.0 - "Moore Features" (2020-10-13)++The most notable addition in this release is the implementation of the Boyer-Moore string search algorithm.++**Compatibility:**++- Extracted the UTF-16 manipulation functions from `Data.Text.AhoCorasick.Automaton` into `Data.Text.Utf16`+- Changed `Data.Text.AhoCorasick.Searcher.Searcher` to remember the case sensitivity used for constructing the searcher+- Removed `Data.Text.AhoCorasick.Searcher.containsAnyIgnoreCase`, the correct implementation is now chosen by `containsAny` based on the case sensitivity of the searcher++Other changes:++- Added `Data.Text.AhoCorasick.Splitter` for splitting a lot of text using the same needle+- Added `Data.Text.BoyerMoore.Automaton`, a UTF-16 implementation of Boyer-Moore+- Added `Data.Text.BoyerMoore.Searcher` for searching for multiple needles at once using Boyer-Moore+- Added `Data.Text.BoyerMoore.Replacer` for replacing text based on the Boyer-Moore search+- Added optional `FromJSON`/`ToJSON` instances for most types (can be toggled via `aeson` cabal flag)++## v1.0.0.0 - "Initial Release" (2019-03-19)++This is the initial open-source release.++- Added `Data.Text.AhoCorasick.Automaton`, a UTF-16 implementation of the Aho-Corasick search algorithm+- Added `Data.Text.AhoCorasick.Searcher`, a bulk search abstraction based on Aho-Corasick+- Added `Data.Text.AhoCorasick.Replacer`, a bulk replace abstraction based on Aho-Corasick
alfred-margaret.cabal view
@@ -1,5 +1,5 @@ name:                alfred-margaret-version:             2.1.0.2+version:             2.1.1.0 synopsis:            Fast Aho-Corasick string searching description:         An efficient implementation of the Aho-Corasick                      string searching algorithm.@@ -12,6 +12,7 @@ category:            Data, Text build-type:          Simple extra-source-files:  README.md+                   , CHANGELOG.md                    , performance.png cabal-version:       >=1.10 tested-with:@@ -23,6 +24,8 @@                    , GHC == 9.0.2                      -- Nixpkgs unstable (Updated 2024-09-05)                    , GHC == 9.6.6+                     -- Nixpkgs unstable (Updated 2026-04-22)+                   , GHC == 9.14.1  source-repository head   type:     git@@ -57,10 +60,10 @@                      , Data.Text.BoyerMooreCI.Automaton                      , Data.Text.BoyerMooreCI.Replacer                      , Data.Text.BoyerMooreCI.Searcher-                     , Data.TypedByteArray+  other-modules:       Data.Primitive.Extended   build-depends:       base             >= 4.7 && < 5-    , containers       >= 0.6 && < 0.8+    , containers       >= 0.6 && < 0.9     , deepseq          >= 1.4 && < 1.6     , hashable         >= 1.4.0.2 && < 1.6     , primitive        >= 0.6.4 && < 0.10@@ -76,7 +79,7 @@   cpp-options:         -DHAS_AESON   }   if flag(llvm) {-  ghc-options:         -fllvm -optlo=-O3 -optlo=-tailcallelim+  ghc-options:         -fllvm -optlo=-passes=default<O3>   }  test-suite test-suite
+ src/Data/Primitive/Extended.hs view
@@ -0,0 +1,21 @@+-- | "Data.Primitive" extended with extra definitions.+--+-- Based on the ".Extended Modules" pattern:+-- https://jaspervdj.be/posts/2015-01-20-haskell-design-patterns-extended-modules.html+module Data.Primitive.Extended+  ( module Data.Primitive+  , replicateMutablePrimArray+  )+  where++import Data.Primitive+import Control.Monad.Primitive (PrimMonad(..))++-- | Like 'replicatePrimArray', but does not freeze the array afterwards and+-- stays within a monadic context, so it can easily be mutated further.+{-# INLINE replicateMutablePrimArray #-}+replicateMutablePrimArray :: (Prim a, PrimMonad m) => Int -> a -> m (MutablePrimArray (PrimState m) a)+replicateMutablePrimArray len value = do+    arr <- newPrimArray len+    setPrimArray arr 0 len value+    pure arr
src/Data/Text/AhoCorasick/Automaton.hs view
@@ -46,8 +46,14 @@ import Control.DeepSeq (NFData) import Data.Bits (Bits (shiftL, shiftR, (.&.), (.|.))) import Data.Char (chr)-import Data.Foldable (foldl') import Data.IntMap.Strict (IntMap)+import Data.Primitive.Extended+  ( Prim+  , PrimArray+  , generatePrimArray+  , indexPrimArray+  , primArrayFromList+  ) import Data.Word (Word32, Word64) import GHC.Generics (Generic) @@ -58,11 +64,9 @@  import Data.Text.CaseSensitivity (CaseSensitivity (..)) import Data.Text.Utf8 (CodePoint, CodeUnitIndex (CodeUnitIndex), Text (..))-import Data.TypedByteArray (Prim, TypedByteArray)  import qualified Data.Text as Text import qualified Data.Text.Utf8 as Utf8-import qualified Data.TypedByteArray as TBA  -- TYPES -- | A numbered state in the Aho-Corasick automaton.@@ -105,14 +109,14 @@   { machineValues               :: !(Vector.Vector [v])   -- ^ For every state, the values associated with its needles. If the state is   -- not a match state, the list is empty.-  , machineTransitions          :: !(TypedByteArray Transition)+  , machineTransitions          :: !(PrimArray Transition)   -- ^ A packed vector of transitions. For every state, there is a slice of this   -- vector that starts at the offset given by `machineOffsets`, and ends at the   -- first wildcard transition.-  , machineOffsets              :: !(TypedByteArray Offset)+  , machineOffsets              :: !(PrimArray Offset)   -- ^ For every state, the index into `machineTransitions` where the transition   -- list for that state starts.-  , machineRootAsciiTransitions :: !(TypedByteArray Transition)+  , machineRootAsciiTransitions :: !(PrimArray Transition)   -- ^ A lookup table for transitions from the root state, an optimization to   -- avoid having to walk all transitions, at the cost of using a bit of   -- additional memory.@@ -159,11 +163,11 @@ -- the transitions for a specific state, we also produce a vector of start -- indices. All transition lists are terminated by a wildcard transition, so -- there is no need to record the length.-packTransitions :: [[Transition]] -> (TypedByteArray Transition, TypedByteArray Offset)+packTransitions :: [[Transition]] -> (PrimArray Transition, PrimArray Offset) packTransitions transitions =   let-    packed = TBA.fromList $ concat transitions-    offsets = TBA.fromList $ map fromIntegral $ scanl (+) 0 $ fmap List.length transitions+    packed = primArrayFromList $ concat transitions+    offsets = primArrayFromList $ map fromIntegral $ scanl (+) 0 $ fmap List.length transitions   in     (packed, offsets) @@ -295,8 +299,8 @@ -- O(1) lookup of a transition, rather than doing a linear scan over all -- transitions. The fallback goes back to the initial state, state 0. {-# NOINLINE buildAsciiTransitionLookupTable  #-}-buildAsciiTransitionLookupTable :: IntMap State -> TypedByteArray Transition-buildAsciiTransitionLookupTable transitions = TBA.generate asciiCount $ \i ->+buildAsciiTransitionLookupTable :: IntMap State -> PrimArray Transition+buildAsciiTransitionLookupTable transitions = generatePrimArray asciiCount $ \i ->   case IntMap.lookup i transitions of     Just state -> newTransition (Char.chr i) state     Nothing    -> newWildcardTransition 0@@ -384,8 +388,8 @@ at = Vector.unsafeIndex  {-# INLINE uAt #-}-uAt :: Prim a => TypedByteArray a -> Int -> a-uAt = TBA.unsafeIndex+uAt :: Prim a => PrimArray a -> Int -> a+uAt = indexPrimArray  -- RUNNING THE MACHINE @@ -560,5 +564,3 @@   where     loop "" = [""]     loop (c:cs) = (:) <$> Utf8.unlowerCodePoint c <*> loop cs--
src/Data/Text/AhoCorasick/Replacer.hs view
@@ -156,7 +156,7 @@ -- A match collected while running replacements. It is isomorphic to the Match -- reported by the automaton, but the data is arranged in a more useful way: -- as the start index and length of the match, and the replacement.-data Match = Match !CodeUnitIndex !CodeUnitIndex !Text deriving (Eq, Ord, Show)+data Match = Match !CodeUnitIndex !CodeUnitIndex !Text deriving (Eq, Ord, Show, Generic)  -- | Apply replacements of all matches. Assumes that the matches are ordered by -- match position, and that no matches overlap.
src/Data/Text/AhoCorasick/Splitter.hs view
@@ -4,6 +4,8 @@ -- Licensed under the 3-clause BSD license, see the LICENSE file in the -- repository root. +{-# LANGUAGE GHC2021 #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} @@ -19,6 +21,7 @@     , splitReverseIgnoreCase     ) where +import GHC.Generics (Generic) import Control.DeepSeq (NFData (..)) import Data.Function (on) import Data.Hashable (Hashable (..))@@ -46,6 +49,7 @@     { splitterAutomaton :: AcMachine () -- INVARIANT: Exactly one needle.     , splitterSeparator :: Text         -- INVARIANT: Equivalent to needle.     }+  deriving Generic  #if defined(HAS_AESON) instance AE.ToJSON Splitter where@@ -129,6 +133,7 @@     , accumFragmentStart :: !Aho.CodeUnitIndex       -- ^ First byte of current fragment (that is the non-separator part)     }+  deriving Generic  -- | Finalizing the accumulator does more than just 'accumResult', hence this -- is a separate function.
src/Data/Text/BoyerMoore/Automaton.hs view
@@ -37,6 +37,15 @@ import Control.Monad (when) import Control.Monad.ST (runST) import Data.Hashable (Hashable (..))+import Data.Primitive.Extended+  ( Prim+  , PrimArray+  , indexPrimArray+  , newPrimArray+  , replicateMutablePrimArray+  , unsafeFreezePrimArray+  , writePrimArray+  ) import GHC.Generics (Generic)  #if defined(HAS_AESON)@@ -45,10 +54,8 @@  import Data.Text.CaseSensitivity (CaseSensitivity (..)) import Data.Text.Utf8 (CodeUnit, CodeUnitIndex (..), Text)-import Data.TypedByteArray (Prim, TypedByteArray)  import qualified Data.Text.Utf8 as Utf8-import qualified Data.TypedByteArray as TBA  data Next a   = Done !a@@ -167,7 +174,7 @@  -- | The suffix table tells us for each character of the pattern how many characters we can -- jump ahead if the match fails at that point.-newtype SuffixTable = SuffixTable (TypedByteArray Int)+newtype SuffixTable = SuffixTable (PrimArray Int)   deriving stock (Generic, Show)   deriving anyclass (NFData) @@ -180,7 +187,7 @@ buildSuffixTable pattern = runST $ do   let patLen = Utf8.lengthUtf8 pattern -  table <- TBA.newTypedByteArray $ codeUnitIndex patLen+  table <- newPrimArray $ codeUnitIndex patLen    let     -- Case 1: For each position of the pattern we record the shift that would align the pattern so@@ -206,7 +213,7 @@           prefixIndex             | isPrefix pattern (p + 1) = p + 1             | otherwise = lastPrefixIndex-        TBA.writeTypedByteArray table (codeUnitIndex p) (codeUnitIndex $ prefixIndex + patLen - 1 - p)+        writePrimArray table (codeUnitIndex p) (codeUnitIndex $ prefixIndex + patLen - 1 - p)         init1 prefixIndex (p - 1)       | otherwise = pure () @@ -219,21 +226,21 @@         let           suffixLen = suffixLength pattern p         when (Utf8.unsafeIndexCodeUnit pattern (p - suffixLen) /= Utf8.unsafeIndexCodeUnit pattern (patLen - 1 - suffixLen)) $-          TBA.writeTypedByteArray table (codeUnitIndex $ patLen - 1 - suffixLen) (codeUnitIndex $ patLen - 1 - p + suffixLen)+          writePrimArray table (codeUnitIndex $ patLen - 1 - suffixLen) (codeUnitIndex $ patLen - 1 - p + suffixLen)         init2 (p + 1)       | otherwise = pure ()    init1 (patLen - 1) (patLen - 1)   init2 0 -  SuffixTable <$> TBA.unsafeFreezeTypedByteArray table+  SuffixTable <$> unsafeFreezePrimArray table   -- | The bad char table tells us how far we may skip ahead when encountering a certain character -- in the input string. For example, if there's a character that is not contained in the pattern at -- all, we can skip ahead until after that character. data BadCharTable = BadCharTable-  { badCharTableEntries :: {-# UNPACK #-} !(TypedByteArray Int)+  { badCharTableEntries :: {-# UNPACK #-} !(PrimArray Int)     -- ^ The element type should be CodeUnitIndex, but there's no unboxed vector for that type, and     -- defining it would be a lot of boilerplate.   , badCharTablePatternLen :: CodeUnitIndex@@ -284,7 +291,7 @@    -- Initialize table with the maximum skip distance, which is the length of the pattern.   -- This applies to all characters that are not part of the pattern.-  asciiTable <- TBA.replicate badcharTableSize $ codeUnitIndex patLen+  asciiTable <- replicateMutablePrimArray badcharTableSize $ codeUnitIndex patLen    let     -- Fill the bad character table based on the rightmost occurrence of a character in the pattern.@@ -319,13 +326,13 @@       -- for(i = 0; i < patLen - 1; i++) {       | i < patLen - 1 = do         let patChar = Utf8.unsafeIndexCodeUnit pattern i-        TBA.writeTypedByteArray asciiTable (fromIntegral patChar) (codeUnitIndex $ patLen - 1 - i)+        writePrimArray asciiTable (fromIntegral patChar) (codeUnitIndex $ patLen - 1 - i)         fillTable (i + 1)       | otherwise = pure ()    fillTable 0 -  asciiTableFrozen <- TBA.unsafeFreezeTypedByteArray asciiTable+  asciiTableFrozen <- unsafeFreezePrimArray asciiTable    pure BadCharTable     { badCharTableEntries = asciiTableFrozen@@ -336,6 +343,6 @@ -- Helper functions for easily toggling the safety of this module  -- | Read from a lookup table at the specified index.-indexTable :: Prim a => TypedByteArray a -> Int -> a+indexTable :: Prim a => PrimArray a -> Int -> a {-# INLINE indexTable #-}-indexTable = TBA.unsafeIndex+indexTable = indexPrimArray
src/Data/Text/BoyerMoore/Replacer.hs view
@@ -4,6 +4,8 @@ -- Licensed under the 3-clause BSD license, see the LICENSE file in the -- repository root. +{-# LANGUAGE GHC2021 #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleInstances #-} @@ -12,6 +14,7 @@       replaceSingleLimited     ) where +import GHC.Generics (Generic) import Data.Text.Utf8 (Text) import Data.Text.BoyerMoore.Automaton (Automaton, CodeUnitIndex) @@ -89,3 +92,4 @@   , rsLength :: !CodeUnitIndex     -- ^ Length of the newly build string so far, measured in CodeUnits   }+  deriving Generic
src/Data/Text/BoyerMooreCI/Automaton.hs view
@@ -37,6 +37,19 @@ import Control.DeepSeq (NFData) import Control.Monad.ST (runST) import Data.Hashable (Hashable (..))+import Data.Primitive.Extended+  ( Prim+  , PrimArray+  , foldrPrimArray+  , indexPrimArray+  , newPrimArray+  , primArrayFromList+  , primArrayToList+  , replicateMutablePrimArray+  , sizeofPrimArray+  , unsafeFreezePrimArray+  , writePrimArray+  ) import Data.Text.Internal (Text (..)) import GHC.Generics (Generic) @@ -46,13 +59,11 @@  import Data.Text.CaseSensitivity (CaseSensitivity (..)) import Data.Text.Utf8 (BackwardsIter (..), CodePoint, CodeUnitIndex (..))-import Data.TypedByteArray (Prim, TypedByteArray)  import qualified Data.Char as Char import qualified Data.HashMap.Strict as HashMap import qualified Data.Text as Text import qualified Data.Text.Utf8 as Utf8-import qualified Data.TypedByteArray as TBA  data Next a   = Done !a@@ -70,7 +81,7 @@ -- finding @aaaa@ in @aaaaa....aaaaaa@ as for each match it would scan back the whole /m/ characters -- of the pattern. data Automaton = Automaton-  { automatonPattern :: !(TypedByteArray CodePoint)+  { automatonPattern :: !(PrimArray CodePoint)   , automatonPatternHash :: !Int   , automatonSuffixTable :: !SuffixTable   , automatonBadCharLookup :: !BadCharLookup@@ -103,7 +114,7 @@     , automatonMinPatternSkip = minimumSkipForVector patternVec     }   where-    patternVec = TBA.fromList (Text.unpack pattern_)+    patternVec = primArrayFromList (Text.unpack pattern_)  -- | Finds all matches in the text, calling the match callback with the first and last byte index of -- each match of the pattern.@@ -115,7 +126,7 @@   -> a {-# INLINE runText #-} runText seed f automaton !text-  | TBA.null pattern_ = seed+  | sizeofPrimArray pattern_ == 0 = seed   | otherwise = alignPattern seed initialHaystackMin (initialHaystackMin + minPatternSkip - 1)   where     Automaton pattern_ _ suffixTable badCharTable minPatternSkip = automaton@@ -142,7 +153,7 @@       | otherwise =           let             !iter = Utf8.unsafeIndexAnywhereInCodePoint' (case text of Text d _ _ -> d) alignmentEnd-            !patternIndex = TBA.length pattern_ - 1+            !patternIndex = sizeofPrimArray pattern_ - 1             -- End of char may be somewhere different than where we started looking             !alignmentEnd' = backwardsIterEndOfChar iter           in@@ -160,7 +171,7 @@       let         !haystackCodePointLower = Utf8.lowerCodePoint (backwardsIterChar iter)       in-        case haystackCodePointLower == TBA.unsafeIndex pattern_ patternIndex of+        case haystackCodePointLower == indexPrimArray pattern_ patternIndex of            True | patternIndex == 0 ->             -- We found a complete match (all pattern characters matched)@@ -214,7 +225,7 @@  -- | Return the pattern that was used to construct the automaton, O(n). patternText :: Automaton -> Text-patternText = Text.pack . TBA.toList . automatonPattern+patternText = Text.pack . primArrayToList . automatonPattern   -- | Number of bytes that we can skip in the haystack if we want to skip no more@@ -248,31 +259,31 @@ --     minimumSkipForVector (TBA.fromList "ab..cd") == 6 --     minimumSkipForVector (TBA.fromList "aⱥ💩") == 7 ---minimumSkipForVector :: TypedByteArray CodePoint -> CodeUnitIndex-minimumSkipForVector = TBA.foldr (\cp s -> s + minimumSkipForCodePoint cp) 0+minimumSkipForVector :: PrimArray CodePoint -> CodeUnitIndex+minimumSkipForVector = foldrPrimArray (\cp s -> s + minimumSkipForCodePoint cp) 0   -- | The suffix table tells us for each codepoint (not byte!) of the pattern how many bytes (not -- codepoints!) we can jump ahead if the match fails at that point.-newtype SuffixTable = SuffixTable (TypedByteArray CodeUnitIndex)+newtype SuffixTable = SuffixTable (PrimArray CodeUnitIndex)   deriving stock (Generic)   deriving anyclass (NFData)  instance Show SuffixTable where-  show (SuffixTable table) = "SuffixTable (TBA.toList " <> show (TBA.toList table) <> ")"+  show (SuffixTable table) = "SuffixTable (TBA.toList " <> show (primArrayToList table) <> ")"  -- | Lookup an entry in the suffix table. suffixLookup :: SuffixTable -> Int -> CodeUnitIndex {-# INLINE suffixLookup #-} suffixLookup (SuffixTable table) = indexTable table -buildSuffixTable :: TypedByteArray CodePoint -> SuffixTable+buildSuffixTable :: PrimArray CodePoint -> SuffixTable buildSuffixTable pattern_ = runST $ do   let-    patLen = TBA.length pattern_+    patLen = sizeofPrimArray pattern_     wholePatternSkip = minimumSkipForVector pattern_ -  table <- TBA.newTypedByteArray patLen+  table <- newPrimArray patLen    let     -- Case 1: For each position of the pattern we record the shift that would align the pattern so@@ -303,7 +314,7 @@                         Nothing -> lastSkipBytes                         -- Skip the whole pattern _except_ the bytes for the suffix(==prefix)                         Just nonSkippableBytes -> wholePatternSkip - nonSkippableBytes-        TBA.writeTypedByteArray table p skipBytes+        writePrimArray table p skipBytes         init1 skipBytes (p - 1)       | otherwise = pure () @@ -314,30 +325,30 @@     init2 p skipBytes       | p < patLen - 1 = do           -- If we find a suffix that ends at p, we can skip everything _after_ p.-          let skipBytes' = skipBytes - minimumSkipForCodePoint (TBA.unsafeIndex pattern_ p)+          let skipBytes' = skipBytes - minimumSkipForCodePoint (indexPrimArray pattern_ p)           case substringIsSuffix pattern_ p of             Nothing -> pure ()             Just suffixLen -> do-              TBA.writeTypedByteArray table (patLen - 1 - suffixLen) skipBytes'+              writePrimArray table (patLen - 1 - suffixLen) skipBytes'           init2 (p + 1) skipBytes'       | otherwise = pure ()    init1 (wholePatternSkip-1) (patLen - 1)   init2 0 wholePatternSkip-  TBA.writeTypedByteArray table (patLen - 1) (CodeUnitIndex 1)+  writePrimArray table (patLen - 1) (CodeUnitIndex 1) -  SuffixTable <$> TBA.unsafeFreezeTypedByteArray table+  SuffixTable <$> unsafeFreezePrimArray table  -- | True if the suffix of the @pattern@ starting from @pos@ is a prefix of the pattern -- For example, @suffixIsPrefix \"aabbaa\" 4 == Just 2@.-suffixIsPrefix :: TypedByteArray CodePoint -> Int -> Maybe CodeUnitIndex+suffixIsPrefix :: PrimArray CodePoint -> Int -> Maybe CodeUnitIndex suffixIsPrefix pattern_ pos = go 0 (CodeUnitIndex 0)   where-    suffixLen = TBA.length pattern_ - pos+    suffixLen = sizeofPrimArray pattern_ - pos     go !i !skipBytes       | i < suffixLen =-          let prefixChar = TBA.unsafeIndex pattern_ i in-          if prefixChar == TBA.unsafeIndex pattern_ (pos + i)+          let prefixChar = indexPrimArray pattern_ i in+          if prefixChar == indexPrimArray pattern_ (pos + i)             then go (i + 1) (skipBytes + minimumSkipForCodePoint prefixChar)             else Nothing       | otherwise = Just skipBytes@@ -362,12 +373,12 @@ --   substringIsSuffix (Vector.fromList "abaacaabcbaac") 4 == Just 4  -- baac == baac --   substringIsSuffix (Vector.fromList "abaacaabcbaac") 8 == Just 1  -- c == c ---substringIsSuffix :: TypedByteArray CodePoint -> Int -> Maybe Int+substringIsSuffix :: PrimArray CodePoint -> Int -> Maybe Int substringIsSuffix pattern_ pos = go 0   where-    patLen = TBA.length pattern_+    patLen = sizeofPrimArray pattern_     go i | i > pos = Nothing  -- prefix==suffix, so already covered by suffixIsPrefix-         | TBA.unsafeIndex pattern_ (pos - i) == TBA.unsafeIndex pattern_ (patLen - 1 - i) =+         | indexPrimArray pattern_ (pos - i) == indexPrimArray pattern_ (patLen - 1 - i) =              go (i + 1)          | i == 0 = Nothing  -- Nothing matched          | otherwise = Just i@@ -377,7 +388,7 @@ -- character in the input string. For example, if there's a character that is not contained in the -- pattern at all, we can skip ahead until after that character. data BadCharLookup = BadCharLookup-  { badCharLookupTable :: {-# UNPACK #-} !(TypedByteArray CodeUnitIndex)+  { badCharLookupTable :: {-# UNPACK #-} !(PrimArray CodeUnitIndex)   , badCharLookupMap :: !(HashMap.HashMap CodePoint CodeUnitIndex)   , badCharLookupDefault :: !CodeUnitIndex   }@@ -400,7 +411,7 @@   -buildBadCharLookup :: TypedByteArray CodePoint -> BadCharLookup+buildBadCharLookup :: PrimArray CodePoint -> BadCharLookup buildBadCharLookup pattern_ = runST $ do    let@@ -408,7 +419,7 @@    -- Initialize table with the maximum skip distance, which is the length of the pattern.   -- This applies to all characters that are not part of the pattern.-  table <- (TBA.replicate badCharTableSize defaultSkip)+  table <- replicateMutablePrimArray badCharTableSize defaultSkip    let     -- Fill the bad character table based on the rightmost occurrence of a character in the pattern.@@ -449,15 +460,15 @@         let skipBytes' = skipBytes - minimumSkipForCodePoint patChar in         if fromEnum patChar < badCharTableSize         then do-          TBA.writeTypedByteArray table (fromEnum patChar) skipBytes'+          writePrimArray table (fromEnum patChar) skipBytes'           fillTable badCharMap skipBytes' patChars         else           let badCharMap' = HashMap.insert patChar skipBytes' badCharMap           in fillTable badCharMap' skipBytes' patChars -  badCharMap <- fillTable HashMap.empty defaultSkip (TBA.toList pattern_)+  badCharMap <- fillTable HashMap.empty defaultSkip (primArrayToList pattern_) -  tableFrozen <- TBA.unsafeFreezeTypedByteArray table+  tableFrozen <- unsafeFreezePrimArray table    pure BadCharLookup     { badCharLookupTable = tableFrozen@@ -469,6 +480,6 @@ -- Helper functions for easily toggling the safety of this module  -- | Read from a lookup table at the specified index.-indexTable :: Prim a => TypedByteArray a -> Int -> a+indexTable :: Prim a => PrimArray a -> Int -> a {-# INLINE indexTable #-}-indexTable = TBA.unsafeIndex+indexTable = indexPrimArray
src/Data/Text/BoyerMooreCI/Replacer.hs view
@@ -4,6 +4,8 @@ -- Licensed under the 3-clause BSD license, see the LICENSE file in the -- repository root. +{-# LANGUAGE GHC2021 #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleInstances #-} @@ -12,6 +14,7 @@       replaceSingleLimited     ) where +import GHC.Generics (Generic) import Data.Text.Utf8 (Text) import Data.Text.BoyerMooreCI.Automaton (Automaton, CodeUnitIndex) @@ -87,3 +90,4 @@   , rsLength :: !CodeUnitIndex     -- ^ Length of the newly build string so far, measured in CodeUnits   }+  deriving Generic
src/Data/Text/Utf8.hs view
@@ -361,6 +361,7 @@   , backwardsIterEndOfChar :: {-# UNPACK #-} !CodeUnitIndex     -- ^ Points to the last byte of the codepoint that we're focused on   }+  deriving Generic  -- | Similar to unsafeIndexCodePoint', but assumes that the given index is the -- end of a utf8 codepoint. It returns the decoded code point and the index
− src/Data/TypedByteArray.hs
@@ -1,112 +0,0 @@--- Alfred-Margaret: Fast Aho-Corasick string searching--- Copyright 2022 Channable------ Licensed under the 3-clause BSD license, see the LICENSE file in the--- repository root.-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Data.TypedByteArray-    ( Data.TypedByteArray.replicate-    , MutableTypedByteArray-    , Prim-    , TypedByteArray-    , fromList-    , toList-    , generate-    , newTypedByteArray-    , unsafeFreezeTypedByteArray-    , unsafeIndex-    , writeTypedByteArray-    , null-    , length-    , foldr-    ) where--import Prelude hiding (foldr, length, null)--import Control.DeepSeq (NFData (rnf))-import Control.Monad.Primitive (PrimMonad (PrimState))-import Control.Monad.ST (runST)-import Data.Primitive (ByteArray (ByteArray), MutableByteArray, Prim, byteArrayFromList,-                       indexByteArray, newByteArray, sizeOf, unsafeFreezeByteArray, writeByteArray)--import qualified Data.Primitive as Primitive----- | Thin wrapper around 'ByteArray' that makes signatures and indexing nicer to read.-newtype TypedByteArray a = TypedByteArray ByteArray-    deriving (Show, Eq)---- | Thin wrapper around 'MutableByteArray s' that makes signatures and indexing nicer to read.-newtype MutableTypedByteArray a s = MutableTypedByteArray (MutableByteArray s)--instance NFData (TypedByteArray a) where-    rnf (TypedByteArray (ByteArray !_)) = ()--{-# INLINE newTypedByteArray #-}-newTypedByteArray :: forall a m. (Prim a, PrimMonad m) => Int -> m (MutableTypedByteArray a (PrimState m))-newTypedByteArray = fmap MutableTypedByteArray . newByteArray . (* sizeOf (undefined :: a))--{-# INLINE fromList #-}-fromList :: Prim a => [a] -> TypedByteArray a-fromList = TypedByteArray . byteArrayFromList--{-# INLINE toList #-}-toList :: Prim a => TypedByteArray a -> [a]-toList = foldr (:) []---- | Element index without bounds checking.-{-# INLINE unsafeIndex #-}-unsafeIndex :: Prim a => TypedByteArray a -> Int -> a-unsafeIndex (TypedByteArray arr) = indexByteArray arr--{-# INLINE generate #-}--- | Construct a 'TypedByteArray' of the given length by applying the function to each index in @[0..n-1]@.-generate :: Prim a => Int -> (Int -> a) -> TypedByteArray a-generate !n f = runST $ do-    -- Allocate enough space for n elements of type a-    arr <- newTypedByteArray n-    intLoop 0 n $ \i -> i `seq` writeTypedByteArray arr i $ f i--    unsafeFreezeTypedByteArray arr--replicate :: (Prim a, PrimMonad m) => Int -> a -> m (MutableTypedByteArray a (PrimState m))-replicate n value = do-    arr <- newTypedByteArray n-    intLoop 0 n $ \i -> i `seq` writeTypedByteArray arr i value-    pure arr--{-# INLINE writeTypedByteArray #-}-writeTypedByteArray :: (Prim a, PrimMonad m) => MutableTypedByteArray a (PrimState m) -> Int -> a -> m ()-writeTypedByteArray (MutableTypedByteArray array) = writeByteArray array--{-# INLINE unsafeFreezeTypedByteArray #-}-unsafeFreezeTypedByteArray :: PrimMonad m => MutableTypedByteArray a (PrimState m) -> m (TypedByteArray a)-unsafeFreezeTypedByteArray (MutableTypedByteArray array) = TypedByteArray <$> unsafeFreezeByteArray array--{-# INLINE intLoop #-}-intLoop :: Monad m => Int -> Int -> (Int -> m ()) -> m ()-intLoop !iStart !n p = go iStart-    where-        go !i-            | i >= n = pure ()-            | otherwise = do-                p i-                go (i + 1)--{-# INLINE null #-}-null :: TypedByteArray a -> Bool-null (TypedByteArray arr) =-  Primitive.sizeofByteArray arr == 0  -- under the assumption that elements are not size 0--{-# INLINE length #-}-length :: forall a. Prim a => TypedByteArray a -> Int-length (TypedByteArray arr) =-  -- This is how foldrByteArray calculates it, so must be good-  Primitive.sizeofByteArray arr `quot` sizeOf (undefined :: a)--{-# INLINE foldr #-}-foldr :: Prim a => (a -> b -> b) -> b -> TypedByteArray a -> b-foldr f a (TypedByteArray arr) = Primitive.foldrByteArray f a arr-