diff --git a/CBOR.cabal b/CBOR.cabal
new file mode 100644
--- /dev/null
+++ b/CBOR.cabal
@@ -0,0 +1,60 @@
+name:                CBOR
+version:             0.1.0.0
+synopsis:            Encode/Decode values to/from CBOR
+description:         Provides a simple type to represent CBOR values as well 
+                     as instances of the Get and Put classes from the binary 
+                     package to encode\/decode those values to\/from 
+                     ByteStrings. /N.B./ Alpha, interface subject to change!
+homepage:            https://github.com/orclev/CBOR
+license:             LGPL-3
+license-file:        LICENSE
+author:              R. Kyle Murphy
+maintainer:          orclev@gmail.com
+-- copyright:           
+category:            Data
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+
+library
+  exposed-modules:   Data.CBOR, Data.Binary.CBOR
+  other-modules:     Data.CBOR.Util
+  ghc-options:       -Wall -O2
+  build-depends:       base >=4.6 && < 5
+                     , bytestring >= 0.10
+                     , binary >= 0.7
+                     , binary-bits >= 0.3
+                     , data-binary-ieee754 >= 0.4
+                       
+  hs-source-dirs:    src
+  default-language:    Haskell2010
+
+test-suite tests
+  type:              exitcode-stdio-1.0
+  hs-source-dirs:    test, src
+  main-is:           Suite.hs
+  default-language:    Haskell2010
+  build-depends:       base >= 4.6 && < 5
+                     , bytestring >= 0.10
+                     , binary >= 0.7
+                     , binary-bits >= 0.3
+                     , data-binary-ieee754 >= 0.4
+                     , test-framework >= 0.3.3
+                     , test-framework-quickcheck2 >= 0.3
+                     , QuickCheck >= 2.6
+
+test-suite doctests
+  type:              exitcode-stdio-1.0
+  hs-source-dirs:    test, src
+  main-is:           doctests.hs
+  default-language:    Haskell2010
+  build-depends:       base >= 4.6 && < 5
+                     , bytestring >= 0.10
+                     , binary >= 0.7
+                     , binary-bits >= 0.3
+                     , data-binary-ieee754 >= 0.4
+                     , doctest >= 0.9
+
+source-repository head
+  type:     git
+  location: https://github.com/orclev/CBOR.git
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,165 @@
+GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+CBOR
+====
+
+Haskell encoder/decoder for CBOR encoding.
+
+See http://tools.ietf.org/html/rfc7049 for details.
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/src/Data/Binary/CBOR.hs b/src/Data/Binary/CBOR.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Binary/CBOR.hs
@@ -0,0 +1,215 @@
+module Data.Binary.CBOR (
+  getCBOR, putCBOR
+) where
+
+import Data.CBOR
+import Data.CBOR.Util
+import Prelude hiding (take)
+import Data.Word
+import Data.Binary
+import Data.Binary.Get
+import Data.Binary.Put
+import Data.Binary.IEEE754 hiding (getFloat16be)
+import Data.Binary.IEEE754.HalfFloat
+import qualified Data.Binary.Bits.Get as B
+import qualified Data.Binary.Bits.Put as B
+import Control.Applicative
+import Control.Monad (replicateM)
+import qualified Data.ByteString as BS
+
+-- $setup
+-- >>> import qualified Data.ByteString.Lazy as LBS
+
+-- | Reads a header byte and parses it into a tuple of type and extra data values.
+--
+-- >>> let x = LBS.pack [214]
+-- >>> runGet getHeaderBlock x
+-- (6,22)
+getHeaderBlock :: Get (Word8, Word8)
+getHeaderBlock = B.runBitGet . B.block $ (,) <$> B.word8 3 <*> B.word8 5
+
+-- | Writes a header byte given a type and extra data value.
+--
+-- >>> LBS.unpack $ runPut $ putHeaderBlock 6 22
+-- [214]
+putHeaderBlock :: Word8 -> Word8 -> Put
+putHeaderBlock a b = B.runBitPut (B.putWord8 3 a >> B.putWord8 5 b)
+
+-- | Reads a header byte as well as any associated size data contained in the following bytes.
+--
+-- >>> let x = LBS.pack [26,111,122,133,144]
+-- >>> runGet getHeader x
+-- (0,1870300560)
+getHeader :: Integral a => Get (Word8, a)
+getHeader = do
+  (a, b) <- getHeaderBlock
+  ((,) a) <$> getSize b
+
+-- | Writes a header byte as well as an associated number of bytes indicating the size of the following data.
+--
+-- >>> LBS.unpack $ runPut (putHeader 0 1870300560)
+-- [26,111,122,133,144]
+putHeader :: Integral a => Word8 -> a -> Put
+putHeader a b | b >= 4294967296 || b <= -4294967297 = putHeaderBlock a 27 >> putWord64be (toInt $ neg b)
+              | b >= 65536 || b <= -65537 = putHeaderBlock a 26 >> putWord32be (toInt $ neg b)
+              | b >= 256 || b <= -257 = putHeaderBlock a 25 >> putWord16be (toInt $ neg b)
+              | b >= 24 || b <= -25 = putHeaderBlock a 24 >> putWord8 (toInt $ neg b)
+              | otherwise = putHeaderBlock a (toInt $ neg b)
+  where
+    neg x = if x < 0 then (x + 1) * (-1) else x 
+
+-- | Reads an appropriate number of bytes for the extra data provided as the first argument.
+--
+-- >>> let x = LBS.pack [111,122,133,144]
+-- >>> runGet (getSize 26) x
+-- 1870300560
+getSize :: Integral a => Word8 -> Get a
+getSize 31 = return $ -1
+getSize 30 = fail "Additional information of 30 undefined for this type."
+getSize 29 = fail "Additional information of 29 undefined for this type."
+getSize 28 = fail "Additional information of 28 undefined for this type."
+getSize 27 = toInt <$> getWord64be
+getSize 26 = toInt <$> getWord32be
+getSize 25 = toInt <$> getWord16be
+getSize 24 = toInt <$> getWord8
+getSize x = return $ toInt x
+
+-- | Reads CBOR encoded data
+--
+-- >>> let x = LBS.pack [26,111,122,133,144]
+-- >>> runGet getCBOR x
+-- CBOR_UInt 1870300560
+getCBOR :: Get CBOR
+getCBOR = do
+  (x, _) <- lookAhead getHeaderBlock
+  case x of
+    0 -> getUnsignedInt
+    1 -> getNegativeInt
+    2 -> getBS
+    3 -> getTextString
+    4 -> getArray
+    5 -> getMap
+    6 -> getTag
+    7 -> getOther
+    _ -> fail "Unknown CBOR type"
+
+-- | Writes CBOR encoded data
+--
+-- >>> let x = CBOR_Array [CBOR_UInt 42, CBOR_Float 3.14]
+-- >>> LBS.unpack $ runPut (putCBOR x)
+-- [130,24,42,250,64,72,245,195]
+putCBOR :: CBOR -> Put
+putCBOR (CBOR_UInt x) = putHeader 0 x
+putCBOR (CBOR_SInt x) = putHeader 1 x
+putCBOR (CBOR_BS x) = putHeader 2 (BS.length x) >> putByteString x
+putCBOR (CBOR_TS x) = putHeader 3 (BS.length x) >> putByteString x
+putCBOR (CBOR_Array x) = putHeader 4 (length x) >> mapM_ putCBOR x
+putCBOR (CBOR_Map x) = putHeader 5 (length x) >> mapM_ putPair x
+putCBOR (CBOR_Tag x y) = putHeader 6 x >> putCBOR y 
+putCBOR CBOR_False = putHeaderBlock 7 20
+putCBOR CBOR_True = putHeaderBlock 7 21
+putCBOR CBOR_NULL = putHeaderBlock 7 22
+putCBOR CBOR_Undefined = putHeaderBlock 7 23
+putCBOR (CBOR_Byte x) = putHeaderBlock 7 24 >> putWord8 x
+putCBOR (CBOR_HalfFloat x) = putHeaderBlock 7 25 >> putFloat16be x
+putCBOR (CBOR_Float x) = putHeaderBlock 7 26 >> putFloat32be x
+putCBOR (CBOR_Double x) = putHeaderBlock 7 27 >> putFloat64be x 
+putCBOR (CBOR_Reserved 28) = putHeaderBlock 7 28
+putCBOR (CBOR_Reserved 29) = putHeaderBlock 7 29
+putCBOR (CBOR_Reserved 30) = putHeaderBlock 7 30
+putCBOR (CBOR_Reserved _) = fail "Invalid reserved value"
+putCBOR CBOR_Stop = putHeaderBlock 7 31
+putCBOR (CBOR_Unassigned x) = putHeaderBlock 7 (toInt x)
+
+putPair :: (CBOR, CBOR) -> Put
+putPair (a,b) = putCBOR a >> putCBOR b
+
+getUnsignedInt :: Get CBOR
+getUnsignedInt = do
+  CBOR_UInt . snd <$> getHeader
+
+getNegativeInt :: Get CBOR
+getNegativeInt = CBOR_SInt . neg . snd <$> getHeader
+  where 
+    neg a = -1 - a
+
+getBS :: Get CBOR
+getBS = do
+  (_, x) <- getHeaderBlock
+  case x of
+    31 -> CBOR_BS <$> chunk
+    _ -> CBOR_BS <$> (getSize x >>= getByteString)
+  where
+    chunk = do
+      h <- getHeaderBlock
+      case h of
+        (7,31) -> return BS.empty
+        (2, x) -> BS.append <$> (getSize x >>= getByteString) <*> chunk
+        _ -> fail "Illegal chunk type found in indefinite bytestring"
+
+
+getTextString :: Get CBOR
+getTextString = do
+  (_, x) <- getHeaderBlock
+  case x of
+    31 -> CBOR_TS <$> chunk
+    _ -> CBOR_TS <$> (getSize x >>= getByteString)
+  where
+    chunk = do
+      h <- getHeaderBlock
+      case h of
+        (7,31) -> return BS.empty
+        (3, x) -> BS.append <$> (getSize x >>= getByteString) <*> chunk
+        _ -> fail "Illegal chunk type found in indefinite textstring"
+
+getArray :: Get CBOR
+getArray = do
+  (_, x) <- getHeaderBlock
+  case x of
+    31 -> CBOR_Array <$> chunk
+    _ -> CBOR_Array <$> (getSize x >>= (flip replicateM) getCBOR)
+  where
+    chunk = do
+      h <- getHeaderBlock
+      case h of
+        (7,31) -> return []
+        _ -> (:) <$> getCBOR <*> chunk
+
+getMap :: Get CBOR
+getMap = do
+  (_, x) <- getHeaderBlock
+  case x of
+    31 -> CBOR_Map <$> chunk
+    _ -> CBOR_Map <$> (getSize x >>= (flip replicateM) getPair)
+  where
+    chunk = do
+      h <- getHeaderBlock
+      case h of
+        (7,31) -> return []
+        _ -> (:) <$> getPair <*> chunk
+
+getPair :: Get (CBOR,CBOR)
+getPair = (,) <$> getCBOR <*> getCBOR
+
+getTag :: Get CBOR
+getTag = do
+  (_, x) <- getHeaderBlock
+  CBOR_Tag <$> getSize x <*> getCBOR
+
+getOther :: Get CBOR
+getOther = do
+  (_, x) <- getHeaderBlock
+  case x of
+    20 -> return CBOR_False
+    21 -> return CBOR_True
+    22 -> return CBOR_NULL
+    23 -> return CBOR_Undefined
+    24 -> CBOR_Byte <$> getWord8
+    25 -> CBOR_HalfFloat <$> getFloat16be
+    26 -> CBOR_Float <$> getFloat32be
+    27 -> CBOR_Double <$> getFloat64be
+    28 -> return $ CBOR_Reserved 28
+    29 -> return $ CBOR_Reserved 29
+    30 -> return $ CBOR_Reserved 30
+    31 -> return CBOR_Stop
+    x' -> return $ CBOR_Unassigned (toInt x')
diff --git a/src/Data/CBOR.hs b/src/Data/CBOR.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/CBOR.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE BangPatterns, GeneralizedNewtypeDeriving #-}
+-- | Provides functions to serialize CBOR encoded values to/from ByteStrings.
+--
+--   Warning, this package is very alpha quality right now. I've attempted to
+--   implement a useful subset of the CBOR standard, but the interface is still
+--   being refined and subject to change. Do not use this for anything important!
+module Data.CBOR ( CBOR(..)
+                 , HalfFloat(..)
+                 ) where
+
+import Data.Word
+import qualified Data.ByteString as BS
+
+-- | Data type for CBOR values
+data CBOR =   CBOR_UInt Integer
+            | CBOR_SInt Integer
+            | CBOR_BS BS.ByteString
+            | CBOR_TS BS.ByteString -- ^ TextString. This SHOULD be a UTF8 string if the spec is being followed.
+            | CBOR_Array [CBOR]
+            | CBOR_Map [(CBOR,CBOR)]
+            | CBOR_Tag Integer CBOR
+            | CBOR_HalfFloat HalfFloat -- ^ N.B. Currently stored as an opaque value because I can't be bothered to implement my own half width float type.
+            | CBOR_Float Float
+            | CBOR_Double Double
+            | CBOR_NULL
+            | CBOR_Undefined -- ^ Undefined as per the CBOR specification, a value of this type will usually represent a serializatoin error.
+            | CBOR_Reserved Int -- ^ Reserved, you probably shouldn't see any of these.
+            | CBOR_Unassigned Int -- ^ Unassigned
+            | CBOR_True
+            | CBOR_False
+            | CBOR_Byte Word8
+            | CBOR_Stop -- ^ Stop marker for indefinite encoding scheme
+            deriving (Show, Eq)
+
+newtype HalfFloat = HF Word16 -- ^ Opaque value, actual implementation TODO
+  deriving (Eq, Ord)
+
+instance Show HalfFloat where
+  show (HF _) = "HalfFloat" -- N.B. We don't really support rendering of half width floats
diff --git a/src/Data/CBOR/Util.hs b/src/Data/CBOR/Util.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/CBOR/Util.hs
@@ -0,0 +1,39 @@
+module Data.CBOR.Util (
+  toInt, decodeType, packType, minorBits, majorBits
+) where
+
+import Data.Bits
+import Data.Word
+
+-- | Bit mask for type portion of header byte
+majorBits :: Word8
+majorBits = 224 -- 11100000
+
+-- | Bit mask for extra data portion of header byte
+minorBits :: Word8
+minorBits = 31 -- 00011111
+
+-- | Convenience function to coerce one kind of integral into another
+--
+-- >>> let x = 42 :: Word8
+-- >>> toInt x :: Int
+-- 42
+toInt :: (Integral a, Num b) => a -> b
+toInt = fromInteger . toInteger
+
+-- | Parses a CBOR header byte to extract the type and extra data
+--
+-- >>> decodeType 214
+-- (6,22)
+decodeType :: Word8 -> (Word8, Word8)
+decodeType x = (x `shiftR` 5, x .&. minorBits)
+
+-- | Takes a tuple containing a type and extra data value and packs them into a CBOR header byte
+--
+-- >>> packType (6,22)
+-- 214
+packType :: (Word8, Word8) -> Word8
+packType (mt, eb) = mt' .|. eb'
+  where
+    mt' = (mt `shiftL` 5) .&. majorBits
+    eb' = eb .&. minorBits
diff --git a/test/Suite.hs b/test/Suite.hs
new file mode 100644
--- /dev/null
+++ b/test/Suite.hs
@@ -0,0 +1,113 @@
+module Main where
+import Test.QuickCheck
+import qualified Data.ByteString as BS
+import Data.Word
+import Data.CBOR
+import Data.CBOR.Util
+import Data.Binary.CBOR
+import Test.Framework
+-- import Test.QuickCheck.Property
+import Test.Framework.Providers.QuickCheck2
+import Data.Binary.Get (runGet)
+import Data.Binary.Put (runPut)
+import Control.Applicative
+import Data.Monoid
+
+
+main :: IO ()
+main = defaultMain tests
+-- main = defaultMainWithOpts tests runnerOptions
+
+runnerOptions :: RunnerOptions' Maybe
+runnerOptions = mempty {
+    ropt_test_options = Just testOptions
+  }
+
+testOptions :: TestOptions' Maybe
+testOptions = mempty {
+      topt_maximum_generated_tests = Just 5
+    , topt_maximum_unsuitable_generated_tests = Just 1
+    , topt_maximum_test_size = Just 1
+    , topt_maximum_test_depth = Just 1
+  }
+
+tests :: [Test]
+tests = [testGroup "Group1" [
+    testProperty "round_trip" prop_roundtrip
+  ]]
+
+prop_roundtrip :: CBOR -> Bool
+prop_roundtrip x = x == runGet getCBOR (runPut $ putCBOR x)
+
+instance Arbitrary HalfFloat where
+  arbitrary = HF <$> arbitrary
+--instance Arbitrary BS.ByteString where
+--  arbitrary :: Gen BS.ByteString
+--  arbitrary = 
+
+instance Arbitrary CBOR where
+  arbitrary = sized sizedCBOR
+
+sizedCBOR :: Int -> Gen CBOR
+sizedCBOR 0 = 
+  oneof [
+      CBOR_UInt <$> choose (0, toInt (maxBound :: Word64))
+      , CBOR_SInt <$> (-) (-1) <$> choose (0, toInt (maxBound :: Word64))
+      , CBOR_BS <$> arbitraryByteString
+      , CBOR_TS <$> arbitraryTextString
+      , CBOR_HalfFloat <$> arbitrary
+      , CBOR_Float <$> arbitrary
+      , CBOR_Double <$> arbitrary
+      , return CBOR_NULL
+      , return CBOR_Undefined
+      , CBOR_Reserved <$> oneof [return 28,return 29,return 30]
+      , CBOR_Unassigned <$> oneof (map return $ [0..19])
+      , return CBOR_True
+      , return CBOR_False
+      , CBOR_Byte <$> arbitrary
+  ]
+sizedCBOR n = 
+  oneof [
+        CBOR_UInt <$> choose (0, toInt (maxBound :: Word64))
+      , CBOR_SInt <$> (-) (-1) <$> choose (0, toInt (maxBound :: Word64))
+      , CBOR_BS <$> arbitraryByteString
+      , CBOR_TS <$> arbitraryTextString
+      , CBOR_Array <$> listOf1 (sizedCBOR $ n `div` 2)
+      , CBOR_Map <$> listOf1 ((,) <$> (sizedCBOR $ n `div` 2) `suchThat` allowedKeyTypes <*> (sizedCBOR $ n `div` 2))
+      , CBOR_Tag <$> choose (0, toInt (maxBound :: Word64)) <*> (sizedCBOR $ n `div` 2)
+      , CBOR_HalfFloat <$> arbitrary
+      , CBOR_Float <$> arbitrary
+      , CBOR_Double <$> arbitrary
+      , return CBOR_NULL
+      , return CBOR_Undefined
+      , CBOR_Reserved <$> oneof [return 28,return 29,return 30]
+      , CBOR_Unassigned <$> oneof (map return $ [0..19])
+      , return CBOR_True
+      , return CBOR_False
+      , CBOR_Byte <$> arbitrary
+    ]
+
+allowedKeyTypes :: CBOR -> Bool
+allowedKeyTypes x = case x of
+  (CBOR_UInt _) -> True
+  (CBOR_SInt _) -> True
+  (CBOR_BS _) -> True
+  (CBOR_TS _) -> True
+  (CBOR_Tag _ y) -> allowedKeyTypes y
+  (CBOR_HalfFloat _) -> True
+  (CBOR_Float _) -> True
+  (CBOR_Double _) -> True
+  (CBOR_Reserved _) -> True
+  (CBOR_Unassigned _) -> True
+  CBOR_True -> True
+  CBOR_False -> True
+  CBOR_NULL -> True
+  (CBOR_Byte _) -> True
+  _ -> False
+
+arbitraryByteString :: Gen BS.ByteString
+arbitraryByteString = BS.pack <$> listOf1 arbitrary
+
+arbitraryTextString :: Gen BS.ByteString
+arbitraryTextString = BS.pack <$> listOf1 (choose (0, 0x79))
+  -- Technically it can be higher than this, but then it must be encoded as multiple bytes 
diff --git a/test/doctests.hs b/test/doctests.hs
new file mode 100644
--- /dev/null
+++ b/test/doctests.hs
@@ -0,0 +1,3 @@
+import Test.DocTest
+main :: IO ()
+main = doctest ["-isrc", "src/Data/CBOR.hs", "src/Data/CBOR/Util.hs", "src/Data/Binary/CBOR.hs"]
