| 166 | } |
| 167 | |
| 168 | func GetEncodedRootLength(path string) int { |
| 169 | ln := len(path) |
| 170 | if ln == 0 { |
| 171 | return 0 |
| 172 | } |
| 173 | ch0 := path[0] |
| 174 | |
| 175 | // POSIX or UNC |
| 176 | if ch0 == '/' || ch0 == '\\' { |
| 177 | if ln == 1 || path[1] != ch0 { |
| 178 | return 1 // POSIX: "/" (or non-normalized "\") |
| 179 | } |
| 180 | |
| 181 | offset := 2 |
| 182 | p1 := strings.IndexByte(path[offset:], ch0) |
| 183 | if p1 < 0 { |
| 184 | return ln // UNC: "//server" or "\\server" |
| 185 | } |
| 186 | |
| 187 | return p1 + offset + 1 // UNC: "//server/" or "\\server\" |
| 188 | } |
| 189 | |
| 190 | // DOS |
| 191 | if IsVolumeCharacter(ch0) && ln > 1 && path[1] == ':' { |
| 192 | if ln == 2 { |
| 193 | return 2 // DOS: "c:" (but not "c:d") |
| 194 | } |
| 195 | ch2 := path[2] |
| 196 | if ch2 == '/' || ch2 == '\\' { |
| 197 | return 3 // DOS: "c:/" or "c:\" |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | // Untitled paths (e.g., "^/untitled/ts-nul-authority/Untitled-1") |
| 202 | if ch0 == '^' && ln > 1 && path[1] == '/' { |
| 203 | return 2 // Untitled: "^/" |
| 204 | } |
| 205 | |
| 206 | // URL |
| 207 | schemeEnd := strings.Index(path, urlSchemeSeparator) |
| 208 | if schemeEnd != -1 { |
| 209 | authorityStart := schemeEnd + len(urlSchemeSeparator) |
| 210 | authorityLength := strings.Index(path[authorityStart:], "/") |
| 211 | if authorityLength != -1 { // URL: "file:///", "file://server/", "file://server/path" |
| 212 | authorityEnd := authorityStart + authorityLength |
| 213 | |
| 214 | // For local "file" URLs, include the leading DOS volume (if present). |
| 215 | // Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a |
| 216 | // special case interpreted as "the machine from which the URL is being interpreted". |
| 217 | scheme := path[:schemeEnd] |
| 218 | authority := path[authorityStart:authorityEnd] |
| 219 | if scheme == "file" && (authority == "" || authority == "localhost") && (len(path) > authorityEnd+2) && IsVolumeCharacter(path[authorityEnd+1]) { |
| 220 | volumeSeparatorEnd := getFileUrlVolumeSeparatorEnd(path, authorityEnd+2) |
| 221 | if volumeSeparatorEnd != -1 { |
| 222 | if volumeSeparatorEnd == len(path) { |
| 223 | // URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a" |
| 224 | // but not "file:///c:d" or "file:///c%3ad" |
| 225 | return ^volumeSeparatorEnd |