|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #ifndef nsReadLine_h__ |
|
8 #define nsReadLine_h__ |
|
9 |
|
10 #include "nsIInputStream.h" |
|
11 #include "mozilla/Likely.h" |
|
12 |
|
13 /** |
|
14 * @file |
|
15 * Functions to read complete lines from an input stream. |
|
16 * |
|
17 * To properly use the helper function in here (NS_ReadLine) the caller should |
|
18 * create a nsLineBuffer<T> with new, and pass it to NS_ReadLine every time it |
|
19 * wants a line out. |
|
20 * |
|
21 * When done, the object should be deleted. |
|
22 */ |
|
23 |
|
24 /** |
|
25 * @internal |
|
26 * Buffer size. This many bytes will be buffered. If a line is longer than this, |
|
27 * the partial line will be appended to the out parameter of NS_ReadLine and the |
|
28 * buffer will be emptied. |
|
29 * Note: if you change this constant, please update the regression test in |
|
30 * netwerk/test/unit/test_readline.js accordingly (bug 397850). |
|
31 */ |
|
32 #define kLineBufferSize 4096 |
|
33 |
|
34 /** |
|
35 * @internal |
|
36 * Line buffer structure, buffers data from an input stream. |
|
37 * The buffer is empty when |start| == |end|. |
|
38 * Invariant: |start| <= |end| |
|
39 */ |
|
40 template<typename CharT> |
|
41 class nsLineBuffer { |
|
42 public: |
|
43 nsLineBuffer() : start(buf), end(buf) { } |
|
44 |
|
45 CharT buf[kLineBufferSize+1]; |
|
46 CharT* start; |
|
47 CharT* end; |
|
48 }; |
|
49 |
|
50 /** |
|
51 * Read a line from an input stream. Lines are separated by '\r' (0x0D) or '\n' |
|
52 * (0x0A), or "\r\n" or "\n\r". |
|
53 * |
|
54 * @param aStream |
|
55 * The stream to read from |
|
56 * @param aBuffer |
|
57 * The line buffer to use. A single line buffer must not be used with |
|
58 * different input streams. |
|
59 * @param aLine [out] |
|
60 * The string where the line will be stored. |
|
61 * @param more [out] |
|
62 * Whether more data is available in the buffer. If true, NS_ReadLine may |
|
63 * be called again to read further lines. Otherwise, further calls to |
|
64 * NS_ReadLine will return an error. |
|
65 * |
|
66 * @retval NS_OK |
|
67 * Read successful |
|
68 * @retval error |
|
69 * Input stream returned an error upon read. See |
|
70 * nsIInputStream::read. |
|
71 */ |
|
72 template<typename CharT, class StreamType, class StringType> |
|
73 nsresult |
|
74 NS_ReadLine (StreamType* aStream, nsLineBuffer<CharT> * aBuffer, |
|
75 StringType & aLine, bool *more) |
|
76 { |
|
77 CharT eolchar = 0; // the first eol char or 1 after \r\n or \n\r is found |
|
78 |
|
79 aLine.Truncate(); |
|
80 |
|
81 while (1) { // will be returning out of this loop on eol or eof |
|
82 if (aBuffer->start == aBuffer->end) { // buffer is empty. Read into it. |
|
83 uint32_t bytesRead; |
|
84 nsresult rv = aStream->Read(aBuffer->buf, kLineBufferSize, &bytesRead); |
|
85 if (NS_FAILED(rv) || MOZ_UNLIKELY(bytesRead == 0)) { |
|
86 *more = false; |
|
87 return rv; |
|
88 } |
|
89 aBuffer->start = aBuffer->buf; |
|
90 aBuffer->end = aBuffer->buf + bytesRead; |
|
91 *(aBuffer->end) = '\0'; |
|
92 } |
|
93 |
|
94 /* |
|
95 * Walk the buffer looking for an end-of-line. |
|
96 * There are 3 cases to consider: |
|
97 * 1. the eol char is the last char in the buffer |
|
98 * 2. the eol char + one more char at the end of the buffer |
|
99 * 3. the eol char + two or more chars at the end of the buffer |
|
100 * we need at least one char after the first eol char to determine if |
|
101 * it's a \r\n or \n\r sequence (and skip over it), and we need one |
|
102 * more char after the end-of-line to set |more| correctly. |
|
103 */ |
|
104 CharT* current = aBuffer->start; |
|
105 if (MOZ_LIKELY(eolchar == 0)) { |
|
106 for ( ; current < aBuffer->end; ++current) { |
|
107 if (*current == '\n' || *current == '\r') { |
|
108 eolchar = *current; |
|
109 *current++ = '\0'; |
|
110 aLine.Append(aBuffer->start); |
|
111 break; |
|
112 } |
|
113 } |
|
114 } |
|
115 if (MOZ_LIKELY(eolchar != 0)) { |
|
116 for ( ; current < aBuffer->end; ++current) { |
|
117 if ((eolchar == '\r' && *current == '\n') || |
|
118 (eolchar == '\n' && *current == '\r')) { |
|
119 eolchar = 1; |
|
120 continue; |
|
121 } |
|
122 aBuffer->start = current; |
|
123 *more = true; |
|
124 return NS_OK; |
|
125 } |
|
126 } |
|
127 |
|
128 if (eolchar == 0) |
|
129 aLine.Append(aBuffer->start); |
|
130 aBuffer->start = aBuffer->end; // mark the buffer empty |
|
131 } |
|
132 } |
|
133 |
|
134 #endif // nsReadLine_h__ |