Process Hacker
format.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * string formatting
4  *
5  * Copyright (C) 2010-2011 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /*
24  * This module provides a high-performance string formatting mechanism.
25  * Instead of using format strings, the user supplies an array of
26  * structures. This system is 2-5 times faster than printf-based functions.
27  *
28  * This file contains the public interfaces, while including the real
29  * formatting code from elsewhere. There are currently two functions:
30  * PhFormat, which returns a string object containing the formatted string,
31  * and PhFormatToBuffer, which writes the formatted string to a buffer. The
32  * latter is a bit faster due to the lack of resizing logic.
33  */
34 
35 #include <phbase.h>
36 #include <locale.h>
37 
38 extern ULONG PhMaxSizeUnit;
39 
40 #define SMALL_BUFFER_LENGTH (PH_OBJECT_SMALL_OBJECT_SIZE - FIELD_OFFSET(PH_STRING, Data) - sizeof(WCHAR))
41 #define BUFFER_SIZE 512
42 
43 #define PHP_FORMAT_NEGATIVE 0x1
44 #define PHP_FORMAT_POSITIVE 0x2
45 #define PHP_FORMAT_PAD 0x4
46 
47 // Internal CRT routines needed for floating-point conversion
48 
49 errno_t __cdecl _cfltcvt_l(double *arg, char *buffer, size_t sizeInBytes,
50  int format, int precision, int caps, _locale_t plocinfo);
51 
52 void __cdecl _cropzeros_l(char *_Buf, _locale_t _Locale);
53 void __cdecl _forcdecpt_l(char *_Buf, _locale_t _Locale);
54 
55 // Keep in sync with PhSizeUnitNames
56 static PH_STRINGREF PhpSizeUnitNamesCounted[7] =
57 {
58  PH_STRINGREF_INIT(L"B"),
59  PH_STRINGREF_INIT(L"kB"),
60  PH_STRINGREF_INIT(L"MB"),
61  PH_STRINGREF_INIT(L"GB"),
62  PH_STRINGREF_INIT(L"TB"),
63  PH_STRINGREF_INIT(L"PB"),
64  PH_STRINGREF_INIT(L"EB")
65 };
66 
67 static PH_INITONCE PhpFormatInitOnce = PH_INITONCE_INIT;
68 static WCHAR PhpFormatDecimalSeparator = '.';
69 static WCHAR PhpFormatThousandSeparator = ',';
70 static _locale_t PhpFormatUserLocale = NULL;
71 
73  _In_ PPH_STRING String,
74  _Inout_ PSIZE_T AllocatedLength,
75  _In_ SIZE_T UsedLength,
76  _In_ SIZE_T NeededLength
77  )
78 {
79  PPH_STRING newString;
80  SIZE_T allocatedLength;
81 
82  allocatedLength = *AllocatedLength;
83  allocatedLength *= 2;
84 
85  if (allocatedLength < UsedLength + NeededLength)
86  allocatedLength = UsedLength + NeededLength;
87 
88  newString = PhCreateStringEx(NULL, allocatedLength);
89  memcpy(newString->Buffer, String->Buffer, UsedLength);
90  PhDereferenceObject(String);
91 
92  *AllocatedLength = allocatedLength;
93 
94  return newString;
95 }
96 
106  _In_reads_(Count) PPH_FORMAT Format,
107  _In_ ULONG Count,
108  _In_opt_ SIZE_T InitialCapacity
109  )
110 {
111  PPH_STRING string;
112  SIZE_T allocatedLength;
113  PWSTR buffer;
114  SIZE_T usedLength;
115 
116  // Set up the buffer.
117 
118  // If the specified initial capacity is too small (or zero), use the
119  // largest buffer size which will still be eligible for allocation from
120  // the small object free list.
121  if (InitialCapacity < SMALL_BUFFER_LENGTH)
122  InitialCapacity = SMALL_BUFFER_LENGTH;
123 
124  string = PhCreateStringEx(NULL, InitialCapacity);
125  allocatedLength = InitialCapacity;
126  buffer = string->Buffer;
127  usedLength = 0;
128 
129 #undef ENSURE_BUFFER
130 #undef OK_BUFFER
131 #undef ADVANCE_BUFFER
132 
133 #define ENSURE_BUFFER(NeededLength) \
134  do { \
135  if (allocatedLength < usedLength + (NeededLength)) \
136  { \
137  string = PhpResizeFormatBuffer(string, &allocatedLength, usedLength, (NeededLength)); \
138  buffer = string->Buffer + usedLength / sizeof(WCHAR); \
139  } \
140  } while (0)
141 
142 #define OK_BUFFER (TRUE)
143 
144 #define ADVANCE_BUFFER(Length) \
145  do { buffer += (Length) / sizeof(WCHAR); usedLength += (Length); } while (0)
146 
147 #include "format_i.h"
148 
149  string->Length = usedLength;
150  // Null-terminate the string.
151  string->Buffer[usedLength / sizeof(WCHAR)] = 0;
152 
153  return string;
154 }
155 
176  _In_reads_(Count) PPH_FORMAT Format,
177  _In_ ULONG Count,
178  _Out_writes_bytes_opt_(BufferLength) PWSTR Buffer,
179  _In_opt_ SIZE_T BufferLength,
180  _Out_opt_ PSIZE_T ReturnLength
181  )
182 {
183  PWSTR buffer;
184  SIZE_T usedLength;
185  BOOLEAN overrun;
186 
187  buffer = Buffer;
188  usedLength = 0;
189  overrun = FALSE;
190 
191  // Make sure we don't try to write anything if we don't have a buffer.
192  if (!Buffer)
193  overrun = TRUE;
194 
195 #undef ENSURE_BUFFER
196 #undef OK_BUFFER
197 #undef ADVANCE_BUFFER
198 
199 #define ENSURE_BUFFER(NeededLength) \
200  do { \
201  if (!overrun && (BufferLength < usedLength + (NeededLength))) \
202  overrun = TRUE; \
203  } while (0)
204 
205 #define OK_BUFFER (!overrun)
206 
207 #define ADVANCE_BUFFER(Length) \
208  do { buffer += (Length) / sizeof(WCHAR); usedLength += (Length); } while (0)
209 
210 #include "format_i.h"
211 
212  // Write the null-terminator.
213  ENSURE_BUFFER(sizeof(WCHAR));
214  if (OK_BUFFER)
215  *buffer = 0;
216  else if (Buffer && BufferLength != 0) // try to null-terminate even if this function fails
217  *Buffer = 0;
218  ADVANCE_BUFFER(sizeof(WCHAR));
219 
220  if (ReturnLength)
221  *ReturnLength = usedLength;
222 
223  return OK_BUFFER;
224 }