diff --git a/Data/Attoparsec.hs b/Data/Attoparsec.hs
--- a/Data/Attoparsec.hs
+++ b/Data/Attoparsec.hs
@@ -15,6 +15,9 @@
     -- * Differences from Parsec
     -- $parsec
 
+    -- * Incremental input
+    -- $incremental
+
     -- * Performance considerations
     -- $performance
 
@@ -28,6 +31,7 @@
     -- * Running parsers
     , parse
     , feed
+    , I.parseOnly
     , parseWith
     , parseTest
 
@@ -61,14 +65,19 @@
     , I.takeWhile1
     , I.takeTill
 
+    -- ** Consume all remaining input
+    , I.takeByteString
+    , I.takeLazyByteString
+
     -- * State observation and manipulation functions
     , I.endOfInput
-    , I.ensure
+    , I.atEnd
     ) where
 
 import Data.Attoparsec.Combinator
 import qualified Data.Attoparsec.Internal as I
 import qualified Data.ByteString as B
+import Data.Attoparsec.Internal (Result(..), parse)
 
 -- $parsec
 --
@@ -84,12 +93,11 @@
 -- * Much of the performance advantage of Attoparsec is gained via
 --   high-performance parsers such as 'I.takeWhile' and 'I.string'.
 --   If you use complicated combinators that return lists of bytes or
---   characters, there really isn't much performance difference the
---   two libraries.
+--   characters, there is less performance difference between the two
+--   libraries.
 --
 -- * Unlike Parsec 3, Attoparsec does not support being used as a
---   monad transformer.  This is mostly a matter of the implementor
---   not having needed that functionality.
+--   monad transformer.
 --
 -- * Attoparsec is specialised to deal only with strict 'B.ByteString'
 --   input.  Efficiency concernts rule out both lists and lazy
@@ -97,12 +105,37 @@
 --   allow consumption of very large input without a large footprint.
 --   For this need, Attoparsec's incremental input provides an
 --   excellent substitute, with much more control over when input
---   takes place.
+--   takes place.  If you must use lazy bytestrings, see the 'Lazy'
+--   module, which feeds lazy chunks to a regular parser.
 --
 -- * Parsec parsers can produce more helpful error messages than
 --   Attoparsec parsers.  This is a matter of focus: Attoparsec avoids
 --   the extra book-keeping in favour of higher performance.
 
+-- $incremental
+--
+-- Attoparsec supports incremental input, meaning that you can feed it
+-- a bytestring that represents only part of the expected total amount
+-- of data to parse. If your parser reaches the end of a fragment of
+-- input and could consume more input, it will suspend parsing and
+-- return a 'Partial' continuation.
+--
+-- Supplying the 'Partial' continuation with another bytestring will
+-- resume parsing at the point where it was suspended. You must be
+-- prepared for the result of the resumed parse to be another
+-- 'Partial' continuation.
+--
+-- To indicate that you have no more input, supply the 'Partial'
+-- continuation with an empty bytestring.
+--
+-- Remember that some parsing combinators will not return a result
+-- until they reach the end of input.  They may thus cause 'Partial'
+-- results to be returned.
+--
+-- If you do not need support for incremental input, consider using
+-- the 'I.parseOnly' function to run your parser.  It will never
+-- prompt for more input.
+
 -- $performance
 --
 -- If you write an Attoparsec-based parser carefully, it can be
@@ -146,57 +179,18 @@
 -- The 'Result' type is an instance of 'Functor', where 'fmap'
 -- transforms the value in a 'Done' result.
 
--- | The result of a parse.
-data Result r = Fail !B.ByteString [String] String
-              -- ^ The parse failed.  The 'B.ByteString' is the input
-              -- that had not yet been consumed when the failure
-              -- occurred.  The @[@'String'@]@ is a list of contexts
-              -- in which the error occurred.  The 'String' is the
-              -- message describing the error, if any.
-              | Partial (B.ByteString -> Result r)
-              -- ^ Supply this continuation with more input so that
-              -- the parser can resume.  To indicate that no more
-              -- input is available, use an 'B.empty' string.
-              | Done !B.ByteString r
-              -- ^ The parse succeeded.  The 'B.ByteString' is the
-              -- input that had not yet been consumed (if any) when
-              -- the parse succeeded.
-
-instance Show r => Show (Result r) where
-    show (Fail bs stk msg) =
-        "Fail " ++ show bs ++ " " ++ show stk ++ " " ++ show msg
-    show (Partial _)       = "Partial _"
-    show (Done bs r)       = "Done " ++ show bs ++ " " ++ show r
-
 -- | If a parser has returned a 'Partial' result, supply it with more
 -- input.
 feed :: Result r -> B.ByteString -> Result r
 feed f@(Fail _ _ _) _ = f
 feed (Partial k) d    = k d
 feed (Done bs r) d    = Done (B.append bs d) r
-
-fmapR :: (a -> b) -> Result a -> Result b
-fmapR _ (Fail st stk msg) = Fail st stk msg
-fmapR f (Partial k)       = Partial (fmapR f . k)
-fmapR f (Done bs r)       = Done bs (f r)
-
-instance Functor Result where
-    fmap = fmapR
+{-# INLINE feed #-}
 
 -- | Run a parser and print its result to standard output.
 parseTest :: (Show a) => I.Parser a -> B.ByteString -> IO ()
 parseTest p s = print (parse p s)
 
-translate :: I.Result a -> Result a
-translate (I.Fail st stk msg) = Fail (I.input st) stk msg
-translate (I.Partial k)       = Partial (translate . k)
-translate (I.Done st r)       = Done (I.input st) r
-
--- | Run a parser and return its result.
-parse :: I.Parser a -> B.ByteString -> Result a
-parse m s = translate (I.parse m s)
-{-# INLINE parse #-}
-
 -- | Run a parser with an initial input string, and a monadic action
 -- that can supply more input if needed.
 parseWith :: Monad m =>
@@ -208,10 +202,10 @@
           -> B.ByteString
           -- ^ Initial input for the parser.
           -> m (Result a)
-parseWith refill p s = step $ I.parse p s
-  where step (I.Fail st stk msg) = return $! Fail (I.input st) stk msg
-        step (I.Partial k)       = (step . k) =<< refill
-        step (I.Done st r)       = return $! Done (I.input st) r
+parseWith refill p s = step $ parse p s
+  where step (Partial k) = (step . k) =<< refill
+        step r           = return r
+{-# INLINE parseWith #-}
 
 -- | Convert a 'Result' value to a 'Maybe' value. A 'Partial' result
 -- is treated as failure.
diff --git a/Data/Attoparsec/Char8.hs b/Data/Attoparsec/Char8.hs
--- a/Data/Attoparsec/Char8.hs
+++ b/Data/Attoparsec/Char8.hs
@@ -69,6 +69,10 @@
     , takeWhile1
     , takeTill
 
+    -- ** Consume all remaining input
+    , I.takeByteString
+    , I.takeLazyByteString
+
     -- * Text parsing
     , I.endOfLine
     , isEndOfLine
@@ -79,17 +83,21 @@
     , hexadecimal
     , signed
     , double
+    , Number(..)
+    , number
     , rational
 
     -- * State observation and manipulation functions
     , I.endOfInput
-    , I.ensure
+    , I.atEnd
     ) where
 
 import Control.Applicative ((*>), (<$>), (<|>))
 import Data.Attoparsec.Combinator
 import Data.Attoparsec.FastSet (charClass, memberChar)
 import Data.Attoparsec.Internal (Parser, (<?>))
+import Data.Attoparsec.Number (Number(..))
+import Data.Bits (Bits, (.|.), shiftL)
 import Data.ByteString.Internal (c2w, w2c)
 import Data.Ratio ((%))
 import Data.String (IsString(..))
@@ -328,14 +336,16 @@
 -- @\'a\'@ through @\'f\'@ may be upper or lower case.
 --
 -- This parser does not accept a leading @\"0x\"@ string.
-hexadecimal :: Integral a => Parser a
+hexadecimal :: (Integral a, Bits a) => Parser a
 {-# SPECIALISE hexadecimal :: Parser Int #-}
 hexadecimal = B8.foldl' step 0 `fmap` I.takeWhile1 isHexDigit
-  where isHexDigit w = (w >= 48 && w <= 57) || (x >= 97 && x <= 102)
-            where x = toLower w
-        step a w | w >= 48 && w <= 57  = a * 16 + fromIntegral (w - 48)
-                 | otherwise           = a * 16 + fromIntegral (x - 87)
-            where x = toLower w
+  where
+    isHexDigit w = (w >= 48 && w <= 57) ||
+                   (w >= 97 && w <= 102) ||
+                   (w >= 65 && w <= 90)
+    step a w | w >= 48 && w <= 57  = (a `shiftL` 4) .|. fromIntegral (w - 48)
+             | w >= 97             = (a `shiftL` 4) .|. fromIntegral (w - 87)
+             | otherwise           = (a `shiftL` 4) .|. fromIntegral (w - 55)
 
 -- | Parse and decode an unsigned decimal number.
 decimal :: Integral a => Parser a
@@ -379,7 +389,7 @@
 --
 -- >rational "3.foo" == Done 3.0 ".foo"
 -- >rational "3e"    == Done 3.0 "e"
-rational :: RealFloat a => Parser a
+rational :: Fractional a => Parser a
 {-# SPECIALIZE rational :: Parser Double #-}
 rational = floaty $ \real frac fracDenom -> fromRational $
                      real % 1 + frac % fracDenom
@@ -397,11 +407,29 @@
 -- around the 15th decimal place.  For 0.001% of numbers, this
 -- function will lose precision at the 13th or 14th decimal place.
 double :: Parser Double
-double = floaty $ \real frac fracDenom ->
-                   fromIntegral real +
-                   fromIntegral frac / fromIntegral fracDenom
+double = floaty asDouble
 
-floaty :: RealFloat a => (Integer -> Integer -> Integer -> a) -> Parser a
+asDouble :: Integer -> Integer -> Integer -> Double
+asDouble real frac fracDenom =
+    fromIntegral real + fromIntegral frac / fromIntegral fracDenom
+{-# INLINE asDouble #-}
+
+-- | Parse a number, attempting to preserve both speed and precision.
+--
+-- The syntax accepted by this parser is the same as for 'rational'.
+--
+-- /Note/: This function is almost ten times faster than 'rational'.
+-- On integral inputs, it gives perfectly accurate answers, and on
+-- floating point inputs, it is slightly less accurate than
+-- 'rational'.
+number :: Parser Number
+number = floaty $ \real frac fracDenom ->
+         if frac == 0 && fracDenom == 0
+         then I real
+         else D (asDouble real frac fracDenom)
+{-# INLINE number #-}
+
+floaty :: Fractional a => (Integer -> Integer -> Integer -> a) -> Parser a
 {-# INLINE floaty #-}
 floaty f = do
   let minus = 45
@@ -429,7 +457,7 @@
           else if power == 0
                then f real fraction (10 ^ fracDigits)
                else f real fraction (10 ^ fracDigits) * (10 ^^ power)
-  return $! if sign == plus
-            then n
-            else -n
+  return $ if sign == plus
+           then n
+           else -n
   
diff --git a/Data/Attoparsec/Internal.hs b/Data/Attoparsec/Internal.hs
--- a/Data/Attoparsec/Internal.hs
+++ b/Data/Attoparsec/Internal.hs
@@ -3,7 +3,7 @@
 -- Module      :  Data.Attoparsec.Internal
 -- Copyright   :  Bryan O'Sullivan 2007-2010
 -- License     :  BSD3
--- 
+--
 -- Maintainer  :  bos@serpentine.com
 -- Stability   :  experimental
 -- Portability :  unknown
@@ -16,10 +16,10 @@
     -- * Parser types
       Parser
     , Result(..)
-    , S(input)
 
     -- * Running parsers
     , parse
+    , parseOnly
 
     -- * Combinators
     , (<?>)
@@ -51,8 +51,13 @@
     , takeWhile1
     , takeTill
 
+    -- ** Consume all remaining input
+    , takeByteString
+    , takeLazyByteString
+
     -- * State observation and manipulation functions
     , endOfInput
+    , atEnd
     , ensure
 
     -- * Utilities
@@ -74,62 +79,78 @@
 import qualified Data.ByteString.Char8 as B
 import qualified Data.ByteString.Internal as B
 import qualified Data.ByteString.Unsafe as B
+import qualified Data.ByteString.Lazy as L
 
-data Result r = Fail S [String] String
+-- | The result of a parse.
+data Result r = Fail B.ByteString [String] String
+              -- ^ The parse failed.  The 'B.ByteString' is the input
+              -- that had not yet been consumed when the failure
+              -- occurred.  The @[@'String'@]@ is a list of contexts
+              -- in which the error occurred.  The 'String' is the
+              -- message describing the error, if any.
               | Partial (B.ByteString -> Result r)
-              | Done S r
+              -- ^ Supply this continuation with more input so that
+              -- the parser can resume.  To indicate that no more
+              -- input is available, use an 'B.empty' string.
+              | Done B.ByteString r
+              -- ^ The parse succeeded.  The 'B.ByteString' is the
+              -- input that had not yet been consumed (if any) when
+              -- the parse succeeded.
 
+instance Show r => Show (Result r) where
+    show (Fail bs stk msg) =
+        "Fail " ++ show bs ++ " " ++ show stk ++ " " ++ show msg
+    show (Partial _)       = "Partial _"
+    show (Done bs r)       = "Done " ++ show bs ++ " " ++ show r
+
+fmapR :: (a -> b) -> Result a -> Result b
+fmapR _ (Fail st stk msg) = Fail st stk msg
+fmapR f (Partial k)       = Partial (fmapR f . k)
+fmapR f (Done bs r)       = Done bs (f r)
+
+instance Functor Result where
+    fmap = fmapR
+
+newtype Input = I {unI :: B.ByteString}
+newtype Added = A {unA :: B.ByteString}
+
 -- | The 'Parser' type is a monad.
 newtype Parser a = Parser {
-      runParser :: forall r. S
+      runParser :: forall r. Input -> Added -> More
                 -> Failure   r
                 -> Success a r
                 -> Result r
     }
 
-type Failure   r = S -> [String] -> String -> Result r
-type Success a r = S -> a -> Result r
+type Failure   r = Input -> Added -> More -> [String] -> String -> Result r
+type Success a r = Input -> Added -> More -> a -> Result r
 
 -- | Have we read all available input?
 data More = Complete | Incomplete
             deriving (Eq, Show)
 
-plusMore :: More -> More -> More
-plusMore Complete _ = Complete
-plusMore _ Complete = Complete
-plusMore _ _        = Incomplete
-{-# INLINE plusMore #-}
-
-instance Monoid More where
-    mempty  = Incomplete
-    mappend = plusMore
-
-data S = S {
-      input  :: !B.ByteString
-    , _added :: B.ByteString
-    , more  :: !More
-    } deriving (Show)
-
-instance Show r => Show (Result r) where
-    show (Fail _ stack msg) = "Fail " ++ show stack ++ " " ++ show msg
-    show (Partial _) = "Partial _"
-    show (Done bs r) = "Done " ++ show bs ++ " " ++ show r
-
-addS :: S -> S -> S
-addS (S s0 a0 c0) (S _s1 a1 c1) = S (s0 +++ a1) (a0 +++ a1) (mappend c0 c1)
+addS :: Input -> Added -> More
+     -> Input -> Added -> More
+     -> (Input -> Added -> More -> r) -> r
+addS i0 a0 m0 _i1 a1 m1 f =
+    let !i = I (unI i0 +++ unA a1)
+        a  = A (unA a0 +++ unA a1)
+        m  = m0 <> m1
+    in f i a m
+  where
+    Complete <> _ = Complete
+    _ <> Complete = Complete
+    _ <> _        = Incomplete
 {-# INLINE addS #-}
 
-instance Monoid S where
-    mempty  = S B.empty B.empty Incomplete
-    mappend = addS
-
 bindP :: Parser a -> (a -> Parser b) -> Parser b
 bindP m g =
-    Parser (\st0 kf ks -> runParser m st0 kf (\s a -> runParser (g a) s kf ks))
+    Parser $ \i0 a0 m0 kf ks -> runParser m i0 a0 m0 kf $
+                                \i1 a1 m1 a -> runParser (g a) i1 a1 m1 kf ks
 {-# INLINE bindP #-}
 
 returnP :: a -> Parser a
-returnP a = Parser (\st0 _kf ks -> ks st0 a)
+returnP a = Parser (\i0 a0 m0 _kf ks -> ks i0 a0 m0 a)
 {-# INLINE returnP #-}
 
 instance Monad Parser where
@@ -137,15 +158,16 @@
     (>>=)  = bindP
     fail   = failDesc
 
-noAdds :: S -> S
-noAdds (S s0 _a0 c0) = S s0 B.empty c0
+noAdds :: Input -> Added -> More
+       -> (Input -> Added -> More -> r) -> r
+noAdds i0 _a0 m0 f = f i0 (A B.empty) m0
 {-# INLINE noAdds #-}
 
 plus :: Parser a -> Parser a -> Parser a
-plus a b = Parser $ \st0 kf ks ->
-           let kf' st1 _ _ = runParser b (mappend st0 st1) kf ks
-               !st2 = noAdds st0
-           in  runParser a st2 kf' ks
+plus a b = Parser $ \i0 a0 m0 kf ks ->
+           let kf' i1 a1 m1 _ _ = addS i0 a0 m0 i1 a1 m1 $
+                                  \ i2 a2 m2 -> runParser b i2 a2 m2 kf ks
+           in  noAdds i0 a0 m0 $ \i2 a2 m2 -> runParser a i2 a2 m2 kf' ks
 {-# INLINE plus #-}
 
 instance MonadPlus Parser where
@@ -154,7 +176,8 @@
     mplus = plus
 
 fmapP :: (a -> b) -> Parser a -> Parser b
-fmapP p m = Parser (\st0 f k -> runParser m st0 f (\s a -> k s (p a)))
+fmapP p m = Parser $ \i0 a0 m0 f k ->
+            runParser m i0 a0 m0 f $ \i1 a1 s1 a -> k i1 a1 s1 (p a)
 {-# INLINE fmapP #-}
 
 instance Functor Parser where
@@ -170,7 +193,7 @@
 instance Applicative Parser where
     pure   = returnP
     (<*>)  = apP
-    
+
     -- These definitions are equal to the defaults, but this
     -- way the optimizer doesn't have to work so hard to figure
     -- that out.
@@ -188,48 +211,56 @@
     (<|>) = plus
 
 failDesc :: String -> Parser a
-failDesc err = Parser (\st0 kf _ks -> kf st0 [] msg)
+failDesc err = Parser (\i0 a0 m0 kf _ks -> kf i0 a0 m0 [] msg)
     where msg = "Failed reading: " ++ err
 {-# INLINE failDesc #-}
 
--- | Succeed only if at least @n@ bytes of input are available.
-ensure :: Int -> Parser ()
-ensure n = Parser $ \st0@(S s0 _a0 _c0) kf ks ->
-    if B.length s0 >= n
-    then ks st0 ()
-    else runParser (demandInput >> ensure n) st0 kf ks
+-- | If at least @n@ bytes of input are available, return the current
+-- input, otherwise fail.
+ensure :: Int -> Parser B.ByteString
+ensure !n = Parser $ \i0 a0 m0 kf ks ->
+    if B.length (unI i0) >= n
+    then ks i0 a0 m0 (unI i0)
+    else runParser (demandInput >> ensure n) i0 a0 m0 kf ks
 
 -- | Ask for input.  If we receive any, pass it to a success
 -- continuation, otherwise to a failure continuation.
-prompt :: S -> (S -> Result r) -> (S -> Result r) -> Result r
-prompt (S s0 a0 _c0) kf ks = Partial $ \s ->
+prompt :: Input -> Added -> More
+       -> (Input -> Added -> More -> Result r)
+       -> (Input -> Added -> More -> Result r)
+       -> Result r
+prompt i0 a0 _m0 kf ks = Partial $ \s ->
     if B.null s
-    then kf $! S s0 a0 Complete
-    else ks $! S (s0 +++ s) (a0 +++ s) Incomplete
+    then kf i0 a0 Complete
+    else ks (I (unI i0 +++ s)) (A (unA a0 +++ s)) Incomplete
 
 -- | Immediately demand more input via a 'Partial' continuation
 -- result.
 demandInput :: Parser ()
-demandInput = Parser $ \st0 kf ks ->
-    if more st0 == Complete
-    then kf st0 ["demandInput"] "not enough bytes"
-    else prompt st0 (\st -> kf st ["demandInput"] "not enough bytes") (`ks` ())
+demandInput = Parser $ \i0 a0 m0 kf ks ->
+    if m0 == Complete
+    then kf i0 a0 m0 ["demandInput"] "not enough bytes"
+    else let kf' i a m = kf i a m ["demandInput"] "not enough bytes"
+             ks' i a m = ks i a m ()
+         in prompt i0 a0 m0 kf' ks'
 
 -- | This parser always succeeds.  It returns 'True' if any input is
 -- available either immediately or on demand, and 'False' if the end
 -- of all input has been reached.
 wantInput :: Parser Bool
-wantInput = Parser $ \st0@(S s0 _a0 c0) _kf ks ->
+wantInput = Parser $ \i0 a0 m0 _kf ks ->
   case () of
-    _ | not (B.null s0) -> ks st0 True
-      | c0 == Complete  -> ks st0 False
-      | otherwise       -> prompt st0 (`ks` False) (`ks` True)
+    _ | not (B.null (unI i0)) -> ks i0 a0 m0 True
+      | m0 == Complete  -> ks i0 a0 m0 False
+      | otherwise       -> let kf' i a m = ks i a m False
+                               ks' i a m = ks i a m True
+                           in prompt i0 a0 m0 kf' ks'
 
 get :: Parser B.ByteString
-get  = Parser (\st0 _kf ks -> ks st0 (input st0))
+get  = Parser $ \i0 a0 m0 _kf ks -> ks i0 a0 m0 (unI i0)
 
 put :: B.ByteString -> Parser ()
-put s = Parser (\(S _s0 a0 c0) _kf ks -> ks (S s a0 c0) ())
+put s = Parser $ \_i0 a0 m0 _kf ks -> ks (I s) a0 m0 ()
 
 (+++) :: B.ByteString -> B.ByteString -> B.ByteString
 (+++) = B.append
@@ -243,8 +274,10 @@
 -- lookahead.  The downside to using this combinator is that it can
 -- retain input for longer than is desirable.
 try :: Parser a -> Parser a
-try p = Parser $ \st0 kf ks ->
-        runParser p (noAdds st0) (kf . mappend st0) ks
+try p = Parser $ \i0 a0 m0 kf ks ->
+        noAdds i0 a0 m0 $ \i1 a1 m1 ->
+            let kf' i2 a2 m2 = addS i0 a0 m0 i2 a2 m2 kf
+            in runParser p i1 a1 m1 kf' ks
 
 -- | The parser @satisfy p@ succeeds for any byte for which the
 -- predicate @p@ returns 'True'. Returns the byte that is actually
@@ -254,8 +287,7 @@
 -- >    where isDigit w = w >= 48 && w <= 57
 satisfy :: (Word8 -> Bool) -> Parser Word8
 satisfy p = do
-  ensure 1
-  s <- get
+  s <- ensure 1
   let w = B.unsafeHead s
   if p w
     then put (B.unsafeTail s) >> return w
@@ -268,8 +300,7 @@
 -- >    where isDigit w = w >= 48 && w <= 57
 skip :: (Word8 -> Bool) -> Parser ()
 skip p = do
-  ensure 1
-  s <- get
+  s <- ensure 1
   if p (B.unsafeHead s)
     then put (B.unsafeTail s)
     else fail "skip"
@@ -279,8 +310,7 @@
 -- parser returns the transformed byte that was parsed.
 satisfyWith :: (Word8 -> a) -> (a -> Bool) -> Parser a
 satisfyWith f p = do
-  ensure 1
-  s <- get
+  s <- ensure 1
   let c = f (B.unsafeHead s)
   if p c
     then put (B.unsafeTail s) >> return c
@@ -292,14 +322,14 @@
   hack :: Storable b => b -> Parser b
   hack dummy = do
     (fp,o,_) <- B.toForeignPtr `fmapP` take (sizeOf dummy)
-    return . B.inlinePerformIO . withForeignPtr fp $ \p -> peek (castPtr $ p `plusPtr` o)
+    return . B.inlinePerformIO . withForeignPtr fp $ \p ->
+        peek (castPtr $ p `plusPtr` o)
 
 -- | Consume @n@ bytes of input, but succeed only if the predicate
 -- returns 'True'.
 takeWith :: Int -> (B.ByteString -> Bool) -> Parser B.ByteString
 takeWith n p = do
-  ensure n
-  s <- get
+  s <- ensure n
   let (h,t) = B.splitAt n s
   if p h
     then put t >> return h
@@ -339,11 +369,11 @@
 skipWhile p = go
  where
   go = do
-    input <- wantInput
-    when input $ do
-      t <- B8.dropWhile p <$> get
-      put t
-      when (B.null t) go
+    t <- B8.dropWhile p <$> get
+    put t
+    when (B.null t) $ do
+      input <- wantInput
+      when input go
 {-# INLINE skipWhile #-}
 
 -- | Consume input as long as the predicate returns 'False'
@@ -372,16 +402,36 @@
 takeWhile p = (B.concat . reverse) `fmap` go []
  where
   go acc = do
-    input <- wantInput
-    if input
+    (h,t) <- B8.span p <$> get
+    put t
+    if B.null t
       then do
-        (h,t) <- B8.span p <$> get
-        put t
-        if B.null t
+        input <- wantInput
+        if input
           then go (h:acc)
           else return (h:acc)
-      else return acc
+      else return (h:acc)
 
+takeRest :: Parser [B.ByteString]
+takeRest = go []
+ where
+  go acc = do
+    input <- wantInput
+    if input
+      then do
+        s <- get
+        put B.empty
+        go (s:acc)
+      else return (reverse acc)
+
+-- | Consume all remaining input and return it as a single string.
+takeByteString :: Parser B.ByteString
+takeByteString = B.concat `fmap` takeRest
+
+-- | Consume all remaining input and return it as a single string.
+takeLazyByteString :: Parser L.ByteString
+takeLazyByteString = L.fromChunks `fmap` takeRest
+
 -- | A stateful scanner.  The predicate consumes and transforms a
 -- state argument, and each transformed state is passed to successive
 -- invocations of the predicate on each byte of the input until one
@@ -394,29 +444,33 @@
 -- combinators such as 'many', because such parsers loop until a
 -- failure occurs.  Careless use will thus result in an infinite loop.
 scan :: s -> (s -> Word8 -> Maybe s) -> Parser B.ByteString
-scan s0 p = (B.concat . reverse) `fmap` go [] s0
+scan s0 p = do
+  chunks <- go [] s0
+  case chunks of
+    [x] -> return x
+    xs  -> return . B.concat . reverse $ xs
  where
   go acc s1 = do
-    input <- wantInput
-    if input
+    let scanner (B.PS fp off len) =
+          withForeignPtr fp $ \ptr -> do
+            let inner !i !s | i == off+len = done (i-off) s
+                            | otherwise = do
+                                        w <- peekByteOff ptr i
+                                        case p s w of
+                                          Just s' -> inner (i+1) s'
+                                          Nothing -> done (i-off) s
+                done !i !s = return (B.PS fp off i, B.PS fp (off+i) (len-i),s)
+            inner off s1
+    (h,t,s') <- (unsafePerformIO . scanner) <$> get
+    put t
+    if B.null t
       then do
-        let scanner (B.PS fp off len) =
-              withForeignPtr fp $ \ptr -> do
-                let inner !i !s | i == off+len = return (i-off,s)
-                                | otherwise = do
-                                            w <- peekByteOff ptr i
-                                            case p s w of
-                                              Just s' -> inner (i+1) s'
-                                              Nothing -> return (i-off,s)
-                (i,s') <- inner off s1
-                return (B.PS fp off i, B.PS fp (off+i) (len-i),s')
-        (h,t,s') <- (unsafePerformIO . scanner) <$> get
-        put t
-        if B.null t
+        input <- wantInput
+        if input
           then go (h:acc) s'
           else return (h:acc)
-      else return acc
-{-# INLINE scan #-}    
+      else return (h:acc)
+{-# INLINE scan #-}
 
 -- | Consume input as long as the predicate returns 'True', and return
 -- the consumed input.
@@ -471,15 +525,24 @@
 
 -- | Match only if all input has been consumed.
 endOfInput :: Parser ()
-endOfInput = Parser $ \st0@S{..} kf ks ->
-             if B.null input
-             then if more == Complete
-                  then ks st0 ()
-                  else let kf' st1 _ _ = ks (mappend st0 st1) ()
-                           ks' st1 _   = kf (mappend st0 st1) [] "endOfInput"
-                       in  runParser demandInput st0 kf' ks'
-             else kf st0 [] "endOfInput"
-                                               
+endOfInput = Parser $ \i0 a0 m0 kf ks ->
+             if B.null (unI i0)
+             then if m0 == Complete
+                  then ks i0 a0 m0 ()
+                  else let kf' i1 a1 m1 _ _ = addS i0 a0 m0 i1 a1 m1 $
+                                              \ i2 a2 m2 -> ks i2 a2 m2 ()
+                           ks' i1 a1 m1 _   = addS i0 a0 m0 i1 a1 m1 $
+                                              \ i2 a2 m2 -> kf i2 a2 m2 []
+                                                            "endOfInput"
+                       in  runParser demandInput i0 a0 m0 kf' ks'
+             else kf i0 a0 m0 [] "endOfInput"
+
+-- | Return an indication of whether the end of input has been
+-- reached.
+atEnd :: Parser Bool
+atEnd = not <$> wantInput
+{-# INLINE atEnd #-}
+
 -- | Match either a single newline character @\'\\n\'@, or a carriage
 -- return followed by a newline character @\"\\r\\n\"@.
 endOfLine :: Parser ()
@@ -489,19 +552,31 @@
 (<?>) :: Parser a
       -> String                 -- ^ the name to use if parsing fails
       -> Parser a
-p <?> msg = Parser $ \s kf ks -> runParser p s (\s' strs m -> kf s' (msg:strs) m) ks
+p <?> msg0 = Parser $ \i0 a0 m0 kf ks ->
+             let kf' i a m strs msg = kf i a m (msg0:strs) msg
+             in runParser p i0 a0 m0 kf' ks
 {-# INLINE (<?>) #-}
 infix 0 <?>
 
 -- | Terminal failure continuation.
 failK :: Failure a
-failK st0 stack msg = Fail st0 stack msg
+failK i0 _a0 _m0 stack msg = Fail (unI i0) stack msg
+{-# INLINE failK #-}
 
 -- | Terminal success continuation.
 successK :: Success a a
-successK state a = Done state a
+successK i0 _a0 _m0 a = Done (unI i0) a
+{-# INLINE successK #-}
 
 -- | Run a parser.
 parse :: Parser a -> B.ByteString -> Result a
-parse m s = runParser m (S s B.empty Incomplete) failK successK
+parse m s = runParser m (I s) (A B.empty) Incomplete failK successK
 {-# INLINE parse #-}
+
+-- | Run a parser that cannot be resupplied via a 'Partial' result.
+parseOnly :: Parser a -> B.ByteString -> Either String a
+parseOnly m s = case runParser m (I s) (A B.empty) Complete failK successK of
+                  Fail _ _ err -> Left err
+                  Done _ a     -> Right a
+                  _            -> error "parseOnly: impossible error!"
+{-# INLINE parseOnly #-}
diff --git a/Data/Attoparsec/Number.hs b/Data/Attoparsec/Number.hs
new file mode 100644
--- /dev/null
+++ b/Data/Attoparsec/Number.hs
@@ -0,0 +1,112 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+-- |
+-- Module      :  Data.Attoparsec.Number
+-- Copyright   :  Bryan O'Sullivan 2011
+-- License     :  BSD3
+--
+-- Maintainer  :  bos@serpentine.com
+-- Stability   :  experimental
+-- Portability :  unknown
+--
+-- A simple number type, useful for parsing both exact and inexact
+-- quantities without losing much precision.
+module Data.Attoparsec.Number
+    (
+      Number(..)
+    ) where
+
+import Data.Data (Data)
+import Data.Function (on)
+import Data.Typeable (Typeable)
+
+-- | A numeric type that can represent integers accurately, and
+-- floating point numbers to the precision of a 'Double'.
+data Number = I !Integer
+            | D {-# UNPACK #-} !Double
+              deriving (Typeable, Data)
+
+instance Show Number where
+    show (I a) = show a
+    show (D a) = show a
+
+binop :: (Integer -> Integer -> a) -> (Double -> Double -> a)
+      -> Number -> Number -> a
+binop _ d (D a) (D b) = d a b
+binop i _ (I a) (I b) = i a b
+binop _ d (D a) (I b) = d a (fromIntegral b)
+binop _ d (I a) (D b) = d (fromIntegral a) b
+{-# INLINE binop #-}
+
+instance Eq Number where
+    (==) = binop (==) (==)
+    {-# INLINE (==) #-}
+
+    (/=) = binop (/=) (/=)
+    {-# INLINE (/=) #-}
+
+instance Ord Number where
+    (<) = binop (<) (<)
+    {-# INLINE (<) #-}
+
+    (>) = binop (>) (>)
+    {-# INLINE (>) #-}
+
+instance Num Number where
+    (+) = binop (((I$!).) . (+)) (((D$!).) . (+))
+    {-# INLINE (+) #-}
+
+    (-) = binop (((I$!).) . (-)) (((D$!).) . (-))
+    {-# INLINE (-) #-}
+
+    (*) = binop (((I$!).) . (+)) (((D$!).) . (+))
+    {-# INLINE (*) #-}
+
+    abs (I a) = I $! abs a
+    abs (D a) = D $! abs a
+    {-# INLINE abs #-}
+
+    negate (I a) = I $! negate a
+    negate (D a) = D $! negate a
+    {-# INLINE negate #-}
+
+    signum (I a) = I $! signum a
+    signum (D a) = D $! signum a
+    {-# INLINE signum #-}
+
+    fromInteger = (I$!) . fromInteger
+    {-# INLINE fromInteger #-}
+
+instance Real Number where
+    toRational (I a) = fromIntegral a
+    toRational (D a) = toRational a
+    {-# INLINE toRational #-}
+
+instance Fractional Number where
+    fromRational = (D$!) . fromRational
+    {-# INLINE fromRational #-}
+
+    (/) = binop (((D$!).) . (/) `on` fromIntegral)
+                (((D$!).) . (/))
+    {-# INLINE (/) #-}
+
+    recip (I a) = D $! recip (fromIntegral a)
+    recip (D a) = D $! recip a
+    {-# INLINE recip #-}
+
+instance RealFrac Number where
+    properFraction (I a) = (fromIntegral a,0)
+    properFraction (D a) = case properFraction a of
+                             (i,d) -> (i,D d)
+    {-# INLINE properFraction #-}
+    truncate (I a) = fromIntegral a
+    truncate (D a) = truncate a
+    {-# INLINE truncate #-}
+    round (I a) = fromIntegral a
+    round (D a) = round a
+    {-# INLINE round #-}
+    ceiling (I a) = fromIntegral a
+    ceiling (D a) = ceiling a
+    {-# INLINE ceiling #-}
+    floor (I a) = fromIntegral a
+    floor (D a) = floor a
+    {-# INLINE floor #-}
diff --git a/Data/Attoparsec/Zepto.hs b/Data/Attoparsec/Zepto.hs
new file mode 100644
--- /dev/null
+++ b/Data/Attoparsec/Zepto.hs
@@ -0,0 +1,146 @@
+{-# LANGUAGE BangPatterns, MagicHash, UnboxedTuples #-}
+
+-- |
+-- Module      :  Data.Attoparsec.Zepto
+-- Copyright   :  Bryan O'Sullivan 2011
+-- License     :  BSD3
+-- 
+-- Maintainer  :  bos@serpentine.com
+-- Stability   :  experimental
+-- Portability :  unknown
+--
+-- A tiny, highly specialized combinator parser for 'B.ByteString'
+-- strings.
+--
+-- While the main Attoparsec module generally performs well, this
+-- module is particularly fast for simple non-recursive loops that
+-- should not normally result in failed parses.
+--
+-- /Warning/: on more complex inputs involving recursion or failure,
+-- parsers based on this module may be as much as /ten times slower/
+-- than regular Attoparsec! You should /only/ use this module when you
+-- have benchmarks that prove that its use speeds your code up.
+module Data.Attoparsec.Zepto
+    (
+      Parser
+    , parse
+    , atEnd
+    , string
+    , take
+    , takeWhile
+    ) where
+
+import Data.Word (Word8)
+import Control.Applicative
+import Control.Monad
+import Data.Monoid
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Unsafe as B
+import Data.ByteString (ByteString)
+import Prelude hiding (take, takeWhile)
+
+newtype S = S {
+      input :: ByteString
+    }
+
+data Result a = Fail String
+              | OK !a
+
+-- | A simple parser.
+--
+-- This monad is strict in its state, and the monadic bind operator
+-- ('>>=') evaluates each result to weak head normal form before
+-- passing it along.
+newtype Parser a = Parser {
+      runParser :: S -> (# Result a, S #)
+    }
+
+instance Functor Parser where
+    fmap f m = Parser $ \s -> case runParser m s of
+                                (# OK a, s' #)     -> (# OK (f a), s' #)
+                                (# Fail err, s' #) -> (# Fail err, s' #)
+    {-# INLINE fmap #-}
+
+instance Monad Parser where
+    return a = Parser $ \s -> (# OK a, s #)
+    {-# INLINE return #-}
+
+    m >>= k   = Parser $ \s -> case runParser m s of
+                                 (# OK a, s' #) -> runParser (k a) s'
+                                 (# Fail err, s' #) -> (# Fail err, s' #)
+    {-# INLINE (>>=) #-}
+
+    fail msg = Parser $ \s -> (# Fail msg, s #)
+
+instance MonadPlus Parser where
+    mzero = fail "mzero"
+    {-# INLINE mzero #-}
+
+    mplus a b = Parser $ \s ->
+                case runParser a s of
+                  (# ok@(OK _), s' #) -> (# ok, s' #)
+                  (# _, _ #) -> case runParser b s of
+                                   (# ok@(OK _), s'' #) -> (# ok, s'' #)
+                                   (# err, s'' #) -> (# err, s'' #)
+    {-# INLINE mplus #-}
+
+instance Applicative Parser where
+    pure   = return
+    (<*>)  = ap
+
+gets :: (S -> a) -> Parser a
+gets f = Parser $ \s -> (# OK (f s), s #)
+{-# INLINE gets #-}
+
+put :: S -> Parser ()
+put s = Parser $ \_ -> (# OK (), s #)
+{-# INLINE put #-}
+
+-- | Run a parser.
+parse :: Parser a -> ByteString -> Either String a
+parse p bs = case runParser p (S bs) of
+               (# OK a, _ #) -> Right a
+               (# Fail err, _ #) -> Left err
+
+instance Monoid (Parser a) where
+    mempty  = fail "mempty"
+    {-# INLINE mempty #-}
+    mappend = mplus
+
+instance Alternative Parser where
+    empty = fail "empty"
+    {-# INLINE empty #-}
+    (<|>) = mplus
+
+-- | Consume input while the predicate returns 'True'.
+takeWhile :: (Word8 -> Bool) -> Parser ByteString
+takeWhile p = do
+  (h,t) <- gets (B.span p . input)
+  put (S t)
+  return h
+{-# INLINE takeWhile #-}
+
+-- | Consume @n@ bytes of input.
+take :: Int -> Parser ByteString
+take !n = do
+  s <- gets input
+  if B.length s >= n
+    then put (S (B.unsafeDrop n s)) >> return (B.unsafeTake n s)
+    else fail "insufficient input"
+{-# INLINE take #-}
+
+-- | Match a string exactly.
+string :: ByteString -> Parser ()
+string s = do
+  i <- gets input
+  if s `B.isPrefixOf` i
+    then put (S (B.unsafeDrop (B.length s) i)) >> return ()
+    else fail "string"
+{-# INLINE string #-}
+
+-- | Indicate whether the end of the input has been reached.
+atEnd :: Parser Bool
+atEnd = do
+  i <- gets input
+  return $! B.null i
+{-# INLINE atEnd #-}
diff --git a/attoparsec.cabal b/attoparsec.cabal
--- a/attoparsec.cabal
+++ b/attoparsec.cabal
@@ -1,12 +1,12 @@
 name:            attoparsec
-version:         0.8.4.0
+version:         0.8.5.0
 license:         BSD3
 license-file:    LICENSE
 category:        Text, Parsing
 author:          Bryan O'Sullivan <bos@serpentine.com>
 maintainer:      Bryan O'Sullivan <bos@serpentine.com>
 stability:       experimental
-tested-with:     GHC == 6.10.4, GHC == 6.12.1
+tested-with:     GHC == 6.10.4, GHC == 6.12.3, GHC == 7.0.1
 synopsis:        Fast combinator parsing for bytestrings
 cabal-version:   >= 1.6
 homepage:        http://bitbucket.org/bos/attoparsec
@@ -58,6 +58,8 @@
                    Data.Attoparsec.Combinator
                    Data.Attoparsec.FastSet
                    Data.Attoparsec.Lazy
+                   Data.Attoparsec.Number
+                   Data.Attoparsec.Zepto
   other-modules:   Data.Attoparsec.Internal
   ghc-options:     -Wall
 
