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