Skip to content

Instantly share code, notes, and snippets.

@mikesamuel
Created October 3, 2017 03:55
Show Gist options
  • Save mikesamuel/b57c92975fc14d8e0bbb4ebfcf2baece to your computer and use it in GitHub Desktop.
Save mikesamuel/b57c92975fc14d8e0bbb4ebfcf2baece to your computer and use it in GitHub Desktop.
private static final boolean DEBUG_RDS = false;
static void removeDotSegmentsInPlace(StringBuilder path, int left) {
// The code below has excerpts from the spec interspersed.
// The "input buffer" and "output buffer" referred to in the spec
// are both just regions of path.
// The loop deals with the exclusive cases by continuing instead
// of proceeding to the bottom.
boolean isAbsolute = left < path.length() && path.charAt(left) == '/';
// RFC 3986 Section 5.2.4
// 1. The input buffer is initialized with the now-appended path
// components and the output buffer is initialized to the empty
// string.
int inputBufferStart = left;
final int inputBufferEnd = path.length();
final int outputBufferStart = left;
int outputBufferEnd = left;
// 2. While the input buffer is not empty, loop as follows:
while (inputBufferStart < inputBufferEnd) {
if (DEBUG_RDS) {
System.err.println(
"\t[" + path.substring(outputBufferStart, outputBufferEnd) + "]" +
path.substring(outputBufferEnd, inputBufferStart) + "[" +
path.substring(inputBufferStart, inputBufferEnd) + "]");
}
char c0 = path.charAt(inputBufferStart);
// A. If the input buffer begins with a prefix of "../" or "./",
// then remove that prefix from the input buffer; otherwise,
if (c0 == '.') {
char c1;
if (inputBufferStart + 1 < inputBufferEnd) {
if ('/' == (c1 = path.charAt(inputBufferStart + 1))) {
inputBufferStart += 2;
continue;
}
if ('.' == c1 && inputBufferStart + 2 < inputBufferEnd
&& '/' == path.charAt(inputBufferStart + 2)) {
inputBufferStart += 3;
continue;
}
}
}
// B. if the input buffer begins with a prefix of "/./" or "/.",
// where "." is a complete path segment, then replace that
// prefix with "/" in the input buffer; otherwise,
if (c0 == '/' && inputBufferStart + 1 < inputBufferEnd
&& '.' == path.charAt(inputBufferStart + 1)) {
if (inputBufferStart + 2 == inputBufferEnd) {
inputBufferStart += 1;
path.setCharAt(inputBufferStart, '/');
continue;
} else if ('/' == path.charAt(inputBufferStart + 2)) {
inputBufferStart += 2;
continue;
}
}
// C. if the input buffer begins with a prefix of "/../" or "/..",
// where ".." is a complete path segment, then replace that
// prefix with "/" in the input buffer and remove the last
// segment and its preceding "/" (if any) from the output
// buffer; otherwise,
if (c0 == '/' && inputBufferStart + 2 < inputBufferEnd
&& '.' == path.charAt(inputBufferStart + 1)
&& '.' == path.charAt(inputBufferStart + 2)) {
boolean foundDotDot = false;
if (inputBufferStart + 3 == inputBufferEnd) {
inputBufferStart += 2;
path.setCharAt(inputBufferStart, '/');
foundDotDot = true;
} else if ('/' == path.charAt(inputBufferStart + 3)) {
inputBufferStart += 3;
foundDotDot = true;
}
if (foundDotDot) {
while (outputBufferEnd > outputBufferStart) {
--outputBufferEnd;
if (path.charAt(outputBufferEnd) == '/') { break; }
}
if (outputBufferEnd == outputBufferStart && !isAbsolute) {
// !!!This differs from spec!!!
// Do not convert relative URLs into absolute ones via parent
// navigation.
inputBufferStart += 1;
}
continue;
}
}
// D. if the input buffer consists only of "." or "..", then remove
// that from the input buffer; otherwise,
if (c0 == '.') {
if (inputBufferStart + 1 == inputBufferEnd) {
inputBufferStart += 1;
continue;
} else if (inputBufferStart + 2 == inputBufferEnd
&& '.' == path.charAt(inputBufferStart + 1)) {
inputBufferStart += 2;
continue;
}
}
// E. move the first path segment in the input buffer to the end of
// the output buffer, including the initial "/" character (if
// any) and any subsequent characters up to, but not including,
// the next "/" character or the end of the input buffer.
do {
path.setCharAt(outputBufferEnd++, path.charAt(inputBufferStart++));
} while (inputBufferStart < inputBufferEnd
&& path.charAt(inputBufferStart) != '/');
}
// 3. Finally, the output buffer is returned as the result of
// remove_dot_segments.
path.setLength(outputBufferEnd);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment