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 | |