Commit 79692251 authored by Karandeep Bhatia's avatar Karandeep Bhatia Committed by Commit Bot

Extensions: Binary search to find generated schema.

This will ensure that finding the generated schemas is a
log(num_schemas) * |number_of_api_providers| operation. We can then also
remove the caching in ExtensionAPI, which should save memory for
renderers. Note that since for the browser process, GetSchema is never
called, ExtensionAPI::IsKnownAPI was a linear (in the number of schemas)
operation (since |schemas_| is always empty). Now it'd be more
efficient.

This depends on the fact that our code generating logic generates
schemas in sorted order. Add a static assert to ensure that it holds.
Since std::is_sorted is not constexpr until c++20, modify
base::STLIsSorted to be constexpr (at least for arrays which have
constexpr std::begin and std::end since c++14).

Example generated code: https://paste.googleplex.com/6322126191591424.

BUG=None

Change-Id: Ib463c7f8ca5f317ecf6900dc811fa902c72e92d8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2291265
Commit-Queue: Karan Bhatia <karandeepb@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#788913}
parent cad4c87e
......@@ -447,10 +447,22 @@ typename Map::iterator TryEmplace(Map& map,
std::forward<Args>(args)...);
}
// Returns true if the container is sorted.
// Returns true if the container is sorted. Requires constexpr std::begin/end,
// which exists for arrays in C++14.
// Note that std::is_sorted is constexpr beginning C++20 and this should be
// switched to use it when C++20 is supported.
template <typename Container>
bool STLIsSorted(const Container& cont) {
return std::is_sorted(std::begin(cont), std::end(cont));
constexpr bool STLIsSorted(const Container& cont) {
auto it = std::begin(cont);
const auto end = std::end(cont);
if (it == end)
return true;
for (auto prev = it++; it != end; prev = it++) {
if (*it < *prev)
return false;
}
return true;
}
// Returns a new ResultType containing the difference of two sorted containers.
......
......@@ -808,5 +808,16 @@ TEST(STLUtilTest, OptionalOrNullptr) {
EXPECT_NE(nullptr, base::OptionalOrNullptr(optional));
}
TEST(STLUtilTest, STLIsSortedConstexpr) {
constexpr int kArrayAscending[] = {1, 2, 3, 4};
static_assert(base::STLIsSorted(kArrayAscending), "");
constexpr int kArrayDescending[] = {4, 3, 2, 1};
static_assert(!base::STLIsSorted(kArrayDescending), "");
constexpr int kArrayEqual[] = {1, 1, 1, 1};
static_assert(base::STLIsSorted(kArrayEqual), "");
}
} // namespace
} // namespace base
......@@ -224,18 +224,12 @@ Feature::Availability ExtensionAPI::IsAvailable(const std::string& full_name,
base::StringPiece ExtensionAPI::GetSchemaStringPiece(
const std::string& api_name) {
DCHECK_EQ(api_name, GetAPINameFromFullName(api_name, nullptr));
auto cached = schema_strings_.find(api_name);
if (cached != schema_strings_.end())
return cached->second;
ExtensionsClient* client = ExtensionsClient::Get();
DCHECK(client);
if (!default_configuration_initialized_)
return base::StringPiece();
base::StringPiece schema = client->GetAPISchema(api_name);
if (!schema.empty())
schema_strings_[api_name] = schema;
return schema;
}
......
......@@ -172,9 +172,6 @@ class ExtensionAPI {
std::map<std::string, std::unique_ptr<const base::DictionaryValue>>;
SchemaMap schemas_;
using StringPieceMap = std::map<std::string, base::StringPiece>;
StringPieceMap schema_strings_;
// FeatureProviders used for resolving dependencies.
typedef std::map<std::string, const FeatureProvider*> FeatureProviderMap;
FeatureProviderMap dependency_providers_;
......
......@@ -334,6 +334,11 @@ class _SchemasCCGenerator(object):
c.Append('#include "%s"' % (os.path.join(self._bundle._source_file_dir,
'generated_schemas.h')))
c.Append()
c.Append('#include <algorithm>')
c.Append('#include <iterator>')
c.Append()
c.Append('#include "base/stl_util.h"')
c.Append()
c.Append('namespace {')
for api in self._bundle._api_defs:
namespace = self._bundle._model.namespaces[api.get('namespace')]
......@@ -348,8 +353,9 @@ class _SchemasCCGenerator(object):
json_content[i:i + max_length]
for i in range(0, len(json_content), max_length)
]
c.Append('const char %s[] = R"R(%s)R";' % (_FormatNameAsConstant(
namespace.name), ')R" R"R('.join(segments)))
c.Append(
'constexpr char %s[] = R"R(%s)R";' %
(_FormatNameAsConstant(namespace.name), ')R" R"R('.join(segments)))
c.Append('} // namespace')
c.Append()
c.Concat(cpp_util.OpenNamespace(self._bundle._cpp_namespace))
......@@ -363,9 +369,13 @@ class _SchemasCCGenerator(object):
c.Append('// static')
c.Sblock('base::StringPiece %s::Get(base::StringPiece name) {' %
self._bundle._GenerateBundleClass('GeneratedSchemas'))
c.Append('static const struct {')
c.Append(' base::StringPiece name;')
c.Append(' base::StringPiece schema;')
c.Sblock('static constexpr struct kSchemaMapping {')
c.Append('const base::StringPiece name;')
c.Append('const base::StringPiece schema;')
c.Sblock('constexpr bool operator<(const kSchemaMapping& that) const {')
c.Append('return name < that.name;')
c.Eblock('}')
c.Eblock()
c.Sblock('} kSchemas[] = {')
namespaces = [self._bundle._model.namespaces[api.get('namespace')].name
for api in self._bundle._api_defs]
......@@ -373,11 +383,18 @@ class _SchemasCCGenerator(object):
schema_constant_name = _FormatNameAsConstant(namespace)
c.Append('{"%s", %s},' % (namespace, schema_constant_name))
c.Eblock('};')
c.Sblock('for (const auto& schema : kSchemas) {')
c.Sblock('if (schema.name == name)')
c.Append('return schema.schema;')
c.Append('static_assert(base::STLIsSorted(kSchemas), "|kSchemas| should be '
'sorted.");')
c.Sblock('auto it = std::lower_bound(std::begin(kSchemas), '
'std::end(kSchemas),')
c.Append('kSchemaMapping{name, base::StringPiece()});')
c.Eblock()
c.Eblock('}')
c.Sblock('if (it != std::end(kSchemas) && it->name == name)')
c.Append('return it->schema;')
c.Eblock()
c.Append('return base::StringPiece();')
c.Eblock('}')
c.Append()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment