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.
12 PackedString, -- abstract
14 -- Creating the beasts
15 packString, -- :: [Char] -> PackedString
16 packStringST, -- :: [Char] -> ST s PackedString
17 packCBytesST, -- :: Int -> Addr -> ST s PackedString
19 byteArrayToPS, -- :: ByteArray Int -> PackedString
20 cByteArrayToPS, -- :: ByteArray Int -> PackedString
21 unsafeByteArrayToPS, -- :: ByteArray a -> Int -> PackedString
23 psToByteArray, -- :: PackedString -> ByteArray Int
24 psToByteArrayST, -- :: PackedString -> ST s (ByteArray Int)
25 psToCString, -- :: PackedString -> Addr
26 isCString, -- :: PackedString -> Bool
28 unpackPS, -- :: PackedString -> [Char]
29 unpackNBytesPS, -- :: PackedString -> Int -> [Char]
30 unpackPSIO, -- :: PackedString -> IO [Char]
32 hPutPS, -- :: Handle -> PackedString -> IO ()
34 nilPS, -- :: PackedString
35 consPS, -- :: Char -> PackedString -> 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
72 -- Converting to C strings
85 import PrelBase ( showList__ ) -- ToDo: better
88 import PrelArr ( StateAndMutableByteArray#(..) , StateAndByteArray#(..) )
91 import IOExts ( unsafePerformIO )
99 %************************************************************************
101 \subsection{@PackedString@ type declaration}
103 %************************************************************************
107 = PS ByteArray# -- the bytes
108 Int# -- length (*not* including NUL at the end)
109 Bool -- True <=> contains a NUL
110 | CPS Addr# -- pointer to the (null-terminated) bytes in C land
111 Int# -- length, as per strlen
112 -- definitely doesn't contain a NUL
114 instance Eq PackedString where
115 x == y = compare x y == EQ
116 x /= y = compare x y /= EQ
118 instance Ord PackedString where
120 x <= y = compare x y /= GT
121 x < y = compare x y == LT
122 x >= y = compare x y /= LT
123 x > y = compare x y == GT
124 max x y = case (compare x y) of { LT -> y ; EQ -> x ; GT -> x }
125 min x y = case (compare x y) of { LT -> x ; EQ -> x ; GT -> y }
127 --instance Read PackedString: ToDo
129 instance Show PackedString where
130 showsPrec p ps r = showsPrec p (unpackPS ps) r
131 showList = showList__ (showsPrec 0)
135 %************************************************************************
137 \subsection{@PackedString@ instances}
139 %************************************************************************
141 We try hard to make this go fast:
143 comparePS :: PackedString -> PackedString -> Ordering
145 comparePS (PS bs1 len1 has_null1) (PS bs2 len2 has_null2)
146 | not has_null1 && not has_null2
148 _ccall_ strcmp ba1 ba2 >>= \ (I# res) ->
151 else if res ==# 0# then EQ
155 ba1 = ByteArray (0, I# (len1 -# 1#)) bs1
156 ba2 = ByteArray (0, I# (len2 -# 1#)) bs2
158 comparePS (PS bs1 len1 has_null1) (CPS bs2 len2)
161 _ccall_ strcmp ba1 ba2 >>= \ (I# res) ->
164 else if res ==# 0# then EQ
168 ba1 = ByteArray (0, I# (len1 -# 1#)) bs1
171 comparePS (CPS bs1 len1) (CPS bs2 len2)
173 _ccall_ strcmp ba1 ba2 >>= \ (I# res) ->
176 else if res ==# 0# then EQ
183 comparePS a@(CPS _ _) b@(PS _ _ has_null2)
185 = -- try them the other way 'round
186 case (comparePS b a) of { LT -> GT; EQ -> EQ; GT -> LT }
188 comparePS ps1 ps2 -- slow catch-all case (esp for "has_null" True)
191 end1 = lengthPS# ps1 -# 1#
192 end2 = lengthPS# ps2 -# 1#
195 = if char# ># end1 then
196 if char# ># end2 then -- both strings ran out at once
198 else -- ps1 ran out before ps2
200 else if char# ># end2 then
201 GT -- ps2 ran out before ps1
204 ch1 = indexPS# ps1 char#
205 ch2 = indexPS# ps2 char#
207 if ch1 `eqChar#` ch2 then
208 looking_at (char# +# 1#)
209 else if ch1 `ltChar#` ch2 then LT
214 %************************************************************************
216 \subsection{Constructor functions}
218 %************************************************************************
220 Easy ones first. @packString@ requires getting some heap-bytes and
221 scribbling stuff into them.
224 nilPS :: PackedString
227 consPS :: Char -> PackedString -> PackedString
228 consPS c cs = packString (c : (unpackPS cs)) -- ToDo:better
230 packString :: [Char] -> PackedString
231 packString str = runST (packStringST str)
233 packStringST :: [Char] -> ST s PackedString
235 let len = length str in
238 packNCharsST :: Int -> [Char] -> ST s PackedString
239 packNCharsST len@(I# length#) str =
241 allocate an array that will hold the string
242 (not forgetting the NUL byte at the end)
244 new_ps_array (length# +# 1#) >>= \ ch_array ->
245 -- fill in packed string from "str"
246 fill_in ch_array 0# str >>
248 freeze_ps_array ch_array >>= \ (ByteArray _ frozen#) ->
249 let has_null = byteArrayHasNUL# frozen# length# in
250 return (PS frozen# length# has_null)
252 fill_in :: MutableByteArray s Int -> Int# -> [Char] -> ST s ()
253 fill_in arr_in# idx [] =
254 write_ps_array arr_in# idx (chr# 0#) >>
257 fill_in arr_in# idx (C# c : cs) =
258 write_ps_array arr_in# idx c >>
259 fill_in arr_in# (idx +# 1#) cs
261 byteArrayToPS :: ByteArray Int -> PackedString
262 byteArrayToPS (ByteArray ixs@(_, ix_end) frozen#) =
268 else ((index ixs ix_end) + 1)
271 PS frozen# n# (byteArrayHasNUL# frozen# n#)
273 -- byteArray is zero-terminated, make everything upto it
275 cByteArrayToPS :: ByteArray Int -> PackedString
276 cByteArrayToPS (ByteArray ixs@(_, ix_end) frozen#) =
282 else ((index ixs ix_end) + 1)
288 | ch# `eqChar#` '\0'# = i# -- everything upto the sentinel
289 | otherwise = findNull (i# +# 1#)
291 ch# = indexCharArray# frozen# i#
293 PS frozen# len# False
295 unsafeByteArrayToPS :: ByteArray a -> Int -> PackedString
296 unsafeByteArrayToPS (ByteArray _ frozen#) (I# n#)
297 = PS frozen# n# (byteArrayHasNUL# frozen# n#)
299 psToByteArray :: PackedString -> ByteArray Int
300 psToByteArray (PS bytes n has_null)
301 = ByteArray (0, I# (n -# 1#)) bytes
303 psToByteArray (CPS addr len#)
306 byte_array_form = packCBytes len (A# addr)
308 case byte_array_form of { PS bytes _ _ ->
309 ByteArray (0, len - 1) bytes }
311 -- isCString is useful when passing PackedStrings to the
312 -- outside world, and need to figure out whether you can
313 -- pass it as an Addr or ByteArray.
315 isCString :: PackedString -> Bool
316 isCString (CPS _ _ ) = True
319 psToCString :: PackedString -> Addr
320 psToCString (CPS addr _) = (A# addr)
321 psToCString (PS bytes n# has_null) =
323 stuff <- _ccall_ malloc ((I# n#) * (``sizeof(char)''))
326 | n# ==# 0# = return ()
328 let ch# = indexCharArray# bytes i#
329 writeCharOffAddr stuff (I# i#) (C# ch#)
330 fill_in (n# -# 1#) (i# +# 1#)
336 %************************************************************************
338 \subsection{Destructor functions (taking @PackedStrings@ apart)}
340 %************************************************************************
343 -- OK, but this code gets *hammered*:
345 -- = [ indexPS ps n | n <- [ 0::Int .. lengthPS ps - 1 ] ]
347 unpackPS :: PackedString -> [Char]
348 unpackPS (PS bytes len has_null)
353 | otherwise = C# ch : unpack (nh +# 1#)
355 ch = indexCharArray# bytes nh
357 unpackPS (CPS addr len)
361 | ch `eqChar#` '\0'# = []
362 | otherwise = C# ch : unpack (nh +# 1#)
364 ch = indexCharOffAddr# addr nh
366 unpackNBytesPS :: PackedString -> Int -> [Char]
367 unpackNBytesPS ps len@(I# l#)
368 | len < 0 = error ("PackedString.unpackNBytesPS: negative length "++ show len)
372 PS bytes len# has_null -> unpackPS (PS bytes (min# len# l#) has_null)
373 CPS a len# -> unpackPS (CPS a (min# len# l#))
379 unpackPSIO :: PackedString -> IO String
380 unpackPSIO ps@(PS bytes len has_null) = return (unpackPS ps)
381 unpackPSIO (CPS addr len)
385 ch <- readCharOffAddr (A# addr) (I# nh)
389 ls <- unpack (nh +# 1#)
394 Output a packed string via a handle:
397 hPutPS :: Handle -> PackedString -> IO ()
398 hPutPS handle (CPS a# len#) = hPutBuf handle (A# a#) (I# len#)
399 hPutPS handle (PS ba# len# _) = hPutBufBA handle (ByteArray bottom ba#) (I# len#)
401 bottom = error "hPutPS"
404 The dual to @_putPS@, note that the size of the chunk specified
405 is the upper bound of the size of the chunk returned.
409 getPS :: _FILE -> Int -> IO PackedString
410 getPS file len@(I# len#)
411 | len# <=# 0# = return nilPS -- I'm being kind here.
413 -- Allocate an array for system call to store its bytes into.
414 new_ps_array len# >>= \ ch_arr ->
415 freeze_ps_array ch_arr >>= \ (ByteArray _ frozen#) ->
417 byte_array = ByteArray (0, I# len#) frozen#
419 _ccall_ fread byte_array (1::Int) len file >>= \ (I# read#) ->
420 if read# ==# 0# then -- EOF or other error
421 error "getPS: EOF reached or other error"
424 The system call may not return the number of
425 bytes requested. Instead of failing with an error
426 if the number of bytes read is less than requested,
427 a packed string containing the bytes we did manage
428 to snarf is returned.
431 has_null = byteArrayHasNUL# frozen# read#
433 return (PS frozen# read# has_null)
437 %************************************************************************
439 \subsection{List-mimicking functions for @PackedStrings@}
441 %************************************************************************
443 First, the basic functions that do look into the representation;
444 @indexPS@ is the most important one.
447 lengthPS :: PackedString -> Int
448 lengthPS ps = I# (lengthPS# ps)
450 {-# INLINE lengthPS# #-}
452 lengthPS# (PS _ i _) = i
453 lengthPS# (CPS _ i) = i
455 {-# INLINE strlen# #-}
457 strlen# :: Addr# -> Int
460 _ccall_ strlen (A# a) >>= \ len@(I# _) ->
464 byteArrayHasNUL# :: ByteArray# -> Int#{-length-} -> Bool
465 byteArrayHasNUL# bs len
467 _ccall_ byteArrayHasNUL__ ba (I# len) >>= \ (I# res) ->
469 if res ==# 0# then False else True
472 ba = ByteArray (0, I# (len -# 1#)) bs
474 -----------------------
476 indexPS :: PackedString -> Int -> Char
477 indexPS ps (I# n) = C# (indexPS# ps n)
479 {-# INLINE indexPS# #-}
481 indexPS# (PS bs i _) n
482 = --ASSERT (n >=# 0# && n <# i) -- error checking: my eye! (WDP 94/10)
486 = indexCharOffAddr# a n
489 Now, the rest of the functions can be defined without digging
490 around in the representation.
493 headPS :: PackedString -> Char
495 | nullPS ps = error "headPS: head []"
496 | otherwise = C# (indexPS# ps 0#)
498 tailPS :: PackedString -> PackedString
500 | len <=# 0# = error "tailPS: tail []"
502 | otherwise = substrPS# ps 1# (len -# 1#)
506 nullPS :: PackedString -> Bool
507 nullPS (PS _ i _) = i ==# 0#
508 nullPS (CPS _ i) = i ==# 0#
510 appendPS :: PackedString -> PackedString -> PackedString
514 | otherwise = concatPS [xs,ys]
516 mapPS :: (Char -> Char) -> PackedString -> PackedString {-or String?-}
522 new_ps_array (length +# 1#) >>= \ ps_arr ->
523 whizz ps_arr length 0# >>
524 freeze_ps_array ps_arr >>= \ (ByteArray _ frozen#) ->
525 let has_null = byteArrayHasNUL# frozen# length in
526 return (PS frozen# length has_null))
528 length = lengthPS# xs
530 whizz :: MutableByteArray s Int -> Int# -> Int# -> ST s ()
533 = write_ps_array arr# i (chr# 0#) >>
539 write_ps_array arr# i (case f (C# ch) of { (C# x) -> x}) >>
540 whizz arr# (n -# 1#) (i +# 1#)
542 filterPS :: (Char -> Bool) -> PackedString -> PackedString {-or String?-}
548 Filtering proceeds as follows:
550 * traverse the list, applying the pred. to each element,
551 remembering the positions where it was satisfied.
553 Encode these positions using a run-length encoding of the gaps
554 between the matching positions.
556 * Allocate a MutableByteArray in the heap big enough to hold
557 all the matched entries, and copy the elements that matched over.
559 A better solution that merges the scan© passes into one,
560 would be to copy the filtered elements over into a growable
561 buffer. No such operation currently supported over
562 MutableByteArrays (could of course use malloc&realloc)
563 But, this solution may in the case of repeated realloc's
564 be worse than the current solution.
568 (rle,len_filtered) = filter_ps (len# -# 1#) 0# 0# []
569 len_filtered# = case len_filtered of { I# x# -> x#}
571 if len# ==# len_filtered# then
572 {- not much filtering as everything passed through. -}
574 else if len_filtered# ==# 0# then
577 new_ps_array (len_filtered# +# 1#) >>= \ ps_arr ->
578 copy_arr ps_arr rle 0# 0# >>
579 freeze_ps_array ps_arr >>= \ (ByteArray _ frozen#) ->
580 let has_null = byteArrayHasNUL# frozen# len_filtered# in
581 return (PS frozen# len_filtered# has_null))
585 matchOffset :: Int# -> [Char] -> (Int,[Char])
586 matchOffset off [] = (I# off,[])
587 matchOffset off (C# c:cs) =
592 if x==# 0# then -- escape code, add 255#
597 copy_arr :: MutableByteArray s Int -> [Char] -> Int# -> Int# -> ST s ()
598 copy_arr arr# [_] _ _ = return ()
599 copy_arr arr# ls n i =
601 (x,ls') = matchOffset 0# ls
602 n' = n +# (case x of { (I# x#) -> x#}) -# 1#
605 write_ps_array arr# i ch >>
606 copy_arr arr# ls' (n' +# 1#) (i +# 1#)
608 esc :: Int# -> Int# -> [Char] -> [Char]
609 esc v 0# ls = (C# (chr# v)):ls
610 esc v n ls = esc v (n -# 1#) (C# (chr# 0#):ls)
612 filter_ps :: Int# -> Int# -> Int# -> [Char] -> ([Char],Int)
613 filter_ps n hits run acc
616 escs = run `quotInt#` 255#
617 v = run `remInt#` 255#
619 (esc (v +# 1#) escs acc, I# hits)
627 escs = run `quotInt#` 255#
628 v = run `remInt#` 255#
629 acc' = esc (v +# 1#) escs acc
631 filter_ps n' (hits +# 1#) 0# acc'
633 filter_ps n' hits (run +# 1#) acc
636 foldlPS :: (a -> Char -> a) -> a -> PackedString -> a
645 --whizzLR :: a -> Int# -> a
648 | otherwise = whizzLR (f b (C# (indexPS# ps idx))) (idx +# 1#)
651 foldrPS :: (Char -> a -> a) -> a -> PackedString -> a
660 --whizzRL :: a -> Int# -> a
663 | otherwise = whizzRL (f (C# (indexPS# ps idx)) b) (idx -# 1#)
665 takePS :: Int -> PackedString -> PackedString
668 | otherwise = substrPS# ps 0# (n -# 1#)
670 dropPS :: Int -> PackedString -> PackedString
673 | otherwise = substrPS# ps n (lengthPS# ps -# 1#)
677 splitAtPS :: Int -> PackedString -> (PackedString, PackedString)
678 splitAtPS n ps = (takePS n ps, dropPS n ps)
680 takeWhilePS :: (Char -> Bool) -> PackedString -> PackedString
683 break_pt = char_pos_that_dissatisfies
689 if break_pt ==# 0# then
692 substrPS# ps 0# (break_pt -# 1#)
694 dropWhilePS :: (Char -> Bool) -> PackedString -> PackedString
698 break_pt = char_pos_that_dissatisfies
704 if len ==# break_pt then
707 substrPS# ps break_pt (len -# 1#)
709 elemPS :: Char -> PackedString -> Bool
713 break_pt = first_char_pos_that_satisfies
721 char_pos_that_dissatisfies :: (Char# -> Bool) -> PackedString -> Int# -> Int# -> Int#
723 char_pos_that_dissatisfies p ps len pos
724 | pos >=# len = pos -- end
725 | p (indexPS# ps pos) = -- predicate satisfied; keep going
726 char_pos_that_dissatisfies p ps len (pos +# 1#)
727 | otherwise = pos -- predicate not satisfied
729 first_char_pos_that_satisfies :: (Char# -> Bool) -> PackedString -> Int# -> Int# -> Int#
730 first_char_pos_that_satisfies p ps len pos
731 | pos >=# len = pos -- end
732 | p (indexPS# ps pos) = pos -- got it!
733 | otherwise = first_char_pos_that_satisfies p ps len (pos +# 1#)
735 -- ToDo: could certainly go quicker
736 spanPS :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
737 spanPS p ps = (takeWhilePS p ps, dropWhilePS p ps)
739 breakPS :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
740 breakPS p ps = spanPS (not . p) ps
742 linesPS :: PackedString -> [PackedString]
743 linesPS ps = splitPS '\n' ps
745 wordsPS :: PackedString -> [PackedString]
746 wordsPS ps = splitWithPS isSpace ps
748 reversePS :: PackedString -> PackedString
750 if nullPS ps then -- don't create stuff unnecessarily.
754 new_ps_array (length +# 1#) >>= \ arr# -> -- incl NUL byte!
755 fill_in arr# (length -# 1#) 0# >>
756 freeze_ps_array arr# >>= \ (ByteArray _ frozen#) ->
757 let has_null = byteArrayHasNUL# frozen# length in
758 return (PS frozen# length has_null))
760 length = lengthPS# ps
762 fill_in :: MutableByteArray s Int -> Int# -> Int# -> ST s ()
763 fill_in arr_in# n i =
767 write_ps_array arr_in# i ch >>
769 write_ps_array arr_in# (i +# 1#) (chr# 0#) >>
772 fill_in arr_in# (n -# 1#) (i +# 1#)
774 concatPS :: [PackedString] -> PackedString
778 tot_len# = case (foldr ((+) . lengthPS) 0 pss) of { I# x -> x }
779 tot_len = I# tot_len#
782 new_ps_array (tot_len# +# 1#) >>= \ arr# -> -- incl NUL byte!
783 packum arr# pss 0# >>
784 freeze_ps_array arr# >>= \ (ByteArray _ frozen#) ->
786 let has_null = byteArrayHasNUL# frozen# tot_len# in
788 return (PS frozen# tot_len# has_null)
791 packum :: MutableByteArray s Int -> [PackedString] -> Int# -> ST s ()
794 = write_ps_array arr pos (chr# 0#) >>
796 packum arr (ps : pss) pos
797 = fill arr pos ps 0# (lengthPS# ps) >>= \ (I# next_pos) ->
798 packum arr pss next_pos
800 fill :: MutableByteArray s Int -> Int# -> PackedString -> Int# -> Int# -> ST s Int
802 fill arr arr_i ps ps_i ps_len
804 = return (I# (arr_i +# ps_len))
806 = write_ps_array arr (arr_i +# ps_i) (indexPS# ps ps_i) >>
807 fill arr arr_i ps (ps_i +# 1#) ps_len
809 ------------------------------------------------------------
810 joinPS :: PackedString -> [PackedString] -> PackedString
811 joinPS filler pss = concatPS (splice pss)
815 splice (x:y:xs) = x:filler:splice (y:xs)
817 -- ToDo: the obvious generalisation
819 Some properties that hold:
822 where False = any (map (x `elemPS`) ls')
823 False = any (map (nullPS) ls')
825 * all x's have been chopped out.
826 * no empty PackedStrings in returned list. A conseq.
831 * joinPS (packString [x]) (_splitPS x ls) = ls
835 splitPS :: Char -> PackedString -> [PackedString]
836 splitPS (C# ch) = splitWithPS (\ (C# c) -> c `eqChar#` ch)
838 splitWithPS :: (Char -> Bool) -> PackedString -> [PackedString]
839 splitWithPS pred ps =
849 first_char_pos_that_satisfies
855 if break_pt ==# n then -- immediate match, no substring to cut out.
856 splitify (break_pt +# 1#)
858 substrPS# ps n (break_pt -# 1#): -- leave out the matching character
859 splitify (break_pt +# 1#)
862 %************************************************************************
864 \subsection{Local utility functions}
866 %************************************************************************
868 The definition of @_substrPS@ is essentially:
869 @take (end - begin + 1) (drop begin str)@.
872 substrPS :: PackedString -> Int -> Int -> PackedString
873 substrPS ps (I# begin) (I# end) = substrPS# ps begin end
877 = error "substrPS: bounds out of range"
879 | s >=# len || result_len# <=# 0#
884 new_ps_array (result_len# +# 1#) >>= \ ch_arr -> -- incl NUL byte!
886 freeze_ps_array ch_arr >>= \ (ByteArray _ frozen#) ->
888 let has_null = byteArrayHasNUL# frozen# result_len# in
890 return (PS frozen# result_len# has_null)
895 result_len# = (if e <# len then (e +# 1#) else len) -# s
896 result_len = I# result_len#
898 -----------------------
899 fill_in :: MutableByteArray s Int -> Int# -> ST s ()
902 | idx ==# result_len#
903 = write_ps_array arr_in# idx (chr# 0#) >>
907 ch = indexPS# ps (s +# idx)
909 write_ps_array arr_in# idx ch >>
910 fill_in arr_in# (idx +# 1#)
913 (Very :-) ``Specialised'' versions of some CharArray things...
916 new_ps_array :: Int# -> ST s (MutableByteArray s Int)
917 write_ps_array :: MutableByteArray s Int -> Int# -> Char# -> ST s ()
918 freeze_ps_array :: MutableByteArray s Int -> ST s (ByteArray Int)
920 new_ps_array size = ST $ \ s# ->
921 case newCharArray# size s# of { StateAndMutableByteArray# s2# barr# ->
922 STret s2# (MutableByteArray bot barr#)}
924 bot = error "new_ps_array"
926 write_ps_array (MutableByteArray _ barr#) n ch = ST $ \ s# ->
927 case writeCharArray# barr# n ch s# of { s2# ->
930 -- same as unsafeFreezeByteArray
931 freeze_ps_array (MutableByteArray ixs arr#) = ST $ \ s# ->
932 case unsafeFreezeByteArray# arr# s# of { StateAndByteArray# s2# frozen# ->
933 STret s2# (ByteArray ixs frozen#) }
937 %*********************************************************
939 \subsection{Packing and unpacking C strings}
941 %*********************************************************
944 unpackCString :: Addr -> [Char]
946 -- Calls to the next four are injected by the compiler itself,
947 -- to deal with literal strings
948 packCString# :: [Char] -> ByteArray#
949 unpackCString# :: Addr# -> [Char]
950 unpackCString2# :: Addr# -> Int# -> [Char]
951 unpackAppendCString# :: Addr# -> [Char] -> [Char]
952 unpackFoldrCString# :: Addr# -> (Char -> a -> a) -> a -> a
954 packCString# str = case (packString str) of { PS bytes _ _ -> bytes }
956 unpackCString a@(A# addr) =
957 if a == ``NULL'' then
966 | ch `eqChar#` '\0'# = []
967 | otherwise = C# ch : unpack (nh +# 1#)
969 ch = indexCharOffAddr# addr nh
971 unpackCStringIO :: Addr -> IO String
973 | addr == ``NULL'' = return ""
974 | otherwise = unpack 0#
977 ch <- readCharOffAddr addr (I# nh)
981 ls <- unpack (nh +# 1#)
984 -- unpack 'len' chars
985 unpackCStringLenIO :: Addr -> Int -> IO String
986 unpackCStringLenIO addr l@(I# len#)
987 | len# <# 0# = fail (userError ("PackedString.unpackCStringLenIO: negative length (" ++ show l ++ ")"))
988 | len# ==# 0# = return ""
989 | otherwise = unpack [] (len# -# 1#)
992 ch <- readCharOffAddr addr (I# 0#)
995 ch <- readCharOffAddr addr (I# nh)
996 unpack (ch:acc) (nh -# 1#)
998 unpackCString2# addr len
999 -- This one is called by the compiler to unpack literal strings with NULs in them; rare.
1000 = unpackPS (packCBytes (I# len) (A# addr))
1002 unpackAppendCString# addr rest
1006 | ch `eqChar#` '\0'# = rest
1007 | otherwise = C# ch : unpack (nh +# 1#)
1009 ch = indexCharOffAddr# addr nh
1011 unpackFoldrCString# addr f z
1015 | ch `eqChar#` '\0'# = z
1016 | otherwise = C# ch `f` unpack (nh +# 1#)
1018 ch = indexCharOffAddr# addr nh
1021 cStringToPS :: Addr -> PackedString
1022 cStringToPS (A# a#) = -- the easy one; we just believe the caller
1025 len = case (strlen# a#) of { I# x -> x }
1027 packBytesForC :: [Char] -> ByteArray Int
1028 packBytesForC str = psToByteArray (packString str)
1030 psToByteArrayST :: [Char] -> ST s (ByteArray Int)
1031 psToByteArrayST str =
1032 packStringST str >>= \ (PS bytes n has_null) ->
1033 --later? ASSERT(not has_null)
1034 return (ByteArray (0, I# (n -# 1#)) bytes)
1036 packNBytesForCST :: Int -> [Char] -> ST s (ByteArray Int)
1037 packNBytesForCST len str =
1038 packNCharsST len str >>= \ (PS bytes n has_null) ->
1039 return (ByteArray (0, I# (n -# 1#)) bytes)
1041 packCBytes :: Int -> Addr -> PackedString
1042 packCBytes len addr = runST (packCBytesST len addr)
1044 packCBytesST :: Int -> Addr -> ST s PackedString
1045 packCBytesST len@(I# length#) (A# addr) =
1047 allocate an array that will hold the string
1048 (not forgetting the NUL byte at the end)
1050 new_ps_array (length# +# 1#) >>= \ ch_array ->
1051 -- fill in packed string from "addr"
1052 fill_in ch_array 0# >>
1053 -- freeze the puppy:
1054 freeze_ps_array ch_array >>= \ (ByteArray _ frozen#) ->
1055 let has_null = byteArrayHasNUL# frozen# length# in
1056 return (PS frozen# length# has_null)
1058 fill_in :: MutableByteArray s Int -> Int# -> ST s ()
1062 = write_ps_array arr_in# idx (chr# 0#) >>
1065 = case (indexCharOffAddr# addr idx) of { ch ->
1066 write_ps_array arr_in# idx ch >>
1067 fill_in arr_in# (idx +# 1#) }