nsReadLine.h (4229B)
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 NS_ReadLine(StreamType* aStream, nsLineBuffer<CharT>* aBuffer, 74 StringType& aLine, bool* more) { 75 CharT eolchar = 0; // the first eol char or 1 after \r\n or \n\r is found 76 77 aLine.Truncate(); 78 79 while (true) { // will be returning out of this loop on eol or eof 80 if (aBuffer->start == aBuffer->end) { // buffer is empty. Read into it. 81 uint32_t bytesRead; 82 nsresult rv = aStream->Read(aBuffer->buf, kLineBufferSize, &bytesRead); 83 if (NS_FAILED(rv) || MOZ_UNLIKELY(bytesRead == 0)) { 84 *more = false; 85 return rv; 86 } 87 aBuffer->start = aBuffer->buf; 88 aBuffer->end = aBuffer->buf + bytesRead; 89 *(aBuffer->end) = '\0'; 90 } 91 92 /* 93 * Walk the buffer looking for an end-of-line. 94 * There are 3 cases to consider: 95 * 1. the eol char is the last char in the buffer 96 * 2. the eol char + one more char at the end of the buffer 97 * 3. the eol char + two or more chars at the end of the buffer 98 * we need at least one char after the first eol char to determine if 99 * it's a \r\n or \n\r sequence (and skip over it), and we need one 100 * more char after the end-of-line to set |more| correctly. 101 */ 102 CharT* current = aBuffer->start; 103 if (MOZ_LIKELY(eolchar == 0)) { 104 for (; current < aBuffer->end; ++current) { 105 if (*current == '\n' || *current == '\r') { 106 eolchar = *current; 107 *current++ = '\0'; 108 aLine.Append(aBuffer->start); 109 break; 110 } 111 } 112 } 113 if (MOZ_LIKELY(eolchar != 0)) { 114 for (; current < aBuffer->end; ++current) { 115 if ((eolchar == '\r' && *current == '\n') || 116 (eolchar == '\n' && *current == '\r')) { 117 eolchar = 1; 118 continue; 119 } 120 aBuffer->start = current; 121 *more = true; 122 return NS_OK; 123 } 124 } 125 126 if (eolchar == 0) aLine.Append(aBuffer->start); 127 aBuffer->start = aBuffer->end; // mark the buffer empty 128 } 129 } 130 131 #endif // nsReadLine_h__