├── executable └── mcinsight.app │ └── Contents │ ├── PkgInfo │ ├── MacOS │ └── mcinsight │ ├── Resources │ └── English.lproj │ │ ├── MainMenu.nib │ │ └── InfoPlist.strings │ └── Info.plist ├── English.lproj └── InfoPlist.strings ├── mcinsight.xcodeproj ├── TemplateIcon.icns ├── josh.pbxuser ├── project.pbxproj ├── aa.pbxuser ├── gerard.pbxuser └── josh.mode1v3 ├── mcinsight_Prefix.pch ├── .gitignore ├── LogDelegate.h ├── Runner.h ├── LogInfo.m ├── LogInfo.h ├── ValueInfo.m ├── README ├── main.m ├── Runner.m ├── ValueInfo.h ├── EchoServer.h ├── Info.plist ├── data_AppDelegate.h ├── MemcacheSnapshot.h ├── LogDelegate.m ├── MemcacheSnapshot.m ├── data_AppDelegate.m ├── AsyncSocket.h ├── EchoServer.m └── protocol.txt /executable/mcinsight.app/Contents/PkgInfo: -------------------------------------------------------------------------------- 1 | APPL???? -------------------------------------------------------------------------------- /English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewfromcali/mcinsight/HEAD/English.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /mcinsight.xcodeproj/TemplateIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewfromcali/mcinsight/HEAD/mcinsight.xcodeproj/TemplateIcon.icns -------------------------------------------------------------------------------- /executable/mcinsight.app/Contents/MacOS/mcinsight: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewfromcali/mcinsight/HEAD/executable/mcinsight.app/Contents/MacOS/mcinsight -------------------------------------------------------------------------------- /mcinsight_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'mcinsight' target in the 'mcinsight' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /executable/mcinsight.app/Contents/Resources/English.lproj/MainMenu.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewfromcali/mcinsight/HEAD/executable/mcinsight.app/Contents/Resources/English.lproj/MainMenu.nib -------------------------------------------------------------------------------- /executable/mcinsight.app/Contents/Resources/English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewfromcali/mcinsight/HEAD/executable/mcinsight.app/Contents/Resources/English.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | 3 | *.pbxuser 4 | *.perspectivev3 5 | *.mode1v3 6 | *.mode2v3 7 | !default.pbxuser 8 | !default.perspectivev3 9 | !default.mode1v3 10 | !default.mode2v3 11 | 12 | *~.nib 13 | *~.xib 14 | 15 | .DS_Store 16 | -------------------------------------------------------------------------------- /LogDelegate.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | #import 4 | 5 | @interface LogDelegate : NSObject { 6 | IBOutlet NSTableView *table; 7 | } 8 | 9 | @property (nonatomic, retain) NSTableView *table; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /Runner.h: -------------------------------------------------------------------------------- 1 | // 2 | // Runner.h 3 | // mcinsight 4 | // 5 | // Created by aa on 7/10/08. 6 | // Copyright 2008 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface Runner : NSObject { 13 | 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /LogInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // LogInfo.m 3 | // mcinsight 4 | // 5 | // Created by aa on 7/13/08. 6 | // Copyright 2008 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "LogInfo.h" 10 | 11 | 12 | @implementation LogInfo 13 | 14 | @synthesize sid; 15 | @synthesize data; 16 | @synthesize direction; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LogInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // LogInfo.h 3 | // mcinsight 4 | // 5 | // Created by aa on 7/13/08. 6 | // Copyright 2008 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface LogInfo : NSObject { 13 | long sid; 14 | NSString *data; 15 | BOOL direction; 16 | } 17 | 18 | @property (nonatomic, retain) NSString *data; 19 | @property long sid; 20 | @property BOOL direction; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /ValueInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // ValueInfo.m 3 | // mcinsight 4 | // 5 | // Created by aa on 7/11/08. 6 | // Copyright 2008 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "ValueInfo.h" 10 | 11 | 12 | @implementation ValueInfo 13 | 14 | @synthesize data; 15 | @synthesize expiry; 16 | @synthesize key; 17 | @synthesize insertedAt; 18 | @synthesize hits; 19 | @synthesize flag; 20 | @synthesize command; 21 | 22 | -(id) init { 23 | if (self = [super init]) { 24 | hits=0; 25 | } 26 | return self; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | After learning how to use Cocoa classes, I started to write "mcinsight" 2 | (mem cache insight). 3 | The idea is to run this stand alone mac application on your dev system 4 | instead of a real memcached server. When you launch it, it opens a socket 5 | server on port 11211 (default memcached port) and does everything a real 6 | memcached does but with a GUI on top. 7 | 8 | You can see every key, click on it and see it's value, it's size, etc. 9 | You can sort by keyname, expiring time, etc. 10 | 11 | Sorry, no windows or linux version. Have to upgrade to mac. 12 | 13 | -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // mcinsight 4 | // 5 | // Created by aa on 7/9/08. 6 | // Copyright __MyCompanyName__ 2008. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "Runner.h" 11 | 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; 16 | Runner *runner = [Runner alloc]; 17 | [NSThread detachNewThreadSelector:@selector(run) toTarget:runner withObject:nil]; 18 | [runner release]; 19 | [autoreleasepool release]; 20 | return NSApplicationMain(argc, (const char **) argv); 21 | } 22 | -------------------------------------------------------------------------------- /Runner.m: -------------------------------------------------------------------------------- 1 | // 2 | // Runner.m 3 | // mcinsight 4 | // 5 | // Created by aa on 7/10/08. 6 | // Copyright 2008 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "Runner.h" 10 | #import "EchoServer.h" 11 | 12 | @implementation Runner 13 | 14 | - (void)run { 15 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 16 | EchoServer *es = [[EchoServer alloc] init]; 17 | NSString *portString = @"11211"; 18 | 19 | [es performSelector:@selector(acceptOnPortString:) withObject:portString afterDelay:1.0]; 20 | [[NSRunLoop currentRunLoop] run]; 21 | [EchoServer release]; 22 | [es release]; 23 | [pool release]; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /ValueInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // ValueInfo.h 3 | // mcinsight 4 | // 5 | // Created by aa on 7/11/08. 6 | // Copyright 2008 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface ValueInfo : NSObject { 13 | NSMutableData *data; 14 | NSString *key; 15 | NSString *command; 16 | int expiry; 17 | int hits; 18 | NSString *flag; 19 | NSTimeInterval insertedAt; 20 | } 21 | 22 | @property (nonatomic, retain) NSMutableData *data; 23 | @property (nonatomic, retain) NSString *key; 24 | @property (nonatomic, retain) NSString *command; 25 | @property (nonatomic, retain) NSString *flag; 26 | @property int expiry; 27 | @property int hits; 28 | @property NSTimeInterval insertedAt; 29 | @end 30 | -------------------------------------------------------------------------------- /EchoServer.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | #import "AsyncSocket.h" 4 | #import "ValueInfo.h" 5 | 6 | @interface EchoServer : NSObject 7 | { 8 | NSMutableArray *sockets; 9 | } 10 | +(NSMutableDictionary*)getDict; 11 | +(NSMutableArray*)getLog; 12 | +(NSInteger)getTotalHits; 13 | +(NSInteger)getTotalMisses; 14 | -(id) init; 15 | -(void) dealloc; 16 | -(void) acceptOnPortString:(NSString *)str; 17 | -(void) onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err; 18 | -(void) onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket; 19 | -(void) onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port; 20 | -(void) onSocket:(AsyncSocket *)sock didReadData:(NSData*)data withTag:(long)tag; 21 | -(void) onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag; 22 | @end 23 | -------------------------------------------------------------------------------- /executable/mcinsight.app/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | mcinsight 9 | CFBundleIdentifier 10 | com.yourcompany.mcinsight 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | mcinsight 15 | CFBundlePackageType 16 | APPL 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1.0 21 | NSMainNibFile 22 | MainMenu 23 | NSPrincipalClass 24 | NSApplication 25 | 26 | 27 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.yourcompany.${PRODUCT_NAME:identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | NSMainNibFile 24 | MainMenu 25 | NSPrincipalClass 26 | NSApplication 27 | 28 | 29 | -------------------------------------------------------------------------------- /data_AppDelegate.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | #import 4 | #import "MemcacheSnapshot.h"; 5 | 6 | @interface data_AppDelegate : NSObject { 7 | IBOutlet NSTableView *table; 8 | IBOutlet NSTextView *text; 9 | IBOutlet NSPopUpButton *pop; 10 | IBOutlet NSTextField *totalKeysTextField; 11 | IBOutlet NSTextField *cacheHitsTextField; 12 | IBOutlet NSTextField *cacheMissesTextField; 13 | IBOutlet NSTextField *hitRatioTextField; 14 | IBOutlet NSTextField *totalKeySizeTextField; 15 | IBOutlet NSTextField *totalValueSizeTextField; 16 | IBOutlet NSSearchField *searchField; 17 | NSArray *descriptors; 18 | NSString *searchFilter; 19 | MemcacheSnapshot *memcacheSnapshot; 20 | } 21 | 22 | - (IBAction) flushAll: (id) sender; 23 | - (IBAction) flushSelected: (id) sender; 24 | - (IBAction) search: (id) sender; 25 | @property (nonatomic, retain) NSTableView *table; 26 | @property (nonatomic, retain) NSTextView *text; 27 | @property (nonatomic, retain) NSPopUpButton *pop; 28 | @property (nonatomic, retain) NSArray *descriptors; 29 | @property (nonatomic, retain) NSString *searchFilter; 30 | @property (retain) MemcacheSnapshot *memcacheSnapshot; 31 | @end 32 | -------------------------------------------------------------------------------- /MemcacheSnapshot.h: -------------------------------------------------------------------------------- 1 | // 2 | // MemcacheSnapshot.h 3 | // mcinsight 4 | // 5 | // Created by Gerard Gualberto on 6/28/09. 6 | // Copyright 2009 Tierra Innovation. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface MemcacheSnapshot : NSObject { 13 | NSMutableArray *entries; 14 | NSInteger totalKeys; 15 | NSInteger cacheHits; 16 | NSInteger cacheMisses; 17 | NSString *hitRatio; 18 | NSInteger totalKeySize; 19 | NSInteger totalValueSize; 20 | } 21 | 22 | @property (nonatomic, readonly) NSInteger totalKeys; 23 | @property (nonatomic, readonly) NSInteger totalKeySize; 24 | @property (nonatomic, readonly) NSInteger totalValueSize; 25 | @property (nonatomic, readonly) NSInteger cacheHits; 26 | @property (nonatomic, readonly) NSInteger cacheMisses; 27 | @property (nonatomic, readonly) NSString *hitRatio; 28 | @property (assign) NSMutableArray *entries; 29 | 30 | -(NSDictionary*)getEntryAt: (NSInteger)index; 31 | -(NSString*)formatExpiresAt: (NSInteger)expiresAt insertedAt:(NSInteger)insertedAt; 32 | -(void)filterBy: (NSString *)filter; 33 | -(NSString *)stringFromFileSize: (NSInteger)theSize; 34 | -(NSString *)stringFromSeconds: (NSInteger)totalSeconds; 35 | @end 36 | -------------------------------------------------------------------------------- /LogDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // LogDelegate.m 3 | // mcinsight 4 | // 5 | // Created by aa on 7/13/08. 6 | // Copyright 2008 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "LogDelegate.h" 10 | #import "EchoServer.h" 11 | #import "LogInfo.h" 12 | 13 | static BOOL logThreadStarted = NO; 14 | 15 | @implementation LogDelegate 16 | 17 | @synthesize table; 18 | 19 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView { 20 | 21 | if (!logThreadStarted) { 22 | [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; 23 | logThreadStarted = YES; 24 | } 25 | 26 | return [[EchoServer getLog] count]; 27 | } 28 | 29 | - (void)run { 30 | while (TRUE) { 31 | [table reloadData]; 32 | sleep(1); 33 | } 34 | } 35 | 36 | - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex { 37 | NSString *col = [[aTableColumn headerCell] stringValue]; 38 | 39 | LogInfo *info = [[EchoServer getLog] objectAtIndex:rowIndex]; 40 | 41 | if ([col isEqualToString:@"#"]) 42 | return [NSString stringWithFormat:@"%d", rowIndex]; 43 | if ([col isEqualToString:@"id"]) 44 | return [NSString stringWithFormat:@"%d", info.sid]; 45 | if ([col isEqualToString:@"data"]) 46 | return info.data; 47 | if ([col isEqualToString:@"direction"]) { 48 | if (info.direction) 49 | return @"OUT"; 50 | return @"IN"; 51 | } 52 | 53 | return @""; 54 | } 55 | 56 | - (void) dealloc { 57 | [table release]; 58 | [super dealloc]; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /MemcacheSnapshot.m: -------------------------------------------------------------------------------- 1 | // 2 | // MemcacheSnapshot.m 3 | // mcinsight 4 | // 5 | // Created by Gerard Gualberto on 6/28/09. 6 | // Copyright 2009 Tierra Innovation. All rights reserved. 7 | // 8 | 9 | #import "MemcacheSnapshot.h"; 10 | #import "EchoServer.h"; 11 | #import "ValueInfo.h"; 12 | 13 | @implementation MemcacheSnapshot 14 | 15 | @synthesize totalKeys; 16 | @synthesize totalKeySize; 17 | @synthesize totalValueSize; 18 | @synthesize entries; 19 | @synthesize cacheHits; 20 | @synthesize cacheMisses; 21 | @synthesize hitRatio; 22 | 23 | 24 | - (id) init { 25 | if (self = [super init]) { 26 | entries = [NSMutableArray array]; 27 | NSArray *keys = [[EchoServer getDict] allKeys]; 28 | 29 | cacheHits = [EchoServer getTotalHits]; 30 | cacheMisses = [EchoServer getTotalMisses]; 31 | float ratio = ((float)cacheHits/(cacheHits + cacheMisses)) * 100; 32 | 33 | if (cacheMisses > 0) { 34 | hitRatio = [ NSString stringWithFormat: @"%.2f %%", ratio]; 35 | } else { 36 | hitRatio = @"0.00 %"; 37 | } 38 | 39 | 40 | totalKeys = [keys count]; 41 | totalKeySize = 0; 42 | totalValueSize = 0; 43 | for (NSString *key in keys) { 44 | //NSLog (@"key -- %@", key); 45 | 46 | ValueInfo *vi = [[EchoServer getDict] objectForKey:key]; 47 | NSDictionary *cacheData; 48 | cacheData = [NSDictionary dictionaryWithObjectsAndKeys: 49 | key, @"key", 50 | [ self stringFromSeconds: lround([[NSDate date] timeIntervalSince1970] - vi.insertedAt)], @"inserted ago", 51 | [self formatExpiresAt: vi.expiry insertedAt:vi.insertedAt], @"expires in", 52 | [self stringFromFileSize: [key length]], @"key size", 53 | [ NSString stringWithFormat: @"%d", vi.hits], @"hits", 54 | [ self stringFromFileSize: [vi.data length]], @"value size", 55 | vi.data, @"value", 56 | nil 57 | ]; 58 | [entries addObject: cacheData]; 59 | totalKeySize += [key length]; 60 | totalValueSize += [vi.data length]; 61 | } 62 | } 63 | //NSLog (@"totalKeys = %d", totalKeys); 64 | 65 | return self; 66 | } 67 | 68 | -(NSString *)stringFromFileSize: (NSInteger)theSize{ 69 | float floatSize = theSize; 70 | //if (theSize<1023) 71 | // return([NSString stringWithFormat:@"%i b",theSize]); 72 | floatSize = floatSize / 1024; 73 | if (floatSize<1023) 74 | return([NSString stringWithFormat:@"%1.2f KB",floatSize]); 75 | floatSize = floatSize / 1024; 76 | if (floatSize<1023) 77 | return([NSString stringWithFormat:@"%1.1f MB",floatSize]); 78 | floatSize = floatSize / 1024; 79 | 80 | return([NSString stringWithFormat:@"%1.1f GB",floatSize]); 81 | } 82 | 83 | -(NSString *)stringFromSeconds: (NSInteger)totalSeconds{ 84 | int hours = totalSeconds / (60*60); 85 | int seconds_remaing = totalSeconds % (60*60); 86 | int minutes = seconds_remaing / 60; 87 | int seconds = seconds_remaing % 60; 88 | 89 | return [NSString stringWithFormat: @"%d:%02d:%02d",hours, minutes, seconds]; 90 | } 91 | 92 | -(void)filterBy: (NSString *)filter { 93 | if ((filter != nil) && (![filter isEqualToString:@""])){ 94 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(key CONTAINS %@)", filter]; 95 | [entries filterUsingPredicate:predicate]; 96 | } 97 | } 98 | 99 | -(NSDictionary*)getEntryAt: (NSInteger)index { 100 | //now that we're filtering, we should check that requested index still exists 101 | if (index >= 0 && index < [entries count]) { 102 | return [entries objectAtIndex:index]; 103 | } 104 | else { 105 | return nil; 106 | } 107 | } 108 | 109 | -(NSString*)formatExpiresAt: (NSInteger)expiresAt insertedAt:(NSInteger)insertedAt { 110 | if (expiresAt == 0) { 111 | return @"never"; 112 | } else { 113 | int left = expiresAt - lround([[NSDate date] timeIntervalSince1970] - insertedAt); 114 | if (left < 1) { 115 | return @"---"; 116 | } else { 117 | return [ self stringFromSeconds: left ]; 118 | } 119 | } 120 | } 121 | 122 | 123 | @end 124 | -------------------------------------------------------------------------------- /data_AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "data_AppDelegate.h"; 2 | #import "EchoServer.h"; 3 | #import "ValueInfo.h"; 4 | 5 | static BOOL threadStarted = NO; 6 | 7 | @implementation data_AppDelegate 8 | 9 | @synthesize table; 10 | @synthesize text; 11 | @synthesize pop; 12 | @synthesize descriptors; 13 | @synthesize searchFilter; 14 | @synthesize memcacheSnapshot; 15 | 16 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView { 17 | 18 | if (!threadStarted) { 19 | [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; 20 | threadStarted = YES; 21 | } 22 | 23 | return [memcacheSnapshot totalKeys]; 24 | } 25 | 26 | - (void)run { 27 | while (TRUE) { 28 | NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; 29 | 30 | memcacheSnapshot = [[MemcacheSnapshot alloc] init]; 31 | 32 | if ([descriptors count] > 0) { 33 | [memcacheSnapshot.entries sortUsingDescriptors:descriptors]; 34 | } else { 35 | NSSortDescriptor *keyDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"key" 36 | ascending:YES 37 | selector:@selector(localizedCaseInsensitiveCompare:)] autorelease]; 38 | 39 | NSArray *initDescriptors = [NSArray arrayWithObjects:keyDescriptor, nil]; 40 | [memcacheSnapshot.entries sortUsingDescriptors:initDescriptors]; 41 | } 42 | 43 | [memcacheSnapshot filterBy:searchFilter]; 44 | [table reloadData]; 45 | [totalKeysTextField setIntValue:[memcacheSnapshot totalKeys]]; 46 | [totalKeySizeTextField setStringValue: [memcacheSnapshot stringFromFileSize: [memcacheSnapshot totalKeySize]]]; 47 | [totalValueSizeTextField setStringValue: [memcacheSnapshot stringFromFileSize: [memcacheSnapshot totalValueSize]]]; 48 | 49 | [cacheHitsTextField setIntValue:[memcacheSnapshot cacheHits]]; 50 | [cacheMissesTextField setIntValue:[memcacheSnapshot cacheMisses]]; 51 | [hitRatioTextField setStringValue:[memcacheSnapshot hitRatio]]; 52 | sleep(1); 53 | [memcacheSnapshot release]; 54 | [autoreleasepool release]; 55 | } 56 | } 57 | 58 | - (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)offset { 59 | return proposedMax-100; 60 | } 61 | - (CGFloat)splitView:(NSSplitView *)sender constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)offset { 62 | return proposedMin+100; 63 | } 64 | - (CGFloat)splitView:(NSSplitView *)sender constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)offset { 65 | return proposedPosition; 66 | } 67 | 68 | - (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(int)rowIndex { 69 | NSMutableData *value = [[memcacheSnapshot getEntryAt:rowIndex] objectForKey:@"value"]; 70 | 71 | NSString *pops = [[pop selectedItem] title]; 72 | if ([pops isEqualToString:@"Hex"]) 73 | [text setString:[value description]]; 74 | else if ([pops isEqualToString:@"Plain Text"]){ 75 | NSString *tempString = [[NSString alloc] initWithData:value encoding:NSASCIIStringEncoding]; 76 | [text setString:tempString]; 77 | [tempString release]; 78 | } 79 | else if ([pops isEqualToString:@"Ruby Objects"]) { 80 | NSPipe *inPipe = [NSPipe pipe]; 81 | NSPipe *outPipe = [NSPipe pipe]; 82 | 83 | NSFileHandle *inFile = [inPipe fileHandleForWriting]; 84 | [inFile writeData:value]; 85 | [inFile closeFile]; 86 | 87 | NSTask *myTask = [[NSTask alloc] init]; 88 | [myTask setLaunchPath: @"/usr/bin/ruby"]; 89 | [myTask setArguments: [NSArray arrayWithObjects:@"-e", @"puts Marshal.load(STDIN.read).inspect", nil]]; 90 | [myTask setStandardInput: inPipe]; 91 | [myTask setStandardOutput: outPipe]; 92 | [myTask launch]; 93 | [myTask waitUntilExit]; 94 | [myTask release]; 95 | 96 | NSString *tempString = [[NSString alloc] initWithData:[[outPipe fileHandleForReading] readDataToEndOfFile] 97 | encoding:NSASCIIStringEncoding]; 98 | [text setString:tempString]; 99 | [tempString release]; 100 | } 101 | 102 | [text setFont:[NSFont fontWithName:@"Courier" size:14.0]]; 103 | 104 | [table selectRowIndexes: [NSIndexSet indexSetWithIndex:rowIndex] byExtendingSelection:false]; 105 | return true; 106 | } 107 | 108 | - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex { 109 | 110 | NSString *col = [[aTableColumn headerCell] stringValue]; 111 | NSDictionary *entry = [memcacheSnapshot getEntryAt:rowIndex]; 112 | return [entry objectForKey:col]; 113 | } 114 | 115 | - (void)tableView:(NSTableView *)aTableView sortDescriptorsDidChange:(NSArray *)oldDescriptors 116 | { 117 | NSArray *newDescriptors = [aTableView sortDescriptors]; 118 | descriptors = newDescriptors; 119 | [memcacheSnapshot.entries sortUsingDescriptors:newDescriptors]; 120 | [aTableView reloadData]; 121 | } 122 | 123 | - (IBAction) flushAll: (id) sender { 124 | [[EchoServer getDict] removeAllObjects]; 125 | [table reloadData]; 126 | [text setString:@""]; 127 | } 128 | 129 | - (IBAction) flushSelected: (id) sender { 130 | if ([table selectedRow] > -1) { 131 | NSDictionary *entry = [memcacheSnapshot getEntryAt:[table selectedRow]]; 132 | [[EchoServer getDict] removeObjectForKey: [entry objectForKey:@"key"]]; 133 | [table reloadData]; 134 | [text setString:@""]; 135 | } 136 | } 137 | 138 | - (IBAction) search: (id) sender { 139 | searchFilter = [searchField stringValue]; 140 | NSLog(@"%@", searchFilter); 141 | [memcacheSnapshot filterBy:searchFilter]; 142 | [table reloadData]; 143 | } 144 | 145 | - (void) dealloc { 146 | [table release]; 147 | [memcacheSnapshot release]; 148 | [super dealloc]; 149 | } 150 | 151 | 152 | @end 153 | -------------------------------------------------------------------------------- /mcinsight.xcodeproj/josh.pbxuser: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | 000BB3A20E2A986B009BBF2D /* LogDelegate.h */ = { 4 | uiCtxt = { 5 | sepNavIntBoundsRect = "{{0, 0}, {691, 430}}"; 6 | sepNavSelRange = "{0, 0}"; 7 | sepNavVisRange = "{0, 182}"; 8 | sepNavWindowFrame = "{{38, 444}, {750, 558}}"; 9 | }; 10 | }; 11 | 000BB3A90E2A9B3C009BBF2D /* LogInfo.h */ = { 12 | uiCtxt = { 13 | sepNavIntBoundsRect = "{{0, 0}, {691, 430}}"; 14 | sepNavSelRange = "{0, 0}"; 15 | sepNavVisRange = "{0, 334}"; 16 | sepNavWindowFrame = "{{61, 423}, {750, 558}}"; 17 | }; 18 | }; 19 | 001020A90E27C111006BD8F1 /* ValueInfo.h */ = { 20 | uiCtxt = { 21 | sepNavIntBoundsRect = "{{0, 0}, {691, 430}}"; 22 | sepNavSelRange = "{0, 0}"; 23 | sepNavVisRange = "{0, 601}"; 24 | sepNavWindowFrame = "{{130, 360}, {750, 558}}"; 25 | }; 26 | }; 27 | 001020AA0E27C111006BD8F1 /* ValueInfo.m */ = { 28 | uiCtxt = { 29 | sepNavIntBoundsRect = "{{0, 0}, {691, 430}}"; 30 | sepNavSelRange = "{152, 0}"; 31 | sepNavVisRange = "{0, 350}"; 32 | sepNavWindowFrame = "{{153, 339}, {750, 558}}"; 33 | }; 34 | }; 35 | 0031C4610E25C312001217B5 /* data_AppDelegate.h */ = { 36 | uiCtxt = { 37 | sepNavIntBoundsRect = "{{0, 0}, {691, 430}}"; 38 | sepNavSelRange = "{0, 0}"; 39 | sepNavVisRange = "{0, 345}"; 40 | sepNavWindowFrame = "{{84, 402}, {750, 558}}"; 41 | }; 42 | }; 43 | 0085C0300E265EAE00C14E74 /* Runner.h */ = { 44 | uiCtxt = { 45 | sepNavIntBoundsRect = "{{0, 0}, {691, 430}}"; 46 | sepNavSelRange = "{0, 0}"; 47 | sepNavVisRange = "{0, 192}"; 48 | sepNavWindowFrame = "{{107, 381}, {750, 558}}"; 49 | }; 50 | }; 51 | 00FE564F0E265CA8008DD4BA /* EchoServer.m */ = { 52 | uiCtxt = { 53 | sepNavIntBoundsRect = "{{0, 0}, {990, 3318}}"; 54 | sepNavSelRange = "{7895, 0}"; 55 | sepNavVisRange = "{7058, 1895}"; 56 | sepNavWindowFrame = "{{459, 170}, {1049, 832}}"; 57 | }; 58 | }; 59 | 00FE56510E265CEB008DD4BA /* EchoServer.h */ = { 60 | uiCtxt = { 61 | sepNavIntBoundsRect = "{{0, 0}, {691, 430}}"; 62 | sepNavSelRange = "{0, 0}"; 63 | sepNavVisRange = "{0, 680}"; 64 | sepNavWindowFrame = "{{15, 465}, {750, 558}}"; 65 | }; 66 | }; 67 | 036BA1440FD4BCD10069CA45 /* mcinsight */ = { 68 | isa = PBXExecutable; 69 | activeArgIndices = ( 70 | ); 71 | argumentStrings = ( 72 | ); 73 | autoAttachOnCrash = 1; 74 | breakpointsEnabled = 0; 75 | configStateDict = { 76 | }; 77 | customDataFormattersEnabled = 1; 78 | debuggerPlugin = GDBDebugging; 79 | disassemblyDisplayState = 0; 80 | dylibVariantSuffix = ""; 81 | enableDebugStr = 1; 82 | environmentEntries = ( 83 | ); 84 | executableSystemSymbolLevel = 0; 85 | executableUserSymbolLevel = 0; 86 | libgmallocEnabled = 0; 87 | name = mcinsight; 88 | sourceDirectories = ( 89 | ); 90 | }; 91 | 036BA1520FD4BCD30069CA45 /* Source Control */ = { 92 | isa = PBXSourceControlManager; 93 | fallbackIsa = XCSourceControlManager; 94 | isSCMEnabled = 0; 95 | scmConfiguration = { 96 | }; 97 | }; 98 | 036BA1530FD4BCD30069CA45 /* Code sense */ = { 99 | isa = PBXCodeSenseManager; 100 | indexTemplatePath = ""; 101 | }; 102 | 036BA17D0FD4C0650069CA45 /* PBXTextBookmark */ = { 103 | isa = PBXTextBookmark; 104 | fRef = 00FE564F0E265CA8008DD4BA /* EchoServer.m */; 105 | name = "EchoServer.m: 194"; 106 | rLen = 0; 107 | rLoc = 6611; 108 | rType = 0; 109 | vrLen = 2165; 110 | vrLoc = 5914; 111 | }; 112 | 036BA1A30FD4C1F60069CA45 /* PBXTextBookmark */ = { 113 | isa = PBXTextBookmark; 114 | fRef = 00FE564F0E265CA8008DD4BA /* EchoServer.m */; 115 | name = "EchoServer.m: 194"; 116 | rLen = 0; 117 | rLoc = 6611; 118 | rType = 0; 119 | vrLen = 2164; 120 | vrLoc = 5914; 121 | }; 122 | 03B469710FD4CE1800DC8053 /* PBXTextBookmark */ = { 123 | isa = PBXTextBookmark; 124 | fRef = 00FE564F0E265CA8008DD4BA /* EchoServer.m */; 125 | name = "EchoServer.m: 194"; 126 | rLen = 0; 127 | rLoc = 6611; 128 | rType = 0; 129 | vrLen = 2107; 130 | vrLoc = 5914; 131 | }; 132 | 03B4697C0FD4CEB100DC8053 /* PBXTextBookmark */ = { 133 | isa = PBXTextBookmark; 134 | fRef = 00FE564F0E265CA8008DD4BA /* EchoServer.m */; 135 | name = "EchoServer.m: 194"; 136 | rLen = 0; 137 | rLoc = 6611; 138 | rType = 0; 139 | vrLen = 2107; 140 | vrLoc = 5914; 141 | }; 142 | 03B4697D0FD4CEB100DC8053 /* PBXTextBookmark */ = { 143 | isa = PBXTextBookmark; 144 | fRef = 00FE564F0E265CA8008DD4BA /* EchoServer.m */; 145 | name = "EchoServer.m: 220"; 146 | rLen = 0; 147 | rLoc = 7948; 148 | rType = 0; 149 | vrLen = 2054; 150 | vrLoc = 6631; 151 | }; 152 | 03B4697E0FD4CF3E00DC8053 /* PBXTextBookmark */ = { 153 | isa = PBXTextBookmark; 154 | fRef = 00FE564F0E265CA8008DD4BA /* EchoServer.m */; 155 | name = "EchoServer.m: 218"; 156 | rLen = 0; 157 | rLoc = 7895; 158 | rType = 0; 159 | vrLen = 1895; 160 | vrLoc = 7058; 161 | }; 162 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 163 | activeBuildConfigurationName = Release; 164 | activeExecutable = 036BA1440FD4BCD10069CA45 /* mcinsight */; 165 | activeTarget = 8D1107260486CEB800E47090 /* mcinsight */; 166 | codeSenseManager = 036BA1530FD4BCD30069CA45 /* Code sense */; 167 | executables = ( 168 | 036BA1440FD4BCD10069CA45 /* mcinsight */, 169 | ); 170 | perUserDictionary = { 171 | PBXConfiguration.PBXFileTableDataSource3.PBXErrorsWarningsDataSource = { 172 | PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; 173 | PBXFileTableDataSourceColumnSortingKey = PBXErrorsWarningsDataSource_LocationID; 174 | PBXFileTableDataSourceColumnWidthsKey = ( 175 | 20, 176 | 274, 177 | 636, 178 | ); 179 | PBXFileTableDataSourceColumnsKey = ( 180 | PBXErrorsWarningsDataSource_TypeID, 181 | PBXErrorsWarningsDataSource_MessageID, 182 | PBXErrorsWarningsDataSource_LocationID, 183 | ); 184 | }; 185 | PBXConfiguration.PBXFileTableDataSource3.PBXExecutablesDataSource = { 186 | PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; 187 | PBXFileTableDataSourceColumnSortingKey = PBXExecutablesDataSource_NameID; 188 | PBXFileTableDataSourceColumnWidthsKey = ( 189 | 22, 190 | 300, 191 | 608, 192 | ); 193 | PBXFileTableDataSourceColumnsKey = ( 194 | PBXExecutablesDataSource_ActiveFlagID, 195 | PBXExecutablesDataSource_NameID, 196 | PBXExecutablesDataSource_CommentsID, 197 | ); 198 | }; 199 | PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = { 200 | PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; 201 | PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; 202 | PBXFileTableDataSourceColumnWidthsKey = ( 203 | 20, 204 | 720, 205 | 20, 206 | 48, 207 | 43, 208 | 43, 209 | 20, 210 | ); 211 | PBXFileTableDataSourceColumnsKey = ( 212 | PBXFileDataSource_FiletypeID, 213 | PBXFileDataSource_Filename_ColumnID, 214 | PBXFileDataSource_Built_ColumnID, 215 | PBXFileDataSource_ObjectSize_ColumnID, 216 | PBXFileDataSource_Errors_ColumnID, 217 | PBXFileDataSource_Warnings_ColumnID, 218 | PBXFileDataSource_Target_ColumnID, 219 | ); 220 | }; 221 | PBXPerProjectTemplateStateSaveDate = 265604607; 222 | PBXWorkspaceStateSaveDate = 265604607; 223 | }; 224 | perUserProjectItems = { 225 | 036BA17D0FD4C0650069CA45 = 036BA17D0FD4C0650069CA45 /* PBXTextBookmark */; 226 | 036BA1A30FD4C1F60069CA45 = 036BA1A30FD4C1F60069CA45 /* PBXTextBookmark */; 227 | 03B469710FD4CE1800DC8053 /* PBXTextBookmark */ = 03B469710FD4CE1800DC8053 /* PBXTextBookmark */; 228 | 03B4697C0FD4CEB100DC8053 /* PBXTextBookmark */ = 03B4697C0FD4CEB100DC8053 /* PBXTextBookmark */; 229 | 03B4697D0FD4CEB100DC8053 /* PBXTextBookmark */ = 03B4697D0FD4CEB100DC8053 /* PBXTextBookmark */; 230 | 03B4697E0FD4CF3E00DC8053 /* PBXTextBookmark */ = 03B4697E0FD4CF3E00DC8053 /* PBXTextBookmark */; 231 | }; 232 | sourceControlManager = 036BA1520FD4BCD30069CA45 /* Source Control */; 233 | userBuildSettings = { 234 | }; 235 | }; 236 | 29B97316FDCFA39411CA2CEA /* main.m */ = { 237 | uiCtxt = { 238 | sepNavIntBoundsRect = "{{0, 0}, {691, 430}}"; 239 | sepNavSelRange = "{0, 0}"; 240 | sepNavVisRange = "{0, 461}"; 241 | sepNavWindowFrame = "{{176, 318}, {750, 558}}"; 242 | }; 243 | }; 244 | 8D1107260486CEB800E47090 /* mcinsight */ = { 245 | activeExec = 0; 246 | executables = ( 247 | 036BA1440FD4BCD10069CA45 /* mcinsight */, 248 | ); 249 | }; 250 | } 251 | -------------------------------------------------------------------------------- /AsyncSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // AsyncSocket.h 3 | // 4 | // Created by Dustin Voss on Wed Jan 29 2003. 5 | // This class is in the public domain. 6 | // If used, I'd appreciate it if you credit me. 7 | // 8 | // E-Mail: d-j-v@earthlink.net 9 | // 10 | 11 | /* 12 | * Make sure to include /System/Library/Frameworks/CoreServices.framework in the project. 13 | */ 14 | 15 | #import 16 | #import "ValueInfo.h" 17 | 18 | @class AsyncSocket; 19 | @class AsyncReadPacket; 20 | @class AsyncWritePacket; 21 | 22 | extern NSString *const AsyncSocketException; 23 | extern NSString *const AsyncSocketErrorDomain; 24 | 25 | enum AsyncSocketError 26 | { 27 | AsyncSocketCFSocketError = kCFSocketError, // From CFSocketError enum. 28 | AsyncSocketNoError = 0, // Never used. 29 | AsyncSocketCanceledError, // onSocketWillConnect: returned NO. 30 | AsyncSocketReadTimeoutError, 31 | AsyncSocketWriteTimeoutError 32 | }; 33 | typedef enum AsyncSocketError AsyncSocketError; 34 | 35 | @interface NSObject ( AsyncSocketDelegate ) 36 | 37 | /* In the event of an error, the socket is closed. You may call "readDataWithTimeout:tag:" during this call-back to get the last bit of data off the socket. When connecting, this delegate method may be called before "onSocket:didAcceptNewSocket:" or "onSocket:didConnectToHost:". */ 38 | -(void) onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err; 39 | 40 | /* Called when a socket disconnects with or without error. If you want to release a socket after it disconnects, do so here. It is not safe to do that during "onSocket:willDisconnectWithError:". */ 41 | -(void) onSocketDidDisconnect:(AsyncSocket *)sock; 42 | 43 | /* Called when a socket accepts a connection. Another socket is spawned to handle it. The new socket will have the same delegate and will call "onSocket:didConnectToHost:port:". */ 44 | -(void) onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket; 45 | 46 | /* Called when a new socket is spawned to handle a connection. This method should return the run-loop of the thread on which the new socket and its delegate should operate. If omitted, [NSRunLoop currentRunLoop] is used. */ 47 | -(NSRunLoop *) onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket; 48 | 49 | /* Called when a socket is about to connect. This method should return YES to continue, or NO to abort. If aborted, will result in AsyncSocketCanceledError. */ 50 | -(BOOL) onSocketWillConnect:(AsyncSocket *)sock; 51 | 52 | /* Called when a socket connects and is ready for reading and writing. "host" will be an IP address, not a DNS name. */ 53 | -(void) onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port; 54 | 55 | /* Called when a socket has completed reading the requested data. Not called if there is an error. */ 56 | -(void) onSocket:(AsyncSocket *)sock didReadData:(NSData*)data withTag:(long)tag; 57 | 58 | /* Called when a socket has completed writing the requested data. Not called if there is an error. */ 59 | -(void) onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag; 60 | 61 | @end 62 | 63 | @interface AsyncSocket : NSObject 64 | { 65 | CFSocketRef theSocket; // IPv4/IPv6 accept or connect socket. 66 | CFSocketRef theSocket6; // IPv6 accept socket. 67 | CFReadStreamRef theReadStream; 68 | CFWriteStreamRef theWriteStream; 69 | 70 | CFRunLoopSourceRef theSource; // For theSocket. 71 | CFRunLoopSourceRef theSource6; // For theSocket6. 72 | CFRunLoopRef theRunLoop; 73 | CFSocketContext theContext; 74 | 75 | NSMutableArray *theReadQueue; 76 | AsyncReadPacket *theCurrentRead; 77 | NSTimer *theReadTimer; 78 | NSData *partialReadBuffer; 79 | 80 | NSMutableArray *theWriteQueue; 81 | AsyncWritePacket *theCurrentWrite; 82 | NSTimer *theWriteTimer; 83 | 84 | NSTimer *thePollTimer; 85 | id theDelegate; 86 | Byte theFlags; 87 | 88 | long theUserData; 89 | 90 | BOOL mc_dataMode; 91 | int mc_size; 92 | ValueInfo *mc_vi; 93 | } 94 | 95 | @property (nonatomic, retain) ValueInfo *mc_vi; 96 | @property BOOL mc_dataMode; 97 | @property int mc_size; 98 | 99 | - (id) init; 100 | - (id) initWithDelegate:(id)delegate; 101 | - (id) initWithDelegate:(id)delegate userData:(long)userData; 102 | - (void) dealloc; 103 | 104 | /* String representation is long but has no "\n". */ 105 | - (NSString *) description; 106 | 107 | /* Use "canSafelySetDelegate" to see if there is any pending business (reads and writes) with the current delegate before changing it. It is, of course, safe to change the delegate before connecting or accepting connections. */ 108 | - (id) delegate; 109 | - (BOOL) canSafelySetDelegate; 110 | - (void) setDelegate:(id)delegate; 111 | 112 | /* User data can be a long, or an id or void * cast to a long. */ 113 | - (long) userData; 114 | - (void) setUserData:(long)userData; 115 | 116 | /* Don't use these to read or write. And don't close them, either! */ 117 | - (CFSocketRef) getCFSocket; 118 | - (CFReadStreamRef) getCFReadStream; 119 | - (CFWriteStreamRef) getCFWriteStream; 120 | 121 | /* Once one of these methods is called, the AsyncSocket instance is locked in, and the rest can't be called without disconnecting the socket first. If the attempt times out or fails, these methods either return NO or call "onSocket:willDisconnectWithError:" and "onSockedDidDisconnect:". */ 122 | - (BOOL) acceptOnPort:(UInt16)port error:(NSError **)errPtr; 123 | - (BOOL) acceptOnAddress:(NSString *)hostaddr port:(UInt16)port error:(NSError **)errPtr; 124 | - (BOOL) connectToHost:(NSString*)hostname onPort:(UInt16)port error:(NSError **)errPtr; 125 | 126 | /* Disconnects immediately. Any pending reads or writes are dropped. */ 127 | - (void) disconnect; 128 | 129 | /* Disconnects after all pending writes have completed. After calling this, the read and write methods (including "readDataWithTimeout:tag:") will do nothing. The socket will disconnect even if there are still pending reads. */ 130 | - (void) disconnectAfterWriting; 131 | 132 | /* Returns YES if the socket and streams are open, connected, and ready for reading and writing. */ 133 | - (BOOL) isConnected; 134 | 135 | /* Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected. The host will be an IP address. */ 136 | - (NSString *) connectedHost; 137 | - (UInt16) connectedPort; 138 | 139 | - (NSString *) localHost; 140 | - (UInt16) localPort; 141 | 142 | /* The following methods won't block. To not time out, use a negative time interval. If they time out, "onSocket:disconnectWithError:" is called. The tag is for your convenience. You can use it as an array index, step number, state id, pointer, etc., just like the socket's user data. */ 143 | 144 | /* This will read a certain number of bytes, and call the delegate method when those bytes have been read. If there is an error, partially read data is lost. If the length is 0, this method does nothing and the delegate is not called. */ 145 | - (void) readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag; 146 | 147 | /* This reads bytes until (and including) the passed "data" parameter, which acts as a separator. The bytes and the separator are returned by the delegate method. If you pass nil or 0-length data as the "data" parameter, the method will do nothing, and the delegate will not be called. To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for a character, the read will prematurely end. */ 148 | - (void) readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; 149 | 150 | /* This reads the first available bytes. */ 151 | - (void) readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag; 152 | 153 | /* Writes data. If you pass in nil or 0-length data, this method does nothing and the delegate will not be called. */ 154 | - (void) writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; 155 | 156 | /* Returns progress of current read or write, from 0.0 to 1.0, or NaN if no read/write (use isnan() to check). "tag", "done" and "total" will be filled in if they aren't NULL. */ 157 | - (float) progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total; 158 | - (float) progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total; 159 | 160 | /* A few common line separators, for use with "readDataToData:withTimeout:tag:". */ 161 | + (NSData *) CRLFData; // 0x0D0A 162 | + (NSData *) CRData; // 0x0D 163 | + (NSData *) LFData; // 0x0A 164 | + (NSData *) ZeroData; // 0x00 165 | 166 | @end 167 | -------------------------------------------------------------------------------- /EchoServer.m: -------------------------------------------------------------------------------- 1 | 2 | #import "EchoServer.h" 3 | #import "ValueInfo.h" 4 | #import "LogInfo.h" 5 | 6 | static NSMutableDictionary *dict; 7 | static NSMutableArray *loggy; 8 | static NSInteger totalHits; 9 | static NSInteger totalMisses; 10 | 11 | @implementation EchoServer 12 | 13 | +(NSMutableDictionary*)getDict { 14 | return dict; 15 | } 16 | +(NSMutableArray*)getLog { 17 | return loggy; 18 | } 19 | +(NSInteger)getTotalHits { 20 | return totalHits; 21 | } 22 | +(NSInteger)getTotalMisses { 23 | return totalMisses; 24 | } 25 | 26 | -(id) init 27 | { 28 | self = [super init]; 29 | dict = [NSMutableDictionary dictionary]; 30 | loggy = [NSMutableArray array]; 31 | sockets = [[NSMutableArray alloc] initWithCapacity:2]; 32 | totalHits = 0; 33 | totalMisses = 0; 34 | 35 | AsyncSocket *acceptor = [[AsyncSocket alloc] initWithDelegate:self]; 36 | [sockets addObject:acceptor]; 37 | [acceptor release]; 38 | return self; 39 | } 40 | 41 | -(void) dealloc 42 | { 43 | [sockets release]; 44 | [super dealloc]; 45 | } 46 | 47 | - (void) acceptOnPortString:(NSString *)str 48 | { 49 | NSAssert ([[NSRunLoop currentRunLoop] currentMode] != nil, @"Run loop is not running"); 50 | 51 | UInt16 port = [str intValue]; 52 | AsyncSocket *acceptor = (AsyncSocket *)[sockets objectAtIndex:0]; 53 | 54 | NSError *err = nil; 55 | if ([acceptor acceptOnPort:port error:&err]){ 56 | //NSLog (@"Waiting for connections on port %u.", port); 57 | } 58 | else 59 | { 60 | //NSLog (@"Cannot accept connections on port %u. Error domain %@ code %d (%@). Exiting.", port, [err domain], [err code], [err localizedDescription]); 61 | exit(1); 62 | } 63 | } 64 | 65 | -(ValueInfo *)getVI:(NSString *)key { 66 | ValueInfo *temp = [dict objectForKey:key]; 67 | 68 | if (temp == nil) 69 | return nil; 70 | 71 | if (temp.expiry == 0) 72 | return temp; 73 | int left = temp.expiry - lround([[NSDate date] timeIntervalSince1970] - temp.insertedAt); 74 | if (left < 1) { 75 | [dict removeObjectForKey:key]; 76 | return nil; 77 | } 78 | return temp; 79 | } 80 | 81 | -(void)sendOut:(AsyncSocket *)sock string:(NSString *)string tag:(long)tag { 82 | LogInfo *info = [LogInfo alloc]; 83 | info.data = string; 84 | info.sid = tag; 85 | info.direction = YES; 86 | [loggy addObject:info]; 87 | [info release]; 88 | [sock writeData:[[string stringByAppendingString:@"\r\n"] dataUsingEncoding:NSASCIIStringEncoding] withTimeout:-1 tag:tag]; 89 | } 90 | 91 | -(void) onSocket:(AsyncSocket *)sock didReadData:(NSData*)data withTag:(long)tag 92 | { 93 | // set session:99e825b027f10f2688b0a67ec570acca 0 1800 61\r\n 94 | // wefwelfkwelfwelfkwelfkwelfwef\r\n 95 | 96 | if (sock.mc_dataMode) { 97 | [sock.mc_vi.data appendData:data]; 98 | 99 | if ([sock.mc_vi.data length] - 2 == sock.mc_size) { 100 | sock.mc_dataMode = NO; 101 | [sock.mc_vi.data setLength:[sock.mc_vi.data length] - 2]; 102 | sock.mc_vi.insertedAt = [[NSDate date] timeIntervalSince1970]; 103 | 104 | LogInfo *info = [LogInfo alloc]; 105 | info.data = @"BINARY DATA"; 106 | info.sid = tag; 107 | [loggy addObject:info]; 108 | 109 | ValueInfo *temp = [self getVI:sock.mc_vi.key]; 110 | [info release]; 111 | 112 | if ([sock.mc_vi.command isEqualToString:@"add"]) { 113 | if (temp == nil) { 114 | [dict setObject:sock.mc_vi forKey:sock.mc_vi.key]; 115 | [self sendOut:sock string:@"STORED" tag:tag]; 116 | } 117 | else 118 | [self sendOut:sock string:@"NOT_STORED" tag:tag]; 119 | } else if ([sock.mc_vi.command isEqualToString:@"set"]) { 120 | [dict setObject:sock.mc_vi forKey:sock.mc_vi.key]; 121 | [self sendOut:sock string:@"STORED" tag:tag]; 122 | } else if ([sock.mc_vi.command isEqualToString:@"append"]) { 123 | if (temp != nil) { 124 | [temp.data appendData:sock.mc_vi.data]; 125 | [dict setObject:temp forKey:sock.mc_vi.key]; 126 | [self sendOut:sock string:@"STORED" tag:tag]; 127 | } else 128 | [self sendOut:sock string:@"NOT_STORED" tag:tag]; 129 | } else if ([sock.mc_vi.command isEqualToString:@"prepend"]) { 130 | if (temp != nil) { 131 | [sock.mc_vi.data appendData:temp.data]; 132 | temp.data = sock.mc_vi.data; 133 | [dict setObject:temp forKey:sock.mc_vi.key]; 134 | [self sendOut:sock string:@"STORED" tag:tag]; 135 | } else 136 | [self sendOut:sock string:@"NOT_STORED" tag:tag]; 137 | } else if ([sock.mc_vi.command isEqualToString:@"cas"]) { 138 | [dict setObject:sock.mc_vi forKey:sock.mc_vi.key]; 139 | [self sendOut:sock string:@"STORED" tag:tag]; 140 | } else if ([sock.mc_vi.command isEqualToString:@"replace"]) { 141 | if (temp != nil) { 142 | [dict setObject:sock.mc_vi forKey:sock.mc_vi.key]; 143 | [self sendOut:sock string:@"STORED" tag:tag]; 144 | } else 145 | [self sendOut:sock string:@"NOT_STORED" tag:tag]; 146 | } 147 | } 148 | 149 | } else { 150 | NSString *str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; 151 | NSString *str2 = [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 152 | 153 | [str release]; 154 | LogInfo *info = [LogInfo alloc]; 155 | info.data = str2; 156 | info.sid = tag; 157 | [loggy addObject:info]; 158 | 159 | NSArray *listItems = [str2 componentsSeparatedByString:@" "]; 160 | 161 | NSString *command = [listItems objectAtIndex:0]; 162 | NSString *key = [listItems objectAtIndex:1]; 163 | [info release]; 164 | 165 | if ([command isEqualToString:@"set"] || [command isEqualToString:@"add"] || [command isEqualToString:@"replace"] || 166 | [command isEqualToString:@"append"] || [command isEqualToString:@"prepend"] || [command isEqualToString:@"cas"]) { 167 | sock.mc_vi = [[ValueInfo alloc] init]; 168 | sock.mc_vi.key = [listItems objectAtIndex:1]; 169 | sock.mc_vi.flag = [listItems objectAtIndex:2]; 170 | sock.mc_vi.expiry = [[listItems objectAtIndex:3] intValue]; 171 | sock.mc_size = [[listItems objectAtIndex:4] intValue]; 172 | sock.mc_dataMode = YES; 173 | sock.mc_vi.data = [NSMutableData alloc]; 174 | sock.mc_vi.command = command; 175 | } else if ([command isEqualToString:@"get"] || [command isEqualToString:@"gets"]) { 176 | // get session:99e825b027f10f2688b0a67ec570acca 177 | // VALUE session:99e825b027f10f2688b0a67ec570acca 0 61\r\n 178 | // ewfjwekfjwekfjwekfjwekfjkwefjk\r\n 179 | // END 180 | int i = 1; 181 | while (true) { 182 | ValueInfo *temp = [self getVI:[listItems objectAtIndex:i]]; 183 | if (temp) { 184 | temp.hits++; 185 | totalHits++; 186 | [self sendOut:sock string:[NSString stringWithFormat:@"VALUE %@ %@ %d", temp.key, temp.flag, [temp.data length]] tag:tag]; 187 | 188 | LogInfo *info = [LogInfo alloc]; 189 | info.data = @"BINARY DATA"; 190 | info.sid = tag; 191 | info.direction = YES; 192 | [loggy addObject:info]; 193 | [info release]; 194 | 195 | [sock writeData:temp.data withTimeout:-1 tag:tag]; 196 | [sock writeData:[@"\r\n" dataUsingEncoding:NSASCIIStringEncoding] withTimeout:-1 tag:tag]; 197 | } else { 198 | totalMisses++; 199 | } 200 | i++; 201 | if (i >= [listItems count]) 202 | break; 203 | } 204 | 205 | [self sendOut:sock string:@"END" tag:tag]; 206 | } else if ([command isEqualToString:@"incr"]) { 207 | ValueInfo *temp = [self getVI:key]; 208 | 209 | if (temp) { 210 | NSString *sval = [[NSString alloc] initWithData:temp.data encoding:NSASCIIStringEncoding]; 211 | unsigned long long val = [sval longLongValue] + 1; 212 | [sval release]; 213 | temp.data = [NSMutableData dataWithData:[[NSString stringWithFormat:@"%d", val] dataUsingEncoding:NSASCIIStringEncoding]]; 214 | [dict setObject:temp forKey:key]; 215 | [self sendOut:sock string:[NSString stringWithFormat:@"%d", val] tag:tag]; 216 | } else 217 | [self sendOut:sock string:@"NOT_FOUND" tag:tag]; 218 | } else if ([command isEqualToString:@"decr"]) { 219 | ValueInfo *temp = [self getVI:key]; 220 | if (temp) { 221 | NSString *sval = [[NSString alloc] initWithData:temp.data encoding:NSASCIIStringEncoding]; 222 | unsigned long long val = [sval longLongValue] - 1; 223 | [sval release]; 224 | if (val < 0) 225 | val = 0; 226 | temp.data = [NSMutableData dataWithData:[[NSString stringWithFormat:@"%d", val] dataUsingEncoding:NSASCIIStringEncoding]]; 227 | [dict setObject:temp forKey:key]; 228 | [self sendOut:sock string:[NSString stringWithFormat:@"%d", val] tag:tag]; 229 | } else 230 | [self sendOut:sock string:@"NOT_FOUND" tag:tag]; 231 | } else if ([command isEqualToString:@"delete"]) { 232 | ValueInfo *temp = [dict objectForKey:key]; 233 | if (temp) { 234 | [[EchoServer getDict] removeObjectForKey:key]; 235 | } 236 | [self sendOut:sock string:@"DELETED" tag:tag]; 237 | } else if ([command isEqualToString:@"flush_all"]) { 238 | [[EchoServer getDict] removeAllObjects]; 239 | [self sendOut:sock string:@"DELETED" tag:tag]; 240 | } 241 | } 242 | 243 | NSData *newline = [@"\n" dataUsingEncoding:NSASCIIStringEncoding]; 244 | [sock readDataToData:newline withTimeout:-1 tag:tag]; 245 | } 246 | 247 | -(void) onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err 248 | { 249 | if (err != nil) { 250 | //NSLog (@"Socket will disconnect. Error domain %@, code %d (%@).", [err domain], [err code], [err localizedDescription]); 251 | } 252 | else { 253 | //NSLog (@"Socket will disconnect. No error."); 254 | } 255 | } 256 | 257 | 258 | -(void) onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket 259 | { 260 | //NSLog (@"Socket %d accepting connection.", [sockets count]); 261 | [sockets addObject:newSocket]; 262 | } 263 | 264 | -(void) onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port 265 | { 266 | //NSLog (@"Socket %d successfully accepted connection from %@ %u.", [sockets indexOfObject:sock], host, port); 267 | NSData *newline = [@"\n" dataUsingEncoding:NSASCIIStringEncoding]; 268 | 269 | [sock readDataToData:newline withTimeout:-1 tag:[sockets indexOfObject:sock]]; 270 | } 271 | 272 | 273 | -(void) onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag 274 | { 275 | //NSLog (@"Wrote to socket %d.", tag); 276 | } 277 | 278 | 279 | 280 | 281 | @end 282 | -------------------------------------------------------------------------------- /mcinsight.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 45; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 000BB3A40E2A986B009BBF2D /* LogDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 000BB3A30E2A986B009BBF2D /* LogDelegate.m */; }; 11 | 000BB3AB0E2A9B3C009BBF2D /* LogInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 000BB3AA0E2A9B3C009BBF2D /* LogInfo.m */; }; 12 | 001020AB0E27C111006BD8F1 /* ValueInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 001020AA0E27C111006BD8F1 /* ValueInfo.m */; }; 13 | 0031C4630E25C312001217B5 /* data_AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0031C4620E25C312001217B5 /* data_AppDelegate.m */; }; 14 | 0085C0320E265EAE00C14E74 /* Runner.m in Sources */ = {isa = PBXBuildFile; fileRef = 0085C0310E265EAE00C14E74 /* Runner.m */; }; 15 | 00FE56340E26559F008DD4BA /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00FE56330E26559F008DD4BA /* CoreServices.framework */; }; 16 | 00FE563A0E2655AF008DD4BA /* AsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 00FE56390E2655AF008DD4BA /* AsyncSocket.m */; }; 17 | 00FE56500E265CA8008DD4BA /* EchoServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 00FE564F0E265CA8008DD4BA /* EchoServer.m */; }; 18 | 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; }; 19 | 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 20 | 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 21 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; 22 | D52C5E1A0FF840A100F25DA7 /* MemcacheSnapshot.m in Sources */ = {isa = PBXBuildFile; fileRef = D52C5E190FF840A100F25DA7 /* MemcacheSnapshot.m */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 000BB3A20E2A986B009BBF2D /* LogDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogDelegate.h; sourceTree = ""; }; 27 | 000BB3A30E2A986B009BBF2D /* LogDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LogDelegate.m; sourceTree = ""; }; 28 | 000BB3A90E2A9B3C009BBF2D /* LogInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogInfo.h; sourceTree = ""; }; 29 | 000BB3AA0E2A9B3C009BBF2D /* LogInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LogInfo.m; sourceTree = ""; }; 30 | 001020A90E27C111006BD8F1 /* ValueInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ValueInfo.h; sourceTree = ""; }; 31 | 001020AA0E27C111006BD8F1 /* ValueInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ValueInfo.m; sourceTree = ""; }; 32 | 0031C4610E25C312001217B5 /* data_AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data_AppDelegate.h; sourceTree = ""; }; 33 | 0031C4620E25C312001217B5 /* data_AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = data_AppDelegate.m; sourceTree = ""; }; 34 | 0085C0300E265EAE00C14E74 /* Runner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Runner.h; sourceTree = ""; }; 35 | 0085C0310E265EAE00C14E74 /* Runner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Runner.m; sourceTree = ""; }; 36 | 00FE56330E26559F008DD4BA /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = /System/Library/Frameworks/CoreServices.framework; sourceTree = ""; }; 37 | 00FE56380E2655AF008DD4BA /* AsyncSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AsyncSocket.h; sourceTree = ""; }; 38 | 00FE56390E2655AF008DD4BA /* AsyncSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AsyncSocket.m; sourceTree = ""; }; 39 | 00FE564F0E265CA8008DD4BA /* EchoServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EchoServer.m; sourceTree = ""; }; 40 | 00FE56510E265CEB008DD4BA /* EchoServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EchoServer.h; sourceTree = ""; }; 41 | 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 42 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 43 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; 44 | 1DDD58150DA1D0A300B32029 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; }; 45 | 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 46 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 47 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 48 | 32CA4F630368D1EE00C91783 /* mcinsight_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mcinsight_Prefix.pch; sourceTree = ""; }; 49 | 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 8D1107320486CEB800E47090 /* mcinsight.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = mcinsight.app; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | D52C5E180FF840A100F25DA7 /* MemcacheSnapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemcacheSnapshot.h; sourceTree = ""; }; 52 | D52C5E190FF840A100F25DA7 /* MemcacheSnapshot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MemcacheSnapshot.m; sourceTree = ""; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 8D11072E0486CEB800E47090 /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 61 | 00FE56340E26559F008DD4BA /* CoreServices.framework in Frameworks */, 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXFrameworksBuildPhase section */ 66 | 67 | /* Begin PBXGroup section */ 68 | 080E96DDFE201D6D7F000001 /* Classes */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 00FE56510E265CEB008DD4BA /* EchoServer.h */, 72 | 00FE564F0E265CA8008DD4BA /* EchoServer.m */, 73 | 00FE56380E2655AF008DD4BA /* AsyncSocket.h */, 74 | 00FE56390E2655AF008DD4BA /* AsyncSocket.m */, 75 | 0031C4610E25C312001217B5 /* data_AppDelegate.h */, 76 | 0031C4620E25C312001217B5 /* data_AppDelegate.m */, 77 | 0085C0300E265EAE00C14E74 /* Runner.h */, 78 | 0085C0310E265EAE00C14E74 /* Runner.m */, 79 | 001020A90E27C111006BD8F1 /* ValueInfo.h */, 80 | 001020AA0E27C111006BD8F1 /* ValueInfo.m */, 81 | 000BB3A20E2A986B009BBF2D /* LogDelegate.h */, 82 | 000BB3A30E2A986B009BBF2D /* LogDelegate.m */, 83 | 000BB3A90E2A9B3C009BBF2D /* LogInfo.h */, 84 | 000BB3AA0E2A9B3C009BBF2D /* LogInfo.m */, 85 | D52C5E180FF840A100F25DA7 /* MemcacheSnapshot.h */, 86 | D52C5E190FF840A100F25DA7 /* MemcacheSnapshot.m */, 87 | ); 88 | name = Classes; 89 | sourceTree = ""; 90 | }; 91 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, 95 | ); 96 | name = "Linked Frameworks"; 97 | sourceTree = ""; 98 | }; 99 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 00FE56330E26559F008DD4BA /* CoreServices.framework */, 103 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */, 104 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */, 105 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */, 106 | ); 107 | name = "Other Frameworks"; 108 | sourceTree = ""; 109 | }; 110 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 8D1107320486CEB800E47090 /* mcinsight.app */, 114 | ); 115 | name = Products; 116 | sourceTree = ""; 117 | }; 118 | 29B97314FDCFA39411CA2CEA /* mcinsight */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 080E96DDFE201D6D7F000001 /* Classes */, 122 | 29B97315FDCFA39411CA2CEA /* Other Sources */, 123 | 29B97317FDCFA39411CA2CEA /* Resources */, 124 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 125 | 19C28FACFE9D520D11CA2CBB /* Products */, 126 | ); 127 | name = mcinsight; 128 | sourceTree = ""; 129 | }; 130 | 29B97315FDCFA39411CA2CEA /* Other Sources */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 32CA4F630368D1EE00C91783 /* mcinsight_Prefix.pch */, 134 | 29B97316FDCFA39411CA2CEA /* main.m */, 135 | ); 136 | name = "Other Sources"; 137 | sourceTree = ""; 138 | }; 139 | 29B97317FDCFA39411CA2CEA /* Resources */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 8D1107310486CEB800E47090 /* Info.plist */, 143 | 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, 144 | 1DDD58140DA1D0A300B32029 /* MainMenu.xib */, 145 | ); 146 | name = Resources; 147 | sourceTree = ""; 148 | }; 149 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, 153 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, 154 | ); 155 | name = Frameworks; 156 | sourceTree = ""; 157 | }; 158 | /* End PBXGroup section */ 159 | 160 | /* Begin PBXNativeTarget section */ 161 | 8D1107260486CEB800E47090 /* mcinsight */ = { 162 | isa = PBXNativeTarget; 163 | buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "mcinsight" */; 164 | buildPhases = ( 165 | 8D1107290486CEB800E47090 /* Resources */, 166 | 8D11072C0486CEB800E47090 /* Sources */, 167 | 8D11072E0486CEB800E47090 /* Frameworks */, 168 | ); 169 | buildRules = ( 170 | ); 171 | dependencies = ( 172 | ); 173 | name = mcinsight; 174 | productInstallPath = "$(HOME)/Applications"; 175 | productName = mcinsight; 176 | productReference = 8D1107320486CEB800E47090 /* mcinsight.app */; 177 | productType = "com.apple.product-type.application"; 178 | }; 179 | /* End PBXNativeTarget section */ 180 | 181 | /* Begin PBXProject section */ 182 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 183 | isa = PBXProject; 184 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "mcinsight" */; 185 | compatibilityVersion = "Xcode 3.1"; 186 | hasScannedForEncodings = 1; 187 | mainGroup = 29B97314FDCFA39411CA2CEA /* mcinsight */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 8D1107260486CEB800E47090 /* mcinsight */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 8D1107290486CEB800E47090 /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, 202 | 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */, 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | }; 206 | /* End PBXResourcesBuildPhase section */ 207 | 208 | /* Begin PBXSourcesBuildPhase section */ 209 | 8D11072C0486CEB800E47090 /* Sources */ = { 210 | isa = PBXSourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | 8D11072D0486CEB800E47090 /* main.m in Sources */, 214 | 0031C4630E25C312001217B5 /* data_AppDelegate.m in Sources */, 215 | 00FE563A0E2655AF008DD4BA /* AsyncSocket.m in Sources */, 216 | 00FE56500E265CA8008DD4BA /* EchoServer.m in Sources */, 217 | 0085C0320E265EAE00C14E74 /* Runner.m in Sources */, 218 | 001020AB0E27C111006BD8F1 /* ValueInfo.m in Sources */, 219 | 000BB3A40E2A986B009BBF2D /* LogDelegate.m in Sources */, 220 | 000BB3AB0E2A9B3C009BBF2D /* LogInfo.m in Sources */, 221 | D52C5E1A0FF840A100F25DA7 /* MemcacheSnapshot.m in Sources */, 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | /* End PBXSourcesBuildPhase section */ 226 | 227 | /* Begin PBXVariantGroup section */ 228 | 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { 229 | isa = PBXVariantGroup; 230 | children = ( 231 | 089C165DFE840E0CC02AAC07 /* English */, 232 | ); 233 | name = InfoPlist.strings; 234 | sourceTree = ""; 235 | }; 236 | 1DDD58140DA1D0A300B32029 /* MainMenu.xib */ = { 237 | isa = PBXVariantGroup; 238 | children = ( 239 | 1DDD58150DA1D0A300B32029 /* English */, 240 | ); 241 | name = MainMenu.xib; 242 | sourceTree = ""; 243 | }; 244 | /* End PBXVariantGroup section */ 245 | 246 | /* Begin XCBuildConfiguration section */ 247 | C01FCF4B08A954540054247B /* Debug */ = { 248 | isa = XCBuildConfiguration; 249 | buildSettings = { 250 | ALWAYS_SEARCH_USER_PATHS = NO; 251 | COPY_PHASE_STRIP = NO; 252 | GCC_DYNAMIC_NO_PIC = NO; 253 | GCC_ENABLE_FIX_AND_CONTINUE = YES; 254 | GCC_MODEL_TUNING = G5; 255 | GCC_OPTIMIZATION_LEVEL = 0; 256 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 257 | GCC_PREFIX_HEADER = mcinsight_Prefix.pch; 258 | INFOPLIST_FILE = Info.plist; 259 | INSTALL_PATH = "$(HOME)/Applications"; 260 | PRODUCT_NAME = mcinsight; 261 | }; 262 | name = Debug; 263 | }; 264 | C01FCF4C08A954540054247B /* Release */ = { 265 | isa = XCBuildConfiguration; 266 | buildSettings = { 267 | ALWAYS_SEARCH_USER_PATHS = NO; 268 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 269 | GCC_MODEL_TUNING = G5; 270 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 271 | GCC_PREFIX_HEADER = mcinsight_Prefix.pch; 272 | INFOPLIST_FILE = Info.plist; 273 | INSTALL_PATH = "$(HOME)/Applications"; 274 | PRODUCT_NAME = mcinsight; 275 | }; 276 | name = Release; 277 | }; 278 | C01FCF4F08A954540054247B /* Debug */ = { 279 | isa = XCBuildConfiguration; 280 | buildSettings = { 281 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 282 | GCC_C_LANGUAGE_STANDARD = c99; 283 | GCC_ENABLE_OBJC_GC = unsupported; 284 | GCC_OPTIMIZATION_LEVEL = 0; 285 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 286 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 287 | GCC_WARN_UNUSED_VARIABLE = YES; 288 | ONLY_ACTIVE_ARCH = YES; 289 | PREBINDING = NO; 290 | SDKROOT = macosx10.5; 291 | }; 292 | name = Debug; 293 | }; 294 | C01FCF5008A954540054247B /* Release */ = { 295 | isa = XCBuildConfiguration; 296 | buildSettings = { 297 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 298 | GCC_C_LANGUAGE_STANDARD = c99; 299 | GCC_ENABLE_OBJC_GC = unsupported; 300 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 301 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 302 | GCC_WARN_UNUSED_VARIABLE = YES; 303 | PREBINDING = NO; 304 | SDKROOT = macosx10.5; 305 | }; 306 | name = Release; 307 | }; 308 | /* End XCBuildConfiguration section */ 309 | 310 | /* Begin XCConfigurationList section */ 311 | C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "mcinsight" */ = { 312 | isa = XCConfigurationList; 313 | buildConfigurations = ( 314 | C01FCF4B08A954540054247B /* Debug */, 315 | C01FCF4C08A954540054247B /* Release */, 316 | ); 317 | defaultConfigurationIsVisible = 0; 318 | defaultConfigurationName = Release; 319 | }; 320 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "mcinsight" */ = { 321 | isa = XCConfigurationList; 322 | buildConfigurations = ( 323 | C01FCF4F08A954540054247B /* Debug */, 324 | C01FCF5008A954540054247B /* Release */, 325 | ); 326 | defaultConfigurationIsVisible = 0; 327 | defaultConfigurationName = Release; 328 | }; 329 | /* End XCConfigurationList section */ 330 | }; 331 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 332 | } 333 | -------------------------------------------------------------------------------- /protocol.txt: -------------------------------------------------------------------------------- 1 | Protocol 2 | -------- 3 | 4 | Clients of memcached communicate with server through TCP connections. 5 | (A UDP interface is also available; details are below under "UDP 6 | protocol.") A given running memcached server listens on some 7 | (configurable) port; clients connect to that port, send commands to 8 | the server, read responses, and eventually close the connection. 9 | 10 | There is no need to send any command to end the session. A client may 11 | just close the connection at any moment it no longer needs it. Note, 12 | however, that clients are encouraged to cache their connections rather 13 | than reopen them every time they need to store or retrieve data. This 14 | is because memcached is especially designed to work very efficiently 15 | with a very large number (many hundreds, more than a thousand if 16 | necessary) of open connections. Caching connections will eliminate the 17 | overhead associated with establishing a TCP connection (the overhead 18 | of preparing for a new connection on the server side is insignificant 19 | compared to this). 20 | 21 | There are two kinds of data sent in the memcache protocol: text lines 22 | and unstructured data. Text lines are used for commands from clients 23 | and responses from servers. Unstructured data is sent when a client 24 | wants to store or retrieve data. The server will transmit back 25 | unstructured data in exactly the same way it received it, as a byte 26 | stream. The server doesn't care about byte order issues in 27 | unstructured data and isn't aware of them. There are no limitations on 28 | characters that may appear in unstructured data; however, the reader 29 | of such data (either a client or a server) will always know, from a 30 | preceding text line, the exact length of the data block being 31 | transmitted. 32 | 33 | Text lines are always terminated by \r\n. Unstructured data is _also_ 34 | terminated by \r\n, even though \r, \n or any other 8-bit characters 35 | may also appear inside the data. Therefore, when a client retrieves 36 | data from a server, it must use the length of the data block (which it 37 | will be provided with) to determine where the data block ends, and not 38 | the fact that \r\n follows the end of the data block, even though it 39 | does. 40 | 41 | Keys 42 | ---- 43 | 44 | Data stored by memcached is identified with the help of a key. A key 45 | is a text string which should uniquely identify the data for clients 46 | that are interested in storing and retrieving it. Currently the 47 | length limit of a key is set at 250 characters (of course, normally 48 | clients wouldn't need to use such long keys); the key must not include 49 | control characters or whitespace. 50 | 51 | Commands 52 | -------- 53 | 54 | There are three types of commands. 55 | 56 | Storage commands (there are six: "set", "add", "replace", "append" 57 | "prepend" and "cas") ask the server to store some data identified by a key. The 58 | client sends a command line, and then a data block; after that the 59 | client expects one line of response, which will indicate success or 60 | faulure. 61 | 62 | Retrieval commands (there are two: "get" and "gets") ask the server to 63 | retrieve data corresponding to a set of keys (one or more keys in one 64 | request). The client sends a command line, which includes all the 65 | requested keys; after that for each item the server finds it sends to 66 | the client one response line with information about the item, and one 67 | data block with the item's data; this continues until the server 68 | finished with the "END" response line. 69 | 70 | All other commands don't involve unstructured data. In all of them, 71 | the client sends one command line, and expects (depending on the 72 | command) either one line of response, or several lines of response 73 | ending with "END" on the last line. 74 | 75 | A command line always starts with the name of the command, followed by 76 | parameters (if any) delimited by whitespace. Command names are 77 | lower-case and are case-sensitive. 78 | 79 | Expiration times 80 | ---------------- 81 | 82 | Some commands involve a client sending some kind of expiration time 83 | (relative to an item or to an operation requested by the client) to 84 | the server. In all such cases, the actual value sent may either be 85 | Unix time (number of seconds since January 1, 1970, as a 32-bit 86 | value), or a number of seconds starting from current time. In the 87 | latter case, this number of seconds may not exceed 60*60*24*30 (number 88 | of seconds in 30 days); if the number sent by a client is larger than 89 | that, the server will consider it to be real Unix time value rather 90 | than an offset from current time. 91 | 92 | 93 | Error strings 94 | ------------- 95 | 96 | Each command sent by a client may be answered with an error string 97 | from the server. These error strings come in three types: 98 | 99 | - "ERROR\r\n" 100 | 101 | means the client sent a nonexistent command name. 102 | 103 | - "CLIENT_ERROR \r\n" 104 | 105 | means some sort of client error in the input line, i.e. the input 106 | doesn't conform to the protocol in some way. is a 107 | human-readable error string. 108 | 109 | - "SERVER_ERROR \r\n" 110 | 111 | means some sort of server error prevents the server from carrying 112 | out the command. is a human-readable error string. In cases 113 | of severe server errors, which make it impossible to continue 114 | serving the client (this shouldn't normally happen), the server will 115 | close the connection after sending the error line. This is the only 116 | case in which the server closes a connection to a client. 117 | 118 | 119 | In the descriptions of individual commands below, these error lines 120 | are not again specifically mentioned, but clients must allow for their 121 | possibility. 122 | 123 | 124 | Storage commands 125 | ---------------- 126 | 127 | First, the client sends a command line which looks like this: 128 | 129 | [noreply]\r\n 130 | cas [noreply]\r\n 131 | 132 | - is "set", "add", "replace", "append" or "prepend" 133 | 134 | "set" means "store this data". 135 | 136 | "add" means "store this data, but only if the server *doesn't* already 137 | hold data for this key". 138 | 139 | "replace" means "store this data, but only if the server *does* 140 | already hold data for this key". 141 | 142 | "append" means "add this data to an existing key after existing data". 143 | 144 | "prepend" means "add this data to an existing key before existing data". 145 | 146 | The append and prepend commands do not accept flags or exptime. 147 | They update existing data portions, and ignore new flag and exptime 148 | settings. 149 | 150 | "cas" is a check and set operation which means "store this data but 151 | only if no one else has updated since I last fetched it." 152 | 153 | - is the key under which the client asks to store the data 154 | 155 | - is an arbitrary 16-bit unsigned integer (written out in 156 | decimal) that the server stores along with the data and sends back 157 | when the item is retrieved. Clients may use this as a bit field to 158 | store data-specific information; this field is opaque to the server. 159 | Note that in memcached 1.2.1 and higher, flags may be 32-bits, instead 160 | of 16, but you might want to restrict yourself to 16 bits for 161 | compatibility with older versions. 162 | 163 | - is expiration time. If it's 0, the item never expires 164 | (although it may be deleted from the cache to make place for other 165 | items). If it's non-zero (either Unix time or offset in seconds from 166 | current time), it is guaranteed that clients will not be able to 167 | retrieve this item after the expiration time arrives (measured by 168 | server time). 169 | 170 | - is the number of bytes in the data block to follow, *not* 171 | including the delimiting \r\n. may be zero (in which case 172 | it's followed by an empty data block). 173 | 174 | - is a unique 64-bit value of an existing entry. 175 | Clients should use the value returned from the "gets" command 176 | when issuing "cas" updates. 177 | 178 | - "noreply" optional parameter instructs the server to not send the 179 | reply. NOTE: if the request line is malformed, the server can't 180 | parse "noreply" option reliably. In this case it may send the error 181 | to the client, and not reading it on the client side will break 182 | things. Client should construct only valid requests. 183 | 184 | After this line, the client sends the data block: 185 | 186 | \r\n 187 | 188 | - is a chunk of arbitrary 8-bit data of length 189 | from the previous line. 190 | 191 | After sending the command line and the data blockm the client awaits 192 | the reply, which may be: 193 | 194 | - "STORED\r\n", to indicate success. 195 | 196 | - "NOT_STORED\r\n" to indicate the data was not stored, but not 197 | because of an error. This normally means that either that the 198 | condition for an "add" or a "replace" command wasn't met, or that the 199 | item is in a delete queue (see the "delete" command below). 200 | 201 | - "EXISTS\r\n" to indicate that the item you are trying to store with 202 | a "cas" command has been modified since you last fetched it. 203 | 204 | - "NOT_FOUND\r\n" to indicate that the item you are trying to store 205 | with a "cas" command did not exist or has been deleted. 206 | 207 | 208 | Retrieval command: 209 | ------------------ 210 | 211 | The retrieval commands "get" and "gets" operates like this: 212 | 213 | get *\r\n 214 | gets *\r\n 215 | 216 | - * means one or more key strings separated by whitespace. 217 | 218 | After this command, the client expects zero or more items, each of 219 | which is received as a text line followed by a data block. After all 220 | the items have been transmitted, the server sends the string 221 | 222 | "END\r\n" 223 | 224 | to indicate the end of response. 225 | 226 | Each item sent by the server looks like this: 227 | 228 | VALUE []\r\n 229 | \r\n 230 | 231 | - is the key for the item being sent 232 | 233 | - is the flags value set by the storage command 234 | 235 | - is the length of the data block to follow, *not* including 236 | its delimiting \r\n 237 | 238 | - is a unique 64-bit integer that uniquely identifies 239 | this specific item. 240 | 241 | - is the data for this item. 242 | 243 | If some of the keys appearing in a retrieval request are not sent back 244 | by the server in the item list this means that the server does not 245 | hold items with such keys (because they were never stored, or stored 246 | but deleted to make space for more items, or expired, or explicitly 247 | deleted by a client). 248 | 249 | 250 | Deletion 251 | -------- 252 | 253 | The command "delete" allows for explicit deletion of items: 254 | 255 | delete [