packages feed

compact (empty) → 0.1.0.0

raw patch · 7 files changed

+277/−0 lines, 7 filesdep +basedep +binarydep +bytestringsetup-changed

Dependencies added: base, binary, bytestring, compact, directory, ghc-compact

Files

+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Revision history for compact++## 0.1.0.0  -- YYYY-mm-dd++* First version. Released on an unsuspecting world.
+ Data/Compact.hs view
@@ -0,0 +1,19 @@+module Data.Compact (+    -- * The 'Compact' type+    Compact,++    -- * Compacting data+    compact,+    compactWithSharing,+    compactAdd,+    compactAddWithSharing,+    compactSized,++    -- * Inspecting a Compact+    getCompact,+    inCompact,+    isCompact,+    compactSize,+) where++import GHC.Compact
+ Data/Compact/Serialize.hs view
@@ -0,0 +1,153 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Data.Compact.Serialize+    ( writeCompact, unsafeReadCompact+    , hPutCompact, hUnsafeGetCompact+    ) where++import Type.Reflection+import Control.Monad+import Data.Monoid+import Data.IORef+import Data.Word+import System.IO++import Foreign.Ptr+import Foreign.Storable+import Foreign.Marshal.Alloc++import qualified Data.Binary as B+import qualified Data.ByteString as BS+import qualified Data.ByteString.Unsafe as BSU+import qualified Data.ByteString.Lazy as BSL+import qualified Data.ByteString.Builder as BSB++import GHC.Compact+import GHC.Compact.Serialized++newtype CompactFile a = CompactFile (SerializedCompact a)++-- NB: This instance cannot be put on SerializedCompact as+-- ghc-compact does not have a binary dependency+instance (Typeable a) => B.Binary (CompactFile a) where+    get = do+        SomeTypeRep tyrep <- B.get+        case tyrep `eqTypeRep` typeRep @a of+          Just HRefl -> CompactFile <$> getSerializedCompact+          Nothing -> fail $+            "Data.Compact.Serialized: expected " ++ show (typeRep @a) +++            " but got " ++ show tyrep+    put (CompactFile a) = B.put (typeRep @a) >> putSerializedCompact a++putPtr :: Ptr a -> B.Put+putPtr = B.put @Word64 . fromIntegral . ptrToWordPtr++getPtr :: B.Get (Ptr a)+getPtr = wordPtrToPtr . fromIntegral <$> B.get @Word64++getList :: B.Get a -> B.Get [a]+getList getElem = do+    n <- B.get @Int+    replicateM n getElem++putList :: (a -> B.Put) -> [a] -> B.Put+putList putElem xs = do+    B.put @Int (length xs)+    mapM_ putElem xs++getSerializedCompact :: B.Get (SerializedCompact a)+getSerializedCompact = SerializedCompact <$> getList getBlock <*> getPtr+  where+    getBlock :: B.Get (Ptr a, Word)+    getBlock = (,) <$> getPtr <*> B.get++putSerializedCompact :: SerializedCompact a -> B.Put+putSerializedCompact (SerializedCompact a b) = putList putBlock a <> putPtr b+  where+    putBlock :: (Ptr a, Word) -> B.Put+    putBlock (ptr, len) = putPtr ptr <> B.put len++-- | Write a compact region to a file.  The resulting file can+-- be read back into memory using 'unsafeReadCompact'.+--+writeCompact :: Typeable a => FilePath -> Compact a -> IO ()+writeCompact fname compact =+    withFile fname WriteMode $ \h -> hPutCompact h compact++-- | Write a compact region to a 'Handle'.  The compact region+-- can be read out of the handle by using 'hUnsafeGetCompact'.+--+hPutCompact :: Typeable a => Handle -> Compact a -> IO ()+hPutCompact hdl compact =+    withSerializedCompact compact $ \scompact -> do+        let bs = B.encode $ CompactFile scompact+        -- By writing out the length of the metadata segment, we+        -- can read out a single word, read out the metadata segment,+        -- and then immediately start blasting further data from+        -- the handle into the memory region where the compact+        -- is going to go.  Otherwise, we have to indirect through+        -- a lazy bytestring which has a cost.+        hPutStorable hdl (fromIntegral (BSL.length bs) :: Int)+        BSL.hPut hdl bs+        let putBlock (ptr, len) = hPutBuf hdl ptr (fromIntegral len)+        mapM_ putBlock (serializedCompactBlockList scompact)++-- | Read out a compact region that was serialized to a file.+-- See 'hUnsafeGetCompact' for safety considerations when using this function.+--+unsafeReadCompact :: Typeable a => FilePath -> IO (Either String (Compact a))+unsafeReadCompact fname =+    withFile fname ReadMode $ \hdl -> hUnsafeGetCompact hdl++-- | Read out a compact region from a handle.+--+-- Compact regions written to handles this way are subject to some+-- restrictions:+--+--  * Our binary representation contains direct pointers to the info+--    tables of objects in the region.  This means that the info tables+--    of the receiving process must be laid out in exactly the same+--    way as from the original process; in practice, this means using+--    static linking, using the exact same binary and turning off ASLR.  This+--    API does NOT do any safety checking and will probably segfault if you+--    get it wrong.  DO NOT run this on untrusted input.+--+--  * You must read out the value at the correct type.  We will+--    check this for you and raise an error if the types do not match+--    (this is what the 'Typeable' constraint is for).+--+hUnsafeGetCompact+    :: forall a. Typeable a+    => Handle -> IO (Either String (Compact a))+hUnsafeGetCompact hdl = do+    l <- hGetStorable hdl+    mbs <- BSL.hGet hdl (l :: Int)+    case B.decodeOrFail @(CompactFile a) mbs of+      Left (_, _, err) -> return $ Left err+      Right (rest, _, CompactFile scompact)+        | not (BSL.null rest) -> return . Left+            $ "Had " ++ show (BSL.length rest) ++ " bytes of leftover metadata"+        | otherwise  -> do+          res <- importCompact scompact $ \ptr l ->+                    void $ hGetBuf hdl ptr (fromIntegral l)+          case res of+            Nothing -> return $ Left "Failed to import compact"+            Just compact -> return $ Right compact++hPutStorable :: forall a. Storable a => Handle -> a -> IO ()+hPutStorable h a =+    alloca $ \ptr -> do+        poke ptr a+        hPutBuf h ptr (sizeOf (undefined :: a))++hGetStorable :: forall a. Storable a => Handle -> IO a+hGetStorable h =+    alloca $ \ptr -> do+        hGetBuf h ptr (sizeOf (undefined :: a))+        peek ptr
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2017, Edward Z. Yang++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Edward Z. Yang nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ compact.cabal view
@@ -0,0 +1,42 @@+name:                compact+version:             0.1.0.0+synopsis:            Non-GC'd, contiguous storage for immutable data structures+description:+    This package provides user-facing APIs for working with+    "compact regions", which hold a fully evaluated Haskell object graph.+    These regions maintain the invariant that no pointers live inside the struct+    that point outside it, which ensures efficient garbage collection without+    ever reading the structure contents (effectively, it works as a manually+    managed "oldest generation" which is never freed until the whole is+    released).++    This package is currently highly experimental, but we hope it may+    be useful to some people.  It is GHC 8.2 only.  The bare-bones library+    that ships with GHC is ghc-compact.+license:             BSD3+license-file:        LICENSE+author:              Edward Z. Yang, Ben Gamari+maintainer:          ezyang@mit.edu, ben@smart-cactus.org+copyright:           (c) 2017 Edward Z. Yang, Ben Gamari+category:            Data+build-type:          Simple+extra-source-files:  ChangeLog.md+cabal-version:       >=1.10++library+  exposed-modules:     Data.Compact+                       Data.Compact.Serialize+  build-depends:       base       >= 4.10 && < 4.11,+                       ghc-compact,+                       -- TODO: SomeTypeRep instance is in unreleased version+                       -- of binary that is bundled with GHC dev branch+                       binary     >= 0.8.4.1 && < 0.9,+                       bytestring >= 0.10 && < 0.11+  default-language:    Haskell2010++test-suite compact-test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      tests+  main-is:             compact-test.hs+  build-depends:       compact, base, directory+  default-language:    Haskell2010
+ tests/compact-test.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE TypeApplications #-}++import Data.Compact+import Data.Compact.Serialize+import Control.Exception+import System.IO+import System.Directory+import System.Mem+import Control.Monad++main = do+    tmp <- getTemporaryDirectory+    let val = ("hello", 1, 42, 42, Just 42) :: (String, Int, Int, Integer, Maybe Int)+    c <- compact val+    (fp, h) <- openTempFile tmp "compact.bin"+    hPutCompact h c+    hClose h+    performMajorGC+    r <- unsafeReadCompact @(String, Int, Int, Integer, Maybe Int) fp+    c' <- case r of+            Left err -> fail err+            Right x -> return x+    removeFile fp+    print (getCompact c')+    when (val /= getCompact c') $ fail "did not match"+    putStrLn "OK"