src/Network/Buffer.hh
changeset 200 2dbf40661580
child 284 27ce69fd1e06
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Network/Buffer.hh	Thu Dec 04 21:59:23 2008 +0000
@@ -0,0 +1,150 @@
+#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