Fix HookAPI Expected and Refactor Enum classes (#729)

This commit is contained in:
tequ
2026-05-26 10:02:17 +09:00
committed by GitHub
parent 7f9a9364b0
commit 90333b6fd0
7 changed files with 689 additions and 565 deletions

View File

@@ -9,7 +9,7 @@ ENUM_FILE="$SCRIPT_DIR/../include/xrpl/hook/Enum.h"
echo '// For documentation please see: https://xrpl-hooks.readme.io/reference/'
echo '// Generated using generate_error.sh'
echo '#ifndef HOOK_ERROR_CODES'
sed -n '/enum hook_return_code/,/};/p' "$ENUM_FILE" |
sed -n '/enum class hook_return_code/,/};/p' "$ENUM_FILE" |
awk '
function ltrim(s) { sub(/^[[:space:]]+/, "", s); return s }
function rtrim(s) { sub(/[[:space:]]+$/, "", s); return s }
@@ -31,7 +31,7 @@ sed -n '/enum hook_return_code/,/};/p' "$ENUM_FILE" |
{
line = $0
if (line ~ /enum[[:space:]]+hook_return_code/)
if (line ~ /enum[[:space:]]+class[[:space:]]+hook_return_code/)
next
if (line ~ /^[[:space:]]*\{/)
next

View File

@@ -320,7 +320,7 @@ namespace compare_mode {
enum compare_mode : uint32_t { EQUAL = 1, LESS = 2, GREATER = 4 };
}
enum hook_return_code : int64_t {
enum class hook_return_code : int64_t {
SUCCESS =
0, // return codes > 0 are reserved for hook apis to return "success"
OUT_OF_BOUNDS =
@@ -387,7 +387,7 @@ enum hook_return_code : int64_t {
TOO_MANY_NAMESPACES = -45
};
enum ExitType : uint8_t {
enum class ExitType : uint8_t {
UNSET = 0,
WASM_ERROR = 1,
ROLLBACK = 2,

View File

@@ -89,58 +89,69 @@
#define WASM_VAL_TYPE(T, b) CAT2(TYP_, T)
#define DECLARE_HOOK_FUNCTION(R, F, ...) \
R F(hook::HookContext& hookCtx, \
WasmEdge_CallingFrameContext const& frameCtx __VA_OPT__( \
COMMA __VA_ARGS__)); \
extern WasmEdge_Result WasmFunction##F( \
void* data_ptr, \
const WasmEdge_CallingFrameContext* frameCtx, \
const WasmEdge_Value* in, \
WasmEdge_Value* out); \
extern WasmEdge_ValType WasmFunctionParams##F[]; \
extern WasmEdge_ValType WasmFunctionResult##F[]; \
extern WasmEdge_FunctionTypeContext* WasmFunctionType##F; \
#define UNSIGNED_TYPE(T) std::make_unsigned_t<T>
#define DECLARE_HOOK_FUNCTION(R, F, ...) \
std::variant<UNSIGNED_TYPE(R), hook_api::hook_return_code> F( \
hook::HookContext& hookCtx, \
WasmEdge_CallingFrameContext const& frameCtx __VA_OPT__( \
COMMA __VA_ARGS__)); \
extern WasmEdge_Result WasmFunction##F( \
void* data_ptr, \
const WasmEdge_CallingFrameContext* frameCtx, \
const WasmEdge_Value* in, \
WasmEdge_Value* out); \
extern WasmEdge_ValType WasmFunctionParams##F[]; \
extern WasmEdge_ValType WasmFunctionResult##F[]; \
extern WasmEdge_FunctionTypeContext* WasmFunctionType##F; \
extern WasmEdge_String WasmFunctionName##F;
#define DEFINE_HOOK_FUNCTION(R, F, ...) \
WasmEdge_Result hook_api::WasmFunction##F( \
void* data_ptr, \
const WasmEdge_CallingFrameContext* frameCtx, \
const WasmEdge_Value* in, \
WasmEdge_Value* out) \
{ \
__VA_OPT__(int _stack = 0;) \
__VA_OPT__(FOR_VARS(VAR_ASSIGN, 2, __VA_ARGS__);) \
hook::HookContext* hookCtx = \
reinterpret_cast<hook::HookContext*>(data_ptr); \
R return_code = hook_api::F( \
*hookCtx, \
*const_cast<WasmEdge_CallingFrameContext*>(frameCtx) \
__VA_OPT__(COMMA STRIP_TYPES(__VA_ARGS__))); \
if (return_code == RC_ROLLBACK || return_code == RC_ACCEPT) \
return WasmEdge_Result_Terminate; \
out[0] = RET_ASSIGN(R, return_code); \
return WasmEdge_Result_Success; \
}; \
WasmEdge_ValType hook_api::WasmFunctionParams##F[] = { \
__VA_OPT__(FOR_VARS(WASM_VAL_TYPE, 0, __VA_ARGS__))}; \
WasmEdge_ValType hook_api::WasmFunctionResult##F[1] = { \
WASM_VAL_TYPE(R, dummy)}; \
WasmEdge_FunctionTypeContext* hook_api::WasmFunctionType##F = \
WasmEdge_FunctionTypeCreate( \
WasmFunctionParams##F, \
VA_NARGS(NULL __VA_OPT__(, __VA_ARGS__)), \
WasmFunctionResult##F, \
1); \
WasmEdge_String hook_api::WasmFunctionName##F = \
WasmEdge_StringCreateByCString(#F); \
R hook_api::F( \
hook::HookContext& hookCtx, \
WasmEdge_CallingFrameContext const& frameCtx __VA_OPT__( \
#define DEFINE_HOOK_FUNCTION(R, F, ...) \
WasmEdge_Result hook_api::WasmFunction##F( \
void* data_ptr, \
const WasmEdge_CallingFrameContext* frameCtx, \
const WasmEdge_Value* in, \
WasmEdge_Value* out) \
{ \
__VA_OPT__(int _stack = 0;) \
__VA_OPT__(FOR_VARS(VAR_ASSIGN, 2, __VA_ARGS__);) \
hook::HookContext* hookCtx = \
reinterpret_cast<hook::HookContext*>(data_ptr); \
auto const& return_code = hook_api::F( \
*hookCtx, \
*const_cast<WasmEdge_CallingFrameContext*>(frameCtx) \
__VA_OPT__(COMMA STRIP_TYPES(__VA_ARGS__))); \
if (std::holds_alternative<hook_api::hook_return_code>(return_code) && \
(std::get<hook_api::hook_return_code>(return_code) == \
RC_ROLLBACK || \
std::get<hook_api::hook_return_code>(return_code) == RC_ACCEPT)) \
return WasmEdge_Result_Terminate; \
out[0] = RET_ASSIGN( \
R, \
std::holds_alternative<UNSIGNED_TYPE(R)>(return_code) \
? std::get<UNSIGNED_TYPE(R)>(return_code) \
: R(std::get<hook_api::hook_return_code>(return_code))); \
return WasmEdge_Result_Success; \
}; \
WasmEdge_ValType hook_api::WasmFunctionParams##F[] = { \
__VA_OPT__(FOR_VARS(WASM_VAL_TYPE, 0, __VA_ARGS__))}; \
WasmEdge_ValType hook_api::WasmFunctionResult##F[1] = { \
WASM_VAL_TYPE(R, dummy)}; \
WasmEdge_FunctionTypeContext* hook_api::WasmFunctionType##F = \
WasmEdge_FunctionTypeCreate( \
WasmFunctionParams##F, \
VA_NARGS(NULL __VA_OPT__(, __VA_ARGS__)), \
WasmFunctionResult##F, \
1); \
WasmEdge_String hook_api::WasmFunctionName##F = \
WasmEdge_StringCreateByCString(#F); \
std::variant<UNSIGNED_TYPE(R), hook_api::hook_return_code> hook_api::F( \
hook::HookContext& hookCtx, \
WasmEdge_CallingFrameContext const& frameCtx __VA_OPT__( \
COMMA __VA_ARGS__))
#define HOOK_SETUP() \
using enum hook_api::hook_return_code; \
try \
{ \
[[maybe_unused]] ApplyContext& applyCtx = hookCtx.applyCtx; \
@@ -203,7 +214,7 @@
host_memory_ptr, \
guest_memory_length) \
{ \
int64_t bytes_written = 0; \
uint64_t bytes_written = 0; \
WRITE_WASM_MEMORY( \
bytes_written, \
guest_dst_ptr, \
@@ -272,7 +283,7 @@
data_ptr < (data_ptr_in)) \
return INTERNAL_ERROR; \
if (data_len == 0) \
return 0; \
return 0ULL; \
if ((write_ptr_in) == 0) \
return data_as_int64(data_ptr, data_len); \
if (data_len > (write_len_in)) \

View File

@@ -50,6 +50,7 @@ private:
}
public:
using enum hook_api::hook_return_code;
void
test_accept(FeatureBitset features)
{
@@ -4513,6 +4514,249 @@ public:
BEAST_EXPECT(ok.value());
}
void
testHookFloat(FeatureBitset features)
{
// testcase("Test hook float");
using namespace jtx;
using namespace hook::hook_float;
{
// get_exponent
testcase << "get_exponent";
BEAST_EXPECT(get_exponent(-1).error() == INVALID_FLOAT);
BEAST_EXPECT(get_exponent(0).value() == 0);
// 1234567891000000 * 10^(-5)
BEAST_EXPECT(get_exponent(6270245249190730432).value() == -5);
}
{
// get_mantissa
testcase << "get_mantissa";
BEAST_EXPECT(get_mantissa(-1).error() == INVALID_FLOAT);
BEAST_EXPECT(get_mantissa(0).value() == 0);
// 1234567891000000 * 10^(-5)
BEAST_EXPECT(
get_mantissa(6270245249190730432).value() == 1234567891000000);
}
{
// is_negative
testcase << "is_negative";
// 1234567891000000 * 10^(-5)
BEAST_EXPECT(is_negative(6270245249190730432) == false);
// -1234567891000000 * 10^(-5)
BEAST_EXPECT(is_negative(1658559230763342528) == true);
}
{
// invert_sign
testcase << "invert_sign";
BEAST_EXPECT(
invert_sign(6270245249190730432) == 1658559230763342528);
BEAST_EXPECT(
invert_sign(1658559230763342528) == 6270245249190730432);
}
{
// set_sign
testcase << "set_sign";
BEAST_EXPECT(
set_sign(6270245249190730432, true) == 1658559230763342528);
BEAST_EXPECT(
set_sign(1658559230763342528, false) == 6270245249190730432);
BEAST_EXPECT(
set_sign(6270245249190730432, false) == 6270245249190730432);
BEAST_EXPECT(
set_sign(1658559230763342528, true) == 1658559230763342528);
}
{
// set_mantissa
testcase << "set_mantissa";
BEAST_EXPECT(
set_mantissa(6270245249190730432, maxMantissa + 1).error() ==
MANTISSA_OVERSIZED);
BEAST_EXPECT(
set_mantissa(6270245249190730432, minMantissa - 1).error() ==
MANTISSA_UNDERSIZED);
BEAST_EXPECT(
set_mantissa(6270245249190730432, 1234567891000000).value() ==
6270245249190730432);
BEAST_EXPECT(
set_mantissa(1658559230763342528, 1234567891000000).value() ==
1658559230763342528);
BEAST_EXPECT(
set_mantissa(6270245249190730432, 1098765432100000).value() ==
6270109446731830432);
BEAST_EXPECT(
set_mantissa(1658559230763342528, 1098765432100000).value() ==
1658423428304442528);
}
{
// set_exponent
testcase << "set_exponent";
BEAST_EXPECT(
set_exponent(6270245249190730432, maxExponent + 1).error() ==
EXPONENT_OVERSIZED);
BEAST_EXPECT(
set_exponent(6270245249190730432, minExponent - 1).error() ==
EXPONENT_UNDERSIZED);
BEAST_EXPECT(
set_exponent(6270245249190730432, 40).value() ==
7080893182117419712);
BEAST_EXPECT(
set_exponent(6270245249190730432, -40).value() ==
5639741301358860992);
}
{
// make_float
testcase << "make_float";
BEAST_EXPECT(make_float(0, -5, false).value() == 0);
// invalid mantissa
BEAST_EXPECT(
make_float(maxMantissa + 1, -5, false).error() ==
MANTISSA_OVERSIZED);
BEAST_EXPECT(
make_float(minMantissa - 1, -5, false).error() ==
MANTISSA_UNDERSIZED);
// invalid exponent
BEAST_EXPECT(
make_float(1234567891000000, maxExponent + 1, false).error() ==
EXPONENT_OVERSIZED);
BEAST_EXPECT(
make_float(1234567891000000, minExponent - 1, false).error() ==
EXPONENT_UNDERSIZED);
BEAST_EXPECT(
make_float(1234567891000000, -5, false).value() ==
6270245249190730432);
BEAST_EXPECT(
make_float(1234567891000000, -5, true).value() ==
1658559230763342528);
}
{
// normalize_xfl
testcase << "normalize_xfl";
{
// zero
uint64_t mantissa = 0;
int32_t exponent = -5;
BEAST_EXPECT(
normalize_xfl(mantissa, exponent, false).value() == 0);
BEAST_EXPECT(mantissa == 0);
BEAST_EXPECT(exponent == -5);
}
{
// scale the mantissa up into the canonical range
uint64_t mantissa = 12345;
int32_t exponent = 0;
BEAST_EXPECT(
normalize_xfl(mantissa, exponent, false).value() ==
make_float(1234500000000000ULL, -11, false).value());
BEAST_EXPECT(mantissa == 1234500000000000ULL);
BEAST_EXPECT(exponent == -11);
}
{
// scale the mantissa down into the canonical range
uint64_t mantissa = 1234567891000000000ULL;
int32_t exponent = -5;
BEAST_EXPECT(
normalize_xfl(mantissa, exponent, false).value() ==
make_float(1234567891000000ULL, -2, false).value());
BEAST_EXPECT(mantissa == 1234567891000000ULL);
BEAST_EXPECT(exponent == -2);
}
{
// explicit negative sign
uint64_t mantissa = 12345;
int32_t exponent = 0;
BEAST_EXPECT(
normalize_xfl(mantissa, exponent, true).value() ==
make_float(1234500000000000ULL, -11, true).value());
BEAST_EXPECT(mantissa == 1234500000000000ULL);
BEAST_EXPECT(exponent == -11);
}
{
// signed negative mantissa
int64_t mantissa = -12345;
int32_t exponent = 0;
BEAST_EXPECT(
normalize_xfl(mantissa, exponent).value() ==
make_float(1234500000000000ULL, -11, true).value());
BEAST_EXPECT(mantissa == -1234500000000000LL);
BEAST_EXPECT(exponent == -11);
}
{
// signed minimum is adjusted before taking the absolute value
int64_t mantissa = std::numeric_limits<int64_t>::min();
int32_t exponent = 0;
BEAST_EXPECT(
normalize_xfl(mantissa, exponent).value() ==
make_float(9223372036854775ULL, 3, true).value());
BEAST_EXPECT(mantissa == -9223372036854775LL);
BEAST_EXPECT(exponent == 3);
}
{
// one below the minimum mantissa is rounded into range
uint64_t mantissa = minMantissa - 1;
int32_t exponent = -5;
BEAST_EXPECT(
normalize_xfl(mantissa, exponent, false).value() ==
make_float(minMantissa, -5, false).value());
BEAST_EXPECT(mantissa == minMantissa);
BEAST_EXPECT(exponent == -5);
}
{
// underflow returns canonical zero
uint64_t mantissa = 1;
int32_t exponent = minExponent;
BEAST_EXPECT(
normalize_xfl(mantissa, exponent, false).value() == 0);
BEAST_EXPECT(mantissa == 0);
BEAST_EXPECT(exponent == 0);
}
{
// exponent overflow is reported
uint64_t mantissa = minMantissa;
int32_t exponent = maxExponent + 1;
BEAST_EXPECT(
normalize_xfl(mantissa, exponent, false).error() ==
XFL_OVERFLOW);
BEAST_EXPECT(mantissa == minMantissa);
BEAST_EXPECT(exponent == maxExponent + 1);
}
}
}
void
testWithFeatures(FeatureBitset features)
{
@@ -4615,6 +4859,7 @@ public:
{
using namespace test::jtx;
testWithFeatures(supported_amendments());
testHookFloat(supported_amendments());
}
};

View File

@@ -3,11 +3,298 @@
#include <xrpld/app/misc/Transaction.h>
#include <xrpl/hook/Enum.h>
#include <cfenv>
namespace hook {
using namespace ripple;
using HookReturnCode = hook_api::hook_return_code;
namespace hook_float {
using enum hook_api::hook_return_code;
// power of 10 LUT for fast integer math
static int64_t power_of_ten[19] = {
1LL,
10LL,
100LL,
1000LL,
10000LL,
100000LL,
1000000LL,
10000000LL,
100000000LL,
1000000000LL,
10000000000LL,
100000000000LL,
1000000000000LL,
10000000000000LL,
100000000000000LL,
1000000000000000LL, // 15
10000000000000000LL,
100000000000000000LL,
1000000000000000000LL,
};
using namespace hook_api;
static int64_t const minMantissa = 1000000000000000ull;
static int64_t const maxMantissa = 9999999999999999ull;
static int32_t const minExponent = -96;
static int32_t const maxExponent = 80;
inline Expected<int32_t, HookReturnCode>
get_exponent(int64_t float1)
{
using enum hook_api::hook_return_code;
if (float1 < 0)
return Unexpected(INVALID_FLOAT);
if (float1 == 0)
return 0u;
uint64_t float_in = (uint64_t)float1;
float_in >>= 54U;
float_in &= 0xFFU;
return static_cast<int32_t>(float_in) - 97;
}
inline Expected<uint64_t, HookReturnCode>
get_mantissa(int64_t float1)
{
using enum hook_api::hook_return_code;
if (float1 < 0)
return Unexpected(INVALID_FLOAT);
if (float1 == 0)
return 0ULL;
float1 -= ((((uint64_t)float1) >> 54U) << 54U);
return float1;
}
inline bool
is_negative(int64_t float1)
{
return ((float1 >> 62U) & 1ULL) == 0;
}
inline int64_t
invert_sign(int64_t float1)
{
int64_t r = (int64_t)(((uint64_t)float1) ^ (1ULL << 62U));
return r;
}
inline int64_t
set_sign(int64_t float1, bool set_negative)
{
bool neg = is_negative(float1);
if ((neg && set_negative) || (!neg && !set_negative))
return float1;
return invert_sign(float1);
}
inline Expected<uint64_t, HookReturnCode>
set_mantissa(int64_t float1, uint64_t mantissa)
{
using enum hook_api::hook_return_code;
if (mantissa > maxMantissa)
return Unexpected(MANTISSA_OVERSIZED);
if (mantissa < minMantissa)
return Unexpected(MANTISSA_UNDERSIZED);
return float1 - get_mantissa(float1).value() + mantissa;
}
inline Expected<uint64_t, HookReturnCode>
set_exponent(int64_t float1, int32_t exponent)
{
using enum hook_api::hook_return_code;
if (exponent > maxExponent)
return Unexpected(EXPONENT_OVERSIZED);
if (exponent < minExponent)
return Unexpected(EXPONENT_UNDERSIZED);
uint64_t exp = (exponent + 97);
exp <<= 54U;
float1 &= ~(0xFFLL << 54);
float1 += (int64_t)exp;
return float1;
}
inline Expected<uint64_t, HookReturnCode>
make_float(ripple::IOUAmount& amt)
{
using enum hook_api::hook_return_code;
int64_t man_out = amt.mantissa();
int64_t float_out = 0;
bool neg = man_out < 0;
if (neg)
man_out *= -1;
float_out = set_sign(float_out, neg);
auto const mantissa = set_mantissa(float_out, (uint64_t)man_out);
if (!mantissa)
// TODO: This change requires the amendment.
// return Unexpected(mantissa.error());
float_out = (int64_t)mantissa.error();
else
float_out = mantissa.value();
auto const exponent = set_exponent(float_out, amt.exponent());
if (!exponent)
return Unexpected(exponent.error());
float_out = exponent.value();
return float_out;
}
inline Expected<uint64_t, HookReturnCode>
make_float(uint64_t mantissa, int32_t exponent, bool neg)
{
using enum hook_api::hook_return_code;
if (mantissa == 0)
return 0ULL;
if (mantissa > maxMantissa)
return Unexpected(MANTISSA_OVERSIZED);
if (mantissa < minMantissa)
return Unexpected(MANTISSA_UNDERSIZED);
if (exponent > maxExponent)
return Unexpected(EXPONENT_OVERSIZED);
if (exponent < minExponent)
return Unexpected(EXPONENT_UNDERSIZED);
int64_t out = 0;
auto const m = set_mantissa(out, mantissa);
if (!m)
return Unexpected(m.error()); // LCOV_EXCL_LINE
out = m.value();
auto const e = set_exponent(out, exponent);
if (!e)
return Unexpected(e.error()); // LCOV_EXCL_LINE
out = e.value();
out = set_sign(out, neg);
return out;
}
/**
* This function normalizes the mantissa and exponent passed, if it can.
* It returns the XFL and mutates the supplied manitssa and exponent.
* If a negative mantissa is provided then the returned XFL has the negative
* flag set. If there is an overflow error return XFL_OVERFLOW. On underflow
* returns canonical 0
*/
template <typename T>
inline Expected<uint64_t, HookReturnCode>
normalize_xfl(T& man, int32_t& exp, bool neg = false)
{
if (man == 0)
return 0ULL;
if (man == std::numeric_limits<int64_t>::min())
man++;
constexpr bool sman = std::is_same<T, int64_t>::value;
static_assert(sman || std::is_same<T, uint64_t>());
if constexpr (sman)
{
if (man < 0)
{
man *= -1LL;
neg = true;
}
}
// mantissa order
std::feclearexcept(FE_ALL_EXCEPT);
int32_t mo = log10(man);
// defensively ensure log10 produces a sane result; we'll borrow the
// overflow error code if it didn't
if (std::fetestexcept(FE_INVALID))
return Unexpected(XFL_OVERFLOW); // LCOV_EXCL_LINE
int32_t adjust = 15 - mo;
if (adjust > 0)
{
// defensive check
if (adjust > 18)
return 0ULL; // LCOV_EXCL_LINE
man *= power_of_ten[adjust];
exp -= adjust;
}
else if (adjust < 0)
{
// defensive check
if (-adjust > 18)
return Unexpected(XFL_OVERFLOW); // LCOV_EXCL_LINE
man /= power_of_ten[-adjust];
exp -= adjust;
}
if (man == 0)
{
exp = 0;
return 0ULL;
}
// even after adjustment the mantissa can be outside the range by one place
// improving the math above would probably alleviate the need for these
// branches
if (man < minMantissa)
{
if (man == minMantissa - 1LL)
man += 1LL;
else
{
man *= 10LL;
exp--;
}
}
if (man > maxMantissa)
{
if (man == maxMantissa + 1LL)
man -= 1LL;
else
{
man /= 10LL;
exp++;
}
}
if (exp < minExponent)
{
man = 0;
exp = 0;
return 0ULL;
}
if (man == 0)
{
exp = 0;
return 0ULL;
}
if (exp > maxExponent)
return Unexpected(XFL_OVERFLOW);
auto const ret = make_float((uint64_t)man, exp, neg);
if constexpr (sman)
{
if (neg)
man *= -1LL;
}
if (!ret)
return Unexpected(ret.error());
return ret;
}
const int64_t float_one_internal =
make_float(1000000000000000ull, -15, false).value();
} // namespace hook_float
using namespace ripple;
using Bytes = std::vector<std::uint8_t>;
struct HookContext; // defined in applyHook.h
@@ -170,7 +457,7 @@ public:
Expected<ripple::uint256, HookReturnCode>
hook_hash(int32_t hook_no) const;
Expected<int64_t, HookReturnCode>
Expected<uint64_t, HookReturnCode>
hook_again() const;
Expected<Blob, HookReturnCode>
@@ -320,7 +607,7 @@ private:
bool modified) const;
// these are only used by get_stobject_length below
enum parse_error {
enum class STOParseErrorCode {
pe_unexpected_end = -1,
pe_unknown_type_early = -2, // detected early
pe_unknown_type_late = -3, // end of function
@@ -329,8 +616,8 @@ private:
};
inline Expected<
int32_t,
parse_error>
uint32_t,
STOParseErrorCode>
get_stobject_length(
unsigned char* start, // in - begin iterator
unsigned char* maxptr, // in - end iterator

View File

@@ -5,285 +5,8 @@
#include <xrpld/app/ledger/TransactionMaster.h>
#include <xrpld/app/tx/detail/Import.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <cfenv>
namespace hook {
namespace hook_float {
// power of 10 LUT for fast integer math
static int64_t power_of_ten[19] = {
1LL,
10LL,
100LL,
1000LL,
10000LL,
100000LL,
1000000LL,
10000000LL,
100000000LL,
1000000000LL,
10000000000LL,
100000000000LL,
1000000000000LL,
10000000000000LL,
100000000000000LL,
1000000000000000LL, // 15
10000000000000000LL,
100000000000000000LL,
1000000000000000000LL,
};
using namespace hook_api;
static int64_t const minMantissa = 1000000000000000ull;
static int64_t const maxMantissa = 9999999999999999ull;
static int32_t const minExponent = -96;
static int32_t const maxExponent = 80;
inline Expected<int32_t, HookReturnCode>
get_exponent(int64_t float1)
{
if (float1 < 0)
return Unexpected(INVALID_FLOAT);
if (float1 == 0)
return 0;
uint64_t float_in = (uint64_t)float1;
float_in >>= 54U;
float_in &= 0xFFU;
return ((int32_t)float_in) - 97;
}
inline Expected<uint64_t, HookReturnCode>
get_mantissa(int64_t float1)
{
if (float1 < 0)
return Unexpected(INVALID_FLOAT);
if (float1 == 0)
return 0;
float1 -= ((((uint64_t)float1) >> 54U) << 54U);
return float1;
}
inline bool
is_negative(int64_t float1)
{
return ((float1 >> 62U) & 1ULL) == 0;
}
inline int64_t
invert_sign(int64_t float1)
{
int64_t r = (int64_t)(((uint64_t)float1) ^ (1ULL << 62U));
return r;
}
inline int64_t
set_sign(int64_t float1, bool set_negative)
{
bool neg = is_negative(float1);
if ((neg && set_negative) || (!neg && !set_negative))
return float1;
return invert_sign(float1);
}
inline Expected<uint64_t, HookReturnCode>
set_mantissa(int64_t float1, uint64_t mantissa)
{
if (mantissa > maxMantissa)
return Unexpected(MANTISSA_OVERSIZED);
if (mantissa < minMantissa)
return Unexpected(MANTISSA_UNDERSIZED);
return float1 - get_mantissa(float1).value() + mantissa;
}
inline Expected<uint64_t, HookReturnCode>
set_exponent(int64_t float1, int32_t exponent)
{
if (exponent > maxExponent)
return Unexpected(EXPONENT_OVERSIZED);
if (exponent < minExponent)
return Unexpected(EXPONENT_UNDERSIZED);
uint64_t exp = (exponent + 97);
exp <<= 54U;
float1 &= ~(0xFFLL << 54);
float1 += (int64_t)exp;
return float1;
}
inline Expected<uint64_t, HookReturnCode>
make_float(ripple::IOUAmount& amt)
{
int64_t man_out = amt.mantissa();
int64_t float_out = 0;
bool neg = man_out < 0;
if (neg)
man_out *= -1;
float_out = set_sign(float_out, neg);
auto const mantissa = set_mantissa(float_out, (uint64_t)man_out);
if (!mantissa)
// TODO: This change requires the amendment.
// return Unexpected(mantissa.error());
float_out = mantissa.error();
else
float_out = mantissa.value();
auto const exponent = set_exponent(float_out, amt.exponent());
if (!exponent)
return Unexpected(exponent.error());
float_out = exponent.value();
return float_out;
}
inline Expected<uint64_t, HookReturnCode>
make_float(uint64_t mantissa, int32_t exponent, bool neg)
{
if (mantissa == 0)
return 0;
if (mantissa > maxMantissa)
return Unexpected(MANTISSA_OVERSIZED);
if (mantissa < minMantissa)
return Unexpected(MANTISSA_UNDERSIZED);
if (exponent > maxExponent)
return Unexpected(EXPONENT_OVERSIZED);
if (exponent < minExponent)
return Unexpected(EXPONENT_UNDERSIZED);
int64_t out = 0;
auto const m = set_mantissa(out, mantissa);
if (!m)
return m.error();
out = m.value();
auto const e = set_exponent(out, exponent);
if (!e)
return e.error();
out = e.value();
out = set_sign(out, neg);
return out;
}
/**
* This function normalizes the mantissa and exponent passed, if it can.
* It returns the XFL and mutates the supplied manitssa and exponent.
* If a negative mantissa is provided then the returned XFL has the negative
* flag set. If there is an overflow error return XFL_OVERFLOW. On underflow
* returns canonical 0
*/
template <typename T>
inline Expected<uint64_t, HookReturnCode>
normalize_xfl(T& man, int32_t& exp, bool neg = false)
{
if (man == 0)
return 0;
if (man == std::numeric_limits<int64_t>::min())
man++;
constexpr bool sman = std::is_same<T, int64_t>::value;
static_assert(sman || std::is_same<T, uint64_t>());
if constexpr (sman)
{
if (man < 0)
{
man *= -1LL;
neg = true;
}
}
// mantissa order
std::feclearexcept(FE_ALL_EXCEPT);
int32_t mo = log10(man);
// defensively ensure log10 produces a sane result; we'll borrow the
// overflow error code if it didn't
if (std::fetestexcept(FE_INVALID))
return Unexpected(XFL_OVERFLOW);
int32_t adjust = 15 - mo;
if (adjust > 0)
{
// defensive check
if (adjust > 18)
return 0;
man *= power_of_ten[adjust];
exp -= adjust;
}
else if (adjust < 0)
{
// defensive check
if (-adjust > 18)
return Unexpected(XFL_OVERFLOW);
man /= power_of_ten[-adjust];
exp -= adjust;
}
if (man == 0)
{
exp = 0;
return 0;
}
// even after adjustment the mantissa can be outside the range by one place
// improving the math above would probably alleviate the need for these
// branches
if (man < minMantissa)
{
if (man == minMantissa - 1LL)
man += 1LL;
else
{
man *= 10LL;
exp--;
}
}
if (man > maxMantissa)
{
if (man == maxMantissa + 1LL)
man -= 1LL;
else
{
man /= 10LL;
exp++;
}
}
if (exp < minExponent)
{
man = 0;
exp = 0;
return 0;
}
if (man == 0)
{
exp = 0;
return 0;
}
if (exp > maxExponent)
return Unexpected(XFL_OVERFLOW);
auto const ret = make_float((uint64_t)man, exp, neg);
if constexpr (sman)
{
if (neg)
man *= -1LL;
}
if (!ret)
return ret.error();
return ret;
}
const int64_t float_one_internal =
make_float(1000000000000000ull, -15, false).value();
} // namespace hook_float
using namespace ripple;
using namespace hook_float;
@@ -1178,7 +901,7 @@ HookAPI::etxn_details(uint8_t* out_ptr) const
auto hash = etxn_nonce();
if (!hash.has_value())
return INTERNAL_ERROR;
return Unexpected(INTERNAL_ERROR);
memcpy(out, hash->data(), 32);
@@ -1273,7 +996,7 @@ HookAPI::float_set(int32_t exponent, int64_t mantissa) const
{
if (normalized.error() == XFL_OVERFLOW)
return Unexpected(INVALID_FLOAT);
return normalized.error();
return Unexpected(normalized.error());
}
if (normalized.value() == 0)
return Unexpected(INVALID_FLOAT);
@@ -1321,7 +1044,7 @@ HookAPI::float_mulratio(
auto const result = make_float((uint64_t)man1, exp1, is_negative(float1));
if (!result)
return result.error();
return Unexpected(result.error());
return result;
}
@@ -1932,7 +1655,7 @@ HookAPI::hook_hash(int32_t hook_no) const
return hook.getFieldH256(sfHookHash);
}
Expected<int64_t, HookReturnCode>
Expected<uint64_t, HookReturnCode>
HookAPI::hook_again() const
{
if (hookCtx.result.executeAgainAsWeak)
@@ -1941,7 +1664,7 @@ HookAPI::hook_again() const
if (hookCtx.result.isStrong)
{
hookCtx.result.executeAgainAsWeak = true;
return 1;
return 1ULL;
}
return Unexpected(PREREQUISITE_NOT_MET);
@@ -2607,7 +2330,7 @@ HookAPI::slot_float(uint32_t slot_no) const
normalized = ret.value();
}
if (normalized == EXPONENT_UNDERSIZED)
if (normalized == (int64_t)EXPONENT_UNDERSIZED)
/* exponent undersized (underflow) */
return 0; // return 0 in this case
return normalized;
@@ -3134,8 +2857,8 @@ HookAPI::set_state_cache(
// including header bytes (and footer bytes in the event of array or object)
// negative indicates error
inline Expected<
int32_t,
HookAPI::parse_error>
uint32_t,
HookAPI::STOParseErrorCode>
HookAPI::get_stobject_length(
unsigned char* start, // in - begin iterator
unsigned char* maxptr, // in - end iterator
@@ -3148,6 +2871,7 @@ HookAPI::get_stobject_length(
int recursion_depth) // used internally
const
{
using enum HookAPI::STOParseErrorCode;
if (recursion_depth > 10)
return Unexpected(pe_excessive_nesting);
@@ -3156,7 +2880,7 @@ HookAPI::get_stobject_length(
: STI_VECTOR256;
if (type > max_sti_type)
return pe_unknown_type_early;
return Unexpected(pe_unknown_type_early);
unsigned char* end = maxptr;
unsigned char* upto = start;
@@ -3216,7 +2940,7 @@ HookAPI::get_stobject_length(
// not supported types
if (type == STI_NUMBER || type == STI_UINT96 || type == STI_UINT192 ||
type == STI_UINT384 || type == STI_UINT512)
return pe_unknown_type_early;
return Unexpected(pe_unknown_type_early);
bool is_vl =
(type == STI_ACCOUNT || type == STI_VL ||
@@ -3286,7 +3010,7 @@ HookAPI::get_stobject_length(
length = 20;
break;
default:
return -1;
return Unexpected(pe_unknown_type_late);
}
}
else if (type == STI_AMOUNT) /* AMOUNT */
@@ -3308,7 +3032,7 @@ HookAPI::get_stobject_length(
int flag = *(upto + length++);
// flag shoud be 0x01 or 0x10 or 0x20 or those union
if (flag == 0 || flag & ~(0x01 | 0x10 | 0x20))
return pe_unexpected_end;
return Unexpected(pe_unexpected_end);
if (flag & 0x01) // account
length += 20;
if (flag & 0x10) // currency
@@ -3329,10 +3053,10 @@ HookAPI::get_stobject_length(
else if (lastflag == 0x00)
break; // end byte
else
return pe_unexpected_end;
return Unexpected(pe_unexpected_end);
}
if (upto >= end)
return pe_unexpected_end;
return Unexpected(pe_unexpected_end);
}
else if (type == STI_ISSUE)
{

View File

@@ -14,8 +14,6 @@
#include <xrpl/protocol/st.h>
#include <xrpl/protocol/tokens.h>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <any>
#include <cfenv>
#include <memory>
#include <optional>
#include <string>
@@ -598,160 +596,9 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
} // namespace hook
namespace hook_float {
using namespace hook_api;
static int64_t const minMantissa = 1000000000000000ull;
static int64_t const maxMantissa = 9999999999999999ull;
static int32_t const minExponent = -96;
static int32_t const maxExponent = 80;
inline int32_t
get_exponent(int64_t float1)
{
if (float1 < 0)
return INVALID_FLOAT;
if (float1 == 0)
return 0;
uint64_t float_in = (uint64_t)float1;
float_in >>= 54U;
float_in &= 0xFFU;
return ((int32_t)float_in) - 97;
}
inline int64_t
get_mantissa(int64_t float1)
{
if (float1 < 0)
return INVALID_FLOAT;
if (float1 == 0)
return 0;
float1 -= ((((uint64_t)float1) >> 54U) << 54U);
return float1;
}
inline bool
is_negative(int64_t float1)
{
return ((float1 >> 62U) & 1ULL) == 0;
}
inline int64_t
invert_sign(int64_t float1)
{
int64_t r = (int64_t)(((uint64_t)float1) ^ (1ULL << 62U));
return r;
}
inline int64_t
set_sign(int64_t float1, bool set_negative)
{
bool neg = is_negative(float1);
if ((neg && set_negative) || (!neg && !set_negative))
return float1;
return invert_sign(float1);
}
inline int64_t
set_mantissa(int64_t float1, uint64_t mantissa)
{
if (mantissa > maxMantissa)
return MANTISSA_OVERSIZED;
if (mantissa < minMantissa)
return MANTISSA_UNDERSIZED;
return float1 - get_mantissa(float1) + mantissa;
}
inline int64_t
set_exponent(int64_t float1, int32_t exponent)
{
if (exponent > maxExponent)
return EXPONENT_OVERSIZED;
if (exponent < minExponent)
return EXPONENT_UNDERSIZED;
uint64_t exp = (exponent + 97);
exp <<= 54U;
float1 &= ~(0xFFLL << 54);
float1 += (int64_t)exp;
return float1;
}
inline int64_t
make_float(ripple::IOUAmount& amt)
{
int64_t man_out = amt.mantissa();
int64_t float_out = 0;
bool neg = man_out < 0;
if (neg)
man_out *= -1;
float_out = set_sign(float_out, neg);
float_out = set_mantissa(float_out, (uint64_t)man_out);
float_out = set_exponent(float_out, amt.exponent());
return float_out;
}
inline int64_t
make_float(uint64_t mantissa, int32_t exponent, bool neg)
{
if (mantissa == 0)
return 0;
if (mantissa > maxMantissa)
return MANTISSA_OVERSIZED;
if (mantissa < minMantissa)
return MANTISSA_UNDERSIZED;
if (exponent > maxExponent)
return EXPONENT_OVERSIZED;
if (exponent < minExponent)
return EXPONENT_UNDERSIZED;
int64_t out = 0;
out = set_mantissa(out, mantissa);
out = set_exponent(out, exponent);
out = set_sign(out, neg);
return out;
}
} // namespace hook_float
using namespace hook_float;
using namespace hook::hook_float;
using hook::Bytes;
inline int32_t
no_free_slots(hook::HookContext& hookCtx)
{
return hook_api::max_slots - hookCtx.slot.size() <= 0;
}
inline std::optional<int32_t>
get_free_slot(hook::HookContext& hookCtx)
{
// allocate a slot
int32_t slot_into = 0;
if (hookCtx.slot_free.size() > 0)
{
slot_into = hookCtx.slot_free.front();
hookCtx.slot_free.pop();
return slot_into;
}
// no slots were available in the queue so increment slot counter until we
// find a free slot usually this will be the next available but the hook
// developer may have allocated any slot ahead of when the counter gets
// there
do
{
slot_into = ++hookCtx.slot_counter;
} while (hookCtx.slot.find(slot_into) != hookCtx.slot.end() &&
// this condition should always be met, if for some reason, somehow
// it is not then we will return the final slot every time.
hookCtx.slot_counter <= hook_api::max_slots);
if (hookCtx.slot_counter > hook_api::max_slots)
return {};
return slot_into;
}
// cu_ptr is a pointer into memory, bounds check is assumed to have already
// happened
inline std::optional<Currency>
@@ -807,7 +654,7 @@ parseCurrency(uint8_t* cu_ptr, uint32_t cu_len)
return {};
}
inline int64_t
inline std::variant<uint64_t, hook_api::hook_return_code>
serialize_keylet(
ripple::Keylet& kl,
uint8_t* memory,
@@ -815,7 +662,7 @@ serialize_keylet(
uint32_t write_len)
{
if (write_len < 34)
return hook_api::TOO_SMALL;
return TOO_SMALL;
memory[write_ptr + 0] = (kl.type >> 8) & 0xFFU;
memory[write_ptr + 1] = (kl.type >> 0) & 0xFFU;
@@ -823,7 +670,7 @@ serialize_keylet(
for (int i = 0; i < 32; ++i)
memory[write_ptr + 2 + i] = kl.key.data()[i];
return 34;
return 34ULL;
}
std::optional<ripple::Keylet>
@@ -866,19 +713,19 @@ hook::computeCreationFee(uint64_t byteCount)
}
// many datatypes can be encoded into an int64_t
inline int64_t
inline std::variant<uint64_t, hook_api::hook_return_code>
data_as_int64(void const* ptr_raw, uint32_t len)
{
if (len > 8)
return hook_api::hook_return_code::TOO_BIG;
return TOO_BIG;
uint8_t const* ptr = reinterpret_cast<uint8_t const*>(ptr_raw);
uint64_t output = 0;
for (int i = 0, j = (len - 1) * 8; i < len; ++i, j -= 8)
output += (((uint64_t)ptr[i]) << j);
if ((1ULL << 63U) & output)
return hook_api::hook_return_code::TOO_BIG;
return (int64_t)output;
return TOO_BIG;
return output;
}
/* returns true iff every even char is ascii and every odd char is 00
@@ -1265,7 +1112,7 @@ DEFINE_HOOK_FUNCTION(
return OUT_OF_BOUNDS;
if (!j.trace())
return 0;
return 0ULL;
if (read_len > 128)
read_len = 128;
@@ -1283,12 +1130,12 @@ DEFINE_HOOK_FUNCTION(
(const char*)memory + read_ptr, read_len)
<< ": " << number;
return 0;
return 0ULL;
}
}
j.trace() << "HookTrace[" << HC_ACC() << "]: " << number;
return 0;
return 0ULL;
HOOK_TEARDOWN();
}
@@ -1308,7 +1155,7 @@ DEFINE_HOOK_FUNCTION(
return OUT_OF_BOUNDS;
if (!j.trace())
return 0;
return 0ULL;
if (mread_len > 128)
mread_len = 128;
@@ -1371,7 +1218,7 @@ DEFINE_HOOK_FUNCTION(
<< std::string_view((const char*)output_storage, out_len);
}
return 0;
return 0ULL;
HOOK_TEARDOWN();
}
@@ -1476,7 +1323,8 @@ DEFINE_HOOK_FUNCTION(
auto const sleAccount = view.peek(hookCtx.result.accountKeylet);
if (!sleAccount && view.rules().enabled(featureExtendedHookState))
return tefINTERNAL;
// should return hook_api::hook_return_code
return static_cast<hook_api::hook_return_code>(tefINTERNAL);
uint16_t const hookStateScale = sleAccount->isFieldPresent(sfHookStateScale)
? sleAccount->getFieldU16(sfHookStateScale)
@@ -1500,7 +1348,8 @@ DEFINE_HOOK_FUNCTION(
{
auto const sleAccount = view.peek(hookCtx.result.accountKeylet);
if (!sleAccount)
return tefINTERNAL;
// should return hook_api::hook_return_code
return static_cast<hook_api::hook_return_code>(tefINTERNAL);
}
if (!key)
@@ -1715,7 +1564,8 @@ hook::finalizeHookResult(
// add a metadata entry for this hook execution result
{
STObject meta{sfHookExecution};
meta.setFieldU8(sfHookResult, hookResult.exitType);
meta.setFieldU8(
sfHookResult, static_cast<uint8_t>(hookResult.exitType));
meta.setAccountID(sfHookAccount, hookResult.account);
// RH NOTE: this is probably not necessary, a direct cast should always
@@ -2225,7 +2075,7 @@ DEFINE_HOOK_FUNCTION(int64_t, slot_type, uint32_t slot_no, uint32_t flags)
if (flags == 0)
{
auto const base = std::get<0>(*result);
return base.getFName().fieldCode;
return static_cast<uint64_t>(base.getFName().fieldCode);
}
else
{
@@ -2836,7 +2686,8 @@ DEFINE_HOOK_FUNCTION(
if (NOT_IN_BOUNDS(write_ptr, txID.size(), memory_length))
return OUT_OF_BOUNDS;
auto const write_txid = [&]() -> int64_t {
auto const write_txid =
[&]() -> std::variant<uint64_t, hook_api::hook_return_code> {
WRITE_WASM_MEMORY_AND_RETURN(
write_ptr,
txID.size(),
@@ -2846,12 +2697,15 @@ DEFINE_HOOK_FUNCTION(
memory_length);
};
int64_t result = write_txid();
auto result = write_txid();
if (std::holds_alternative<hook_api::hook_return_code>(result))
return std::get<hook_api::hook_return_code>(result);
if (result == 32)
auto const value = std::get<uint64_t>(result);
if (value == 32)
hookCtx.result.emittedTxn.push(tpTrans);
return result;
return value;
HOOK_TEARDOWN();
}
@@ -3340,7 +3194,7 @@ DEFINE_HOOK_FUNCTION(
uint32_t field_id)
{
// proxy only no setup or teardown
int64_t ret = sto_emplace(
auto ret = sto_emplace(
hookCtx,
frameCtx,
write_ptr,
@@ -3351,8 +3205,12 @@ DEFINE_HOOK_FUNCTION(
0,
field_id);
if (ret > 0 && ret == read_len)
return DOESNT_EXIST;
if (std::holds_alternative<uint64_t>(ret))
{
auto const value = std::get<uint64_t>(ret);
if (value > 0 && value == read_len)
return DOESNT_EXIST;
}
return ret;
}
@@ -3375,7 +3233,7 @@ DEFINE_HOOK_FUNCTION(
auto const result = api.sto_validate(data);
if (!result)
return result.error();
return result.value() ? 1 : 0;
return result.value() ? 1ULL : 0ULL;
HOOK_TEARDOWN();
}
@@ -3411,7 +3269,7 @@ DEFINE_HOOK_FUNCTION(
auto const result = api.util_verify(data, sig, key);
if (!result)
return result.error();
return result.value() ? 1 : 0;
return result.value() ? 1ULL : 0ULL;
HOOK_TEARDOWN();
}
@@ -3506,26 +3364,30 @@ DEFINE_HOOK_FUNCTION(int32_t, _g, uint32_t id, uint32_t maxitr)
<< "Iterations: " << hookCtx.guard_map[id];
}
hookCtx.result.exitType = hook_api::ExitType::ROLLBACK;
hookCtx.result.exitCode = GUARD_VIOLATION;
hookCtx.result.exitCode = (int64_t)GUARD_VIOLATION;
return RC_ROLLBACK;
}
return 1;
return 1U;
HOOK_TEARDOWN();
}
#define RETURN_IF_INVALID_FLOAT(float1) \
{ \
if (float1 < 0) \
return hook_api::INVALID_FLOAT; \
if (float1 != 0) \
{ \
uint64_t mantissa = get_mantissa(float1); \
int32_t exponent = get_exponent(float1); \
if (mantissa < minMantissa || mantissa > maxMantissa || \
exponent > maxExponent || exponent < minExponent) \
return INVALID_FLOAT; \
} \
#define RETURN_IF_INVALID_FLOAT(float1) \
{ \
if (float1 < 0) \
return INVALID_FLOAT; \
if (float1 != 0) \
{ \
auto const mantissa = get_mantissa(float1); \
auto const exponent = get_exponent(float1); \
if (!mantissa || !exponent) \
return INVALID_FLOAT; \
if (mantissa.value() < minMantissa || \
mantissa.value() > maxMantissa || \
exponent.value() > maxExponent || \
exponent.value() < minExponent) \
return INVALID_FLOAT; \
} \
}
DEFINE_HOOK_FUNCTION(
@@ -3542,7 +3404,7 @@ DEFINE_HOOK_FUNCTION(
return OUT_OF_BOUNDS;
if (!j.trace())
return 0;
return 0ULL;
if (read_len > 128)
read_len = 128;
@@ -3552,38 +3414,33 @@ DEFINE_HOOK_FUNCTION(
*((const char*)memory + read_ptr + read_len - 1) == '\0')
read_len--;
auto const messageKey = (read_len == 0)
? ""
: std::string_view((const char*)memory + read_ptr, read_len);
if (float1 == 0)
{
j.trace() << "HookTrace[" << HC_ACC() << "]: "
<< (read_len == 0
? ""
: std::string_view(
(const char*)memory + read_ptr, read_len))
j.trace() << "HookTrace[" << HC_ACC() << "]: " << messageKey
<< ": Float 0*10^(0) <ZERO>";
return 0;
return 0ULL;
}
uint64_t man = get_mantissa(float1);
int32_t exp = get_exponent(float1);
auto const man = get_mantissa(float1);
auto const exp = get_exponent(float1);
bool neg = is_negative(float1);
if (man < minMantissa || man > maxMantissa || exp < minExponent ||
exp > maxExponent)
if (!man || !exp || man.value() < minMantissa ||
man.value() > maxMantissa || exp.value() < minExponent ||
exp.value() > maxExponent)
{
j.trace() << "HookTrace[" << HC_ACC() << "]:"
<< (read_len == 0
? ""
: std::string_view(
(const char*)memory + read_ptr, read_len))
j.trace() << "HookTrace[" << HC_ACC() << "]: " << messageKey
<< ": Float <INVALID>";
return 0;
return 0ULL;
}
j.trace() << "HookTrace[" << HC_ACC() << "]:"
<< (read_len == 0 ? ""
: std::string_view(
(const char*)memory + read_ptr, read_len))
<< ": Float " << (neg ? "-" : "") << man << "*10^(" << exp << ")";
return 0;
j.trace() << "HookTrace[" << HC_ACC() << "]:" << messageKey << ": Float "
<< (neg ? "-" : "") << man.value() << "*10^(" << exp.value()
<< ")";
return 0ULL;
HOOK_TEARDOWN();
}