Skip to content

Commit

Permalink
Use IdTraits for parsing to avoid creating lots of strings
Browse files Browse the repository at this point in the history
  • Loading branch information
rsjbailey committed Mar 14, 2024
1 parent 80805eb commit b0ab49a
Show file tree
Hide file tree
Showing 13 changed files with 249 additions and 107 deletions.
118 changes: 73 additions & 45 deletions include/adm/detail/id_parser.hpp
Original file line number Diff line number Diff line change
@@ -1,70 +1,101 @@
#pragma once

#include <algorithm>
#include <string>
#include <sstream>

namespace adm {
namespace detail {
/// object used to help with ID parsing
///
/// construct one with the tyep name (for errors) and the ID string to
/// parse, then call the methods to check and parse different aspects of
/// the ID
inline bool id_starts_with(std::string const &id, const char *prefix,
std::size_t prefix_length) {
return prefix_length <= id.size() &&
std::equal(prefix, prefix + prefix_length, id.begin(),
id.begin() + prefix_length);
}

class IdParseException : public std::runtime_error {
public:
IdParseException(std::size_t index)
: std::runtime_error("Id parser error - bad hex digit"),
index{index} {}
std::size_t index;
};

template <typename T>
struct IdTraits;

constexpr std::size_t strlen_constexpr(char const *start) {
const char *end = start;
while (*end != '\0') ++end;
return end - start;
}

struct IdSection {};

class IDParser {
public:
/// construct given the type name for errors, and the ID string to parse
///
/// this stores type_ and id_ but owns neither -- they must be kept alive
/// externally
IDParser(const char *type_, const std::string &id_)
: type(type_), id(id_) {}
/// construct given non-owning reference to the ID string to parse
explicit IDParser(std::string const &id) : id{id} {}

/// check the length
void check_size(size_t size) {
if (id.size() != size) {
template <typename IdT>
void check_size() {
using TraitsT = IdTraits<IdT>;
constexpr auto length{detail::strlen_constexpr(TraitsT::format)};
if (id.size() != length) {
std::ostringstream errorString;
errorString << "invalid " << type << " (wrong length, should be "
<< std::to_string(size) << " characters): " << id;
errorString << "invalid " << TraitsT::name
<< " (wrong length, should be " << length
<< " characters): " << id;
throw std::runtime_error(errorString.str());
}
}

void check_prefix(const char *prefix, size_t size) const {
assert(prefix && prefix[size] == 0);
check_prefix(std::string(prefix));
}

/// check that the start of the ID matches the given prefix
void check_prefix(std::string const &prefix) const {
auto prefix_error = [this](std::string const &prefix) {
template <typename IdT>
void check_prefix() const {
using TraitsT = IdTraits<IdT>;
constexpr auto prefix_length{strlen_constexpr(TraitsT::prefix)};
if (!id_starts_with(id, TraitsT::prefix, prefix_length)) {
std::ostringstream errorString;
errorString << "invalid " << type << " (incorrect prefix, should be '"
<< prefix << "'): " << id;
errorString << "invalid " << TraitsT::name
<< " (incorrect prefix, should be '" << TraitsT::prefix
<< "'): " << id;
throw std::runtime_error(errorString.str());
};

if (prefix.size() > id.size()) {
prefix_error(prefix);
}
for (size_t i = 0; i < prefix.size(); i++)
if (id[i] != prefix.at(i)) {
prefix_error(prefix);
}
}

/// check that there's an inderscore at the given position
void check_underscore(size_t pos) {
assert(pos < id.size());
if (id[pos] != '_') {
/// check that there's an underscore at the given position
template <typename IdT>
void check_underscore() {
using TraitsT = IdTraits<IdT>;
auto underscore_pos = TraitsT::underscore_position;
assert(underscore_pos < id.size());
if (id[underscore_pos] != '_') {
std::ostringstream errorString;
errorString << "invalid " << type << " (expected underscore at char "
<< pos << "): " << id;
errorString << "invalid " << TraitsT::name
<< " (expected underscore at char " << underscore_pos
<< "): " << id;
throw std::runtime_error(errorString.str());
}
}

/// parse a hex value case-insensitively starting at start for len chars
template <typename IdT>
unsigned parse_hex(size_t start, size_t len) {
try {
return parse_hex_impl(start, len);
} catch (IdParseException const &e) {
using TraitsT = IdTraits<IdT>;
std::ostringstream errorString;
errorString << "invalid " << TraitsT::name
<< " (expected hex char at char" << e.index
<< "): " << id;
throw std::runtime_error(errorString.str());
};
}

/// parse a hex value case-insensitively starting at start for len chars
unsigned parse_hex_impl(size_t start, size_t len) {
assert(start + len <= id.size());

// parse manually -- all of the built-in methods would require copying
Expand All @@ -81,10 +112,7 @@ namespace adm {
else if ('A' <= c && c <= 'F')
c_value = c - ('A' - 10);
else {
std::ostringstream errorString;
errorString << "invalid " << type << " (expected hex char at char"
<< i << "): " << id;
throw std::runtime_error(errorString.str());
throw IdParseException(i);
}
acc = (acc << 4) | c_value;
}
Expand All @@ -93,8 +121,7 @@ namespace adm {
}

private:
const char *type;
const std::string &id;
std::string const &id;
};

/// write a hex value into an existing string
Expand All @@ -114,5 +141,6 @@ namespace adm {
throw std::runtime_error(errorString.str());
}
}

} // namespace detail
} // namespace adm
25 changes: 18 additions & 7 deletions src/elements/audio_block_format_id.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,26 @@ namespace adm {
os << formatId(*this);
}

namespace detail {
template <>
struct IdTraits<AudioBlockFormatId> {
static constexpr const char* name{"audioBlockFormatID"};
static constexpr const char* prefix{"AB_"};
static constexpr const char* format{"AB_yyyyxxxx_zzzzzzzz"};
constexpr static std::size_t const underscore_position{11};
};

} // namespace detail

AudioBlockFormatId parseAudioBlockFormatId(const std::string& id) {
// AB_yyyyxxxx_zzzzzzzz
detail::IDParser parser("AudioBlockFormatId", id);
parser.check_size(20);
parser.check_prefix("AB_", 3);
auto type = parser.parse_hex(3, 4);
auto value = parser.parse_hex(7, 4);
parser.check_underscore(11);
auto counter = parser.parse_hex(12, 8);
detail::IDParser parser{id};
parser.check_size<AudioBlockFormatId>();
parser.check_prefix<AudioBlockFormatId>();
auto type = parser.parse_hex<AudioBlockFormatId>(3, 4);
auto value = parser.parse_hex<AudioBlockFormatId>(7, 4);
parser.check_underscore<AudioBlockFormatId>();
auto counter = parser.parse_hex<AudioBlockFormatId>(12, 8);
return AudioBlockFormatId(TypeDescriptor(type), AudioBlockFormatIdValue(value),
AudioBlockFormatIdCounter(counter));
}
Expand Down
20 changes: 14 additions & 6 deletions src/elements/audio_channel_format_id.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include "adm/detail/optional_comparison.hpp"

namespace adm {

// ---- Defaults ---- //
const TypeDescriptor AudioChannelFormatId::channelTypeDefault_ =
TypeDefinition::UNDEFINED;
Expand Down Expand Up @@ -84,13 +83,22 @@ namespace adm {
os << formatId(*this);
}

namespace detail {
template <>
struct IdTraits<AudioChannelFormatId> {
static constexpr char const* name{"audioChannelFormatID"};
static constexpr char const* prefix{"AC_"};
static constexpr char const* format{"AC_yyyyxxxx"};
};
} // namespace detail

AudioChannelFormatId parseAudioChannelFormatId(const std::string& id) {
// AC_yyyyxxxx
detail::IDParser parser("AudioChannelFormatId", id);
parser.check_prefix("AC_", 3);
parser.check_size(11);
auto type = parser.parse_hex(3, 4);
auto value = parser.parse_hex(7, 4);
detail::IDParser parser{id};
parser.check_prefix<AudioChannelFormatId>();
parser.check_size<AudioChannelFormatId>();
auto type = parser.parse_hex<AudioChannelFormatId>(3, 4);
auto value = parser.parse_hex<AudioChannelFormatId>(7, 4);
return AudioChannelFormatId(TypeDescriptor(type),
AudioChannelFormatIdValue(value));
}
Expand Down
17 changes: 13 additions & 4 deletions src/elements/audio_content_id.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,21 @@ namespace adm {
// ---- Common ---- //
void AudioContentId::print(std::ostream& os) const { os << formatId(*this); }

namespace detail {
template <>
struct IdTraits<AudioContentId> {
static constexpr char const* name{"audioContentID"};
static constexpr char const* prefix{"ACO_"};
static constexpr char const* format{"ACO_xxxx"};
};
} // namespace detail

AudioContentId parseAudioContentId(const std::string& id) {
// ACO_xxxx
detail::IDParser parser("AudioContentId", id);
parser.check_size(8);
parser.check_prefix("ACO_", 4);
auto value = parser.parse_hex(4, 4);
detail::IDParser parser{id};
parser.check_prefix<AudioContentId>();
parser.check_size<AudioContentId>();
auto value = parser.parse_hex<AudioContentId>(4, 4);
return AudioContentId(AudioContentIdValue(value));
}

Expand Down
19 changes: 14 additions & 5 deletions src/elements/audio_object_id.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,22 @@ namespace adm {
// ---- Common ---- //
void AudioObjectId::print(std::ostream& os) const { os << formatId(*this); }

namespace detail {
template <>
struct IdTraits<AudioObjectId> {
static constexpr char const* name{"audioObjectID"};
static constexpr char const* prefix{"AO_"};
static constexpr char const* format{"AO_xxxx"};
};
} // namespace detail

AudioObjectId parseAudioObjectId(const std::string& id) {
// AO_xxxx
detail::IDParser parser("AudioObjectId", id);
parser.check_prefix("AO_", 3);
parser.check_size(7);
auto value = parser.parse_hex(3, 4);
return AudioObjectId(AudioObjectIdValue(value));
detail::IDParser parser{id};
parser.check_prefix<AudioObjectId>();
parser.check_size<AudioObjectId>();
auto value = parser.parse_hex<AudioObjectId>(3, 4);
return {AudioObjectIdValue(value)};
}

std::string formatId(const AudioObjectId& id) {
Expand Down
19 changes: 14 additions & 5 deletions src/elements/audio_pack_format_id.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,22 @@ namespace adm {
os << formatId(*this);
}

namespace detail {
template <>
struct IdTraits<AudioPackFormatId> {
static constexpr char const* name{"audioPackFormatID"};
static constexpr char const* prefix{"AP_"};
static constexpr char const* format{"AP_yyyyxxxx"};
};
} // namespace detail

AudioPackFormatId parseAudioPackFormatId(const std::string& id) {
// AP_yyyyxxxx
detail::IDParser parser("AudioPackFormatId", id);
parser.check_prefix("AP_", 3);
parser.check_size(11);
auto type = parser.parse_hex(3, 4);
auto value = parser.parse_hex(7, 4);
detail::IDParser parser{id};
parser.check_prefix<AudioPackFormatId>();
parser.check_size<AudioPackFormatId>();
auto type = parser.parse_hex<AudioPackFormatId>(3, 4);
auto value = parser.parse_hex<AudioPackFormatId>(7, 4);
return AudioPackFormatId(TypeDescriptor(type),
AudioPackFormatIdValue(value));
}
Expand Down
17 changes: 13 additions & 4 deletions src/elements/audio_programme_id.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,21 @@ namespace adm {
os << formatId(*this);
}

namespace detail {
template <>
struct IdTraits<AudioProgrammeId> {
static constexpr char const* name{"audioProgrammeID"};
static constexpr char const* prefix{"APR_"};
static constexpr char const* format{"APR_xxxx"};
};
} // namespace detail

AudioProgrammeId parseAudioProgrammeId(const std::string& id) {
// APR_xxxx
detail::IDParser parser("AudioProgrammeId", id);
parser.check_size(8);
parser.check_prefix("APR_", 4);
auto value = parser.parse_hex(4, 4);
detail::IDParser parser{id};
parser.check_prefix<AudioProgrammeId>();
parser.check_size<AudioProgrammeId>();
auto value = parser.parse_hex<AudioProgrammeId>(4, 4);
return AudioProgrammeId(AudioProgrammeIdValue(value));
}

Expand Down
19 changes: 14 additions & 5 deletions src/elements/audio_stream_format_id.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,22 @@ namespace adm {
os << formatId(*this);
}

namespace detail {
template <>
struct IdTraits<AudioStreamFormatId> {
static constexpr char const* name{"audioStreamFormatID"};
static constexpr char const* prefix{"AS_"};
static constexpr char const* format{"AS_yyyyxxxx"};
};
} // namespace detail

AudioStreamFormatId parseAudioStreamFormatId(const std::string& id) {
// AS_yyyyxxxx
detail::IDParser parser("AudioStreamFormatId", id);
parser.check_size(11);
parser.check_prefix("AS_", 3);
auto type = parser.parse_hex(3, 4);
auto value = parser.parse_hex(7, 4);
detail::IDParser parser{id};
parser.check_prefix<AudioStreamFormatId>();
parser.check_size<AudioStreamFormatId>();
auto type = parser.parse_hex<AudioStreamFormatId>(3, 4);
auto value = parser.parse_hex<AudioStreamFormatId>(7, 4);
return AudioStreamFormatId(TypeDescriptor(type),
AudioStreamFormatIdValue(value));
}
Expand Down
Loading

0 comments on commit b0ab49a

Please sign in to comment.