mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-23 12:35:50 +00:00
* Clean STBase-derived class creation interfaces * Annotate overriden STBase virtual functions * Optimize path deserialization * Prefer range-based for * Prefer std::unique_ptr * Remove BOOST_FOREACH
909 lines
22 KiB
C++
909 lines
22 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#include <BeastConfig.h>
|
|
#include <ripple/basics/Log.h>
|
|
#include <ripple/json/json_reader.h>
|
|
#include <ripple/json/to_string.h>
|
|
#include <ripple/protocol/STBase.h>
|
|
#include <ripple/protocol/STAccount.h>
|
|
#include <ripple/protocol/STArray.h>
|
|
#include <ripple/protocol/STObject.h>
|
|
#include <ripple/protocol/STParsedJSON.h>
|
|
#include <beast/module/core/text/LexicalCast.h>
|
|
#include <beast/cxx14/memory.h> // <memory>
|
|
|
|
namespace ripple {
|
|
|
|
std::unique_ptr<STBase>
|
|
STObject::makeDefaultObject (SerializedTypeID id, SField::ref name)
|
|
{
|
|
assert ((id == STI_NOTPRESENT) || (id == name.fieldType));
|
|
|
|
switch (id)
|
|
{
|
|
case STI_NOTPRESENT:
|
|
return std::make_unique <STBase> (name);
|
|
|
|
case STI_UINT8:
|
|
return std::make_unique <STUInt8> (name);
|
|
|
|
case STI_UINT16:
|
|
return std::make_unique <STUInt16> (name);
|
|
|
|
case STI_UINT32:
|
|
return std::make_unique <STUInt32> (name);
|
|
|
|
case STI_UINT64:
|
|
return std::make_unique <STUInt64> (name);
|
|
|
|
case STI_AMOUNT:
|
|
return std::make_unique <STAmount> (name);
|
|
|
|
case STI_HASH128:
|
|
return std::make_unique <STHash128> (name);
|
|
|
|
case STI_HASH160:
|
|
return std::make_unique <STHash160> (name);
|
|
|
|
case STI_HASH256:
|
|
return std::make_unique <STHash256> (name);
|
|
|
|
case STI_VECTOR256:
|
|
return std::make_unique <STVector256> (name);
|
|
|
|
case STI_VL:
|
|
return std::make_unique <STBlob> (name);
|
|
|
|
case STI_ACCOUNT:
|
|
return std::make_unique <STAccount> (name);
|
|
|
|
case STI_PATHSET:
|
|
return std::make_unique <STPathSet> (name);
|
|
|
|
case STI_OBJECT:
|
|
return std::make_unique <STObject> (name);
|
|
|
|
case STI_ARRAY:
|
|
return std::make_unique <STArray> (name);
|
|
|
|
default:
|
|
WriteLog (lsFATAL, STObject) <<
|
|
"Object type: " << beast::lexicalCast <std::string> (id);
|
|
assert (false);
|
|
throw std::runtime_error ("Unknown object type");
|
|
}
|
|
}
|
|
|
|
// VFALCO TODO Remove the 'depth' parameter
|
|
std::unique_ptr<STBase>
|
|
STObject::makeDeserializedObject (SerializedTypeID id, SField::ref name,
|
|
SerializerIterator& sit, int depth)
|
|
{
|
|
switch (id)
|
|
{
|
|
case STI_NOTPRESENT:
|
|
return STBase::deserialize (name);
|
|
|
|
case STI_UINT8:
|
|
return STUInt8::deserialize (sit, name);
|
|
|
|
case STI_UINT16:
|
|
return STUInt16::deserialize (sit, name);
|
|
|
|
case STI_UINT32:
|
|
return STUInt32::deserialize (sit, name);
|
|
|
|
case STI_UINT64:
|
|
return STUInt64::deserialize (sit, name);
|
|
|
|
case STI_AMOUNT:
|
|
return STAmount::deserialize (sit, name);
|
|
|
|
case STI_HASH128:
|
|
return STHash128::deserialize (sit, name);
|
|
|
|
case STI_HASH160:
|
|
return STHash160::deserialize (sit, name);
|
|
|
|
case STI_HASH256:
|
|
return STHash256::deserialize (sit, name);
|
|
|
|
case STI_VECTOR256:
|
|
return STVector256::deserialize (sit, name);
|
|
|
|
case STI_VL:
|
|
return STBlob::deserialize (sit, name);
|
|
|
|
case STI_ACCOUNT:
|
|
return STAccount::deserialize (sit, name);
|
|
|
|
case STI_PATHSET:
|
|
return STPathSet::deserialize (sit, name);
|
|
|
|
case STI_ARRAY:
|
|
return STArray::deserialize (sit, name);
|
|
|
|
case STI_OBJECT:
|
|
return STObject::deserialize (sit, name);
|
|
|
|
default:
|
|
throw std::runtime_error ("Unknown object type");
|
|
}
|
|
}
|
|
|
|
void STObject::set (const SOTemplate& type)
|
|
{
|
|
mData.clear ();
|
|
mType = &type;
|
|
|
|
for (SOTemplate::value_type const& elem : type.peek ())
|
|
{
|
|
if (elem->flags != SOE_REQUIRED)
|
|
giveObject (makeNonPresentObject (elem->e_field));
|
|
else
|
|
giveObject (makeDefaultObject (elem->e_field));
|
|
}
|
|
}
|
|
|
|
bool STObject::setType (const SOTemplate& type)
|
|
{
|
|
boost::ptr_vector<STBase> newData (type.peek ().size ());
|
|
bool valid = true;
|
|
|
|
mType = &type;
|
|
|
|
STBase** array = mData.c_array();
|
|
std::size_t count = mData.size ();
|
|
|
|
for (auto const& elem : type.peek ())
|
|
{
|
|
// Loop through all the fields in the template
|
|
bool match = false;
|
|
|
|
for (std::size_t i = 0; i < count; ++i)
|
|
if ((array[i] != nullptr) &&
|
|
(array[i]->getFName () == elem->e_field))
|
|
{
|
|
// matching entry in the object, move to new vector
|
|
match = true;
|
|
|
|
if ((elem->flags == SOE_DEFAULT) && array[i]->isDefault ())
|
|
{
|
|
WriteLog (lsWARNING, STObject) <<
|
|
"setType( " << getFName ().getName () <<
|
|
") invalid default " << elem->e_field.fieldName;
|
|
valid = false;
|
|
}
|
|
|
|
newData.push_back (array[i]);
|
|
array[i] = nullptr;
|
|
break;
|
|
}
|
|
|
|
if (!match)
|
|
{
|
|
// no match found in the object for an entry in the template
|
|
if (elem->flags == SOE_REQUIRED)
|
|
{
|
|
WriteLog (lsWARNING, STObject) <<
|
|
"setType( " << getFName ().getName () <<
|
|
") invalid missing " << elem->e_field.fieldName;
|
|
valid = false;
|
|
}
|
|
|
|
// Make a default object
|
|
newData.push_back (makeNonPresentObject (elem->e_field).release ());
|
|
}
|
|
}
|
|
|
|
for (std::size_t i = 0; i < count; ++i)
|
|
{
|
|
// Anything left over in the object must be discardable
|
|
if ((array[i] != nullptr) && !array[i]->getFName ().isDiscardable ())
|
|
{
|
|
WriteLog (lsWARNING, STObject) <<
|
|
"setType( " << getFName ().getName () <<
|
|
") invalid leftover " << array[i]->getFName ().getName ();
|
|
valid = false;
|
|
}
|
|
}
|
|
|
|
// Swap the template matching data in for the old data,
|
|
// freeing any leftover junk
|
|
mData.swap (newData);
|
|
|
|
return valid;
|
|
}
|
|
|
|
bool STObject::isValidForType ()
|
|
{
|
|
boost::ptr_vector<STBase>::iterator it = mData.begin ();
|
|
|
|
for (SOTemplate::value_type const& elem : mType->peek ())
|
|
{
|
|
if (it == mData.end ())
|
|
return false;
|
|
|
|
if (elem->e_field != it->getFName ())
|
|
return false;
|
|
|
|
++it;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool STObject::isFieldAllowed (SField::ref field)
|
|
{
|
|
if (mType == nullptr)
|
|
return true;
|
|
|
|
return mType->getIndex (field) != -1;
|
|
}
|
|
|
|
// return true = terminated with end-of-object
|
|
bool STObject::set (SerializerIterator& sit, int depth)
|
|
{
|
|
bool reachedEndOfObject = false;
|
|
|
|
// Empty the destination buffer
|
|
//
|
|
mData.clear ();
|
|
|
|
// Consume data in the pipe until we run out or reach the end
|
|
//
|
|
while (!reachedEndOfObject && !sit.empty ())
|
|
{
|
|
int type;
|
|
int field;
|
|
|
|
// Get the metadata for the next field
|
|
//
|
|
sit.getFieldID (type, field);
|
|
|
|
reachedEndOfObject = (type == STI_OBJECT) && (field == 1);
|
|
|
|
if ((type == STI_ARRAY) && (field == 1))
|
|
{
|
|
WriteLog (lsWARNING, STObject) <<
|
|
"Encountered object with end of array marker";
|
|
throw std::runtime_error ("Illegal terminator in object");
|
|
}
|
|
|
|
if (!reachedEndOfObject)
|
|
{
|
|
// Figure out the field
|
|
//
|
|
SField::ref fn = SField::getField (type, field);
|
|
|
|
if (fn.isInvalid ())
|
|
{
|
|
WriteLog (lsWARNING, STObject) <<
|
|
"Unknown field: field_type=" << type <<
|
|
", field_name=" << field;
|
|
throw std::runtime_error ("Unknown field");
|
|
}
|
|
|
|
// Unflatten the field
|
|
//
|
|
giveObject (
|
|
makeDeserializedObject (fn.fieldType, fn, sit, depth + 1));
|
|
}
|
|
}
|
|
|
|
return reachedEndOfObject;
|
|
}
|
|
|
|
|
|
std::unique_ptr<STBase>
|
|
STObject::deserialize (SerializerIterator& sit, SField::ref name)
|
|
{
|
|
std::unique_ptr <STObject> object (std::make_unique <STObject> (name));
|
|
object->set (sit, 1);
|
|
return std::move (object);
|
|
}
|
|
|
|
bool STObject::hasMatchingEntry (const STBase& t)
|
|
{
|
|
const STBase* o = peekAtPField (t.getFName ());
|
|
|
|
if (!o)
|
|
return false;
|
|
|
|
return t == *o;
|
|
}
|
|
|
|
std::string STObject::getFullText () const
|
|
{
|
|
std::string ret;
|
|
bool first = true;
|
|
|
|
if (fName->hasName ())
|
|
{
|
|
ret = fName->getName ();
|
|
ret += " = {";
|
|
}
|
|
else ret = "{";
|
|
|
|
for (STBase const& elem : mData)
|
|
{
|
|
if (elem.getSType () != STI_NOTPRESENT)
|
|
{
|
|
if (!first)
|
|
ret += ", ";
|
|
else
|
|
first = false;
|
|
|
|
ret += elem.getFullText ();
|
|
}
|
|
}
|
|
|
|
ret += "}";
|
|
return ret;
|
|
}
|
|
|
|
void STObject::add (Serializer& s, bool withSigningFields) const
|
|
{
|
|
std::map<int, const STBase*> fields;
|
|
|
|
for (STBase const& elem : mData)
|
|
{
|
|
// pick out the fields and sort them
|
|
if ((elem.getSType () != STI_NOTPRESENT) &&
|
|
elem.getFName ().shouldInclude (withSigningFields))
|
|
{
|
|
fields.insert (std::make_pair (elem.getFName ().fieldCode, &elem));
|
|
}
|
|
}
|
|
|
|
for (auto const& mapEntry : fields)
|
|
{
|
|
// insert them in sorted order
|
|
const STBase* field = mapEntry.second;
|
|
|
|
// When we serialize an object inside another object,
|
|
// the type associated by rule with this field name
|
|
// must be OBJECT, or the object cannot be deserialized
|
|
assert ((field->getSType() != STI_OBJECT) ||
|
|
(field->getFName().fieldType == STI_OBJECT));
|
|
|
|
field->addFieldID (s);
|
|
field->add (s);
|
|
|
|
if (dynamic_cast<const STArray*> (field) != nullptr)
|
|
s.addFieldID (STI_ARRAY, 1);
|
|
else if (dynamic_cast<const STObject*> (field) != nullptr)
|
|
s.addFieldID (STI_OBJECT, 1);
|
|
}
|
|
}
|
|
|
|
std::string STObject::getText () const
|
|
{
|
|
std::string ret = "{";
|
|
bool first = false;
|
|
for (STBase const& elem : mData)
|
|
{
|
|
if (!first)
|
|
{
|
|
ret += ", ";
|
|
first = false;
|
|
}
|
|
|
|
ret += elem.getText ();
|
|
}
|
|
ret += "}";
|
|
return ret;
|
|
}
|
|
|
|
bool STObject::isEquivalent (const STBase& t) const
|
|
{
|
|
const STObject* v = dynamic_cast<const STObject*> (&t);
|
|
|
|
if (!v)
|
|
{
|
|
WriteLog (lsDEBUG, STObject) <<
|
|
"notEquiv " << getFullText() << " not object";
|
|
return false;
|
|
}
|
|
|
|
typedef boost::ptr_vector<STBase>::const_iterator const_iter;
|
|
const_iter it1 = mData.begin (), end1 = mData.end ();
|
|
const_iter it2 = v->mData.begin (), end2 = v->mData.end ();
|
|
|
|
while ((it1 != end1) && (it2 != end2))
|
|
{
|
|
if ((it1->getSType () != it2->getSType ()) || !it1->isEquivalent (*it2))
|
|
{
|
|
if (it1->getSType () != it2->getSType ())
|
|
{
|
|
WriteLog (lsDEBUG, STObject) << "notEquiv type " <<
|
|
it1->getFullText() << " != " << it2->getFullText();
|
|
}
|
|
else
|
|
{
|
|
WriteLog (lsDEBUG, STObject) << "notEquiv " <<
|
|
it1->getFullText() << " != " << it2->getFullText();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
++it1;
|
|
++it2;
|
|
}
|
|
|
|
return (it1 == end1) && (it2 == end2);
|
|
}
|
|
|
|
uint256 STObject::getHash (std::uint32_t prefix) const
|
|
{
|
|
Serializer s;
|
|
s.add32 (prefix);
|
|
add (s, true);
|
|
return s.getSHA512Half ();
|
|
}
|
|
|
|
uint256 STObject::getSigningHash (std::uint32_t prefix) const
|
|
{
|
|
Serializer s;
|
|
s.add32 (prefix);
|
|
add (s, false);
|
|
return s.getSHA512Half ();
|
|
}
|
|
|
|
int STObject::getFieldIndex (SField::ref field) const
|
|
{
|
|
if (mType != nullptr)
|
|
return mType->getIndex (field);
|
|
|
|
int i = 0;
|
|
for (STBase const& elem : mData)
|
|
{
|
|
if (elem.getFName () == field)
|
|
return i;
|
|
|
|
++i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
const STBase& STObject::peekAtField (SField::ref field) const
|
|
{
|
|
int index = getFieldIndex (field);
|
|
|
|
if (index == -1)
|
|
throw std::runtime_error ("Field not found");
|
|
|
|
return peekAtIndex (index);
|
|
}
|
|
|
|
STBase& STObject::getField (SField::ref field)
|
|
{
|
|
int index = getFieldIndex (field);
|
|
|
|
if (index == -1)
|
|
throw std::runtime_error ("Field not found");
|
|
|
|
return getIndex (index);
|
|
}
|
|
|
|
SField::ref STObject::getFieldSType (int index) const
|
|
{
|
|
return mData[index].getFName ();
|
|
}
|
|
|
|
const STBase* STObject::peekAtPField (SField::ref field) const
|
|
{
|
|
int index = getFieldIndex (field);
|
|
|
|
if (index == -1)
|
|
return nullptr;
|
|
|
|
return peekAtPIndex (index);
|
|
}
|
|
|
|
STBase* STObject::getPField (SField::ref field, bool createOkay)
|
|
{
|
|
int index = getFieldIndex (field);
|
|
|
|
if (index == -1)
|
|
{
|
|
if (createOkay && isFree ())
|
|
return getPIndex (giveObject (makeDefaultObject (field)));
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
return getPIndex (index);
|
|
}
|
|
|
|
bool STObject::isFieldPresent (SField::ref field) const
|
|
{
|
|
int index = getFieldIndex (field);
|
|
|
|
if (index == -1)
|
|
return false;
|
|
|
|
return peekAtIndex (index).getSType () != STI_NOTPRESENT;
|
|
}
|
|
|
|
STObject& STObject::peekFieldObject (SField::ref field)
|
|
{
|
|
STBase* rf = getPField (field, true);
|
|
|
|
if (!rf)
|
|
throw std::runtime_error ("Field not found");
|
|
|
|
if (rf->getSType () == STI_NOTPRESENT)
|
|
rf = makeFieldPresent (field);
|
|
|
|
STObject* cf = dynamic_cast<STObject*> (rf);
|
|
|
|
if (!cf)
|
|
throw std::runtime_error ("Wrong field type");
|
|
|
|
return *cf;
|
|
}
|
|
|
|
bool STObject::setFlag (std::uint32_t f)
|
|
{
|
|
STUInt32* t = dynamic_cast<STUInt32*> (getPField (sfFlags, true));
|
|
|
|
if (!t)
|
|
return false;
|
|
|
|
t->setValue (t->getValue () | f);
|
|
return true;
|
|
}
|
|
|
|
bool STObject::clearFlag (std::uint32_t f)
|
|
{
|
|
STUInt32* t = dynamic_cast<STUInt32*> (getPField (sfFlags));
|
|
|
|
if (!t)
|
|
return false;
|
|
|
|
t->setValue (t->getValue () & ~f);
|
|
return true;
|
|
}
|
|
|
|
bool STObject::isFlag (std::uint32_t f) const
|
|
{
|
|
return (getFlags () & f) == f;
|
|
}
|
|
|
|
std::uint32_t STObject::getFlags (void) const
|
|
{
|
|
const STUInt32* t = dynamic_cast<const STUInt32*> (peekAtPField (sfFlags));
|
|
|
|
if (!t)
|
|
return 0;
|
|
|
|
return t->getValue ();
|
|
}
|
|
|
|
STBase* STObject::makeFieldPresent (SField::ref field)
|
|
{
|
|
int index = getFieldIndex (field);
|
|
|
|
if (index == -1)
|
|
{
|
|
if (!isFree ())
|
|
throw std::runtime_error ("Field not found");
|
|
|
|
return getPIndex (giveObject (makeNonPresentObject (field)));
|
|
}
|
|
|
|
STBase* f = getPIndex (index);
|
|
|
|
if (f->getSType () != STI_NOTPRESENT)
|
|
return f;
|
|
|
|
mData.replace (index, makeDefaultObject (f->getFName ()).release ());
|
|
return getPIndex (index);
|
|
}
|
|
|
|
void STObject::makeFieldAbsent (SField::ref field)
|
|
{
|
|
int index = getFieldIndex (field);
|
|
|
|
if (index == -1)
|
|
throw std::runtime_error ("Field not found");
|
|
|
|
const STBase& f = peekAtIndex (index);
|
|
|
|
if (f.getSType () == STI_NOTPRESENT)
|
|
return;
|
|
|
|
mData.replace (index, makeNonPresentObject (f.getFName ()).release ());
|
|
}
|
|
|
|
bool STObject::delField (SField::ref field)
|
|
{
|
|
int index = getFieldIndex (field);
|
|
|
|
if (index == -1)
|
|
return false;
|
|
|
|
delField (index);
|
|
return true;
|
|
}
|
|
|
|
void STObject::delField (int index)
|
|
{
|
|
mData.erase (mData.begin () + index);
|
|
}
|
|
|
|
std::string STObject::getFieldString (SField::ref field) const
|
|
{
|
|
const STBase* rf = peekAtPField (field);
|
|
|
|
if (!rf) throw std::runtime_error ("Field not found");
|
|
|
|
return rf->getText ();
|
|
}
|
|
|
|
unsigned char STObject::getFieldU8 (SField::ref field) const
|
|
{
|
|
return getFieldByValue <STUInt8> (field);
|
|
}
|
|
|
|
std::uint16_t STObject::getFieldU16 (SField::ref field) const
|
|
{
|
|
return getFieldByValue <STUInt16> (field);
|
|
}
|
|
|
|
std::uint32_t STObject::getFieldU32 (SField::ref field) const
|
|
{
|
|
return getFieldByValue <STUInt32> (field);
|
|
}
|
|
|
|
std::uint64_t STObject::getFieldU64 (SField::ref field) const
|
|
{
|
|
return getFieldByValue <STUInt64> (field);
|
|
}
|
|
|
|
uint128 STObject::getFieldH128 (SField::ref field) const
|
|
{
|
|
return getFieldByValue <STHash128> (field);
|
|
}
|
|
|
|
uint160 STObject::getFieldH160 (SField::ref field) const
|
|
{
|
|
return getFieldByValue <STHash160> (field);
|
|
}
|
|
|
|
uint256 STObject::getFieldH256 (SField::ref field) const
|
|
{
|
|
return getFieldByValue <STHash256> (field);
|
|
}
|
|
|
|
RippleAddress STObject::getFieldAccount (SField::ref field) const
|
|
{
|
|
const STBase* rf = peekAtPField (field);
|
|
|
|
if (!rf)
|
|
throw std::runtime_error ("Field not found");
|
|
|
|
SerializedTypeID id = rf->getSType ();
|
|
|
|
if (id == STI_NOTPRESENT) return RippleAddress ();
|
|
|
|
const STAccount* cf = dynamic_cast<const STAccount*> (rf);
|
|
|
|
if (!cf)
|
|
throw std::runtime_error ("Wrong field type");
|
|
|
|
return cf->getValueNCA ();
|
|
}
|
|
|
|
Account STObject::getFieldAccount160 (SField::ref field) const
|
|
{
|
|
auto rf = peekAtPField (field);
|
|
if (!rf)
|
|
throw std::runtime_error ("Field not found");
|
|
|
|
Account account;
|
|
if (rf->getSType () != STI_NOTPRESENT)
|
|
{
|
|
const STAccount* cf = dynamic_cast<const STAccount*> (rf);
|
|
|
|
if (!cf)
|
|
throw std::runtime_error ("Wrong field type");
|
|
|
|
cf->getValueH160 (account);
|
|
}
|
|
|
|
return account;
|
|
}
|
|
|
|
Blob STObject::getFieldVL (SField::ref field) const
|
|
{
|
|
return getFieldByValue <STBlob> (field);
|
|
}
|
|
|
|
STAmount const& STObject::getFieldAmount (SField::ref field) const
|
|
{
|
|
static STAmount const empty{};
|
|
return getFieldByConstRef <STAmount> (field, empty);
|
|
}
|
|
|
|
const STArray& STObject::getFieldArray (SField::ref field) const
|
|
{
|
|
static STArray const empty{};
|
|
return getFieldByConstRef <STArray> (field, empty);
|
|
}
|
|
|
|
STPathSet const& STObject::getFieldPathSet (SField::ref field) const
|
|
{
|
|
static STPathSet const empty{};
|
|
return getFieldByConstRef <STPathSet> (field, empty);
|
|
}
|
|
|
|
const STVector256& STObject::getFieldV256 (SField::ref field) const
|
|
{
|
|
static STVector256 const empty{};
|
|
return getFieldByConstRef <STVector256> (field, empty);
|
|
}
|
|
|
|
void STObject::setFieldU8 (SField::ref field, unsigned char v)
|
|
{
|
|
setFieldUsingSetValue <STUInt8> (field, v);
|
|
}
|
|
|
|
void STObject::setFieldU16 (SField::ref field, std::uint16_t v)
|
|
{
|
|
setFieldUsingSetValue <STUInt16> (field, v);
|
|
}
|
|
|
|
void STObject::setFieldU32 (SField::ref field, std::uint32_t v)
|
|
{
|
|
setFieldUsingSetValue <STUInt32> (field, v);
|
|
}
|
|
|
|
void STObject::setFieldU64 (SField::ref field, std::uint64_t v)
|
|
{
|
|
setFieldUsingSetValue <STUInt64> (field, v);
|
|
}
|
|
|
|
void STObject::setFieldH128 (SField::ref field, uint128 const& v)
|
|
{
|
|
setFieldUsingSetValue <STHash128> (field, v);
|
|
}
|
|
|
|
void STObject::setFieldH256 (SField::ref field, uint256 const& v)
|
|
{
|
|
setFieldUsingSetValue <STHash256> (field, v);
|
|
}
|
|
|
|
void STObject::setFieldV256 (SField::ref field, STVector256 const& v)
|
|
{
|
|
setFieldUsingSetValue <STVector256> (field, v);
|
|
}
|
|
|
|
void STObject::setFieldAccount (SField::ref field, Account const& v)
|
|
{
|
|
STBase* rf = getPField (field, true);
|
|
|
|
if (!rf)
|
|
throw std::runtime_error ("Field not found");
|
|
|
|
if (rf->getSType () == STI_NOTPRESENT)
|
|
rf = makeFieldPresent (field);
|
|
|
|
STAccount* cf = dynamic_cast<STAccount*> (rf);
|
|
|
|
if (!cf)
|
|
throw std::runtime_error ("Wrong field type");
|
|
|
|
cf->setValueH160 (v);
|
|
}
|
|
|
|
void STObject::setFieldVL (SField::ref field, Blob const& v)
|
|
{
|
|
setFieldUsingSetValue <STBlob> (field, v);
|
|
}
|
|
|
|
void STObject::setFieldAmount (SField::ref field, STAmount const& v)
|
|
{
|
|
setFieldUsingAssignment (field, v);
|
|
}
|
|
|
|
void STObject::setFieldPathSet (SField::ref field, STPathSet const& v)
|
|
{
|
|
setFieldUsingAssignment (field, v);
|
|
}
|
|
|
|
void STObject::setFieldArray (SField::ref field, STArray const& v)
|
|
{
|
|
setFieldUsingAssignment (field, v);
|
|
}
|
|
|
|
Json::Value STObject::getJson (int options) const
|
|
{
|
|
Json::Value ret (Json::objectValue);
|
|
|
|
// TODO(tom): this variable is never changed...?
|
|
int index = 1;
|
|
for (auto const& it: mData)
|
|
{
|
|
if (it.getSType () != STI_NOTPRESENT)
|
|
{
|
|
auto const& n = it.getFName ();
|
|
auto key = n.hasName () ? std::string(n.getJsonName ()) :
|
|
std::to_string (index);
|
|
ret[key] = it.getJson (options);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool STObject::operator== (const STObject& obj) const
|
|
{
|
|
// This is not particularly efficient, and only compares data elements
|
|
// with binary representations
|
|
int matches = 0;
|
|
for (STBase const& t1 : mData)
|
|
{
|
|
if ((t1.getSType () != STI_NOTPRESENT) && t1.getFName ().isBinary ())
|
|
{
|
|
// each present field must have a matching field
|
|
bool match = false;
|
|
for (STBase const& t2 : obj.mData)
|
|
{
|
|
if (t1.getFName () == t2.getFName ())
|
|
{
|
|
if (t2 != t1)
|
|
return false;
|
|
|
|
match = true;
|
|
++matches;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!match)
|
|
{
|
|
WriteLog (lsTRACE, STObject) <<
|
|
"STObject::operator==: no match for " <<
|
|
t1.getFName ().getName ();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
int fields = 0;
|
|
for (STBase const& t2 : obj.mData)
|
|
{
|
|
if ((t2.getSType () != STI_NOTPRESENT) && t2.getFName ().isBinary ())
|
|
++fields;
|
|
}
|
|
|
|
if (fields != matches)
|
|
{
|
|
WriteLog (lsTRACE, STObject) << "STObject::operator==: " <<
|
|
fields << " fields, " << matches << " matches";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // ripple
|