diff --git a/bleach/html5lib_shim.py b/bleach/html5lib_shim.py
index d97d5c4..3ebfc8a 100644
--- a/bleach/html5lib_shim.py
+++ b/bleach/html5lib_shim.py
@@ -512,13 +512,13 @@ def convert_entities(text):
 def match_entity(stream):
     """Returns first entity in stream or None if no entity exists
 
-    Note: For Bleach purposes, entities must start with a "&" and end with
-    a ";". This ignoresambiguous character entities that have no ";" at the
-    end.
+    Note: For Bleach purposes, entities must start with a "&" and end with a
+    ";". This ignores ambiguous character entities that have no ";" at the end.
 
     :arg stream: the character stream
 
-    :returns: ``None`` or the entity string without "&" or ";"
+    :returns: the entity string without "&" or ";" if it's a valid character
+        entity; ``None`` otherwise
 
     """
     # Nix the & at the beginning
@@ -557,9 +557,11 @@ def match_entity(stream):
     # Handle character entities
     while stream and stream[0] not in end_characters:
         c = stream.pop(0)
-        if not ENTITIES_TRIE.has_keys_with_prefix(possible_entity):
-            break
         possible_entity += c
+        if not ENTITIES_TRIE.has_keys_with_prefix(possible_entity):
+            # If it's not a prefix, then it's not an entity and we're
+            # out
+            return None
 
     if possible_entity and stream and stream[0] == ";":
         return possible_entity

diff --git a/tests/test_clean.py b/tests/test_clean.py
index 9aca28a..c22ffa3 100644
--- a/tests/test_clean.py
+++ b/tests/test_clean.py
@@ -11,11 +11,14 @@ from bleach._vendor.html5lib.constants import rcdataElements
 @pytest.mark.parametrize(
     "data",
     [
-        "<span>text & </span>",
         "a < b",
         "link http://link.com",
         "text<em>",
+        # Verify idempotentcy with character entity handling
+        "<span>text & </span>",
         "jim &current joe",
+        "&&nbsp; &nbsp;&",
+        "jim &xx; joe",
         # Link with querystring items
         '<a href="http://example.com?foo=bar&bar=foo&amp;biz=bash">',
     ],
@@ -156,41 +159,47 @@ def test_bare_entities_get_escaped_correctly(text, expected):
 @pytest.mark.parametrize(
     "text, expected",
     [
-        # Test character entities
+        # Test character entities in text don't get escaped
         ("&amp;", "&amp;"),
         ("&nbsp;", "&nbsp;"),
         ("&nbsp; test string &nbsp;", "&nbsp; test string &nbsp;"),
         ("&lt;em&gt;strong&lt;/em&gt;", "&lt;em&gt;strong&lt;/em&gt;"),
-        # Test character entity at beginning of string
+        # Test character entity at beginning of string doesn't get escaped
         ("&amp;is cool", "&amp;is cool"),
-        # Test it at the end of the string
+        # Test character entity at end of the string doesn't get escaped
         ("cool &amp;", "cool &amp;"),
-        # Test bare ampersands and entities at beginning
+        # Test bare ampersands before an entity at the beginning of the string
+        # gets escaped
         ("&&amp; is cool", "&amp;&amp; is cool"),
-        # Test entities and bare ampersand at end
+        # Test ampersand after an entity at the end of the string gets escaped
         ("&amp; is cool &amp;&", "&amp; is cool &amp;&amp;"),
-        # Test missing semi-colon means we don't treat it like an entity
+        # Test missing semi-colons mean we don't treat the thing as an entity--Bleach
+        # only recognizes character entities that start with & and end with ;
         ("this &amp that", "this &amp;amp that"),
-        # Test a thing that looks like a character entity, but isn't because it's
-        # missing a ; (&current)
         (
             "http://example.com?active=true&current=true",
             "http://example.com?active=true&amp;current=true",
         ),
-        # Test character entities in attribute values are left alone
+        # Test character entities in attribute values are not escaped
         ('<a href="?art&amp;copy">foo</a>', '<a href="?art&amp;copy">foo</a>'),
         ('<a href="?this=&gt;that">foo</a>', '<a href="?this=&gt;that">foo</a>'),
-        # Ambiguous ampersands get escaped in attributes
+        # Things in attributes that aren't character entities get escaped
         (
             '<a href="http://example.com/&xx;">foo</a>',
             '<a href="http://example.com/&amp;xx;">foo</a>',
         ),
+        (
+            '<a href="http://example.com?&adp;">foo</a>',
+            '<a href="http://example.com?&amp;adp;">foo</a>',
+        ),
         (
             '<a href="http://example.com?active=true&current=true">foo</a>',
             '<a href="http://example.com?active=true&amp;current=true">foo</a>',
         ),
-        # Ambiguous ampersands in text are not escaped
-        ("&xx;", "&xx;"),
+        # Things in text that aren't character entities get escaped
+        ("&xx;", "&amp;xx;"),
+        ("&adp;", "&amp;adp;"),
+        ("&currdupe;", "&amp;currdupe;"),
         # Test numeric entities
         ("&#39;", "&#39;"),
         ("&#34;", "&#34;"),

bleach.clean method returns unexpected value for string with unknown entities
For string strings like "&adp;" and "&*!;" actual output is "&ad;;" and "&*;;".
But I expect that such values should remain original value ("&adp;" and "&*!;" corresponding).
tests/test_clean.py::test_character_entities_handling[&xx;-&amp;xx;] tests/test_clean.py::test_character_entities_handling[&adp;-&amp;adp;]
tests/test_clean.py::test_clean_idempotent[a tests/test_clean.py::test_clean_idempotent[link tests/test_clean.py::test_clean_idempotent[text<em>] tests/test_clean.py::test_clean_idempotent[<span>text tests/test_clean.py::test_clean_idempotent[jim tests/test_clean.py::test_clean_idempotent[&&nbsp; tests/test_clean.py::test_clean_idempotent[<a tests/test_clean.py::test_clean_idempotent_img tests/test_clean.py::test_only_text_is_cleaned tests/test_clean.py::test_empty tests/test_clean.py::test_content_has_no_html tests/test_clean.py::test_content_has_allowed_html[an tests/test_clean.py::test_content_has_allowed_html[another tests/test_clean.py::test_html_is_lowercased tests/test_clean.py::test_comments[<!-- tests/test_clean.py::test_comments[<!--open tests/test_clean.py::test_comments[<!--comment-->text-True-text] tests/test_clean.py::test_comments[<!--comment-->text-False-<!--comment-->text] tests/test_clean.py::test_comments[text<!-- tests/test_clean.py::test_comments[text<!--comment-->-True-text] tests/test_clean.py::test_comments[text<!--comment-->-False-text<!--comment-->] tests/test_clean.py::test_invalid_char_in_tag tests/test_clean.py::test_unclosed_tag tests/test_clean.py::test_nested_script_tag tests/test_clean.py::test_bare_entities_get_escaped_correctly[an tests/test_clean.py::test_bare_entities_get_escaped_correctly[tag tests/test_clean.py::test_character_entities_handling[&amp;-&amp;] tests/test_clean.py::test_character_entities_handling[&nbsp;-&nbsp;] tests/test_clean.py::test_character_entities_handling[&nbsp; tests/test_clean.py::test_character_entities_handling[&lt;em&gt;strong&lt;/em&gt;-&lt;em&gt;strong&lt;/em&gt;] tests/test_clean.py::test_character_entities_handling[&amp;is tests/test_clean.py::test_character_entities_handling[cool tests/test_clean.py::test_character_entities_handling[&&amp; tests/test_clean.py::test_character_entities_handling[&amp; tests/test_clean.py::test_character_entities_handling[this tests/test_clean.py::test_character_entities_handling[http://example.com?active=true&current=true-http://example.com?active=true&amp;current=true] tests/test_clean.py::test_character_entities_handling[<a tests/test_clean.py::test_character_entities_handling[&currdupe;-&amp;currdupe;] tests/test_clean.py::test_character_entities_handling[&#39;-&#39;] tests/test_clean.py::test_character_entities_handling[&#34;-&#34;] tests/test_clean.py::test_character_entities_handling[&#123;-&#123;] tests/test_clean.py::test_character_entities_handling[&#x0007b;-&#x0007b;] tests/test_clean.py::test_character_entities_handling[&#x0007B;-&#x0007B;] tests/test_clean.py::test_character_entities_handling[&#-&amp;#] tests/test_clean.py::test_character_entities_handling[&#<-&amp;#&lt;] tests/test_clean.py::test_character_entities_handling[&#39;&#34;-&#39;&#34;] tests/test_clean.py::test_stripping_tags[a tests/test_clean.py::test_stripping_tags[<p><a tests/test_clean.py::test_stripping_tags[<p><span>multiply tests/test_clean.py::test_stripping_tags[<ul><li><script></li></ul>-kwargs4-<ul><li></li></ul>] tests/test_clean.py::test_stripping_tags[<isindex>-kwargs6-] tests/test_clean.py::test_stripping_tags[Yeah tests/test_clean.py::test_stripping_tags[<sarcasm>-kwargs8-] tests/test_clean.py::test_stripping_tags[</sarcasm>-kwargs9-] tests/test_clean.py::test_stripping_tags[</ tests/test_clean.py::test_stripping_tags[Foo tests/test_clean.py::test_stripping_tags[Favorite tests/test_clean.py::test_stripping_tags[</3-kwargs14-&lt;/3] tests/test_clean.py::test_escaping_tags[<img tests/test_clean.py::test_escaping_tags[<script>safe()</script>-&lt;script&gt;safe()&lt;/script&gt;] tests/test_clean.py::test_escaping_tags[<style>body{}</style>-&lt;style&gt;body{}&lt;/style&gt;] tests/test_clean.py::test_escaping_tags[<ul><li><script></li></ul>-<ul><li>&lt;script&gt;</li></ul>] tests/test_clean.py::test_escaping_tags[<isindex>-&lt;isindex&gt;] tests/test_clean.py::test_escaping_tags[<sarcasm/>-&lt;sarcasm/&gt;] tests/test_clean.py::test_escaping_tags[<sarcasm>-&lt;sarcasm&gt;] tests/test_clean.py::test_escaping_tags[</sarcasm>-&lt;/sarcasm&gt;] tests/test_clean.py::test_escaping_tags[</ tests/test_clean.py::test_escaping_tags[</3-&lt;/3] tests/test_clean.py::test_escaping_tags[<bar@example.com>-&lt;bar@example.com&gt;] tests/test_clean.py::test_escaping_tags[Favorite tests/test_clean.py::test_stripping_tags_is_safe[<scri<script>pt>alert(1)</scr</script>ipt>-pt&gt;alert(1)ipt&gt;] tests/test_clean.py::test_stripping_tags_is_safe[<scri<scri<script>pt>pt>alert(1)</script>-pt&gt;pt&gt;alert(1)] tests/test_clean.py::test_allowed_styles tests/test_clean.py::test_href_with_wrong_tag tests/test_clean.py::test_disallowed_attr tests/test_clean.py::test_unquoted_attr_values_are_quoted tests/test_clean.py::test_unquoted_event_handler_attr_value tests/test_clean.py::test_invalid_filter_attr tests/test_clean.py::test_poster_attribute tests/test_clean.py::test_attributes_callable tests/test_clean.py::test_attributes_wildcard tests/test_clean.py::test_attributes_wildcard_callable tests/test_clean.py::test_attributes_tag_callable tests/test_clean.py::test_attributes_tag_list tests/test_clean.py::test_attributes_list tests/test_clean.py::test_uri_value_allowed_protocols[<a tests/test_clean.py::test_svg_attr_val_allows_ref tests/test_clean.py::test_svg_allow_local_href[<svg><pattern tests/test_clean.py::test_svg_allow_local_href_nonlocal[<svg><pattern tests/test_clean.py::test_invisible_characters[1\x0723-1?23] tests/test_clean.py::test_invisible_characters[1\x0823-1?23] tests/test_clean.py::test_invisible_characters[1\x0b23-1?23] tests/test_clean.py::test_invisible_characters[1\x0c23-1?23] tests/test_clean.py::test_invisible_characters[import tests/test_clean.py::test_nonexistent_namespace tests/test_clean.py::test_self_closing_tags_self_close[area] tests/test_clean.py::test_self_closing_tags_self_close[base] tests/test_clean.py::test_self_closing_tags_self_close[br] tests/test_clean.py::test_self_closing_tags_self_close[embed] tests/test_clean.py::test_self_closing_tags_self_close[hr] tests/test_clean.py::test_self_closing_tags_self_close[img] tests/test_clean.py::test_self_closing_tags_self_close[input] tests/test_clean.py::test_self_closing_tags_self_close[link] tests/test_clean.py::test_self_closing_tags_self_close[meta] tests/test_clean.py::test_self_closing_tags_self_close[param] tests/test_clean.py::test_self_closing_tags_self_close[source] tests/test_clean.py::test_self_closing_tags_self_close[track] tests/test_clean.py::test_noscript_rawtag_[title-<noscript><title></noscript><img tests/test_clean.py::test_noscript_rawtag_[textarea-<noscript><textarea></noscript><img tests/test_clean.py::test_noscript_rawtag_[script-<noscript><script></noscript><img tests/test_clean.py::test_noscript_rawtag_[style-<noscript><style></noscript><img tests/test_clean.py::test_noscript_rawtag_[noembed-<noscript><noembed></noscript><img tests/test_clean.py::test_noscript_rawtag_[noframes-<noscript><noframes></noscript><img tests/test_clean.py::test_noscript_rawtag_[iframe-<noscript><iframe></noscript><img tests/test_clean.py::test_noscript_rawtag_[xmp-<noscript><xmp></noscript><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[math-noframes-<math><noframes><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[math-noscript-<math><noscript><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[math-style-<math><style><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[math-noembed-<math><noembed><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[math-script-<math><script><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[math-xmp-<math><xmp><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[math-iframe-<math><iframe><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[svg-noframes-<svg><noframes><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[svg-noscript-<svg><noscript><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[svg-style-<svg><style><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[svg-noembed-<svg><noembed><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[svg-script-<svg><script><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[svg-xmp-<svg><xmp><img tests/test_clean.py::test_namespace_rc_data_element_strip_false[svg-iframe-<svg><iframe><img tests/test_clean.py::test_html_comments_escaped[math-p-style-<math></p><style><!--</style><img tests/test_clean.py::test_html_comments_escaped[math-br-style-<math></br><style><!--</style><img tests/test_clean.py::test_html_comments_escaped[svg-p-style-<svg></p><style><!--</style><img tests/test_clean.py::test_html_comments_escaped[svg-br-style-<svg></br><style><!--</style><img tests/test_clean.py::test_html_comments_escaped[math-p-title-<math></p><title><!--</title><img tests/test_clean.py::test_html_comments_escaped[math-br-title-<math></br><title><!--</title><img tests/test_clean.py::test_html_comments_escaped[svg-p-title-<svg></p><title><!--</title><img tests/test_clean.py::test_html_comments_escaped[svg-br-title-<svg></br><title><!--</title><img tests/test_clean.py::test_html_comments_escaped[math-p-noscript-<math></p><noscript><!--</noscript><img tests/test_clean.py::test_html_comments_escaped[math-br-noscript-<math></br><noscript><!--</noscript><img tests/test_clean.py::test_html_comments_escaped[svg-p-noscript-<svg></p><noscript><!--</noscript><img tests/test_clean.py::test_html_comments_escaped[svg-br-noscript-<svg></br><noscript><!--</noscript><img tests/test_clean.py::test_html_comments_escaped[math-p-script-<math></p><script><!--</script><img tests/test_clean.py::test_html_comments_escaped[math-br-script-<math></br><script><!--</script><img tests/test_clean.py::test_html_comments_escaped[svg-p-script-<svg></p><script><!--</script><img tests/test_clean.py::test_html_comments_escaped[svg-br-script-<svg></br><script><!--</script><img tests/test_clean.py::test_html_comments_escaped[math-p-noembed-<math></p><noembed><!--</noembed><img tests/test_clean.py::test_html_comments_escaped[math-br-noembed-<math></br><noembed><!--</noembed><img tests/test_clean.py::test_html_comments_escaped[svg-p-noembed-<svg></p><noembed><!--</noembed><img tests/test_clean.py::test_html_comments_escaped[svg-br-noembed-<svg></br><noembed><!--</noembed><img tests/test_clean.py::test_html_comments_escaped[math-p-textarea-<math></p><textarea><!--</textarea><img tests/test_clean.py::test_html_comments_escaped[math-br-textarea-<math></br><textarea><!--</textarea><img tests/test_clean.py::test_html_comments_escaped[svg-p-textarea-<svg></p><textarea><!--</textarea><img tests/test_clean.py::test_html_comments_escaped[svg-br-textarea-<svg></br><textarea><!--</textarea><img tests/test_clean.py::test_html_comments_escaped[math-p-noframes-<math></p><noframes><!--</noframes><img tests/test_clean.py::test_html_comments_escaped[math-br-noframes-<math></br><noframes><!--</noframes><img tests/test_clean.py::test_html_comments_escaped[svg-p-noframes-<svg></p><noframes><!--</noframes><img tests/test_clean.py::test_html_comments_escaped[svg-br-noframes-<svg></br><noframes><!--</noframes><img tests/test_clean.py::test_html_comments_escaped[math-p-iframe-<math></p><iframe><!--</iframe><img tests/test_clean.py::test_html_comments_escaped[math-br-iframe-<math></br><iframe><!--</iframe><img tests/test_clean.py::test_html_comments_escaped[svg-p-iframe-<svg></p><iframe><!--</iframe><img tests/test_clean.py::test_html_comments_escaped[svg-br-iframe-<svg></br><iframe><!--</iframe><img tests/test_clean.py::test_html_comments_escaped[math-p-xmp-<math></p><xmp><!--</xmp><img tests/test_clean.py::test_html_comments_escaped[math-br-xmp-<math></br><xmp><!--</xmp><img tests/test_clean.py::test_html_comments_escaped[svg-p-xmp-<svg></p><xmp><!--</xmp><img tests/test_clean.py::test_html_comments_escaped[svg-br-xmp-<svg></br><xmp><!--</xmp><img tests/test_clean.py::test_regressions[1.test] tests/test_clean.py::test_regressions[2.test] tests/test_clean.py::test_regressions[3.test] tests/test_clean.py::test_regressions[4.test] tests/test_clean.py::test_regressions[5.test] tests/test_clean.py::test_regressions[6.test] tests/test_clean.py::test_regressions[7.test] tests/test_clean.py::test_regressions[8.test] tests/test_clean.py::test_regressions[9.test] tests/test_clean.py::test_regressions[10.test] tests/test_clean.py::test_regressions[11.test] tests/test_clean.py::test_regressions[12.test] tests/test_clean.py::test_regressions[13.test] tests/test_clean.py::test_regressions[14.test] tests/test_clean.py::test_regressions[15.test] tests/test_clean.py::test_regressions[16.test] tests/test_clean.py::test_regressions[17.test] tests/test_clean.py::test_regressions[18.test] tests/test_clean.py::test_regressions[19.test] tests/test_clean.py::test_regressions[20.test] tests/test_clean.py::test_preserves_attributes_order tests/test_clean.py::TestCleaner::test_basics tests/test_clean.py::TestCleaner::test_filters