From 5c4e0ffd9768de0c51a42baa35d9c29636fdd99a Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Wed, 4 Nov 2020 16:08:01 -0800 Subject: [PATCH] [subset] Add a basic priority queue datastructure (binary heap). --- src/Makefile.am | 6 +- src/Makefile.sources | 1 + src/hb-priority-queue.hh | 149 +++++++++++++++++++++++++++++++++++++ src/test-priority-queue.cc | 89 ++++++++++++++++++++++ 4 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 src/hb-priority-queue.hh create mode 100644 src/test-priority-queue.cc diff --git a/src/Makefile.am b/src/Makefile.am index 20e9be32f..7a0ca2985 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -342,7 +342,7 @@ test_gsub_would_substitute_SOURCES = test-gsub-would-substitute.cc test_gsub_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS) test_gsub_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS) -COMPILED_TESTS = test-algs test-array test-iter test-meta test-number test-ot-tag test-unicode-ranges test-bimap test-repacker +COMPILED_TESTS = test-algs test-array test-iter test-meta test-number test-ot-tag test-priority-queue test-unicode-ranges test-bimap test-repacker COMPILED_TESTS_CPPFLAGS = $(HBCFLAGS) -DMAIN -UNDEBUG COMPILED_TESTS_LDADD = libharfbuzz.la $(HBLIBS) check_PROGRAMS += $(COMPILED_TESTS) @@ -356,6 +356,10 @@ test_array_SOURCES = test-array.cc test_array_CPPFLAGS = $(HBCFLAGS) test_array_LDADD = libharfbuzz.la $(HBLIBS) +test_priority_queue_SOURCES = test-priority-queue.cc hb-static.cc +test_priority_queue_CPPFLAGS = $(HBCFLAGS) +test_priority_queue_LDADD = libharfbuzz.la $(HBLIBS) + test_repacker_SOURCES = test-repacker.cc hb-static.cc test_repacker_CPPFLAGS = $(HBCFLAGS) test_repacker_LDADD = libharfbuzz.la libharfbuzz-subset.la $(HBLIBS) diff --git a/src/Makefile.sources b/src/Makefile.sources index 933ae8850..da97530fe 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -167,6 +167,7 @@ HB_BASE_sources = \ hb-unicode.hh \ hb-utf.hh \ hb-vector.hh \ + hb-priority-queue.hh \ hb.hh \ $(NULL) diff --git a/src/hb-priority-queue.hh b/src/hb-priority-queue.hh new file mode 100644 index 000000000..6fd1a8d0b --- /dev/null +++ b/src/hb-priority-queue.hh @@ -0,0 +1,149 @@ +/* + * Copyright © 2020 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#ifndef HB_PRIORITY_QUEUE_HH +#define HB_PRIORITY_QUEUE_HH + +#include "hb.hh" +#include "hb-vector.hh" + +/* + * hb_priority_queue_t + * + * Priority queue implemented as a binary heap. Supports extract minimum + * and insert operations. + */ +struct hb_priority_queue_t +{ + HB_DELETE_COPY_ASSIGN (hb_priority_queue_t); + hb_priority_queue_t () { init (); } + ~hb_priority_queue_t () { fini (); } + + private: + typedef hb_pair_t item_t; + hb_vector_t heap; + + public: + void init () { heap.init (); } + + void fini () { heap.fini (); } + + void reset () { heap.resize (0); } + + bool in_error () const { return heap.in_error (); } + + void insert (unsigned value, unsigned priority) + { + heap.push (item_t (value, priority)); + bubble_up (heap.length - 1); + } + + item_t extract_minimum () + { + item_t result = heap[0]; + + heap[0] = heap[heap.length - 1]; + heap.shrink (heap.length - 1); + bubble_down (0); + + return result; + } + + const item_t& minimum () + { + return heap[0]; + } + + bool is_empty () const { return heap.length == 0; } + + /* Sink interface. */ + hb_priority_queue_t& operator << (item_t item) + { insert (item.first, item.second); return *this; } + + private: + + unsigned parent (unsigned index) + { + return (index - 1) / 2; + } + + unsigned left_child (unsigned index) + { + return 2 * index + 1; + } + + unsigned right_child (unsigned index) + { + return 2 * index + 2; + } + + void bubble_down (unsigned index) + { + unsigned left = left_child (index); + unsigned right = right_child (index); + + bool has_left = left < heap.length; + if (!has_left) + // If there's no left, then there's also no right. + return; + + bool has_right = right < heap.length; + if (heap[index] <= heap[left] + && (!has_right || heap[index] <= heap[right])) + return; + + if (!has_right || heap[left] < heap[right]) + { + swap (index, left); + bubble_down (left); + return; + } + + swap (index, right); + bubble_down (right); + } + + void bubble_up (unsigned index) + { + if (index == 0) return; + + unsigned parent_index = parent (index); + if (heap[parent_index].second <= heap[index].second) + return; + + swap (index, parent_index); + bubble_up (parent_index); + } + + void swap (unsigned a, unsigned b) + { + item_t temp = heap[a]; + heap[a] = heap[b]; + heap[b] = temp; + } +}; + +#endif /* HB_PRIORITY_QUEUE_HH */ diff --git a/src/test-priority-queue.cc b/src/test-priority-queue.cc new file mode 100644 index 000000000..65c2a320b --- /dev/null +++ b/src/test-priority-queue.cc @@ -0,0 +1,89 @@ +/* + * Copyright © 2020 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#include "hb.hh" +#include "hb-priority-queue.hh" + +static void +test_insert () +{ + hb_priority_queue_t queue; + assert (queue.is_empty ()); + + queue.insert (0, 10); + assert (!queue.is_empty ()); + assert (queue.minimum () == hb_pair (0, 10)); + + queue.insert (1, 20); + assert (queue.minimum () == hb_pair (0, 10)); + + queue.insert (2, 5); + assert (queue.minimum () == hb_pair (2, 5)); + + queue.insert (3, 15); + assert (queue.minimum () == hb_pair (2, 5)); + + queue.insert (4, 1); + assert (queue.minimum () == hb_pair (4, 1)); +} + +static void +test_extract () +{ + hb_priority_queue_t queue; + queue.insert (0, 0); + queue.insert (6, 60); + queue.insert (3, 30); + queue.insert (4, 40); + queue.insert (2, 20); + queue.insert (5, 50); + queue.insert (7, 70); + queue.insert (1, 10); + + for (int i = 0; i < 8; i++) + { + assert (!queue.is_empty ()); + assert (queue.minimum () == hb_pair (i, i * 10)); + assert (queue.extract_minimum () == hb_pair (i, i * 10)); + } + + assert (queue.is_empty ()); +} + +static void +test_extract_empty () +{ + hb_priority_queue_t queue; + assert (queue.extract_minimum () == hb_pair (0, 0)); +} + +int +main (int argc, char **argv) +{ + test_insert (); + test_extract (); + test_extract_empty (); +}