uri: store offsets rather than pointers

This simplifies copy and assignment.
This commit is contained in:
Danny Robson 2022-05-18 16:00:34 +10:00
parent 2d18241e61
commit dc46dc7c91
3 changed files with 92 additions and 86 deletions

110
uri.cpp
View File

@ -24,28 +24,20 @@ minimal_percent_encode (std::string &str)
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
cruft::uri::uri (std::string &&_value): cruft::uri::uri (std::string &&_value):
m_views { m_offsets {},
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
},
m_value (std::move (_value)) m_value (std::move (_value))
{ {
minimal_percent_encode (m_value); minimal_percent_encode (m_value);
parse (); parse ();
if (m_views[0].begin () == nullptr)
m_views[0] = { m_value.data (), 0 };
for (int i = 1; i < NUM_COMPONENTS; ++i) { for (int i = 1; i < NUM_COMPONENTS; ++i) {
if (m_views[i].data () != nullptr) if (m_offsets[i].second != m_offsets[i].first)
continue; continue;
m_views[i] = { m_views[i - 1].end (), 0 }; m_offsets[i] = {
m_offsets[i - 1].second,
m_offsets[i - 1].second
};
} }
CHECK_SANITY (*this); CHECK_SANITY (*this);
@ -70,31 +62,6 @@ uri::uri (const std::string &_value):
{ ; } { ; }
//-----------------------------------------------------------------------------
uri::uri (uri const &rhs)
: m_views (rhs.m_views)
, m_value (rhs.m_value)
{
auto const offset = rhs.m_value.data () - m_value.data ();
for (auto &i: m_views)
i -= offset;
CHECK_SANITY (*this);
}
//-----------------------------------------------------------------------------
uri& uri::operator= (uri &&rhs) noexcept
{
m_views = std::move (rhs.m_views);
m_value = std::move (rhs.m_value);
CHECK_SANITY (*this);
return *this;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static std::string static std::string
combine_components ( combine_components (
@ -155,9 +122,12 @@ std::string_view
uri::heirarchical (void) const& uri::heirarchical (void) const&
{ {
for (int i = USER; i <= PATH; ++i) for (int i = USER; i <= PATH; ++i)
if (!m_views[i].empty ()) if (m_offsets[i].first != m_offsets[i].second)
return { m_views[i].data (), std::size_t (m_views[PATH].end () - m_views[i].data ()) }; return {
return { m_views[USER].data (), 0 }; m_value.data () + m_offsets[ i].first,
m_value.data () + m_offsets[PATH].second
};
return { m_value.data () + m_offsets[USER].first, 0 };
} }
@ -166,9 +136,15 @@ std::string_view
uri::authority (void) const& uri::authority (void) const&
{ {
for (int i = USER; i <= PORT; ++i) for (int i = USER; i <= PORT; ++i)
if (!m_views[i].empty ()) if (m_offsets[i].first != m_offsets[i].second)
return { m_views[i].data (), std::size_t (m_views[PORT ].end () - m_views[i].data ()) }; return {
return { m_views[USER].data (), 0 }; m_value.data () + m_offsets[ i].first,
m_value.data () + m_offsets[PORT].second
};
return {
m_value.data () + m_offsets[USER].first, 0
};
} }
@ -177,7 +153,10 @@ std::string_view
uri::get (component c) const& uri::get (component c) const&
{ {
CHECK_INDEX (c, NUM_COMPONENTS); CHECK_INDEX (c, NUM_COMPONENTS);
return { m_views[c].data (), m_views[c].size () }; return {
m_value.data () + m_offsets[c].first,
m_value.data () + m_offsets[c].second,
};
} }
@ -185,21 +164,20 @@ uri::get (component c) const&
void void
uri::set (component c, std::string_view val) uri::set (component c, std::string_view val)
{ {
std::ptrdiff_t const diff = val.size () - m_views[c].size (); std::ptrdiff_t const diff = val.size () - (m_offsets[c].second - m_offsets[c].first);
auto const base = m_value.data ();
m_value.replace ( m_value.replace (
m_views[c].data () - m_value.data (), m_offsets[c].first,
m_views[c].size (), m_offsets[c].second - m_offsets[c].first,
val val
); );
auto const offset = m_value.data () - base;
for (int i = 0; i <= c; ++i) m_offsets[c].second = m_offsets[c].first + val.size ();
m_views[i] += offset;
m_views[c] = { m_views[c].begin (), m_views[c].end () + diff }; for (int i = c + 1; i != component::NUM_COMPONENTS; ++i) {
for (int i = c + 1; i != component::NUM_COMPONENTS; ++i) m_offsets[i].first += diff;
m_views[i] += offset + diff; m_offsets[i].second += diff;
}
CHECK_SANITY (*this); CHECK_SANITY (*this);
} }
@ -208,19 +186,19 @@ uri::set (component c, std::string_view val)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void uri::clear_fragment () void uri::clear_fragment ()
{ {
if (!m_views[FRAGMENT]) if (m_offsets[FRAGMENT].first == m_offsets[FRAGMENT].second)
return; return;
m_value.erase ( m_value.erase (
m_views[FRAGMENT].begin () - m_value.data (), m_offsets[FRAGMENT].first,
m_views[FRAGMENT].size () m_offsets[FRAGMENT].second - m_offsets[FRAGMENT].first
); );
CHECK (m_value.back () == '#'); CHECK (m_value.back () == '#');
m_value.pop_back (); m_value.pop_back ();
// Don't set this to nullptr. Other code assumes these views aren't null // Don't set this to nullptr. Other code assumes these views aren't null
// (eg, for offsetting during copy construction). // (eg, for offsetting during copy construction).
m_views[FRAGMENT] = { m_views[FRAGMENT - 1].end (), 0 }; m_offsets[FRAGMENT].first = m_offsets[FRAGMENT].second = m_offsets[FRAGMENT - 1].second;
} }
@ -267,6 +245,18 @@ cruft::vector_to_query (std::vector<std::pair<std::string, std::string>> const &
} }
///////////////////////////////////////////////////////////////////////////////
std::array<std::string_view, cruft::uri::NUM_COMPONENTS>
uri::components () const& noexcept
{
std::array<std::string_view, cruft::uri::NUM_COMPONENTS> res;
for (int i = 0; i != NUM_COMPONENTS; ++i)
res[i] = get (uri::component (i));
return res;
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
bool bool
cruft::operator== (cruft::uri const &a, cruft::uri const &b) noexcept cruft::operator== (cruft::uri const &a, cruft::uri const &b) noexcept

View File

@ -29,31 +29,31 @@ using cruft::uri;
action failure {__success = false; } action failure {__success = false; }
action scheme_begin { starts[SCHEME] = p; } action scheme_begin { starts[SCHEME] = p; }
action scheme_end { CHECK (starts[SCHEME]); m_views[SCHEME] = { starts[SCHEME], p }; } action scheme_end { record_component (SCHEME); }
action hier_begin { ; } action hier_begin { ; }
action hier_end { ; } action hier_end { ; }
action user_begin { starts[USER] = p; } action user_begin { starts[USER] = p; }
action user_end { CHECK (starts[USER]); m_views[USER] = { starts[USER], p }; } action user_end { record_component (USER); }
action host_begin { starts[HOST] = p; } action host_begin { starts[HOST] = p; }
action host_end { CHECK (starts[HOST]); m_views[HOST] = { starts[HOST], p }; } action host_end { record_component (HOST); }
action port_begin { starts[PORT] = p; } action port_begin { starts[PORT] = p; }
action port_end { CHECK (starts[PORT]); m_views[PORT] = { starts[PORT], p }; } action port_end { record_component (PORT); }
action authority_begin { ; } action authority_begin { ; }
action authority_end { ; } action authority_end { ; }
action path_begin { starts[PATH] = p; } action path_begin { starts[PATH] = p; }
action path_end { CHECK (starts[PATH]); m_views[PATH] = { starts[PATH], p }; } action path_end { record_component (PATH); }
action query_begin { starts[QUERY] = p; } action query_begin { starts[QUERY] = p; }
action query_end { CHECK (starts[QUERY]); m_views[QUERY] = { starts[QUERY], p }; } action query_end { record_component (QUERY); }
action fragment_begin { starts[FRAGMENT] = p; } action fragment_begin { starts[FRAGMENT] = p; }
action fragment_end { CHECK (starts[FRAGMENT]); m_views[FRAGMENT] = { starts[FRAGMENT], p }; } action fragment_end { record_component (FRAGMENT); }
action uri_begin {} action uri_begin {}
action uri_end {} action uri_end {}
@ -79,20 +79,22 @@ cruft::uri::parse (void)
{ {
char const *starts[NUM_COMPONENTS] = {}; char const *starts[NUM_COMPONENTS] = {};
m_views = { const char *str = m_value.data ();
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
};
const char *p = m_value.data (); const char *p = m_value.data ();
const char *pe = m_value.data () + m_value.size (); const char *pe = m_value.data () + m_value.size ();
const char *eof = pe; const char *eof = pe;
m_offsets = {};
auto record_component = [&] (int const idx) {
// CHECK (m_offsets[idx].first == 0);
// CHECK (m_offsets[idx].second == 0);
CHECK (starts[idx]);
m_offsets[idx].first = starts[idx] - str;
m_offsets[idx].second = p - str;
};
bool __success = false; bool __success = false;
int cs; int cs;
@ -100,6 +102,17 @@ cruft::uri::parse (void)
%%write init; %%write init;
%%write exec; %%write exec;
if constexpr (debug_enabled) {
if (!m_value.empty ())
CHECK (std::any_of (
m_offsets.begin (),
m_offsets.end (),
[] (auto const &i)
{
return !!i.second;
}));
}
if (!__success) if (!__success)
throw parse_error (fmt::format ("invalid uri '{}'", m_value)); throw parse_error (fmt::format ("invalid uri '{}'", m_value));
} }

21
uri.hpp
View File

@ -35,10 +35,12 @@ namespace cruft {
class uri { class uri {
public: public:
explicit uri (std::string &&); explicit uri (std::string &&);
uri& operator= (uri &&) noexcept;
uri (uri const&); uri (uri &&) noexcept = default;
uri& operator= (uri const&); uri& operator= (uri &&) noexcept = default;
uri (uri const&) = default;
uri& operator= (uri const&) = default;
explicit uri (const std::string&); explicit uri (const std::string&);
explicit uri (const char *); explicit uri (const char *);
@ -100,16 +102,16 @@ namespace cruft {
std::string_view pq (void) const& std::string_view pq (void) const&
{ {
return { return {
m_views[PATH].begin (), m_value.data () + m_offsets[PATH].first,
m_views[QUERY].end () m_value.data () + m_offsets[QUERY].second
}; };
} }
std::string_view std::string_view
pqf (void) const& { pqf (void) const& {
return { return {
m_views[PATH].begin (), m_value.data () + m_offsets[PATH].first,
m_views[FRAGMENT].end () m_value.data () + m_offsets[FRAGMENT].second
}; };
} }
@ -118,14 +120,15 @@ namespace cruft {
// void clear (component); // void clear (component);
void clear_fragment (void); void clear_fragment (void);
auto components (void) const& noexcept { return m_views; } std::array<std::string_view, NUM_COMPONENTS>
components (void) const& noexcept;
static std::string percent_decode (view<const char*>); static std::string percent_decode (view<const char*>);
private: private:
void parse (void); void parse (void);
std::array<view<const char*>, NUM_COMPONENTS> m_views; std::array<std::pair<int, int>, NUM_COMPONENTS> m_offsets;
std::string m_value; std::string m_value;
}; };