The decisions are determined in the connection protocol.
I've thought about making this a template parameter, but
didn't like what I was coming up with when I looked at
that. The thisFormat_ isn't changable after the buffer
is constructed. The otherFormat_ is but that is to permit
the same buffer to be used to handle requests from both
little and big endian users. The way things are set up
now, it is possible that a programming error could lead
to incorrectly setting the otherFormat_ in the middle of
a connection, but that doesn't seem like a likely problem
to me.
I've changed Buffer to be a class template that takes
a formatting type. There are three format types --
SameFormat, LeastSignificantFirst and
MostSignificantFirst. I've copied almost the whole
file here.
#ifndef Buffer_hh
#define Buffer_hh
#include <climits>
#if CHAR_BIT != 8
#error Only 8 bit char supported
#endif
#if defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined
(__WIN32__) || defined(__CYGWIN__)
#include <windows.h>
#else
#include <errno.h>
#include <sys/ioctl.h>
#include <unistd.h>
#endif
#include <string.h>
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <ErrorWordsShepherd.hh>
uint8_t const least_significant_first = 0;
uint8_t const most_significant_first = 1;
inline
uint8_t
MachineByteOrder()
{
int i = 1;
char *p = (char *) &i;
if (1 == p[0]) { // Looks like little endian
return least_significant_first;
} else {
return most_significant_first; // probably big endian
}
}
template <typename W>
class Buffer;
class SameFormat
{
public:
template <typename U>
void
Write(Buffer<SameFormat>& buf, U data);
template <typename U>
void
WriteBlock(Buffer<SameFormat>& buf, U const* data, unsigned int
elements);
};
class LeastSignificantFirst
{
public:
inline void Write(Buffer<LeastSignificantFirst>& buf, uint16_t);
inline void Write(Buffer<LeastSignificantFirst>& buf, uint32_t);
inline void Write(Buffer<LeastSignificantFirst>& buf, uint64_t);
template <typename U>
inline void
WriteBlock(Buffer<LeastSignificantFirst>& buf,
U const* data, unsigned int elements);
inline void
WriteBlock(Buffer<LeastSignificantFirst>& buf,
uint8_t const* data, unsigned int elements);
inline void
WriteBlock(Buffer<LeastSignificantFirst>& buf,
int8_t const* data, unsigned int elements);
};
class MostSignificantFirst
{
public:
inline void Write(Buffer<MostSignificantFirst>& buf, uint16_t);
inline void Write(Buffer<MostSignificantFirst>& buf, uint32_t);
inline void Write(Buffer<MostSignificantFirst>& buf, uint64_t);
template <typename U>
inline void
WriteBlock(Buffer<MostSignificantFirst>& buf,
U const* data, unsigned int elements);
inline void
WriteBlock(Buffer<MostSignificantFirst>& buf,
uint8_t const* data, unsigned int elements);
inline void
WriteBlock(Buffer<MostSignificantFirst>& buf,
int8_t const* data, unsigned int elements);
};
template <typename W>
class Buffer
{
unsigned int bufsize_;
unsigned int vsize_;
unsigned int index_;
unsigned int recIndex_;
#if defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined
(__WIN32__) || defined(__CYGWIN__)
DWORD minBytesAvailable_;
#else
unsigned int minBytesAvailable_;
#endif
unsigned int bytesRemainingInMsg_;
unsigned char* buf_;
W writer_;
public:
#if defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined
(__WIN32__) || defined(__CYGWIN__)
#ifdef EE_FILEIO
HANDLE sock_;
#else
SOCKET sock_;
#endif
#else
int sock_;
#endif
explicit Buffer(unsigned int bufsize) :
bufsize_(bufsize), vsize_(0), index_(0), recIndex_(0),
minBytesAvailable_(0), bytesRemainingInMsg_(0)
{
if (bufsize_ < 8) {
throw failure("buffer size too small");
}
buf_ = new unsigned char[bufsize_];
}
~Buffer()
{
delete [] buf_;
}
#if defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined
(__WIN32__) || defined(__CYGWIN__)
void
PersistentWrite(void const* data, int len)
{
char* d2 = (char*) data;
#ifdef EE_FILEIO
DWORD bytesWritten = 0;
BOOL res = WriteFile(sock_, d2, len, &bytesWritten, NULL);
if (!res) {
throw failure("PersistentWrite -- WriteFile");
}
#else
int rc;
while ((rc = send(sock_, d2, len, 0)) != len) {
if (rc < 0) {
throw failure("PersistentWrite -- send");
}
len -= rc;
d2 += rc;
}
#endif
}
void
PersistentRead(void* data, int len)
{
char* d2 = (char*) data;
#ifdef EE_FILEIO
DWORD bytesRead = 0;
BOOL res = ReadFile(sock_, d2, len, &bytesRead, NULL);
if (!res) {
throw failure("PersistentRead -- ReadFile");
}
#else
int rc;
while ((rc = recv(sock_, d2, len, 0)) != len) {
if (rc < 0) {
} else {
if (rc == 0) {
}
len -= rc;
d2 += rc;
}
}
#endif
}
#else
void
PersistentWrite(void const* data, int len)
{
int rc;
unsigned char const* d2 = static_cast<unsigned char const*>
(data);
while ((rc = write(sock_, d2, len)) != len) {
if (rc == -1) {
throw failure("Buffer:
ersistentWrite -- write");
}
len -= rc;
d2 += rc;
}
}
void
PersistentRead(void* data, int len)
{
int rc;
unsigned char* d2 = static_cast<unsigned char*> (data);
while ((rc = read(sock_, d2, len)) != len) {
if (rc < 0) {
throw failure("Buffer:
ersistentRead-- read -- rc == -1");
}
else {
if (rc == 0) {
throw failure("Buffer:
ersistentRead-- read -- rc == 0");
}
len -= rc;
d2 += rc;
}
}
}
#endif
void
SendStoredData()
{
if (index_ > 0) {
PersistentWrite(buf_, index_);
index_ = 0;
}
}
void
Resize(unsigned int newsize)
{
if (newsize < index_) {
SendStoredData();
}
unsigned char* tmp = new unsigned char[newsize];
memcpy(tmp, buf_, index_);
delete [] buf_;
buf_ = tmp;
bufsize_ = newsize;
}
void
Receive(void const* data, unsigned int dlen)
{
if (dlen > bufsize_ - index_) {
#ifdef UDP
memcpy(buf_ + index_, data, bufsize_ - index_);
data += bufsize_ - index_;
dlen -= bufsize_ - index_;
#endif
SendStoredData();
if (dlen > bufsize_) {
PersistentWrite(data, dlen);
return;
}
}
memcpy(buf_ + index_, data, dlen);
index_ += dlen;
}
void
Put(unsigned char byte)
{
if (index_ >= bufsize_) {
SendStoredData();
}
buf_[index_] = byte;
++index_;
}
void
Receive(uint8_t value)
{
Put(value);
}
void
Receive(uint16_t value)
{
writer_.Write(*this, value);
}
void
Receive(uint32_t value)
{
writer_.Write(*this, value);
}
void
Receive(uint64_t value)
{
writer_.Write(*this, value);
}
void
Receive(int8_t value)
{
uint8_t tmp = value;
Put(tmp);
}
void
Receive(int16_t value)
{
uint16_t tmp = value;
writer_.Write(*this, tmp);
}
void
Receive(int32_t value)
{
uint32_t tmp = value;
writer_.Write(*this, tmp);
}
void
Receive(int64_t value)
{
uint64_t tmp = value;
writer_.Write(*this, tmp);
}
void
Receive(float value)
{
uint32_t tmp;
memcpy(&tmp, &value, sizeof tmp);
writer_.Write(*this, tmp);
}
void
Receive(double value)
{
uint64_t tmp;
memcpy(&tmp, &value, sizeof tmp);
writer_.Write(*this, tmp);
}
template <typename T>
void
ReceiveBlock(T const* data, unsigned int elements)
{
writer_.WriteBlock(*this, data, elements);
}
#if defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined
(__WIN32__) || defined(__CYGWIN__)
void
Give(void* address, unsigned int len)
{
using std::min;
if (len > bytesRemainingInMsg_) {
throw failure("Buffer::Give -- len > bytesRemainingInMsg_");
}
char * addr = static_cast<char*> (address);
if (vsize_ > recIndex_) {
if (len <= (vsize_ - recIndex_)) {
memcpy(addr, buf_ + recIndex_, len);
recIndex_ += len;
}
else {
memcpy(addr, buf_ + recIndex_, vsize_ - recIndex_);
addr += (vsize_ - recIndex_);
len -= (vsize_ - recIndex_);
bytesRemainingInMsg_ -= (vsize_ - recIndex_);
recIndex_ = vsize_;
goto pull;
}
}
else {
pull:
int rc = 0;
if (minBytesAvailable_ < len) {
#ifdef EE_FILEIO
minBytesAvailable_ = bufsize_;
#else
if ((rc = ioctlsocket(sock_, FIONREAD, &minBytesAvailable_))
==
SOCKET_ERROR) {
throw failure("Buffer::Give -- ioctl");
}
#endif
}
if (len > bufsize_ || len > minBytesAvailable_) {
PersistentRead(addr, len);
minBytesAvailable_ = 0;
}
else {
vsize_ =
min(min(bufsize_, bytesRemainingInMsg_),
minBytesAvailable_);
minBytesAvailable_ -= vsize_;
#ifdef EE_FILEIO
PersistentRead(buf_, vsize_);
#else
if ((rc = recv(sock_, reinterpret_cast<char*> (buf_), vsize_,
0))
!= vsize_) {
throw failure("Buffer::Give -- recv");
}
#endif
memcpy(addr, buf_, len);
recIndex_ = len;
}
}
bytesRemainingInMsg_ -= len;
}
#else
void
Give(void* address, unsigned int len)
{
using std::min;
if (len > bytesRemainingInMsg_) {
throw failure("Buffer::Give -- len > bytesRemainingInMsg_");
}
char * addr = static_cast<char *> (address);
if (vsize_ > recIndex_) {
if (len <= (vsize_ - recIndex_)) {
memcpy(addr, buf_ + recIndex_, len);
recIndex_ += len;
}
else {
memcpy(addr, buf_ + recIndex_, vsize_ - recIndex_);
addr += (vsize_ - recIndex_);
len -= (vsize_ - recIndex_);
bytesRemainingInMsg_ -= (vsize_ - recIndex_);
recIndex_ = vsize_;
goto pull;
}
}
else {
pull:
int rc = 0;
if (minBytesAvailable_ < len) {
if ((rc = ioctl(sock_, FIONREAD, &minBytesAvailable_)) == -1)
{
throw failure("Buffer::Give -- ioctl");
}
}
if (len > bufsize_ || len > minBytesAvailable_) {
PersistentRead(addr, len);
minBytesAvailable_ = 0;
}
else {
vsize_ =
min(min(bufsize_, bytesRemainingInMsg_),
minBytesAvailable_);
minBytesAvailable_ -= vsize_;
if ((rc = read(sock_, buf_, vsize_)) != vsize_) {
throw failure("Buffer::Give -- read");
}
memcpy(addr, buf_, len);
recIndex_ = len;
}
}
bytesRemainingInMsg_ -= len;
}
#endif
void
Reset()
{
index_ = 0;
recIndex_ = 0;
vsize_ = 0;
minBytesAvailable_ = 0;
bytesRemainingInMsg_ = 0;
}
void
SetMsgLength(unsigned int newMsgLength)
{
bytesRemainingInMsg_ = newMsgLength;
}
private:
Buffer(Buffer const&);
Buffer& operator=(Buffer const&);
};
template <typename U>
void
SameFormat::Write(Buffer<SameFormat>& buf, U data)
{
buf.Receive(&data, sizeof data);
};
template <typename U>
void
SameFormat::WriteBlock(Buffer<SameFormat>& buf,
U const* data, unsigned int elements)
{
buf.Receive(data, elements * sizeof(U));
}
inline void
LeastSignificantFirst::Write(Buffer<LeastSignificantFirst>& buf,
uint16_t value)
{
buf.Put( (value ) & 0xFF );
buf.Put( (value >> 8) & 0xFF );
}
inline void
LeastSignificantFirst::Write(Buffer<LeastSignificantFirst>& buf,
uint32_t value)
{
buf.Put( (value ) & 0xFF );
buf.Put( (value >> 8) & 0xFF );
buf.Put( (value >> 16) & 0xFF );
buf.Put( (value >> 24) & 0xFF );
}
inline void
LeastSignificantFirst::Write(Buffer<LeastSignificantFirst>& buf,
uint64_t value)
{
buf.Put( (value ) & 0xFF );
buf.Put( (value >> 8) & 0xFF );
buf.Put( (value >> 16) & 0xFF );
buf.Put( (value >> 24) & 0xFF );
buf.Put( (value >> 32) & 0xFF );
buf.Put( (value >> 40) & 0xFF );
buf.Put( (value >> 48) & 0xFF );
buf.Put( (value >> 56) & 0xFF );
}
template <typename U>
inline void
LeastSignificantFirst::WriteBlock(Buffer<LeastSignificantFirst>& buf,
U const* data, unsigned int
elements)
{
for (unsigned int ii = 0; ii < elements; ++ii) {
buf.Receive(*(data + ii));
}
}
// Two overloads for when U is uint8_t or int8_t
inline void
LeastSignificantFirst::WriteBlock(Buffer<LeastSignificantFirst>& buf,
uint8_t const* data, unsigned int
elements)
{
buf.Receive(data, elements);
}
inline void
LeastSignificantFirst::WriteBlock(Buffer<LeastSignificantFirst>& buf,
int8_t const* data, unsigned int
elements)
{
buf.Receive(data, elements);
}
inline void
MostSignificantFirst::Write(Buffer<MostSignificantFirst>& buf,
uint16_t value)
{
buf.Put( (value >> 8) & 0xFF );
buf.Put( (value ) & 0xFF );
}
inline void
MostSignificantFirst::Write(Buffer<MostSignificantFirst>& buf,
uint32_t value)
{
buf.Put( (value >> 24) & 0xFF );
buf.Put( (value >> 16) & 0xFF );
buf.Put( (value >> 8) & 0xFF );
buf.Put( (value ) & 0xFF );
}
inline void
MostSignificantFirst::Write(Buffer<MostSignificantFirst>& buf,
uint64_t value)
{
buf.Put( (value >> 56) & 0xFF );
buf.Put( (value >> 48) & 0xFF );
buf.Put( (value >> 40) & 0xFF );
buf.Put( (value >> 32) & 0xFF );
buf.Put( (value >> 24) & 0xFF );
buf.Put( (value >> 16) & 0xFF );
buf.Put( (value >> 8) & 0xFF );
buf.Put( (value ) & 0xFF );
}
template <typename U>
inline void
MostSignificantFirst::WriteBlock(Buffer<MostSignificantFirst>& buf,
U const* data, unsigned int
elements)
{
for (unsigned int ii = 0; ii < elements; ++ii) {
buf.Receive(*(data + ii));
}
}
inline void
MostSignificantFirst::WriteBlock(Buffer<MostSignificantFirst>& buf,
uint8_t const* data, unsigned int
elements)
{
buf.Receive(data, elements);
}
inline void
MostSignificantFirst::WriteBlock(Buffer<MostSignificantFirst>& buf,
int8_t const* data, unsigned int
elements)
{
buf.Receive(data, elements);
}
#endif
The names of some of the functions are likely to change.
I've just taken the fast route in putting this together.
The performance of this version is better than the non-
template version, but the improvement looks to be minor.
An executable built with the new version is significantly
smaller than it formerly was.
This new version isn't available on line yet. In the
past we added an option that allows users to specify
whether they want the code in header-only (integrated)
form or as two separate files. This was in part
because of some comments made by Kanze about build
times I think. Any thoughts on how to reconcile this
template use with that support? I'm not sure if it's
possible to keep supporting the separate header and
implementation approach with these changes.
Brian Wood
http://webEbenezer.net