2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 24 SBuf */
14 #include "base/InstanceId.h"
17 #include "sbuf/Exceptions.h"
18 #include "sbuf/forward.h"
19 #include "sbuf/MemBlob.h"
20 #include "sbuf/Stats.h"
30 /* SBuf placeholder for printf */
32 #define SQUIDSBUFPH "%.*s"
33 #define SQUIDSBUFPRINT(s) (s).plength(),(s).rawContent()
34 #endif /* SQUIDSBUFPH */
36 // TODO: move within SBuf and rename
44 /** Forward input const_iterator for SBufs
46 * Please note that any operation on the underlying SBuf may invalidate
47 * all iterators over it, resulting in undefined behavior by them.
49 class SBufIterator
: public std::iterator
<std::input_iterator_tag
, char>
53 typedef MemBlob::size_type size_type
;
54 bool operator==(const SBufIterator
&s
) const;
55 bool operator!=(const SBufIterator
&s
) const;
57 const char &operator*() const { return *iter
; }
58 SBufIterator
& operator++() { ++iter
; return *this; }
61 SBufIterator(const SBuf
&, size_type
);
66 /** Reverse input const_iterator for SBufs
68 * Please note that any operation on the underlying SBuf may invalidate
69 * all iterators over it, resulting in undefined behavior by them.
71 class SBufReverseIterator
: public SBufIterator
75 SBufReverseIterator
& operator++() { --iter
; return *this;}
76 const char &operator*() const { return *(iter
-1); }
78 SBufReverseIterator(const SBuf
&s
, size_type sz
) : SBufIterator(s
,sz
) {}
83 * Features: refcounted backing store, cheap copy and sub-stringing
84 * operations, copy-on-write to isolate change operations to each instance.
85 * Where possible, we're trying to mimic std::string's interface.
90 typedef MemBlob::size_type size_type
;
91 typedef SBufIterator const_iterator
;
92 typedef SBufReverseIterator const_reverse_iterator
;
93 static const size_type npos
= 0xffffffff; // max(uint32_t)
95 /// Maximum size of a SBuf. By design it MUST be < MAX(size_type)/2. Currently 256Mb.
96 static const size_type maxSize
= 0xfffffff;
98 /// create an empty (zero-size) SBuf
101 #if __cplusplus >= 201103L
102 SBuf(SBuf
&& S
) : store_(std::move(S
.store_
)), off_(S
.off_
), len_(S
.len_
) {
104 S
.store_
=NULL
; //RefCount supports NULL, and S is about to be destructed
110 /** Constructor: import c-style string
112 * Create a new SBuf containing a COPY of the contents of the
114 * \param S the c string to be copied
115 * \param n how many bytes to import into the SBuf. If it is npos
116 * or unspecified, imports to end-of-cstring
117 * \note it is the caller's responsibility not to go out of bounds
118 * \note bounds is 0 <= pos < length(); caller must pay attention to signedness
120 explicit SBuf(const char *S
, size_type n
);
121 explicit SBuf(const char *S
);
123 /// Constructor: import std::string. Contents are copied.
124 explicit SBuf(const std::string
&s
);
128 /** Explicit assignment.
130 * Current SBuf will share backing store with the assigned one.
132 SBuf
& assign(const SBuf
&S
);
134 /** Assignment operator.
136 * Current SBuf will share backing store with the assigned one.
138 SBuf
& operator =(const SBuf
& S
) {return assign(S
);}
139 #if __cplusplus >= 201103L
140 SBuf
& operator =(SBuf
&&S
) {
143 store_
= std::move(S
.store_
);
146 S
.store_
= NULL
; //RefCount supports NULL, and S is about to be destructed
154 /** Import a c-string into a SBuf, copying the data.
156 * It is the caller's duty to free the imported string, if needed.
157 * \param S the c string to be copied
158 * \param n how many bytes to import into the SBuf. If it is npos
159 * or unspecified, imports to end-of-cstring
160 * \note it is the caller's responsibility not to go out of bounds
161 * \note to assign a std::string use the pattern:
162 * assign(stdstr.data(), stdstd.length())
164 SBuf
& assign(const char *S
, size_type n
);
165 SBuf
& assign(const char *S
) {return assign(S
,npos
);}
167 /** Assignment operator. Copy a NULL-terminated c-style string into a SBuf.
169 * Copy a c-style string into a SBuf. Shortcut for SBuf.assign(S)
170 * It is the caller's duty to free the imported string, if needed.
173 SBuf
& operator =(const char *S
) {return assign(S
);}
175 /** reset the SBuf as if it was just created.
177 * Resets the SBuf to empty, memory is freed lazily.
183 * Append the supplied SBuf to the current one; extend storage as needed.
185 SBuf
& append(const SBuf
& S
);
187 /// Append a single character. The character may be NUL (\0).
188 SBuf
& append(const char c
);
190 /** Append operation for C-style strings.
192 * Append the supplied c-string to the SBuf; extend storage
195 * \param S the c string to be copied. Can be NULL.
196 * \param Ssize how many bytes to import into the SBuf. If it is npos
197 * or unspecified, imports to end-of-cstring. If S is NULL,
199 * \note to append a std::string use the pattern
200 * cstr_append(stdstr.data(), stdstd.length())
202 SBuf
& append(const char * S
, size_type Ssize
);
203 SBuf
& append(const char * S
) { return append(S
,npos
); }
205 /** Assignment operation with printf(3)-style definition
206 * \note arguments may be evaluated more than once, be careful
209 SBuf
& Printf(const char *fmt
, ...);
211 /** Append operation with printf-style arguments
212 * \note arguments may be evaluated more than once, be careful
215 SBuf
& appendf(const char *fmt
, ...);
217 /** Append operation, with vsprintf(3)-style arguments.
218 * \note arguments may be evaluated more than once, be careful
221 SBuf
& vappendf(const char *fmt
, va_list vargs
);
223 /// print the SBuf contents to the supplied ostream
224 std::ostream
& print(std::ostream
&os
) const;
226 /** print SBuf contents and debug information about the SBuf to an ostream
228 * Debug function, dumps to a stream informations on the current SBuf,
229 * including low-level details and statistics.
231 std::ostream
& dump(std::ostream
&os
) const;
233 /** random-access read to any char within the SBuf
235 * does not check access bounds. If you need that, use at()
237 char operator [](size_type pos
) const {++stats
.getChar
; return store_
->mem
[off_
+pos
];}
239 /** random-access read to any char within the SBuf.
241 * \throw OutOfBoundsException when access is out of bounds
242 * \note bounds is 0 <= pos < length(); caller must pay attention to signedness
244 char at(size_type pos
) const {checkAccessBounds(pos
); return operator[](pos
);}
246 /** direct-access set a byte at a specified operation.
248 * \param pos the position to be overwritten
249 * \param toset the value to be written
250 * \throw OutOfBoundsException when pos is of bounds
251 * \note bounds is 0 <= pos < length(); caller must pay attention to signedness
252 * \note performs a copy-on-write if needed.
254 void setAt(size_type pos
, char toset
);
256 /** compare to other SBuf, str(case)cmp-style
258 * \param isCaseSensitive one of caseSensitive or caseInsensitive
259 * \param n compare up to this many bytes. if npos (default), compare whole SBufs
260 * \retval >0 argument of the call is greater than called SBuf
261 * \retval <0 argument of the call is smaller than called SBuf
262 * \retval 0 argument of the call has the same contents of called SBuf
264 int compare(const SBuf
&S
, const SBufCaseSensitive isCaseSensitive
, const size_type n
) const;
265 int compare(const SBuf
&S
, const SBufCaseSensitive isCaseSensitive
) const {
266 return compare(S
, isCaseSensitive
, npos
);
269 /// shorthand version for compare()
270 inline int cmp(const SBuf
&S
, const size_type n
) const {
271 return compare(S
,caseSensitive
,n
);
273 inline int cmp(const SBuf
&S
) const {
274 return compare(S
,caseSensitive
,npos
);
277 /// shorthand version for case-insensitive compare()
278 inline int caseCmp(const SBuf
&S
, const size_type n
) const {
279 return compare(S
,caseInsensitive
,n
);
281 inline int caseCmp(const SBuf
&S
) const {
282 return compare(S
,caseInsensitive
,npos
);
285 /// Comparison with a C-string.
286 int compare(const char *s
, const SBufCaseSensitive isCaseSensitive
, const size_type n
) const;
287 int compare(const char *s
, const SBufCaseSensitive isCaseSensitive
) const {
288 return compare(s
,isCaseSensitive
,npos
);
291 /// Shorthand version for C-string compare().
292 inline int cmp(const char *S
, const size_type n
) const {
293 return compare(S
,caseSensitive
,n
);
295 inline int cmp(const char *S
) const {
296 return compare(S
,caseSensitive
,npos
);
299 /// Shorthand version for case-insensitive C-string compare().
300 inline int caseCmp(const char *S
, const size_type n
) const {
301 return compare(S
,caseInsensitive
,n
);
303 inline int caseCmp(const char *S
) const {
304 return compare(S
,caseInsensitive
,npos
);
307 /** check whether the entire supplied argument is a prefix of the SBuf.
308 * \param S the prefix to match against
309 * \param isCaseSensitive one of caseSensitive or caseInsensitive
310 * \retval true argument is a prefix of the SBuf
312 bool startsWith(const SBuf
&S
, const SBufCaseSensitive isCaseSensitive
= caseSensitive
) const;
314 bool operator ==(const SBuf
& S
) const;
315 bool operator !=(const SBuf
& S
) const;
316 bool operator <(const SBuf
&S
) const {return (cmp(S
) < 0);}
317 bool operator >(const SBuf
&S
) const {return (cmp(S
) > 0);}
318 bool operator <=(const SBuf
&S
) const {return (cmp(S
) <= 0);}
319 bool operator >=(const SBuf
&S
) const {return (cmp(S
) >= 0);}
321 /** Consume bytes at the head of the SBuf
323 * Consume N chars at SBuf head, or to SBuf's end,
324 * whichever is shorter. If more bytes are consumed than available,
325 * the SBuf is emptied
326 * \param n how many bytes to remove; could be zero.
327 * npos (or no argument) means 'to the end of SBuf'
328 * \return a new SBuf containing the consumed bytes.
330 SBuf
consume(size_type n
= npos
);
332 /// gets global statistic informations
333 static const SBufStats
& GetStats();
335 /** Copy SBuf contents into user-supplied C buffer.
337 * Export a copy of the SBuf's contents into the user-supplied
338 * buffer, up to the user-supplied-length. No zero-termination is performed
339 * \return num the number of actually-copied chars.
341 size_type
copy(char *dest
, size_type n
) const;
343 /** exports a pointer to the SBuf internal storage.
344 * \warning ACCESSING RAW STORAGE IS DANGEROUS!
346 * Returns a ead-only pointer to SBuf's content. No terminating null
347 * character is appended (use c_str() for that).
348 * The returned value points to an internal location whose contents
349 * are guaranteed to remain unchanged only until the next call
350 * to a non-constant member function of the SBuf object. Such a
351 * call may be implicit (e.g., when SBuf is destroyed
352 * upon leaving the current context).
353 * This is a very UNSAFE way of accessing the data.
354 * This call never returns NULL.
356 * \note the memory management system guarantees that the exported region
357 * of memory will remain valid if the caller keeps holding
358 * a valid reference to the SBuf object and does not write or append to
361 * SBuf foo("some string");
362 * const char *bar = foo.rawContent();
363 * doSomething(bar); //safe
364 * foo.append(" other string");
365 * doSomething(bar); //unsafe
368 const char* rawContent() const;
370 /** Exports a writable pointer to the SBuf internal storage.
371 * \warning Use with EXTREME caution, this is a dangerous operation.
373 * Returns a pointer to the first unused byte in the SBuf's storage,
374 * which can be be used for appending. At least minSize bytes will
375 * be available for writing.
376 * The returned pointer must not be stored by the caller, as it will
377 * be invalidated by the first call to a non-const method call
379 * This call guarantees to never return NULL.
381 * \note Unlike reserveSpace(), this method does not guarantee exclusive
382 * buffer ownership. It is instead optimized for a one writer
383 * (appender), many readers scenario by avoiding unnecessary
384 * copying and allocations.
385 * \throw SBufTooBigException if the user tries to allocate too big a SBuf
387 char *rawSpace(size_type minSize
);
389 /** Obtain how much free space is available in the backing store.
391 * \note: unless the client just cow()ed, it is not guaranteed that
392 * the free space can be used.
394 size_type
spaceSize() const { return store_
->spaceSize(); }
396 /** Force a SBuf's size
397 * \warning use with EXTREME caution, this is a dangerous operation
399 * Adapt the SBuf internal state after external interference
400 * such as writing into it via rawSpace.
401 * \throw TextException if SBuf doesn't have exclusive ownership of store
402 * \throw SBufTooBigException if new size is bigger than available store space
404 void forceSize(size_type newSize
);
406 /** exports a null-terminated reference to the SBuf internal storage.
407 * \warning ACCESSING RAW STORAGE IS DANGEROUS! DO NOT EVER USE
408 * THE RETURNED POINTER FOR WRITING
410 * The returned value points to an internal location whose contents
411 * are guaranteed to remain unchanged only until the next call
412 * to a non-constant member function of the SBuf object. Such a
413 * call may be implicit (e.g., when SBuf is destroyed
414 * upon leaving the current context).
415 * This is a very UNSAFE way of accessing the data.
416 * This call never returns NULL.
418 * \note the memory management system guarantees that the exported region
419 * of memory will remain valid will remain valid only if the
420 * caller keeps holding a valid reference to the SBuf object and
421 * does not write or append to it
425 /// Returns the number of bytes stored in SBuf.
426 size_type
length() const {return len_
;}
428 /** Get the length of the SBuf, as a signed integer
430 * Compatibility function for printf(3) which requires a signed int
431 * \throw SBufTooBigException if the SBuf is too big for a signed integer
433 int plength() const {
434 if (length()>INT_MAX
)
435 throw SBufTooBigException(__FILE__
, __LINE__
);
436 return static_cast<int>(length());
439 /** Check whether the SBuf is empty
441 * \return true if length() == 0
443 bool isEmpty() const {return (len_
==0);}
445 /** Request to guarantee the SBuf's free store space.
447 * After the reserveSpace request, the SBuf is guaranteed to have at
448 * least minSpace bytes of unused backing store following the currently
449 * used portion and single ownership of the backing store.
450 * \throw SBufTooBigException if the user tries to allocate too big a SBuf
452 void reserveSpace(size_type minSpace
) {
453 Must(minSpace
<= maxSize
);
454 Must(length() <= maxSize
- minSpace
);
455 reserveCapacity(length()+minSpace
);
458 /** Request to guarantee the SBuf's store capacity
460 * After this method is called, the SBuf is guaranteed to have at least
461 * minCapacity bytes of total buffer size, including the currently-used
462 * portion; it is also guaranteed that after this call this SBuf
463 * has unique ownership of the underlying memory store.
464 * \throw SBufTooBigException if the user tries to allocate too big a SBuf
466 void reserveCapacity(size_type minCapacity
);
468 /** Accommodate caller's requirements regarding SBuf's storage if possible.
470 * \return spaceSize(), which may be zero
472 size_type
reserve(const SBufReservationRequirements
&requirements
);
476 * Removes SBuf prefix and suffix, leaving a sequence of 'n'
477 * bytes starting from position 'pos', first byte is at pos 0.
478 * It is an in-place-modifying version of substr.
479 * \param pos start sub-stringing from this byte. If it is
480 * npos or it is greater than the SBuf length, the SBuf is cleared and
481 * an empty SBuf is returned.
482 * \param n maximum number of bytes of the resulting SBuf.
483 * npos means "to end of SBuf".
484 * if it is 0, the SBuf is cleared and an empty SBuf is returned.
485 * if it overflows the end of the SBuf, it is capped to the end of SBuf
488 SBuf
& chop(size_type pos
, size_type n
= npos
);
490 /** Remove characters in the toremove set at the beginning, end or both
492 * \param toremove characters to be removed. Stops chomping at the first
493 * found char not in the set
494 * \param atBeginning if true (default), strips at the beginning of the SBuf
495 * \param atEnd if true (default), strips at the end of the SBuf
497 SBuf
& trim(const SBuf
&toRemove
, bool atBeginning
= true, bool atEnd
= true);
499 /** Extract a part of the current SBuf.
501 * Return a fresh a fresh copy of a portion the current SBuf, which is
502 * left untouched. The same parameter convetions apply as for chop.
505 SBuf
substr(size_type pos
, size_type n
= npos
) const;
507 /** Find first occurrence of character in SBuf
509 * Returns the index in the SBuf of the first occurrence of char c.
510 * \return npos if the char was not found
511 * \param startPos if specified, ignore any occurrences before that position
512 * if startPos is npos or greater than length() npos is always returned
513 * if startPos is less than zero, it is ignored
515 size_type
find(char c
, size_type startPos
= 0) const;
517 /** Find first occurrence of SBuf in SBuf.
519 * Returns the index in the SBuf of the first occurrence of the
520 * sequence contained in the str argument.
521 * \param startPos if specified, ignore any occurrences before that position
522 * if startPos is npos or greater than length() npos is always returned
523 * \return npos if the SBuf was not found
525 size_type
find(const SBuf
& str
, size_type startPos
= 0) const;
527 /** Find last occurrence of character in SBuf
529 * Returns the index in the SBuf of the last occurrence of char c.
530 * \return npos if the char was not found
531 * \param endPos if specified, ignore any occurrences after that position.
532 * if npos or greater than length(), the whole SBuf is considered
534 size_type
rfind(char c
, size_type endPos
= npos
) const;
536 /** Find last occurrence of SBuf in SBuf
538 * Returns the index in the SBuf of the last occurrence of the
539 * sequence contained in the str argument.
540 * \return npos if the sequence was not found
541 * \param endPos if specified, ignore any occurrences after that position
542 * if npos or greater than length(), the whole SBuf is considered
544 size_type
rfind(const SBuf
&str
, size_type endPos
= npos
) const;
546 /** Find first occurrence of character of set in SBuf
548 * Finds the first occurrence of ANY of the characters in the supplied set in
550 * \return npos if no character in the set could be found
551 * \param startPos if specified, ignore any occurrences before that position
552 * if npos, then npos is always returned
554 * TODO: rename to camelCase
556 size_type
findFirstOf(const CharacterSet
&set
, size_type startPos
= 0) const;
558 /** Find last occurrence of character of set in SBuf
560 * Finds the last occurrence of ANY of the characters in the supplied set in
562 * \return npos if no character in the set could be found
563 * \param endPos if specified, ignore any occurrences after that position
564 * if npos, the entire SBuf is searched
566 size_type
findLastOf(const CharacterSet
&set
, size_type endPos
= npos
) const;
568 /** Find first occurrence character NOT in character set
570 * \return npos if all characters in the SBuf are from set
571 * \param startPos if specified, ignore any occurrences before that position
572 * if npos, then npos is always returned
574 * TODO: rename to camelCase
576 size_type
findFirstNotOf(const CharacterSet
&set
, size_type startPos
= 0) const;
578 /** Find last occurrence character NOT in character set
580 * \return npos if all characters in the SBuf are from set
581 * \param endPos if specified, ignore any occurrences after that position
582 * if npos, then the entire SBuf is searched
584 size_type
findLastNotOf(const CharacterSet
&set
, size_type endPos
= npos
) const;
586 /// converts all characters to lower case; \see man tolower(3)
589 /// converts all characters to upper case; \see man toupper(3)
592 /// std::string export function
593 std::string
toStdString() const { return std::string(buf(),length()); }
595 const_iterator
begin() const {
596 return const_iterator(*this, 0);
599 const_iterator
end() const {
600 return const_iterator(*this, length());
603 const_reverse_iterator
rbegin() const {
604 return const_reverse_iterator(*this, length());
607 const_reverse_iterator
rend() const {
608 return const_reverse_iterator(*this, 0);
611 // TODO: possibly implement erase() similar to std::string's erase
612 // TODO: possibly implement a replace() call
616 * Keeps SBuf's MemBlob alive in a blob-destroying context where
617 * a seemingly unrelated memory pointer may belong to the same blob.
618 * For [an extreme] example, consider: a.append(a).
619 * Compared to an SBuf temporary, this class is optimized to
620 * preserve blobs only if needed and to reduce debugging noise.
625 Locker(SBuf
*parent
, const char *otherBuffer
) {
626 // lock if otherBuffer intersects the parents buffer area
627 const MemBlob
*blob
= parent
->store_
.getRaw();
628 if (blob
->mem
<= otherBuffer
&& otherBuffer
< (blob
->mem
+ blob
->capacity
))
632 MemBlob::Pointer locket
;
636 MemBlob::Pointer store_
; ///< memory block, possibly shared with other SBufs
637 size_type off_
; ///< our content start offset from the beginning of shared store_
638 size_type len_
; ///< number of our content bytes in shared store_
639 static SBufStats stats
; ///< class-wide statistics
641 /// SBuf object identifier; does not change when contents do,
642 /// including during assignment
643 const InstanceId
<SBuf
> id
;
645 /** obtain prototype store
647 * Just-created SBufs all share to the same MemBlob.
648 * This call instantiates and returns it.
650 static MemBlob::Pointer
GetStorePrototype();
653 * obtains a char* to the beginning of this SBuf in memory.
654 * \note the obtained string is NOT null-terminated.
656 char * buf() const {return (store_
->mem
+off_
);}
658 /** returns the pointer to the first char after this SBuf end
660 * No checks are made that the space returned is safe, checking that is
663 char * bufEnd() const {return (store_
->mem
+off_
+len_
);}
666 * Try to guesstimate how big a MemBlob to allocate.
667 * The result is guarranteed to be to be at least the desired size.
669 size_type
estimateCapacity(size_type desired
) const {return (2*desired
);}
671 void reAlloc(size_type newsize
);
673 void cow(size_type minsize
= npos
);
675 void checkAccessBounds(size_type pos
) const;
677 /** Low-level append operation
679 * Takes as input a contiguous area of memory and appends its contents
680 * to the SBuf, taking care of memory management. Does no bounds checking
681 * on the supplied memory buffer, it is the duty of the caller to ensure
682 * that the supplied area is valid.
684 SBuf
& lowAppend(const char * memArea
, size_type areaSize
);
687 /// Named SBuf::reserve() parameters. Defaults ask for and restrict nothing.
688 class SBufReservationRequirements
691 typedef SBuf::size_type size_type
;
694 * Parameters are listed in the reverse order of importance: Satisfaction of
695 * the lower-listed requirements may violate the higher-listed requirements.
697 size_type idealSpace
= 0; ///< if allocating anyway, provide this much space
698 size_type minSpace
= 0; ///< allocate if spaceSize() is smaller
699 size_type maxCapacity
= SBuf::maxSize
; ///< do not allocate more than this
700 bool allowShared
= true; ///< whether sharing our storage with others is OK
703 /// ostream output operator
704 inline std::ostream
&
705 operator <<(std::ostream
& os
, const SBuf
& S
)
710 /// Returns a lower-cased copy of its parameter.
718 /// Returns an upper-cased copy of its parameter.
727 * Copy an SBuf into a C-string.
729 * Guarantees that the output is a c-string of length
730 * no more than SBuf::length()+1 by appending a \0 byte
731 * to the C-string copy of the SBuf contents.
733 * \note The destination c-string memory MUST be of at least
736 * No protection is added to prevent \0 bytes within the string.
737 * Unexpectedly short strings are a problem for the receiver
738 * to deal with if it cares.
740 * Unlike SBuf::c_str() does not alter the SBuf in any way.
743 SBufToCstring(char *d
, const SBuf
&s
)
745 s
.copy(d
, s
.length());
746 d
[s
.length()] = '\0'; // 0-terminate the destination
747 debugs(1, DBG_DATA
, "built c-string '" << d
<< "' from " << s
);
751 * Copy an SBuf into a C-string.
753 * \see SBufToCstring(char *d, const SBuf &s)
755 * \returns A dynamically allocated c-string based on SBuf.
756 * Use xfree() / safe_free() to release the c-string.
759 SBufToCstring(const SBuf
&s
)
761 char *d
= static_cast<char*>(xmalloc(s
.length()+1));
767 SBufIterator::SBufIterator(const SBuf
&s
, size_type pos
)
768 : iter(s
.rawContent()+pos
)
772 SBufIterator::operator==(const SBufIterator
&s
) const
774 // note: maybe the sbuf comparison is unnecessary?
775 return iter
== s
.iter
;
779 SBufIterator::operator!=(const SBufIterator
&s
) const
781 // note: maybe the sbuf comparison is unnecessary?
782 return iter
!= s
.iter
;
785 #endif /* SQUID_SBUF_H */