3 % (c) The GRASP/AQUA Project, Glasgow University, 1993-1996
5 \section{Packed strings}
7 This sits on top of the sequencing/arrays world, notably @ByteArray#@s.
9 Glorious hacking (all the hard work) by Bryan O'Sullivan.
15 packString, -- :: [Char] -> PackedString
16 packStringST, -- :: [Char] -> ST s PackedString
17 packCString, -- :: Addr -> PackedString
18 packCBytes, -- :: Int -> Addr -> PackedString
19 packCBytesST, -- :: Int -> Addr -> ST s PackedString
20 packStringForC, -- :: [Char] -> ByteArray#
21 packBytesForC, -- :: [Char] -> ByteArray Int
22 packBytesForCST, -- :: [Char] -> ST s (ByteArray Int)
23 nilPS, -- :: PackedString
24 consPS, -- :: Char -> PackedString -> PackedString
26 byteArrayToPS, -- :: ByteArray Int -> PackedString
27 unsafeByteArrayToPS, -- :: ByteArray a -> Int -> PackedString
28 psToByteArray, -- :: PackedString -> ByteArray Int
30 unpackPS, -- :: PackedString -> [Char]
32 hPutPS, -- :: Handle -> PackedString -> IO ()
33 putPS, -- :: FILE -> PackedString -> PrimIO () -- ToDo: more sensible type
34 getPS, -- :: FILE -> Int -> PrimIO PackedString
36 headPS, -- :: PackedString -> Char
37 tailPS, -- :: PackedString -> PackedString
38 nullPS, -- :: PackedString -> Bool
39 appendPS, -- :: PackedString -> PackedString -> PackedString
40 lengthPS, -- :: PackedString -> Int
41 {- 0-origin indexing into the string -}
42 indexPS, -- :: PackedString -> Int -> Char
43 mapPS, -- :: (Char -> Char) -> PackedString -> PackedString
44 filterPS, -- :: (Char -> Bool) -> PackedString -> PackedString
45 foldlPS, -- :: (a -> Char -> a) -> a -> PackedString -> a
46 foldrPS, -- :: (Char -> a -> a) -> a -> PackedString -> a
47 takePS, -- :: Int -> PackedString -> PackedString
48 dropPS, -- :: Int -> PackedString -> PackedString
49 splitAtPS, -- :: Int -> PackedString -> (PackedString, PackedString)
50 takeWhilePS, -- :: (Char -> Bool) -> PackedString -> PackedString
51 dropWhilePS, -- :: (Char -> Bool) -> PackedString -> PackedString
52 spanPS, -- :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
53 breakPS, -- :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
54 linesPS, -- :: PackedString -> [PackedString]
56 wordsPS, -- :: PackedString -> [PackedString]
57 reversePS, -- :: PackedString -> PackedString
58 splitPS, -- :: Char -> PackedString -> [PackedString]
59 splitWithPS, -- :: (Char -> Bool) -> PackedString -> [PackedString]
60 joinPS, -- :: PackedString -> [PackedString] -> PackedString
61 concatPS, -- :: [PackedString] -> PackedString
62 elemPS, -- :: Char -> PackedString -> Bool
65 pluck out a piece of a PS start and end
66 chars you want; both 0-origin-specified
68 substrPS, -- :: PackedString -> Int -> Int -> PackedString
74 import Char ( isSpace )
79 %************************************************************************
81 \subsection{@PackedString@ type declaration}
83 %************************************************************************
85 The type comes from GHCbase; we re-export it abstractly.
87 %************************************************************************
89 \subsection{@PackedString@ instances}
91 %************************************************************************
93 We try hard to make this go fast:
96 comparePS :: PackedString -> PackedString -> Ordering
98 comparePS (PS bs1 len1 has_null1) (PS bs2 len2 has_null2)
99 | not has_null1 && not has_null2
100 = unsafePerformPrimIO (
101 _ccall_ strcmp ba1 ba2 >>= \ (I# res) ->
104 else if res ==# 0# then EQ
108 ba1 = ByteArray (0, I# (len1 -# 1#)) bs1
109 ba2 = ByteArray (0, I# (len2 -# 1#)) bs2
111 comparePS (PS bs1 len1 has_null1) (CPS bs2 len2)
113 = unsafePerformPrimIO (
114 _ccall_ strcmp ba1 ba2 >>= \ (I# res) ->
117 else if res ==# 0# then EQ
121 ba1 = ByteArray (0, I# (len1 -# 1#)) bs1
124 comparePS (CPS bs1 len1) (CPS bs2 len2)
125 = unsafePerformPrimIO (
126 _ccall_ strcmp ba1 ba2 >>= \ (I# res) ->
129 else if res ==# 0# then EQ
136 comparePS a@(CPS _ _) b@(PS _ _ has_null2)
138 = -- try them the other way 'round
139 case (comparePS b a) of { LT -> GT; EQ -> EQ; GT -> LT }
141 comparePS ps1 ps2 -- slow catch-all case (esp for "has_null" True)
144 end1 = lengthPS# ps1 -# 1#
145 end2 = lengthPS# ps2 -# 1#
148 = if char# ># end1 then
149 if char# ># end2 then -- both strings ran out at once
151 else -- ps1 ran out before ps2
153 else if char# ># end2 then
154 GT -- ps2 ran out before ps1
157 ch1 = indexPS# ps1 char#
158 ch2 = indexPS# ps2 char#
160 if ch1 `eqChar#` ch2 then
161 looking_at (char# +# 1#)
162 else if ch1 `ltChar#` ch2 then LT
167 %************************************************************************
169 \subsection{Constructor functions}
171 %************************************************************************
173 Easy ones first. @packString@ requires getting some heap-bytes and
174 scribbling stuff into them.
178 packCString :: Addr -> PackedString
179 packCString (A# a#) = -- the easy one; we just believe the caller
182 len = case (strlen# a#) of { I# x -> x }
184 nilPS :: PackedString
187 consPS :: Char -> PackedString -> PackedString
188 consPS c cs = packString (c : (unpackPS cs)) -- ToDo:better
190 packStringForC :: [Char] -> ByteArray#
191 packStringForC = packStringForC__ -- from GHCbase
193 packBytesForC :: [Char] -> ByteArray Int
194 packBytesForC str = psToByteArray (packString str)
196 packBytesForCST :: [Char] -> ST s (ByteArray Int)
197 packBytesForCST str =
198 packStringST str >>= \ (PS bytes n has_null) ->
199 --later? ASSERT(not has_null)
200 return (ByteArray (0, I# (n -# 1#)) bytes)
202 packNBytesForCST :: Int -> [Char] -> ST s (ByteArray Int)
203 packNBytesForCST len str =
204 packNCharsST len str >>= \ (PS bytes n has_null) ->
205 return (ByteArray (0, I# (n -# 1#)) bytes)
207 packString :: [Char] -> PackedString
208 packString str = runST (packStringST str)
210 packStringST :: [Char] -> ST s PackedString
212 let len = length str in
215 packNCharsST :: Int -> [Char] -> ST s PackedString
216 packNCharsST len@(I# length#) str =
218 allocate an array that will hold the string
219 (not forgetting the NUL byte at the end)
221 new_ps_array (length# +# 1#) >>= \ ch_array ->
222 -- fill in packed string from "str"
223 fill_in ch_array 0# str >>
225 freeze_ps_array ch_array >>= \ (ByteArray _ frozen#) ->
226 let has_null = byteArrayHasNUL# frozen# length# in
227 return (PS frozen# length# has_null)
229 fill_in :: MutableByteArray s Int -> Int# -> [Char] -> ST s ()
230 fill_in arr_in# idx [] =
231 write_ps_array arr_in# idx (chr# 0#) >>
234 fill_in arr_in# idx (C# c : cs) =
235 write_ps_array arr_in# idx c >>
236 fill_in arr_in# (idx +# 1#) cs
238 packCBytes :: Int -> Addr -> PackedString
239 packCBytes len addr = runST (packCBytesST len addr)
241 packCBytesST :: Int -> Addr -> ST s PackedString
242 packCBytesST len@(I# length#) (A# addr) =
244 allocate an array that will hold the string
245 (not forgetting the NUL byte at the end)
247 new_ps_array (length# +# 1#) >>= \ ch_array ->
248 -- fill in packed string from "addr"
249 fill_in ch_array 0# >>
251 freeze_ps_array ch_array >>= \ (ByteArray _ frozen#) ->
252 let has_null = byteArrayHasNUL# frozen# length# in
253 return (PS frozen# length# has_null)
255 fill_in :: MutableByteArray s Int -> Int# -> ST s ()
259 = write_ps_array arr_in# idx (chr# 0#) >>
262 = case (indexCharOffAddr# addr idx) of { ch ->
263 write_ps_array arr_in# idx ch >>
264 fill_in arr_in# (idx +# 1#) }
266 byteArrayToPS :: ByteArray Int -> PackedString
267 byteArrayToPS (ByteArray ixs@(_, ix_end) frozen#) =
273 else ((index ixs ix_end) + 1)
276 PS frozen# n# (byteArrayHasNUL# frozen# n#)
278 unsafeByteArrayToPS :: ByteArray a -> Int -> PackedString
279 unsafeByteArrayToPS (ByteArray _ frozen#) (I# n#)
280 = PS frozen# n# (byteArrayHasNUL# frozen# n#)
282 psToByteArray :: PackedString -> ByteArray Int
283 psToByteArray (PS bytes n has_null)
284 = ByteArray (0, I# (n -# 1#)) bytes
286 psToByteArray (CPS addr len#)
289 byte_array_form = packCBytes len (A# addr)
291 case byte_array_form of { PS bytes _ _ ->
292 ByteArray (0, len - 1) bytes }
296 %************************************************************************
298 \subsection{Destructor functions (taking @PackedStrings@ apart)}
300 %************************************************************************
304 -- OK, but this code gets *hammered*:
306 -- = [ indexPS ps n | n <- [ 0::Int .. lengthPS ps - 1 ] ]
308 unpackPS :: PackedString -> [Char]
309 unpackPS (PS bytes len has_null)
314 | otherwise = C# ch : unpack (nh +# 1#)
316 ch = indexCharArray# bytes nh
318 unpackPS (CPS addr len)
322 | ch `eqChar#` '\0'# = []
323 | otherwise = C# ch : unpack (nh +# 1#)
325 ch = indexCharOffAddr# addr nh
329 Output a packed string via a handle:
334 hPutPS :: Handle -> PackedString -> IO ()
345 _readHandle handle >>= \ htype ->
347 _ErrorHandle ioError ->
348 _writeHandle handle htype >>
351 _writeHandle handle htype >>
352 failWith (IllegalOperation "handle is closed")
353 _SemiClosedHandle _ _ ->
354 _writeHandle handle htype >>
355 failWith (IllegalOperation "handle is closed")
357 _writeHandle handle htype >>
358 failWith (IllegalOperation "handle is not open for writing")
360 _getBufferMode other >>= \ other ->
361 (case _bufferMode other of
362 Just LineBuffering ->
363 writeLines (_filePtr other)
364 Just (BlockBuffering (Just size)) ->
365 writeBlocks (_filePtr other) size
366 Just (BlockBuffering Nothing) ->
367 writeBlocks (_filePtr other) ``BUFSIZ''
368 _ -> -- Nothing is treated pessimistically as NoBuffering
369 writeChars (_filePtr other) 0#
371 _writeHandle handle (_markHandle other) >>
375 _constructError "hPutStr" >>= \ ioError ->
381 writeLines :: Addr -> PrimIO Bool
382 writeLines = writeChunks ``BUFSIZ'' True
384 writeBlocks :: Addr -> Int -> PrimIO Bool
385 writeBlocks fp size = writeChunks size False fp
388 The breaking up of output into lines along \n boundaries
389 works fine as long as there are newlines to split by.
390 Avoid the splitting up into lines altogether (doesn't work
391 for overly long lines like the stuff that showsPrec instances
392 normally return). Instead, we split them up into fixed size
393 chunks before blasting them off to the Real World.
395 Hacked to avoid multiple passes over the strings - unsightly, but
396 a whole lot quicker. -- SOF 3/96
399 writeChunks :: Int -> Bool -> Addr -> PrimIO Bool
400 writeChunks (I# bufLen) chopOnNewLine fp =
401 newCharArray (0,I# bufLen) >>= \ arr@(MutableByteArray _ arr#) ->
403 shoveString :: Int# -> Int# -> PrimIO Bool
405 | i ==# pslen = -- end of string
409 _ccall_ writeFile arr fp (I# n) >>= \rc ->
413 case writeCharArray# arr# n (indexPS# ps i) s# of
415 {- Flushing lines - should we bother? -}
416 (if n ==# bufLen then
417 _ccall_ writeFile arr fp (I# (n +# 1#)) >>= \rc ->
419 shoveString 0# (i +# 1#)
423 shoveString (n +# 1#) (i +# 1#)) (S# s1#))
427 writeChars :: Addr -> Int# -> PrimIO Bool
429 | i ==# pslen = return True
431 _ccall_ filePutc fp (ord (C# (indexPS# ps i))) >>= \ rc ->
433 writeChars fp (i +# 1#)
437 ---------------------------------------------
439 putPS :: _FILE -> PackedString -> PrimIO ()
440 putPS file ps@(PS bytes len has_null)
445 byte_array = ByteArray (0, I# (len -# 1#)) bytes
447 _ccall_ fwrite byte_array (1::Int){-size-} (I# len) file
448 >>= \ (I# written) ->
449 if written ==# len then
452 error "GHCps.putPS: fwrite failed!\n"
454 putPS file (CPS addr len)
458 = _ccall_ fputs (A# addr) file >>= \ (I# _){-force type-} ->
463 The dual to @_putPS@, note that the size of the chunk specified
464 is the upper bound of the size of the chunk returned.
468 getPS :: _FILE -> Int -> PrimIO PackedString
469 getPS file len@(I# len#)
470 | len# <=# 0# = return nilPS -- I'm being kind here.
472 -- Allocate an array for system call to store its bytes into.
473 new_ps_array len# >>= \ ch_arr ->
474 freeze_ps_array ch_arr >>= \ (ByteArray _ frozen#) ->
476 byte_array = ByteArray (0, I# len#) frozen#
478 _ccall_ fread byte_array (1::Int) len file >>= \ (I# read#) ->
479 if read# ==# 0# then -- EOF or other error
480 error "GHCps.getPS: EOF reached or other error"
483 The system call may not return the number of
484 bytes requested. Instead of failing with an error
485 if the number of bytes read is less than requested,
486 a packed string containing the bytes we did manage
487 to snarf is returned.
490 has_null = byteArrayHasNUL# frozen# read#
492 return (PS frozen# read# has_null)
497 %************************************************************************
499 \subsection{List-mimicking functions for @PackedStrings@}
501 %************************************************************************
503 First, the basic functions that do look into the representation;
504 @indexPS@ is the most important one.
507 lengthPS :: PackedString -> Int
508 lengthPS ps = I# (lengthPS# ps)
510 {-# INLINE lengthPS# #-}
512 lengthPS# (PS _ i _) = i
513 lengthPS# (CPS _ i) = i
515 {-# INLINE strlen# #-}
517 strlen# :: Addr# -> Int
519 = unsafePerformPrimIO (
520 _ccall_ strlen (A# a) >>= \ len@(I# _) ->
524 byteArrayHasNUL# :: ByteArray# -> Int#{-length-} -> Bool
525 byteArrayHasNUL# bs len
526 = unsafePerformPrimIO (
527 _ccall_ byteArrayHasNUL__ ba (I# len) >>= \ (I# res) ->
529 if res ==# 0# then False else True
532 ba = ByteArray (0, I# (len -# 1#)) bs
534 -----------------------
536 indexPS :: PackedString -> Int -> Char
537 indexPS ps (I# n) = C# (indexPS# ps n)
539 {-# INLINE indexPS# #-}
541 indexPS# (PS bs i _) n
542 = --ASSERT (n >=# 0# && n <# i) -- error checking: my eye! (WDP 94/10)
546 = indexCharOffAddr# a n
550 Now, the rest of the functions can be defined without digging
551 around in the representation.
555 headPS :: PackedString -> Char
557 | nullPS ps = error "GHCps.headPS: head []"
558 | otherwise = C# (indexPS# ps 0#)
560 tailPS :: PackedString -> PackedString
562 | len <=# 0# = error "GHCps.tailPS: tail []"
564 | otherwise = substrPS# ps 1# (len -# 1#)
568 nullPS :: PackedString -> Bool
569 nullPS (PS _ i _) = i ==# 0#
570 nullPS (CPS _ i) = i ==# 0#
572 {- (ToDo: some non-lousy implementations...)
574 Old : _appendPS xs ys = packString (unpackPS xs ++ unpackPS ys)
577 appendPS :: PackedString -> PackedString -> PackedString
581 | otherwise = concatPS [xs,ys]
583 {- OLD: mapPS f xs = packString (map f (unpackPS xs)) -}
585 mapPS :: (Char -> Char) -> PackedString -> PackedString {-or String?-}
591 new_ps_array (length +# 1#) >>= \ ps_arr ->
592 whizz ps_arr length 0# >>
593 freeze_ps_array ps_arr >>= \ (ByteArray _ frozen#) ->
594 let has_null = byteArrayHasNUL# frozen# length in
595 return (PS frozen# length has_null))
597 length = lengthPS# xs
599 whizz :: MutableByteArray s Int -> Int# -> Int# -> ST s ()
602 = write_ps_array arr# i (chr# 0#) >>
608 write_ps_array arr# i (case f (C# ch) of { (C# x) -> x}) >>
609 whizz arr# (n -# 1#) (i +# 1#)
611 filterPS :: (Char -> Bool) -> PackedString -> PackedString {-or String?-}
617 Filtering proceeds as follows:
619 * traverse the list, applying the pred. to each element,
620 remembering the positions where it was satisfied.
622 Encode these positions using a run-length encoding of the gaps
623 between the matching positions.
625 * Allocate a MutableByteArray in the heap big enough to hold
626 all the matched entries, and copy the elements that matched over.
628 A better solution that merges the scan© passes into one,
629 would be to copy the filtered elements over into a growable
630 buffer. No such operation currently supported over
631 MutableByteArrays (could of course use malloc&realloc)
632 But, this solution may in the case of repeated realloc's
633 be worse than the current solution.
637 (rle,len_filtered) = filter_ps len# 0# 0# []
638 len_filtered# = case len_filtered of { I# x# -> x#}
640 if len# ==# len_filtered# then
641 {- not much filtering as everything passed through. -}
643 else if len_filtered# ==# 0# then
646 new_ps_array (len_filtered# +# 1#) >>= \ ps_arr ->
647 copy_arr ps_arr rle 0# 0# >>
648 freeze_ps_array ps_arr >>= \ (ByteArray _ frozen#) ->
649 let has_null = byteArrayHasNUL# frozen# len_filtered# in
650 return (PS frozen# len_filtered# has_null))
654 matchOffset :: Int# -> [Char] -> (Int,[Char])
655 matchOffset off [] = (I# off,[])
656 matchOffset off (C# c:cs) =
661 if x==# 0# then -- escape code, add 255#
666 copy_arr :: MutableByteArray s Int -> [Char] -> Int# -> Int# -> ST s ()
667 copy_arr arr# [_] _ _ = return ()
668 copy_arr arr# ls n i =
670 (x,ls') = matchOffset 0# ls
671 n' = n +# (case x of { (I# x#) -> x#}) -# 1#
674 write_ps_array arr# i ch >>
675 copy_arr arr# ls' (n' +# 1#) (i +# 1#)
677 esc :: Int# -> Int# -> [Char] -> [Char]
678 esc v 0# ls = (C# (chr# v)):ls
679 esc v n ls = esc v (n -# 1#) (C# (chr# 0#):ls)
681 filter_ps :: Int# -> Int# -> Int# -> [Char] -> ([Char],Int)
682 filter_ps n hits run acc
685 escs = run `quotInt#` 255#
686 v = run `remInt#` 255#
688 (esc (v +# 1#) escs acc, I# hits)
696 escs = run `quotInt#` 255#
697 v = run `remInt#` 255#
698 acc' = esc (v +# 1#) escs acc
700 filter_ps n' (hits +# 1#) 0# acc'
702 filter_ps n' hits (run +# 1#) acc
705 foldlPS :: (a -> Char -> a) -> a -> PackedString -> a
714 --whizzLR :: a -> Int# -> a
717 | otherwise = whizzLR (f b (C# (indexPS# ps idx))) (idx +# 1#)
720 foldrPS :: (Char -> a -> a) -> a -> PackedString -> a
729 --whizzRL :: a -> Int# -> a
732 | otherwise = whizzRL (f (C# (indexPS# ps idx)) b) (idx -# 1#)
734 takePS :: Int -> PackedString -> PackedString
737 | otherwise = substrPS# ps 0# (n -# 1#)
739 dropPS :: Int -> PackedString -> PackedString
742 | otherwise = substrPS# ps n (lengthPS# ps -# 1#)
746 splitAtPS :: Int -> PackedString -> (PackedString, PackedString)
747 splitAtPS n ps = (takePS n ps, dropPS n ps)
749 takeWhilePS :: (Char -> Bool) -> PackedString -> PackedString
752 break_pt = char_pos_that_dissatisfies
758 if break_pt ==# 0# then
761 substrPS# ps 0# (break_pt -# 1#)
763 dropWhilePS :: (Char -> Bool) -> PackedString -> PackedString
767 break_pt = char_pos_that_dissatisfies
773 if len ==# break_pt then
776 substrPS# ps break_pt (len -# 1#)
778 elemPS :: Char -> PackedString -> Bool
782 break_pt = first_char_pos_that_satisfies
790 char_pos_that_dissatisfies :: (Char# -> Bool) -> PackedString -> Int# -> Int# -> Int#
792 char_pos_that_dissatisfies p ps len pos
793 | pos >=# len = pos -- end
794 | p (indexPS# ps pos) = -- predicate satisfied; keep going
795 char_pos_that_dissatisfies p ps len (pos +# 1#)
796 | otherwise = pos -- predicate not satisfied
798 char_pos_that_dissatisfies p ps len pos -- dead code: HACK to avoid badly-typed error msg
801 first_char_pos_that_satisfies :: (Char# -> Bool) -> PackedString -> Int# -> Int# -> Int#
802 first_char_pos_that_satisfies p ps len pos
803 | pos >=# len = pos -- end
804 | p (indexPS# ps pos) = pos -- got it!
805 | otherwise = first_char_pos_that_satisfies p ps len (pos +# 1#)
807 -- ToDo: could certainly go quicker
808 spanPS :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
809 spanPS p ps = (takeWhilePS p ps, dropWhilePS p ps)
811 breakPS :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
812 breakPS p ps = spanPS (not . p) ps
814 linesPS :: PackedString -> [PackedString]
815 linesPS ps = splitPS '\n' ps
817 wordsPS :: PackedString -> [PackedString]
818 wordsPS ps = splitWithPS isSpace ps
820 reversePS :: PackedString -> PackedString
822 if nullPS ps then -- don't create stuff unnecessarily.
826 new_ps_array (length +# 1#) >>= \ arr# -> -- incl NUL byte!
827 fill_in arr# (length -# 1#) 0# >>
828 freeze_ps_array arr# >>= \ (ByteArray _ frozen#) ->
829 let has_null = byteArrayHasNUL# frozen# length in
830 return (PS frozen# length has_null))
832 length = lengthPS# ps
834 fill_in :: MutableByteArray s Int -> Int# -> Int# -> ST s ()
835 fill_in arr_in# n i =
839 write_ps_array arr_in# i ch >>
841 write_ps_array arr_in# (i +# 1#) (chr# 0#) >>
844 fill_in arr_in# (n -# 1#) (i +# 1#)
846 concatPS :: [PackedString] -> PackedString
850 tot_len# = case (foldr ((+) . lengthPS) 0 pss) of { I# x -> x }
851 tot_len = I# tot_len#
854 new_ps_array (tot_len# +# 1#) >>= \ arr# -> -- incl NUL byte!
855 packum arr# pss 0# >>
856 freeze_ps_array arr# >>= \ (ByteArray _ frozen#) ->
858 let has_null = byteArrayHasNUL# frozen# tot_len# in
860 return (PS frozen# tot_len# has_null)
863 packum :: MutableByteArray s Int -> [PackedString] -> Int# -> ST s ()
866 = write_ps_array arr pos (chr# 0#) >>
868 packum arr (ps : pss) pos
869 = fill arr pos ps 0# (lengthPS# ps) >>= \ (I# next_pos) ->
870 packum arr pss next_pos
872 fill :: MutableByteArray s Int -> Int# -> PackedString -> Int# -> Int# -> ST s Int
874 fill arr arr_i ps ps_i ps_len
876 = return (I# (arr_i +# ps_len))
878 = write_ps_array arr (arr_i +# ps_i) (indexPS# ps ps_i) >>
879 fill arr arr_i ps (ps_i +# 1#) ps_len
881 ------------------------------------------------------------
882 joinPS :: PackedString -> [PackedString] -> PackedString
883 joinPS filler pss = concatPS (splice pss)
887 splice (x:y:xs) = x:filler:splice (y:xs)
889 -- ToDo: the obvious generalisation
891 Some properties that hold:
894 where False = any (map (x `elemPS`) ls')
895 False = any (map (nullPS) ls')
897 * all x's have been chopped out.
898 * no empty PackedStrings in returned list. A conseq.
903 * joinPS (packString [x]) (_splitPS x ls) = ls
907 splitPS :: Char -> PackedString -> [PackedString]
908 splitPS (C# ch) = splitWithPS (\ (C# c) -> c `eqChar#` ch)
910 splitWithPS :: (Char -> Bool) -> PackedString -> [PackedString]
911 splitWithPS pred ps =
921 first_char_pos_that_satisfies
927 if break_pt ==# n then -- immediate match, no substring to cut out.
928 splitify (break_pt +# 1#)
930 substrPS# ps n (break_pt -# 1#): -- leave out the matching character
931 splitify (break_pt +# 1#)
935 %************************************************************************
937 \subsection{Local utility functions}
939 %************************************************************************
941 The definition of @_substrPS@ is essentially:
942 @take (end - begin + 1) (drop begin str)@.
945 substrPS :: PackedString -> Int -> Int -> PackedString
946 substrPS ps (I# begin) (I# end) = substrPS# ps begin end
950 = error "GHCps.substrPS: bounds out of range"
952 | s >=# len || result_len# <=# 0#
957 new_ps_array (result_len# +# 1#) >>= \ ch_arr -> -- incl NUL byte!
959 freeze_ps_array ch_arr >>= \ (ByteArray _ frozen#) ->
961 let has_null = byteArrayHasNUL# frozen# result_len# in
963 return (PS frozen# result_len# has_null)
968 result_len# = (if e <# len then (e +# 1#) else len) -# s
969 result_len = I# result_len#
971 -----------------------
972 fill_in :: MutableByteArray s Int -> Int# -> ST s ()
975 | idx ==# result_len#
976 = write_ps_array arr_in# idx (chr# 0#) >>
980 ch = indexPS# ps (s +# idx)
982 write_ps_array arr_in# idx ch >>
983 fill_in arr_in# (idx +# 1#)
987 (Very :-) ``Specialised'' versions of some CharArray things...
990 new_ps_array :: Int# -> ST s (MutableByteArray s Int)
991 write_ps_array :: MutableByteArray s Int -> Int# -> Char# -> ST s ()
992 freeze_ps_array :: MutableByteArray s Int -> ST s (ByteArray Int)
994 new_ps_array size = ST $ \ (S# s) ->
995 case (newCharArray# size s) of { StateAndMutableByteArray# s2# barr# ->
996 (MutableByteArray bot barr#, S# s2#)}
998 bot = error "new_ps_array"
1000 write_ps_array (MutableByteArray _ barr#) n ch = ST $ \ (S# s#) ->
1001 case writeCharArray# barr# n ch s# of { s2# ->
1004 -- same as unsafeFreezeByteArray
1005 freeze_ps_array (MutableByteArray ixs arr#) = ST $ \ (S# s#) ->
1006 case unsafeFreezeByteArray# arr# s# of { StateAndByteArray# s2# frozen# ->
1007 (ByteArray ixs frozen#, S# s2#) }