HORIZON HASKELLDocslts/ghc-9.10.xc74966e2026-09-27Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · c74966e · 2026-09-27

Modulebyte-order-0.1.3.1Haskell2010

System.ByteOrder

This module offers an interface to portably work with byte arrays whose contents are known to be of a fixed endianness. There are two ways to use this module:

  • Untyped Conversions: The functions toBigEndian, toLittleEndian, fromBigEndian, and fromLittleEndian convert between native-endian words and big/little-endian words. The word resulting from to(Big|Little)Endian should be written to a primitive byte array or a pointer afterwards. (There is no other purpose of such a conversion.) Similarly, the argument to from(Big|Little)Endian should be a word that was read from a primitive byte array or a pointer. This interface is useful when serializing or deserializing a data structure with fields of varying sizes.

  • Typed Conversions: The type Fixed provides a convenient type-directed interface to working with arrays of homogenous words. This interface is easier to use and should be preferred when possible.

The example at the bottom of this page demonstrates how to use the type-directed interface.

  • 2 types
  • 2 classes
  • 3 values

Types

2 declarations
datadata ByteOrder
#

Byte ordering.

Constructors

  • BigEndian

    most-significant-byte occurs in lowest address.

  • LittleEndian

    least-significant-byte occurs in lowest address.

Instances8Bounded, Enum, Eq, Ord, Read, Show, …
newtypenewtype Fixed (a :: ByteOrder) b where
#

A word whose byte order is specified (not platform dependent) when working with Prim, Storable, and PrimUnaligned (this last instance is provided alongside the typeclass itself in the primitive-unaligned library).

Constructors

Instances9Enum, Eq, Integral, Num, Ord, Real, …

Classes

2 declarations
classclass Bytes a where
#

Types that are represented as a fixed-sized word. For these types, the bytes can be swapped. The instances of this class use byteswapping primitives and compile-time knowledge of native endianness to provide portable endianness conversion functions.

Instances11Bytes, …
  • Bytes Int16Defined in byte-order-0.1.3.1 · System.ByteOrder.Class
  • Bytes Int32Defined in byte-order-0.1.3.1 · System.ByteOrder.Class
  • Bytes Int64Defined in byte-order-0.1.3.1 · System.ByteOrder.Class
  • Bytes Int8Defined in byte-order-0.1.3.1 · System.ByteOrder.Class
  • Bytes Word16Defined in byte-order-0.1.3.1 · System.ByteOrder.Class
  • Bytes Word32Defined in byte-order-0.1.3.1 · System.ByteOrder.Class
  • Bytes Word64Defined in byte-order-0.1.3.1 · System.ByteOrder.Class
  • Bytes Word8Defined in byte-order-0.1.3.1 · System.ByteOrder.Class
  • Bytes WordDefined in byte-order-0.1.3.1 · System.ByteOrder.Class
  • Bytes Word128Defined in byte-order-0.1.3.1 · System.ByteOrder.Class
  • Bytes Word256Defined in byte-order-0.1.3.1 · System.ByteOrder.Class

Conversion

4 declarations
methodtoBigEndian :: a -> a
#

Convert from a native-endian word to a big-endian word.

methodtoLittleEndian :: a -> a
#

Convert from a native-endian word to a little-endian word.

valuefromBigEndian :: Bytes a => a -> a
#

Convert from a big-endian word to a native-endian word.

System Byte Order

1 declaration

Example

0 declarations

Suppose there is a protocol for aggregating numbers that uses stream sockets for communication. The protocol interprets all numbers as unsigned. It is described as follows:

  1. The client sends the server a little-endian 16-bit number N. This is how many numbers will follow.

  2. The client sends N little-endian 64-bit numbers to the server.

  3. The server responds with two little-endian 64-bit numbers: the sum and the product of the N numbers it received.

Assume the existence of a send and receive that block until the total number of requested bytes have been handled. They both work on their argument arrays starting at index zero, which ensures that any 2-byte, 4-byte, or 8-byte types will be aligned properly. (GHC always machine-word aligns the payload of a byte array.) Additionally, assume the typed and untyped functions that convert between PrimArray and ByteArray by changing out the data constructor.

send :: Socket -> ByteArray -> IO ()
receive :: Socket -> Int -> IO ByteArray
typed :: ByteArray -> PrimArray a
untyped :: PrimArray a -> ByteArray

For simplicity, all error-handling is omitted. With the type-directed interface, the server is implemented as:

import Data.Primitive.ByteArray
import Data.Primitive.PrimArray
import System.ByteOrder

server :: Socket -> IO a
server sckt = forever $ do
  totalByteArray <- receive sckt 2
  let totalPrimArray = typed totalByteArray :: PrimArray (Fixed 'LittleEndian Word16)
  let Fixed total = indexPrimArray totalPrimArray 0
  numberByteArray <- receive sckt (8 * fromIntegral @Word16 @Int total)
  let (sum,prod) = foldlPrimArray'
        (\(!sumN,!prodN) (Fixed n) -> (sumN + n, prodN * n))
        (0,1)
        (typed numberByteArray :: PrimArray (Fixed 'LittleEndian Word64))
  reply :: MutablePrimArray RealWorld (Fixed 'LittleEndian Word64) <- newPrimArray 2
  writePrimArray reply 0 (Fixed sum)
  writePrimArray reply 1 (Fixed prod)
  send sckt . untyped =<< unsafeFreezePrimArray reply

Not every explicit type annotation above is needed. Some are provided for the reader's benefit. As long as the user ensures that the typed primitive arrays use Fixed in their element types, the endianness conversions are guaranteed to be correct.