-- Copyright (C) 2017 Fraser Tweedale -- -- hs-notmuch is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program. If not, see . {-# LANGUAGE GeneralizedNewtypeDeriving #-} module Notmuch.Tag ( Tag , getTag , mkTag , tagUseAsCString , tagFromCString , tagMaxLen ) where import Control.DeepSeq (NFData) import Data.Maybe (fromJust) import Data.String (IsString(..)) import qualified Data.ByteString as B import qualified Data.ByteString.Internal as B import qualified Data.ByteString.Unsafe as B import Foreign.C (CString) import Notmuch.Binding.Constants (tagMaxLen) -- | Message tag. Use 'mkTag' to construct a tag. Or use -- @-XOverloadedStrings@, but beware that the @IsString@ instance -- is non-total. -- -- This data type avoid copying when passing tags to /libnotmuch/. -- But copies do occur when reading tags from a database. -- -- A previous experiment with interning showed no benefit. Tags -- are typically very short so the overhead erodes any advantage. -- newtype Tag = Tag B.ByteString deriving (Eq, Ord, NFData) instance Show Tag where show = show . getTag -- | Throws exception if the tag is empty or too long. instance IsString Tag where fromString = fromJust . mkTag . fromString -- | /O(1)/ getTag :: Tag -> B.ByteString getTag (Tag s) = B.init s -- trim null byte -- | /O(n)/ @Just@ a tag, or @Nothing@ if the string is too long -- -- Use UTF-8 encoding to include non-ASCII characters in a tag. -- mkTag :: B.ByteString -> Maybe Tag mkTag s = if w < 1 || w > tagMaxLen then Nothing else Just $ Tag (s `B.snoc` 0) where w = B.length s -- | /O(1)/ tagUseAsCString :: Tag -> (CString -> IO a) -> IO a tagUseAsCString (Tag bs) = B.unsafeUseAsCString bs {-# INLINE tagUseAsCString #-} -- | /O(n)/ @CString@ must be null-terminated and non-empty. -- We must copy the tag into pinned memory so that it can be -- used again as a CString without copying. -- tagFromCString :: CString -> IO Tag tagFromCString cstr = Tag <$> do len <- B.c_strlen cstr B.packCStringLen (cstr, fromIntegral len + 1 {- include null byte -})