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