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%p] mode=%@>",
113                     NSStringFromClass([self class]), 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
154int 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
182BOOL 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
234BOOL 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}
266