diff options
Diffstat (limited to 'erts/emulator/test/driver_SUITE_data/queue_drv.c')
-rw-r--r-- | erts/emulator/test/driver_SUITE_data/queue_drv.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/erts/emulator/test/driver_SUITE_data/queue_drv.c b/erts/emulator/test/driver_SUITE_data/queue_drv.c new file mode 100644 index 0000000000..ded69f89f9 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/queue_drv.c @@ -0,0 +1,195 @@ +#include <stdio.h> +#include "erl_driver.h" + +#define put_int32(i, s) {((char*)(s))[0] = (char)((i) >> 24) & 0xff; \ + ((char*)(s))[1] = (char)((i) >> 16) & 0xff; \ + ((char*)(s))[2] = (char)((i) >> 8) & 0xff; \ + ((char*)(s))[3] = (char)((i) & 0xff);} + +#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \ + (((unsigned char*) (s))[1] << 16) | \ + (((unsigned char*) (s))[2] << 8) | \ + (((unsigned char*) (s))[3])) + +/* + * Data operations. To use, send code using erlang:port_control/2, + * then send the data to the port. + */ + +#define PUSHQ 0 +#define ENQ 1 +#define PUSHQ_BIN 2 +#define ENQ_BIN 3 +#define PUSHQV 4 +#define ENQV 5 + +/* + * Control operations. Data is returned directly. + */ +#define DEQ 6 +#define BYTES_QUEUED 7 +#define READ_HEAD 8 + +static ErlDrvPort erlang_port; +static unsigned opcode; /* Opcode for next operation. */ +static ErlDrvData queue_start(ErlDrvPort, char*); +static void queue_stop(ErlDrvData), queue_read(ErlDrvData, char*, int); +static void queue_outputv(ErlDrvData, ErlIOVec*); +static int control(ErlDrvData, unsigned int, char*, int, char**, int); +static ErlDrvBinary* read_head(ErlDrvPort, int bytes); + +static ErlDrvEntry queue_driver_entry = +{ + NULL, + queue_start, + queue_stop, + queue_read, + NULL, + NULL, + "queue_drv", + NULL, + NULL, + control, + NULL, + queue_outputv, + NULL +}; + +DRIVER_INIT(queue_drv) +{ + erlang_port = (ErlDrvPort) -1; + return &queue_driver_entry; +} + +static ErlDrvData queue_start(ErlDrvPort port, char *buf) +{ + if (erlang_port != (ErlDrvPort)-1) { + return ERL_DRV_ERROR_GENERAL; + } + erlang_port = port; + opcode = 0xFFFFFFFF; + set_port_control_flags(erlang_port, PORT_CONTROL_FLAG_BINARY); + return (ErlDrvData)port; +} + +/* messages from Erlang */ +static void queue_read(ErlDrvData port, char *buf, int len) +{ +} + +static void queue_stop(ErlDrvData port) +{ + erlang_port = (ErlDrvPort) -1; +} + +static int +control(ErlDrvData drv_data, unsigned command, char* buf, int len, char** rbuf, int rlen) +{ + ErlDrvBinary* b; + + switch (command) { + case PUSHQ: + case ENQ: + case PUSHQ_BIN: + case ENQ_BIN: + case PUSHQV: + case ENQV: + opcode = command; + *rbuf = NULL; + return 0; + case DEQ: + *rbuf = NULL; + if (len != 4) { + driver_failure_atom(erlang_port, "deq: bad length"); + } else { + int n = get_int32(buf); + driver_deq(erlang_port, n); + } + return 0; + case BYTES_QUEUED: + *rbuf = (char*)(b = driver_alloc_binary(4)); + put_int32(driver_sizeq(erlang_port), b->orig_bytes); + return 0; + case READ_HEAD: + if (len != 4) { + driver_failure_atom(erlang_port, "read_head: bad length"); + return 0; + } else { + int n = get_int32(buf); + *rbuf = (char *) read_head(erlang_port, n); + return 0; /* Ignored anyway */ + } + default: + driver_failure_atom(erlang_port, "bad opcode to control()"); + return 0; + } +} + +static void +queue_outputv(ErlDrvData drv_data, ErlIOVec* ev) +{ + ErlDrvBinary* bin; + ErlDrvPort ix = (ErlDrvPort) drv_data; + int i = ev->vsize - 1; + int offset; + + switch (opcode) { + case PUSHQ: + driver_pushq(ix, ev->iov[i].iov_base, ev->iov[i].iov_len); + break; + case ENQ: + driver_enq(ix, ev->iov[i].iov_base, ev->iov[i].iov_len); + break; + case PUSHQ_BIN: + case ENQ_BIN: + if (ev->binv[i] != NULL) { + bin = ev->binv[i]; + offset = ev->iov[i].iov_base - bin->orig_bytes; + } else { + bin = driver_alloc_binary(ev->iov[i].iov_len); + memcpy(bin->orig_bytes, ev->iov[i].iov_base, ev->iov[i].iov_len); + offset = 0; + } + if (opcode == PUSHQ_BIN) { + driver_pushq_bin(ix, bin, offset, ev->iov[i].iov_len); + } else { + driver_enq_bin(ix, bin, offset, ev->iov[i].iov_len); + } + if (ev->binv[i] == NULL) { + driver_free_binary(bin); + } + break; + case PUSHQV: + driver_pushqv(ix, ev, 0); + break; + case ENQV: + driver_enqv(ix, ev, 0); + break; + default: + fprintf(stderr, "[queue_drv] Bad opcode %d\n", opcode); + driver_failure_atom(ix, "bad_opcode"); + break; + } +} + +static ErlDrvBinary* +read_head(ErlDrvPort ix, int bytes) +{ + int len_io_queue; + SysIOVec* iov = driver_peekq(ix, &len_io_queue); + int bytes_left = bytes; + int copied = 0; + ErlDrvBinary* b; + int iv; + + b = driver_alloc_binary(bytes); + iv = 0; + while (bytes_left > 0 && iv < len_io_queue) { + int n = (iov[iv].iov_len < bytes_left) ? iov[iv].iov_len : bytes_left; + memcpy(b->orig_bytes+copied, iov[iv].iov_base, n); + copied += n; + bytes_left -= n; + iv++; + } + return b; +} |