00001 /******************************************************************************* 00002 00003 @file Integer.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.Integer; 00041 00042 private import mango.convert.Atoi; 00043 00044 /****************************************************************************** 00045 00046 A set of functions for converting between string and integer 00047 values. 00048 00049 ******************************************************************************/ 00050 00051 struct IntegerTemplate(T) 00052 { 00053 private alias AtoiTemplate!(T) Atoi; 00054 00055 enum Format {Binary='b', Octal='o', Hex='x', HexUpper='X', Integer='d', Unsigned='u'}; 00056 00057 /********************************************************************** 00058 00059 Declare stylistic flags 00060 00061 **********************************************************************/ 00062 00063 enum Flags 00064 { 00065 None = 0, // no flags 00066 Fill = 1, // do some kind of padding 00067 Left = Fill << 1, // left justify 00068 Prec = Left << 1, // precision was provided 00069 Hash = Prec << 1, // prefix integer with type 00070 Space = Hash << 1, // prefix with space 00071 Zero = Space << 1, // prefix integer with zero 00072 Sign = Zero << 1, // unused 00073 Comma = Sign << 1, // unused 00074 Plus = Comma << 1, // prefix decimal with '+' 00075 Array = Plus << 1, // array flag 00076 }; 00077 00078 00079 /*********************************************************************** 00080 00081 Format numeric values into the provided output buffer. The 00082 traditional printf() conversion specifiers are adhered to, 00083 and the following types are supported: 00084 00085 u - unsigned decimal 00086 d - signed decimal 00087 o - octal 00088 x - lowercase hexadecimal 00089 X - uppercase hexadecimal 00090 b - binary 00091 00092 Modifiers supported include: 00093 00094 # : prefix the conversion with a type identifier 00095 + : prefix positive decimals with a '+' 00096 space : prefix positive decimals with one space 00097 0 : left-pad the number with zeros 00098 00099 These modifiers are specifed via the 'flags' provided, 00100 and are represented via these identifiers: 00101 00102 # : Flags.Hash 00103 + : Flags.Plus 00104 space : Flags.Space 00105 0 : Flags.Zero 00106 00107 The provided 'dst' buffer should be sufficiently large 00108 enough to house the output. A 64-element array is often 00109 the maximum required (for a padded binary 64-bit string) 00110 00111 ***********************************************************************/ 00112 00113 final static T[] format (T[] dst, long i, Format fmt = Format.Integer, Flags flags = Flags.None) 00114 { 00115 T[] prefix; 00116 int len = dst.length; 00117 00118 // must have some buffer space to operate within! 00119 if (len) 00120 { 00121 uint radix; 00122 T[] numbers = "0123456789abcdef"; 00123 00124 // pre-conversion setup 00125 switch (fmt) 00126 { 00127 case Format.Integer: 00128 if (i < 0) 00129 { 00130 prefix = "-"; 00131 i = -i; 00132 } 00133 else 00134 if (flags & Flags.Space) 00135 prefix = " "; 00136 else 00137 if (flags & Flags.Plus) 00138 prefix = "+"; 00139 // fall through! 00140 case Format.Unsigned: 00141 radix = 10; 00142 break; 00143 00144 case Format.Binary: 00145 radix = 2; 00146 if (flags & Flags.Hash) 00147 prefix = "0b"; 00148 break; 00149 00150 case Format.Octal: 00151 radix = 8; 00152 if (flags & Flags.Hash) 00153 prefix = "0o"; 00154 break; 00155 00156 case Format.Hex: 00157 radix = 16; 00158 if (flags & Flags.Hash) 00159 prefix = "0x"; 00160 break; 00161 00162 case Format.HexUpper: 00163 radix = 16; 00164 numbers = "0123456789ABCDEF"; 00165 if (flags & Flags.Hash) 00166 prefix = "0X"; 00167 break; 00168 00169 default: 00170 // raw output; no formatting 00171 assert (fmt >= 2 && fmt <= 16); 00172 radix = fmt; 00173 break; 00174 } 00175 00176 // convert number to text 00177 T* p = dst.ptr + len; 00178 if (i <= uint.max) 00179 { 00180 uint v = cast (uint) i; 00181 do { 00182 *--p = numbers[v % radix]; 00183 } while ((v /= radix) && --len); 00184 } 00185 else 00186 { 00187 ulong v = cast (ulong) i; 00188 do { 00189 *--p = numbers[cast(uint) (v % radix)]; 00190 } while ((v /= radix) && --len); 00191 } 00192 } 00193 00194 // are we about to overflow? 00195 if (--len < 0 || 0 > (len -= prefix.length)) 00196 error ("Integer.format : output buffer too small"); 00197 00198 // prefix number with zeros? 00199 if (flags & Flags.Zero) 00200 { 00201 dst [prefix.length .. len + prefix.length] = '0'; 00202 len = 0; 00203 } 00204 00205 // write optional prefix string ... 00206 dst [len .. len + prefix.length] = prefix[]; 00207 00208 // return slice of provided output buffer 00209 return dst [len .. dst.length]; 00210 } 00211 00212 00213 /********************************************************************** 00214 00215 Parse an integer value from the provided 'src' string. 00216 The string is also inspected for a radix (defaults to 10), 00217 which can be overridden by setting 'radix' to non-zero. 00218 00219 A non-null 'ate' will return the number of characters used 00220 to construct the returned value. 00221 00222 **********************************************************************/ 00223 00224 final static long parse (T[] src, uint radix=0, uint* ate=null) 00225 { 00226 return Atoi.parse (src, radix, ate); 00227 } 00228 00229 00230 /********************************************************************** 00231 00232 Throw a format error. This is used by a number of other 00233 modules in this package 00234 00235 **********************************************************************/ 00236 00237 package final static void error (char[] msg) 00238 { 00239 static class FormatException : Exception 00240 { 00241 this (char[] msg) 00242 { 00243 super (msg); 00244 } 00245 } 00246 00247 throw new FormatException (msg); 00248 } 00249 } 00250 00251 00252 /****************************************************************************** 00253 00254 ******************************************************************************/ 00255 00256 alias IntegerTemplate!(char) Integer; 00257