blob: 5a3f0c2508e51227a17594b962bae5c96490a809 [file] [log] [blame]
/*
* Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "config.h"
#include "list.h"
#include "internal.h"
#include <algorithm>
#define DUMP_STATISTICS 0
using std::min;
namespace KJS {
// tunable parameters
const int poolSize = 512;
const int inlineValuesSize = 5;
enum ListImpState { unusedInPool = 0, usedInPool, usedOnHeap, immortal };
struct ListImp : ListImpBase
{
ListImpState state;
int capacity;
JSValue** overflow;
union {
JSValue *values[inlineValuesSize];
ListImp *nextInFreeList;
};
#if DUMP_STATISTICS
int sizeHighWaterMark;
#endif
void markValues();
};
struct HeapListImp : ListImp
{
HeapListImp *nextInHeapList;
HeapListImp *prevInHeapList;
};
static ListImp pool[poolSize];
static ListImp *poolFreeList;
static HeapListImp *heapList;
static int poolUsed;
#if DUMP_STATISTICS
static int numLists;
static int numListsHighWaterMark;
static int listSizeHighWaterMark;
static int numListsDestroyed;
static int numListsBiggerThan[17];
struct ListStatisticsExitLogger { ~ListStatisticsExitLogger(); };
static ListStatisticsExitLogger logger;
ListStatisticsExitLogger::~ListStatisticsExitLogger()
{
printf("\nKJS::List statistics:\n\n");
printf("%d lists were allocated\n", numLists);
printf("%d lists was the high water mark\n", numListsHighWaterMark);
printf("largest list had %d elements\n", listSizeHighWaterMark);
if (numListsDestroyed) {
putc('\n', stdout);
for (int i = 0; i < 17; i++) {
printf("%.1f%% of the lists (%d) had more than %d element%s\n",
100.0 * numListsBiggerThan[i] / numListsDestroyed,
numListsBiggerThan[i],
i, i == 1 ? "" : "s");
}
putc('\n', stdout);
}
}
#endif
inline void ListImp::markValues()
{
int inlineSize = min(size, inlineValuesSize);
for (int i = 0; i != inlineSize; ++i) {
if (!values[i]->marked()) {
values[i]->mark();
}
}
int overflowSize = size - inlineSize;
for (int i = 0; i != overflowSize; ++i) {
if (!overflow[i]->marked()) {
overflow[i]->mark();
}
}
}
void List::markProtectedLists()
{
int seen = 0;
int used = poolUsed;
for (int i = 0; i < poolSize && seen < used; i++) {
if (pool[i].state == usedInPool) {
seen++;
if (pool[i].valueRefCount > 0) {
pool[i].markValues();
}
}
}
for (HeapListImp *l = heapList; l; l = l->nextInHeapList) {
if (l->valueRefCount > 0) {
l->markValues();
}
}
}
static inline ListImp *allocateListImp()
{
ASSERT(JSLock::lockCount() > 0);
// Find a free one in the pool.
if (poolUsed < poolSize) {
ListImp *imp = poolFreeList ? poolFreeList : &pool[0];
poolFreeList = imp->nextInFreeList ? imp->nextInFreeList : imp + 1;
imp->state = usedInPool;
poolUsed++;
return imp;
}
HeapListImp *imp = new HeapListImp;
imp->state = usedOnHeap;
// link into heap list
if (heapList) {
heapList->prevInHeapList = imp;
}
imp->nextInHeapList = heapList;
imp->prevInHeapList = NULL;
heapList = imp;
return imp;
}
List::List() : _impBase(allocateListImp())
{
ListImp *imp = static_cast<ListImp *>(_impBase);
imp->size = 0;
imp->refCount = 1;
imp->valueRefCount = 1;
imp->capacity = 0;
imp->overflow = 0;
#if DUMP_STATISTICS
if (++numLists > numListsHighWaterMark)
numListsHighWaterMark = numLists;
imp->sizeHighWaterMark = 0;
#endif
}
void List::markValues()
{
static_cast<ListImp *>(_impBase)->markValues();
}
void List::release()
{
ASSERT(JSLock::lockCount() > 0);
ListImp *imp = static_cast<ListImp *>(_impBase);
#if DUMP_STATISTICS
--numLists;
++numListsDestroyed;
for (int i = 0; i < 17; i++)
if (imp->sizeHighWaterMark > i)
++numListsBiggerThan[i];
#endif
delete [] imp->overflow;
imp->overflow = 0;
if (imp->state == usedInPool) {
imp->state = unusedInPool;
imp->nextInFreeList = poolFreeList;
poolFreeList = imp;
poolUsed--;
} else {
ASSERT(imp->state == usedOnHeap);
HeapListImp *list = static_cast<HeapListImp *>(imp);
// unlink from heap list
if (!list->prevInHeapList) {
heapList = list->nextInHeapList;
if (heapList) {
heapList->prevInHeapList = NULL;
}
} else {
list->prevInHeapList->nextInHeapList = list->nextInHeapList;
if (list->nextInHeapList) {
list->nextInHeapList->prevInHeapList = list->prevInHeapList;
}
}
delete list;
}
}
JSValue *List::at(int i) const
{
ListImp *imp = static_cast<ListImp *>(_impBase);
if ((unsigned)i >= (unsigned)imp->size)
return jsUndefined();
if (i < inlineValuesSize)
return imp->values[i];
return imp->overflow[i - inlineValuesSize];
}
void List::clear()
{
_impBase->size = 0;
}
void List::append(JSValue *v)
{
ASSERT(JSLock::lockCount() > 0);
ListImp *imp = static_cast<ListImp *>(_impBase);
int i = imp->size++;
#if DUMP_STATISTICS
if (imp->size > listSizeHighWaterMark)
listSizeHighWaterMark = imp->size;
#endif
if (i < inlineValuesSize) {
imp->values[i] = v;
return;
}
if (i >= imp->capacity) {
int newCapacity = i * 2;
JSValue** newOverflow = new JSValue* [newCapacity - inlineValuesSize];
JSValue** oldOverflow = imp->overflow;
int oldOverflowSize = i - inlineValuesSize;
for (int j = 0; j != oldOverflowSize; j++)
newOverflow[j] = oldOverflow[j];
delete [] oldOverflow;
imp->overflow = newOverflow;
imp->capacity = newCapacity;
}
imp->overflow[i - inlineValuesSize] = v;
}
List List::copy() const
{
List copy;
copy.copyFrom(*this);
return copy;
}
void List::copyFrom(const List& other)
{
ListImp *imp = static_cast<ListImp *>(other._impBase);
int size = imp->size;
int inlineSize = min(size, inlineValuesSize);
for (int i = 0; i != inlineSize; ++i)
append(imp->values[i]);
JSValue** overflow = imp->overflow;
int overflowSize = size - inlineSize;
for (int i = 0; i != overflowSize; ++i)
append(overflow[i]);
}
List List::copyTail() const
{
List copy;
ListImp *imp = static_cast<ListImp *>(_impBase);
int size = imp->size;
int inlineSize = min(size, inlineValuesSize);
for (int i = 1; i < inlineSize; ++i)
copy.append(imp->values[i]);
JSValue** overflow = imp->overflow;
int overflowSize = size - inlineSize;
for (int i = 0; i < overflowSize; ++i)
copy.append(overflow[i]);
return copy;
}
const List& List::empty()
{
static List* staticEmptyList = new List;
return *staticEmptyList;
}
List &List::operator=(const List &b)
{
ListImpBase *bImpBase = b._impBase;
++bImpBase->refCount;
++bImpBase->valueRefCount;
deref();
_impBase = bImpBase;
return *this;
}
} // namespace KJS