blob: 21aedf0be37351c953c69f31c35615ec2d47b2bf [file] [log] [blame] [edit]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#import "input_stream_adapter.h"
#import <Foundation/Foundation.h>
#include <os/log.h>
@implementation DOBJCDartInputStreamAdapter {
Dart_Port _sendPort;
NSCondition *_dataCondition;
NSMutableData *_data;
NSStreamStatus _status;
BOOL _done;
NSError *_error;
id<NSStreamDelegate> __weak _delegate;
}
+ (instancetype)inputStreamWithPort:(Dart_Port)sendPort {
DOBJCDartInputStreamAdapter *stream = [[DOBJCDartInputStreamAdapter alloc] init];
if (stream != nil) {
stream->_sendPort = sendPort;
stream->_dataCondition = [[NSCondition alloc] init];
stream->_data = [[NSMutableData alloc] init];
stream->_done = NO;
stream->_status = NSStreamStatusNotOpen;
stream->_error = nil;
// From https://developer.apple.com/documentation/foundation/nsstream:
// "...by a default, a stream object must be its own delegate..."
stream->_delegate = stream;
}
return stream;
}
- (void)dealloc {
if (_status != NSStreamStatusClosed) {
Dart_PostInteger_DL(_sendPort, -1);
}
}
- (NSUInteger)addData:(NSData *)data {
[_dataCondition lock];
[_data appendData:data];
[_dataCondition broadcast];
[_dataCondition unlock];
return [_data length];
}
- (void)setDone {
[_dataCondition lock];
_done = YES;
[_dataCondition broadcast];
[_dataCondition unlock];
}
- (void)setError:(NSError *)error {
[_dataCondition lock];
_error = error;
[_dataCondition broadcast];
[_dataCondition unlock];
}
#pragma mark - NSStream
- (void)scheduleInRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode {
}
- (void)removeFromRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode {
}
- (void)open {
[_dataCondition lock];
_status = NSStreamStatusOpen;
[_dataCondition unlock];
}
- (void)close {
[_dataCondition lock];
_status = NSStreamStatusClosed;
if (!_done && _error == nil) {
__unused const bool success = Dart_PostInteger_DL(_sendPort, -1);
NSCAssert(success, @"DartInputStreamAdapter: Dart_PostCObject_DL failed.");
}
[_dataCondition unlock];
}
- (id)propertyForKey:(NSStreamPropertyKey)key {
return nil;
}
- (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key {
return NO;
}
- (id<NSStreamDelegate>)delegate {
return _delegate;
}
- (void)setDelegate:(id<NSStreamDelegate>)delegate {
// From https://developer.apple.com/documentation/foundation/nsstream:
// "...so a delegate message with an argument of nil should restore this
// delegate..."
if (delegate == nil) {
_delegate = self;
} else {
_delegate = delegate;
}
}
- (NSError *)streamError {
return _error;
}
- (NSStreamStatus)streamStatus {
return _status;
}
#pragma mark - NSInputStream
- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len {
if (_status == NSStreamStatusNotOpen) {
os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_DEBUG,
"DartInputStreamAdapter: read before open");
return -1;
}
[_dataCondition lock];
while (([_data length] == 0) && !_done && _error == nil) {
__unused const bool success = Dart_PostInteger_DL(_sendPort, len);
NSCAssert(success, @"DartInputStreamAdapter: Dart_PostCObject_DL failed.");
[_dataCondition wait];
}
NSInteger copySize;
if (_error == nil) {
copySize = MIN(len, [_data length]);
NSRange readRange = NSMakeRange(0, copySize);
[_data getBytes:(void *)buffer range:readRange];
// Shift the remaining data over to the beginning of the buffer.
// NOTE: this makes small reads expensive!
[_data replaceBytesInRange:readRange withBytes:NULL length:0];
if (_done && [_data length] == 0) {
_status = NSStreamStatusAtEnd;
}
} else {
_status = NSStreamStatusError;
copySize = -1;
}
[_dataCondition unlock];
return copySize;
}
- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len {
return NO;
}
- (BOOL)hasBytesAvailable {
return _status == NSStreamStatusOpen;
}
#pragma mark - NSStreamDelegate
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
id<NSStreamDelegate> delegate = _delegate;
if (delegate != self) {
[delegate stream:self handleEvent:streamEvent];
}
}
@end
@implementation DOBJCDartInputStreamAdapterWeakHolder
+ (instancetype)holderWithInputStreamAdapter:(DOBJCDartInputStreamAdapter *)adapter {
DOBJCDartInputStreamAdapterWeakHolder * holder = [[DOBJCDartInputStreamAdapterWeakHolder alloc] init];
if (holder != nil) {
holder->adapter = adapter;
}
return holder;
}
@synthesize adapter;
@end