Patchwork [07,of,55,RFC,c-hglib:level1] model 1: additional functions, yield like function hg_fetch_entry

login
register
mail settings
Submitter Iulian Stana
Date Sept. 14, 2013, 12:35 a.m.
Message ID <1af66df0c7bf87a3c9df.1379118919@doppler>
Download mbox | patch
Permalink /patch/2451/
State Deferred, archived
Headers show

Comments

Iulian Stana - Sept. 14, 2013, 12:35 a.m.
# HG changeset patch
# User Iulian Stana <julian.stana@gmail.com>
# 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

Patch

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