diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -44,7 +44,7 @@
 Exponential growth provides for amortized linear time.
 Such structure can be implemented without linear types, but that would
 greatly affect user experience by polluting everything with `ST` monad.
-Users are encouraged to use `Buffer` API, and built-in benchmarks refer to it.
+Users are encouraged to use `Buffer` API, and **built-in benchmarks refer to it.**
 
 The second interface is more traditional `newtype Builder = Builder (Buffer ⊸ Buffer)`
 with `Monoid` instance. This type provides easy migration from other builders,
@@ -52,136 +52,52 @@
 significantly faster than `Data.Text.Lazy.Builder`, as witnessed by benchmarks
 for `blaze-builder` below.
 
-## Case study
-
-Let's benchmark builders, which concatenate all `Char` from `minBound` to `maxBound`, producing a large `Text`:
-
-```haskell
-#!/usr/bin/env cabal
-{- cabal:
-build-depends: base, tasty-bench, text, text-builder, text-builder-linear
-ghc-options: -O2
--}
-
-import qualified Data.Text as T
-import qualified Data.Text.Lazy as TL
-import qualified Data.Text.Lazy.Builder as TLB
-import qualified Text.Builder as TB
-import qualified Data.Text.Builder.Linear as TBL
-import System.Environment (getArgs)
-import Test.Tasty.Bench
-
-mkBench :: Monoid a => String -> (Char -> a) -> (a -> Int) -> Benchmark
-mkBench s f g = bench s $ nf (g . foldMap f . enumFromTo minBound) maxBound
-{-# INLINE mkBench #-}
-
-main :: IO ()
-main = defaultMain
-  [ mkBench "text, lazy" TLB.singleton (fromIntegral . TL.length . TLB.toLazyText)
-  , mkBench "text, strict" TLB.singleton (T.length . TL.toStrict . TLB.toLazyText)
-  , mkBench "text-builder" TB.char (T.length . TB.run)
-  , mkBench "text-builder-linear" TBL.fromChar (T.length . TBL.runBuilder)
-  ]
-```
-
-Running this program with `cabal run Main.hs -- +RTS -T` yields following results:
-
-```
-text, lazy:
-  4.25 ms ± 107 μs,  11 MB allocated, 912 B  copied
-text, strict:
-  7.18 ms ± 235 μs,  24 MB allocated,  10 MB copied
-text-builder:
-  80.1 ms ± 3.0 ms, 218 MB allocated, 107 MB copied
-text-builder-linear:
-  5.37 ms ± 146 μs,  44 MB allocated,  78 KB copied
-```
-
-The first result seems the best both in time and memory and corresponds to the
-usual `Text` builder, where we do not materialize the entire result at all.
-It builds chunks of lazy `Text` lazily and consumes them at once by
-`TL.length`. Thus there are 11 MB of allocations in nursery, none of which
-survive generation 0 garbage collector, so nothing is copied.
-
-The second result is again the usual `Text` builder, but emulates a strict
-consumer: we materialize a strict `Text` before computing length. Allocation
-are doubled, and half of them (corresponding to the strict `Text`) survive to
-the heap. Time is also almost twice longer, but still quite good.
-
-The third result is for `text-builder` and demonstrates how bad things could
-go with strict builders, aiming to precompute the precise length of the
-buffer: allocating a thunk per char is tremendously slow and expensive.
-
-The last result corresponds to the current package. We generate a strict
-`Text` by growing and reallocating the buffer, thus allocations are quite
-high. Nevertheless, it is already faster than the usual `Text` builder with
-strict consumer and does not strain the garbage collector.
-
-Things get very different if we remove `{-# INLINE mkBench #-}`:
-
-```
-text, lazy:
-  36.9 ms ± 599 μs, 275 MB allocated,  30 KB copied
-text, strict:
-  44.7 ms ± 1.3 ms, 287 MB allocated,  25 MB copied
-text-builder:
-  77.6 ms ± 2.2 ms, 218 MB allocated, 107 MB copied
-text-builder-linear:
-  5.35 ms ± 212 μs,  44 MB allocated,  79 KB copied
-```
-
-Builders from `text` package degrade rapidly, 6-8x slower and 10-20x more
-allocations. That's because their constant factors rely crucially on
-everything getting inlined, which makes their performance fragile and
-unreliable in large-scale applications. On the bright side of things, our
-builder remains as fast as before and now is a clear champion.
-
 ## Benchmarks for `Text`
 
-Measured with GHC 9.6 on aarch64:
+Measured with GHC 9.12 on aarch64:
 
 |Group / size|`text`|`text-builder`|  |This package|  |
 |------------|-----:|-------------:|-:|-----------:|-:|
 | **Text** ||||||
-|1|47.4 ns|24.2 ns|0.51x|35.2 ns|0.74x|
-|10|509 ns|195 ns|0.38x|197 ns|0.39x|
-|100|4.94 μs|1.74 μs|0.35x|1.66 μs|0.34x|
-|1000|52.6 μs|17.0 μs|0.32x|15.0 μs|0.28x|
-|10000|646 μs|206 μs|0.32x|155 μs|0.24x|
-|100000|12.2 ms|3.34 ms|0.27x|2.60 ms|0.21x|
-|1000000|159 ms|55.3 ms|0.35x|16.1 ms|0.10x|
+|1|63.3 ns|30.8 ns|0.49x|60.5 ns|0.95x|
+|10|764 ns|267 ns|0.35x|319 ns|0.42x|
+|100|7.53 μs|2.48 μs|0.33x|2.61 μs|0.35x|
+|1000|80.5 μs|26.7 μs|0.33x|23.1 μs|0.29x|
+|10000|949 μs|319 μs|0.34x|242 μs|0.26x|
+|100000|18.5 ms|8.22 ms|0.44x|2.36 ms|0.13x|
+|1000000|216 ms|107 ms|0.49x|22.9 ms|0.11x|
 | **Char** ||||||
-|1|46.9 ns|21.1 ns|0.45x|22.3 ns|0.48x|
-|10|229 ns|152 ns|0.66x|79.9 ns|0.35x|
-|100|2.00 μs|1.23 μs|0.61x|618 ns|0.31x|
-|1000|21.9 μs|10.3 μs|0.47x|6.28 μs|0.29x|
-|10000|285 μs|153 μs|0.54x|68.5 μs|0.24x|
-|100000|7.70 ms|4.08 ms|0.53x|992 μs|0.13x|
-|1000000|110 ms|106 ms|0.96x|9.19 ms|0.08x|
+|1|49.0 ns|34.8 ns|0.71x|38.1 ns|0.78x|
+|10|365 ns|293 ns|0.80x|117 ns|0.32x|
+|100|3.20 μs|2.38 μs|0.74x|804 ns|0.25x|
+|1000|35.4 μs|18.4 μs|0.52x|7.68 μs|0.22x|
+|10000|460 μs|265 μs|0.58x|86.5 μs|0.19x|
+|100000|12.7 ms|6.96 ms|0.55x|930 μs|0.07x|
+|1000000|175 ms|178 ms|1.02x|10.5 ms|0.06x|
 | **Decimal** ||||||
-|1|97.7 ns|872 ns|8.92x|80.2 ns|0.82x|
-|10|864 ns|8.72 μs|10.09x|684 ns|0.79x|
-|100|9.07 μs|93.5 μs|10.32x|7.25 μs|0.80x|
-|1000|92.4 μs|1.06 ms|11.44x|67.5 μs|0.73x|
-|10000|1.13 ms|13.4 ms|11.88x|667 μs|0.59x|
-|100000|18.7 ms|141 ms|7.57x|7.57 ms|0.41x|
-|1000000|229 ms|1.487 s|6.48x|67.8 ms|0.30x|
+|1|148 ns|490 ns|3.30x|126 ns|0.85x|
+|10|1.34 μs|4.80 μs|3.57x|1.07 μs|0.80x|
+|100|14.3 μs|53.6 μs|3.76x|10.8 μs|0.76x|
+|1000|148 μs|738 μs|5.00x|106 μs|0.72x|
+|10000|1.66 ms|19.8 ms|11.96x|1.05 ms|0.63x|
+|100000|28.3 ms|251 ms|8.88x|10.7 ms|0.38x|
+|1000000|334 ms|2.803 s|8.40x|108 ms|0.32x|
 | **Hexadecimal** ||||||
-|1|403 ns|749 ns|1.86x|43.9 ns|0.11x|
-|10|3.94 μs|7.66 μs|1.94x|308 ns|0.08x|
-|100|42.8 μs|89.0 μs|2.08x|2.88 μs|0.07x|
-|1000|486 μs|986 μs|2.03x|27.7 μs|0.06x|
-|10000|7.10 ms|12.6 ms|1.77x|283 μs|0.04x|
-|100000|80.1 ms|133 ms|1.65x|3.53 ms|0.04x|
-|1000000|867 ms|1.340 s|1.55x|28.9 ms|0.03x|
+|1|711 ns|81.2 ns|0.11x|74.2 ns|0.10x|
+|10|7.06 μs|795 ns|0.11x|510 ns|0.07x|
+|100|76.3 μs|8.04 μs|0.11x|4.62 μs|0.06x|
+|1000|862 μs|141 μs|0.16x|44.6 μs|0.05x|
+|10000|12.4 ms|1.73 ms|0.14x|451 μs|0.04x|
+|100000|138 ms|20.4 ms|0.15x|4.52 ms|0.03x|
+|1000000|1.502 s|228 ms|0.15x|45.9 ms|0.03x|
 | **Double** ||||||
-|1|7.56 μs|18.3 μs|2.42x|414 ns|0.05x|
-|10|76.5 μs|188 μs|2.46x|4.23 μs|0.06x|
-|100|754 μs|2.35 ms|3.11x|44.4 μs|0.06x|
-|1000|7.94 ms|25.8 ms|3.25x|436 μs|0.05x|
-|10000|79.1 ms|285 ms|3.60x|4.90 ms|0.06x|
-|100000|796 ms|2.938 s|3.69x|45.1 ms|0.06x|
-|1000000|8.003 s|32.411 s|4.05x|436 ms|0.05x|
+|1|13.4 μs|35.3 μs|2.63x|638 ns|0.05x|
+|10|137 μs|393 μs|2.88x|6.52 μs|0.05x|
+|100|1.35 ms|5.62 ms|4.15x|67.9 μs|0.05x|
+|1000|14.2 ms|71.9 ms|5.05x|671 μs|0.05x|
+|10000|143 ms|750 ms|5.25x|7.18 ms|0.05x|
+|100000|1.435 s|7.941 s|5.53x|70.4 ms|0.05x|
+|1000000|14.366 s|101.342 s|7.05x|689 ms|0.05x|
 
 If you are not convinced by synthetic data,
 here are benchmarks for
@@ -207,53 +123,55 @@
 ## Benchmarks for `ByteString`
 
 Somewhat surprisingly, `text-builder-linear` now offers rendering to strict `ByteString`
-as well. It is consistently faster than `bytestring` when a string gets over 32k
+as well. It gets consistently faster than `bytestring`
+in all benchmarks except `Double` ones
+once a string gets over 32k
 (which is `defaultChunkSize` for `bytestring` builder). For mid-sized strings
 `bytestring` is slightly faster in certain disciplines, mostly by virtue of using
 `cbits` via FFI, while this package remains 100% native Haskell.
 
-Benchmarks below were measured with GHC 9.6 on aarch64 and include comparison
+Benchmarks below were measured with GHC 9.12 on aarch64 and include comparison
 to [`bytestring-strict-builder`](https://hackage.haskell.org/package/bytestring-strict-builder):
 
 |Group / size|`bytestring`|`…-strict-builder`|  |This package|  |
 |------------|-----------:|-----------------:|-:|-----------:|-:|
 | **Text** ||||||
-|1|106 ns|33.5 ns|0.32x|35.2 ns|0.33x|
-|10|322 ns|217 ns|0.68x|197 ns|0.61x|
-|100|2.49 μs|1.89 μs|0.76x|1.66 μs|0.67x|
-|1000|21.8 μs|18.5 μs|0.85x|15.0 μs|0.69x|
-|10000|231 μs|212 μs|0.92x|155 μs|0.67x|
-|100000|3.97 ms|3.54 ms|0.89x|2.60 ms|0.66x|
-|1000000|81.2 ms|51.5 ms|0.63x|16.1 ms|0.20x|
+|1|156 ns|55.9 ns|0.36x|60.5 ns|0.39x|
+|10|552 ns|374 ns|0.68x|319 ns|0.58x|
+|100|4.71 μs|3.25 μs|0.69x|2.61 μs|0.55x|
+|1000|41.7 μs|31.9 μs|0.76x|23.1 μs|0.56x|
+|10000|438 μs|366 μs|0.84x|242 μs|0.55x|
+|100000|7.58 ms|6.52 ms|0.86x|2.36 ms|0.31x|
+|1000000|112 ms|88.1 ms|0.78x|22.9 ms|0.20x|
 | **Char** ||||||
-|1|99.0 ns|19.4 ns|0.20x|22.3 ns|0.23x|
-|10|270 ns|82.9 ns|0.31x|79.9 ns|0.30x|
-|100|1.77 μs|723 ns|0.41x|618 ns|0.35x|
-|1000|20.4 μs|8.37 μs|0.41x|6.28 μs|0.31x|
-|10000|322 μs|129 μs|0.40x|68.5 μs|0.21x|
-|100000|10.4 ms|2.50 ms|0.24x|992 μs|0.10x|
-|1000000|143 ms|67.4 ms|0.47x|9.19 ms|0.06x|
+|1|138 ns|30.9 ns|0.22x|38.1 ns|0.28x|
+|10|408 ns|136 ns|0.33x|117 ns|0.29x|
+|100|2.96 μs|1.25 μs|0.42x|804 ns|0.27x|
+|1000|30.4 μs|14.2 μs|0.47x|7.68 μs|0.25x|
+|10000|394 μs|218 μs|0.55x|86.5 μs|0.22x|
+|100000|11.8 ms|4.00 ms|0.34x|930 μs|0.08x|
+|1000000|161 ms|112 ms|0.69x|10.5 ms|0.07x|
 | **Decimal** ||||||
-|1|152 ns|174 ns|1.14x|80.2 ns|0.53x|
-|10|685 ns|1.55 μs|2.26x|684 ns|1.00x|
-|100|5.88 μs|17.2 μs|2.93x|7.25 μs|1.23x|
-|1000|60.3 μs|196 μs|3.25x|67.5 μs|1.12x|
-|10000|648 μs|4.25 ms|6.57x|667 μs|1.03x|
-|100000|11.2 ms|62.8 ms|5.62x|7.57 ms|0.68x|
-|1000000|150 ms|655 ms|4.37x|67.8 ms|0.45x|
+|1|209 ns|295 ns|1.41x|126 ns|0.60x|
+|10|1.10 μs|2.67 μs|2.43x|1.07 μs|0.98x|
+|100|9.76 μs|29.8 μs|3.05x|10.8 μs|1.11x|
+|1000|100 μs|340 μs|3.40x|106 μs|1.06x|
+|10000|1.02 ms|7.32 ms|7.15x|1.05 ms|1.02x|
+|100000|14.6 ms|103 ms|7.04x|10.7 ms|0.73x|
+|1000000|179 ms|1.233 s|6.87x|108 ms|0.60x|
 | **Hexadecimal** ||||||
-|1|94.7 ns|||43.9 ns|0.46x|
-|10|255 ns|||308 ns|1.21x|
-|100|1.72 μs|||2.88 μs|1.67x|
-|1000|18.9 μs|||27.7 μs|1.46x|
-|10000|250 μs|||283 μs|1.13x|
-|100000|6.94 ms|||3.53 ms|0.51x|
-|1000000|93.2 ms|||28.9 ms|0.31x|
+|1|131 ns|||74.2 ns|0.57x|
+|10|360 ns|||510 ns|1.42x|
+|100|2.76 μs|||4.62 μs|1.68x|
+|1000|28.6 μs|||44.6 μs|1.56x|
+|10000|330 μs|||451 μs|1.37x|
+|100000|7.30 ms|||4.52 ms|0.62x|
+|1000000|103 ms|||45.9 ms|0.45x|
 | **Double** ||||||
-|1|457 ns|||414 ns|0.91x|
-|10|3.94 μs|||4.23 μs|1.07x|
-|100|40.3 μs|||44.4 μs|1.10x|
-|1000|398 μs|||436 μs|1.10x|
-|10000|5.65 ms|||4.90 ms|0.87x|
-|100000|63.3 ms|||45.1 ms|0.71x|
-|1000000|673 ms|||436 ms|0.65x|
+|1|456 ns|||638 ns|1.40x|
+|10|3.58 μs|||6.52 μs|1.82x|
+|100|36.2 μs|||67.9 μs|1.87x|
+|1000|367 μs|||671 μs|1.83x|
+|10000|5.17 ms|||7.18 ms|1.39x|
+|100000|59.0 ms|||70.4 ms|1.19x|
+|1000000|605 ms|||689 ms|1.14x|
diff --git a/bench/BenchChar.hs b/bench/BenchChar.hs
--- a/bench/BenchChar.hs
+++ b/bench/BenchChar.hs
@@ -10,16 +10,12 @@
 import Data.Char
 import qualified Data.Text as T
 import Data.Text.Builder.Linear.Buffer
-import qualified Data.Text.Lazy as TL
 import Data.Text.Lazy (toStrict)
-import qualified Data.Text.Lazy.Builder as TB
 import Data.Text.Lazy.Builder (toLazyText, singleton)
-import qualified Data.Text.Internal.Fusion.Common as Fusion
-import qualified Data.Text.Internal.Fusion as Fusion
 import Test.Tasty.Bench
 
 #ifdef MIN_VERSION_text_builder
-import qualified Text.Builder
+import qualified TextBuilder
 #endif
 
 #ifdef MIN_VERSION_bytestring_strict_builder
@@ -44,10 +40,10 @@
 
 #ifdef MIN_VERSION_text_builder
 benchStrictBuilder ∷ Int → T.Text
-benchStrictBuilder = Text.Builder.run . go mempty
+benchStrictBuilder = TextBuilder.toText . go mempty
   where
     go !acc 0 = acc
-    go !acc n = let ch = chr n in go (Text.Builder.char ch <> (acc <> Text.Builder.char ch)) (n - 1)
+    go !acc n = let ch = chr n in go (TextBuilder.char ch <> (acc <> TextBuilder.char ch)) (n - 1)
 #endif
 
 #ifdef MIN_VERSION_bytestring_strict_builder
@@ -65,15 +61,15 @@
     go !acc 0 = acc
     go !acc n = let ch = chr n in go (ch .<| (acc |>. ch)) (n - 1)
 
-benchSingleChar ∷ Benchmark
-benchSingleChar = bgroup "Single" $ map mkGroupChar [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6]
+benchSingleChar ∷ [Benchmark]
+benchSingleChar = map mkGroupChar [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6]
 
 mkGroupChar :: Int → Benchmark
 mkGroupChar n = bgroup (show n)
   [ bench "Data.Text.Lazy.Builder" $ nf benchLazyBuilder n
   , bench "Data.ByteString.Builder" $ nf benchLazyBuilderBS n
 #ifdef MIN_VERSION_text_builder
-  , bench "Text.Builder" $ nf benchStrictBuilder n
+  , bench "TextBuilder" $ nf benchStrictBuilder n
 #endif
 #ifdef MIN_VERSION_bytestring_strict_builder
   , bench "ByteString.StrictBuilder" $ nf benchStrictBuilderBS n
@@ -82,160 +78,8 @@
   ]
 
 --------------------------------------------------------------------------------
--- Multiple chars
---------------------------------------------------------------------------------
-
-charCount :: Word
-charCount = 3
-
-benchCharsLazyBuilder ∷ Int → T.Text
-benchCharsLazyBuilder = TL.toStrict . TB.toLazyText . go mempty
-  where
-    go !acc 0 = acc
-    go !acc n = let ch = chr n in go (replicateChar ch <> (acc <> replicateChar ch)) (n - 1)
-
-    replicateChar ch = TB.fromText (Fusion.unstream (Fusion.replicateCharI charCount ch))
-
-{- [FIXME] bad performance
-benchCharsLazyBuilderBS ∷ Int → B.ByteString
-benchCharsLazyBuilderBS = B.toStrict . B.toLazyByteString . go mempty
-  where
-    go !acc 0 = acc
-    go !acc n =
-      let ch = chr n
-      in go (replicateChar ch <> (acc <> replicateChar ch)) (n - 1)
-
-    replicateChar ch = stimes charCount (B.charUtf8 ch)
--}
-
-#ifdef MIN_VERSION_text_builder
-benchCharsStrictBuilder ∷ Int → T.Text
-benchCharsStrictBuilder = Text.Builder.run . go mempty
-  where
-    go !acc 0 = acc
-    go !acc n = let ch = chr n in go (replicateChar ch <> (acc <> replicateChar ch)) (n - 1)
-
-    -- [TODO] Is there a better way?
-    replicateChar ch = Text.Builder.padFromRight (fromIntegral charCount) ch mempty
-#endif
-
-{- [TODO]
-#ifdef MIN_VERSION_bytestring_strict_builder
-benchCharsStrictBuilderBS ∷ Int → B.ByteString
-benchCharsStrictBuilderBS = ByteString.StrictBuilder.builderBytes . go mempty
-  where
-    go !acc 0 = acc
-    go !acc n = let ch = chr n in go _ (n - 1)
-#endif
--}
-
-benchCharsLinearBuilder ∷ Int → T.Text
-benchCharsLinearBuilder m = runBuffer (\b → go b m)
-  where
-    go ∷ Buffer ⊸ Int → Buffer
-    go !acc 0 = acc
-    go !acc n = let ch = chr n in go (prependChars charCount ch (appendChars charCount ch acc)) (n - 1)
-
-benchMultipleChars ∷ Benchmark
-benchMultipleChars = bgroup "Multiple" $ map mkGroupChars [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6]
-
-mkGroupChars :: Int → Benchmark
-mkGroupChars n = bgroup (show n)
-  [ bench "Data.Text.Lazy.Builder" $ nf benchCharsLazyBuilder n
-  -- , bench "Data.ByteString.Builder" $ nf benchCharsLazyBuilderBS n
-#ifdef MIN_VERSION_text_builder
-  , bench "Text.Builder" $ nf benchCharsStrictBuilder n
-#endif
--- #ifdef MIN_VERSION_bytestring_strict_builder
---   , bench "ByteString.StrictBuilder" $ nf benchCharsStrictBuilderBS n
--- #endif
-  , bench "Data.Text.Builder.Linear" $ nf benchCharsLinearBuilder n
-  ]
-
---------------------------------------------------------------------------------
--- Padding
---------------------------------------------------------------------------------
-
-benchPaddingLazyBuilder ∷ Int → T.Text
-benchPaddingLazyBuilder = toStrict . toLazyText . go mempty 0
-  where
-    go !acc !_ 0 = acc
-    go !acc l  n =
-      let ch = chr n
-          !l' = l + 2 * fromIntegral charCount
-      in go (withText (T.justifyLeft l' ch)
-                      (withText (T.justifyRight (l + fromIntegral charCount) ch) acc))
-            l'
-            (n - 1)
-
-    withText f = TB.fromText . f . TL.toStrict . TB.toLazyText
-
-{- [TODO]
-benchPaddingLazyBuilderBS ∷ Int → B.ByteString
-benchPaddingLazyBuilderBS = B.toStrict . B.toLazyByteString . go mempty
-  where
-    go !acc 0 = acc
-    go !acc n = let ch = chr n in go _ (n - 1)
--}
-
-#ifdef MIN_VERSION_text_builder
-benchPaddingStrictBuilder ∷ Int → T.Text
-benchPaddingStrictBuilder = Text.Builder.run . go mempty 0
-  where
-    go !acc !_ 0 = acc
-    go !acc l  n =
-      let ch = chr n
-          !l' = l + 2 * fromIntegral charCount
-      in go (Text.Builder.padFromRight l' ch (Text.Builder.padFromLeft (l + fromIntegral charCount) ch acc))
-            l'
-            (n - 1)
-#endif
-
-{- [TODO]
-#ifdef MIN_VERSION_bytestring_strict_builder
-benchPaddingStrictBuilderBS ∷ Int → B.ByteString
-benchPaddingStrictBuilderBS = ByteString.StrictBuilder.builderBytes . go mempty
-  where
-    go !acc 0 = acc
-    go !acc n = let ch = chr n in go _ (n - 1)
-#endif
--}
-
-benchPaddingLinearBuilder ∷ Int → T.Text
-benchPaddingLinearBuilder m = runBuffer (\b → go b 0 m)
-  where
-    go ∷ Buffer ⊸ Word → Int → Buffer
-    go !acc !_ 0 = acc
-    go !acc l  n =
-      let ch = chr n
-          !l' = l + 2 * charCount
-      in go (justifyLeft l' ch (justifyRight (l + charCount) ch acc))
-            l'
-            (n - 1)
-
-benchPadding ∷ Benchmark
-benchPadding = bgroup "Padding" $ map mkGroupPadding [1e0, 1e1, 1e2, 1e3, 1e4{-, 1e5, 1e6-}] -- NOTE: too long with 1e5
-
-mkGroupPadding :: Int → Benchmark
-mkGroupPadding n = bgroup (show n)
-  [ bench "Data.Text.Lazy.Builder" $ nf benchPaddingLazyBuilder n
-  -- , bench "Data.ByteString.Builder" $ nf benchPaddingLazyBuilderBS n
-#ifdef MIN_VERSION_text_builder
-  , bench "Text.Builder" $ nf benchPaddingStrictBuilder n
-#endif
--- #ifdef MIN_VERSION_bytestring_strict_builder
---   , bench "ByteString.StrictBuilder" $ nf benchPaddingStrictBuilderBS n
--- #endif
-  , bench "Data.Text.Builder.Linear" $ nf benchPaddingLinearBuilder n
-  ]
-
---------------------------------------------------------------------------------
 -- All benchmarks
 --------------------------------------------------------------------------------
 
 benchChar ∷ Benchmark
-benchChar = bgroup "Char"
-  [ benchSingleChar
-  , benchMultipleChars
-  , benchPadding ]
-
+benchChar = bgroup "Char" benchSingleChar
diff --git a/bench/BenchDecimal.hs b/bench/BenchDecimal.hs
--- a/bench/BenchDecimal.hs
+++ b/bench/BenchDecimal.hs
@@ -2,144 +2,76 @@
 -- Copyright:   (c) 2022 Andrew Lelechenko
 -- Licence:     BSD3
 -- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>
+
 module BenchDecimal (benchDecimal) where
 
-import Data.ByteString qualified as B
-import Data.ByteString.Builder qualified as B
-import Data.Text qualified as T
-import Data.Text.Builder.Linear.Buffer (Buffer, runBuffer, ($$<|), ($<|), (|>$), (|>$$))
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Builder as B
+import qualified Data.Text as T
+import Data.Text.Builder.Linear.Buffer
 import Data.Text.Lazy (toStrict)
 import Data.Text.Lazy.Builder (toLazyText)
 import Data.Text.Lazy.Builder.Int (decimal)
-import Test.Tasty.Bench (Benchmark, bench, bgroup, nf)
+import Test.Tasty.Bench
 
 #ifdef MIN_VERSION_text_builder
-import qualified Text.Builder
+import qualified TextBuilder
 #endif
 
 #ifdef MIN_VERSION_bytestring_strict_builder
 import qualified ByteString.StrictBuilder
 #endif
 
-benchDecimal ∷ Benchmark
-benchDecimal = bgroup "Decimal" [benchBoundedDecimal, benchUnboundedDecimal]
-
---------------------------------------------------------------------------------
--- Bounded
---------------------------------------------------------------------------------
-
-int ∷ Int
+int :: Int
 int = 123456789123456789
 
-benchLazyBuilder ∷ Integral a ⇒ a → Int → T.Text
-benchLazyBuilder k = toStrict . toLazyText . go mempty
+benchLazyBuilder ∷ Int → T.Text
+benchLazyBuilder = toStrict . toLazyText . go mempty
   where
     go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (decimal i <> (acc <> decimal i)) (n - 1)
-{-# SPECIALIZE benchLazyBuilder ∷ Int → Int → T.Text #-}
-{-# SPECIALIZE benchLazyBuilder ∷ Integer → Int → T.Text #-}
+    go !acc n = let i = n * int in go (decimal i <> (acc <> decimal i)) (n - 1)
 
-benchLazyBuilderBS ∷ Int → Int → B.ByteString
-benchLazyBuilderBS k = B.toStrict . B.toLazyByteString . go mempty
+benchLazyBuilderBS ∷ Int → B.ByteString
+benchLazyBuilderBS = B.toStrict . B.toLazyByteString . go mempty
   where
     go !acc 0 = acc
-    go !acc n = let i = n * k in go (B.intDec i <> (acc <> B.intDec i)) (n - 1)
+    go !acc n = let i = n * int in go (B.intDec i <> (acc <> B.intDec i)) (n - 1)
 
 #ifdef MIN_VERSION_text_builder
-benchStrictBuilder ∷ (Integral a) ⇒ a → Int → T.Text
-benchStrictBuilder k = Text.Builder.run . go mempty
+benchStrictBuilder ∷ Int → T.Text
+benchStrictBuilder = TextBuilder.toText . go mempty
   where
     go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (Text.Builder.decimal i <> (acc <> Text.Builder.decimal i)) (n - 1)
-{-# SPECIALIZE benchStrictBuilder ∷ Int → Int → T.Text #-}
-{-# SPECIALIZE benchStrictBuilder ∷ Integer → Int → T.Text #-}
+    go !acc n = let i = n * int in go (TextBuilder.decimal i <> (acc <> TextBuilder.decimal i)) (n - 1)
 #endif
 
 #ifdef MIN_VERSION_bytestring_strict_builder
-benchStrictBuilderBS ∷ (Integral a) ⇒ a  → Int → B.ByteString
-benchStrictBuilderBS k = ByteString.StrictBuilder.builderBytes . go mempty
+benchStrictBuilderBS ∷ Int → B.ByteString
+benchStrictBuilderBS = ByteString.StrictBuilder.builderBytes . go mempty
   where
     go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (ByteString.StrictBuilder.asciiIntegral i <> (acc <> ByteString.StrictBuilder.asciiIntegral i)) (n - 1)
-{-# SPECIALIZE benchStrictBuilderBS ∷ Int → Int → B.ByteString #-}
-{-# SPECIALIZE benchStrictBuilderBS ∷ Integer → Int → B.ByteString #-}
+    go !acc n = let i = n * int in go (ByteString.StrictBuilder.asciiIntegral i <> (acc <> ByteString.StrictBuilder.asciiIntegral i)) (n - 1)
 #endif
 
-benchBoundedLinearBuilder ∷ Int → Int → T.Text
-benchBoundedLinearBuilder k m = runBuffer (\b → go b m)
+benchLinearBuilder ∷ Int → T.Text
+benchLinearBuilder m = runBuffer (\b → go b m)
   where
     go ∷ Buffer ⊸ Int → Buffer
     go !acc 0 = acc
-    go !acc n = let i = n * k in go (i $<| (acc |>$ i)) (n - 1)
-
-benchBoundedDecimal ∷ Benchmark
-benchBoundedDecimal = bgroup "Bounded" $ map mkBoundedGroup [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6]
-
-mkBoundedGroup ∷ Int → Benchmark
-mkBoundedGroup n =
-  bgroup
-    (show n)
-    [ bench "Data.Text.Lazy.Builder" $ nf (benchLazyBuilder int) n
-    , bench "Data.ByteString.Builder" $ nf (benchLazyBuilderBS int) n
-#ifdef MIN_VERSION_text_builder
-    , bench "Text.Builder" $ nf (benchStrictBuilder int) n
-#endif
-#ifdef MIN_VERSION_bytestring_strict_builder
-    , bench "ByteString.StrictBuilder" $ nf (benchStrictBuilderBS int) n
-#endif
-    , bench "Data.Text.Builder.Linear" $ nf (benchBoundedLinearBuilder int) n
-    ]
-
---------------------------------------------------------------------------------
--- Unbounded
---------------------------------------------------------------------------------
-
-integerSmall ∷ Integer
-integerSmall = toInteger (div @Word maxBound 20)
-
-integerBig ∷ Integer
-integerBig = toInteger (maxBound @Word - 1) ^ (10 ∷ Word)
-
-integerHuge ∷ Integer
-integerHuge = toInteger (maxBound @Word - 1) ^ (100 ∷ Word)
-
-benchUnboundedDecimal ∷ Benchmark
-benchUnboundedDecimal =
-  bgroup
-    "Unbounded"
-    [ bgroup "Small" $ map (mkUnboundedGroup integerSmall) [1e0, 1e1, 1e2, 1e3, 1e4, 1e5]
-    , bgroup "Big" $ map (mkUnboundedGroup integerBig) [1e0, 1e1, 1e2, 1e3, 1e4]
-    , bgroup "Huge" $ map (mkUnboundedGroup integerHuge) [1e0, 1e1, 1e2, 1e3]
-    ]
-
--- NOTE: In the following benchmarks, the ByteString builder would share work
--- if the prepender and the appender are identical, while our linear buffer does
--- not. So we increment the appender to get a fair benchmark.
-
-benchUnboundedLazyBuilderBS ∷ Integer → Int → B.ByteString
-benchUnboundedLazyBuilderBS k = B.toStrict . B.toLazyByteString . go mempty
-  where
-    go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (B.integerDec i <> (acc <> B.integerDec (i + 1))) (n - 1)
+    go !acc n = let i = n * int in go (i $<| (acc |>$ i)) (n - 1)
 
-benchUnboundedLinearBuilder ∷ Integer → Int → T.Text
-benchUnboundedLinearBuilder k m = runBuffer (\b → go b m)
-  where
-    go ∷ Buffer ⊸ Int → Buffer
-    go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (i $$<| (acc |>$$ (i + 1))) (n - 1)
+benchDecimal ∷ Benchmark
+benchDecimal = bgroup "Decimal" $ map mkGroup [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6]
 
-mkUnboundedGroup ∷ Integer → Int → Benchmark
-mkUnboundedGroup integer n =
-  bgroup
-    (show n)
-    [ bench "Data.Text.Lazy.Builder" $ nf (benchLazyBuilder integer) n
-    , bench "Data.ByteString.Builder" $ nf (benchUnboundedLazyBuilderBS integer) n
+mkGroup :: Int → Benchmark
+mkGroup n = bgroup (show n)
+  [ bench "Data.Text.Lazy.Builder" $ nf benchLazyBuilder n
+  , bench "Data.ByteString.Builder" $ nf benchLazyBuilderBS n
 #ifdef MIN_VERSION_text_builder
-    , bench "Text.Builder" $ nf (benchStrictBuilder integer) n
+  , bench "TextBuilder" $ nf benchStrictBuilder n
 #endif
 #ifdef MIN_VERSION_bytestring_strict_builder
-    , bench "ByteString.StrictBuilder" $ nf (benchStrictBuilderBS integer) n
+  , bench "ByteString.StrictBuilder" $ nf benchStrictBuilderBS n
 #endif
-    , bench "Data.Text.Builder.Linear" $ nf (benchUnboundedLinearBuilder integer) n
-    ]
+  , bench "Data.Text.Builder.Linear" $ nf benchLinearBuilder n
+  ]
diff --git a/bench/BenchDecimalUnbounded.hs b/bench/BenchDecimalUnbounded.hs
deleted file mode 100644
--- a/bench/BenchDecimalUnbounded.hs
+++ /dev/null
@@ -1,174 +0,0 @@
-{-# LANGUAGE NumDecimals #-}
-
--- |
--- Copyright:   (c) 2022 Andrew Lelechenko
--- Licence:     BSD3
--- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>
-module BenchDecimalUnbounded (benchDecimalUnbounded) where
-
-import Data.ByteString qualified as B
-import Data.ByteString.Builder qualified as B
-import Data.Text qualified as T
-import Data.Text.Builder.Linear.Buffer (Buffer, runBuffer, ($$<|), (|>$$))
-import Data.Text.Lazy qualified as TL
-import Data.Text.Lazy.Builder qualified as TB
-import Data.Text.Lazy.Builder.Int qualified as TB
-import Test.Tasty.Bench (Benchmark, bench, bgroup, nf)
-
-benchUnboundedLinearBuilderAppend ∷ Integer → Int → T.Text
-benchUnboundedLinearBuilderAppend k m = runBuffer (`go` m)
-  where
-    go ∷ Buffer ⊸ Int → Buffer
-    go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (acc |>$$ i) (n - 1)
-
-benchUnboundedLinearBuilderPrepend ∷ Integer → Int → T.Text
-benchUnboundedLinearBuilderPrepend k m = runBuffer (`go` m)
-  where
-    go ∷ Buffer ⊸ Int → Buffer
-    go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (i $$<| acc) (n - 1)
-
--- NOTE: In the following benchmark, the ByteString builder would share work
--- if the prepender and the appender are identical, while our linear buffer does
--- not. So we increment the appender to get a fair benchmark.
-
-benchUnboundedLinearBuilder ∷ Integer → Int → T.Text
-benchUnboundedLinearBuilder k m = runBuffer (`go` m)
-  where
-    go ∷ Buffer ⊸ Int → Buffer
-    go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (i $$<| (acc |>$$ (i + 1))) (n - 1)
-
-benchUnboundedLazyBuilderBSAppend ∷ Integer → Int → B.ByteString
-benchUnboundedLazyBuilderBSAppend k = B.toStrict . B.toLazyByteString . go mempty
-  where
-    go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (acc <> B.integerDec i) (n - 1)
-
-benchUnboundedLazyBuilderBSPrepend ∷ Integer → Int → B.ByteString
-benchUnboundedLazyBuilderBSPrepend k = B.toStrict . B.toLazyByteString . go mempty
-  where
-    go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (B.integerDec i <> acc) (n - 1)
-
-benchUnboundedLazyBuilderBS ∷ Integer → Int → B.ByteString
-benchUnboundedLazyBuilderBS k = B.toStrict . B.toLazyByteString . go mempty
-  where
-    go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (B.integerDec i <> (acc <> B.integerDec (i + 1))) (n - 1)
-
-benchLazyBuilderAppend ∷ Integer → Int → T.Text
-benchLazyBuilderAppend k = TL.toStrict . TB.toLazyText . go mempty
-  where
-    go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (acc <> TB.decimal i) (n - 1)
-
-benchLazyBuilderPrepend ∷ Integer → Int → T.Text
-benchLazyBuilderPrepend k = TL.toStrict . TB.toLazyText . go mempty
-  where
-    go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (TB.decimal i <> acc) (n - 1)
-
-benchLazyBuilder ∷ Integer → Int → T.Text
-benchLazyBuilder k = TL.toStrict . TB.toLazyText . go mempty
-  where
-    go !acc 0 = acc
-    go !acc n = let i = fromIntegral n * k in go (TB.decimal i <> (acc <> TB.decimal (i + 1))) (n - 1)
-
-data NamedInteger = I !String !Integer
-
-mkGroup
-  ∷ String
-  → [Int]
-  → (Integer → Int → T.Text)
-  → (Integer → Int → B.ByteString)
-  → (Integer → Int → T.Text)
-  → [NamedInteger]
-  → Benchmark
-mkGroup name counts f g h = bgroup name . map mkBenches
-  where
-    mkBenches (I benchName i) =
-      bgroup
-        benchName
-        (map (\count → bgroup (show count) (mkBench i count)) counts)
-    mkBench i count =
-      [ bench "Data.Text.Lazy.Builder" $ nf (f i) count
-      , bench "Data.ByteString.Builder" $ nf (g i) count
-      , bench "Data.Text.Builder.Linear" $ nf (h i) count
-      ]
-{-# INLINE mkGroup #-}
-
-integers ∷ [NamedInteger]
-integers =
-  [ I "Small" (toInteger (div @Word maxBound 20)) -- ~ 9e17
-  , I "Big01" (toInteger (maxBound @Word - 1) ^ (2 ∷ Word)) -- ~3e38
-  , I "Big02" (toInteger (maxBound @Word - 1) ^ (5 ∷ Word)) -- ~2e96
-  , I "Big03" (toInteger (maxBound @Word - 1) ^ (10 ∷ Word)) -- ~5e192
-  , I "Big04" (toInteger (maxBound @Word - 1) ^ (15 ∷ Word)) -- ~1e289
-  , I "Big05" (toInteger (maxBound @Word - 1) ^ (20 ∷ Word)) -- ~2e385
-  -- , I "Big05a" (toInteger (maxBound @Word - 1) ^ (21 ∷ Word)) -- ~4e404
-  -- , I "Big05b" (toInteger (maxBound @Word - 1) ^ (22 ∷ Word)) -- ~7e423
-  -- , I "Big05c" (toInteger (maxBound @Word - 1) ^ (23 ∷ Word)) -- ~1e443
-  -- , I "Big05d" (toInteger (maxBound @Word - 1) ^ (24 ∷ Word)) -- ~2e462
-  , I "Big06" (toInteger (maxBound @Word - 1) ^ (25 ∷ Word)) -- ~4e481
-  -- , I "Big06a" (toInteger (maxBound @Word - 1) ^ (26 ∷ Word)) -- ~8e500
-  -- , I "Big06b" (toInteger (maxBound @Word - 1) ^ (27 ∷ Word)) -- ~2e520
-  -- , I "Big06c" (toInteger (maxBound @Word - 1) ^ (28 ∷ Word))
-  -- , I "Big06d" (toInteger (maxBound @Word - 1) ^ (29 ∷ Word))
-  , I "Big07" (toInteger (maxBound @Word - 1) ^ (30 ∷ Word)) -- ~ 9e577
-  , I "Big08" (toInteger (maxBound @Word - 1) ^ (35 ∷ Word)) -- ~ 2e674
-  , I "Big09" (toInteger (maxBound @Word - 1) ^ (40 ∷ Word)) -- ~ 4e770
-  , I "Big10" (toInteger (maxBound @Word - 1) ^ (45 ∷ Word)) -- ~ 9e866
-  , I "Big11" (toInteger (maxBound @Word - 1) ^ (50 ∷ Word)) -- ~ 2e963
-  , I "Huge01" (toInteger (maxBound @Word - 1) ^ (75 ∷ Word)) -- ~9e1444
-  , I "Huge02" (toInteger (maxBound @Word - 1) ^ (100 ∷ Word)) -- ~4e1926
-  , I "Huge03" (toInteger (maxBound @Word - 1) ^ (200 ∷ Word)) -- ~2e3853
-  , I "Huge04" (toInteger (maxBound @Word - 1) ^ (300 ∷ Word)) -- ~6e5779
-  , I "Huge05" (toInteger (maxBound @Word - 1) ^ (400 ∷ Word)) -- ~2e7706
-  -- , I "Huge05a" (toInteger (maxBound @Word - 1) ^ (450 ∷ Word))
-  , I "Huge06" (toInteger (maxBound @Word - 1) ^ (500 ∷ Word)) -- ~9e9632
-  -- , I "Huge06b" (toInteger (maxBound @Word - 1) ^ (600 ∷ Word))
-  , I "Huge07" (toInteger (maxBound @Word - 1) ^ (700 ∷ Word)) -- ~1e13486
-  , I "Huge08" (toInteger (maxBound @Word - 1) ^ (1000 ∷ Word)) -- ~8e19265
-  , I "Huge09" (toInteger (maxBound @Word - 1) ^ (3000 ∷ Word)) -- ~6e57797
-  , I "Huge10" (toInteger (maxBound @Word - 1) ^ (5000 ∷ Word)) -- ~4e96329
-  , I "Huge11" (toInteger (maxBound @Word - 1) ^ (10000 ∷ Word)) -- ~2e192659
-  , I "Huge12" (toInteger (maxBound @Word - 1) ^ (100000 ∷ Word)) -- ~9e1926591
-  -- , I "Huge13" (toInteger (maxBound @Word - 1) ^ (1000000 ∷ Word))
-  , I "1e20" 1e20
-  , I "1e100" 1e100
-  , I "1e300" (10 ^ (300 ∷ Word))
-  , I "1e500" (10 ^ (500 ∷ Word))
-  , I "1e1000" (10 ^ (1000 ∷ Word))
-  ]
-
-benchDecimalUnbounded ∷ Benchmark
-benchDecimalUnbounded =
-  bgroup
-    "Decimal: detailed unbounded"
-    [ mkGroup
-        "Append"
-        counts
-        benchLazyBuilderAppend
-        benchUnboundedLazyBuilderBSAppend
-        benchUnboundedLinearBuilderAppend
-        integers
-    , mkGroup
-        "Prepend"
-        counts
-        benchLazyBuilderPrepend
-        benchUnboundedLazyBuilderBSPrepend
-        benchUnboundedLinearBuilderPrepend
-        integers
-    , mkGroup
-        "Both"
-        counts
-        benchLazyBuilder
-        benchUnboundedLazyBuilderBS
-        benchUnboundedLinearBuilder
-        integers
-    ]
-  where
-    counts ∷ [Int]
-    counts = [1e0, 1e1, 1e2]
diff --git a/bench/BenchDouble.hs b/bench/BenchDouble.hs
--- a/bench/BenchDouble.hs
+++ b/bench/BenchDouble.hs
@@ -15,7 +15,8 @@
 import Test.Tasty.Bench
 
 #ifdef MIN_VERSION_text_builder
-import qualified Text.Builder
+import qualified TextBuilder
+import qualified TextBuilderDev as TextBuilder
 #endif
 
 dbl :: Double
@@ -35,10 +36,10 @@
 
 #ifdef MIN_VERSION_text_builder
 benchStrictBuilder ∷ Int → T.Text
-benchStrictBuilder = Text.Builder.run . go mempty
+benchStrictBuilder = TextBuilder.toText . go mempty
   where
     go !acc 0 = acc
-    go !acc n = let d = fromIntegral n * dbl in go (Text.Builder.fixedDouble 17 d <> (acc <> Text.Builder.fixedDouble 17 d)) (n - 1)
+    go !acc n = let d = fromIntegral n * dbl in go (TextBuilder.doubleFixedPoint 17 d <> (acc <> TextBuilder.doubleFixedPoint 17 d)) (n - 1)
 #endif
 
 benchLinearBuilder ∷ Int → T.Text
@@ -56,7 +57,7 @@
   [ bench "Data.Text.Lazy.Builder" $ nf benchLazyBuilder n
   , bench "Data.ByteString.Builder" $ nf benchLazyBuilderBS n
 #ifdef MIN_VERSION_text_builder
-  , bench "Text.Builder" $ nf benchStrictBuilder n
+  , bench "TextBuilder" $ nf benchStrictBuilder n
 #endif
   , bench "Data.Text.Builder.Linear" $ nf benchLinearBuilder n
   ]
diff --git a/bench/BenchHexadecimal.hs b/bench/BenchHexadecimal.hs
--- a/bench/BenchHexadecimal.hs
+++ b/bench/BenchHexadecimal.hs
@@ -15,56 +15,48 @@
 import Test.Tasty.Bench
 
 #ifdef MIN_VERSION_text_builder
-import qualified Text.Builder
+import qualified TextBuilder
 #endif
 
 word :: Word
 word = 123456789123456789
 
-benchLazyBuilder ∷ Word → T.Text
+benchLazyBuilder ∷ Int → T.Text
 benchLazyBuilder = toStrict . toLazyText . go mempty
   where
     go !acc 0 = acc
-    go !acc n = let i = n * word in go (hexadecimal i <> (acc <> hexadecimal i)) (n - 1)
+    go !acc n = let i = fromIntegral n * word in go (hexadecimal i <> (acc <> hexadecimal i)) (n - 1)
 
-benchLazyBuilderBS ∷ Word → B.ByteString
+benchLazyBuilderBS ∷ Int → B.ByteString
 benchLazyBuilderBS = B.toStrict . B.toLazyByteString . go mempty
   where
     go !acc 0 = acc
-    go !acc n = go (B.wordHex n <> (acc <> B.wordHex n)) (n - 1)
+    go !acc n = go (B.wordHex (fromIntegral n) <> (acc <> B.wordHex (fromIntegral n))) (n - 1)
 
 #ifdef MIN_VERSION_text_builder
-benchStrictBuilder ∷ Word → T.Text
-benchStrictBuilder = Text.Builder.run . go mempty
+benchStrictBuilder ∷ Int → T.Text
+benchStrictBuilder = TextBuilder.toText . go mempty
   where
     go !acc 0 = acc
-    go !acc n = let i = n * word in go (Text.Builder.hexadecimal i <> (acc <> Text.Builder.hexadecimal i)) (n - 1)
+    go !acc n = let i = fromIntegral n * word in go (TextBuilder.hexadecimal i <> (acc <> TextBuilder.hexadecimal i)) (n - 1)
 #endif
 
-benchLinearBuilderWord ∷ Word → T.Text
-benchLinearBuilderWord m = runBuffer (\b → go b m)
-  where
-    go ∷ Buffer ⊸ Word → Buffer
-    go !acc 0 = acc
-    go !acc n = let i = n * word in go (i &<| (acc |>& i)) (n - 1)
-
-benchLinearBuilderInt ∷ Word → T.Text
-benchLinearBuilderInt m = runBuffer (\b → go b (fromIntegral m))
+benchLinearBuilder ∷ Int → T.Text
+benchLinearBuilder m = runBuffer (\b → go b m)
   where
     go ∷ Buffer ⊸ Int → Buffer
     go !acc 0 = acc
-    go !acc n = let i = n * fromIntegral word in go (i &<| (acc |>& i)) (n - 1)
+    go !acc n = let i = fromIntegral n * word in go (i &<| (acc |>& i)) (n - 1)
 
 benchHexadecimal ∷ Benchmark
 benchHexadecimal = bgroup "Hexadecimal" $ map mkGroup [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6]
 
-mkGroup :: Word → Benchmark
+mkGroup :: Int → Benchmark
 mkGroup n = bgroup (show n)
   [ bench "Data.Text.Lazy.Builder" $ nf benchLazyBuilder n
   , bench "Data.ByteString.Builder" $ nf benchLazyBuilderBS n
 #ifdef MIN_VERSION_text_builder
-  , bench "Text.Builder" $ nf benchStrictBuilder n
+  , bench "TextBuilder" $ nf benchStrictBuilder n
 #endif
-  , bench "Data.Text.Builder.Linear (Word)" $ nf benchLinearBuilderWord n
-  , bench "Data.Text.Builder.Linear (Int)" $ nf benchLinearBuilderInt n
+  , bench "Data.Text.Builder.Linear" $ nf benchLinearBuilder n
   ]
diff --git a/bench/BenchText.hs b/bench/BenchText.hs
--- a/bench/BenchText.hs
+++ b/bench/BenchText.hs
@@ -15,7 +15,7 @@
 import Test.Tasty.Bench
 
 #ifdef MIN_VERSION_text_builder
-import qualified Text.Builder
+import qualified TextBuilder
 #endif
 
 #ifdef MIN_VERSION_bytestring_strict_builder
@@ -41,9 +41,9 @@
 
 #ifdef MIN_VERSION_text_builder
 benchStrictBuilder ∷ Int → T.Text
-benchStrictBuilder = Text.Builder.run . go mempty
+benchStrictBuilder = TextBuilder.toText . go mempty
   where
-    txtB = Text.Builder.text txt
+    txtB = TextBuilder.text txt
     go !acc 0 = acc
     go !acc n = go (txtB <> (acc <> txtB)) (n - 1)
 #endif
@@ -72,7 +72,7 @@
   [ bench "Data.Text.Lazy.Builder" $ nf benchLazyBuilder n
   , bench "Data.ByteString.Builder" $ nf benchLazyBuilderBS n
 #ifdef MIN_VERSION_text_builder
-  , bench "Text.Builder" $ nf benchStrictBuilder n
+  , bench "TextBuilder" $ nf benchStrictBuilder n
 #endif
 #ifdef MIN_VERSION_bytestring_strict_builder
   , bench "ByteString.StrictBuilder" $ nf benchStrictBuilderBS n
diff --git a/bench/Main.hs b/bench/Main.hs
--- a/bench/Main.hs
+++ b/bench/Main.hs
@@ -2,6 +2,7 @@
 -- Copyright:   (c) 2022 Andrew Lelechenko
 -- Licence:     BSD3
 -- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>
+
 module Main where
 
 import Test.Tasty.Bench
@@ -9,28 +10,23 @@
 
 import BenchChar
 import BenchDecimal
-import BenchDecimalUnbounded (benchDecimalUnbounded)
 import BenchDouble
 import BenchHexadecimal
 import BenchText
 
 main ∷ IO ()
-main =
-  defaultMain $
-    map (mapLeafBenchmarks addCompare) $
-      [ benchText
-      , benchChar
-      , benchDecimal
-      , benchDecimalUnbounded
-      , benchHexadecimal
-      , benchDouble
-      ]
+main = defaultMain $ map (mapLeafBenchmarks addCompare) $
+  [ benchText
+  , benchChar
+  , benchDecimal
+  , benchHexadecimal
+  , benchDouble
+  ]
 
-textBenchName ∷ String
--- textBenchName = "Data.Text.Lazy.Builder"
-textBenchName = "Data.ByteString.Builder"
+textBenchName :: String
+textBenchName = "Data.Text.Lazy.Builder"
 
-addCompare ∷ ([String] → Benchmark → Benchmark)
+addCompare :: ([String] -> Benchmark -> Benchmark)
 addCompare (name : path)
   | name /= textBenchName = bcompare (printAwkExpr (locateBenchmark (textBenchName : path)))
 addCompare _ = id
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,8 @@
+## 0.1.4
+
+* Add `instance Eq Builder` and `instance Ord Builder`.
+* Fix a bug in `dropBuffer` and `takeBuffer`.
+
 ## 0.1.3
 
 * Add decimal builders for unbounded inputs: `fromUnboundedDec`, `(|>$$)` and `($$<|)`.
diff --git a/src/Data/Text/Builder/Linear.hs b/src/Data/Text/Builder/Linear.hs
--- a/src/Data/Text/Builder/Linear.hs
+++ b/src/Data/Text/Builder/Linear.hs
@@ -1,9 +1,11 @@
+{-# LANGUAGE CPP #-}
+
 -- |
 -- Copyright:   (c) 2022 Andrew Lelechenko
 -- Licence:     BSD3
 -- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>
 --
--- Builder for strict 'Text' and 'ByteString', based on linear types. It consistently
+-- Builder for strict t'Text' and 'ByteString', based on linear types. It consistently
 -- outperforms "Data.Text.Lazy.Builder"
 -- from @text@ as well as a strict builder from @text-builder@,
 -- and scales better.
@@ -24,10 +26,15 @@
 import Data.ByteString.Internal (ByteString (..))
 import Data.Text.Internal (Text (..))
 import GHC.Exts (Addr#, IsString (..))
+#if MIN_VERSION_base(4,17,0)
+import GHC.Exts (Multiplicity)
+#else
+import GHC.Base (Multiplicity)
+#endif
 
 import Data.Text.Builder.Linear.Buffer
 
--- | Thin wrapper over 'Buffer' with a handy 'Semigroup' instance.
+-- | Thin wrapper over t'Buffer' with a handy 'Semigroup' instance.
 --
 -- >>> :set -XOverloadedStrings -XMagicHash
 -- >>> fromText "foo" <> fromChar '_' <> fromAddr "bar"#
@@ -36,12 +43,24 @@
 -- Remember: this is a strict builder, so on contrary to "Data.Text.Lazy.Builder"
 -- for optimal performance you should use strict left folds instead of lazy right ones.
 --
--- Note that (similar to other builders) concatenation of 'Builder's allocates
+-- Note that (similar to other builders) concatenation of t'Builder's allocates
 -- thunks. This is to a certain extent mitigated by aggressive inlining,
--- but it is faster to use 'Buffer' directly.
+-- but it is faster to use t'Buffer' directly.
 newtype Builder = Builder {unBuilder ∷ Buffer ⊸ Buffer}
 
--- | Run 'Builder' computation on an empty 'Buffer', returning strict 'Text'.
+-- | @since 0.1.4
+instance Eq Builder where
+  b1 == b2 = runBuilder b1 == runBuilder b2
+
+-- | @since 0.1.4
+instance Ord Builder where
+  compare b1 b2 = compare (runBuilder b1) (runBuilder b2)
+  b1 <= b2 = runBuilder b1 <= runBuilder b2
+  b1 < b2 = runBuilder b1 < runBuilder b2
+  b1 >= b2 = runBuilder b1 >= runBuilder b2
+  b1 > b2 = runBuilder b1 > runBuilder b2
+
+-- | Run t'Builder' computation on an empty t'Buffer', returning strict t'Text'.
 --
 -- >>> :set -XOverloadedStrings -XMagicHash
 -- >>> runBuilder (fromText "foo" <> fromChar '_' <> fromAddr "bar"#)
@@ -49,12 +68,12 @@
 --
 -- This function has a polymorphic arrow and thus can be used both in
 -- usual and linear contexts.
-runBuilder ∷ ∀ m. Builder %m → Text
+runBuilder ∷ ∀ (m ∷ Multiplicity). Builder %m → Text
 runBuilder (Builder f) = runBuffer f
 {-# INLINE runBuilder #-}
 
 -- | Same as 'runBuilder', but returning a UTF-8 encoded strict 'ByteString'.
-runBuilderBS ∷ ∀ m. Builder %m → ByteString
+runBuilderBS ∷ ∀ (m ∷ Multiplicity). Builder %m → ByteString
 runBuilderBS (Builder f) = runBufferBS f
 {-# INLINE runBuilderBS #-}
 
@@ -69,12 +88,12 @@
   mempty = Builder (\b → b)
   {-# INLINE mempty #-}
 
--- | Use 'fromString' to create 'Builder' from 'String'.
+-- | Use 'fromString' to create t'Builder' from 'String'.
 instance IsString Builder where
   fromString = fromText . fromString
   {-# INLINE fromString #-}
 
--- | Create 'Builder', containing a given 'Text'.
+-- | Create t'Builder', containing a given t'Text'.
 --
 -- >>> :set -XOverloadedStrings
 -- >>> fromText "foo" <> fromText "bar"
@@ -85,7 +104,7 @@
 fromText x = Builder $ \b → b |> x
 {-# INLINE fromText #-}
 
--- | Create 'Builder', containing a given 'Char'.
+-- | Create t'Builder', containing a given 'Char'.
 --
 -- >>> fromChar 'x' <> fromChar 'y'
 -- "xy"
@@ -96,7 +115,7 @@
 fromChar x = Builder $ \b → b |>. x
 {-# INLINE fromChar #-}
 
--- | Create 'Builder', containing a null-terminated UTF-8 string, specified by 'Addr#'.
+-- | Create t'Builder', containing a null-terminated UTF-8 string, specified by 'Addr#'.
 --
 -- >>> :set -XMagicHash
 -- >>> fromAddr "foo"# <> fromAddr "bar"#
@@ -108,7 +127,7 @@
 fromAddr x = Builder $ \b → b |># x
 {-# INLINE fromAddr #-}
 
--- | Create 'Builder', containing decimal representation of a given /bounded/ integer.
+-- | Create t'Builder', containing decimal representation of a given /bounded/ integer.
 --
 -- >>> fromChar 'x' <> fromDec (123 :: Int)
 -- "x123"
@@ -116,7 +135,7 @@
 fromDec x = Builder $ \b → b |>$ x
 {-# INLINE fromDec #-}
 
--- | Create 'Builder', containing decimal representation of a given /unbounded/ integer.
+-- | Create t'Builder', containing decimal representation of a given /unbounded/ integer.
 --
 -- >>> fromChar 'x' <> fromUnboundedDec (1e24 :: Integer)
 -- "x1000000000000000000000000"
@@ -126,7 +145,7 @@
 fromUnboundedDec x = Builder $ \b → b |>$$ x
 {-# INLINE fromUnboundedDec #-}
 
--- | Create 'Builder', containing hexadecimal representation of a given integer.
+-- | Create t'Builder', containing hexadecimal representation of a given integer.
 --
 -- >>> :set -XMagicHash
 -- >>> fromAddr "0x"# <> fromHex (0x123def :: Int)
@@ -135,7 +154,7 @@
 fromHex x = Builder $ \b → b |>& x
 {-# INLINE fromHex #-}
 
--- | Create 'Builder', containing decimal representation of a given 'Double'.
+-- | Create t'Builder', containing decimal representation of a given 'Double'.
 --
 -- >>> :set -XMagicHash
 -- >>> fromAddr "pi="# <> fromDouble pi
diff --git a/src/Data/Text/Builder/Linear/Array.hs b/src/Data/Text/Builder/Linear/Array.hs
--- a/src/Data/Text/Builder/Linear/Array.hs
+++ b/src/Data/Text/Builder/Linear/Array.hs
@@ -16,17 +16,16 @@
 ) where
 
 import Data.Text.Array qualified as A
-import GHC.Exts (Int (..), isByteArrayPinned#, isTrue#, setByteArray#, sizeofByteArray#)
 import GHC.ST (ST (..))
 
-#if __GLASGOW_HASKELL__ >= 909
-import GHC.Exts (unsafeThawByteArray#)
+#if MIN_VERSION_base(4,20,0)
+import GHC.Exts (Int (..), isByteArrayPinned#, isTrue#, setByteArray#, sizeofByteArray#, unsafeThawByteArray#)
 #else
-import GHC.Exts (unsafeCoerce#)
+import GHC.Exts (Int (..), isByteArrayPinned#, isTrue#, setByteArray#, sizeofByteArray#, unsafeCoerce#)
 #endif
 
 unsafeThaw ∷ A.Array → ST s (A.MArray s)
-#if __GLASGOW_HASKELL__ >= 909
+#if MIN_VERSION_base(4,20,0)
 unsafeThaw (A.ByteArray a) = ST $ \s# → case unsafeThawByteArray# a s# of
   (# s'#, ma #) -> (# s'#, A.MutableByteArray ma #)
 #else
diff --git a/src/Data/Text/Builder/Linear/Buffer.hs b/src/Data/Text/Builder/Linear/Buffer.hs
--- a/src/Data/Text/Builder/Linear/Buffer.hs
+++ b/src/Data/Text/Builder/Linear/Buffer.hs
@@ -4,7 +4,7 @@
 -- Licence:     BSD3
 -- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>
 --
--- 'Buffer' for strict 'Text', based on linear types.
+-- t'Buffer' for strict t'Text', based on linear types.
 module Data.Text.Builder.Linear.Buffer (
   -- * Type
   Buffer,
@@ -83,7 +83,7 @@
 import Data.Text.Builder.Linear.Double
 import Data.Text.Builder.Linear.Hex
 
--- | Append 'Text' suffix to a 'Buffer' by mutating it.
+-- | Append t'Text' suffix to a t'Buffer' by mutating it.
 -- If a suffix is statically known, consider using '(|>#)' for optimal performance.
 --
 -- >>> :set -XOverloadedStrings -XLinearTypes
@@ -98,7 +98,7 @@
     (\dst dstOff → A.copyI srcLen dst dstOff src srcOff)
     buffer
 
--- | Prepend 'Text' prefix to a 'Buffer' by mutating it.
+-- | Prepend t'Text' prefix to a t'Buffer' by mutating it.
 -- If a prefix is statically known, consider using '(#<|)' for optimal performance.
 --
 -- >>> :set -XOverloadedStrings -XLinearTypes
@@ -114,7 +114,7 @@
     buffer
 
 -- | Append a null-terminated UTF-8 string
--- to a 'Buffer' by mutating it. E. g.,
+-- to a t'Buffer' by mutating it. E. g.,
 --
 -- >>> :set -XOverloadedStrings -XLinearTypes -XMagicHash
 -- >>> runBuffer (\b -> b |># "foo"# |># "bar"#)
@@ -134,7 +134,7 @@
     srcLen = I# (cstringLength# addr#)
 
 -- | Prepend a null-terminated UTF-8 string
--- to a 'Buffer' by mutating it. E. g.,
+-- to a t'Buffer' by mutating it. E. g.,
 --
 -- >>> :set -XOverloadedStrings -XLinearTypes -XMagicHash
 -- >>> runBuffer (\b -> "foo"# #<| "bar"# #<| b)
diff --git a/src/Data/Text/Builder/Linear/Char.hs b/src/Data/Text/Builder/Linear/Char.hs
--- a/src/Data/Text/Builder/Linear/Char.hs
+++ b/src/Data/Text/Builder/Linear/Char.hs
@@ -31,7 +31,7 @@
 -- Single char
 --------------------------------------------------------------------------------
 
--- | Append 'Char' to a 'Buffer' by mutating it.
+-- | Append 'Char' to a t'Buffer' by mutating it.
 --
 -- >>> :set -XLinearTypes
 -- >>> runBuffer (\b -> b |>. 'q' |>. 'w')
@@ -45,7 +45,7 @@
 infixl 6 |>.
 buffer |>. ch = appendBounded 4 (\dst dstOff → unsafeWrite dst dstOff ch) buffer
 
--- | Prepend 'Char' to a 'Buffer' by mutating it.
+-- | Prepend 'Char' to a t'Buffer' by mutating it.
 --
 -- >>> :set -XLinearTypes
 -- >>> runBuffer (\b -> 'q' .<| 'w' .<| b)
@@ -95,11 +95,15 @@
 -- Multiple chars
 --------------------------------------------------------------------------------
 
--- | Prepend a given count of a 'Char' to a 'Buffer'.
+-- | Prepend a given count of a 'Char' to a t'Buffer'.
 --
 -- >>> :set -XLinearTypes
 -- >>> runBuffer (\b -> prependChars 3 'x' (b |>. 'A'))
 -- "xxxA"
+--
+-- __Warning:__ In contrast to 'Data.Text.Lazy.Builder.singleton', it is the
+-- responsibility of the caller to sanitize surrogate code points with
+-- 'Data.Text.Internal.safe'.
 prependChars ∷ Word → Char → Buffer ⊸ Buffer
 prependChars count ch buff
   | count == 0 = buff
@@ -115,11 +119,15 @@
               )
               buff
 
--- | Apppend a given count of a 'Char' to a 'Buffer'.
+-- | Apppend a given count of a 'Char' to a t'Buffer'.
 --
 -- >>> :set -XLinearTypes
 -- >>> runBuffer (\b -> appendChars 3 'x' (b |>. 'A'))
 -- "Axxx"
+--
+-- __Warning:__ In contrast to 'Data.Text.Lazy.Builder.singleton', it is the
+-- responsibility of the caller to sanitize surrogate code points with
+-- 'Data.Text.Internal.safe'.
 appendChars ∷ Word → Char → Buffer ⊸ Buffer
 appendChars count ch buff
   | count == 0 = buff
@@ -164,6 +172,10 @@
 --
 -- >>> runBuffer (\b -> (b |> "Test:") `appendJustified` "AAA" `appendJustified` "BBBBBBB")
 -- "Test:         AAA     BBBBBBB"
+--
+-- __Warning:__ In contrast to 'Data.Text.Lazy.Builder.singleton', it is the
+-- responsibility of the caller to sanitize surrogate code points with
+-- 'Data.Text.Internal.safe'.
 justifyRight ∷ Word → Char → Buffer ⊸ Buffer
 justifyRight n ch buff = case lengthOfBuffer buff of
   (# buff', len #) →
@@ -183,6 +195,10 @@
 --
 -- Note that 'newEmptyBuffer' is needed in some situations. See 'justifyRight'
 -- for an example.
+--
+-- __Warning:__ In contrast to 'Data.Text.Lazy.Builder.singleton', it is the
+-- responsibility of the caller to sanitize surrogate code points with
+-- 'Data.Text.Internal.safe'.
 justifyLeft ∷ Word → Char → Buffer ⊸ Buffer
 justifyLeft n ch buff = case lengthOfBuffer buff of
   (# buff', len #) →
@@ -201,6 +217,10 @@
 --
 -- Note that 'newEmptyBuffer' is needed in some situations. See 'justifyRight'
 -- for an example.
+--
+-- __Warning:__ In contrast to 'Data.Text.Lazy.Builder.singleton', it is the
+-- responsibility of the caller to sanitize surrogate code points with
+-- 'Data.Text.Internal.safe'.
 center ∷ Word → Char → Buffer ⊸ Buffer
 center n ch buff = case lengthOfBuffer buff of
   (# buff', len #) →
diff --git a/src/Data/Text/Builder/Linear/Core.hs b/src/Data/Text/Builder/Linear/Core.hs
--- a/src/Data/Text/Builder/Linear/Core.hs
+++ b/src/Data/Text/Builder/Linear/Core.hs
@@ -4,7 +4,7 @@
 -- Licence:     BSD3
 -- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>
 --
--- Low-level routines for 'Buffer' manipulations.
+-- Low-level routines for t'Buffer' manipulations.
 module Data.Text.Builder.Linear.Core (
   -- * Type
   Buffer,
diff --git a/src/Data/Text/Builder/Linear/Double.hs b/src/Data/Text/Builder/Linear/Double.hs
--- a/src/Data/Text/Builder/Linear/Double.hs
+++ b/src/Data/Text/Builder/Linear/Double.hs
@@ -21,8 +21,6 @@
 
 -- | Append the decimal representation of a 'Double'.
 --
--- Matches 'show' in displaying in standard or scientific notation:
---
 -- >>> runBuffer (\b -> b |>% 123.456)
 -- "123.456"
 --
@@ -38,9 +36,6 @@
     buffer
 
 -- | Prepend the decimal representation of a 'Double'.
---
--- Matches 'show' in displaying in standard or scientific notation
--- (see examples in @'(|>%)'@).
 (%<|) ∷ Double → Buffer ⊸ Buffer
 
 infixr 6 %<|
diff --git a/src/Data/Text/Builder/Linear/Internal.hs b/src/Data/Text/Builder/Linear/Internal.hs
--- a/src/Data/Text/Builder/Linear/Internal.hs
+++ b/src/Data/Text/Builder/Linear/Internal.hs
@@ -1,10 +1,12 @@
+{-# LANGUAGE CPP #-}
+
 -- |
 -- Copyright:   (c) 2022 Andrew Lelechenko
 --              (c) 2023 Pierre Le Marre
 -- Licence:     BSD3
 -- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>
 --
--- Internal routines for 'Buffer' manipulations.
+-- Internal routines for t'Buffer' manipulations.
 module Data.Text.Builder.Linear.Internal (
   -- * Type
   Buffer,
@@ -35,21 +37,26 @@
 import Data.Text qualified as T
 import Data.Text.Array qualified as A
 import Data.Text.Internal (Text (..))
-import GHC.Exts (Int (..), Levity (..), RuntimeRep (..), TYPE, byteArrayContents#, plusAddr#, unsafeCoerce#)
 import GHC.ForeignPtr (ForeignPtr (..), ForeignPtrContents (..))
 import GHC.ST (ST (..), runST)
 
+#if MIN_VERSION_base(4,20,0)
+import GHC.Exts (Int (..), Levity (..), RuntimeRep (..), TYPE, byteArrayContents#, plusAddr#, unsafeThawByteArray#, realWorld#)
+#else
+import GHC.Exts (Int (..), Levity (..), RuntimeRep (..), TYPE, byteArrayContents#, plusAddr#, unsafeCoerce#)
+#endif
+
 import Data.Text.Builder.Linear.Array
 
--- | Internally 'Buffer' is a mutable buffer.
--- If a client gets hold of a variable of type 'Buffer',
+-- | Internally t'Buffer' is a mutable buffer.
+-- If a client gets hold of a variable of type t'Buffer',
 -- they'd be able to pass a mutable buffer to concurrent threads.
 -- That's why API below is carefully designed to prevent such possibility:
--- clients always work with linear functions 'Buffer' ⊸ 'Buffer' instead
--- and run them on an empty 'Buffer' to extract results.
+-- clients always work with linear functions t'Buffer' ⊸ t'Buffer' instead
+-- and run them on an empty t'Buffer' to extract results.
 --
 -- In terms of [@linear-base@](https://hackage.haskell.org/package/linear-base)
--- 'Buffer' is [@Consumable@](https://hackage.haskell.org/package/linear-base/docs/Prelude-Linear.html#t:Consumable)
+-- t'Buffer' is [@Consumable@](https://hackage.haskell.org/package/linear-base/docs/Prelude-Linear.html#t:Consumable)
 -- (see 'consumeBuffer')
 -- and [@Dupable@](https://hackage.haskell.org/package/linear-base/docs/Prelude-Linear.html#t:Dupable)
 -- (see 'dupBuffer'),
@@ -63,19 +70,19 @@
 -- Remember: this is a strict builder, so on contrary to "Data.Text.Lazy.Builder"
 -- for optimal performance you should use strict left folds instead of lazy right ones.
 --
--- 'Buffer' is an unlifted datatype,
+-- t'Buffer' is an unlifted datatype,
 -- so you can put it into an unboxed tuple @(# ..., ... #)@,
 -- but not into @(..., ...)@.
 data Buffer ∷ TYPE ('BoxedRep 'Unlifted) where
   Buffer ∷ {-# UNPACK #-} !Text → Buffer
 
--- | Unwrap 'Buffer', no-op.
+-- | Unwrap t'Buffer', no-op.
 -- Most likely, this is not the function you're looking for
 -- and you need 'runBuffer' instead.
 unBuffer ∷ Buffer ⊸ Text
 unBuffer (Buffer x) = x
 
--- | Run a linear function on an empty 'Buffer', producing a strict 'Text'.
+-- | Run a linear function on an empty t'Buffer', producing a strict t'Text'.
 --
 -- Be careful to write @runBuffer (\\b -> ...)@ instead of @runBuffer $ \\b -> ...@,
 -- because current implementation of linear types lacks special support for '($)'.
@@ -88,10 +95,8 @@
 -- 'runBuffer' is similar in spirit to mutable arrays API in
 -- [@Data.Array.Mutable.Linear@](https://hackage.haskell.org/package/linear-base/docs/Data-Array-Mutable-Linear.html),
 -- which provides functions like
--- [@fromList@](https://hackage.haskell.org/package/linear-base/docs/Data-Array-Mutable-Linear.html#v:fromList) ∷ [@a@] → (@Vector@ @a@ ⊸ [@Ur@](https://hackage.haskell.org/package/linear-base-0.3.0/docs/Prelude-Linear.html#t:Ur) b) ⊸ [@Ur@](https://hackage.haskell.org/package/linear-base-0.3.0/docs/Prelude-Linear.html#t:Ur) @b@.
--- Here the initial buffer is always empty and @b@ is 'Text'. Since 'Text' is
--- [@Movable@](https://hackage.haskell.org/package/linear-base/docs/Prelude-Linear.html#t:Movable),
--- 'Text' and [@Ur@](https://hackage.haskell.org/package/linear-base-0.3.0/docs/Prelude-Linear.html#t:Ur) 'Text' are equivalent.
+-- [@fromList@](https://hackage.haskell.org/package/linear-base/docs/Data-Array-Mutable-Linear.html#v:fromList) ∷ @Movable@ @b@ ⇒ [@a@] → (@Array@ @a@ ⊸ @b@) ⊸ @b@.
+-- Here the initial buffer is always empty and @b@ is t'Text'.
 runBuffer ∷ (Buffer ⊸ Buffer) ⊸ Text
 runBuffer f = unBuffer (shrinkBuffer (f (Buffer mempty)))
 {-# NOINLINE runBuffer #-}
@@ -100,7 +105,7 @@
   See https://github.com/Bodigrim/linear-builder/issues/19
   and https://github.com/tweag/linear-base/pull/187#discussion_r489081926
   for the discussion why NOINLINE here and below in 'runBufferBS' is necessary.
-  Without it CSE (common subexpression elimination) can pull out 'Buffer's from
+  Without it CSE (common subexpression elimination) can pull out t'Buffer's from
   different 'runBuffer's and share them, which is absolutely not what we want.
 -}
 
@@ -110,7 +115,11 @@
   Buffer (Text (A.ByteArray arr) (I# from) len) → BS fp len
     where
       addr# = byteArrayContents# arr `plusAddr#` from
+#if MIN_VERSION_base(4,20,0)
+      fp = ForeignPtr addr# (PlainPtr (let !(# _, ma #) = unsafeThawByteArray# arr realWorld# in ma))
+#else
       fp = ForeignPtr addr# (PlainPtr (unsafeCoerce# arr))
+#endif
 {-# NOINLINE runBufferBS #-}
 
 shrinkBuffer ∷ Buffer ⊸ Buffer
@@ -126,9 +135,9 @@
   arr ← A.unsafeFreeze marr
   pure $ Text arr 0 0
 
--- | Create an empty 'Buffer'.
+-- | Create an empty t'Buffer'.
 --
--- The first 'Buffer' is the input and the second is a new empty 'Buffer'.
+-- The first t'Buffer' is the input and the second is a new empty t'Buffer'.
 --
 -- This function is needed in some situations, e.g. with
 -- 'Data.Text.Builder.Linear.Buffer.justifyRight'. The following example creates
@@ -149,7 +158,7 @@
 -- "Test:         AAA     BBBBBBB"
 --
 -- Note: a previous buffer is necessary in order to create an empty buffer with
--- the same characteristics.
+-- the same pinnedness.
 newEmptyBuffer ∷ Buffer ⊸ (# Buffer, Buffer #)
 newEmptyBuffer (Buffer t@(Text arr _ _)) =
   (# Buffer t, Buffer (if isPinned arr then memptyPinned else mempty) #)
@@ -160,7 +169,7 @@
 -- from [@linear-base@](https://hackage.haskell.org/package/linear-base).
 --
 -- It is a bit tricky to use because of
--- <https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/linear_types.html#limitations current limitations>
+-- <https://downloads.haskell.org/ghc/9.8.1/docs/users_guide/exts/linear_types.html#limitations current limitations>
 -- of linear types with regards to @let@ and @where@. E. g., one cannot write
 --
 -- > let (# b1, b2 #) = dupBuffer b in ("foo" <| b1) >< (b2 |> "bar")
@@ -172,7 +181,7 @@
 -- >>> runBuffer (\b -> case dupBuffer b of (# b1, b2 #) -> ("foo" <| b1) >< (b2 |> "bar"))
 -- "foobar"
 --
--- Note the unboxed tuple: 'Buffer' is an unlifted datatype,
+-- Note the unboxed tuple: t'Buffer' is an unlifted datatype,
 -- so it cannot be put into @(..., ...)@.
 dupBuffer ∷ Buffer ⊸ (# Buffer, Buffer #)
 dupBuffer (Buffer x) = (# Buffer x, Buffer (T.copy x) #)
@@ -184,7 +193,7 @@
 consumeBuffer ∷ Buffer ⊸ ()
 consumeBuffer Buffer {} = ()
 
--- | Erase buffer's content, replacing it with an empty 'Text'.
+-- | Erase buffer's content, replacing it with an empty t'Text'.
 eraseBuffer ∷ Buffer ⊸ Buffer
 eraseBuffer (Buffer (Text arr _ _)) =
   Buffer (if isPinned arr then memptyPinned else mempty)
@@ -208,23 +217,23 @@
 lengthOfBuffer ∷ Buffer ⊸ (# Buffer, Word #)
 lengthOfBuffer (Buffer t) = (# Buffer t, fromIntegral (T.length t) #)
 
--- | Slice 'Buffer' by dropping given number of 'Char's.
+-- | Slice t'Buffer' by dropping given number of 'Char's.
 dropBuffer ∷ Word → Buffer ⊸ Buffer
 dropBuffer nChar (Buffer t@(Text arr off len))
-  | nByte <= 0 = Buffer (Text arr (off + len) 0)
+  | nByte < 0 = Buffer (Text arr (off + len) 0)
   | otherwise = Buffer (Text arr (off + nByte) (len - nByte))
   where
     nByte = T.measureOff (fromIntegral nChar) t
 
--- | Slice 'Buffer' by taking given number of 'Char's.
+-- | Slice t'Buffer' by taking given number of 'Char's.
 takeBuffer ∷ Word → Buffer ⊸ Buffer
 takeBuffer nChar (Buffer t@(Text arr off _))
-  | nByte <= 0 = Buffer t
+  | nByte < 0 = Buffer t
   | otherwise = Buffer (Text arr off nByte)
   where
     nByte = T.measureOff (fromIntegral nChar) t
 
--- | Low-level routine to append data of unknown size to a 'Buffer'.
+-- | Low-level routine to append data of unknown size to a t'Buffer'.
 appendBounded
   ∷ Int
   -- ^ Upper bound for the number of bytes, written by an action
@@ -248,7 +257,7 @@
   pure $ Text new dstOff (dstLen + srcLen)
 {-# INLINE appendBounded #-}
 
--- | Low-level routine to append data of unknown size to a 'Buffer', giving
+-- | Low-level routine to append data of unknown size to a t'Buffer', giving
 -- the action the choice between two strategies.
 --
 -- See also: 'appendBounded'.
@@ -296,12 +305,12 @@
           -- Note: we rely on copyM allowing overlaps
           A.copyM newM (dstOff + dstLen) newM (off' - count) count
           pure (dstOff, count)
-  !(dstOff', srcLen) ← writer append prepend
+  (dstOff', srcLen) ← writer append prepend
   new ← A.unsafeFreeze newM
   pure $ Text new dstOff' (dstLen + srcLen)
 {-# INLINE appendBounded' #-}
 
--- | Low-level routine to append data of known size to a 'Buffer'.
+-- | Low-level routine to append data of known size to a t'Buffer'.
 appendExact
   ∷ Int
   -- ^ Exact number of bytes, written by an action
@@ -315,7 +324,7 @@
     (\dst dstOff → appender dst dstOff >> pure srcLen)
 {-# INLINE appendExact #-}
 
--- | Low-level routine to prepend data of unknown size to a 'Buffer'.
+-- | Low-level routine to prepend data of unknown size to a t'Buffer'.
 prependBounded
   ∷ Int
   -- ^ Upper bound for the number of bytes, written by an action
@@ -344,7 +353,7 @@
       pure $ Text new newOff (dstLen + srcLen)
 {-# INLINE prependBounded #-}
 
--- | Low-level routine to prepend data of unknown size to a 'Buffer'.
+-- | Low-level routine to prepend data of unknown size to a t'Buffer'.
 --
 -- Contrary to 'prependBounded', only use a prepend action.
 --
@@ -374,7 +383,7 @@
       pure $ Text new (off - srcLen) (dstLen + srcLen)
 {-# INLINE prependBounded' #-}
 
--- | Low-level routine to append data of known size to a 'Buffer'.
+-- | Low-level routine to append data of known size to a t'Buffer'.
 prependExact
   ∷ Int
   -- ^ Exact number of bytes, written by an action
@@ -389,7 +398,7 @@
     (\dst dstOff → appender dst dstOff >> pure srcLen)
 {-# INLINE prependExact #-}
 
--- | Concatenate two 'Buffer's, potentially mutating both of them.
+-- | Concatenate two t'Buffer's, potentially mutating both of them.
 --
 -- You likely need to use 'dupBuffer' to get hold on two builders at once:
 --
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -21,6 +21,7 @@
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as T
 import Data.Text.Builder.Linear.Buffer
+import Data.Text.Builder.Linear.Core (dropBuffer, takeBuffer)
 import Data.Text.Internal (Text(..))
 import Data.Text.Lazy (toStrict)
 import Data.Text.Lazy.Builder qualified as TB
@@ -120,7 +121,7 @@
     where
       arbitraryCharCount = chooseBoundedIntegral (0, 6)
       arbitraryTotalLength = chooseBoundedIntegral (3, 20)
-      arbitraryInteger = chooseInteger 
+      arbitraryInteger = chooseInteger
         ( fromIntegral @Int minBound ^ (3 :: Word)
         , fromIntegral @Int maxBound ^ (3 :: Word) )
 
@@ -186,12 +187,7 @@
       then hexadecimal x
       else hexadecimal (fromIntegral @_ @Word64 x .&. (shiftL 1 (intSize x) - 1))
 
-    hexadecimalSW (SomeWordN x) = hexadecimalW x
-
-    hexadecimalW ∷ (KnownNat n) ⇒ WordN n → TB.Builder
-    hexadecimalW x = if x >= 0
-      then hexadecimal x
-      else hexadecimal (fromIntegral @_ @Word64 x .&. (shiftL 1 (intSize x) - 1))
+    hexadecimalSW (SomeWordN x) = hexadecimal x
 
     intersperseText ∷ [TB.Builder] → Text
     intersperseText bs =
@@ -242,7 +238,24 @@
   , testProperty "CSE 1" prop6
   , testProperty "CSE 2" prop7
   , testProperty "unbounded integers" prop8
+  , testProperty "dropBuffer" propDropBuffer
+  , testProperty "dropBuffer" propDropBuffer
+  , testProperty "takeBuffer" propTakeBuffer
   ]
+
+propDropBuffer :: Word → Text → Property
+propDropBuffer n xs =
+  suff === runBuffer (\b → dropBuffer n (b |> xs)) .&&.
+  T.encodeUtf8 suff === runBufferBS (\b → dropBuffer n (b |> xs))
+  where
+    suff = T.drop (fromIntegral n) xs
+
+propTakeBuffer :: Word → Text → Property
+propTakeBuffer n xs =
+  pref === runBuffer (\b → takeBuffer n (b |> xs)) .&&.
+  T.encodeUtf8 pref === runBufferBS (\b → takeBuffer n (b |> xs))
+  where
+    pref = T.take (fromIntegral n) xs
 
 prop1 ∷ [Action] → Property
 prop1 acts = interpretOnText acts mempty ===
diff --git a/text-builder-linear.cabal b/text-builder-linear.cabal
--- a/text-builder-linear.cabal
+++ b/text-builder-linear.cabal
@@ -1,12 +1,15 @@
 cabal-version:   2.4
 name:            text-builder-linear
-version:         0.1.3
+version:         0.1.4
 license:         BSD-3-Clause
 license-file:    LICENSE
 copyright:       2022 Andrew Lelechenko
 maintainer:      Andrew Lelechenko <andrew.lelechenko@gmail.com>
 author:          Andrew Lelechenko
-tested-with:     ghc ==9.2.8 ghc ==9.4.8 ghc ==9.6.6 ghc ==9.8.2 ghc ==9.10.1
+tested-with:
+    ghc ==9.2.8 ghc ==9.4.8 ghc ==9.6.7 ghc ==9.8.4 ghc ==9.10.3
+    ghc ==9.12.2 ghc ==9.14.1
+
 homepage:        https://github.com/Bodigrim/linear-builder
 synopsis:        Builder for Text and ByteString based on linear types
 description:
@@ -20,7 +23,7 @@
 
 source-repository head
     type:     git
-    location: git://github.com/Bodigrim/linear-builder.git
+    location: git@github.com:Bodigrim/linear-builder.git
 
 library
     exposed-modules:
@@ -48,7 +51,7 @@
         base >=4.16 && <5,
         text >=2.0 && <2.2,
         bytestring >=0.11 && <0.13,
-        ghc-bignum >=1.1 && < 2.0,
+        ghc-bignum >=1.1 && <2,
         quote-quot >=0.2.1 && <0.3
 
 test-suite linear-builder-tests
@@ -57,8 +60,8 @@
     hs-source-dirs:     test
     default-language:   GHC2021
     default-extensions:
-        DerivingStrategies LinearTypes MagicHash NumDecimals PatternSynonyms
-        UnboxedTuples UnicodeSyntax
+        DerivingStrategies LinearTypes MagicHash NumDecimals
+        PatternSynonyms UnboxedTuples UnicodeSyntax
 
     ghc-options:
         -Wall -Wno-orphans -threaded -rtsopts "-with-rtsopts -N"
@@ -77,7 +80,6 @@
     other-modules:
         BenchChar
         BenchDecimal
-        BenchDecimalUnbounded
         BenchDouble
         BenchHexadecimal
         BenchText
@@ -93,7 +95,8 @@
         -- NOTE: The following packages are optional, but are not required that
         --       often. While they could be guarded by a flag, we prefer keeping
         --       the Hackage page simple. Just uncomment these lines when needed.
-        -- bytestring-strict-builder >= 0.4.5 && < 0.5,
-        -- text-builder >= 0.6.7 && < 0.7,
+        -- bytestring-strict-builder >=0.4.5 && <0.5,
+        -- text-builder >=1.0 && <1.1,
+        -- text-builder-dev >=0.4 && <0.5,
         tasty,
-        tasty-bench >=0.4 && <0.5
+        tasty-bench >=0.4 && <0.6
