Skip to content

Commit b33932d

Browse files
committed
Use classic enum and focus on code readability
1 parent d067d06 commit b33932d

File tree

1 file changed

+64
-73
lines changed

1 file changed

+64
-73
lines changed

src/ESPAsyncWebServer.h

Lines changed: 64 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,19 @@ class AsyncWebServerRequest {
731731
};
732732

733733
class AsyncURIMatcher {
734+
private:
735+
// Matcher types are internal, not part of public API
736+
enum class Type {
737+
None, // default state: matcher does not match anything
738+
Auto, // parse _uri at construct time and infer match type(s): _uri may be transformed to remove wildcards
739+
All, // matches everything
740+
Exact, // matches equivalent to regex: ^{_uri}$
741+
Prefix, // matches equivalent to regex: ^{_uri}.*
742+
Extension, // non-regular match: /pattern../*.ext
743+
BackwardCompatible, // matches equivalent to regex: ^{_uri}(/.*)?$
744+
Regex, // matches _url as regex
745+
};
746+
734747
public:
735748
/**
736749
* @brief No special matching behavior (default)
@@ -762,9 +775,9 @@ class AsyncURIMatcher {
762775
static constexpr uint16_t CaseInsensitive = (1 << 0);
763776

764777
// public constructors
765-
AsyncURIMatcher() : _flags(All) {}
766-
AsyncURIMatcher(String uri, uint16_t modifiers = None) : AsyncURIMatcher(std::move(uri), Auto, modifiers) {}
767-
AsyncURIMatcher(const char *uri, uint16_t modifiers = None) : AsyncURIMatcher(String(uri), Auto, modifiers) {}
778+
AsyncURIMatcher() : _flags(intptr_t(Type::None)) {}
779+
AsyncURIMatcher(String uri, uint16_t modifiers = None) : AsyncURIMatcher(std::move(uri), Type::Auto, modifiers) {}
780+
AsyncURIMatcher(const char *uri, uint16_t modifiers = None) : AsyncURIMatcher(String(uri), Type::Auto, modifiers) {}
768781

769782
#ifdef ASYNCWEBSERVER_REGEX
770783
AsyncURIMatcher(const AsyncURIMatcher &c) : _value(c._value), _flags(c._flags) {
@@ -774,7 +787,7 @@ class AsyncURIMatcher {
774787
}
775788

776789
AsyncURIMatcher(AsyncURIMatcher &&c) : _value(std::move(c._value)), _flags(c._flags) {
777-
c._flags = 0;
790+
c._flags = intptr_t(Type::None);
778791
}
779792

780793
~AsyncURIMatcher() {
@@ -810,7 +823,7 @@ class AsyncURIMatcher {
810823
_flags = r._flags;
811824
if (r._isRegex()) {
812825
// We have adopted it
813-
r._flags = 0;
826+
r._flags = intptr_t(Type::None);
814827
}
815828
return *this;
816829
}
@@ -825,11 +838,6 @@ class AsyncURIMatcher {
825838
#endif
826839

827840
bool matches(AsyncWebServerRequest *request) const {
828-
// Match-all is tested first
829-
if (_flags & All) {
830-
return true;
831-
}
832-
833841
#ifdef ASYNCWEBSERVER_REGEX
834842
if (_isRegex()) {
835843
std::smatch matches;
@@ -843,34 +851,33 @@ class AsyncURIMatcher {
843851
return false;
844852
}
845853
#endif
846-
String path = request->url();
847-
if (_flags & (CaseInsensitive << 16)) {
848-
path.toLowerCase();
849-
}
850854

851-
// Exact match (should be the most common case)
852-
if ((_flags & Exact) && (_value == path)) {
853-
return true;
854-
}
855+
// extract matcher type from _flags
856+
const intptr_t flags = _flags >> 1; // shift off disambiguation bit;
857+
const Type type = static_cast<Type>(flags & 0xFFFF); // Type is lower 16 bits
858+
const uint16_t modifiers = flags >> 16; // Modifiers are upper 16 bits
855859

856-
// Prefix match types
857-
if ((_flags & Prefix) && path.startsWith(_value)) {
858-
return true;
859-
}
860-
if ((_flags & PrefixFolder) && path.startsWith(_value + "/")) {
861-
return true;
860+
// apply modifiers
861+
String path = request->url();
862+
if (modifiers & CaseInsensitive) {
863+
path.toLowerCase();
862864
}
863865

864-
// Extension match
865-
if (_flags & Extension) {
866-
int split = _value.lastIndexOf("/*.");
867-
if (split >= 0 && path.startsWith(_value.substring(0, split)) && path.endsWith(_value.substring(split + 2))) {
868-
return true;
866+
switch (type) {
867+
case Type::All: return true;
868+
case Type::Exact: return (_value == path);
869+
case Type::Prefix: return path.startsWith(_value);
870+
case Type::Extension:
871+
{
872+
int split = _value.lastIndexOf("/*.");
873+
return (split >= 0 && path.startsWith(_value.substring(0, split)) && path.endsWith(_value.substring(split + 2)));
869874
}
875+
case Type::BackwardCompatible: return (_value == path) || path.startsWith(_value + "/");
876+
default:
877+
// Should never happen - programming error
878+
assert("Invalid type");
879+
return false;
870880
}
871-
872-
// we did not match
873-
return false;
874881
}
875882

876883
// static factory methods for common match types
@@ -882,7 +889,7 @@ class AsyncURIMatcher {
882889
* Usage: server.on(AsyncURIMatcher::all(), handler);
883890
*/
884891
static inline AsyncURIMatcher all() {
885-
return AsyncURIMatcher{{}, All, None};
892+
return AsyncURIMatcher{{}, Type::All, None};
886893
}
887894

888895
/**
@@ -897,7 +904,7 @@ class AsyncURIMatcher {
897904
* Doesn't match: "/LOGIN" (unless CaseInsensitive flag used)
898905
*/
899906
static inline AsyncURIMatcher exact(String c, uint16_t modifiers = None) {
900-
return AsyncURIMatcher{std::move(c), Exact, modifiers};
907+
return AsyncURIMatcher{std::move(c), Type::Exact, modifiers};
901908
}
902909

903910
/**
@@ -911,7 +918,7 @@ class AsyncURIMatcher {
911918
* Note: This is pure prefix matching - does NOT require folder separator
912919
*/
913920
static inline AsyncURIMatcher prefix(String c, uint16_t modifiers = None) {
914-
return AsyncURIMatcher{std::move(c), Prefix, modifiers};
921+
return AsyncURIMatcher{std::move(c), Type::Prefix, modifiers};
915922
}
916923

917924
/**
@@ -929,12 +936,12 @@ class AsyncURIMatcher {
929936
static inline AsyncURIMatcher dir(String c, uint16_t modifiers = None) {
930937
// Pre-calculate folder for efficiency
931938
if (!c.length()) {
932-
return AsyncURIMatcher{"/", Prefix, modifiers};
939+
return AsyncURIMatcher{"/", Type::Prefix, modifiers};
933940
}
934941
if (c[c.length() - 1] != '/') {
935942
c.concat('/');
936943
}
937-
return AsyncURIMatcher{std::move(c), Prefix, modifiers};
944+
return AsyncURIMatcher{std::move(c), Type::Prefix, modifiers};
938945
}
939946

940947
/**
@@ -951,7 +958,7 @@ class AsyncURIMatcher {
951958
* The path before "/\*." must match exactly, and the URI must end with the extension.
952959
*/
953960
static inline AsyncURIMatcher ext(String c, uint16_t modifiers = None) {
954-
return AsyncURIMatcher{std::move(c), Extension, modifiers};
961+
return AsyncURIMatcher{std::move(c), Type::Extension, modifiers};
955962
}
956963

957964
#ifdef ASYNCWEBSERVER_REGEX
@@ -970,33 +977,15 @@ class AsyncURIMatcher {
970977
* Performance note: Regex matching is slower than other match types.
971978
*/
972979
static inline AsyncURIMatcher regex(String c, uint16_t modifiers = None) {
973-
return AsyncURIMatcher{std::move(c), Regex, modifiers};
980+
return AsyncURIMatcher{std::move(c), Type::Regex, modifiers};
974981
}
975982
#endif
976983

977984
private:
978-
// Matcher types
979-
enum Type : uint16_t {
980-
// Meta types - low bits
981-
Auto = (1 << 0), // parse _uri at construct time and infer match type(s)
982-
// (_uri may be transformed to remove wildcards)
983-
984-
All = (1 << 1), // No type set
985-
Exact = (1 << 2), // matches equivalent to regex: ^{_uri}$
986-
Prefix = (1 << 3), // matches equivalent to regex: ^{_uri}.*
987-
PrefixFolder = (1 << 4), // matches equivalent to regex: ^{_uri}/.*
988-
Extension = (1 << 5), // non-regular match: /pattern../*.ext
989-
990-
#ifdef ASYNCWEBSERVER_REGEX
991-
NonRegex = (1 << 0), // bit to use as pointer tag
992-
Regex = (1 << 15), // matches _url as regex
993-
#endif
994-
};
995-
996985
// fields
997986
String _value;
998987
union {
999-
intptr_t _flags;
988+
intptr_t _flags; // type and flags packed together
1000989
#ifdef ASYNCWEBSERVER_REGEX
1001990
// Overlay the pattern pointer storage with the type. It is treated as a tagged pointer:
1002991
// if any of the LSBs are set, it stores type, as a valid object must be aligned and so
@@ -1018,40 +1007,42 @@ class AsyncURIMatcher {
10181007
#endif
10191008

10201009
// Core private constructor
1021-
AsyncURIMatcher(String uri, Type type = Auto, uint16_t modifiers = None) : _value(std::move(uri)), _flags(uint32_t(modifiers) << 16 | type) {
1010+
AsyncURIMatcher(String uri, Type type, uint16_t modifiers) : _value(std::move(uri)) {
10221011
#ifdef ASYNCWEBSERVER_REGEX
1023-
if ((type & Regex) || ((type & Auto) && _value.startsWith("^") && _value.endsWith("$"))) {
1012+
if ((type == Type::Regex) || ((type == Type::Auto) && _value.startsWith("^") && _value.endsWith("$"))) {
10241013
pattern = new std::regex(_value.c_str(), (modifiers & CaseInsensitive) ? (std::regex::icase | std::regex::optimize) : (std::regex::optimize));
1025-
return; // no additional processing - modifiers are overwritten
1014+
return; // no additional processing - flags are overwritten by pattern pointer
10261015
}
10271016
#endif
10281017
if (modifiers & CaseInsensitive) {
10291018
_value.toLowerCase();
10301019
}
1031-
if (type & Auto) {
1020+
if (type == Type::Auto) {
10321021
// Inspect _value to set flags
10331022
// empty URI matches everything
10341023
if (!_value.length()) {
1035-
_flags = All;
1036-
return; // Does not require extra bit for regex disambiguation
1037-
}
1038-
if (_value.endsWith("*")) {
1024+
type = Type::All;
1025+
} else if (_value.endsWith("*")) {
10391026
// wildcard match with * at the end
1040-
_flags |= Prefix;
1027+
type = Type::Prefix;
10411028
_value = _value.substring(0, _value.length() - 1);
10421029
} else if (_value.lastIndexOf("/*.") >= 0) {
10431030
// prefix match with /*.ext
10441031
// matches any path ending with .ext
10451032
// e.g. /images/*.png will match /images/pic.png and /images/2023/pic.png but not /img/pic.png
1046-
_flags |= Extension;
1033+
type = Type::Extension;
10471034
} else {
1048-
// No special values - use default of folder and exact
1049-
_flags |= PrefixFolder | Exact;
1035+
// backward compatible use case: exact match or prefix with trailing /
1036+
type = Type::BackwardCompatible;
10501037
}
10511038
}
1052-
#ifdef ASYNCWEBSERVER_REGEX
1053-
_flags |= NonRegex; // disambiguate regex case
1054-
#endif
1039+
_flags = uint32_t(modifiers) << 16 | uint16_t(type);
1040+
// Use lsb to disambiguate from regex pointer in the case where someone has regex activated but uses a non-regex type.
1041+
// We always do this shift, even if regex is not enabled, to keep the layout identical and also catch programmatic errors earlier.
1042+
// For example a mistake is to set a modifier flag to (1 << 15), which is the msb of the uint16_t.
1043+
// This msb is discarded during this shift operation.
1044+
// So pay attention to not have more than 15 modifier flags.
1045+
_flags = (_flags << 1) + 1;
10551046
}
10561047
};
10571048

0 commit comments

Comments
 (0)