## Nan-boxing技术介绍

NaN-boxing看起来像英文翻译的“南拳”，其实它是表示一个无效的double数。NaN-boxing技术：通过一个64位的数字来表示多种数据类型的技术，它通过一个nan浮点数来保存数据，根据IEEE-754浮点数标准，double类型的NAN形式为：
sign
| exponent
|     |
[0][11111111111][yyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
|      |
tag     |
为了演示一下nan-boxing的应用，我写一个简单的例子来展示一下它是如何存储数据的，看起来就像一个动态类型。

#pragma once
#include <cstddef>
#include <assert.h>
#include <cstdint>
using namespace std;

enum TypeTag {
BOOL,
INT32,
UINT32,
UINT64,
INT64,
DOUBLE,
CHAR_ARRAY,
STRING,
NIL
};

#define TAG_SHIFT 47

union Value {
uint64_t ival;
double fval;

Value(double x) : fval(x)
{
}

Value(int v)
{
ival = NAN_MASK | ((uint64_t)INT32 << TAG_SHIFT) | (uint64_t)v;
}
Value(uint32_t v)
{
ival = NAN_MASK | ((uint64_t)UINT32 << TAG_SHIFT) | (uint64_t)v;
}

Value(int64_t v)
{
ival = static_cast<uint64_t>(v);
}

Value(uint64_t v)
{
ival = v;
}

Value(bool v)
{
ival = NAN_MASK | ((uint64_t)BOOL << TAG_SHIFT) | (uint64_t)v;
}

Value(const char* v)
{
ival = NAN_MASK | ((uint64_t)CHAR_ARRAY << TAG_SHIFT) | (uint64_t)v;
}

Value(const string& v)
{
ival = NAN_MASK | ((uint64_t)STRING << TAG_SHIFT) | (uint64_t)&v;
}

Value(TypeTag tag = NIL, void *payload = nullptr) {
}

int toInt() const
{
assert(getTag() == INT32);
}

int64_t toInt64() const
{
return (int64_t)ival;
}

uint32_t toUInt() const
{
assert(getTag() == UINT32);
}

uint64_t toUInt64() const
{
return ival;
}

bool toBool() const
{
assert(getTag() == BOOL);
}

double toDouble() const
{
assert(getTag() == DOUBLE);
return fval;
}

char *toCharArray() const
{
assert(getTag() == CHAR_ARRAY);
}

string& toString() const
{
assert(getTag() == STRING);
}

TypeTag getTag() const
{
}

{
}

bool operator<(const Value& other)  const
{
return hash()<other.hash();
}

bool operator==(const Value& other)  const
{
return hash() == other.hash();
}
private:
{
}

uint64_t toHashUInt64() const
{
assert(getTag() < INT64);
if (getTag() == UINT64)
return ival;

}

int64_t toHashInt64() const
{
assert(getTag() == INT64);
}

std::size_t hash() const {
switch (getTag()) {
case         UINT64:
case         INT32:
case         UINT32:
case           BOOL:
return std::hash<uint64_t>()(toHashUInt64());
case         INT64:
return std::hash<int64_t>()(toHashInt64());
case DOUBLE:
return std::hash<double>()(toDouble());
case STRING:
return std::hash<std::string>()(toString());
default:
throw std::invalid_argument("can not find this type");
}
}
};

void Test()
{
Value t="a";
cout << t.toCharArray() << endl;

Value v = (int)2;
auto r0 = v.toInt();

Value v1 = (uint32_t)2;
auto r1 = v1.toUInt();
Value v2 = (int64_t)2;
auto r2 = v2.toInt64();
Value v3 = (uint64_t)2;
auto r3 = v3.toUInt64();

Value v4 = 2.5;
auto r4 = v4.toDouble();

string s1 = "a";
Value v5 = s1;
auto r5 = v5.toString();
string s = r5;

Value b = false;
bool r = b.toBool();
b = true;
r = b.toBool();
}