diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Revision history for byteslice
 
+## 0.2.11.0 -- 2023-07-25
+
+* Add `Data.Bytes.Encode.LittleEndian`.
+* Add `splitTetragram1`.
+
 ## 0.2.10.0 -- 2023-05-01
 
 * Add `equals13`, `equals14`, `equals15`.
diff --git a/byteslice.cabal b/byteslice.cabal
--- a/byteslice.cabal
+++ b/byteslice.cabal
@@ -1,6 +1,6 @@
 cabal-version: 2.4
 name: byteslice
-version: 0.2.10.0
+version: 0.2.11.0
 synopsis: Slicing managed and unmanaged memory
 description:
   This library provides types that allow the user to talk about a slice of
@@ -26,6 +26,7 @@
     Data.Bytes
     Data.Bytes.Chunks
     Data.Bytes.Encode.BigEndian
+    Data.Bytes.Encode.LittleEndian
     Data.Bytes.Internal
     Data.Bytes.Mutable
     Data.Bytes.Text.Ascii
@@ -45,9 +46,10 @@
   build-depends:
     , base >=4.14 && <5
     , bytestring >=0.10.8 && <0.12
+    , natural-arithmetic >=0.1.4
     , primitive >=0.7.4 && <0.10
-    , primitive-unlifted >=0.1.2 && <0.2
     , primitive-addr >=0.1 && <0.2
+    , primitive-unlifted >=0.1.2 && <2.2
     , run-st >=0.1.1 && <0.2
     , text >=1.2.5
     , text-short >=0.1.3 && <0.2
diff --git a/src/Data/Bytes.hs b/src/Data/Bytes.hs
--- a/src/Data/Bytes.hs
+++ b/src/Data/Bytes.hs
@@ -1,6 +1,7 @@
 {-# language BangPatterns #-}
 {-# language BlockArguments #-}
 {-# language DuplicateRecordFields #-}
+{-# language KindSignatures #-}
 {-# language MagicHash #-}
 {-# language NamedFieldPuns #-}
 {-# language RankNTypes #-}
@@ -72,6 +73,7 @@
   , Byte.splitStream
     -- ** Fixed from Beginning
   , Byte.split1
+  , splitTetragram1
   , Byte.split2
   , Byte.split3
   , Byte.split4
@@ -85,6 +87,7 @@
     -- * Searching
   , replace
   , findIndices
+  , findTetragramIndex
     -- * Counting
   , Byte.count
     -- * Prefix and Suffix
@@ -158,6 +161,9 @@
     -- * Unlifted Types
   , lift
   , unlift
+    -- * Length Indexed
+  , withLength
+  , withLengthU
   ) where
 
 import Prelude hiding (length,takeWhile,dropWhile,null,foldl,foldr,elem,replicate,any,all,readFile,map)
@@ -165,20 +171,24 @@
 import Control.Monad.Primitive (PrimMonad,primitive_,unsafeIOToPrim)
 import Control.Monad.ST.Run (runByteArrayST)
 import Cstrlen (cstringLength#)
+import Data.Bits (unsafeShiftL,(.|.))
 import Data.ByteString.Short.Internal (ShortByteString(SBS))
 import Data.Bytes.Pure (length,fromByteArray,foldr,unsafeDrop)
 import Data.Bytes.Pure (unsafeIndex,toShortByteString)
-import Data.Bytes.Types (Bytes(Bytes,array,offset))
+import Data.Bytes.Types (Bytes(Bytes,array,offset),BytesN(BytesN))
+import Data.Bytes.Types (ByteArrayN(ByteArrayN))
 import Data.Primitive (Array,ByteArray(ByteArray))
 import Data.Text.Short (ShortText)
 import Foreign.C.String (CString)
 import Foreign.Ptr (Ptr,plusPtr,castPtr)
 import GHC.Exts (Addr#,Word#,Int#)
 import GHC.Exts (Int(I#),Ptr(Ptr))
-import GHC.Word (Word8(W8#))
+import GHC.Word (Word8(W8#),Word32)
 import Reps (Bytes#(..),word8ToWord#)
 import Data.Bytes.Search (findIndices,replace,isInfixOf)
 
+import qualified Arithmetic.Nat as Nat
+import qualified Arithmetic.Types as Arithmetic
 import qualified Data.Bytes.Byte as Byte
 import qualified Data.Bytes.Chunks as Chunks
 import qualified Data.Bytes.IO as BIO
@@ -193,6 +203,7 @@
 import qualified Data.Primitive.Ptr as PM
 import qualified Data.Text.Short as TS
 import qualified GHC.Exts as Exts
+import qualified GHC.TypeNats as GHC
 
 -- | Extract the head and tail of the 'Bytes', returning 'Nothing' if
 -- it is empty.
@@ -671,3 +682,76 @@
 concatArray :: Array Bytes -> Bytes
 {-# inline concatArray #-}
 concatArray !xs = Pure.fromByteArray (concatArrayU xs)
+
+-- | Convert 'Bytes' to 'BytesN', exposing the length in a type-safe
+-- way in the callback.
+withLength ::
+     Bytes
+  -> (forall (n :: GHC.Nat). Arithmetic.Nat n -> BytesN n -> a)
+  -> a
+{-# inline withLength #-}
+withLength Bytes{array,offset,length=len} f = Nat.with
+  len
+  (\n -> f n BytesN{array,offset})
+
+withLengthU ::
+     ByteArray
+  -> (forall (n :: GHC.Nat). Arithmetic.Nat n -> ByteArrayN n -> a)
+  -> a
+{-# inline withLengthU #-}
+withLengthU !arr f = Nat.with
+  (PM.sizeofByteArray arr)
+  (\n -> f n (ByteArrayN arr))
+
+findTetragramIndex ::
+     Word8
+  -> Word8
+  -> Word8
+  -> Word8
+  -> Bytes
+  -> Maybe Int
+findTetragramIndex !w0 !w1 !w2 !w3 (Bytes arr off len) = if len < 4
+  then Nothing
+  else
+    let !target = 
+          unsafeShiftL (fromIntegral w0 :: Word32) 24
+          .|.
+          unsafeShiftL (fromIntegral w1 :: Word32) 16
+          .|.
+          unsafeShiftL (fromIntegral w2 :: Word32) 8
+          .|.
+          unsafeShiftL (fromIntegral w3 :: Word32) 0
+        !end = off + len
+        go !ix !acc = if acc == target
+          then
+            let n = ix - off
+             in Just (n - 4)
+          else if ix < end
+            then
+              let !w = PM.indexByteArray arr ix :: Word8
+                  acc' =
+                    (fromIntegral w :: Word32)
+                    .|.
+                    unsafeShiftL acc 8
+               in go (ix + 1) acc'
+            else Nothing
+        !acc0 =
+          unsafeShiftL (fromIntegral (PM.indexByteArray arr 0 :: Word8) :: Word32) 24
+          .|.
+          unsafeShiftL (fromIntegral (PM.indexByteArray arr 1 :: Word8) :: Word32) 16
+          .|.
+          unsafeShiftL (fromIntegral (PM.indexByteArray arr 2 :: Word8) :: Word32) 8
+          .|.
+          unsafeShiftL (fromIntegral (PM.indexByteArray arr 3 :: Word8) :: Word32) 0
+     in go 4 acc0
+
+splitTetragram1 ::
+     Word8
+  -> Word8
+  -> Word8
+  -> Word8
+  -> Bytes
+  -> Maybe (Bytes,Bytes)
+splitTetragram1 !w0 !w1 !w2 !w3 !b = case findTetragramIndex w0 w1 w2 w3 b of
+  Nothing -> Nothing
+  Just n -> Just (Pure.unsafeTake n b, Pure.unsafeDrop (n + 4) b)
diff --git a/src/Data/Bytes/Chunks.hs b/src/Data/Bytes/Chunks.hs
--- a/src/Data/Bytes/Chunks.hs
+++ b/src/Data/Bytes/Chunks.hs
@@ -47,7 +47,7 @@
   , writeFile
   ) where
 
-import Prelude hiding (length,concat,reverse,readFile,writeFile,null)
+import Prelude hiding (Foldable(..),concat,reverse,readFile,writeFile)
 
 import Control.Exception (IOException,catch)
 import Control.Monad.ST.Run (runIntByteArrayST)
diff --git a/src/Data/Bytes/Encode/LittleEndian.hs b/src/Data/Bytes/Encode/LittleEndian.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Bytes/Encode/LittleEndian.hs
@@ -0,0 +1,77 @@
+{-# language BangPatterns #-}
+{-# language TypeApplications #-}
+
+module Data.Bytes.Encode.LittleEndian
+  ( word16
+  , word32
+  , word64
+  , int16
+  , int32
+  , int64
+  ) where
+
+import Control.Monad.ST.Run (runByteArrayST)
+import Data.Bits (unsafeShiftR)
+import Data.Bytes.Types (Bytes)
+import Data.Int (Int16,Int32,Int64)
+import Data.Primitive (ByteArray)
+import Data.Word (Word8,Word16,Word32,Word64)
+
+import qualified Data.Bytes.Pure as Pure
+import qualified Data.Primitive as PM
+
+-- | Encode a 32-bit signed integer as 4 bytes.
+int32 :: Int32 -> Bytes
+{-# inline int32 #-}
+int32 = word32 . fromIntegral @Int32 @Word32 
+
+-- | Encode a 32-bit unsigned integer as 4 bytes.
+word32 :: Word32 -> Bytes
+word32 !w = Pure.fromByteArray (word32U w)
+
+word32U :: Word32 -> ByteArray
+word32U !w = runByteArrayST $ do
+  arr <- PM.newByteArray 4
+  PM.writeByteArray arr 3 (fromIntegral @Word32 @Word8 (unsafeShiftR w 24))
+  PM.writeByteArray arr 2 (fromIntegral @Word32 @Word8 (unsafeShiftR w 16))
+  PM.writeByteArray arr 1 (fromIntegral @Word32 @Word8 (unsafeShiftR w 8))
+  PM.writeByteArray arr 0 (fromIntegral @Word32 @Word8 w)
+  PM.unsafeFreezeByteArray arr
+
+-- | Encode a 16-bit signed integer as 4 bytes.
+int16 :: Int16 -> Bytes
+{-# inline int16 #-}
+int16 = word16 . fromIntegral @Int16 @Word16 
+
+-- | Encode a 16-bit unsigned integer as 4 bytes.
+word16 :: Word16 -> Bytes
+word16 !w = Pure.fromByteArray (word16U w)
+
+word16U :: Word16 -> ByteArray
+word16U !w = runByteArrayST $ do
+  arr <- PM.newByteArray 2
+  PM.writeByteArray arr 1 (fromIntegral @Word16 @Word8 (unsafeShiftR w 8))
+  PM.writeByteArray arr 0 (fromIntegral @Word16 @Word8 w)
+  PM.unsafeFreezeByteArray arr
+
+-- | Encode a 16-bit signed integer as 4 bytes.
+int64 :: Int64 -> Bytes
+{-# inline int64 #-}
+int64 = word64 . fromIntegral @Int64 @Word64 
+
+-- | Encode a 16-bit unsigned integer as 4 bytes.
+word64 :: Word64 -> Bytes
+word64 !w = Pure.fromByteArray (word64U w)
+
+word64U :: Word64 -> ByteArray
+word64U !w = runByteArrayST $ do
+  arr <- PM.newByteArray 8
+  PM.writeByteArray arr 7 (fromIntegral @Word64 @Word8 (unsafeShiftR w 56))
+  PM.writeByteArray arr 6 (fromIntegral @Word64 @Word8 (unsafeShiftR w 48))
+  PM.writeByteArray arr 5 (fromIntegral @Word64 @Word8 (unsafeShiftR w 40))
+  PM.writeByteArray arr 4 (fromIntegral @Word64 @Word8 (unsafeShiftR w 32))
+  PM.writeByteArray arr 3 (fromIntegral @Word64 @Word8 (unsafeShiftR w 24))
+  PM.writeByteArray arr 2 (fromIntegral @Word64 @Word8 (unsafeShiftR w 16))
+  PM.writeByteArray arr 1 (fromIntegral @Word64 @Word8 (unsafeShiftR w 8))
+  PM.writeByteArray arr 0 (fromIntegral @Word64 @Word8 w)
+  PM.unsafeFreezeByteArray arr
diff --git a/src/Data/Bytes/Pure.hs b/src/Data/Bytes/Pure.hs
--- a/src/Data/Bytes/Pure.hs
+++ b/src/Data/Bytes/Pure.hs
@@ -41,7 +41,7 @@
   , toShortByteString
   ) where
 
-import Prelude hiding (length,foldl,foldr,map,null)
+import Prelude hiding (Foldable(..),map)
 
 import Control.Monad.Primitive (PrimState,PrimMonad)
 import Control.Monad.ST.Run (runByteArrayST)
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -265,6 +265,34 @@
           Nothing -> property False
           Just (y2,z2) -> (y1,z1) === (bytesToByteString y2, bytesToByteString z2)
         _ -> property Discard
+  , testProperty "splitTetragram1A" $ \(w0 :: Word8) (w1 :: Word8) (w2 :: Word8) (w3 :: Word8) (xs :: [Word8]) ->
+      (w0 /= 0xEF && w1 /= 0xEF && w2 /= 0xEF && w3 /= 0xEF)
+      ==>
+      case Bytes.splitTetragram1 w0 w1 w2 w3 (slicedPack (0xEF : w0 : w1 : w2 : w3 : xs)) of
+        Nothing -> property False
+        Just (pre,post) ->
+          (pre,post) === (Exts.fromList [0xEF], Exts.fromList xs) 
+  , testProperty "splitTetragram1B" $ \(w0 :: Word8) (w1 :: Word8) (w2 :: Word8) (w3 :: Word8) (xs :: [Word8]) ->
+      (w0 /= 0xEF && w1 /= 0xEF && w2 /= 0xEF && w3 /= 0xEF)
+      ==>
+      case Bytes.splitTetragram1 w0 w1 w2 w3 (slicedPack (0xEF : 0xEF : 0xEF : 0xEF : w0 : w1 : w2 : w3 : xs)) of
+        Nothing -> property False
+        Just (pre,post) ->
+          (pre,post) === (Exts.fromList (List.replicate 4 0xEF), Exts.fromList xs) 
+  , testProperty "splitTetragram1C" $ \(w0 :: Word8) (w1 :: Word8) (w2 :: Word8) (xs :: [Word8]) ->
+      (w0 /= 0xEF && w1 /= 0xEF && w2 /= 0xEF)
+      ==>
+      case Bytes.splitTetragram1 w0 w1 w2 0xEF (slicedPack (xs ++ [w0, w1, w2, 0xEF])) of
+        Nothing -> property False
+        Just (pre,post) ->
+          (pre,post) === (Exts.fromList xs, mempty)
+  , testProperty "splitTetragram1D" $ \(w0 :: Word8) (w1 :: Word8) (w2 :: Word8) (xs :: [Word8]) ->
+      (w0 /= 0xEF && w1 /= 0xEF && w2 /= 0xEF)
+      ==>
+      case Bytes.splitTetragram1 w0 w1 w2 0xEF (slicedPack (xs ++ [w0, w1, w2, 0xEF, 0xEF])) of
+        Nothing -> property False
+        Just (pre,post) ->
+          (pre,post) === (Exts.fromList xs, Exts.fromList [0xEF])
   , testProperty "split2" $ \(xs :: [Word8]) (ys :: [Word8]) (zs :: [Word8]) ->
       (all (/=0xEF) xs && all (/=0xEF) ys && all (/=0xEF) zs)
       ==>
