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); |