/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include "impl.h" #undef aio_oq_init #define OQ AIO_OQ #define OQE AIO_OQE struct aio_oqe { OQE *link; int special; union { struct { const char *data; int left; void (*done)(void *); void *donearg; } norm; struct { void *vp; int i; } spec; } ; } ; #define head aio_v1.aio_head #define tail aio_v1.aio_tail #define len aio_v1.aio_len static void queue_common(OQ *q, OQE *e) { e->link = 0; *q->tail = e; q->tail = &e->link; } static void queue_norm(OQ *q, OQE *e, int l) { e->special = 0; e->norm.left = l; q->len += l; queue_common(q,e); } void aio_oq_init(OQ *q, int ver) { switch (ver) { default: panic("aio_oq_init: impossible version"); break; case 1: break; } q->aio_version = ver; q->len = 0; q->head = 0; q->tail = &q->head; } unsigned int aio_oq_qlen(OQ *q) { return(q->len); } int aio_oq_headlen(OQ *q) { if (q->head == 0) return(AIO_OQ_HL_EMPTY); if (q->head->special) return(AIO_OQ_HL_SPECIAL); return(q->head->norm.left); } int aio_oq_empty(OQ *q) { return(q->head == 0); } int aio_oq_nonempty(OQ *q) { return(q->head != 0); } void aio_oq_queue_point(OQ *q, const void *data, int l) { OQE *e; if (l == AIO_STRLEN) l = strlen(data); if (l == 0) return; if (l < 1) panic("bad length"); e = malloc(sizeof(OQE)); e->norm.data = data; e->norm.done = 0; queue_norm(q,e,l); } void aio_oq_queue_copy(OQ *q, const void *data, int l) { OQE *e; if (l == AIO_STRLEN) l = strlen(data); if (l == 0) return; if (l < 1) panic("bad length"); e = malloc(sizeof(OQE)+l); e->norm.data = (void *) (e+1); e->norm.done = 0; bcopy(data,e+1,l); queue_norm(q,e,l); } void aio_oq_queue_free(OQ *q, void *data, int l) { OQE *e; if (l == AIO_STRLEN) l = strlen(data); if (l == 0) { free(data); return; } if (l < 1) panic("bad length"); e = malloc(sizeof(OQE)); e->norm.data = data; e->norm.done = &free; e->norm.donearg = data; queue_norm(q,e,l); } void aio_oq_queue_cb(OQ *q, const void *data, int l, void (*cb)(void *), void *cbarg) { OQE *e; if (l == AIO_STRLEN) l = strlen(data); if (l == 0) { (*cb)(cbarg); return; } if (l < 1) panic("bad length"); e = malloc(sizeof(OQE)); e->norm.data = data; e->norm.done = cb; e->norm.donearg = cbarg; queue_norm(q,e,l); } void aio_oq_queue_printf(OQ *q, const char *fmt, ...) { va_list ap; char *s; int l; OQE *e; va_start(ap,fmt); l = vasprintf(&s,fmt,ap); va_end(ap); if (l < 1) return; e = malloc(sizeof(OQE)); e->norm.data = s; e->norm.done = &free; e->norm.donearg = s; queue_norm(q,e,l); } void aio_oq_queue_special(OQ *q, void *vp, int i) { OQE *e; e = malloc(sizeof(OQE)); e->special = 1; e->spec.vp = vp; e->spec.i = i; queue_common(q,e); } int aio_oq_writev(OQ *q, int fd, int maxb, int (*dospecial)(void *, int)) { static int maxiov = -1; static struct iovec *iov; static int iovn; int iovl; OQE *e; int nb; int totb; if (maxiov < 0) { int mib[2]; size_t oldlen; mib[0] = CTL_KERN; mib[1] = KERN_IOV_MAX; oldlen = sizeof(maxiov); if (sysctl(&mib[0],2,&maxiov,&oldlen,0,0) < 0) { panic("can't get kern.iov_max: %s",strerror(errno)); } if (maxiov > 64) maxiov = 64; if (maxiov < 1) maxiov = 1; iov = 0; iovn = 0; } iovl = 0; totb = 0; for (e=q->head;e;e=e->link) { if ((maxb >= 0) && (totb >= maxb)) break; if (iovl >= maxiov) break; if (e->special) { int rv; if (iovl > 0) break; if (! dospecial) panic("unhandled special"); if (! (q->head = e->link)) q->tail = &q->head; rv = (*dospecial)(e->spec.vp,e->spec.i); free(e); return(rv); } if (iovl >= iovn) iov = realloc(iov,(iovn=iovl+4)*sizeof(*iov)); nb = e->norm.left; if ((maxb >= 0) && (nb > maxb-totb)) nb = maxb - totb; iov[iovl++] = (struct iovec){ .iov_base=dequal(e->norm.data), .iov_len=nb }; totb += nb; } if (iovl < 1) return(0); return(writev(fd,iov,iovl)); } int aio_oq_dropdata(OQ *q, int n) { OQE *e; while ((e=q->head) && n && (n >= e->norm.left)) { if (e->special) panic("dropping special"); q->head = e->link; q->len -= e->norm.left; n -= e->norm.left; if (e->norm.done) (*e->norm.done)(e->norm.donearg); free(e); } if (e) { if (n) { e->norm.data += n; e->norm.left -= n; q->len -= n; } return(0); } else { q->tail = &q->head; if (q->len) panic("empty queue with nonzero length"); return(1); } } void aio_oq_flush(OQ *q) { OQE *e; while (q->head) { e = q->head; q->head = e->link; if (!e->special && e->norm.done) (*e->norm.done)(e->norm.donearg); free(e); } q->len = 0; q->tail = &q->head; } void aio_oq_flush_special(OQ *q, void (*dospecial)(void *, int)) { OQE *e; while (q->head) { e = q->head; q->head = e->link; if (e->special) { (*dospecial)(e->spec.vp,e->spec.i); } else if (e->norm.done) { (*e->norm.done)(e->norm.donearg); } free(e); } q->len = 0; q->tail = &q->head; }