Skip to content

Commit

Permalink
Simplified numberHelpers.c and made it more resilient
Browse files Browse the repository at this point in the history
  • Loading branch information
Patrik Sletmo authored and TamtamHero committed Apr 29, 2020
1 parent 876226c commit 71eb5dc
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 157 deletions.
12 changes: 10 additions & 2 deletions src/xrp/format/amount.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,23 @@ void formatIssuedCurrency(field_t* field, char *dst) {
formatCurrency(&field->data[8], dst, true);

uint16_t textPos = strlen(dst);
dst[textPos++] = ' ';
if (textPos > 0) {
// Only add space if a currency was printed
dst[textPos++] = ' ';
}

if (value << 1u == 0) {
// Special case for the value zero
dst[textPos] = '0';
return;
}

if (mantissa < 1000000000000000 || mantissa > 9999999999999999) {
if (exponent < EXP_MIN || exponent > EXP_MAX) {
SNPRINTF(dst, "Invalid exponent!");
return;
}

if (mantissa < MANTISSA_MIN || mantissa > MANTISSA_MAX) {
SNPRINTF(dst, "Invalid mantissa!");
return;
}
Expand Down
194 changes: 40 additions & 154 deletions src/xrp/parse/numberHelpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,181 +15,67 @@
* limitations under the License.
********************************************************************************/

#include <assert.h>
#include "numberHelpers.h"
#include "os.h"
#include "string.h"

typedef enum {
NUM_INTEGER, // 1000
NUM_SEMI_DECIMAL, // 1000.5
NUM_DECIMAL, // 0.123
NUM_EXTREME // 1.34e40
} presentationType_e;
void normalize(uint64_t *mantissaParam, int16_t *exponentParam);

void formatNumberData(char* dst, uint16_t len, int16_t exponent, uint64_t mantissa);
void removeTrailingZeros(char* dst, int16_t *exponentParam);
presentationType_e determineFormat(char *dst, int16_t exponent);

void parseInteger(char *dst, uint16_t len, int16_t *exponentParam);
void parseSemiDecimal(char *dst, uint16_t len, int16_t *exponentParam);
void parseDecimal(char *dst, uint16_t len, int16_t *exponentParam);
void parseExtreme(char *dst, uint16_t len, int16_t *exponentParam);


void parseDecimalNumber(char* dst, uint16_t len, uint8_t sign, int16_t exponent, uint64_t mantissa) {
uint16_t textPos = 0;
void parseDecimalNumber(char* dst, uint16_t maxLen, uint8_t sign, int16_t exponent, uint64_t mantissa) {
assert(maxLen >= 100);
assert(exponent >= EXP_MIN);
assert(exponent <= EXP_MAX);
assert(mantissa >= MANTISSA_MIN);
assert(mantissa <= MANTISSA_MAX);

// 0. Abort early if number matches special case for zero
if (sign == 0 && exponent == 0 && mantissa == 0) {
// Special case: Value is zero
dst[textPos] = '0';
} else {
if (sign == 0) {
dst[textPos++] = '-';
}

formatNumberData(dst + textPos, len - textPos, exponent, mantissa);
dst[0] = '0';
return;
}
}

void formatNumberData(char* dst, uint16_t len, int16_t exponent, uint64_t mantissa) {
print_uint64_t(dst, len, mantissa);
removeTrailingZeros(dst, &exponent);
// 1. Add leading minus sign if number is negative
if (sign == 0) {
dst[0] = '-';

presentationType_e format = determineFormat(dst, exponent);
switch (format) {
case NUM_INTEGER:
parseInteger(dst, len, &exponent);
break;
case NUM_SEMI_DECIMAL:
parseSemiDecimal(dst, len, &exponent);
break;
case NUM_DECIMAL:
parseDecimal(dst, len, &exponent);
break;
case NUM_EXTREME:
parseExtreme(dst, len, &exponent);
break;
default:
THROW(INVALID_PARAMETER);
dst++;
maxLen--;
}

if (exponent != 0) {
size_t mantissaLength = strlen(dst);
// 2. Normalize mantissa by removing redundant trailing zeros
normalize(&mantissa, &exponent);

int numWritten = snprintf(dst + mantissaLength, len - mantissaLength, "e%d", exponent);
if (numWritten < 0 || (size_t)numWritten >= len - mantissaLength) {
snprintf(dst, len, "Formatting failed");
}
}
}
// 3. Print the entire mantissa to our buffer
print_uint64_t(dst, maxLen, mantissa);

void removeTrailingZeros(char* dst, int16_t *exponentParam) {
size_t strEnd = strlen(dst);
for (size_t i = strEnd - 1; i > 0; --i) {
if (dst[i] == '0') {
(*exponentParam)++;
dst[i] = '\0';
} else {
break;
}
}
}
// 4. Store the length of our normalized mantissa
uint16_t len = strlen(dst);

presentationType_e determineFormat(char *dst, int16_t exponent) {
size_t mantissaLength = strlen(dst);
int32_t baseLength = mantissaLength + exponent;
// 5. Calculate the position of the decimal point relative to dst
int16_t decimalPos = len + exponent;

if (baseLength > 0 && baseLength < 10) {
if (exponent > 0) {
return NUM_INTEGER;
} else {
return NUM_SEMI_DECIMAL;
}
} else if (exponent < 0 && exponent > -10) {
return NUM_DECIMAL;
if (exponent >= 0) {
// Exponent is positive, "multiply" the mantissa (decimalPos not needed)
os_memset(dst + len, '0', exponent);
} else if (decimalPos > 0) {
// Decimal position is within our bounds, make room for it and add it
os_memmove(dst + decimalPos + 1, dst + decimalPos, len);
dst[decimalPos] = '.';
} else {
return NUM_EXTREME;
// Decimal position is outside our bounds, move the mantissa to make
// sure that the decimal point can fit within the new bounds
os_memmove(dst - decimalPos + 2, dst, len);
os_memset(dst, '0', -decimalPos + 2);
dst[1] = '.';
}
}

void parseInteger(char *dst, uint16_t len, int16_t *exponentParam) {
size_t mantissaLength = strlen(dst);
if (mantissaLength + *exponentParam >= len - 1) {
THROW(NOT_ENOUGH_SPACE);
}

while (*exponentParam > 0) {
dst[mantissaLength] = '0';

mantissaLength++;
(*exponentParam)--;
}
}

void parseSemiDecimal(char *dst, uint16_t len, int16_t *exponentParam) {
size_t mantissaLength = strlen(dst);
int16_t numDecimals = -*exponentParam;

if (numDecimals < 0) {
THROW(INVALID_PARAMETER);
}

uint16_t uNumDecimals = (uint16_t) numDecimals;
char *decimalBreakpoint = dst + mantissaLength - uNumDecimals;

if (uNumDecimals > 0) {
if (mantissaLength + 1 >= len - 1) {
THROW(NOT_ENOUGH_SPACE);
}

os_memmove(decimalBreakpoint + 1, decimalBreakpoint, uNumDecimals);
*decimalBreakpoint = '.';
void normalize(uint64_t *mantissaParam, int16_t *exponentParam) {
while (*mantissaParam > 0 && *mantissaParam % 10 == 0) {
*mantissaParam = *mantissaParam / 10;
(*exponentParam)++;
}

*exponentParam = 0;
}

void parseDecimal(char *dst, uint16_t len, int16_t *exponentParam) {
size_t mantissaLength = strlen(dst);
int32_t numZeros = -*exponentParam - mantissaLength + 1;
int32_t posShift = numZeros + 1;

if (posShift < 0) {
THROW(INVALID_PARAMETER);
}

if (mantissaLength + posShift >= len - 1) {
THROW(NOT_ENOUGH_SPACE);
}

uint32_t uPosShift = (uint32_t) posShift;
os_memmove(dst + posShift, dst, mantissaLength);
os_memset(dst, '0', uPosShift);

dst[1] = '.';

*exponentParam = 0;
}

void parseExtreme(char *dst, uint16_t len, int16_t *exponentParam) {
size_t mantissaLength = strlen(dst);
if (mantissaLength == 0) {
THROW(INVALID_STATE);
}

if (*exponentParam == 0) {
// Don't add a decimal if it does not provide any additional value
return;
}

if (mantissaLength + 1 >= len - 1) {
THROW(NOT_ENOUGH_SPACE);
}

os_memmove(dst + 1, dst, mantissaLength);
dst[1] = '.';

*exponentParam += mantissaLength - 1;
}

char intToNumberChar(uint64_t value) {
Expand Down
7 changes: 6 additions & 1 deletion src/xrp/parse/numberHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@

#include <stdint.h>

void parseDecimalNumber(char* dst, uint16_t len, uint8_t sign, int16_t exponent, uint64_t mantissa);
#define EXP_MIN -96
#define EXP_MAX 80
#define MANTISSA_MIN 1000000000000000
#define MANTISSA_MAX 9999999999999999

void parseDecimalNumber(char* dst, uint16_t maxLen, uint8_t sign, int16_t exponent, uint64_t mantissa);
char intToNumberChar(uint64_t value);
void print_uint64_t(char *dst, uint16_t len, uint64_t value);

Expand Down

0 comments on commit 71eb5dc

Please sign in to comment.