From ac0a6b05dc90078b7bdfcc04614e7aac6deaa6a4 Mon Sep 17 00:00:00 2001 From: Adrian Cochrane Date: Tue, 26 Sep 2023 12:10:47 +1300 Subject: [PATCH] Add more documentation. --- Data/Text/Glyphize/Array.hs | 14 ++++++++++++++ Data/Text/Glyphize/Buffer.hs | 6 +++++- Data/Text/Glyphize/Font.hs | 2 ++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Data/Text/Glyphize/Array.hs b/Data/Text/Glyphize/Array.hs index 34f089a..07ea54e 100644 --- a/Data/Text/Glyphize/Array.hs +++ b/Data/Text/Glyphize/Array.hs @@ -12,21 +12,29 @@ import Foreign.Marshal.Array (copyArray) import GHC.IO (IO(IO)) import GHC.Exts (realWorld#, oneShot) +-- | Clone the given array so it can be freed without the losing access to the data. +-- Uses `memcpy` so it gets very heavily optimized by the OS. +clonePtr :: Ptr a -> Int -> ForeignPtr a clonePtr ptr l = do ret <- mallocForeignPtrArray l withForeignPtr ret $ \ptr' -> copyArray ptr' ptr l return ret +-- | Iterate over an array in a ForeignPtr, no matter how small or large it is. peekLazy :: Storable a => ForeignPtr a -> Int -> [a] peekLazy fp 0 = [] peekLazy fp n | n <= chunkSize = withFP $ peekEager [] n | otherwise = withFP $ peekEager (plusForeignPtr fp chunkSize `peekLazy` (-) n chunkSize) chunkSize where withFP = accursedUnutterablePerformIO . withForeignPtr fp +-- | Variation of peekArray, taking a tail to append to the decoded list. peekEager acc 0 ptr = return acc peekEager acc n ptr = let n' = pred n in do e <- peekElemOff ptr n' peekEager (e:acc) n' ptr +-- | How many words should be decoded by `peekLazy` & `iterateLazy`. chunkSize = 1024 -- 4k, benchmarks seem to like it! +-- | Convert an array from C code into a Haskell list, +-- performant no matter how small or large it is. iterateLazy :: Storable a => Ptr a -> Int -> IO [a] iterateLazy ptr l | l < 0 = putStrLn ("Invalid array length: " ++ show l) >> return [] @@ -67,4 +75,10 @@ iterateLazy ptr l accursedUnutterablePerformIO :: IO a -> a accursedUnutterablePerformIO (IO m) = case m realWorld# of (# _, r #) -> r +-- | Harfbuzz produces ~40x as much output data as its input data. +-- In many applications that input data would be a large fraction of its heap. +-- As such, unless callers are processing these results, it is usually more +-- efficient for Haskell to recompute the glyphs than to store them. +-- +-- This synonym of `oneShot` is used to instruct Haskell of this fact. noCache = oneShot diff --git a/Data/Text/Glyphize/Buffer.hs b/Data/Text/Glyphize/Buffer.hs index 5fad2d1..074ce38 100644 --- a/Data/Text/Glyphize/Buffer.hs +++ b/Data/Text/Glyphize/Buffer.hs @@ -395,12 +395,14 @@ data GlyphInfo = GlyphInfo { -- but only when it is safe to do the elongation without interrupting text shaping. } deriving (Show, Read, Eq, Generic) instance NFData GlyphInfo +-- | Decodes multiple `GlyphInfo`s from a dereferenced `Word32` list according to +-- Harfbuzz's ABI. decodeInfos :: [Word32] -> [GlyphInfo] decodeInfos (codepoint':cluster':mask:_:_:rest) = GlyphInfo codepoint' cluster' (mask `testBit` 1) (mask `testBit` 2) (mask `testBit` 3):decodeInfos rest decodeInfos _ = [] --- | Decodes `Buffer'`'s glyph information array.' +-- | Decodes `Buffer'`'s glyph information array. glyphInfos buf' = do arr <- throwNull $ hb_buffer_get_glyph_infos buf' nullPtr length <- hb_buffer_get_length buf' @@ -428,6 +430,8 @@ data GlyphPos = GlyphPos { -- not effect how much the line advances. } deriving (Show, Read, Eq, Generic) instance NFData GlyphPos +-- | Decode multiple `GlyphPos`s from a dereferenced list according to +-- Harfbuzz's ABI. decodePositions (x_advance':y_advance':x_offset':y_offset':_:rest) = GlyphPos x_advance' y_advance' x_offset' y_offset':decodePositions rest decodePositions _ = [] diff --git a/Data/Text/Glyphize/Font.hs b/Data/Text/Glyphize/Font.hs index fa0b614..2aa2227 100644 --- a/Data/Text/Glyphize/Font.hs +++ b/Data/Text/Glyphize/Font.hs @@ -922,6 +922,8 @@ bs2blob (BS bytes len) = do blob <- throwNull $ withForeignPtr bytes $ \bytes' -> hb_blob_create bytes' len hb_MEMORY_MODE_DUPLICATE nullPtr nullFunPtr newForeignPtr hb_blob_destroy blob +-- | Convert from a ByteString to a temporary copy of Harfbuzz's equivalent. +-- Do not use this Blob outside the passed callback. withBlob :: ByteString -> (Blob_ -> IO a) -> IO a withBlob (BS bytes len) cb = withForeignPtr bytes $ \bytes' -> do throwNull $ pure bytes' -- 2.30.2