ved_pretend_gzip(struct req *req, enum vdp_action act, void **priv,
    const void *pv, ssize_t l)
{
	uint8_t buf1[5], buf2[5];
	const uint8_t *p;
	uint16_t lx;
	struct ecx *ecx;
	struct req *preq;

	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
	CAST_OBJ_NOTNULL(ecx, *priv, ECX_MAGIC);
	preq = ecx->preq;

	(void)priv;
	if (act == VDP_INIT)
		return (0);
	if (act == VDP_FINI) {
		*priv = NULL;
		return (0);
	}
	if (l == 0)
		return (ved_bytes(req, ecx->preq, act, pv, l));

	p = pv;

	if (ecx->isgzip) {
		ecx->crc = crc32(ecx->crc, p, l);
		ecx->l_crc += l;
	}

	lx = 65535;
	buf1[0] = 0;
	vle16enc(buf1 + 1, lx);
	vle16enc(buf1 + 3, ~lx);

	while (l > 0) {
		if (l >= 65535) {
			lx = 65535;
			if (ved_bytes(req, preq, VDP_NULL, buf1, sizeof buf1))
				return (-1);
		} else {
			lx = (uint16_t)l;
			buf2[0] = 0;
			vle16enc(buf2 + 1, lx);
			vle16enc(buf2 + 3, ~lx);
			if (ved_bytes(req, preq, VDP_NULL, buf2, sizeof buf2))
				return (-1);
		}
		if (ved_bytes(req, preq, VDP_NULL, p, lx))
			return (-1);
		l -= lx;
		p += lx;
	}
	/* buf2 is local, have to flush */
	return (ved_bytes(req, preq, VDP_FLUSH, NULL, 0));
}
ved_vdp_bytes(struct req *req, enum vdp_action act, void **priv,
    const void *ptr, ssize_t len)
{
	struct req *preq;

	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
	if (act == VDP_INIT)
		return (0);
	if (act == VDP_FINI) {
		*priv = NULL;
		return (0);
	}
	CAST_OBJ_NOTNULL(preq, *priv, REQ_MAGIC);
	return (ved_bytes(req, preq, act, ptr, len));
}
static void
ved_stripgzip(struct req *req, struct busyobj *bo)
{
	ssize_t start, last, stop, lpad;
	ssize_t l;
	char *p;
	uint32_t icrc;
	uint32_t ilen;
	uint64_t olen;
	uint8_t *dbits;
	uint8_t *pp;
	uint8_t tailbuf[8];
	enum objiter_status ois;
	void *oi;
	void *sp;
	ssize_t sl, ll, dl;
	struct ecx *ecx;
	struct req *preq;

	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
	CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
	CAST_OBJ_NOTNULL(ecx, req->transport_priv, ECX_MAGIC);
	preq = ecx->preq;

	if (bo != NULL)
		VBO_waitstate(bo, BOS_FINISHED);

	AN(ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED));

	/*
	 * This is the interesting case: Deliver all the deflate
	 * blocks, stripping the "LAST" bit of the last one and
	 * padding it, as necessary, to a byte boundary.
	 */

	p = ObjGetattr(req->wrk, req->objcore, OA_GZIPBITS, &l);
	AN(p);
	assert(l == 32);
	start = vbe64dec(p);
	last = vbe64dec(p + 8);
	stop = vbe64dec(p + 16);
	olen = ObjGetLen(req->wrk, req->objcore);
	assert(start > 0 && start < olen * 8);
	assert(last > 0 && last < olen * 8);
	assert(stop > 0 && stop < olen * 8);
	assert(last >= start);
	assert(last < stop);

	/* The start bit must be byte aligned. */
	AZ(start & 7);

	/*
	 * XXX: optimize for the case where the 'last'
	 * XXX: bit is in a empty copy block
	 */

	memset(tailbuf, 0xdd, sizeof tailbuf);
	dbits = WS_Alloc(req->ws, 8);
	AN(dbits);
	ll = 0;
	oi = ObjIterBegin(req->wrk, req->objcore);
	do {
		ois = ObjIter(req->objcore, oi, &sp, &sl);
		pp = sp;
		if (sl > 0) {
			/* Skip over the GZIP header */
			dl = start / 8 - ll;
			if (dl > 0) {
				/* Before start, skip */
				if (dl > sl)
					dl = sl;
				ll += dl;
				sl -= dl;
				pp += dl;
			}
		}
		if (sl > 0) {
			/* The main body of the object */
			dl = last / 8 - ll;
			if (dl > 0) {
				if (dl > sl)
					dl = sl;
				if (ved_bytes(req, preq, VDP_NULL, pp, dl))
					break;
				ll += dl;
				sl -= dl;
				pp += dl;
			}
		}
		if (sl > 0 && ll == last / 8) {
			/* Remove the "LAST" bit */
			dbits[0] = *pp;
			dbits[0] &= ~(1U << (last & 7));
			if (ved_bytes(req, preq, VDP_NULL, dbits, 1))
				break;
			ll++;
			sl--;
			pp++;
		}
		if (sl > 0) {
			/* Last block */
			dl = stop / 8 - ll;
			if (dl > 0) {
				if (dl > sl)
					dl = sl;
				if (ved_bytes(req, preq, VDP_NULL, pp, dl))
					break;
				ll += dl;
				sl -= dl;
				pp += dl;
			}
		}
		if (sl > 0 && (stop & 7) && ll == stop / 8) {
			/* Add alignment to byte boundary */
			dbits[1] = *pp;
			ll++;
			sl--;
			pp++;
			switch((int)(stop & 7)) {
			case 1: /*
				 * x000....
				 * 00000000 00000000 11111111 11111111
				 */
			case 3: /*
				 * xxx000..
				 * 00000000 00000000 11111111 11111111
				 */
			case 5: /*
				 * xxxxx000
				 * 00000000 00000000 11111111 11111111
				 */
				dbits[2] = 0x00; dbits[3] = 0x00;
				dbits[4] = 0xff; dbits[5] = 0xff;
				lpad = 5;
				break;
			case 2: /* xx010000 00000100 00000001 00000000 */
				dbits[1] |= 0x08;
				dbits[2] = 0x20;
				dbits[3] = 0x80;
				dbits[4] = 0x00;
				lpad = 4;
				break;
			case 4: /* xxxx0100 00000001 00000000 */
				dbits[1] |= 0x20;
				dbits[2] = 0x80;
				dbits[3] = 0x00;
				lpad = 3;
				break;
			case 6: /* xxxxxx01 00000000 */
				dbits[1] |= 0x80;
				dbits[2] = 0x00;
				lpad = 2;
				break;
			case 7:	/*
				 * xxxxxxx0
				 * 00......
				 * 00000000 00000000 11111111 11111111
				 */
				dbits[2] = 0x00;
				dbits[3] = 0x00; dbits[4] = 0x00;
				dbits[5] = 0xff; dbits[6] = 0xff;
				lpad = 6;
				break;
			case 0: /* xxxxxxxx */
			default:
				WRONG("compiler must be broken");
			}
			if (ved_bytes(req, preq, VDP_NULL, dbits + 1, lpad))
				break;
		}
		if (sl > 0) {
			/* Recover GZIP tail */
			dl = olen - ll;
			assert(dl >= 0);
			if (dl > sl)
				dl = sl;
			if (dl > 0) {
				assert(dl <= 8);
				l = ll - (olen - 8);
				assert(l >= 0);
				assert(l <= 8);
				assert(l + dl <= 8);
				memcpy(tailbuf + l, pp, dl);
				ll += dl;
				sl -= dl;
				pp += dl;
			}
		}
	} while (ois == OIS_DATA || ois == OIS_STREAM);
	ObjIterEnd(req->objcore, &oi);
	(void)ved_bytes(req, preq, VDP_FLUSH, NULL, 0);

	icrc = vle32dec(tailbuf);
	ilen = vle32dec(tailbuf + 4);

	ecx->crc = crc32_combine(ecx->crc, icrc, ilen);
	ecx->l_crc += ilen;
}
static void
ved_stripgzip(struct req *req, const struct boc *boc)
{
	ssize_t l;
	char *p;
	uint32_t icrc;
	uint32_t ilen;
	uint8_t *dbits;
	struct ecx *ecx;
	struct ved_foo foo;

	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
	CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
	CAST_OBJ_NOTNULL(ecx, req->transport_priv, ECX_MAGIC);

	INIT_OBJ(&foo, VED_FOO_MAGIC);
	foo.req = req;
	foo.preq = ecx->preq;
	memset(foo.tailbuf, 0xdd, sizeof foo.tailbuf);

	/* OA_GZIPBITS is not valid until BOS_FINISHED */
	if (boc != NULL)
		ObjWaitState(req->objcore, BOS_FINISHED);

	AN(ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED));

	/*
	 * This is the interesting case: Deliver all the deflate
	 * blocks, stripping the "LAST" bit of the last one and
	 * padding it, as necessary, to a byte boundary.
	 */

	p = ObjGetAttr(req->wrk, req->objcore, OA_GZIPBITS, &l);
	AN(p);
	assert(l == 32);
	foo.start = vbe64dec(p);
	foo.last = vbe64dec(p + 8);
	foo.stop = vbe64dec(p + 16);
	foo.olen = ObjGetLen(req->wrk, req->objcore);
	assert(foo.start > 0 && foo.start < foo.olen * 8);
	assert(foo.last > 0 && foo.last < foo.olen * 8);
	assert(foo.stop > 0 && foo.stop < foo.olen * 8);
	assert(foo.last >= foo.start);
	assert(foo.last < foo.stop);

	/* The start bit must be byte aligned. */
	AZ(foo.start & 7);

	dbits = WS_Alloc(req->ws, 8);
	AN(dbits);
	foo.dbits = dbits;
	(void)ObjIterate(req->wrk, req->objcore, &foo, ved_objiterate);
	/* XXX: error check ?? */
	(void)ved_bytes(req, foo.preq, VDP_FLUSH, NULL, 0);

	icrc = vle32dec(foo.tailbuf);
	ilen = vle32dec(foo.tailbuf + 4);

	ecx->crc = crc32_combine(ecx->crc, icrc, ilen);
	ecx->l_crc += ilen;
}
static int
ved_objiterate(void *priv, int flush, const void *ptr, ssize_t len)
{
	struct ved_foo *foo;
	const uint8_t *pp;
	ssize_t dl;
	ssize_t l;

	CAST_OBJ_NOTNULL(foo, priv, VED_FOO_MAGIC);
	(void)flush;
	pp = ptr;
	if (len > 0) {
		/* Skip over the GZIP header */
		dl = foo->start / 8 - foo->ll;
		if (dl > 0) {
			/* Before foo.start, skip */
			if (dl > len)
				dl = len;
			foo->ll += dl;
			len -= dl;
			pp += dl;
		}
	}
	if (len > 0) {
		/* The main body of the object */
		dl = foo->last / 8 - foo->ll;
		if (dl > 0) {
			if (dl > len)
				dl = len;
			if (ved_bytes(foo->req, foo->preq, VDP_NULL, pp, dl))
				return(-1);
			foo->ll += dl;
			len -= dl;
			pp += dl;
		}
	}
	if (len > 0 && foo->ll == foo->last / 8) {
		/* Remove the "LAST" bit */
		foo->dbits[0] = *pp;
		foo->dbits[0] &= ~(1U << (foo->last & 7));
		if (ved_bytes(foo->req, foo->preq, VDP_NULL, foo->dbits, 1))
			return (-1);
		foo->ll++;
		len--;
		pp++;
	}
	if (len > 0) {
		/* Last block */
		dl = foo->stop / 8 - foo->ll;
		if (dl > 0) {
			if (dl > len)
				dl = len;
			if (ved_bytes(foo->req, foo->preq, VDP_NULL, pp, dl))
				return (-1);
			foo->ll += dl;
			len -= dl;
			pp += dl;
		}
	}
	if (len > 0 && (foo->stop & 7) && foo->ll == foo->stop / 8) {
		/* Add alignment to byte boundary */
		foo->dbits[1] = *pp;
		foo->ll++;
		len--;
		pp++;
		switch((int)(foo->stop & 7)) {
		case 1: /*
			 * x000....
			 * 00000000 00000000 11111111 11111111
			 */
		case 3: /*
			 * xxx000..
			 * 00000000 00000000 11111111 11111111
			 */
		case 5: /*
			 * xxxxx000
			 * 00000000 00000000 11111111 11111111
			 */
			foo->dbits[2] = 0x00; foo->dbits[3] = 0x00;
			foo->dbits[4] = 0xff; foo->dbits[5] = 0xff;
			foo->lpad = 5;
			break;
		case 2: /* xx010000 00000100 00000001 00000000 */
			foo->dbits[1] |= 0x08;
			foo->dbits[2] = 0x20;
			foo->dbits[3] = 0x80;
			foo->dbits[4] = 0x00;
			foo->lpad = 4;
			break;
		case 4: /* xxxx0100 00000001 00000000 */
			foo->dbits[1] |= 0x20;
			foo->dbits[2] = 0x80;
			foo->dbits[3] = 0x00;
			foo->lpad = 3;
			break;
		case 6: /* xxxxxx01 00000000 */
			foo->dbits[1] |= 0x80;
			foo->dbits[2] = 0x00;
			foo->lpad = 2;
			break;
		case 7:	/*
			 * xxxxxxx0
			 * 00......
			 * 00000000 00000000 11111111 11111111
			 */
			foo->dbits[2] = 0x00;
			foo->dbits[3] = 0x00; foo->dbits[4] = 0x00;
			foo->dbits[5] = 0xff; foo->dbits[6] = 0xff;
			foo->lpad = 6;
			break;
		case 0: /* xxxxxxxx */
		default:
			WRONG("compiler must be broken");
		}
		if (ved_bytes(foo->req, foo->preq,
		    VDP_NULL, foo->dbits + 1, foo->lpad))
			return (-1);
	}
	if (len > 0) {
		/* Recover GZIP tail */
		dl = foo->olen - foo->ll;
		assert(dl >= 0);
		if (dl > len)
			dl = len;
		if (dl > 0) {
			assert(dl <= 8);
			l = foo->ll - (foo->olen - 8);
			assert(l >= 0);
			assert(l <= 8);
			assert(l + dl <= 8);
			memcpy(foo->tailbuf + l, pp, dl);
			foo->ll += dl;
			len -= dl;
		}
	}
	assert(len == 0);
	return (0);
}