]> www.infradead.org Git - users/dhowells/kafs-utils.git/commitdiff
rxgen: Use the xg files as sources for rxgen
authorDavid Howells <dhowells@redhat.com>
Fri, 10 Jan 2014 01:44:40 +0000 (01:44 +0000)
committerDavid Howells <dhowells@redhat.com>
Fri, 10 Jan 2014 01:44:40 +0000 (01:44 +0000)
Use the Arla xg files rather than C headers as sources for the rxgen script.

Signed-off-by: David Howells <dhowells@redhat.com>
16 files changed:
Makefile
af_rxrpc.c
circ_buf.h [new file with mode: 0644]
compile_pykafs.py
py_rxgen.c
py_rxgen.h
rpc-api/afsuuid.h
rpc-api/vldb.xg
rxgen.h
rxgen/emit_c_struct.pm
rxgen/emit_c_sync_funcs.pm
rxgen/emit_py_module.pm
rxgen/emit_py_sync_funcs.pm
rxgen/emit_py_types.pm
rxgen/rxgen.pl
vl-test.py

index 3de2872bb3629837ee4862be7725334a3e8dd479..33be8567673795c2682dcd3307d924b99226f240 100644 (file)
--- 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)
index ca5fa6aaf7676424c544d434e519ae5f11b27e50..a65cf63245dde579a71c84f9dc4ce35b4858e174 100644 (file)
@@ -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 (file)
index 0000000..27da8f6
--- /dev/null
@@ -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  */
index c5af6edff21344a9f7baabaf06637216dc3ada48..0b48c7fc4734782b10ade832d8637c490fead3a8 100644 (file)
@@ -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"
                                      ],
                          )])
index 8f94e6aa77673858b3301277a1b1e8a9d40b1054..b05ca71315dfe2a15e343db35041fa5347b1baf7 100644 (file)
 #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;
 }
 
 /*
index bbe8392363c3a865eb9aedbf684a692c4c0a3f6d..63913794298b274d6a9ad23bb9b73d0e9bf1f066 100644 (file)
@@ -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 */
index acd2bb5dd3affb331ebe77cc18243d8b72fe9d0b..6b045768986bed426c1e2ddd216f3d7d63bda267 100644 (file)
 %#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_ */
index 6e720789b0365319957cbc6be6d87cb76c3a8514..326e20958d3f802a75e7fae70e2d5514818a7fa6 100644 (file)
@@ -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 2637dc9f0ac8196b2c603f1c943e04db487ead06..4aa8f508a8b3ae3fac5afdd8baa417b720f86bb4 100644 (file)
--- 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);
index 825f5c9060ded02f25f46b40be5c11d56eae633f..75bab2b53bc26ad9a18d7e190ba4a188c33644b3 100644 (file)
@@ -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;
index aaa939feea06dbf667bc5630af8eacaeee442219..ba21fd03cca8fa82ecedd6a12e606c6f243e63f4 100644 (file)
 # 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";
 }
 
index de31f76eb35da1a67f0e417404c4cfc548d8c1f3..54c0ea0452fbc57379bcbeae3b9b0d1a6d7b86b7 100644 (file)
@@ -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";
index 52649d36cef91926c9fe9dee52b41fd0694c5a7d..cdf48143ee5e703119f795202f8d13fc545e46b6 100644 (file)
 # 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 "&param_$name";
-       } elsif ($type =~ /struct ([a-zA-Z_][a-zA-Z0-9_]*)[*]/) {
-           print PYOUT "&py_", $1, "Type, &param_$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 "&param_", $p->{name};
+       } elsif ($p->{class} eq "struct") {
+           print PYOUT "&py_", $p->{type}, "Type, &param_", $p->{name};
+       } elsif ($p->{class} eq "bulk") {
+           print PYOUT "&PyList_Type, &param_", $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&param_$name";
+               print PYOUT "\t\t&param_", $p->{name};
+           }
+       } elsif ($p->{class} eq "struct") {
+           print PYOUT "\t\t&param_", $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&param_$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";
            }
        }
     }
index 73d4f65828b5b6798a8959382ec76918f1ddfc99..3be828a7182f9285e0fa1db7acbd7a08bf0aa526 100644 (file)
 # 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;
index 0fc2dfb6445157823398f93030dfa95c1c4a22da..ae58c8d5240399c12e7787d81b59b97fff2560b2 100755 (executable)
@@ -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 <stdint.h>\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 <stdint.h>\n";
+print RXOUT "#include \"afs_xg.h\"\n";
 print RXOUT "#include <stdio.h>\n";
 print RXOUT "#include <stdlib.h>\n";
 print RXOUT "#include <string.h>\n";
 print RXOUT "#include <unistd.h>\n";
 print RXOUT "#include <errno.h>\n";
 print RXOUT "#include <sys/socket.h>\n";
+print RXOUT "#include <sys/param.h>\n";
 print RXOUT "#include <arpa/inet.h>\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 <Python.h>\n";
-print PYHDR "#include <stdint.h>\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 <arpa/inet.h>\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();
index 789f85e92a76deb64e7f6b28adb4a28392b49f9b..eb0ffb6b04ed277b8950615206abf437c1dbaaad 100755 (executable)
@@ -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);