content: module Util where
  
  import Foreign
  import Foreign.Marshal
  import Foreign.Ptr
  import Foreign.Storable (peek, pokeByteOff)
  import System.IO.Unsafe
  
  
  -- the following two functions are copied from hsdns package:
  -- http://hackage.haskell.org/package/hsdns-1.6.1/docs/src/ADNS-Endian.html#readWord32
  -- note that later versions don't have these functions
  
  data Endian
    = LittleEndian                -- ^ byte order: @1234@
    | BigEndian                   -- ^ byte order: @4321@
    | PDPEndian                   -- ^ byte order: @3412@
    deriving (Show, Eq)
  
  {-# NOINLINE endian #-}
  endian :: Endian
  endian =
    System.IO.Unsafe.unsafePerformIO $
      allocaArray (sizeOf (undefined :: Word32)) $ \p -> do
        let val = 0x01020304 :: Word32
        poke p val
        let p' = castPtr p :: Ptr Word8
        val' <- peekArray 4 p'
        case val' of
          (0x01:0x02:0x03:0x04:[]) -> return BigEndian
          (0x04:0x03:0x02:0x01:[]) -> return LittleEndian
          (0x03:0x04:0x01:0x02:[]) -> return PDPEndian
          _                        -> error "unknown endian"
  
  readWord32 :: Word32 -> (Word8, Word8, Word8, Word8)
  readWord32 n =
    let (b1,n1) = (n  .&. 255, n  `shiftR` 8)
        (b2,n2) = (n1 .&. 255, n1 `shiftR` 8)
        (b3,n3) = (n2 .&. 255, n2 `shiftR` 8)
        b4      = n3 .&. 255
    in
    case endian of
      BigEndian    -> (fromIntegral b4, fromIntegral b3, fromIntegral b2, fromIntegral b1)
      LittleEndian -> (fromIntegral b1, fromIntegral b2, fromIntegral b3, fromIntegral b4)
      PDPEndian    -> (fromIntegral b2, fromIntegral b1, fromIntegral b4, fromIntegral b3)
  
  -- copied from random-source package: http://hackage.haskell.org/package/random-source-0.3.0.6/docs/src/Data-Random-Internal-Words.html#buildWord32
  -- there're more auxilliary functions which come handy
  
  {-# INLINE buildWord32 #-}
  -- |Build a word out of 4 bytes.  No promises are made regarding the order
  -- in which the bytes are stuffed.  Note that this means that a 'RandomSource'
  -- or 'MonadRandom' making use of the default definition of 'getRandomWord', etc.,
  -- may return different random values on different platforms when started 
  -- with the same seed, depending on the platform's endianness.
  buildWord32 :: Word8 -> Word8 -> Word8 -> Word8 -> Word32
  buildWord32 b0 b1 b2 b3
      = unsafePerformIO . allocaBytes 4 $ \p -> do
          pokeByteOff p 0 b0
          pokeByteOff p 1 b1
          pokeByteOff p 2 b2
          pokeByteOff p 3 b3
          peek (castPtr p)
  
  