2 * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #import <JavaScriptCore/Assertions.h>
30 #import <WebKit/WebKitLogging.h>
31 #import <WebKit/WebLRUFileList.h>
38 #define RemoveFromHeap (TRUE)
39 #define DontRemoveFromHeap (FALSE)
45 CFMutableDictionaryRef dict;
47 unsigned int totalSize;
50 typedef struct NSLRUFileData NSLRUFileData;
54 unsigned long fileSize;
55 CFTimeInterval heapTime;
56 CFTimeInterval updatedTime;
57 unsigned int markRemoved;
58 char path[1]; // variable size, big enough to hold path string and trailing nul
61 static CFComparisonResult compareTimes(const void *val1, const void *val2, void *context);
62 static Boolean cStringEqual(const void *val1, const void *val2);
63 static CFHashCode cStringHash(const void *val);
64 static Boolean NSLRUFileDataEqual(const void *val1, const void *val2);
66 static NSLRUFileData *WebLRUFileListGetOldestFileData(WebLRUFileList *list, Boolean removeFromHeap);
68 static void NSLRUFileDataReleaseApplierFunction(const void *key, const void *value, void *context);
69 static void NSLRUFileDataRelease(CFAllocatorRef allocator, const void *value);
72 WebLRUFileList *WebLRUFileListCreate(void)
74 CFBinaryHeapCallBacks heapCallbacks = {0, NULL, NULL, NULL, compareTimes};
75 CFBinaryHeapCompareContext heapCompareContext = {0, NULL, NULL, NULL, NULL};
76 CFDictionaryKeyCallBacks dictKeyCallbacks = {0, NULL, NULL, NULL, cStringEqual, cStringHash};
77 CFDictionaryValueCallBacks dictValCallbacks = {0, NULL, NULL, NULL, NSLRUFileDataEqual};
79 WebLRUFileList *list = malloc(sizeof(WebLRUFileList));
81 list->rootDirectory = NULL;
82 list->heap = CFBinaryHeapCreate(NULL, 0, &heapCallbacks, &heapCompareContext);
83 list->dict = CFDictionaryCreateMutable(NULL, 0, &dictKeyCallbacks, &dictValCallbacks);
90 void WebLRUFileListRelease(WebLRUFileList *list)
93 free(list->rootDirectory);
94 WebLRUFileListRemoveAllFilesFromList(list);
95 CFRelease(list->heap);
96 CFRelease(list->dict);
100 int WebLRUFileListRebuildFileDataUsingRootDirectory(WebLRUFileList *list, const char *path)
108 struct stat statInfo;
110 if (stat(path, &statInfo) == -1) {
113 if ((statInfo.st_mode & S_IFDIR) == 0) {
117 WebLRUFileListRemoveAllFilesFromList(list);
118 free(list->rootDirectory);
119 list->rootDirectory = strdup(path);
120 int pathLength = strlen(path);
122 paths[0] = (char *)path;
125 fts = fts_open(paths, FTS_COMFOLLOW | FTS_LOGICAL, NULL);
127 while ((ent = fts_read(fts))) {
128 if (ent->fts_statp->st_mode & S_IFREG) {
129 const char *filePath = ent->fts_accpath + pathLength + 1;
130 WebLRUFileListSetFileData(list, filePath, ent->fts_statp->st_size, ent->fts_statp->st_ctimespec.tv_sec - kCFAbsoluteTimeIntervalSince1970);
139 unsigned int WebLRUFileListRemoveFileWithPath(WebLRUFileList *list, const char *path)
144 unsigned int removedFileSize = 0;
146 NSLRUFileData *data = (NSLRUFileData *)CFDictionaryGetValue(list->dict, path);
148 CFDictionaryRemoveValue(list->dict, path);
149 data->markRemoved = 1;
150 removedFileSize = data->fileSize;
151 list->totalSize -= data->fileSize;
152 ASSERT(list->count > 0);
156 return removedFileSize;
159 void WebLRUFileListTouchFileWithPath(WebLRUFileList *list, const char *path)
164 NSLRUFileData *data = (NSLRUFileData *)CFDictionaryGetValue(list->dict, path);
166 // This is not the same as the "real" mod time of the file on disk
167 // but it is close enough for our purposes
168 data->updatedTime = CFAbsoluteTimeGetCurrent();
172 void WebLRUFileListSetFileData(WebLRUFileList *list, const char *path, unsigned long fileSize, CFTimeInterval time)
177 NSLRUFileData *data = (NSLRUFileData *)CFDictionaryGetValue(list->dict, path);
179 // update existing data
180 list->totalSize -= data->fileSize;
181 list->totalSize += fileSize;
182 data->fileSize = fileSize;
183 data->updatedTime = time;
186 // create new data object
187 // malloc a block that can accommodate the string
188 // account for the size of the string when
189 // allocating the correct size block.
190 size_t mallocSize = offsetof(NSLRUFileData, path) + strlen(path) + 1;
191 data = malloc(mallocSize);
192 data->fileSize = fileSize;
193 data->heapTime = data->updatedTime = time;
194 data->markRemoved = 0;
195 strcpy(data->path, path);
196 list->totalSize += fileSize;
198 CFBinaryHeapAddValue(list->heap, data);
199 CFDictionarySetValue(list->dict, data->path, data);
203 Boolean WebLRUFileListGetPathOfOldestFile(WebLRUFileList *list, char *buffer, unsigned int length)
207 NSLRUFileData *data = WebLRUFileListGetOldestFileData(list, DontRemoveFromHeap);
210 unsigned pathLength = strlen(data->path);
211 if (length - 1 >= pathLength) {
212 strcpy(buffer, data->path);
220 unsigned int WebLRUFileListRemoveOldestFileFromList(WebLRUFileList *list)
224 if (list->count == 0) {
228 NSLRUFileData *data = WebLRUFileListGetOldestFileData(list, RemoveFromHeap);
231 LOG_ERROR("list->count > 0, but no data returned from WebLRUFileListGetOldestFileData");
234 // no need to remove from heap explicitly
235 // WebLRUFileListGetOldestFileData with RemoveFromHeap call does that
236 CFDictionaryRemoveValue(list->dict, data->path);
237 ASSERT(list->count > 0);
239 unsigned int removedFileSize = data->fileSize;
240 list->totalSize -= removedFileSize;
241 LOG(FileDatabaseActivity, "\n\t%s : %ld : %.0f : %.0f\n", data->path, data->fileSize, data->heapTime, data->updatedTime);
242 NSLRUFileDataRelease(NULL, data);
243 return removedFileSize;
246 Boolean WebLRUFileListContainsItem(WebLRUFileList *list, const char *path)
251 if (list->count == 0) {
255 NSLRUFileData *data = (NSLRUFileData *)CFDictionaryGetValue(list->dict, path);
263 unsigned int WebLRUFileListGetFileSize(WebLRUFileList *list, const char *path)
268 unsigned int result = 0;
270 if (list->count == 0) {
274 NSLRUFileData *data = (NSLRUFileData *)CFDictionaryGetValue(list->dict, path);
276 LOG_ERROR("list->count > 0, but no data returned from CFDictionaryGetValue with path: %s", path);
279 result = data->fileSize;
284 unsigned int WebLRUFileListCountItems(WebLRUFileList *list)
290 unsigned int WebLRUFileListGetTotalSize(WebLRUFileList *list)
293 return list->totalSize;
296 void WebLRUFileListRemoveAllFilesFromList(WebLRUFileList *list)
300 // use dictionary applier function to free all NSLRUFileData objects
301 CFDictionaryApplyFunction(list->dict, NSLRUFileDataReleaseApplierFunction, NULL);
303 CFDictionaryRemoveAllValues(list->dict);
304 CFBinaryHeapRemoveAllValues(list->heap);
309 static CFComparisonResult compareTimes(const void *val1, const void *val2, void *context)
314 CFTimeInterval t1 = ((NSLRUFileData *)val1)->heapTime;
315 CFTimeInterval t2 = ((NSLRUFileData *)val2)->heapTime;
317 if (t1 > t2) return kCFCompareGreaterThan;
318 if (t1 < t2) return kCFCompareLessThan;
319 return kCFCompareEqualTo;
322 static Boolean cStringEqual(const void *val1, const void *val2)
327 const char *s1 = (const char *)val1;
328 const char *s2 = (const char *)val2;
329 return strcmp(s1, s2) == 0;
332 // Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's
333 // or anything like that.
334 static const unsigned PHI = 0x9e3779b9U;
336 // This hash algorithm comes from:
337 // http://burtleburtle.net/bob/hash/hashfaq.html
338 // http://burtleburtle.net/bob/hash/doobs.html
339 static CFHashCode cStringHash(const void *val)
343 const char *s = (const char *)val;
344 int length = strlen(s);
345 int prefixLength = length < 8 ? length : 8;
346 int suffixPosition = length < 16 ? 8 : length - 8;
354 for (i = 0; i < prefixLength; i++) {
355 h += (unsigned char)s[i];
359 for (i = suffixPosition; i < length; i++) {
360 h += (unsigned char)s[i];
372 static Boolean NSLRUFileDataEqual(const void *val1, const void *val2)
377 NSLRUFileData *d1 = (NSLRUFileData *)val1;
378 NSLRUFileData *d2 = (NSLRUFileData *)val2;
379 return (d1->fileSize == d2->fileSize &&
380 d1->heapTime == d2->heapTime &&
381 d1->updatedTime == d2->updatedTime &&
382 d1->markRemoved == d2->markRemoved &&
383 strcmp(d1->path, d2->path) == 0);
386 static NSLRUFileData *WebLRUFileListGetOldestFileData(WebLRUFileList *list, Boolean removeFromHeap)
390 // Use the heap count directory to account for the need to do lazy removal of heap objects
391 // that may have been left over by calls to WebLRUFileListRemoveFileWithPath
392 // since whenever WebLRUFileListRemoveFileWithPath is called,
393 // list->count and the heap's count may no longer be equal
394 unsigned int heapCount = CFBinaryHeapGetCount(list->heap);
395 if (heapCount == 0) {
402 for (i = 0; i < heapCount; i++) {
403 data = (NSLRUFileData *)CFBinaryHeapGetMinimum(list->heap);
404 if (data->markRemoved) {
405 // clean up this object that was marked for removal earlier
406 CFBinaryHeapRemoveMinimumValue(list->heap);
407 NSLRUFileDataRelease(NULL, data);
409 else if (data->heapTime != data->updatedTime) {
410 // update this object
411 // reinsert into heap
412 CFBinaryHeapRemoveMinimumValue(list->heap);
413 data->heapTime = data->updatedTime;
414 CFBinaryHeapAddValue(list->heap, data);
417 // found an object that was neither updated nor marked for removal
419 if (removeFromHeap) {
420 CFBinaryHeapRemoveMinimumValue(list->heap);
426 // we "wrapped around"
427 // all nodes were touched after they were added, and we are back at the start
428 // just pop off the object that is on top now.
429 data = (NSLRUFileData *)CFBinaryHeapGetMinimum(list->heap);
430 if (data && removeFromHeap) {
431 ASSERT(data->heapTime == data->updatedTime);
432 CFBinaryHeapRemoveMinimumValue(list->heap);
437 static void NSLRUFileDataReleaseApplierFunction(const void *key, const void *value, void *context)
440 free((NSLRUFileData *)value);
443 static void NSLRUFileDataRelease(CFAllocatorRef allocator, const void *value)
446 free((NSLRUFileData *)value);
449 // -----------------------------------------------------
452 static void NSLRUFileDataBinaryHeapDumpApplierFunction(const void *value, void *context)
454 NSMutableString *string = (NSMutableString *)context;
455 NSLRUFileData *data = (NSLRUFileData *)value;
456 [string appendFormat:@" %s : %6ld : %.0f : %.0f\n", data->path, data->fileSize, data->heapTime, data->updatedTime];
459 static void NSLRUFileDataDictDumpApplierFunction(const void *key, const void *value, void *context)
461 NSMutableString *string = (NSMutableString *)context;
462 NSLRUFileData *data = (NSLRUFileData *)value;
463 [string appendFormat:@" %s -> %6ld : %.0f : %.0f\n", (const char *)key, data->fileSize, data->heapTime, data->updatedTime];
466 NSString *WebLRUFileListDescription(WebLRUFileList *list)
468 NSMutableString *string = [NSMutableString string];
470 [string appendFormat:@"\nList root path: %s\n", list->rootDirectory];
471 [string appendFormat:@"List count: %d items\n", list->count];
472 [string appendFormat:@"List total size: %d bytes\n", list->totalSize];
474 [string appendFormat:@"\nHeap data: %ld items\n", CFBinaryHeapGetCount(list->heap)];
475 CFBinaryHeapApplyFunction(list->heap, NSLRUFileDataBinaryHeapDumpApplierFunction, string);
477 [string appendFormat:@"\nDict data: %ld items\n", CFDictionaryGetCount(list->dict)];
478 CFDictionaryApplyFunction(list->dict, NSLRUFileDataDictDumpApplierFunction, string);