187 lines
5.7 KiB
Objective-C
187 lines
5.7 KiB
Objective-C
/* Copyright 2014 Google Inc. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
|
#error "This file requires ARC support."
|
|
#endif
|
|
|
|
#import "GTMReadMonitorInputStream.h"
|
|
|
|
@implementation GTMReadMonitorInputStream {
|
|
NSInputStream *_inputStream; // Encapsulated stream that does the work.
|
|
|
|
NSThread *_thread; // Thread in which this object was created.
|
|
NSArray *_runLoopModes; // Modes for calling callbacks, when necessary.
|
|
}
|
|
|
|
@synthesize readDelegate = _readDelegate;
|
|
@synthesize readSelector = _readSelector;
|
|
@synthesize runLoopModes = _runLoopModes;
|
|
|
|
// We'll forward all unhandled messages to the NSInputStream class or to the encapsulated input
|
|
// stream. This is needed for all messages sent to NSInputStream which aren't handled by our
|
|
// superclass; that includes various private run loop calls.
|
|
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
|
|
return [NSInputStream methodSignatureForSelector:selector];
|
|
}
|
|
|
|
+ (void)forwardInvocation:(NSInvocation *)invocation {
|
|
[invocation invokeWithTarget:[NSInputStream class]];
|
|
}
|
|
|
|
- (BOOL)respondsToSelector:(SEL)selector {
|
|
return [_inputStream respondsToSelector:selector];
|
|
}
|
|
|
|
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
|
|
return [_inputStream methodSignatureForSelector:selector];
|
|
}
|
|
|
|
- (void)forwardInvocation:(NSInvocation *)invocation {
|
|
[invocation invokeWithTarget:_inputStream];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
+ (instancetype)inputStreamWithStream:(NSInputStream *)input {
|
|
return [[self alloc] initWithStream:input];
|
|
}
|
|
|
|
- (instancetype)initWithStream:(NSInputStream *)input {
|
|
self = [super init];
|
|
if (self) {
|
|
_inputStream = input;
|
|
_thread = [NSThread currentThread];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)init {
|
|
[self doesNotRecognizeSelector:_cmd];
|
|
return nil;
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len {
|
|
// Read from the encapsulated stream.
|
|
NSInteger numRead = [_inputStream read:buffer maxLength:len];
|
|
if (numRead > 0) {
|
|
if (_readDelegate && _readSelector) {
|
|
// Call the read selector with the buffer and number of bytes actually read into it.
|
|
BOOL isOnOriginalThread = [_thread isEqual:[NSThread currentThread]];
|
|
if (isOnOriginalThread) {
|
|
// Invoke immediately.
|
|
NSData *data = [NSData dataWithBytesNoCopy:buffer
|
|
length:(NSUInteger)numRead
|
|
freeWhenDone:NO];
|
|
[self invokeReadSelectorWithBuffer:data];
|
|
} else {
|
|
// Copy the buffer into an NSData to be retained by the performSelector,
|
|
// and invoke on the proper thread.
|
|
SEL sel = @selector(invokeReadSelectorWithBuffer:);
|
|
NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numRead];
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
|
if (_runLoopModes) {
|
|
[self performSelector:sel
|
|
onThread:_thread
|
|
withObject:data
|
|
waitUntilDone:NO
|
|
modes:_runLoopModes];
|
|
} else {
|
|
[self performSelector:sel onThread:_thread withObject:data waitUntilDone:NO];
|
|
}
|
|
#pragma clang diagnostic pop
|
|
}
|
|
}
|
|
}
|
|
return numRead;
|
|
}
|
|
|
|
- (void)invokeReadSelectorWithBuffer:(NSData *)data {
|
|
const void *buffer = data.bytes;
|
|
int64_t length = (int64_t)data.length;
|
|
|
|
id argSelf = self;
|
|
id readDelegate = _readDelegate;
|
|
if (readDelegate) {
|
|
NSMethodSignature *signature = [readDelegate methodSignatureForSelector:_readSelector];
|
|
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
|
[invocation setSelector:_readSelector];
|
|
[invocation setTarget:readDelegate];
|
|
[invocation setArgument:&argSelf atIndex:2];
|
|
[invocation setArgument:&buffer atIndex:3];
|
|
[invocation setArgument:&length atIndex:4];
|
|
[invocation invoke];
|
|
}
|
|
}
|
|
|
|
- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len {
|
|
return [_inputStream getBuffer:buffer length:len];
|
|
}
|
|
|
|
- (BOOL)hasBytesAvailable {
|
|
return [_inputStream hasBytesAvailable];
|
|
}
|
|
|
|
#pragma mark Standard messages
|
|
|
|
// Pass expected messages to our encapsulated stream.
|
|
//
|
|
// We want our encapsulated NSInputStream to handle the standard messages;
|
|
// we don't want the superclass to handle them.
|
|
- (void)open {
|
|
[_inputStream open];
|
|
}
|
|
|
|
- (void)close {
|
|
[_inputStream close];
|
|
}
|
|
|
|
- (id<NSStreamDelegate>)delegate {
|
|
return [_inputStream delegate];
|
|
}
|
|
|
|
- (void)setDelegate:(id<NSStreamDelegate>)delegate {
|
|
[_inputStream setDelegate:delegate];
|
|
}
|
|
|
|
- (id)propertyForKey:(NSString *)key {
|
|
return [_inputStream propertyForKey:key];
|
|
}
|
|
|
|
- (BOOL)setProperty:(id)property forKey:(NSString *)key {
|
|
return [_inputStream setProperty:property forKey:key];
|
|
}
|
|
|
|
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode {
|
|
[_inputStream scheduleInRunLoop:aRunLoop forMode:mode];
|
|
}
|
|
|
|
- (void)removeFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode {
|
|
[_inputStream removeFromRunLoop:aRunLoop forMode:mode];
|
|
}
|
|
|
|
- (NSStreamStatus)streamStatus {
|
|
return [_inputStream streamStatus];
|
|
}
|
|
|
|
- (NSError *)streamError {
|
|
return [_inputStream streamError];
|
|
}
|
|
|
|
@end
|