{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_HADDOCK hide #-} module PopKey.Encoding where import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS import qualified Data.Store as S import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.Encoding as LT import PopKey.Internal2 -- for instance decls only import Data.Functor.Const import Data.Functor.Identity import Data.Graph (Graph) import Data.IntMap (IntMap) import Data.IntSet (IntSet) import Data.Map (Map) import Data.Proxy import Data.Ratio import Data.Semigroup import Data.Sequence (Seq) import Data.Set (Set) -- import Data.Tree (Tree) - no store instance available import GHC.Generics import GHC.Natural import GHC.Int import GHC.Word -- | A simple wrapper to declare you do not want this data to be granularly partitioned by poppy. newtype StoreBlob a = StoreBlob { unStoreBlob :: a } deriving (Generic,Eq,Ord,Show,Bounded) deriving newtype Enum -- | Inverse law: @pkDecode . pkEncode = id@. Note that this encoding is explicitly for use with poppy - use your discretion (or better, test!) to decide the granularity with which you wish to use this encoding as opposed to the standard store encoding. Relying more on PopKeyEncoding will probably use less space, but at the cost of storing items in less contiguous memory. class PopKeyEncoding a where type Shape a type Shape a = GShape (Rep a) shape :: I (Shape a) default shape :: (GPopKeyEncoding a (Rep a) , GShape (Rep a) ~ Shape a) => I (Shape a) shape = gshape @a @(Rep a) pkEncode :: a -> F' (Shape a) BS.ByteString default pkEncode :: (Generic a , GPopKeyEncoding a (Rep a) , GShape (Rep a) ~ Shape a) => a -> F' (Shape a) BS.ByteString pkEncode = gpkEncode @a @(Rep a) . from pkDecode :: F' (Shape a) BS.ByteString -> a default pkDecode :: (Generic a , GPopKeyEncoding a (Rep a) , GShape (Rep a) ~ Shape a) => F' (Shape a) BS.ByteString -> a pkDecode = to . gpkDecode @a @(Rep a) class GPopKeyEncoding s f where type GShape f gshape :: I (GShape f) gpkEncode :: f a -> F' (GShape f) BS.ByteString gpkDecode :: F' (GShape f) BS.ByteString -> f a instance GPopKeyEncoding s U1 where type GShape U1 = () {-# INLINE gshape #-} gshape = ISingle {-# INLINE gpkEncode #-} gpkEncode = const (Single' mempty) {-# INLINE gpkDecode #-} gpkDecode = const U1 instance PopKeyEncoding a => GPopKeyEncoding s (K1 i a) where type GShape (K1 i a) = Shape a {-# INLINE gshape #-} gshape = shape @a {-# INLINE gpkEncode #-} gpkEncode (K1 x) = pkEncode x {-# INLINE gpkDecode #-} gpkDecode = K1 . pkDecode instance (GPopKeyEncoding s a , GPopKeyEncoding s b) => GPopKeyEncoding s (a :*: b) where type GShape (a :*: b) = (GShape a , GShape b) {-# INLINE gshape #-} gshape = IProd (gshape @s @a) (gshape @s @b) {-# INLINE gpkEncode #-} gpkEncode (a :*: b) = Prod' (gpkEncode @s a) (gpkEncode @s b) {-# INLINE gpkDecode #-} gpkDecode (Prod' a b) = gpkDecode @s a :*: gpkDecode @s b instance (GPopKeyEncoding s a , GPopKeyEncoding s b) => GPopKeyEncoding s (a :+: b) where type GShape (a :+: b) = Either (GShape a) (GShape b) {-# INLINE gshape #-} gshape = ISum (gshape @s @a) (gshape @s @b) {-# INLINE gpkEncode #-} gpkEncode (L1 x) = Sum' (Left (gpkEncode @s x)) gpkEncode (R1 x) = Sum' (Right (gpkEncode @s x)) {-# INLINE gpkDecode #-} gpkDecode (Sum' x) = case x of Left l -> L1 (gpkDecode @s l) Right r -> R1 (gpkDecode @s r) instance GPopKeyEncoding s f => GPopKeyEncoding s (M1 i t f) where type GShape (M1 i t f) = GShape f {-# INLINE gshape #-} gshape = gshape @s @f {-# INLINE gpkEncode #-} gpkEncode (M1 x) = gpkEncode @s x {-# INLINE gpkDecode #-} gpkDecode = M1 . gpkDecode @s --------------- -- INSTANCES -- --------------- instance S.Store a => PopKeyEncoding (StoreBlob a) where type Shape (StoreBlob a) = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode . unStoreBlob {-# INLINE pkDecode #-} pkDecode (Single' x) = StoreBlob do S.decodeEx x instance PopKeyEncoding BS.ByteString where type Shape BS.ByteString = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' {-# INLINE pkDecode #-} pkDecode (Single' x) = x instance PopKeyEncoding LBS.ByteString where type Shape LBS.ByteString = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . LBS.toStrict {-# INLINE pkDecode #-} pkDecode (Single' x) = LBS.fromStrict x instance S.Store a => PopKeyEncoding [ a ] where type Shape [ a ] = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = case S.size @a of S.ConstSize _ -> Single' . BS.concat . fmap S.encode _ -> Single' . S.encode {-# INLINE pkDecode #-} pkDecode = \(Single' r) -> case S.size @a of S.ConstSize k -> S.decodeEx <$> chunks k r _ -> S.decodeEx r where chunks :: Int -> BS.ByteString -> [ BS.ByteString ] chunks i b | BS.length b == 0 = [] | otherwise = let (x , xs) = BS.splitAt i b in x : chunks i xs -- override text store instance since it uses Haskell's bloaded UTF16 encoding, which in this -- context would be a terrible choice. instance PopKeyEncoding T.Text where type Shape T.Text = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . T.encodeUtf8 {-# INLINE pkDecode #-} pkDecode (Single' x) = T.decodeUtf8 x instance PopKeyEncoding LT.Text where type Shape LT.Text = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . LBS.toStrict . LT.encodeUtf8 {-# INLINE pkDecode #-} pkDecode (Single' x) = LT.decodeUtf8 (LBS.fromStrict x) instance PopKeyEncoding Char where type Shape Char = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Double where type Shape Double = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Float where type Shape Float = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Int8 where type Shape Int8 = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Int16 where type Shape Int16 = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Int32 where type Shape Int32 = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Int64 where type Shape Int64 = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Int where type Shape Int = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Word8 where type Shape Word8 = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Word16 where type Shape Word16 = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Word32 where type Shape Word32 = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Word64 where type Shape Word64 = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Word where type Shape Word = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Integer where type Shape Integer = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Natural where type Shape Natural = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode . toInteger {-# INLINE pkDecode #-} pkDecode (Single' x) = fromInteger do S.decodeEx x instance PopKeyEncoding Rational where type Shape Rational = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance S.Store a => PopKeyEncoding (Ratio a) where type Shape (Ratio a) = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding Graph where type Shape Graph = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance S.Store a => PopKeyEncoding (IntMap a) where type Shape (IntMap a) = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding IntSet where type Shape IntSet = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance (Ord a , S.Store a , S.Store b) => PopKeyEncoding (Map a b) where type Shape (Map a b) = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance S.Store a => PopKeyEncoding (Seq a) where type Shape (Seq a) = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance (Ord a , S.Store a) => PopKeyEncoding (Set a) where type Shape (Set a) = () {-# INLINE shape #-} shape = ISingle {-# INLINE pkEncode #-} pkEncode = Single' . S.encode {-# INLINE pkDecode #-} pkDecode (Single' x) = S.decodeEx x instance PopKeyEncoding () instance PopKeyEncoding (Proxy a) instance PopKeyEncoding Bool instance PopKeyEncoding a => PopKeyEncoding (Maybe a) instance PopKeyEncoding a => PopKeyEncoding (Min a) instance PopKeyEncoding a => PopKeyEncoding (Max a) instance PopKeyEncoding a => PopKeyEncoding (First a) instance PopKeyEncoding a => PopKeyEncoding (Last a) instance PopKeyEncoding a => PopKeyEncoding (Identity a) instance PopKeyEncoding a => PopKeyEncoding (Sum a) instance PopKeyEncoding a => PopKeyEncoding (Product a) instance PopKeyEncoding a => PopKeyEncoding (Const a b) instance (PopKeyEncoding a , PopKeyEncoding b) => PopKeyEncoding (Arg a b) instance (PopKeyEncoding a , PopKeyEncoding b) => PopKeyEncoding (Either a b) instance (PopKeyEncoding a , PopKeyEncoding b) => PopKeyEncoding (a , b) instance (PopKeyEncoding a , PopKeyEncoding b , PopKeyEncoding c) => PopKeyEncoding (a , b , c) instance (PopKeyEncoding a , PopKeyEncoding b , PopKeyEncoding c , PopKeyEncoding d) => PopKeyEncoding (a , b , c , d) instance (PopKeyEncoding a , PopKeyEncoding b , PopKeyEncoding c , PopKeyEncoding d , PopKeyEncoding e) => PopKeyEncoding (a , b , c , d , e)