diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Revision history for bytesmith
 
+## 0.3.11.0 -- 2024-01-05
+
+* Add `Data.Bytes.Parser.Latin.hexWord32`.
+* Add `Data.Bytes.Parser.Latin.hexFixedWord(128|256)`.
+* Add `Data.Bytes.Parser.takeN`.
+
 ## 0.3.10.0 -- 2023-07-25
 
 * Add `mapErrorEffectfully`.
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.10.0
+version: 0.3.11.0
 synopsis: Nonresumable byte parser
 description:
   Parse bytes as fast as possible. This is a nonresumable parser
@@ -34,12 +34,13 @@
     Data.Bytes.Parser.Types
   build-depends:
     , base >=4.12 && <5
-    , bytestring >=0.10.8 && <=0.12
     , byteslice >=0.2.6 && <0.3
+    , bytestring >=0.10.8 && <=0.12
     , contiguous >= 0.6 && < 0.7
-    , primitive >=0.7 && <0.9
-    , text-short >=0.1.3 && <0.2
+    , natural-arithmetic >=0.1.3
+    , primitive >=0.7 && <0.10
     , run-st >=0.1 && <0.2
+    , text-short >=0.1.3 && <0.2
     , wide-word >=0.1.0.9 && <0.2
   hs-source-dirs: src
   ghc-options: -O2 -Wall
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
@@ -37,6 +37,7 @@
   , any
     -- * Many Bytes
   , take
+  , takeN
   , takeUpTo
   , takeWhile
   , takeTrailedBy
@@ -108,14 +109,16 @@
 import Data.Bytes.Parser.Internal (uneffectful#,uneffectfulInt#)
 import Data.Bytes.Parser.Types (Result(Failure,Success),Slice(Slice))
 import Data.Bytes.Parser.Unsafe (unconsume,expose,cursor)
-import Data.Bytes.Types (Bytes(..))
+import Data.Bytes.Types (Bytes(..),BytesN(BytesN))
 import Data.Primitive (ByteArray(..))
+import Data.Primitive.Contiguous (Contiguous,Element)
 import Foreign.C.String (CString)
 import GHC.Exts (Int(I#),Word#,Int#,Char#,runRW#,(+#),(-#),(>=#))
 import GHC.ST (ST(..))
 import GHC.Word (Word32(W32#),Word8)
-import Data.Primitive.Contiguous (Contiguous,Element)
 
+import qualified Arithmetic.Nat as Nat
+import qualified Arithmetic.Types as Arithmetic
 import qualified Data.Bytes as B
 import qualified Data.Bytes.Parser.Internal as Internal
 import qualified Data.Primitive as PM
@@ -422,6 +425,17 @@
   then case B.unsafeTake n chunk of
     bs -> Internal.Success bs (offset chunk + n) (length chunk - n)
   else Internal.Failure e
+
+-- | Variant of 'take' that tracks the length of the result in the result type.
+takeN :: e -> Arithmetic.Nat n -> Parser e s (BytesN n)
+takeN e n0 = uneffectful $ \chunk -> if n <= B.length chunk
+  then case B.unsafeTake n chunk of
+    Bytes theChunk theOff _ -> Internal.Success (BytesN theChunk theOff) (offset chunk + n) (length chunk - n)
+  else Internal.Failure e
+  where
+  !n = Nat.demote n0
+
+
 
 -- | Take at most the given number of bytes. This is greedy. It will
 --   consume as many bytes as there are available until it has consumed
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
@@ -80,11 +80,14 @@
     -- *** Variable Length
   , hexWord8
   , hexWord16
+  , hexWord32
     -- *** Fixed Length
   , hexFixedWord8
   , hexFixedWord16
   , hexFixedWord32
   , hexFixedWord64
+  , hexFixedWord128
+  , hexFixedWord256
     -- *** Digit
   , hexNibbleLower
   , tryHexNibbleLower
@@ -97,14 +100,15 @@
 import Data.Bits ((.|.))
 import Data.Bytes.Types (Bytes(..))
 import Data.Bytes.Parser.Internal (InternalStep(..),unfailing)
+import Data.Bytes.Parser (bindFromLiftedToInt,isEndOfInput,endOfInput)
 import Data.Bytes.Parser.Internal (Parser(..),ST#,uneffectful,Result#,uneffectful#)
 import Data.Bytes.Parser.Internal (Result(..),indexLatinCharArray,upcastUnitSuccess)
 import Data.Bytes.Parser.Internal (boxBytes)
-import Data.Bytes.Parser (bindFromLiftedToInt,isEndOfInput,endOfInput)
 import Data.Bytes.Parser.Unsafe (expose,cursor,unconsume)
-import Data.Word (Word8)
 import Data.Char (ord)
 import Data.Kind (Type)
+import Data.WideWord (Word256(Word256),Word128(Word128))
+import Data.Word (Word8)
 import GHC.Exts (Int(I#),Char(C#),Word#,Int#,Char#,(+#),(-#),indexCharArray#)
 import GHC.Exts (TYPE,RuntimeRep,int2Word#,or#)
 import GHC.Exts (ltWord#,gtWord#,notI#)
@@ -473,6 +477,12 @@
     (# s1, r #) -> (# s1, upcastWord16Result r #)
   )
 
+hexWord32 :: e -> Parser e s Word32
+hexWord32 e = Parser
+  (\chunk0 s0 -> case hexSmallWordStart e 4294967296 (boxBytes chunk0) s0 of
+    (# s1, r #) -> (# s1, upcastWord32Result r #)
+  )
+
 -- | Parse a decimal-encoded 16-bit word. If the number is larger
 -- than 65535, this parser fails.
 decWord16 :: e -> Parser e s Word16
@@ -1045,6 +1055,18 @@
 #endif
           ), b, c #) #) #)
   )
+
+hexFixedWord128 :: e -> Parser e s Word128
+hexFixedWord128 e = Word128
+  <$> hexFixedWord64 e
+  <*> hexFixedWord64 e
+
+hexFixedWord256 :: e -> Parser e s Word256
+hexFixedWord256 e = Word256
+  <$> hexFixedWord64 e
+  <*> hexFixedWord64 e
+  <*> hexFixedWord64 e
+  <*> hexFixedWord64 e
 
 hexFixedWord64# :: e -> Parser e s Word#
 {-# noinline hexFixedWord64# #-}
