This is an automated email from the ASF dual-hosted git repository. bneradt pushed a commit to branch dev-1-0-12 in repository https://gitbox.apache.org/repos/asf/trafficserver-libswoc.git
commit 55cf559d3e0336e0a215f13fc70a1a1ad455ef92 Author: Alan M. Carroll <[email protected]> AuthorDate: Tue Feb 18 16:41:20 2020 -0600 Update comments in IPSpace example. --- unit_tests/ex_ipspace_properties.cc | 190 +++++++++++++++++++++++++----------- 1 file changed, 131 insertions(+), 59 deletions(-) diff --git a/unit_tests/ex_ipspace_properties.cc b/unit_tests/ex_ipspace_properties.cc index 35d591d..d00cafb 100644 --- a/unit_tests/ex_ipspace_properties.cc +++ b/unit_tests/ex_ipspace_properties.cc @@ -14,6 +14,7 @@ #include <swoc/TextView.h> #include <swoc/swoc_ip.h> #include <swoc/bwf_ip.h> +#include <swoc/Scalar.h> #include <swoc/swoc_file.h> using namespace std::literals; @@ -132,21 +133,31 @@ public: /** Add a property column to the table. * + * @tparam P Property class. * @param col Column descriptor. - * @return @a this + * @return @a A pointer to the property. + * + * The @c Property instance must be owned by the @c Table because changes are made to it specific + * to this instance of @c Table. */ - self_type & add_column(Property::Handle && col); + template < typename P > + P * add_column(std::unique_ptr<P> && col); - /// A row for the table. + /// A row in the table. class Row { - using self_type = Row; + using self_type = Row; ///< Self reference type. public: + /// Default cconstruct an row with uninitialized data. Row(MemSpan<std::byte> span) : _data(span) {} - MemSpan<std::byte> span_for(Property const& prop) const { - return MemSpan<std::byte>{_data}.remove_prefix(prop.offset()); - } + /** Extract property specific data from @a this. + * + * @param prop Property that defines the data. + * @return The range of bytes in the row for @a prop. + */ + MemSpan<std::byte> span_for(Property const& prop) const; + protected: - MemSpan<std::byte> _data; + MemSpan<std::byte> _data; ///< Raw row data. }; /** Parse input. @@ -195,15 +206,25 @@ protected: */ TextView token(TextView & line); + /** Localize view. + * + * @param src View to localize. + * @return The localized view. + * + * This copies @a src to the internal @c MemArena and returns a view of the copied data. + */ TextView localize(TextView const& src); }; -auto Table::add_column(Property::Handle &&col) -> self_type & { +template < typename P > +P * Table::add_column(std::unique_ptr<P> &&col) { + auto prop = col.get(); + auto idx = _columns.size(); col->assign_offset(_size); - col->assign_idx(_columns.size()); - _size += col->size(); + col->assign_idx(idx); + _size += static_cast<Property*>(prop)->size(); _columns.emplace_back(std::move(col)); - return *this; + return prop; } TextView Table::localize(TextView const&src) { @@ -214,25 +235,21 @@ TextView Table::localize(TextView const&src) { TextView Table::token(TextView & line) { TextView::size_type idx = 0; - // Characters of interest in a null terminated string. - char sep_list[3] = {'"', SEP, 0}; + // Characters of interest. + TextView sep_list { {'"', SEP} , 2 }; bool in_quote_p = false; while (idx < line.size()) { // Next character of interest. idx = line.find_first_of(sep_list, idx); - if (TextView::npos == idx) { - // no more, consume all of @a line. + if (TextView::npos == idx) { // nothing interesting left, consume all of @a line. break; - } else if ('"' == line[idx]) { - // quote, skip it and flip the quote state. + } else if ('"' == line[idx]) { // quote, skip it and flip the quote state. in_quote_p = !in_quote_p; ++idx; } else if (SEP == line[idx]) { // separator. - if (in_quote_p) { - // quoted separator, skip and continue. + if (in_quote_p) { // quoted separator, skip and continue. ++idx; - } else { - // found token, finish up. + } else { // found token, finish up. break; } } @@ -246,24 +263,31 @@ TextView Table::token(TextView & line) { bool Table::parse(TextView src) { unsigned line_no = 0; while (src) { - auto line = src.take_prefix_at('\n'); + auto line = src.take_prefix_at('\n').ltrim_if(&isspace); ++line_no; + // skip blank and comment lines. + if (line.empty() || '#' == *line) { + continue; + } + auto range_token = line.take_prefix_at(','); IPRange range{range_token}; if (range.empty()) { std::cout << W().print("{} is not a valid range specification.", range_token); continue; // This is an error, real code should report it. } - MemSpan<std::byte> span = _arena.alloc(_size).rebind<std::byte>(); - Row row{span}; + + auto span = _arena.alloc(_size).rebind<std::byte>(); // need this broken out. + Row row{span}; // store the original span to preserve it. for ( auto const& col : _columns) { auto token = this->token(line); if (col->needs_localized_token()) { token = this->localize(token); } - if (! col->parse(token, MemSpan<std::byte>{span.data(), col->size()})) { + if (! col->parse(token, span.subspan(0, col->size()))) { std::cout << W().print("Value \"{}\" at index {} on line {} is invalid.", token, col->idx(), line_no); } + // drop reference to storage used by this column. span.remove_prefix(col->size()); } _space.mark(range, std::move(row)); @@ -277,42 +301,77 @@ auto Table::find(IPAddr const &addr) -> Row * { bool operator == (Table::Row const&, Table::Row const&) { return false; } -// --- +MemSpan<std::byte> Table::Row::span_for(Table::Property const&prop) const { + return _data.subspan(prop.offset(), prop.size()); +} -class FlagProperty : public Table::Property { - using self_type = FlagProperty; - using super_type = Table::Property; -public: - static constexpr size_t SIZE = sizeof(bool); -protected: - size_t size() const override { return SIZE; } - bool parse(TextView token, MemSpan<std::byte> span) override; -}; +// --- +/** A set of keys, each of which represents an independent property. + * The set of keys must be specified at construction, keys not in the list are invalid. + */ class FlagGroupProperty : public Table::Property { - using self_type = FlagGroupProperty; - using super_type = Table::Property; + using self_type = FlagGroupProperty; ///< Self reference type. + using super_type = Table::Property; ///< Parent type. public: - static constexpr size_t SIZE = sizeof(uint8_t); + /** Construct with a @a name and a list of @a tags. + * + * @param name of the property + * @param tags List of valid tags that represent attributes. + * + * Input tokens must consist of lists of tokens, each of which is one of the @a tags. + * This is stored so that the exact set of tags present can be retrieved. + */ FlagGroupProperty(TextView const& name, std::initializer_list<TextView> tags); - bool is_set(unsigned idx, Table::Row const& row) const; + /** Check for a tag being present. + * + * @param idx Tag index, as specified in the constructor tag list. + * @param row Row data from the @c Table. + * @return @c true if the tag was present, @c false if not. + */ + bool is_set(Table::Row const&row, unsigned idx) const; + protected: - size_t size() const override { return SIZE; } + size_t size() const override; ///< Storeage required in a row. + + /** Parse a token. + * + * @param token Token to parse (list of tags). + * @param span Storage for parsed results. + * @return @c true on a successful parse, @c false if not. + */ bool parse(TextView token, MemSpan<std::byte> span) override; + /// List of tags. std::vector<TextView> _tags; }; -class TagProperty : public Table::Property { - using self_type = TagProperty; - using super_type = Table::Property; -public: // owner - static constexpr size_t SIZE = sizeof(uint8_t); - using super_type::super_type; +/** Enumeration property. + * The tokens for this property are assumed to be from a limited set of tags. Each token, the + * value for that row, must be one of those tags. The tags do not need to be specified, but will be + * accumulated as needed. The property supports a maximum of 255 distinct tags. + */ +class EnumProperty : public Table::Property { + using self_type = EnumProperty; ///< Self reference type. + using super_type = Table::Property; ///< Parent type. + using store_type = __uint8_t; ///< Row storage type. +public: + using super_type::super_type; ///< Inherit super type constructors. + + /// @return The enumeration tag for this @a row. + TextView operator() (Table::Row const& row) const; protected: - std::vector<TextView> _tags; + std::vector<TextView> _tags; ///< Tags in the enumeration. - size_t size() const override { return SIZE; } + /// @a return Size of required storage. + size_t size() const override { return sizeof(store_type); } + + /** Parse a token. + * + * @param token Token to parse (an enumeration tag). + * @param span Storage for parsed results. + * @return @c true on a successful parse, @c false if not. + */ bool parse(TextView token, MemSpan<std::byte> span) override; }; @@ -365,12 +424,16 @@ bool FlagGroupProperty::parse(TextView token, MemSpan<std::byte> span) { return true; } -bool FlagGroupProperty::is_set(unsigned flag_idx, Table::Row const& row) const { +bool FlagGroupProperty::is_set(Table::Row const&row, unsigned idx) const { auto sp = row.span_for(*this); - return std::byte{0} != ((sp[flag_idx/8] >> (flag_idx%8)) & std::byte{1}); + return std::byte{0} != ((sp[idx / 8] >> (idx % 8)) & std::byte{1}); } -bool TagProperty::parse(TextView token, MemSpan<std::byte> span) { +size_t FlagGroupProperty::size() const { + return swoc::Scalar<8>(swoc::round_up(_tags.size())).count(); +} + +bool EnumProperty::parse(TextView token, MemSpan<std::byte> span) { // Already got one? auto spot = std::find_if(_tags.begin(), _tags.end(), [&](TextView const& tag) { return 0 == strcasecmp(token, tag); }); if (spot == _tags.end()) { // nope, add it to the list. @@ -381,15 +444,20 @@ bool TagProperty::parse(TextView token, MemSpan<std::byte> span) { return true; } +TextView EnumProperty::operator()(Table::Row const& row) const { + auto idx = row.span_for(*this).rebind<store_type>()[0]; + return _tags[idx]; +} + // --- TEST_CASE("IPSpace properties", "[libswoc][ip][ex][properties]") { Table table; auto flag_names = { "prod"_tv, "dmz"_tv, "internal"_tv}; - table.add_column(std::make_unique<TagProperty>("owner")); - table.add_column(std::make_unique<TagProperty>("colo")); - table.add_column(std::make_unique<FlagGroupProperty>("flags"_tv, flag_names)); - table.add_column(std::make_unique<StringProperty>("Description")); + auto owner = table.add_column(std::make_unique<EnumProperty>("owner")); + auto colo = table.add_column(std::make_unique<EnumProperty>("colo")); + auto flags = table.add_column(std::make_unique<FlagGroupProperty>("flags"_tv, flag_names)); + auto description = table.add_column(std::make_unique<StringProperty>("Description")); TextView src = R"(10.1.1.0/24,asf,cmi,prod;internal,"ASF core net" 192.168.28.0/25,asf,ind,prod,"Indy Net" @@ -400,8 +468,12 @@ TEST_CASE("IPSpace properties", "[libswoc][ip][ex][properties]") { REQUIRE(3 == table.size()); auto row = table.find(IPAddr{"10.1.1.56"}); REQUIRE(nullptr != row); - CHECK(true == static_cast<FlagGroupProperty*>(table.column(2))->is_set(0, *row)); - CHECK(false == static_cast<FlagGroupProperty*>(table.column(2))->is_set(1, *row)); - CHECK(true == static_cast<FlagGroupProperty*>(table.column(2))->is_set(2, *row)); + CHECK(true == flags->is_set(*row, 0)); + CHECK(false == flags->is_set(*row, 1)); + CHECK(true == flags->is_set(*row, 2)); + + row = table.find(IPAddr{"192.168.28.131"}); + REQUIRE(row != nullptr); + CHECK("abq"_tv == (*colo)(*row)); };
