659 lines
15 KiB
C++
659 lines
15 KiB
C++
#include "extract_basic.h"
|
|
#include <QVariant>
|
|
|
|
using namespace extract;
|
|
|
|
|
|
AbstractExtractor::AbstractExtractor(const QString& name, DataType data)
|
|
{
|
|
_abs_data.name_store = name;
|
|
_abs_data.type_store = data;
|
|
}
|
|
|
|
bool AbstractExtractor::setOffsetSpan(int bytes)
|
|
{
|
|
this->_abs_data.byte_offset = bytes;
|
|
return true;
|
|
}
|
|
|
|
bool AbstractExtractor::setCountWithin(int bytes)
|
|
{
|
|
this->_abs_data.byte_count = bytes;
|
|
return true;
|
|
}
|
|
|
|
QString AbstractExtractor::name() const
|
|
{
|
|
return _abs_data.name_store;
|
|
}
|
|
|
|
DataType AbstractExtractor::outType() const
|
|
{
|
|
return _abs_data.type_store;
|
|
}
|
|
|
|
int AbstractExtractor::offsetSpan() const
|
|
{
|
|
return _abs_data.byte_offset;
|
|
}
|
|
|
|
int AbstractExtractor::countWithin() const
|
|
{
|
|
return this->_abs_data.byte_count;
|
|
}
|
|
|
|
void AbstractExtractor::loadFrom(
|
|
std::shared_ptr<TranslateBasic> core, const QJsonObject& obj)
|
|
{
|
|
INT32_PEAK(_abs_data.byte_offset, obj);
|
|
INT32_PEAK(_abs_data.byte_count, obj);
|
|
}
|
|
|
|
void AbstractExtractor::saveTo(QJsonObject& obj) const
|
|
{
|
|
INT32_SAVE(_abs_data.byte_offset, obj);
|
|
INT32_SAVE(_abs_data.byte_count, obj);
|
|
}
|
|
|
|
void AbstractExtractor::registSubField(std::shared_ptr<ScopeFieldsSetter> inst)
|
|
{
|
|
|
|
}
|
|
|
|
BytesAsHex::BytesAsHex()
|
|
: AbstractExtractor(NAME(BytesAsHex), DataType::TextString) {
|
|
setCountWithin(1);
|
|
}
|
|
|
|
#include "TranslateBasic.h"
|
|
void BytesAsHex::parse(const QByteArray& bytes, std::shared_ptr<DataAccessContext> out) const
|
|
{
|
|
QString result;
|
|
for (auto char_v : bytes) {
|
|
result += QString("%1").arg(char_v, 2, 16, QChar('0'));
|
|
}
|
|
out->append(result);
|
|
}
|
|
|
|
std::shared_ptr<Serializable> BytesAsHex::newDefault() const
|
|
{
|
|
return std::make_shared<BytesAsHex>();
|
|
}
|
|
|
|
BytesAsBitCombine::BytesAsBitCombine()
|
|
: AbstractExtractor(NAME(BytesAsBitCombine), DataType::TextString)
|
|
{
|
|
|
|
}
|
|
|
|
bool BytesAsBitCombine::setSwitchOption(int bit_index, const QString& keyword)
|
|
{
|
|
if (bit_index >= 0 && bit_index <= this->countWithin() * 8) {
|
|
_combine._switch_options[bit_index] = keyword;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QHash<int, QString> BytesAsBitCombine::switchOptions() const
|
|
{
|
|
return _combine._switch_options;
|
|
}
|
|
|
|
void BytesAsBitCombine::clearOptions()
|
|
{
|
|
_combine._switch_options.clear();
|
|
}
|
|
|
|
void BytesAsBitCombine::parse(const QByteArray& bytes, std::shared_ptr<DataAccessContext> out) const
|
|
{
|
|
auto keys = _combine._switch_options.keys();
|
|
std::sort(keys.begin(), keys.end());
|
|
|
|
QString result;
|
|
for (auto idx : keys) {
|
|
auto byte_idx = idx / 8;
|
|
auto bit_idx = idx % 8;
|
|
auto char_v = bytes.at(byte_idx);
|
|
if (char_v & (0x1u << bit_idx))
|
|
result += QString("%1<%2>;").arg(_combine._switch_options[idx], "Y");
|
|
else
|
|
result += QString("%1<%2>;").arg(_combine._switch_options[idx], "N");
|
|
}
|
|
out->append(result);
|
|
}
|
|
|
|
#include <QJsonArray>
|
|
void BytesAsBitCombine::loadFrom(
|
|
std::shared_ptr<TranslateBasic> core, const QJsonObject& obj)
|
|
{
|
|
AbstractExtractor::loadFrom(core, obj);
|
|
|
|
_combine._switch_options.clear();
|
|
QJsonArray arr;
|
|
ARRAY_PEAK(arr, obj);
|
|
for (auto index = 0; index < arr.size(); ++index) {
|
|
auto switch_obj = arr.at(index);
|
|
auto key = switch_obj["index"].toInt();
|
|
auto content = switch_obj["content"].toString();
|
|
_combine._switch_options[key] = content;
|
|
}
|
|
}
|
|
|
|
void BytesAsBitCombine::saveTo(QJsonObject& obj) const
|
|
{
|
|
AbstractExtractor::saveTo(obj);
|
|
|
|
QJsonArray arr;
|
|
for (auto key : _combine._switch_options.keys()) {
|
|
QJsonObject switch_obj;
|
|
switch_obj["index"] = key;
|
|
switch_obj["content"] = _combine._switch_options[key];
|
|
arr.append(switch_obj);
|
|
}
|
|
ARRAY_SAVE(arr, obj);
|
|
}
|
|
|
|
std::shared_ptr<Serializable> BytesAsBitCombine::newDefault() const
|
|
{
|
|
return std::make_shared<BytesAsBitCombine>();
|
|
}
|
|
|
|
std::shared_ptr<Serializable> BytesAsInteger::newDefault() const
|
|
{
|
|
return std::make_shared<BytesAsInteger>();
|
|
}
|
|
|
|
BytesAsInteger::BytesAsInteger()
|
|
:AbstractExtractor(NAME(BytesAsInteger), DataType::Integer) {
|
|
}
|
|
|
|
bool BytesAsInteger::setCountWithin(int bytes)
|
|
{
|
|
bytes = std::min(8, std::max(bytes, 0));
|
|
return AbstractExtractor::setCountWithin(bytes);
|
|
}
|
|
|
|
void BytesAsInteger::parse(const QByteArray& bytes, std::shared_ptr<DataAccessContext> out) const
|
|
{
|
|
auto last = bytes[bytes.size() - 1];
|
|
auto mark = last == 0 ? 0 : last / std::abs(last);
|
|
|
|
auto xbuffer = bytes;
|
|
if (mark >= 0) {
|
|
xbuffer.append(8 - bytes.size(), 0);
|
|
}
|
|
else {
|
|
xbuffer.append(8 - bytes.size(), static_cast<char>(0xff));
|
|
}
|
|
|
|
unsigned long long value = 0;
|
|
for (auto vidx = xbuffer.size() - 1; vidx >= 0; vidx--) {
|
|
auto vchar = xbuffer.at(vidx);
|
|
value = value << 8;
|
|
value += (uchar)vchar;
|
|
}
|
|
|
|
out->append(*((long long*)(&value)));
|
|
}
|
|
|
|
#include <QTextCodec>
|
|
|
|
BytesAsString::BytesAsString()
|
|
:AbstractExtractor(NAME(BytesAsString), DataType::TextString) {
|
|
_strings._conv_with = QTextCodec::codecForName("GBK");
|
|
}
|
|
|
|
void BytesAsString::setStrCodec(QTextCodec* ins)
|
|
{
|
|
this->_strings._conv_with = ins;
|
|
}
|
|
|
|
QString BytesAsString::codecName() const
|
|
{
|
|
return this->_strings._conv_with->name();
|
|
}
|
|
|
|
void BytesAsString::parse(const QByteArray& bytes, std::shared_ptr<DataAccessContext> out) const
|
|
{
|
|
auto v = _strings._conv_with->toUnicode(bytes);
|
|
out->append(v);
|
|
}
|
|
|
|
void BytesAsString::loadFrom(std::shared_ptr<TranslateBasic> core, const QJsonObject& obj)
|
|
{
|
|
AbstractExtractor::loadFrom(core, obj);
|
|
|
|
QString codec_name;
|
|
STRING_PEAK(codec_name, obj);
|
|
this->_strings._conv_with = QTextCodec::codecForName(codec_name.toLatin1());
|
|
}
|
|
|
|
void BytesAsString::saveTo(QJsonObject& obj) const
|
|
{
|
|
AbstractExtractor::saveTo(obj);
|
|
|
|
auto codec_name = this->codecName();
|
|
STRING_SAVE(codec_name, obj);
|
|
}
|
|
|
|
std::shared_ptr<Serializable> BytesAsString::newDefault() const
|
|
{
|
|
return std::make_shared<BytesAsString>();
|
|
}
|
|
|
|
void BytesAsFloat::parse(const QByteArray& bytes, std::shared_ptr<DataAccessContext> out) const
|
|
{
|
|
out->append(*((float*)bytes.data()));
|
|
}
|
|
|
|
std::shared_ptr<Serializable> BytesAsFloat::newDefault() const
|
|
{
|
|
return std::make_shared<BytesAsFloat>();
|
|
}
|
|
|
|
BytesAsFloat::BytesAsFloat()
|
|
:AbstractExtractor(NAME(BytesAsFloat), DataType::Flt32)
|
|
{
|
|
this->setCountWithin(4);
|
|
}
|
|
|
|
std::shared_ptr<Serializable> BytesAsDouble::newDefault() const
|
|
{
|
|
return std::make_shared<BytesAsDouble>();
|
|
}
|
|
|
|
void BytesAsDouble::parse(const QByteArray& bytes, std::shared_ptr<DataAccessContext> out) const
|
|
{
|
|
out->append(*((double*)bytes.data()));
|
|
}
|
|
|
|
BytesAsDouble::BytesAsDouble()
|
|
:AbstractExtractor(NAME(BytesAsDouble), DataType::Dbl64)
|
|
{
|
|
this->setCountWithin(8);
|
|
}
|
|
|
|
BytesAsUnsigned::BytesAsUnsigned()
|
|
:AbstractExtractor(NAME(BytesAsUnsigned), DataType::Unsigned)
|
|
{
|
|
|
|
}
|
|
|
|
bool BytesAsUnsigned::setCountWithin(int bytes)
|
|
{
|
|
auto count = std::max(1, std::min(8, bytes));
|
|
return AbstractExtractor::setCountWithin(count);
|
|
}
|
|
|
|
void BytesAsUnsigned::parse(const QByteArray& bytes, std::shared_ptr<DataAccessContext> out) const
|
|
{
|
|
unsigned long long value = 0;
|
|
for (auto vidx = bytes.size() - 1; vidx >= 0; vidx--) {
|
|
auto vchar = bytes.at(vidx);
|
|
value = value << 8;
|
|
value += (uchar)vchar;
|
|
}
|
|
out->append(value);
|
|
}
|
|
|
|
std::shared_ptr<Serializable> BytesAsUnsigned::newDefault() const
|
|
{
|
|
return std::make_shared<BytesAsUnsigned>();
|
|
}
|
|
|
|
#include <stdexcept>
|
|
|
|
bool BytesAsList::setOffsetSpan(int value)
|
|
{
|
|
this->_list._bytes_offset = value;
|
|
return true;
|
|
}
|
|
|
|
std::shared_ptr<ExtractUnit> BytesAsList::elementRule() const
|
|
{
|
|
return this->_list._bind_unit;
|
|
}
|
|
|
|
bool BytesAsList::appendElementRule(std::shared_ptr<ExtractUnit> u)
|
|
{
|
|
this->_list._bind_unit = u;
|
|
return true;
|
|
}
|
|
|
|
std::shared_ptr<SizeProvider> BytesAsList::sizeProvider() const
|
|
{
|
|
return this->_list._bind_size_v;
|
|
}
|
|
|
|
bool BytesAsList::setSizeProvider(std::shared_ptr<SizeProvider> ins)
|
|
{
|
|
this->_list._bind_size_v = ins;
|
|
return true;
|
|
}
|
|
|
|
QString BytesAsList::name() const
|
|
{
|
|
return NAME(BytesAsList);
|
|
}
|
|
|
|
DataType BytesAsList::outType() const
|
|
{
|
|
return DataType::LIST_COLLECTION;
|
|
}
|
|
|
|
int BytesAsList::offsetSpan() const
|
|
{
|
|
return _list._bytes_offset;
|
|
}
|
|
|
|
int BytesAsList::countWithin() const
|
|
{
|
|
auto expr = this->_list._bind_size_v->expression();
|
|
if (this->_list._bind_size_v->value(expr) < 0) {
|
|
return 0;
|
|
}
|
|
|
|
return this->_list._bind_size_v->value(expr)
|
|
* this->_list._bind_unit->countWithin();
|
|
}
|
|
|
|
#include "TranslateBasic.h"
|
|
void BytesAsList::loadFrom(std::shared_ptr<TranslateBasic> core, const QJsonObject& obj)
|
|
{
|
|
INT32_PEAK(_list._bytes_offset, obj);
|
|
|
|
QString bind_unit_name = "";
|
|
QString bind_size_provider_name = "";
|
|
STRING_PEAK(bind_unit_name, obj);
|
|
STRING_PEAK(bind_size_provider_name, obj);
|
|
auto xdev = core->sizeProviderList()[bind_size_provider_name]->newDefault();
|
|
this->_list._bind_size_v = std::dynamic_pointer_cast<SizeProvider>(xdev);
|
|
xdev = core->extractUnitList()[bind_unit_name]->newDefault();
|
|
this->_list._bind_unit = std::dynamic_pointer_cast<ExtractUnit>(xdev);
|
|
|
|
QString bind_provider_expr = "";
|
|
STRING_SAVE(bind_provider_expr, obj);
|
|
this->_list._bind_size_v->setExpression(bind_provider_expr);
|
|
|
|
QJsonObject unit_obj;
|
|
QJsonObject size_provider_obj;
|
|
OBJECT_PEAK(unit_obj, obj);
|
|
OBJECT_PEAK(size_provider_obj, obj);
|
|
|
|
this->_list._bind_size_v->loadFrom(core, size_provider_obj);
|
|
this->_list._bind_unit->loadFrom(core, unit_obj);
|
|
}
|
|
|
|
#include <QJsonArray>
|
|
void BytesAsList::saveTo(QJsonObject& obj) const
|
|
{
|
|
INT32_SAVE(_list._bytes_offset, obj);
|
|
|
|
QString bind_unit_name = "";
|
|
QJsonObject unit_obj;
|
|
if (this->_list._bind_unit) {
|
|
bind_unit_name = this->_list._bind_unit->name();
|
|
this->_list._bind_unit->saveTo(unit_obj);
|
|
}
|
|
STRING_SAVE(bind_unit_name, obj);
|
|
OBJECT_SAVE(unit_obj, obj);
|
|
|
|
QString bind_size_provider_name = "";
|
|
QString bind_provider_expr = "";
|
|
QJsonObject size_provider_obj;
|
|
if (this->_list._bind_size_v) {
|
|
bind_provider_expr = this->_list._bind_size_v->expression();
|
|
bind_size_provider_name = this->_list._bind_size_v->name();
|
|
this->_list._bind_size_v->saveTo(size_provider_obj);
|
|
}
|
|
STRING_SAVE(bind_size_provider_name, obj);
|
|
STRING_SAVE(bind_provider_expr, obj);
|
|
OBJECT_SAVE(size_provider_obj, obj);
|
|
}
|
|
|
|
void BytesAsList::parse(const QByteArray& bytes, std::shared_ptr<DataAccessContext> out) const
|
|
{
|
|
auto expr = this->_list._bind_size_v->expression();
|
|
auto size_value = this->_list._bind_size_v->value(expr);
|
|
auto unit_size = this->_list._bind_unit->countWithin();
|
|
|
|
for (auto idx = 0; idx < size_value; ++idx) {
|
|
auto secn = bytes.mid(unit_size * idx, unit_size);
|
|
|
|
auto slice_context = std::make_shared<ValueAccess>();
|
|
slice_context->init(QString("ls<%1>").arg(idx), out);
|
|
this->_list._bind_unit->parse(secn, slice_context);
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Serializable> BytesAsList::newDefault() const
|
|
{
|
|
return std::make_shared<BytesAsList>();
|
|
}
|
|
|
|
void BytesAsList::registSubField(std::shared_ptr<ScopeFieldsSetter> inst)
|
|
{
|
|
inst->setField("ls", this->_list._bind_unit->outType());
|
|
auto layer = std::make_shared<FieldSetterLayer>("ls", inst);
|
|
this->_list._bind_unit->registSubField(layer);
|
|
}
|
|
|
|
QString BytesAsUnion::name() const
|
|
{
|
|
return NAME(BytesAsUnion);
|
|
}
|
|
|
|
DataType BytesAsUnion::outType() const
|
|
{
|
|
return DataType::UNION_COMBINATE;
|
|
}
|
|
|
|
int BytesAsUnion::offsetSpan() const
|
|
{
|
|
return _union.byte_offset;
|
|
}
|
|
|
|
int BytesAsUnion::countWithin() const
|
|
{
|
|
return _union.byte_count;
|
|
}
|
|
|
|
void BytesAsUnion::parse(const QByteArray& bytes, std::shared_ptr<DataAccessContext> out) const
|
|
{
|
|
for (auto u : this->elementRules())
|
|
if (u->checkpass(bytes)) {
|
|
auto enum_context = std::make_shared<ValueAccess>();
|
|
enum_context->init(u->name(), out);
|
|
u->bindRule()->parse(bytes, enum_context);
|
|
}
|
|
}
|
|
|
|
void BytesAsUnion::loadFrom(
|
|
std::shared_ptr<TranslateBasic> core, const QJsonObject& obj)
|
|
{
|
|
INT32_PEAK(_union.byte_count, obj);
|
|
INT32_PEAK(_union.byte_offset, obj);
|
|
|
|
QJsonArray array;
|
|
ARRAY_PEAK(array, obj);
|
|
this->_union._rule_list.clear();
|
|
for (auto index = 0; index < array.size(); ++index) {
|
|
auto rule_obj = array.at(index);
|
|
|
|
QString match_name;
|
|
STRING_PEAK(match_name, rule_obj);
|
|
QJsonObject match_obj;
|
|
OBJECT_PEAK(match_obj, rule_obj);
|
|
|
|
auto match = core->ruleMatchList()[match_name];
|
|
auto nmatch = match->newDefault();
|
|
nmatch->loadFrom(core, match_obj);
|
|
|
|
this->_union._rule_list.append(
|
|
std::dynamic_pointer_cast<RuleMatch>(nmatch));
|
|
}
|
|
}
|
|
|
|
void BytesAsUnion::saveTo(QJsonObject& obj) const
|
|
{
|
|
INT32_SAVE(_union.byte_count, obj);
|
|
INT32_SAVE(_union.byte_offset, obj);
|
|
|
|
QJsonArray array;
|
|
for (auto insr : this->_union._rule_list) {
|
|
QJsonObject rule_obj;
|
|
auto match_name = insr->name();
|
|
STRING_SAVE(match_name, rule_obj);
|
|
|
|
QJsonObject match_obj;
|
|
insr->saveTo(match_obj);
|
|
OBJECT_SAVE(match_obj, rule_obj);
|
|
|
|
array.append(rule_obj);
|
|
}
|
|
ARRAY_SAVE(array, obj);
|
|
}
|
|
|
|
std::shared_ptr<Serializable> BytesAsUnion::newDefault() const
|
|
{
|
|
return std::make_shared<BytesAsUnion>();
|
|
}
|
|
|
|
QList<std::shared_ptr<RuleMatch>> BytesAsUnion::elementRules() const
|
|
{
|
|
return _union._rule_list;
|
|
}
|
|
|
|
bool BytesAsUnion::appendElementRule(std::shared_ptr<RuleMatch> u)
|
|
{
|
|
this->_union._rule_list << u;
|
|
return true;
|
|
}
|
|
|
|
void BytesAsUnion::clearRules()
|
|
{
|
|
this->_union._rule_list.clear();
|
|
}
|
|
|
|
bool BytesAsUnion::setCountWithin(int bytes)
|
|
{
|
|
this->_union.byte_count = bytes;
|
|
return true;
|
|
}
|
|
|
|
bool BytesAsUnion::setOffsetSpan(int value)
|
|
{
|
|
this->_union.byte_offset = value;
|
|
return true;
|
|
}
|
|
|
|
void BytesAsUnion::registSubField(std::shared_ptr<ScopeFieldsSetter> inst)
|
|
{
|
|
for (auto rl : this->_union._rule_list) {
|
|
inst->setField(rl->name(), rl->bindRule()->outType());
|
|
auto layer = std::make_shared<FieldSetterLayer>(rl->name(), inst);
|
|
rl->bindRule()->registSubField(layer);
|
|
}
|
|
}
|
|
|
|
void BytesAsRuleSet::registSubField(std::shared_ptr<ScopeFieldsSetter> inst)
|
|
{
|
|
for (auto rl_key : this->_rules._sub_rules.keys()){
|
|
auto rlinst = this->_rules._sub_rules[rl_key];
|
|
inst->setField(rl_key, rlinst->outType());
|
|
auto layer = std::make_shared<FieldSetterLayer>(rl_key, inst);
|
|
rlinst->registSubField(layer);
|
|
}
|
|
}
|
|
|
|
void BytesAsRuleSet::parse(const QByteArray& bytes, std::shared_ptr<DataAccessContext> out) const
|
|
{
|
|
auto bufx = bytes;
|
|
for (auto keym : this->_rules._sub_rules.keys()) {
|
|
auto rule_context = std::make_shared<ValueAccess>();
|
|
rule_context->init(keym, out);
|
|
|
|
auto rule = this->_rules._sub_rules[keym];
|
|
bufx = bufx.mid(rule->offsetSpan());
|
|
auto count = rule->countWithin();
|
|
auto secx = bufx.mid(0, count);
|
|
rule->parse(secx, rule_context);
|
|
bufx = bufx.mid(count);
|
|
}
|
|
}
|
|
|
|
QString BytesAsRuleSet::name() const
|
|
{
|
|
return NAME(BytesAsRuleSet);
|
|
}
|
|
|
|
DataType BytesAsRuleSet::outType() const
|
|
{
|
|
return DataType::SUB_RULE;
|
|
}
|
|
|
|
bool BytesAsRuleSet::setOffsetSpan(int bytes)
|
|
{
|
|
this->_rules._byte_offset = bytes;
|
|
return true;
|
|
}
|
|
|
|
int BytesAsRuleSet::offsetSpan() const
|
|
{
|
|
return this->_rules._byte_offset;
|
|
}
|
|
|
|
int BytesAsRuleSet::countWithin() const
|
|
{
|
|
return this->_rules._byte_count;
|
|
}
|
|
|
|
void BytesAsRuleSet::loadFrom(std::shared_ptr<TranslateBasic> core, const QJsonObject& obj)
|
|
{
|
|
INT32_PEAK(_rules._byte_count, obj);
|
|
INT32_PEAK(_rules._byte_offset, obj);
|
|
|
|
QJsonArray array;
|
|
ARRAY_SAVE(array, obj);
|
|
|
|
for (auto index = 0; index < array.size(); ++index) {
|
|
auto rule_obj = array.at(index);
|
|
QString match_name, key;
|
|
QJsonObject match_obj;
|
|
STRING_PEAK(match_name, rule_obj);
|
|
STRING_PEAK(key, rule_obj);
|
|
OBJECT_PEAK(match_obj, rule_obj);
|
|
|
|
auto xdev = core->extractUnitList()[match_name]->newDefault();
|
|
xdev->loadFrom(core, match_obj);
|
|
_rules._sub_rules[key] = std::dynamic_pointer_cast<ExtractUnit>(xdev);
|
|
}
|
|
}
|
|
|
|
void BytesAsRuleSet::saveTo(QJsonObject& obj) const
|
|
{
|
|
INT32_SAVE(_rules._byte_count, obj);
|
|
INT32_SAVE(_rules._byte_offset, obj);
|
|
|
|
QJsonArray array;
|
|
for (auto key : _rules._sub_rules.keys()) {
|
|
QJsonObject rule_obj, match_obj;
|
|
|
|
auto info = _rules._sub_rules[key];
|
|
STRING_SAVE(key, rule_obj);
|
|
QString match_name = info->name();
|
|
STRING_SAVE(match_name, rule_obj);
|
|
info->saveTo(match_obj);
|
|
OBJECT_SAVE(match_obj, rule_obj);
|
|
|
|
array.append(rule_obj);
|
|
}
|
|
ARRAY_SAVE(array, obj);
|
|
}
|
|
|
|
std::shared_ptr<Serializable> BytesAsRuleSet::newDefault() const
|
|
{
|
|
return std::make_shared<BytesAsRuleSet>();
|
|
}
|