فهرست منبع

Hash case-insensitive keys in a loop

case_ignore::hash recursed once per character of the key, and trailer
field names from the wire reach it, so a long one could overflow the
stack of a build that keeps the recursion (-O0). Hash in a loop instead,
which gives the same values.
yhirose 1 روز پیش
والد
کامیت
82e28aa4f3
2فایلهای تغییر یافته به همراه16 افزوده شده و 11 حذف شده
  1. 7 11
      httplib.h
  2. 9 0
      test/test.cc

+ 7 - 11
httplib.h

@@ -676,17 +676,13 @@ struct equal_to {
 
 struct hash {
   size_t operator()(const std::string &key) const {
-    return hash_core(key.data(), key.size(), 0);
-  }
-
-  size_t hash_core(const char *s, size_t l, size_t h) const {
-    return (l == 0) ? h
-                    : hash_core(s + 1, l - 1,
-                                // Unsets the 6 high bits of h, therefore no
-                                // overflow happens
-                                (((std::numeric_limits<size_t>::max)() >> 6) &
-                                 h * 33) ^
-                                    static_cast<unsigned char>(to_lower(*s)));
+    size_t h = 0;
+    for (auto c : key) {
+      // Unsets the 6 high bits of h, therefore no overflow happens
+      h = (((std::numeric_limits<size_t>::max)() >> 6) & h * 33) ^
+          static_cast<unsigned char>(to_lower(c));
+    }
+    return h;
   }
 };
 

+ 9 - 0
test/test.cc

@@ -643,6 +643,15 @@ TEST(FileExtensionTest, TakesTheAlphanumericSuffix) {
   EXPECT_EQ(long_ext, detail::file_extension("a." + long_ext));
 }
 
+TEST(CaseIgnoreHashTest, IgnoresCaseAndTakesLongKeys) {
+  detail::case_ignore::hash hash;
+  EXPECT_EQ(hash("Content-Type"), hash("content-TYPE"));
+  EXPECT_NE(hash("Content-Type"), hash("Content-Length"));
+
+  // Long enough to overflow the stack if hashed one call per character.
+  EXPECT_EQ(hash(std::string(1 << 20, 'A')), hash(std::string(1 << 20, 'a')));
+}
+
 TEST(FindContentTypeTest, ComparesWholeExtensions) {
   EXPECT_EQ("text/html", detail::find_content_type("/a.html", {}, ""));
   EXPECT_EQ("image/svg+xml", detail::find_content_type("/a.svg", {}, ""));