diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,16 @@
 # ChangeLog
 
+## 0.4.3
+
+* Now builds with primitive >= 0.6.4.0
+
+## 0.4.2
+
+* Adds `unsafeMakePokeState`, `unsafeMakePeekState`, and
+  `maybeAlignmentBufferSize`, so that library users can write their own
+  `encode` / `decode` functions.
+  See [#126](https://github.com/fpco/store/pull/126)
+
 ## 0.4.1
 
 * Less aggressive inlining, resulting in faster compilation / simplifier
diff --git a/src/Data/Store/Core.hs b/src/Data/Store/Core.hs
--- a/src/Data/Store/Core.hs
+++ b/src/Data/Store/Core.hs
@@ -30,6 +30,8 @@
     , pokeFromForeignPtr, peekToPlainForeignPtr, pokeFromPtr
       -- * ByteArray
     , pokeFromByteArray, peekToByteArray
+      -- * Creation of PokeState / PeekState
+    , unsafeMakePokeState, unsafeMakePeekState, maybeAlignmentBufferSize
     ) where
 
 import           Control.Applicative
@@ -40,14 +42,14 @@
 import           Data.ByteString (ByteString)
 import qualified Data.ByteString.Internal as BS
 import           Data.Monoid ((<>))
-import           Data.Primitive.ByteArray
+import           Data.Primitive.ByteArray (ByteArray, MutableByteArray(..), newByteArray, unsafeFreezeByteArray)
 import qualified Data.Text as T
 import           Data.Typeable
 import           Data.Word
 import           Foreign.ForeignPtr (ForeignPtr, withForeignPtr, castForeignPtr)
 import           Foreign.Ptr
 import           Foreign.Storable as Storable
-import           GHC.Prim (unsafeCoerce#, RealWorld, copyByteArrayToAddr#, copyAddrToByteArray#)
+import           GHC.Prim (unsafeCoerce#, RealWorld, ByteArray#, copyByteArrayToAddr#, copyAddrToByteArray#)
 import           GHC.Ptr (Ptr(..))
 import           GHC.Types (IO(..), Int(..))
 import           Prelude
@@ -138,6 +140,23 @@
     }
 #endif
 
+-- | Make a 'PokeState' from a buffer pointer.
+--
+-- The first argument is a pointer to the memory to write to. The second
+-- argument is an IO action which is invoked if the store-core package
+-- was built with the @force-alignment@ flag. The action should yield a
+-- pointer to scratch memory as large as 'maybeAlignmentBufferSize'.
+--
+-- Since 0.4.2
+unsafeMakePokeState :: Ptr Word8 -- ^ pokeStatePtr
+                    -> IO (Ptr Word8) -- ^ action to produce pokeStateAlignPtr
+                    -> IO PokeState
+#if ALIGNED_MEMORY
+unsafeMakePokeState ptr f = PokeState ptr =<< f
+#else
+unsafeMakePokeState ptr _ = return $ PokeState ptr
+#endif
+
 -- | Exception thrown while running 'poke'. Note that other types of
 -- exceptions could also be thrown. Invocations of 'fail' in the 'Poke'
 -- monad causes this exception to be thrown.
@@ -243,6 +262,23 @@
     { peekStateEndPtr :: Ptr Word8 }
 #endif
 
+-- | Make a 'PeekState' from a buffer pointer.
+--
+-- The first argument is a pointer to the memory to write to. The second
+-- argument is an IO action which is invoked if the store-core package
+-- was built with the @force-alignment@ flag. The action should yield a
+-- pointer to scratch memory as large as 'maybeAlignmentBufferSize'.
+--
+-- Since 0.4.2
+unsafeMakePeekState :: Ptr Word8 -- ^ peekStateEndPtr
+                    -> IO (Ptr Word8) -- ^ action to produce peekStateAlignPtr
+                    -> IO PeekState
+#if ALIGNED_MEMORY
+unsafeMakePeekState ptr f = PeekState ptr =<< f
+#else
+unsafeMakePeekState ptr _ = return $ PeekState ptr
+#endif
+
 -- | Exception thrown while running 'peek'. Note that other types of
 -- exceptions can also be thrown. Invocations of 'fail' in the 'Poke'
 -- monad causes this exception to be thrown.
@@ -317,6 +353,18 @@
 #if ALIGNED_MEMORY
 alignBufferSize :: Int
 alignBufferSize = 32
+#endif
+
+-- | If store-core is built with the @force-alignment@ flag, then this
+-- will be a 'Just' value indicating the amount of memory that is
+-- expected in the alignment buffer used by 'PeekState' and 'PokeState'.
+-- Currently this will either be @Just 32@ or @Nothing@.
+maybeAlignmentBufferSize :: Maybe Int
+maybeAlignmentBufferSize =
+#if ALIGNED_MEMORY
+  Just alignBufferSize
+#else
+  Nothing
 #endif
 
 -- | Checks if the offset matches the expected length, and throw a
diff --git a/store-core.cabal b/store-core.cabal
--- a/store-core.cabal
+++ b/store-core.cabal
@@ -1,9 +1,11 @@
--- This file has been generated from package.yaml by hpack version 0.17.0.
+-- This file has been generated from package.yaml by hpack version 0.28.2.
 --
 -- see: https://github.com/sol/hpack
+--
+-- hash: b3ccbe532560b303c9ece1b2c15d8ac87b4a29a903a5d36d1266987bec548296
 
 name:           store-core
-version:        0.4.1
+version:        0.4.3
 synopsis:       Fast and lightweight binary serialization
 category:       Serialization, Data
 homepage:       https://github.com/fpco/store#readme
@@ -14,7 +16,6 @@
 license-file:   LICENSE
 build-type:     Simple
 cabal-version:  >= 1.10
-
 extra-source-files:
     ChangeLog.md
 
@@ -27,19 +28,23 @@
   default: False
 
 library
+  exposed-modules:
+      Data.Store.Core
+  other-modules:
+      Paths_store_core
   hs-source-dirs:
       src
   ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -O2
   build-depends:
       base >=4.7 && <5
-    , primitive >=0.6 && < 1.0
-    , bytestring >=0.10.4.0 && < 1.0
-    , fail
-    , transformers >=0.3.0.0 && < 1.0
-    , ghc-prim >=0.3.1.0 && < 1.0
-    , text >=1.2.0.4 && < 2.0
+    , bytestring >=0.10.4.0 && <1.0
+    , ghc-prim >=0.3.1.0 && <1.0
+    , primitive >=0.6 && <1.0
+    , text >=1.2.0.4 && <2.0
+    , transformers >=0.3.0.0 && <1.0
   if flag(force-alignment) || arch(PPC) || arch(PPC64) || arch(Mips) || arch(Sparc) || arch(Arm)
     cpp-options: -DALIGNED_MEMORY
-  exposed-modules:
-      Data.Store.Core
+  if impl(ghc < 8.0)
+    build-depends:
+        fail
   default-language: Haskell2010
