diff --git a/Data/Aeson/Parser/Internal.hs b/Data/Aeson/Parser/Internal.hs
--- a/Data/Aeson/Parser/Internal.hs
+++ b/Data/Aeson/Parser/Internal.hs
@@ -36,34 +36,23 @@
 import Prelude ()
 import Prelude.Compat
 
-import Control.Monad.IO.Class (liftIO)
 import Data.Aeson.Types.Internal (IResult(..), JSONPath, Result(..), Value(..))
 import Data.Attoparsec.ByteString.Char8 (Parser, char, endOfInput, scientific, skipSpace, string)
+import Data.Attoparsec.ByteString.Char8 (Parser, char, endOfInput, scientific, skipSpace, string)
 import Data.Bits ((.|.), shiftL)
 import Data.ByteString.Internal (ByteString(..))
 import Data.Char (chr)
 import Data.Text (Text)
-import Data.Text.Encoding (decodeUtf8')
-import Data.Text.Internal.Encoding.Utf8 (ord2, ord3, ord4)
-import Data.Text.Internal.Unsafe.Char (ord)
-import Data.Vector as Vector (Vector, empty, fromList, reverse)
-import Data.Word (Word8)
-import Foreign.ForeignPtr (withForeignPtr)
-import Foreign.Marshal.Utils (copyBytes)
-import Foreign.Ptr (Ptr, minusPtr, plusPtr)
-import Foreign.Storable (poke)
-import System.IO.Unsafe (unsafePerformIO)
+import Data.Vector as Vector (Vector, empty, fromListN, reverse)
 import qualified Data.Attoparsec.ByteString as A
 import qualified Data.Attoparsec.Lazy as L
-import qualified Data.Attoparsec.Zepto as Z
 import qualified Data.ByteString as B
-import qualified Data.ByteString.Internal as B
 import qualified Data.ByteString.Lazy as L
-import qualified Data.ByteString.Unsafe as B
 import qualified Data.HashMap.Strict as H
+import Data.Aeson.Parser.Unescape
 
 #if MIN_VERSION_ghc_prim(0,3,1)
-import GHC.Base (Int#, (==#), isTrue#, orI#, word2Int#)
+import GHC.Base (Int#, (==#), isTrue#, word2Int#)
 import GHC.Word (Word8(W8#))
 #endif
 
@@ -126,16 +115,18 @@
   w <- A.peekWord8'
   if w == CLOSE_CURLY
     then A.anyWord8 >> return H.empty
-    else loop H.empty
+    else loop []
  where
-  loop m0 = do
+  -- Why use acc pattern here, you may ask? because 'H.fromList' use 'unsafeInsert'
+  -- and it's much faster because it's doing in place update to the 'HashMap'!
+  loop acc = do
     k <- str <* skipSpace <* char ':'
     v <- val <* skipSpace
-    let !m = H.insert k v m0
     ch <- A.satisfy $ \w -> w == COMMA || w == CLOSE_CURLY
+    let acc' = (k, v) : acc
     if ch == COMMA
-      then skipSpace >> loop m
-      else return m
+      then skipSpace >> loop acc'
+      else return (H.fromList acc')
 {-# INLINE objectValues #-}
 
 array_ :: Parser Value
@@ -152,14 +143,14 @@
   w <- A.peekWord8'
   if w == CLOSE_SQUARE
     then A.anyWord8 >> return Vector.empty
-    else loop []
+    else loop [] 1
   where
-    loop acc = do
+    loop acc !len = do
       v <- val <* skipSpace
       ch <- A.satisfy $ \w -> w == COMMA || w == CLOSE_SQUARE
       if ch == COMMA
-        then skipSpace >> loop (v:acc)
-        else return (Vector.reverse (Vector.fromList (v:acc)))
+        then skipSpace >> loop (v:acc) (len+1)
+        else return (Vector.reverse (Vector.fromListN len (v:acc)))
 {-# INLINE arrayValues #-}
 
 -- | Parse any JSON value.  You should usually 'json' in preference to
@@ -215,101 +206,30 @@
 jstring_ :: Parser Text
 {-# INLINE jstring_ #-}
 jstring_ = {-# SCC "jstring_" #-} do
-  (s, fin) <- A.runScanner startState go
-  _ <- A.anyWord8
-  s1 <- if isEscaped fin
-        then case unescape s of
-               Right r  -> return r
-               Left err -> fail err
-        else return s
-  case decodeUtf8' s1 of
+  s <- A.scan startState go <* A.anyWord8
+  case unescapeText s of
     Right r  -> return r
     Left err -> fail $ show err
  where
 #if MIN_VERSION_ghc_prim(0,3,1)
-    isEscaped (S _ escaped) = isTrue# escaped
-    startState              = S 0# 0#
-    go (S a b) (W8# c)
-      | isTrue# a                     = Just (S 0# b)
+    startState              = S 0#
+    go (S a) (W8# c)
+      | isTrue# a                     = Just (S 0#)
       | isTrue# (word2Int# c ==# 34#) = Nothing   -- double quote
       | otherwise = let a' = word2Int# c ==# 92#  -- backslash
-                    in Just (S a' (orI# a' b))
+                    in Just (S a')
 
-data S = S Int# Int#
+data S = S Int#
 #else
-    isEscaped (S _ escaped) = escaped
-    startState              = S False False
-    go (S a b) c
-      | a                  = Just (S False b)
+    startState              = False
+    go a c
+      | a                  = Just False
       | c == DOUBLE_QUOTE  = Nothing
       | otherwise = let a' = c == backslash
-                    in Just (S a' (a' || b))
+                    in Just a'
       where backslash = BACKSLASH
-
-data S = S !Bool !Bool
 #endif
 
-unescape :: ByteString -> Either String ByteString
-unescape s = unsafePerformIO $ do
-  let len = B.length s
-  fp <- B.mallocByteString len
-  -- We perform no bounds checking when writing to the destination
-  -- string, as unescaping always makes it shorter than the source.
-  withForeignPtr fp $ \ptr -> do
-    ret <- Z.parseT (go ptr) s
-    case ret of
-      Left err -> return (Left err)
-      Right p -> do
-        let newlen = p `minusPtr` ptr
-            slop = len - newlen
-        Right <$> if slop >= 128 && slop >= len `quot` 4
-                  then B.create newlen $ \np -> copyBytes np ptr newlen
-                  else return (PS fp 0 newlen)
- where
-  go ptr = do
-    h <- Z.takeWhile (/=BACKSLASH)
-    let rest = do
-          start <- Z.take 2
-          let !slash = B.unsafeHead start
-              !t = B.unsafeIndex start 1
-              escape = case B.elemIndex t "\"\\/ntbrfu" of
-                         Just i -> i
-                         _      -> 255
-          if slash /= BACKSLASH || escape == 255
-            then fail "invalid JSON escape sequence"
-            else
-            if t /= 117 -- 'u'
-              then copy h ptr >>= word8 (B.unsafeIndex mapping escape) >>= go
-              else do
-                   a <- hexQuad
-                   if a < 0xd800 || a > 0xdfff
-                     then copy h ptr >>= charUtf8 (chr a) >>= go
-                     else do
-                       b <- Z.string "\\u" *> hexQuad
-                       if a <= 0xdbff && b >= 0xdc00 && b <= 0xdfff
-                         then let !c = ((a - 0xd800) `shiftL` 10) +
-                                       (b - 0xdc00) + 0x10000
-                              in copy h ptr >>= charUtf8 (chr c) >>= go
-                         else fail "invalid UTF-16 surrogates"
-    done <- Z.atEnd
-    if done
-      then copy h ptr
-      else rest
-  mapping = "\"\\/\n\t\b\r\f"
-
-hexQuad :: Z.ZeptoT IO Int
-hexQuad = do
-  s <- Z.take 4
-  let hex n | w >= C_0 && w <= C_9 = w - C_0
-            | w >= C_a && w <= C_f = w - 87
-            | w >= C_A && w <= C_F = w - 55
-            | otherwise          = 255
-        where w = fromIntegral $ B.unsafeIndex s n
-      a = hex 0; b = hex 1; c = hex 2; d = hex 3
-  if (a .|. b .|. c .|. d) /= 255
-    then return $! d .|. (c `shiftL` 4) .|. (b `shiftL` 8) .|. (a `shiftL` 12)
-    else fail "invalid hex escape"
-
 decodeWith :: Parser Value -> (Value -> Result a) -> L.ByteString -> Maybe a
 decodeWith p to s =
     case L.parse p s of
@@ -373,38 +293,3 @@
 -- end-of-input.  See also: 'json''.
 jsonEOF' :: Parser Value
 jsonEOF' = json' <* skipSpace <* endOfInput
-
-word8 :: Word8 -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
-word8 w ptr = do
-  liftIO $ poke ptr w
-  return $! ptr `plusPtr` 1
-
-copy :: ByteString -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
-copy (PS fp off len) ptr =
-  liftIO . withForeignPtr fp $ \src -> do
-    copyBytes ptr (src `plusPtr` off) len
-    return $! ptr `plusPtr` len
-
-charUtf8 :: Char -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
-charUtf8 ch ptr
-  | ch < '\x80'   = liftIO $ do
-                       poke ptr (fromIntegral (ord ch))
-                       return $! ptr `plusPtr` 1
-  | ch < '\x800'  = liftIO $ do
-                       let (a,b) = ord2 ch
-                       poke ptr a
-                       poke (ptr `plusPtr` 1) b
-                       return $! ptr `plusPtr` 2
-  | ch < '\xffff' = liftIO $ do
-                       let (a,b,c) = ord3 ch
-                       poke ptr a
-                       poke (ptr `plusPtr` 1) b
-                       poke (ptr `plusPtr` 2) c
-                       return $! ptr `plusPtr` 3
-  | otherwise     = liftIO $ do
-                       let (a,b,c,d) = ord4 ch
-                       poke ptr a
-                       poke (ptr `plusPtr` 1) b
-                       poke (ptr `plusPtr` 2) c
-                       poke (ptr `plusPtr` 3) d
-                       return $! ptr `plusPtr` 4
diff --git a/Data/Aeson/Parser/Unescape.hs b/Data/Aeson/Parser/Unescape.hs
new file mode 100644
--- /dev/null
+++ b/Data/Aeson/Parser/Unescape.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+{-# LANGUAGE MagicHash                #-}
+{-# LANGUAGE UnliftedFFITypes         #-}
+
+module Data.Aeson.Parser.Unescape (
+  unescapeText
+) where
+
+import           Control.Exception          (evaluate, throw, try)
+import           Control.Monad.ST.Unsafe    (unsafeIOToST, unsafeSTToIO)
+import           Data.ByteString            as B
+import           Data.ByteString.Internal   as B hiding (c2w)
+import qualified Data.Text.Array            as A
+import           Data.Text.Encoding.Error   (UnicodeException (..))
+import           Data.Text.Internal         (Text (..))
+import           Data.Text.Internal.Private (runText)
+import           Data.Text.Unsafe           (unsafeDupablePerformIO)
+import           Data.Word                  (Word8)
+import           Foreign.C.Types            (CInt (..), CSize (..))
+import           Foreign.ForeignPtr         (withForeignPtr)
+import           Foreign.Marshal.Utils      (with)
+import           Foreign.Ptr                (Ptr, plusPtr)
+import           Foreign.Storable           (peek)
+import           GHC.Base                   (MutableByteArray#)
+
+foreign import ccall unsafe "_js_decode_string" c_js_decode
+    :: MutableByteArray# s -> Ptr CSize
+    -> Ptr Word8 -> Ptr Word8 -> IO CInt
+
+unescapeText' :: ByteString -> Text
+unescapeText' (PS fp off len) = runText $ \done -> do
+  let go dest = withForeignPtr fp $ \ptr ->
+        with (0::CSize) $ \destOffPtr -> do
+          let end = ptr `plusPtr` (off + len)
+              loop curPtr = do
+                res <- c_js_decode (A.maBA dest) destOffPtr curPtr end
+                case res of
+                  0 -> do
+                    n <- peek destOffPtr
+                    unsafeSTToIO (done dest (fromIntegral n))
+                  _ ->
+                    throw (DecodeError desc Nothing)
+          loop (ptr `plusPtr` off)
+  (unsafeIOToST . go) =<< A.new len
+ where
+  desc = "Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream"
+{-# INLINE unescapeText' #-}
+
+unescapeText :: ByteString -> Either UnicodeException Text
+unescapeText = unsafeDupablePerformIO . try . evaluate . unescapeText'
+{-# INLINE unescapeText #-}
diff --git a/Data/Aeson/Types/FromJSON.hs b/Data/Aeson/Types/FromJSON.hs
--- a/Data/Aeson/Types/FromJSON.hs
+++ b/Data/Aeson/Types/FromJSON.hs
@@ -87,7 +87,7 @@
 import Data.Int (Int16, Int32, Int64, Int8)
 import Data.List.NonEmpty (NonEmpty(..))
 import Data.Maybe (fromMaybe)
-import Data.Monoid ((<>), Dual(..), First (..), Last (..))
+import Data.Monoid ((<>))
 import Data.Proxy (Proxy(..))
 import Data.Ratio ((%), Ratio)
 import Data.Scientific (Scientific)
@@ -104,6 +104,7 @@
 import GHC.Generics
 import Numeric.Natural (Natural)
 import Text.ParserCombinators.ReadP (readP_to_S)
+import Unsafe.Coerce (unsafeCoerce)
 import qualified Data.Aeson.Parser.Time as Time
 import qualified Data.Attoparsec.ByteString.Char8 as A (endOfInput, parseOnly, scientific)
 import qualified Data.DList as DList
@@ -112,7 +113,9 @@
 import qualified Data.IntMap as IntMap
 import qualified Data.IntSet as IntSet
 import qualified Data.Map as M
+import qualified Data.Monoid as Monoid
 import qualified Data.Scientific as Scientific
+import qualified Data.Semigroup as Semigroup
 import qualified Data.Sequence as Seq
 import qualified Data.Set as Set
 import qualified Data.Text as T
@@ -124,7 +127,6 @@
 import qualified Data.Vector.Primitive as VP
 import qualified Data.Vector.Storable as VS
 import qualified Data.Vector.Unboxed as VU
-import Unsafe.Coerce (unsafeCoerce)
 
 #ifndef HAS_COERCIBLE
 #define HAS_COERCIBLE (__GLASGOW_HASKELL__ >= 707)
@@ -655,9 +657,9 @@
                Just v  -> parseJSON v <?> Key key
 {-# INLINE (.:) #-}
 
--- | Retrieve the value associated with the given key of an 'Object'.  The
--- result is 'Nothing' if the key is not present or value is 'Null', or 'empty'
--- if the value cannot be converted to the desired type.
+-- | Retrieve the value associated with the given key of an 'Object'. The
+-- result is 'Nothing' if the key is not present or if its value is 'Null',
+-- or 'empty' if the value cannot be converted to the desired type.
 --
 -- This accessor is most useful if the key and value can be absent
 -- from an object without affecting its validity.  If the key and
@@ -668,8 +670,12 @@
                Just v  -> parseJSON v <?> Key key
 {-# INLINE (.:?) #-}
 
--- | Like '.:?', but the resulting parser will fail,
--- if the key is present but is 'Null'.
+-- | Retrieve the value associated with the given key of an 'Object'.
+-- The result is 'Nothing' if the key is not present or 'empty' if the
+-- value cannot be converted to the desired type.
+--
+-- This differs from '.:?' by attempting to parse 'Null' the same as any
+-- other JSON value, instead of interpreting it as 'Nothing'.
 (.:!) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
 obj .:! key = case H.lookup key obj of
                Nothing -> pure Nothing
@@ -1605,29 +1611,113 @@
 -- base Monoid/Semigroup
 -------------------------------------------------------------------------------
 
-instance FromJSON1 Dual where
-    liftParseJSON p _ = fmap Dual . p
+instance FromJSON1 Monoid.Dual where
+    liftParseJSON p _ = fmap Monoid.Dual . p
     {-# INLINE liftParseJSON #-}
 
-instance FromJSON a => FromJSON (Dual a) where
+instance FromJSON a => FromJSON (Monoid.Dual a) where
     parseJSON = parseJSON1
     {-# INLINE parseJSON #-}
 
 
-instance FromJSON1 First where
-    liftParseJSON p p' = fmap First . liftParseJSON p p'
+instance FromJSON1 Monoid.First where
+    liftParseJSON p p' = fmap Monoid.First . liftParseJSON p p'
     {-# INLINE liftParseJSON #-}
 
-instance FromJSON a => FromJSON (First a) where
+instance FromJSON a => FromJSON (Monoid.First a) where
     parseJSON = parseJSON1
     {-# INLINE parseJSON #-}
 
 
-instance FromJSON1 Last where
-    liftParseJSON p p' = fmap Last . liftParseJSON p p'
+instance FromJSON1 Monoid.Last where
+    liftParseJSON p p' = fmap Monoid.Last . liftParseJSON p p'
     {-# INLINE liftParseJSON #-}
 
-instance FromJSON a => FromJSON (Last a) where
+instance FromJSON a => FromJSON (Monoid.Last a) where
+    parseJSON = parseJSON1
+    {-# INLINE parseJSON #-}
+
+
+instance FromJSON1 Semigroup.Min where
+    liftParseJSON p _ a = Semigroup.Min <$> p a
+    {-# INLINE liftParseJSON #-}
+
+    liftParseJSONList _ p a = fmap Semigroup.Min <$> p a
+    {-# INLINE liftParseJSONList #-}
+
+instance (FromJSON a) => FromJSON (Semigroup.Min a) where
+    parseJSON = parseJSON1
+    {-# INLINE parseJSON #-}
+
+    parseJSONList = liftParseJSONList parseJSON parseJSONList
+    {-# INLINE parseJSONList #-}
+
+
+instance FromJSON1 Semigroup.Max where
+    liftParseJSON p _ a = Semigroup.Max <$> p a
+    {-# INLINE liftParseJSON #-}
+
+    liftParseJSONList _ p a = fmap Semigroup.Max <$> p a
+    {-# INLINE liftParseJSONList #-}
+
+instance (FromJSON a) => FromJSON (Semigroup.Max a) where
+    parseJSON = parseJSON1
+    {-# INLINE parseJSON #-}
+
+    parseJSONList = liftParseJSONList parseJSON parseJSONList
+    {-# INLINE parseJSONList #-}
+
+
+instance FromJSON1 Semigroup.First where
+    liftParseJSON p _ a = Semigroup.First <$> p a
+    {-# INLINE liftParseJSON #-}
+
+    liftParseJSONList _ p a = fmap Semigroup.First <$> p a
+    {-# INLINE liftParseJSONList #-}
+
+instance (FromJSON a) => FromJSON (Semigroup.First a) where
+    parseJSON = parseJSON1
+    {-# INLINE parseJSON #-}
+
+    parseJSONList = liftParseJSONList parseJSON parseJSONList
+    {-# INLINE parseJSONList #-}
+
+
+instance FromJSON1 Semigroup.Last where
+    liftParseJSON p _ a = Semigroup.Last <$> p a
+    {-# INLINE liftParseJSON #-}
+
+    liftParseJSONList _ p a = fmap Semigroup.Last <$> p a
+    {-# INLINE liftParseJSONList #-}
+
+instance (FromJSON a) => FromJSON (Semigroup.Last a) where
+    parseJSON = parseJSON1
+    {-# INLINE parseJSON #-}
+
+    parseJSONList = liftParseJSONList parseJSON parseJSONList
+    {-# INLINE parseJSONList #-}
+
+
+instance FromJSON1 Semigroup.WrappedMonoid where
+    liftParseJSON p _ a = Semigroup.WrapMonoid <$> p a
+    {-# INLINE liftParseJSON #-}
+
+    liftParseJSONList _ p a = fmap Semigroup.WrapMonoid <$> p a
+    {-# INLINE liftParseJSONList #-}
+
+instance (FromJSON a) => FromJSON (Semigroup.WrappedMonoid a) where
+    parseJSON = parseJSON1
+    {-# INLINE parseJSON #-}
+
+    parseJSONList = liftParseJSONList parseJSON parseJSONList
+    {-# INLINE parseJSONList #-}
+
+
+instance FromJSON1 Semigroup.Option where
+    liftParseJSON p p' = fmap Semigroup.Option . liftParseJSON p p'
+    {-# INLINE liftParseJSON #-}
+
+instance FromJSON a => FromJSON (Semigroup.Option a) where
     parseJSON = parseJSON1
     {-# INLINE parseJSON #-}
 
diff --git a/Data/Aeson/Types/ToJSON.hs b/Data/Aeson/Types/ToJSON.hs
--- a/Data/Aeson/Types/ToJSON.hs
+++ b/Data/Aeson/Types/ToJSON.hs
@@ -78,7 +78,7 @@
 import Data.Int (Int16, Int32, Int64, Int8)
 import Data.List (intersperse)
 import Data.List.NonEmpty (NonEmpty(..))
-import Data.Monoid ((<>), Dual(..), First(..), Last(..))
+import Data.Monoid ((<>))
 import Data.Proxy (Proxy(..))
 import Data.Ratio (Ratio, denominator, numerator)
 import Data.Scientific (Scientific)
@@ -104,7 +104,9 @@
 import qualified Data.IntSet as IntSet
 import qualified Data.List.NonEmpty as NE
 import qualified Data.Map as M
+import qualified Data.Monoid as Monoid
 import qualified Data.Scientific as Scientific
+import qualified Data.Semigroup as Semigroup
 import qualified Data.Sequence as Seq
 import qualified Data.Set as Set
 import qualified Data.Text as T
@@ -2026,14 +2028,14 @@
 -- base Monoid/Semigroup
 -------------------------------------------------------------------------------
 
-instance ToJSON1 Dual where
-    liftToJSON t _ = t . getDual
+instance ToJSON1 Monoid.Dual where
+    liftToJSON t _ = t . Monoid.getDual
     {-# INLINE liftToJSON #-}
 
-    liftToEncoding t _ = t . getDual
+    liftToEncoding t _ = t . Monoid.getDual
     {-# INLINE liftToEncoding #-}
 
-instance ToJSON a => ToJSON (Dual a) where
+instance ToJSON a => ToJSON (Monoid.Dual a) where
     toJSON = toJSON1
     {-# INLINE toJSON #-}
 
@@ -2041,14 +2043,14 @@
     {-# INLINE toEncoding #-}
 
 
-instance ToJSON1 First where
-    liftToJSON t to' = liftToJSON t to' . getFirst
+instance ToJSON1 Monoid.First where
+    liftToJSON t to' = liftToJSON t to' . Monoid.getFirst
     {-# INLINE liftToJSON #-}
 
-    liftToEncoding t to' = liftToEncoding t to' . getFirst
+    liftToEncoding t to' = liftToEncoding t to' . Monoid.getFirst
     {-# INLINE liftToEncoding #-}
 
-instance ToJSON a => ToJSON (First a) where
+instance ToJSON a => ToJSON (Monoid.First a) where
     toJSON = toJSON1
     {-# INLINE toJSON #-}
 
@@ -2056,14 +2058,103 @@
     {-# INLINE toEncoding #-}
 
 
-instance ToJSON1 Last where
-    liftToJSON t to' = liftToJSON t to' . getLast
+instance ToJSON1 Monoid.Last where
+    liftToJSON t to' = liftToJSON t to' . Monoid.getLast
     {-# INLINE liftToJSON #-}
 
-    liftToEncoding t to' = liftToEncoding t to' . getLast
+    liftToEncoding t to' = liftToEncoding t to' . Monoid.getLast
     {-# INLINE liftToEncoding #-}
 
-instance ToJSON a => ToJSON (Last a) where
+instance ToJSON a => ToJSON (Monoid.Last a) where
+    toJSON = toJSON1
+    {-# INLINE toJSON #-}
+
+    toEncoding = toEncoding1
+    {-# INLINE toEncoding #-}
+
+
+instance ToJSON1 Semigroup.Min where
+    liftToJSON t _ (Semigroup.Min x) = t x
+    {-# INLINE liftToJSON #-}
+
+    liftToEncoding t _ (Semigroup.Min x) = t x
+    {-# INLINE liftToEncoding #-}
+
+instance ToJSON a => ToJSON (Semigroup.Min a) where
+    toJSON = toJSON1
+    {-# INLINE toJSON #-}
+
+    toEncoding = toEncoding1
+    {-# INLINE toEncoding #-}
+
+
+instance ToJSON1 Semigroup.Max where
+    liftToJSON t _ (Semigroup.Max x) = t x
+    {-# INLINE liftToJSON #-}
+
+    liftToEncoding t _ (Semigroup.Max x) = t x
+    {-# INLINE liftToEncoding #-}
+
+instance ToJSON a => ToJSON (Semigroup.Max a) where
+    toJSON = toJSON1
+    {-# INLINE toJSON #-}
+
+    toEncoding = toEncoding1
+    {-# INLINE toEncoding #-}
+
+instance ToJSON1 Semigroup.First where
+    liftToJSON t _ (Semigroup.First x) = t x
+    {-# INLINE liftToJSON #-}
+
+    liftToEncoding t _ (Semigroup.First x) = t x
+    {-# INLINE liftToEncoding #-}
+
+instance ToJSON a => ToJSON (Semigroup.First a) where
+    toJSON = toJSON1
+    {-# INLINE toJSON #-}
+
+    toEncoding = toEncoding1
+    {-# INLINE toEncoding #-}
+
+
+instance ToJSON1 Semigroup.Last where
+    liftToJSON t _ (Semigroup.Last x) = t x
+    {-# INLINE liftToJSON #-}
+
+    liftToEncoding t _ (Semigroup.Last x) = t x
+    {-# INLINE liftToEncoding #-}
+
+instance ToJSON a => ToJSON (Semigroup.Last a) where
+    toJSON = toJSON1
+    {-# INLINE toJSON #-}
+
+    toEncoding = toEncoding1
+    {-# INLINE toEncoding #-}
+
+
+instance ToJSON1 Semigroup.WrappedMonoid where
+    liftToJSON t _ (Semigroup.WrapMonoid x) = t x
+    {-# INLINE liftToJSON #-}
+
+    liftToEncoding t _ (Semigroup.WrapMonoid x) = t x
+    {-# INLINE liftToEncoding #-}
+
+instance ToJSON a => ToJSON (Semigroup.WrappedMonoid a) where
+    toJSON = toJSON1
+    {-# INLINE toJSON #-}
+
+    toEncoding = toEncoding1
+    {-# INLINE toEncoding #-}
+
+
+instance ToJSON1 Semigroup.Option where
+    liftToJSON t to' = liftToJSON t to' . Semigroup.getOption
+    {-# INLINE liftToJSON #-}
+
+    liftToEncoding t to' = liftToEncoding t to' . Semigroup.getOption
+    {-# INLINE liftToEncoding #-}
+
+instance ToJSON a => ToJSON (Semigroup.Option a) where
     toJSON = toJSON1
     {-# INLINE toJSON #-}
 
diff --git a/aeson.cabal b/aeson.cabal
--- a/aeson.cabal
+++ b/aeson.cabal
@@ -1,5 +1,5 @@
 name:            aeson
-version:         1.0.0.0
+version:         1.0.1.0
 license:         BSD3
 license-file:    LICENSE
 category:        Text, Web, JSON
@@ -52,6 +52,7 @@
     examples/*.hs
     examples/Twitter/*.hs
     include/*.h
+    cbits/*.c
 
 flag developer
   description: operate in developer mode
@@ -91,6 +92,7 @@
     Data.Aeson.Encoding.Builder
     Data.Aeson.Internal.Functions
     Data.Aeson.Parser.Internal
+    Data.Aeson.Parser.Unescape
     Data.Aeson.Parser.Time
     Data.Aeson.Types.FromJSON
     Data.Aeson.Types.Generic
@@ -146,6 +148,7 @@
     ghc-options: -O2
 
   include-dirs: include
+  c-sources:  cbits/unescape_string.c
 
 test-suite tests
   default-language: Haskell2010
@@ -170,7 +173,7 @@
 
   build-depends:
     HUnit,
-    QuickCheck >= 2.7 && <2.8.3,
+    QuickCheck >= 2.7 && <2.9.2,
     aeson,
     attoparsec,
     base,
@@ -178,7 +181,7 @@
     base-orphans >= 0.5.3 && <0.6,
     containers,
     dlist,
-    generic-deriving >= 1.10 && < 1.11,
+    generic-deriving >= 1.10 && < 1.12,
     ghc-prim >= 0.2,
     hashable >= 1.2.4.0,
     scientific,
diff --git a/benchmarks/aeson-benchmarks.cabal b/benchmarks/aeson-benchmarks.cabal
--- a/benchmarks/aeson-benchmarks.cabal
+++ b/benchmarks/aeson-benchmarks.cabal
@@ -11,28 +11,27 @@
 
 library
   hs-source-dirs: .. .
-
+  c-sources:  ../cbits/unescape_string.c
   exposed-modules:
     Data.Aeson
     Data.Aeson.Encoding
-    Data.Aeson.Parser
-    Data.Aeson.Text
-    Data.Aeson.Types
-    Data.Aeson.TH
-
+    Data.Aeson.Encoding.Builder
     Data.Aeson.Encoding.Internal
     Data.Aeson.Internal
-    Data.Aeson.Internal.Time
-
-    Data.Aeson.Encoding.Builder
     Data.Aeson.Internal.Functions
+    Data.Aeson.Internal.Time
+    Data.Aeson.Parser
     Data.Aeson.Parser.Internal
     Data.Aeson.Parser.Time
+    Data.Aeson.Parser.Unescape
+    Data.Aeson.TH
+    Data.Aeson.Text
+    Data.Aeson.Types
+    Data.Aeson.Types.Class
     Data.Aeson.Types.FromJSON
     Data.Aeson.Types.Generic
-    Data.Aeson.Types.ToJSON
-    Data.Aeson.Types.Class
     Data.Aeson.Types.Internal
+    Data.Aeson.Types.ToJSON
 
   build-depends:
     attoparsec >= 0.13.0.1,
diff --git a/cbits/unescape_string.c b/cbits/unescape_string.c
new file mode 100644
--- /dev/null
+++ b/cbits/unescape_string.c
@@ -0,0 +1,150 @@
+// Copyright (c) 2008-2009 Bjoern Hoehrmann
+// Copyright (c) 2015, Ondrej Palkovsky
+// Copyright (c) 2016, Winterland
+
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+
+
+#define UTF8_ACCEPT 0
+#define UTF8_REJECT 12
+
+static const uint8_t utf8d[] = {
+  // The first part of the table maps bytes to character classes that
+  // to reduce the size of the transition table and create bitmasks.
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+   8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+
+  // The second part is a transition table that maps a combination
+  // of a state of the automaton and a character class to a state.
+   0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
+  12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
+  12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
+  12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
+  12,36,12,12,12,12,12,12,12,12,12,12,
+};
+
+static inline uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
+  uint32_t type = utf8d[byte];
+
+  *codep = (*state != UTF8_ACCEPT) ?
+    (byte & 0x3fu) | (*codep << 6) :
+    (0xff >> type) & (byte);
+
+  *state = utf8d[256 + *state + type];
+  return *state;
+}
+
+static inline uint16_t decode_hex(uint32_t c)
+{
+  if (c >= '0' && c <= '9')      return c - '0';
+  else if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+  else if (c >= 'A' && c <= 'F') return c - 'A' + 10;
+  return 0xFFFF; // Should not happen
+}
+
+// Decode, return non-zero value on error
+int _js_decode_string(uint16_t *const dest, size_t *destoff,
+                  const uint8_t *s, const uint8_t *const srcend)
+{
+  uint16_t *d = dest + *destoff;
+  uint32_t state = 0;
+  uint32_t codepoint;
+
+  uint8_t surrogate = 0;
+  uint16_t temp_hex = 0;
+  uint16_t unidata;
+
+  // Optimized version of dispatch when just an ASCII char is expected
+  #define DISPATCH_ASCII(label) {\
+    if (s >= srcend) {\
+      return -1;\
+    }\
+    codepoint = *s++;\
+    goto label;\
+  }
+
+  standard:
+    // Test end of stream
+    while (s < srcend) {
+        if (decode(&state, &codepoint, *s++) != UTF8_ACCEPT) {
+          if (state == UTF8_REJECT) { return -1; }
+          continue;
+        }
+
+        if (codepoint == '\\')
+          DISPATCH_ASCII(backslash)
+        else if (codepoint <= 0xffff)
+          *d++ = (uint16_t) codepoint;
+        else {
+          *d++ = (uint16_t) (0xD7C0 + (codepoint >> 10));
+          *d++ = (uint16_t) (0xDC00 + (codepoint & 0x3FF));
+        }
+    }
+    *destoff = d - dest;
+    // Exit point
+    return (state != UTF8_ACCEPT);
+  backslash:
+    switch (codepoint) {
+      case '"':
+      case '\\':
+      case '/':
+        *d++ = (uint16_t) codepoint;
+        goto standard;
+        break;
+      case 'b': *d++ = '\b';goto standard;
+      case 'f': *d++ = '\f';goto standard;
+      case 'n': *d++ = '\n';goto standard;
+      case 'r': *d++ = '\r';goto standard;
+      case 't': *d++ = '\t';goto standard;
+      case 'u': DISPATCH_ASCII(unicode1);;break;
+      default:
+        return -1;
+    }
+  unicode1:
+    temp_hex = decode_hex(codepoint);
+    if (temp_hex == 0xFFFF) { return -1; }
+    else unidata = temp_hex << 12;
+    DISPATCH_ASCII(unicode2);
+  unicode2:
+    temp_hex = decode_hex(codepoint);
+    if (temp_hex == 0xFFFF) { return -1; }
+    else unidata |= temp_hex << 8;
+    DISPATCH_ASCII(unicode3);
+  unicode3:
+    temp_hex = decode_hex(codepoint);
+    if (temp_hex == 0xFFFF) { return -1; }
+    else unidata |= temp_hex << 4;
+    DISPATCH_ASCII(unicode4);
+  unicode4:
+    temp_hex = decode_hex(codepoint);
+    if (temp_hex == 0xFFFF) { return -1; }
+    else unidata |= temp_hex;
+    *d++ = (uint16_t) unidata;
+
+    if (surrogate) {
+      if (unidata <= 0xDC00 || unidata >= 0xDFFF) // is not low surrogate
+        return -1;
+      surrogate = 0;
+    } else if (unidata >= 0xD800 && unidata <= 0xDBFF ) { // is high surrogate
+        surrogate = 1;
+        DISPATCH_ASCII(surrogate1);
+    } else if (unidata >= 0xDC00 && unidata <= 0xDFFF) { // is low surrogate
+        return -1;
+    }
+    goto standard;
+  surrogate1:
+    if (codepoint != '\\') { return -1; }
+    DISPATCH_ASCII(surrogate2)
+  surrogate2:
+    if (codepoint != 'u') { return -1; }
+    DISPATCH_ASCII(unicode1)
+}
+
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,16 @@
 For the latest version of this document, please see [https://github.com/bos/aeson/blob/master/changelog.md](https://github.com/bos/aeson/blob/master/changelog.md).
 
+# 1.0.1.0
+
+* Decoding performance has been significantly improved (see
+  https://github.com/bos/aeson/pull/452).
+
+* Add `ToJSON`/`FromJSON` instances for newtypes from
+  `Data.Semigroup`: `Min`, `Max`, `First`, `Last`, `WrappedMonoid`,
+  `Option`.
+
+* Make the documentation for `.:!` more accurate.
+
 # 1.0.0.0
 
 Major enhancements:
diff --git a/tests/Instances.hs b/tests/Instances.hs
--- a/tests/Instances.hs
+++ b/tests/Instances.hs
@@ -11,24 +11,29 @@
 import Prelude ()
 import Prelude.Compat
 
-import Control.Applicative (Const(..), empty)
+import Control.Applicative (empty)
 import Control.Monad
 import Data.Aeson.Types
 import Data.Function (on)
 import Data.Functor.Compose (Compose (..))
-import Data.Functor.Identity (Identity (..))
-import Data.List.NonEmpty (NonEmpty(..))
 import Data.Proxy (Proxy(..))
 import Data.Tagged (Tagged(..))
 import Data.Time (ZonedTime(..), TimeZone(..))
 import Data.Time.Clock (UTCTime(..))
-import Data.Version
 import Functions
-import Test.QuickCheck (Arbitrary(..), getNonNegative, elements, listOf1, oneof, resize)
+import Test.QuickCheck (Arbitrary(..), elements,  oneof)
 import Types
 import qualified Data.DList as DList
 import qualified Data.HashMap.Strict as HM
 
+#if !MIN_VERSION_QuickCheck(2,9,0)
+import Control.Applicative (Const(..))
+import Data.List.NonEmpty (NonEmpty(..))
+import Data.Functor.Identity (Identity (..))
+import Data.Version
+import Test.QuickCheck (getNonNegative, listOf1, resize)
+#endif
+
 import Data.Orphans ()
 import Test.QuickCheck.Instances ()
 #if MIN_VERSION_base(4,7,0)
@@ -172,21 +177,11 @@
 instance (ApproxEq a) => ApproxEq [a] where
     a =~ b = length a == length b && all (uncurry (=~)) (zip a b)
 
-instance Arbitrary Version where
-    arbitrary = makeVersion . fmap getNonNegative <$> resize 4 (listOf1 arbitrary)
-
-instance Arbitrary a => Arbitrary (NonEmpty a) where
-    arbitrary = (:|) <$> arbitrary <*> arbitrary
-
 -- Version tags are deprecated, so we avoid using them in the Arbitrary
 -- instance. However, the recommended constructor 'makeVersion' is not
 -- exported by "Data.Version" until base-4.8.0.0. For previous versions,
 -- a definition is given below.
 
-#if !MIN_VERSION_base(4,8,0)
-makeVersion :: [Int] -> Version
-makeVersion b = Version b []
-#endif
 
 #if !MIN_VERSION_base(4,8,0) && !MIN_VERSION_QuickCheck(2,8,3)
 instance Arbitrary Natural where
@@ -199,15 +194,27 @@
 instance Arbitrary b => Arbitrary (Tagged a b) where
     arbitrary = Tagged <$> arbitrary
 
+instance Arbitrary a => Arbitrary (DList.DList a) where
+    arbitrary = DList.fromList <$> arbitrary
+
+instance Arbitrary (f (g a)) => Arbitrary (Compose f g a) where
+    arbitrary = Compose <$> arbitrary
+
+#if !MIN_VERSION_QuickCheck(2,9,0)
 instance Arbitrary a => Arbitrary (Const a b) where
     arbitrary = Const <$> arbitrary
 
-instance Arbitrary a => Arbitrary (DList.DList a) where
-    arbitrary = DList.fromList <$> arbitrary
+instance Arbitrary a => Arbitrary (NonEmpty a) where
+    arbitrary = (:|) <$> arbitrary <*> arbitrary
 
--- would like to have -- https://github.com/nick8325/quickcheck/issues/73
+instance Arbitrary Version where
+    arbitrary = makeVersion . fmap getNonNegative <$> resize 4 (listOf1 arbitrary)
+
+#if !MIN_VERSION_base(4,8,0)
+makeVersion :: [Int] -> Version
+makeVersion b = Version b []
+#endif
+
 instance Arbitrary a => Arbitrary (Identity a) where
     arbitrary = Identity <$> arbitrary
-
-instance Arbitrary (f (g a)) => Arbitrary (Compose f g a) where
-    arbitrary = Compose <$> arbitrary
+#endif
diff --git a/tests/UnitTests.hs b/tests/UnitTests.hs
--- a/tests/UnitTests.hs
+++ b/tests/UnitTests.hs
@@ -59,6 +59,8 @@
 import qualified Data.IntMap as IntMap
 import qualified Data.IntSet as IntSet
 import qualified Data.Map as M
+import qualified Data.Monoid as Monoid
+import qualified Data.Semigroup as Semigroup
 import qualified Data.Sequence as Seq
 import qualified Data.Set as Set
 import qualified Data.Text.Lazy as LT
@@ -419,6 +421,18 @@
 
   , Example "MyEither Int String: Left"  "42"      (MyLeft 42     :: MyEither Int String)
   , Example "MyEither Int String: Right" "\"foo\"" (MyRight "foo" :: MyEither Int String)
+
+  -- newtypes from Monoid/Semigroup
+  , Example "Monoid.Dual Int" "2" (pure 2 :: Monoid.Dual Int)
+  , Example "Monoid.First Int" "2" (pure 2 :: Monoid.First Int)
+  , Example "Monoid.Last Int" "2" (pure 2 :: Monoid.Last Int)
+  , Example "Semigroup.Min Int" "2" (pure 2 :: Semigroup.Min Int)
+  , Example "Semigroup.Max Int" "2" (pure 2 :: Semigroup.Max Int)
+  , Example "Semigroup.First Int" "2" (pure 2 :: Semigroup.First Int)
+  , Example "Semigroup.Last Int" "2" (pure 2 :: Semigroup.Last Int)
+  , Example "Semigroup.WrappedMonoid Int" "2" (Semigroup.WrapMonoid 2 :: Semigroup.WrappedMonoid Int)
+  , Example "Semigroup.Option Just" "2" (pure 2 :: Semigroup.Option Int)
+  , Example "Semigroup.Option Nothing" "null" (Semigroup.Option (Nothing :: Maybe Bool))
   ]
 
 
