整理代码
This commit is contained in:
commit
c7b4e178ec
364 changed files with 59691 additions and 0 deletions
57
util/AESUtil.h
Normal file
57
util/AESUtil.h
Normal 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
35
util/Base64Util.h
Normal 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
144
util/Broadcast.cpp
Normal 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
117
util/Broadcast.h
Normal 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
15
util/BuilderSet.cpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "BuilderSet.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
}
|
||||
63
util/BuilderSet.h
Normal file
63
util/BuilderSet.h
Normal 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
5
util/CMakeLists.txt
Normal 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
28
util/ClassAssistant.cpp
Normal 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
68
util/ClassAssistant.h
Normal 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
26
util/Configurable.cpp
Normal 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
55
util/Configurable.h
Normal 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
33
util/Configuration.cpp
Normal 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
140
util/Configuration.h
Normal 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
467
util/DateTimeUtils.h
Normal 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
104
util/Device.cpp
Normal 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
47
util/Device.h
Normal 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
15
util/FactoryManager.cpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "FactoryManager.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
}
|
||||
79
util/FactoryManager.h
Normal file
79
util/FactoryManager.h
Normal 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
71
util/FileUtil.h
Normal 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
125
util/FrpUtil.h
Normal 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
386
util/HTTPClient.h
Normal 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
0
util/HandlesManager.cpp
Normal file
67
util/HandlesManager.h
Normal file
67
util/HandlesManager.h
Normal 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
29
util/HexBinaryCodec.cpp
Executable 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
23
util/HexBinaryCodec.h
Executable 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
257
util/JsonUtil.h
Normal 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
25
util/Math.cpp
Normal 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
37
util/Math.h
Normal 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
37
util/Md5Util.h
Normal 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
20
util/ParallelRunner.cpp
Normal 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
35
util/ParallelRunner.h
Normal 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
|
||||
44
util/PocoConfigurationCopier.cpp
Normal file
44
util/PocoConfigurationCopier.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
23
util/PocoConfigurationCopier.h
Normal file
23
util/PocoConfigurationCopier.h
Normal 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
54
util/Service.cpp
Normal 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
31
util/Service.h
Normal 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
15
util/SimpleFactory.cpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "SimpleFactory.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
}
|
||||
20
util/SimpleFactory.h
Normal file
20
util/SimpleFactory.h
Normal 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
35
util/Subsystem.cpp
Normal 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
39
util/Subsystem.h
Normal 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
829
util/SystemInfo.cpp
Normal 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
140
util/SystemInfo.h
Normal 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
62
util/Task.cpp
Normal 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
93
util/Task.h
Normal 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
38
util/Temperature.cpp
Normal 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
22
util/Temperature.h
Normal 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
169
util/WifiUtil.h
Normal 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
203
util/linux-system-usage.hpp
Normal 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
711
util/structs/ParamStruct.h
Normal 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
3
util/util.cmake
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
add_subdirectory(util)
|
||||
include_directories(util)
|
||||
target_link_libraries(${PROJNAME} PRIVATE util)
|
||||
201
util/xpack/LICENSE
Executable file
201
util/xpack/LICENSE
Executable 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
84
util/xpack/README-bson.md
Executable 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
407
util/xpack/README-en.md
Executable 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
418
util/xpack/README.md
Executable 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
54
util/xpack/bson.h
Executable 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
588
util/xpack/bson_builder.h
Executable 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
279
util/xpack/bson_decoder.h
Executable 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
246
util/xpack/bson_encoder.h
Executable 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
450
util/xpack/bson_type.h
Executable 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
25
util/xpack/config.h
Executable 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
27
util/xpack/example/alias.cpp
Executable 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
23
util/xpack/example/bitfield.cpp
Executable 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;
|
||||
}
|
||||
40
util/xpack/example/char_array.cpp
Executable file
40
util/xpack/example/char_array.cpp
Executable 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
66
util/xpack/example/custom.cpp
Executable 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
45
util/xpack/example/encoder.cpp
Executable 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
58
util/xpack/example/enum.cpp
Executable 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
69
util/xpack/example/inherit.cpp
Executable 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;
|
||||
}
|
||||
73
util/xpack/example/json-data.cpp
Executable file
73
util/xpack/example/json-data.cpp
Executable 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
36
util/xpack/example/makefile
Executable 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
19
util/xpack/example/msvc.make
Executable 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
|
||||
70
util/xpack/example/my_decoder.cpp
Executable file
70
util/xpack/example/my_decoder.cpp
Executable 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
89
util/xpack/example/mysql.cpp
Executable 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;
|
||||
}
|
||||
61
util/xpack/example/sharedptr.cpp
Executable file
61
util/xpack/example/sharedptr.cpp
Executable 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
61
util/xpack/example/sqlite.cpp
Executable 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
118
util/xpack/example/union1.cpp
Executable 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;
|
||||
}
|
||||
37
util/xpack/example/xml-content.cpp
Executable file
37
util/xpack/example/xml-content.cpp
Executable 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
67
util/xpack/example/xml.cpp
Executable 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;
|
||||
}
|
||||
59
util/xpack/example/xpack_out.cpp
Executable file
59
util/xpack/example/xpack_out.cpp
Executable 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
121
util/xpack/example/xtype.cpp
Executable 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;
|
||||
}
|
||||
108
util/xpack/example/xtype_advance.cpp
Executable file
108
util/xpack/example/xtype_advance.cpp
Executable 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
65
util/xpack/example/yaml.cpp
Executable 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
188
util/xpack/extend.h
Executable 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
557
util/xpack/gtest/bson_test.cpp
Executable 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
167
util/xpack/gtest/gtest_stub.h
Executable 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
45
util/xpack/gtest/makefile
Executable 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
16
util/xpack/gtest/msvc.make
Executable 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
879
util/xpack/gtest/test.cpp
Executable 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
63
util/xpack/json.h
Executable 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
115
util/xpack/json_data.h
Executable 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
218
util/xpack/json_decoder.h
Executable 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
294
util/xpack/json_encoder.h
Executable 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
503
util/xpack/l1l2_expand.h
Executable 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
44
util/xpack/mysql.h
Executable 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
222
util/xpack/mysql_decoder.h
Executable 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
69
util/xpack/numeric.h
Executable 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
284
util/xpack/rapidjson/allocators.h
Executable 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_
|
||||
78
util/xpack/rapidjson/cursorstreamwrapper.h
Executable file
78
util/xpack/rapidjson/cursorstreamwrapper.h
Executable 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
2652
util/xpack/rapidjson/document.h
Executable file
File diff suppressed because it is too large
Load diff
299
util/xpack/rapidjson/encodedstream.h
Executable file
299
util/xpack/rapidjson/encodedstream.h
Executable 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
716
util/xpack/rapidjson/encodings.h
Executable 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
74
util/xpack/rapidjson/error/en.h
Executable 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
Loading…
Add table
Add a link
Reference in a new issue