Skip to content

Commit

Permalink
Merge pull request #1176 from LLNL/feature/nselliott/sidre-deep-copy
Browse files Browse the repository at this point in the history
Add deep copy methods for Group and View
  • Loading branch information
nselliott committed Sep 11, 2023
2 parents c36a089 + 58c1363 commit e8ee083
Show file tree
Hide file tree
Showing 5 changed files with 382 additions and 18 deletions.
95 changes: 87 additions & 8 deletions src/axom/sidre/core/Group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,41 @@ View* Group::copyView(View* view)
return copy;
}

/*
*************************************************************************
*
* Create a deep copy of given View and attach to this Group.
*
* The deep copy performs a copy of all described data.
*
*************************************************************************
*/
View* Group::deepCopyView(View* view, int allocID)
{
allocID = getValidAllocatorID(allocID);

if(view == nullptr || hasChildView(view->getName()))
{
SLIC_CHECK_MSG(
view != nullptr,
SIDRE_GROUP_LOG_PREPEND << "Null pointer provided, no View to copy.");

if(view != nullptr)
{
SLIC_CHECK_MSG(!hasChildView(view->getName()),
SIDRE_GROUP_LOG_PREPEND
<< "Group already has a View named '" << view->getName()
<< "' so View copy operation cannot happen");
}

return nullptr;
}

View* copy = createView(view->getName());
view->deepCopyView(copy, allocID);
return copy;
}

////////////////////////////////////////////////////////////////////////
//
// Child Group query methods.
Expand Down Expand Up @@ -1355,19 +1390,63 @@ Group* Group::copyGroup(Group* group)
Group* res = createGroup(group->getName());

// copy child Groups to new Group
IndexType gidx = group->getFirstValidGroupIndex();
while(indexIsValid(gidx))
for(auto& grp : group->groups())
{
res->copyGroup(group->getGroup(gidx));
gidx = group->getNextValidGroupIndex(gidx);
res->copyGroup(&grp);
}

// copy Views to new Group
IndexType vidx = group->getFirstValidViewIndex();
while(indexIsValid(vidx))
for(auto& view : group->views())
{
res->copyView(&view);
}

return res;
}

/*
*************************************************************************
*
* Create a deep copy of given Group and make it a child of this Group.
*
* The deep copy of a Group will copy the group hierarchy and deep copy
* all Views within the hierarchy.
*
*************************************************************************
*/
Group* Group::deepCopyGroup(Group* group, int allocID)
{
allocID = getValidAllocatorID(allocID);

if(group == nullptr || hasChildGroup(group->getName()))
{
SLIC_CHECK_MSG(
group != nullptr,
SIDRE_GROUP_LOG_PREPEND << "Null pointer provided, no Group to copy.");

if(group != nullptr)
{
SLIC_CHECK_MSG(!hasChildGroup(group->getName()),
SIDRE_GROUP_LOG_PREPEND
<< "Invalid copy operation. Group already has "
<< "a child named '" << group->getName() << "'.");
}

return nullptr;
}

Group* res = createGroup(group->getName());

// copy child Groups to new Group
for(auto& grp : group->groups())
{
res->deepCopyGroup(&grp, allocID);
}

// copy Views to new Group
for(auto& view : group->views())
{
res->copyView(group->getView(vidx));
vidx = group->getNextValidViewIndex(vidx);
res->deepCopyView(&view, allocID);
}

return res;
Expand Down
68 changes: 59 additions & 9 deletions src/axom/sidre/core/Group.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -890,20 +890,43 @@ class Group
View* moveView(View* view);

/*!
* \brief Create a copy of given View object and add it to this Group.
* \brief Create a (shallow) copy of given View object and add it to this
* Group.
*
* Note that View copying is a "shallow" copy; the data associated with
* the View is not copied. The new View object is associated with
* the same data as the original.
*
* If given Group pointer is null or Group already has a child Group with
* same name as given Group, method is a no-op.
* If given View pointer is null or Group already has a View with
* same name as given View, method is a no-op.
*
* \return pointer to given argument Group object or nullptr if Group
* is not moved into this Group.
* \sa deepCopyView
*
* \return pointer to the new copied View object or nullptr if a View
* is not successfully copied into this Group.
*/
View* copyView(View* view);

/*!
* \brief Create a deep copy of given View object and add it to this Group.
*
* Note that for Views that use sidre Buffers or external data, the
* deep copy performs a copy of the data described by the View
* into a new Buffer attached to the destination View. If the source View
* describes only part of a Buffer or part of an external array, due to
* strides and or offsets into the data, only the values seen by the
* description will be copied into the new View. The new View will have
* a Buffer that is the exact size of the number of values that are copied,
* with an offset of zero and a stride of one.
*
* If given View pointer is null or Group already has a View with
* same name as given View, method is a no-op.
*
* \return pointer to the new copied View object or nullptr if a View
* is not copied into this Group.
*/
View* deepCopyView(View* view, int allocID = INVALID_ALLOCATOR_ID);

//@}

//@{
Expand Down Expand Up @@ -1228,8 +1251,8 @@ class Group
Group* moveGroup(Group* group);

/*!
* \brief Create a copy of Group hierarchy rooted at given Group and make it
* a child of this Group.
* \brief Create a (shallow) copy of Group hierarchy rooted at given
* Group and make it a child of this Group.
*
* Note that all Views in the Group hierarchy are copied as well.
*
Expand All @@ -1241,11 +1264,38 @@ class Group
* If given Group pointer is null or Group already has a child Group with
* same name as given Group, method is a no-op.
*
* \return pointer to given argument Group object or nullptr if Group
* is not moved into this Group.
* \sa deepCopyGroup
*
* \return pointer to the new copied Group object or nullptr if a Group
* is not copied into this Group.
*/
Group* copyGroup(Group* group);

/*!
* \brief Create a deep copy of Group hierarchy rooted at given Group and
* make it a child of this Group.
*
* Note that all Views in the Group hierarchy are deep copied as well.
*
* The deep copy of the Group creates a duplicate of the entire Group
* hierarchy and performs a deep copy of the data described by the Views
* in the hierarchy.
*
* The Views in the new Group hierarchy will each allocate and use
* new Buffers to hold their copied data. Each Buffer will be sized to
* receive only the data values seen by the description of the original
* View and will have zero offset and a strid of one.
*
* If given Group pointer is null or Group already has a child Group with
* same name as given Group, method is a no-op.
*
* \sa deepCopyGroup
*
* \return pointer to the new copied Group object or nullptr if a Group
* is not copied into this Group.
*/
Group* deepCopyGroup(Group* group, int allocID = INVALID_ALLOCATOR_ID);

//@}

//@{
Expand Down
96 changes: 96 additions & 0 deletions src/axom/sidre/core/View.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,102 @@ void View::copyView(View* copy) const
}
}

/*
*************************************************************************
*
* PRIVATE method copy the contents of this into a undescribed EMPTY view.
*
*************************************************************************
*/
void View::deepCopyView(View* copy, int allocID) const
{
SLIC_ASSERT_MSG(copy->m_state == EMPTY && !copy->isDescribed(),
SIDRE_VIEW_LOG_PREPEND
<< "deepCopyView can only copy into undescribed view "
<< "with empty state.");

if(isDescribed())
{
copy->describe(m_schema.dtype());
if(hasBuffer() || m_state == EXTERNAL)
{
copy->allocate(getTypeID(), getNumElements(), allocID);
}
}

switch(m_state)
{
case EMPTY:
// Nothing more to do
break;
case STRING:
case SCALAR:
copy->m_node = m_node;
copy->m_state = m_state;
copy->m_is_applied = true;
break;
case EXTERNAL:
if(!copy->isAllocated())
{
copy->allocate(allocID);
}
if(isApplied())
{
copy->apply();
}
{
IndexType stride = getStride();
IndexType src_offset = getOffset();
IndexType dst_offset = copy->getOffset();
IndexType num_bytes = getBytesPerElement();
IndexType j = 0;
for(IndexType i = 0; i < getNumElements(); ++i)
{
char* copy_dst =
static_cast<char*>(copy->getVoidPtr()) + (dst_offset + i) * num_bytes;
const char* copy_src =
static_cast<const char*>(getVoidPtr()) + (src_offset + j) * num_bytes;
axom::copy(copy_dst, copy_src, num_bytes);

j += stride;
}
}
break;
case BUFFER:
if(isAllocated() && !copy->isAllocated())
{
copy->allocate(allocID);
}
if(isApplied())
{
copy->apply();
}
if(hasBuffer())
{
IndexType stride = getStride();
IndexType src_offset = getOffset();
IndexType dst_offset = copy->getOffset();
IndexType num_bytes = getBytesPerElement();
IndexType j = 0;
for(IndexType i = 0; i < getNumElements(); ++i)
{
char* copy_dst =
static_cast<char*>(copy->getVoidPtr()) + (dst_offset + i) * num_bytes;
const char* copy_src =
static_cast<const char*>(getVoidPtr()) + (src_offset + j) * num_bytes;
axom::copy(copy_dst, copy_src, num_bytes);

j += stride;
}
}
break;
default:
SLIC_ASSERT_MSG(false,
SIDRE_VIEW_LOG_PREPEND << "View is in unexpected state: "
<< getStateStringName(m_state));
}
}

/*
*************************************************************************
*
Expand Down
15 changes: 14 additions & 1 deletion src/axom/sidre/core/View.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1363,13 +1363,26 @@ class View
}

/*!
* \brief Copy view contents into an undescribed EMPTY view.
* \brief Copy contents of this View contents into an undescribed EMPTY View.
*
* For SCALAR and STRING the data is copied; EXTERNAL,
* data pointer is copied; BUFFER attaches the buffer.
*/
void copyView(View* copy) const;

/*!
* \brief Deep copy contents of this View contents into an undescribed
* EMPTY View.
*
* For SCALAR and STRING the data is copied and the state is preserved.
* For BUFFER and EXTERNAL, the data described by this View is copied into a
* new Buffer that is of the size needed to hold the copied data. Any
* parts of the source Buffer or external array that are not seen due
* to offsets and strides in the description will not be copied. The copied
* View will have BUFFER state with zero offset and a stride of one.
*/
void deepCopyView(View* copy, int allocID = INVALID_ALLOCATOR_ID) const;

/*!
* \brief Add view description and references to it's data to a conduit tree.
*/
Expand Down

0 comments on commit e8ee083

Please sign in to comment.