Browse Source

Damn it; my lovely resilient scheme falls down when you have a protocol

that switches from line-oriented to byte-oriented partway through, such as
HTTP.
tags/v0.9.3
Michael Brown 18 years ago
parent
commit
83b7933f8a
3 changed files with 61 additions and 85 deletions
  1. 42
    62
      src/core/linebuf.c
  2. 7
    16
      src/include/gpxe/linebuf.h
  3. 12
    7
      src/tests/linebuf_test.c

+ 42
- 62
src/core/linebuf.c View File

30
 #include <gpxe/linebuf.h>
30
 #include <gpxe/linebuf.h>
31
 
31
 
32
 /**
32
 /**
33
- * Line terminators
33
+ * Retrieve buffered-up line
34
  *
34
  *
35
- * These values are used in the @c skip_terminators bitmask.
36
- */
37
-enum line_terminators {
38
-	TERM_CR = 1,
39
-	TERM_NL = 2,
40
-	TERM_NUL = 4,
41
-};
42
-
43
-/**
44
- * Get terminator ID corresponding to character
45
- *
46
- * @v character		Character
47
- * @ret terminator_id	Terminator ID, or -1
35
+ * @v linebuf		Line buffer
36
+ * @ret line		Buffered line, or NULL if no line ready to read
48
  */
37
  */
49
-static int terminator_id ( unsigned int character ) {
50
-	switch ( character ) {
51
-	case '\r':
52
-		return TERM_CR;
53
-	case '\n':
54
-		return TERM_NL;
55
-	case '\0':
56
-		return TERM_NUL;
57
-	default:
58
-		return -1;
59
-	}
38
+char * buffered_line ( struct line_buffer *linebuf ) {
39
+	return ( linebuf->ready ? linebuf->data : NULL );
60
 }
40
 }
61
 
41
 
62
 /**
42
 /**
68
 	free ( linebuf->data );
48
 	free ( linebuf->data );
69
 	linebuf->data = NULL;
49
 	linebuf->data = NULL;
70
 	linebuf->len = 0;
50
 	linebuf->len = 0;
51
+	linebuf->ready = 0;
71
 }
52
 }
72
 
53
 
73
 /**
54
 /**
76
  * @v linebuf			Line buffer
57
  * @v linebuf			Line buffer
77
  * @v data			New data to add
58
  * @v data			New data to add
78
  * @v len			Length of new data to add
59
  * @v len			Length of new data to add
79
- * @ret buffered		Amount of data consumed and added to the buffer
80
- * @ret <0			Out of memory
60
+ * @ret rc			Return status code
81
  * 
61
  * 
82
- * If line_buffer() does not consume the entirety of the new data
83
- * (i.e. if @c buffered is not equal to @c len), then an end of line
84
- * has been reached and the buffered-up line can be obtained from
85
- * buffered_line().  Carriage returns and newlines will have been
86
- * stripped, and the line will be NUL-terminated.  This buffered line
87
- * is valid only until the next call to line_buffer() (or to
88
- * empty_line_buffer()).
62
+ * If line_buffer() returns >0, then an end of line has been reached
63
+ * and the buffered-up line can be obtained from buffered_line().
64
+ * Carriage returns and newlines will have been stripped, and the line
65
+ * will be NUL-terminated.  This buffered line is valid only until the
66
+ * next call to line_buffer() (or to empty_line_buffer()).
67
+ *
68
+ * @c data and @c len will be updated to reflect the data consumed by
69
+ * line_buffer().
89
  *
70
  *
90
  * Note that line buffers use dynamically allocated storage; you
71
  * Note that line buffers use dynamically allocated storage; you
91
  * should call empty_line_buffer() before freeing a @c struct @c
72
  * should call empty_line_buffer() before freeing a @c struct @c
92
  * line_buffer.
73
  * line_buffer.
93
  */
74
  */
94
-int line_buffer ( struct line_buffer *linebuf, const char *data, size_t len ) {
95
-	size_t consume = 0;
96
-	size_t copy = 0;
75
+int line_buffer ( struct line_buffer *linebuf,
76
+		  const char **data, size_t *len ) {
77
+	const char *eol;
78
+	size_t consume;
97
 	size_t new_len;
79
 	size_t new_len;
98
 	char *new_data;
80
 	char *new_data;
99
-	int terminator;
100
 
81
 
101
-	/* First, handle the termination of the previous line */
102
-	if ( linebuf->skip_terminators ) {
103
-		/* Free buffered string */
82
+	/* Free any completed line from previous iteration */
83
+	if ( linebuf->ready )
104
 		empty_line_buffer ( linebuf );
84
 		empty_line_buffer ( linebuf );
105
-		/* Skip over any terminators from the end of a previous line */
106
-		for ( ; consume < len ; consume++ ) {
107
-			terminator = terminator_id ( data[consume] );
108
-			if ( ( terminator < 0 ) ||
109
-			     ! ( linebuf->skip_terminators & terminator ) ) {
110
-				linebuf->skip_terminators = 0;
111
-				break;
112
-			}
113
-			linebuf->skip_terminators &= ~terminator;
114
-		}
115
-	}
116
 
85
 
117
-	/* Scan up to the next terminator, if any */
118
-	for ( ; consume < len ; consume++, copy++ ) {
119
-		if ( terminator_id ( data[consume] ) >= 0 ) {
120
-			linebuf->skip_terminators = -1U;
121
-			break;
122
-		}
86
+	/* Search for line terminator */
87
+	if ( ( eol = memchr ( *data, '\n', *len ) ) ) {
88
+		consume = ( eol - *data + 1 );
89
+	} else {
90
+		consume = *len;
123
 	}
91
 	}
124
 
92
 
125
 	/* Reallocate data buffer and copy in new data */
93
 	/* Reallocate data buffer and copy in new data */
126
-	new_len = ( linebuf->len + copy );
94
+	new_len = ( linebuf->len + consume );
127
 	new_data = realloc ( linebuf->data, ( new_len + 1 ) );
95
 	new_data = realloc ( linebuf->data, ( new_len + 1 ) );
128
 	if ( ! new_data )
96
 	if ( ! new_data )
129
 		return -ENOMEM;
97
 		return -ENOMEM;
130
-	memcpy ( ( new_data + linebuf->len ), ( data + consume - copy ),
131
-		 copy );
98
+	memcpy ( ( new_data + linebuf->len ), *data, consume );
132
 	new_data[new_len] = '\0';
99
 	new_data[new_len] = '\0';
133
 	linebuf->data = new_data;
100
 	linebuf->data = new_data;
134
 	linebuf->len = new_len;
101
 	linebuf->len = new_len;
135
-	return consume;
102
+
103
+	/* Update data and len */
104
+	*data += consume;
105
+	*len -= consume;
106
+
107
+	/* If we have reached end of line, trim the line and mark as ready */
108
+	if ( eol ) {
109
+		linebuf->data[--linebuf->len] = '\0'; /* trim NL */
110
+		if ( linebuf->data[linebuf->len - 1] == '\r' )
111
+			linebuf->data[--linebuf->len] = '\0'; /* trim CR */
112
+		linebuf->ready = 1;
113
+	}
114
+
115
+	return 0;
136
 }
116
 }

+ 7
- 16
src/include/gpxe/linebuf.h View File

12
 
12
 
13
 /** A line buffer */
13
 /** A line buffer */
14
 struct line_buffer {
14
 struct line_buffer {
15
-	/** Current data in the buffer */
15
+	/** Current string in the buffer */
16
 	char *data;
16
 	char *data;
17
-	/** Length of current data */
17
+	/** Length of current string, excluding the terminating NUL */
18
 	size_t len;
18
 	size_t len;
19
-	/** Bitmask of terminating characters to skip over */
20
-	unsigned int skip_terminators;
19
+	/** String is ready to read */
20
+	int ready;
21
 };
21
 };
22
 
22
 
23
-/**
24
- * Retrieve buffered-up line
25
- *
26
- * @v linebuf		Line buffer
27
- * @ret line		Buffered line, or NULL if no line present
28
- */
29
-static inline char * buffered_line ( struct line_buffer *linebuf ) {
30
-	return linebuf->data;
31
-}
32
-
33
-extern int line_buffer ( struct line_buffer *linebuf, const char *data,
34
-			 size_t len );
23
+extern char * buffered_line ( struct line_buffer *linebuf );
24
+extern int line_buffer ( struct line_buffer *linebuf,
25
+		  const char **data, size_t *len );
35
 extern void empty_line_buffer ( struct line_buffer *linebuf );
26
 extern void empty_line_buffer ( struct line_buffer *linebuf );
36
 
27
 
37
 #endif /* _GPXE_LINEBUF_H */
28
 #endif /* _GPXE_LINEBUF_H */

+ 12
- 7
src/tests/linebuf_test.c View File

5
 
5
 
6
 static const char data1[] = 
6
 static const char data1[] = 
7
 "Hello world\r\n"
7
 "Hello world\r\n"
8
-"This is a particularly mean set of lines\n"
9
-"with a mixture of terminators\r\r\n"
8
+"This is a reasonably nice set of lines\n"
9
+"with not many different terminators\r\n\r\n"
10
 "There should be exactly one blank line above\n"
10
 "There should be exactly one blank line above\n"
11
 "and this line should never appear at all since it has no terminator";
11
 "and this line should never appear at all since it has no terminator";
12
 
12
 
14
 	struct line_buffer linebuf;
14
 	struct line_buffer linebuf;
15
 	const char *data = data1;
15
 	const char *data = data1;
16
 	size_t len = ( sizeof ( data1 ) - 1 /* be mean; strip the NUL */ );
16
 	size_t len = ( sizeof ( data1 ) - 1 /* be mean; strip the NUL */ );
17
-	size_t buffered;
17
+	char *line;
18
+	int rc;
18
 
19
 
19
 	memset ( &linebuf, 0, sizeof ( linebuf ) );
20
 	memset ( &linebuf, 0, sizeof ( linebuf ) );
20
-	while ( ( buffered = line_buffer ( &linebuf, data, len ) ) != len ) {
21
-		printf ( "\"%s\"\n", buffered_line ( &linebuf ) );
22
-		data += buffered;
23
-		len -= buffered;
21
+	while ( len ) {
22
+		if ( ( rc = line_buffer ( &linebuf, &data, &len ) ) != 0 ) {
23
+			printf ( "line_buffer() failed: %s\n",
24
+				 strerror ( rc ) );
25
+			return;
26
+		}
27
+		if ( ( line = buffered_line ( &linebuf ) ) )
28
+			printf ( "\"%s\"\n", line );
24
 	}
29
 	}
25
 
30
 
26
 	empty_line_buffer ( &linebuf );
31
 	empty_line_buffer ( &linebuf );

Loading…
Cancel
Save