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