Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial commit

  • Loading branch information...
commit 0c6fc156ce6e4d6ba2ddfa69dc5ea3f5c1f72989 0 parents
@schacon authored
27 Git.h
@@ -0,0 +1,27 @@
+//
+// Git.h
+// ObjGit
+//
+
+//#import <UIKit/UIKit.h>
+//#import <Foundation/NSNetServices.h>
+//#include <CFNetwork/CFSocketStream.h>
+
+#import <Foundation/Foundation.h>
+
+@interface Git : NSObject {
+ NSString* gitDirectory;
+}
+
+@property(assign, readwrite) NSString *gitDirectory;
+
+- (BOOL) openRepo:(NSString *)dirPath;
+- (BOOL) ensureGitPath;
+- (void) initGitRepo;
+
+- (void) writeObject:(NSData *)objectData withType:(int)type withSize:(int)size;
+
+- (NSMutableArray *) getCommitsFromSha:(NSString *)shaValue withLimit:(int)commitSize;
+- (NSString *) getLooseObjectPathBySha:(NSString *)shaValue;
+
+@end
106 Git.m
@@ -0,0 +1,106 @@
+//
+// Git.m
+// ObjGit
+//
+
+#import "Git.h"
+#import "GitObject.h"
+#import "GitCommit.h"
+#import "GitServerHandler.h"
+
+@implementation Git
+
+@synthesize gitDirectory;
+
+- (id) init
+{
+ return self;
+}
+
+- (void) dealloc
+{
+ [super dealloc];
+}
+
+- (BOOL) ensureGitPath {
+ BOOL isDir;
+ NSFileManager *fm = [NSFileManager defaultManager];
+ if ([fm fileExistsAtPath:gitDirectory isDirectory:&isDir] && isDir) {
+ return YES;
+ } else {
+ [self initGitRepo];
+ }
+ return YES;
+}
+
+- (void) initGitRepo {
+ NSFileManager *fm = [NSFileManager defaultManager];
+ NSString *dir;
+ [fm createDirectoryAtPath:gitDirectory attributes:nil];
+ NSLog(@"Dir Created: %@ %d", gitDirectory, [gitDirectory length]);
+
+ NSLog(@"Dir: %@", [gitDirectory substringToIndex:([gitDirectory length] - 20)]);
+
+ dir = [gitDirectory stringByAppendingString:@"/refs"];
+ NSLog(@"Ref Created: %@]", dir);
+
+ [fm createDirectoryAtPath:dir attributes:nil];
+ [fm createDirectoryAtPath:[gitDirectory stringByAppendingPathComponent:@"refs/heads"] attributes:nil];
+ [fm createDirectoryAtPath:[gitDirectory stringByAppendingPathComponent:@"refs/tags"] attributes:nil];
+ [fm createDirectoryAtPath:[gitDirectory stringByAppendingPathComponent:@"objects"] attributes:nil];
+ [fm createDirectoryAtPath:[gitDirectory stringByAppendingPathComponent:@"objects/info"] attributes:nil];
+ [fm createDirectoryAtPath:[gitDirectory stringByAppendingPathComponent:@"objects/pack"] attributes:nil];
+ [fm createDirectoryAtPath:[gitDirectory stringByAppendingPathComponent:@"branches"] attributes:nil];
+ [fm createDirectoryAtPath:[gitDirectory stringByAppendingPathComponent:@"hooks"] attributes:nil];
+ [fm createDirectoryAtPath:[gitDirectory stringByAppendingPathComponent:@"info"] attributes:nil];
+}
+
+- (void) writeObject:(NSData *)objectData withType:(int)type withSize:(int)size
+{
+ NSLog(@"WRITE OBJECT");
+}
+
+
+- (BOOL) openRepo:(NSString *)dirPath
+{
+ gitDirectory = dirPath;
+ return YES;
+}
+
+- (NSMutableArray *) getCommitsFromSha:(NSString *)shaValue withLimit:(int)commitSize
+{
+ NSString *currentSha;
+ NSMutableArray *toDoArray = [NSMutableArray arrayWithCapacity:10];
+ NSMutableArray *commitArray = [NSMutableArray arrayWithCapacity:commitSize];
+ GitCommit *gCommit;
+
+ [toDoArray addObject: shaValue];
+
+ // loop for commits
+ while( ([toDoArray count] > 0) && ([commitArray count] < commitSize) ) {
+ currentSha = [[toDoArray objectAtIndex: 0] retain];
+ [toDoArray removeObjectAtIndex:0];
+
+ NSString *objectPath = [self getLooseObjectPathBySha:currentSha];
+ NSFileHandle *fm = [NSFileHandle fileHandleForReadingAtPath:objectPath];
+
+ gCommit = [[GitCommit alloc] initFromRaw:[fm availableData] withSha:currentSha];
+ [toDoArray addObjectsFromArray:gCommit.parentShas];
+ [commitArray addObject:gCommit];
+ }
+
+ // NSLog(@"s: %@", commitArray);
+
+ return commitArray;
+}
+
+- (NSString *) getLooseObjectPathBySha: (NSString *)shaValue
+{
+ NSString *looseSubDir = [shaValue substringWithRange:NSMakeRange(0, 2)];
+ NSString *looseFileName = [shaValue substringWithRange:NSMakeRange(2, 38)];
+
+ return [NSString stringWithFormat: @"%@/objects/%@/%@", \
+ gitDirectory, looseSubDir, looseFileName];
+}
+
+@end
38 GitCommit.h
@@ -0,0 +1,38 @@
+//
+// GitCommit.h
+// ObjGit
+//
+
+#import <Foundation/Foundation.h>
+#import "GitObject.h"
+
+@interface GitCommit : NSObject {
+ NSArray *parentShas;
+ NSString *treeSha;
+ NSString *author;
+ NSString *author_email;
+ NSDate *authored_date;
+ NSString *committer;
+ NSString *committer_email;
+ NSDate *committed_date;
+ NSString *message;
+ GitObject *git_object;
+}
+
+@property(assign, readwrite) NSArray *parentShas;
+@property(assign, readwrite) NSString *treeSha;
+@property(assign, readwrite) NSString *author;
+@property(assign, readwrite) NSString *author_email;
+@property(assign, readwrite) NSDate *authored_date;
+@property(assign, readwrite) NSString *committer;
+@property(assign, readwrite) NSString *committer_email;
+@property(assign, readwrite) NSDate *committed_date;
+@property(assign, readwrite) NSString *message;
+@property(assign, readwrite) GitObject *git_object;
+
+- (id) initFromRaw:(NSData *)rawData withSha:(NSString *)shaValue;
+- (void) parseContent;
+- (void) logObject;
+- (NSArray *) parseAuthorString:(NSString *)authorString withType:(NSString *)typeString;
+
+@end
108 GitCommit.m
@@ -0,0 +1,108 @@
+//
+// GitCommit.m
+// ObjGit
+//
+
+#import "GitObject.h"
+#import "GitCommit.h"
+
+@implementation GitCommit
+
+@synthesize parentShas;
+@synthesize treeSha;
+@synthesize author;
+@synthesize author_email;
+@synthesize authored_date;
+@synthesize committer;
+@synthesize committer_email;
+@synthesize committed_date;
+@synthesize message;
+@synthesize git_object;
+
+- (id) initFromRaw:(NSData *)rawData withSha:(NSString *)shaValue
+{
+ self = [super init];
+ git_object = [[GitObject alloc] initFromRaw:rawData withSha:shaValue];
+ [self parseContent];
+ // [self logObject];
+ return self;
+}
+
+- (void) logObject
+{
+ NSLog(@"tree : %@", treeSha);
+ NSLog(@"author : %@, %@ : %@", author, author_email, authored_date);
+ NSLog(@"committer: %@, %@ : %@", committer, committer_email, committed_date);
+ NSLog(@"parents : %@", parentShas);
+ NSLog(@"message : %@", message);
+}
+
+- (void) parseContent
+{
+ // extract parent shas, tree sha, author/committer info, message
+ NSArray *lines = [git_object.contents componentsSeparatedByString:@"\n"];
+ NSEnumerator *enumerator;
+ NSMutableArray *parents;
+ NSMutableString *buildMessage;
+ NSString *line, *key, *val;
+ int inMessage = 0;
+
+ buildMessage = [NSMutableString new];
+ parents = [NSMutableArray new];
+
+ enumerator = [lines objectEnumerator];
+ while ((line = [enumerator nextObject]) != nil) {
+ // NSLog(@"line: %@", line);
+ if(!inMessage) {
+ if([line length] == 0) {
+ inMessage = 1;
+ } else {
+ NSArray *values = [line componentsSeparatedByString:@" "];
+ key = [values objectAtIndex: 0];
+ val = [values objectAtIndex: 1];
+ if([key isEqualToString: @"tree"]) {
+ treeSha = val;
+ } else if ([key isEqualToString: @"parent"]) {
+ [parents addObject: val];
+ } else if ([key isEqualToString: @"author"]) {
+ NSArray *name_email_date = [self parseAuthorString:line withType:@"author "];
+ author = [name_email_date objectAtIndex: 0];
+ author_email = [name_email_date objectAtIndex: 1];
+ authored_date = [name_email_date objectAtIndex: 2];
+ } else if ([key isEqualToString: @"committer"]) {
+ NSArray *name_email_date = [self parseAuthorString:line withType:@"committer "];
+ committer = [name_email_date objectAtIndex: 0];
+ committer_email = [name_email_date objectAtIndex: 1];
+ committed_date = [name_email_date objectAtIndex: 2];
+ }
+ }
+ } else {
+ [buildMessage appendString: line];
+ }
+ }
+ message = buildMessage;
+ parentShas = parents;
+}
+
+- (NSArray *) parseAuthorString:(NSString *)authorString withType:(NSString *)typeString
+{
+ NSArray *name_email_date;
+ name_email_date = [authorString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
+
+ NSString *nameVal = [name_email_date objectAtIndex: 0];
+ NSString *emailVal = [name_email_date objectAtIndex: 1];
+ NSString *dateVal = [name_email_date objectAtIndex: 2];
+ NSDate *dateDateVal;
+ dateDateVal = [NSDate dateWithTimeIntervalSince1970:[dateVal doubleValue]];
+
+ NSMutableString *tempValue = [[NSMutableString alloc] init];
+ [tempValue setString:nameVal];
+ [tempValue replaceOccurrencesOfString: typeString
+ withString: @""
+ options: 0
+ range: NSMakeRange(0, [tempValue length])];
+
+ return [NSArray arrayWithObjects:tempValue, emailVal, dateDateVal, nil];
+}
+
+@end
26 GitObject.h
@@ -0,0 +1,26 @@
+//
+// GitObject.h
+// ObjGit
+//
+
+#import <Foundation/Foundation.h>
+
+@interface GitObject : NSObject {
+ NSString* sha;
+ NSInteger size;
+ NSString* type;
+ NSString* contents;
+ NSData* raw;
+}
+
+@property(assign, readwrite) NSString *sha;
+@property(assign, readwrite) NSInteger size;
+@property(assign, readwrite) NSString *type;
+@property(assign, readwrite) NSString *contents;
+@property(assign, readwrite) NSData *raw;
+
+- (id) initFromRaw:(NSData *)rawData withSha:(NSString *)shaValue;
+- (void) parseRaw;
+- (NSData *) inflateRaw:(NSData *)rawData;
+
+@end
53 GitObject.m
@@ -0,0 +1,53 @@
+//
+// GitObject.m
+// ObjGit
+//
+
+#import "GitObject.h"
+#import "NSDataCompression.h"
+
+@implementation GitObject
+
+@synthesize sha;
+@synthesize size;
+@synthesize type;
+@synthesize contents;
+@synthesize raw;
+
+- (id) initFromRaw:(NSData *)rawData withSha:(NSString *)shaValue
+{
+ self = [super init];
+ sha = shaValue;
+ raw = [self inflateRaw:rawData];
+ // NSLog(@"sha: %@", sha);
+ // NSLog(@"raw: %@", raw);
+ [self parseRaw];
+ return self;
+}
+
+- (void) parseRaw
+{
+ char *ptr, *bytes = (char *)[raw bytes];
+ int len, rest;
+ len = (ptr = memchr(bytes, nil, len = [raw length])) ? ptr - bytes : len;
+ rest = [raw length] - len;
+
+ ptr++;
+ NSString *header = [NSString stringWithCString:bytes length:len];
+ contents = [NSString stringWithCString:ptr length:rest];
+
+ NSArray *headerData = [header componentsSeparatedByString:@" "];
+ type = [headerData objectAtIndex:0];
+ size = [[headerData objectAtIndex:1] intValue];
+
+ //NSLog(@"type:%@", type);
+ //NSLog(@"len:%d", size);
+ //NSLog(@"con:%@", contents);
+}
+
+- (NSData *) inflateRaw:(NSData *)rawData
+{
+ return [rawData decompressedData];
+}
+
+@end
45 GitServerHandler.h
@@ -0,0 +1,45 @@
+//
+// GitServerHandler.h
+// ObjGit
+//
+
+#import <Foundation/Foundation.h>
+
+@interface GitServerHandler : NSObject {
+ NSInputStream* inStream;
+ NSOutputStream* outStream;
+ Git* gitRepo;
+ NSString* gitPath;
+
+ NSMutableArray* refsRead;
+ int capabilitiesSent;
+}
+
+@property(assign, readwrite) NSInputStream *inStream;
+@property(assign, readwrite) NSOutputStream *outStream;
+@property(assign, readwrite) Git *gitRepo;
+@property(assign, readwrite) NSString *gitPath;
+
+@property(assign, readwrite) NSMutableArray *refsRead;
+@property(assign, readwrite) int capabilitiesSent;
+
+- (void) initWithGit:(Git *)git gitPath:(NSString *)gitRepoPath input:(NSInputStream *)streamIn output:(NSOutputStream *)streamOut;
+- (void) handleRequest;
+- (void) receivePack:(NSString *)repositoryName;
+
+- (void) sendRefs;
+- (void) sendRef:(NSString *)refName sha:(NSString *)shaString;
+- (void) readRefs;
+- (void) readPack;
+- (void) writeRefs;
+- (NSData *) readData:(int)size;
+
+- (int) readPackHeader;
+- (void) unpackObject;
+
+- (void) packetFlush;
+- (void) writeServer:(NSString *)dataWrite;
+- (void) sendPacket:(NSString *)dataSend;
+- (NSString *) packetReadLine;
+
+@end
334 GitServerHandler.m
@@ -0,0 +1,334 @@
+//
+// GitServerHandler.m
+// ObjGit
+//
+
+#define NULL_SHA @"0000000000000000000000000000000000000000"
+#define CAPABILITIES @" "
+
+#define OBJ_NONE 0
+#define OBJ_COMMIT 1
+#define OBJ_TREE 2
+#define OBJ_BLOB 3
+#define OBJ_TAG 4
+#define OBJ_OFS_DELTA 6
+#define OBJ_REF_DELTA 7
+
+#import "Git.h"
+#import "GitServerHandler.h"
+#include <zlib.h>
+
+@implementation GitServerHandler
+
+@synthesize inStream;
+@synthesize outStream;
+@synthesize gitRepo;
+@synthesize gitPath;
+
+@synthesize refsRead;
+@synthesize capabilitiesSent;
+
+- (void) initWithGit:(Git *)git gitPath:(NSString *)gitRepoPath input:(NSInputStream *)streamIn output:(NSOutputStream *)streamOut
+{
+ gitRepo = git;
+ gitPath = gitRepoPath;
+ inStream = streamIn;
+ outStream = streamOut;
+ [self handleRequest];
+}
+
+/*
+ * initiates communication with an incoming request
+ * and passes it to the appropriate receiving function
+ * either upload-pack for fetches or receive-pack for pushes
+ */
+- (void) handleRequest {
+ NSString *header, *command, *repository, *repo, *hostpath;
+ header = [self packetReadLine];
+
+ NSArray *values = [header componentsSeparatedByString:@" "];
+ command = [values objectAtIndex: 0];
+ repository = [values objectAtIndex: 1];
+
+ values = [repository componentsSeparatedByCharactersInSet:[NSCharacterSet controlCharacterSet]];
+ repo = [values objectAtIndex: 0];
+ hostpath = [values objectAtIndex: 1];
+
+ NSLog(@"header: %@ : %@ : %@", command, repo, hostpath);
+
+ NSString *dir = [gitPath stringByAppendingPathComponent:repo];
+ [gitRepo openRepo:dir];
+
+ if([command isEqualToString: @"git-receive-pack"]) { // git push //
+ NSLog(@"RECEIVE-PACK");
+ [self receivePack:repository];
+ } else if ([command isEqualToString: @"git-upload-pack"]) { // git fetch //
+ NSLog(@"UPLOAD-PACK not implemented yet");
+ //[self upload-pack:repository];
+ }
+
+}
+
+/*
+ * handles a push request - this involves validating the request,
+ * initializing the repository if it's not there, sending the
+ * refs we have, receiving the packfile form the client and unpacking
+ * the packed objects (eventually we should have an option to keep the
+ * packfile and build an index instead)
+ */
+- (void) receivePack:(NSString *)repositoryName {
+ capabilitiesSent = 0;
+
+ [gitRepo ensureGitPath];
+
+ [self sendRefs];
+ [self readRefs];
+ [self readPack];
+ [self writeRefs];
+}
+
+
+/*** RECEIVE-PACK FUNCTIONS ***/
+
+- (void) sendRefs {
+ NSLog(@"send refs");
+
+ // get refs from gitRepo //
+ // foreach ref, send to client //
+ /*
+ refs.each do |ref|
+ send_ref(ref[1], ref[0])
+ end
+ */
+
+ // send capabilities and null sha to client if no refs //
+ if(!capabilitiesSent)
+ [self sendRef:@"capabilities^{}" sha:NULL_SHA];
+ [self packetFlush];
+}
+
+- (void) sendRef:(NSString *)refName sha:(NSString *)shaString {
+ NSString *sendData;
+ if(capabilitiesSent)
+ sendData = [[NSString alloc] initWithFormat:@"%@ %@\n", shaString, refName];
+ else
+ sendData = [[NSString alloc] initWithFormat:@"%@ %@%c%@\n", shaString, refName, 0, CAPABILITIES];
+ [self writeServer:sendData];
+ capabilitiesSent = 1;
+}
+
+- (void) readRefs {
+ NSString *data;
+ NSLog(@"read refs");
+ data = [self packetReadLine];
+ while([data length] > 0) {
+ NSArray *values = [data componentsSeparatedByString:@" "];
+ [refsRead addObject: values]; // save the refs for writing later
+
+ /* DEBUGGING */
+ NSLog(@"ref: [%@ : %@ : %@]", [values objectAtIndex: 0], \
+ [values objectAtIndex: 1], [values objectAtIndex: 2]);
+
+ data = [self packetReadLine];
+ }
+}
+
+/*
+ * read packfile data from the stream and expand the objects out to disk
+ */
+- (void) readPack {
+ NSLog(@"read pack");
+ int n;
+ int entries = [self readPackHeader];
+
+ for(n = 1; n <= entries; n++) {
+ NSLog(@"entry: %d", n);
+ [self unpackObject];
+ }
+ // receive and process checksum
+}
+
+- (void) unpackObject {
+ NSLog(@"unpack object");
+
+ // read in the header
+ int size, type, shift;
+ uint8_t byte[1];
+ [inStream read:byte maxLength:1];
+
+ size = byte[0] & 0xf;
+ type = (byte[0] >> 4) & 7;
+ shift = 4;
+ while((byte[0] & 0x80) != 0) {
+ [inStream read:byte maxLength:1];
+ size |= ((byte[0] & 0x7f) << shift);
+ shift += 7;
+ }
+
+ NSLog(@"\nTYPE: %d\n", type);
+ NSLog(@"size: %d\n", size);
+
+ if((type == OBJ_COMMIT) || (type == OBJ_TREE) || (type == OBJ_BLOB) || (type == OBJ_TAG)) {
+ NSData *objectData;
+ objectData = [self readData:size];
+ [gitRepo writeObject:objectData withType:type withSize:size];
+ // TODO : check saved delta objects
+ } else if ((type == OBJ_REF_DELTA) || (type == OBJ_OFS_DELTA)) {
+ NSLog(@"NO SUPPORT FOR DELTAS YET");
+ } else {
+ NSLog(@"bad object type %d", type);
+ }
+}
+
+- (NSData *) readData:(int)size {
+ // read in the data
+ NSMutableData *decompressed = [NSMutableData dataWithLength: size];
+ BOOL done = NO;
+ int status;
+
+ uint8_t buffer[2];
+ [inStream read:buffer maxLength:1];
+
+ z_stream strm;
+ strm.next_in = buffer;
+ strm.avail_in = 1;
+ strm.total_out = 0;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+
+ if (inflateInit (&strm) != Z_OK)
+ NSLog(@"Inflate Issue");
+
+ while (!done)
+ {
+ // Make sure we have enough room and reset the lengths.
+ if (strm.total_out >= [decompressed length])
+ [decompressed increaseLengthBy: 100];
+ strm.next_out = [decompressed mutableBytes] + strm.total_out;
+ strm.avail_out = [decompressed length] - strm.total_out;
+
+ // Inflate another chunk.
+ status = inflate (&strm, Z_SYNC_FLUSH);
+ if (status == Z_STREAM_END) done = YES;
+ else if (status != Z_OK) {
+ NSLog(@"status for break: %d", status);
+ break;
+ }
+
+ if(!done) {
+ [inStream read:buffer maxLength:1];
+ strm.next_in = buffer;
+ strm.avail_in = 1;
+ }
+ }
+ if (inflateEnd (&strm) != Z_OK)
+ NSLog(@"Inflate Issue");
+
+ // Set real length.
+ if (done)
+ [decompressed setLength: strm.total_out];
+
+ return decompressed;
+}
+
+- (int) readPackHeader {
+ NSLog(@"read pack header");
+
+ uint8_t inSig[4], inVer[4], inEntries[4];
+ uint32_t version, entries;
+ [inStream read:inSig maxLength:4];
+ [inStream read:inVer maxLength:4];
+ [inStream read:inEntries maxLength:4];
+
+ entries = (inEntries[0] << 24) | (inEntries[1] << 16) | (inEntries[2] << 8) | inEntries[3];
+ version = (inVer[0] << 24) | (inVer[1] << 16) | (inVer[2] << 8) | inVer[3];
+ return entries;
+}
+
+/*
+ * write refs to disk after successful read
+ */
+- (void) writeRefs {
+ NSLog(@"write refs");
+}
+
+
+/*** NETWORK FUNCTIONS ***/
+
+- (void) packetFlush {
+ [self sendPacket:@"0000"];
+}
+
+- (void) sendPacket:(NSString *)dataWrite {
+ NSLog(@"send:[%@]", dataWrite);
+ int len = [dataWrite length];
+ uint8_t buffer[len];
+ [[dataWrite dataUsingEncoding:NSUTF8StringEncoding] getBytes:buffer];
+ [outStream write:buffer maxLength:len];
+}
+
+// FROM GIT : pkt-line.c : Linus //
+
+#define hex(a) (hexchar[(a) & 15])
+- (void) writeServer:(NSString *)dataWrite {
+ NSLog(@"write:[%@]", dataWrite);
+ unsigned int len = [dataWrite length];
+
+ static char hexchar[] = "0123456789abcdef";
+ uint8_t buffer[4];
+
+ len += 4;
+ buffer[0] = hex(len >> 12);
+ buffer[1] = hex(len >> 8);
+ buffer[2] = hex(len >> 4);
+ buffer[3] = hex(len);
+
+ NSLog(@"write len");
+ [outStream write:buffer maxLength:4];
+ NSLog(@"write data");
+ [self sendPacket:dataWrite];
+}
+
+- (NSString *) packetReadLine {
+ uint8_t linelen[4];
+ unsigned int len = 0;
+ len = [inStream read:linelen maxLength:4];
+
+ if(!len) {
+ if ([inStream streamStatus] != NSStreamStatusAtEnd)
+ NSLog(@"protocol error: read error");
+ }
+
+ int n;
+ len = 0;
+ for (n = 0; n < 4; n++) {
+ unsigned char c = linelen[n];
+ len <<= 4;
+ if (c >= '0' && c <= '9') {
+ len += c - '0';
+ continue;
+ }
+ if (c >= 'a' && c <= 'f') {
+ len += c - 'a' + 10;
+ continue;
+ }
+ if (c >= 'A' && c <= 'F') {
+ len += c - 'A' + 10;
+ continue;
+ }
+ NSLog(@"protocol error: bad line length character");
+ }
+
+ if (!len)
+ return @"";
+
+ len -= 4;
+ uint8_t data[len];
+
+ [inStream read:data maxLength:len];
+ data[len] = 0;
+
+ return [[NSString alloc] initWithBytes:data length:len encoding:NSASCIIStringEncoding];
+}
+
+@end
15 NSDataCompression.h
@@ -0,0 +1,15 @@
+//
+// NSDataCompression.h
+// ObjGit
+//
+// thankfully borrowed from the Etoile framework
+//
+
+#include <Foundation/Foundation.h>
+
+@interface NSData (Compression)
+
+- (NSData *) compressedData;
+- (NSData *) decompressedData;
+
+@end
81 NSDataCompression.m
@@ -0,0 +1,81 @@
+//
+// NSDataCompression.m
+// ObjGit
+//
+// thankfully borrowed from the Etoile framework
+//
+
+#import "NSDataCompression.h"
+#include <zlib.h>
+
+@implementation NSData (Compression)
+- (NSData *) compressedData
+{
+ NSData *result = nil;
+ unsigned int srcLength = [self length];
+ if (srcLength > 0)
+ {
+ uLong buffLength = srcLength * 1.001 + 12;
+ NSMutableData *compData = [[NSMutableData alloc] initWithCapacity:buffLength];
+ [compData increaseLengthBy:buffLength];
+ int error = compress( [compData mutableBytes], &buffLength,
+ [self bytes], srcLength );
+ switch( error ) {
+ case Z_OK:
+ [compData setLength: buffLength];
+ //ASSIGNCOPY(result, compData);
+ break;
+ default:
+ NSAssert( YES, @"Error compressing: Memory Error!" );
+ break;
+ }
+ //RELEASE(compData);
+ }
+ return result;
+}
+
+- (NSData *) decompressedData
+{
+ if ([self length] == 0) return self;
+
+ unsigned full_length = [self length];
+ unsigned half_length = [self length] / 2;
+
+ NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
+ BOOL done = NO;
+ int status;
+
+ z_stream strm;
+ strm.next_in = (Bytef *)[self bytes];
+ strm.avail_in = [self length];
+ strm.total_out = 0;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+
+ if (inflateInit (&strm) != Z_OK) return nil;
+ while (!done)
+ {
+ // Make sure we have enough room and reset the lengths.
+ if (strm.total_out >= [decompressed length])
+ [decompressed increaseLengthBy: half_length];
+ strm.next_out = [decompressed mutableBytes] + strm.total_out;
+ strm.avail_out = [decompressed length] - strm.total_out;
+
+ // Inflate another chunk.
+ status = inflate (&strm, Z_SYNC_FLUSH);
+ if (status == Z_STREAM_END) done = YES;
+ else if (status != Z_OK) break;
+ }
+ if (inflateEnd (&strm) != Z_OK) return nil;
+
+ // Set real length.
+ if (done)
+ {
+ [decompressed setLength: strm.total_out];
+ return [NSData dataWithData: decompressed];
+ }
+ else
+ return nil;
+
+}
+@end
Please sign in to comment.
Something went wrong with that request. Please try again.