Explorar el Código

Added generic line-buffering code (a la stdio)

tags/v0.9.3
Michael Brown hace 18 años
padre
commit
996b091b50
Se han modificado 3 ficheros con 200 adiciones y 0 borrados
  1. 136
    0
      src/core/linebuf.c
  2. 37
    0
      src/include/gpxe/linebuf.h
  3. 27
    0
      src/tests/linebuf_test.c

+ 136
- 0
src/core/linebuf.c Ver fichero

@@ -0,0 +1,136 @@
1
+/*
2
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+/**
20
+ * @file
21
+ *
22
+ * Line buffering
23
+ *
24
+ */
25
+
26
+#include <stdint.h>
27
+#include <string.h>
28
+#include <stdlib.h>
29
+#include <errno.h>
30
+#include <gpxe/linebuf.h>
31
+
32
+/**
33
+ * Line terminators
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
48
+ */
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
+	}
60
+}
61
+
62
+/**
63
+ * Discard line buffer contents
64
+ *
65
+ * @v linebuf		Line buffer
66
+ */
67
+void empty_line_buffer ( struct line_buffer *linebuf ) {
68
+	free ( linebuf->data );
69
+	linebuf->data = NULL;
70
+	linebuf->len = 0;
71
+}
72
+
73
+/**
74
+ * Buffer up received data by lines
75
+ *
76
+ * @v linebuf			Line buffer
77
+ * @v data			New data to add
78
+ * @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
81
+ * 
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()).
89
+ *
90
+ * Note that line buffers use dynamically allocated storage; you
91
+ * should call empty_line_buffer() before freeing a @c struct @c
92
+ * line_buffer.
93
+ */
94
+int line_buffer ( struct line_buffer *linebuf, const char *data, size_t len ) {
95
+	size_t consume = 0;
96
+	size_t copy = 0;
97
+	size_t new_len;
98
+	char *new_data;
99
+	int terminator;
100
+
101
+	/* First, handle the termination of the previous line */
102
+	if ( linebuf->skip_terminators ) {
103
+		/* Free buffered string */
104
+		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
+
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
+		}
123
+	}
124
+
125
+	/* Reallocate data buffer and copy in new data */
126
+	new_len = ( linebuf->len + copy );
127
+	new_data = realloc ( linebuf->data, ( new_len + 1 ) );
128
+	if ( ! new_data )
129
+		return -ENOMEM;
130
+	memcpy ( ( new_data + linebuf->len ), ( data + consume - copy ),
131
+		 copy );
132
+	new_data[new_len] = '\0';
133
+	linebuf->data = new_data;
134
+	linebuf->len = new_len;
135
+	return consume;
136
+}

+ 37
- 0
src/include/gpxe/linebuf.h Ver fichero

@@ -0,0 +1,37 @@
1
+#ifndef _GPXE_LINEBUF_H
2
+#define _GPXE_LINEBUF_H
3
+
4
+/** @file
5
+ *
6
+ * Line buffering
7
+ *
8
+ */
9
+
10
+#include <stdint.h>
11
+#include <stddef.h>
12
+
13
+/** A line buffer */
14
+struct line_buffer {
15
+	/** Current data in the buffer */
16
+	char *data;
17
+	/** Length of current data */
18
+	size_t len;
19
+	/** Bitmask of terminating characters to skip over */
20
+	unsigned int skip_terminators;
21
+};
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 );
35
+extern void empty_line_buffer ( struct line_buffer *linebuf );
36
+
37
+#endif /* _GPXE_LINEBUF_H */

+ 27
- 0
src/tests/linebuf_test.c Ver fichero

@@ -0,0 +1,27 @@
1
+#include <stdint.h>
2
+#include <string.h>
3
+#include <vsprintf.h>
4
+#include <gpxe/linebuf.h>
5
+
6
+static const char data1[] = 
7
+"Hello world\r\n"
8
+"This is a particularly mean set of lines\n"
9
+"with a mixture of terminators\r\r\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";
12
+
13
+void linebuf_test ( void ) {
14
+	struct line_buffer linebuf;
15
+	const char *data = data1;
16
+	size_t len = ( sizeof ( data1 ) - 1 /* be mean; strip the NUL */ );
17
+	size_t buffered;
18
+
19
+	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;
24
+	}
25
+
26
+	empty_line_buffer ( &linebuf );
27
+}

Loading…
Cancelar
Guardar