diff --git a/attoparsec.cabal b/attoparsec.cabal
--- a/attoparsec.cabal
+++ b/attoparsec.cabal
@@ -1,5 +1,5 @@
 name:            attoparsec
-version:         0.4
+version:         0.5
 license:         BSD3
 license-file:    LICENSE
 category:        Text, Parsing
@@ -8,6 +8,7 @@
 synopsis:        Combinator parsing with Data.ByteString.Lazy
 cabal-version:   >= 1.2
 build-type:      Simple
+description:     Fast, flexible text-oriented parsing of lazy ByteStrings.
 
 flag split-base
 flag applicative-in-base
diff --git a/src/Data/ParserCombinators/Attoparsec/Char8.hs b/src/Data/ParserCombinators/Attoparsec/Char8.hs
--- a/src/Data/ParserCombinators/Attoparsec/Char8.hs
+++ b/src/Data/ParserCombinators/Attoparsec/Char8.hs
@@ -65,6 +65,7 @@
     , match
     , inClass
     , notInClass
+    , endOfLine
     ) where
 
 import Control.Applicative ((<$>))
@@ -78,7 +79,7 @@
 import Data.ParserCombinators.Attoparsec.Internal
     (Parser, ParseError, (<?>), parse, parseAt, parseTest, try, manyTill, eof,
      skipMany, skipMany1, count, lookAhead, peek, sepBy, sepBy1, string,
-     eitherP, getInput, getConsumed, takeAll, notEmpty, match)
+     eitherP, getInput, getConsumed, takeAll, notEmpty, match, endOfLine)
 import Prelude hiding (takeWhile)
 
 -- | Character parser.
diff --git a/src/Data/ParserCombinators/Attoparsec/FastSet.hs b/src/Data/ParserCombinators/Attoparsec/FastSet.hs
--- a/src/Data/ParserCombinators/Attoparsec/FastSet.hs
+++ b/src/Data/ParserCombinators/Attoparsec/FastSet.hs
@@ -30,6 +30,7 @@
     ) where
 
 import Data.Bits ((.&.), (.|.), shiftR)
+import qualified Data.ByteString.Char8 as B8
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Internal as I
 import qualified Data.ByteString.Unsafe as U
@@ -41,7 +42,8 @@
     deriving (Eq, Ord)
 
 instance Show FastSet where
-    show _ = "FastSet"
+    show (Sorted s) = "FastSet Sorted " ++ show (B8.unpack s)
+    show (Table _) = "FastSet Table"
 
 -- | The lower bound on the size of a lookup table.  We choose this to
 -- balance table density against performance.
@@ -64,8 +66,8 @@
               | otherwise =
                   let mid = (lo + hi) `div` 2
                   in case compare w (U.unsafeIndex s mid) of
-                       GT -> search lo (mid - 1)
-                       LT -> search (mid + 1) hi
+                       GT -> search (mid + 1) hi
+                       LT -> search lo (mid - 1)
                        _ -> True
 
 -- | Check the set for membership.  Only works with 8-bit characters:
diff --git a/src/Data/ParserCombinators/Attoparsec/Internal.hs b/src/Data/ParserCombinators/Attoparsec/Internal.hs
--- a/src/Data/ParserCombinators/Attoparsec/Internal.hs
+++ b/src/Data/ParserCombinators/Attoparsec/Internal.hs
@@ -59,6 +59,7 @@
     , skipWhile
     , notEmpty
     , match
+    , endOfLine
     ) where
 
 import Control.Applicative (Alternative(..), Applicative(..), (<$>))
@@ -66,7 +67,10 @@
 import Control.Monad.Fix (MonadFix(..))
 import qualified Data.ByteString as SB
 import qualified Data.ByteString.Lazy as LB
-import qualified Data.ByteString.Lazy.Internal as I
+import qualified Data.ByteString.Lazy.Char8 as L8
+import qualified Data.ByteString.Unsafe as U
+import qualified Data.ByteString.Internal as I
+import qualified Data.ByteString.Lazy.Internal as LB
 import Data.Int (Int64)
 import Data.Word (Word8)
 import Prelude hiding (takeWhile)
@@ -132,14 +136,14 @@
 
 mkState :: LB.ByteString -> Int64 -> S
 mkState s = case s of
-              I.Empty -> S SB.empty s
-              I.Chunk x xs -> S x xs
+              LB.Empty -> S SB.empty s
+              LB.Chunk x xs -> S x xs
 
 -- | Turn our chunked representation back into a normal lazy
 -- ByteString.
 (+:) :: SB.ByteString -> LB.ByteString -> LB.ByteString
 sb +: lb | SB.null sb = lb
-         | otherwise = I.Chunk sb lb
+         | otherwise = LB.Chunk sb lb
 {-# INLINE (+:) #-}
 
 infix 0 <?>
@@ -156,8 +160,8 @@
 nextChunk :: Parser ()
 nextChunk = Parser $ \(S _ lb n) ->
             case lb of
-              I.Chunk sb' lb' -> Right ((), S sb' lb' n)
-              I.Empty -> Left (lb, [])
+              LB.Chunk sb' lb' -> Right ((), S sb' lb' n)
+              LB.Empty -> Left (lb, [])
 
 -- | Get remaining input.
 getInput :: Parser LB.ByteString
@@ -202,12 +206,24 @@
 string s = Parser $ \(S sb lb n) ->
            let bs = sb +: lb
                l = LB.length s
-               (h, t) = LB.splitAt l bs
+               (h,t) = LB.splitAt l bs
            in if s == h
               then Right (s, mkState t (n + l))
               else Left (bs, [])
 {-# INLINE string #-}
 
+endOfLine :: Parser ()
+endOfLine = Parser $ \(S sb lb n) ->
+            let bs = sb +: lb
+            in case I.w2c (U.unsafeHead sb) of
+                 '\n' -> Right ((), mkState (LB.tail bs) (n + 1))
+                 '\r' -> let (h,t) = LB.splitAt 2 bs
+                             rn = L8.pack "\r\n"
+                         in if h == rn
+                            then Right ((), mkState t (n + 2))
+                            else Right ((), mkState (LB.tail bs) (n + 1))
+                 _ -> Left (bs, ["EOL"])
+
 -- | Satisfy a literal string, after applying a transformation to both
 -- it and the matching text.
 stringTransform :: (LB.ByteString -> LB.ByteString) -> LB.ByteString
@@ -255,7 +271,7 @@
 takeTill :: (Word8 -> Bool) -> Parser LB.ByteString
 takeTill p =
   Parser $ \(S sb lb n) ->
-  case LB.span (not . p) (sb +: lb) of
+  case LB.break p (sb +: lb) of
     (h,t) | LB.null t -> Left (h, [])
           | otherwise -> Right (h, mkState t (n + LB.length h))
 {-# INLINE takeTill #-}
