2
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
@property (nonatomic, assign) NSInteger decodedPrefixBytes; // 已解码并写入 sseTextBuffer 的字节数(SSE)
|
||||
@property (nonatomic, assign) NSInteger deliveredCharCount; // 已回传的字符数(非 SSE,用于做增量)
|
||||
@property (nonatomic, assign) BOOL hasEmitted; // 是否已经输出过正文(用于“首段删 1 个 \t”)
|
||||
@property (nonatomic, assign) BOOL lastChunkEndedWithTab; // 上一个已输出分片是否以 "\t" 结尾(用于跨分片去除“\t 后空格”)
|
||||
@property (nonatomic, strong) NSMutableArray<NSString *> *pendingQueue; // 待回调的分片(节流输出)
|
||||
@property (nonatomic, strong) NSTimer *flushTimer; // 定时从队列取出一条回调
|
||||
@property (nonatomic, strong, nullable) NSError *finishError; // 结束时的错误(需要等队列清空再回调)
|
||||
@@ -57,7 +58,7 @@ static NSUInteger kb_validUTF8PrefixLen(NSData *data) {
|
||||
_buffer = [NSMutableData data];
|
||||
_sseTextBuffer = [NSMutableString string];
|
||||
_pendingQueue = [NSMutableArray array];
|
||||
_flushInterval = 0.10;
|
||||
_flushInterval = 0.1;
|
||||
_splitLargeDeltasOnWhitespace = YES;
|
||||
}
|
||||
return self;
|
||||
@@ -90,6 +91,7 @@ static NSUInteger kb_validUTF8PrefixLen(NSData *data) {
|
||||
self.decodedPrefixBytes = 0;
|
||||
self.deliveredCharCount = 0;
|
||||
self.hasEmitted = NO;
|
||||
self.lastChunkEndedWithTab = NO;
|
||||
[self.pendingQueue removeAllObjects];
|
||||
[self.flushTimer invalidate]; self.flushTimer = nil;
|
||||
self.finishError = nil;
|
||||
@@ -108,6 +110,7 @@ static NSUInteger kb_validUTF8PrefixLen(NSData *data) {
|
||||
self.decodedPrefixBytes = 0;
|
||||
self.deliveredCharCount = 0;
|
||||
self.hasEmitted = NO;
|
||||
self.lastChunkEndedWithTab = NO;
|
||||
[self.pendingQueue removeAllObjects];
|
||||
[self.flushTimer invalidate]; self.flushTimer = nil;
|
||||
self.finishError = nil;
|
||||
@@ -251,26 +254,37 @@ static NSUInteger kb_validUTF8PrefixLen(NSData *data) {
|
||||
- (void)emitChunk:(NSString *)rawText {
|
||||
if (rawText.length == 0) return;
|
||||
NSString *text = rawText;
|
||||
// 1) 统一处理 “/t” -> “\t”
|
||||
if (self.treatSlashTAsTab) {
|
||||
text = [text stringByReplacingOccurrencesOfString:@"/t" withString:@"\t"];
|
||||
}
|
||||
// 2) 仅在整体首段:去掉一个起始的 "\t",以及其后紧邻的一个空格(若存在)
|
||||
if (!self.hasEmitted && self.trimLeadingTabOnce) {
|
||||
// 跳过前导空白,只删除“一个”起始的 \t
|
||||
NSUInteger i = 0;
|
||||
while (i < text.length) {
|
||||
unichar c = [text characterAtIndex:i];
|
||||
if (c == ' ' || c == '\r' || c == '\n') { i++; continue; }
|
||||
break;
|
||||
}
|
||||
if (i < text.length && [text characterAtIndex:i] == '\t') {
|
||||
NSMutableString *m = [text mutableCopy];
|
||||
[m deleteCharactersInRange:NSMakeRange(i, 1)];
|
||||
text = m;
|
||||
if (text.length > 0 && [text characterAtIndex:0] == '\t') {
|
||||
NSUInteger start = 1;
|
||||
if (start < text.length && [text characterAtIndex:start] == ' ') start++;
|
||||
text = [text substringFromIndex:start];
|
||||
}
|
||||
}
|
||||
if (text.length == 0) return;
|
||||
// 3) 从第二段开始:去掉每个段首的一个空格(即 “\t ” -> “\t”),跨分片也处理
|
||||
if (text.length > 0) {
|
||||
// 跨分片:若上个分片以 \t 结尾,本分片起始的一个或多个空格去掉一个
|
||||
if (self.lastChunkEndedWithTab) {
|
||||
NSUInteger j = 0;
|
||||
while (j < text.length && [text characterAtIndex:j] == ' ') { j++; }
|
||||
if (j > 0) {
|
||||
text = [text substringFromIndex:1]; // 仅去一个空格
|
||||
}
|
||||
}
|
||||
// 同一分片内:将 “\t ” 规范化为 “\t”(仅去一个空格)
|
||||
text = [text stringByReplacingOccurrencesOfString:@"\t " withString:@"\t"];
|
||||
}
|
||||
if (text.length == 0) { self.lastChunkEndedWithTab = NO; return; }
|
||||
if (self.onChunk) dispatch_async(dispatch_get_main_queue(), ^{ self.onChunk(text); });
|
||||
self.hasEmitted = YES;
|
||||
// 记录末尾是否为分段分隔符 \t(用于跨分片处理)
|
||||
unichar lastc = [text characterAtIndex:text.length - 1];
|
||||
self.lastChunkEndedWithTab = (lastc == '\t');
|
||||
}
|
||||
|
||||
#pragma mark - Queue/Flush
|
||||
|
||||
Reference in New Issue
Block a user