-- | Incremental pure disassembly. module Hdis86.Incremental ( disassembleOne , disassembleLazy ) where import Hdis86.Types import qualified Hdis86.Pure as P import qualified Hdis86.IO as I import Data.Maybe import System.IO.Unsafe ( unsafePerformIO ) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BL disOne :: Config -> BS.ByteString -> Maybe Metadata disOne cfg bs = unsafePerformIO $ do ud <- I.newUD I.setInputBuffer ud bs I.setConfig ud cfg n <- I.advance ud case n of Just _ -> Just `fmap` I.getMetadata ud Nothing -> return Nothing -- | Split a @'BS.ByteString'@ into an instruction and the remaining -- @'BS.ByteString'@. -- -- Returns @'Nothing'@ if the input is empty or contains an -- incomplete instruction. disassembleOne :: Config -> BS.ByteString -> Maybe (Metadata, BS.ByteString) disassembleOne cfg bs = disOne cfg bs >>= (fmap getRem . getMD) where -- invalid instruction: distinguish from incomplete instruction -- by appending 0x90 = nop getMD m@Metadata { mdInst = i0@Inst { inOpcode = Iinvalid }, mdBytes = ibs } = case P.disassemble cfg (ibs `BS.snoc` 0x90) of [i1, Inst [] Inop []] | i0 == i1 -> Just m _ -> Nothing getMD m = Just m getRem m = (m, BS.drop (fromIntegral $ mdLength m) bs) -- | Disassemble a lazy @'BL.ByteString'@. -- -- The output is produced lazily. disassembleLazy :: Config -> BL.ByteString -> [Metadata] disassembleLazy cfg = go (cfgOrigin cfg) BS.empty . BL.toChunks where go n bs chunks = let newcfg = cfg { cfgOrigin = n } in case disassembleOne newcfg bs of Just (md, bsrem) -> md : go nn bsrem chunks where nn = n + fromIntegral (mdLength md) Nothing -> case chunks of (x:xs) -> go n (bs `BS.append` x) xs -- end of input; produce a final incomplete instruction if any [] -> maybeToList $ disOne newcfg bs