]> err.no Git - sope/blob - sope-core/NGStreams/NGByteBuffer.m
Drop apache 1 build-dependency
[sope] / sope-core / NGStreams / NGByteBuffer.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include "NGByteBuffer.h"
23 #include "common.h"
24 #include <sys/time.h>
25
26 typedef struct NGByteBufferLA {
27   unsigned char byte;
28   char          isEOF:1;
29   char          isFetched:1;
30 } LA_NGByteBuffer;
31
32 @implementation NGByteBuffer
33
34 static BOOL  ProfileByteBuffer = NO;
35 static Class DataStreamClass = Nil;
36
37 + (void)initialize {
38   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
39   static BOOL didInit = NO;
40   if (didInit) return;
41   didInit = YES;
42   
43   ProfileByteBuffer = [ud boolForKey:@"ProfileByteBufferEnabled"];
44   DataStreamClass   = NSClassFromString(@"NGDataStream");
45 }
46
47 + (int)version {
48   return [super version] + 1;
49 }
50
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];
55 }
56
57 - (id)initWithSource:(id<NGStream>)_source la:(unsigned)_la {
58   if (_source == nil) {
59     [self release];
60     return nil;
61   }
62   if (*(Class *)_source == DataStreamClass) {
63     [self release];
64     return [_source retain];
65   }
66   if ((self = [super initWithSource:_source])) {
67     unsigned size = 0;
68     
69     if (_la < 1) {
70       [NSException raise:NSRangeException
71                    format:@"lookahead depth is less than one (%d)", _la];
72     }
73
74     // Find first power of 2 >= to requested size
75     for (size = 2; size < _la; size *=2);
76     
77     self->la = malloc(sizeof(LA_NGByteBuffer) * size + 4);
78     memset(self->la, 0, sizeof(LA_NGByteBuffer) * size);
79
80     self->bufLen      = size;
81     self->sizeLessOne = self->bufLen - 1;
82     self->headIdx     = 0;
83     self->wasEOF      = NO;
84     if ([self->source respondsToSelector:@selector(methodForSelector:)]) {
85       self->readByte = (int(*)(id, SEL))
86         [(NSObject *)self->source methodForSelector:@selector(readByte)];
87     }
88     if ([self respondsToSelector:@selector(methodForSelector:)]) {
89       self->laFunction = (int(*)(id, SEL, unsigned))
90         [(NSObject *)self methodForSelector:@selector(la:)];
91     }
92   }
93   return self;
94 }
95
96 - (id)init {
97   [self release];
98   [self doesNotRecognizeSelector:_cmd];
99   return nil;
100 }
101
102 - (id)initWithSource:(id<NGStream>)_source {
103   [self release];
104   [self doesNotRecognizeSelector:_cmd];
105   return nil;
106 }
107
108 - (id)initWithInputSource:(id<NGInputStream>)_source {
109   [self release];
110   [self doesNotRecognizeSelector:_cmd];
111   return nil;
112 }
113
114 - (id)initWithOutputSource:(id<NGOutputStream>)_source {
115   [self release];
116   [self doesNotRecognizeSelector:_cmd];
117   return nil;
118 }
119
120 - (void)dealloc {
121   if (self->la) free(self->la);
122   [super dealloc];
123 }
124
125 /* operations */
126
127 - (int)readByte {
128   int byte = (self->laFunction == NULL)
129     ? [self la:0]
130     : self->laFunction(self, @selector(la:), 0);
131   [self consume];
132   return byte;
133 }
134
135 - (unsigned)readBytes:(void *)_buf count:(unsigned)_len {
136   if (_len == 0)
137     return 0;
138
139   if (!(self->la[(self->headIdx & self->sizeLessOne)].isFetched)) {
140     int byte = [self readByte];
141
142     if (byte == -1)
143       [NGEndOfStreamException raiseWithStream:self->source];
144
145     ((char *)_buf)[0] = byte;
146     return 1;
147   }
148   else {
149     unsigned      cnt    = 0;
150     int           idxCnt = self->headIdx & sizeLessOne;
151     unsigned char buffer[self->bufLen];
152     
153     while (self->la[idxCnt].isFetched && cnt < _len && cnt < bufLen) {
154       buffer[cnt] = self->la[idxCnt].byte;
155       cnt++;
156       idxCnt = (cnt + self->headIdx) & sizeLessOne;
157     }
158     memcpy(_buf, buffer, cnt);
159     [self consume:cnt];
160     return cnt;
161   }
162   return 0;
163 }
164
165 - (int)la:(unsigned)_la {
166   // TODO: huge method, should be split up
167   volatile unsigned result, idx;
168   unsigned i = 0;
169   
170   result = -1;
171   *(&idx) = (_la + self->headIdx) & self->sizeLessOne;
172   
173   if (_la > self->sizeLessOne) {
174     [NSException raise:NSRangeException
175                  format:@"tried to look ahead too far (la=%d, max=%d)", 
176                   _la, self->bufLen];
177   }
178   
179   if (self->wasEOF) {
180     result = (!self->la[idx].isFetched || self->la[idx].isEOF)
181       ? -1 : self->la[idx].byte;
182     return result;
183   }
184   
185   if (self->la[idx].isFetched) {
186     result = (self->la[idx].isEOF) ? -1 : self->la[idx].byte;
187     return result;
188   }
189
190   *(&i) = 0;
191   for (i = 0;
192        i < _la &&
193          self->la[(self->headIdx + i) & self->sizeLessOne].isFetched;
194        i++);
195   
196   /* 
197      If we should read more than 5 bytes, we take the time costs of an
198      exception handler 
199   */
200   if ((_la - i + 1) <= 5) {
201     while (i <= _la) {
202 #if DEBUG
203       struct timeval tv;
204       double         ti = 0.0;
205 #endif
206           
207       int byte = 0;
208
209 #if DEBUG
210       if (ProfileByteBuffer) {
211         gettimeofday(&tv, NULL);
212         ti =  (double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0);
213       }
214 #endif
215       byte = (self->readByte == NULL)
216         ? [self->source readByte]
217         : (int)self->readByte(self->source, @selector(readByte));
218
219 #if DEBUG
220       if (ProfileByteBuffer) {
221         gettimeofday(&tv, NULL);
222         ti = (double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0) - ti;
223         if (ti > 0.01) {
224           fprintf(stderr, "[%s] <read bytes from stream> : time "
225                   "needed: %4.4fs\n",
226                   __PRETTY_FUNCTION__, ti < 0.0 ? -1.0 : ti);
227         }
228       }
229 #endif
230     
231       if (byte == -1) {  // EOF was reached
232         self->wasEOF = YES;
233         break;
234       }
235       else {
236         int ix = (self->headIdx + i) & self->sizeLessOne;
237         self->la[ix].byte      = byte;
238         self->la[ix].isFetched = 1;
239       }
240       i++;
241     }
242   }
243   else {
244     BOOL readStream = YES;
245     NSException *exc = nil;
246     
247     while (readStream) {
248       int  cntReadBytes  = 0;
249       int  cnt           = 0;
250       int  desiredBytes  = _la - i+1;
251       char *tmpBuffer;
252
253       // TODO: check whether malloc is used for sufficiently large blocks!
254       tmpBuffer = malloc(desiredBytes + 2);
255
256       cntReadBytes = (self->readBytes == NULL)
257         ? [self->source readBytes:tmpBuffer count:desiredBytes]
258         : self->readBytes(self->source, @selector(readBytes:count:),
259                           tmpBuffer, desiredBytes);
260           
261       if (cntReadBytes == NGStreamError) {
262         exc = [[self->source lastException] retain];
263         break;
264       }
265       else {
266         if (cntReadBytes == desiredBytes)
267           readStream = NO;
268
269         cnt = 0;
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;
274           i++;
275           cnt++;
276           cntReadBytes--;
277         }
278       }
279           
280       if (tmpBuffer) free(tmpBuffer);
281     }
282     if (exc) {
283       if (![exc isKindOfClass:[NGEndOfStreamException class]]) {
284         [self setLastException:exc];
285         return NGStreamError;
286       }
287       self->wasEOF = YES;
288     }
289   }
290   
291   if (self->wasEOF) {
292     while (i <= _la) {
293       self->la[(self->headIdx + i) & self->sizeLessOne].isEOF = YES;
294       i++;
295     }
296   }
297   
298   result = (self->la[idx].isEOF) ? -1 : self->la[idx].byte;
299   return result;
300 }
301
302 - (void)consume {
303   int idx = self->headIdx & sizeLessOne;
304   
305   if (!(self->la[idx].isFetched)) {
306     (self->laFunction == NULL)
307       ? [self la:0]
308       : self->laFunction(self, @selector(la:), 0);
309   }
310   self->la[idx].isFetched = 0;
311   self->headIdx++;
312 }
313
314 - (void)consume:(unsigned)_cnt {
315   while (_cnt > 0) {
316     int idx = self->headIdx & sizeLessOne;
317     
318     if (!(self->la[idx].isFetched))
319       (self->laFunction == NULL)
320         ? [self la:0]
321         : self->laFunction(self, @selector(la:), 0);
322
323     self->la[idx].isFetched = 0;
324     self->headIdx++;
325     _cnt--;
326   }
327 }
328
329 /* description */
330
331 - (NSString *)description {
332   NSMutableString *ms;
333   
334   ms = [NSMutableString stringWithCapacity:128];
335
336   [ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
337   
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:@">"];
342   return ms;
343 }
344
345 @end /* NGByteBuffer */