~alcinnz/harfbuzz-pure

ref: ac0a6b05dc90078b7bdfcc04614e7aac6deaa6a4 harfbuzz-pure/Data/Text/Glyphize/Array.hs -rw-r--r-- 3.6 KiB
ac0a6b05 — Adrian Cochrane Add more documentation. 1 year, 3 months ago
                                                                                
519746dd Adrian Cochrane
ac0a6b05 Adrian Cochrane
ac0a6b05 Adrian Cochrane
ac0a6b05 Adrian Cochrane
ac0a6b05 Adrian Cochrane
ac0a6b05 Adrian Cochrane
519746dd Adrian Cochrane
519746dd Adrian Cochrane
519746dd Adrian Cochrane
ac0a6b05 Adrian Cochrane
519746dd Adrian Cochrane
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
{-# LANGUAGE MagicHash, UnboxedTuples #-}
-- | Published almost entirely for benchmarks, comparing to stdlib!
-- Should have little direct interest to Harfbuzz callers, & I'm not promising a stable API.
-- This is here because, as it turns out, Harfbuzz can return a lot of output!
module Data.Text.Glyphize.Array where

import Foreign.Storable (Storable(..))
import Foreign.ForeignPtr (ForeignPtr, plusForeignPtr, withForeignPtr, mallocForeignPtrArray)
import Foreign.Ptr
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 []
  | l == 0 = return []
  | otherwise = do
    fp <- clonePtr ptr l
    return $ noCache peekLazy fp $ fromEnum l

-- | This \"function\" has a superficial similarity to 'System.IO.Unsafe.unsafePerformIO' but
-- it is in fact a malevolent agent of chaos. It unpicks the seams of reality
-- (and the 'IO' monad) so that the normal rules no longer apply. It lulls you
-- into thinking it is reasonable, but when you are not looking it stabs you
-- in the back and aliases all of your mutable buffers. The carcass of many a
-- seasoned Haskell programmer lie strewn at its feet.
--
-- Witness the trail of destruction:
--
-- * <https://github.com/haskell/bytestring/commit/71c4b438c675aa360c79d79acc9a491e7bbc26e7>
--
-- * <https://github.com/haskell/bytestring/commit/210c656390ae617d9ee3b8bcff5c88dd17cef8da>
--
-- * <https://github.com/haskell/aeson/commit/720b857e2e0acf2edc4f5512f2b217a89449a89d>
--
-- * <https://ghc.haskell.org/trac/ghc/ticket/3486>
--
-- * <https://ghc.haskell.org/trac/ghc/ticket/3487>
--
-- * <https://ghc.haskell.org/trac/ghc/ticket/7270>
--
-- * <https://gitlab.haskell.org/ghc/ghc/-/issues/22204>
--
-- Do not talk about \"safe\"! You do not know what is safe!
--
-- Yield not to its blasphemous call! Flee traveller! Flee or you will be
-- corrupted and devoured!
--
{-# INLINE accursedUnutterablePerformIO #-}
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