// // HashMapHash.m // pithos-macos // // Copyright 2011 GRNET S.A. All rights reserved. // // Redistribution and use in source and binary forms, with or // without modification, are permitted provided that the following // conditions are met: // // 1. Redistributions of source code must retain the above // copyright notice, this list of conditions and the following // disclaimer. // // 2. Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials // provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // The views and conclusions contained in the software and // documentation are those of the authors and should not be // interpreted as representing official policies, either expressed // or implied, of GRNET S.A. #import "HashMapHash.h" #import @implementation HashMapHash + (NSString *)calculateHashMapHash:(NSArray *)hashes { const char padding[] = { '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0', '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'}; if ([hashes count] == 0) return [HashMapHash formatHash:[HashMapHash calculateHash:[NSData dataWithBytes:padding length:0]]]; if ([hashes count] == 1) return [HashMapHash formatHash:[hashes objectAtIndex:0]]; int s = 2; while (s < [hashes count]) s = s * 2; NSMutableArray *h = [NSMutableArray array]; for (int i = 0; i < [hashes count]; i++) [h addObject:[hashes objectAtIndex:i]]; for (int i = 0; i < s - [hashes count]; i++) [h addObject:[NSMutableData dataWithBytes:padding length:32]]; while ([h count] > 1) { for (int i = 0; i < [h count]; i = i + 2) { NSMutableData *bytesData = [h objectAtIndex:i]; [bytesData appendData:[h objectAtIndex:i + 1]]; [h replaceObjectAtIndex:i withObject:[HashMapHash calculateHash:bytesData]]; } NSMutableIndexSet *indicesToRemove = [NSMutableIndexSet indexSet]; for (int i = 0; i < [h count]; i = i + 2) [indicesToRemove addIndex:i + 1]; [h removeObjectsAtIndexes:indicesToRemove]; } return [HashMapHash formatHash:[h objectAtIndex:0]]; } + (NSData *)calculateHash:(NSData *)data { unsigned char hash[CC_SHA256_DIGEST_LENGTH]; CC_SHA256([data bytes], (unsigned int)[data length], hash); return [NSMutableData dataWithBytes:hash length:CC_SHA256_DIGEST_LENGTH]; } + (NSString *)formatHash:(NSData *)data { NSString *hashString; hashString = [NSString stringWithFormat:@"%@", data]; hashString = [hashString stringByReplacingOccurrencesOfString:@" " withString:@""]; hashString = [hashString stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]; return hashString; } // XXX hardcoded hash algorithm for now + (NSArray *)calculateObjectHashMap:(NSString *)filePath withBlockHash:(NSString *)blockHash andBlockSize:(NSUInteger)blockSize { NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:filePath]; if (fh == nil) { NSLog(@"failed to open file: %@", filePath); return nil; } NSFileManager *fm = [NSFileManager defaultManager]; NSDictionary *attributes = [fm attributesOfItemAtPath:filePath error:nil]; NSUInteger endOfFile = [attributes fileSize]; NSUInteger fileOffset = 0; [fh seekToFileOffset:fileOffset]; NSMutableArray *hashes = [[[NSMutableArray alloc] init] autorelease]; while(fileOffset < endOfFile){ NSData *data = [fh readDataOfLength:blockSize]; // Strip trailing slashes. const char *bytes = [data bytes]; NSInteger i; for (i = [data length] - 1; i >= 0; i--) if (bytes[i] != '\0') break; if (i != [data length] - 1) { if (i >= 0) data = [data subdataWithRange:NSMakeRange(0, i + 1)]; else data = [NSData data]; } unsigned char hash[CC_SHA256_DIGEST_LENGTH]; if ( CC_SHA256([data bytes], (unsigned int)[data length], hash) ) { NSData *sha256 = [NSMutableData dataWithBytes:hash length:CC_SHA256_DIGEST_LENGTH]; [hashes addObject:sha256]; } fileOffset += blockSize; [fh seekToFileOffset:fileOffset]; } [fh closeFile]; NSArray *hashMap = [NSArray arrayWithArray:hashes]; return hashMap; } + (NSArray *)objectHashMapStrings:(NSString *)filePath withBlockHash:(NSString *)blockHash andBlockSize:(NSUInteger)blockSize { NSArray *hashHexes = [HashMapHash calculateObjectHashMap:filePath withBlockHash:blockHash andBlockSize:blockSize]; if (hashHexes == nil) return nil; NSMutableArray *hashStrings = [[[NSMutableArray alloc] init] autorelease]; for (NSData *hashHex in hashHexes) { [hashStrings addObject:[HashMapHash formatHash:hashHex]]; } NSArray *hashMap = [NSArray arrayWithArray:hashStrings]; return hashMap; } @end