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 | } |
---|