diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,12 @@
+# Changelog
+
+`builder` uses [PVP Versioning][1].
+The changelog is available [on GitHub][2].
+
+0.0.0
+=====
+
+* Initially created.
+
+[1]: https://pvp.haskell.org
+[2]: https://github.com/chessai/builder/releases
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2019, chessai
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. 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.
+
+3. Neither the name of the copyright holder nor the names of its
+   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 HOLDER 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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# builder
+
+[![Hackage](https://img.shields.io/hackage/v/builder.svg)](https://hackage.haskell.org/package/builder)
+[![BSD3 license](https://img.shields.io/badge/license-BSD3-blue.svg)](LICENSE)
+[![Build status](https://secure.travis-ci.org/chessai/builder.svg)](https://travis-ci.org/chessai/builder)
+
+bounded bytearray builder type
diff --git a/builder.cabal b/builder.cabal
new file mode 100644
--- /dev/null
+++ b/builder.cabal
@@ -0,0 +1,56 @@
+cabal-version: 2.2
+name:
+  builder
+version:
+  0.1
+synopsis:
+  bounded ByteArray builder type
+description:
+  A Builder type for ByteArray. Appending these builders
+  can be cheaper than when appending ByteArray values, since
+  only one buffer allocation is performed.
+homepage:
+  https://github.com/chessai/builder
+bug-reports:
+  https://github.com/chessai/builder/issues
+license:
+  BSD-3-Clause
+license-file:
+  LICENSE
+author:
+  chessai
+maintainer:
+  chessai <chessai1996@gmail.com>
+copyright:
+  © 2019 chessai
+category:
+  Data
+build-type:
+  Simple
+extra-doc-files:
+    README.md
+  , CHANGELOG.md
+tested-with:
+  GHC == 8.6.5
+
+library
+  hs-source-dirs:
+    src
+  exposed-modules:
+    Builder
+  build-depends:
+    , base >= 4.11 && < 4.14
+    , byte-order >= 0.1.1 && < 0.2
+    , primitive >= 0.6.4 && < 0.8
+    , primitive-unaligned >= 0.1 && < 0.2
+  ghc-options:
+    -Wall
+    -O2
+  default-language:
+    Haskell2010
+
+source-repository head
+  type:
+    git
+  location:
+    https://github.com/chessai/builder.git
diff --git a/src/Builder.hs b/src/Builder.hs
new file mode 100644
--- /dev/null
+++ b/src/Builder.hs
@@ -0,0 +1,211 @@
+{-# language
+    BangPatterns
+  , MagicHash
+  , RankNTypes
+  , UnboxedTuples
+  #-}
+
+-- | A 'Builder' type for 'ByteArray'. Appending these builders
+--   can be cheaper than when appending 'ByteArray' values, since
+--   you only perform one buffer allocation.
+module Builder
+  ( -- * Builder type
+    Builder
+
+    -- * Consumption
+  , build
+
+    -- * Construction
+  , buildUnaligned
+  , buildWord8
+  , buildWord16
+  , buildWord32
+  , buildWord64
+  , buildWord
+  , buildInt8
+  , buildInt16
+  , buildInt32
+  , buildInt64
+  , buildInt
+  , buildFloat
+  , buildDouble
+  , buildChar
+  , buildPtr
+  , buildByteArray
+  , buildFixed
+  ) where
+
+import Data.Primitive hiding (writeByteArray)
+import Data.Primitive.ByteArray.Unaligned
+import Data.Int
+import Data.Word
+import GHC.Exts hiding (build)
+import System.ByteOrder
+
+newtype Writer s = W
+  { _w :: ()
+      => MutableByteArray# s
+      -- buffer
+      -> Int#
+      -- offset into buffer
+      -> (State# s -> (# State# s, Int# #))
+      -- update to offset
+  }
+
+instance Semigroup (Writer s) where
+  W x <> W y = W $ \marr# ix0# s0# -> case x marr# ix0# s0# of
+    (# s1#, ix1# #) -> y marr# ix1# s1#
+  {-# inline (<>) #-}
+
+instance Monoid (Writer s) where
+  mempty = W $ \_ ix0# s0# -> (# s0#, ix0# #)
+  {-# inline mempty #-}
+
+data BI s = BI !Int !(Writer s)
+
+instance Semigroup (BI s) where
+  BI len0 w0 <> BI len1 w1 = BI (len0 + len1) (w0 <> w1)
+  {-# inline (<>) #-}
+
+instance Monoid (BI s) where
+  mempty = BI 0 mempty
+  {-# inline mempty #-}
+
+-- | A 'Builder' for 'ByteArray's that has O(1) append.
+--   To create a 'ByteArray', use 'build'. This will only
+--   do one allocation.
+newtype Builder = Builder (forall s. BI s)
+
+instance Semigroup Builder where
+  Builder b0 <> Builder b1 = Builder (b0 <> b1)
+  {-# inline (<>) #-}
+
+instance Monoid Builder where
+  mempty = Builder mempty
+  {-# inline mempty #-}
+
+runWriter# :: Int# -> Writer s -> State# s -> (# State# s, ByteArray# #)
+runWriter# sz# (W g) = \s0# -> case newByteArray# sz# s0# of
+  (# s1#, marr# #) -> case g marr# 0# s1# of
+    (# s2#, _ #) -> case unsafeFreezeByteArray# marr# s2# of
+      (# s3#, b# #) -> (# s3#, b# #)
+{-# inline runWriter# #-}
+
+-- | Convert a 'Builder' into a 'ByteArray'.
+build :: Builder -> ByteArray
+build (Builder (BI (I# len#) w)) = case runRW# (runWriter# len# w) of
+  (# _, b# #) -> ByteArray b#
+{-# inline build #-}
+
+writeUnaligned :: (Prim a, PrimUnaligned a)
+  => a
+  -> Writer s
+writeUnaligned a = W $ \marr# ix0# s0# ->
+  case writeUnalignedByteArray# marr# ix0# a s0# of
+    s1# -> (# s1#, ix0# +# alignment# a #)
+{-# inline writeUnaligned #-}
+
+writeByteArray :: ()
+  => ByteArray
+  -> Int
+  -> Int
+  -> Writer s
+writeByteArray (ByteArray src#) (I# off#) (I# len#)
+  = W $ \marr# ix0# s0# ->
+      case copyByteArray# src# off# marr# ix0# len# s0# of
+        s1# -> (# s1#, ix0# +# len# #)
+{-# inline writeByteArray #-}
+
+-- | A 'Builder' for any 'Prim' and 'PrimUnaligned' value.
+buildUnaligned :: (Prim a, PrimUnaligned a)
+  => a
+  -> Builder
+buildUnaligned a = Builder (BI (sizeOf a) (writeUnaligned a))
+{-# inline buildUnaligned #-}
+
+-- | A 'Builder' for 'Word8'.
+buildWord8 :: Word8 -> Builder
+buildWord8 = buildUnaligned
+{-# inline buildWord8 #-}
+
+-- | A 'Builder' for 'Word16'.
+buildWord16 :: Word16 -> Builder
+buildWord16 = buildUnaligned
+{-# inline buildWord16 #-}
+
+-- | A 'Builder' for 'Word32'.
+buildWord32 :: Word32 -> Builder
+buildWord32 = buildUnaligned
+{-# inline buildWord32 #-}
+
+-- | A 'Builder' for 'Word64'.
+buildWord64 :: Word64 -> Builder
+buildWord64 = buildUnaligned
+{-# inline buildWord64 #-}
+
+-- | A 'Builder' for 'Word'.
+buildWord :: Word -> Builder
+buildWord = buildUnaligned
+{-# inline buildWord #-}
+
+-- | A 'Builder' for 'Int8'.
+buildInt8 :: Int8 -> Builder
+buildInt8 = buildUnaligned
+{-# inline buildInt8 #-}
+
+-- | A 'Builder' for 'Int16'.
+buildInt16 :: Int16 -> Builder
+buildInt16 = buildUnaligned
+{-# inline buildInt16 #-}
+
+-- | A 'Builder' for 'Int32'.
+buildInt32 :: Int32 -> Builder
+buildInt32 = buildUnaligned
+{-# inline buildInt32 #-}
+
+-- | A 'Builder' for 'Int64'.
+buildInt64 :: Int64 -> Builder
+buildInt64 = buildUnaligned
+{-# inline buildInt64 #-}
+
+-- | A 'Builder' for 'Int'.
+buildInt :: Int -> Builder
+buildInt = buildUnaligned
+{-# inline buildInt #-}
+
+-- | A 'Builder' for 'Char'.
+buildChar :: Char -> Builder
+buildChar = buildUnaligned
+{-# inline buildChar #-}
+
+-- | A 'Builder' for a 'ByteArray' slice.
+buildByteArray :: ()
+  => ByteArray -- ^ source array
+  -> Int -- ^ offset into source array
+  -> Int -- ^ number of bytes to copy
+  -> Builder
+buildByteArray b o n = Builder (BI n (writeByteArray b o n))
+{-# inline buildByteArray #-}
+
+-- | A 'Builder' for 'Float'.
+buildFloat :: Float -> Builder
+buildFloat = buildUnaligned
+{-# inline buildFloat #-}
+
+-- | A 'Builder' for 'Double'.
+buildDouble :: Double -> Builder
+buildDouble = buildUnaligned
+{-# inline buildDouble #-}
+
+-- | A 'Builder' for @'Ptr' a@.
+buildPtr :: Ptr a -> Builder
+buildPtr = buildUnaligned
+{-# inline buildPtr #-}
+
+-- | A 'Builder' for @'Fixed' b a@
+--   This provides better control over endianness when writing.
+buildFixed :: (FixedOrdering b, Bytes a, Prim a, PrimUnaligned a)
+  => Fixed b a
+  -> Builder
+buildFixed = buildUnaligned
+{-# inline buildFixed #-}
