Current section

Files

Jump to
clixir c_src clixir_support.c
Raw

c_src/clixir_support.c

/*
* Clixir Support and I/O functions.
*
* The stuff that talks to BEAM lives here.
*/
#include "clixir_support.h"
// If you want full protocol hexdumps:
// #define CLIXIR_PROTOCOL_DUMP 1
static void handle_command(const char *command, unsigned short len);
static void clixir_read_loop();
int main() {
clixir_read_loop();
}
static void clixir_read_loop() {
// Protocol: 2 bytes with big endian length, then the actual command.
// So maximum size we can read is 65535. We're not even gonna allocate
// that with today's stack sizes and a single process. Once we start pushing
// large graphics objects like bitmaps through the pipe, we'll regret the
// decision but maybe by then we have converted this to a full C node.
char buffer[BUF_SIZE];
unsigned char size_buffer[2];
while (1) {
ssize_t bytes_read = read(STDIN_FILENO, size_buffer, 2);
if (bytes_read == 0) {
// We really only expect EOF here.
fprintf(stderr, "Clixir executable ending normally on EOF\n");
exit(0);
}
if (bytes_read != 2) {
fprintf(stderr, "Unexpected size read: %ld, errno = %d, exiting.\n", bytes_read, errno);
exit(errno);
}
unsigned short size = (size_buffer[0] << 8) + size_buffer[1];
bytes_read = read(STDIN_FILENO, buffer, size);
if (bytes_read <= 0) {
fprintf(stderr, "Error, read %ld bytes, expected positive number\n", bytes_read);
exit(-1);
} else if (bytes_read < size) {
fprintf(stderr, "Error, short read. Expected %d, got %ld\n", size, bytes_read);
dump_hex('<', buffer, bytes_read);
exit(-1);
} else {
dump_hex('<', buffer, size);
handle_command(buffer, size);
}
}
}
// _dispatch_command will be generated by Clixir, this is its signature:
extern void _dispatch_command(const char *buf, unsigned short len, int *index);
static void _handle_command(const char *command, unsigned short len, int *index) {
// For now, we parse the command, then echo it back.
// Note that all we accept for now is
// {cast, <<function_name>>, args.... [, callback_pid]}
// Or, preferably, an array of these.
ei_term term;
if (*index >= len) {
return;
}
int result = ei_decode_ei_term(command, index, &term);
assert(result == 1);
switch (term.ei_type) {
case ERL_SMALL_TUPLE_EXT:
_dispatch_command(command, len, index);
_handle_command(command, len, index);
break;
case ERL_LIST_EXT:
// A list of commands; we can send this for efficiency. Loop and go.
for (int i = 0; i < term.arity; i++) {
_handle_command(command, len, index);
}
break;
case ERL_NIL_EXT:
// Skip nil.
break;
default:
fprintf(stderr, "Unknown term type %c / %d\n", term.ei_type, term.ei_type);
assert(1 == 0);
}
}
static void handle_command(const char *command, unsigned short len) {
int index = 1;
_handle_command(command, len, &index); // Skip version number
}
void write_response_bytes(const char *bytes, unsigned short len) {
struct iovec iov[2];
unsigned char size_buffer[2];
size_buffer[0] = len >> 8;
size_buffer[1] = len & 0xff;
iov[0].iov_base = size_buffer;
iov[0].iov_len = 2;
iov[1].iov_base = (char *) bytes; // ok to drop the const here, read-only access
iov[1].iov_len = len;
dump_hex('>', size_buffer, 2);
dump_hex('>', bytes, len);
assert (writev(STDOUT_FILENO, iov, 2) == len + 2);
}
// For debugging, shamely stolen from github
// https://gist.githubusercontent.com/ccbrown/9722406/raw/05202cd8f86159ff09edc879b70b5ac6be5d25d0/DumpHex.c
void dump_hex(const char dir, const void* data, size_t size) {
#ifdef CLIXIR_PROTOCOL_DUMP
char ascii[17];
size_t i, j;
ascii[16] = '\0';
fprintf(stderr, "%c ", dir);
for (i = 0; i < size; ++i) {
fprintf(stderr,"%02X ", ((unsigned char*)data)[i]);
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
ascii[i % 16] = ((unsigned char*)data)[i];
} else {
ascii[i % 16] = '.';
}
if ((i+1) % 8 == 0 || i+1 == size) {
fprintf(stderr," ");
if ((i+1) % 16 == 0) {
fprintf(stderr,"| %s \n%c ", ascii, dir);
} else if (i+1 == size) {
ascii[(i+1) % 16] = '\0';
if ((i+1) % 16 <= 8) {
fprintf(stderr," ");
}
for (j = (i+1) % 16; j < 16; ++j) {
fprintf(stderr," ");
}
fprintf(stderr,"| %s \n%c ", ascii, dir);
}
}
}
fprintf(stderr, "\n");
fflush(stderr);
#endif
}