blob: 622a4aba4ca2f31888386f2c820a4d41812ecde2 [file] [log] [blame]
/* CFBigNumber.c
Copyright (c) 2012-2016, Apple Inc. and the Swift project authors
Portions Copyright (c) 2014-2016 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
Responsibility: Christopher Kane
Original author: Zhi Feng Huang
*/
#include <CoreFoundation/CFBigNumber.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "CFInternal.h"
typedef struct {
int64_t high;
uint64_t low;
} CFSInt128Struct;
#define kCFNumberSInt128Type 17
CF_EXPORT CFNumberType _CFNumberGetType2(CFNumberRef number);
#if __LP64__
#ifndef _INT128_T
#define _INT128_T
typedef __int128_t int128_t;
#endif
#ifndef _UINT128_T
#define _UINT128_T
typedef __uint128_t uint128_t;
#endif
#ifndef INT128_MIN
#define INT128_MIN ((__int128_t)0 - ((__int128_t)1 << 126) - ((__int128_t)1 << 126))
#endif
#ifndef INT128_MAX
#define INT128_MAX ((__int128_t)-1 + ((__int128_t)1 << 126) + ((__int128_t)1 << 126))
#endif
#ifndef UINT128_MAX
#define UINT128_MAX (((__uint128_t)1 << 127) - (__uint128_t)1 + ((__uint128_t)1 << 127))
#endif
#endif
#define BIG_DIGITS_LIMIT 1000000000L
#define BIG_DIGITS_LIMIT_2 ((uint64_t)BIG_DIGITS_LIMIT * (uint64_t)BIG_DIGITS_LIMIT)
#define BIG_DIGITS_LIMIT_3 ((__uint128_t)BIG_DIGITS_LIMIT_2 * (__uint128_t)BIG_DIGITS_LIMIT)
#define BIG_DIGITS_LIMIT_4 ((__uint128_t)BIG_DIGITS_LIMIT_3 * (__uint128_t)BIG_DIGITS_LIMIT)
#define GET_FIFTH_DIGIT(A) (A / BIG_DIGITS_LIMIT_4)
#define GET_FOURTH_DIGIT(A) (A / BIG_DIGITS_LIMIT_3)
#define GET_THIRD_DIGIT(A) (A / BIG_DIGITS_LIMIT_2)
#define GET_SECOND_DIGIT(A) (A / BIG_DIGITS_LIMIT)
#define GET_REMAINDER_FIFTH_DIGIT(A,B) (A - B * BIG_DIGITS_LIMIT_4)
#define GET_REMAINDER_FOURTH_DIGIT(A,B) (A - B * BIG_DIGITS_LIMIT_3)
#define GET_REMAINDER_THIRD_DIGIT(A,B) (A - B * BIG_DIGITS_LIMIT_2)
#define GET_REMAINDER_SECOND_DIGIT(A,B) (A - B * BIG_DIGITS_LIMIT)
void _CFBigNumInitWithInt8(_CFBigNum *r, int8_t inNum) {
memset(r, 0, sizeof(*r));
uint8_t unsignInNum = inNum;
if (inNum < 0) {
r->sign = -1;
unsignInNum = -1 * inNum;
}
r->digits[0] = unsignInNum;
}
void _CFBigNumInitWithInt16(_CFBigNum *r, int16_t inNum) {
memset(r, 0, sizeof(*r));
uint16_t unsignInNum = inNum;
if (inNum < 0) {
r->sign = -1;
unsignInNum = -1 * inNum;
}
r->digits[0] = unsignInNum;
}
void _CFBigNumInitWithInt32(_CFBigNum *r, int32_t inNum) {
memset(r, 0, sizeof(*r));
uint32_t unsignInNum = inNum;
if (inNum < 0) {
r->sign = -1;
unsignInNum = -1 * inNum;
}
uint32_t dig0 = GET_SECOND_DIGIT(unsignInNum);
r->digits[0] = GET_REMAINDER_SECOND_DIGIT(unsignInNum, dig0);
r->digits[1] = dig0;
}
void _CFBigNumInitWithInt64(_CFBigNum *r, int64_t inNum) {
memset(r, 0, sizeof(*r));
uint64_t unsignInNum = inNum;
if (inNum < 0) {
r->sign = -1;
unsignInNum = -1 * inNum;
}
uint32_t dig2 = GET_THIRD_DIGIT(unsignInNum);
unsignInNum = GET_REMAINDER_THIRD_DIGIT(unsignInNum, (uint64_t)dig2);
uint32_t dig1 = GET_SECOND_DIGIT(unsignInNum);
r->digits[0] = GET_REMAINDER_SECOND_DIGIT(unsignInNum, (uint64_t)dig1);
r->digits[1] = dig1;
r->digits[2] = dig2;
}
#ifdef __LP64__
void _CFBigNumInitWithInt128(_CFBigNum *r, __int128_t inNum) {
memset(r, 0, sizeof(*r));
__uint128_t unsignInNum = inNum;
if (inNum < 0) {
r->sign = -1;
unsignInNum = -1 * inNum;
}
uint32_t dig4 = GET_FIFTH_DIGIT(unsignInNum);
unsignInNum = GET_REMAINDER_FIFTH_DIGIT(unsignInNum, (__uint128_t)dig4);
uint32_t dig3 = GET_FOURTH_DIGIT(unsignInNum);
unsignInNum = GET_REMAINDER_FOURTH_DIGIT(unsignInNum, (__uint128_t)dig3);
uint32_t dig2 = GET_THIRD_DIGIT(unsignInNum);
unsignInNum = GET_REMAINDER_THIRD_DIGIT(unsignInNum, (__uint128_t)dig2);
uint32_t dig1 = GET_SECOND_DIGIT(unsignInNum);
r->digits[0] = GET_REMAINDER_SECOND_DIGIT(unsignInNum, (__uint128_t)dig1);
r->digits[1] = dig1;
r->digits[2] = dig2;
r->digits[3] = dig3;
r->digits[4] = dig4;
}
#endif
void _CFBigNumInitWithUInt8(_CFBigNum *r, uint8_t inNum) {
memset(r, 0, sizeof(*r));
uint8_t unsignInNum = inNum;
r->digits[0] = unsignInNum;
}
void _CFBigNumInitWithUInt16(_CFBigNum *r, uint16_t inNum) {
memset(r, 0, sizeof(*r));
uint16_t unsignInNum = inNum;
r->digits[0] = unsignInNum;
}
void _CFBigNumInitWithUInt32(_CFBigNum *r, uint32_t inNum) {
memset(r, 0, sizeof(*r));
uint32_t unsignInNum = inNum;
uint32_t dig0 = GET_SECOND_DIGIT(unsignInNum);
r->digits[0] = GET_REMAINDER_SECOND_DIGIT(unsignInNum, dig0);
r->digits[1] = dig0;
}
void _CFBigNumInitWithUInt64(_CFBigNum *r, uint64_t inNum) {
memset(r, 0, sizeof(*r));
uint64_t unsignInNum = inNum;
uint32_t dig2 = GET_THIRD_DIGIT(unsignInNum);
unsignInNum = GET_REMAINDER_THIRD_DIGIT(unsignInNum, (uint64_t)dig2);
uint32_t dig1 = GET_SECOND_DIGIT(unsignInNum);
r->digits[0] = GET_REMAINDER_SECOND_DIGIT(unsignInNum, (uint64_t)dig1);
r->digits[1] = dig1;
r->digits[2] = dig2;
}
#ifdef __LP64__
void _CFBigNumInitWithUInt128(_CFBigNum *r, __uint128_t inNum) {
memset(r, 0, sizeof(*r));
__uint128_t unsignInNum = inNum;
uint32_t dig4 = GET_FIFTH_DIGIT(unsignInNum);
unsignInNum = GET_REMAINDER_FIFTH_DIGIT(unsignInNum, (__uint128_t)dig4);
uint32_t dig3 = GET_FOURTH_DIGIT(unsignInNum);
unsignInNum = GET_REMAINDER_FOURTH_DIGIT(unsignInNum, (__uint128_t)dig3);
uint32_t dig2 = GET_THIRD_DIGIT(unsignInNum);
unsignInNum = GET_REMAINDER_THIRD_DIGIT(unsignInNum, (__uint128_t)dig2);
uint32_t dig1 = GET_SECOND_DIGIT(unsignInNum);
r->digits[0] = GET_REMAINDER_SECOND_DIGIT(unsignInNum, (__uint128_t)dig1);
r->digits[1] = dig1;
r->digits[2] = dig2;
r->digits[3] = dig3;
r->digits[4] = dig4;
}
#endif
int8_t _CFBigNumGetInt8(const _CFBigNum *num) {
int8_t result = num->digits[0];
if (num->sign < 0) {
result = -1 * result;
}
return result;
}
int16_t _CFBigNumGetInt16(const _CFBigNum *num) {
int16_t result = num->digits[0];
if (num->sign < 0) {
result = -1 * result;
}
return result;
}
int32_t _CFBigNumGetInt32(const _CFBigNum *num) {
int32_t result = num->digits[0];
result += num->digits[1] * BIG_DIGITS_LIMIT;
if (num->sign < 0) {
result = -1 * result;
}
return result;
}
int64_t _CFBigNumGetInt64(const _CFBigNum *num) {
int64_t result = num->digits[0];
result += (int64_t)num->digits[1] * BIG_DIGITS_LIMIT;
result += (int64_t)num->digits[2] * BIG_DIGITS_LIMIT_2;
if (num->sign < 0) {
result = -1 * result;
}
return result;
}
#if __LP64__
__int128_t _CFBigNumGetInt128(const _CFBigNum *num) {
__int128_t result = num->digits[0];
result += (__int128_t)num->digits[1] * BIG_DIGITS_LIMIT;
result += (__int128_t)num->digits[2] * BIG_DIGITS_LIMIT_2;
result += (__int128_t)num->digits[3] * BIG_DIGITS_LIMIT_3;
result += (__int128_t)num->digits[4] * BIG_DIGITS_LIMIT_4;
if (num->sign < 0) {
result = -1 * result;
}
return result;
}
#endif
uint8_t _CFBigNumGetUInt8(const _CFBigNum *num) {
uint8_t result = num->digits[0];
return result;
}
uint16_t _CFBigNumGetUInt16(const _CFBigNum *num) {
uint16_t result = num->digits[0];
return result;
}
uint32_t _CFBigNumGetUInt32(const _CFBigNum *num) {
uint32_t result = num->digits[0];
result += num->digits[1] * BIG_DIGITS_LIMIT;
return result;
}
uint64_t _CFBigNumGetUInt64(const _CFBigNum *num) {
uint64_t result = num->digits[0];
result += (uint64_t)num->digits[1] * BIG_DIGITS_LIMIT;
result += (uint64_t)num->digits[2] * BIG_DIGITS_LIMIT_2;
return result;
}
#if __LP64__
__uint128_t _CFBigNumGetUInt128(const _CFBigNum *num) {
__uint128_t result = num->digits[0];
result += (__uint128_t)num->digits[1] * BIG_DIGITS_LIMIT;
result += (__uint128_t)num->digits[2] * BIG_DIGITS_LIMIT_2;
result += (__uint128_t)num->digits[3] * BIG_DIGITS_LIMIT_3;
result += (__uint128_t)num->digits[4] * BIG_DIGITS_LIMIT_4;
return result;
}
#endif
void _CFBigNumInitWithCFNumber(_CFBigNum *r, CFNumberRef inNum) {
uint8_t bytes[128 + 16];
memset(bytes, 0, sizeof(bytes));
// round bytes up to next multiple of 16; compiler attributes won't always guarantee big alignment
void *bytesa = (uint8_t *)(((uintptr_t)bytes / 16) * 16 + 16);
CFNumberType type = _CFNumberGetType2(inNum);
CFNumberGetValue(inNum, type, bytesa);
_CFBigNumInitWithBytes(r, bytesa, type);
}
void _CFBigNumInitWithBytes(_CFBigNum *r, const void *bytes, CFNumberType type) {
switch (type) {
case kCFNumberSInt8Type:
_CFBigNumInitWithInt8(r, *(int8_t *)bytes);
return;
case kCFNumberSInt16Type:
_CFBigNumInitWithInt16(r, *(int16_t *)bytes);
return;
case kCFNumberSInt32Type:
_CFBigNumInitWithInt32(r, *(int32_t *)bytes);
return;
case kCFNumberSInt64Type:
_CFBigNumInitWithInt64(r, *(int64_t *)bytes);
return;
case kCFNumberCharType:
_CFBigNumInitWithInt8(r, *(int8_t *)bytes);
return;
case kCFNumberShortType:
_CFBigNumInitWithInt16(r, *(int16_t *)bytes);
return;
case kCFNumberIntType:
_CFBigNumInitWithInt32(r, *(int32_t *)bytes);
return;
case kCFNumberLongType:
if (sizeof(long) == 8) {
_CFBigNumInitWithInt64(r, *(int64_t *)bytes);
} else {
_CFBigNumInitWithInt32(r, *(int32_t *)bytes);
}
return;
case kCFNumberLongLongType:
_CFBigNumInitWithInt64(r, *(int64_t *)bytes);
return;
case kCFNumberCFIndexType:
if (sizeof(CFIndex) == 8) {
_CFBigNumInitWithInt64(r, *(int64_t *)bytes);
} else {
_CFBigNumInitWithInt32(r, *(int32_t *)bytes);
}
return;
case kCFNumberNSIntegerType:
if (sizeof(long) == 8) { // NSInteger follows long
_CFBigNumInitWithInt64(r, *(int64_t *)bytes);
} else {
_CFBigNumInitWithInt32(r, *(int32_t *)bytes);
}
return;
#if __LP64__
case kCFNumberSInt128Type: {
CFSInt128Struct s;
memmove(&s, bytes, sizeof(CFSInt128Struct)); // the hard way because bytes might not be aligned
__int128_t val = (__int128_t)s.low + ((__int128_t)s.high << 64);
_CFBigNumInitWithInt128(r, val);
return;
}
#endif
default:
return;
}
}
CFNumberRef _CFNumberCreateWithBigNum(const _CFBigNum *input) {
if (0 == input->digits[4] && 0 == input->digits[3] && 0 == input->digits[2] && 0 == input->digits[1]) {
// This bumps up the size of the most negative n-bit value to the next larger size; oh well
if (input->digits[0] <= INT8_MAX) {
int8_t num = _CFBigNumGetInt8(input);
CFNumberRef result = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt8Type, (const void *)&num);
return result;
} else if (input->digits[0] <= INT16_MAX) {
int16_t num = _CFBigNumGetInt16(input);
CFNumberRef result = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt16Type, (const void *)&num);
return result;
}
}
_CFBigNum maxlimit, minlimit;
if (0 == input->digits[4] && 0 == input->digits[3] && 0 == input->digits[2]) {
_CFBigNumInitWithInt32(&maxlimit, INT32_MAX);
_CFBigNumInitWithInt32(&minlimit, INT32_MIN);
CFComparisonResult cr = _CFBigNumCompare(input, &maxlimit);
CFComparisonResult crn = _CFBigNumCompare(input, &minlimit);
if ((kCFCompareLessThan == cr || kCFCompareEqualTo == cr) && (kCFCompareLessThan != crn)) {
int32_t num = _CFBigNumGetInt32(input);
CFNumberRef result = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt32Type, (const void *)&num);
return result;
}
}
if (0 == input->digits[4] && 0 == input->digits[3]) {
_CFBigNumInitWithInt64(&maxlimit, INT64_MAX);
_CFBigNumInitWithInt64(&minlimit, INT64_MIN);
CFComparisonResult cr = _CFBigNumCompare(input, &maxlimit);
CFComparisonResult crn = _CFBigNumCompare(input, &minlimit);
if ((kCFCompareLessThan == cr || kCFCompareEqualTo == cr) && (kCFCompareLessThan != crn)) {
int64_t num = _CFBigNumGetInt64(input);
CFNumberRef result = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt64Type, (const void *)&num);
return result;
}
}
#if __LP64__
_CFBigNumInitWithInt128(&maxlimit, INT128_MAX);
_CFBigNumInitWithInt128(&minlimit, INT128_MIN);
CFComparisonResult cr = _CFBigNumCompare(input, &maxlimit);
CFComparisonResult crn = _CFBigNumCompare(input, &minlimit);
if ((kCFCompareLessThan == cr || kCFCompareEqualTo == cr) && (kCFCompareLessThan != crn)) {
__int128_t num = _CFBigNumGetInt128(input);
CFNumberRef result = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt128Type, (const void *)&num);
return result;
}
#endif
return NULL;
}
CFComparisonResult _CFBigNumCompare(const _CFBigNum *a, const _CFBigNum *b) {
Boolean sameSign = a->sign == b->sign;
if (sameSign) {
Boolean negative = a->sign < 0;
for (CFIndex i = sizeof(a->digits) / sizeof(a->digits[0]); i--;) {
if (a->digits[i] < b->digits[i]) {
return !negative ? kCFCompareLessThan : kCFCompareGreaterThan;
}
if (a->digits[i] > b->digits[i]) {
return negative ? kCFCompareLessThan : kCFCompareGreaterThan;
}
}
return kCFCompareEqualTo;
}
return (a->sign < b->sign) ? kCFCompareLessThan : kCFCompareGreaterThan;
}
// abs(a) < abs(b)
// do not care about the sign
static Boolean _CFBigNumAbsoluteLessThan(const _CFBigNum *a, const _CFBigNum *b) {
for (CFIndex i = sizeof(a->digits) / sizeof(a->digits[0]); i--;) {
if (a->digits[i] < b->digits[i]) {
return true;
}
if (a->digits[i] > b->digits[i]) {
return false;
}
}
return false;
}
// r = a * -1
void _CFBigNumNeg(_CFBigNum *r, const _CFBigNum *a) {
memmove(r, a, sizeof(*a));
Boolean aIsZero = true;
for (CFIndex i = 0; i < sizeof(a->digits) / sizeof(a->digits[0]); i++) {
if (a->digits[i] != 0) {
aIsZero = false;
break;
}
}
// if a is zero, we do not flip the sign
if (!aIsZero) {
// 0 -> -1
// -1 -> 0
r->sign = r->sign * r->sign - 1;
}
}
uint8_t _CFBigNumAdd(_CFBigNum *r, const _CFBigNum *a, const _CFBigNum *b) {
uint8_t carry = 0;
Boolean sameSign = a->sign == b->sign;
if (sameSign) {
for (CFIndex i = 0; i < sizeof(a->digits) / sizeof(a->digits[0]); i++) {
uint32_t result = a->digits[i] + b->digits[i] + carry;
if (result > BIG_DIGITS_LIMIT) {
carry = 1;
result -= BIG_DIGITS_LIMIT;
} else {
carry = 0;
}
r->digits[i] = result;
}
r->sign = a->sign;
return carry;
} else {
// the algorithm here is to find the larger one and do the subtraction and then neg the result if necessary
const _CFBigNum *bigNum = NULL;
const _CFBigNum *smallNum = NULL;
if (_CFBigNumAbsoluteLessThan(a, b)) {
bigNum = b;
smallNum = a;
} else {
bigNum = a;
smallNum = b;
}
for (int i = 0; i < sizeof(a->digits) / sizeof(a->digits[0]); i++) {
int64_t result = (int64_t)bigNum->digits[i] - (int64_t)smallNum->digits[i] - carry;
if (result < 0) {
carry = 1;
result += BIG_DIGITS_LIMIT;
} else {
carry = 0;
}
r->digits[i] = result;
}
if (bigNum->sign < 0) {
r->sign = -1;
} else {
r->sign = 0;
}
return carry;
}
}
uint8_t _CFBigNumSub(_CFBigNum *r, const _CFBigNum *a, const _CFBigNum *b) {
_CFBigNum nb;
_CFBigNumNeg(&nb, b);
return _CFBigNumAdd(r, a, &nb);
}
void _CFBigNumToCString(const _CFBigNum *vp, Boolean leading_zeros, Boolean leading_plus, char *buffer, size_t buflen) {
if (vp->sign < 0) {
*buffer++ = '-';
buflen--;
} else if (leading_plus) {
*buffer++ = '+';
buflen--;
}
char tmp[46];
snprintf(tmp, sizeof(tmp), "%09u%09u%09u%09u%09u", vp->digits[4], vp->digits[3], vp->digits[2], vp->digits[1], vp->digits[0]);
if (leading_zeros) {
memset(buffer, '0', buflen);
uint32_t tocopy = __CFMin(sizeof(tmp), buflen);
memmove(buffer + buflen - tocopy, tmp + sizeof(tmp) - tocopy, tocopy); // copies trailing nul from tmp to nul-terminate
} else {
char *s = tmp;
while (*s == '0') s++;
if (*s == 0) s--; // if tmp is all zeros, copy out at least one zero
strlcpy(buffer, s, buflen);
}
}
void _CFBigNumFromCString(_CFBigNum *r, const char *string) {
memset(r, 0, sizeof(*r));
char *copy = (char *)calloc(strlen(string)+1, sizeof(char));
memcpy(copy, string, strlen(string)+1);
char *working = copy;
if (*working == '-') {
r->sign = -1;
working++;
} else if (*working == '+') {
working++;
}
while (*working == '0') {
working++;
}
size_t length = strlen(working);
if (length == 0) { // the number is zero
free(copy);
return;
}
int curDigit = 0;
while (curDigit + 1 < sizeof(r->digits) / sizeof(r->digits[0]) && 9 < length) {
uint32_t digit = atol(working+length-9);
r->digits[curDigit] = digit;
*(working+length-9) = 0;
length -= 9;
curDigit++;
}
uint32_t digit = atol(working);
r->digits[curDigit] = digit;
free(copy);
}
char *_CFBigNumCopyDescription(const _CFBigNum *num) {
char *result = (char *)calloc(1024, sizeof(char));
sprintf(result, "sign:%s 1st:%u 2nd:%u 3rd:%u 4th:%u 5th:%u", num->sign < 0 ? "-" : "+", num->digits[0], num->digits[1], num->digits[2], num->digits[3], num->digits[4]);
return result;
}