164 строки
6.5 KiB
C++
164 строки
6.5 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
// dtoa doesn't provide a header file so this simple one was created.
|
|
|
|
#ifndef HERMES_DTOA_DTOA_H
|
|
#define HERMES_DTOA_DTOA_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/// dtoa functions need to allocate memory and that job is handled by the dtoa
|
|
/// allocator. \c dtoa_alloc is an opaque struct representing the allocator. It
|
|
/// is created by calling \c dtoa_alloc_init() with a pointer to a memory
|
|
/// buffer declared with \c DECL_DTOA_ALLOC_MEM(name, size).
|
|
///
|
|
/// The allocator metadata itself is placed in that memory buffer and the rest
|
|
/// of it is used to satisfy memory allocations. If it is not enough, additional
|
|
/// allocations are made in the regular heap with malloc()/free(). So, the
|
|
/// larger the buffer declared by \c DECL_DTOA_ALLOC_MEM(), the less probability
|
|
/// there that a heap allocation will be needed.
|
|
///
|
|
/// The allocator is not thread safe, so we must guarantee that it is used only
|
|
/// by one thread a time. Usually we just create it on the stack (which is very
|
|
/// fast), but it could live in other places, as long as the single thread
|
|
/// requirement is satisfied.
|
|
///
|
|
/// The allocator metadata itself is less than 128 bytes - the rest is the
|
|
/// "allocation buffer". The dtoa documentation states that 2304 byte allocation
|
|
/// buffer is sufficient for most cases except the unusual ones, and a 7400 byte
|
|
/// allocation buffer is sufficient for all cases.
|
|
///
|
|
/// We don't need to avoid heap allocation at all costs, so we have chosen a
|
|
/// total allocator size of 1200 bytes, which seems to avoid heap allocations
|
|
/// for "normal" cases.
|
|
typedef struct dtoa_alloc dtoa_alloc;
|
|
|
|
/// The minimal size of the dtoa allocator memory buffer. The metadata is less
|
|
/// than 128 bytes and the rest is available to satisfy dtoa allocations.
|
|
#define DTOA_ALLOC_MIN_SIZE 256
|
|
/// The default size of the dtoa allocator memory buffer, which we use when
|
|
/// declaring it on the stack. The value attempts to find balance between
|
|
/// excessive stack consumption and avoiding allocations for "normal" cases.
|
|
#define DTOA_ALLOC_DEFAULT_SIZE 1200
|
|
|
|
/// This macro is used to declare a memory buffer for the dtoa allocator. Most
|
|
/// of all it ensures that the memory is properly aligned. The variable declared
|
|
/// by this macro is then passed to \c dtoa_alloc_init().
|
|
#define DECL_DTOA_ALLOC_MEM(name, bytelen) \
|
|
union { \
|
|
void *p; \
|
|
double d; \
|
|
long long l; \
|
|
char mem[(bytelen)]; \
|
|
} name
|
|
|
|
/// Initialize an allocator using the specified memory buffer, and return a
|
|
/// pointer to the allocator. Note that this does not stipulate that the
|
|
/// returned pointer will equal \c mem.
|
|
dtoa_alloc *dtoa_alloc_init(void *mem, int bytelen);
|
|
|
|
/// Destroy the previously initialized allocator. Primarily, this call frees
|
|
/// any heap allocations in the allocator.
|
|
void dtoa_alloc_done(dtoa_alloc *dalloc);
|
|
|
|
/// Converts double into ascii string.
|
|
/// \param dd the double to convert.
|
|
/// \param mode the rounding mode, 0 for default.<ul>
|
|
/// <li>0 ==> shortest string that yields d when read in
|
|
/// and rounded to nearest.
|
|
/// <li>1 ==> like 0, but with Steele & White stopping rule;
|
|
/// e.g. with IEEE P754 arithmetic , mode 0 gives
|
|
/// 1e23 whereas mode 1 gives 9.999999999999999e22.
|
|
/// <li>2 ==> max(1,ndigits) significant digits. This gives a
|
|
/// return value similar to that of ecvt, except
|
|
/// that trailing zeros are suppressed.
|
|
/// <li>3 ==> through ndigits past the decimal point. This
|
|
/// gives a return value similar to that from fcvt,
|
|
/// except that trailing zeros are suppressed, and
|
|
/// ndigits can be negative.
|
|
/// <li>4,5 ==> similar to 2 and 3, respectively, but (in
|
|
/// round-nearest mode) with the tests of mode 0 to
|
|
/// possibly return a shorter string that rounds to d.
|
|
/// With IEEE arithmetic and compilation with
|
|
/// -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same
|
|
/// as modes 2 and 3 when FLT_ROUNDS != 1.
|
|
/// <li>6-9 ==> Debugging modes similar to mode - 4: don't try
|
|
/// fast floating-point estimate (if applicable).
|
|
/// <li>Values of mode other than 0-9 are treated as mode 0.
|
|
/// </ul>
|
|
/// \param ndigits number of digits of precision, 0 for default.
|
|
/// \param decpt where to store position of the decimal. (n in ES5.1 9.8.1)
|
|
/// \param sign location to store 1 if negative number, 0 if positive number.
|
|
/// \param rve location to store pointer to the end of the returned string.
|
|
/// \return string representation of s in ES5.1 9.8.1
|
|
char *g_dtoa(
|
|
dtoa_alloc *dalloc,
|
|
double dd,
|
|
int mode,
|
|
int ndigits,
|
|
int *decpt,
|
|
int *sign,
|
|
char **rve);
|
|
|
|
/// Same as dtoa, but #defines ROUND_BIASED, which enables the mode which is
|
|
/// used for getting results with a fixed number of digits after the decimal.
|
|
/// It also modifies a check in dtoa (see the NOTE in dtoa_fixed.c),
|
|
/// which ensures that 0.5 does not get flushed to 0, but rather rounds up to 1.
|
|
/// A separate function is necessary because dtoa needs compilation flags
|
|
/// to change options and provides no runtime means of doing so,
|
|
/// and modification of the code was needed to ensure correctly biased rounding.
|
|
char *dtoa_fixedpoint(
|
|
dtoa_alloc *dalloc,
|
|
double dd,
|
|
int mode,
|
|
int ndigits,
|
|
int *decpt,
|
|
int *sign,
|
|
char **rve);
|
|
|
|
/// Free the result of \c g_dtoa() and \c dtoa_fixedpoint().
|
|
void g_freedtoa(dtoa_alloc *dalloc, char *);
|
|
|
|
char *g_fmt(char *, double);
|
|
double hermes_g_strtod(const char *s00, char **se);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
/// A convenience RAII wrapper around a dtoa allocator. The usage should be
|
|
/// self-explanatory. Declare it on the stack (or as a class member), supplying
|
|
/// the memory buffer size, and pass it to the dtoa functions.
|
|
template <int bytelen = DTOA_ALLOC_DEFAULT_SIZE>
|
|
class DtoaAllocator {
|
|
public:
|
|
DtoaAllocator(const DtoaAllocator &) = delete;
|
|
void operator=(const DtoaAllocator &) = delete;
|
|
|
|
DtoaAllocator() {
|
|
dalloc_ = dtoa_alloc_init(&mem_, bytelen);
|
|
}
|
|
~DtoaAllocator() {
|
|
dtoa_alloc_done(dalloc_);
|
|
}
|
|
|
|
operator dtoa_alloc *() {
|
|
return dalloc_;
|
|
}
|
|
|
|
private:
|
|
DECL_DTOA_ALLOC_MEM(mem_, bytelen);
|
|
dtoa_alloc *dalloc_;
|
|
};
|
|
#endif
|
|
|
|
#endif // HERMES_DTOA_DTOA_H
|