2 % (c) The GRASP/AQUA Project, Glasgow University, 1993-1996
4 \section{Packed strings}
6 This sits on top of the sequencing/arrays world, notably @ByteArray#@s.
8 Glorious hacking (all the hard work) by Bryan O'Sullivan.
13 packString, -- :: [Char] -> PackedString
14 packStringST, -- :: [Char] -> ST s PackedString
15 nilPS, -- :: PackedString
16 consPS, -- :: Char -> PackedString -> PackedString
18 byteArrayToPS, -- :: ByteArray Int -> PackedString
19 unsafeByteArrayToPS, -- :: ByteArray a -> Int -> PackedString
20 psToByteArray, -- :: PackedString -> ByteArray Int
22 unpackPS, -- :: PackedString -> [Char]
24 hPutPS, -- :: Handle -> PackedString -> IO ()
25 putPS, -- :: FILE -> PackedString -> PrimIO () -- ToDo: more sensible type
26 getPS, -- :: FILE -> Int -> PrimIO PackedString
28 headPS, -- :: PackedString -> Char
29 tailPS, -- :: PackedString -> PackedString
30 nullPS, -- :: PackedString -> Bool
31 appendPS, -- :: PackedString -> PackedString -> PackedString
32 lengthPS, -- :: PackedString -> Int
33 {- 0-origin indexing into the string -}
34 indexPS, -- :: PackedString -> Int -> Char
35 mapPS, -- :: (Char -> Char) -> PackedString -> PackedString
36 filterPS, -- :: (Char -> Bool) -> PackedString -> PackedString
37 foldlPS, -- :: (a -> Char -> a) -> a -> PackedString -> a
38 foldrPS, -- :: (Char -> a -> a) -> a -> PackedString -> a
39 takePS, -- :: Int -> PackedString -> PackedString
40 dropPS, -- :: Int -> PackedString -> PackedString
41 splitAtPS, -- :: Int -> PackedString -> (PackedString, PackedString)
42 takeWhilePS, -- :: (Char -> Bool) -> PackedString -> PackedString
43 dropWhilePS, -- :: (Char -> Bool) -> PackedString -> PackedString
44 spanPS, -- :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
45 breakPS, -- :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
46 linesPS, -- :: PackedString -> [PackedString]
48 wordsPS, -- :: PackedString -> [PackedString]
49 reversePS, -- :: PackedString -> PackedString
50 splitPS, -- :: Char -> PackedString -> [PackedString]
51 splitWithPS, -- :: (Char -> Bool) -> PackedString -> [PackedString]
52 joinPS, -- :: PackedString -> [PackedString] -> PackedString
53 concatPS, -- :: [PackedString] -> PackedString
54 elemPS, -- :: Char -> PackedString -> Bool
57 Pluck out a piece of a PS start and end
58 chars you want; both 0-origin-specified
60 substrPS, -- :: PackedString -> Int -> Int -> PackedString
64 -- Converting to C strings
66 unpackCString#, unpackCString2#, unpackAppendCString#, unpackFoldrCString#,
67 packCBytesST, unpackCString
71 import IOBase ( error ) {-# SOURCE #-}
80 %************************************************************************
82 \subsection{@PackedString@ type declaration}
84 %************************************************************************
88 = PS ByteArray# -- the bytes
89 Int# -- length (*not* including NUL at the end)
90 Bool -- True <=> contains a NUL
91 | CPS Addr# -- pointer to the (null-terminated) bytes in C land
92 Int# -- length, as per strlen
93 -- definitely doesn't contain a NUL
95 instance Eq PackedString where
96 x == y = compare x y == EQ
97 x /= y = compare x y /= EQ
99 instance Ord PackedString where
101 x <= y = compare x y /= GT
102 x < y = compare x y == LT
103 x >= y = compare x y /= LT
104 x > y = compare x y == GT
105 max x y = case (compare x y) of { LT -> y ; EQ -> x ; GT -> x }
106 min x y = case (compare x y) of { LT -> x ; EQ -> x ; GT -> y }
108 --instance Read PackedString: ToDo
110 instance Show PackedString where
111 showsPrec p ps r = showsPrec p (unpackPS ps) r
112 showList = showList__ (showsPrec 0)
116 %************************************************************************
118 \subsection{@PackedString@ instances}
120 %************************************************************************
122 We try hard to make this go fast:
124 comparePS :: PackedString -> PackedString -> Ordering
126 comparePS (PS bs1 len1 has_null1) (PS bs2 len2 has_null2)
127 | not has_null1 && not has_null2
128 = unsafePerformPrimIO (
129 _ccall_ strcmp ba1 ba2 >>= \ (I# res) ->
132 else if res ==# 0# then EQ
136 ba1 = ByteArray (0, I# (len1 -# 1#)) bs1
137 ba2 = ByteArray (0, I# (len2 -# 1#)) bs2
139 comparePS (PS bs1 len1 has_null1) (CPS bs2 len2)
141 = unsafePerformPrimIO (
142 _ccall_ strcmp ba1 ba2 >>= \ (I# res) ->
145 else if res ==# 0# then EQ
149 ba1 = ByteArray (0, I# (len1 -# 1#)) bs1
152 comparePS (CPS bs1 len1) (CPS bs2 len2)
153 = unsafePerformPrimIO (
154 _ccall_ strcmp ba1 ba2 >>= \ (I# res) ->
157 else if res ==# 0# then EQ
164 comparePS a@(CPS _ _) b@(PS _ _ has_null2)
166 = -- try them the other way 'round
167 case (comparePS b a) of { LT -> GT; EQ -> EQ; GT -> LT }
169 comparePS ps1 ps2 -- slow catch-all case (esp for "has_null" True)
172 end1 = lengthPS# ps1 -# 1#
173 end2 = lengthPS# ps2 -# 1#
176 = if char# ># end1 then
177 if char# ># end2 then -- both strings ran out at once
179 else -- ps1 ran out before ps2
181 else if char# ># end2 then
182 GT -- ps2 ran out before ps1
185 ch1 = indexPS# ps1 char#
186 ch2 = indexPS# ps2 char#
188 if ch1 `eqChar#` ch2 then
189 looking_at (char# +# 1#)
190 else if ch1 `ltChar#` ch2 then LT
195 %************************************************************************
197 \subsection{Constructor functions}
199 %************************************************************************
201 Easy ones first. @packString@ requires getting some heap-bytes and
202 scribbling stuff into them.
205 nilPS :: PackedString
208 consPS :: Char -> PackedString -> PackedString
209 consPS c cs = packString (c : (unpackPS cs)) -- ToDo:better
211 packString :: [Char] -> PackedString
212 packString str = runST (packStringST str)
214 packStringST :: [Char] -> ST s PackedString
216 let len = length str in
219 packNCharsST :: Int -> [Char] -> ST s PackedString
220 packNCharsST len@(I# length#) str =
222 allocate an array that will hold the string
223 (not forgetting the NUL byte at the end)
225 new_ps_array (length# +# 1#) >>= \ ch_array ->
226 -- fill in packed string from "str"
227 fill_in ch_array 0# str >>
229 freeze_ps_array ch_array >>= \ (ByteArray _ frozen#) ->
230 let has_null = byteArrayHasNUL# frozen# length# in
231 return (PS frozen# length# has_null)
233 fill_in :: MutableByteArray s Int -> Int# -> [Char] -> ST s ()
234 fill_in arr_in# idx [] =
235 write_ps_array arr_in# idx (chr# 0#) >>
238 fill_in arr_in# idx (C# c : cs) =
239 write_ps_array arr_in# idx c >>
240 fill_in arr_in# (idx +# 1#) cs
242 byteArrayToPS :: ByteArray Int -> PackedString
243 byteArrayToPS (ByteArray ixs@(_, ix_end) frozen#) =
249 else ((index ixs ix_end) + 1)
252 PS frozen# n# (byteArrayHasNUL# frozen# n#)
254 unsafeByteArrayToPS :: ByteArray a -> Int -> PackedString
255 unsafeByteArrayToPS (ByteArray _ frozen#) (I# n#)
256 = PS frozen# n# (byteArrayHasNUL# frozen# n#)
258 psToByteArray :: PackedString -> ByteArray Int
259 psToByteArray (PS bytes n has_null)
260 = ByteArray (0, I# (n -# 1#)) bytes
262 psToByteArray (CPS addr len#)
265 byte_array_form = packCBytes len (A# addr)
267 case byte_array_form of { PS bytes _ _ ->
268 ByteArray (0, len - 1) bytes }
271 %************************************************************************
273 \subsection{Destructor functions (taking @PackedStrings@ apart)}
275 %************************************************************************
278 -- OK, but this code gets *hammered*:
280 -- = [ indexPS ps n | n <- [ 0::Int .. lengthPS ps - 1 ] ]
282 unpackPS :: PackedString -> [Char]
283 unpackPS (PS bytes len has_null)
288 | otherwise = C# ch : unpack (nh +# 1#)
290 ch = indexCharArray# bytes nh
292 unpackPS (CPS addr len)
296 | ch `eqChar#` '\0'# = []
297 | otherwise = C# ch : unpack (nh +# 1#)
299 ch = indexCharOffAddr# addr nh
302 Output a packed string via a handle:
306 hPutPS :: Handle -> PackedString -> IO ()
317 _readHandle handle >>= \ htype ->
319 _ErrorHandle ioError ->
320 _writeHandle handle htype >>
323 _writeHandle handle htype >>
324 failWith (IllegalOperation "handle is closed")
325 _SemiClosedHandle _ _ ->
326 _writeHandle handle htype >>
327 failWith (IllegalOperation "handle is closed")
329 _writeHandle handle htype >>
330 failWith (IllegalOperation "handle is not open for writing")
332 _getBufferMode other >>= \ other ->
333 (case _bufferMode other of
334 Just LineBuffering ->
335 writeLines (_filePtr other)
336 Just (BlockBuffering (Just size)) ->
337 writeBlocks (_filePtr other) size
338 Just (BlockBuffering Nothing) ->
339 writeBlocks (_filePtr other) ``BUFSIZ''
340 _ -> -- Nothing is treated pessimistically as NoBuffering
341 writeChars (_filePtr other) 0#
343 _writeHandle handle (_markHandle other) >>
347 _constructError "hPutStr" >>= \ ioError ->
353 writeLines :: Addr -> PrimIO Bool
354 writeLines = writeChunks ``BUFSIZ'' True
356 writeBlocks :: Addr -> Int -> PrimIO Bool
357 writeBlocks fp size = writeChunks size False fp
360 The breaking up of output into lines along \n boundaries
361 works fine as long as there are newlines to split by.
362 Avoid the splitting up into lines altogether (doesn't work
363 for overly long lines like the stuff that showsPrec instances
364 normally return). Instead, we split them up into fixed size
365 chunks before blasting them off to the Real World.
367 Hacked to avoid multiple passes over the strings - unsightly, but
368 a whole lot quicker. -- SOF 3/96
371 writeChunks :: Int -> Bool -> Addr -> PrimIO Bool
372 writeChunks (I# bufLen) chopOnNewLine fp =
373 newCharArray (0,I# bufLen) >>= \ arr@(MutableByteArray _ arr#) ->
375 shoveString :: Int# -> Int# -> PrimIO Bool
377 | i ==# pslen = -- end of string
381 _ccall_ writeFile arr fp (I# n) >>= \rc ->
385 case writeCharArray# arr# n (indexPS# ps i) s# of
387 {- Flushing lines - should we bother? -}
388 (if n ==# bufLen then
389 _ccall_ writeFile arr fp (I# (n +# 1#)) >>= \rc ->
391 shoveString 0# (i +# 1#)
395 shoveString (n +# 1#) (i +# 1#)) (S# s1#))
399 writeChars :: Addr -> Int# -> PrimIO Bool
401 | i ==# pslen = return True
403 _ccall_ filePutc fp (ord (C# (indexPS# ps i))) >>= \ rc ->
405 writeChars fp (i +# 1#)
409 ---------------------------------------------
411 putPS :: _FILE -> PackedString -> PrimIO ()
412 putPS file ps@(PS bytes len has_null)
417 byte_array = ByteArray (0, I# (len -# 1#)) bytes
419 _ccall_ fwrite byte_array (1::Int){-size-} (I# len) file
420 >>= \ (I# written) ->
421 if written ==# len then
424 error "putPS: fwrite failed!\n"
426 putPS file (CPS addr len)
430 = _ccall_ fputs (A# addr) file >>= \ (I# _){-force type-} ->
434 The dual to @_putPS@, note that the size of the chunk specified
435 is the upper bound of the size of the chunk returned.
438 getPS :: _FILE -> Int -> PrimIO PackedString
439 getPS file len@(I# len#)
440 | len# <=# 0# = return nilPS -- I'm being kind here.
442 -- Allocate an array for system call to store its bytes into.
443 new_ps_array len# >>= \ ch_arr ->
444 freeze_ps_array ch_arr >>= \ (ByteArray _ frozen#) ->
446 byte_array = ByteArray (0, I# len#) frozen#
448 _ccall_ fread byte_array (1::Int) len file >>= \ (I# read#) ->
449 if read# ==# 0# then -- EOF or other error
450 error "getPS: EOF reached or other error"
453 The system call may not return the number of
454 bytes requested. Instead of failing with an error
455 if the number of bytes read is less than requested,
456 a packed string containing the bytes we did manage
457 to snarf is returned.
460 has_null = byteArrayHasNUL# frozen# read#
462 return (PS frozen# read# has_null)
466 %************************************************************************
468 \subsection{List-mimicking functions for @PackedStrings@}
470 %************************************************************************
472 First, the basic functions that do look into the representation;
473 @indexPS@ is the most important one.
476 lengthPS :: PackedString -> Int
477 lengthPS ps = I# (lengthPS# ps)
479 {-# INLINE lengthPS# #-}
481 lengthPS# (PS _ i _) = i
482 lengthPS# (CPS _ i) = i
484 {-# INLINE strlen# #-}
486 strlen# :: Addr# -> Int
488 = unsafePerformPrimIO (
489 _ccall_ strlen (A# a) >>= \ len@(I# _) ->
493 byteArrayHasNUL# :: ByteArray# -> Int#{-length-} -> Bool
494 byteArrayHasNUL# bs len
495 = unsafePerformPrimIO (
496 _ccall_ byteArrayHasNUL__ ba (I# len) >>= \ (I# res) ->
498 if res ==# 0# then False else True
501 ba = ByteArray (0, I# (len -# 1#)) bs
503 -----------------------
505 indexPS :: PackedString -> Int -> Char
506 indexPS ps (I# n) = C# (indexPS# ps n)
508 {-# INLINE indexPS# #-}
510 indexPS# (PS bs i _) n
511 = --ASSERT (n >=# 0# && n <# i) -- error checking: my eye! (WDP 94/10)
515 = indexCharOffAddr# a n
518 Now, the rest of the functions can be defined without digging
519 around in the representation.
522 headPS :: PackedString -> Char
524 | nullPS ps = error "headPS: head []"
525 | otherwise = C# (indexPS# ps 0#)
527 tailPS :: PackedString -> PackedString
529 | len <=# 0# = error "tailPS: tail []"
531 | otherwise = substrPS# ps 1# (len -# 1#)
535 nullPS :: PackedString -> Bool
536 nullPS (PS _ i _) = i ==# 0#
537 nullPS (CPS _ i) = i ==# 0#
539 {- (ToDo: some non-lousy implementations...)
541 Old : _appendPS xs ys = packString (unpackPS xs ++ unpackPS ys)
544 appendPS :: PackedString -> PackedString -> PackedString
548 | otherwise = concatPS [xs,ys]
550 {- OLD: mapPS f xs = packString (map f (unpackPS xs)) -}
552 mapPS :: (Char -> Char) -> PackedString -> PackedString {-or String?-}
558 new_ps_array (length +# 1#) >>= \ ps_arr ->
559 whizz ps_arr length 0# >>
560 freeze_ps_array ps_arr >>= \ (ByteArray _ frozen#) ->
561 let has_null = byteArrayHasNUL# frozen# length in
562 return (PS frozen# length has_null))
564 length = lengthPS# xs
566 whizz :: MutableByteArray s Int -> Int# -> Int# -> ST s ()
569 = write_ps_array arr# i (chr# 0#) >>
575 write_ps_array arr# i (case f (C# ch) of { (C# x) -> x}) >>
576 whizz arr# (n -# 1#) (i +# 1#)
578 filterPS :: (Char -> Bool) -> PackedString -> PackedString {-or String?-}
584 Filtering proceeds as follows:
586 * traverse the list, applying the pred. to each element,
587 remembering the positions where it was satisfied.
589 Encode these positions using a run-length encoding of the gaps
590 between the matching positions.
592 * Allocate a MutableByteArray in the heap big enough to hold
593 all the matched entries, and copy the elements that matched over.
595 A better solution that merges the scan© passes into one,
596 would be to copy the filtered elements over into a growable
597 buffer. No such operation currently supported over
598 MutableByteArrays (could of course use malloc&realloc)
599 But, this solution may in the case of repeated realloc's
600 be worse than the current solution.
604 (rle,len_filtered) = filter_ps len# 0# 0# []
605 len_filtered# = case len_filtered of { I# x# -> x#}
607 if len# ==# len_filtered# then
608 {- not much filtering as everything passed through. -}
610 else if len_filtered# ==# 0# then
613 new_ps_array (len_filtered# +# 1#) >>= \ ps_arr ->
614 copy_arr ps_arr rle 0# 0# >>
615 freeze_ps_array ps_arr >>= \ (ByteArray _ frozen#) ->
616 let has_null = byteArrayHasNUL# frozen# len_filtered# in
617 return (PS frozen# len_filtered# has_null))
621 matchOffset :: Int# -> [Char] -> (Int,[Char])
622 matchOffset off [] = (I# off,[])
623 matchOffset off (C# c:cs) =
628 if x==# 0# then -- escape code, add 255#
633 copy_arr :: MutableByteArray s Int -> [Char] -> Int# -> Int# -> ST s ()
634 copy_arr arr# [_] _ _ = return ()
635 copy_arr arr# ls n i =
637 (x,ls') = matchOffset 0# ls
638 n' = n +# (case x of { (I# x#) -> x#}) -# 1#
641 write_ps_array arr# i ch >>
642 copy_arr arr# ls' (n' +# 1#) (i +# 1#)
644 esc :: Int# -> Int# -> [Char] -> [Char]
645 esc v 0# ls = (C# (chr# v)):ls
646 esc v n ls = esc v (n -# 1#) (C# (chr# 0#):ls)
648 filter_ps :: Int# -> Int# -> Int# -> [Char] -> ([Char],Int)
649 filter_ps n hits run acc
652 escs = run `quotInt#` 255#
653 v = run `remInt#` 255#
655 (esc (v +# 1#) escs acc, I# hits)
663 escs = run `quotInt#` 255#
664 v = run `remInt#` 255#
665 acc' = esc (v +# 1#) escs acc
667 filter_ps n' (hits +# 1#) 0# acc'
669 filter_ps n' hits (run +# 1#) acc
672 foldlPS :: (a -> Char -> a) -> a -> PackedString -> a
681 --whizzLR :: a -> Int# -> a
684 | otherwise = whizzLR (f b (C# (indexPS# ps idx))) (idx +# 1#)
687 foldrPS :: (Char -> a -> a) -> a -> PackedString -> a
696 --whizzRL :: a -> Int# -> a
699 | otherwise = whizzRL (f (C# (indexPS# ps idx)) b) (idx -# 1#)
701 takePS :: Int -> PackedString -> PackedString
704 | otherwise = substrPS# ps 0# (n -# 1#)
706 dropPS :: Int -> PackedString -> PackedString
709 | otherwise = substrPS# ps n (lengthPS# ps -# 1#)
713 splitAtPS :: Int -> PackedString -> (PackedString, PackedString)
714 splitAtPS n ps = (takePS n ps, dropPS n ps)
716 takeWhilePS :: (Char -> Bool) -> PackedString -> PackedString
719 break_pt = char_pos_that_dissatisfies
725 if break_pt ==# 0# then
728 substrPS# ps 0# (break_pt -# 1#)
730 dropWhilePS :: (Char -> Bool) -> PackedString -> PackedString
734 break_pt = char_pos_that_dissatisfies
740 if len ==# break_pt then
743 substrPS# ps break_pt (len -# 1#)
745 elemPS :: Char -> PackedString -> Bool
749 break_pt = first_char_pos_that_satisfies
757 char_pos_that_dissatisfies :: (Char# -> Bool) -> PackedString -> Int# -> Int# -> Int#
759 char_pos_that_dissatisfies p ps len pos
760 | pos >=# len = pos -- end
761 | p (indexPS# ps pos) = -- predicate satisfied; keep going
762 char_pos_that_dissatisfies p ps len (pos +# 1#)
763 | otherwise = pos -- predicate not satisfied
765 char_pos_that_dissatisfies p ps len pos -- dead code: HACK to avoid badly-typed error msg
768 first_char_pos_that_satisfies :: (Char# -> Bool) -> PackedString -> Int# -> Int# -> Int#
769 first_char_pos_that_satisfies p ps len pos
770 | pos >=# len = pos -- end
771 | p (indexPS# ps pos) = pos -- got it!
772 | otherwise = first_char_pos_that_satisfies p ps len (pos +# 1#)
774 -- ToDo: could certainly go quicker
775 spanPS :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
776 spanPS p ps = (takeWhilePS p ps, dropWhilePS p ps)
778 breakPS :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
779 breakPS p ps = spanPS (not . p) ps
781 linesPS :: PackedString -> [PackedString]
782 linesPS ps = splitPS '\n' ps
784 wordsPS :: PackedString -> [PackedString]
785 wordsPS ps = splitWithPS isSpace ps
787 reversePS :: PackedString -> PackedString
789 if nullPS ps then -- don't create stuff unnecessarily.
793 new_ps_array (length +# 1#) >>= \ arr# -> -- incl NUL byte!
794 fill_in arr# (length -# 1#) 0# >>
795 freeze_ps_array arr# >>= \ (ByteArray _ frozen#) ->
796 let has_null = byteArrayHasNUL# frozen# length in
797 return (PS frozen# length has_null))
799 length = lengthPS# ps
801 fill_in :: MutableByteArray s Int -> Int# -> Int# -> ST s ()
802 fill_in arr_in# n i =
806 write_ps_array arr_in# i ch >>
808 write_ps_array arr_in# (i +# 1#) (chr# 0#) >>
811 fill_in arr_in# (n -# 1#) (i +# 1#)
813 concatPS :: [PackedString] -> PackedString
817 tot_len# = case (foldr ((+) . lengthPS) 0 pss) of { I# x -> x }
818 tot_len = I# tot_len#
821 new_ps_array (tot_len# +# 1#) >>= \ arr# -> -- incl NUL byte!
822 packum arr# pss 0# >>
823 freeze_ps_array arr# >>= \ (ByteArray _ frozen#) ->
825 let has_null = byteArrayHasNUL# frozen# tot_len# in
827 return (PS frozen# tot_len# has_null)
830 packum :: MutableByteArray s Int -> [PackedString] -> Int# -> ST s ()
833 = write_ps_array arr pos (chr# 0#) >>
835 packum arr (ps : pss) pos
836 = fill arr pos ps 0# (lengthPS# ps) >>= \ (I# next_pos) ->
837 packum arr pss next_pos
839 fill :: MutableByteArray s Int -> Int# -> PackedString -> Int# -> Int# -> ST s Int
841 fill arr arr_i ps ps_i ps_len
843 = return (I# (arr_i +# ps_len))
845 = write_ps_array arr (arr_i +# ps_i) (indexPS# ps ps_i) >>
846 fill arr arr_i ps (ps_i +# 1#) ps_len
848 ------------------------------------------------------------
849 joinPS :: PackedString -> [PackedString] -> PackedString
850 joinPS filler pss = concatPS (splice pss)
854 splice (x:y:xs) = x:filler:splice (y:xs)
856 -- ToDo: the obvious generalisation
858 Some properties that hold:
861 where False = any (map (x `elemPS`) ls')
862 False = any (map (nullPS) ls')
864 * all x's have been chopped out.
865 * no empty PackedStrings in returned list. A conseq.
870 * joinPS (packString [x]) (_splitPS x ls) = ls
874 splitPS :: Char -> PackedString -> [PackedString]
875 splitPS (C# ch) = splitWithPS (\ (C# c) -> c `eqChar#` ch)
877 splitWithPS :: (Char -> Bool) -> PackedString -> [PackedString]
878 splitWithPS pred ps =
888 first_char_pos_that_satisfies
894 if break_pt ==# n then -- immediate match, no substring to cut out.
895 splitify (break_pt +# 1#)
897 substrPS# ps n (break_pt -# 1#): -- leave out the matching character
898 splitify (break_pt +# 1#)
901 %************************************************************************
903 \subsection{Local utility functions}
905 %************************************************************************
907 The definition of @_substrPS@ is essentially:
908 @take (end - begin + 1) (drop begin str)@.
911 substrPS :: PackedString -> Int -> Int -> PackedString
912 substrPS ps (I# begin) (I# end) = substrPS# ps begin end
916 = error "substrPS: bounds out of range"
918 | s >=# len || result_len# <=# 0#
923 new_ps_array (result_len# +# 1#) >>= \ ch_arr -> -- incl NUL byte!
925 freeze_ps_array ch_arr >>= \ (ByteArray _ frozen#) ->
927 let has_null = byteArrayHasNUL# frozen# result_len# in
929 return (PS frozen# result_len# has_null)
934 result_len# = (if e <# len then (e +# 1#) else len) -# s
935 result_len = I# result_len#
937 -----------------------
938 fill_in :: MutableByteArray s Int -> Int# -> ST s ()
941 | idx ==# result_len#
942 = write_ps_array arr_in# idx (chr# 0#) >>
946 ch = indexPS# ps (s +# idx)
948 write_ps_array arr_in# idx ch >>
949 fill_in arr_in# (idx +# 1#)
952 (Very :-) ``Specialised'' versions of some CharArray things...
955 new_ps_array :: Int# -> ST s (MutableByteArray s Int)
956 write_ps_array :: MutableByteArray s Int -> Int# -> Char# -> ST s ()
957 freeze_ps_array :: MutableByteArray s Int -> ST s (ByteArray Int)
959 new_ps_array size = ST $ \ (S# s) ->
960 case (newCharArray# size s) of { StateAndMutableByteArray# s2# barr# ->
961 (MutableByteArray bot barr#, S# s2#)}
963 bot = error "new_ps_array"
965 write_ps_array (MutableByteArray _ barr#) n ch = ST $ \ (S# s#) ->
966 case writeCharArray# barr# n ch s# of { s2# ->
969 -- same as unsafeFreezeByteArray
970 freeze_ps_array (MutableByteArray ixs arr#) = ST $ \ (S# s#) ->
971 case unsafeFreezeByteArray# arr# s# of { StateAndByteArray# s2# frozen# ->
972 (ByteArray ixs frozen#, S# s2#) }
976 %*********************************************************
978 \subsection{Packing and unpacking C strings}
980 %*********************************************************
983 unpackCString :: Addr -> [Char]
985 -- Calls to the next four are injected by the compiler itself,
986 -- to deal with literal strings
987 packCString# :: [Char] -> ByteArray#
988 unpackCString# :: Addr# -> [Char]
989 unpackCString2# :: Addr# -> Int -> [Char]
990 unpackAppendCString# :: Addr# -> [Char] -> [Char]
991 unpackFoldrCString# :: Addr# -> (Char -> a -> a) -> a -> a
993 packCString# str = case (packString str) of { PS bytes _ _ -> bytes }
995 unpackCString (A# addr) = unpackCString# addr
1001 | ch `eqChar#` '\0'# = []
1002 | True = C# ch : unpack (nh +# 1#)
1004 ch = indexCharOffAddr# addr nh
1006 unpackCString2# addr len
1007 -- This one is called by the compiler to unpack literal strings with NULs in them; rare.
1008 = unpackPS (packCBytes len (A# addr))
1010 unpackAppendCString# addr rest
1014 | ch `eqChar#` '\0'# = rest
1015 | True = C# ch : unpack (nh +# 1#)
1017 ch = indexCharOffAddr# addr nh
1019 unpackFoldrCString# addr f z
1023 | ch `eqChar#` '\0'# = z
1024 | True = C# ch `f` unpack (nh +# 1#)
1026 ch = indexCharOffAddr# addr nh
1029 cStringToPS :: Addr -> PackedString
1030 cStringToPS (A# a#) = -- the easy one; we just believe the caller
1033 len = case (strlen# a#) of { I# x -> x }
1035 packBytesForC :: [Char] -> ByteArray Int
1036 packBytesForC str = psToByteArray (packString str)
1038 packBytesForCST :: [Char] -> ST s (ByteArray Int)
1039 packBytesForCST str =
1040 packStringST str >>= \ (PS bytes n has_null) ->
1041 --later? ASSERT(not has_null)
1042 return (ByteArray (0, I# (n -# 1#)) bytes)
1044 packNBytesForCST :: Int -> [Char] -> ST s (ByteArray Int)
1045 packNBytesForCST len str =
1046 packNCharsST len str >>= \ (PS bytes n has_null) ->
1047 return (ByteArray (0, I# (n -# 1#)) bytes)
1049 packCBytes :: Int -> Addr -> PackedString
1050 packCBytes len addr = runST (packCBytesST len addr)
1052 packCBytesST :: Int -> Addr -> ST s PackedString
1053 packCBytesST len@(I# length#) (A# addr) =
1055 allocate an array that will hold the string
1056 (not forgetting the NUL byte at the end)
1058 new_ps_array (length# +# 1#) >>= \ ch_array ->
1059 -- fill in packed string from "addr"
1060 fill_in ch_array 0# >>
1061 -- freeze the puppy:
1062 freeze_ps_array ch_array >>= \ (ByteArray _ frozen#) ->
1063 let has_null = byteArrayHasNUL# frozen# length# in
1064 return (PS frozen# length# has_null)
1066 fill_in :: MutableByteArray s Int -> Int# -> ST s ()
1070 = write_ps_array arr_in# idx (chr# 0#) >>
1073 = case (indexCharOffAddr# addr idx) of { ch ->
1074 write_ps_array arr_in# idx ch >>
1075 fill_in arr_in# (idx +# 1#) }