{-# LANGUAGE ViewPatterns, TupleSections, ScopedTypeVariables, DeriveDataTypeable, ForeignFunctionInterface, GADTs #-} module Output.Names(writeNames, searchNames) where import Data.List.Extra import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Unsafe as BS import qualified Data.Vector.Storable as V import General.Str import Foreign.Ptr import Foreign.Marshal import Foreign.C.String import Foreign.C.Types import Control.Exception import System.IO.Unsafe import Data.Maybe import Input.Item import General.Util import General.Store foreign import ccall text_search_bound :: CString -> IO CInt foreign import ccall text_search :: CString -> Ptr CString -> CInt -> Ptr CInt -> IO CInt data NamesSize a where NamesSize :: NamesSize Int deriving Typeable data NamesItems a where NamesItems :: NamesItems (V.Vector TargetId) deriving Typeable data NamesText a where NamesText :: NamesText BS.ByteString deriving Typeable writeNames :: StoreWrite -> [(Maybe TargetId, Item)] -> IO () writeNames store xs = do let (ids, strs) = unzip [(i, [' ' | isUpper1 name] ++ lower name) | (Just i, x) <- xs, name <- itemNamePart x] let b = BS.intercalate (BS.pack "\0") (map strPack strs) `BS.append` BS.pack "\0\0" bound <- BS.unsafeUseAsCString b $ \ptr -> text_search_bound ptr storeWrite store NamesSize $ fromIntegral bound storeWrite store NamesItems $ V.fromList ids storeWrite store NamesText b itemNamePart :: Item -> [String] itemNamePart (IModule x) = [last $ splitOn "." x] itemNamePart x = maybeToList $ itemName x searchNames :: StoreRead -> Bool -> [String] -> [TargetId] -- very important to not search for [" "] or [] since the output buffer is too small searchNames store exact (filter (/= "") . map trim -> xs) = unsafePerformIO $ do let vs = storeRead store NamesItems -- if there are no questions, we will match everything, which exceeds the result buffer if null xs then return $ V.toList vs else do let tweak x = strPack $ [' ' | isUpper1 x] ++ lower x ++ "\0" bracket (mallocArray $ storeRead store NamesSize) free $ \result -> BS.unsafeUseAsCString (storeRead store NamesText) $ \haystack -> withs (map (BS.unsafeUseAsCString . tweak) xs) $ \needles -> withArray0 nullPtr needles $ \needles -> do found <- c_text_search haystack needles (if exact then 1 else 0) result xs <- peekArray (fromIntegral found) result return $ map ((vs V.!) . fromIntegral) xs {-# NOINLINE c_text_search #-} -- for profiling c_text_search a b c d = text_search a b c d