/* * Copyright (C) 2010, Google Inc. * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available * under the terms of the Eclipse Distribution License v1.0 which * accompanies this distribution, is reproduced below, and is * available at http://www.eclipse.org/org/documents/edl-v10.php * * All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * - Neither the name of the Eclipse Foundation, Inc. nor the * names of its contributors may be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.eclipse.jgit.internal.storage.pack; /** * Supports {@link DeltaIndex} by performing a partial scan of the content. */ class DeltaIndexScanner { final int[] table; // To save memory the buckets for hash chains are stored in correlated // arrays. This permits us to get 3 values per entry, without paying // the penalty for an object header on each entry. final long[] entries; final int[] next; final int tableMask; private int entryCnt; DeltaIndexScanner(byte[] raw, int len) { // Clip the length so it falls on a block boundary. We won't // bother to scan the final partial block. // len -= (len % DeltaIndex.BLKSZ); final int worstCaseBlockCnt = len / DeltaIndex.BLKSZ; if (worstCaseBlockCnt < 1) { table = new int[] {}; tableMask = 0; entries = new long[] {}; next = new int[] {}; } else { table = new int[tableSize(worstCaseBlockCnt)]; tableMask = table.length - 1; // As we insert blocks we preincrement so that 0 is never a // valid entry. Therefore we have to allocate one extra space. // entries = new long[1 + worstCaseBlockCnt]; next = new int[entries.length]; scan(raw, len); } } private void scan(byte[] raw, final int end) { // We scan the input backwards, and always insert onto the // front of the chain. This ensures that chains will have lower // offsets at the front of the chain, allowing us to prefer the // earlier match rather than the later match. // int lastHash = 0; int ptr = end - DeltaIndex.BLKSZ; do { final int key = DeltaIndex.hashBlock(raw, ptr); final int tIdx = key & tableMask; final int head = table[tIdx]; if (head != 0 && lastHash == key) { // Two consecutive blocks have the same content hash, // prefer the earlier block because we want to use the // longest sequence we can during encoding. // entries[head] = (((long) key) << 32) | ptr; } else { final int eIdx = ++entryCnt; entries[eIdx] = (((long) key) << 32) | ptr; next[eIdx] = head; table[tIdx] = eIdx; } lastHash = key; ptr -= DeltaIndex.BLKSZ; } while (0 <= ptr); } private static int tableSize(final int worstCaseBlockCnt) { int shift = 32 - Integer.numberOfLeadingZeros(worstCaseBlockCnt); int sz = 1 << (shift - 1); if (sz < worstCaseBlockCnt) sz <<= 1; return sz; } }