/* * The Unix Channel * * by Michel Beaudouin-Lafon * * Copyright 1990-1995 * Laboratoire de Recherche en Informatique (LRI) * * Messages, buffers * * $Id$ * $CurLog$ */ #include "MsgBuffer.h" #include "Message.h" #include "ccu/String.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{FIGURES/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 () : UchIOS () { Begin = Start = Stop = End = 0; GetErr = false; GrowSize = MinSize = 128; MaxSize = 0; } /*? Construct a buffer with \var{sz} bytes. ?*/ UchMsgBuffer :: UchMsgBuffer (int sz) : UchIOS () { 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) : UchIOS () { 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) : UchIOS (b) { 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 () { Clear (); } /*? 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 :: Clear () { 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) Clear (); } else Start += n; //printf ("flushed\n"); } /*? Flush the buffer until \var{p}. This can be used when the buffer 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 :: WriteBuf (const byte* buf, int n) { GetErr = false; if (! Begin || End - Stop < n) NeedSize (n); memcpy (Stop, buf, n); Stop += n; } /*?nextdoc?*/ void UchMsgBuffer :: WriteByte (byte b) { GetErr = false; if (! Begin || End - Stop < 1) NeedSize (1); *Stop++ = b; } /*?nextdoc?*/ void UchMsgBuffer :: WriteChar (char c) { WriteByte ((byte) c); } /*?nextdoc?*/ void UchMsgBuffer :: WriteLong (lword l) { WriteBuf ((const byte*)&l, lwsize); } /*? Append a short word, a long word, a byte or a character to a buffer. The buffer is extended as necessary. ?*/ void UchMsgBuffer :: WriteShort (sword s) { WriteBuf ((const byte*)&s, swsize); } void UchMsgBuffer :: WriteString (const char* s) { Append (s); } /*? 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 :: WriteMsg (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 // bool UchMsgBuffer :: GetBuf (byte* b, int n, bool peek, int offset) { if (GetErr) return false; if (!peek || offset < 0) offset = 0; if (Stop - Start < offset + n) { GetErr = true; return false; } memcpy (b, Start+offset, n); if (! peek) Flush (n); return true; } int UchMsgBuffer :: GetString (char* str, int n, const char* delim, bool peek, int offset) { if (GetErr) return 0; if (!peek || offset < 0) offset = 0; if (n < 0 || Stop - Start < offset + n) n = Stop - Start - offset; if (n < 0) return 0; int i = 0; char* q = str; char* p; for (p = (char*) (Start+offset); *p && n; n--, i++) { if (delim) { const char* s; for (s = delim; *s; s++) if (*s == *p) break; if (*s) break; } *q++ = *p++; } *q = '\0'; /* if we reached the end of a string, flush the null character as well */ if (! peek) Flush (*p ? i : i + 1); return i; } /*?nextdoc?*/ bool UchMsgBuffer :: ReadLong (lword& l) { return GetBuf ((byte*) &l, lwsize, false); } /*?nextdoc?*/ bool UchMsgBuffer :: ReadShort (sword& s) { return GetBuf ((byte*) &s, swsize, false); } /*?nextdoc?*/ bool UchMsgBuffer :: ReadByte (byte& b) { return GetBuf (&b, 1, false); } /*? 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 :: ReadChar (char& c) { return GetBuf ((byte*) &c, 1, false); } /*? Extract a string from a buffer and copy it to a \typ{CcuString}. This assumes that the string in the buffer is null-terminated. ?*/ int UchMsgBuffer :: ReadString (CcuString& s) { if (GetErr) return 0; int n = Stop - Start; byte* p = Start; while (n-- && *p) ++p; int l = p - Start; s.Assign ((const char*) Start, l); Flush (*p ? l : l + 1); return l; } /*? 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 :: ReadBuf (byte* b, int n) { return GetBuf (b, n, false); } /*?nextdoc?*/ int UchMsgBuffer :: ReadString (char* str, char delim, int n) { char ds [2]; ds [0] = delim; ds [1] = 0; return GetString (str, n, ds, false); } /*?nextdoc?*/ int UchMsgBuffer :: ReadString (char* str, int n) { return GetString (str, n, 0, false); } /*? Get at most \var{n} characters from the buffer into \var{str}. \var{delim} (resp. \var{ds} if not null) is a delimiter: the buffer is transferred until a null character or the delimiter is encountered. The number of characters actually transferred is returned. \var{str} is always terminated by a null character. ?*/ int UchMsgBuffer :: ReadString (char* str, const char* ds, int n) { return GetString (str, n, ds, false); } /*? 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 :: ReadMsg (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 :: PeekByte (byte& b, int offset) { return GetBuf (&b, 1, true, offset); } /*?nextdoc?*/ bool UchMsgBuffer :: PeekShort (sword& sw, int offset) { return GetBuf ((byte*) &sw, swsize, true, offset); } /*?nextdoc?*/ bool UchMsgBuffer :: PeekLong (lword& lw, int offset) { return GetBuf ((byte*) &lw, lwsize, true, offset); } /*? These functions extract a byte, a short word, a long word, a byte array 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 :: PeekBuf (byte* b, int n, int offset) { return GetBuf (b, n, true, offset); } int UchMsgBuffer :: PeekString (CcuString& s, int offset) { if (GetErr) return 0; int n = Stop - Start - offset; byte* p = Start + offset; while (n-- && *p) ++p; int l = p - Start; s.Assign ((const char*) (Start + offset), l); return l; } /*?nextdoc?*/ int UchMsgBuffer :: PeekString (char* str, int n, char delim, int offset) { char ds [2]; ds [0] = delim; ds [1] = 0; return GetString (str, n, ds, true, offset); } /*? Get at most \var{n} characters from the buffer into \var{str}. \var{delim} (resp. \var{ds} if not null) is a delimiter: the buffer is transferred until a null character or the delimiter is encountered. The number of characters actually transferred is returned. \var{str} is always terminated by a null character. ?*/ int UchMsgBuffer :: PeekString (char* str, int n, const char* delim, int offset) { return GetString (str, n, delim, true, offset); } //----- 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) Clear (); } 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 */