From ba066c34dde204aa192d03a23a81356374d93731 Mon Sep 17 00:00:00 2001 From: chatty Date: Wed, 7 Apr 1993 11:50:31 +0000 Subject: Initial revision --- comm/MsgBuffer.cc | 707 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 707 insertions(+) create mode 100644 comm/MsgBuffer.cc (limited to 'comm/MsgBuffer.cc') 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 +#include +#include + +// 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 */ + -- cgit v1.1