src/Network/Buffer.hh
author terom
Thu, 04 Dec 2008 21:59:23 +0000
changeset 200 2dbf40661580
child 284 27ce69fd1e06
permissions -rw-r--r--
better NetworkBuffer/Packet stuff + some additional Physics+Network stuff + random fixes
#ifndef NETWORK_BUFFER_HH
#define NETWORK_BUFFER_HH

#include "Socket.hh"
#include "../Error.hh"

#include <cassert>

/*
 * Minimum chunk size to avoid handling single bytes at a time (for resize, mainly)
 */
const size_t NETWORK_BUFFER_CHUNK_SIZE = 1024;

class NetworkBufferError : public Error {
    public:
        NetworkBufferError (const std::string &message) : Error(message) { }
};

/*
 * Base buffer operations for uffered socket send/recv
 */
class NetworkBufferBase {
    protected:
        // the socket that we use
        NetworkSocket socket;

        char *buf;
        size_t size, offset;
    
    public:
        NetworkBufferBase (NetworkSocket &socket, size_t size_hint);
        ~NetworkBufferBase (void);
    
    private:
        NetworkBufferBase (const NetworkBufferBase &copy);
        NetworkBufferBase& operator= (const NetworkBufferBase &copy);
    
    protected:
        void resize (size_t item_size);
        void trim (size_t prefix_size);
};

/*
 * Buffered prefix-len socket input
 */
class NetworkBufferInput : public NetworkBufferBase {
    public:
        NetworkBufferInput (NetworkSocket &socket, size_t size_hint);

    private:
        /*
         * Attempts to recv the given number of bytes, returning true on success
         */
        bool try_read (size_t item_size);

        /*
         * Returns true if the buffer contains at least the given amount of data
         */
        bool have_data (size_t data_size);
    
    public:
        /*
         * Attempts to read the length prefix into val_ref, returning true on success
         */
        bool peek_prefix (uint16_t &val_ref);
        bool peek_prefix (uint32_t &val_ref);
        
        /*
         * This attempts to collect the prefix + data into our buffer, and then returns the length and a pointer to the
         * internal memory buffer. Use flush_data when done with the data
         */

        // XXX: template definition moved here from .cc
        template <typename PrefixType> bool peek_data (PrefixType &prefix, char *&buf_ref) {
            size_t missing = 0;
            
            do {    
                // do we have the prefix?
                if (peek_prefix(prefix)) {
                    // do we already have the payload?
                    if (offset >= sizeof(PrefixType) + prefix) {
                        break;

                    } else {
                        missing = (sizeof(PrefixType) + prefix) - offset;
                    }

                } else {
                    missing = sizeof(PrefixType);
                }

                // sanity-check
                // XXX: a zero-prefix will trigger this
                assert(missing);
                
                // try and read the missing data
                if (try_read(missing) == false) {
                    // if unable to read what we need, return zero.
                    return false;
                }
                
                // assess the situation again
            } while (true);
            
            // update the buf_ref to point past the prefix-length
            buf_ref = buf + sizeof(PrefixType);

            // return
            return true;
        }
            
        template <typename PrefixType> void flush_data (void) {
            PrefixType prefix;
            
            // we *must* have a valid prefix
            assert(peek_prefix(prefix));

            // trim the bytes out
            trim(sizeof(PrefixType) + prefix);
        }
};

/*
 * Buffered prefix-len socket output
 */
class NetworkBufferOutput : public NetworkBufferBase {
    public:
        NetworkBufferOutput (NetworkSocket &socket, size_t size_hint);

    private:
        /*
         * If our buffer is empty, fast-path the given buf_ptr directly to send(), else copy the remaining
         * portion to our buffer for later use with flush_write
         */
        void push_write (char *buf_ptr, size_t buf_size);
   
    public:    
        /*
         * Try and send() stuff out of our buffer, or ignore if it's empty
         */
        void flush_write (void);
        
        /*
         * push_write, first the given prefix and then the buf (which contains <prefix> bytes of data)
         */
        void write_prefix (char *buf, uint16_t prefix);
        void write_prefix (char *buf, uint32_t prefix);
};

#endif