+/*
+ * For very large strings, strtod and family might exhaust memory in tight
+ * memory conditions (especially in 32-bits). Such large strings could also
+ * tie up a CPU for minutes at a time. Either can be considered a denial-of-
+ * service vunerability.
+ *
+ * To fix, we limit the string size to the maximum we need to calculate the
+ * rounding point correctly. The longest string corresponding to the exact
+ * value of a floating point number occuring at 1.f...f p^-n, where n is
+ * the (absolute value of the) smallest exponent for a normalize number.
+ *
+ * To calculate this number of decimal digits, we use the formula:
+ *
+ * (n + m) - int(n * log10(2)) + 3
+ *
+ * where m is the number of bits in the f...f fraction. This is the number
+ * of decimal digits for the least significant bit minus the number of leading
+ * zeros for the most significant bit (the '1'), plus a few to compensate for
+ * an extra digits due to the full 1.f...f value, an extra digit for the
+ * mid-way point for rounding and an extra guard digit.
+ *
+ * Using the approximation log10(2) ~ 1233 / (2^12), converting to the fpi.emin
+ * and fpi.nbits values, we get:
+ *
+ * -fpi.emin -((1233 * (-fpi.nbits - fpi.emin)) >> 12) + 3
+ *
+ * Finally, we add an extra digit, either '1' or '0', to represent whether
+ * to-be-truncated digits contain a non-zero digit, or are all zeros,
+ * respectively.
+ *
+ * The truncated string is allocated on the heap, so code using
+ * TRUNCATE_DIGITS() will need to free that space when no longer needed.
+ * Pass a char * as the second argument, initialized to NULL; if its value
+ * becomes non-NULL, memory was allocated.
+ */
+#define LOG2NUM 1233
+#define LOG2DENOMSHIFT 12
+#define TRUNCATEDIGITS(_nbits, _emin) (-(_emin) - ((LOG2NUM * (-(_nbits) - (_emin))) >> LOG2DENOMSHIFT) + 3)
+
+#define TRUNCATE_DIGITS(_s0, _temp, _nd, _nd0, _nf, _nbits, _emin, _dplen) \
+{ \
+ int _maxdigits = TRUNCATEDIGITS((_nbits), (_emin)); \
+ if ((_nd) > _maxdigits && \
+ ((_temp) = MALLOC(_maxdigits + (_dplen) + 2)) != NULL) { \
+ char *_tp = (_temp) + _maxdigits; \
+ if ((_nd0) >= _maxdigits) { \
+ memcpy((_temp), (_s0), _maxdigits); \
+ if ((_nd) > (_nd0)) *_tp++ = '1'; \
+ else { \
+ const char *_q = (_s0) + _maxdigits; \
+ int _n = (_nd0) - _maxdigits; \
+ for(; _n > 0 && *_q == '0'; _n--, _q++) {} \
+ *_tp++ = _n > 0 ? '1' : '0'; \
+ } \
+ (_nf) = -((_nd0) - (_maxdigits + 1)); \
+ (_nd0) = _maxdigits + 1; \
+ } \
+ else if ((_nd0) == 0) { \
+ memcpy((_temp), (_s0), _maxdigits); \
+ *_tp++ = '1'; \
+ (_nf) -= ((_nd) - (_maxdigits + 1)); \
+ } \
+ else { \
+ memcpy((_temp), (_s0), _maxdigits + (_dplen)); \
+ _tp += (_dplen); \
+ *_tp++ = '1'; \
+ (_nf) = (_maxdigits + 1) - (_nd0); \
+ } \
+ *_tp = 0; \
+ (_nd) = _maxdigits + 1; \
+ (_s0) = (_temp); \
+ } \
+ }
+