packages feed

InternedData-0.0.0.1: Data/ByteString/Interned.hs

-- | An implementation of @Int@-mapped @ByteString@s with internalization.
-- Wrap a @ByteString@ with 'ibs' to receive a @IBS@. This internalizes the
-- given @ByteString@, meaning that two bytestring inputs @x@ and @y@ will
-- yield the same @IBS@ if they have the same bytestring representation.
--
-- For convenience, conversion from and to text is possible as well and
-- uses @UTF8@ encoding.
--
-- Since internalized @ByteString@ values are never released, be sure to
-- use it sparingly. I.e. to internalize words, not full sentences.
--
-- NOTE Currently, we use a @ByteString@ internally and can not @compact@
-- the structure. The code remains, though as comments in case we switch to
-- another structure.

module Data.ByteString.Interned
  ( module Data.ByteString.Interned
  ) where

import           Control.Applicative
import           Control.DeepSeq (NFData(..))
import           Data.Aeson as A
import           Data.Binary      as DB
import           Data.ByteString (ByteString)
import           Data.Hashable
import           Data.Serialize   as DS
import           Data.Serialize.Text
import           Data.String as IS
import           Data.String.Conversions
import           Data.Text.Binary
import           Data.Text.Encoding (decodeUtf8,encodeUtf8)
import           Data.Text (Text)
import           Data.Vector.Unboxed.Deriving
import           GHC.Generics

import           Data.ByteString.Interned.Internal



-- | An @IBS@ behaves much like a @ByteString@, but is represented as an
-- @Int@ internally. Its phantom type is polykinded, since we might want to
-- use type-level strings to name things.

newtype IBS k = IBS { getIBS :: Int }
  deriving (Eq,Generic)

derivingUnbox "IBS"
  [t| forall k . IBS k → Int |]
  [|  getIBS                 |]
  [|  IBS                    |]

instance Ord (IBS k) where
  IBS l `compare` IBS r = ibsBimapLookupInt l `compare` ibsBimapLookupInt r
  {-# Inline compare #-}

ibs ∷ ByteString → IBS k
ibs s = IBS $! ibsBimapAdd s
{-# Inline ibs #-}

-- | Handy wrapper to internalize a @Text@ and get a 'IBS'.

ibsText ∷ Text → IBS k
ibsText s = IBS $! ibsBimapAdd $ encodeUtf8 s
{-# Inline ibsText #-}

instance IsString (IBS k) where
  fromString = ibsText . IS.fromString
  {-# Inline fromString #-}

instance Show (IBS k) where
  showsPrec p i r = showsPrec p (ibsTo i :: String) r
  {-# Inline showsPrec #-}

instance Read (IBS k) where
  readsPrec p str = [ (ibsText $ IS.fromString s, y) | (s,y) <- readsPrec p str ]
  {-# Inline readsPrec #-}

instance Hashable (IBS k)

-- | Convert into an @IBS@, using a @Text@ intermediate for proper UTF8
-- conversion.

ibsFrom ∷ ConvertibleStrings x Text ⇒ x → IBS k
ibsFrom = ibsText . convertString
{-# Inline ibsFrom #-}

ibsTo ∷ ConvertibleStrings Text x ⇒ IBS k → x
ibsTo = convertString . ibsToText
{-# Inline ibsTo #-}

ibsToText ∷ IBS k → Text
ibsToText = decodeUtf8 . ibsToUtf8
{-# Inline ibsToText #-}

ibsToUtf8 ∷ IBS k → ByteString
ibsToUtf8 = ibsBimapLookupInt . getIBS
{-# Inline ibsToUtf8 #-}

instance NFData (IBS k) where
  rnf = rnf . getIBS
  {-# Inline rnf #-}

instance Binary (IBS k) where
  put = DB.put . ibsToText
  get = ibs <$> DB.get
  {-# Inline put #-}
  {-# Inline get #-}

instance Serialize (IBS k) where
  put = DS.put . ibsToText
  get = ibs <$> DS.get
  {-# Inline put #-}
  {-# Inline get #-}

instance FromJSON (IBS k) where
  parseJSON s = ibsText <$> parseJSON s
  {-# Inline parseJSON #-}

instance ToJSON (IBS k) where
  toJSON = toJSON . ibsToText
  {-# Inline toJSON #-}