2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "NGByteBuffer.h"
26 typedef struct NGByteBufferLA {
32 @implementation NGByteBuffer
34 static BOOL ProfileByteBuffer = NO;
35 static Class DataStreamClass = Nil;
38 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
39 static BOOL didInit = NO;
43 ProfileByteBuffer = [ud boolForKey:@"ProfileByteBufferEnabled"];
44 DataStreamClass = NSClassFromString(@"NGDataStream");
48 return [super version] + 1;
51 + (id)byteBufferWithSource:(id<NGStream>)_source la:(unsigned)_la {
52 if (_source == nil) return nil;
53 if (*(Class *)_source == DataStreamClass) return _source;
54 return [[[self alloc] initWithSource:_source la:_la] autorelease];
57 - (id)initWithSource:(id<NGStream>)_source la:(unsigned)_la {
62 if (*(Class *)_source == DataStreamClass) {
64 return [_source retain];
66 if ((self = [super initWithSource:_source])) {
70 [NSException raise:NSRangeException
71 format:@"lookahead depth is less than one (%d)", _la];
74 // Find first power of 2 >= to requested size
75 for (size = 2; size < _la; size *=2);
77 self->la = malloc(sizeof(LA_NGByteBuffer) * size + 4);
78 memset(self->la, 0, sizeof(LA_NGByteBuffer) * size);
81 self->sizeLessOne = self->bufLen - 1;
84 if ([self->source respondsToSelector:@selector(methodForSelector:)]) {
85 self->readByte = (int(*)(id, SEL))
86 [(NSObject *)self->source methodForSelector:@selector(readByte)];
88 if ([self respondsToSelector:@selector(methodForSelector:)]) {
89 self->laFunction = (int(*)(id, SEL, unsigned))
90 [(NSObject *)self methodForSelector:@selector(la:)];
98 [self doesNotRecognizeSelector:_cmd];
102 - (id)initWithSource:(id<NGStream>)_source {
104 [self doesNotRecognizeSelector:_cmd];
108 - (id)initWithInputSource:(id<NGInputStream>)_source {
110 [self doesNotRecognizeSelector:_cmd];
114 - (id)initWithOutputSource:(id<NGOutputStream>)_source {
116 [self doesNotRecognizeSelector:_cmd];
121 if (self->la) free(self->la);
128 int byte = (self->laFunction == NULL)
130 : self->laFunction(self, @selector(la:), 0);
135 - (unsigned)readBytes:(void *)_buf count:(unsigned)_len {
139 if (!(self->la[(self->headIdx & self->sizeLessOne)].isFetched)) {
140 int byte = [self readByte];
143 [NGEndOfStreamException raiseWithStream:self->source];
145 ((char *)_buf)[0] = byte;
150 int idxCnt = self->headIdx & sizeLessOne;
151 unsigned char buffer[self->bufLen];
153 while (self->la[idxCnt].isFetched && cnt < _len && cnt < bufLen) {
154 buffer[cnt] = self->la[idxCnt].byte;
156 idxCnt = (cnt + self->headIdx) & sizeLessOne;
158 memcpy(_buf, buffer, cnt);
165 - (int)la:(unsigned)_la {
166 // TODO: huge method, should be split up
167 volatile unsigned result, idx;
171 *(&idx) = (_la + self->headIdx) & self->sizeLessOne;
173 if (_la > self->sizeLessOne) {
174 [NSException raise:NSRangeException
175 format:@"tried to look ahead too far (la=%d, max=%d)",
180 result = (!self->la[idx].isFetched || self->la[idx].isEOF)
181 ? -1 : self->la[idx].byte;
185 if (self->la[idx].isFetched) {
186 result = (self->la[idx].isEOF) ? -1 : self->la[idx].byte;
193 self->la[(self->headIdx + i) & self->sizeLessOne].isFetched;
197 If we should read more than 5 bytes, we take the time costs of an
200 if ((_la - i + 1) <= 5) {
210 if (ProfileByteBuffer) {
211 gettimeofday(&tv, NULL);
212 ti = (double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0);
215 byte = (self->readByte == NULL)
216 ? [self->source readByte]
217 : (int)self->readByte(self->source, @selector(readByte));
220 if (ProfileByteBuffer) {
221 gettimeofday(&tv, NULL);
222 ti = (double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0) - ti;
224 fprintf(stderr, "[%s] <read bytes from stream> : time "
226 __PRETTY_FUNCTION__, ti < 0.0 ? -1.0 : ti);
231 if (byte == -1) { // EOF was reached
236 int ix = (self->headIdx + i) & self->sizeLessOne;
237 self->la[ix].byte = byte;
238 self->la[ix].isFetched = 1;
244 BOOL readStream = YES;
245 NSException *exc = nil;
248 int cntReadBytes = 0;
250 int desiredBytes = _la - i+1;
253 // TODO: check whether malloc is used for sufficiently large blocks!
254 tmpBuffer = malloc(desiredBytes + 2);
256 cntReadBytes = (self->readBytes == NULL)
257 ? [self->source readBytes:tmpBuffer count:desiredBytes]
258 : self->readBytes(self->source, @selector(readBytes:count:),
259 tmpBuffer, desiredBytes);
261 if (cntReadBytes == NGStreamError) {
262 exc = [[self->source lastException] retain];
266 if (cntReadBytes == desiredBytes)
270 while (cntReadBytes > 0) {
271 int ix = (self->headIdx + i) & self->sizeLessOne;
272 self->la[ix].byte = tmpBuffer[cnt];
273 self->la[ix].isFetched = 1;
280 if (tmpBuffer) free(tmpBuffer);
283 if (![exc isKindOfClass:[NGEndOfStreamException class]]) {
284 [self setLastException:exc];
285 return NGStreamError;
293 self->la[(self->headIdx + i) & self->sizeLessOne].isEOF = YES;
298 result = (self->la[idx].isEOF) ? -1 : self->la[idx].byte;
303 int idx = self->headIdx & sizeLessOne;
305 if (!(self->la[idx].isFetched)) {
306 (self->laFunction == NULL)
308 : self->laFunction(self, @selector(la:), 0);
310 self->la[idx].isFetched = 0;
314 - (void)consume:(unsigned)_cnt {
316 int idx = self->headIdx & sizeLessOne;
318 if (!(self->la[idx].isFetched))
319 (self->laFunction == NULL)
321 : self->laFunction(self, @selector(la:), 0);
323 self->la[idx].isFetched = 0;
331 - (NSString *)description {
334 ms = [NSMutableString stringWithCapacity:128];
336 [ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
338 if (self->source) [ms appendFormat:@" source=%@", self->source];
339 [ms appendFormat:@" mode=%@", [self modeDescription]];
340 [ms appendFormat:@" la=%d", self->bufLen];
341 [ms appendString:@">"];
345 @end /* NGByteBuffer */