1 // Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and
2 // other Axom Project Developers. See the top-level LICENSE file for details.
3 //
4 // SPDX-License-Identifier: (BSD-3-Clause)
5
6 // Associated header file
7 #include "View.hpp"
8
9 // Sidre component headers
10 #include "Buffer.hpp"
11 #include "Group.hpp"
12 #include "DataStore.hpp"
13 #include "Attribute.hpp"
14
15 #include "axom/core/Macros.hpp"
16
17 namespace axom
18 {
19 namespace sidre
20 {
21 /*
22 *************************************************************************
23 *
24 * Return path of View's owning Group object.
25 * Needs to be in the .cpp file because Group methods aren't
26 * accessible in .hpp file.
27 *
28 *************************************************************************
29 */
getPath() const30 std::string View::getPath() const { return getOwningGroup()->getPathName(); }
31
32 /*
33 *************************************************************************
34 *
35 * Return full path of View object, including its name.
36 * Needs to be in the .cpp file because Group methods aren't
37 * accessible in .hpp file.
38 *
39 *************************************************************************
40 */
getPathName() const41 std::string View::getPathName() const
42 {
43 const auto path = getPath();
44
45 if(path.length() < 1)
46 {
47 return getName();
48 }
49
50 return path + getOwningGroup()->getPathDelimiter() + getName();
51 }
52
53 /*
54 *************************************************************************
55 *
56 * Allocate data for view, previously described.
57 * The state may transition from EMPTY to BUFFER;
58 * otherwise, the state must already be BUFFER.
59 *
60 *************************************************************************
61 */
allocate(int allocID)62 View* View::allocate(int allocID)
63 {
64 allocID = getValidAllocatorID(allocID);
65
66 if(isAllocateValid())
67 {
68 if(m_state == EMPTY)
69 {
70 SLIC_ASSERT_MSG(m_data_buffer == nullptr,
71 SIDRE_VIEW_LOG_PREPEND
72 << "State was EMPTY, but data buffer was not null.");
73 m_data_buffer = m_owning_group->getDataStore()->createBuffer();
74 m_data_buffer->attachToView(this);
75 m_state = BUFFER;
76 }
77
78 TypeID type = static_cast<TypeID>(m_schema.dtype().id());
79 IndexType num_elems = m_schema.dtype().number_of_elements();
80 m_data_buffer->allocate(type, num_elems, allocID);
81 apply();
82 }
83
84 return this;
85 }
86
87 /*
88 *************************************************************************
89 *
90 * Allocate data for view with type and number of elements.
91 *
92 *************************************************************************
93 */
allocate(TypeID type,IndexType num_elems,int allocID)94 View* View::allocate(TypeID type, IndexType num_elems, int allocID)
95 {
96 allocID = getValidAllocatorID(allocID);
97
98 if(type == NO_TYPE_ID || num_elems < 0)
99 {
100 SLIC_CHECK_MSG(type != NO_TYPE_ID,
101 SIDRE_VIEW_LOG_PREPEND
102 << "Could not allocate: Data type was 'NO_TYPE_ID'.");
103 SLIC_CHECK_MSG(
104 num_elems >= 0,
105 SIDRE_VIEW_LOG_PREPEND
106 << "Could not allocate: num_elems cannot be less than zero.");
107
108 return this;
109 }
110
111 describe(type, num_elems);
112 allocate(allocID);
113
114 return this;
115 }
116
117 /*
118 *************************************************************************
119 *
120 * Allocate data for view described by a Conduit data type object.
121 *
122 *************************************************************************
123 */
allocate(const DataType & dtype,int allocID)124 View* View::allocate(const DataType& dtype, int allocID)
125 {
126 allocID = getValidAllocatorID(allocID);
127
128 if(dtype.is_empty())
129 {
130 SLIC_CHECK_MSG(!dtype.is_empty(),
131 SIDRE_VIEW_LOG_PREPEND
132 << "Unable to allocate View with empty data type.");
133 return this;
134 }
135
136 describe(dtype);
137 allocate(allocID);
138
139 return this;
140 }
141
142 /*
143 *************************************************************************
144 *
145 * Reallocate data for view to given number of elements.
146 * This function requires that the view is already described.
147 * The state may transition from EMPTY to BUFFER;
148 * otherwise, the state must already be BUFFER.
149 *
150 *************************************************************************
151 */
reallocate(IndexType num_elems)152 View* View::reallocate(IndexType num_elems)
153 {
154 TypeID vtype = static_cast<TypeID>(m_schema.dtype().id());
155
156 if(num_elems < 0)
157 {
158 SLIC_CHECK_MSG(
159 false,
160 SIDRE_VIEW_LOG_PREPEND << "Unable to reallocate, num_elems must be >= 0");
161 }
162 else if(isAllocateValid())
163 {
164 if(m_state == EMPTY)
165 {
166 allocate(vtype, num_elems);
167 }
168 else if(m_data_buffer->isAllocated()) // XXX if ( isAllocated() )
169 {
170 describe(vtype, num_elems);
171 m_data_buffer->reallocate(num_elems);
172 apply();
173 }
174 else
175 {
176 allocate(vtype, num_elems);
177 }
178 }
179
180 return this;
181 }
182
183 /*
184 *************************************************************************
185 *
186 * Deallocate data for view.
187 *
188 *************************************************************************
189 */
deallocate()190 View* View::deallocate()
191 {
192 if(!isAllocateValid())
193 {
194 SLIC_CHECK_MSG(isAllocateValid(),
195 SIDRE_VIEW_LOG_PREPEND
196 << "View's state " << getStateStringName(m_state)
197 << " does not allow data deallocation");
198 return this;
199 }
200
201 if(hasBuffer())
202 {
203 m_data_buffer->deallocate();
204 }
205
206 return this;
207 }
208
209 /*
210 *************************************************************************
211 *
212 * Reallocate data for view using a Conduit data type object.
213 *
214 *************************************************************************
215 */
reallocate(const DataType & dtype)216 View* View::reallocate(const DataType& dtype)
217 {
218 // If we don't have an allocated buffer, we can just call allocate.
219 if(!isAllocated())
220 {
221 return allocate(dtype);
222 }
223
224 TypeID type = static_cast<TypeID>(dtype.id());
225 TypeID view_type = static_cast<TypeID>(m_schema.dtype().id());
226
227 if(dtype.is_empty() || !isAllocateValid() || type != view_type)
228 {
229 SLIC_CHECK_MSG(!dtype.is_empty(),
230 SIDRE_VIEW_LOG_PREPEND
231 << "Unable to re-allocate View with empty data type.");
232 SLIC_CHECK_MSG(isAllocateValid(),
233 SIDRE_VIEW_LOG_PREPEND
234 << "View's state " << getStateStringName(m_state)
235 << " does not allow data re-allocation");
236 SLIC_CHECK_MSG(type == view_type,
237 SIDRE_VIEW_LOG_PREPEND
238 << "Attempting to re-allocate view with different type.");
239 return this;
240 }
241
242 describe(dtype);
243 IndexType num_elems = dtype.number_of_elements();
244 m_data_buffer->reallocate(num_elems);
245 apply();
246
247 return this;
248 }
249
250 /*
251 *************************************************************************
252 *
253 * Attach/detach buffer to view.
254 *
255 *************************************************************************
256 */
attachBuffer(Buffer * buff)257 View* View::attachBuffer(Buffer* buff)
258 {
259 if(m_state == BUFFER && buff == nullptr)
260 {
261 Buffer* old_buffer = detachBuffer();
262 if(old_buffer->getNumViews() == 0)
263 {
264 getOwningGroup()->getDataStore()->destroyBuffer(old_buffer);
265 }
266 unapply();
267 }
268 else if(m_state == EMPTY && buff != nullptr)
269 {
270 m_data_buffer = buff;
271 buff->attachToView(this);
272 m_state = BUFFER;
273 SLIC_ASSERT(m_is_applied == false);
274
275 // If view is described and the buffer is allocated, then call apply.
276 if(isDescribed() && m_data_buffer->isAllocated())
277 {
278 apply();
279 }
280 }
281
282 return this;
283 }
284
285 /*
286 *************************************************************************
287 *
288 * Detach buffer from view.
289 *
290 *************************************************************************
291 */
detachBuffer()292 Buffer* View::detachBuffer()
293 {
294 Buffer* buff = nullptr;
295
296 if(m_state == BUFFER)
297 {
298 buff = m_data_buffer;
299 m_data_buffer->detachFromView(this);
300 }
301
302 return buff;
303 }
304
305 /*
306 *************************************************************************
307 *
308 * Clear data and metadata from a View.
309 *
310 *************************************************************************
311 */
clear()312 void View::clear()
313 {
314 switch(m_state)
315 {
316 case EMPTY:
317 undescribe();
318 break;
319 case BUFFER:
320 attachBuffer(nullptr);
321 undescribe();
322 break;
323 case EXTERNAL:
324 setExternalDataPtr(nullptr);
325 undescribe();
326 break;
327 case STRING:
328 case SCALAR:
329 unapply();
330 undescribe();
331 break;
332 default:
333 SLIC_ASSERT_MSG(false,
334 SIDRE_VIEW_LOG_PREPEND << "View is in unexpected state: "
335 << getStateStringName(m_state));
336 }
337
338 m_attr_values.clear();
339 m_state = EMPTY;
340 }
341
342 /*
343 *************************************************************************
344 *
345 * Apply data description to data.
346 *
347 *************************************************************************
348 */
apply()349 View* View::apply()
350 {
351 if(!isApplyValid())
352 {
353 SLIC_CHECK_MSG(isApplyValid(),
354 SIDRE_VIEW_LOG_PREPEND
355 << "View's state, '" << getStateStringName(m_state)
356 << "', does not allow apply operation");
357 return this;
358 }
359
360 void* data_pointer = nullptr;
361
362 if(hasBuffer())
363 {
364 data_pointer = m_data_buffer->getVoidPtr();
365 }
366 else
367 {
368 SLIC_ASSERT(m_state == EXTERNAL);
369 data_pointer = m_external_ptr;
370 }
371
372 m_node.set_external(m_schema, data_pointer);
373 m_is_applied = true;
374
375 return this;
376 }
377
378 /*
379 *************************************************************************
380 *
381 * Apply given # elems, offset, stride description to data view.
382 *
383 *************************************************************************
384 */
apply(IndexType num_elems,IndexType offset,IndexType stride)385 View* View::apply(IndexType num_elems, IndexType offset, IndexType stride)
386 {
387 if(num_elems < 0)
388 {
389 SLIC_CHECK_MSG(num_elems >= 0,
390 SIDRE_VIEW_LOG_PREPEND
391 << "Could not apply -- num_elems was less than zero.");
392 return this;
393 }
394
395 DataType dtype(m_schema.dtype());
396 if(dtype.is_empty())
397 {
398 dtype = conduit::DataType::default_dtype(m_data_buffer->getTypeID());
399 }
400
401 const size_t bytes_per_elem = dtype.element_bytes();
402
403 dtype.set_number_of_elements(num_elems);
404 dtype.set_offset(offset * bytes_per_elem);
405 dtype.set_stride(stride * bytes_per_elem);
406
407 describe(dtype);
408
409 apply();
410
411 return this;
412 }
413
414 /*
415 *************************************************************************
416 *
417 * Apply given type, # elems, offset, stride description to data view.
418 *
419 *************************************************************************
420 */
apply(TypeID type,IndexType num_elems,IndexType offset,IndexType stride)421 View* View::apply(TypeID type, IndexType num_elems, IndexType offset, IndexType stride)
422 {
423 if(type == NO_TYPE_ID || num_elems < 0)
424 {
425 SLIC_CHECK_MSG(
426 type != NO_TYPE_ID,
427 SIDRE_VIEW_LOG_PREPEND << "Could not apply -- invalid type.");
428
429 SLIC_CHECK_MSG(num_elems >= 0,
430 SIDRE_VIEW_LOG_PREPEND
431 << "Could not apply -- num_elems was less than zero.");
432
433 return this;
434 }
435
436 DataType dtype = conduit::DataType::default_dtype(type);
437
438 const size_t bytes_per_elem = dtype.element_bytes();
439
440 dtype.set_number_of_elements(num_elems);
441 dtype.set_offset(offset * bytes_per_elem);
442 dtype.set_stride(stride * bytes_per_elem);
443
444 describe(dtype);
445 apply();
446
447 return this;
448 }
449
450 /*
451 *************************************************************************
452 *
453 * Apply given type, number of dimensions and shape to data view.
454 * If ndims is 1 then do not save in m_shape.
455 *
456 *************************************************************************
457 */
apply(TypeID type,int ndims,const IndexType * shape)458 View* View::apply(TypeID type, int ndims, const IndexType* shape)
459 {
460 if(type == NO_TYPE_ID || ndims < 1 || shape == nullptr)
461 {
462 SLIC_CHECK_MSG(
463 type != NO_TYPE_ID,
464 SIDRE_VIEW_LOG_PREPEND << "Could not apply -- invalid type.");
465 SLIC_CHECK_MSG(
466 ndims >= 1,
467 SIDRE_VIEW_LOG_PREPEND << "Could not apply -- ndims was less than one.");
468 SLIC_CHECK_MSG(
469 shape != nullptr,
470 SIDRE_VIEW_LOG_PREPEND << "Could not apply -- shape was null.");
471
472 return this;
473 }
474
475 describe(type, ndims, shape);
476 apply();
477
478 return this;
479 }
480
481 /*
482 *************************************************************************
483 *
484 * Apply a data type description to data view.
485 *
486 *************************************************************************
487 */
apply(const DataType & dtype)488 View* View::apply(const DataType& dtype)
489 {
490 if(dtype.is_empty())
491 {
492 SLIC_CHECK_MSG(!dtype.is_empty(),
493 SIDRE_VIEW_LOG_PREPEND
494 << "Unable to apply description, data type is empty.");
495 return this;
496 }
497
498 describe(dtype);
499 apply();
500
501 return this;
502 }
503
504 /*
505 *************************************************************************
506 *
507 * Get void pointer to any data held by the view.
508 *
509 *************************************************************************
510 */
getVoidPtr() const511 void* View::getVoidPtr() const
512 {
513 void* rv = nullptr;
514
515 switch(m_state)
516 {
517 case EMPTY:
518 break;
519 case EXTERNAL:
520 if(isApplied())
521 {
522 rv = const_cast<void*>(m_node.data_ptr());
523 }
524 else
525 {
526 rv = m_external_ptr; // Opaque
527 }
528 break;
529 case BUFFER:
530 if(isApplied())
531 {
532 rv = const_cast<void*>(m_node.data_ptr());
533 }
534 else
535 {
536 SLIC_CHECK_MSG(false,
537 SIDRE_VIEW_LOG_PREPEND << "View has no applied data.");
538 }
539 break;
540 case STRING:
541 case SCALAR:
542 rv = const_cast<void*>(m_node.data_ptr());
543 break;
544 default:
545 SLIC_ASSERT_MSG(false,
546 SIDRE_VIEW_LOG_PREPEND << "View is in unexpected state: "
547 << getStateStringName(m_state));
548 }
549
550 return rv;
551 }
552
553 /*
554 *************************************************************************
555 *
556 * Set data view to hold external data.
557 *
558 *************************************************************************
559 */
setExternalDataPtr(void * external_ptr)560 View* View::setExternalDataPtr(void* external_ptr)
561 {
562 if(m_state == EMPTY || m_state == EXTERNAL)
563 {
564 if(external_ptr == nullptr)
565 {
566 unapply();
567 m_external_ptr = nullptr;
568 m_state = EMPTY;
569 }
570 else
571 {
572 m_external_ptr = external_ptr;
573 m_state = EXTERNAL;
574
575 if(isDescribed())
576 {
577 apply();
578 }
579 }
580 }
581 else
582 {
583 SLIC_CHECK_MSG(m_state == EMPTY || m_state == EXTERNAL,
584 SIDRE_VIEW_LOG_PREPEND
585 << "Calling setExternalDataPtr on View with "
586 << getStateStringName(m_state) << " data is not allowed.");
587 }
588
589 return this;
590 }
591
592 /*
593 *************************************************************************
594 *
595 * Update the data in this View with the data from other
596 *
597 *************************************************************************
598 */
updateFrom(const View * other)599 View* View::updateFrom(const View* other)
600 {
601 if(!isUpdateableFrom(other))
602 {
603 SLIC_WARNING(SIDRE_VIEW_LOG_PREPEND
604 << "View '" << getPathName() << "' is not updateable "
605 << "from View '" << other->getPathName() << "'");
606 return this;
607 }
608
609 SLIC_WARNING_IF(getTypeID() != other->getTypeID(),
610 SIDRE_VIEW_LOG_PREPEND
611 << "Updating View " << getPathName() << " with type "
612 << getTypeID() << " from View " << other->getPathName()
613 << " with type " << other->getTypeID());
614
615 char* dst = static_cast<char*>(getVoidPtr());
616 dst += getOffset() * getBytesPerElement();
617
618 char* src = static_cast<char*>(other->getVoidPtr());
619 src += other->getOffset() * other->getBytesPerElement();
620
621 copy(dst, src, getTotalBytes());
622
623 return this;
624 }
625
626 /*
627 *************************************************************************
628 *
629 * Return true if view contains allocated data. This could mean a buffer
630 * with allocated data, or a scalar value, or a string.
631 *
632 * Note: Most of our isXXX functions are implemented in the header.
633 * This one is in not, because we are only forward declaring the buffer
634 * class in the view header.
635 *************************************************************************
636 */
isAllocated() const637 bool View::isAllocated() const
638 {
639 bool rv = false;
640
641 switch(m_state)
642 {
643 case EMPTY:
644 break;
645 case BUFFER:
646 // false if buffer is not allocated or view is not described
647 rv = isDescribed() && m_data_buffer->isAllocated();
648 break;
649 case EXTERNAL:
650 case STRING:
651 case SCALAR:
652 rv = true;
653 break;
654 default:
655 SLIC_ASSERT_MSG(false,
656 SIDRE_VIEW_LOG_PREPEND << "View is in unexpected state: "
657 << getStateStringName(m_state));
658 }
659
660 return rv;
661 }
662
663 /*
664 *************************************************************************
665 *
666 * Return number of dimensions and fill in shape information.
667 *
668 *************************************************************************
669 */
getShape(int ndims,IndexType * shape) const670 int View::getShape(int ndims, IndexType* shape) const
671 {
672 if(static_cast<unsigned>(ndims) < m_shape.size())
673 {
674 return -1;
675 }
676
677 const int shapeSize = getNumDimensions();
678 for(int i = 0; i < shapeSize; ++i)
679 {
680 shape[i] = m_shape[i];
681 }
682
683 // Fill the rest of the array with zeros (when ndims > shapeSize)
684 if(ndims > shapeSize)
685 {
686 for(int i = shapeSize; i < ndims; ++i)
687 {
688 shape[i] = 0;
689 }
690 }
691
692 return m_shape.size();
693 }
694
695 /*
696 *************************************************************************
697 *
698 * Return offset from description in terms of number of elements (0 if not
699 * described)
700 *
701 *************************************************************************
702 */
getOffset() const703 IndexType View::getOffset() const
704 {
705 int offset = 0;
706
707 if(isDescribed())
708 {
709 offset = m_schema.dtype().offset();
710
711 const int bytes_per_elem = getBytesPerElement();
712 if(bytes_per_elem != 0)
713 {
714 SLIC_ERROR_IF(
715 offset % bytes_per_elem != 0,
716 SIDRE_VIEW_LOG_PREPEND
717 << "Error calculating offset. "
718 << "Sidre assumes that offsets are given as integral number "
719 << "of elements into the array. In this View, the offset was "
720 << offset << " bytes and each element is " << bytes_per_elem
721 << " bytes. If you have a need for "
722 << "non-integral offsets, please contact the Sidre team");
723
724 offset /= bytes_per_elem;
725 }
726 }
727
728 return static_cast<IndexType>(offset);
729 }
730
731 /*
732 *************************************************************************
733 *
734 * Return stride from description in terms of number of elements (1 if not
735 * described)
736 *
737 *************************************************************************
738 */
getStride() const739 IndexType View::getStride() const
740 {
741 int stride = 1;
742
743 if(isDescribed())
744 {
745 stride = m_schema.dtype().stride();
746
747 const int bytes_per_elem = getBytesPerElement();
748 if(bytes_per_elem != 0)
749 {
750 SLIC_ERROR_IF(
751 stride % bytes_per_elem != 0,
752 SIDRE_VIEW_LOG_PREPEND
753 << "Error caclulating stride. "
754 << "Sidre assumes that strides are given as integral number "
755 << "of elements into the array. In this View, the stride was "
756 << stride << " bytes and each element is " << bytes_per_elem
757 << " bytes. If you have a need for "
758 << "non-integral strides, please contact the Sidre team");
759
760 stride /= bytes_per_elem;
761 }
762 }
763
764 return static_cast<IndexType>(stride);
765 }
766
767 /*
768 *************************************************************************
769 *
770 * Test equivalence of two Views
771 *
772 *************************************************************************
773 */
isEquivalentTo(const View * other) const774 bool View::isEquivalentTo(const View* other) const
775 {
776 //add isAllocated() if it can be declared const
777 return (getName() == other->getName()) && (getTypeID() == other->getTypeID()) &&
778 (isApplied() == other->isApplied()) && (hasBuffer() == other->hasBuffer()) &&
779 (getTotalBytes() == other->getTotalBytes());
780 }
781
782 /*
783 *************************************************************************
784 *
785 * Test whether the two Views can update each other
786 *
787 *************************************************************************
788 */
isUpdateableFrom(const View * other) const789 bool View::isUpdateableFrom(const View* other) const
790 {
791 const bool valid_state = (m_state == BUFFER) || (m_state == EXTERNAL);
792 const bool other_valid_state =
793 (other->m_state == BUFFER) || (other->m_state == EXTERNAL);
794 const bool same_length = (getTotalBytes() == other->getTotalBytes());
795 const bool unit_stride = (getStride() == 1) && (other->getStride() == 1);
796
797 return valid_state && other_valid_state && same_length && unit_stride;
798 }
799
800 /*
801 *************************************************************************
802 *
803 * Print JSON description of data view to stdout.
804 *
805 *************************************************************************
806 */
print() const807 void View::print() const { print(std::cout); }
808
809 /*
810 *************************************************************************
811 *
812 * Print JSON description of data view to an ostream.
813 *
814 *************************************************************************
815 */
print(std::ostream & os) const816 void View::print(std::ostream& os) const
817 {
818 Node n;
819 copyToConduitNode(n);
820 n.to_json_stream(os);
821 }
822
823 /*
824 *************************************************************************
825 *
826 * Copy data view description to given Conduit node.
827 *
828 *************************************************************************
829 */
copyToConduitNode(Node & n) const830 void View::copyToConduitNode(Node& n) const
831 {
832 n["name"] = m_name;
833 n["schema"] = m_schema.to_json();
834 n["value"] = m_node.to_json();
835 n["state"] = getStateStringName(m_state);
836 n["is_applied"] = m_is_applied;
837 }
838
839 /*
840 *************************************************************************
841 *
842 * Copy data view native layout to given Conduit node.
843 *
844 *************************************************************************
845 */
createNativeLayout(Node & n) const846 void View::createNativeLayout(Node& n) const
847 {
848 // see ATK-726 - Handle undescribed and unallocated views in Sidre's
849 // createNativeLayout()
850 // TODO: Need to handle cases where the view is not described
851 // TODO: Need to handle cases where the view is not allocated
852 // TODO: Need to handle cases where the view is not applied
853
854 // Note: We are using conduit's pointer rather than the View pointer
855 // since the conduit pointer handles offsetting
856 // Note: const_cast the pointer to satisfy conduit's interface
857 void* data_ptr = const_cast<void*>(m_node.data_ptr());
858 n.set_external(m_node.schema(), data_ptr);
859 }
860
861 /*
862 *************************************************************************
863 *
864 * PRIVATE ctor for View not associated with any data.
865 *
866 *************************************************************************
867 */
View(const std::string & name)868 View::View(const std::string& name)
869 : m_name(name)
870 , m_index(InvalidIndex)
871 , m_owning_group(nullptr)
872 , m_data_buffer(nullptr)
873 , m_schema()
874 , m_node()
875 , m_shape()
876 , m_external_ptr(nullptr)
877 , m_state(EMPTY)
878 , m_is_applied(false)
879 { }
880
881 /*
882 *************************************************************************
883 *
884 * PRIVATE dtor.
885 *
886 *************************************************************************
887 */
~View()888 View::~View()
889 {
890 if(m_data_buffer != nullptr)
891 {
892 m_data_buffer->detachFromView(this);
893 }
894 }
895
896 /*
897 *************************************************************************
898 *
899 * PRIVATE method to describe data view with type and number of elements.
900 * Caller has already checked arguments.
901 *
902 *************************************************************************
903 */
describe(TypeID type,IndexType num_elems)904 void View::describe(TypeID type, IndexType num_elems)
905 {
906 DataType dtype = conduit::DataType::default_dtype(type);
907 dtype.set_number_of_elements(num_elems);
908 m_schema.set(dtype);
909 describeShape();
910 m_is_applied = false;
911 }
912
913 /*
914 *************************************************************************
915 *
916 * PRIVATE method to describe data view with type, number of dimensions,
917 * and number of elements per dimension.
918 * Caller has already checked arguments.
919 *
920 *************************************************************************
921 */
describe(TypeID type,int ndims,const IndexType * shape)922 void View::describe(TypeID type, int ndims, const IndexType* shape)
923 {
924 IndexType num_elems = 0;
925 if(ndims > 0)
926 {
927 num_elems = shape[0];
928 for(int i = 1; i < ndims; i++)
929 {
930 num_elems *= shape[i];
931 }
932 }
933
934 describe(type, num_elems);
935 describeShape(ndims, shape);
936 }
937
938 /*
939 *************************************************************************
940 *
941 * PRIVATE method to describe data view with a Conduit data type object.
942 * Caller has already checked arguments.
943 *
944 *************************************************************************
945 */
describe(const DataType & dtype)946 void View::describe(const DataType& dtype)
947 {
948 m_schema.set(dtype);
949 describeShape();
950 m_is_applied = false;
951 }
952
953 /*
954 *************************************************************************
955 *
956 * PRIVATE method set shape to described length.
957 * This is called after describe to set the shape.
958 *
959 *************************************************************************
960 */
describeShape()961 void View::describeShape()
962 {
963 m_shape.clear();
964 m_shape.push_back(m_schema.dtype().number_of_elements());
965 }
966
967 /*
968 *************************************************************************
969 *
970 * PRIVATE method set shape from user input.
971 *
972 *************************************************************************
973 */
describeShape(int ndims,const IndexType * shape)974 void View::describeShape(int ndims, const IndexType* shape)
975 {
976 m_shape.clear();
977 for(int i = 0; i < ndims; i++)
978 {
979 m_shape.push_back(shape[i]);
980 }
981 }
982
983 /*
984 *************************************************************************
985 *
986 * PRIVATE method copy the contents of this into a undescribed EMPTY view.
987 *
988 *************************************************************************
989 */
copyView(View * copy) const990 void View::copyView(View* copy) const
991 {
992 SLIC_ASSERT_MSG(copy->m_state == EMPTY && !copy->isDescribed(),
993 SIDRE_VIEW_LOG_PREPEND
994 << "copyView can only copy into undescribed view "
995 << "with empty state.");
996
997 if(isDescribed())
998 {
999 copy->describe(m_schema.dtype());
1000 }
1001
1002 switch(m_state)
1003 {
1004 case EMPTY:
1005 // Nothing more to do
1006 break;
1007 case STRING:
1008 case SCALAR:
1009 copy->m_node = m_node;
1010 copy->m_state = m_state;
1011 copy->m_is_applied = true;
1012 break;
1013 case EXTERNAL:
1014 copy->setExternalDataPtr(m_external_ptr);
1015 break;
1016 case BUFFER:
1017 copy->attachBuffer(m_data_buffer);
1018 break;
1019 default:
1020 SLIC_ASSERT_MSG(false,
1021 SIDRE_VIEW_LOG_PREPEND << "View is in unexpected state: "
1022 << getStateStringName(m_state));
1023 }
1024 }
1025
1026 /*
1027 *************************************************************************
1028 *
1029 * PRIVATE method returns true if view can allocate data; else false.
1030 *
1031 * This method does not need to emit the view state as part of it's
1032 * checking. The caller functions are already printing out the view
1033 * state if this function returns false.
1034 *************************************************************************
1035 */
isAllocateValid() const1036 bool View::isAllocateValid() const
1037 {
1038 bool rv = false;
1039
1040 switch(m_state)
1041 {
1042 case EMPTY:
1043 rv = isDescribed();
1044 break;
1045 case STRING:
1046 case SCALAR:
1047 case EXTERNAL:
1048 SLIC_CHECK_MSG(false,
1049 SIDRE_VIEW_LOG_PREPEND
1050 << "Allocate is not valid for view in '"
1051 << getStateStringName(m_state) << "' state.");
1052 break;
1053 case BUFFER:
1054 rv = isDescribed() && m_data_buffer->getNumViews() == 1;
1055 break;
1056 default:
1057 SLIC_ASSERT_MSG(false,
1058 SIDRE_VIEW_LOG_PREPEND << "View is in unexpected state: "
1059 << getStateStringName(m_state));
1060 }
1061
1062 return rv;
1063 }
1064
1065 /*
1066 *************************************************************************
1067 *
1068 * PRIVATE method returns true if apply is a valid operation on view;
1069 * else false.
1070 *
1071 * For an EXTERNAL view, assume user provided m_external_ptr and
1072 * description are consistent. This includes m_external_ptr == NULL.
1073 *
1074 *************************************************************************
1075 */
isApplyValid() const1076 bool View::isApplyValid() const
1077 {
1078 bool rv = false;
1079
1080 if(!isDescribed())
1081 {
1082 SLIC_CHECK_MSG(
1083 false,
1084 SIDRE_VIEW_LOG_PREPEND
1085 << "Apply is not valid. View does not have a description.");
1086 return rv;
1087 }
1088
1089 switch(m_state)
1090 {
1091 case EMPTY:
1092 case STRING:
1093 case SCALAR:
1094 SLIC_CHECK_MSG(false,
1095 SIDRE_VIEW_LOG_PREPEND
1096 << "Apply is not valid for View with state '"
1097 << getStateStringName(m_state) << "'.'");
1098 break;
1099 case EXTERNAL:
1100 SLIC_ASSERT(m_external_ptr != nullptr);
1101 rv = isDescribed();
1102 break;
1103 case BUFFER:
1104 rv =
1105 0 <= getTotalBytes() && getTotalBytes() <= m_data_buffer->getTotalBytes();
1106 SLIC_CHECK_MSG(
1107 0 <= getTotalBytes(),
1108 SIDRE_VIEW_LOG_PREPEND << "Apply is not valid on data with zero length.");
1109 SLIC_CHECK_MSG(getTotalBytes() <= m_data_buffer->getTotalBytes(),
1110 SIDRE_VIEW_LOG_PREPEND
1111 << "Apply is not valid. "
1112 << "View's datatype length exceeds bytes in buffer.");
1113 break;
1114 default:
1115 SLIC_ASSERT_MSG(false,
1116 SIDRE_VIEW_LOG_PREPEND << "View is in unexpected state: "
1117 << getStateStringName(m_state));
1118 }
1119
1120 return rv;
1121 }
1122
1123 /*
1124 *************************************************************************
1125 *
1126 * PRIVATE method returns string name of given view state enum value.
1127 *
1128 *************************************************************************
1129 */
getStateStringName(State state)1130 char const* View::getStateStringName(State state)
1131 {
1132 char const* ret_string = NULL;
1133
1134 switch(state)
1135 {
1136 case EMPTY:
1137 ret_string = "EMPTY";
1138 break;
1139 case BUFFER:
1140 ret_string = "BUFFER";
1141 break;
1142 case EXTERNAL:
1143 ret_string = "EXTERNAL";
1144 break;
1145 case SCALAR:
1146 ret_string = "SCALAR";
1147 break;
1148 case STRING:
1149 ret_string = "STRING";
1150 break;
1151 default:
1152 ret_string = "UNKNOWN";
1153 }
1154
1155 return ret_string;
1156 }
1157
1158 /*
1159 *************************************************************************
1160 *
1161 * PRIVATE method returns state enum value when given string with a
1162 * state name.
1163 *
1164 *************************************************************************
1165 */
getStateId(const std::string & name) const1166 View::State View::getStateId(const std::string& name) const
1167 {
1168 State res = EMPTY;
1169 if(name == "EMPTY")
1170 {
1171 res = EMPTY;
1172 }
1173 else if(name == "BUFFER")
1174 {
1175 res = BUFFER;
1176 }
1177 else if(name == "EXTERNAL")
1178 {
1179 res = EXTERNAL;
1180 }
1181 else if(name == "SCALAR")
1182 {
1183 res = SCALAR;
1184 }
1185 else if(name == "STRING")
1186 {
1187 res = STRING;
1188 }
1189 else if(name == "UNKNOWN")
1190 {
1191 res = EMPTY;
1192 }
1193
1194 return res;
1195 }
1196
1197 /*
1198 *************************************************************************
1199 *
1200 * PRIVATE method to copy view data to given Conduit node using
1201 * given set of ids to maintain correct association of data buffers
1202 * to data views.
1203 *
1204 *************************************************************************
1205 */
exportTo(conduit::Node & data_holder,std::set<IndexType> & buffer_indices) const1206 void View::exportTo(conduit::Node& data_holder,
1207 std::set<IndexType>& buffer_indices) const
1208 {
1209 data_holder["state"] = getStateStringName(m_state);
1210 exportAttribute(data_holder);
1211
1212 switch(m_state)
1213 {
1214 case EMPTY:
1215 if(isDescribed())
1216 {
1217 exportDescription(data_holder);
1218 }
1219 break;
1220 case BUFFER:
1221 {
1222 IndexType buffer_id = getBuffer()->getIndex();
1223 data_holder["buffer_id"] = buffer_id;
1224 if(isDescribed())
1225 {
1226 exportDescription(data_holder);
1227 }
1228 data_holder["is_applied"] = static_cast<unsigned char>(m_is_applied);
1229 buffer_indices.insert(buffer_id);
1230 break;
1231 }
1232 case EXTERNAL:
1233 if(isDescribed())
1234 {
1235 exportDescription(data_holder);
1236 }
1237 else
1238 {
1239 // If there is no description, make it an EMPTY view
1240 data_holder["state"] = getStateStringName(EMPTY);
1241 }
1242 break;
1243 case SCALAR:
1244 case STRING:
1245 data_holder["value"] = getNode();
1246 break;
1247 default:
1248 SLIC_ASSERT_MSG(false,
1249 SIDRE_VIEW_LOG_PREPEND << "View is in unexpected state: "
1250 << getStateStringName(m_state));
1251 }
1252 }
1253
1254 /*
1255 *************************************************************************
1256 * TODO
1257 *
1258 *************************************************************************
1259 */
importFrom(conduit::Node & data_holder,const std::map<IndexType,IndexType> & buffer_id_map)1260 void View::importFrom(conduit::Node& data_holder,
1261 const std::map<IndexType, IndexType>& buffer_id_map)
1262 {
1263 m_state = getStateId(data_holder["state"].as_string());
1264 importAttribute(data_holder);
1265
1266 switch(m_state)
1267 {
1268 case EMPTY:
1269 importDescription(data_holder);
1270 break;
1271 case BUFFER:
1272 {
1273 // If view has a buffer, the easiest way to restore it is to use a series of
1274 // API calls.
1275 // Start from scratch
1276 m_state = EMPTY;
1277
1278 IndexType old_buffer_id = data_holder["buffer_id"].to_int64();
1279 bool is_applied = data_holder["is_applied"].as_unsigned_char();
1280
1281 SLIC_ASSERT_MSG(buffer_id_map.find(old_buffer_id) != buffer_id_map.end(),
1282 SIDRE_VIEW_LOG_PREPEND << "Buffer id map is old."
1283 << "New id entry for buffer "
1284 << old_buffer_id);
1285
1286 Buffer* buffer =
1287 m_owning_group->getDataStore()->getBuffer(buffer_id_map.at(old_buffer_id));
1288
1289 importDescription(data_holder);
1290 attachBuffer(buffer);
1291 if(is_applied)
1292 {
1293 apply();
1294 }
1295 break;
1296 }
1297 case EXTERNAL:
1298 importDescription(data_holder);
1299 break;
1300 case SCALAR:
1301 case STRING:
1302 m_node = data_holder["value"];
1303 m_schema.set(m_node.schema());
1304 m_is_applied = true;
1305 break;
1306 default:
1307 SLIC_ASSERT_MSG(false,
1308 SIDRE_VIEW_LOG_PREPEND << "View is in unexpected state: "
1309 << getStateStringName(m_state));
1310 }
1311 }
1312
1313 /*
1314 *************************************************************************
1315 *
1316 * Import Node holding an array into a View with an attached Buffer.
1317 *
1318 *************************************************************************
1319 */
importArrayNode(const Node & array)1320 View* View::importArrayNode(const Node& array)
1321 {
1322 conduit::DataType array_dtype = array.dtype();
1323
1324 if(array_dtype.is_number())
1325 {
1326 if(m_state == BUFFER)
1327 {
1328 setBufferViewToEmpty();
1329 }
1330 if(m_state == EMPTY)
1331 {
1332 Buffer* buff = m_owning_group->getDataStore()->createBuffer();
1333
1334 conduit::index_t num_ele = array_dtype.number_of_elements();
1335 conduit::index_t ele_bytes = DataType::default_bytes(array_dtype.id());
1336
1337 buff->allocate((TypeID)array_dtype.id(), num_ele);
1338
1339 // copy the data in a way that matches
1340 // to compact representation of the buffer
1341 conduit::uint8* data_ptr = (conduit::uint8*)buff->getVoidPtr();
1342 for(conduit::index_t i = 0; i < num_ele; i++)
1343 {
1344 memcpy(data_ptr, array.element_ptr(i), ele_bytes);
1345 data_ptr += ele_bytes;
1346 }
1347
1348 attachBuffer(buff);
1349
1350 // it is important to not use the data type directly
1351 // it could contain offsets that are no longer
1352 // valid our new buffer
1353 apply((TypeID)array_dtype.id(), array_dtype.number_of_elements());
1354 }
1355 else
1356 {
1357 SLIC_CHECK_MSG(m_state == EMPTY,
1358 SIDRE_VIEW_LOG_PREPEND
1359 << "Unable to import array Node to View with state: "
1360 << getStateStringName(m_state));
1361 }
1362 }
1363 else
1364 {
1365 SLIC_CHECK_MSG(array_dtype.is_number(),
1366 SIDRE_VIEW_LOG_PREPEND
1367 << "Unable to import array from Node of type: "
1368 << array_dtype.name());
1369 }
1370
1371 return this;
1372 }
1373
1374 /*
1375 *************************************************************************
1376 *
1377 * PRIVATE method to save view's description to a conduit tree.
1378 * The shape information is only written if there is more than
1379 * one dimension.
1380 *
1381 *************************************************************************
1382 */
exportDescription(conduit::Node & data_holder) const1383 void View::exportDescription(conduit::Node& data_holder) const
1384 {
1385 data_holder["schema"] = m_schema.to_json();
1386 if(getNumDimensions() > 1)
1387 {
1388 data_holder["shape"].set(m_shape);
1389 }
1390 }
1391
1392 /*
1393 *************************************************************************
1394 *
1395 * PRIVATE method to restore a view's description from a conduit tree.
1396 *
1397 *************************************************************************
1398 */
importDescription(conduit::Node & data_holder)1399 void View::importDescription(conduit::Node& data_holder)
1400 {
1401 if(data_holder.has_path("schema"))
1402 {
1403 conduit::Schema schema(data_holder["schema"].as_string());
1404 describe(schema.dtype());
1405 if(data_holder.has_path("shape"))
1406 {
1407 Node& n = data_holder["shape"];
1408 IndexType* shape = n.value();
1409 int ndims = n.dtype().number_of_elements();
1410 describeShape(ndims, shape);
1411 }
1412 }
1413 }
1414
1415 /*
1416 *************************************************************************
1417 *
1418 * PRIVATE method to save view's attributes to a conduit tree.
1419 * Only add "attribute" Node if the View has any attributes.
1420 *
1421 *************************************************************************
1422 */
exportAttribute(conduit::Node & data_holder) const1423 void View::exportAttribute(conduit::Node& data_holder) const
1424 {
1425 IndexType aidx = getFirstValidAttrValueIndex();
1426
1427 if(aidx == InvalidIndex)
1428 {
1429 return;
1430 }
1431
1432 Node& node = data_holder["attribute"];
1433 node.set(DataType::object());
1434
1435 while(indexIsValid(aidx))
1436 {
1437 const Attribute* attr = getAttribute(aidx);
1438
1439 node[attr->getName()] = getAttributeNodeRef(attr);
1440
1441 aidx = getNextValidAttrValueIndex(aidx);
1442 }
1443 }
1444
1445 /*
1446 *************************************************************************
1447 *
1448 * PRIVATE method to restore a view's attributes from a conduit tree.
1449 *
1450 *************************************************************************
1451 */
importAttribute(conduit::Node & data_holder)1452 void View::importAttribute(conduit::Node& data_holder)
1453 {
1454 if(data_holder.has_path("attribute"))
1455 {
1456 conduit::NodeIterator attrs_itr = data_holder["attribute"].children();
1457 while(attrs_itr.has_next())
1458 {
1459 Node& n_attr = attrs_itr.next();
1460 std::string attr_name = attrs_itr.name();
1461
1462 Attribute* attr = getAttribute(attr_name);
1463 if(attr != nullptr)
1464 {
1465 m_attr_values.setNode(attr, n_attr);
1466 }
1467 }
1468 }
1469 }
1470
1471 /*
1472 *************************************************************************
1473 *
1474 * Rename this View with a new string name.
1475 *
1476 *************************************************************************
1477 */
rename(const std::string & new_name)1478 bool View::rename(const std::string& new_name)
1479 {
1480 bool do_rename = true;
1481 if(new_name != m_name)
1482 {
1483 Group* parent = getOwningGroup();
1484 SLIC_CHECK(parent != nullptr);
1485
1486 if(new_name.empty())
1487 {
1488 SLIC_WARNING(SIDRE_VIEW_LOG_PREPEND
1489 << "Cannot rename View to an empty string.");
1490 do_rename = false;
1491 }
1492 else if(new_name.find(parent->getPathDelimiter()) != std::string::npos)
1493 {
1494 SLIC_WARNING(SIDRE_VIEW_LOG_PREPEND
1495 << "Cannot rename View " << getPathName() << " to path name '"
1496 << new_name << "'. Only strings without path delimiters can "
1497 << "be passed into the rename method.");
1498 do_rename = false;
1499 }
1500 else if(parent->hasGroup(new_name) || parent->hasView(new_name))
1501 {
1502 SLIC_WARNING(SIDRE_VIEW_LOG_PREPEND
1503 << "Parent group '" << parent->getPathName()
1504 << "' already has a child object named " << new_name << ". "
1505 << "View " << getPathName() << " will not be renamed.");
1506 do_rename = false;
1507 }
1508 else
1509 {
1510 View* detached_view = parent->detachView(m_name);
1511 SLIC_CHECK(detached_view == this);
1512
1513 m_name = new_name;
1514
1515 View* attached_view = parent->attachView(detached_view);
1516 AXOM_UNUSED_VAR(attached_view);
1517 SLIC_CHECK(attached_view == this);
1518 }
1519 }
1520
1521 return do_rename;
1522 }
1523
1524 /*
1525 *************************************************************************
1526 *
1527 * Return pointer to Attribute from Attribute index.
1528 *
1529 *************************************************************************
1530 */
getAttribute(IndexType idx)1531 Attribute* View::getAttribute(IndexType idx)
1532 {
1533 Attribute* attr = getOwningGroup()->getDataStore()->getAttribute(idx);
1534 return attr;
1535 }
1536
1537 /*
1538 *************************************************************************
1539 *
1540 * Return pointer to Attribute from Attribute index.
1541 *
1542 *************************************************************************
1543 */
getAttribute(IndexType idx) const1544 const Attribute* View::getAttribute(IndexType idx) const
1545 {
1546 const Attribute* attr = getOwningGroup()->getDataStore()->getAttribute(idx);
1547 return attr;
1548 }
1549
1550 /*
1551 *************************************************************************
1552 *
1553 * Return pointer to Attribute from Attribute name.
1554 *
1555 *************************************************************************
1556 */
getAttribute(const std::string & name)1557 Attribute* View::getAttribute(const std::string& name)
1558 {
1559 Attribute* attr = getOwningGroup()->getDataStore()->getAttribute(name);
1560 return attr;
1561 }
1562
1563 /*
1564 *************************************************************************
1565 *
1566 * Return pointer to Attribute from Attribute name.
1567 *
1568 *************************************************************************
1569 */
getAttribute(const std::string & name) const1570 const Attribute* View::getAttribute(const std::string& name) const
1571 {
1572 const Attribute* attr = getOwningGroup()->getDataStore()->getAttribute(name);
1573 return attr;
1574 }
1575
1576 /*
1577 *************************************************************************
1578 *
1579 * Set Attribute for a View from Attribute name.
1580 *
1581 *************************************************************************
1582 */
setAttributeString(IndexType idx,const std::string & value)1583 bool View::setAttributeString(IndexType idx, const std::string& value)
1584 {
1585 const Attribute* attr = getAttribute(idx);
1586
1587 if(attr == nullptr)
1588 {
1589 return false;
1590 }
1591
1592 return m_attr_values.setString(attr, value);
1593 }
1594
1595 /*
1596 *************************************************************************
1597 *
1598 * Set Attribute for a View from Attribute name.
1599 *
1600 *************************************************************************
1601 */
setAttributeString(const std::string & name,const std::string & value)1602 bool View::setAttributeString(const std::string& name, const std::string& value)
1603 {
1604 const Attribute* attr = getAttribute(name);
1605
1606 if(attr == nullptr)
1607 {
1608 return false;
1609 }
1610
1611 return m_attr_values.setString(attr, value);
1612 }
1613
1614 /*
1615 *************************************************************************
1616 *
1617 * Set Attribute for a View from Attribute pointer.
1618 *
1619 *************************************************************************
1620 */
setAttributeString(const Attribute * attr,const std::string & value)1621 bool View::setAttributeString(const Attribute* attr, const std::string& value)
1622 {
1623 if(attr == nullptr)
1624 {
1625 SLIC_CHECK_MSG(attr != nullptr,
1626 SIDRE_VIEW_LOG_PREPEND
1627 << "setAttributeString: called with a null Attribute");
1628 return false;
1629 }
1630
1631 return m_attr_values.setString(attr, value);
1632 }
1633
1634 /*
1635 *************************************************************************
1636 *
1637 * Return a string attribute from the Attribute index.
1638 *
1639 * If the value has not been explicitly set, return the current default.
1640 *
1641 *************************************************************************
1642 */
getAttributeString(IndexType idx) const1643 const char* View::getAttributeString(IndexType idx) const
1644 {
1645 const Attribute* attr = getAttribute(idx);
1646
1647 if(attr == nullptr)
1648 {
1649 return nullptr;
1650 }
1651
1652 return m_attr_values.getString(attr);
1653 }
1654
1655 /*
1656 *************************************************************************
1657 *
1658 * Return a string attribute from the Attribute name.
1659 *
1660 * If the value has not been explicitly set, return the current default.
1661 *
1662 *************************************************************************
1663 */
getAttributeString(const std::string & name) const1664 const char* View::getAttributeString(const std::string& name) const
1665 {
1666 const Attribute* attr = getAttribute(name);
1667
1668 if(attr == nullptr)
1669 {
1670 return nullptr;
1671 }
1672
1673 return m_attr_values.getString(attr);
1674 }
1675
1676 /*
1677 *************************************************************************
1678 *
1679 * Return a string attribute from the Attribute pointer.
1680 *
1681 * If the value has not been explicitly set, return the current default.
1682 *
1683 *************************************************************************
1684 */
getAttributeString(const Attribute * attr) const1685 const char* View::getAttributeString(const Attribute* attr) const
1686 {
1687 if(attr == nullptr)
1688 {
1689 SLIC_CHECK_MSG(attr != nullptr,
1690 SIDRE_VIEW_LOG_PREPEND
1691 << "getAttributeString: called with a null Attribute");
1692 return nullptr;
1693 }
1694
1695 return m_attr_values.getString(attr);
1696 }
1697
1698 /*
1699 *************************************************************************
1700 *
1701 * PRIVATE method to return a valid umpire::Allocator ID.
1702 *
1703 *************************************************************************
1704 */
getValidAllocatorID(int allocID)1705 int View::getValidAllocatorID(int allocID)
1706 {
1707 #ifdef AXOM_USE_UMPIRE
1708 if(allocID == INVALID_ALLOCATOR_ID)
1709 {
1710 allocID = getOwningGroup()->getDefaultAllocatorID();
1711 }
1712 #endif
1713
1714 return allocID;
1715 }
1716
1717 } /* end namespace sidre */
1718 } /* end namespace axom */
1719