diff --git a/bench/Main.hs b/bench/Main.hs
--- a/bench/Main.hs
+++ b/bench/Main.hs
@@ -1,10 +1,11 @@
 module Main where
 
+import Criterion.Main
 import qualified Data.Text.Encoding as Text
-import Gauge.Main
 import qualified PtrPoker.Write as Write
 import Prelude
 
+main :: IO ()
 main =
   defaultMain
     [ bgroup
diff --git a/library/PtrPoker/ByteString.hs b/library/PtrPoker/ByteString.hs
--- a/library/PtrPoker/ByteString.hs
+++ b/library/PtrPoker/ByteString.hs
@@ -1,6 +1,3 @@
-{-# LANGUAGE CPP #-}
-{-# OPTIONS_GHC -Wno-unused-imports #-}
-
 module PtrPoker.ByteString where
 
 import Data.ByteString
@@ -9,10 +6,9 @@
 import qualified Data.ByteString.Builder.Scientific as ScientificBuilder
 import Data.ByteString.Internal
 import qualified Data.ByteString.Lazy as Lazy
-import qualified Data.Text.Encoding as TextEncoding
+import qualified PtrPoker.Compat.Text as TextCompat
 import qualified PtrPoker.Ffi as Ffi
 import PtrPoker.Prelude hiding (empty)
-import qualified PtrPoker.Text as Text
 
 builderWithStrategy :: Builder.AllocationStrategy -> Builder.Builder -> ByteString
 builderWithStrategy strategy builder =
@@ -42,15 +38,6 @@
     actualSize <- withForeignPtr fp (\p -> populate (plusPtr p (pred allocSize)))
     return $! PS fp (allocSize - actualSize) actualSize
 
-{-# INLINEABLE textUtf8 #-}
+{-# INLINE textUtf8 #-}
 textUtf8 :: Text -> ByteString
-#if MIN_VERSION_text(2,0,0)
-textUtf8 t = TextEncoding.encodeUtf8 t
-#else
-textUtf8 = Text.destruct $ \arr off len ->
-  if len == 0
-    then empty
-    else unsafeCreateUptoN (len * 3) $ \ptr -> do
-      postPtr <- inline Ffi.encodeText ptr arr (fromIntegral off) (fromIntegral len)
-      return (minusPtr postPtr ptr)
-#endif
+textUtf8 = TextCompat.encodeInUtf8
diff --git a/library/PtrPoker/Compat/ByteString.hs b/library/PtrPoker/Compat/ByteString.hs
new file mode 100644
--- /dev/null
+++ b/library/PtrPoker/Compat/ByteString.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE CPP #-}
+
+module PtrPoker.Compat.ByteString where
+
+#if MIN_VERSION_bytestring(0,11,0)
+import Data.ByteString.Internal
+import PtrPoker.Prelude
+
+{-# INLINE poke #-}
+poke :: ByteString -> Ptr Word8 -> IO (Ptr Word8)
+poke (BS fptr length) ptr =
+  {-# SCC "poke" #-}
+  withForeignPtr fptr $ \ bytesPtr ->
+    memcpy ptr bytesPtr length $>
+    plusPtr ptr length
+#else
+import Data.ByteString.Internal
+import PtrPoker.Prelude
+
+{-# INLINE poke #-}
+poke :: ByteString -> Ptr Word8 -> IO (Ptr Word8)
+poke (PS fptr offset length) ptr =
+  {-# SCC "poke" #-}
+  withForeignPtr fptr $ \ bytesPtr ->
+    memcpy ptr (plusPtr bytesPtr offset) length $>
+    plusPtr ptr length
+#endif
diff --git a/library/PtrPoker/Compat/Text.hs b/library/PtrPoker/Compat/Text.hs
new file mode 100644
--- /dev/null
+++ b/library/PtrPoker/Compat/Text.hs
@@ -0,0 +1,62 @@
+{-# LANGUAGE CPP #-}
+
+module PtrPoker.Compat.Text where
+
+#if MIN_VERSION_text(2,0,0)
+import qualified Data.Text.Array as TextArray
+import qualified Data.Text.Encoding as TextEncoding
+import qualified Data.Text.Internal as TextInternal
+import PtrPoker.Prelude
+
+{-# INLINE destruct #-}
+destruct :: (ByteArray# -> Int -> Int -> x) -> Text -> x
+destruct k (TextInternal.Text (TextArray.ByteArray arr) off len) = k arr off len
+
+{-# INLINE utf8EncodingSize #-}
+utf8EncodingSize :: Text -> Int
+utf8EncodingSize = destruct $ \_arr _off len -> len
+
+{-# INLINEABLE encodeInUtf8 #-}
+encodeInUtf8 :: Text -> ByteString
+encodeInUtf8 t = TextEncoding.encodeUtf8 t
+
+{-# INLINE pokeInUtf8 #-}
+pokeInUtf8 :: Text -> Ptr Word8 -> IO (Ptr Word8)
+pokeInUtf8 (TextInternal.Text arr off len) p =
+  stToIO (TextArray.copyToPointer arr off p len) $> plusPtr p len
+#else
+import qualified Data.ByteString.Internal as ByteStringInternal
+import qualified Data.Text.Array as TextArray
+import qualified Data.Text.Internal as TextInternal
+import qualified PtrPoker.Ffi as Ffi
+import PtrPoker.Prelude
+
+{-# INLINE destruct #-}
+destruct :: (ByteArray# -> Int -> Int -> x) -> Text -> x
+destruct k (TextInternal.Text (TextArray.Array arr) off len) =
+  k arr off len
+
+{-# INLINEABLE utf8EncodingSize #-}
+utf8EncodingSize :: Text -> Int
+utf8EncodingSize (TextInternal.Text (TextArray.Array arr) off len) =
+  Ffi.countTextAllocationSize
+    arr
+    (fromIntegral off)
+    (fromIntegral len)
+    & unsafeDupablePerformIO
+    & fromIntegral
+
+{-# INLINEABLE encodeInUtf8 #-}
+encodeInUtf8 :: Text -> ByteString
+encodeInUtf8 (TextInternal.Text (TextArray.Array arr) off len) =
+  if len == 0
+    then mempty
+    else ByteStringInternal.unsafeCreateUptoN (len * 3) $ \ptr -> do
+      postPtr <- inline Ffi.encodeText ptr arr (fromIntegral off) (fromIntegral len)
+      return (minusPtr postPtr ptr)
+
+{-# INLINE pokeInUtf8 #-}
+pokeInUtf8 :: Text -> Ptr Word8 -> IO (Ptr Word8)
+pokeInUtf8 (TextInternal.Text (TextArray.Array arr) off len) p =
+  Ffi.encodeText p arr (fromIntegral off) (fromIntegral len)
+#endif
diff --git a/library/PtrPoker/IO/ByteString.hs b/library/PtrPoker/IO/ByteString.hs
deleted file mode 100644
--- a/library/PtrPoker/IO/ByteString.hs
+++ /dev/null
@@ -1,28 +0,0 @@
-{-# LANGUAGE CPP #-}
-
-module PtrPoker.IO.ByteString where
-
-import Data.ByteString.Internal
-import PtrPoker.Prelude
-
-#if MIN_VERSION_bytestring(0,11,0)
-
-{-# INLINE pokeByteString #-}
-pokeByteString :: Ptr Word8 -> ByteString -> IO (Ptr Word8)
-pokeByteString ptr (BS fptr length) =
-  {-# SCC "pokeByteString" #-}
-  withForeignPtr fptr $ \ bytesPtr ->
-    memcpy ptr bytesPtr length $>
-    plusPtr ptr length
-
-#else
-
-{-# INLINE pokeByteString #-}
-pokeByteString :: Ptr Word8 -> ByteString -> IO (Ptr Word8)
-pokeByteString ptr (PS fptr offset length) =
-  {-# SCC "pokeByteString" #-}
-  withForeignPtr fptr $ \ bytesPtr ->
-    memcpy ptr (plusPtr bytesPtr offset) length $>
-    plusPtr ptr length
-
-#endif
diff --git a/library/PtrPoker/Poke.hs b/library/PtrPoker/Poke.hs
--- a/library/PtrPoker/Poke.hs
+++ b/library/PtrPoker/Poke.hs
@@ -1,15 +1,10 @@
-{-# LANGUAGE CPP #-}
-{-# OPTIONS_GHC -Wno-unused-imports #-}
-
 module PtrPoker.Poke where
 
-import qualified Data.Text.Array as TextArray
-import qualified Data.Text.Internal as TextInternal
+import qualified PtrPoker.Compat.ByteString as ByteStringCompat
+import qualified PtrPoker.Compat.Text as TextCompat
 import qualified PtrPoker.Ffi as Ffi
-import qualified PtrPoker.IO.ByteString as ByteStringIO
 import qualified PtrPoker.IO.Prim as PrimIO
 import PtrPoker.Prelude hiding (concat)
-import qualified PtrPoker.Text as Text
 
 {-# RULES
 "foldMap" forall f foldable.
@@ -26,7 +21,7 @@
 instance Semigroup Poke where
   {-# INLINE [1] (<>) #-}
   Poke lIO <> Poke rIO =
-    Poke (lIO >=> rIO)
+    Poke (\p -> lIO p >>= rIO)
   sconcat =
     concat
 
@@ -54,7 +49,7 @@
 {-# INLINE byteString #-}
 byteString :: ByteString -> Poke
 byteString bs =
-  Poke $ \ptr -> inline ByteStringIO.pokeByteString ptr bs
+  Poke $ inline ByteStringCompat.poke bs
 
 -- |
 -- Encode Word8 as byte, incrementing the pointer by 1.
@@ -145,15 +140,7 @@
 -- Encode Text in UTF8.
 {-# INLINE textUtf8 #-}
 textUtf8 :: Text -> Poke
-#if MIN_VERSION_text(2,0,0)
-textUtf8 (TextInternal.Text arr off len) =
-  Poke (\p -> do
-    stToIO $ TextArray.copyToPointer arr off p len
-    pure (plusPtr p len))
-#else
-textUtf8 = Text.destruct $ \arr off len ->
-  Poke (\p -> Ffi.encodeText p arr (fromIntegral off) (fromIntegral len))
-#endif
+textUtf8 = Poke . TextCompat.pokeInUtf8
 
 -- * ASCII integers
 
diff --git a/library/PtrPoker/Size.hs b/library/PtrPoker/Size.hs
--- a/library/PtrPoker/Size.hs
+++ b/library/PtrPoker/Size.hs
@@ -1,12 +1,9 @@
-{-# LANGUAGE CPP #-}
-
 -- |
 -- Functions that compute the required allocation size by value.
 module PtrPoker.Size where
 
-import qualified PtrPoker.Ffi as Ffi
+import qualified PtrPoker.Compat.Text as TextCompat
 import PtrPoker.Prelude
-import qualified PtrPoker.Text as Text
 
 -- |
 -- Efficiently count the amount of bytes required to encode Word64
@@ -203,14 +200,4 @@
 -- in UTF8.
 {-# INLINE textUtf8 #-}
 textUtf8 :: Text -> Int
-#if MIN_VERSION_text(2,0,0)
-textUtf8 = Text.destruct $ \_arr _off len -> len
-#else
-textUtf8 = Text.destruct $ \arr off len ->
-  Ffi.countTextAllocationSize
-    arr
-    (fromIntegral off)
-    (fromIntegral len)
-    & unsafeDupablePerformIO
-    & fromIntegral
-#endif
+textUtf8 = TextCompat.utf8EncodingSize
diff --git a/library/PtrPoker/Text.hs b/library/PtrPoker/Text.hs
deleted file mode 100644
--- a/library/PtrPoker/Text.hs
+++ /dev/null
@@ -1,15 +0,0 @@
-{-# LANGUAGE CPP #-}
-
-module PtrPoker.Text where
-
-import qualified Data.Text.Array as TextArray
-import qualified Data.Text.Internal as Text
-import PtrPoker.Prelude
-
-{-# INLINE destruct #-}
-destruct :: (ByteArray# -> Int -> Int -> x) -> Text -> x
-#if MIN_VERSION_text(2,0,0)
-destruct k (Text.Text (TextArray.ByteArray arr) off len) = k arr off len
-#else
-destruct k (Text.Text (TextArray.aBA -> arr) off len) = k arr off len
-#endif
diff --git a/library/PtrPoker/Write.hs b/library/PtrPoker/Write.hs
--- a/library/PtrPoker/Write.hs
+++ b/library/PtrPoker/Write.hs
@@ -240,30 +240,8 @@
 -- Render Text in UTF8.
 --
 -- Does pretty much the same as 'Data.Text.Encoding.encodeUtf8',
--- both implementation and performance-wise,
 -- while allowing you to avoid redundant @memcpy@
 -- compared to @('byteString' . 'Data.Text.Encoding.encodeUtf8')@.
---
--- Following are the benchmark results comparing the performance of
--- @('writeToByteString' . 'textUtf8')@ with
--- @Data.Text.Encoding.'Data.Text.Encoding.encodeUtf8'@
--- on inputs in Latin and Greek (requiring different number of surrogate bytes).
--- The results show that they are quite similar.
---
--- === __Benchmark results__
---
--- > textUtf8/ptr-poker/latin/1               mean 51.54 ns  ( +- 3.083 ns  )
--- > textUtf8/ptr-poker/latin/10              mean 132.8 ns  ( +- 14.75 ns  )
--- > textUtf8/ptr-poker/latin/100             mean 860.6 ns  ( +- 66.61 ns  )
--- > textUtf8/ptr-poker/greek/1               mean 106.4 ns  ( +- 19.28 ns  )
--- > textUtf8/ptr-poker/greek/10              mean 498.4 ns  ( +- 8.022 ns  )
--- > textUtf8/ptr-poker/greek/100             mean 4.462 μs  ( +- 31.58 ns  )
--- > textUtf8/text/latin/1                    mean 52.77 ns  ( +- 3.311 ns  )
--- > textUtf8/text/latin/10                   mean 206.1 ns  ( +- 26.78 ns  )
--- > textUtf8/text/latin/100                  mean 1.337 μs  ( +- 43.34 ns  )
--- > textUtf8/text/greek/1                    mean 88.22 ns  ( +- 1.119 ns  )
--- > textUtf8/text/greek/10                   mean 475.2 ns  ( +- 21.15 ns  )
--- > textUtf8/text/greek/100                  mean 4.252 μs  ( +- 64.33 ns  )
 {-# INLINEABLE textUtf8 #-}
 textUtf8 :: Text -> Write
 textUtf8 =
diff --git a/ptr-poker.cabal b/ptr-poker.cabal
--- a/ptr-poker.cabal
+++ b/ptr-poker.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name:          ptr-poker
-version:       0.1.2.10
+version:       0.1.2.11
 synopsis:      Pointer poking action construction and composition toolkit
 homepage:      https://github.com/nikita-volkov/ptr-poker
 bug-reports:   https://github.com/nikita-volkov/ptr-poker/issues
@@ -67,11 +67,11 @@
 
   other-modules:
     PtrPoker.ByteString
+    PtrPoker.Compat.ByteString
+    PtrPoker.Compat.Text
     PtrPoker.Ffi
-    PtrPoker.IO.ByteString
     PtrPoker.IO.Prim
     PtrPoker.Prelude
-    PtrPoker.Text
 
   c-sources:
     cbits/dtoa.c
@@ -91,11 +91,11 @@
   hs-source-dirs: test
   main-is:        Main.hs
   build-depends:
-    , hedgehog           >=1.0.3    && <2
-    , isomorphism-class  >=0.1.0.7  && <0.2
-    , numeric-limits     >=0.1      && <0.2
+    , hedgehog           >=1.2     && <2
+    , isomorphism-class  >=0.1.0.8 && <0.2
+    , numeric-limits     >=0.1     && <0.2
     , ptr-poker
-    , rerebase           >=1.10.0.1 && <2
+    , rerebase           >=1.16.1  && <2
 
 benchmark bench
   import:         base-settings
@@ -104,6 +104,6 @@
   main-is:        Main.hs
   ghc-options:    -O2 -threaded -with-rtsopts=-N
   build-depends:
-    , gauge      >=0.2.5    && <0.3
+    , criterion  >=1.6    && <2
     , ptr-poker
-    , rerebase   >=1.10.0.1 && <2
+    , rerebase   >=1.16.1 && <2
