| #include "bej_common.h" |
| |
| #include <float.h> |
| #include <inttypes.h> |
| #include <stdio.h> |
| |
| uint64_t bejGetUnsignedInteger(const uint8_t* bytes, uint8_t numOfBytes) |
| { |
| uint64_t num = 0; |
| for (uint8_t i = 0; i < numOfBytes; ++i) |
| { |
| num |= (uint64_t)(*(bytes + i)) << (i * 8); |
| } |
| return num; |
| } |
| |
| uint64_t bejGetNnint(const uint8_t* nnint) |
| { |
| // In nnint, first byte indicate how many bytes are there. Remaining bytes |
| // represent the value in little-endian format. |
| const uint8_t size = *nnint; |
| return bejGetUnsignedInteger(nnint + sizeof(uint8_t), size); |
| } |
| |
| uint8_t bejGetNnintSize(const uint8_t* nnint) |
| { |
| // In nnint, first byte indicate how many bytes are there. |
| return *nnint + sizeof(uint8_t); |
| } |
| |
| uint8_t bejIntLengthOfValue(int64_t val) |
| { |
| // Only need to encode 0x00 or 0xFF |
| if (val == 0 || val == -1) |
| { |
| return 1; |
| } |
| |
| // Starts at the MSB. LSB index is 0. |
| uint8_t byteIndex = sizeof(uint64_t) - 1; |
| const uint8_t bitsPerByte = 8; |
| // The current byte being looked at. Starts at MSB. |
| uint8_t currentByte = (val >> (bitsPerByte * byteIndex)) & 0xFF; |
| uint8_t byteLength = sizeof(int64_t); |
| |
| while ((val > 0 && currentByte == 0) || (val < 0 && currentByte == 0xFF)) |
| { |
| byteLength--; |
| byteIndex--; |
| currentByte = (val >> (bitsPerByte * byteIndex)) & 0xFF; |
| } |
| |
| // If the value is positive and encoded MSBbit is 1 we need to add 0x00 to |
| // the encoded value as padding. |
| if (val > 0 && (currentByte & 0x80)) |
| { |
| byteLength++; |
| } |
| |
| // If the value is negative and encoded MSBbit is 0 we need to add 0xFF to |
| // the encoded value as padding. |
| if (val < 0 && !(currentByte & 0x80)) |
| { |
| byteLength++; |
| } |
| |
| return byteLength; |
| } |
| |
| uint8_t bejNnintEncodingSizeOfUInt(uint64_t val) |
| { |
| uint8_t bytes = 0; |
| do |
| { |
| // Even if the value is 0, we need a byte for that. |
| ++bytes; |
| val = val >> 8; |
| } while (val != 0); |
| // Need 1 byte to add the nnint length. |
| return bytes + 1; |
| } |
| |
| uint8_t bejNnintLengthFieldOfUInt(uint64_t val) |
| { |
| // From the size of the encoded value, we need 1 byte for the length field. |
| return bejNnintEncodingSizeOfUInt(val) - 1; |
| } |
| |
| double bejFabs(double x) |
| { |
| return (x < 0) ? -x : x; |
| } |
| |
| double bejModf(double x, double* integer) |
| { |
| // x expected to be within int64_t max value. |
| int64_t i = (int64_t)x; |
| *integer = (double)i; |
| return x - *integer; |
| } |
| |
| int bejGetDoubleFromBejReal(const struct BejReal* value, double* output) |
| { |
| // Maximum supported zero count in BejReal type. The value selected will prevent |
| // overflow of divisor(uin64_t). We could support larger zero count but that |
| // will require more computational logic and we probably do not need that level |
| // of precision. |
| #define BEJ_REAL_MAX_SUPPORTED_ZERO_COUNT 19 |
| |
| // Maximum value possible for the uint64_t divisor used. |
| #define BEJ_UINT64_DIVISOR_MAX ((uint64_t)10000000000000000000ULL) |
| |
| if (value->zeroCount > BEJ_REAL_MAX_SUPPORTED_ZERO_COUNT) |
| { |
| fprintf(stderr, |
| "Provided zero count: %" PRIu64 " exceeds supported: %u\n", |
| value->zeroCount, BEJ_REAL_MAX_SUPPORTED_ZERO_COUNT); |
| return -1; |
| } |
| |
| // Initialize the multiplication or division factor based on exponent |
| double exp_factor = 1.00; |
| for (int64_t exp = bejFabs(value->exp); exp > 0; --exp) |
| { |
| // Check if the multiplication factor is larger than what can be |
| // supported. |
| if (exp_factor > (DBL_MAX / 10.0)) |
| { |
| fprintf(stderr, "Multiplication factor derived from exp is larger " |
| "than a double\n"); |
| return -1; |
| } |
| exp_factor *= 10; |
| } |
| |
| // The double value will be formed as follows: |
| // value.whole = 1 |
| // value.zeroCount = 3 |
| // value.fract = 525 |
| // output = whole.[zero_count][fractional_part] ==> 1.000525. |
| // Then the exponent will be applied. |
| // output /* 10^(exp) |
| |
| // Get the whole value. |
| *output = value->whole; |
| |
| // Calculating the divisor needed to divide the fract. Check for divisor |
| // overflow. |
| uint64_t divisor = 1; |
| while (divisor <= value->fract && (divisor < BEJ_UINT64_DIVISOR_MAX)) |
| { |
| divisor *= 10; |
| } |
| |
| double fraction = (double)value->fract / divisor; |
| // In case divisor was going to overflow, we need to divide fraction by an |
| // additional 10. |
| if (divisor == BEJ_UINT64_DIVISOR_MAX) |
| { |
| fraction /= 10; |
| } |
| |
| // Then we need to divide the fractional part by the number of zeros. |
| // Calculating the divisor for that. Divisor will not overflow since we have |
| // limit the zero count. |
| divisor = 1; |
| for (uint64_t zeroCount = value->zeroCount; zeroCount > 0; --zeroCount) |
| { |
| divisor *= 10; |
| } |
| fraction /= divisor; |
| |
| // "fract" part doesn't include the sign. We need to use the sign of the |
| // "whole". The current representation doesn't support negative values less |
| // than 0 and greater then -1. That means if the whole part is 0, then sign |
| // will be always +. |
| fraction *= ((value->whole < 0) ? -1 : 1); |
| *output += fraction; |
| |
| if (value->exp > 0) |
| { |
| // Check if the result is larger than a double |
| if (*output > (DBL_MAX / exp_factor)) |
| { |
| fprintf(stderr, "Result is larger than a double\n"); |
| return -1; |
| } |
| *output *= exp_factor; |
| } |
| // Using else if to avoid unnecessary division if exp == 0. |
| else if (value->exp < 0) |
| { |
| *output /= exp_factor; |
| } |
| |
| return 0; |
| } |