3 -- (c) The University of Glasgow 2002
5 -- Binary I/O library, with special tweaks for GHC
7 -- Based on the nhc98 Binary library, which is copyright
8 -- (c) Malcolm Wallace and Colin Runciman, University of York, 1998.
9 -- Under the terms of the license for that software, we must tell you
10 -- where you can obtain the original version of the Binary library, namely
11 -- http://www.cs.york.ac.uk/fp/nhc98/
18 openBinIO, openBinIO_,
31 -- for writing instances:
44 getBinFileWithDict, -- :: Binary a => FilePath -> IO a
45 putBinFileWithDict, -- :: Binary a => FilePath -> ModuleName -> a -> IO ()
49 #include "HsVersions.h"
51 -- The *host* architecture version:
59 import PackageConfig ( PackageId, packageIdFS, fsToPackageId )
61 #if __GLASGOW_HASKELL__ < 503
69 import GlaExts hiding (ByteArray, newByteArray, freezeByteArray)
72 import PrelIOBase ( IOError(..), IOErrorType(..)
73 #if __GLASGOW_HASKELL__ > 411
77 import PrelReal ( Ratio(..) )
78 import PrelIOBase ( IO(..) )
79 import IOExts ( openFileEx, IOModeEx(..) )
87 import Data.Char ( ord, chr )
88 import Data.Array.Base ( unsafeRead, unsafeWrite )
89 import Control.Monad ( when )
90 import Control.Exception ( throwDyn )
91 import System.IO as IO
92 import System.IO.Unsafe ( unsafeInterleaveIO )
93 import System.IO.Error ( mkIOError, eofErrorType )
94 import GHC.Real ( Ratio(..) )
96 import GHC.IOBase ( IO(..) )
97 import GHC.Word ( Word8(..) )
98 #if __GLASGOW_HASKELL__ < 601
99 -- openFileEx is available from the lang package, but we want to
100 -- be independent of hslibs libraries.
101 import GHC.Handle ( openFileEx, IOModeEx(..) )
103 import System.IO ( openBinaryFile )
107 #if __GLASGOW_HASKELL__ < 601
108 openBinaryFile f mode = openFileEx f (BinaryMode mode)
111 #if __GLASGOW_HASKELL__ < 503
112 type BinArray = MutableByteArray RealWorld Int
113 newArray_ bounds = stToIO (newCharArray bounds)
114 unsafeWrite arr ix e = stToIO (writeWord8Array arr ix e)
115 unsafeRead arr ix = stToIO (readWord8Array arr ix)
116 #if __GLASGOW_HASKELL__ < 411
117 newByteArray# = newCharArray#
119 hPutArray h arr sz = hPutBufBAFull h arr sz
120 hGetArray h sz = hGetBufBAFull h sz
122 mkIOError :: IOErrorType -> String -> Maybe Handle -> Maybe FilePath -> Exception
123 mkIOError t location maybe_hdl maybe_filename
124 = IOException (IOError maybe_hdl t location ""
125 #if __GLASGOW_HASKELL__ > 411
133 #define SIZEOF_HSINT INT_SIZE_IN_BYTES
136 #ifndef SIZEOF_HSWORD
137 #define SIZEOF_HSWORD WORD_SIZE_IN_BYTES
141 type BinArray = IOUArray Int Word8
144 ---------------------------------------------------------------
146 ---------------------------------------------------------------
149 = BinMem { -- binary data stored in an unboxed array
150 bh_usr :: UserData, -- sigh, need parameterized modules :-)
151 off_r :: !FastMutInt, -- the current offset
152 sz_r :: !FastMutInt, -- size of the array (cached)
153 arr_r :: !(IORef BinArray) -- the array (bounds: (0,size-1))
155 -- XXX: should really store a "high water mark" for dumping out
156 -- the binary data to a file.
158 | BinIO { -- binary data stored in a file
160 off_r :: !FastMutInt, -- the current offset (cached)
161 hdl :: !IO.Handle -- the file handle (must be seekable)
163 -- cache the file ptr in BinIO; using hTell is too expensive
164 -- to call repeatedly. If anyone else is modifying this Handle
165 -- at the same time, we'll be screwed.
167 getUserData :: BinHandle -> UserData
168 getUserData bh = bh_usr bh
170 setUserData :: BinHandle -> UserData -> BinHandle
171 setUserData bh us = bh { bh_usr = us }
174 ---------------------------------------------------------------
176 ---------------------------------------------------------------
178 newtype Bin a = BinPtr Int
179 deriving (Eq, Ord, Show, Bounded)
181 castBin :: Bin a -> Bin b
182 castBin (BinPtr i) = BinPtr i
184 ---------------------------------------------------------------
186 ---------------------------------------------------------------
189 put_ :: BinHandle -> a -> IO ()
190 put :: BinHandle -> a -> IO (Bin a)
191 get :: BinHandle -> IO a
193 -- define one of put_, put. Use of put_ is recommended because it
194 -- is more likely that tail-calls can kick in, and we rarely need the
195 -- position return value.
196 put_ bh a = do put bh a; return ()
197 put bh a = do p <- tellBin bh; put_ bh a; return p
199 putAt :: Binary a => BinHandle -> Bin a -> a -> IO ()
200 putAt bh p x = do seekBin bh p; put bh x; return ()
202 getAt :: Binary a => BinHandle -> Bin a -> IO a
203 getAt bh p = do seekBin bh p; get bh
205 openBinIO_ :: IO.Handle -> IO BinHandle
206 openBinIO_ h = openBinIO h
208 openBinIO :: IO.Handle -> IO BinHandle
212 return (BinIO noUserData r h)
214 openBinMem :: Int -> IO BinHandle
216 | size <= 0 = error "Data.Binary.openBinMem: size must be >= 0"
218 arr <- newArray_ (0,size-1)
219 arr_r <- newIORef arr
220 ix_r <- newFastMutInt
221 writeFastMutInt ix_r 0
222 sz_r <- newFastMutInt
223 writeFastMutInt sz_r size
224 return (BinMem noUserData ix_r sz_r arr_r)
226 tellBin :: BinHandle -> IO (Bin a)
227 tellBin (BinIO _ r _) = do ix <- readFastMutInt r; return (BinPtr ix)
228 tellBin (BinMem _ r _ _) = do ix <- readFastMutInt r; return (BinPtr ix)
230 seekBin :: BinHandle -> Bin a -> IO ()
231 seekBin (BinIO _ ix_r h) (BinPtr p) = do
232 writeFastMutInt ix_r p
233 hSeek h AbsoluteSeek (fromIntegral p)
234 seekBin h@(BinMem _ ix_r sz_r a) (BinPtr p) = do
235 sz <- readFastMutInt sz_r
237 then do expandBin h p; writeFastMutInt ix_r p
238 else writeFastMutInt ix_r p
240 isEOFBin :: BinHandle -> IO Bool
241 isEOFBin (BinMem _ ix_r sz_r a) = do
242 ix <- readFastMutInt ix_r
243 sz <- readFastMutInt sz_r
245 isEOFBin (BinIO _ ix_r h) = hIsEOF h
247 writeBinMem :: BinHandle -> FilePath -> IO ()
248 writeBinMem (BinIO _ _ _) _ = error "Data.Binary.writeBinMem: not a memory handle"
249 writeBinMem (BinMem _ ix_r sz_r arr_r) fn = do
250 h <- openBinaryFile fn WriteMode
251 arr <- readIORef arr_r
252 ix <- readFastMutInt ix_r
254 #if __GLASGOW_HASKELL__ <= 500
255 -- workaround a bug in old implementation of hPutBuf (it doesn't
256 -- set the FILEOBJ_RW_WRITTEN flag on the file object, so the file doens't
257 -- get flushed properly). Adding an extra '\0' doens't do any harm.
262 readBinMem :: FilePath -> IO BinHandle
263 -- Return a BinHandle with a totally undefined State
264 readBinMem filename = do
265 h <- openBinaryFile filename ReadMode
266 filesize' <- hFileSize h
267 let filesize = fromIntegral filesize'
268 arr <- newArray_ (0,filesize-1)
269 count <- hGetArray h arr filesize
270 when (count /= filesize)
271 (error ("Binary.readBinMem: only read " ++ show count ++ " bytes"))
273 arr_r <- newIORef arr
274 ix_r <- newFastMutInt
275 writeFastMutInt ix_r 0
276 sz_r <- newFastMutInt
277 writeFastMutInt sz_r filesize
278 return (BinMem noUserData ix_r sz_r arr_r)
280 -- expand the size of the array to include a specified offset
281 expandBin :: BinHandle -> Int -> IO ()
282 expandBin (BinMem _ ix_r sz_r arr_r) off = do
283 sz <- readFastMutInt sz_r
284 let sz' = head (dropWhile (<= off) (iterate (* 2) sz))
285 arr <- readIORef arr_r
286 arr' <- newArray_ (0,sz'-1)
287 sequence_ [ unsafeRead arr i >>= unsafeWrite arr' i
288 | i <- [ 0 .. sz-1 ] ]
289 writeFastMutInt sz_r sz'
290 writeIORef arr_r arr'
292 hPutStrLn stderr ("Binary: expanding to size: " ++ show sz')
295 expandBin (BinIO _ _ _) _ = return ()
296 -- no need to expand a file, we'll assume they expand by themselves.
298 -- -----------------------------------------------------------------------------
299 -- Low-level reading/writing of bytes
301 putWord8 :: BinHandle -> Word8 -> IO ()
302 putWord8 h@(BinMem _ ix_r sz_r arr_r) w = do
303 ix <- readFastMutInt ix_r
304 sz <- readFastMutInt sz_r
305 -- double the size of the array if it overflows
307 then do expandBin h ix
309 else do arr <- readIORef arr_r
311 writeFastMutInt ix_r (ix+1)
313 putWord8 (BinIO _ ix_r h) w = do
314 ix <- readFastMutInt ix_r
315 hPutChar h (chr (fromIntegral w)) -- XXX not really correct
316 writeFastMutInt ix_r (ix+1)
319 getWord8 :: BinHandle -> IO Word8
320 getWord8 (BinMem _ ix_r sz_r arr_r) = do
321 ix <- readFastMutInt ix_r
322 sz <- readFastMutInt sz_r
324 #if __GLASGOW_HASKELL__ <= 408
325 throw (mkIOError eofErrorType "Data.Binary.getWord8" Nothing Nothing)
327 ioError (mkIOError eofErrorType "Data.Binary.getWord8" Nothing Nothing)
329 arr <- readIORef arr_r
330 w <- unsafeRead arr ix
331 writeFastMutInt ix_r (ix+1)
333 getWord8 (BinIO _ ix_r h) = do
334 ix <- readFastMutInt ix_r
336 writeFastMutInt ix_r (ix+1)
337 return $! (fromIntegral (ord c)) -- XXX not really correct
339 putByte :: BinHandle -> Word8 -> IO ()
340 putByte bh w = put_ bh w
342 getByte :: BinHandle -> IO Word8
345 -- -----------------------------------------------------------------------------
346 -- Primitve Word writes
348 instance Binary Word8 where
352 instance Binary Word16 where
353 put_ h w = do -- XXX too slow.. inline putWord8?
354 putByte h (fromIntegral (w `shiftR` 8))
355 putByte h (fromIntegral (w .&. 0xff))
359 return $! ((fromIntegral w1 `shiftL` 8) .|. fromIntegral w2)
362 instance Binary Word32 where
364 putByte h (fromIntegral (w `shiftR` 24))
365 putByte h (fromIntegral ((w `shiftR` 16) .&. 0xff))
366 putByte h (fromIntegral ((w `shiftR` 8) .&. 0xff))
367 putByte h (fromIntegral (w .&. 0xff))
373 return $! ((fromIntegral w1 `shiftL` 24) .|.
374 (fromIntegral w2 `shiftL` 16) .|.
375 (fromIntegral w3 `shiftL` 8) .|.
379 instance Binary Word64 where
381 putByte h (fromIntegral (w `shiftR` 56))
382 putByte h (fromIntegral ((w `shiftR` 48) .&. 0xff))
383 putByte h (fromIntegral ((w `shiftR` 40) .&. 0xff))
384 putByte h (fromIntegral ((w `shiftR` 32) .&. 0xff))
385 putByte h (fromIntegral ((w `shiftR` 24) .&. 0xff))
386 putByte h (fromIntegral ((w `shiftR` 16) .&. 0xff))
387 putByte h (fromIntegral ((w `shiftR` 8) .&. 0xff))
388 putByte h (fromIntegral (w .&. 0xff))
398 return $! ((fromIntegral w1 `shiftL` 56) .|.
399 (fromIntegral w2 `shiftL` 48) .|.
400 (fromIntegral w3 `shiftL` 40) .|.
401 (fromIntegral w4 `shiftL` 32) .|.
402 (fromIntegral w5 `shiftL` 24) .|.
403 (fromIntegral w6 `shiftL` 16) .|.
404 (fromIntegral w7 `shiftL` 8) .|.
407 -- -----------------------------------------------------------------------------
408 -- Primitve Int writes
410 instance Binary Int8 where
411 put_ h w = put_ h (fromIntegral w :: Word8)
412 get h = do w <- get h; return $! (fromIntegral (w::Word8))
414 instance Binary Int16 where
415 put_ h w = put_ h (fromIntegral w :: Word16)
416 get h = do w <- get h; return $! (fromIntegral (w::Word16))
418 instance Binary Int32 where
419 put_ h w = put_ h (fromIntegral w :: Word32)
420 get h = do w <- get h; return $! (fromIntegral (w::Word32))
422 instance Binary Int64 where
423 put_ h w = put_ h (fromIntegral w :: Word64)
424 get h = do w <- get h; return $! (fromIntegral (w::Word64))
426 -- -----------------------------------------------------------------------------
427 -- Instances for standard types
429 instance Binary () where
430 put_ bh () = return ()
432 -- getF bh p = case getBitsF bh 0 p of (_,b) -> ((),b)
434 instance Binary Bool where
435 put_ bh b = putByte bh (fromIntegral (fromEnum b))
436 get bh = do x <- getWord8 bh; return $! (toEnum (fromIntegral x))
437 -- getF bh p = case getBitsF bh 1 p of (x,b) -> (toEnum x,b)
439 instance Binary Char where
440 put_ bh c = put_ bh (fromIntegral (ord c) :: Word32)
441 get bh = do x <- get bh; return $! (chr (fromIntegral (x :: Word32)))
442 -- getF bh p = case getBitsF bh 8 p of (x,b) -> (toEnum x,b)
444 instance Binary Int where
445 #if SIZEOF_HSINT == 4
446 put_ bh i = put_ bh (fromIntegral i :: Int32)
449 return $! (fromIntegral (x :: Int32))
450 #elif SIZEOF_HSINT == 8
451 put_ bh i = put_ bh (fromIntegral i :: Int64)
454 return $! (fromIntegral (x :: Int64))
456 #error "unsupported sizeof(HsInt)"
458 -- getF bh = getBitsF bh 32
460 instance Binary a => Binary [a] where
464 then putByte bh (fromIntegral len :: Word8)
465 else do putByte bh 0xff; put_ bh (fromIntegral len :: Word32)
471 else return (fromIntegral b :: Word32)
472 let loop 0 = return []
473 loop n = do a <- get bh; as <- loop (n-1); return (a:as)
476 instance (Binary a, Binary b) => Binary (a,b) where
477 put_ bh (a,b) = do put_ bh a; put_ bh b
478 get bh = do a <- get bh
482 instance (Binary a, Binary b, Binary c) => Binary (a,b,c) where
483 put_ bh (a,b,c) = do put_ bh a; put_ bh b; put_ bh c
484 get bh = do a <- get bh
489 instance (Binary a, Binary b, Binary c, Binary d) => Binary (a,b,c,d) where
490 put_ bh (a,b,c,d) = do put_ bh a; put_ bh b; put_ bh c; put_ bh d
491 get bh = do a <- get bh
497 instance Binary a => Binary (Maybe a) where
498 put_ bh Nothing = putByte bh 0
499 put_ bh (Just a) = do putByte bh 1; put_ bh a
500 get bh = do h <- getWord8 bh
503 _ -> do x <- get bh; return (Just x)
505 instance (Binary a, Binary b) => Binary (Either a b) where
506 put_ bh (Left a) = do putByte bh 0; put_ bh a
507 put_ bh (Right b) = do putByte bh 1; put_ bh b
508 get bh = do h <- getWord8 bh
510 0 -> do a <- get bh ; return (Left a)
511 _ -> do b <- get bh ; return (Right b)
513 #ifdef __GLASGOW_HASKELL__
514 instance Binary Integer where
515 put_ bh (S# i#) = do putByte bh 0; put_ bh (I# i#)
516 put_ bh (J# s# a#) = do
519 let sz# = sizeofByteArray# a# -- in *bytes*
520 put_ bh (I# sz#) -- in *bytes*
521 putByteArray bh a# sz#
526 0 -> do (I# i#) <- get bh
528 _ -> do (I# s#) <- get bh
530 (BA a#) <- getByteArray bh sz
533 putByteArray :: BinHandle -> ByteArray# -> Int# -> IO ()
534 putByteArray bh a s# = loop 0#
536 | n# ==# s# = return ()
538 putByte bh (indexByteArray a n#)
541 getByteArray :: BinHandle -> Int -> IO ByteArray
542 getByteArray bh (I# sz) = do
543 (MBA arr) <- newByteArray sz
545 | n ==# sz = return ()
548 writeByteArray arr n w
554 data ByteArray = BA ByteArray#
555 data MBA = MBA (MutableByteArray# RealWorld)
557 newByteArray :: Int# -> IO MBA
558 newByteArray sz = IO $ \s ->
559 case newByteArray# sz s of { (# s, arr #) ->
562 freezeByteArray :: MutableByteArray# RealWorld -> IO ByteArray
563 freezeByteArray arr = IO $ \s ->
564 case unsafeFreezeByteArray# arr s of { (# s, arr #) ->
567 writeByteArray :: MutableByteArray# RealWorld -> Int# -> Word8 -> IO ()
569 #if __GLASGOW_HASKELL__ < 503
570 writeByteArray arr i w8 = IO $ \s ->
571 case word8ToWord w8 of { W# w# ->
572 case writeCharArray# arr i (chr# (word2Int# w#)) s of { s ->
575 writeByteArray arr i (W8# w) = IO $ \s ->
576 case writeWord8Array# arr i w s of { s ->
580 #if __GLASGOW_HASKELL__ < 503
581 indexByteArray a# n# = fromIntegral (I# (ord# (indexCharArray# a# n#)))
583 indexByteArray a# n# = W8# (indexWord8Array# a# n#)
586 instance (Integral a, Binary a) => Binary (Ratio a) where
587 put_ bh (a :% b) = do put_ bh a; put_ bh b
588 get bh = do a <- get bh; b <- get bh; return (a :% b)
591 instance Binary (Bin a) where
592 put_ bh (BinPtr i) = put_ bh i
593 get bh = do i <- get bh; return (BinPtr i)
595 -- -----------------------------------------------------------------------------
596 -- Lazy reading/writing
598 lazyPut :: Binary a => BinHandle -> a -> IO ()
600 -- output the obj with a ptr to skip over it:
602 put_ bh pre_a -- save a slot for the ptr
603 put_ bh a -- dump the object
604 q <- tellBin bh -- q = ptr to after object
605 putAt bh pre_a q -- fill in slot before a with ptr to q
606 seekBin bh q -- finally carry on writing at q
608 lazyGet :: Binary a => BinHandle -> IO a
610 p <- get bh -- a BinPtr
612 a <- unsafeInterleaveIO (getAt bh p_a)
613 seekBin bh p -- skip over the object for now
616 -- --------------------------------------------------------------
617 -- Main wrappers: getBinFileWithDict, putBinFileWithDict
619 -- This layer is built on top of the stuff above,
620 -- and should not know anything about BinHandles
621 -- --------------------------------------------------------------
623 initBinMemSize = (1024*1024) :: Int
625 #if WORD_SIZE_IN_BITS == 32
626 binaryInterfaceMagic = 0x1face :: Word32
627 #elif WORD_SIZE_IN_BITS == 64
628 binaryInterfaceMagic = 0x1face64 :: Word32
631 getBinFileWithDict :: Binary a => FilePath -> IO a
632 getBinFileWithDict file_path = do
633 bh <- Binary.readBinMem file_path
635 -- Read the magic number to check that this really is a GHC .hi file
636 -- (This magic number does not change when we change
637 -- GHC interface file format)
639 when (magic /= binaryInterfaceMagic) $
640 throwDyn (ProgramError (
641 "magic number mismatch: old/corrupt interface file?"))
643 -- Read the dictionary
644 -- The next word in the file is a pointer to where the dictionary is
645 -- (probably at the end of the file)
646 dict_p <- Binary.get bh -- Get the dictionary ptr
647 data_p <- tellBin bh -- Remember where we are now
649 dict <- getDictionary bh
650 seekBin bh data_p -- Back to where we were before
652 -- Initialise the user-data field of bh
653 let bh' = setUserData bh (initReadState dict)
655 -- At last, get the thing
658 putBinFileWithDict :: Binary a => FilePath -> a -> IO ()
659 putBinFileWithDict file_path the_thing = do
660 bh <- openBinMem initBinMemSize
661 put_ bh binaryInterfaceMagic
663 -- Remember where the dictionary pointer will go
664 dict_p_p <- tellBin bh
665 put_ bh dict_p_p -- Placeholder for ptr to dictionary
667 -- Make some intial state
668 usr_state <- newWriteState
670 -- Put the main thing,
671 put_ (setUserData bh usr_state) the_thing
673 -- Get the final-state
674 j <- readIORef (ud_next usr_state)
675 fm <- readIORef (ud_map usr_state)
676 dict_p <- tellBin bh -- This is where the dictionary will start
678 -- Write the dictionary pointer at the fornt of the file
679 putAt bh dict_p_p dict_p -- Fill in the placeholder
680 seekBin bh dict_p -- Seek back to the end of the file
682 -- Write the dictionary itself
683 putDictionary bh j (constructDictionary j fm)
685 -- And send the result to the file
686 writeBinMem bh file_path
688 -- -----------------------------------------------------------------------------
690 -- -----------------------------------------------------------------------------
693 UserData { -- This field is used only when reading
694 ud_dict :: Dictionary,
696 -- The next two fields are only used when writing
697 ud_next :: IORef Int, -- The next index to use
698 ud_map :: IORef (UniqFM (Int,FastString))
701 noUserData = error "Binary.UserData: no user data"
703 initReadState :: Dictionary -> UserData
704 initReadState dict = UserData{ ud_dict = dict,
705 ud_next = undef "next",
706 ud_map = undef "map" }
708 newWriteState :: IO UserData
711 out_r <- newIORef emptyUFM
712 return (UserData { ud_dict = panic "dict",
717 undef s = panic ("Binary.UserData: no " ++ s)
719 ---------------------------------------------------------
721 ---------------------------------------------------------
723 type Dictionary = Array Int FastString -- The dictionary
724 -- Should be 0-indexed
726 putDictionary :: BinHandle -> Int -> Dictionary -> IO ()
727 putDictionary bh sz dict = do
729 mapM_ (putFS bh) (elems dict)
731 getDictionary :: BinHandle -> IO Dictionary
732 getDictionary bh = do
734 elems <- sequence (take sz (repeat (getFS bh)))
735 return (listArray (0,sz-1) elems)
737 constructDictionary :: Int -> UniqFM (Int,FastString) -> Dictionary
738 constructDictionary j fm = array (0,j-1) (eltsUFM fm)
740 ---------------------------------------------------------
741 -- Reading and writing FastStrings
742 ---------------------------------------------------------
744 putFS bh (FastString id l ba) = do
747 putFS bh s = error ("Binary.put_(FastString): " ++ unpackFS s)
748 -- Note: the length of the FastString is *not* the same as
749 -- the size of the ByteArray: the latter is rounded up to a
750 -- multiple of the word size.
752 {- -- possible faster version, not quite there yet:
753 getFS bh@BinMem{} = do
755 arr <- readIORef (arr_r bh)
756 off <- readFastMutInt (off_r bh)
757 return $! (mkFastSubStringBA# arr off l)
761 (BA ba) <- getByteArray bh (I# l)
762 return $! (mkFastSubStringBA# ba 0# l)
764 instance Binary PackageId where
765 put_ bh pid = put_ bh (packageIdFS pid)
766 get bh = do { fs <- get bh; return (fsToPackageId fs) }
768 instance Binary FastString where
769 put_ bh f@(FastString id l ba) =
770 case getUserData bh of {
771 UserData { ud_next = j_r, ud_map = out_r, ud_dict = dict} -> do
772 out <- readIORef out_r
773 let uniq = getUnique f
774 case lookupUFM out uniq of
775 Just (j,f) -> put_ bh j
780 writeIORef out_r (addToUFM out uniq (j,f))
782 put_ bh s = error ("Binary.put_(FastString): " ++ show (unpackFS s))
786 return $! (ud_dict (getUserData bh) ! j)