1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "StackArena.h"
8 #include "nsAlgorithm.h"
13 // A block of memory that the stack will chop up and hand out.
15 // Subtract sizeof(StackBlock*) to give space for the |mNext| field.
16 static const size_t MAX_USABLE_SIZE
= 4096 - sizeof(StackBlock
*);
19 char mBlock
[MAX_USABLE_SIZE
];
21 // Another block of memory that would only be created if our stack
25 StackBlock() : mNext(nullptr) {}
26 ~StackBlock() = default;
29 static_assert(sizeof(StackBlock
) == 4096, "StackBlock must be 4096 bytes");
31 // We hold an array of marks. A push pushes a mark on the stack.
34 // The block of memory from which we are currently handing out chunks.
37 // Our current position in the block.
41 StackArena
* AutoStackArena::gStackArena
;
43 StackArena::StackArena() {
47 // Allocate our stack memory.
48 mBlocks
= new StackBlock();
55 StackArena::~StackArena() {
59 StackBlock
* toDelete
= mBlocks
;
60 mBlocks
= mBlocks
->mNext
;
65 size_t StackArena::SizeOfExcludingThis(
66 mozilla::MallocSizeOf aMallocSizeOf
) const {
68 StackBlock
* block
= mBlocks
;
70 n
+= aMallocSizeOf(block
);
73 n
+= aMallocSizeOf(mMarks
);
77 static const int STACK_ARENA_MARK_INCREMENT
= 50;
79 void StackArena::Push() {
80 // Resize the mark array if we overrun it. Failure to allocate the
81 // mark array is not fatal; we just won't free to that mark. This
82 // allows callers not to worry about error checking.
83 if (mStackTop
>= mMarkLength
) {
84 uint32_t newLength
= mStackTop
+ STACK_ARENA_MARK_INCREMENT
;
85 StackMark
* newMarks
= new StackMark
[newLength
];
88 memcpy(newMarks
, mMarks
, sizeof(StackMark
) * mMarkLength
);
90 // Fill in any marks that we couldn't allocate during a prior call
92 for (; mMarkLength
< mStackTop
; ++mMarkLength
) {
93 MOZ_ASSERT_UNREACHABLE("should only hit this on out-of-memory");
94 newMarks
[mMarkLength
].mBlock
= mCurBlock
;
95 newMarks
[mMarkLength
].mPos
= mPos
;
99 mMarkLength
= newLength
;
103 // Set a mark at the top (if we can).
104 NS_ASSERTION(mStackTop
< mMarkLength
, "out of memory");
105 if (mStackTop
< mMarkLength
) {
106 mMarks
[mStackTop
].mBlock
= mCurBlock
;
107 mMarks
[mStackTop
].mPos
= mPos
;
113 void* StackArena::Allocate(size_t aSize
) {
114 NS_ASSERTION(mStackTop
> 0, "Allocate called without Push");
116 // Align to a multiple of 8.
117 aSize
= NS_ROUNDUP
<size_t>(aSize
, 8);
119 // On stack overflow, grab another block.
120 if (mPos
+ aSize
>= StackBlock::MAX_USABLE_SIZE
) {
121 NS_ASSERTION(aSize
<= StackBlock::MAX_USABLE_SIZE
,
122 "Requested memory is greater that our block size!!");
123 if (mCurBlock
->mNext
== nullptr) {
124 mCurBlock
->mNext
= new StackBlock();
127 mCurBlock
= mCurBlock
->mNext
;
131 // Return the chunk they need.
132 void* result
= mCurBlock
->mBlock
+ mPos
;
138 void StackArena::Pop() {
140 NS_ASSERTION(mStackTop
> 0, "unmatched pop");
143 if (mStackTop
>= mMarkLength
) {
144 // We couldn't allocate the marks array at the time of the push, so
145 // we don't know where we're freeing to.
146 MOZ_ASSERT_UNREACHABLE("out of memory");
147 if (mStackTop
== 0) {
148 // But we do know if we've completely pushed the stack.
156 // Mark the "freed" memory with 0xdd to help with debugging of memory
157 // allocation problems.
159 StackBlock
*block
= mMarks
[mStackTop
].mBlock
, *block_end
= mCurBlock
;
160 size_t pos
= mMarks
[mStackTop
].mPos
;
161 for (; block
!= block_end
; block
= block
->mNext
, pos
= 0) {
162 memset(block
->mBlock
+ pos
, 0xdd, sizeof(block
->mBlock
) - pos
);
164 memset(block
->mBlock
+ pos
, 0xdd, mPos
- pos
);
168 mCurBlock
= mMarks
[mStackTop
].mBlock
;
169 mPos
= mMarks
[mStackTop
].mPos
;
172 } // namespace mozilla