| 107 | namespace |
| 108 | { |
| 109 | static std::size_t GetPathRootLength(StringView path) |
| 110 | { |
| 111 | if (path.empty()) return 0; |
| 112 | |
| 113 | #if defined(DEATH_TARGET_WINDOWS) |
| 114 | constexpr StringView ExtendedPathPrefix = "\\\\?\\"_s; |
| 115 | constexpr StringView UncExtendedPathPrefix = "\\\\?\\UNC\\"_s; |
| 116 | |
| 117 | std::size_t i = 0; |
| 118 | std::size_t pathLength = path.size(); |
| 119 | std::size_t volumeSeparatorLength = 2; // Length to the colon "C:" |
| 120 | std::size_t uncRootLength = 2; // Length to the start of the server name "\\" |
| 121 | |
| 122 | bool extendedSyntax = path.hasPrefix(ExtendedPathPrefix); |
| 123 | bool extendedUncSyntax = path.hasPrefix(UncExtendedPathPrefix); |
| 124 | if (extendedSyntax) { |
| 125 | // Shift the position we look for the root from to account for the extended prefix |
| 126 | if (extendedUncSyntax) { |
| 127 | // "\\" -> "\\?\UNC\" |
| 128 | uncRootLength = UncExtendedPathPrefix.size(); |
| 129 | } else { |
| 130 | // "C:" -> "\\?\C:" |
| 131 | volumeSeparatorLength += ExtendedPathPrefix.size(); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | if ((!extendedSyntax || extendedUncSyntax) && (path[0] == '/' || path[0] == '\\')) { |
| 136 | // UNC or simple rooted path (e.g., "\foo", NOT "\\?\C:\foo") |
| 137 | i = 1; |
| 138 | if (extendedUncSyntax || (pathLength > 1 && (path[1] == '/' || path[1] == '\\'))) { |
| 139 | // UNC ("\\?\UNC\" or "\\"), scan past the next two directory separators at most (e.g., "\\?\UNC\Server\Share" or "\\Server\Share\") |
| 140 | i = uncRootLength; |
| 141 | std::int32_t n = 2; // Maximum separators to skip |
| 142 | while (i < pathLength && ((path[i] != '/' && path[i] != '\\') || --n > 0)) { |
| 143 | i++; |
| 144 | } |
| 145 | // Keep the last path separator as part of root prefix |
| 146 | if (i < pathLength && (path[i] == '/' || path[i] == '\\')) { |
| 147 | i++; |
| 148 | } |
| 149 | } |
| 150 | } else if (pathLength >= volumeSeparatorLength && path[volumeSeparatorLength - 1] == ':') { |
| 151 | // Path is at least longer than where we expect a colon and has a colon ("\\?\A:", "A:") |
| 152 | // If the colon is followed by a directory separator, move past it |
| 153 | i = volumeSeparatorLength; |
| 154 | if (pathLength >= volumeSeparatorLength + 1 && (path[volumeSeparatorLength] == '/' || path[volumeSeparatorLength] == '\\')) { |
| 155 | i++; |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | return i; |
| 160 | #else |
| 161 | # if defined(DEATH_TARGET_ANDROID) |
| 162 | if (path.hasPrefix(AndroidAssetStream::Prefix)) { |
| 163 | return AndroidAssetStream::Prefix.size(); |
| 164 | } |
| 165 | # elif defined(DEATH_TARGET_SWITCH) || defined(DEATH_TARGET_VITA) |
| 166 | // Switch mount points: romfs:, sdmc:, etc. |
no test coverage detected