| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | // -------------------------------------------------------------------------------- |
| 5 | // SBuffer.h (Safe Buffer) |
| 6 | // |
| 7 | |
| 8 | // -------------------------------------------------------------------------------- |
| 9 | |
| 10 | // -------------------------------------------------------------------------------- |
| 11 | // SBuffer is a relatively safe way to manipulate a dynamically |
| 12 | // allocated data buffer. An SBuffer is conceptually a simple array |
| 13 | // of bytes. It maintains both a conceptual size and an actual allocated size. |
| 14 | // |
| 15 | // SBuffer provides safe access to the data buffer by providing rich high |
| 16 | // level functionality (like insertion, deleteion, copying, comparison, and |
| 17 | // iteration) without exposing direct pointers to its buffers. |
| 18 | // |
| 19 | // For interoperability, SBuffers can expose their buffers - either as readonly |
| 20 | // by BYTE * or void * cases, or as writable by the OpenRawBuffer/CloseRawBuffer |
| 21 | // entry points. Use of these should be limited wherever possible though; as there |
| 22 | // is always a possibilility of buffer overrun. |
| 23 | // |
| 24 | // To mimimize heap allocations, the InlineSBuffer template will preallocate a fixed |
| 25 | // size buffer inline with the SBuffer object itself. It will use this buffer unless |
| 26 | // it needs a bigger one, in which case it transparently moves on to using the heap. |
| 27 | // The StackSBuffer class instatiates the InlineSBuffer with a standard heuristic |
| 28 | // stack preallocation size. |
| 29 | // |
| 30 | // SBuffer is "subclassable" to add content typeing to the buffer. See SArray and |
| 31 | // SString for examples. |
| 32 | // -------------------------------------------------------------------------------- |
| 33 | |
| 34 | |
| 35 | #ifndef _SBUFFER_H_ |
| 36 | #define _SBUFFER_H_ |
| 37 | |
| 38 | #include "clrtypes.h" |
| 39 | #include "iterator.h" |
| 40 | #include "check.h" |
| 41 | #include "daccess.h" |
| 42 | #include "memoryrange.h" |
| 43 | |
| 44 | // ================================================================================ |
| 45 | // Macros for computing padding |
| 46 | // ================================================================================ |
| 47 | |
| 48 | #define ALIGNMENT(size) \ |
| 49 | (( (size^(size-1)) >> 1) +1) |
| 50 | |
| 51 | #define ALIGN(size, align) \ |
| 52 | (((size)+((align)-1)) & ~((align)-1)) |
| 53 | |
| 54 | #define PAD(size, align) \ |
| 55 | (ALIGN((size), (align)) - (size)) |
| 56 | |
| 57 | // ================================================================================ |
| 58 | // SBuffer : base class for safe buffers |
| 59 | // ================================================================================ |
| 60 | |
| 61 | typedef DPTR(class SBuffer) PTR_SBuffer; |
| 62 | |
| 63 | class SBuffer |
| 64 | { |
| 65 | public: |
| 66 | //-------------------------------------------------------------------- |
| 67 | // Flags and constants |
| 68 | //-------------------------------------------------------------------- |
| 69 | |
| 70 | enum ImmutableFlag |
| 71 | { |
| 72 | Immutable |
| 73 | }; |
| 74 | |
| 75 | enum PreallocFlag |
| 76 | { |
| 77 | Prealloc |
| 78 | }; |
| 79 | |
| 80 | //-------------------------------------------------------------------- |
| 81 | // Types |
| 82 | //-------------------------------------------------------------------- |
| 83 | |
| 84 | public: |
| 85 | class CIterator; |
| 86 | friend class CIterator; |
| 87 | |
| 88 | class Iterator; |
| 89 | friend class Iterator; |
| 90 | |
| 91 | //-------------------------------------------------------------------- |
| 92 | // Initializers and constructors |
| 93 | //-------------------------------------------------------------------- |
| 94 | |
| 95 | public: |
| 96 | // Constructors |
| 97 | SBuffer(); |
| 98 | SBuffer(COUNT_T size); |
| 99 | SBuffer(const BYTE *buffer, COUNT_T size); |
| 100 | explicit SBuffer(const SBuffer &buffer); |
| 101 | |
| 102 | // Immutable constructor should ONLY be used if buffer will |
| 103 | // NEVER BE FREED OR MODIFIED. PERIOD. . |
| 104 | SBuffer(ImmutableFlag immutable, const BYTE *buffer, COUNT_T size); |
| 105 | |
| 106 | // Prealloc should be allocated inline with SBuffer - it must have the same |
| 107 | // lifetime as SBuffer's memory. |
| 108 | SBuffer(PreallocFlag prealloc, void *buffer, COUNT_T size); |
| 109 | |
| 110 | ~SBuffer(); |
| 111 | |
| 112 | void Clear(); |
| 113 | |
| 114 | void Set(const SBuffer &buffer); |
| 115 | void Set(const BYTE *buffer, COUNT_T size); |
| 116 | void SetImmutable(const BYTE *buffer, COUNT_T size); |
| 117 | |
| 118 | //-------------------------------------------------------------------- |
| 119 | // Buffer size routines. A buffer has an externally visible size, but |
| 120 | // it also has an internal allocation size which may be larger. |
| 121 | //-------------------------------------------------------------------- |
| 122 | |
| 123 | // Get and set size of buffer. Note that the actual size of the |
| 124 | // internally allocated memory block may be bigger. |
| 125 | COUNT_T GetSize() const; |
| 126 | void SetSize(COUNT_T count); |
| 127 | |
| 128 | // Grow size of buffer to maximum amount without reallocating. |
| 129 | void MaximizeSize(); |
| 130 | |
| 131 | //-------------------------------------------------------------------- |
| 132 | // Buffer allocation routines |
| 133 | //-------------------------------------------------------------------- |
| 134 | |
| 135 | // Return the current available allocation space of the buffer. |
| 136 | COUNT_T GetAllocation() const; |
| 137 | |
| 138 | // Preallocate some memory you expect to use. This can prevent |
| 139 | // multiple reallocations. Note this does not change the visible |
| 140 | // size of the buffer. |
| 141 | void Preallocate(COUNT_T allocation) const; |
| 142 | |
| 143 | // Shrink memory usage of buffer to minimal amount. Note that |
| 144 | // this does not change the visible size of the buffer. |
| 145 | void Trim() const; |
| 146 | |
| 147 | //-------------------------------------------------------------------- |
| 148 | // Content manipulation routines |
| 149 | //-------------------------------------------------------------------- |
| 150 | |
| 151 | void Zero(); |
| 152 | void Fill(BYTE value); |
| 153 | void Fill(const Iterator &to, BYTE value, COUNT_T size); |
| 154 | |
| 155 | // Internal copy. "Copy" leaves from range as is; "Move" |
| 156 | // leaves from range in uninitialized state. |
| 157 | // (This distinction is more important when using from a |
| 158 | // typed wrapper than in the base SBuffer class.) |
| 159 | // |
| 160 | // NOTE: Copy vs Move is NOT based on whether ranges overlap |
| 161 | // or not. Ranges may overlap in either case. |
| 162 | // |
| 163 | // Note that both Iterators must be on THIS buffer. |
| 164 | void Copy(const Iterator &to, const CIterator &from, COUNT_T size); |
| 165 | void Move(const Iterator &to, const CIterator &from, COUNT_T size); |
| 166 | |
| 167 | // External copy. |
| 168 | void Copy(const Iterator &i, const SBuffer &source); |
| 169 | void Copy(const Iterator &i, const void *source, COUNT_T size); |
| 170 | void Copy(void *dest, const CIterator &i, COUNT_T size); |
| 171 | |
| 172 | // Insert bytes at the given iterator location. |
| 173 | void Insert(const Iterator &i, const SBuffer &source); |
| 174 | void Insert(const Iterator &i, COUNT_T size); |
| 175 | |
| 176 | // Delete bytes at the given iterator location |
| 177 | void Delete(const Iterator &i, COUNT_T size); |
| 178 | |
| 179 | // Replace bytes at the given iterator location |
| 180 | void Replace(const Iterator &i, COUNT_T deleteSize, const SBuffer &insert); |
| 181 | void Replace(const Iterator &i, COUNT_T deleteSize, COUNT_T insertSize); |
| 182 | |
| 183 | // Compare entire buffer; return -1, 0, 1 |
| 184 | int Compare(const SBuffer &compare) const; |
| 185 | int Compare(const BYTE *match, COUNT_T size) const; |
| 186 | |
| 187 | // Compare entire buffer; return TRUE or FALSE |
| 188 | BOOL Equals(const SBuffer &compare) const; |
| 189 | BOOL Equals(const BYTE *match, COUNT_T size) const; |
| 190 | |
| 191 | // Match portion of this buffer to given bytes; return TRUE or FALSE |
| 192 | BOOL Match(const CIterator &i, const SBuffer &match) const; |
| 193 | BOOL Match(const CIterator &i, const BYTE *match, COUNT_T size) const; |
| 194 | |
| 195 | //-------------------------------------------------------------------- |
| 196 | // Iterators |
| 197 | // |
| 198 | // Note that any iterator returned is not |
| 199 | // valid after any operation which may resize the buffer, unless |
| 200 | // the operation was performed on that particular iterator. |
| 201 | //-------------------------------------------------------------------- |
| 202 | |
| 203 | CIterator Begin() const; |
| 204 | CIterator End() const; |
| 205 | |
| 206 | Iterator Begin(); |
| 207 | Iterator End(); |
| 208 | |
| 209 | BYTE & operator[] (int index); |
| 210 | const BYTE & operator[] (int index) const; |
| 211 | |
| 212 | //-------------------------------------------------------------------- |
| 213 | // Raw buffer access |
| 214 | // |
| 215 | // Accessing a raw buffer via pointer is inherently more dangerous than |
| 216 | // other uses of this API, and should be avoided if at all possible. |
| 217 | // It is primarily provided for compatibility with existing APIs. |
| 218 | // |
| 219 | // Note that any buffer pointer returned is not |
| 220 | // valid after any operation which may resize the buffer. |
| 221 | //-------------------------------------------------------------------- |
| 222 | |
| 223 | // Casting operators return the existing buffer as |
| 224 | // a raw const pointer. Note that the pointer is valid only |
| 225 | // until the buffer is modified via an API. |
| 226 | operator const void *() const; |
| 227 | operator const BYTE *() const; |
| 228 | |
| 229 | // To write directly to the SString's underlying buffer: |
| 230 | // 1) Call OpenRawBuffer() and pass it the count of bytes |
| 231 | // you need. |
| 232 | // 2) That returns a pointer to the raw buffer which you can write to. |
| 233 | // 3) When you are done writing to the pointer, call CloseBuffer() |
| 234 | // and pass it the count of bytes you actually wrote. |
| 235 | // The pointer from step 1 is now invalid. |
| 236 | |
| 237 | // example usage: |
| 238 | // void GetInfo(SBuffer &buf) |
| 239 | // { |
| 240 | // BYTE *p = buf.OpenRawBuffer(3); |
| 241 | // OSGetSomeInfo(p, 3); |
| 242 | // buf.CloseRawBuffer(); |
| 243 | // } |
| 244 | |
| 245 | // You should open the buffer, write the data, and immediately close it. |
| 246 | // No sbuffer operations are valid while the buffer is opened. |
| 247 | // |
| 248 | // In a debug build, Open/Close will do lots of little checks to make sure |
| 249 | // you don't buffer overflow while it's opened. In a retail build, this |
| 250 | // is a very streamlined action. |
| 251 | |
| 252 | // Open the raw buffer for writing count bytes |
| 253 | BYTE *OpenRawBuffer(COUNT_T maxCount); |
| 254 | |
| 255 | // Call after OpenRawBuffer(). |
| 256 | |
| 257 | // Provide the count of bytes actually used. This will make sure the |
| 258 | // SBuffer's size is correct. |
| 259 | void CloseRawBuffer(COUNT_T actualCount); |
| 260 | |
| 261 | // Close the buffer. Assumes that we completely filled the buffer |
| 262 | // that OpenRawBuffer() gave back. |
| 263 | void CloseRawBuffer(); |
| 264 | |
| 265 | //-------------------------------------------------------------------- |
| 266 | // Check routines. These are typically used internally, but may be |
| 267 | // called externally if desired. |
| 268 | //-------------------------------------------------------------------- |
| 269 | |
| 270 | CHECK CheckBufferClosed() const; |
| 271 | static CHECK CheckSize(COUNT_T size); |
| 272 | static CHECK CheckAllocation(COUNT_T allocation); |
| 273 | CHECK CheckIteratorRange(const CIterator &i) const; |
| 274 | CHECK CheckIteratorRange(const CIterator &i, COUNT_T size) const; |
| 275 | |
| 276 | CHECK Check() const; |
| 277 | CHECK Invariant() const; |
| 278 | CHECK InternalInvariant() const; |
| 279 | |
| 280 | protected: |
| 281 | |
| 282 | //-------------------------------------------------------------------- |
| 283 | // Internal helper routines |
| 284 | //-------------------------------------------------------------------- |
| 285 | |
| 286 | // Preserve = preserve contents while reallocating |
| 287 | typedef enum |
| 288 | { |
| 289 | DONT_PRESERVE = 0, |
| 290 | PRESERVE = 1, |
| 291 | } Preserve; |
| 292 | |
| 293 | void Resize(COUNT_T size, Preserve preserve = PRESERVE); |
| 294 | void ResizePadded(COUNT_T size, Preserve preserve = PRESERVE); |
| 295 | void TweakSize(COUNT_T size); |
| 296 | void ReallocateBuffer(COUNT_T allocation, Preserve preserve); |
| 297 | void EnsureMutable() const; |
| 298 | |
| 299 | //-------------------------------------------------------------------- |
| 300 | // We define some extra flags and fields for subclasses (these are specifically |
| 301 | // designed for SString, but use otherwise if desired.) |
| 302 | //-------------------------------------------------------------------- |
| 303 | |
| 304 | BOOL IsFlag1() const; |
| 305 | void SetFlag1(); |
| 306 | void ClearFlag1(); |
| 307 | |
| 308 | BOOL IsFlag2() const; |
| 309 | void SetFlag2(); |
| 310 | void ClearFlag2(); |
| 311 | |
| 312 | BOOL IsFlag3() const; |
| 313 | void SetFlag3(); |
| 314 | void ClearFlag3(); |
| 315 | |
| 316 | INT GetRepresentationField() const; |
| 317 | void SetRepresentationField(int value); |
| 318 | |
| 319 | protected: |
| 320 | |
| 321 | //-------------------------------------------------------------------- |
| 322 | // Flag access |
| 323 | //-------------------------------------------------------------------- |
| 324 | |
| 325 | BOOL IsAllocated() const; |
| 326 | void SetAllocated(); |
| 327 | void ClearAllocated(); |
| 328 | |
| 329 | BOOL IsImmutable() const; |
| 330 | void SetImmutable(); |
| 331 | void ClearImmutable(); |
| 332 | |
| 333 | #if _DEBUG |
| 334 | BOOL IsOpened() const; |
| 335 | void SetOpened(); |
| 336 | void ClearOpened(); |
| 337 | #endif |
| 338 | |
| 339 | //-------------------------------------------------------------------- |
| 340 | // Buffer management routines |
| 341 | //-------------------------------------------------------------------- |
| 342 | |
| 343 | // Allocate and free a memory buffer |
| 344 | BYTE *NewBuffer(COUNT_T allocation); |
| 345 | void DeleteBuffer(BYTE *buffer, COUNT_T allocation); |
| 346 | |
| 347 | // Use existing buffer |
| 348 | BYTE *UseBuffer(BYTE *buffer, COUNT_T *allocation); |
| 349 | |
| 350 | CHECK CheckBuffer(const BYTE* buffer, COUNT_T allocation) const; |
| 351 | |
| 352 | // Manipulates contents of the buffer via the plugins below, but |
| 353 | // adds some debugging checks. Should always call through here rather |
| 354 | // than directly calling the extensibility points. |
| 355 | void DebugMoveBuffer(__out_bcount(size) BYTE *to, BYTE *from, COUNT_T size); |
| 356 | void DebugCopyConstructBuffer(__out_bcount(size) BYTE *to, const BYTE *from, COUNT_T size); |
| 357 | void DebugConstructBuffer(BYTE *buffer, COUNT_T size); |
| 358 | void DebugDestructBuffer(BYTE *buffer, COUNT_T size); |
| 359 | |
| 360 | void DebugStompUnusedBuffer(BYTE *buffer, COUNT_T size); |
| 361 | #ifdef _DEBUG |
| 362 | static BOOL EnsureGarbageCharOnly(const BYTE *buffer, COUNT_T size); |
| 363 | #endif |
| 364 | CHECK CheckUnusedBuffer(const BYTE *buffer, COUNT_T size) const; |
| 365 | |
| 366 | #ifdef DACCESS_COMPILE |
| 367 | public: |
| 368 | |
| 369 | // Expose the raw Target address of the buffer to DAC. |
| 370 | // This does not do any marshalling. This can be useful if the caller wants to allocate the buffer on |
| 371 | // its own heap so that it can survive Flush calls. |
| 372 | MemoryRange DacGetRawBuffer() const |
| 373 | { |
| 374 | SUPPORTS_DAC; |
| 375 | PTR_VOID p = dac_cast<PTR_VOID>((TADDR) m_buffer); |
| 376 | return MemoryRange(p, GetSize()); |
| 377 | } |
| 378 | |
| 379 | protected: |
| 380 | |
| 381 | // Return a host copy of the buffer, allocated on the DAC heap (and thus invalidated at the next call to Flush). |
| 382 | void* DacGetRawContent(void) const |
| 383 | { |
| 384 | SUPPORTS_DAC; |
| 385 | |
| 386 | // SBuffers are used in DAC in two ways - buffers in the host, and marshalled buffers from the target. |
| 387 | // This is a problem - we can't reason about the address space of the buffer statically, and instead rely on |
| 388 | // the dynamic usage (i.e. the methods are basically bifurcated into those you can use on host instances, |
| 389 | // and those you can use on marshalled copies). |
| 390 | // Ideally we'll have two versions of the SBuffer code - one that's marshalled (normal DACization) and one |
| 391 | // that isn't (host-only utility). This is the "dual-mode DAC problem". |
| 392 | // But this only affects a couple classes, and so for now we'll ignore the problem - causing a bunch of DacCop |
| 393 | // violations. |
| 394 | DACCOP_IGNORE(CastBetweenAddressSpaces, "SBuffer has the dual-mode DAC problem" ); |
| 395 | DACCOP_IGNORE(FieldAccess, "SBuffer has the dual-mode DAC problem" ); |
| 396 | TADDR bufAddr = (TADDR)m_buffer; |
| 397 | |
| 398 | return DacInstantiateTypeByAddress(bufAddr, m_size, true); |
| 399 | } |
| 400 | |
| 401 | void EnumMemoryRegions(CLRDataEnumMemoryFlags flags) const |
| 402 | { |
| 403 | SUPPORTS_DAC; |
| 404 | |
| 405 | if (flags != CLRDATA_ENUM_MEM_TRIAGE) |
| 406 | { |
| 407 | DacEnumMemoryRegion((TADDR)m_buffer, m_size); |
| 408 | } |
| 409 | } |
| 410 | #endif |
| 411 | |
| 412 | //---------------------------------------------------------------------------- |
| 413 | // Iterator base class |
| 414 | //---------------------------------------------------------------------------- |
| 415 | |
| 416 | friend class CheckedIteratorBase<SBuffer>; |
| 417 | |
| 418 | class Index : public CheckedIteratorBase<SBuffer> |
| 419 | { |
| 420 | friend class SBuffer; |
| 421 | |
| 422 | friend class CIterator; |
| 423 | friend class Indexer<const BYTE, CIterator>; |
| 424 | |
| 425 | friend class Iterator; |
| 426 | friend class Indexer<BYTE, Iterator>; |
| 427 | |
| 428 | protected: |
| 429 | BYTE* m_ptr; |
| 430 | |
| 431 | Index(); |
| 432 | Index(SBuffer *container, SCOUNT_T index); |
| 433 | BYTE &GetAt(SCOUNT_T delta) const; |
| 434 | void Skip(SCOUNT_T delta); |
| 435 | SCOUNT_T Subtract(const Index &i) const; |
| 436 | |
| 437 | CHECK DoCheck(SCOUNT_T delta) const; |
| 438 | |
| 439 | void Resync(const SBuffer *container, BYTE *value) const; |
| 440 | }; |
| 441 | |
| 442 | public: |
| 443 | |
| 444 | class CIterator : public Index, public Indexer<const BYTE, CIterator> |
| 445 | { |
| 446 | friend class SBuffer; |
| 447 | |
| 448 | public: |
| 449 | CIterator() |
| 450 | { |
| 451 | } |
| 452 | |
| 453 | CIterator(const SBuffer *buffer, int index) |
| 454 | : Index(const_cast<SBuffer*>(buffer), index) |
| 455 | { |
| 456 | } |
| 457 | }; |
| 458 | |
| 459 | class Iterator : public Index, public Indexer<BYTE, Iterator> |
| 460 | { |
| 461 | friend class SBuffer; |
| 462 | |
| 463 | public: |
| 464 | operator const CIterator &() const |
| 465 | { |
| 466 | return *(const CIterator *)this; |
| 467 | } |
| 468 | |
| 469 | operator CIterator &() |
| 470 | { |
| 471 | return *(CIterator *)this; |
| 472 | } |
| 473 | |
| 474 | Iterator() |
| 475 | { |
| 476 | } |
| 477 | |
| 478 | Iterator(SBuffer *buffer, int index) |
| 479 | : Index(buffer, index) |
| 480 | { |
| 481 | } |
| 482 | |
| 483 | }; |
| 484 | |
| 485 | |
| 486 | //---------------------------------------------------------------------------- |
| 487 | // Member and data declarations |
| 488 | //---------------------------------------------------------------------------- |
| 489 | |
| 490 | private: |
| 491 | enum |
| 492 | { |
| 493 | REPRESENTATION_MASK = 0x07, |
| 494 | ALLOCATED = 0x08, |
| 495 | IMMUTABLE = 0x10, |
| 496 | OPENED = 0x20, |
| 497 | FLAG1 = 0x40, |
| 498 | FLAG2 = 0x80, |
| 499 | FLAG3 = 0x100, |
| 500 | }; |
| 501 | |
| 502 | COUNT_T m_size; // externally visible size |
| 503 | COUNT_T m_allocation; // actual allocated size |
| 504 | UINT32 m_flags; // @todo: steal flags from sizes |
| 505 | |
| 506 | protected: |
| 507 | union { |
| 508 | BYTE *m_buffer; |
| 509 | wchar_t *m_asStr; // For debugging, view as a unicode string |
| 510 | }; |
| 511 | |
| 512 | #if _DEBUG |
| 513 | protected: |
| 514 | // We will update the "revision" of the buffer every time it is potentially reallocation, |
| 515 | // so we can tell when iterators are no longer valid. |
| 516 | int m_revision; |
| 517 | #endif |
| 518 | }; |
| 519 | |
| 520 | // ================================================================================ |
| 521 | // InlineSBuffer : Tlempate for an SBuffer with preallocated buffer space |
| 522 | // ================================================================================ |
| 523 | |
| 524 | #define BUFFER_ALIGNMENT 4 |
| 525 | |
| 526 | template <COUNT_T size> |
| 527 | class InlineSBuffer : public SBuffer |
| 528 | { |
| 529 | private: |
| 530 | #ifdef _MSC_VER |
| 531 | #pragma warning(push) |
| 532 | #pragma warning(disable:4200) // zero sized array |
| 533 | #pragma warning(disable:4324) // don't complain if DECLSPEC_ALIGN actually pads |
| 534 | DECLSPEC_ALIGN(BUFFER_ALIGNMENT) BYTE m_prealloc[size]; |
| 535 | #pragma warning(pop) |
| 536 | #else |
| 537 | // use UINT64 to get maximum alignment of the memory |
| 538 | UINT64 m_prealloc[ALIGN(size,sizeof(UINT64))/sizeof(UINT64)]; |
| 539 | #endif // _MSC_VER |
| 540 | |
| 541 | public: |
| 542 | InlineSBuffer() |
| 543 | : SBuffer(Prealloc, (BYTE*)m_prealloc, size) |
| 544 | { |
| 545 | WRAPPER_NO_CONTRACT; |
| 546 | } |
| 547 | }; |
| 548 | |
| 549 | |
| 550 | // a 1K sized buffer filled with $ that we'll use in debug builds for verification |
| 551 | #define GARBAGE_FILL_DWORD 0x24242424 // $$$$ |
| 552 | #define GARBAGE_FILL_BUFFER_ITEMS 16 |
| 553 | #define GARBAGE_FILL_BUFFER_SIZE GARBAGE_FILL_BUFFER_ITEMS*sizeof(DWORD) |
| 554 | // ================================================================================ |
| 555 | // StackSBuffer : SBuffer with relatively large preallocated buffer for stack use |
| 556 | // ================================================================================ |
| 557 | |
| 558 | #define STACK_ALLOC 256 |
| 559 | |
| 560 | typedef InlineSBuffer<STACK_ALLOC> StackSBuffer; |
| 561 | |
| 562 | // ================================================================================ |
| 563 | // Inline definitions |
| 564 | // ================================================================================ |
| 565 | |
| 566 | /// a wrapper for templates and such, that use "==". |
| 567 | /// more expensive than a typical "==", though |
| 568 | inline BOOL operator == (const SBuffer& b1,const SBuffer& b2) |
| 569 | { |
| 570 | return b1.Equals(b2); |
| 571 | }; |
| 572 | |
| 573 | #include <sbuffer.inl> |
| 574 | |
| 575 | #endif // _SBUFFER_H_ |
| 576 | |