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):
m_views {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
},
m_offsets {},
m_value (std::move (_value))
{
minimal_percent_encode (m_value);
parse ();
if (m_views[0].begin () == nullptr)
m_views[0] = { m_value.data (), 0 };
for (int i = 1; i < NUM_COMPONENTS; ++i) {
if (m_views[i].data () != nullptr)
if (m_offsets[i].second != m_offsets[i].first)
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);
@ -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
combine_components (
@ -155,9 +122,12 @@ std::string_view
uri::heirarchical (void) const&
{
for (int i = USER; i <= PATH; ++i)
if (!m_views[i].empty ())
return { m_views[i].data (), std::size_t (m_views[PATH].end () - m_views[i].data ()) };
return { m_views[USER].data (), 0 };
if (m_offsets[i].first != m_offsets[i].second)
return {
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&
{
for (int i = USER; i <= PORT; ++i)
if (!m_views[i].empty ())
return { m_views[i].data (), std::size_t (m_views[PORT ].end () - m_views[i].data ()) };
return { m_views[USER].data (), 0 };
if (m_offsets[i].first != m_offsets[i].second)
return {
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&
{
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
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_views[c].data () - m_value.data (),
m_views[c].size (),
m_offsets[c].first,
m_offsets[c].second - m_offsets[c].first,
val
);
auto const offset = m_value.data () - base;
for (int i = 0; i <= c; ++i)
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)
m_views[i] += offset + diff;
m_offsets[c].second = m_offsets[c].first + val.size ();
for (int i = c + 1; i != component::NUM_COMPONENTS; ++i) {
m_offsets[i].first += diff;
m_offsets[i].second += diff;
}
CHECK_SANITY (*this);
}
@ -208,19 +186,19 @@ uri::set (component c, std::string_view val)
//-----------------------------------------------------------------------------
void uri::clear_fragment ()
{
if (!m_views[FRAGMENT])
if (m_offsets[FRAGMENT].first == m_offsets[FRAGMENT].second)
return;
m_value.erase (
m_views[FRAGMENT].begin () - m_value.data (),
m_views[FRAGMENT].size ()
m_offsets[FRAGMENT].first,
m_offsets[FRAGMENT].second - m_offsets[FRAGMENT].first
);
CHECK (m_value.back () == '#');
m_value.pop_back ();
// Don't set this to nullptr. Other code assumes these views aren't null
// (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
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 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_end { ; }
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_end { CHECK (starts[HOST]); m_views[HOST] = { starts[HOST], p }; }
action host_end { record_component (HOST); }
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_end { ; }
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_end { CHECK (starts[QUERY]); m_views[QUERY] = { starts[QUERY], p }; }
action query_end { record_component (QUERY); }
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_end {}
@ -79,20 +79,22 @@ cruft::uri::parse (void)
{
char const *starts[NUM_COMPONENTS] = {};
m_views = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
};
const char *str = m_value.data ();
const char *p = m_value.data ();
const char *pe = m_value.data () + m_value.size ();
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;
int cs;
@ -100,6 +102,17 @@ cruft::uri::parse (void)
%%write init;
%%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)
throw parse_error (fmt::format ("invalid uri '{}'", m_value));
}

21
uri.hpp
View File

@ -35,10 +35,12 @@ namespace cruft {
class uri {
public:
explicit uri (std::string &&);
uri& operator= (uri &&) noexcept;
uri (uri const&);
uri& operator= (uri const&);
uri (uri &&) noexcept = default;
uri& operator= (uri &&) noexcept = default;
uri (uri const&) = default;
uri& operator= (uri const&) = default;
explicit uri (const std::string&);
explicit uri (const char *);
@ -100,16 +102,16 @@ namespace cruft {
std::string_view pq (void) const&
{
return {
m_views[PATH].begin (),
m_views[QUERY].end ()
m_value.data () + m_offsets[PATH].first,
m_value.data () + m_offsets[QUERY].second
};
}
std::string_view
pqf (void) const& {
return {
m_views[PATH].begin (),
m_views[FRAGMENT].end ()
m_value.data () + m_offsets[PATH].first,
m_value.data () + m_offsets[FRAGMENT].second
};
}
@ -118,14 +120,15 @@ namespace cruft {
// void clear (component);
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*>);
private:
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;
};