34#define PROFILE_WRITES 0
39#define CACHE_PLIST_FORMAT NSPropertyListBinaryFormat_v1_0
41#define CACHE_PLIST_FORMAT NSPropertyListGNUstepBinaryFormat
83@interface OOCacheManager (Private)
92- (BOOL)writeDict:(NSDictionary *)inDict;
94- (void)buildCachesFromDictionary:(NSDictionary *)inDict;
97- (BOOL)directoryExists:(NSString *)inPath create:(BOOL)inCreate;
102@interface OOCacheManager (PlatformSpecific)
104- (NSString *)cachePathCreatingIfNecessary:(BOOL)inCreate;
113 NSDictionary *_cacheContents;
116- (id) initWithCacheContents:(NSDictionary *)cacheContents;
144- (NSString *)description
146 return [NSString stringWithFormat:@"<%@ %p>{dirty=%s}", [
self class], self, [
self dirty] ? "yes" : "no"];
162- (id)objectForKey:(NSString *)inKey inCache:(NSString *)inCacheKey
164 NSMutableDictionary *cache =
nil;
167 NSParameterAssert(inKey !=
nil && inCacheKey !=
nil);
169 cache = [_caches objectForKey:inCacheKey];
172 result = [cache objectForKey:inKey];
192- (void)setObject:(
id)inObject forKey:(NSString *)inKey inCache:(NSString *)inCacheKey
194 NSMutableDictionary *cache =
nil;
196 NSParameterAssert(inObject !=
nil && inKey !=
nil && inCacheKey !=
nil);
200 cache = [_caches objectForKey:inCacheKey];
203 cache = [NSMutableDictionary dictionary];
209 [_caches setObject:cache forKey:inCacheKey];
212 [cache setObject:inObject forKey:inKey];
218- (void)removeObjectForKey:(NSString *)inKey inCache:(NSString *)inCacheKey
220 NSMutableDictionary *cache =
nil;
222 NSParameterAssert(inKey !=
nil && inCacheKey !=
nil);
224 cache = [_caches objectForKey:inCacheKey];
227 if (
nil != [cache objectForKey:inKey])
229 [cache removeObjectForKey:inKey];
245- (void)clearCache:(NSString *)inCacheKey
247 NSParameterAssert(inCacheKey !=
nil);
249 if (
nil != [_caches objectForKey:inCacheKey])
251 [_caches removeObjectForKey:inCacheKey];
262- (void)clearAllCaches
265 _caches = [[NSMutableDictionary alloc] init];
270- (void) reloadAllCaches
279 if (_permitWrites && [
self dirty] && _scheduledWrite ==
nil)
287- (void)finishOngoingFlush
295- (void)setAllowCacheWrites:(BOOL)flag
297 _permitWrites = (flag != NO);
301- (NSString *)cacheDirectoryPathCreatingIfNecessary:(BOOL)create
311 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
312 if (![
self directoryExists:cachePath create:create])
return nil;
316 [[NSFileManager defaultManager] removeFileAtPath:[cachePath stringByAppendingPathComponent:@"Oolite-cache.plist"] handler:nil];
319 cachePath = [cachePath stringByAppendingPathComponent:@"org.aegidian.oolite"];
320 if (![
self directoryExists:cachePath create:create])
return nil;
327@implementation OOCacheManager (Private)
331 NSDictionary *cache =
nil;
332 NSString *cacheVersion =
nil;
333 NSString *ooliteVersion =
nil;
334 NSData *endianTag =
nil;
335 NSNumber *formatVersion =
nil;
337 uint64_t endianTagValue = 0;
339 ooliteVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
343 cache = [
self loadDict];
350 cacheVersion = [cache objectForKey:kCacheKeyVersion];
351 if (![cacheVersion isEqual:ooliteVersion])
353 OOLog(
kOOLogDataCacheRebuild,
@"Data cache version (%@) does not match Oolite version (%@), rebuilding cache.", cacheVersion, ooliteVersion);
357 formatVersion = [cache objectForKey:kCacheKeyFormatVersion];
366 endianTag = [cache objectForKey:kCacheKeyEndianTag];
367 if (![endianTag isKindOfClass:[NSData
class]] || [endianTag length] !=
sizeof endianTagValue)
374 endianTagValue = *(
const uint64_t *)[endianTag bytes];
386 [
self buildCachesFromDictionary:[cache objectForKey:kCacheKeyCaches]];
398 if (_caches ==
nil) _caches = [[NSMutableDictionary alloc] init];
405 NSMutableDictionary *newCache =
nil;
406 NSString *ooliteVersion =
nil;
407 NSData *endianTag =
nil;
408 NSNumber *formatVersion =
nil;
409 NSDictionary *pListRep =
nil;
412 if (_caches ==
nil)
return;
413 if (_scheduledWrite !=
nil)
return;
420 OOLog(
@"dataCache.willWrite",
@"%@",
@"Scheduling data cache write.");
422 OOLog(
@"dataCache.willWrite",
@"%@",
@"About to write cache.");
425 ooliteVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
426 endianTag = [NSData dataWithBytes:&endianTagValue length:sizeof endianTagValue];
427 formatVersion = [NSNumber numberWithUnsignedInt:kFormatVersionValue];
429 pListRep = [
self dictionaryOfCaches];
430 if (ooliteVersion ==
nil || endianTag ==
nil || formatVersion ==
nil || pListRep ==
nil)
432 OOLog(
@"dataCache.cantWrite",
@"%@",
@"Failed to write data cache -- prerequisites not fulfilled. This is an internal error, please report it.");
436 newCache = [NSMutableDictionary dictionaryWithCapacity:4];
437 [newCache setObject:ooliteVersion forKey:kCacheKeyVersion];
438 [newCache setObject:formatVersion forKey:kCacheKeyFormatVersion];
439 [newCache setObject:endianTag forKey:kCacheKeyEndianTag];
440 [newCache setObject:pListRep forKey:kCacheKeyCaches];
442#if PROFILE_WRITES && !WRITE_ASYNC
447 NSDictionary *cacheData = newCache;
452 OOLog(
@"dataCache.profile",
@"Time to prepare cache data: %g seconds.", endT);
458 OOLog(
@"dataCache.profile",
@"Time to prepare cache data: %g seconds.", prepareT);
461 if ([
self writeDict:newCache])
495 NSString *path =
nil;
497 NSString *errorString =
nil;
500 path = [
self cachePathCreatingIfNecessary:NO];
501 if (path ==
nil)
return nil;
505 data = [NSData dataWithContentsOfFile:path];
506 if (data ==
nil)
return nil;
508 contents = [NSPropertyListSerialization propertyListFromData:data
509 mutabilityOption:NSPropertyListImmutable
511 errorDescription:&errorString];
513 @catch (NSException *exception)
515 errorString = [exception reason];
519 if (errorString !=
nil)
521 OOLog(
@"dataCache.badData",
@"Could not read data cache: %@", errorString);
522#if OOLITE_RELEASE_PLIST_ERROR_STRINGS
523 [errorString release];
527 if (![contents isKindOfClass:[NSDictionary
class]])
return nil;
533- (BOOL)writeDict:(NSDictionary *)inDict
535 NSString *path =
nil;
537 NSString *errorDesc =
nil;
539 path = [
self cachePathCreatingIfNecessary:YES];
540 if (path ==
nil)
return NO;
546 plist = [NSPropertyListSerialization dataFromPropertyList:inDict format:CACHE_PLIST_FORMAT errorDescription:&errorDesc];
549#if OOLITE_RELEASE_PLIST_ERROR_STRINGS
550 [errorDesc autorelease];
560 BOOL result = [plist writeToFile:path atomically:NO];
565 OOLog(
@"dataCache.profile",
@"Time to serialize cache: %g seconds. Time to write data: %g seconds.", serializeT, writeT);
575- (void)buildCachesFromDictionary:(NSDictionary *)inDict
577 NSEnumerator *keyEnum =
nil;
580 NSMutableDictionary *cache =
nil;
582 if (inDict ==
nil )
return;
585 _caches = [[NSMutableDictionary alloc] initWithCapacity:[inDict count]];
587 for (keyEnum = [inDict keyEnumerator]; (key = [keyEnum nextObject]); )
589 value = [inDict oo_dictionaryForKey:key];
592 cache = [NSMutableDictionary dictionaryWithDictionary:value];
595 [_caches setObject:cache forKey:key];
604 return [OODeepCopy(_caches) autorelease];
608- (BOOL)directoryExists:(NSString *)inPath create:(BOOL)inCreate
610 BOOL exists, directory;
611 NSFileManager *fmgr = [NSFileManager defaultManager];
613 exists = [fmgr fileExistsAtPath:inPath isDirectory:&directory];
615 if (exists && !directory)
622 if (!inCreate)
return NO;
623 if (![fmgr oo_createDirectoryAtPath:inPath attributes:
nil])
636- (NSString *)cachePathCreatingIfNecessary:(BOOL)create
638 NSString *cachePath = [
self cacheDirectoryPathCreatingIfNecessary:create];
639 return [cachePath stringByAppendingPathComponent:@"Data Cache.plist"];
644- (NSString *)cachePathCreatingIfNecessary:(BOOL)create
646 NSString *cachePath = [
self cacheDirectoryPathCreatingIfNecessary:create];
647 return [cachePath stringByAppendingPathComponent:@"Oolite-cache.plist"];
655@implementation OOCacheManager (Singleton)
664+ (id)allocWithZone:(NSZone *)inZone
675- (id)copyWithZone:(NSZone *)inZone
687- (NSUInteger)retainCount
708- (id) initWithCacheContents:(NSDictionary *)cacheContents
710 if ((
self = [super init]))
712 _cacheContents = [cacheContents copy];
713 if (_cacheContents ==
nil)
732- (void) performAsyncTask
746- (void) completeAsyncTask
static NSString *const kCacheKeyEndianTag
static NSString *const kOOLogDataCacheWriteSuccess
static NSString *const kOOLogDataCacheBuildPathError
static NSString *const kOOLogDataCacheRetrieveSuccess
static NSString *const kOOLogDataCacheClearSuccess
static NSString *const kOOLogDataCacheWriteFailed
static NSString *const kOOLogDataCacheRetrieveFailed
static NSString *const kOOLogDataCacheSetFailed
static NSString *const kCacheKeyCaches
static OOCacheManager * sSingleton
static NSString *const kOOLogDataCacheRebuild
static NSString *const kOOLogDataCacheSerializationError
static NSString *const kCacheKeyVersion
static NSString *const kOOLogDataCacheRemoveSuccess
static NSString *const kOOLogDataCacheNotFound
static NSString *const kCacheKeyFormatVersion
static NSString *const kOOLogDataCacheSetSuccess
static NSString *const kOOLogDataCacheFound
static OODebugMonitor * sSingleton
#define OODebugLog(class, format,...)
#define OOLogOutdentIf(class)
#define OOLog(class, format,...)
#define OOLogIndentIf(class)
NSDictionary * loadDict()
NSDictionary * dictionaryOfCaches()
BOOL addTask:priority:(id< OOAsyncWorkTask > task,[priority] OOAsyncWorkPriority priority)
void waitForTaskToComplete:(id< OOAsyncWorkTask > task)
OOAsyncWorkManager * sharedAsyncWorkManager()