blob: b9621600f512c59e59ae2b5e290acc07f1f6db4b [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "LifoAlloc.h"
using namespace js;
namespace js {
namespace detail {
BumpChunk *
BumpChunk::new_(size_t chunkSize)
{
JS_ASSERT(RoundUpPow2(chunkSize) == chunkSize);
void *mem = js_malloc(chunkSize);
if (!mem)
return NULL;
BumpChunk *result = new (mem) BumpChunk(chunkSize - sizeof(BumpChunk));
// We assume that the alignment of sAlign is less than that of
// the underlying memory allocator -- creating a new BumpChunk should
// always satisfy the sAlign alignment constraint.
JS_ASSERT(AlignPtr(result->bump) == result->bump);
return result;
}
void
BumpChunk::delete_(BumpChunk *chunk)
{
#ifdef DEBUG
// Part of the chunk may have been marked as poisoned/noaccess. Undo that
// before writing the 0xcd bytes.
size_t size = sizeof(*chunk) + chunk->bumpSpaceSize;
MOZ_MAKE_MEM_UNDEFINED(chunk, size);
memset(chunk, 0xcd, size);
#endif
js_free(chunk);
}
bool
BumpChunk::canAlloc(size_t n)
{
char *aligned = AlignPtr(bump);
char *bumped = aligned + n;
return bumped <= limit && bumped > headerBase();
}
} // namespace detail
} // namespace js
void
LifoAlloc::freeAll()
{
while (first) {
BumpChunk *victim = first;
first = first->next();
decrementCurSize(victim->computedSizeOfIncludingThis());
BumpChunk::delete_(victim);
}
first = latest = last = NULL;
// Nb: maintaining curSize_ correctly isn't easy. Fortunately, this is an
// excellent sanity check.
JS_ASSERT(curSize_ == 0);
}
LifoAlloc::BumpChunk *
LifoAlloc::getOrCreateChunk(size_t n)
{
if (first) {
// Look for existing, unused BumpChunks to satisfy the request.
while (latest->next()) {
latest = latest->next();
latest->resetBump(); // This was an unused BumpChunk on the chain.
if (latest->canAlloc(n))
return latest;
}
}
size_t defaultChunkFreeSpace = defaultChunkSize_ - sizeof(BumpChunk);
size_t chunkSize;
if (n > defaultChunkFreeSpace) {
size_t allocSizeWithHeader = n + sizeof(BumpChunk);
// Guard for overflow.
if (allocSizeWithHeader < n ||
(allocSizeWithHeader & (size_t(1) << (tl::BitSize<size_t>::result - 1)))) {
return NULL;
}
chunkSize = RoundUpPow2(allocSizeWithHeader);
} else {
chunkSize = defaultChunkSize_;
}
// If we get here, we couldn't find an existing BumpChunk to fill the request.
BumpChunk *newChunk = BumpChunk::new_(chunkSize);
if (!newChunk)
return NULL;
if (!first) {
latest = first = last = newChunk;
} else {
JS_ASSERT(latest && !latest->next());
latest->setNext(newChunk);
latest = last = newChunk;
}
size_t computedChunkSize = newChunk->computedSizeOfIncludingThis();
JS_ASSERT(computedChunkSize == chunkSize);
incrementCurSize(computedChunkSize);
return newChunk;
}
void
LifoAlloc::transferFrom(LifoAlloc *other)
{
JS_ASSERT(!markCount);
JS_ASSERT(latest == first);
JS_ASSERT(!other->markCount);
if (!other->first)
return;
incrementCurSize(other->curSize_);
append(other->first, other->last);
other->first = other->last = other->latest = NULL;
other->curSize_ = 0;
}
void
LifoAlloc::transferUnusedFrom(LifoAlloc *other)
{
JS_ASSERT(!markCount);
JS_ASSERT(latest == first);
if (other->markCount || !other->first)
return;
// Transfer all chunks *after* |latest|.
if (other->latest->next()) {
if (other->latest == other->first) {
// We're transferring everything except the first chunk.
size_t delta = other->curSize_ - other->first->computedSizeOfIncludingThis();
other->decrementCurSize(delta);
incrementCurSize(delta);
} else {
for (BumpChunk *chunk = other->latest->next(); chunk; chunk = chunk->next()) {
size_t size = chunk->computedSizeOfIncludingThis();
incrementCurSize(size);
other->decrementCurSize(size);
}
}
append(other->latest->next(), other->last);
other->latest->setNext(NULL);
other->last = other->latest;
}
}