mirror of
https://github.com/harfbuzz/harfbuzz.git
synced 2025-04-13 00:32:59 +00:00
[repacker] in classdef estimator tests compare results to actual class def serialization.
Fix the estimator to actually match real serialization sizes.
This commit is contained in:
parent
8129b21dea
commit
0ac9e7da39
2 changed files with 142 additions and 73 deletions
|
@ -135,7 +135,13 @@ struct ClassDef : public OT::ClassDef
|
|||
|
||||
struct class_def_size_estimator_t
|
||||
{
|
||||
// TODO(garretrieger): for coverage take the same approach as class def (compute the running total for each format).
|
||||
// TODO(garretrieger): update to support beyond64k coverage/classdef tables.
|
||||
constexpr static unsigned class_def_format1_base_size = 6;
|
||||
constexpr static unsigned class_def_format2_base_size = 4;
|
||||
constexpr static unsigned coverage_base_size = 4;
|
||||
constexpr static unsigned bytes_per_range = 6;
|
||||
constexpr static unsigned bytes_per_glyph = 2;
|
||||
|
||||
template<typename It>
|
||||
class_def_size_estimator_t (It glyph_and_class)
|
||||
: num_ranges_per_class (), glyphs_per_class ()
|
||||
|
@ -176,8 +182,8 @@ struct class_def_size_estimator_t
|
|||
}
|
||||
|
||||
void reset() {
|
||||
class_def_1_size = 4;
|
||||
class_def_2_size = 4;
|
||||
class_def_1_size = class_def_format1_base_size;
|
||||
class_def_2_size = class_def_format2_base_size;
|
||||
included_glyphs.clear();
|
||||
included_classes.clear();
|
||||
}
|
||||
|
@ -185,10 +191,8 @@ struct class_def_size_estimator_t
|
|||
// Compute the size of coverage for all glyphs added via 'add_class_def_size'.
|
||||
unsigned coverage_size () const
|
||||
{
|
||||
// format 1 is 2 bytes per glyph.
|
||||
unsigned format1_size = 4 + 2 * included_glyphs.get_population();
|
||||
// format 2 is 6 bytes per range.
|
||||
unsigned format2_size = 4 + 6 * num_glyph_ranges();
|
||||
unsigned format1_size = coverage_base_size + bytes_per_glyph * included_glyphs.get_population();
|
||||
unsigned format2_size = coverage_base_size + bytes_per_range * num_glyph_ranges();
|
||||
return hb_min(format1_size, format2_size);
|
||||
}
|
||||
|
||||
|
@ -196,23 +200,23 @@ struct class_def_size_estimator_t
|
|||
unsigned add_class_def_size (unsigned klass)
|
||||
{
|
||||
if (!included_classes.has(klass)) {
|
||||
// ClassDef 1 takes 2 bytes per glyph.
|
||||
class_def_1_size += 2 * glyphs_per_class.get (klass).get_population ();
|
||||
// ClassDef 2 takes 6 bytes per range.
|
||||
class_def_2_size += 6 * num_ranges_per_class.get (klass);
|
||||
|
||||
hb_set_t* glyphs = nullptr;
|
||||
if (glyphs_per_class.has(klass, &glyphs)) {
|
||||
included_glyphs.union_(*glyphs);
|
||||
}
|
||||
|
||||
class_def_1_size = class_def_format1_base_size;
|
||||
if (!included_glyphs.is_empty()) {
|
||||
unsigned min_glyph = included_glyphs.get_min();
|
||||
unsigned max_glyph = included_glyphs.get_max();
|
||||
class_def_1_size += bytes_per_glyph * (max_glyph - min_glyph + 1);
|
||||
}
|
||||
|
||||
class_def_2_size += bytes_per_range * num_ranges_per_class.get (klass);
|
||||
|
||||
included_classes.add(klass);
|
||||
}
|
||||
|
||||
if (!gids_consecutive())
|
||||
return class_def_2_size;
|
||||
|
||||
// ClassDef1 can only be used when gids are consecutive.
|
||||
return hb_min (class_def_1_size, class_def_2_size);
|
||||
}
|
||||
|
||||
|
@ -227,18 +231,6 @@ struct class_def_size_estimator_t
|
|||
return count;
|
||||
}
|
||||
|
||||
bool gids_consecutive() const {
|
||||
hb_codepoint_t start = HB_SET_VALUE_INVALID;
|
||||
hb_codepoint_t end = HB_SET_VALUE_INVALID;
|
||||
|
||||
unsigned count = 0;
|
||||
while (included_glyphs.next_range (&start, &end)) {
|
||||
count++;
|
||||
if (count > 1) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool in_error ()
|
||||
{
|
||||
if (num_ranges_per_class.in_error ()) return true;
|
||||
|
|
|
@ -26,27 +26,113 @@
|
|||
|
||||
#include "gsubgpos-context.hh"
|
||||
#include "classdef-graph.hh"
|
||||
#include "hb-iter.hh"
|
||||
#include "hb-serialize.hh"
|
||||
|
||||
typedef hb_codepoint_pair_t gid_and_class_t;
|
||||
typedef hb_vector_t<gid_and_class_t> gid_and_class_list_t;
|
||||
|
||||
template<typename It>
|
||||
static unsigned actual_class_def_size(It glyph_and_class) {
|
||||
char buffer[100];
|
||||
hb_serialize_context_t serializer(buffer, 100);
|
||||
OT::ClassDef_serialize (&serializer, glyph_and_class);
|
||||
serializer.end_serialize ();
|
||||
assert(!serializer.in_error());
|
||||
hb_bytes_t class_def_copy = serializer.copy_bytes ();
|
||||
return class_def_copy.get_size();
|
||||
}
|
||||
|
||||
static bool incremental_size_is (const gid_and_class_list_t& list, unsigned klass,
|
||||
unsigned cov_expected, unsigned class_def_expected)
|
||||
static unsigned actual_class_def_size(gid_and_class_list_t consecutive_map, hb_vector_t<unsigned> classes) {
|
||||
auto filtered_it =
|
||||
+ consecutive_map.as_sorted_array().iter()
|
||||
| hb_filter([&] (unsigned c) {
|
||||
for (unsigned klass : classes) {
|
||||
if (c == klass) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}, hb_second);
|
||||
return actual_class_def_size(+ filtered_it);
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
static unsigned actual_coverage_size(It glyphs) {
|
||||
char buffer[100];
|
||||
hb_serialize_context_t serializer(buffer, 100);
|
||||
OT::Layout::Common::Coverage_serialize (&serializer, glyphs);
|
||||
serializer.end_serialize ();
|
||||
assert(!serializer.in_error());
|
||||
hb_bytes_t coverage_copy = serializer.copy_bytes ();
|
||||
return coverage_copy.get_size();
|
||||
}
|
||||
|
||||
static unsigned actual_coverage_size(gid_and_class_list_t consecutive_map, hb_vector_t<unsigned> classes) {
|
||||
auto filtered_it =
|
||||
+ consecutive_map.as_sorted_array().iter()
|
||||
| hb_filter([&] (unsigned c) {
|
||||
for (unsigned klass : classes) {
|
||||
if (c == klass) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}, hb_second);
|
||||
return actual_coverage_size(+ filtered_it | hb_map_retains_sorting(hb_first));
|
||||
}
|
||||
|
||||
static bool check_coverage_size(graph::class_def_size_estimator_t& estimator,
|
||||
const gid_and_class_list_t& map,
|
||||
hb_vector_t<unsigned> klasses)
|
||||
{
|
||||
unsigned result = estimator.coverage_size();
|
||||
unsigned expected = actual_coverage_size(map, klasses);
|
||||
if (result != expected) {
|
||||
printf ("FAIL: estimated coverage expected size %u but was %u\n", expected, result);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool check_add_class_def_size(graph::class_def_size_estimator_t& estimator,
|
||||
const gid_and_class_list_t& map,
|
||||
unsigned klass, hb_vector_t<unsigned> klasses)
|
||||
{
|
||||
unsigned result = estimator.add_class_def_size(klass);
|
||||
unsigned expected = actual_class_def_size(map, klasses);
|
||||
if (result != expected) {
|
||||
printf ("FAIL: estimated class def expected size %u but was %u\n", expected, result);
|
||||
return false;
|
||||
}
|
||||
|
||||
return check_coverage_size(estimator, map, klasses);
|
||||
}
|
||||
|
||||
static bool check_add_class_def_size (const gid_and_class_list_t& list, unsigned klass)
|
||||
{
|
||||
graph::class_def_size_estimator_t estimator (list.iter ());
|
||||
|
||||
unsigned result = estimator.add_class_def_size (klass);
|
||||
if (result != class_def_expected)
|
||||
auto filtered_it =
|
||||
+ list.as_sorted_array().iter()
|
||||
| hb_filter([&] (unsigned c) {
|
||||
return c == klass;
|
||||
}, hb_second);
|
||||
|
||||
unsigned expected = actual_class_def_size(filtered_it);
|
||||
if (result != expected)
|
||||
{
|
||||
printf ("FAIL: class def expected size %u but was %u\n", class_def_expected, result);
|
||||
printf ("FAIL: class def expected size %u but was %u\n", expected, result);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto cov_it = + filtered_it | hb_map_retains_sorting(hb_first);
|
||||
result = estimator.coverage_size ();
|
||||
if (result != cov_expected)
|
||||
expected = actual_coverage_size(cov_it);
|
||||
if (result != expected)
|
||||
{
|
||||
printf ("FAIL: coverage expected size %u but was %u\n", cov_expected, result);
|
||||
printf ("FAIL: coverage expected size %u but was %u\n", expected, result);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -55,30 +141,31 @@ static bool incremental_size_is (const gid_and_class_list_t& list, unsigned klas
|
|||
|
||||
static void test_class_and_coverage_size_estimates ()
|
||||
{
|
||||
// TODO(garretrieger): test against the actual serialized sizes of class def tables
|
||||
gid_and_class_list_t empty = {
|
||||
};
|
||||
assert (incremental_size_is (empty, 0, 4, 4));
|
||||
assert (incremental_size_is (empty, 1, 4, 4));
|
||||
assert (check_add_class_def_size (empty, 0));
|
||||
assert (check_add_class_def_size (empty, 1));
|
||||
|
||||
gid_and_class_list_t class_zero = {
|
||||
{5, 0},
|
||||
};
|
||||
assert (incremental_size_is (class_zero, 0, 6, 4));
|
||||
assert (check_add_class_def_size (class_zero, 0));
|
||||
|
||||
gid_and_class_list_t consecutive = {
|
||||
{4, 0},
|
||||
{5, 0},
|
||||
|
||||
{6, 1},
|
||||
{7, 1},
|
||||
|
||||
{8, 2},
|
||||
{9, 2},
|
||||
{10, 2},
|
||||
{11, 2},
|
||||
};
|
||||
assert (incremental_size_is (consecutive, 0, 8, 4));
|
||||
assert (incremental_size_is (consecutive, 1, 8, 8));
|
||||
assert (incremental_size_is (consecutive, 2, 10, 10));
|
||||
assert (check_add_class_def_size (consecutive, 0));
|
||||
assert (check_add_class_def_size (consecutive, 1));
|
||||
assert (check_add_class_def_size (consecutive, 2));
|
||||
|
||||
gid_and_class_list_t non_consecutive = {
|
||||
{4, 0},
|
||||
|
@ -92,9 +179,9 @@ static void test_class_and_coverage_size_estimates ()
|
|||
{11, 2},
|
||||
{13, 2},
|
||||
};
|
||||
assert (incremental_size_is (non_consecutive, 0, 8, 4));
|
||||
assert (incremental_size_is (non_consecutive, 1, 8, 4 + 2*6));
|
||||
assert (incremental_size_is (non_consecutive, 2, 12, 4 + 2*6));
|
||||
assert (check_add_class_def_size (non_consecutive, 0));
|
||||
assert (check_add_class_def_size (non_consecutive, 1));
|
||||
assert (check_add_class_def_size (non_consecutive, 2));
|
||||
|
||||
gid_and_class_list_t multiple_ranges = {
|
||||
{4, 0},
|
||||
|
@ -109,8 +196,8 @@ static void test_class_and_coverage_size_estimates ()
|
|||
{12, 1},
|
||||
{13, 1},
|
||||
};
|
||||
assert (incremental_size_is (multiple_ranges, 0, 8, 4));
|
||||
assert (incremental_size_is (multiple_ranges, 1, 4 + 2 * 6, 4 + 3 * 6));
|
||||
assert (check_add_class_def_size (multiple_ranges, 0));
|
||||
assert (check_add_class_def_size (multiple_ranges, 1));
|
||||
}
|
||||
|
||||
static void test_running_class_and_coverage_size_estimates () {
|
||||
|
@ -136,18 +223,13 @@ static void test_running_class_and_coverage_size_estimates () {
|
|||
};
|
||||
|
||||
graph::class_def_size_estimator_t estimator1(consecutive_map.iter());
|
||||
assert(estimator1.add_class_def_size(1) == 4 + 6); // format 2, 1 range
|
||||
assert(estimator1.coverage_size() == 4 + 6); // format 2, 1 range
|
||||
assert(estimator1.add_class_def_size(2) == 4 + 10); // format 1, 5 glyphs
|
||||
assert(estimator1.coverage_size() == 4 + 6); // format 2, 1 range
|
||||
assert(estimator1.add_class_def_size(3) == 4 + 18); // format 2, 3 ranges
|
||||
assert(estimator1.coverage_size() == 4 + 6); // format 2, 1 range
|
||||
assert(check_add_class_def_size(estimator1, consecutive_map, 1, {1}));
|
||||
assert(check_add_class_def_size(estimator1, consecutive_map, 2, {1, 2}));
|
||||
assert(check_add_class_def_size(estimator1, consecutive_map, 3, {1, 2, 3}));
|
||||
|
||||
estimator1.reset();
|
||||
assert(estimator1.add_class_def_size(2) == 4 + 2); // format 1, 1 glyph
|
||||
assert(estimator1.coverage_size() == 4 + 2); // format 1, 1 glyph
|
||||
assert(estimator1.add_class_def_size(3) == 4 + 12); // format 2, 2 ranges
|
||||
assert(estimator1.coverage_size() == 4 + 6); // format 1, 1 range
|
||||
assert(check_add_class_def_size(estimator1, consecutive_map, 2, {2}));
|
||||
assert(check_add_class_def_size(estimator1, consecutive_map, 3, {2, 3}));
|
||||
|
||||
// #### With non-consecutive gids: always uses format 2 ###
|
||||
gid_and_class_list_t non_consecutive_map = {
|
||||
|
@ -172,35 +254,30 @@ static void test_running_class_and_coverage_size_estimates () {
|
|||
};
|
||||
|
||||
graph::class_def_size_estimator_t estimator2(non_consecutive_map.iter());
|
||||
assert(estimator2.add_class_def_size(1) == 4 + 6); // format 2, 1 range
|
||||
assert(estimator2.coverage_size() == 4 + 6); // format 2, 1 range
|
||||
assert(estimator2.add_class_def_size(2) == 4 + 18); // format 2, 3 ranges
|
||||
assert(estimator2.coverage_size() == 4 + 2 * 6); // format 1, 6 glyphs
|
||||
assert(estimator2.add_class_def_size(3) == 4 + 24); // format 2, 4 ranges
|
||||
assert(estimator2.coverage_size() == 4 + 3 * 6); // format 2, 3 ranges
|
||||
assert(check_add_class_def_size(estimator2, non_consecutive_map, 1, {1}));
|
||||
assert(check_add_class_def_size(estimator2, non_consecutive_map, 2, {1, 2}));
|
||||
assert(check_add_class_def_size(estimator2, non_consecutive_map, 3, {1, 2, 3}));
|
||||
|
||||
estimator2.reset();
|
||||
assert(estimator2.add_class_def_size(2) == 4 + 12); // format 1, 1 range
|
||||
assert(estimator2.coverage_size() == 4 + 4); // format 1, 2 glyphs
|
||||
assert(estimator2.add_class_def_size(3) == 4 + 18); // format 2, 2 ranges
|
||||
assert(estimator2.coverage_size() == 4 + 2 * 6); // format 2, 2 ranges
|
||||
assert(check_add_class_def_size(estimator2, non_consecutive_map, 2, {2}));
|
||||
assert(check_add_class_def_size(estimator2, non_consecutive_map, 3, {2, 3}));
|
||||
}
|
||||
|
||||
static void test_running_class_size_estimates_with_locally_consecutive_glyphs () {
|
||||
gid_and_class_list_t consecutive_map = {
|
||||
gid_and_class_list_t map = {
|
||||
{1, 1},
|
||||
{6, 2},
|
||||
{7, 3},
|
||||
};
|
||||
|
||||
graph::class_def_size_estimator_t estimator(consecutive_map.iter());
|
||||
assert(estimator.add_class_def_size(1) == 4 + 2); // format 1, 1 glyph
|
||||
assert(estimator.add_class_def_size(2) == 4 + 12); // format 2, 2 ranges
|
||||
assert(estimator.add_class_def_size(3) == 4 + 18); // format 2, 3 ranges
|
||||
graph::class_def_size_estimator_t estimator(map.iter());
|
||||
assert(check_add_class_def_size(estimator, map, 1, {1}));
|
||||
assert(check_add_class_def_size(estimator, map, 2, {1, 2}));
|
||||
assert(check_add_class_def_size(estimator, map, 3, {1, 2, 3}));
|
||||
|
||||
estimator.reset();
|
||||
assert(estimator.add_class_def_size(2) == 4 + 2); // format 1, 1 glyphs
|
||||
assert(estimator.add_class_def_size(3) == 4 + 4); // format 1, 2 glyphs
|
||||
assert(check_add_class_def_size(estimator, map, 2, {2}));
|
||||
assert(check_add_class_def_size(estimator, map, 3, {2, 3}));
|
||||
}
|
||||
|
||||
int
|
||||
|
|
Loading…
Add table
Reference in a new issue