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
61typedef DPTR(class SBuffer) PTR_SBuffer;
62
63class 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
367public:
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
379protected:
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
526template <COUNT_T size>
527class 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
560typedef 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
568inline 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