diff --git a/users/sterni/blipqn/blipqn.bqn b/users/sterni/blipqn/blipqn.bqn index a9d904ca0..a18f68b2e 100644 --- a/users/sterni/blipqn/blipqn.bqn +++ b/users/sterni/blipqn/blipqn.bqn @@ -1,20 +1,42 @@ -MakeFlipdot⇐ +WithFlipdot⇐ -# - host (char*) argument: -# - Assume char is i8 (safe even if it's actually u8 if -# ASCII). We can't let •FFI check this for us since CBQN doesn't support -# specifying c7. We do this in MakeFlipdot -# - Needs to be zero-terminated, we also do this in MakeFlipdot. -# - bitmap_len (size_t) argument: assume size_t is at least u32. -ffi_send_to_flipdot ← "libflipdot.so" •FFI "i8"‿"send_to_flipdot"‿"*i8:c8"‿"u16"‿"*u8"‿"u32" +t ← { + bool ⇐ "i8" + # needs bound checking in wrapper (no c7 in CBQN) and zero byte appended + charp ⇐ "*i8:c8" + # assume size_t is at least 32bit + size ⇐ "u32" + # used for struct flipdot * + hdl ⇐ "*" +} + +ffi_flipdot_open ← "libflipdot.so" •FFI t.hdl‿"flipdot_open"‿t.charp‿"u16" +ffi_flipdot_close ← "libflipdot.so" •FFI ""‿"flipdot_close"‿t.hdl +ffi_flipdot_send ← "libflipdot.so" •FFI t.bool‿"flipdot_send"‿t.hdl‿"*u8"‿t.size Compact ← +´∘(⌽ × 2⊸⋆∘↕∘≠)˘∘(∘‿8⊸⥊) MakeFlipdot ← { - host‿port 𝕊 w‿h: - "Total pixel count must be divisible by 8" ! 0=8|w×h + host‿port‿w‿h: "Hostname must be ASCII" ! ∧´(@+127)≥host + "Total pixel count must be divisible by 8" ! 0=8|w×h + + hdl ← FFI_flipdot_open (host∾@)‿port + Close ⇐ {𝕤 ⋄ FFI_flipdot_close ⋈hdl} + shape ⇐ h‿w empty ⇐ shape⥊0 - Send ⇐ {"sendto(2) error"!FFI_send_to_flipdot (host∾@)‿port∾(⊢⋈≠) Compact 𝕩} + Send ⇐ {FFI_flipdot_send hdl∾(⊢⋈≠) Compact 𝕩} } + +# Inspired by Go's defer. Will execute 𝔽 after 𝔾 (passing 𝕨 and 𝕩 to each) +# even if an error occurs. If an error occurred, _defer_ will cause an +# assertion failure with the error message after executing 𝔾. This looses +# the original error location, though. +_defer_ ← { + # At least in CBQN, •CurrentError may fail for namespace related reasons + s‿r ← (1⊸⋈∘𝔾)⎊(0⊸⋈∘(•CurrentError⎊("_defer_: Unknown Error occurred"˙))) 𝕩 ⋄ 𝔽 𝕩 ⋄ r⊣r!s; + (𝕨⊸𝔽) _𝕣_ (𝕨⊸𝔾) 𝕩 +} + +WithFlipdot ← {{𝕩.Close @} _defer_ 𝕏 MakeFlipdot 𝕨} diff --git a/users/sterni/blipqn/examples.bqn b/users/sterni/blipqn/examples.bqn index 85c5b479e..54b4f6012 100755 --- a/users/sterni/blipqn/examples.bqn +++ b/users/sterni/blipqn/examples.bqn @@ -1,6 +1,6 @@ #!/usr/bin/env BQN -⟨MakeFlipdot⟩ ⇐ •Import "../lib/"⊸∾⍟("bin" (⊣≡(-∘≠⊸↑)) •path) "blipqn.bqn" +⟨WithFlipdot⟩ ⇐ •Import "../lib/"⊸∾⍟("bin" (⊣≡(-∘≠⊸↑)) •path) "blipqn.bqn" examples ← ⍉>⟨ "random"‿{𝕩.Send 𝕩.shape •rand.Range 2}, @@ -19,11 +19,9 @@ opts ← { host‿port‿width‿height‿example ← •args example ⇐ - addr ⇐ host⋈•ParseFloat port - dim ⇐ •ParseFloat¨ width‿height + cfg ⇐ ⟨host⟩∾•ParseFloat¨ port‿width‿height } action ← examples (⊏⊸(⊑∘⊐) ⊑ ⊏˜⟜1)⟜< opts.example -flip ← opts.addr MakeFlipdot opts.dim -Action flip +opts.cfg WithFlipdot action diff --git a/users/sterni/blipqn/flipdot.c b/users/sterni/blipqn/flipdot.c index 1bdd06c8f..aa1cb85fc 100644 --- a/users/sterni/blipqn/flipdot.c +++ b/users/sterni/blipqn/flipdot.c @@ -1,9 +1,9 @@ #define _DEFAULT_SOURCE // see getnameinfo(3), for NI_MAX* #define _POSIX_C_SOURCE 200112L // see getaddrinfo(3) #include -#include #include #include +#include #include #include #include @@ -19,29 +19,53 @@ int resolve_addr(char *host, char *port, struct addrinfo **addrs) { return getaddrinfo(host, port, &hints, addrs); } -// Send given bytes[len] to the host:port via UDP, returns 1 if all bytes -// where sent and no locally detectable errors occurred. -int8_t send_to_flipdot(char *host, in_port_t port_number, uint8_t *bitmap, - size_t bitmap_len) { +struct flipdot { + int sockfd; + struct addrinfo *addrs; +}; + +// Assumes all pointers in struct flipdot are not NULL which should be the case +// for any struct returned by flipdot_open(). +void flipdot_close(struct flipdot *flipdot) { + freeaddrinfo(flipdot->addrs); + close(flipdot->sockfd); + free(flipdot); +} + +// Returns NULL if some error occurred. Note that errno isn't necessarily set. +struct flipdot *flipdot_open(char *host, in_port_t port_number) { char port[NI_MAXSERV]; - int sockfd = -1; - ssize_t sent = 0; - struct addrinfo *addrs = NULL; + struct flipdot *flipdot = malloc(sizeof(struct flipdot)); + if (flipdot == NULL) goto error; - if (snprintf(port, sizeof port, "%d", port_number) < 0) goto error; + memset(flipdot, 0, sizeof(struct flipdot)); + flipdot->sockfd = -1; + flipdot->addrs = NULL; - if (resolve_addr(host, port, &addrs) != 0) goto error; + if (snprintf(port, sizeof(port), "%d", port_number) < 0) goto error; + if (resolve_addr(host, port, &flipdot->addrs) != 0) goto error; - sockfd = socket(addrs->ai_family, SOCK_DGRAM, IPPROTO_UDP); - if (sockfd < 0) goto error; + flipdot->sockfd = socket(flipdot->addrs->ai_family, SOCK_DGRAM, IPPROTO_UDP); + if (flipdot->sockfd < 0) goto error; - sent = - sendto(sockfd, bitmap, bitmap_len, 0, addrs->ai_addr, addrs->ai_addrlen); - - if (sent != (ssize_t)bitmap_len) goto error; + return flipdot; error: - if (addrs != NULL) freeaddrinfo(addrs); - if (sockfd >= 0) close(sockfd); + if (flipdot != NULL) { + if (flipdot->sockfd >= 0) close(flipdot->sockfd); + if (flipdot->addrs != NULL) freeaddrinfo(flipdot->addrs); + free(flipdot); + } + + return NULL; +} + +// Send given bytes[len] to the given flipdot, returns 1 if all bytes were sent +// and no locally detectable errors occurred, 0 otherwise. +int8_t flipdot_send(struct flipdot *flipdot, uint8_t *bitmap, + size_t bitmap_len) { + ssize_t sent = sendto(flipdot->sockfd, bitmap, bitmap_len, 0, + flipdot->addrs->ai_addr, flipdot->addrs->ai_addrlen); + return (sent == (ssize_t)bitmap_len); }