Skip to content

Commit a347cc9

Browse files
committed
Polish second attempt
1 parent 2c1f7b0 commit a347cc9

24 files changed

Lines changed: 753 additions & 8257 deletions

File tree

HIGH_LEVEL_DESIGN.md

Lines changed: 0 additions & 1050 deletions
This file was deleted.

HIGH_LEVEL_DESIGN_old.md

Lines changed: 0 additions & 1246 deletions
This file was deleted.

ISSUE_ANALYSIS.md

Lines changed: 0 additions & 1555 deletions
This file was deleted.

PROPOSAL.md

Lines changed: 0 additions & 805 deletions
This file was deleted.

PROPOSAL_DESIGN.md

Lines changed: 0 additions & 1480 deletions
This file was deleted.

PROPOSAL_v3.md

Lines changed: 0 additions & 695 deletions
This file was deleted.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
-- | High-level wrappers around the generated qrcodegen bindings.
2+
module HighLevel (
3+
encodeText
4+
, getSize
5+
, getModule
6+
) where
7+
8+
import Data.Word (Word8)
9+
import Foreign.Marshal.Utils qualified as Marshal
10+
import Foreign.Ptr (Ptr)
11+
12+
import HsBindgen.Runtime.HighLevel.Refine (OutMarshaller, input, output,
13+
peekIncompleteArrayOut, pureIn,
14+
pureRes, result, scratchOut,
15+
toHighLevel, withCStringIn,
16+
withConstIncompleteArrayIn)
17+
import HsBindgen.Runtime.IncompleteArray (IncompleteArray)
18+
19+
import QRCodeGenerator.Generated qualified as QR
20+
import QRCodeGenerator.Generated.Safe qualified as QR
21+
22+
-- | Lift @qrcodegen_encodeText@. Destructure the leading scratch @()@ at
23+
-- the call site.
24+
encodeText
25+
:: String
26+
-> QR.Qrcodegen_Ecc
27+
-> Int
28+
-> Int
29+
-> QR.Qrcodegen_Mask
30+
-> Bool
31+
-> IO ((), (IncompleteArray Word8, Bool))
32+
encodeText = toHighLevel
33+
( input withCStringIn
34+
$ output tempScratch
35+
$ output qrCodeOut
36+
$ input (pureIn id)
37+
$ input (pureIn fromIntegral)
38+
$ input (pureIn fromIntegral)
39+
$ input (pureIn id)
40+
$ input (pureIn Marshal.fromBool)
41+
$ result (pureRes Marshal.toBool)
42+
) QR.qrcodegen_encodeText
43+
where
44+
maxLen :: Int
45+
maxLen = fromIntegral QR.qrcodegen_BUFFER_LEN_MAX
46+
47+
tempScratch :: OutMarshaller (Ptr Word8) ()
48+
tempScratch = scratchOut maxLen
49+
50+
qrCodeOut :: OutMarshaller (Ptr Word8) (IncompleteArray Word8)
51+
qrCodeOut = peekIncompleteArrayOut maxLen
52+
53+
getSize :: IncompleteArray Word8 -> IO Int
54+
getSize = toHighLevel
55+
( input withConstIncompleteArrayIn
56+
$ result (pureRes fromIntegral)
57+
) QR.qrcodegen_getSize
58+
59+
getModule :: IncompleteArray Word8 -> Int -> Int -> IO Bool
60+
getModule = toHighLevel
61+
( input withConstIncompleteArrayIn
62+
$ input (pureIn fromIntegral)
63+
$ input (pureIn fromIntegral)
64+
$ result (pureRes Marshal.toBool)
65+
) QR.qrcodegen_getModule

examples/c-qrcode/hs-project/app/Main.hs

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,11 @@ import Control.Monad (guard)
66
import Data.Bool (bool)
77
import Data.Foldable (for_)
88
import Data.Word (Word8)
9-
import Foreign qualified as F
10-
import Foreign.C qualified as F
119

12-
import HsBindgen.Runtime.IncompleteArray qualified as IA
13-
import HsBindgen.Runtime.IsArray qualified as IsA
14-
import HsBindgen.Runtime.Prelude
15-
import HsBindgen.Runtime.PtrConst qualified as PtrConst
10+
import HsBindgen.Runtime.IncompleteArray (IncompleteArray)
1611

12+
import HighLevel qualified as HL
1713
import QRCodeGenerator.Generated qualified as QR
18-
import QRCodeGenerator.Generated.Safe qualified as QR
19-
20-
fromPtr
21-
:: forall a .
22-
F.Storable a
23-
=> Int -> F.Ptr a -> IO (IncompleteArray a)
24-
fromPtr len p = IA.peekArray len p'
25-
where
26-
p' :: F.Ptr (IncompleteArray a)
27-
p' = IA.toPtr p
2814

2915
-- static void printQr(const uint8_t qrcode[]) {
3016
-- int size = qrcodegen_getSize(qrcode);
@@ -39,41 +25,36 @@ fromPtr len p = IA.peekArray len p'
3925
-- }
4026
printQr :: IncompleteArray Word8 -> IO ()
4127
printQr qrCode = do
42-
size <- IsA.withElemPtr qrCode $ \ptr -> QR.qrcodegen_getSize (PtrConst.unsafeFromPtr ptr)
28+
size <- HL.getSize qrCode
4329
let border = 4
4430
range = [-border .. size + border - 1]
4531
for_ range $ \y -> do
4632
for_ range $ \x -> do
47-
str <- bool " " "██" . F.toBool <$>
48-
(IsA.withElemPtr qrCode $ \ptr -> QR.qrcodegen_getModule (PtrConst.unsafeFromPtr ptr) x y)
49-
putStr str
33+
isOn <- HL.getModule qrCode x y
34+
putStr (bool " " "██" isOn)
5035
putStr "\n"
5136
putStr "\n"
5237

5338
-- static void doBasicDemo(void) {
54-
-- const char *text = "Hello, world!"; // User-supplied text
55-
-- enum qrcodegen_Ecc errCorLvl = qrcodegen_Ecc_LOW; // Error correction level
56-
--
57-
-- // Make and print the QR Code symbol
39+
-- const char *text = "Hello, world!";
40+
-- enum qrcodegen_Ecc errCorLvl = qrcodegen_Ecc_LOW;
5841
-- uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX];
5942
-- uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX];
6043
-- bool ok = qrcodegen_encodeText(text, tempBuffer, qrcode, errCorLvl,
6144
-- qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true);
62-
-- if (ok)
63-
-- printQr(qrcode);
45+
-- if (ok) printQr(qrcode);
6446
-- }
6547
basicDemo :: IO ()
6648
basicDemo = do
67-
F.withCAString "Hello, world!" $ \text ->
68-
F.allocaArray (fromIntegral QR.qrcodegen_BUFFER_LEN_MAX) $ \tempBuffer -> do
69-
F.allocaArray (fromIntegral QR.qrcodegen_BUFFER_LEN_MAX) $ \qrCode -> do
70-
b <- QR.qrcodegen_encodeText (PtrConst.unsafeFromPtr text) tempBuffer qrCode QR.Qrcodegen_Ecc_LOW
71-
QR.qrcodegen_VERSION_MIN QR.qrcodegen_VERSION_MAX
72-
QR.Qrcodegen_Mask_AUTO (F.fromBool True)
73-
qrCodeIA <- IA.peekArray (fromIntegral QR.qrcodegen_BUFFER_LEN_MAX) (IA.toPtr qrCode)
74-
guard (F.toBool b)
75-
printQr qrCodeIA
49+
((), (qrCode, ok)) <- HL.encodeText
50+
"Hello, world!"
51+
QR.Qrcodegen_Ecc_LOW
52+
(fromIntegral QR.qrcodegen_VERSION_MIN)
53+
(fromIntegral QR.qrcodegen_VERSION_MAX)
54+
QR.Qrcodegen_Mask_AUTO
55+
True
56+
guard ok
57+
printQr qrCode
7658

7759
main :: IO ()
78-
main = do
79-
basicDemo
60+
main = basicDemo

examples/c-qrcode/hs-project/c-qrcode.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ library
3232
executable c-qrcode
3333
import: warnings
3434
main-is: Main.hs
35+
other-modules: HighLevel
3536
build-depends:
3637
, base
3738
, c-qrcode
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{-# LANGUAGE DerivingStrategies #-}
2+
3+
-- | High-level wrappers around the generated libpcap bindings.
4+
module HighLevel (
5+
PcapError (..)
6+
, findAllDevNames
7+
) where
8+
9+
import Control.Exception (Exception, throwIO)
10+
import Control.Monad (when)
11+
import Foreign.C.String qualified as C
12+
import Foreign.C.Types (CInt)
13+
import Foreign.Marshal.Alloc (alloca)
14+
import Foreign.Ptr (Ptr, nullPtr)
15+
import Foreign.Storable (peek)
16+
17+
import HsBindgen.Runtime.HighLevel.Refine (OutMarshaller (..), output,
18+
peekCStringOut, pureRes, result,
19+
toHighLevel)
20+
21+
import Generated.Pcap qualified as Pcap
22+
import Generated.Pcap.Safe qualified as Pcap
23+
24+
-- | Thrown by 'findAllDevNames' when @pcap_findalldevs@ reports failure.
25+
data PcapError = PcapError { msg :: String, code :: CInt }
26+
deriving stock (Show)
27+
instance Exception PcapError
28+
29+
-- | Allocate a @pcap_if_t **@, walk the linked list after the call, free it.
30+
peekPcapDeviceNames
31+
:: OutMarshaller (Ptr (Ptr Pcap.Pcap_if_t)) [String]
32+
peekPcapDeviceNames = OutMarshaller $ \k -> alloca $ \pp -> do
33+
r <- k pp
34+
headPtr <- peek pp
35+
names <- collect [] headPtr
36+
Pcap.pcap_freealldevs headPtr
37+
pure (names, r)
38+
where
39+
collect acc ptr
40+
| ptr == nullPtr = pure (reverse acc)
41+
| otherwise = do
42+
dev <- peek ptr
43+
name <- C.peekCString (Pcap.pcap_if_t_name dev)
44+
collect (name : acc) (Pcap.pcap_if_t_next dev)
45+
46+
-- | Collect the names of all devices visible to libpcap. The status check
47+
-- runs after the spec rather than inside a 'throwOn' because the error
48+
-- buffer lives in a separate output position.
49+
findAllDevNames :: IO [String]
50+
findAllDevNames = do
51+
(names, (errMsg, status)) <- toHighLevel
52+
( output peekPcapDeviceNames
53+
$ output (peekCStringOut (fromIntegral Pcap.pCAP_ERRBUF_SIZE))
54+
$ result (pureRes id)
55+
) Pcap.pcap_findalldevs
56+
when (status /= 0) $ throwIO (PcapError errMsg status)
57+
pure names

0 commit comments

Comments
 (0)