1 /*
2  *  Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Library General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2 of the License, or (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Library General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Library General Public License
15  *  along with this library; see the file COPYING.LIB.  If not, write to
16  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  *  Boston, MA 02110-1301, USA.
18  *
19  */
20 
21 #include "list.h"
22 
23 #include "internal.h"
24 #include <algorithm>
25 
26 #define DUMP_STATISTICS 0
27 
28 using std::min;
29 
30 namespace KJS
31 {
32 
33 // tunable parameters
34 const int poolSize = 512;
35 
36 enum ListImpState { unusedInPool = 0, usedInPool, usedOnHeap, immortal };
37 
38 struct ListImp : ListImpBase {
39     ListImpState state;
40 
41     union {
42         int capacity; // or 0 if data is inline
43         ListImp *nextInFreeList;
44     };
45 
46     LocalStorageEntry values[inlineListValuesSize];
47 
48 #if DUMP_STATISTICS
49     int sizeHighWaterMark;
50 #endif
51 
52     void markValues();
53 };
54 
55 struct HeapListImp : ListImp {
56     HeapListImp *nextInHeapList;
57     HeapListImp *prevInHeapList;
58 };
59 
60 static ListImp pool[poolSize];
61 static ListImp *poolFreeList;
62 static HeapListImp *heapList;
63 static int poolUsed;
64 
65 #if DUMP_STATISTICS
66 
67 static int numLists;
68 static int numListsHighWaterMark;
69 
70 static int listSizeHighWaterMark;
71 
72 static int numListsDestroyed;
73 static int numListsBiggerThan[17];
74 
75 struct ListStatisticsExitLogger {
76     ~ListStatisticsExitLogger();
77 };
78 
79 static ListStatisticsExitLogger logger;
80 
~ListStatisticsExitLogger()81 ListStatisticsExitLogger::~ListStatisticsExitLogger()
82 {
83     printf("\nKJS::List statistics:\n\n");
84     printf("%d lists were allocated\n", numLists);
85     printf("%d lists was the high water mark\n", numListsHighWaterMark);
86     printf("largest list had %d elements\n", listSizeHighWaterMark);
87     if (numListsDestroyed) {
88         putc('\n', stdout);
89         for (int i = 0; i < 17; i++) {
90             printf("%.1f%% of the lists (%d) had more than %d element%s\n",
91                    100.0 * numListsBiggerThan[i] / numListsDestroyed,
92                    numListsBiggerThan[i],
93                    i, i == 1 ? "" : "s");
94         }
95         putc('\n', stdout);
96     }
97 }
98 
99 #endif
100 
markValues()101 inline void ListImp::markValues()
102 {
103     for (int i = 0; i != size; ++i) {
104         if (!JSValue::marked(data[i].val.valueVal)) {
105             JSValue::mark(data[i].val.valueVal);
106         }
107     }
108 }
109 
markProtectedLists()110 void List::markProtectedLists()
111 {
112     int seen = 0;
113     int used = poolUsed;
114 
115     for (int i = 0; i < poolSize && seen < used; i++) {
116         if (pool[i].state == usedInPool) {
117             seen++;
118             pool[i].markValues();
119         }
120     }
121 
122     for (HeapListImp *l = heapList; l; l = l->nextInHeapList) {
123         l->markValues();
124     }
125 }
126 
allocateListImp()127 static inline ListImp *allocateListImp()
128 {
129     // Find a free one in the pool.
130     if (poolUsed < poolSize) {
131         ListImp *imp = poolFreeList ? poolFreeList : &pool[0];
132         poolFreeList = imp->nextInFreeList ? imp->nextInFreeList : imp + 1;
133         imp->state = usedInPool;
134         poolUsed++;
135         return imp;
136     }
137 
138     HeapListImp *imp = new HeapListImp;
139     imp->state = usedOnHeap;
140     // link into heap list
141     if (heapList) {
142         heapList->prevInHeapList = imp;
143     }
144     imp->nextInHeapList = heapList;
145     imp->prevInHeapList = nullptr;
146     heapList = imp;
147 
148     return imp;
149 }
150 
List()151 List::List() : _impBase(allocateListImp())
152 {
153     ListImp *imp = static_cast<ListImp *>(_impBase);
154     imp->size = 0;
155     imp->refCount = 1;
156     imp->capacity = 0;
157     imp->data     = imp->values;
158 
159 #if DUMP_STATISTICS
160     if (++numLists > numListsHighWaterMark) {
161         numListsHighWaterMark = numLists;
162     }
163     imp->sizeHighWaterMark = 0;
164 #endif
165 }
166 
release()167 void List::release()
168 {
169     ListImp *imp = static_cast<ListImp *>(_impBase);
170 
171 #if DUMP_STATISTICS
172     if (imp->size > imp->sizeHighWaterMark) {
173         imp->sizeHighWaterMark = imp->size;
174     }
175 
176     --numLists;
177     ++numListsDestroyed;
178     for (int i = 0; i < 17; i++)
179         if (imp->sizeHighWaterMark > i) {
180             ++numListsBiggerThan[i];
181         }
182 #endif
183 
184     if (imp->capacity) {
185         delete [] imp->data;
186     }
187     imp->data = nullptr;
188 
189     if (imp->state == usedInPool) {
190         imp->state = unusedInPool;
191         imp->nextInFreeList = poolFreeList;
192         poolFreeList = imp;
193         poolUsed--;
194     } else {
195         assert(imp->state == usedOnHeap);
196         HeapListImp *list = static_cast<HeapListImp *>(imp);
197 
198         // unlink from heap list
199         if (!list->prevInHeapList) {
200             heapList = list->nextInHeapList;
201             if (heapList) {
202                 heapList->prevInHeapList = nullptr;
203             }
204         } else {
205             list->prevInHeapList->nextInHeapList = list->nextInHeapList;
206             if (list->nextInHeapList) {
207                 list->nextInHeapList->prevInHeapList = list->prevInHeapList;
208             }
209         }
210 
211         delete list;
212     }
213 }
214 
appendSlowCase(JSValue * v)215 void List::appendSlowCase(JSValue *v)
216 {
217     ListImp *imp = static_cast<ListImp *>(_impBase);
218 
219     int i = imp->size++; // insert index/old size
220 
221 #if DUMP_STATISTICS
222     if (imp->size > listSizeHighWaterMark) {
223         listSizeHighWaterMark = imp->size;
224     }
225 #endif
226 
227     // If we got here, we need to use an out-of-line buffer.
228 
229     if (i >= imp->capacity) {
230         int newCapacity = i * 2;
231 
232         LocalStorageEntry *newBuffer = new LocalStorageEntry[newCapacity];
233 
234         // Copy everything over
235         for (int c = 0; c < i; ++c) {
236             newBuffer[c] = imp->data[c];
237         }
238 
239         if (imp->capacity) { // had an old out-of-line buffer
240             delete[] imp->data;
241         }
242 
243         imp->data     = newBuffer;
244         imp->capacity = newCapacity;
245     }
246 
247     imp->data[i].val.valueVal = v;
248 }
249 
copy() const250 List List::copy() const
251 {
252     List copy;
253     copy.copyFrom(*this);
254     return copy;
255 }
256 
copyFrom(const List & other)257 void List::copyFrom(const List &other)
258 {
259     // Assumption: we're empty (e.g. called from copy)
260     ListImpBase *otherImp = other._impBase;
261     ListImp *ourImp       = static_cast<ListImp *>(_impBase);
262 
263     assert(ourImp->size == 0 && ourImp->capacity == 0);
264 
265     int size = otherImp->size;
266     ourImp->size     = size;
267 
268     if (size > inlineListValuesSize) {
269         // need an out-of-line buffer
270         ourImp->capacity = size;
271         ourImp->data     = new LocalStorageEntry[size];
272     } else {
273         ourImp->capacity = 0;
274     }
275 
276     for (int c = 0; c < size; ++c) {
277         ourImp->data[c] = otherImp->data[c];
278     }
279 }
280 
copyTail() const281 List List::copyTail() const
282 {
283     List copy;
284 
285     ListImpBase *inImp  = _impBase;
286     ListImp     *outImp = static_cast<ListImp *>(copy._impBase);
287 
288     int size      = inImp->size - 1;
289 
290     if (size < 0) {
291         size = 0;    // copyTail on empty list.
292     }
293 
294     outImp->size  = size;
295 
296     if (size > inlineListValuesSize) {
297         // need an out-of-line buffer
298         outImp->capacity = size;
299         outImp->data     = new LocalStorageEntry[size];
300     } else {
301         outImp->capacity = 0;
302     }
303 
304     for (int c = 0; c < size; ++c) {
305         outImp->data[c] = inImp->data[c + 1];
306     }
307 
308     return copy;
309 }
310 
empty()311 const List &List::empty()
312 {
313     static List emptyList;
314     return emptyList;
315 }
316 
operator =(const List & b)317 List &List::operator=(const List &b)
318 {
319     ListImpBase *bImpBase = b._impBase;
320     ++bImpBase->refCount;
321     deref();
322     _impBase = bImpBase;
323     return *this;
324 }
325 
326 } // namespace KJS
327 
328