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 /*!
7  ******************************************************************************
8  *
9  * \file View.hpp
10  *
11  * \brief   Header file containing definition of View class.
12  *
13  ******************************************************************************
14  */
15 
16 #ifndef SIDRE_VIEW_HPP_
17 #define SIDRE_VIEW_HPP_
18 
19 // Standard C++ headers
20 #include <string>
21 #include <set>
22 
23 // Other axom headers
24 #include "axom/config.hpp"
25 #include "axom/core/memory_management.hpp"
26 #include "axom/core/Macros.hpp"
27 #include "axom/core/Types.hpp"
28 #include "axom/slic.hpp"
29 
30 // Sidre headers
31 #include "axom/sidre/core/SidreTypes.hpp"
32 #include "axom/sidre/core/AttrValues.hpp"
33 
34 namespace axom
35 {
36 namespace sidre
37 {
38 // Helper macro for defining a prepend string for sidre::View log messages
39 // We are using it to add the pathName() of the view
40 #ifndef SIDRE_VIEW_LOG_PREPEND
41   #define SIDRE_VIEW_LOG_PREPEND "[View: '" << this->getPathName() << "'] "
42 #endif
43 
44 class Buffer;
45 class Group;
46 class DataStore;
47 class Attribute;
48 
49 /*!
50  * \class View
51  *
52  * \brief A View object describes data, which may be
53  *        owned by the view object (e.g., via an attached Buffer) or
54  *        owned externally.
55  *
56  * The View class has the following properties:
57  *
58  *    - View objects can only be created via the Group interface,
59  *      not constructed directly. A View object is owned by the Group
60  *      object that creates it. A View object owned by a Group object
61  *      that is a descendant of some ancestor Group is a descendant
62  *      View of the ancestor Group.
63  *    - A View object has a unique name (string) within the Group
64  *      that owns it.
65  *    - A View holds a pointer to the Group that created it and which
66  *      owns it.
67  *    - A View object can describe and provide access to data in one of
68  *      four ways:
69  *        * A view can describe (a subset of) data owned by an existing
70  *          Buffer. In this case, the data can be (re)allocated or
71  *          deallocated by the view if and only if it is the only view
72  *          attached to the buffer.
73  *        * A view can describe and allocate data using semantics similar
74  *          to Buffer data description and allocation. In this case, no
75  *          other view is allowed to (re)allocate or deallocate the data held
76  *          by the associated data buffer.
77  *        * A view can describe data associated with a pointer to an
78  *          "external" data object. In this case, the view cannot (re)allocate
79  *          or deallocate the data. However, all other view operations are
80  *          essentially the same as the previous two cases.
81  *        * It can hold a pointer to an undescribed (i.e., "opaque") data
82  *          object. In this case, the view knows nothing about the type or
83  *          structure of the data; it is essentially just a handle to the data.
84  *    - For any View object that is "external" or associated with a
85  *      Buffer, the data description of the view may be specified, or
86  *      changed, by calling one of the apply() methods.
87  *
88  */
89 class View
90 {
91 public:
92   //
93   // Friend declaration to constrain usage via controlled access to
94   // private members.
95   //
96   friend class Group;
97   friend class Buffer;
98 
99   //@{
100   //!  @name View query and accessor methods
101 
102   /*!
103    * \brief Return index of View within owning Group.
104    *
105    * If View is detached, return sidre::InvalidIndex.
106    */
getIndex() const107   IndexType getIndex() const { return m_index; }
108 
109   /*!
110    * \brief Return const reference to name of View.
111    *
112    * \sa getPath(), getPathName()
113    */
getName() const114   const std::string& getName() const { return m_name; }
115 
116   /*!
117    * \brief Return path of View's owning Group object.
118    *
119    * \sa getName(), getPathName()
120    */
121   std::string getPath() const;
122 
123   /*!
124    * \brief Return full path of View object, including its name.
125    *
126    * If a DataStore contains a Group tree structure a/b/c/d/e, with
127    * group d owning a view v, the following results are expected:
128    *
129    * Method Call      | Result
130    * -----------------|----------
131    * v->getName()     | v
132    * v->getPath()     | a/b/c/d
133    * v->getPathName() | a/b/c/d/v
134    *
135    * \sa getName(), getPath(), Group::getPathName()
136    */
137   std::string getPathName() const;
138 
139   /*!
140    * \brief Return pointer to non-const Group that owns View object.
141    */
getOwningGroup()142   Group* getOwningGroup() { return m_owning_group; }
143 
144   /*!
145    * \brief Return pointer to const Group that owns View object.
146    */
getOwningGroup() const147   const Group* getOwningGroup() const { return m_owning_group; }
148 
149   /*!
150    * \brief Return true if view has a an associated Buffer object
151    *        (e.g., view is not opaque, it has been created with a buffer,
152    *         it has been allocated, etc.); false otherwise.
153    */
hasBuffer() const154   bool hasBuffer() const { return m_data_buffer != nullptr; }
155 
156   /*!
157    * \brief Return pointer to non-const Buffer associated with View.
158    */
getBuffer()159   Buffer* getBuffer() { return m_data_buffer; }
160 
161   /*!
162    * \brief Return pointer to const Buffer associated with View.
163    */
getBuffer() const164   const Buffer* getBuffer() const { return m_data_buffer; }
165 
166   /*!
167    * \brief Return true if view holds external data; false otherwise.
168    */
isExternal() const169   bool isExternal() const { return m_state == EXTERNAL; }
170 
171   /*!
172    * \brief Return true if view is described and refers to a buffer
173    * that has been allocated.
174    */
175   bool isAllocated() const;
176 
177   /*!
178    * \brief Return true if data description (schema) has been applied to data
179    *        in buffer associated with view; false otherwise.
180    */
isApplied() const181   bool isApplied() const { return m_is_applied; }
182 
183   /*!
184    * \brief Return true if data description exists.  It may/may not have been
185    * applied to the data yet.  ( Check isApplied() for that. )
186    */
isDescribed() const187   bool isDescribed() const { return !m_schema.dtype().is_empty(); }
188 
189   /*!
190    * \brief Return true if view is empty.
191    */
isEmpty() const192   bool isEmpty() const { return m_state == EMPTY; }
193 
194   /*!
195    * \brief Convenience function that returns true if view is opaque
196    *        (i.e., has access to data but has no knowledge of the data
197    *        type or structure); false otherwise.
198    */
isOpaque() const199   bool isOpaque() const { return m_state == EXTERNAL && !isApplied(); }
200 
201   /*!
202    * \brief Return true if view contains a scalar value.
203    */
isScalar() const204   bool isScalar() const { return m_state == SCALAR; }
205 
206   /*!
207    * \brief Return true if view contains a string value.
208    */
isString() const209   bool isString() const { return m_state == STRING; }
210 
211   /*!
212    * \brief Return type of data for this View object.
213    *        Return NO_TYPE_ID for an undescribed view.
214    */
getTypeID() const215   TypeID getTypeID() const
216   {
217     if(isDescribed())
218     {
219       return static_cast<TypeID>(m_schema.dtype().id());
220     }
221     else
222     {
223       return NO_TYPE_ID;
224     }
225   }
226 
227   /*!
228    * \brief Return total number of bytes associated with this View object.
229    *
230    * \attention This is the total bytes described by the view; they may not
231    *            yet be allocated.
232    */
getTotalBytes() const233   IndexType getTotalBytes() const { return m_schema.total_strided_bytes(); }
234 
235   /*!
236    * \brief Return total number of elements described by this View object.
237    *
238    * \attention This is the number of elements described by the view;
239    *            they may not yet be allocated.
240    */
getNumElements() const241   IndexType getNumElements() const
242   {
243     return m_schema.dtype().number_of_elements();
244   }
245 
246   /*!
247    * \brief Return number of bytes per element in the described view.
248    *
249    * \attention This is the number of bytes per element described by the view
250    *            which may not yet be allocated.
251    */
getBytesPerElement() const252   IndexType getBytesPerElement() const
253   {
254     return m_schema.dtype().element_bytes();
255   }
256 
257   /*!
258    * \brief Return the offset in number of elements for the data described by
259    *  this View object.
260    *
261    * \warning The code currently assumes that offsets into a view are given in
262    *  terms of whole elements.  It is an assertion error if this is not the
263    *  case.  If you have a different use case, please talk to the Sidre team.
264    *
265    * \note View::getData() and View::getArray() already account for the offset
266    *        and return a pointer to the first element in the array:
267    *        View::getVoidPtr() does not account for the offset.
268    *
269    * \attention This function is based on the view description.  It does not
270    * imply that the data is allocated.
271    *
272    * \return The offset, in terms of the number of elements, from the described
273    *  array to the first element.
274    */
275   IndexType getOffset() const;
276 
277   /*!
278    * \brief Return the stride in number of elements for the data described by
279    *  this View object.
280    *
281    * \warning The code currently assumes that strides into a view are given in
282    *  terms of whole elements.  It is an assertion error if this is not the
283    *  case.  If you have a different use case, please talk to the Sidre team.
284    *
285    * \attention This function is based on the view description.  It does not
286    * imply that the data is allocated.
287    *
288    * \return The stride, in terms of the number of elements, between elements in
289    *  the described array.
290    */
291   IndexType getStride() const;
292 
293   /*!
294    * \brief Return dimensionality of this View's data.
295    *
296    * \sa getShape()
297    */
getNumDimensions() const298   int getNumDimensions() const { return static_cast<int>(m_shape.size()); }
299 
300   /*!
301    * \brief Return number of dimensions in data view and fill in shape
302    *        information of this data view object.
303    *
304    *  ndims - maximum number of dimensions to return.
305    *  shape - user supplied buffer assumed to be ndims long.
306    *
307    *  Return the number of dimensions of the view.
308    *  Return -1 if shape is too short to hold all dimensions.
309    */
310   int getShape(int ndims, IndexType* shape) const;
311 
312   /*!
313    * \brief Return const reference to schema describing data.
314    */
getSchema() const315   const Schema& getSchema() const { return m_node.schema(); }
316 
317   /*!
318    * \brief Return non-const reference to Conduit node holding data.
319    */
getNode()320   Node& getNode() { return m_node; }
321 
322   /*!
323    * \brief Return const reference to Conduit node holding data.
324    */
getNode() const325   const Node& getNode() const { return m_node; }
326 
327   /*!
328    * \brief Returns boolean telling whether two Views have equivalent
329    *  internal description, in terms of name, datatype, and current state of the
330    *  object. Values of the data are not checked.
331    */
332   bool isEquivalentTo(const View* other) const;
333 
334   /*!
335    * \brief Returns true if both Views are either associated with a buffer or
336    * external, they span the same number of bytes and have unit stride.
337    */
338   bool isUpdateableFrom(const View* other) const;
339 
340   //@}
341 
342   //@{
343   //!  @name View allocation methods
344 
345   /*!
346    * \brief Allocate data for a view, previously described.
347    *
348    * \note Allocation from a view is allowed only if it is the only
349    *       view associated with its buffer (when it has one), or the view
350    *       is not external, not a string view, or not a scalar view.
351    *       If none of these condition is true, this method does nothing.
352    *
353    * \return pointer to this View object.
354    */
355   View* allocate(int allocID = INVALID_ALLOCATOR_ID);
356 
357   /*!
358    * \brief Allocate data for view given type and number of elements.
359    *
360    * \note The allocate() method (above) describes conditions where View
361    *       allocation is allowed.  If the conditions are not met,
362    *       type is NO_TYPE_ID, or num_elems < 0, this method does nothing.
363    *
364    * \return pointer to this View object.
365    */
366   View* allocate(TypeID type,
367                  IndexType num_elems,
368                  int allocID = INVALID_ALLOCATOR_ID);
369 
370   /*!
371    * \brief Allocate data for view described by a Conduit data type object.
372    *
373    * \note The allocate() method describes conditions where view
374    *       allocation is allowed. If the conditions are not met,
375    *       this method does nothing.
376    *
377    * \return pointer to this View object.
378    */
379   View* allocate(const DataType& dtype, int allocID = INVALID_ALLOCATOR_ID);
380 
381   /*!
382    * \brief  Reallocate data for view to given number of elements (type
383    *         stays the same).
384    *
385    * \note Reallocation from a view is only allowed under that same conditions
386    *       for the allocate() method. If the conditions are not met
387    *       or num_elems < 0 this method does nothing.
388    *
389    * \return pointer to this View object.
390    */
391   View* reallocate(IndexType num_elems);
392 
393   /*!
394    * \brief  Reallocate data for view as specified by Conduit data type object.
395    *
396    * \note Reallocation from a view is allowed under the conditions
397    *       described by the allocate() method. If the conditions are not met
398    *       or dtype is undefined, this method does nothing.
399    *
400    * \note The given data type object must match the view type, if it is
401    *       defined. If not, the method does nothing.
402    *
403    * \return pointer to this View object.
404    */
405   View* reallocate(const DataType& dtype);
406 
407   /*!
408    * \brief  Deallocate data for view.
409    *
410    * \note Deallocation from a view is only allowed under the conditions
411    *       described by the allocate() method. If the conditions are not met
412    *       or a Buffer is not attached this method does nothing.
413    *
414    * \return pointer to this View object.
415    */
416   View* deallocate();
417 
418   //@}
419 
420   /*!
421    * \brief Attach Buffer object to data view.
422    *
423    * If the view has no description, then the buffer's description
424    * is copied into the view.
425    *
426    * Note that, in general, the view cannot be used to access data in
427    * buffer until one of the apply() methods is called. However, if
428    * the view has a valid data description with a total number of bytes
429    * that is <= number of bytes held in the buffer, then apply() will
430    * be called internally.
431    *
432    * If data view already has a buffer, or it is an external view,
433    * a scalar view, or a string view, this method does nothing.
434    *
435    * If data view already has a buffer and buff is NULL, the attached
436    * buffer will be detached. After the view is detached from the
437    * buffer, if the buffer has no views attached to it, then it will
438    * be destroyed.
439    *
440    * \return pointer to this View object.
441    */
442   View* attachBuffer(Buffer* buff);
443 
444   /*!
445    * \brief Describe the data view and attach Buffer object.
446    *
447    * \return pointer to this View object.
448    */
attachBuffer(TypeID type,IndexType num_elems,Buffer * buff)449   View* attachBuffer(TypeID type, IndexType num_elems, Buffer* buff)
450   {
451     describe(type, num_elems);
452     attachBuffer(buff);
453     return this;
454   }
455 
456   /*!
457    * \brief Describe the data view and attach Buffer object.
458    *
459    * \return pointer to this View object.
460    */
attachBuffer(TypeID type,int ndims,const IndexType * shape,Buffer * buff)461   View* attachBuffer(TypeID type, int ndims, const IndexType* shape, Buffer* buff)
462   {
463     describe(type, ndims, shape);
464     attachBuffer(buff);
465     return this;
466   }
467 
468   /*!
469    * \brief Detach this view from its Buffer.
470    *
471    * If the view has no buffer, the method does nothing.
472    *
473    * \return pointer to detached buffer.
474    */
475   Buffer* detachBuffer();
476 
477   /*!
478    * \brief Clear data and metadata from a View.
479    *
480    * The view will be EMPTY. There will be no description
481    * or data associated with the View.
482    */
483   void clear();
484 
485   //@{
486   //!  @name Methods to apply View description to data.
487 
488   /*!
489    * \brief Apply view description to data.
490    *
491    * If view holds a scalar or a string, the method does nothing.
492    *
493    * \return pointer to this View object.
494    */
495   View* apply();
496 
497   /*!
498    * \brief Apply data description defined by number of elements, and
499    *        optionally offset and stride to data view (type remains the same).
500    *
501    * \note The units for offset and stride are in number of elements, which
502    *       is different than the Conduit DataType usage below where offset
503    *       and stride are in number of bytes.
504    *
505    * \attention If view has been previously described (or applied), this
506    *            operation will apply the new data description to the view.
507    *
508    * If view holds a scalar or a string, is external and does not have a
509    * sufficient data description to get type information, or given number
510    * of elements < 0, or offset < 0, the method does nothing.
511    *
512    * \return pointer to this View object.
513    */
514   View* apply(IndexType num_elems, IndexType offset = 0, IndexType stride = 1);
515 
516   /*!
517    * \brief Apply data description defined by type and number of elements, and
518    *        optionally offset and stride to data view.
519    *
520    * \note The units for offset and stride are in number of elements, which
521    *       is different than the Conduit DataType usage below where offset
522    *       and stride are in number of bytes.
523    *
524    * \attention If view has been previously described (or applied), this
525    *            operation will apply the new data description to the view.
526    *
527    * If view holds a scalar or a string, or type is NO_TYPE_ID,
528    * or given number of elements < 0, or offset < 0, the method does nothing.
529    *
530    * \return pointer to this View object.
531    */
532   View* apply(TypeID type,
533               IndexType num_elems,
534               IndexType offset = 0,
535               IndexType stride = 1);
536 
537   /*!
538    * \brief Apply data description defined by type and shape information
539    *        to data view.
540    *
541    * \note The units for the shape are in number of elements.
542    *
543    * \attention If view has been previously described (or applied), this
544    *            operation will apply the new data description to the view.
545    *
546    * If view holds a scalar or a string, or type is NO_TYPE_ID,
547    * or given number of dimensions < 0, or pointer to shape is null,
548    * the method does nothing.
549    *
550    * \return pointer to this View object.
551    */
552   View* apply(TypeID type, int ndims, const IndexType* shape);
553 
554   /*!
555    * \brief Apply data description of given Conduit data type to data view.
556    *
557    * If view holds a scalar or a string, the method does nothing.
558    *
559    * \return pointer to this View object.
560    */
561   View* apply(const DataType& dtype);
562 
563   //@}
564 
565   //@{
566   //!  @name Methods to set data in the view (scalar, string, or external data).
567 
568   /*!
569    * \brief Set the view to hold the given scalar.
570    *
571    * \return pointer to this View object.
572    */
573   template <typename ScalarType>
setScalar(ScalarType value)574   View* setScalar(ScalarType value)
575   {
576     // If this view already contains a scalar, issue a warning if the user is
577     // changing the underlying type ( ie: integer -> float ).
578 #if defined(AXOM_DEBUG)
579     if(m_state == SCALAR)
580     {
581       DataTypeId arg_id = detail::SidreTT<ScalarType>::id;
582       SLIC_CHECK_MSG(arg_id == m_node.dtype().id(),
583                      SIDRE_VIEW_LOG_PREPEND
584                        << "You are setting a scalar value which has changed "
585                        << " the underlying data type. "
586                        << "Old type: " << m_node.dtype().name() << ", "
587                        << "new type: " << DataType::id_to_name(arg_id) << ".");
588     }
589 #endif
590 
591     // Note: most of these calls that set the view class members are
592     //       unnecessary if the view already holds a scalar.  May be
593     //       a future optimization opportunity to split the
594     if(m_state == EMPTY || m_state == SCALAR)
595     {
596       m_node.set(value);
597       m_schema.set(m_node.schema());
598       m_state = SCALAR;
599       m_is_applied = true;
600       describeShape();
601     }
602     else
603     {
604       SLIC_CHECK_MSG(m_state == EMPTY || m_state == SCALAR,
605                      SIDRE_VIEW_LOG_PREPEND
606                        << "Unable to set scalar value on view "
607                        << " with state: " << getStateStringName(m_state));
608     }
609     return this;
610   }
611 
612   /*!
613    * \brief Set the view to hold the given scalar.
614    *
615    * \return pointer to this View object.
616    */
setScalar(Node & value)617   View* setScalar(Node& value)
618   {
619     // If this view already contains a scalar, issue a warning if the user is
620     // changing the underlying type ( ie: integer -> float ).
621 #if defined(AXOM_DEBUG)
622     if(m_state == SCALAR)
623     {
624       SLIC_CHECK_MSG(
625         value.dtype().id() == m_node.dtype().id(),
626         SIDRE_VIEW_LOG_PREPEND
627           << "Setting a scalar value in view  which has changed "
628           << "the underlying data type."
629           << "Old type: " << m_node.dtype().name() << ", "
630           << "New type: " << DataType::id_to_name(value.dtype().id()) << ".");
631     }
632 #endif
633 
634     // Note: most of these calls that set the view class members are
635     //       unnecessary if the view already holds a scalar.  May be
636     //       a future optimization opportunity to split the
637     if(m_state == EMPTY || m_state == SCALAR)
638     {
639       m_node.set(value);
640       m_schema.set(m_node.schema());
641       m_state = SCALAR;
642       m_is_applied = true;
643       describeShape();
644     }
645     else
646     {
647       SLIC_CHECK_MSG(m_state == EMPTY || m_state == SCALAR,
648                      SIDRE_VIEW_LOG_PREPEND
649                        << "Unable to set scalar value on view with state: "
650                        << getStateStringName(m_state));
651     }
652     return this;
653   }
654 
655   /*
656  * \brief set the View to hold an array in a Buffer
657  *
658  * This takes a Node that holds an array of data and sets up the View to
659  * hold a copy of that data in an attached Buffer.
660  *
661  * The prerequisites are that the Node must have numerical conduit data type
662  * (it returns true for a call to conduit::DataType::is_number()), and the
663  * state of this View must be either EMPTY or BUFFER when entering.  If the
664  * prerequisites are not met, a warning will be issued and this View will be
665  * unchanged.
666  *
667  * If this View has the state BUFFER when this method is called, it will
668  * become detached from its previous Buffer.
669  *
670  * \return pointer to this View object
671  */
672   View* importArrayNode(const Node& array);
673 
674   //
675   // RDH -- Add an overload of the following that takes a const char *.
676   //
677   /*!
678  * \brief Set the view to hold the given string.
679  *
680  * \return pointer to this View object.
681  */
setString(const std::string & value)682   View* setString(const std::string& value)
683   {
684     // Note: most of these calls that set the view class members are
685     //       unnecessary if the view already holds a string.  May be
686     //       a future optimization opportunity to split the
687     if(m_state == EMPTY || m_state == STRING)
688     {
689       m_node.set_string(value);
690       m_schema.set(m_node.schema());
691       m_state = STRING;
692       m_is_applied = true;
693       describeShape();
694     }
695     else
696     {
697       SLIC_CHECK_MSG(m_state == EMPTY || m_state == STRING,
698                      SIDRE_VIEW_LOG_PREPEND
699                        << "Unable to set string value on view with state: "
700                        << getStateStringName(m_state));
701     }
702     return this;
703   };
704 
705   /*!
706    * \brief Set view to hold external data.
707    *
708    * Data is undescribed (i.e., view is opaque) until an apply methods
709    * is called on the view.
710    *
711    * If external_ptr is NULL, the view will be EMPTY.
712    * Any existing description is unchanged.
713    *
714    * \return pointer to this View object.
715    */
716   View* setExternalDataPtr(void* external_ptr);
717 
718   /*!
719    * \brief Set view to hold described external data.
720    *
721    * If external_ptr is NULL, the view will be EMPTY.
722    *
723    * \return pointer to this View object.
724    */
setExternalDataPtr(TypeID type,IndexType num_elems,void * external_ptr)725   View* setExternalDataPtr(TypeID type, IndexType num_elems, void* external_ptr)
726   {
727     describe(type, num_elems);
728     setExternalDataPtr(external_ptr);
729     return this;
730   }
731 
732   /*!
733    * \brief Set view to hold described external data.
734    *
735    * If external_ptr is NULL, the view will be EMPTY.
736    *
737    * \return pointer to this View object.
738    */
setExternalDataPtr(TypeID type,int ndims,const IndexType * shape,void * external_ptr)739   View* setExternalDataPtr(TypeID type,
740                            int ndims,
741                            const IndexType* shape,
742                            void* external_ptr)
743   {
744     describe(type, ndims, shape);
745     setExternalDataPtr(external_ptr);
746     return this;
747   }
748 
749   //@}
750 
751   /*!
752  * \brief Update the data in this View with the data in other
753  * if isUpdateableFrom( other ). Otherwise nothing is done.
754  *
755  * \return pointer to this View object.
756  */
757   View* updateFrom(const View* other);
758 
759   //@{
760   //! @name Methods to retrieve data in a view.
761 
762   /*!
763    * \brief Return a pointer or conduit array object to the view's array data.
764    *
765    * Return value depends on variable type caller assigns it to. For example,
766    * if view holds an integer array, the following usage is possible:
767    *
768    *      int* a = view->getArray();      // Get array as int pointer
769    *      int_array a = view->getArray(); // Get array as Conduit array struct.
770    *
771    * \note The returned pointer accounts for the View's offset, so getArray()[0]
772    *       always points to the first element in the array.
773    */
getArray()774   Node::Value getArray()
775   {
776     //TODO add check that view holds array data.  Will be added in later commit.
777     //If debug, should trigger assert.  If release, issue warning.
778     return getData();
779   }
780 
781   /*!
782    * \brief Returns a pointer to the string contained in the view.
783    *
784    *  If the view is not a STRING, then nullptr is returned.
785    */
getString() const786   const char* getString() const
787   {
788     if(m_state == STRING)
789     {
790       return m_node.as_char8_str();
791     }
792     else
793     {
794       return nullptr;
795     }
796   }
797 
798   /*!
799    * \brief Returns a copy of the scalar value contained in the view.
800    */
getScalar() const801   Node::ConstValue getScalar() const
802   {
803     SLIC_CHECK_MSG(
804       m_state == SCALAR,
805       SIDRE_VIEW_LOG_PREPEND << "View::getScalar() called on non-scalar view.");
806     return getData();
807   }
808 
809   /*!
810    * \brief Return data held by view and cast it to any compatible type
811    *  allowed by Conduit (return type depends on type caller assigns it to).
812    *
813    *  If view does not contain allocated data, an empty Node::Value will be
814    *  returned.
815    *
816    *  \note The return value already accounts for the View's offset
817    *   (when present), so, if the View is an array, getData()[0] already points
818    *   to the first element
819    */
820   /// @{
getData()821   Node::Value getData()
822   {
823     SLIC_CHECK_MSG(isAllocated(),
824                    SIDRE_VIEW_LOG_PREPEND
825                      << "No view data present, memory has not been allocated.");
826     SLIC_CHECK_MSG(
827       isDescribed(),
828       SIDRE_VIEW_LOG_PREPEND << "View data description not present.");
829 
830     // this will return a default value
831     return m_node.value();
832   }
833 
getData() const834   Node::ConstValue getData() const
835   {
836     SLIC_CHECK_MSG(isAllocated(),
837                    SIDRE_VIEW_LOG_PREPEND
838                      << "No view data present, memory has not been allocated.");
839     SLIC_CHECK_MSG(isDescribed(),
840                    SIDRE_VIEW_LOG_PREPEND "View data description not present.");
841 
842     // this will return a default value
843     return m_node.value();
844   }
845   /// @}
846 
847   /*!
848    * \brief Lightweight templated wrapper around getData() that can be used when
849    *  you are calling getData(), but not assigning the return type.
850    *
851    * \sa getData()
852    */
853   template <typename DataType>
getData()854   DataType getData()
855   {
856     DataType data = m_node.value();
857     return data;
858   }
859 
860   /*!
861    * \brief Returns a void pointer to the view's data
862    *
863    * \note This function returns the base pointer that was used to set up the
864    *  view. It does not account for any offsets or strides in the View's
865    *  description.
866    *
867    * To access the first data element, you will need to cast to the appropriate
868    * type and add the offset.  E.g. if the underlying data is an array of
869    * integers you can access the first element as follows:
870    *
871    *      void* vptr = view->getVoidPtr();
872    *      int*  iptr = static_cast<int*>(vptr) + view->getOffset();
873    *
874    *
875    * \sa getData(), getArray()
876    */
877   void* getVoidPtr() const;
878 
879   //@}
880 
881   //@{
882   //!  @name View print methods.
883 
884   /*!
885    * \brief Print JSON description of data view to stdout.
886    */
887   void print() const;
888 
889   /*!
890    * \brief Print JSON description of data view to an ostream.
891    */
892   void print(std::ostream& os) const;
893 
894   //@}
895 
896   /*!
897    * \brief Copy data view description to given Conduit node.
898    */
899   void copyToConduitNode(Node& n) const;
900 
901   /*!
902    * \brief Copy data view native layout to given Conduit node.
903    *
904    * The native layout is a Conduit Node hierarchy that maps the Conduit Node
905    * data externally to the Sidre View data so that it can be filled in from the
906    * data in the file (independent of file format) and can be accessed as a
907    * Conduit tree.
908    */
909   void createNativeLayout(Node& n) const;
910 
911   /*!
912    * \brief Change the name of this View.
913    *
914    * The name of this view is changed to the new name.  This also changes
915    * the name for this view held by the owning group.
916    *
917    * Warnings will occur and the name will not be changed under these
918    * conditions:  If the new name is an empty string, if the new name
919    * contains a path delimiter (usually '/'), or if the new name is
920    * identical to a name that is already held by the parent for another
921    * Group or View object.
922    *
923    * \param new_name    The new name for this view.
924    *
925    * \return            Success or failure of rename.
926    */
927   bool rename(const std::string& new_name);
928 
929   //@{
930   //!  @name Attribute Value query and accessor methods
931 
932   Attribute* getAttribute(IndexType idx);
933 
934   const Attribute* getAttribute(IndexType idx) const;
935 
936   Attribute* getAttribute(const std::string& name);
937 
938   const Attribute* getAttribute(const std::string& name) const;
939 
940   /*!
941    * \brief Return true if the attribute has been explicitly set; else false.
942    */
hasAttributeValue(IndexType idx) const943   bool hasAttributeValue(IndexType idx) const
944   {
945     const Attribute* attr = getAttribute(idx);
946     return m_attr_values.hasValue(attr);
947   }
948 
949   /*!
950    * \brief Return true if the attribute has been explicitly set; else false.
951    */
hasAttributeValue(const std::string & name) const952   bool hasAttributeValue(const std::string& name) const
953   {
954     const Attribute* attr = getAttribute(name);
955     return m_attr_values.hasValue(attr);
956   }
957 
958   /*!
959    * \brief Return true if the attribute has been explicitly set; else false.
960    */
hasAttributeValue(const Attribute * attr) const961   bool hasAttributeValue(const Attribute* attr) const
962   {
963     SLIC_CHECK_MSG(attr != nullptr,
964                    SIDRE_VIEW_LOG_PREPEND
965                      << "hasAttributeValue: called with a null Attribute");
966 
967     return m_attr_values.hasValue(attr);
968   }
969 
970   /*!
971    * \brief Set Attribute to its default value from Attribute index.
972    *
973    * This causes hasAttributeValue to return false for the attribute.
974    */
setAttributeToDefault(IndexType idx)975   bool setAttributeToDefault(IndexType idx)
976   {
977     const Attribute* attr = getAttribute(idx);
978     return m_attr_values.setToDefault(attr);
979   }
980 
981   /*!
982    * \brief Set Attribute to its default value from Attribute name.
983    *
984    * This causes hasAttributeValue to return false for the attribute.
985    */
setAttributeToDefault(const std::string & name)986   bool setAttributeToDefault(const std::string& name)
987   {
988     const Attribute* attr = getAttribute(name);
989     return m_attr_values.setToDefault(attr);
990   }
991 
992   /*!
993    * \brief Set Attribute to its default value from Attribute pointer.
994    *
995    * This causes hasAttributeValue to return false for the attribute.
996    */
setAttributeToDefault(const Attribute * attr)997   bool setAttributeToDefault(const Attribute* attr)
998   {
999     SLIC_CHECK_MSG(attr != nullptr,
1000                    SIDRE_VIEW_LOG_PREPEND
1001                      << "getAttributeToDefault: called with a null Attribute");
1002 
1003     return m_attr_values.setToDefault(attr);
1004   }
1005 
1006   /*!
1007    * \brief Set Attribute for a View from Attribute index.
1008    */
1009   template <typename ScalarType>
setAttributeScalar(IndexType idx,ScalarType value)1010   bool setAttributeScalar(IndexType idx, ScalarType value)
1011   {
1012     const Attribute* attr = getAttribute(idx);
1013     if(attr == nullptr)
1014     {
1015       return false;
1016     }
1017 
1018     return m_attr_values.setScalar(attr, value);
1019   }
1020 
1021   /*!
1022    * \brief Set Attribute for a View from Attribute name.
1023    */
1024   template <typename ScalarType>
setAttributeScalar(const std::string & name,ScalarType value)1025   bool setAttributeScalar(const std::string& name, ScalarType value)
1026   {
1027     const Attribute* attr = getAttribute(name);
1028     if(attr == nullptr)
1029     {
1030       return false;
1031     }
1032 
1033     return m_attr_values.setScalar(attr, value);
1034   }
1035 
1036   /*!
1037    * \brief Set Attribute for a View from Attribute pointer.
1038    */
1039   template <typename ScalarType>
setAttributeScalar(const Attribute * attr,ScalarType value)1040   bool setAttributeScalar(const Attribute* attr, ScalarType value)
1041   {
1042     if(attr == nullptr)
1043     {
1044       SLIC_CHECK_MSG(attr != nullptr,
1045                      SIDRE_VIEW_LOG_PREPEND
1046                        << "setAttributeScalar: called with a null Attribute");
1047       return false;
1048     }
1049 
1050     return m_attr_values.setScalar(attr, value);
1051   }
1052 
1053   /*!
1054    * \brief Set Attribute for a View from Attribute index.
1055    */
1056   bool setAttributeString(IndexType indx, const std::string& value);
1057 
1058   /*!
1059    * \brief Set Attribute for a View from Attribute name.
1060    */
1061   bool setAttributeString(const std::string& name, const std::string& value);
1062 
1063   /*!
1064    * \brief Set Attribute for a View from Attribute pointer.
1065    */
1066   bool setAttributeString(const Attribute* attr, const std::string& value);
1067 
1068   /*!
1069    * \brief Return scalar attribute value from Attribute indx.
1070    */
getAttributeScalar(IndexType idx) const1071   Node::ConstValue getAttributeScalar(IndexType idx) const
1072   {
1073     const Attribute* attr = getAttribute(idx);
1074     if(attr == nullptr)
1075     {
1076       return m_attr_values.getEmptyNodeRef().value();
1077     }
1078 
1079     return m_attr_values.getScalar(attr);
1080   }
1081 
1082   /*!
1083    * \brief Return scalar attribute value from Attribute name.
1084    */
getAttributeScalar(const std::string & name) const1085   Node::ConstValue getAttributeScalar(const std::string& name) const
1086   {
1087     const Attribute* attr = getAttribute(name);
1088     if(attr == nullptr)
1089     {
1090       return m_attr_values.getEmptyNodeRef().value();
1091     }
1092 
1093     return m_attr_values.getScalar(attr);
1094   }
1095 
1096   /*!
1097    * \brief Return scalar attribute value from Attribute pointer.
1098    */
getAttributeScalar(const Attribute * attr) const1099   Node::ConstValue getAttributeScalar(const Attribute* attr) const
1100   {
1101     if(attr == nullptr)
1102     {
1103       SLIC_CHECK_MSG(
1104         attr != nullptr,
1105         SIDRE_VIEW_LOG_PREPEND << "getScalar: called with a null Attribute");
1106       return m_attr_values.getEmptyNodeRef().value();
1107     }
1108 
1109     return m_attr_values.getScalar(attr);
1110   }
1111 
1112   /*!
1113    * \brief Lightweight templated wrapper around getAttributeScalar()
1114    *  that can be used when you are calling getAttributeScalar(), but not
1115    *  assigning the return type.
1116    *
1117    * \sa getAttributeScalar()
1118    */
1119   template <typename DataType>
getAttributeScalar(IndexType idx)1120   DataType getAttributeScalar(IndexType idx)
1121   {
1122     const Attribute* attr = getAttribute(idx);
1123     const Node& node = m_attr_values.getValueNodeRef(attr);
1124     DataType data = node.value();
1125     return data;
1126   }
1127 
1128   /*!
1129    * \brief Lightweight templated wrapper around getAttributeScalar()
1130    *  that can be used when you are calling getAttributeScalar(), but not
1131    *  assigning the return type.
1132    *
1133    * \sa getAttributeScalar()
1134    */
1135   template <typename DataType>
getAttributeScalar(const std::string & name)1136   DataType getAttributeScalar(const std::string& name)
1137   {
1138     const Attribute* attr = getAttribute(name);
1139     const Node& node = m_attr_values.getValueNodeRef(attr);
1140     DataType data = node.value();
1141     return data;
1142   }
1143 
1144   /*!
1145    * \brief Lightweight templated wrapper around getAttributeScalar()
1146    *  that can be used when you are calling getAttributeScalar(), but not
1147    *  assigning the return type.
1148    *
1149    * \sa getAttributeScalar()
1150    */
1151   template <typename DataType>
getAttributeScalar(const Attribute * attr)1152   DataType getAttributeScalar(const Attribute* attr)
1153   {
1154     SLIC_CHECK_MSG(attr != nullptr,
1155                    SIDRE_VIEW_LOG_PREPEND
1156                      << "getAttributeScalar: called with a null Attribute");
1157 
1158     const Node& node = m_attr_values.getValueNodeRef(attr);
1159     DataType data = node.value();
1160     return data;
1161   }
1162 
1163   /*!
1164    * \brief Return a string attribute from the Attribute index.
1165    *
1166    * If the value has not been explicitly set, return the current default.
1167    */
1168   const char* getAttributeString(IndexType idx) const;
1169 
1170   /*!
1171    * \brief Return a string attribute from the Attribute name.
1172    *
1173    * If the value has not been explicitly set, return the current default.
1174    */
1175   const char* getAttributeString(const std::string& name) const;
1176 
1177   /*!
1178    * \brief Return a string attribute from the Attribute pointer.
1179    *
1180    * If the value has not been explicitly set, return the current default.
1181    */
1182   const char* getAttributeString(const Attribute* attr) const;
1183 
1184   /*!
1185    * \brief Return reference to attribute node from Attribute index.
1186    *
1187    * If the value has not been explicitly set, return the current default.
1188    */
getAttributeNodeRef(IndexType idx) const1189   const Node& getAttributeNodeRef(IndexType idx) const
1190   {
1191     const Attribute* attr = getAttribute(idx);
1192     return m_attr_values.getValueNodeRef(attr);
1193   }
1194 
1195   /*!
1196    * \brief Return reference to attribute node from Attribute name.
1197    *
1198    * If the value has not been explicitly set, return the current default.
1199    */
getAttributeNodeRef(const std::string & name) const1200   const Node& getAttributeNodeRef(const std::string& name) const
1201   {
1202     const Attribute* attr = getAttribute(name);
1203     return m_attr_values.getValueNodeRef(attr);
1204   }
1205 
1206   /*!
1207    * \brief Return reference to attribute node from Attribute pointer.
1208    *
1209    * If the value has not been explicitly set, return the current default.
1210    */
getAttributeNodeRef(const Attribute * attr) const1211   const Node& getAttributeNodeRef(const Attribute* attr) const
1212   {
1213     SLIC_CHECK_MSG(attr != nullptr,
1214                    SIDRE_VIEW_LOG_PREPEND
1215                      << "getAttributeNodeRef: called with a null Attribute");
1216 
1217     return m_attr_values.getValueNodeRef(attr);
1218   }
1219 
1220   /*!
1221    * \brief Return first valid Attribute index for a set Attribute in
1222    *        View object (i.e., smallest index over all Attributes).
1223    *
1224    * sidre::InvalidIndex is returned if View has no Attributes set.
1225    */
getFirstValidAttrValueIndex() const1226   IndexType getFirstValidAttrValueIndex() const
1227   {
1228     return m_attr_values.getFirstValidAttrValueIndex();
1229   }
1230 
1231   /*!
1232    * \brief Return next valid Attribute index for a set Attribute in
1233    *        View object after given index (i.e., smallest index over
1234    *        all Attribute indices larger than given one).
1235    *
1236    * sidre::InvalidIndex is returned if there is no valid index greater
1237    * than given one.
1238    * getNextAttrValueIndex(InvalidIndex) returns InvalidIndex.
1239    */
getNextValidAttrValueIndex(IndexType idx) const1240   IndexType getNextValidAttrValueIndex(IndexType idx) const
1241   {
1242     return m_attr_values.getNextValidAttrValueIndex(idx);
1243   }
1244 
1245   //@}
1246 
1247 private:
1248   DISABLE_DEFAULT_CTOR(View);
1249   DISABLE_MOVE_AND_ASSIGNMENT(View);
1250 
1251   //@{
1252   //!  @name Private View ctor and dtor
1253   //!        (callable only by Group and View methods).
1254 
1255   /*!
1256    *  \brief Private ctor that creates a View with given name
1257    *         which has no data associated with it.
1258    */
1259   View(const std::string& name);
1260 
1261   /*!
1262    * \brief Private copy ctor.
1263    */
1264   View(const View& source);
1265 
1266   /*!
1267    * \brief Private dtor.
1268    */
1269   ~View();
1270 
1271   //@}
1272 
1273   //@{
1274   //!  @name Private View declaration methods.
1275   //!        (callable only by Group and View methods).
1276 
1277   /*!
1278    * \brief Describe a data view with given type and number of elements.
1279    *
1280    *
1281    * \attention If view has been previously described, this operation will
1282    *            re-describe the view. To have the new description take effect,
1283    *            the apply() method must be called.
1284    *
1285    * If given type of NO_TYPE_ID, or number of elements < 0, or view is opaque,
1286    * method does nothing.
1287    */
1288   void describe(TypeID type, IndexType num_elems);
1289 
1290   /*!
1291    * \brief Describe a data view with given type, number of dimensions, and
1292    *        number of elements per dimension.
1293    *
1294    *
1295    * \attention If view has been previously described, this operation will
1296    *            re-describe the view. To have the new description take effect,
1297    *            the apply() method must be called.
1298    *
1299    * If given type of NO_TYPE_ID, or number of dimensions or total
1300    * number of elements < 0, or view is opaque, method does nothing.
1301    */
1302   void describe(TypeID type, int ndims, const IndexType* shape);
1303 
1304   /*!
1305    * \brief Declare a data view with a Conduit data type object.
1306    *
1307    * \attention If view has been previously described, this operation will
1308    *            re-describe the view. To have the new description take effect,
1309    *            the apply() method must be called.
1310    *
1311    * If view is opaque, the method does nothing.
1312    */
1313   void describe(const DataType& dtype);
1314 
1315   /*!
1316    * \brief Set the shape to be a one dimension with the described number of
1317    * elements.
1318    */
1319   void describeShape();
1320 
1321   /*!
1322    * \brief Set the shape to be a ndims dimensions with shape.
1323    */
1324   void describeShape(int ndims, const IndexType* shape);
1325 
1326   /*!
1327    * \brief Private method to remove user provided description.
1328    */
undescribe()1329   void undescribe()
1330   {
1331     m_schema.reset();
1332     m_shape.clear();
1333   }
1334 
1335   /*!
1336    * \brief Copy view contents into an undescribed EMPTY view.
1337    *
1338    * For SCALAR and STRING the data is copied; EXTERNAL,
1339    * data pointer is copied; BUFFER attaches the buffer.
1340    */
1341   void copyView(View* copy) const;
1342 
1343   /*!
1344    * \brief Add view description and references to it's data to a conduit tree.
1345    */
1346   void exportTo(conduit::Node& data_holder,
1347                 std::set<IndexType>& buffer_indices) const;
1348 
1349   /*!
1350    * \brief Restore a view's description and data from a conduit tree.
1351    * This does not include a view's buffer data, that is done in the buffer
1352    */
1353   void importFrom(conduit::Node& data_holder,
1354                   const std::map<IndexType, IndexType>& buffer_id_map);
1355 
1356   /*!
1357    * \brief Add view's description to a conduit tree.
1358    */
1359   void exportDescription(conduit::Node& data_holder) const;
1360 
1361   /*!
1362    * \brief Restore a view's description from a conduit tree.
1363    */
1364   void importDescription(conduit::Node& data_holder);
1365 
1366   /*!
1367    * \brief Add view's attributes to a conduit tree.
1368    */
1369   void exportAttribute(conduit::Node& data_holder) const;
1370 
1371   /*!
1372    * \brief Restore a view's attributes from a conduit tree.
1373    */
1374   void importAttribute(conduit::Node& data_holder);
1375 
1376   /*!
1377    *  \brief Private method to remove any applied description;
1378    *         but preserves user provided description.
1379    *
1380    *  Note: The description is stored in m_schema.
1381    */
unapply()1382   void unapply()
1383   {
1384     m_node.reset();
1385     m_is_applied = false;
1386   }
1387 
1388   /*!
1389    *  \brief Private method to reset a view from BUFFER to EMPTY.
1390    *
1391    *         Used by Buffer when detaching from a view.
1392    */
setBufferViewToEmpty()1393   void setBufferViewToEmpty()
1394   {
1395     m_data_buffer = nullptr;
1396     m_state = EMPTY;
1397     unapply();
1398   }
1399 
1400   //@}
1401 
1402   //@{
1403   //!  @name Private methods that indicate when certain view operations are valid.
1404 
1405   /*!
1406    *  \brief Private method returns true if data allocation on view is a
1407    *         valid operation; else false
1408    */
1409   bool isAllocateValid() const;
1410 
1411   /*!
1412    *  \brief Private method returns true if apply is a valid operation on
1413    *         view; else false
1414    */
1415   bool isApplyValid() const;
1416 
1417   //@}
1418 
1419   ///
1420   /// Enum with constants that identify the state of a view.
1421   ///
1422   /// Note that these states are mutually-exclusive. These constants
1423   /// combined with the boolean m_is_applied uniquely identify the view
1424   /// state, or how it was created and defined.
1425   ///
1426   enum State
1427   {
1428     EMPTY,     // View created with name only :
1429                //    has no data or data description
1430     BUFFER,    // View has a buffer attached explicitly. :
1431                //    applied may be true or false
1432     EXTERNAL,  // View holds pointer to external data (no buffer) :
1433                //    applied may be true or false
1434     SCALAR,    // View holds scalar data (via setScalar()):
1435                //    applied is true
1436     STRING     // View holds string data (view setString()):
1437                //    applied is true
1438   };
1439 
1440   /*!
1441    *  \brief Private method returns string name of given view state enum value.
1442    */
1443   static char const* getStateStringName(State state);
1444 
1445   /*!
1446    *  \brief Private method returns state enum value give a state name.
1447    */
1448   State getStateId(const std::string& name) const;
1449 
1450   /*!
1451    * \brief Private method. If allocatorID is a valid allocator ID then return
1452    *  it. Otherwise return the ID of the default allocator of the owning group.
1453    */
1454   int getValidAllocatorID(int allocatorID);
1455 
1456   /// Name of this View object.
1457   std::string m_name;
1458 
1459   /// Index of this View object within m_owning_group.
1460   IndexType m_index;
1461 
1462   /// Group object that owns this View object.
1463   Group* m_owning_group;
1464 
1465   /// Buffer associated with this View object.
1466   Buffer* m_data_buffer;
1467 
1468   /// Data description (schema) that describes the view's data.
1469   Schema m_schema;
1470 
1471   /// Conduit node used to access the data in this View.
1472   Node m_node;
1473 
1474   /// Shape information
1475   std::vector<IndexType> m_shape;
1476 
1477   /// Pointer to external memory
1478   void* m_external_ptr;
1479 
1480   /// State of view.
1481   State m_state;
1482 
1483   /// Has data description been applied to the view's data?
1484   bool m_is_applied;
1485 
1486   /// Attribute Values
1487   AttrValues m_attr_values;
1488 };
1489 
1490 } /* end namespace sidre */
1491 } /* end namespace axom */
1492 
1493 #endif /* SIDRE_VIEW_HPP_ */
1494