diff --git a/Example/PINRemoteImage Tests/PINRemoteImage_Tests.m b/Example/PINRemoteImage Tests/PINRemoteImage_Tests.m index ebe62fe..5214ec6 100644 --- a/Example/PINRemoteImage Tests/PINRemoteImage_Tests.m +++ b/Example/PINRemoteImage Tests/PINRemoteImage_Tests.m @@ -108,6 +108,11 @@ return [NSURL URLWithString:@"https://www.gstatic.com/webp/gallery3/4_webp_ll.webp"]; } +- (NSURL *)veryLongURL +{ + return [NSURL URLWithString:@"https://placekitten.com/g/200/300?longarg=helloMomHowAreYouDoing.IamFineJustMovedToLiveWithANiceChapWeTravelTogetherInHisBlueBoxThroughSpaceAndTimeMaybeYouveMetHimAlready.YesterdayWeMetACultureOfPeopleWithTentaclesWhoSingWithAVeryCelestialVoice.SoGood.SeeYouSoon.MaybeYesterday.WhoKnows.XOXO"]; +} + #pragma mark - - (void)didReceiveData:(NSData *)data forTask:(NSURLSessionTask *)task @@ -763,4 +768,43 @@ [self waitForExpectationsWithTimeout:[self timeoutTimeInterval] handler:nil]; } +- (void)testDiskCacheOnLongURLs +{ + XCTestExpectation *expectation = [self expectationWithDescription:@"Image is available in the disk cache"]; + PINCache *cache = self.imageManager.cache; + NSURL *longURL = [self veryLongURL]; + NSString *key = [self.imageManager cacheKeyForURL:longURL processorKey:nil]; + [self.imageManager downloadImageWithURL:longURL + options:PINRemoteImageManagerDownloadOptionsNone + completion:^(PINRemoteImageManagerResult *result) + { + XCTAssertNotNil(result.image, @"Image should not be nil"); + id diskCachedObj = [cache.diskCache objectForKey:key]; + XCTAssertNotNil(diskCachedObj); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:[self timeoutTimeInterval] handler:nil]; +} + +- (void)testLongCacheKeyCreationPerformance +{ + [self measureBlock:^{ + NSURL *longURL = [self veryLongURL]; + for (NSUInteger i = 0; i < 10000; i++) { + __unused NSString *key = [self.imageManager cacheKeyForURL:longURL processorKey:nil]; + } + }]; +} + +- (void)testDefaultCacheKeyCreationPerformance +{ + [self measureBlock:^{ + NSURL *defaultURL = [self JPEGURL]; + for (NSUInteger i = 0; i < 10000; i++) { + __unused NSString *key = [self.imageManager cacheKeyForURL:defaultURL processorKey:nil]; + } + }]; +} + @end diff --git a/Pod/Classes/PINRemoteImageManager.m b/Pod/Classes/PINRemoteImageManager.m index 1485309..b393012 100644 --- a/Pod/Classes/PINRemoteImageManager.m +++ b/Pod/Classes/PINRemoteImageManager.m @@ -9,6 +9,7 @@ #import "PINRemoteImageManager.h" #import +#import #import "PINAlternateRepresentationProvider.h" #import "PINRemoteImage.h" @@ -27,6 +28,10 @@ #define PINRemoteImageManagerDefaultTimeout 60.0 +//A limit of 200 characters is chosen because PINDiskCache +//may expand the length by encoding certain characters +#define PINRemoteImageManagerCacheKeyMaxLength 200 + NSOperationQueuePriority operationPriorityWithImageManagerPriority(PINRemoteImageManagerPriority priority) { switch (priority) { case PINRemoteImageManagerPriorityVeryLow: @@ -1392,6 +1397,29 @@ static dispatch_once_t sharedDispatchToken; if (processorKey.length > 0) { cacheKey = [cacheKey stringByAppendingString:[NSString stringWithFormat:@"-<%@>", processorKey]]; } + + //PINDiskCache uses this key as the filename of the file written to disk + //Due to the current filesystem used in Darwin, this name must be limited to 255 chars. + //In case the generated key exceeds PINRemoteImageManagerCacheKeyMaxLength characters, + //we return the hash of it instead. + if (cacheKey.length > PINRemoteImageManagerCacheKeyMaxLength) { + __block CC_MD5_CTX ctx; + CC_MD5_Init(&ctx); + NSData *data = [cacheKey dataUsingEncoding:NSUTF8StringEncoding]; + [data enumerateByteRangesUsingBlock:^(const void * _Nonnull bytes, NSRange byteRange, BOOL * _Nonnull stop) { + CC_MD5_Update(&ctx, bytes, (CC_LONG)byteRange.length); + }]; + + unsigned char digest[CC_MD5_DIGEST_LENGTH]; + CC_MD5_Final(digest, &ctx); + + NSMutableString *hexString = [NSMutableString stringWithCapacity:(CC_MD5_DIGEST_LENGTH * 2)]; + for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { + [hexString appendFormat:@"%02lx", (unsigned long)digest[i]]; + } + cacheKey = [hexString copy]; + } + return cacheKey; }