Browse Source

Implement I/O with vector<char>.


git-svn-id: https://svn.microneil.com/svn/CodeDweller/branches/adeniz_1@84 d34b734f-a00e-4b39-a726-e4eeb87269ab
adeniz_1
adeniz 9 years ago
parent
commit
c9f179a972
2 changed files with 258 additions and 118 deletions
  1. 0
    113
      child.cpp
  2. 258
    5
      child.hpp

+ 0
- 113
child.cpp View File

} }
void Child::CircularBuffer::getAndErase(std::string &buf, size_t nBytes) {
if ( (0 == nBytes) || (nBytes > nUsed()) ) {
nBytes = nUsed();
}
buf.clear();
if (buf.capacity() < nBytes) {
buf.reserve(nBytes);
}
for (size_t i = 0; i < nBytes; i++) {
buf.push_back(buffer[iBegin]);
nextIndex(iBegin);
}
}
Child::Child(std::vector<std::string> const &args, Child::Child(std::vector<std::string> const &args,
size_t bufSize, size_t bufSize,
std::uint16_t nominalAboveMinPollTime_ms, std::uint16_t nominalAboveMinPollTime_ms,
} }
bool Child::write(std::string const &data) {
if (!isRunning()) {
throw std::logic_error("No child process is running.");
}
// The free space in the write buffer can be checked without
// locking the mutex because:
//
// 1) bufferCapacity is unchanging, and
//
// 2) nWriteBytes is only decreased by the writer thread.
//
// This means that the calculated free space can only increase by
// the action of the writer thread; the calculated free space is a
// minimum value. In the worst case, this method would return
// false unnecessarily.
if (data.size() > bufferCapacity - nWriteBytes) {
return false;
}
std::lock_guard<std::mutex> lock(writeBufferMutex);
std::copy(data.data(),
data.data() + data.size(),
&(writeBuffer[nWriteBytes]));
nWriteBytes += data.size();
return true;
}
size_t Child::writeAndShrink(std::string &data) {
if (!isRunning()) {
throw std::logic_error("No child process is running.");
}
// See the comment above regarding checking the free space in the
// write buffer without locking the mutex.
size_t nFree = bufferCapacity - nWriteBytes;
if (0 == nFree) {
return 0;
}
std::lock_guard<std::mutex> lock(writeBufferMutex);
size_t nBytesToCopy = data.size();
if (nBytesToCopy > nFree) {
nBytesToCopy = nFree;
}
std::copy(data.data(),
data.data() + nBytesToCopy,
&(writeBuffer[nWriteBytes]));
nWriteBytes += nBytesToCopy;
data.erase(0, nBytesToCopy);
return nBytesToCopy;
}
bool Child::isFinishedWriting() const { bool Child::isFinishedWriting() const {
return ( (0 == nWriteBytes) && return ( (0 == nWriteBytes) &&
(0 == nTransmitBytes) ); (0 == nTransmitBytes) );
} }
size_t Child::read(std::string &data, size_t nBytes) {
if (!isRunning()) {
throw std::logic_error("No child process is running.");
}
data.clear();
// Whether the read circular buffer is empty can be checked
// without locking the mutex because:
//
if (readBuffer.empty()) {
return 0;
}
std::lock_guard<std::mutex> lock(readBufferMutex);
size_t nBytesToRead = nBytes;
if (nBytesToRead > readBuffer.nUsed()) {
nBytesToRead = readBuffer.nUsed();
}
readBuffer.getAndErase(data, nBytesToRead);
return data.size();
}
void Child::close() { void Child::close() {
if (!childStarted) { if (!childStarted) {

+ 258
- 5
child.hpp View File

This method gets the specified number of bytes from the This method gets the specified number of bytes from the
buffer, and erases those bytes from the buffer. buffer, and erases those bytes from the buffer.


The type T must have the following methods:

<ol>

<li>clear().</li>

<li>capacity().</li>

<li>reserve().</li>

<li>push_back(char).</li>

</ol>

Both std::vector<char> and std::string can be used for T.

@param[out] buf receives the data. The contents of buf are @param[out] buf receives the data. The contents of buf are
replaced with the data in the circular buffer. replaced with the data in the circular buffer.


a value of zero for nBytes gets and erases all the data. a value of zero for nBytes gets and erases all the data.


*/ */
void getAndErase(std::string &buf, size_t nBytes = 0);
template<typename T>
void getAndErase(T &buf, size_t nBytes) {

if ( (0 == nBytes) || (nBytes > nUsed()) ) {
nBytes = nUsed();
}

buf.clear();
if (buf.capacity() < nBytes) {
buf.reserve(nBytes);
}

for (size_t i = 0; i < nBytes; i++) {
buf.push_back(buffer[iBegin]);
nextIndex(iBegin);
}

}


private: private:


/** All-or-nothing non-blocking queue write request to the child. /** All-or-nothing non-blocking queue write request to the child.


This methods attempts to queue a write request consisting of This methods attempts to queue a write request consisting of
the entire contents of the string.
the entire contents of a container.

The type T must have the following methods:

<ol>

<li>begin().</li>

<li>end().</li>

<li>size().</li>

</ol>

Both std::vector<char> and std::string can be used for T.


@param[in] data is the data to queue. @param[in] data is the data to queue.


otherwise. otherwise.


*/ */
bool write(std::string const &data);
template<typename T>
bool write(T const &data) {

if (!isRunning()) {
throw std::logic_error("No child process is running.");
}

// The free space in the write buffer can be checked without
// locking the mutex because:
//
// 1) bufferCapacity is unchanging, and
//
// 2) nWriteBytes is only decreased by the writer thread.
//
// This means that the calculated free space can only increase
// by the action of the writer thread; the calculated free space
// is a minimum value. In the worst case, this method would
// return false unnecessarily.
if (data.size() > bufferCapacity - nWriteBytes) {
return false;
}

std::lock_guard<std::mutex> lock(writeBufferMutex);

std::copy(data.begin(),
data.end(),
&(writeBuffer[nWriteBytes]));

nWriteBytes += data.size();

return true;

}

/** All-or-nothing non-blocking queue write request to the child.

This methods attempts to queue a write request consisting of
the entire contents of string literal.

@param[in] data points to the string literal.

@returns true if the write request was queued, false
otherwise.

*/
bool write(char const *data) {
return write(std::string(data));
}

/** Non-blocking queue write request to the child.

This methods attempts to queue a write request consisting of
the the specified contents of a container. The number of
bytes queued is the smaller of the size of the container and
the number of free bytes in the output buffer.

The type T must have the following methods:

<ol>

<li>begin().</li>

<li>end().</li>

<li>size().</li>

</ol>

Both std::vector<char> and std::string can be used for T.

@param[in] data is the data to queue.

@param[in] nBytes is the requested number of bytes to queue.

@returns the number of bytes queued.

*/
template<typename T>
size_t write(T const &data, size_t nBytes) {

if (!isRunning()) {
throw std::logic_error("No child process is running.");
}

// See the comment above regarding checking the free space in the
// write buffer without locking the mutex.
size_t nFree = bufferCapacity - nWriteBytes;

if (nBytes > nFree) {
nBytes = nFree;
}
if (nBytes > data.size()) {
nBytes = data.size();
}

std::lock_guard<std::mutex> lock(writeBufferMutex);

std::copy(data.begin(),
data.begin() + nBytes,
&(writeBuffer[nWriteBytes]));

nWriteBytes += nBytes;

return nBytes;

}


/** Non-blocking queue write request to the child. /** Non-blocking queue write request to the child.


This methods attempts to queue a write request consisting of This methods attempts to queue a write request consisting of
as much as possible of the contents of the string. as much as possible of the contents of the string.


The type T must have the following methods:

<ol>

<li>begin().</li>

<li>size().</li>

</ol>

Both std::vector<char> and std::string can be used for T.

@param[in, out] data on input is the data to queue. On @param[in, out] data on input is the data to queue. On
output, data contains the data that was not queued. output, data contains the data that was not queued.


@returns the number of bytes queued. @returns the number of bytes queued.


*/ */
size_t writeAndShrink(std::string &data);
template<typename T>
size_t writeAndShrink(T &data) {

if (!isRunning()) {
throw std::logic_error("No child process is running.");
}

// See the comment above regarding checking the free space in the
// write buffer without locking the mutex.
size_t nFree = bufferCapacity - nWriteBytes;

if (0 == nFree) {
return 0;
}

std::lock_guard<std::mutex> lock(writeBufferMutex);

size_t nBytesToCopy = data.size();

if (nBytesToCopy > nFree) {
nBytesToCopy = nFree;
}

std::copy(data.begin(),
data.begin() + nBytesToCopy,
&(writeBuffer[nWriteBytes]));
nWriteBytes += nBytesToCopy;

data.erase(data.begin(), data.begin() + nBytesToCopy);

return nBytesToCopy;

}


/** Check if all queued data was transmitted. /** Check if all queued data was transmitted.


the child. The data that is provided is erased from the input the child. The data that is provided is erased from the input
buffer. buffer.


The type T must have the following methods:

<ol>

<li>Methods required by CircularBuffer::getAndErase().</li>

<li>size().</li>

</ol>

Both std::vector<char> and std::string can be used for T.

@param[out] data contains the copied data. @param[out] data contains the copied data.


@param[in] nBytes is the number of bytes to attempt to copy. @param[in] nBytes is the number of bytes to attempt to copy.


@returns the number of bytes copied. @returns the number of bytes copied.


@see CircularBuffer::getAndErase().

*/ */
size_t read(std::string &data, size_t nBytes = 0);
template<typename T>
size_t read(T &data, size_t nBytes = 0) {

if (!isRunning()) {
throw std::logic_error("No child process is running.");
}

data.clear();

// Can be called in the user thread without locking the mutex.
if (readBuffer.empty()) {
return 0;
}

std::lock_guard<std::mutex> lock(readBufferMutex);

size_t nBytesToRead = nBytes;

if (nBytesToRead > readBuffer.nUsed()) {
nBytesToRead = readBuffer.nUsed();
}
readBuffer.getAndErase(data, nBytesToRead);

return data.size();

}


/** Check whether the child process is running. /** Check whether the child process is running.


*/ */
void run(); void run();


/** Non-blocking request to get data read from the child.

This method attempts to get up to a specified number of bytes
of data from the input buffer containing data received from
the child. The data that is provided is erased from the input
buffer.

@param[in, out] dataPtr points to the memory that receives the
data.

@param[in] nBytes is the number of bytes to attempt to copy.

@returns the number of bytes copied.

*/
size_t read(char *const dataPtr, size_t nBytes);

/// Reader thread object. /// Reader thread object.
std::thread readerThread; std::thread readerThread;



Loading…
Cancel
Save