From b47165c8f0b6cae506c5d5bfdad241106d6fe6e9 Mon Sep 17 00:00:00 2001 From: "J. Daniel Smith" Date: Mon, 13 Nov 2023 17:34:02 -0500 Subject: [PATCH] latest from CODA-OSS and NITRO (#715) * latest from CODA-OSS and NITRO * Squashed 'externals/coda-oss/' changes from e87c32b4d..03f1a3bdf 03f1a3bdf release 2023-10-23 (#747) 4dd7b2fbd Merge branch 'main' of github.com:mdaus/coda-oss 9428f12a0 add extensions for .log and known Windows/Linux binaries (#746) c3fd20a88 added some OLD Visual Studio extensions d00db384a everything is text except for known binaries c3813bb29 eol=lf is the default 4a2f472c7 add extensions for .log and known Windows/Linux binaries (#746) 18f2c051f more FmtX() -> str::Format() changes (#745) 959532681 reduce use of FmtX macro (#743) fffac7fc4 Fix memory leaks in "cli" (#741) 95ff879ba make it easier to turn on AVX2/AVX512F (#740) a950c848b enable AVX2 and AVX512F CMake builds (#739) 369737085 use std::ssize() to reduce casts (#738) 8ae7dabac std::size() and std::ssize() (#737) 8db480be5 enable ASAN for GitHub builds (#736) c92a55d7c build in SIX 333b91024 Squashed commit of the following: ac8c312db Only allow va_args formatting under controlled circumstances (#735) 589aacfd7 str::to_native() for when conversion to std::string really is needed 743feb41e try hard not to lose string encoding (#734) 1854a9b46 hide use of str::cast() inside `details` namespace (#733) fbe0c85f9 compile-time getSIMDInstructionSet() (#732) ee2c46358 more consistent naming for routines to convert between encodings (#731) 00e2dd928 another FmtX() overload 165a799c5 more FmtX() overloads (#729) 8dee6f7a6 revert toString() changes (#728) bf7ae4a71 use overloads rather than varargs for std::format() (#727) 22e050207 utility routines to reduce duplicated code (#726) a5ea60647 remove str::EncodedStringView (#725) 168cbae01 reduce differences between 'main' and 'cpp17' (#724) 5b892bf5f release 2023-08-18 (#723) 0b1327d1e restore mem::AutoPtr; too much of a hassle moving it to numpyutils :-( (#722) 7691adb56 normalize line endings (#721) b7d50efdd update to HDF5 1.14.2 (#720) fcc96ec69 Update .gitattributes (#719) bb82a94c9 xerces-c 3.2.4 (#718) ef4ad7cf3 Update to e2fsprogs 1.47.0 (#717) b4ca18a11 fix compiler warning about order of operations a5df5b823 update to HighFive 2.7.1 (#716) 4109ee5d2 `mem::AutoPtr` is only for Python bindings (#715) 00d843f87 remove hdf5.lite (#714) 443dd3825 Reduce compiler warnings, speed up builds (#713) d60861821 get optional working with partial C++17 (#712) d5bd0d804 "warning STL4036: is removed in C++20." c19ece7f9 Don't interfere with a partial C++20 implementation (#711) 065e86ddc operator==() for QName (#710) 1c16380ce remove zint* typedefs; don't want to encourage use of types::Complex (#709) 6dd247991 our optional<> is now closer to C++17 (#708) 87ac61739 strict checking on `std::complex` is too much of a hassle for now 034d52c86 overloads to byte-swap type::Complex are too much trouble (#707) 42d449c33 sys::byteSwap overloads for types::Complex (#705) 4092fd8e7 remove work-around for NITRO bug (#704) e2472acfc build in NITRO and SIX (#703) 32ccf9105 Use same build paths as Visual Studio (#701) 366ac9f43 Another round of removing compiler warnings (#702) a7f8ef260 Fix compile warnings from building CODA (#700) f70b4202f account for coda-oss.vcxproj being in other SLNs af3faebfc Remove more compiler/code-analysis warnings (#699) 82be2a6db unittests should work w/o install (#698) dbb90a06d add msbuild for coda-oss.sln (#697) 5a417140a reduce compiler warnings (#696) 288619dfa all modules now part of coda-oss.vcxproj (#695) c2fc5fc66 add more projects to coda-oss-lite.vcxproj (#694) ceb86c186 support $(PlatformToolset) as a "special" environment variable (#693) d78a8595a OS::getSIMDInstructionSet() utility (#692) 2d2df467d fix `python3 waf dumplib` 54033e70e Merge branch 'main' into feature/complex_short 3c63f9f65 std::numbers from C++20 (#691) 892dd0e00 ComplexInteger and ComplexReal to better match existing naming conventions (#690) b3872181e match coda-oss naming conventions (#688) 704d6867f beef-up our complex type (#687) 47c1c1cd6 check is_absolute() for URLs (#686) c042373e1 be sure our Path::isAbolute() matches std::filesystem::path::absolute() (#684) ad10286bc volatile is about "special" memory, not threading (#685) f4d42005f fix build error in NITRO ff11a5557 keep using std::complex for now (#682) c88b9c053 types::complex (#681) d1244a080 don't need our own make_unique in C++14 (#680) aeec0131c assert()s for mem::CopyablePtr (#679) 72b0ebd60 add types::complex_short (#678) 932130a58 patch to build other projects c00c1f203 coda-oss release 2023-06-06 (#677) ef54bbcd5 remove more compiler warnings (#676) dadfc5ce6 distinguish between byte-swapping a buffer and single value (#674) 90187f6cd more xml.lite tweaks for SIDD-3.0/ISM (#675) eb9960772 stronger type-checking for byteSwap() (#673) ff4f820ed xml.lite tweaks to support SIDD 3.0 ISM (#672) b1de8c0e5 std::byte should be a unique type (#671) c05bf9a02 allow enums to be byte-swapped too 1f9fd88d6 remove spurious 47684c45b byteSwap now uses byte buffers (#670) cbc659db2 add swapBytes() utility from SIX (#669) 891481b64 simplify byte-swapping (#668) 540ae763e more byteSwap() tweaks 0774c03c4 threaded byteSwap() (#667) d156370d3 swapping a single-byte value makes no sense c120e3255 be sure parameter is used to avoid compiler warning e85ec9331 --output-on-failure for CTest (#666) e80376197 turn off "there is no warning number" warning c5f0a5d15 A C-string may not be NULL-terminated (#665) 0c5eb29ae use platform-specific routines for byteSwap() (#664) 0b7d581fa remove transform_async() (#663) f6489b6be Merge branch 'main' into feature/xml.lite_tweaks 836c426a2 use function-pointers so that isConsoleOutput is only checked once 56e3c45b1 move depthPrint() functionality into non-member function in preparation for future changes ddcd26d97 Merge branch 'main' into feature/xml.lite_tweaks 69cc0e506 use the more rigorous create_and_check_datatype() 9efb87558 Merge branch 'main' into feature/hdf5 4d2f2f417 more HighFive unittests (#662) 14191a844 HighFive::create_datatype() goes from C++ to HighFive 98583473f utility routines to read string attributes 1fa75ce81 use the C API to read a string attribute 71e7b69f5 still can't figure out how to read a string attribute :-( e96f37a69 test reading the file attributes a25244519 getAttribute() unittest 8f12a3000 getDataType() unittest 857ff0af3 HighFive utility routines (#661) 1d687db57 writeDataSet() utility overload 106aa6894 sigh ... H5Easy::dump() fill fails on Windows/WAF :-( 2641b60b2 Merge branch 'main' into feature/hdf5 35c19e7e8 change actions to @v3 (#660) 212bbd3a3 works on local machine, but not build server ... ? 7125118b0 dump of 1D vector doesn't work :-( c704db435 sigh ... WAF build still failing :-( 01aae4616 does dump() of a 1D vector work? 76a53c813 comment-out H5 writing :-( 0f0e19aff test_highfive_dump() cb8f73795 trying to get highfive_dump() unittest working w/Windows-WAF 6584a264a does test_highfive_create() work? cde6147ce tweak HighFive wrappers (#659) 316566854 Revert "trying HighFive "write" unittests again" a9ec24ca4 trying HighFive "write" unittests again 585ad49a5 tweak names of utility routines 4c91a4d97 make it easier to read a std::vector and std::vecotr 0217ffa26 readDataSet() now works for 1D data 95e8973f2 trying to get hdf5::lite::load() working 4d294611d "const" correctness 5e6305c3f fix load_complex() 4a134dc5f start work on utility routines to read complex data from HDF5 9d76a7f41 Merge branch 'main' into feature/hdf5 8f9667a24 whitepsace ebd3fc99e Merge branch 'main' into feature/hdf5 40091b069 comment-out writing tests for now ... need to figure our WAF bulid failure 935aa3459 be sure the dataset has real data 114b9bf33 update release notes bd9c0b26c tweak HighFive utility routines acda1ef57 turn off diagnostics around expected failures 22a748840 readDataSet() utility routine for HighFive bd88a8c25 HighFive writeDataSet() utility to work with our SpanRC 6142f5b33 use HighFive routines to write a HDF5 file 5bbf1abaf Use HiveFive routines to get info about the file 84fbc8378 duplicate unittests with H5Easy f1f054c03 Merge branch 'main' into feature/hdf5 9b63ca470 fix directory names f6f826689 fix directory names 7aeb82c33 Merge branch 'main' into feature/hdf5 d028baaeb hook up HighFive header-only library (#653) 3083b0a31 Revert "HighFive 2.6.2" 246985a7f Revert ""build" HighFive HDF5 library" a8b75a586 Revert "turn off HighFive Boost support" ec68d5f83 Revert "Add HighFive unittests" f1f85b9e7 Revert "get test_high_five_base more-or-less compiling" 5ea634ee1 Revert "more work on getting HighFive unittests to build" ecc45433c more work on getting HighFive unittests to build bb194788a get test_high_five_base more-or-less compiling d42bde000 Add HighFive unittests ddc86bb32 turn off HighFive Boost support b255122d4 "build" HighFive HDF5 library 396cc3ef2 HighFive 2.6.2 5e5f9d9c0 Merge branch 'main' into feature/hdf5 ee938b4a5 changes from SIX bb764df90 Merge branch 'main' into feature/xml.lite_tweaks de2a24380 make derived classes 'final' if possible 14e19bcd2 Change xml lite function to virtual (#645) 8f42ac8e9 Merge branch 'main' into feature/xml.lite_tweaks 18ad90645 hdf5Write unittest 3462e1179 createFile() and writeFile() overloads ecee81d53 fix typos 197eecfa6 sketch-out hdf5::lite::writeFile() bd2311795 use SpanRC for writeFile(), not yet implemented ea9af7510 simple SpanRC to hold a 2D-size and pointer 027c19ee8 createFile() unittest 1f9d07ecb hook up createFile() 8c7e4473f start hooking up HDF5 writing 146e0bea3 Merge branch 'main' into feature/hdf5 88ca9fcb7 Merge branch 'main' into feature/hdf5 42b604b46 Squashed commit of the following: 10ee602c2 Merge branch 'main' into feature/hdf5 67aa42b69 restore changes from "main" 8bbfcbfbf unittests can be simplified to match fewer "view" classes 126bb802e Merge branch 'main' into feature/hdf5 3f8ba7a42 again, don't need a class just to convert from std::vector<> to std::span<> 24c2b489c Squashed commit of the following: 2703c119d Squashed commit of the following: 9d5228a2b don't need an entire class just to convert a std::vector<> into std::span<> 51bc931dc Merge branch 'main' into feature/hdf5 a84f25816 Squashed commit of the following: c4d2ed696 add missing #include guards, fix type in existing #include guard d541525a0 use a single ComplexViewConstIterator for all views 86e6a459f CODA_OSS_disable_warning causes GCC errors :-( 5d4b9c2cb only need an custom iterator for ComplexSpansView d9f0fb128 hook up iterators b9329e4db initial pass at a ComplexViewConstIterator 635238873 remove compiler warning about unused "constexpr" variables b39f6096f use the casing from H5 to make copy/pasting code slightly easier 0887b13eb Merge branch 'main' into feature/hdf5 bd07df1ca Consistent casing for Dataset, Datatype, Dataspace 7acd30ee2 tweak hdf5.lite dependencies 38ab914df Jupyter notebook for creating H5 files 95a040e0b _small.h5 is now (correctly) FLOAT32 107e7c487 make a simple values() member function to avoid template magic e1feca919 use TEST_SPECIFIC_EXCEPTION macro instead of try/catch 738333688 readDatasetT() now throws for the wrong buffer type 6b2cc2529 Merge branch 'feature/hdf5' of github.com:mdaus/coda-oss into feature/hdf5 310f8fd3d can't get template magic right for copy_axis() 86b306d59 stepping through copy ctors in the debugger is annoying f243e92d6 trying to make wrong type of buffer fail 2b10d9652 read in new sample file a28e59d8c help the compiler with type deduction 49bf5e9bc nested_complex_float_data_small.h5 e029325fc utility routines to "deconstruct" and array of std::complex dede3bd39 Merge branch 'main' into feature/hdf5 904b1ef5e tweak class names, make_() and copy() utility routines 8237b9efb make it harder to pass the wrong types to ComplexViews 4d9aeda2c ComplexArrayView and ComplexParallelView utility classes f5e367dfa test std::span> a4a2844f2 read in the nested "i" and "r" data 115615265 sample file has subgroups 8e1b7869a Merge branch 'feature/hdf5' of github.com:mdaus/coda-oss into feature/hdf5 9f4232a1d update sample H5 file 8c55db73a walk through HDF5 sub-groups 7775ed9c4 Update 123_barfoo_catdog_cx.h5 677975d7c Matlab code to create sample H5 file a0e7dfe07 Update test_hdf5info.cpp 0b67e1602 pass __FILE__ and __LINE__ from calling site for a more accurate exception message 86a677321 skeleton for more sample data 85f79b099 Merge branch 'main' into feature/hdf5 18088e942 Merge branch 'main' into feature/hdf5 3a1d17692 Merge branch 'main' into feature/hdf5 1755c69d7 Merge branch 'main' into feature/hdf5 9ad015432 No more "11" suffix on exception names c20d96251 Squashed commit of the following: c88cee999 other values to be filled-in bca4a4ecd incorporation NamedObject from HDF5 docs 61fa68f72 groupInfo() 460e7d766 datasetInfo() 14eb9b764 start filling in DatasetInfo afe5f1c3a start to fill in DatasetInfo 77a968c72 start filling in GroupInfo d81bcdfd9 openGroup() to open groups (loc) a0cd29469 comment-out "dataset" unittest for now 86e006024 begin filling in FileInfo 366dda6ab a return_type_of utility is needed to deduce the return type e21928263 explicitly pass return type to template 4937ccd11 template to reduce boilerplate when calling try_catch_H5Exceptions b3b5ebde7 use new exception utility routines ea1c03ef0 put exception handling/conversion in a utility routine 819a99d39 utility routine for exception handling 6f34eea97 put utilities in a separate file for easier reuse fcbde4f24 break utility routines into smaller pieces for easier reuse 52358ea8a WIN32 no longer automatically defined? 5a4286472 Revert "build HDF5 with C89" 680e599e9 build HDF5 with C89 a87a07121 Merge branch 'main' into feature/hdf5 8447c1a90 Revert "sym-links instead of copying files" db3b5e12b Merge branch 'main' into feature/xml.lite_tweaks fb60b5696 Merge branch 'main' into feature/hdf5 5110a5cc8 Comments about _u and _q 1a937d32c Merge branch 'main' into feature/xml.lite_tweaks fa06f04d7 get ready for hdf5.lite enhancdements b040c7c43 sym-links instead of copying files aa431bb47 use _u for xml::lite::Uri 3d0c6d58c fix case-sensitive #include filename 93dcd0e52 operator() for getElementByName() 75a93af85 more operator[] overloads to make attribute management easier 4ab8216f8 user-defined string literals to remove some noise around xml::lite::QName f82f0b0fc Merge branch 'main' into feature/xml.lite_tweaks ae30e3644 Merge branch 'feature/xml.lite_tweaks' of github.com:mdaus/coda-oss into feature/xml.lite_tweaks ffdd9beb0 simplify attribute creation 9bf5414f5 simplify attribute creation 82d7a4e95 SWIG gets confused about namespaces 7a61d0741 fix bug on Element ctor uncovered by unittest fdd7e58c1 QName is also in the xerces namespace which confuses SWIG bindings a325b7053 operator+=() overload for addChild daf30e6c0 Merge branch 'feature/xml.lite_tweaks' of github.com:mdaus/coda-oss into feature/xml.lite_tweaks b887d2b47 provide overloads for Element& rather than creating new "reference" types 1fa6bba38 rename test_xmleasy.cpp 7c8c9e0f1 += overload 850da6f63 overload for std::string 4547fc5a7 use UIT-8 strings for characterData 4723462a3 convenient addChild() overloads e48720753 copy over ElementReference from xml.easy a4ca30a0d Merge branch 'main' into feature/xml.lite_tweaks 6ae9f0b71 Revert "check-in of new xml.easy (to move code between computers)" f7466a6d7 Revert "simple routines for single element" a5490230d Revert "make some operators simplier ways of calling functions" c9a25630a Revert "get document creation working" 8af8710b0 Revert "free functions instead of member functions" 16c3847cb Revert "ElementReference distinct from Element" 7d68e156f Revert "ElementMutableReference" 00eb2a282 Merge branch 'main' into feature/xml.lite_tweaks a42969c1f ElementMutableReference a20ae9355 ElementReference distinct from Element 14eeeea0b free functions instead of member functions 4aae014b3 get document creation working 883569269 make some operators simplier ways of calling functions 053bd1212 simple routines for single element 8bf701a2e check-in of new xml.easy (to move code between computers) 41f959051 unittests for creating XML documents from scratch 9752d50ae Merge branch 'main' into feature/xml.lite_tweaks 1531d5709 by default, don't validate strings passed to Uri() 46d13d4bf Merge branch 'master' into feature/xml.lite_tweaks 39b547d32 remove more vestiges of Expat and LibXML ec8274d52 remove LibXML and Expat as they're no longer used/supported. 20eeefeef Merge branch 'master' into feature/xml.lite_tweaks 95074b9b1 update for newer Intel compiler 7024f71e1 Merge branch 'master' into feature/xml.lite_tweaks 57b1cbc83 Merge branch 'master' into feature/xml.lite_tweaks 4b67561c3 remove validate() overload that nobody is using fa15f1e5d Squashed commit of the following: 1484a9090 test the new validate() API 470da70fb hookup StringStreamT routines 2cddf2504 begin hooking up validate() overloads 1b5d910f3 overload validate() for UTF-8 and Windows-1252 03309b8c9 Squashed commit of the following: b72c6c5bf older compiler doesn't like our make_unique af8f00307 validate UTF-8 XML on Linux 211188613 unit-test for LEGACY XML validation 3c1169d2b Squashed commit of the following: 3afff19ca std::filesystem::path for FileInputStreamOS 908d452f8 WIP: validate all of our sample XML files 00f9bb16b validate against a XML schema 243d8c356 Merge branch 'master' into feature/xml.lite_tweaks 2815d707d fix to work with SWIG bindings. :-( 460862132 trying (again) to remove vestiages of old code e3c83a858 Revert "new code should use UTF-8" 811207c92 new code should use UTF-8 0ffd835f9 Squashed commit of the following: 1e7e03ded Merge branch 'master' into feature/xml.lite_tweaks c1d806aff Merge branch 'master' into feature/xml.lite_tweaks 850d3c811 str::strip() that can be easier to use than str::trim() 580ba9c8c explicitly =delete move 2b39831a8 Squashed commit of the following: 39eebdc23 Merge branch 'master' into feature/xml.lite_tweaks 9adf86cba force calling new UTF-8 write() routines ea61b6204 Merge branch 'master' into feature/xml.lite_tweaks 8a34583fa overload to take schemaPaths as filesystem::path 8671b442f parse XML embedded in a binary file ec4a902f1 updates from xerces.lite 80dc4d963 updates from xerces.lite 549766d6c Attributes::contains() no longer catches an exception 8a645ceac need "sys/" when building in other environments 36af08269 super-simple URI validation 78ef28a3e SWIG bindings are a PITA! :-( e9cba8491 SWIG needs help with Uri 8a8d8dc07 another routines used by pre-build SWIG bindings 818e1ec5d pre-build SWIG bindings use getElementByTagName() member function 067cac5d8 old compiler gets confused on unadorned QName ba92c0ae7 more use of Uri and QName 446c7d17a use QName in new code d6f8b0c83 more direct use of QName 90fff1c73 use xml::lite::QName instead of tuple 646cbb5ed more direct use of QName and Uri ba589ea3b make QName more robust bab0ee8b5 createElement() -> addNewElement() e3a145747 grab changes from six-library 32285e95c Merge branch 'master' into feature/xml.lite_tweaks 9f79f0bf6 Merge branch 'master' into feature/xml.lite_tweaks a12bbc32c make it easier to create new Elements with a value fc9967f98 make it easy for callers to addChild() keep a reference to the Element 4627766b7 be sure test_xmlparser works in "externals" of other projects bf2276396 "private" is part of the name-mangling fad92bcc8 making sure copy-ctor is implemented f90fdcead consolidate common XML test code 9fc53f2d5 use str:: utility for casting 6da6f794b still trying to find the right macro for SWIG 0c1b86c56 still trying to fix SWIG fdc6fc9bd trying to fix SWIG build error 7835e8c27 SWIG needs copy-ctor 585695942 disable copy/assignment for Element, it's probably almost always wrong 391fed613 fix double-delete caused by copying 61790fe69 retry parsing XML with Windows-1252 if first parse() fails 63cffac59 change string_encoding to match coda-oss style of PascalCase 010479bbe read an XML file we know is wrongly encoded as Windows-1252 9a0505062 more references instead of pointers 2d44b6951 Reading Windows-1252 w/o "encoding" fails 63dc7b076 read Windows-1252 too c9434c9cb test as UIT-8 too f310ccf0c get reading from UTF-8 XML working on Windows 1fa39c2be get testReadUtf8XmlFile working on Linux 1a83cd815 sys::Path is too much trouble right now ed60aa22c unit-test to read XML from a file a9336db7c Squashed commit of the following: 0825beb0d Merge branch 'master' into feature/xml.lite_tweaks c618489be Merge branch 'master' into feature/xml.lite_tweaks e8e4b8fe1 determine string_encoding based on platform 1f43bcfc2 create a new Element by using the platform to determine "characterData" encoding 961bef66b Merge branch 'master' into feature/xml.lite_tweaks e9798a5cb fix static_assert() 6f7772874 Merge branch 'master' into feature/xml.lite_tweaks b98d4f5a9 Merge branch 'master' into feature/xml.lite_tweaks 1b5abba2a The (old) version of SWIG we're using doesn't like certain C++11 features. 53bdeabaf Merge branch 'master' into feature/xml.lite_tweaks 60cf8ae80 "" doesn't work with decltype() in older C++ 97e72477a reduce getValue() overloads by making "key" a template argument 5e6373e55 reduce code duplication f9e7cfeee provide castValue instead of getValue(T&) cbd0bd8f2 castValue throws instead of returning a bool like getValue(T&) 87c7514fc Merge branch 'master' into feature/xml.lite_tweaks 10cc61223 make getElement*() consistent for zero or >1 results f5b137e3c Merge branch 'master' into feature/xml.lite_tweaks 1765efc62 allow clients to specify toType() and toString() for getValue() and setValue() df8b746e1 allow clients to specify their own toType/toString routines 66702726a Merge branch 'master' into feature/xml.lite_tweaks 6956311f1 Merge branch 'master' into feature/xml.lite_tweaks d505f3593 Merge branch 'master' into feature/xml.lite_tweaks fbd106115 catch a BadCastException and return false from getValue() 3a78377b5 use a template to reduce duplicated code 0ad4b8606 Merge branch 'master' into feature/xml.lite_tweaks a848aa3a2 get & set the characer data as a type f3ee1ee12 utility routines to set an attribute value 595227683 templates to get an attribute value convert to a specific type 06639227b miised a change in last commit 1aa458ef8 add getValue() overloads that return true/false rather than throwing faa6d3075 added getElementByTagName() overloads as that's a very common use-case git-subtree-dir: externals/coda-oss git-subtree-split: 03f1a3bdfa72d1baf84625fb6bcf9467e5792ace * Squashed 'externals/nitro/' changes from c8ecbe9ae..0357b1432 0357b1432 NITRO-2.11.5 (#589) a0ad6f943 CODA-OSS 2023-10-23 (#588) 1b631e578 latest from CODA-OSS (#587) 574bfe2b6 FmtX() -> str::Format() (#586) edfa0f7ce latest from CODA-OSS (#585) b26e15318 latest from CODA-OSS (#583) 0db9bdb29 fix ASAN diagnostics (#582) e418beceb Merge commit '6144b2cfb436a5696bab62c81651b47edf07aa8c' 6144b2cfb Squashed 'externals/coda-oss/' changes from 8dee6f7a68..c92a55d7c7 32d9c4fe4 latest from CODA-OSS 49f6338d2 latest from CODA-OSS (#581) 72cd86cbf latest from CODA-OSS (#580) e4920a743 FmtX() -> str::Format() 81ea6f178 Squashed 'externals/coda-oss/' changes from e87c32b4de..8dee6f7a68 e488297c3 Merge commit '81ea6f1789863f26f06dc447f7784383dc7cb595' 72209535d toString() changes from CODA-OSS (#579) fc1f59799 latest from CODA-OSS (#578) 02e8f80c6 latest from CODA-OSS (#577) e7ea728ec reduce differences between cpp17 and main branches (#576) 547d0aa9f NITRO-2.11.4 (#575) fe309c4ba Merge branch 'main' of github.com:mdaus/nitro 10efa9990 latest from CODA-OSS (#574) d08f1c0a1 CRLF db5d3d484 latest from CODA-OSS 98c755048 CRLF aa1482543 CRLF bfdbe69a4 Latest from CODA-OSS (#573) 9e4ce0b58 latest from CODA-OSS (#572) 227a8a8f4 trust coda-oss for right -std flags 13869687e latest from CODA-OSS (#571) 5724d8c18 latest from CODA-OSS (#570) fb794f0fe latest from CODA-OSS (#569) 7a6132ba0 update files changes in cpp17 branch (#568) de91d4977 Fix bug creating NITFException (#567) 225273436 fix YAML for 'main' 5d1c83d11 single project for unittests (#566) 102a019db latest from CODA-OSS (known broken build) (#565) b4ae2d429 match YAML from coda-oss 176bcaf6d build NITRO.SLN using msbuild (#562) 85e9043b8 latest from CODA-OSS (#561) 49ec50325 use new "PlatformToolkit" special environment variable (#560) 6c06e3711 latest from CODA-OSS (#559) cee9feb42 latest from CODA-OSS (#558) 3f01809fa latest from coda-oss (#557) 471fb3fc1 Update test_j2k_loading++.cpp b12caf2fc latest from CODA-OSS (#556) 0cd432624 use sys::make_span (#555) 05dae18a3 patch to build other projects 8f974e995 NITRO-2.11.3 (#554) 870aa6afd update to coda-oss 2023-06-05 (#553) 2fd7a0bfa latest from coda-oss (#552) 0eecce004 invoke() utility to reduce code duplication (#550) 59fb02fe9 latest from coda-oss (#551) 9fbf2b7b8 Fill out adapter free block which is used for nitf decompression (#549) 089ba0b5b latest from coda-oss 3b52f0025 latest from coda-oss (#547) 90c6263e2 latest from coda-oss (#544) 90d513ac5 latest from coda-oss (#543) git-subtree-dir: externals/nitro git-subtree-split: 0357b143293bc7b1ce5d5d941f1a5e6564b319a9 * latest from CODA-OSS * Squashed 'externals/coda-oss/' changes from 03f1a3bdf..3ae8f6afb 3ae8f6afb Merge pull request #751 from mdaus/bugfix/hdf5-missing-files-main 3ec510861 Add missing files 1693eac7e Update build_unittest.yml git-subtree-dir: externals/coda-oss git-subtree-split: 3ae8f6afb3816bbb2ca9a72fbbd9ef78644eb5bf * latest from CODA-OSS and NITRO * Squashed 'externals/coda-oss/' changes from 3ae8f6afb..92f8b88c7 92f8b88c7 std::ostringstream overloads 8cb27de27 reduce use of .c_str() and .str() (#752) git-subtree-dir: externals/coda-oss git-subtree-split: 92f8b88c758504e8d0d2a3dfb389b71d49b557e5 * Squashed 'externals/nitro/' changes from 0357b1432..0342b596a 0342b596a latest from CODA-OSS (#592) 0084f9bf7 reduce use of .str() (#591) 253f57b2b latest from CODA-OSS (#590) d6bc1d83a disable code-analysis to fix Github builds 981e75116 Update main.yml 907bf91cd Update frequent_check.yml git-subtree-dir: externals/nitro git-subtree-split: 0342b596aa3b4e1809086e4676d3e49fd8ccd261 * latest from CODA-OSS and NITRO * Squashed 'externals/coda-oss/' changes from 92f8b88c7..aea8e7b1e aea8e7b1e reduce use of str::toString() (#754) 8062154a4 HighFive 2,8,0 (#753) git-subtree-dir: externals/coda-oss git-subtree-split: aea8e7b1ee845039a62fafca207a4df6e8df15e0 * Squashed 'externals/nitro/' changes from 0342b596a..a37046807 a37046807 latest from CODA-OSS (#596) 205bf8e36 infrastructure for "preloading" TREs (#593) git-subtree-dir: externals/nitro git-subtree-split: a37046807ab36de5268e1247e901e704e3f1e81e * return type needs to be explicit for subsequent specialization --- externals/coda-oss/ReleaseNotes.md | 3 + .../modules/c++/cli/unittests/test_cli.cpp | 4 +- .../modules/c++/io/source/FileUtils.cpp | 2 +- .../modules/c++/io/unittests/test_streams.cpp | 4 +- .../c++/logging/source/StandardFormatter.cpp | 5 +- .../c++/logging/source/XMLFormatter.cpp | 4 +- .../include/math/linear/Eigenvalue.h | 4 +- .../c++/math.poly/include/math/poly/TwoD.h | 9 +- .../c++/math/include/math/ConvexHull.h | 3 +- .../modules/c++/math/source/Utilities.cpp | 2 +- .../modules/c++/mt/include/mt/WorkerThread.h | 2 +- .../modules/c++/net/source/Socket.cpp | 4 +- .../modules/c++/str/include/str/Convert.h | 44 +- .../c++/str/unittests/test_base_convert.cpp | 6 +- .../modules/c++/sys/include/sys/Conf.h | 4 +- .../modules/c++/sys/source/AbstractOS.cpp | 6 +- .../modules/c++/sys/source/DateTime.cpp | 6 +- .../modules/c++/sys/source/FileWin32.cpp | 2 +- .../modules/c++/sys/unittests/test_os.cpp | 2 +- .../modules/c++/tiff/source/Utils.cpp | 8 +- .../include/xml/lite/ValidatorInterface.h | 2 +- .../modules/c++/xml.lite/source/Element.cpp | 2 +- .../CMake/config/CompilerFlagsHelpers.cmake | 59 - .../CMake/config/ReleaseDebugAutoFlags.cmake | 45 - .../doc/poster/example_eigen.cpp | 35 - .../include/highfive/H5Attribute.hpp | 134 -- .../include/highfive/H5DataSpace.hpp | 118 -- .../src/examples/boost_multi_array_2D.cpp | 51 - .../create_attribute_string_integer.cpp | 69 -- .../src/examples/create_dataset_double.cpp | 46 - .../examples/create_dataset_half_float.cpp | 56 - .../src/examples/create_datatype.cpp | 110 -- .../examples/create_extensible_dataset.cpp | 71 -- .../src/examples/eigen_matrix.cpp | 50 - .../src/examples/hl_hdf5_inmemory_files.cpp | 74 -- .../examples/read_write_dataset_string.cpp | 60 - .../examples/read_write_fixedlen_string.cpp | 52 - .../src/examples/read_write_raw_ptr.cpp | 81 -- .../src/examples/read_write_single_scalar.cpp | 53 - .../examples/select_by_id_dataset_cpp11.cpp | 75 -- .../examples/select_partial_dataset_cpp11.cpp | 56 - .../include/simpleton.hpp | 27 - ...{HighFive-2.7.1.zip => HighFive-2.8.0.zip} | Bin 588796 -> 608394 bytes .../.clang-format | 0 .../.git-blame-ignore-revs | 0 .../.github/ISSUE_TEMPLATE/bug_report.md | 0 .../.github/ISSUE_TEMPLATE/config.yml | 0 .../.github/ISSUE_TEMPLATE/feature_request.md | 0 .../.github/build.sh | 0 .../.github/pull_request_template.md | 0 .../HighFive-2.8.0/.github/run_examples.sh | 20 + .../check_doxygen_awesome_version.yml | 0 .../.github/workflows/ci.yml | 127 +- .../.github/workflows/clang_format.yml | 3 + .../.github/workflows/coverage.yml | 15 +- .../.github/workflows/gh-pages.yml | 2 +- .../.github/workflows/integration_trigger.yml | 0 .../.github/workflows/version_file.yml | 36 + .../.gitignore | 0 .../.gitmodules | 0 .../.travis.yml | 0 .../AUTHORS.txt | 5 + .../CHANGELOG.md | 31 + .../CMake/HighFiveConfig.cmake.in | 8 +- .../CMake/HighFiveTargetDeps.cmake | 3 + .../CMake/HighFiveTargetExport.cmake | 1 - .../CMake/HighFiveWarnings.cmake | 36 + .../CMake/config/TestHelpers.cmake | 0 .../CMakeLists.txt | 27 +- .../LICENSE | 0 .../README.md | 130 +- .../highfive/HighFive-2.8.0/codecov.yml | 8 + .../doc/CMakeLists.txt | 0 .../doc/Doxyfile | 2 + .../doc/DoxygenLayout.xml | 2 +- .../HighFive-2.8.0/doc/developer_guide.md | 93 ++ .../doxygen-awesome-css/doxygen-awesome.css | 34 +- .../update_doxygen_awesome.sh | 0 .../doc/environment.yaml | 0 .../HighFive-2.8.0/doc/installation.md | 254 ++++ .../doc/poster/example1_hdf5.cpp | 0 .../doc/poster/example1_highfive.cpp | 0 .../doc/poster/example3.cpp | 2 +- .../doc/poster/example6.cpp | 4 +- .../doc/poster/example_boost.cpp | 4 +- .../doc/poster/example_boost_ublas.cpp | 2 +- .../doc/poster/example_easy_h5py.py | 0 .../doc/poster/example_easy_highfive.cpp | 1 + .../doc/poster/example_props.cpp | 2 +- .../doc/poster/examples.js | 0 .../doc/poster/godbolt.org.ico | Bin .../doc/poster/index.html | 0 .../include/highfive/H5Attribute.hpp | 266 ++++ .../include/highfive/H5DataSet.hpp | 0 .../include/highfive/H5DataSpace.hpp | 243 ++++ .../include/highfive/H5DataType.hpp | 86 +- .../include/highfive/H5Easy.hpp | 0 .../include/highfive/H5Exception.hpp | 0 .../include/highfive/H5File.hpp | 11 + .../include/highfive/H5FileDriver.hpp | 0 .../include/highfive/H5Group.hpp | 0 .../include/highfive/H5Object.hpp | 0 .../include/highfive/H5PropertyList.hpp | 134 +- .../include/highfive/H5Reference.hpp | 0 .../include/highfive/H5Selection.hpp | 0 .../include/highfive/H5Utility.hpp | 0 .../include/highfive/H5Version.hpp | 33 + .../include/highfive/H5Version.hpp.in | 0 .../highfive/bits/H5Annotate_traits.hpp | 0 .../highfive/bits/H5Annotate_traits_misc.hpp | 0 .../highfive/bits/H5Attribute_misc.hpp | 52 +- .../highfive/bits/H5Converter_misc.hpp | 421 +++++++ .../include/highfive/bits/H5DataSet_misc.hpp | 0 .../include/highfive/bits/H5DataType_misc.hpp | 168 ++- .../highfive/bits/H5Dataspace_misc.hpp | 13 +- .../highfive/bits/H5Exception_misc.hpp | 0 .../highfive/bits/H5FileDriver_misc.hpp | 0 .../include/highfive/bits/H5File_misc.hpp | 18 + .../include/highfive/bits/H5Friends.hpp | 0 .../highfive/bits/H5Inspector_misc.hpp} | 153 +-- .../highfive/bits/H5Iterables_misc.hpp | 0 .../include/highfive/bits/H5Node_traits.hpp | 22 + .../highfive/bits/H5Node_traits_misc.hpp | 36 + .../include/highfive/bits/H5Object_misc.hpp | 0 .../include/highfive/bits/H5Path_traits.hpp | 0 .../highfive/bits/H5Path_traits_misc.hpp | 2 +- .../highfive/bits/H5PropertyList_misc.hpp | 30 +- .../highfive/bits/H5ReadWrite_misc.hpp | 60 +- .../highfive/bits/H5Reference_misc.hpp | 0 .../highfive/bits/H5Selection_misc.hpp | 0 .../include/highfive/bits/H5Slice_traits.hpp | 44 +- .../highfive/bits/H5Slice_traits_misc.hpp | 63 +- .../include/highfive/bits/H5Utils.hpp | 0 .../include/highfive/bits/H5_definitions.hpp | 0 .../include/highfive/bits/string_padding.hpp | 14 + .../highfive/h5easy_bits/H5Easy_Eigen.hpp | 0 .../highfive/h5easy_bits/H5Easy_misc.hpp | 0 .../highfive/h5easy_bits/H5Easy_opencv.hpp | 0 .../highfive/h5easy_bits/H5Easy_public.hpp | 0 .../highfive/h5easy_bits/H5Easy_scalar.hpp | 0 .../highfive/h5easy_bits/H5Easy_vector.hpp | 0 .../highfive/h5easy_bits/H5Easy_xtensor.hpp | 0 .../include/highfive/highfive.hpp | 14 + .../src/benchmarks/README.md | 0 .../src/benchmarks/hdf5_bench.cpp | 0 .../src/benchmarks/hdf5_bench_improved.cpp | 0 .../src/benchmarks/highfive_bench.cpp | 2 +- .../src/benchmarks/imgs/bench_hdf5_base.png | Bin .../benchmarks/imgs/bench_hdf5_improved.png | Bin .../src/benchmarks/imgs/bench_highfive.png | Bin .../src/benchmarks/run_benchmark.sh | 0 .../src/examples/CMakeLists.txt | 20 +- .../src/examples/boost_multi_array_2D.cpp | 40 + .../src/examples/boost_multiarray_complex.cpp | 7 +- .../src/examples/boost_ublas_double.cpp | 3 +- .../src/examples/compound_types.cpp | 11 +- .../create_attribute_string_integer.cpp | 54 + .../src/examples/create_dataset_double.cpp | 38 + .../examples/create_dataset_half_float.cpp | 49 + .../src/examples/create_datatype.cpp | 101 ++ .../examples/create_extensible_dataset.cpp | 63 + .../src/examples/create_large_attribute.cpp | 19 + .../examples/create_page_allocated_files.cpp | 46 +- .../src/examples/easy_attribute.cpp | 1 - .../src/examples/easy_dumpoptions.cpp | 0 .../src/examples/easy_load_dump.cpp | 0 .../src/examples/hl_hdf5_inmemory_files.cpp | 69 ++ .../examples/parallel_hdf5_collective_io.cpp | 14 +- .../examples/parallel_hdf5_independent_io.cpp | 12 +- .../examples/read_write_dataset_string.cpp | 51 + .../examples/read_write_fixedlen_string.cpp | 42 + .../src/examples/read_write_raw_ptr.cpp | 73 ++ .../src/examples/read_write_single_scalar.cpp | 46 + .../src/examples/read_write_std_strings.cpp | 114 ++ .../examples/read_write_vector_dataset.cpp | 28 +- .../read_write_vector_dataset_references.cpp | 16 +- .../src/examples/readme_snippet.cpp | 9 +- .../src/examples/renaming_objects.cpp | 6 +- .../examples/select_by_id_dataset_cpp11.cpp | 67 + .../examples/select_partial_dataset_cpp11.cpp | 49 + .../test_dependent_library/CMakeLists.txt | 2 +- .../test_dependent_library/deps/.gitignore | 0 .../include/simpleton.hpp | 14 + .../test_dependent_library/src/otherton.cpp | 0 .../test_dependent_library/src/simpleton.cpp | 0 .../tests/test_project_integration.sh | 2 +- .../tests/unit/CMakeLists.txt | 7 +- .../tests/unit/test_all_types.cpp | 20 +- .../tests/unit/tests_high_five.hpp | 19 +- .../tests/unit/tests_high_five_base.cpp | 635 +++++++++- .../tests/unit/tests_high_five_easy.cpp | 71 +- .../tests/unit/tests_high_five_multi_dims.cpp | 3 +- .../tests/unit/tests_high_five_parallel.cpp | 6 +- .../unit/tests_import_public_headers.cpp | 0 .../highfive/include/highfive/H5Attribute.hpp | 200 ++- .../highfive/include/highfive/H5DataSpace.hpp | 203 +++- .../highfive/include/highfive/H5DataType.hpp | 86 +- .../highfive/include/highfive/H5File.hpp | 11 + .../include/highfive/H5PropertyList.hpp | 134 +- .../highfive/include/highfive/H5Version.hpp | 33 + .../highfive/bits/H5Attribute_misc.hpp | 52 +- .../highfive/bits/H5Converter_misc.hpp | 1078 +++++------------ .../include/highfive/bits/H5DataType_misc.hpp | 168 ++- .../highfive/bits/H5Dataspace_misc.hpp | 13 +- .../include/highfive/bits/H5File_misc.hpp | 18 + .../highfive/bits/H5Inspector_misc.hpp | 858 +++++++++++++ .../include/highfive/bits/H5Node_traits.hpp | 22 + .../highfive/bits/H5Node_traits_misc.hpp | 36 + .../highfive/bits/H5Path_traits_misc.hpp | 2 +- .../highfive/bits/H5PropertyList_misc.hpp | 30 +- .../highfive/bits/H5ReadWrite_misc.hpp | 60 +- .../include/highfive/bits/H5Slice_traits.hpp | 44 +- .../highfive/bits/H5Slice_traits_misc.hpp | 63 +- .../include/highfive/bits/string_padding.hpp | 14 + .../highfive/include/highfive/highfive.hpp | 14 + .../highfive/include/import/highfive.h | 1 + .../highfive/unittests/tests_high_five.hpp | 31 +- .../unittests/tests_high_five_base.cpp | 964 +++++++++++---- externals/nitro/UnitTest/UnitTest.vcxproj | 30 - .../c++/nitf/unittests/test_load_plugins.cpp | 7 +- externals/nitro/modules/c/nitf-c.vcxproj | 9 + .../nitro/modules/c/nitf-c.vcxproj.filters | 12 + externals/nitro/modules/c/nitf/CMakeLists.txt | 1 + .../modules/c/nitf/include/nitf/Defines.h | 21 +- .../c/nitf/include/nitf/PluginIdentifier.h | 7 + .../c/nitf/include/nitf/PluginRegistry.h | 2 + .../nitro/modules/c/nitf/shared/ACCHZB.c | 4 +- externals/nitro/modules/c/nitf/shared/ACFTA.c | 4 +- .../nitro/modules/c/nitf/shared/AIMIDA.c | 2 +- .../nitro/modules/c/nitf/shared/AIMIDB.c | 4 +- .../nitro/modules/c/nitf/shared/CSCRNA.c | 4 +- .../nitro/modules/c/nitf/shared/CSEXRB.c | 4 +- .../nitro/modules/c/nitf/shared/ENGRDA.c | 14 +- .../nitro/modules/c/nitf/shared/EXPLTA.c | 2 +- .../nitro/modules/c/nitf/shared/HISTOA.c | 4 +- .../nitro/modules/c/nitf/shared/IOMAPA.c | 2 +- .../nitro/modules/c/nitf/shared/JITCID.c | 4 +- .../nitro/modules/c/nitf/shared/MENSRA.c | 2 +- .../nitro/modules/c/nitf/shared/PATCHA.c | 2 +- .../nitro/modules/c/nitf/shared/PTPRAA.c | 4 +- .../nitro/modules/c/nitf/shared/RPFHDR.c | 4 +- .../nitro/modules/c/nitf/shared/TEST_DES.c | 22 +- .../modules/c/nitf/shared/XML_DATA_CONTENT.c | 4 +- .../modules/c/nitf/source/PluginRegistry.c | 147 ++- externals/nitro/modules/c/nitf/source/TREs.c | 67 + .../nitro/modules/c/nrt/include/nrt/DLL.h | 3 +- .../nitro/modules/c/nrt/source/DLLUnix.c | 11 + .../nitro/modules/c/nrt/source/DLLWin32.c | 14 +- externals/nitro/nitro.sln | 42 +- 249 files changed, 7300 insertions(+), 3308 deletions(-) delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/config/CompilerFlagsHelpers.cmake delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/config/ReleaseDebugAutoFlags.cmake delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_eigen.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Attribute.hpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5DataSpace.hpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/boost_multi_array_2D.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_attribute_string_integer.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_dataset_double.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_dataset_half_float.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_datatype.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_extensible_dataset.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/eigen_matrix.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/hl_hdf5_inmemory_files.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_dataset_string.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_fixedlen_string.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_raw_ptr.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_single_scalar.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/select_by_id_dataset_cpp11.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/select_partial_dataset_cpp11.cpp delete mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_dependent_library/include/simpleton.hpp rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1.zip => HighFive-2.8.0.zip} (67%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.clang-format (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.git-blame-ignore-revs (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.github/ISSUE_TEMPLATE/bug_report.md (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.github/ISSUE_TEMPLATE/config.yml (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.github/ISSUE_TEMPLATE/feature_request.md (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.github/build.sh (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.github/pull_request_template.md (100%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/run_examples.sh rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.github/workflows/check_doxygen_awesome_version.yml (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.github/workflows/ci.yml (75%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.github/workflows/clang_format.yml (90%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.github/workflows/coverage.yml (73%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.github/workflows/gh-pages.yml (94%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.github/workflows/integration_trigger.yml (100%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/version_file.yml rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.gitignore (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.gitmodules (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/.travis.yml (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/AUTHORS.txt (92%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/CHANGELOG.md (86%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/CMake/HighFiveConfig.cmake.in (93%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/CMake/HighFiveTargetDeps.cmake (96%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/CMake/HighFiveTargetExport.cmake (96%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/HighFiveWarnings.cmake rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/CMake/config/TestHelpers.cmake (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/CMakeLists.txt (90%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/LICENSE (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/README.md (60%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/codecov.yml rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/CMakeLists.txt (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/Doxyfile (99%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/DoxygenLayout.xml (100%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/developer_guide.md rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/doxygen-awesome-css/doxygen-awesome.css (99%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/doxygen-awesome-css/update_doxygen_awesome.sh (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/environment.yaml (100%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/installation.md rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/poster/example1_hdf5.cpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/poster/example1_highfive.cpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/poster/example3.cpp (95%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/poster/example6.cpp (93%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/poster/example_boost.cpp (85%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/poster/example_boost_ublas.cpp (97%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/poster/example_easy_h5py.py (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/poster/example_easy_highfive.cpp (99%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/poster/example_props.cpp (94%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/poster/examples.js (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/poster/godbolt.org.ico (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/doc/poster/index.html (100%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Attribute.hpp rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/H5DataSet.hpp (100%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5DataSpace.hpp rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/H5DataType.hpp (80%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/H5Easy.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/H5Exception.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/H5File.hpp (88%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/H5FileDriver.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/H5Group.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/H5Object.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/H5PropertyList.hpp (77%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/H5Reference.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/H5Selection.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/H5Utility.hpp (100%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Version.hpp rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/H5Version.hpp.in (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Annotate_traits.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Annotate_traits_misc.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Attribute_misc.hpp (80%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Converter_misc.hpp rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5DataSet_misc.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5DataType_misc.hpp (76%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Dataspace_misc.hpp (92%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Exception_misc.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5FileDriver_misc.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5File_misc.hpp (87%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Friends.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1/include/highfive/bits/H5Converter_misc.hpp => HighFive-2.8.0/include/highfive/bits/H5Inspector_misc.hpp} (88%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Iterables_misc.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Node_traits.hpp (90%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Node_traits_misc.hpp (90%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Object_misc.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Path_traits.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Path_traits_misc.hpp (95%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5PropertyList_misc.hpp (95%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5ReadWrite_misc.hpp (72%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Reference_misc.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Selection_misc.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Slice_traits.hpp (85%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Slice_traits_misc.hpp (86%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5Utils.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/bits/H5_definitions.hpp (100%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/string_padding.hpp rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/h5easy_bits/H5Easy_Eigen.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/h5easy_bits/H5Easy_misc.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/h5easy_bits/H5Easy_opencv.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/h5easy_bits/H5Easy_public.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/h5easy_bits/H5Easy_scalar.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/h5easy_bits/H5Easy_vector.hpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/include/highfive/h5easy_bits/H5Easy_xtensor.hpp (100%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/highfive.hpp rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/benchmarks/README.md (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/benchmarks/hdf5_bench.cpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/benchmarks/hdf5_bench_improved.cpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/benchmarks/highfive_bench.cpp (90%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/benchmarks/imgs/bench_hdf5_base.png (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/benchmarks/imgs/bench_hdf5_improved.png (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/benchmarks/imgs/bench_highfive.png (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/benchmarks/run_benchmark.sh (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/CMakeLists.txt (63%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/boost_multi_array_2D.cpp rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/boost_multiarray_complex.cpp (88%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/boost_ublas_double.cpp (97%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/compound_types.cpp (73%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_attribute_string_integer.cpp create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_dataset_double.cpp create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_dataset_half_float.cpp create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_datatype.cpp create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_extensible_dataset.cpp create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_large_attribute.cpp rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/create_page_allocated_files.cpp (53%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/easy_attribute.cpp (99%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/easy_dumpoptions.cpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/easy_load_dump.cpp (100%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/hl_hdf5_inmemory_files.cpp rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/parallel_hdf5_collective_io.cpp (90%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/parallel_hdf5_independent_io.cpp (90%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_dataset_string.cpp create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_fixedlen_string.cpp create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_raw_ptr.cpp create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_single_scalar.cpp create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_std_strings.cpp rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/read_write_vector_dataset.cpp (64%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/read_write_vector_dataset_references.cpp (85%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/readme_snippet.cpp (70%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/src/examples/renaming_objects.cpp (94%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/select_by_id_dataset_cpp11.cpp create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/select_partial_dataset_cpp11.cpp rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/tests/test_dependent_library/CMakeLists.txt (97%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/tests/test_dependent_library/deps/.gitignore (100%) create mode 100644 externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_dependent_library/include/simpleton.hpp rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/tests/test_dependent_library/src/otherton.cpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/tests/test_dependent_library/src/simpleton.cpp (100%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/tests/test_project_integration.sh (95%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/tests/unit/CMakeLists.txt (90%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/tests/unit/test_all_types.cpp (90%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/tests/unit/tests_high_five.hpp (95%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/tests/unit/tests_high_five_base.cpp (82%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/tests/unit/tests_high_five_easy.cpp (78%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/tests/unit/tests_high_five_multi_dims.cpp (98%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/tests/unit/tests_high_five_parallel.cpp (97%) rename externals/coda-oss/modules/drivers/highfive/{HighFive-2.7.1 => HighFive-2.8.0}/tests/unit/tests_import_public_headers.cpp (100%) create mode 100644 externals/coda-oss/modules/drivers/highfive/include/highfive/H5Version.hpp create mode 100644 externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Inspector_misc.hpp create mode 100644 externals/coda-oss/modules/drivers/highfive/include/highfive/bits/string_padding.hpp create mode 100644 externals/coda-oss/modules/drivers/highfive/include/highfive/highfive.hpp create mode 100644 externals/nitro/modules/c/nitf/source/TREs.c diff --git a/externals/coda-oss/ReleaseNotes.md b/externals/coda-oss/ReleaseNotes.md index 0c8abecb73..bd37823dd2 100644 --- a/externals/coda-oss/ReleaseNotes.md +++ b/externals/coda-oss/ReleaseNotes.md @@ -1,5 +1,8 @@ # coda-oss Release Notes +## [Release 202?-??-??](https://github.com/mdaus/coda-oss/releases/tag/202?-??-??) +* Update to [HighFive 2.8.0](https://github.com/BlueBrain/HighFive/releases/tag/v2.8.0). + ## [Release 2023-10-23](https://github.com/mdaus/coda-oss/releases/tag/2023-10-23) * Tweaked **.gitattributes**. * Removed *str::EncodedStringView*; made *str* conversion routines more consistent. diff --git a/externals/coda-oss/modules/c++/cli/unittests/test_cli.cpp b/externals/coda-oss/modules/c++/cli/unittests/test_cli.cpp index d8d1578ff9..288df201a6 100644 --- a/externals/coda-oss/modules/c++/cli/unittests/test_cli.cpp +++ b/externals/coda-oss/modules/c++/cli/unittests/test_cli.cpp @@ -45,7 +45,7 @@ TEST_CASE(testValue) for(int i = 0; i < 10; ++i) { floats.push_back(10.0f * i); - strings.push_back(str::toString(i)); + strings.push_back(std::to_string(i)); } // floats @@ -60,7 +60,7 @@ TEST_CASE(testValue) v.setContainer(strings); for(int i = 0; i < 10; ++i) { - TEST_ASSERT_EQ(v.at(i), str::toString(i)); + TEST_ASSERT_EQ(v.at(i), std::to_string(i)); } TEST_ASSERT_EQ(std::ssize(v), 10); } diff --git a/externals/coda-oss/modules/c++/io/source/FileUtils.cpp b/externals/coda-oss/modules/c++/io/source/FileUtils.cpp index e0aaf24ea7..5e34ef8cc0 100644 --- a/externals/coda-oss/modules/c++/io/source/FileUtils.cpp +++ b/externals/coda-oss/modules/c++/io/source/FileUtils.cpp @@ -154,7 +154,7 @@ std::string io::FileUtils::createFile(std::string dirname, int count = 0; while (os.exists(outFilename)) { - std::string base = filename + "-" + str::toString(++count); + std::string base = filename + "-" + std::to_string(++count); outFilename = sys::Path::joinPaths(dirname, base); if (!ext.empty()) { diff --git a/externals/coda-oss/modules/c++/io/unittests/test_streams.cpp b/externals/coda-oss/modules/c++/io/unittests/test_streams.cpp index f3d777ed89..ce253f7e3e 100644 --- a/externals/coda-oss/modules/c++/io/unittests/test_streams.cpp +++ b/externals/coda-oss/modules/c++/io/unittests/test_streams.cpp @@ -259,8 +259,8 @@ TEST_CASE(testRotate) for(size_t i = 0; i < maxFiles - 1; ++i) { - std::string fname = outFile + "." + str::toString(i + 1); - std::string next = outFile + "." + str::toString(i + 2); + std::string fname = outFile + "." + std::to_string(i + 1); + std::string next = outFile + "." + std::to_string(i + 2); TEST_ASSERT(os.isFile(fname)); TEST_ASSERT_FALSE(os.isFile(next)); diff --git a/externals/coda-oss/modules/c++/logging/source/StandardFormatter.cpp b/externals/coda-oss/modules/c++/logging/source/StandardFormatter.cpp index 82eeac8e79..32e687c340 100644 --- a/externals/coda-oss/modules/c++/logging/source/StandardFormatter.cpp +++ b/externals/coda-oss/modules/c++/logging/source/StandardFormatter.cpp @@ -48,15 +48,14 @@ void StandardFormatter::format(const LogRecord* record, io::OutputStream& os) co // populate log long threadId = sys::getThreadID(); std::string format = mFmt; - str::replace(format, THREAD_ID, str::toString(threadId)); + str::replace(format, THREAD_ID, std::to_string(threadId)); str::replace(format, LOG_NAME, name); str::replace(format, LOG_LEVEL, record->getLevelName()); str::replace(format, TIMESTAMP, record->getTimeStamp()); if (record->getLineNum() >= 0) { str::replace(format, FILE_NAME, record->getFile()); - str::replace(format, LINE_NUM, - str::toString(record->getLineNum())); + str::replace(format, LINE_NUM, std::to_string(record->getLineNum())); } else { diff --git a/externals/coda-oss/modules/c++/logging/source/XMLFormatter.cpp b/externals/coda-oss/modules/c++/logging/source/XMLFormatter.cpp index b0bd450bfc..c3f41b2f46 100644 --- a/externals/coda-oss/modules/c++/logging/source/XMLFormatter.cpp +++ b/externals/coda-oss/modules/c++/logging/source/XMLFormatter.cpp @@ -54,8 +54,8 @@ void logging::XMLFormatter::format(const logging::LogRecord* record, io::OutputS // conver record std::string name = (record->getName().empty()) ? ("DEFAULT") : record->getName(); - std::string line = str::toString(record->getLineNum()); - std::string threadID = str::toString(sys::getThreadID()); + const auto line = std::to_string(record->getLineNum()); + const auto threadID = std::to_string(sys::getThreadID()); // populate vector with record std::vector logRecord; diff --git a/externals/coda-oss/modules/c++/math.linear/include/math/linear/Eigenvalue.h b/externals/coda-oss/modules/c++/math.linear/include/math/linear/Eigenvalue.h index 39edf699d2..724e031d36 100644 --- a/externals/coda-oss/modules/c++/math.linear/include/math/linear/Eigenvalue.h +++ b/externals/coda-oss/modules/c++/math.linear/include/math/linear/Eigenvalue.h @@ -96,9 +96,7 @@ class Eigenvalue if (A.rows() != A.cols()) { throw except::Exception(Ctxt( - "Expected square matrix but got rows = " + - str::toString(A.rows()) + ", cols = " + - str::toString(A.cols()))); + "Expected square matrix but got rows = " + std::to_string(A.rows()) + ", cols = " + std::to_string(A.cols()))); } if (isSymmetric(A)) diff --git a/externals/coda-oss/modules/c++/math.poly/include/math/poly/TwoD.h b/externals/coda-oss/modules/c++/math.poly/include/math/poly/TwoD.h index 05c8893f4b..574d49c543 100644 --- a/externals/coda-oss/modules/c++/math.poly/include/math/poly/TwoD.h +++ b/externals/coda-oss/modules/c++/math.poly/include/math/poly/TwoD.h @@ -109,15 +109,10 @@ class TwoD void set(size_t i, const OneD<_T>& p) { if (i > orderX()) - throw except::Exception( - Ctxt("Index [" + str::toString(i) + - "] is out of bounds for orderX [" + - str::toString(orderX()) + "]")); + throw except::Exception(Ctxt("Index [" + std::to_string(i) + "] is out of bounds for orderX [" + std::to_string(orderX()) + "]")); else if (p.order() != orderY()) throw except::Exception( - Ctxt("OneD poly [" + str::toString(p.order()) + - "] is of the incorrect size for orderY [" + - str::toString(orderY()) + "]")); + Ctxt("OneD poly [" + std::to_string(p.order()) + "] is of the incorrect size for orderY [" + std::to_string(orderY()) + "]")); else mCoef[i] = p; } diff --git a/externals/coda-oss/modules/c++/math/include/math/ConvexHull.h b/externals/coda-oss/modules/c++/math/include/math/ConvexHull.h index 787e231195..55849f9bcd 100644 --- a/externals/coda-oss/modules/c++/math/include/math/ConvexHull.h +++ b/externals/coda-oss/modules/c++/math/include/math/ConvexHull.h @@ -97,8 +97,7 @@ class ConvexHull { throw except::Exception(Ctxt( "ConvexHull constructor error: must use at least 2 input " - "points but " + str::toString(rawPoints.size()) + - " were used")); + "points but " + std::to_string(rawPoints.size()) + " were used")); } // Enforce (at compile time) that T is a signed type diff --git a/externals/coda-oss/modules/c++/math/source/Utilities.cpp b/externals/coda-oss/modules/c++/math/source/Utilities.cpp index 7d4cbc73c1..aa60b36fc7 100644 --- a/externals/coda-oss/modules/c++/math/source/Utilities.cpp +++ b/externals/coda-oss/modules/c++/math/source/Utilities.cpp @@ -41,7 +41,7 @@ sys::Uint64_T nChooseK(size_t n, size_t k) if (n < k) { throw except::Exception(Ctxt("n Choose k undefined for n < k.\n" - "n: " + str::toString(n) + " k: " + str::toString(k))); + "n: " + std::to_string(n) + " k: " + std::to_string(k))); } // Algorithm to compute n Choose k without using factorials found here: diff --git a/externals/coda-oss/modules/c++/mt/include/mt/WorkerThread.h b/externals/coda-oss/modules/c++/mt/include/mt/WorkerThread.h index 2a2e204684..d2321e5491 100644 --- a/externals/coda-oss/modules/c++/mt/include/mt/WorkerThread.h +++ b/externals/coda-oss/modules/c++/mt/include/mt/WorkerThread.h @@ -98,7 +98,7 @@ template class WorkerThread : public sys::Thread */ std::string getThreadId() { - return str::toString(sys::getThreadID()); + return std::to_string(sys::getThreadID()); } protected: diff --git a/externals/coda-oss/modules/c++/net/source/Socket.cpp b/externals/coda-oss/modules/c++/net/source/Socket.cpp index b7b8627c5d..33c44057d9 100644 --- a/externals/coda-oss/modules/c++/net/source/Socket.cpp +++ b/externals/coda-oss/modules/c++/net/source/Socket.cpp @@ -101,9 +101,7 @@ size_t net::Socket::recv(void* b, size_t len, int flags) sys::Err err; std::ostringstream oss; - oss << "When receiving " << str::toString(len) << " bytes: " << - err.toString(); - + oss << "When receiving " << len << " bytes: " << err.toString(); throw sys::SocketException(Ctxt(oss)); } else if (numBytes == 0) diff --git a/externals/coda-oss/modules/c++/str/include/str/Convert.h b/externals/coda-oss/modules/c++/str/include/str/Convert.h index f58f233da9..cbcbcf0e15 100644 --- a/externals/coda-oss/modules/c++/str/include/str/Convert.h +++ b/externals/coda-oss/modules/c++/str/include/str/Convert.h @@ -55,14 +55,56 @@ template int getPrecision(const types::ComplexInteger&); // Note that std::to_string() doesn't necessarily generate the same output as writing // to std::cout; see https://en.cppreference.com/w/cpp/string/basic_string/to_string template -std::string toString(const T& value) +std::string toString_(const T& value) { std::ostringstream buf; buf.precision(getPrecision(value)); buf << std::boolalpha << value; return buf.str(); } +template +inline std::string toString(const T& value) +{ + return toString_(value); +} +// https://en.cppreference.com/w/cpp/string/basic_string/to_string +inline auto toString(int value) +{ + return toString_(value); +} +inline auto toString(long value) +{ + return toString_(value); +} +inline auto toString(long long value) +{ + return toString_(value); +} +inline auto toString(unsigned value) +{ + return toString_(value); +} +inline auto toString(unsigned long value) +{ + return toString_(value); +} +inline auto toString(unsigned long long value) +{ + return toString_(value); +} +inline auto toString(float value) +{ + return toString_(value); +} +inline auto toString(double value) +{ + return toString_(value); +} +inline auto toString(long double value) +{ + return toString_(value); +} inline std::string toString(uint8_t value) { return toString(gsl::narrow(value)); diff --git a/externals/coda-oss/modules/c++/str/unittests/test_base_convert.cpp b/externals/coda-oss/modules/c++/str/unittests/test_base_convert.cpp index fe7bfc3091..3b951015ad 100644 --- a/externals/coda-oss/modules/c++/str/unittests/test_base_convert.cpp +++ b/externals/coda-oss/modules/c++/str/unittests/test_base_convert.cpp @@ -64,9 +64,9 @@ TEST_CASE(testBadConvert) TEST_CASE(testEightBitIntToString) { - TEST_ASSERT_EQ(str::toString(static_cast(1)), "1"); - TEST_ASSERT_EQ(str::toString(static_cast(2)), "2"); - TEST_ASSERT_EQ(str::toString(static_cast(-2)), "-2"); + TEST_ASSERT_EQ(std::to_string(static_cast(1)), "1"); + TEST_ASSERT_EQ(std::to_string(static_cast(2)), "2"); + TEST_ASSERT_EQ(std::to_string(static_cast(-2)), "-2"); } TEST_CASE(testCharToString) diff --git a/externals/coda-oss/modules/c++/sys/include/sys/Conf.h b/externals/coda-oss/modules/c++/sys/include/sys/Conf.h index 6dd45ea28d..3469b69cf4 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/Conf.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/Conf.h @@ -217,9 +217,7 @@ namespace sys #error "Don't know how to implement alignedAlloc()." #endif if (!p) - throw except::Exception(Ctxt( - "Aligned allocation failure of size [" + - str::toString(size) + "] bytes")); + throw except::Exception(Ctxt("Aligned allocation failure of size [" + std::to_string(size) + "] bytes")); return p; } diff --git a/externals/coda-oss/modules/c++/sys/source/AbstractOS.cpp b/externals/coda-oss/modules/c++/sys/source/AbstractOS.cpp index d1cd33306f..dd22c37868 100644 --- a/externals/coda-oss/modules/c++/sys/source/AbstractOS.cpp +++ b/externals/coda-oss/modules/c++/sys/source/AbstractOS.cpp @@ -292,7 +292,7 @@ static std::string getSpecialEnv_PID(const AbstractOS& os, const std::string& en UNREFERENCED_PARAMETER(envVar); #endif const auto pid = os.getProcessId(); - return str::toString(pid); + return std::to_string(pid); } static std::string getSpecialEnv_USER(const AbstractOS& os, const std::string& envVar) @@ -394,7 +394,7 @@ static std::string getSpecialEnv_SECONDS_() // https://en.cppreference.com/w/cpp/chrono/c/difftime static const auto start = std::time(nullptr); const auto diff = static_cast(std::difftime(std::time(nullptr), start)); - return str::toString(diff); + return std::to_string(diff); } static std::string getSpecialEnv_SECONDS(const AbstractOS&, const std::string& envVar) { @@ -475,7 +475,7 @@ std::string AbstractOS::getSpecialEnv(const std::string& envVar) const if (envVar == "EPOCHSECONDS") { - return str::toString(sys::DateTime::getEpochSeconds()); + return std::to_string(sys::DateTime::getEpochSeconds()); } if (envVar == "OSTYPE") diff --git a/externals/coda-oss/modules/c++/sys/source/DateTime.cpp b/externals/coda-oss/modules/c++/sys/source/DateTime.cpp index a79b9bb8c0..b6669002d0 100644 --- a/externals/coda-oss/modules/c++/sys/source/DateTime.cpp +++ b/externals/coda-oss/modules/c++/sys/source/DateTime.cpp @@ -104,9 +104,7 @@ char* strptime(const char *buf, const char *fmt, struct tm& tm, double& millis) { bc = *bp++; if (bc != fc) - throw except::Exception(Ctxt( - "Value does not match format (" + str::toString(fc) + - "): " + str::toString(bc))); + throw except::Exception(Ctxt("Value does not match format (" + str::toString(fc) + "): " + str::toString(bc))); continue; } @@ -316,7 +314,7 @@ char* strptime(const char *buf, const char *fmt, struct tm& tm, double& millis) case 'Y': // The year. i = TM_YEAR_BASE; if (!(conv_num(bp, i, 0, 9999))) - throw except::Exception(Ctxt("Invalid year: " + str::toString(i))); + throw except::Exception(Ctxt("Invalid year: " + std::to_string(i))); tm.tm_year = i - TM_YEAR_BASE; break; diff --git a/externals/coda-oss/modules/c++/sys/source/FileWin32.cpp b/externals/coda-oss/modules/c++/sys/source/FileWin32.cpp index d1ed79a67d..7ab87bb47d 100644 --- a/externals/coda-oss/modules/c++/sys/source/FileWin32.cpp +++ b/externals/coda-oss/modules/c++/sys/source/FileWin32.cpp @@ -140,7 +140,7 @@ sys::Off_T sys::File::seekTo(sys::Off_T offset, int whence) if (SetFilePointerEx(mHandle, largeInt, &newFilePointer, dwMoveMethod) == 0) { const auto dwLastError = GetLastError(); - throw sys::SystemException(Ctxt("SetFilePointer failed: GetLastError() = " + str::toString(dwLastError))); + throw sys::SystemException(Ctxt("SetFilePointer failed: GetLastError() = " + std::to_string(dwLastError))); } return static_cast(newFilePointer.QuadPart); diff --git a/externals/coda-oss/modules/c++/sys/unittests/test_os.cpp b/externals/coda-oss/modules/c++/sys/unittests/test_os.cpp index 2261acd0ea..ba1d71b9af 100644 --- a/externals/coda-oss/modules/c++/sys/unittests/test_os.cpp +++ b/externals/coda-oss/modules/c++/sys/unittests/test_os.cpp @@ -350,7 +350,7 @@ TEST_CASE(testSpecialEnvVars) result = os.getSpecialEnv("$"); // i.e., ${$} TEST_ASSERT_FALSE(result.empty()); TEST_ASSERT_EQ(result, pid); - const auto strPid = str::toString(os.getProcessId()); + const auto strPid = std::to_string(os.getProcessId()); TEST_ASSERT_EQ(result, strPid); result = os.getSpecialEnv("PWD"); diff --git a/externals/coda-oss/modules/c++/tiff/source/Utils.cpp b/externals/coda-oss/modules/c++/tiff/source/Utils.cpp index ff6e60d044..0c15220d50 100644 --- a/externals/coda-oss/modules/c++/tiff/source/Utils.cpp +++ b/externals/coda-oss/modules/c++/tiff/source/Utils.cpp @@ -106,9 +106,8 @@ tiff::IFD* tiff::Utils::createGeoTiffIFD(tiff::IFD* ifd) if (idx + 3 >= geoVals.size()) { throw except::Exception(Ctxt( - "'GeoKeyDirectoryTag' specified " + - str::toString(numKeys) + " keys but the IFD entry only had " + - str::toString(geoVals.size()) + " values")); + "'GeoKeyDirectoryTag' specified " + + std::to_string(numKeys) + " keys but the IFD entry only had " + std::to_string(geoVals.size()) + " values")); } const unsigned short keyId = @@ -143,8 +142,7 @@ tiff::IFD* tiff::Utils::createGeoTiffIFD(tiff::IFD* ifd) { if (count != 1) { - throw except::Exception(Ctxt( - "Expected a count of 1 but got " + str::toString(count))); + throw except::Exception(Ctxt("Expected a count of 1 but got " + std::to_string(count))); } entry->addValue(new tiff::GenericType(valueStr)); diff --git a/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/ValidatorInterface.h b/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/ValidatorInterface.h index 4e50656eff..024c0a8b16 100644 --- a/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/ValidatorInterface.h +++ b/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/ValidatorInterface.h @@ -79,7 +79,7 @@ struct ValidationInfo final std::ostringstream oss; oss << "[" << this->getLevel() << "]" << " from File: " << this->getFile() << - " on Line: " << str::toString(this->getLine()) << + " on Line: " << this->getLine() << " with Message: " << this->getMessage(); return oss.str(); } diff --git a/externals/coda-oss/modules/c++/xml.lite/source/Element.cpp b/externals/coda-oss/modules/c++/xml.lite/source/Element.cpp index 5da980abae..dcc211e6ca 100644 --- a/externals/coda-oss/modules/c++/xml.lite/source/Element.cpp +++ b/externals/coda-oss/modules/c++/xml.lite/source/Element.cpp @@ -129,7 +129,7 @@ std::tuple getElement(TGetElements getElements { return std::make_tuple(elements[0], ""); } - return std::make_tuple(nullptr, str::toString(elements.size())); + return std::make_tuple(nullptr, std::to_string(elements.size())); } template xml::lite::Element& getElement(TGetElements getElements, TMakeContext makeContext) diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/config/CompilerFlagsHelpers.cmake b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/config/CompilerFlagsHelpers.cmake deleted file mode 100644 index e3755ad8c6..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/config/CompilerFlagsHelpers.cmake +++ /dev/null @@ -1,59 +0,0 @@ -# CompilerFlagsHelpers.cmake -# -# set of Convenience functions for portable compiler flags -# -# License: BSD 3 -# -# Copyright (c) 2016, Adrien Devresse -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -set(SUPPORTED_COMPILER_LANGUAGE_LIST "C;CXX") - -foreach(COMPILER_LANGUAGE ${SUPPORTED_COMPILER_LANGUAGE_LIST}) - - # XLC compiler - if(CMAKE_${COMPILER_LANGUAGE}_COMPILER_ID MATCHES "XL") - - # XLC -qinfo=all is awfully verbose on any platforms that use the GNU STL - # Enable by default only the relevant one - set(CMAKE_${COMPILER_LANGUAGE}_WARNING_ALL "-qformat=all -qinfo=lan:trx:ret:zea:cmp:ret") - - ## GCC, CLANG, rest of the world - elseif(CMAKE_${COMPILER_LANGUAGE}_COMPILER_ID MATCHES "Clang" - OR CMAKE_${COMPILER_LANGUAGE}_COMPILER_ID MATCHES "GNU" - OR CMAKE_${COMPILER_LANGUAGE}_COMPILER_ID MATCHES "Intel") - set(CMAKE_${COMPILER_LANGUAGE}_WARNING_ALL " -Wall -Wextra") - string(CONCAT CMAKE_${COMPILER_LANGUAGE}_WARNING_DEBUG - " -Werror -Wshadow -Wnon-virtual-dtor -Wunused -Woverloaded-virtual" - " -Wformat=2 -Wconversion -Wsign-conversion -Wno-error=deprecated-declarations" - ) - if(NOT CMAKE_${COMPILER_LANGUAGE}_COMPILER_IS_ICC) - string(CONCAT CMAKE_${COMPILER_LANGUAGE}_WARNING_DEBUG - ${CMAKE_${COMPILER_LANGUAGE}_WARNING_DEBUG} - " -Wpedantic -Wcast-align -Wdouble-promotion" - ) - endif() - endif() - - if(CMAKE_${COMPILER_LANGUAGE}_COMPILER_ID MATCHES "GNU" - AND (CMAKE_${COMPILER_LANGUAGE}_COMPILER_VERSION VERSION_GREATER "4.7.0")) - set(CMAKE_${COMPILER_LANGUAGE}_WARNING_ALL - "${CMAKE_${COMPILER_LANGUAGE}_WARNING_ALL} -Wno-unused-parameter") - endif() - -endforeach() - - - diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/config/ReleaseDebugAutoFlags.cmake b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/config/ReleaseDebugAutoFlags.cmake deleted file mode 100644 index 2b980698eb..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/config/ReleaseDebugAutoFlags.cmake +++ /dev/null @@ -1,45 +0,0 @@ -# ReleaseDebugAutoFlags.cmake -# -# Release / Debug configuration helper -# -# License: BSD 3 -# -# Copyright (c) 2016, Adrien Devresse -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -## default configuration -if(NOT CMAKE_BUILD_TYPE AND (NOT CMAKE_CONFIGURATION_TYPES)) - set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE) - message(STATUS "Setting build type to '${CMAKE_BUILD_TYPE}' as none was specified.") -endif() - - -# Different configuration types: -# -# Debug : Optimized for debugging, include symbols -# Release : Release mode, no debuginfo -# RelWithDebInfo : Distribution mode, basic optimizations for potable code with debuginfos - -include(CompilerFlagsHelpers) - - -string(APPEND CMAKE_C_FLAGS_RELEASE "${CMAKE_C_WARNING_ALL}") -string(APPEND CMAKE_C_FLAGS_DEBUG "${CMAKE_C_WARNING_ALL}" "${CMAKE_C_WARNING_DEBUG}") -string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_WARNING_ALL}" "${CMAKE_C_WARNING_DEBUG}") - - -string(APPEND CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_WARNING_ALL}") -string(APPEND CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_WARNING_ALL}" "${CMAKE_CXX_WARNING_DEBUG}") -string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_WARNING_ALL}" "${CMAKE_C_WARNING_DEBUG}") diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_eigen.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_eigen.cpp deleted file mode 100644 index 9c21dbc738..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_eigen.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include - -#define H5_USE_EIGEN 1 - -#include -#include - -using namespace HighFive; - -void data_io(void) { - const std::string DATASET_NAME("dset"); - const int nrows = 10; - const int ncols = 3; - - try { - Eigen::MatrixXd mat(nrows, ncols); - - for (int i = 0; i < nrows; ++i) { - for (int j = 0; j < ncols; ++j) { - mat(i, j) = double(j + i * 100); - } - } - - File file("eigen_mat.h5", File::ReadWrite | File::Create | File::Truncate); - - DataSet dset = file.createDataSet(DATASET_NAME, mat); - dset.write(mat); - - Eigen::MatrixXd result; - dset.read(result); - - } catch (const Exception& err) { - std::cerr << err.what() << std::endl; - } -} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Attribute.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Attribute.hpp deleted file mode 100644 index c20b6c8f23..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Attribute.hpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c), 2017, Ali Can Demiralp - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#pragma once - -#include - -#include - -#include "H5DataType.hpp" -#include "H5Object.hpp" -#include "bits/H5Friends.hpp" -#include "bits/H5Path_traits.hpp" - -namespace HighFive { -class DataSpace; - -namespace detail { - -/// \brief Internal hack to create an `Attribute` from an ID. -/// -/// WARNING: Creating an Attribute from an ID has implications w.r.t. the lifetime of the object -/// that got passed via its ID. Using this method careless opens up the suite of issues -/// related to C-style resource management, including the analog of double free, dangling -/// pointers, etc. -/// -/// NOTE: This is not part of the API and only serves to work around a compiler issue in GCC which -/// prevents us from using `friend`s instead. This function should only be used for internal -/// purposes. The problematic construct is: -/// -/// template -/// friend class SomeCRTP; -/// -/// \private -Attribute make_attribute(hid_t hid); -} // namespace detail - -/// -/// \brief Class representing an attribute of a dataset or group -/// -class Attribute: public Object, public PathTraits { - public: - const static ObjectType type = ObjectType::Attribute; - - /// - /// \brief return the name of the current attribute - /// \return the name of the attribute - std::string getName() const; - - size_t getStorageSize() const; - - /// - /// \brief getDataType - /// \return return the datatype associated with this dataset - /// - DataType getDataType() const; - - /// - /// \brief getSpace - /// \return return the dataspace associated with this dataset - /// - DataSpace getSpace() const; - - /// - /// \brief getMemSpace - /// \return same than getSpace for DataSet, compatibility with Selection - /// class - /// - DataSpace getMemSpace() const; - - /// \brief Return the attribute - template - T read() const; - - /// - /// Read the attribute into a buffer - /// An exception is raised if the numbers of dimension of the buffer and of - /// the attribute are different - /// - /// The array type can be a N-pointer or a N-vector ( e.g int** integer two - /// dimensional array ) - template - void read(T& array) const; - - /// \brief Read the attribute into a buffer - template - void read(T* array, const DataType& dtype = {}) const; - - /// - /// Write the integrality N-dimension buffer to this attribute - /// An exception is raised if the numbers of dimension of the buffer and of - /// the attribute are different - /// - /// The array type can be a N-pointer or a N-vector ( e.g int** integer two - /// dimensional array ) - template - void write(const T& buffer); - - /// \brief Write a buffer to this attribute - template - void write_raw(const T* buffer, const DataType& dtype = {}); - - /// \brief Get the list of properties for creation of this attribute - AttributeCreateProps getCreatePropertyList() const { - return details::get_plist(*this, H5Aget_create_plist); - } - - // No empty attributes - Attribute() = delete; - - protected: - using Object::Object; - - private: -#if HIGHFIVE_HAS_FRIEND_DECLARATIONS - template - friend class ::HighFive::AnnotateTraits; -#endif - - friend Attribute detail::make_attribute(hid_t); -}; - -namespace detail { -inline Attribute make_attribute(hid_t hid) { - return Attribute(hid); -} -} // namespace detail - -} // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5DataSpace.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5DataSpace.hpp deleted file mode 100644 index 9c16472050..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5DataSpace.hpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c), 2017, Adrien Devresse - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#pragma once - -#include -#include -#include -#include -#include - -#include "H5Object.hpp" -#include "bits/H5_definitions.hpp" - -namespace HighFive { - -/// -/// \brief Class representing the space (dimensions) of a dataset -/// -class DataSpace: public Object { - public: - const static ObjectType type = ObjectType::DataSpace; - - static const size_t UNLIMITED = SIZE_MAX; - - /// dataspace type - enum DataspaceType { - dataspace_scalar, - dataspace_null - // simple dataspace are handle directly from their dimensions - }; - - /// create a dataspace of N-dimensions - /// Each dimension is configured this way - /// size(dim1) = vec[0] - /// size(dim2) = vec[1] - /// etc... - explicit DataSpace(const std::vector& dims); - - // create a dataspace of N-dimensions - template - explicit DataSpace(const std::array& dims); - - /// Make sure that DataSpace({1,2,3}) works on GCC. This is - /// the shortcut form of the vector initializer, but one some compilers (gcc) - /// this does not resolve correctly without this constructor. - DataSpace(const std::initializer_list& items); - - /// Allow directly listing 1 or more dimensions to initialize, - /// that is, DataSpace(1,2) means DataSpace(std::vector{1,2}). - template - explicit DataSpace(size_t dim1, Args... dims); - - /// Create a dataspace from an iterator pair - /// - /// Explicitly disable DataSpace(int_like, int_like) from trying to use this constructor - template ::value, IT>::type> - DataSpace(const IT begin, const IT end); - - /// \brief Create a resizable N-dimensional dataspace - /// \param dims Initial size of dataspace - /// \param maxdims Maximum size of the dataspace - explicit DataSpace(const std::vector& dims, const std::vector& maxdims); - - /// - /// \brief DataSpace create a scalar dataspace or a null dataset - /// - explicit DataSpace(DataspaceType dtype); - - /// Create a new DataSpace - /// with a different id available for modifications - DataSpace clone() const; - - /// - /// \brief getNumberDimensions - /// \return the number of dimensions in the current dataspace - /// - size_t getNumberDimensions() const; - - /// \brief getDimensions - /// \return return a vector of N-element, each element is the size of the - /// associated dataset dimension - std::vector getDimensions() const; - - /// \brief getElementCount - /// \return the total number of elements in the dataspace - size_t getElementCount() const; - - /// \brief getMaxDimensions - /// \return return a vector of N-element, each element is the size of the - /// associated dataset maximum dimension - std::vector getMaxDimensions() const; - - /// Create a dataspace matching a type accepted by details::inspector - template - static DataSpace From(const T& value); - - template - static DataSpace FromCharArrayStrings(const char (&)[N][Width]); - - protected: - DataSpace() = default; - - friend class Attribute; - friend class File; - friend class DataSet; -}; - -} // namespace HighFive - -// We include bits right away since DataSpace is user-constructible -#include "bits/H5Dataspace_misc.hpp" diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/boost_multi_array_2D.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/boost_multi_array_2D.cpp deleted file mode 100644 index 29ce9f4513..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/boost_multi_array_2D.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c), 2017, Adrien Devresse - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#include - -#undef H5_USE_BOOST -#define H5_USE_BOOST - -#include -#include - -using namespace HighFive; - -const std::string FILE_NAME("boost_multiarray_example.h5"); -const std::string DATASET_NAME("dset"); -const int size_x = 10; -const int size_y = 3; - -// Create a 2D dataset 10x3 of double with boost multi array -// and write it to a file -int main(void) { - try { - boost::multi_array my_array(boost::extents[size_x][size_y]); - - for (int i = 0; i < size_x; ++i) { - for (int j = 0; j < size_y; ++j) { - my_array[i][j] = double(j + i * size_y); - } - } - - // we create a new hdf5 file - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); - - // let's create our dataset of the size of the boost::multi_array - DataSet dataset = file.createDataSet(DATASET_NAME, DataSpace::From(my_array)); - - // we fill it - dataset.write(my_array); - - } catch (Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; // successfully terminated -} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_attribute_string_integer.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_attribute_string_integer.cpp deleted file mode 100644 index 564901bdaa..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_attribute_string_integer.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c), 2017, Adrien Devresse - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#include -#include -#include - -#include -#include -#include -#include - -using namespace HighFive; - -const std::string FILE_NAME("create_attribute.h5"); -const std::string DATASET_NAME("my_dataset"); - -const std::string ATTRIBUTE_NAME_NOTE("note"); -const std::string ATTRIBUTE_NAME_VERSION("version_string"); - -// create a dataset from a vector of string -// read it back and print it -int main(void) { - try { - // Create a new file using the default property lists. - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); - - // Create a dummy dataset of one single integer - DataSet dataset = file.createDataSet(DATASET_NAME, DataSpace(1), create_datatype()); - - // Now let's add a attribute on this dataset - // This attribute will be named "note" - // and have the following content - std::string string_list("very important Dataset !"); - - Attribute a = dataset.createAttribute(ATTRIBUTE_NAME_NOTE, - DataSpace::From(string_list)); - a.write(string_list); - - // We also add a "version" attribute - // that will be an array 1x2 of integer - std::vector version; - version.push_back(1); - version.push_back(0); // version 1.0 - - Attribute v = dataset.createAttribute(ATTRIBUTE_NAME_VERSION, - DataSpace::From(version)); - v.write(version); - - // Ok all attributes are now written - - // let's list the keys of all attributes now - std::vector all_attributes_keys = dataset.listAttributeNames(); - for (const auto& attr: all_attributes_keys) { - std::cout << "attribute: " << attr << std::endl; - } - - } catch (const Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; // successfully terminated -} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_dataset_double.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_dataset_double.cpp deleted file mode 100644 index ba7f4bc034..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_dataset_double.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c), 2017, Adrien Devresse - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#include -#include -#include - -#include -#include -#include - -const std::string FILE_NAME("create_dataset_example.h5"); -const std::string DATASET_NAME("dset"); - -// Create a dataset name "dset" of double 4x6 -// -int main(void) { - using namespace HighFive; - try { - // Create a new file using the default property lists. - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); - - // Define the size of our dataset: 2x6 - std::vector dims{2, 6}; - - // Create the dataset - DataSet dataset = file.createDataSet(DATASET_NAME, DataSpace(dims)); - - double data[2][6] = {{1.1, 2.2, 3.3, 4.4, 5.5, 6.6}, - {11.11, 12.12, 13.13, 14.14, 15.15, 16.16}}; - - // write it - dataset.write(data); - - } catch (Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; // successfully terminated -} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_dataset_half_float.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_dataset_half_float.cpp deleted file mode 100644 index c73d009944..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_dataset_half_float.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c), 2022, Blue Brain Project - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ - -#ifdef H5_USE_HALF_FLOAT - -#include -#include -#include - -#include -#include -#include - -const std::string FILE_NAME("create_dataset_half_float_example.h5"); -const std::string DATASET_NAME("dset"); - -// Create a dataset name "dset", size 4x6, and type float16_t (i.e., 16-bit half-precision -// floating-point format) -// -int main(void) { - using namespace HighFive; - try { - // Create a new file using the default property lists. - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); - - // Define the size of our dataset: 4x6 - std::vector dims{4, 6}; - - // Create the dataset - DataSet dataset = file.createDataSet(DATASET_NAME, DataSpace(dims)); - - std::vector> data; - for (size_t i = 0; i < 4; ++i) { - data.emplace_back(); - for (size_t j = 0; j < 6; ++j) - data[i].emplace_back((i + 1) * (j + 1)); - } - - // write it - dataset.write(data); - - } catch (Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; // successfully terminated -} - -#endif \ No newline at end of file diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_datatype.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_datatype.cpp deleted file mode 100644 index c7aa1bfbe7..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_datatype.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include - -#include -#include - -using namespace HighFive; - -static const std::string FILE_NAME("create_datatype_example.h5"); -static const std::string DATASET_NAME("test_dataset"); - - -// Struct representation of custom type (type v below) -typedef struct { - char a; - short b; - unsigned long long c; -} csl; - -bool operator==(csl x, csl y) { - return x.a == y.a && x.b == y.b && x.c == y.c; -} - -bool operator!=(csl x, csl y) { - return !(x == y); -} - -// Tell HighFive how to create the HDF5 datatype for this base type by -// using the HIGHFIVE_REGISTER_TYPE macro -CompoundType create_compound_csl() { - return {{"u1", create_datatype()}, - {"u2", create_datatype()}, - {"u3", create_datatype()}}; -} -HIGHFIVE_REGISTER_TYPE(csl, create_compound_csl) - -int main(void) { - try { - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); - - // Create a simple compound type with automatic alignment of the - // members. For this the type alignment is trivial. - std::vector t_members( - {{"real", create_datatype()}, {"imag", create_datatype()}}); - CompoundType t(t_members); - t.commit(file, "new_type1"); - - // Create a complex nested datatype with manual alignment - CompoundType u({{"u1", t, 0}, {"u2", t, 9}, {"u3", create_datatype(), 20}}, 26); - u.commit(file, "new_type3"); - - // Create a more complex type with automatic alignment. For this the - // type alignment is more complex. - CompoundType v_aligned{{"u1", create_datatype()}, - {"u2", create_datatype()}, - {"u3", create_datatype()}}; - // introspect the compound type - std::cout << "v_aligned size: " << v_aligned.getSize(); - for (const auto& member: v_aligned.getMembers()) { - std::cout << " field " << member.name << " offset: " << member.offset << std::endl; - } - - v_aligned.commit(file, "new_type2_aligned"); - - // Create a more complex type with a fully packed alignment. The - // equivalent type is created with a standard struct alignment in the - // implementation of HighFive::create_datatype above - CompoundType v_packed({{"u1", create_datatype(), 0}, - {"u2", create_datatype(), 1}, - {"u3", create_datatype(), 3}}, - 11); - v_packed.commit(file, "new_type2_packed"); - - - // Initialise some data - std::vector data; - data.push_back({'f', 1, 4}); - data.push_back({'g', -4, 18}); - - // Write the data into the file in a fully packed form - DataSet dataset = file.createDataSet(DATASET_NAME, DataSpace(2), v_packed); - dataset.write(data); - - file.flush(); - - // Read a subset of the data back - std::vector result; - dataset.select({0}, {2}).read(result); - - for (size_t i = 0; i < data.size(); ++i) { - if (result[i] != data[i]) { - std::cout << "result[" << i << "]:" << std::endl; - std::cout << " " << result[i].a << std::endl; - std::cout << " " << result[i].b << std::endl; - std::cout << " " << result[i].c << std::endl; - std::cout << "data[" << i << "]:" << std::endl; - std::cout << " " << data[i].a << std::endl; - std::cout << " " << data[i].b << std::endl; - std::cout << " " << data[i].c << std::endl; - } - } - - - } catch (const Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - return 1; - } - - return 0; // successfully terminated -} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_extensible_dataset.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_extensible_dataset.cpp deleted file mode 100644 index 96dc2a11b3..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_extensible_dataset.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c), 2017, Adrien Devresse - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#include -#include -#include - -#include -#include -#include - -const std::string FILE_NAME("create_extensible_dataset_example.h5"); -const std::string DATASET_NAME("dset"); - -// Create a dataset name "dset" of double 4x6 -// -int main(void) { - using namespace HighFive; - try { - // Create a new file using the default property lists. - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); - - // Create a dataspace with initial shape and max shape - DataSpace dataspace = DataSpace({4, 5}, {17, DataSpace::UNLIMITED}); - - // Use chunking - DataSetCreateProps props; - props.add(Chunking(std::vector{2, 2})); - - // Create the dataset - DataSet dataset = - file.createDataSet(DATASET_NAME, dataspace, create_datatype(), props); - - // Write into the initial part of the dataset - double t1[3][1] = {{2.0}, {2.0}, {4.0}}; - dataset.select({0, 0}, {3, 1}).write(t1); - - // Resize the dataset to a larger size - dataset.resize({4, 6}); - - // Write into the new part of the dataset - double t2[1][3] = {{4.0, 8.0, 6.0}}; - dataset.select({3, 3}, {1, 3}).write(t2); - - // now we read it back - std::vector> result; - dataset.read(result); - - // we print it out and see: - // 2 0 0 0 0 0 - // 2 0 0 0 0 0 - // 4 0 0 0 0 0 - // 0 0 0 4 8 6 - for (auto row: result) { - for (auto col: row) - std::cout << " " << col; - std::cout << std::endl; - } - - } catch (const Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; // successfully terminated -} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/eigen_matrix.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/eigen_matrix.cpp deleted file mode 100644 index 50c3d40b2b..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/eigen_matrix.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c), 2017, Adrien Devresse - * Copyright (c), 2022, Blue Brain Project - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#include - -#ifdef H5_USE_EIGEN - -#include -#include - -using namespace HighFive; - -const std::string FILE_NAME("eigen_matrix_example.h5"); -const std::string DATASET_NAME("dset"); -const int nrows = 10; -const int ncols = 3; - -// Create a 2D dataset 10x3 of double with eigen matrix -// and write it to a file -int main(void) { - try { - Eigen::MatrixXd matrix(nrows, ncols); - - for (int i = 0; i < nrows; ++i) { - for (int j = 0; j < ncols; ++j) { - matrix(i, j) = double(j + i * 100); - } - } - - // we create a new hdf5 file - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); - - // let's create our dataset of the size of the eigen matrix - file.createDataSet(DATASET_NAME, matrix); - - } catch (Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; // successfully terminated -} - -#endif diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/hl_hdf5_inmemory_files.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/hl_hdf5_inmemory_files.cpp deleted file mode 100644 index 212e64ec58..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/hl_hdf5_inmemory_files.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c), 2022, Blue Brain Project - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#include -#include - - -#include -#include - -using namespace HighFive; - -class InMemoryFile: public HighFive::File { - public: - explicit InMemoryFile(std::vector buffer) - : _buffer(std::move(buffer)) { - _hid = H5LTopen_file_image(_buffer.data(), - sizeof(_buffer[0]) * _buffer.size(), - H5LT_FILE_IMAGE_DONT_RELEASE | H5LT_FILE_IMAGE_DONT_COPY); - } - - private: - std::vector _buffer; -}; - - -// Create a 2D dataset 10x3 of double with eigen matrix -// and write it to a file -int main(void) { - const std::string FILE_NAME("inmemory_file.h5"); - const std::string DATASET_NAME("dset"); - - try { - auto data = std::vector{1.0, 2.0, 3.0}; - - { - // We create an HDF5 file. - File file(FILE_NAME, File::Truncate); - file.createDataSet(DATASET_NAME, data); - } - - // Simulate having an inmemory file by reading a file - // byte-by-byte into RAM. - auto buffer = std::vector(1ul << 20); - auto file = std::fopen(FILE_NAME.c_str(), "r"); - auto nread = std::fread(buffer.data(), sizeof(buffer[0]), buffer.size(), file); - std::cout << "Bytes read: " << nread << "\n"; - - // Create a file from a buffer. - auto h5 = InMemoryFile(std::move(buffer)); - - // Read a dataset as usual. - auto read_back = h5.getDataSet(DATASET_NAME).read>(); - - // Check if the values match. - for (size_t i = 0; i < read_back.size(); ++i) { - if (read_back[i] != data[i]) { - throw std::runtime_error("Values don't match."); - } else { - std::cout << "read_back[" << i << "] = " << read_back[i] << "\n"; - } - } - } catch (Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; // successfully terminated -} \ No newline at end of file diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_dataset_string.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_dataset_string.cpp deleted file mode 100644 index ec1813eb2f..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_dataset_string.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c), 2017, Adrien Devresse - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#include -#include -#include - -#include -#include -#include - -using namespace HighFive; - -const std::string FILE_NAME("create_dataset_string_example.h5"); -const std::string DATASET_NAME("story"); - -// create a dataset from a vector of string -// read it back and print it -int main(void) { - try { - // Create a new file using the default property lists. - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); - - std::vector string_list; - string_list.push_back("Hello World !"); - string_list.push_back( - "This string list is mapped to a dataset of " - "variable length string"); - string_list.push_back("Encoding is done in UTF-8 - 你好 - Здравствуйте!"); - string_list.push_back("May the force be with you"); - string_list.push_back("Enjoy !"); - - // create a dataset ready to contains strings of the size of the vector - // string_list - DataSet dataset = file.createDataSet(DATASET_NAME, - DataSpace::From(string_list)); - - // let's write our vector of string - dataset.write(string_list); - - // now we read it back - std::vector result_string_list; - dataset.read(result_string_list); - - for (size_t i = 0; i < result_string_list.size(); ++i) { - std::cout << ":" << i << " " << result_string_list[i] << "\n"; - } - - } catch (Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; // successfully terminated -} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_fixedlen_string.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_fixedlen_string.cpp deleted file mode 100644 index 1d13f97bc5..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_fixedlen_string.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c), 2020, Blue Brain Project - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#include -#include -#include - -#include -#include -#include - -using namespace HighFive; - -static const std::string FILE_NAME("create_dataset_string_example.h5"); - -// create a dataset from a vector of string -// read it back and print it -int main(void) { - try { - // Create a new file using the default property lists. - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); - const char strings_fixed[][16] = {"abcabcabcabcabc", "123123123123123"}; - - // create a dataset ready to contains strings of the size of the vector - file.createDataSet("ds1", DataSpace(2)).write(strings_fixed); - - // Without specific type info this will create an int8 dataset - file.createDataSet("ds2", strings_fixed); - - // Now test the new interface type - FixedLenStringArray<10> arr{"0000000", "1111111"}; // also accepts std::vector - auto ds = file.createDataSet("ds3", arr); - - // Read back truncating to 4 chars - FixedLenStringArray<4> array_back; - ds.read(array_back); - std::cout << "First item is '" << array_back[0] << "'" << std::endl - << "Second item is '" << array_back[1] << "'" << std::endl; - - - } catch (const Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; // successfully terminated -} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_raw_ptr.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_raw_ptr.cpp deleted file mode 100644 index bef55278b1..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_raw_ptr.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c), 2017, Adrien Devresse - * Copyright (c), 2022, Blue Brain Project - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#include -#include -#include - -#include -#include -#include - -const std::string FILE_NAME("read_write_raw_ptr.h5"); -const std::string DATASET_NAME("array"); - -// This create a "multi-dimensional" array. Meaning a pointer with -// dimensions. The `std::vector` is mearly a convenient way -// of allocating and releasing memory. -// -// Conceptionually this is only a raw pointer with dimensions. The -// data is store in row-major, aka C-style, without stride, offset -// or padding. -std::vector make_array(const std::vector& dims) { - auto n_elements = dims[0] * dims[1]; - std::vector nd_array(n_elements, 0.0); - - for (size_t i = 0; i < dims[0]; ++i) { - for (size_t j = 0; j < dims[1]; ++j) { - nd_array[j + i * dims[1]] = double(j) + 100.0 * double(i); - } - } - - return nd_array; -} - -int main(void) { - using namespace HighFive; - try { - // Create a new file using the default property lists. - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); - - // Let's write to file. - { - std::vector dims{3, 5}; - auto nd_array = make_array(dims); - - // First, create a dataset with the correct dimensions. - auto dataset = file.createDataSet(DATASET_NAME, DataSpace(dims)); - - // Then write, using the raw pointer. - dataset.write_raw(nd_array.data()); - } - - // Let's read from file. - { - auto dataset = file.getDataSet(DATASET_NAME); - - // First read the dimensions. - auto dims = dataset.getDimensions(); - - // Then allocate memory. - auto n_elements = dims[0] * dims[1]; - auto nd_array = std::vector(n_elements); - - // Finally, read into the memory by passing a raw pointer to the library. - dataset.read(nd_array.data()); - } - - - } catch (Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; -} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_single_scalar.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_single_scalar.cpp deleted file mode 100644 index ed0492d680..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_single_scalar.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c), 2017, Adrien Devresse - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#include -#include -#include - -#include -#include -#include - -const std::string FILE_NAME("read_write_scalar.h5"); -const std::string DATASET_NAME("single_scalar"); - -// Create a dataset name "single_scalar" -// which contains only the perfect integer number "42" -// -int main(void) { - using namespace HighFive; - try { - // Create a new file using the default property lists. - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); - - int perfect_number = 42; - - // Create the dataset - DataSet dataset = file.createDataSet(DATASET_NAME, DataSpace::From(perfect_number)); - - // write it - dataset.write(perfect_number); - - // flush everything - file.flush(); - - // let's read it back - int potentially_perfect_number; - - dataset.read(potentially_perfect_number); - - std::cout << "perfect number: " << potentially_perfect_number << std::endl; - - } catch (Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; // successfully terminated -} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/select_by_id_dataset_cpp11.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/select_by_id_dataset_cpp11.cpp deleted file mode 100644 index 7941689197..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/select_by_id_dataset_cpp11.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c), 2017, Adrien Devresse - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#include -#include -#include -#include - -#include -#include -#include - -const std::string FILE_NAME("select_partial_string.h5"); -const std::string DATASET_NAME("message"); - -// Create a dataset name "dset" of double 4x6 -// -int main(void) { - using namespace HighFive; - try { - // Create a new file using the default property lists. - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); - - { - // We have a set of string - std::vector values = { - "Cat", - "Dog", - "Hello", - "Tree", - "World", - "Plane", - ", ", - "你好", - "Tea", - "Moon", - "صباح جميل", - "Spaceship", - }; - - // let's create a dataset - DataSet dataset = file.createDataSet(DATASET_NAME, - DataSpace::From(values)); - - // and write them - dataset.write(values); - } - - { - DataSet dataset = file.getDataSet(DATASET_NAME); - - // now let's read back by cherry pick our interesting string - std::vector result; - // we select only element N° 2 and 5 - dataset.select(ElementSet({2, 4, 6, 7, 6, 10})).read(result); - - // and display it - for (auto i: result) { - std::cout << i << " "; - } - std::cout << "\n"; - } - - } catch (Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; // successfully terminated -} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/select_partial_dataset_cpp11.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/select_partial_dataset_cpp11.cpp deleted file mode 100644 index dce12fde70..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/select_partial_dataset_cpp11.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c), 2017, Adrien Devresse - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#include -#include -#include -#include - -#include -#include -#include - -const std::string FILE_NAME("select_partial_example.h5"); -const std::string DATASET_NAME("dset"); - -// Create a dataset name "dset" of double 4x6 -// -int main(void) { - using namespace HighFive; - try { - // Create a new file using the default property lists. - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); - - // we have some example values in a 2D vector 2x5 - std::vector> values = {{1.0, 2.0, 4.0, 8.0, 16.0}, - {32.0, 64.0, 128.0, 256.0, 512.0}}; - - // let's create a dataset of this size - DataSet dataset = file.createDataSet(DATASET_NAME, DataSpace::From(values)); - // and write them - dataset.write(values); - - // now we read back 2x2 values after an offset of 0x2 - std::vector> result; - dataset.select({0, 2}, {2, 2}).read(result); - - // we print out 4 values - for (auto i: result) { - for (auto j: i) { - std::cout << " " << j; - } - std::cout << "\n"; - } - - } catch (Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; // successfully terminated -} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_dependent_library/include/simpleton.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_dependent_library/include/simpleton.hpp deleted file mode 100644 index 0684294d0f..0000000000 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_dependent_library/include/simpleton.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef H5_TEST_SIMPLETON_HPP -#define H5_TEST_SIMPLETON_HPP - -// Include all headers here to catch any missing `inline` statements, since -// they will be included by two different compilation units. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Boost should always be found in this setup -#include - -void function(const HighFive::Object& obj); -void other_function(const boost::numeric::ublas::matrix& m); - -#endif diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1.zip b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0.zip similarity index 67% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1.zip rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0.zip index 560c224ac01303a77ce45bded4dd116f9b53960b..a1471cfe0050c64169dfa93520bb27e3a87514be 100644 GIT binary patch delta 188747 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 QMHd+vxkHNbijpq>2eCWPD*ylh delta 169147 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⪈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

x(xp8e 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 z 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 V=zCFhbaXWI4Sc*b4X9qa{|7dAfeiov diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.clang-format b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.clang-format similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.clang-format rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.clang-format diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.git-blame-ignore-revs b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.git-blame-ignore-revs similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.git-blame-ignore-revs rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.git-blame-ignore-revs diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/ISSUE_TEMPLATE/bug_report.md b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/ISSUE_TEMPLATE/bug_report.md similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/ISSUE_TEMPLATE/bug_report.md rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/ISSUE_TEMPLATE/bug_report.md diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/ISSUE_TEMPLATE/config.yml b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/ISSUE_TEMPLATE/config.yml similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/ISSUE_TEMPLATE/config.yml rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/ISSUE_TEMPLATE/config.yml diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/ISSUE_TEMPLATE/feature_request.md b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/ISSUE_TEMPLATE/feature_request.md similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/ISSUE_TEMPLATE/feature_request.md rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/ISSUE_TEMPLATE/feature_request.md diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/build.sh b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/build.sh similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/build.sh rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/build.sh diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/pull_request_template.md b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/pull_request_template.md similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/pull_request_template.md rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/pull_request_template.md diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/run_examples.sh b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/run_examples.sh new file mode 100644 index 0000000000..77861f3884 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/run_examples.sh @@ -0,0 +1,20 @@ +#! /usr/bin/env bash + +set -e + +if [[ $# -eq 0 ]] +then + examples_dir="." +elif [[ $# -eq 1 ]] +then + examples_dir="$1" +else + echo "Usage: $0 [EXAMPLES_DIR]" + exit -1 +fi + +for f in "${examples_dir}"/*_bin +do + echo "-- ${f}" + "${f}" +done diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/check_doxygen_awesome_version.yml b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/check_doxygen_awesome_version.yml similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/check_doxygen_awesome_version.yml rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/check_doxygen_awesome_version.yml diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/ci.yml b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/ci.yml similarity index 75% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/ci.yml rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/ci.yml index 4ae0a0b4bc..e7f5fca10b 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/ci.yml +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/ci.yml @@ -54,6 +54,9 @@ jobs: os: ubuntu-20.04 pkgs: 'libboost-all-dev' flags: '-DCMAKE_CXX_STANDARD=17' + - config: + os: ubuntu-22.04 + flags: '-DHIGHFIVE_USE_BOOST=Off -DCMAKE_CXX_STANDARD=20' steps: - uses: actions/checkout@v3 @@ -77,7 +80,7 @@ jobs: - name: Test working-directory: ${{github.workspace}}/build - run: ctest --output-on-failure -C $BUILD_TYPE + run: ctest -j2 --output-on-failure -C $BUILD_TYPE # Job testing several versions of hdf5 @@ -86,7 +89,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - hdf5_version : [ hdf5-1_8_23, hdf5-1_10_9, hdf5-1_12_2, hdf5-1_14_0 ] + hdf5_version : [ hdf5-1_8_23, hdf5-1_10_11, hdf5-1_12_2, hdf5-1_14_3 ] steps: - uses: actions/checkout@v3 @@ -102,18 +105,25 @@ jobs: wget https://github.com/HDFGroup/hdf5/archive/refs/tags/${{ matrix.hdf5_version }}.tar.gz --output-document hdf5.tar.gz tar xf hdf5.tar.gz mkdir -p hdf5-${{ matrix.hdf5_version }}/BUILD && cd hdf5-${{ matrix.hdf5_version }}/BUILD - cmake .. -GNinja -DCMAKE_INSTALL_PREFIX=$HOME/${{ matrix.hdf5_version }} -DHDF5_ENABLE_Z_LIB_SUPPORT=ON -DUSE_LIBAEC=ON + cmake .. -DCMAKE_BUILD_TYPE=Release -GNinja -DCMAKE_INSTALL_PREFIX=$HOME/${{ matrix.hdf5_version }} -DHDF5_ENABLE_Z_LIB_SUPPORT=ON -DUSE_LIBAEC=ON -DHDF5_BUILD_EXAMPLES=OFF -DBUILD_STATIC_LIBS=OFF -DBUILD_TESTING=OFF ninja && ninja install - name: Build run: | - mkdir BUILD && cd BUILD - cmake -GNinja -DHDF5_ROOT=$HOME/${{ matrix.hdf5_version }} .. - ninja + CMAKE_OPTIONS=( + -GNinja + -DHDF5_ROOT=$HOME/${{ matrix.hdf5_version }} + ) + source $GITHUB_WORKSPACE/.github/build.sh - name: Test - run: | - cd BUILD && ctest --output-on-failure + working-directory: ${{github.workspace}}/build + run: ctest -j2 --output-on-failure -C $BUILD_TYPE + + + - name: Examples + working-directory: ${{github.workspace}}/build/src/examples + run: $GITHUB_WORKSPACE/.github/run_examples.sh # Job testing several compilers on a stable Linux # ==================================================== @@ -146,7 +156,11 @@ jobs: - name: Test working-directory: ${{github.workspace}}/build - run: ctest --output-on-failure -C $BUILD_TYPE + run: ctest -j2 --output-on-failure -C $BUILD_TYPE + + - name: Examples + working-directory: ${{github.workspace}}/build/src/examples + run: $GITHUB_WORKSPACE/.github/run_examples.sh # Job running unit-test with sanitizers # ===================================== @@ -185,22 +199,43 @@ jobs: - name: Test working-directory: ${{github.workspace}}/build - run: ctest --output-on-failure -C $BUILD_TYPE + run: ctest -j2 --output-on-failure -C $BUILD_TYPE + + - name: Examples + working-directory: ${{github.workspace}}/build/src/examples + run: $GITHUB_WORKSPACE/.github/run_examples.sh # Job to check using HighFive from other CMake projects # ===================================================== CMake_Project: runs-on: ubuntu-20.04 + strategy: + matrix: + parallelism: [ serial, parallel ] steps: - uses: actions/checkout@v3 with: submodules: true - - name: "Install libraries" + - name: "Update Ubuntu" run: | sudo apt-get -qq update - sudo apt-get -qq install libboost-all-dev libhdf5-dev libsz2 ninja-build + + - name: "Install common libraries" + run: | + sudo apt-get -qq install libboost-all-dev libsz2 ninja-build + + - name: "Install serial HDF5" + if: matrix.parallelism == 'serial' + run: | + sudo apt-get -qq install libhdf5-dev + + + - name: "Install parallel HDF5" + if: matrix.parallelism == 'parallel' + run: | + sudo apt-get -qq install libhdf5-openmpi-dev - name: "CMake Project Integration" run: bash tests/test_project_integration.sh @@ -230,7 +265,11 @@ jobs: # Job testing in OSX # ================== OSX: - runs-on: macOS-12 + runs-on: ${{matrix.os}} + strategy: + matrix: + os: [ "macOS-12" ] + cxxstd: ["14", "17", "20"] steps: - uses: actions/checkout@v3 @@ -250,29 +289,44 @@ jobs: -DHIGHFIVE_BUILD_DOCS:BOOL=FALSE -DHIGHFIVE_TEST_SINGLE_INCLUDES=ON -DCMAKE_CXX_FLAGS="-coverage -O0" + -DCMAKE_CXX_STANDARD=${{matrix.cxxstd}} ) source $GITHUB_WORKSPACE/.github/build.sh - name: Test working-directory: ${{github.workspace}}/build - run: ctest --output-on-failure -C $BUILD_TYPE + run: ctest -j2 --output-on-failure -C $BUILD_TYPE + + - name: Examples + working-directory: ${{github.workspace}}/build/src/examples + run: $GITHUB_WORKSPACE/.github/run_examples.sh # Job testing in Windows # ====================== - Windows-2022: + Windows: runs-on: ${{matrix.os}} strategy: matrix: os: [ "windows-2022"] vs-toolset: [ "v141", "v143" ] - cxxstd: ["14", "17"] + cxxstd: ["14", "17", "20"] + + include: + - os: "windows-2019" + vs-toolset: "v142" + cxxstd: "14" + + - os: "windows-2019" + vs-toolset: "v142" + cxxstd: "17" + steps: - uses: actions/checkout@v3 with: submodules: true - - uses: mamba-org/provision-with-micromamba@main + - uses: mamba-org/setup-micromamba@v1 with: environment-file: doc/environment.yaml environment-name: win-test @@ -294,41 +348,4 @@ jobs: - name: Test working-directory: ${{github.workspace}}/build shell: bash -l {0} - run: ctest --output-on-failure -C $BUILD_TYPE - - Windows-2019: - runs-on: ${{matrix.os}} - strategy: - matrix: - os: [ "windows-2019"] - vs-toolset: [ "v142" ] - cxxstd: ["14", "17"] - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - - uses: mamba-org/provision-with-micromamba@main - with: - environment-file: doc/environment.yaml - environment-name: win-test - - - name: Build - shell: bash -l {0} - run: | - CMAKE_OPTIONS=( - -T ${{matrix.vs-toolset}} - -DCMAKE_CXX_STANDARD=${{matrix.cxxstd}} - -DHIGHFIVE_UNIT_TESTS=ON - -DHIGHFIVE_USE_BOOST:BOOL=ON - -DHIGHFIVE_USE_EIGEN:BOOL=ON - -DHIGHFIVE_USE_XTENSOR:BOOL=ON - -DHIGHFIVE_TEST_SINGLE_INCLUDES=ON - ) - source $GITHUB_WORKSPACE/.github/build.sh - - - name: Test - working-directory: ${{github.workspace}}/build - shell: bash -l {0} - run: ctest --output-on-failure -C $BUILD_TYPE - + run: ctest -j2 --output-on-failure -C $BUILD_TYPE diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/clang_format.yml b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/clang_format.yml similarity index 90% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/clang_format.yml rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/clang_format.yml index 99cb6e1b3b..56f2fd8d53 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/clang_format.yml +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/clang_format.yml @@ -32,5 +32,8 @@ jobs: then echo "Some files are not well formatted:" echo $modified_files + echo "" + echo "The diff is:" + git diff exit 1 fi diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/coverage.yml b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/coverage.yml similarity index 73% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/coverage.yml rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/coverage.yml index 020bc5f7ea..b3f4a212bc 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/coverage.yml +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/coverage.yml @@ -41,7 +41,7 @@ jobs: fetch-depth: 2 submodules: true - - name: Build and test for code coverage + - name: Build for code coverage run: | CMAKE_OPTIONS=( -GNinja @@ -54,14 +54,15 @@ jobs: -DCMAKE_CXX_FLAGS="-coverage -O0" ) source $GITHUB_WORKSPACE/.github/build.sh - cd $HIGHFIVE_BUILD - (cd $GITHUB_WORKSPACE; lcov --capture --initial --directory . --no-external --output-file build/coverage-base.info) - cmake --build $HIGHFIVE_BUILD --target test - (cd $GITHUB_WORKSPACE; lcov --capture --directory . --no-external --output-file build/coverage-run.info) - lcov --add-tracefile coverage-base.info --add-tracefile coverage-run.info --output-file coverage-combined.info + - name: Test for code coverage + run: | + lcov --capture --initial --directory . --no-external --output-file build/coverage-base.info + (cd build; cmake --build . --target test) + lcov --capture --directory . --no-external --output-file build/coverage-run.info + (cd build; lcov --add-tracefile coverage-base.info --add-tracefile coverage-run.info --output-file coverage-combined.info) - uses: codecov/codecov-action@v3 with: files: ./build/coverage-combined.info - fail_ci_if_error: true + fail_ci_if_error: false verbose: true token: ${{ secrets.CODECOV_TOKEN }} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/gh-pages.yml b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/gh-pages.yml similarity index 94% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/gh-pages.yml rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/gh-pages.yml index 117836fd5c..2032f91abc 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/gh-pages.yml +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/gh-pages.yml @@ -24,7 +24,7 @@ jobs: with: submodules: 'recursive' - - uses: mamba-org/provision-with-micromamba@main + - uses: mamba-org/setup-micromamba@v1 with: environment-file: doc/environment.yaml environment-name: doc-build diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/integration_trigger.yml b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/integration_trigger.yml similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.github/workflows/integration_trigger.yml rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/integration_trigger.yml diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/version_file.yml b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/version_file.yml new file mode 100644 index 0000000000..816137e955 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.github/workflows/version_file.yml @@ -0,0 +1,36 @@ +name: HighFive Check Version File + +on: + push: + branches: + - ci_test + - release/** + pull_request: + branches: + - master + - release/** + +jobs: + CheckVersion: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - name: "Install libraries" + run: | + sudo apt-get -qq update + sudo apt-get -qq install libhdf5-dev ninja-build + + - name: Build + run: | + # Will trigger `configure_file` for H5Version.hpp. + cmake -DHIGHFIVE_USE_BOOST=Off -B build . + + - name: Test + run: | + # Check that the file hasn't changed, i.e. was updated + # after changing the version number. + ! git status | grep include/highfive/H5Version.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.gitignore b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.gitignore similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.gitignore rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.gitignore diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.gitmodules b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.gitmodules similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.gitmodules rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.gitmodules diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.travis.yml b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.travis.yml similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/.travis.yml rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/.travis.yml diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/AUTHORS.txt b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/AUTHORS.txt similarity index 92% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/AUTHORS.txt rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/AUTHORS.txt index 11d03032fb..78f573a9a6 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/AUTHORS.txt +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/AUTHORS.txt @@ -2,6 +2,7 @@ Adrien Devresse Alexandru Săvulescu Ali Can Demiralp Angelos Plastropoulos +@antonysigma Chris Byrohl Chris De Grendele @contre @@ -12,6 +13,8 @@ Fernando L. Pereira @guoxy Haoran Ni Henry Schreiner +@hn-sl +Hunter Belanger @JaWSnl Jia Li John W. Peterson @@ -36,6 +39,7 @@ Pablo Toharia Philip Deegan Philipp Gloor Pramod Kumbhar +@Quark-X10 Richard Shaw Rick Nitsche Rob Latham @@ -48,6 +52,7 @@ Tino Wagner Tobias Klauser Tom de Geus Tom Vander Aa +Torsten Reuschel Tristan Carel Wolf Vollprecht Y. Yang diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CHANGELOG.md b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CHANGELOG.md similarity index 86% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CHANGELOG.md rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CHANGELOG.md index f6d03a438c..9a8cd86139 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CHANGELOG.md +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CHANGELOG.md @@ -1,3 +1,34 @@ +# Changes +## Version 2.8.0 - 2023-11-02 +### Important Change + - `Eigen::Matrix` is (by default) stored with column-major index ordering. Under + certain conditions `Eigen::Matrix` was written and read as row-major. + Due to code duplication H5Easy isn't affected by this bug. Starting + `2.8.0` HighFive will now throw an exception whenever prior versions would + have read with incorrect assumptions about the index ordering. (#731) + +### New Features + - Improve reading and writing `std::string` as fixed and variable length HDF5 strings (#744). + - Implement creation of hard links (#765). Thanks to @Quark-X10. + - Get the size of file and amound of tracked unused space (#764). Thanks to @Quark-X10. + - `class DataType` has a new ctor to open a commited `DataType` (#796). Thanks to @Quark-X10. + - Allow user-specified `mem_space` for hyperslabs. (#740) + - New properties: `AttributePhaseChange`. (#785) + - New options to link against HDF5 statically (#823). Thanks @HunterBelanger. + - Add support for `std::complex` valid with C++23 (#828). Thanks @unbtorsten. + - Add a top-level header to include all compononents (#818). + +### Improvements + - Add concept checks to `Property` if C++20 for better errors (#811). Thanks @antonysigma. + - Add parallel HDF5 test in CI (#760). + - Simplify github workflow (#761). + - Move inspectors in their own file to be able to better implements strings (#759). + +### Bug Fix + - Fix vector constructor ambiguity in H5DataType.hpp (#775). Thanks to @hn-sl. + - `getElementCount()` fixed. (#787) + - Remove leak when calling dtor of `CompoundType`. (#798) + ## Version 2.7.1 - 2023-04-04 ### Bug Fix - Revert removing `#include "H5FileDriver.hpp"` from `H5File.hpp` (#711). diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/HighFiveConfig.cmake.in b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/HighFiveConfig.cmake.in similarity index 93% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/HighFiveConfig.cmake.in rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/HighFiveConfig.cmake.in index 370e828635..464a645d03 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/HighFiveConfig.cmake.in +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/HighFiveConfig.cmake.in @@ -15,6 +15,8 @@ if(TARGET HighFive) return() endif() +@PACKAGE_INIT@ + # Get HighFive targets include("${CMAKE_CURRENT_LIST_DIR}/HighFiveTargets.cmake") @@ -61,8 +63,12 @@ if(HIGHFIVE_USE_XTENSOR AND NOT CMAKE_VERSION VERSION_LESS 3.8) set_property(TARGET HighFive APPEND PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_14) endif() -message(STATUS "HIGHFIVE @PROJECT_VERSION@: (Re)Detecting Highfive dependencies (HIGHFIVE_USE_INSTALL_DEPS=NO)") +if(NOT HighFive_FIND_QUIETLY) + message(STATUS "HIGHFIVE @PROJECT_VERSION@: (Re)Detecting Highfive dependencies (HIGHFIVE_USE_INSTALL_DEPS=NO)") +endif() include("${CMAKE_CURRENT_LIST_DIR}/HighFiveTargetDeps.cmake") foreach(dependency HighFive_libheaders libdeps) copy_interface_properties(HighFive ${dependency}) endforeach() + +check_required_components(HighFive) diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/HighFiveTargetDeps.cmake b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/HighFiveTargetDeps.cmake similarity index 96% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/HighFiveTargetDeps.cmake rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/HighFiveTargetDeps.cmake index 8fddf39e77..919b53544e 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/HighFiveTargetDeps.cmake +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/HighFiveTargetDeps.cmake @@ -26,6 +26,7 @@ if(NOT TARGET libdeps) # HDF5 if(NOT DEFINED HDF5_C_LIBRARIES) set(HDF5_PREFER_PARALLEL ${HIGHFIVE_PARALLEL_HDF5}) + set(HDF5_USE_STATIC_LIBRARIES ${HIGHFIVE_STATIC_HDF5}) find_package(HDF5 REQUIRED) endif() @@ -36,6 +37,8 @@ if(NOT TARGET libdeps) target_include_directories(libdeps SYSTEM INTERFACE ${HDF5_INCLUDE_DIRS}) target_link_libraries(libdeps INTERFACE ${HDF5_LIBRARIES}) target_compile_definitions(libdeps INTERFACE ${HDF5_DEFINITIONS}) + target_compile_definitions(libdeps INTERFACE HIGHFIVE_HAS_CONCEPTS=$) + # Boost if(HIGHFIVE_USE_BOOST) diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/HighFiveTargetExport.cmake b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/HighFiveTargetExport.cmake similarity index 96% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/HighFiveTargetExport.cmake rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/HighFiveTargetExport.cmake index 011f7f4832..9906f39513 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/HighFiveTargetExport.cmake +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/HighFiveTargetExport.cmake @@ -4,7 +4,6 @@ add_library(libheaders INTERFACE) target_include_directories(libheaders INTERFACE "$" - "$" "$") # Combined HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/HighFiveWarnings.cmake b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/HighFiveWarnings.cmake new file mode 100644 index 0000000000..8e8ec22019 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/HighFiveWarnings.cmake @@ -0,0 +1,36 @@ +if(TARGET HighFiveWarnings) + # Allow multiple `include(HighFiveWarnings)`, which would + # attempt to redefine `HighFiveWarnings` and fail without + # this check. + return() +endif() + +add_library(HighFiveWarnings INTERFACE) + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" + OR CMAKE_CXX_COMPILER_ID MATCHES "GNU" + OR CMAKE_CXX_COMPILER_ID MATCHES "Intel") + + target_compile_options(HighFiveWarnings + INTERFACE + -Wall + -Wextra + -Wshadow + -Wnon-virtual-dtor + -Wunused + -Woverloaded-virtual + -Wformat=2 + -Wconversion + -Wsign-conversion + -Wno-error=deprecated-declarations + ) + + if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Intel") + target_compile_options(HighFiveWarnings + INTERFACE + -Wpedantic + -Wcast-align + -Wdouble-promotion + ) + endif() +endif() diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/config/TestHelpers.cmake b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/config/TestHelpers.cmake similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMake/config/TestHelpers.cmake rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMake/config/TestHelpers.cmake diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMakeLists.txt b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMakeLists.txt similarity index 90% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMakeLists.txt rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMakeLists.txt index 7c8bbd8037..af274d9e25 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/CMakeLists.txt +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/CMakeLists.txt @@ -5,15 +5,14 @@ else() cmake_policy(VERSION 3.13) endif() -project(HighFive VERSION 2.7.1) +project(HighFive VERSION 2.8.0) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/include/highfive/H5Version.hpp.in - ${CMAKE_CURRENT_BINARY_DIR}/include/highfive/H5Version.hpp) + ${CMAKE_CURRENT_SOURCE_DIR}/include/highfive/H5Version.hpp) # INCLUDES list(APPEND CMAKE_MODULE_PATH - ${PROJECT_SOURCE_DIR}/CMake - ${PROJECT_SOURCE_DIR}/CMake/portability - ${PROJECT_SOURCE_DIR}/CMake/config) + ${CMAKE_CURRENT_SOURCE_DIR}/CMake + ${CMAKE_CURRENT_SOURCE_DIR}/CMake/config) # OPTIONS # Compatibility within Highfive 2.x series @@ -33,9 +32,11 @@ option(HIGHFIVE_USE_OPENCV "Enable OpenCV testing" ${USE_OPENCV}) option(HIGHFIVE_USE_XTENSOR "Enable xtensor testing" ${USE_XTENSOR}) option(HIGHFIVE_EXAMPLES "Compile examples" ON) option(HIGHFIVE_PARALLEL_HDF5 "Enable Parallel HDF5 support" OFF) +option(HIGHFIVE_STATIC_HDF5 "Staticly link to HDF5 library" OFF) option(HIGHFIVE_BUILD_DOCS "Enable documentation building" ON) option(HIGHFIVE_VERBOSE "Set logging level to verbose." OFF) option(HIGHFIVE_GLIBCXX_ASSERTIONS "Enable bounds check for STL." OFF) +option(HIGHFIVE_HAS_CONCEPTS "Print readable compiler errors w/ C++20 concepts" ON) # Controls if HighFive classes are friends of each other. # @@ -103,17 +104,12 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION "include" PATTERN "*.in" EXCLUDE) -# Installation of configured headers -install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/ - DESTINATION "include") - - # Preparing local building (tests, examples) # ------------------------------------------ # Disable test if Boost was expressly disabled, or if HighFive is a sub-project if (NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) - if(HIGHFIVE_UNIT_TESTS) + if(HIGHFIVE_UNIT_TESTS AND NOT HighFive_FIND_QUIETLY) message(WARNING "Unit tests have been DISABLED.") endif() set(HIGHFIVE_UNIT_TESTS FALSE) @@ -134,15 +130,6 @@ if(HIGHFIVE_UNIT_TESTS) endif() endif() - -if(CMAKE_CXX_COMPILER_ID MATCHES "Intel") - # ICC gets mad if we shorten "int"s - add_definitions("-wd1682") -endif() - -# Set compile time flags _after_ including required dependencies -include(ReleaseDebugAutoFlags) - if(HIGHFIVE_EXAMPLES) add_subdirectory(src/examples) endif() diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/LICENSE b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/LICENSE similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/LICENSE rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/LICENSE diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/README.md b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/README.md similarity index 60% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/README.md rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/README.md index 0ebb8c0996..3ea0680157 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/README.md +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/README.md @@ -1,6 +1,5 @@ # HighFive - HDF5 header-only C++ Library -[![Build Status](https://travis-ci.org/BlueBrain/HighFive.svg?branch=master)](https://travis-ci.org/BlueBrain/HighFive) [![Doxygen -> gh-pages](https://github.com/BlueBrain/HighFive/workflows/gh-pages/badge.svg)](https://BlueBrain.github.io/HighFive) [![codecov](https://codecov.io/gh/BlueBrain/HighFive/branch/master/graph/badge.svg?token=UBKxHEn7RS)](https://codecov.io/gh/BlueBrain/HighFive) [![HighFive_Integration_tests](https://github.com/BlueBrain/HighFive-testing/actions/workflows/integration.yml/badge.svg)](https://github.com/BlueBrain/HighFive-testing/actions/workflows/integration.yml) @@ -11,12 +10,11 @@ Documentation: https://bluebrain.github.io/HighFive/ HighFive is a modern header-only C++11 friendly interface for libhdf5. -HighFive supports STL vector/string, Boost::UBLAS, Boost::Multi-array, Eigen and Xtensor. It handles C++ from/to HDF5 with automatic type mapping. -HighFive does not require additional libraries (see dependencies) and supports both HDF5 thread safety and Parallel HDF5 (contrary to the official hdf5 cpp) +HighFive supports STL vector/string, Boost::UBLAS, Boost::Multi-array and Xtensor. It handles C++ from/to HDF5 with automatic type mapping. +HighFive does not require additional libraries (see dependencies). It integrates nicely with other CMake projects by defining (and exporting) a HighFive target. - ### Design - Simple C++-ish minimalist interface - No other dependency than libhdf5 @@ -43,13 +41,19 @@ It integrates nicely with other CMake projects by defining (and exporting) a Hig - xtensor (optional, opt-in with -D*HIGHFIVE_USE_XTENSOR*=ON) - half (optional, opt-in with -D*HIGHFIVE_USE_HALF_FLOAT*=ON) +### Known flaws +- HighFive is not thread-safe. At best it has the same limitations as the HDF5 library. However, HighFive objects modify their members without protecting these writes. Users have reported that HighFive is not thread-safe even when using the threadsafe HDF5 library, e.g., https://github.com/BlueBrain/HighFive/discussions/675. +- Eigen support in core HighFive is broken. See https://github.com/BlueBrain/HighFive/issues/532. H5Easy is not + affected. +- The support of fixed length strings isn't ideal. + ## Examples #### Write a std::vector to 1D HDF5 dataset and read it back ```c++ -#include +#include using namespace HighFive; @@ -105,12 +109,6 @@ See [create_attribute_string_integer.cpp](https://github.com/BlueBrain/HighFive/ See [src/examples/](https://github.com/BlueBrain/HighFive/blob/master/src/examples/) subdirectory for more info. -### Compiling with HighFive - -```bash -c++ -o program -I/path/to/highfive/include source.cpp -lhdf5 -``` - ### H5Easy For several 'standard' use cases the [highfive/H5Easy.hpp](include/highfive/H5Easy.hpp) interface is available. It allows: @@ -152,60 +150,98 @@ whereby the `int` type of this example can be replaced by any of the above types ## CMake integration +There's two common paths of integrating HighFive into a CMake based project. +The first is to "vendor" HighFive, the second is to install HighFive as a +normal C++ library. Due to how HighFive CMake code works, sometimes following +the third Bailout Approach is needed. -HighFive can easily be used by other C++ CMake projects. +### Vendoring HighFive -You may use HighFive from a folder in your project (typically a git submodule). +In this approach the HighFive sources are included in a subdirectory of the +project (typically as a git submodule), for example in `third_party/HighFive`. + +The projects `CMakeLists.txt` add the following lines ```cmake -cmake_minimum_required(VERSION 3.1 FATAL_ERROR) -project(foo) -set(CMAKE_CXX_STANDARD 11) +add_executable(foo foo.cpp) + +# You might want to turn off Boost support: +if(NOT DEFINED HIGHFIVE_USE_BOOST) + set(HIGHFIVE_USE_BOOST Off) +endif() -add_subdirectory(highfive_folder) -add_executable(bar bar.cpp) -target_link_libraries(bar HighFive) +# Include the subdirectory and use the target HighFive. +add_subdirectory(third_party/HighFive) +target_link_libraries(foo HighFive) ``` -Alternativelly you can install HighFive once and use it in several projects via `find_package()`. +**Note:** `add_subdirectory(third_party/HighFive)` will search and "link" HDF5 +and optional dependencies such as Boost. -A HighFive target will bring the compilation settings to find HighFive headers and all chosen dependencies. +### Regular Installation of HighFive +Alternatively you can install HighFive once and use it in several projects via +`find_package()`. First one should clone the sources: +```bash +git clone --recursive https://github.com/BlueBrain/HighFive.git HighFive-src +``` +By default CMake will install systemwide, which is likely not appropriate. The +instruction below allow users to select a custom path where HighFive will be +installed, e.g. `HIGHFIVE_INSTALL_PREFIX=${HOME}/third_party/HighFive` or some +other location. The CMake invocations would be +```bash +cmake -DHIGHFIVE_EXAMPLES=Off \ + -DHIGHFIVE_USE_BOOST=Off \ + -DHIGHFIVE_UNIT_TESTS=Off \ + -DCMAKE_INSTALL_PREFIX=${HIGHFIVE_INSTALL_PREFIX} \ + -B HighFive-src/build \ + HighFive-src + +cmake --build HighFive-src/build +cmake --install HighFive-src/build +``` +This will install (i.e. copy) the headers to +`${HIGHFIVE_INSTALL_PREFIX}/include` and some CMake files into an appropriate +subfolder of `${HIGHFIVE_INSTALL_PREFIX}`. + +The projects `CMakeLists.txt` should add the following: ```cmake # ... +add_executable(foo foo.cpp) + find_package(HighFive REQUIRED) -add_executable(bar bar.cpp) -target_link_libraries(bar HighFive) +target_link_libraries(foo HighFive) ``` -**Note:** Like with other libraries you may need to provide CMake the location to find highfive: `CMAKE_PREFIX_PATH=` -**Note:** `find_package(HighFive)` will search dependencies as well (e.g. Boost if requested). In order to use the same dependencies found at HighFive install time (e.g. for system deployments) you may set `HIGHFIVE_USE_INSTALL_DEPS=YES` +**Note:** If HighFive hasn't been installed in a default location, CMake needs +to be told where to find it which can be done by adding +`-DCMAKE_PREFIX_PATH=${HIGHFIVE_INSTALL_PREFIX}` to the CMake command for +building the project using HighFive. The variable `CMAKE_PREFIX_PATH` is a +semi-colon `;` separated list of directories. -### Installing -When installing via CMake, besides the headers, a HighFiveConfig.cmake is generated which provides the HighFive target, as seen before. Note: You may need to set `CMAKE_INSTALL_PREFIX`: -```bash -mkdir build && cd build -# Look up HighFive CMake options, consider inspecting with `ccmake` -cmake .. -DHIGHFIVE_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX="" -make install -``` +**Note:** `find_package(HighFive)` will search and "link" HDF5 and optional +dependencies such as Boost. -### Test Compilation -As a header-only library, HighFive doesn't require compilation. You may however build tests and examples. +### The Bailout Approach +Since both `add_subdirectory` and `find_package` will trigger finding HDF5 and +other optional dependencies of HighFive as well as the `target_link_libraries` +code for "linking" with the dependencies, things can go wrong. + +Fortunately, HighFive is a header only library and all that's needed is the +headers. Preferably, the version obtained by installing HighFive, since those +include `H5Version.hpp`. Let's assume they've been copied to +`third_party/HighFive`. Then one could create a target: ```bash -mkdir build && cd build -cmake ../ -make # build tests and examples -make test # build and run unit tests -``` +add_library(HighFive INTERFACE) +target_include_directory(HighFive INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/HighFive/include) -**Note:** Unit tests require Boost. In case it's unavailable you may use `-DHIGHFIVE_USE_BOOST=OFF`. -HighFive with disable support for Boost types as well as unit tests (though most examples will build). -### Code formatting -If you want to propose pull requests to this project, do not forget to format code with -clang-format version 12. -The .clang-format is at the root of the git repository. +add_executable(foo foo.cpp) +target_link_libraries(foo HighFive) +``` + +One known case where this is required is when vendoring the optional +dependencies of HighFive. # Questions? @@ -213,6 +249,8 @@ Do you have questions on how to use HighFive? Would you like to share an interes discuss HighFive features? Head over to the [Discussions](https://github.com/BlueBrain/HighFive/discussions) forum and join the community. +For bugs and issues please use [Issues](https://github.com/BlueBrain/HighFive/issues). + # Funding & Acknowledgment The development of this software was supported by funding to the Blue Brain Project, a research center of the École polytechnique fédérale de Lausanne (EPFL), from the Swiss government's ETH Board of the Swiss Federal Institutes of Technology. diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/codecov.yml b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/codecov.yml new file mode 100644 index 0000000000..bfdc9877d9 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/codecov.yml @@ -0,0 +1,8 @@ +coverage: + status: + project: + default: + informational: true + patch: + default: + informational: true diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/CMakeLists.txt b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/CMakeLists.txt similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/CMakeLists.txt rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/CMakeLists.txt diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/Doxyfile b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/Doxyfile similarity index 99% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/Doxyfile rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/Doxyfile index db9fb98a4c..6ebc393ec2 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/Doxyfile +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/Doxyfile @@ -865,6 +865,8 @@ WARN_LOGFILE = # Note: If this tag is empty the current directory is searched. INPUT = @CMAKE_CURRENT_SOURCE_DIR@/../include \ + @CMAKE_CURRENT_SOURCE_DIR@/installation.md \ + @CMAKE_CURRENT_SOURCE_DIR@/developer_guide.md \ @CMAKE_CURRENT_SOURCE_DIR@/../CHANGELOG.md \ @CMAKE_CURRENT_SOURCE_DIR@/../README.md diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/DoxygenLayout.xml b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/DoxygenLayout.xml similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/DoxygenLayout.xml rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/DoxygenLayout.xml index 76c566b2d9..8fee2ee56a 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/DoxygenLayout.xml +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/DoxygenLayout.xml @@ -181,6 +181,7 @@ + @@ -205,7 +206,6 @@ - diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/developer_guide.md b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/developer_guide.md new file mode 100644 index 0000000000..3017289b51 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/developer_guide.md @@ -0,0 +1,93 @@ +# Developer Guide +First clone the repository and remember the `--recursive`: +```bash +git clone --recursive git@github.com:BlueBrain/HighFive.git +``` +The instructions to recover if you forgot are: +```bash +git submodule update --init --recursive +``` + +One remark on submodules: each HighFive commit expects that the submodules are +at a particular commit. The catch is that performing `git checkout` will not +update the submodules automatically. Hence, sometimes a `git submodule update +--recursive` might be needed to checkout the expected version of the +submodules. + +## Compiling and Running the Tests +The instructions for compiling with examples and unit-tests are: + +```bash +cmake -B build -DCMAKE_BUILD_TYPE={Debug,Release} . +cmake --build build --parallel +ctest --test-dir build +``` + +You might want to turn off Boost `-DHIGHFIVE_USE_BOOST=Off` or turn on other +optional dependencies. + +## Contributing +There's numerous HDF5 features that haven't been wrapped yet. HighFive is a +collaborative effort to slowly cover ever larger parts of the HDF5 library. +The process of contributing is to fork the repository and then create a PR. +Please ensure that any new API is appropriately documented and covered with +tests. + +### Code formatting +The project is formatted using clang-format version 12.0.1 and CI will complain +if a commit isn't formatted accordingly. The `.clang-format` is at the root of +the git repository. Conveniently, `clang-format` is available via `pip`: + +```bash +python -m venv venv +source venv/bin/activate + +pip install clang-format==12.0.1 +``` + +The changed lines can be formatted with `git-clang-format`, e.g. to format all lines changed compared to master: + +```bash +git-clang-format master +``` +(add `-f` to allow formatting unstaged changes if you trust it to not destroy +your changes.) + +## Releasing HighFive +Before releasing a new version perform the following: + +* Update `CHANGELOG.md` and `AUTHORS.txt` as required. +* Update `CMakeLists.txt` and `include/highfive/H5Version.hpp`. +* Follow semantic versioning when deciding the next version number. +* Check that + [HighFive-testing](https://github.com/BlueBrain/HighFive-testing/actions) ran + recently. + +At this point there should be a commit on master which will be the release +candidate. Don't tag it yet. + +Next step is to update the [HighFive/spack](https://github.com/BlueBrain/spack) +recipe such that the proposed version points to the release candidate using the +SHA of that commit. The recipe will look something like this: + +```python + # ... + + version("2.8.0", commit="094400f22145bcdcd2726ce72888d9d1c21e7068") + version("2.7.1", sha256="25b4c51a94d1e670dc93b9b73f51e79b65d8ff49bcd6e5d5582d5ecd2789a249") + version("2.7.0", sha256="8e05672ddf81a59ce014b1d065bd9a8c5034dbd91a5c2578e805ef880afa5907") + # ... +``` + +Push the changes to the BlueBrain spack repository. This will trigger building +all BBP dependencies of HighFive, i.e. another integration test. Don't actually +merge this commit yet. + +Now that we know that the integration test ran, and all BBP software can be +built with the proposed version of HighFive, we can proceed and create the +release. Once this is done perform a final round of updates: + +* Download the archive (`*.tar.gz`) and compute its SHA256. +* Update BlueBrain Spack recipe to use the archive and not the Git commit. +* Update the upstream Spack recipe. + diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/doxygen-awesome-css/doxygen-awesome.css b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/doxygen-awesome-css/doxygen-awesome.css similarity index 99% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/doxygen-awesome-css/doxygen-awesome.css rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/doxygen-awesome-css/doxygen-awesome.css index e8399cbce5..08238977a6 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/doxygen-awesome-css/doxygen-awesome.css +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/doxygen-awesome-css/doxygen-awesome.css @@ -776,6 +776,8 @@ html.dark-mode iframe#MSearchResults { #side-nav { padding: 0 !important; background: var(--side-nav-background); + min-width: 8px; + max-width: 50vw; } @media screen and (max-width: 767px) { @@ -863,8 +865,9 @@ html.dark-mode iframe#MSearchResults { } .ui-resizable-e { - background: var(--separator-color); - width: 1px; + width: 4px; + background: transparent; + box-shadow: inset -1px 0 0 0 var(--separator-color); } /* @@ -2450,6 +2453,10 @@ h2:hover a.anchorlink, h1:hover a.anchorlink, h3:hover a.anchorlink, h4:hover a. Optional tab feature */ +.tabbed { + margin: var(--spacing-medium) auto; +} + .tabbed ul { padding-inline-start: 0px; margin: 0; @@ -2487,6 +2494,7 @@ h2:hover a.anchorlink, h1:hover a.anchorlink, h3:hover a.anchorlink, h4:hover a. font-size: var(--page-font-size); cursor: pointer; box-shadow: 0 1px 0 0 var(--separator-color); + position: relative; } .tabs-overview button.tab-button .tab-title { @@ -2501,22 +2509,22 @@ h2:hover a.anchorlink, h1:hover a.anchorlink, h3:hover a.anchorlink, h4:hover a. } .tabs-overview button.tab-button:hover .tab-title { - background: var(--primary-color); - color: var(--page-background-color); + background: var(--separator-color); box-shadow: none; } .tabs-overview button.tab-button.active { color: var(--primary-color); - box-shadow: 0 1px 0 0 var(--primary-color), inset 0 -1px 0 0 var(--primary-color); } -@media (prefers-color-scheme: dark) { - html:not(.light-mode) .tabs-overview button.tab-button:hover .tab-title { - color: var(--page-foreground-color); - } +.tabs-overview button.tab-button.active::after { + content: ''; + display: block; + position: absolute; + left: 0px; + bottom: 0; + right: 0px; + height: 3px; + border-radius: var(--border-radius-small) var(--border-radius-small) 0 0; + background-color: var(--primary-color); } - -html.dark-mode .tabs-overview button.tab-button:hover .tab-title { - color: var(--page-foreground-color); -} \ No newline at end of file diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/doxygen-awesome-css/update_doxygen_awesome.sh b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/doxygen-awesome-css/update_doxygen_awesome.sh similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/doxygen-awesome-css/update_doxygen_awesome.sh rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/doxygen-awesome-css/update_doxygen_awesome.sh diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/environment.yaml b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/environment.yaml similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/environment.yaml rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/environment.yaml diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/installation.md b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/installation.md new file mode 100644 index 0000000000..41521bba5e --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/installation.md @@ -0,0 +1,254 @@ +# Beginners Installation Guide on Linux + +These installation instruction are aimed at developers that aren't very +familiar with installing C/C++ software and using CMake on Linux. + +## Obtaining CMake +You'll need a compiler and CMake. We'll assume that a reasonably modern C++ +compiler is available. Often a sufficiently new version CMake is also present +on the system. + +If not, there's two options: use the system package manager or use `pip`. CMake +is improving in leaps and bounds. Which means you want a recent version. We +suggest reconsidering fighting an older version of CMake if you can simply +install the newest version via `pip`. + +## Obtaining HDF5 +First, you need to decide if you need MPI-support. Rule of thumb is: if you're +unsure, then you don't need it. If you need MPI you must install an +MPI-enabled version of HDF5. Otherwise pick either one, if something is already +installed, e.g. because `h5py` was installed as a system package, stick with +that version. + +The options for installing HDF5 are: +1. Use the system package manager. +2. On a cluster use modules. +3. Use [Spack](https://github.com/spack/spack). +4. Use [Conan](https://conan.io). +5. Manually install it. + +The system package manager will install HDF5 in a default location, were CMake +will later be able to find it without further help. All the other approaches +install into a non-default location and CMake might need help locating HDF5. +The way one tells CMake where to find HDF5 is through `CMAKE_PREFIX_PATH`, +e.g., + + cmake -DCMAKE_PREFIX_PATH="${HDF5_ROOT}" ... + +Note that `${HDF5_ROOT}` points to the folder which contains the two folders +`include` and `lib`. + +### System Package Manager +The default choice is to use the system package manager to install HDF5. +One thing to keep an eye out is that certain Linux distributions don't install +the headers automatically. Since you're about to develop an application which +(indirectly) uses HDF5, you need those headers. If the packages are split, the +development package is often suffixed with `-dev` or `-devel`. + +#### Ubuntu +The package manager is apt. To install the HDF5 C library without MPI support: + + sudo apt-get install libhdf5-dev + +for MPI support you'd install `libhdf5-openmpi-dev`. + +#### ArchLinux +On ArchLinux you install + + sudo pacman -S hdf5 + +or `hdf5-openmpi` for HDF5 with OpenMPI support. + + +### Using Modules +If you're on a cluster, HDF5 has almost certainly been installed for you. +Figure out how to use it. This is the preferred solution on clusters. As +always, the precise instructions depend on the cluster, but something like + + module load hdf5 + +will probably be part of the solution. Try if `module avail` helps. Otherwise, +you'd need to check the documentation for your cluster. Cluster admins like to +hide the good stuff, i.e. modern versions, behind another package `"new"` or +some other mechanism. + +You might need to know where HDF5 has been installed. You can find out what a +module does by typing + + module show hdf5 + +If it contains something about prepending to `CMAKE_PREFIX_PATH`, then CMake +should find the correct version automatically after loading the module. + +### Using Spack +If neither of the above work, the next best choice might be Spack. It's a +package manager for scientific computing. The general idea behind it is to +avoid dependency issues by compiling a compatible set of everything. + +Obtain Spack by cloning their repo: + + git clone https://github.com/spack/spack.git + +Activate Spack by sourcing a magic file: + + source spack/share/spack/setup-env.sh + +which will put the command `spack` into your `PATH`. Okay, now we're set. First +step is to create an environment for your project, which we'll call `useful`: + + spack env create useful + spack env activate -p useful + spack add hdf5 + spack install --jobs NPROC + +If you need MPI support use `hdf5+mpi` instead. The location of the HDF5 +installation is `spack location --install-dir hdf5`. + +### Conan +If Spack doesn't work, you can try Conan. + +### Manually Install HDF5 +If all else fails, you can try to manually install HDF5. First you need to +obtain the source code. For example by using `git` or by downloading an archive +from their webpage. + + git clone https://github.com/HDFGroup/hdf5 + cd hdf5 + git checkout hdf5-1_14_0 + +Now, fingers crossed it'll compile and install: + + cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../hdf5-v1.14.0 -B build . + cmake --build build --parallel [NPROC] + cmake --install build + +Note that here we picked the installation path (or more precisely prefix) to be +`../hdf5-v1.14.0`. You might want to install HDF5 somewhere else. This +installation prefix is the also the path you need to give CMake so it's able to +find HDF5 later on. + +### Confirming HDF5 Has Been Installed +For this purpose we need a dummy file `dummy.cpp` to compile: + + #include + + int main() { + auto file = H5Fcreate("foo.h5", H5F_ACC_EXCL, H5P_DEFAULT, H5P_DEFAULT); + H5Fclose(file); + return 0; + } + +and a `CMakeLists.txt` with the following contents + + cmake_minimum_required(VERSION 3.19) + project(Dummy) + + find_package(HDF5 REQUIRED) + add_executable(dummy dummy.cpp) + target_link_libraries(dummy HDF5::HDF5) + +Now run CMake to configure the build system and keep an eye out for some a line +related to HDF5, e.g. + + $ cmake -B build . + ... + -- Found HDF5: hdf5-shared (found version "1.14.0") + ... + +Compile and check that it's doing something with sensible + + $ cmake -build build --verbose + [ 50%] Building CXX object CMakeFiles/dummy.dir/dummy.cpp.o + /usr/bin/c++ ... -isystem ${HDF5_ROOT}/include ... -c ${PWD}/dummy.cpp + [100%] Linking CXX executable dummy + /usr/bin/c++ ... -o dummy -Wl,-rpath,${HDF5_ROOT}/lib ${HDF5_ROOT}/lib/libhdf5.so.310.0.0 ... + +mostly you're checking that the paths are what you'd expect them to be. If +this command was successful, chances are high that HDF5 is properly installed +and you've figured out the correct CMake invocations. If you want you can run +the executable: + + build/dummy + +which would create an empty file `foo.h5`. + +## Obtaining HighFive + +In principle the same instruction as for HDF5 can be used. However, HighFive is +much less popular compared to HDF5 and therefore the system package manager +likely doesn't know about it, nor does Conan. You're left with Spack and the +manual process. It seems someone has done the wonderful work of adding HighFive +to conda-forge, so maybe that's also an option. + +### Git Submodules +This is the well-tested method for "vendoring" HighFive, i.e. including the +HighFive sources with those of you project. + +### Spack +Similarly as for HDF5, you can use Spack to install HighFive: + + spack env activate -p useful + spack add highfive + spack install --jobs NPROC + +Again `spack location --install-dir highfive` will return the path where +HighFive was installed. Since the Spack recipe of HighFive declares HDF5 as a +dependency, technically, it's not necessary to add `hdf5`, just `highfive` is +enough. + +### Manually Install HighFive +Just like before the steps are, clone, configure, compile (kinda a no-op), +install. The detailed instructions would be + + git clone --recursive https://github.com/BlueBrain/HighFive.git + cd HighFive + git checkout v2.8.0 + +If it complains that Catch is missing, you forgot the `--recursive`. To fix +this you type + + git submodule update --init --recursive + +Okay, on to configure, compile and install. The CMake commands are + + cmake -DCMAKE_INSTALL_PREFIX=../highfive-v2.7.1 -DHIGHFIVE_USE_BOOST=Off -B build . + cmake --build build --parallel + cmake --install build + +### Confirming It Works +We again need a dummy file called `dummy.cpp` with the following contents + + #include + + int main() { + auto file = HighFive::File("foo.h5", HighFive::File::Create); + return 0; + } + +and the following `CMakeLists.txt`: + + cmake_minimum_required(VERSION 3.19) + project(UseHighFive) + + find_package(HighFive REQUIRED) + add_executable(dummy dummy.cpp) + target_link_libraries(dummy HighFive) + +The required CMake commands are: + + $ cmake -DCMAKE_PREFIX_PATH="${HDF5_ROOT};${HIGHFIVE_ROOT}" -B build . + ... + -- HIGHFIVE 2.7.1: (Re)Detecting Highfive dependencies (HIGHFIVE_USE_INSTALL_DEPS=NO) + -- Found HDF5: hdf5-shared (found version "1.14.0") + ... + + $ cmake --build build --verbose + [ 50%] Building CXX object CMakeFiles/dummy.dir/dummy.cpp.o + /usr/bin/c++ ... -isystem ${HIGHFIVE_ROOT}/include -isystem ${HDF5_ROOT}/include ... -c dummy.cpp + [100%] Linking CXX executable dummy + /usr/bin/c++ ... -o dummy -Wl,-rpath,${HDF5_ROOT}/lib ${HDF5_ROOT}/lib/libhdf5.so.310.0.0 ... + +Pay attention to the semi-colon (not colon like the rest of Linux) used to +separate directories in `CMAKE_PREFIX_PATH`. If this worked you should be set +to either copy the instruction to your "real" project, or start developing the +rest of your project. diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example1_hdf5.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example1_hdf5.cpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example1_hdf5.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example1_hdf5.cpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example1_highfive.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example1_highfive.cpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example1_highfive.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example1_highfive.cpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example3.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example3.cpp similarity index 95% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example3.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example3.cpp index 64f2ed6be7..e18fbbf83e 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example3.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example3.cpp @@ -1,4 +1,4 @@ -#include +#include typedef struct { diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example6.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example6.cpp similarity index 93% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example6.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example6.cpp index 8f7419f23f..41a0505705 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example6.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example6.cpp @@ -2,9 +2,7 @@ #include -#include -#include -#include +#include int main(int argc, char** argv) { diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_boost.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_boost.cpp similarity index 85% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_boost.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_boost.cpp index aceaa20681..56b78d074e 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_boost.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_boost.cpp @@ -1,11 +1,9 @@ #include #define H5_USE_BOOST 1 +#include #include -#include -#include -#include using complex_t = std::complex; diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_boost_ublas.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_boost_ublas.cpp similarity index 97% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_boost_ublas.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_boost_ublas.cpp index 3a2b4c73a5..986a671de5 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_boost_ublas.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_boost_ublas.cpp @@ -1,6 +1,7 @@ #include #define H5_USE_BOOST 1 +#include // In some versions of Boost (starting with 1.64), you have to // include the serialization header before ublas @@ -8,7 +9,6 @@ #include #include -#include using namespace HighFive; diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_easy_h5py.py b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_easy_h5py.py similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_easy_h5py.py rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_easy_h5py.py diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_easy_highfive.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_easy_highfive.cpp similarity index 99% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_easy_highfive.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_easy_highfive.cpp index 07d37a22c5..700056cae9 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_easy_highfive.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_easy_highfive.cpp @@ -1,4 +1,5 @@ #include + #include int main() { diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_props.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_props.cpp similarity index 94% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_props.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_props.cpp index e46fe119ce..0e5b14bde7 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/example_props.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/example_props.cpp @@ -1,4 +1,4 @@ -#include +#include using namespace HighFive; diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/examples.js b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/examples.js similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/examples.js rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/examples.js diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/godbolt.org.ico b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/godbolt.org.ico similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/godbolt.org.ico rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/godbolt.org.ico diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/index.html b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/index.html similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/doc/poster/index.html rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/doc/poster/index.html diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Attribute.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Attribute.hpp new file mode 100644 index 0000000000..810d388ae8 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Attribute.hpp @@ -0,0 +1,266 @@ +/* + * Copyright (c), 2017, Ali Can Demiralp + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#pragma once + +#include + +#include + +#include "H5DataType.hpp" +#include "H5Object.hpp" +#include "bits/H5Friends.hpp" +#include "bits/H5Path_traits.hpp" + +namespace HighFive { +class DataSpace; + +namespace detail { + +/// \brief Internal hack to create an `Attribute` from an ID. +/// +/// WARNING: Creating an Attribute from an ID has implications w.r.t. the lifetime of the object +/// that got passed via its ID. Using this method careless opens up the suite of issues +/// related to C-style resource management, including the analog of double free, dangling +/// pointers, etc. +/// +/// NOTE: This is not part of the API and only serves to work around a compiler issue in GCC which +/// prevents us from using `friend`s instead. This function should only be used for internal +/// purposes. The problematic construct is: +/// +/// template +/// friend class SomeCRTP; +/// +/// \private +Attribute make_attribute(hid_t hid); +} // namespace detail + +/// \brief Class representing an Attribute of a DataSet or Group +/// +/// \sa AnnotateTraits::createAttribute, AnnotateTraits::getAttribute, AnnotateTraits::listAttributeNames, AnnotateTraits::hasAttribute, AnnotateTraits::deleteAttribute for create, get, list, check or delete Attribute +class Attribute: public Object, public PathTraits { + public: + const static ObjectType type = ObjectType::Attribute; + + /// \brief Get the name of the current Attribute. + /// \code{.cpp} + /// auto attr = dset.createAttribute("my_attribute", DataSpace::From(string_list)); + /// std::cout << attr.getName() << std::endl; // Will print "my_attribute" + /// \endcode + /// \since 2.2.2 + std::string getName() const; + + /// \brief The number of bytes required to store the attribute in the HDF5 file. + /// \code{.cpp} + /// size_t size = dset.createAttribute("foo", DataSpace(1, 2)).getStorageSize(); + /// \endcode + /// \since 1.0 + size_t getStorageSize() const; + + /// \brief Get the DataType of the Attribute. + /// \code{.cpp} + /// Attribute attr = dset.createAttribute("foo", DataSpace(1, 2)); + /// auto dtype = attr.getDataType(); // Will be an hdf5 type deduced from int + /// \endcode + /// \since 1.0 + DataType getDataType() const; + + /// \brief Get the DataSpace of the current Attribute. + /// \code{.cpp} + /// Attribute attr = dset.createAttribute("foo", DataSpace(1, 2)); + /// auto dspace = attr.getSpace(); // This will be a DataSpace of dimension 1 * 2 + /// \endcode + /// \since 1.0 + DataSpace getSpace() const; + + /// \brief Get the DataSpace of the current Attribute. + /// \note This is an alias of getSpace(). + /// \since 1.0 + DataSpace getMemSpace() const; + + /// \brief Get the value of the Attribute. + /// \code{.cpp} + /// Attribute attr = dset.getAttribute("foo"); + /// // The value will contains what have been written in the attribute + /// std::vector value = attr.read>(); + /// \endcode + /// \since 2.5.0 + template + T read() const; + + /// \brief Get the value of the Attribute in a buffer. + /// + /// Read the attribute into an existing object. Only available for + /// supported types `T`. If `array` has preallocated the correct amount of + /// memory, then this routine should not trigger reallocation. Otherwise, + /// if supported, the object will be resized. + /// + /// An exception is raised if the numbers of dimension of the buffer and of + /// the attribute are different. + /// + /// \code{.cpp} + /// // Will read into `value` avoiding memory allocation if the dimensions + /// // match, i.e. if the attribute `"foo"` has three element. + /// std::vector value(3); + /// file.getAttribute("foo").read(value); + /// \endcode + /// \since 1.0 + template + void read(T& array) const; + + /// \brief Read the attribute into a pre-allocated buffer. + /// \param array A pointer to the first byte of sufficient pre-allocated memory. + /// \param mem_datatype The DataType of the array. + /// + /// \note This is the shallowest wrapper around `H5Aread`. If possible + /// prefer either Attribute::read() const or Attribute::read(T&) const. + /// + /// \code{.cpp} + /// auto attr = file.getAttribute("foo"); + /// + /// // Simulate custom allocation by the application. + /// size_t n_elements = attr.getSpace().getElementCount(); + /// int * ptr = (int*) malloc(n_elements*sizeof(int)); + /// + /// // Read into the pre-allocated memory. + /// attr.read(ptr, mem_datatype); + /// \endcode + /// \since 2.2.2 + template + void read(T* array, const DataType& mem_datatype) const; + + /// \brief Read the attribute into a buffer. + /// Behaves like Attribute::read(T*, const DataType&) const but + /// additionally this overload deduces the memory datatype from `T`. + /// + /// \param array Pointer to the first byte of pre-allocated memory. + /// + /// \note If possible prefer either Attribute::read() const or Attribute::read(T&) const. + /// + /// \code{.cpp} + /// auto attr = file.getAttribute("foo"); + /// + /// // Simulate custom allocation by the application. + /// size_t n_elements = attr.getSpace().getElementCount(); + /// int * ptr = (int*) malloc(n_elements*sizeof(int)); + /// + /// // Read into the pre-allocated memory. + /// attr.read(ptr); + /// \endcode + /// \since 2.2.2 + template + void read(T* array) const; + + /// \brief Write the value into the Attribute. + /// + /// Write the value to the attribute. For supported types `T`, this overload + /// will write the value to the attribute. The datatype and dataspace are + /// deduced automatically. However, since the attribute has already been + /// created, the dimensions of `value` must match those of the attribute. + /// + /// \code{.cpp} + /// // Prefer the fused version if creating and writing the attribute + /// // at the same time. + /// dset.createAttribute("foo", std::vector{1, 2, 3}); + /// + /// // To overwrite the value: + /// std::vector value{4, 5, 6}; + /// dset.getAttribute("foo").write(value); + /// \endcode + /// \since 1.0 + template + void write(const T& value); + + /// \brief Write from a raw pointer. + /// + /// Values that have been correctly arranged memory, can be written directly + /// by passing a raw pointer. + /// + /// \param buffer Pointer to the first byte of the value. + /// \param mem_datatype The DataType of the buffer. + /// + /// \note This is the shallowest wrapper around `H5Awrite`. It's useful + /// if you need full control. If possible prefer Attribute::write. + /// + /// \code{.cpp} + /// Attribute attr = dset.createAttribute("foo", DataSpace(2, 3)); + /// + /// // Simulate the application creating `value` and only exposing access + /// // to the raw pointer `ptr`. + /// std::vector> value{{1, 2, 3}, {4, 5, 6}}; + /// int * ptr = (int*) value.data(); + /// + /// // Simply write the bytes to disk. + /// attr.write(ptr, AtomicType()); + /// \endcode + /// \since 2.2.2 + template + void write_raw(const T* buffer, const DataType& mem_datatype); + + /// \brief Write from a raw pointer. + /// + /// Much like Attribute::write_raw(const T*, const DataType&). + /// Additionally, this overload attempts to automatically deduce the + /// datatype of the buffer. Note, that the file datatype is already set. + /// + /// \param buffer Pointer to the first byte. + /// + /// \note If possible prefer Attribute::write. + /// + /// \code{.cpp} + /// // Simulate the application creating `value` and only exposing access + /// // to the raw pointer `ptr`. + /// std::vector> value{{1, 2, 3}, {4, 5, 6}}; + /// int * ptr = (int*) value.data(); + /// + /// // Simply write the bytes to disk. + /// attr.write(ptr); + /// \endcode + /// \since 2.2.2 + template + void write_raw(const T* buffer); + + /// \brief The create property list used for this attribute. + /// + /// Some of HDF5 properties/setting of an attribute are defined by a + /// create property list. This method returns a copy of the create + /// property list used during creation of the attribute. + /// + /// \code{.cpp} + /// auto acpl = attr.getCreatePropertyList(); + /// + /// // For example to create another attribute with the same properties. + /// file.createAttribute("foo", 42, acpl); + /// \endcode + /// \since 2.5.0 + AttributeCreateProps getCreatePropertyList() const { + return details::get_plist(*this, H5Aget_create_plist); + } + + // No empty attributes + Attribute() = delete; + + protected: + using Object::Object; + + private: +#if HIGHFIVE_HAS_FRIEND_DECLARATIONS + template + friend class ::HighFive::AnnotateTraits; +#endif + + friend Attribute detail::make_attribute(hid_t); +}; + +namespace detail { +inline Attribute make_attribute(hid_t hid) { + return Attribute(hid); +} +} // namespace detail + +} // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5DataSet.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5DataSet.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5DataSet.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5DataSet.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5DataSpace.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5DataSpace.hpp new file mode 100644 index 0000000000..95d04dbbbd --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5DataSpace.hpp @@ -0,0 +1,243 @@ +/* + * Copyright (c), 2017, Adrien Devresse + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#pragma once + +#include +#include +#include +#include +#include + +#include "H5Object.hpp" +#include "bits/H5_definitions.hpp" + +namespace HighFive { + +/// \brief Class representing the space (dimensions) of a DataSet +/// +/// \code{.cpp} +/// // Create a DataSpace of dimension 1 x 2 x 3 +/// DataSpace dspace(1, 2, 3); +/// std::cout << dspace.getElementCount() << std::endl; // Print 1 * 2 * 3 = 6 +/// std::cout << dspace.getNumberDimensions() << std::endl; // Print 3 +/// std::vector dims = dspace.getDimensions(); // dims is {1, 2, 3} +/// \endcode +class DataSpace: public Object { + public: + const static ObjectType type = ObjectType::DataSpace; + + /// \brief Magic value to specify that a DataSpace can grow without limit. + /// + /// This value should be used with DataSpace::DataSpace(const std::vector& dims, const + /// std::vector& maxdims); + /// + /// \since 2.0 + static const size_t UNLIMITED = SIZE_MAX; + + /// \brief An enum to create scalar and null DataSpace with DataSpace::DataSpace(DataspaceType dtype). + /// + /// This enum is needed otherwise we will not be able to distringuish between both with normal + /// constructors. Both have a dimension of 0. + /// \since 1.3 + enum DataspaceType { + dataspace_scalar, ///< Value to create scalar DataSpace + dataspace_null, ///< Value to create null DataSpace + // simple dataspace are handle directly from their dimensions + }; + + /// \brief Create a DataSpace of N-dimensions from a std::vector. + /// \param dims Dimensions of the new DataSpace + /// + /// \code{.cpp} + /// // Create a DataSpace with 2 dimensions: 1 and 3 + /// DataSpace(std::vector{1, 3}); + /// \endcode + /// \since 1.0 + explicit DataSpace(const std::vector& dims); + + /// \brief Create a DataSpace of N-dimensions from a std::array. + /// \param dims Dimensions of the new DataSpace + /// + /// \code{.cpp} + /// // Create a DataSpace with 2 dimensions: 1 and 3 + /// DataSpace(std::array{1, 3}); + /// \endcode + /// \since 2.3 + template + explicit DataSpace(const std::array& dims); + + /// \brief Create a DataSpace of N-dimensions from an initializer list. + /// \param dims Dimensions of the new DataSpace + /// + /// \code{.cpp} + /// // Create a DataSpace with 2 dimensions: 1 and 3 + /// DataSpace{1, 3}; + /// \endcode + /// \since 2.1 + DataSpace(const std::initializer_list& dims); + + /// \brief Create a DataSpace of N-dimensions from direct values. + /// \param dim1 The first dimension + /// \param dims The following dimensions + /// + /// \code{.cpp} + /// // Create a DataSpace with 2 dimensions: 1 and 3 + /// DataSpace(1, 3); + /// \endcode + /// \since 2.1 + template + explicit DataSpace(size_t dim1, Args... dims); + + /// \brief Create a DataSpace from a pair of iterators. + /// \param begin The beginning of the container + /// \param end The end of the container + /// + /// \code{.cpp} + /// // Create a DataSpace with 2 dimensions: 1 and 3 + /// std::vector v{1, 3}; + /// DataSpace(v.begin(), v.end()); + /// \endcode + /// + /// \since 2.0 + // Attention: Explicitly disable DataSpace(int_like, int_like) from trying + // to use this constructor + template ::value, IT>::type> + DataSpace(const IT begin, const IT end); + + /// \brief Create a resizable N-dimensional DataSpace. + /// \param dims Initial size of dataspace + /// \param maxdims Maximum size of the dataspace + /// + /// \code{.cpp} + /// // Create a DataSpace with 2 dimensions: 1 and 3. + /// // It can later be resized up to a maximum of 10 x 10 + /// DataSpace(std::vector{1, 3}, std::vector{10, 10}); + /// \endcode + /// + /// \see UNLIMITED for a DataSpace that can be resized without limit. + /// \since 2.0 + explicit DataSpace(const std::vector& dims, const std::vector& maxdims); + + /// \brief Create a scalar or a null DataSpace. + /// + /// This overload enables creating scalar or null data spaces, both have + /// dimension 0. + /// + /// \param space_type The value from the enum + /// + /// \code{.cpp} + /// DataSpace(DataspaceType::dataspace_scalar); + /// \endcode + /// + /// \attention Avoid braced intialization in these cases, i.e. + /// \code{.cpp} + /// // This is not a scalar dataset: + /// DataSpace{DataspaceType::dataspace_scalar}; + /// \endcode + /// + /// \since 1.3 + explicit DataSpace(DataspaceType space_type); + + /// \brief Create a copy of the DataSpace which will have different id. + /// + /// \code{.cpp} + /// DataSpace dspace1(1, 3); + /// auto dspace2 = dspace.clone(); + /// \endcode + /// + /// \since 1.0 + DataSpace clone() const; + + /// \brief Returns the number of dimensions of a DataSpace. + /// \code{.cpp} + /// DataSpace dspace(1, 3); + /// size_t number_of_dim = dspace.getNumberDimensions(); // returns 2 + /// \endcode + /// \since 1.0 + size_t getNumberDimensions() const; + + /// \brief Returns the size of the dataset in each dimension. + /// + /// For zero-dimensional datasets (e.g. scalar or null datasets) an empty + /// vector is returned. + /// + /// \code{.cpp} + /// DataSpace dspace(1, 3); + /// auto dims = dspace.getDimensions(); // returns {1, 3} + /// \endcode + /// + /// \sa DataSpace::getMaxDimensions + /// + /// \since 1.0 + std::vector getDimensions() const; + + /// \brief Return the number of elements in this DataSpace. + /// + /// \code{.cpp} + /// DataSpace dspace(1, 3); + /// size_t elementcount = dspace.getElementCount(); // return 1 x 3 = 3 + /// \endcode + /// \since 2.1 + size_t getElementCount() const; + + /// \brief Returns the maximum size of the dataset in each dimension. + /// + /// This is the maximum size a dataset can be extended to, which may be + /// different from the current size of the dataset. + /// + /// \code{.cpp} + /// DataSpace dspace(std::vector{1, 3}, std::vector{UNLIMITED, 10}); + /// dspace.getMaxDimensions(); // Return {UNLIMITED, 10} + /// \endcode + /// + /// \sa DataSpace::getDimensions + /// \since 2.0 + std::vector getMaxDimensions() const; + + /// \brief Automatically deduce the DataSpace from a container/value. + /// + /// Certain containers and scalar values are fully supported by HighFive. + /// For these containers, HighFive can deduce the dimensions from `value`. + /// + /// \code{.cpp} + /// double d = 42.0; + /// std::vector> v = {{4, 5, 6}, {7, 8, 9}}; + /// DataSpace::From(v); // A DataSpace of dimensions 2, 3. + /// DataSpace::From(d); // A scalar dataspace. + /// \endcode + /// + /// \since 1.0 + template + static DataSpace From(const T& value); + + /// \brief Create a DataSpace from a value of type string array. + /// \param string_array An C-array of C-string (null-terminated). + /// + /// \code{.cpp} + /// char string_array[2][10] = {"123456789", "abcdefghi"}; + /// auto dspace = DataSpace::FromCharArrayStrings(string_array); // dspace is a DataSpace of + /// dimensions 2 + /// \endcode + /// \since 2.2 + template + static DataSpace FromCharArrayStrings(const char (&string_array)[N][Width]); + + protected: + DataSpace() = default; + + friend class Attribute; + friend class File; + friend class DataSet; +}; + +} // namespace HighFive + +// We include bits right away since DataSpace is user-constructible +#include "bits/H5Dataspace_misc.hpp" diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5DataType.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5DataType.hpp similarity index 80% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5DataType.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5DataType.hpp index 43f758e452..886107961b 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5DataType.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5DataType.hpp @@ -11,9 +11,12 @@ #include #include +#include + #include "H5Object.hpp" #include "bits/H5Utils.hpp" +#include "bits/string_padding.hpp" #include "H5PropertyList.hpp" namespace HighFive { @@ -47,6 +50,7 @@ inline DataTypeClass operator&(DataTypeClass lhs, DataTypeClass rhs) { return static_cast(static_cast(lhs) & static_cast(rhs)); } +class StringType; /// /// \brief HDF5 Data Type @@ -85,6 +89,11 @@ class DataType: public Object { /// bool isFixedLenStr() const; + /// + /// \brief Returns this datatype as a `StringType`. + /// + StringType asStringType() const; + /// /// \brief Check the DataType was default constructed. /// Such value might represent auto-detection of the datatype from a buffer @@ -106,8 +115,67 @@ class DataType: public Object { friend class File; friend class DataSet; friend class CompoundType; + template + friend class NodeTraits; +}; + + +enum class CharacterSet : std::underlying_type::type { + Ascii = H5T_CSET_ASCII, + Utf8 = H5T_CSET_UTF8, +}; + +class StringType: public DataType { + public: + /// + /// \brief For stings return the character set. + /// + CharacterSet getCharacterSet() const; + + /// + /// \brief For fixed length stings return the padding. + /// + StringPadding getPadding() const; + + protected: + using DataType::DataType; + friend class DataType; +}; + +class FixedLengthStringType: public StringType { + public: + /// + /// \brief Create a fixed length string datatype. + /// + /// The string will be `size` bytes long, regardless whether it's ASCII or + /// UTF8. In particular, a string with `n` UFT8 characters in general + /// requires `4*n` bytes. + /// + /// The string padding is subtle, essentially it's just a hint. A + /// nullterminated string is guaranteed to have one `'\0'` which marks the + /// semantic end of the string. The length of the buffer must be at least + /// `size` bytes regardless. HDF5 will read or write `size` bytes, + /// irrespective of the when the `\0` occurs. + /// + /// Note that when writing passing `StringPadding::NullTerminated` is a + /// guarantee to the reader that it contains a `\0`. Therefore, make sure + /// that the string really is nullterminated. Otherwise prefer a + /// null-padded string which only means states that the buffer is filled up + /// with 0 or more `\0`. + FixedLengthStringType(size_t size, + StringPadding padding, + CharacterSet character_set = CharacterSet::Ascii); }; +class VariableLengthStringType: public StringType { + public: + /// + /// \brief Create a variable length string HDF5 datatype. + /// + VariableLengthStringType(CharacterSet character_set = CharacterSet::Ascii); +}; + + /// /// \brief create an HDF5 DataType from a C++ type /// @@ -175,11 +243,14 @@ class CompoundType: public DataType { size_t n_members = static_cast(result); members.reserve(n_members); for (unsigned i = 0; i < n_members; i++) { - const char* name = H5Tget_member_name(_hid, i); + char* name = H5Tget_member_name(_hid, i); size_t offset = H5Tget_member_offset(_hid, i); hid_t member_hid = H5Tget_member_type(_hid, i); DataType member_type{member_hid}; - members.emplace_back(name, member_type, offset); + members.emplace_back(std::string(name), member_type, offset); + if (H5free_memory(name) < 0) { + throw DataTypeException("Could not free names from the compound datatype"); + } } } @@ -250,7 +321,7 @@ class EnumType: public DataType { } EnumType(std::initializer_list t_members) - : EnumType(std::vector({t_members})) {} + : EnumType(std::vector(t_members)) {} /// \brief Commit datatype into the given Object /// \param object Location to commit object into @@ -280,15 +351,20 @@ DataType create_and_check_datatype(); /// Although fixed-len arrays can be created 'raw' without the need for /// this structure, to retrieve results efficiently it must be used. /// +/// \tparam N Size of the string in bytes, including the null character. Note, +/// that all string must be null-terminated. +/// template class FixedLenStringArray { public: FixedLenStringArray() = default; /// - /// \brief Create a FixedStringArray from a raw contiguous buffer + /// \brief Create a FixedStringArray from a raw contiguous buffer. + /// + /// The argument `n_strings` specifies the number of strings. /// - FixedLenStringArray(const char array[][N], std::size_t length); + FixedLenStringArray(const char array[][N], std::size_t n_strings); /// /// \brief Create a FixedStringArray from a sequence of strings. diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Easy.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Easy.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Easy.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Easy.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Exception.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Exception.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Exception.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Exception.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5File.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5File.hpp similarity index 88% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5File.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5File.hpp index d8dac16964..9b393e5a35 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5File.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5File.hpp @@ -110,6 +110,17 @@ class File: public Object, public NodeTraits, public AnnotateTraits return details::get_plist(*this, H5Fget_access_plist); } + /// \brief Get the size of this file in bytes + size_t getFileSize() const; + + /// \brief Get the amount of tracked, unused space in bytes. + /// + /// Note, this is a wrapper for `H5Fget_freespace` and returns the number + /// bytes in the free space manager. This might be different from the total + /// amount of unused space in the HDF5 file, since the free space manager + /// might not track everything or not track across open-close cycles. + size_t getFreeSpace() const; + protected: File() = default; using Object::Object; diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5FileDriver.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5FileDriver.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5FileDriver.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5FileDriver.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Group.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Group.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Group.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Group.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Object.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Object.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Object.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Object.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5PropertyList.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5PropertyList.hpp similarity index 77% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5PropertyList.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5PropertyList.hpp index 6122820e5a..53b3c4a137 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5PropertyList.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5PropertyList.hpp @@ -22,6 +22,67 @@ namespace HighFive { +/// \defgroup PropertyLists Property Lists +/// HDF5 is configured through what they call property lists. In HDF5 the +/// process has four steps: +/// +/// 1. Create a property list. As users we now have an `hid_t` identifying the +/// property list. +/// 2. Set properties as desired. +/// 3. Pass the HID to the HDF5 function to be configured. +/// 4. Free the property list. +/// +/// Note that the mental picture is that one creates a settings object, and +/// then passes those settings to a function such as `H5Dwrite`. In and of +/// themselves the settings don't change the behaviour of HDF5. Rather they +/// need to be used to take affect. +/// +/// The second aspect is that property lists represent any number of related +/// settings, e.g. there's property lists anything related to creating files +/// and another for accessing files, same for creating and accessing datasets, +/// etc. Settings that affect creating files, must be passed a file creation +/// property list, while settings that affect file access require a file access +/// property list. +/// +/// In HighFive the `PropertyList` works similar in that it's a object +/// representing the settings, i.e. internally it's just the property lists +/// HID. Just like in HDF5 one adds the settings to the settings object; and +/// then passes the settings object to the respective method. Example: +/// +/// +/// // Create an object which contains the setting to +/// // open files with MPI-IO. +/// auto fapl = FileAccessProps(); +/// fapl.add(MPIOFileAccess(MPI_COMM_WORLD, MPI_INFO_NULL); +/// +/// // To open a specific file with MPI-IO, we do: +/// auto file = File("foo.h5", File::ReadOnly, fapl); +/// +/// Note that the `MPIOFileAccess` object by itself doesn't affect the +/// `FileAccessProps`. Rather it needs to be explicitly added to the `fapl` +/// (the group of file access related settings), and then the `fapl` needs to +/// be passed to the constructor of `File` for the settings to take affect. +/// +/// This is important to understand when reading properties. Example: +/// +/// // Obtain the file access property list: +/// auto fapl = file.getAccessPropertyList() +/// +/// // Extracts a copy of the collective MPI-IO metadata settings from +/// // the group of file access related setting, i.e. the `fapl`: +/// auto mpio_metadata = MPIOCollectiveMetadata(fapl); +/// +/// if(mpio_metadata.isCollectiveRead()) { +/// // something specific if meta data is read collectively. +/// } +/// +/// // Careful, this only affects the `mpio_metadata` object, but not the +/// // `fapl`, and also not whether `file` uses collective MPI-IO for +/// // metadata. +/// mpio_metadata = MPIOCollectiveMetadata(false, false); +/// +/// @{ + /// /// \brief Types of property lists /// @@ -72,6 +133,26 @@ class PropertyListBase: public Object { friend T details::get_plist(const U&, hid_t (*f)(hid_t)); }; +/// \interface PropertyInterface +/// \brief HDF5 file property object +/// +/// A property is an object which is expected to have a method with the +/// following signature `void apply(hid_t hid) const` +/// +/// \sa Instructions to document C++20 concepts with Doxygen: https://github.com/doxygen/doxygen/issues/2732#issuecomment-509629967 +/// +/// \cond +#if HIGHFIVE_HAS_CONCEPTS && __cplusplus >= 202002L +template +concept PropertyInterface = requires(P p, const hid_t hid) { + {p.apply(hid)}; +}; + +#else +#define PropertyInterface typename +#endif +/// \endcond + /// /// \brief HDF5 property Lists /// @@ -88,8 +169,8 @@ class PropertyList: public PropertyListBase { /// Add a property to this property list. /// A property is an object which is expected to have a method with the /// following signature void apply(hid_t hid) const - /// - template + /// \tparam PropertyInterface + template void add(const P& property); /// @@ -377,6 +458,7 @@ class PageBufferSize { #endif /// \brief Set hints as to how many links to expect and their average length +/// \implements PropertyInterface /// class EstimatedLinkInfo { public: @@ -402,6 +484,7 @@ class EstimatedLinkInfo { }; +/// \implements PropertyInterface class Chunking { public: explicit Chunking(const std::vector& dims); @@ -420,6 +503,7 @@ class Chunking { std::vector _dims; }; +/// \implements PropertyInterface class Deflate { public: explicit Deflate(unsigned level); @@ -431,6 +515,7 @@ class Deflate { const unsigned _level; }; +/// \implements PropertyInterface class Szip { public: explicit Szip(unsigned options_mask = H5_SZIP_EC_OPTION_MASK, @@ -446,6 +531,7 @@ class Szip { const unsigned _pixels_per_block; }; +/// \implements PropertyInterface class Shuffle { public: Shuffle() = default; @@ -460,6 +546,7 @@ class Shuffle { /// The precise time of when HDF5 requests space to store the dataset /// can be configured. Please, consider the upstream documentation for /// `H5Pset_alloc_time`. +/// \implements PropertyInterface class AllocationTime { public: explicit AllocationTime(H5D_alloc_time_t alloc_time); @@ -476,6 +563,7 @@ class AllocationTime { /// Dataset access property to control chunk cache configuration. /// Do not confuse with the similar file access property for H5Pset_cache +/// \implements PropertyInterface class Caching { public: /// https://support.hdfgroup.org/HDF5/doc/RM/H5P/H5Pset_chunk_cache.html for @@ -498,6 +586,7 @@ class Caching { double _w0; }; +/// \implements PropertyInterface class CreateIntermediateGroup { public: explicit CreateIntermediateGroup(bool create = true); @@ -518,6 +607,7 @@ class CreateIntermediateGroup { }; #ifdef H5_HAVE_PARALLEL +/// \implements PropertyInterface class UseCollectiveIO { public: explicit UseCollectiveIO(bool enable = true); @@ -540,6 +630,7 @@ class UseCollectiveIO { /// creation of this object. This object will not update automatically for later data transfers, /// i.e. `H5Pget_mpio_no_collective_cause` is called in the constructor, and not when fetching /// a value, such as `wasCollective`. +/// \implements PropertyInterface class MpioNoCollectiveCause { public: explicit MpioNoCollectiveCause(const DataTransferProps& dxpl); @@ -575,6 +666,7 @@ struct CreationOrder { /// /// Let user retrieve objects by creation order time instead of name. /// +/// \implements PropertyInterface class LinkCreationOrder { public: /// @@ -599,6 +691,44 @@ class LinkCreationOrder { unsigned _flags; }; + +/// +/// \brief Set threshold for attribute storage. +/// +/// HDF5 can store Attributes in the object header (compact) or in the B-tree +/// (dense). This property sets the threshold when attributes are moved to one +/// or the other storage format. +/// +/// Please refer to the upstream documentation of `H5Pset_attr_phase_change` or +/// Section 8 (Attributes) in the User Guide, in particular Subsection 8.5. +/// +/// \implements PropertyInterface +class AttributePhaseChange { + public: + /// + /// \brief Create the property from the threshold values. + /// + /// When the number of attributes hits `max_compact` the attributes are + /// moved to dense storage, once the number drops to below `min_dense` the + /// attributes are moved to compact storage. + AttributePhaseChange(unsigned max_compact, unsigned min_dense); + + /// \brief Extract threshold values from property list. + explicit AttributePhaseChange(const GroupCreateProps& gcpl); + + unsigned max_compact() const; + unsigned min_dense() const; + + private: + friend GroupCreateProps; + void apply(hid_t hid) const; + + unsigned _max_compact; + unsigned _min_dense; +}; + +/// @} + } // namespace HighFive #include "bits/H5PropertyList_misc.hpp" diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Reference.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Reference.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Reference.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Reference.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Selection.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Selection.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Selection.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Selection.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Utility.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Utility.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Utility.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Utility.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Version.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Version.hpp new file mode 100644 index 0000000000..dc238432cb --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Version.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c), 2020 + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#pragma once + +#define HIGHFIVE_VERSION_MAJOR 2 +#define HIGHFIVE_VERSION_MINOR 8 +#define HIGHFIVE_VERSION_PATCH 0 + +/** \brief Concatenated representation of the HighFive version. + * + * \warning The macro `HIGHFIVE_VERSION` by itself isn't valid C/C++. + * + * However, it can be stringified with two layers of macros, e.g., + * \code{.cpp} + * #define STRINGIFY_VALUE(s) STRINGIFY_NAME(s) + * #define STRINGIFY_NAME(s) #s + * + * std::cout << STRINGIFY_VALUE(HIGHFIVE_VERSION) << "\n"; + * \endcode + */ +#define HIGHFIVE_VERSION 2.8.0 + +/** \brief String representation of the HighFive version. + * + * \warning This macro only exists from 2.7.1 onwards. + */ +#define HIGHFIVE_VERSION_STRING "2.8.0" diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Version.hpp.in b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Version.hpp.in similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/H5Version.hpp.in rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/H5Version.hpp.in diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Annotate_traits.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Annotate_traits.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Annotate_traits.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Annotate_traits.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Annotate_traits_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Annotate_traits_misc.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Annotate_traits_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Annotate_traits_misc.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Attribute_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Attribute_misc.hpp similarity index 80% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Attribute_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Attribute_misc.hpp index 65cdb2d86b..6516788297 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Attribute_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Attribute_misc.hpp @@ -61,8 +61,9 @@ inline T Attribute::read() const { template inline void Attribute::read(T& array) const { const DataSpace& mem_space = getMemSpace(); + auto file_datatype = getDataType(); const details::BufferInfo buffer_info( - getDataType(), + file_datatype, [this]() -> std::string { return this->getName(); }, details::BufferInfo::read); @@ -82,37 +83,43 @@ inline void Attribute::read(T& array) const { return; } - auto r = details::data_converter::get_reader(dims, array); - read(r.get_pointer(), buffer_info.data_type); + auto r = details::data_converter::get_reader(dims, array, file_datatype); + read(r.getPointer(), buffer_info.data_type); // re-arrange results - r.unserialize(); - auto t = create_datatype::base_type>(); + r.unserialize(array); + + auto t = buffer_info.data_type; auto c = t.getClass(); + if (c == DataTypeClass::VarLen || t.isVariableStr()) { #if H5_VERSION_GE(1, 12, 0) // This one have been created in 1.12.0 - (void) H5Treclaim(t.getId(), mem_space.getId(), H5P_DEFAULT, r.get_pointer()); + (void) H5Treclaim(t.getId(), mem_space.getId(), H5P_DEFAULT, r.getPointer()); #else // This one is deprecated since 1.12.0 - (void) H5Dvlen_reclaim(t.getId(), mem_space.getId(), H5P_DEFAULT, r.get_pointer()); + (void) H5Dvlen_reclaim(t.getId(), mem_space.getId(), H5P_DEFAULT, r.getPointer()); #endif } } template -inline void Attribute::read(T* array, const DataType& dtype) const { +inline void Attribute::read(T* array, const DataType& mem_datatype) const { static_assert(!std::is_const::value, "read() requires a non-const structure to read data into"); - using element_type = typename details::inspector::base_type; - // Auto-detect mem datatype if not provided - const DataType& mem_datatype = dtype.empty() ? create_and_check_datatype() - : dtype; if (H5Aread(getId(), mem_datatype.getId(), static_cast(array)) < 0) { HDF5ErrMapper::ToException("Error during HDF5 Read: "); } } +template +inline void Attribute::read(T* array) const { + using element_type = typename details::inspector::base_type; + const DataType& mem_datatype = create_and_check_datatype(); + + read(array, mem_datatype); +} + template inline void Attribute::write(const T& buffer) { const DataSpace& mem_space = getMemSpace(); @@ -121,8 +128,10 @@ inline void Attribute::write(const T& buffer) { return; } + auto file_datatype = getDataType(); + const details::BufferInfo buffer_info( - getDataType(), + file_datatype, [this]() -> std::string { return this->getName(); }, details::BufferInfo::write); @@ -132,18 +141,23 @@ inline void Attribute::write(const T& buffer) { << " into dataset of dimensions " << mem_space.getNumberDimensions(); throw DataSpaceException(ss.str()); } - auto w = details::data_converter::serialize(buffer); - write_raw(w.get_pointer(), buffer_info.data_type); + auto w = details::data_converter::serialize(buffer, file_datatype); + write_raw(w.getPointer(), buffer_info.data_type); } template -inline void Attribute::write_raw(const T* buffer, const DataType& dtype) { - using element_type = typename details::inspector::base_type; - const auto& mem_datatype = dtype.empty() ? create_and_check_datatype() : dtype; - +inline void Attribute::write_raw(const T* buffer, const DataType& mem_datatype) { if (H5Awrite(getId(), mem_datatype.getId(), buffer) < 0) { HDF5ErrMapper::ToException("Error during HDF5 Write: "); } } +template +inline void Attribute::write_raw(const T* buffer) { + using element_type = typename details::inspector::base_type; + const auto& mem_datatype = create_and_check_datatype(); + + write_raw(buffer, mem_datatype); +} + } // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Converter_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Converter_misc.hpp new file mode 100644 index 0000000000..00749d1b6d --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Converter_misc.hpp @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2022 Blue Brain Project + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#pragma once + +#include + +#include "H5Inspector_misc.hpp" +#include "../H5DataType.hpp" + +namespace HighFive { +namespace details { + +template +struct is_std_string { + static constexpr bool value = + std::is_same::base_type, std::string>::value; +}; + +template +struct enable_shallow_copy + : public std::enable_if::value && inspector::is_trivially_copyable, V> {}; + +template +struct enable_deep_copy + : public std::enable_if::value && !inspector::is_trivially_copyable, V> {}; + +template +struct enable_string_copy: public std::enable_if::value, V> {}; + + +template +struct ShallowCopyBuffer { + using type = unqualified_t; + using hdf5_type = + typename std::conditional::hdf5_type>::type, + typename inspector::hdf5_type>::type; + + ShallowCopyBuffer() = delete; + + explicit ShallowCopyBuffer(typename std::conditional::type val) + : ptr(inspector::data(val)){}; + + hdf5_type* getPointer() const { + return ptr; + } + + hdf5_type* begin() const { + return getPointer(); + } + + void unserialize(T& /* val */) const { + /* nothing to do. */ + } + + private: + hdf5_type* ptr; +}; + +template +struct DeepCopyBuffer { + using type = unqualified_t; + using hdf5_type = typename inspector::hdf5_type; + + explicit DeepCopyBuffer(const std::vector& _dims) + : buffer(inspector::getSize(_dims)) + , dims(_dims) {} + + hdf5_type* getPointer() { + return buffer.data(); + } + + hdf5_type const* getPointer() const { + return buffer.data(); + } + + hdf5_type* begin() { + return getPointer(); + } + + hdf5_type const* begin() const { + return getPointer(); + } + + void unserialize(T& val) const { + inspector::unserialize(buffer.data(), dims, val); + } + + private: + std::vector buffer; + std::vector dims; +}; + +enum class BufferMode { Read, Write }; + + +/// +/// \brief String length in bytes excluding the `\0`. +/// +inline size_t char_buffer_size(char const* const str, size_t max_string_length) { + for (size_t i = 0; i <= max_string_length; ++i) { + if (str[i] == '\0') { + return i; + } + } + + return max_string_length; +} + + +/// +/// \brief A buffer for reading/writing strings. +/// +/// A string in HDF5 can be represented as a fixed or variable length string. +/// The important difference for this buffer is that `H5D{read,write}` expects +/// different input depending on whether the strings are fixed or variable length. +/// For fixed length strings, it expects an array of chars, i.e. one string +/// packed after the other contiguously. While for variable length strings it +/// expects a list of pointers to the beginning of each string. Variable length +/// string must be null-terminated; because that's how their length is +/// determined. +/// +/// This buffer hides the difference between fixed and variable length strings +/// by having internal data structures available for both cases at compile time. +/// The choice which internal buffer to use is made at runtime. +/// +/// Consider an HDF5 dataset with N fixed-length strings, each of which is M +/// characters long. Then the in-memory strings are copied into an internal +/// buffer of size N*M. If null- or space-padded the buffer should be filled +/// with the appropriate character. This is important if the in-memory strings +/// are less than M characters long. +/// +/// An HDF5 dataset with N variable-length strings (all null-terminated) uses +/// the internal list of pointers to the beginning of each string. Those +/// pointers can either point to the in-memory strings themselves, if those +/// strings are known to be null-terminated. Otherwise the in-memory strings are +/// copied to an internal buffer of null-terminated strings; and the pointer +/// points to the start of the string in the internal buffer. +/// +/// This class is responsible for arranging the strings properly before passing +/// the buffers to HDF5. To keep this class generic, it provides a generic +/// read/write interface to the internal strings, i.e. a pointer with a size. +/// For reading from the buffer the proxy is called `StringConstView`. This +/// proxy object is to be used by the `inspector` to copy from the buffer into +/// the final destination, e.g. an `std::string`. Similarly, there's a proxy +/// object for serializing into the buffer, i.e. the `StringView`. Again the +/// `inspector` is responsible for obtaining the pointer, size and padding of +/// the string. +/// +/// Nomenclature: +/// - size of a string is the number of bytes required to store the string, +/// including the null character for null-terminated strings. +/// +/// - length of a string is the number of bytes without the null character. +/// +/// Note: both 'length' and 'size' are counted in number of bytes, not number +/// of symbols or characters. Even for UTF8 strings. +template +struct StringBuffer { + using type = unqualified_t; + using hdf5_type = typename inspector::hdf5_type; + + class StringView { + public: + StringView(StringBuffer& _buffer, size_t _i) + : buffer(_buffer) + , i(_i) {} + + /// + /// \brief Assign the in-memory string to the buffer. + /// + /// This method copies the in-memory string to the appropriate + /// internal buffer as needed. + /// + /// The `length` is the length of the string in bytes. + void assign(char const* data, size_t length, StringPadding padding) { + if (buffer.isVariableLengthString()) { + if (padding == StringPadding::NullTerminated) { + buffer.variable_length_pointers[i] = data; + } else { + buffer.variable_length_buffer[i] = std::string(data, length); + buffer.variable_length_pointers[i] = buffer.variable_length_buffer[i].data(); + } + } else if (buffer.isFixedLengthString()) { + // If the buffer is fixed-length and null-terminated, then + // `buffer.string_length` doesn't include the null-character. + if (length > buffer.string_length) { + throw std::invalid_argument("String length too big."); + } + + memcpy(&buffer.fixed_length_buffer[i * buffer.string_size], data, length); + } + } + + private: + StringBuffer& buffer; + size_t i; + }; + + + class StringConstView { + public: + StringConstView(const StringBuffer& _buffer, size_t _i) + : buffer(_buffer) + , i(_i) {} + + /// \brief Pointer to the first byte of the string. + /// + /// The valid indices for this pointer are: 0, ..., length() - 1. + char const* data() const { + if (buffer.isVariableLengthString()) { + return buffer.variable_length_pointers[i]; + } else { + return &buffer.fixed_length_buffer[i * buffer.string_size]; + } + } + + /// \brief Length of the string in bytes. + /// + /// Note that for null-terminated strings the "length" doesn't include + /// the null character. Hence, if storing this string as a + /// null-terminated string, the destination buffer needs to be at least + /// `length() + 1` bytes long. + size_t length() const { + if (buffer.isNullTerminated()) { + return char_buffer_size(data(), buffer.string_length); + } else { + return buffer.string_length; + } + } + + private: + const StringBuffer& buffer; + size_t i; + }; + + + class Iterator { + public: + Iterator(StringBuffer& _buffer, size_t _pos) + : buffer(_buffer) + , pos(_pos) {} + + Iterator operator+(size_t n_strings) const { + return Iterator(buffer, pos + n_strings); + } + + void operator+=(size_t n_strings) { + pos += n_strings; + } + + StringView operator*() { + return StringView(buffer, pos); + } + + StringConstView operator*() const { + return StringConstView(buffer, pos); + } + + private: + StringBuffer& buffer; + size_t pos; + }; + + StringBuffer(std::vector _dims, const DataType& _file_datatype) + : file_datatype(_file_datatype.asStringType()) + , padding(file_datatype.getPadding()) + , string_size(file_datatype.isVariableStr() ? size_t(-1) : file_datatype.getSize()) + , string_length(string_size - size_t(isNullTerminated())) + , dims(_dims) { + if (string_size == 0 && isNullTerminated()) { + throw DataTypeException( + "Fixed-length, null-terminated need at least one byte to store the " + "null-character."); + } + + auto n_strings = compute_total_size(dims); + if (isVariableLengthString()) { + variable_length_buffer.resize(n_strings); + variable_length_pointers.resize(n_strings); + } else { + char pad = padding == StringPadding::SpacePadded ? ' ' : '\0'; + fixed_length_buffer.assign(n_strings * string_size, pad); + } + } + + bool isVariableLengthString() const { + return file_datatype.isVariableStr(); + } + + bool isFixedLengthString() const { + return file_datatype.isFixedLenStr(); + } + + bool isNullTerminated() const { + return file_datatype.getPadding() == StringPadding::NullTerminated; + } + + + void* getPointer() { + if (file_datatype.isVariableStr()) { + return variable_length_pointers.data(); + } else { + return fixed_length_buffer.data(); + } + } + + Iterator begin() { + return Iterator(*this, 0ul); + } + + void unserialize(T& val) { + inspector::unserialize(begin(), dims, val); + } + + private: + StringType file_datatype; + StringPadding padding; + size_t string_size; // Size of buffer required to store the string. + // Meaningful for fixed length strings only. + size_t string_length; // Semantic length of string. + std::vector dims; + + std::vector fixed_length_buffer; + std::vector variable_length_buffer; + std::vector< + typename std::conditional::type*> + variable_length_pointers; +}; + + +template +struct Writer; + +template +struct Writer::type>: public ShallowCopyBuffer { + private: + using super = ShallowCopyBuffer; + + public: + explicit Writer(const T& val, const DataType& /* file_datatype */) + : super(val){}; +}; + +template +struct Writer::type>: public DeepCopyBuffer { + explicit Writer(const T& val, const DataType& /* file_datatype */) + : DeepCopyBuffer(inspector::getDimensions(val)) { + inspector::serialize(val, this->begin()); + } +}; + +template +struct Writer::type>: public StringBuffer { + explicit Writer(const T& val, const DataType& _file_datatype) + : StringBuffer(inspector::getDimensions(val), _file_datatype) { + inspector::serialize(val, this->begin()); + } +}; + +template +struct Reader; + +template +struct Reader::type>: public ShallowCopyBuffer { + private: + using super = ShallowCopyBuffer; + using type = typename super::type; + + public: + Reader(const std::vector&, type& val, const DataType& /* file_datatype */) + : super(val) {} +}; + +template +struct Reader::type>: public DeepCopyBuffer { + private: + using super = DeepCopyBuffer; + using type = typename super::type; + + public: + Reader(const std::vector& _dims, type&, const DataType& /* file_datatype */) + : super(_dims) {} +}; + + +template +struct Reader::type>: public StringBuffer { + public: + explicit Reader(const std::vector& _dims, + const T& /* val */, + const DataType& _file_datatype) + : StringBuffer(_dims, _file_datatype) {} +}; + +struct data_converter { + template + static Writer serialize(const typename inspector::type& val, + const DataType& file_datatype) { + return Writer(val, file_datatype); + } + + template + static Reader get_reader(const std::vector& dims, + T& val, + const DataType& file_datatype) { + // TODO Use bufferinfo for recursive_ndim + auto effective_dims = details::squeezeDimensions(dims, inspector::recursive_ndim); + inspector::prepare(val, effective_dims); + return Reader(effective_dims, val, file_datatype); + } +}; + +} // namespace details +} // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5DataSet_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5DataSet_misc.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5DataSet_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5DataSet_misc.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5DataType_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5DataType_misc.hpp similarity index 76% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5DataType_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5DataType_misc.hpp index afe200c800..8535d617ab 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5DataType_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5DataType_misc.hpp @@ -22,10 +22,68 @@ #include #endif -#include "H5Converter_misc.hpp" +#include "H5Inspector_misc.hpp" namespace HighFive { +namespace detail { + +inline hid_t h5t_copy(hid_t original) { + auto copy = H5Tcopy(original); + if (copy == H5I_INVALID_HID) { + HDF5ErrMapper::ToException("Error copying datatype."); + } + + return copy; +} + +inline hsize_t h5t_get_size(hid_t hid) { + hsize_t size = H5Tget_size(hid); + if (size == 0) { + HDF5ErrMapper::ToException("Error getting size of datatype."); + } + + return size; +} + +inline H5T_cset_t h5t_get_cset(hid_t hid) { + auto cset = H5Tget_cset(hid); + if (cset == H5T_CSET_ERROR) { + HDF5ErrMapper::ToException("Error getting cset of datatype."); + } + + return cset; +} + +inline H5T_str_t h5t_get_strpad(hid_t hid) { + auto strpad = H5Tget_strpad(hid); + if (strpad == H5T_STR_ERROR) { + HDF5ErrMapper::ToException("Error getting strpad of datatype."); + } + + return strpad; +} + +inline void h5t_set_size(hid_t hid, hsize_t size) { + if (H5Tset_size(hid, size) < 0) { + HDF5ErrMapper::ToException("Error setting size of datatype."); + } +} + +inline void h5t_set_cset(hid_t hid, H5T_cset_t cset) { + if (H5Tset_cset(hid, cset) < 0) { + HDF5ErrMapper::ToException("Error setting cset of datatype."); + } +} + +inline void h5t_set_strpad(hid_t hid, H5T_str_t strpad) { + if (H5Tset_strpad(hid, strpad) < 0) { + HDF5ErrMapper::ToException("Error setting strpad of datatype."); + } +} +} // namespace detail + + namespace { // unnamed inline DataTypeClass convert_type_class(const H5T_class_t& tclass); inline std::string type_class_string(DataTypeClass); @@ -41,7 +99,7 @@ inline DataTypeClass DataType::getClass() const { } inline size_t DataType::getSize() const { - return H5Tget_size(_hid); + return detail::h5t_get_size(_hid); } inline bool DataType::operator==(const DataType& other) const { @@ -68,68 +126,110 @@ inline bool DataType::isReference() const { return H5Tequal(_hid, H5T_STD_REF_OBJ) > 0; } +inline StringType DataType::asStringType() const { + if (getClass() != DataTypeClass::String) { + throw DataTypeException("Invalid conversion to StringType."); + } + + if (isValid() && H5Iinc_ref(_hid) < 0) { + throw ObjectException("Reference counter increase failure"); + } + + return StringType(_hid); +} + inline std::string DataType::string() const { return type_class_string(getClass()) + std::to_string(getSize() * 8); } +inline StringPadding StringType::getPadding() const { + return StringPadding(detail::h5t_get_strpad(_hid)); +} + +inline CharacterSet StringType::getCharacterSet() const { + return CharacterSet(detail::h5t_get_cset(_hid)); +} + +inline FixedLengthStringType::FixedLengthStringType(size_t size, + StringPadding padding, + CharacterSet character_set) { + if (size == 0 && padding == StringPadding::NullTerminated) { + throw DataTypeException( + "Fixed-length, null-terminated need at least one byte to store the null-character."); + } + + _hid = detail::h5t_copy(H5T_C_S1); + + detail::h5t_set_size(_hid, hsize_t(size)); + detail::h5t_set_cset(_hid, H5T_cset_t(character_set)); + detail::h5t_set_strpad(_hid, H5T_str_t(padding)); +} + +inline VariableLengthStringType::VariableLengthStringType(CharacterSet character_set) { + _hid = detail::h5t_copy(H5T_C_S1); + + detail::h5t_set_size(_hid, H5T_VARIABLE); + detail::h5t_set_cset(_hid, H5T_cset_t(character_set)); +} + // char mapping template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_CHAR); + _hid = detail::h5t_copy(H5T_NATIVE_CHAR); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_SCHAR); + _hid = detail::h5t_copy(H5T_NATIVE_SCHAR); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_UCHAR); + _hid = detail::h5t_copy(H5T_NATIVE_UCHAR); } // short mapping template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_SHORT); + _hid = detail::h5t_copy(H5T_NATIVE_SHORT); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_USHORT); + _hid = detail::h5t_copy(H5T_NATIVE_USHORT); } // integer mapping template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_INT); + _hid = detail::h5t_copy(H5T_NATIVE_INT); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_UINT); + _hid = detail::h5t_copy(H5T_NATIVE_UINT); } // long mapping template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_LONG); + _hid = detail::h5t_copy(H5T_NATIVE_LONG); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_ULONG); + _hid = detail::h5t_copy(H5T_NATIVE_ULONG); } // long long mapping template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_LLONG); + _hid = detail::h5t_copy(H5T_NATIVE_LLONG); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_ULLONG); + _hid = detail::h5t_copy(H5T_NATIVE_ULLONG); } // half-float, float, double and long double mapping @@ -138,11 +238,11 @@ using float16_t = half_float::half; template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_FLOAT); + _hid = detail::h5t_copy(H5T_NATIVE_FLOAT); // Sign position, exponent position, exponent size, mantissa position, mantissa size H5Tset_fields(_hid, 15, 10, 5, 0, 10); // Total datatype size (in bytes) - H5Tset_size(_hid, 2); + detail::h5t_set_size(_hid, 2); // Floating point exponent bias H5Tset_ebias(_hid, 15); } @@ -150,17 +250,17 @@ inline AtomicType::AtomicType() { template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_FLOAT); + _hid = detail::h5t_copy(H5T_NATIVE_FLOAT); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_DOUBLE); + _hid = detail::h5t_copy(H5T_NATIVE_DOUBLE); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_LDOUBLE); + _hid = detail::h5t_copy(H5T_NATIVE_LDOUBLE); } // std string @@ -173,7 +273,7 @@ inline AtomicType::AtomicType() { // std byte template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_B8); + _hid = detail::h5t_copy(H5T_NATIVE_B8); } #endif @@ -200,8 +300,8 @@ class AtomicType>: public DataType { : DataType( CompoundType({{"r", create_datatype(), 0}, {"i", create_datatype(), sizeof(T)}}, sizeof(std::complex))) { - static_assert(std::is_floating_point::value, - "std::complex accepts only floating point numbers."); + static_assert(std::is_arithmetic::value, + "std::complex accepts only floating point and integral numbers."); } }; @@ -230,18 +330,15 @@ inline FixedLenStringArray::FixedLenStringArray(const char array[][N], std::s template inline FixedLenStringArray::FixedLenStringArray(const std::string* iter_begin, const std::string* iter_end) { - datavec.resize(static_cast(iter_end - iter_begin)); - for (auto& dst_array: datavec) { - const char* src = (iter_begin++)->c_str(); - const size_t length = std::min(N - 1, std::strlen(src)); - std::memcpy(dst_array.data(), src, length); - dst_array[length] = 0; + datavec.reserve(static_cast(iter_end - iter_begin)); + for (std::string const* it = iter_begin; it != iter_end; ++it) { + push_back(*it); } } template inline FixedLenStringArray::FixedLenStringArray(const std::vector& vec) - : FixedLenStringArray(&vec.front(), &vec.back()) {} + : FixedLenStringArray(vec.data(), vec.data() + vec.size()) {} template inline FixedLenStringArray::FixedLenStringArray( @@ -271,7 +368,7 @@ inline std::string FixedLenStringArray::getString(std::size_t i) const { // Reference mapping template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_STD_REF_OBJ); + _hid = detail::h5t_copy(H5T_STD_REF_OBJ); } inline size_t find_first_atomic_member_size(hid_t hid) { @@ -294,7 +391,7 @@ inline size_t find_first_atomic_member_size(hid_t hid) { } else if (H5Tget_class(hid) == H5T_STRING) { return 1; } - return H5Tget_size(hid); + return detail::h5t_get_size(hid); } // Calculate the padding required to align an element of a struct @@ -325,7 +422,7 @@ inline void CompoundType::create(size_t size) { // Do a first pass to find the total size of the compound datatype for (auto& member: members) { - size_t member_size = H5Tget_size(member.base_type.getId()); + size_t member_size = detail::h5t_get_size(member.base_type.getId()); if (member_size == 0) { throw DataTypeException("Cannot get size of DataType with hid: " + @@ -393,12 +490,9 @@ inline void EnumType::commit(const Object& object, const std::string& name) c namespace { inline hid_t create_string(size_t length) { - hid_t _hid = H5Tcopy(H5T_C_S1); - if (H5Tset_size(_hid, length) < 0) { - HDF5ErrMapper::ToException("Unable to define datatype size to variable"); - } - // define encoding to UTF-8 by default - H5Tset_cset(_hid, H5T_CSET_UTF8); + hid_t _hid = detail::h5t_copy(H5T_C_S1); + detail::h5t_set_size(_hid, length); + detail::h5t_set_cset(_hid, H5T_CSET_UTF8); return _hid; } diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Dataspace_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Dataspace_misc.hpp similarity index 92% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Dataspace_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Dataspace_misc.hpp index a72054ad29..0fdcacefdb 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Dataspace_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Dataspace_misc.hpp @@ -62,9 +62,9 @@ inline DataSpace::DataSpace(const std::vector& dims, const std::vector DataSpace::getDimensions() const { } inline size_t DataSpace::getElementCount() const { - const std::vector& dims = getDimensions(); - return std::accumulate(dims.begin(), dims.end(), size_t{1u}, std::multiplies()); + hssize_t nelements = H5Sget_simple_extent_npoints(_hid); + if (nelements < 0) { + HDF5ErrMapper::ToException( + "Unable to get number of elements in dataspace"); + } + + return static_cast(nelements); } inline std::vector DataSpace::getMaxDimensions() const { diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Exception_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Exception_misc.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Exception_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Exception_misc.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5FileDriver_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5FileDriver_misc.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5FileDriver_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5FileDriver_misc.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5File_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5File_misc.hpp similarity index 87% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5File_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5File_misc.hpp index a63338b82b..b90792a712 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5File_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5File_misc.hpp @@ -127,4 +127,22 @@ inline void File::flush() { } } +inline size_t File::getFileSize() const { + hsize_t sizeValue = 0; + if (H5Fget_filesize(_hid, &sizeValue) < 0) { + HDF5ErrMapper::ToException( + std::string("Unable to retrieve size of file " + getName())); + } + return static_cast(sizeValue); +} + +inline size_t File::getFreeSpace() const { + hssize_t unusedSize = H5Fget_freespace(_hid); + if (unusedSize < 0) { + HDF5ErrMapper::ToException( + std::string("Unable to retrieve unused space of file " + getName())); + } + return static_cast(unusedSize); +} + } // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Friends.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Friends.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Friends.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Friends.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Converter_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Inspector_misc.hpp similarity index 88% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Converter_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Inspector_misc.hpp index a46f01174b..05ed6bc3ec 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Converter_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Inspector_misc.hpp @@ -6,13 +6,21 @@ * http://www.boost.org/LICENSE_1_0.txt) * */ + #pragma once #include #include +#include +#include +#include +#include #include #include "../H5Reference.hpp" + +#include "string_padding.hpp" + #ifdef H5_USE_BOOST #include // starting Boost 1.64, serialization header must come before ublas @@ -23,7 +31,9 @@ #include #endif + namespace HighFive { + namespace details { inline bool checkDimensions(const std::vector& dims, size_t n_dim_requested) { @@ -255,14 +265,15 @@ struct inspector: type_helper { throw DataSpaceException("A std::string cannot be written directly."); } - static void serialize(const type& val, hdf5_type* m) { - *m = val.c_str(); + template + static void serialize(const type& val, It m) { + (*m).assign(val.data(), val.size(), StringPadding::NullTerminated); } - static void unserialize(const hdf5_type* vec, - const std::vector& /* dims */, - type& val) { - val = vec[0]; + template + static void unserialize(const It& vec, const std::vector& /* dims */, type& val) { + const auto& view = *vec; + val.assign(view.data(), view.length()); } }; @@ -365,7 +376,10 @@ struct inspector> { sizes[0] = val.size(); if (!val.empty()) { auto s = inspector::getDimensions(val[0]); - std::copy(s.begin(), s.end(), sizes.begin() + 1); + assert(s.size() + ndim == sizes.size()); + for (size_t i = 0; i < s.size(); ++i) { + sizes[i + ndim] = s[i]; + } } return sizes; } @@ -394,7 +408,8 @@ struct inspector> { return inspector::data(val[0]); } - static void serialize(const type& val, hdf5_type* m) { + template + static void serialize(const type& val, It m) { size_t subsize = inspector::getSizeVal(val[0]); for (auto&& e: val) { inspector::serialize(e, m); @@ -402,9 +417,8 @@ struct inspector> { } } - static void unserialize(const hdf5_type* vec_align, - const std::vector& dims, - type& val) { + template + static void unserialize(const It& vec_align, const std::vector& dims, type& val) { std::vector next_dims(dims.begin() + 1, dims.end()); size_t next_size = compute_total_size(next_dims); for (size_t i = 0; i < dims[0]; ++i) { @@ -480,6 +494,7 @@ struct inspector> { static constexpr size_t ndim = 1; static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && + sizeof(type) == N * sizeof(T) && inspector::is_trivially_copyable; static std::vector getDimensions(const type& val) { @@ -515,7 +530,8 @@ struct inspector> { return inspector::data(val[0]); } - static void serialize(const type& val, hdf5_type* m) { + template + static void serialize(const type& val, It m) { size_t subsize = inspector::getSizeVal(val[0]); for (auto& e: val) { inspector::serialize(e, m); @@ -523,9 +539,8 @@ struct inspector> { } } - static void unserialize(const hdf5_type* vec_align, - const std::vector& dims, - type& val) { + template + static void unserialize(const It& vec_align, const std::vector& dims, type& val) { if (dims[0] != N) { std::ostringstream os; os << "Impossible to pair DataSet with " << dims[0] << " elements into an array with " @@ -625,7 +640,18 @@ struct inspector> { static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && inspector::is_trivially_copyable; + + static void assert_not_buggy(Eigen::Index nrows, Eigen::Index ncols) { + if (nrows > 1 && ncols > 1) { + throw std::runtime_error( + "HighFive has been broken for Eigen::Matrix. Please check " + "https://github.com/BlueBrain/HighFive/issues/532."); + } + } + static std::vector getDimensions(const type& val) { + assert_not_buggy(val.rows(), val.cols()); + std::vector sizes{static_cast(val.rows()), static_cast(val.cols())}; auto s = inspector::getDimensions(val.data()[0]); sizes.insert(sizes.end(), s.begin(), s.end()); @@ -646,23 +672,29 @@ struct inspector> { val.resize(static_cast(dims[0]), static_cast(dims[1])); } + + assert_not_buggy(val.rows(), val.cols()); } static hdf5_type* data(type& val) { + assert_not_buggy(val.rows(), val.cols()); return inspector::data(*val.data()); } static const hdf5_type* data(const type& val) { + assert_not_buggy(val.rows(), val.cols()); return inspector::data(*val.data()); } static void serialize(const type& val, hdf5_type* m) { + assert_not_buggy(val.rows(), val.cols()); std::memcpy(m, val.data(), static_cast(val.size()) * sizeof(hdf5_type)); } static void unserialize(const hdf5_type* vec_align, const std::vector& dims, type& val) { + assert_not_buggy(val.rows(), val.cols()); if (dims.size() < 2) { std::ostringstream os; os << "Impossible to pair DataSet with " << dims.size() @@ -733,7 +765,8 @@ struct inspector> { return inspector::data(*val.data()); } - static void serialize(const type& val, hdf5_type* m) { + template + static void serialize(const type& val, It m) { size_t size = val.num_elements(); size_t subsize = inspector::getSizeVal(*val.origin()); for (size_t i = 0; i < size; ++i) { @@ -741,9 +774,8 @@ struct inspector> { } } - static void unserialize(const hdf5_type* vec_align, - const std::vector& dims, - type& val) { + template + static void unserialize(It vec_align, const std::vector& dims, type& val) { std::vector next_dims(dims.begin() + ndim, dims.end()); size_t subsize = compute_total_size(next_dims); for (size_t i = 0; i < val.num_elements(); ++i) { @@ -822,88 +854,5 @@ struct inspector> { }; #endif -template -struct Writer { - using hdf5_type = typename inspector::hdf5_type; - const hdf5_type* get_pointer() { - if (vec.empty()) { - return ptr; - } else { - return vec.data(); - } - } - std::vector vec{}; - const hdf5_type* ptr{nullptr}; -}; - -template -struct Reader { - using type = unqualified_t; - using hdf5_type = typename inspector::hdf5_type; - - Reader(const std::vector& _dims, type& _val) - : dims(_dims) - , val(_val) {} - - hdf5_type* get_pointer() { - if (vec.empty()) { - return inspector::data(val); - } else { - return vec.data(); - } - } - - void unserialize() { - if (!vec.empty()) { - inspector::unserialize(vec.data(), dims, val); - } - } - - std::vector dims{}; - std::vector vec{}; - type& val{}; -}; - -struct data_converter { - template - static typename std::enable_if::is_trivially_copyable, Writer>::type serialize( - const typename inspector::type& val) { - Writer w; - w.ptr = inspector::data(val); - return w; - } - - template - static typename std::enable_if::is_trivially_copyable, Writer>::type serialize( - const typename inspector::type& val) { - Writer w; - w.vec.resize(inspector::getSizeVal(val)); - inspector::serialize(val, w.vec.data()); - return w; - } - - template - static - typename std::enable_if>::is_trivially_copyable, Reader>::type - get_reader(const std::vector& dims, T& val) { - auto effective_dims = details::squeezeDimensions(dims, inspector::recursive_ndim); - Reader r(effective_dims, val); - inspector::prepare(r.val, effective_dims); - return r; - } - - template - static typename std::enable_if>::is_trivially_copyable, - Reader>::type - get_reader(const std::vector& dims, T& val) { - auto effective_dims = details::squeezeDimensions(dims, inspector::recursive_ndim); - - Reader r(effective_dims, val); - inspector::prepare(r.val, effective_dims); - r.vec.resize(inspector::getSize(effective_dims)); - return r; - } -}; - } // namespace details } // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Iterables_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Iterables_misc.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Iterables_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Iterables_misc.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Node_traits.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Node_traits.hpp similarity index 90% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Node_traits.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Node_traits.hpp index bdd70332e8..d53d3f0488 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Node_traits.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Node_traits.hpp @@ -129,6 +129,14 @@ class NodeTraits { /// \return the group object Group getGroup(const std::string& group_name) const; + /// + /// \brief open a commited datatype with the name type_name + /// \param type_name + /// \return the datatype object + DataType getDataType( + const std::string& type_name, + const DataTypeAccessProps& accessProps = DataTypeAccessProps::Default()) const; + /// /// \brief return the number of leaf objects of the node / group /// \return number of leaf objects @@ -208,6 +216,20 @@ class NodeTraits { const LinkAccessProps& linkAccessProps = LinkAccessProps(), const bool parents = true); + /// + /// \brief Creates hardlinks + /// \param link_name The name of the link + /// \param target_obj The target object + /// \param linkCreateProps A Link_Create property list. Notice "parents=true" overrides + /// \param linkAccessProps The Link_Access property list + /// \param parents Whether parent groups should be created: Default: true + template + void createHardLink(const std::string& link_name, + const T& target_obj, + LinkCreateProps linkCreateProps = LinkCreateProps(), + const LinkAccessProps& linkAccessProps = LinkAccessProps(), + const bool parents = true); + private: using derivate_type = Derivate; diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Node_traits_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Node_traits_misc.hpp similarity index 90% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Node_traits_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Node_traits_misc.hpp index 0af1281755..2f75ff3111 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Node_traits_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Node_traits_misc.hpp @@ -174,6 +174,19 @@ inline Group NodeTraits::getGroup(const std::string& group_name) const return detail::make_group(hid); } +template +inline DataType NodeTraits::getDataType(const std::string& type_name, + const DataTypeAccessProps& accessProps) const { + const auto hid = H5Topen2(static_cast(this)->getId(), + type_name.c_str(), + accessProps.getId()); + if (hid < 0) { + HDF5ErrMapper::ToException( + std::string("Unable to open the datatype \"") + type_name + "\":"); + } + return DataType(hid); +} + template inline size_t NodeTraits::getNumberObjects() const { hsize_t res; @@ -358,6 +371,29 @@ inline void NodeTraits::createExternalLink(const std::string& link_nam } } +template +template +inline void NodeTraits::createHardLink(const std::string& link_name, + const T& target_obj, + LinkCreateProps linkCreateProps, + const LinkAccessProps& linkAccessProps, + const bool parents) { + static_assert(!std::is_same::value, + "hdf5 doesn't support hard links to Attributes"); + if (parents) { + linkCreateProps.add(CreateIntermediateGroup{}); + } + auto status = H5Lcreate_hard(target_obj.getId(), + ".", + static_cast(this)->getId(), + link_name.c_str(), + linkCreateProps.getId(), + linkAccessProps.getId()); + if (status < 0) { + HDF5ErrMapper::ToException(std::string("Unable to create hard link: ")); + } +} + template inline Object NodeTraits::_open(const std::string& node_name, diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Object_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Object_misc.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Object_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Object_misc.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Path_traits.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Path_traits.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Path_traits.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Path_traits.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Path_traits_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Path_traits_misc.hpp similarity index 95% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Path_traits_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Path_traits_misc.hpp index 73704f03af..444e9294bf 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Path_traits_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Path_traits_misc.hpp @@ -34,7 +34,7 @@ inline PathTraits::PathTraits() { template inline std::string PathTraits::getPath() const { return details::get_name([this](char* buffer, size_t length) { - return H5Iget_name(static_cast(this)->getId(), buffer, length); + return H5Iget_name(static_cast(*this).getId(), buffer, length); }); } diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5PropertyList_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5PropertyList_misc.hpp similarity index 95% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5PropertyList_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5PropertyList_misc.hpp index 6a4ef9efd9..cef301e53a 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5PropertyList_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5PropertyList_misc.hpp @@ -70,7 +70,7 @@ inline void PropertyList::_initializeIfNeeded() { } template -template +template inline void PropertyList::add(const P& property) { _initializeIfNeeded(); property.apply(_hid); @@ -543,4 +543,32 @@ inline void LinkCreationOrder::fromPropertyList(hid_t hid) { "Error getting property for link creation order"); } } + +inline AttributePhaseChange::AttributePhaseChange(unsigned max_compact, unsigned min_dense) + : _max_compact(max_compact) + , _min_dense(min_dense) {} + +inline AttributePhaseChange::AttributePhaseChange(const GroupCreateProps& gcpl) { + if (H5Pget_attr_phase_change(gcpl.getId(), &_max_compact, &_min_dense) < 0) { + HDF5ErrMapper::ToException( + "Error getting property for attribute phase change"); + } +} + +inline unsigned AttributePhaseChange::max_compact() const { + return _max_compact; +} + +inline unsigned AttributePhaseChange::min_dense() const { + return _min_dense; +} + +inline void AttributePhaseChange::apply(hid_t hid) const { + if (H5Pset_attr_phase_change(hid, _max_compact, _min_dense) < 0) { + HDF5ErrMapper::ToException( + "Error getting property for attribute phase change"); + } +} + + } // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5ReadWrite_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5ReadWrite_misc.hpp similarity index 72% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5ReadWrite_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5ReadWrite_misc.hpp index 491e613896..c8e7361740 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5ReadWrite_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5ReadWrite_misc.hpp @@ -19,9 +19,13 @@ template using unqualified_t = typename std::remove_const::type>::type; // Find the type of an eventual char array, otherwise void -template +template struct type_char_array { - using type = void; + using type = typename std::conditional< + std::is_same::base_type, std::string>::value, + std::string, + void>::type; + static constexpr bool is_char_array = false; }; template @@ -29,6 +33,7 @@ struct type_char_array { using type = typename std::conditional, char>::value, char*, typename type_char_array::type>::type; + static constexpr bool is_char_array = true; }; template @@ -36,6 +41,7 @@ struct type_char_array { using type = typename std::conditional, char>::value, char[N], typename type_char_array::type>::type; + static constexpr bool is_char_array = true; }; template @@ -43,7 +49,7 @@ struct BufferInfo { using type_no_const = typename std::remove_const::type; using elem_type = typename details::inspector::base_type; using char_array_t = typename details::type_char_array::type; - static constexpr bool is_char_array = !std::is_same::value; + static constexpr bool is_char_array = details::type_char_array::is_char_array; enum Operation { read, write }; const Operation op; @@ -63,29 +69,44 @@ struct string_type_checker { static DataType getDataType(const DataType&, const DataType&); }; +inline void enforce_ascii_hack(const DataType& dst, const DataType& src) { + // Note: constness only refers to constness of the DataType object, which + // is just an ID, we can/will change properties of `dst`. + + // TEMP. CHANGE: Ensure that the character set is properly configured to prevent + // converter issues on HDF5 <=v1.12.0 when loading ASCII strings first. + // See https://github.com/HDFGroup/hdf5/issues/544 for further information. + if (H5Tget_cset(src.getId()) == H5T_CSET_ASCII) { + H5Tset_cset(dst.getId(), H5T_CSET_ASCII); + } +} + template <> struct string_type_checker { inline static DataType getDataType(const DataType& element_type, const DataType& dtype) { - // TEMP. CHANGE: Ensure that the character set is properly configured to prevent - // converter issues on HDF5 <=v1.12.0 when loading ASCII strings first. - // See https://github.com/HDFGroup/hdf5/issues/544 for further information. - if (H5Tget_class(element_type.getId()) == H5T_STRING && - H5Tget_cset(dtype.getId()) == H5T_CSET_ASCII) { - H5Tset_cset(element_type.getId(), H5T_CSET_ASCII); + if (H5Tget_class(element_type.getId()) == H5T_STRING) { + enforce_ascii_hack(element_type, dtype); } return element_type; } }; +template <> +struct string_type_checker { + inline static DataType getDataType(const DataType&, const DataType& file_datatype) { + // The StringBuffer ensures that the data is transformed such that it + // matches the datatype of the dataset, i.e. `file_datatype` and + // `mem_datatype` are the same. + return file_datatype; + } +}; + template struct string_type_checker { inline static DataType getDataType(const DataType& element_type, const DataType& dtype) { DataType return_type = (dtype.isFixedLenStr()) ? AtomicType() : element_type; - // TEMP. CHANGE: See string_type_checker definition - if (H5Tget_cset(dtype.getId()) == H5T_CSET_ASCII) { - H5Tset_cset(return_type.getId(), H5T_CSET_ASCII); - } + enforce_ascii_hack(return_type, dtype); return return_type; } }; @@ -93,13 +114,11 @@ struct string_type_checker { template <> struct string_type_checker { inline static DataType getDataType(const DataType&, const DataType& dtype) { - if (dtype.isFixedLenStr()) + if (dtype.isFixedLenStr()) { throw DataSetException("Can't output variable-length to fixed-length strings"); - // TEMP. CHANGE: See string_type_checker definition - DataType return_type = AtomicType(); - if (H5Tget_cset(dtype.getId()) == H5T_CSET_ASCII) { - H5Tset_cset(return_type.getId(), H5T_CSET_ASCII); } + DataType return_type = AtomicType(); + enforce_ascii_hack(return_type, dtype); return return_type; } }; @@ -114,11 +133,6 @@ BufferInfo::BufferInfo(const DataType& dtype, F getName, Operation _op) ((is_fixed_len_string && is_char_array) ? 1 : 0)) , data_type( string_type_checker::getDataType(create_datatype(), dtype)) { - if (is_fixed_len_string && std::is_same::value) { - throw DataSetException( - "Can't output std::string as fixed-length. " - "Use raw arrays or FixedLenStringArray"); - } // We warn. In case they are really not convertible an exception will rise on read/write if (dtype.getClass() != data_type.getClass()) { HIGHFIVE_LOG_WARN(getName() + "\": data and hdf5 dataset have different types: " + diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Reference_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Reference_misc.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Reference_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Reference_misc.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Selection_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Selection_misc.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Selection_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Selection_misc.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Slice_traits.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Slice_traits.hpp similarity index 85% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Slice_traits.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Slice_traits.hpp index 719b4e5036..52c52713f0 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Slice_traits.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Slice_traits.hpp @@ -258,6 +258,15 @@ class SliceTraits { /// Therefore, the only memspaces supported for general hyperslabs are one-dimensional arrays. Selection select(const HyperSlab& hyperslab) const; + /// + /// \brief Select an \p hyperslab in the current Slice/Dataset. + /// + /// If the selection can be read into a simple, multi-dimensional dataspace, + /// then this overload enable specifying the shape of the memory dataspace + /// with `memspace`. Note, that simple implies no offsets, strides or + /// number of blocks, just the size of the block in each dimension. + Selection select(const HyperSlab& hyperslab, const DataSpace& memspace) const; + /// /// \brief Select a region in the current Slice/Dataset of \p count points at /// \p offset separated by \p stride. If strides are not provided they will @@ -308,9 +317,21 @@ class SliceTraits { /// \param xfer_props: Data Transfer properties template void read(T* array, - const DataType& dtype = DataType(), + const DataType& dtype, const DataTransferProps& xfer_props = DataTransferProps()) const; + /// + /// Read the entire dataset into a raw buffer + /// + /// Same as `read(T*, const DataType&, const DataTransferProps&)`. However, + /// this overload deduces the HDF5 datatype of the element of `array` from + /// `T`. Note, that the file datatype is already fixed. + /// + /// \param array: A buffer containing enough space for the data + /// \param xfer_props: Data Transfer properties + template + void read(T* array, const DataTransferProps& xfer_props = DataTransferProps()) const; + /// /// Write the integrality N-dimension buffer to this dataset /// An exception is raised is if the numbers of dimension of the buffer and @@ -322,22 +343,33 @@ class SliceTraits { void write(const T& buffer, const DataTransferProps& xfer_props = DataTransferProps()); /// - /// Write from a raw buffer into this dataset + /// Write from a raw pointer into this dataset. /// /// No dimensionality checks will be performed, it is the user's /// responsibility to ensure that the buffer holds the right amount of /// elements. For n-dimensional matrices the buffer layout follows H5 /// default conventions. + /// + /// Note, this is the shallowest wrapper around `H5Dwrite` and should + /// be used if full control is needed. Generally prefer `write`. + /// /// \param buffer: A buffer containing the data to be written - /// \param dtype: The type of the data, in case it cannot be automatically guessed + /// \param dtype: The datatype of `buffer`, i.e. the memory data type. /// \param xfer_props: The HDF5 data transfer properties, e.g. collective MPI-IO. template void write_raw(const T* buffer, - const DataType& dtype = DataType(), + const DataType& mem_datatype, const DataTransferProps& xfer_props = DataTransferProps()); - protected: - inline Selection select_impl(const HyperSlab& hyperslab, const DataSpace& memspace) const; + /// + /// Write from a raw pointer into this dataset. + /// + /// Same as `write_raw(const T*, const DataTransferProps&)`. However, this + /// overload attempts to guess the data type of `buffer`, i.e. the memory + /// datatype. Note that the file datatype is already fixed. + /// + template + void write_raw(const T* buffer, const DataTransferProps& xfer_props = DataTransferProps()); }; } // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Slice_traits_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Slice_traits_misc.hpp similarity index 86% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Slice_traits_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Slice_traits_misc.hpp index faae237d8b..7b07c9abf9 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Slice_traits_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Slice_traits_misc.hpp @@ -64,8 +64,8 @@ inline ElementSet::ElementSet(const std::vector>& eleme } template -inline Selection SliceTraits::select_impl(const HyperSlab& hyperslab, - const DataSpace& memspace) const { +inline Selection SliceTraits::select(const HyperSlab& hyperslab, + const DataSpace& memspace) const { // Note: The current limitation are that memspace must describe a // packed memspace. // @@ -98,7 +98,7 @@ inline Selection SliceTraits::select(const std::vector& offset const std::vector& block) const { auto slab = HyperSlab(RegularHyperSlab(offset, count, stride, block)); auto memspace = DataSpace(count); - return select_impl(slab, memspace); + return select(slab, memspace); } template @@ -121,7 +121,7 @@ inline Selection SliceTraits::select(const std::vector& column std::vector memdims = dims; memdims.back() = columns.size(); - return select_impl(slab, DataSpace(memdims)); + return select(slab, DataSpace(memdims)); } template @@ -172,8 +172,10 @@ inline void SliceTraits::read(T& array, const DataTransferProps& xfer_ const auto& slice = static_cast(*this); const DataSpace& mem_space = slice.getMemSpace(); + auto file_datatype = slice.getDataType(); + const details::BufferInfo buffer_info( - slice.getDataType(), + file_datatype, [&slice]() -> std::string { return details::get_dataset(slice).getPath(); }, details::BufferInfo::Operation::read); @@ -193,19 +195,20 @@ inline void SliceTraits::read(T& array, const DataTransferProps& xfer_ return; } - auto r = details::data_converter::get_reader(dims, array); - read(r.get_pointer(), buffer_info.data_type, xfer_props); + auto r = details::data_converter::get_reader(dims, array, file_datatype); + read(r.getPointer(), buffer_info.data_type, xfer_props); // re-arrange results - r.unserialize(); - auto t = create_datatype::base_type>(); + r.unserialize(array); + + auto t = buffer_info.data_type; auto c = t.getClass(); if (c == DataTypeClass::VarLen || t.isVariableStr()) { #if H5_VERSION_GE(1, 12, 0) // This one have been created in 1.12.0 - (void) H5Treclaim(t.getId(), mem_space.getId(), xfer_props.getId(), r.get_pointer()); + (void) H5Treclaim(t.getId(), mem_space.getId(), xfer_props.getId(), r.getPointer()); #else // This one is deprecated since 1.12.0 - (void) H5Dvlen_reclaim(t.getId(), mem_space.getId(), xfer_props.getId(), r.get_pointer()); + (void) H5Dvlen_reclaim(t.getId(), mem_space.getId(), xfer_props.getId(), r.getPointer()); #endif } } @@ -214,16 +217,12 @@ inline void SliceTraits::read(T& array, const DataTransferProps& xfer_ template template inline void SliceTraits::read(T* array, - const DataType& dtype, + const DataType& mem_datatype, const DataTransferProps& xfer_props) const { static_assert(!std::is_const::value, "read() requires a non-const structure to read data into"); - const auto& slice = static_cast(*this); - using element_type = typename details::inspector::base_type; - // Auto-detect mem datatype if not provided - const DataType& mem_datatype = dtype.empty() ? create_and_check_datatype() - : dtype; + const auto& slice = static_cast(*this); if (H5Dread(details::get_dataset(slice).getId(), mem_datatype.getId(), @@ -235,6 +234,15 @@ inline void SliceTraits::read(T* array, } } +template +template +inline void SliceTraits::read(T* array, const DataTransferProps& xfer_props) const { + using element_type = typename details::inspector::base_type; + const DataType& mem_datatype = create_and_check_datatype(); + + read(array, mem_datatype, xfer_props); +} + template template @@ -246,8 +254,10 @@ inline void SliceTraits::write(const T& buffer, const DataTransferProp return; } + auto file_datatype = slice.getDataType(); + const details::BufferInfo buffer_info( - slice.getDataType(), + file_datatype, [&slice]() -> std::string { return details::get_dataset(slice).getPath(); }, details::BufferInfo::Operation::write); @@ -258,19 +268,17 @@ inline void SliceTraits::write(const T& buffer, const DataTransferProp << " into dataset with n = " << buffer_info.n_dimensions << " dimensions."; throw DataSpaceException(ss.str()); } - auto w = details::data_converter::serialize(buffer); - write_raw(w.get_pointer(), buffer_info.data_type, xfer_props); + auto w = details::data_converter::serialize(buffer, file_datatype); + write_raw(w.getPointer(), buffer_info.data_type, xfer_props); } template template inline void SliceTraits::write_raw(const T* buffer, - const DataType& dtype, + const DataType& mem_datatype, const DataTransferProps& xfer_props) { - using element_type = typename details::inspector::base_type; const auto& slice = static_cast(*this); - const auto& mem_datatype = dtype.empty() ? create_and_check_datatype() : dtype; if (H5Dwrite(details::get_dataset(slice).getId(), mem_datatype.getId(), @@ -282,5 +290,14 @@ inline void SliceTraits::write_raw(const T* buffer, } } +template +template +inline void SliceTraits::write_raw(const T* buffer, const DataTransferProps& xfer_props) { + using element_type = typename details::inspector::base_type; + const auto& mem_datatype = create_and_check_datatype(); + + write_raw(buffer, mem_datatype, xfer_props); +} + } // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Utils.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Utils.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5Utils.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5Utils.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5_definitions.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5_definitions.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/bits/H5_definitions.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/H5_definitions.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/string_padding.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/string_padding.hpp new file mode 100644 index 0000000000..e6e6908ddc --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/bits/string_padding.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace HighFive { + +enum class StringPadding : std::underlying_type::type { + NullTerminated = H5T_STR_NULLTERM, + NullPadded = H5T_STR_NULLPAD, + SpacePadded = H5T_STR_SPACEPAD +}; + + +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_Eigen.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_Eigen.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_Eigen.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_Eigen.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_misc.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_misc.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_misc.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_misc.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_opencv.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_opencv.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_opencv.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_opencv.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_public.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_public.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_public.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_public.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_scalar.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_scalar.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_scalar.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_scalar.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_vector.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_vector.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_vector.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_vector.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_xtensor.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_xtensor.hpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/include/highfive/h5easy_bits/H5Easy_xtensor.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/h5easy_bits/H5Easy_xtensor.hpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/highfive.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/highfive.hpp new file mode 100644 index 0000000000..f5e20cae91 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/include/highfive/highfive.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/README.md b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/README.md similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/README.md rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/README.md diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/hdf5_bench.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/hdf5_bench.cpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/hdf5_bench.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/hdf5_bench.cpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/hdf5_bench_improved.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/hdf5_bench_improved.cpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/hdf5_bench_improved.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/hdf5_bench_improved.cpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/highfive_bench.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/highfive_bench.cpp similarity index 90% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/highfive_bench.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/highfive_bench.cpp index e4e04dee8e..bcc7be93ce 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/highfive_bench.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/highfive_bench.cpp @@ -1,4 +1,4 @@ -#include +#include #include const std::vector> data(1000000, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/imgs/bench_hdf5_base.png b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/imgs/bench_hdf5_base.png similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/imgs/bench_hdf5_base.png rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/imgs/bench_hdf5_base.png diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/imgs/bench_hdf5_improved.png b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/imgs/bench_hdf5_improved.png similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/imgs/bench_hdf5_improved.png rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/imgs/bench_hdf5_improved.png diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/imgs/bench_highfive.png b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/imgs/bench_highfive.png similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/imgs/bench_highfive.png rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/imgs/bench_highfive.png diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/run_benchmark.sh b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/run_benchmark.sh similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/benchmarks/run_benchmark.sh rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/benchmarks/run_benchmark.sh diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/CMakeLists.txt b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/CMakeLists.txt similarity index 63% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/CMakeLists.txt rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/CMakeLists.txt index 97196cfa7e..8b5f8b0aff 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/CMakeLists.txt +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/CMakeLists.txt @@ -1,8 +1,8 @@ -include_directories(${HDF5_INCLUDE_DIRS}) +include(HighFiveWarnings) -function(compile_example exemple_source) +function(compile_example example_source) - get_filename_component(example_filename ${exemple_source} NAME) + get_filename_component(example_filename ${example_source} NAME) string(REPLACE ".cpp" "_bin" example_name ${example_filename}) if(${example_filename} MATCHES ".*eigen.*") @@ -31,15 +31,17 @@ function(compile_example exemple_source) if(${example_name} MATCHES ".*hl_hdf5.*") find_package(HDF5 QUIET COMPONENTS HL NAMES HDF5_HL) - if(${HDF5_FL_FOUND}) - add_executable(${example_name} ${exemple_source}) - target_link_libraries(${example_name} HighFive ${HDF5_HL_LIBRARIES}) + if(${HDF5_HL_FOUND}) + message("HDF5 HL: ${HDF5_HL_LIBRARIES}") + add_executable(${example_name} ${example_source}) + target_link_libraries(${example_name} HighFive HighFiveWarnings ${HDF5_HL_LIBRARIES}) endif() - else() - add_executable(${example_name} ${exemple_source}) - target_link_libraries(${example_name} HighFive) + return() endif() + add_executable(${example_name} ${example_source}) + target_link_libraries(${example_name} HighFive HighFiveWarnings) + endfunction() file(GLOB list_example "*.cpp") diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/boost_multi_array_2D.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/boost_multi_array_2D.cpp new file mode 100644 index 0000000000..4bec1ec129 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/boost_multi_array_2D.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c), 2017, Adrien Devresse + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include + +#undef H5_USE_BOOST +#define H5_USE_BOOST + +#include +#include + +using namespace HighFive; + +// Create a 2D dataset 10x3 of double with boost multi array +// and write it to a file. +int main(void) { + const int nx = 10; + const int ny = 3; + + boost::multi_array array(boost::extents[nx][ny]); + + for (int i = 0; i < nx; ++i) { + for (int j = 0; j < ny; ++j) { + array[i][j] = double(j + i * ny); + } + } + + // We create a new HDF5 file + File file("boost_multiarray_example.h5", File::Truncate); + + // let's create our dataset of the size of the boost::multi_array + file.createDataSet("dset", array); + + return 0; +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/boost_multiarray_complex.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/boost_multiarray_complex.cpp similarity index 88% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/boost_multiarray_complex.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/boost_multiarray_complex.cpp index a5c1985834..37481db625 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/boost_multiarray_complex.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/boost_multiarray_complex.cpp @@ -12,10 +12,9 @@ #undef H5_USE_BOOST #define H5_USE_BOOST +#include + #include -#include -#include -#include typedef std::complex complex_t; @@ -25,7 +24,7 @@ int main() { multi_array[1][1][0][0] = complex_t{1.1, 1.2}; HighFive::File file("multi_array_complex.h5", HighFive::File::Truncate); - HighFive::DataSet dataset = file.createDataSet("multi_array", multi_array); + return 0; } diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/boost_ublas_double.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/boost_ublas_double.cpp similarity index 97% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/boost_ublas_double.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/boost_ublas_double.cpp index ca3307ecfc..b025475b95 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/boost_ublas_double.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/boost_ublas_double.cpp @@ -11,13 +11,14 @@ #undef H5_USE_BOOST #define H5_USE_BOOST +#include + // In some versions of Boost (starting with 1.64), you have to include the serialization header // before ublas #include #include #include -#include using namespace HighFive; diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/compound_types.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/compound_types.cpp similarity index 73% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/compound_types.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/compound_types.cpp index 0f127f0a6f..158bc6dc6d 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/compound_types.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/compound_types.cpp @@ -10,7 +10,7 @@ // Compound datatype test :: May 2021 // ////////////////////////////////// -#include +#include typedef struct { @@ -27,17 +27,16 @@ HighFive::CompoundType create_compound_Size2D() { HIGHFIVE_REGISTER_TYPE(Size2D, create_compound_Size2D) int main() { - const std::string FILE_NAME("compounds_test.h5"); - const std::string DATASET_NAME("dims"); + const std::string dataset_name("dims"); - HighFive::File file(FILE_NAME, HighFive::File::Truncate); + HighFive::File file("compounds_test.h5", HighFive::File::Truncate); auto t1 = create_compound_Size2D(); t1.commit(file, "Size2D"); std::vector dims = {{1., 2.5}, {3., 4.5}}; - auto dataset = file.createDataSet(DATASET_NAME, dims); + auto dataset = file.createDataSet(dataset_name, dims); auto g1 = file.createGroup("group1"); - g1.createAttribute(DATASET_NAME, dims); + g1.createAttribute(dataset_name, dims); } diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_attribute_string_integer.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_attribute_string_integer.cpp new file mode 100644 index 0000000000..f658457adf --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_attribute_string_integer.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c), 2017, Adrien Devresse + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include +#include +#include + +#include + +using namespace HighFive; + +// create a dataset from a vector of string +// read it back and print it +int main(void) { + // Create a new file using the default property lists. + File file("create_attribute.h5", File::Truncate); + + // Create a dummy dataset of one single integer + DataSet dataset = file.createDataSet("dset", DataSpace(1), create_datatype()); + + // Now let's add a attribute on this dataset + // This attribute will be named "note" + // and have the following content + std::string note = "Very important Dataset!"; + + // Write in one line of code: + dataset.createAttribute("note", note); + + // We also add a "version" attribute + // that will be an array 1x2 of integer + std::vector version{1, 0}; + + Attribute v = dataset.createAttribute("version", version); + + // We can also create attributes on the file: + file.createAttribute("file_version", 1); + + // and on groups in the file: + auto group = file.createGroup("group"); + group.createAttribute("secret", 123); + + // let's now list the keys of all attributes + std::vector all_attributes_keys = dataset.listAttributeNames(); + for (const auto& attr: all_attributes_keys) { + std::cout << "attribute: " << attr << std::endl; + } + + return 0; +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_dataset_double.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_dataset_double.cpp new file mode 100644 index 0000000000..d15fbdc54f --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_dataset_double.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c), 2017, Adrien Devresse + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include +#include +#include + +#include + +// Create a dataset name "dset" of double 4x6 +int main(void) { + using namespace HighFive; + + // Create a new file using the default property lists. Note that + // `File::Truncate` will, if present, truncate the file before opening + // it for reading and writing. + File file("create_dataset_example.h5", File::Truncate); + + // Define the size of our dataset: 2x6 + std::vector dims{2, 6}; + + // Create the dataset + DataSet dataset = file.createDataSet("dset", DataSpace(dims)); + + double data[2][6] = {{1.1, 2.2, 3.3, 4.4, 5.5, 6.6}, + {11.11, 12.12, 13.13, 14.14, 15.15, 16.16}}; + + // write it + dataset.write(data); + + + return 0; // successfully terminated +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_dataset_half_float.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_dataset_half_float.cpp new file mode 100644 index 0000000000..2b720cd187 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_dataset_half_float.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c), 2022, Blue Brain Project + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ + +#ifdef H5_USE_HALF_FLOAT + +#include +#include +#include + +#include + +const std::string FILE_NAME("create_dataset_half_float_example.h5"); +const std::string DATASET_NAME("dset"); + +// Create a dataset name "dset", size 4x6, and type float16_t (i.e., 16-bit half-precision +// floating-point format) +// +int main(void) { + using namespace HighFive; + + // Create a new file using the default property lists. + File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); + + // Define the size of our dataset: 4x6 + std::vector dims{4, 6}; + + // Create the dataset + DataSet dataset = file.createDataSet(DATASET_NAME, DataSpace(dims)); + + std::vector> data; + for (size_t i = 0; i < 4; ++i) { + data.emplace_back(); + for (size_t j = 0; j < 6; ++j) + data[i].emplace_back((i + 1) * (j + 1)); + } + + // write it + dataset.write(data); + + return 0; +} + +#endif diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_datatype.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_datatype.cpp new file mode 100644 index 0000000000..0b1a0fb520 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_datatype.cpp @@ -0,0 +1,101 @@ +#include + +#include + +using namespace HighFive; + +static const std::string FILE_NAME("create_datatype_example.h5"); +static const std::string DATASET_NAME("test_dataset"); + + +// Struct representation of custom type (type v below) +typedef struct { + char a; + short b; + unsigned long long c; +} csl; + +bool operator==(csl x, csl y) { + return x.a == y.a && x.b == y.b && x.c == y.c; +} + +bool operator!=(csl x, csl y) { + return !(x == y); +} + +// Tell HighFive how to create the HDF5 datatype for this base type by +// using the HIGHFIVE_REGISTER_TYPE macro +CompoundType create_compound_csl() { + return {{"u1", create_datatype()}, + {"u2", create_datatype()}, + {"u3", create_datatype()}}; +} +HIGHFIVE_REGISTER_TYPE(csl, create_compound_csl) + +int main(void) { + File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); + + // Create a simple compound type with automatic alignment of the + // members. For this the type alignment is trivial. + std::vector t_members( + {{"real", create_datatype()}, {"imag", create_datatype()}}); + CompoundType t(t_members); + t.commit(file, "new_type1"); + + // Create a complex nested datatype with manual alignment + CompoundType u({{"u1", t, 0}, {"u2", t, 9}, {"u3", create_datatype(), 20}}, 26); + u.commit(file, "new_type3"); + + // Create a more complex type with automatic alignment. For this the + // type alignment is more complex. + CompoundType v_aligned{{"u1", create_datatype()}, + {"u2", create_datatype()}, + {"u3", create_datatype()}}; + // introspect the compound type + std::cout << "v_aligned size: " << v_aligned.getSize(); + for (const auto& member: v_aligned.getMembers()) { + std::cout << " field " << member.name << " offset: " << member.offset << std::endl; + } + + v_aligned.commit(file, "new_type2_aligned"); + + // Create a more complex type with a fully packed alignment. The + // equivalent type is created with a standard struct alignment in the + // implementation of HighFive::create_datatype above + CompoundType v_packed({{"u1", create_datatype(), 0}, + {"u2", create_datatype(), 1}, + {"u3", create_datatype(), 3}}, + 11); + v_packed.commit(file, "new_type2_packed"); + + + // Initialise some data + std::vector data; + data.push_back({'f', 1, 4}); + data.push_back({'g', -4, 18}); + + // Write the data into the file in a fully packed form + DataSet dataset = file.createDataSet(DATASET_NAME, DataSpace(2), v_packed); + dataset.write(data); + + file.flush(); + + // Read a subset of the data back + std::vector result; + dataset.select({0}, {2}).read(result); + + for (size_t i = 0; i < data.size(); ++i) { + if (result[i] != data[i]) { + std::cout << "result[" << i << "]:" << std::endl; + std::cout << " " << result[i].a << std::endl; + std::cout << " " << result[i].b << std::endl; + std::cout << " " << result[i].c << std::endl; + std::cout << "data[" << i << "]:" << std::endl; + std::cout << " " << data[i].a << std::endl; + std::cout << " " << data[i].b << std::endl; + std::cout << " " << data[i].c << std::endl; + } + } + + return 0; +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_extensible_dataset.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_extensible_dataset.cpp new file mode 100644 index 0000000000..17153cd574 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_extensible_dataset.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c), 2017, Adrien Devresse + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include +#include +#include + +#include + +const std::string FILE_NAME("create_extensible_dataset_example.h5"); +const std::string DATASET_NAME("dset"); + +// Create a dataset name "dset" of double 4x6 +// +int main(void) { + using namespace HighFive; + + // Create a new file using the default property lists. + File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); + + // Create a dataspace with initial shape and max shape + DataSpace dataspace = DataSpace({4, 5}, {17, DataSpace::UNLIMITED}); + + // Use chunking + DataSetCreateProps props; + props.add(Chunking(std::vector{2, 2})); + + // Create the dataset + DataSet dataset = file.createDataSet(DATASET_NAME, dataspace, create_datatype(), props); + + // Write into the initial part of the dataset + double t1[3][1] = {{2.0}, {2.0}, {4.0}}; + dataset.select({0, 0}, {3, 1}).write(t1); + + // Resize the dataset to a larger size + dataset.resize({4, 6}); + + // Write into the new part of the dataset + double t2[1][3] = {{4.0, 8.0, 6.0}}; + dataset.select({3, 3}, {1, 3}).write(t2); + + // now we read it back + std::vector> result; + dataset.read(result); + + // we print it out and see: + // 2 0 0 0 0 0 + // 2 0 0 0 0 0 + // 4 0 0 0 0 0 + // 0 0 0 4 8 6 + for (auto row: result) { + for (auto col: row) + std::cout << " " << col; + std::cout << std::endl; + } + + return 0; +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_large_attribute.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_large_attribute.cpp new file mode 100644 index 0000000000..022d11d87b --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_large_attribute.cpp @@ -0,0 +1,19 @@ +#include +#include + +#include + +int main() { + std::vector large_attr(16000, 0.0); + + auto fapl = HighFive::FileAccessProps::Default(); + fapl.add(HighFive::FileVersionBounds(H5F_LIBVER_LATEST, H5F_LIBVER_LATEST)); + HighFive::File file("create_large_attribute.h5", HighFive::File::Truncate, fapl); + auto gcpl = HighFive::GroupCreateProps::Default(); + gcpl.add(HighFive::AttributePhaseChange(0, 0)); + + auto group = file.createGroup("grp", gcpl); + group.createAttribute("attr", large_attr); + + return 0; +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_page_allocated_files.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_page_allocated_files.cpp similarity index 53% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_page_allocated_files.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_page_allocated_files.cpp index df58f0d3a0..0b4d29dffb 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/create_page_allocated_files.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/create_page_allocated_files.cpp @@ -12,14 +12,11 @@ #include #include -#include +#include // This example requires HDF5 version 1.10.1 or newer. #if H5_VERSION_GE(1, 10, 1) -const std::string FILE_NAME("create_page_allocated_files.h5"); -const std::string DATASET_NAME("array"); - // This example show how to create an HDF5 file that internally aggregates // metadata and raw data into separate pages. The advantage of this approach // is that reading a single page, pulls in the metadata for a large chunk of @@ -40,34 +37,29 @@ const std::string DATASET_NAME("array"); int main() { using namespace HighFive; - try { - // Create a new file requesting paged allocation. - auto create_props = FileCreateProps{}; - // Let request pagesizes of 16 kB. This setting should be tuned - // in real applications. We'll allow HDF5 to not keep track of - // left-over free space of size less than 128 bytes. Finally, - // we don't need the free space manager to be stored in the - // HDF5 file. - size_t pagesize = 16 * 1024; // Must be tuned. - size_t threshold = 128; - size_t persist = false; + // Create a new file requesting paged allocation. + auto create_props = FileCreateProps{}; - create_props.add(FileSpaceStrategy(H5F_FSPACE_STRATEGY_PAGE, persist, threshold)); - create_props.add(FileSpacePageSize(pagesize)); + // Let request pagesizes of 16 kB. This setting should be tuned + // in real applications. We'll allow HDF5 to not keep track of + // left-over free space of size less than 128 bytes. Finally, + // we don't need the free space manager to be stored in the + // HDF5 file. + size_t pagesize = 16 * 1024; // Must be tuned. + size_t threshold = 128; + size_t persist = false; - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate, create_props); + create_props.add(FileSpaceStrategy(H5F_FSPACE_STRATEGY_PAGE, persist, threshold)); + create_props.add(FileSpacePageSize(pagesize)); - // The `file` (and also the low-level `file.getId()`) behave as normal, i.e. - // one can proceed to add content to the file as usual. + File file("create_page_allocated_files.h5", File::Truncate, create_props); - auto data = std::vector{0.0, 1.0, 2.0}; - file.createDataSet(DATASET_NAME, data); + // The `file` (and also the low-level `file.getId()`) behave as normal, i.e. + // one can proceed to add content to the file as usual. - } catch (Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } + auto data = std::vector{0.0, 1.0, 2.0}; + file.createDataSet("data", data); return 0; } @@ -77,4 +69,4 @@ int main() { std::cout << "This example can't be run prior to HDF5 1.10.1.\n"; return 0; } -#endif \ No newline at end of file +#endif diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/easy_attribute.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/easy_attribute.cpp similarity index 99% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/easy_attribute.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/easy_attribute.cpp index be03c5378a..87a7f6592c 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/easy_attribute.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/easy_attribute.cpp @@ -34,6 +34,5 @@ int main() { H5Easy::dumpAttribute(file, "/path/to/measurement", "description", desc); H5Easy::dumpAttribute(file, "/path/to/measurement", "temperature", temperature); - return 0; } diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/easy_dumpoptions.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/easy_dumpoptions.cpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/easy_dumpoptions.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/easy_dumpoptions.cpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/easy_load_dump.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/easy_load_dump.cpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/easy_load_dump.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/easy_load_dump.cpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/hl_hdf5_inmemory_files.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/hl_hdf5_inmemory_files.cpp new file mode 100644 index 0000000000..088fd71cc8 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/hl_hdf5_inmemory_files.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c), 2022, Blue Brain Project + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include +#include + +#include + +#include + +using namespace HighFive; + +class InMemoryFile: public HighFive::File { + public: + explicit InMemoryFile(std::vector buffer) + : _buffer(std::move(buffer)) { + _hid = H5LTopen_file_image(_buffer.data(), + sizeof(_buffer[0]) * _buffer.size(), + H5LT_FILE_IMAGE_DONT_RELEASE | H5LT_FILE_IMAGE_DONT_COPY); + } + + private: + std::vector _buffer; +}; + + +// Create a 2D dataset 10x3 of double with eigen matrix +// and write it to a file +int main(void) { + const std::string file_name("inmemory_file.h5"); + const std::string dataset_name("dset"); + + auto data = std::vector{1.0, 2.0, 3.0}; + + { + // We create an HDF5 file. + File file(file_name, File::Truncate); + file.createDataSet(dataset_name, data); + } + + // Simulate having an inmemory file by reading a file + // byte-by-byte into RAM. + auto buffer = std::vector(1ul << 20); + auto file = std::fopen(file_name.c_str(), "r"); + auto nread = std::fread(buffer.data(), sizeof(buffer[0]), buffer.size(), file); + std::cout << "Bytes read: " << nread << "\n"; + + // Create a file from a buffer. + auto h5 = InMemoryFile(std::move(buffer)); + + // Read a dataset as usual. + auto read_back = h5.getDataSet(dataset_name).read>(); + + // Check if the values match. + for (size_t i = 0; i < read_back.size(); ++i) { + if (read_back[i] != data[i]) { + throw std::runtime_error("Values don't match."); + } else { + std::cout << "read_back[" << i << "] = " << read_back[i] << "\n"; + } + } + + return 0; +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/parallel_hdf5_collective_io.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/parallel_hdf5_collective_io.cpp similarity index 90% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/parallel_hdf5_collective_io.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/parallel_hdf5_collective_io.cpp index 6eeecb0ce5..7261b7cf1d 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/parallel_hdf5_collective_io.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/parallel_hdf5_collective_io.cpp @@ -13,13 +13,10 @@ #include -#include -#include -#include -#include +#include -const std::string FILE_NAME("parallel_collective_example.h5"); -const std::string DATASET_NAME("dset"); +const std::string file_name("parallel_collective_example.h5"); +const std::string dataset_name("dset"); // Currently, HighFive doesn't wrap retrieving information from property lists. // Therefore, one needs to use HDF5 directly. For example, to see if collective @@ -55,6 +52,7 @@ int main(int argc, char** argv) { MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); using namespace HighFive; + try { // MPI-IO requires informing HDF5 that we want something other than // the default behaviour. This is done through property lists. We @@ -70,7 +68,7 @@ int main(int argc, char** argv) { fapl.add(MPIOCollectiveMetadata{}); // Now we can create the file as usual. - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate, fapl); + File file(file_name, File::Truncate, fapl); // We can create a group as usual, but all MPI ranks must participate. auto group = file.createGroup("grp"); @@ -81,7 +79,7 @@ int main(int argc, char** argv) { dims[1] = 2ul; // We follow the path for - DataSet dataset = group.createDataSet(DATASET_NAME, DataSpace(dims)); + DataSet dataset = group.createDataSet(dataset_name, DataSpace(dims)); // Each node want to write its own rank two time in // its associated row diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/parallel_hdf5_independent_io.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/parallel_hdf5_independent_io.cpp similarity index 90% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/parallel_hdf5_independent_io.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/parallel_hdf5_independent_io.cpp index 5c5537f3a2..b43012890c 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/parallel_hdf5_independent_io.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/parallel_hdf5_independent_io.cpp @@ -13,13 +13,9 @@ #include -#include -#include -#include -#include +#include -const std::string FILE_NAME("parallel_independent_example.h5"); -const std::string DATASET_NAME("dset"); +const std::string file_name("parallel_independent_example.h5"); // This is an example of how to let MPI ranks read independent parts of the // HDF5 file. @@ -41,7 +37,7 @@ int main(int argc, char** argv) { // ... // } if (mpi_rank == 0) { - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); + File file(file_name, File::ReadWrite | File::Create | File::Truncate); for (int i = 0; i < mpi_size; ++i) { std::stringstream group_name; @@ -74,7 +70,7 @@ int main(int argc, char** argv) { // fapl.add(MPIOCollectiveMetadataWrite{}); // Now we can create the file as usual. - File file(FILE_NAME, File::ReadOnly, fapl); + File file(file_name, File::ReadOnly, fapl); // Note that this operation isn't collective. Each MPI rank is requesting to // open a different group. diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_dataset_string.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_dataset_string.cpp new file mode 100644 index 0000000000..2a4c3c4911 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_dataset_string.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c), 2017, Adrien Devresse + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include +#include +#include + +#include + +using namespace HighFive; + +const std::string file_name("create_dataset_string_example.h5"); +const std::string dataset_name("story"); + +// create a dataset from a vector of string +// read it back and print it +int main(void) { + // Create a new file using the default property lists. + File file(file_name, File::ReadWrite | File::Create | File::Truncate); + + std::vector string_list; + string_list.push_back("Hello World !"); + string_list.push_back( + "This string list is mapped to a dataset of " + "variable length string"); + string_list.push_back("Encoding is done in UTF-8 - 你好 - Здравствуйте!"); + string_list.push_back("May the force be with you"); + string_list.push_back("Enjoy !"); + + // create a dataset ready to contains strings of the size of the vector + // string_list + DataSet dataset = file.createDataSet(dataset_name, DataSpace::From(string_list)); + + // let's write our vector of string + dataset.write(string_list); + + // now we read it back + std::vector result_string_list; + dataset.read(result_string_list); + + for (size_t i = 0; i < result_string_list.size(); ++i) { + std::cout << ":" << i << " " << result_string_list[i] << "\n"; + } + + return 0; // successfully terminated +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_fixedlen_string.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_fixedlen_string.cpp new file mode 100644 index 0000000000..60589637ea --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_fixedlen_string.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c), 2020, Blue Brain Project + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include +#include + +#include + +using namespace HighFive; + +// This examples shows how compile time constant strings work. +// +// Note, that as of version 2.8.0., writing `std::string` as fixed-length +// strings there's a simpler API. +int main() { + // Create a new file using the default property lists. + File file("create_dataset_string_example.h5", File::Truncate); + const char strings_fixed[][16] = {"abcabcabcabcabc", "123123123123123"}; + + // create a dataset ready to contains strings of the size of the vector + file.createDataSet("ds1", DataSpace(2)).write(strings_fixed); + + // Without specific type info this will create an int8 dataset + file.createDataSet("ds2", strings_fixed); + + // Now test the new interface type + FixedLenStringArray<10> arr{"0000000", "1111111"}; + auto ds = file.createDataSet("ds3", arr); + + // Read back truncating to 4 chars + FixedLenStringArray<4> array_back; + ds.read(array_back); + std::cout << "First item is '" << array_back[0] << "'\n" + << "Second item is '" << array_back[1] << "'\n"; + + return 0; +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_raw_ptr.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_raw_ptr.cpp new file mode 100644 index 0000000000..b6cd9eda59 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_raw_ptr.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c), 2017, Adrien Devresse + * Copyright (c), 2022, Blue Brain Project + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include +#include +#include + +#include + +const std::string file_name("read_write_raw_ptr.h5"); +const std::string dataset_name("array"); + +// This create a "multi-dimensional" array. Meaning a pointer with +// dimensions. The `std::vector` is mearly a convenient way +// of allocating and releasing memory. +// +// Conceptionually this is only a raw pointer with dimensions. The +// data is store in row-major, aka C-style, without stride, offset +// or padding. +std::vector make_array(const std::vector& dims) { + auto n_elements = dims[0] * dims[1]; + std::vector nd_array(n_elements, 0.0); + + for (size_t i = 0; i < dims[0]; ++i) { + for (size_t j = 0; j < dims[1]; ++j) { + nd_array[j + i * dims[1]] = double(j) + 100.0 * double(i); + } + } + + return nd_array; +} + +int main(void) { + using namespace HighFive; + + // Create a new file using the default property lists. + File file(file_name, File::ReadWrite | File::Create | File::Truncate); + + // Let's write to file. + { + std::vector dims{3, 5}; + auto nd_array = make_array(dims); + + // First, create a dataset with the correct dimensions. + auto dataset = file.createDataSet(dataset_name, DataSpace(dims)); + + // Then write, using the raw pointer. + dataset.write_raw(nd_array.data()); + } + + // Let's read from file. + { + auto dataset = file.getDataSet(dataset_name); + + // First read the dimensions. + auto dims = dataset.getDimensions(); + + // Then allocate memory. + auto n_elements = dims[0] * dims[1]; + auto nd_array = std::vector(n_elements); + + // Finally, read into the memory by passing a raw pointer to the library. + dataset.read(nd_array.data()); + } + + return 0; +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_single_scalar.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_single_scalar.cpp new file mode 100644 index 0000000000..4b4c6887c9 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_single_scalar.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c), 2017, Adrien Devresse + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include +#include +#include + +#include + +const std::string file_name("read_write_scalar.h5"); +const std::string dataset_name("single_scalar"); + +// Create a dataset name "single_scalar" +// which contains only the perfect integer number "42" +// +int main(void) { + using namespace HighFive; + + // Create a new file using the default property lists. + File file(file_name, File::ReadWrite | File::Create | File::Truncate); + + int perfect_number = 42; + + // Create the dataset + DataSet dataset = file.createDataSet(dataset_name, DataSpace::From(perfect_number)); + + // write it + dataset.write(perfect_number); + + // flush everything + file.flush(); + + // let's read it back + int potentially_perfect_number; + + dataset.read(potentially_perfect_number); + + std::cout << "perfect number: " << potentially_perfect_number << std::endl; + + return 0; // successfully terminated +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_std_strings.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_std_strings.cpp new file mode 100644 index 0000000000..7699e0c0c4 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_std_strings.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c), 2023, Blue Brain Project, EPFL + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include +#include +#include + +#include +#include +#include + +using namespace HighFive; + +// This example shows how to write (containers of) `std::string` +// to dataset either as fixed or variable length HDF5 strings. +// The feature is available from 2.8.0 onwards. +int main(void) { + auto file = File("read_write_std_string.h5", File::Truncate); + + // A string of length 3 in a buffer of size 4 bytes. We'll use "length" for + // the semantic length of the string, i.e. excluding the '\0' character and + // "size" to refer to the length of the buffer in which the string is stored. + // For null-terminated strings, the `size == length + 1`. + std::string ascii_string = "foo"; + auto scalar_dataspace = DataSpace(DataSpace::dataspace_scalar); + + // Just write the string: + file.createDataSet("single_automatic", ascii_string); + + // The above results in writing the string as an HDF5 variable length UTF8 + // string. In HDF5 a variable length string doesn't specify the length of + // the string. Variable length strings are always null-terminated. + auto variable_stringtype = VariableLengthStringType(); + file.createDataSet("single_variable", scalar_dataspace, variable_stringtype) + .write(ascii_string); + + // HDF5 also has the concept of fixed length string. In fixed length strings + // the size of the string, in bytes, is part of the datatype. The HDF5 API + // for fixed and variable length strings is distinct. Hence, when writing + // string that need to be read by other programs, it can matter if the string + // is stored as fixed or variable length. + // + // Important: The HDF5 string size is the size of the buffer required to + // store the string. + // + // We know that ascii_string requires 4 bytes to store, but want to store + // it in fixed length strings of length 8. Additionally, we promise that + // the strings are null-terminated. The character set defaults to ASCII. + auto fixed_stringtype = FixedLengthStringType(8, StringPadding::NullTerminated); + file.createDataSet("single_fixed_nullterm", scalar_dataspace, fixed_stringtype) + .write(ascii_string); + + // When reading into an `std::string` it doesn't matter if the HDF5 datatype + // is fixed or variable length. HighFive will internally read into a buffer + // and then write to the final destination. + auto from_variable = file.getDataSet("single_variable").read(); + auto from_fixed = file.getDataSet("single_fixed_nullterm").read(); + + // Note that because the fixed length string is null-terminated, + // `from_fixed.size() == ascii_string.size()` despite it being stored as a string of + // length 8. + std::cout << "from_variable = '" << from_variable << "' size = " << from_variable.size() + << "\n"; + std::cout << "from_fixed = '" << from_fixed << "' size = " << from_fixed.size() << "\n"; + + // Fixed-length string don't have to be null-terminated. Their length could + // be defined simply by the known size of the buffer required to store the + // string. To deal with the situation where the string is shorter than the + // buffer, one defines a padding character. This must be either the null or + // space character. We'll show null-padded, space-padded works the same way. + auto fixed_nullpad = FixedLengthStringType(8, StringPadding::NullPadded); + file.createDataSet("single_fixed_nullpad", scalar_dataspace, fixed_nullpad).write(ascii_string); + + // Note that because we only know that the string is padded with nulls, but we + // don't know if those nulls were part of the string to begin with. The full + // size of the buffer is read into the `std::string`. The length of the + // `std::string` is the size of the string type. + auto from_nullpad = file.getDataSet("single_fixed_nullpad").read(); + std::cout << "from_nullpad = '" << from_nullpad << "' size = " << from_nullpad.size() << "\n"; + + // Let's look at UTF8 strings. In HDF5 the size of a string is the size in + // bytes of the buffer required to store the string. A UTF8 symbol/character + // requires 1 to 4 bytes. + // + // The 'a' is 1 byte, the 'α' 2 bytes, therefore a total of 3 bytes (same + // as `utf8_string.size()`). Which including the null character fits into + // 8 bytes. However, 8 bytes would, in general not be enough to store 2 + // UTF8 characters and the null character. Which would require 9 bytes. + std::string utf8_string = "aα"; + auto fixed_utf8_type = + FixedLengthStringType(8, StringPadding::NullTerminated, CharacterSet::Utf8); + file.createDataSet("single_fixed_utf8", scalar_dataspace, fixed_utf8_type).write(utf8_string); + + auto from_utf8 = file.getDataSet("single_fixed_utf8").read(); + std::cout << "from_utf8 = '" << from_utf8 << "' size = " << from_utf8.size() << "\n"; + + // Finally, containers of `std::string`s work analogously: + auto ascii_strings = std::vector{"123", "456"}; + file.createDataSet("multi_fixed_nullterm", DataSpace::From(ascii_strings), fixed_stringtype) + .write(ascii_strings); + + auto ascii_strings_from_fixed = + file.getDataSet("multi_fixed_nullterm").read>(); + + // In order to see details of how each is stored in the HDF5 file use: + // h5dump read_write_std_string.h5 + + return 0; +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_vector_dataset.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_vector_dataset.cpp similarity index 64% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_vector_dataset.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_vector_dataset.cpp index 4ed57a9b35..9718c1c2bc 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_vector_dataset.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_vector_dataset.cpp @@ -10,20 +10,18 @@ #include #include -#include -#include -#include +#include using namespace HighFive; -const std::string FILE_NAME("dataset_integer.h5"); -const std::string DATASET_NAME("dset"); +const std::string file_name("dataset_integer.h5"); +const std::string dataset_name("dset"); const size_t size_dataset = 20; // create a dataset 1D from a vector of string void write_dataset() { // we create a new hdf5 file - File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate); + File file(file_name, File::ReadWrite | File::Create | File::Truncate); std::vector data(size_dataset); for (size_t i = 0; i < data.size(); ++i) { @@ -32,7 +30,7 @@ void write_dataset() { // let's create a dataset of native integer with the size of the vector // 'data' - DataSet dataset = file.createDataSet(DATASET_NAME, DataSpace::From(data)); + DataSet dataset = file.createDataSet(dataset_name, DataSpace::From(data)); // let's write our vector of int to the HDF5 dataset dataset.write(data); @@ -41,12 +39,12 @@ void write_dataset() { // read our data back void read_dataset() { // we open the existing hdf5 file we created before - File file(FILE_NAME, File::ReadOnly); + File file(file_name, File::ReadOnly); std::vector read_data; // we get the dataset - DataSet dataset = file.getDataSet(DATASET_NAME); + DataSet dataset = file.getDataSet(dataset_name); // we convert the hdf5 dataset to a single dimension vector dataset.read(read_data); @@ -58,14 +56,8 @@ void read_dataset() { } int main(void) { - try { - write_dataset(); - read_dataset(); + write_dataset(); + read_dataset(); - } catch (Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - - return 0; // successfully terminated + return 0; } diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_vector_dataset_references.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_vector_dataset_references.cpp similarity index 85% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_vector_dataset_references.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_vector_dataset_references.cpp index 8733dc2758..ca08467682 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/read_write_vector_dataset_references.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/read_write_vector_dataset_references.cpp @@ -10,10 +10,7 @@ #include #include -#include -#include -#include -#include +#include // create a dataset 1D from a vector of int void write_dataset() { @@ -69,13 +66,8 @@ void read_dataset() { } int main() { - try { - write_dataset(); - read_dataset(); + write_dataset(); + read_dataset(); - } catch (const HighFive::Exception& err) { - // catch and print any HDF5 error - std::cerr << err.what() << std::endl; - } - return 0; // successfully terminated + return 0; } diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/readme_snippet.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/readme_snippet.cpp similarity index 70% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/readme_snippet.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/readme_snippet.cpp index 47187ee44f..160dabce56 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/readme_snippet.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/readme_snippet.cpp @@ -1,4 +1,4 @@ -#include +#include using namespace HighFive; @@ -22,9 +22,10 @@ int main() { // Read back, with allocating: auto data = dataset.read>(); - // Because `data` has the correct size, this will - // not cause `data` to be reallocated: - dataset.read(data); + // Because `pre_allocated` has the correct size, this will + // not cause `pre_allocated` to be reallocated: + auto pre_allocated = std::vector(50); + dataset.read(pre_allocated); } return 0; diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/renaming_objects.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/renaming_objects.cpp similarity index 94% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/renaming_objects.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/renaming_objects.cpp index 0f2a93ac70..f0759e52a2 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/src/examples/renaming_objects.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/renaming_objects.cpp @@ -1,8 +1,4 @@ -#include -#include -#include -#include -#include +#include using namespace HighFive; diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/select_by_id_dataset_cpp11.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/select_by_id_dataset_cpp11.cpp new file mode 100644 index 0000000000..973c57435f --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/select_by_id_dataset_cpp11.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c), 2017, Adrien Devresse + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include +#include +#include +#include + +#include + +const std::string file_name("select_partial_string.h5"); +const std::string dataset_name("message"); + +// Create a dataset name "dset" of double 4x6 +// +int main(void) { + using namespace HighFive; + + // Create a new file using the default property lists. + File file(file_name, File::Truncate); + + { + // We have a set of string + std::vector values = { + "Cat", + "Dog", + "Hello", + "Tree", + "World", + "Plane", + ", ", + "你好", + "Tea", + "Moon", + "صباح جميل", + "Spaceship", + }; + + // let's create a dataset + DataSet dataset = file.createDataSet(dataset_name, DataSpace::From(values)); + + // and write them + dataset.write(values); + } + + { + DataSet dataset = file.getDataSet(dataset_name); + + // now let's read back by cherry pick our interesting string + std::vector result; + // we select only element N° 2 and 5 + dataset.select(ElementSet({2, 4, 6, 7, 6, 10})).read(result); + + // and display it + for (auto i: result) { + std::cout << i << " "; + } + std::cout << "\n"; + } + + return 0; // successfully terminated +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/select_partial_dataset_cpp11.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/select_partial_dataset_cpp11.cpp new file mode 100644 index 0000000000..1e480c1603 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/src/examples/select_partial_dataset_cpp11.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c), 2017, Adrien Devresse + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include +#include +#include +#include + +#include + +const std::string file_name("select_partial_example.h5"); +const std::string dataset_name("dset"); + +// Create a dataset name "dset" of double 4x6 +// +int main(void) { + using namespace HighFive; + + // Create a new file using the default property lists. + File file(file_name, File::ReadWrite | File::Create | File::Truncate); + + // we have some example values in a 2D vector 2x5 + std::vector> values = {{1.0, 2.0, 4.0, 8.0, 16.0}, + {32.0, 64.0, 128.0, 256.0, 512.0}}; + + // let's create a dataset of this size + DataSet dataset = file.createDataSet(dataset_name, DataSpace::From(values)); + // and write them + dataset.write(values); + + // now we read back 2x2 values after an offset of 0x2 + std::vector> result; + dataset.select({0, 2}, {2, 2}).read(result); + + // we print out 4 values + for (auto i: result) { + for (auto j: i) { + std::cout << " " << j; + } + std::cout << "\n"; + } + + return 0; // successfully terminated +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_dependent_library/CMakeLists.txt b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_dependent_library/CMakeLists.txt similarity index 97% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_dependent_library/CMakeLists.txt rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_dependent_library/CMakeLists.txt index 1231a14196..570dba224e 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_dependent_library/CMakeLists.txt +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_dependent_library/CMakeLists.txt @@ -15,7 +15,7 @@ option(USE_BUNDLED_HIGHFIVE "Use highfive from deps folder. Otherwise must be in if(USE_BUNDLED_HIGHFIVE) add_subdirectory("deps/HighFive" EXCLUDE_FROM_ALL) else() - find_package(HighFive REQUIRED) + find_package(HighFive REQUIRED QUIET) endif() add_library(simpleton SHARED "src/simpleton.cpp" "src/otherton.cpp") diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_dependent_library/deps/.gitignore b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_dependent_library/deps/.gitignore similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_dependent_library/deps/.gitignore rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_dependent_library/deps/.gitignore diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_dependent_library/include/simpleton.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_dependent_library/include/simpleton.hpp new file mode 100644 index 0000000000..b98a09fda0 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_dependent_library/include/simpleton.hpp @@ -0,0 +1,14 @@ +#ifndef H5_TEST_SIMPLETON_HPP +#define H5_TEST_SIMPLETON_HPP + +// Include all headers here to catch any missing `inline` statements, since +// they will be included by two different compilation units. +#include + +// Boost should always be found in this setup +#include + +void function(const HighFive::Object& obj); +void other_function(const boost::numeric::ublas::matrix& m); + +#endif diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_dependent_library/src/otherton.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_dependent_library/src/otherton.cpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_dependent_library/src/otherton.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_dependent_library/src/otherton.cpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_dependent_library/src/simpleton.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_dependent_library/src/simpleton.cpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_dependent_library/src/simpleton.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_dependent_library/src/simpleton.cpp diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_project_integration.sh b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_project_integration.sh similarity index 95% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_project_integration.sh rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_project_integration.sh index 0d2b51af35..ae88695a0c 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/test_project_integration.sh +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/test_project_integration.sh @@ -11,7 +11,7 @@ test_install() { local builddir="${BUILDDIR}/${project}/${2}" shift shift - ln -s ../../.. "${TESTDIR}/${project}/deps/HighFive" + ln -sf ../../.. "${TESTDIR}/${project}/deps/HighFive" rm -rf "${builddir}" mkdir -p "${builddir}" pushd "${builddir}" diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/CMakeLists.txt b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/CMakeLists.txt similarity index 90% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/CMakeLists.txt rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/CMakeLists.txt index 7f24abf975..3644d117ce 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/CMakeLists.txt +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/CMakeLists.txt @@ -1,5 +1,6 @@ include(CTest) include(Catch) +include(HighFiveWarnings) if(MSVC) add_definitions(/bigobj) @@ -8,7 +9,7 @@ endif() ## Base tests foreach(test_name tests_high_five_base tests_high_five_multi_dims tests_high_five_easy test_all_types) add_executable(${test_name} "${test_name}.cpp") - target_link_libraries(${test_name} HighFive Catch2::Catch2WithMain) + target_link_libraries(${test_name} HighFive HighFiveWarnings Catch2::Catch2WithMain) catch_discover_tests(${test_name}) endforeach() @@ -18,7 +19,7 @@ if(HIGHFIVE_PARALLEL_HDF5) ## parallel MPI tests add_executable(tests_parallel_bin ${tests_parallel_src}) - target_link_libraries(tests_parallel_bin HighFive Catch2::Catch2) + target_link_libraries(tests_parallel_bin HighFive HighFiveWarnings Catch2::Catch2) # We need to patch in a call to `mpirun` or equivalent when using # parallel tests. Somehow, this is not foreseen in Catch2, modify the @@ -48,6 +49,6 @@ if(HIGHFIVE_TEST_SINGLE_INCLUDES) get_filename_component(CLASS_NAME ${PUBLIC_HEADER} NAME_WE) configure_file(tests_import_public_headers.cpp "tests_${CLASS_NAME}.cpp" @ONLY) add_executable("tests_include_${CLASS_NAME}" "${CMAKE_CURRENT_BINARY_DIR}/tests_${CLASS_NAME}.cpp") - target_link_libraries("tests_include_${CLASS_NAME}" HighFive) + target_link_libraries("tests_include_${CLASS_NAME}" HighFive HighFiveWarnings) endforeach() endif() diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/test_all_types.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/test_all_types.cpp similarity index 90% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/test_all_types.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/test_all_types.cpp index ed265e6704..d74579af6b 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/test_all_types.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/test_all_types.cpp @@ -8,21 +8,15 @@ */ #include -#include -#include -#include -#include -#include -#include - #include +#include #include "tests_high_five.hpp" using namespace HighFive; TEMPLATE_TEST_CASE("Scalar in DataSet", "[Types]", bool, std::string) { - const std::string FILE_NAME("Test_type.h5"); + const std::string FILE_NAME("rw_dataset_" + typeNameHelper() + ".h5"); const std::string DATASET_NAME("dset"); TestType t1{}; @@ -52,7 +46,7 @@ TEMPLATE_TEST_CASE("Scalar in DataSet", "[Types]", bool, std::string) { } TEMPLATE_PRODUCT_TEST_CASE("Scalar in std::vector", "[Types]", std::vector, (bool, std::string)) { - const std::string FILE_NAME("Test_vector.h5"); + const std::string FILE_NAME("rw_dataset_" + typeNameHelper() + ".h5"); const std::string DATASET_NAME("dset"); TestType t1(5); @@ -84,7 +78,7 @@ TEMPLATE_PRODUCT_TEST_CASE("Scalar in std::vector", "[Types]", std::vector, (bool, std::string)) { - const std::string FILE_NAME("Test_vector_vector.h5"); + const std::string FILE_NAME("rw_dataset_vector_" + typeNameHelper() + ".h5"); const std::string DATASET_NAME("dset"); std::vector t1(5); for (auto&& e: t1) { @@ -118,7 +112,7 @@ TEMPLATE_PRODUCT_TEST_CASE("Scalar in std::vector", } TEMPLATE_TEST_CASE("Scalar in std::array", "[Types]", bool, std::string) { - const std::string FILE_NAME("Test_array.h5"); + const std::string FILE_NAME("rw_dataset_array_" + typeNameHelper() + ".h5"); const std::string DATASET_NAME("dset"); std::array t1{}; @@ -149,7 +143,7 @@ TEMPLATE_TEST_CASE("Scalar in std::array", "[Types]", bool, std::string) { } TEMPLATE_TEST_CASE("Scalar in std::vector", "[Types]", bool, std::string) { - const std::string FILE_NAME("Test_vector_array.h5"); + const std::string FILE_NAME("rw_dataset_vector_array_" + typeNameHelper() + ".h5"); const std::string DATASET_NAME("dset"); std::vector> t1(5); @@ -182,7 +176,7 @@ TEMPLATE_TEST_CASE("Scalar in std::vector", "[Types]", bool, std::st #if HIGHFIVE_CXX_STD >= 17 TEMPLATE_PRODUCT_TEST_CASE("Scalar in std::vector", "[Types]", std::vector, std::byte) { - const std::string FILE_NAME("Test_vector_byte.h5"); + const std::string FILE_NAME("rw_dataset_vector_" + typeNameHelper() + ".h5"); const std::string DATASET_NAME("dset"); TestType t1(5, std::byte(0xCD)); diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five.hpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five.hpp similarity index 95% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five.hpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five.hpp index 2d23c0879c..0ebd58c448 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five.hpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five.hpp @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include // We don't need windows specific functionality. However, to better detect defects caused by macros, // we include this header. @@ -162,16 +165,22 @@ struct ContentGenerate { template inline std::string typeNameHelper() { std::string name = typeid(T).name(); -#if defined(WIN32) - // Replace illegal windows file path characters std::replace(std::begin(name), std::end(name), ' ', '_'); std::replace(std::begin(name), std::end(name), '<', '_'); std::replace(std::begin(name), std::end(name), '>', '_'); std::replace(std::begin(name), std::end(name), ':', '_'); -#endif - return name; + + if (name.size() > 64) { + std::stringstream hash; + hash << std::hex << std::hash{}(name); + + return hash.str(); + } else { + return name; + } } + template inline HighFive::DataSet readWriteDataset(const DataT& ndvec, DataT& result, @@ -193,4 +202,4 @@ inline HighFive::DataSet readWriteDataset(const DataT& ndvec, dataset.read(result); return dataset; -} \ No newline at end of file +} diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five_base.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five_base.cpp similarity index 82% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five_base.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five_base.cpp index 8c5706221b..899170d932 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five_base.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five_base.cpp @@ -18,18 +18,11 @@ #include #include -#include -#include -#include -#include -#include -#include -#include - #include #include #include +#include #include "tests_high_five.hpp" using namespace HighFive; @@ -291,12 +284,12 @@ TEST_CASE("Test group properties") { File file(file_name, File::Truncate, fapl); GroupCreateProps props; - props.add(EstimatedLinkInfo(1000, 500)); + props.add(EstimatedLinkInfo(10, 60)); auto group = file.createGroup("g", props); auto sizes = group.getEstimatedLinkInfo(); - CHECK(sizes.first == 1000); - CHECK(sizes.second == 500); + CHECK(sizes.first == 10); + CHECK(sizes.second == 60); } TEST_CASE("Test allocation time") { @@ -317,13 +310,21 @@ TEST_CASE("Test allocation time") { CHECK(alloc_size == data.size() * sizeof(decltype(data)::value_type)); } +/* + * Test to ensure legacy support: DataSet used to have a default constructor. + * However, it is not useful to have a DataSet object that does not actually + * refer to a dataset in a file. Hence, the the default constructor was + * deprecated. + * This test is to ensure that the constructor is not accidentally removed and + * thereby break users' code. + */ TEST_CASE("Test default constructors") { - const std::string file_name("h5_group_test.h5"); + const std::string file_name("h5_default_ctors.h5"); const std::string dataset_name("dset"); File file(file_name, File::Truncate); auto ds = file.createDataSet(dataset_name, std::vector{1, 2, 3, 4, 5}); - DataSet d2; // deprecated as it constructs unsafe objects + DataSet d2; // expect deprecation warning, as it constructs unsafe object // d2.getFile(); // runtime error CHECK(!d2.isValid()); d2 = ds; // copy @@ -426,6 +427,67 @@ TEST_CASE("Test groups and datasets") { } } +TEST_CASE("FileSpace") { + const std::string filename = "filespace.h5"; + const std::string ds_path = "dataset"; + const std::vector data{13, 24, 36}; + + File file(filename, File::Truncate); + file.createDataSet(ds_path, data); + + CHECK(file.getFileSize() > 0); +} + +TEST_CASE("FreeSpace (default)") { + const std::string filename = "freespace_default.h5"; + const std::string ds_path = "dataset"; + const std::vector data{13, 24, 36}; + + { + File file(filename, File::Truncate); + auto dset = file.createDataSet(ds_path, data); + } + + { + File file(filename, File::ReadWrite); + file.unlink(ds_path); + CHECK(file.getFreeSpace() > 0); + CHECK(file.getFreeSpace() < file.getFileSize()); + } +} + +#if H5_VERSION_GE(1, 10, 1) +TEST_CASE("FreeSpace (tracked)") { + const std::string filename = "freespace_tracked.h5"; + const std::string ds_path = "dataset"; + const std::vector data{13, 24, 36}; + + { + FileCreateProps fcp; + fcp.add(FileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 0)); + File file(filename, File::Truncate, fcp); + auto dset = file.createDataSet(ds_path, data); + } + + { + File file(filename, File::ReadWrite); + file.unlink(ds_path); + +#if H5_VERSION_GE(1, 12, 0) + // This fails on 1.10.x but starts working in 1.12.0 + CHECK(file.getFreeSpace() > 0); +#endif + CHECK(file.getFreeSpace() < file.getFileSize()); + } + + { + File file(filename, File::ReadOnly); + CHECK(file.getFreeSpace() > 0); + CHECK(file.getFreeSpace() < file.getFileSize()); + } +} +#endif + TEST_CASE("Test extensible datasets") { const std::string file_name("create_extensible_dataset_example.h5"); const std::string dataset_name("dset"); @@ -633,6 +695,45 @@ TEST_CASE("Simple test for type equality") { CHECK(int_var != uint_var); } +TEST_CASE("StringType") { + SECTION("enshrine-defaults") { + auto fixed_length = FixedLengthStringType(32, StringPadding::SpacePadded); + auto variable_length = VariableLengthStringType(); + + REQUIRE(fixed_length.getCharacterSet() == CharacterSet::Ascii); + REQUIRE(variable_length.getCharacterSet() == CharacterSet::Ascii); + } + + SECTION("fixed-length") { + auto fixed_length = + FixedLengthStringType(32, StringPadding::SpacePadded, CharacterSet::Utf8); + auto string_type = fixed_length.asStringType(); + + REQUIRE(string_type.getId() == fixed_length.getId()); + REQUIRE(string_type.getCharacterSet() == CharacterSet::Utf8); + REQUIRE(string_type.getPadding() == StringPadding::SpacePadded); + REQUIRE(string_type.getSize() == 32); + REQUIRE(!string_type.isVariableStr()); + REQUIRE(string_type.isFixedLenStr()); + } + + SECTION("variable-length") { + auto variable_length = VariableLengthStringType(CharacterSet::Utf8); + auto string_type = variable_length.asStringType(); + + REQUIRE(string_type.getId() == variable_length.getId()); + REQUIRE(string_type.getCharacterSet() == CharacterSet::Utf8); + REQUIRE(string_type.isVariableStr()); + REQUIRE(!string_type.isFixedLenStr()); + } + + SECTION("atomic") { + auto atomic = AtomicType(); + REQUIRE_THROWS(atomic.asStringType()); + } +} + + TEST_CASE("DataTypeEqualTakeBack") { const std::string file_name("h5tutr_dset.h5"); const std::string dataset_name("dset"); @@ -674,9 +775,11 @@ TEST_CASE("DataSpaceTest") { DataSpace space = dataset.getSpace(); DataSpace space2 = dataset.getSpace(); + auto space3 = space.clone(); // verify space id are different CHECK(space.getId() != space2.getId()); + CHECK(space.getId() != space3.getId()); // verify space id are consistent CHECK(space.getDimensions().size() == 2); @@ -684,6 +787,39 @@ TEST_CASE("DataSpaceTest") { CHECK(space.getDimensions()[1] == 1); } +TEST_CASE("DataSpace::getElementCount") { + SECTION("null") { + auto space = DataSpace(DataSpace::dataspace_null); + CHECK(space.getElementCount() == 0); + } + + SECTION("scalar") { + auto space = DataSpace(DataSpace::dataspace_scalar); + CHECK(space.getElementCount() == 1); + } + + SECTION("simple, empty (1D)") { + auto space = DataSpace(0); + CHECK(space.getElementCount() == 0); + } + + SECTION("simple, empty (2D)") { + auto space = DataSpace(0, 0); + CHECK(space.getElementCount() == 0); + } + + SECTION("simple, non-empty (2D)") { + auto space = DataSpace(2, 3); + CHECK(space.getElementCount() == 6); + } + + SECTION("FromCharArrayStrings") { + char string_array[2][10] = {"123456789", "abcdefghi"}; + auto space = DataSpace::FromCharArrayStrings(string_array); + CHECK(space.getElementCount() == 2); + } +} + TEST_CASE("DataSpaceVectorTest") { // Create 1D shortcut dataspace DataSpace space(7); @@ -934,6 +1070,34 @@ TEMPLATE_LIST_TEST_CASE("ReadWriteAttributeVector", "[template]", dataset_test_t readWriteAttributeVectorTest(); } +TEST_CASE("WriteLargeAttribute") { + std::vector large_attr(16000, 0.0); + + auto fapl = HighFive::FileAccessProps::Default(); + fapl.add(HighFive::FileVersionBounds(H5F_LIBVER_LATEST, H5F_LIBVER_LATEST)); + HighFive::File file("create_large_attribute.h5", HighFive::File::Truncate, fapl); + auto gcpl = HighFive::GroupCreateProps::Default(); + gcpl.add(HighFive::AttributePhaseChange(0, 0)); + + auto group = file.createGroup("grp", gcpl); + CHECK_NOTHROW(group.createAttribute("attr", large_attr)); +} + +TEST_CASE("AttributePhaseChange") { + auto fapl = HighFive::FileAccessProps::Default(); + fapl.add(HighFive::FileVersionBounds(H5F_LIBVER_LATEST, H5F_LIBVER_LATEST)); + HighFive::File file("attribute_phase_change.h5", HighFive::File::Truncate, fapl); + + auto gcpl = HighFive::GroupCreateProps::Default(); + gcpl.add(HighFive::AttributePhaseChange(42, 24)); + + auto group = file.createGroup("grp", gcpl); + + auto actual = AttributePhaseChange(group.getCreatePropertyList()); + CHECK(actual.min_dense() == 24); + CHECK(actual.max_compact() == 42); +} + TEST_CASE("datasetOffset") { std::string filename = "datasetOffset.h5"; std::string dsetname = "dset"; @@ -2211,8 +2375,72 @@ TEST_CASE("HighFiveSoftLinks") { } } +TEST_CASE("HighFiveHardLinks Dataset (create intermediate)") { + const std::string file_name("hardlinks_dataset_intermiate.h5"); + const std::string ds_path("/group/dataset"); + const std::string ds_link_path("/alternate/dataset"); + const std::vector data{12, 24, 36}; + + { + File file(file_name, File::Truncate); + auto dset = file.createDataSet(ds_path, data); + file.createHardLink(ds_link_path, dset); + file.unlink(ds_path); + } + + { + File file(file_name, File::ReadWrite); + auto data_out = file.getDataSet(ds_link_path).read>(); + CHECK(data == data_out); + } +} + +TEST_CASE("HighFiveHardLinks Dataset (relative paths)") { + const std::string file_name("hardlinks_dataset_relative.h5"); + const std::string ds_path("/group/dataset"); + const std::string ds_link_path("/alternate/dataset"); + const std::vector data{12, 24, 36}; + + { + File file(file_name, File::Truncate); + auto dset = file.createDataSet(ds_path, data); + + auto alternate = file.createGroup("/alternate"); + alternate.createHardLink("dataset", dset); + file.unlink(ds_path); + } + + { + File file(file_name, File::ReadWrite); + auto data_out = file.getDataSet(ds_link_path).read>(); + CHECK(data == data_out); + } +} + +TEST_CASE("HighFiveHardLinks Group") { + const std::string file_name("hardlinks_group.h5"); + const std::string group_path("/group"); + const std::string ds_name("dataset"); + const std::string group_link_path("/alternate"); + const std::vector data{12, 24, 36}; + + { + File file(file_name, File::Truncate); + auto dset = file.createDataSet(group_path + "/" + ds_name, data); + auto group = file.getGroup(group_path); + file.createHardLink(group_link_path, group); + file.unlink(group_path); + } + + { + File file(file_name, File::ReadWrite); + auto data_out = file.getDataSet(group_link_path + "/" + ds_name).read>(); + CHECK(data == data_out); + } +} + TEST_CASE("HighFiveRename") { - File file("move.h5", File::ReadWrite | File::Create | File::Truncate); + File file("h5_rename.h5", File::ReadWrite | File::Create | File::Truncate); int number = 100; @@ -2237,7 +2465,7 @@ TEST_CASE("HighFiveRename") { } TEST_CASE("HighFiveRenameRelative") { - File file("move.h5", File::ReadWrite | File::Create | File::Truncate); + File file("h5_rename_relative.h5", File::ReadWrite | File::Create | File::Truncate); Group group = file.createGroup("group"); int number = 100; @@ -2424,6 +2652,10 @@ TEST_CASE("HighFiveCompounds") { CompoundType t2_from_hid(t2); CHECK(t2 == t2_from_hid); + + // Back from a DataType + CHECK_NOTHROW(CompoundType(DataType(t1_from_hid))); + CHECK_THROWS(CompoundType(AtomicType{})); } struct GrandChild { @@ -2676,6 +2908,250 @@ TEST_CASE("HighFiveEnum") { } } +TEST_CASE("HighFiveReadType") { + const std::string file_name("readtype_test.h5"); + const std::string datatype_name1("my_type"); + const std::string datatype_name2("position"); + + File file(file_name, File::ReadWrite | File::Create | File::Truncate); + + CompoundType t1 = create_compound_csl1(); + t1.commit(file, datatype_name1); + + CompoundType t2 = file.getDataType(datatype_name1); + + auto t3 = create_enum_position(); + t3.commit(file, datatype_name2); + + DataType t4 = file.getDataType(datatype_name2); + + CHECK(t2 == t1); + CHECK(t4 == t3); +} + +class ForwardToAttribute { + public: + ForwardToAttribute(const HighFive::File& file) + : _file(file) {} + + template + HighFive::Attribute create(const std::string& name, const T& value) { + return _file.createAttribute(name, value); + } + + HighFive::Attribute create(const std::string& name, + const HighFive::DataSpace filespace, + const HighFive::DataType& datatype) { + return _file.createAttribute(name, filespace, datatype); + } + + HighFive::Attribute get(const std::string& name) { + return _file.getAttribute(name); + } + + private: + HighFive::File _file; +}; + +class ForwardToDataSet { + public: + ForwardToDataSet(const HighFive::File& file) + : _file(file) {} + + template + HighFive::DataSet create(const std::string& name, const T& value) { + return _file.createDataSet(name, value); + } + + HighFive::DataSet create(const std::string& name, + const HighFive::DataSpace filespace, + const HighFive::DataType& datatype) { + return _file.createDataSet(name, filespace, datatype); + } + + HighFive::DataSet get(const std::string& name) { + return _file.getDataSet(name); + } + + private: + HighFive::File _file; +}; + +template +void check_single_string(Proxy proxy, size_t string_length) { + auto value = std::string(string_length, 'o'); + auto dataspace = DataSpace::From(value); + + auto n_chars = value.size() + 1; + auto n_chars_overlength = n_chars + 10; + auto fixed_length = FixedLengthStringType(n_chars, StringPadding::NullTerminated); + auto overlength_nullterm = FixedLengthStringType(n_chars_overlength, + StringPadding::NullTerminated); + auto overlength_nullpad = FixedLengthStringType(n_chars_overlength, StringPadding::NullPadded); + auto overlength_spacepad = FixedLengthStringType(n_chars_overlength, + StringPadding::SpacePadded); + auto variable_length = VariableLengthStringType(); + + SECTION("automatic") { + proxy.create("auto", value); + REQUIRE(proxy.get("auto").template read() == value); + } + + SECTION("fixed length") { + proxy.create("fixed", dataspace, fixed_length).write(value); + REQUIRE(proxy.get("fixed").template read() == value); + } + + SECTION("overlength null-terminated") { + proxy.create("overlength_nullterm", dataspace, overlength_nullterm).write(value); + REQUIRE(proxy.get("overlength_nullterm").template read() == value); + } + + SECTION("overlength null-padded") { + proxy.create("overlength_nullpad", dataspace, overlength_nullpad).write(value); + auto expected = std::string(n_chars_overlength, '\0'); + expected.replace(0, value.size(), value.data()); + REQUIRE(proxy.get("overlength_nullpad").template read() == expected); + } + + SECTION("overlength space-padded") { + proxy.create("overlength_spacepad", dataspace, overlength_spacepad).write(value); + auto expected = std::string(n_chars_overlength, ' '); + expected.replace(0, value.size(), value.data()); + REQUIRE(proxy.get("overlength_spacepad").template read() == expected); + } + + SECTION("variable length") { + proxy.create("variable", dataspace, variable_length).write(value); + REQUIRE(proxy.get("variable").template read() == value); + } +} + +template +void check_multiple_string(Proxy proxy, size_t string_length) { + using value_t = std::vector; + auto value = value_t{std::string(string_length, 'o'), std::string(string_length, 'x')}; + + auto dataspace = DataSpace::From(value); + + auto string_overlength = string_length + 10; + auto onpoint_nullpad = FixedLengthStringType(string_length, StringPadding::NullPadded); + auto onpoint_spacepad = FixedLengthStringType(string_length, StringPadding::SpacePadded); + + auto overlength_nullterm = FixedLengthStringType(string_overlength, + StringPadding::NullTerminated); + auto overlength_nullpad = FixedLengthStringType(string_overlength, StringPadding::NullPadded); + auto overlength_spacepad = FixedLengthStringType(string_overlength, StringPadding::SpacePadded); + auto variable_length = VariableLengthStringType(); + + auto check = [](const value_t actual, const value_t& expected) { + REQUIRE(actual.size() == expected.size()); + for (size_t i = 0; i < actual.size(); ++i) { + REQUIRE(actual[i] == expected[i]); + } + }; + + SECTION("automatic") { + proxy.create("auto", value); + check(proxy.get("auto").template read(), value); + } + + SECTION("variable length") { + proxy.create("variable", dataspace, variable_length).write(value); + check(proxy.get("variable").template read(), value); + } + + auto make_padded_reference = [&](char pad, size_t n) { + auto expected = std::vector(value.size(), std::string(n, pad)); + for (size_t i = 0; i < value.size(); ++i) { + expected[i].replace(0, value[i].size(), value[i].data()); + } + + return expected; + }; + + auto check_fixed_length = [&](const std::string& label, size_t length) { + SECTION(label + " null-terminated") { + auto datatype = FixedLengthStringType(length + 1, StringPadding::NullTerminated); + proxy.create(label + "_nullterm", dataspace, datatype).write(value); + check(proxy.get(label + "_nullterm").template read(), value); + } + + SECTION(label + " null-padded") { + auto datatype = FixedLengthStringType(length, StringPadding::NullPadded); + proxy.create(label + "_nullpad", dataspace, datatype).write(value); + auto expected = make_padded_reference('\0', length); + check(proxy.get(label + "_nullpad").template read(), expected); + } + + SECTION(label + " space-padded") { + auto datatype = FixedLengthStringType(length, StringPadding::SpacePadded); + proxy.create(label + "_spacepad", dataspace, datatype).write(value); + auto expected = make_padded_reference(' ', length); + check(proxy.get(label + "_spacepad").template read(), expected); + } + }; + + check_fixed_length("onpoint", string_length); + check_fixed_length("overlength", string_length + 5); + + + SECTION("underlength null-terminated") { + auto datatype = FixedLengthStringType(string_length, StringPadding::NullTerminated); + REQUIRE_THROWS(proxy.create("underlength_nullterm", dataspace, datatype).write(value)); + } + + SECTION("underlength nullpad") { + auto datatype = FixedLengthStringType(string_length - 1, StringPadding::NullPadded); + REQUIRE_THROWS(proxy.create("underlength_nullpad", dataspace, datatype).write(value)); + } + + SECTION("underlength spacepad") { + auto datatype = FixedLengthStringType(string_length - 1, StringPadding::NullTerminated); + REQUIRE_THROWS(proxy.create("underlength_spacepad", dataspace, datatype).write(value)); + } +} + +TEST_CASE("HighFiveSTDString (dataset, single, short)") { + File file("std_string_dataset_single_short.h5", File::Truncate); + check_single_string(ForwardToDataSet(file), 3); +} + +TEST_CASE("HighFiveSTDString (attribute, single, short)") { + File file("std_string_attribute_single_short.h5", File::Truncate); + check_single_string(ForwardToAttribute(file), 3); +} + +TEST_CASE("HighFiveSTDString (dataset, single, long)") { + File file("std_string_dataset_single_long.h5", File::Truncate); + check_single_string(ForwardToDataSet(file), 256); +} + +TEST_CASE("HighFiveSTDString (attribute, single, long)") { + File file("std_string_attribute_single_long.h5", File::Truncate); + check_single_string(ForwardToAttribute(file), 256); +} + +TEST_CASE("HighFiveSTDString (dataset, multiple, short)") { + File file("std_string_dataset_multiple_short.h5", File::Truncate); + check_multiple_string(ForwardToDataSet(file), 3); +} + +TEST_CASE("HighFiveSTDString (attribute, multiple, short)") { + File file("std_string_attribute_multiple_short.h5", File::Truncate); + check_multiple_string(ForwardToAttribute(file), 3); +} + +TEST_CASE("HighFiveSTDString (dataset, multiple, long)") { + File file("std_string_dataset_multiple_short.h5", File::Truncate); + check_multiple_string(ForwardToDataSet(file), 256); +} + +TEST_CASE("HighFiveSTDString (attribute, multiple, long)") { + File file("std_string_attribute_multiple_short.h5", File::Truncate); + check_multiple_string(ForwardToAttribute(file), 256); +} + TEST_CASE("HighFiveFixedString") { const std::string file_name("array_atomic_types.h5"); const std::string group_1("group1"); @@ -2710,6 +3186,7 @@ TEST_CASE("HighFiveFixedString") { file.createDataSet("ds4", DataSpace(2)).write(strings_fixed); } + { // Cant convert flex-length to fixed-length const char* buffer[] = {"abcd", "1234"}; SilenceHDF5 silencer; @@ -2724,8 +3201,6 @@ TEST_CASE("HighFiveFixedString") { { // Dedicated FixedLenStringArray FixedLenStringArray<10> arr{"0000000", "1111111"}; - // For completeness, test also the other constructor - FixedLenStringArray<10> arrx(std::vector{"0000", "1111"}); // More API: test inserting something arr.push_back("2222"); @@ -2753,6 +3228,72 @@ TEST_CASE("HighFiveFixedString") { CHECK((*iter)[1] == 'y'); } } + + { + // Direct way of writing `std::string` as a fixed length + // HDF5 string. + + std::string value = "foo"; + auto n_chars = value.size() + 1; + + auto datatype = FixedLengthStringType(n_chars, StringPadding::NullTerminated); + auto dataspace = DataSpace(1); + + auto ds = file.createDataSet("ds8", dataspace, datatype); + ds.write_raw(value.data(), datatype); + + { + // Due to missing non-const overload of `data()` until C++17 we'll + // read into something else instead (don't forget the '\0'). + auto expected = std::vector(n_chars, '!'); + ds.read(expected.data(), datatype); + + CHECK(expected.size() == value.size() + 1); + for (size_t i = 0; i < value.size(); ++i) { + REQUIRE(expected[i] == value[i]); + } + } + +#if HIGHFIVE_CXX_STD >= 17 + { + auto expected = std::string(value.size(), '-'); + ds.read(expected.data(), datatype); + + REQUIRE(expected == value); + } +#endif + } + + { + size_t n_chars = 4; + size_t n_strings = 2; + + std::vector value(n_chars * n_strings, '!'); + + auto datatype = FixedLengthStringType(n_chars, StringPadding::NullTerminated); + auto dataspace = DataSpace(n_strings); + + auto ds = file.createDataSet("ds9", dataspace, datatype); + ds.write_raw(value.data(), datatype); + + auto expected = std::vector(value.size(), '-'); + ds.read(expected.data(), datatype); + + CHECK(expected.size() == value.size()); + for (size_t i = 0; i < value.size(); ++i) { + REQUIRE(expected[i] == value[i]); + } + } +} + +template +static void check_fixed_len_string_array_contents(const FixedLenStringArray& array, + const std::vector& expected) { + REQUIRE(array.size() == expected.size()); + + for (size_t i = 0; i < array.size(); ++i) { + CHECK(array[i] == expected[i]); + } } TEST_CASE("HighFiveFixedLenStringArrayStructure") { @@ -2769,16 +3310,52 @@ TEST_CASE("HighFiveFixedLenStringArrayStructure") { return output; }; + SECTION("create from std::vector (onpoint)") { + auto expected = std::vector{"000", "111"}; + auto actual = FixedLenStringArray<4>(expected); + check_fixed_len_string_array_contents(actual, expected); + } + + SECTION("create from std::vector (oversized)") { + auto expected = std::vector{"000", "111"}; + auto actual = FixedLenStringArray<8>(expected); + check_fixed_len_string_array_contents(actual, expected); + } + + SECTION("create from pointers (onpoint)") { + auto expected = std::vector{"000", "111"}; + auto actual = FixedLenStringArray<4>(expected.data(), expected.data() + expected.size()); + check_fixed_len_string_array_contents(actual, expected); + } + + SECTION("create from pointers (oversized)") { + auto expected = std::vector{"000", "111"}; + auto actual = FixedLenStringArray<8>(expected.data(), expected.data() + expected.size()); + check_fixed_len_string_array_contents(actual, expected); + } + + + SECTION("create from std::initializer_list (onpoint)") { + auto expected = std::vector{"000", "111"}; + auto actual = FixedLenStringArray<4>{"000", "111"}; + check_fixed_len_string_array_contents(actual, expected); + } + + SECTION("create from std::initializer_list (oversized)") { + auto expected = std::vector{"000", "111"}; + auto actual = FixedLenStringArray<8>{"000", "111"}; + check_fixed_len_string_array_contents(actual, expected); + } + // manipulate FixedLenStringArray with std::copy - { + SECTION("compatible with std::copy") { const fixed_array_t arr1{"0000000", "1111111"}; fixed_array_t arr2{"0000000", "1111111"}; std::copy(arr1.begin(), arr1.end(), std::back_inserter(arr2)); CHECK(arr2.size() == 4); } - // manipulate FixedLenStringArray with std::transform - { + SECTION("compatible with std::transform") { fixed_array_t arr; { const fixed_array_t arr1{"0000000", "1111111"}; @@ -2789,8 +3366,7 @@ TEST_CASE("HighFiveFixedLenStringArrayStructure") { CHECK(arr[1] == std::string("2222222")); } - // manipulate FixedLenStringArray with std::transform and reverse iterator - { + SECTION("compatible with std::transform (reverse iterator)") { fixed_array_t arr; { const fixed_array_t arr1{"0000000", "1111111"}; @@ -2801,8 +3377,7 @@ TEST_CASE("HighFiveFixedLenStringArrayStructure") { CHECK(arr[1] == std::string("0000000")); } - // manipulate FixedLenStringArray with std::remove_copy_if - { + SECTION("compatible with std::remove_copy_if") { fixed_array_t arr2; { const fixed_array_t arr1{"0000000", "1111111"}; @@ -2987,7 +3562,7 @@ TEST_CASE("HighFiveEigen") { vec_in << 1, 2, 3, 4, 5, 6, 7, 8, 9; Eigen::Matrix vec_out; - test_eigen_vec(file, ds_name_flavor, vec_in, vec_out); + CHECK_THROWS(test_eigen_vec(file, ds_name_flavor, vec_in, vec_out)); } // Eigen MatrixXd @@ -2996,7 +3571,7 @@ TEST_CASE("HighFiveEigen") { Eigen::MatrixXd vec_in = 100. * Eigen::MatrixXd::Random(20, 5); Eigen::MatrixXd vec_out(20, 5); - test_eigen_vec(file, ds_name_flavor, vec_in, vec_out); + CHECK_THROWS(test_eigen_vec(file, ds_name_flavor, vec_in, vec_out)); } // std::vector @@ -3010,7 +3585,7 @@ TEST_CASE("HighFiveEigen") { vec_in.push_back(m2); std::vector vec_out(2, Eigen::MatrixXd::Zero(20, 5)); - test_eigen_vec(file, ds_name_flavor, vec_in, vec_out); + CHECK_THROWS(test_eigen_vec(file, ds_name_flavor, vec_in, vec_out)); } #ifdef H5_USE_BOOST @@ -3044,13 +3619,15 @@ TEST_CASE("HighFiveEigen") { } } boost::multi_array vec_out(boost::extents[3][2][2]); - for (int i = 0; i < 3; ++i) + for (int i = 0; i < 3; ++i) { for (int j = 0; j < 2; ++j) { for (int k = 0; k < 2; ++k) { vec_out[i][j][k] = Eigen::MatrixXd::Zero(3, 3); } } - test_eigen_vec(file, ds_name_flavor, vec_in, vec_out); + } + + CHECK_THROWS(test_eigen_vec(file, ds_name_flavor, vec_in, vec_out)); } #endif diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five_easy.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five_easy.cpp similarity index 78% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five_easy.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five_easy.cpp index 64a7706740..e003c32340 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five_easy.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five_easy.cpp @@ -56,40 +56,66 @@ TEST_CASE("H5Easy_Compression") { } TEST_CASE("H5Easy_scalar") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_scalar.h5", H5Easy::File::Overwrite); double a = 1.2345; int b = 12345; std::string c = "12345"; + std::complex d = std::complex(1.2345, -5.4321); + std::complex e = std::complex(12345, -54321); H5Easy::dump(file, "/path/to/a", a); H5Easy::dump(file, "/path/to/b", b); H5Easy::dump(file, "/path/to/c", c); H5Easy::dump(file, "/path/to/c", c, H5Easy::DumpMode::Overwrite); + H5Easy::dump(file, "/path/to/d", d); + H5Easy::dump(file, "/path/to/e", e); double a_r = H5Easy::load(file, "/path/to/a"); int b_r = H5Easy::load(file, "/path/to/b"); std::string c_r = H5Easy::load(file, "/path/to/c"); + std::complex d_r = H5Easy::load>(file, "/path/to/d"); + std::complex e_r = H5Easy::load>(file, "/path/to/e"); CHECK(a == a_r); CHECK(b == b_r); CHECK(c == c_r); + CHECK(d == d_r); + CHECK(e == e_r); } TEST_CASE("H5Easy_vector1d") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_vector1d.h5", H5Easy::File::Overwrite); std::vector a = {1, 2, 3, 4, 5}; + std::vector> b = {std::complex(1, .1), + std::complex(2, -.4), + std::complex(3, .9), + std::complex(4, -.16), + std::complex(5, .25)}; + std::vector> c = {std::complex(1, -5), + std::complex(2, -4), + std::complex(3, -3), + std::complex(4, -2), + std::complex(5, -1)}; H5Easy::dump(file, "/path/to/a", a); + H5Easy::dump(file, "/path/to/b", b); + H5Easy::dump(file, "/path/to/c", c); std::vector a_r = H5Easy::load>(file, "/path/to/a"); + std::vector> b_r = + H5Easy::load>>(file, "/path/to/b"); + std::vector> c_r = + H5Easy::load>>(file, "/path/to/c"); CHECK(a == a_r); + CHECK(b == b_r); + CHECK(c == c_r); } TEST_CASE("H5Easy_vector2d") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_vector2d.h5", H5Easy::File::Overwrite); std::vector> a({{0, 1}, {2, 3}, {4, 5}}); @@ -101,7 +127,7 @@ TEST_CASE("H5Easy_vector2d") { } TEST_CASE("H5Easy_vector2d_compression") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_vector2d_compression.h5", H5Easy::File::Overwrite); std::vector> a({{0, 1}, {2, 3}, {4, 5}}); @@ -118,7 +144,7 @@ TEST_CASE("H5Easy_vector2d_compression") { } TEST_CASE("H5Easy_vector3d") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_vector3d.h5", H5Easy::File::Overwrite); using type = std::vector>>; @@ -132,7 +158,7 @@ TEST_CASE("H5Easy_vector3d") { } TEST_CASE("H5Easy_Attribute_scalar") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_attribute_scalar.h5", H5Easy::File::Overwrite); double a = 1.2345; int b = 12345; @@ -155,7 +181,7 @@ TEST_CASE("H5Easy_Attribute_scalar") { #ifdef H5_USE_XTENSOR TEST_CASE("H5Easy_extend1d") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_extend1d.h5", H5Easy::File::Overwrite); for (size_t i = 0; i < 10; ++i) { H5Easy::dump(file, "/path/to/A", i, {i}); @@ -172,7 +198,7 @@ TEST_CASE("H5Easy_extend1d") { } TEST_CASE("H5Easy_extend2d") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_extend2d.h5", H5Easy::File::Overwrite); for (size_t i = 0; i < 10; ++i) { for (size_t j = 0; j < 5; ++j) { @@ -193,7 +219,7 @@ TEST_CASE("H5Easy_extend2d") { } TEST_CASE("H5Easy_xtensor") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_xtensor.h5", H5Easy::File::Overwrite); xt::xtensor A = 100. * xt::random::randn({20, 5}); xt::xtensor B = A; @@ -209,7 +235,7 @@ TEST_CASE("H5Easy_xtensor") { } TEST_CASE("H5Easy_xarray") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_xarray.h5", H5Easy::File::Overwrite); xt::xarray A = 100. * xt::random::randn({20, 5}); xt::xarray B = A; @@ -225,7 +251,7 @@ TEST_CASE("H5Easy_xarray") { } TEST_CASE("H5Easy_view") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_view.h5", H5Easy::File::Overwrite); xt::xtensor A = 100. * xt::random::randn({20, 5}); auto a = xt::view(A, xt::range(0, 10), xt::range(0, 10)); @@ -238,7 +264,7 @@ TEST_CASE("H5Easy_view") { } TEST_CASE("H5Easy_xtensor_compress") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_xtensor_compress.h5", H5Easy::File::Overwrite); xt::xtensor A = 100. * xt::random::randn({20, 5}); xt::xtensor B = A; @@ -260,7 +286,7 @@ TEST_CASE("H5Easy_xtensor_compress") { } TEST_CASE("H5Easy_Attribute_xtensor") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_attribute_xtensor.h5", H5Easy::File::Overwrite); xt::xtensor A = 100. * xt::random::randn({20, 5}); xt::xtensor B = A; @@ -280,7 +306,7 @@ TEST_CASE("H5Easy_Attribute_xtensor") { #ifdef H5_USE_EIGEN TEST_CASE("H5Easy_Eigen_MatrixX") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_eigen_MatrixX.h5", H5Easy::File::Overwrite); Eigen::MatrixXd A = 100. * Eigen::MatrixXd::Random(20, 5); Eigen::MatrixXi B = A.cast(); @@ -296,7 +322,7 @@ TEST_CASE("H5Easy_Eigen_MatrixX") { } TEST_CASE("H5Easy_Eigen_ArrayXX") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_eigen_ArrayXX.h5", H5Easy::File::Overwrite); Eigen::ArrayXXf A = 100. * Eigen::ArrayXXf::Random(20, 5); Eigen::ArrayXXi B = A.cast(); @@ -312,7 +338,7 @@ TEST_CASE("H5Easy_Eigen_ArrayXX") { } TEST_CASE("H5Easy_Eigen_ArrayX") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_eigen_ArrayX.h5", H5Easy::File::Overwrite); Eigen::ArrayXf A = Eigen::ArrayXf::Random(50); Eigen::ArrayXi B = A.cast(); @@ -329,7 +355,7 @@ TEST_CASE("H5Easy_Eigen_ArrayX") { TEST_CASE("H5Easy_Eigen_VectorX") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_eigen_VectorX.h5", H5Easy::File::Overwrite); Eigen::VectorXd A = 100. * Eigen::VectorXd::Random(20); Eigen::VectorXi B = A.cast(); @@ -348,7 +374,7 @@ TEST_CASE("H5Easy_Eigen_MatrixXRowMajor") { typedef Eigen::Matrix MatrixXd; typedef Eigen::Matrix MatrixXi; - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("H5Easy_Eigen_MatrixXRowMajor.h5", H5Easy::File::Overwrite); MatrixXd A = 100. * MatrixXd::Random(20, 5); MatrixXi B = A.cast(); @@ -367,7 +393,7 @@ TEST_CASE("H5Easy_Eigen_VectorXRowMajor") { typedef Eigen::Matrix VectorXd; typedef Eigen::Matrix VectorXi; - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_eigen_VectorXRowMajor.h5", H5Easy::File::Overwrite); VectorXd A = 100. * VectorXd::Random(20); VectorXi B = A.cast(); @@ -383,7 +409,7 @@ TEST_CASE("H5Easy_Eigen_VectorXRowMajor") { } TEST_CASE("H5Easy_Eigen_Map") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_eigen_Map.h5", H5Easy::File::Overwrite); std::vector A = {1, 2, 3, 4, 5, 6, 7, 8, 9}; Eigen::Map mapped_vector(A.data(), static_cast(A.size())); @@ -396,7 +422,7 @@ TEST_CASE("H5Easy_Eigen_Map") { } TEST_CASE("H5Easy_Attribute_Eigen_MatrixX") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_attribute_eigen_MatrixX.h5", H5Easy::File::Overwrite); Eigen::MatrixXd A = 100. * Eigen::MatrixXd::Random(20, 5); Eigen::MatrixXi B = A.cast(); @@ -415,7 +441,7 @@ TEST_CASE("H5Easy_Attribute_Eigen_MatrixX") { #ifdef H5_USE_OPENCV TEST_CASE("H5Easy_OpenCV_Mat_") { - H5Easy::File file("test.h5", H5Easy::File::Overwrite); + H5Easy::File file("h5easy_opencv_Mat_.h5", H5Easy::File::Overwrite); using T = typename cv::Mat_; @@ -436,6 +462,7 @@ TEST_CASE("H5Easy_OpenCV_Mat_") { H5Easy::dump(file, "/path/to/A", A); H5Easy::dumpAttribute(file, "/path/to/A", "attr", A); + T A_r = H5Easy::load(file, "/path/to/A"); T B_r = H5Easy::loadAttribute(file, "/path/to/A", "attr"); diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five_multi_dims.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five_multi_dims.cpp similarity index 98% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five_multi_dims.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five_multi_dims.cpp index 4a4b8231ce..442f1c9cc5 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five_multi_dims.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five_multi_dims.cpp @@ -10,8 +10,7 @@ #include #include -#include -#include +#include #ifdef H5_USE_BOOST diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five_parallel.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five_parallel.cpp similarity index 97% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five_parallel.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five_parallel.cpp index b5518f48c6..8b096205e8 100644 --- a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_high_five_parallel.cpp +++ b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_high_five_parallel.cpp @@ -13,15 +13,11 @@ #include #include -#include -#include -#include -#include - #include #include #include +#include #include "tests_high_five.hpp" using namespace HighFive; diff --git a/externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_import_public_headers.cpp b/externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_import_public_headers.cpp similarity index 100% rename from externals/coda-oss/modules/drivers/highfive/HighFive-2.7.1/tests/unit/tests_import_public_headers.cpp rename to externals/coda-oss/modules/drivers/highfive/HighFive-2.8.0/tests/unit/tests_import_public_headers.cpp diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/H5Attribute.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/H5Attribute.hpp index c20b6c8f23..810d388ae8 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/H5Attribute.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/H5Attribute.hpp @@ -40,72 +40,204 @@ namespace detail { Attribute make_attribute(hid_t hid); } // namespace detail +/// \brief Class representing an Attribute of a DataSet or Group /// -/// \brief Class representing an attribute of a dataset or group -/// +/// \sa AnnotateTraits::createAttribute, AnnotateTraits::getAttribute, AnnotateTraits::listAttributeNames, AnnotateTraits::hasAttribute, AnnotateTraits::deleteAttribute for create, get, list, check or delete Attribute class Attribute: public Object, public PathTraits { public: const static ObjectType type = ObjectType::Attribute; - /// - /// \brief return the name of the current attribute - /// \return the name of the attribute + /// \brief Get the name of the current Attribute. + /// \code{.cpp} + /// auto attr = dset.createAttribute("my_attribute", DataSpace::From(string_list)); + /// std::cout << attr.getName() << std::endl; // Will print "my_attribute" + /// \endcode + /// \since 2.2.2 std::string getName() const; + /// \brief The number of bytes required to store the attribute in the HDF5 file. + /// \code{.cpp} + /// size_t size = dset.createAttribute("foo", DataSpace(1, 2)).getStorageSize(); + /// \endcode + /// \since 1.0 size_t getStorageSize() const; - /// - /// \brief getDataType - /// \return return the datatype associated with this dataset - /// + /// \brief Get the DataType of the Attribute. + /// \code{.cpp} + /// Attribute attr = dset.createAttribute("foo", DataSpace(1, 2)); + /// auto dtype = attr.getDataType(); // Will be an hdf5 type deduced from int + /// \endcode + /// \since 1.0 DataType getDataType() const; - /// - /// \brief getSpace - /// \return return the dataspace associated with this dataset - /// + /// \brief Get the DataSpace of the current Attribute. + /// \code{.cpp} + /// Attribute attr = dset.createAttribute("foo", DataSpace(1, 2)); + /// auto dspace = attr.getSpace(); // This will be a DataSpace of dimension 1 * 2 + /// \endcode + /// \since 1.0 DataSpace getSpace() const; - /// - /// \brief getMemSpace - /// \return same than getSpace for DataSet, compatibility with Selection - /// class - /// + /// \brief Get the DataSpace of the current Attribute. + /// \note This is an alias of getSpace(). + /// \since 1.0 DataSpace getMemSpace() const; - /// \brief Return the attribute + /// \brief Get the value of the Attribute. + /// \code{.cpp} + /// Attribute attr = dset.getAttribute("foo"); + /// // The value will contains what have been written in the attribute + /// std::vector value = attr.read>(); + /// \endcode + /// \since 2.5.0 template T read() const; + /// \brief Get the value of the Attribute in a buffer. + /// + /// Read the attribute into an existing object. Only available for + /// supported types `T`. If `array` has preallocated the correct amount of + /// memory, then this routine should not trigger reallocation. Otherwise, + /// if supported, the object will be resized. /// - /// Read the attribute into a buffer /// An exception is raised if the numbers of dimension of the buffer and of - /// the attribute are different + /// the attribute are different. /// - /// The array type can be a N-pointer or a N-vector ( e.g int** integer two - /// dimensional array ) + /// \code{.cpp} + /// // Will read into `value` avoiding memory allocation if the dimensions + /// // match, i.e. if the attribute `"foo"` has three element. + /// std::vector value(3); + /// file.getAttribute("foo").read(value); + /// \endcode + /// \since 1.0 template void read(T& array) const; - /// \brief Read the attribute into a buffer + /// \brief Read the attribute into a pre-allocated buffer. + /// \param array A pointer to the first byte of sufficient pre-allocated memory. + /// \param mem_datatype The DataType of the array. + /// + /// \note This is the shallowest wrapper around `H5Aread`. If possible + /// prefer either Attribute::read() const or Attribute::read(T&) const. + /// + /// \code{.cpp} + /// auto attr = file.getAttribute("foo"); + /// + /// // Simulate custom allocation by the application. + /// size_t n_elements = attr.getSpace().getElementCount(); + /// int * ptr = (int*) malloc(n_elements*sizeof(int)); + /// + /// // Read into the pre-allocated memory. + /// attr.read(ptr, mem_datatype); + /// \endcode + /// \since 2.2.2 template - void read(T* array, const DataType& dtype = {}) const; + void read(T* array, const DataType& mem_datatype) const; + /// \brief Read the attribute into a buffer. + /// Behaves like Attribute::read(T*, const DataType&) const but + /// additionally this overload deduces the memory datatype from `T`. /// - /// Write the integrality N-dimension buffer to this attribute - /// An exception is raised if the numbers of dimension of the buffer and of - /// the attribute are different + /// \param array Pointer to the first byte of pre-allocated memory. + /// + /// \note If possible prefer either Attribute::read() const or Attribute::read(T&) const. + /// + /// \code{.cpp} + /// auto attr = file.getAttribute("foo"); + /// + /// // Simulate custom allocation by the application. + /// size_t n_elements = attr.getSpace().getElementCount(); + /// int * ptr = (int*) malloc(n_elements*sizeof(int)); + /// + /// // Read into the pre-allocated memory. + /// attr.read(ptr); + /// \endcode + /// \since 2.2.2 + template + void read(T* array) const; + + /// \brief Write the value into the Attribute. + /// + /// Write the value to the attribute. For supported types `T`, this overload + /// will write the value to the attribute. The datatype and dataspace are + /// deduced automatically. However, since the attribute has already been + /// created, the dimensions of `value` must match those of the attribute. + /// + /// \code{.cpp} + /// // Prefer the fused version if creating and writing the attribute + /// // at the same time. + /// dset.createAttribute("foo", std::vector{1, 2, 3}); /// - /// The array type can be a N-pointer or a N-vector ( e.g int** integer two - /// dimensional array ) + /// // To overwrite the value: + /// std::vector value{4, 5, 6}; + /// dset.getAttribute("foo").write(value); + /// \endcode + /// \since 1.0 template - void write(const T& buffer); + void write(const T& value); - /// \brief Write a buffer to this attribute + /// \brief Write from a raw pointer. + /// + /// Values that have been correctly arranged memory, can be written directly + /// by passing a raw pointer. + /// + /// \param buffer Pointer to the first byte of the value. + /// \param mem_datatype The DataType of the buffer. + /// + /// \note This is the shallowest wrapper around `H5Awrite`. It's useful + /// if you need full control. If possible prefer Attribute::write. + /// + /// \code{.cpp} + /// Attribute attr = dset.createAttribute("foo", DataSpace(2, 3)); + /// + /// // Simulate the application creating `value` and only exposing access + /// // to the raw pointer `ptr`. + /// std::vector> value{{1, 2, 3}, {4, 5, 6}}; + /// int * ptr = (int*) value.data(); + /// + /// // Simply write the bytes to disk. + /// attr.write(ptr, AtomicType()); + /// \endcode + /// \since 2.2.2 template - void write_raw(const T* buffer, const DataType& dtype = {}); + void write_raw(const T* buffer, const DataType& mem_datatype); - /// \brief Get the list of properties for creation of this attribute + /// \brief Write from a raw pointer. + /// + /// Much like Attribute::write_raw(const T*, const DataType&). + /// Additionally, this overload attempts to automatically deduce the + /// datatype of the buffer. Note, that the file datatype is already set. + /// + /// \param buffer Pointer to the first byte. + /// + /// \note If possible prefer Attribute::write. + /// + /// \code{.cpp} + /// // Simulate the application creating `value` and only exposing access + /// // to the raw pointer `ptr`. + /// std::vector> value{{1, 2, 3}, {4, 5, 6}}; + /// int * ptr = (int*) value.data(); + /// + /// // Simply write the bytes to disk. + /// attr.write(ptr); + /// \endcode + /// \since 2.2.2 + template + void write_raw(const T* buffer); + + /// \brief The create property list used for this attribute. + /// + /// Some of HDF5 properties/setting of an attribute are defined by a + /// create property list. This method returns a copy of the create + /// property list used during creation of the attribute. + /// + /// \code{.cpp} + /// auto acpl = attr.getCreatePropertyList(); + /// + /// // For example to create another attribute with the same properties. + /// file.createAttribute("foo", 42, acpl); + /// \endcode + /// \since 2.5.0 AttributeCreateProps getCreatePropertyList() const { return details::get_plist(*this, H5Aget_create_plist); } diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/H5DataSpace.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/H5DataSpace.hpp index 9c16472050..95d04dbbbd 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/H5DataSpace.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/H5DataSpace.hpp @@ -19,90 +19,215 @@ namespace HighFive { +/// \brief Class representing the space (dimensions) of a DataSet /// -/// \brief Class representing the space (dimensions) of a dataset -/// +/// \code{.cpp} +/// // Create a DataSpace of dimension 1 x 2 x 3 +/// DataSpace dspace(1, 2, 3); +/// std::cout << dspace.getElementCount() << std::endl; // Print 1 * 2 * 3 = 6 +/// std::cout << dspace.getNumberDimensions() << std::endl; // Print 3 +/// std::vector dims = dspace.getDimensions(); // dims is {1, 2, 3} +/// \endcode class DataSpace: public Object { public: const static ObjectType type = ObjectType::DataSpace; + /// \brief Magic value to specify that a DataSpace can grow without limit. + /// + /// This value should be used with DataSpace::DataSpace(const std::vector& dims, const + /// std::vector& maxdims); + /// + /// \since 2.0 static const size_t UNLIMITED = SIZE_MAX; - /// dataspace type + /// \brief An enum to create scalar and null DataSpace with DataSpace::DataSpace(DataspaceType dtype). + /// + /// This enum is needed otherwise we will not be able to distringuish between both with normal + /// constructors. Both have a dimension of 0. + /// \since 1.3 enum DataspaceType { - dataspace_scalar, - dataspace_null + dataspace_scalar, ///< Value to create scalar DataSpace + dataspace_null, ///< Value to create null DataSpace // simple dataspace are handle directly from their dimensions }; - /// create a dataspace of N-dimensions - /// Each dimension is configured this way - /// size(dim1) = vec[0] - /// size(dim2) = vec[1] - /// etc... + /// \brief Create a DataSpace of N-dimensions from a std::vector. + /// \param dims Dimensions of the new DataSpace + /// + /// \code{.cpp} + /// // Create a DataSpace with 2 dimensions: 1 and 3 + /// DataSpace(std::vector{1, 3}); + /// \endcode + /// \since 1.0 explicit DataSpace(const std::vector& dims); - // create a dataspace of N-dimensions + /// \brief Create a DataSpace of N-dimensions from a std::array. + /// \param dims Dimensions of the new DataSpace + /// + /// \code{.cpp} + /// // Create a DataSpace with 2 dimensions: 1 and 3 + /// DataSpace(std::array{1, 3}); + /// \endcode + /// \since 2.3 template explicit DataSpace(const std::array& dims); - /// Make sure that DataSpace({1,2,3}) works on GCC. This is - /// the shortcut form of the vector initializer, but one some compilers (gcc) - /// this does not resolve correctly without this constructor. - DataSpace(const std::initializer_list& items); - - /// Allow directly listing 1 or more dimensions to initialize, - /// that is, DataSpace(1,2) means DataSpace(std::vector{1,2}). + /// \brief Create a DataSpace of N-dimensions from an initializer list. + /// \param dims Dimensions of the new DataSpace + /// + /// \code{.cpp} + /// // Create a DataSpace with 2 dimensions: 1 and 3 + /// DataSpace{1, 3}; + /// \endcode + /// \since 2.1 + DataSpace(const std::initializer_list& dims); + + /// \brief Create a DataSpace of N-dimensions from direct values. + /// \param dim1 The first dimension + /// \param dims The following dimensions + /// + /// \code{.cpp} + /// // Create a DataSpace with 2 dimensions: 1 and 3 + /// DataSpace(1, 3); + /// \endcode + /// \since 2.1 template explicit DataSpace(size_t dim1, Args... dims); - /// Create a dataspace from an iterator pair + /// \brief Create a DataSpace from a pair of iterators. + /// \param begin The beginning of the container + /// \param end The end of the container + /// + /// \code{.cpp} + /// // Create a DataSpace with 2 dimensions: 1 and 3 + /// std::vector v{1, 3}; + /// DataSpace(v.begin(), v.end()); + /// \endcode /// - /// Explicitly disable DataSpace(int_like, int_like) from trying to use this constructor + /// \since 2.0 + // Attention: Explicitly disable DataSpace(int_like, int_like) from trying + // to use this constructor template ::value, IT>::type> DataSpace(const IT begin, const IT end); - /// \brief Create a resizable N-dimensional dataspace + /// \brief Create a resizable N-dimensional DataSpace. /// \param dims Initial size of dataspace /// \param maxdims Maximum size of the dataspace + /// + /// \code{.cpp} + /// // Create a DataSpace with 2 dimensions: 1 and 3. + /// // It can later be resized up to a maximum of 10 x 10 + /// DataSpace(std::vector{1, 3}, std::vector{10, 10}); + /// \endcode + /// + /// \see UNLIMITED for a DataSpace that can be resized without limit. + /// \since 2.0 explicit DataSpace(const std::vector& dims, const std::vector& maxdims); + /// \brief Create a scalar or a null DataSpace. /// - /// \brief DataSpace create a scalar dataspace or a null dataset + /// This overload enables creating scalar or null data spaces, both have + /// dimension 0. /// - explicit DataSpace(DataspaceType dtype); - - /// Create a new DataSpace - /// with a different id available for modifications - DataSpace clone() const; + /// \param space_type The value from the enum + /// + /// \code{.cpp} + /// DataSpace(DataspaceType::dataspace_scalar); + /// \endcode + /// + /// \attention Avoid braced intialization in these cases, i.e. + /// \code{.cpp} + /// // This is not a scalar dataset: + /// DataSpace{DataspaceType::dataspace_scalar}; + /// \endcode + /// + /// \since 1.3 + explicit DataSpace(DataspaceType space_type); + /// \brief Create a copy of the DataSpace which will have different id. /// - /// \brief getNumberDimensions - /// \return the number of dimensions in the current dataspace + /// \code{.cpp} + /// DataSpace dspace1(1, 3); + /// auto dspace2 = dspace.clone(); + /// \endcode /// + /// \since 1.0 + DataSpace clone() const; + + /// \brief Returns the number of dimensions of a DataSpace. + /// \code{.cpp} + /// DataSpace dspace(1, 3); + /// size_t number_of_dim = dspace.getNumberDimensions(); // returns 2 + /// \endcode + /// \since 1.0 size_t getNumberDimensions() const; - /// \brief getDimensions - /// \return return a vector of N-element, each element is the size of the - /// associated dataset dimension + /// \brief Returns the size of the dataset in each dimension. + /// + /// For zero-dimensional datasets (e.g. scalar or null datasets) an empty + /// vector is returned. + /// + /// \code{.cpp} + /// DataSpace dspace(1, 3); + /// auto dims = dspace.getDimensions(); // returns {1, 3} + /// \endcode + /// + /// \sa DataSpace::getMaxDimensions + /// + /// \since 1.0 std::vector getDimensions() const; - /// \brief getElementCount - /// \return the total number of elements in the dataspace + /// \brief Return the number of elements in this DataSpace. + /// + /// \code{.cpp} + /// DataSpace dspace(1, 3); + /// size_t elementcount = dspace.getElementCount(); // return 1 x 3 = 3 + /// \endcode + /// \since 2.1 size_t getElementCount() const; - /// \brief getMaxDimensions - /// \return return a vector of N-element, each element is the size of the - /// associated dataset maximum dimension + /// \brief Returns the maximum size of the dataset in each dimension. + /// + /// This is the maximum size a dataset can be extended to, which may be + /// different from the current size of the dataset. + /// + /// \code{.cpp} + /// DataSpace dspace(std::vector{1, 3}, std::vector{UNLIMITED, 10}); + /// dspace.getMaxDimensions(); // Return {UNLIMITED, 10} + /// \endcode + /// + /// \sa DataSpace::getDimensions + /// \since 2.0 std::vector getMaxDimensions() const; - /// Create a dataspace matching a type accepted by details::inspector + /// \brief Automatically deduce the DataSpace from a container/value. + /// + /// Certain containers and scalar values are fully supported by HighFive. + /// For these containers, HighFive can deduce the dimensions from `value`. + /// + /// \code{.cpp} + /// double d = 42.0; + /// std::vector> v = {{4, 5, 6}, {7, 8, 9}}; + /// DataSpace::From(v); // A DataSpace of dimensions 2, 3. + /// DataSpace::From(d); // A scalar dataspace. + /// \endcode + /// + /// \since 1.0 template static DataSpace From(const T& value); + /// \brief Create a DataSpace from a value of type string array. + /// \param string_array An C-array of C-string (null-terminated). + /// + /// \code{.cpp} + /// char string_array[2][10] = {"123456789", "abcdefghi"}; + /// auto dspace = DataSpace::FromCharArrayStrings(string_array); // dspace is a DataSpace of + /// dimensions 2 + /// \endcode + /// \since 2.2 template - static DataSpace FromCharArrayStrings(const char (&)[N][Width]); + static DataSpace FromCharArrayStrings(const char (&string_array)[N][Width]); protected: DataSpace() = default; diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/H5DataType.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/H5DataType.hpp index 43f758e452..886107961b 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/H5DataType.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/H5DataType.hpp @@ -11,9 +11,12 @@ #include #include +#include + #include "H5Object.hpp" #include "bits/H5Utils.hpp" +#include "bits/string_padding.hpp" #include "H5PropertyList.hpp" namespace HighFive { @@ -47,6 +50,7 @@ inline DataTypeClass operator&(DataTypeClass lhs, DataTypeClass rhs) { return static_cast(static_cast(lhs) & static_cast(rhs)); } +class StringType; /// /// \brief HDF5 Data Type @@ -85,6 +89,11 @@ class DataType: public Object { /// bool isFixedLenStr() const; + /// + /// \brief Returns this datatype as a `StringType`. + /// + StringType asStringType() const; + /// /// \brief Check the DataType was default constructed. /// Such value might represent auto-detection of the datatype from a buffer @@ -106,8 +115,67 @@ class DataType: public Object { friend class File; friend class DataSet; friend class CompoundType; + template + friend class NodeTraits; +}; + + +enum class CharacterSet : std::underlying_type::type { + Ascii = H5T_CSET_ASCII, + Utf8 = H5T_CSET_UTF8, +}; + +class StringType: public DataType { + public: + /// + /// \brief For stings return the character set. + /// + CharacterSet getCharacterSet() const; + + /// + /// \brief For fixed length stings return the padding. + /// + StringPadding getPadding() const; + + protected: + using DataType::DataType; + friend class DataType; +}; + +class FixedLengthStringType: public StringType { + public: + /// + /// \brief Create a fixed length string datatype. + /// + /// The string will be `size` bytes long, regardless whether it's ASCII or + /// UTF8. In particular, a string with `n` UFT8 characters in general + /// requires `4*n` bytes. + /// + /// The string padding is subtle, essentially it's just a hint. A + /// nullterminated string is guaranteed to have one `'\0'` which marks the + /// semantic end of the string. The length of the buffer must be at least + /// `size` bytes regardless. HDF5 will read or write `size` bytes, + /// irrespective of the when the `\0` occurs. + /// + /// Note that when writing passing `StringPadding::NullTerminated` is a + /// guarantee to the reader that it contains a `\0`. Therefore, make sure + /// that the string really is nullterminated. Otherwise prefer a + /// null-padded string which only means states that the buffer is filled up + /// with 0 or more `\0`. + FixedLengthStringType(size_t size, + StringPadding padding, + CharacterSet character_set = CharacterSet::Ascii); }; +class VariableLengthStringType: public StringType { + public: + /// + /// \brief Create a variable length string HDF5 datatype. + /// + VariableLengthStringType(CharacterSet character_set = CharacterSet::Ascii); +}; + + /// /// \brief create an HDF5 DataType from a C++ type /// @@ -175,11 +243,14 @@ class CompoundType: public DataType { size_t n_members = static_cast(result); members.reserve(n_members); for (unsigned i = 0; i < n_members; i++) { - const char* name = H5Tget_member_name(_hid, i); + char* name = H5Tget_member_name(_hid, i); size_t offset = H5Tget_member_offset(_hid, i); hid_t member_hid = H5Tget_member_type(_hid, i); DataType member_type{member_hid}; - members.emplace_back(name, member_type, offset); + members.emplace_back(std::string(name), member_type, offset); + if (H5free_memory(name) < 0) { + throw DataTypeException("Could not free names from the compound datatype"); + } } } @@ -250,7 +321,7 @@ class EnumType: public DataType { } EnumType(std::initializer_list t_members) - : EnumType(std::vector({t_members})) {} + : EnumType(std::vector(t_members)) {} /// \brief Commit datatype into the given Object /// \param object Location to commit object into @@ -280,15 +351,20 @@ DataType create_and_check_datatype(); /// Although fixed-len arrays can be created 'raw' without the need for /// this structure, to retrieve results efficiently it must be used. /// +/// \tparam N Size of the string in bytes, including the null character. Note, +/// that all string must be null-terminated. +/// template class FixedLenStringArray { public: FixedLenStringArray() = default; /// - /// \brief Create a FixedStringArray from a raw contiguous buffer + /// \brief Create a FixedStringArray from a raw contiguous buffer. + /// + /// The argument `n_strings` specifies the number of strings. /// - FixedLenStringArray(const char array[][N], std::size_t length); + FixedLenStringArray(const char array[][N], std::size_t n_strings); /// /// \brief Create a FixedStringArray from a sequence of strings. diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/H5File.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/H5File.hpp index d8dac16964..9b393e5a35 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/H5File.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/H5File.hpp @@ -110,6 +110,17 @@ class File: public Object, public NodeTraits, public AnnotateTraits return details::get_plist(*this, H5Fget_access_plist); } + /// \brief Get the size of this file in bytes + size_t getFileSize() const; + + /// \brief Get the amount of tracked, unused space in bytes. + /// + /// Note, this is a wrapper for `H5Fget_freespace` and returns the number + /// bytes in the free space manager. This might be different from the total + /// amount of unused space in the HDF5 file, since the free space manager + /// might not track everything or not track across open-close cycles. + size_t getFreeSpace() const; + protected: File() = default; using Object::Object; diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/H5PropertyList.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/H5PropertyList.hpp index 6122820e5a..53b3c4a137 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/H5PropertyList.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/H5PropertyList.hpp @@ -22,6 +22,67 @@ namespace HighFive { +/// \defgroup PropertyLists Property Lists +/// HDF5 is configured through what they call property lists. In HDF5 the +/// process has four steps: +/// +/// 1. Create a property list. As users we now have an `hid_t` identifying the +/// property list. +/// 2. Set properties as desired. +/// 3. Pass the HID to the HDF5 function to be configured. +/// 4. Free the property list. +/// +/// Note that the mental picture is that one creates a settings object, and +/// then passes those settings to a function such as `H5Dwrite`. In and of +/// themselves the settings don't change the behaviour of HDF5. Rather they +/// need to be used to take affect. +/// +/// The second aspect is that property lists represent any number of related +/// settings, e.g. there's property lists anything related to creating files +/// and another for accessing files, same for creating and accessing datasets, +/// etc. Settings that affect creating files, must be passed a file creation +/// property list, while settings that affect file access require a file access +/// property list. +/// +/// In HighFive the `PropertyList` works similar in that it's a object +/// representing the settings, i.e. internally it's just the property lists +/// HID. Just like in HDF5 one adds the settings to the settings object; and +/// then passes the settings object to the respective method. Example: +/// +/// +/// // Create an object which contains the setting to +/// // open files with MPI-IO. +/// auto fapl = FileAccessProps(); +/// fapl.add(MPIOFileAccess(MPI_COMM_WORLD, MPI_INFO_NULL); +/// +/// // To open a specific file with MPI-IO, we do: +/// auto file = File("foo.h5", File::ReadOnly, fapl); +/// +/// Note that the `MPIOFileAccess` object by itself doesn't affect the +/// `FileAccessProps`. Rather it needs to be explicitly added to the `fapl` +/// (the group of file access related settings), and then the `fapl` needs to +/// be passed to the constructor of `File` for the settings to take affect. +/// +/// This is important to understand when reading properties. Example: +/// +/// // Obtain the file access property list: +/// auto fapl = file.getAccessPropertyList() +/// +/// // Extracts a copy of the collective MPI-IO metadata settings from +/// // the group of file access related setting, i.e. the `fapl`: +/// auto mpio_metadata = MPIOCollectiveMetadata(fapl); +/// +/// if(mpio_metadata.isCollectiveRead()) { +/// // something specific if meta data is read collectively. +/// } +/// +/// // Careful, this only affects the `mpio_metadata` object, but not the +/// // `fapl`, and also not whether `file` uses collective MPI-IO for +/// // metadata. +/// mpio_metadata = MPIOCollectiveMetadata(false, false); +/// +/// @{ + /// /// \brief Types of property lists /// @@ -72,6 +133,26 @@ class PropertyListBase: public Object { friend T details::get_plist(const U&, hid_t (*f)(hid_t)); }; +/// \interface PropertyInterface +/// \brief HDF5 file property object +/// +/// A property is an object which is expected to have a method with the +/// following signature `void apply(hid_t hid) const` +/// +/// \sa Instructions to document C++20 concepts with Doxygen: https://github.com/doxygen/doxygen/issues/2732#issuecomment-509629967 +/// +/// \cond +#if HIGHFIVE_HAS_CONCEPTS && __cplusplus >= 202002L +template +concept PropertyInterface = requires(P p, const hid_t hid) { + {p.apply(hid)}; +}; + +#else +#define PropertyInterface typename +#endif +/// \endcond + /// /// \brief HDF5 property Lists /// @@ -88,8 +169,8 @@ class PropertyList: public PropertyListBase { /// Add a property to this property list. /// A property is an object which is expected to have a method with the /// following signature void apply(hid_t hid) const - /// - template + /// \tparam PropertyInterface + template void add(const P& property); /// @@ -377,6 +458,7 @@ class PageBufferSize { #endif /// \brief Set hints as to how many links to expect and their average length +/// \implements PropertyInterface /// class EstimatedLinkInfo { public: @@ -402,6 +484,7 @@ class EstimatedLinkInfo { }; +/// \implements PropertyInterface class Chunking { public: explicit Chunking(const std::vector& dims); @@ -420,6 +503,7 @@ class Chunking { std::vector _dims; }; +/// \implements PropertyInterface class Deflate { public: explicit Deflate(unsigned level); @@ -431,6 +515,7 @@ class Deflate { const unsigned _level; }; +/// \implements PropertyInterface class Szip { public: explicit Szip(unsigned options_mask = H5_SZIP_EC_OPTION_MASK, @@ -446,6 +531,7 @@ class Szip { const unsigned _pixels_per_block; }; +/// \implements PropertyInterface class Shuffle { public: Shuffle() = default; @@ -460,6 +546,7 @@ class Shuffle { /// The precise time of when HDF5 requests space to store the dataset /// can be configured. Please, consider the upstream documentation for /// `H5Pset_alloc_time`. +/// \implements PropertyInterface class AllocationTime { public: explicit AllocationTime(H5D_alloc_time_t alloc_time); @@ -476,6 +563,7 @@ class AllocationTime { /// Dataset access property to control chunk cache configuration. /// Do not confuse with the similar file access property for H5Pset_cache +/// \implements PropertyInterface class Caching { public: /// https://support.hdfgroup.org/HDF5/doc/RM/H5P/H5Pset_chunk_cache.html for @@ -498,6 +586,7 @@ class Caching { double _w0; }; +/// \implements PropertyInterface class CreateIntermediateGroup { public: explicit CreateIntermediateGroup(bool create = true); @@ -518,6 +607,7 @@ class CreateIntermediateGroup { }; #ifdef H5_HAVE_PARALLEL +/// \implements PropertyInterface class UseCollectiveIO { public: explicit UseCollectiveIO(bool enable = true); @@ -540,6 +630,7 @@ class UseCollectiveIO { /// creation of this object. This object will not update automatically for later data transfers, /// i.e. `H5Pget_mpio_no_collective_cause` is called in the constructor, and not when fetching /// a value, such as `wasCollective`. +/// \implements PropertyInterface class MpioNoCollectiveCause { public: explicit MpioNoCollectiveCause(const DataTransferProps& dxpl); @@ -575,6 +666,7 @@ struct CreationOrder { /// /// Let user retrieve objects by creation order time instead of name. /// +/// \implements PropertyInterface class LinkCreationOrder { public: /// @@ -599,6 +691,44 @@ class LinkCreationOrder { unsigned _flags; }; + +/// +/// \brief Set threshold for attribute storage. +/// +/// HDF5 can store Attributes in the object header (compact) or in the B-tree +/// (dense). This property sets the threshold when attributes are moved to one +/// or the other storage format. +/// +/// Please refer to the upstream documentation of `H5Pset_attr_phase_change` or +/// Section 8 (Attributes) in the User Guide, in particular Subsection 8.5. +/// +/// \implements PropertyInterface +class AttributePhaseChange { + public: + /// + /// \brief Create the property from the threshold values. + /// + /// When the number of attributes hits `max_compact` the attributes are + /// moved to dense storage, once the number drops to below `min_dense` the + /// attributes are moved to compact storage. + AttributePhaseChange(unsigned max_compact, unsigned min_dense); + + /// \brief Extract threshold values from property list. + explicit AttributePhaseChange(const GroupCreateProps& gcpl); + + unsigned max_compact() const; + unsigned min_dense() const; + + private: + friend GroupCreateProps; + void apply(hid_t hid) const; + + unsigned _max_compact; + unsigned _min_dense; +}; + +/// @} + } // namespace HighFive #include "bits/H5PropertyList_misc.hpp" diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/H5Version.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/H5Version.hpp new file mode 100644 index 0000000000..dc238432cb --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/H5Version.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c), 2020 + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#pragma once + +#define HIGHFIVE_VERSION_MAJOR 2 +#define HIGHFIVE_VERSION_MINOR 8 +#define HIGHFIVE_VERSION_PATCH 0 + +/** \brief Concatenated representation of the HighFive version. + * + * \warning The macro `HIGHFIVE_VERSION` by itself isn't valid C/C++. + * + * However, it can be stringified with two layers of macros, e.g., + * \code{.cpp} + * #define STRINGIFY_VALUE(s) STRINGIFY_NAME(s) + * #define STRINGIFY_NAME(s) #s + * + * std::cout << STRINGIFY_VALUE(HIGHFIVE_VERSION) << "\n"; + * \endcode + */ +#define HIGHFIVE_VERSION 2.8.0 + +/** \brief String representation of the HighFive version. + * + * \warning This macro only exists from 2.7.1 onwards. + */ +#define HIGHFIVE_VERSION_STRING "2.8.0" diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Attribute_misc.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Attribute_misc.hpp index 65cdb2d86b..6516788297 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Attribute_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Attribute_misc.hpp @@ -61,8 +61,9 @@ inline T Attribute::read() const { template inline void Attribute::read(T& array) const { const DataSpace& mem_space = getMemSpace(); + auto file_datatype = getDataType(); const details::BufferInfo buffer_info( - getDataType(), + file_datatype, [this]() -> std::string { return this->getName(); }, details::BufferInfo::read); @@ -82,37 +83,43 @@ inline void Attribute::read(T& array) const { return; } - auto r = details::data_converter::get_reader(dims, array); - read(r.get_pointer(), buffer_info.data_type); + auto r = details::data_converter::get_reader(dims, array, file_datatype); + read(r.getPointer(), buffer_info.data_type); // re-arrange results - r.unserialize(); - auto t = create_datatype::base_type>(); + r.unserialize(array); + + auto t = buffer_info.data_type; auto c = t.getClass(); + if (c == DataTypeClass::VarLen || t.isVariableStr()) { #if H5_VERSION_GE(1, 12, 0) // This one have been created in 1.12.0 - (void) H5Treclaim(t.getId(), mem_space.getId(), H5P_DEFAULT, r.get_pointer()); + (void) H5Treclaim(t.getId(), mem_space.getId(), H5P_DEFAULT, r.getPointer()); #else // This one is deprecated since 1.12.0 - (void) H5Dvlen_reclaim(t.getId(), mem_space.getId(), H5P_DEFAULT, r.get_pointer()); + (void) H5Dvlen_reclaim(t.getId(), mem_space.getId(), H5P_DEFAULT, r.getPointer()); #endif } } template -inline void Attribute::read(T* array, const DataType& dtype) const { +inline void Attribute::read(T* array, const DataType& mem_datatype) const { static_assert(!std::is_const::value, "read() requires a non-const structure to read data into"); - using element_type = typename details::inspector::base_type; - // Auto-detect mem datatype if not provided - const DataType& mem_datatype = dtype.empty() ? create_and_check_datatype() - : dtype; if (H5Aread(getId(), mem_datatype.getId(), static_cast(array)) < 0) { HDF5ErrMapper::ToException("Error during HDF5 Read: "); } } +template +inline void Attribute::read(T* array) const { + using element_type = typename details::inspector::base_type; + const DataType& mem_datatype = create_and_check_datatype(); + + read(array, mem_datatype); +} + template inline void Attribute::write(const T& buffer) { const DataSpace& mem_space = getMemSpace(); @@ -121,8 +128,10 @@ inline void Attribute::write(const T& buffer) { return; } + auto file_datatype = getDataType(); + const details::BufferInfo buffer_info( - getDataType(), + file_datatype, [this]() -> std::string { return this->getName(); }, details::BufferInfo::write); @@ -132,18 +141,23 @@ inline void Attribute::write(const T& buffer) { << " into dataset of dimensions " << mem_space.getNumberDimensions(); throw DataSpaceException(ss.str()); } - auto w = details::data_converter::serialize(buffer); - write_raw(w.get_pointer(), buffer_info.data_type); + auto w = details::data_converter::serialize(buffer, file_datatype); + write_raw(w.getPointer(), buffer_info.data_type); } template -inline void Attribute::write_raw(const T* buffer, const DataType& dtype) { - using element_type = typename details::inspector::base_type; - const auto& mem_datatype = dtype.empty() ? create_and_check_datatype() : dtype; - +inline void Attribute::write_raw(const T* buffer, const DataType& mem_datatype) { if (H5Awrite(getId(), mem_datatype.getId(), buffer) < 0) { HDF5ErrMapper::ToException("Error during HDF5 Write: "); } } +template +inline void Attribute::write_raw(const T* buffer) { + using element_type = typename details::inspector::base_type; + const auto& mem_datatype = create_and_check_datatype(); + + write_raw(buffer, mem_datatype); +} + } // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Converter_misc.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Converter_misc.hpp index a46f01174b..00749d1b6d 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Converter_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Converter_misc.hpp @@ -9,899 +9,411 @@ #pragma once #include -#include -#include - -#include "../H5Reference.hpp" -#ifdef H5_USE_BOOST -#include -// starting Boost 1.64, serialization header must come before ublas -#include -#include -#endif -#ifdef H5_USE_EIGEN -#include -#endif + +#include "H5Inspector_misc.hpp" +#include "../H5DataType.hpp" namespace HighFive { namespace details { -inline bool checkDimensions(const std::vector& dims, size_t n_dim_requested) { - size_t n_dim_actual = dims.size(); - - // We should allow reading scalar from shapes like `(1, 1, 1)`. - if (n_dim_requested == 0) { - if (n_dim_actual == 0ul) { - return true; - } - - return size_t(std::count(dims.begin(), dims.end(), 1ul)) == n_dim_actual; - } - - // For non-scalar datasets, we can squeeze away singleton dimension, but - // we never add any. - if (n_dim_actual < n_dim_requested) { - return false; - } - - // Special case for 1-dimensional arrays, which can squeeze `1`s from either - // side simultaneously if needed. - if (n_dim_requested == 1ul) { - return n_dim_actual >= 1ul && - size_t(std::count(dims.begin(), dims.end(), 1ul)) >= n_dim_actual - 1ul; - } - - // All other cases strip front only. This avoid unstable behaviour when - // squeezing singleton dimensions. - size_t n_dim_excess = n_dim_actual - n_dim_requested; - - bool squeeze_back = true; - for (size_t i = 1; i <= n_dim_excess; ++i) { - if (dims[n_dim_actual - i] != 1) { - squeeze_back = false; - break; - } - } - - return squeeze_back; -} - - -inline std::vector squeezeDimensions(const std::vector& dims, - size_t n_dim_requested) { - auto format_error_message = [&]() -> std::string { - return "Can't interpret dims = " + format_vector(dims) + " as " + - std::to_string(n_dim_requested) + "-dimensional."; - }; - - if (n_dim_requested == 0) { - if (!checkDimensions(dims, n_dim_requested)) { - throw std::invalid_argument(format_error_message()); - } - - return {1ul}; - } - - auto n_dim = dims.size(); - if (n_dim < n_dim_requested) { - throw std::invalid_argument(format_error_message()); - } - - if (n_dim_requested == 1ul) { - size_t non_singleton_dim = size_t(-1); - for (size_t i = 0; i < n_dim; ++i) { - if (dims[i] != 1ul) { - if (non_singleton_dim == size_t(-1)) { - non_singleton_dim = i; - } else { - throw std::invalid_argument(format_error_message()); - } - } - } - - return {dims[std::min(non_singleton_dim, n_dim - 1)]}; - } - - size_t n_dim_excess = dims.size() - n_dim_requested; - for (size_t i = 1; i <= n_dim_excess; ++i) { - if (dims[n_dim - i] != 1) { - throw std::invalid_argument(format_error_message()); - } - } - - return std::vector(dims.begin(), - dims.end() - static_cast(n_dim_excess)); -} -} // namespace details +template +struct is_std_string { + static constexpr bool value = + std::is_same::base_type, std::string>::value; +}; +template +struct enable_shallow_copy + : public std::enable_if::value && inspector::is_trivially_copyable, V> {}; -inline size_t compute_total_size(const std::vector& dims) { - return std::accumulate(dims.begin(), dims.end(), size_t{1u}, std::multiplies()); -} +template +struct enable_deep_copy + : public std::enable_if::value && !inspector::is_trivially_copyable, V> {}; -template -using unqualified_t = typename std::remove_const::type>::type; - -/***** -inspector { - using type = T - // base_type is the base type inside c++ (e.g. std::vector => int) - using base_type - // hdf5_type is the base read by hdf5 (c-type) (e.g. std::vector => const char*) - using hdf5_type - - // Number of dimensions starting from here - static constexpr size_t recursive_ndim - // Is the inner type trivially copyable for optimisation - // If this value is true: data() is mandatory - // If this value is false: getSizeVal, getSize, serialize, unserialize are mandatory - static constexpr bool is_trivially_copyable - - // Reading: - // Allocate the value following dims (should be recursive) - static void prepare(type& val, const std::vector dims) - // Return the size of the vector pass to/from hdf5 from a vector of dims - static size_t getSize(const std::vector& dims) - // Return a pointer of the first value of val (for reading) - static hdf5_type* data(type& val) - // Take a serialized vector 'in', some dims and copy value to val (for reading) - static void unserialize(const hdf5_type* in, const std::vector&i, type& val) - - - // Writing: - // Return the size of the vector pass to/from hdf5 from a value - static size_t getSizeVal(const type& val) - // Return a point of the first value of val - static const hdf5_type* data(const type& val) - // Take a val and serialize it inside 'out' - static void serialize(const type& val, hdf5_type* out) - // Return an array of dimensions of the space needed for writing val - static std::vector getDimensions(const type& val) -} -*****/ +template +struct enable_string_copy: public std::enable_if::value, V> {}; -namespace details { -template -struct type_helper { +template +struct ShallowCopyBuffer { using type = unqualified_t; - using base_type = unqualified_t; - using hdf5_type = base_type; - - static constexpr size_t ndim = 0; - static constexpr size_t recursive_ndim = ndim; - static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value; - - static std::vector getDimensions(const type& /* val */) { - return {}; - } - - static size_t getSizeVal(const type& val) { - return compute_total_size(getDimensions(val)); - } - - static size_t getSize(const std::vector& dims) { - return compute_total_size(dims); - } + using hdf5_type = + typename std::conditional::hdf5_type>::type, + typename inspector::hdf5_type>::type; - static void prepare(type& /* val */, const std::vector& /* dims */) {} + ShallowCopyBuffer() = delete; - static hdf5_type* data(type& val) { - static_assert(is_trivially_copyable, "The type is not trivially copyable"); - return &val; - } + explicit ShallowCopyBuffer(typename std::conditional::type val) + : ptr(inspector::data(val)){}; - static const hdf5_type* data(const type& val) { - static_assert(is_trivially_copyable, "The type is not trivially copyable"); - return &val; + hdf5_type* getPointer() const { + return ptr; } - static void serialize(const type& val, hdf5_type* m) { - static_assert(is_trivially_copyable, "The type is not trivially copyable"); - *m = val; + hdf5_type* begin() const { + return getPointer(); } - static void unserialize(const hdf5_type* vec, - const std::vector& /* dims */, - type& val) { - static_assert(is_trivially_copyable, "The type is not trivially copyable"); - val = vec[0]; + void unserialize(T& /* val */) const { + /* nothing to do. */ } -}; -template -struct inspector: type_helper {}; - -enum class Boolean : int8_t { - HighFiveFalse = 0, - HighFiveTrue = 1, + private: + hdf5_type* ptr; }; -template <> -struct inspector: type_helper { - using base_type = Boolean; - using hdf5_type = int8_t; - - static constexpr bool is_trivially_copyable = false; - - static hdf5_type* data(type& /* val */) { - throw DataSpaceException("A boolean cannot be read directly."); - } +template +struct DeepCopyBuffer { + using type = unqualified_t; + using hdf5_type = typename inspector::hdf5_type; - static const hdf5_type* data(const type& /* val */) { - throw DataSpaceException("A boolean cannot be written directly."); - } + explicit DeepCopyBuffer(const std::vector& _dims) + : buffer(inspector::getSize(_dims)) + , dims(_dims) {} - static void unserialize(const hdf5_type* vec, - const std::vector& /* dims */, - type& val) { - val = vec[0] != 0 ? true : false; + hdf5_type* getPointer() { + return buffer.data(); } - static void serialize(const type& val, hdf5_type* m) { - *m = val ? 1 : 0; + hdf5_type const* getPointer() const { + return buffer.data(); } -}; - -template <> -struct inspector: type_helper { - using hdf5_type = const char*; - static hdf5_type* data(type& /* val */) { - throw DataSpaceException("A std::string cannot be read directly."); + hdf5_type* begin() { + return getPointer(); } - static const hdf5_type* data(const type& /* val */) { - throw DataSpaceException("A std::string cannot be written directly."); + hdf5_type const* begin() const { + return getPointer(); } - static void serialize(const type& val, hdf5_type* m) { - *m = val.c_str(); + void unserialize(T& val) const { + inspector::unserialize(buffer.data(), dims, val); } - static void unserialize(const hdf5_type* vec, - const std::vector& /* dims */, - type& val) { - val = vec[0]; - } + private: + std::vector buffer; + std::vector dims; }; -template <> -struct inspector: type_helper { - using hdf5_type = hobj_ref_t; +enum class BufferMode { Read, Write }; - static constexpr bool is_trivially_copyable = false; - static hdf5_type* data(type& /* val */) { - throw DataSpaceException("A Reference cannot be read directly."); - } - - static const hdf5_type* data(const type& /* val */) { - throw DataSpaceException("A Reference cannot be written directly."); - } - - static void serialize(const type& val, hdf5_type* m) { - hobj_ref_t ref; - val.create_ref(&ref); - *m = ref; - } - - static void unserialize(const hdf5_type* vec, - const std::vector& /* dims */, - type& val) { - val = type{vec[0]}; +/// +/// \brief String length in bytes excluding the `\0`. +/// +inline size_t char_buffer_size(char const* const str, size_t max_string_length) { + for (size_t i = 0; i <= max_string_length; ++i) { + if (str[i] == '\0') { + return i; + } } -}; - -template -struct inspector> { - using type = FixedLenStringArray; - using value_type = char*; - using base_type = FixedLenStringArray; - using hdf5_type = char; - static constexpr size_t ndim = 1; - static constexpr size_t recursive_ndim = ndim; - static constexpr bool is_trivially_copyable = false; + return max_string_length; +} - static std::vector getDimensions(const type& val) { - return std::vector{val.size()}; - } - static size_t getSizeVal(const type& val) { - return N * compute_total_size(getDimensions(val)); - } +/// +/// \brief A buffer for reading/writing strings. +/// +/// A string in HDF5 can be represented as a fixed or variable length string. +/// The important difference for this buffer is that `H5D{read,write}` expects +/// different input depending on whether the strings are fixed or variable length. +/// For fixed length strings, it expects an array of chars, i.e. one string +/// packed after the other contiguously. While for variable length strings it +/// expects a list of pointers to the beginning of each string. Variable length +/// string must be null-terminated; because that's how their length is +/// determined. +/// +/// This buffer hides the difference between fixed and variable length strings +/// by having internal data structures available for both cases at compile time. +/// The choice which internal buffer to use is made at runtime. +/// +/// Consider an HDF5 dataset with N fixed-length strings, each of which is M +/// characters long. Then the in-memory strings are copied into an internal +/// buffer of size N*M. If null- or space-padded the buffer should be filled +/// with the appropriate character. This is important if the in-memory strings +/// are less than M characters long. +/// +/// An HDF5 dataset with N variable-length strings (all null-terminated) uses +/// the internal list of pointers to the beginning of each string. Those +/// pointers can either point to the in-memory strings themselves, if those +/// strings are known to be null-terminated. Otherwise the in-memory strings are +/// copied to an internal buffer of null-terminated strings; and the pointer +/// points to the start of the string in the internal buffer. +/// +/// This class is responsible for arranging the strings properly before passing +/// the buffers to HDF5. To keep this class generic, it provides a generic +/// read/write interface to the internal strings, i.e. a pointer with a size. +/// For reading from the buffer the proxy is called `StringConstView`. This +/// proxy object is to be used by the `inspector` to copy from the buffer into +/// the final destination, e.g. an `std::string`. Similarly, there's a proxy +/// object for serializing into the buffer, i.e. the `StringView`. Again the +/// `inspector` is responsible for obtaining the pointer, size and padding of +/// the string. +/// +/// Nomenclature: +/// - size of a string is the number of bytes required to store the string, +/// including the null character for null-terminated strings. +/// +/// - length of a string is the number of bytes without the null character. +/// +/// Note: both 'length' and 'size' are counted in number of bytes, not number +/// of symbols or characters. Even for UTF8 strings. +template +struct StringBuffer { + using type = unqualified_t; + using hdf5_type = typename inspector::hdf5_type; - static size_t getSize(const std::vector& dims) { - return N * compute_total_size(dims); - } + class StringView { + public: + StringView(StringBuffer& _buffer, size_t _i) + : buffer(_buffer) + , i(_i) {} + + /// + /// \brief Assign the in-memory string to the buffer. + /// + /// This method copies the in-memory string to the appropriate + /// internal buffer as needed. + /// + /// The `length` is the length of the string in bytes. + void assign(char const* data, size_t length, StringPadding padding) { + if (buffer.isVariableLengthString()) { + if (padding == StringPadding::NullTerminated) { + buffer.variable_length_pointers[i] = data; + } else { + buffer.variable_length_buffer[i] = std::string(data, length); + buffer.variable_length_pointers[i] = buffer.variable_length_buffer[i].data(); + } + } else if (buffer.isFixedLengthString()) { + // If the buffer is fixed-length and null-terminated, then + // `buffer.string_length` doesn't include the null-character. + if (length > buffer.string_length) { + throw std::invalid_argument("String length too big."); + } - static void prepare(type& /* val */, const std::vector& dims) { - if (dims[0] > N) { - std::ostringstream os; - os << "Size of FixedlenStringArray (" << N << ") is too small for dims (" << dims[0] - << ")."; - throw DataSpaceException(os.str()); + memcpy(&buffer.fixed_length_buffer[i * buffer.string_size], data, length); + } } - } - static hdf5_type* data(type& val) { - return val.data(); - } + private: + StringBuffer& buffer; + size_t i; + }; - static const hdf5_type* data(const type& val) { - return val.data(); - } - static void serialize(const type& val, hdf5_type* m) { - for (size_t i = 0; i < val.size(); ++i) { - std::memcpy(m + i * N, val[i], N); + class StringConstView { + public: + StringConstView(const StringBuffer& _buffer, size_t _i) + : buffer(_buffer) + , i(_i) {} + + /// \brief Pointer to the first byte of the string. + /// + /// The valid indices for this pointer are: 0, ..., length() - 1. + char const* data() const { + if (buffer.isVariableLengthString()) { + return buffer.variable_length_pointers[i]; + } else { + return &buffer.fixed_length_buffer[i * buffer.string_size]; + } } - } - static void unserialize(const hdf5_type* vec, const std::vector& dims, type& val) { - for (size_t i = 0; i < dims[0]; ++i) { - std::array s; - std::memcpy(s.data(), vec + (i * N), N); - val.push_back(s); + /// \brief Length of the string in bytes. + /// + /// Note that for null-terminated strings the "length" doesn't include + /// the null character. Hence, if storing this string as a + /// null-terminated string, the destination buffer needs to be at least + /// `length() + 1` bytes long. + size_t length() const { + if (buffer.isNullTerminated()) { + return char_buffer_size(data(), buffer.string_length); + } else { + return buffer.string_length; + } } - } -}; -template -struct inspector> { - using type = std::vector; - using value_type = unqualified_t; - using base_type = typename inspector::base_type; - using hdf5_type = typename inspector::hdf5_type; - - static constexpr size_t ndim = 1; - static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; - static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && - inspector::is_trivially_copyable; - - static std::vector getDimensions(const type& val) { - std::vector sizes(recursive_ndim, 1ul); - sizes[0] = val.size(); - if (!val.empty()) { - auto s = inspector::getDimensions(val[0]); - std::copy(s.begin(), s.end(), sizes.begin() + 1); - } - return sizes; - } + private: + const StringBuffer& buffer; + size_t i; + }; - static size_t getSizeVal(const type& val) { - return compute_total_size(getDimensions(val)); - } - static size_t getSize(const std::vector& dims) { - return compute_total_size(dims); - } + class Iterator { + public: + Iterator(StringBuffer& _buffer, size_t _pos) + : buffer(_buffer) + , pos(_pos) {} - static void prepare(type& val, const std::vector& dims) { - val.resize(dims[0]); - std::vector next_dims(dims.begin() + 1, dims.end()); - for (auto&& e: val) { - inspector::prepare(e, next_dims); + Iterator operator+(size_t n_strings) const { + return Iterator(buffer, pos + n_strings); } - } - static hdf5_type* data(type& val) { - return inspector::data(val[0]); - } - - static const hdf5_type* data(const type& val) { - return inspector::data(val[0]); - } - - static void serialize(const type& val, hdf5_type* m) { - size_t subsize = inspector::getSizeVal(val[0]); - for (auto&& e: val) { - inspector::serialize(e, m); - m += subsize; + void operator+=(size_t n_strings) { + pos += n_strings; } - } - static void unserialize(const hdf5_type* vec_align, - const std::vector& dims, - type& val) { - std::vector next_dims(dims.begin() + 1, dims.end()); - size_t next_size = compute_total_size(next_dims); - for (size_t i = 0; i < dims[0]; ++i) { - inspector::unserialize(vec_align + i * next_size, next_dims, val[i]); + StringView operator*() { + return StringView(buffer, pos); } - } -}; -template <> -struct inspector> { - using type = std::vector; - using value_type = bool; - using base_type = Boolean; - using hdf5_type = uint8_t; - - static constexpr size_t ndim = 1; - static constexpr size_t recursive_ndim = ndim; - static constexpr bool is_trivially_copyable = false; - - static std::vector getDimensions(const type& val) { - std::vector sizes{val.size()}; - return sizes; - } - - static size_t getSizeVal(const type& val) { - return val.size(); - } - - static size_t getSize(const std::vector& dims) { - if (dims.size() > 1) { - throw DataSpaceException("std::vector is only 1 dimension."); + StringConstView operator*() const { + return StringConstView(buffer, pos); } - return dims[0]; - } - static void prepare(type& val, const std::vector& dims) { - if (dims.size() > 1) { - throw DataSpaceException("std::vector is only 1 dimension."); - } - val.resize(dims[0]); - } - - static hdf5_type* data(type& /* val */) { - throw DataSpaceException("A std::vector cannot be read directly."); - } - - static const hdf5_type* data(const type& /* val */) { - throw DataSpaceException("A std::vector cannot be written directly."); - } + private: + StringBuffer& buffer; + size_t pos; + }; - static void serialize(const type& val, hdf5_type* m) { - for (size_t i = 0; i < val.size(); ++i) { - m[i] = val[i] ? 1 : 0; + StringBuffer(std::vector _dims, const DataType& _file_datatype) + : file_datatype(_file_datatype.asStringType()) + , padding(file_datatype.getPadding()) + , string_size(file_datatype.isVariableStr() ? size_t(-1) : file_datatype.getSize()) + , string_length(string_size - size_t(isNullTerminated())) + , dims(_dims) { + if (string_size == 0 && isNullTerminated()) { + throw DataTypeException( + "Fixed-length, null-terminated need at least one byte to store the " + "null-character."); + } + + auto n_strings = compute_total_size(dims); + if (isVariableLengthString()) { + variable_length_buffer.resize(n_strings); + variable_length_pointers.resize(n_strings); + } else { + char pad = padding == StringPadding::SpacePadded ? ' ' : '\0'; + fixed_length_buffer.assign(n_strings * string_size, pad); } } - static void unserialize(const hdf5_type* vec_align, - const std::vector& dims, - type& val) { - for (size_t i = 0; i < dims[0]; ++i) { - val[i] = vec_align[i] != 0 ? true : false; - } + bool isVariableLengthString() const { + return file_datatype.isVariableStr(); } -}; -template -struct inspector> { - using type = std::array; - using value_type = unqualified_t; - using base_type = typename inspector::base_type; - using hdf5_type = typename inspector::hdf5_type; - - static constexpr size_t ndim = 1; - static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; - static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && - inspector::is_trivially_copyable; - - static std::vector getDimensions(const type& val) { - std::vector sizes{N}; - if (!val.empty()) { - auto s = inspector::getDimensions(val[0]); - sizes.insert(sizes.end(), s.begin(), s.end()); - } - return sizes; + bool isFixedLengthString() const { + return file_datatype.isFixedLenStr(); } - static size_t getSizeVal(const type& val) { - return compute_total_size(getDimensions(val)); + bool isNullTerminated() const { + return file_datatype.getPadding() == StringPadding::NullTerminated; } - static size_t getSize(const std::vector& dims) { - return compute_total_size(dims); - } - static void prepare(type& /* val */, const std::vector& dims) { - if (dims[0] > N) { - std::ostringstream os; - os << "Size of std::array (" << N << ") is too small for dims (" << dims[0] << ")."; - throw DataSpaceException(os.str()); + void* getPointer() { + if (file_datatype.isVariableStr()) { + return variable_length_pointers.data(); + } else { + return fixed_length_buffer.data(); } } - static hdf5_type* data(type& val) { - return inspector::data(val[0]); + Iterator begin() { + return Iterator(*this, 0ul); } - static const hdf5_type* data(const type& val) { - return inspector::data(val[0]); + void unserialize(T& val) { + inspector::unserialize(begin(), dims, val); } - static void serialize(const type& val, hdf5_type* m) { - size_t subsize = inspector::getSizeVal(val[0]); - for (auto& e: val) { - inspector::serialize(e, m); - m += subsize; - } - } + private: + StringType file_datatype; + StringPadding padding; + size_t string_size; // Size of buffer required to store the string. + // Meaningful for fixed length strings only. + size_t string_length; // Semantic length of string. + std::vector dims; - static void unserialize(const hdf5_type* vec_align, - const std::vector& dims, - type& val) { - if (dims[0] != N) { - std::ostringstream os; - os << "Impossible to pair DataSet with " << dims[0] << " elements into an array with " - << N << " elements."; - throw DataSpaceException(os.str()); - } - std::vector next_dims(dims.begin() + 1, dims.end()); - size_t next_size = compute_total_size(next_dims); - for (size_t i = 0; i < dims[0]; ++i) { - inspector::unserialize(vec_align + i * next_size, next_dims, val[i]); - } - } + std::vector fixed_length_buffer; + std::vector variable_length_buffer; + std::vector< + typename std::conditional::type*> + variable_length_pointers; }; -// Cannot be use for reading -template -struct inspector { - using type = T*; - using value_type = unqualified_t; - using base_type = typename inspector::base_type; - using hdf5_type = typename inspector::hdf5_type; - - static constexpr size_t ndim = 1; - static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; - static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && - inspector::is_trivially_copyable; - - static size_t getSizeVal(const type& /* val */) { - throw DataSpaceException("Not possible to have size of a T*"); - } - static std::vector getDimensions(const type& /* val */) { - throw DataSpaceException("Not possible to have size of a T*"); - } +template +struct Writer; - static const hdf5_type* data(const type& val) { - return reinterpret_cast(val); - } +template +struct Writer::type>: public ShallowCopyBuffer { + private: + using super = ShallowCopyBuffer; - /* it works because there is only T[][][] currently - we will fix it one day */ - static void serialize(const type& /* val */, hdf5_type* /* m */) { - throw DataSpaceException("Not possible to serialize a T*"); - } + public: + explicit Writer(const T& val, const DataType& /* file_datatype */) + : super(val){}; }; -// Cannot be use for reading -template -struct inspector { - using type = T[N]; - using value_type = unqualified_t; - using base_type = typename inspector::base_type; - using hdf5_type = typename inspector::hdf5_type; - - static constexpr size_t ndim = 1; - static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; - static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && - inspector::is_trivially_copyable; - - static size_t getSizeVal(const type& val) { - return compute_total_size(getDimensions(val)); - } - - static std::vector getDimensions(const type& val) { - std::vector sizes{N}; - if (N > 0) { - auto s = inspector::getDimensions(val[0]); - sizes.insert(sizes.end(), s.begin(), s.end()); - } - return sizes; - } - - static const hdf5_type* data(const type& val) { - return inspector::data(val[0]); - } - - /* it works because there is only T[][][] currently - we will fix it one day */ - static void serialize(const type& val, hdf5_type* m) { - size_t subsize = inspector::getSizeVal(val[0]); - for (size_t i = 0; i < N; ++i) { - inspector::serialize(val[i], m + i * subsize); - } +template +struct Writer::type>: public DeepCopyBuffer { + explicit Writer(const T& val, const DataType& /* file_datatype */) + : DeepCopyBuffer(inspector::getDimensions(val)) { + inspector::serialize(val, this->begin()); } }; -#ifdef H5_USE_EIGEN -template -struct inspector> { - using type = Eigen::Matrix; - using value_type = T; - using base_type = typename inspector::base_type; - using hdf5_type = base_type; - - static constexpr size_t ndim = 2; - static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; - static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && - inspector::is_trivially_copyable; - - static std::vector getDimensions(const type& val) { - std::vector sizes{static_cast(val.rows()), static_cast(val.cols())}; - auto s = inspector::getDimensions(val.data()[0]); - sizes.insert(sizes.end(), s.begin(), s.end()); - return sizes; - } - - static size_t getSizeVal(const type& val) { - return compute_total_size(getDimensions(val)); - } - - static size_t getSize(const std::vector& dims) { - return compute_total_size(dims); - } - - static void prepare(type& val, const std::vector& dims) { - if (dims[0] != static_cast(val.rows()) || - dims[1] != static_cast(val.cols())) { - val.resize(static_cast(dims[0]), - static_cast(dims[1])); - } - } - - static hdf5_type* data(type& val) { - return inspector::data(*val.data()); - } - - static const hdf5_type* data(const type& val) { - return inspector::data(*val.data()); - } - - static void serialize(const type& val, hdf5_type* m) { - std::memcpy(m, val.data(), static_cast(val.size()) * sizeof(hdf5_type)); - } - - static void unserialize(const hdf5_type* vec_align, - const std::vector& dims, - type& val) { - if (dims.size() < 2) { - std::ostringstream os; - os << "Impossible to pair DataSet with " << dims.size() - << " dimensions into an eigen-matrix."; - throw DataSpaceException(os.str()); - } - std::memcpy(val.data(), vec_align, compute_total_size(dims) * sizeof(hdf5_type)); +template +struct Writer::type>: public StringBuffer { + explicit Writer(const T& val, const DataType& _file_datatype) + : StringBuffer(inspector::getDimensions(val), _file_datatype) { + inspector::serialize(val, this->begin()); } }; -#endif - -#ifdef H5_USE_BOOST -template -struct inspector> { - using type = boost::multi_array; - using value_type = T; - using base_type = typename inspector::base_type; - using hdf5_type = typename inspector::hdf5_type; - - static constexpr size_t ndim = Dims; - static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; - static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && - inspector::is_trivially_copyable; - - static std::vector getDimensions(const type& val) { - std::vector sizes; - for (size_t i = 0; i < ndim; ++i) { - sizes.push_back(val.shape()[i]); - } - auto s = inspector::getDimensions(val.data()[0]); - sizes.insert(sizes.end(), s.begin(), s.end()); - return sizes; - } - static size_t getSizeVal(const type& val) { - return compute_total_size(getDimensions(val)); - } - - static size_t getSize(const std::vector& dims) { - return compute_total_size(dims); - } - - static void prepare(type& val, const std::vector& dims) { - if (dims.size() < ndim) { - std::ostringstream os; - os << "Only '" << dims.size() << "' given but boost::multi_array is of size '" << ndim - << "'."; - throw DataSpaceException(os.str()); - } - boost::array ext; - std::copy(dims.begin(), dims.begin() + ndim, ext.begin()); - val.resize(ext); - std::vector next_dims(dims.begin() + Dims, dims.end()); - std::size_t size = std::accumulate(dims.begin(), - dims.begin() + Dims, - std::size_t{1}, - std::multiplies()); - for (size_t i = 0; i < size; ++i) { - inspector::prepare(*(val.origin() + i), next_dims); - } - } - - static hdf5_type* data(type& val) { - return inspector::data(*val.data()); - } - - static const hdf5_type* data(const type& val) { - return inspector::data(*val.data()); - } - - static void serialize(const type& val, hdf5_type* m) { - size_t size = val.num_elements(); - size_t subsize = inspector::getSizeVal(*val.origin()); - for (size_t i = 0; i < size; ++i) { - inspector::serialize(*(val.origin() + i), m + i * subsize); - } - } - - static void unserialize(const hdf5_type* vec_align, - const std::vector& dims, - type& val) { - std::vector next_dims(dims.begin() + ndim, dims.end()); - size_t subsize = compute_total_size(next_dims); - for (size_t i = 0; i < val.num_elements(); ++i) { - inspector::unserialize(vec_align + i * subsize, - next_dims, - *(val.origin() + i)); - } - } -}; +template +struct Reader; template -struct inspector> { - using type = boost::numeric::ublas::matrix; - using value_type = unqualified_t; - using base_type = typename inspector::base_type; - using hdf5_type = typename inspector::hdf5_type; - - static constexpr size_t ndim = 2; - static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; - static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && - inspector::is_trivially_copyable; - - static std::vector getDimensions(const type& val) { - std::vector sizes{val.size1(), val.size2()}; - auto s = inspector::getDimensions(val(0, 0)); - sizes.insert(sizes.end(), s.begin(), s.end()); - return sizes; - } - - static size_t getSizeVal(const type& val) { - return compute_total_size(getDimensions(val)); - } - - static size_t getSize(const std::vector& dims) { - return compute_total_size(dims); - } - - static void prepare(type& val, const std::vector& dims) { - if (dims.size() < ndim) { - std::ostringstream os; - os << "Impossible to pair DataSet with " << dims.size() << " dimensions into a " << ndim - << " boost::numeric::ublas::matrix"; - throw DataSpaceException(os.str()); - } - val.resize(dims[0], dims[1], false); - } - - static hdf5_type* data(type& val) { - return inspector::data(val(0, 0)); - } - - static const hdf5_type* data(const type& val) { - return inspector::data(val(0, 0)); - } - - static void serialize(const type& val, hdf5_type* m) { - size_t size = val.size1() * val.size2(); - size_t subsize = inspector::getSizeVal(val(0, 0)); - for (size_t i = 0; i < size; ++i) { - inspector::serialize(*(&val(0, 0) + i), m + i * subsize); - } - } - - static void unserialize(const hdf5_type* vec_align, - const std::vector& dims, - type& val) { - std::vector next_dims(dims.begin() + ndim, dims.end()); - size_t subsize = compute_total_size(next_dims); - size_t size = val.size1() * val.size2(); - for (size_t i = 0; i < size; ++i) { - inspector::unserialize(vec_align + i * subsize, - next_dims, - *(&val(0, 0) + i)); - } - } +struct Reader::type>: public ShallowCopyBuffer { + private: + using super = ShallowCopyBuffer; + using type = typename super::type; + + public: + Reader(const std::vector&, type& val, const DataType& /* file_datatype */) + : super(val) {} }; -#endif template -struct Writer { - using hdf5_type = typename inspector::hdf5_type; - const hdf5_type* get_pointer() { - if (vec.empty()) { - return ptr; - } else { - return vec.data(); - } - } - std::vector vec{}; - const hdf5_type* ptr{nullptr}; +struct Reader::type>: public DeepCopyBuffer { + private: + using super = DeepCopyBuffer; + using type = typename super::type; + + public: + Reader(const std::vector& _dims, type&, const DataType& /* file_datatype */) + : super(_dims) {} }; -template -struct Reader { - using type = unqualified_t; - using hdf5_type = typename inspector::hdf5_type; - - Reader(const std::vector& _dims, type& _val) - : dims(_dims) - , val(_val) {} - hdf5_type* get_pointer() { - if (vec.empty()) { - return inspector::data(val); - } else { - return vec.data(); - } - } - - void unserialize() { - if (!vec.empty()) { - inspector::unserialize(vec.data(), dims, val); - } - } - - std::vector dims{}; - std::vector vec{}; - type& val{}; +template +struct Reader::type>: public StringBuffer { + public: + explicit Reader(const std::vector& _dims, + const T& /* val */, + const DataType& _file_datatype) + : StringBuffer(_dims, _file_datatype) {} }; struct data_converter { template - static typename std::enable_if::is_trivially_copyable, Writer>::type serialize( - const typename inspector::type& val) { - Writer w; - w.ptr = inspector::data(val); - return w; + static Writer serialize(const typename inspector::type& val, + const DataType& file_datatype) { + return Writer(val, file_datatype); } template - static typename std::enable_if::is_trivially_copyable, Writer>::type serialize( - const typename inspector::type& val) { - Writer w; - w.vec.resize(inspector::getSizeVal(val)); - inspector::serialize(val, w.vec.data()); - return w; - } - - template - static - typename std::enable_if>::is_trivially_copyable, Reader>::type - get_reader(const std::vector& dims, T& val) { - auto effective_dims = details::squeezeDimensions(dims, inspector::recursive_ndim); - Reader r(effective_dims, val); - inspector::prepare(r.val, effective_dims); - return r; - } - - template - static typename std::enable_if>::is_trivially_copyable, - Reader>::type - get_reader(const std::vector& dims, T& val) { + static Reader get_reader(const std::vector& dims, + T& val, + const DataType& file_datatype) { + // TODO Use bufferinfo for recursive_ndim auto effective_dims = details::squeezeDimensions(dims, inspector::recursive_ndim); - - Reader r(effective_dims, val); - inspector::prepare(r.val, effective_dims); - r.vec.resize(inspector::getSize(effective_dims)); - return r; + inspector::prepare(val, effective_dims); + return Reader(effective_dims, val, file_datatype); } }; diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5DataType_misc.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5DataType_misc.hpp index afe200c800..8535d617ab 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5DataType_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5DataType_misc.hpp @@ -22,10 +22,68 @@ #include #endif -#include "H5Converter_misc.hpp" +#include "H5Inspector_misc.hpp" namespace HighFive { +namespace detail { + +inline hid_t h5t_copy(hid_t original) { + auto copy = H5Tcopy(original); + if (copy == H5I_INVALID_HID) { + HDF5ErrMapper::ToException("Error copying datatype."); + } + + return copy; +} + +inline hsize_t h5t_get_size(hid_t hid) { + hsize_t size = H5Tget_size(hid); + if (size == 0) { + HDF5ErrMapper::ToException("Error getting size of datatype."); + } + + return size; +} + +inline H5T_cset_t h5t_get_cset(hid_t hid) { + auto cset = H5Tget_cset(hid); + if (cset == H5T_CSET_ERROR) { + HDF5ErrMapper::ToException("Error getting cset of datatype."); + } + + return cset; +} + +inline H5T_str_t h5t_get_strpad(hid_t hid) { + auto strpad = H5Tget_strpad(hid); + if (strpad == H5T_STR_ERROR) { + HDF5ErrMapper::ToException("Error getting strpad of datatype."); + } + + return strpad; +} + +inline void h5t_set_size(hid_t hid, hsize_t size) { + if (H5Tset_size(hid, size) < 0) { + HDF5ErrMapper::ToException("Error setting size of datatype."); + } +} + +inline void h5t_set_cset(hid_t hid, H5T_cset_t cset) { + if (H5Tset_cset(hid, cset) < 0) { + HDF5ErrMapper::ToException("Error setting cset of datatype."); + } +} + +inline void h5t_set_strpad(hid_t hid, H5T_str_t strpad) { + if (H5Tset_strpad(hid, strpad) < 0) { + HDF5ErrMapper::ToException("Error setting strpad of datatype."); + } +} +} // namespace detail + + namespace { // unnamed inline DataTypeClass convert_type_class(const H5T_class_t& tclass); inline std::string type_class_string(DataTypeClass); @@ -41,7 +99,7 @@ inline DataTypeClass DataType::getClass() const { } inline size_t DataType::getSize() const { - return H5Tget_size(_hid); + return detail::h5t_get_size(_hid); } inline bool DataType::operator==(const DataType& other) const { @@ -68,68 +126,110 @@ inline bool DataType::isReference() const { return H5Tequal(_hid, H5T_STD_REF_OBJ) > 0; } +inline StringType DataType::asStringType() const { + if (getClass() != DataTypeClass::String) { + throw DataTypeException("Invalid conversion to StringType."); + } + + if (isValid() && H5Iinc_ref(_hid) < 0) { + throw ObjectException("Reference counter increase failure"); + } + + return StringType(_hid); +} + inline std::string DataType::string() const { return type_class_string(getClass()) + std::to_string(getSize() * 8); } +inline StringPadding StringType::getPadding() const { + return StringPadding(detail::h5t_get_strpad(_hid)); +} + +inline CharacterSet StringType::getCharacterSet() const { + return CharacterSet(detail::h5t_get_cset(_hid)); +} + +inline FixedLengthStringType::FixedLengthStringType(size_t size, + StringPadding padding, + CharacterSet character_set) { + if (size == 0 && padding == StringPadding::NullTerminated) { + throw DataTypeException( + "Fixed-length, null-terminated need at least one byte to store the null-character."); + } + + _hid = detail::h5t_copy(H5T_C_S1); + + detail::h5t_set_size(_hid, hsize_t(size)); + detail::h5t_set_cset(_hid, H5T_cset_t(character_set)); + detail::h5t_set_strpad(_hid, H5T_str_t(padding)); +} + +inline VariableLengthStringType::VariableLengthStringType(CharacterSet character_set) { + _hid = detail::h5t_copy(H5T_C_S1); + + detail::h5t_set_size(_hid, H5T_VARIABLE); + detail::h5t_set_cset(_hid, H5T_cset_t(character_set)); +} + // char mapping template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_CHAR); + _hid = detail::h5t_copy(H5T_NATIVE_CHAR); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_SCHAR); + _hid = detail::h5t_copy(H5T_NATIVE_SCHAR); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_UCHAR); + _hid = detail::h5t_copy(H5T_NATIVE_UCHAR); } // short mapping template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_SHORT); + _hid = detail::h5t_copy(H5T_NATIVE_SHORT); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_USHORT); + _hid = detail::h5t_copy(H5T_NATIVE_USHORT); } // integer mapping template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_INT); + _hid = detail::h5t_copy(H5T_NATIVE_INT); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_UINT); + _hid = detail::h5t_copy(H5T_NATIVE_UINT); } // long mapping template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_LONG); + _hid = detail::h5t_copy(H5T_NATIVE_LONG); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_ULONG); + _hid = detail::h5t_copy(H5T_NATIVE_ULONG); } // long long mapping template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_LLONG); + _hid = detail::h5t_copy(H5T_NATIVE_LLONG); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_ULLONG); + _hid = detail::h5t_copy(H5T_NATIVE_ULLONG); } // half-float, float, double and long double mapping @@ -138,11 +238,11 @@ using float16_t = half_float::half; template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_FLOAT); + _hid = detail::h5t_copy(H5T_NATIVE_FLOAT); // Sign position, exponent position, exponent size, mantissa position, mantissa size H5Tset_fields(_hid, 15, 10, 5, 0, 10); // Total datatype size (in bytes) - H5Tset_size(_hid, 2); + detail::h5t_set_size(_hid, 2); // Floating point exponent bias H5Tset_ebias(_hid, 15); } @@ -150,17 +250,17 @@ inline AtomicType::AtomicType() { template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_FLOAT); + _hid = detail::h5t_copy(H5T_NATIVE_FLOAT); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_DOUBLE); + _hid = detail::h5t_copy(H5T_NATIVE_DOUBLE); } template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_LDOUBLE); + _hid = detail::h5t_copy(H5T_NATIVE_LDOUBLE); } // std string @@ -173,7 +273,7 @@ inline AtomicType::AtomicType() { // std byte template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_NATIVE_B8); + _hid = detail::h5t_copy(H5T_NATIVE_B8); } #endif @@ -200,8 +300,8 @@ class AtomicType>: public DataType { : DataType( CompoundType({{"r", create_datatype(), 0}, {"i", create_datatype(), sizeof(T)}}, sizeof(std::complex))) { - static_assert(std::is_floating_point::value, - "std::complex accepts only floating point numbers."); + static_assert(std::is_arithmetic::value, + "std::complex accepts only floating point and integral numbers."); } }; @@ -230,18 +330,15 @@ inline FixedLenStringArray::FixedLenStringArray(const char array[][N], std::s template inline FixedLenStringArray::FixedLenStringArray(const std::string* iter_begin, const std::string* iter_end) { - datavec.resize(static_cast(iter_end - iter_begin)); - for (auto& dst_array: datavec) { - const char* src = (iter_begin++)->c_str(); - const size_t length = std::min(N - 1, std::strlen(src)); - std::memcpy(dst_array.data(), src, length); - dst_array[length] = 0; + datavec.reserve(static_cast(iter_end - iter_begin)); + for (std::string const* it = iter_begin; it != iter_end; ++it) { + push_back(*it); } } template inline FixedLenStringArray::FixedLenStringArray(const std::vector& vec) - : FixedLenStringArray(&vec.front(), &vec.back()) {} + : FixedLenStringArray(vec.data(), vec.data() + vec.size()) {} template inline FixedLenStringArray::FixedLenStringArray( @@ -271,7 +368,7 @@ inline std::string FixedLenStringArray::getString(std::size_t i) const { // Reference mapping template <> inline AtomicType::AtomicType() { - _hid = H5Tcopy(H5T_STD_REF_OBJ); + _hid = detail::h5t_copy(H5T_STD_REF_OBJ); } inline size_t find_first_atomic_member_size(hid_t hid) { @@ -294,7 +391,7 @@ inline size_t find_first_atomic_member_size(hid_t hid) { } else if (H5Tget_class(hid) == H5T_STRING) { return 1; } - return H5Tget_size(hid); + return detail::h5t_get_size(hid); } // Calculate the padding required to align an element of a struct @@ -325,7 +422,7 @@ inline void CompoundType::create(size_t size) { // Do a first pass to find the total size of the compound datatype for (auto& member: members) { - size_t member_size = H5Tget_size(member.base_type.getId()); + size_t member_size = detail::h5t_get_size(member.base_type.getId()); if (member_size == 0) { throw DataTypeException("Cannot get size of DataType with hid: " + @@ -393,12 +490,9 @@ inline void EnumType::commit(const Object& object, const std::string& name) c namespace { inline hid_t create_string(size_t length) { - hid_t _hid = H5Tcopy(H5T_C_S1); - if (H5Tset_size(_hid, length) < 0) { - HDF5ErrMapper::ToException("Unable to define datatype size to variable"); - } - // define encoding to UTF-8 by default - H5Tset_cset(_hid, H5T_CSET_UTF8); + hid_t _hid = detail::h5t_copy(H5T_C_S1); + detail::h5t_set_size(_hid, length); + detail::h5t_set_cset(_hid, H5T_CSET_UTF8); return _hid; } diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Dataspace_misc.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Dataspace_misc.hpp index a72054ad29..0fdcacefdb 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Dataspace_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Dataspace_misc.hpp @@ -62,9 +62,9 @@ inline DataSpace::DataSpace(const std::vector& dims, const std::vector DataSpace::getDimensions() const { } inline size_t DataSpace::getElementCount() const { - const std::vector& dims = getDimensions(); - return std::accumulate(dims.begin(), dims.end(), size_t{1u}, std::multiplies()); + hssize_t nelements = H5Sget_simple_extent_npoints(_hid); + if (nelements < 0) { + HDF5ErrMapper::ToException( + "Unable to get number of elements in dataspace"); + } + + return static_cast(nelements); } inline std::vector DataSpace::getMaxDimensions() const { diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5File_misc.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5File_misc.hpp index a63338b82b..b90792a712 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5File_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5File_misc.hpp @@ -127,4 +127,22 @@ inline void File::flush() { } } +inline size_t File::getFileSize() const { + hsize_t sizeValue = 0; + if (H5Fget_filesize(_hid, &sizeValue) < 0) { + HDF5ErrMapper::ToException( + std::string("Unable to retrieve size of file " + getName())); + } + return static_cast(sizeValue); +} + +inline size_t File::getFreeSpace() const { + hssize_t unusedSize = H5Fget_freespace(_hid); + if (unusedSize < 0) { + HDF5ErrMapper::ToException( + std::string("Unable to retrieve unused space of file " + getName())); + } + return static_cast(unusedSize); +} + } // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Inspector_misc.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Inspector_misc.hpp new file mode 100644 index 0000000000..05ed6bc3ec --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Inspector_misc.hpp @@ -0,0 +1,858 @@ +/* + * Copyright (c) 2022 Blue Brain Project + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "../H5Reference.hpp" + +#include "string_padding.hpp" + +#ifdef H5_USE_BOOST +#include +// starting Boost 1.64, serialization header must come before ublas +#include +#include +#endif +#ifdef H5_USE_EIGEN +#include +#endif + + +namespace HighFive { + +namespace details { + +inline bool checkDimensions(const std::vector& dims, size_t n_dim_requested) { + size_t n_dim_actual = dims.size(); + + // We should allow reading scalar from shapes like `(1, 1, 1)`. + if (n_dim_requested == 0) { + if (n_dim_actual == 0ul) { + return true; + } + + return size_t(std::count(dims.begin(), dims.end(), 1ul)) == n_dim_actual; + } + + // For non-scalar datasets, we can squeeze away singleton dimension, but + // we never add any. + if (n_dim_actual < n_dim_requested) { + return false; + } + + // Special case for 1-dimensional arrays, which can squeeze `1`s from either + // side simultaneously if needed. + if (n_dim_requested == 1ul) { + return n_dim_actual >= 1ul && + size_t(std::count(dims.begin(), dims.end(), 1ul)) >= n_dim_actual - 1ul; + } + + // All other cases strip front only. This avoid unstable behaviour when + // squeezing singleton dimensions. + size_t n_dim_excess = n_dim_actual - n_dim_requested; + + bool squeeze_back = true; + for (size_t i = 1; i <= n_dim_excess; ++i) { + if (dims[n_dim_actual - i] != 1) { + squeeze_back = false; + break; + } + } + + return squeeze_back; +} + + +inline std::vector squeezeDimensions(const std::vector& dims, + size_t n_dim_requested) { + auto format_error_message = [&]() -> std::string { + return "Can't interpret dims = " + format_vector(dims) + " as " + + std::to_string(n_dim_requested) + "-dimensional."; + }; + + if (n_dim_requested == 0) { + if (!checkDimensions(dims, n_dim_requested)) { + throw std::invalid_argument(format_error_message()); + } + + return {1ul}; + } + + auto n_dim = dims.size(); + if (n_dim < n_dim_requested) { + throw std::invalid_argument(format_error_message()); + } + + if (n_dim_requested == 1ul) { + size_t non_singleton_dim = size_t(-1); + for (size_t i = 0; i < n_dim; ++i) { + if (dims[i] != 1ul) { + if (non_singleton_dim == size_t(-1)) { + non_singleton_dim = i; + } else { + throw std::invalid_argument(format_error_message()); + } + } + } + + return {dims[std::min(non_singleton_dim, n_dim - 1)]}; + } + + size_t n_dim_excess = dims.size() - n_dim_requested; + for (size_t i = 1; i <= n_dim_excess; ++i) { + if (dims[n_dim - i] != 1) { + throw std::invalid_argument(format_error_message()); + } + } + + return std::vector(dims.begin(), + dims.end() - static_cast(n_dim_excess)); +} +} // namespace details + + +inline size_t compute_total_size(const std::vector& dims) { + return std::accumulate(dims.begin(), dims.end(), size_t{1u}, std::multiplies()); +} + +template +using unqualified_t = typename std::remove_const::type>::type; + +/***** +inspector { + using type = T + // base_type is the base type inside c++ (e.g. std::vector => int) + using base_type + // hdf5_type is the base read by hdf5 (c-type) (e.g. std::vector => const char*) + using hdf5_type + + // Number of dimensions starting from here + static constexpr size_t recursive_ndim + // Is the inner type trivially copyable for optimisation + // If this value is true: data() is mandatory + // If this value is false: getSizeVal, getSize, serialize, unserialize are mandatory + static constexpr bool is_trivially_copyable + + // Reading: + // Allocate the value following dims (should be recursive) + static void prepare(type& val, const std::vector dims) + // Return the size of the vector pass to/from hdf5 from a vector of dims + static size_t getSize(const std::vector& dims) + // Return a pointer of the first value of val (for reading) + static hdf5_type* data(type& val) + // Take a serialized vector 'in', some dims and copy value to val (for reading) + static void unserialize(const hdf5_type* in, const std::vector&i, type& val) + + + // Writing: + // Return the size of the vector pass to/from hdf5 from a value + static size_t getSizeVal(const type& val) + // Return a point of the first value of val + static const hdf5_type* data(const type& val) + // Take a val and serialize it inside 'out' + static void serialize(const type& val, hdf5_type* out) + // Return an array of dimensions of the space needed for writing val + static std::vector getDimensions(const type& val) +} +*****/ + + +namespace details { +template +struct type_helper { + using type = unqualified_t; + using base_type = unqualified_t; + using hdf5_type = base_type; + + static constexpr size_t ndim = 0; + static constexpr size_t recursive_ndim = ndim; + static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value; + + static std::vector getDimensions(const type& /* val */) { + return {}; + } + + static size_t getSizeVal(const type& val) { + return compute_total_size(getDimensions(val)); + } + + static size_t getSize(const std::vector& dims) { + return compute_total_size(dims); + } + + static void prepare(type& /* val */, const std::vector& /* dims */) {} + + static hdf5_type* data(type& val) { + static_assert(is_trivially_copyable, "The type is not trivially copyable"); + return &val; + } + + static const hdf5_type* data(const type& val) { + static_assert(is_trivially_copyable, "The type is not trivially copyable"); + return &val; + } + + static void serialize(const type& val, hdf5_type* m) { + static_assert(is_trivially_copyable, "The type is not trivially copyable"); + *m = val; + } + + static void unserialize(const hdf5_type* vec, + const std::vector& /* dims */, + type& val) { + static_assert(is_trivially_copyable, "The type is not trivially copyable"); + val = vec[0]; + } +}; + +template +struct inspector: type_helper {}; + +enum class Boolean : int8_t { + HighFiveFalse = 0, + HighFiveTrue = 1, +}; + +template <> +struct inspector: type_helper { + using base_type = Boolean; + using hdf5_type = int8_t; + + static constexpr bool is_trivially_copyable = false; + + static hdf5_type* data(type& /* val */) { + throw DataSpaceException("A boolean cannot be read directly."); + } + + static const hdf5_type* data(const type& /* val */) { + throw DataSpaceException("A boolean cannot be written directly."); + } + + static void unserialize(const hdf5_type* vec, + const std::vector& /* dims */, + type& val) { + val = vec[0] != 0 ? true : false; + } + + static void serialize(const type& val, hdf5_type* m) { + *m = val ? 1 : 0; + } +}; + +template <> +struct inspector: type_helper { + using hdf5_type = const char*; + + static hdf5_type* data(type& /* val */) { + throw DataSpaceException("A std::string cannot be read directly."); + } + + static const hdf5_type* data(const type& /* val */) { + throw DataSpaceException("A std::string cannot be written directly."); + } + + template + static void serialize(const type& val, It m) { + (*m).assign(val.data(), val.size(), StringPadding::NullTerminated); + } + + template + static void unserialize(const It& vec, const std::vector& /* dims */, type& val) { + const auto& view = *vec; + val.assign(view.data(), view.length()); + } +}; + +template <> +struct inspector: type_helper { + using hdf5_type = hobj_ref_t; + + static constexpr bool is_trivially_copyable = false; + + static hdf5_type* data(type& /* val */) { + throw DataSpaceException("A Reference cannot be read directly."); + } + + static const hdf5_type* data(const type& /* val */) { + throw DataSpaceException("A Reference cannot be written directly."); + } + + static void serialize(const type& val, hdf5_type* m) { + hobj_ref_t ref; + val.create_ref(&ref); + *m = ref; + } + + static void unserialize(const hdf5_type* vec, + const std::vector& /* dims */, + type& val) { + val = type{vec[0]}; + } +}; + +template +struct inspector> { + using type = FixedLenStringArray; + using value_type = char*; + using base_type = FixedLenStringArray; + using hdf5_type = char; + + static constexpr size_t ndim = 1; + static constexpr size_t recursive_ndim = ndim; + static constexpr bool is_trivially_copyable = false; + + static std::vector getDimensions(const type& val) { + return std::vector{val.size()}; + } + + static size_t getSizeVal(const type& val) { + return N * compute_total_size(getDimensions(val)); + } + + static size_t getSize(const std::vector& dims) { + return N * compute_total_size(dims); + } + + static void prepare(type& /* val */, const std::vector& dims) { + if (dims[0] > N) { + std::ostringstream os; + os << "Size of FixedlenStringArray (" << N << ") is too small for dims (" << dims[0] + << ")."; + throw DataSpaceException(os.str()); + } + } + + static hdf5_type* data(type& val) { + return val.data(); + } + + static const hdf5_type* data(const type& val) { + return val.data(); + } + + static void serialize(const type& val, hdf5_type* m) { + for (size_t i = 0; i < val.size(); ++i) { + std::memcpy(m + i * N, val[i], N); + } + } + + static void unserialize(const hdf5_type* vec, const std::vector& dims, type& val) { + for (size_t i = 0; i < dims[0]; ++i) { + std::array s; + std::memcpy(s.data(), vec + (i * N), N); + val.push_back(s); + } + } +}; + +template +struct inspector> { + using type = std::vector; + using value_type = unqualified_t; + using base_type = typename inspector::base_type; + using hdf5_type = typename inspector::hdf5_type; + + static constexpr size_t ndim = 1; + static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; + static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && + inspector::is_trivially_copyable; + + static std::vector getDimensions(const type& val) { + std::vector sizes(recursive_ndim, 1ul); + sizes[0] = val.size(); + if (!val.empty()) { + auto s = inspector::getDimensions(val[0]); + assert(s.size() + ndim == sizes.size()); + for (size_t i = 0; i < s.size(); ++i) { + sizes[i + ndim] = s[i]; + } + } + return sizes; + } + + static size_t getSizeVal(const type& val) { + return compute_total_size(getDimensions(val)); + } + + static size_t getSize(const std::vector& dims) { + return compute_total_size(dims); + } + + static void prepare(type& val, const std::vector& dims) { + val.resize(dims[0]); + std::vector next_dims(dims.begin() + 1, dims.end()); + for (auto&& e: val) { + inspector::prepare(e, next_dims); + } + } + + static hdf5_type* data(type& val) { + return inspector::data(val[0]); + } + + static const hdf5_type* data(const type& val) { + return inspector::data(val[0]); + } + + template + static void serialize(const type& val, It m) { + size_t subsize = inspector::getSizeVal(val[0]); + for (auto&& e: val) { + inspector::serialize(e, m); + m += subsize; + } + } + + template + static void unserialize(const It& vec_align, const std::vector& dims, type& val) { + std::vector next_dims(dims.begin() + 1, dims.end()); + size_t next_size = compute_total_size(next_dims); + for (size_t i = 0; i < dims[0]; ++i) { + inspector::unserialize(vec_align + i * next_size, next_dims, val[i]); + } + } +}; + +template <> +struct inspector> { + using type = std::vector; + using value_type = bool; + using base_type = Boolean; + using hdf5_type = uint8_t; + + static constexpr size_t ndim = 1; + static constexpr size_t recursive_ndim = ndim; + static constexpr bool is_trivially_copyable = false; + + static std::vector getDimensions(const type& val) { + std::vector sizes{val.size()}; + return sizes; + } + + static size_t getSizeVal(const type& val) { + return val.size(); + } + + static size_t getSize(const std::vector& dims) { + if (dims.size() > 1) { + throw DataSpaceException("std::vector is only 1 dimension."); + } + return dims[0]; + } + + static void prepare(type& val, const std::vector& dims) { + if (dims.size() > 1) { + throw DataSpaceException("std::vector is only 1 dimension."); + } + val.resize(dims[0]); + } + + static hdf5_type* data(type& /* val */) { + throw DataSpaceException("A std::vector cannot be read directly."); + } + + static const hdf5_type* data(const type& /* val */) { + throw DataSpaceException("A std::vector cannot be written directly."); + } + + static void serialize(const type& val, hdf5_type* m) { + for (size_t i = 0; i < val.size(); ++i) { + m[i] = val[i] ? 1 : 0; + } + } + + static void unserialize(const hdf5_type* vec_align, + const std::vector& dims, + type& val) { + for (size_t i = 0; i < dims[0]; ++i) { + val[i] = vec_align[i] != 0 ? true : false; + } + } +}; + +template +struct inspector> { + using type = std::array; + using value_type = unqualified_t; + using base_type = typename inspector::base_type; + using hdf5_type = typename inspector::hdf5_type; + + static constexpr size_t ndim = 1; + static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; + static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && + sizeof(type) == N * sizeof(T) && + inspector::is_trivially_copyable; + + static std::vector getDimensions(const type& val) { + std::vector sizes{N}; + if (!val.empty()) { + auto s = inspector::getDimensions(val[0]); + sizes.insert(sizes.end(), s.begin(), s.end()); + } + return sizes; + } + + static size_t getSizeVal(const type& val) { + return compute_total_size(getDimensions(val)); + } + + static size_t getSize(const std::vector& dims) { + return compute_total_size(dims); + } + + static void prepare(type& /* val */, const std::vector& dims) { + if (dims[0] > N) { + std::ostringstream os; + os << "Size of std::array (" << N << ") is too small for dims (" << dims[0] << ")."; + throw DataSpaceException(os.str()); + } + } + + static hdf5_type* data(type& val) { + return inspector::data(val[0]); + } + + static const hdf5_type* data(const type& val) { + return inspector::data(val[0]); + } + + template + static void serialize(const type& val, It m) { + size_t subsize = inspector::getSizeVal(val[0]); + for (auto& e: val) { + inspector::serialize(e, m); + m += subsize; + } + } + + template + static void unserialize(const It& vec_align, const std::vector& dims, type& val) { + if (dims[0] != N) { + std::ostringstream os; + os << "Impossible to pair DataSet with " << dims[0] << " elements into an array with " + << N << " elements."; + throw DataSpaceException(os.str()); + } + std::vector next_dims(dims.begin() + 1, dims.end()); + size_t next_size = compute_total_size(next_dims); + for (size_t i = 0; i < dims[0]; ++i) { + inspector::unserialize(vec_align + i * next_size, next_dims, val[i]); + } + } +}; + +// Cannot be use for reading +template +struct inspector { + using type = T*; + using value_type = unqualified_t; + using base_type = typename inspector::base_type; + using hdf5_type = typename inspector::hdf5_type; + + static constexpr size_t ndim = 1; + static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; + static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && + inspector::is_trivially_copyable; + + static size_t getSizeVal(const type& /* val */) { + throw DataSpaceException("Not possible to have size of a T*"); + } + + static std::vector getDimensions(const type& /* val */) { + throw DataSpaceException("Not possible to have size of a T*"); + } + + static const hdf5_type* data(const type& val) { + return reinterpret_cast(val); + } + + /* it works because there is only T[][][] currently + we will fix it one day */ + static void serialize(const type& /* val */, hdf5_type* /* m */) { + throw DataSpaceException("Not possible to serialize a T*"); + } +}; + +// Cannot be use for reading +template +struct inspector { + using type = T[N]; + using value_type = unqualified_t; + using base_type = typename inspector::base_type; + using hdf5_type = typename inspector::hdf5_type; + + static constexpr size_t ndim = 1; + static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; + static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && + inspector::is_trivially_copyable; + + static size_t getSizeVal(const type& val) { + return compute_total_size(getDimensions(val)); + } + + static std::vector getDimensions(const type& val) { + std::vector sizes{N}; + if (N > 0) { + auto s = inspector::getDimensions(val[0]); + sizes.insert(sizes.end(), s.begin(), s.end()); + } + return sizes; + } + + static const hdf5_type* data(const type& val) { + return inspector::data(val[0]); + } + + /* it works because there is only T[][][] currently + we will fix it one day */ + static void serialize(const type& val, hdf5_type* m) { + size_t subsize = inspector::getSizeVal(val[0]); + for (size_t i = 0; i < N; ++i) { + inspector::serialize(val[i], m + i * subsize); + } + } +}; + +#ifdef H5_USE_EIGEN +template +struct inspector> { + using type = Eigen::Matrix; + using value_type = T; + using base_type = typename inspector::base_type; + using hdf5_type = base_type; + + static constexpr size_t ndim = 2; + static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; + static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && + inspector::is_trivially_copyable; + + + static void assert_not_buggy(Eigen::Index nrows, Eigen::Index ncols) { + if (nrows > 1 && ncols > 1) { + throw std::runtime_error( + "HighFive has been broken for Eigen::Matrix. Please check " + "https://github.com/BlueBrain/HighFive/issues/532."); + } + } + + static std::vector getDimensions(const type& val) { + assert_not_buggy(val.rows(), val.cols()); + + std::vector sizes{static_cast(val.rows()), static_cast(val.cols())}; + auto s = inspector::getDimensions(val.data()[0]); + sizes.insert(sizes.end(), s.begin(), s.end()); + return sizes; + } + + static size_t getSizeVal(const type& val) { + return compute_total_size(getDimensions(val)); + } + + static size_t getSize(const std::vector& dims) { + return compute_total_size(dims); + } + + static void prepare(type& val, const std::vector& dims) { + if (dims[0] != static_cast(val.rows()) || + dims[1] != static_cast(val.cols())) { + val.resize(static_cast(dims[0]), + static_cast(dims[1])); + } + + assert_not_buggy(val.rows(), val.cols()); + } + + static hdf5_type* data(type& val) { + assert_not_buggy(val.rows(), val.cols()); + return inspector::data(*val.data()); + } + + static const hdf5_type* data(const type& val) { + assert_not_buggy(val.rows(), val.cols()); + return inspector::data(*val.data()); + } + + static void serialize(const type& val, hdf5_type* m) { + assert_not_buggy(val.rows(), val.cols()); + std::memcpy(m, val.data(), static_cast(val.size()) * sizeof(hdf5_type)); + } + + static void unserialize(const hdf5_type* vec_align, + const std::vector& dims, + type& val) { + assert_not_buggy(val.rows(), val.cols()); + if (dims.size() < 2) { + std::ostringstream os; + os << "Impossible to pair DataSet with " << dims.size() + << " dimensions into an eigen-matrix."; + throw DataSpaceException(os.str()); + } + std::memcpy(val.data(), vec_align, compute_total_size(dims) * sizeof(hdf5_type)); + } +}; +#endif + +#ifdef H5_USE_BOOST +template +struct inspector> { + using type = boost::multi_array; + using value_type = T; + using base_type = typename inspector::base_type; + using hdf5_type = typename inspector::hdf5_type; + + static constexpr size_t ndim = Dims; + static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; + static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && + inspector::is_trivially_copyable; + + static std::vector getDimensions(const type& val) { + std::vector sizes; + for (size_t i = 0; i < ndim; ++i) { + sizes.push_back(val.shape()[i]); + } + auto s = inspector::getDimensions(val.data()[0]); + sizes.insert(sizes.end(), s.begin(), s.end()); + return sizes; + } + + static size_t getSizeVal(const type& val) { + return compute_total_size(getDimensions(val)); + } + + static size_t getSize(const std::vector& dims) { + return compute_total_size(dims); + } + + static void prepare(type& val, const std::vector& dims) { + if (dims.size() < ndim) { + std::ostringstream os; + os << "Only '" << dims.size() << "' given but boost::multi_array is of size '" << ndim + << "'."; + throw DataSpaceException(os.str()); + } + boost::array ext; + std::copy(dims.begin(), dims.begin() + ndim, ext.begin()); + val.resize(ext); + std::vector next_dims(dims.begin() + Dims, dims.end()); + std::size_t size = std::accumulate(dims.begin(), + dims.begin() + Dims, + std::size_t{1}, + std::multiplies()); + for (size_t i = 0; i < size; ++i) { + inspector::prepare(*(val.origin() + i), next_dims); + } + } + + static hdf5_type* data(type& val) { + return inspector::data(*val.data()); + } + + static const hdf5_type* data(const type& val) { + return inspector::data(*val.data()); + } + + template + static void serialize(const type& val, It m) { + size_t size = val.num_elements(); + size_t subsize = inspector::getSizeVal(*val.origin()); + for (size_t i = 0; i < size; ++i) { + inspector::serialize(*(val.origin() + i), m + i * subsize); + } + } + + template + static void unserialize(It vec_align, const std::vector& dims, type& val) { + std::vector next_dims(dims.begin() + ndim, dims.end()); + size_t subsize = compute_total_size(next_dims); + for (size_t i = 0; i < val.num_elements(); ++i) { + inspector::unserialize(vec_align + i * subsize, + next_dims, + *(val.origin() + i)); + } + } +}; + +template +struct inspector> { + using type = boost::numeric::ublas::matrix; + using value_type = unqualified_t; + using base_type = typename inspector::base_type; + using hdf5_type = typename inspector::hdf5_type; + + static constexpr size_t ndim = 2; + static constexpr size_t recursive_ndim = ndim + inspector::recursive_ndim; + static constexpr bool is_trivially_copyable = std::is_trivially_copyable::value && + inspector::is_trivially_copyable; + + static std::vector getDimensions(const type& val) { + std::vector sizes{val.size1(), val.size2()}; + auto s = inspector::getDimensions(val(0, 0)); + sizes.insert(sizes.end(), s.begin(), s.end()); + return sizes; + } + + static size_t getSizeVal(const type& val) { + return compute_total_size(getDimensions(val)); + } + + static size_t getSize(const std::vector& dims) { + return compute_total_size(dims); + } + + static void prepare(type& val, const std::vector& dims) { + if (dims.size() < ndim) { + std::ostringstream os; + os << "Impossible to pair DataSet with " << dims.size() << " dimensions into a " << ndim + << " boost::numeric::ublas::matrix"; + throw DataSpaceException(os.str()); + } + val.resize(dims[0], dims[1], false); + } + + static hdf5_type* data(type& val) { + return inspector::data(val(0, 0)); + } + + static const hdf5_type* data(const type& val) { + return inspector::data(val(0, 0)); + } + + static void serialize(const type& val, hdf5_type* m) { + size_t size = val.size1() * val.size2(); + size_t subsize = inspector::getSizeVal(val(0, 0)); + for (size_t i = 0; i < size; ++i) { + inspector::serialize(*(&val(0, 0) + i), m + i * subsize); + } + } + + static void unserialize(const hdf5_type* vec_align, + const std::vector& dims, + type& val) { + std::vector next_dims(dims.begin() + ndim, dims.end()); + size_t subsize = compute_total_size(next_dims); + size_t size = val.size1() * val.size2(); + for (size_t i = 0; i < size; ++i) { + inspector::unserialize(vec_align + i * subsize, + next_dims, + *(&val(0, 0) + i)); + } + } +}; +#endif + +} // namespace details +} // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Node_traits.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Node_traits.hpp index bdd70332e8..d53d3f0488 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Node_traits.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Node_traits.hpp @@ -129,6 +129,14 @@ class NodeTraits { /// \return the group object Group getGroup(const std::string& group_name) const; + /// + /// \brief open a commited datatype with the name type_name + /// \param type_name + /// \return the datatype object + DataType getDataType( + const std::string& type_name, + const DataTypeAccessProps& accessProps = DataTypeAccessProps::Default()) const; + /// /// \brief return the number of leaf objects of the node / group /// \return number of leaf objects @@ -208,6 +216,20 @@ class NodeTraits { const LinkAccessProps& linkAccessProps = LinkAccessProps(), const bool parents = true); + /// + /// \brief Creates hardlinks + /// \param link_name The name of the link + /// \param target_obj The target object + /// \param linkCreateProps A Link_Create property list. Notice "parents=true" overrides + /// \param linkAccessProps The Link_Access property list + /// \param parents Whether parent groups should be created: Default: true + template + void createHardLink(const std::string& link_name, + const T& target_obj, + LinkCreateProps linkCreateProps = LinkCreateProps(), + const LinkAccessProps& linkAccessProps = LinkAccessProps(), + const bool parents = true); + private: using derivate_type = Derivate; diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Node_traits_misc.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Node_traits_misc.hpp index 0af1281755..2f75ff3111 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Node_traits_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Node_traits_misc.hpp @@ -174,6 +174,19 @@ inline Group NodeTraits::getGroup(const std::string& group_name) const return detail::make_group(hid); } +template +inline DataType NodeTraits::getDataType(const std::string& type_name, + const DataTypeAccessProps& accessProps) const { + const auto hid = H5Topen2(static_cast(this)->getId(), + type_name.c_str(), + accessProps.getId()); + if (hid < 0) { + HDF5ErrMapper::ToException( + std::string("Unable to open the datatype \"") + type_name + "\":"); + } + return DataType(hid); +} + template inline size_t NodeTraits::getNumberObjects() const { hsize_t res; @@ -358,6 +371,29 @@ inline void NodeTraits::createExternalLink(const std::string& link_nam } } +template +template +inline void NodeTraits::createHardLink(const std::string& link_name, + const T& target_obj, + LinkCreateProps linkCreateProps, + const LinkAccessProps& linkAccessProps, + const bool parents) { + static_assert(!std::is_same::value, + "hdf5 doesn't support hard links to Attributes"); + if (parents) { + linkCreateProps.add(CreateIntermediateGroup{}); + } + auto status = H5Lcreate_hard(target_obj.getId(), + ".", + static_cast(this)->getId(), + link_name.c_str(), + linkCreateProps.getId(), + linkAccessProps.getId()); + if (status < 0) { + HDF5ErrMapper::ToException(std::string("Unable to create hard link: ")); + } +} + template inline Object NodeTraits::_open(const std::string& node_name, diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Path_traits_misc.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Path_traits_misc.hpp index 73704f03af..444e9294bf 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Path_traits_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Path_traits_misc.hpp @@ -34,7 +34,7 @@ inline PathTraits::PathTraits() { template inline std::string PathTraits::getPath() const { return details::get_name([this](char* buffer, size_t length) { - return H5Iget_name(static_cast(this)->getId(), buffer, length); + return H5Iget_name(static_cast(*this).getId(), buffer, length); }); } diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5PropertyList_misc.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5PropertyList_misc.hpp index 6a4ef9efd9..cef301e53a 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5PropertyList_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5PropertyList_misc.hpp @@ -70,7 +70,7 @@ inline void PropertyList::_initializeIfNeeded() { } template -template +template inline void PropertyList::add(const P& property) { _initializeIfNeeded(); property.apply(_hid); @@ -543,4 +543,32 @@ inline void LinkCreationOrder::fromPropertyList(hid_t hid) { "Error getting property for link creation order"); } } + +inline AttributePhaseChange::AttributePhaseChange(unsigned max_compact, unsigned min_dense) + : _max_compact(max_compact) + , _min_dense(min_dense) {} + +inline AttributePhaseChange::AttributePhaseChange(const GroupCreateProps& gcpl) { + if (H5Pget_attr_phase_change(gcpl.getId(), &_max_compact, &_min_dense) < 0) { + HDF5ErrMapper::ToException( + "Error getting property for attribute phase change"); + } +} + +inline unsigned AttributePhaseChange::max_compact() const { + return _max_compact; +} + +inline unsigned AttributePhaseChange::min_dense() const { + return _min_dense; +} + +inline void AttributePhaseChange::apply(hid_t hid) const { + if (H5Pset_attr_phase_change(hid, _max_compact, _min_dense) < 0) { + HDF5ErrMapper::ToException( + "Error getting property for attribute phase change"); + } +} + + } // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5ReadWrite_misc.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5ReadWrite_misc.hpp index 491e613896..c8e7361740 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5ReadWrite_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5ReadWrite_misc.hpp @@ -19,9 +19,13 @@ template using unqualified_t = typename std::remove_const::type>::type; // Find the type of an eventual char array, otherwise void -template +template struct type_char_array { - using type = void; + using type = typename std::conditional< + std::is_same::base_type, std::string>::value, + std::string, + void>::type; + static constexpr bool is_char_array = false; }; template @@ -29,6 +33,7 @@ struct type_char_array { using type = typename std::conditional, char>::value, char*, typename type_char_array::type>::type; + static constexpr bool is_char_array = true; }; template @@ -36,6 +41,7 @@ struct type_char_array { using type = typename std::conditional, char>::value, char[N], typename type_char_array::type>::type; + static constexpr bool is_char_array = true; }; template @@ -43,7 +49,7 @@ struct BufferInfo { using type_no_const = typename std::remove_const::type; using elem_type = typename details::inspector::base_type; using char_array_t = typename details::type_char_array::type; - static constexpr bool is_char_array = !std::is_same::value; + static constexpr bool is_char_array = details::type_char_array::is_char_array; enum Operation { read, write }; const Operation op; @@ -63,29 +69,44 @@ struct string_type_checker { static DataType getDataType(const DataType&, const DataType&); }; +inline void enforce_ascii_hack(const DataType& dst, const DataType& src) { + // Note: constness only refers to constness of the DataType object, which + // is just an ID, we can/will change properties of `dst`. + + // TEMP. CHANGE: Ensure that the character set is properly configured to prevent + // converter issues on HDF5 <=v1.12.0 when loading ASCII strings first. + // See https://github.com/HDFGroup/hdf5/issues/544 for further information. + if (H5Tget_cset(src.getId()) == H5T_CSET_ASCII) { + H5Tset_cset(dst.getId(), H5T_CSET_ASCII); + } +} + template <> struct string_type_checker { inline static DataType getDataType(const DataType& element_type, const DataType& dtype) { - // TEMP. CHANGE: Ensure that the character set is properly configured to prevent - // converter issues on HDF5 <=v1.12.0 when loading ASCII strings first. - // See https://github.com/HDFGroup/hdf5/issues/544 for further information. - if (H5Tget_class(element_type.getId()) == H5T_STRING && - H5Tget_cset(dtype.getId()) == H5T_CSET_ASCII) { - H5Tset_cset(element_type.getId(), H5T_CSET_ASCII); + if (H5Tget_class(element_type.getId()) == H5T_STRING) { + enforce_ascii_hack(element_type, dtype); } return element_type; } }; +template <> +struct string_type_checker { + inline static DataType getDataType(const DataType&, const DataType& file_datatype) { + // The StringBuffer ensures that the data is transformed such that it + // matches the datatype of the dataset, i.e. `file_datatype` and + // `mem_datatype` are the same. + return file_datatype; + } +}; + template struct string_type_checker { inline static DataType getDataType(const DataType& element_type, const DataType& dtype) { DataType return_type = (dtype.isFixedLenStr()) ? AtomicType() : element_type; - // TEMP. CHANGE: See string_type_checker definition - if (H5Tget_cset(dtype.getId()) == H5T_CSET_ASCII) { - H5Tset_cset(return_type.getId(), H5T_CSET_ASCII); - } + enforce_ascii_hack(return_type, dtype); return return_type; } }; @@ -93,13 +114,11 @@ struct string_type_checker { template <> struct string_type_checker { inline static DataType getDataType(const DataType&, const DataType& dtype) { - if (dtype.isFixedLenStr()) + if (dtype.isFixedLenStr()) { throw DataSetException("Can't output variable-length to fixed-length strings"); - // TEMP. CHANGE: See string_type_checker definition - DataType return_type = AtomicType(); - if (H5Tget_cset(dtype.getId()) == H5T_CSET_ASCII) { - H5Tset_cset(return_type.getId(), H5T_CSET_ASCII); } + DataType return_type = AtomicType(); + enforce_ascii_hack(return_type, dtype); return return_type; } }; @@ -114,11 +133,6 @@ BufferInfo::BufferInfo(const DataType& dtype, F getName, Operation _op) ((is_fixed_len_string && is_char_array) ? 1 : 0)) , data_type( string_type_checker::getDataType(create_datatype(), dtype)) { - if (is_fixed_len_string && std::is_same::value) { - throw DataSetException( - "Can't output std::string as fixed-length. " - "Use raw arrays or FixedLenStringArray"); - } // We warn. In case they are really not convertible an exception will rise on read/write if (dtype.getClass() != data_type.getClass()) { HIGHFIVE_LOG_WARN(getName() + "\": data and hdf5 dataset have different types: " + diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Slice_traits.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Slice_traits.hpp index 719b4e5036..52c52713f0 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Slice_traits.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Slice_traits.hpp @@ -258,6 +258,15 @@ class SliceTraits { /// Therefore, the only memspaces supported for general hyperslabs are one-dimensional arrays. Selection select(const HyperSlab& hyperslab) const; + /// + /// \brief Select an \p hyperslab in the current Slice/Dataset. + /// + /// If the selection can be read into a simple, multi-dimensional dataspace, + /// then this overload enable specifying the shape of the memory dataspace + /// with `memspace`. Note, that simple implies no offsets, strides or + /// number of blocks, just the size of the block in each dimension. + Selection select(const HyperSlab& hyperslab, const DataSpace& memspace) const; + /// /// \brief Select a region in the current Slice/Dataset of \p count points at /// \p offset separated by \p stride. If strides are not provided they will @@ -308,9 +317,21 @@ class SliceTraits { /// \param xfer_props: Data Transfer properties template void read(T* array, - const DataType& dtype = DataType(), + const DataType& dtype, const DataTransferProps& xfer_props = DataTransferProps()) const; + /// + /// Read the entire dataset into a raw buffer + /// + /// Same as `read(T*, const DataType&, const DataTransferProps&)`. However, + /// this overload deduces the HDF5 datatype of the element of `array` from + /// `T`. Note, that the file datatype is already fixed. + /// + /// \param array: A buffer containing enough space for the data + /// \param xfer_props: Data Transfer properties + template + void read(T* array, const DataTransferProps& xfer_props = DataTransferProps()) const; + /// /// Write the integrality N-dimension buffer to this dataset /// An exception is raised is if the numbers of dimension of the buffer and @@ -322,22 +343,33 @@ class SliceTraits { void write(const T& buffer, const DataTransferProps& xfer_props = DataTransferProps()); /// - /// Write from a raw buffer into this dataset + /// Write from a raw pointer into this dataset. /// /// No dimensionality checks will be performed, it is the user's /// responsibility to ensure that the buffer holds the right amount of /// elements. For n-dimensional matrices the buffer layout follows H5 /// default conventions. + /// + /// Note, this is the shallowest wrapper around `H5Dwrite` and should + /// be used if full control is needed. Generally prefer `write`. + /// /// \param buffer: A buffer containing the data to be written - /// \param dtype: The type of the data, in case it cannot be automatically guessed + /// \param dtype: The datatype of `buffer`, i.e. the memory data type. /// \param xfer_props: The HDF5 data transfer properties, e.g. collective MPI-IO. template void write_raw(const T* buffer, - const DataType& dtype = DataType(), + const DataType& mem_datatype, const DataTransferProps& xfer_props = DataTransferProps()); - protected: - inline Selection select_impl(const HyperSlab& hyperslab, const DataSpace& memspace) const; + /// + /// Write from a raw pointer into this dataset. + /// + /// Same as `write_raw(const T*, const DataTransferProps&)`. However, this + /// overload attempts to guess the data type of `buffer`, i.e. the memory + /// datatype. Note that the file datatype is already fixed. + /// + template + void write_raw(const T* buffer, const DataTransferProps& xfer_props = DataTransferProps()); }; } // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Slice_traits_misc.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Slice_traits_misc.hpp index faae237d8b..7b07c9abf9 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Slice_traits_misc.hpp +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/H5Slice_traits_misc.hpp @@ -64,8 +64,8 @@ inline ElementSet::ElementSet(const std::vector>& eleme } template -inline Selection SliceTraits::select_impl(const HyperSlab& hyperslab, - const DataSpace& memspace) const { +inline Selection SliceTraits::select(const HyperSlab& hyperslab, + const DataSpace& memspace) const { // Note: The current limitation are that memspace must describe a // packed memspace. // @@ -98,7 +98,7 @@ inline Selection SliceTraits::select(const std::vector& offset const std::vector& block) const { auto slab = HyperSlab(RegularHyperSlab(offset, count, stride, block)); auto memspace = DataSpace(count); - return select_impl(slab, memspace); + return select(slab, memspace); } template @@ -121,7 +121,7 @@ inline Selection SliceTraits::select(const std::vector& column std::vector memdims = dims; memdims.back() = columns.size(); - return select_impl(slab, DataSpace(memdims)); + return select(slab, DataSpace(memdims)); } template @@ -172,8 +172,10 @@ inline void SliceTraits::read(T& array, const DataTransferProps& xfer_ const auto& slice = static_cast(*this); const DataSpace& mem_space = slice.getMemSpace(); + auto file_datatype = slice.getDataType(); + const details::BufferInfo buffer_info( - slice.getDataType(), + file_datatype, [&slice]() -> std::string { return details::get_dataset(slice).getPath(); }, details::BufferInfo::Operation::read); @@ -193,19 +195,20 @@ inline void SliceTraits::read(T& array, const DataTransferProps& xfer_ return; } - auto r = details::data_converter::get_reader(dims, array); - read(r.get_pointer(), buffer_info.data_type, xfer_props); + auto r = details::data_converter::get_reader(dims, array, file_datatype); + read(r.getPointer(), buffer_info.data_type, xfer_props); // re-arrange results - r.unserialize(); - auto t = create_datatype::base_type>(); + r.unserialize(array); + + auto t = buffer_info.data_type; auto c = t.getClass(); if (c == DataTypeClass::VarLen || t.isVariableStr()) { #if H5_VERSION_GE(1, 12, 0) // This one have been created in 1.12.0 - (void) H5Treclaim(t.getId(), mem_space.getId(), xfer_props.getId(), r.get_pointer()); + (void) H5Treclaim(t.getId(), mem_space.getId(), xfer_props.getId(), r.getPointer()); #else // This one is deprecated since 1.12.0 - (void) H5Dvlen_reclaim(t.getId(), mem_space.getId(), xfer_props.getId(), r.get_pointer()); + (void) H5Dvlen_reclaim(t.getId(), mem_space.getId(), xfer_props.getId(), r.getPointer()); #endif } } @@ -214,16 +217,12 @@ inline void SliceTraits::read(T& array, const DataTransferProps& xfer_ template template inline void SliceTraits::read(T* array, - const DataType& dtype, + const DataType& mem_datatype, const DataTransferProps& xfer_props) const { static_assert(!std::is_const::value, "read() requires a non-const structure to read data into"); - const auto& slice = static_cast(*this); - using element_type = typename details::inspector::base_type; - // Auto-detect mem datatype if not provided - const DataType& mem_datatype = dtype.empty() ? create_and_check_datatype() - : dtype; + const auto& slice = static_cast(*this); if (H5Dread(details::get_dataset(slice).getId(), mem_datatype.getId(), @@ -235,6 +234,15 @@ inline void SliceTraits::read(T* array, } } +template +template +inline void SliceTraits::read(T* array, const DataTransferProps& xfer_props) const { + using element_type = typename details::inspector::base_type; + const DataType& mem_datatype = create_and_check_datatype(); + + read(array, mem_datatype, xfer_props); +} + template template @@ -246,8 +254,10 @@ inline void SliceTraits::write(const T& buffer, const DataTransferProp return; } + auto file_datatype = slice.getDataType(); + const details::BufferInfo buffer_info( - slice.getDataType(), + file_datatype, [&slice]() -> std::string { return details::get_dataset(slice).getPath(); }, details::BufferInfo::Operation::write); @@ -258,19 +268,17 @@ inline void SliceTraits::write(const T& buffer, const DataTransferProp << " into dataset with n = " << buffer_info.n_dimensions << " dimensions."; throw DataSpaceException(ss.str()); } - auto w = details::data_converter::serialize(buffer); - write_raw(w.get_pointer(), buffer_info.data_type, xfer_props); + auto w = details::data_converter::serialize(buffer, file_datatype); + write_raw(w.getPointer(), buffer_info.data_type, xfer_props); } template template inline void SliceTraits::write_raw(const T* buffer, - const DataType& dtype, + const DataType& mem_datatype, const DataTransferProps& xfer_props) { - using element_type = typename details::inspector::base_type; const auto& slice = static_cast(*this); - const auto& mem_datatype = dtype.empty() ? create_and_check_datatype() : dtype; if (H5Dwrite(details::get_dataset(slice).getId(), mem_datatype.getId(), @@ -282,5 +290,14 @@ inline void SliceTraits::write_raw(const T* buffer, } } +template +template +inline void SliceTraits::write_raw(const T* buffer, const DataTransferProps& xfer_props) { + using element_type = typename details::inspector::base_type; + const auto& mem_datatype = create_and_check_datatype(); + + write_raw(buffer, mem_datatype, xfer_props); +} + } // namespace HighFive diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/string_padding.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/string_padding.hpp new file mode 100644 index 0000000000..e6e6908ddc --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/bits/string_padding.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace HighFive { + +enum class StringPadding : std::underlying_type::type { + NullTerminated = H5T_STR_NULLTERM, + NullPadded = H5T_STR_NULLPAD, + SpacePadded = H5T_STR_SPACEPAD +}; + + +} diff --git a/externals/coda-oss/modules/drivers/highfive/include/highfive/highfive.hpp b/externals/coda-oss/modules/drivers/highfive/include/highfive/highfive.hpp new file mode 100644 index 0000000000..f5e20cae91 --- /dev/null +++ b/externals/coda-oss/modules/drivers/highfive/include/highfive/highfive.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/externals/coda-oss/modules/drivers/highfive/include/import/highfive.h b/externals/coda-oss/modules/drivers/highfive/include/import/highfive.h index 5780ca5883..475015d11e 100644 --- a/externals/coda-oss/modules/drivers/highfive/include/import/highfive.h +++ b/externals/coda-oss/modules/drivers/highfive/include/import/highfive.h @@ -30,6 +30,7 @@ #endif #include "highfive/H5Easy.hpp" +#incldue "highfive/highfive.hpp" #if _MSC_VER #pragma comment(lib, "shlwapi") // StrStrI() diff --git a/externals/coda-oss/modules/drivers/highfive/unittests/tests_high_five.hpp b/externals/coda-oss/modules/drivers/highfive/unittests/tests_high_five.hpp index 79866b4638..0ebd58c448 100644 --- a/externals/coda-oss/modules/drivers/highfive/unittests/tests_high_five.hpp +++ b/externals/coda-oss/modules/drivers/highfive/unittests/tests_high_five.hpp @@ -12,6 +12,17 @@ #include #include #include +#include +#include +#include + +// We don't need windows specific functionality. However, to better detect defects caused by macros, +// we include this header. +// The list of identifiers is taken from `Boost::Predef`. +#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) || \ + defined(__WINDOWS__) +#include +#endif using ldcomplex = std::complex; using dcomplex = std::complex; @@ -35,8 +46,8 @@ using base_test_types = std::tuple using float16_t = half_float::half; -using numerical_test_types = decltype( - std::tuple_cat(std::declval(), std::tuple())); +using numerical_test_types = + decltype(std::tuple_cat(std::declval(), std::tuple())); #else using numerical_test_types = base_test_types; #endif @@ -154,16 +165,22 @@ struct ContentGenerate { template inline std::string typeNameHelper() { std::string name = typeid(T).name(); -#if defined(WIN32) - // Replace illegal windows file path characters std::replace(std::begin(name), std::end(name), ' ', '_'); std::replace(std::begin(name), std::end(name), '<', '_'); std::replace(std::begin(name), std::end(name), '>', '_'); std::replace(std::begin(name), std::end(name), ':', '_'); -#endif - return name; + + if (name.size() > 64) { + std::stringstream hash; + hash << std::hex << std::hash{}(name); + + return hash.str(); + } else { + return name; + } } + template inline HighFive::DataSet readWriteDataset(const DataT& ndvec, DataT& result, @@ -185,4 +202,4 @@ inline HighFive::DataSet readWriteDataset(const DataT& ndvec, dataset.read(result); return dataset; -} \ No newline at end of file +} diff --git a/externals/coda-oss/modules/drivers/highfive/unittests/tests_high_five_base.cpp b/externals/coda-oss/modules/drivers/highfive/unittests/tests_high_five_base.cpp index b9fb666f50..8cbce80961 100644 --- a/externals/coda-oss/modules/drivers/highfive/unittests/tests_high_five_base.cpp +++ b/externals/coda-oss/modules/drivers/highfive/unittests/tests_high_five_base.cpp @@ -62,14 +62,15 @@ inline bool Equals_(const std::vector& lhs, const std::vector& rhs) #define CHECK_NOTHROW(f) (f); TEST_SUCCESS #define REQUIRE(x) TEST_ASSERT_TRUE(x) #define CHECK_THAT(x, y) TEST_ASSERT(Equals_(x, y)) -#define INFO(...) {} +#define INFO(...) { } #define SECTION(name) +#include #include "tests_high_five.hpp" -TEST_CASE(Basic_HighFive_tests) { - using namespace HighFive; +using namespace HighFive; +TEST_CASE(Basic_HighFive_tests) { const std::string file_name("h5tutr_dset.h5"); const std::string dataset_name("dset"); @@ -108,8 +109,6 @@ TEST_CASE(Basic_HighFive_tests) { } TEST_CASE(Test_silent_HighFive) { - using namespace HighFive; - // Setting up a buffer for stderr so we can detect if the stack trace // was disabled fflush(stderr); @@ -131,8 +130,6 @@ TEST_CASE(Test_silent_HighFive) { } TEST_CASE(Test_open_modes_in_HighFive) { - using namespace HighFive; - const std::string file_name("openmodes.h5"); std::remove(file_name.c_str()); @@ -171,8 +168,6 @@ TEST_CASE(Test_open_modes_in_HighFive) { } TEST_CASE(Test_file_version_bounds) { - using namespace HighFive; - const std::string file_name("h5_version_bounds.h5"); std::remove(file_name.c_str()); @@ -196,9 +191,8 @@ TEST_CASE(Test_file_version_bounds) { } } +#if H5_VERSION_GE(1, 10, 1) TEST_CASE(Test_file_space_strategy) { - using namespace HighFive; - const std::string file_name("h5_file_space_strategy.h5"); auto strategies = std::vector{H5F_FSPACE_STRATEGY_FSM_AGGR, H5F_FSPACE_STRATEGY_AGGR, @@ -221,8 +215,6 @@ TEST_CASE(Test_file_space_strategy) { } TEST_CASE(Test_file_space_page_size) { - using namespace HighFive; - const std::string file_name("h5_file_space_page_size.h5"); hsize_t page_size = 1024; { @@ -241,8 +233,6 @@ TEST_CASE(Test_file_space_page_size) { #ifndef H5_HAVE_PARALLEL TEST_CASE(Test_page_buffer_size) { - using namespace HighFive; - const std::string file_name("h5_page_buffer_size.h5"); hsize_t page_size = 1024; { @@ -304,10 +294,9 @@ TEST_CASE(Test_page_buffer_size) { } } #endif +#endif TEST_CASE(Test_metadata_block_size_assignment) { - using namespace HighFive; - const std::string file_name("h5_meta_block_size.h5"); std::remove(file_name.c_str()); @@ -329,8 +318,6 @@ TEST_CASE(Test_metadata_block_size_assignment) { } TEST_CASE(Test_group_properties) { - using namespace HighFive; - const std::string file_name("h5_group_properties.h5"); FileAccessProps fapl; // When using hdf5 1.10.2 and later, the lower bound may be set to @@ -339,17 +326,15 @@ TEST_CASE(Test_group_properties) { File file(file_name, File::Truncate, fapl); GroupCreateProps props; - props.add(EstimatedLinkInfo(1000, 500)); + props.add(EstimatedLinkInfo(10, 60)); auto group = file.createGroup("g", props); auto sizes = group.getEstimatedLinkInfo(); - CHECK(sizes.first == 1000); - CHECK(sizes.second == 500); + CHECK(sizes.first == 10); + CHECK(sizes.second == 60); } TEST_CASE(Test_allocation_time) { - using namespace HighFive; - const std::string file_name("h5_dataset_alloc_time.h5"); File file(file_name, File::Truncate); @@ -367,15 +352,21 @@ TEST_CASE(Test_allocation_time) { CHECK(alloc_size == data.size() * sizeof(decltype(data)::value_type)); } +/* + * Test to ensure legacy support: DataSet used to have a default constructor. + * However, it is not useful to have a DataSet object that does not actually + * refer to a dataset in a file. Hence, the the default constructor was + * deprecated. + * This test is to ensure that the constructor is not accidentally removed and + * thereby break users' code. + */ TEST_CASE(Test_default_constructors) { - using namespace HighFive; - - const std::string file_name("h5_group_test.h5"); + const std::string file_name("h5_default_ctors.h5"); const std::string dataset_name("dset"); File file(file_name, File::Truncate); auto ds = file.createDataSet(dataset_name, std::vector{1, 2, 3, 4, 5}); - DataSet d2; // deprecated as it constructs unsafe objects + DataSet d2; // expect deprecation warning, as it constructs unsafe object // d2.getFile(); // runtime error CHECK(!d2.isValid()); d2 = ds; // copy @@ -383,8 +374,6 @@ TEST_CASE(Test_default_constructors) { } TEST_CASE(Test_groups_and_datasets) { - using namespace HighFive; - const std::string file_name("h5_group_test.h5"); const std::string dataset_name("dset"); const std::string chunked_dataset_name("chunked_dset"); @@ -480,9 +469,68 @@ TEST_CASE(Test_groups_and_datasets) { } } -TEST_CASE(Test_extensible_datasets) { - using namespace HighFive; +TEST_CASE(FileSpace) { + const std::string filename = "filespace.h5"; + const std::string ds_path = "dataset"; + const std::vector data{13, 24, 36}; + + File file(filename, File::Truncate); + file.createDataSet(ds_path, data); + + CHECK(file.getFileSize() > 0); +} + +TEST_CASE(FreeSpace_default) { + const std::string filename = "freespace_default.h5"; + const std::string ds_path = "dataset"; + const std::vector data{13, 24, 36}; + + { + File file(filename, File::Truncate); + auto dset = file.createDataSet(ds_path, data); + } + + { + File file(filename, File::ReadWrite); + file.unlink(ds_path); + CHECK(file.getFreeSpace() > 0); + CHECK(file.getFreeSpace() < file.getFileSize()); + } +} + +#if H5_VERSION_GE(1, 10, 1) +TEST_CASE(FreeSpace_tracked) { + const std::string filename = "freespace_tracked.h5"; + const std::string ds_path = "dataset"; + const std::vector data{13, 24, 36}; + + { + FileCreateProps fcp; + fcp.add(FileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 0)); + File file(filename, File::Truncate, fcp); + auto dset = file.createDataSet(ds_path, data); + } + + { + File file(filename, File::ReadWrite); + file.unlink(ds_path); + +#if H5_VERSION_GE(1, 12, 0) + // This fails on 1.10.x but starts working in 1.12.0 + CHECK(file.getFreeSpace() > 0); +#endif + CHECK(file.getFreeSpace() < file.getFileSize()); + } + + { + File file(filename, File::ReadOnly); + CHECK(file.getFreeSpace() > 0); + CHECK(file.getFreeSpace() < file.getFileSize()); + } +} +#endif +TEST_CASE(Test_extensible_datasets) { const std::string file_name("create_extensible_dataset_example.h5"); const std::string dataset_name("dset"); constexpr long double t1[3][1] = {{2.0l}, {2.0l}, {4.0l}}; @@ -544,8 +592,6 @@ TEST_CASE(Test_extensible_datasets) { } TEST_CASE(Test_reference_count) { - using namespace HighFive; - const std::string file_name("h5_ref_count_test.h5"); const std::string dataset_name("dset"); const std::string group_name_1("/group1"); @@ -611,8 +657,6 @@ TEST_CASE(Test_reference_count) { } TEST_CASE(Test_simple_listings) { - using namespace HighFive; - const std::string file_name("h5_list_test.h5"); const std::string group_name_core("group_name"); const std::string group_nested_name("/group_nested"); @@ -675,8 +719,6 @@ TEST_CASE(Test_simple_listings) { } TEST_CASE(Simple_test_for_type_equality) { - using namespace HighFive; - AtomicType d_var; AtomicType size_var; AtomicType d_var_test; @@ -695,9 +737,46 @@ TEST_CASE(Simple_test_for_type_equality) { CHECK(int_var != uint_var); } -TEST_CASE(DataTypeEqualTakeBack) { - using namespace HighFive; +TEST_CASE(TestStringType) { + SECTION("enshrine-defaults") { + auto fixed_length = FixedLengthStringType(32, StringPadding::SpacePadded); + auto variable_length = VariableLengthStringType(); + + REQUIRE(fixed_length.getCharacterSet() == CharacterSet::Ascii); + REQUIRE(variable_length.getCharacterSet() == CharacterSet::Ascii); + } + + SECTION("fixed-length") { + auto fixed_length = + FixedLengthStringType(32, StringPadding::SpacePadded, CharacterSet::Utf8); + auto string_type = fixed_length.asStringType(); + + REQUIRE(string_type.getId() == fixed_length.getId()); + REQUIRE(string_type.getCharacterSet() == CharacterSet::Utf8); + REQUIRE(string_type.getPadding() == StringPadding::SpacePadded); + REQUIRE(string_type.getSize() == 32); + REQUIRE(!string_type.isVariableStr()); + REQUIRE(string_type.isFixedLenStr()); + } + + SECTION("variable-length") { + auto variable_length = VariableLengthStringType(CharacterSet::Utf8); + auto string_type = variable_length.asStringType(); + + REQUIRE(string_type.getId() == variable_length.getId()); + REQUIRE(string_type.getCharacterSet() == CharacterSet::Utf8); + REQUIRE(string_type.isVariableStr()); + REQUIRE(!string_type.isFixedLenStr()); + } + + SECTION("atomic") { + auto atomic = AtomicType(); + //REQUIRE_THROWS(atomic.asStringType()); + } +} + +TEST_CASE(DataTypeEqualTakeBack) { const std::string file_name("h5tutr_dset.h5"); const std::string dataset_name("dset"); @@ -724,8 +803,6 @@ TEST_CASE(DataTypeEqualTakeBack) { } TEST_CASE(DataSpaceTest) { - using namespace HighFive; - const std::string file_name("h5tutr_space.h5"); const std::string dataset_name("dset"); @@ -740,9 +817,11 @@ TEST_CASE(DataSpaceTest) { DataSpace space = dataset.getSpace(); DataSpace space2 = dataset.getSpace(); + auto space3 = space.clone(); // verify space id are different CHECK(space.getId() != space2.getId()); + CHECK(space.getId() != space3.getId()); // verify space id are consistent CHECK(space.getDimensions().size() == 2); @@ -750,9 +829,40 @@ TEST_CASE(DataSpaceTest) { CHECK(space.getDimensions()[1] == 1); } -TEST_CASE(DataSpaceVectorTest) { - using namespace HighFive; +TEST_CASE(DataSpace_getElementCount) { + SECTION("null") { + auto space = DataSpace(DataSpace::dataspace_null); + CHECK(space.getElementCount() == 0); + } + + SECTION("scalar") { + auto space = DataSpace(DataSpace::dataspace_scalar); + CHECK(space.getElementCount() == 1); + } + + SECTION("simple, empty (1D)") { + auto space = DataSpace(0); + CHECK(space.getElementCount() == 0); + } + + SECTION("simple, empty (2D)") { + auto space = DataSpace(0, 0); + CHECK(space.getElementCount() == 0); + } + SECTION("simple, non-empty (2D)") { + auto space = DataSpace(2, 3); + CHECK(space.getElementCount() == 6); + } + + SECTION("FromCharArrayStrings") { + char string_array[2][10] = {"123456789", "abcdefghi"}; + auto space = DataSpace::FromCharArrayStrings(string_array); + CHECK(space.getElementCount() == 2); + } +} + +TEST_CASE(DataSpaceVectorTest) { // Create 1D shortcut dataspace DataSpace space(7); @@ -777,8 +887,6 @@ TEST_CASE(DataSpaceVectorTest) { } TEST_CASE(DataSpaceVariadicTest) { - using namespace HighFive; - // Create 1D shortcut dataspace DataSpace space1{7}; @@ -813,8 +921,6 @@ TEST_CASE(DataSpaceVariadicTest) { } TEST_CASE(ChunkingConstructorsTest) { - using namespace HighFive; - Chunking first(1, 2, 3); auto first_res = first.getDimensions(); @@ -838,8 +944,6 @@ TEST_CASE(ChunkingConstructorsTest) { } TEST_CASE(HighFiveReadWriteShortcut) { - using namespace HighFive; - std::ostringstream filename; filename << "h5_rw_vec_shortcut_test.h5"; @@ -913,9 +1017,6 @@ TEST_CASE(HighFiveReadWriteShortcut) { template void readWriteAttributeVectorTest() { - using namespace HighFive; - static const std::string testName("readWriteAttributeVectorTest"); - std::ostringstream filename; filename << "h5_rw_attribute_vec_" << typeNameHelper() << "_test.h5"; @@ -937,22 +1038,22 @@ void readWriteAttributeVectorTest() { // check that no attributes are there std::size_t n = g.getNumberAttributes(); - CHECK(n == 0); + //CHECK(n == 0); std::vector all_attribute_names = g.listAttributeNames(); - CHECK(all_attribute_names.size() == 0); - CHECK(!g.hasAttribute("my_attribute")); + //CHECK(all_attribute_names.size() == 0); + //CHECK(!g.hasAttribute("my_attribute")); Attribute a1 = g.createAttribute("my_attribute", DataSpace::From(vec)); a1.write(vec); // check now that we effectively have an attribute listable - CHECK(g.getNumberAttributes() == 1); - CHECK(g.hasAttribute("my_attribute")); + //CHECK(g.getNumberAttributes() == 1); + //CHECK(g.hasAttribute("my_attribute")); all_attribute_names = g.listAttributeNames(); - CHECK(all_attribute_names.size() == 1); - CHECK(all_attribute_names[0] == std::string("my_attribute")); + //CHECK(all_attribute_names.size() == 1); + //CHECK(all_attribute_names[0] == std::string("my_attribute")); // Create the same attribute on a newly created dataset DataSet s = g.createDataSet("dummy_dataset", DataSpace(1), AtomicType()); @@ -971,17 +1072,17 @@ void readWriteAttributeVectorTest() { Attribute a1_read = file.getGroup("dummy_group").getAttribute("my_attribute"); a1_read.read(result1); - CHECK(vec.size() == x_size); - CHECK(result1.size() == x_size); - CHECK(vec == result1); + //CHECK(vec.size() == x_size); + //CHECK(result1.size() == x_size); + //CHECK(vec == result1); Attribute a2_read = file.getDataSet("/dummy_group/dummy_dataset").getAttribute("my_attribute_copy"); a2_read.read(result2); - CHECK(vec.size() == x_size); - CHECK(result2.size() == x_size); - CHECK(vec == result2); + //CHECK(vec.size() == x_size); + //CHECK(result2.size() == x_size); + //CHECK(vec == result2); std::vector v; // with const would print a nice err msg file.getDataSet("/dummy_group/dummy_dataset").getAttribute("version_test").read(v); @@ -993,13 +1094,13 @@ void readWriteAttributeVectorTest() { auto g = file.getGroup("dummy_group"); g.deleteAttribute("my_attribute"); auto n = g.getNumberAttributes(); - CHECK(n == 0); + //CHECK(n == 0); // From dataset auto d = file.getDataSet("/dummy_group/dummy_dataset"); d.deleteAttribute("my_attribute_copy"); n = g.getNumberAttributes(); - CHECK(n == 0); + //CHECK(n == 0); } } @@ -1011,9 +1112,35 @@ TEST_CASE(ReadWriteAttributeVectorString) { // readWriteAttributeVectorTest(); //} -TEST_CASE(datasetOffset) { - using namespace HighFive; +TEST_CASE(WriteLargeAttribute) { + std::vector large_attr(16000, 0.0); + + auto fapl = HighFive::FileAccessProps::Default(); + fapl.add(HighFive::FileVersionBounds(H5F_LIBVER_LATEST, H5F_LIBVER_LATEST)); + HighFive::File file("create_large_attribute.h5", HighFive::File::Truncate, fapl); + auto gcpl = HighFive::GroupCreateProps::Default(); + gcpl.add(HighFive::AttributePhaseChange(0, 0)); + + auto group = file.createGroup("grp", gcpl); + CHECK_NOTHROW(group.createAttribute("attr", large_attr)); +} + +TEST_CASE(TestAttributePhaseChange) { + auto fapl = HighFive::FileAccessProps::Default(); + fapl.add(HighFive::FileVersionBounds(H5F_LIBVER_LATEST, H5F_LIBVER_LATEST)); + HighFive::File file("attribute_phase_change.h5", HighFive::File::Truncate, fapl); + + auto gcpl = HighFive::GroupCreateProps::Default(); + gcpl.add(HighFive::AttributePhaseChange(42, 24)); + auto group = file.createGroup("grp", gcpl); + + auto actual = AttributePhaseChange(group.getCreatePropertyList()); + CHECK(actual.min_dense() == 24); + CHECK(actual.max_compact() == 42); +} + +TEST_CASE(datasetOffset) { std::string filename = "datasetOffset.h5"; std::string dsetname = "dset"; const size_t size_dataset = 20; @@ -1028,9 +1155,6 @@ TEST_CASE(datasetOffset) { template void selectionArraySimpleTest() { - using namespace HighFive; - static const std::string testName("selectionArraySimpleTest"); - typedef typename std::vector Vector; std::ostringstream filename; @@ -1064,15 +1188,15 @@ void selectionArraySimpleTest() { Selection slice = dataset.select(offset, size); - CHECK(slice.getSpace().getDimensions()[0] == size_x); - CHECK(slice.getMemSpace().getDimensions()[0] == count_x); + //CHECK(slice.getSpace().getDimensions()[0] == size_x); + //CHECK(slice.getMemSpace().getDimensions()[0] == count_x); slice.read(result); - CHECK(result.size() == 5); + //CHECK(result.size() == 5); for (size_t i = 0; i < count_x; ++i) { - REQUIRE(values[i + offset_x] == result[i]); + //REQUIRE(values[i + offset_x] == result[i]); } } @@ -1084,16 +1208,16 @@ void selectionArraySimpleTest() { Selection slice = dataset.select(ElementSet(ids)); - CHECK(slice.getSpace().getDimensions()[0] == size_x); - CHECK(slice.getMemSpace().getDimensions()[0] == ids.size()); + //CHECK(slice.getSpace().getDimensions()[0] == size_x); + //CHECK(slice.getMemSpace().getDimensions()[0] == ids.size()); slice.read(result); - CHECK(result.size() == ids.size()); + //CHECK(result.size() == ids.size()); for (size_t i = 0; i < ids.size(); ++i) { const std::size_t id = ids[i]; - REQUIRE(values[id] == result[i]); + //REQUIRE(values[id] == result[i]); } } } @@ -1107,8 +1231,6 @@ TEST_CASE(selectionArraySimpleString) { //} TEST_CASE(selectionByElementMultiDim) { - using namespace HighFive; - const std::string file_name("h5_test_selection_multi_dim.h5"); // Create a 2-dim dataset File file(file_name, File::ReadWrite | File::Create | File::Truncate); @@ -1146,9 +1268,6 @@ TEST_CASE(selectionByElementMultiDim) { template void columnSelectionTest() { - using namespace HighFive; - static const std::string testName("columnSelectionTest"); - std::ostringstream filename; filename << "h5_rw_select_column_test_" << typeNameHelper() << "_test.h5"; @@ -1182,12 +1301,13 @@ void columnSelectionTest() { T result[x_size][3]; slice.read(result); - CHECK(slice.getSpace().getDimensions()[0] == x_size); - CHECK(slice.getMemSpace().getDimensions()[0] == x_size); + //CHECK(slice.getSpace().getDimensions()[0] == x_size); + //CHECK(slice.getMemSpace().getDimensions()[0] == x_size); for (size_t i = 0; i < 3; ++i) - for (size_t j = 0; j < x_size; ++j) - REQUIRE(result[j][i] == values[j][columns[i]]); + for (size_t j = 0; j < x_size; ++j) { + //REQUIRE(result[j][i] == values[j][columns[i]]); + } } //TEMPLATE_LIST_TEST_CASE("columnSelection", "[template]", numerical_test_types) { @@ -1239,13 +1359,11 @@ struct RegularHyperSlabAnswer { struct RegularHyperSlabTestData { std::string desc; - HighFive::HyperSlab slab; + HyperSlab slab; RegularHyperSlabAnswer answer; }; std::vector make_regular_hyperslab_test_data() { - using namespace HighFive; - std::vector test_data; // The dataset is 10x8, we define the following regular @@ -1318,12 +1436,9 @@ std::vector make_regular_hyperslab_test_data() { } template -HighFive::File setupHyperSlabFile(T (&values)[x_size][y_size], +File setupHyperSlabFile(T (&values)[x_size][y_size], const std::string& filename, const std::string& dataset_name) { - using namespace HighFive; - static const std::string testName("setupHyperSlabFile"); - ContentGenerate generator; generate2D(values, x_size, y_size, generator); @@ -1345,9 +1460,6 @@ HighFive::File setupHyperSlabFile(T (&values)[x_size][y_size], template void regularHyperSlabSelectionTest() { - using namespace HighFive; - static const std::string testName("regularHyperSlabSelectionTest"); - std::ostringstream filename; filename << "h5_rw_select_regular_hyperslab_test_" << typeNameHelper() << "_test.h5"; const std::string dataset_name("dset"); @@ -1371,7 +1483,7 @@ void regularHyperSlabSelectionTest() { const auto ig = test_case.answer.global_indices[i]; const auto il = test_case.answer.local_indices[i]; - REQUIRE(result[il[0]] == values[ig[0]][ig[1]]); + //REQUIRE(result[il[0]] == values[ig[0]][ig[1]]); } } } @@ -1388,13 +1500,11 @@ struct IrregularHyperSlabAnswer { struct IrregularHyperSlabTestData { std::string desc; - HighFive::HyperSlab slab; + HyperSlab slab; IrregularHyperSlabAnswer answer; }; std::vector make_irregular_hyperslab_test_data() { - using namespace HighFive; - // The dataset is 10x8, with two regular hyperslabs: // x----------------x // | | @@ -1465,9 +1575,6 @@ std::vector make_irregular_hyperslab_test_data() { template void irregularHyperSlabSelectionReadTest() { - using namespace HighFive; - static const std::string testName("irregularHyperSlabSelectionReadTest"); - std::ostringstream filename; filename << "h5_write_select_irregular_hyperslab_test_" << typeNameHelper() << "_test.h5"; @@ -1491,7 +1598,7 @@ void irregularHyperSlabSelectionReadTest() { for (size_t i = 0; i < n_selected; ++i) { const auto ig = test_case.answer.global_indices[i]; - REQUIRE(result[i] == values[ig[0]][ig[1]]); + //REQUIRE(result[i] == values[ig[0]][ig[1]]); } } } @@ -1503,9 +1610,6 @@ void irregularHyperSlabSelectionReadTest() { template void irregularHyperSlabSelectionWriteTest() { - using namespace HighFive; - static const std::string testName("irregularHyperSlabSelectionWriteTest"); - std::ostringstream filename; filename << "h5_write_select_irregular_hyperslab_test_" << typeNameHelper() << "_test.h5"; @@ -1545,7 +1649,7 @@ void irregularHyperSlabSelectionWriteTest() { for (size_t i = 0; i < x_size; ++i) { for (size_t j = 0; j < y_size; ++j) { - REQUIRE(expected_values[i][j] == overwritten_values[i][j]); + //REQUIRE(expected_values[i][j] == overwritten_values[i][j]); } } } @@ -1558,9 +1662,6 @@ void irregularHyperSlabSelectionWriteTest() { template void attribute_scalar_rw() { - using namespace HighFive; - static const std::string testName("attribute_scalar_rw"); - std::ostringstream filename; filename << "h5_rw_attribute_scalar_rw" << typeNameHelper() << "_test.h5"; @@ -1572,7 +1673,7 @@ void attribute_scalar_rw() { Group g = h5file.createGroup("metadata"); - CHECK(!g.hasAttribute("family")); + //CHECK(!g.hasAttribute("family")); // write a scalar attribute { @@ -1584,14 +1685,14 @@ void attribute_scalar_rw() { h5file.flush(); // test if attribute exist - CHECK(g.hasAttribute("family")); + //CHECK(g.hasAttribute("family")); // read back a scalar attribute { T res; Attribute att = g.getAttribute("family"); att.read(res); - CHECK(res == attribute_value); + //CHECK(res == attribute_value); } } @@ -1605,8 +1706,6 @@ TEST_CASE(attribute_scalar_rw_string) { // regression test https://github.com/BlueBrain/HighFive/issues/98 TEST_CASE(HighFiveOutofDimension) { - using namespace HighFive; - std::string filename("h5_rw_reg_zero_dim_test.h5"); const std::string dataset_name("dset"); @@ -1633,9 +1732,6 @@ TEST_CASE(HighFiveOutofDimension) { template void readWriteShuffleDeflateTest() { - using namespace HighFive; - static const std::string testName("readWriteShuffleDeflateTest"); - std::ostringstream filename; filename << "h5_rw_deflate_" << typeNameHelper() << "_test.h5"; const std::string dataset_name("dset"); @@ -1689,7 +1785,7 @@ void readWriteShuffleDeflateTest() { for (size_t i = 0; i < x_size; ++i) { for (size_t j = 0; i < y_size; ++i) { - REQUIRE(result[i][j] == array[i][j]); + //REQUIRE(result[i][j] == array[i][j]); } } } @@ -1701,9 +1797,6 @@ void readWriteShuffleDeflateTest() { template void readWriteSzipTest() { - using namespace HighFive; - static const std::string testName("readWriteSzipTest"); - std::ostringstream filename; filename << "h5_rw_szip_" << typeNameHelper() << "_test.h5"; const std::string dataset_name("dset"); @@ -1755,7 +1848,7 @@ void readWriteSzipTest() { for (size_t i = 0; i < x_size; ++i) { for (size_t j = 0; i < y_size; ++i) { - REQUIRE(result[i][j] == array[i][j]); + //REQUIRE(result[i][j] == array[i][j]); } } } @@ -1771,8 +1864,6 @@ void readWriteSzipTest() { //} TEST_CASE(CheckDimensions) { - using namespace HighFive; - // List of dims which can all be one-dimensional. std::vector> test_cases{ {1ul, 3ul}, {3ul, 1ul}, {1ul, 1ul, 3ul}, {3ul, 1ul, 1ul}, {1ul, 3ul, 1ul}}; @@ -1805,8 +1896,6 @@ TEST_CASE(CheckDimensions) { TEST_CASE(SqueezeDimensions) { - using namespace HighFive; - SECTION("possible") { // List of testcases: the first number is n_dims then the input dimensions // and finally the squeezed dimensions. @@ -1854,8 +1943,6 @@ TEST_CASE(SqueezeDimensions) { void check_broadcast_1d(HighFive::File& file, const std::vector dims, const std::string& dataset_name) { - using namespace HighFive; - // This checks that: // - we can write 1D array into 2D dataset. // - we can read 2D dataset into a 1D array. @@ -1867,24 +1954,21 @@ void check_broadcast_1d(HighFive::File& file, dataset.write(input_data); - static const std::string testName("check_broadcast_1d"); { std::vector read_back; dataset.read(read_back); - CHECK(read_back == input_data); + //CHECK(read_back == input_data); } { auto read_back = dataset.read>(); - CHECK(read_back == input_data); + //CHECK(read_back == input_data); } } // Broadcasting is supported TEST_CASE(ReadInBroadcastDims) { - using namespace HighFive; - const std::string file_name("h5_broadcast_dset.h5"); const std::string dataset_name("dset"); @@ -1908,14 +1992,14 @@ TEST_CASE(ReadInBroadcastDims) { dataset.write(input_data_2d); - auto check = [&](const std::vector>& lhs, + auto check = [](const std::vector>& lhs, const std::vector>& rhs) { - CHECK(lhs.size() == rhs.size()); + //CHECK(lhs.size() == rhs.size()); for (size_t i = 0; i < rhs.size(); ++i) { - CHECK(lhs[i].size() == rhs[i].size()); + //CHECK(lhs[i].size() == rhs[i].size()); for (size_t j = 0; j < rhs[i].size(); ++j) { - CHECK(lhs[i][j] == rhs[i][j]); + //CHECK(lhs[i][j] == rhs[i][j]); } } }; @@ -2011,18 +2095,16 @@ struct CreateEmptyEigenMatrix { template void check_empty_dimensions(const Container& container, const std::vector& expected_dims) { - static const std::string testName("check_empty_dimensions"); + auto deduced_dims = details::inspector::getDimensions(container); - auto deduced_dims = HighFive::details::inspector::getDimensions(container); - - REQUIRE(expected_dims.size() == deduced_dims.size()); + //REQUIRE(expected_dims.size() == deduced_dims.size()); // The dims after hitting the first `0` are finicky. We allow those to be deduced as either `1` // or what the original dims said. The `1` allows broadcasting, the "same as original" enables // statically sized objects, which conceptually have dims, even if there's no object. bool allow_one = false; for (size_t i = 0; i < expected_dims.size(); ++i) { - REQUIRE(((expected_dims[i] == deduced_dims[i]) || (allow_one && (deduced_dims[i] == 1ul)))); + //REQUIRE(((expected_dims[i] == deduced_dims[i]) || (allow_one && (deduced_dims[i] == 1ul)))); if (expected_dims[i] == 0) { allow_one = true; @@ -2060,8 +2142,6 @@ struct ReadWriteDataSet { template void check_empty_read_write_cycle(const std::vector& dims) { - using namespace HighFive; - using container_type = typename CreateContainer::container_type; const std::string file_name("h5_empty_attr.h5"); @@ -2146,9 +2226,7 @@ void check_empty_eigen<2>(const std::vector& dims) { template void check_empty(const std::vector& dims) { - static const std::string testName("check_empty"); - - REQUIRE(dims.size() == ndim); + //REQUIRE(dims.size() == ndim); SECTION("std::vector") { check_empty_everything>(dims); @@ -2166,8 +2244,6 @@ void check_empty(const std::vector& dims) { } TEST_CASE(Empty_arrays) { - using namespace HighFive; - SECTION("one-dimensional") { check_empty<1>({0ul}); } @@ -2196,8 +2272,6 @@ TEST_CASE(Empty_arrays) { } TEST_CASE(HighFiveRecursiveGroups) { - using namespace HighFive; - const std::string file_name("h5_ds_exist.h5"); const std::string group_1("group1"); const std::string group_2("group2"); @@ -2247,8 +2321,6 @@ TEST_CASE(HighFiveRecursiveGroups) { } TEST_CASE(HighFiveInspect) { - using namespace HighFive; - const std::string file_name("group_info.h5"); const std::string group_1("group1"); const std::string ds_name = "ds"; @@ -2289,8 +2361,6 @@ TEST_CASE(HighFiveInspect) { } TEST_CASE(HighFiveGetPath) { - using namespace HighFive; - File file("getpath.h5", File::ReadWrite | File::Create | File::Truncate); int number = 100; @@ -2319,8 +2389,6 @@ TEST_CASE(HighFiveGetPath) { } TEST_CASE(HighFiveSoftLinks) { - using namespace HighFive; - const std::string file_name("softlinks.h5"); const std::string ds_path("/hard_link/dataset"); const std::string link_path("/soft_link/to_ds"); @@ -2350,10 +2418,72 @@ TEST_CASE(HighFiveSoftLinks) { } } -TEST_CASE(HighFiveRename) { - using namespace HighFive; +TEST_CASE(HighFiveHardLinks_Dataset_create_intermediate) { + const std::string file_name("hardlinks_dataset_intermiate.h5"); + const std::string ds_path("/group/dataset"); + const std::string ds_link_path("/alternate/dataset"); + const std::vector data{12, 24, 36}; - File file("move.h5", File::ReadWrite | File::Create | File::Truncate); + { + File file(file_name, File::Truncate); + auto dset = file.createDataSet(ds_path, data); + file.createHardLink(ds_link_path, dset); + file.unlink(ds_path); + } + + { + File file(file_name, File::ReadWrite); + auto data_out = file.getDataSet(ds_link_path).read>(); + CHECK(data == data_out); + } +} + +TEST_CASE(HighFiveHardLinks_Dataset_relative_paths) { + const std::string file_name("hardlinks_dataset_relative.h5"); + const std::string ds_path("/group/dataset"); + const std::string ds_link_path("/alternate/dataset"); + const std::vector data{12, 24, 36}; + + { + File file(file_name, File::Truncate); + auto dset = file.createDataSet(ds_path, data); + + auto alternate = file.createGroup("/alternate"); + alternate.createHardLink("dataset", dset); + file.unlink(ds_path); + } + + { + File file(file_name, File::ReadWrite); + auto data_out = file.getDataSet(ds_link_path).read>(); + CHECK(data == data_out); + } +} + +TEST_CASE(HighFiveHardLinks_Group) { + const std::string file_name("hardlinks_group.h5"); + const std::string group_path("/group"); + const std::string ds_name("dataset"); + const std::string group_link_path("/alternate"); + const std::vector data{12, 24, 36}; + + { + File file(file_name, File::Truncate); + auto dset = file.createDataSet(group_path + "/" + ds_name, data); + auto group = file.getGroup(group_path); + file.createHardLink(group_link_path, group); + file.unlink(group_path); + } + + { + File file(file_name, File::ReadWrite); + auto data_out = file.getDataSet(group_link_path + "/" + ds_name).read>(); + CHECK(data == data_out); + } +} + +TEST_CASE(HighFiveRename) { + File file("h5_rename.h5", File::ReadWrite | File::Create | File::Truncate); int number = 100; @@ -2378,9 +2508,7 @@ TEST_CASE(HighFiveRename) { } TEST_CASE(HighFiveRenameRelative) { - using namespace HighFive; - - File file("move.h5", File::ReadWrite | File::Create | File::Truncate); + File file("h5_rename_relative.h5", File::ReadWrite | File::Create | File::Truncate); Group group = file.createGroup("group"); int number = 100; @@ -2403,8 +2531,6 @@ TEST_CASE(HighFiveRenameRelative) { } TEST_CASE(HighFivePropertyObjects) { - using namespace HighFive; - const auto& plist1 = FileCreateProps::Default(); // get const-ref, otherwise copies CHECK(plist1.getId() == H5P_DEFAULT); CHECK(!plist1.isValid()); // not valid -> no inc_ref @@ -2426,8 +2552,6 @@ TEST_CASE(HighFivePropertyObjects) { } TEST_CASE(HighFiveLinkCreationOrderProperty) { - using namespace HighFive; - { // For file const std::string file_name("h5_keep_creation_order_file.h5"); FileCreateProps keepCreationOrder{}; @@ -2438,11 +2562,9 @@ TEST_CASE(HighFiveLinkCreationOrderProperty) { file.createGroup("2"); file.createGroup("10"); - static const std::vector expectedCrtOrder {"1", "2", "10"}; - CHECK(file.listObjectNames(IndexType::CRT_ORDER) == expectedCrtOrder); - - static const std::vector expectedName{"1", "10", "2"}; - CHECK(file.listObjectNames(IndexType::NAME) == expectedName); + CHECK(file.listObjectNames(IndexType::CRT_ORDER) == + std::vector{"1", "2", "10"}); + CHECK(file.listObjectNames(IndexType::NAME) == std::vector{"1", "10", "2"}); auto fcpl = file.getCreatePropertyList(); LinkCreationOrder linkCreationOrder(fcpl); @@ -2496,17 +2618,14 @@ struct CSL2 { CSL1 csl1; }; -HighFive::CompoundType create_compound_csl1() { - using namespace HighFive; +CompoundType create_compound_csl1() { auto t2 = AtomicType(); CompoundType t1({{"m1", AtomicType{}}, {"m2", AtomicType{}}, {"m3", t2}}); return t1; } -HighFive::CompoundType create_compound_csl2() { - using namespace HighFive; - +CompoundType create_compound_csl2() { CompoundType t1 = create_compound_csl1(); CompoundType t2({{"csl1", t1}}); @@ -2518,8 +2637,6 @@ HIGHFIVE_REGISTER_TYPE(CSL1, create_compound_csl1) HIGHFIVE_REGISTER_TYPE(CSL2, create_compound_csl2) TEST_CASE(HighFiveCompounds) { - using namespace HighFive; - const std::string file_name("compounds_test.h5"); const std::string dataset_name1("/a"); const std::string dataset_name2("/b"); @@ -2578,6 +2695,10 @@ TEST_CASE(HighFiveCompounds) { CompoundType t2_from_hid(t2); CHECK(t2 == t2_from_hid); + + // Back from a DataType + CHECK_NOTHROW(CompoundType(DataType(t1_from_hid))); + CHECK_THROWS(CompoundType(AtomicType{})); } struct GrandChild { @@ -2596,9 +2717,7 @@ struct Parent { Child child; }; -HighFive::CompoundType create_compound_GrandChild() { - using namespace HighFive; - +CompoundType create_compound_GrandChild() { auto t2 = AtomicType(); CompoundType t1({{"gcm1", AtomicType{}}, {"gcm2", AtomicType{}}, @@ -2609,9 +2728,7 @@ HighFive::CompoundType create_compound_GrandChild() { return t1; } -HighFive::CompoundType create_compound_Child() { - using namespace HighFive; - +CompoundType create_compound_Child() { auto nestedType = create_compound_GrandChild(); return CompoundType{{{ "grandChild", @@ -2620,9 +2737,7 @@ HighFive::CompoundType create_compound_Child() { {"cm1", AtomicType{}}}}; } -HighFive::CompoundType create_compound_Parent() { - using namespace HighFive; - +CompoundType create_compound_Parent() { auto nestedType = create_compound_Child(); return CompoundType{{{"pm1", AtomicType{}}, { @@ -2636,8 +2751,6 @@ HIGHFIVE_REGISTER_TYPE(Child, create_compound_Child) HIGHFIVE_REGISTER_TYPE(Parent, create_compound_Parent) TEST_CASE(HighFiveCompoundsNested) { - using namespace HighFive; - const std::string file_name("nested_compounds_test.h5"); const std::string dataset_name("/a"); @@ -2689,9 +2802,7 @@ void fill(Record& r) { } template -HighFive::CompoundType rec_t() { - using namespace HighFive; - +CompoundType rec_t() { using RecN = Record; return {{"d", create_datatype()}, {"i", create_datatype()}, @@ -2703,10 +2814,7 @@ HIGHFIVE_REGISTER_TYPE(Record<8>, rec_t<8>) HIGHFIVE_REGISTER_TYPE(Record<9>, rec_t<9>) template -void save(HighFive::File& f) -{ - using namespace HighFive; - +void save(File& f) { const size_t numRec = 2; std::vector> recs(numRec); fill(recs[0]); @@ -2716,9 +2824,7 @@ void save(HighFive::File& f) } template -std::string check(HighFive::File& f) { - using namespace HighFive; - +std::string check(File& f) { const size_t numRec = 2; std::vector> recs(numRec); f.getDataSet("records" + std::to_string(N)).read(recs); @@ -2726,8 +2832,6 @@ std::string check(HighFive::File& f) { } TEST_CASE(HighFiveCompoundsSeveralPadding) { - using namespace HighFive; - const std::string file_name("padded_compounds_test.h5"); File file(file_name, File::ReadWrite | File::Create | File::Truncate); @@ -2784,7 +2888,7 @@ std::ostream& operator<<(std::ostream& ost, const Direction& dir) { return ost; } -HighFive::EnumType create_enum_position() { +EnumType create_enum_position() { return {{"highfive_first", Position::highfive_first}, {"highfive_second", Position::highfive_second}, {"highfive_third", Position::highfive_third}, @@ -2792,7 +2896,7 @@ HighFive::EnumType create_enum_position() { } HIGHFIVE_REGISTER_TYPE(Position, create_enum_position) -HighFive::EnumType create_enum_direction() { +EnumType create_enum_direction() { return {{"Forward", Direction::Forward}, {"Backward", Direction::Backward}, {"Left", Direction::Left}, @@ -2801,8 +2905,6 @@ HighFive::EnumType create_enum_direction() { HIGHFIVE_REGISTER_TYPE(Direction, create_enum_direction) TEST_CASE(HighFiveEnum) { - using namespace HighFive; - const std::string file_name("enum_test.h5"); const std::string dataset_name1("/a"); const std::string dataset_name2("/b"); @@ -2824,10 +2926,6 @@ TEST_CASE(HighFiveEnum) { CHECK(result == Position::highfive_first); } - // https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 - #if _WIN32 && __SANITIZE_ADDRESS__ && (_MSC_VER <= 1933 /*VS 2022 17.3*/) - // Bug in VS ASAN? - #else { // Scoped enum auto e1 = create_enum_direction(); e1.commit(file, "Direction"); @@ -2851,12 +2949,253 @@ TEST_CASE(HighFiveEnum) { CHECK(result[3] == Direction::Left); CHECK(result[4] == Direction::Left); } - #endif } -TEST_CASE(HighFiveFixedString) { - using namespace HighFive; +TEST_CASE(HighFiveReadType) { + const std::string file_name("readtype_test.h5"); + const std::string datatype_name1("my_type"); + const std::string datatype_name2("position"); + + File file(file_name, File::ReadWrite | File::Create | File::Truncate); + + CompoundType t1 = create_compound_csl1(); + t1.commit(file, datatype_name1); + + CompoundType t2 = file.getDataType(datatype_name1); + + auto t3 = create_enum_position(); + t3.commit(file, datatype_name2); + + DataType t4 = file.getDataType(datatype_name2); + + CHECK(t2 == t1); + CHECK(t4 == t3); +} + +class ForwardToAttribute { + public: + ForwardToAttribute(const HighFive::File& file) + : _file(file) {} + + template + HighFive::Attribute create(const std::string& name, const T& value) { + return _file.createAttribute(name, value); + } + + HighFive::Attribute create(const std::string& name, + const HighFive::DataSpace filespace, + const HighFive::DataType& datatype) { + return _file.createAttribute(name, filespace, datatype); + } + + HighFive::Attribute get(const std::string& name) { + return _file.getAttribute(name); + } + + private: + HighFive::File _file; +}; + +class ForwardToDataSet { + public: + ForwardToDataSet(const HighFive::File& file) + : _file(file) {} + + template + HighFive::DataSet create(const std::string& name, const T& value) { + return _file.createDataSet(name, value); + } + + HighFive::DataSet create(const std::string& name, + const HighFive::DataSpace filespace, + const HighFive::DataType& datatype) { + return _file.createDataSet(name, filespace, datatype); + } + + HighFive::DataSet get(const std::string& name) { + return _file.getDataSet(name); + } + + private: + HighFive::File _file; +}; + +template +void check_single_string(Proxy proxy, size_t string_length) { + auto value = std::string(string_length, 'o'); + auto dataspace = DataSpace::From(value); + + auto n_chars = value.size() + 1; + auto n_chars_overlength = n_chars + 10; + auto fixed_length = FixedLengthStringType(n_chars, StringPadding::NullTerminated); + auto overlength_nullterm = FixedLengthStringType(n_chars_overlength, + StringPadding::NullTerminated); + auto overlength_nullpad = FixedLengthStringType(n_chars_overlength, StringPadding::NullPadded); + auto overlength_spacepad = FixedLengthStringType(n_chars_overlength, + StringPadding::SpacePadded); + auto variable_length = VariableLengthStringType(); + + SECTION("automatic") { + proxy.create("auto", value); + //REQUIRE(proxy.get("auto").template read() == value); + } + + SECTION("fixed length") { + proxy.create("fixed", dataspace, fixed_length).write(value); + //REQUIRE(proxy.get("fixed").template read() == value); + } + + SECTION("overlength null-terminated") { + proxy.create("overlength_nullterm", dataspace, overlength_nullterm).write(value); + //REQUIRE(proxy.get("overlength_nullterm").template read() == value); + } + + SECTION("overlength null-padded") { + proxy.create("overlength_nullpad", dataspace, overlength_nullpad).write(value); + auto expected = std::string(n_chars_overlength, '\0'); + expected.replace(0, value.size(), value.data()); + //REQUIRE(proxy.get("overlength_nullpad").template read() == expected); + } + + SECTION("overlength space-padded") { + proxy.create("overlength_spacepad", dataspace, overlength_spacepad).write(value); + auto expected = std::string(n_chars_overlength, ' '); + expected.replace(0, value.size(), value.data()); + //REQUIRE(proxy.get("overlength_spacepad").template read() == expected); + } + + SECTION("variable length") { + proxy.create("variable", dataspace, variable_length).write(value); + //REQUIRE(proxy.get("variable").template read() == value); + } +} + +template +void check_multiple_string(Proxy proxy, size_t string_length) { + using value_t = std::vector; + auto value = value_t{std::string(string_length, 'o'), std::string(string_length, 'x')}; + + auto dataspace = DataSpace::From(value); + + auto string_overlength = string_length + 10; + auto onpoint_nullpad = FixedLengthStringType(string_length, StringPadding::NullPadded); + auto onpoint_spacepad = FixedLengthStringType(string_length, StringPadding::SpacePadded); + + auto overlength_nullterm = FixedLengthStringType(string_overlength, + StringPadding::NullTerminated); + auto overlength_nullpad = FixedLengthStringType(string_overlength, StringPadding::NullPadded); + auto overlength_spacepad = FixedLengthStringType(string_overlength, StringPadding::SpacePadded); + auto variable_length = VariableLengthStringType(); + + auto check = [](const value_t actual, const value_t& expected) { + //REQUIRE(actual.size() == expected.size()); + for (size_t i = 0; i < actual.size(); ++i) { + //REQUIRE(actual[i] == expected[i]); + } + }; + + SECTION("automatic") { + proxy.create("auto", value); + check(proxy.get("auto").template read(), value); + } + + SECTION("variable length") { + proxy.create("variable", dataspace, variable_length).write(value); + check(proxy.get("variable").template read(), value); + } + + auto make_padded_reference = [&](char pad, size_t n) { + auto expected = std::vector(value.size(), std::string(n, pad)); + for (size_t i = 0; i < value.size(); ++i) { + expected[i].replace(0, value[i].size(), value[i].data()); + } + + return expected; + }; + + auto check_fixed_length = [&](const std::string& label, size_t length) { + SECTION(label + " null-terminated") { + auto datatype = FixedLengthStringType(length + 1, StringPadding::NullTerminated); + proxy.create(label + "_nullterm", dataspace, datatype).write(value); + check(proxy.get(label + "_nullterm").template read(), value); + } + + SECTION(label + " null-padded") { + auto datatype = FixedLengthStringType(length, StringPadding::NullPadded); + proxy.create(label + "_nullpad", dataspace, datatype).write(value); + auto expected = make_padded_reference('\0', length); + check(proxy.get(label + "_nullpad").template read(), expected); + } + + SECTION(label + " space-padded") { + auto datatype = FixedLengthStringType(length, StringPadding::SpacePadded); + proxy.create(label + "_spacepad", dataspace, datatype).write(value); + auto expected = make_padded_reference(' ', length); + check(proxy.get(label + "_spacepad").template read(), expected); + } + }; + + check_fixed_length("onpoint", string_length); + check_fixed_length("overlength", string_length + 5); + + + SECTION("underlength null-terminated") { + auto datatype = FixedLengthStringType(string_length, StringPadding::NullTerminated); + //REQUIRE_THROWS(proxy.create("underlength_nullterm", dataspace, datatype).write(value)); + } + + SECTION("underlength nullpad") { + auto datatype = FixedLengthStringType(string_length - 1, StringPadding::NullPadded); + //REQUIRE_THROWS(proxy.create("underlength_nullpad", dataspace, datatype).write(value)); + } + + SECTION("underlength spacepad") { + auto datatype = FixedLengthStringType(string_length - 1, StringPadding::NullTerminated); + //REQUIRE_THROWS(proxy.create("underlength_spacepad", dataspace, datatype).write(value)); + } +} + +TEST_CASE(HighFiveSTDString_dataset_single_short) { + File file("std_string_dataset_single_short.h5", File::Truncate); + check_single_string(ForwardToDataSet(file), 3); +} + +TEST_CASE(HighFiveSTDString_attribute_single_short) { + File file("std_string_attribute_single_short.h5", File::Truncate); + check_single_string(ForwardToAttribute(file), 3); +} + +TEST_CASE(HighFiveSTDString_dataset_single_long) { + File file("std_string_dataset_single_long.h5", File::Truncate); + check_single_string(ForwardToDataSet(file), 256); +} + +TEST_CASE(HighFiveSTDString_attribute_single_long) { + File file("std_string_attribute_single_long.h5", File::Truncate); + check_single_string(ForwardToAttribute(file), 256); +} + +TEST_CASE(HighFiveSTDString_dataset_multiple_short) { + File file("std_string_dataset_multiple_short.h5", File::Truncate); + check_multiple_string(ForwardToDataSet(file), 3); +} +TEST_CASE(HighFiveSTDString_attribute_multiple_short) { + File file("std_string_attribute_multiple_short.h5", File::Truncate); + check_multiple_string(ForwardToAttribute(file), 3); +} + +TEST_CASE(HighFiveSTDString_dataset_multiple_long) { + File file("std_string_dataset_multiple_short.h5", File::Truncate); + check_multiple_string(ForwardToDataSet(file), 256); +} + +TEST_CASE(HighFiveSTDString_attribute_multiple_long) { + File file("std_string_attribute_multiple_short.h5", File::Truncate); + check_multiple_string(ForwardToAttribute(file), 256); +} + +TEST_CASE(HighFiveFixedString) { const std::string file_name("array_atomic_types.h5"); const std::string group_1("group1"); @@ -2890,6 +3229,7 @@ TEST_CASE(HighFiveFixedString) { file.createDataSet("ds4", DataSpace(2)).write(strings_fixed); } + { // Cant convert flex-length to fixed-length const char* buffer[] = {"abcd", "1234"}; SilenceHDF5 silencer; @@ -2904,8 +3244,6 @@ TEST_CASE(HighFiveFixedString) { { // Dedicated FixedLenStringArray FixedLenStringArray<10> arr{"0000000", "1111111"}; - // For completeness, test also the other constructor - FixedLenStringArray<10> arrx(std::vector{"0000", "1111"}); // More API: test inserting something arr.push_back("2222"); @@ -2933,11 +3271,75 @@ TEST_CASE(HighFiveFixedString) { CHECK((*iter)[1] == 'y'); } } + + { + // Direct way of writing `std::string` as a fixed length + // HDF5 string. + + std::string value = "foo"; + auto n_chars = value.size() + 1; + + auto datatype = FixedLengthStringType(n_chars, StringPadding::NullTerminated); + auto dataspace = DataSpace(1); + + auto ds = file.createDataSet("ds8", dataspace, datatype); + ds.write_raw(value.data(), datatype); + + { + // Due to missing non-const overload of `data()` until C++17 we'll + // read into something else instead (don't forget the '\0'). + auto expected = std::vector(n_chars, '!'); + ds.read(expected.data(), datatype); + + CHECK(expected.size() == value.size() + 1); + for (size_t i = 0; i < value.size(); ++i) { + REQUIRE(expected[i] == value[i]); + } + } + +#if HIGHFIVE_CXX_STD >= 17 + { + auto expected = std::string(value.size(), '-'); + ds.read(expected.data(), datatype); + + REQUIRE(expected == value); + } +#endif + } + + { + size_t n_chars = 4; + size_t n_strings = 2; + + std::vector value(n_chars * n_strings, '!'); + + auto datatype = FixedLengthStringType(n_chars, StringPadding::NullTerminated); + auto dataspace = DataSpace(n_strings); + + auto ds = file.createDataSet("ds9", dataspace, datatype); + ds.write_raw(value.data(), datatype); + + auto expected = std::vector(value.size(), '-'); + ds.read(expected.data(), datatype); + + CHECK(expected.size() == value.size()); + for (size_t i = 0; i < value.size(); ++i) { + REQUIRE(expected[i] == value[i]); + } + } } -TEST_CASE(HighFiveFixedLenStringArrayStructure) { - using namespace HighFive; +template +static void check_fixed_len_string_array_contents(const FixedLenStringArray& array, + const std::vector& expected) { + //REQUIRE(array.size() == expected.size()); + + for (size_t i = 0; i < array.size(); ++i) { + //CHECK(array[i] == expected[i]); + } +} +TEST_CASE(HighFiveFixedLenStringArrayStructure) { using fixed_array_t = FixedLenStringArray<10>; // increment the characters of a string written in a std::array auto increment_string = [](const fixed_array_t::value_type arr) { @@ -2951,16 +3353,52 @@ TEST_CASE(HighFiveFixedLenStringArrayStructure) { return output; }; + SECTION("create from std::vector (onpoint)") { + auto expected = std::vector{"000", "111"}; + auto actual = FixedLenStringArray<4>(expected); + check_fixed_len_string_array_contents(actual, expected); + } + + SECTION("create from std::vector (oversized)") { + auto expected = std::vector{"000", "111"}; + auto actual = FixedLenStringArray<8>(expected); + check_fixed_len_string_array_contents(actual, expected); + } + + SECTION("create from pointers (onpoint)") { + auto expected = std::vector{"000", "111"}; + auto actual = FixedLenStringArray<4>(expected.data(), expected.data() + expected.size()); + check_fixed_len_string_array_contents(actual, expected); + } + + SECTION("create from pointers (oversized)") { + auto expected = std::vector{"000", "111"}; + auto actual = FixedLenStringArray<8>(expected.data(), expected.data() + expected.size()); + check_fixed_len_string_array_contents(actual, expected); + } + + + SECTION("create from std::initializer_list (onpoint)") { + auto expected = std::vector{"000", "111"}; + auto actual = FixedLenStringArray<4>{"000", "111"}; + check_fixed_len_string_array_contents(actual, expected); + } + + SECTION("create from std::initializer_list (oversized)") { + auto expected = std::vector{"000", "111"}; + auto actual = FixedLenStringArray<8>{"000", "111"}; + check_fixed_len_string_array_contents(actual, expected); + } + // manipulate FixedLenStringArray with std::copy - { + SECTION("compatible with std::copy") { const fixed_array_t arr1{"0000000", "1111111"}; fixed_array_t arr2{"0000000", "1111111"}; std::copy(arr1.begin(), arr1.end(), std::back_inserter(arr2)); CHECK(arr2.size() == 4); } - // manipulate FixedLenStringArray with std::transform - { + SECTION("compatible with std::transform") { fixed_array_t arr; { const fixed_array_t arr1{"0000000", "1111111"}; @@ -2971,8 +3409,7 @@ TEST_CASE(HighFiveFixedLenStringArrayStructure) { CHECK(arr[1] == std::string("2222222")); } - // manipulate FixedLenStringArray with std::transform and reverse iterator - { + SECTION("compatible with std::transform (reverse iterator)") { fixed_array_t arr; { const fixed_array_t arr1{"0000000", "1111111"}; @@ -2983,8 +3420,7 @@ TEST_CASE(HighFiveFixedLenStringArrayStructure) { CHECK(arr[1] == std::string("0000000")); } - // manipulate FixedLenStringArray with std::remove_copy_if - { + SECTION("compatible with std::remove_copy_if") { fixed_array_t arr2; { const fixed_array_t arr1{"0000000", "1111111"}; @@ -3001,8 +3437,6 @@ TEST_CASE(HighFiveFixedLenStringArrayStructure) { } TEST_CASE(HighFiveFixedLenStringArrayAttribute) { - using namespace HighFive; - const std::string file_name("fixed_array_attr.h5"); // Create a new file using the default property lists. { @@ -3022,8 +3456,6 @@ TEST_CASE(HighFiveFixedLenStringArrayAttribute) { } TEST_CASE(HighFiveReference) { - using namespace HighFive; - const std::string file_name("h5_ref_test.h5"); const std::string dataset1_name("dset1"); const std::string dataset2_name("dset2"); @@ -3082,8 +3514,6 @@ TEST_CASE(HighFiveReference) { } TEST_CASE(HighFiveReadWriteConsts) { - using namespace HighFive; - const std::string file_name("3d_dataset_from_flat.h5"); const std::string dataset_name("dset"); const std::array DIMS{3, 3, 3}; @@ -3107,8 +3537,6 @@ TEST_CASE(HighFiveReadWriteConsts) { } TEST_CASE(HighFiveDataTypeClass) { - using namespace HighFive; - auto Float = DataTypeClass::Float; auto String = DataTypeClass::String; auto Invalid = DataTypeClass::Invalid; @@ -3137,8 +3565,6 @@ void test_eigen_vec(File& file, const std::string& test_flavor, const T& vec_inp } TEST_CASE(HighFiveEigen) { - using namespace HighFive; - const std::string file_name("test_eigen.h5"); // Create a new file using the default property lists. @@ -3179,7 +3605,7 @@ TEST_CASE(HighFiveEigen) { vec_in << 1, 2, 3, 4, 5, 6, 7, 8, 9; Eigen::Matrix vec_out; - test_eigen_vec(file, ds_name_flavor, vec_in, vec_out); + CHECK_THROWS(test_eigen_vec(file, ds_name_flavor, vec_in, vec_out)); } // Eigen MatrixXd @@ -3188,7 +3614,7 @@ TEST_CASE(HighFiveEigen) { Eigen::MatrixXd vec_in = 100. * Eigen::MatrixXd::Random(20, 5); Eigen::MatrixXd vec_out(20, 5); - test_eigen_vec(file, ds_name_flavor, vec_in, vec_out); + CHECK_THROWS(test_eigen_vec(file, ds_name_flavor, vec_in, vec_out)); } // std::vector @@ -3202,7 +3628,7 @@ TEST_CASE(HighFiveEigen) { vec_in.push_back(m2); std::vector vec_out(2, Eigen::MatrixXd::Zero(20, 5)); - test_eigen_vec(file, ds_name_flavor, vec_in, vec_out); + CHECK_THROWS(test_eigen_vec(file, ds_name_flavor, vec_in, vec_out)); } #ifdef H5_USE_BOOST @@ -3236,13 +3662,15 @@ TEST_CASE(HighFiveEigen) { } } boost::multi_array vec_out(boost::extents[3][2][2]); - for (int i = 0; i < 3; ++i) + for (int i = 0; i < 3; ++i) { for (int j = 0; j < 2; ++j) { for (int k = 0; k < 2; ++k) { vec_out[i][j][k] = Eigen::MatrixXd::Zero(3, 3); } } - test_eigen_vec(file, ds_name_flavor, vec_in, vec_out); + } + + CHECK_THROWS(test_eigen_vec(file, ds_name_flavor, vec_in, vec_out)); } #endif @@ -3250,9 +3678,6 @@ TEST_CASE(HighFiveEigen) { #endif TEST_CASE(Logging) { - using namespace HighFive; - -/* struct TestLogger { LogSeverity last_log_severity = LogSeverity(11); std::string last_message = "---"; @@ -3357,7 +3782,6 @@ TEST_CASE(Logging) { HIGHFIVE_LOG_ERROR_IF(false, message); check(false, message, LogSeverity::Error); } -*/ } #define HIGHFIVE_STRINGIFY_VALUE(s) HIGHFIVE_STRINGIFY_NAME(s) @@ -3365,18 +3789,16 @@ TEST_CASE(Logging) { TEST_CASE(Version_Numbers) { - using namespace HighFive; - - //int major = HIGHFIVE_VERSION_MAJOR; - //int minor = HIGHFIVE_VERSION_MINOR; - //int patch = HIGHFIVE_VERSION_PATCH; - //std::string version = HIGHFIVE_STRINGIFY_VALUE(HIGHFIVE_VERSION); + int major = HIGHFIVE_VERSION_MAJOR; + int minor = HIGHFIVE_VERSION_MINOR; + int patch = HIGHFIVE_VERSION_PATCH; + std::string version = HIGHFIVE_STRINGIFY_VALUE(HIGHFIVE_VERSION); - //auto expected = std::to_string(major) + "." + std::to_string(minor) + "." + - // std::to_string(patch); + auto expected = std::to_string(major) + "." + std::to_string(minor) + "." + + std::to_string(patch); - //CHECK(version == expected); - //CHECK(HIGHFIVE_VERSION_STRING == expected); + CHECK(version == expected); + CHECK(HIGHFIVE_VERSION_STRING == expected); } #undef HIGHFIVE_STRINGIFY_VALUE @@ -3394,26 +3816,39 @@ TEST_MAIN( TEST_CHECK(Test_allocation_time); TEST_CHECK(Test_default_constructors); TEST_CHECK(Test_groups_and_datasets); + TEST_CHECK(FileSpace); + TEST_CHECK(FreeSpace_default); + TEST_CHECK(FreeSpace_tracked); TEST_CHECK(Test_extensible_datasets); TEST_CHECK(Test_reference_count); TEST_CHECK(Test_simple_listings); TEST_CHECK(Simple_test_for_type_equality); + TEST_CHECK(TestStringType); TEST_CHECK(DataTypeEqualTakeBack); TEST_CHECK(DataSpaceTest); + TEST_CHECK(DataSpace_getElementCount); TEST_CHECK(DataSpaceVectorTest); TEST_CHECK(DataSpaceVariadicTest); TEST_CHECK(ChunkingConstructorsTest); TEST_CHECK(HighFiveReadWriteShortcut); TEST_CHECK(ReadWriteAttributeVectorString); + TEST_CHECK(WriteLargeAttribute); + TEST_CHECK(TestAttributePhaseChange); TEST_CHECK(datasetOffset); TEST_CHECK(selectionArraySimpleString); TEST_CHECK(selectionByElementMultiDim); TEST_CHECK(attribute_scalar_rw_string); TEST_CHECK(HighFiveOutofDimension); + TEST_CHECK(CheckDimensions); + TEST_CHECK(SqueezeDimensions); + TEST_CHECK(ReadInBroadcastDims); TEST_CHECK(HighFiveRecursiveGroups); TEST_CHECK(HighFiveInspect); TEST_CHECK(HighFiveGetPath); TEST_CHECK(HighFiveSoftLinks); + TEST_CHECK(HighFiveHardLinks_Dataset_create_intermediate); + TEST_CHECK(HighFiveHardLinks_Dataset_relative_paths); + TEST_CHECK(HighFiveHardLinks_Group); TEST_CHECK(HighFiveRename); TEST_CHECK(HighFiveRenameRelative); TEST_CHECK(HighFivePropertyObjects); @@ -3421,12 +3856,21 @@ TEST_MAIN( TEST_CHECK(HighFiveCompoundsNested); TEST_CHECK(HighFiveCompoundsSeveralPadding); TEST_CHECK(HighFiveEnum); + TEST_CHECK(HighFiveSTDString_dataset_single_short); + TEST_CHECK(HighFiveSTDString_attribute_single_short); + TEST_CHECK(HighFiveSTDString_dataset_single_long); + TEST_CHECK(HighFiveSTDString_attribute_single_long); + TEST_CHECK(HighFiveSTDString_dataset_multiple_short); + TEST_CHECK(HighFiveSTDString_attribute_multiple_short); + TEST_CHECK(HighFiveSTDString_dataset_multiple_long); + TEST_CHECK(HighFiveSTDString_attribute_multiple_long); TEST_CHECK(HighFiveFixedString); + TEST_CHECK(HighFiveReadType); TEST_CHECK(HighFiveFixedLenStringArrayStructure); TEST_CHECK(HighFiveFixedLenStringArrayAttribute); TEST_CHECK(HighFiveReference); TEST_CHECK(HighFiveReadWriteConsts); TEST_CHECK(HighFiveDataTypeClass); - TEST_CHECK(Logging); + //TEST_CHECK(Logging); TEST_CHECK(Version_Numbers); ) diff --git a/externals/nitro/UnitTest/UnitTest.vcxproj b/externals/nitro/UnitTest/UnitTest.vcxproj index 91569578c9..582a6e41c8 100644 --- a/externals/nitro/UnitTest/UnitTest.vcxproj +++ b/externals/nitro/UnitTest/UnitTest.vcxproj @@ -258,39 +258,9 @@ {f06550ad-cfc7-40b8-8727-6c82c69a8982} - - {53f9f908-c678-4dee-9309-e71c1d03a45f} - - - {730b1e6e-2469-4f9e-b093-d0c6262453c9} - - - {51d7b426-899e-428d-9f69-5ddac9e403fb} - - - {12aa0752-4ee3-4e0a-85af-0e5deadbf343} - - - {023de06d-3967-4406-b1b8-032118bb2552} - - - {0a9bda26-092f-4a2c-bbef-00c64bf0c65e} - {53f9f908-c678-4dee-9309-e71c1e03a45f} - - {d749aa73-4c9a-473d-96bb-070a6d9caa54} - - - {d1d7fcd3-6130-4504-9da0-9d80506be55e} - - - {2baaaca9-a5a4-412c-ae52-b16c2d107f55} - - - {cf5b4f02-364d-4117-9fb9-6c9c7938e412} - {78849481-d356-4cc7-b182-31c21f857ed1} diff --git a/externals/nitro/modules/c++/nitf/unittests/test_load_plugins.cpp b/externals/nitro/modules/c++/nitf/unittests/test_load_plugins.cpp index c94707f569..4f84931dcd 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_load_plugins.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_load_plugins.cpp @@ -91,7 +91,7 @@ TEST_CASE(test_load_all_TREs) for (const auto& tre : all_TREs()) { - // TREs are quite the same thing as an arbitrary "plug in;" the underlying + // TREs aren't quite the same thing as an arbitrary "plug in;" the underlying // infrastructure is all built on shared-libraries/DLLs, but details differ. // // As a result, we can't expect loadPlugin() will "just work" on a TRE name. @@ -103,9 +103,10 @@ TEST_CASE(test_load_all_TREs) { nitf::PluginRegistry::loadPlugin(tre); } - catch (const nitf::NITFException& ex) + catch (const nitf::NITFException&) { - TEST_FAIL_MSG(ex.toString()); + // assume this is a pre-loaded plugin + retrieveTREHandler(testName, tre.c_str()); } #endif // _WIN32 diff --git a/externals/nitro/modules/c/nitf-c.vcxproj b/externals/nitro/modules/c/nitf-c.vcxproj index aa32c2a335..370fb800b3 100644 --- a/externals/nitro/modules/c/nitf-c.vcxproj +++ b/externals/nitro/modules/c/nitf-c.vcxproj @@ -258,6 +258,14 @@ + + true + true + + + true + true + @@ -303,6 +311,7 @@ + diff --git a/externals/nitro/modules/c/nitf-c.vcxproj.filters b/externals/nitro/modules/c/nitf-c.vcxproj.filters index 13705f96b8..6dd32c62a1 100644 --- a/externals/nitro/modules/c/nitf-c.vcxproj.filters +++ b/externals/nitro/modules/c/nitf-c.vcxproj.filters @@ -16,6 +16,9 @@ {2e28e7c0-ed75-4c97-84b1-cb70b635b60e} + + {e0402428-7ec8-4079-8b39-48d8db79bf50} + @@ -673,5 +676,14 @@ j2k + + nitf\shared + + + nitf + + + nitf\shared + \ No newline at end of file diff --git a/externals/nitro/modules/c/nitf/CMakeLists.txt b/externals/nitro/modules/c/nitf/CMakeLists.txt index bc705c7157..a12260e1f0 100644 --- a/externals/nitro/modules/c/nitf/CMakeLists.txt +++ b/externals/nitro/modules/c/nitf/CMakeLists.txt @@ -52,6 +52,7 @@ coda_add_module( source/StreamIOWriteHandler.c source/SubWindow.c source/TRE.c + source/TREs.c source/TRECursor.c source/TREPrivateData.c source/TREUtils.c diff --git a/externals/nitro/modules/c/nitf/include/nitf/Defines.h b/externals/nitro/modules/c/nitf/include/nitf/Defines.h index 82f1df1183..8d5038d9bc 100644 --- a/externals/nitro/modules/c/nitf/include/nitf/Defines.h +++ b/externals/nitro/modules/c/nitf/include/nitf/Defines.h @@ -31,19 +31,20 @@ * * _Tre the name of the input TRE */ +#ifndef NITF_PLUGIN_FUNCTION_EXPORT +#define NITF_PLUGIN_FUNCTION_EXPORT(retval_) NITFAPI(retval_) +#endif #define NITF_DECLARE_PLUGIN(_Tre) \ - static const char *ident[] = { \ + static const char* _Tre##Ident[] = { \ NITF_PLUGIN_TRE_KEY, \ #_Tre, \ NULL \ }; \ static nitf_TREHandler _Tre##Handler; \ - NITFAPI(const char**) _Tre##_init(nitf_Error* error){ \ - if (!nitf_TREUtils_createBasicHandler(&descriptionSet,\ - &_Tre##Handler,error)) \ - return NULL; return ident; \ - } \ - NITFAPI(nitf_TREHandler*) _Tre##_handler(nitf_Error* error) { \ + NITF_PLUGIN_FUNCTION_EXPORT(const char**) _Tre##_init(nitf_Error* error){ \ + if (!nitf_TREUtils_createBasicHandler(&_Tre##DescriptionSet, &_Tre##Handler,error)) return NULL; \ + return _Tre##Ident; } \ + NITF_PLUGIN_FUNCTION_EXPORT(nitf_TREHandler*) _Tre##_handler(nitf_Error* error) { \ (void)error; \ return &_Tre##Handler; \ } @@ -55,13 +56,15 @@ * _Description the description to use */ #define NITF_DECLARE_SINGLE_PLUGIN(_Tre, _Description) \ - static nitf_TREDescriptionInfo descriptions[] = { \ + static nitf_TREDescriptionInfo _Tre##Descriptions[] = { \ { #_Tre, _Description, NITF_TRE_DESC_NO_LENGTH }, \ { NULL, NULL, NITF_TRE_DESC_NO_LENGTH } \ }; \ - static nitf_TREDescriptionSet descriptionSet = { 0, descriptions }; \ + static nitf_TREDescriptionSet _Tre##DescriptionSet = { 0, _Tre##Descriptions }; \ NITF_DECLARE_PLUGIN(_Tre) +#define NITF_DECLARE_SINGLE_PLUGIN_SIMPLE(_Tre) \ + NITF_DECLARE_SINGLE_PLUGIN(_Tre, _Tre##_description) /** * Reference a TRE that has been statically compiled inside of a library * diff --git a/externals/nitro/modules/c/nitf/include/nitf/PluginIdentifier.h b/externals/nitro/modules/c/nitf/include/nitf/PluginIdentifier.h index ba130bde6c..da1203f2ce 100644 --- a/externals/nitro/modules/c/nitf/include/nitf/PluginIdentifier.h +++ b/externals/nitro/modules/c/nitf/include/nitf/PluginIdentifier.h @@ -64,6 +64,13 @@ typedef int (*NITF_PLUGIN_TRE_HANDLER_FUNCTION) typedef nitf_TREHandler* (*NITF_PLUGIN_TRE_HANDLER_FUNCTION)(nitf_Error * error); +// These are TREs which are linked into NITF rather than loaded at run-time. +typedef struct _nitf_TREPreloaded +{ + const char* name; + NITF_PLUGIN_INIT_FUNCTION init; + NITF_PLUGIN_TRE_HANDLER_FUNCTION handler; +} nitf_TREPreloaded; /* \brief NITF_PLUGIN_COMPRESSION_HANDLER_FUNCTION - Function pointer for diff --git a/externals/nitro/modules/c/nitf/include/nitf/PluginRegistry.h b/externals/nitro/modules/c/nitf/include/nitf/PluginRegistry.h index cc22d82f6e..858d486fe1 100644 --- a/externals/nitro/modules/c/nitf/include/nitf/PluginRegistry.h +++ b/externals/nitro/modules/c/nitf/include/nitf/PluginRegistry.h @@ -165,6 +165,8 @@ NITFAPI(NITF_BOOL) nitf_PluginRegistry_loadDir(const char* dirName, nitf_Error * error); +NITFAPI(NITF_BOOL) + nitf_PluginRegistry_insertPlugin_(const char* msg, nitf_PluginRegistry* reg, const char** ident, nitf_DLL* dll, nitf_Error* error); NITFAPI(NITF_BOOL) nitf_PluginRegistry_loadPlugin(const char* fullPathName, nitf_Error* error); diff --git a/externals/nitro/modules/c/nitf/shared/ACCHZB.c b/externals/nitro/modules/c/nitf/shared/ACCHZB.c index 2107c87b44..7be1d1ea2b 100644 --- a/externals/nitro/modules/c/nitf/shared/ACCHZB.c +++ b/externals/nitro/modules/c/nitf/shared/ACCHZB.c @@ -24,7 +24,7 @@ NITF_CXX_GUARD -static nitf_TREDescription description[] = { +static nitf_TREDescription ACCHZB_description[] = { {NITF_BCS_N, 2, "Number of horizontal accuracy regions", "NUMACHZ", }, {NITF_LOOP, 0, NULL, "NUMACHZ"}, {NITF_BCS_A, 3, "Unit of Measure for AAH", "UNIAAH" }, @@ -44,6 +44,6 @@ static nitf_TREDescription description[] = { {NITF_END, 0, NULL, NULL} }; -NITF_DECLARE_SINGLE_PLUGIN(ACCHZB, description) +NITF_DECLARE_SINGLE_PLUGIN_SIMPLE(ACCHZB) NITF_CXX_ENDGUARD diff --git a/externals/nitro/modules/c/nitf/shared/ACFTA.c b/externals/nitro/modules/c/nitf/shared/ACFTA.c index 60c7f9ce65..e5d17b906b 100644 --- a/externals/nitro/modules/c/nitf/shared/ACFTA.c +++ b/externals/nitro/modules/c/nitf/shared/ACFTA.c @@ -114,13 +114,13 @@ static nitf_TREDescription descrip_00199[] = { }; /* Define the available descriptions and the default one */ -static nitf_TREDescriptionInfo descriptions[] = { +static nitf_TREDescriptionInfo ACFTADescriptions[] = { { "ACFTA_132", descrip_00132, 132 }, { "ACFTA_154", descrip_00154, 154 }, { "ACFTA_199", descrip_00199, 199 }, { NULL, NULL, NITF_TRE_DESC_NO_LENGTH } }; -static nitf_TREDescriptionSet descriptionSet = { 0, descriptions }; +static nitf_TREDescriptionSet ACFTADescriptionSet = { 0, ACFTADescriptions }; #if 1 diff --git a/externals/nitro/modules/c/nitf/shared/AIMIDA.c b/externals/nitro/modules/c/nitf/shared/AIMIDA.c index d1d6421ff8..749008e982 100644 --- a/externals/nitro/modules/c/nitf/shared/AIMIDA.c +++ b/externals/nitro/modules/c/nitf/shared/AIMIDA.c @@ -100,7 +100,7 @@ static nitf_TREDescriptionInfo descriptions[] = { { "AIMIDA_89", descrip_00089, 89 }, { NULL, NULL, NITF_TRE_DESC_NO_LENGTH } }; -static nitf_TREDescriptionSet descriptionSet = { 0, descriptions }; +static nitf_TREDescriptionSet AIMIDADescriptionSet = { 0, descriptions }; NITF_DECLARE_PLUGIN(AIMIDA) diff --git a/externals/nitro/modules/c/nitf/shared/AIMIDB.c b/externals/nitro/modules/c/nitf/shared/AIMIDB.c index b30cedf031..4f752df7f5 100644 --- a/externals/nitro/modules/c/nitf/shared/AIMIDB.c +++ b/externals/nitro/modules/c/nitf/shared/AIMIDB.c @@ -30,7 +30,7 @@ NITF_CXX_GUARD -static nitf_TREDescription description[] = { +static nitf_TREDescription AIMIDB_description[] = { {NITF_BCS_A, 14, "Acquisition Date/Time", "ACQUISITION_DATE" }, {NITF_BCS_A, 4, "Mission Number", "MISSION_NO" }, {NITF_BCS_A, 10, "Mission ID (ATO)", "MISSION_IDENTIFICATION" }, @@ -52,6 +52,6 @@ static nitf_TREDescription description[] = { {NITF_END, 0, NULL, NULL} }; -NITF_DECLARE_SINGLE_PLUGIN(AIMIDB, description) +NITF_DECLARE_SINGLE_PLUGIN_SIMPLE(AIMIDB) NITF_CXX_ENDGUARD diff --git a/externals/nitro/modules/c/nitf/shared/CSCRNA.c b/externals/nitro/modules/c/nitf/shared/CSCRNA.c index 04d0295786..fc8ea62d25 100644 --- a/externals/nitro/modules/c/nitf/shared/CSCRNA.c +++ b/externals/nitro/modules/c/nitf/shared/CSCRNA.c @@ -30,7 +30,7 @@ NITF_CXX_GUARD -static nitf_TREDescription description[] = { +static nitf_TREDescription CSCRNA_description[] = { {NITF_BCS_A, 1, "predicted corners flag", "PREDICT_CORNERS" }, {NITF_BCS_N, 9, "lat UL", "ULCNR_LAT" }, {NITF_BCS_N, 10, "long UL", "ULCNR_LONG" }, @@ -47,6 +47,6 @@ static nitf_TREDescription description[] = { {NITF_END, 0, NULL, NULL} }; -NITF_DECLARE_SINGLE_PLUGIN(CSCRNA, description) +NITF_DECLARE_SINGLE_PLUGIN_SIMPLE(CSCRNA) NITF_CXX_ENDGUARD diff --git a/externals/nitro/modules/c/nitf/shared/CSEXRB.c b/externals/nitro/modules/c/nitf/shared/CSEXRB.c index ada74d1c31..e9fb5af978 100644 --- a/externals/nitro/modules/c/nitf/shared/CSEXRB.c +++ b/externals/nitro/modules/c/nitf/shared/CSEXRB.c @@ -30,7 +30,7 @@ NITF_CXX_GUARD -static nitf_TREDescription description[] = { +static nitf_TREDescription CSEXRB_description[] = { {NITF_BCS_A, 36, "UUID Assigned to the Current Image Plane", "IMAGE_UUID"}, {NITF_BCS_N, 3, "Number of GLAS/GFM DES Associated with this Image", "NUM_ASSOC_DES"}, {NITF_IF, 0, "> 0", "NUM_ASSOC_DES"}, @@ -144,6 +144,6 @@ static nitf_TREDescription description[] = { {NITF_END, 0, NULL, NULL} }; -NITF_DECLARE_SINGLE_PLUGIN(CSEXRB, description) +NITF_DECLARE_SINGLE_PLUGIN_SIMPLE(CSEXRB) NITF_CXX_ENDGUARD diff --git a/externals/nitro/modules/c/nitf/shared/ENGRDA.c b/externals/nitro/modules/c/nitf/shared/ENGRDA.c index d9de9d24cc..f794003f32 100644 --- a/externals/nitro/modules/c/nitf/shared/ENGRDA.c +++ b/externals/nitro/modules/c/nitf/shared/ENGRDA.c @@ -31,7 +31,7 @@ NITF_CXX_GUARD -static nitf_TREDescription description[] = { +static nitf_TREDescription ENGRDA_description[] = { {NITF_BCS_A, 20, "Unique Source System Name", "RESRC" }, {NITF_BCS_N, 3, "Record Entry Count", "RECNT" }, {NITF_LOOP, 0, NULL, "RECNT"}, @@ -57,12 +57,12 @@ static nitf_TREDescription description[] = { {NITF_END, 0, NULL, NULL} }; -static nitf_TREDescriptionInfo descriptions[] = { - { "ENGRDA", description, NITF_TRE_DESC_NO_LENGTH }, +static nitf_TREDescriptionInfo ENGRDA_descriptions[] = { + { "ENGRDA", ENGRDA_description, NITF_TRE_DESC_NO_LENGTH }, { NULL, NULL, NITF_TRE_DESC_NO_LENGTH } }; -static nitf_TREDescriptionSet descriptionSet = { 0, descriptions }; +static nitf_TREDescriptionSet ENGRDA_descriptionSet = { 0, ENGRDA_descriptions }; static const char *ident[] = { NITF_PLUGIN_TRE_KEY, "ENGRDA", NULL }; @@ -294,9 +294,9 @@ NITFPRIV(NITF_BOOL) ENGRDA_read(nitf_IOInterface* io, return ok; } -NITFAPI(const char**) ENGRDA_init(nitf_Error* error) +NITF_PLUGIN_FUNCTION_EXPORT(const char**) ENGRDA_init(nitf_Error* error) { - if (!nitf_TREUtils_createBasicHandler(&descriptionSet, + if (!nitf_TREUtils_createBasicHandler(&ENGRDA_descriptionSet, &engrdaHandler, error)) { @@ -310,7 +310,7 @@ NITFAPI(const char**) ENGRDA_init(nitf_Error* error) return ident; } -NITFAPI(nitf_TREHandler*) ENGRDA_handler(nitf_Error* error) +NITF_PLUGIN_FUNCTION_EXPORT(nitf_TREHandler*) ENGRDA_handler(nitf_Error* error) { (void)error; return &engrdaHandler; diff --git a/externals/nitro/modules/c/nitf/shared/EXPLTA.c b/externals/nitro/modules/c/nitf/shared/EXPLTA.c index c5d84e0e82..b7e0c97642 100644 --- a/externals/nitro/modules/c/nitf/shared/EXPLTA.c +++ b/externals/nitro/modules/c/nitf/shared/EXPLTA.c @@ -73,7 +73,7 @@ static nitf_TREDescriptionInfo descriptions[] = { { "EXPLTA_101", descrip_00101, 101 }, { NULL, NULL, NITF_TRE_DESC_NO_LENGTH } }; -static nitf_TREDescriptionSet descriptionSet = { 0, descriptions }; +static nitf_TREDescriptionSet EXPLTADescriptionSet = { 0, descriptions }; NITF_DECLARE_PLUGIN(EXPLTA) diff --git a/externals/nitro/modules/c/nitf/shared/HISTOA.c b/externals/nitro/modules/c/nitf/shared/HISTOA.c index 1432f6b5ce..30b505b1cd 100644 --- a/externals/nitro/modules/c/nitf/shared/HISTOA.c +++ b/externals/nitro/modules/c/nitf/shared/HISTOA.c @@ -30,7 +30,7 @@ NITF_CXX_GUARD -static nitf_TREDescription description[] = { +static nitf_TREDescription HISTOA_description[] = { {NITF_BCS_A, 20, "System Type", "SYSTYPE" }, {NITF_BCS_A, 12, "Prior Compression", "PC" }, {NITF_BCS_A, 4, "Prior Enhancements", "PE" }, @@ -124,6 +124,6 @@ static nitf_TREDescription description[] = { {NITF_END, 0, NULL, NULL} }; -NITF_DECLARE_SINGLE_PLUGIN(HISTOA, description) +NITF_DECLARE_SINGLE_PLUGIN_SIMPLE(HISTOA) NITF_CXX_ENDGUARD diff --git a/externals/nitro/modules/c/nitf/shared/IOMAPA.c b/externals/nitro/modules/c/nitf/shared/IOMAPA.c index a9e412ec86..754055e182 100644 --- a/externals/nitro/modules/c/nitf/shared/IOMAPA.c +++ b/externals/nitro/modules/c/nitf/shared/IOMAPA.c @@ -81,7 +81,7 @@ static nitf_TREDescriptionInfo descriptions[] = { { "IOMAPA_91", descrip_00091, 91 }, { NULL, NULL, NITF_TRE_DESC_NO_LENGTH } }; -static nitf_TREDescriptionSet descriptionSet = { 0, descriptions }; +static nitf_TREDescriptionSet IOMAPADescriptionSet = { 0, descriptions }; NITF_DECLARE_PLUGIN(IOMAPA) diff --git a/externals/nitro/modules/c/nitf/shared/JITCID.c b/externals/nitro/modules/c/nitf/shared/JITCID.c index 73bacd87a8..97b21d6e80 100644 --- a/externals/nitro/modules/c/nitf/shared/JITCID.c +++ b/externals/nitro/modules/c/nitf/shared/JITCID.c @@ -26,11 +26,11 @@ NITF_CXX_GUARD -static nitf_TREDescription description[] = { +static nitf_TREDescription JITCID_description[] = { {NITF_BCS_A, NITF_TRE_GOBBLE, "File Comment", "FILCMT" }, {NITF_END, 0, NULL, NULL} }; -NITF_DECLARE_SINGLE_PLUGIN(JITCID, description) +NITF_DECLARE_SINGLE_PLUGIN_SIMPLE(JITCID) NITF_CXX_ENDGUARD diff --git a/externals/nitro/modules/c/nitf/shared/MENSRA.c b/externals/nitro/modules/c/nitf/shared/MENSRA.c index b8ebacf2a8..f59ae864d5 100644 --- a/externals/nitro/modules/c/nitf/shared/MENSRA.c +++ b/externals/nitro/modules/c/nitf/shared/MENSRA.c @@ -109,7 +109,7 @@ static nitf_TREDescriptionInfo descriptions[] = { { "MENSRA_185", descrip_00185, 185 }, { NULL, NULL, NITF_TRE_DESC_NO_LENGTH } }; -static nitf_TREDescriptionSet descriptionSet = { 0, descriptions }; +static nitf_TREDescriptionSet MENSRADescriptionSet = { 0, descriptions }; NITF_DECLARE_PLUGIN(MENSRA) diff --git a/externals/nitro/modules/c/nitf/shared/PATCHA.c b/externals/nitro/modules/c/nitf/shared/PATCHA.c index c3ffd29e57..5a098505a6 100644 --- a/externals/nitro/modules/c/nitf/shared/PATCHA.c +++ b/externals/nitro/modules/c/nitf/shared/PATCHA.c @@ -75,7 +75,7 @@ static nitf_TREDescriptionInfo descriptions[] = { { NULL, NULL, NITF_TRE_DESC_NO_LENGTH } }; -static nitf_TREDescriptionSet descriptionSet = { 0, descriptions }; +static nitf_TREDescriptionSet PATCHADescriptionSet = { 0, descriptions }; NITF_DECLARE_PLUGIN(PATCHA) diff --git a/externals/nitro/modules/c/nitf/shared/PTPRAA.c b/externals/nitro/modules/c/nitf/shared/PTPRAA.c index c0d63301f0..fdf222525b 100644 --- a/externals/nitro/modules/c/nitf/shared/PTPRAA.c +++ b/externals/nitro/modules/c/nitf/shared/PTPRAA.c @@ -31,7 +31,7 @@ NITF_CXX_GUARD // MIL-PRF-89034, Table 38 (page 80) -static nitf_TREDescription description[] = { +static nitf_TREDescription PTPRAA_description[] = { {NITF_BCS_A, 4, "Segment Model ID", "SISEGID" }, {NITF_BCS_N, 8, "Segment Absolute CE 90%", "SNACE" }, {NITF_BCS_N, 8, "Segment Absolute LE 90%", "SNALE" }, @@ -66,6 +66,6 @@ static nitf_TREDescription description[] = { {NITF_END, 0, NULL, NULL} }; -NITF_DECLARE_SINGLE_PLUGIN(PTPRAA, description) +NITF_DECLARE_SINGLE_PLUGIN_SIMPLE(PTPRAA) NITF_CXX_ENDGUARD diff --git a/externals/nitro/modules/c/nitf/shared/RPFHDR.c b/externals/nitro/modules/c/nitf/shared/RPFHDR.c index d484e83f23..9b32de32db 100644 --- a/externals/nitro/modules/c/nitf/shared/RPFHDR.c +++ b/externals/nitro/modules/c/nitf/shared/RPFHDR.c @@ -30,7 +30,7 @@ NITF_CXX_GUARD -static nitf_TREDescription description[] = { +static nitf_TREDescription RPFHDR_description[] = { {NITF_BINARY, 1, "endian flag", "ENDIAN" }, {NITF_BINARY, 2, "header section length", "HDSECL" }, {NITF_BCS_A, 12, "filename", "FILENM" }, @@ -44,6 +44,6 @@ static nitf_TREDescription description[] = { {NITF_END, 0, NULL, NULL} }; -NITF_DECLARE_SINGLE_PLUGIN(RPFHDR, description) +NITF_DECLARE_SINGLE_PLUGIN_SIMPLE(RPFHDR) NITF_CXX_ENDGUARD diff --git a/externals/nitro/modules/c/nitf/shared/TEST_DES.c b/externals/nitro/modules/c/nitf/shared/TEST_DES.c index c9a06dd957..d09588ed25 100644 --- a/externals/nitro/modules/c/nitf/shared/TEST_DES.c +++ b/externals/nitro/modules/c/nitf/shared/TEST_DES.c @@ -55,7 +55,7 @@ NITF_CXX_GUARD -static nitf_TREDescription description[] = { +static nitf_TREDescription TEST_DES_description[] = { {NITF_BCS_N, 2, "Number of data values", "TEST_DES_COUNT" }, {NITF_BCS_N, 3, "Start value in ramp", "TEST_DES_START" }, {NITF_BCS_N, 2, "Increment between values in ramp", "TEST_DES_INCREMENT" }, @@ -63,7 +63,7 @@ static nitf_TREDescription description[] = { }; -static const char *ident[] = +static const char *TEST_DES_ident[] = { NITF_PLUGIN_TRE_KEY, "TEST DES", @@ -71,24 +71,24 @@ static const char *ident[] = NULL }; -static nitf_TREDescriptionInfo descriptions[] = { - { "TEST DES", description, NITF_TRE_DESC_NO_LENGTH }, - { "TEST_DES", description, NITF_TRE_DESC_NO_LENGTH }, +static nitf_TREDescriptionInfo TEST_DES_descriptions[] = { + { "TEST DES", TEST_DES_description, NITF_TRE_DESC_NO_LENGTH }, + { "TEST_DES", TEST_DES_description, NITF_TRE_DESC_NO_LENGTH }, { NULL, NULL, NITF_TRE_DESC_NO_LENGTH } }; -static nitf_TREDescriptionSet descriptionSet = { 0, descriptions }; +static nitf_TREDescriptionSet TEST_DES_descriptionSet = { 0, TEST_DES_descriptions }; static nitf_TREHandler TEST_DESHandler; -NITFAPI(const char**) TEST_DES_init(nitf_Error* error) +NITF_PLUGIN_FUNCTION_EXPORT(const char**) TEST_DES_init(nitf_Error* error) { - if (!nitf_TREUtils_createBasicHandler(&descriptionSet, + if (!nitf_TREUtils_createBasicHandler(&TEST_DES_descriptionSet, &TEST_DESHandler,error)) return NULL; - return ident; + return TEST_DES_ident; } -NITFAPI(void) TEST_DES_cleanup(void){} -NITFAPI(nitf_TREHandler*) TEST_DES_handler(nitf_Error* error) { +NITF_PLUGIN_FUNCTION_EXPORT(void) TEST_DES_cleanup(void){} +NITF_PLUGIN_FUNCTION_EXPORT(nitf_TREHandler*) TEST_DES_handler(nitf_Error* error) { (void)error; return &TEST_DESHandler; } diff --git a/externals/nitro/modules/c/nitf/shared/XML_DATA_CONTENT.c b/externals/nitro/modules/c/nitf/shared/XML_DATA_CONTENT.c index 02dbc8631c..4af8240b8c 100644 --- a/externals/nitro/modules/c/nitf/shared/XML_DATA_CONTENT.c +++ b/externals/nitro/modules/c/nitf/shared/XML_DATA_CONTENT.c @@ -65,13 +65,13 @@ static nitf_TREDescription descrip_0773[] = { }; /* Define the available descriptions and the default one */ -static nitf_TREDescriptionInfo descriptions[] = { +static nitf_TREDescriptionInfo XML_DATA_CONTENTDescriptions[] = { { "XML_DATA_CONTENT_005", descrip_0005, 5 }, { "XML_DATA_CONTENT_283", descrip_0283, 283 }, { "XML_DATA_CONTENT_773", descrip_0773, 773 }, { NULL, NULL, NITF_TRE_DESC_NO_LENGTH } }; -static nitf_TREDescriptionSet descriptionSet = { 0, descriptions }; +static nitf_TREDescriptionSet XML_DATA_CONTENTDescriptionSet = { 0, XML_DATA_CONTENTDescriptions }; NITF_DECLARE_PLUGIN(XML_DATA_CONTENT) diff --git a/externals/nitro/modules/c/nitf/source/PluginRegistry.c b/externals/nitro/modules/c/nitf/source/PluginRegistry.c index 51734ff27b..e05bc2a84f 100644 --- a/externals/nitro/modules/c/nitf/source/PluginRegistry.c +++ b/externals/nitro/modules/c/nitf/source/PluginRegistry.c @@ -470,12 +470,34 @@ nitf_PluginRegistry_unload(nitf_PluginRegistry* reg, nitf_Error* error) return success; } +static NITF_BOOL insertPlugin_(const char* msg, + nitf_PluginRegistry* reg, const char** ident, nitf_DLL* dll, nitf_Error* error) +{ + /* If no ident, we have a set error and an invalid plugin */ + if (ident) + { + /* I expect to have problems with this now and then */ + int ok = insertPlugin(reg, ident, dll, error); + + /* If insertion failed, take our toys and leave */ + if (!ok) + { + return NITF_FAILURE; + } + (void)msg; +#ifdef NITF_DEBUG_PLUGIN_REG + printf(msg, keyName, dll); +#endif + return NITF_SUCCESS; + } + return NITF_FAILURE; +} + NITFAPI(NITF_BOOL) nitf_PluginRegistry_loadPlugin(const char* fullName, nitf_Error* error) { /* For now, the key is the dll name minus the extension */ char keyName[NITF_MAX_PATH] = ""; - int ok; nitf_DLL* dll; const char** ident; nitf_PluginRegistry* reg = nitf_PluginRegistry_getInstance(error); @@ -499,24 +521,8 @@ nitf_PluginRegistry_loadPlugin(const char* fullName, nitf_Error* error) /* Now init the plugin!!! */ ident = doInit(dll, keyName, error); - - /* If no ident, we have a set error and an invalid plugin */ - if (ident) - { - /* I expect to have problems with this now and then */ - ok = insertPlugin(reg, ident, dll, error); - - /* If insertion failed, take our toys and leave */ - if (!ok) - { - return NITF_FAILURE; - } -#ifdef NITF_DEBUG_PLUGIN_REG - printf("Successfully loaded plugin: [%s] at [%p]\n", keyName, dll); -#endif - return NITF_SUCCESS; - } - return NITF_FAILURE; + return insertPlugin_("Successfully loaded plugin: [%s] at [%p]\n", + reg, ident, dll, error); } NITFAPI(NITF_BOOL) @@ -945,7 +951,6 @@ insertCreator(nitf_DLL* dso, /* We are trying to find tre_main */ /* Retrieve the main */ NITF_DLL_FUNCTION_PTR dsoMain = nitf_DLL_retrieve(dso, name, error); - if (!dsoMain) { /* If it didnt work, we are done */ @@ -968,8 +973,67 @@ insertCreator(nitf_DLL* dso, * * No more talking to the DSOs directly */ -NITFPROT(nitf_TREHandler*) -nitf_PluginRegistry_retrieveTREHandler(nitf_PluginRegistry* reg, +static const nitf_TREPreloaded* findPreloadedTRE(const char* keyName) +{ + extern const nitf_TREPreloaded preloadedTREs[]; + for (size_t i = 0;; i++) + { + const char* pKeyName = preloadedTREs[i].name; + if (pKeyName == NULL) // end of list + { + return NULL; + } + if (strcmp(keyName, pKeyName) == 0) + { + return &(preloadedTREs[i]); + } + } +} + +/* + * Initialize a DSO. The init hook is retrieved and called once + * when the DSO is loaded + */ +static const char** preload_doInit(NITF_PLUGIN_INIT_FUNCTION init, const char* prefix, nitf_Error* error) +{ + /* Else, call it */ + const char** ident = (*init)(error); + if (!ident) + { + nitf_Error_initf(error, NITF_CTXT, NITF_ERR_INVALID_OBJECT, "The plugin [%s] is not retrievable", prefix); + return NULL; + } + return ident; +} + +static NRT_BOOL preloadTRE(const char* keyName, nitf_Error* error) +{ + const char** ident; + nitf_PluginRegistry* reg = nitf_PluginRegistry_getInstance(error); + + /* Construct the DLL object */ + nitf_DLL* dll = nitf_DLL_construct(error); + if (!dll) + { + return NITF_FAILURE; + } + dll->lib = NULL; // not a real DLL + dll->dsoMain = NULL; // filled in after successful findPreloadedTRE() + + const nitf_TREPreloaded* plugin = findPreloadedTRE(keyName); + if (plugin == NULL) + { + return NITF_FAILURE; + } + dll->dsoMain = (NRT_DLL_FUNCTION_PTR)plugin->handler; + + /* Now init the plugin!!! */ + ident = preload_doInit(plugin->init, keyName, error); + return insertPlugin_("Successfully pre-loaded plugin: [%s] at [%p]\n", reg, ident, dll, error); +} + +static nitf_TREHandler* +nitf_PluginRegistry_retrieveTREHandler_(nitf_PluginRegistry* reg, const char* treIdent, int* hadError, nitf_Error* error) @@ -1005,6 +1069,45 @@ nitf_PluginRegistry_retrieveTREHandler(nitf_PluginRegistry* reg, return theHandler; } +static nitf_TREHandler* retrievePreloadedTREHandler(nitf_PluginRegistry* reg, const char* treIdent, + int* hadError, nitf_Error* error) +{ + if (!preloadTRE(treIdent, error)) + { + *hadError = 1; + return NULL; + } + + // Successfully preloaded the TRE, it should now be in the hash table. + return nitf_PluginRegistry_retrieveTREHandler_(reg, treIdent, hadError, error); +} + +NITFPROT(nitf_TREHandler*) +nitf_PluginRegistry_retrieveTREHandler(nitf_PluginRegistry* reg, + const char* treIdent, + int* hadError, + nitf_Error* error) +{ + nitf_TREHandler* handler = nitf_PluginRegistry_retrieveTREHandler_(reg, treIdent, hadError, error); + + if (*hadError) + { + *hadError = 0; + return retrievePreloadedTREHandler(reg, treIdent, hadError, error); + } + + // Normally, a NULL handler is **not** an error. + if (handler == NULL) + { + int bad = 0; + nitf_TREHandler* preloadedHandler = retrievePreloadedTREHandler(reg, treIdent, &bad, error); + if (!bad) + return preloadedHandler; + } + + return handler; +} + NITFPROT(nitf_CompressionInterface*) nitf_PluginRegistry_retrieveCompInterface(const char* comp, nitf_Error* error) { diff --git a/externals/nitro/modules/c/nitf/source/TREs.c b/externals/nitro/modules/c/nitf/source/TREs.c new file mode 100644 index 0000000000..8cedb44e5b --- /dev/null +++ b/externals/nitro/modules/c/nitf/source/TREs.c @@ -0,0 +1,67 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2004 - 2014, MDA Information Systems LLC + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + +#undef NITF_PLUGIN_FUNCTION_EXPORT +#define NITF_PLUGIN_FUNCTION_EXPORT(retval_) static retval_ + +#include "nitf/TRE.h" +#include "nitf/PluginIdentifier.h" + +#if _MSC_VER +#pragma warning(disable: 4464) // relative include path contains '..' +#endif + +#include "../shared/ACCHZB.c" +#include "../shared/ACCPOB.c" +#include "../shared/ACFTA.c" +#include "../shared/AIMIDB.c" +#include "../shared/CSCRNA.c" +#include "../shared/CSEXRB.c" +#include "../shared/ENGRDA.c" +#include "../shared/HISTOA.c" +#include "../shared/JITCID.c" +#include "../shared/PTPRAA.c" +#include "../shared/RPFHDR.c" + +#include "../shared/TEST_DES.c" + +#define NITF_preload_TRE(Tre_) { #Tre_, Tre_##_init, Tre_##_handler } + +extern const nitf_TREPreloaded preloadedTREs[]; +const nitf_TREPreloaded preloadedTREs[] = { +/* + NITF_preload_TRE(ACCHZB), + NITF_preload_TRE(ACCPOB), + NITF_preload_TRE(ACFTA), + NITF_preload_TRE(AIMIDB), + NITF_preload_TRE(CSCRNA), + NITF_preload_TRE(CSEXRB), + //NITF_preload_TRE(ENGRDA), + NITF_preload_TRE(HISTOA), + NITF_preload_TRE(JITCID), + NITF_preload_TRE(PTPRAA), + NITF_preload_TRE(RPFHDR), + + NITF_preload_TRE(TEST_DES), +*/ + { NULL, NULL, NULL } +}; diff --git a/externals/nitro/modules/c/nrt/include/nrt/DLL.h b/externals/nitro/modules/c/nrt/include/nrt/DLL.h index 9705b8a54e..dbdb2127fd 100644 --- a/externals/nitro/modules/c/nrt/include/nrt/DLL.h +++ b/externals/nitro/modules/c/nrt/include/nrt/DLL.h @@ -51,7 +51,8 @@ typedef struct _NRT_DLL { char *libname; /* The name of the library */ - NRT_NATIVE_DLL lib; /* A handle to the library */ + NRT_NATIVE_DLL lib; /* A handle to the library, or NULL for "preloaded" TREs */ + NRT_DLL_FUNCTION_PTR dsoMain; /* If 'lib' is NULL, the main() function */ } nrt_DLL; NRT_CXX_GUARD diff --git a/externals/nitro/modules/c/nrt/source/DLLUnix.c b/externals/nitro/modules/c/nrt/source/DLLUnix.c index 1cd147e76c..fb68470ccf 100644 --- a/externals/nitro/modules/c/nrt/source/DLLUnix.c +++ b/externals/nitro/modules/c/nrt/source/DLLUnix.c @@ -39,6 +39,7 @@ NRTAPI(nrt_DLL *) nrt_DLL_construct(nrt_Error * error) } dll->libname = NULL; dll->lib = NULL; + dll->dsoMain = NULL; return dll; } @@ -84,6 +85,7 @@ NRTAPI(NRT_BOOL) nrt_DLL_load(nrt_DLL * dll, const char *libname, return NRT_FAILURE; } + dll->dsoMain = NULL; return NRT_SUCCESS; } @@ -120,7 +122,16 @@ NRTAPI(NRT_DLL_FUNCTION_PTR) nrt_DLL_retrieve(nrt_DLL * dll, NRT_ERR_RETRIEVING_DLL_HOOK); } return ptr; + } + // This might be a "preloaded" TRE + if (dll->dsoMain) + { + const char* underscore = strchr(function, '_'); + if ((underscore != NULL) && strcmp(underscore, "_handler") == 0) + { + return dll->dsoMain; + } } /* You shouldnt be calling it if it didnt load */ diff --git a/externals/nitro/modules/c/nrt/source/DLLWin32.c b/externals/nitro/modules/c/nrt/source/DLLWin32.c index b6c7138c0f..d5a59e621c 100644 --- a/externals/nitro/modules/c/nrt/source/DLLWin32.c +++ b/externals/nitro/modules/c/nrt/source/DLLWin32.c @@ -37,6 +37,7 @@ NRTAPI(nrt_DLL *) nrt_DLL_construct(nrt_Error * error) { dll->libname = NULL; dll->lib = NULL; + dll->dsoMain = NULL; } return dll; } @@ -62,7 +63,7 @@ NRTAPI(void) nrt_DLL_destruct(nrt_DLL ** dll) NRTAPI(NRT_BOOL) nrt_DLL_isValid(nrt_DLL * dll) { - return (dll->lib != (NRT_NATIVE_DLL) NULL); + return (dll->lib != NULL) && (dll->dsoMain == NULL); } NRTAPI(NRT_BOOL) nrt_DLL_load(nrt_DLL * dll, const char *libname, @@ -87,6 +88,7 @@ NRTAPI(NRT_BOOL) nrt_DLL_load(nrt_DLL * dll, const char *libname, dll->libname = NULL; return NRT_FAILURE; } + dll->dsoMain = NULL; return NRT_SUCCESS; } @@ -132,6 +134,16 @@ NRTAPI(NRT_DLL_FUNCTION_PTR) nrt_DLL_retrieve(nrt_DLL * dll, return ptr; } + // This might be a "preloaded" TRE + if (dll->dsoMain) + { + const char* underscore = strchr(function, '_'); + if ((underscore != NULL) && strcmp(underscore, "_handler") == 0) + { + return dll->dsoMain; + } + } + nrt_Error_initf(error, NRT_CTXT, NRT_ERR_UNINITIALIZED_DLL_READ, "Failed to retrieve function [%s] -- DLL appears to be uninitialized", function); diff --git a/externals/nitro/nitro.sln b/externals/nitro/nitro.sln index d32ec93681..56cfb2e041 100644 --- a/externals/nitro/nitro.sln +++ b/externals/nitro/nitro.sln @@ -22,8 +22,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nitf-c++", "modules\c++\nit EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ACCPOB", "modules\c\nitf\ACCPOB.vcxproj", "{730B1E6E-2469-4F9E-B093-D0C6262453C9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ACCHZB", "modules\c\nitf\ACCHZB.vcxproj", "{53F9F908-C678-4DEE-9309-E71C1D03A45F}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ACFTA", "modules\c\nitf\ACFTA.vcxproj", "{51D7B426-899E-428D-9F69-5DDAC9E403FB}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AIMIDB", "modules\c\nitf\AIMIDB.vcxproj", "{12AA0752-4EE3-4E0A-85AF-0E5DEADBF343}" @@ -36,14 +34,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CSCRNA", "modules\c\nitf\CS EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RPFHDR", "modules\c\nitf\RPFHDR.vcxproj", "{CF5B4F02-364D-4117-9FB9-6C9C7938E412}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XML_DATA_CONTENT", "modules\c\nitf\XML_DATA_CONTENT.vcxproj", "{78849481-D356-4CC7-B182-31C21F857ED1}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "show_nitf++", "modules\c++\nitf\apps\show_nitf++\show_nitf++.vcxproj", "{839FF52C-57D1-45B6-81FD-5C7D72523EE5}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PTPRAA", "modules\c\nitf\PTPRAA.vcxproj", "{2BAAACA9-A5A4-412C-AE52-B16C2D107F55}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HISTOA", "modules\c\nitf\HISTOA.vcxproj", "{D749AA73-4C9A-473D-96BB-070A6D9CAA54}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ENGRDA", "modules\c\nitf\ENGRDA.vcxproj", "{53F9F908-C678-4DEE-9309-E71C1E03A45F}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "J2KCompress", "modules\c\j2k\J2KCompress.vcxproj", "{A676EDF3-F231-47C8-A6E6-0FE50B50B71B}" @@ -65,6 +59,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "coda-oss", "externals\coda- EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTest", "UnitTest\UnitTest.vcxproj", "{8ACE478C-8F6F-4D42-9B43-7D75882D4BE1}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ACCHZB", "modules\c\nitf\ACCHZB.vcxproj", "{53F9F908-C678-4DEE-9309-E71C1D03A45F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HISTOA", "modules\c\nitf\HISTOA.vcxproj", "{D749AA73-4C9A-473D-96BB-070A6D9CAA54}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XML_DATA_CONTENT", "modules\c\nitf\XML_DATA_CONTENT.vcxproj", "{78849481-D356-4CC7-B182-31C21F857ED1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -83,10 +83,6 @@ Global {730B1E6E-2469-4F9E-B093-D0C6262453C9}.Debug|x64.Build.0 = Debug|x64 {730B1E6E-2469-4F9E-B093-D0C6262453C9}.Release|x64.ActiveCfg = Release|x64 {730B1E6E-2469-4F9E-B093-D0C6262453C9}.Release|x64.Build.0 = Release|x64 - {53F9F908-C678-4DEE-9309-E71C1D03A45F}.Debug|x64.ActiveCfg = Debug|x64 - {53F9F908-C678-4DEE-9309-E71C1D03A45F}.Debug|x64.Build.0 = Debug|x64 - {53F9F908-C678-4DEE-9309-E71C1D03A45F}.Release|x64.ActiveCfg = Release|x64 - {53F9F908-C678-4DEE-9309-E71C1D03A45F}.Release|x64.Build.0 = Release|x64 {51D7B426-899E-428D-9F69-5DDAC9E403FB}.Debug|x64.ActiveCfg = Debug|x64 {51D7B426-899E-428D-9F69-5DDAC9E403FB}.Debug|x64.Build.0 = Debug|x64 {51D7B426-899E-428D-9F69-5DDAC9E403FB}.Release|x64.ActiveCfg = Release|x64 @@ -107,10 +103,6 @@ Global {CF5B4F02-364D-4117-9FB9-6C9C7938E412}.Debug|x64.Build.0 = Debug|x64 {CF5B4F02-364D-4117-9FB9-6C9C7938E412}.Release|x64.ActiveCfg = Release|x64 {CF5B4F02-364D-4117-9FB9-6C9C7938E412}.Release|x64.Build.0 = Release|x64 - {78849481-D356-4CC7-B182-31C21F857ED1}.Debug|x64.ActiveCfg = Debug|x64 - {78849481-D356-4CC7-B182-31C21F857ED1}.Debug|x64.Build.0 = Debug|x64 - {78849481-D356-4CC7-B182-31C21F857ED1}.Release|x64.ActiveCfg = Release|x64 - {78849481-D356-4CC7-B182-31C21F857ED1}.Release|x64.Build.0 = Release|x64 {839FF52C-57D1-45B6-81FD-5C7D72523EE5}.Debug|x64.ActiveCfg = Debug|x64 {839FF52C-57D1-45B6-81FD-5C7D72523EE5}.Debug|x64.Build.0 = Debug|x64 {839FF52C-57D1-45B6-81FD-5C7D72523EE5}.Release|x64.ActiveCfg = Release|x64 @@ -119,10 +111,6 @@ Global {2BAAACA9-A5A4-412C-AE52-B16C2D107F55}.Debug|x64.Build.0 = Debug|x64 {2BAAACA9-A5A4-412C-AE52-B16C2D107F55}.Release|x64.ActiveCfg = Release|x64 {2BAAACA9-A5A4-412C-AE52-B16C2D107F55}.Release|x64.Build.0 = Release|x64 - {D749AA73-4C9A-473D-96BB-070A6D9CAA54}.Debug|x64.ActiveCfg = Debug|x64 - {D749AA73-4C9A-473D-96BB-070A6D9CAA54}.Debug|x64.Build.0 = Debug|x64 - {D749AA73-4C9A-473D-96BB-070A6D9CAA54}.Release|x64.ActiveCfg = Release|x64 - {D749AA73-4C9A-473D-96BB-070A6D9CAA54}.Release|x64.Build.0 = Release|x64 {53F9F908-C678-4DEE-9309-E71C1E03A45F}.Debug|x64.ActiveCfg = Debug|x64 {53F9F908-C678-4DEE-9309-E71C1E03A45F}.Debug|x64.Build.0 = Debug|x64 {53F9F908-C678-4DEE-9309-E71C1E03A45F}.Release|x64.ActiveCfg = Release|x64 @@ -147,27 +135,39 @@ Global {8ACE478C-8F6F-4D42-9B43-7D75882D4BE1}.Debug|x64.Build.0 = Debug|x64 {8ACE478C-8F6F-4D42-9B43-7D75882D4BE1}.Release|x64.ActiveCfg = Release|x64 {8ACE478C-8F6F-4D42-9B43-7D75882D4BE1}.Release|x64.Build.0 = Release|x64 + {53F9F908-C678-4DEE-9309-E71C1D03A45F}.Debug|x64.ActiveCfg = Debug|x64 + {53F9F908-C678-4DEE-9309-E71C1D03A45F}.Debug|x64.Build.0 = Debug|x64 + {53F9F908-C678-4DEE-9309-E71C1D03A45F}.Release|x64.ActiveCfg = Release|x64 + {53F9F908-C678-4DEE-9309-E71C1D03A45F}.Release|x64.Build.0 = Release|x64 + {D749AA73-4C9A-473D-96BB-070A6D9CAA54}.Debug|x64.ActiveCfg = Debug|x64 + {D749AA73-4C9A-473D-96BB-070A6D9CAA54}.Debug|x64.Build.0 = Debug|x64 + {D749AA73-4C9A-473D-96BB-070A6D9CAA54}.Release|x64.ActiveCfg = Release|x64 + {D749AA73-4C9A-473D-96BB-070A6D9CAA54}.Release|x64.Build.0 = Release|x64 + {78849481-D356-4CC7-B182-31C21F857ED1}.Debug|x64.ActiveCfg = Debug|x64 + {78849481-D356-4CC7-B182-31C21F857ED1}.Debug|x64.Build.0 = Debug|x64 + {78849481-D356-4CC7-B182-31C21F857ED1}.Release|x64.ActiveCfg = Release|x64 + {78849481-D356-4CC7-B182-31C21F857ED1}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {730B1E6E-2469-4F9E-B093-D0C6262453C9} = {27A2685A-E869-42A2-956D-92994F60C536} - {53F9F908-C678-4DEE-9309-E71C1D03A45F} = {27A2685A-E869-42A2-956D-92994F60C536} {51D7B426-899E-428D-9F69-5DDAC9E403FB} = {27A2685A-E869-42A2-956D-92994F60C536} {12AA0752-4EE3-4E0A-85AF-0E5DEADBF343} = {27A2685A-E869-42A2-956D-92994F60C536} {D1D7FCD3-6130-4504-9DA0-9D80506BE55E} = {27A2685A-E869-42A2-956D-92994F60C536} {023DE06D-3967-4406-B1B8-032118BB2552} = {27A2685A-E869-42A2-956D-92994F60C536} {CF5B4F02-364D-4117-9FB9-6C9C7938E412} = {27A2685A-E869-42A2-956D-92994F60C536} - {78849481-D356-4CC7-B182-31C21F857ED1} = {27A2685A-E869-42A2-956D-92994F60C536} {2BAAACA9-A5A4-412C-AE52-B16C2D107F55} = {27A2685A-E869-42A2-956D-92994F60C536} - {D749AA73-4C9A-473D-96BB-070A6D9CAA54} = {27A2685A-E869-42A2-956D-92994F60C536} {53F9F908-C678-4DEE-9309-E71C1E03A45F} = {27A2685A-E869-42A2-956D-92994F60C536} {A676EDF3-F231-47C8-A6E6-0FE50B50B71B} = {27A2685A-E869-42A2-956D-92994F60C536} {C787537A-0CAC-4D6D-A6D6-A66765A06753} = {27A2685A-E869-42A2-956D-92994F60C536} {A45CB073-25A7-411D-A7E7-589BCC8AF547} = {5C5727E7-0CFF-42B4-8F5A-D31B3BC81F21} {0A9BDA26-092F-4A2C-BBEF-00C64BF0C65E} = {27A2685A-E869-42A2-956D-92994F60C536} {9997E895-5161-4DDF-8F3F-099894CB2F21} = {7D26D571-0014-4C50-BF86-612E743E64B6} + {53F9F908-C678-4DEE-9309-E71C1D03A45F} = {27A2685A-E869-42A2-956D-92994F60C536} + {D749AA73-4C9A-473D-96BB-070A6D9CAA54} = {27A2685A-E869-42A2-956D-92994F60C536} + {78849481-D356-4CC7-B182-31C21F857ED1} = {27A2685A-E869-42A2-956D-92994F60C536} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2D7AC542-BBB6-4BAC-8BF1-7E76C714BBA4}