[3998] | 1 | /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
---|
| 2 | /* |
---|
| 3 | * Copyright (C) 2004-2013 HUBzero Foundation, LLC |
---|
| 4 | * |
---|
| 5 | * Author: George A. Howlett <gah@purdue.edu> |
---|
| 6 | */ |
---|
| 7 | |
---|
| 8 | #include <cstdlib> |
---|
| 9 | #include <cerrno> |
---|
| 10 | #include <cstdio> |
---|
| 11 | #include <unistd.h> |
---|
| 12 | #include <cstring> |
---|
| 13 | |
---|
| 14 | #include "ReadBuffer.h" |
---|
| 15 | #include "Trace.h" |
---|
| 16 | |
---|
| 17 | using namespace GeoVis; |
---|
| 18 | |
---|
| 19 | /** |
---|
| 20 | * \param[in] fd File descriptor to read |
---|
| 21 | * \param[in] bufferSize Block size to use in internal buffer |
---|
| 22 | */ |
---|
| 23 | ReadBuffer::ReadBuffer(int fd, size_t bufferSize) : |
---|
| 24 | _bufferSize(bufferSize), |
---|
| 25 | _fd(fd), |
---|
| 26 | _lastStatus(OK) |
---|
| 27 | { |
---|
| 28 | _bytes = new unsigned char [_bufferSize]; |
---|
| 29 | flush(); |
---|
| 30 | } |
---|
| 31 | |
---|
| 32 | ReadBuffer::~ReadBuffer() |
---|
| 33 | { |
---|
| 34 | TRACE("Deleting ReadBuffer"); |
---|
| 35 | delete [] _bytes; |
---|
| 36 | } |
---|
| 37 | |
---|
| 38 | /** |
---|
| 39 | * \brief Checks if a new line is currently in the buffer. |
---|
| 40 | * |
---|
| 41 | * \return the index of the character past the new line. |
---|
| 42 | */ |
---|
| 43 | size_t |
---|
| 44 | ReadBuffer::nextNewLine() |
---|
| 45 | { |
---|
| 46 | /* Check for a newline in the current buffer. */ |
---|
| 47 | unsigned char *ptr = |
---|
| 48 | (unsigned char *)memchr(_bytes + _mark, '\n', _fill - _mark); |
---|
| 49 | if (ptr == NULL) |
---|
| 50 | return 0; |
---|
| 51 | else |
---|
| 52 | return (ptr - _bytes + 1); |
---|
| 53 | } |
---|
| 54 | |
---|
| 55 | /** |
---|
| 56 | * \brief Fills the buffer with available data. |
---|
| 57 | * |
---|
| 58 | * Any existing data in the buffer is moved to the front of the buffer, |
---|
| 59 | * then the channel is read to fill the rest of the buffer. |
---|
| 60 | * |
---|
| 61 | * \return If an error occur when reading the channel, then ERROR is |
---|
| 62 | * returned. ENDFILE is returned on EOF. If the buffer can't be filled, |
---|
| 63 | * then CONTINUE is returned. |
---|
| 64 | */ |
---|
| 65 | ReadBuffer::BufferStatus |
---|
| 66 | ReadBuffer::doFill() |
---|
| 67 | { |
---|
| 68 | //TRACE("Enter, mark: %lu fill: %lu", _mark, _fill); |
---|
| 69 | if (_mark >= _fill) { |
---|
| 70 | flush(); /* Fully consumed buffer */ |
---|
| 71 | } |
---|
| 72 | if (_mark > 0) { |
---|
| 73 | /* Some data has been consumed. Move the unconsumed data to the front |
---|
| 74 | * of the buffer. */ |
---|
| 75 | TRACE("memmove %lu bytes", _fill-_mark); |
---|
| 76 | memmove(_bytes, _bytes + _mark, _fill - _mark); |
---|
| 77 | _fill -= _mark; |
---|
| 78 | _mark = 0; |
---|
| 79 | } |
---|
| 80 | ssize_t numRead; |
---|
| 81 | size_t bytesLeft; |
---|
| 82 | |
---|
| 83 | bytesLeft = _bufferSize - _fill; |
---|
| 84 | //TRACE("going to read %lu bytes", bytesLeft); |
---|
| 85 | numRead = read(_fd, _bytes + _fill, bytesLeft); |
---|
| 86 | if (numRead == 0) { |
---|
| 87 | /* EOF */ |
---|
| 88 | TRACE("EOF found reading buffer"); |
---|
| 89 | return ReadBuffer::ENDFILE; |
---|
| 90 | } |
---|
| 91 | if (numRead < 0) { |
---|
| 92 | if (errno != EAGAIN) { |
---|
| 93 | ERROR("error reading buffer: %s", strerror(errno)); |
---|
| 94 | return ReadBuffer::ERROR; |
---|
| 95 | } |
---|
| 96 | TRACE("Short read for buffer"); |
---|
| 97 | return ReadBuffer::CONTINUE; |
---|
| 98 | } |
---|
| 99 | _fill += numRead; |
---|
| 100 | //TRACE("Read %lu bytes", numRead); |
---|
| 101 | return ((size_t)numRead == bytesLeft) |
---|
| 102 | ? ReadBuffer::OK : ReadBuffer::CONTINUE; |
---|
| 103 | } |
---|
| 104 | |
---|
| 105 | /** |
---|
| 106 | * \brief Read the requested number of bytes from the buffer. |
---|
| 107 | |
---|
| 108 | * Fails if the requested number of bytes are not immediately |
---|
| 109 | * available. Never should be short. |
---|
| 110 | */ |
---|
| 111 | ReadBuffer::BufferStatus |
---|
| 112 | ReadBuffer::followingData(unsigned char *out, size_t numBytes) |
---|
| 113 | { |
---|
| 114 | TRACE("Enter"); |
---|
| 115 | while (numBytes > 0) { |
---|
| 116 | size_t bytesLeft; |
---|
| 117 | |
---|
| 118 | bytesLeft = _fill - _mark; |
---|
| 119 | if (bytesLeft > 0) { |
---|
| 120 | int size; |
---|
| 121 | |
---|
| 122 | /* Pull bytes out of the buffer, updating the mark. */ |
---|
| 123 | size = (bytesLeft > numBytes) ? numBytes : bytesLeft; |
---|
| 124 | memcpy(out, _bytes + _mark, size); |
---|
| 125 | _mark += size; |
---|
| 126 | numBytes -= size; |
---|
| 127 | out += size; |
---|
| 128 | } |
---|
| 129 | if (numBytes == 0) { |
---|
| 130 | /* Received requested # bytes. */ |
---|
| 131 | return ReadBuffer::OK; |
---|
| 132 | } |
---|
| 133 | /* Didn't get enough bytes, need to read some more. */ |
---|
| 134 | _lastStatus = doFill(); |
---|
| 135 | if (_lastStatus == ReadBuffer::ERROR || |
---|
| 136 | _lastStatus == ReadBuffer::ENDFILE) { |
---|
| 137 | return _lastStatus; |
---|
| 138 | } |
---|
| 139 | } |
---|
| 140 | return ReadBuffer::OK; |
---|
| 141 | } |
---|
| 142 | |
---|
| 143 | /** |
---|
| 144 | * \brief Returns the next available line (terminated by a newline) |
---|
| 145 | * |
---|
| 146 | * If insufficient data is in the buffer, then the channel is |
---|
| 147 | * read for more data. If reading the channel results in a |
---|
| 148 | * short read, CONTINUE is returned and *numBytesPtr is set to 0. |
---|
| 149 | */ |
---|
| 150 | ReadBuffer::BufferStatus |
---|
| 151 | ReadBuffer::getLine(size_t *numBytesPtr, unsigned char **bytesPtr) |
---|
| 152 | { |
---|
| 153 | TRACE("Enter"); |
---|
| 154 | *numBytesPtr = 0; |
---|
| 155 | *bytesPtr = NULL; |
---|
| 156 | |
---|
| 157 | _lastStatus = ReadBuffer::OK; |
---|
| 158 | for (;;) { |
---|
| 159 | size_t _newline; |
---|
| 160 | |
---|
| 161 | _newline = nextNewLine(); |
---|
| 162 | if (_newline > 0) { |
---|
| 163 | /* Start of the line. */ |
---|
| 164 | *bytesPtr = _bytes + _mark; |
---|
| 165 | /* Number of bytes in the line. */ |
---|
| 166 | *numBytesPtr = _newline - _mark; |
---|
| 167 | _mark = _newline; |
---|
| 168 | return ReadBuffer::OK; |
---|
| 169 | } |
---|
| 170 | /* Couldn't find a newline, so it may be that we need to read some |
---|
| 171 | * more. Check first that last read wasn't a short read. */ |
---|
| 172 | if (_lastStatus == ReadBuffer::CONTINUE) { |
---|
| 173 | /* No complete line just yet. */ |
---|
| 174 | return ReadBuffer::CONTINUE; |
---|
| 175 | } |
---|
| 176 | /* Try to add more data to the buffer. */ |
---|
| 177 | _lastStatus = doFill(); |
---|
| 178 | if (_lastStatus == ReadBuffer::ERROR || |
---|
| 179 | _lastStatus == ReadBuffer::ENDFILE) { |
---|
| 180 | return _lastStatus; |
---|
| 181 | } |
---|
| 182 | /* OK or CONTINUE */ |
---|
| 183 | } |
---|
| 184 | return ReadBuffer::CONTINUE; |
---|
| 185 | } |
---|