diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2017, Kazu Yamamoto
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Kazu Yamamoto nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Network/ByteOrder.hs b/Network/ByteOrder.hs
new file mode 100644
--- /dev/null
+++ b/Network/ByteOrder.hs
@@ -0,0 +1,280 @@
+-- | Peek and poke functions for network byte order.
+
+module Network.ByteOrder (
+    -- *Types
+    Buffer
+  , Offset
+    -- *Poking
+  , poke8
+  , poke16
+  , poke24
+  , poke32
+  , poke64
+    -- *Peeking
+  , peek8
+  , peek16
+  , peek24
+  , peek32
+  , peek64
+    -- *From Word to ByteString
+  , bytestring8
+  , bytestring16
+  , bytestring32
+  , bytestring64
+    -- *From ByteString to Word
+  , word8
+  , word16
+  , word32
+  , word64
+    -- *Utilities
+  , unsafeWithByteString
+  ) where
+
+import Data.Bits (shiftR, shiftL, (.&.), (.|.))
+import Data.ByteString.Internal (ByteString(..), unsafeCreate)
+import Data.Word (Word8, Word16, Word32, Word64)
+import Foreign.ForeignPtr (withForeignPtr)
+import Foreign.Ptr (Ptr, plusPtr)
+import Foreign.Storable (poke, peek)
+import System.IO.Unsafe (unsafeDupablePerformIO)
+
+-- $setup
+-- >>> import Data.ByteString hiding (foldl')
+-- >>> import Data.Word
+-- >>> import Data.List
+
+----------------------------------------------------------------
+
+type Buffer = Ptr Word8
+type Offset = Int
+
+----------------------------------------------------------------
+
+(+.) :: Buffer -> Offset -> Buffer
+(+.) = plusPtr
+
+----------------------------------------------------------------
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4]
+-- >>> unsafeWithByteString buf (poke8 0)
+-- >>> unpack buf
+-- [0,2,3,4]
+poke8 :: Word8 -> Buffer -> Offset -> IO ()
+poke8 w ptr off = poke (ptr +. off) w
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4]
+-- >>> unsafeWithByteString buf (poke16 (7*256 + 8))
+-- >>> unpack buf
+-- [7,8,3,4]
+poke16 :: Word16 -> Buffer -> Offset -> IO ()
+poke16 w ptr off = do
+    poke8 w0 ptr off
+    poke8 w1 ptr (off + 1)
+  where
+    w0 = fromIntegral ((w `shiftR`  8) .&. 0xff)
+    w1 = fromIntegral  (w              .&. 0xff)
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4]
+-- >>> unsafeWithByteString buf (poke24 (6*65536 + 7*256 + 8))
+-- >>> unpack buf
+-- [6,7,8,4]
+poke24 :: Word32 -> Buffer -> Offset -> IO ()
+poke24 w ptr off = do
+    poke8 w0 ptr off
+    poke8 w1 ptr (off + 1)
+    poke8 w2 ptr (off + 2)
+  where
+    w0 = fromIntegral ((w `shiftR` 16) .&. 0xff)
+    w1 = fromIntegral ((w `shiftR`  8) .&. 0xff)
+    w2 = fromIntegral  (w              .&. 0xff)
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4]
+-- >>> unsafeWithByteString buf (poke32 (6*65536 + 7*256 + 8))
+-- >>> unpack buf
+-- [0,6,7,8]
+poke32 :: Word32 -> Buffer -> Offset -> IO ()
+poke32 w ptr off = do
+    poke8 w0 ptr off
+    poke8 w1 ptr (off + 1)
+    poke8 w2 ptr (off + 2)
+    poke8 w3 ptr (off + 3)
+  where
+    w0 = fromIntegral ((w `shiftR` 24) .&. 0xff)
+    w1 = fromIntegral ((w `shiftR` 16) .&. 0xff)
+    w2 = fromIntegral ((w `shiftR`  8) .&. 0xff)
+    w3 = fromIntegral  (w              .&. 0xff)
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4,5,6,7,8]
+-- >>> unsafeWithByteString buf (poke64 (6*65536 + 7*256 + 8))
+-- >>> unpack buf
+-- [0,0,0,0,0,6,7,8]
+poke64 :: Word64 -> Buffer -> Offset -> IO ()
+poke64 w ptr off = do
+    poke8 w0 ptr off
+    poke8 w1 ptr (off + 1)
+    poke8 w2 ptr (off + 2)
+    poke8 w3 ptr (off + 3)
+    poke8 w4 ptr (off + 4)
+    poke8 w5 ptr (off + 5)
+    poke8 w6 ptr (off + 6)
+    poke8 w7 ptr (off + 7)
+  where
+    w0 = fromIntegral ((w `shiftR` 56) .&. 0xff)
+    w1 = fromIntegral ((w `shiftR` 48) .&. 0xff)
+    w2 = fromIntegral ((w `shiftR` 40) .&. 0xff)
+    w3 = fromIntegral ((w `shiftR` 32) .&. 0xff)
+    w4 = fromIntegral ((w `shiftR` 24) .&. 0xff)
+    w5 = fromIntegral ((w `shiftR` 16) .&. 0xff)
+    w6 = fromIntegral ((w `shiftR`  8) .&. 0xff)
+    w7 = fromIntegral  (w              .&. 0xff)
+
+----------------------------------------------------------------
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4]
+-- >>> unsafeWithByteString buf peek8
+-- 1
+peek8 :: Buffer -> Offset -> IO Word8
+peek8 ptr off = peek (ptr +. off)
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4]
+-- >>> unsafeWithByteString buf peek16
+-- 258
+peek16 :: Buffer -> Offset -> IO Word16
+peek16 ptr off = do
+    w0 <- (`shiftL` 8) . fromIntegral <$> peek8 ptr off
+    w1 <-                fromIntegral <$> peek8 ptr (off + 1)
+    return $ w0 .|. w1
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4]
+-- >>> unsafeWithByteString buf peek24
+-- 66051
+peek24 :: Buffer -> Offset -> IO Word32
+peek24 ptr off = do
+    w0 <- (`shiftL` 16) . fromIntegral <$> peek8 ptr off
+    w1 <- (`shiftL`  8) . fromIntegral <$> peek8 ptr (off + 1)
+    w2 <-                 fromIntegral <$> peek8 ptr (off + 2)
+    return $ w0 .|. w1 .|. w2
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4]
+-- >>> unsafeWithByteString buf peek32
+-- 16909060
+peek32 :: Buffer -> Offset -> IO Word32
+peek32 ptr off = do
+    w0 <- (`shiftL` 24) . fromIntegral <$> peek8 ptr off
+    w1 <- (`shiftL` 16) . fromIntegral <$> peek8 ptr (off + 1)
+    w2 <- (`shiftL`  8) . fromIntegral <$> peek8 ptr (off + 2)
+    w3 <-                 fromIntegral <$> peek8 ptr (off + 3)
+    return $ w0 .|. w1 .|. w2 .|. w3
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4,5,6,7,8]
+-- >>> unsafeWithByteString buf peek64
+-- 72623859790382856
+peek64 :: Buffer -> Offset -> IO Word64
+peek64 ptr off = do
+    w0 <- (`shiftL` 56) . fromIntegral <$> peek8 ptr off
+    w1 <- (`shiftL` 48) . fromIntegral <$> peek8 ptr (off + 1)
+    w2 <- (`shiftL` 40) . fromIntegral <$> peek8 ptr (off + 2)
+    w3 <- (`shiftL` 32) . fromIntegral <$> peek8 ptr (off + 3)
+    w4 <- (`shiftL` 24) . fromIntegral <$> peek8 ptr (off + 4)
+    w5 <- (`shiftL` 16) . fromIntegral <$> peek8 ptr (off + 5)
+    w6 <- (`shiftL`  8) . fromIntegral <$> peek8 ptr (off + 6)
+    w7 <-                 fromIntegral <$> peek8 ptr (off + 7)
+    return $ w0 .|. w1 .|. w2 .|. w3 .|. w4 .|. w5 .|. w6 .|. w7
+
+----------------------------------------------------------------
+
+-- |
+--
+-- >>> let w = 5 :: Word8
+-- >>> unpack $ bytestring8 w
+-- [5]
+bytestring8 :: Word8 -> ByteString
+bytestring8 w = unsafeCreate 1 $ \ptr -> poke8 w ptr 0
+
+-- |
+--
+-- >>> let w = foldl' (\x y -> x * 256 + y) 0 [5,6] :: Word16
+-- >>> unpack $ bytestring16 w
+-- [5,6]
+bytestring16 :: Word16 -> ByteString
+bytestring16 w = unsafeCreate 2 $ \ptr -> poke16 w ptr 0
+
+-- |
+--
+-- >>> let w = foldl' (\x y -> x * 256 + y) 0 [5,6,7,8] :: Word32
+-- >>> unpack $ bytestring32 w
+-- [5,6,7,8]
+bytestring32 :: Word32 -> ByteString
+bytestring32 w = unsafeCreate 4 $ \ptr -> poke32 w ptr 0
+
+-- |
+--
+-- >>> let w = foldl' (\x y -> x * 256 + y) 0 [1,2,3,4,5,6,7,8] :: Word64
+-- >>> unpack $ bytestring64 w
+-- [1,2,3,4,5,6,7,8]
+bytestring64 :: Word64 -> ByteString
+bytestring64 w = unsafeCreate 8 $ \ptr -> poke64 w ptr 0
+
+----------------------------------------------------------------
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4,5,6,7,8]
+-- >>> word8 buf
+-- 1
+word8 :: ByteString -> Word8
+word8 bs = unsafeDupablePerformIO $ unsafeWithByteString bs peek8
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4,5,6,7,8]
+-- >>> word16 buf
+-- 258
+word16 :: ByteString -> Word16
+word16 bs = unsafeDupablePerformIO $ unsafeWithByteString bs peek16
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4,5,6,7,8]
+-- >>> word32 buf
+-- 16909060
+word32 :: ByteString -> Word32
+word32 bs = unsafeDupablePerformIO $ unsafeWithByteString bs peek32
+
+-- |
+--
+-- >>> let buf = pack [1,2,3,4,5,6,7,8]
+-- >>> word64 buf
+-- 72623859790382856
+word64 :: ByteString -> Word64
+word64 bs = unsafeDupablePerformIO $ unsafeWithByteString bs peek64
+
+----------------------------------------------------------------
+
+-- | Using 'ByteString' as 'Buffer' and call the 'IO' action
+--   of the second argument by passing the start point and the offset
+--   of the 'ByteString'.
+--   Note that if a 'ByteString' is created newly, its offset is 0.
+unsafeWithByteString :: ByteString -> (Buffer -> Offset -> IO a) -> IO a
+unsafeWithByteString (PS fptr off _) io = withForeignPtr fptr $
+    \ptr -> io ptr off
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/network-byte-order.cabal b/network-byte-order.cabal
new file mode 100644
--- /dev/null
+++ b/network-byte-order.cabal
@@ -0,0 +1,40 @@
+-- Initial network-byte-order.cabal generated by cabal init.  For further
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                network-byte-order
+version:             0.0.0.0
+synopsis:            Network byte order utilities
+description:         Peek and poke functions for network byte order.
+license:             BSD3
+license-file:        LICENSE
+author:              Kazu Yamamoto <kazu@iij.ad.jp>
+maintainer:          Kazu Yamamoto <kazu@iij.ad.jp>
+-- copyright:
+category:            Network
+build-type:          Simple
+-- extra-source-files:  ChangeLog.md
+cabal-version:       >= 1.10
+
+library
+  ghc-options:         -Wall
+  exposed-modules:     Network.ByteOrder
+  -- other-modules:
+  -- other-extensions:
+  build-depends:       base >= 4.8 && < 5
+                     , bytestring
+  -- hs-source-dirs:
+  default-language:    Haskell2010
+
+test-suite doctest
+  type:                 exitcode-stdio-1.0
+  default-language:     Haskell2010
+  hs-source-dirs:       test
+  ghc-options:          -Wall
+  main-is:              doctests.hs
+  build-depends:        base
+                      , bytestring
+                      , doctest
+
+source-repository head
+  type:     git
+  location: git://github.com/kazu-yamamoto/network-byte-order.git
diff --git a/test/doctests.hs b/test/doctests.hs
new file mode 100644
--- /dev/null
+++ b/test/doctests.hs
@@ -0,0 +1,10 @@
+module Main where
+
+import Test.DocTest
+
+main :: IO ()
+main = doctest [
+    "-XOverloadedStrings"
+  , "-XCPP"
+  , "Network/ByteOrder.hs"
+  ]
