blob: ae4b0e223b19a6c4637024ecdb7f68128b9aa2a1 [file] [log] [blame]
// Copyright (c) 2023, 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 "CUPHTTPStreamToNSInputStreamAdapter.h"
#import <Foundation/Foundation.h>
#include <os/log.h>
@implementation CUPHTTPStreamToNSInputStreamAdapter {
Dart_Port _sendPort;
NSCondition* _dataCondition;
NSMutableData * _data;
NSStreamStatus _status;
BOOL _done;
NSError* _error;
id<NSStreamDelegate> _delegate; // This is a weak reference.
}
- (instancetype)initWithPort:(Dart_Port)sendPort {
self = [super init];
if (self != nil) {
_sendPort = sendPort;
_dataCondition = [[NSCondition alloc] init];
_data = [[NSMutableData alloc] init];
_done = NO;
_status = NSStreamStatusNotOpen;
_error = nil;
_delegate = self;
}
return self;
}
- (void)dealloc {
[_dataCondition release];
[_data release];
[_error release];
[super dealloc];
}
- (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 release];
_error = [error retain];
_status = NSStreamStatusError;
[_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;
[_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 {
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 {
os_log_with_type(OS_LOG_DEFAULT,
OS_LOG_TYPE_DEBUG,
"CUPHTTPStreamToNSInputStreamAdapter: read len=%tu", len);
[_dataCondition lock];
while ([_data length] == 0 && !_done && _error == nil) {
// There is no data to return so signal the Dart code that it should add more data through
// [self addData:].
Dart_CObject message_cobj;
message_cobj.type = Dart_CObject_kInt64;
message_cobj.value.as_int64 = len;
const bool success = Dart_PostCObject_DL(_sendPort, &message_cobj);
NSCAssert(success, @"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.
[_data replaceBytesInRange: readRange withBytes: NULL length: 0];
if (_done && [_data length] == 0) {
_status = NSStreamStatusAtEnd;
}
} else {
copySize = -1;
}
[_dataCondition unlock];
return copySize;
}
- (BOOL)getBuffer:(uint8_t**)buffer length:(NSUInteger*)len {
return NO;
}
- (BOOL)hasBytesAvailable {
return YES;
}
#pragma mark - NSStreamDelegate
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
id<NSStreamDelegate> delegate = _delegate;
if (delegate != self) {
os_log_with_type(OS_LOG_DEFAULT,
OS_LOG_TYPE_ERROR,
"CUPHTTPStreamToNSInputStreamAdapter: non-self delegate was invoked");
}
}
@end