diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2012 Dennis Felsing
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. 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.
+
+3. Neither the name of the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,3 @@
+module Main where
+import Distribution.Simple
+main = defaultMain
diff --git a/benchmarks/Main.hs b/benchmarks/Main.hs
new file mode 100644
--- /dev/null
+++ b/benchmarks/Main.hs
@@ -0,0 +1,55 @@
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE RecursiveDo #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveAnyClass #-}
+
+import Criterion.Main
+import Criterion.Types
+import HeapSize
+import GHC.Generics (Generic)
+import Data.Primitive.ByteArray
+import Data.IORef
+import Control.DeepSeq
+
+listBenchmark :: Benchmark
+listBenchmark = bgroup "[Int]" $
+  map mkBenchmark
+    [ 1000
+    , 10000
+    , 100000
+    , 1000000
+    ]
+  where
+    mkBenchmark :: Int -> Benchmark
+    mkBenchmark size = env (pure [0 .. size]) $ \lst -> bench (show size) $ nfAppIO recursiveSize lst
+
+byteArrayBenchmark :: Benchmark
+byteArrayBenchmark = bgroup "ByteArray#" $
+  -- Just to make sure that it's O(1)
+  map mkBenchmark [10, 10000]
+  where
+    mkBenchmark :: Int -> Benchmark
+    mkBenchmark size = env
+      (newByteArray size)
+      (\lst -> bench (show size) $ nfAppIO recursiveSize lst)
+
+
+data C = C Int (IORef C)
+  deriving (Generic, NFData)
+
+circularBenchmark :: Benchmark
+circularBenchmark = env mkT (\c -> bench "Circular" (nfAppIO recursiveSize c))
+  where
+    mkT :: IO C
+    mkT = do
+      rec first <- newIORef (C 1 second)
+          second <- newIORef (C 2 third)
+          third <- newIORef (C 3 first)
+      readIORef first
+
+
+main :: IO ()
+main =
+  defaultMainWith
+    (defaultConfig {reportFile = Just "ghc-datasize-benchmark.html"})
+    [ listBenchmark, byteArrayBenchmark, circularBenchmark ]
diff --git a/cbits/heapsize_prim.cmm b/cbits/heapsize_prim.cmm
new file mode 100644
--- /dev/null
+++ b/cbits/heapsize_prim.cmm
@@ -0,0 +1,15 @@
+#include "Cmm.h"
+
+unpackClosurePtrs ( P_ closure )
+{
+    W_ ptrArray;
+    ("ptr" ptrArray) = foreign "C" heap_view_closurePtrs(MyCapability() "ptr", UNTAG(closure) "ptr");
+    return (ptrArray);
+}
+
+closureSize ( P_ closure )
+{
+    W_ len;
+    (len) = foreign "C" heap_view_closureSize(UNTAG(closure) "ptr");
+    return (len);
+}
diff --git a/heapsize.cabal b/heapsize.cabal
new file mode 100644
--- /dev/null
+++ b/heapsize.cabal
@@ -0,0 +1,47 @@
+cabal-version:      3.0
+name:               heapsize
+version:            0.1
+license:            BSD-3-Clause
+license-file:       LICENSE
+category:           GHC, Debug, Development
+build-type:         Simple
+author:             Michail Pardalos <mpardalos@gmail.com>
+maintainer:         Michail Pardalos <mpardalos@gmail.com>
+copyright:          Michail Pardalos 2020
+synopsis:           Determine the size of runtime data structures
+description:        heapsize is a tool to determine the size data structures.
+                    Determining the size of recursive data structures is
+                    supported. All sizes are in Bytes.
+
+Library
+  Exposed-modules:
+    HeapSize
+  c-sources:
+    cbits/heapsize_prim.cmm
+  Default-Language: Haskell2010
+  Build-depends:
+      base >= 4.12 && < 4.15
+    , deepseq >= 1.3 && < 1.5
+    , ghc-heap >= 8.6
+    , unordered-containers ==0.2.*
+    , hashable ==1.3.*
+    , primitive
+  Hs-source-dirs: src/
+  Ghc-options: -Wall
+
+benchmark benchmarks
+  build-depends:
+      base
+    , heapsize
+    , criterion
+    , primitive
+    , deepseq
+  default-language: Haskell2010
+  ghc-options:      -Wall
+  hs-source-dirs:   benchmarks
+  main-is:          Main.hs
+  type:             exitcode-stdio-1.0
+
+source-repository head
+    type:     git
+    location: https://github.com/mpardalos/heapsize.git
diff --git a/src/HeapSize.hs b/src/HeapSize.hs
new file mode 100644
--- /dev/null
+++ b/src/HeapSize.hs
@@ -0,0 +1,120 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE UnboxedTuples #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE GHCForeignImportPrim #-}
+{-# LANGUAGE UnliftedFFITypes #-}
+
+{- |
+   Module      : HeapSize
+   Copyright   : (c) Michail Pardalos
+   License     : 3-Clause BSD-style
+   Maintainer  : mpardalos@gmail.com
+
+   Based on GHC.Datasize by Dennis Felsing
+ -}
+module HeapSize (
+  recursiveSize,
+  recursiveSizeNoGC,
+  recursiveSizeNF,
+  closureSize
+  )
+  where
+
+import Control.DeepSeq (NFData, force)
+
+import GHC.Exts hiding (closureSize#)
+import GHC.Arr
+import GHC.Exts.Heap hiding (size)
+import qualified Data.HashSet as H
+import Data.IORef
+import Data.Hashable
+
+import Control.Monad
+
+import System.Mem
+
+foreign import prim "aToWordzh" aToWord# :: Any -> Word#
+foreign import prim "unpackClosurePtrs" unpackClosurePtrs# :: Any -> Array# b
+foreign import prim "closureSize" closureSize# :: Any -> Int#
+
+-- | Get the *non-recursive* size of an closure in words
+closureSize :: a -> IO Int
+closureSize x = return (I# (closureSize# (unsafeCoerce# x)))
+
+getClosures :: a -> IO (Array Int Box)
+getClosures x = case unpackClosurePtrs# (unsafeCoerce# x) of
+    pointers ->
+      let nelems = I# (sizeofArray# pointers)
+      in pure (fmap Box $ Array 0 (nelems - 1) nelems pointers)
+
+-- | Calculate the recursive size of GHC objects in Bytes. Note that the actual
+--   size in memory is calculated, so shared values are only counted once.
+--
+--   Call with
+--   @
+--    recursiveSize $! 2
+--   @
+--   to force evaluation to WHNF before calculating the size.
+--
+--   Call with
+--   @
+--    recursiveSize $!! \"foobar\"
+--   @
+--   ($!! from Control.DeepSeq) to force full evaluation before calculating the
+--   size.
+--
+--   A garbage collection is performed before the size is calculated, because
+--   the garbage collector would make heap walks difficult.
+--
+--   This function works very quickly on small data structures, but can be slow
+--   on large and complex ones. If speed is an issue it's probably possible to
+--   get the exact size of a small portion of the data structure and then
+--   estimate the total size from that.
+recursiveSize :: a -> IO Int
+recursiveSize x = performGC >> recursiveSizeNoGC x
+
+-- | Same as `recursiveSize` except without performing garbage collection first.
+--   Useful if you want to measure the size of many objects in sequence. You can
+--   call `performGC` once at first and then use this function to avoid multiple
+--   unnecessary garbage collections.
+recursiveSizeNoGC :: a -> IO Int
+recursiveSizeNoGC x = do
+  state <- newIORef (0, H.empty)
+  go state (asBox x)
+
+  fst <$> readIORef state
+  where
+    go :: IORef (Int, H.HashSet HashableBox) -> Box -> IO ()
+    go state b@(Box y) = do
+      (_, closuresSeen) <- readIORef state
+
+      when (not $ H.member (HashableBox b) closuresSeen) $ do
+        thisSize <- closureSize y
+        modifyIORef state $ \(size, _) ->
+          (size + thisSize, H.insert (HashableBox b) closuresSeen)
+
+        mapM_ (go state) =<< getClosures y
+
+-- | Calculate the recursive size of GHC objects in Bytes after calling
+-- Control.DeepSeq.force on the data structure to force it into Normal Form.
+-- Using this function requires that the data structure has an `NFData`
+-- typeclass instance.
+
+recursiveSizeNF :: NFData a => a -> IO Int
+recursiveSizeNF = recursiveSize . force
+
+newtype HashableBox = HashableBox Box
+    deriving newtype Show
+
+-- | Pointer Equality
+instance Eq HashableBox where
+    (HashableBox (Box a1)) == (HashableBox (Box a2)) =
+        W# (aToWord# a1) == W# (aToWord# a2)
+
+-- | Pointer hash
+instance Hashable HashableBox where
+    hashWithSalt n (HashableBox (Box a)) = hashWithSalt n (W# (aToWord# a))
