From: Wilfried Goesgens Date: Tue, 21 Dec 2010 23:37:55 +0000 (+0100) Subject: libevent integration X-Git-Tag: v8.01~500 X-Git-Url: https://code.citadel.org/?p=citadel.git;a=commitdiff_plain;h=ceec483b7aac5ec74a718a1080de75e10860d298 libevent integration - Add functions that are able to do just one read()/write() so they can be used as libevent callbacks. - Add linereader function that reads from "incomplete buffers" and falls back to a re-attemt if not enough data is available. --- diff --git a/libcitadel/lib/libcitadel.h b/libcitadel/lib/libcitadel.h index 989945a54..f0c8ab735 100644 --- a/libcitadel/lib/libcitadel.h +++ b/libcitadel/lib/libcitadel.h @@ -238,7 +238,31 @@ int StrBufTCP_read_buffered_line_fast(StrBuf *Line, int selectresolution, const char **Error); + int StrBufSipLine(StrBuf *LineBuf, StrBuf *Buf, const char **Ptr); + +typedef enum _eReadState { + eReadFail, + eReadSuccess, + eMustReadMore, + eBufferNotEmpty +} eReadState; + +typedef struct _file_buffer { + StrBuf *Buf; + const char *ReadWritePointer; + int fd; + int LineCompleted; + int nBlobBytesWanted; +} IOBuffer; + +long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB); +int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB); + +eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB); +eReadState StrBufCheckBuffer(IOBuffer *FB); + + int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong, const char *Repl, long ReplLen); int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator); int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars); diff --git a/libcitadel/lib/stringbuf.c b/libcitadel/lib/stringbuf.c index 88cea7d70..6d8f549d3 100644 --- a/libcitadel/lib/stringbuf.c +++ b/libcitadel/lib/stringbuf.c @@ -3392,7 +3392,191 @@ int CompressBuffer(StrBuf *Buf) return 0; } +/******************************************************************************* + * File I/O; Callbacks to libevent * + *******************************************************************************/ + +long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB) +{ + long bufremain = 0; + int n; + + /* + * check whether the read pointer is somewhere in a range + * where a cut left is inexpensive + */ + if (FB->ReadWritePointer != NULL) + { + long already_read = FB->ReadWritePointer - FB->Buf->buf; + bufremain = FB->Buf->BufSize - FB->Buf->BufUsed; + + if (already_read != 0) { + long unread = FB->Buf->BufUsed - already_read; + /* else nothing to compact... */ + if (unread == 0) { + FB->ReadWritePointer = FB->Buf->buf; + bufremain = FB->Buf->BufSize; + } + else if ((unread < 64) || + (bufremain < already_read)) + { + /* + * if its just a tiny bit remaining, or we run out of space... + * lets tidy up. + */ + FB->Buf->BufUsed = unread; + if (unread < already_read) + memcpy(FB->Buf->buf, FB->ReadWritePointer, unread); + else + memmove(FB->Buf->buf, FB->ReadWritePointer, unread); + FB->ReadWritePointer = FB->Buf->buf; + bufremain = FB->Buf->BufSize - unread; + } + else if (bufremain < (FB->Buf->BufSize / 10)) { + /* get a bigger buffer */ ///TODO: special increase function that won't copy the already read! + IncreaseBuf(FB->Buf, 0, -1); + FB->ReadWritePointer = FB->Buf->buf + unread; + bufremain = FB->Buf->BufSize - unread; + } + } + + } + else { + FB->ReadWritePointer = FB->Buf->buf; + bufremain = FB->Buf->BufSize; + } + n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain - 1); + + if (n > 0) { + FB->Buf->BufUsed += n; + FB->Buf->buf[FB->Buf->BufUsed] = '\0'; + } + return n; +} + +int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB) +{ + long WriteRemain; + int n; + + if (FB->ReadWritePointer != NULL) + { + WriteRemain = FB->Buf->BufUsed - + (FB->ReadWritePointer - + FB->Buf->buf); + } + else { + FB->ReadWritePointer = FB->Buf->buf; + WriteRemain = FB->Buf->BufUsed; + } + + n = write(fd, FB->ReadWritePointer, WriteRemain); + if (n > 0) { + FB->ReadWritePointer += n; + + if (FB->ReadWritePointer == + FB->Buf->buf + FB->Buf->BufUsed) + { + FlushStrBuf(FB->Buf); + FB->ReadWritePointer = NULL; + return 0; + } + // check whether we've got something to write + // get the maximum chunk plus the pointer we can send + // write whats there + // if not all was sent, remember the send pointer for the next time + return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed; + } + return n; +} + +/** + * @ingroup StrBuf_IO + * @brief extract a "next line" from Buf; Ptr to persist across several iterations + * @param LineBuf your line will be copied here. + * @param FB BLOB with lines of text... + * @param Ptr moved arround to keep the next-line across several iterations + * has to be &NULL on start; will be &NotNULL on end of buffer + * @returns size of copied buffer + */ +eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB) +{ + const char *aptr, *ptr, *eptr; + char *optr, *xptr; + + if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) { + FB->ReadWritePointer = StrBufNOTNULL; + return eReadFail; + } + + FlushStrBuf(LineBuf); + if (FB->ReadWritePointer == NULL) + ptr = aptr = FB->Buf->buf; + else + ptr = aptr = FB->ReadWritePointer; + + optr = LineBuf->buf; + eptr = FB->Buf->buf + FB->Buf->BufUsed; + xptr = LineBuf->buf + LineBuf->BufSize - 1; + + while ((ptr <= eptr) && + (*ptr != '\n') && + (*ptr != '\r') ) + { + *optr = *ptr; + optr++; ptr++; + if (optr == xptr) { + LineBuf->BufUsed = optr - LineBuf->buf; + IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1); + optr = LineBuf->buf + LineBuf->BufUsed; + xptr = LineBuf->buf + LineBuf->BufSize - 1; + } + } + if (ptr >= eptr) { + if (optr > LineBuf->buf) + optr --; + if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) { + LineBuf->BufUsed = optr - LineBuf->buf; + *optr = '\0'; + return eMustReadMore; + } + } + LineBuf->BufUsed = optr - LineBuf->buf; + *optr = '\0'; + if ((ptr <= eptr) && (*ptr == '\r')) + ptr ++; + if ((ptr <= eptr) && (*ptr == '\n')) + ptr ++; + + if (ptr < eptr) { + FB->ReadWritePointer = ptr; + } + else { + FlushStrBuf(FB->Buf); + FB->ReadWritePointer = NULL; + } + + return eReadSuccess; +} + +/** + * @ingroup StrBuf_CHUNKED_IO + * @brief check whether the chunk-buffer has more data waiting or not. + * @param FB Chunk-Buffer to inspect + */ +eReadState StrBufCheckBuffer(IOBuffer *FB) +{ + if (FB == NULL) + return eReadFail; + if (FB->Buf->BufUsed == 0) + return eReadSuccess; + if (FB->ReadWritePointer == NULL) + return eBufferNotEmpty; + if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer) + return eBufferNotEmpty; + return eReadSuccess; +} /******************************************************************************* * File I/O; Prefer buffered read since its faster! *