From 9eb32f2e38f7ed7ed7e85484201e7ea933c15cca Mon Sep 17 00:00:00 2001 From: Philipp Geier Date: Mon, 17 Nov 2025 13:25:38 +0000 Subject: [PATCH 1/2] Use unique_ptr in CodesContent; Remove MallocCodesContent --- src/metkit/CMakeLists.txt | 2 - src/metkit/codes/BUFRDecoder.cc | 12 ++- src/metkit/codes/BufrContent.cc | 27 +----- src/metkit/codes/BufrContent.h | 5 +- src/metkit/codes/CodesContent.cc | 116 +++++-------------------- src/metkit/codes/CodesContent.h | 15 ++-- src/metkit/codes/CodesSplitter.cc | 20 +++-- src/metkit/codes/GRIBDecoder.cc | 15 +++- src/metkit/codes/MallocCodesContent.cc | 53 ----------- src/metkit/codes/MallocCodesContent.h | 46 ---------- src/metkit/codes/api/CodesAPI.cc | 17 ++++ src/metkit/codes/api/CodesAPI.h | 19 +++- src/tools/bufr-sanity-check.cc | 6 +- 13 files changed, 102 insertions(+), 251 deletions(-) delete mode 100644 src/metkit/codes/MallocCodesContent.cc delete mode 100644 src/metkit/codes/MallocCodesContent.h diff --git a/src/metkit/CMakeLists.txt b/src/metkit/CMakeLists.txt index 3252f404..ca259c4f 100644 --- a/src/metkit/CMakeLists.txt +++ b/src/metkit/CMakeLists.txt @@ -137,8 +137,6 @@ if ( HAVE_GRIB ) codes/UserDataContent.h codes/DataContent.cc codes/DataContent.h - codes/MallocCodesContent.cc - codes/MallocCodesContent.h codes/CodesSplitter.cc codes/CodesSplitter.h codes/GribAccessor.cc diff --git a/src/metkit/codes/BUFRDecoder.cc b/src/metkit/codes/BUFRDecoder.cc index 08052e15..7ca458ba 100644 --- a/src/metkit/codes/BUFRDecoder.cc +++ b/src/metkit/codes/BUFRDecoder.cc @@ -89,14 +89,15 @@ void BUFRDecoder::getMetadata(const eckit::message::Message& msg, eckit::message // https://confluence.ecmwf.int/display/ECC/bufr_keys_iterator h->set("unpack", 1); - for (auto& k : h->keys()) { + for (const auto& k : h->keys()) { auto name = k.name(); if (name == "subsetNumber") { continue; } - /* get key size to see if it is an array */ + // Get key size to see if it is an array + // Only continue for scalar values if (h->size(name) != 1) { continue; } @@ -113,6 +114,13 @@ void BUFRDecoder::getMetadata(const eckit::message::Message& msg, eckit::message if constexpr (std::is_same_v || std::is_arithmetic_v) { gather.setValue(name, std::forward(v)); } + else { + // Unhandled types are all array types - the prior call checking `size != 1` only allows for + // scalars. + throw eckit::Exception( + std::string("Unexpected type when accessing BURF message metadata ") + typeid(v).name(), + Here()); + } }, k.get()); break; diff --git a/src/metkit/codes/BufrContent.cc b/src/metkit/codes/BufrContent.cc index 1836d036..92a4e030 100644 --- a/src/metkit/codes/BufrContent.cc +++ b/src/metkit/codes/BufrContent.cc @@ -8,14 +8,8 @@ * does it submit to any jurisdiction. */ -/// @author Baudouin Raoult -/// @author Emanuele Danovaro -/// @date Mar 2022 - #include "metkit/codes/BufrContent.h" -#include "metkit/codes/GribHandle.h" - #include "eccodes.h" namespace metkit { @@ -24,29 +18,14 @@ namespace codes { //---------------------------------------------------------------------------------------------------------------------- -BufrContent::BufrContent(codes_handle* handle, bool delete_handle) : CodesContent(handle, delete_handle) {} - -BufrContent::BufrContent(const codes_handle* handle) : BufrContent(const_cast(handle), false) {} - -BufrContent::~BufrContent() {} - +BufrContent::BufrContent(std::unique_ptr handle) : CodesContent(std::move(handle)) {} //---------------------------------------------------------------------------------------------------------------------- void BufrContent::transform(const eckit::OrderedStringDict& dict) { - - std::vector values; - - for (auto& kv : dict) { - codes_values v; - v.name = kv.first.c_str(); - v.long_value = std::stol(kv.second); - v.type = GRIB_TYPE_LONG; - - values.push_back(v); + for (const auto& [key, value] : dict) { + handle_->set(key, std::stol(value)); } - - CODES_CALL(codes_set_values(handle_, values.data(), values.size())); } diff --git a/src/metkit/codes/BufrContent.h b/src/metkit/codes/BufrContent.h index 5e0e630a..1e84326f 100644 --- a/src/metkit/codes/BufrContent.h +++ b/src/metkit/codes/BufrContent.h @@ -25,10 +25,9 @@ namespace codes { class BufrContent : public CodesContent { public: - BufrContent(codes_handle* handle, bool delete_handle); - explicit BufrContent(const codes_handle* handle); + BufrContent(std::unique_ptr handle); - ~BufrContent(); + virtual ~BufrContent() = default; protected: diff --git a/src/metkit/codes/CodesContent.cc b/src/metkit/codes/CodesContent.cc index 9c464f5f..3967a6c3 100644 --- a/src/metkit/codes/CodesContent.cc +++ b/src/metkit/codes/CodesContent.cc @@ -8,9 +8,6 @@ * does it submit to any jurisdiction. */ -/// @author Baudouin Raoult -/// @date Jun 2020 - #include "metkit/codes/CodesContent.h" #include "eccodes.h" @@ -19,8 +16,6 @@ #include "eckit/io/DataHandle.h" #include "eckit/io/MemoryHandle.h" -#include "eckit/memory/Zero.h" - #include "metkit/codes/GribHandle.h" namespace metkit { @@ -28,37 +23,21 @@ namespace codes { //---------------------------------------------------------------------------------------------------------------------- -CodesContent::CodesContent(codes_handle* handle, bool delete_handle) : handle_(handle), delete_handle_(delete_handle) { - ASSERT(handle_); -} - -CodesContent::CodesContent(const codes_handle* handle) : CodesContent(const_cast(handle), false) {} - - -CodesContent::~CodesContent() { - if (delete_handle_) { - codes_handle_delete(handle_); - } -} +CodesContent::CodesContent(std::unique_ptr handle) : handle_(std::move(handle)) {} //---------------------------------------------------------------------------------------------------------------------- size_t CodesContent::length() const { - size_t size; - const void* data; - CODES_CALL(codes_get_message(handle_, &data, &size)); - return size; + return handle_->messageSize(); } //---------------------------------------------------------------------------------------------------------------------- void CodesContent::write(eckit::DataHandle& handle) const { - size_t size; - const void* data; - CODES_CALL(codes_get_message(handle_, &data, &size)); - if (handle.write(data, size) != size) { + auto data = handle_->messageData(); + if (handle.write(data.data(), data.size()) != data.size()) { std::ostringstream oss; oss << "Write error to data handle " << handle; throw eckit::WriteError(oss.str(), Here()); @@ -69,10 +48,8 @@ void CodesContent::write(eckit::DataHandle& handle) const { //---------------------------------------------------------------------------------------------------------------------- eckit::DataHandle* CodesContent::readHandle() const { - size_t size; - const void* data; - CODES_CALL(codes_get_message(handle_, &data, &size)); - return new eckit::MemoryHandle(data, size); + auto data = handle_->messageData(); + return new eckit::MemoryHandle(data.data(), data.size()); } @@ -86,128 +63,79 @@ void CodesContent::print(std::ostream& s) const { //---------------------------------------------------------------------------------------------------------------------- std::string CodesContent::getString(const std::string& key) const { - char values[10240]; - size_t len = sizeof(values); - - values[0] = 0; - - CODES_CALL(codes_get_string(handle_, key.c_str(), values, &len)); - // ASSERT(err) - - return values; + return handle_->getString(key); } //---------------------------------------------------------------------------------------------------------------------- long CodesContent::getLong(const std::string& key) const { - long v = 0; - CODES_CALL(codes_get_long(handle_, key.c_str(), &v)); - return v; + return handle_->getLong(key); } //---------------------------------------------------------------------------------------------------------------------- double CodesContent::getDouble(const std::string& key) const { - double v = 0; - CODES_CALL(codes_get_double(handle_, key.c_str(), &v)); - return v; + return handle_->getDouble(key); } //---------------------------------------------------------------------------------------------------------------------- void CodesContent::getDoubleArray(const std::string& key, std::vector& values) const { - size_t size = 0; - CODES_CALL(codes_get_size(handle_, key.c_str(), &size)); - - size_t count = size; - values.resize(count); - CODES_CALL(codes_get_double_array(handle_, key.c_str(), &values[0], &count)); - ASSERT(count == size); + values = handle_->getDoubleArray(key); } //---------------------------------------------------------------------------------------------------------------------- void CodesContent::getFloatArray(const std::string& key, std::vector& values) const { - size_t size = 0; - CODES_CALL(codes_get_size(handle_, key.c_str(), &size)); - - size_t count = size; - values.resize(count); - CODES_CALL(codes_get_float_array(handle_, key.c_str(), &values[0], &count)); - ASSERT(count == size); + values = handle_->getFloatArray(key); } //---------------------------------------------------------------------------------------------------------------------- size_t CodesContent::getSize(const std::string& key) const { - size_t size = 0; - CODES_CALL(codes_get_size(handle_, key.c_str(), &size)); - return size; + return handle_->size(key); } //---------------------------------------------------------------------------------------------------------------------- void CodesContent::getDoubleArray(const std::string& key, double* data, size_t len) const { - size_t count = len; - CODES_CALL(codes_get_double_array(handle_, key.c_str(), data, &count)); - ASSERT(count == len); + auto arr = handle_->getDoubleArray(key); + ASSERT(len == arr.size()); + std::copy(arr.begin(), arr.end(), data); } //---------------------------------------------------------------------------------------------------------------------- void CodesContent::getFloatArray(const std::string& key, float* data, size_t len) const { - size_t count = len; - CODES_CALL(codes_get_float_array(handle_, key.c_str(), data, &count)); - ASSERT(count == len); + auto arr = handle_->getFloatArray(key); + ASSERT(len == arr.size()); + std::copy(arr.begin(), arr.end(), data); } //---------------------------------------------------------------------------------------------------------------------- void CodesContent::transform(const eckit::OrderedStringDict& dict) { - - std::vector values; - values.reserve(dict.size()); - - for (auto& kv : dict) { - codes_values v; - v.name = kv.first.c_str(); - v.string_value = kv.second.c_str(); - v.type = GRIB_TYPE_STRING; - - values.push_back(v); + for (auto& [key, value] : dict) { + handle_->set(key, value); } - - CODES_CALL(codes_set_values(handle_, values.data(), values.size())); } //---------------------------------------------------------------------------------------------------------------------- eckit::Offset CodesContent::offset() const { - long pos; - CODES_CALL(codes_get_long(handle_, "offset", &pos)); - return pos; -} - - -//---------------------------------------------------------------------------------------------------------------------- - -const codes_handle* CodesContent::codesHandle() const { - return handle_; + return handle_->getLong("offset"); } //---------------------------------------------------------------------------------------------------------------------- const void* CodesContent::data() const { - size_t size; - const void* data; - CODES_CALL(codes_get_message(handle_, &data, &size)); - return data; + return handle_->messageData().data(); } diff --git a/src/metkit/codes/CodesContent.h b/src/metkit/codes/CodesContent.h index c09c1beb..e0153001 100644 --- a/src/metkit/codes/CodesContent.h +++ b/src/metkit/codes/CodesContent.h @@ -13,35 +13,31 @@ #pragma once -#include "eckit/message/MessageContent.h" +#include "metkit/codes/api/CodesAPI.h" -typedef struct grib_handle codes_handle; +#include "eckit/message/MessageContent.h" namespace metkit { namespace codes { - //---------------------------------------------------------------------------------------------------------------------- class CodesContent : public eckit::message::MessageContent { public: - CodesContent(codes_handle* handle, bool delete_handle); - explicit CodesContent(const codes_handle* handle); + CodesContent(std::unique_ptr handle); - ~CodesContent(); + virtual ~CodesContent() = default; protected: - codes_handle* handle_; + std::unique_ptr handle_; using eckit::message::MessageContent::transform; void transform(const eckit::OrderedStringDict&) override; private: - bool delete_handle_; - size_t length() const override; void write(eckit::DataHandle& handle) const override; eckit::DataHandle* readHandle() const override; @@ -56,7 +52,6 @@ class CodesContent : public eckit::message::MessageContent { void getFloatArray(const std::string& key, float* data, size_t lenExpected) const override; eckit::Offset offset() const override; - const codes_handle* codesHandle() const; const void* data() const override; }; diff --git a/src/metkit/codes/CodesSplitter.cc b/src/metkit/codes/CodesSplitter.cc index 168c0971..bcf1205c 100644 --- a/src/metkit/codes/CodesSplitter.cc +++ b/src/metkit/codes/CodesSplitter.cc @@ -12,13 +12,13 @@ #include "eccodes.h" -#include "eckit/config/Resource.h" #include "eckit/io/PeekHandle.h" #include "eckit/log/Log.h" #include "eckit/message/Message.h" +#include "metkit/codes/CodesContent.h" #include "metkit/codes/GribHandle.h" -#include "metkit/codes/MallocCodesContent.h" +#include "metkit/codes/api/CodesAPI.h" namespace metkit { namespace codes { @@ -53,25 +53,27 @@ static long readcb(void* data, void* buffer, long len) { eckit::message::Message CodesSplitter::next() { size_t size; - int err = 0; - void* data = wmo_read_any_from_stream_malloc(&handle_, &readcb, &size, &err); + int err = 0; + auto deleter = [](uint8_t* ptr) { ::free(ptr); }; + auto data = std::unique_ptr{ + reinterpret_cast(wmo_read_any_from_stream_malloc(&handle_, &readcb, &size, &err)), deleter}; if (err != 0 and err != GRIB_END_OF_FILE) { - if (data) { - ::free(data); - } if (err == GRIB_WRONG_LENGTH && handle_.canSeek()) { eckit::Offset off = handle_.position() - eckit::Length(size); handle_.seek((off < eckit::Offset(0) ? eckit::Offset(0) : off) + eckit::Offset(4)); } - CODES_CALL(err); + std::string msg = std::string("CodesSplitter::next() - wmo_read_any_from_stream_malloc failed: ") + + std::string(codes_get_error_message(err)); + throw CodesException(msg, Here()); } if (!data) { return eckit::message::Message(); } - return eckit::message::Message(new MallocCodesContent(data, size, 0)); + return eckit::message::Message( + new CodesContent(codesHandleFromMessageCopy({static_cast(data.get()), size}))); } void CodesSplitter::print(std::ostream& s) const { diff --git a/src/metkit/codes/GRIBDecoder.cc b/src/metkit/codes/GRIBDecoder.cc index c7f9613d..5d37b933 100644 --- a/src/metkit/codes/GRIBDecoder.cc +++ b/src/metkit/codes/GRIBDecoder.cc @@ -9,6 +9,7 @@ */ #include "eckit/config/Resource.h" +#include "eckit/exception/Exceptions.h" #include "eckit/io/Buffer.h" #include "eckit/message/Message.h" #include "eckit/serialisation/MemoryStream.h" @@ -25,6 +26,7 @@ namespace metkit { namespace codes { + //---------------------------------------------------------------------------------------------------------------------- bool GRIBDecoder::match(const eckit::message::Message& msg) const { @@ -43,13 +45,14 @@ void GRIBDecoder::getMetadata(const eckit::message::Message& msg, eckit::message auto h = codesHandleFromMessage({static_cast(msg.data()), msg.length()}); - for (auto& k : h->keys(nameSpace)) { + for (const auto& k : h->keys(nameSpace)) { auto name = k.name(); if (name[0] == '_') continue; // skip silly underscores in GRIB - /* get key size to see if it is an array */ + // Get key size to see if it is an array + // Only continue for scalar values if (h->size(name) != 1) { continue; } @@ -72,6 +75,14 @@ void GRIBDecoder::getMetadata(const eckit::message::Message& msg, eckit::message if constexpr (std::is_same_v || std::is_arithmetic_v) { gather.setValue(name, std::forward(v)); } + else { + // Unhandled types are all array types - the prior call checking `size != 1` only allows + // for scalars. + throw eckit::Exception( + std::string("Unexpected type when accessing GRIB message metadata ") + + typeid(v).name(), + Here()); + } }, k.get()); } diff --git a/src/metkit/codes/MallocCodesContent.cc b/src/metkit/codes/MallocCodesContent.cc deleted file mode 100644 index 13728e7a..00000000 --- a/src/metkit/codes/MallocCodesContent.cc +++ /dev/null @@ -1,53 +0,0 @@ -/* - * (C) Copyright 2017- ECMWF. - * - * This software is licensed under the terms of the Apache Licence Version 2.0 - * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. - * In applying this licence, ECMWF does not waive the privileges and immunities - * granted to it by virtue of its status as an intergovernmental organisation nor - * does it submit to any jurisdiction. - */ - -/// @author Baudouin Raoult -/// @date Jun 2020 - -#include -#include - -#include "eckit/exception/Exceptions.h" -#include "eckit/message/MessageContent.h" - -#include "metkit/codes/CodesContent.h" -#include "metkit/codes/MallocCodesContent.h" - -#include "eccodes.h" /// @todo remove this depedency on eccodes fom here - -namespace metkit { -namespace codes { - -MallocCodesContent::MallocCodesContent(void* data, size_t size, const eckit::Offset& offset) : - CodesContent(codes_handle_new_from_message(nullptr, data, size), true), - buffer_(data), - length_(size), - offset_(offset) {} - -MallocCodesContent::~MallocCodesContent() { - ::free(buffer_); -} - -void MallocCodesContent::print(std::ostream& s) const { - s << "MallocCodesContent[]"; -} - -eckit::Offset MallocCodesContent::offset() const { - return offset_; -} -const void* MallocCodesContent::data() const { - return buffer_; -} -size_t MallocCodesContent::length() const { - return length_; -} - -} // namespace codes -} // namespace metkit diff --git a/src/metkit/codes/MallocCodesContent.h b/src/metkit/codes/MallocCodesContent.h deleted file mode 100644 index 7aa6f45f..00000000 --- a/src/metkit/codes/MallocCodesContent.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * (C) Copyright 2017- ECMWF. - * - * This software is licensed under the terms of the Apache Licence Version 2.0 - * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. - * In applying this licence, ECMWF does not waive the privileges and immunities - * granted to it by virtue of its status as an intergovernmental organisation nor - * does it submit to any jurisdiction. - */ - -/// @author Baudouin Raoult -/// @author Tiago Quintino -/// @date Jun 2020 - -#pragma once - -#include "eckit/io/Offset.h" -#include "metkit/codes/CodesContent.h" - - -namespace metkit { -namespace codes { - -class MallocCodesContent : public CodesContent { -public: - - MallocCodesContent(void* data, size_t size, const eckit::Offset& offset); - ~MallocCodesContent(); - -private: // methods - - void print(std::ostream& s) const override; - eckit::Offset offset() const override; - const void* data() const override; - size_t length() const override; - -private: // members - - void* buffer_; - size_t length_; - eckit::Offset offset_; -}; - - -} // namespace codes -} // namespace metkit diff --git a/src/metkit/codes/api/CodesAPI.cc b/src/metkit/codes/api/CodesAPI.cc index ac54a1b0..5f6f71e0 100644 --- a/src/metkit/codes/api/CodesAPI.cc +++ b/src/metkit/codes/api/CodesAPI.cc @@ -69,6 +69,8 @@ class OwningCodesHandle : public CodesHandle { size_t messageSize() const override; + int64_t messageOffset() const override; + Span messageData() const override; bool isDefined(const std::string& key) const override; bool isMissing(const std::string& key) const override; bool has(const std::string& key) const override; @@ -137,6 +139,12 @@ size_t OwningCodesHandle::messageSize() const { return size; } +int64_t OwningCodesHandle::messageOffset() const { + int64_t offset; + throwOnError(codes_get_message_offset(raw(), &offset), Here(), "CodesHandle::messageOffset()"); + return offset; +} + bool OwningCodesHandle::isDefined(const std::string& key) const { return codes_is_defined(raw(), key.c_str()) == 1; } @@ -367,6 +375,15 @@ void OwningCodesHandle::copyInto(uint8_t* data, size_t size) const { throwOnError(codes_get_message_copy(raw(), data, &s), Here(), "CodesHandle::copy(uint8_t*, size_t*)"); } +/// Copy the message into a new allocated buffer +Span OwningCodesHandle::messageData() const { + size_t s; + const uint8_t* data; + throwOnError(codes_get_message(raw(), reinterpret_cast(&data), &s), Here(), + "CodesHandle::messageData()"); + return {data, s}; +} + class ConcreteKeyIterator : public KeyIterator { public: // methods diff --git a/src/metkit/codes/api/CodesAPI.h b/src/metkit/codes/api/CodesAPI.h index 17487a40..863296d1 100644 --- a/src/metkit/codes/api/CodesAPI.h +++ b/src/metkit/codes/api/CodesAPI.h @@ -82,10 +82,6 @@ class CodesHandle { virtual ~CodesHandle() = default; - /// Retrieve binary size of the handled message. - /// @return Size of the message in bytes. - virtual size_t messageSize() const = 0; - /// Check if a key is defined. /// @param key Name of the field that is checked to be defined. /// @return True if the field is defined. @@ -273,6 +269,21 @@ class CodesHandle { virtual void copyInto(uint8_t* data, size_t size) const = 0; + /// Retrieve binary size of the handled message. + /// @return Size in bytes of the message. + virtual size_t messageSize() const = 0; + + /// Return the pointer to the underlying buffer. + /// @return Contiguous array to the underlying buffer as a span. + /// No data is copied or owned, lifetime is bound to this handle. + /// @see messageSize() + virtual Span messageData() const = 0; + + /// Retrieve offset of the handled message. + /// @return Offset in bytes of the message in the underlying buffer. + virtual int64_t messageOffset() const = 0; + + /// Iterate keys in a GRIB2 or BUFR handle. /// /// @param flags Iterator flags to filter keys diff --git a/src/tools/bufr-sanity-check.cc b/src/tools/bufr-sanity-check.cc index f1a39d80..550e4c5f 100644 --- a/src/tools/bufr-sanity-check.cc +++ b/src/tools/bufr-sanity-check.cc @@ -22,6 +22,7 @@ #include "metkit/codes/BUFRDecoder.h" #include "metkit/codes/BufrContent.h" +#include "metkit/codes/api/CodesAPI.h" #include "metkit/tool/MetkitTool.h" #define WRONG_KEY_LENGTH 65535 @@ -311,12 +312,13 @@ void BufrCheck::process(const PathName& input, const PathName& output) { try { pos = reader.position(); if ((rawMsg = reader.next())) { - codes_handle* h = codes_handle_new_from_message(nullptr, rawMsg.data(), rawMsg.length()); + auto h = + codes::codesHandleFromMessage({reinterpret_cast(rawMsg.data()), rawMsg.length()}); if (!h) { throw FailedLibraryCall("eccodes", "codes_handle_new_from_message", "failed to create handle", Here()); } - codes::BufrContent* c = new codes::BufrContent(h, true); + codes::BufrContent* c = new codes::BufrContent(std::move(h)); message::Message msg(c); // verify the presence of section 2 (to store the MARS key) From 0628d0d116c541dcf2fae89bd1592d7ce14c28ee Mon Sep 17 00:00:00 2001 From: Philipp Geier Date: Wed, 19 Nov 2025 15:43:39 +0000 Subject: [PATCH 2/2] CodesHandle::getBytes: use length instead of size to determine array size --- src/metkit/codes/BUFRDecoder.cc | 13 ++++++++ src/metkit/codes/GRIBDecoder.cc | 52 +++++++++++++++++--------------- src/metkit/codes/api/CodesAPI.cc | 12 ++++++-- src/metkit/codes/api/CodesAPI.h | 8 +++++ 4 files changed, 59 insertions(+), 26 deletions(-) diff --git a/src/metkit/codes/BUFRDecoder.cc b/src/metkit/codes/BUFRDecoder.cc index 7ca458ba..6a485724 100644 --- a/src/metkit/codes/BUFRDecoder.cc +++ b/src/metkit/codes/BUFRDecoder.cc @@ -114,6 +114,19 @@ void BUFRDecoder::getMetadata(const eckit::message::Message& msg, eckit::message if constexpr (std::is_same_v || std::is_arithmetic_v) { gather.setValue(name, std::forward(v)); } + else if constexpr (std::is_same_v>) { + static const char hex_digits[] = "0123456789abcdef"; + std::string out; + out.resize(v.size() * 2); + + for (size_t i = 0; i < v.size(); i++) { + uint8_t b = v[i]; + out[2 * i] = hex_digits[b >> 4]; + out[2 * i + 1] = hex_digits[b & 0x0F]; + } + + gather.setValue(name, out); + } else { // Unhandled types are all array types - the prior call checking `size != 1` only allows for // scalars. diff --git a/src/metkit/codes/GRIBDecoder.cc b/src/metkit/codes/GRIBDecoder.cc index 5d37b933..f11aa6d8 100644 --- a/src/metkit/codes/GRIBDecoder.cc +++ b/src/metkit/codes/GRIBDecoder.cc @@ -17,11 +17,11 @@ #include "metkit/codes/GRIBDecoder.h" #include "metkit/codes/api/CodesAPI.h" +#include "eccodes.h" + #include #include -#include "eccodes.h" - namespace metkit { namespace codes { @@ -63,29 +63,33 @@ void GRIBDecoder::getMetadata(const eckit::message::Message& msg, eckit::message break; } case eckit::message::ValueRepresentation::Native: { - // https://jira.ecmwf.int/browse/ECC-2166 - if (name == "uuidOfHGrid") { - // uuidOfHGrid returns size 1 although it contains 16 bytes - gather.setValue(name, k.getString()); - } - else { - std::visit( - [&](auto&& v) { - using Type = std::decay_t; - if constexpr (std::is_same_v || std::is_arithmetic_v) { - gather.setValue(name, std::forward(v)); - } - else { - // Unhandled types are all array types - the prior call checking `size != 1` only allows - // for scalars. - throw eckit::Exception( - std::string("Unexpected type when accessing GRIB message metadata ") + - typeid(v).name(), - Here()); + std::visit( + [&](auto&& v) { + using Type = std::decay_t; + if constexpr (std::is_same_v || std::is_arithmetic_v) { + gather.setValue(name, std::forward(v)); + } + if constexpr (std::is_same_v>) { + static const char hex_digits[] = "0123456789abcdef"; + std::string out; + out.resize(v.size() * 2); + + for (size_t i = 0; i < v.size(); i++) { + uint8_t b = v[i]; + out[2 * i] = hex_digits[b >> 4]; + out[2 * i + 1] = hex_digits[b & 0x0F]; } - }, - k.get()); - } + gather.setValue(name, out); + } + else { + // Unhandled types are all array types - the prior call checking `size != 1` only allows + // for scalars. + throw eckit::Exception( + std::string("Unexpected type when accessing GRIB message metadata ") + typeid(v).name(), + Here()); + } + }, + k.get()); break; } } diff --git a/src/metkit/codes/api/CodesAPI.cc b/src/metkit/codes/api/CodesAPI.cc index 5f6f71e0..7328d85f 100644 --- a/src/metkit/codes/api/CodesAPI.cc +++ b/src/metkit/codes/api/CodesAPI.cc @@ -15,6 +15,7 @@ #include "eccodes.h" + namespace std { template <> struct default_delete { @@ -87,6 +88,7 @@ class OwningCodesHandle : public CodesHandle { void forceSet(const std::string& key, Span value) override; void forceSet(const std::string& key, Span value) override; size_t size(const std::string& key) const override; + size_t length(const std::string& key) const override; CodesValue get(const std::string& key) const override; NativeType type(const std::string& key) const override; long getLong(const std::string& key) const override; @@ -222,6 +224,12 @@ size_t OwningCodesHandle::size(const std::string& key) const { return size; } +size_t OwningCodesHandle::length(const std::string& key) const { + size_t length; + throwOnError(codes_get_length(raw(), key.c_str(), &length), Here(), "CodesHandle::length(string)", key); + return length; +} + /// Get the value of the key CodesValue OwningCodesHandle::get(const std::string& key) const { NativeType ktype = type(key); @@ -353,7 +361,7 @@ std::vector OwningCodesHandle::getStringArray(const std::string& ke std::vector OwningCodesHandle::getBytes(const std::string& key) const { std::vector ret; - std::size_t ksize = size(key); + std::size_t ksize = length(key) / 2; ret.resize(ksize); throwOnError(codes_get_bytes(raw(), key.c_str(), ret.data(), &ksize), Here(), "CodesHandle::getBytes(string)", key); ret.resize(ksize); @@ -506,7 +514,7 @@ class ConcreteKeyIterator : public KeyIterator { std::vector getBytes() const override { std::vector ret; std::string key = name(); - std::size_t size = refHandle_.get().size(key); + std::size_t size = refHandle_.get().length(key) / 2; ret.resize(size); throwOnError(codes_keys_iterator_get_bytes(it_.get(), ret.data(), &size), Here(), "KeyIterator::getBytes()"); ret.resize(size); diff --git a/src/metkit/codes/api/CodesAPI.h b/src/metkit/codes/api/CodesAPI.h index 863296d1..a99aa452 100644 --- a/src/metkit/codes/api/CodesAPI.h +++ b/src/metkit/codes/api/CodesAPI.h @@ -199,6 +199,14 @@ class CodesHandle { /// @return For given scalars 1 is returned. For given arrays the size of the array is returned.. virtual size_t size(const std::string& key) const = 0; + /// Returns the number of chars/bytes contained for a given key + // of type string/byte array. + /// + /// Can be used to determine if a field is storing a scalar or an array. + /// @param key Name of the field that is supposed to be inspected. + /// @return Number of chars/bytes of contained string/byte array + virtual size_t length(const std::string& key) const = 0; + /// Get the value of the key. /// /// High-level functionality: