Mercurial > hg4j
comparison src/org/tmatesoft/hg/core/Nodeid.java @ 598:d29d9dc6c128
Utilize the fact nodeids are very different and are read anyway to speed up reverse lookup
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Fri, 03 May 2013 15:19:18 +0200 |
| parents | d9c07e1432c4 |
| children |
comparison
equal
deleted
inserted
replaced
| 597:c895b5556937 | 598:d29d9dc6c128 |
|---|---|
| 31 * @author Artem Tikhomirov | 31 * @author Artem Tikhomirov |
| 32 * @author TMate Software Ltd. | 32 * @author TMate Software Ltd. |
| 33 * | 33 * |
| 34 */ | 34 */ |
| 35 public final class Nodeid implements Comparable<Nodeid> { | 35 public final class Nodeid implements Comparable<Nodeid> { |
| 36 | 36 |
| 37 /** | |
| 38 * Length of the nodeid in bytes | |
| 39 */ | |
| 40 public static final int SIZE = 20; | |
| 41 | |
| 42 /** | |
| 43 * Length of nodeid string representation, in bytes | |
| 44 */ | |
| 45 public static final int SIZE_ASCII = 40; | |
| 46 | |
| 37 /** | 47 /** |
| 38 * <b>nullid</b>, empty root revision. | 48 * <b>nullid</b>, empty root revision. |
| 39 */ | 49 */ |
| 40 public static final Nodeid NULL = new Nodeid(new byte[20], false); | 50 public static final Nodeid NULL = new Nodeid(new byte[SIZE], false); |
| 41 | 51 |
| 42 private final byte[] binaryData; | 52 private final byte[] binaryData; |
| 43 | 53 |
| 44 /** | 54 /** |
| 45 * @param binaryRepresentation - array of exactly 20 bytes | 55 * @param binaryRepresentation - array of exactly 20 bytes |
| 47 * @throws HgBadNodeidFormatException custom {@link IllegalArgumentException} subclass if supplied binary representation doesn't correspond to 20 bytes of sha1 digest | 57 * @throws HgBadNodeidFormatException custom {@link IllegalArgumentException} subclass if supplied binary representation doesn't correspond to 20 bytes of sha1 digest |
| 48 */ | 58 */ |
| 49 public Nodeid(byte[] binaryRepresentation, boolean shallClone) { | 59 public Nodeid(byte[] binaryRepresentation, boolean shallClone) { |
| 50 // 5 int fields => 32 bytes | 60 // 5 int fields => 32 bytes |
| 51 // byte[20] => 48 bytes (16 bytes is Nodeid with one field, 32 bytes for byte[20] | 61 // byte[20] => 48 bytes (16 bytes is Nodeid with one field, 32 bytes for byte[20] |
| 52 if (binaryRepresentation == null || binaryRepresentation.length != 20) { | 62 if (binaryRepresentation == null || binaryRepresentation.length != SIZE) { |
| 53 throw new HgBadNodeidFormatException(String.format("Bad value: %s", String.valueOf(binaryRepresentation))); | 63 throw new HgBadNodeidFormatException(String.format("Bad value: %s", String.valueOf(binaryRepresentation))); |
| 54 } | 64 } |
| 55 /* | 65 /* |
| 56 * byte[].clone() is not reflected when ran with -agentlib:hprof=heap=sites | 66 * byte[].clone() is not reflected when ran with -agentlib:hprof=heap=sites |
| 57 * thus not to get puzzled why there are N Nodeids and much less byte[] instances, | 67 * thus not to get puzzled why there are N Nodeids and much less byte[] instances, |
| 67 binaryData = shallClone ? binaryRepresentation.clone() : binaryRepresentation; | 77 binaryData = shallClone ? binaryRepresentation.clone() : binaryRepresentation; |
| 68 } | 78 } |
| 69 | 79 |
| 70 @Override | 80 @Override |
| 71 public int hashCode() { | 81 public int hashCode() { |
| 82 return hashCode(binaryData); | |
| 83 } | |
| 84 | |
| 85 /** | |
| 86 * Handy alternative to calculate hashcode without need to get {@link Nodeid} instance | |
| 87 * @param binaryNodeid array of exactly 20 bytes | |
| 88 * @return same value as <code>new Nodeid(binaryNodeid, false).hashCode()</code> | |
| 89 */ | |
| 90 public static int hashCode(byte[] binaryNodeid) { | |
| 91 assert binaryNodeid.length == SIZE; | |
| 72 // digest (part thereof) seems to be nice candidate for the hashCode | 92 // digest (part thereof) seems to be nice candidate for the hashCode |
| 73 byte[] b = binaryData; | 93 byte[] b = binaryNodeid; |
| 74 return b[0] << 24 | (b[1] & 0xFF) << 16 | (b[2] & 0xFF) << 8 | (b[3] & 0xFF); | 94 return b[0] << 24 | (b[1] & 0xFF) << 16 | (b[2] & 0xFF) << 8 | (b[3] & 0xFF); |
| 75 } | 95 } |
| 76 | 96 |
| 77 @Override | 97 @Override |
| 78 public boolean equals(Object o) { | 98 public boolean equals(Object o) { |
| 91 | 111 |
| 92 public int compareTo(Nodeid o) { | 112 public int compareTo(Nodeid o) { |
| 93 if (this == o) { | 113 if (this == o) { |
| 94 return 0; | 114 return 0; |
| 95 } | 115 } |
| 96 for (int i = 0; i < 20; i++) { | 116 for (int i = 0; i < SIZE; i++) { |
| 97 if (binaryData[i] != o.binaryData[i]) { | 117 if (binaryData[i] != o.binaryData[i]) { |
| 98 // if we need truly ascending sort, need to respect byte sign | 118 // if we need truly ascending sort, need to respect byte sign |
| 99 // return (binaryData[i] & 0xFF) < (o.binaryData[i] & 0xFF) ? -1 : 1; | 119 // return (binaryData[i] & 0xFF) < (o.binaryData[i] & 0xFF) ? -1 : 1; |
| 100 // however, for our purposes partial sort is pretty enough | 120 // however, for our purposes partial sort is pretty enough |
| 101 return binaryData[i] < o.binaryData[i] ? -1 : 1; | 121 return binaryData[i] < o.binaryData[i] ? -1 : 1; |
| 119 | 139 |
| 120 public boolean isNull() { | 140 public boolean isNull() { |
| 121 if (this == NULL) { | 141 if (this == NULL) { |
| 122 return true; | 142 return true; |
| 123 } | 143 } |
| 124 for (int i = 0; i < 20; i++) { | 144 for (int i = 0; i < SIZE; i++) { |
| 125 if (this.binaryData[i] != 0) { | 145 if (this.binaryData[i] != 0) { |
| 126 return false; | 146 return false; |
| 127 } | 147 } |
| 128 } | 148 } |
| 129 return true; | 149 return true; |
| 141 * @param binaryRepresentation - byte array of a length at least offset + 20 | 161 * @param binaryRepresentation - byte array of a length at least offset + 20 |
| 142 * @param offset - index in the array to start from | 162 * @param offset - index in the array to start from |
| 143 * @throws HgBadNodeidFormatException custom {@link IllegalArgumentException} subclass when arguments don't select 20 bytes | 163 * @throws HgBadNodeidFormatException custom {@link IllegalArgumentException} subclass when arguments don't select 20 bytes |
| 144 */ | 164 */ |
| 145 public static Nodeid fromBinary(byte[] binaryRepresentation, int offset) { | 165 public static Nodeid fromBinary(byte[] binaryRepresentation, int offset) { |
| 146 if (binaryRepresentation == null || binaryRepresentation.length - offset < 20) { | 166 if (binaryRepresentation == null || binaryRepresentation.length - offset < SIZE) { |
| 147 throw new HgBadNodeidFormatException(String.format("Bad value: %s", String.valueOf(binaryRepresentation))); | 167 throw new HgBadNodeidFormatException(String.format("Bad value: %s", String.valueOf(binaryRepresentation))); |
| 148 } | 168 } |
| 149 int i = 0; | 169 int i = 0; |
| 150 while (i < 20 && binaryRepresentation[offset+i] == 0) i++; | 170 while (i < SIZE && binaryRepresentation[offset+i] == 0) i++; |
| 151 if (i == 20) { | 171 if (i == SIZE) { |
| 152 return NULL; | 172 return NULL; |
| 153 } | 173 } |
| 154 if (offset == 0 && binaryRepresentation.length == 20) { | 174 if (offset == 0 && binaryRepresentation.length == SIZE) { |
| 155 return new Nodeid(binaryRepresentation, true); | 175 return new Nodeid(binaryRepresentation, true); |
| 156 } | 176 } |
| 157 byte[] b = new byte[20]; // create new instance if no other reasonable guesses possible | 177 byte[] b = new byte[SIZE]; // create new instance if no other reasonable guesses possible |
| 158 System.arraycopy(binaryRepresentation, offset, b, 0, 20); | 178 System.arraycopy(binaryRepresentation, offset, b, 0, SIZE); |
| 159 return new Nodeid(b, false); | 179 return new Nodeid(b, false); |
| 160 } | 180 } |
| 161 | 181 |
| 162 /** | 182 /** |
| 163 * Parse encoded representation. | 183 * Parse encoded representation. |
| 165 * @param asciiRepresentation - encoded form of the Nodeid. | 185 * @param asciiRepresentation - encoded form of the Nodeid. |
| 166 * @return object representation | 186 * @return object representation |
| 167 * @throws HgBadNodeidFormatException custom {@link IllegalArgumentException} subclass when argument doesn't match encoded form of 20-bytes sha1 digest. | 187 * @throws HgBadNodeidFormatException custom {@link IllegalArgumentException} subclass when argument doesn't match encoded form of 20-bytes sha1 digest. |
| 168 */ | 188 */ |
| 169 public static Nodeid fromAscii(String asciiRepresentation) throws HgBadNodeidFormatException { | 189 public static Nodeid fromAscii(String asciiRepresentation) throws HgBadNodeidFormatException { |
| 170 if (asciiRepresentation.length() != 40) { | 190 if (asciiRepresentation.length() != SIZE_ASCII) { |
| 171 throw new HgBadNodeidFormatException(String.format("Bad value: %s", asciiRepresentation)); | 191 throw new HgBadNodeidFormatException(String.format("Bad value: %s", asciiRepresentation)); |
| 172 } | 192 } |
| 173 // XXX is better impl for String possible? | 193 // XXX is better impl for String possible? |
| 174 return fromAscii(asciiRepresentation.toCharArray(), 0, 40); | 194 return fromAscii(asciiRepresentation.toCharArray(), 0, SIZE_ASCII); |
| 175 } | 195 } |
| 176 | 196 |
| 177 /** | 197 /** |
| 178 * Parse encoded representation. Similar to {@link #fromAscii(String)}. | 198 * Parse encoded representation. Similar to {@link #fromAscii(String)}. |
| 179 * @throws HgBadNodeidFormatException custom {@link IllegalArgumentException} subclass when bytes are not hex digits or number of bytes != 40 (160 bits) | 199 * @throws HgBadNodeidFormatException custom {@link IllegalArgumentException} subclass when bytes are not hex digits or number of bytes != 40 (160 bits) |
| 180 */ | 200 */ |
| 181 public static Nodeid fromAscii(byte[] asciiRepresentation, int offset, int length) throws HgBadNodeidFormatException { | 201 public static Nodeid fromAscii(byte[] asciiRepresentation, int offset, int length) throws HgBadNodeidFormatException { |
| 182 if (length != 40) { | 202 if (length != SIZE_ASCII) { |
| 183 throw new HgBadNodeidFormatException(String.format("Expected 40 hex characters for nodeid, not %d", length)); | 203 throw new HgBadNodeidFormatException(String.format("Expected %d hex characters for nodeid, not %d", SIZE_ASCII, length)); |
| 184 } | 204 } |
| 185 try { | 205 try { |
| 186 byte[] data = new byte[20]; | 206 byte[] data = new byte[SIZE]; |
| 187 boolean zeroBytes = DigestHelper.ascii2bin(asciiRepresentation, offset, length, data); | 207 boolean zeroBytes = DigestHelper.ascii2bin(asciiRepresentation, offset, length, data); |
| 188 if (zeroBytes) { | 208 if (zeroBytes) { |
| 189 return NULL; | 209 return NULL; |
| 190 } | 210 } |
| 191 return new Nodeid(data, false); | 211 return new Nodeid(data, false); |
