From patchwork Sat Sep 14 00:35:19 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [07, of, 55, RFC, c-hglib:level1] model 1: additional functions, yield like function hg_fetch_entry From: Iulian Stana X-Patchwork-Id: 2451 Message-Id: <1af66df0c7bf87a3c9df.1379118919@doppler> To: mercurial-devel@selenic.com Date: Sat, 14 Sep 2013 03:35:19 +0300 # HG changeset patch # User Iulian Stana # Date 1379110033 -10800 # Sat Sep 14 01:07:13 2013 +0300 # Node ID 1af66df0c7bf87a3c9df7f8d2599320cc7939804 # Parent 33c4d1b5c5f037e4163f681e7b1ee5141fae1842 model 1: additional functions, yield like function hg_fetch_entry This mechanism could be called model (1): (1) Return immedietely after having sent the command to commandserv, just wrapping a call to hg_rawcommand(). Other API functions are provided to retrieve: (a) the data sent in response by the commandserv, in parsed (structured) form (b) the exitcode, i.e. the content of the 'r' channel after all things have happened. I created a yield like mechanism to deal with this model. hg_fetch_entry is a modularizated function created to get lines or csets from cmdserver. hg_fetch_cset_entry: the yield mechanism that will get the next changeset hg_fetch_line_entry: the yield mechanism that will get the next line Also this commit presents the hg_stream_buffer structure used in those yield mechanism, and the additional functions for this mechanism: detect_null_byte: detect the first null byte detect_endline_byte: detect the first endline byte erase_entry: erase the some data from a pointer diff --git a/client.c b/client.c --- a/client.c +++ b/client.c @@ -400,3 +400,84 @@ return exitcode; } + +/* The yield next step. Getting the next entry. */ +int hg_fetch_entry(hg_stream_buffer *stream, int (*detect_byte)(char *buff, + int buf_size, int data_on_pipe), int func_type) +{ + hg_header *head; + int exitcode, bc; + char read_data[BUFF_SIZE]; + + /* Erase the first entry from stream pointer. + * This entry was already pass to user.*/ + if(stream->buf_size){ + stream->buf_size = erase_entry(&stream->buffer, stream->buf_size, + stream->first_entry_size); + } + + while(!detect_byte(stream->buffer + func_type, stream->buf_size, 1)) + { + head = hg_read_header(stream->handle); + if(head->channel == r) + break; + /* trash output data, handle error data, return -1 + **/ + else if(head->channel == e){ + trash_data(stream->handle, stream->callback); + free(stream->command); + free(stream->buffer); + free(stream); + return -1; + } + + if(bc = hg_rawread(stream->handle, read_data, BUFF_SIZE), bc < 0) + return -1; + append_data(&stream->buffer, read_data, stream->buf_size, bc); + stream->buf_size += bc; + + } + if(func_type == 1){ + if(stream->buffer[0] != '\0'){ + bc = detect_null_byte(stream->buffer, stream->buf_size, 0); + stream->buf_size = erase_entry(&stream->buffer, + stream->buf_size, bc); + } + } + if(bc = detect_byte(stream->buffer + func_type, stream->buf_size, 0), + bc > 0){ + stream->first_entry_size = bc; + return 1; + } + + exitcode = hg_exitcode(stream->handle); + free(stream->command); + free(stream->buffer); + free(stream); + if(exitcode) + return -1; + return 0; +} + +/* The cbuf next step. Getting the next changeset. */ +int hg_fetch_cset_entry(hg_csetstream_buffer *cbuf, hg_cset_entry *centry) +{ + int exitcode = hg_fetch_entry(cbuf, &detect_null_byte, 1); + if (exitcode == 1){ + cbuf->first_entry_size ++; + /* Parse first cset from data. */ + parse_changeset(cbuf->buffer + 1, centry); + } + return exitcode; +} + +/* The lbuf next step. Getting the next changeset. */ +int hg_fetch_line_entry(hg_linestream_buffer *lbuf, char **lentry) +{ + int exitcode = hg_fetch_entry(lbuf, &detect_endline_byte, 0); + if (exitcode == 1){ + lbuf->buffer[lbuf->first_entry_size - 1] = '\0'; + *lentry = lbuf->buffer; + } + return exitcode; +} diff --git a/client.h b/client.h --- a/client.h +++ b/client.h @@ -89,6 +89,58 @@ } hg_handle; /** + * \struct hg_csetstream_buffer + * \brief This structure will be use to create the yield-like mechanism. In this + * structure all the necessary informations about the current command will be + * maintained + * + * \var hg_csetstream_buffer::handle + * The handle of the connection, wherewith I want to communicate + * \var hg_csetstream_buffer::callback + * A function that will handle error data. + * \var hg_csetstream_buffer::command + * The command sended to cmdserver. + * \var hg_csetstream_buffer::buffer + * Place where the data will be maintained until is passed to user and used. + * \var hg_csetstream_buffer::buf_size + * The size for the buffer. + * \var hg_csetstream_buffer::first_cset_size + * The size for the first cset, maintained in buffer. + * + * \typedef hg_csetstream_buffer + * \brief This structure will be use to create the yield-like mechanism. In this + * structure all the necessary informations about the current command will be + * maintained + * + * \param handle + * The handle of the connection, wherewith I want to communicate + * \param callback + * A function that will handle error data. + * \param command + * The command sended to cmdserver. + * \param buffer + * Place where the data will be maintained until is passed to user and used. + * \param buf_size + * The size for the buffer. + * \param first_cset_size + * The size for the first cset, maintained in buffer. + * */ +typedef struct hg_stream_buffer{ + hg_handle *handle; + int (*callback)(const char *msg, size_t len); + char **command; + char *buffer; + int buf_size; + int first_entry_size; +}hg_stream_buffer; + +/** + * Same as hg_stream_buffer structure. + **/ +typedef struct hg_stream_buffer hg_linestream_buffer; +typedef struct hg_stream_buffer hg_csetstream_buffer; + +/** * \brief Reading the header from cmdsrv. * * The function will read the header from the command server and will save it to @@ -295,4 +347,77 @@ int hg_addremove(hg_handle *handle, int(*callback)(const char *msg, size_t len), char *argument[]); +/** + * \brief The yield mechanism that will get the next entry. + * + * This function is used inside of hg_fetch_cset_entry() and hg_fetch_line_entry() + * function. This function represent the modularized function of those two. + * + * \param hg_stream_buffer The stream structure to store entry data. + * \param detect_byte The function to detect the size of the next entry. + * \param func_type The type for function that will use this function: + * - 0 for hg_fetch_cset_entry() used with detect_null_byte() function + * - 1 for hg_fetch_line_entry() used with detect_endline_byte() function + * \retval 1 Succesful operation, pass the first find cset to centry structure + * \retval 0 To indicate the end of log_command, everything works well. + * \retval -1 to indicate an error, with errno set appropriately. + * + * errno can be: + * - EINVAL - Invalid argument (handle it's set to a null pointer) + * - read(2) command errors + * - read_header error + * */ +int hg_fetch_entry(hg_stream_buffer *stream, int (*detect_byte)(char *buff, + int buf_size, int data_on_pipe), int func_type); + +/** + * \brief The yield mechanism that will get the next changeset. + * + * The revision history could have a huge mass of data. You cannot pass the + * entire history in one call, so we use an iterator-like mechanism. Calling + * the hg_fetch_log_entry. The next changeset will be read from cmd-server, + * parse and pass to hg_cset_entry structure. + * The cset_entry structure will handle a changeset with the following string + * fields: + * - rev + * - node + * - tags (space delimited) + * - branch + * - author + * - desc + * + * \param hg_csetstream_buffer The buffer structure to store cset data. + * \param centry The hg_cset_entry structure where the changeset will be stored + * and pass + * \retval 1 Succesful operation, pass the first find cset to centry structure + * \retval 0 To indicate the end of log_command, everything works well. + * \retval -1 to indicate an error, with errno set appropriately. + * + * errno can be: + * - EINVAL - Invalid argument (handle it's set to a null pointer) + * - read(2) command errors + * - read_header error + * */ +int hg_fetch_cset_entry(hg_csetstream_buffer *cbuf, hg_cset_entry *centry); + +/** + * \brief The yield mechanism that will get the next line. + * + * Some commands could perform huge amount of data, to pass this data to users + * in a parse way mode I provide this function. This function will return a line + * in a single call. + * + * \param hg_linestream_buffer The buffer structure to store line data. + * \param string Address where a line will be placed. + * \retval 1 Succesful operation, pass the first find line to lentry pointer. + * \retval 0 To indicate the end of command, everything works well. + * \retval -1 to indicate an error, with errno set appropriately. + * + * errno can be: + * - EINVAL - Invalid argument (handle it's set to a null pointer) + * - read(2) command errors + * - read_header error + * */ +int hg_fetch_line_entry(hg_linestream_buffer *lbuf, char **lentry); + #endif diff --git a/utils.c b/utils.c --- a/utils.c +++ b/utils.c @@ -82,3 +82,55 @@ memcpy(*dest + dsize, source, ssize + 1); return 0; } + +/* + * Erase the top entry from buff pointer. + * */ +int erase_entry(char **buff, int buf_size, int first_entry_size) +{ + int new_buff_size = buf_size - first_entry_size; + char *new_buff = malloc(new_buff_size + 1); + memcpy(new_buff, *buff + first_entry_size, new_buff_size + 1); + free(*buff); + *buff = new_buff; + return new_buff_size; +} + +/* + * Detect the first null byte. + * */ +int detect_null_byte(char *buffer, int buf_size, int data_on_pipe) +{ + if(buffer == NULL) + return 0; + char *char_ptr; + + for(char_ptr = buffer; char_ptr < buffer + buf_size; ++char_ptr) + if( *char_ptr == '\0') + break; + + if((char_ptr - buffer + 1 < buf_size && data_on_pipe) || + (char_ptr - buffer + 1 <= buf_size && !data_on_pipe)) + return char_ptr - buffer; + + return 0; +} + +/* + * Detect the first endline byte. + * */ +int detect_endline_byte(char *buffer, int buf_size, int data_on_pipe) +{ + if(buffer == NULL) + return 0; + char *char_ptr; + + for(char_ptr = buffer; char_ptr < buffer + buf_size; ++char_ptr) + if( *char_ptr == '\n') + break; + if((char_ptr - buffer + 1 < buf_size && data_on_pipe) || + (char_ptr - buffer + 1 <= buf_size && !data_on_pipe)) + return char_ptr - buffer + 1; + + return 0; +} diff --git a/utils.h b/utils.h --- a/utils.h +++ b/utils.h @@ -51,4 +51,60 @@ * */ int append_data(char **dest, char *source, int dsize, int ssize); +/** + * \brief Erase the some data from a pointer. + * + * After an entry is send to the user I don't need any more that + * data, so I create space for the incoming data. + * + * \param buff The pointer that I wished to resize. + * \param buf_size The size of the pointer + * \param first_entry_size The size that I want to erase. + * \retval integer The new size of the pointer. + * */ +int erase_entry(char **buff, int buf_size, int first_entry_size); + +/** + * \brief Detect the first null byte. + * + * Sometimes I will store data that will contain more null bytes, + * and I would like to know the length to the first null byte. + * + * Also this function will tell me if the buffer contain more null + * bytes. (if data_on_pipe is set to 1 I want to know the length to + * the first entry, also if data_on_pipe is set to 0 I want to know + * the length to the first entry or length to the end). + * + * \param buffer The pointer where I want to make de evaluation. + * \param buf_size The size of the pointer + * \param data_on_pipe + * - Is 1 if I want to know the lenght to the first entry. + * - Is 0 if I want to know the lenght to the first entry or the + * length to the end. + * \retval length The length of the first entry. + * */ +int detect_null_byte(char *buffer, int buf_size, int data_on_pipe); + +/** + * \brief Detect the first endline byte. + * + * Sometimes I will store data that will contain more endline bytes, + * and I would like to know the length to the first endline byte. + * + * Also this function will tell me if the buffer contain more endline + * bytes. (if data_on_pipe is set to 1 I want to know the length to + * the first entry, also if data_on_pipe is set to 0 I want to know + * the length to the first entry or length to the end). + * + * \param buffer The pointer where I want to make de evaluation. + * \param buf_size The size of the pointer + * \param data_on_pipe + * - Is 1 if I want to know the lenght to the first entry. + * - Is 0 if I want to know the lenght to the first entry or the + * length to the end. + * \retval length The length of the first entry. + * */ +int detect_endline_byte(char *buffer, int buf_size, int data_on_pipe); + + #endif