View Javadoc
1   /*
2    * Copyright (C) 2010, Google Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.internal.storage.pack;
12  
13  /**
14   * Supports {@link DeltaIndex} by performing a partial scan of the content.
15   */
16  class DeltaIndexScanner {
17  	final int[] table;
18  
19  	// To save memory the buckets for hash chains are stored in correlated
20  	// arrays. This permits us to get 3 values per entry, without paying
21  	// the penalty for an object header on each entry.
22  
23  	final long[] entries;
24  
25  	final int[] next;
26  
27  	final int tableMask;
28  
29  	private int entryCnt;
30  
31  	DeltaIndexScanner(byte[] raw, int len) {
32  		// Clip the length so it falls on a block boundary. We won't
33  		// bother to scan the final partial block.
34  		//
35  		len -= (len % DeltaIndex.BLKSZ);
36  
37  		final int worstCaseBlockCnt = len / DeltaIndex.BLKSZ;
38  		if (worstCaseBlockCnt < 1) {
39  			table = new int[] {};
40  			tableMask = 0;
41  
42  			entries = new long[] {};
43  			next = new int[] {};
44  
45  		} else {
46  			table = new int[tableSize(worstCaseBlockCnt)];
47  			tableMask = table.length - 1;
48  
49  			// As we insert blocks we preincrement so that 0 is never a
50  			// valid entry. Therefore we have to allocate one extra space.
51  			//
52  			entries = new long[1 + worstCaseBlockCnt];
53  			next = new int[entries.length];
54  
55  			scan(raw, len);
56  		}
57  	}
58  
59  	private void scan(byte[] raw, int end) {
60  		// We scan the input backwards, and always insert onto the
61  		// front of the chain. This ensures that chains will have lower
62  		// offsets at the front of the chain, allowing us to prefer the
63  		// earlier match rather than the later match.
64  		//
65  		int lastHash = 0;
66  		int ptr = end - DeltaIndex.BLKSZ;
67  		do {
68  			final int key = DeltaIndex.hashBlock(raw, ptr);
69  			final int tIdx = key & tableMask;
70  
71  			final int head = table[tIdx];
72  			if (head != 0 && lastHash == key) {
73  				// Two consecutive blocks have the same content hash,
74  				// prefer the earlier block because we want to use the
75  				// longest sequence we can during encoding.
76  				//
77  				entries[head] = (((long) key) << 32) | ptr;
78  			} else {
79  				final int eIdx = ++entryCnt;
80  				entries[eIdx] = (((long) key) << 32) | ptr;
81  				next[eIdx] = head;
82  				table[tIdx] = eIdx;
83  			}
84  
85  			lastHash = key;
86  			ptr -= DeltaIndex.BLKSZ;
87  		} while (0 <= ptr);
88  	}
89  
90  	private static int tableSize(int worstCaseBlockCnt) {
91  		int shift = 32 - Integer.numberOfLeadingZeros(worstCaseBlockCnt);
92  		int sz = 1 << (shift - 1);
93  		if (sz < worstCaseBlockCnt)
94  			sz <<= 1;
95  		return sz;
96  	}
97  }