diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Revision history for bytesmith
 
+## 0.3.9.0 -- 2022-07-14
+
+* Build with GHC 9.2.3.
+
 ## 0.3.8.0 -- 2021-10-11
 
 * Add `peek` and `peek'` to `Data.Bytes.Parser.Latin`.
diff --git a/bytesmith.cabal b/bytesmith.cabal
--- a/bytesmith.cabal
+++ b/bytesmith.cabal
@@ -1,6 +1,6 @@
 cabal-version: 2.2
 name: bytesmith
-version: 0.3.8.0
+version: 0.3.9.0
 synopsis: Nonresumable byte parser
 description:
   Parse bytes as fast as possible. This is a nonresumable parser
@@ -33,9 +33,9 @@
     Data.Bytes.Parser.Types
   build-depends:
     , base >=4.12 && <5
-    , bytestring >=0.10.8 && <0.11
-    , byteslice >=0.1.4 && <0.3
-    , contiguous >= 0.4 && < 0.7
+    , bytestring >=0.10.8 && <=0.11.3.0
+    , byteslice >=0.2.6 && <0.3
+    , contiguous >= 0.6 && < 0.7
     , primitive >=0.7 && <0.8
     , text-short >=0.1.3 && <0.2
     , run-st >=0.1 && <0.2
diff --git a/src/Data/Bytes/Parser.hs b/src/Data/Bytes/Parser.hs
--- a/src/Data/Bytes/Parser.hs
+++ b/src/Data/Bytes/Parser.hs
@@ -15,6 +15,7 @@
 {-# language TypeApplications #-}
 {-# language UnboxedSums #-}
 {-# language UnboxedTuples #-}
+{-# language CPP #-}
 
 -- | Parse non-resumable sequence of bytes. To parse a byte sequence
 -- as text, use the @Ascii@, @Latin@, and @Utf8@ modules instead.
@@ -217,12 +218,20 @@
 -- | Consume input matching the @NUL@-terminated C String.
 cstring :: e -> CString -> Parser e s ()
 cstring e (Exts.Ptr ptr0) = Parser
-  ( \(# arr, off0, len0 #) s -> 
-    let go !ptr !off !len = case Exts.indexWord8OffAddr# ptr 0# of
+  ( \(# arr, off0, len0 #) s ->
+    let go !ptr !off !len = case
+#if MIN_VERSION_base(4,16,0)
+                 Exts.word8ToWord#
+#endif
+            (Exts.indexWord8OffAddr# ptr 0#) of
           0## -> (# s, (# | (# (), off, len #) #) #)
           c -> case len of
             0# -> (# s, (# e | #) #)
-            _ -> case Exts.eqWord# c (Exts.indexWord8Array# arr off) of
+            _ -> case Exts.eqWord# c (
+#if MIN_VERSION_base(4,16,0)
+                 Exts.word8ToWord#
+#endif
+              (Exts.indexWord8Array# arr off)) of
               1# -> go (Exts.plusAddr# ptr 1# ) (off +# 1# ) (len -# 1# )
               _ -> (# s, (# e | #) #)
      in go ptr0 off0 len0
@@ -483,7 +492,11 @@
   (\x s0 -> case f x s0 of
     (# s1, r #) -> case r of
       (# e | #) -> (# s1, (# e | #) #)
-      (# | (# W32# a, b, c #) #) -> (# s1, (# | (# a, b, c #) #) #)
+      (# | (# W32# a, b, c #) #) -> (# s1, (# | (#
+#if MIN_VERSION_base(4,16,0)
+        Exts.word32ToWord#
+#endif
+        a, b, c #) #) #)
   )
 
 -- | Convert a @(Int,Int)@ parser to a @(# Int#, Int# #)@ parser.
@@ -504,7 +517,11 @@
   (\x s0 -> case f x s0 of
     (# s1, r #) -> case r of
       (# e | #) -> (# s1, (# e | #) #)
-      (# | (# a, b, c #) #) -> (# s1, (# | (# W32# a, b, c #) #) #)
+      (# | (# a, b, c #) #) -> (# s1, (# | (# W32# (
+#if MIN_VERSION_base(4,16,0)
+        Exts.wordToWord32#
+#endif
+        a), b, c #) #) #)
   )
 
 -- | Convert a @(# Int#, Int# #)@ parser to a @(Int,Int)@ parser.
diff --git a/src/Data/Bytes/Parser/Internal.hs b/src/Data/Bytes/Parser/Internal.hs
--- a/src/Data/Bytes/Parser/Internal.hs
+++ b/src/Data/Bytes/Parser/Internal.hs
@@ -131,9 +131,7 @@
   (<*>) = Control.Monad.ap
 
 instance Monad (Parser e s) where
-  {-# inline return #-}
   {-# inline (>>=) #-}
-  return = pureParser
   (>>=) = bindParser
 
 instance Functor (Parser e s) where
diff --git a/src/Data/Bytes/Parser/Latin.hs b/src/Data/Bytes/Parser/Latin.hs
--- a/src/Data/Bytes/Parser/Latin.hs
+++ b/src/Data/Bytes/Parser/Latin.hs
@@ -15,6 +15,7 @@
 {-# language TypeApplications #-}
 {-# language UnboxedSums #-}
 {-# language UnboxedTuples #-}
+{-# language CPP #-}
 
 -- | Parse input as though it were text encoded by
 -- ISO 8859-1 (Latin-1). All byte sequences are valid
@@ -385,7 +386,7 @@
   -> Bytes -- chunk
   -> Result# e ()
 skipDigitsAscii1LoopStart e !c = if length c > 0
-  then 
+  then
     let w = indexLatinCharArray (array c) (offset c)
      in if w >= '0' && w <= '9'
           then upcastUnitSuccess (skipDigitsAsciiLoop (Bytes.unsafeDrop 1 c))
@@ -406,6 +407,7 @@
   upcastUnitSuccess (skipDigitsAsciiLoop c)
 
 unI :: Int -> Int#
+{-# inline unI #-}
 unI (I# w) = w
 
 -- | Skip the character any number of times. This succeeds
@@ -600,6 +602,7 @@
   else (# | (# unW acc, unI (offset chunk0), 0# #) #)
 
 unW :: Word -> Word#
+{-# inline unW #-}
 unW (W# w) = w
 
 decWordStart ::
@@ -619,19 +622,31 @@
 upcastWord16Result :: Result# e Word# -> Result# e Word16
 {-# inline upcastWord16Result #-}
 upcastWord16Result (# e | #) = (# e | #)
-upcastWord16Result (# | (# a, b, c #) #) = (# | (# W16# a, b, c #) #)
+upcastWord16Result (# | (# a, b, c #) #) = (# | (# W16# (
+#if MIN_VERSION_base(4,16,0)
+  Exts.wordToWord16#
+#endif
+  a), b, c #) #)
 
 -- Precondition: the word is small enough
 upcastWord32Result :: Result# e Word# -> Result# e Word32
 {-# inline upcastWord32Result #-}
 upcastWord32Result (# e | #) = (# e | #)
-upcastWord32Result (# | (# a, b, c #) #) = (# | (# W32# a, b, c #) #)
+upcastWord32Result (# | (# a, b, c #) #) = (# | (# W32# (
+#if MIN_VERSION_base(4,16,0)
+  Exts.wordToWord32#
+#endif
+  a), b, c #) #)
 
 -- Precondition: the word is small enough
 upcastWord8Result :: Result# e Word# -> Result# e Word8
 {-# inline upcastWord8Result #-}
 upcastWord8Result (# e | #) = (# e | #)
-upcastWord8Result (# | (# a, b, c #) #) = (# | (# W8# a, b, c #) #)
+upcastWord8Result (# | (# a, b, c #) #) = (# | (# W8# (
+#if MIN_VERSION_base(4,16,0)
+  Exts.wordToWord8#
+#endif
+  a), b, c #) #)
 
 -- | Parse a decimal-encoded number. If the number is too large to be
 -- represented by a machine integer, this fails with the provided
@@ -707,7 +722,7 @@
     (\chunk0 s0 -> decPosIntStart e (boxBytes chunk0) s0)
   '-' -> Parser -- minus sign
     (\chunk0 s0 -> decNegIntStart e (boxBytes chunk0) s0)
-  _ -> Parser -- no sign, there should be a digit here 
+  _ -> Parser -- no sign, there should be a digit here
     (\chunk0 s0 ->
       let !w = char2Word c - 48
         in if w < 10
@@ -722,7 +737,7 @@
 decStandardInt# e = any e `bindFromLiftedToInt` \c -> case c of
   '-' -> Parser -- minus sign
     (\chunk0 s0 -> decNegIntStart e (boxBytes chunk0) s0)
-  _ -> Parser -- no sign, there should be a digit here 
+  _ -> Parser -- no sign, there should be a digit here
     (\chunk0 s0 ->
       let !w = char2Word c - 48
         in if w < 10
@@ -759,7 +774,7 @@
   '-' -> do
     x <- decUnsignedInteger e
     pure $! negate x
-  _ -> Parser -- no sign, there should be a digit here 
+  _ -> Parser -- no sign, there should be a digit here
     (\chunk0 s0 ->
       let !w = char2Word c - 48 in
       if w < 10
@@ -795,11 +810,11 @@
     let !w = fromIntegral @Word8 @Word
           (PM.indexByteArray (array chunk0) (offset chunk0)) - 48
      in if w < 10
-          then 
+          then
             case decPosIntMore e w (maxIntAsWord + 1) (Bytes.unsafeDrop 1 chunk0) of
-             (# | (# x, y, z #) #) -> 
+             (# | (# x, y, z #) #) ->
                (# s0, (# | (# (notI# x +# 1# ), y, z #) #) #)
-             (# err | #) -> 
+             (# err | #) ->
                (# s0, (# err | #) #)
           else (# s0, (# e | #) #)
   else (# s0, (# e | #) #)
@@ -827,7 +842,7 @@
 -- exceeds the upper bound.
 decPosIntMore ::
      e -- Error message
-  -> Word -- Accumulator, precondition: less than or equal to bound 
+  -> Word -- Accumulator, precondition: less than or equal to bound
   -> Word -- Inclusive Upper Bound, either (2^63 - 1) or 2^63
   -> Bytes -- Chunk
   -> Result# e Int#
@@ -918,7 +933,7 @@
 -- | Skip all characters until the terminator is encountered
 -- and then consume the matching character as well. Visually,
 -- @skipTrailedBy \'C\'@ advances the cursor like this:
--- 
+--
 -- >  A Z B Y C X C W
 -- > |->->->->-|
 --
@@ -931,7 +946,7 @@
 -- | Skip all characters until the terminator is encountered.
 -- This does not consume the terminator. Visually, @skipUntil \'C\'@
 -- advances the cursor like this:
--- 
+--
 -- >  A Z B Y C X C W
 -- > |->->->-|
 --
@@ -971,7 +986,11 @@
   (\x s0 -> case runParser (hexFixedWord32# e) x s0 of
     (# s1, r #) -> case r of
       (# err | #) -> (# s1, (# err | #) #)
-      (# | (# a, b, c #) #) -> (# s1, (# | (# W32# a, b, c #) #) #)
+      (# | (# a, b, c #) #) -> (# s1, (# | (# W32# (
+#if MIN_VERSION_base(4,16,0)
+        Exts.wordToWord32#
+#endif
+        a), b, c #) #) #)
   )
 
 hexFixedWord32# :: e -> Parser e s Word#
@@ -1020,7 +1039,7 @@
   then
     let go !off !len !acc = case len of
           0 -> case acc of
-            W# r -> 
+            W# r ->
               (# | (# r
               ,  unI off
               ,  unI (length chunk) -# 16# #) #)
@@ -1042,7 +1061,11 @@
   (\x s0 -> case runParser (hexFixedWord16# e) x s0 of
     (# s1, r #) -> case r of
       (# err | #) -> (# s1, (# err | #) #)
-      (# | (# a, b, c #) #) -> (# s1, (# | (# W16# a, b, c #) #) #)
+      (# | (# a, b, c #) #) -> (# s1, (# | (# W16# (
+#if MIN_VERSION_base(4,16,0)
+        Exts.wordToWord16#
+#endif
+        a), b, c #) #) #)
   )
 
 hexFixedWord16# :: e -> Parser e s Word#
@@ -1074,7 +1097,11 @@
   (\x s0 -> case runParser (hexFixedWord8# e) x s0 of
     (# s1, r #) -> case r of
       (# err | #) -> (# s1, (# err | #) #)
-      (# | (# a, b, c #) #) -> (# s1, (# | (# W8# a, b, c #) #) #)
+      (# | (# a, b, c #) #) -> (# s1, (# | (# W8# (
+#if MIN_VERSION_base(4,16,0)
+        Exts.wordToWord8#
+#endif
+        a), b, c #) #) #)
   )
 
 hexFixedWord8# :: e -> Parser e s Word#
@@ -1173,7 +1200,7 @@
 -- word is less than or equal to the upper bound
 positivePushBase10 :: Word -> Word -> Word -> (Bool,Word)
 {-# inline positivePushBase10 #-}
-positivePushBase10 (W# a) (W# b) (W# upper) = 
+positivePushBase10 (W# a) (W# b) (W# upper) =
   let !(# ca, r0 #) = Exts.timesWord2# a 10##
       !r1 = Exts.plusWord# r0 b
       !cb = int2Word# (gtWord# r1 upper)
@@ -1183,7 +1210,7 @@
 
 unsignedPushBase10 :: Word -> Word -> (Bool,Word)
 {-# inline unsignedPushBase10 #-}
-unsignedPushBase10 (W# a) (W# b) = 
+unsignedPushBase10 (W# a) (W# b) =
   let !(# ca, r0 #) = Exts.timesWord2# a 10##
       !r1 = Exts.plusWord# r0 b
       !cb = int2Word# (ltWord# r1 r0)
diff --git a/src/Data/Bytes/Parser/Rebindable.hs b/src/Data/Bytes/Parser/Rebindable.hs
--- a/src/Data/Bytes/Parser/Rebindable.hs
+++ b/src/Data/Bytes/Parser/Rebindable.hs
@@ -6,6 +6,7 @@
 {-# language TypeInType #-}
 {-# language UnboxedSums #-}
 {-# language UnboxedTuples #-}
+{-# language CPP #-}
 
 -- | Provides levity-polymorphic variants of @>>=@, @>>@, and @pure@
 -- used to assemble parsers whose result types are unlifted. This
@@ -21,10 +22,16 @@
   , Pure(..)
   ) where
 
-import Prelude () 
+import Prelude ()
 import GHC.Exts (TYPE,RuntimeRep(..))
 import Data.Bytes.Parser.Internal (Parser(..))
 
+#if MIN_VERSION_base(4,16,0)
+import GHC.Exts (LiftedRep)
+#else
+type LiftedRep = 'LiftedRep
+#endif
+
 class Bind (ra :: RuntimeRep) (rb :: RuntimeRep) where
   (>>=) :: forall e s (a :: TYPE ra) (b :: TYPE rb).
     Parser e s a -> (a -> Parser e s b) -> Parser e s b
@@ -160,8 +167,8 @@
       (# | (# _, b, c #) #) -> g (# arr, b, c #) s1
   )
 
-bindInt2to5Parser :: forall 
-  (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep])) 
+bindInt2to5Parser :: forall
+  (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep]))
   (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]))
   e s.
   Parser e s a -> (a -> Parser e s b) -> Parser e s b
@@ -174,7 +181,7 @@
         runParser (g y) (# arr, b, c #) s1
   )
 
-sequenceInt2to5Parser :: forall 
+sequenceInt2to5Parser :: forall
   (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep]))
   (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]))
   e s.
@@ -187,25 +194,25 @@
       (# | (# _, b, c #) #) -> g (# arr, b, c #) s1
   )
 
-instance Bind 'LiftedRep 'LiftedRep where
+instance Bind LiftedRep LiftedRep where
   {-# inline (>>=) #-}
   {-# inline (>>) #-}
   (>>=) = bindParser
   (>>) = sequenceParser
 
-instance Bind 'WordRep 'LiftedRep where
+instance Bind 'WordRep LiftedRep where
   {-# inline (>>=) #-}
   {-# inline (>>) #-}
   (>>=) = bindWordParser
   (>>) = sequenceWordParser
 
-instance Bind 'IntRep 'LiftedRep where
+instance Bind 'IntRep LiftedRep where
   {-# inline (>>=) #-}
   {-# inline (>>) #-}
   (>>=) = bindIntParser
   (>>) = sequenceIntParser
 
-instance Bind ('TupleRep '[ 'IntRep, 'IntRep]) 'LiftedRep where
+instance Bind ('TupleRep '[ 'IntRep, 'IntRep]) LiftedRep where
   {-# inline (>>=) #-}
   {-# inline (>>) #-}
   (>>=) = bindIntPairParser
@@ -213,15 +220,15 @@
 
 
 instance Bind ('TupleRep '[ 'IntRep, 'IntRep])
-              ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]) 
+              ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])
   where
   {-# inline (>>=) #-}
   {-# inline (>>) #-}
   (>>=) = bindInt2to5Parser
   (>>) = sequenceInt2to5Parser
 
-instance Bind ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]) 
-              'LiftedRep
+instance Bind ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])
+              LiftedRep
   where
   {-# inline (>>=) #-}
   {-# inline (>>) #-}
@@ -230,21 +237,21 @@
 
 
 instance Bind 'IntRep
-              ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]) 
+              ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])
   where
   {-# inline (>>=) #-}
   {-# inline (>>) #-}
   (>>=) = bindFromIntToInt5
   (>>) = sequenceIntToInt5
 
-instance Bind 'LiftedRep ('TupleRep '[ 'IntRep, 'IntRep]) where
+instance Bind LiftedRep ('TupleRep '[ 'IntRep, 'IntRep]) where
   {-# inline (>>=) #-}
   {-# inline (>>) #-}
   (>>=) = bindFromLiftedToIntPair
   (>>) = sequenceLiftedToIntPair
 
-instance Bind 'LiftedRep
-              ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]) 
+instance Bind LiftedRep
+              ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])
   where
   {-# inline (>>=) #-}
   {-# inline (>>) #-}
@@ -257,13 +264,13 @@
   (>>=) = bindFromIntToIntPair
   (>>) = sequenceIntToIntPair
 
-instance Bind 'LiftedRep 'IntRep where
+instance Bind LiftedRep 'IntRep where
   {-# inline (>>=) #-}
   {-# inline (>>) #-}
   (>>=) = bindFromLiftedToInt
   (>>) = sequenceLiftedToInt
 
-instance Pure 'LiftedRep where
+instance Pure LiftedRep where
   {-# inline pure #-}
   pure = pureParser
 
@@ -343,7 +350,7 @@
 
 bindFromLiftedToIntPair ::
      forall s e
-       (a :: TYPE 'LiftedRep)
+       (a :: TYPE LiftedRep)
        (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep ])).
      Parser s e a
   -> (a -> Parser s e b)
@@ -359,7 +366,7 @@
 
 sequenceLiftedToIntPair ::
      forall s e
-       (a :: TYPE 'LiftedRep)
+       (a :: TYPE LiftedRep)
        (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep ])).
      Parser s e a
   -> Parser s e b
@@ -375,7 +382,7 @@
 
 bindFromLiftedToInt5 ::
      forall s e
-       (a :: TYPE 'LiftedRep)
+       (a :: TYPE LiftedRep)
        (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])).
      Parser s e a
   -> (a -> Parser s e b)
@@ -391,7 +398,7 @@
 
 sequenceLiftedToInt5 ::
      forall s e
-       (a :: TYPE 'LiftedRep)
+       (a :: TYPE LiftedRep)
        (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep ])).
      Parser s e a
   -> Parser s e b
@@ -406,7 +413,7 @@
 
 bindFromLiftedToInt ::
      forall s e
-       (a :: TYPE 'LiftedRep)
+       (a :: TYPE LiftedRep)
        (b :: TYPE 'IntRep).
      Parser s e a
   -> (a -> Parser s e b)
@@ -422,7 +429,7 @@
 
 sequenceLiftedToInt ::
      forall s e
-       (a :: TYPE 'LiftedRep)
+       (a :: TYPE LiftedRep)
        (b :: TYPE 'IntRep).
      Parser s e a
   -> Parser s e b
diff --git a/src/Data/Bytes/Parser/Utf8.hs b/src/Data/Bytes/Parser/Utf8.hs
--- a/src/Data/Bytes/Parser/Utf8.hs
+++ b/src/Data/Bytes/Parser/Utf8.hs
@@ -15,6 +15,7 @@
 {-# language TypeApplications #-}
 {-# language UnboxedSums #-}
 {-# language UnboxedTuples #-}
+{-# language CPP #-}
 
 -- | Parse input as UTF-8-encoded text. Parsers in this module will
 -- fail if they encounter a byte above @0x7F@.
@@ -46,8 +47,12 @@
   (\(# arr, off, len #) s0 -> case len ># 0# of
     1# ->
       let !w0 = Exts.indexWord8Array# arr off
-       in if | oneByteChar (W8# w0) -> 
-                 (# s0, (# | (# chr# (Exts.word2Int# w0), off +# 1#, len -# 1# #) #) #)
+       in if | oneByteChar (W8# w0) ->
+                 (# s0, (# | (# chr# (Exts.word2Int# (
+#if MIN_VERSION_base(4,16,0)
+                 Exts.word8ToWord#
+#endif
+                 w0)), off +# 1#, len -# 1# #) #) #)
              | twoByteChar (W8# w0) ->
                  if | I# len > 1
                     , w1 <- Exts.indexWord8Array# arr (off +# 1#)
@@ -81,9 +86,9 @@
 codepointFromFourBytes w1 w2 w3 w4 = C#
   ( chr#
     ( unI $ fromIntegral
-      ( unsafeShiftL (word8ToWord w1 .&. 0b00001111) 18 .|. 
-        unsafeShiftL (word8ToWord w2 .&. 0b00111111) 12 .|. 
-        unsafeShiftL (word8ToWord w3 .&. 0b00111111) 6 .|. 
+      ( unsafeShiftL (word8ToWord w1 .&. 0b00001111) 18 .|.
+        unsafeShiftL (word8ToWord w2 .&. 0b00111111) 12 .|.
+        unsafeShiftL (word8ToWord w3 .&. 0b00111111) 6 .|.
         (word8ToWord w4 .&. 0b00111111)
       )
     )
@@ -93,8 +98,8 @@
 codepointFromThreeBytes w1 w2 w3 = C#
   ( chr#
     ( unI $ fromIntegral
-      ( unsafeShiftL (word8ToWord w1 .&. 0b00001111) 12 .|. 
-        unsafeShiftL (word8ToWord w2 .&. 0b00111111) 6 .|. 
+      ( unsafeShiftL (word8ToWord w1 .&. 0b00001111) 12 .|.
+        unsafeShiftL (word8ToWord w2 .&. 0b00111111) 6 .|.
         (word8ToWord w3 .&. 0b00111111)
       )
     )
@@ -104,7 +109,7 @@
 codepointFromTwoBytes w1 w2 = C#
   ( chr#
     ( unI $ fromIntegral @Word @Int
-      ( unsafeShiftL (word8ToWord w1 .&. 0b00011111) 6 .|. 
+      ( unsafeShiftL (word8ToWord w1 .&. 0b00011111) 6 .|.
         (word8ToWord w2 .&. 0b00111111)
       )
     )
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,5 +1,6 @@
 {-# language BangPatterns #-}
 {-# language DataKinds #-}
+{-# language MagicHash #-}
 {-# language MultiWayIf #-}
 {-# language NumDecimals #-}
 {-# language OverloadedStrings #-}
@@ -111,6 +112,13 @@
       P.parseBytes (replicateM (length xs) (BigEndian.word128 ())) bs
       ===
       P.parseBytes (fmap Exts.toList (BigEndian.word128Array () (length xs))) bs
+  , testProperty "cstring" $ \(xs :: [Word8]) ->
+      let ys = Exts.fromList xs
+          bs = Bytes.singleton 0x31 <> ys
+       in
+      P.parseBytes (P.cstring () (Exts.Ptr "1"# ) *> P.bytes () ys *> pure 42) bs
+      ===
+      (P.Success (Slice (Bytes.length ys + 1) 0 42) :: P.Result () Integer)
   , testCase "big-endian-word256" $
       P.parseBytesMaybe (BigEndian.word256Array () 1) (Exts.fromList [
         0x12, 0x34, 0x56, 0x78, 0x90,
