2 % (c) The GRASP/AQUA Project, Glasgow University, 1993-1998
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 psToCString, -- :: PackedString -> Addr
25 isCString, -- :: PackedString -> Bool
27 unpackPS, -- :: PackedString -> [Char]
28 unpackNBytesPS, -- :: PackedString -> Int -> [Char]
29 unpackPSIO, -- :: PackedString -> IO [Char]
31 hPutPS, -- :: Handle -> PackedString -> IO ()
32 hGetPS, -- :: Handle -> Int -> IO PackedString
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
70 comparePS -- :: PackedString -> PackedString -> Ordering
75 import PrelBase ( showList__ ) -- ToDo: better
83 import PrelArr ( StateAndMutableByteArray#(..) , StateAndByteArray#(..) )
86 import IOExts ( unsafePerformIO )
88 import PrelHandle ( hFillBufBA )
95 %************************************************************************
97 \subsection{@PackedString@ type declaration}
99 %************************************************************************
103 = PS ByteArray# -- the bytes
104 Int# -- length (*not* including NUL at the end)
105 Bool -- True <=> contains a NUL
106 | CPS Addr# -- pointer to the (null-terminated) bytes in C land
107 Int# -- length, as per strlen
108 -- definitely doesn't contain a NUL
110 instance Eq PackedString where
111 x == y = compare x y == EQ
112 x /= y = compare x y /= EQ
114 instance Ord PackedString where
116 x <= y = compare x y /= GT
117 x < y = compare x y == LT
118 x >= y = compare x y /= LT
119 x > y = compare x y == GT
120 max x y = case (compare x y) of { LT -> y ; EQ -> x ; GT -> x }
121 min x y = case (compare x y) of { LT -> x ; EQ -> x ; GT -> y }
123 --instance Read PackedString: ToDo
125 instance Show PackedString where
126 showsPrec p ps r = showsPrec p (unpackPS ps) r
127 showList = showList__ (showsPrec 0)
131 %************************************************************************
133 \subsection{@PackedString@ instances}
135 %************************************************************************
137 We try hard to make this go fast:
139 comparePS :: PackedString -> PackedString -> Ordering
141 comparePS (PS bs1 len1 has_null1) (PS bs2 len2 has_null2)
142 | not has_null1 && not has_null2
144 _ccall_ strcmp ba1 ba2 >>= \ (I# res) ->
147 else if res ==# 0# then EQ
151 ba1 = ByteArray (0, I# (len1 -# 1#)) bs1
152 ba2 = ByteArray (0, I# (len2 -# 1#)) bs2
154 comparePS (PS bs1 len1 has_null1) (CPS bs2 len2)
157 _ccall_ strcmp ba1 ba2 >>= \ (I# res) ->
160 else if res ==# 0# then EQ
164 ba1 = ByteArray (0, I# (len1 -# 1#)) bs1
167 comparePS (CPS bs1 len1) (CPS bs2 len2)
169 _ccall_ strcmp ba1 ba2 >>= \ (I# res) ->
172 else if res ==# 0# then EQ
179 comparePS a@(CPS _ _) b@(PS _ _ has_null2)
181 = -- try them the other way 'round
182 case (comparePS b a) of { LT -> GT; EQ -> EQ; GT -> LT }
184 comparePS ps1 ps2 -- slow catch-all case (esp for "has_null" True)
187 end1 = lengthPS# ps1 -# 1#
188 end2 = lengthPS# ps2 -# 1#
191 = if char# ># end1 then
192 if char# ># end2 then -- both strings ran out at once
194 else -- ps1 ran out before ps2
196 else if char# ># end2 then
197 GT -- ps2 ran out before ps1
200 ch1 = indexPS# ps1 char#
201 ch2 = indexPS# ps2 char#
203 if ch1 `eqChar#` ch2 then
204 looking_at (char# +# 1#)
205 else if ch1 `ltChar#` ch2 then LT
210 %************************************************************************
212 \subsection{Constructor functions}
214 %************************************************************************
216 Easy ones first. @packString@ requires getting some heap-bytes and
217 scribbling stuff into them.
220 nilPS :: PackedString
223 consPS :: Char -> PackedString -> PackedString
224 consPS c cs = packString (c : (unpackPS cs)) -- ToDo:better
226 packString :: [Char] -> PackedString
227 packString str = runST (packStringST str)
229 packStringST :: [Char] -> ST s PackedString
231 let len = length str in
234 packNCharsST :: Int -> [Char] -> ST s PackedString
235 packNCharsST len@(I# length#) str =
237 allocate an array that will hold the string
238 (not forgetting the NUL byte at the end)
240 new_ps_array (length# +# 1#) >>= \ ch_array ->
241 -- fill in packed string from "str"
242 fill_in ch_array 0# str >>
244 freeze_ps_array ch_array length# >>= \ (ByteArray _ frozen#) ->
245 let has_null = byteArrayHasNUL# frozen# length# in
246 return (PS frozen# length# has_null)
248 fill_in :: MutableByteArray s Int -> Int# -> [Char] -> ST s ()
249 fill_in arr_in# idx [] =
250 write_ps_array arr_in# idx (chr# 0#) >>
253 fill_in arr_in# idx (C# c : cs) =
254 write_ps_array arr_in# idx c >>
255 fill_in arr_in# (idx +# 1#) cs
257 byteArrayToPS :: ByteArray Int -> PackedString
258 byteArrayToPS (ByteArray ixs@(_, ix_end) frozen#) =
264 else ((index ixs ix_end) + 1)
267 PS frozen# n# (byteArrayHasNUL# frozen# n#)
269 -- byteArray is zero-terminated, make everything upto it
271 cByteArrayToPS :: ByteArray Int -> PackedString
272 cByteArrayToPS (ByteArray ixs@(_, ix_end) frozen#) =
278 else ((index ixs ix_end) + 1)
284 | ch# `eqChar#` '\0'# = i# -- everything upto the sentinel
285 | otherwise = findNull (i# +# 1#)
287 ch# = indexCharArray# frozen# i#
289 PS frozen# len# False
291 unsafeByteArrayToPS :: ByteArray a -> Int -> PackedString
292 unsafeByteArrayToPS (ByteArray _ frozen#) (I# n#)
293 = PS frozen# n# (byteArrayHasNUL# frozen# n#)
295 psToByteArray :: PackedString -> ByteArray Int
296 psToByteArray (PS bytes n has_null)
297 = ByteArray (0, I# (n -# 1#)) bytes
299 psToByteArray (CPS addr len#)
302 byte_array_form = packCBytes len (A# addr)
304 case byte_array_form of { PS bytes _ _ ->
305 ByteArray (0, len - 1) bytes }
307 -- isCString is useful when passing PackedStrings to the
308 -- outside world, and need to figure out whether you can
309 -- pass it as an Addr or ByteArray.
311 isCString :: PackedString -> Bool
312 isCString (CPS _ _ ) = True
315 psToCString :: PackedString -> Addr
316 psToCString (CPS addr _) = (A# addr)
317 psToCString (PS bytes n# has_null) =
319 stuff <- _ccall_ malloc ((I# n#) * (``sizeof(char)''))
322 | n# ==# 0# = return ()
324 let ch# = indexCharArray# bytes i#
325 writeCharOffAddr stuff (I# i#) (C# ch#)
326 fill_in (n# -# 1#) (i# +# 1#)
332 %************************************************************************
334 \subsection{Destructor functions (taking @PackedStrings@ apart)}
336 %************************************************************************
339 -- OK, but this code gets *hammered*:
341 -- = [ indexPS ps n | n <- [ 0::Int .. lengthPS ps - 1 ] ]
343 unpackPS :: PackedString -> [Char]
344 unpackPS (PS bytes len has_null)
349 | otherwise = C# ch : unpack (nh +# 1#)
351 ch = indexCharArray# bytes nh
353 unpackPS (CPS addr len)
357 | ch `eqChar#` '\0'# = []
358 | otherwise = C# ch : unpack (nh +# 1#)
360 ch = indexCharOffAddr# addr nh
362 unpackNBytesPS :: PackedString -> Int -> [Char]
363 unpackNBytesPS ps len@(I# l#)
364 | len < 0 = error ("PackedString.unpackNBytesPS: negative length "++ show len)
368 PS bytes len# has_null -> unpackPS (PS bytes (min# len# l#) has_null)
369 CPS a len# -> unpackPS (CPS a (min# len# l#))
375 unpackPSIO :: PackedString -> IO String
376 unpackPSIO ps@(PS bytes len has_null) = return (unpackPS ps)
377 unpackPSIO (CPS addr len)
381 ch <- readCharOffAddr (A# addr) (I# nh)
385 ls <- unpack (nh +# 1#)
390 Output a packed string via a handle:
393 hPutPS :: Handle -> PackedString -> IO ()
394 hPutPS handle (CPS a# len#) = hPutBuf handle (A# a#) (I# len#)
395 hPutPS handle (PS ba# len# _) = hPutBufBA handle (ByteArray bottom ba#) (I# len#)
397 bottom = error "hPutPS"
400 The dual to @_putPS@, note that the size of the chunk specified
401 is the upper bound of the size of the chunk returned.
404 hGetPS :: Handle -> Int -> IO PackedString
405 hGetPS hdl len@(I# len#)
406 | len# <=# 0# = return nilPS -- I'm being kind here.
408 -- Allocate an array for system call to store its bytes into.
409 stToIO (new_ps_array len# ) >>= \ ch_arr ->
410 stToIO (freeze_ps_array ch_arr len#) >>= \ (ByteArray _ frozen#) ->
412 byte_array = ByteArray (0, I# len#) frozen#
414 hFillBufBA hdl byte_array len >>= \ (I# read#) ->
415 if read# ==# 0# then -- EOF or other error
416 fail (userError "hGetPS: EOF reached or other error")
419 The system call may not return the number of
420 bytes requested. Instead of failing with an error
421 if the number of bytes read is less than requested,
422 a packed string containing the bytes we did manage
423 to snarf is returned.
426 has_null = byteArrayHasNUL# frozen# read#
428 return (PS frozen# read# has_null)
432 %************************************************************************
434 \subsection{List-mimicking functions for @PackedStrings@}
436 %************************************************************************
438 First, the basic functions that do look into the representation;
439 @indexPS@ is the most important one.
442 lengthPS :: PackedString -> Int
443 lengthPS ps = I# (lengthPS# ps)
445 {-# INLINE lengthPS# #-}
447 lengthPS# (PS _ i _) = i
448 lengthPS# (CPS _ i) = i
450 {-# INLINE strlen# #-}
452 strlen# :: Addr# -> Int
455 _ccall_ strlen (A# a) >>= \ len@(I# _) ->
459 byteArrayHasNUL# :: ByteArray# -> Int#{-length-} -> Bool
460 byteArrayHasNUL# bs len
462 _ccall_ byteArrayHasNUL__ ba (I# len) >>= \ (I# res) ->
464 if res ==# 0# then False else True
467 ba = ByteArray (0, I# (len -# 1#)) bs
469 -----------------------
471 indexPS :: PackedString -> Int -> Char
472 indexPS ps (I# n) = C# (indexPS# ps n)
474 {-# INLINE indexPS# #-}
476 indexPS# (PS bs i _) n
477 = --ASSERT (n >=# 0# && n <# i) -- error checking: my eye! (WDP 94/10)
481 = indexCharOffAddr# a n
484 Now, the rest of the functions can be defined without digging
485 around in the representation.
488 headPS :: PackedString -> Char
490 | nullPS ps = error "headPS: head []"
491 | otherwise = C# (indexPS# ps 0#)
493 tailPS :: PackedString -> PackedString
495 | len <=# 0# = error "tailPS: tail []"
497 | otherwise = substrPS# ps 1# (len -# 1#)
501 nullPS :: PackedString -> Bool
502 nullPS (PS _ i _) = i ==# 0#
503 nullPS (CPS _ i) = i ==# 0#
505 appendPS :: PackedString -> PackedString -> PackedString
509 | otherwise = concatPS [xs,ys]
511 mapPS :: (Char -> Char) -> PackedString -> PackedString {-or String?-}
517 new_ps_array (length +# 1#) >>= \ ps_arr ->
518 whizz ps_arr length 0# >>
519 freeze_ps_array ps_arr length >>= \ (ByteArray _ frozen#) ->
520 let has_null = byteArrayHasNUL# frozen# length in
521 return (PS frozen# length has_null))
523 length = lengthPS# xs
525 whizz :: MutableByteArray s Int -> Int# -> Int# -> ST s ()
528 = write_ps_array arr# i (chr# 0#) >>
534 write_ps_array arr# i (case f (C# ch) of { (C# x) -> x}) >>
535 whizz arr# (n -# 1#) (i +# 1#)
537 filterPS :: (Char -> Bool) -> PackedString -> PackedString {-or String?-}
543 Filtering proceeds as follows:
545 * traverse the list, applying the pred. to each element,
546 remembering the positions where it was satisfied.
548 Encode these positions using a run-length encoding of the gaps
549 between the matching positions.
551 * Allocate a MutableByteArray in the heap big enough to hold
552 all the matched entries, and copy the elements that matched over.
554 A better solution that merges the scan© passes into one,
555 would be to copy the filtered elements over into a growable
556 buffer. No such operation currently supported over
557 MutableByteArrays (could of course use malloc&realloc)
558 But, this solution may in the case of repeated realloc's
559 be worse than the current solution.
563 (rle,len_filtered) = filter_ps (len# -# 1#) 0# 0# []
564 len_filtered# = case len_filtered of { I# x# -> x#}
566 if len# ==# len_filtered# then
567 {- not much filtering as everything passed through. -}
569 else if len_filtered# ==# 0# then
572 new_ps_array (len_filtered# +# 1#) >>= \ ps_arr ->
573 copy_arr ps_arr rle 0# 0# >>
574 freeze_ps_array ps_arr len_filtered# >>= \ (ByteArray _ frozen#) ->
575 let has_null = byteArrayHasNUL# frozen# len_filtered# in
576 return (PS frozen# len_filtered# has_null))
580 matchOffset :: Int# -> [Char] -> (Int,[Char])
581 matchOffset off [] = (I# off,[])
582 matchOffset off (C# c:cs) =
587 if x==# 0# then -- escape code, add 255#
592 copy_arr :: MutableByteArray s Int -> [Char] -> Int# -> Int# -> ST s ()
593 copy_arr arr# [_] _ _ = return ()
594 copy_arr arr# ls n i =
596 (x,ls') = matchOffset 0# ls
597 n' = n +# (case x of { (I# x#) -> x#}) -# 1#
600 write_ps_array arr# i ch >>
601 copy_arr arr# ls' (n' +# 1#) (i +# 1#)
603 esc :: Int# -> Int# -> [Char] -> [Char]
604 esc v 0# ls = (C# (chr# v)):ls
605 esc v n ls = esc v (n -# 1#) (C# (chr# 0#):ls)
607 filter_ps :: Int# -> Int# -> Int# -> [Char] -> ([Char],Int)
608 filter_ps n hits run acc
611 escs = run `quotInt#` 255#
612 v = run `remInt#` 255#
614 (esc (v +# 1#) escs acc, I# hits)
622 escs = run `quotInt#` 255#
623 v = run `remInt#` 255#
624 acc' = esc (v +# 1#) escs acc
626 filter_ps n' (hits +# 1#) 0# acc'
628 filter_ps n' hits (run +# 1#) acc
631 foldlPS :: (a -> Char -> a) -> a -> PackedString -> a
640 --whizzLR :: a -> Int# -> a
643 | otherwise = whizzLR (f b (C# (indexPS# ps idx))) (idx +# 1#)
646 foldrPS :: (Char -> a -> a) -> a -> PackedString -> a
655 --whizzRL :: a -> Int# -> a
658 | otherwise = whizzRL (f (C# (indexPS# ps idx)) b) (idx -# 1#)
660 takePS :: Int -> PackedString -> PackedString
663 | otherwise = substrPS# ps 0# (n -# 1#)
665 dropPS :: Int -> PackedString -> PackedString
668 | otherwise = substrPS# ps n (lengthPS# ps -# 1#)
672 splitAtPS :: Int -> PackedString -> (PackedString, PackedString)
673 splitAtPS n ps = (takePS n ps, dropPS n ps)
675 takeWhilePS :: (Char -> Bool) -> PackedString -> PackedString
678 break_pt = char_pos_that_dissatisfies
684 if break_pt ==# 0# then
687 substrPS# ps 0# (break_pt -# 1#)
689 dropWhilePS :: (Char -> Bool) -> PackedString -> PackedString
693 break_pt = char_pos_that_dissatisfies
699 if len ==# break_pt then
702 substrPS# ps break_pt (len -# 1#)
704 elemPS :: Char -> PackedString -> Bool
708 break_pt = first_char_pos_that_satisfies
716 char_pos_that_dissatisfies :: (Char# -> Bool) -> PackedString -> Int# -> Int# -> Int#
718 char_pos_that_dissatisfies p ps len pos
719 | pos >=# len = pos -- end
720 | p (indexPS# ps pos) = -- predicate satisfied; keep going
721 char_pos_that_dissatisfies p ps len (pos +# 1#)
722 | otherwise = pos -- predicate not satisfied
724 first_char_pos_that_satisfies :: (Char# -> Bool) -> PackedString -> Int# -> Int# -> Int#
725 first_char_pos_that_satisfies p ps len pos
726 | pos >=# len = pos -- end
727 | p (indexPS# ps pos) = pos -- got it!
728 | otherwise = first_char_pos_that_satisfies p ps len (pos +# 1#)
730 -- ToDo: could certainly go quicker
731 spanPS :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
732 spanPS p ps = (takeWhilePS p ps, dropWhilePS p ps)
734 breakPS :: (Char -> Bool) -> PackedString -> (PackedString, PackedString)
735 breakPS p ps = spanPS (not . p) ps
737 linesPS :: PackedString -> [PackedString]
738 linesPS ps = splitPS '\n' ps
740 wordsPS :: PackedString -> [PackedString]
741 wordsPS ps = splitWithPS isSpace ps
743 reversePS :: PackedString -> PackedString
745 if nullPS ps then -- don't create stuff unnecessarily.
749 new_ps_array (length +# 1#) >>= \ arr# -> -- incl NUL byte!
750 fill_in arr# (length -# 1#) 0# >>
751 freeze_ps_array arr# length >>= \ (ByteArray _ frozen#) ->
752 let has_null = byteArrayHasNUL# frozen# length in
753 return (PS frozen# length has_null))
755 length = lengthPS# ps
757 fill_in :: MutableByteArray s Int -> Int# -> Int# -> ST s ()
758 fill_in arr_in# n i =
762 write_ps_array arr_in# i ch >>
764 write_ps_array arr_in# (i +# 1#) (chr# 0#) >>
767 fill_in arr_in# (n -# 1#) (i +# 1#)
769 concatPS :: [PackedString] -> PackedString
773 tot_len# = case (foldr ((+) . lengthPS) 0 pss) of { I# x -> x }
774 tot_len = I# tot_len#
777 new_ps_array (tot_len# +# 1#) >>= \ arr# -> -- incl NUL byte!
778 packum arr# pss 0# >>
779 freeze_ps_array arr# tot_len# >>= \ (ByteArray _ frozen#) ->
781 let has_null = byteArrayHasNUL# frozen# tot_len# in
783 return (PS frozen# tot_len# has_null)
786 packum :: MutableByteArray s Int -> [PackedString] -> Int# -> ST s ()
789 = write_ps_array arr pos (chr# 0#) >>
791 packum arr (ps : pss) pos
792 = fill arr pos ps 0# (lengthPS# ps) >>= \ (I# next_pos) ->
793 packum arr pss next_pos
795 fill :: MutableByteArray s Int -> Int# -> PackedString -> Int# -> Int# -> ST s Int
797 fill arr arr_i ps ps_i ps_len
799 = return (I# (arr_i +# ps_len))
801 = write_ps_array arr (arr_i +# ps_i) (indexPS# ps ps_i) >>
802 fill arr arr_i ps (ps_i +# 1#) ps_len
804 ------------------------------------------------------------
805 joinPS :: PackedString -> [PackedString] -> PackedString
806 joinPS filler pss = concatPS (splice pss)
810 splice (x:y:xs) = x:filler:splice (y:xs)
812 -- ToDo: the obvious generalisation
814 Some properties that hold:
817 where False = any (map (x `elemPS`) ls')
818 False = any (map (nullPS) ls')
820 * all x's have been chopped out.
821 * no empty PackedStrings in returned list. A conseq.
826 * joinPS (packString [x]) (_splitPS x ls) = ls
830 splitPS :: Char -> PackedString -> [PackedString]
831 splitPS (C# ch) = splitWithPS (\ (C# c) -> c `eqChar#` ch)
833 splitWithPS :: (Char -> Bool) -> PackedString -> [PackedString]
834 splitWithPS pred ps =
844 first_char_pos_that_satisfies
850 if break_pt ==# n then -- immediate match, no substring to cut out.
851 splitify (break_pt +# 1#)
853 substrPS# ps n (break_pt -# 1#): -- leave out the matching character
854 splitify (break_pt +# 1#)
857 %************************************************************************
859 \subsection{Local utility functions}
861 %************************************************************************
863 The definition of @_substrPS@ is essentially:
864 @take (end - begin + 1) (drop begin str)@.
867 substrPS :: PackedString -> Int -> Int -> PackedString
868 substrPS ps (I# begin) (I# end) = substrPS# ps begin end
872 = error "substrPS: bounds out of range"
874 | s >=# len || result_len# <=# 0#
879 new_ps_array (result_len# +# 1#) >>= \ ch_arr -> -- incl NUL byte!
881 freeze_ps_array ch_arr result_len# >>= \ (ByteArray _ frozen#) ->
883 let has_null = byteArrayHasNUL# frozen# result_len# in
885 return (PS frozen# result_len# has_null)
890 result_len# = (if e <# len then (e +# 1#) else len) -# s
891 result_len = I# result_len#
893 -----------------------
894 fill_in :: MutableByteArray s Int -> Int# -> ST s ()
897 | idx ==# result_len#
898 = write_ps_array arr_in# idx (chr# 0#) >>
902 ch = indexPS# ps (s +# idx)
904 write_ps_array arr_in# idx ch >>
905 fill_in arr_in# (idx +# 1#)
908 %*********************************************************
910 \subsection{Packing and unpacking C strings}
912 %*********************************************************
915 cStringToPS :: Addr -> PackedString
916 cStringToPS (A# a#) = -- the easy one; we just believe the caller
919 len = case (strlen# a#) of { I# x -> x }
921 packCBytes :: Int -> Addr -> PackedString
922 packCBytes len addr = runST (packCBytesST len addr)
924 packCBytesST :: Int -> Addr -> ST s PackedString
925 packCBytesST len@(I# length#) (A# addr) =
927 allocate an array that will hold the string
928 (not forgetting the NUL byte at the end)
930 new_ps_array (length# +# 1#) >>= \ ch_array ->
931 -- fill in packed string from "addr"
932 fill_in ch_array 0# >>
934 freeze_ps_array ch_array length# >>= \ (ByteArray _ frozen#) ->
935 let has_null = byteArrayHasNUL# frozen# length# in
936 return (PS frozen# length# has_null)
938 fill_in :: MutableByteArray s Int -> Int# -> ST s ()
942 = write_ps_array arr_in# idx (chr# 0#) >>
945 = case (indexCharOffAddr# addr idx) of { ch ->
946 write_ps_array arr_in# idx ch >>
947 fill_in arr_in# (idx +# 1#) }