00001 /******************************************************************************* 00002 00003 @file Conduit.d 00004 00005 Copyright (c) 2004 Kris Bell 00006 00007 This software is provided 'as-is', without any express or implied 00008 warranty. In no event will the authors be held liable for damages 00009 of any kind arising from the use of this software. 00010 00011 Permission is hereby granted to anyone to use this software for any 00012 purpose, including commercial applications, and to alter it and/or 00013 redistribute it freely, subject to the following restrictions: 00014 00015 1. The origin of this software must not be misrepresented; you must 00016 not claim that you wrote the original software. If you use this 00017 software in a product, an acknowledgment within documentation of 00018 said product would be appreciated but is not required. 00019 00020 2. Altered source versions must be plainly marked as such, and must 00021 not be misrepresented as being the original software. 00022 00023 3. This notice may not be removed or altered from any distribution 00024 of the source. 00025 00026 4. Derivative works are permitted, but they must carry this notice 00027 in full and credit the original source. 00028 00029 00030 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00031 00032 00033 @version Initial version, March 2004 00034 @author Kris 00035 00036 00037 *******************************************************************************/ 00038 00039 module mango.io.Conduit; 00040 00041 private import mango.io.Buffer, 00042 mango.io.Exception; 00043 00044 public import mango.io.model.IConduit; 00045 00046 /******************************************************************************* 00047 00048 Conduit abstract base-class, implementing interface IConduit. 00049 Only the conduit-specific read, write, and buffer-factory 00050 need to be implemented for a concrete conduit implementation. 00051 See FileConduit for an example. 00052 00053 Conduits provide virtualized access to external content, and 00054 represent things like files or Internet connections. Conduits 00055 are modelled by mango.io.model.IConduit, and implemented via 00056 classes FileConduit and SocketConduit. 00057 00058 Additional kinds of conduit are easy to construct: one either 00059 subclasses mango.io.Conduit, or implements mango.io.model.IConduit. 00060 A conduit typically reads and writes from/to an IBuffer in large 00061 chunks, typically the entire buffer. Alternatively, one can invoke 00062 read(dst[]) and/or write(src[]) directly. 00063 00064 *******************************************************************************/ 00065 00066 class Conduit : IConduit, IConduitFilter 00067 { 00068 private ConduitStyle.Bits style; 00069 private IConduitFilter filter; 00070 private bool seekable; 00071 00072 /*********************************************************************** 00073 00074 Return a preferred size for buffering conduit I/O 00075 00076 ***********************************************************************/ 00077 00078 abstract uint bufferSize (); 00079 00080 /*********************************************************************** 00081 00082 conduit-specific reader 00083 00084 ***********************************************************************/ 00085 00086 protected abstract uint reader (void[] dst); 00087 00088 /*********************************************************************** 00089 00090 conduit-specific writer 00091 00092 ***********************************************************************/ 00093 00094 protected abstract uint writer (void[] src); 00095 00096 00097 /*********************************************************************** 00098 00099 Construct a conduit with the given style and seek abilities. 00100 Conduits are either seekable or non-seekable. 00101 00102 ***********************************************************************/ 00103 00104 this (ConduitStyle.Bits style, bool seekable) 00105 { 00106 filter = this; 00107 this.style = style; 00108 this.seekable = seekable; 00109 } 00110 00111 /*********************************************************************** 00112 00113 Method to close the filters. This is invoked from the 00114 Resource base-class when the resource is being closed. 00115 You should ensure that a subclass invokes this as part 00116 of its closure mechanics. 00117 00118 ***********************************************************************/ 00119 00120 void close () 00121 { 00122 filter.unbind (); 00123 filter = this; 00124 } 00125 00126 /*********************************************************************** 00127 00128 flush provided content to the conduit 00129 00130 ***********************************************************************/ 00131 00132 bool flush (void[] src) 00133 { 00134 int len = src.length; 00135 00136 for (int i, written; written < len;) 00137 if ((i = write (src[written..len])) != Eof) 00138 written += i; 00139 else 00140 return false; 00141 return true; 00142 } 00143 00144 /*********************************************************************** 00145 00146 Please refer to IConduit.attach for details 00147 00148 ***********************************************************************/ 00149 00150 void attach (IConduitFilter filter) 00151 { 00152 filter.bind (this, this.filter); 00153 } 00154 00155 /*********************************************************************** 00156 00157 ***********************************************************************/ 00158 00159 protected void bind (IConduit conduit, IConduitFilter next) 00160 { 00161 } 00162 00163 /*********************************************************************** 00164 00165 ***********************************************************************/ 00166 00167 protected void unbind () 00168 { 00169 } 00170 00171 /*********************************************************************** 00172 00173 read from conduit into a target buffer 00174 00175 ***********************************************************************/ 00176 00177 uint read (void[] dst) 00178 { 00179 return filter.reader (dst); 00180 } 00181 00182 /*********************************************************************** 00183 00184 write to conduit from a source buffer 00185 00186 ***********************************************************************/ 00187 00188 uint write (void [] src) 00189 { 00190 return filter.writer (src); 00191 } 00192 00193 /*********************************************************************** 00194 00195 Returns true if this conduit is seekable (whether it 00196 implements ISeekable) 00197 00198 ***********************************************************************/ 00199 00200 bool isSeekable () 00201 { 00202 return seekable; 00203 } 00204 00205 /*********************************************************************** 00206 00207 Returns true is this conduit can be read from 00208 00209 ***********************************************************************/ 00210 00211 bool isReadable () 00212 { 00213 return cast(bool) ((style.access & ConduitStyle.Access.Read) != 0); 00214 } 00215 00216 /*********************************************************************** 00217 00218 Returns true if this conduit can be written to 00219 00220 ***********************************************************************/ 00221 00222 bool isWritable () 00223 { 00224 return cast(bool) ((style.access & ConduitStyle.Access.Write) != 0); 00225 } 00226 00227 /*********************************************************************** 00228 00229 Returns true if this conduit is text-based 00230 00231 ***********************************************************************/ 00232 00233 bool isTextual () 00234 { 00235 return cast(bool) ((style.access & ConduitStyle.Access.Text) != 0); 00236 } 00237 00238 /*********************************************************************** 00239 00240 Transfer the content of another conduit to this one. Returns 00241 a reference to this class, and throws IOException on failure. 00242 00243 ***********************************************************************/ 00244 00245 IConduit copy (IConduit source) 00246 { 00247 Buffer buffer = new Buffer (this); 00248 00249 while (buffer.fill (source) != Eof) 00250 if (buffer.drain () == Eof) 00251 throw new IOException ("Eof while copying conduit"); 00252 00253 // flush any remains into the target 00254 buffer.flush (); 00255 return this; 00256 } 00257 00258 /*********************************************************************** 00259 00260 Return the style used when creating this conduit 00261 00262 ***********************************************************************/ 00263 00264 ConduitStyle.Bits getStyle () 00265 { 00266 return style; 00267 } 00268 00269 /*********************************************************************** 00270 00271 Is the application terminating? 00272 00273 ***********************************************************************/ 00274 00275 static bool isHalting () 00276 { 00277 return halting; 00278 } 00279 } 00280 00281 00282 /******************************************************************************* 00283 00284 Define a conduit filter base-class. The filter is invoked 00285 via its reader() method whenever a block of content is 00286 being read, and by its writer() method whenever content is 00287 being written. 00288 00289 The filter should return the number of bytes it has actually 00290 produced: less than or equal to the length of the provided 00291 array. 00292 00293 Filters are chained together such that the last filter added 00294 is the first one invoked. It is the responsibility of each 00295 filter to invoke the next link in the chain; for example: 00296 00297 @code 00298 class MungingFilter : ConduitFilter 00299 { 00300 int reader (void[] dst) 00301 { 00302 // read the next X bytes 00303 int count = next.reader (dst); 00304 00305 // set everything to '*' ! 00306 dst[0..count] = '*'; 00307 00308 // say how many we read 00309 return count; 00310 } 00311 00312 int writer (void[] src) 00313 { 00314 byte[] tmp = new byte[src.length]; 00315 00316 // set everything to '*' 00317 tmp = '*'; 00318 00319 // write the munged output 00320 return next.writer (tmp); 00321 } 00322 } 00323 @endcode 00324 00325 Notice how this filter invokes the 'next' instance before 00326 munging the content ... the far end of the chain is where 00327 the original IConduit reader is attached, so it will get 00328 invoked eventually assuming each filter invokes 'next'. 00329 If the next reader fails it will return IConduit.Eof, as 00330 should your filter (or throw an IOException). From a client 00331 perspective, filters are attached like this: 00332 00333 @code 00334 FileConduit fc = new FileConduit (...); 00335 00336 fc.attach (new ZipFilter); 00337 fc.attach (new MungingFilter); 00338 @endcode 00339 00340 Again, the last filter attached is the first one invoked 00341 when a block of content is actually read. Each filter has 00342 two additional methods that it may use to control behavior: 00343 00344 @code 00345 class ConduitFilter : IConduitFilter 00346 { 00347 protected IConduitFilter next; 00348 00349 void bind (IConduit conduit, IConduitFilter next) 00350 { 00351 this.next = next; 00352 } 00353 00354 void unbind () 00355 { 00356 } 00357 } 00358 @endcode 00359 00360 The first method is invoked when the filter is attached to a 00361 conduit, while the second is invoked just before the conduit 00362 is closed. Both of these may be overridden by the filter for 00363 whatever purpose desired. 00364 00365 Note that a conduit filter can choose to sidestep reading from 00366 the conduit (per the usual case), and produce its input from 00367 somewhere else entirely. This mechanism supports the notion 00368 of 'piping' between multiple conduits, or between a conduit 00369 and something else entirely; it's a bridging mechanism. 00370 00371 00372 *******************************************************************************/ 00373 00374 class ConduitFilter : IConduitFilter 00375 { 00376 protected IConduitFilter next; 00377 00378 /*********************************************************************** 00379 00380 ***********************************************************************/ 00381 00382 uint reader (void[] dst) 00383 { 00384 return next.reader (dst); 00385 } 00386 00387 /*********************************************************************** 00388 00389 ***********************************************************************/ 00390 00391 uint writer (void[] src) 00392 { 00393 return next.writer (src); 00394 } 00395 00396 /*********************************************************************** 00397 00398 ***********************************************************************/ 00399 00400 protected void bind (IConduit conduit, IConduitFilter next) 00401 { 00402 this.next = next; 00403 } 00404 00405 /*********************************************************************** 00406 00407 ***********************************************************************/ 00408 00409 protected void unbind () 00410 { 00411 next.unbind (); 00412 } 00413 00414 /*********************************************************************** 00415 00416 ***********************************************************************/ 00417 00418 protected final void error (char[] msg) 00419 { 00420 throw new IOException (msg); 00421 } 00422 } 00423 00424 00425 /******************************************************************************* 00426 00427 Set a flag when the application is halting. This is used to avoid 00428 closure mechanics while the object pool may be in a state of flux. 00429 00430 *******************************************************************************/ 00431 00432 private static bool halting; 00433 00434 private static ~this() 00435 { 00436 halting = true; 00437 } 00438