00001 /******************************************************************************* 00002 00003 @file AbstractWriter.d 00004 00005 00006 Copyright (c) 2004 Kris Bell 00007 00008 This software is provided 'as-is', without any express or implied 00009 warranty. In no event will the authors be held liable for damages 00010 of any kind arising from the use of this software. 00011 00012 Permission is hereby granted to anyone to use this software for any 00013 purpose, including commercial applications, and to alter it and/or 00014 redistribute it freely, subject to the following restrictions: 00015 00016 1. The origin of this software must not be misrepresented; you must 00017 not claim that you wrote the original software. If you use this 00018 software in a product, an acknowledgment within documentation of 00019 said product would be appreciated but is not required. 00020 00021 2. Altered source versions must be plainly marked as such, and must 00022 not be misrepresented as being the original software. 00023 00024 3. This notice may not be removed or altered from any distribution 00025 of the source. 00026 00027 4. Derivative works are permitted, but they must carry this notice 00028 in full and credit the original source. 00029 00030 00031 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00032 00033 00034 @version Initial version, October 2004 00035 @author Kris, Ivan Senji (the "alias opShl put" idea) 00036 00037 *******************************************************************************/ 00038 00039 module mango.io.AbstractWriter; 00040 00041 private import mango.io.Exception; 00042 00043 public import mango.io.model.IWriter, 00044 mango.io.model.IBuffer, 00045 mango.io.model.IConduit; 00046 00047 /******************************************************************************* 00048 00049 Writer base-class. Writers provide the means to append formatted 00050 data to an IBuffer, and expose a convenient method of handling a 00051 variety of data types. In addition to writing native types such 00052 as integer and char[], writers also process any class which has 00053 implemented the IWritable interface (one method). 00054 00055 Writers support both a C++ iostream type syntax, along with a put() 00056 syntax. Operations may be chained back-to-back. 00057 00058 All writers support the full set of native data types, plus a full 00059 selection of array types. 00060 00061 The code below illustrates basic operation upon a memory buffer: 00062 00063 @code 00064 Buffer buf = new Buffer (256); 00065 00066 // map same buffer into both reader and writer 00067 IReader r = new Reader(buf); 00068 IWriter w = new Writer(buf); 00069 00070 int i = 10; 00071 long j = 20; 00072 double d = 3.14159; 00073 char[] c = "fred"; 00074 00075 // write data types out 00076 w << c << i << j << d; 00077 00078 // read them back again 00079 r >> c >> i >> j >> d; 00080 00081 // reset 00082 buf.clear(); 00083 00084 // same thing again, but using put() syntax instead 00085 w.put(c).put(i).put(j).put(d); 00086 r.get(c).get(i).get(j).get(d); 00087 00088 @endcode 00089 00090 Writers may also be used with any class implementing the IWritable 00091 interface. See PickleReader for an example of how this can be put 00092 to good use. 00093 00094 *******************************************************************************/ 00095 00096 class AbstractWriter : IWriter 00097 { 00098 alias opShl put; 00099 00100 /*********************************************************************** 00101 00102 ***********************************************************************/ 00103 00104 union Encoder 00105 { 00106 struct { 00107 BufferEncoder int1, 00108 int8, 00109 int16, 00110 int32, 00111 int64, 00112 int8u, 00113 int16u, 00114 int32u, 00115 int64u, 00116 float32, 00117 float64, 00118 float80, 00119 char8, 00120 char16, 00121 char32; 00122 } 00123 BufferEncoder[15] encoders; 00124 } 00125 00126 // public newline adaptor 00127 static INewlineWriter newline; 00128 00129 // a couple of pre-constructed exceptions 00130 protected static IOException ovf; 00131 00132 protected IBuffer buffer; 00133 00134 protected Encoder encode; 00135 00136 private bool prefixArray = true; 00137 00138 /*********************************************************************** 00139 00140 Construct some static exception instances, and create the 00141 public 'newline' instance. 00142 00143 ***********************************************************************/ 00144 00145 static this () 00146 { 00147 newline = new NewlineWriter; 00148 00149 ovf = new IOException ("output buffer too small"); 00150 } 00151 00152 /*********************************************************************** 00153 00154 Construct a Writer upon the provided IBuffer. All formatted 00155 output will be appended to this buffer. 00156 00157 ***********************************************************************/ 00158 00159 this (IBuffer buffer) 00160 { 00161 this.buffer = buffer; 00162 } 00163 00164 /*********************************************************************** 00165 00166 Return the associated buffer 00167 00168 ***********************************************************************/ 00169 00170 final IBuffer getBuffer () 00171 { 00172 return buffer; 00173 } 00174 00175 /*********************************************************************** 00176 00177 Bind an IEncoder to the writer. Encoders are intended to 00178 be used as a conversion mechanism between various character 00179 representations (encodings), or the translation of any data 00180 type from one representation to another. Each data type may 00181 be configured with a distinct encoder, covering all native 00182 types (15 in total). 00183 00184 An appropriate encoder set should be attached to each 00185 IWriter, and thus be available for subsequent use. A raw 00186 binary implementation is attached by default (no encoding). 00187 00188 See module mango.icu.UMango for an example of encoder 00189 implementation -- those classes bind the ICU converters 00190 to this IO package. 00191 00192 ***********************************************************************/ 00193 00194 final void setEncoder (IEncoder e) 00195 { 00196 encode.encoders[e.type] = e.bind (this); 00197 } 00198 00199 /*********************************************************************** 00200 00201 Flush the output of this writer. Returns false if the 00202 operation failed, true otherwise. 00203 00204 ***********************************************************************/ 00205 00206 IWriter flush () 00207 { 00208 buffer.flush (); 00209 return this; 00210 } 00211 00212 /*********************************************************************** 00213 00214 Output a newline. Do this indirectly so that it can be 00215 intercepted by subclasses. 00216 00217 ***********************************************************************/ 00218 00219 IWriter cr () 00220 { 00221 return put (newline); 00222 } 00223 00224 /*********************************************************************** 00225 00226 ***********************************************************************/ 00227 00228 void enableArrayPrefix (bool on) 00229 { 00230 prefixArray = on; 00231 } 00232 00233 /*********************************************************************** 00234 00235 Write a class to the current buffer-position 00236 00237 ***********************************************************************/ 00238 00239 private final uint length (uint len) 00240 { 00241 if (prefixArray) 00242 put (len); 00243 return len; 00244 } 00245 00246 /*********************************************************************** 00247 00248 Write a class to the current buffer-position 00249 00250 ***********************************************************************/ 00251 00252 IWriter opShl (IWritable x) 00253 { 00254 assert (x); 00255 x.write (this); 00256 return this; 00257 } 00258 00259 /*********************************************************************** 00260 00261 Write a boolean value to the current buffer-position 00262 00263 ***********************************************************************/ 00264 00265 IWriter opShl (bool x) 00266 { 00267 encode.int1 (&x, x.sizeof); 00268 return this; 00269 } 00270 00271 /*********************************************************************** 00272 00273 Write an unsigned byte value to the current buffer-position 00274 00275 ***********************************************************************/ 00276 00277 IWriter opShl (ubyte x) 00278 { 00279 encode.int8u (&x, x.sizeof); 00280 return this; 00281 } 00282 00283 /*********************************************************************** 00284 00285 Write a byte value to the current buffer-position 00286 00287 ***********************************************************************/ 00288 00289 IWriter opShl (byte x) 00290 { 00291 encode.int8 (&x, x.sizeof); 00292 return this; 00293 } 00294 00295 /*********************************************************************** 00296 00297 Write an unsigned short value to the current buffer-position 00298 00299 ***********************************************************************/ 00300 00301 IWriter opShl (ushort x) 00302 { 00303 encode.int16u (&x, x.sizeof); 00304 return this; 00305 } 00306 00307 /*********************************************************************** 00308 00309 Write a short value to the current buffer-position 00310 00311 ***********************************************************************/ 00312 00313 IWriter opShl (short x) 00314 { 00315 encode.int16 (&x, x.sizeof); 00316 return this; 00317 } 00318 00319 /*********************************************************************** 00320 00321 Write a unsigned int value to the current buffer-position 00322 00323 ***********************************************************************/ 00324 00325 IWriter opShl (uint x) 00326 { 00327 encode.int32u (&x, x.sizeof); 00328 return this; 00329 } 00330 00331 /*********************************************************************** 00332 00333 Write an int value to the current buffer-position 00334 00335 ***********************************************************************/ 00336 00337 IWriter opShl (int x) 00338 { 00339 encode.int32 (&x, x.sizeof); 00340 return this; 00341 } 00342 00343 /*********************************************************************** 00344 00345 Write an unsigned long value to the current buffer-position 00346 00347 ***********************************************************************/ 00348 00349 IWriter opShl (ulong x) 00350 { 00351 encode.int64u (&x, x.sizeof); 00352 return this; 00353 } 00354 00355 /*********************************************************************** 00356 00357 Write a long value to the current buffer-position 00358 00359 ***********************************************************************/ 00360 00361 IWriter opShl (long x) 00362 { 00363 encode.int64 (&x, x.sizeof); 00364 return this; 00365 } 00366 00367 /*********************************************************************** 00368 00369 Write a float value to the current buffer-position 00370 00371 ***********************************************************************/ 00372 00373 IWriter opShl (float x) 00374 { 00375 encode.float32 (&x, x.sizeof); 00376 return this; 00377 } 00378 00379 /*********************************************************************** 00380 00381 Write a double value to the current buffer-position 00382 00383 ***********************************************************************/ 00384 00385 IWriter opShl (double x) 00386 { 00387 encode.float64 (&x, x.sizeof); 00388 return this; 00389 } 00390 00391 /*********************************************************************** 00392 00393 Write a real value to the current buffer-position 00394 00395 ***********************************************************************/ 00396 00397 IWriter opShl (real x) 00398 { 00399 encode.float80 (&x, x.sizeof); 00400 return this; 00401 } 00402 00403 /*********************************************************************** 00404 00405 Write a char value to the current buffer-position 00406 00407 ***********************************************************************/ 00408 00409 IWriter opShl (char x) 00410 { 00411 encode.char8 (&x, x.sizeof); 00412 return this; 00413 } 00414 00415 /*********************************************************************** 00416 00417 Write a wide char value to the current buffer-position 00418 00419 ***********************************************************************/ 00420 00421 IWriter opShl (wchar x) 00422 { 00423 encode.char16 (&x, x.sizeof); 00424 return this; 00425 } 00426 00427 /*********************************************************************** 00428 00429 Write a double char value to the current buffer-position 00430 00431 ***********************************************************************/ 00432 00433 IWriter opShl (dchar x) 00434 { 00435 encode.char32 (&x, x.sizeof); 00436 return this; 00437 } 00438 00439 /*********************************************************************** 00440 00441 Write a byte array to the current buffer-position 00442 00443 ***********************************************************************/ 00444 00445 IWriter opShl (byte[] x) 00446 { 00447 encode.int8 (x, length (x.length) * byte.sizeof); 00448 return this; 00449 } 00450 00451 /*********************************************************************** 00452 00453 Write an unsigned byte array to the current buffer-position 00454 00455 ***********************************************************************/ 00456 00457 IWriter opShl (ubyte[] x) 00458 { 00459 encode.int8u (x, length (x.length) * ubyte.sizeof); 00460 return this; 00461 } 00462 00463 /*********************************************************************** 00464 00465 Write a short array to the current buffer-position 00466 00467 ***********************************************************************/ 00468 00469 IWriter opShl (short[] x) 00470 { 00471 encode.int16 (x, length (x.length) * short.sizeof); 00472 return this; 00473 } 00474 00475 /*********************************************************************** 00476 00477 Write an unsigned short array to the current buffer-position 00478 00479 ***********************************************************************/ 00480 00481 IWriter opShl (ushort[] x) 00482 { 00483 encode.int16u (x, length (x.length) * ushort.sizeof); 00484 return this; 00485 } 00486 00487 /*********************************************************************** 00488 00489 Write an int array to the current buffer-position 00490 00491 ***********************************************************************/ 00492 00493 IWriter opShl (int[] x) 00494 { 00495 encode.int32 (x, length (x.length) * int.sizeof); 00496 return this; 00497 } 00498 00499 /*********************************************************************** 00500 00501 Write an unsigned int array to the current buffer-position 00502 00503 ***********************************************************************/ 00504 00505 IWriter opShl (uint[] x) 00506 { 00507 encode.int32u (x, length (x.length) * uint.sizeof); 00508 return this; 00509 } 00510 00511 /*********************************************************************** 00512 00513 Write a long array to the current buffer-position 00514 00515 ***********************************************************************/ 00516 00517 IWriter opShl (long[] x) 00518 { 00519 encode.int64 (x, length (x.length) * long.sizeof); 00520 return this; 00521 } 00522 00523 /*********************************************************************** 00524 00525 Write an unsigned long array to the current buffer-position 00526 00527 ***********************************************************************/ 00528 00529 IWriter opShl (ulong[] x) 00530 { 00531 encode.int64u (x, length (x.length) * ulong.sizeof); 00532 return this; 00533 } 00534 00535 /*********************************************************************** 00536 00537 Write a float array to the current buffer-position 00538 00539 ***********************************************************************/ 00540 00541 IWriter opShl (float[] x) 00542 { 00543 encode.float32 (x, length (x.length) * float.sizeof); 00544 return this; 00545 } 00546 00547 /*********************************************************************** 00548 00549 Write a double array to the current buffer-position 00550 00551 ***********************************************************************/ 00552 00553 IWriter opShl (double[] x) 00554 { 00555 encode.float64 (x, length (x.length) * double.sizeof); 00556 return this; 00557 } 00558 00559 /*********************************************************************** 00560 00561 Write a real array to the current buffer-position 00562 00563 ***********************************************************************/ 00564 00565 IWriter opShl (real[] x) 00566 { 00567 encode.float80 (x, length (x.length) * real.sizeof); 00568 return this; 00569 } 00570 00571 /*********************************************************************** 00572 00573 Write a char array to the current buffer-position 00574 00575 ***********************************************************************/ 00576 00577 IWriter opShl (char[] x) 00578 { 00579 encode.char8 (x, length (x.length) * char.sizeof); 00580 return this; 00581 } 00582 00583 /*********************************************************************** 00584 00585 Write a char array to the current buffer-position 00586 00587 ***********************************************************************/ 00588 00589 IWriter opShlw (wchar[] x) 00590 { 00591 encode.char16 (x, length (x.length) * wchar.sizeof); 00592 return this; 00593 } 00594 00595 /*********************************************************************** 00596 00597 Write a char array to the current buffer-position 00598 00599 ***********************************************************************/ 00600 00601 IWriter opShld (dchar[] x) 00602 { 00603 encode.char32 (x, length (x.length) * dchar.sizeof); 00604 return this; 00605 } 00606 } 00607 00608 00609 /******************************************************************************* 00610 00611 A class to handle newline output. One might reasonable expect to 00612 emit a char[] for newlines; FileSystem.NewlineString for example. 00613 Turns out that it's much more efficient to intercept line-breaks 00614 when they're implemented in a more formal manner (such as this). 00615 00616 For example, ColumnWriter() and TextWriter() both must intercept 00617 newline output so they can adjust formatting appropriately. It is 00618 much more efficient for such writers to intercept the IWritable 00619 put() method instead of scanning each char[] for the various \\n 00620 combinations. 00621 00622 Please use the INewlineWriter interface for emitting newlines. 00623 00624 *******************************************************************************/ 00625 00626 private import mango.io.FileSystem; 00627 00628 class NewlineWriter : INewlineWriter 00629 { 00630 private char[] fmt; 00631 00632 /*********************************************************************** 00633 00634 Construct a default newline, using the char[] defined 00635 by FileSystem.NewlineString 00636 00637 ***********************************************************************/ 00638 00639 this () 00640 { 00641 this (FileSystem.NewlineString); 00642 } 00643 00644 /*********************************************************************** 00645 00646 Construct a newline using the provided character array 00647 00648 ***********************************************************************/ 00649 00650 this (char[] fmt) 00651 { 00652 this.fmt = fmt; 00653 } 00654 00655 /*********************************************************************** 00656 00657 Write this newline through the provided writer. This makes 00658 NewlineWriter IWritable compatible. 00659 00660 ***********************************************************************/ 00661 00662 void write (IWriter w) 00663 { 00664 w.put (fmt); 00665 } 00666 }