summaryrefslogtreecommitdiff
path: root/comm/MsgBuffer.cc
diff options
context:
space:
mode:
authorchatty1993-04-07 11:50:31 +0000
committerchatty1993-04-07 11:50:31 +0000
commitba066c34dde204aa192d03a23a81356374d93731 (patch)
tree39391f6235d2cf8a59a0634ac5ea430cdd21f5d4 /comm/MsgBuffer.cc
parent05ab076e1c2a9ca16472f9a6b47b8d22914b3783 (diff)
downloadivy-league-ba066c34dde204aa192d03a23a81356374d93731.zip
ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.gz
ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.bz2
ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.xz
Initial revision
Diffstat (limited to 'comm/MsgBuffer.cc')
-rw-r--r--comm/MsgBuffer.cc707
1 files changed, 707 insertions, 0 deletions
diff --git a/comm/MsgBuffer.cc b/comm/MsgBuffer.cc
new file mode 100644
index 0000000..0aefd6c
--- /dev/null
+++ b/comm/MsgBuffer.cc
@@ -0,0 +1,707 @@
+/*
+ * The Unix Channel
+ *
+ * by Michel Beaudouin-Lafon
+ *
+ * Copyright 1990-1993
+ * Laboratoire de Recherche en Informatique (LRI)
+ *
+ * Messages, buffers
+ *
+ * $Id$
+ * $CurLog$
+ */
+
+#include "MsgBuffer.h"
+#include "Message.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+
+// management of buffers
+// The buffer is said to be allocated if Begin is non zero.
+// There is a special constructor that builds an allocated buffer with Begin = 0 :
+// UchMsgBuffer (UchMsgBuffer&, int len)
+// This buffer can only be read from and flushed
+// This is useful for creating a fake buffer that is actually a pointer to
+// a subpart of the original one. Thus, the new buffer can be safely read from
+// without eating too far away
+//
+
+/*?class UchMsgBuffer
+A \typ{UchMsgBuffer} is an array of bytes of a given size that can be partially filled.
+The buffer may grow whenever bytes are appended to it.
+The initial size, the increments and the maximum sizes can be defined.
+If the buffer exceeds its maximal size, it will be deleted when it becomes empty
+in order to give it a smaller size.
+
+Data can be appended and extracted from the buffer by \fun{operator <<} and \fun{operator >>}.
+When extracting data, the buffer may not contain enough bytes.
+We say that a ``get error'' has occurred and the buffer remembers it until it is modified
+by the functions \fun{Append} or \fun{More}, or by \fun{operator <<}.
+
+The class \typ{^{pUchMsgBuffer}} implements smart pointers to buffers.
+Smart pointers behave like pointers but they manage a reference count on the
+pointed to objects for an automatic reclaim of dynamically allocated objects.
+
+\fig{buffer}{Some operations on a buffer.}
+?*/
+
+static int padding [] = { 0, 1, 2, 3};
+inline int pad (int sz) { return sz + padding [sz & 03]; }
+
+/*?
+Construct an empty buffer.
+The buffer will be allocated with a default size whenever data is appended to it.
+This default size is currently 128 bytes.
+?*/
+UchMsgBuffer :: UchMsgBuffer ()
+{
+ Begin = Start = Stop = End = 0;
+ GetErr = FALSE;
+ GrowSize = MinSize = 128;
+ MaxSize = 0;
+}
+
+/*?
+Construct a buffer with \var{sz} bytes.
+?*/
+UchMsgBuffer :: UchMsgBuffer (int sz)
+{
+ MinSize = GrowSize = sz = pad (sz);
+ Begin = Start = Stop = new byte [sz];
+ End = Begin + sz;
+ GetErr = FALSE;
+ MaxSize = 0;
+}
+
+/*?
+Construct a buffer with minimum \var{sz} bytes, growing by \var{grow} bytes,
+until a maximum size of \var{max} bytes.
+?*/
+UchMsgBuffer :: UchMsgBuffer (int sz, int grow, int max)
+{
+ MinSize = sz = pad (sz);
+ Begin = Start = Stop = new byte [sz];
+ End = Begin + sz;
+ GetErr = FALSE;
+ GrowSize = grow;
+ MaxSize = max;
+}
+
+/*?
+Construct a so called fake buffer, i.e. a buffer whose contents is made of the
+first \var{l} characters of buffer \var{b} (or the whole buffer if \var{l} is zero).
+The contents of the buffers is actually shared until the fake buffer is destructed
+or data is appended to it.
+Fake buffers are mainly used to extract data without exceeding a given size and
+without making a copy.
+Note that the contents of the fake buffer can be overwritten if you read and then append to
+the original buffer.
+The rule of thumb is to avoid modifying the original buffer while there is a fake buffer on it.
+?*/
+UchMsgBuffer :: UchMsgBuffer (const UchMsgBuffer& b, int l)
+{
+ Begin = 0;
+ Start = b.Start;
+ if (l && Start + l <= b.Stop)
+ End = Stop = Start + l;
+ else
+ End = Stop = b.Stop;
+ GetErr = FALSE;
+ GrowSize = b.GrowSize;
+ MinSize = b.MinSize;
+ MaxSize = b.MaxSize;
+}
+
+/*?nodoc?*/
+UchMsgBuffer :: ~UchMsgBuffer ()
+{
+ Delete ();
+}
+
+/*?
+Delete a buffer, i.e. free its internal byte buffer.
+The buffer can still be used after this function has been called (it will reallocate the byte buffer).
+The destructor calls this function.
+?*/
+void
+UchMsgBuffer :: Delete ()
+{
+ if (Begin)
+ delete Begin;
+ Begin = Start = Stop = End = 0;
+}
+
+/*?
+Insure that a UchMsgBuffer.has at least \var{sz} free bytes.
+The buffer may be reallocated with a new size to provide the necessary space.
+?*/
+void
+UchMsgBuffer :: NeedSize (int sz)
+{
+ // brand new buffer
+ if (! Begin) {
+ if (End) // ! Begin && End => fake buffer
+ return;
+ if ((sz = pad (sz)) < MinSize)
+ sz = MinSize;
+ Begin = Start = Stop = new byte [sz];
+ End = Begin + sz;
+ return;
+ }
+
+ // if enough space, do nothing
+ if (End - Stop >= sz)
+ return;
+
+ // try to compact
+ int l = Stop - Start;
+ if (l && (Start - Begin) + (End - Stop) >= sz) {
+ memcpy (Begin, Start, l);
+ Start = Begin;
+ Stop = Start + l;
+ return;
+ }
+
+ // sigh! allocate new
+ sz = pad (sz + ((l < GrowSize) ? GrowSize : l));
+ byte* b = new byte [sz];
+ if (l)
+ memcpy (b, Start, l);
+ delete Begin;
+ Begin = Start = b;
+ Stop = Start + l;
+ End = Begin + sz;
+}
+
+/*?
+Discard the \var{n} first bytes of a buffer, or the whole buffer if \var{n} is negative.
+If the buffer becomes empty and had exceeded its maximum size, it is reset.
+?*/
+void
+UchMsgBuffer :: Flush (int n)
+{
+//printf ("UchMsgBuffer %x : Flush (%d)\n", this, n);
+ if (! Begin)
+ if (! End) // ! Begin && End => fake buffer
+ return;
+ if (n < 0 || Start + n >= Stop) {
+ Start = Stop = Begin;
+ // free if too big
+ if (MaxSize > 0 && End - Start >= MaxSize)
+ Delete ();
+ } else
+ Start += n;
+//printf ("flushed\n");
+}
+
+/*?
+Flush the buffer until \var{p}.
+This can be used when the UchMsgBuffer.has been accessed through \fun{Buffer ()}
+and you want to flush until a given position in the buffer.
+?*/
+void
+UchMsgBuffer :: Flush (byte* p)
+{
+ if (p >= Start && p <= Stop)
+ Flush (p - Start);
+}
+
+/*?
+Set the minimum, increment and maximum size of a buffer (in bytes).
+If an argument is negative, the corresponding size is not changed.
+If an argument is zero, a default value is used:
+128 for minimum and increment, 0 for maximum.
+Sizes are rounded to the nearest multiple of 4.
+If the maximum size is 0, there is no upper bound on the size of the buffer.
+Note that the buffer can always grow as big as necessary to hold the appended data;
+the maximum size is only used to reallocate the buffer whenever it becomes empty.
+?*/
+void
+UchMsgBuffer :: SetSizes (int min, int grow, int max)
+{
+ if (min >= 0)
+ MinSize = pad (min);
+ if (grow >= 0)
+ GrowSize = pad (grow);
+ if (max >= 0)
+ MaxSize = pad (grow);
+ if (min == 0)
+ MinSize = 128;
+ if (grow == 0)
+ GrowSize = 128;
+}
+
+//----- append operations
+//
+
+/*?
+Append the \var{n} first bytes of \var{buf} to the buffer.
+All append functions call \fun{NeedSize} so that appending always succeeds.
+?*/
+void
+UchMsgBuffer :: Append (const byte* buf, int n)
+{
+ GetErr = FALSE;
+ if (! Begin || End - Stop < n)
+ NeedSize (n);
+ memcpy (Stop, buf, n);
+ Stop += n;
+}
+
+/*?nextdoc?*/
+void
+UchMsgBuffer :: Append (byte b)
+{
+ GetErr = FALSE;
+ if (! Begin || End - Stop < 1)
+ NeedSize (1);
+ *Stop++ = b;
+}
+
+#ifdef DOC
+
+/*?nextdoc?*/
+void
+UchMsgBuffer :: Append (char c)
+{ }
+
+/*?nextdoc?*/
+void
+UchMsgBuffer :: Append (lword l)
+{ }
+
+/*?
+Append a short word, a long word, a byte or a character to a buffer.
+The buffer is extended as necessary.
+?*/
+void
+UchMsgBuffer :: Append (sword s)
+{ }
+
+#endif /* DOC */
+
+/*?
+Append a string to a buffer.
+If the second argument is \var{TRUE}, the terminating null byte is appended, else it is not.
+If the string is the null pointer, nothing is appended to the buffer.
+?*/
+void
+UchMsgBuffer :: Append (const char* s, bool nullbyte)
+{
+ if (! s)
+ return;
+ GetErr = FALSE;
+ int l = strlen (s) + (nullbyte ? 1 : 0);
+ if (! Begin || End - Stop < l)
+ NeedSize (l);
+ memcpy (Stop, s, l);
+ Stop += l;
+}
+
+/*?
+Append a message \var{msg} to the buffer.
+The virtual function \fun{WriteTo} of the buffer is called.
+The length of the message is automatically computed and prepended to the message itself.
+?*/
+void
+UchMsgBuffer :: Append (UchMessage& msg)
+{
+ GetErr = FALSE;
+
+ // reserve space for length
+ if (! Begin || End - Stop < lwsize)
+ NeedSize (lwsize);
+ // save current position (saving Start is wrong because it may move)
+ lword l = Stop - Start;
+ // skip space for length (this means that the length includes the 4 bytes of the length itself)
+ Stop += lwsize;
+
+ // append message to buffer
+ msg.WriteTo (*this);
+
+ // now patch the buffer ...
+ // retrieve position
+ byte* p = Start + l;
+ // compute length
+ lword len = (Stop - Start) - l;
+ // and patch
+ memcpy (p, &len, lwsize);
+}
+
+//----- get operations
+//
+
+/*?
+Extract exactly \var{n} bytes from the buffer and copy them into \var{b}.
+Return TRUE if the bytes were extracted, else return FALSE and mark the buffer with the get error flag.
+If \var{peek} is TRUE, the buffer is not flushed after extracting the data
+(i.e. the extracted data is still in the buffer).
+?*/
+bool
+UchMsgBuffer :: Get (byte* b, int n, bool peek)
+{
+//printf ("UchMsgBuffer %x : Get %d bytes\n", this, n);
+ if (GetErr)
+ return FALSE;
+ if (Stop - Start < n) {
+ GetErr = TRUE;
+//printf ("Get failed : %d bytes\n", Stop - Start);
+ return FALSE;
+ }
+ memcpy (b, Start, n);
+ if (! peek)
+ Flush (n);
+ return TRUE;
+}
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Get (byte* b, bool peek)
+{
+//printf ("UchMsgBuffer %x : Get byte\n", this);
+ if (GetErr)
+ return FALSE;
+ if (Stop - Start < 1) {
+ GetErr = TRUE;
+//printf ("Get failed : %d bytes\n", Stop - Start);
+ return FALSE;
+ }
+ *b = *Start;
+ if (! peek)
+ Flush (1);
+ return TRUE;
+}
+
+#ifdef DOC
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Get (char* c, bool peek)
+{
+}
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Get (sword* s, bool peek)
+{ }
+
+/*?
+Extract a long word, a short word, a byte, or a character from the buffer into the argument.
+Return TRUE if the data was actually extracted, else return FALSE and mark the buffer with the get error flag.
+If \var{peek} is TRUE, the buffer is not flushed after extracting the data.
+?*/
+bool
+UchMsgBuffer :: Get (lword* l, bool peek)
+{
+}
+
+#endif /* DOC */
+
+/*?nextdoc?*/
+int
+UchMsgBuffer :: Get (char* str, int n, char delim, bool peek)
+{
+ char ds [2];
+ ds [0] = delim;
+ ds [1] = 0;
+ return Get (str, n, ds, peek);
+}
+
+/*?
+Get at most \var{n} characters from the buffer into \var{str}.
+If \var{n} is negative, no limit other than the size of the buffer
+is imposed.
+If \var{delim} is non null, it is a delimiter or a string of delimiters:
+the buffer is transferred until a delimiter is encountered.
+A null character always terminates the transfer.
+If \var{peek} is TRUE, the buffer is not flushed after extracting the data.
+The number of characters actually transferred is returned.
+\var{str} is always terminated by a null character.
+?*/
+int
+UchMsgBuffer :: Get (char* str, int n, const char* delim, bool peek)
+{
+ if (GetErr)
+ return 0;
+ if (n < 0 || Stop - Start < n)
+ n = Stop - Start;
+ if (! n)
+ return 0;
+
+ int i = 0;
+ char*q = str;
+ for (char* p = (char*) Start; *p && n; n--, i++) {
+ if (delim) {
+ for (const char *s = delim; *s; s++)
+ if (*s == *p)
+ break;
+ if (*s)
+ break;
+ }
+ *q++ = *p++;
+ }
+ *q = '\0';
+ if (! peek)
+ Flush (i);
+ return i;
+}
+
+/*?
+Get a message from the buffer.
+The message must be prefixed by its length, as done by \fun{Append(UchMessage*)}.
+The virtual function \fun{ReadFrom} of the message is called to convert the buffer into a message.
+If there is not enough data in the buffer, FALSE is returned.
+If \fun{ReadFrom} reads past the end of the message,
+FALSE is returned and the get error flag is set.
+?*/
+bool
+UchMsgBuffer :: Get (UchMessage* msg)
+{
+ if (GetErr || (Stop - Start < lwsize))
+ return FALSE;
+
+ // get length
+ lword l = Stop - Start;
+ lword len = 0;
+ memcpy ((byte*) &len, Start, lwsize);
+
+ // check that buffer is big enough
+ if (l < len)
+ return FALSE;
+
+ // read from buffer
+ Start += lwsize;
+ msg->ReadFrom (*this, len);
+
+ // align to length
+ lword rl = l - (Stop - Start);
+ if (rl == len) {
+ return TRUE;
+ } else
+ if (rl < len) {
+ Flush ((int) len - (int) rl);
+ return TRUE;
+ } else {
+ GetErr = TRUE;
+ return FALSE;
+ }
+}
+
+//------ peek operations
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Peek (byte* b, lword offset)
+{
+ if (Stop - Start < offset + 1)
+ return FALSE;
+ *b = *(Start + offset);
+ return TRUE;
+}
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Peek (sword* sw, lword offset)
+{
+ if (Stop - Start < offset + swsize)
+ return FALSE;
+ memcpy (sw, Start + offset, swsize);
+ return TRUE;
+}
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Peek (lword* lw, lword offset)
+{
+ if (Stop - Start < offset + lwsize)
+ return FALSE;
+ memcpy (lw, Start + offset, lwsize);
+ return TRUE;
+}
+
+/*?
+These functions extract a byte, a short word, a long word, a byte string
+at \var{offset} bytes from the start of the buffer.
+They return FALSE if the data to peek falls outside the buffer, else TRUE.
+?*/
+bool
+UchMsgBuffer :: Peek (int sz, byte* b, lword offset)
+{
+ if (Stop - Start < offset + sz)
+ return FALSE;
+ memcpy (b, Start + offset, sz);
+ return TRUE;
+}
+
+#ifdef DOC
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: MsgPeek (byte* b, lword offset)
+{ }
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: MsgPeek (sword* sw, lword offset)
+{ }
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: MsgPeek (lword* lw, lword offset)
+{ }
+
+/*?
+These function are similar to the \fun{Peek} functions;
+they must be used when the buffer contains a message:
+a message is always prefixed by its length, thus changing the origin of the offset.
+Note that the length itself can be extracted with \fun{Peek(lword*, 0)}.
+?*/
+bool
+UchMsgBuffer :: MsgPeek (int sz, byte* b, lword offset)
+{ }
+
+#endif /* DOC */
+
+//----- miscellaneous
+//
+
+/*?
+Extend the buffer's contents by \var{n} bytes.
+This function is useful when data has been appended to the buffer without
+using one of the \fun{Append} functions.
+For instance, to read \var{n} bytes from a Unix file descriptor into a buffer,
+you would do:
+\begin{ccode}
+b.NeedSize (n);
+int nb = read (fd, (char*) Free (), n);
+if (nb > 0)
+ b.More (n);
+\end{ccode}
+?*/
+void
+UchMsgBuffer :: More (int n)
+{
+ GetErr = FALSE;
+ if (End) // Begin == 0 for fake buffers
+ Stop += n;
+}
+
+/*?
+Discard the \var{n} last bytes of the buffer.
+This is useful to remove erroneous data that has just been appended to the buffer.
+This is in some sense the reverse operation of \fun{More}.
+?*/
+void
+UchMsgBuffer :: Discard (int n)
+{
+ if (! Begin)
+ if (! End) // ! Begin && End => fake buffer
+ return;
+ if (Stop - n <= Start) {
+ Start = Stop = Begin;
+ // free if too big
+ if (MaxSize > 0 && End - Start >= MaxSize)
+ Delete ();
+ } else
+ Stop -= n;
+
+}
+
+#ifdef DOC
+// fake entries for inline functions
+
+/*?nextdoc?*/
+byte*
+UchMsgBuffer :: Buffer ()
+{ }
+
+/*?
+Return the address of the beginning of the buffer and the length of the buffer.
+This is used in conjunction with \fun{Flush} to extract data from the buffer, as in the following example:
+\begin{ccode}
+byte* p = b.Buffer ();
+int n = b.BufLength ();
+n = write (fd, (const char*) p, n);
+if (n > 0)
+ b.Flush (n);
+\end{ccode}
+?*/
+int
+UchMsgBuffer :: BufLength ()
+{ }
+
+/*?nextdoc?*/
+byte*
+UchMsgBuffer :: Free ()
+{ }
+
+/*?
+Return the address of the first free byte in the buffer and the number of free bytes.
+This may be used in conjunction with \fun{NeedSize} and \fun{More}
+to append data to the buffer.
+?*/
+int
+UchMsgBuffer :: FreeLength ()
+{ }
+
+/*?
+Grow the buffer if its free size is less than its grow size.
+This function can be called when a buffer is full.
+?*/
+void
+UchMsgBuffer :: Grow ()
+{ }
+
+/*?
+This operator is equivalent to the \fun{Append} operations.
+The type of the data can be a long word, a short word, a byte, a character, or a string;
+for strings, the null character is included.
+The operator returns the buffer so that several items can be added.
+?*/
+UchMsgBuffer&
+UchMsgBuffer :: operator << (type data)
+{ }
+
+/*?nextdoc?*/
+UchMsgBuffer&
+UchMsgBuffer :: operator >> (type* data)
+{ }
+
+/*?
+This operator is equivalent to the \fun{Get} operations.
+The type of the data can be a long word, a short word, a byte, or a character.
+The operator returns the buffer so that several items can be read.
+Errors can be tested by the functions \fun{Error} and \fun{Ok}.
+?*/
+UchMsgBuffer&
+UchMsgBuffer :: operator >> (type& data)
+{ }
+
+/*?nextdoc?*/
+bool
+UchMsgBuffer :: Error ()
+{ }
+
+/*?
+Test the state of the buffer, as set by the get functions.
+\fun{Error} returns TRUE if a get error has occurred.
+\fun{Ok} is the negation of \fun{Error}.
+?*/
+bool
+UchMsgBuffer :: Ok ()
+{ }
+
+/*?
+Reset the error flag.
+The error flag is automatically reset by the functions \fun{Append} and \fun{More},
+and by \fun{operator <<}.
+?*/
+void
+UchMsgBuffer :: ResetError ()
+{ }
+
+#endif /* DOC */
+