diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Revision history for byteslice
+
+## 0.1.0.0 -- YYYY-mm-dd
+
+* First version.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2019, Andrew Martin
+
+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 Andrew Martin 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/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/byteslice.cabal b/byteslice.cabal
new file mode 100644
--- /dev/null
+++ b/byteslice.cabal
@@ -0,0 +1,22 @@
+cabal-version: 2.2
+name: byteslice
+version: 0.1.0.0
+synopsis: Slicing ByteArray and MutableByteArray
+-- description:
+homepage: https://github.com/andrewthad/byteslice
+bug-reports: https://github.com/andrewthad/byteslice/issues
+license: BSD-3-Clause
+license-file: LICENSE
+author: Andrew Martin
+maintainer: andrew.thaddeus@gmail.com
+copyright: 2019 Andrew Martin
+category: Data
+extra-source-files: CHANGELOG.md
+
+library
+  exposed-modules: Data.Bytes.Types
+  build-depends:
+    , base >=4.11.1.0 && <5
+    , primitive >=0.6.4 && <0.7
+  hs-source-dirs: src
+  default-language: Haskell2010
diff --git a/src/Data/Bytes/Types.hs b/src/Data/Bytes/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Bytes/Types.hs
@@ -0,0 +1,87 @@
+{-# language BangPatterns #-}
+{-# language MagicHash #-}
+{-# language TypeFamilies #-}
+
+module Data.Bytes.Types
+  ( Bytes(..)
+  , MutableBytes(..)
+  ) where
+
+import Data.Primitive (ByteArray(..),MutableByteArray(..))
+import Data.Bits ((.&.),unsafeShiftR)
+import Data.Char (ord)
+import Data.Word (Word8)
+import GHC.Base (unsafeChr)
+import GHC.Exts (Int(I#),unsafeCoerce#,sameMutableByteArray#)
+import GHC.Exts (isTrue#,compareByteArrays#,IsList(..))
+
+import qualified Data.List as L
+import qualified Data.Primitive as PM
+
+-- | A slice of a 'ByteArray'.
+data Bytes = Bytes
+  {-# UNPACK #-} !ByteArray -- payload
+  {-# UNPACK #-} !Int -- offset
+  {-# UNPACK #-} !Int -- length
+
+-- | A slice of a 'MutableByteArray'.
+data MutableBytes s = MutableBytes
+  {-# UNPACK #-} !(MutableByteArray s) -- payload
+  {-# UNPACK #-} !Int -- offset
+  {-# UNPACK #-} !Int -- length
+
+instance IsList Bytes where
+  type Item Bytes = Word8
+  fromListN n xs = Bytes (fromListN n xs) 0 n
+  fromList xs = fromListN (L.length xs) xs
+  toList (Bytes arr off len) = toListLoop off len arr
+
+toListLoop :: Int -> Int -> ByteArray -> [Word8]
+toListLoop !off !len !arr = if len > 0
+  then PM.indexByteArray arr off : toListLoop (off + 1) (len - 1) arr
+  else []
+
+instance Show Bytes where
+  showsPrec d (Bytes arr off len) s = if len == 0
+    then showString "[]" s
+    else showString "[0x"
+       $ showHexDigits (PM.indexByteArray arr off)
+       $ showLoop (off + 1) (len - 1) arr
+       $ showChar ']'
+       $ s
+
+showLoop :: Int -> Int -> ByteArray -> String -> String
+showLoop !ix !len !arr s = if len > 0
+  then ',':'0':'x':showHexDigits (PM.indexByteArray arr ix) (showLoop (ix + 1) (len - 1) arr s)
+  else s
+
+showHexDigits :: Word8 -> String -> String
+showHexDigits !w s = word4ToChar (unsafeShiftR w 4) : word4ToChar (0x0F .&. w) : s
+
+word4ToChar :: Word8 -> Char
+word4ToChar w = if w < 10
+  then unsafeChr (ord '0' + fromIntegral w)
+  else unsafeChr (ord 'a' + (fromIntegral w) - 10)
+
+instance Eq Bytes where
+  {-# INLINE (==) #-}
+  Bytes arr1 off1 len1 == Bytes arr2 off2 len2
+    | len1 /= len2 = False
+    | sameByteArray arr1 arr2 && off1 == off2 = True
+    | otherwise = compareByteArrays arr1 off1 arr2 off2 len1 == EQ
+
+instance Ord Bytes where
+  {-# INLINE compare #-}
+  compare (Bytes arr1 off1 len1) (Bytes arr2 off2 len2)
+    | sameByteArray arr1 arr2 && off1 == off2 && len1 == len2 = EQ
+    | otherwise = compareByteArrays arr1 off1 arr2 off2 len1
+
+compareByteArrays :: ByteArray -> Int -> ByteArray -> Int -> Int -> Ordering
+{-# INLINE compareByteArrays #-}
+compareByteArrays (ByteArray ba1#) (I# off1#) (ByteArray ba2#) (I# off2#) (I# n#) =
+  compare (I# (compareByteArrays# ba1# off1# ba2# off2# n#)) 0
+
+sameByteArray :: ByteArray -> ByteArray -> Bool
+{-# INLINE sameByteArray #-}
+sameByteArray (ByteArray ba1#) (ByteArray ba2#) =
+  isTrue# (sameMutableByteArray# (unsafeCoerce# ba1#) (unsafeCoerce# ba2#))
