]> err.no Git - sope/blob - sope-core/NGStreams/NGStream.m
synced with latest additions and bumped framework versions
[sope] / sope-core / NGStreams / NGStream.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 <NGStreams/NGStreamExceptions.h>
23 #include <NGStreams/NGStream.h>
24 #include <NGStreams/NGFilterStream.h>
25 #include "common.h"
26
27 @implementation NGStream
28
29 /* primitives */
30
31 - (void)setLastException:(NSException *)_exception {
32   [_exception raise];
33 }
34 - (NSException *)lastException {
35   return nil;
36 }
37
38 - (unsigned)readBytes:(void *)_buf count:(unsigned)_len {
39   [self subclassResponsibility:_cmd];
40   return 0;
41 }
42 - (unsigned)writeBytes:(const void *)_buf count:(unsigned)_len {
43   [self subclassResponsibility:_cmd];
44   return 0;
45 }
46
47 - (BOOL)flush {
48   return YES;
49 }
50 - (BOOL)close {
51   return YES;
52 }
53
54 - (NGStreamMode)mode {
55   [self subclassResponsibility:_cmd];
56   return 0;
57 }
58 - (BOOL)isRootStream {
59   [self subclassResponsibility:_cmd];
60   return NO;
61 }
62
63 // methods method which write exactly _len bytes
64
65 - (BOOL)safeReadBytes:(void *)_buf count:(unsigned)_len {
66   return NGSafeReadBytesFromStream(self, _buf, _len);
67 }
68
69 - (BOOL)safeWriteBytes:(const void *)_buf count:(unsigned)_len {
70   return NGSafeWriteBytesToStream(self, _buf, _len);
71 }
72
73 /* marking */
74
75 - (BOOL)mark {
76   NSLog(@"WARNING: called mark on a stream which doesn't support marking !");
77   return NO;
78 }
79 - (BOOL)rewind {
80   [NGStreamException raiseWithStream:self reason:@"marking not supported"];
81   return NO;
82 }
83 - (BOOL)markSupported {
84   return NO;
85 }
86
87 /* convenience methods */
88
89 - (int)readByte {
90   return NGReadByteFromStream(self);
91 }
92
93 /* description */
94
95 - (NSString *)modeDescription {
96   NSString *result = @"unknown";
97   
98   switch ([self mode]) {
99     case NGStreamMode_undefined: result = @"undefined"; break;
100     case NGStreamMode_readOnly:  result = @"r";         break;
101     case NGStreamMode_writeOnly: result = @"w";         break;
102     case NGStreamMode_readWrite: result = @"rw";        break;
103     default:
104       [NGUnknownStreamModeException raiseWithStream:self];
105       break;
106   }
107   return result;
108 }
109
110 - (NSString *)description {
111   return [NSString stringWithFormat:
112                      @"<%@[0x%08X] mode=%@>",
113                      NSStringFromClass([self class]), (unsigned)self,
114                      [self modeDescription]];
115 }
116
117 @end /* NGStream */
118
119 @implementation NGStream(DataMethods)
120
121 - (NSData *)readDataOfLength:(unsigned int)_length {
122   unsigned readCount;
123   char buf[_length];
124   
125   if (_length == 0) return [NSData data];
126   
127   readCount = [self readBytes:buf count:_length];
128   if (readCount == NGStreamError)
129     return nil;
130   
131   return [NSData dataWithBytes:buf length:readCount];
132 }
133
134 - (NSData *)safeReadDataOfLength:(unsigned int)_length {
135   char buf[_length];
136   
137   if (_length == 0) return [NSData data];
138   if (![self safeReadBytes:buf count:_length])
139     return nil;
140   return [NSData dataWithBytes:buf length:_length];
141 }
142
143 - (unsigned int)writeData:(NSData *)_data {
144   return [self writeBytes:[_data bytes] count:[_data length]];
145 }
146 - (BOOL)safeWriteData:(NSData *)_data {
147   return [self safeWriteBytes:[_data bytes] count:[_data length]];
148 }
149
150 @end /* NGStream(DataMethods) */
151
152 // concrete implementations as functions
153
154 int NGReadByteFromStream(id<NGInputStream> _stream) {
155   volatile int  result = -1;
156   unsigned char c;
157
158   NS_DURING {
159     int l;
160     l = [_stream readBytes:&c count:sizeof(unsigned char)];
161     if (l == NGStreamError) {
162       NSException *e = [(id)_stream lastException];
163       if ([e isKindOfClass:[NGEndOfStreamException class]])
164         *(&result) = -1;
165       else
166         [e raise];
167     }
168     else
169       *(&result) = c;
170   }
171   NS_HANDLER {
172     if ([localException isKindOfClass:[NGEndOfStreamException class]])
173       *(&result) = -1;
174     else
175       [localException raise];
176   }
177   NS_ENDHANDLER;
178   
179   return result;
180 }
181
182 BOOL NGSafeReadBytesFromStream(id<NGInputStream> _in, void *_buf, unsigned _len){
183   volatile int toBeRead;
184   volatile int readResult;
185   volatile NGIOReadMethodType readBytes;
186
187   *(&toBeRead) = _len;
188   readBytes = (NGIOReadMethodType)
189     [(NSObject *)_in methodForSelector:@selector(readBytes:count:)];
190
191   NS_DURING {
192     void *pos = _buf;
193     
194     while (YES) {
195       *(&readResult) = (unsigned)readBytes(_in, @selector(readBytes:count:),
196                                            pos, toBeRead);
197
198       if (readResult == NGStreamError) {
199         /* TODO: improve exception handling ... */
200         [[(id)_in lastException] raise];
201       }
202       else if (readResult == toBeRead) {
203         // all bytes were read successfully, return
204         break;
205       }
206       
207       if (readResult < 1) {
208         [NSException raise:NSInternalInconsistencyException
209                      format:@"readBytes:count: returned a value < 1"];
210       }
211
212       toBeRead -= readResult;
213       pos += readResult;
214     }
215   }
216   NS_HANDLER {
217     if ([localException isKindOfClass:[NGEndOfStreamException class]]) {
218       [[[NGEndOfStreamException alloc]
219                                 initWithStream:(id)_in
220                                 readCount:(_len - toBeRead)
221                                 safeCount:_len
222                                 data:[NSData dataWithBytes:_buf
223                                              length:(_len - toBeRead)]]
224                                 raise];
225     }
226     else {
227       [localException raise];
228     }
229   }
230   NS_ENDHANDLER;
231   return YES;
232 }
233
234 BOOL NGSafeWriteBytesToStream(id<NGOutputStream> _o,const void *_b,unsigned _l) {
235   int  toBeWritten = _l;
236   int  writeResult;
237   void *pos = (void *)_b;
238   NGIOWriteMethodType writeBytes;
239   
240   writeBytes = (NGIOWriteMethodType)
241     [(NSObject *)_o methodForSelector:@selector(writeBytes:count:)];
242   
243   while (YES) {
244     writeResult =
245       (int)writeBytes(_o, @selector(writeBytes:count:), pos, toBeWritten);
246     
247     if (writeResult == NGStreamError) {
248       /* remember number of written bytes ??? */
249       return NO;
250     }
251     else if (writeResult == toBeWritten) {
252       // all bytes were written successfully, return
253       break;
254     }
255     
256     if (writeResult < 1) {
257       [NSException raise:NSInternalInconsistencyException
258                    format:@"writeBytes:count: returned a value<1 in %@", _o];
259     }
260
261     toBeWritten -= writeResult;
262     pos += writeResult;
263   }
264   return YES;
265 }