summaryrefslogtreecommitdiff
path: root/DString.hs
diff options
context:
space:
mode:
Diffstat (limited to 'DString.hs')
-rw-r--r--DString.hs71
1 files changed, 71 insertions, 0 deletions
diff --git a/DString.hs b/DString.hs
new file mode 100644
index 0000000..042ed09
--- /dev/null
+++ b/DString.hs
@@ -0,0 +1,71 @@
+-- | Some kind of dynamic string, or dequeue string, if you like, that
+-- supports efficient concatenation on both sides if you don't make your
+-- chunks too weird.
+module DString (
+ DString,
+ pack, unpack, singleton, length, take, drop, null, head, concat, hPutStr
+) where
+
+import Prelude hiding (length, take, drop, null, head, concat)
+
+import Data.Foldable (toList)
+import qualified Data.Sequence as S
+import Data.Sequence (ViewL((:<)))
+import qualified Data.Text.Lazy as T
+import qualified Data.Text.Lazy.IO as T
+import System.IO (Handle)
+
+
+newtype DString = DString (S.Seq T.Text)
+ deriving (Show)
+
+instance Eq DString where
+ DString s == DString s' = T.concat (toList s) == T.concat (toList s')
+
+pack :: String -> DString
+pack s = DString (S.singleton (T.pack s))
+
+unpack :: DString -> String
+unpack (DString s) = T.unpack (T.concat (toList s))
+
+singleton :: Char -> DString
+singleton c = DString (S.singleton (T.singleton c))
+
+length :: DString -> Int
+length (DString s) = fromIntegral (sum (map T.length (toList s)))
+
+take :: Int -> DString -> DString
+take n (DString s) =
+ let n' = fromIntegral n
+ lens = S.scanl (\acc t -> acc + T.length t) 0 s
+ ndigits = S.length (S.takeWhileL (< n') lens) + 1
+ text = T.take n' (T.concat (toList (S.take ndigits s)))
+ in DString (S.singleton text)
+
+drop :: Int -> DString -> DString
+drop n (DString s)
+ | n <= 0 = DString s
+ | otherwise =
+ let n' = fromIntegral n
+ lens = S.scanl (\acc text -> acc + T.length text) 0 s
+ ndigits = S.length (S.takeWhileL (< n') lens) - 1
+ s' = S.drop ndigits s
+ remain = n' - (lens `S.index` ndigits)
+ s'' = case S.viewl s' of
+ S.EmptyL -> S.empty
+ text :< rest -> T.drop remain text S.<| rest
+ in DString s''
+
+null :: DString -> Bool
+null (DString s) = sum (map T.length (toList s)) == 0
+
+head :: DString -> Char
+head (DString s) = case S.viewl s of
+ S.EmptyL -> error "Empty DString in DString.head"
+ text :< rest -> if T.length text > 0 then T.head text else DString.head (DString rest)
+
+concat :: [DString] -> DString
+concat strs = DString (mconcat [s | DString s <- strs])
+
+hPutStr :: Handle -> DString -> IO ()
+hPutStr h (DString s) = mapM_ (T.hPutStr h) (toList s)