1/* 2 * 3 * Copyright 2015 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19#import "GRXImmediateWriter.h" 20 21#import "NSEnumerator+GRXUtil.h" 22 23@implementation GRXImmediateWriter { 24 NSEnumerator *_enumerator; 25 NSError *_errorOrNil; 26 id<GRXWriteable> _writeable; 27} 28 29@synthesize state = _state; 30 31- (instancetype)init { 32 return [self initWithEnumerator:nil error:nil]; // results in an empty writer. 33} 34 35// Designated initializer 36- (instancetype)initWithEnumerator:(NSEnumerator *)enumerator error:(NSError *)errorOrNil { 37 if (((self = [super init]))) { 38 _enumerator = enumerator; 39 _errorOrNil = errorOrNil; 40 _state = GRXWriterStateNotStarted; 41 } 42 return self; 43} 44 45#pragma mark Convenience constructors 46 47+ (instancetype)writerWithEnumerator:(NSEnumerator *)enumerator error:(NSError *)errorOrNil { 48 return [[self alloc] initWithEnumerator:enumerator error:errorOrNil]; 49} 50 51+ (GRXWriter *)writerWithEnumerator:(NSEnumerator *)enumerator { 52 return [self writerWithEnumerator:enumerator error:nil]; 53} 54 55+ (GRXWriter *)writerWithValueSupplier:(id (^)(void))block { 56 return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithValueSupplier:block]]; 57} 58 59+ (GRXWriter *)writerWithContainer:(id<NSFastEnumeration>)container { 60 return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithContainer:container]]; 61 ; 62} 63 64+ (GRXWriter *)writerWithValue:(id)value { 65 return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithSingleValue:value]]; 66} 67 68+ (GRXWriter *)writerWithError:(NSError *)error { 69 return [self writerWithEnumerator:nil error:error]; 70} 71 72+ (GRXWriter *)emptyWriter { 73 return [self writerWithEnumerator:nil error:nil]; 74} 75 76#pragma mark Conformance with GRXWriter 77 78// Most of the complexity in this implementation is the result of supporting pause and resumption of 79// the GRXWriter. It's an important feature for instances of GRXWriter that are backed by a 80// container (which may be huge), or by a NSEnumerator (which may even be infinite). 81 82- (void)writeUntilPausedOrStopped { 83 id value; 84 while (value = [_enumerator nextObject]) { 85 [_writeable writeValue:value]; 86 // If the writeable has a reference to us, it might change our state to paused or finished. 87 if (_state == GRXWriterStatePaused || _state == GRXWriterStateFinished) { 88 return; 89 } 90 } 91 [self finishWithError:_errorOrNil]; 92} 93 94- (void)startWithWriteable:(id<GRXWriteable>)writeable { 95 _state = GRXWriterStateStarted; 96 _writeable = writeable; 97 [self writeUntilPausedOrStopped]; 98} 99 100- (void)finishWithError:(NSError *)errorOrNil { 101 _state = GRXWriterStateFinished; 102 _enumerator = nil; 103 _errorOrNil = nil; 104 id<GRXWriteable> writeable = _writeable; 105 _writeable = nil; 106 [writeable writesFinishedWithError:errorOrNil]; 107} 108 109- (void)setState:(GRXWriterState)newState { 110 // Manual transitions are only allowed from the started or paused states. 111 if (_state == GRXWriterStateNotStarted || _state == GRXWriterStateFinished) { 112 return; 113 } 114 115 switch (newState) { 116 case GRXWriterStateFinished: 117 _state = newState; 118 _enumerator = nil; 119 _errorOrNil = nil; 120 // Per GRXWriter's contract, setting the state to Finished manually 121 // means one doesn't wish the writeable to be messaged anymore. 122 _writeable = nil; 123 return; 124 case GRXWriterStatePaused: 125 _state = newState; 126 return; 127 case GRXWriterStateStarted: 128 if (_state == GRXWriterStatePaused) { 129 _state = newState; 130 [self writeUntilPausedOrStopped]; 131 } 132 return; 133 case GRXWriterStateNotStarted: 134 return; 135 } 136} 137 138@end 139