Skip to content

Commit 413f554

Browse files
Googlercopybara-github
authored andcommitted
Support for GITSHA1 digest function.
This is the algorithm used by git to hash blobs. PiperOrigin-RevId: 839262183 Change-Id: I075ba6a6acfb2d80b3b7b441510dc6c3228adf72
1 parent 4aaceff commit 413f554

File tree

6 files changed

+479
-0
lines changed

6 files changed

+479
-0
lines changed
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
// Copyright 2025 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package com.google.devtools.build.lib.vfs;
15+
16+
import static com.google.common.base.Preconditions.checkArgument;
17+
import static com.google.common.base.Preconditions.checkPositionIndexes;
18+
import static java.nio.charset.StandardCharsets.UTF_8;
19+
20+
import com.google.common.hash.Funnel;
21+
import com.google.common.hash.HashCode;
22+
import com.google.common.hash.HashFunction;
23+
import com.google.common.hash.Hasher;
24+
import com.google.common.hash.Hashing;
25+
import com.google.common.hash.PrimitiveSink;
26+
import com.google.common.io.ByteArrayDataOutput;
27+
import com.google.common.io.ByteStreams;
28+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
29+
import java.nio.ByteBuffer;
30+
import java.nio.charset.Charset;
31+
32+
/** A {@link HashFunction} for GITSHA1. */
33+
public final class GitSha1HashFunction implements HashFunction {
34+
public static final HashFunction INSTANCE = new GitSha1HashFunction();
35+
private static final HashFunction SHA1 = Hashing.sha1();
36+
private static final byte[] header = {'b', 'l', 'o', 'b', ' '};
37+
38+
private Hasher newInitializedHasher(int blobSize) {
39+
Hasher hasher = SHA1.newHasher();
40+
hasher.putBytes(header);
41+
hasher.putString(Integer.toString(blobSize), UTF_8);
42+
hasher.putByte((byte) 0);
43+
return hasher;
44+
}
45+
46+
@Override
47+
public int bits() {
48+
return 160;
49+
}
50+
51+
@Override
52+
public Hasher newHasher() {
53+
return new DelayedGitSha1Hasher();
54+
}
55+
56+
@Override
57+
public Hasher newHasher(int expectedInputSize) {
58+
checkArgument(
59+
expectedInputSize >= 0, "expectedInputSize must be >= 0 but was %s", expectedInputSize);
60+
return newInitializedHasher(expectedInputSize);
61+
}
62+
63+
/* The following methods implement the {HashFunction} interface. */
64+
65+
@Override
66+
public <T> HashCode hashObject(T instance, Funnel<? super T> funnel) {
67+
return newHasher().putObject(instance, funnel).hash();
68+
}
69+
70+
@Override
71+
public HashCode hashUnencodedChars(CharSequence input) {
72+
int len = input.length();
73+
return newHasher(len * 2).putUnencodedChars(input).hash();
74+
}
75+
76+
@Override
77+
public HashCode hashString(CharSequence input, Charset charset) {
78+
return newHasher(input.length()).putString(input, charset).hash();
79+
}
80+
81+
@Override
82+
public HashCode hashInt(int input) {
83+
return newHasher(4).putInt(input).hash();
84+
}
85+
86+
@Override
87+
public HashCode hashLong(long input) {
88+
return newHasher(8).putLong(input).hash();
89+
}
90+
91+
@Override
92+
public HashCode hashBytes(byte[] input) {
93+
return hashBytes(input, 0, input.length);
94+
}
95+
96+
@Override
97+
public HashCode hashBytes(byte[] input, int off, int len) {
98+
checkPositionIndexes(off, off + len, input.length);
99+
return newHasher(len).putBytes(input, off, len).hash();
100+
}
101+
102+
@Override
103+
public HashCode hashBytes(ByteBuffer input) {
104+
return newHasher(input.remaining()).putBytes(input).hash();
105+
}
106+
107+
private class DelayedGitSha1Hasher implements Hasher {
108+
private final ByteArrayOutput output;
109+
110+
DelayedGitSha1Hasher() {
111+
output = new ByteArrayOutput();
112+
}
113+
114+
@CanIgnoreReturnValue
115+
@Override
116+
public Hasher putBoolean(boolean b) {
117+
output.putByte(b ? (byte) 1 : (byte) 0);
118+
return this;
119+
}
120+
121+
@CanIgnoreReturnValue
122+
@Override
123+
public Hasher putByte(byte b) {
124+
output.putByte(b);
125+
return this;
126+
}
127+
128+
@CanIgnoreReturnValue
129+
@Override
130+
public Hasher putBytes(byte[] bytes) {
131+
output.putBytes(bytes);
132+
return this;
133+
}
134+
135+
@CanIgnoreReturnValue
136+
@Override
137+
public Hasher putBytes(byte[] bytes, int off, int len) {
138+
output.putBytes(bytes, off, len);
139+
return this;
140+
}
141+
142+
@CanIgnoreReturnValue
143+
@Override
144+
public Hasher putBytes(ByteBuffer b) {
145+
output.putBytes(b.array(), b.arrayOffset() + b.position(), b.remaining());
146+
return this;
147+
}
148+
149+
@CanIgnoreReturnValue
150+
@Override
151+
public Hasher putShort(short s) {
152+
output.putShort(s);
153+
return this;
154+
}
155+
156+
@CanIgnoreReturnValue
157+
@Override
158+
public Hasher putInt(int i) {
159+
output.putInt(i);
160+
return this;
161+
}
162+
163+
@CanIgnoreReturnValue
164+
@Override
165+
public Hasher putLong(long l) {
166+
output.putLong(l);
167+
return this;
168+
}
169+
170+
@CanIgnoreReturnValue
171+
@Override
172+
public <T> Hasher putObject(T instance, Funnel<? super T> funnel) {
173+
funnel.funnel(instance, output);
174+
return this;
175+
}
176+
177+
@CanIgnoreReturnValue
178+
@Override
179+
public Hasher putChar(char c) {
180+
output.putChar(c);
181+
return this;
182+
}
183+
184+
@CanIgnoreReturnValue
185+
@Override
186+
public Hasher putDouble(double d) {
187+
output.putDouble(d);
188+
return this;
189+
}
190+
191+
@CanIgnoreReturnValue
192+
@Override
193+
public Hasher putFloat(float f) {
194+
output.putFloat(f);
195+
return this;
196+
}
197+
198+
@CanIgnoreReturnValue
199+
@Override
200+
public Hasher putUnencodedChars(CharSequence charSequence) {
201+
for (int i = 0; i < charSequence.length(); i++) {
202+
output.putChar(charSequence.charAt(i));
203+
}
204+
return this;
205+
}
206+
207+
@CanIgnoreReturnValue
208+
@Override
209+
public Hasher putString(CharSequence charSequence, Charset charset) {
210+
output.putBytes(charSequence.toString().getBytes(charset));
211+
return this;
212+
}
213+
214+
@Override
215+
public HashCode hash() {
216+
byte[] body = output.toByteArray();
217+
return newHasher(body.length).putBytes(body).hash();
218+
}
219+
220+
@Override
221+
public int hashCode() {
222+
return hash().hashCode();
223+
}
224+
225+
@Override
226+
public boolean equals(Object obj) {
227+
if (obj instanceof Hasher hasher) {
228+
return this.hash().equals(hasher.hash());
229+
}
230+
return false;
231+
}
232+
}
233+
234+
private static class ByteArrayOutput implements PrimitiveSink {
235+
private final ByteArrayDataOutput buffer = ByteStreams.newDataOutput();
236+
237+
@CanIgnoreReturnValue
238+
@Override
239+
public ByteArrayOutput putBoolean(boolean b) {
240+
buffer.writeBoolean(b);
241+
return this;
242+
}
243+
244+
@CanIgnoreReturnValue
245+
@Override
246+
public ByteArrayOutput putByte(byte b) {
247+
buffer.write(b);
248+
return this;
249+
}
250+
251+
@CanIgnoreReturnValue
252+
@Override
253+
public ByteArrayOutput putBytes(byte[] bytes) {
254+
buffer.write(bytes);
255+
return this;
256+
}
257+
258+
@CanIgnoreReturnValue
259+
@Override
260+
public ByteArrayOutput putBytes(byte[] bytes, int off, int len) {
261+
buffer.write(bytes, off, len);
262+
return this;
263+
}
264+
265+
@CanIgnoreReturnValue
266+
@Override
267+
public ByteArrayOutput putBytes(ByteBuffer b) {
268+
buffer.write(b.array(), b.arrayOffset() + b.position(), b.remaining());
269+
return this;
270+
}
271+
272+
@CanIgnoreReturnValue
273+
@Override
274+
public ByteArrayOutput putChar(char c) {
275+
buffer.writeChar(c);
276+
return this;
277+
}
278+
279+
@CanIgnoreReturnValue
280+
@Override
281+
public ByteArrayOutput putDouble(double d) {
282+
buffer.writeDouble(d);
283+
return this;
284+
}
285+
286+
@CanIgnoreReturnValue
287+
@Override
288+
public ByteArrayOutput putFloat(float f) {
289+
buffer.writeFloat(f);
290+
return this;
291+
}
292+
293+
@CanIgnoreReturnValue
294+
@Override
295+
public ByteArrayOutput putInt(int i) {
296+
buffer.writeInt(i);
297+
return this;
298+
}
299+
300+
@CanIgnoreReturnValue
301+
@Override
302+
public ByteArrayOutput putLong(long l) {
303+
buffer.writeLong(l);
304+
return this;
305+
}
306+
307+
@CanIgnoreReturnValue
308+
@Override
309+
public ByteArrayOutput putShort(short s) {
310+
buffer.writeShort(s);
311+
return this;
312+
}
313+
314+
@CanIgnoreReturnValue
315+
@Override
316+
public ByteArrayOutput putString(CharSequence charSequence, Charset charset) {
317+
buffer.write(charSequence.toString().getBytes(charset));
318+
return this;
319+
}
320+
321+
@CanIgnoreReturnValue
322+
@Override
323+
public ByteArrayOutput putUnencodedChars(CharSequence charSequence) {
324+
for (int i = 0; i < charSequence.length(); i++) {
325+
buffer.writeChar(charSequence.charAt(i));
326+
}
327+
return this;
328+
}
329+
330+
byte[] toByteArray() {
331+
return buffer.toByteArray();
332+
}
333+
}
334+
}

0 commit comments

Comments
 (0)