mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-16 04:29:08 +00:00
bignum: Add exponent and trailing to bn_format
This commit is contained in:
parent
22ebd62b85
commit
88527dde7a
106
bignum.c
106
bignum.c
@ -960,56 +960,90 @@ void bn_divmod1000(bignum256 *a, uint32_t *r)
|
||||
*r = rem;
|
||||
}
|
||||
|
||||
// 2^256 has 78 digits in decimal (+ 1 for decimal point, + 1 for leading zero, + 1 for trailing zero)
|
||||
#define DIGITLEN (78 + 1 + 1 + 1)
|
||||
|
||||
int bn_format(const bignum256 *amnt, const char *prefix, const char *suffix, int decimals, char *out, int outlen)
|
||||
size_t bn_format(const bignum256 *amnt, const char *prefix, const char *suffix, unsigned int decimals, int exponent, bool trailing, char *out, size_t outlen)
|
||||
{
|
||||
// convert bignum to characters
|
||||
size_t prefixlen = prefix ? strlen(prefix) : 0;
|
||||
size_t suffixlen = suffix ? strlen(suffix) : 0;
|
||||
|
||||
char *start = &out[prefixlen + suffixlen], *end = &out[outlen];
|
||||
char *str = end;
|
||||
|
||||
#define BN_FORMAT_PUSH_CHECKED(c) \
|
||||
do { \
|
||||
if (str == start) return 0; \
|
||||
*--str = (c); \
|
||||
} while (0)
|
||||
|
||||
#define BN_FORMAT_PUSH(n) \
|
||||
do { \
|
||||
if (exponent < 0) { \
|
||||
exponent++; \
|
||||
} else { \
|
||||
if ((n) > 0 || trailing || str != end || decimals <= 1) { \
|
||||
BN_FORMAT_PUSH_CHECKED('0' + (n)); \
|
||||
} \
|
||||
if (decimals > 0 && decimals-- == 1) { \
|
||||
BN_FORMAT_PUSH_CHECKED('.'); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
bignum256 val;
|
||||
memcpy(&val, amnt, sizeof(bignum256));
|
||||
char digits[DIGITLEN];
|
||||
memset(digits, '0', DIGITLEN);
|
||||
int pos = 1; // keep one trailing zero
|
||||
for (int i = 0; i < 78 / 3; i++) {
|
||||
|
||||
if (bn_is_zero(&val)) {
|
||||
exponent = 0;
|
||||
}
|
||||
|
||||
for (; exponent > 0; exponent--) {
|
||||
BN_FORMAT_PUSH(0);
|
||||
}
|
||||
|
||||
unsigned int digits = bn_digitcount(&val);
|
||||
for (unsigned int i = 0; i < digits / 3; i++) {
|
||||
uint32_t limb;
|
||||
bn_divmod1000(&val, &limb);
|
||||
if (pos == decimals + 1) { digits[DIGITLEN - 1 - pos] = '.'; pos++; }
|
||||
digits[DIGITLEN - 1 - pos] = '0' + (limb % 10); pos++;
|
||||
if (pos == decimals + 1) { digits[DIGITLEN - 1 - pos] = '.'; pos++; }
|
||||
digits[DIGITLEN - 1 - pos] = '0' + ((limb / 10) % 10); pos++;
|
||||
if (pos == decimals + 1) { digits[DIGITLEN - 1 - pos] = '.'; pos++; }
|
||||
digits[DIGITLEN - 1 - pos] = '0' + ((limb / 100) % 10); pos++;
|
||||
|
||||
BN_FORMAT_PUSH(limb % 10);
|
||||
limb /= 10;
|
||||
BN_FORMAT_PUSH(limb % 10);
|
||||
limb /= 10;
|
||||
BN_FORMAT_PUSH(limb % 10);
|
||||
}
|
||||
|
||||
// drop leading zeroes
|
||||
int digitstart = 0;
|
||||
while (digitstart < DIGITLEN - 1 && digits[digitstart] == '0' && digits[digitstart + 1] >= '0' && digits[digitstart + 1] <= '9') {
|
||||
digitstart++;
|
||||
if (digits % 3 != 0) {
|
||||
uint32_t limb;
|
||||
bn_divmod1000(&val, &limb);
|
||||
|
||||
switch (digits % 3) {
|
||||
case 2:
|
||||
BN_FORMAT_PUSH(limb % 10);
|
||||
limb /= 10;
|
||||
//-fallthrough
|
||||
|
||||
case 1:
|
||||
BN_FORMAT_PUSH(limb % 10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// drop trailing zeroes
|
||||
int digitend = DIGITLEN - 1;
|
||||
while (digitend > 0 && digits[digitend] == '0' && digits[digitend - 1] >= '0' && digits[digitend - 1] <= '9') {
|
||||
digitend--;
|
||||
while (decimals > 0 || str[0] == '\0' || str[0] == '.') {
|
||||
BN_FORMAT_PUSH(0);
|
||||
}
|
||||
|
||||
int digitslen = digitend - digitstart + 1;
|
||||
int prefixlen = prefix != NULL ? strlen(prefix) : 0;
|
||||
int suffixlen = suffix != NULL ? strlen(suffix) : 0;
|
||||
size_t len = end - str;
|
||||
memmove(&out[prefixlen], str, len);
|
||||
|
||||
// output buffer is too small
|
||||
if (prefixlen + digitslen + suffixlen + 1 > outlen) {
|
||||
return 0;
|
||||
if (prefixlen) {
|
||||
memcpy(out, prefix, prefixlen);
|
||||
}
|
||||
if (suffixlen) {
|
||||
memcpy(&out[prefixlen + len], suffix, suffixlen);
|
||||
}
|
||||
|
||||
// copy result to output buffer
|
||||
memcpy(out, prefix, prefixlen);
|
||||
memcpy(out + prefixlen, digits + digitstart, digitslen);
|
||||
memcpy(out + prefixlen + digitslen, suffix, suffixlen);
|
||||
out[prefixlen + digitslen + suffixlen] = 0;
|
||||
|
||||
return prefixlen + digitslen + suffixlen;
|
||||
size_t length = prefixlen + len + suffixlen;
|
||||
out[length] = '\0';
|
||||
return length;
|
||||
}
|
||||
|
||||
#if USE_BN_PRINT
|
||||
|
4
bignum.h
4
bignum.h
@ -25,6 +25,8 @@
|
||||
#ifndef __BIGNUM_H__
|
||||
#define __BIGNUM_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "options.h"
|
||||
|
||||
@ -156,7 +158,7 @@ void bn_divmod58(bignum256 *a, uint32_t *r);
|
||||
|
||||
void bn_divmod1000(bignum256 *a, uint32_t *r);
|
||||
|
||||
int bn_format(const bignum256 *amnt, const char *prefix, const char *suffix, int decimals, char *out, int outlen);
|
||||
size_t bn_format(const bignum256 *amnt, const char *prefix, const char *suffix, unsigned int decimals, int exponent, bool trailing, char *out, size_t outlen);
|
||||
|
||||
#if USE_BN_PRINT
|
||||
void bn_print(const bignum256 *a);
|
||||
|
50
test_check.c
50
test_check.c
@ -405,127 +405,127 @@ START_TEST(test_bignum_format) {
|
||||
int r;
|
||||
|
||||
bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000000"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 1);
|
||||
ck_assert_str_eq(buf, "0");
|
||||
|
||||
bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000000"), &a);
|
||||
r = bn_format(&a, "", "", 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, "", "", 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 1);
|
||||
ck_assert_str_eq(buf, "0");
|
||||
|
||||
bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000000"), &a);
|
||||
r = bn_format(&a, NULL, "SFFX", 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, "SFFX", 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 1 + 4);
|
||||
ck_assert_str_eq(buf, "0SFFX");
|
||||
|
||||
bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000000"), &a);
|
||||
r = bn_format(&a, "PRFX", NULL, 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, "PRFX", NULL, 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 4 + 1);
|
||||
ck_assert_str_eq(buf, "PRFX0");
|
||||
|
||||
bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000000"), &a);
|
||||
r = bn_format(&a, "PRFX", "SFFX", 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, "PRFX", "SFFX", 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 4 + 1 + 4);
|
||||
ck_assert_str_eq(buf, "PRFX0SFFX");
|
||||
|
||||
bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000000"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 18, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 18, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 3);
|
||||
ck_assert_str_eq(buf, "0.0");
|
||||
|
||||
bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000001"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 1);
|
||||
ck_assert_str_eq(buf, "1");
|
||||
|
||||
bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000002"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 1);
|
||||
ck_assert_str_eq(buf, "2");
|
||||
|
||||
bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000005"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 1);
|
||||
ck_assert_str_eq(buf, "5");
|
||||
|
||||
bn_read_be(fromhex("000000000000000000000000000000000000000000000000000000000000000a"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 2);
|
||||
ck_assert_str_eq(buf, "10");
|
||||
|
||||
bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000014"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 2);
|
||||
ck_assert_str_eq(buf, "20");
|
||||
|
||||
bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000032"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 2);
|
||||
ck_assert_str_eq(buf, "50");
|
||||
|
||||
bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000000064"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 3);
|
||||
ck_assert_str_eq(buf, "100");
|
||||
|
||||
bn_read_be(fromhex("00000000000000000000000000000000000000000000000000000000000000c8"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 3);
|
||||
ck_assert_str_eq(buf, "200");
|
||||
|
||||
bn_read_be(fromhex("00000000000000000000000000000000000000000000000000000000000001f4"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 3);
|
||||
ck_assert_str_eq(buf, "500");
|
||||
|
||||
bn_read_be(fromhex("00000000000000000000000000000000000000000000000000000000000003e8"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 4);
|
||||
ck_assert_str_eq(buf, "1000");
|
||||
|
||||
bn_read_be(fromhex("0000000000000000000000000000000000000000000000000000000000989680"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 7, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 7, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 3);
|
||||
ck_assert_str_eq(buf, "1.0");
|
||||
|
||||
bn_read_be(fromhex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 0, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 78);
|
||||
ck_assert_str_eq(buf, "115792089237316195423570985008687907853269984665640564039457584007913129639935");
|
||||
|
||||
bn_read_be(fromhex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 1, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 1, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 79);
|
||||
ck_assert_str_eq(buf, "11579208923731619542357098500868790785326998466564056403945758400791312963993.5");
|
||||
|
||||
bn_read_be(fromhex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 2, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 2, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 79);
|
||||
ck_assert_str_eq(buf, "1157920892373161954235709850086879078532699846656405640394575840079131296399.35");
|
||||
|
||||
bn_read_be(fromhex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 8, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 8, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 79);
|
||||
ck_assert_str_eq(buf, "1157920892373161954235709850086879078532699846656405640394575840079131.29639935");
|
||||
|
||||
bn_read_be(fromhex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe3bbb00"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 8, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 8, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 72);
|
||||
ck_assert_str_eq(buf, "1157920892373161954235709850086879078532699846656405640394575840079131.0");
|
||||
|
||||
bn_read_be(fromhex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 18, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 18, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 79);
|
||||
ck_assert_str_eq(buf, "115792089237316195423570985008687907853269984665640564039457.584007913129639935");
|
||||
|
||||
bn_read_be(fromhex("fffffffffffffffffffffffffffffffffffffffffffffffff7e52fe5afe40000"), &a);
|
||||
r = bn_format(&a, NULL, NULL, 18, buf, sizeof(buf));
|
||||
r = bn_format(&a, NULL, NULL, 18, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 62);
|
||||
ck_assert_str_eq(buf, "115792089237316195423570985008687907853269984665640564039457.0");
|
||||
|
||||
bn_read_be(fromhex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a);
|
||||
r = bn_format(&a, "quite a long prefix", "even longer suffix", 60, buf, sizeof(buf));
|
||||
r = bn_format(&a, "quite a long prefix", "even longer suffix", 60, 0, false, buf, sizeof(buf));
|
||||
ck_assert_int_eq(r, 116);
|
||||
ck_assert_str_eq(buf, "quite a long prefix115792089237316195.423570985008687907853269984665640564039457584007913129639935even longer suffix");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user