整理代码

This commit is contained in:
蒲爽 2026-09-28 21:33:04 +08:00
commit c7b4e178ec
364 changed files with 59691 additions and 0 deletions

57
util/AESUtil.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef A89D0C3C_4F05_4631_A720_D8B2CEA5E99A
#define A89D0C3C_4F05_4631_A720_D8B2CEA5E99A
#include <Poco/Base64Decoder.h>
#include <Poco/Base64Encoder.h>
#include <Poco/Crypto/Cipher.h>
#include <Poco/Crypto/CipherFactory.h>
#include <Poco/Crypto/CipherKey.h>
#include <Poco/StreamCopier.h>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "Logger.h"
namespace Util
{
class AESUtil
{
public:
static std::string encryptAES(const std::string& plaintext, const std::string& key, const std::string& iv)
{
try
{
Poco::Crypto::CipherKey cipherKey("aes-256-cbc", key, iv);
Poco::Crypto::CipherFactory& factory = Poco::Crypto::CipherFactory::defaultFactory();
std::unique_ptr<Poco::Crypto::Cipher> pCipher(factory.createCipher(cipherKey));
std::string ciphertext = pCipher->encryptString(plaintext, Poco::Crypto::Cipher::ENC_BASE64_NO_LF);
return ciphertext;
}
catch (const Poco::Exception& e)
{
logger().error("encryptAES error: %s", std::string(e.what()));
return "";
}
}
static std::string decryptAES(const std::string& ciphertext, const std::string& key, const std::string& iv)
{
try
{
Poco::Crypto::CipherKey cipherKey("aes-256-cbc", key, iv);
Poco::Crypto::CipherFactory& factory = Poco::Crypto::CipherFactory::defaultFactory();
std::unique_ptr<Poco::Crypto::Cipher> pCipher(factory.createCipher(cipherKey));
std::string plaintext = pCipher->decryptString(ciphertext, Poco::Crypto::Cipher::ENC_BASE64_NO_LF);
return plaintext;
}
catch (const Poco::Exception& e)
{
logger().error("decryptAES error: %s", std::string(e.what()));
return "";
}
}
};
}
#endif

35
util/Base64Util.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef CFCB972C_1202_49D2_AAD4_9066BA17F94F
#define CFCB972C_1202_49D2_AAD4_9066BA17F94F
#include <iostream>
#include <sstream>
#include <string>
#include "Poco/Base64Decoder.h"
#include "Poco/Base64Encoder.h"
#include "Poco/StreamCopier.h"
namespace Util
{
class Base64Util
{
private:
public:
static std::string base64Encode(const std::string& input)
{
std::ostringstream oss;
Poco::Base64Encoder encoder(oss, 0);
encoder.write(input.data(), input.size());
encoder.close();
return oss.str();
}
static std::string base64Decode(const std::string& input)
{
std::istringstream iss(input);
Poco::Base64Decoder decoder(iss);
std::ostringstream oss;
Poco::StreamCopier::copyStream(decoder, oss);
return oss.str();
}
};
}
#endif

144
util/Broadcast.cpp Normal file
View file

@ -0,0 +1,144 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Broadcast.h"
#include <Poco/Mutex.h>
#include <Poco/ScopedLock.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
class BroadcastObserver
{
public:
BroadcastObserver(BroadcastReceiver& receiver, IntentFilter::Ptr pFilter): _pReceiver(&receiver), _pFilter(pFilter)
{
}
void broadcast(Intent::Ptr pIntent)
{
if (_pFilter->match(pIntent))
_pReceiver->onReceive(pIntent);
}
bool equals(const BroadcastObserver& observer)
{
return _pReceiver == observer._pReceiver;
}
bool isBoundTo(BroadcastReceiver& receiver)
{
return _pReceiver == &receiver;
}
private:
BroadcastReceiver* _pReceiver;
IntentFilter::Ptr _pFilter;
};
class BroadcastCore
{
public:
void sendBroadcast(Intent::Ptr pIntent)
{
Poco::ScopedLockWithUnlock<Poco::Mutex> lock(_mutex);
BroadcastObserverList list(_broadcastObservers);
lock.unlock();
for (auto& p : list)
p->broadcast(pIntent);
}
void registerReceiver(BroadcastReceiver& receiver, IntentFilter::Ptr pFilter)
{
Poco::Mutex::ScopedLock lock(_mutex);
for (auto& obs : _broadcastObservers)
{
if (obs->isBoundTo(receiver))
return;
}
_broadcastObservers.push_back(new BroadcastObserver(receiver, pFilter));
}
void unregisterReceiver(BroadcastReceiver& receiver)
{
Poco::Mutex::ScopedLock lock(_mutex);
for (BroadcastObserverList::iterator it = _broadcastObservers.begin(); it != _broadcastObservers.end();)
{
if ((*it)->isBoundTo(receiver))
it = _broadcastObservers.erase(it);
else
++it;
}
}
private:
using BroadcastObserverPtr = Poco::SharedPtr<BroadcastObserver>;
using BroadcastObserverList = std::vector<BroadcastObserverPtr>;
BroadcastObserverList _broadcastObservers;
mutable Poco::Mutex _mutex;
};
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Intent& Intent::putExtra(const std::string& key, const char* value)
{
return putExtra(key, std::string(value));
}
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
void IntentFilter::addAction(const std::string& action)
{
_actions.push_back(action);
}
bool IntentFilter::match(Intent::Ptr pIntent) const
{
const std::string& target = pIntent->getAction();
return std::find(_actions.begin(), _actions.end(), target) != _actions.end();
}
/*
*******************************************************************************
* LOCAL VARIABLES
*******************************************************************************
*/
namespace
{
static BroadcastCore core;
}
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
void BroadcastSender::sendBroadcast(Intent::Ptr pIntent)
{
core.sendBroadcast(pIntent);
}
void BroadcastReceiverRegistry::registerReceiver(BroadcastReceiver& receiver, IntentFilter::Ptr pFilter)
{
core.registerReceiver(receiver, pFilter);
}
void BroadcastReceiverRegistry::unregisterReceiver(BroadcastReceiver& receiver)
{
core.unregisterReceiver(receiver);
}
}

117
util/Broadcast.h Normal file
View file

@ -0,0 +1,117 @@
#ifndef C81E1B2C_54AF_4E7D_800B_F21E0DACFD8B
#define C81E1B2C_54AF_4E7D_800B_F21E0DACFD8B
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <unordered_map>
#include <vector>
#include <Poco/Format.h>
#include <Poco/SharedPtr.h>
#include <Poco/Any.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Intent
{
public:
using Ptr = Poco::SharedPtr<Intent>;
template <class T> Intent& putExtra(const std::string& key, const T& value);
Intent& putExtra(const std::string& key, const char* value);
template <class T> const T getExtra(const std::string& key) const;
const std::string& getAction() const;
void setAction(const std::string& action);
private:
using Value = Poco::Any;
using Values = std::unordered_map<std::string, Value>;
Values _values;
std::string _action;
};
template <class T> inline Intent& Intent::putExtra(const std::string& key, const T& value)
{
_values[key] = value;
return *this;
}
template <class T> inline const T Intent::getExtra(const std::string& key) const
{
if (_values.find(key) == _values.end())
throw Poco::NotFoundException(Poco::format("Intent: Can't find value: %s", key), -ENODATA);
return Poco::AnyCast<T>(_values.at(key));
}
inline const std::string& Intent::getAction() const
{
return _action;
}
inline void Intent::setAction(const std::string& action)
{
_action = action;
}
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class IntentFilter
{
public:
using Ptr = Poco::SharedPtr<IntentFilter>;
void addAction(const std::string& action);
bool match(Intent::Ptr pIntent) const;
private:
std::vector<std::string> _actions;
};
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class BroadcastReceiver
{
public:
virtual void onReceive(Intent::Ptr pIntent) = 0;
};
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class BroadcastSender
{
public:
void sendBroadcast(Intent::Ptr pIntent);
};
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class BroadcastReceiverRegistry
{
public:
void registerReceiver(BroadcastReceiver& receiver, IntentFilter::Ptr pFilter);
void unregisterReceiver(BroadcastReceiver& receiver);
};
}
#endif

15
util/BuilderSet.cpp Normal file
View file

@ -0,0 +1,15 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "BuilderSet.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
}

63
util/BuilderSet.h Normal file
View file

@ -0,0 +1,63 @@
#ifndef BE7ED734_3D5C_4600_89C3_4082EB263BE0
#define BE7ED734_3D5C_4600_89C3_4082EB263BE0
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/SharedPtr.h>
#include <map>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
template <class T, class TKey, class TSelector, class... TArgs> class BuilderSet
{
private:
class IBuilder
{
public:
using Ptr = Poco::SharedPtr<IBuilder>;
virtual ~IBuilder()
{
}
virtual T* build(TArgs... args) = 0;
};
template <class C> class Builder: public IBuilder
{
public:
C* build(TArgs... args) override
{
C* p = new C(args...);
return p;
}
};
using Builders = std::map<TKey, typename IBuilder::Ptr>;
public:
static T* build(TArgs... args)
{
TKey key = TSelector::select(args...);
return _builders[key]->build(args...);
}
template <class C> static void registerClass(TKey key)
{
_builders[key] = new Builder<C>();
}
private:
static Builders _builders;
};
template <class T, class TKey, class TSelect, class... TArgs>
typename BuilderSet<T, TKey, TSelect, TArgs...>::Builders BuilderSet<T, TKey, TSelect, TArgs...>::_builders;
}
#endif

5
util/CMakeLists.txt Normal file
View file

@ -0,0 +1,5 @@
set(target util)
aux_source_directory(. UTIL_SOURCES)
add_library(${target} STATIC ${UTIL_SOURCES} )

28
util/ClassAssistant.cpp Normal file
View file

@ -0,0 +1,28 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "ClassAssistant.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
std::string Util::ClassAssistant::demangle(const std::type_info& ti)
{
int status;
char* demangled = abi::__cxa_demangle(ti.name(), nullptr, nullptr, &status);
if (status == 0)
{
std::string result(demangled);
free(demangled);
return result;
}
return ti.name();
}
}

68
util/ClassAssistant.h Normal file
View file

@ -0,0 +1,68 @@
#ifndef AD5404F9_7A8A_45FF_8ABE_66F7E9E53212
#define AD5404F9_7A8A_45FF_8ABE_66F7E9E53212
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <cxxabi.h>
#include <string>
#include <typeinfo>
/*
*******************************************************************************
* MACROS
*******************************************************************************
*/
#define PROPERTY_DECLARE(name, type, defaultValue, setAccessibility, getAccessibility) \
private: \
type _##name = defaultValue; \
setAccessibility: \
void set##name(const type& value) \
{ \
_##name = value; \
} \
getAccessibility: \
type get##name() \
{ \
return _##name; \
}
#define READONLY_PROPERTY_DECLARE(name, type) \
private: \
type _##name; \
\
public: \
const type& name() \
{ \
return _##name; \
}
namespace Util
{
/*
*******************************************************************************
* FUNCTIONS
*******************************************************************************
*/
class ClassAssistant
{
public:
template <typename T> static std::string demangle();
template <typename T> static std::string demangle(const T& object);
static std::string demangle(const std::type_info& ti);
};
template <typename T> std::string ClassAssistant::demangle()
{
return demangle(typeid(T));
}
template <typename T> inline std::string ClassAssistant::demangle(const T& object)
{
return demangle(typeid(object));
}
}
#endif

26
util/Configurable.cpp Normal file
View file

@ -0,0 +1,26 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Configurable.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
void Configurable::commit()
{
ParameterNames names;
config().getChangedParameters(names);
if (names.size())
{
onConfigChanged(names);
for (auto& name : names)
config().confirmParameterChanged(name);
}
}
}

55
util/Configurable.h Normal file
View file

@ -0,0 +1,55 @@
#ifndef E6D074E1_FEDB_4EFB_902B_95FF6BD2EB09
#define E6D074E1_FEDB_4EFB_902B_95FF6BD2EB09
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Configuration.h"
#include <unordered_set>
/*
*******************************************************************************
* MACROS
*******************************************************************************
*/
#define CONFIGURABLE_DECLARE_PARAMETER(name, type) \
type get##name() \
{ \
return config().getParameter<type>(#name); \
} \
void set##name(const type value) \
{ \
config().setParameter(#name, value); \
} \
const std::string _##name = (#name);
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Configurable
{
public:
using ParameterNames = Configuration::ParameterNames;
public:
void commit();
Configuration& config();
protected:
virtual void onConfigChanged(ParameterNames& names) = 0;
private:
Configuration _config;
};
inline Configuration& Configurable::config()
{
return _config;
}
}
#endif

33
util/Configuration.cpp Normal file
View file

@ -0,0 +1,33 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Configuration.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
bool Configuration::ParameterNames::exists(std::string name) const
{
return find(name) != end();
}
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Configuration::~Configuration()
{
}
void Configuration::copyTo(Configuration& other)
{
other._parameters = _parameters;
}
}

140
util/Configuration.h Normal file
View file

@ -0,0 +1,140 @@
#ifndef __CONFIG_CONFIGURATION_H__
#define __CONFIG_CONFIGURATION_H__
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <iostream>
#include <map>
#include <unordered_set>
#include "Poco/Any.h"
#include "Poco/Exception.h"
#include "Poco/Format.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Configuration
{
private:
class Parameter
{
public:
template <class T> T getValue()
{
return Poco::AnyCast<T>(_value);
}
void setValue(const Poco::Any& value)
{
this->_value = value;
}
void setChanged(bool changed)
{
this->_changed = changed;
}
bool getChanged()
{
return _changed;
}
bool empty()
{
return _value.empty();
}
Poco::Any& value()
{
return _value;
}
private:
Poco::Any _value;
bool _changed;
};
public:
class ParameterNames: public std::unordered_set<std::string>
{
public:
bool exists(std::string name) const;
};
public:
virtual ~Configuration();
template <class T> Configuration& setParameter(const std::string& name, const T& value)
{
Parameter& parameter = _parameters[name];
if (parameter.empty() || parameter.getValue<T>() != value)
{
parameter.setChanged(true);
parameter.setValue(value);
}
return *this;
}
template <class T> T getParameter(const std::string& name)
{
return findParameter(name).getValue<T>();
}
bool isParameterChanged(const std::string& name)
{
return findParameter(name).getChanged();
}
void confirmParameterChanged(const std::string& name)
{
findParameter(name).setChanged(false);
}
void getChangedParameters(ParameterNames& names)
{
names.clear();
for (auto& p : _parameters)
{
if (p.second.getChanged())
names.insert(p.first);
}
}
const std::string& name() const
{
return _name;
}
void copyTo(Configuration& other);
protected:
void setName(std::string& name)
{
_name = name;
}
Parameter& findParameter(const std::string& name)
{
if (_parameters.find(name) == _parameters.end())
throw Poco::NotFoundException(Poco::format("Can't find parameter: %s", name), -ENODATA);
return _parameters[name];
}
private:
using Parameters = std::map<std::string, Parameter>;
Parameters _parameters;
std::string _name;
};
}
#endif

467
util/DateTimeUtils.h Normal file
View file

@ -0,0 +1,467 @@
#ifndef A283B06E_3ED5_4D78_A2B5_4B87495A80A6
#define A283B06E_3ED5_4D78_A2B5_4B87495A80A6
#include <chrono>
#include <cmath>
#include <ctime>
#include <string>
#include "Poco/DateTime.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/DateTimeParser.h"
#include "Poco/Exception.h"
#include "Poco/LocalDateTime.h"
#include "Poco/Timespan.h"
#include "Poco/Timestamp.h"
namespace Util
{
class DateTimeUtils
{
public:
static Poco::DateTime now()
{
return Poco::DateTime();
}
static Poco::LocalDateTime localNow()
{
return Poco::LocalDateTime();
}
static std::string nowStr(const std::string& formatStr = "%Y-%m-%d %H:%M:%S")
{
return format(Poco::DateTime(), formatStr);
}
static std::string localNowStr(const std::string& formatStr = "%Y-%m-%d %H:%M:%S")
{
return Poco::DateTimeFormatter::format(Poco::LocalDateTime(), formatStr);
}
static Poco::DateTime parse(const std::string& str, const std::string& formatStr = "")
{
auto dt = Poco::DateTime(1970, 1, 1);
if (str.empty())
{
return dt;
}
int timeZoneDifferential = 0;
if (!formatStr.empty())
{
if (Poco::DateTimeParser::tryParse(formatStr, str, dt, timeZoneDifferential))
{
return dt;
}
}
else
{
if (Poco::DateTimeParser::tryParse(Poco::DateTimeFormat::ISO8601_FORMAT, str, dt, timeZoneDifferential) ||
Poco::DateTimeParser::tryParse("%Y-%m-%d %H:%M:%S", str, dt, timeZoneDifferential) ||
Poco::DateTimeParser::tryParse("%Y/%m/%d %H:%M:%S", str, dt, timeZoneDifferential) ||
Poco::DateTimeParser::tryParse("%Y%m%d%H%M%S", str, dt, timeZoneDifferential))
{
return dt;
}
}
return dt;
}
static std::string format(const Poco::DateTime& dt, const std::string& formatStr = "%Y-%m-%d %H:%M:%S")
{
return Poco::DateTimeFormatter::format(dt, formatStr);
}
static std::string toISO8601(const Poco::DateTime& dt)
{
return Poco::DateTimeFormatter::format(dt, Poco::DateTimeFormat::ISO8601_FORMAT);
}
static std::string toRFC1123(const Poco::DateTime& dt)
{
return Poco::DateTimeFormatter::format(dt, Poco::DateTimeFormat::RFC1123_FORMAT);
}
static Poco::DateTime addDays(const Poco::DateTime& dt, int days)
{
return dt + Poco::Timespan(days, 0, 0, 0, 0);
}
static Poco::DateTime addHours(const Poco::DateTime& dt, int hours)
{
return dt + Poco::Timespan(0, hours, 0, 0, 0);
}
static Poco::DateTime addMinutes(const Poco::DateTime& dt, int minutes)
{
return dt + Poco::Timespan(0, 0, minutes, 0, 0);
}
static Poco::DateTime addSeconds(const Poco::DateTime& dt, int seconds)
{
return dt + Poco::Timespan(0, 0, 0, seconds, 0);
}
static Poco::Timespan diff(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
Poco::Timestamp::TimeDiff diffMicroseconds = 0;
if (ts1 > ts2)
{
diffMicroseconds = ts1 - ts2;
}
else
{
diffMicroseconds = ts2 - ts1;
}
return Poco::Timespan(diffMicroseconds);
}
static long diffSeconds(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
Poco::Timestamp::TimeDiff diffMicroseconds = 0;
if (ts1 > ts2)
{
diffMicroseconds = ts1 - ts2;
}
else
{
diffMicroseconds = ts2 - ts1;
}
Poco::Timespan ts(diffMicroseconds);
return ts.totalSeconds();
}
static long long diffMilliseconds(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
Poco::Timestamp::TimeDiff diffMicroseconds = 0;
if (ts1 > ts2)
{
diffMicroseconds = ts1 - ts2;
}
else
{
diffMicroseconds = ts2 - ts1;
}
return diffMicroseconds / 1000;
}
static long long diffMicroseconds(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
Poco::Timestamp::TimeDiff diffMicroseconds = 0;
if (ts1 > ts2)
{
diffMicroseconds = ts1 - ts2;
}
else
{
diffMicroseconds = ts2 - ts1;
}
return diffMicroseconds;
}
static void diffComponents(const Poco::DateTime& dt1, const Poco::DateTime& dt2, int& days, int& hours,
int& minutes, int& seconds)
{
Poco::Timespan ts = diff(dt1, dt2);
days = ts.days();
hours = ts.hours();
minutes = ts.minutes();
seconds = ts.seconds();
}
static Poco::Timespan calculateDifference(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp::TimeVal us1 = dt1.timestamp().epochMicroseconds();
Poco::Timestamp::TimeVal us2 = dt2.timestamp().epochMicroseconds();
Poco::Timestamp::TimeVal diff = 0;
if (us1 > us2)
{
diff = us1 - us2;
}
else
{
diff = us2 - us1;
}
return Poco::Timespan(diff);
}
static bool isBefore(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
return ts1 < ts2;
}
static bool isAfter(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
return ts1 > ts2;
}
static bool isEqual(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
return ts1 == ts2;
}
static bool isLeapYear(int year)
{
return Poco::DateTime::isLeapYear(year);
}
static int daysInMonth(int year, int month)
{
if (month < 1 || month > 12)
{
return 0;
}
Poco::DateTime nextMonth;
if (month == 12)
{
nextMonth = Poco::DateTime(year + 1, 1, 1);
}
else
{
nextMonth = Poco::DateTime(year, month + 1, 1);
}
Poco::DateTime lastDay = nextMonth - Poco::Timespan(1, 0, 0, 0, 0);
return lastDay.day();
}
static std::string getWeekdayName(int dayOfWeek)
{
static const std::string weekdays[] = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
if (dayOfWeek >= 0 && dayOfWeek <= 6)
{
return weekdays[dayOfWeek];
}
return "未知";
}
static time_t getTimestamp()
{
Poco::Timestamp ts;
return ts.epochTime();
}
static long long getTimestampMs()
{
Poco::Timestamp ts;
return ts.epochMicroseconds() / 1000;
}
static long long getTimestampUs()
{
Poco::Timestamp ts;
return ts.epochMicroseconds();
}
static Poco::DateTime fromTimestamp(time_t timestamp)
{
Poco::Timestamp ts(static_cast<Poco::Timestamp::TimeVal>(timestamp) * 1000000);
return Poco::DateTime(ts);
}
static Poco::DateTime fromTimestampMs(long long timestampMs)
{
Poco::Timestamp ts(static_cast<Poco::Timestamp::TimeVal>(timestampMs) * 1000);
return Poco::DateTime(ts);
}
static Poco::DateTime fromTimestampUs(long long timestampUs)
{
Poco::Timestamp ts(static_cast<Poco::Timestamp::TimeVal>(timestampUs));
return Poco::DateTime(ts);
}
static Poco::Timestamp toTimestamp(const Poco::DateTime& dt)
{
return dt.timestamp();
}
static Poco::DateTime toDateTime(const Poco::Timestamp& ts)
{
return Poco::DateTime(ts);
}
static Poco::DateTime getStartOfDay(const Poco::DateTime& dt)
{
return Poco::DateTime(dt.year(), dt.month(), dt.day());
}
static Poco::DateTime getEndOfDay(const Poco::DateTime& dt)
{
return Poco::DateTime(dt.year(), dt.month(), dt.day(), 23, 59, 59, 999999);
}
static int getAge(const Poco::DateTime& birthDate)
{
Poco::DateTime now = DateTimeUtils::now();
int age = now.year() - birthDate.year();
if (now.month() < birthDate.month() || (now.month() == birthDate.month() && now.day() < birthDate.day()))
{
age--;
}
return age;
}
static bool isSameDay(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
return dt1.year() == dt2.year() && dt1.month() == dt2.month() && dt1.day() == dt2.day();
}
static std::string formatTimespan(const Poco::Timespan& ts)
{
std::string result;
if (ts.days() > 0)
{
result += std::to_string(ts.days()) + "天";
}
if (ts.hours() > 0)
{
result += std::to_string(ts.hours()) + "小时";
}
if (ts.minutes() > 0)
{
result += std::to_string(ts.minutes()) + "分";
}
if (ts.seconds() > 0)
{
result += std::to_string(ts.seconds()) + "秒";
}
if (result.empty())
{
result = "0秒";
}
return result;
}
static std::string formatTimeDifference(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timespan diff = calculateDifference(dt1, dt2);
return formatTimespan(diff);
}
static std::string formatTimespanWithMs(const Poco::Timespan& ts)
{
std::string result = formatTimespan(ts);
int milliseconds = ts.milliseconds();
if (milliseconds > 0)
{
result += std::to_string(milliseconds) + "毫秒";
}
return result;
}
static int getWeekOfYear(const Poco::DateTime& dt)
{
Poco::DateTime firstDay(dt.year(), 1, 1);
Poco::Timespan diff = dt - firstDay;
int dayOfYear = diff.days() + 1;
int firstDayWeek = firstDay.dayOfWeek();
int week = (dayOfYear + firstDayWeek - 1) / 7 + 1;
return week;
}
static Poco::DateTime getFirstDayOfMonth(const Poco::DateTime& dt)
{
return Poco::DateTime(dt.year(), dt.month(), 1);
}
static Poco::DateTime getLastDayOfMonth(const Poco::DateTime& dt)
{
int lastDay = daysInMonth(dt.year(), dt.month());
return Poco::DateTime(dt.year(), dt.month(), lastDay);
}
static Poco::DateTime getFirstDayOfWeek(const Poco::DateTime& dt)
{
int dayOfWeek = dt.dayOfWeek();
int daysToMonday = (dayOfWeek == 0) ? 6 : (dayOfWeek - 1);
return addDays(dt, -daysToMonday);
}
static Poco::DateTime getLastDayOfWeek(const Poco::DateTime& dt)
{
int dayOfWeek = dt.dayOfWeek();
int daysToSunday = (dayOfWeek == 0) ? 0 : (7 - dayOfWeek);
return addDays(dt, daysToSunday);
}
static long long getTime(const Poco::DateTime& dt)
{
Poco::Timestamp ts = dt.timestamp();
return ts.epochMicroseconds() / 1000;
}
static time_t getTimeSeconds(const Poco::DateTime& dt)
{
Poco::Timestamp ts = dt.timestamp();
return ts.epochTime();
}
static Poco::DateTime fromTime(long long milliseconds)
{
Poco::Timestamp ts(static_cast<Poco::Timestamp::TimeVal>(milliseconds) * 1000);
return Poco::DateTime(ts);
}
static Poco::DateTime fromTimeSeconds(time_t seconds)
{
Poco::Timestamp ts(static_cast<Poco::Timestamp::TimeVal>(seconds) * 1000000);
return Poco::DateTime(ts);
}
static long long currentTimeMillis()
{
Poco::Timestamp ts;
return ts.epochMicroseconds() / 1000;
}
static time_t currentTimeSeconds()
{
Poco::Timestamp ts;
return ts.epochTime();
}
};
}
#endif

104
util/Device.cpp Normal file
View file

@ -0,0 +1,104 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Device.h"
#include <stdarg.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include "Poco/Exception.h"
#include "Poco/Format.h"
#include <iostream>
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Device::Device(std::string path, Mode mode): _path(path), _mode(mode), _fd(-1)
{
}
Device::~Device()
{
close();
}
void Device::open()
{
int flags;
switch (_mode)
{
default:
case Mode::RD_ONLY:
flags = O_RDONLY;
break;
case Mode::WR_ONLY:
flags = O_WRONLY;
break;
case Mode::RDWR:
flags = O_RDWR;
break;
}
_fd = ::open(_path.c_str(), flags);
if (_fd < 0)
throw Poco::InvalidAccessException(Poco::format("Access %s error: %d", _path, _fd), _fd);
}
int Device::write(uint8_t* data, int size)
{
return ::write(_fd, data, size);
}
int Device::read(uint8_t* buffer, int count)
{
return ::read(_fd, buffer, count);
}
void Device::close()
{
if (_fd >= 0)
::close(_fd);
}
int Device::fcntl(int cmd)
{
return ::fcntl(_fd, cmd);
}
int Device::fcntl(int cmd, long arg)
{
return ::fcntl(_fd, cmd, arg);
}
int Device::fcntl(int cmd, long arg, void* lock)
{
return ::fcntl(_fd, cmd, arg, lock);
}
int Device::poll(unsigned int& revents, int timeout)
{
struct pollfd pollfd;
int rc;
pollfd.fd = _fd;
pollfd.events = POLLIN;
rc = ::poll(&pollfd, 1, timeout);
if (rc > 0)
revents = pollfd.revents;
return rc;
}
}

47
util/Device.h Normal file
View file

@ -0,0 +1,47 @@
#ifndef C5ADE9DB_6B16_4E4F_8C58_0101E9832DE0
#define C5ADE9DB_6B16_4E4F_8C58_0101E9832DE0
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <string>
#include <poll.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Device
{
public:
enum Mode
{
RD_ONLY,
WR_ONLY,
RDWR,
};
public:
Device(std::string path, Mode mode = Mode::RD_ONLY);
~Device();
virtual void open();
virtual int write(uint8_t* data, int size);
virtual int read(uint8_t* buffer, int count);
virtual void close();
virtual int fcntl(int cmd);
virtual int fcntl(int cmd, long arg);
virtual int fcntl(int cmd, long arg, void* lock);
virtual int poll(unsigned int& revents, int timeout);
private:
std::string _path;
Mode _mode;
int _fd;
};
}
#endif

15
util/FactoryManager.cpp Normal file
View file

@ -0,0 +1,15 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "FactoryManager.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
}

79
util/FactoryManager.h Normal file
View file

@ -0,0 +1,79 @@
#ifndef BE13A370_B199_43EC_A4E6_E2D4FD197D63
#define BE13A370_B199_43EC_A4E6_E2D4FD197D63
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "ClassAssistant.h"
#include <map>
#include <Poco/SharedPtr.h>
#include <Poco/Format.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
template <class T> class SimpleSelector
{
public:
static T select(T val)
{
return val;
}
};
template <class T, class TKey, class TSelector, class... TArgs> class FactoryManager
{
private:
class IFactory
{
public:
using Ptr = Poco::SharedPtr<IFactory>;
virtual ~IFactory()
{
}
public:
virtual T* create(TArgs... args) = 0;
};
template <class C> class Factory: public IFactory
{
public:
C* create(TArgs... args) override
{
return new C(args...);
}
};
using Factories = std::map<TKey, typename IFactory::Ptr>;
public:
static T* create(TArgs... args)
{
TKey key = TSelector::select(args...);
if (_factories.find(key) == _factories.end())
throw Poco::NotFoundException(
Poco::format("FactoryManager: Factory '%s' does not exists.", Util::ClassAssistant::demangle<T>()),
-EINVAL);
return _factories[key]->create(args...);
}
template <class C> static void registerClass(TKey key)
{
_factories[key] = new Factory<C>();
}
private:
static Factories _factories;
};
template <class T, class TKey, class TSelect, class... TArgs>
typename FactoryManager<T, TKey, TSelect, TArgs...>::Factories FactoryManager<T, TKey, TSelect, TArgs...>::_factories;
}
#endif

71
util/FileUtil.h Normal file
View file

@ -0,0 +1,71 @@
#ifndef B2D3FCFE_BBC5_4EFD_9AA7_7EEB5C58E2AE
#define B2D3FCFE_BBC5_4EFD_9AA7_7EEB5C58E2AE
#include <Poco/File.h>
#include <Poco/FileStream.h>
#include <Poco/StreamCopier.h>
#include <string>
#include "Logger.h"
namespace Util
{
class FileUtil
{
private:
public:
static std::string readFile(const std::string& fileFullName)
{
std::string content;
Poco::File file(fileFullName);
if (file.exists())
{
try
{
Poco::FileInputStream fis(fileFullName);
Poco::StreamCopier::copyToString(fis, content);
fis.close();
}
catch (Poco::Exception& e)
{
logger().error("readFile read error: %s", std::string(e.what()));
}
}
return content;
}
static bool writeFile(const std::string& fileFullName, const std::string& content)
{
try
{
Poco::FileOutputStream fos(fileFullName, std::ios::trunc);
fos << content;
fos.close();
int fd = ::open(fileFullName.c_str(), O_RDWR);
if (fd != -1)
{
::fsync(fd);
::close(fd);
}
return true;
}
catch (Poco::Exception& e)
{
logger().error("writeFile file error: %s", std::string(e.what()));
}
return false;
}
static void delFile(const std::string& fileFullName)
{
if (!fileFullName.empty())
{
if (Poco::File(fileFullName).exists())
{
Poco::File(fileFullName).remove();
logger().debug("删除文件: %s", fileFullName);
}
}
}
};
}
#endif

125
util/FrpUtil.h Normal file
View file

@ -0,0 +1,125 @@
#ifndef FRP_UTIL_H
#define FRP_UTIL_H
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <string>
#include "Application.h"
#include "Poco/JSON/Object.h"
#include "util/SystemInfo.h"
namespace Util
{
class FrpUtil
{
private:
public:
static constexpr const char* frpcConfigPathName = "/customer/frp/frpc.toml";
static constexpr const char* frpcPathName = "/customer/frp/frpc";
public:
static int getFrpcPid()
{
return Util::SystemInfo::getProcessPID(frpcPathName);
}
static void startFrpc(std::string serialNum, Poco::JSON::Object::Ptr service_ports, bool forceRestart)
{
int http_local_port = service_ports->get("http_port");
int http_remote_port = service_ports->get("http_port_nat_port");
int http_api_local_port = service_ports->get("http_api_port");
int http_api_remote_port = service_ports->get("http_api_port_nat_port");
int rtmp_local_port = service_ports->get("rtmp_port");
int rtmp_remote_port = service_ports->get("rtmp_port_nat_port");
int rtsp_local_port = service_ports->get("rtsp_port");
int rtsp_remote_port = service_ports->get("rtsp_port_nat_port");
int srt_local_port = service_ports->get("srt_port");
int srt_remote_port = service_ports->get("srt_port_nat_port");
int nat_frp_enable = service_ports->get("nat_frp_enable");
try
{
int pid = getFrpcPid();
if (nat_frp_enable == 0)
{
if (pid > 0)
{
stopFrpc(pid);
}
return;
}
if (pid > 0)
{
if (forceRestart)
{
stopFrpc(pid);
}
else
{
return;
}
}
pid_t child = fork();
if (child < 0)
{
logger().information("frpc fork failed");
return;
}
if (child > 0)
{
logger().information("frpc daemon starting...");
return;
}
setsid();
for (int i = 3; i < 1024; ++i)
{
close(i);
}
int fd = open("/dev/null", O_RDWR);
if (fd >= 0)
{
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
}
setenv("FRP_SERIAL_NUM", serialNum.c_str(), 1);
setenv("FRP_HTTP_LOCAL_PORT", std::to_string(http_local_port).c_str(), 1);
setenv("FRP_HTTP_REMOTE_PORT", std::to_string(http_remote_port).c_str(), 1);
setenv("FRP_HTTP_API_LOCAL_PORT", std::to_string(http_api_local_port).c_str(), 1);
setenv("FRP_HTTP_API_REMOTE_PORT", std::to_string(http_api_remote_port).c_str(), 1);
setenv("FRP_RTMP_LOCAL_PORT", std::to_string(rtmp_local_port).c_str(), 1);
setenv("FRP_RTMP_REMOTE_PORT", std::to_string(rtmp_remote_port).c_str(), 1);
setenv("FRP_RTSP_LOCAL_PORT", std::to_string(rtsp_local_port).c_str(), 1);
setenv("FRP_RTSP_REMOTE_PORT", std::to_string(rtsp_remote_port).c_str(), 1);
setenv("FRP_SRT_LOCAL_PORT", std::to_string(srt_local_port).c_str(), 1);
setenv("FRP_SRT_REMOTE_PORT", std::to_string(srt_remote_port).c_str(), 1);
execlp(frpcPathName, frpcPathName, "-c", frpcConfigPathName, (char*)NULL);
exit(EXIT_FAILURE);
}
catch (const std::exception& e)
{
logger().information("doStartFrpc,error: %s", std::string(e.what()));
}
}
static void stopFrpc(int pid)
{
if (pid > 0)
{
Util::SystemInfo::executeCommand("kill -9 " + std::to_string(pid));
logger().information("frpc stoped,pid=%d........", pid);
}
}
};
}
#endif

386
util/HTTPClient.h Normal file
View file

@ -0,0 +1,386 @@
#ifndef C99FBF1C_5805_40A3_963F_5BC1CA998C5C
#define C99FBF1C_5805_40A3_963F_5BC1CA998C5C
#include <Poco/Exception.h>
#include <Poco/Net/HTMLForm.h>
#include <Poco/Net/HTTPClientSession.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/Net/MessageHeader.h>
#include <Poco/StreamCopier.h>
#include <Poco/URI.h>
#include <cctype>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "Logger.h"
namespace Util
{
class HTTPClient
{
public:
struct HttpResponse
{
int statusCode;
std::string statusMessage;
std::string content;
std::string contentType;
std::map<std::string, std::string> headers;
bool success;
HttpResponse(): statusCode(0), success(false)
{
}
};
struct ProxyConfig
{
std::string host;
int port;
bool enabled;
ProxyConfig(): port(0), enabled(false)
{
}
ProxyConfig(const std::string& h, int p): host(h), port(p), enabled(true)
{
}
};
struct RequestConfig
{
int timeout;
std::string userAgent;
std::map<std::string, std::string> headers;
bool followRedirects;
ProxyConfig proxy;
RequestConfig(): timeout(30), userAgent("HTTPClient/1.0"), followRedirects(true)
{
}
};
static HttpResponse get(const std::string& url, const RequestConfig& config = RequestConfig())
{
return executeRequest("GET", url, "", config);
}
static bool downloadFile(const std::string& url, const std::string& localFilePath,
const RequestConfig& config = RequestConfig())
{
try
{
Poco::URI uri(url);
auto session = createSession(uri, config.proxy);
session->setTimeout(Poco::Timespan(config.timeout, 0));
std::string path = getPathAndQuery(uri);
Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, path);
setupRequest(request, config);
session->sendRequest(request);
Poco::Net::HTTPResponse response;
std::istream& responseStream = session->receiveResponse(response);
if (response.getStatus() != Poco::Net::HTTPResponse::HTTP_OK)
{
std::cerr << "Download failed: HTTP " << response.getStatus() << std::endl;
return false;
}
std::ofstream file(localFilePath, std::ios::binary);
if (!file.is_open())
{
std::cerr << "Cannot open file: " << localFilePath << std::endl;
return false;
}
Poco::StreamCopier::copyStream(responseStream, file);
file.close();
return true;
}
catch (Poco::Exception& e)
{
std::cerr << "Download exception: " << e.displayText() << std::endl;
return false;
}
}
static HttpResponse postJson(const std::string& url, const std::string& jsonData,
const RequestConfig& config = RequestConfig())
{
RequestConfig jsonConfig = config;
jsonConfig.headers["Content-Type"] = "application/json";
return executeRequest("POST", url, jsonData, jsonConfig);
}
static HttpResponse postForm(const std::string& url, const std::map<std::string, std::string>& formData,
const RequestConfig& config = RequestConfig())
{
std::string formString = buildFormData(formData);
RequestConfig formConfig = config;
formConfig.headers["Content-Type"] = "application/x-www-form-urlencoded";
return executeRequest("POST", url, formString, formConfig);
}
static HttpResponse postFormWithPoco(const std::string& url, const std::map<std::string, std::string>& formData,
const RequestConfig& config = RequestConfig())
{
try
{
Poco::URI uri(url);
auto session = createSession(uri, config.proxy);
session->setTimeout(Poco::Timespan(config.timeout, 0));
std::string path = getPathAndQuery(uri);
Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, path);
setupRequest(request, config);
request.setContentType("application/x-www-form-urlencoded");
Poco::Net::HTMLForm form;
for (const auto& field : formData)
{
form.set(field.first, field.second);
}
std::ostream& requestStream = session->sendRequest(request);
form.write(requestStream);
Poco::Net::HTTPResponse response;
std::istream& responseStream = session->receiveResponse(response);
HttpResponse result;
result.statusCode = response.getStatus();
result.statusMessage = response.getReason();
result.contentType = response.getContentType();
result.success = (result.statusCode == Poco::Net::HTTPResponse::HTTP_OK);
Poco::Net::NameValueCollection::ConstIterator it = response.begin();
while (it != response.end())
{
result.headers[it->first] = it->second;
++it;
}
Poco::StreamCopier::copyToString(responseStream, result.content);
return result;
}
catch (Poco::Exception& e)
{
std::cerr << "POST form exception: " << e.displayText() << std::endl;
HttpResponse result;
result.success = false;
result.statusMessage = e.displayText();
return result;
}
}
static HttpResponse post(const std::string& url, const std::string& data,
const std::string& contentType = "text/plain",
const RequestConfig& config = RequestConfig())
{
RequestConfig postConfig = config;
postConfig.headers["Content-Type"] = contentType;
return executeRequest("POST", url, data, postConfig);
}
static HttpResponse put(const std::string& url, const std::string& data = "",
const RequestConfig& config = RequestConfig())
{
return executeRequest("PUT", url, data, config);
}
static HttpResponse deleteRequest(const std::string& url, const RequestConfig& config = RequestConfig())
{
return executeRequest("DELETE", url, "", config);
}
static RequestConfig createProxyConfig(const std::string& proxyHost, int proxyPort,
const RequestConfig& baseConfig = RequestConfig())
{
RequestConfig config = baseConfig;
config.proxy = ProxyConfig(proxyHost, proxyPort);
return config;
}
static HttpResponse executeRequest(const std::string& method, const std::string& url, const std::string& data,
const RequestConfig& config)
{
HttpResponse result;
try
{
Poco::URI uri(url);
auto session = createSession(uri, config.proxy);
session->setTimeout(Poco::Timespan(config.timeout, 0));
std::string path = getPathAndQuery(uri);
Poco::Net::HTTPRequest request(method, path);
setupRequest(request, config);
if (!data.empty())
{
request.setContentLength(data.length());
std::ostream& requestStream = session->sendRequest(request);
requestStream << data;
}
else
{
session->sendRequest(request);
}
Poco::Net::HTTPResponse response;
std::istream& responseStream = session->receiveResponse(response);
result.statusCode = response.getStatus();
result.statusMessage = response.getReason();
result.contentType = response.getContentType();
result.success = (result.statusCode == Poco::Net::HTTPResponse::HTTP_OK);
Poco::Net::NameValueCollection::ConstIterator it = response.begin();
while (it != response.end())
{
result.headers[it->first] = it->second;
++it;
}
Poco::StreamCopier::copyToString(responseStream, result.content);
if (config.followRedirects && (result.statusCode == Poco::Net::HTTPResponse::HTTP_MOVED_PERMANENTLY ||
result.statusCode == Poco::Net::HTTPResponse::HTTP_FOUND ||
result.statusCode == Poco::Net::HTTPResponse::HTTP_SEE_OTHER))
{
auto locationIt = result.headers.find("Location");
if (locationIt != result.headers.end())
{
std::cout << "Redirecting to: " << locationIt->second << std::endl;
return executeRequest(method, locationIt->second, data, config);
}
}
}
catch (Poco::TimeoutException& e)
{
std::cerr << "Request timeout: " << e.displayText() << std::endl;
result.success = false;
result.statusMessage = "Timeout";
}
catch (Poco::Exception& e)
{
std::cerr << "HTTP exception: " << e.displayText() << std::endl;
result.success = false;
result.statusMessage = e.displayText();
}
catch (std::exception& e)
{
std::cerr << "Standard exception: " << e.what() << std::endl;
result.success = false;
result.statusMessage = e.what();
}
return result;
}
private:
static std::string urlEncode(const std::string& value)
{
std::ostringstream escaped;
escaped.fill('0');
escaped << std::hex;
for (char c : value)
{
if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
{
escaped << c;
}
else
{
escaped << '%' << std::setw(2) << int(static_cast<unsigned char>(c));
}
}
return escaped.str();
}
static std::string buildFormData(const std::map<std::string, std::string>& formData)
{
std::ostringstream formStream;
bool first = true;
for (const auto& field : formData)
{
if (!first)
{
formStream << "&";
}
formStream << urlEncode(field.first) << "=" << urlEncode(field.second);
first = false;
}
return formStream.str();
}
static std::unique_ptr<Poco::Net::HTTPClientSession> createSession(const Poco::URI& uri, const ProxyConfig& proxy)
{
std::unique_ptr<Poco::Net::HTTPClientSession> session;
logger().debug("HTTPClient url: %s", uri.toString());
if (proxy.enabled)
{
session = std::make_unique<Poco::Net::HTTPClientSession>(proxy.host, proxy.port);
session->setProxy(uri.getHost(), uri.getPort());
}
else
{
if (uri.getScheme() == "https")
{
logger().warning("HTTPClient 暂时不支持https,自动调整为使用http连接: %s", uri.toString());
session = std::make_unique<Poco::Net::HTTPClientSession>(uri.getHost(), uri.getPort());
}
else
{
session = std::make_unique<Poco::Net::HTTPClientSession>(uri.getHost(), uri.getPort());
}
}
return session;
}
static void setupRequest(Poco::Net::HTTPRequest& request, const RequestConfig& config)
{
request.setVersion(Poco::Net::HTTPMessage::HTTP_1_1);
request.set("User-Agent", config.userAgent);
request.set("Accept", "*/*");
request.set("Connection", "close");
for (const auto& header : config.headers)
{
request.set(header.first, header.second);
}
}
static std::string getPathAndQuery(const Poco::URI& uri)
{
std::string path = uri.getPathAndQuery();
if (path.empty())
{
path = "/";
}
return path;
}
};
}
#endif

0
util/HandlesManager.cpp Normal file
View file

67
util/HandlesManager.h Normal file
View file

@ -0,0 +1,67 @@
#ifndef F0270054_CDC4_4A91_B09F_73A4C75D9356
#define F0270054_CDC4_4A91_B09F_73A4C75D9356
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/Mutex.h>
#include <set>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
template <typename T> class HandlesManager
{
public:
using HandleCollection = std::set<T>;
public:
HandlesManager(unsigned count): HandlesManager(0, count)
{
}
HandlesManager(unsigned begin, unsigned count)
{
for (T h = begin; h < (begin + count); h++)
_freeHandles.insert(h);
}
HandlesManager(HandleCollection& c)
{
_freeHandles.insert(c);
}
T allocHandle()
{
Poco::FastMutex::ScopedLock lock(_lock);
if (_freeHandles.size() == 0)
throw Poco::NotFoundException("Collection is empty", -ENODATA);
T h = *_freeHandles.begin();
_freeHandles.erase(h);
return h;
}
void freeHandle(T handle)
{
Poco::FastMutex::ScopedLock lock(_lock);
if (_freeHandles.count(handle) != 0)
throw Poco::NotFoundException("Double free", -EINVAL);
_freeHandles.insert(handle);
}
private:
std::set<T> _freeHandles;
Poco::FastMutex _lock;
};
}
#endif

29
util/HexBinaryCodec.cpp Executable file
View file

@ -0,0 +1,29 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "HexBinaryCodec.h"
#include <sstream>
#include <Poco/HexBinaryEncoder.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
std::string HexBinaryCodec::encode(const void* data, unsigned size, bool uppercase)
{
std::ostringstream ostr;
Poco::HexBinaryEncoder encoder(ostr);
encoder.rdbuf()->setUppercase(uppercase);
uint8_t* pBytes = (uint8_t*)data;
for (unsigned i = 0; i < size; i++)
encoder << pBytes[i];
encoder.close();
return ostr.str();
}
}

23
util/HexBinaryCodec.h Executable file
View file

@ -0,0 +1,23 @@
#ifndef E955D6BE_6497_479F_8D49_2A73C17E05E9
#define E955D6BE_6497_479F_8D49_2A73C17E05E9
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <string>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class HexBinaryCodec
{
public:
static std::string encode(const void* data, unsigned size, bool uppercase = true);
};
}
#endif

257
util/JsonUtil.h Normal file
View file

@ -0,0 +1,257 @@
#ifndef JSONUTIL_H
#define JSONUTIL_H
#include <map>
#include <regex>
#include <string>
#include <vector>
#include "util/xpack/json.h"
#include <Poco/JSON/Array.h>
#include <Poco/JSON/Object.h>
#include <Poco/JSON/Parser.h>
namespace Util
{
class JsonUtil
{
private:
public:
template <class T> static std::string encode(T t)
{
return xpack::json::encode(t);
}
template <typename T> static T decode(std::string jsonStr)
{
T t;
xpack::json::decode(jsonStr, t);
return t;
}
static std::string jsonToString(const Poco::JSON::Object::Ptr& jsonObject)
{
std::ostringstream oss;
Poco::JSON::Stringifier::stringify(jsonObject, oss);
return oss.str();
}
static Poco::JSON::Object::Ptr stringToJson(const std::string& jsonString)
{
Poco::JSON::Parser parser;
Poco::Dynamic::Var result = parser.parse(jsonString);
return result.extract<Poco::JSON::Object::Ptr>();
}
static Poco::JSON::Array::Ptr stringToJsonArray(const std::string& jsonArrayString)
{
Poco::JSON::Parser parser;
Poco::Dynamic::Var result = parser.parse(jsonArrayString);
return result.extract<Poco::JSON::Array::Ptr>();
}
static Poco::Dynamic::Var getNestedValue(Poco::JSON::Object::Ptr json, const std::string& path)
{
if (path.empty())
return json;
std::regex arrayRegex(R"((\w+)((?:\[\d+\])+))");
std::istringstream pathStream(path);
std::string segment;
Poco::Dynamic::Var currentVar = json;
while (std::getline(pathStream, segment, '.'))
{
std::smatch match;
if (std::regex_match(segment, match, arrayRegex))
{
std::string arrayKey = match[1];
std::string indicesStr = match[2];
std::vector<std::size_t> indices;
std::regex indexRegex(R"(\[(\d+)\])");
std::sregex_iterator it(indicesStr.begin(), indicesStr.end(), indexRegex);
std::sregex_iterator end;
while (it != end)
{
indices.push_back(static_cast<std::size_t>(std::stoi((*it)[1])));
++it;
}
Poco::JSON::Object::Ptr obj = currentVar.extract<Poco::JSON::Object::Ptr>();
if (!obj->has(arrayKey))
{
throw std::runtime_error("getNestedValue Key not found: " + arrayKey);
}
Poco::JSON::Array::Ptr array = obj->getArray(arrayKey);
if (!array)
{
throw std::runtime_error("getNestedValue Expected an array at key: " + arrayKey);
}
for (std::size_t i = 0; i < indices.size(); i++)
{
std::size_t index = indices[i];
if (index >= array->size())
{
throw std::out_of_range("getNestedValue Array index out of bounds: " + std::to_string(index));
}
currentVar = array->get(index);
if (i < indices.size() - 1)
{
array = currentVar.extract<Poco::JSON::Array::Ptr>();
if (!array)
{
throw std::runtime_error("getNestedValue Expected an array at index: " +
std::to_string(index));
}
}
}
}
else
{
Poco::JSON::Object::Ptr obj = currentVar.extract<Poco::JSON::Object::Ptr>();
if (!obj->has(segment))
{
throw std::runtime_error("getNestedValue Key not found: " + segment);
}
currentVar = obj->get(segment);
}
}
return currentVar;
}
static Poco::JSON::Object::Ptr getNestedValue(Poco::JSON::Object::Ptr json, const std::vector<std::string>& paths)
{
Poco::JSON::Object::Ptr results = new Poco::JSON::Object();
for (const auto& path : paths)
{
results->set(path, getNestedValue(json, path));
}
return results;
}
static void setNestedValue(Poco::JSON::Object::Ptr jsonObject, const std::string& path,
const Poco::Dynamic::Var& value)
{
if (!jsonObject)
{
throw std::invalid_argument("setNestedValue JSON object is null.");
}
std::regex arrayRegex(R"((\w+)((?:\[\d+\])+))");
std::istringstream pathStream(path);
std::string segment;
Poco::JSON::Object::Ptr currentObject = jsonObject;
while (std::getline(pathStream, segment, '.'))
{
std::smatch match;
if (std::regex_match(segment, match, arrayRegex))
{
std::string arrayKey = match[1];
std::string indicesStr = match[2];
std::vector<std::size_t> indices;
std::regex indexRegex(R"(\[(\d+)\])");
std::sregex_iterator it(indicesStr.begin(), indicesStr.end(), indexRegex);
std::sregex_iterator end;
while (it != end)
{
indices.push_back(static_cast<std::size_t>(std::stoi((*it)[1])));
++it;
}
Poco::JSON::Array::Ptr array;
if (!currentObject->has(arrayKey))
{
array = new Poco::JSON::Array();
currentObject->set(arrayKey, array);
}
else
{
array = currentObject->getArray(arrayKey);
if (!array)
{
throw std::runtime_error("setNestedValue Expected an array at key: " + arrayKey);
}
}
for (std::size_t i = 0; i < indices.size() - 1; i++)
{
while (array->size() <= indices[i])
{
Poco::JSON::Array::Ptr nestedArray = new Poco::JSON::Array();
array->add(nestedArray);
}
Poco::JSON::Array::Ptr nextArray = array->getArray(indices[i]);
if (!nextArray)
{
throw std::runtime_error("setNestedValue Expected an array at index: " +
std::to_string(indices[i]));
}
array = nextArray;
}
std::size_t lastIndex = indices.back();
while (array->size() <= lastIndex)
{
Poco::JSON::Object::Ptr nestedObject = new Poco::JSON::Object();
array->add(nestedObject);
}
if (pathStream.peek() == EOF)
{
array->set(lastIndex, value);
return;
}
currentObject = array->getObject(lastIndex);
if (!currentObject)
{
throw std::runtime_error("setNestedValue Expected an object at index: " +
std::to_string(lastIndex));
}
}
else
{
if (pathStream.peek() == EOF)
{
currentObject->set(segment, value);
return;
}
if (!currentObject->has(segment))
{
Poco::JSON::Object::Ptr nestedObject = new Poco::JSON::Object();
currentObject->set(segment, nestedObject);
}
currentObject = currentObject->getObject(segment);
if (!currentObject)
{
throw std::runtime_error("setNestedValue Expected an object at key: " + segment);
}
}
}
}
static void setNestedValue(Poco::JSON::Object::Ptr jsonObject,
const std::map<std::string, Poco::Dynamic::Var>& pathValues)
{
for (const auto& kv : pathValues)
{
setNestedValue(jsonObject, kv.first, kv.second);
}
}
};
}
#endif

25
util/Math.cpp Normal file
View file

@ -0,0 +1,25 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Math.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
unsigned Math::alignup(unsigned num, unsigned align)
{
return (((num) + (align)-1) & ~((align)-1));
}
unsigned Math::aligndown(unsigned num, unsigned align)
{
return ((num) & ~((align)-1));
}
}

37
util/Math.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef D6937363_A08A_40BF_B387_36E8AC570ECF
#define D6937363_A08A_40BF_B387_36E8AC570ECF
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Math
{
public:
static unsigned alignup(unsigned num, unsigned align);
static unsigned aligndown(unsigned num, unsigned align);
template <typename T> static T max(T a, T b)
{
return a > b ? a : b;
}
template <typename T> static T min(T a, T b)
{
return a < b ? a : b;
}
template <typename T> static T clamp(T value, T minVal, T maxVal)
{
return max(minVal, min(maxVal, value));
}
};
}
#endif

37
util/Md5Util.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef B6E698FD_68E6_4CB8_9BBA_BFE96C7501B0
#define B6E698FD_68E6_4CB8_9BBA_BFE96C7501B0
#include <Poco/DigestStream.h>
#include <Poco/MD5Engine.h>
#include <Poco/UUID.h>
#include <Poco/UUIDGenerator.h>
#include <iostream>
namespace Util
{
class Md5Util
{
public:
static std::string md5(const std::string& data)
{
Poco::MD5Engine md5Engine;
Poco::DigestOutputStream dos(md5Engine);
dos << data;
dos.close();
const Poco::DigestEngine::Digest& digest = md5Engine.digest();
return Poco::DigestEngine::digestToHex(digest);
}
static std::string uuid()
{
Poco::UUIDGenerator& generator = Poco::UUIDGenerator::defaultGenerator();
Poco::UUID uuid = generator.createRandom();
return uuid.toString();
}
};
}
#endif

20
util/ParallelRunner.cpp Normal file
View file

@ -0,0 +1,20 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "ParallelRunner.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
void ParallelRunner::join()
{
for (auto& t : _threads)
t->join();
}
}

35
util/ParallelRunner.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef DE82057F_5520_4849_8108_E9A31AAC1878
#define DE82057F_5520_4849_8108_E9A31AAC1878
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <functional>
#include <Poco/Thread.h>
#include <Poco/SharedPtr.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class ParallelRunner
{
public:
template <class Func, class... Args> void run(Func func, Args... args)
{
Poco::Thread* pThread = new Poco::Thread();
pThread->startFunc([=]() -> void { func(args...); });
_threads.push_back(pThread);
}
void join();
private:
std::vector<Poco::SharedPtr<Poco::Thread>> _threads;
};
}
#endif

View file

@ -0,0 +1,44 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "PocoConfigurationCopier.h"
#include <Poco/Util/JSONConfiguration.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
void PocoConfigurationCopier::copy(Poco::Util::AbstractConfiguration::Ptr& from,
Poco::Util::AbstractConfiguration::Ptr& to)
{
bool usingJSON = false;
if (to.isNull())
{
to = new Poco::Util::JSONConfiguration();
usingJSON = true;
}
else
usingJSON = !to.cast<Poco::Util::JSONConfiguration>().isNull();
Poco::AutoPtr<Poco::Util::JSONConfiguration> pJConfig = from.cast<Poco::Util::JSONConfiguration>();
if (pJConfig && usingJSON)
{
std::stringstream ss;
pJConfig->save(ss);
to.cast<Poco::Util::JSONConfiguration>()->load(ss);
}
else
{
Poco::Util::AbstractConfiguration::Keys keys;
from->keys(keys);
for (const auto& key : keys)
to->setString(key, from->getString(key));
}
}
}

View file

@ -0,0 +1,23 @@
#ifndef B2336806_10E0_4BFA_9CC6_0995436A32EB
#define B2336806_10E0_4BFA_9CC6_0995436A32EB
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/Util/AbstractConfiguration.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class PocoConfigurationCopier
{
public:
static void copy(Poco::Util::AbstractConfiguration::Ptr& from, Poco::Util::AbstractConfiguration::Ptr& to);
};
}
#endif

54
util/Service.cpp Normal file
View file

@ -0,0 +1,54 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Service.h"
#include <iostream>
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
namespace Util
{
void Service::start()
{
if (isRunning())
return;
_pThread = new Poco::Thread();
try
{
_pThread->start(*this);
}
catch (Poco::Exception& e)
{
std::cout << " --> Service: Poco::Exception: " << e.displayText() << "(" << e.what() << ")" << std::endl;
}
catch (std::exception& e)
{
std::cout << " --> Service: std::exception: " << e.what() << std::endl;
}
}
void Service::stop()
{
if (isRunning())
{
cancel();
_pThread->join();
_pThread.reset();
reset();
}
}
bool Service::isRunning() const
{
return !_pThread.isNull() && _pThread->isRunning();
}
}

31
util/Service.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef F3A063B4_C5E0_40B0_AFDA_B9DC936E9E82
#define F3A063B4_C5E0_40B0_AFDA_B9DC936E9E82
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/Task.h>
#include <Poco/Thread.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Service: public Poco::Task
{
public:
using Poco::Task::Task;
virtual void start();
virtual void stop();
bool isRunning() const;
private:
Poco::SharedPtr<Poco::Thread> _pThread;
};
}
#endif

15
util/SimpleFactory.cpp Normal file
View file

@ -0,0 +1,15 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "SimpleFactory.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
}

20
util/SimpleFactory.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef FF4EF0DA_D455_437D_A5ED_BA7A7994B9CB
#define FF4EF0DA_D455_437D_A5ED_BA7A7994B9CB
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
template <class T, class... TArgs> class SimpleFactory
{
public:
static T* create(TArgs... args)
{
return new T(args...);
}
};
}
#endif

35
util/Subsystem.cpp Normal file
View file

@ -0,0 +1,35 @@
/*
*******************************************************************************
* INCLUDES
*******************************************************************************
*/
#include "Subsystem.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Subsystem::Subsystem(int maxCapacity): _threadPool(2, 16, 60, 0), _taskManager(_threadPool)
{
}
void Subsystem::run(Service* pService)
{
_taskManager.start(pService);
}
void Subsystem::stopAll()
{
_taskManager.cancelAll();
_taskManager.joinAll();
}
void Subsystem::uninitialize()
{
stopAll();
}
}

39
util/Subsystem.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef DC436124_02CE_49A1_88B1_1A95453FA546
#define DC436124_02CE_49A1_88B1_1A95453FA546
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Service.h"
#include <Poco/Task.h>
#include <Poco/TaskManager.h>
#include <Poco/Thread.h>
#include <Poco/ThreadPool.h>
#include <Poco/Util/Subsystem.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Subsystem: public Poco::Util::Subsystem
{
public:
Subsystem(int maxCapacity = 16);
protected:
void run(Service* pService);
void stopAll();
virtual void uninitialize() override;
private:
Poco::ThreadPool _threadPool;
Poco::TaskManager _taskManager;
};
}
#endif

829
util/SystemInfo.cpp Normal file
View file

@ -0,0 +1,829 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "SystemInfo.h"
#include <Poco/DirectoryIterator.h>
#include <Poco/Path.h>
#include <Poco/Pipe.h>
#include <Poco/PipeStream.h>
#include <Poco/Process.h>
#include <Poco/StreamCopier.h>
#include <dirent.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <unistd.h>
#include <algorithm>
#include <atomic>
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <experimental/filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include <vector>
#include "Application.h"
#include "Logger.h"
#include "Poco/Thread.h"
#include "linux-system-usage.hpp"
using namespace get_system_usage_linux;
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
unsigned SystemInfo::cpuUsage()
{
CPU_stats t1 = read_cpu_data();
Poco::Thread::sleep(100);
CPU_stats t2 = read_cpu_data();
return (unsigned)(100.0f * get_cpu_usage(t1, t2));
}
SystemInfo::MemoryInfo SystemInfo::readMemoryInfo()
{
auto memory_data = read_memory_data();
MemoryInfo info;
info.total = memory_data.total_memory;
info.free = memory_data.free_memory;
info.usage = 100.0f * memory_data.get_memory_usage();
info.available = memory_data.available_memory;
return info;
}
std::map<std::string, Util::SystemInfo::StorageInfo> SystemInfo::getStorageInfo(bool returnSpace)
{
std::ifstream mounts("/proc/mounts");
std::string line;
std::map<std::string, StorageInfo> storageMap;
if (!mounts.is_open())
{
logger().error("Failed to open /proc/mounts");
return storageMap;
}
while (std::getline(mounts, line))
{
std::istringstream iss(line);
std::string device, path;
iss >> device >> path;
std::string deviceType = device.find("/dev/sd") == 0 ? "disk" : "";
deviceType = !deviceType.empty() ? deviceType : (device.find("/dev/mmcblk") == 0 ? "mmcblk" : "");
deviceType = !deviceType.empty() ? deviceType : (path.find("/mnt/storages/nfs") == 0 ? "nfs" : "");
deviceType = !deviceType.empty() ? deviceType : (path.find("/mnt/storages/cifs") == 0 ? "cifs" : "");
if (!deviceType.empty())
{
try
{
if (!mountPointEnable(path))
{
logger().error("%s Mount point is not available", path);
continue;
}
StorageInfo storageInfo;
storageInfo.device = device;
storageInfo.path = path;
storageInfo.status = 1;
storageInfo.type = deviceType;
if (returnSpace)
{
struct statvfs stat;
int ret_code = statvfs(path.c_str(), &stat);
if (ret_code != 0)
{
int err = errno;
logger().error("Failed to get disk usage information for: %s", strerror(err));
}
else
{
unsigned long long total = stat.f_blocks * stat.f_frsize;
unsigned long long free = stat.f_bfree * stat.f_frsize;
unsigned long long available = stat.f_bavail * stat.f_frsize;
unsigned long long used = total - free;
storageInfo.total = total;
storageInfo.used = used;
storageInfo.available = available;
storageInfo.free = free;
}
}
storageMap[device] = storageInfo;
}
catch (const std::exception& e)
{
logger().error("SystemInfo::getStorageInfo erorr: %s", std::string(e.what()));
}
}
}
auto blocks = getBlockDevices();
for (const auto& block : blocks)
{
if (block.empty() || block.back() != '1')
{
continue;
}
auto it = storageMap.find(block);
if (it == storageMap.end())
{
StorageInfo storageInfo;
storageInfo.status = 2;
storageInfo.device = block;
storageMap[block] = storageInfo;
}
}
return storageMap;
}
bool isBlockDevice(const std::string& path)
{
struct stat statbuf;
if (stat(path.c_str(), &statbuf) != 0)
{
return false;
}
return S_ISBLK(statbuf.st_mode);
}
std::vector<std::string> SystemInfo::getBlockDevices()
{
std::vector<std::string> blockDevices;
std::ifstream file("/proc/partitions");
std::string line;
while (std::getline(file, line))
{
std::istringstream iss(line);
std::string major, minor, blocks, name;
if (!(iss >> major >> minor >> blocks >> name))
{
continue;
}
std::string devicePath = "/dev/" + name;
if (devicePath.find("/dev/sd") == 0 && isBlockDevice(devicePath))
{
blockDevices.push_back(devicePath);
}
}
return blockDevices;
}
std::vector<Util::SystemInfo::FileInfo> SystemInfo::getFileInfoList(std::string parentPath)
{
std::vector<Util::SystemInfo::FileInfo> fileInfoList;
Poco::Path path(parentPath);
if (!path.isDirectory())
{
logger().error("Error: %s is not a directory.", parentPath);
return fileInfoList;
}
try
{
Poco::DirectoryIterator it(parentPath);
Poco::DirectoryIterator end;
while (it != end)
{
const Poco::Path& p = it.path();
Util::SystemInfo::FileInfo fileInfo;
fileInfo.name = p.getFileName();
fileInfo.parentPath = parentPath;
fileInfo.type = it->isDirectory() ? 2 : (it->isFile() ? 1 : 0);
if (it->isDirectory())
{
}
else if (it->isFile())
{
Poco::File file(p.toString());
fileInfo.size = file.getSize();
fileInfo.lastModifyTime = file.getLastModified().epochTime();
}
++it;
fileInfoList.push_back(fileInfo);
}
}
catch (Poco::Exception& e)
{
logger().error("An error occurred: %s", e.displayText());
}
return fileInfoList;
}
unsigned long long Util::SystemInfo::getPathFreeSpace(const std::string& path)
{
struct statvfs stat;
if (statvfs(path.c_str(), &stat) != 0)
{
logger().error("Failed to get filesystem stats for %s", path);
return 0;
}
unsigned long long free = stat.f_bfree * stat.f_frsize;
return free;
}
unsigned long long Util::SystemInfo::checkSizeAndDelEarliestFile(const std::string& basePath,
const unsigned int limitMB,
const std::string deletePath,
const std::string& deleteExtName,
const std::set<std::string> exculdePathOrFiles)
{
int i = 10;
unsigned long long freeBytes = 0;
while (i-- > 0)
{
freeBytes = getPathFreeSpace(basePath);
unsigned long long limitBytes = static_cast<unsigned long long>(limitMB) * 1024 * 1024;
logger().information(" freeBytes=%s, limitBytes=%s", std::to_string(freeBytes), std::to_string(limitBytes));
if (freeBytes < limitBytes)
{
logger().information(" Space is less than=%s. Deleting earliest file...", std::to_string(limitMB));
try
{
std::string findCmd = "find " + deletePath + " -type f -name \"*" + deleteExtName +
"\" -exec stat -c '%mY %n' {} + | sort -n | head -n 1 | cut -d' ' -f2-";
logger().information("Execute command: %s", findCmd);
std::string oldestFile;
std::array<char, 128> buffer;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(findCmd.c_str(), "r"), pclose);
if (!pipe)
{
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
{
oldestFile += buffer.data();
}
if (!oldestFile.empty() && oldestFile[oldestFile.size() - 1] == '\n')
{
oldestFile.erase(oldestFile.size() - 1);
}
if (!oldestFile.empty())
{
bool isExecuteDelete = true;
for (std::string name : exculdePathOrFiles)
{
if (oldestFile.find(name) == 0)
{
isExecuteDelete = false;
break;
}
}
if (isExecuteDelete)
{
try
{
Poco::File file(oldestFile);
if (file.exists())
{
file.remove();
logger().information("File deleted successfully: %s", oldestFile);
}
else
{
logger().information("File does not exist: %s", oldestFile);
}
}
catch (const Poco::Exception& ex)
{
logger().information("Failed to delete file: : %s, error:", oldestFile, ex.displayText());
}
}
else
{
logger().information("deleted old file -> exculde path or file:%s", oldestFile);
}
}
else
{
logger().information("deleted old file -> No .mp4 files found");
}
}
catch (const std::exception& e)
{
logger().error("deleted old file -> Error: ", e.what());
}
}
else
{
break;
}
}
if (freeBytes <= 0)
{
freeBytes = getPathFreeSpace(basePath);
}
return freeBytes;
}
bool Util::SystemInfo::mountPointEnable(const std::string& localPath)
{
return Util::SystemInfo::executeCommand("ls " + localPath, 5);
}
bool Util::SystemInfo::mountCifs(std::string ip, std::string remotePath, std::string localPath, std::string username,
std::string password, std::string vers, std::string charset)
{
Util::SystemInfo::umount(localPath);
Util::SystemInfo::executeCommand("rm -rf " + localPath);
Util::SystemInfo::executeCommand("mkdir -p " + localPath);
std::string _charset = charset.empty() ? "" : ",iocharset=" + charset;
std::string mountCmd = Poco::format("mount -t cifs -o username=%s,password=%s,vers=%s%s //%s%s %s", username,
password, vers, _charset, ip, remotePath, localPath);
return Util::SystemInfo::executeCommand(mountCmd);
}
bool Util::SystemInfo::mountNfs(std::string ip, std::string remotePath, std::string localPath, std::string vers)
{
Util::SystemInfo::umount(localPath);
Util::SystemInfo::executeCommand("rm -rf " + localPath);
Util::SystemInfo::executeCommand("mkdir -p " + localPath);
std::string mountCmd = Poco::format("mount -t nfs -o nolock,vers=%s %s:%s %s", vers, ip, remotePath, localPath);
return Util::SystemInfo::executeCommand(mountCmd, 5);
}
bool Util::SystemInfo::umount(std::string localPath)
{
return Util::SystemInfo::executeCommand("umount -f " + localPath);
}
int Util::SystemInfo::getProcessPID(const std::string& processName)
{
try
{
DIR* dir = opendir("/proc");
if (!dir)
{
return 0;
}
std::string targetName = processName;
size_t lastSlash = targetName.find_last_of('/');
if (lastSlash != std::string::npos)
{
targetName = targetName.substr(lastSlash + 1);
}
struct dirent* entry;
int pid = 0;
while ((entry = readdir(dir)))
{
if (entry->d_type != DT_DIR || !isdigit(entry->d_name[0]))
{
continue;
}
std::string commPath = std::string("/proc/") + entry->d_name + "/comm";
std::ifstream commFile(commPath);
if (commFile)
{
std::string commName;
if (std::getline(commFile, commName))
{
commName.erase(std::remove(commName.begin(), commName.end(), '\n'), commName.end());
if (commName == targetName)
{
pid = std::stoi(entry->d_name);
break;
}
continue;
}
}
std::string cmdlinePath = std::string("/proc/") + entry->d_name + "/cmdline";
std::ifstream cmdlineFile(cmdlinePath);
if (cmdlineFile)
{
std::string cmdline;
if (std::getline(cmdlineFile, cmdline))
{
size_t nullPos = cmdline.find('\0');
if (nullPos != std::string::npos)
{
cmdline.resize(nullPos);
}
lastSlash = cmdline.find_last_of('/');
std::string exeName = (lastSlash != std::string::npos) ? cmdline.substr(lastSlash + 1) : cmdline;
if (exeName == targetName)
{
pid = std::stoi(entry->d_name);
break;
}
}
}
}
closedir(dir);
return pid;
}
catch (const std::exception& e)
{
logger().error("getProcessPID Error: %s", std::string(e.what()));
}
return 0;
}
std::vector<std::string> Util::SystemInfo::getNetworkInterfaceNames()
{
std::vector<std::string> interfaceNames;
try
{
Poco::Net::NetworkInterface::List interfaces = Poco::Net::NetworkInterface::list(false, false);
for (const auto& interface : interfaces)
{
if (!interface.isLoopback()
&& !interface.name().empty())
{
interfaceNames.push_back(interface.name());
}
}
}
catch (const Poco::Exception& e)
{
logger().error("getNetworkInterfaceNames Error: %s", std::string(e.what()));
}
return interfaceNames;
}
Util::SystemInfo::NetworkInterfaceInfo Util::SystemInfo::getNetWorkInterfaceInfo(const std::string& interfaceName,
bool returnGateway)
{
Util::SystemInfo::NetworkInterfaceInfo info;
try
{
auto interface = Poco::Net::NetworkInterface::forName(interfaceName);
if (interface.name() == interfaceName)
{
info.name = interface.name();
info.displayName = interface.displayName();
info.ipAddress = interface.address().toString();
info.subnetMask = interface.subnetMask().toString();
info.broadcastAddress = interface.broadcastAddress().toString();
auto macAddress = interface.macAddress();
info.macAddress = Poco::format("%02X:%02X:%02X:%02X:%02X:%02X", (unsigned)macAddress[0],
(unsigned)macAddress[1], (unsigned)macAddress[2], (unsigned)macAddress[3],
(unsigned)macAddress[4], (unsigned)macAddress[5]);
info.isUp = interface.isUp();
if (returnGateway)
{
info.gateway = gatewayAddress(interfaceName).toString();
}
info.isInitialized = true;
}
}
catch (const Poco::Exception& e)
{
logger().error("Error getNetWorkInterfaceInfo info: %s", std::string(e.what()));
}
return info;
}
Poco::Net::IPAddress Util::SystemInfo::localAddress(const std::string& interfaceName)
{
try
{
return Poco::Net::NetworkInterface::forName(interfaceName).address();
}
catch (...)
{
return Poco::Net::IPAddress("0.0.0.0");
}
}
std::map<std::string, Poco::Net::IPAddress> Util::SystemInfo::localAddresses()
{
std::map<std::string, Poco::Net::IPAddress> ipMap;
try
{
Poco::Net::NetworkInterface::List interfaces = Poco::Net::NetworkInterface::list();
for (const auto& interface : interfaces)
{
if (interface.isUp() && !interface.isLoopback() && !interface.address().isWildcard() &&
interface.address().isUnicast() && interface.address().family() == Poco::Net::AddressFamily::IPv4)
{
ipMap[interface.name()] = interface.address();
}
}
}
catch (const Poco::Exception& e)
{
logger().error("localAddresses Error: %s", std::string(e.what()));
}
return ipMap;
}
std::vector<std::string> Util::SystemInfo::localAddressesList()
{
std::vector<std::string> ipList;
try
{
Poco::Net::NetworkInterface::List interfaces = Poco::Net::NetworkInterface::list();
for (const auto& interface : interfaces)
{
if (interface.isUp() && !interface.isLoopback())
{
Poco::Net::NetworkInterface::AddressList addresses = interface.addressList();
for (const auto& addressInfo : addresses)
{
const Poco::Net::IPAddress& ip = addressInfo.get<0>();
if (!ip.isWildcard() && ip.isUnicast() && ip.family() == Poco::Net::AddressFamily::IPv4)
{
ipList.push_back(ip.toString());
}
}
}
}
}
catch (const Poco::Exception& e)
{
logger().error("localAddresses Error: %s", std::string(e.what()));
}
return ipList;
}
std::string Util::SystemInfo::localAddressOfAvailable()
{
std::map<std::string, Poco::Net::IPAddress> ipMap = localAddresses();
std::string hostIp = "0.0.0.0";
if (!ipMap.empty())
{
hostIp = ipMap.begin()->second.toString();
}
return hostIp;
}
std::string Util::SystemInfo::getFirstWirelessInterfaceName()
{
auto interfaces = getNetworkInterfaceNames();
for (auto& name : interfaces)
{
std::vector<std::string> wirelessPatterns = {"wlan", "wi-fi", "wireless", "wifi", "ath", "ra", "wlp", "wlan"};
for (const auto& pattern : wirelessPatterns)
{
if (Poco::toLower(name).find(pattern) != std::string::npos)
{
return name;
}
}
}
return "";
}
Poco::Net::IPAddress Util::SystemInfo::subnetMask(const std::string& interfaceName)
{
try
{
return Poco::Net::NetworkInterface::forName(interfaceName).subnetMask();
}
catch (...)
{
return Poco::Net::IPAddress("0.0.0.0");
}
}
Poco::Net::NetworkInterface::MACAddress Util::SystemInfo::macAddress(const std::string& interfaceName)
{
try
{
return Poco::Net::NetworkInterface::forName(interfaceName).macAddress();
}
catch (...)
{
return Poco::Net::NetworkInterface::MACAddress();
}
}
Poco::Net::NetworkInterface::MACAddress Util::SystemInfo::getFirstMacAddress()
{
auto interfaces = getNetworkInterfaceNames();
try
{
for (const auto& name : interfaces)
{
return Poco::Net::NetworkInterface::forName(name).macAddress();
}
return Poco::Net::NetworkInterface::MACAddress();
}
catch (...)
{
return Poco::Net::NetworkInterface::MACAddress();
}
}
bool Util::SystemInfo::netInterfaceIsExist(const std::string& interfaceName)
{
if (interfaceName.empty())
{
return false;
}
std::vector<std::string> interfaceNames = Util::SystemInfo::getNetworkInterfaceNames();
for (const auto& name : interfaceNames)
{
if (interfaceName == name)
{
return true;
}
}
return false;
}
bool Util::SystemInfo::netInterfaceIsAvailable(const std::string& interfaceName)
{
std::vector<std::string> interfaceNames;
try
{
Poco::Net::NetworkInterface::List interfaces = Poco::Net::NetworkInterface::list(false, false);
for (const auto& interface : interfaces)
{
if (!interface.isLoopback() && interface.isUp() && !interface.name().empty())
{
if (interfaceName == interface.name())
{
return true;
}
}
}
}
catch (const Poco::Exception& e)
{
logger().error("netInterfaceIsAvailable Error: %s", std::string(e.what()));
}
return false;
}
Poco::Net::IPAddress Util::SystemInfo::gatewayAddress(const std::string& interfaceName)
{
#define BUFLEN 20480
Poco::Net::IPAddress invalid("0.0.0.0");
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd < 0)
{
logger().error("NETLINK socket error");
return invalid;
}
char req[BUFLEN] = {0};
struct nlmsghdr* nh = (struct nlmsghdr*)req;
nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
nh->nlmsg_type = RTM_GETROUTE;
nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
struct rtmsg* rtm = (struct rtmsg*)NLMSG_DATA(nh);
rtm->rtm_family = AF_INET;
rtm->rtm_table = RT_TABLE_MAIN;
struct sockaddr_nl nladdr = {0};
nladdr.nl_family = AF_NETLINK;
if (bind(fd, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0)
{
logger().error("NETLINK bind error");
close(fd);
return invalid;
}
if (send(fd, req, sizeof(req), 0) < 0)
{
logger().error("NETLINK send error");
close(fd);
return invalid;
}
char buf[BUFLEN] = {0};
struct iovec iov = {buf, sizeof(buf)};
struct sockaddr_nl sa;
struct msghdr msg = {&sa, sizeof(sa), &iov, 1, NULL, 0, 0};
int len = recvmsg(fd, &msg, 0);
if (len < 0)
{
logger().error("NETLINK recvmsg error");
close(fd);
return invalid;
}
struct nlmsghdr* nhp;
for (nhp = (struct nlmsghdr*)buf; NLMSG_OK(nhp, len); nhp = NLMSG_NEXT(nhp, len))
{
struct rtmsg* rtp = (struct rtmsg*)NLMSG_DATA(nhp);
if (rtp->rtm_table != RT_TABLE_MAIN || rtp->rtm_family != AF_INET)
{
continue;
}
struct rtattr* attr = (struct rtattr*)RTM_RTA(rtp);
int attrlen = RTM_PAYLOAD(nhp);
for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen))
{
if (attr->rta_type != RTA_GATEWAY)
{
continue;
}
uint32_t gateway = *(uint32_t*)RTA_DATA(attr);
char gwstr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &gateway, gwstr, sizeof(gwstr));
close(fd);
return Poco::Net::IPAddress(std::string(gwstr));
}
}
close(fd);
return invalid;
}
bool Util::SystemInfo::executeCommand(const std::string& command, int timeout_sec, std::string& output)
{
if (timeout_sec <= 0)
{
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command.c_str(), "r"), pclose);
if (!pipe)
{
logger().error("Failed to execute command: %s", command);
return false;
}
char buffer[128];
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr)
{
output += buffer;
}
int status = pclose(pipe.release());
int ret = WEXITSTATUS(status);
if (ret != 0)
{
logger().error("Command failed: %s, exit code: %d, error: %s", command, ret, output);
return false;
}
logger().information("Command succeeded: %s, exit code: %d", command, ret);
return true;
}
output.clear();
std::string timeoutCommand = "timeout -s KILL " + std::to_string(timeout_sec) + " " + command;
timeoutCommand += " 2>/dev/null || timeout -s KILL -t " + std::to_string(timeout_sec) + " " + command;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(timeoutCommand.c_str(), "r"), pclose);
if (!pipe)
{
logger().error("2Failed to execute command: %s", timeoutCommand);
return false;
}
char buffer[128];
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr)
{
output += buffer;
}
int status = pclose(pipe.release());
int ret = WEXITSTATUS(status);
if (ret != 0)
{
if (ret == 124 || output.find("time") != std::string::npos)
{
logger().error("2Command timeout: %s", timeoutCommand);
}
else
{
logger().error("2Command failed: %s, exit code: %d, error: %s", timeoutCommand, ret, output);
}
return false;
}
logger().information("2Command succeeded: %s, exit code: %d", timeoutCommand, ret);
return true;
}
}

140
util/SystemInfo.h Normal file
View file

@ -0,0 +1,140 @@
#ifndef C42159C1_B275_444E_86D0_9A26B781F354
#define C42159C1_B275_444E_86D0_9A26B781F354
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/Net/IPAddress.h>
#include <Poco/Net/NetworkInterface.h>
#include <stdint.h>
#include <ctime>
#include "util/xpack/json.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class SystemInfo
{
public:
struct MemoryInfo
{
uint32_t total;
uint32_t free;
uint32_t usage;
uint32_t available;
};
struct StorageInfo
{
std::string type = "";
int status = 0;
std::string device = "";
std::string path = "";
unsigned long long total = 0;
unsigned long long used = 0;
unsigned long long available = 0;
unsigned long long free = 0;
StorageInfo() = default;
XPACK(O(type, status, device, path, total, used, available, free));
};
struct FileInfo
{
std::string name;
std::string parentPath = "";
int type;
off_t size;
time_t lastModifyTime;
XPACK(O(name, parentPath, type, size, lastModifyTime));
};
struct NetworkInterfaceInfo
{
std::string name;
std::string displayName;
std::string ipAddress = "0.0.0.0";
std::string subnetMask = "0.0.0.0";
std::string broadcastAddress;
std::string gateway = "0.0.0.0";
std::string macAddress;
bool isUp;
bool isInitialized;
NetworkInterfaceInfo() = default;
XPACK(O(name, displayName, ipAddress, subnetMask, broadcastAddress, gateway, macAddress, isUp));
};
public:
static unsigned cpuUsage();
static MemoryInfo readMemoryInfo();
static std::map<std::string, Util::SystemInfo::StorageInfo> getStorageInfo(bool returnSpace = true);
static std::vector<std::string> getBlockDevices();
static std::vector<Util::SystemInfo::FileInfo> getFileInfoList(std::string parentPath);
static unsigned long long getPathFreeSpace(const std::string& path);
static unsigned long long checkSizeAndDelEarliestFile(const std::string& basePath, const unsigned int limitMB,
const std::string deletePath,
const std::string& deleteExtName,
const std::set<std::string> exculdePathOrFiles);
static bool mountPointEnable(const std::string& localPath);
static bool mountCifs(std::string ip, std::string remotePath, std::string localPath, std::string username,
std::string password, std::string vers = "2.0", std::string charset = "");
static bool mountNfs(std::string ip, std::string remotePath, std::string localPath, std::string vers = "4");
static bool umount(std::string localPath);
static int getProcessPID(const std::string& processName);
static std::vector<std::string> getNetworkInterfaceNames();
static NetworkInterfaceInfo getNetWorkInterfaceInfo(const std::string& interfaceName, bool returnGateway = false);
static bool netInterfaceIsExist(const std::string& interfaceName);
static bool netInterfaceIsAvailable(const std::string& interfaceName);
static Poco::Net::IPAddress localAddress(const std::string& interfaceName);
static std::map<std::string, Poco::Net::IPAddress> localAddresses();
static std::vector<std::string> localAddressesList();
static std::string localAddressOfAvailable();
static std::string getFirstWirelessInterfaceName();
static Poco::Net::IPAddress subnetMask(const std::string& interfaceName);
static Poco::Net::NetworkInterface::MACAddress macAddress(const std::string& interfaceName);
static Poco::Net::NetworkInterface::MACAddress getFirstMacAddress();
static Poco::Net::IPAddress gatewayAddress(const std::string& interfaceName);
static bool executeCommand(const std::string& command)
{
return executeCommand(command, 0);
}
static bool executeCommand(const std::string& command, int timeout_sec)
{
std::string output;
return executeCommand(command, timeout_sec, output);
}
static bool executeCommand(const std::string& command, int timeout_sec, std::string& output);
};
}
#endif

62
util/Task.cpp Normal file
View file

@ -0,0 +1,62 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Task.h"
#include "Logger.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Task::Task(const std::string& name): _name(name)
{
}
void Task::run(std::function<void(std::atomic<bool>& isCancelled)> func)
{
cancel();
Poco::FastMutex::ScopedLock lock(_lock);
_pThread = new Poco::Thread();
_isCancelled = false;
_pThread->startFunc(
[this, func]() -> void
{
try
{
func(_isCancelled);
}
catch (Poco::Exception& e)
{
logger().error("Task(%s): %s", name(), e.displayText());
}
catch (std::exception& e)
{
logger().error("Task(%s): %s", name(), std::string(e.what()));
}
});
}
void Task::cancel()
{
if (isRunning())
{
Poco::FastMutex::ScopedLock lock(_lock);
_isCancelled = true;
_pThread->join();
_pThread.reset();
}
}
bool Task::isRunning()
{
Poco::FastMutex::ScopedLock lock(_lock);
return !_pThread.isNull() && _pThread->isRunning();
}
}

93
util/Task.h Normal file
View file

@ -0,0 +1,93 @@
#ifndef EBDC071F_8F58_4C5D_ACB9_7C534616BEC5
#define EBDC071F_8F58_4C5D_ACB9_7C534616BEC5
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/Thread.h>
#include <exception>
#include <functional>
#include <iostream>
#include <string>
#include <thread>
#include <tuple>
#include <utility>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Task
{
public:
Task(const std::string& name = "Anonymous");
public:
void run(std::function<void(std::atomic<bool>& isCancelled)> func);
template <typename Func, typename... Args> static void run2(Func&& func, Args&&... args)
{
run2WithError(std::forward<Func>(func), nullptr, std::forward<Args>(args)...);
}
template <typename Func, typename... Args>
static void run2WithError(Func&& func, std::function<void(std::exception_ptr)> onError, Args&&... args)
{
auto boundFunc = std::bind(std::forward<Func>(func), std::forward<Args>(args)...);
std::thread(
[boundFunc, onError]() mutable
{
try
{
boundFunc();
}
catch (const std::exception& e)
{
if (onError)
{
onError(std::current_exception());
}
else
{
std::cerr << "Util::Task::run2 exception: " << e.what() << std::endl;
}
}
catch (...)
{
if (onError)
{
onError(std::current_exception());
}
else
{
std::cerr << "Util::Task::run2 exception!" << std::endl;
}
}
})
.detach();
}
void cancel();
bool isRunning();
const std::string& name() const;
private:
const std::string _name;
std::atomic<bool> _isCancelled;
Poco::FastMutex _lock;
Poco::SharedPtr<Poco::Thread> _pThread;
};
inline const std::string& Task::name() const
{
return _name;
}
}
#endif

38
util/Temperature.cpp Normal file
View file

@ -0,0 +1,38 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Temperature.h"
#include "Device.h"
#include "Application.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
float Temperature::read()
{
Device device("/sys/devices/virtual/mstar/msys/TEMP_R");
try
{
device.open();
char str[64];
device.read((uint8_t*)str, sizeof(str));
int temp;
sscanf(str, "Temperature %d", &temp);
device.close();
return temp;
}
catch (const std::exception& e)
{
}
return 0.0f;
}
}

22
util/Temperature.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef AE8A4A92_D487_4675_9A6A_0B266E47C520
#define AE8A4A92_D487_4675_9A6A_0B266E47C520
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Temperature
{
public:
static float read();
};
}
#endif

169
util/WifiUtil.h Normal file
View file

@ -0,0 +1,169 @@
#ifndef WIFI_UTIL_H
#define WIFI_UTIL_H
#include <Poco/JSON/Array.h>
#include <Poco/JSON/Object.h>
#include <Poco/StringTokenizer.h>
#include <sstream>
#include <vector>
#include "Application.h"
#include "util/SystemInfo.h"
namespace Util
{
class WifiUtil
{
private:
public:
static constexpr const char* frpcConfigPathName = "/customer/frp/frpc.toml";
static constexpr const char* frpcPathName = "/customer/frp/frpc";
public:
static Poco::JSON::Object::Ptr wifiStaStatus(const std::string& interfaceName)
{
Poco::JSON::Object::Ptr statusMap = new Poco::JSON::Object;
try
{
std::string retStr;
bool retFlag = Util::SystemInfo::executeCommand("wpa_cli -i " + interfaceName + " status", 10, retStr);
if (retFlag)
{
Poco::StringTokenizer lines(retStr, "\n", Poco::StringTokenizer::TOK_TRIM);
for (const auto& line : lines)
{
size_t eqPos = line.find('=');
if (eqPos != std::string::npos)
{
std::string key = line.substr(0, eqPos);
std::string value = line.substr(eqPos + 1);
statusMap->set(key, value);
}
}
}
}
catch (const std::exception& e)
{
logger().error("wifiStaStatus failed: %s", std::string(e.what()));
}
return statusMap;
}
static bool wifiStaScan(const std::string& interfaceName, bool wifiEnable = false)
{
try
{
return Util::SystemInfo::executeCommand("/customer/customer-network.sh " + interfaceName + " scan");
}
catch (const std::exception& e)
{
logger().error("wifiStaScan failed: %s", std::string(e.what()));
}
return false;
}
static Poco::JSON::Array::Ptr wifiStaScanResults(const std::string& interfaceName)
{
Poco::JSON::Array::Ptr jsonArray = new Poco::JSON::Array;
std::string retStr;
bool retFlag = Util::SystemInfo::executeCommand("wpa_cli -i " + interfaceName + " scan_results", 10, retStr);
if (!retFlag)
{
return jsonArray;
}
Poco::StringTokenizer lines(retStr, "\n", Poco::StringTokenizer::TOK_TRIM);
for (size_t i = 1; i < lines.count(); ++i)
{
Poco::StringTokenizer tokens(lines[i], "\t", Poco::StringTokenizer::TOK_TRIM);
if (tokens.count() < 4)
continue;
Poco::JSON::Object::Ptr netObj = new Poco::JSON::Object;
netObj->set("bssid", tokens[0]);
netObj->set("frequency", std::stoi(tokens[1]));
netObj->set("signal_level", std::stoi(tokens[2]));
if (tokens.count() == 4 && tokens[3].find('[') == std::string::npos)
{
netObj->set("flags", "OPEN");
netObj->set("ssid", convertHexEscape(tokens[3]));
}
else
{
netObj->set("flags", tokens[3]);
netObj->set("ssid", tokens.count() > 4 ? convertHexEscape(tokens[4]) : "");
}
jsonArray->add(netObj);
}
return jsonArray;
}
static bool wifiStaConnect(const std::string& interfaceName, int status)
{
if (status == 1)
{
return Util::SystemInfo::executeCommand("/customer/customer-network.sh " + interfaceName + " up");
}
else if (status == 0)
{
return Util::SystemInfo::executeCommand("/customer/customer-network.sh " + interfaceName + " down");
}
return false;
}
static Poco::JSON::Array::Ptr wifiGetApClients(const std::string& interfaceName)
{
Poco::JSON::Array::Ptr jsonArray = new Poco::JSON::Array;
std::string retStr;
bool retFlag = Util::SystemInfo::executeCommand(
"ip neigh show dev " + interfaceName + " nud reachable | awk '{print $1, $3}'", 10, retStr);
if (!retFlag)
{
return jsonArray;
}
Poco::StringTokenizer lines(retStr, "\n", Poco::StringTokenizer::TOK_TRIM);
for (size_t i = 0; i < lines.count(); ++i)
{
if (lines[i].empty())
continue;
Poco::StringTokenizer tokens(lines[i], " ", Poco::StringTokenizer::TOK_TRIM);
if (tokens.count() < 2)
{
continue;
}
std::string ip = tokens[0], mac = tokens[1];
Poco::JSON::Object::Ptr netObj = new Poco::JSON::Object;
netObj->set("ip", ip);
netObj->set("mac", mac);
jsonArray->add(netObj);
}
return jsonArray;
}
static std::string convertHexEscape(const std::string& input)
{
std::stringstream ss;
for (size_t i = 0; i < input.length();)
{
if (input.substr(i, 2) == "\\x" && i + 3 < input.length())
{
ss << static_cast<char>(std::stoi(input.substr(i + 2, 2), nullptr, 16));
i += 4;
}
else
{
ss << input[i];
i++;
}
}
return ss.str();
}
};
}
#endif

203
util/linux-system-usage.hpp Normal file
View file

@ -0,0 +1,203 @@
/* ---------------------------------------------------------------------
* Luiz Carlos doleron @ 2021
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero Public License for more details.
* ----------------------------------------------------------------------
*/
#ifndef _GET_CPU_USAGE_LINUX_
#define _GET_CPU_USAGE_LINUX_
#include <iostream>
#include <fstream>
#include <sstream>
#include <sys/statvfs.h>
namespace get_system_usage_linux
{
struct CPU_stats
{
// see http://www.linuxhowtos.org/manpages/5/proc.htm
int user;
int nice;
int system;
int idle;
int iowait;
int irq;
int softirq;
int steal;
int guest;
int guest_nice;
int get_total_idle()
const {
return idle + iowait;
}
int get_total_active()
const {
return user + nice + system + irq + softirq + steal + guest + guest_nice;
}
};
struct Memory_stats
{
int total_memory;
int available_memory;
int free_memory;
int total_swap;
int free_swap;
float get_memory_usage() {
const float result = static_cast<float>(total_memory - available_memory) / total_memory;
return result;
}
float get_swap_usage() {
const float result = static_cast<float>(total_swap - free_swap) / total_swap;
return result;
}
};
inline CPU_stats read_cpu_data()
{
CPU_stats result;
std::ifstream proc_stat("/proc/stat");
if (proc_stat.good())
{
std::string line;
getline(proc_stat, line);
unsigned int *stats_p = (unsigned int *)&result;
std::stringstream iss(line);
std::string cpu;
iss >> cpu;
while (iss >> *stats_p)
{
stats_p++;
};
}
proc_stat.close();
return result;
}
inline int get_val(const std::string &target, const std::string &content) {
int result = -1;
std::size_t start = content.find(target);
if (start != std::string::npos) {
int begin = start + target.length();
std::size_t end = content.find("kB", start);
std::string substr = content.substr(begin, end - begin);
result = std::stoi(substr);
}
return result;
}
inline Memory_stats read_memory_data()
{
Memory_stats result;
std::ifstream proc_meminfo("/proc/meminfo");
if (proc_meminfo.good())
{
std::string content((std::istreambuf_iterator<char>(proc_meminfo)),
std::istreambuf_iterator<char>());
result.total_memory = get_val("MemTotal:", content);
result.free_memory = get_val("MemFree:", content);
result.total_swap = get_val("SwapTotal:", content);
result.free_swap = get_val("SwapFree:", content);
result.available_memory = get_val("MemAvailable:", content);
}
proc_meminfo.close();
return result;
}
inline float get_cpu_usage(const CPU_stats &first, const CPU_stats &second) {
const float active_time = static_cast<float>(second.get_total_active() - first.get_total_active());
const float idle_time = static_cast<float>(second.get_total_idle() - first.get_total_idle());
const float total_time = active_time + idle_time;
return active_time / total_time;
}
inline float get_disk_usage(const std::string & disk) {
struct statvfs diskData;
statvfs(disk.c_str(), &diskData);
auto total = diskData.f_blocks;
auto free = diskData.f_bfree;
auto diff = total - free;
float result = static_cast<float>(diff) / total;
return result;
}
//see https://unix.stackexchange.com/questions/304845/discrepancy-between-number-of-cores-and-thermal-zones-in-sys-class-thermal/342023
inline int find_thermalzone_index() {
int result = 0;
bool stop = false;
// 20 must stop anyway
for (int i = 0; !stop && i < 20; ++i) {
std::ifstream thermal_file("/sys/class/thermal/thermal_zone" + std::to_string(i) + "/type");
if (thermal_file.good())
{
std::string line;
getline(thermal_file, line);
if (line.compare("x86_pkg_temp") == 0) {
result = i;
stop = true;
}
} else {
stop = true;
}
thermal_file.close();
}
return result;
}
inline int get_thermalzone_temperature(int thermal_index) {
int result = -1;
std::ifstream thermal_file("/sys/class/thermal/thermal_zone" + std::to_string(thermal_index) + "/temp");
if (thermal_file.good())
{
std::string line;
getline(thermal_file, line);
std::stringstream iss(line);
iss >> result;
} else {
throw std::invalid_argument(std::to_string(thermal_index) + " doesn't refer to a valid thermal zone.");
}
thermal_file.close();
return result;
}
}
#endif

711
util/structs/ParamStruct.h Normal file
View file

@ -0,0 +1,711 @@
#ifndef PARAMSTRUCT_H
#define PARAMSTRUCT_H
#include <string>
#include <vector>
#include "Application.h"
#include "util/xpack/json.h"
namespace Util
{
namespace Structs
{
struct Channel_stream
{
std::string sn;
int enable = 0;
std::string name;
std::string appname;
std::string streamid;
int bitrate = 8192;
std::string bitrate_ctl;
std::string codec;
int encoded_size_mode = 0;
std::string encoded_size;
int fps = 30;
int gopmode = 0;
int goptxt = 30;
int video_flip_mirror_enable = 0;
int video_clipping_enable = 0;
int video_clipping_left = 0;
int video_clipping_right = 0;
int video_clipping_width = 100;
int video_clipping_height = 100;
int video_rotate = 0;
std::string pkey;
std::string rtmp_url;
std::string rtsp_url;
std::string flv_url;
std::string hls_url;
std::string webrtc_url;
std::string ts_url;
std::string srt_url;
std::string h5_url;
int multicast_enable = 1;
std::string multicast_ip_port;
int push_enable = 1;
std::string push_url;
int push_connected = 0;
Channel_stream() = default;
XPACK(O(enable, sn, name, appname, streamid, bitrate, bitrate_ctl, codec, encoded_size_mode, encoded_size, fps,
gopmode, goptxt, video_flip_mirror_enable, video_clipping_enable, video_clipping_left, video_clipping_right,
video_clipping_width, video_clipping_height, video_rotate, pkey, rtmp_url, rtsp_url, flv_url, hls_url,
webrtc_url, ts_url, srt_url, multicast_enable, multicast_ip_port, push_enable, push_url),
C(c_push_connected, F(0), push_connected))
template <class Decoder>
bool c_push_connected_decode(Decoder& de, Channel_stream& t, const char* name, int& data, const xpack::Extend* ext)
{
(void)t;
bool ret = false;
try
{
std::string raw;
ret = de.decode(name, raw, ext);
if (ret)
{
sscanf(raw.c_str(), "%i", &data);
}
}
catch (const std::exception& e)
{ // TODO:如果原则本身就是整数,会转换失败,怎么办???
ret = de.decode(name, data, ext);
}
return ret;
}
template <class Encoder>
bool c_push_connected_encode(Encoder& en, const Channel_stream& t, const char* name, const int& data,
const xpack::Extend* ext)
{
(void)t;
bool ret = en.encode(name, data, ext);
return ret;
}
};
struct Channel_input_type_mix_layer
{
int chn_id = 0;
int stream_id = 0;
int audio_status = 0;
float pos_x = 0.0f;
float pos_y = 0.0f;
float pos_w = 0.3333f;
float pos_h = 0.3333f;
int z_index = 0;
Channel_input_type_mix_layer() = default;
XPACK(O(chn_id, stream_id, audio_status, pos_x, pos_y, pos_w, pos_h, z_index))
};
struct Channel_input_type_mix
{
std::string layout_id;
std::vector<Channel_input_type_mix_layer> layers;
Channel_input_type_mix() = default;
XPACK(O(layout_id, layers))
};
struct Channel_input_type_ndi
{
std::string ndirecv_name;
Channel_input_type_ndi() = default;
XPACK(O(ndirecv_name))
};
struct Channel_input_type_net
{
int stream_mode = 0;
int min_delay = 500;
std::string path;
int rtp_type = 0;
std::string appname;
std::string streamid;
std::string push_remote_ip;
std::string pull_path;
int proxy_status;
int ipc_onvif = 0;
std::string ipc_ip;
int ipc_port = 8000;
int ipc_web_port = 80;
std::string ipc_username = "admin";
std::string ipc_password;
std::string ipc_ptz_xaddr;
std::string ipc_ptz_profile_token;
Channel_input_type_net() = default;
XPACK(O(stream_mode, min_delay, path, rtp_type, pull_path, proxy_status, appname, streamid, push_remote_ip,
ipc_onvif, ipc_ip, ipc_port, ipc_web_port, ipc_username, ipc_password, ipc_ptz_xaddr,
ipc_ptz_profile_token))
};
struct Channel_input_type_usb
{
std::string alsa;
int channel = -1;
std::string lnk;
Channel_input_type_usb() = default;
XPACK(O(alsa, channel, lnk))
};
struct Channel_audio_set
{
std::vector<int> audio_src = {0};
int audio_channel = 0;
int digital_volume_gain = 0;
int pts_offset = 0;
int encoder_bitrate = 128;
int encoder_methods = 0;
int sampling_rate = -1;
int collected_audio_frame = 0;
Channel_audio_set() = default;
XPACK(O(audio_src, audio_channel, digital_volume_gain, pts_offset, encoder_bitrate, encoder_methods, sampling_rate,
collected_audio_frame))
};
struct Channel_input_type_digimage_point
{
float x = 0;
float y = 0;
Channel_input_type_digimage_point() = default;
XPACK(O(x, y))
};
struct Channel_input_type_digimage
{
int auto_update = 0;
int tolerance = 0;
int src_fg_channel = 0;
std::string src_bg_type;
std::string src_bg_image;
int src_bg_channel = 0;
std::vector<Channel_input_type_digimage_point> point;
Channel_input_type_digimage() = default;
XPACK(O(auto_update, tolerance, src_fg_channel, src_bg_type, src_bg_image, src_bg_channel, point))
};
struct Channel_input_type_carousel_file
{
std::string filename;
int video_len = 0;
Channel_input_type_carousel_file() = default;
XPACK(O(filename, video_len))
};
struct Channel_input_type_carousel
{
int decodeA = 0;
int decodeV = 0;
int playing_file_index = -1;
long playing_file_position = 0;
std::vector<Channel_input_type_carousel_file> files;
Channel_input_type_carousel() = default;
XPACK(O(decodeA, decodeV, files), C(c_playing_file_index, F(0), playing_file_index))
template <class Decoder>
bool c_playing_file_index_decode(Decoder& de, Channel_input_type_carousel& t, const char* name, int& data,
const xpack::Extend* ext)
{
(void)t;
bool ret = false;
try
{
std::string raw;
ret = de.decode(name, raw, ext);
if (ret)
{
sscanf(raw.c_str(), "%i", &data);
}
}
catch (const std::exception& e)
{ // TODO:如果原则本身就是整数,会转换失败,怎么办???
ret = de.decode(name, data, ext);
}
return ret;
}
template <class Encoder>
bool c_playing_file_index_encode(Encoder& en, const Channel_input_type_carousel& t, const char* name,
const int& data, const xpack::Extend* ext)
{
(void)t;
bool ret = en.encode(name, data, ext);
return ret;
}
};
struct Channel_channel
{
int enable = 0;
std::string name = "HDMI";
std::string input_type = "HDMI";
int input_video_framerate = 0;
std::string input_video_size;
int input_video_collected_frame = 0;
int input_video_lost_frame = 0;
Channel_audio_set audio_set;
std::vector<Channel_stream> streams;
Channel_input_type_digimage input_type_digimage;
Channel_input_type_carousel input_type_carousel;
Channel_input_type_mix input_type_mix;
Channel_input_type_ndi input_type_ndi;
Channel_input_type_net input_type_net;
Channel_input_type_usb input_type_usb;
Channel_channel() = default;
XPACK(O(enable, name, input_type, input_video_framerate, input_video_size, input_video_collected_frame,
input_video_lost_frame, audio_set, streams, input_type_digimage, input_type_carousel, input_type_mix,
input_type_ndi, input_type_net, input_type_usb))
};
struct Channel_params
{
std::vector<Channel_channel> channels;
Channel_params() = default;
XPACK(O(channels))
};
struct Record_record
{
int enable = 0;
int flv = 0;
int mp4 = 1;
int mp4_max_second = 600;
int status = 0;
std::string start_time;
Record_record() = default;
XPACK(O(enable, flv, mp4, mp4_max_second, status, start_time))
};
struct Record_params
{
std::string base_path;
int loop_storage_enable = 0;
int loop_storage_min_size = 1024;
std::vector<std::vector<Record_record>> records;
Record_params() = default;
XPACK(O(base_path, loop_storage_enable, loop_storage_min_size, records))
};
struct Gb28181_chn
{
int enable = 0;
std::string gb_chn_id;
std::string gb_chn_name;
int stream_state = 0;
Gb28181_chn() = default;
XPACK(O(enable, gb_chn_id, gb_chn_name, stream_state))
};
struct Gb28181_params
{
int enable = 0;
std::string spec_model = "H.264/H.265 Encoder";
std::string manufacture = "Encoder";
int local_sip_port = 5061;
int con_state = 0;
std::string auth_id;
std::string auth_passwd;
int heartbeat_cycle = 30;
int heartbeat_timeout_cnt = 3;
int registration_period = 86400;
std::string sip_server_addr;
std::string sip_server_domain;
std::string sip_server_id;
int sip_server_port = 5060;
std::vector<std::vector<Gb28181_chn>> gb28181_chns;
Gb28181_params() = default;
XPACK(O(enable, spec_model, manufacture, local_sip_port, con_state, auth_id, auth_passwd, heartbeat_cycle,
heartbeat_timeout_cnt, registration_period, sip_server_addr, sip_server_domain, sip_server_id,
sip_server_port, gb28181_chns))
};
struct Loopout_audio
{
int enable = 1;
int chn_id = 0;
int audio_volume_gain;
Loopout_audio() = default;
XPACK(O(enable, chn_id, audio_volume_gain))
};
struct Loopout_video
{
int enable = 1;
int low_latency = 0;
std::string encoded_size;
int rotate = 0;
int mirror = 0;
int chn_id = 0;
int stream_id = 0;
std::string type;
int ui = 0;
int vid = 0;
std::string matrix;
int luma = 64;
int contrast = 64;
int saturation = 64;
int hue = 64;
int audio_volume_gain;
Loopout_video() = default;
XPACK(O(enable, low_latency, encoded_size, rotate, mirror, chn_id, stream_id, type, ui, vid, matrix, luma, contrast,
saturation, hue, audio_volume_gain))
};
struct Loopout_params
{
Loopout_audio audio;
Loopout_video hdmi;
Loopout_video sdi;
Loopout_params() = default;
XPACK(O(hdmi, sdi))
};
struct Mix_lay_def_layer
{
int chn_id = -1;
int stream_id = 0;
int audio_status = 0;
int lock = 0;
float pos_x = 0.0f;
float pos_y = 0.0f;
float pos_w = 0.3333f;
float pos_h = 0.3333f;
int z_index = 0;
Mix_lay_def_layer() = default;
XPACK(O(chn_id, stream_id, audio_status, lock, pos_x, pos_y, pos_w, pos_h, z_index))
};
struct Mix_lay_def_layout
{
std::string layout_id;
std::string layout_name;
int enable = 1;
std::vector<Mix_lay_def_layer> layers;
Mix_lay_def_layout() = default;
XPACK(O(layout_id, layout_name, layers))
};
struct Mix_lay_def
{
std::vector<Mix_lay_def_layout> layouts;
Mix_lay_def() = default;
XPACK(O(layouts))
};
struct Osd_osd
{
int enable = 0;
int chn_id = 0;
int stream_id = 0;
std::string type = "pic";
std::string content;
std::string font;
std::string color;
std::string bgcolor;
float alpha = 0;
float scale = 0;
int speed = 0;
float x = 0;
float y = 0;
int z = 0;
float w = 0;
float h = 0;
Osd_osd() = default;
XPACK(O(enable, chn_id, stream_id, type, content, font, color, bgcolor, alpha, scale, speed, x, y, z, w, h))
};
struct Osd_resource
{
std::string path;
std::string time;
std::string type;
Osd_resource() = default;
XPACK(O(path, time, type))
};
struct Osd_params
{
std::vector<std::vector<std::vector<Osd_osd>>> osds;
std::vector<Osd_resource> resources;
Osd_params() = default;
XPACK(O(osds, resources))
};
struct Push_url
{
int enable = 0;
std::string des;
std::string url;
int stream_state = 0;
Push_url() = default;
XPACK(O(enable, des, url, stream_state))
};
struct Push_params
{
int srcA = -1;
int srcV = 0;
int srcV_stream = 0;
int autorun = 0;
std::string start_day;
std::string start_time;
std::string stop_day;
std::string stop_time;
std::vector<Push_url> urls;
Push_params() = default;
XPACK(O(srcA, srcV, srcV_stream, autorun, start_day, start_time, stop_day, stop_time, urls))
};
struct Status_params
{
std::string version;
std::string image_version;
std::string serial_num;
float cpu_usage = 0;
float cpu_junc_tmp = 0;
float memory_free = 0;
float memory_total = 0;
std::string runtime;
std::string device_time;
int net_state_up = 0;
int net_state_dw = 0;
std::string fireware_download_url;
std::string fireware_upgrade_last_log;
Status_params() = default;
XPACK(O(version, serial_num, cpu_usage, cpu_junc_tmp, memory_free, memory_total, runtime, device_time, net_state_up,
net_state_dw, fireware_download_url, fireware_upgrade_last_log))
};
struct System_http
{
std::string user_name;
std::string user_password;
std::string language = "zh";
int api_auth = 0;
System_http() = default;
XPACK(O(user_name, user_password, language, api_auth))
};
struct System_service_ports
{
int nat_port_enable = 0;
std::string nat_port_host = "";
int http_api_port = 22112;
int http_api_port_nat_port = 22112;
int http_port = 80;
int http_port_nat_port = 80;
int rtmp_port = 1935;
int rtmp_port_nat_port = 1935;
int rtsp_port = 554;
int rtsp_port_nat_port = 554;
int srt_port = 9000;
int srt_port_nat_port = 9000;
int nat_frp_enable = 0;
int nat_frp_pid = 0;
System_service_ports() = default;
XPACK(O(nat_port_enable, nat_port_host, http_api_port, http_api_port_nat_port, http_port, http_port_nat_port,
rtmp_port, rtmp_port_nat_port, rtsp_port, rtsp_port_nat_port, srt_port, srt_port_nat_port, nat_frp_enable,
nat_frp_pid))
};
struct System_share_storage_cifs
{
int enable = 0;
std::string ip;
std::string path;
std::string username;
std::string password;
std::string local_path = "/mnt/storages/cifs";
std::string vers = "2.0";
System_share_storage_cifs() = default;
XPACK(O(enable, ip, path, username, password, local_path, vers))
};
struct System_share_storage_nfs
{
int enable = 0;
std::string ip;
std::string path;
std::string local_path = "/mnt/stroages/nfs";
std::string vers = "4";
System_share_storage_nfs() = default;
XPACK(O(enable, ip, path, local_path, vers))
};
struct System_share_storage
{
System_share_storage_cifs cifs;
System_share_storage_nfs nfs;
System_share_storage() = default;
XPACK(O(cifs, nfs))
};
struct Network_set_config
{
int enable = 0;
int dhcp_enable = 0;
std::string host_ip;
std::string mac_addr;
std::string netmask;
std::string gateway;
std::string dns1;
std::string dns2;
std::string ssid;
std::string ssid_pwd;
std::string ssid_mode = "STA";
Network_set_config() = default;
XPACK(O(enable, dhcp_enable, host_ip, mac_addr, netmask, gateway, dns1, dns2, ssid, ssid_pwd, ssid_mode))
};
struct Network_set
{
Network_set_config eth0;
Network_set_config wlan0;
Network_set() = default;
XPACK(O(eth0, wlan0))
};
struct Time_set
{
int ntp_enable = 1;
std::string ntp_server = "ntp.ntsc.ac.cn";
int ntp_server_port = 123;
int ntp_idle = 60;
std::string time_zone = "8";
Time_set() = default;
XPACK(O(ntp_enable, ntp_server, ntp_server_port, ntp_idle, time_zone))
};
struct Schedule_restart
{
int restart_enable = 0;
std::string restart_time = "06:10";
Schedule_restart() = default;
XPACK(O(restart_enable, restart_time))
};
struct System_serialtrans
{
std::string type = "232";
int work_mode = 0;
int baudrate = 9600;
std::string remote_addr;
int remote_port = 10000;
int listen_port = 10000;
System_serialtrans() = default;
XPACK(O(type, work_mode, baudrate, remote_addr, remote_port, listen_port))
};
struct System_nattraversal_frp
{
int enable = 0;
std::string remote_addr;
int remote_port = 10001;
int listen_port = 10001;
System_nattraversal_frp() = default;
XPACK(O(enable, remote_addr, remote_port, listen_port))
};
struct System_nattraversal_rtty
{
int enable = 0;
std::string name;
std::string remote_addr;
std::string id = "dfdfdsfdfdsfds";
std::string token;
System_nattraversal_rtty() = default;
XPACK(O(enable, name, remote_addr, id, token))
};
struct System_nattraversal
{
System_nattraversal_frp frp;
System_nattraversal_rtty rtty;
XPACK(O(frp, rtty))
};
struct System_moresettings
{
std::string logo_title = "H.265/H.264 Encoder";
std::string logo_image;
std::string menu_location = "left";
std::string h5play_title = "";
int screen_turn_time = 5;
System_moresettings() = default;
XPACK(O(logo_title, logo_image, menu_location, h5play_title, screen_turn_time))
};
struct System_params
{
System_http http;
Network_set network_set;
Time_set time_set;
Schedule_restart schedule_restart;
std::vector<System_serialtrans> serialtrans;
System_nattraversal nattraversal;
System_moresettings moresettings;
System_service_ports service_ports;
System_share_storage share_storage;
System_params() = default;
XPACK(O(http, network_set, time_set, schedule_restart, serialtrans, nattraversal, moresettings, service_ports,
share_storage))
};
struct Onvif_chn
{
uint32_t enable = 1;
Onvif_chn() = default;
XPACK(O(enable))
};
struct Onvif_params
{
int auth_enable = 0;
std::string auth_username = "admin";
std::string auth_password = "admin";
int time_sync_enable = 0;
uint32_t enable = 1;
std::string device_name = "H.264/H.265 Encoder";
std::string spec_model = "H.264/H.265 Encoder";
std::string manufacture = "DaweiVision";
std::string firmware_version;
std::string serial_number;
std::string hardware_id = "1.0";
std::string device_type = "Encoder";
std::string ip;
std::string mac;
uint web_service_bind_port = 8000;
std::string location_country = "China";
std::string location_city = "Chengdu";
std::vector<std::vector<Onvif_chn>> chns;
Onvif_params() = default;
XPACK(O(auth_enable, auth_username, auth_password, time_sync_enable, enable, device_name, spec_model, manufacture,
firmware_version, serial_number, hardware_id, device_type, ip, mac, web_service_bind_port, location_country,
location_city, chns))
};
struct Diskinfo
{
int status = 0;
std::string device = "";
std::string path = "";
unsigned long long total = 0;
unsigned long long used = 0;
unsigned long long available = 0;
unsigned long long free = 0;
Diskinfo() = default;
XPACK(O(status, device, path, total, used, available, free))
};
}
}
#endif

3
util/util.cmake Normal file
View file

@ -0,0 +1,3 @@
add_subdirectory(util)
include_directories(util)
target_link_libraries(${PROJNAME} PRIVATE util)

201
util/xpack/LICENSE Executable file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

84
util/xpack/README-bson.md Executable file
View file

@ -0,0 +1,84 @@
* 用于在C++结构体和bson之间互相转换
* 这个库可以配合[mongoxclient](https://github.com/xyz347/mongoxclient)使用
# 目录
- [encoder](#encoder)
- [decoder](#decoder)
- [bson types](#bsontypes)
- [builder](#builder)
- [重要说明](#重要说明)
## encoder
用于将结构体转成bson格式。范例:
```C++
struct Test {
int64_t id;
std::string name;
std::string email;
XPACK(A(id, "bson:_id"), O(name, email));
};
Test t;
std::string data = xpack::bson::encode(t); // data就是t转成bson的数据
```
## decoder
用于将bson数据转成结构体
用于将结构体转成bson格式。范例:
```C++
struct Test {
int64_t id;
std::string name;
std::string email;
XPACK(A(id, "bson:_id"), O(name, email));
};
std::string data;
// data = xxxx; data存储了相应的bson数据
Test t;
xpack::bson::decode(data, t);
// xpack::bson::decode 还有另一个参数方式:static void decode(const uint8_t* data, size_t len, T &val)
```
## bsontypes
- date time: `bson_date_time_t`
- timestamp: `bson_timestamp_t`
- binary: `bson_binary_t`
- regex: `bson_regex_t`
- oid: `bson_oid_t`
- decimal128: `bson_decimal128_t`
前4种是xbson额外定义的,后面两种是定义在libbson内的,为了统一,前4种也是定义在全局命名空间。
## builder
**需要C++11或以上版本支持**。用json的格式来构造bson数据。查询接口中的query,一般只需要指定很少几个字段,这个场景用结构体表达不是很方便。用builder就会简单很多。基本用法是:
1. 定义一个BsonBuilder:
- 参数是一个json格式的字符串
- 里面的双引号用单引号替代
- 变量用问号作为占位符,问号后面可以加一个单词用来作为助记符,比如"?"和"?uid"是等效的
- key也可以是变量,key作为变量,问号不用加引号
- BsonBuilder是线程安全的,建议定义为static变量,这样json字符串只需解析一次
范例:
```C++
static xpack::BsonBuilder bd("{'_id':?, ?:?}"); // 带占位符的builder
static xpack::BsonBuilder empty("{}"); // 空bson
static xpack::BsonBuilder student("{'type':'student'}"); // 不带占位符的builder
```
2. 调用Encode函数来构造bson数据,Encode的参数对应的是各个占位符,因此参数个数应该和占位符一致
比如对于上述的BsonBuilder
```C++
std::string data1 = bd.Encode(12345, "type", "master");
std::string data2 = empty.Encode();
std::string data3 = student.Encode();
// 这些bson的数据可以用在mongoxclient的各个接口中
```
3. 其他的一些API
- EncodeAsJson 和Encode类似,但是输出的是json格式的数据,可以用于调试等场景(所以这个其实也是个JsonBuilder)
- Error 如果构造BsonBuilder的json字符串有误,这个接口可以返回错误信息
- static std::string En(const std::string&fmt, Args... args)。这个是把构造BsonBuilder和Encode合在一起了,不是很建议使用,建议用于调试,因为每次都要构造一次BsonBuilder
## 重要说明
- 依赖于[libbson](https://github.com/mongodb/libbson/tree/1.0.0),需自行安装

407
util/xpack/README-en.md Executable file
View file

@ -0,0 +1,407 @@
xpack
====
* Used to convert between C++ structure and json/xml, bson is supported in [xbson](https://github.com/xyz347/xbson).
* Only header files, no need to compile library files, so there is no Makefile.
* Support MySQL, depends on `libmysqlclient-dev`, need to install by yourself. **not fully tested**
* Support Sqlite, depends on [libsqlite3](https://cppget.org/libsqlite3), need to install it yourself. **not fully tested**
* Support yaml, depend on [yaml-cpp](https://github.com/jbeder/yaml-cpp), need to install it yourself. **not fully tested**
* For details, please refer to the example
------
* [Quick start](#quick-start)
* [Container Support](#container-support)
* [FLAG](#flag)
* [Alias](#alias)
* [Bitfield](#bitfield)
* [Inherit](#inherit)
* [enum](#enum)
* [Custom codec](#custom-codec)
* [union](#union)
* [Schema indeterminate type](#schema-indeterminate-type)
* [Define macro outside the structure](#define-macro-outside-the-structure)
* [Array](#array)
* [Format indentation](#format-indentation)
* [XML array](#xml-array)
* [CDATA](#cdata)
* [Qt support](#qt-support)
* [Important note](#important-note)
Quick start
----
- Use macro `XPACK`(or `XPACK_OUT` if you don't want to change the structure) include structure members
- Use xpack::json::encode to convert structure to json
- Use xpack::json::decode to convert json to structure
```C++
#include <iostream>
#include "xpack/json.h" // include this header file for json or "xpack/xml.h" for xml
using namespace std;
struct User {
int id;
string name;
XPACK(O(id, name)); // Add XPACK at the end of structure, up to 99 'O' can be added, and each 'O' can contain up to 99 members
};
int main(int argc, char *argv[]) {
User u;
string data = "{\"id\":12345, \"name\":\"xpack\"}";
xpack::json::decode(data, u); // json to structure
cout<<u.id<<';'<<u.name<<endl;
string json = xpack::json::encode(u); // structure to json
cout<<json<<endl;
return 0;
}
```
Container Support
----
The following containers are currently supported (std)
- vector
- set
- list
- map<string, T>
- map<integer, T> // only for json
- unordered_map<string, T> (need C++11)
- shared_ptr (need C++11)
FLAG
----
In the macro `XPACK`, variables need to be enclosed with letters, such as `XPACK(O(a,b))`, `XPACK` can contain multiple letters, and each letter can contain multiple variables. Currently supported letters are:
- X Usage: X(F(flag1, flag2...), member1, member2,...) Multiple flags can be written in 'F', currently supported flags are:
- 0 no flag
- OE omitempty. When encoding, if the variable is 0 or an empty string or false, the variable is ignored
- EN empty as null. When json encoding, encode empty variable as null(OE is ignore the variable)
- M mandatory. When decoding, if the key corresponding to the variable does not exist, an exception is thrown
- ATTR attribute. When xml encode, put the value in attribute
- SL single line, When json encoding, put vector in single line
- C Usage: C(customcodec, F(flag1,flags...), member1, member2,...) For custom codec function, please refer to [Custom codec](#custom-codec) for details
- O Usage: O(member1, member2, ...) Same as X(F(0), member1, member2, ...) no flags
- M Usage: M(member1, member2, ...) Same as X(F(M), member1, member2, ...) mandatory
- A Usage: A(member1, alias1, member2, alias2...) Used when the key and the variable name are different, please refer to [Alias](#alias) for details
- AF Usage: AF(F(flag1, flag2,...), member1, alias1, member2, alias2...) Alias with flags
- B Usage: B(F(flag1, flag2, ...), member1, member2, ...) For bitfield, please refer to [Bitfield](#bitfield) for details. **bitfield not support alias**
- I Usage: I(baseclass1, baseclass2....), baseclassx is parent class, please refer to [Inherit](#inherit) for details
- E Usage: E(member1, member2) For enumeration variables that do not support C++11. **E not support alias**
Alias
----
- Used when the key and the variable name are different
- Usage: A(member1, alias1, member2, alias2...) or AF(F(flag1, flag2,...), member1, alias1, member2, alias2...), alias's format is "x t:n"
- x stands for global alias, not required
- t stands for type(currently support json/xml/bson), n stands for type alias, type alias is not required
- The order of precedence is: type alias, global alias, variable name
- For example, `id bson:_id`, in bson will use `_id` and json/xml will use `id`
- Type alias can take some type flags, format is "t:n,flag1,flag2,...", currently supported flags are:
- cdata. For xml, indicates that it is cdata
- sbs. For xml vector, In normal, `vector<int> nums` will be encoded into `<nums><nums>1</nums><nums>2</nums><nums>`, in sbs is `<nums>1</nums><nums>2</nums>`
``` C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
long uid;
string name;
XPACK(A(uid, "id"), O(name)); // "uid" use alias "id"
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"id\":123, \"name\":\"Pony\"}";
xpack::json::decode(json, t);
cout<<t.uid<<endl;
return 0;
}
```
Bitfield
----
- Use 'B' include bitfield members. **bitfield not support alias**
``` C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
short ver:8;
short len:8;
string name;
XPACK(B(F(0), ver, len), O(name));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"ver\":4, \"len\":20, \"name\":\"IPv4\"}";
xpack::json::decode(json, t);
cout<<t.ver<<endl;
cout<<t.len<<endl;
return 0;
}
```
Inherit
----
- Use 'I' include parent classes. If you don't need variables from the parent class, you don't need use 'I'
- Parent class need add macro XPACK/XPACK_OUT
```C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct P1 {
string mail;
XPACK(O(mail));
};
struct P2 {
long version;
XPACK(O(version));
};
struct Test:public P1, public P2 {
long uid;
string name;
XPACK(I(P1, P2), O(uid, name));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"mail\":\"pony@xpack.com\", \"version\":2019, \"id\":123, \"name\":\"Pony\"}";
xpack::json::decode(json, t);
cout<<t.mail<<endl;
cout<<t.version<<endl;
return 0;
}
```
enum
----
- If support C++11, enum is same as other type, use X/O/M/A is ok
- Otherwise need to use 'E', usage: E(F(...), member1, member2, ...)
```C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
enum Enum {
X = 0,
Y = 1,
Z = 2,
};
struct Test {
string name;
Enum e;
XPACK(O(name), E(F(0), e));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"name\":\"IPv4\", \"e\":1}";
xpack::json::decode(json, t);
cout<<t.name<<endl;
cout<<t.e<<endl;
return 0;
}
```
Custom codec
----
Used in scenarios where you want to define your own codec method, such as:
- Wish to encode numeric types in string or hexadecimal
- Don't want to encode the struct variable by variable:
```C++
struct Time {
long ts; //unix timestamp
};
```
Don't want to encode to {"ts":1218196800} but "2008-08-08 20:00:00"
There are two ways to achieve:
- Use xtype. You can refer to this [example](example/xtype.cpp)
- Use 'C' include members need to custome codec(Hereinafter referred to as the `C method`), You can refer to this [example](example/custom.cpp)
Both methods need to implement custom codec, but there are some differences:
- xtype is at the type level, that is, once a certain type is encapsulated with xtype, the custom encode/decode will take effect for this type. xtype cannot be used for basic types (int/string, etc.)
- The C method can support basic types (int/string, etc.) and non-basic types, but only works on variables contained in C, such as
```C++
int a;
int b;
XPACK(O(a), C(custome_int, F(0), b ))
```
Then "a" still uses the default codec, and "b" uses the custom codec.
1. xtype takes precedence over the XPACK macro, that is, if xtype is defined, the encode/decode of xtype will be used first
2. The C method takes precedence over xtype, that is, the variables contained in C must use the codec method specified in C
With these two features, some more flexible codec control can be realized. For example, this [example](example/xtype_advance.cpp) realizes a function of encoding according to the variable situation. If Sub.type==1, then encode seq1, Otherwise encode seq2. `__x_pack_decode` and `__x_pack_encode` are the decode/encode functions added to the structure by the XPACK macro, and the custom codec function can call the default codec function of xpack through these functions.
union
----
Unions can be handled using [Custom codec](#custom-codec). You can refer to this [example](example/union1.cpp)
Array
----
- When decoding, if the number of elements exceeds the length of the array, it will be truncated
- The char array needs to have a '\0' terminator
```C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
char name[64];
char email[64];
XPACK(O(name, email));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"name\":\"Pony\", \"email\":\"pony@xpack.com\"}";
xpack::json::decode(json, t);
cout<<t.name<<endl;
cout<<t.email<<endl;
return 0;
}
```
Schema indeterminate type
----
- Json only
- Use [xpack::JsonData](json_data.h) for this kind of content
- You can refer to this [example](example/json-data.cpp)
- The main APIs for xpack::JsonData are:
- Type. To get the field type
- IsXXX. To check is the field type is xxx, same as `return Type()==xxxx;`
- GetXXX. To get the field value
- operator bool. To check is't a valid xpack::JsonData
- Size. To get the array's elements number
- `operator [](size_t index)`. Used to get the index element in array
- `operator [](const char *key)`. Used to get the element by key in object
- Iterator:
- Begin. Get the first element's iterator
- Next. Get the next iterator
- Key. Get the key of iterator
- Val. Get the value of iterator
Define macro outside the structure
----
- Use `XPACK_OUT` instead of `XPACK` to include structure members
- `XPACK_OUT` must define in global namespace
```c++
#include <sys/time.h>
#include <iostream>
#include "xpack/json.h"
using namespace std;
/*
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
};
*/
// timeval is thirdparty struct
XPACK_OUT(timeval, O(tv_sec, tv_usec));
struct T {
int a;
string b;
timeval t;
XPACK(O(a, b, t));
};
int main(int argc, char *argv[]) {
T t;
T r;
t.a = 123;
t.b = "xpack";
t.t.tv_sec = 888;
t.t.tv_usec = 999;
string s = xpack::json::encode(t);
cout<<s<<endl;
xpack::json::decode(s, r);
cout<<r.a<<','<<r.b<<','<<r.t.tv_sec<<','<<r.t.tv_usec<<endl;
return 0;
}
```
Format indentation
----
- The default encode is not indented, and the readability is not good
- If indentation is required, it can be controlled by the last two parameters of encode:
- indentCount Indicates the number of characters for indentation, <0 means no indentation, 0 means newline but no indentation
- indentChar Characters that represent indentation, use spaces or tabs
XML array
----
- Arrays use variable names as element labels by default, such as "ids":[1,2,3] will be encoded as:
``` xml
<ids>
<ids>1</ids>
<ids>2</ids>
<ids>3</ids>
</ids>
```
- You can use alias to change the label name. For example: A(ids,"xml:ids,vl@id"), vl@id means to use id as label:
``` xml
<ids>
<id>1</id>
<id>2</id>
<id>3</id>
</ids>
```
- If want to encode array element side by side, can use `sbs` flag. For example: `A(ids, "xml:ids,sbs")`. **Note that the sbs tag can only be used for arrays(vector), and it may crash if used elsewhere**
``` xml
<ids>1</ids>
<ids>2</ids>
<ids>3</ids>
```
CDATA
----
- For xml CDATA, need to use `cdata` flag. For example: A(data, "xml:data,cdata")
- variable type must be std::string
- Non-cdata types can also add cdata flag, which will be processed as ordinary strings
Qt support
----
- Modify [config.h](config.h) to enable XPACK_SUPPORT_QT(or enable it in compile flags)
- Currently supports: QString/QMap/QList/QVector
Important note
----
- Try not to start the variable name with __x_pack, otherwise it may conflict with the library.
- vc6 is not supported
- msvc has only been tested on vs2019
- The serialization and deserialization of json uses [rapidjson](https://github.com/Tencent/rapidjson) (November 2018 edition)
- The deserialization of xml uses [rapidxml](http://rapidxml.sourceforge.net)
- The serialization of xml is written by myself, without reference to RFC, there may be some differences from the standard.

418
util/xpack/README.md Executable file
View file

@ -0,0 +1,418 @@
xpack
====
[English](README-en.md)
* 用于在C++结构体和json/xml/yaml/bson/mysql/sqlite之间互相转换
* 只有头文件, 无需编译库文件,所以也没有Makefile。
* 支持bson,依赖于`libbson-1.0`,需自行安装。**未经充分测试**,具体请参考[README](README-bson.md)
* 支持MySQL,依赖于`libmysqlclient-dev`,需自行安装。**未经充分测试**
* 支持Sqlite,依赖于[libsqlite3](https://cppget.org/libsqlite3),需自行安装。**未经充分测试**
* 支持yaml,依赖于[yaml-cpp](https://github.com/jbeder/yaml-cpp),需自行安装。**未经充分测试**
* 具体可以参考example的例子
------
* [基本用法](#基本用法)
* [容器支持](#容器支持)
* [FLAG](#flag)
* [别名](#别名)
* [位域](#位域)
* [继承](#继承)
* [枚举](#枚举)
* [自定义编解码](#自定义编解码)
* [union](#union)
* [不定类型](#不定类型)
* [数组](#数组)
* [第三方类和结构体](#第三方类和结构体)
* [格式化缩进](#格式化缩进)
* [XML数组](#xml数组)
* [CDATA](#cdata)
* [Qt支持](#qt支持)
* [MySQL](#mysql)
* [重要说明](#重要说明)
基本用法
----
- 结构体后面用XPACK宏包含各个变量,XPACK内还需要一个字母,不同字母的意义请参考[FLAG](#flag)
- 用xpack::json::encode把结构体转json
- 用xpack::json::decode把json转结构体
```C++
#include <iostream>
#include "xpack/json.h" // Json包含这个头文件,xml则包含xpack/xml.h
using namespace std;
struct User {
int id;
string name;
XPACK(O(id, name)); // 添加宏定义XPACK在结构体定义结尾
};
int main(int argc, char *argv[]) {
User u;
string data = "{\"id\":12345, \"name\":\"xpack\"}";
xpack::json::decode(data, u); // json转结构体
cout<<u.id<<';'<<u.name<<endl;
string json = xpack::json::encode(u); // 结构体转json
cout<<json<<endl;
return 0;
}
```
容器支持
----
目前支持下列容器(std)
- vector
- set
- list
- map<string, T>
- map<integer, T> // 仅JSON,XML不支持
- unordered_map<string, T> (需要C++11支持)
- shared_ptr (需要C++11支持)
FLAG
----
宏XPACK里面,需要用字母将变量包含起来,比如XPACK(O(a,b)),XPACK可以包含多个字母,每个字母可以包含多个变量。目前支持的字母有:
- X。格式是X(F(flag1, flag2...), member1, member2,...) F里面包含各种FLAG,目前支持的有:
- 0 没有任何FLAG
- OE omitempty,encode的时候,如果变量是0或者空字符串或者false,则不生成对应的key信息
- EN empty as null, 用于json的encode,OE是直接不生成empty的字段,EN则是生成一个null
- M mandatory,decode的时候,如果这个字段不存在,则抛出异常,用于一些id字段。
- ATTR attribute,xml encode的时候,把值放到attribute里面。
- SL single line, json encode的时候,对于数组,放在一行里面
- C。格式是C(customcodec, F(flag1,flags...), member1, member2,...)用于自定义编解码函数,详情请参考[自定义编解码](#自定义编解码)
- O。等价于X(F(0), ...) 没有任何FLAG。
- M。等价于X(F(M),...) 表示这些字段是必须存在的。
- A。[别名](#别名),A(member1, alias1, member2, alias2...),用于变量和key名不一样的情况
- AF。带FLAG的[别名](#别名),AF(F(flag1, flag2,...), member1, alias1, member2, alias2...)
- B。[位域](#位域),B(F(flag1, flag2, ...), member1, member2, ...) **位域不支持别名**
- I。[继承](#继承),I(baseclass1, baseclass2....),里面放父类
- E。[枚举](#枚举):
- 如果编译器支持C++11,不需要用E,枚举可以放X/O/M/A里面。
- 否则枚举只能放E里面,不支持别名
别名
----
- 用于变量名和key名不一致的场景
- 格式是A(变量,别名....)或者AF(F(flags), 变量,别名....),别名的格式是"x t:n"的格式
- x表示全局别名,t表示类型(目前支持json、xml、bson),n表示类型下的别名
- 全局别名可以没有,比如`json:_id`是合法的
- 类型别名可以没有,比如`_id`是合法的
- 有类型别名优先用类型别名,否则用全局别名,都没有,则用变量名
``` C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
long uid;
string name;
XPACK(A(uid, "id"), O(name)); // "uid"的别名是"id"
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"id\":123, \"name\":\"Pony\"}";
xpack::json::decode(json, t);
cout<<t.uid<<endl;
return 0;
}
```
位域
----
- 使用"B"来包含位域变量,**位域不支持别名**
``` C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
short ver:8;
short len:8;
string name;
XPACK(B(F(0), ver, len), O(name));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"ver\":4, \"len\":20, \"name\":\"IPv4\"}";
xpack::json::decode(json, t);
cout<<t.ver<<endl;
cout<<t.len<<endl;
return 0;
}
```
继承
----
- 使用"I"来包含父类。需要用到父类的变量就包含,用不到可以不包含。
- 父类的父类也需要包含,比如class Base; class Base1:public Base; class Base2:public Base1;那么在Base2中需要I(Base1, Base)
- 父类也需要定义XPACK/XPACK_OUT宏。
```C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct P1 {
string mail;
XPACK(O(mail));
};
struct P2 {
long version;
XPACK(O(version));
};
struct Test:public P1, public P2 {
long uid;
string name;
XPACK(I(P1, P2), O(uid, name));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"mail\":\"pony@xpack.com\", \"version\":2019, \"id\":123, \"name\":\"Pony\"}";
xpack::json::decode(json, t);
cout<<t.mail<<endl;
cout<<t.version<<endl;
return 0;
}
```
枚举
----
- 如果编译器支持C++11,则枚举和普通变量名一样,放X/O/M/A里面皆可。
- 否则需要放到E里面,格式是E(F(...), member1, member2, ...)
```C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
enum Enum {
X = 0,
Y = 1,
Z = 2,
};
struct Test {
string name;
Enum e;
XPACK(O(name), E(F(0), e));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"name\":\"IPv4\", \"e\":1}";
xpack::json::decode(json, t);
cout<<t.name<<endl;
cout<<t.e<<endl;
return 0;
}
```
自定义编解码
----
应用场景
- 有些基础类型想用自定义方式编码,比如用字符串的方式来编码整数/浮点数
- 部分类型可能不想按结构体变量逐个编码,比如定义了一个时间结构体:
```C++
struct Time {
long ts; //unix timestamp
};
```
并不希望编码成{"ts":1218196800} 这种格式,而是希望编码成"2008-08-08 20:00:00"这种格式。
这里有两种方式:
- 使用xtype,可以参考[例子](example/xtype.cpp)
- 用C来包含需要自定义编解码的变量(下面简称C方法),可以参考[例子](example/custom.cpp)
两种方法本质上都是自己去实现encode/decode,但是有以下区别:
- xtype是类型级别的,也就是一旦某个类型用xtype封装之后,那么自定义的encode/decode就对这个类型生效。xtype无法作用于基本类型(int/string等)
- C方法能支持基本类型(int/string等)和非基本类型,但是仅作用于用C包含的变量,比如int a;int b; O(a), C(custome_int, F(0), b);那么a还是用默认的编解码,b才是用自定义的编解码。
1. xtype优先于XPACK宏,也就是定义了xtype的,会优先使用xtype的encode/decode
2. C方法优先于xtype,也就是用C包含的变量,一定会用C里面指定的编解码方法。
用这两个特性,可以实现一些比较灵活的编解码控制,比如这个[例子](example/xtype_advance.cpp)实现了一个根据变量情况来编码的功能,如果Sub.type==1则encode seq1,否则encode seq2. `__x_pack_decode`和`__x_pack_encode`是XPACK宏给结构体添加的decode/encode函数,自定义编解码函数可以通过这些函数调用xpack默认的编解码功能。
union
----
可以使用[自定义编解码](#自定义编解码)来处理联合体,可以参考[范例](example/union1.cpp)
数组
----
- decode的时候如果元素个数超过数组的长度,会截断
- char数组按有\0结束符处理
```C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
char name[64];
char email[64];
XPACK(O(name, email));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"name\":\"Pony\", \"email\":\"pony@xpack.com\"}";
xpack::json::decode(json, t);
cout<<t.name<<endl;
cout<<t.email<<endl;
return 0;
}
```
不定类型
----
- 用于json的schema不确定的场景
- 用[xpack::JsonData](json_data.h)来接收这些信息
- 可以参考[例子](example/json-data.cpp)
- xpack::JsonData主要的方法有:
- Type。用于获取类型
- IsXXX系列函数。用于判断是否是某种类型,基本等价于return Type()==xxxx;
- GetXXX系列函数。用来提取值。
- 重载bool。用来判断是否是一个合法的JsonData。
- Size。用于数组类型判断元素的个数
- `operator [](size_t index)` 用来取数组的第index个元素(从0开始)
- `operator [](const char *key)` 用来根据key取Object类型的元素
- Begin。用来遍历Object的元素,取第一个。
- Next。配合Begin使用,获取下一个元素。
- Key。配置Begin和Next使用,遍历的时候获取Key
第三方类和结构体
----
- 用XPACK_OUT而非XPACK来包含变量
- XPACK_OUT必须定义在全局命名空间
```c++
#include <sys/time.h>
#include <iostream>
#include "xpack/json.h"
using namespace std;
/*
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
};
*/
// timeval is thirdparty struct
XPACK_OUT(timeval, O(tv_sec, tv_usec));
struct T {
int a;
string b;
timeval t;
XPACK(O(a, b, t));
};
int main(int argc, char *argv[]) {
T t;
T r;
t.a = 123;
t.b = "xpack";
t.t.tv_sec = 888;
t.t.tv_usec = 999;
string s = xpack::json::encode(t);
cout<<s<<endl;
xpack::json::decode(s, r);
cout<<r.a<<','<<r.b<<','<<r.t.tv_sec<<','<<r.t.tv_usec<<endl;
return 0;
}
```
格式化缩进
----
- encode缺省生成的json/xml是没有缩进的,适合程序使用,如果让人读,可以进行缩进。
- encode的最后两个参数控制
- indentCount 表示缩进的字符数,<0表示不缩进,0则是换行但是不缩进
- indentChar 表示缩进的字符,用空格或者制表符
XML数组
----
- 数组默认会用变量名作为元素的标签,比如"ids":[1,2,3],对应的xml是:
``` xml
<ids>
<ids>1</ids>
<ids>2</ids>
<ids>3</ids>
</ids>
```
- 可以用别名的方式来控制数组的元素的标签,比如A(ids,"xml:ids,vl@id"),vl后面跟着一个@xx,xx就是数组的标签,生成的结果就是:
``` xml
<ids>
<id>1</id>
<id>2</id>
<id>3</id>
</ids>
```
- 如果希望数组直接展开,而不是在外面包一层,可以用别名加"sbs"的flag来实现,比如A(ids, "xml:ids,sbs"),**注意,sbs标签仅能用于数组,其他地方使用可能会崩溃**
``` xml
<ids>1</ids>
<ids>2</ids>
<ids>3</ids>
```
CDATA
----
- 对于CDATA类型,需要用"cdata"的flag来实现,比如A(data, "xml:data,cdata")
- cdata只能用std::string来接收
- 如果变量对应的xml不是CDATA结构,会按普通字符串来处理比如`<data>hello</data>`也可以解析成功
Qt支持
----
- 修改config.h,开启XPACK_SUPPORT_QT这个宏(或者在编译选项开启)
- 当前支持 QString/QMap/QList/QVector
MySQL
----
- 目前仅支持decode,还不支持encode
- 未经过完整测试,使用需要谨慎
- 当前支持类型有:
- string。简单测试。
- 整型。简单测试。
- 浮点型。未测试。
- 用整型(比如time_t)接收TIME/DATETIME/TIMESTAMP。未测试。
- 自定义类型转换,is_xpack_mysql_xtype,类似于xtype。未测试。
- api有两个(xpack::mysql::) :
- `static void decode(MYSQL_RES *result, T &val)`
- 用来将MYSQL_RES转成结构体或者vector<>,如果是非vector,则只转换第一个row
- `static void decode(MYSQL_RES *result, const std::string&field, T &val)`
- 用来解析某个字段,用于只想获得某个字段内容的场景,比如select id from mytable where name = lilei,只想获得id信息。val支持vector
重要说明
----
- 变量名尽量不要用__x_pack开头,不然可能会和库有冲突。
- vc6不支持。
- msvc没有做很多测试,只用2019做过简单测试。
- json的序列化反序列化用的是[rapidjson](https://github.com/Tencent/rapidjson)
- xml的反序列化用的是[rapidxml](http://rapidxml.sourceforge.net)
- xml的序列化是我自己写的,没有参考RFC,可能有和标准不一样的地方.
- 有疑问可以加QQ群878041110

54
util/xpack/bson.h Executable file
View file

@ -0,0 +1,54 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_BSON_H
#define __X_PACK_BSON_H
#include "bson_decoder.h"
#include "bson_encoder.h"
#include "xpack.h"
#ifdef X_PACK_SUPPORT_CXX0X // support c++11 or later
#include "bson_builder.h"
#endif
namespace xpack {
class bson {
public:
template <class T>
static void decode(const std::string &data, T &val) {
BsonDecoder de;
de.decode(data, val);
}
template <class T>
static void decode(const uint8_t* data, size_t len, T &val) {
BsonDecoder de;
de.decode(data, len, val);
}
template <class T>
static std::string encode(const T &val) {
BsonEncoder en;
return en.encode(val);
}
};
}
#endif

588
util/xpack/bson_builder.h Executable file
View file

@ -0,0 +1,588 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_BSON_BUILDER_H
#define __X_PACK_BSON_BUILDER_H
#include <stdexcept>
#include "bson_encoder.h"
#ifndef X_PACK_SUPPORT_CXX0X // support c++11 or later
#error "need c++11 or later"
#endif
namespace xpack {
class BsonBuilder {
enum {
Unknow = 0,
ObjectBegin = 1<<0,
ObjectEnd = 1<<1,
ArrayBegin = 1<<2,
ArrayEnd = 1<<3,
Comma = 1<<4,
Colon = 1<<5,
Integer = 1<<6,
Float = 1<<7,
String = 1<<8,
Bool = 1<<9,
Variable = 1<<10,
BEof = 1<<11
};
std::string tokenName(int type) const {
std::string ret;
if (0 != (ObjectBegin&type)) {
ret += "/{";
}
if (0 != (ObjectEnd&type)) {
ret += "/}";
}
if (0 != (ArrayBegin&type)) {
ret += "/[";
}
if (0 != (ArrayEnd&type)) {
ret += "/]";
}
if (0 != (Comma&type)) {
ret += "/,";
}
if (0 != (Colon&type)) {
ret += "/:";
}
if (0 != ((Float|Integer|Bool)&type)) {
ret += "/number/bool";
}
if (0 != (String&type)) {
ret += "/string";
}
if (0 != (Variable&type)) {
ret += "/variable";
}
if (!ret.empty()) {
ret = ret.substr(1);
}
return ret;
}
struct Token {
int type; // see enum
union {
const char *sval;
long lval;
double dval;
bool bval;
};
Token():type(Unknow),sval(NULL){}
};
struct Item {
Token tk;
const char *key;
bool kvar; // key is variable
Item():tk(),key(NULL), kvar(false) {
}
};
struct Encoder {
const std::vector<const Item*>* items;
BsonWriter wr;
XEncoder<BsonWriter> en;
size_t idx; // items index;
size_t vidx; // variable index;
std::vector<std::string> vkeys; // store variable key
Item tmpIt; // key:value when key is a variable, copy item to here
Item *pIt; // point to tmpIt
std::vector<size_t> arrayIndex;
std::vector<const char*> objKeys; // store object key. use in object end
std::vector<const char*> arrKeys; // store array key, use in array end
Encoder(const std::vector<const Item*>&its):items(&its), en(wr), idx(0), vidx(0), pIt(NULL){}
// array key. index: "0", "1", "2", "3"
const char *Key(const char *raw) {
if (NULL != raw) {
return raw;
} else if (arrayIndex.size()>0) {
size_t idx = arrayIndex.size() - 1;
return wr.IndexKey(arrayIndex[idx]++);
} else {
return NULL;
}
}
};
public:
BsonBuilder(const std::string& _fmt):raw(_fmt),en(NULL) {
dup = new char[raw.length()+1];
strcpy(dup, raw.c_str());
if (parse()) {
for (size_t i=0; i<items.size(); ++i) {
if (items[i]->kvar || items[i]->tk.type==Variable) {
return;
}
}
// no variable. pre build it
bstr = Encode();
}
}
~BsonBuilder() {
if (NULL != dup) {
for (size_t i=0; i<items.size(); ++i) {
delete items[i];
}
delete[] dup;
dup = NULL;
} else if (NULL != en) {
delete en;
en = NULL;
}
}
std::string Error() const {
return fmtErr;
}
template <typename... Args>
std::string Encode(Args... args) {
if (!bstr.empty()) {
return bstr;
} else if (!fmtErr.empty()) {
return "";
}
BsonBuilder bd(*this);
bd.iencode(args...);
bd.end(NULL);
return bd.en->en.String();
}
template <typename... Args>
std::string EncodeAsJson(Args... args) {
if (!fmtErr.empty()) {
return "";
}
BsonBuilder bd(*this);
bd.iencode(args...);
bd.end(NULL);
return bd.en->wr.Json();
}
template <typename... Args>
static std::string En(const std::string&fmt, Args... args) {
BsonBuilder bb(fmt);
return bb.Encode(args...);
}
private: // encoder
BsonBuilder(const BsonBuilder&bd):dup(NULL) {
en = new Encoder(bd.items);
}
// implement iencode()/iencode(T)/iencode(T, Args...) to expand template arguments
void iencode() {
}
void iencode(const char*val) { // xencoder not allow pointer
std::string s(val);
encode(s);
++en->vidx;
}
template <typename T>
void iencode(const T &val) {
encode(val);
++en->vidx;
}
template <typename T, typename... Args>
void iencode(const T& first, Args... args) {
iencode(first);
iencode(args...);
}
template <typename T>
BsonBuilder& encode(const T &val) {
// encode data items without variable
for (; en->idx<en->items->size(); ++en->idx) {
const Item *it = en->items->at(en->idx);
if ((!it->kvar) && it->tk.type!=Variable) {
add(it, 1);
} else {
break;
}
}
if (en->idx >= en->items->size()) { // finish
return *this;
} else if (NULL != en->pIt) { // key:val. key and val is variable, key replaced in last encode
add(en->pIt, val);
en->pIt = NULL;
++en->idx;
return *this;
}
const Item *it = en->items->at(en->idx);
if (it->kvar) { // key:val and key is variable, we fill string to key
en->tmpIt = *it; // copy item info
en->pIt = &en->tmpIt;
en->pIt->key = dumpStr(val); // dump key string
en->pIt->kvar = false;
if (it->tk.type != Variable) { // value not variable
add(en->pIt, 1);
en->pIt = NULL;
++en->idx;
}
} else { // val is variable
add(it, val);
++en->idx;
}
return *this;
}
// add items to encoder
template <typename T>
void add(const Item *it, const T&val) {
const char *key = en->Key(it->key);
switch (it->tk.type) {
case ObjectBegin:
en->en.ob(key);
en->objKeys.push_back(key);
break;
case ObjectEnd:
en->en.oe(en->objKeys.back());
en->objKeys.pop_back();
break;
case ArrayBegin:
en->arrayIndex.push_back(0);
en->en.ab(key);
en->arrKeys.push_back(key);
break;
case ArrayEnd:
en->arrayIndex.pop_back();
en->en.ae(en->arrKeys.back());
en->arrKeys.pop_back();
break;
case Integer:
en->en.add(key, it->tk.lval);
break;
case Float:
en->en.add(key, it->tk.dval);
break;
case String:
en->en.add(key, std::string(it->tk.sval));
break;
case Bool:
en->en.add(key, it->tk.bval);
break;
case Variable:
en->en.add(key, val);
break;
}
}
// encode the last none variable item
void end(std::string *err) {
for (; en->idx<en->items->size(); ++en->idx) {
const Item *it = en->items->at(en->idx);
if (it->kvar || it->tk.type==Variable) {
if (NULL != err) {
*err = "less variable";
}
return;
}
add(it, 1);
}
}
// type to const char*
const char *dumpStr(const std::string&str) {
en->vkeys.push_back(str);
return en->vkeys[en->vkeys.size()-1].c_str();
}
const char *dumpStr(const char *key) {
return key;
}
template <typename T>
const char *dumpStr(const T&val) {
(void)val;
std::string err = "variable "+Util::itoa(en->vidx)+" is a key, must be std::string or const char*";
throw std::runtime_error(err);
return "";
}
bool isEnd(char ch) {
return ch==' ' || ch==':' || ch==',' || ch=='}' || ch==']'|| ch=='\0';
}
// a simple bson string parser
bool parse() {
std::vector<char> stack;
Item *item;
char *p = dup;
char *end = p;
int keyIndex = -1;
char ch;
bool waitkey = false; // {key:value, expect key
int exp = ObjectBegin; // expect type
while (true) {
item = new Item;
p = end;
nextToken(p, &end, item->tk);
if (item->tk.type == BEof) {
delete item;
if (!fmtErr.empty()) {
return false;
}
if (stack.size() > 0) {
if ((ch=stack[stack.size()-1]) == '{') { // in object
fmtErr = "miss } at the end of string";
} else {
fmtErr = "miss ] at the end of string";
}
return false;
}
return true;
} else if (item->tk.type == Unknow) {
fmtErr = "unknow token:";
fmtErr += p;
delete item;
break;
}
if ((item->tk.type&exp) == 0) {
fmtErr = "unexpected token. expect["+tokenName(exp)+"]. but get["+tokenName(item->tk.type)+"]";
if (NULL != item->tk.sval) {
fmtErr += ". in [";
fmtErr += item->tk.sval;
fmtErr += "]";
}
delete item;
break;
}
// key:value parse value done
if (keyIndex >= 0) {
const Item *it = items[keyIndex];
items.pop_back();
keyIndex = -1;
if (it->tk.type == String) {
item->key = it->tk.sval;
} else {
item->kvar = true;
}
delete it;
}
switch (item->tk.type) {
case ObjectBegin:
items.push_back(item);
exp = ObjectEnd | Variable | String;
stack.push_back('{');
waitkey = true;
break;
case ObjectEnd:
waitkey = false;
case ArrayEnd:
items.push_back(item);
stack.pop_back();
if (stack.size() == 0) {
exp = BEof;
} else if ((ch=stack[stack.size()-1]) == '{') { // in object
exp = ObjectEnd | Comma | String | Variable;
} else {
exp = ArrayEnd | Comma | Integer | Float | String | Bool | Variable;
}
break;
case ArrayBegin:
items.push_back(item);
exp = ObjectBegin | ArrayBegin | ArrayEnd | Integer | Float | String | Bool | Variable;
stack.push_back('[');
break;
case Comma:
if ((ch=stack[stack.size()-1]) == '{') { // in object
exp = String | Variable;
waitkey = true;
} else {
exp = ObjectBegin | ArrayBegin | Integer | Float | String | Bool | Variable;
}
delete item;
break;
case Colon:
exp = ObjectBegin | ArrayBegin | Integer | Float | String | Bool | Variable;
keyIndex = int(items.size()) - 1;
waitkey = false;
delete item;
break;
case Integer:
case Float:
case String:
case Bool:
case Variable:
items.push_back(item);
if ((ch=stack[stack.size()-1]) == '{') { // in object
if (waitkey) {
waitkey = false;
exp = Colon;
} else {
exp = ObjectEnd | Comma;
}
} else {
exp = ArrayEnd | Comma | Integer | Float | String | Bool | Variable;
}
break;
}
}
return false;
}
// get next token from data, may modify data
void nextToken(char* data, char**end, Token &tk) {
char ch;
size_t i = 0;
*end = NULL;
while (data[i] == ' ') {
i++;
}
if (data[i] == '\0') {
tk.type = BEof;
return;
}
tk.sval = data+i;
while ((ch=data[i++]) != '\0') {
if (tk.type == String) {
if (ch != '\'') {
continue;
} else if (isEnd(data[i])) {
data[i-1] = '\0';
*end = data+i;
} else {
tk.type = Unknow;
}
return;
} else if (tk.type == Integer || tk.type == Float) {
if (ch>='0' && ch<='9') {
continue;
} else if (ch == '.') {
tk.type = Float;
continue;
} else if (isEnd(ch)) {
if (tk.type == Integer) {
data[i-1] = '\0';
if (Util::atoi(tk.sval, tk.lval)) {
*end = data+i-1;
} else {
tk.type = Unknow;
}
data[i-1] = ch;
} else {
char *dend;
tk.dval = strtod(tk.sval, &dend);
if (dend != data+i-1) {
tk.type = Unknow;
} else {
*end = dend;
}
}
} else {
tk.type = Unknow;
}
return;
} else if (tk.type == Variable) {
if ((ch>='0' && ch<='9') || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z') || ch=='_') {
continue;
} else if (isEnd(ch)) {
*end = data+i-1;
} else {
tk.type = Unknow;
}
return;
} else if (ch == '{') {
tk.type = ObjectBegin;
break;
} else if (ch == '}') {
tk.type = ObjectEnd;
break;
} else if (ch == '[') {
tk.type = ArrayBegin;
break;
} else if (ch == ']') {
tk.type = ArrayEnd;
break;
} else if (ch == ',') {
tk.type = Comma;
break;
} else if (ch == ':') {
tk.type = Colon;
break;
} else if (ch == '\'') { // string
tk.type = String;
tk.sval = data+i;
} else if (ch == '?') {
tk.type = Variable;
} else if (ch=='-' || ch=='+' || (ch>='0' && ch<='9')) {
tk.type = Integer;
tk.sval = data+i-1;
} else if (ch == 't') { //
if (strncmp(data+i-1, "true", 4) == 0 && isEnd(data[i+3])) {
tk.type = Bool;
tk.bval = true;
*end = data+i+3;
}
return;
} else if (ch == 'f') { //
if (strncmp(data+i-1, "false", 5) == 0 && isEnd(data[i+4])) {
tk.type = Bool;
tk.bval = false;
*end = data+i+4;
}
return;
} else {
return;
}
}
if (tk.type != Unknow) {
if (*end==NULL && (tk.type==String||tk.type==Float||tk.type==Integer||tk.type==Variable)) {
fmtErr = "no end for string/integer/float/variable";
tk.type = BEof;
} else {
*end = data+i;
}
}
}
std::string raw;
std::string fmtErr;
std::string lastErr;
std::string bstr; // for that without any variable
char* dup; // for parse
std::vector<const Item*> items;
Encoder *en;
};
}
#endif

279
util/xpack/bson_decoder.h Executable file
View file

@ -0,0 +1,279 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_BSON_DECODER_H
#define __X_PACK_BSON_DECODER_H
#include <string.h>
#include <libbson-1.0/bson.h>
#include "util.h"
#include "xdecoder.h"
#include "bson_type.h"
namespace xpack {
class BsonNode {
typedef XDecoder<BsonNode> decoder;
friend class BsonDecoder;
public:
typedef size_t Iterator;
BsonNode(const bson_iter_t* iter = NULL):it(iter),inited(false) {
if (NULL != it) {
type = bson_iter_type(it);
}
}
inline static const char * Name() {
return "bson";
}
operator bool () const {
return it != NULL;
}
bool IsNull() const {
return NULL!=it && BSON_TYPE_NULL==bson_iter_type(it);
}
BsonNode Find(decoder&de, const char*key, const Extend *ext) {
(void)de;
(void)ext;
if (!inited) {
this->init();
}
if (BSON_TYPE_DOCUMENT != type) {
de.decode_exception("not document", NULL);
}
node_index::iterator iter = _childs_index.find(key);
if (iter != _childs_index.end()) {
return BsonNode(&_childs[iter->second]);
} else {
return BsonNode();
}
}
size_t Size(decoder&de) {
(void)de;
if (!inited) {
this->init();
}
if (BSON_TYPE_ARRAY == type) {
return _childs.size();
} else {
de.decode_exception("not array", NULL);
return 0;
}
}
BsonNode At(size_t index) const { // no exception
return BsonNode(&(_childs[index]));
}
BsonNode Next(decoder&de, BsonNode&p, Iterator&iter, std::string&key) {
(void)de;
if (!p.inited) {
p.init();
}
if (BSON_TYPE_DOCUMENT != p.type) {
de.decode_exception("not document", NULL);
}
if (it != p.it) {
++iter;
} else {
iter = 0;
}
if (iter != p._childs.size()) {
bson_iter_t *t = &p._childs[iter];
key = bson_iter_key(t);
return BsonNode(t);
}
return BsonNode();
}
bool Get(decoder&de, std::string&val, const Extend*ext) {
(void)ext;
if (BSON_TYPE_UTF8 == type) {
uint32_t length;
const char* data = bson_iter_utf8(it, &length);
if (NULL != data) {
val = std::string(data, length);
}
} else if (BSON_TYPE_NULL != type) {
de.decode_exception("not string", NULL);
}
return true;
}
bool Get(decoder&de, bool &val, const Extend*ext) {
(void)de;
(void)ext;
val = bson_iter_as_bool(it);
return true;
}
template <class T>
typename x_enable_if<numeric<T>::is_integer, bool>::type Get(decoder&de, T &val, const Extend*ext){
(void)de;
(void)ext;
val = (T)bson_iter_as_int64(it);
return true;
}
template <class T>
typename x_enable_if<numeric<T>::is_float, bool>::type Get(decoder&de, T &val, const Extend*ext){
(void)de;
(void)ext;
val = (T)bson_iter_double(it);
return true;
}
/*std::string json() const {
if (NULL == _data) {
return "";
}
size_t length=BSON_UINT32_TO_LE(*(int32_t*)_data);
bson_t b; // local is ok
bson_init_static(&b, _data, length);
size_t s;
char *jstr = bson_as_json(&b, &s);
if (jstr) {
std::string j(jstr);
bson_free(jstr);
return j;
} else {
return "";
}
}*/
// bson type
bool decode_type_spec(bson_oid_t &val, const Extend *ext) {
(void)ext;
const bson_oid_t *t = bson_iter_oid(it);
if (t != NULL) {
bson_oid_init_from_data(&val, t->bytes);
return true;
} else {
return false;
}
}
bool decode_type_spec(bson_date_time_t &val, const Extend *ext) {
(void)ext;
val.ts = bson_iter_date_time(it);
return true;
}
bool decode_type_spec(bson_timestamp_t &val, const Extend *ext) {
(void)ext;
bson_iter_timestamp(it, &val.timestamp, &val.increment);
return true;
}
bool decode_type_spec(bson_decimal128_t &val, const Extend *ext) {
(void)ext;
return bson_iter_decimal128(it, &val);
}
bool decode_type_spec(bson_regex_t &val, const Extend *ext) {
(void)ext;
const char *options = NULL;
const char *regex = bson_iter_regex(it, &options);
if (NULL != regex) {
val.pattern = regex;
}
if (NULL != options) {
val.options = options;
}
return true;
}
bool decode_type_spec(bson_binary_t &val, const Extend *ext) {
(void)ext;
uint32_t len = 0;
const uint8_t *data = NULL;
bson_iter_binary(it, &val.subType, &len, &data);
if (data != NULL && len > 0) {
val.data = std::string((const char*)data, size_t(len));
}
return true;
}
private:
typedef std::map<const char*, size_t, cmp_str> node_index; // index of _childs
// get object or array info
void init(bool top = false) {
inited = true;
bson_iter_t sub;
if (NULL != it) {
if (top) {
type = BSON_TYPE_DOCUMENT;
memcpy((void*)&sub, (void*)it, sizeof(sub));
} else if (!bson_iter_recurse(it, &sub)) {
return;
}
size_t i = 0;
while (bson_iter_next(&sub)) {
_childs.push_back(sub);
if (type == BSON_TYPE_DOCUMENT) {
_childs_index[bson_iter_key(&sub)] = i++;
}
}
}
}
const bson_iter_t* it;
bson_type_t type;
bool inited;
std::vector<bson_iter_t> _childs; // childs
node_index _childs_index;
};
class BsonDecoder {
public:
// if length==0, length will get from data
template <class T>
bool decode(const uint8_t*data, size_t length, T&val) {
length = (length>0)?length:BSON_UINT32_TO_LE(*(int32_t*)data);
bson_t b;
bson_iter_t it;
bson_init_static(&b, data, length);
bson_iter_init(&it, &b);
BsonNode node(&it);
node.init(true);
return XDecoder<BsonNode>(NULL, (const char*)NULL, node).decode(val, NULL);
}
template <class T>
bool decode(const std::string&data, T&val) {
return decode((const uint8_t*)data.data(), data.length(), val);
}
};
template<>struct is_xpack_type_spec<BsonNode, bson_oid_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonNode, bson_date_time_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonNode, bson_timestamp_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonNode, bson_decimal128_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonNode, bson_binary_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonNode, bson_regex_t> {static bool const value = true;};
}
#endif

246
util/xpack/bson_encoder.h Executable file
View file

@ -0,0 +1,246 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_BSON_ENCODER_H
#define __X_PACK_BSON_ENCODER_H
#include <libbson-1.0/bson.h>
#include "xpack/util.h"
#include "xpack/xencoder.h"
#include "bson_type.h"
namespace xpack {
class BsonWriter: private noncopyable {
friend class BsonBuilder;
struct Node {
const char *key;
Node *parent;
bson_t data;
Node(const char *_key = NULL, Node *_parent=NULL):key(_key), parent(_parent) {
if (NULL == parent) {
bson_init(&data);
}
}
~Node() {
if (NULL == parent) {
bson_destroy(&data);
}
}
};
friend class XEncoder<BsonWriter>;
friend class BsonEncoder;
const static bool support_null = true;
public:
BsonWriter() {
cur = new Node(NULL, NULL);
nodes.push_back(cur);
}
~BsonWriter() {
std::list<Node*>::iterator it;
for (it = nodes.begin(); it!=nodes.end(); ++it) {
delete *it;
}
}
private:
inline static const char *Name() {
return "bson";
}
inline const char *IndexKey(size_t index) {
if (index > indexStr.size()+1) {
std::string s = Util::itoa(index);
errCase.push_back(s);
return errCase.back().c_str();
}
if (index >= indexStr.size()) {
std::string s = Util::itoa(index);
indexStr.push_back(s);
}
return indexStr[index].c_str();
}
// return bson binary data
std::string String() const {
return std::string((const char*)bson_get_data(&cur->data), cur->data.len);
}
// return json format string
std::string Json() const {
size_t len;
char *jstr = bson_as_json(&cur->data, &len);
std::string ret(jstr);
bson_free(jstr);
return ret;
}
void ArrayBegin(const char *key, const Extend *ext) {
(void)ext;
if (key != NULL) {
Node *tmp = new Node(key, cur);
nodes.push_back(tmp);
bson_append_array_begin(&cur->data, key, strlen(key), &tmp->data);
cur = tmp;
}
}
void ArrayEnd(const char *key, const Extend *ext) {
(void)ext;
if (NULL != key) {
bson_append_array_end(&cur->parent->data, &cur->data);
cur = cur->parent; // do not pop and delete
}
}
void ObjectBegin(const char *key, const Extend *ext) {
(void)ext;
if (key != NULL) {
Node *tmp = new Node(key, cur);
nodes.push_back(tmp);
bson_append_document_begin(&cur->data, key, strlen(key), &tmp->data);
cur = tmp;
}
}
void ObjectEnd(const char *key, const Extend *ext) {
(void)ext;
if (NULL != key) { // in case of inherit, object key is NULL
bson_append_document_end(&cur->parent->data, &cur->data);
cur = cur->parent; // do not pop and delete
}
}
bool WriteNull(const char*key, const Extend *ext) {
(void)ext;
bson_append_null(&cur->data, key, strlen(key));
return true;
}
bool encode_bool(const char*key, const bool &val, const Extend *ext) {
(void)ext;
bson_append_bool(&cur->data, key, strlen(key), val);
return true;
}
bool encode_string(const char*key, const char*val, size_t length, const Extend *ext) {
(void)ext;
bson_append_utf8(&cur->data, key, strlen(key), val, length);
return true;
}
bool encode_string(const char*key, const std::string& val, const Extend *ext) {
(void)ext;
bson_append_utf8(&cur->data, key, strlen(key), (const char*)val.data(), val.length());
return true;
}
bool encode_string(const char*key, const char* val, const Extend *ext) {
(void)ext;
if (NULL != val) {
bson_append_utf8(&cur->data, key, strlen(key), val, strlen(val));
} else {
bson_append_utf8(&cur->data, key, strlen(key), "", 0);
}
return true;
}
#define X_PACK_BSON_ENCODE_NUMBER(vtype, ftype, ctype) \
inline bool encode_number(const char*key, const vtype&val, const Extend*ext) {\
(void)ext;\
bson_append_##ftype(&cur->data, key, strlen(key), (vtype)val);\
return true; \
}
X_PACK_BSON_ENCODE_NUMBER(char, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(signed char, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(unsigned char, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(short, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(unsigned short, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(int, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(unsigned int, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(long, int64, int64_t)
X_PACK_BSON_ENCODE_NUMBER(unsigned long, int64, int64_t)
X_PACK_BSON_ENCODE_NUMBER(long long, int64, int64_t)
X_PACK_BSON_ENCODE_NUMBER(unsigned long long, int64, int64_t)
X_PACK_BSON_ENCODE_NUMBER(float, double, double)
X_PACK_BSON_ENCODE_NUMBER(double, double, double)
X_PACK_BSON_ENCODE_NUMBER(long double, double, double)
// bson types
bool encode_type_spec(const char*key, const bson_oid_t &val, const Extend *ext) {
(void)ext;
bson_append_oid(&cur->data, key, strlen(key), &val);
return true;
}
bool encode_type_spec(const char*key, const bson_date_time_t &val, const Extend *ext) {
(void)ext;
bson_append_date_time(&cur->data, key, strlen(key), val.ts);
return true;
}
bool encode_type_spec(const char *key, const bson_timestamp_t &val, const Extend *ext) {
(void)ext;
bson_append_timestamp(&cur->data, key, strlen(key), val.timestamp, val.increment);
return true;
}
bool encode_type_spec(const char *key, const bson_decimal128_t &val, const Extend *ext) {
(void)ext;
return bson_append_decimal128(&cur->data, key, strlen(key), &val);
}
bool encode_type_spec(const char *key, const bson_regex_t &val, const Extend *ext) {
(void)ext;
return bson_append_regex(&cur->data, key, strlen(key), val.pattern.c_str(), val.options.c_str());
}
bool encode_type_spec(const char *key, const bson_binary_t &val, const Extend *ext) {
(void)ext;
return bson_append_binary(&cur->data, key, strlen(key), val.subType, (const uint8_t*)val.data.data(), val.data.length());
}
private:
std::vector<std::string> indexStr;
std::list<std::string> errCase;
std::list<Node*> nodes;
Node *cur;
};
class BsonEncoder {
public:
BsonEncoder() {
}
template <class T>
std::string encode(const T&val) {
BsonWriter wr;
XEncoder<BsonWriter> en(wr);
en.encode(NULL, val, NULL);
return wr.String();
}
template <class T>
std::string encode_as_json(const T&val) {
BsonWriter wr;
XEncoder<BsonWriter> en(wr);
en.encode(NULL, val, NULL);
return wr.Json();
}
};
template<>struct is_xpack_type_spec<BsonWriter, bson_oid_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonWriter, bson_date_time_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonWriter, bson_timestamp_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonWriter, bson_decimal128_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonWriter, bson_binary_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonWriter, bson_regex_t> {static bool const value = true;};
}
#endif

450
util/xpack/bson_type.h Executable file
View file

@ -0,0 +1,450 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_BSON_TYPE_H
#define __X_PACK_BSON_TYPE_H
#include <map>
#include <string>
#include <sstream>
#include <ctime>
#include <time.h>
#include <stdint.h>
#include <libbson-1.0/bson.h>
#include "xencoder.h"
#include "xdecoder.h"
#include "xpack.h"
/*
// define in this file
bson_date_time_t
bson_timestamp_t
bson_binary_t
bson_regex_t
// define in bson.h
bson_oid_t
bson_decimal128_t
*/
// define in global namespace for consistency with bson
struct bson_timestamp_t {
uint32_t timestamp;
uint32_t increment;
bson_timestamp_t(const uint32_t&t=0, const uint32_t&i=0):timestamp(t),increment(i){}
XPACK(A(timestamp, "t", increment, "i"));
};
struct bson_regex_t {
std::string pattern; // libbson not escape string, this should be a bug
std::string options;
// ver==1 {"$regularExpression": {pattern: <string>, "options": <string>"}}
// ver==2 {"$regex: <string>, $options: <string>"}
int ver; // ver==1
bson_regex_t():ver(2){}
};
struct bson_date_time_t {
int64_t ts; // milliseconds
bson_date_time_t(const int64_t&t=0):ts(t){}
operator int64_t() const {return ts;}
// parse rfc3339 time format(milliseconds). C++ doesn't even have a full-featured standard time library T_T
// "2000-01-01T01:01:01[.001]Z[07:00]" string should ends with \0
bool parse_rfc3339(const char buf[]) {
struct tm tm;
memset((void*)&tm, 0, sizeof(tm));
const char *end = strptime(buf, "%Y-%m-%dT%H:%M:%S", &tm);
if (end == NULL) {
return false;
}
int msec = 0;
int offset = 0;
try {
size_t pos;
if (*end == '.') {
msec = std::stoi(end+1, &pos);
if (pos==0 || pos > 3) {
return false;
}
end += pos+1;
}
if (*end != 'Z') {
pos = 0;
int hour = std::stoi(end, &pos);
if (pos == 0 || pos > 3) {
return false;
}
end += pos;
if (*end != ':') {
return false;
}
int min = std::stoi(end+1, &pos);
if (hour > 0) {
offset = hour*3600 + min*60;
} else {
offset = -(-hour*3600 + min*60);
}
} else {
++end;
if (*end != '\0') {
return false;
}
}
} catch(...) {
return false;
}
time_t tmp = mktime(&tm);
ts = tmp*1000 + msec - offset - timezone*1000;
return true;
}
};
struct bson_binary_t {
std::string data; // std::string is more general, so don't use std::basic_string<uint8_t>
bson_subtype_t subType;
inline static const char *b64() {
return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
}
// b64_ntop and b64_pton maybe should move to a public place
static void b64_ntop(const uint8_t *bin, size_t size, std::string&out) {
const char *b = b64();
out.reserve((size+2)/3*4);
size_t n = size/3*3;
size_t i;
for (i=0; i<n; i+=3) {
out.push_back(b[bin[i]>>2]);
out.push_back(b[((bin[i]&0x3)<<4) | (bin[i+1]>>4)]);
out.push_back(b[((bin[i+1]&0xf)<<2) | (bin[i+2]>>6)]);
out.push_back(b[bin[i+2]&0x3f]);
}
size_t left = size%3;
if (left == 1) {
out.push_back(b[bin[i]>>2]);
out.push_back(b[(bin[i]&0x3)<<4]);
out.push_back('=');
out.push_back('=');
} else if (left == 2) {
out.push_back(b[bin[i]>>2]);
out.push_back(b[((bin[i]&0x3)<<4) | (bin[i+1]>>4)]);
out.push_back(b[(bin[i+1]&0xf)<<2]);
out.push_back('=');
}
}
static bool b64_pton(const std::string&in, std::string&out) {
static uint8_t bmap[256] = {0};
if (bmap[255] == 0) { // init bmap, thread safe
for (size_t i=0; i<255; i++) bmap[i] = 0xff;
const char *p = b64();
for (uint8_t i=0; p[i]!='\0'; ++i) bmap[(int)p[i]] = i;
bmap[255] = 0xff;
}
size_t size = in.length();
if (size==0 || 0 != (size&0x3)) {
return false;
}
out.reserve((size+3)/4*3);
const char *data = in.data();
for (size_t i=0; i<size; i+=4) {
uint8_t i1 = bmap[(uint8_t)data[i]];
uint8_t i2 = bmap[(uint8_t)data[i+1]];
uint8_t i3 = bmap[(uint8_t)data[i+2]];
uint8_t i4 = bmap[(uint8_t)data[i+3]];
if (i1!=0xff && i2!=0xff) {
out.push_back((char)((i1<<2) | (i2>>4)));
} else {
return false;
}
if (i3 != 0xff) {
out.push_back((char)((i2<<4) | (i3>>2)));
} else if (data[i+2]=='=' && data[i+3]=='=' && i+4==size) {
return true;
} else {
return false;
}
if (i4 != 0xff) {
out.push_back((char)((i3<<6) | i4));
} else if (data[i+3]=='=' && i+4==size) {
return true;
} else {
return false;
}
}
return true;
}
};
namespace xpack {
// bson type to json
// ref https://github.com/mongodb/specifications/blob/master/source/extended-json.rst#conversion-table
// Relaxed Extended JSON Format Only
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ObjectId ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// {"$oid":"xxxxx"}
template<>
struct is_xpack_xtype<bson_oid_t> {static bool const value = true;};
template<class Decoder>
bool xpack_xtype_decode(Decoder &de, bson_oid_t &val, const Extend *ext) {
(void)ext;
char buf[25];
bool ret = de.decode("$oid", buf, NULL);
if (ret) {
bson_oid_init_from_string(&val, buf);
}
return ret;
}
template<class Encoder>
bool xpack_xtype_encode(Encoder &en, const char*key, const bson_oid_t &val, const Extend *ext) {
char buf[25];
bson_oid_to_string(&val, buf);
en.ObjectBegin(key, ext);
en.encode("$oid", buf, NULL);
en.ObjectEnd(key, ext);
return true;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ date time ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// {"$date": 12345}
// {"$date":"2006-01-02T15:04:05Z"}
// {"$date":{"$numberLong":"123456"}}
template<>
struct is_xpack_xtype<bson_date_time_t> {static bool const value = true;};
template<class Decoder>
bool xpack_xtype_decode(Decoder &de, bson_date_time_t &val, const Extend *ext) {
(void)ext;
static const char *dkey = "$date";
bool ret;
try {
ret = de.decode(dkey, val.ts, NULL);
return ret;
} catch (...){}
char buf[25];
try {
ret = de.decode(dkey, buf, NULL);
if (ret) {
ret = val.parse_rfc3339(buf);
}
return ret;
} catch (...) {}
try {
ret = de.decode("$numberLong", buf, NULL);
if (ret) {
ret = Util::atoi(buf, val.ts);
}
return ret;
} catch (...) {}
return false;
}
template<class Encoder>
bool xpack_xtype_encode(Encoder &en, const char*key, const bson_date_time_t &val, const Extend *ext) {
en.ObjectBegin(key, ext);
if (val.ts >= 0) {
char buf[32];
int msecs = int(val.ts % 1000);
time_t secs = (time_t)(val.ts/1000);
#ifndef _MSC_VER
struct tm posix_date;
size_t sz = strftime(buf, sizeof buf, "%FT%T", gmtime_r(&secs, &posix_date));
#else
size_t sz = strftime(buf, sizeof buf, "%FT%T", gmtime(&secs));
#endif
if (msecs > 0) {
sprintf(&buf[sz], ".%03dZ", msecs);
} else {
strcpy(&buf[sz], "Z");
}
en.encode("$date", buf, NULL);
} else { // key:{"$date":{"$numberLong":ts}} ts use string not int
en.ObjectBegin("$date", NULL);
en.encode("$numberLong", Util::itoa(val.ts), NULL);
en.ObjectEnd("$date", NULL);
}
en.ObjectEnd(key, ext);
return true;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ timestamp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// {"$timestamp":{"t":xxx, "i":xxx}}
template<>
struct is_xpack_xtype<bson_timestamp_t> {static bool const value = true;};
template<class Decoder>
bool xpack_xtype_decode(Decoder &de, bson_timestamp_t &val, const Extend *ext) {
(void)ext;
bool ret = de.decode_struct("$timestamp", val, NULL);
return ret;
}
template<class Encoder>
bool xpack_xtype_encode(Encoder &en, const char*key, const bson_timestamp_t &val, const Extend *ext) {
en.ObjectBegin(key, ext);
en.encode_struct("$timestamp", val, NULL);
en.ObjectEnd(key, ext);
return true;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ numberDecimal ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// {"$numberDecimal":"123.4"}
template<>
struct is_xpack_xtype<bson_decimal128_t> {static bool const value = true;};
template<class Decoder>
bool xpack_xtype_decode(Decoder &de, bson_decimal128_t &val, const Extend *ext) {
(void)ext;
char buf[BSON_DECIMAL128_STRING];
bool ret = de.decode("$numberDecimal", buf, NULL);
if (ret) {
bson_decimal128_from_string(buf, &val);
}
return ret;
}
template<class Encoder>
bool xpack_xtype_encode(Encoder &en, const char*key, const bson_decimal128_t &val, const Extend *ext) {
char buf[BSON_DECIMAL128_STRING];
bson_decimal128_to_string(&val, buf);
en.ObjectBegin(key, ext);
en.encode("$numberDecimal", buf, NULL);
en.ObjectEnd(key, ext);
return true;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ binary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// {"$binary":{"base64":"aGVsbG8=","subType":"80"}}
// {"$binary":"aGVsbG8=", "$type":"80"}
template<>
struct is_xpack_xtype<bson_binary_t> {static bool const value = true;};
template<class Decoder>
bool xpack_xtype_decode(Decoder &de, bson_binary_t &val, const Extend *ext) {
(void)ext;
try {
std::string subType;
std::string data;
bool ret1;
bool ret2;
Decoder st = de.Find("$type", NULL);
if (!st) {
Decoder bd = de.Find("$binary", NULL);
if (!bd) {
return false;
}
ret1 = bd.decode("subType", subType, NULL);
ret2 = bd.decode("base64", data, NULL);
} else {
ret1 = st.decode(subType, NULL);
ret2 = de.decode("$binary", data, NULL);
}
if (ret1 && (subType.length()==1 || subType.length()==2)) {
int st = 0;
for (size_t i=0; i<subType.length(); ++i) {
int tmp = (int)(subType[i]);
if (tmp>='0' && tmp<='9') {
tmp -= '0';
} else if (tmp>='a' && tmp<='f') {
tmp = 10 + tmp-'a';
} else if (tmp>='A' && tmp<='F') {
tmp = 10 + tmp-'A';
} else {
return false;
}
st = (st<<4) + tmp;
}
val.subType = (bson_subtype_t)st;
} else {
return false;
}
if (ret2) {
ret2 = bson_binary_t::b64_pton(data, val.data);
}
return ret2;
} catch (...){
return false;
}
return true;
}
template<class Encoder>
bool xpack_xtype_encode(Encoder &en, const char*key, const bson_binary_t &val, const Extend *ext) {
en.ObjectBegin(key, ext);
en.ObjectBegin("$binary", NULL);
std::string b64;
bson_binary_t::b64_ntop((const uint8_t*)val.data.data(), val.data.length(), b64);
en.encode("base64", b64, NULL);
std::stringstream ss;
ss<<std::hex<<val.subType;
en.encode("subType", ss.str(), NULL);
en.ObjectEnd("$binary", NULL);
en.ObjectEnd(key, ext);
return true;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ regex ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
template<>
struct is_xpack_xtype<bson_regex_t> {static bool const value = true;};
template<class Decoder>
bool xpack_xtype_decode(Decoder &de, bson_regex_t &val, const Extend *ext) {
(void)ext;
Decoder v1 = de.Find("$regularExpression", NULL);
try {
if (v1) {
v1.decode("pattern", val.pattern, NULL);
v1.decode("options", val.options, NULL);
val.ver = 1;
} else {
de.decode("$regex", val.pattern, NULL);
de.decode("$options", val.options, NULL);
val.ver = 2;
}
} catch(...){}
return true;
}
template<class Encoder>
bool xpack_xtype_encode(Encoder &en, const char*key, const bson_regex_t &val, const Extend *ext) {
en.ObjectBegin(key, ext);
if (val.ver == 1) {
en.ObjectBegin("$regularExpression", NULL);
en.encode("pattern", val.pattern, NULL);
en.encode("options", val.options, NULL);
en.ObjectEnd("$regularExpression", NULL);
} else {
en.encode("$regex", val.pattern, NULL);
en.encode("$options", val.options, NULL);
}
en.ObjectEnd(key, ext);
return true;
}
}
#endif

25
util/xpack/config.h Executable file
View file

@ -0,0 +1,25 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_CONFIG_H
#define __X_PACK_CONFIG_H
// support qt
//#define XPACK_SUPPORT_QT
#endif

27
util/xpack/example/alias.cpp Executable file
View file

@ -0,0 +1,27 @@
#include <iostream>
#include "xpack/json.h"
#include "xpack/xml.h"
using namespace std;
struct Test {
long uid;
string name;
XPACK(A(uid, "id xml:xid"), O(name)); // xml use 'xid' and others use 'id'
};
int main() {
Test t;
string json="{\"id\":123, \"name\":\"Pony\"}";
xpack::json::decode(json, t);
cout<<t.uid<<endl;
string xstr = xpack::xml::encode(t, "root");
cout<<xstr<<endl;
Test t1;
xpack::xml::decode(xstr, t1);
cout<<t1.uid<<endl;
return 0;
}

23
util/xpack/example/bitfield.cpp Executable file
View file

@ -0,0 +1,23 @@
#include <iostream>
#include "xpack/json.h"
#include "xpack/xml.h"
using namespace std;
struct Test {
short ver:8;
short len:8;
string name;
XPACK(B(F(0), ver, len), O(name));
};
int main() {
Test t;
string json="{\"ver\":4, \"len\":20, \"name\":\"IPv4\"}";
xpack::json::decode(json, t);
cout<<t.ver<<endl;
cout<<t.len<<endl;
cout<<xpack::xml::encode(t, "root")<<endl;
return 0;
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
char name[64];
char email[64];
XPACK(O(name, email));
};
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
Test t;
string json="{\"name\":\"Pony\", \"email\":\"pony@xpack.com\"}";
xpack::json::decode(json, t);
cout<<t.name<<endl;
cout<<t.email<<endl;
return 0;
}

66
util/xpack/example/custom.cpp Executable file
View file

@ -0,0 +1,66 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
unsigned int uid;
string name;
XPACK(C(hexint, F(0), uid), O(name));
};
namespace xpack {
template <class Decoder>
bool hexint_decode(Decoder&de, Test&t, const char *name, unsigned int&data, const xpack::Extend *ext) {
(void)t;
string raw;
bool ret = de.decode(name, raw, ext);
if (ret) {
sscanf(raw.c_str(), "%x", &data);
}
return ret;
}
template <class Encoder>
bool hexint_encode(Encoder&en, const Test&t, const char *name, const unsigned &data, const xpack::Extend *ext) {
(void)t;
char buf[64];
sprintf(buf, "%x", data);
bool ret = en.encode(name, buf, ext);
return ret;
}
}
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
Test t;
t.uid = 127;
t.name = "PonyJack";
string str = xpack::json::encode(t);
cout<<str<<endl;
Test n;
xpack::json::decode(str, t);
cout<<t.uid<<endl;
return 0;
}

45
util/xpack/example/encoder.cpp Executable file
View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#include "xpack/json.h"
#include "xpack/xml.h"
using namespace std;
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
vector<int> vi(3);
vi[0] = 1;
vi[1] = 2;
vi[2] = 3;
xpack::JsonWriter jw;
xpack::XEncoder<xpack::JsonWriter> je(jw);
xpack::XmlWriter xw;
xpack::XEncoder<xpack::XmlWriter> xe(xw);
je.ob(NULL).add("vv", vi).add("i",10).add("s","hello").oe();
xe.ob("root").add("vv", vi).add("i",10).add("s","hello").oe();
cout<<je.String()<<endl;
cout<<xe.String()<<endl;
return 0;
}

58
util/xpack/example/enum.cpp Executable file
View file

@ -0,0 +1,58 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#include "xpack/json.h"
using namespace std;
enum Enum {
X = 0,
Y = 1,
Z = 2,
};
enum class Enum1 {
X = 0,
Y = 1,
Z = 2,
};
enum class Enum2:int64_t {
X = 0,
Y = 1,
Z = 2,
};
struct Test {
string name;
Enum e;
Enum1 e1;
Enum2 e2;
XPACK(O(name), E(F(0), e), O(e1, e2));
};
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
Test t;
string json="{\"name\":\"IPv4\", \"e\":1, \"e1\":2, \"e2\":2}";
xpack::json::decode(json, t);
cout<<xpack::json::encode(t)<<endl;
return 0;
}

69
util/xpack/example/inherit.cpp Executable file
View file

@ -0,0 +1,69 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct P1 {
string mail;
XPACK(O(mail));
};
struct P2 {
long version;
XPACK(O(version));
};
struct Test:public P1, public P2 {
long uid;
string name;
XPACK(I(P1, P2), A(uid, "id"), O(name));
};
struct Pa {
string mail;
XPACK(O(mail));
};
struct Pb: public Pa {
long version;
XPACK(I(Pa), O(version));
};
struct Pc:public Pb {
long uid;
string name;
XPACK(I(Pb), A(uid, "id"), O(name));
};
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
Test t;
string json="{\"mail\":\"pony@xpack.com\", \"version\":2019, \"id\":123, \"name\":\"Pony\"}";
xpack::json::decode(json, t);
cout<<xpack::json::encode(t)<<endl;
Pc p;
xpack::json::decode(json, p);
cout<<xpack::json::encode(p)<<endl;
return 0;
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Example {
string type;
xpack::JsonData data;
XPACK(O(type, data));
};
struct Range {
int min;
int max;
XPACK(O(min, max));
};
struct User {
int id;
string name;
XPACK(O(id, name));
};
static void test(const std::string &data) {
Example e;
xpack::json::decode(data, e);
if (e.type == "range") {
Range r;
e.data.Get(r);
cout<<"min in r:"<<r.min<<endl;
cout<<"max in r:"<<r.max<<endl;
cout<<"min:"<<e.data["min"].Get<int>()<<endl;
cout<<"max:"<<e.data["max"].Get<int>()<<endl;
} else if (e.type == "user") {
cout<<"id:"<<e.data["id"].Get<int>()<<endl;
cout<<"name:"<<e.data["name"].Get<std::string>()<<endl;
} else {
cout<<"unknow type"<<endl;
}
cout<<"re encode:"<<xpack::json::encode(e)<<endl;
cout<<"data encode:"<<xpack::json::encode(e.data)<<endl;
cout<<"data dir encode:"<<e.data.String()<<endl;
}
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
string s1 = "{\"type\":\"range\", \"data\":{\"min\":12, \"max\":22}}";
string s2 = "{\"type\":\"user\", \"data\":{\"id\":123, \"name\":\"xpack\"}}";
test(s1);
test(s2);
return 0;
}

36
util/xpack/example/makefile Executable file
View file

@ -0,0 +1,36 @@
ifeq ($(GPP),)
GPP=g++
endif
SRC=$(wildcard *.cpp)
TAR=$(basename $(SRC))
ifeq ($(c11),off)
NEEDC11=sharedptr
TAR:=$(filter-out $(NEEDC11),$(TAR))
else
CXXFLAG=-std=c++11
endif
%:%.cpp
$(GPP) -o $@ -g $< -Wall -Wextra -I ../.. ${CXXFLAG}
@echo ============ run $@ ================
@./$@
@-rm $@
@-rm -rf $@.dSYM
tar:$(TAR)
@echo -------test done-------
mysql:mysql.cpp
$(GPP) -o $@ -g $< -lmysqlclient -I ../..
sqlite:sqlite.cpp
$(GPP) -o $@ -g $< -lsqlite -I ../..
yaml:yaml.cpp
$(GPP) -o $@ -g $< -Wall -Wextra -I ../.. -I ../../yaml-cpp/include ../../yaml-cpp/build/libyaml-cpp.a -std=c++11
@echo ============ run $@ ================
@./$@
@-rm $@
@-rm -rf $@.dSYM

19
util/xpack/example/msvc.make Executable file
View file

@ -0,0 +1,19 @@
MAKEFLAGS += --no-builtin-rules
MAKEFLAGS += --no-builtin-variables
SRC=$(wildcard *.cpp)
TAR=$(SRC:.cpp=)
NEEDLIB=mysql sqlite yaml
TAR:=$(filter-out $(NEEDLIB),$(TAR))
%:%.cpp
cl /Fe:${CURDIR}/$@.exe /nologo /wd4819 $< /EHsc -I ../..
@echo ============ run $@.exe ================
@$@.exe
@del $@.exe $@.obj
tar:$(TAR)
@echo -------test done-------
clean:
@-del *.obj *.exe

View file

@ -0,0 +1,70 @@
#include <iostream>
#include <string>
#include "xpack/json.h"
using namespace std;
// assign value by name
class AssignDecoder {
public:
AssignDecoder(const std::vector<std::string>&vs, const unsigned char *src, unsigned char *dst) {
for (size_t i=0; i<vs.size(); ++i) {
fields.insert(vs[i]);
}
this->src = src;
this->dst = dst;
}
// decoder need implemetion decode function
template<class T>
bool decode(const char *key, T&val, const xpack::Extend *ext) {
(void)ext;
// set value by offset
if (fields.end() != fields.find(key)) {
unsigned int offset = (unsigned int)((unsigned char*)&val - dst);
val = *((T*)(src+offset));
}
return true;
}
private:
std::set<std::string> fields;
const unsigned char *src;
unsigned char *dst;
};
// SetFields<T>(src, dst, "a,b") means dst.a = src.a; dst.b = src.b;
template <class T>
void SetFields(const T&src, T&dst, const std::string&fields) {
std::vector<std::string> vs;
xpack::Util::split(vs, fields, ',');
if (vs.size() == 0) {
return;
}
AssignDecoder de(vs, (unsigned char*)(&src), (unsigned char*)(&dst));
dst.__x_pack_decode(de, dst, NULL);
}
struct Data {
int a;
int b;
std::string c;
XPACK(O(a,b,c));
};
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
Data d1;
Data d2;
d1.a = 5;
d1.b = 10;
d1.c = "hello";
d2.b = 9;
SetFields<Data>(d1, d2, "a,c");
cout<<d2.a<<','<<d2.b<<','<<d2.c<<endl;
return 0;
}

89
util/xpack/example/mysql.cpp Executable file
View file

@ -0,0 +1,89 @@
#include <iostream>
#include <mysql/mysql.h>
#include <xpack/mysql.h>
#include <xpack/json.h>
using namespace std;
struct VID {
int64_t Vid;
int64_t vlen;
string sid;
int64_t level;
XPACK(A(Vid, "db:vid"), O(vlen, sid, level));
};
typedef char cfgstr[64];
struct config {
cfgstr host;
uint32_t port;
cfgstr user;
cfgstr password;
cfgstr dbname;
XPACK(M(host, port, user, password, dbname))
};
int main(int argc, char **argv) {
MYSQL *mysql = NULL;
if (mysql_library_init(argc, argv, NULL)) {
cout<<"could not initialize MySQL client library"<<endl;
return -1;
}
mysql = mysql_init(mysql);
if (!mysql) {
cout<<"Init faild, out of memory?"<<endl;
return -1;
}
config cfg;
xpack::json::decode_file("./config.json", cfg);
if (!mysql_real_connect(mysql, /* MYSQL structure to use */
cfg.host, /* server hostname or IP address */
cfg.user, /* mysql user */
cfg.password, /* password */
cfg.dbname, /* default database to use, NULL for none */
cfg.port, /* port number, 0 for default */
NULL, /* socket file or named pipe name */
CLIENT_FOUND_ROWS /* connection flags */ )) {
cout<<"Connect failed"<<endl;
} else {
if (mysql_query(mysql, argv[1])) {
cout<<"Query failed: "<<mysql_error(mysql)<<endl;
} else {
MYSQL_RES *result = mysql_store_result(mysql);
if (!result) {
cout<<"Couldn't get results set: "<<mysql_error(mysql)<<endl;
} else {
VID vid;
xpack::mysql::decode(result, vid); // decode first row
vector<VID> vids;
xpack::mysql::decode(result, vids);// decode all rows
vector<int64_t> ids;
xpack::mysql::decode(result, "vid", ids); // decode vid column of all rows
int64_t id;
xpack::mysql::decode(result, "vid", id); // decode vid column of first row
cout<<xpack::json::encode(vid)<<endl;
cout<<xpack::json::encode(vids)<<endl;
cout<<xpack::json::encode(ids)<<endl;
cout<<id<<endl;
mysql_free_result(result);
}
}
}
mysql_close(mysql);
mysql_library_end();
return EXIT_SUCCESS;
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#include "xpack/json.h"
#include "xpack/xml.h"
using namespace std;
struct User {
int64_t id;
string name;
string mail;
std::shared_ptr<string> bio;
User(int64_t i=0, const string& n="", const string& m=""):id(i),name(n),mail(m){}
XPACK(O(id, name, mail, bio));
};
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
User u1(1, "Jack", "jack@xpack.com");
User u2;
User u3;
string json = xpack::json::encode(u1);
string xml = xpack::xml::encode(u1, "root");
cout<<"========================"<<endl;
cout<<json<<endl;
cout<<xml<<endl;
cout<<"========================"<<endl;
xpack::json::decode(json, u2);
cout<<xpack::json::encode(u2)<<endl;
xpack::xml::decode(xml, u3);
cout<<xpack::xml::encode(u3, "root")<<endl;
cout<<"========================"<<endl;
u1.bio.reset(new std::string("farmer"));
json = xpack::json::encode(u1);
xml = xpack::xml::encode(u1, "root");
cout<<json<<endl;
cout<<xml<<endl;
return 0;
}

61
util/xpack/example/sqlite.cpp Executable file
View file

@ -0,0 +1,61 @@
#include <iostream>
#include <xpack/sqlite.h>
#include <xpack/json.h>
using namespace std;
struct VID {
int64_t Vid;
int64_t vlen;
string sid;
int64_t level;
XPACK(A(Vid, "db:vid"), O(vlen, sid, level));
};
int main(int argc, char **argv) {
(void)argc;
char *errMsg = NULL;
sqlite *pDB = sqlite_open("./test.db", 0, &errMsg);
if (NULL == pDB) {
cout<<"open sqlite db fail:"<<errMsg<<endl;
sqlite_freemem(errMsg);
return -1;
}
int nCols = 0;
int nRows = 0;
char **azResult = NULL;
int ret = sqlite_get_table(pDB, argv[1], &azResult, &nRows, &nCols, &errMsg);
if (ret != SQLITE_OK) {
cout<<"query sqlite db fail:"<<ret<<endl;
return -1;
}
cout<<nRows<<','<<nCols<<endl;
for (int i=0; i<nCols; ++i) {
cout<<azResult[i]<<' ';
}
cout<<endl;
int idx = nCols;
for (int i=0; i<nRows; ++i) {
for (int j=0; j<nCols; ++j, ++idx) {
if (NULL == azResult[idx]) {
cout<<"NULL"<<' ';
} else {
cout<<azResult[idx]<<' ';
}
}
cout<<endl;
}
if (NULL != azResult) {
sqlite_free_table(azResult);
}
if (NULL != errMsg) {
sqlite_freemem(errMsg);
}
sqlite_close(pDB);
return 0;
}

118
util/xpack/example/union1.cpp Executable file
View file

@ -0,0 +1,118 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Base {
int a;
int b;
XPACK(O(a, b));
};
struct Data {
int type;
union {
Base b;
int i;
char s[10];
};
string email;
string birthday;
// 'type' determines which member of the union to use, so we put type in 'C'
// union does not need to be put into XPACK, we will handled it in 'type'
XPACK(C(data_uinon, F(0), type), O(email, birthday));
};
struct User {
string name;
Data data;
XPACK(O(name, data));
};
namespace xpack { // must define in namespace xpack
template<class OBJ>
bool data_uinon_decode(OBJ &obj, Data &d, const char*key, int &type, const Extend *ext) {
(void)ext;
obj.decode(key, type, ext);
switch (type) {
case 1:
obj.decode("b", d.b, NULL); // key("b") should same as encode
break;
case 2:
obj.decode("i", d.i, NULL);
break;
case 3:
obj.decode("s", d.s, NULL);
break;
}
return true;
}
template<class OBJ>
bool data_uinon_encode(OBJ &obj, const Data &d, const char*key, const int &type, const Extend *ext) {
(void)ext;
obj.encode(key, type, NULL);
switch (type) {
case 1:
obj.encode("b", d.b, NULL);
break;
case 2:
obj.encode("i", d.i, NULL);
break;
case 3:
obj.encode("s", d.s, NULL);
break;
}
return true;
}
}
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
User u;
u.name = "Pony";
u.data.type = 1;
u.data.b.a = 10;
u.data.b.b = 12;
u.data.email = "Pony@xpack.com";
u.data.birthday = "20200202";
string s = xpack::json::encode(u);
cout<<s<<endl;
u.data.type = 2;
u.data.i = 22;
s = xpack::json::encode(u);
cout<<s<<endl;
u.data.type = 3;
strcpy(u.data.s, "hello");
s = xpack::json::encode(u);
cout<<s<<endl;
User u1;
xpack::json::decode(s, u1);
cout<<u1.data.s<<endl;
return 0;
}

View file

@ -0,0 +1,37 @@
#include <iostream>
#include <xpack/xml.h>
using namespace std;
struct Base {
int a;
int b;
string c;
XPACK(X(F(ATTR), a, b), X(F(XML_CONTENT), c));
};
struct Root {
Base root;
XPACK(O(root));
};
int main() {
Root r;
r.root.a = 1;
r.root.b = 2;
r.root.c = "hello";
string s = xpack::xml::encode(r, "root", 0, 4, ' ');
cout<<s<<endl;
string s1 = xpack::xml::encode(r, "root");
cout<<s1<<endl;
Root r1,r2;
xpack::xml::decode(s, r1);
xpack::xml::decode(s1, r2);
cout<<xpack::xml::encode(r1, "r1")<<endl;
cout<<xpack::xml::encode(r2, "r2")<<endl;
return 0;
}

67
util/xpack/example/xml.cpp Executable file
View file

@ -0,0 +1,67 @@
#include <iostream>
#include <xpack/xml.h>
using namespace std;
struct Base {
int a;
int b;
XPACK(O(a, b));//, X(F(ATTR), b));
};
struct Root {
Base root;
XPACK(O(root));
};
struct Top {
int k;
string n;
vector<vector<Base> > bs;
vector<int> vi;
float f;
std::string name; // cdata
XPACK(O(k, n, f), A(bs, "xml:base,sbs", vi, "xml:vi,vl@x", name, "xml:name,cdata"));
};
int main() {
Top t;
t.k = 666;
t.n = "你好";
t.f = 1.23333;
t.vi.resize(2);
t.vi[0] = 1;
t.vi[1] = 2;
t.bs.resize(2);
t.bs[0].resize(2);
t.bs[1].resize(2);
t.bs[0][0].a = 100;
t.bs[0][0].b = 200;
t.bs[0][1].a = 101;
t.bs[0][1].b = 201;
t.bs[1][0].a = 110;
t.bs[1][0].b = 210;
t.bs[1][1].a = 111;
t.bs[1][1].b = 211;
t.name = "好好学习,天天向上<good good study, day day up>";
string s = xpack::xml::encode(t, "root", 0, 4, ' ');
cout<<s<<endl;
xpack::XmlEncoder en;
en.SetMaxDecimalPlaces(3);
cout<<"=========="<<endl<<en.encode(t, "root")<<endl;
Top t1;
xpack::xml::decode(s, t1);
cout<<xpack::xml::encode(t1, "root", 0, 4, ' ')<<endl;
Base b1;
bool ret = xpack::XmlDecoder().decode("<root><a>1</a><b>2</b></root>", b1);
cout<<ret<<','<<b1.a<<','<<b1.b<<endl;
Root r1;
ret = xpack::XmlDecoder().decode("<root><a>1</a><b>2</b></root>", r1, true);
cout<<ret<<','<<r1.root.a<<','<<r1.root.b<<endl;
return 0;
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// #include <sys/time.h> linux only
#include <iostream>
#include "xpack/json.h"
#ifndef _MSC_VER
#include <sys/time.h>
#endif
using namespace std;
#ifdef _MSC_VER
struct timeval {
long tv_sec;
long tv_usec;
};
#endif
// timeval is thirdparty struct
XPACK_OUT(timeval, O(tv_sec, tv_usec));
struct T {
int a;
string b;
timeval t;
XPACK(O(a, b, t));
};
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
T t;
T r;
t.a = 123;
t.b = "xpack";
t.t.tv_sec = 888;
t.t.tv_usec = 999;
string s = xpack::json::encode(t);
cout<<s<<endl;
xpack::json::decode(s, r);
cout<<r.a<<','<<r.b<<','<<r.t.tv_sec<<','<<r.t.tv_usec<<endl;
return 0;
}

121
util/xpack/example/xtype.cpp Executable file
View file

@ -0,0 +1,121 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#include "xpack/json.h"
#include "xpack/xml.h"
using namespace std;
struct Date {
long unix_time;
};
namespace xpack { // must define in namespace xpack
template<>
struct is_xpack_xtype<Date> {static bool const value = true;};
// implement decode
template<class OBJ>
bool xpack_xtype_decode(OBJ &obj, Date &val, const Extend *ext) {
std::string str;
obj.decode(str, ext);
if (str.empty()) {
return false;
}
#ifndef _MSC_VER
tm ttm;
if (0 != strptime(str.c_str(), "%Y-%m-%d %H:%M:%S", &ttm)) {
val.unix_time = mktime(&ttm);
} else {
val.unix_time = 0;
}
#else
static int days[]={31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 32};
struct tm ttm={0};
sscanf_s(str.c_str(), "%d-%d-%d %d:%d:%d", &ttm.tm_year, &ttm.tm_mon, &ttm.tm_mday, &ttm.tm_hour, &ttm.tm_min, &ttm.tm_sec);
ttm.tm_mon-=1; // mon[0-11]
ttm.tm_year-=1900; // since 1900
val.unix_time = mktime(&ttm);
#endif
return true;
}
// implement encode
template<class OBJ>
bool xpack_xtype_encode(OBJ &obj, const char*key, const Date &val, const Extend *ext) {
time_t tt = (time_t)val.unix_time;
tm ttm;
#ifndef _MSC_VER
localtime_r(&tt, &ttm);
#else
localtime_s(&ttm, &tt);
#endif
char buf[64];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &ttm);
return obj.encode(key, buf, ext);
}
}
struct User {
int64_t id;
string name;
string mail;
Date d;
User(int64_t i=0, const string& n="", const string& m="", long _d=0):id(i),name(n),mail(m){d.unix_time = _d;}
XPACK(O(id, name, mail, d));
};
struct Group {
string name;
int64_t master;
vector<User> members;
XPACK(O(name, master, members));
};
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
Group g;
g.name = "C++";
g.master = 2019;
g.members.resize(2);
g.members[0] = User(1, "Jack", "jack@xpack.com", 1);
g.members[1] = User(2, "Pony", "pony@xpack.com", 1609249232);
string json = xpack::json::encode(g, 0, 2, ' ');
cout<<json<<endl;
string xml = xpack::xml::encode(g, "root", 0, 2, ' ');
cout<<xml<<endl;
Group n1;
xpack::json::decode(json, n1);
cout<<n1.name<<';'<<n1.members[0].name<<';'<<n1.members[0].d.unix_time<<endl;
Group n2;
xpack::xml::decode(xml, n2);
cout<<n2.name<<';'<<n2.members[1].name<<';'<<n2.members[1].d.unix_time<<endl;
return 0;
}

View file

@ -0,0 +1,108 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <iostream>
#include "xpack/json.h"
using namespace std;
// The requirement is to encode seq1 when type==1, and encode seq2 when type==2
struct Sub {
int type;
string seq1;
string seq2;
XPACK(O(type, seq1, seq2));
};
struct Test {
unsigned int uid;
string name;
Sub sub;
XPACK(O(uid, name, sub));
};
namespace xpack {
template<>
struct is_xpack_xtype<Sub> {static bool const value = true;};
template <class Decoder>
bool xpack_xtype_decode(Decoder& de, Sub &data, const xpack::Extend *ext) {
de.decode_struct(data, ext);
return true;
}
template <class BaseEncoder>
class MyEncoder {
public:
MyEncoder(BaseEncoder&b, const Sub&sub):_b(&b), _s(&sub){}
template <class T>
bool encode(const char*key, const T&val, const xpack::Extend *ext) {
if (strcmp(key, "seq1") == 0) {
if (_s->type == 1) {
return _b->encode(key, val, ext);
} else {
return false;
}
} else if (strcmp(key, "seq2") == 0) {
if (_s->type == 2) {
return _b->encode(key, val, ext);
} else {
return false;
}
} else {
return _b->encode(key, val, ext);
}
}
private:
BaseEncoder *_b;
const Sub *_s;
};
template <class Encoder>
bool xpack_xtype_encode(Encoder &en, const char *name, const Sub &data, const xpack::Extend *ext) {
(void)name;
MyEncoder<Encoder> my(en, data);
data.__x_pack_encode(my, data, ext);
return true;
}
}
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
Test t;
t.uid = 127;
t.name = "PonyJack";
t.sub.type = 1;
t.sub.seq1 = "10086";
t.sub.seq2 = "10001";
string str = xpack::json::encode(t);
cout<<str<<endl;
Test n;
xpack::json::decode(str, t);
cout<<t.uid<<endl;
t.sub.type = 2;
cout<<xpack::json::encode(t)<<endl;
return 0;
}

65
util/xpack/example/yaml.cpp Executable file
View file

@ -0,0 +1,65 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#include "xpack/yaml.h"
#include "xpack/json.h"
using namespace std;
struct User {
int64_t id;
string name;
string mail;
User(int64_t i=0, const string& n="", const string& m=""):id(i),name(n),mail(m){}
XPACK(O(id, name, mail));
};
struct Group {
string name;
int64_t master;
map<string, string> flags;
vector<User> members;
User arrays[2];
XPACK(O(name, master, flags, members, arrays));
};
int main() {
User u;
xpack::yaml::decode_file("./test.yml", u);
string json = xpack::json::encode(u);
cout<<"json:"<<json<<endl;
cout<<"yaml is =================="<<endl<<xpack::yaml::encode(u)<<endl<<"=================="<<endl;
Group g;
g.name = "C++";
g.master = 2019;
g.members.resize(2);
g.members[0] = User(1, "Jack", "jack@xpack.com");
g.members[1] = User(2, "Pony", "pony@xpack.com");
g.arrays[0] = g.members[0];
g.arrays[1] = g.members[1];
g.flags["a"] = "good";
g.flags["b"] = "nice";
string ys = xpack::yaml::encode(g);
cout<<"yaml is =================="<<endl<<ys<<endl<<"=================="<<endl;
Group g1;
xpack::yaml::decode(ys, g1);
cout<<xpack::json::encode(g1)<<endl;
return 0;
}

188
util/xpack/extend.h Executable file
View file

@ -0,0 +1,188 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_EXTEND_H
#define __X_PACK_EXTEND_H
#include <cstddef>
#include <map>
#include <set>
#include "config.h"
#include "util.h"
namespace xpack {
// user flag
#define X_PACK_FLAG_0 0
#define X_PACK_FLAG_OE (1<<0) // omitempty, in encode
#define X_PACK_FLAG_M (1<<1) // mandatory, in decode
#define X_PACK_FLAG_EN (1<<2) // empty as null, in json encode
#define X_PACK_FLAG_SL (1<<3) // encode as one line, currently only supports json vector
#define X_PACK_FLAG_ATTR (1<<15) // for xml encode, encode in attribute
// for xml encode/decode: <label attr1="a" attr2="b">content</label>
// CONTENT flag for decode and encode content
// A structure can only have one CONTENT flag. If multiple ones are defined, the behavior is undefined.
#define X_PACK_FLAG_XML_CONTENT (1<<16)
// control flag
#define X_PACK_CTRL_FLAG_INHERIT (1<<0)
// Alias name. [def ][type:name[,flag,key@value,flag]] def not support flag
struct Alias {
const char *raw; // raw name
const char *alias; // alias define
struct Type {
std::string name;
std::set<std::string> flags;
std::map<std::string, std::string> kv_flags;
};
Alias(const char *_raw, const char *_alias):raw(_raw), alias(_alias) {
std::vector<std::string> tps;
Util::split(tps, alias, ' ');
for (size_t i=0; i<tps.size(); ++i) {
std::vector<std::string> typeFlag;
Util::split(typeFlag, tps[i], ':', 1);
if (typeFlag.size() == 1) { // no ':', default name
def = tps[i];
} else { // type:name[,flags]
Type tp;
std::vector<std::string> nameFlags;
Util::split(nameFlags, typeFlag[1], ',');
tp.name = nameFlags[0];
if (nameFlags.size() > 1) {
for (size_t j=1; j<nameFlags.size(); ++j) {
std::vector<std::string> kvFlag;
Util::split(kvFlag, nameFlags[j], '@', 1);
if (kvFlag.size() == 1) {
tp.flags.insert(nameFlags[j]);
} else {
tp.kv_flags[kvFlag[0]] = kvFlag[1];
}
}
}
types[typeFlag[0]] = tp;
}
}
}
const char *Name(const char *type) const {
std::map<std::string, Type>::const_iterator iter = types.find(type);
if (iter == types.end()) {
if (def.empty()) {
return raw;
} else {
return def.c_str();
}
} else {
return iter->second.name.c_str();
}
}
bool Flag(const std::string&type, const std::string& flag, std::string *value=NULL) const {
std::map<std::string, Type>::const_iterator it1 = types.find(type);
if (it1 != types.end()) {
if (it1->second.flags.find(flag) != it1->second.flags.end()) {
return true;
}
std::map<std::string, std::string>::const_iterator it2 = it1->second.kv_flags.find(flag);
if (it2 != it1->second.kv_flags.end()) {
if (NULL != value) {
*value = it2->second;
}
return true;
}
}
return false;
}
private:
std::string def; // default name
std::map<std::string, Type> types;
};
struct Extend {
int flag;
int ctrl_flag;
const Alias *alias;
Extend(int _flag, const Alias *_alias):flag(_flag), ctrl_flag(0), alias(_alias) {
}
Extend(const Extend *ext) {
if (NULL != ext) {
flag = ext->flag;
ctrl_flag = ext->ctrl_flag;
alias = ext->alias;
} else {
flag = 0;
ctrl_flag = 0;
alias = NULL;
}
}
static int Flag(const Extend *ext) {
if (NULL == ext) {
return 0;
} else {
return ext->flag;
}
}
static int CtrlFlag(const Extend *ext) {
if (NULL == ext) {
return 0;
} else {
return ext->ctrl_flag;
}
}
static bool AliasFlag(const Extend *ext, const std::string&type, const std::string& flag, std::string *value=NULL) {
if (NULL==ext || NULL==ext->alias) {
return false;
}
return ext->alias->Flag(type, flag, value);
}
static bool OmitEmpty(const Extend *ext) {
return NULL!=ext && (ext->flag&X_PACK_FLAG_OE);
}
static bool EmptyNull(const Extend *ext) {
return NULL!=ext && (ext->flag&X_PACK_FLAG_EN);
}
static bool Mandatory(const Extend *ext) {
return NULL!=ext && (ext->flag&X_PACK_FLAG_M);
}
static bool Attribute(const Extend *ext) {
return NULL!=ext && (ext->flag&X_PACK_FLAG_ATTR);
}
static bool XmlContent(const Extend *ext) {
return NULL!=ext && (ext->flag&X_PACK_FLAG_XML_CONTENT);
}
};
}
#endif

557
util/xpack/gtest/bson_test.cpp Executable file
View file

@ -0,0 +1,557 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Linux only, need gtest
#include <iostream>
#ifdef XGTEST
#include<gtest/gtest.h>
#else
#include "gtest_stub.h"
#endif
#include "xpack/json.h"
#include "xpack/bson.h"
#include "string.h"
using namespace std;
struct Base {
int a;
string b;
Base(const int _a=0, const string&_b=""):a(_a), b(_b){}
XPACK(O(a, b));
};
struct POD {
int a;
char b[10];
XPACK(O(a, b));
};
// ++++++++++++++++++++++BuiltInTypes++++++++++++++++++++++++++++
struct BuiltInTypes {
signed char sch;
char ch;
unsigned char uch;
short sh;
unsigned short ush;
int i;
unsigned int ui;
long l;
unsigned long ul;
long long ll;
unsigned long long ull;
float f;
double d;
long double ld;
bool b;
XPACK(AF(F(ATTR), sch, "xml:s:ch"), X(F(ATTR), ch, uch, sh, ush), O(i, ui, l, ul, ll, ull, f, d, ld, b));
};
void checkBuiltInTypes(const BuiltInTypes&a, const BuiltInTypes&b) {
EXPECT_EQ(a.sch, b.sch);
EXPECT_EQ(a.ch, b.ch);
EXPECT_EQ(a.uch, b.uch);
EXPECT_EQ(a.sh, b.sh);
EXPECT_EQ(a.ush, b.ush);
EXPECT_EQ(a.i, b.i);
EXPECT_EQ(a.ui, b.ui);
EXPECT_EQ(a.l, b.l);
EXPECT_EQ(a.ul, b.ul);
EXPECT_EQ(a.ll, b.ll);
EXPECT_EQ(a.ull, b.ull);
EXPECT_FLOAT_EQ(a.f, b.f);
EXPECT_DOUBLE_EQ(a.d, b.d);
EXPECT_DOUBLE_EQ(a.ld, b.ld);
EXPECT_TRUE(a.b);
}
TEST(builtin, types) {
BuiltInTypes bt;
bt.sch = 0x7f;
bt.ch = 0x7f;
bt.uch = 0x80;
bt.sh = 0x7fff;
bt.ush = 0x8000;
bt.i = 0x7fffffff;
bt.ui = 0x80000000;
bt.l = 0x7fffffffffffffff;
bt.ul = 0x8000000000000000;
bt.ll = bt.l;
bt.ull = bt.ul;
bt.f = 1.234;
bt.d = 9.678;
bt.ld = 30.678;
bt.b = true;
xpack::BsonEncoder en;
string js = en.encode_as_json(bt);
cout<<"bson:"<<js<<endl;
BuiltInTypes j1;
xpack::bson::decode(en.encode(bt), j1);
checkBuiltInTypes(j1, bt);
}
// ++++++++++++++++++++bit field+++++++++++++++++++++++++++
struct BitField {
short a:8;
short b:8;
XPACK(B(F(0), a, b));
};
void checkBitField(const BitField&a, const BitField&b) {
EXPECT_EQ(a.a, b.a);
EXPECT_EQ(a.b, b.b);
}
TEST(bitfiled, base) {
BitField bf;
bf.a = 0x70;
bf.b = 0x7f;
xpack::BsonEncoder en;
string js = en.encode_as_json(bf);
BitField j1;
xpack::bson::decode(en.encode(bf), j1);
checkBitField(j1, bf);
}
// +++++++++++++++++++ enum +++++++++++++++++++++
enum Enum1 {
E0 = 0,
E1 = 1,
};
enum class Enum2:int64_t {
E2 = 2,
E3 = 3,
};
struct EnumBase {
Enum1 e1;
Enum2 e2;
XPACK(O(e1), E(F(0), e2));
};
void checkEnumBase(const EnumBase&a, const EnumBase&b) {
EXPECT_EQ(a.e1, b.e1);
EXPECT_EQ(a.e2, b.e2);
}
TEST(enum, base) {
EnumBase eb;
eb.e1 = E0;
eb.e2 = Enum2::E3;
xpack::BsonEncoder en;
string js = en.encode_as_json(eb);
EnumBase j1;
xpack::bson::decode(en.encode(eb), j1);
checkEnumBase(j1, eb);
}
// ++++++++++++++++inherit+++++++++++++++++++++++++++++
struct InheritBase {
int b1;
string b2;
XPACK(O(b1, b2));
};
struct InheritChild:public InheritBase {
int c1;
string c2;
XPACK(I(InheritBase), O(c1, c2));
};
void checkInherit(const InheritChild&a, const InheritChild&b) {
EXPECT_EQ(a.b1, b.b1);
EXPECT_EQ(a.b2, b.b2);
EXPECT_EQ(a.c1, b.c1);
EXPECT_EQ(a.c2, b.c2);
}
TEST(inherit, base) {
InheritChild c;
c.b1 = 1;
c.b2 = "base";
c.c1 = 2;
c.c2 = "child";
xpack::BsonEncoder en;
string js = en.encode_as_json(c);
InheritChild j1;
xpack::bson::decode(en.encode(c), j1);
checkInherit(j1, c);
}
// ++++++++++++++++++++++++array++++++++++++++++++++++++++
struct Array {
int a[3];
char b[3];
Base c[3];
XPACK(O(a, b, c));
};
void checkArray(const Array&a, const Array&b) {
EXPECT_EQ(a.a[0], b.a[0]);
EXPECT_EQ(a.a[1], b.a[1]);
EXPECT_EQ(a.a[2], b.a[2]);
EXPECT_EQ(a.b[0], b.b[0]);
EXPECT_EQ(a.b[1], b.b[1]);
EXPECT_EQ(a.b[2], b.b[2]);
EXPECT_EQ(a.c[0].a, b.c[0].a);
EXPECT_EQ(a.c[0].b, b.c[0].b);
EXPECT_EQ(a.c[1].a, b.c[1].a);
EXPECT_EQ(a.c[1].b, b.c[1].b);
EXPECT_EQ(a.c[2].a, b.c[2].a);
EXPECT_EQ(a.c[2].b, b.c[2].b);
}
TEST(array, base) {
Array a;
a.a[0] = 1;
a.a[1] = 2;
a.a[2] = 3;
a.b[0] = 'g';
a.b[1] = 'o';
a.b[2] = '\0';
a.c[0].a = 11;
a.c[1].a = 12;
a.c[2].a = 13;
a.c[0].b = "hello";
a.c[1].b = "good";
a.c[1].b = "nice";
xpack::BsonEncoder en;
string js = en.encode_as_json(a);
Array j1;
xpack::bson::decode(en.encode(a), j1);
checkArray(j1, a);
}
// +++++++++++++++ container ++++++++++++++++++++++
struct ContainerBase {
map<string, int> m;
set<int> s;
vector<int> v;
list<int> l;
vector<vector<int> > vv;
XPACK(O(m, s, v, l, vv));
};
void checkContainerBase(ContainerBase&cb) {
EXPECT_EQ(cb.m.size(), 2U);
EXPECT_EQ(cb.m["a"], 1);
EXPECT_EQ(cb.m["b"], 2);
EXPECT_EQ(cb.s.size(), 2U);
EXPECT_TRUE(cb.s.find(3)!=cb.s.end());
EXPECT_TRUE(cb.s.find(4)!=cb.s.end());
EXPECT_TRUE(cb.s.find(5)==cb.s.end());
EXPECT_EQ(cb.v.size(), 2U);
EXPECT_EQ(cb.v[0], 5);
EXPECT_EQ(cb.v[1], 6);
EXPECT_EQ(cb.l.size(), 2U);
EXPECT_EQ(*(cb.l.begin()), 7);
EXPECT_EQ(*(++cb.l.begin()), 8);
EXPECT_EQ(cb.vv.size(), 2U);
EXPECT_EQ(cb.vv[0].size(), 2U);
EXPECT_EQ(cb.vv[1].size(), 2U);
EXPECT_EQ(cb.vv[0][0], 11);
EXPECT_EQ(cb.vv[0][1], 12);
EXPECT_EQ(cb.vv[1][0], 22);
EXPECT_EQ(cb.vv[1][1], 23);
}
TEST(container, base) {
string s = "{\"m\":{\"a\":1, \"b\":2}, \"s\":[3, 4], \"v\":[5,6], \"l\":[7, 8], \"vv\":[[11,12],[22,23]]}";
ContainerBase cb;
xpack::json::decode(s, cb);
checkContainerBase(cb);
xpack::BsonEncoder en;
string js = en.encode_as_json(cb);
ContainerBase j1;
xpack::bson::decode(en.encode(cb), j1);
checkContainerBase(j1);
}
struct ContainerStruct {
map<string, Base> m;
vector<Base> v;
list<Base> l;
vector<vector<Base> > vv;
XPACK(O(m, v, l, vv));
};
void checkContainerStruct(ContainerStruct&a) {
EXPECT_EQ(a.m.size(), 2U);
EXPECT_EQ(a.m["a"].a, 1);
EXPECT_EQ(a.m["a"].b, "good");
EXPECT_EQ(a.m["b"].a, 2);
EXPECT_EQ(a.m["b"].b, "nice");
EXPECT_EQ(a.v.size(), 2U);
EXPECT_EQ(a.v[0].a, 3);
EXPECT_EQ(a.v[0].b, "hello");
EXPECT_EQ(a.v[1].a, 4);
EXPECT_EQ(a.v[1].b, "wow");
list<Base>::const_iterator lit = a.l.begin();
EXPECT_EQ(a.l.size(), 2U);
EXPECT_EQ(lit->a, 5);
EXPECT_EQ(lit->b, "dida");
++lit;
EXPECT_EQ(lit->a, 6);
EXPECT_EQ(lit->b, "haha");
EXPECT_EQ(a.vv.size(), 2U);
EXPECT_EQ(a.vv[0].size(), 1U);
EXPECT_EQ(a.vv[1].size(), 2U);
EXPECT_EQ(a.vv[0][0].a, 7);
EXPECT_EQ(a.vv[0][0].b, "lala");
EXPECT_EQ(a.vv[1][0].a, 8);
EXPECT_EQ(a.vv[1][0].b, "wawa");
EXPECT_EQ(a.vv[1][1].a, 9);
EXPECT_EQ(a.vv[1][1].b, "kaka");
}
TEST(container, struct) {
ContainerStruct cb;
cb.m["a"] = Base(1, "good");
cb.m["b"] = Base(2, "nice");
cb.v.push_back(Base(3, "hello"));
cb.v.push_back(Base(4, "wow"));
cb.l.push_back(Base(5, "dida"));
cb.l.push_back(Base(6, "haha"));
cb.vv.resize(2);
cb.vv[0].resize(1);
cb.vv[1].resize(2);
cb.vv[0][0].a = 7;
cb.vv[0][0].b = "lala";
cb.vv[1][0].a = 8;
cb.vv[1][0].b = "wawa";
cb.vv[1][1].a = 9;
cb.vv[1][1].b = "kaka";
xpack::BsonEncoder en;
string js = en.encode_as_json(cb);
ContainerStruct j1;
xpack::bson::decode(en.encode(cb), j1);
checkContainerStruct(j1);
}
// ++++++++++++++++++ BSON TYPES ++++++++++++++++++++++++++++
struct BsonTypes {
bson_date_time_t dt;
bson_timestamp_t ts;
bson_binary_t binary;
bson_regex_t regex;
bson_oid_t oid;
bson_decimal128_t decimal;
XPACK(A(oid, "_id"), O(dt, ts, binary, regex, decimal));
};
void checkBsonTypes(const BsonTypes&a, const BsonTypes&b, bool checkDecimal) {
EXPECT_EQ(a.dt.ts, b.dt.ts);
EXPECT_EQ(a.ts.timestamp, b.ts.timestamp);
EXPECT_EQ(a.ts.increment, b.ts.increment);
EXPECT_EQ(a.binary.data, b.binary.data);
EXPECT_EQ(a.binary.subType, b.binary.subType);
EXPECT_EQ(a.regex.pattern, b.regex.pattern);
EXPECT_EQ(a.regex.options, b.regex.options);
EXPECT_EQ(0, memcmp(a.oid.bytes, b.oid.bytes, sizeof(a.oid.bytes)));
if (checkDecimal) {
EXPECT_EQ(a.decimal.high, b.decimal.high);
EXPECT_EQ(a.decimal.low, b.decimal.low);
}
}
TEST(base, bson_types) {
BsonTypes bt;
bt.dt.ts = 1638374400018;
bt.ts.timestamp = 1638374400;
bt.ts.increment = 1;
bt.binary.data = "hello";
bt.binary.subType = BSON_SUBTYPE_USER;
bt.regex.pattern = "^([0-9a-zA-Z]+)";
bt.regex.options = "i";
bt.regex.ver = 2;
uint8_t bytes[] = {0x57, 0xe1, 0x93, 0xd7, 0xa9, 0xcc, 0x81, 0xb4, 0x02, 0x74, 0x98, 0xb5};
memcpy((void*)bt.oid.bytes, bytes, sizeof(bt.oid.bytes));
bt.decimal.high = 1;
bt.decimal.low = 2;
string js = xpack::json::encode(bt);
cout<<"json:"<<js<<endl;
BsonTypes bt1;
xpack::json::decode(js, bt1);
checkBsonTypes(bt, bt1, false);
xpack::BsonEncoder en;
string js1 = en.encode_as_json(bt);
cout<<"bjson:"<<js1<<endl;
BsonTypes bt2;
xpack::json::decode(js1, bt2);
checkBsonTypes(bt, bt2, false);
BsonTypes bt3;
xpack::bson::decode(en.encode(bt), bt3);
checkBsonTypes(bt, bt3, true);
}
TEST(bson, builders) {
static xpack::BsonBuilder bd("{?:?, 'users':?}");
EXPECT_TRUE(bd.Error().empty());
vector<int> v(3);
v[0] = 1; v[1] = 2; v[2] = 3;
cout<<"json1:"<<bd.EncodeAsJson("hi", true, v)<<endl;
cout<<"json2:"<<bd.EncodeAsJson("uid", 123.0, "LiLei/HanMeimei/Jim")<<endl;
cout<<"json3:"<<bd.EncodeAsJson("Lang", "C++", "")<<endl;
}
// +++++++++++++++++++ QT ++++++++++++++++++++
#ifdef XPACK_SUPPORT_QT
struct ContainerQT {
QMap<string, Base> m;
QVector<Base> v;
QList<Base> l;
QVector<QVector<Base> > vv;
XPACK(O(m, v, l, vv));
};
void checkContainerQT(ContainerQT&a) {
EXPECT_EQ(a.m.size(), 2);
EXPECT_EQ(a.m["a"].a, 1);
EXPECT_EQ(a.m["a"].b, "good");
EXPECT_EQ(a.m["b"].a, 2);
EXPECT_EQ(a.m["b"].b, "nice");
EXPECT_EQ(a.v.size(), 2);
EXPECT_EQ(a.v[0].a, 3);
EXPECT_EQ(a.v[0].b, "hello");
EXPECT_EQ(a.v[1].a, 4);
EXPECT_EQ(a.v[1].b, "wow");
QList<Base>::const_iterator lit = a.l.begin();
EXPECT_EQ(a.l.size(), 2);
EXPECT_EQ(lit->a, 5);
EXPECT_EQ(lit->b, "dida");
++lit;
EXPECT_EQ(lit->a, 6);
EXPECT_EQ(lit->b, "haha");
EXPECT_EQ(a.vv.size(), 2);
EXPECT_EQ(a.vv[0].size(), 1);
EXPECT_EQ(a.vv[1].size(), 2);
EXPECT_EQ(a.vv[0][0].a, 7);
EXPECT_EQ(a.vv[0][0].b, "lala");
EXPECT_EQ(a.vv[1][0].a, 8);
EXPECT_EQ(a.vv[1][0].b, "wawa");
EXPECT_EQ(a.vv[1][1].a, 9);
EXPECT_EQ(a.vv[1][1].b, "kaka");
}
TEST(container, QT) {
ContainerQT cb;
cb.m["a"] = Base(1, "good");
cb.m["b"] = Base(2, "nice");
cb.v.push_back(Base(3, "hello"));
cb.v.push_back(Base(4, "wow"));
cb.l.push_back(Base(5, "dida"));
cb.l.push_back(Base(6, "haha"));
cb.vv.resize(2);
cb.vv[0].resize(1);
cb.vv[1].resize(2);
cb.vv[0][0].a = 7;
cb.vv[0][0].b = "lala";
cb.vv[1][0].a = 8;
cb.vv[1][0].b = "wawa";
cb.vv[1][1].a = 9;
cb.vv[1][1].b = "kaka";
xpack::BsonEncoder en;
en.encode(NULL, cb, NULL);
string js = en.Json();
ContainerQT j1;
xpack::bson::decode(en.String(), j1);
checkContainerQT(j1);
}
#endif
// +++++++++++++++++ flags ++++++++++++++++++
struct FlagM {
int a;
string b;
XPACK(O(a), M(b));
};
TEST(flags, M) {
static xpack::BsonBuilder bd("{'a':1}");
bool except = false;
try {
FlagM j1;
xpack::bson::decode(bd.Encode(), j1);
} catch(...) {
except = true;
}
EXPECT_TRUE(except);
}
// ++++++++++++++++++bug history+++++++++++++++++++++++
TEST(bughis, notexists) {
Base b(9, "");
static xpack::BsonBuilder bd("{'a':1}");
xpack::bson::decode(bd.Encode(), b);
EXPECT_EQ(b.a, 1);
}
int main(int argc, char *argv[]) {
#ifdef XGTEST
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
#else
(void)argc;
(void)argv;
TC_CONTAINER::RUN();
return 0;
#endif
}

167
util/xpack/gtest/gtest_stub.h Executable file
View file

@ -0,0 +1,167 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_GTEST_STUB_H
#define __X_GTEST_STUB_H
#include <vector>
#include <iostream>
#ifdef XPACK_SUPPORT_QT
#include <QString>
#endif
#include <xpack/traits.h>
#include <xpack/numeric.h>
// test callback function
typedef void(*test_case)();
// a test case
struct text_ctx {
const char *group;
const char *name;
test_case tc;
};
class Status {
public:
static int&c() {
static int _c;
return _c;
}
};
// to collect all test
class TC_CONTAINER {
public:
// add a test case to container
static void add(test_case tc, const char* g, const char*n) {
text_ctx tx;
tx.group = g;
tx.name = n;
tx.tc = tc;
I()._tcs.push_back(tx);
}
// run all test case
static void RUN() {
const std::vector<text_ctx>& tcs = I()._tcs;
for (size_t i=0; i<tcs.size(); ++i) {
Status::c() = 0;
std::cout<<tcs[i].group<<" "<<tcs[i].name<<" start --->"<<std::endl;
tcs[i].tc();
if (Status::c() == 0) {
std::cout<<tcs[i].group<<" "<<tcs[i].name<<" passed.";
} else {
std::cout<<tcs[i].group<<" "<<tcs[i].name<<" fail. ";
}
std::cout<<"<---"<<std::endl;
}
}
private:
// singleton
static TC_CONTAINER& I() {
static TC_CONTAINER _inst;
return _inst;
}
std::vector<text_ctx> _tcs;
};
// add test case in ctor
class AUTO_ADD_TC {
public:
AUTO_ADD_TC(test_case tc, const char* g, const char*n) {
TC_CONTAINER::add(tc, g, n);
}
};
// type convert to support std::cout
class TV {
public:
#ifdef X_PACK_SUPPORT_CXX0X
template <class TYPE>
static typename std::enable_if<std::is_enum<TYPE>::value, int64_t>::type tv(const TYPE &d) {
return (int64_t)d;
}
template <class TYPE>
static typename std::enable_if<!std::is_enum<TYPE>::value, TYPE>::type tv(const TYPE &d) {
return d;
}
static const char* tv(const char* d) {
return d;
}
#else
template <class TYPE>
static const TYPE& tv(const TYPE&d) {
return d;
}
#endif
#ifdef XPACK_SUPPORT_QT
static std::string tv(const QString &d) {
return d.toStdString();
}
#endif
};
#define expect_eq_float(a,b,delta) \
do {\
if (!((a)>=(b)-delta && (a)<=(b)+delta)) {\
std::cout<<std::endl<<"++++++++++"<<__FILE__<<':'<<__LINE__<<'['<<__FUNCTION__<<']'<<"EXPECT_EQ fail.++++++++"<<std::endl;\
std::cout<<"expect:"<<TV::tv(b)<<std::endl;\
std::cout<<"actual:"<<TV::tv(a)<<std::endl<<std::endl;\
++Status::c();\
}\
}while(false)
#define EXPECT_EQ(a,b) \
do {\
if (!((a) == (b))) {\
std::cout<<std::endl<<"++++++++++"<<__FILE__<<':'<<__LINE__<<'['<<__FUNCTION__<<']'<<"EXPECT_EQ fail.++++++++"<<std::endl;\
std::cout<<"expect:"<<TV::tv(b)<<std::endl;\
std::cout<<"actual:"<<TV::tv(a)<<std::endl<<std::endl;\
++Status::c();\
}\
}while(false)
#define TEST(a, b) static void a##_##b(); static AUTO_ADD_TC __aat__##a##_##b(a##_##b, #a, #b); static void a##_##b()
#define EXPECT_FLOAT_EQ(a, b) expect_eq_float(a, b, 0.000001)
#define EXPECT_DOUBLE_EQ(a, b) expect_eq_float(a, b, 0.000001)
#define EXPECT_TRUE(a) \
do {\
if (!(a)) {\
std::cout<<std::endl<<"+++++++++++++++++++++++++++"<<__FILE__<<':'<<__LINE__<<'['<<__FUNCTION__<<']'<<"EXPECT_TRUE fail."<<std::endl;\
++Status::c();\
}\
}while(false)
#define EXPECT_FALSE(a) \
do {\
if ((a)) {\
std::cout<<std::endl<<"+++++++++++++++++++++++++++"<<__FILE__<<':'<<__LINE__<<'['<<__FUNCTION__<<']'<<"EXPECT_FALSE fail."<<std::endl;\
++Status::c();\
}\
}while(false)
#endif

45
util/xpack/gtest/makefile Executable file
View file

@ -0,0 +1,45 @@
ifeq ($(GPP),)
GPP=g++
endif
MFLAG=-Wall -Wextra
INC=-I ../..
LIB=-pthread
# c11 default on
ifneq ($(c11),off)
MFLAG+=-std=c++11
# gtest default off
ifeq ($(gtest),on)
INC+=-I ../../googletest/googletest/include
LIB+=-L ../../googletest/lib -lgtest
MFLAG+=-DXGTEST
endif
endif
# QT default off
ifeq ($(qt),on)
INC+=-I /usr/include/qt4 -I /usr/include/qt4/QtCore
LIB+=-lQtCore
MFLAG+=-DXPACK_SUPPORT_QT
endif
ifneq ($(xout),)
MFLAG+=-DXPACK_OUT_TEST
endif
xtest:
$(GPP) -o $@ -g $(MFLAG) test.cpp $(INC) $(LIB)
@-valgrind --tool=memcheck --leak-check=full ./$@
@-rm $@
test:
$(GPP) -o $@ -g $(MFLAG) test.cpp $(INC) $(LIB)
@-./$@
@-rm $@
bson:bson_test.cpp
$(GPP) -o $@ -g $< -std=c++11 $(INC) $(LIB) $(MFLAG) -lbson-1.0
@-valgrind --tool=memcheck --leak-check=full ./$@
@-rm $@

16
util/xpack/gtest/msvc.make Executable file
View file

@ -0,0 +1,16 @@
SRC=$(wildcard *.cpp)
TAR=$(SRC:.cpp=.exe)
ifneq ($(xout),)
MFLAG+=-DXPACK_OUT_TEST
endif
%.exe:%.cpp
cl /Fe:${CURDIR}/$@ $< /EHsc /nologo /wd4819 -I ../.. $(MFLAG)
@echo ============ run $@ ================
@$@
tar:$(TAR)
@echo -------test done-------
@-del *.obj
@-del *.exe

879
util/xpack/gtest/test.cpp Executable file
View file

@ -0,0 +1,879 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#ifdef XGTEST
#include<gtest/gtest.h>
#else
#include "gtest_stub.h"
#endif
#include "xpack/json.h"
#include "xpack/xml.h"
#include "string.h"
using namespace std;
struct Base {
int a;
string b;
Base(const int _a=0, const string&_b=""):a(_a), b(_b){}
#ifndef XPACK_OUT_TEST
XPACK(O(a, b));
};
#else
};
XPACK_OUT(Base, O(a,b));
#endif
struct POD {
int a;
char b[10];
#ifndef XPACK_OUT_TEST
XPACK(O(a, b));
};
#else
};
XPACK_OUT(POD, O(a,b));
#endif
// ++++++++++++++++++++++BuiltInTypes++++++++++++++++++++++++++++
struct BuiltInTypes {
signed char sch;
char ch;
unsigned char uch;
short sh;
unsigned short ush;
int i;
unsigned int ui;
long l;
unsigned long ul;
long long ll;
unsigned long long ull;
float f;
double d;
long double ld;
bool b;
#ifndef XPACK_OUT_TEST
XPACK(AF(F(ATTR), sch, "xml:s:ch"), X(F(ATTR), ch, uch, sh, ush), O(i, ui, l, ul, ll, ull, f, d, ld, b));
};
#else
};
XPACK_OUT(BuiltInTypes, AF(F(ATTR), sch, "xml:s:ch"), X(F(ATTR), ch, uch, sh, ush), O(i, ui, l, ul, ll, ull, f, d, ld, b));
#endif
void checkBuiltInTypes(const BuiltInTypes&a, const BuiltInTypes&b) {
EXPECT_EQ(a.sch, b.sch);
EXPECT_EQ(a.ch, b.ch);
EXPECT_EQ(a.uch, b.uch);
EXPECT_EQ(a.sh, b.sh);
EXPECT_EQ(a.ush, b.ush);
EXPECT_EQ(a.i, b.i);
EXPECT_EQ(a.ui, b.ui);
EXPECT_EQ(a.l, b.l);
EXPECT_EQ(a.ul, b.ul);
EXPECT_EQ(a.ll, b.ll);
EXPECT_EQ(a.ull, b.ull);
EXPECT_FLOAT_EQ(a.f, b.f);
EXPECT_DOUBLE_EQ(a.d, b.d);
EXPECT_DOUBLE_EQ(a.ld, b.ld);
EXPECT_TRUE(a.b);
}
TEST(builtin, types) {
BuiltInTypes bt;
bt.sch = 0x7f;
bt.ch = 0x7f;
bt.uch = 0x80;
bt.sh = 0x7fff;
bt.ush = 0x8000;
bt.i = 0x7fffffff;
bt.ui = 0x80000000;
bt.l = 0x7fffffffffffffff;
bt.ul = 0x8000000000000000;
bt.ll = bt.l;
bt.ull = bt.ul;
bt.f = 1.234;
bt.d = 9.678;
bt.ld = 30.678;
bt.b = true;
string s1 = xpack::json::encode(bt);
BuiltInTypes j1;
xpack::json::decode(s1, j1);
checkBuiltInTypes(j1, bt);
string s2 = xpack::xml::encode(bt, "root");
BuiltInTypes j2;
xpack::xml::decode(s2, j2);
checkBuiltInTypes(j2, bt);
}
// ++++++++++++++++++++bit field+++++++++++++++++++++++++++
struct BitField {
short a:8;
short b:8;
#ifndef XPACK_OUT_TEST
XPACK(B(F(0), a, b));
};
#else
};
XPACK_OUT(BitField, B(F(0), a, b));
#endif
void checkBitField(const BitField&a, const BitField&b) {
EXPECT_EQ(a.a, b.a);
EXPECT_EQ(a.b, b.b);
}
TEST(bitfiled, base) {
BitField bf;
bf.a = 0x70;
bf.b = 0x7f;
string s1 = xpack::json::encode(bf);
BitField j1;
xpack::json::decode(s1, j1);
checkBitField(j1, bf);
string s2 = xpack::xml::encode(bf, "root");
BitField j2;
xpack::xml::decode(s2, j2);
checkBitField(j2, bf);
}
// +++++++++++++++++++ enum +++++++++++++++++++++
enum Enum1 {
E0 = 0,
E1 = 1,
};
#ifdef X_PACK_SUPPORT_CXX0X
enum class Enum2:int64_t {
E2 = 2,
E3 = 3,
};
#else
enum Enum2 {
E2 = 2,
E3 = 3,
};
#endif
struct EnumBase {
Enum1 e1;
Enum2 e2;
#ifndef XPACK_OUT_TEST
#ifdef X_PACK_SUPPORT_CXX0X
XPACK(O(e1), E(F(0), e2));
#else
XPACK(E(F(0), e1, e2));
#endif
};
#else
};
#ifdef X_PACK_SUPPORT_CXX0X
XPACK_OUT(EnumBase, O(e1), E(F(0), e2));
#else
XPACK_OUT(EnumBase, E(F(0), e1, e2));
#endif
#endif
void checkEnumBase(const EnumBase&a, const EnumBase&b) {
EXPECT_EQ(a.e1, b.e1);
EXPECT_EQ(a.e2, b.e2);
}
TEST(enum, base) {
EnumBase eb;
eb.e1 = E0;
#ifdef X_PACK_SUPPORT_CXX0X
eb.e2 = Enum2::E3;
#else
eb.e2 = E3;
#endif
string s1 = xpack::json::encode(eb);
EnumBase j1;
xpack::json::decode(s1, j1);
checkEnumBase(j1, eb);
string s2 = xpack::xml::encode(eb, "root");
EnumBase j2;
xpack::xml::decode(s2, j2);
checkEnumBase(j2, eb);
}
#ifdef X_PACK_SUPPORT_CXX0X
TEST(enum, map) {
map<Enum2, int> m;
m[Enum2::E2] = 2;
m[Enum2::E3] = 3;
string s1 = xpack::json::encode(m);
map<Enum2, int> j1;
xpack::json::decode(s1, j1);
EXPECT_EQ(j1[Enum2::E2], 2);
EXPECT_EQ(j1[Enum2::E3], 3);
}
#endif
// ++++++++++++++++inherit+++++++++++++++++++++++++++++
struct InheritBase {
int b1;
string b2;
#ifndef XPACK_OUT_TEST
XPACK(O(b1, b2));
};
#else
};
XPACK_OUT(InheritBase, O(b1, b2));
#endif
struct InheritChild:public InheritBase {
int c1;
string c2;
#ifndef XPACK_OUT_TEST
XPACK(I(InheritBase), O(c1, c2));
};
#else
};
XPACK_OUT(InheritChild, I(InheritBase), O(c1, c2));
#endif
void checkInherit(const InheritChild&a, const InheritChild&b) {
EXPECT_EQ(a.b1, b.b1);
EXPECT_EQ(a.b2, b.b2);
EXPECT_EQ(a.c1, b.c1);
EXPECT_EQ(a.c2, b.c2);
}
TEST(inherit, base) {
InheritChild c;
c.b1 = 1;
c.b2 = "base";
c.c1 = 2;
c.c2 = "child";
string s1 = xpack::json::encode(c);
InheritChild j1;
xpack::json::decode(s1, j1);
checkInherit(j1, c);
string s2 = xpack::xml::encode(c, "root");
InheritChild j2;
xpack::xml::decode(s2, j2);
checkInherit(j2, c);
}
// ++++++++++++++++++++++++array++++++++++++++++++++++++++
struct Array {
int a[3];
char b[3];
Base c[3];
#ifndef XPACK_OUT_TEST
XPACK(O(a, b, c));
};
#else
};
XPACK_OUT(Array, O(a, b, c));
#endif
void checkArray(const Array&a, const Array&b) {
EXPECT_EQ(a.a[0], b.a[0]);
EXPECT_EQ(a.a[1], b.a[1]);
EXPECT_EQ(a.a[2], b.a[2]);
EXPECT_EQ(a.b[0], b.b[0]);
EXPECT_EQ(a.b[1], b.b[1]);
EXPECT_EQ(a.b[2], b.b[2]);
EXPECT_EQ(a.c[0].a, b.c[0].a);
EXPECT_EQ(a.c[0].b, b.c[0].b);
EXPECT_EQ(a.c[1].a, b.c[1].a);
EXPECT_EQ(a.c[1].b, b.c[1].b);
EXPECT_EQ(a.c[2].a, b.c[2].a);
EXPECT_EQ(a.c[2].b, b.c[2].b);
}
TEST(array, base) {
Array a;
a.a[0] = 1;
a.a[1] = 2;
a.a[2] = 3;
a.b[0] = 'g';
a.b[1] = 'o';
a.b[2] = '\0';
a.c[0].a = 11;
a.c[1].a = 12;
a.c[2].a = 13;
a.c[0].b = "hello";
a.c[1].b = "good";
a.c[1].b = "nice";
string s1 = xpack::json::encode(a);
Array j1;
xpack::json::decode(s1, j1);
checkArray(j1, a);
string s2 = xpack::xml::encode(a, "root");
Array j2;
xpack::xml::decode(s2, j2);
checkArray(j2, a);
char ca[3];
xpack::json::decode("\"good\"", ca);
EXPECT_TRUE(0==strcmp(ca, "go"));
int ia[3];
xpack::json::decode("[1, 2, 3, 4]", ia);
EXPECT_EQ(ia[2], 3);
}
// +++++++++++++++ container ++++++++++++++++++++++
struct ContainerBase {
map<string, int> m;
set<int> s;
vector<int> v;
list<int> l;
vector<vector<int> > vv;
#ifndef XPACK_OUT_TEST
XPACK(O(m, s, v, l, vv));
};
#else
};
XPACK_OUT(ContainerBase, O(m, s, v, l, vv));
#endif
void checkContainerBase(ContainerBase&cb) {
EXPECT_EQ(cb.m.size(), 2U);
EXPECT_EQ(cb.m["a"], 1);
EXPECT_EQ(cb.m["b"], 2);
EXPECT_EQ(cb.s.size(), 2U);
EXPECT_TRUE(cb.s.find(3)!=cb.s.end());
EXPECT_TRUE(cb.s.find(4)!=cb.s.end());
EXPECT_TRUE(cb.s.find(5)==cb.s.end());
EXPECT_EQ(cb.v.size(), 2U);
EXPECT_EQ(cb.v[0], 5);
EXPECT_EQ(cb.v[1], 6);
EXPECT_EQ(cb.l.size(), 2U);
EXPECT_EQ(*(cb.l.begin()), 7);
EXPECT_EQ(*(++cb.l.begin()), 8);
EXPECT_EQ(cb.vv.size(), 2U);
EXPECT_EQ(cb.vv[0].size(), 2U);
EXPECT_EQ(cb.vv[1].size(), 2U);
EXPECT_EQ(cb.vv[0][0], 11);
EXPECT_EQ(cb.vv[0][1], 12);
EXPECT_EQ(cb.vv[1][0], 22);
EXPECT_EQ(cb.vv[1][1], 23);
}
TEST(container, base) {
string s = "{\"m\":{\"a\":1, \"b\":2}, \"s\":[3, 4], \"v\":[5,6], \"l\":[7, 8], \"vv\":[[11,12],[22,23]]}";
ContainerBase cb;
xpack::json::decode(s, cb);
checkContainerBase(cb);
string s1 = xpack::json::encode(cb);
ContainerBase cb2;
xpack::json::decode(s1, cb2);
string s2 = xpack::json::encode(cb2);
EXPECT_EQ(s1, s2);
string s3 = xpack::xml::encode(cb, "root");
ContainerBase cb3;
xpack::xml::decode(s3, cb3);
checkContainerBase(cb3);
string s4 = xpack::xml::encode(cb3, "root");
EXPECT_EQ(s3, s4);
}
struct ContainerStruct {
map<string, Base> m;
vector<Base> v;
list<Base> l;
vector<vector<Base> > vv;
#ifndef XPACK_OUT_TEST
XPACK(O(m, v, l, vv));
};
#else
};
XPACK_OUT(ContainerStruct, O(m, v, l, vv));
#endif
void checkContainerStruct(ContainerStruct&a) {
EXPECT_EQ(a.m.size(), 2U);
EXPECT_EQ(a.m["a"].a, 1);
EXPECT_EQ(a.m["a"].b, "good");
EXPECT_EQ(a.m["b"].a, 2);
EXPECT_EQ(a.m["b"].b, "nice");
EXPECT_EQ(a.v.size(), 2U);
EXPECT_EQ(a.v[0].a, 3);
EXPECT_EQ(a.v[0].b, "hello");
EXPECT_EQ(a.v[1].a, 4);
EXPECT_EQ(a.v[1].b, "wow");
list<Base>::const_iterator lit = a.l.begin();
EXPECT_EQ(a.l.size(), 2U);
EXPECT_EQ(lit->a, 5);
EXPECT_EQ(lit->b, "dida");
++lit;
EXPECT_EQ(lit->a, 6);
EXPECT_EQ(lit->b, "haha");
EXPECT_EQ(a.vv.size(), 2U);
EXPECT_EQ(a.vv[0].size(), 1U);
EXPECT_EQ(a.vv[1].size(), 2U);
EXPECT_EQ(a.vv[0][0].a, 7);
EXPECT_EQ(a.vv[0][0].b, "lala");
EXPECT_EQ(a.vv[1][0].a, 8);
EXPECT_EQ(a.vv[1][0].b, "wawa");
EXPECT_EQ(a.vv[1][1].a, 9);
EXPECT_EQ(a.vv[1][1].b, "kaka");
}
TEST(container, struct) {
ContainerStruct cb;
cb.m["a"] = Base(1, "good");
cb.m["b"] = Base(2, "nice");
cb.v.push_back(Base(3, "hello"));
cb.v.push_back(Base(4, "wow"));
cb.l.push_back(Base(5, "dida"));
cb.l.push_back(Base(6, "haha"));
cb.vv.resize(2);
cb.vv[0].resize(1);
cb.vv[1].resize(2);
cb.vv[0][0].a = 7;
cb.vv[0][0].b = "lala";
cb.vv[1][0].a = 8;
cb.vv[1][0].b = "wawa";
cb.vv[1][1].a = 9;
cb.vv[1][1].b = "kaka";
string s1 = xpack::json::encode(cb);
ContainerStruct cb1;
xpack::json::decode(s1, cb1);
checkContainerStruct(cb1);
string s2 = xpack::json::encode(cb1);
EXPECT_EQ(s1, s2);
string s3 = xpack::xml::encode(cb, "root");
ContainerStruct cb3;
xpack::xml::decode(s3, cb3);
checkContainerStruct(cb3);
string s4 = xpack::xml::encode(cb3, "root");
EXPECT_EQ(s3, s4);
}
// +++++++++++++++++++ QT ++++++++++++++++++++
#ifdef XPACK_SUPPORT_QT
struct ContainerQT {
QMap<string, Base> m;
QVector<Base> v;
QList<Base> l;
QVector<QVector<Base> > vv;
#ifndef XPACK_OUT_TEST
XPACK(O(m, v, l, vv));
};
#else
};
XPACK_OUT(ContainerQT, O(m, v, l, vv));
#endif
void checkContainerQT(ContainerQT&a) {
EXPECT_EQ(a.m.size(), 2);
EXPECT_EQ(a.m["a"].a, 1);
EXPECT_EQ(a.m["a"].b, "good");
EXPECT_EQ(a.m["b"].a, 2);
EXPECT_EQ(a.m["b"].b, "nice");
EXPECT_EQ(a.v.size(), 2);
EXPECT_EQ(a.v[0].a, 3);
EXPECT_EQ(a.v[0].b, "hello");
EXPECT_EQ(a.v[1].a, 4);
EXPECT_EQ(a.v[1].b, "wow");
QList<Base>::const_iterator lit = a.l.begin();
EXPECT_EQ(a.l.size(), 2);
EXPECT_EQ(lit->a, 5);
EXPECT_EQ(lit->b, "dida");
++lit;
EXPECT_EQ(lit->a, 6);
EXPECT_EQ(lit->b, "haha");
EXPECT_EQ(a.vv.size(), 2);
EXPECT_EQ(a.vv[0].size(), 1);
EXPECT_EQ(a.vv[1].size(), 2);
EXPECT_EQ(a.vv[0][0].a, 7);
EXPECT_EQ(a.vv[0][0].b, "lala");
EXPECT_EQ(a.vv[1][0].a, 8);
EXPECT_EQ(a.vv[1][0].b, "wawa");
EXPECT_EQ(a.vv[1][1].a, 9);
EXPECT_EQ(a.vv[1][1].b, "kaka");
}
TEST(container, QT) {
ContainerQT cb;
cb.m["a"] = Base(1, "good");
cb.m["b"] = Base(2, "nice");
cb.v.push_back(Base(3, "hello"));
cb.v.push_back(Base(4, "wow"));
cb.l.push_back(Base(5, "dida"));
cb.l.push_back(Base(6, "haha"));
cb.vv.resize(2);
cb.vv[0].resize(1);
cb.vv[1].resize(2);
cb.vv[0][0].a = 7;
cb.vv[0][0].b = "lala";
cb.vv[1][0].a = 8;
cb.vv[1][0].b = "wawa";
cb.vv[1][1].a = 9;
cb.vv[1][1].b = "kaka";
string s1 = xpack::json::encode(cb);
ContainerQT cb1;
xpack::json::decode(s1, cb1);
checkContainerQT(cb1);
string s2 = xpack::json::encode(cb1);
EXPECT_EQ(s1, s2);
string s3 = xpack::xml::encode(cb, "root");
ContainerQT cb3;
xpack::xml::decode(s3, cb3);
checkContainerQT(cb3);
string s4 = xpack::xml::encode(cb3, "root");
EXPECT_EQ(s3, s4);
}
#endif
// ++++++++++++++ xtypes +++++++++++++++++++++
struct XtypeUnion {
int type;
union {
POD p;
int i;
char s[10];
};
};
struct XtypeUnionTop {
string name;
XtypeUnion un;
#ifndef XPACK_OUT_TEST
XPACK(O(name, un));
};
#else
};
XPACK_OUT(XtypeUnionTop, O(name, un));
#endif
namespace xpack { // must define in namespace xpack
template<>
struct is_xpack_xtype<XtypeUnion> {static bool const value = true;};
template<class OBJ>
bool xpack_xtype_decode(OBJ &obj, XtypeUnion &val, const Extend *ext) {
(void)ext;
OBJ *o = &obj;
o->decode("type", val.type, NULL);
switch (val.type) {
case 1:
o->decode("p", val.p, NULL);
break;
case 2:
o->decode("i", val.i, NULL);
break;
case 3:
o->decode("s", val.s, NULL);
break;
}
return true;
}
template<class OBJ>
bool xpack_xtype_encode(OBJ &obj, const char*key, const XtypeUnion &val, const Extend *ext) {
obj.ObjectBegin(key, ext);
obj.encode("type", val.type, NULL);
switch (val.type) {
case 1:
obj.encode("p", val.p, NULL);
break;
case 2:
obj.encode("i", val.i, NULL);
break;
case 3:
obj.encode("s", val.s, NULL);
break;
}
obj.ObjectEnd(key, ext);
return true;
}
}
void checkXtypesUnion(const XtypeUnionTop &xt, bool cn) {
if (cn) {
EXPECT_EQ(xt.name, "hello");
}
if (xt.un.type == 1) {
EXPECT_EQ(xt.un.p.a, 10);
EXPECT_TRUE(0 == strcmp(xt.un.p.b, "good"));
} else if (xt.un.type == 2) {
EXPECT_EQ(xt.un.i, 20);
} else if (xt.un.type == 3) {
EXPECT_TRUE(0 == strcmp(xt.un.s, "nice"));
} else {
EXPECT_EQ(xt.un.type, 1);
}
}
TEST(xtypes, union) {
XtypeUnionTop xt;
xt.name = "hello";
xt.un.type = 1;
xt.un.p.a = 10;
strcpy(xt.un.p.b, "good");
string s = xpack::json::encode(xt);
XtypeUnionTop xt1;
xpack::json::decode(s, xt1);
checkXtypesUnion(xt1, true);
s = xpack::xml::encode(xt, "root");
XtypeUnionTop xt2;
xpack::xml::decode(s, xt2);
checkXtypesUnion(xt2, true);
s = xpack::json::encode(xt.un);
XtypeUnionTop xt3;
xpack::json::decode(s, xt3.un);
checkXtypesUnion(xt3, false);
s = xpack::xml::encode(xt.un, "root");
XtypeUnionTop xt4;
xpack::xml::decode(s, xt4.un);
checkXtypesUnion(xt4, false);
}
// +++++++++++++++++ custom +++++++++++++++++
struct Custom {
int a;
int b;
int c;
XPACK(O(a, b), C(hex, F(0), c));
};
namespace xpack {
template <class OBJ>
bool hex_encode(OBJ &obj, const Custom&c, const char*key, const int &i, const Extend *ext) {
(void)c;
stringstream ss;
ss<<"0x"<<std::hex<<i;
return obj.encode(key, ss.str(), ext);
}
template <class OBJ>
bool hex_decode(OBJ &obj, Custom&c, const char*key, int &i, const Extend *ext) {
(void)c;
string s;
if (!obj.decode(key, s, ext)) {
return false;
}
stringstream ss;
ss<<std::hex<<s;
ss>>i;
return true;
}
}
TEST(custom, hex) {
Custom c;
c.a = 1;
c.b = 2;
c.c = 0xe;
string s = xpack::json::encode(c);
EXPECT_EQ(s, "{\"a\":1,\"b\":2,\"c\":\"0xe\"}");
Custom c1;
xpack::json::decode(s, c1);
EXPECT_EQ(c1.a, 1);
EXPECT_EQ(c1.b, 2);
EXPECT_EQ(c1.c, 0xe);
}
// +++++++++++++++++ flags ++++++++++++++++++
struct FlagEN {
int a;
string b;
#ifdef X_PACK_SUPPORT_CXX0X
shared_ptr<int> c;
#else
int c;
#endif
XPACK(X(F(EN), a, b, c));
FlagEN(){c=0;}
};
TEST(flags, EN) {
FlagEN fe;
fe.a = 1;
string s = xpack::json::encode(fe);
EXPECT_EQ(s, "{\"a\":1,\"b\":null,\"c\":null}");
FlagEN f1;
xpack::json::decode(s, f1);
EXPECT_EQ(f1.a, 1);
EXPECT_EQ(f1.b, "");
#ifdef X_PACK_SUPPORT_CXX0X
EXPECT_EQ(f1.c.get(), (int*)NULL);
#else
EXPECT_EQ(f1.c, 0);
#endif
}
struct FlagM {
int a;
string b;
XPACK(O(a), M(b));
};
TEST(flags, M) {
string s = "{\"a\":1}";
bool except = false;
try {
FlagM f;
xpack::json::decode(s, f);
} catch(...) {
except = true;
}
EXPECT_TRUE(except);
}
struct FlagATTR {
int a;
string b;
XPACK(O(a), X(F(ATTR), b));
};
TEST(flags, ATTR) {
FlagATTR f;
f.a = 1;
f.b = "hello";
string s = xpack::xml::encode(f, "root");
EXPECT_EQ(s, "<root b=\"hello\"><a>1</a></root>");
FlagATTR f1;
xpack::xml::decode(s, f1);
EXPECT_EQ(f1.a, 1);
EXPECT_EQ(f1.b, "hello");
}
struct FlagVectorLabel {
vector<int> a;
vector<int> b;
vector<int> c;
XPACK(O(a), A(b, "xml:b,sbs", c, "xml:c,vl@x"));
};
TEST(flags, vectorlabel) {
FlagVectorLabel f;
f.a.push_back(1);
f.a.push_back(2);
f.b.push_back(3);
f.b.push_back(4);
f.c.push_back(5);
f.c.push_back(6);
string s = xpack::xml::encode(f, "root");
EXPECT_EQ(s, "<root><a><a>1</a><a>2</a></a><b>3</b><b>4</b><c><x>5</x><x>6</x></c></root>");
FlagVectorLabel f1;
xpack::xml::decode(s, f1);
EXPECT_EQ(f1.a.size(), 2U);
EXPECT_EQ(f1.a[0], 1);
EXPECT_EQ(f1.a[1], 2);
EXPECT_EQ(f1.b.size(), 2U);
EXPECT_EQ(f1.b[0], 3);
EXPECT_EQ(f1.b[1], 4);
EXPECT_EQ(f1.c.size(), 2U);
EXPECT_EQ(f1.c[0], 5);
EXPECT_EQ(f1.c[1], 6);
}
TEST(jsondata, memory) {
xpack::JsonData *jd = new xpack::JsonData;
xpack::json::decode("[0, 1, 2, 3]", *jd);
EXPECT_TRUE(jd->IsArray());
EXPECT_EQ(jd->Size(), 4U);
EXPECT_TRUE((*jd)[(size_t)0].IsNumber());
EXPECT_EQ((*jd)[1].Get<int64_t>(), 1);
xpack::JsonData s = *jd;
delete jd;
EXPECT_TRUE(s.IsArray());
EXPECT_TRUE(s[(size_t)0].IsNumber());
EXPECT_EQ(s[1].Get<int64_t>(), 1);
}
// ++++++++++++++++++bug history+++++++++++++++++++++++
TEST(bughis, notexists) {
Base b(9, "");
xpack::json::decode("{\"a\":1}", b);
EXPECT_EQ(b.a, 1);
Base b1(10, "");
xpack::xml::decode("<root><a>1</a></root>", b1);
EXPECT_EQ(b1.a, 1);
}
struct OmitEmpty{
int a;
vector<int> b;
#ifdef X_PACK_SUPPORT_CXX0X
shared_ptr<Base> c;
#else
Base c;
#endif
XPACK(X(F(OE), a, b, c));
};
TEST(bughis, omitempty) {
OmitEmpty oe;
oe.a = 0;
oe.b.push_back(0);
oe.b.push_back(0);
#ifdef X_PACK_SUPPORT_CXX0X
oe.c.reset(new Base(0, ""));
#endif
string s = xpack::json::encode(oe);
EXPECT_EQ(s, "{\"b\":[0,0],\"c\":{\"a\":0,\"b\":\"\"}}");
s = xpack::xml::encode(oe, "root");
EXPECT_EQ(s, "<root><b><b>0</b><b>0</b></b><c><a>0</a><b/></c></root>");
oe.b.clear();
oe.a = 1;
s = xpack::json::encode(oe);
EXPECT_EQ(s, "{\"a\":1,\"c\":{\"a\":0,\"b\":\"\"}}");
s = xpack::xml::encode(oe, "root");
EXPECT_EQ(s, "<root><a>1</a><c><a>0</a><b/></c></root>");
}
#ifdef X_PACK_SUPPORT_CXX0X
struct SharedPtrNull{
shared_ptr<xpack::JsonData> jd;
XPACK(O(jd));
};
TEST(bughis, shared_ptr_null) {
SharedPtrNull sn;
xpack::json::decode("{\"jd\":null}", sn);
EXPECT_FALSE(sn.jd);
}
#endif
int main(int argc, char *argv[]) {
#ifdef XGTEST
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
#else
(void)argc;
(void)argv;
TC_CONTAINER::RUN();
return 0;
#endif
}

63
util/xpack/json.h Executable file
View file

@ -0,0 +1,63 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_JSON_H
#define __X_PACK_JSON_H
#include "json_decoder.h"
#include "json_encoder.h"
#if defined(X_PACK_SUPPORT_CXX0X) || defined (_GNU_SOURCE)
#include "json_data.h"
#endif
#include "xpack.h"
namespace xpack {
class json {
public:
template <class T>
static void decode(const std::string &data, T &val) {
JsonDecoder de;
de.decode(data, val);
}
template <class T>
static void decode(const rapidjson::Value &data, T &val) {
JsonNode node(&data);
XDecoder<JsonNode>(NULL, (const char*)NULL, node).decode(val, NULL);
}
template <class T>
static void decode_file(const std::string &file_name, T &val) {
JsonDecoder de;
de.decode_file(file_name, val);
}
template <class T>
static std::string encode(const T &val) {
JsonEncoder en;
return en.encode(val);
}
template <class T>
static std::string encode(const T &val, int flag, int indentCount, char indentChar) {
(void)flag;
JsonEncoder en(indentCount, indentChar);
return en.encode(val);
}
};
}
#endif

115
util/xpack/json_data.h Executable file
View file

@ -0,0 +1,115 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_JSON_DATA_H
#define __X_PACK_JSON_DATA_H
#include "rapidjson_custom.h"
#include "rapidjson/document.h"
#include "util.h"
#include "traits.h"
namespace xpack {
// wrapper for rapidjson::Value.
// If we use other json parser someday, users won't have to modify the code.
// Most of the code is copied from rapidjson.
enum JsonType {
kNullType = 0, //!< null
kFalseType = 1, //!< false
kTrueType = 2, //!< true
kObjectType = 3, //!< object
kArrayType = 4, //!< array
kStringType = 5, //!< string
kNumberType = 6 //!< number
};
class JsonData {
friend class JsonNode;
friend class JsonWriter;
public:
JsonData():current(NULL){}
// check is valid JsonData
operator bool() const {
return NULL != current;
}
JsonType Type() const {
if (NULL != current) {
return static_cast<JsonType>(current->GetType());
}
return kNullType; // ?
}
bool IsNull() const { return NULL==current||current->IsNull(); }
bool IsBool() const { return NULL!=current && current->IsBool(); }
bool IsObject() const { return NULL!=current && current->IsObject(); }
bool IsArray() const { return NULL!=current && current->IsArray(); }
bool IsNumber() const { return NULL!=current && current->IsNumber(); }
bool IsDouble() const { return NULL!=current && current->IsDouble(); }
bool IsString() const { return NULL!=current && current->IsString(); }
size_t Size() const {return (size_t)current->Size();}
JsonData operator [](const char *key) const {
if (NULL != current && current->IsObject()) {
rapidjson::Value::ConstMemberIterator iter = current->FindMember(key);
if (iter != current->MemberEnd()) {
return JsonData(&iter->value);
} // no exception ?
}
return JsonData();
}
JsonData operator [](size_t index) const {
if (NULL != current && current->IsArray() && index < (size_t)current->Size()) {
return JsonData(&(*current)[(rapidjson::SizeType)index]);
}
return JsonData();
}
std::string String() const; // Implementation in json_encoder.h
template <typename T> // For base byte(integer/float/double/bool/std::string), implementation in json_decoder.h
T Get() const;
template <typename T> // struct, implementation in json_decoder.h
bool Get(T &val) const;
private:
JsonData(const rapidjson::Value*v):current(v){}
void reset(const rapidjson::Value *v) {
node.reset();
allocator.reset();
current = NULL;
if (NULL != v) {
if (v->IsNull()) {
node.reset(new rapidjson::Value());
} else {
allocator.reset(new rapidjson::MemoryPoolAllocator<>());
node.reset(new rapidjson::Value(*v, *allocator, true));
}
current = node.get();
}
}
x_shared_ptr<rapidjson::MemoryPoolAllocator<> > allocator;
x_shared_ptr<rapidjson::Value> node; // release node first
const rapidjson::Value* current;
};
}
#endif

218
util/xpack/json_decoder.h Executable file
View file

@ -0,0 +1,218 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_JSON_DECODER_H
#define __X_PACK_JSON_DECODER_H
#include <fstream>
#include "rapidjson_custom.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "xdecoder.h"
#include "json_data.h"
namespace xpack {
class JsonNode {
typedef XDecoder<JsonNode> decoder;
public:
typedef rapidjson::Value::ConstMemberIterator Iterator;
JsonNode(const rapidjson::Value* val=NULL):v(val){}
// convert JsonData to JsonNode
// The life cycle of jd cannot be shorter than JsonNode
JsonNode(const JsonData&jd):v(jd.current){}
inline static const char * Name() {
return "json";
}
operator bool() const {
return v != NULL;
}
bool IsNull() const {
return v!=NULL && v->IsNull();
}
JsonNode Find(decoder&de, const char*key, const Extend *ext) const {
(void)ext;
if (v->IsNull()) {
return JsonNode();
} else if (!v->IsObject()) {
de.decode_exception("not object", NULL);
}
rapidjson::Value::ConstMemberIterator iter = v->FindMember(key);
if (iter != v->MemberEnd()) {
return JsonNode(&iter->value);
} else {
return JsonNode();
}
}
size_t Size(decoder&de) const {
if (v->IsNull()) {
return 0;
} else if (!v->IsArray()) {
de.decode_exception("not array", NULL);
}
return (size_t)v->Size();
}
JsonNode At(size_t index) const { // no exception
return JsonNode(&(*v)[(rapidjson::SizeType)index]);
}
JsonNode Next(decoder&de, const JsonNode&parent, Iterator&iter, std::string&key) const {
if (!parent.v->IsObject()) {
de.decode_exception("not object", NULL);
}
if (v != parent.v) {
++iter;
} else {
iter = parent.v->MemberBegin();
}
if (iter != parent.v->MemberEnd()) {
key = iter->name.GetString();
return JsonNode(&iter->value);
}
return JsonNode();
}
bool Get(decoder&de, std::string&val, const Extend*ext) {
(void)ext;
if (v->IsString()) {
val = std::string(v->GetString(), v->GetStringLength());
} else if (!v->IsNull()) {
de.decode_exception("not string", NULL);
}
return true;
}
bool Get(decoder&de, bool &val, const Extend*ext) {
(void)ext;
if (v->IsBool()) {
val = v->GetBool();
} else if (v->IsInt64()) {
val = (0 != (v->GetInt64()));
} else if (v->IsNull()) {
val = false;
} else {
de.decode_exception("not bool or integer", NULL);
}
return true;
}
template <class T>
typename x_enable_if<numeric<T>::is_integer, bool>::type Get(decoder&de, T &val, const Extend*ext){
(void)ext;
if (v->IsInt()) {
val = (T)v->GetInt();
} else if (v->IsUint()) {
val = (T)v->GetUint();
} else if (v->IsInt64()) {
val = (T)v->GetInt64();
} else if (v->IsUint64()) {
val = (T)v->GetUint64();
} else if (v->IsNull()) {
val = 0;
} else {
de.decode_exception("not integer", NULL);
}
return true;
}
template <class T>
typename x_enable_if<numeric<T>::is_float, bool>::type Get(decoder&de, T &val, const Extend*ext){
(void)ext;
if (v->IsNumber()) {
val = (T)v->GetDouble();
} else if (v->IsNull()) {
val = 0;
} else {
de.decode_exception("not number", NULL);
}
return true;
}
////////////// for JsonData ///////////////////////
bool decode_type_spec(JsonData& val, const Extend *ext) {
(void)ext;
val.reset(v);
return true;
}
private:
const rapidjson::Value* v;
rapidjson::Value::ConstMemberIterator iter;
};
class JsonDecoder {
public:
template <class T>
bool decode(const std::string&str, T&val) {
rapidjson::Document doc;
if (this->parse(str, doc)) {
JsonNode node(&doc);
return XDecoder<JsonNode>(NULL, (const char*)NULL, node).decode(val, NULL);
}
return false;
}
template <class T>
bool decode_file(const std::string&fname, T&val) {
std::string data;
bool ret = Util::readfile(fname, data);
if (ret) {
ret = this->decode(data, val);
}
return ret;
}
private:
bool parse(const std::string&data, rapidjson::Document &doc) {
std::string err;
const unsigned int parseFlags = rapidjson::kParseNanAndInfFlag;
doc.Parse<parseFlags>(data.data(), data.length());
if (doc.HasParseError()) {
size_t offset = doc.GetErrorOffset();
std::string parse_err(rapidjson::GetParseError_En(doc.GetParseError()));
std::string err_data = data.substr(offset, 32);
err = "Parse json fail. err="+parse_err+". offset="+err_data;
throw std::runtime_error(err);
}
return true;
}
};
// /////////////// JsonData ///////////////////
template<>struct is_xpack_type_spec<JsonNode, JsonData> {static bool const value = true;};
template <typename T>
inline T JsonData::Get() const {
JsonNode node(*this);
T t;
XDecoder<JsonNode>(NULL, (const char*)NULL, node).decode(t, NULL);
return t;
}
template <typename T>
inline bool JsonData::Get(T&val) const {
JsonNode node(*this);
return XDecoder<JsonNode>(NULL, (const char*)NULL, node).decode(val, NULL);
}
}
#endif

294
util/xpack/json_encoder.h Executable file
View file

@ -0,0 +1,294 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_JSON_ENCODER_H
#define __X_PACK_JSON_ENCODER_H
#include <string>
#include "rapidjson_custom.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"
#include "xencoder.h"
#include "json_data.h"
namespace xpack {
class JsonWriter:private noncopyable {
typedef rapidjson::StringBuffer JSON_WRITER_BUFFER;
typedef rapidjson::Writer<rapidjson::StringBuffer> JSON_WRITER_WRITER;
typedef rapidjson::PrettyWriter<rapidjson::StringBuffer> JSON_WRITER_PRETTY;
friend class XEncoder<JsonWriter>;
friend class JsonEncoder;
const static bool support_null = true;
public:
JsonWriter(int indentCount = -1, char indentChar = ' ', int maxDecimalPlaces = -1) {
_buf = new JSON_WRITER_BUFFER;
if (indentCount < 0) {
_writer = new JSON_WRITER_WRITER(*_buf);
if (maxDecimalPlaces > 0) {
_writer->SetMaxDecimalPlaces(maxDecimalPlaces);
}
_pretty = NULL;
} else {
_pretty = new JSON_WRITER_PRETTY(*_buf);
_pretty->SetIndent(indentChar, indentCount);
if (maxDecimalPlaces > 0) {
_pretty->SetMaxDecimalPlaces(maxDecimalPlaces);
}
_writer = NULL;
}
}
~JsonWriter() {
if (NULL != _buf) {
delete _buf;
_buf = NULL;
}
if (NULL != _writer) {
delete _writer;
_writer = NULL;
}
if (NULL != _pretty) {
delete _pretty;
_pretty = NULL;
}
}
private:
inline static const char *Name() {
return "json";
}
inline const char *IndexKey(size_t index) {
(void)index;
return NULL;
}
std::string String() {
return _buf->GetString();
}
void ArrayBegin(const char *key, const Extend *ext) {
(void)ext;
xpack_set_key(key);
if (NULL != _writer) {
_writer->StartArray();
} else {
if (Extend::Flag(ext) & X_PACK_FLAG_SL) {
_pretty->SetFormatOptions(rapidjson::kFormatSingleLineArray);
}
_pretty->StartArray();
}
}
void ArrayEnd(const char *key, const Extend *ext) {
(void)key;
(void)ext;
if (NULL != _writer) {
_writer->EndArray();
} else {
_pretty->EndArray();
if (Extend::Flag(ext) & X_PACK_FLAG_SL) {
_pretty->SetFormatOptions(rapidjson::kFormatDefault);
}
}
}
void ObjectBegin(const char *key, const Extend *ext) {
(void)ext;
xpack_set_key(key);
if (NULL != _writer) {
_writer->StartObject();
} else {
_pretty->StartObject();
}
}
void ObjectEnd(const char *key, const Extend *ext) {
(void)key;
(void)ext;
if (NULL != _writer) {
_writer->EndObject();
} else {
_pretty->EndObject();
}
}
bool WriteNull(const char*key, const Extend *ext) {
(void)ext;
xpack_set_key(key);
if (NULL != _writer) {
_writer->Null();
} else {
_pretty->Null();
}
return true;
}
bool encode_bool(const char*key, const bool&val, const Extend *ext) {
(void)ext;
xpack_set_key(key);
if (NULL != _writer) {
_writer->Bool(val);
} else {
_pretty->Bool(val);
}
return true;
}
bool encode_string(const char*key, const char*val, size_t length, const Extend *ext) {
(void)ext;
xpack_set_key(key);
if (NULL != _writer) {
_writer->String(val, length);
} else {
_pretty->String(val, length);
}
return true;
}
bool encode_string(const char*key, const std::string&val, const Extend *ext) {
return this->encode_string(key, val.data(), val.length(), ext);
}
template <typename T>
typename x_enable_if<numeric<T>::is_integer && numeric<T>::is_signed, bool>::type encode_number(const char*key, const T&val, const Extend *ext) {
(void)ext;
xpack_set_key(key);
if (NULL != _writer) {
_writer->Int64((int64_t)val);
} else {
_pretty->Int64((int64_t)val);
}
return true;
}
template <typename T>
typename x_enable_if<numeric<T>::is_integer && !numeric<T>::is_signed, bool>::type encode_number(const char*key, const T&val, const Extend *ext) {
(void)ext;
xpack_set_key(key);
if (NULL != _writer) {
_writer->Uint64((uint64_t)val);
} else {
_pretty->Uint64((uint64_t)val);
}
return true;
}
template <typename T>
typename x_enable_if<numeric<T>::is_float, bool>::type encode_number(const char*key, const T&val, const Extend *ext) {
(void)ext;
xpack_set_key(key);
if (NULL != _writer) {
_writer->Double((double)val);
} else {
_pretty->Double((double)val);
}
return true;
}
// JsonData
bool encode_type_spec(const char*key, const JsonData&val, const Extend *ext) {
if (val.current == NULL) {
return false;
}
return this->encode_json_value(key, *val.current, ext);
}
bool encode_json_value(const char*key, const rapidjson::Value& val, const Extend *ext) {
switch (val.GetType()){
case rapidjson::kNullType:
return this->WriteNull(key, ext);
case rapidjson::kFalseType:
case rapidjson::kTrueType:
return this->encode_bool(key, val.GetBool(), ext);
case rapidjson::kStringType:
return this->encode_string(key, val.GetString(), (size_t)val.GetStringLength(), ext);
case rapidjson::kNumberType:
if (val.IsDouble()) {
return this->encode_number(key, val.GetDouble(), ext);
} else {
return this->encode_number(key, val.GetInt64(), ext);
}
case rapidjson::kObjectType:
this->ObjectBegin(key, ext);
for (rapidjson::Value::ConstMemberIterator iter = val.MemberBegin(); iter!=val.MemberEnd(); ++iter) {
this->encode_json_value(iter->name.GetString(), iter->value, ext);
}
this->ObjectEnd(key, ext);
break;
case rapidjson::kArrayType:{
this->ArrayBegin(key, ext);
size_t max = (size_t)val.Size();
for (size_t i = 0; i<max; ++i) {
this->encode_json_value(NULL, val[(rapidjson::SizeType)i], ext);
}
this->ArrayEnd(key, ext);
}
break;
}
return true;
}
void xpack_set_key(const char*key) { // openssl defined set_key macro, so we named it xpack_set_key
if (NULL!=key && key[0]!='\0') {
if (NULL != _writer) {
_writer->Key(key);
} else {
_pretty->Key(key);
}
}
}
JSON_WRITER_BUFFER* _buf;
JSON_WRITER_WRITER* _writer;
JSON_WRITER_PRETTY* _pretty;
};
class JsonEncoder {
public:
JsonEncoder() {
indentCount = -1;
indentChar = ' ';
maxDecimalPlaces = -1;
}
JsonEncoder(int _indentCount, char _indentChar, int _maxDecimalPlaces = -1) { // compat
indentCount = _indentCount;
indentChar = _indentChar;
maxDecimalPlaces = _maxDecimalPlaces;
}
void SetMaxDecimalPlaces(int _maxDecimalPlaces) {
maxDecimalPlaces = _maxDecimalPlaces;
}
template <class T>
std::string encode(const T&val) {
JsonWriter wr(indentCount, indentChar, maxDecimalPlaces);
XEncoder<JsonWriter> en(wr);
en.encode(NULL, val, NULL);
return wr.String();
}
private:
int indentCount;
char indentChar;
int maxDecimalPlaces;
};
// //////////////// JsonData ///////////////////////
template<>struct is_xpack_type_spec<JsonWriter, JsonData> {static bool const value = true;};
inline std::string JsonData::String() const {
JsonEncoder en;
return en.encode(*this);
}
}
#endif

503
util/xpack/l1l2_expand.h Executable file
View file

@ -0,0 +1,503 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_L1_L2_EXPAND_H
#define __X_L1_L2_EXPAND_H
// expand macro depend on parameters number
#define X_PACK_COUNT(LEVEL, ACTION, _99,_98,_97,_96,_95,_94,_93,_92,_91,_90,_89,_88,_87,_86,_85,_84,_83,_82,_81,_80,_79,_78,_77,_76,_75,_74,_73,_72,_71,_70,_69,_68,_67,_66,_65,_64,_63,_62,_61,_60,_59,_58,_57,_56,_55,_54,_53,_52,_51,_50,_49,_48,_47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1,N,...) LEVEL##N
#define X_PACK_EXPAND(...) __VA_ARGS__
#if !defined(_MSC_VER) || defined(__clang__)
#define X_PACK_N(LEVEL, ACTION, ...) X_PACK_COUNT(LEVEL, ACTION, __VA_ARGS__, _99,_98,_97,_96,_95,_94,_93,_92,_91,_90,_89,_88,_87,_86,_85,_84,_83,_82,_81,_80,_79,_78,_77,_76,_75,_74,_73,_72,_71,_70,_69,_68,_67,_66,_65,_64,_63,_62,_61,_60,_59,_58,_57,_56,_55,_54,_53,_52,_51,_50,_49,_48,_47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) (ACTION, __VA_ARGS__)
#define X_PACK_N2(LEVEL, ACTION, ARG, ...) X_PACK_COUNT(LEVEL, ACTION, __VA_ARGS__, _99,_98,_97,_96,_95,_94,_93,_92,_91,_90,_89,_88,_87,_86,_85,_84,_83,_82,_81,_80,_79,_78,_77,_76,_75,_74,_73,_72,_71,_70,_69,_68,_67,_66,_65,_64,_63,_62,_61,_60,_59,_58,_57,_56,_55,_54,_53,_52,_51,_50,_49,_48,_47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1) (ACTION, ARG, __VA_ARGS__)
#define X_PACK_L1_DEF(ACT, M) ACT(M) // here will expand to ACT(O(xxx)), ACT(A(a,x)), ACT(M(xxx))
#define X_PACK_L1_1(ACT, M) X_PACK_L1_DEF(ACT, M)
#define X_PACK_L1_2(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_1(ACT, __VA_ARGS__)
#define X_PACK_L1_3(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_2(ACT, __VA_ARGS__)
#define X_PACK_L1_4(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_3(ACT, __VA_ARGS__)
#define X_PACK_L1_5(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_4(ACT, __VA_ARGS__)
#define X_PACK_L1_6(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_5(ACT, __VA_ARGS__)
#define X_PACK_L1_7(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_6(ACT, __VA_ARGS__)
#define X_PACK_L1_8(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_7(ACT, __VA_ARGS__)
#define X_PACK_L1_9(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_8(ACT, __VA_ARGS__)
#define X_PACK_L1_10(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_9(ACT, __VA_ARGS__)
#define X_PACK_L1_11(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_10(ACT, __VA_ARGS__)
#define X_PACK_L1_12(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_11(ACT, __VA_ARGS__)
#define X_PACK_L1_13(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_12(ACT, __VA_ARGS__)
#define X_PACK_L1_14(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_13(ACT, __VA_ARGS__)
#define X_PACK_L1_15(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_14(ACT, __VA_ARGS__)
#define X_PACK_L1_16(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_15(ACT, __VA_ARGS__)
#define X_PACK_L1_17(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_16(ACT, __VA_ARGS__)
#define X_PACK_L1_18(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_17(ACT, __VA_ARGS__)
#define X_PACK_L1_19(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_18(ACT, __VA_ARGS__)
#define X_PACK_L1_20(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_19(ACT, __VA_ARGS__)
#define X_PACK_L1_21(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_20(ACT, __VA_ARGS__)
#define X_PACK_L1_22(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_21(ACT, __VA_ARGS__)
#define X_PACK_L1_23(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_22(ACT, __VA_ARGS__)
#define X_PACK_L1_24(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_23(ACT, __VA_ARGS__)
#define X_PACK_L1_25(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_24(ACT, __VA_ARGS__)
#define X_PACK_L1_26(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_25(ACT, __VA_ARGS__)
#define X_PACK_L1_27(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_26(ACT, __VA_ARGS__)
#define X_PACK_L1_28(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_27(ACT, __VA_ARGS__)
#define X_PACK_L1_29(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_28(ACT, __VA_ARGS__)
#define X_PACK_L1_30(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_29(ACT, __VA_ARGS__)
#define X_PACK_L1_31(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_30(ACT, __VA_ARGS__)
#define X_PACK_L1_32(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_31(ACT, __VA_ARGS__)
#define X_PACK_L1_33(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_32(ACT, __VA_ARGS__)
#define X_PACK_L1_34(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_33(ACT, __VA_ARGS__)
#define X_PACK_L1_35(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_34(ACT, __VA_ARGS__)
#define X_PACK_L1_36(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_35(ACT, __VA_ARGS__)
#define X_PACK_L1_37(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_36(ACT, __VA_ARGS__)
#define X_PACK_L1_38(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_37(ACT, __VA_ARGS__)
#define X_PACK_L1_39(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_38(ACT, __VA_ARGS__)
#define X_PACK_L1_40(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_39(ACT, __VA_ARGS__)
#define X_PACK_L1_41(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_40(ACT, __VA_ARGS__)
#define X_PACK_L1_42(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_41(ACT, __VA_ARGS__)
#define X_PACK_L1_43(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_42(ACT, __VA_ARGS__)
#define X_PACK_L1_44(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_43(ACT, __VA_ARGS__)
#define X_PACK_L1_45(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_44(ACT, __VA_ARGS__)
#define X_PACK_L1_46(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_45(ACT, __VA_ARGS__)
#define X_PACK_L1_47(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_46(ACT, __VA_ARGS__)
#define X_PACK_L1_48(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_47(ACT, __VA_ARGS__)
#define X_PACK_L1_49(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_48(ACT, __VA_ARGS__)
#define X_PACK_L1_50(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_49(ACT, __VA_ARGS__)
#define X_PACK_L1_51(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_50(ACT, __VA_ARGS__)
#define X_PACK_L1_52(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_51(ACT, __VA_ARGS__)
#define X_PACK_L1_53(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_52(ACT, __VA_ARGS__)
#define X_PACK_L1_54(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_53(ACT, __VA_ARGS__)
#define X_PACK_L1_55(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_54(ACT, __VA_ARGS__)
#define X_PACK_L1_56(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_55(ACT, __VA_ARGS__)
#define X_PACK_L1_57(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_56(ACT, __VA_ARGS__)
#define X_PACK_L1_58(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_57(ACT, __VA_ARGS__)
#define X_PACK_L1_59(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_58(ACT, __VA_ARGS__)
#define X_PACK_L1_60(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_59(ACT, __VA_ARGS__)
#define X_PACK_L1_61(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_60(ACT, __VA_ARGS__)
#define X_PACK_L1_62(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_61(ACT, __VA_ARGS__)
#define X_PACK_L1_63(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_62(ACT, __VA_ARGS__)
#define X_PACK_L1_64(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_63(ACT, __VA_ARGS__)
#define X_PACK_L1_65(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_64(ACT, __VA_ARGS__)
#define X_PACK_L1_66(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_65(ACT, __VA_ARGS__)
#define X_PACK_L1_67(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_66(ACT, __VA_ARGS__)
#define X_PACK_L1_68(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_67(ACT, __VA_ARGS__)
#define X_PACK_L1_69(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_68(ACT, __VA_ARGS__)
#define X_PACK_L1_70(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_69(ACT, __VA_ARGS__)
#define X_PACK_L1_71(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_70(ACT, __VA_ARGS__)
#define X_PACK_L1_72(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_71(ACT, __VA_ARGS__)
#define X_PACK_L1_73(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_72(ACT, __VA_ARGS__)
#define X_PACK_L1_74(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_73(ACT, __VA_ARGS__)
#define X_PACK_L1_75(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_74(ACT, __VA_ARGS__)
#define X_PACK_L1_76(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_75(ACT, __VA_ARGS__)
#define X_PACK_L1_77(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_76(ACT, __VA_ARGS__)
#define X_PACK_L1_78(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_77(ACT, __VA_ARGS__)
#define X_PACK_L1_79(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_78(ACT, __VA_ARGS__)
#define X_PACK_L1_80(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_79(ACT, __VA_ARGS__)
#define X_PACK_L1_81(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_80(ACT, __VA_ARGS__)
#define X_PACK_L1_82(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_81(ACT, __VA_ARGS__)
#define X_PACK_L1_83(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_82(ACT, __VA_ARGS__)
#define X_PACK_L1_84(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_83(ACT, __VA_ARGS__)
#define X_PACK_L1_85(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_84(ACT, __VA_ARGS__)
#define X_PACK_L1_86(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_85(ACT, __VA_ARGS__)
#define X_PACK_L1_87(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_86(ACT, __VA_ARGS__)
#define X_PACK_L1_88(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_87(ACT, __VA_ARGS__)
#define X_PACK_L1_89(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_88(ACT, __VA_ARGS__)
#define X_PACK_L1_90(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_89(ACT, __VA_ARGS__)
#define X_PACK_L1_91(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_90(ACT, __VA_ARGS__)
#define X_PACK_L1_92(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_91(ACT, __VA_ARGS__)
#define X_PACK_L1_93(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_92(ACT, __VA_ARGS__)
#define X_PACK_L1_94(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_93(ACT, __VA_ARGS__)
#define X_PACK_L1_95(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_94(ACT, __VA_ARGS__)
#define X_PACK_L1_96(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_95(ACT, __VA_ARGS__)
#define X_PACK_L1_97(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_96(ACT, __VA_ARGS__)
#define X_PACK_L1_98(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_97(ACT, __VA_ARGS__)
#define X_PACK_L1_99(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_98(ACT, __VA_ARGS__)
#define X_PACK_L2_DEF(ACT, ARG, M) ACT(ARG, M)
#define X_PACK_L2_1(ACT, ARG, M) X_PACK_L2_DEF(ACT, ARG, M)
#define X_PACK_L2_2(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_1(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_3(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_2(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_4(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_3(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_5(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_4(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_6(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_5(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_7(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_6(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_8(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_7(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_9(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_8(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_10(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_9(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_11(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_10(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_12(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_11(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_13(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_12(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_14(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_13(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_15(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_14(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_16(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_15(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_17(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_16(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_18(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_17(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_19(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_18(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_20(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_19(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_21(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_20(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_22(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_21(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_23(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_22(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_24(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_23(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_25(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_24(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_26(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_25(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_27(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_26(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_28(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_27(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_29(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_28(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_30(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_29(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_31(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_30(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_32(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_31(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_33(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_32(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_34(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_33(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_35(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_34(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_36(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_35(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_37(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_36(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_38(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_37(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_39(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_38(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_40(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_39(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_41(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_40(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_42(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_41(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_43(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_42(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_44(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_43(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_45(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_44(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_46(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_45(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_47(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_46(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_48(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_47(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_49(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_48(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_50(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_49(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_51(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_50(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_52(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_51(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_53(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_52(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_54(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_53(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_55(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_54(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_56(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_55(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_57(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_56(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_58(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_57(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_59(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_58(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_60(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_59(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_61(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_60(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_62(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_61(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_63(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_62(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_64(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_63(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_65(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_64(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_66(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_65(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_67(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_66(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_68(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_67(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_69(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_68(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_70(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_69(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_71(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_70(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_72(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_71(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_73(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_72(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_74(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_73(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_75(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_74(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_76(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_75(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_77(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_76(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_78(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_77(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_79(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_78(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_80(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_79(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_81(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_80(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_82(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_81(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_83(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_82(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_84(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_83(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_85(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_84(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_86(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_85(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_87(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_86(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_88(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_87(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_89(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_88(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_90(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_89(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_91(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_90(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_92(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_91(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_93(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_92(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_94(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_93(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_95(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_94(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_96(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_95(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_97(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_96(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_98(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_97(ACT, ARG, __VA_ARGS__)
#define X_PACK_L2_99(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_98(ACT, ARG, __VA_ARGS__)
// two para every time. for alias
#define X_PACK_L2_2_DEF(ACT, ARG, M, NAME) ACT(ARG, M, NAME)
#define X_PACK_L2_2_2(ACT, ARG, M, NAME) X_PACK_L2_2_DEF(ACT, ARG, M, NAME)
#define X_PACK_L2_2_4(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_2(ACT,ARG, __VA_ARGS__)
#define X_PACK_L2_2_6(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_4(ACT,ARG, __VA_ARGS__)
#define X_PACK_L2_2_8(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_6(ACT,ARG, __VA_ARGS__)
#define X_PACK_L2_2_10(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_8(ACT,ARG, __VA_ARGS__)
#define X_PACK_L2_2_12(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_10(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_14(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_12(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_16(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_14(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_18(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_16(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_20(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_18(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_22(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_20(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_24(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_22(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_26(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_24(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_28(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_26(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_30(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_28(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_32(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_30(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_34(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_32(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_36(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_34(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_38(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_36(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_40(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_38(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_42(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_40(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_44(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_42(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_46(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_44(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_48(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_46(ACT, ARG,__VA_ARGS__)
#define X_PACK_L2_2_50(ACT, ARG,M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_48(ACT, ARG,__VA_ARGS__)
#else // msvc
// thx https://stackoverflow.com/questions/5134523/msvc-doesnt-expand-va-args-correctly
// in MSVC's preprocessor, __VA_ARGS__ is treat as a normal parameter, so it will expand at last, and in gcc, it's expand at first. so we need expand it first
#define X_PACK_N(LEVEL, ACTION, ...) X_PACK_EXPAND(X_PACK_COUNT(LEVEL, ACTION, __VA_ARGS__,_99,_98,_97,_96,_95,_94,_93,_92,_91,_90,_89,_88,_87,_86,_85,_84,_83,_82,_81,_80,_79,_78,_77,_76,_75,_74,_73,_72,_71,_70,_69,_68,_67,_66,_65,_64,_63,_62,_61,_60,_59,_58,_57,_56,_55,_54,_53,_52,_51,_50,_49,_48,_47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1)) X_PACK_EXPAND((ACTION, __VA_ARGS__))
#define X_PACK_N2(LEVEL, ACTION, ARG, ...) X_PACK_EXPAND(X_PACK_COUNT(LEVEL, ACTION, __VA_ARGS__,_99,_98,_97,_96,_95,_94,_93,_92,_91,_90,_89,_88,_87,_86,_85,_84,_83,_82,_81,_80,_79,_78,_77,_76,_75,_74,_73,_72,_71,_70,_69,_68,_67,_66,_65,_64,_63,_62,_61,_60,_59,_58,_57,_56,_55,_54,_53,_52,_51,_50,_49,_48,_47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1)) X_PACK_EXPAND((ACTION, ARG, __VA_ARGS__))
#define X_PACK_L1_DEF(ACT, M) ACT(M) // here will expand to ACT(O(xxx)), ACT(A(a,x)), ACT(M(xxx))
#define X_PACK_L1_1(ACT, M) X_PACK_L1_DEF(ACT, M)
#define X_PACK_L1_2(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_1 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_3(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_2 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_4(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_3 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_5(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_4 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_6(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_5 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_7(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_6 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_8(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_7 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_9(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_8 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_10(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_9 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_11(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_10 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_12(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_11 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_13(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_12 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_14(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_13 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_15(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_14 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_16(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_15 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_17(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_16 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_18(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_17 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_19(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_18 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_20(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_19 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_21(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_20 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_22(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_21 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_23(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_22 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_24(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_23 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_25(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_24 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_26(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_25 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_27(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_26 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_28(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_27 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_29(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_28 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_30(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_29 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_31(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_30 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_32(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_31 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_33(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_32 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_34(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_33 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_35(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_34 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_36(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_35 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_37(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_36 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_38(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_37 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_39(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_38 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_40(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_39 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_41(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_40 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_42(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_41 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_43(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_42 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_44(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_43 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_45(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_44 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_46(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_45 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_47(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_46 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_48(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_47 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_49(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_48 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_50(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_49 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_51(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_50 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_52(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_51 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_53(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_52 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_54(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_53 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_55(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_54 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_56(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_55 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_57(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_56 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_58(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_57 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_59(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_58 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_60(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_59 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_61(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_60 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_62(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_61 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_63(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_62 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_64(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_63 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_65(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_64 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_66(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_65 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_67(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_66 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_68(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_67 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_69(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_68 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_70(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_69 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_71(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_70 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_72(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_71 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_73(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_72 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_74(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_73 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_75(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_74 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_76(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_75 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_77(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_76 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_78(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_77 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_79(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_78 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_80(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_79 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_81(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_80 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_82(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_81 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_83(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_82 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_84(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_83 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_85(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_84 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_86(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_85 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_87(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_86 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_88(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_87 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_89(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_88 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_90(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_89 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_91(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_90 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_92(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_91 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_93(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_92 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_94(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_93 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_95(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_94 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_96(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_95 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_97(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_96 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_98(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_97 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L1_99(ACT, M,...) X_PACK_L1_DEF(ACT, M) X_PACK_L1_98 X_PACK_EXPAND((ACT, __VA_ARGS__))
#define X_PACK_L2_DEF(ACT, ARG, M) ACT(ARG, M)
#define X_PACK_L2_1(ACT, ARG, M) X_PACK_L2_DEF(ACT, ARG, M)
#define X_PACK_L2_2(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_1 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_3(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_2 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_4(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_3 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_5(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_4 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_6(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_5 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_7(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_6 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_8(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_7 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_9(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_8 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_10(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_9 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_11(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_10 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_12(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_11 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_13(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_12 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_14(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_13 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_15(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_14 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_16(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_15 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_17(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_16 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_18(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_17 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_19(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_18 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_20(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_19 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_21(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_20 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_22(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_21 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_23(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_22 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_24(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_23 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_25(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_24 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_26(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_25 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_27(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_26 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_28(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_27 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_29(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_28 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_30(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_29 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_31(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_30 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_32(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_31 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_33(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_32 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_34(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_33 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_35(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_34 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_36(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_35 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_37(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_36 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_38(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_37 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_39(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_38 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_40(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_39 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_41(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_40 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_42(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_41 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_43(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_42 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_44(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_43 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_45(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_44 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_46(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_45 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_47(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_46 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_48(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_47 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_49(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_48 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_50(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_49 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_51(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_50 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_52(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_51 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_53(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_52 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_54(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_53 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_55(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_54 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_56(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_55 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_57(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_56 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_58(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_57 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_59(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_58 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_60(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_59 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_61(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_60 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_62(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_61 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_63(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_62 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_64(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_63 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_65(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_64 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_66(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_65 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_67(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_66 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_68(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_67 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_69(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_68 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_70(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_69 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_71(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_70 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_72(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_71 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_73(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_72 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_74(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_73 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_75(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_74 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_76(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_75 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_77(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_76 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_78(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_77 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_79(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_78 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_80(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_79 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_81(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_80 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_82(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_81 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_83(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_82 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_84(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_83 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_85(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_84 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_86(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_85 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_87(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_86 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_88(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_87 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_89(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_88 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_90(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_89 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_91(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_90 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_92(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_91 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_93(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_92 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_94(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_93 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_95(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_94 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_96(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_95 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_97(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_96 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_98(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_97 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_99(ACT, ARG, M,...) X_PACK_L2_DEF(ACT, ARG, M) X_PACK_L2_98 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
// two para every time. for alias
#define X_PACK_L2_2_DEF(ACT, ARG, M, NAME) ACT(ARG, M, NAME)
#define X_PACK_L2_2_2(ACT, ARG, M, NAME) X_PACK_L2_2_DEF(ACT, ARG, M, NAME)
#define X_PACK_L2_2_4(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_2 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_6(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_4 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_8(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_6 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_10(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_8 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_12(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_10 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_14(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_12 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_16(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_14 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_18(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_16 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_20(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_18 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_22(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_20 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_24(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_22 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_26(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_24 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_28(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_26 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_30(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_28 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_32(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_30 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_34(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_32 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_36(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_34 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_38(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_36 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_40(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_38 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_42(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_40 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_44(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_42 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_46(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_44 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_48(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_46 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#define X_PACK_L2_2_50(ACT, ARG, M, NAME,...) X_PACK_L2_2_DEF(ACT, ARG, M, NAME) X_PACK_L2_2_48 X_PACK_EXPAND((ACT, ARG, __VA_ARGS__))
#endif
#endif

44
util/xpack/mysql.h Executable file
View file

@ -0,0 +1,44 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_MYSQL_H
#define __X_PACK_MYSQL_H
#include "mysql_decoder.h"
#include "xpack.h"
namespace xpack {
class mysql {
public:
// convert MYSQL_RES to a struct or vector<struct>
template <class T>
static void decode(MYSQL_RES *result, T &val) {
MySQLDecoder de(result);
de.decode_top(val, NULL);
}
// select name from test where id = 1; // just want to get name, did not want to use a struct
template <class T>
static void decode(MYSQL_RES *result, const std::string&field, T &val) {
MySQLDecoder de(result);
de.decode_column(field.c_str(), val, NULL);
}
};
}
#endif

222
util/xpack/mysql_decoder.h Executable file
View file

@ -0,0 +1,222 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_MYSQL_DECODER_H
#define __X_PACK_MYSQL_DECODER_H
#include <stdexcept>
#include <map>
#include <vector>
#include <cstdlib>
#include <mysql/mysql.h>
#include "extend.h"
#include "traits.h"
#include "json.h"
#include "string.h"
#include "time.h"
namespace xpack {
// for mysql types not list here
// if true we will call xpack_mysql_decode(MYSQL_FIELD *field, char *column, T&val, const Extend *ext)
template <class T>
struct is_xpack_mysql_xtype {static bool const value = false;};
class MySQLDecoder {
friend class mysql;
public:
template <typename T>
bool decode(const char*key, T&val, const Extend *ext) {
int idx = find(key);
if (idx >= 0) {
return this->decode_type(idx, val, ext);
}
return false;
}
const char *Name() const {
return "db";
}
private:
MYSQL_RES *_res;
MYSQL_ROW _row;
std::map<const char*, int, cmp_str> _index;
MySQLDecoder(MYSQL_RES *result):_res(result), _row(NULL) {
mysql_data_seek(_res, 0); // reset cursor to head
for (int i=0; i<int(result->field_count); ++i) {
_index[result->fields[i].name] = i;
}
}
// decode to struct
template <class T>
inline XPACK_IS_XPACK(T) decode_top(T& val, const Extend *ext) {
if (NULL != (_row = mysql_fetch_row(_res))) {
val.__x_pack_decode(*this, val, ext);
return true;
}
return false;
}
template <class T>
inline XPACK_IS_XPACK(T) decode_top(std::vector<T>& val, const Extend *ext) {
while (NULL != (_row = mysql_fetch_row(_res))) {
val.push_back(T());
T& tmp = val.back();
tmp.__x_pack_decode(*this, tmp, ext);
}
return true;
}
template <class T>
inline XPACK_IS_XOUT(T) decode_top(T& val, const Extend *ext) {
if (NULL != (_row = mysql_fetch_row(_res))) {
__x_pack_decode_out(*this, val, ext);
}
return true;
}
template <class T>
inline XPACK_IS_XOUT(T) decode_top(std::vector<T>& val, const Extend *ext) {
while (NULL != (_row = mysql_fetch_row(_res))) {
val.push_back(T());
__x_pack_decode_out(*this, val.back(), ext);
}
return true;
}
// decode special column of first row
template <class T>
bool decode_column(const char*field, T&val, const Extend *ext) {
int idx;
if (NULL==_row && NULL == (_row = mysql_fetch_row(_res)) && (0 > (idx = find(field)))) {
return false;
}
return decode_type(idx, val, ext);
}
// decode special column of rows
template <class T>
bool decode_column(const char*field, std::vector<T>&val, const Extend *ext) {
int idx;
if (0 > (idx = find(field))) {
return false;
}
while (NULL != (_row = mysql_fetch_row(_res))) {
T t;
decode_type(idx, t, ext);
val.push_back(t);
}
return true;
}
int find(const char*field) const {
std::map<const char*, int, cmp_str>::const_iterator it = _index.find(field);
if (it != _index.end()) {
return it->second;
}
return -1;
}
// std::string
bool decode_type(const int idx, std::string &val, const Extend *ext) {
val = _row[idx];
return true;
}
// bool
bool decode_type(const int idx, bool &val, const Extend *ext) {
int tmp;
if (decode_type(idx, tmp, ext)) {
val = tmp != 0;
return true;
}
return false;
}
// unsigned integer. DATE and TIME use signed plz
template <class T>
typename x_enable_if<numeric<T>::is_integer && !numeric<T>::is_signed, bool>::type decode_type(const int idx, T &val, const Extend *ext) {
val = (T)std::strtoul(_row[idx], NULL, 10);
return true;
}
// signed integer
template <class T>
typename x_enable_if<numeric<T>::is_integer && numeric<T>::is_signed, bool>::type decode_type(const int idx, T &val, const Extend *ext) {
const char *str = _row[idx];
if (str==NULL || str[0]=='\0') {
val = 0;
return true;
}
switch (_res->fields[idx].type) {
case MYSQL_TYPE_TIME: {
std::vector<std::string> ss;
if (3 == Util::split(ss, str, ':')) {
long int s0 = std::strtol(ss[0].c_str(), NULL, 10);
long int s1 = std::strtol(ss[1].c_str(), NULL, 10);
long int s2 = std::strtol(ss[2].c_str(), NULL, 10);
if (s0 >= 0) {
val = 1;
} else {
val = -1;
s0 *= -1;
}
val *= (T)(s0*3600 + s1*60 +s2);
}
}
break;
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:{ // parse to unix timestamp, seconds, discard fractional part
tm t;
strptime(str, "%F %T", &t);
val = (T)mktime(&t);
}
break;
default:
val = (T)std::strtol(str, NULL, 10);
}
return true;
}
// float
template <class T>
typename x_enable_if<numeric<T>::is_float, bool>::type decode_type(const int idx, T &val, const Extend *ext) {
val = (T)std::strtod(_row[idx], NULL);
return true;
}
// mysql covert type
template <class T>
inline typename x_enable_if<is_xpack_mysql_xtype<T>::value, bool>::type decode_type(const int idx, T &val, const Extend *ext) {
return xpack_mysql_decode(&_res->fields[idx], _row[idx], val, ext);
}
// class/struct defined XPACK/XPACK_OUT, default use json to parse, if use other, use xpack_mysql_decode
template <class T>
inline XPACK_IS_XOUT(T) decode_type(const int idx, T &val, const Extend *ext) {
(void)ext;
return xpack::json::decode(_row[idx], val);
}
template <class T>
inline XPACK_IS_XPACK(T) decode_type(const int idx, T &val, const Extend *ext) {
(void)ext;
return xpack::json::decode(_row[idx], val);
}
};
}
#endif

69
util/xpack/numeric.h Executable file
View file

@ -0,0 +1,69 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __X_PACK_NUMERIC_H
#define __X_PACK_NUMERIC_H
namespace xpack {
template <class T>
struct numeric{static bool const value = false; static bool const is_integer = false;static bool const is_float = false;static bool const is_signed = false;};
template<>
struct numeric<char>{static bool const value = true; static bool const is_integer = true;static bool const is_signed = true;};
template<>
struct numeric<signed char>{static bool const value = true; static bool const is_integer = true;static bool const is_signed = true;};
template<>
struct numeric<unsigned char>{static bool const value = true; static bool const is_integer = true;;static bool const is_signed = false;};
template<>
struct numeric<short>{static bool const value = true; static bool const is_integer = true;static bool const is_signed = true;};
template<>
struct numeric<unsigned short>{static bool const value = true; static bool const is_integer = true;;static bool const is_signed = false;};
template<>
struct numeric<int>{static bool const value = true; static bool const is_integer = true;static bool const is_signed = true;};
template<>
struct numeric<unsigned int>{static bool const value = true; static bool const is_integer = true;;static bool const is_signed = false;};
template<>
struct numeric<long>{static bool const value = true; static bool const is_integer = true;static bool const is_signed = true;};
template<>
struct numeric<unsigned long>{static bool const value = true; static bool const is_integer = true;;static bool const is_signed = false;};
template<>
struct numeric<long long>{static bool const value = true; static bool const is_integer = true;static bool const is_signed = true;};
template<>
struct numeric<unsigned long long>{static bool const value = true; static bool const is_integer = true;;static bool const is_signed = false;};
template<>
struct numeric<float>{static bool const value = true;static bool const is_float = true;};
template<>
struct numeric<double>{static bool const value = true;static bool const is_float = true;};
template<>
struct numeric<long double>{static bool const value = true;static bool const is_float = true;};
}
#endif

284
util/xpack/rapidjson/allocators.h Executable file
View file

@ -0,0 +1,284 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ALLOCATORS_H_
#define RAPIDJSON_ALLOCATORS_H_
#include "rapidjson.h"
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Allocator
/*! \class rapidjson::Allocator
\brief Concept for allocating, resizing and freeing memory block.
Note that Malloc() and Realloc() are non-static but Free() is static.
So if an allocator need to support Free(), it needs to put its pointer in
the header of memory block.
\code
concept Allocator {
static const bool kNeedFree; //!< Whether this allocator needs to call Free().
// Allocate a memory block.
// \param size of the memory block in bytes.
// \returns pointer to the memory block.
void* Malloc(size_t size);
// Resize a memory block.
// \param originalPtr The pointer to current memory block. Null pointer is permitted.
// \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
// \param newSize the new size in bytes.
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
// Free a memory block.
// \param pointer to the memory block. Null pointer is permitted.
static void Free(void *ptr);
};
\endcode
*/
/*! \def RAPIDJSON_ALLOCATOR_DEFUALT_CHUNK_CAPACITY
\ingroup RAPIDJSON_CONFIG
\brief User-defined kDefaultChunkCapacity definition.
User can define this as any \c size that is a power of 2.
*/
#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024)
#endif
///////////////////////////////////////////////////////////////////////////////
// CrtAllocator
//! C-runtime library allocator.
/*! This class is just wrapper for standard C library memory routines.
\note implements Allocator concept
*/
class CrtAllocator {
public:
static const bool kNeedFree = true;
void* Malloc(size_t size) {
if (size) // behavior of malloc(0) is implementation defined.
return std::malloc(size);
else
return NULL; // standardize to returning NULL.
}
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
(void)originalSize;
if (newSize == 0) {
std::free(originalPtr);
return NULL;
}
return std::realloc(originalPtr, newSize);
}
static void Free(void *ptr) { std::free(ptr); }
};
///////////////////////////////////////////////////////////////////////////////
// MemoryPoolAllocator
//! Default memory allocator used by the parser and DOM.
/*! This allocator allocate memory blocks from pre-allocated memory chunks.
It does not free memory blocks. And Realloc() only allocate new memory.
The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
User may also supply a buffer as the first chunk.
If the user-buffer is full then additional chunks are allocated by BaseAllocator.
The user-buffer is not deallocated by this allocator.
\tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
\note implements Allocator concept
*/
template <typename BaseAllocator = CrtAllocator>
class MemoryPoolAllocator {
public:
static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
//! Constructor with chunkSize.
/*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
\param baseAllocator The allocator for allocating memory chunks.
*/
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
{
}
//! Constructor with user-supplied buffer.
/*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
The user buffer will not be deallocated when this allocator is destructed.
\param buffer User supplied buffer.
\param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
\param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
\param baseAllocator The allocator for allocating memory chunks.
*/
MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
{
RAPIDJSON_ASSERT(buffer != 0);
RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
chunkHead_->capacity = size - sizeof(ChunkHeader);
chunkHead_->size = 0;
chunkHead_->next = 0;
}
//! Destructor.
/*! This deallocates all memory chunks, excluding the user-supplied buffer.
*/
~MemoryPoolAllocator() {
Clear();
RAPIDJSON_DELETE(ownBaseAllocator_);
}
//! Deallocates all memory chunks, excluding the user-supplied buffer.
void Clear() {
while (chunkHead_ && chunkHead_ != userBuffer_) {
ChunkHeader* next = chunkHead_->next;
baseAllocator_->Free(chunkHead_);
chunkHead_ = next;
}
if (chunkHead_ && chunkHead_ == userBuffer_)
chunkHead_->size = 0; // Clear user buffer
}
//! Computes the total capacity of allocated memory chunks.
/*! \return total capacity in bytes.
*/
size_t Capacity() const {
size_t capacity = 0;
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
capacity += c->capacity;
return capacity;
}
//! Computes the memory blocks allocated.
/*! \return total used bytes.
*/
size_t Size() const {
size_t size = 0;
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
size += c->size;
return size;
}
//! Allocates a memory block. (concept Allocator)
void* Malloc(size_t size) {
if (!size)
return NULL;
size = RAPIDJSON_ALIGN(size);
if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size))
return NULL;
void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size;
chunkHead_->size += size;
return buffer;
}
//! Resizes a memory block (concept Allocator)
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
if (originalPtr == 0)
return Malloc(newSize);
if (newSize == 0)
return NULL;
originalSize = RAPIDJSON_ALIGN(originalSize);
newSize = RAPIDJSON_ALIGN(newSize);
// Do not shrink if new size is smaller than original
if (originalSize >= newSize)
return originalPtr;
// Simply expand it if it is the last allocation and there is sufficient space
if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
size_t increment = static_cast<size_t>(newSize - originalSize);
if (chunkHead_->size + increment <= chunkHead_->capacity) {
chunkHead_->size += increment;
return originalPtr;
}
}
// Realloc process: allocate and copy memory, do not free original buffer.
if (void* newBuffer = Malloc(newSize)) {
if (originalSize)
std::memcpy(newBuffer, originalPtr, originalSize);
return newBuffer;
}
else
return NULL;
}
//! Frees a memory block (concept Allocator)
static void Free(void *ptr) { (void)ptr; } // Do nothing
private:
//! Copy constructor is not permitted.
MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
//! Copy assignment operator is not permitted.
MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
//! Creates a new chunk.
/*! \param capacity Capacity of the chunk in bytes.
\return true if success.
*/
bool AddChunk(size_t capacity) {
if (!baseAllocator_)
ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)();
if (ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) {
chunk->capacity = capacity;
chunk->size = 0;
chunk->next = chunkHead_;
chunkHead_ = chunk;
return true;
}
else
return false;
}
static const int kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity.
//! Chunk header for perpending to each chunk.
/*! Chunks are stored as a singly linked list.
*/
struct ChunkHeader {
size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
size_t size; //!< Current size of allocated memory in bytes.
ChunkHeader *next; //!< Next chunk in the linked list.
};
ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
void *userBuffer_; //!< User supplied buffer.
BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
};
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_ENCODINGS_H_

View file

@ -0,0 +1,78 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_
#define RAPIDJSON_CURSORSTREAMWRAPPER_H_
#include "stream.h"
#if defined(__GNUC__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#if defined(_MSC_VER) && _MSC_VER <= 1800
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4702) // unreachable code
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Cursor stream wrapper for counting line and column number if error exists.
/*!
\tparam InputStream Any stream that implements Stream Concept
*/
template <typename InputStream, typename Encoding = UTF8<> >
class CursorStreamWrapper : public GenericStreamWrapper<InputStream, Encoding> {
public:
typedef typename Encoding::Ch Ch;
CursorStreamWrapper(InputStream& is):
GenericStreamWrapper<InputStream, Encoding>(is), line_(1), col_(0) {}
// counting line and column number
Ch Take() {
Ch ch = this->is_.Take();
if(ch == '\n') {
line_ ++;
col_ = 0;
} else {
col_ ++;
}
return ch;
}
//! Get the error line number, if error exists.
size_t GetLine() const { return line_; }
//! Get the error column number, if error exists.
size_t GetColumn() const { return col_; }
private:
size_t line_; //!< Current Line
size_t col_; //!< Current Column
};
#if defined(_MSC_VER) && _MSC_VER <= 1800
RAPIDJSON_DIAG_POP
#endif
#if defined(__GNUC__)
RAPIDJSON_DIAG_POP
#endif
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_

2652
util/xpack/rapidjson/document.h Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,299 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ENCODEDSTREAM_H_
#define RAPIDJSON_ENCODEDSTREAM_H_
#include "stream.h"
#include "memorystream.h"
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Input byte stream wrapper with a statically bound encoding.
/*!
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
\tparam InputByteStream Type of input byte stream. For example, FileReadStream.
*/
template <typename Encoding, typename InputByteStream>
class EncodedInputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
public:
typedef typename Encoding::Ch Ch;
EncodedInputStream(InputByteStream& is) : is_(is) {
current_ = Encoding::TakeBOM(is_);
}
Ch Peek() const { return current_; }
Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
size_t Tell() const { return is_.Tell(); }
// Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
EncodedInputStream(const EncodedInputStream&);
EncodedInputStream& operator=(const EncodedInputStream&);
InputByteStream& is_;
Ch current_;
};
//! Specialized for UTF8 MemoryStream.
template <>
class EncodedInputStream<UTF8<>, MemoryStream> {
public:
typedef UTF8<>::Ch Ch;
EncodedInputStream(MemoryStream& is) : is_(is) {
if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take();
if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take();
if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take();
}
Ch Peek() const { return is_.Peek(); }
Ch Take() { return is_.Take(); }
size_t Tell() const { return is_.Tell(); }
// Not implemented
void Put(Ch) {}
void Flush() {}
Ch* PutBegin() { return 0; }
size_t PutEnd(Ch*) { return 0; }
MemoryStream& is_;
private:
EncodedInputStream(const EncodedInputStream&);
EncodedInputStream& operator=(const EncodedInputStream&);
};
//! Output byte stream wrapper with statically bound encoding.
/*!
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
\tparam OutputByteStream Type of input byte stream. For example, FileWriteStream.
*/
template <typename Encoding, typename OutputByteStream>
class EncodedOutputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
public:
typedef typename Encoding::Ch Ch;
EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) {
if (putBOM)
Encoding::PutBOM(os_);
}
void Put(Ch c) { Encoding::Put(os_, c); }
void Flush() { os_.Flush(); }
// Not implemented
Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
EncodedOutputStream(const EncodedOutputStream&);
EncodedOutputStream& operator=(const EncodedOutputStream&);
OutputByteStream& os_;
};
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
//! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
/*!
\tparam CharType Type of character for reading.
\tparam InputByteStream type of input byte stream to be wrapped.
*/
template <typename CharType, typename InputByteStream>
class AutoUTFInputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
public:
typedef CharType Ch;
//! Constructor.
/*!
\param is input stream to be wrapped.
\param type UTF encoding type if it is not detected from the stream.
*/
AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
DetectType();
static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
takeFunc_ = f[type_];
current_ = takeFunc_(*is_);
}
UTFType GetType() const { return type_; }
bool HasBOM() const { return hasBOM_; }
Ch Peek() const { return current_; }
Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
size_t Tell() const { return is_->Tell(); }
// Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
AutoUTFInputStream(const AutoUTFInputStream&);
AutoUTFInputStream& operator=(const AutoUTFInputStream&);
// Detect encoding type with BOM or RFC 4627
void DetectType() {
// BOM (Byte Order Mark):
// 00 00 FE FF UTF-32BE
// FF FE 00 00 UTF-32LE
// FE FF UTF-16BE
// FF FE UTF-16LE
// EF BB BF UTF-8
const unsigned char* c = reinterpret_cast<const unsigned char *>(is_->Peek4());
if (!c)
return;
unsigned bom = static_cast<unsigned>(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24));
hasBOM_ = false;
if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); }
else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); }
else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); }
// RFC 4627: Section 3
// "Since the first two characters of a JSON text will always be ASCII
// characters [RFC0020], it is possible to determine whether an octet
// stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
// at the pattern of nulls in the first four octets."
// 00 00 00 xx UTF-32BE
// 00 xx 00 xx UTF-16BE
// xx 00 00 00 UTF-32LE
// xx 00 xx 00 UTF-16LE
// xx xx xx xx UTF-8
if (!hasBOM_) {
int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
switch (pattern) {
case 0x08: type_ = kUTF32BE; break;
case 0x0A: type_ = kUTF16BE; break;
case 0x01: type_ = kUTF32LE; break;
case 0x05: type_ = kUTF16LE; break;
case 0x0F: type_ = kUTF8; break;
default: break; // Use type defined by user.
}
}
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
}
typedef Ch (*TakeFunc)(InputByteStream& is);
InputByteStream* is_;
UTFType type_;
Ch current_;
TakeFunc takeFunc_;
bool hasBOM_;
};
//! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
/*!
\tparam CharType Type of character for writing.
\tparam OutputByteStream type of output byte stream to be wrapped.
*/
template <typename CharType, typename OutputByteStream>
class AutoUTFOutputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
public:
typedef CharType Ch;
//! Constructor.
/*!
\param os output stream to be wrapped.
\param type UTF encoding type.
\param putBOM Whether to write BOM at the beginning of the stream.
*/
AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
putFunc_ = f[type_];
if (putBOM)
PutBOM();
}
UTFType GetType() const { return type_; }
void Put(Ch c) { putFunc_(*os_, c); }
void Flush() { os_->Flush(); }
// Not implemented
Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
AutoUTFOutputStream(const AutoUTFOutputStream&);
AutoUTFOutputStream& operator=(const AutoUTFOutputStream&);
void PutBOM() {
typedef void (*PutBOMFunc)(OutputByteStream&);
static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
f[type_](*os_);
}
typedef void (*PutFunc)(OutputByteStream&, Ch);
OutputByteStream* os_;
UTFType type_;
PutFunc putFunc_;
};
#undef RAPIDJSON_ENCODINGS_FUNC
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_FILESTREAM_H_

716
util/xpack/rapidjson/encodings.h Executable file
View file

@ -0,0 +1,716 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ENCODINGS_H_
#define RAPIDJSON_ENCODINGS_H_
#include "rapidjson.h"
#if defined(_MSC_VER) && !defined(__clang__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data
RAPIDJSON_DIAG_OFF(4702) // unreachable code
#elif defined(__GNUC__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
RAPIDJSON_DIAG_OFF(overflow)
#endif
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Encoding
/*! \class rapidjson::Encoding
\brief Concept for encoding of Unicode characters.
\code
concept Encoding {
typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition.
enum { supportUnicode = 1 }; // or 0 if not supporting unicode
//! \brief Encode a Unicode codepoint to an output stream.
//! \param os Output stream.
//! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively.
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint);
//! \brief Decode a Unicode codepoint from an input stream.
//! \param is Input stream.
//! \param codepoint Output of the unicode codepoint.
//! \return true if a valid codepoint can be decoded from the stream.
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint);
//! \brief Validate one Unicode codepoint from an encoded stream.
//! \param is Input stream to obtain codepoint.
//! \param os Output for copying one codepoint.
//! \return true if it is valid.
//! \note This function just validating and copying the codepoint without actually decode it.
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os);
// The following functions are deal with byte streams.
//! Take a character from input byte stream, skip BOM if exist.
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is);
//! Take a character from input byte stream.
template <typename InputByteStream>
static Ch Take(InputByteStream& is);
//! Put BOM to output byte stream.
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os);
//! Put a character to output byte stream.
template <typename OutputByteStream>
static void Put(OutputByteStream& os, Ch c);
};
\endcode
*/
///////////////////////////////////////////////////////////////////////////////
// UTF8
//! UTF-8 encoding.
/*! http://en.wikipedia.org/wiki/UTF-8
http://tools.ietf.org/html/rfc3629
\tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char.
\note implements Encoding concept
*/
template<typename CharType = char>
struct UTF8 {
typedef CharType Ch;
enum { supportUnicode = 1 };
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint) {
if (codepoint <= 0x7F)
os.Put(static_cast<Ch>(codepoint & 0xFF));
else if (codepoint <= 0x7FF) {
os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
}
else if (codepoint <= 0xFFFF) {
os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
else {
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
}
template<typename OutputStream>
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
if (codepoint <= 0x7F)
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
else if (codepoint <= 0x7FF) {
PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
}
else if (codepoint <= 0xFFFF) {
PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
else {
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
}
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu)
#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70)
typename InputStream::Ch c = is.Take();
if (!(c & 0x80)) {
*codepoint = static_cast<unsigned char>(c);
return true;
}
unsigned char type = GetRange(static_cast<unsigned char>(c));
if (type >= 32) {
*codepoint = 0;
} else {
*codepoint = (0xFFu >> type) & static_cast<unsigned char>(c);
}
bool result = true;
switch (type) {
case 2: RAPIDJSON_TAIL(); return result;
case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result;
case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result;
case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
default: return false;
}
#undef RAPIDJSON_COPY
#undef RAPIDJSON_TRANS
#undef RAPIDJSON_TAIL
}
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os) {
#define RAPIDJSON_COPY() os.Put(c = is.Take())
#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70)
Ch c;
RAPIDJSON_COPY();
if (!(c & 0x80))
return true;
bool result = true;
switch (GetRange(static_cast<unsigned char>(c))) {
case 2: RAPIDJSON_TAIL(); return result;
case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result;
case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result;
case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
default: return false;
}
#undef RAPIDJSON_COPY
#undef RAPIDJSON_TRANS
#undef RAPIDJSON_TAIL
}
static unsigned char GetRange(unsigned char c) {
// Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
// With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types.
static const unsigned char type[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
};
return type[c];
}
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
typename InputByteStream::Ch c = Take(is);
if (static_cast<unsigned char>(c) != 0xEFu) return c;
c = is.Take();
if (static_cast<unsigned char>(c) != 0xBBu) return c;
c = is.Take();
if (static_cast<unsigned char>(c) != 0xBFu) return c;
c = is.Take();
return c;
}
template <typename InputByteStream>
static Ch Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
return static_cast<Ch>(is.Take());
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0xEFu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xBBu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xBFu));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, Ch c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(c));
}
};
///////////////////////////////////////////////////////////////////////////////
// UTF16
//! UTF-16 encoding.
/*! http://en.wikipedia.org/wiki/UTF-16
http://tools.ietf.org/html/rfc2781
\tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead.
\note implements Encoding concept
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
For streaming, use UTF16LE and UTF16BE, which handle endianness.
*/
template<typename CharType = wchar_t>
struct UTF16 {
typedef CharType Ch;
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
enum { supportUnicode = 1 };
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
if (codepoint <= 0xFFFF) {
RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
os.Put(static_cast<typename OutputStream::Ch>(codepoint));
}
else {
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
unsigned v = codepoint - 0x10000;
os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
os.Put(static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
}
}
template<typename OutputStream>
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
if (codepoint <= 0xFFFF) {
RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
PutUnsafe(os, static_cast<typename OutputStream::Ch>(codepoint));
}
else {
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
unsigned v = codepoint - 0x10000;
PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
PutUnsafe(os, static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
}
}
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
typename InputStream::Ch c = is.Take();
if (c < 0xD800 || c > 0xDFFF) {
*codepoint = static_cast<unsigned>(c);
return true;
}
else if (c <= 0xDBFF) {
*codepoint = (static_cast<unsigned>(c) & 0x3FF) << 10;
c = is.Take();
*codepoint |= (static_cast<unsigned>(c) & 0x3FF);
*codepoint += 0x10000;
return c >= 0xDC00 && c <= 0xDFFF;
}
return false;
}
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
typename InputStream::Ch c;
os.Put(static_cast<typename OutputStream::Ch>(c = is.Take()));
if (c < 0xD800 || c > 0xDFFF)
return true;
else if (c <= 0xDBFF) {
os.Put(c = is.Take());
return c >= 0xDC00 && c <= 0xDFFF;
}
return false;
}
};
//! UTF-16 little endian encoding.
template<typename CharType = wchar_t>
struct UTF16LE : UTF16<CharType> {
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
CharType c = Take(is);
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
}
template <typename InputByteStream>
static CharType Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
unsigned c = static_cast<uint8_t>(is.Take());
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
return static_cast<CharType>(c);
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, CharType c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
}
};
//! UTF-16 big endian encoding.
template<typename CharType = wchar_t>
struct UTF16BE : UTF16<CharType> {
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
CharType c = Take(is);
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
}
template <typename InputByteStream>
static CharType Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
return static_cast<CharType>(c);
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, CharType c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
}
};
///////////////////////////////////////////////////////////////////////////////
// UTF32
//! UTF-32 encoding.
/*! http://en.wikipedia.org/wiki/UTF-32
\tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead.
\note implements Encoding concept
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
For streaming, use UTF32LE and UTF32BE, which handle endianness.
*/
template<typename CharType = unsigned>
struct UTF32 {
typedef CharType Ch;
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
enum { supportUnicode = 1 };
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
os.Put(codepoint);
}
template<typename OutputStream>
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
PutUnsafe(os, codepoint);
}
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
Ch c = is.Take();
*codepoint = c;
return c <= 0x10FFFF;
}
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
Ch c;
os.Put(c = is.Take());
return c <= 0x10FFFF;
}
};
//! UTF-32 little endian enocoding.
template<typename CharType = unsigned>
struct UTF32LE : UTF32<CharType> {
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
CharType c = Take(is);
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
}
template <typename InputByteStream>
static CharType Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
unsigned c = static_cast<uint8_t>(is.Take());
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
return static_cast<CharType>(c);
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, CharType c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
}
};
//! UTF-32 big endian encoding.
template<typename CharType = unsigned>
struct UTF32BE : UTF32<CharType> {
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
CharType c = Take(is);
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
}
template <typename InputByteStream>
static CharType Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
return static_cast<CharType>(c);
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, CharType c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
}
};
///////////////////////////////////////////////////////////////////////////////
// ASCII
//! ASCII encoding.
/*! http://en.wikipedia.org/wiki/ASCII
\tparam CharType Code unit for storing 7-bit ASCII data. Default is char.
\note implements Encoding concept
*/
template<typename CharType = char>
struct ASCII {
typedef CharType Ch;
enum { supportUnicode = 0 };
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint) {
RAPIDJSON_ASSERT(codepoint <= 0x7F);
os.Put(static_cast<Ch>(codepoint & 0xFF));
}
template<typename OutputStream>
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
RAPIDJSON_ASSERT(codepoint <= 0x7F);
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
}
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
uint8_t c = static_cast<uint8_t>(is.Take());
*codepoint = c;
return c <= 0X7F;
}
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os) {
uint8_t c = static_cast<uint8_t>(is.Take());
os.Put(static_cast<typename OutputStream::Ch>(c));
return c <= 0x7F;
}
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
uint8_t c = static_cast<uint8_t>(Take(is));
return static_cast<Ch>(c);
}
template <typename InputByteStream>
static Ch Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
return static_cast<Ch>(is.Take());
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
(void)os;
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, Ch c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(c));
}
};
///////////////////////////////////////////////////////////////////////////////
// AutoUTF
//! Runtime-specified UTF encoding type of a stream.
enum UTFType {
kUTF8 = 0, //!< UTF-8.
kUTF16LE = 1, //!< UTF-16 little endian.
kUTF16BE = 2, //!< UTF-16 big endian.
kUTF32LE = 3, //!< UTF-32 little endian.
kUTF32BE = 4 //!< UTF-32 big endian.
};
//! Dynamically select encoding according to stream's runtime-specified UTF encoding type.
/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType().
*/
template<typename CharType>
struct AutoUTF {
typedef CharType Ch;
enum { supportUnicode = 1 };
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
template<typename OutputStream>
static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) {
typedef void (*EncodeFunc)(OutputStream&, unsigned);
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) };
(*f[os.GetType()])(os, codepoint);
}
template<typename OutputStream>
static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
typedef void (*EncodeFunc)(OutputStream&, unsigned);
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) };
(*f[os.GetType()])(os, codepoint);
}
template <typename InputStream>
static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) {
typedef bool (*DecodeFunc)(InputStream&, unsigned*);
static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) };
return (*f[is.GetType()])(is, codepoint);
}
template <typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
typedef bool (*ValidateFunc)(InputStream&, OutputStream&);
static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) };
return (*f[is.GetType()])(is, os);
}
#undef RAPIDJSON_ENCODINGS_FUNC
};
///////////////////////////////////////////////////////////////////////////////
// Transcoder
//! Encoding conversion.
template<typename SourceEncoding, typename TargetEncoding>
struct Transcoder {
//! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
template<typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) {
unsigned codepoint;
if (!SourceEncoding::Decode(is, &codepoint))
return false;
TargetEncoding::Encode(os, codepoint);
return true;
}
template<typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
unsigned codepoint;
if (!SourceEncoding::Decode(is, &codepoint))
return false;
TargetEncoding::EncodeUnsafe(os, codepoint);
return true;
}
//! Validate one Unicode codepoint from an encoded stream.
template<typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
return Transcode(is, os); // Since source/target encoding is different, must transcode.
}
};
// Forward declaration.
template<typename Stream>
inline void PutUnsafe(Stream& stream, typename Stream::Ch c);
//! Specialization of Transcoder with same source and target encoding.
template<typename Encoding>
struct Transcoder<Encoding, Encoding> {
template<typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) {
os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class.
return true;
}
template<typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class.
return true;
}
template<typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
return Encoding::Validate(is, os); // source/target encoding are the same
}
};
RAPIDJSON_NAMESPACE_END
#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__))
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_ENCODINGS_H_

74
util/xpack/rapidjson/error/en.h Executable file
View file

@ -0,0 +1,74 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ERROR_EN_H_
#define RAPIDJSON_ERROR_EN_H_
#include "error.h"
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(switch-enum)
RAPIDJSON_DIAG_OFF(covered-switch-default)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Maps error code of parsing into error message.
/*!
\ingroup RAPIDJSON_ERRORS
\param parseErrorCode Error code obtained in parsing.
\return the error message.
\note User can make a copy of this function for localization.
Using switch-case is safer for future modification of error codes.
*/
inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) {
switch (parseErrorCode) {
case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty.");
case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values.");
case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member.");
case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string.");
case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string.");
case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string.");
case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double.");
case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number.");
case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
default: return RAPIDJSON_ERROR_STRING("Unknown error.");
}
}
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_ERROR_EN_H_

Some files were not shown because too many files have changed in this diff Show more