aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/vfs/WindowsPathFragment.java
diff options
context:
space:
mode:
authorGravatar nharmata <nharmata@google.com>2017-04-24 17:41:23 +0200
committerGravatar Vladimir Moskva <vladmos@google.com>2017-04-24 18:00:38 +0200
commitaac13242c1e2bb6d1870e1284795bd3ca370984c (patch)
treef0cbeda6d8806f2d0560f9941da7fb52ce95dc80 /src/main/java/com/google/devtools/build/lib/vfs/WindowsPathFragment.java
parent3d596d63f883fff56001ed7b2e5cf51dba45f082 (diff)
Make PathFragment an abstract class.
There are now four concrete implementations: RelativeUnixPathFragment, AbsoluteUnixPathFragment, RelativeWindowsPathFragment, AbsoluteWindowsPathFragment. Goals: -Reduce memory usage of PathFragment on non-Windows platforms while maintaining existing semantics. -Make a few simple performance improvements along the way. -Add TODOs for a few more simple performance improvements. -Open the way for reducing code complexity of PathFragment. All of the Windows-specific stuff ought not complicate the code so much. Non goals: -Make the entire codebase as pretty as possible wrt PathFragment & Windows. -Make PathFragment usage more sane in general (e.g. change semantics to ban coexistence of Windows and Unix PathFragments). -Optimize PathFragment as much as possible wrt memory or even in any other dimensions (e.g. gc churn, cpu). To elaborate, the primary motivation is per-instance memory usage of PathFragment on Unix platforms: Before this change ------------------ +UseCompressedOops --> 32 bytes per instance -UseCompressedOops --> 40 bytes per instance After this change ------------------ +UseCompressedOops --> 24 bytes per instance -UseCompressedOops --> 32 bytes per instance Since Bazel can retain lots of PathFragments, the memory savings of this CL are fairly large. RELNOTES: None PiperOrigin-RevId: 154052905
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/vfs/WindowsPathFragment.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/WindowsPathFragment.java252
1 files changed, 252 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/WindowsPathFragment.java b/src/main/java/com/google/devtools/build/lib/vfs/WindowsPathFragment.java
new file mode 100644
index 0000000000..4f94535acf
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/vfs/WindowsPathFragment.java
@@ -0,0 +1,252 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.vfs;
+
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+
+/**
+ * Abstract base class for {@link PathFragment} instances that will be allocated when Blaze is run
+ * on a Windows platform.
+ */
+abstract class WindowsPathFragment extends PathFragment {
+ static final Helper HELPER = new Helper();
+
+ protected final char driveLetter;
+
+ protected WindowsPathFragment(char driveLetter, String[] segments) {
+ super(segments);
+ this.driveLetter = driveLetter;
+ }
+
+ @Override
+ public String windowsVolume() {
+ return (driveLetter != '\0') ? driveLetter + ":" : "";
+ }
+
+ @Override
+ public char getDriveLetter() {
+ return driveLetter;
+ }
+
+ @Override
+ protected int computeHashCode() {
+ int h = 0;
+ for (String segment : segments) {
+ int segmentHash = segment.toLowerCase().hashCode();
+ h = h * 31 + segmentHash;
+ }
+ return h;
+ }
+
+ private static class Helper extends PathFragment.Helper {
+ private static final char SEPARATOR_CHAR = '/';
+ // TODO(laszlocsomor): Lots of internal PathFragment operations, e.g. getPathString, use the
+ // primary separator char and do not use this.
+ private static final char EXTRA_SEPARATOR_CHAR = '\\';
+
+ @Override
+ PathFragment create(String path) {
+ // TODO(laszlocsomor): Character#isLetter returns true for some non ASCII characters.
+ char driveLetter =
+ path.length() >= 2 && path.charAt(1) == ':' && Character.isLetter(path.charAt(0))
+ ? Character.toUpperCase(path.charAt(0))
+ : '\0';
+ if (driveLetter != '\0') {
+ path = path.substring(2);
+ // TODO(bazel-team): Decide what to do about non-absolute paths with a volume name, e.g.
+ // C:x.
+ }
+ boolean isAbsolute = path.length() > 0 && isSeparator(path.charAt(0));
+ return isAbsolute
+ ? new AbsoluteWindowsPathFragment(driveLetter, segment(path, 1))
+ : new RelativeWindowsPathFragment(driveLetter, segment(path, 0));
+ }
+
+ @Override
+ PathFragment createAlreadyInterned(char driveLetter, boolean isAbsolute, String[] segments) {
+ return isAbsolute
+ ? new AbsoluteWindowsPathFragment(driveLetter, segments)
+ : new RelativeWindowsPathFragment(driveLetter, segments);
+ }
+
+ @Override
+ char getPrimarySeparatorChar() {
+ return SEPARATOR_CHAR;
+ }
+
+ @Override
+ boolean isSeparator(char c) {
+ return c == SEPARATOR_CHAR || c == EXTRA_SEPARATOR_CHAR;
+ }
+
+ @Override
+ boolean containsSeparatorChar(String path) {
+ // TODO(laszlocsomor): This is inefficient.
+ return path.indexOf(SEPARATOR_CHAR) != -1 || path.indexOf(EXTRA_SEPARATOR_CHAR) != -1;
+ }
+
+ @Override
+ boolean segmentsEqual(int length, String[] segments1, int offset1, String[] segments2) {
+ if ((segments1.length - offset1) < length || segments2.length < length) {
+ return false;
+ }
+ for (int i = 0; i < length; ++i) {
+ String seg1 = segments1[i + offset1];
+ String seg2 = segments2[i];
+ if ((seg1 == null) != (seg2 == null)) {
+ return false;
+ }
+ if (seg1 == null) {
+ continue;
+ }
+ // TODO(laszlocsomor): The calls to String#toLowerCase are inefficient and potentially
+ // repeated too. Also, why not use String#equalsIgnoreCase.
+ seg1 = seg1.toLowerCase();
+ seg2 = seg2.toLowerCase();
+ if (!seg1.equals(seg2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ protected int compare(PathFragment pathFragment1, PathFragment pathFragment2) {
+ if (pathFragment1.isAbsolute() != pathFragment2.isAbsolute()) {
+ return pathFragment1.isAbsolute() ? -1 : 1;
+ }
+ int cmp = Character.compare(pathFragment1.getDriveLetter(), pathFragment2.getDriveLetter());
+ if (cmp != 0) {
+ return cmp;
+ }
+ String[] segments1 = pathFragment1.segments();
+ String[] segments2 = pathFragment2.segments();
+ int len1 = segments1.length;
+ int len2 = segments2.length;
+ int n = Math.min(len1, len2);
+ for (int i = 0; i < n; i++) {
+ String seg1 = segments1[i].toLowerCase();
+ String seg2 = segments2[i].toLowerCase();
+ cmp = seg1.compareTo(seg2);
+ if (cmp != 0) {
+ return cmp;
+ }
+ }
+ return len1 - len2;
+ }
+ }
+
+ private static final class AbsoluteWindowsPathFragment extends WindowsPathFragment {
+ private AbsoluteWindowsPathFragment(char driveLetter, String[] segments) {
+ super(driveLetter, segments);
+ }
+
+ @Override
+ public boolean isAbsolute() {
+ return true;
+ }
+
+ @Override
+ protected int computeHashCode() {
+ int h = Boolean.TRUE.hashCode();
+ h = h * 31 + super.computeHashCode();
+ h = h * 31 + Character.valueOf(getDriveLetter()).hashCode();
+ return h;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof AbsoluteWindowsPathFragment)) {
+ return false;
+ }
+ if (this == other) {
+ return true;
+ }
+ AbsoluteWindowsPathFragment otherAbsoluteWindowsPathFragment =
+ (AbsoluteWindowsPathFragment) other;
+ return this.driveLetter == otherAbsoluteWindowsPathFragment.driveLetter
+ && HELPER.segmentsEqual(this.segments, otherAbsoluteWindowsPathFragment.segments);
+ }
+
+ // Java serialization looks for the presence of this method in the concrete class. It is not
+ // inherited from the parent class.
+ @Override
+ protected Object writeReplace() {
+ return super.writeReplace();
+ }
+
+ // Java serialization looks for the presence of this method in the concrete class. It is not
+ // inherited from the parent class.
+ @Override
+ protected void readObject(ObjectInputStream stream) throws InvalidObjectException {
+ super.readObject(stream);
+ }
+ }
+
+ private static final class RelativeWindowsPathFragment extends WindowsPathFragment {
+ private RelativeWindowsPathFragment(char driveLetter, String[] segments) {
+ super(driveLetter, segments);
+ }
+
+ @Override
+ public boolean isAbsolute() {
+ return false;
+ }
+
+ @Override
+ protected int computeHashCode() {
+ int h = Boolean.FALSE.hashCode();
+ h = h * 31 + super.computeHashCode();
+ if (!isEmpty()) {
+ h = h * 31 + Character.valueOf(getDriveLetter()).hashCode();
+ }
+ return h;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof RelativeWindowsPathFragment)) {
+ return false;
+ }
+ if (this == other) {
+ return true;
+ }
+ RelativeWindowsPathFragment otherRelativeWindowsPathFragment =
+ (RelativeWindowsPathFragment) other;
+ return isEmpty() && otherRelativeWindowsPathFragment.isEmpty()
+ ? true
+ : this.driveLetter == otherRelativeWindowsPathFragment.driveLetter
+ && HELPER.segmentsEqual(this.segments, otherRelativeWindowsPathFragment.segments);
+ }
+
+ private boolean isEmpty() {
+ return segmentCount() == 0;
+ }
+
+ // Java serialization looks for the presence of this method in the concrete class. It is not
+ // inherited from the parent class.
+ @Override
+ protected Object writeReplace() {
+ return super.writeReplace();
+ }
+
+ // Java serialization looks for the presence of this method in the concrete class. It is not
+ // inherited from the parent class.
+ @Override
+ protected void readObject(ObjectInputStream stream) throws InvalidObjectException {
+ super.readObject(stream);
+ }
+ }
+}