/* * 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 #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(_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(_impBase)->markValues(); } void List::release() { ASSERT(JSLock::lockCount() > 0); ListImp *imp = static_cast(_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(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(_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(_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(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(_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