Parcourir la 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 il y a 9 ans
Parent
révision
c9f179a972
2 fichiers modifiés avec 258 ajouts et 118 suppressions
  1. 0
    113
      child.cpp
  2. 258
    5
      child.hpp

+ 0
- 113
child.cpp Voir le fichier

@@ -766,24 +766,6 @@ namespace CodeDweller {
}
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,
size_t bufSize,
std::uint16_t nominalAboveMinPollTime_ms,
@@ -875,106 +857,11 @@ namespace CodeDweller {
}
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 {
return ( (0 == nWriteBytes) &&
(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() {
if (!childStarted) {

+ 258
- 5
child.hpp Voir le fichier

@@ -478,6 +478,22 @@ namespace CodeDweller {
This method gets the specified number of bytes from the
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
replaced with the data in the circular buffer.

@@ -485,7 +501,24 @@ namespace CodeDweller {
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:

@@ -627,7 +660,21 @@ namespace CodeDweller {
/** 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 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.

@@ -635,20 +682,169 @@ namespace CodeDweller {
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.

This methods attempts to queue a write request consisting of
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
output, data contains the data that was not 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.

@@ -665,6 +861,18 @@ namespace CodeDweller {
the child. The data that is provided is erased from the input
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[in] nBytes is the number of bytes to attempt to copy.
@@ -673,8 +881,36 @@ namespace CodeDweller {

@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.

@@ -749,6 +985,23 @@ namespace CodeDweller {
*/
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.
std::thread readerThread;


Chargement…
Annuler
Enregistrer