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