Skip to content

Commit

Permalink
implemented struct deepcopy without arrayfield case
Browse files Browse the repository at this point in the history
Signed-off-by: Artur Gajowniczek <artur.gajowniczek@gmail.com>
  • Loading branch information
argaj committed Mar 5, 2024
1 parent 23ec919 commit 70fe707
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 13 deletions.
2 changes: 2 additions & 0 deletions cpp/csp/engine/DialectGenericType.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ struct DialectGenericType
DialectGenericType & operator=( const DialectGenericType & rhs );
DialectGenericType & operator=( DialectGenericType && rhs );

void deepcopy( const DialectGenericType & rhs );

bool operator==( const DialectGenericType & rhs ) const;
bool operator!=( const DialectGenericType & rhs ) const { return !( (*this)==rhs); }

Expand Down
18 changes: 11 additions & 7 deletions cpp/csp/engine/Struct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ Struct * StructMeta::createRaw() const
initialize( s );

if( m_default )
s -> copyFrom( m_default.get() );
s -> copyFrom( m_default.get(), true ); //TODO should be copy or deepcopy?

return s;
}
Expand Down Expand Up @@ -226,7 +226,7 @@ void StructMeta::initialize( Struct * s ) const
m_base -> initialize( s );
}

void StructMeta::copyFrom( const Struct * src, Struct * dest )
void StructMeta::copyFrom( const Struct * src, Struct * dest, bool isDeepcopy )
{
if( unlikely( src == dest ) )
return;
Expand All @@ -236,10 +236,11 @@ void StructMeta::copyFrom( const Struct * src, Struct * dest )
CSP_THROW( TypeError, "Attempting to copy from struct type '" << src -> meta() -> name() << "' to struct type '" << dest -> meta() -> name()
<< "'. copy_from may only be used to copy from same type or derived types" );

dest -> meta() -> copyFromImpl( src, dest );
}

void StructMeta::copyFromImpl( const Struct * src, Struct * dest ) const
dest -> meta() -> copyFromImpl( src, dest, isDeepcopy );
}

void StructMeta::copyFromImpl( const Struct * src, Struct * dest, bool isDeepcopy ) const
{
//quick outs, if fully native we can memcpy the whole thing
if( isNative() )
Expand All @@ -255,7 +256,10 @@ void StructMeta::copyFromImpl( const Struct * src, Struct * dest ) const
auto * field = m_fields[ idx ].get();

if( field -> isSet( src ) )
static_cast<NonNativeStructField*>( field ) -> copyFrom( src, dest );
if( !isDeepcopy )
static_cast<NonNativeStructField*>( field ) -> copyFrom( src, dest );
else
static_cast<NonNativeStructField*>( field ) -> deepcopyFrom( src, dest );
else
static_cast<NonNativeStructField*>( field ) -> clearValue( dest );
}
Expand All @@ -266,7 +270,7 @@ void StructMeta::copyFromImpl( const Struct * src, Struct * dest ) const
partialNativeSize() );

if( m_base )
m_base -> copyFromImpl( src, dest );
m_base -> copyFromImpl( src, dest, isDeepcopy );
}
}

Expand Down
54 changes: 49 additions & 5 deletions cpp/csp/engine/Struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class StructField
//copy methods need not deal with mask set/unset, only copy values
virtual void copyFrom( const Struct * src, Struct * dest ) const = 0;

virtual void deepcopyFrom( const Struct * src, Struct * dest ) const = 0;

template<typename T>
struct upcast;

Expand Down Expand Up @@ -149,6 +151,11 @@ class NativeStructField : public StructField
value( dest ) = value( src );
}

void deepcopyFrom( const Struct * src, Struct * dest ) const override
{
value( dest ) = value( src );
}

protected:
NativeStructField( CspTypePtr type, const std::string & fieldname ) : StructField( type, fieldname, sizeof( T ), alignof( T ) )
{}
Expand Down Expand Up @@ -199,6 +206,11 @@ class NotImplementedStructField : public StructField
{
CSP_THROW( NotImplemented, "Struct fields are not supported for type " << CspType::Type::fromCType<T>::type );
}

void deepcopyFrom( const Struct * src, Struct * dest ) const override
{
CSP_THROW( NotImplemented, "Struct fields are not supported for type " << CspType::Type::fromCType<T>::type );
}
};


Expand Down Expand Up @@ -268,6 +280,11 @@ class StringStructField final : public NonNativeStructField
value( dest ) = value( src );
}

virtual void deepcopyFrom( const Struct * src, Struct * dest ) const
{
value( dest ) = value( src );
}

virtual bool isEqual( const Struct * x, const Struct * y ) const
{
return value( x ) == value( y );
Expand Down Expand Up @@ -327,6 +344,11 @@ class ArrayStructField : public NonNativeStructField
value( dest ) = value( src );
}

void deepcopyFrom( const Struct * src, Struct * dest ) const override //TODO implement
{
value( dest ) = value( src );
}

bool isEqual( const Struct * x, const Struct * y ) const override
{
return value( x ) == value( y );
Expand Down Expand Up @@ -410,6 +432,11 @@ class DialectGenericStructField : public NonNativeStructField
value( dest ) = value( src );
}

void deepcopyFrom( const Struct * src, Struct * dest ) const override
{
( ( DialectGenericType * ) valuePtr( dest ) ) -> deepcopy( * ( ( DialectGenericType * ) valuePtr( src ) ) );
}

bool isEqual( const Struct * x, const Struct * y ) const override
{
return value( x ) == value( y );
Expand Down Expand Up @@ -567,7 +594,7 @@ class StructMeta : public std::enable_shared_from_this<StructMeta>
void destroy( Struct * s ) const;
bool isEqual( const Struct * x, const Struct * y ) const;
size_t hash( const Struct * x ) const;
static void copyFrom( const Struct * src, Struct * dest );
static void copyFrom( const Struct * src, Struct * dest, bool isDeepcopy );
static void updateFrom( const Struct * src, Struct * dest );
void clear( Struct * s ) const;
bool allFieldsSet( const Struct * s ) const;
Expand All @@ -585,7 +612,7 @@ class StructMeta : public std::enable_shared_from_this<StructMeta>
using FieldMap = std::unordered_map<const char *,StructFieldPtr, hash::CStrHash, hash::CStrEq >;

size_t partialNativeSize() const { return m_size - m_nativeStart; }
void copyFromImpl( const Struct * src, Struct * dest ) const;
void copyFromImpl( const Struct * src, Struct * dest, bool isDeepcopy ) const;
void updateFromImpl( const Struct * src, Struct * dest ) const;

std::string m_name;
Expand Down Expand Up @@ -647,15 +674,28 @@ class Struct
StructPtr copy() const
{
StructPtr copy = meta() -> create();
copy -> copyFrom( this );
copy -> copyFrom( this, false );
return copy;
}

StructPtr deepcopy() const
{
StructPtr copy = meta() -> create();
copy -> copyFrom( this, true );
return copy;
}

void copyFrom( const Struct * rhs )
void copyFrom( const Struct * rhs, bool isDeepcopy )
{

StructMeta::copyFrom( rhs, this );
}

void deepcopyFrom( const Struct * rhs )
{
StructMeta::deepcopyFrom( rhs, this );
}

void updateFrom( const Struct * rhs )
{
StructMeta::updateFrom( rhs, this );
Expand All @@ -666,7 +706,6 @@ class Struct
return meta() -> allFieldsSet( this );
}

//void deepcopyFrom( StructMeta * meta, Struct * rhs );

//used to cache dialect representations of this struct, if needed
void * dialectPtr() const { return hidden() -> dialectPtr; }
Expand Down Expand Up @@ -786,6 +825,11 @@ class StructStructField final : public NonNativeStructField
value( dest ) = value( src );
}

virtual void deepcopyFrom( const Struct * src, Struct * dest ) const override
{
value( dest ) = value(src) -> deepcopy();
}

virtual bool isEqual( const Struct * x, const Struct * y ) const override
{
return ( *value( x ).get() ) == ( *value( y ).get() );
Expand Down
9 changes: 9 additions & 0 deletions cpp/csp/python/PyCspType.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include <csp/python/PyCspType.h>
#include <csp/python/Conversions.h>
#include <Python.h>

static_assert( sizeof( csp::DialectGenericType ) == sizeof( csp::python::PyObjectPtr ) );
static_assert( alignof( csp::DialectGenericType ) == alignof( csp::python::PyObjectPtr ) );
Expand Down Expand Up @@ -26,6 +28,13 @@ DialectGenericType::DialectGenericType( DialectGenericType &&rhs )
new( this ) csp::python::PyObjectPtr( reinterpret_cast<const csp::python::PyObjectPtr &&>(rhs) );
}

void DialectGenericType::deepcopy( const DialectGenericType & rhs )
{
static PyObject *pyDeepcopy = PyObject_GetAttrString( PyImport_ImportModule( "copy" ), "deepcopy" );
PyObject * deepcopy = PyObject_CallObject( pyDeepcopy, PyTuple_Pack(1, python::toPython( rhs ) ) );

new( this ) DialectGenericType( python::fromPython<DialectGenericType>( deepcopy ) );
}

DialectGenericType &DialectGenericType::operator=( const DialectGenericType &rhs )
{
Expand Down
10 changes: 9 additions & 1 deletion cpp/csp/python/PyStruct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,13 @@ PyObject * PyStruct_copy( PyStruct * self )
return copy;
}

PyObject * PyStruct_deepcopy( PyStruct * self )
{
PyObject * deepcopy = self -> ob_type -> tp_alloc( self -> ob_type, 0 );
new ( deepcopy ) PyStruct( self -> struct_ -> deepcopy() );
return deepcopy;
}

PyObject * PyStruct_clear( PyStruct * self )
{
CSP_BEGIN_METHOD;
Expand All @@ -864,7 +871,7 @@ PyObject * PyStruct_copy_from( PyStruct * self, PyObject * o )
if( !PyType_IsSubtype( Py_TYPE( o ), &PyStruct::PyType ) )
CSP_THROW( TypeError, "Attempting to copy from non-struct type " << Py_TYPE( o ) -> tp_name );

self -> struct_ -> copyFrom( ( ( PyStruct * ) o ) -> struct_.get() );
self -> struct_ -> copyFrom( ( ( PyStruct * ) o ) -> struct_.get(), true );
CSP_RETURN_NONE;
}

Expand All @@ -886,6 +893,7 @@ PyObject * PyStruct_all_fields_set( PyStruct * self )

static PyMethodDef PyStruct_methods[] = {
{ "copy", (PyCFunction) PyStruct_copy, METH_NOARGS, "make a shallow copy of the struct" },
{ "deepcopy", (PyCFunction) PyStruct_deepcopy, METH_NOARGS, "make a deep copy of the struct" },
{ "clear", (PyCFunction) PyStruct_clear, METH_NOARGS, "clear all fields" },
{ "copy_from", (PyCFunction) PyStruct_copy_from, METH_O, "copy from struct. struct must be same type or a derived type. unset fields will copy over" },
{ "update_from", (PyCFunction) PyStruct_update_from, METH_O, "update from struct. struct must be same type or a derived type. unset fields will be not be copied" },
Expand Down
75 changes: 75 additions & 0 deletions csp/tests/impl/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,17 @@ class StructWithStruct(csp.Struct):
s: BaseNative


class StructWithStructWithMutable(csp.Struct):
a: int
s: BaseNonNative


class StructWithLists(csp.Struct):
# native_list: [int]
# struct_list: [BaseNative]
dialect_generic_list: [list]


class AllTypes(csp.Struct):
b: bool = False
i: int = 123
Expand Down Expand Up @@ -297,6 +308,55 @@ def test_copy(self):
base.copy_from(derived)
self.assertEqual(base.i, derived.i)

def test_deepcopy(self):
values = {'i' : 123, 'f': 123.456, 'b': True,
'i2': 111, 'f2': 111.222, 'b2': False,
's' : 'str', 'o': {}, 'l': [1, 2, 3],
's2': 'str2', 'o2': None, 'l2': [4, 5, 6],
'a1' : list(range(20)),
'a2' : list(str(x) for x in range(30)),
'a3' : [ [], 'hey', {}, lambda x: 1 ],
'i3': 333, 'l3': list(str(x * x) for x in range(5)),
'native_list': [1, 2, 3], 'struct_list': [BaseNative(i=1), BaseNative(i=2)],
'dialect_generic_list': [[1], [2]]
}

for typ in (BaseNative, BaseNonNative, BaseMixed, DerivedFullyNative, DerivedPartialNative, DerivedMixed, DerivedMixedNoop, DerivedMixedAfterNoop):
self.assertEqual(typ(), typ().deepcopy())
o = typ()
for key in typ.metadata().keys():
setattr(o, key, values[key])
self.assertEqual(o, o.deepcopy())
# Compare with unset field
delattr(o, list(typ.metadata().keys())[0])
self.assertEqual(o, o.deepcopy())

o = StructWithStructWithMutable()
o.s = BaseNonNative()
o.s.l = [1, 2, 3]

o_deepcopy = o.deepcopy()
self.assertEqual(o, o_deepcopy)

o.s.l[0] = -1
self.assertNotEqual(o, o_deepcopy)

list_new_values = {
# 'native_list': -1,
# 'struct_list': BaseNative(i=-1),
'dialect_generic_list': [-1]
}

for list_type in list_new_values.keys():
o = StructWithLists()
for key in StructWithLists.metadata().keys():
setattr(o, key, values[key])
o_deepcopy = o.deepcopy()
self.assertEqual(o, o_deepcopy)
# getattr(o, list_type)[0] = list_new_values[list_type]
getattr(o, list_type)[0][0] = list_new_values[list_type]
self.assertNotEqual(o, o_deepcopy)

def test_derived_defaults(self):
s1 = StructWithDefaults()
s2 = DerivedStructWithDefaults()
Expand Down Expand Up @@ -473,6 +533,21 @@ def test_copy_from_unsets(self):
self.assertFalse(hasattr(dest2, "s")) # unsets
self.assertFalse(hasattr(dest2, "a1"))

def test_deepcopy_from(self):
source = StructWithLists(struct_list=[BaseNative(i=123)],
dialect_generic_list=[{'a': 1}])

blank = StructWithLists()
blank.deepcopy_from(source)

source.struct_list[0].i = -1
source.dialect_generic_list[0]['b'] = 2

self.assertEqual(source.struct_list[0].i, -1 )
self.assertEqual(source.dialect_generic_list[0], {'a': 1, 'b': 2})
self.assertEqual(blank.struct_list[0].i, 123)
self.assertEqual(blank.dialect_generic_list[0], {'a': 1})

def test_update_from(self):
source = DerivedMixed(i=1, f=2.3, i2=4, s2="woodchuck")
dest1 = DerivedMixed(i=5, b=True, l2=[1, 2, 3])
Expand Down

0 comments on commit 70fe707

Please sign in to comment.