Ver código fonte

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 anos atrás
pai
commit
83b7933f8a
3 arquivos alterados com 61 adições e 85 exclusões
  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 Ver arquivo

@@ -30,33 +30,13 @@
30 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,6 +48,7 @@ void empty_line_buffer ( struct line_buffer *linebuf ) {
68 48
 	free ( linebuf->data );
69 49
 	linebuf->data = NULL;
70 50
 	linebuf->len = 0;
51
+	linebuf->ready = 0;
71 52
 }
72 53
 
73 54
 /**
@@ -76,61 +57,60 @@ void empty_line_buffer ( struct line_buffer *linebuf ) {
76 57
  * @v linebuf			Line buffer
77 58
  * @v data			New data to add
78 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 71
  * Note that line buffers use dynamically allocated storage; you
91 72
  * should call empty_line_buffer() before freeing a @c struct @c
92 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 79
 	size_t new_len;
98 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 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 93
 	/* Reallocate data buffer and copy in new data */
126
-	new_len = ( linebuf->len + copy );
94
+	new_len = ( linebuf->len + consume );
127 95
 	new_data = realloc ( linebuf->data, ( new_len + 1 ) );
128 96
 	if ( ! new_data )
129 97
 		return -ENOMEM;
130
-	memcpy ( ( new_data + linebuf->len ), ( data + consume - copy ),
131
-		 copy );
98
+	memcpy ( ( new_data + linebuf->len ), *data, consume );
132 99
 	new_data[new_len] = '\0';
133 100
 	linebuf->data = new_data;
134 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 Ver arquivo

@@ -12,26 +12,17 @@
12 12
 
13 13
 /** A line buffer */
14 14
 struct line_buffer {
15
-	/** Current data in the buffer */
15
+	/** Current string in the buffer */
16 16
 	char *data;
17
-	/** Length of current data */
17
+	/** Length of current string, excluding the terminating NUL */
18 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 26
 extern void empty_line_buffer ( struct line_buffer *linebuf );
36 27
 
37 28
 #endif /* _GPXE_LINEBUF_H */

+ 12
- 7
src/tests/linebuf_test.c Ver arquivo

@@ -5,8 +5,8 @@
5 5
 
6 6
 static const char data1[] = 
7 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 10
 "There should be exactly one blank line above\n"
11 11
 "and this line should never appear at all since it has no terminator";
12 12
 
@@ -14,13 +14,18 @@ void linebuf_test ( void ) {
14 14
 	struct line_buffer linebuf;
15 15
 	const char *data = data1;
16 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 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 31
 	empty_line_buffer ( &linebuf );

Carregando…
Cancelar
Salvar