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 00027 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00028 00029 00030 @version Initial version, March 2004 00031 @author Kris 00032 00033 00034 *******************************************************************************/ 00035 00036 module mango.io.Conduit; 00037 00038 private import mango.io.Resource, 00039 mango.io.Exception, 00040 mango.io.ConduitStyle; 00041 00042 private import mango.io.model.IBuffer; 00043 00044 public import mango.io.model.IConduit; 00045 00046 /******************************************************************************* 00047 00048 Conduit abstract base-class, implementing interface IConduit. 00049 Only the conduit-specific reader, writer, 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 reads and writes from/to an IBuffer in large chunks, 00061 typically the entire buffer. 00062 00063 *******************************************************************************/ 00064 00065 class Conduit : Resource, IConduit 00066 { 00067 private ConduitStyle style; 00068 private IConduitSource input; 00069 private IConduitSink output; 00070 private bool seekable; 00071 00072 /*********************************************************************** 00073 00074 Construct a conduit with the given style and seek abilities. 00075 Conduits are either seekable or non-seekable. 00076 00077 ***********************************************************************/ 00078 00079 this (ConduitStyle style, bool seekable) 00080 { 00081 input = this; 00082 output = this; 00083 this.style = style; 00084 this.seekable = seekable; 00085 } 00086 00087 /*********************************************************************** 00088 00089 Create a Buffer of a conduit-specific size 00090 00091 ***********************************************************************/ 00092 00093 abstract IBuffer createBuffer (); 00094 00095 /*********************************************************************** 00096 00097 conduit-specific reader 00098 00099 ***********************************************************************/ 00100 00101 protected abstract int reader (void[] dst); 00102 00103 /*********************************************************************** 00104 00105 conduit-specific writer 00106 00107 ***********************************************************************/ 00108 00109 protected abstract int writer (void[] src); 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 protected override void closure () 00121 { 00122 input.unbind (); 00123 output.unbind (); 00124 } 00125 00126 /*********************************************************************** 00127 00128 Please refer to IConduit.attach for details 00129 00130 ***********************************************************************/ 00131 00132 void attach (IConduitSource input) 00133 { 00134 input.bind (this.input); 00135 this.input = input; 00136 } 00137 00138 /*********************************************************************** 00139 00140 Please refer to IConduit.attach for details 00141 00142 ***********************************************************************/ 00143 00144 void attach (IConduitSink output) 00145 { 00146 output.bind (this.output); 00147 this.output = output; 00148 } 00149 00150 /*********************************************************************** 00151 00152 ***********************************************************************/ 00153 00154 protected void bind (IConduitSink next) 00155 { 00156 } 00157 00158 /*********************************************************************** 00159 00160 ***********************************************************************/ 00161 00162 protected void bind (IConduitSource next) 00163 { 00164 } 00165 00166 /*********************************************************************** 00167 00168 ***********************************************************************/ 00169 00170 protected void unbind () 00171 { 00172 } 00173 00174 /*********************************************************************** 00175 00176 read from conduit into a target buffer 00177 00178 ***********************************************************************/ 00179 00180 int read (IBuffer target) 00181 in { 00182 assert (target); 00183 } 00184 body 00185 { 00186 return target.write (&input.reader); 00187 } 00188 00189 /*********************************************************************** 00190 00191 write to conduit from a source buffer 00192 00193 ***********************************************************************/ 00194 00195 int write (IBuffer source) 00196 in { 00197 assert (source); 00198 } 00199 body 00200 { 00201 int count = source.read (&output.writer); 00202 00203 // if we didn't write everything, move remaining content 00204 // to front of buffer for a subsequent write 00205 source.compress (); 00206 return count; 00207 } 00208 00209 /*********************************************************************** 00210 00211 Returns true if this conduit is seekable (whether it 00212 implements ISeekable) 00213 00214 ***********************************************************************/ 00215 00216 bool isSeekable () 00217 { 00218 return seekable; 00219 } 00220 00221 /*********************************************************************** 00222 00223 Returns true is this conduit can be read from 00224 00225 ***********************************************************************/ 00226 00227 bool isReadable () 00228 { 00229 return (style.access & ConduitStyle.Access.Read) != 0; 00230 } 00231 00232 /*********************************************************************** 00233 00234 Returns true if this conduit can be written to 00235 00236 ***********************************************************************/ 00237 00238 bool isWritable () 00239 { 00240 return (style.access & ConduitStyle.Access.Write) != 0; 00241 } 00242 00243 /*********************************************************************** 00244 00245 Transfer the content of another conduit to this one. Returns 00246 a reference to this class, and throws IOException on failure. 00247 00248 ***********************************************************************/ 00249 00250 IConduit copy (IConduit source) 00251 { 00252 IBuffer buffer = createBuffer; 00253 00254 while (source.read (buffer) != Eof) 00255 if (write (buffer) == Eof) 00256 throw new IOException ("Eof while copying conduit"); 00257 00258 // flush any remains into the target 00259 return flush (buffer); 00260 } 00261 00262 /*********************************************************************** 00263 00264 Flush buffer content out to this conduit. 00265 00266 Throws an IOException if flushing results in an Eof condition. 00267 00268 ***********************************************************************/ 00269 00270 IConduit flush (IBuffer source) 00271 { 00272 while (source.readable) 00273 if (write (source) == Eof) 00274 throw new IOException ("Eof while flushing conduit"); 00275 return this; 00276 } 00277 00278 /*********************************************************************** 00279 00280 Return the style used when creating this conduit 00281 00282 ***********************************************************************/ 00283 00284 ConduitStyle getStyle () 00285 { 00286 return style; 00287 } 00288 } 00289 00290 00291 00292 /******************************************************************************* 00293 00294 Defines an input filter base-class. The filter is invoked 00295 via its reader(void[]) method whenever a block of content is 00296 being read; the void[] array represents content destination. 00297 The filter should return the number of bytes it has actually 00298 produced: less than or equal to the length of the provided 00299 array. 00300 00301 Filters are chained together such that the last filter added 00302 is the first one invoked. It is the responsibility of each 00303 filter to invoke the next link in the chain; for example: 00304 00305 @code 00306 class MungingInputFilter : FilterInput 00307 { 00308 int reader (void[] dst) 00309 { 00310 // read the next X bytes 00311 int count = next.reader (dst); 00312 00313 // set everything to '*' ! 00314 dst[0..count] = '*'; 00315 00316 // say how many we read 00317 return count; 00318 } 00319 } 00320 @endcode 00321 00322 Notice how this filter invokes the 'next' instance before 00323 munging the content ... the far end of the chain is where 00324 the original IConduit reader is attached, so it will get 00325 invoked eventually assuming each filter invokes 'next'. 00326 If the next reader fails it will return IConduit.Eof, as 00327 should your filter (or throw an IOException). From a client 00328 perspective, filters are attached like this: 00329 00330 @code 00331 FileConduit fc = new FileConduit (...); 00332 00333 fc.attach (new ZipInputFilter); 00334 fc.attach (new MungingInputFilter); 00335 @endcode 00336 00337 Again, the last filter attached is the first one invoked 00338 when a block of content is actually read. Each filter has 00339 two additional methods that it may use to control behavior: 00340 00341 @code 00342 class FilterInput : IFilterInput 00343 { 00344 protected IFilterInput next; 00345 00346 void bind (IFilterInput next) 00347 { 00348 this.next = next; 00349 } 00350 00351 void unbind () 00352 { 00353 } 00354 } 00355 @endcode 00356 00357 The first method is invoked when the filter is attached to a 00358 conduit, while the second is invoked just before the conduit 00359 is closed. Both of these may be overridden by the filter for 00360 whatever purpose desired. 00361 00362 Note that an input filter can choose to sidestep reading from 00363 the conduit (per the usual case), and produce its input from 00364 somewhere else entirely. This mechanism supports the notion 00365 of 'piping' between multiple conduits, or between a conduit 00366 and something else entirely; it's a bridging mechanism. 00367 00368 *******************************************************************************/ 00369 00370 class ConduitSource : IConduitSource 00371 { 00372 protected IConduitSource next; 00373 00374 /*********************************************************************** 00375 00376 ***********************************************************************/ 00377 00378 abstract int reader (void[] dst); 00379 00380 /*********************************************************************** 00381 00382 ***********************************************************************/ 00383 00384 protected void bind (IConduitSource next) 00385 { 00386 //printf ("bind input\n"); 00387 this.next = next; 00388 } 00389 00390 /*********************************************************************** 00391 00392 ***********************************************************************/ 00393 00394 protected void unbind () 00395 { 00396 //printf ("unbind input\n"); 00397 next.unbind (); 00398 } 00399 } 00400 00401 00402 /******************************************************************************* 00403 00404 Defines an output filter base-class. The filter is invoked 00405 via its writer(void[]) method whenever a block of content is 00406 being written; the void[] array supplies writeable content. 00407 The filter should return the number of bytes it has actually 00408 consumed: less than or equal to the length of the provided 00409 array. 00410 00411 Filters are chained together such that the last filter added 00412 is the first one invoked. It is the responsibility of each 00413 filter to invoke the next link in the chain; for example: 00414 00415 @code 00416 class MungingOutputFilter : FilterOutput 00417 { 00418 int writer (void[] src) 00419 { 00420 char[src.length] tmp; 00421 00422 // set everything to '*' 00423 tmp = '*'; 00424 00425 // write the munged output 00426 return next.writer (tmp); 00427 } 00428 } 00429 @endcode 00430 00431 Notice how the filter invokes the 'next' instance after 00432 munging the content ... the far end of the chain is where 00433 the original IConduit writer is attached, so it will get 00434 invoked eventually assuming each filter invokes 'next'. 00435 If the next writer fails it will return IConduit.Eof, as 00436 should your filter (or throw an IOException). At the client 00437 level, filters are attached like this: 00438 00439 @code 00440 FileConduit fc = new FileConduit (...); 00441 00442 fc.attach (new ZipOutputFilter); 00443 fc.attach (new MungingOutputFilter); 00444 @endcode 00445 00446 Again, the last filter attached is the first one invoked 00447 when a block of content is actually written. Each filter has 00448 two additional methods that it may use to control behavior: 00449 00450 @code 00451 class FilterOutput : IFilterOutput 00452 { 00453 protected IFilterOutput next; 00454 00455 void bind (IFilterOutput next) 00456 { 00457 this.next = next; 00458 } 00459 00460 void unbind () 00461 { 00462 } 00463 } 00464 @endcode 00465 00466 The first method is invoked when the filter is attached to a 00467 conduit, while the second is invoked just before the conduit 00468 is closed. Both of these may be overridden by the filter for 00469 whatever purpose desired. 00470 00471 Note that an output filter can choose to sidestep writing to 00472 the conduit (per the usual case), and direct its output to 00473 somewhere else entirely. This mechanism supports the notion 00474 of 'piping' between multiple conduits, or between a conduit 00475 and something else entirely; as with the input-filter case, 00476 this is a bridging mechanism. 00477 00478 *******************************************************************************/ 00479 00480 class ConduitSink : IConduitSink 00481 { 00482 protected IConduitSink next; 00483 00484 /*********************************************************************** 00485 00486 ***********************************************************************/ 00487 00488 abstract int writer (void[] src); 00489 00490 /*********************************************************************** 00491 00492 ***********************************************************************/ 00493 00494 protected void bind (IConduitSink next) 00495 { 00496 //printf ("bind output\n"); 00497 this.next = next; 00498 } 00499 00500 /*********************************************************************** 00501 00502 ***********************************************************************/ 00503 00504 protected void unbind () 00505 { 00506 //printf ("unbind output\n"); 00507 next.unbind (); 00508 } 00509 }