### O(1) Slicing: take, drop, splitAt Source: https://context7.com/haskell/bytestring/llms.txt Perform efficient O(1) slicing operations like `take`, `drop`, `takeEnd`, `dropEnd`, and `splitAt`. These operations return new ByteStrings that point to the original buffer without copying. The `windows` example demonstrates creating sliding windows over a ByteString. ```haskell import qualified Data.ByteString as B main :: IO () main = do let bs = B.pack [0..9] -- [0,1,2,3,4,5,6,7,8,9] print (B.take 3 bs) -- "\NUL\SOH\STX" print (B.drop 7 bs) -- "\a\b\t" print (B.takeEnd 2 bs) -- "\b\t" print (B.dropEnd 2 bs) -- "\NUL\SOH\STX\ETX\EOT\ENQ\ACK\a" let (prefix, suffix) = B.splitAt 5 bs print (B.length prefix) -- 5 print (B.length suffix) -- 5 -- Slide a window of size 3 let windows n b = [B.take n (B.drop i b) | i <- [0 .. B.length b - n]] print (map B.unpack (windows 3 (B.pack [1..5]))) -- [[1,2,3],[2,3,4],[3,4,5]] ``` -------------------------------- ### O(1) Size Queries: length and null Source: https://context7.com/haskell/bytestring/llms.txt Use `length` to get the number of bytes and `null` to check if a ByteString is empty. Both operations are O(1) because the length is stored with the ByteString. The guard pattern example shows a safe way to handle potentially empty ByteStrings. ```haskell import qualified Data.ByteString as B main :: IO () main = do let bs = B.pack [1..100] print (B.length bs) -- 100 print (B.null bs) -- False print (B.null B.empty) -- True -- Guard pattern let safeHead b = if B.null b then Nothing else Just (B.head b) print (safeHead bs) -- Just 1 print (safeHead B.empty) -- Nothing ``` -------------------------------- ### Substring Predicates: `isPrefixOf`, `isSuffixOf`, `isInfixOf`, `stripPrefix` Source: https://context7.com/haskell/bytestring/llms.txt Efficiently check if a ByteString starts with (`isPrefixOf`), ends with (`isSuffixOf`), or contains (`isInfixOf`) another. `stripPrefix` safely removes a prefix, returning `Nothing` if it's not present. ```haskell import qualified Data.ByteString as B main :: IO () main = do let url = B.pack (map fromIntegral "https://example.com/path") let https = B.pack (map fromIntegral "https://") let com = B.pack (map fromIntegral ".com") print (B.isPrefixOf https url) -- True print (B.isSuffixOf com url) -- False (ends in /path) print (B.isInfixOf com url) -- True -- Strip prefix safely case B.stripPrefix https url of Just rest -> print (B.unpack rest) -- "example.com/path" Nothing -> putStrLn "not https" ``` -------------------------------- ### Encode Packet with Big-Endian Integers Source: https://context7.com/haskell/bytestring/llms.txt Use `word16BE`, `word32BE`, and `word8` to construct a `ByteString.Builder` for serializing a record with big-endian encoded integers. The example demonstrates encoding a `Packet` data type. ```haskell import Data.ByteString.Builder import qualified Data.ByteString.Lazy as L -- Binary serialization of a record data Packet = Packet { pMagic :: Word16 -- big-endian , pLength :: Word32 -- big-endian , pPayload :: [Word8] } encodePacket :: Packet -> L.ByteString encodePacket p = toLazyByteString $ word16BE (pMagic p) <> word32BE (pLength p) <> mconcat (map word8 (pPayload p)) main :: IO () main = do let pkt = Packet 0xDEAD 5 [1,2,3,4,5] let bs = encodePacket pkt print (L.unpack bs) -- [222,173,0,0,0,5,1,2,3,4,5] -- Host-endian (from Data.ByteString.Builder.Extra) import Data.ByteString.Builder.Extra (intHost, word64Host) -- let nativeInt = toLazyByteString (intHost 42) return () ``` -------------------------------- ### Predicate-based Splitting: span, break, spanEnd, breakEnd Source: https://context7.com/haskell/bytestring/llms.txt Split ByteStrings based on predicates using `span` (take while true), `break` (take until true), `spanEnd`, and `breakEnd`. These operations are O(n). `span p` is equivalent to `(takeWhile p, dropWhile p)`, and `break p` is `span (not . p)`. The `isDigit` example shows how to extract leading and trailing digits. ```haskell import qualified Data.ByteString as B import Data.Word (Word8) isDigit :: Word8 -> Bool isDigit w = w >= 48 && w <= 57 main :: IO () main = do let bs = B.pack (map fromIntegral "123abc456") -- span: collect leading digits let (digits, rest) = B.span isDigit bs print (B.unpack digits) -- [49,50,51] ("123") print (B.unpack rest) -- [97,98,99,52,53,54] ("abc456") -- break: split at first non-digit let (pre, post) = B.break (not . isDigit) bs print (B.unpack pre) -- [49,50,51] -- spanEnd: collect trailing digits let (body, trail) = B.spanEnd isDigit bs print (B.unpack trail) -- [52,53,54] ("456") ``` -------------------------------- ### File and Handle I/O with ByteString Source: https://context7.com/haskell/bytestring/llms.txt Demonstrates binary file operations using `writeFile`, `readFile`, and `appendFile`. Also shows handle-based I/O and basic error handling with `try`. ```haskell import qualified Data.ByteString as B import System.IO (withBinaryFile, IOMode(..)) import Control.Exception (try, SomeException) main :: IO () main = do -- Write binary data B.writeFile "/tmp/test.bin" (B.pack [0x00..0xFF]) -- Read it back bs <- B.readFile "/tmp/test.bin" print (B.length bs) -- 256 print (B.head bs, B.last bs) -- (0,255) -- Append B.appendFile "/tmp/test.bin" (B.replicate 10 0xAA) bs2 <- B.readFile "/tmp/test.bin" print (B.length bs2) -- 266 -- Handle-based reading with error handling result <- try (B.readFile "/nonexistent") :: IO (Either SomeException B.ByteString) case result of Left err -> putStrLn ("Error: " ++ show err) Right _ -> putStrLn "ok" ``` -------------------------------- ### Fine-tuned Builder Execution Source: https://context7.com/haskell/bytestring/llms.txt Explains how to control builder execution using `safeStrategy` and `untrimmedStrategy` for buffer management and `flush` for chunk boundaries. Appending to a suffix is also shown. ```haskell import Data.ByteString.Builder import Data.ByteString.Builder.Extra import qualified Data.ByteString.Lazy as L main :: IO () main = do let b = mconcat [stringUtf8 "chunk" <> intDec i <> flush | i <- [1..5 :: Int]] -- safeStrategy: first buffer 128 bytes, subsequent buffers 1024 bytes -- trims each chunk to exactly the filled amount let lbs1 = toLazyByteStringWith (safeStrategy 128 1024) L.empty b print (length (L.toChunks lbs1)) -- 5 or more chunks (one per flush) -- untrimmedStrategy: no trimming (faster, slightly wasteful) let lbs2 = toLazyByteStringWith (untrimmedStrategy 128 1024) L.empty b print (L.toStrict lbs1 == L.toStrict lbs2) -- True (same bytes) -- Append to a suffix let lbs3 = toLazyByteStringWith (safeStrategy 256 4096) (L.singleton 0) b print (L.last lbs3) -- 0 ``` -------------------------------- ### singleton / empty Source: https://context7.com/haskell/bytestring/llms.txt Constructs minimal ByteStrings. `singleton` creates a one-byte ByteString in O(1), and `empty` represents a zero-length ByteString. ```APIDOC ## singleton / empty — Constructing minimal ByteStrings `singleton` converts one `Word8` into a one-byte `ByteString` in O(1) by slicing a static allocation. `empty` is the zero-length `ByteString` and acts as the `mempty` of the `Monoid` instance. ```haskell import qualified Data.ByteString as B import Data.Word (Word8) main :: IO () main = do let zero = B.singleton 0x00 let ff = B.singleton 0xFF print (B.length zero) -- 1 print (B.length B.empty) -- 0 -- Monoid concatenation let combined = zero <> B.pack [0x01, 0x02] <> ff print combined -- "\NUL\SOH\STX\255" print (B.length combined) -- 4 ``` ``` -------------------------------- ### Builder Execution Source: https://context7.com/haskell/bytestring/llms.txt Explains how to execute a Builder to produce a lazy ByteString or write directly to a Handle. Demonstrates efficient concatenation. ```APIDOC ## `Data.ByteString.Builder` — Efficient incremental construction ### `toLazyByteString` / `hPutBuilder` / `writeFile` — Builder execution A `Builder` accumulates byte sequences with O(1) `<>`. Execute it with `toLazyByteString` (returns a lazy `ByteString`) or `hPutBuilder` (writes directly to a `Handle` without an intermediate lazy `ByteString`). ### Example Usage: ```haskell import Data.ByteString.Builder import qualified Data.ByteString.Lazy as L main :: IO () main = do -- Build a simple binary frame: 4-byte length + payload let payload = byteString (mconcat (map (\i -> toEnum i) [65..90])) -- Actually, use stringUtf8: let msg = stringUtf8 "Hello, world!" let frame = int32BE (fromIntegral (L.length (toLazyByteString msg))) <> msg let result = toLazyByteString frame print (L.length result) -- 4 + 13 = 17 -- Write directly to a file (no intermediate lazy ByteString allocated) writeFile "/tmp/frame.bin" frame -- Concatenation is O(1) let manyChunks = mconcat [word8 (fromIntegral i) | i <- [0..255 :: Int]] print (L.length (toLazyByteString manyChunks)) -- 256 ``` ``` -------------------------------- ### C FFI Integration with ByteString Source: https://context7.com/haskell/bytestring/llms.txt Shows how to integrate ByteStrings with C functions using `useAsCString` for passing to C and `packCString`/`packCStringLen` for receiving from C. `useAsCString` is scoped to the callback. ```haskell import qualified Data.ByteString as B import Foreign.C.String (withCString, peekCString) main :: IO () main = do let bs = B.pack (map fromIntegral "hello\0world") -- Pass to a C function expecting CString B.useAsCString bs $ \cstr -> do str <- peekCString cstr -- reads until first NUL putStrLn str -- "hello" -- Round-trip through CString withCString "from C" $ \cstr -> do bs2 <- B.packCString cstr print bs2 -- "from C" -- With explicit length (may contain NUL bytes) withCString "ab\0cd" $ \cstr -> do bs3 <- B.packCStringLen (cstr, 5) print (B.length bs3) -- 5 ``` -------------------------------- ### Floating-Point Builders Source: https://context7.com/haskell/bytestring/llms.txt Demonstrates building strings from floating-point numbers using `doubleDec`, `floatDec`, and custom formatting options like `standard`, `scientific`, and `generic`. ```haskell import Data.ByteString.Builder import Data.ByteString.Builder.RealFloat import qualified Data.ByteString.Lazy as L main :: IO () main = do print (toLazyByteString (doubleDec 3.141592653589793)) -- "3.141592653589793" print (toLazyByteString (floatDec 1.0e23)) -- "1.0e23" -- Custom format: fixed notation with 4 decimal places print (toLazyByteString (formatDouble (standard 4) 3.14159)) -- "3.1416" -- Scientific notation print (toLazyByteString (formatDouble scientific 0.000123)) -- "1.23e-4" -- Generic (picks shorter of standard/scientific) print (toLazyByteString (formatDouble generic 100000.0)) -- "1.0e5" ``` -------------------------------- ### Compact Unpinned Storage Source: https://context7.com/haskell/bytestring/llms.txt Demonstrates the usage of ShortByteString for compact storage, ideal for keys in maps and storing small strings. Includes conversion functions between strict and short ByteStrings. ```APIDOC ## `toShort` / `fromShort` / `lazyToShort` / `lazyFromShort` — Compact unpinned storage `ShortByteString` wraps `Data.Array.Byte.ByteArray` (unpinned memory). It is ideal for keys in `Map`s or `HashMap`s and for storing many small strings. Use `toShort` / `fromShort` to convert with strict `ByteString`; use `lazyToShort` / `lazyFromShort` for lazy `ByteString` (added in 0.13.0.0). ### Example Usage: ```haskell import qualified Data.ByteString as B import qualified Data.ByteString.Short as SB import qualified Data.Map.Strict as Map main :: IO () main = do -- Convert strict -> short (makes a copy into unpinned memory) let strict = B.pack [72,101,108,108,111] let short = SB.toShort strict print (SB.length short) -- 5 -- Convert back let back = SB.fromShort short print (back == strict) -- True -- Store many small identifiers compactly let ids :: Map.Map SB.ShortByteString Int ids = Map.fromList [ (SB.toShort (B.pack (map fromIntegral "alice")), 1) , (SB.toShort (B.pack (map fromIntegral "bob")), 2) ] print (Map.size ids) -- 2 -- isValidUtf8 also available on ShortByteString print (SB.isValidUtf8 short) -- True ``` ``` -------------------------------- ### Construct minimal ByteStrings with singleton/empty Source: https://context7.com/haskell/bytestring/llms.txt `singleton` creates a one-byte ByteString in O(1) by slicing a static allocation. `empty` represents a zero-length ByteString and is the `mempty` for the `Monoid` instance. Use `Monoid` concatenation for combining ByteStrings. ```haskell import qualified Data.ByteString as B import Data.Word (Word8) main :: IO () main = do let zero = B.singleton 0x00 let ff = B.singleton 0xFF print (B.length zero) -- 1 print (B.length B.empty) -- 0 -- Monoid concatenation let combined = zero <> B.pack [0x01, 0x02] <> ff print combined -- "\NUL\SOH\STX\255" print (B.length combined) -- 4 ``` -------------------------------- ### Efficient Pair-wise Operations with ByteString Source: https://context7.com/haskell/bytestring/llms.txt Illustrates packZipWith for efficient, direct ByteString output from pair-wise operations like XOR encryption, avoiding intermediate lists. Also shows zipWith for creating lists of results. ```haskell import qualified Data.ByteString as B import Data.Bits (xor) main :: IO () main = do let key = B.pack [0xAA, 0xBB, 0xCC, 0xDD] let msg = B.pack [0x01, 0x02, 0x03, 0x04] -- XOR-encrypt: returns ByteString directly (most efficient) let enc = B.packZipWith xor msg key print (B.unpack enc) -- [171,185,207,217] -- Decrypt let dec = B.packZipWith xor enc key print (dec == msg) -- True -- zipWith to list of sums let sums = B.zipWith (\a b -> fromIntegral a + fromIntegral b :: Int) msg key print sums -- [171,189,207,225] ``` -------------------------------- ### Stateful Traversals with ByteString Source: https://context7.com/haskell/bytestring/llms.txt Demonstrates scanl for prefix-reduction and mapAccumL for combining map and fold operations with an accumulator. scanl's output length is input length + 1. ```haskell import qualified Data.ByteString as B import Data.Word (Word8) import Data.Bits (xor) main :: IO () main = do let bs = B.pack [1,2,3,4,5] -- Running XOR from the left (output length = input length + 1) print (B.unpack (B.scanl xor 0 bs)) -- [0,1,3,0,4,1] -- mapAccumL: number bytes with a counter let (finalCount, numbered) = B.mapAccumL (\acc b -> (acc+1, acc)) 0 bs print finalCount -- 5 print (B.unpack numbered) -- [0,1,2,3,4] ``` -------------------------------- ### Splitting ByteStrings with `split`, `splitWith`, `breakSubstring` Source: https://context7.com/haskell/bytestring/llms.txt Use `split` for delimiter-based splitting, `splitWith` for predicate-based splitting, and `breakSubstring` for efficient multi-byte pattern matching. `breakSubstring` returns the portion before and at the first match. ```haskell import qualified Data.ByteString as B main :: IO () main = do -- CSV-style split on comma (0x2C) let csv = B.pack (map fromIntegral "a,bb,ccc,d") let fields = B.split 0x2C csv print (map B.unpack fields) -- [[97],[98,98],[99,99,99],[100]] -- splitWith on any punctuation let isPunct w = w == 0x2C || w == 0x2E || w == 0x21 let tokens = B.splitWith isPunct (B.pack (map fromIntegral "hi,bye.ok!")) mapM_ (print . B.unpack) tokens -- [104,105] [98,121,101] [111,107] [] -- breakSubstring: find HTTP header boundary let sep = B.pack [13,10,13,10] -- "\r\n\r\n" let http = B.pack (map fromIntegral "GET / HTTP/1.1\r\n\r\nBody") let (hdrs, body) = B.breakSubstring sep http print (B.length hdrs) -- 16 ``` -------------------------------- ### Embedding Existing Data into Builder Source: https://context7.com/haskell/bytestring/llms.txt Shows how to embed pre-existing strict, lazy, or short ByteStrings, or single bytes, into a Builder. ```APIDOC ### `byteString` / `lazyByteString` / `shortByteString` / `word8` / `int8` — Embedding existing data Embed a pre-existing strict/lazy/short `ByteString`, or a single `Word8`/`Int8`, into a `Builder`. For large strict `ByteString` payloads consider `byteStringInsert` from `Data.ByteString.Builder.Extra` to avoid copying. ### Example Usage: ```haskell import Data.ByteString.Builder import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Short as SB main :: IO () main = do let header = S.pack [0xCA, 0xFE, 0xBA, 0xBE] let payload = SB.toShort (S.pack [1..8]) let b = byteString header <> word8 0xFF <> shortByteString payload <> int8 (-1) let out = L.toStrict (toLazyByteString b) print (S.length out) -- 4 + 1 + 8 + 1 = 14 print (S.unpack out) -- [202,254,186,190,255,1,2,3,4,5,6,7,8,255] ``` ``` -------------------------------- ### Convert ByteString to ShortByteString and back Source: https://context7.com/haskell/bytestring/llms.txt Use `toShort` to convert a strict `ByteString` to a `ShortByteString` for compact storage in unpinned memory. Use `fromShort` to convert back. `ShortByteString` is suitable for keys in maps and storing small strings. ```haskell import qualified Data.ByteString as B import qualified Data.ByteString.Short as SB import qualified Data.Map.Strict as Map main :: IO () main = do -- Convert strict -> short (makes a copy into unpinned memory) let strict = B.pack [72,101,108,108,111] let short = SB.toShort strict print (SB.length short) -- 5 -- Convert back let back = SB.fromShort short print (back == strict) -- True -- Store many small identifiers compactly let ids :: Map.Map SB.ShortByteString Int ids = Map.fromList [ (SB.toShort (B.pack (map fromIntegral "alice")), 1) , (SB.toShort (B.pack (map fromIntegral "bob")), 2) ] print (Map.size ids) -- 2 -- isValidUtf8 also available on ShortByteString print (SB.isValidUtf8 short) -- True ``` -------------------------------- ### Character Encoding with ByteString Builders Source: https://context7.com/haskell/bytestring/llms.txt Demonstrates different character encoding strategies (UTF-8, char7, string8) using ByteString builders. The IsString instance defaults to stringUtf8. ```haskell {-# LANGUAGE OverloadedStrings #-} import Data.ByteString.Builder import qualified Data.ByteString.Lazy as L main :: IO () main = do -- IsString instance uses stringUtf8 let b1 = "café" :: Builder print (L.unpack (toLazyByteString b1)) -- [99,97,102,195,169] -- UTF-8 bytes for "café" -- Explicit encoding choices let b2 = char7 'A' <> charUtf8 'λ' <> string8 "hello" print (L.length (toLazyByteString b2)) -- 1 + 2 + 5 = 8 -- CSV example from the module docs let row = mconcat . zipWith ("sep" x -> sep <> x) (mempty : repeat (char7 ',')) let cells = map stringUtf8 ["name", "age", "city"] let csvRow = row cells <> charUtf8 '\n' print (toLazyByteString csvRow) -- "name,age,city\n" ``` -------------------------------- ### pack / unpack Source: https://context7.com/haskell/bytestring/llms.txt Converts between a list of Word8 values and a strict ByteString. Both operations are O(n). ```APIDOC ## pack / unpack — Convert between [Word8] and ByteString `pack` copies a list of `Word8` values into a freshly allocated strict `ByteString`; `unpack` produces a list of bytes by traversing the buffer. Both are O(n). For large lists of string literals consider `OverloadedStrings` or the `Builder` API instead. ```haskell import qualified Data.ByteString as B import Data.Word (Word8) main :: IO () main = do let bytes :: [Word8] bytes = [72, 101, 108, 108, 111] -- "Hello" as ASCII codes let bs = B.pack bytes print bs -- "Hello" let roundTrip = B.unpack bs print roundTrip -- [72,101,108,108,111] -- OverloadedStrings alternative (requires {-# LANGUAGE OverloadedStrings #-}) let bs2 = "Hello" :: B.ByteString print (B.unpack bs2 == bytes) -- True ``` ``` -------------------------------- ### Execute Builder to Lazy ByteString or File Source: https://context7.com/haskell/bytestring/llms.txt Execute a `Builder` to produce a lazy `ByteString` using `toLazyByteString`, or write directly to a `Handle` with `hPutBuilder` to avoid intermediate allocation. Concatenation of `Builder`s using `<>` is O(1). ```haskell import Data.ByteString.Builder import qualified Data.ByteString.Lazy as L main :: IO () main = do -- Build a simple binary frame: 4-byte length + payload let payload = byteString (mconcat (map (\i -> toEnum i) [65..90])) -- Actually, use stringUtf8: let msg = stringUtf8 "Hello, world!" let frame = int32BE (fromIntegral (L.length (toLazyByteString msg))) <> msg let result = toLazyByteString frame print (L.length result) -- 4 + 13 = 17 -- Write directly to a file (no intermediate lazy ByteString allocated) writeFile "/tmp/frame.bin" frame -- Concatenation is O(1) let manyChunks = mconcat [word8 (fromIntegral i) | i <- [0..255 :: Int]] print (L.length (toLazyByteString manyChunks)) -- 256 ``` -------------------------------- ### head / tail / last / init / uncons / unsnoc Source: https://context7.com/haskell/bytestring/llms.txt Provides functions for deconstructing ByteStrings. `head`, `tail`, `last`, and `init` are partial and will throw errors on empty ByteStrings. The total `uncons` and `unsnoc` variants return `Maybe` for safe pattern matching. ```APIDOC ## head / tail / last / init / uncons / unsnoc — Structural deconstruction `head`, `tail`, `last`, and `init` are partial (throw on empty); prefer the total `uncons`/`unsnoc` variants that return `Maybe` for safe pattern matching. ```haskell import qualified Data.ByteString as B safeProcess :: B.ByteString -> String safeProcess bs = case B.uncons bs of Nothing -> "empty" Just (h, rest) -> "head=" ++ show h ++ " rest-length=" ++ show (B.length rest) main :: IO () main = do let bs = B.pack [10, 20, 30] putStrLn (safeProcess bs) -- head=10 rest-length=2 case B.unsnoc bs of Nothing -> putStrLn "empty" Just (ini, l) -> do print l -- 30 print ini -- "\n\DC4" print (B.head bs) -- 10 print (B.last bs) -- 30 ``` ``` -------------------------------- ### ASCII Numeric Builders Source: https://context7.com/haskell/bytestring/llms.txt Shows how to encode integers into ASCII decimal or hexadecimal formats using various builder functions. `byteStringHex` is used for encoding entire ByteStrings. ```haskell import Data.ByteString.Builder import qualified Data.ByteString.Lazy as L import qualified Data.ByteString as S main :: IO () main = do -- Decimal print (toLazyByteString (intDec (-42))) -- "-42" print (toLazyByteString (word32Dec 4294967295)) -- "4294967295" print (toLazyByteString (integerDec (10^30))) -- "1000000000000000000000000000000" -- Hexadecimal (shortest representation) print (toLazyByteString (word16Hex 0x0A10)) -- "a10" print (toLazyByteString (word8Hex 0xFF)) -- "ff" -- Fixed-width hex print (toLazyByteString (word32HexFixed 255)) -- "000000ff" -- Hex-dump a ByteString let bs = S.pack [0xDE, 0xAD, 0xBE, 0xEF] print (toLazyByteString (byteStringHex bs)) -- "deadbeef" ``` -------------------------------- ### Element-wise Transformations: map, filter, partition Source: https://context7.com/haskell/bytestring/llms.txt Apply functions to each byte using `map`, retain bytes matching a predicate with `filter`, or split into two ByteStrings based on a predicate using `partition`. `partition` performs the split in a single pass. ```haskell import qualified Data.ByteString as B import Data.Word (Word8) import Data.Bits (xor) main :: IO () main = do let bs = B.pack [65..90] -- 'A'..'Z' as bytes -- XOR every byte with a key (simple cipher) let key = 0x20 :: Word8 let scrambled = B.map (`xor` key) bs print scrambled -- "abcdefghijklmnopqrstuvwxyz" -- Keep only even bytes let evens = B.filter even bs print evens -- "BDFHJLNPRTVXZ" -- Split odd vs even let (odds, evens2) = B.partition odd bs print (B.length odds, B.length evens2) -- (13,13) ``` -------------------------------- ### Binary Encoding of Integers Source: https://context7.com/haskell/bytestring/llms.txt Details functions for encoding fixed-width integers (16, 32, 64-bit) and IEEE floats into big-endian and little-endian formats. ```APIDOC ### Binary encoding: big-endian and little-endian integers `int16BE`/`int32BE`/`int64BE`, `word16BE`/`word32BE`/`word64BE` and their `LE` counterparts encode fixed-width integers. Also `floatBE`/`doubleBE`/`floatLE`/`doubleLE` for IEEE floats. ### Example Usage: ```haskell import Data.ByteString.Builder import qualified Data.ByteString.Lazy as L -- Binary serialization of a record data Packet = Packet { pMagic :: Word16 -- big-endian , pLength :: Word32 -- big-endian , pPayload :: [Word8] } encodePacket :: Packet -> L.ByteString encodePacket p = toLazyByteString $ word16BE (pMagic p) <> word32BE (pLength p) <> mconcat (map word8 (pPayload p)) main :: IO () main = do let pkt = Packet 0xDEAD 5 [1,2,3,4,5] let bs = encodePacket pkt print (L.unpack bs) -- [222,173,0,0,0,5,1,2,3,4,5] -- Host-endian (from Data.ByteString.Builder.Extra) import Data.ByteString.Builder.Extra (intHost, word64Host) -- let nativeInt = toLazyByteString (intHost 42) return () ``` ``` -------------------------------- ### Reductions: foldl', foldr, foldl1' Source: https://context7.com/haskell/bytestring/llms.txt Use `foldl'` for strict left reduction to a scalar value, `foldr` for building lazy output, and `foldl1'`/`foldr1` for non-empty inputs. The `checksum` function demonstrates a strict left fold for XORing bytes, and `collectAbove` shows a right fold for accumulating elements. ```haskell import qualified Data.ByteString as B import Data.Word (Word8) import Data.Bits (xor) -- Compute XOR checksum checksum :: B.ByteString -> Word8 checksum = B.foldl' xor 0 -- Collect bytes greater than threshold collectAbove :: Word8 -> B.ByteString -> [Word8] collectAbove threshold = B.foldr ( acc -> if b > threshold then b : acc else acc) [] main :: IO () main = do let bs = B.pack [0x01, 0x02, 0x04, 0x08, 0x10] print (checksum bs) -- 0x1f (XOR of all) print (collectAbove 4 bs) -- [8,16] let bs2 = B.pack [1..10] print (B.foldl' (+) 0 bs2) -- 55 (sum 1..10) print (B.foldl1' max bs2) -- 10 ``` -------------------------------- ### Add bytes and concatenate ByteStrings with cons/snoc/append Source: https://context7.com/haskell/bytestring/llms.txt `cons` prepends a Word8 and `snoc` appends one, both are O(n) due to copying. `append` (or `<>`) concatenates two ByteStrings. For many concatenations, prefer `Builder` or `mconcat` to avoid O(n²) performance. ```haskell import qualified Data.ByteString as B main :: IO () main = do let bs = B.pack [1,2,3] print (B.cons 0 bs) -- "\NUL\SOH\STX\ETX" print (B.snoc bs 4) -- "\SOH\STX\ETX\EOT" print (bs <> B.pack [4,5]) -- "\SOH\STX\ETX\EOT\ENQ" -- mconcat avoids O(n²) for many pieces print (mconcat (map B.singleton [65..70])) -- "ABCDEF" ``` -------------------------------- ### Fast ByteString Accessors (unsafeIndex) Source: https://context7.com/haskell/bytestring/llms.txt Use these functions for performance-critical inner loops where correctness is guaranteed externally. Incorrect usage leads to undefined behavior. ```haskell import qualified Data.ByteString as B import qualified Data.ByteString.Unsafe as BU import Data.Word (Word8) -- Fast inner loop: sum all bytes (caller guarantees non-empty, n <= length) sumBytes :: B.ByteString -> Int -> Word8 -> Word8 sumBytes bs n acc | n >= B.length bs = acc | otherwise = sumBytes bs (n+1) (acc + BU.unsafeIndex bs n) -- Parse a fixed-width big-endian Word32 at known offset readWord32BE :: B.ByteString -> Int -> Word32 readWord32BE bs off = let b0 = fromIntegral (BU.unsafeIndex bs off) b1 = fromIntegral (BU.unsafeIndex bs (off+1)) b2 = fromIntegral (BU.unsafeIndex bs (off+2)) b3 = fromIntegral (BU.unsafeIndex bs (off+3)) in (b0 `shiftL` 24) .|. (b1 `shiftL` 16) .|. (b2 `shiftL` 8) .|. b3 where shiftL = Data.Bits.shiftL; (.|.) = (Data.Bits..|.) main :: IO () main = do let bs = B.pack [0xDE, 0xAD, 0xBE, 0xEF, 0x00] print (readWord32BE bs 0) -- 3735928559 (0xDEADBEEF) print (sumBytes bs 0 0) -- modular sum of all 5 bytes ``` -------------------------------- ### Safely deconstruct ByteStrings with uncons/unsnoc Source: https://context7.com/haskell/bytestring/llms.txt Use the total `uncons` and `unsnoc` functions for safe pattern matching on ByteStrings, returning `Maybe` to handle empty cases. Partial functions like `head`, `tail`, `last`, and `init` will throw errors on empty ByteStrings. ```haskell import qualified Data.ByteString as B safeProcess :: B.ByteString -> String safeProcess bs = case B.uncons bs of Nothing -> "empty" Just (h, rest) -> "head=" ++ show h ++ " rest-length=" ++ show (B.length rest) main :: IO () main = do let bs = B.pack [10, 20, 30] putStrLn (safeProcess bs) -- head=10 rest-length=2 case B.unsnoc bs of Nothing -> putStrLn "empty" Just (ini, l) -> do print l -- 30 print ini -- "\n\DC4" print (B.head bs) -- 10 print (B.last bs) -- 30 ``` -------------------------------- ### Searching ByteStrings: `elemIndex`, `elemIndices`, `findIndex`, `count` Source: https://context7.com/haskell/bytestring/llms.txt Find the first (`elemIndex`, `findIndex`) or all (`elemIndices`, `findIndices`) occurrences of a byte or elements satisfying a predicate. `count` efficiently counts occurrences. ```haskell import qualified Data.ByteString as B import Data.Word (Word8) main :: IO () main = do let bs = B.pack (map fromIntegral "abracadabra") -- First and last occurrence of 'a' (0x61) print (B.elemIndex 0x61 bs) -- Just 0 print (B.elemIndexEnd 0x61 bs) -- Just 10 -- All occurrences print (B.elemIndices 0x61 bs) -- [0,2,4,6,10] -- Count bytes equal to 'a' print (B.count 0x61 bs) -- 5 -- First byte > 'c' (0x63) print (B.findIndex (> 0x63) bs) -- Just 1 ('r' = 0x72) -- All positions satisfying predicate print (B.findIndices even bs) -- [1,3,5,7,9] ``` -------------------------------- ### Convert between [Word8] and Strict ByteString with pack/unpack Source: https://context7.com/haskell/bytestring/llms.txt Use `pack` to copy a list of Word8 values into a strict ByteString and `unpack` to convert a ByteString back to a list. Both operations are O(n). For large string literals, consider `OverloadedStrings` or the `Builder` API. ```haskell import qualified Data.ByteString as B import Data.Word (Word8) main :: IO () main = do let bytes :: [Word8] bytes = [72, 101, 108, 108, 111] -- "Hello" as ASCII codes let bs = B.pack bytes print bs -- "Hello" let roundTrip = B.unpack bs print roundTrip -- [72,101,108,108,111] -- OverloadedStrings alternative (requires {-# LANGUAGE OverloadedStrings #-}) let bs2 = "Hello" :: B.ByteString print (B.unpack bs2 == bytes) -- True ``` -------------------------------- ### Random Access to ByteStrings: `index`, `indexMaybe`, `(!?)` Source: https://context7.com/haskell/bytestring/llms.txt Access bytes by index. `index` is unsafe and throws errors on out-of-bounds access. `indexMaybe` and `(!?)` provide safe access, returning `Maybe Word8`. ```haskell import qualified Data.ByteString as B main :: IO () main = do let bs = B.pack [10, 20, 30, 40, 50] -- Safe indexing print (bs B.!? 2) -- Just 30 print (bs B.!? 99) -- Nothing -- Unsafe (throws): only use when bounds are guaranteed print (B.index bs 0) -- 10 -- Walk with index let pairs = [(i, bs B.!? i) | i <- [0..6]] mapM_ print pairs -- (0,Just 10) .. (4,Just 50) (5,Nothing) (6,Nothing) ``` -------------------------------- ### Array-Style Operations: `sort`, `reverse`, `replicate`, `unfoldr` Source: https://context7.com/haskell/bytestring/llms.txt Perform array-like operations on ByteStrings. `sort` uses efficient counting sort for larger ByteStrings. `replicate` creates a ByteString of repeated bytes. `unfoldr` builds a ByteString from a seed value. ```haskell import qualified Data.ByteString as B main :: IO () main = do -- Counting sort let bs = B.pack [5,3,1,4,1,5,9,2,6] print (B.sort bs) -- "\SOH\SOH\STX\ETX\EOT\ENQ\ENQ\ACK\t" print (B.unpack (B.sort bs)) -- [1,1,2,3,4,5,5,6,9] -- Reverse print (B.reverse bs) -- [6,2,9,5,1,4,1,3,5] -- replicate: 10 copies of 0xFF print (B.replicate 10 0xFF) -- unfoldr: Fibonacci bytes (mod 256) let fibs = B.unfoldr (\(a,b) -> if a > 200 then Nothing else Just (a, (b, a+b))) (0,1) print (B.unpack fibs) -- [0,1,1,2,3,5,8,13,21,34,55,89,144] ``` -------------------------------- ### Embed existing data into a Builder Source: https://context7.com/haskell/bytestring/llms.txt Embed pre-existing strict, lazy, or short `ByteString`s, or single `Word8`/`Int8` values into a `Builder`. For large strict `ByteString` payloads, consider `byteStringInsert` to avoid copying. ```haskell import Data.ByteString.Builder import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Short as SB main :: IO () main = do let header = S.pack [0xCA, 0xFE, 0xBA, 0xBE] let payload = SB.toShort (S.pack [1..8]) let b = byteString header <> word8 0xFF <> shortByteString payload <> int8 (-1) let out = L.toStrict (toLazyByteString b) print (S.length out) -- 4 + 1 + 8 + 1 = 14 print (S.unpack out) -- [202,254,186,190,255,1,2,3,4,5,6,7,8,255] ``` -------------------------------- ### Chunk Conversion for Lazy ByteStrings Source: https://context7.com/haskell/bytestring/llms.txt Convert between lists of strict ByteStrings and lazy ByteStrings using fromChunks and toChunks. Use toStrict and fromStrict for conversions between strict and lazy representations. ```haskell import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L main :: IO () main = do -- Assemble from strict chunks let chunks = map (S.pack . map fromIntegral) [[1,2,3],[4,5],[6]] let lbs = L.fromChunks chunks print (L.length lbs) -- 6 -- Inspect chunks print (map S.length (L.toChunks lbs)) -- [3,2,1] -- Convert strict <-> lazy let strict = S.pack [1..10] let lazy = L.fromStrict strict let strict2 = L.toStrict lazy print (strict == strict2) -- True -- Lazy I/O: read entire file lazily -- contents <- L.readFile "/etc/passwd" -- print (L.length contents) ``` -------------------------------- ### Structural Rearrangements with `intercalate`, `intersperse`, `group` Source: https://context7.com/haskell/bytestring/llms.txt Use `intercalate` to join ByteStrings with a separator, `intersperse` to insert a byte between elements, and `group` to run-length encode adjacent equal bytes. `group` is useful for processing consecutive identical data. ```haskell import qualified Data.ByteString as B main :: IO () main = do let sep = B.pack [0x2C, 0x20] -- ", " let words' = map (B.pack . map fromIntegral) ["one","two","three"] print (B.intercalate sep words') -- "one, two, three" let bs = B.pack [0x41, 0x42, 0x43] -- "ABC" print (B.intersperse 0x2D bs) -- "A-B-C" let grouped = B.group (B.pack [1,1,2,3,3,3,1]) print (map B.unpack grouped) -- [[1,1],[2],[3,3,3],[1]] ```