Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

Format.d

Go to the documentation of this file.
00001 /*******************************************************************************
00002 
00003         @file Format.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; Nov 2005
00034 
00035         @author         Kris
00036 
00037 
00038 *******************************************************************************/
00039 
00040 module mango.convert.Format;
00041 
00042 private import  std.stdarg;
00043 
00044 private import  mango.convert.Type,
00045                 mango.convert.Integer;
00046 
00047 
00048 /******************************************************************************
00049 
00050         Functions for styled, readable, output. See parse() for the 
00051         list of format specifiers.
00052 
00053 ******************************************************************************/
00054 
00055 struct FormatStructTemplate(T)
00056 {       
00057         private alias IntegerTemplate!(T) Integer;
00058 
00059         private alias Integer.Flags Flags;
00060 
00061         typedef T[] function (T[], double, uint, bool) DblFormat;
00062 
00063         typedef uint delegate (void[], uint type) Emitter;
00064 
00065         typedef void delegate () Close;
00066 
00067 
00068         private Emitter         sink;           // text emitter
00069         private Close           close;          // completion notifier
00070         private int             style,          // character following %
00071                                 width,          // width specifier
00072                                 precision;      // number of decimals
00073         private Flags           flags;          // format controls
00074         private T[]             head,           // start of format string
00075                                 tail,           // end of format string
00076                                 meta;           // current format string
00077         private DblFormat       dFormat;        // floating-point handler
00078         private T[]             workspace;      // formatting buffer
00079 
00080 
00081         private static T[64]   Spaces = ' ';    // for padding the output
00082 
00083         mixin Type.TextType!(T);
00084 
00085 
00086         /**********************************************************************
00087 
00088                 Default styles for supported styles. This is used to 
00089                 format discrete values, or those provided without a 
00090                 specified format-string. 
00091 
00092         **********************************************************************/
00093 
00094         private static ubyte DefaultStyle[] = ['s', 'd', 'u', 'd', 'u', 
00095                                                'd', 'u', 'd', 'u', 'f', 
00096                                                'f', 'f', 's', 's', 's', 
00097                                                'x'];
00098 
00099 
00100         /**********************************************************************
00101 
00102                 Configure this Format with an output handler, a
00103                 workspace area, and a floating point handler.
00104                 The latter is optional.
00105 
00106         **********************************************************************/
00107 
00108         void ctor (Emitter sink, Close close, T[] workspace, DblFormat dFormat = null)
00109         {
00110                 this.sink = sink;
00111                 this.close = close;
00112                 this.dFormat = dFormat;
00113                 this.workspace = workspace;
00114         }
00115 
00116         /**********************************************************************
00117 
00118                 Throw an error
00119 
00120         **********************************************************************/
00121 
00122         static void error (char[] msg)
00123         {
00124                 Integer.error(msg);
00125         }
00126 
00127         /***********************************************************************
00128         
00129                 General purpose va_arg format routine, used by a number of
00130                 classes and structs to emit printf() styled text. 
00131 
00132                 This implementation simply converts its arguments into the
00133                 appropriate internal style, and invokes the standard Mango
00134                 style-formatter for each.
00135 
00136                 Note that this can handle arrays of type in addition to the
00137                 usual char[] ~ e.g. one can print an array of integers just
00138                 as easily as a single integer.
00139 
00140                 See parse() for the list of format specifiers.
00141 
00142         ***********************************************************************/
00143 
00144         int opCall (T[] format, TypeInfo[] arguments, va_list argptr)
00145         {      
00146                 int length;
00147 
00148                 // set the format string
00149                 meta = format;
00150 
00151                 // traverse the arguments ...
00152                 foreach (TypeInfo ti; arguments)
00153                         {
00154                         int count = 1;
00155                         void *p = argptr;
00156 
00157                         uint t = ti.classinfo.name[9];
00158 
00159                         // is this an array style?
00160                         if (t == 'A')
00161                            {
00162                            t = ti.classinfo.name[10];
00163                            p = (*(cast(void[]*) argptr)).ptr;
00164                            count = (*(cast(void[]*) argptr)).length;
00165                            }                
00166 
00167                         t = getType (t);
00168                         int width = Type.widths[t];
00169 
00170                         // print this argument ...
00171                         length += emit (p, width * count, t);
00172 
00173                         // get next argument ...
00174                         argptr += (count > 1) ? (void[]).sizeof : ((width + int.sizeof - 1) & ~(int.sizeof - 1));
00175                         }
00176 
00177                 // flush any remaining format text
00178                 if (meta.length)
00179                    {
00180                    length += sink (meta, TextType);
00181                    meta = null;
00182                    }
00183 
00184                 if (close)
00185                     close();
00186 
00187                 return length;
00188         }
00189 
00190 
00191         /***********************************************************************
00192         
00193                 Emit a single formatted argument. Format may be null
00194 
00195         ***********************************************************************/
00196 
00197         int opCall (T[] format, void* src, uint bytes, uint type)
00198         {
00199                 meta = format;
00200                 int length = emit (src, bytes, type);
00201 
00202                 // flush any remaining format text
00203                 if (meta.length)
00204                    {
00205                    length += sink (meta, TextType);
00206                    meta = null;
00207                    }
00208                 return length;
00209         }
00210 
00211 
00212         /***********************************************************************
00213         
00214                 Emit a single formatted argument.
00215 
00216                 Note that emit() can handle type-arrays in addition to the
00217                 usual char[]; e.g. one can print an array of integers just
00218                 as easily as a single integer. To support this, a new style
00219                 flag has been introduced to terminate the format string at
00220                 the point of use. That is, the rest of the format string is
00221                 considered to be part of the current format specifier, and
00222                 will be repeated for each array element. For example, the
00223                 format string " %@x," has a preceeding space, a trailing
00224                 comma, and the array flag '@'. This will output an array of 
00225                 numeric values (char, byte, short, int, long, float, double,
00226                 real, pointer) as a set of formatted hexadecimal strings.
00227 
00228                 See parse() for the list of format specifiers.
00229 
00230         ***********************************************************************/
00231 
00232         int emit (void* src, uint bytes, uint type)
00233         {
00234                 int     iValue;
00235                 long    lValue;
00236                 double  fValue;
00237                 uint    length;
00238 
00239                 // convert format segment to Style
00240                 if (parse (meta) == 0)
00241                     style = DefaultStyle[type];
00242 
00243                 if (flags & Flags.Array)
00244                     meta = null;
00245                 else
00246                    {
00247                    // flip remaining format, if array not specified
00248                    meta = tail;
00249                    tail = null;
00250                    }
00251 
00252                 // get width of elements (note: does not work for bit[])
00253                 int size = Type.widths[type];
00254 
00255                 // always emit prefix text 
00256                 if (head.length)
00257                     length = sink (head, TextType);                   
00258 
00259                 // for all bytes in source ...
00260                 while (bytes)
00261                       {
00262                       switch (type)
00263                              {
00264                              case Type.Bool:
00265                                   iValue = *cast(bool*) src;
00266                                   if (style != 's')
00267                                       goto int32Format;
00268 
00269                                   static T[] True = "true",
00270                                              False = "false";
00271 
00272                                   length += sink (iValue ? True : False, TextType);
00273                                   break;
00274 
00275                              case Type.Byte:
00276                                   iValue = *cast(byte*) src;
00277                                   goto int32Format;
00278 
00279                              case Type.UByte:
00280                                   iValue = *cast(ubyte*) src;
00281                                   goto int32Format;
00282 
00283                              case Type.Short:
00284                                   iValue = *cast(short*) src;
00285                                   goto int32Format;
00286 
00287                              case Type.UShort:
00288                                   iValue = *cast(ushort*) src;
00289                                   goto int32Format;
00290 
00291                              case Type.Int:
00292                              case Type.UInt:
00293                              case Type.Pointer:
00294 int32:
00295                                   iValue = *cast(int*) src;
00296 int32Format:
00297                                   length += emit (cast(long) iValue);
00298                                   break;
00299 
00300                              case Type.Long:
00301                              case Type.ULong:
00302 int64:
00303                                   lValue = *cast(long*) src;
00304 int64Format:
00305                                   length += emit (lValue);
00306                                   break;
00307 
00308                              case Type.Float:
00309                                   if (style == 'x' || 
00310                                       style == 'X')
00311                                       goto int32;
00312                                   fValue = *cast(float*) src;
00313                                   goto floating;
00314 
00315                              case Type.Double:
00316                                   if (style == 'x' || 
00317                                       style == 'X')
00318                                       goto int64;
00319                                   fValue = *cast(double*) src;
00320 floating:
00321                                   length += emit (fValue);
00322                                   break;
00323 
00324                              case Type.Real:
00325                                   fValue = *cast(real*) src;
00326                                   goto floating;
00327 
00328                              case Type.Utf8:
00329                              case Type.Utf16:
00330                              case Type.Utf32:
00331 
00332                                   int len = bytes;
00333                                   if (style == 's')
00334                                      {
00335                                      // emit as a string
00336                                      if (flags & Flags.Prec)
00337                                          if (precision < len)
00338                                              len = precision;
00339                                      bytes = size;
00340                                      }
00341                                   else
00342                                      if (style == 'c')
00343                                          // emit a single character
00344                                          len = size;
00345                                      else
00346                                         {
00347                                         // emit as a number
00348                                         if (type == Type.Utf16)
00349                                             type = Type.UShort;
00350                                         else
00351                                         if (type == Type.Utf32)
00352                                             type = Type.UInt;
00353                                         else
00354                                            type = Type.UByte;
00355                                         continue;
00356                                         }
00357 
00358                                   // emit as a string segment
00359                                   length += emit (src[0..len], type);
00360                                   break;
00361 
00362                              default:
00363                                   Integer.error ("Format.emit : unexpected argument type");
00364                              }
00365 
00366                       // bump counters and loop around for next instance
00367                       bytes -= size;
00368                       src += size;
00369                       }
00370                 return length;
00371         }
00372 
00373 
00374         /**********************************************************************
00375 
00376                 internal method to map data styles 
00377 
00378         **********************************************************************/
00379 
00380         private static uint getType (uint t)
00381         {
00382                 static  ubyte xlate[] = 
00383                         [
00384                         Type.Utf8, Type.Bool, 0, Type.Double, Type.Real, 
00385                         Type.Float, Type.Byte, Type.UByte, Type.Int, 0, 
00386                         Type.UInt, Type.Long, Type.ULong, 0, 0, 0, 0, 0, 
00387                         Type.Short, Type.UShort, Type.Utf16, 0, Type.Utf32, 
00388                         ];
00389 
00390                 if (t >= 'a' && t <= 'w')
00391                     t = xlate[t - 'a'];
00392                 else
00393                    if (t == 'P')
00394                        t = Type.Pointer;
00395                    else
00396                       Integer.error ("Format.getType : unexpected argument type");
00397                 return t;
00398         }
00399 
00400         /**********************************************************************
00401 
00402                 Clear the current state. This is typically used internally 
00403                 only.
00404 
00405         **********************************************************************/
00406 
00407         private void reset ()
00408         {
00409                 flags = 0;
00410                 head = tail = null;   
00411                 width = workspace.length;
00412         }
00413 
00414         /**********************************************************************
00415 
00416                 Emit some spaces. This was originally an inner method, 
00417                 but that caused the code size to inexplicably increase
00418                 by a large amount. A regular private function does not
00419                 have that effect.
00420 
00421         **********************************************************************/
00422 
00423         private int spaces (int count)
00424         {       
00425                 int ret;
00426 
00427                 while (count > Spaces.length)
00428                       {
00429                       ret += sink (Spaces, TextType);
00430                       count -= Spaces.length;
00431                       }
00432                 return ret + sink (Spaces[0..count], TextType);
00433         }
00434 
00435         /**********************************************************************
00436 
00437                 Emit a field, surrounded by optional prefix and postfix 
00438                 strings, and optionally padded with spaces.
00439 
00440         **********************************************************************/
00441 
00442         private int emit (void[] field, int type = TextType)
00443         {
00444                 int i = 0;
00445                 int pad = 0;
00446 
00447                 // emit prefix?
00448                 //if (head.length)
00449                     //i += sink (head, TextType);                   
00450 
00451                 // should we pad output?
00452                 if (flags & Flags.Fill && flags & Flags.Space)
00453                    {
00454                    pad = width - field.length;
00455                    if (pad < 0)
00456                        pad = 0;
00457 
00458                    // right-aligned?
00459                    if ((flags & Flags.Left) == 0)
00460                       {
00461                       i += spaces (pad);
00462                       pad = 0;
00463                       }                        
00464                    }
00465 
00466                 // emit field itself, indicating provided type
00467                 i += sink (field, type);
00468 
00469                 // any trailing padding?
00470                 if (pad)
00471                     i += spaces (pad);
00472 
00473                 // emit postfix
00474                 if (tail.length)
00475                     i += sink (tail, TextType);   
00476 
00477                 return i;
00478         }
00479 
00480         /**********************************************************************
00481 
00482                 Emit an integer field
00483 
00484         **********************************************************************/
00485 
00486         private int emit (long field)
00487         {
00488                 return emit (Integer.format (workspace[0..width], field, 
00489                              cast(Integer.Format) style, flags));
00490         }
00491 
00492         /**********************************************************************
00493 
00494                 Emit a floating-point field
00495 
00496         **********************************************************************/
00497 
00498         private int emit (double field)
00499         {
00500                 if (dFormat == null)
00501                     Integer.error ("Format.emit : decimal formatting not configured");
00502 
00503                 return emit (dFormat (workspace, field, 
00504                             (flags & Flags.Prec) ? precision : 6, 
00505                              cast(bool) (style == 'e')));
00506         }
00507 
00508         /**********************************************************************
00509 
00510                 test for a digit
00511 
00512         **********************************************************************/
00513 
00514         private static final bool isDigit (T t)
00515         {
00516                 return cast(bool) (t >= '0' && t <= '9');
00517         }
00518 
00519         /**********************************************************************
00520 
00521                 Parse a format specifier into its constituent
00522                 flags and values. Syntax follows the traditional
00523                 printf() approach, as follows:
00524 
00525                 %[flags][width][.precision]style
00526 
00527                 Where 'style' is one of:
00528 
00529                 s : string format
00530                 c : character format
00531                 d : signed format
00532                 u : unsigned format
00533                 x : hexadecimal format
00534                 X : uppercase hexadecimal format
00535                 e : scientific notation 
00536                 f : floating point format
00537                 g : 'e' or 'f', based upon width
00538 
00539                 Note that there are no variants on the format
00540                 styles ~ long, int, short, and byte differences
00541                 are all handled internally.
00542 
00543                 The 'flags' supported:
00544 
00545                 space : prefix negative integer with one space;
00546                         pad any style when combined with a width
00547                         specifier
00548                 -     : left-align fields padded with spaces
00549                 +     : prefix positive integer with one '+'
00550                 0     : prefix integers with zeroes; requires a
00551                         width specification
00552                 #     : prefix integers with a style specifier
00553                 @     : Array specifier
00554 
00555                 The 'width' should be specified for either zero or
00556                 space padding, and may be used with all formatting
00557                 styles.
00558 
00559                 A 'precision' can be used to stipulate the number
00560                 of decimal-places, or a slice of a text string.
00561 
00562                 Note that the Format package supports array-output
00563                 in addition to the usual printf() output.
00564 
00565         **********************************************************************/
00566 
00567         private T parse (T[] format)
00568         {
00569                 reset ();
00570                 head = format;
00571                 T* p = format.ptr;
00572 
00573                 for (int i = format.length; --i > 0; ++p)
00574                      if (*p == '%')
00575                          if (p[1] == '%')
00576                              ++p;
00577                          else
00578                             {
00579                             int len = p - format.ptr;
00580                             head = format [0..len];   
00581                             while (1)
00582                                   {
00583                                   switch (--i, *++p)
00584                                          {
00585                                          case '-': 
00586                                               flags |= Flags.Left;
00587                                               continue;
00588 
00589                                          case '+': 
00590                                               flags |= Flags.Plus;
00591                                               continue;
00592 
00593                                          case '#': 
00594                                               flags |= Flags.Hash;
00595                                               continue;
00596 
00597                                          case ' ': 
00598                                               flags |= Flags.Space;
00599                                               continue;
00600 
00601                                          case '0': 
00602                                               flags |= Flags.Zero;
00603                                               continue;
00604 
00605                                          case '@': 
00606                                               flags |= Flags.Array;
00607                                               continue;
00608 
00609                                          default: 
00610                                               ++i;
00611                                               break;
00612                                          }
00613                                   break;
00614                                   }
00615 
00616                             if (isDigit(*p))
00617                                {
00618                                int tmp;
00619                                do {
00620                                   tmp = tmp * 10 + (*p - '0');
00621                                   } while (--i && isDigit(*++p));
00622 
00623                                flags |= Flags.Fill;
00624                                width = tmp;
00625                                }
00626                             else
00627                                flags &= ~Flags.Zero;
00628 
00629 
00630                             if (*p == '.')
00631                                {
00632                                int tmp;
00633                                while (i-- && isDigit(*++p))
00634                                       tmp = tmp * 10 + (*p - '0');
00635 
00636                                flags |= Flags.Prec;
00637                                precision = tmp;
00638                                }
00639 
00640                             if (--i < 0)
00641                                 Integer.error ("Format.parse : missing format specifier");
00642 
00643                             tail = format [format.length-i..format.length];
00644                             return style = *p;
00645                             }
00646 
00647                 return style = 0;
00648         }
00649 }
00650 
00651 
00652 /******************************************************************************
00653 
00654         Functions for styled, readable, output. See Format.parse() for the 
00655         list of format specifiers.
00656 
00657 ******************************************************************************/
00658 
00659 alias FormatStructTemplate!(char) FormatStruct;
00660 
00661 
00662 /******************************************************************************
00663 
00664 ******************************************************************************/
00665 
00666 class FormatClassTemplate(T)
00667 {
00668         alias FormatStructTemplate!(T) Format;
00669 
00670         private T[128]          tmp;
00671         private Format          format;
00672 
00673         /**********************************************************************
00674 
00675         **********************************************************************/
00676 
00677         this (Format.Emitter sink, Format.Close close, Format.DblFormat df = null)
00678         {
00679                 format.ctor (sink, close, tmp, df);
00680         }
00681 
00682         /**********************************************************************
00683 
00684         **********************************************************************/
00685 
00686         int opCall (T[] fmt, ...)
00687         {
00688                 return format (fmt, _arguments, _argptr);
00689         }
00690 }
00691 
00692 
00693 alias FormatClassTemplate!(char) Format;

Generated on Sat Dec 24 17:28:32 2005 for Mango by  doxygen 1.4.0