feat(sterni/blipqn): reuse address and socket for multiple Sends
This is achieved by storing the resources we need to acquire for interacting with the flipdot (socket fd and addrinfo struct) in a `struct flipdot` that is dynamically allocated and treated as an opaque pointer object via the BQN FFI. To make sure these resources are released correctly, we only provide a lisp style WithFlipdot to the user which takes care of acquiring and releasing the `struct flipdot`. This works even if an error occurs in the function the user provides thanks to _defer_. I'm not sure if calling it _defer_ is right since Go's error handling works differently, so defer really is deferred execution in a sense which doesn't really fit what we're doing here. The closest is probably Haskell's bracket, but that name references it's triadic nature which doesn't fit our implementation. Change-Id: Iff65d277a448dbe0c6ac93e816ece5ab6fa10190 Reviewed-on: https://cl.tvl.fyi/c/depot/+/13011 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
This commit is contained in:
parent
1027e21eee
commit
a9e121380b
3 changed files with 78 additions and 34 deletions
|
|
@ -1,20 +1,42 @@
|
||||||
MakeFlipdot⇐
|
WithFlipdot⇐
|
||||||
|
|
||||||
# - host (char*) argument:
|
t ← {
|
||||||
# - Assume char is i8 (safe even if it's actually u8 if
|
bool ⇐ "i8"
|
||||||
# ASCII). We can't let •FFI check this for us since CBQN doesn't support
|
# needs bound checking in wrapper (no c7 in CBQN) and zero byte appended
|
||||||
# specifying c7. We do this in MakeFlipdot
|
charp ⇐ "*i8:c8"
|
||||||
# - Needs to be zero-terminated, we also do this in MakeFlipdot.
|
# assume size_t is at least 32bit
|
||||||
# - bitmap_len (size_t) argument: assume size_t is at least u32.
|
size ⇐ "u32"
|
||||||
ffi_send_to_flipdot ← "libflipdot.so" •FFI "i8"‿"send_to_flipdot"‿"*i8:c8"‿"u16"‿"*u8"‿"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⊸⥊)
|
Compact ← +´∘(⌽ × 2⊸⋆∘↕∘≠)˘∘(∘‿8⊸⥊)
|
||||||
|
|
||||||
MakeFlipdot ← {
|
MakeFlipdot ← {
|
||||||
host‿port 𝕊 w‿h:
|
host‿port‿w‿h:
|
||||||
"Total pixel count must be divisible by 8" ! 0=8|w×h
|
|
||||||
"Hostname must be ASCII" ! ∧´(@+127)≥host
|
"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
|
shape ⇐ h‿w
|
||||||
empty ⇐ shape⥊0
|
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 𝕨}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env BQN
|
#!/usr/bin/env BQN
|
||||||
|
|
||||||
⟨MakeFlipdot⟩ ⇐ •Import "../lib/"⊸∾⍟("bin" (⊣≡(-∘≠⊸↑)) •path) "blipqn.bqn"
|
⟨WithFlipdot⟩ ⇐ •Import "../lib/"⊸∾⍟("bin" (⊣≡(-∘≠⊸↑)) •path) "blipqn.bqn"
|
||||||
|
|
||||||
examples ← ⍉>⟨
|
examples ← ⍉>⟨
|
||||||
"random"‿{𝕩.Send 𝕩.shape •rand.Range 2},
|
"random"‿{𝕩.Send 𝕩.shape •rand.Range 2},
|
||||||
|
|
@ -19,11 +19,9 @@ opts ← {
|
||||||
host‿port‿width‿height‿example ← •args
|
host‿port‿width‿height‿example ← •args
|
||||||
|
|
||||||
example ⇐
|
example ⇐
|
||||||
addr ⇐ host⋈•ParseFloat port
|
cfg ⇐ ⟨host⟩∾•ParseFloat¨ port‿width‿height
|
||||||
dim ⇐ •ParseFloat¨ width‿height
|
|
||||||
}
|
}
|
||||||
|
|
||||||
action ← examples (⊏⊸(⊑∘⊐) ⊑ ⊏˜⟜1)⟜< opts.example
|
action ← examples (⊏⊸(⊑∘⊐) ⊑ ⊏˜⟜1)⟜< opts.example
|
||||||
|
|
||||||
flip ← opts.addr MakeFlipdot opts.dim
|
opts.cfg WithFlipdot action
|
||||||
Action flip
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
#define _DEFAULT_SOURCE // see getnameinfo(3), for NI_MAX*
|
#define _DEFAULT_SOURCE // see getnameinfo(3), for NI_MAX*
|
||||||
#define _POSIX_C_SOURCE 200112L // see getaddrinfo(3)
|
#define _POSIX_C_SOURCE 200112L // see getaddrinfo(3)
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <limits.h>
|
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
@ -19,29 +19,53 @@ int resolve_addr(char *host, char *port, struct addrinfo **addrs) {
|
||||||
return getaddrinfo(host, port, &hints, addrs);
|
return getaddrinfo(host, port, &hints, addrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send given bytes[len] to the host:port via UDP, returns 1 if all bytes
|
struct flipdot {
|
||||||
// where sent and no locally detectable errors occurred.
|
int sockfd;
|
||||||
int8_t send_to_flipdot(char *host, in_port_t port_number, uint8_t *bitmap,
|
struct addrinfo *addrs;
|
||||||
size_t bitmap_len) {
|
};
|
||||||
|
|
||||||
|
// 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];
|
char port[NI_MAXSERV];
|
||||||
int sockfd = -1;
|
struct flipdot *flipdot = malloc(sizeof(struct flipdot));
|
||||||
ssize_t sent = 0;
|
if (flipdot == NULL) goto error;
|
||||||
struct addrinfo *addrs = NULL;
|
|
||||||
|
|
||||||
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);
|
flipdot->sockfd = socket(flipdot->addrs->ai_family, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
if (sockfd < 0) goto error;
|
if (flipdot->sockfd < 0) goto error;
|
||||||
|
|
||||||
sent =
|
return flipdot;
|
||||||
sendto(sockfd, bitmap, bitmap_len, 0, addrs->ai_addr, addrs->ai_addrlen);
|
|
||||||
|
|
||||||
if (sent != (ssize_t)bitmap_len) goto error;
|
|
||||||
|
|
||||||
error:
|
error:
|
||||||
if (addrs != NULL) freeaddrinfo(addrs);
|
if (flipdot != NULL) {
|
||||||
if (sockfd >= 0) close(sockfd);
|
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);
|
return (sent == (ssize_t)bitmap_len);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue