netwerk/base/public/nsReadLine.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/base/public/nsReadLine.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,134 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#ifndef nsReadLine_h__
    1.11 +#define nsReadLine_h__
    1.12 +
    1.13 +#include "nsIInputStream.h"
    1.14 +#include "mozilla/Likely.h"
    1.15 +
    1.16 +/**
    1.17 + * @file
    1.18 + * Functions to read complete lines from an input stream.
    1.19 + *
    1.20 + * To properly use the helper function in here (NS_ReadLine) the caller should
    1.21 + * create a nsLineBuffer<T> with new, and pass it to NS_ReadLine every time it
    1.22 + * wants a line out.
    1.23 + *
    1.24 + * When done, the object should be deleted.
    1.25 + */
    1.26 +
    1.27 +/**
    1.28 + * @internal
    1.29 + * Buffer size. This many bytes will be buffered. If a line is longer than this,
    1.30 + * the partial line will be appended to the out parameter of NS_ReadLine and the
    1.31 + * buffer will be emptied.
    1.32 + * Note: if you change this constant, please update the regression test in
    1.33 + * netwerk/test/unit/test_readline.js accordingly (bug 397850).
    1.34 + */
    1.35 +#define kLineBufferSize 4096
    1.36 +
    1.37 +/**
    1.38 + * @internal
    1.39 + * Line buffer structure, buffers data from an input stream.
    1.40 + * The buffer is empty when |start| == |end|.
    1.41 + * Invariant: |start| <= |end|
    1.42 + */
    1.43 +template<typename CharT>
    1.44 +class nsLineBuffer {
    1.45 +  public:
    1.46 +    nsLineBuffer() : start(buf), end(buf) { }
    1.47 +
    1.48 +  CharT buf[kLineBufferSize+1];
    1.49 +  CharT* start;
    1.50 +  CharT* end;
    1.51 +};
    1.52 +
    1.53 +/**
    1.54 + * Read a line from an input stream. Lines are separated by '\r' (0x0D) or '\n'
    1.55 + * (0x0A), or "\r\n" or "\n\r".
    1.56 + *
    1.57 + * @param aStream
    1.58 + *        The stream to read from
    1.59 + * @param aBuffer
    1.60 + *        The line buffer to use.  A single line buffer must not be used with
    1.61 + *        different input streams.
    1.62 + * @param aLine [out]
    1.63 + *        The string where the line will be stored.
    1.64 + * @param more [out]
    1.65 + *        Whether more data is available in the buffer. If true, NS_ReadLine may
    1.66 + *        be called again to read further lines. Otherwise, further calls to
    1.67 + *        NS_ReadLine will return an error.
    1.68 + *
    1.69 + * @retval NS_OK
    1.70 + *         Read successful
    1.71 + * @retval error
    1.72 + *         Input stream returned an error upon read. See
    1.73 + *         nsIInputStream::read.
    1.74 + */
    1.75 +template<typename CharT, class StreamType, class StringType>
    1.76 +nsresult
    1.77 +NS_ReadLine (StreamType* aStream, nsLineBuffer<CharT> * aBuffer,
    1.78 +             StringType & aLine, bool *more)
    1.79 +{
    1.80 +  CharT eolchar = 0; // the first eol char or 1 after \r\n or \n\r is found
    1.81 +
    1.82 +  aLine.Truncate();
    1.83 +
    1.84 +  while (1) { // will be returning out of this loop on eol or eof
    1.85 +    if (aBuffer->start == aBuffer->end) { // buffer is empty.  Read into it.
    1.86 +      uint32_t bytesRead;
    1.87 +      nsresult rv = aStream->Read(aBuffer->buf, kLineBufferSize, &bytesRead);
    1.88 +      if (NS_FAILED(rv) || MOZ_UNLIKELY(bytesRead == 0)) {
    1.89 +        *more = false;
    1.90 +        return rv;
    1.91 +      }
    1.92 +      aBuffer->start = aBuffer->buf;
    1.93 +      aBuffer->end = aBuffer->buf + bytesRead;
    1.94 +      *(aBuffer->end) = '\0';
    1.95 +    }
    1.96 +
    1.97 +    /*
    1.98 +     * Walk the buffer looking for an end-of-line.
    1.99 +     * There are 3 cases to consider:
   1.100 +     *  1. the eol char is the last char in the buffer
   1.101 +     *  2. the eol char + one more char at the end of the buffer
   1.102 +     *  3. the eol char + two or more chars at the end of the buffer
   1.103 +     * we need at least one char after the first eol char to determine if
   1.104 +     * it's a \r\n or \n\r sequence (and skip over it), and we need one
   1.105 +     * more char after the end-of-line to set |more| correctly.
   1.106 +     */
   1.107 +    CharT* current = aBuffer->start;
   1.108 +    if (MOZ_LIKELY(eolchar == 0)) {
   1.109 +      for ( ; current < aBuffer->end; ++current) {
   1.110 +        if (*current == '\n' || *current == '\r') {
   1.111 +          eolchar = *current;
   1.112 +          *current++ = '\0';
   1.113 +          aLine.Append(aBuffer->start);
   1.114 +          break;
   1.115 +        }
   1.116 +      }
   1.117 +    }
   1.118 +    if (MOZ_LIKELY(eolchar != 0)) {
   1.119 +      for ( ; current < aBuffer->end; ++current) {
   1.120 +        if ((eolchar == '\r' && *current == '\n') ||
   1.121 +            (eolchar == '\n' && *current == '\r')) {
   1.122 +          eolchar = 1;
   1.123 +          continue;
   1.124 +        }
   1.125 +        aBuffer->start = current;
   1.126 +        *more = true;
   1.127 +        return NS_OK;
   1.128 +      }
   1.129 +    }
   1.130 +
   1.131 +    if (eolchar == 0)
   1.132 +      aLine.Append(aBuffer->start);
   1.133 +    aBuffer->start = aBuffer->end; // mark the buffer empty
   1.134 +  }
   1.135 +}
   1.136 +
   1.137 +#endif // nsReadLine_h__

mercurial