<commit_before>{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ViewPatterns #-}
-- | Support serializing Dalvik code in SSA form.
--
-- This is binary serialization and it preserves sharing.  Explicit
-- functions are provided (as opposed to class instances) for two
-- reasons:
--
-- 1) Decoupling from a specific serialization library
--
-- 2) Some trickiness was required to deal with knot tying during
-- deserialization.  The trick did not fit in the instance declaration
-- cleanly.
module Dalvik.SSA.Internal.Serialize (
  deserializeDex,
  serializeDex,
  -- Helpers
  dexFields,
  dexMethodRefs
  ) where

import Control.Applicative
import Control.Arrow ( second )
import qualified Data.ByteString as BS
import qualified Data.Foldable as F
import qualified Data.HashMap.Strict as HM
import qualified Data.List.NonEmpty as NEL
import Data.Map ( Map )
import qualified Data.Map as M
import Data.Maybe ( fromMaybe, maybeToList )
import qualified Data.Serialize as S
import qualified Data.Set as Set
import qualified Data.Vector as V

import Dalvik.SSA.Internal.Pretty ()
import Dalvik.SSA.Types

-- | Deserialize a previously serialized Dex file from a strict 'ByteString'
--
-- This operation preserves the sharing of the original dex file.
deserializeDex :: BS.ByteString -> Either String DexFile
deserializeDex bs =
  let getKnot = snd . either error id
      res = S.runGet (getDex (getKnot res)) bs
  in case res of
    Left err -> Left err
    Right (df, _) -> Right df

serializeDex :: DexFile -> BS.ByteString
serializeDex = S.runPut . putDex

-- | A type holding state for the knot tying procedure.
data Knot = Knot { knotTypeTable :: !(Map Int Type)
                 , knotClasses :: !(Map Int Class)
                 , knotMethods :: !(Map Int Method)
                 , knotValues :: !(Map Int Value)
                 , knotBlocks :: !(Map Int BasicBlock)
                 , knotMethodRefs :: !(Map Int MethodRef)
                 , knotFields :: !(Map Int Field)
                 }

emptyKnot :: Knot
emptyKnot = Knot { knotTypeTable = M.empty
                 , knotClasses = M.empty
                 , knotMethods = M.empty
                 , knotValues = M.empty
                 , knotBlocks = M.empty
                 , knotMethodRefs = M.empty
                 , knotFields = M.empty
                 }

-- | Format:
--
-- Int (id source)
-- Type list
-- Constant list
-- Field list
-- Method ref list
-- Class list
putDex :: DexFile -> S.Put
putDex df = do
  S.put (dexIdSrc df)
  tt <- putTypeTable (dexTypes df)
  putList (putConstant tt) (dexConstants df)
  putList (putField tt) (dexFields df)
  putList (putMethodRef tt) (dexMethodRefs df)
  putList (putClass tt) (dexClasses df)

getDex :: Knot -> S.Get (DexFile, Knot)
getDex fknot = do
  idSrc <- S.get<commit_msg>Add a file header to the binary format<commit_after>{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ViewPatterns #-}
-- | Support serializing Dalvik code in SSA form.
--
-- This is binary serialization and it preserves sharing.  Explicit
-- functions are provided (as opposed to class instances) for two
-- reasons:
--
-- 1) Decoupling from a specific serialization library
--
-- 2) Some trickiness was required to deal with knot tying during
-- deserialization.  The trick did not fit in the instance declaration
-- cleanly.
module Dalvik.SSA.Internal.Serialize (
  deserializeDex,
  serializeDex,
  -- Helpers
  dexFields,
  dexMethodRefs
  ) where

import Control.Applicative
import Control.Arrow ( second )
import Control.Monad ( unless )
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy.Builder as LBS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Foldable as F
import qualified Data.HashMap.Strict as HM
import qualified Data.List.NonEmpty as NEL
import Data.Map ( Map )
import qualified Data.Map as M
import Data.Maybe ( fromMaybe, maybeToList )
import Data.Monoid
import qualified Data.Serialize as S
import qualified Data.Set as Set
import qualified Data.Vector as V

import Dalvik.SSA.Internal.Pretty ()
import Dalvik.SSA.Types

formatVersion :: Int
formatVersion = 1

-- | This file header is a clone of the PNG header, but with a
-- different ASCII tag.
fileHeader :: BS.ByteString
fileHeader = LBS.toStrict $ LBS.toLazyByteString bldr
  where
    bldr = mconcat [ LBS.word8 0x89
                   , LBS.string8 "DLVK"
                   , LBS.word8 0x0d
                   , LBS.word8 0x0a
                   , LBS.word8 0x1a
                   , LBS.word8 0x0a
                   ]

-- | Deserialize a previously serialized Dex file from a strict 'ByteString'
--
-- This operation preserves the sharing of the original dex file.
deserializeDex :: BS.ByteString -> Either String DexFile
deserializeDex bs =
  let getKnot = snd . either error id
      res = S.runGet (getDex (getKnot res)) bs
  in case res of
    Left err -> Left err
    Right (df, _) -> Right df

serializeDex :: DexFile -> BS.ByteString
serializeDex = S.runPut . putDex

-- | A type holding state for the knot tying procedure.
data Knot = Knot { knotTypeTable :: !(Map Int Type)
                 , knotClasses :: !(Map Int Class)
                 , knotMethods :: !(Map Int Method)
                 , knotValues :: !(Map Int Value)
                 , knotBlocks :: !(Map Int BasicBlock)
                 , knotMethodRefs :: !(Map Int MethodRef)
                 , knotFields :: !(Map Int Field)
                 }

emptyKnot :: Knot
emptyKnot = Knot { knotTypeTable = M.empty
                 , knotClasses = M.empty
                 , knotMethods = M.empty
                 , knotValues = M.empty
                 , knotBlocks = M.empty
                 , knotMethodRefs = M.empty
                 , knotFields = M.empty
                 }

-- | Format:
--
-- Int (id source)
-- Type list
-- Constant list
-- Field list
-- Method ref list
-- Class list
putDex :: DexFile -> S.Put
putDex df = do
  S.putByteString fileHeader
  S.put formatVersion
  S.put (dexIdSrc df)
  tt <- putTypeTable (dexTypes df)
  putList (putConstant tt) (dexConstants df)
  putList (putField tt) (dexFields df)
  putList (putMethodRef tt) (dexMethodRefs df)
  putList (putClass tt) (dexClasses df)

getDex :: Knot -> S.Get (DexFile, Knot)
getDex fknot = do
  sig <- S.getByteString $ fromIntegral $ BS.length fileHeader
  unless (sig == fileHeader) $ fail ("Deserialize: Signature mismatch, got " ++ show sig)
  fv <- S.get
  unless (fv == formatVersion) $ fail ("Deserialize: File format mismatch, got " ++ show fv ++ " but was expecting " ++ show formatVersion)
  idSrc <- S.get
{'fraction_non_alphanumeric': 0.09137904819826669, 'fraction_numerical': 0.003344990117074654, 'mean_word_length': 2.5120128136679125, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '232224', 'n_tokens_mistral': 2009, 'n_tokens_neox': 1939, 'n_words': 695}