summaryrefslogtreecommitdiff
path: root/src/inc/sbuffer.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/inc/sbuffer.h')
-rw-r--r--src/inc/sbuffer.h575
1 files changed, 575 insertions, 0 deletions
diff --git a/src/inc/sbuffer.h b/src/inc/sbuffer.h
new file mode 100644
index 0000000000..e56322ac88
--- /dev/null
+++ b/src/inc/sbuffer.h
@@ -0,0 +1,575 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// --------------------------------------------------------------------------------
+// SBuffer.h (Safe Buffer)
+//
+
+// --------------------------------------------------------------------------------
+
+// --------------------------------------------------------------------------------
+// SBuffer is a relatively safe way to manipulate a dynamically
+// allocated data buffer. An SBuffer is conceptually a simple array
+// of bytes. It maintains both a conceptual size and an actual allocated size.
+//
+// SBuffer provides safe access to the data buffer by providing rich high
+// level functionality (like insertion, deleteion, copying, comparison, and
+// iteration) without exposing direct pointers to its buffers.
+//
+// For interoperability, SBuffers can expose their buffers - either as readonly
+// by BYTE * or void * cases, or as writable by the OpenRawBuffer/CloseRawBuffer
+// entry points. Use of these should be limited wherever possible though; as there
+// is always a possibilility of buffer overrun.
+//
+// To mimimize heap allocations, the InlineSBuffer template will preallocate a fixed
+// size buffer inline with the SBuffer object itself. It will use this buffer unless
+// it needs a bigger one, in which case it transparently moves on to using the heap.
+// The StackSBuffer class instatiates the InlineSBuffer with a standard heuristic
+// stack preallocation size.
+//
+// SBuffer is "subclassable" to add content typeing to the buffer. See SArray and
+// SString for examples.
+// --------------------------------------------------------------------------------
+
+
+#ifndef _SBUFFER_H_
+#define _SBUFFER_H_
+
+#include "clrtypes.h"
+#include "iterator.h"
+#include "check.h"
+#include "daccess.h"
+#include "memoryrange.h"
+
+// ================================================================================
+// Macros for computing padding
+// ================================================================================
+
+#define ALIGNMENT(size) \
+ (( (size^(size-1)) >> 1) +1)
+
+#define ALIGN(size, align) \
+ (((size)+((align)-1)) & ~((align)-1))
+
+#define PAD(size, align) \
+ (ALIGN((size), (align)) - (size))
+
+// ================================================================================
+// SBuffer : base class for safe buffers
+// ================================================================================
+
+typedef DPTR(class SBuffer) PTR_SBuffer;
+
+class SBuffer
+{
+ public:
+ //--------------------------------------------------------------------
+ // Flags and constants
+ //--------------------------------------------------------------------
+
+ enum ImmutableFlag
+ {
+ Immutable
+ };
+
+ enum PreallocFlag
+ {
+ Prealloc
+ };
+
+ //--------------------------------------------------------------------
+ // Types
+ //--------------------------------------------------------------------
+
+ public:
+ class CIterator;
+ friend class CIterator;
+
+ class Iterator;
+ friend class Iterator;
+
+ //--------------------------------------------------------------------
+ // Initializers and constructors
+ //--------------------------------------------------------------------
+
+ public:
+ // Constructors
+ SBuffer();
+ SBuffer(COUNT_T size);
+ SBuffer(const BYTE *buffer, COUNT_T size);
+ explicit SBuffer(const SBuffer &buffer);
+
+ // Immutable constructor should ONLY be used if buffer will
+ // NEVER BE FREED OR MODIFIED. PERIOD. .
+ SBuffer(ImmutableFlag immutable, const BYTE *buffer, COUNT_T size);
+
+ // Prealloc should be allocated inline with SBuffer - it must have the same
+ // lifetime as SBuffer's memory.
+ SBuffer(PreallocFlag prealloc, void *buffer, COUNT_T size);
+
+ ~SBuffer();
+
+ void Clear();
+
+ void Set(const SBuffer &buffer);
+ void Set(const BYTE *buffer, COUNT_T size);
+ void SetImmutable(const BYTE *buffer, COUNT_T size);
+
+ //--------------------------------------------------------------------
+ // Buffer size routines. A buffer has an externally visible size, but
+ // it also has an internal allocation size which may be larger.
+ //--------------------------------------------------------------------
+
+ // Get and set size of buffer. Note that the actual size of the
+ // internally allocated memory block may be bigger.
+ COUNT_T GetSize() const;
+ void SetSize(COUNT_T count);
+
+ // Grow size of buffer to maximum amount without reallocating.
+ void MaximizeSize();
+
+ //--------------------------------------------------------------------
+ // Buffer allocation routines
+ //--------------------------------------------------------------------
+
+ // Return the current available allocation space of the buffer.
+ COUNT_T GetAllocation() const;
+
+ // Preallocate some memory you expect to use. This can prevent
+ // multiple reallocations. Note this does not change the visible
+ // size of the buffer.
+ void Preallocate(COUNT_T allocation) const;
+
+ // Shrink memory usage of buffer to minimal amount. Note that
+ // this does not change the visible size of the buffer.
+ void Trim() const;
+
+ //--------------------------------------------------------------------
+ // Content manipulation routines
+ //--------------------------------------------------------------------
+
+ void Zero();
+ void Fill(BYTE value);
+ void Fill(const Iterator &to, BYTE value, COUNT_T size);
+
+ // Internal copy. "Copy" leaves from range as is; "Move"
+ // leaves from range in uninitialized state.
+ // (This distinction is more important when using from a
+ // typed wrapper than in the base SBuffer class.)
+ //
+ // NOTE: Copy vs Move is NOT based on whether ranges overlap
+ // or not. Ranges may overlap in either case.
+ //
+ // Note that both Iterators must be on THIS buffer.
+ void Copy(const Iterator &to, const CIterator &from, COUNT_T size);
+ void Move(const Iterator &to, const CIterator &from, COUNT_T size);
+
+ // External copy.
+ void Copy(const Iterator &i, const SBuffer &source);
+ void Copy(const Iterator &i, const void *source, COUNT_T size);
+ void Copy(void *dest, const CIterator &i, COUNT_T size);
+
+ // Insert bytes at the given iterator location.
+ void Insert(const Iterator &i, const SBuffer &source);
+ void Insert(const Iterator &i, COUNT_T size);
+
+ // Delete bytes at the given iterator location
+ void Delete(const Iterator &i, COUNT_T size);
+
+ // Replace bytes at the given iterator location
+ void Replace(const Iterator &i, COUNT_T deleteSize, const SBuffer &insert);
+ void Replace(const Iterator &i, COUNT_T deleteSize, COUNT_T insertSize);
+
+ // Compare entire buffer; return -1, 0, 1
+ int Compare(const SBuffer &compare) const;
+ int Compare(const BYTE *match, COUNT_T size) const;
+
+ // Compare entire buffer; return TRUE or FALSE
+ BOOL Equals(const SBuffer &compare) const;
+ BOOL Equals(const BYTE *match, COUNT_T size) const;
+
+ // Match portion of this buffer to given bytes; return TRUE or FALSE
+ BOOL Match(const CIterator &i, const SBuffer &match) const;
+ BOOL Match(const CIterator &i, const BYTE *match, COUNT_T size) const;
+
+ //--------------------------------------------------------------------
+ // Iterators
+ //
+ // Note that any iterator returned is not
+ // valid after any operation which may resize the buffer, unless
+ // the operation was performed on that particular iterator.
+ //--------------------------------------------------------------------
+
+ CIterator Begin() const;
+ CIterator End() const;
+
+ Iterator Begin();
+ Iterator End();
+
+ BYTE & operator[] (int index);
+ const BYTE & operator[] (int index) const;
+
+ //--------------------------------------------------------------------
+ // Raw buffer access
+ //
+ // Accessing a raw buffer via pointer is inherently more dangerous than
+ // other uses of this API, and should be avoided if at all possible.
+ // It is primarily provided for compatibility with existing APIs.
+ //
+ // Note that any buffer pointer returned is not
+ // valid after any operation which may resize the buffer.
+ //--------------------------------------------------------------------
+
+ // Casting operators return the existing buffer as
+ // a raw const pointer. Note that the pointer is valid only
+ // until the buffer is modified via an API.
+ operator const void *() const;
+ operator const BYTE *() const;
+
+ // To write directly to the SString's underlying buffer:
+ // 1) Call OpenRawBuffer() and pass it the count of bytes
+ // you need.
+ // 2) That returns a pointer to the raw buffer which you can write to.
+ // 3) When you are done writing to the pointer, call CloseBuffer()
+ // and pass it the count of bytes you actually wrote.
+ // The pointer from step 1 is now invalid.
+
+ // example usage:
+ // void GetInfo(SBuffer &buf)
+ // {
+ // BYTE *p = buf.OpenRawBuffer(3);
+ // OSGetSomeInfo(p, 3);
+ // buf.CloseRawBuffer();
+ // }
+
+ // You should open the buffer, write the data, and immediately close it.
+ // No sbuffer operations are valid while the buffer is opened.
+ //
+ // In a debug build, Open/Close will do lots of little checks to make sure
+ // you don't buffer overflow while it's opened. In a retail build, this
+ // is a very streamlined action.
+
+ // Open the raw buffer for writing count bytes
+ BYTE *OpenRawBuffer(COUNT_T maxCount);
+
+ // Call after OpenRawBuffer().
+
+ // Provide the count of bytes actually used. This will make sure the
+ // SBuffer's size is correct.
+ void CloseRawBuffer(COUNT_T actualCount);
+
+ // Close the buffer. Assumes that we completely filled the buffer
+ // that OpenRawBuffer() gave back.
+ void CloseRawBuffer();
+
+ //--------------------------------------------------------------------
+ // Check routines. These are typically used internally, but may be
+ // called externally if desired.
+ //--------------------------------------------------------------------
+
+ CHECK CheckBufferClosed() const;
+ static CHECK CheckSize(COUNT_T size);
+ static CHECK CheckAllocation(COUNT_T allocation);
+ CHECK CheckIteratorRange(const CIterator &i) const;
+ CHECK CheckIteratorRange(const CIterator &i, COUNT_T size) const;
+
+ CHECK Check() const;
+ CHECK Invariant() const;
+ CHECK InternalInvariant() const;
+
+ protected:
+
+ //--------------------------------------------------------------------
+ // Internal helper routines
+ //--------------------------------------------------------------------
+
+ // Preserve = preserve contents while reallocating
+ typedef enum
+ {
+ DONT_PRESERVE = 0,
+ PRESERVE = 1,
+ } Preserve;
+
+ void Resize(COUNT_T size, Preserve preserve = PRESERVE);
+ void ResizePadded(COUNT_T size, Preserve preserve = PRESERVE);
+ void TweakSize(COUNT_T size);
+ void ReallocateBuffer(COUNT_T allocation, Preserve preserve);
+ void EnsureMutable() const;
+
+ //--------------------------------------------------------------------
+ // We define some extra flags and fields for subclasses (these are specifically
+ // designed for SString, but use otherwise if desired.)
+ //--------------------------------------------------------------------
+
+ BOOL IsFlag1() const;
+ void SetFlag1();
+ void ClearFlag1();
+
+ BOOL IsFlag2() const;
+ void SetFlag2();
+ void ClearFlag2();
+
+ BOOL IsFlag3() const;
+ void SetFlag3();
+ void ClearFlag3();
+
+ INT GetRepresentationField() const;
+ void SetRepresentationField(int value);
+
+ protected:
+
+ //--------------------------------------------------------------------
+ // Flag access
+ //--------------------------------------------------------------------
+
+ BOOL IsAllocated() const;
+ void SetAllocated();
+ void ClearAllocated();
+
+ BOOL IsImmutable() const;
+ void SetImmutable();
+ void ClearImmutable();
+
+#if _DEBUG
+ BOOL IsOpened() const;
+ void SetOpened();
+ void ClearOpened();
+#endif
+
+ //--------------------------------------------------------------------
+ // Buffer management routines
+ //--------------------------------------------------------------------
+
+ // Allocate and free a memory buffer
+ BYTE *NewBuffer(COUNT_T allocation);
+ void DeleteBuffer(BYTE *buffer, COUNT_T allocation);
+
+ // Use existing buffer
+ BYTE *UseBuffer(BYTE *buffer, COUNT_T *allocation);
+
+ CHECK CheckBuffer(const BYTE* buffer, COUNT_T allocation) const;
+
+ // Manipulates contents of the buffer via the plugins below, but
+ // adds some debugging checks. Should always call through here rather
+ // than directly calling the extensibility points.
+ void DebugMoveBuffer(__out_bcount(size) BYTE *to, BYTE *from, COUNT_T size);
+ void DebugCopyConstructBuffer(__out_bcount(size) BYTE *to, const BYTE *from, COUNT_T size);
+ void DebugConstructBuffer(BYTE *buffer, COUNT_T size);
+ void DebugDestructBuffer(BYTE *buffer, COUNT_T size);
+
+ void DebugStompUnusedBuffer(BYTE *buffer, COUNT_T size);
+#ifdef _DEBUG
+ static BOOL EnsureGarbageCharOnly(const BYTE *buffer, COUNT_T size);
+#endif
+ CHECK CheckUnusedBuffer(const BYTE *buffer, COUNT_T size) const;
+
+#ifdef DACCESS_COMPILE
+public:
+
+ // Expose the raw Target address of the buffer to DAC.
+ // This does not do any marshalling. This can be useful if the caller wants to allocate the buffer on
+ // its own heap so that it can survive Flush calls.
+ MemoryRange DacGetRawBuffer() const
+ {
+ SUPPORTS_DAC;
+ PTR_VOID p = dac_cast<PTR_VOID>((TADDR) m_buffer);
+ return MemoryRange(p, GetSize());
+ }
+
+protected:
+
+ // Return a host copy of the buffer, allocated on the DAC heap (and thus invalidated at the next call to Flush).
+ void* DacGetRawContent(void) const
+ {
+ SUPPORTS_DAC;
+
+ // SBuffers are used in DAC in two ways - buffers in the host, and marshalled buffers from the target.
+ // This is a problem - we can't reason about the address space of the buffer statically, and instead rely on
+ // the dynamic usage (i.e. the methods are basically bifurcated into those you can use on host instances,
+ // and those you can use on marshalled copies).
+ // Ideally we'll have two versions of the SBuffer code - one that's marshalled (normal DACization) and one
+ // that isn't (host-only utility). This is the "dual-mode DAC problem".
+ // But this only affects a couple classes, and so for now we'll ignore the problem - causing a bunch of DacCop
+ // violations.
+ DACCOP_IGNORE(CastBetweenAddressSpaces, "SBuffer has the dual-mode DAC problem");
+ DACCOP_IGNORE(FieldAccess, "SBuffer has the dual-mode DAC problem");
+ TADDR bufAddr = (TADDR)m_buffer;
+
+ return DacInstantiateTypeByAddress(bufAddr, m_size, true);
+ }
+
+ void EnumMemoryRegions(CLRDataEnumMemoryFlags flags) const
+ {
+ SUPPORTS_DAC;
+
+ if (flags != CLRDATA_ENUM_MEM_TRIAGE)
+ {
+ DacEnumMemoryRegion((TADDR)m_buffer, m_size);
+ }
+ }
+#endif
+
+ //----------------------------------------------------------------------------
+ // Iterator base class
+ //----------------------------------------------------------------------------
+
+ friend class CheckedIteratorBase<SBuffer>;
+
+ class Index : public CheckedIteratorBase<SBuffer>
+ {
+ friend class SBuffer;
+
+ friend class CIterator;
+ friend class Indexer<const BYTE, CIterator>;
+
+ friend class Iterator;
+ friend class Indexer<BYTE, Iterator>;
+
+ protected:
+ BYTE* m_ptr;
+
+ Index();
+ Index(SBuffer *container, SCOUNT_T index);
+ BYTE &GetAt(SCOUNT_T delta) const;
+ void Skip(SCOUNT_T delta);
+ SCOUNT_T Subtract(const Index &i) const;
+
+ CHECK DoCheck(SCOUNT_T delta) const;
+
+ void Resync(const SBuffer *container, BYTE *value) const;
+ };
+
+ public:
+
+ class CIterator : public Index, public Indexer<const BYTE, CIterator>
+ {
+ friend class SBuffer;
+
+ public:
+ CIterator()
+ {
+ }
+
+ CIterator(const SBuffer *buffer, int index)
+ : Index(const_cast<SBuffer*>(buffer), index)
+ {
+ }
+ };
+
+ class Iterator : public Index, public Indexer<BYTE, Iterator>
+ {
+ friend class SBuffer;
+
+ public:
+ operator const CIterator &() const
+ {
+ return *(const CIterator *)this;
+ }
+
+ operator CIterator &()
+ {
+ return *(CIterator *)this;
+ }
+
+ Iterator()
+ {
+ }
+
+ Iterator(SBuffer *buffer, int index)
+ : Index(buffer, index)
+ {
+ }
+
+ };
+
+
+ //----------------------------------------------------------------------------
+ // Member and data declarations
+ //----------------------------------------------------------------------------
+
+ private:
+ enum
+ {
+ REPRESENTATION_MASK = 0x07,
+ ALLOCATED = 0x08,
+ IMMUTABLE = 0x10,
+ OPENED = 0x20,
+ FLAG1 = 0x40,
+ FLAG2 = 0x80,
+ FLAG3 = 0x100,
+ };
+
+ COUNT_T m_size; // externally visible size
+ COUNT_T m_allocation; // actual allocated size
+ UINT32 m_flags; // @todo: steal flags from sizes
+
+ protected:
+ union {
+ BYTE *m_buffer;
+ wchar_t *m_asStr; // For debugging, view as a unicode string
+ };
+
+#if _DEBUG
+ protected:
+ // We will update the "revision" of the buffer every time it is potentially reallocation,
+ // so we can tell when iterators are no longer valid.
+ int m_revision;
+#endif
+};
+
+// ================================================================================
+// InlineSBuffer : Tlempate for an SBuffer with preallocated buffer space
+// ================================================================================
+
+#define BUFFER_ALIGNMENT 4
+
+template <COUNT_T size>
+class InlineSBuffer : public SBuffer
+{
+ private:
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4200) // zero sized array
+#pragma warning(disable:4324) // don't complain if DECLSPEC_ALIGN actually pads
+ DECLSPEC_ALIGN(BUFFER_ALIGNMENT) BYTE m_prealloc[size];
+#pragma warning(pop)
+#else
+ // use UINT64 to get maximum alignment of the memory
+ UINT64 m_prealloc[ALIGN(size,sizeof(UINT64))/sizeof(UINT64)];
+#endif // _MSC_VER
+
+ public:
+ InlineSBuffer()
+ : SBuffer(Prealloc, (BYTE*)m_prealloc, size)
+ {
+ WRAPPER_NO_CONTRACT;
+ }
+};
+
+
+// a 1K sized buffer filled with $ that we'll use in debug builds for verification
+#define GARBAGE_FILL_DWORD 0x24242424 // $$$$
+#define GARBAGE_FILL_BUFFER_ITEMS 16
+#define GARBAGE_FILL_BUFFER_SIZE GARBAGE_FILL_BUFFER_ITEMS*sizeof(DWORD)
+// ================================================================================
+// StackSBuffer : SBuffer with relatively large preallocated buffer for stack use
+// ================================================================================
+
+#define STACK_ALLOC 256
+
+typedef InlineSBuffer<STACK_ALLOC> StackSBuffer;
+
+// ================================================================================
+// Inline definitions
+// ================================================================================
+
+/// a wrapper for templates and such, that use "==".
+/// more expensive than a typical "==", though
+inline BOOL operator == (const SBuffer& b1,const SBuffer& b2)
+{
+ return b1.Equals(b2);
+};
+
+#include <sbuffer.inl>
+
+#endif // _SBUFFER_H_