From 134dee4dcccd32b66cfd53d77c70e768b66a7a6e Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 10 Jan 2014 01:44:40 +0000 Subject: [PATCH] rxgen: Use the xg files as sources for rxgen Use the Arla xg files rather than C headers as sources for the rxgen script. Signed-off-by: David Howells --- Makefile | 11 +- af_rxrpc.c | 96 +++-- circ_buf.h | 30 ++ compile_pykafs.py | 4 +- py_rxgen.c | 276 +++++++++++++-- py_rxgen.h | 46 ++- rpc-api/afsuuid.h | 6 +- rpc-api/vldb.xg | 3 + rxgen.h | 25 +- rxgen/emit_c_struct.pm | 110 ++++-- rxgen/emit_c_sync_funcs.pm | 677 ++++++++++++++++++++++++++++++------ rxgen/emit_py_module.pm | 27 +- rxgen/emit_py_sync_funcs.pm | 351 ++++++++++++------- rxgen/emit_py_types.pm | 337 ++++++++++++------ rxgen/rxgen.pl | 497 ++++++++++++++++++-------- vl-test.py | 2 +- 16 files changed, 1888 insertions(+), 610 deletions(-) create mode 100644 circ_buf.h diff --git a/Makefile b/Makefile index 3de2872..33be856 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,18 @@ CFLAGS := $(shell python3-config --cflags) +RXGEN := ./rxgen/rxgen.pl $(wildcard ./rxgen/*.pm) -pykafs.so: afs_xg.c afs_py.c afs_py.h +GENERATED := afs_xg.c afs_xg.h afs_py.c afs_py.h + +pykafs.so: $(GENERATED) python3 compile_pykafs.py build -AFS_API := afs.h afs_vl.h afs_fs.h afs_cm.h +AFS_API := rpc-api/afsuuid.h rpc-api/vldb.xg -afs_xg.c afs_py.c afs_py.h: $(AFS_API) ./rxgen/rxgen.pl +$(GENERATED): $(AFS_API) $(RXGEN) ./rxgen/rxgen.pl $(AFS_API) clean: find \( -name "*~" -o -name "*.o" -o -name "*.so" \) -delete rm -rf build/ - rm -f afs_xg.c afs_py.c afs_py.h + rm -f $(GENERATED) diff --git a/af_rxrpc.c b/af_rxrpc.c index ca5fa6a..a65cf63 100644 --- a/af_rxrpc.c +++ b/af_rxrpc.c @@ -396,38 +396,54 @@ void rx_close_connection(struct rx_connection *z_conn) */ int rxrpc_send_request(struct rx_connection *z_conn, struct rx_call *call, - const net_xdr_t *request, - size_t request_size, - size_t reply_buf_size) + struct iovec *request, + int request_ioc) { struct msghdr msg; - struct iovec iov[2]; size_t ctrllen; unsigned char control[128]; - call->got_eor = 0; - call->error_code = 0; - call->abort_code = 0; - call->reply_buf_size = reply_buf_size; - call->reply_size = 0; - /* request an operation */ ctrllen = 0; RXRPC_ADD_CALLID(control, ctrllen, (unsigned long)call); - iov[0].iov_base = (void *)request; - iov[0].iov_len = request_size; - msg.msg_name = &z_conn->peer; msg.msg_namelen = sizeof(z_conn->peer); - msg.msg_iov = iov; - msg.msg_iovlen = 1; + msg.msg_iov = request; + msg.msg_iovlen = request_ioc; msg.msg_control = control; msg.msg_controllen = ctrllen; msg.msg_flags = 0; //dump_cmsg(&msg); + if (0) { + const uint8_t *p; + unsigned i, j; + + printf("NAME %02u [", msg.msg_namelen); + p = (const void *)msg.msg_name; + for (i = 0; i < msg.msg_namelen; i++) + printf("%02x", *p++); + printf("]\n"); + + for (j = 0; j < msg.msg_iovlen; j++) { + printf("IOV[%02u] %04zu [", i, msg.msg_iov[j].iov_len); + p = (const void *)msg.msg_iov[j].iov_base; + for (i = 0; i < msg.msg_iov[j].iov_len; i++) + printf("%02x", *p++); + printf("]\n"); + } + + printf("CTRL %02zu [", msg.msg_controllen); + p = (const void *)msg.msg_control; + for (i = 0; i < msg.msg_controllen; i++) + printf("%02x", *p++); + printf("]\n"); + + printf("FLAGS %x\n", msg.msg_flags); + } + return sendmsg(z_conn->fd, &msg, 0) == -1 ? -1 : 0; } @@ -441,26 +457,39 @@ int rxrpc_wait_for_sync_reply(struct rx_connection *z_conn, struct cmsghdr *cmsg; struct msghdr msg; struct iovec iov[2]; - size_t space; - unsigned char control[128], overrun[sizeof(long)]; + unsigned char control[128]; int ret; /* wait for a reply */ - while (!call->got_eor) { - space = call->reply_buf_size - call->reply_size; - iov[0].iov_base = call->reply + call->reply_size; - iov[0].iov_len = space; - iov[1].iov_base = &overrun; - iov[1].iov_len = sizeof(overrun); - - if (iov[0].iov_len > 0) { - msg.msg_iov = iov; - msg.msg_iovlen = 2; + while (!call->got_eor && + CIRC_CNT(call->head, call->tail, call->size) < call->need_size + ) { + struct iovec *piov = iov; + unsigned size = call->size; + unsigned mask = size - 1; + unsigned head = call->head & mask; + unsigned tail = call->tail & mask; + unsigned space = CIRC_SPACE(head, tail, size); + + if (head >= tail) { + piov->iov_base = (void *)call->reply + head; + piov->iov_len = size - head; + piov++; + space -= size - (head & mask); + if (space > 0) { + piov->iov_base = (void *)call->reply; + piov->iov_len = space; + piov++; + } } else { - msg.msg_iov = iov + 1; - msg.msg_iovlen = 1; + piov->iov_base = (void *)call->reply + head; + piov->iov_len = space; + piov++; } + msg.msg_iov = iov; + msg.msg_iovlen = piov - iov; + memcpy(&srx, &z_conn->peer, sizeof(struct sockaddr_rxrpc)); msg.msg_name = &srx; msg.msg_namelen = sizeof(srx); @@ -498,7 +527,7 @@ int rxrpc_wait_for_sync_reply(struct rx_connection *z_conn, sizeof(call->abort_code)); z_conn->last_abort_code = call->abort_code; errno = ECONNABORTED; - return call->abort_code;; + return call->abort_code; case RXRPC_NET_ERROR: case RXRPC_LOCAL_ERROR: @@ -519,12 +548,7 @@ int rxrpc_wait_for_sync_reply(struct rx_connection *z_conn, } } - if (ret > space) { - errno = EMSGSIZE; - return -1; - } - - call->reply_size += ret; + call->head += ret; } return 0; diff --git a/circ_buf.h b/circ_buf.h new file mode 100644 index 0000000..27da8f6 --- /dev/null +++ b/circ_buf.h @@ -0,0 +1,30 @@ +/* + * See Documentation/circular-buffers.txt for more information. + */ + +#ifndef _LINUX_CIRC_BUF_H +#define _LINUX_CIRC_BUF_H 1 + +/* Return count in buffer. */ +#define CIRC_CNT(head,tail,size) (((head) - (tail)) & ((size)-1)) + +/* Return space available, 0..size-1. We always leave one free char + as a completely full buffer has head == tail, which is the same as + empty. */ +#define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size)) + +/* Return count up to the end of the buffer. Carefully avoid + accessing head and tail more than once, so they can change + underneath us without returning inconsistent results. */ +#define CIRC_CNT_TO_END(head,tail,size) \ + ({int end = (size) - (tail); \ + int n = ((head) + end) & ((size)-1); \ + n < end ? n : end;}) + +/* Return space available up to the end of the buffer. */ +#define CIRC_SPACE_TO_END(head,tail,size) \ + ({int end = (size) - 1 - (head); \ + int n = (end + (tail)) & ((size)-1); \ + n <= end ? n : end+1;}) + +#endif /* _LINUX_CIRC_BUF_H */ diff --git a/compile_pykafs.py b/compile_pykafs.py index c5af6ed..0b48c7f 100644 --- a/compile_pykafs.py +++ b/compile_pykafs.py @@ -1,10 +1,10 @@ from distutils.core import setup, Extension setup(name="kafs", version="0.1", ext_modules=[Extension("kafs", - sources = [ "kafs.c", + sources = [ "afs_xg.c", + "kafs.c", "afs_py.c", "py_rxgen.c", - "afs_xg.c", "af_rxrpc.c" ], )]) diff --git a/py_rxgen.c b/py_rxgen.c index 8f94e6a..b05ca71 100644 --- a/py_rxgen.c +++ b/py_rxgen.c @@ -15,6 +15,50 @@ #include "py_rxgen.h" #include "rxgen.h" +PyObject *py_rxgen_get_struct(const void *p, PyObject **cache, + PyObject *(*data_to_type)(const void *elem)) +{ + PyObject *obj; + + if (*cache) { + Py_INCREF(*cache); + return *cache; + } + + obj = data_to_type(p); + if (!obj) + return NULL; + + Py_INCREF(obj); + *cache = obj; + return obj; +} + +int py_rxgen_set_struct(PyObject **cache, PyTypeObject *type, PyObject *val) +{ + if (!PyObject_TypeCheck(val, type)) { + PyErr_Format(PyExc_TypeError, "Unexpected type"); + return -1; + } + Py_XDECREF(*cache); + Py_INCREF(val); + *cache = val; + return 0; +} + +int py_rxgen_premarshal_struct(void *array, size_t size, size_t offs, + PyObject *cache, + int (*premarshal)(PyObject *object)) +{ + if (!cache) + return 0; + + if (premarshal(cache) < 0) + return -1; + memcpy(array, (void *)cache + offs, size); + return 0; +} + PyObject *py_rxgen_get_string(const void *_array, size_t n) { const char *array = _array; @@ -42,13 +86,18 @@ int py_rxgen_set_string(void *_array, size_t n, PyObject *val) return 0; } -PyObject *py_rxgen_get_uint8(const void *_array, size_t n) +PyObject *py_rxgen_get_uint8(const void *_array, size_t n, PyObject **cache) { PyObject *list; const uint8_t *array = _array; int i; - list = PyTuple_New(n); + if (*cache) { + Py_INCREF(*cache); + return *cache; + } + + list = PyList_New(n); if (!list) return NULL; @@ -57,11 +106,14 @@ PyObject *py_rxgen_get_uint8(const void *_array, size_t n) if (!num) goto error; - if (PyTuple_SetItem(list, i, num) != 0) { + if (PyList_SetItem(list, i, num) != 0) { Py_DECREF(num); goto error; } } + + Py_INCREF(list); + *cache = list; return list; error: @@ -69,13 +121,18 @@ error: return NULL; } -PyObject *py_rxgen_get_uint16(const void *_array, size_t n) +PyObject *py_rxgen_get_uint16(const void *_array, size_t n, PyObject **cache) { PyObject *list; const uint16_t *array = _array; int i; - list = PyTuple_New(n); + if (*cache) { + Py_INCREF(*cache); + return *cache; + } + + list = PyList_New(n); if (!list) return NULL; @@ -84,11 +141,14 @@ PyObject *py_rxgen_get_uint16(const void *_array, size_t n) if (!num) goto error; - if (PyTuple_SetItem(list, i, num) != 0) { + if (PyList_SetItem(list, i, num) != 0) { Py_DECREF(num); goto error; } } + + Py_INCREF(list); + *cache = list; return list; error: @@ -96,13 +156,18 @@ error: return NULL; } -PyObject *py_rxgen_get_uint32(const void *_array, size_t n) +PyObject *py_rxgen_get_uint32(const void *_array, size_t n, PyObject **cache) { PyObject *list; const uint32_t *array = _array; int i; - list = PyTuple_New(n); + if (*cache) { + Py_INCREF(*cache); + return *cache; + } + + list = PyList_New(n); if (!list) return NULL; @@ -111,11 +176,14 @@ PyObject *py_rxgen_get_uint32(const void *_array, size_t n) if (!num) goto error; - if (PyTuple_SetItem(list, i, num) != 0) { + if (PyList_SetItem(list, i, num) != 0) { Py_DECREF(num); goto error; } } + + Py_INCREF(list); + *cache = list; return list; error: @@ -123,33 +191,201 @@ error: return NULL; } -int py_rxgen_set_uint32(void *_array, size_t n, PyObject *val) +int py_rxgen_set_array(size_t n, PyObject **cache, PyObject *list) { - uint32_t *array = _array; - int i; + if (!PySequence_Check(list) || + PySequence_Size(list) > n) { + PyErr_Format(PyExc_ValueError, + "Expected a sequence of up to %zu size", n); + return -1; + } + Py_XDECREF(*cache); + Py_INCREF(list); + *cache = list; + return 0; +} + +int py_rxgen_premarshal_uint8(void *_array, size_t n, PyObject *cache) +{ + PyObject *list; + uint8_t *array = _array; + Py_ssize_t i, c; - if (!PyTuple_Check(val)) { - PyErr_Format(PyExc_TypeError, - "Expected a tuple"); + if (!cache) + return 0; + + list = PySequence_Fast(cache, "Expecting list or tuple of integers"); + if (!list) return -1; + c = PySequence_Fast_GET_SIZE(list); + if (c > n) { + PyErr_Format(PyExc_ValueError, + "Expected a sequence of up to %zu size", n); + goto error; + } + + PyErr_Clear(); + for (i = 0; i < c; i++) { + PyObject *p = PySequence_Fast_GET_ITEM(list, i); + unsigned long val = PyLong_AsUnsignedLong(p); + + if (PyErr_Occurred()) + goto error; + array[i] = val; } - if (PyTuple_GET_SIZE(val) != n) { + for (; i < n; i++) + array[i] = 0; + + Py_DECREF(list); + return 0; + +error: + Py_DECREF(list); + return -1; +} + +int py_rxgen_premarshal_uint16(void *_array, size_t n, PyObject *cache) +{ + PyObject *list; + uint16_t *array = _array; + Py_ssize_t i, c; + + if (!cache) + return 0; + + list = PySequence_Fast(cache, "Expecting list or tuple of integers"); + if (!list) + return -1; + c = PySequence_Fast_GET_SIZE(list); + if (c > n) { PyErr_Format(PyExc_ValueError, - "Expected a tuple of %zu size", n); + "Expected a sequence of up to %zu size", n); + goto error; + } + + PyErr_Clear(); + for (i = 0; i < c; i++) { + PyObject *p = PySequence_Fast_GET_ITEM(list, i); + unsigned long val = PyLong_AsUnsignedLong(p); + + if (PyErr_Occurred()) + goto error; + array[i] = val; + } + for (; i < n; i++) + array[i] = 0; + + Py_DECREF(list); + return 0; + +error: + Py_DECREF(list); + return -1; +} + +int py_rxgen_premarshal_uint32(void *_array, size_t n, PyObject *cache) +{ + PyObject *list; + uint32_t *array = _array; + Py_ssize_t i, c; + + if (!cache) + return 0; + + list = PySequence_Fast(cache, "Expecting list or tuple of integers"); + if (!list) return -1; + c = PySequence_Fast_GET_SIZE(list); + if (c > n) { + PyErr_Format(PyExc_ValueError, + "Expected a sequence of up to %zu size", n); + goto error; } PyErr_Clear(); - for (i = 0; i < n; i++) { - PyObject *p = PyTuple_GET_ITEM(val, i); + for (i = 0; i < c; i++) { + PyObject *p = PySequence_Fast_GET_ITEM(list, i); unsigned long val = PyLong_AsUnsignedLong(p); if (PyErr_Occurred()) - return -1; + goto error; array[i] = val; } + for (; i < n; i++) + array[i] = 0; + + Py_DECREF(list); + return 0; + +error: + Py_DECREF(list); + return -1; +} + +PyObject *py_rxgen_get_structs(const void *data, size_t num, size_t size, + PyObject **cache, + PyObject *(*data_to_type)(const void *elem)) +{ + PyObject *list, *obj; + int i; + + if (*cache) { + Py_INCREF(*cache); + return *cache; + } + + list = PyList_New(num); + if (!list) + return NULL; + + for (i = 0; i < num; i++) { + obj = data_to_type(data + num * size); + if (!obj) { + Py_DECREF(list); + return NULL; + } + PyList_SetItem(list, i, obj); + } + + Py_INCREF(list); + *cache = list; + return list; +} + +int py_rxgen_premarshal_structs(void *array, + size_t n, size_t size, size_t offs, + PyObject *cache, + int (*premarshal)(PyObject *object)) +{ + PyObject *list; + Py_ssize_t i, c; + + list = PySequence_Fast(cache, "Expecting list or tuple of structs"); + if (!list) + return -1; + c = PySequence_Fast_GET_SIZE(list); + if (c > n) { + PyErr_Format(PyExc_ValueError, + "Expected a sequence of up to %zu size", n); + goto error; + } + for (i = 0; i < c; i++) { + PyObject *p = PySequence_Fast_GET_ITEM(list, i); + if (premarshal(p) < 0) + goto error; + memcpy(array + i * size, (void *)p + offs, size); + } + + if (i < n) + memset(array + i * size, 0, (n - i) * size); + + Py_DECREF(list); return 0; + +error: + Py_DECREF(list); + return -1; } /* diff --git a/py_rxgen.h b/py_rxgen.h index bbe8392..6391379 100644 --- a/py_rxgen.h +++ b/py_rxgen.h @@ -21,14 +21,46 @@ extern PyTypeObject py_rx_connectionType; extern PyObject *kafs_py_rx_new_connection(PyObject *, PyObject *); -extern PyObject *py_rxgen_get_string(const void *_p, size_t n); -extern PyObject *py_rxgen_get_uint8(const void *_p, size_t n); -extern PyObject *py_rxgen_get_uint16(const void *_p, size_t n); -extern PyObject *py_rxgen_get_uint32(const void *_p, size_t n); +/* + * Single embedded struct handling + */ +extern PyObject *py_rxgen_get_struct(const void *p, PyObject **cache, + PyObject *(*data_to_type)(const void *elem)); +extern int py_rxgen_set_struct(PyObject **cache, PyTypeObject *type, PyObject *val); +extern int py_rxgen_premarshal_struct(void *p, size_t size, size_t offs, + PyObject *cache, + int (*premarshal)(PyObject *object)); +/* + * Embedded string (char array) handling + */ +extern PyObject *py_rxgen_get_string(const void *_p, size_t n); extern int py_rxgen_set_string(void *_p, size_t n, PyObject *val); -extern int py_rxgen_set_uint8(void *_p, size_t n, PyObject *val); -extern int py_rxgen_set_uint16(void *_p, size_t n, PyObject *val); -extern int py_rxgen_set_uint32(void *_p, size_t n, PyObject *val); + +/* + * Embedded general array handling + */ +extern int py_rxgen_set_array(size_t n, PyObject **cache, PyObject *val); + +/* + * Embedded integer array handling + */ +extern PyObject *py_rxgen_get_uint8(const void *_array , size_t n, PyObject **cache); +extern PyObject *py_rxgen_get_uint16(const void *_array, size_t n, PyObject **cache); +extern PyObject *py_rxgen_get_uint32(const void *_array, size_t n, PyObject **cache); + +extern int py_rxgen_premarshal_uint8(void *_array, size_t n, PyObject *cache); +extern int py_rxgen_premarshal_uint16(void *_array, size_t n, PyObject *cache); +extern int py_rxgen_premarshal_uint32(void *_array, size_t n, PyObject *cache); + +/* + * Embedded struct array handling + */ +extern PyObject *py_rxgen_get_structs(const void *data, size_t num, size_t size, + PyObject **cache, + PyObject *(*data_to_type)(const void *elem)); +extern int py_rxgen_premarshal_structs(void *array, size_t n, size_t size, size_t offs, + PyObject *cache, + int (*premarshal)(PyObject *object)); #endif /* _PY_RXGEN_H */ diff --git a/rpc-api/afsuuid.h b/rpc-api/afsuuid.h index acd2bb5..6b04576 100644 --- a/rpc-api/afsuuid.h +++ b/rpc-api/afsuuid.h @@ -34,10 +34,6 @@ %#ifndef _AFSUUID_COMMON_ %#define _AFSUUID_COMMON_ -#ifndef AFSUUID_GENERATE -#define AFSUUID_GENERATE __attribute__((__nogenerate__)) -#endif - struct afsUUID { uint32_t time_low; uint16_t time_mid; @@ -45,6 +41,6 @@ struct afsUUID { uint8_t clock_seq_hi_and_reserved; uint8_t clock_seq_low; uint8_t node[6]; -} AFSUUID_GENERATE; +}; %#endif /* _AFSUUID_COMMON_ */ diff --git a/rpc-api/vldb.xg b/rpc-api/vldb.xg index 6e72078..326e209 100644 --- a/rpc-api/vldb.xg +++ b/rpc-api/vldb.xg @@ -39,6 +39,9 @@ package VL_ #include "common.h" +const VL_PORT = 7003; /* volume location service port */ +const VL_SERVICE = 52; /* RxRPC service ID for the Volume Location service */ + /* * Structures and defines for vldb data */ diff --git a/rxgen.h b/rxgen.h index 2637dc9..4aa8f50 100644 --- a/rxgen.h +++ b/rxgen.h @@ -13,6 +13,7 @@ #define _RXGEN_H #include "af_rxrpc.h" +#include "circ_buf.h" typedef uint32_t net_xdr_t; @@ -26,11 +27,24 @@ struct rx_call { int got_eor; int error_code; uint32_t abort_code; - unsigned reply_buf_size; - unsigned reply_size; - net_xdr_t *reply; + int phase; + unsigned need_size; + unsigned need_bulk_count; + unsigned item_per_buf; + unsigned bulk_index; + + /* Circular buffer holding received data (power-of-two size) */ + unsigned head; + unsigned tail; + unsigned size; + net_xdr_t reply[]; }; +static inline size_t rxgen_call_buffer_space_to_end(struct rx_call *call) +{ + return CIRC_SPACE_TO_END(call->head, call->tail, call->size); +} + extern struct rx_connection *rx_new_connection(const struct sockaddr *sa, socklen_t salen, uint16_t service, @@ -42,9 +56,8 @@ extern void rx_close_connection(struct rx_connection *z_conn); extern int rxrpc_send_request(struct rx_connection *z_conn, struct rx_call *call, - const net_xdr_t *request, - size_t request_size, - size_t reply_buf_size); + struct iovec *request, + int request_ioc); extern int rxrpc_wait_for_sync_reply(struct rx_connection *z_conn, struct rx_call *call); diff --git a/rxgen/emit_c_struct.pm b/rxgen/emit_c_struct.pm index 825f5c9..75bab2b 100644 --- a/rxgen/emit_c_struct.pm +++ b/rxgen/emit_c_struct.pm @@ -8,37 +8,84 @@ # 2 of the Licence, or (at your option) any later version. # +############################################################################### +# +# Emit structure encoders and decoders predeclarations +# +############################################################################### +sub emit_struct_encdec_decls ($) { + my ($struct) = @_; + + print RXOUT "/* ", $struct->{type}, " XDR size ", $struct->{xdr_size}, " */\n" +} + ############################################################################### # # Emit structure encoders and decoders # ############################################################################### -sub emit_struct_encdec ($@) { - my ($struct, @members) = @_; +sub emit_struct_encdec ($) { + my ($struct) = @_; # Dump the banner comment block + print RXHDR "\n"; + print RXHDR @{$struct->{banner}}; print RXOUT "\n"; - print RXOUT @{shift @members}; + print RXOUT @{$struct->{banner}}; + + # Write out a C structure definition for this type + print RXHDR "struct ", $struct->{type}, " {\n"; + foreach my $m (@{$struct->{members}}) { + if ($m->{class} eq "basic") { + print RXHDR "\t", $m->{type}, "\t", $m->{name}; + } elsif ($m->{class} eq "struct") { + print RXHDR "\tstruct ", $m->{type}, "\t", $m->{name}; + } elsif ($m->{class} eq "array") { + if ($m->{elem}->{class} eq "basic") { + print RXHDR "\t", $m->{elem}->{type}, "\t", $m->{name}, "[", $m->{dim}, "]"; + } else { + print RXHDR "\tstruct ", $m->{elem}->{type}, "\t", $m->{name}, "[", $m->{dim}, "]"; + } + } else { + die $m->{where}, ": Unsupported type class '", $m->{class}, "'\n"; + } + print RXHDR ";\n"; + } + print RXHDR "};\n"; # Write an encoding function - print RXOUT "static net_xdr_t *rxgen_encode_", $struct, "(net_xdr_t *xdr, const struct $struct *p)\n"; + print RXOUT "static __attribute__((unused))\n"; + print RXOUT "net_xdr_t *rxgen_encode_", $struct->{type}, "(net_xdr_t *xdr, const struct ", $struct->{type}, " *p)\n"; print RXOUT "{\n"; - foreach my $m (@members) { - my ($type, $name, $array_size, $max_size) = @{$m}; - if ($array_size != -1) { + foreach my $m (@{$struct->{members}}) { + if ($m->{class} eq "array") { print RXOUT "\tint i;\n\n"; last; } } - foreach my $m (@members) { - my ($type, $name, $array_size, $max_size) = @{$m}; - if ($array_size != -1) { - print RXOUT "\tfor (i = 0; i < ", $array_size, "; i++)\n"; - print RXOUT "\t\t*xdr++ = htonl(p->", $name, "[i]);\n"; + foreach my $m (@{$struct->{members}}) { + if ($m->{class} eq "basic") { + if ($m->{type} !~ /64/) { + print RXOUT "\t*xdr++ = htonl(p->", $m->{name}, ");\n"; + } else { + die $m->{where}, ": No encoding for type '", $m->{type}, "'"; + } + } elsif ($m->{class} eq "struct") { + print RXOUT "\txdr = rxgen_encode_", $m->{type}, "(xdr, &p->", $m->{name}, ");\n"; + } elsif ($m->{class} eq "array") { + print RXOUT "\tfor (i = 0; i < ", $m->{dim}, "; i++)\n"; + if ($m->{elem}->{class} eq "basic" && $m->{elem}->{type} !~ /64/) { + print RXOUT "\t\t*xdr++ = htonl(p->", $m->{name}, "[i]);\n"; + } elsif ($m->{elem}->{class} eq "struct") { + print RXOUT "\t\txdr = rxgen_encode_", $m->{elem}->{type}, + "(xdr, &p->", $m->{name}, "[i]);\n"; + } else { + die $m->{where}, ": No encoding for array type '", $m->{elem}->{type}, "'"; + } } else { - print RXOUT "\t*xdr++ = htonl(p->", $name, ");\n"; + die $m->{where}, "No encoding for type class '$class'"; } } @@ -47,31 +94,42 @@ sub emit_struct_encdec ($@) { print RXOUT "\n"; # Write a decoding function - print RXOUT "static const net_xdr_t *rxgen_decode_", $struct, "(struct $struct *p, const net_xdr_t *xdr)\n"; + print RXOUT "static __attribute__((unused))\n"; + print RXOUT "unsigned rxgen_decode_", $struct->{type}, "(struct ", $struct->{type}, " *p, const net_xdr_t *xdr, unsigned tail, unsigned mask)\n"; print RXOUT "{\n"; - foreach my $m (@members) { - my ($type, $name, $array_size, $max_size) = @{$m}; - if ($array_size != -1) { + foreach my $m (@{$struct->{members}}) { + if ($m->{class} eq "array") { print RXOUT "\tint i;\n\n"; last; } } - foreach my $m (@members) { - my ($type, $name, $array_size, $max_size) = @{$m}; - if ($array_size != -1) { - print RXOUT "\tfor (i = 0; i < ", $array_size, "; i++)\n"; - print RXOUT "\t\tp->", $name, "[i] = ntohl(*xdr++);\n"; + foreach my $m (@{$struct->{members}}) { + if ($m->{class} eq "basic") { + if ($m->{type} !~ /64/) { + print RXOUT "\tp->", $m->{name}, " = ntohl(xdr[tail++ & mask]);\n"; + } else { + die $m->{where}, "No decoding for type '$type'"; + } + } elsif ($m->{class} eq "struct") { + print RXOUT "\ttail = rxgen_decode_", $m->{type}, "(&p->", $m->{name}, ", xdr, tail, mask);\n"; + } elsif ($m->{class} eq "array") { + print RXOUT "\tfor (i = 0; i < ", $m->{dim}, "; i++)\n"; + if ($m->{elem}->{class} eq "basic" && $m->{elem}->{type} !~ /64/) { + print RXOUT "\t\tp->", $m->{name}, "[i] = ntohl(xdr[tail++ & mask]);\n"; + } elsif ($m->{elem}->{class} eq "struct") { + print RXOUT "\t\ttail = rxgen_decode_", $m->{elem}->{type}, "(&p->", $m->{name}, "[i], xdr, tail, mask);\n"; + } else { + die $m->{where}, "No decoding for array type '$type'"; + } } else { - print RXOUT "\tp->", $name, " = ntohl(*xdr++);\n"; + die $m->{where}, "No decoding for type class '$class'"; } } - print RXOUT "\treturn xdr;\n"; + print RXOUT "\treturn tail;\n"; print RXOUT "}\n"; - print RXOUT "\n"; - print RXOUT "/* XDR size ", $struct_sizes{$struct}, " */\n" } 1; diff --git a/rxgen/emit_c_sync_funcs.pm b/rxgen/emit_c_sync_funcs.pm index aaa939f..ba21fd0 100644 --- a/rxgen/emit_c_sync_funcs.pm +++ b/rxgen/emit_c_sync_funcs.pm @@ -8,139 +8,599 @@ # 2 of the Licence, or (at your option) any later version. # +############################################################################### +# +# Calculate the C function prototypes +# +############################################################################### +sub calc_func_prototype($) +{ + my ($func) = @_; + + # Function prototype lists (we add commas and the closing bracket later) + my @protos = ( "int " . $func->{name} . "(\n" ); + my @send_protos = ( "int rxgen_send_request_" . $func->{name} . "(\n" ); + my @recv_protos = ( "int rxgen_decode_reply_" . $func->{name} . "(\n" ); + push @protos, "\tstruct rx_connection *z_conn"; + push @send_protos, "\tstruct rx_connection *z_conn"; + push @send_protos, "\tstruct rx_call *call"; + push @recv_protos, "\tstruct rx_connection *z_conn"; + push @recv_protos, "\tstruct rx_call *call"; + + # Arguments to pass when sending a call or processing a reply + my @send_args = (); + my @recv_args = (); + + foreach my $p (@{$func->{params}}) { + my @lines = (); + my @args = (); + + $proto = "\t /*" . $p->{dir} . "*/ "; + if ($p->{class} eq "array") { + die $p->{where}, ": Array arg not supported"; + } elsif ($p->{class} eq "bulk" && + !($p->{elem}->{class} eq "string" || + $p->{elem}->{class} eq "opaque") + ) { + if ($p->{dir} eq "OUT") { + if ($p->{elem}->{class} eq "struct") { + $proto .= "int (*alloc__" . $p->{name} . ")(void *token, int index, "; + $proto .= "struct " . $p->{type} . " **p_object)"; + push @args, "alloc__" . $p->{name}; + } else { + $proto .= "int (*store__" . $p->{name} . ")(void *token, int index, "; + $proto .= $p->{type} . " *value)"; + push @args, "store__" . $p->{name}; + } + push @lines, $proto; + $proto = "\t\t void *token__" . $p->{name}; + push @args, "token__" . $p->{name}; + } else { + if ($p->{elem}->{class} eq "struct") { + $proto .= "int (*get__" . $p->{name} . ")(void *token, int index, "; + $proto .= "const struct " . $p->{type} . " **object)"; + } else { + $proto .= "int (*get__" . $p->{name} . ")(void *token, int index, "; + $proto .= $p->{type} . " *object)"; + } + push @lines, $proto; + $proto = "\t\tvoid *token__" . $p->{name}; + push @lines, $proto; + $proto = "\t\tsize_t nr__" . $p->{name}; + push @args, "get__" . $p->{name}, "token__" . $p->{name}; + push @args, "nr__" . $p->{name}; + } + } else { + if ($p->{class} eq "bulk" && $p->{elem}->{class} ne "string") { + $proto .= "size_t nr_" . $p->{name} . ", "; + push @args, "nr__" . $p->{name}; + } + $proto .= "const " if ($p->{dir} eq "IN" && $p->{class} ne "basic"); + $proto .= "struct " if ($p->{class} eq "struct"); + $proto .= $p->{type} . " " . $p->{ptr} . $p->{name}; + push @args, $p->{name}; + } + + push @lines, $proto; + push @protos, @lines; + push @send_protos, @lines unless ($p->{dir} eq "OUT"); + push @recv_protos, @lines unless ($p->{dir} eq "IN"); + push @send_args, @args unless ($p->{dir} eq "OUT"); + push @recv_args, @args unless ($p->{dir} eq "IN"); + } + + # Terminate each line with a comma, excepting the last, which we terminate + # with a closing bracket. + for (my $i = 1; $i < $#protos; $i++) { + $protos[$i] .= ",\n"; + } + $protos[$#protos] .= ")"; + + for (my $i = 1; $i < $#send_protos; $i++) { + $send_protos[$i] .= ",\n"; + } + $send_protos[$#send_protos] .= ")"; + + for (my $i = 1; $i < $#recv_protos; $i++) { + $recv_protos[$i] .= ",\n"; + } + $recv_protos[$#recv_protos] .= ")"; + + $func->{protos} = \@protos; + $func->{send_protos} = \@send_protos; + $func->{recv_protos} = \@recv_protos; + $func->{send_args} = \@send_args; + $func->{recv_args} = \@recv_args; +} + ############################################################################### # # Emit a function to encode a request # ############################################################################### -sub emit_func_enc_request($$$$$@) +sub emit_func_send_request($) { - my ($func, $op, $request_size, $req_has_charptr, $reply_size, @request) = @_; + my ($func) = @_; - # Function definition and arguments - print RXOUT "int rxgen_send_request_", $func, "(\n"; - print RXOUT "\tstruct rx_connection *z_conn,\n"; - print RXOUT "\tstruct rx_call *call"; - foreach my $p (@request) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - print RXOUT ",\n\t"; - print RXOUT "const " if ($type =~ "[*]"); - print RXOUT "$type $name"; - die "Array arg not supported '$name'" if ($array_size != -1); - } - print RXOUT ")\n"; + # Count up how many chunks we're going to need to send (iovec components) + # and how much XDR buffer size we're going to need. + my $xdr_bufsize = 0; + my $need_tmp = 0; + my @iovs = (); + my $iov = [ "buf + 0" ]; + my $iov_size = 4; + foreach my $p (@{$func->{request}}) { + $iov = [ "buf + " . $xdr_bufsize / 4 ] if (!$iov); - # Function body, beginning with local variables + $p->{enc_ioc} = $#iovs + 1; + + if ($p->{class} eq "bulk") { + $iov_size += 4; # Element count + $xdr_bufsize += $iov_size; + push @{$iov}, "$iov_size"; + $iov_size = 0; + push @iovs, $iov; + $iov = 0; + + push @iovs, [ ($p->{elem}->{class} eq "string" || + $p->{elem}->{class} eq "opaque") ? "(void *)" . $p->{name} : "NULL", + "nr__" . $p->{name} . " * " . $p->{xdr_size} ]; + if ($p->{elem}->{xdr_size} & 3 != 0) { + # Allocate space to use as padding + $need_tmp = 1; + $xdr_bufsize += 4; + $iov = [ "buf + " . $xdr_bufsize / 4 ]; + $iov_size = 0; + } + } else { + $iov_size += $p->{xdr_size}; + } + } + + if ($iov) { + $xdr_bufsize += $iov_size; + push @{$iov}, "$iov_size"; + push @iovs, $iov; + } + + die if ($#iovs < 0); + + # Function definition and arguments + foreach $proto (@{$func->{send_protos}}) { + print RXOUT $proto; + } + print RXOUT "\n"; print RXOUT "{\n"; - if ($req_has_charptr) { - print RXOUT "\tnet_xdr_t request[", $request_size / 4, " + 1], *xdr;\n"; - print RXOUT "\tuint32_t tmp;\n"; - } else { - print RXOUT "\tnet_xdr_t request[", $request_size / 4, "], *xdr;\n"; + + my @bulk_params = (); + foreach my $p (@{$func->{request}}) { + if ($p->{class} eq "bulk" && + $p->{elem}->{class} ne "string" && + $p->{elem}->{class} ne "opaque" + ) { + push @bulk_params, $p; + } + } + + # Local variables + foreach my $p (@{$func->{request}}) { + if ($p->{class} eq "bulk" && $p->{elem}->{class} eq "string") { + print RXOUT "\tsize_t nr__", $p->{name}, " = strlen(", $p->{name}, ");\n"; + } + } + print RXOUT "\tnet_xdr_t buf[", $xdr_bufsize / 4, "];\n"; + print RXOUT "\tstruct iovec iov[", $#iovs + 1, "] = {\n"; + for my $iov (@iovs) { + print RXOUT "\t\t{ ", join(", ", @{$iov}), " },\n"; + } + print RXOUT "\t};\n"; + + if ($func->{req_has_charptr} || @bulk_params) { + print RXOUT "\tsize_t tmp;\n"; + } + print RXOUT "\tint ret;\n"; + + # If we have bulk data that isn't string/opaque, we need a buffer to encode into + if (@bulk_params) { + print RXOUT "\tsize_t bulk_size = 0;\n"; + print RXOUT "\tvoid *bulk_buffer = NULL;\n"; + print RXOUT "\tnet_xdr_t *xdr;\n"; + print RXOUT "\n"; + foreach my $p (@bulk_params) { + print RXOUT "\tbulk_size += nr__", $p->{name}, " * ", $p->{elem}->{xdr_size}, ";\n"; + } + + print RXOUT "\tif (bulk_size > 0) {\n"; + print RXOUT "\t\tbulk_buffer = malloc(bulk_size);\n"; + print RXOUT "\t\tif (!bulk_buffer)\n"; + print RXOUT "\t\t\treturn -1;\n"; + + print RXOUT "\n"; + print RXOUT "\t\tbulk_size = 0;\n"; + foreach my $p (@bulk_params) { + print RXOUT "\t\tiov[", $p->{enc_ioc} + 1, "].iov_base = bulk_buffer + bulk_size;\n"; + print RXOUT "\t\tbulk_size += nr__", $p->{name}, " * ", $p->{elem}->{xdr_size}, ";\n"; + } + + print RXOUT "\t}\n"; } # Marshal the data + my $ix = 4; + print RXOUT "\n"; - print RXOUT "\txdr = request;\n"; - print RXOUT "\t*xdr++ = htonl($op);\n"; - foreach my $p (@request) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - if ($type eq "uint8_t" || - $type eq "uint16_t" || - $type eq "uint32_t") { - print RXOUT "\t*xdr++ = htonl($name);\n"; - } elsif ($type eq "char*") { - print RXOUT "\ttmp = strlen($name);\n"; - print RXOUT "\t*xdr++ = htonl(tmp);\n"; - print RXOUT "\txdr[tmp / 4] = 0;\n"; - print RXOUT "\tmemcpy(xdr, $name, tmp);\n"; - print RXOUT "\txdr += (tmp + 3) / 4;\n"; - } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { - print RXOUT "\txdr = rxgen_encode_$1(xdr, $name);\n"; + #print RXOUT "\txdr = request;\n"; + print RXOUT "\tbuf[0] = htonl(", $func->{opcode}, ");\n"; + foreach my $p (@{$func->{request}}) { + if ($p->{class} eq "basic" && $p->{type} !~ /64/) { + print RXOUT "\tbuf[", $ix/4, "] = htonl(", $p->{name}, ");\n"; + $ix += 4; + } elsif ($p->{class} eq "basic" && $p->{type} =~ /64/) { + print RXOUT "\tbuf[", $ix/4, "] = htonl((unsigned long)", $p->{name}, ");\n"; + $ix += 4; + print RXOUT "\tbuf[", $ix/4, "] = htonl((unsigned long)(", $p->{name}, " >> 32));\n"; + $ix += 4; + } elsif ($p->{class} eq "struct") { + print RXOUT "\trxgen_encode_", $p->{type}, "(buf + ", $ix/4, ", ", $p->{name}, ");\n"; + $ix += $p->{xdr_size}; + } elsif ($p->{class} eq "bulk" && ($p->{elem}->{class} eq "string" || + $p->{elem}->{class} eq "opaque")) { + if (exists $p->{dim}) { + print RXOUT "\tif (nr__", $p->{name}, " > ", $p->{dim}, ") {\n"; + print RXOUT "\t\terrno = EINVAL;\n"; + print RXOUT "\t\treturn -1;\n"; + print RXOUT "\t};\n"; + } + print RXOUT "\tbuf[", $ix/4, "] = htonl(nr__", $p->{name}, ");\n"; + $ix += 4; + print RXOUT "\tbuf[", $ix/4, "] = 0; /* end-of-object padding */\n"; + $ix += 4; + print RXOUT "\ttmp = (4 - (nr__", $p->{name}, " & 3)) & 3;\n"; + print RXOUT "\tiov[", $p->{enc_ioc} + 2, "].iov_len += tmp;\n"; + print RXOUT "\tiov[", $p->{enc_ioc} + 2, "].iov_base -= tmp;\n"; + } elsif ($p->{class} eq "bulk") { + print RXOUT "\txdr = iov[", $p->{enc_ioc} + 1, "].iov_base;\n"; + print RXOUT "\tfor (tmp = 0; tmp < nr__", $p->{name}, "; tmp++) {\n"; + if ($p->{elem}->{class} eq "basic" && $p->{elem}->{type} !~ /64/) { + print RXOUT "\t\t", $p->{elem}->{type}, " x;\n"; + print RXOUT "\t\tget__", $p->{name}, "(token__", $p->{name}, ", tmp, &x);\n"; + print RXOUT "\t\t*xdr++ = htonl(x);\n"; + } elsif ($p->{elem}->{class} eq "struct") { + print RXOUT "\t\txdr = rxgen_encode_", $p->{elem}->{type}, "(xdr, &p->", $p->{name}, "[i]);\n"; + } else { + die $p->{where}, "No decoding for array type '$type'"; + } + print RXOUT "\t}"; + } else { + die $p->{where}, ": Unsupported param encoding"; } } # Send the message print RXOUT "\n"; - print RXOUT "\treturn rxrpc_send_request(z_conn, call, request, (xdr - request) * 4, $reply_size);\n"; + print RXOUT "\tret = rxrpc_send_request(z_conn, call, iov, ", $#iovs + 1, ");\n"; + print RXOUT "\tfree(bulk_buffer);\n" if (@bulk_params); + print RXOUT "\treturn ret;\n"; print RXOUT "}\n"; } ############################################################################### # -# Emit a function to decode a reply +# Emit a function to decode a reply in a way that can be used from asynchronous +# code. # ############################################################################### -sub emit_func_dec_reply($$$@) +sub emit_func_decode_reply($) { - my ($func, $op, $reply_size, @reply) = @_; + my ($func) = @_; - print RXOUT "\n"; + # We fetch the data in a number of phases. Each phase receives a chunk of + # data of a certain size. A phase's size might be dependent on a variable + # in the previous phase. Variable-sized bulk arrays are split across + # multiple phases, with the length being at the end of the first phase and + # the data in the second. + my @phases = (); + my $phase = 0; + my $buf_size = 16; + my $have_bulk = 0; - # Function definition and arguments - print RXOUT "void rxgen_decode_reply_", $func, "(\n"; - print RXOUT "\tstruct rx_connection *z_conn,\n"; - print RXOUT "\tstruct rx_call *call"; - foreach my $p (@reply) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - print RXOUT ",\n\t"; - print RXOUT "$type $name"; - die "Array arg not supported '$name'" if ($array_size != -1); + foreach my $p (@{$func->{reply}}) { + unless ($phase) { + $phase = { type => "flat", size => 0, params => [] }; + push @phases, $phase; + } + + if ($p->{class} eq "basic" || + $p->{class} eq "struct" + ) { + $p->{reply_offset} = $phase->{size}; + $phase->{size} += $p->{xdr_size}; + push @{$phase->{params}}, $p; + } elsif ($p->{class} eq "bulk") { + die if ($p->{elem}->{class} eq "string" || + $p->{elem}->{class} eq "opaque"); + $have_bulk = 1; + + # Bulk objects begin with an element count + $p->{reply_offset} = $phase->{size}; + $phase->{elem_count} = $phase->{size}; + $phase->{size} += 4; + + my %pseudoparam = ( + class => "basic", + type => "bulk_size", + name => $p->{name}, + elem => $p->{elem}, + where => $p->{where}, + xdr_size => $p->{xdr_size}, + ); + push @{$phase->{params}}, \%pseudoparam; + + # Create a new phase + $phase = { + type => "bulk", + name => $p->{name}, + params => [ $p ], + xdr_size => $p->{xdr_size}, + }; + push @phases, $phase; + + # We don't want to be asking recvmsg() for one object at a time if + # they're really small. + my $n_buf = ($p->{xdr_size} < 1020) ? int(1020 / $p->{xdr_size}) : 1; + $n_buf *= $p->{xdr_size}; + $buf_size = $n_buf if ($buf_size < $n_buf); + $phase->{size} = $p->{xdr_size}; + $phase = 0; + } else { + die $p->{where}, "Reply array not supported"; + } } - print RXOUT ")\n"; - # Function body, beginning with local variables + # Determine the size of the reply buffer. It has to be big enough to hold + # all of a flat phase or at least one element of a bulk variable array. + # + # We need to round up to the nearest power-of-2 so that the circular buffer + # algorithm can operate with bitwise-AND masking and we need to leave a + # blank slot so that the head and tail pointers don't collide when the + # buffer is full. + foreach my $phase (@phases) { + $buf_size = $phase->{size} if ($buf_size < $phase->{size}); + } + $buf_size++; + + my $i; + for ($i = 1; $i < $buf_size; $i *= 2) { + ; + } + $buf_size = $i; + $func->{call_buf_size} = $buf_size; + + # Function definition and arguments + print RXOUT "\n"; + foreach $proto (@{$func->{recv_protos}}) { + print RXOUT $proto; + } + print RXOUT "\n"; print RXOUT "{\n"; - print RXOUT "\tconst net_xdr_t *xdr;\n"; - - # Unmarshal the data - print RXOUT "\n"; - print RXOUT "\txdr = call->reply;\n"; - foreach my $p (@reply) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - if ($type eq "int8_t*" || - $type eq "int16_t*" || - $type eq "int32_t*" || - $type eq "uint8_t*" || - $type eq "uint16_t*" || - $type eq "uint32_t*") { - print RXOUT "\t*$name = ntohl(*xdr++);\n"; - } elsif ($type eq "int64_t*" || - $type eq "uint64_t*") { - print RXOUT "\t*$name = (uint64_t)ntohl(*xdr++) << 32\n"; - print RXOUT "\t\t | (uint64_t)ntohl(*xdr++);\n"; - } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { - print RXOUT "\txdr = rxgen_decode_$1($name, xdr);\n"; + + # Local variables + print RXOUT "\tconst net_xdr_t *xdr = call->reply;\n"; + print RXOUT "\tunsigned head = call->head / 4;\n"; + print RXOUT "\tunsigned tail = call->tail / 4;\n"; + print RXOUT "\tunsigned size = call->size / 4;\n"; + print RXOUT "\tunsigned mask = size - 1;\n"; + + if ($have_bulk) { + print RXOUT "\tunion {\n"; + foreach my $p (@{$func->{reply}}) { + if ($p->{class} eq "bulk") { + if ($p->{elem}->{class} eq "basic") { + print RXOUT "\t\t", $p->{type}, " ", $p->{name}, ";\n"; + } else { + print RXOUT "\t\tstruct ", $p->{type}, " *", $p->{name}, ";\n"; + } + } + } + print RXOUT "\t} bulk;\n"; + } + + # Deal with each phase + print RXOUT "\n"; + print RXOUT "\tswitch (call->phase) {\n"; + + print RXOUT "\tcase 0:\n"; + + my $phase_goto_label = 0; + my $close_phase = 0; + my $phix; + for ($phix = 1; $phix <= $#phases + 1; $phix++) { + $phase = $phases[$phix - 1]; + if ($phase_goto_label == $phix) { + print RXOUT "\tphase_", $phix, ":\n"; + $phase_goto_label = 0; + } + print RXOUT "\t\tcall->need_size = ", $phase->{size}, ";\n" + unless ($phase->{type} eq "bulk"); + print RXOUT "\t\tcall->phase = ", $phix, ";\n"; + print RXOUT "\tcase ", $phix, ":\n"; + + print RXOUT "\t\tdo {\n"; + $close_phase = 1; + print RXOUT "\t\t\tif (CIRC_CNT(head, tail, size) < ", $phase->{size}, " / 4)"; + if ($phase->{type} eq "bulk") { + print RXOUT " {\n"; + print RXOUT "\t\t\t\tcall->need_size = MIN(call->need_bulk_count, call->item_per_buf);\n"; + print RXOUT "\t\t\t\tcall->need_size *= ", $phase->{xdr_size}, ";\n"; + } else { + print RXOUT "\n"; } + print RXOUT "\t\t\t\tgoto need_more_data;\n"; + print RXOUT "\t\t\t}" if ($phase->{type} eq "bulk"); + + # Unmarshal the data + print RXOUT "\n"; + foreach my $p (@{$phase->{params}}) { + if ($p->{type} eq "bulk_size") { + print RXOUT "\t\t\tcall->need_bulk_count = ntohl(xdr[tail++ & mask]);\n"; + print RXOUT "\t\t} while (0);\n" if ($close_phase); + $close_phase = 0; + + if ($p->{elem}->{class} eq "basic") { + print RXOUT "\t\tif (store__", $p->{name}, "(token__", $p->{name}, ", call->need_bulk_count, NULL) < 0)\n"; + } else { + print RXOUT "\t\tif (alloc__", $p->{name}, "(token__", $p->{name}, ", call->need_bulk_count, NULL) < 0)\n"; + } + print RXOUT "\t\t\treturn -1;\n"; + print RXOUT "\t\tif (call->need_bulk_count == 0)\n"; + print RXOUT "\t\t\tgoto phase_", $phix + 2, ";\n"; + $phase_goto_label = $phix + 2; + print RXOUT "\t\tcall->item_per_buf = size / (", $p->{xdr_size}, " / 4);\n"; + print RXOUT "\t\tcall->bulk_index = 0;\n"; + next; + } + + if ($p->{class} eq "bulk" && $p->{elem}->{class} eq "basic") { + if ($p->{elem}->{xdr_size} == 4) { + print RXOUT "\t\t\tbulk.", $p->{name}, " = ntohl(xdr[tail++ & mask]);\n"; + } elsif ($p->{elem}->{xdr_size} == 8) { + print RXOUT "\t\t\tbulk.", $p->{name}, " = (uint64_t)ntohl(xdr[tail++ & mask]) << 32\n"; + print RXOUT "\t\t\t\t | (uint64_t)ntohl(xdr[tail++ & mask]);\n"; + } else { + die; + } + print RXOUT "\t\t\tif (store__", $p->{name}, "(token__", $p->{name}, ", call->bulk_index++, &bulk.", $p->{name}, ") < 0)\n"; + print RXOUT "\t\t\t\treturn -1;\n"; + + } elsif ($p->{class} eq "bulk" && $p->{elem}->{class} eq "struct") { + print RXOUT "\t\t\tif (alloc__", $p->{name}, "(token__", $p->{name}, ", call->bulk_index++, &bulk.", $p->{name}, ") < 0)\n"; + print RXOUT "\t\t\t\treturn -1;\n"; + print RXOUT "\t\t\ttail = rxgen_decode_", $p->{type}, "(bulk.", $p->{name}, ", xdr, tail, mask);\n"; + } elsif ($p->{class} eq "basic" && $p->{xdr_size} == 4) { + print RXOUT "\t\t\t*", $p->{name}, " = ntohl(xdr[tail++ & mask]);\n"; + } elsif ($p->{class} eq "basic" && $p->{xdr_size} == 8) { + print RXOUT "\t\t\t*", $p->{name}, " = (uint64_t)ntohl(xdr[tail++ & mask]) << 32\n"; + print RXOUT "\t\t\t\t | (uint64_t)ntohl(xdr[tail++ & mask]);\n"; + } elsif ($p->{class} eq "struct") { + print RXOUT "\t\t\ttail = rxgen_decode_", $p->{type}, "(", $p->{name}, ", xdr, tail, mask);\n"; + } else { + die $p->{where}, ": Unsupported type in decode"; + } + + if ($p->{class} eq "bulk") { + print RXOUT "\t\t\tcall->need_bulk_count--;\n"; + print RXOUT "\t\t} while (call->need_bulk_count > 0);\n"; + $close_phase = 0; + } + } + + print RXOUT "\t\t} while (0);\n" if ($close_phase); } + if ($phase_goto_label == $phix) { + print RXOUT "\tphase_", $phix, ":\n"; + $phase_goto_label = 0; + } + print RXOUT "\t\tcall->phase = ", $phix, ";\n"; + print RXOUT "\tcase ", $phix, ":\n"; + print RXOUT "\t\tif (!call->got_eor) {\n"; + print RXOUT "\t\t\ttail = head;\n"; + print RXOUT "\t\t\tcall->need_size = size - 1;\n"; + print RXOUT "\t\t\tgoto need_more_data_2;\n"; + print RXOUT "\t\t}\n"; + print RXOUT "\t\tbreak;\n"; + print RXOUT "\t}\n"; + + print RXOUT "\n"; + print RXOUT "\tcall->tail = tail * 4;\n"; + print RXOUT "\treturn 0;\n"; + + print RXOUT "\n"; + print RXOUT "need_more_data:\n"; + print RXOUT "\tif (call->got_eor) {\n"; + print RXOUT "\t\terrno = EMSGSIZE;\n"; + print RXOUT "\t\treturn -1;\n"; + print RXOUT "\t}\n"; + print RXOUT "need_more_data_2:\n"; + print RXOUT "\tcall->tail = tail;\n"; + print RXOUT "\treturn 1;\n"; + print RXOUT "}\n"; } ############################################################################### # -# Emit a function to make a simple synchronous call +# Emit a dummy function to decode a replyless reply in a way that can be used +# from asynchronous code. +# +############################################################################### +sub emit_func_dummy_decode_reply($) +{ + my ($func) = @_; + + # Function definition and arguments + print RXOUT "\n"; + foreach $proto (@{$func->{recv_protos}}) { + print RXOUT $proto; + } + print RXOUT "\n"; + print RXOUT "{\n"; + print RXOUT "\tcall->tail = call->head;\n"; + print RXOUT "\tcall->need_size = call->size - 1;\n"; + print RXOUT "\treturn call->got_eor ? 0 : 1;\n"; + + print RXOUT "}\n"; +} + +############################################################################### +# +# Emit a function to allocate a call struct with appropriately sized bufferage. # ############################################################################### -sub emit_func_simple_sync_call($$$$$@) +sub emit_func_alloc_call($) { - my ($func, $request_size, $reply_size, $_request, $_reply, @params) = @_; - my @request = @{$_request}; - my @reply = @{$_reply}; + my ($func) = @_; + + my $buf_size = 16; + + $buf_size = $func->{call_buf_size} if (exists $func->{call_buf_size}); + # Emit a function to allocate an rxrpc_call struct for this call print RXOUT "\n"; + print RXOUT "struct rx_call *rxgen_alloc_call_", $func->{name}, "(void)\n"; + print RXOUT "{\n"; + print RXOUT "\tstruct rx_call *call = malloc(sizeof(*call) + ", $buf_size, ");\n"; + print RXOUT "\tif (call) {\n"; + print RXOUT "\t\tmemset(call, 0, sizeof(*call));\n"; + print RXOUT "\t\tcall->size = ", $buf_size, ";\n"; + print RXOUT "\t}\n"; + print RXOUT "\treturn call;\n"; + print RXOUT "}\n"; +} + +############################################################################### +# +# Emit a function to make a simple synchronous call +# +############################################################################### +sub emit_func_simple_sync_call($) +{ + my ($func) = @_; + + # Function declaration + print RXHDR "\n"; + print RXHDR "extern "; + foreach $proto (@{$func->{protos}}) { + print RXHDR $proto; + } + print RXHDR ";\n"; # Function definition and arguments - print RXOUT "int ", $func, "(\n"; - print RXOUT "\tstruct rx_connection *z_conn"; - foreach my $p (@params) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - print RXOUT ",\n\t"; - print RXOUT "const " if ($type =~ "[*]" && $dir eq "IN"); - print RXOUT "$type $name"; - die "Array arg not supported '$name'" if ($array_size != -1); + print RXOUT "\n"; + foreach $proto (@{$func->{protos}}) { + print RXOUT $proto; } - print RXOUT ")\n"; + print RXOUT "\n"; # Function body, beginning with local variables print RXOUT "{\n"; @@ -149,18 +609,16 @@ sub emit_func_simple_sync_call($$$$$@) print RXOUT "\n"; # Allocate a call record and reply buffer - print RXOUT "\tcall = malloc(sizeof(*call) + $reply_size);\n"; + print RXOUT "\tcall = rxgen_alloc_call_", $func->{name}, "();\n"; print RXOUT "\tif (!call)\n"; print RXOUT "\t\treturn -1;\n"; - print RXOUT "\tcall->reply = (void *)call + sizeof(*call);\n"; print RXOUT "\n"; # Send the request - print RXOUT "\tret = rxgen_send_request_", $func, "("; + print RXOUT "\tret = rxgen_send_request_", $func->{name}, "("; print RXOUT "z_conn, call"; - foreach my $p (@request) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - print RXOUT ", $name"; + foreach (@{$func->{send_args}}) { + print RXOUT ", ", $_; } print RXOUT ");\n"; print RXOUT "\tif (ret != 0) {\n"; @@ -168,27 +626,26 @@ sub emit_func_simple_sync_call($$$$$@) print RXOUT "\t\treturn ret;\n"; print RXOUT "\t}\n"; - # Wait for the reply - print RXOUT "\tret = rxrpc_wait_for_sync_reply(z_conn, call);\n"; - print RXOUT "\tif (ret != 0) {\n"; - print RXOUT "\t\tfree(call);\n"; - print RXOUT "\t\treturn ret;\n"; - print RXOUT "\t}\n"; - print RXOUT "\n"; - - # Unmarshal the reply - if (@reply) { - print RXOUT "\trxgen_decode_reply_", $func, "("; - print RXOUT "z_conn, call"; - foreach my $p (@reply) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - print RXOUT ", $name"; - } - print RXOUT ");\n"; + # Set up the reply buffer and try to parse the reply as it's received + print RXOUT "\tfor (;;) {\n"; + + print RXOUT "\t\tret = rxgen_decode_reply_", $func->{name}, "(z_conn, call"; + foreach my $a (@{$func->{recv_args}}) { + print RXOUT ", ", $a; } + print RXOUT ");\n"; + print RXOUT "\t\tif (ret != 1)\n"; + print RXOUT "\t\t\tbreak;\n"; + + # Receive reply data + print RXOUT "\t\tret = rxrpc_wait_for_sync_reply(z_conn, call);\n"; + print RXOUT "\t\tif (ret != 0)\n"; + print RXOUT "\t\t\tbreak;\n"; + print RXOUT "\t}\n"; print RXOUT "\n"; - print RXOUT "\treturn 0;\n"; + print RXOUT "\tfree(call);\n"; + print RXOUT "\treturn ret;\n"; print RXOUT "}\n"; } diff --git a/rxgen/emit_py_module.pm b/rxgen/emit_py_module.pm index de31f76..54c0ea0 100644 --- a/rxgen/emit_py_module.pm +++ b/rxgen/emit_py_module.pm @@ -21,16 +21,15 @@ sub emit_py_module() { print PYOUT " */\n"; print PYOUT "static PyMethodDef module_methods[] = {\n"; - foreach my $s (@structs) { - my $struct = @{$s}[0]; - print PYOUT "\t{\"new_$struct\", (PyCFunction)kafs_new_py_$struct, METH_NOARGS,\n"; - print PYOUT "\t \"Create a new $struct record.\"\n"; + foreach my $struct (@structs) { + print PYOUT "\t{\"new_", $struct->{type}, "\", (PyCFunction)kafs_new_py_", $struct->{type}, ", METH_NOARGS,\n"; + print PYOUT "\t \"Create a new ", $struct->{type}, " record.\"\n"; print PYOUT "\t},\n"; } - foreach $func (sort keys %funcs) { - my @params = @{$funcs{$func}}; - print PYOUT "\t{\"$func\", (PyCFunction)kafs_$func, METH_VARARGS, \"\" },\n"; + foreach my $funcname (sort keys %func_names) { + my $func = $func_names{$funcname}; + print PYOUT "\t{\"", $func->{name}, "\", (PyCFunction)kafs_", $func->{name}, ", METH_VARARGS, \"\" },\n"; } print PYOUT "\t{\"rx_new_connection\", (PyCFunction)kafs_py_rx_new_connection, METH_VARARGS,\n"; @@ -63,11 +62,9 @@ sub emit_py_module() { print PYOUT "\tif ("; print PYOUT "PyType_Ready(&py_rx_connectionType) < 0"; my $first = 0; - foreach my $s (@structs) { - my @members = @{$s}; - my $struct = $members[0]; + foreach my $struct (@structs) { print PYOUT " ||\n\t " unless ($first); - print PYOUT "PyType_Ready(&py_", $struct, "Type) < 0"; + print PYOUT "PyType_Ready(&py_", $struct->{type}, "Type) < 0"; $first = 0; } print PYOUT ")\n"; @@ -88,11 +85,9 @@ sub emit_py_module() { if (@structs) { print PYOUT "\n"; - foreach my $s (@structs) { - my @members = @{$s}; - my $struct = $members[0]; - print PYOUT "\tPy_INCREF(&py_", $struct, "Type);\n"; - print PYOUT "\tPyModule_AddObject(m, \"$struct\", (PyObject *)&py_", $struct, "Type);\n"; + foreach my $struct (@structs) { + print PYOUT "\tPy_INCREF(&py_", $struct->{type}, "Type);\n"; + print PYOUT "\tPyModule_AddObject(m, \"", $struct->{type}, "\", (PyObject *)&py_", $struct->{type}, "Type);\n"; } print PYOUT "\n"; diff --git a/rxgen/emit_py_sync_funcs.pm b/rxgen/emit_py_sync_funcs.pm index 52649d3..cdf4814 100644 --- a/rxgen/emit_py_sync_funcs.pm +++ b/rxgen/emit_py_sync_funcs.pm @@ -8,57 +8,173 @@ # 2 of the Licence, or (at your option) any later version. # +my %bulk_get_helpers = (); +my %bulk_set_helpers = (); + +############################################################################### +# +# Emit functions to help deal with bulk lists +# +############################################################################### +sub emit_py_func_bulk_helper($) +{ + my ($func) = @_; + + foreach my $p (@{$func->{params}}) { + next if ($p->{class} ne "bulk"); + next if ($p->{elem}->{class} eq "string" || $p->{elem}->{class} eq "opaque"); + + # Data transmission + if ($p->{dir} eq "IN" && !exists $bulk_get_helpers{$p->{type}}) { + $bulk_get_helpers{$p->{type}} = 1; + + print PYOUT "\n"; + print PYOUT "static int py_get__", $p->{type}, "("; + if ($p->{elem}->{class} eq "basic") { + print PYOUT "void *token, int index, ", $p->{type}, " *object)\n"; + } elsif ($p->{elem}->{class} eq "struct") { + print PYOUT "void *token, int index, ", $p->{type}, " **object)\n"; + } else { + die $p->{where}, ": Unsupported type for bulk helper"; + } + + print PYOUT "{\n"; + print PYOUT "\tPyObject *list = token;\n"; + print PYOUT "\tPyObject *item;\n"; + print PYOUT "\n"; + print PYOUT "\titem = PyList_GetItem(list, index);\n"; + print PYOUT "\tif (!item)\n"; + print PYOUT "\t\treturn -1;\n"; + + print PYOUT "\n"; + if ($p->{elem}->{class} eq "basic") { + print PYOUT "\tif (!PyLong_Check(item)) {\n"; + print PYOUT "\t\tPyErr_SetString(PyExc_TypeError, \"Expected list of ", $p->{type}, "\");\n"; + print PYOUT "\t\treturn -1;\n"; + print PYOUT "\t}\n"; + } else { + print PYOUT "\tif (py_premarshal_", $p->{type}, "(item))\n"; + print PYOUT "\t\treturn -1;\n"; + } + + if ($p->{elem}->{class} eq "basic") { + if ($p->{elem}->{type} eq "int64_t") { + print PYOUT "\t*object = PyLong_AsLongLong(item);\n"; + } elsif ($p->{elem}->{type} eq "uint64_t") { + print PYOUT "\t*object = PyLong_AsUnsignedLongLong(item);\n"; + } elsif ($p->{elem}->{type} =~ /^int/) { + print PYOUT "\t*object = PyLong_AsLong(item);\n"; + } elsif ($p->{elem}->{type} =~ /^uint|^char/) { + print PYOUT "\t*object = PyLong_AsUnsignedLong(item);\n"; + } + } else { + print PYOUT "\t*object = &((struct py_", $p->{type}, " *)item)->x;\n"; + } + print PYOUT "\treturn 0;\n"; + print PYOUT "}\n"; + } + + # Data reception + if ($p->{dir} eq "OUT" && !exists $bulk_set_helpers{$p->{type}}) { + $bulk_set_helpers{$p->{type}} = 1; + + print PYOUT "\n"; + if ($p->{elem}->{class} eq "basic") { + print PYOUT "static int py_store__", $p->{type}, "("; + print PYOUT "void *token, int index, ", $p->{type}, " *object)\n"; + } elsif ($p->{elem}->{class} eq "struct") { + print PYOUT "static int py_alloc__", $p->{type}, "("; + print PYOUT "void *token, int index, struct ", $p->{type}, " **object)\n"; + } else { + die $p->{where}, ": Unsupported type for bulk helper"; + } + + print PYOUT "{\n"; + print PYOUT "\tPyObject *list = token;\n"; + print PYOUT "\tPyObject *item;\n"; + + # Bulk array size indication if !object + print PYOUT "\n"; + print PYOUT "\tif (!object)\n"; + print PYOUT "\t\treturn 0;\n"; + + print PYOUT "\n"; + if ($p->{elem}->{class} eq "basic") { + if ($p->{elem}->{type} eq "int64_t") { + print PYOUT "\titem = PyLong_FromLongLong(*object);\n"; + } elsif ($p->{elem}->{type} eq "uint64_t") { + print PYOUT "\titem = PyLong_FromUnsignedLongLong(*object);\n"; + } elsif ($p->{elem}->{type} =~ /^int/) { + print PYOUT "\titem = PyLong_FromLong(*object);\n"; + } elsif ($p->{elem}->{type} =~ /^uint|^char/) { + print PYOUT "\titem = PyLong_FromUnsignedLong(*object);\n"; + } + } else { + print PYOUT "\titem = kafs_new_py_", $p->{type}, "(NULL, NULL);\n"; + } + print PYOUT "\tif (!item)\n"; + print PYOUT "\t\treturn -1;\n"; + + print PYOUT "\n"; + print PYOUT "\tif (PyList_Insert(list, index, item) < 0) {\n"; + print PYOUT "\t\tPy_DECREF(item);\n"; + print PYOUT "\t\treturn -1;\n"; + print PYOUT "\t}\n"; + + if ($p->{elem}->{class} eq "struct") { + print PYOUT "\n"; + print PYOUT "\t*object = &((struct py_", $p->{type}, " *)item)->x;\n"; + } + + print PYOUT "\treturn 0;\n"; + print PYOUT "}\n"; + } + } +} + ############################################################################### # # Emit a python wrapper function to make a simple synchronous call # ############################################################################### -sub emit_py_func_simple_sync_call($$$@) +sub emit_py_func_simple_sync_call($) { - my ($func, $_request, $_reply, @params) = @_; - my @request = @{$_request}; - my @reply = @{$_reply}; + my ($func) = @_; + print PYOUT "\n"; print PYOUT "PyObject *\n"; - print PYOUT "kafs_", $func, "(PyObject *_self, PyObject *args)\n"; + print PYOUT "kafs_", $func->{name}, "(PyObject *_self, PyObject *args)\n"; print PYOUT "{\n"; - # Declare parameter variables + # Local variable declarations representing parameters to send my $need_tmp = 0; print PYOUT "\tstruct py_rx_connection *z_conn;\n"; - foreach my $p (@params) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - if ($type eq "char*") { - die "String output args not supported" unless ($dir eq "IN"); - print PYOUT "\tconst char *param_$name;\n"; - } elsif ($type eq "int8_t" || $type eq "int8_t*" || - $type eq "int16_t" || $type eq "int16_t*" || - $type eq "int32_t" || $type eq "int32_t*" || - $type eq "int64_t" || $type eq "int64_t*" || - $type eq "uint8_t" || $type eq "uint8_t*" || - $type eq "uint16_t" || $type eq "uint16_t*" || - $type eq "uint32_t" || $type eq "uint32_t*" || - $type eq "uint64_t" || $type eq "uint64_t*" - ) { - $type =~ s/[*]$//; - $need_tmp = 1 unless ($dir eq "IN"); - print PYOUT "\t$type param_$name;\n"; - } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { - die "INOUT struct args not supported" if ($dir eq "INOUT"); - print PYOUT "\tstruct py_$1 *param_$name;\n"; + foreach my $p (@{$func->{params}}) { + if ($p->{class} eq "bulk" && $p->{elem}->{class} eq "string") { + die $p->{where}, ": String output args not supported" unless ($p->{dir} eq "IN"); + print PYOUT "\tconst char *param_", $p->{name}, ";\n"; + } elsif ($p->{class} eq "basic") { + $need_tmp = 1 unless ($p->{dir} eq "IN"); + print PYOUT "\t", $p->{type}, " param_", $p->{name}, ";\n"; + } elsif ($p->{class} eq "struct") { + die $p->{where}, ": INOUT struct args not supported" if ($p->{dir} eq "INOUT"); + print PYOUT "\tstruct py_", $p->{type}, " *param_", $p->{name}, ";\n"; + } elsif ($p->{class} eq "bulk") { + die $p->{where}, ": INOUT bulk args not supported" if ($p->{dir} eq "INOUT"); + print PYOUT "\tPyObject *param_", $p->{name}, ";\n" + unless ($p->{dir} eq "OUT"); } else { - die "Unsupported type \"$type\""; + die $p->{where}, ": Unsupported type \"", $p->{type}, "\""; } } # Replies are passed back into lists provided by the caller. INOUT variable # input values must already occupy the lists. - foreach my $p (@reply) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - print PYOUT "\tPyObject *reply_$name;\n"; + foreach my $p (@{$func->{reply}}) { + print PYOUT "\tPyObject *reply_", $p->{name}, ";\n"; } - print PYOUT "\tPyObject *tmp;\n" if ($need_tmp); + print PYOUT "\tPyObject *tmp = NULL;\n" if ($need_tmp); print PYOUT "\tPyObject *res = NULL;\n"; print PYOUT "\tint ret;\n"; @@ -67,100 +183,88 @@ sub emit_py_func_simple_sync_call($$$@) print PYOUT "\n"; print PYOUT "\tif (!PyArg_ParseTuple(args, \"O!"; - foreach my $p (@params) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - if ($dir ne "IN") { - print PYOUT "O!"; - } elsif ($type eq "char*") { + foreach my $p (@{$func->{params}}) { + if ($p->{dir} ne "IN") { print PYOUT "O!"; + } elsif ($p->{type} eq "int8_t") { print PYOUT "B"; + } elsif ($p->{type} eq "int16_t") { print PYOUT "h"; + } elsif ($p->{type} eq "int32_t") { print PYOUT "i"; + } elsif ($p->{type} eq "int64_t") { print PYOUT "L"; + } elsif ($p->{type} eq "uint8_t") { print PYOUT "b"; + } elsif ($p->{type} eq "uint16_t") { print PYOUT "H"; + } elsif ($p->{type} eq "uint32_t") { print PYOUT "I"; + } elsif ($p->{type} eq "uint64_t") { print PYOUT "K"; + } elsif ($p->{class} eq "struct") { print PYOUT "O!"; + } elsif ($p->{class} eq "bulk" && $p->{elem}->{class} eq "string") { print PYOUT "s"; - } elsif ($type eq "int8_t") { - print PYOUT "B"; - } elsif ($type eq "int16_t") { - print PYOUT "h"; - } elsif ($type eq "int32_t") { - print PYOUT "i"; - } elsif ($type eq "int64_t") { - print PYOUT "L"; - } elsif ($type eq "uint8_t") { - print PYOUT "b"; - } elsif ($type eq "uint16_t") { - print PYOUT "H"; - } elsif ($type eq "uint32_t") { - print PYOUT "I"; - } elsif ($type eq "uint64_t") { - print PYOUT "K"; - } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { - print PYOUT "O!"; + } else { + die $p->{where}, ": No py parse for param"; } } print PYOUT "\",\n"; print PYOUT "\t\t\t &py_rx_connectionType, &z_conn"; - foreach my $p (@params) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; + foreach my $p (@{$func->{params}}) { print PYOUT ",\n"; - print PYOUT "\t\t\t /*$dir*/ "; - if ($dir ne "IN") { - print PYOUT "&PyList_Type, &reply_$name"; - } elsif ($type eq "char*" || - $type eq "int8_t" || $type eq "int8_t" || - $type eq "int16_t" || $type eq "int16_t" || - $type eq "int32_t" || $type eq "int32_t" || - $type eq "int64_t" || $type eq "int64_t" || - $type eq "uint8_t" || $type eq "uint8_t" || - $type eq "uint16_t" || $type eq "uint16_t" || - $type eq "uint32_t" || $type eq "uint32_t" || - $type eq "uint64_t" || $type eq "uint64_t") { - print PYOUT "¶m_$name"; - } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { - print PYOUT "&py_", $1, "Type, ¶m_$name"; + print PYOUT "\t\t\t /*", $p->{dir}, "*/ "; + if ($p->{dir} ne "IN") { + print PYOUT "&PyList_Type, &reply_", $p->{name}; + } elsif ($p->{class} eq "bulk" && ($p->{elem}->{class} eq "string" || + $p->{elem}->{class} eq "opaque") || + $p->{class} eq "basic") { + print PYOUT "¶m_", $p->{name}; + } elsif ($p->{class} eq "struct") { + print PYOUT "&py_", $p->{type}, "Type, ¶m_", $p->{name}; + } elsif ($p->{class} eq "bulk") { + print PYOUT "&PyList_Type, ¶m_", $p->{name}; } else { - die "Unsupported type \"$type\""; + die $p->{where}, ": Unsupported type \"", $p->{type}, "\""; } } print PYOUT "))\n"; print PYOUT "\t\treturn NULL;\n"; # Allocate reply buffer objects - if (@reply) { + if (@{$func->{reply}}) { print PYOUT "\n"; - foreach my $p (@reply) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - if ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { - print PYOUT "\tparam_$name = (struct py_$1 *)kafs_new_py_$1(NULL, NULL);\n"; - print PYOUT "\tif (!param_$name)\n"; - print PYOUT "\t\tgoto error_alloc_$name;\n"; + foreach my $p (@{$func->{reply}}) { + if ($p->{class} eq "struct") { + print PYOUT "\tparam_", $p->{name}, " = (struct py_", $p->{type}, " *)kafs_new_py_", $p->{type}, "(NULL, NULL);\n"; + print PYOUT "\tif (!param_", $p->{name}, ")\n"; + print PYOUT "\t\tgoto error_alloc_", $p->{name}, ";\n"; } } } # Make the call print PYOUT "\n"; - print PYOUT "\tret = $func(\n"; + print PYOUT "\tret = ", $func->{name}, "(\n"; print PYOUT "\t\tz_conn->x"; - foreach my $p (@params) { + foreach my $p (@{$func->{params}}) { print PYOUT ",\n"; - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - if ($type eq "char*" || - $type eq "int8_t" || $type eq "int8_t*" || - $type eq "int16_t" || $type eq "int16_t*" || - $type eq "int32_t" || $type eq "int32_t*" || - $type eq "int64_t" || $type eq "int64_t*" || - $type eq "uint8_t" || $type eq "uint8_t*" || - $type eq "uint16_t" || $type eq "uint16_t*" || - $type eq "uint32_t" || $type eq "uint32_t*" || - $type eq "uint64_t" || $type eq "uint64_t*") { - if ($dir eq "IN") { - print PYOUT "\t\tparam_$name"; + if ($p->{class} eq "bulk" && $p->{elem}->{class} eq "string" || + $p->{class} eq "basic") { + if ($p->{dir} eq "IN") { + print PYOUT "\t\tparam_", $p->{name}; } else { - print PYOUT "\t\t¶m_$name"; + print PYOUT "\t\t¶m_", $p->{name}; + } + } elsif ($p->{class} eq "struct") { + print PYOUT "\t\t¶m_", $p->{name}, "->x"; + } elsif ($p->{class} eq "bulk") { + if ($p->{dir} eq "IN") { + print PYOUT "\t\tpy_get__", $p->{type}, ", param_", $p->{name}; + print PYOUT ", PyList_Size(param_", $p->{name}, ")"; + } elsif ($p->{elem}->{class} eq "basic") { + print PYOUT "\t\tpy_store__", $p->{type}, ", reply_", $p->{name}; + } elsif ($p->{elem}->{class} eq "struct") { + print PYOUT "\t\tpy_alloc__", $p->{type}, ", reply_", $p->{name}; + } else { + die; } - } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { - print PYOUT "\t\t¶m_$name->x"; } else { - die "Unsupported type \"$type\""; + die $p->{where}, ": Unsupported type \"", $p->{type}, "\""; } } print PYOUT ");\n"; @@ -175,34 +279,30 @@ sub emit_py_func_simple_sync_call($$$@) print PYOUT "\t}\n"; # Pass back any replies - if (@reply) { + if (@{$func->{reply}}) { print PYOUT "\n"; - foreach my $p (@reply) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - + foreach my $p (@{$func->{reply}}) { my $set_null = 0; my $var = "tmp"; - if ($type eq "int8_t" || $type eq "int8_t*" || - $type eq "int16_t" || $type eq "int16_t*" || - $type eq "int32_t" || $type eq "int32_t*") { - print PYOUT "\ttmp = PyLong_FromLong(param_$name);\n"; - } elsif ($type eq "int64_t" || $type eq "int64_t*") { - print PYOUT "\ttmp = PyLong_FromLongLong(param_$name);\n"; - } elsif ($type eq "uint8_t" || $type eq "uint8_t*" || - $type eq "uint16_t" || $type eq "uint16_t*" || - $type eq "uint32_t" || $type eq "uint32_t*") { - - print PYOUT "\ttmp = PyLong_FromUnsignedLong(param_$name);\n"; - - } elsif ($type eq "uint64_t" || $type eq "uint64_t*") { - print PYOUT "\ttmp = PyLong_FromUnsignedLongLong(param_$name);\n"; + if ($p->{class} eq "basic") { + if ($p->{type} eq "int64_t") { + print PYOUT "\ttmp = PyLong_FromLongLong(param_", $p->{name}, ");\n"; + } elsif ($p->{type} eq "uint64_t") { + print PYOUT "\ttmp = PyLong_FromUnsignedLongLong(param_", $p->{name}, ");\n"; + } elsif ($p->{type} !~ /^u/) { + print PYOUT "\ttmp = PyLong_FromLong(param_", $p->{name}, ");\n"; + } else { + print PYOUT "\ttmp = PyLong_FromUnsignedLong(param_", $p->{name}, ");\n"; + } - } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { - $var = "(PyObject *)param_$name"; + } elsif ($p->{class} eq "struct") { + $var = "(PyObject *)param_" . $p->{name}; $set_null = 1; + } elsif ($p->{class} eq "bulk") { + # All done in the helper func } else { - die "Unsupported type \"$type\""; + die $p->{where}, ": Unsupported class \"", $p->{class}, "\""; } if ($var eq "tmp") { @@ -210,9 +310,9 @@ sub emit_py_func_simple_sync_call($$$@) print PYOUT "\t\tgoto error;\n"; } - print PYOUT "\tif (PyList_Insert(reply_$name, 0, $var) == -1)\n"; + print PYOUT "\tif (PyList_Insert(reply_", $p->{name}, ", 0, ", $var, ") == -1)\n"; print PYOUT "\t\tgoto error", $need_tmp ? "_tmp" : "", ";\n"; - print PYOUT "\tparam_$name = NULL;\n" if ($set_null); + print PYOUT "\tparam_", $p->{name}, " = NULL;\n" if ($set_null); } } @@ -228,12 +328,11 @@ sub emit_py_func_simple_sync_call($$$@) print PYOUT "\tPy_DECREF(tmp);\n"; } print PYOUT "error:\n"; - if (@reply) { - foreach my $p (reverse @reply) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - if ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { - print PYOUT "\tPy_XDECREF(param_$name);\n"; - print PYOUT "error_alloc_$name:\n"; + if (@{$func->{reply}}) { + foreach my $p (reverse @{$func->{reply}}) { + if ($p->{class} eq "struct") { + print PYOUT "\tPy_XDECREF(param_", $p->{name}, ");\n"; + print PYOUT "error_alloc_", $p->{name}, ":\n"; } } } diff --git a/rxgen/emit_py_types.pm b/rxgen/emit_py_types.pm index 73d4f65..3be828a 100644 --- a/rxgen/emit_py_types.pm +++ b/rxgen/emit_py_types.pm @@ -8,118 +8,150 @@ # 2 of the Licence, or (at your option) any later version. # +############################################################################### +# +# Emit python type wrapper declarations +# +############################################################################### +sub emit_py_type_wrapper_decls($) { + my ($s) = @_; + + print PYOUT "static PyTypeObject py_", $s->{type}, "Type;\n"; +} + ############################################################################### # # Emit python type wrappers for C structs. # ############################################################################### -sub emit_py_type_wrapper($@) { - my ($struct, @members) = @_; +sub emit_py_type_wrapper($) { + my ($struct) = @_; # Dump the banner comment block - my @comments = @{shift @members}; - print PYHDR "\n"; - print PYHDR @comments; + print PYHDR @{$struct->{banner}}; print PYOUT "\n"; - print PYOUT @comments; + print PYOUT @{$struct->{banner}}; + + # Divide the struct members into single ints, single structs, char arrays + # (strings) and other arrays + my @single_ints = (); + my @single_structs = (); + my @char_arrays = (); + my @arrays = (); + foreach my $m (@{$struct->{members}}) { + if ($m->{class} eq "array" && $m->{elem}->{type} eq "char") { + push @char_arrays, $m; + } elsif ($m->{class} eq "basic") { + push @single_ints, $m; + } elsif ($m->{class} eq "struct") { + push @single_structs, $m; + } elsif ($m->{class} eq "array") { + push @arrays, $m; + } else { + die; + } + } # Write a python wrapper struct - print PYHDR "struct py_$struct {\n"; + # + # We have a copy of the raw struct and we also have caches for python + # objects for non-integer, non-array bits of the struct. We populate the + # caches when these bits are called for and then fold their contents back + # into the raw struct when we're about to marshal it. + # + print PYHDR "\n"; + print PYHDR "struct py_", $struct->{type}, " {\n"; print PYHDR "\tPyObject_HEAD\n"; - print PYHDR "\tstruct $struct x;\n"; + print PYHDR "\tstruct ", $struct->{type}, " x;\n"; + if ($#single_structs + $#arrays > -2) { + print PYHDR "\tstruct {\n"; + foreach my $m (@single_structs, @arrays) { + print PYHDR "\t\tPyObject *", $m->{name}, ";\n"; + } + print PYHDR "\t} c;\n"; + } print PYHDR "};\n"; - print PYHDR "\n"; - # We want allocation and deallocation functions - print PYOUT "static PyObject *\n"; - print PYOUT "py_", $struct, "_new(PyTypeObject *type, PyObject *args, PyObject *kwds)\n"; - print PYOUT "{\n"; - print PYOUT "\treturn (PyObject *)(struct py_$struct *)type->tp_alloc(type, 0);\n"; - print PYOUT "}\n"; + # We have to have a deallocation function print PYOUT "\n"; print PYOUT "static void\n"; - print PYOUT "py_", $struct, "_dealloc(struct py_$struct *self)\n"; + print PYOUT "py_", $struct->{type}, "_dealloc(struct py_", $struct->{type}, " *self)\n"; print PYOUT "{\n"; + foreach my $m (@single_structs, @arrays) { + print PYOUT "\tPy_XDECREF(self->c.", $m->{name}, ");\n"; + } print PYOUT "\tPy_TYPE(self)->tp_free((PyObject *)self);\n"; print PYOUT "}\n"; - print PYOUT "\n"; - # Divide into single members and array members - my @singles = (); - my @arrays = (); - foreach my $m (@members) { - my ($type, $name, $array_size, $max_size) = @{$m}; - if ($array_size == -1) { - push @singles, $m; - } else { - push @arrays, $m; - } - } - - # Any non-array elements are made directly accessible to the Python interpreter - print PYOUT "static PyMemberDef py_", $struct, "_members[] = {\n"; - if (@singles) { - foreach my $m (@singles) { - my ($type, $name, $array_size, $max_size) = @{$m}; - print PYOUT "\t{ \"$name\", "; - if ($type eq "char") { - print PYOUT "T_CHAR"; - } elsif ($type eq "int8_t") { - print PYOUT "T_BYTE"; - } elsif ($type eq "int16_t") { - print PYOUT "T_SHORT"; - } elsif ($type eq "int32_t") { - print PYOUT "T_INT"; - } elsif ($type eq "int64_t") { - print PYOUT "T_LONGLONG"; - } elsif ($type eq "uint8_t") { - print PYOUT "T_UBYTE"; - } elsif ($type eq "uint16_t") { - print PYOUT "T_USHORT"; - } elsif ($type eq "uint32_t") { - print PYOUT "T_UINT"; - } elsif ($type eq "uint64_t") { - print PYOUT "T_ULONGLONG"; - } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { - die "Don't py-wrap structs yet"; + # Any integer non-array elements are made directly accessible to the Python + # interpreter + if ($#single_ints + $#char_arrays > -2) { + print PYOUT "\n"; + print PYOUT "static PyMemberDef py_", $struct->{type}, "_members[] = {\n"; + foreach my $m (@single_ints) { + print PYOUT "\t{ \"", $m->{name}, "\", "; + if ($m->{type} eq "char") { print PYOUT "T_CHAR"; + } elsif ($m->{type} eq "int8_t") { print PYOUT "T_BYTE"; + } elsif ($m->{type} eq "int16_t") { print PYOUT "T_SHORT"; + } elsif ($m->{type} eq "int32_t") { print PYOUT "T_INT"; + } elsif ($m->{type} eq "int64_t") { print PYOUT "T_LONGLONG"; + } elsif ($m->{type} eq "uint8_t") { print PYOUT "T_UBYTE"; + } elsif ($m->{type} eq "uint16_t") { print PYOUT "T_USHORT"; + } elsif ($m->{type} eq "uint32_t") { print PYOUT "T_UINT"; + } elsif ($m->{type} eq "uint64_t") { print PYOUT "T_ULONGLONG"; } else { - die "Unsupported type \"$type\""; + die $m->{where}, ": Unsupported type \"", $m->{type}, "\""; } - print PYOUT ", offsetof(struct py_$struct, x.$name), 0, \"\"},\n"; + print PYOUT ", offsetof(struct py_", $struct->{type}, ", x.", $m->{name}, "), 0, \"\"},\n"; } + print PYOUT "\t{}\n"; + print PYOUT "};\n"; } - print PYOUT "\t{}\n"; - print PYOUT "};\n"; - print PYOUT "\n"; + + # Non-single integer elements need to be turned into their respective + # Python types and returned. # Array elements have to be accessed through ->tp_[sg]etattro() as - # tuples (int[]/uint[]) or strings (char[]) - if (@arrays) { + # tuples (int[]/uint[]/struct[]) or strings (char[]) + if ($#single_structs + $#arrays + $#char_arrays > -3) { # The attribute get function + print PYOUT "\n"; print PYOUT "static PyObject *\n"; - print PYOUT "py_", $struct, "_getattro(PyObject *_self, PyObject *name)\n"; + print PYOUT "py_", $struct->{type}, "_getattro(PyObject *_self, PyObject *name)\n"; print PYOUT "{\n"; - print PYOUT "\tstruct py_$struct *self = (struct py_$struct *)_self;\n"; + print PYOUT "\tstruct py_", $struct->{type}, " *self = (struct py_", $struct->{type}, " *)_self;\n"; print PYOUT "\n"; print PYOUT "\tif (PyUnicode_Check(name)) {\n"; - foreach my $m (@arrays) { - my ($type, $name, $array_size, $max_size) = @{$m}; - - print PYOUT "\t\tif (PyUnicode_CompareWithASCIIString(name, \"$name\") == 0)\n"; - if ($type eq "char") { - print PYOUT "\t\t\treturn py_rxgen_get_string(&self->x.$name, $array_size);\n"; - } elsif ($type eq "uint8_t") { - print PYOUT "\t\t\treturn py_rxgen_get_uint8(&self->x.$name, $array_size);\n"; - } elsif ($type eq "uint16_t") { - print PYOUT "\t\t\treturn py_rxgen_get_uint16(&self->x.$name, $array_size);\n"; - } elsif ($type eq "uint32_t") { - print PYOUT "\t\t\treturn py_rxgen_get_uint32(&self->x.$name, $array_size);\n"; - } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { - die "Don't py-wrap struct arrays yet"; + foreach my $m (@char_arrays, @single_structs, @arrays) { + print PYOUT "\t\tif (PyUnicode_CompareWithASCIIString(name, \"", $m->{name}, "\") == 0)\n"; + if ($m->{class} eq "struct") { + print PYOUT "\t\t\treturn py_rxgen_get_struct(&self->x.", $m->{name}, ",\n"; + print PYOUT "\t\t\t\t\t\t &self->c.", $m->{name}, ",\n"; + print PYOUT "\t\t\t\t\t\t py_data_to_", $m->{type}, ");\n"; + } elsif ($m->{class} ne "array") { + die $m->{where}, ": Unsupported type class \"", $m->{class}, "\""; + } elsif ($m->{elem}->{class} eq "struct") { + print PYOUT "\t\t\treturn py_rxgen_get_structs(&self->x.", $m->{name}, ", ", $m->{dim}, ",\n"; + print PYOUT "\t\t\t\t\t\t sizeof(struct ", $m->{elem}->{type}, "),\n"; + print PYOUT "\t\t\t\t\t\t &self->c.", $m->{name}, ",\n"; + print PYOUT "\t\t\t\t\t\t py_data_to_", $m->{elem}->{type}, ");\n"; + } elsif ($m->{elem}->{class} ne "basic") { + die $m->{where}, ": Unsupported array type class \"", $m->{elem}->{class}, "\""; + } elsif ($m->{elem}->{type} eq "char") { + print PYOUT "\t\t\treturn py_rxgen_get_string(&self->x.", $m->{name}, ", ", $m->{dim}, ");\n"; + } elsif ($m->{elem}->{type} eq "uint8_t") { + print PYOUT "\t\t\treturn py_rxgen_get_uint8(&self->x.", $m->{name}, ", ", $m->{dim}, ",\n"; + print PYOUT "\t\t\t\t\t\t &self->c.", $m->{name}, ");\n"; + } elsif ($m->{elem}->{type} eq "uint16_t") { + print PYOUT "\t\t\treturn py_rxgen_get_uint16(&self->x.", $m->{name}, ", ", $m->{dim}, ",\n"; + print PYOUT "\t\t\t\t\t\t &self->c.", $m->{name}, ");\n"; + } elsif ($m->{elem}->{type} eq "uint32_t") { + print PYOUT "\t\t\treturn py_rxgen_get_uint32(&self->x.", $m->{name}, ", ", $m->{dim}, ",\n"; + print PYOUT "\t\t\t\t\t\t &self->c.", $m->{name}, ");\n"; } else { - die "Unsupported array type \"$type\""; + die $m->{where}, ": Unsupported array type \"", $m->{elem}->{type}, "\""; } } @@ -131,28 +163,26 @@ sub emit_py_type_wrapper($@) { # The attribute set function print PYOUT "static int\n"; - print PYOUT "py_", $struct, "_setattro(PyObject *_self, PyObject *name, PyObject *val)\n"; + print PYOUT "py_", $struct->{type}, "_setattro(PyObject *_self, PyObject *name, PyObject *val)\n"; print PYOUT "{\n"; - print PYOUT "\tstruct py_$struct *self = (struct py_$struct *)_self;\n"; + print PYOUT "\tstruct py_", $struct->{type}, " *self = (struct py_", $struct->{type}, " *)_self;\n"; print PYOUT "\n"; print PYOUT "\tif (PyUnicode_Check(name)) {\n"; - foreach my $m (@arrays) { - my ($type, $name, $array_size, $max_size) = @{$m}; - - print PYOUT "\t\tif (PyUnicode_CompareWithASCIIString(name, \"$name\") == 0)\n"; - if ($type eq "char") { - print PYOUT "\t\t\treturn py_rxgen_set_string(&self->x.$name, $array_size, val);\n"; - } elsif ($type eq "uint8_t") { - print PYOUT "\t\t\treturn py_rxgen_set_uint8(&self->x.$name, $array_size, val);\n"; - } elsif ($type eq "uint16_t") { - print PYOUT "\t\t\treturn py_rxgen_set_uint16(&self->x.$name, $array_size, val);\n"; - } elsif ($type eq "uint32_t") { - print PYOUT "\t\t\treturn py_rxgen_set_uint32(&self->x.$name, $array_size, val);\n"; - } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { - die "Don't py-wrap struct arrays yet"; + foreach my $m (@char_arrays, @single_structs, @arrays) { + print PYOUT "\t\tif (PyUnicode_CompareWithASCIIString(name, \"", $m->{name}, "\") == 0)\n"; + if ($m->{class} eq "struct") { + print PYOUT "\t\t\treturn py_rxgen_set_struct(&self->c.", $m->{name}, ",\n"; + print PYOUT "\t\t\t\t\t\t &py_", $struct->{type}, "Type, val);\n"; + } elsif ($m->{class} ne "array") { + die $m->{where}, ": Unsupported type class \"", $m->{class}, "\""; + } elsif ($m->{elem}->{type} eq "char") { + print PYOUT "\t\t\treturn py_rxgen_set_string(&self->x.", $m->{name}, ", ", $m->{dim}, ", val);\n"; + } elsif ($m->{elem}->{class} eq "basic" || + $m->{elem}->{class} eq "struct") { + print PYOUT "\t\t\treturn py_rxgen_set_array(", $m->{dim}, ", &self->c.", $m->{name}, ", val);\n"; } else { - die "Unsupported array type \"$type\""; + die $m->{where}, ": Unsupported array type \"", $m->{elem}->{type}, "\""; } } @@ -164,13 +194,12 @@ sub emit_py_type_wrapper($@) { } # Emit the Python type definition - print PYOUT "static PyTypeObject py_", $struct, "Type = {\n"; - + print PYOUT "static PyTypeObject py_", $struct->{type}, "Type = {\n"; print PYOUT "\tPyVarObject_HEAD_INIT(NULL, 0)\n"; - print PYOUT "\t\"kafs.$struct\",\t\t/*tp_name*/\n"; - print PYOUT "\tsizeof(struct py_$struct),\t/*tp_basicsize*/\n"; + print PYOUT "\t\"kafs.", $struct->{type}, "\",\t\t/*tp_name*/\n"; + print PYOUT "\tsizeof(struct py_", $struct->{type}, "),\t/*tp_basicsize*/\n"; print PYOUT "\t0,\t\t\t\t/*tp_itemsize*/\n"; - print PYOUT "\t(destructor)py_", $struct, "_dealloc, /*tp_dealloc*/\n"; + print PYOUT "\t(destructor)py_", $struct->{type}, "_dealloc, /*tp_dealloc*/\n"; print PYOUT "\t0,\t\t\t\t/*tp_print*/\n"; print PYOUT "\t0,\t\t\t\t/*tp_getattr*/\n"; print PYOUT "\t0,\t\t\t\t/*tp_setattr*/\n"; @@ -182,9 +211,9 @@ sub emit_py_type_wrapper($@) { print PYOUT "\t0,\t\t\t\t/*tp_hash */\n"; print PYOUT "\t0,\t\t\t\t/*tp_call*/\n"; print PYOUT "\t0,\t\t\t\t/*tp_str*/\n"; - if (@arrays) { - print PYOUT "\tpy_", $struct, "_getattro,\n"; - print PYOUT "\tpy_", $struct, "_setattro,\n"; + if ($#single_structs + $#arrays + $#char_arrays > -3) { + print PYOUT "\tpy_", $struct->{type}, "_getattro,\n"; + print PYOUT "\tpy_", $struct->{type}, "_setattro,\n"; } else { print PYOUT "\t0,\t\t\t\t/*tp_getattro*/\n"; print PYOUT "\t0,\t\t\t\t/*tp_setattro*/\n"; @@ -212,8 +241,8 @@ sub emit_py_type_wrapper($@) { print PYOUT "\t0,\t\t\t\t/* tp_iter */\n"; print PYOUT "\t0,\t\t\t\t/* tp_iternext */\n"; print PYOUT "\t0,\t\t\t\t/* tp_methods */\n"; - if (@singles) { - print PYOUT "\tpy_", $struct, "_members,\n"; + if (@single_ints) { + print PYOUT "\tpy_", $struct->{type}, "_members,\n"; } else { print PYOUT "\t0,\t\t\t/* tp_members */\n"; } @@ -225,20 +254,104 @@ sub emit_py_type_wrapper($@) { print PYOUT "\t0,\t\t\t\t/* tp_dictoffset */\n"; print PYOUT "\t0,\t\t\t\t/* tp_init */\n"; print PYOUT "\t0,\t\t\t\t/* tp_alloc */\n"; - print PYOUT "\tpy_", $struct, "_new,\t/* tp_new */\n"; + print PYOUT "\t0,\t\t\t\t/* tp_new */\n"; print PYOUT "};\n"; # Emit a function to allocate such a type - print PYHDR "extern PyObject *kafs_new_py_$struct(PyObject *, PyObject *);\n"; + print PYHDR "extern PyObject *kafs_new_py_", $struct->{type}, "(PyObject *, PyObject *);\n"; print PYOUT "\n"; print PYOUT "PyObject *\n"; - print PYOUT "kafs_new_py_$struct(PyObject *_self, PyObject *args)\n"; + print PYOUT "kafs_new_py_", $struct->{type}, "(PyObject *_self, PyObject *args)\n"; print PYOUT "{\n"; - print PYOUT "\tPyObject *obj;\n"; - print PYOUT "\tobj = _PyObject_New(&py_", $struct, "Type);\n"; - print PYOUT "\treturn obj ?: PyExc_MemoryError;\n"; + print PYOUT "\tPyObject *obj = _PyObject_New(&py_", $struct->{type}, "Type);\n"; + print PYOUT "\tstruct py_", $struct->{type}, " *self = (struct py_", $struct->{type}, " *)obj;\n"; + print PYOUT "\tif (!obj)\n"; + print PYOUT "\t\treturn PyExc_MemoryError;\n"; + print PYOUT "\tmemset(&self->x, 0, sizeof(self->x));\n"; + if ($#single_structs + $#arrays > -2) { + print PYOUT "\tmemset(&self->c, 0, sizeof(self->c));\n"; + } + print PYOUT "\treturn obj;\n"; print PYOUT "}\n"; + + # Emit a function to create an object of this type from raw data + print PYHDR "extern PyObject *py_data_to_", $struct->{type}, "(const void *);\n"; + + print PYOUT "\n"; + print PYOUT "PyObject *py_data_to_", $struct->{type}, "(const void *data) {\n"; + print PYOUT "\tPyObject *obj = _PyObject_New(&py_", $struct->{type}, "Type);\n"; + print PYOUT "\tstruct py_", $struct->{type}, " *self = (struct py_", $struct->{type}, " *)obj;\n"; + print PYOUT "\tif (!obj)\n"; + print PYOUT "\t\treturn PyExc_MemoryError;\n"; + print PYOUT "\tmemcpy(&self->x, data, sizeof(self->x));\n"; + if ($#single_structs + $#arrays > -2) { + print PYOUT "\tmemset(&self->c, 0, sizeof(self->c));\n"; + } + print PYOUT "\treturn obj;\n"; + print PYOUT "}\n"; + + # Emit a function to premarshal such a type. This folds the contents of + # the cached Python objects into their raw fields. + # + if ($#single_structs + $#arrays > -2) { + print PYHDR "extern int py_premarshal_", $struct->{type}, "(PyObject *);\n"; + + print PYOUT "\n"; + print PYOUT "int py_premarshal_", $struct->{type}, "(PyObject *_self)\n"; + print PYOUT "{\n"; + print PYOUT "\tstruct py_", $struct->{type}, " *self = (struct py_", $struct->{type}, " *)_self;\n"; + + # Check that the type we've been given is the right one + print PYOUT "\n"; + print PYOUT "\tif (!PyObject_TypeCheck(self, &py_", $struct->{type}, "Type)) {\n"; + print PYOUT "\t\tPyErr_Format(PyExc_TypeError, \"Expected object of type ", $struct->{type}, "\");\n"; + print PYOUT "\t\treturn -1;\n"; + print PYOUT "\t}\n"; + + print PYOUT "\n"; + my $first = 1; + foreach my $m (@single_structs, @arrays) { + if ($first) { + print PYOUT "\tif ("; + $first = 0; + } else { + print PYOUT " ||\n"; + print PYOUT "\t "; + } + + if ($m->{class} eq "struct") { + print PYOUT "py_rxgen_premarshal_struct(&self->x.", $m->{name}, ",\n"; + print PYOUT "\t\t\t\t sizeof(struct ", $m->{type}, "),\n"; + print PYOUT "\t\t\t\t offsetof(struct py_", $m->{type}, ", x),\n"; + print PYOUT "\t\t\t\t self->c.", $m->{name}, ",\n"; + print PYOUT "\t\t\t\t py_premarshal_", $m->{type}, ") < 0"; + } elsif ($m->{class} ne "array") { + die $m->{where}, ": Unsupported type class \"", $m->{class}, "\""; + } elsif ($m->{elem}->{class} eq "struct") { + print PYOUT "py_rxgen_premarshal_structs(&self->x.", $m->{name}, ",\n"; + print PYOUT "\t\t\t\t\t", $m->{dim}, ", sizeof(struct ", $m->{elem}->{type}, "),\n"; + print PYOUT "\t\t\t\t\toffsetof(struct py_", $m->{elem}->{type}, ", x),\n"; + print PYOUT "\t\t\t\t\tself->c.", $m->{name}, ",\n"; + print PYOUT "\t\t\t\t\tpy_premarshal_", $m->{elem}->{type}, ") < 0"; + } elsif ($m->{elem}->{class} ne "basic") { + die $m->{where}, ": Unsupported array type class \"", $m->{elem}->{class}, "\""; + } elsif ($m->{elem}->{type} eq "uint8_t") { + print PYOUT "py_rxgen_premarshal_uint8(&self->x.", $m->{name}, ", ", $m->{dim}, ", self->c.", $m->{name}, ") < 0"; + } elsif ($m->{elem}->{type} eq "uint16_t") { + print PYOUT "py_rxgen_premarshal_uint16(&self->x.", $m->{name}, ", ", $m->{dim}, ", self->c.", $m->{name}, ") < 0"; + } elsif ($m->{elem}->{type} eq "uint32_t") { + print PYOUT "py_rxgen_premarshal_uint32(&self->x.", $m->{name}, ", ", $m->{dim}, ", self->c.", $m->{name}, ") < 0"; + } else { + die $m->{where}, ": Unsupported array type \"", $m->{elem}->{type}, "\""; + } + } + + print PYOUT ")\n"; + print PYOUT "\t\treturn -1;\n"; + print PYOUT "\treturn 0;\n"; + print PYOUT "}\n"; + } } 1; diff --git a/rxgen/rxgen.pl b/rxgen/rxgen.pl index 0fc2dfb..ae58c8d 100755 --- a/rxgen/rxgen.pl +++ b/rxgen/rxgen.pl @@ -24,64 +24,220 @@ use emit_py_types; use emit_py_sync_funcs; use emit_py_module; -die "Need list of sources\n" if ($#ARGV < 0); +die "Need list of xg files to process\n" if ($#ARGV < 0); our @structs = (); # Structure definitions our %struct_sizes = (); # Structure sizes -our %funcs = (); # Function declarations -our %constants = (); # #defined constants +our @funcs = (); # Functions in declaration order +our %func_names = (); # Function name uniquifier +our %constants = (); # Constants +our @abort_codes = (); # Abort codes # # Divide the lines from the files up into typed collections # my @comment = (); +my $pkg = ""; my $banner = 0; my $struct = 0; -my $size = 0; my $func = 0; -my @members; +my $cpp_exclude = 0; +my $error_codes = 0; my @files = @ARGV; +my $file = ""; +my $where = ""; -die "No API files specified" unless (@files); +############################################################################### +# +# Handle defined types. +# +# Each type is specified by a hash of the following elements: +# +# class Complexity class (basic, string, struct, array, bulk) +# type Basic/struct type (char, {u,}int{8,16,32,64}_t, opaque, struct name) +# elem Ref to element type def (if array/bulk) +# multi 1 if array or bulk type, 0 otherwise +# dim Number of elements in array or max elements in bulk array (if array/bulk) +# members Members of struct +# xdr_size Size of XDR encoded object +# where Where defined in file +# banner Banner comment +# +# Members/parameters take a copy of their parent type's hash and add: +# +# name Member or parameter name +# ptr "*" if a pointer +# dir Direction (IN, OUT or INOUT) +# +############################################################################### +# Defined types +our %types = ( + "char" => { class => "basic", type => "char", xdr_size => 4, multi => 0, }, + "int8_t" => { class => "basic", type => "int8_t", xdr_size => 4, multi => 0, }, + "int16_t" => { class => "basic", type => "int16_t", xdr_size => 4, multi => 0, }, + "int32_t" => { class => "basic", type => "int32_t", xdr_size => 4, multi => 0, }, + "int64_t" => { class => "basic", type => "int64_t", xdr_size => 8, multi => 0, }, + "uint8_t" => { class => "basic", type => "uint8_t", xdr_size => 4, multi => 0, }, + "uint16_t" => { class => "basic", type => "uint16_t", xdr_size => 4, multi => 0, }, + "uint32_t" => { class => "basic", type => "uint32_t", xdr_size => 4, multi => 0, }, + "uint64_t" => { class => "basic", type => "uint64_t", xdr_size => 8, multi => 0, }, + "string" => { class => "string", type => "char", xdr_size => 1, multi => 0, }, + "opaque" => { class => "opaque", type => "void", xdr_size => 1, multi => 0, }, + ); + +sub look_up_type($) +{ + my ($type) = @_; + + die $where, ": Undefined type '$type'\n" unless exists $types{$type}; + return $types{$type}; +} -sub parse_header($) { - my ($file) = @_; - open my $APIHDR, "<$file" || die $file; +sub define_type($$) +{ + my ($new_type, $as) = @_; + die $where, ": Redefining type '$new_type'\n" if exists $types{$new_type}; + $as->{where} = $where; + $types{$new_type} = $as; +} + +sub define_typedef($$$) +{ + my ($new_type, $as, $flags) = @_; + + my $type = look_up_type($as); + + my %combined = %{$type}; + + if ($flags->{class} eq "bulk" || + $flags->{class} eq "array") { + die $where, ": Typedef'ing array/bulk as array/bulk not supported\n" + if ($type->{multi}); + $combined{multi} = 1; + $combined{class} = $flags->{class}; + $combined{elem} = $type; + $combined{dim} = $flags->{dim} if (exists $flags->{dim}); + $combined{xdr_size} *= $combined{dim} if ($flags->{class} eq "array"); + } + + define_type($new_type, \%combined); +} + +############################################################################### +# +# Parse an xg interface definition +# +############################################################################### +sub parse_xg($) { + my ($filename) = @_; + $file = $filename; + open my $APIHDR, "<$filename" || die $filename; while (my $line = <$APIHDR>) { + $where = $file . ':' . $. ; + + # Detect #if 0/#endif pairs to exclude parts + if ($line =~ m@^#\s*if\s+0@) { + die $where, ": Embedded #if 0 clause\n" if $cpp_exclude; + $cpp_exclude = 1; + next; + } + + if ($line =~ m@^#\s*endif@) { + die $where, ": Unbalanced #endif\n" unless $cpp_exclude; + $cpp_exclude = 0; + next; + } + + next if $cpp_exclude; + # Gather comments for later attachment to subsequent structs and funcs - if ($line =~ m@^/[*]@) { - die if $banner; + if ($line =~ m@^/[*]\s*$@) { + die $where, ": Embedded comment\n" if $banner; $banner = 1; @comment = ( $line ); next; } if ($banner) { - die if ($line !~ /^ [*]/); + die $where, ": Commentless terminator\n" if ($line !~ /^ [*]/); push @comment, $line; $banner = 0 if ($line =~ m!^ [*]/!); next; } - # Extract #defines - if ($line =~ /^#define\s+([A-Za-z0-9_]+)\s+(.*)/) { - $constants{$1} = $2 if ($2); + chomp($line); + + @comment = () if ($line eq ""); + + # Complain about #defines + die $where, ": Use const not #define" if ($line =~ /^#define/); + + # Extract package prefix + if ($line =~ /^package\s+([A-Za-z_][A-Za-z0-9_]*)/) { + $pkg = $1; + next; + } + + # Extract error codes + if ($line eq "/* Error codes */") { + $error_codes = 1; + next; + } + + $error_codes = 0 if ($line eq ""); + + ####################################################################### + # Extract constants + # + if ($line =~ /^const\s+([A-Za-z0-9_]+)\s*=\s*(.*);/) { + my $c = $1; + my $v = $2; + die $where, ": Duplicate constant $c" if (exists $constants{$c}); + $v =~ s/^\s+//; + $v =~ s/\s+$//; + $constants{$c} = $v; + push @abort_codes, $c if $error_codes; @comment = (); next; } + ####################################################################### + # Extract typedefs + # + if ($line =~ /^typedef\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*;/) { + define_typedef($2, $1, { }); + next; + } + + if ($line =~ /^typedef\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*<\s*>\s*;/) { + define_typedef($2, $1, { class => "bulk" }); + next; + } + + if ($line =~ /^typedef\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*<\s*([a-zA-Z0-9_]+)\s*>\s*;/) { + define_typedef($2, $1, { class => "bulk", dim => $3 }); + next; + } + + ####################################################################### # Extract structures - if ($line =~ /^struct ([a-zA-Z_][a-zA-Z0-9_]*) {/) { - $struct = "$1"; - $size = 0; - @members = ( [ @comment ] ); + # + if ($line =~ /^struct\s+([a-zA-Z_][a-zA-Z0-9_]*) {/) { + my %type = ( + class => "struct", + type => $1, + members => [], + banner => \@comment, + xdr_size => 0, + ); + define_type($1, \%type); + push @structs, \%type; + $struct = \%type; + @comment = (); next; } if ($line =~ /};/ && $struct) { - push @structs, [ $struct, @members ]; - $struct_sizes{$struct} = $size; - @members = (); $struct = 0; next; } @@ -91,87 +247,131 @@ sub parse_header($) { # Strip trailing comments $line =~ s@\s*/[*][^*]*[*]/$@@; - if ($line =~ /(uint[0-9]+_t|char)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*;/) { - push @members, [ $1, $2, -1, -1 ]; - $size += 4; + if ($line =~ /([a-zA-Z_][a-zA-Z0-9_]*)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*;/) { + my %member = %{look_up_type($1)}; + die $where, ": Don't support bulk constructs in structs\n" + if ($member{class} eq "bulk"); + $member{name} = $2; + $member{where} = $where; + push $struct->{members}, \%member; + $struct->{xdr_size} += $member{xdr_size}; #print "nonarray $2\n"; - } elsif ($line =~ /(uint[0-9]+_t|char)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\[\s*([^]]+)\s*\]\s*;/) { - my $type = $1; - my $name = $2; - my $array_size = $3; - if ($array_size =~ /^[0-9]+$/) { - } elsif (exists $constants{$array_size}) { - $array_size = $constants{$array_size}; + } elsif ($line =~ /([a-zA-Z_][a-zA-Z0-9_]*)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\[\s*([^]]+)\s*\]\s*;/) { + my $element = look_up_type($1); + die $where, ": Don't support arrays of bulk constructs or arrays\n" + if ($element->{multi}); + + my %member = (); + $member{class} = "array"; + $member{elem} = $element; + $member{name} = $2; + $member{dim} = $3; + $member{where} = $where; + + if ($member{dim} =~ /^[0-9]+$/) { + $constants{$member{dim}} = $member{dim}; + } elsif (exists $constants{$member{dim}}) { } else { - die "In struct $struct: No constant for [$array_size]" + die $where, ": No constant for [", $member{dim}, "]\n" } - push @members, [ $type, $name, $array_size, -1 ]; - $size += $array_size * 4; + $member{xdr_size} = $constants{$member{dim}} * $element->{xdr_size}; + push $struct->{members}, \%member; + $struct->{xdr_size} += $member{xdr_size}; #print "array $2\n"; } else { - die "Unrecognised struct member"; + die $where, ": Unrecognised struct member '$line'"; } } + ####################################################################### # Extract functions - if ($line =~ /^extern\s+int\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(\s*\n/) { + # + if (!$func && $line =~ /^([a-zA-Z_][a-zA-Z0-9_]*)\s*\(\s*(.*)$/) { #print "func $1\n"; - $func = "$1"; - @members = ( [ @comment ] ); - next; + my %function = ( + name => $pkg . $1, + banner => \@comment, + params => [], + where => $where, + ); + die $where, ": Duplicate function name '$1'\n" + if (exists($func_names{$1})); + $func_names{$1} = \%function; + push @funcs, \%function; + $func = \%function; + @comment = (); + $line = $2; } # Extract function parameters if ($func) { my $dir = ""; - my $const = ""; - my $type = ""; - my $name = ""; my $term = 0; - my $max = -1; - - chomp $line; - - $dir = $1 if ($line =~ s@/[*](IN|OUT|INOUT)[*]/\s+@@); - - if ($line =~ s@/[*]\s+Max ([0-9]+)\s+[*]/@@) { - $max = $1; - } elsif ($line =~ s@/[*]\s+Max ([a-zA-Z0-9_]+)\s+[*]/@@) { - die "No constant for $1" unless exists $constants{$1}; - $max = $constants{$1}; + my $bulk_dim = 0; + + $dir = $1 if ($line =~ s@^\s*(IN|OUT|INOUT)\s+@@); + + if ($line =~ s@<\s*>@@) { + $bulk_dim = -1; + } elsif ($line =~ s@<\s*([0-9]+)\s*>@@) { + $bulk_dim = $1; + $constants{$bulk_dim} = $bulk_dim; + } elsif ($line =~ s@<\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*>@@) { + die $where, ": No constant for $1\n" unless exists $constants{$1}; + $bulk_dim = $1; } # Strip trailing comments $line =~ s@\s*/[*][^*]*[*]/$@@; $line =~ s/\s+$//; - if ($line =~ s/[)];$//) { + if ($line =~ s/[)]\s*=\s*([a-zA-Z0-9_]*)\s*;$//) { $term = 1; + $func->{opcode} = $1; } elsif ($line =~ s/,$//) { $term = 0; } else { - die "Unexpected line termination '$line'"; + die $where, ": Unexpected line termination '$line'"; } $line =~ s/\s+$//; - $const = $1 if ($line =~ s@^const\s+@@); - #print "\"", $line, "\"\n"; - if ($line =~ /(struct\s+[a-zA-Z_][a-zA-Z0-9_]*|uint[0-9]+_t|char)\s+([*]*)([a-zA-Z_][a-zA-Z0-9_]*)\s*/) { - $type = $1 . $2; - $name = $3; + if ($line =~ /([a-zA-Z_][a-zA-Z0-9_]*)\s+([*]*)([a-zA-Z_][a-zA-Z0-9_]*)\s*/) { + die $where, ": No parameter direction specified\n" unless $dir; + + my $type = look_up_type($1); + my %param = %{$type}; + $param{ptr} = $2; + $param{name} = $3; + $param{dir} = $dir; + $param{where} = $where; + + die $where, ": 'string' only supported with IN\n" + if ($param{type} eq "string" && $dir ne "IN"); + die $where, ": Array parameters not supported\n" + if ($param{class} eq "array"); + if ($bulk_dim) { + die $where, ": Bulk-of-bulk parameters not supported\n" + if ($param{class} eq "bulk"); + $param{class} = "bulk"; + $param{elem} = $type; + $param{dim} = $bulk_dim; + } + + $param{ptr} = "*" if ($type->{class} eq "string"); + + #print "- ", $1, " ", $param{name}, " ISA ", $param{class}, ".", $param{type}, " ", $param{dir}, "\n"; + push $func->{params}, \%param; - #print "- ", $name, " ISA ", $type, " ", $dir, "\n"; - push @members, [ $type, $name, -1, $max, $dir ] unless ($1 eq "struct rx_connection"); + } elsif ($line eq "") { + # Parameterless function } else { - die "Unhandled RPC call parameter"; + die $where, ": Unhandled RPC call parameter '$line'"; } if ($term) { - $funcs{$func} = [ @members ]; - @members = (); $func = 0; next; } @@ -182,40 +382,43 @@ sub parse_header($) { } foreach my $file (@files) { - parse_header($file); + parse_xg($file); } print "Extracted ", scalar keys %constants, " constants\n"; -print "Extracted ", scalar @structs, " types\n"; -print "Extracted ", scalar keys %funcs, " functions\n"; +print "Extracted ", scalar @structs, " structs\n"; +print "Extracted ", scalar keys %types, " types\n"; +print "Extracted ", scalar @funcs, " functions\n"; +print "Extracted ", scalar @abort_codes, " abort codes\n"; ############################################################################### # # Create the output files and emit the file prologues. # ############################################################################### +open RXHDR, ">afs_xg.h" || die "afs_xg.h"; +print RXHDR "/* AUTOGENERATED */\n"; +#print RXHDR "#define _XOPEN_SOURCE\n"; +print RXHDR "#include \n"; +print RXHDR "#include \"rxgen.h\"\n"; + open RXOUT, ">afs_xg.c" || die "afs_xg.c"; print RXOUT "/* AUTOGENERATED */\n"; -print RXOUT "#define _XOPEN_SOURCE\n"; -print RXOUT "#include \n"; +print RXOUT "#include \"afs_xg.h\"\n"; print RXOUT "#include \n"; print RXOUT "#include \n"; print RXOUT "#include \n"; print RXOUT "#include \n"; print RXOUT "#include \n"; print RXOUT "#include \n"; +print RXOUT "#include \n"; print RXOUT "#include \n"; -print RXOUT "#include \"rxgen.h\"\n"; +print RXOUT "\n"; open PYHDR, ">afs_py.h" || die "afs_py.h"; print PYHDR "/* AUTOGENERATED */\n"; print PYHDR "#include \n"; -print PYHDR "#include \n"; - -foreach $_ (@files) { - print RXOUT "#include \"$_\"\n"; - print PYHDR "#include \"$_\"\n"; -} +print PYHDR "#include \"afs_xg.h\"\n"; open PYOUT, ">afs_py.c" || die "afs_py.c"; print PYOUT "/* AUTOGENERATED */\n"; @@ -224,13 +427,23 @@ print PYOUT "#include \"structmember.h\"\n"; print PYOUT "#include \"afs_py.h\"\n"; print PYOUT "#include \n"; print PYOUT "#include \"py_rxgen.h\"\n"; +print PYOUT "\n"; + +# Declare constants +print RXHDR "\n"; +foreach my $c (sort keys %constants) { + print RXHDR "#define $c ", $constants{$c}, "\n" unless ($c =~ /^[0-9]/) +} # Declare types foreach my $s (@structs) { - my @members = @{$s}; - my $struct = shift @members; - emit_struct_encdec($struct, @members); - emit_py_type_wrapper($struct, @members); + emit_struct_encdec_decls($s); + emit_py_type_wrapper_decls($s); +} + +foreach my $s (@structs) { + emit_struct_encdec($s); + emit_py_type_wrapper($s); } ############################################################################### @@ -239,26 +452,22 @@ foreach my $s (@structs) { # to input and output usage and work out how big the RPC messages will be. # ############################################################################### -foreach $func (sort keys %funcs) { - my @params = @{$funcs{$func}}; - +foreach $func (@funcs) { # Dump the banner comment block - my @banner = @{shift @params}; print RXOUT "\n"; - print RXOUT @banner; - print PYOUT "\n"; - print PYOUT @banner; - - # Find the Operation ID from the banner comment - my $op = ""; - foreach $_ (@banner) { - if ($_ =~ /Operation: ([A-Z][_A-Z0-9]+)/) { - $op = $1; - last; - } + if (@{$func->{banner}}) { + print RXOUT @{$func->{banner}}; + print PYOUT "\n"; + print PYOUT @{$func->{banner}}; + } else { + print RXOUT "/*\n"; + print RXOUT " * RPC Call ", $func->{name}, "\n"; + print RXOUT " */\n"; } - die "Operation ID unspecified for $func" unless $op; + # Find the Operation ID + die "Operation ID unspecified for ", $func->{name}, "\n" + unless exists $func->{opcode}; # Filter the parameters into request and reply my @request = (); @@ -266,64 +475,74 @@ foreach $func (sort keys %funcs) { my $request_size = 4; my $reply_size = 0; my $req_has_charptr = 0; + my $req_size_is_indefinite = 0; + my $rep_size_is_indefinite = 0; - foreach my $p (@params) { - my ($type, $name, $array_size, $max_size, $dir) = @{$p}; - + foreach my $p (@{$func->{params}}) { #print RXOUT $dir, " ", $type, " ", $name, "\n"; - my $size = 0; - my $has_charptr = 0; - if ($array_size == -1) { - if ($type eq "char*") { - $size = (4 + $max_size + 3) & ~3; - $has_charptr = 1; - } elsif ($type eq "uint8_t" || $type eq "uint8_t*" || - $type eq "uint16_t" || $type eq "uint16_t*" || - $type eq "uint32_t" || $type eq "uint32_t*" || - $type eq "uint64_t" || $type eq "uint64_t*") { - $size = 4; - } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) { - die "No encoding for $type" unless exists $struct_sizes{$1}; - $size = $struct_sizes{$1}; + my $buffer_size = 0; + my $indefinite = 0; + if ($p->{class} eq "basic") { + $buffer_size = $p->{xdr_size}; + + } elsif ($p->{class} eq "struct") { + die unless (exists $p->{xdr_size}); + $buffer_size = $p->{xdr_size}; + + } elsif ($p->{class} eq "bulk") { + die $p->{where}, ": No element type" unless (exists $p->{elem}); + die $p->{where}, ": Element has no XDR size" unless (exists $p->{elem}->{xdr_size}); + if (exists $p->{dim} && $p->{elem}->{xdr_size} > 0) { + $buffer_size = $p->{elem}->{xdr_size}; + $buffer_size *= $constants{$p->{dim}}; + $buffer_size = (4 + $buffer_size + 3) & ~3; + $req_has_charptr = 1 if ($p->{elem}->{class} eq "string"); } else { - die "Unsupported type \"$type\""; + $buffer_size = 4; + $indefinite = 1; } } else { - if ($type eq "uint8_t" || $type eq "uint8_t*" || - $type eq "uint16_t" || $type eq "uint16_t*" || - $type eq "uint32_t" || $type eq "uint32_t*" || - $type eq "uint64_t" || $type eq "uint64_t*") { - $size = 4; - } else { - die "Unsupported type \"$type\""; - } - $size *= $array_size; + die $p->where, ": Unsupported param class \"", $p->{class}, "\""; } - if ($dir eq "IN") { + if ($p->{dir} eq "IN") { push @request, $p; - $request_size += $size; - $req_has_charptr |= $has_charptr; - } elsif ($dir eq "OUT") { + $request_size += $buffer_size; + $req_size_is_indefinite |= $indefinite; + } elsif ($p->{dir} eq "OUT") { push @reply, $p; - $reply_size += $size; - } elsif ($dir eq "INOUT") { + $reply_size += $buffer_size; + $rep_size_is_indefinite |= $indefinite; + } elsif ($p->{dir} eq "INOUT") { push @reply, $p; push @request, $p; - $request_size += $size; - $reply_size += $size; - $req_has_charptr |= $has_charptr; + $request_size += $buffer_size; + $reply_size += $buffer_size; + $req_size_is_indefinite |= $indefinite; + $rep_size_is_indefinite |= $indefinite; } } + $func->{request} = \@request; + $func->{request_size} = $request_size unless ($req_size_is_indefinite); + $func->{req_has_charptr} = $req_has_charptr; + $func->{reply} = \@reply; + $func->{reply_size} = $reply_size unless ($rep_size_is_indefinite); + #print RXOUT "/* req max size: $request_size */\n"; #print RXOUT "/* rep max size: $reply_size */\n"; - emit_func_enc_request($func, $op, $request_size, $req_has_charptr, $reply_size, @request); - emit_func_dec_reply($func, $op, $reply_size, @reply) - if (@reply); - emit_func_simple_sync_call($func, $request_size, $reply_size, \@request, \@reply, @params); - emit_py_func_simple_sync_call($func, \@request, \@reply, @params); + calc_func_prototype($func); + emit_func_send_request($func); + if (@reply) { + emit_func_decode_reply($func); + } else { + emit_func_dummy_decode_reply($func); + } + emit_func_alloc_call($func); + emit_func_simple_sync_call($func); + emit_py_func_bulk_helper($func); + emit_py_func_simple_sync_call($func); } emit_py_module(); diff --git a/vl-test.py b/vl-test.py index 789f85e..eb0ffb6 100755 --- a/vl-test.py +++ b/vl-test.py @@ -63,7 +63,7 @@ print("vladdrs:", vladdrs); for vlserver in vladdrs: print("Trying", vlserver); - z_conn = kafs.rx_new_connection(vlserver, kafs.AFS_VL_PORT, kafs.VL_SERVICE); + z_conn = kafs.rx_new_connection(vlserver, kafs.VL_PORT, kafs.VL_SERVICE); try: ret = kafs.VL_Probe(z_conn); -- 2.49.0