xdrbuf.cpp

Go to the documentation of this file.
00001 /*
00002  * xdrbuf.cpp
00003  *
00004  * Copyright (c) 2008,2009 Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  * XDR input/output buffers.  See xdrbuf.h
00031  */
00032 
00033 // includes --------------------------------------------------------------------
00034 #include "xdrbuf.h"             // always include our own header first
00035 
00036 #include <string.h>
00037 
00038 #ifdef WIN32
00039 #include "winsock.h"
00040 #else   // WIN32
00041 #include "netinet/in.h"         // for htonl, htons, ntohl, etc
00042 #endif  // WIN32
00043 
00044 #include "common/wave_ex.h"
00045 
00046 
00047 namespace xdrbuf {
00048 
00049 /// \ingroup xdrbuf
00050 /*@{*/
00051 
00052 
00053 // interface destructors
00054 Output::~Output(void) throw() { }
00055 Input::~Input(void) throw() { }
00056 
00057 
00058 static const char s_nameLookup[] = "\0abcdefghijklmnopqrstuvwxyz$*?!=";
00059 
00060 
00061 ////////////////////////////////////////////////////////////////////////////////
00062 //
00063 //      static helper methods
00064 //
00065 ////////////////////////////////////////////////////////////////////////////////
00066 
00067 static int
00068 getNameIndex
00069 (
00070 IN char a
00071 )
00072 {
00073         if (a >= 'a' && a <= 'z')
00074                 return a - 'a' + 1;
00075         if (a == '$')
00076                 return 27;
00077         if (a == '*')
00078                 return 28;
00079         if (a == '?')
00080                 return 29;
00081         if (a == '!')
00082                 return 30;
00083         if (a == '=')
00084                 return 31;
00085 
00086         ASSERT(false, "Invalid name: '%c' = %d", a, a);
00087         return 0;
00088 }
00089 
00090 
00091 
00092 class RealPackletHeader : public PackletHeader {
00093 public:
00094         RealPackletHeader(IN word_t& w) { this->setData(w); }
00095 };
00096 
00097 
00098 
00099 static word_t
00100 createPackletHeaderData
00101 (
00102 IN char name,
00103 IN ePackletType type,
00104 IN int size
00105 )
00106 {
00107         if (!isValidPackletName(name)) {
00108                 WAVE_EX(wex);
00109                 wex << "Invalid packlet name: " << name;
00110         }
00111         if (type < 1 || type > 5) {
00112                 WAVE_EX(wex);
00113                 wex << "Invalid packlet type: " << type;
00114         }
00115         if (size < 0 || size > 255) {
00116                 WAVE_EX(wex);
00117                 wex << "Invalid packlet data size: " << size;
00118         }
00119 
00120         word_t p;       // this is the packlet data
00121 
00122         // first, add the type
00123         p = type;
00124         p = p << 5;     // make room for name (5 bits)
00125 
00126         // next, add the name
00127         int i = getNameIndex(name);
00128         p += (word_t) i;
00129         p = p << 8;     // make room for size (8 bits)
00130 
00131         // now add the element count
00132         p += (word_t) size;
00133 
00134         //DPRINTF("Created packlet:");
00135         //DPRINTF("  name: '%c'", name);
00136         //DPRINTF("  type: %d", type);
00137         //DPRINTF("  size: %d", size);
00138         //DPRINTF("  result: 0x%02x", p);
00139 
00140         //getPackletName(p);
00141         //getPackletType(p);
00142         //getPackletDataCount(p);
00143 
00144         return p;
00145 }
00146 
00147 
00148 ////////////////////////////////////////////////////////////////////////////////
00149 //
00150 //      OutBuf -- class that implements the Output interface
00151 //
00152 ////////////////////////////////////////////////////////////////////////////////
00153 
00154 class OutBuf : public Output {
00155 public:
00156         OutBuf(void) throw();
00157         ~OutBuf(void) throw();
00158 
00159         // public class methods ------------------------------------------------
00160         void initialize(IN int size);
00161 
00162         // Output class interface methods -----------------------------
00163         void clear(void);
00164         int getRemainingBytes(void) const throw();
00165         void openPacklet(IN char name);
00166         void closePacklet(IN char name);
00167         void addStringPacklet(IN char name,
00168                                 IN const char * s,
00169                                 IN int length);
00170         void addFloatPacklet(IN char name,
00171                                 IN const float * data,
00172                                 IN int nFloats);
00173         void addInt32Packlet(IN char name,
00174                                 IN const int32_t * data,
00175                                 IN int nInt32s);
00176         const byte_t * getData(void) throw();
00177         int getDataBytes(void) const throw() { return m_pos; }
00178 
00179 private:
00180         typedef std::vector<byte_t> vec_data_t;
00181         typedef std::vector<char> vec_char_t;
00182 
00183         // private helper methods ----------------------------------------------
00184         void addPacklet(IN word_t p);
00185 
00186         // private member data -------------------------------------------------
00187         int                                     m_pos;  // current position
00188         vec_data_t                              m_data;
00189         vec_char_t                              m_packletStack;
00190         int                                     m_size;
00191 };
00192 
00193 
00194 
00195 OutBuf::OutBuf(void)
00196 throw()
00197 {
00198         m_size = 0;
00199         m_pos = -1;
00200 }
00201 
00202 
00203 
00204 OutBuf::~OutBuf(void)
00205 throw()
00206 {
00207 }
00208 
00209 
00210 
00211 void
00212 OutBuf::initialize
00213 (
00214 IN int bytes
00215 )
00216 {
00217         ASSERT(bytes > 0, "Bad byte size: %d", bytes);
00218 
00219         ASSERT(!m_size, "Already initialized?");
00220         m_size = bytes;
00221 
00222         m_data.reserve(bytes);
00223         m_pos = 0;
00224 }
00225 
00226 
00227 
00228 void
00229 OutBuf::clear
00230 (
00231 void
00232 )
00233 {
00234         ASSERT(m_size > 0, "Bad size: %d", m_size);
00235 
00236         m_pos = 0;
00237         ASSERT(m_size == this->getRemainingBytes(),
00238             "Should have %d bytes remaining!", m_size);
00239         m_packletStack.clear();
00240 }
00241 
00242 
00243 
00244 int
00245 OutBuf::getRemainingBytes
00246 (
00247 void
00248 )
00249 const
00250 throw()
00251 {
00252         ASSERT(m_pos >= 0, "Bad position: %d", m_pos);
00253         return m_size - m_pos;
00254 }
00255 
00256 
00257 
00258 void
00259 OutBuf::openPacklet
00260 (
00261 IN char name
00262 )
00263 {
00264         word_t p = createPackletHeaderData(name, ePacklet_ParentBegin, 0);
00265         this->addPacklet(p);
00266         m_packletStack.push_back(name);
00267 }
00268 
00269 
00270 
00271 void
00272 OutBuf::closePacklet
00273 (
00274 IN char name
00275 )
00276 {
00277         if (!isValidPackletName(name)) {
00278                 WAVE_EX(wex);
00279                 wex << "Invalid packlet name: " << name;
00280         }
00281 
00282         // see what is at the top (end) of the packlet stack
00283         if (m_packletStack.size() == 0) {
00284                 WAVE_EX(wex);
00285                 wex << "Cannot close packlet: nothing open!  ";
00286                 wex << "Attempting to close packlet '" << name << "'";
00287         }
00288 
00289         vec_char_t::iterator i = m_packletStack.end();
00290         --i;
00291         char b = *i;
00292         if (b != name) {
00293                 WAVE_EX(wex);
00294                 wex << "Closing a packlet that isn't open?  ";
00295                 wex << "Packlet '" << b << "' was opened, but packlet '";
00296                 wex << name << "' was closed.";
00297         }
00298 
00299         // okay, matches!
00300         m_packletStack.pop_back();
00301 //      DPRINTF("  stack size: %d", m_packletStack.size());
00302         word_t p = createPackletHeaderData(name, ePacklet_ParentEnd, 0);
00303         this->addPacklet(p);
00304 }
00305 
00306 
00307 
00308 void
00309 OutBuf::addStringPacklet
00310 (
00311 IN char name,
00312 IN const char * s,
00313 IN int length
00314 )
00315 {
00316         word_t p = createPackletHeaderData(name, ePacklet_String, length);
00317         this->addPacklet(p);
00318 
00319         // verify space remaining
00320         ASSERT_THROW(this->getRemainingBytes() >= length,
00321             "Out of room in outbut buffer--cannot add string packlet");
00322 
00323         strncpy((char *) (m_data.data() + m_pos), s, length);
00324         m_pos += length;
00325 }
00326 
00327 
00328 
00329 void
00330 OutBuf::addFloatPacklet
00331 (
00332 IN char name,
00333 IN const float * data,
00334 IN int nFloats
00335 )
00336 {
00337         word_t p = createPackletHeaderData(name, ePacklet_Floats, nFloats);
00338         this->addPacklet(p);
00339 
00340         // verify space remaining
00341         int bytes_needed = nFloats * sizeof(float);
00342         ASSERT_THROW(sizeof(dword_t) == sizeof(float),
00343             "Our encoding assumes long and float are same size!");
00344         ASSERT_THROW(this->getRemainingBytes() >= bytes_needed,
00345             "out of room in output buffer--cannot add float packlet");
00346 
00347         for (int i = 0; i < nFloats; ++i) {
00348 
00349                 // This is the only place I miss XDR!
00350                 // I have to convert local floats into something that
00351                 //   can be read over the network, and then converted
00352                 //   back on another host with a different architecture.
00353                 // You can see the IEEE floating point standard here:
00354                 //   http://en.wikipedia.org/wiki/Floating_point
00355                 // However, I'm not going to do a custom encoding of
00356                 //   sign, mantissa, exponent.  Instead, we just
00357                 //   follow proper endian-ness and hope other OS's
00358                 //   use the same standard.  This can be updated over
00359                 //   time to be more robust.
00360                 dword_t in;
00361                 memcpy(&in, data + i, sizeof(dword_t));
00362                 dword_t out = htonl(in);        // convert to network byte order
00363 
00364                 memcpy(m_data.data() + m_pos, &out, sizeof(dword_t));
00365                 m_pos += sizeof(dword_t);
00366         }
00367 }
00368 
00369 
00370 
00371 void
00372 OutBuf::addInt32Packlet
00373 (
00374 IN char name,
00375 IN const int32_t * data,
00376 IN int nInt32s
00377 )
00378 {
00379         word_t p = createPackletHeaderData(name, ePacklet_Int32s, nInt32s);
00380         this->addPacklet(p);
00381 
00382         // verify remaining size
00383         int bytes_needed = nInt32s * sizeof(int32_t);
00384         ASSERT_THROW(this->getRemainingBytes() >= bytes_needed,
00385             "out of room in output buffer--cannot add long packlet");
00386 
00387         for (int i = 0; i < nInt32s; ++i) {
00388                 dword_t out = htonl(data[i]);
00389                 memcpy(m_data.data() + m_pos, &out, sizeof(dword_t));
00390                 m_pos += sizeof(dword_t);
00391         }
00392 }
00393 
00394 
00395 
00396 const byte_t *
00397 OutBuf::getData
00398 (
00399 void
00400 )
00401 throw()
00402 {
00403         if (m_packletStack.size()) {
00404                 WAVE_EX(wex);
00405                 wex << "Unclosed packlets?  Stack is not empty";
00406         }
00407 
00408         return m_data.data();
00409 }
00410 
00411 
00412 
00413 void
00414 OutBuf::addPacklet
00415 (
00416 IN word_t p
00417 )
00418 {
00419         // verify there is room!
00420         ASSERT_THROW(this->getRemainingBytes() >= 2,
00421             "Out of room in output buffer--cannot add packlet");
00422 
00423         // convert to network byte order
00424         word_t w = htons(p);
00425         byte_t * b = (byte_t *) &w;
00426 
00427         m_data[m_pos++] = b[0];
00428         m_data[m_pos++] = b[1];
00429 }
00430 
00431 
00432 
00433 ////////////////////////////////////////////////////////////////////////////////
00434 //
00435 //      InBuf -- class that implements the xdrbuf::Input interface
00436 //
00437 ////////////////////////////////////////////////////////////////////////////////
00438 
00439 class InBuf : public Input {
00440 public:
00441         InBuf(void) throw();
00442         ~InBuf(void) throw();
00443 
00444         // public class methods ------------------------------------------------
00445         void initialize(void) { }
00446 
00447         // xdrbuf::Input class interface methods -------------------------------
00448         void reset(IN const byte_t * stream,
00449                                 IN int bytes);
00450         bool endOfStream(void);
00451         PackletHeader getNextPackletHeader(void);
00452         void readInt32s(OUT int32_t * buffer, IN int count);
00453         void readFloats(OUT float * buffer, IN int count);
00454         void readString(OUT char * buffer, IN int count);
00455 
00456 private:
00457         // private helper methods ----------------------------------------------
00458         enum eExpect {
00459                 eExpect_Packlet         = 1,
00460                 eExpect_Data            = 2,
00461                 eExpect_Invalid         = 0
00462         };
00463 
00464         int getRemainingBytes(void) const throw() { return m_bytes - m_pos; }
00465 
00466         // private member data -------------------------------------------------
00467         const byte_t *          m_stream;
00468         eExpect                 m_expect;
00469         ePackletType            m_type;
00470         int                     m_pos;          // current position in input
00471         int                     m_count;
00472         int                     m_bytes;
00473 };
00474 
00475 
00476 
00477 InBuf::InBuf
00478 (
00479 void
00480 )
00481 throw()
00482 {
00483         m_expect = eExpect_Invalid;
00484         m_bytes = 0;
00485         m_count = -1;
00486         m_type = ePacklet_Invalid;
00487         m_stream = NULL;
00488         m_pos = -1;
00489 }
00490 
00491 
00492 
00493 InBuf::~InBuf
00494 (
00495 void
00496 )
00497 throw()
00498 {
00499 }
00500 
00501 
00502 
00503 void
00504 InBuf::reset
00505 (
00506 IN const byte_t * stream,
00507 IN int bytes
00508 )
00509 {
00510         ASSERT(stream, "null");
00511         ASSERT(bytes > 0, "bad byte count: %d", bytes);
00512 
00513         m_stream = stream;
00514         m_bytes = bytes;
00515         m_expect = eExpect_Packlet;
00516         m_type = ePacklet_Invalid;
00517         m_count = -1;
00518         m_pos = 0;
00519 }
00520 
00521 
00522 
00523 bool
00524 InBuf::endOfStream
00525 (
00526 void
00527 )
00528 {
00529         if (!m_stream)
00530                 return true;
00531 
00532         return (m_pos >= m_bytes);
00533 }
00534 
00535 
00536 
00537 PackletHeader
00538 InBuf::getNextPackletHeader
00539 (
00540 void
00541 )
00542 {
00543         if (eExpect_Packlet != m_expect) {
00544                 WAVE_EX(wex);
00545                 wex << "Packlet header is not expected";
00546         }
00547         ASSERT_THROW(this->getRemainingBytes() >= 2,
00548             "Not enough data remaining for another packlet header");
00549 
00550         // convert back from network to host byte order
00551         word_t * pw = (word_t *) (m_stream + m_pos);
00552         word_t w = ntohs(*pw);
00553         m_pos += 2;
00554 
00555         // now cast
00556         PackletHeader * pph = (PackletHeader *) &w;
00557         PackletHeader ph = *pph;
00558 
00559         m_type = ph.getType();
00560         if (ePacklet_Invalid == m_type) {
00561                 WAVE_EX(wex);
00562                 wex << "Invalid packet type";
00563         }
00564 
00565         m_count = ph.getDataCount();
00566         if (ePacklet_ParentBegin == m_type ||
00567             ePacklet_ParentEnd == m_type) {
00568                 m_expect = eExpect_Packlet;
00569         } else {
00570                 m_expect = eExpect_Data;
00571         }
00572 
00573         return ph;
00574 }
00575 
00576 
00577 
00578 void
00579 InBuf::readInt32s
00580 (
00581 OUT int32_t * buffer,
00582 IN int count
00583 )
00584 {
00585         ASSERT(buffer, "null");
00586         ASSERT(count >= 0, "Bad count");
00587 
00588         if (eExpect_Data != m_expect || ePacklet_Int32s != m_type) {
00589                 WAVE_EX(wex);
00590                 wex << "No int32s available for reading!";
00591         }
00592         m_type = ePacklet_Invalid;
00593         m_expect = eExpect_Packlet;
00594 
00595         if (count != m_count) {
00596                 WAVE_EX(wex);
00597                 wex << "Mismatch in packlet/read counts";
00598         }
00599 
00600         // verify we have the data
00601         int bytes_needed = count * sizeof(int32_t);
00602         ASSERT_THROW(this->getRemainingBytes() >= bytes_needed,
00603             "Not enough remaining data for " << count << " longs");
00604 
00605         for (int i = 0; i < count; ++i) {
00606                 dword_t in = *(dword_t *)(m_stream + m_pos);
00607                 m_pos += sizeof(dword_t);
00608                 buffer[i] = ntohl(in);
00609         }
00610 }
00611 
00612 
00613 
00614 void
00615 InBuf::readFloats
00616 (
00617 OUT float * buffer,
00618 IN int count
00619 )
00620 {
00621         ASSERT(buffer, "null");
00622         ASSERT(count >= 0, "Bad count");
00623 
00624         if (eExpect_Data != m_expect || ePacklet_Floats != m_type) {
00625                 WAVE_EX(wex);
00626                 wex << "No floats available for reading!";
00627         }
00628         m_type = ePacklet_Invalid;
00629         m_expect = eExpect_Packlet;
00630 
00631         if (count != m_count) {
00632                 WAVE_EX(wex);
00633                 wex << "Mismatch in packlet/read counts";
00634         }
00635 
00636         // verify there is data to read
00637         int bytes_needed = count * sizeof(float);
00638         ASSERT_THROW(this->getRemainingBytes() >= bytes_needed,
00639             "Not enough remaining data for " << count << " floats");
00640         ASSERT_THROW(sizeof(float) == sizeof(dword_t),
00641             "Our decoding assumes floats and longs are the same size");
00642 
00643         for (int i = 0; i < count; ++i) {
00644                 // see OutBuf::addFloatPacklet about caveats
00645                 dword_t in;
00646                 memcpy(&in, m_stream + m_pos, sizeof(dword_t));
00647                 m_pos += sizeof(dword_t);
00648                 dword_t out = ntohl(in);
00649                 memcpy(buffer + i, &out, sizeof(dword_t));
00650         }
00651 }
00652 
00653 
00654 
00655 void
00656 InBuf::readString
00657 (
00658 OUT char * buffer,
00659 IN int count
00660 )
00661 {
00662         ASSERT(buffer, "null");
00663         ASSERT(count >= 0, "Bad count");
00664 
00665         if (eExpect_Data != m_expect || ePacklet_String != m_type) {
00666                 WAVE_EX(wex);
00667                 wex << "No string availabile for reading!";
00668         }
00669         m_type = ePacklet_Invalid;
00670         m_expect = eExpect_Packlet;
00671 
00672         if (count != m_count) {
00673                 WAVE_EX(wex);
00674                 wex << "Mismatch in packlet/read counts";
00675         }
00676 
00677         // verify we have enough data
00678         ASSERT_THROW(this->getRemainingBytes() >= count,
00679             "Not enough remaining data to read string of size " << count);
00680 
00681         // copy the characters to the provided buffer
00682         strncpy(buffer, (const char *) (m_stream + m_pos), count);
00683         m_pos += count;
00684 }
00685 
00686 
00687 
00688 ////////////////////////////////////////////////////////////////////////////////
00689 //
00690 //      Public APIs
00691 //
00692 ////////////////////////////////////////////////////////////////////////////////
00693 
00694 bool
00695 isValidPackletName
00696 (
00697 IN char a
00698 )
00699 {
00700         if (a >= 'a' && a <= 'z')
00701                 return true;
00702         if (a == ')' || a == '(')
00703                 return true;
00704         if (a == '?' || a == '!')
00705                 return true;
00706         if (a == '=')
00707                 return true;
00708 
00709         return false;
00710 }
00711 
00712 
00713 
00714 char
00715 PackletHeader::getName
00716 (
00717 void
00718 )
00719 const
00720 throw()
00721 {
00722         byte_t b = (byte_t) ((m_data & 0xFF00) >> 8);   // get high byte
00723         int i = b & 0x1F;                               // lowest 5 bits
00724 
00725         //DPRINTF("Packlet 0x%02x --> name 0x%02x", p, i);
00726 
00727         if (i <= 0) {
00728                 DPRINTF("Null or invalid packet name?  %d", i);
00729                 return 0;
00730         }
00731 
00732         if (i > 31) {
00733                 DPRINTF("Index too large? %d", i);
00734                 return 0;
00735         }
00736         return s_nameLookup[i];
00737 }
00738 
00739 
00740 
00741 ePackletType
00742 PackletHeader::getType
00743 (
00744 void
00745 )
00746 const
00747 throw()
00748 {
00749         byte_t b = (byte_t) ((m_data & 0xFF00) >> 8);   // high byte
00750         int i = b & 0xE0;                               // highest 3 bits
00751         i = i >> 5;
00752 
00753         //DPRINTF("Packlet 0x%02x --> type 0x%02x", p, i);
00754 
00755         if (i <= 0) {
00756                 DPRINTF("Null or invalid packlet type? %d", i);
00757                 return ePacklet_Invalid;
00758         }
00759         if (i >= 6) {
00760                 DPRINTF("Invalid packlet type? %d", i);
00761                 return ePacklet_Invalid;
00762         }
00763         return (ePackletType) i;
00764 }
00765 
00766 
00767 
00768 int
00769 PackletHeader::getDataCount
00770 (
00771 void
00772 )
00773 const
00774 throw()
00775 {
00776         byte_t b = (byte_t) (m_data & 0x00FF);          // low byte
00777 
00778         //DPRINTF("Packlet 0x%02x --> count %d == 0x%02x", p, (int) b, b);
00779 
00780         return (int) b;
00781 }
00782 
00783 
00784 
00785 smart_ptr<Output>
00786 Output::create
00787 (
00788 IN int bytes
00789 )
00790 {
00791         ASSERT(bytes > 0, "Bad byte count: %d", bytes);
00792 
00793         smart_ptr<OutBuf> local = new OutBuf;
00794         ASSERT(local, "out of memory");
00795 
00796         local->initialize(bytes);
00797 
00798         return local;
00799 }
00800 
00801 
00802 
00803 smart_ptr<Input>
00804 Input::create
00805 (
00806 void
00807 )
00808 {
00809         smart_ptr<InBuf> local = new InBuf;
00810         ASSERT(local, "out of memory");
00811 
00812         local->initialize();
00813 
00814         return local;
00815 }
00816 
00817 
00818 
00819 };      // xdrbuf namespace
00820