- fixed a couple of bugs in ntdll environment functions (one in trace,
[wine] / dlls / ntdll / large_int.c
1 /*
2  * Large integer functions
3  *
4  * Copyright 2000 Alexandre Julliard
5  * Copyright 2003 Thomas Mertes
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "windef.h"
23 #include "winternl.h"
24
25 /*
26  * Note: we use LONGLONG instead of LARGE_INTEGER, because
27  * the latter is a structure and the calling convention for
28  * returning a structure would not be binary-compatible.
29  *
30  * FIXME: for platforms that don't have a native LONGLONG type,
31  * we should define LONGLONG as a structure similar to LARGE_INTEGER
32  * and do everything by hand. You are welcome to do it...
33  */
34
35 /******************************************************************************
36  *        RtlLargeIntegerAdd   (NTDLL.@)
37  */
38 LONGLONG WINAPI RtlLargeIntegerAdd( LONGLONG a, LONGLONG b )
39 {
40     return a + b;
41 }
42
43
44 /******************************************************************************
45  *        RtlLargeIntegerSubtract   (NTDLL.@)
46  */
47 LONGLONG WINAPI RtlLargeIntegerSubtract( LONGLONG a, LONGLONG b )
48 {
49     return a - b;
50 }
51
52
53 /******************************************************************************
54  *        RtlLargeIntegerNegate   (NTDLL.@)
55  */
56 LONGLONG WINAPI RtlLargeIntegerNegate( LONGLONG a )
57 {
58     return -a;
59 }
60
61
62 /******************************************************************************
63  *        RtlLargeIntegerShiftLeft   (NTDLL.@)
64  */
65 LONGLONG WINAPI RtlLargeIntegerShiftLeft( LONGLONG a, INT count )
66 {
67     return a << count;
68 }
69
70
71 /******************************************************************************
72  *        RtlLargeIntegerShiftRight   (NTDLL.@)
73  */
74 LONGLONG WINAPI RtlLargeIntegerShiftRight( LONGLONG a, INT count )
75 {
76     return (ULONGLONG)a >> count;
77 }
78
79
80 /******************************************************************************
81  *        RtlLargeIntegerArithmeticShift   (NTDLL.@)
82  */
83 LONGLONG WINAPI RtlLargeIntegerArithmeticShift( LONGLONG a, INT count )
84 {
85     /* FIXME: gcc does arithmetic shift here, but it may not be true on all platforms */
86     return a >> count;
87 }
88
89
90 /******************************************************************************
91  *        RtlLargeIntegerDivide   (NTDLL.@)
92  *
93  * FIXME: should it be signed division instead?
94  */
95 ULONGLONG WINAPI RtlLargeIntegerDivide( ULONGLONG a, ULONGLONG b, ULONGLONG *rem )
96 {
97     ULONGLONG ret = a / b;
98     if (rem) *rem = a - ret * b;
99     return ret;
100 }
101
102
103 /******************************************************************************
104  *        RtlConvertLongToLargeInteger   (NTDLL.@)
105  */
106 LONGLONG WINAPI RtlConvertLongToLargeInteger( LONG a )
107 {
108     return a;
109 }
110
111
112 /******************************************************************************
113  *        RtlConvertUlongToLargeInteger   (NTDLL.@)
114  */
115 ULONGLONG WINAPI RtlConvertUlongToLargeInteger( ULONG a )
116 {
117     return a;
118 }
119
120
121 /******************************************************************************
122  *        RtlEnlargedIntegerMultiply   (NTDLL.@)
123  */
124 LONGLONG WINAPI RtlEnlargedIntegerMultiply( INT a, INT b )
125 {
126     return (LONGLONG)a * b;
127 }
128
129
130 /******************************************************************************
131  *        RtlEnlargedUnsignedMultiply   (NTDLL.@)
132  */
133 ULONGLONG WINAPI RtlEnlargedUnsignedMultiply( UINT a, UINT b )
134 {
135     return (ULONGLONG)a * b;
136 }
137
138
139 /******************************************************************************
140  *        RtlEnlargedUnsignedDivide   (NTDLL.@)
141  */
142 UINT WINAPI RtlEnlargedUnsignedDivide( ULONGLONG a, UINT b, UINT *remptr )
143 {
144 #if defined(__i386__) && defined(__GNUC__)
145     UINT ret, rem, p1, p2;
146
147     p1 = a >> 32;
148     p2 = a &  0xffffffffLL;
149
150     __asm__("div %4,%%eax"
151             : "=a" (ret), "=d" (rem)
152             : "0" (p2), "1" (p1), "g" (b) );
153     if (remptr) *remptr = rem;
154     return ret;
155 #else
156     UINT ret = a / b;
157     if (remptr) *remptr = a % b;
158     return ret;
159 #endif
160 }
161
162
163 /******************************************************************************
164  *        RtlExtendedLargeIntegerDivide   (NTDLL.@)
165  */
166 LONGLONG WINAPI RtlExtendedLargeIntegerDivide( LONGLONG a, INT b, INT *rem )
167 {
168     LONGLONG ret = a / b;
169     if (rem) *rem = a - b * ret;
170     return ret;
171 }
172
173
174 /******************************************************************************
175  *        RtlExtendedIntegerMultiply   (NTDLL.@)
176  */
177 LONGLONG WINAPI RtlExtendedIntegerMultiply( LONGLONG a, INT b )
178 {
179     return a * b;
180 }
181
182
183 /******************************************************************************
184  *        RtlExtendedMagicDivide   (NTDLL.@)
185  *
186  * Allows replacing a division by a longlong constant with a multiplication by
187  * the inverse constant.
188  *
189  * RETURNS
190  *  (dividend * inverse_divisor) >> (64 + shift)
191  *
192  * NOTES
193  *  If the divisor of a division is constant, the constants inverse_divisor and
194  *  shift must be chosen such that inverse_divisor = 2^(64 + shift) / divisor.
195  *  Then we have RtlExtendedMagicDivide(dividend,inverse_divisor,shift) ==
196  *  dividend * inverse_divisor / 2^(64 + shift) == dividend / divisor.
197  *
198  *  The Parameter inverse_divisor although defined as LONGLONG is used as
199  *  ULONGLONG.
200  */
201 #define LOWER_32(A) ((A) & 0xffffffff)
202 #define UPPER_32(A) ((A) >> 32)
203 LONGLONG WINAPI RtlExtendedMagicDivide(
204     LONGLONG dividend,        /* [I] Dividend to be divided by the constant divisor */
205     LONGLONG inverse_divisor, /* [I] Constant computed manually as 2^(64+shift) / divisor */
206     INT shift)                /* [I] Constant shift chosen to make inverse_divisor as big as possible for 64 bits */
207 {
208     ULONGLONG dividend_high;
209     ULONGLONG dividend_low;
210     ULONGLONG inverse_divisor_high;
211     ULONGLONG inverse_divisor_low;
212     ULONGLONG ah_bl;
213     ULONGLONG al_bh;
214     LONGLONG result;
215     int positive;
216
217     if (dividend < 0) {
218         dividend_high = UPPER_32((ULONGLONG) -dividend);
219         dividend_low =  LOWER_32((ULONGLONG) -dividend);
220         positive = 0;
221     } else {
222         dividend_high = UPPER_32((ULONGLONG) dividend);
223         dividend_low =  LOWER_32((ULONGLONG) dividend);
224         positive = 1;
225     } /* if */
226     inverse_divisor_high = UPPER_32((ULONGLONG) inverse_divisor);
227     inverse_divisor_low =  LOWER_32((ULONGLONG) inverse_divisor);
228
229     ah_bl = dividend_high * inverse_divisor_low;
230     al_bh = dividend_low * inverse_divisor_high;
231
232     result = (LONGLONG) ((dividend_high * inverse_divisor_high +
233             UPPER_32(ah_bl) +
234             UPPER_32(al_bh) +
235             UPPER_32(LOWER_32(ah_bl) + LOWER_32(al_bh) +
236                      UPPER_32(dividend_low * inverse_divisor_low))) >> shift);
237
238     if (positive) {
239         return result;
240     } else {
241         return -result;
242     } /* if */
243 }
244
245
246 /******************************************************************************
247  *      RtlLargeIntegerToChar   [NTDLL.@]
248  *
249  * Convert an unsigned large integer to a character string.
250  *
251  * RETURNS
252  *  Success: STATUS_SUCCESS. str contains the converted number
253  *  Failure: STATUS_INVALID_PARAMETER, if base is not 0, 2, 8, 10 or 16.
254  *           STATUS_BUFFER_OVERFLOW, if str would be larger than length.
255  *           STATUS_ACCESS_VIOLATION, if str is NULL.
256  *
257  * NOTES
258  *  Instead of base 0 it uses 10 as base.
259  *  Writes at most length characters to the string str.
260  *  Str is '\0' terminated when length allowes it.
261  *  When str fits exactly in length characters the '\0' is ommitted.
262  *  If value_ptr is NULL it crashes, as the native function does.
263  *
264  * DIFFERENCES
265  * - Accept base 0 as 10 instead of crashing as native function does.
266  * - The native function does produce garbage or STATUS_BUFFER_OVERFLOW for
267  *   base 2, 8 and 16 when the value is larger than 0xFFFFFFFF. 
268  */
269 NTSTATUS WINAPI RtlLargeIntegerToChar(
270     const ULONGLONG *value_ptr, /* [I] Pointer to the value to be converted */
271     ULONG base,                 /* [I] Number base for conversion (allowed 0, 2, 8, 10 or 16) */
272     ULONG length,               /* [I] Length of the str buffer in bytes */
273     PCHAR str)                  /* [O] Destination for the converted value */
274 {
275     ULONGLONG value = *value_ptr;
276     CHAR buffer[65];
277     PCHAR pos;
278     CHAR digit;
279     ULONG len;
280
281     if (base == 0) {
282         base = 10;
283     } else if (base != 2 && base != 8 && base != 10 && base != 16) {
284         return STATUS_INVALID_PARAMETER;
285     } /* if */
286
287     pos = &buffer[64];
288     *pos = '\0';
289
290     do {
291         pos--;
292         digit = value % base;
293         value = value / base;
294         if (digit < 10) {
295             *pos = '0' + digit;
296         } else {
297             *pos = 'A' + digit - 10;
298         } /* if */
299     } while (value != 0L);
300
301     len = &buffer[64] - pos;
302     if (len > length) {
303         return STATUS_BUFFER_OVERFLOW;
304     } else if (str == NULL) {
305         return STATUS_ACCESS_VIOLATION;
306     } else if (len == length) {
307         memcpy(str, pos, len);
308     } else {
309         memcpy(str, pos, len + 1);
310     } /* if */
311     return STATUS_SUCCESS;
312 }
313
314
315 /**************************************************************************
316  *      RtlInt64ToUnicodeString (NTDLL.@)
317  *
318  * Convert a large unsigned integer to a '\0' terminated unicode string.
319  *
320  * RETURNS
321  *  Success: STATUS_SUCCESS. str contains the converted number
322  *  Failure: STATUS_INVALID_PARAMETER, if base is not 0, 2, 8, 10 or 16.
323  *           STATUS_BUFFER_OVERFLOW, if str is too small to hold the string
324  *                  (with the '\0' termination). In this case str->Length
325  *                  is set to the length, the string would have (which can
326  *                  be larger than the MaximumLength).
327  *
328  * NOTES
329  *  Instead of base 0 it uses 10 as base.
330  *  If str is NULL it crashes, as the native function does.
331  *
332  * DIFFERENCES
333  * - Accept base 0 as 10 instead of crashing as native function does.
334  * - Do not return STATUS_BUFFER_OVERFLOW when the string is long enough.
335  *   The native function does this when the string would be longer than 31
336  *   characters even when the string parameter is long enough.
337  * - The native function does produce garbage or STATUS_BUFFER_OVERFLOW for
338  *   base 2, 8 and 16 when the value is larger than 0xFFFFFFFF. 
339  */
340 NTSTATUS WINAPI RtlInt64ToUnicodeString(
341     ULONGLONG value,     /* [I] Value to be converted */
342     ULONG base,          /* [I] Number base for conversion (allowed 0, 2, 8, 10 or 16) */
343     UNICODE_STRING *str) /* [O] Destination for the converted value */
344 {
345     WCHAR buffer[65];
346     PWCHAR pos;
347     WCHAR digit;
348
349     if (base == 0) {
350         base = 10;
351     } else if (base != 2 && base != 8 && base != 10 && base != 16) {
352         return STATUS_INVALID_PARAMETER;
353     } /* if */
354
355     pos = &buffer[64];
356     *pos = '\0';
357
358     do {
359         pos--;
360         digit = value % base;
361         value = value / base;
362         if (digit < 10) {
363             *pos = '0' + digit;
364         } else {
365             *pos = 'A' + digit - 10;
366         } /* if */
367     } while (value != 0L);
368
369     str->Length = (&buffer[64] - pos) * sizeof(WCHAR);
370     if (str->Length >= str->MaximumLength) {
371         return STATUS_BUFFER_OVERFLOW;
372     } else {
373         memcpy(str->Buffer, pos, str->Length + sizeof(WCHAR));
374     } /* if */
375     return STATUS_SUCCESS;
376 }
377
378
379 /******************************************************************************
380  *        _alldiv   (NTDLL.@)
381  */
382 LONGLONG WINAPI _alldiv( LONGLONG a, LONGLONG b )
383 {
384     return a / b;
385 }
386
387
388 /******************************************************************************
389  *        _allmul   (NTDLL.@)
390  */
391 LONGLONG WINAPI _allmul( LONGLONG a, LONGLONG b )
392 {
393     return a * b;
394 }
395
396
397 /******************************************************************************
398  *        _allrem   (NTDLL.@)
399  */
400 LONGLONG WINAPI _allrem( LONGLONG a, LONGLONG b )
401 {
402     return a % b;
403 }
404
405
406 /******************************************************************************
407  *        _aulldiv   (NTDLL.@)
408  */
409 ULONGLONG WINAPI _aulldiv( ULONGLONG a, ULONGLONG b )
410 {
411     return a / b;
412 }
413
414
415 /******************************************************************************
416  *        _aullrem   (NTDLL.@)
417  */
418 ULONGLONG WINAPI _aullrem( ULONGLONG a, ULONGLONG b )
419 {
420     return a % b;
421 }