diff --git a/unison-cli/src/Unison/Main.hs b/unison-cli/src/Unison/Main.hs index ab03c6c38d..37467c83b7 100644 --- a/unison-cli/src/Unison/Main.hs +++ b/unison-cli/src/Unison/Main.hs @@ -25,10 +25,9 @@ import ArgParse ) import Compat (defaultInterruptHandler, withInterruptHandler) import Control.Concurrent (newEmptyMVar, runInUnboundThread, takeMVar) -import Control.Exception (displayException, evaluate, fromException) +import Control.Exception (displayException, fromException) import Data.Bitraversable (bitraverse) import Data.ByteString qualified as BS -import Data.ByteString.Lazy qualified as BL import Data.Either.Validation (Validation (..)) import Data.List.NonEmpty (NonEmpty) import Data.Text qualified as Text @@ -230,8 +229,8 @@ main version = do noOpCheckForChanges CommandLine.ShouldNotWatchFiles Run (RunCompiled file) args -> - BL.readFile file >>= \bs -> - try (evaluate $ RTI.decodeStandalone bs) >>= \case + BS.readFile file >>= \bs -> + try (RTI.decodeStandalone bs) >>= \case Left re -> do exnMessage <- RTI.prettyRuntimeExn fetchIssueFromGitHub re exitError . P.lines $ diff --git a/unison-runtime/cbits/i2d.c b/unison-runtime/cbits/i2d.c new file mode 100644 index 0000000000..41f7abb57d --- /dev/null +++ b/unison-runtime/cbits/i2d.c @@ -0,0 +1,19 @@ +#include + +double unisonWord64ToDouble(uint64_t lin) { + union { + double d; + uint64_t l; + } u; + u.l = lin; + return u.d; +} + +float unisonWord32ToFloat(uint32_t lin) { + union { + float f; + uint32_t l; + } u; + u.l = lin; + return u.f; +} diff --git a/unison-runtime/package.yaml b/unison-runtime/package.yaml index e71573dab0..0f05994a28 100644 --- a/unison-runtime/package.yaml +++ b/unison-runtime/package.yaml @@ -49,6 +49,9 @@ library: - condition: false other-modules: Paths_unison_runtime + c-sources: + - cbits/i2d.c + dependencies: - asn1-encoding - asn1-types @@ -58,7 +61,6 @@ library: - binary - bytes - bytestring - - cereal - clock - containers >= 0.6.3 - cryptonite @@ -119,9 +121,7 @@ tests: other-modules: Paths_unison_parser_typechecker dependencies: - base - - bytes - bytestring - - cereal - code-page - containers - cryptonite diff --git a/unison-runtime/src/Unison/Runtime/ANF/Serialize.hs b/unison-runtime/src/Unison/Runtime/ANF/Serialize.hs index 405198f54c..564666e4cb 100644 --- a/unison-runtime/src/Unison/Runtime/ANF/Serialize.hs +++ b/unison-runtime/src/Unison/Runtime/ANF/Serialize.hs @@ -6,25 +6,18 @@ module Unison.Runtime.ANF.Serialize where import Control.Monad -import Control.Monad.Reader +import Control.Monad.ST (ST) import Control.Monad.State.Strict (StateT (..)) -import Data.Bifunctor (bimap, first) -import Data.Binary.Get (runGetOrFail) -import Data.Binary.Get qualified as BGet import Data.ByteString (ByteString) import Data.ByteString.Builder (Builder) import Data.ByteString.Builder qualified as BU import Data.ByteString.Lazy qualified as L -import Data.Bytes.Get hiding (getBytes) -import Data.Bytes.Serial -import Data.Bytes.VarInt import Data.Foldable (toList) import Data.Functor ((<&>)) import Data.Map as Map (Map, fromDistinctAscList, fromList, lookup) -- machinery for special casing maps import Data.Map.Strict.Internal (Map (..)) import Data.Maybe (mapMaybe) -import Data.Serialize.Get qualified as SGet import Data.Word (Word32, Word64) import GHC.Stack import Unison.ABT.Normalized (Term (..)) @@ -40,6 +33,7 @@ import Unison.Runtime.Exception (die, exn) import Unison.Runtime.Foreign.Function.Type (ForeignFunc) import Unison.Runtime.Referenced import Unison.Runtime.Serialize +import Unison.Runtime.Serialize.Get import Unison.Util.Text qualified as Util.Text import Unison.Var (Type (ANFBlank), Var (..)) import Prelude hiding (getChar, putChar) @@ -72,21 +66,21 @@ putIndex :: Word64 -> Builder putIndex = putVarInt {-# INLINE putIndex #-} -getIndex :: (MonadGet m) => m Word64 -getIndex = unVarInt <$> deserialize +getIndex :: (PrimBase m) => Get m Word64 +getIndex = getVarInt putVar :: (Eq v) => [v] -> v -> Builder putVar ctx v | Just i <- index ctx v = putIndex i | otherwise = exn [] "putVar: variable not in context" -getVar :: (MonadGet m) => [v] -> m v +getVar :: (PrimBase m) => [v] -> Get m v getVar ctx = deindex ctx <$> getIndex putArgs :: (Eq v) => [v] -> [v] -> Builder putArgs ctx is = putFoldable (putVar ctx) is -getArgs :: (MonadGet m) => [v] -> m [v] +getArgs :: (PrimBase m) => [v] -> Get m [v] getArgs ctx = getList (getVar ctx) putCCs :: [Mem] -> Builder @@ -96,7 +90,7 @@ putCCs ccs = putLength n <> foldMap putCC ccs putCC UN = BU.word8 0 putCC BX = BU.word8 1 -getCCs :: (MonadGet m) => m [Mem] +getCCs :: (PrimBase m) => Get m [Mem] getCCs = getList $ getWord8 <&> \case @@ -134,23 +128,22 @@ putGroup refrep fops (Rec bs e) = ctx = pushCtx us [] getGroup :: - (MonadGet m) => - (SerialConfig m) => + (PrimBase m) => (Var v) => - m (SuperGroup Reference v) -getGroup = do + GDeserial m (SuperGroup Reference v) +getGroup s = do l <- getLength let n = fromIntegral l vs = getFresh <$> take l [0 ..] ctx = pushCtx vs [] - cs <- replicateM l (getComb ctx n) - Rec (zip vs cs) <$> getComb ctx n + cs <- replicateM l (getComb ctx n s) + Rec (zip vs cs) <$> getComb ctx n s putCode :: Bool -> Code Reference -> Builder putCode fops (CodeRep g c) = putGroup mempty fops g <> putCacheability c -getCode :: (MonadGet m, SerialConfig m) => m (Code Reference) -getCode = CodeRep <$> getGroup <*> getCacheability +getCode :: (PrimBase m) => GDeserial m (Code Reference) +getCode s = CodeRep <$> getGroup s <*> getCacheability s putInlineInfo :: (Var v) => @@ -161,12 +154,12 @@ putInlineInfo ctx (InlInfo clazz expr) = putInlineClass clazz <> putInlineExpr ctx expr getInlineInfo :: - (MonadGet m, SerialConfig m, Var v) => + (PrimBase m, Var v) => [v] -> Word64 -> - m (InlineInfo Reference v) -getInlineInfo ctx frsh = - InlInfo <$> getInlineClass <*> getInlineExpr ctx frsh + GDeserial m (InlineInfo Reference v) +getInlineInfo ctx frsh s = + InlInfo <$> getInlineClass <*> getInlineExpr ctx frsh s putInlineExpr :: (Var v) => @@ -178,15 +171,15 @@ putInlineExpr ctx (TAbss vs body) = <> putNormal mempty True (pushCtx vs ctx) body getInlineExpr :: - (MonadGet m, SerialConfig m, Var v) => + (PrimBase m, Var v) => [v] -> Word64 -> - m (ANormal Reference v) -getInlineExpr ctx frsh0 = do + GDeserial m (ANormal Reference v) +getInlineExpr ctx frsh0 s = do n <- getLength let frsh = frsh0 + fromIntegral n vs = getFresh <$> take n [frsh0 ..] - TAbss vs <$> getNormal (pushCtx vs ctx) frsh + TAbss vs <$> getNormal (pushCtx vs ctx) frsh s putOptInfos :: (Var v) => OptInfos Reference v -> Builder putOptInfos (arities, inls) = @@ -194,14 +187,15 @@ putOptInfos (arities, inls) = <> putMap putReference (putInlineInfo []) inls -- Note: current version -getOptInfos :: (MonadGet m, Var v) => m (OptInfos Reference v) +getOptInfos :: (PrimBase m, Var v) => Get m (OptInfos Reference v) getOptInfos = - flip runReaderT (Transfer codeVersion, True) $ - (,) - <$> getMap getReference gInt - <*> getMap getReference (getInlineInfo [] 0) + (,) + <$> getMap getReference gInt + <*> getMap + getReference + (getInlineInfo [] 0 (Transfer codeVersion, True)) where - gInt = unVarInt <$> deserialize + gInt = getVarInt putInlineClass :: InlineClass -> Builder putInlineClass = \case @@ -209,7 +203,7 @@ putInlineClass = \case TailInl -> BU.word8 1 Don'tInl -> BU.word8 2 -getInlineClass :: (MonadGet m) => m InlineClass +getInlineClass :: (PrimBase m) => Get m InlineClass getInlineClass = getWord8 >>= \case 0 -> pure AnywhereInl @@ -221,9 +215,9 @@ putCacheability :: Cacheability -> Builder putCacheability Uncacheable = BU.word8 0 putCacheability Cacheable = BU.word8 1 -getCacheability :: (MonadGet m, SerialConfig m) => m Cacheability -getCacheability = - askVersion >>= \case +getCacheability :: (PrimBase m) => GDeserial m Cacheability +getCacheability (ver, _) = + case ver of Transfer v | v >= 3 -> getWord8 >>= \case @@ -246,17 +240,16 @@ getFresh :: (Var v) => Word64 -> v getFresh n = freshenId n $ typed ANFBlank getComb :: - (MonadGet m) => - (SerialConfig m) => + (PrimBase m) => (Var v) => [v] -> Word64 -> - m (SuperNormal Reference v) -getComb ctx frsh0 = do + GDeserial m (SuperNormal Reference v) +getComb ctx frsh0 s = do ccs <- getCCs let us = zipWith (\_ -> getFresh) ccs [frsh0 ..] frsh = frsh0 + fromIntegral (length ccs) - Lambda ccs . TAbss us <$> getNormal (pushCtx us ctx) frsh + Lambda ccs . TAbss us <$> getNormal (pushCtx us ctx) frsh s putNormal :: (Var v) => @@ -310,42 +303,41 @@ putNormal refrep fops ctx tm = case tm of v -> exn [] $ "putNormal: malformed term\n" ++ show v getNormal :: - (MonadGet m) => - (SerialConfig m) => + (PrimBase m) => (Var v) => [v] -> Word64 -> - m (ANormal Reference v) -getNormal ctx frsh0 = + GDeserial m (ANormal Reference v) +getNormal ctx frsh0 s = getTag >>= \case VarT -> TVar <$> getVar ctx ForceT -> TFrc <$> getVar ctx - AppT -> TApp <$> getFunc ctx <*> getArgs ctx + AppT -> TApp <$> getFunc ctx s <*> getArgs ctx HandleT -> THnd <$> getRefs <*> getVar ctx <*> pure Nothing - <*> getNormal ctx frsh0 + <*> getNormal ctx frsh0 s ShiftT -> - flip TShift v <$> getReference <*> getNormal (v : ctx) (frsh0 + 1) + flip TShift v <$> getReference <*> getNormal (v : ctx) (frsh0 + 1) s where v = getFresh frsh0 - MatchT -> TMatch <$> getVar ctx <*> getBranches ctx frsh0 + MatchT -> TMatch <$> getVar ctx <*> getBranches ctx frsh0 s LitT -> TLit <$> getLit BxLitT -> TBLit <$> getLit NameRefT -> TName v . Left <$> getReference <*> getArgs ctx - <*> getNormal (v : ctx) (frsh0 + 1) + <*> getNormal (v : ctx) (frsh0 + 1) s where v = getFresh frsh0 NameVarT -> TName v . Right <$> getVar ctx <*> getArgs ctx - <*> getNormal (v : ctx) (frsh0 + 1) + <*> getNormal (v : ctx) (frsh0 + 1) s where v = getFresh frsh0 LetDirT -> do @@ -354,8 +346,8 @@ getNormal ctx frsh0 = frsh = frsh0 + fromIntegral l us = getFresh <$> take l [frsh0 ..] TLets Direct us ccs - <$> getNormal ctx frsh0 - <*> getNormal (pushCtx us ctx) frsh + <$> getNormal ctx frsh0 s + <*> getNormal (pushCtx us ctx) frsh s LetIndT -> do w <- getWord16be ccs <- getCCs @@ -363,8 +355,8 @@ getNormal ctx frsh0 = frsh = frsh0 + fromIntegral l us = getFresh <$> take l [frsh0 ..] TLets (Indirect w) us ccs - <$> getNormal ctx frsh0 - <*> getNormal (pushCtx us ctx) frsh + <$> getNormal ctx frsh0 s + <*> getNormal (pushCtx us ctx) frsh s putFunc :: (Var v) => @@ -388,19 +380,18 @@ putFunc refrep allowFop ctx f = case f of exn [] $ "putFunc: could not serialize foreign operation: " ++ show f getFunc :: - (MonadGet m, SerialConfig m, Var v) => [v] -> m (Func Reference v) -getFunc ctx = - askFOp >>= \allowFOp -> - getTag >>= \case - FVarT -> FVar <$> getVar ctx - FCombT -> FComb <$> getReference - FContT -> FCont <$> getVar ctx - FConT -> FCon <$> getReference <*> getCTag - FReqT -> FReq <$> getReference <*> getCTag - FPrimT -> FPrim . Left <$> getPOp - FForeignT - | allowFOp -> FPrim . Right <$> getFOp - | otherwise -> exn [] "getFunc: can't deserialize a foreign func" + (PrimBase m, Var v) => [v] -> GDeserial m (Func Reference v) +getFunc ctx (_, allowFOp) = + getTag >>= \case + FVarT -> FVar <$> getVar ctx + FCombT -> FComb <$> getReference + FContT -> FCont <$> getVar ctx + FConT -> FCon <$> getReference <*> getCTag + FReqT -> FReq <$> getReference <*> getCTag + FPrimT -> FPrim . Left <$> getPOp + FForeignT + | allowFOp -> FPrim . Right <$> getFOp + | otherwise -> exn [] "getFunc: can't deserialize a foreign func" -- Note: this numbering is derived, and so not particularly stable. -- However, foreign functions are not serialized for interchange. This @@ -409,15 +400,15 @@ getFunc ctx = putFOp :: ForeignFunc -> Builder putFOp = putVarInt . fromEnum -getFOp :: (MonadGet m) => m ForeignFunc -getFOp = toEnum . unVarInt <$> deserialize +getFOp :: (PrimBase m) => Get m ForeignFunc +getFOp = toEnum <$> getVarInt putPOp :: POp -> Builder putPOp op | Just w <- Map.lookup op pop2word = BU.word16BE w | otherwise = exn [] $ "putPOp: unknown POp: " ++ show op -getPOp :: (MonadGet m) => m POp +getPOp :: (PrimBase m) => Get m POp getPOp = getWord16be >>= \w -> case Map.lookup w word2pop of Just op -> pure op @@ -432,7 +423,7 @@ putLit (C c) = putTag CT <> putChar c putLit (LM r) = putTag LMT <> putReferent r putLit (LY r) = putTag LYT <> putReference r -getLit :: (MonadGet m) => m (Lit Reference) +getLit :: (PrimBase m) => Get m (Lit Reference) getLit = getTag >>= \case IT -> I <$> getInt @@ -497,32 +488,33 @@ putAsMap v = putter . fromDistinctAscList <> putter l <> putter r -getBLit :: (MonadGet m, SerialConfig m) => m (BLit Reference) -getBLit = +getBLit :: (PrimBase m) => GDeserial m (BLit Reference) +getBLit s@(v, fo) = getTag >>= \case TextT -> Text . Util.Text.fromText <$> getText - ListT -> List <$> getSeq getValue + ListT -> List <$> getSeq (getValue s) TmLinkT -> TmLink <$> getReferent TyLinkT -> TyLink <$> getReference BytesT -> Bytes <$> getBytes - QuoteT -> Quote <$> getValue + QuoteT -> Quote <$> (getValue s) CodeT -> - Code . flip CodeRep Uncacheable <$> withCodeVersion getGroup + Code . flip CodeRep Uncacheable <$> getGroup (valueToCode v, fo) BArrT -> BArr <$> getByteArray PosT -> Pos <$> getPositive NegT -> Neg <$> getPositive CharT -> Char <$> getChar FloatT -> Float <$> getFloat - ArrT -> Arr <$> getArray getValue - CachedCodeT -> Code . flip CodeRep Cacheable <$> getGroup + ArrT -> Arr <$> getArray (getValue s) + CachedCodeT -> + Code . flip CodeRep Cacheable <$> getGroup (valueToCode v, fo) MapT -> exn [] "getBLit: unsupported literal map" -{-# SPECIALIZE getBLit :: BDeserial (BLit Reference) #-} -{-# SPECIALIZE getBLit :: SDeserial (BLit Reference) #-} +{-# SPECIALIZE getBLit :: DeserialIO (BLit Reference) #-} +{-# SPECIALIZE getBLit :: DeserialST s (BLit Reference) #-} putRefs :: [Reference] -> Builder putRefs rs = putFoldable putReference rs -getRefs :: (MonadGet m) => m [Reference] +getRefs :: (PrimBase m) => Get m [Reference] getRefs = getList getReference putBranches :: @@ -562,40 +554,39 @@ putBranches refrep fops ctx bs = case bs of _ -> exn [] "putBranches: malformed intermediate term" getBranches :: - (MonadGet m) => - (SerialConfig m) => + (PrimBase m) => (Var v) => [v] -> Word64 -> - m (Branched Reference (ANormal Reference v)) -getBranches ctx frsh0 = + GDeserial m (Branched Reference (ANormal Reference v)) +getBranches ctx frsh0 s = getTag >>= \case MEmptyT -> pure MatchEmpty MIntT -> MatchIntegral - <$> getEnumMap getWord64be (getNormal ctx frsh0) - <*> getMaybe (getNormal ctx frsh0) + <$> getEnumMap getWord64be (getNormal ctx frsh0 s) + <*> getMaybe (getNormal ctx frsh0 s) MTextT -> MatchText - <$> getMap (Util.Text.fromText <$> getText) (getNormal ctx frsh0) - <*> getMaybe (getNormal ctx frsh0) + <$> getMap (Util.Text.fromText <$> getText) (getNormal ctx frsh0 s) + <*> getMaybe (getNormal ctx frsh0 s) MReqT -> MatchRequest - <$> getMapping getReference (getEnumMap getCTag (getCase ctx frsh0)) - <*> (TAbs v <$> getNormal (v : ctx) (frsh0 + 1)) + <$> getMapping getReference (getEnumMap getCTag (getCase ctx frsh0 s)) + <*> (TAbs v <$> getNormal (v : ctx) (frsh0 + 1) s) where v = getFresh frsh0 MDataT -> MatchData <$> getReference - <*> getEnumMap getCTag (getCase ctx frsh0) - <*> getMaybe (getNormal ctx frsh0) - MSumT -> MatchSum <$> getEnumMap getWord64be (getCase ctx frsh0) + <*> getEnumMap getCTag (getCase ctx frsh0 s) + <*> getMaybe (getNormal ctx frsh0 s) + MSumT -> MatchSum <$> getEnumMap getWord64be (getCase ctx frsh0 s) MNumT -> MatchNumeric <$> getReference - <*> getEnumMap getWord64be (getNormal ctx frsh0) - <*> getMaybe (getNormal ctx frsh0) + <*> getEnumMap getWord64be (getNormal ctx frsh0 s) + <*> getMaybe (getNormal ctx frsh0 s) putCase :: (Var v) => @@ -608,30 +599,29 @@ putCase refrep fops ctx (ccs, (TAbss us e)) = putCCs ccs <> putNormal refrep fops (pushCtx us ctx) e getCase :: - (MonadGet m) => - (SerialConfig m) => + (PrimBase m) => (Var v) => [v] -> Word64 -> - m ([Mem], ANormal Reference v) -getCase ctx frsh0 = do + GDeserial m ([Mem], ANormal Reference v) +getCase ctx frsh0 s = do ccs <- getCCs let l = length ccs frsh = frsh0 + fromIntegral l us = getFresh <$> take l [frsh0 ..] - (,) ccs . TAbss us <$> getNormal (pushCtx us ctx) frsh + (,) ccs . TAbss us <$> getNormal (pushCtx us ctx) frsh s putCTag :: CTag -> Builder putCTag c = putVarInt $ fromEnum c -getCTag :: (MonadGet m) => m CTag -getCTag = toEnum . unVarInt <$> deserialize +getCTag :: (PrimBase m) => Get m CTag +getCTag = toEnum <$> getVarInt putGroupRef :: GroupRef Reference -> Builder putGroupRef (GR r i) = putReference r <> BU.word64BE i -getGroupRef :: (MonadGet m) => m (GroupRef Reference) +getGroupRef :: (PrimBase m) => Get m (GroupRef Reference) getGroupRef = GR <$> getReference <*> getWord64be -- Notes @@ -673,52 +663,51 @@ putValue v (BLit (Map l)) = putAsMap v l putValue v (BLit l) = putTag BLitT <> putBLit v l -getValue :: (MonadGet m, SerialConfig m) => m (Value Reference) -getValue = - askVersion >>= \v -> - getTag >>= \case - PartialT - | Transfer vn <- v, - vn < 4 -> do - gr <- getGroupRef - getList getWord64be >>= assertEmptyUnboxed - bs <- getList getValue - pure $ Partial gr bs - | otherwise -> do - gr <- getGroupRef - vs <- getList getValue - pure $ Partial gr vs - DataT - | Transfer vn <- v, - vn < 4 -> do - r <- getReference - w <- getWord64be - getList getWord64be >>= assertEmptyUnboxed - vs <- getList getValue - pure $ Data r w vs - | otherwise -> do - r <- getReference - w <- getWord64be - vs <- getList getValue - pure $ Data r w vs - ContT - | Transfer vn <- v, - vn < 4 -> do - getList getWord64be >>= assertEmptyUnboxed - bs <- getList getValue - k <- getCont - pure $ Cont bs k - | otherwise -> do - bs <- getList getValue - k <- getCont - pure $ Cont bs k - BLitT -> BLit <$> getBLit +getValue :: (PrimBase m) => GDeserial m (Value Reference) +getValue s@(v, _) = + getTag >>= \case + PartialT + | Transfer vn <- v, + vn < 4 -> do + gr <- getGroupRef + getList getWord64be >>= assertEmptyUnboxed + bs <- getList (getValue s) + pure $ Partial gr bs + | otherwise -> do + gr <- getGroupRef + vs <- getList (getValue s) + pure $ Partial gr vs + DataT + | Transfer vn <- v, + vn < 4 -> do + r <- getReference + w <- getWord64be + getList getWord64be >>= assertEmptyUnboxed + vs <- getList (getValue s) + pure $ Data r w vs + | otherwise -> do + r <- getReference + w <- getWord64be + vs <- getList (getValue s) + pure $ Data r w vs + ContT + | Transfer vn <- v, + vn < 4 -> do + getList getWord64be >>= assertEmptyUnboxed + bs <- getList (getValue s) + k <- getCont s + pure $ Cont bs k + | otherwise -> do + bs <- getList (getValue s) + k <- getCont s + pure $ Cont bs k + BLitT -> BLit <$> getBLit s where - assertEmptyUnboxed :: (MonadGet m) => [a] -> m () + assertEmptyUnboxed :: (PrimBase m) => [a] -> Get m () assertEmptyUnboxed [] = pure () assertEmptyUnboxed _ = exn [] "getValue: unboxed values no longer supported" -{-# SPECIALIZE getValue :: BDeserial (Value Reference) #-} -{-# SPECIALIZE getValue :: SDeserial (Value Reference) #-} +{-# SPECIALIZE getValue :: DeserialIO (Value Reference) #-} +{-# SPECIALIZE getValue :: DeserialST s (Value Reference) #-} putCont :: Version -> Cont Reference -> Builder putCont _ KE = putTag KET @@ -735,57 +724,56 @@ putCont v (Push f n gr k) = <> putGroupRef gr <> putCont v k -getCont :: (MonadGet m, SerialConfig m) => m (Cont Reference) -getCont = - askVersion >>= \v -> - getTag >>= \case - KET -> pure KE - MarkT - | Transfer vn <- v, - vn < 4 -> do - getWord64be >>= assert0 "unboxed arg size" - ba <- getWord64be - refs <- getList getReference - vals <- getMapping getReference getValue - cont <- getCont - pure $ Mark ba refs vals cont - | otherwise -> - Mark - <$> getWord64be - <*> getList getReference - <*> getMapping getReference getValue - <*> getCont - PushT - | Transfer vn <- v, - vn < 4 -> do - getWord64be >>= assert0 "unboxed frame size" - bf <- getWord64be - getWord64be >>= assert0 "unboxed arg size" - ba <- getWord64be - gr <- getGroupRef - cont <- getCont - pure $ Push bf ba gr cont - | otherwise -> - Push - <$> getWord64be - <*> getWord64be - <*> getGroupRef - <*> getCont +getCont :: (PrimBase m) => GDeserial m (Cont Reference) +getCont s@(v, _) = + getTag >>= \case + KET -> pure KE + MarkT + | Transfer vn <- v, + vn < 4 -> do + getWord64be >>= assert0 "unboxed arg size" + ba <- getWord64be + refs <- getList getReference + vals <- getMapping getReference (getValue s) + cont <- getCont s + pure $ Mark ba refs vals cont + | otherwise -> + Mark + <$> getWord64be + <*> getList getReference + <*> getMapping getReference (getValue s) + <*> getCont s + PushT + | Transfer vn <- v, + vn < 4 -> do + getWord64be >>= assert0 "unboxed frame size" + bf <- getWord64be + getWord64be >>= assert0 "unboxed arg size" + ba <- getWord64be + gr <- getGroupRef + cont <- getCont s + pure $ Push bf ba gr cont + | otherwise -> + Push + <$> getWord64be + <*> getWord64be + <*> getGroupRef + <*> getCont s where assert0 _name 0 = pure () assert0 name n = exn [] $ "getCont: malformed intermediate term. Expected " <> name <> " to be 0, but got " <> show n -{-# SPECIALIZE getCont :: BDeserial (Cont Reference) #-} -{-# SPECIALIZE getCont :: SDeserial (Cont Reference) #-} +{-# SPECIALIZE getCont :: DeserialIO (Cont Reference) #-} +{-# SPECIALIZE getCont :: DeserialST s (Cont Reference) #-} -deserializeCode :: ByteString -> Either String (Referenced Code) -deserializeCode bs = runGetS go bs +deserializeCode :: ByteString -> IO (Either String (Referenced Code)) +deserializeCode bs = runGetCatchIO go bs where go = getWord32be >>= \case n | n == 4 -> CodeV4.getCodeWithHeader | 1 <= n && n < 4 -> - Plain <$> runReaderT getCode (Transfer n, False) + Plain <$> getCode (Transfer n, False) | otherwise -> fail $ "deserializeGroup: unknown version: " ++ show n @@ -848,22 +836,20 @@ serializeGroupForRehash (Derived h _) sg = f _ = Nothing refrep = Map.fromList . mapMaybe f $ groupTermLinks sg -getVersionedValue :: (MonadGet m) => m (Referenced Value) +getVersionedValue :: (PrimBase m) => Get m (Referenced Value) getVersionedValue = getWord32be >>= \case n | n < 1 -> fail $ "deserializeValue: unknown version: " ++ show n | n < 3 -> fail $ "deserializeValue: unsupported version: " ++ show n - | n <= 4 -> Plain <$> runReaderT getValue (Transfer n, False) + | n <= 4 -> Plain <$> getValue (Transfer n, False) | n == 5 -> ValueV5.getValueWithHeader | otherwise -> fail $ "deserializeValue: unknown version: " ++ show n -{-# SPECIALIZE getVersionedValue :: BGet.Get (Referenced Value) #-} -{-# SPECIALIZE getVersionedValue :: SGet.Get (Referenced Value) #-} +{-# SPECIALIZE getVersionedValue :: Get IO (Referenced Value) #-} +{-# SPECIALIZE getVersionedValue :: Get (ST s) (Referenced Value) #-} -deserializeValue :: L.ByteString -> Either String (Referenced Value) -deserializeValue bs = bimap thd thd $ runGetOrFail getVersionedValue bs - where - thd (_, _, x) = x +deserializeValue :: ByteString -> IO (Either String (Referenced Value)) +deserializeValue bs = runGetCatchIO getVersionedValue bs serializeValue :: Referenced Value -> ByteString serializeValue (dereference -> v) = @@ -913,20 +899,14 @@ serializeValueForHash v = -- Gets a SuperGroup with the current code version. Used for -- interpreter state serialization in U.R.Interface. -getGroupCurrent :: (MonadGet m, Var v) => m (SuperGroup Reference v) -getGroupCurrent = runReaderT getGroup (Transfer codeVersion, False) +getGroupCurrent :: (PrimBase m, Var v) => Get m (SuperGroup Reference v) +getGroupCurrent = getGroup (Transfer codeVersion, False) -askVersion :: (SerialConfig m) => m Version -askVersion = asks fst +type GDeserial m a = (Version, Bool) -> Get m a -askFOp :: (SerialConfig m) => m Bool -askFOp = asks snd +type DeserialIO a = (Version, Bool) -> Get IO a -type SerialConfig m = MonadReader (Version, Bool) m - -type BDeserial = ReaderT (Version, Bool) BGet.Get - -type SDeserial = ReaderT (Version, Bool) SGet.Get +type DeserialST s a = (Version, Bool) -> Get (ST s) a -- Convert value version numbers to code version numbers valueToCode :: Version -> Version @@ -938,9 +918,6 @@ valueToCode v | n > 2 = n - 1 | otherwise = n -withCodeVersion :: (SerialConfig m) => m r -> m r -withCodeVersion = local (first valueToCode) - valueVersion :: Word32 valueVersion = 4 diff --git a/unison-runtime/src/Unison/Runtime/ANF/Serialize/CodeV4.hs b/unison-runtime/src/Unison/Runtime/ANF/Serialize/CodeV4.hs index d53593d67e..2addb92170 100644 --- a/unison-runtime/src/Unison/Runtime/ANF/Serialize/CodeV4.hs +++ b/unison-runtime/src/Unison/Runtime/ANF/Serialize/CodeV4.hs @@ -1,13 +1,10 @@ module Unison.Runtime.ANF.Serialize.CodeV4 where import Control.Monad -import Data.Binary.Get qualified as BGet import Data.ByteString.Builder (Builder) import Data.ByteString.Builder qualified as BU -import Data.Bytes.Get hiding (getBytes) import Data.Functor ((<&>)) import Data.Map.Strict as Map (lookup) -import Data.Serialize.Get qualified as SGet import Data.Word (Word64) import GHC.Stack import Unison.ABT.Normalized (Term (..)) @@ -22,6 +19,7 @@ import Unison.Runtime.Serialize hiding ( getReferent, putReferent, ) +import Unison.Runtime.Serialize.Get import Unison.Util.Text qualified as Util.Text import Unison.Var (Type (ANFBlank), Var (..)) import Prelude hiding (getChar, putChar) @@ -47,7 +45,7 @@ putIndex :: Word64 -> Builder putIndex = putVarInt {-# INLINE putIndex #-} -getIndex :: (MonadGet m) => m Word64 +getIndex :: (PrimBase m) => Get m Word64 getIndex = getVarInt {-# INLINE getIndex #-} @@ -56,14 +54,16 @@ putVar ctx v | Just i <- index ctx v = putIndex i | otherwise = exn [] "putVar: variable not in context" -getVar :: (MonadGet m) => [v] -> m v +getVar :: (PrimBase m) => [v] -> Get m v getVar ctx = deindex ctx <$> getIndex +{-# INLINE getVar #-} putArgs :: (Eq v) => [v] -> [v] -> Builder putArgs ctx is = putFoldable (putVar ctx) is -getArgs :: (MonadGet m) => [v] -> m [v] +getArgs :: (PrimBase m) => [v] -> Get m [v] getArgs ctx = getList (getVar ctx) +{-# INLINE getArgs #-} putCCs :: [Mem] -> Builder putCCs ccs = putLength n <> foldMap putCC ccs @@ -72,13 +72,14 @@ putCCs ccs = putLength n <> foldMap putCC ccs putCC UN = BU.word8 0 putCC BX = BU.word8 1 -getCCs :: (MonadGet m) => m [Mem] +getCCs :: (PrimBase m) => Get m [Mem] getCCs = getList $ getWord8 <&> \case 0 -> UN 1 -> BX _ -> exn [] "getCCs: bad calling convention" +{-# INLINE getCCs #-} -- Serializes a `SuperGroup`. -- @@ -109,9 +110,9 @@ putGroup fops (Rec bs e) = ctx = pushCtx us [] getGroup :: - (MonadGet m) => + (PrimBase m) => (Var v) => - m (SuperGroup RefNum v) + Get m (SuperGroup RefNum v) getGroup = do l <- getLength let n = fromIntegral l @@ -119,13 +120,15 @@ getGroup = do ctx = pushCtx vs [] cs <- replicateM l (getComb ctx n) Rec (zip vs cs) <$> getComb ctx n +{-# INLINEABLE getGroup #-} putCode :: Bool -> (Code RefNum) -> Builder putCode fops (CodeRep g c) = putGroup fops g <> putCacheability c -getCode :: (MonadGet m) => m (Code RefNum) +getCode :: (PrimBase m) => Get m (Code RefNum) getCode = CodeRep <$> getGroup <*> getCacheability +{-# INLINEABLE getCode #-} putCodeWithHeader :: [Reference] -> [Reference] -> Bool -> Code RefNum -> Builder @@ -134,7 +137,7 @@ putCodeWithHeader tyrs tmrs fops co = <> putFoldable putReference tmrs <> putCode fops co -getCodeWithHeader :: (MonadGet m) => m (Referenced Code) +getCodeWithHeader :: (PrimBase m) => Get m (Referenced Code) getCodeWithHeader = do tyl <- getLength tys <- replicateM tyl getReference @@ -142,19 +145,19 @@ getCodeWithHeader = do tms <- replicateM tml getReference co <- getCode pure (WithRefs tys tms co) -{-# SPECIALIZE getCodeWithHeader :: BGet.Get (Referenced Code) #-} -{-# SPECIALIZE getCodeWithHeader :: SGet.Get (Referenced Code) #-} +{-# INLINEABLE getCodeWithHeader #-} putCacheability :: Cacheability -> Builder putCacheability Uncacheable = BU.word8 0 putCacheability Cacheable = BU.word8 1 -getCacheability :: (MonadGet m) => m Cacheability +getCacheability :: (PrimBase m) => Get m Cacheability getCacheability = getWord8 >>= \case 0 -> pure Uncacheable 1 -> pure Cacheable n -> exn [] $ "getBLit: unrecognized cacheability byte: " ++ show n +{-# INLINE getCacheability #-} putComb :: (Var v) => @@ -169,16 +172,17 @@ getFresh :: (Var v) => Word64 -> v getFresh n = freshenId n $ typed ANFBlank getComb :: - (MonadGet m) => + (PrimBase m) => (Var v) => [v] -> Word64 -> - m (SuperNormal RefNum v) + Get m (SuperNormal RefNum v) getComb ctx frsh0 = do ccs <- getCCs let us = zipWith (\_ -> getFresh) ccs [frsh0 ..] frsh = frsh0 + fromIntegral (length ccs) Lambda ccs . TAbss us <$> getNormal (pushCtx us ctx) frsh +{-# INLINEABLE getComb #-} putNormal :: (Var v) => @@ -229,11 +233,11 @@ putNormal fops ctx tm = case tm of v -> exn [] $ "putNormal: malformed term\n" ++ show v getNormal :: - (MonadGet m) => + (PrimBase m) => (Var v) => [v] -> Word64 -> - m (ANormal RefNum v) + Get m (ANormal RefNum v) getNormal ctx frsh0 = getTag >>= \case VarT -> TVar <$> getVar ctx @@ -285,6 +289,7 @@ getNormal ctx frsh0 = TLets (Indirect w) us ccs <$> getNormal ctx frsh0 <*> getNormal (pushCtx us ctx) frsh +{-# INLINEABLE getNormal #-} putFunc :: (Var v) => @@ -300,7 +305,7 @@ putFunc ctx f = case f of FPrim (Left p) -> putTag FPrimT <> putPOp p FPrim (Right f) -> putTag FForeignT <> putFOp f -getFunc :: (MonadGet m, Var v) => [v] -> m (Func RefNum v) +getFunc :: (PrimBase m, Var v) => [v] -> Get m (Func RefNum v) getFunc ctx = getTag >>= \case FVarT -> FVar <$> getVar ctx @@ -310,6 +315,7 @@ getFunc ctx = FReqT -> FReq <$> getRefNum <*> getCTag FPrimT -> FPrim . Left <$> getPOp FForeignT -> FPrim . Right <$> getFOp +{-# INLINEABLE getFunc #-} -- Note: this numbering is derived, and so not particularly stable. -- However, foreign functions are not serialized for interchange. This @@ -318,19 +324,21 @@ getFunc ctx = putFOp :: ForeignFunc -> Builder putFOp = putVarInt . fromEnum -getFOp :: (MonadGet m) => m ForeignFunc +getFOp :: (PrimBase m) => Get m ForeignFunc getFOp = toEnum <$> getVarInt +{-# INLINE getFOp #-} putPOp :: POp -> Builder putPOp op | Just w <- Map.lookup op pop2word = BU.word16BE w | otherwise = exn [] $ "putPOp: unknown POp: " ++ show op -getPOp :: (MonadGet m) => m POp +getPOp :: (PrimBase m) => Get m POp getPOp = getWord16be >>= \w -> case Map.lookup w word2pop of Just op -> pure op Nothing -> exn [] "getPOp: unknown enum code" +{-# INLINE getPOp #-} putLit :: Lit RefNum -> Builder putLit = \case @@ -342,7 +350,7 @@ putLit = \case LM r -> putTag LMT <> putNumberedReferent r LY r -> putTag LYT <> putRefNum r -getLit :: (MonadGet m) => m (Lit RefNum) +getLit :: (PrimBase m) => Get m (Lit RefNum) getLit = getTag >>= \case IT -> I <$> getInt @@ -352,6 +360,7 @@ getLit = CT -> C <$> getChar LMT -> LM <$> getNumberedReferent LYT -> LY <$> getRefNum +{-# INLINEABLE getLit #-} putBranches :: (Var v) => @@ -392,11 +401,11 @@ putBranches fops ctx bs = case bs of _ -> exn [] "putBranches: malformed intermediate term" getBranches :: - (MonadGet m) => + (PrimBase m) => (Var v) => [v] -> Word64 -> - m (Branched RefNum (ANormal RefNum v)) + Get m (Branched RefNum (ANormal RefNum v)) getBranches ctx frsh0 = getTag >>= \case MEmptyT -> pure MatchEmpty @@ -427,6 +436,7 @@ getBranches ctx frsh0 = <$> getRefNum <*> getEnumMap getWord64be (getNormal ctx frsh0) <*> getMaybe (getNormal ctx frsh0) +{-# INLINEABLE getBranches #-} putCase :: (Var v) => @@ -438,20 +448,22 @@ putCase fops ctx (ccs, (TAbss us e)) = putCCs ccs <> putNormal fops (pushCtx us ctx) e getCase :: - (MonadGet m) => + (PrimBase m) => (Var v) => [v] -> Word64 -> - m ([Mem], ANormal RefNum v) + Get m ([Mem], ANormal RefNum v) getCase ctx frsh0 = do ccs <- getCCs let l = length ccs frsh = frsh0 + fromIntegral l us = getFresh <$> take l [frsh0 ..] (,) ccs . TAbss us <$> getNormal (pushCtx us ctx) frsh +{-# INLINEABLE getCase #-} putCTag :: CTag -> Builder putCTag c = putVarInt $ fromEnum c -getCTag :: (MonadGet m) => m CTag +getCTag :: (PrimBase m) => Get m CTag getCTag = toEnum <$> getVarInt +{-# INLINE getCTag #-} diff --git a/unison-runtime/src/Unison/Runtime/ANF/Serialize/ValueV5.hs b/unison-runtime/src/Unison/Runtime/ANF/Serialize/ValueV5.hs index 054d511dff..7ef220e12a 100644 --- a/unison-runtime/src/Unison/Runtime/ANF/Serialize/ValueV5.hs +++ b/unison-runtime/src/Unison/Runtime/ANF/Serialize/ValueV5.hs @@ -7,12 +7,9 @@ module Unison.Runtime.ANF.Serialize.ValueV5 where import Control.Monad (replicateM) -import Data.Binary.Get qualified as BGet import Data.ByteString.Builder (Builder) import Data.ByteString.Builder qualified as BU import Data.ByteString.Lazy qualified as L -import Data.Bytes.Get hiding (getBytes) -import Data.Serialize.Get qualified as SGet import Unison.Reference (Reference) import Unison.Runtime.ANF as ANF hiding (Tag) import Unison.Runtime.ANF.Serialize.CodeV4 @@ -27,6 +24,7 @@ import Unison.Runtime.Serialize hiding putReferent, ) import Unison.Runtime.Serialize qualified as SER +import Unison.Runtime.Serialize.Get import Unison.Util.Text qualified as Util.Text import Prelude hiding (getChar, putChar) @@ -34,8 +32,9 @@ putGroupRef :: GroupRef RefNum -> Builder putGroupRef (GR r i) = putRefNum r <> putVarInt i {-# INLINE putGroupRef #-} -getGroupRef :: (MonadGet m) => m (GroupRef RefNum) +getGroupRef :: (PrimBase m) => Get m (GroupRef RefNum) getGroupRef = GR <$> getRefNum <*> getVarInt +{-# INLINE getGroupRef #-} -- Notes -- @@ -95,7 +94,7 @@ putValue = \case <> putCont k BLit l -> putTag BLitT <> putBLit l -getValue :: (MonadGet m) => m (Value RefNum) +getValue :: (PrimBase m) => Get m (Value RefNum) getValue = getTag >>= \case PartialT -> do @@ -112,8 +111,7 @@ getValue = k <- getCont pure $ Cont bs k BLitT -> BLit <$> getBLit -{-# SPECIALIZE getValue :: BGet.Get (Value RefNum) #-} -{-# SPECIALIZE getValue :: SGet.Get (Value RefNum) #-} +{-# INLINEABLE getValue #-} putCont :: Cont RefNum -> Builder putCont = \case @@ -131,7 +129,7 @@ putCont = \case <> putGroupRef gr <> putCont k -getCont :: (MonadGet m) => m (Cont RefNum) +getCont :: (PrimBase m) => Get m (Cont RefNum) getCont = getTag >>= \case KET -> pure KE @@ -147,8 +145,7 @@ getCont = <*> getVarInt <*> getGroupRef <*> getCont -{-# SPECIALIZE getCont :: BGet.Get (Cont RefNum) #-} -{-# SPECIALIZE getCont :: SGet.Get (Cont RefNum) #-} +{-# INLINEABLE getCont #-} putBLit :: BLit RefNum -> Builder putBLit = \case @@ -173,7 +170,7 @@ putBLit = \case Map m -> putTag MapT <> putMapping putValue putValue m -getBLit :: (MonadGet m) => m (BLit RefNum) +getBLit :: (PrimBase m) => Get m (BLit RefNum) getBLit = getTag >>= \case TextT -> Text . Util.Text.fromText <$> getText @@ -192,8 +189,7 @@ getBLit = ArrT -> Arr <$> getArray getValue CachedCodeT -> Code . flip CodeRep Cacheable <$> getGroup MapT -> Map <$> getMapping getValue getValue -{-# SPECIALIZE getBLit :: BGet.Get (BLit RefNum) #-} -{-# SPECIALIZE getBLit :: SGet.Get (BLit RefNum) #-} +{-# INLINEABLE getBLit #-} putValueWithHeader :: [Reference] -> [Reference] -> Value RefNum -> Builder @@ -212,7 +208,7 @@ versionedValueBytes :: versionedValueBytes tyrs tmrs v = BU.toLazyByteString $ putVersionedValue tyrs tmrs v -getValueWithHeader :: (MonadGet m) => m (Referenced Value) +getValueWithHeader :: (PrimBase m) => Get m (Referenced Value) getValueWithHeader = do tyl <- getLength tys <- replicateM tyl SER.getReference @@ -220,5 +216,4 @@ getValueWithHeader = do tms <- replicateM tml SER.getReference v <- getValue pure (WithRefs tys tms v) -{-# SPECIALIZE getValueWithHeader :: BGet.Get (Referenced Value) #-} -{-# SPECIALIZE getValueWithHeader :: SGet.Get (Referenced Value) #-} +{-# INLINEABLE getValueWithHeader #-} diff --git a/unison-runtime/src/Unison/Runtime/Foreign/Function.hs b/unison-runtime/src/Unison/Runtime/Foreign/Function.hs index 10ae5b1e8c..ce85c7c57e 100644 --- a/unison-runtime/src/Unison/Runtime/Foreign/Function.hs +++ b/unison-runtime/src/Unison/Runtime/Foreign/Function.hs @@ -587,8 +587,7 @@ foreignCallHelper = \case Left err -> die [] err Right bs -> pure $ Bytes.fromLazyByteString bs Code_deserialize -> - mkForeign $ - pure . ANF.deserializeCode . Bytes.toArray + mkForeign $ ANF.deserializeCode . Bytes.toArray Code_display -> mkForeign $ \(nm, (dereference -> ANF.CodeRep sg _)) -> pure $ ANF.prettyGroup @Symbol (Util.Text.unpack nm) sg "" @@ -602,8 +601,7 @@ foreignCallHelper = \case mkForeign $ fmap Bytes.fromLazyByteString . uncurry ANF.serializeValueWithVersion Value_deserialize -> - mkForeign $ - pure . ANF.deserializeValue . Bytes.toLazyByteString + mkForeign $ ANF.deserializeValue . Bytes.toByteString Crypto_HashAlgorithm_Sha3_512 -> mkHashAlgorithm "Sha3_512" Hash.SHA3_512 Crypto_HashAlgorithm_Sha3_256 -> mkHashAlgorithm "Sha3_256" Hash.SHA3_256 Crypto_HashAlgorithm_Sha2_512 -> mkHashAlgorithm "Sha2_512" Hash.SHA512 diff --git a/unison-runtime/src/Unison/Runtime/Interface.hs b/unison-runtime/src/Unison/Runtime/Interface.hs index 925e2985b0..746b15e73f 100644 --- a/unison-runtime/src/Unison/Runtime/Interface.hs +++ b/unison-runtime/src/Unison/Runtime/Interface.hs @@ -38,14 +38,10 @@ import Control.Concurrent.STM as STM import Control.Exception (fromException, tryJust) import Control.Monad import Control.Monad.State -import Data.Binary.Get (Get, runGetOrFail) -import Data.Binary.Get qualified as Get import Data.Bitraversable (bitraverse) import Data.ByteString qualified as B import Data.ByteString.Builder (Builder) import Data.ByteString.Builder qualified as BU -import Data.ByteString.Lazy qualified as BL -import Data.Bytes.Get (MonadGet) import Data.Foldable import Data.IORef import Data.List qualified as L @@ -121,6 +117,7 @@ import Unison.Runtime.Machine import Unison.Runtime.Pattern import Unison.Runtime.Profiling import Unison.Runtime.Serialize as SER +import Unison.Runtime.Serialize.Get import Unison.Runtime.Stack import Unison.Runtime.TypeTags qualified as TT import Unison.Symbol (Symbol) @@ -594,10 +591,10 @@ putTextBig text = where bs = encodeUtf8 text -getTextBig :: Get Text +getTextBig :: (PrimBase m) => Get m Text getTextBig = do - len <- Get.getWord32be - bs <- B.copy <$> Get.getByteString (fromIntegral len) + len <- getWord32be + bs <- B.copy <$> getByteString (fromIntegral len) pure $ decodeUtf8 bs interpCompile :: @@ -853,11 +850,10 @@ catchErrors sub = sub `UnliftIO.catch` (pure . Left . CompileExn) `UnliftIO.catch` (pure . Left . RuntimeExn Nothing) decodeStandalone :: - BL.ByteString -> - Either String (Text, Text, CombIx, StoredCache) -decodeStandalone b = bimap thd thd $ runGetOrFail g b + B.ByteString -> + IO (Either String (Text, Text, CombIx, StoredCache)) +decodeStandalone b = runGetCatchIO g b where - thd (_, _, x) = x g = (,,,) <$> getTextBig @@ -940,7 +936,7 @@ putStoredCache (SCache cs crs cacheableCombs oinfo trs ftm fty int rtm rty sbs) <> putMap putReference putNat rty <> putMap putReference (putFoldable putReference) sbs -getStoredCache :: (MonadGet m) => m StoredCache +getStoredCache :: (PrimBase m) => Get m StoredCache getStoredCache = SCache <$> getEnumMap getNat (getEnumMap getNat getComb) diff --git a/unison-runtime/src/Unison/Runtime/MCode/Serialize.hs b/unison-runtime/src/Unison/Runtime/MCode/Serialize.hs index fb40cca5b5..bc17350520 100644 --- a/unison-runtime/src/Unison/Runtime/MCode/Serialize.hs +++ b/unison-runtime/src/Unison/Runtime/MCode/Serialize.hs @@ -12,9 +12,6 @@ where import Data.ByteString.Builder (Builder) import Data.ByteString.Builder qualified as BU -import Data.Bytes.Get -import Data.Bytes.Serial -import Data.Bytes.VarInt import Data.Void (Void) import Data.Word (Word64) import GHC.Exts (IsList (..)) @@ -23,6 +20,7 @@ import Unison.Runtime.Array (PrimArray) import Unison.Runtime.Foreign.Function.Type (ForeignFunc) import Unison.Runtime.MCode hiding (MatchT) import Unison.Runtime.Serialize +import Unison.Runtime.Serialize.Get import Unison.Util.Text qualified as Util.Text import Prelude hiding (getChar, putChar) @@ -39,7 +37,7 @@ instance Tag CombT where putPackedTag :: PackedTag -> Builder putPackedTag (PackedTag w) = pWord w -getPackedTag :: (MonadGet m) => m PackedTag +getPackedTag :: (PrimBase m) => Get m PackedTag getPackedTag = PackedTag <$> gWord putComb :: (clos -> Builder) -> GComb clos comb -> Builder @@ -49,14 +47,14 @@ putComb pClos = \case (CachedVal w v) -> putTag CachedClosureT <> putNat w <> pClos v -getComb :: (MonadGet m) => m (GComb Void CombIx) +getComb :: (PrimBase m) => Get m (GComb Void CombIx) getComb = getTag >>= \case LamT -> Lam <$> gInt <*> gInt <*> getSection CachedClosureT -> error "getComb: Unexpected serialized Cached Closure" -getMForeignFunc :: (MonadGet m) => m ForeignFunc +getMForeignFunc :: (PrimBase m) => Get m ForeignFunc getMForeignFunc = do toEnum <$> gInt @@ -129,7 +127,7 @@ putSection = \case <> putSection pu <> putEnumMap pWord putBranch bs -getSection :: (MonadGet m) => m Section +getSection :: (PrimBase m) => Get m Section getSection = getTag >>= \case AppT -> App <$> getBool <*> getRef <*> getArgs @@ -242,7 +240,7 @@ putInstr = \case -- Sandboxing failures should only exist in code we're actively running, it shouldn't be serialized. error "putInstr: Unexpected serialized Sandboxing Failure" -getInstr :: (MonadGet m) => m Instr +getInstr :: (PrimBase m) => Get m Instr getInstr = getTag >>= \case Prim1T -> Prim1 <$> getTag <*> gInt @@ -297,7 +295,7 @@ putArgs (VArgR i j) = putTag ArgRT <> pInt i <> pInt j putArgs (VArgN pa) = putTag ArgNT <> putIntArr pa putArgs (VArgV i) = putTag ArgVT <> pInt i -getArgs :: (MonadGet m) => m Args +getArgs :: (PrimBase m) => Get m Args getArgs = getTag >>= \case ZArgsT -> pure ZArgs @@ -324,7 +322,7 @@ putRef (Stk i) = putTag StkT <> pInt i putRef (Env cix _) = putTag EnvT <> putCombIx cix putRef (Dyn i) = putTag DynT <> pWord i -getRef :: (MonadGet m) => m Ref +getRef :: (PrimBase m) => Get m Ref getRef = getTag >>= \case StkT -> Stk <$> gInt @@ -336,7 +334,7 @@ getRef = putCombIx :: CombIx -> Builder putCombIx (CIx r n i) = putReference r <> pWord n <> pWord i -getCombIx :: (MonadGet m) => m CombIx +getCombIx :: (PrimBase m) => Get m CombIx getCombIx = CIx <$> getReference <*> gWord <*> gWord data MLitT = MIT | MNT | MCT | MDT | MTT | MMT | MYT @@ -368,7 +366,7 @@ putLit (MT t) = putTag MTT <> putText (Util.Text.toText t) putLit (MM r) = putTag MMT <> putReferent r putLit (MY r) = putTag MYT <> putReference r -getLit :: (MonadGet m) => m MLit +getLit :: (PrimBase m) => Get m MLit getLit = getTag >>= \case MIT -> MI <$> gInt @@ -408,7 +406,7 @@ putBranch (TestW d m) = putBranch (TestT d m) = putTag TestTT <> putSection d <> putMap (putText . Util.Text.toText) putSection m -getBranch :: (MonadGet m) => m Branch +getBranch :: (PrimBase m) => Get m Branch getBranch = getTag >>= \case Test1T -> Test1 <$> gWord <*> getSection <*> getSection @@ -422,13 +420,13 @@ getBranch = TestWT -> TestW <$> getSection <*> getEnumMap gWord getSection TestTT -> TestT <$> getSection <*> getMap (Util.Text.fromText <$> getText) getSection -gInt :: (MonadGet m) => m Int -gInt = unVarInt <$> deserialize +gInt :: (PrimBase m) => Get m Int +gInt = getVarInt pInt :: Int -> Builder pInt i = putVarInt i -gBool :: (MonadGet m) => m Bool +gBool :: (PrimBase m) => Get m Bool gBool = getWord8 >>= \case 0 -> pure False @@ -439,8 +437,8 @@ pBool :: Bool -> Builder pBool False = BU.word8 0 pBool True = BU.word8 1 -gWord :: (MonadGet m) => m Word64 -gWord = unVarInt <$> deserialize +gWord :: (PrimBase m) => Get m Word64 +gWord = getVarInt pWord :: Word64 -> Builder pWord w = putVarInt w @@ -448,5 +446,5 @@ pWord w = putVarInt w putIntArr :: PrimArray Int -> Builder putIntArr pa = putFoldable pInt $ toList pa -getIntArr :: (MonadGet m) => m (PrimArray Int) +getIntArr :: (PrimBase m) => Get m (PrimArray Int) getIntArr = fromList <$> getList gInt diff --git a/unison-runtime/src/Unison/Runtime/Serialize.hs b/unison-runtime/src/Unison/Runtime/Serialize.hs index c6813e3c8e..f0b6edf1b9 100644 --- a/unison-runtime/src/Unison/Runtime/Serialize.hs +++ b/unison-runtime/src/Unison/Runtime/Serialize.hs @@ -2,15 +2,12 @@ module Unison.Runtime.Serialize where -import Control.Monad (replicateM) -import Data.Bits (Bits, clearBit, setBit, shiftL, shiftR, testBit, (.|.)) +import Control.Monad.Primitive +import Data.Bits (Bits, setBit, shiftR) import Data.ByteString qualified as B import Data.ByteString.Builder (Builder) import Data.ByteString.Builder qualified as BU -import Data.Bytes.Get hiding (getBytes) -import Data.Bytes.Get qualified as Ser -import Data.Bytes.Serial -import Data.Bytes.Signed (Unsigned, unsigned) +import Data.Bytes.Signed import Data.Int (Int64) import Data.Map.Strict as Map (Map, fromList, toList) import Data.Primitive.Array @@ -18,7 +15,6 @@ import Data.Primitive.Array indexArray, sizeofArray, ) -import Data.Sequence (Seq, (|>)) import Data.Text (Text) import Data.Text.Encoding (decodeUtf8, encodeUtf8) import Data.Vector.Primitive qualified as BA @@ -38,10 +34,12 @@ import Unison.Runtime.MCode Prim2 (..), ) import Unison.Runtime.Referenced (RefNum (..)) +import Unison.Runtime.Serialize.Get as Get import Unison.Util.Bytes qualified as Bytes import Unison.Util.EnumContainers as EC +import Prelude hiding (getChar) -unknownTag :: (MonadGet m) => String -> Word8 -> m a +unknownTag :: (PrimBase m) => String -> Word8 -> Get m a unknownTag t w = remaining >>= \r -> exn [] $ @@ -55,26 +53,16 @@ unknownTag t w = class Tag t where tag2word :: t -> Word8 - word2tag :: (MonadGet m) => Word8 -> m t + word2tag :: (PrimBase m) => Word8 -> Get m t putTag :: (Tag t) => t -> Builder putTag = BU.word8 . tag2word {-# INLINE putTag #-} -getTag :: (MonadGet m) => (Tag t) => m t +getTag :: (PrimBase m) => (Tag t) => Get m t getTag = word2tag =<< getWord8 {-# INLINE getTag #-} -getVarInt :: (MonadGet m, Num b, Bits b) => m b -getVarInt = getWord8 >>= go - where - go n - | testBit n 7 = do - m <- getWord8 >>= go - return $ shiftL m 7 .|. clearBit (fromIntegral n) 7 - | otherwise = return $ fromIntegral n -{-# INLINE getVarInt #-} - putVarInt :: (Integral a, Integral (Unsigned a), Bits (Unsigned a)) => a -> Builder putVarInt = go . unsigned @@ -90,36 +78,41 @@ putVarInt = go . unsigned putChar :: Char -> Builder putChar = putVarInt . fromEnum -getChar :: (MonadGet m) => m Char +getChar :: (PrimBase m) => Get m Char getChar = toEnum <$> getVarInt +{-# INLINEABLE getChar #-} putFloat :: Double -> Builder putFloat = BU.doubleBE -getFloat :: (MonadGet m) => m Double -getFloat = deserializeBE +getFloat :: (PrimBase m) => Get m Double +getFloat = getDoublebe +{-# INLINE getFloat #-} putBool :: Bool -> Builder putBool b = BU.word8 (if b then 1 else 0) -getBool :: (MonadGet m) => m Bool +getBool :: (PrimBase m) => Get m Bool getBool = d =<< getWord8 where d 0 = pure False d 1 = pure True d n = exn [] $ "getBool: bad tag: " ++ show n +{-# INLINE getBool #-} putNat :: Word64 -> Builder putNat = BU.word64BE -getNat :: (MonadGet m) => m Word64 +getNat :: (PrimBase m) => Get m Word64 getNat = getWord64be +{-# INLINE getNat #-} putInt :: Int64 -> Builder putInt = BU.int64BE -getInt :: (MonadGet m) => m Int64 -getInt = deserializeBE +getInt :: (PrimBase m) => Get m Int64 +getInt = getInt64be +{-# INLINE getInt #-} putLength :: ( Integral n, @@ -133,13 +126,13 @@ putLength = putVarInt {-# INLINE putLength #-} getLength :: - ( MonadGet m, + ( PrimBase m, Integral n, Integral (Unsigned n), Bits n, Bits (Unsigned n) ) => - m n + Get m n getLength = getVarInt {-# INLINE getLength #-} @@ -156,7 +149,7 @@ putPositive n -- Reads as an Integer, then checks that the result will fit in the -- result type. -getPositive :: forall m n. (Bounded n, Integral n, MonadGet m) => m n +getPositive :: forall m n. (Bounded n, Integral n, PrimBase m) => Get m n getPositive = validate =<< getVarInt where mx0 :: n @@ -164,7 +157,7 @@ getPositive = validate =<< getVarInt mx :: Integer mx = fromIntegral mx0 - validate :: Integer -> m n + validate :: Integer -> Get m n validate n | n <= mx = pure $ fromIntegral n | otherwise = fail $ "getPositive: overflow: " ++ show n @@ -179,27 +172,18 @@ putFoldable putA as = putMap :: (a -> Builder) -> (b -> Builder) -> Map a b -> Builder putMap putA putB m = putMapping putA putB $ Map.toList m -getList :: (MonadGet m) => m a -> m [a] -getList a = getLength >>= (`replicateM` a) -{-# INLINE getList #-} - -getSeq :: (MonadGet m) => m a -> m (Seq a) -getSeq a = getLength >>= pull mempty - where - pull !acc (n :: Int) - | n <= 0 = pure acc - | otherwise = a >>= \x -> pull (acc |> x) (n - 1) -{-# INLINE getSeq #-} - -getMap :: (MonadGet m, Ord a) => m a -> m b -> m (Map a b) -getMap getA getB = Map.fromList <$> getMapping getA getB +-- TODO: switch to MapBuilder when containers gets updated +getMap :: (PrimBase m, Ord a) => Get m a -> Get m b -> Get m (Map a b) +getMap getA getB = + getAccumulatingRevList (Map.fromList . reverse) (getPair getA getB) +{-# INLINEABLE getMap #-} putMapping :: (a -> Builder) -> (b -> Builder) -> [(a, b)] -> Builder putMapping putA putB = putFoldable (putPair putA putB) {-# INLINE putMapping #-} -getMapping :: (MonadGet m) => m a -> m b -> m [(a, b)] -getMapping getA getB = getLength >>= (`replicateM` getPair getA getB) +getMapping :: (PrimBase m) => Get m a -> Get m b -> Get m [(a, b)] +getMapping getA getB = getList (getPair getA getB) {-# INLINE getMapping #-} putEnumMap :: @@ -210,41 +194,44 @@ putEnumMap :: Builder putEnumMap pk pv m = putFoldable (putPair pk pv) (mapToList m) -getEnumMap :: (MonadGet m) => (EnumKey k) => m k -> m v -> m (EnumMap k v) +getEnumMap :: (PrimBase m) => (EnumKey k) => Get m k -> Get m v -> Get m (EnumMap k v) getEnumMap gk gv = mapFromList <$> getList (getPair gk gv) putEnumSet :: (EnumKey k) => (k -> Builder) -> EnumSet k -> Builder putEnumSet pk s = putLength (setSize s) <> foldrSet (\k b -> pk k <> b) mempty s -getEnumSet :: (MonadGet m) => (EnumKey k) => m k -> m (EnumSet k) +getEnumSet :: (PrimBase m) => (EnumKey k) => Get m k -> Get m (EnumSet k) getEnumSet gk = setFromList <$> getList gk putMaybe :: Maybe a -> (a -> Builder) -> Builder putMaybe Nothing _ = BU.word8 0 putMaybe (Just a) putA = BU.word8 1 <> putA a -getMaybe :: (MonadGet m) => m a -> m (Maybe a) +getMaybe :: (PrimBase m) => Get m a -> Get m (Maybe a) getMaybe getA = getWord8 >>= \tag -> case tag of 0 -> pure Nothing 1 -> Just <$> getA _ -> unknownTag "Maybe" tag +{-# INLINE getMaybe #-} putPair :: (a -> Builder) -> (b -> Builder) -> (a, b) -> Builder putPair putA putB (a, b) = putA a <> putB b {-# INLINE putPair #-} -getPair :: (MonadGet m) => m a -> m b -> m (a, b) +getPair :: (PrimBase m) => Get m a -> Get m b -> Get m (a, b) getPair = liftA2 (,) +{-# INLINE getPair #-} -getBytes :: (MonadGet m) => m Bytes.Bytes +getBytes :: (PrimBase m) => Get m Bytes.Bytes getBytes = Bytes.fromChunks <$> getList getBlock +{-# INLINE getBytes #-} putBytes :: Bytes.Bytes -> Builder putBytes = putFoldable putBlock . Bytes.chunks -getByteArray :: (MonadGet m) => m PA.ByteArray +getByteArray :: (PrimBase m) => Get m PA.ByteArray getByteArray = PA.byteArrayFromList <$> getList getWord8 putByteArray :: PA.ByteArray -> Builder @@ -252,11 +239,6 @@ putByteArray a = putLength (PA.sizeofByteArray a) <> BU.shortByteString (PA.byteArrayToShortByteString a) -getArray :: (MonadGet m) => m a -> m (PA.Array a) -getArray a = do - sz <- getLength - PA.arrayFromListN sz <$> replicateM sz a - putArray :: (a -> Builder) -> PA.Array a -> Builder putArray putThing a = putLength sz <> go 0 where @@ -266,7 +248,7 @@ putArray putThing a = putLength sz <> go 0 | otherwise = mempty {-# INLINE putArray #-} -getBlock :: (MonadGet m) => m Bytes.Chunk +getBlock :: (PrimBase m) => Get m Bytes.Chunk getBlock = getLength >>= fmap Bytes.byteStringToChunk . getByteString putBlock :: Bytes.Chunk -> Builder @@ -278,10 +260,10 @@ putHash h = putLength (B.length bs) <> BU.byteString bs bs = Hash.toByteString h {-# INLINE putHash #-} -getHash :: (MonadGet m) => m Hash +getHash :: (PrimBase m) => Get m Hash getHash = do len <- getLength - bs <- Ser.getBytes len + bs <- getByteString len pure $ Hash.fromByteString bs {-# INLINE getHash #-} @@ -295,7 +277,7 @@ putReferent = \case <> putConstructorReference r <> putConstructorType ct -getReferent :: (MonadGet m) => m Referent +getReferent :: (PrimBase m) => Get m Referent getReferent = do tag <- getWord8 case tag of @@ -332,7 +314,7 @@ putNumberedReferent = \case <> putNumberedConstructorReference r <> putConstructorType ct -getReferentByNumber :: (MonadGet m) => GetRefLookup -> m Referent +getReferentByNumber :: (PrimBase m) => GetRefLookup -> Get m Referent getReferentByNumber (tys, tms) = do tag <- getWord8 case tag of @@ -340,14 +322,14 @@ getReferentByNumber (tys, tms) = do 1 -> Con <$> getConstructorReferenceByNumber tys <*> getConstructorType _ -> unknownTag "getReferent" tag -getNumberedReferent :: (MonadGet m) => m (Referent' RefNum) +getNumberedReferent :: (PrimBase m) => Get m (Referent' RefNum) getNumberedReferent = getWord8 >>= \case 0 -> Ref' <$> getRefNum 1 -> Con' <$> getNumberedConstructorReference <*> getConstructorType tag -> unknownTag "getNumberedReferent" tag -getConstructorType :: (MonadGet m) => m CT.ConstructorType +getConstructorType :: (PrimBase m) => Get m CT.ConstructorType getConstructorType = getWord8 >>= \case 0 -> pure CT.Data @@ -365,7 +347,7 @@ putConstructorReferenceByNumber tys (ConstructorReference r i) = putReferenceByNumber tys r <> putLength i getConstructorReferenceByNumber :: - (MonadGet m) => Array Reference -> m ConstructorReference + (PrimBase m) => Array Reference -> Get m ConstructorReference getConstructorReferenceByNumber tys = ConstructorReference <$> getReferenceByNumber tys <*> getLength @@ -375,14 +357,14 @@ putNumberedConstructorReference (ConstructorReference r i) = putRefNum r <> putLength i getNumberedConstructorReference :: - (MonadGet m) => m (GConstructorReference RefNum) + (PrimBase m) => Get m (GConstructorReference RefNum) getNumberedConstructorReference = ConstructorReference <$> getRefNum <*> getLength putString :: String -> Builder putString = putFoldable (putVarInt . fromEnum) -getString :: (MonadGet m) => m String +getString :: (PrimBase m) => Get m String getString = getList (toEnum <$> getVarInt) putText :: Text -> Builder @@ -391,10 +373,10 @@ putText text = putLength (B.length bs) <> BU.byteString bs bs = encodeUtf8 text {-# INLINE putText #-} -getText :: (MonadGet m) => m Text +getText :: (PrimBase m) => Get m Text getText = do len <- getLength - bs <- B.copy <$> Ser.getBytes len + bs <- B.copy <$> getByteString len pure $ decodeUtf8 bs {-# INLINE getText #-} @@ -414,7 +396,7 @@ putRefNum :: RefNum -> Builder putRefNum (RefNum i) = putVarInt i {-# INLINE putRefNum #-} -getReference :: (MonadGet m) => m Reference +getReference :: (PrimBase m) => Get m Reference getReference = do tag <- getWord8 case tag of @@ -423,7 +405,7 @@ getReference = do _ -> unknownTag "Reference" tag {-# INLINE getReference #-} -getReferenceByNumber :: (MonadGet m) => Array Reference -> m Reference +getReferenceByNumber :: (PrimBase m) => Array Reference -> Get m Reference getReferenceByNumber refm = getVarInt >>= lookupRef refm {-# INLINE getReferenceByNumber #-} @@ -433,7 +415,7 @@ lookupRef arr i | otherwise = exn [] $ "lookupRef: index out of bounds: " ++ show i {-# INLINE lookupRef #-} -getRefNum :: (MonadGet m) => m RefNum +getRefNum :: (PrimBase m) => Get m RefNum getRefNum = RefNum <$> getVarInt {-# INLINE getRefNum #-} @@ -441,7 +423,7 @@ putConstructorReference :: ConstructorReference -> Builder putConstructorReference (ConstructorReference r i) = putReference r <> putLength i -getConstructorReference :: (MonadGet m) => m ConstructorReference +getConstructorReference :: (PrimBase m) => Get m ConstructorReference getConstructorReference = ConstructorReference <$> getReference <*> getLength diff --git a/unison-runtime/src/Unison/Runtime/Serialize/Get.hs b/unison-runtime/src/Unison/Runtime/Serialize/Get.hs new file mode 100644 index 0000000000..80f53c1d1c --- /dev/null +++ b/unison-runtime/src/Unison/Runtime/Serialize/Get.hs @@ -0,0 +1,324 @@ +module Unison.Runtime.Serialize.Get + ( Get (..), + GetExn (..), + getExnMsg, + PrimBase (..), + Ix (..), + evaluated, + getByteString, + getWord8, + getVarInt, + getInt64be, + getWord16be, + getWord32be, + getWord64be, + getWord64le, + getDoublebe, + getFloatbe, + getAccumulating, + getAccumulatingRevList, + getArray, + getList, + getSeq, + getPrimArray, + remaining, + runGet, + runGetCatch, + runGetCatchIO, + ) +where + +import Control.Exception +import Control.Monad (replicateM) +import Control.Monad.Primitive +import Control.Monad.ST +import Control.Monad.Trans (MonadTrans (..)) +import Data.Bifunctor (first) +import Data.Bits +import Data.ByteString as BS +import Data.ByteString.Unsafe qualified as BS +import Data.Int +import Data.Primitive.Array +import Data.Primitive.PrimArray +import Data.Primitive.PrimVar +import Data.Primitive.Types +import Data.Sequence qualified as Seq +import Data.Word + +-- TODO: replace with GHC builtins after upgrading to GHC 9.10 +foreign import ccall unisonWord32ToFloat :: Word32 -> Float + +foreign import ccall unisonWord64ToDouble :: Word64 -> Double + +newtype Ix m = Ix (PrimVar (PrimState m) Int) + +newtype Get m a = Get {unGet :: ByteString -> Ix m -> m a} + +runGet :: (PrimBase m) => Get m a -> ByteString -> m a +runGet (Get k) bs = newPrimVar 0 >>= k bs . Ix +{-# SPECIALIZE runGet :: Get IO a -> ByteString -> IO a #-} +{-# SPECIALIZE runGet :: Get (ST s) a -> ByteString -> ST s a #-} + +runGetCatchIO :: Get IO a -> ByteString -> IO (Either String a) +runGetCatchIO g bs = fmap (first getExnMsg) . try $ runGet g bs + +-- This might be somewhat unsafe. It uses IO facilities to catch +-- exceptions in an ST-like monad. When in doubt, prefer `runGetCatchIO`. +runGetCatch :: + (PrimBase m) => Get m a -> ByteString -> m (Either String a) +runGetCatch g bs = + unsafeIOToPrim + . fmap (first getExnMsg) + . try + . unsafePrimToIO + $ runGet g bs +-- runGetCatch @IO should just be safer to run as runGetCatchIO +{-# NOINLINE [1] runGetCatch #-} + +{-# RULES "runGetCatch/IO" runGetCatch = runGetCatchIO #-} + +evaluated :: (PrimBase m) => a -> Get m a +evaluated x = Get \_ _ -> evalPrim x +{-# INLINE evaluated #-} + +data GetExn + = InsufficientBytes String + | UserExn String + deriving (Show) + +getExnMsg :: GetExn -> String +getExnMsg (InsufficientBytes name) = "insufficient bytes in " ++ name +getExnMsg (UserExn msg) = msg + +instance Exception GetExn + +instance (Functor m) => Functor (Get m) where + fmap f (Get k) = Get \b i -> fmap f (k b i) + {-# INLINE fmap #-} + x <$ Get k = Get \b i -> x <$ k b i + {-# INLINE (<$) #-} + +instance (Applicative m) => Applicative (Get m) where + pure x = Get \_ _ -> pure x + {-# INLINE pure #-} + Get kf <*> Get kx = Get \b i -> kf b i <*> kx b i + {-# INLINE (<*>) #-} + liftA2 f (Get kx) (Get ky) = Get \b i -> liftA2 f (kx b i) (ky b i) + {-# INLINE liftA2 #-} + Get kx *> Get ky = Get \b i -> kx b i *> ky b i + {-# INLINE (*>) #-} + Get kx <* Get ky = Get \b i -> kx b i <* ky b i + {-# INLINE (<*) #-} + +instance (Monad m) => Monad (Get m) where + Get k >>= f = Get \b i -> k b i >>= \x -> unGet (f x) b i + {-# INLINE (>>=) #-} + (>>) = (*>) + {-# INLINE (>>) #-} + +instance (Monad m) => MonadFail (Get m) where + fail s = throw $ UserExn s + +instance MonadTrans Get where + lift m = Get \_ _ -> m + {-# INLINE lift #-} + +remaining :: (PrimBase m) => Get m Int +remaining = Get \bs (Ix ix) -> f bs <$> readPrimVar ix + where + f bs i = BS.length bs - i +{-# INLINEABLE remaining #-} + +getWord8 :: (PrimBase m) => Get m Word8 +getWord8 = Get \bs (Ix ix) -> + readPrimVar ix >>= \case + i + | i < BS.length bs -> + BS.unsafeIndex bs i <$ writePrimVar ix (i + 1) + | otherwise -> throw $ InsufficientBytes "getWord8" +{-# INLINE getWord8 #-} + +getVarInt :: (Bits int, Num int, Ord int, PrimBase m) => Get m int +getVarInt = Get \bs (Ix ix) -> readPrimVar ix >>= buildVarInt bs ix +{-# INLINEABLE getVarInt #-} + +buildVarInt :: + (Bits int, Num int, Ord int, PrimBase m) => + ByteString -> + PrimVar (PrimState m) Int -> + Int -> + m int +buildVarInt bs ix i0 + | i0 < sz = eat (i0 + 1) 0 0 $ grab i0 + | otherwise = throw $ InsufficientBytes "getVarInt" + where + sz = BS.length bs + grab j = fromIntegral $ BS.unsafeIndex bs j + + eat !i !acc !sh !m + | not $ testBit m 7, + acc <- acc .|. (m !<<. sh) = + acc <$ writePrimVar ix i + | i < sz, + acc <- acc .|. (clearBit m 7 !<<. sh) = + eat (i + 1) acc (sh + 7) $ grab i + | otherwise = + throw $ InsufficientBytes "getVarInt" +{-# INLINEABLE buildVarInt #-} + +getInt64be :: (PrimBase m) => Get m Int64 +getInt64be = Get \bs (Ix ix) -> + readPrimVar ix >>= \case + i + | i + 7 < BS.length bs -> build bs i <$ writePrimVar ix (i + 8) + | otherwise -> throw $ InsufficientBytes "getInt64be" + where + build bs !i = + (fromIntegral (BS.unsafeIndex bs i) !<<. 56) + .|. (fromIntegral (BS.unsafeIndex bs (i + 1)) !<<. 48) + .|. (fromIntegral (BS.unsafeIndex bs (i + 2)) !<<. 40) + .|. (fromIntegral (BS.unsafeIndex bs (i + 3)) !<<. 32) + .|. (fromIntegral (BS.unsafeIndex bs (i + 4)) !<<. 24) + .|. (fromIntegral (BS.unsafeIndex bs (i + 5)) !<<. 16) + .|. (fromIntegral (BS.unsafeIndex bs (i + 6)) !<<. 8) + .|. (fromIntegral (BS.unsafeIndex bs (i + 7))) +{-# SPECIALIZE getInt64be :: Get IO Int64 #-} +{-# SPECIALIZE getInt64be :: Get (ST s) Int64 #-} + +getWord16be :: (PrimBase m) => Get m Word16 +getWord16be = Get \bs (Ix ix) -> + readPrimVar ix >>= \case + i + | i + 1 < BS.length bs -> build bs i <$ writePrimVar ix (i + 2) + | otherwise -> throw $ InsufficientBytes "getWord16be" + where + build bs !i = + (fromIntegral (BS.unsafeIndex bs i) !<<. 8) + .|. (fromIntegral (BS.unsafeIndex bs (i + 1))) +{-# SPECIALIZE getWord16be :: Get IO Word16 #-} +{-# SPECIALIZE getWord16be :: Get (ST s) Word16 #-} + +getWord32be :: (PrimBase m) => Get m Word32 +getWord32be = Get \bs (Ix ix) -> + readPrimVar ix >>= \case + i + | i + 3 < BS.length bs -> build bs i <$ writePrimVar ix (i + 4) + | otherwise -> throw $ InsufficientBytes "getWord32be" + where + build bs !i = + (fromIntegral (BS.unsafeIndex bs i) !<<. 24) + .|. (fromIntegral (BS.unsafeIndex bs (i + 1)) !<<. 16) + .|. (fromIntegral (BS.unsafeIndex bs (i + 2)) !<<. 8) + .|. (fromIntegral (BS.unsafeIndex bs (i + 3))) +{-# SPECIALIZE getWord32be :: Get IO Word32 #-} +{-# SPECIALIZE getWord32be :: Get (ST s) Word32 #-} + +getWord64be :: (PrimBase m) => Get m Word64 +getWord64be = Get \bs (Ix ix) -> + readPrimVar ix >>= \case + i + | i + 7 < BS.length bs -> build bs i <$ writePrimVar ix (i + 8) + | otherwise -> throw $ InsufficientBytes "getWord64be" + where + build bs !i = + (fromIntegral (BS.unsafeIndex bs i) !<<. 56) + .|. (fromIntegral (BS.unsafeIndex bs (i + 1)) !<<. 48) + .|. (fromIntegral (BS.unsafeIndex bs (i + 2)) !<<. 40) + .|. (fromIntegral (BS.unsafeIndex bs (i + 3)) !<<. 32) + .|. (fromIntegral (BS.unsafeIndex bs (i + 4)) !<<. 24) + .|. (fromIntegral (BS.unsafeIndex bs (i + 5)) !<<. 16) + .|. (fromIntegral (BS.unsafeIndex bs (i + 6)) !<<. 8) + .|. (fromIntegral (BS.unsafeIndex bs (i + 7))) +{-# SPECIALIZE getWord64be :: Get IO Word64 #-} +{-# SPECIALIZE getWord64be :: Get (ST s) Word64 #-} + +getWord64le :: (PrimBase m) => Get m Word64 +getWord64le = Get \bs (Ix ix) -> + readPrimVar ix >>= \case + i + | i + 7 < BS.length bs -> build bs i <$ writePrimVar ix (i + 8) + | otherwise -> throw $ InsufficientBytes "getWord64be" + where + build bs !i = + (fromIntegral (BS.unsafeIndex bs (i + 7)) !<<. 56) + .|. (fromIntegral (BS.unsafeIndex bs (i + 6)) !<<. 48) + .|. (fromIntegral (BS.unsafeIndex bs (i + 5)) !<<. 40) + .|. (fromIntegral (BS.unsafeIndex bs (i + 4)) !<<. 32) + .|. (fromIntegral (BS.unsafeIndex bs (i + 3)) !<<. 24) + .|. (fromIntegral (BS.unsafeIndex bs (i + 2)) !<<. 16) + .|. (fromIntegral (BS.unsafeIndex bs (i + 1)) !<<. 8) + .|. (fromIntegral (BS.unsafeIndex bs i)) +{-# SPECIALIZE getWord64le :: Get IO Word64 #-} +{-# SPECIALIZE getWord64le :: Get (ST s) Word64 #-} + +getFloatbe :: (PrimBase m) => Get m Float +getFloatbe = unisonWord32ToFloat <$> getWord32be +{-# INLINEABLE getFloatbe #-} + +getDoublebe :: (PrimBase m) => Get m Double +getDoublebe = unisonWord64ToDouble <$> getWord64be +{-# INLINEABLE getDoublebe #-} + +getByteString :: (PrimBase m) => Int -> Get m ByteString +getByteString n = Get \bs (Ix ix) -> do + i <- readPrimVar ix + if i + n <= BS.length bs + then BS.unsafeTake n (BS.unsafeDrop i bs) <$ writePrimVar ix (i + n) + else throw $ InsufficientBytes "getBytes" +{-# INLINEABLE getByteString #-} + +getList :: (PrimBase m) => Get m a -> Get m [a] +getList ga = getVarInt >>= (`replicateM` ga) +{-# INLINE getList #-} + +-- Builds a result by repeated snoc in an efficient loop. Should only be +-- used when the snoc is efficient. +getAccumulating :: + (PrimBase m) => + s -> + (s -> a -> s) -> + (s -> r) -> + Get m a -> + Get m r +getAccumulating nil snoc finish = \ga -> Get \bs ix -> + let loop !as (n :: Int) + | n <= 0 = evalPrim $ finish as + | otherwise = unGet ga bs ix >>= \a -> loop (snoc as a) (n - 1) + in unGet getVarInt bs ix >>= loop nil +{-# INLINE getAccumulating #-} + +getAccumulatingRevList :: + (PrimBase m) => + ([a] -> r) -> + Get m a -> + Get m r +getAccumulatingRevList finish = \ga -> Get \bs ix -> + let loop as (n :: Int) + | n <= 0 = evalPrim $ finish as + | otherwise = unGet ga bs ix >>= \a -> loop (a : as) (n - 1) + in unGet getVarInt bs ix >>= loop [] +{-# INLINE getAccumulatingRevList #-} + +getSeq :: (PrimBase m) => Get m a -> Get m (Seq.Seq a) +getSeq ga = getAccumulating mempty (Seq.|>) id ga +{-# INLINEABLE getSeq #-} + +getArray :: (PrimBase m) => Get m a -> Get m (Array a) +getArray ga = Get \bs ix -> do + sz <- unGet getVarInt bs ix + dst <- newArray sz (error "getArray: bad element") + let fill i + | i < sz = unGet ga bs ix >>= writeArray dst i >> fill (i + 1) + | otherwise = unsafeFreezeArray dst + fill 0 +{-# INLINE getArray #-} + +getPrimArray :: (PrimBase m, Prim a) => Get m a -> Get m (PrimArray a) +getPrimArray ga = Get \bs ix -> do + sz <- unGet getVarInt bs ix + dst <- newPrimArray sz + let fill i + | i < sz = unGet ga bs ix >>= writePrimArray dst i >> fill (i + 1) + | otherwise = unsafeFreezePrimArray dst + fill 0 +{-# INLINE getPrimArray #-} diff --git a/unison-runtime/tests/Unison/Test/Runtime/ANF/Serialization.hs b/unison-runtime/tests/Unison/Test/Runtime/ANF/Serialization.hs index ca6812fc03..279b04f0ce 100644 --- a/unison-runtime/tests/Unison/Test/Runtime/ANF/Serialization.hs +++ b/unison-runtime/tests/Unison/Test/Runtime/ANF/Serialization.hs @@ -3,16 +3,14 @@ -- | Round trip tests for ANF serialization. module Unison.Test.Runtime.ANF.Serialization (Unison.Test.Runtime.ANF.Serialization.test) where -import Control.Monad.Reader (runReaderT) +import Control.Monad.ST (ST, runST) import Data.ByteString.Builder (Builder, toLazyByteString) import Data.ByteString.Lazy (toStrict) -import Data.Bytes.Get (runGetS) import Data.Primitive.Array (Array) import Data.Primitive.Array qualified as Array import Data.Primitive.ByteArray (ByteArray) import Data.Primitive.ByteArray qualified as ByteArray import Data.Primitive.Types (Prim) -import Data.Serialize.Get (Get) import EasyTest qualified as EasyTest import Hedgehog hiding (Rec, Test, test) import Hedgehog.Gen qualified as Gen @@ -21,6 +19,7 @@ import Unison.Prelude import Unison.Reference (Reference) import Unison.Runtime.ANF import Unison.Runtime.ANF.Serialize +import Unison.Runtime.Serialize.Get import Unison.Test.Gen import Unison.Util.Bytes qualified as Util.Bytes @@ -95,15 +94,20 @@ genValue = Gen.sized \n -> do valueRoundtrip :: Property valueRoundtrip = - getPutRoundtrip (runReaderT getValue . (,False)) putValue genValue + getPutRoundtrip (getValue . (,False)) putValue genValue -getPutRoundtrip :: (Eq a, Show a) => (Version -> Get a) -> (Version -> a -> Builder) -> Gen a -> Property +getPutRoundtrip :: + (Eq a, Show a) => + (forall s. Version -> Get (ST s) a) -> + (Version -> a -> Builder) -> + Gen a -> + Property getPutRoundtrip get put builder = property $ do v <- forAll builder version <- forAll versionToTest let bytes = toStrict . toLazyByteString $ put version v - runGetS (get version) bytes === Right v + runST (runGetCatch (get version) bytes) === Right v where versionToTest = do Gen.choice diff --git a/unison-runtime/tests/Unison/Test/Runtime/MCode/Serialization.hs b/unison-runtime/tests/Unison/Test/Runtime/MCode/Serialization.hs index a3ad56347d..43d0f57df1 100644 --- a/unison-runtime/tests/Unison/Test/Runtime/MCode/Serialization.hs +++ b/unison-runtime/tests/Unison/Test/Runtime/MCode/Serialization.hs @@ -3,11 +3,10 @@ -- | Round trip tests runtime serialization module Unison.Test.Runtime.MCode.Serialization (Unison.Test.Runtime.MCode.Serialization.test) where +import Control.Monad.ST (ST, runST) import Data.ByteString.Builder (Builder, toLazyByteString) import Data.ByteString.Lazy (toStrict) -import Data.Bytes.Get (runGetS) import Data.Primitive (Prim, PrimArray, primArrayFromList) -import Data.Serialize.Get (Get) import EasyTest qualified as EasyTest import Hedgehog hiding (Rec, Test, test) import Hedgehog.Gen qualified as Gen @@ -17,6 +16,7 @@ import Unison.Runtime.Foreign.Function.Type (ForeignFunc) import Unison.Runtime.Interface import Unison.Runtime.MCode (Args (..), Branch, Comb, CombIx (..), GBranch (..), GComb (..), GCombInfo (..), GInstr (..), GRef (..), GSection (..), Instr, MLit (..), Prim1, Prim2, Ref, Section) import Unison.Runtime.Machine (Combs) +import Unison.Runtime.Serialize.Get import Unison.Runtime.TypeTags (PackedTag (..)) import Unison.Test.Gen import Unison.Util.EnumContainers (EnumMap, EnumSet) @@ -191,9 +191,14 @@ sCacheRoundtrip :: Property sCacheRoundtrip = getPutRoundtrip getStoredCache (putStoredCache) genStoredCache -getPutRoundtrip :: (Eq a, Show a) => Get a -> (a -> Builder) -> Gen a -> Property +getPutRoundtrip :: + (Eq a, Show a) => + (forall s. Get (ST s) a) -> + (a -> Builder) -> + Gen a -> + Property getPutRoundtrip get put builder = property $ do v <- forAll builder let bytes = toStrict . toLazyByteString $ put v - runGetS get bytes === Right v + runST (runGetCatch get bytes) === Right v diff --git a/unison-runtime/unison-runtime.cabal b/unison-runtime/unison-runtime.cabal index 5c3303c048..93ddc20c86 100644 --- a/unison-runtime/unison-runtime.cabal +++ b/unison-runtime/unison-runtime.cabal @@ -73,12 +73,15 @@ library Unison.Runtime.Profiling Unison.Runtime.Referenced Unison.Runtime.Serialize + Unison.Runtime.Serialize.Get Unison.Runtime.SparseVector Unison.Runtime.Stack Unison.Runtime.TypeTags Unison.Runtime.Vector hs-source-dirs: src + c-sources: + cbits/i2d.c default-extensions: ApplicativeDo BangPatterns @@ -119,7 +122,6 @@ library , binary , bytes , bytestring - , cereal , clock , containers >=0.6.3 , crypton-x509 @@ -232,9 +234,7 @@ test-suite runtime-tests ghc-options: -fmax-worker-args=100 -Wall -funbox-strict-fields -O2 -W -threaded -rtsopts "-with-rtsopts=-N -T" -v0 build-depends: base - , bytes , bytestring - , cereal , code-page , containers , cryptonite diff --git a/weeder.toml b/weeder.toml index 2a33919010..04cc851ede 100644 --- a/weeder.toml +++ b/weeder.toml @@ -525,6 +525,12 @@ roots = [ '''^Unison\.Runtime\.Serialize\.putReferenceByNumber$''', '''^Unison\.Runtime\.Serialize\.getReferenceByNumber$''', '''^Unison\.Runtime\.Serialize\.lookupRef$''', + '''^Unison\.Runtime\.ANF\.Serialize\.DeserialIO$''', + '''^Unison\.Runtime\.ANF\.Serialize\.DeserialST$''', + '''^Unison\.Runtime\.Serialize\.Get\.getWord64le$''', + '''^Unison\.Runtime\.Serialize\.Get\.getFloatbe$''', + '''^Unison\.Runtime\.Serialize\.Get\.evaluated$''', + '''^Unison\.Runtime\.Serialize\.Get\.getPrimArray$''', '''^Unison\.Runtime\.SparseVector\.map$''', '''^Unison\.Runtime\.SparseVector\.mask$''', '''^Unison\.Runtime\.SparseVector\.zipWith$''',