[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [Libevent-users] Using evbuffer_peek



On Thu, Nov 11, 2010 at 10:42 PM, Kevin Bowling
<kevin.bowling@xxxxxxxxxx> wrote:
> Hi,
>
> I've seen a few mails talking about evbuffer_peek, but no solid usage of
> it.  The doxygen on the libevent page doesn't cover it generating my own
> from 2.0.8 has shed little light on its use to me.

There's an example at
http://www.wangafu.net/~nickm/libevent-book/Ref7_evbuffer.html that
might give more detail.

> Basically, for my test case I have two bytes in network order that make up a
> short.  These are one byte into the buffer.
>
> uint16_t ulen;
> struct evbuffer_ptr bufpos;
> struct evbuffer_iovec bufvec[2];
>
> if (evbuffer_ptr_set(input, &bufpos, 1, EVBUFFER_PTR_SET) < 0)
>   return(-1); // Add proper error handling
> if (evbuffer_peek(input, 2, &bufpos, bufvec, 1) < 0)
>   return(-1); // Add proper error handling
>
> ulen = ntohs((uint16_t)bufvec[0].iov_base);
>
> I've tried using many variations with the pointers.  I can't even read out
> each byte individually.  What am I doing wrong?

bufvec[0].iov_base is a pointer to char *; if you cast that to a
uint16_t, you'll get junk.  When I change the line to
    ulen = ntohs(*(uint16_t*)bufvec[0].iov_base);
it works fine for me.

But really, that's not quite good enough, for two reasons.  First,
nothing in the C standard says that unaligned accesses are okay, and
iov_base is a pointer into the evbuffer's internal memory, so you
can't count on it being 16-bit aligned.  Instead you might be better
off doing...
    memcpy(&ulen, bufvec[0].iov_base, 2);
    ulen = ntohs(ulen);

But that's not enough either, since it is possible that the result of
evbuffer_peek -- even if it's only two bytes long -- could be split
across multiple iovecs.  So instead you need to change the stuff
starting with the peek to something more like:
    int n;
    n = evbuffer_peek(input, 2, &bufpos, bufvec, 1);
    if (n<0) return -1; /* add error handling */
    if (n == 1) { /* It all fit in bufvec[0] */
        memcpy(&ulen, bufvec[0].iov_base, 2);
        ulen = ntohs(ulen);
     } else {
        assert(n == 2); /* two bytes can't be split across more than 2
chunks. */
        ulen = *(uint8_t*)bufvec[0].iov_base;
        ulen <<= 8;
        ulen |= *(uint8_t*)bufvec[1].iov_base;
     }

Ugly?  You bet!  evbuffer_peek is really only optimized for the case
where you do not want to take any performance hit for copying data out
of the evbuffer, and you're willing to make your code more complex
because of it.  Instead, I'd suggest evbuffer_copyout(), which is way
easier for what you seem to be doing:

   uint8_t tmp[3];
   if (evbuffer_copyout(input, tmp, 3) < 3)
     return -1;
   ulen = (tmp[1]<<8) | tmp[2];

hth,
-- 
Nick
***********************************************************************
To unsubscribe, send an e-mail to majordomo@xxxxxxxxxxxxx with
unsubscribe libevent-users    in the body.