/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Nov 5, 2006 */ package com.bigdata.io; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.zip.Adler32; /** * Utility class for computing the {@link Adler32} checksum of a buffer. This * class is NOT thread-safe. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ * * @todo This could stand some review in its API and its usage. */ public class ChecksumUtility { /** * ThreadLocal {@link ChecksumUtility} factory. */ public static final ThreadLocal<ChecksumUtility> threadChk = new ThreadLocal<ChecksumUtility>() { protected ChecksumUtility initialValue() { return new ChecksumUtility(); } }; /** * static access to a ThreadLocal Checksum utility * * @return the ChecksumUtility */ public static ChecksumUtility getCHK() { return threadChk.get(); // ChecksumUtility chk = (ChecksumUtility) threadChk.get(); // // if (chk == null) { // chk = new ChecksumUtility(); // threadChk.set(chk); // } // // return chk; } /** * Used to compute the checksums. Exposed to subclasses so they can update * the checksum for additional fields. */ protected final Adler32 chk = new Adler32(); /** * Compute the {@link Adler32} checksum of the buffer. The position, mark, * and limit are unchanged by this operation. The operation is optimized * when the buffer is backed by an array. * * @param buf * The buffer. * * @return The checksum. */ public int checksum(final ByteBuffer buf) { return checksum(buf, buf.position(), buf.limit()); } /** * Compute the {@link Adler32} checksum of the buffer. The position, * mark, and limit are unchanged by this operation. The operation is * optimized when the buffer is backed by an array. * * @param buf * The buffer. * @param pos * The starting position. * @param limit * The limit. * * @return The checksum. * * @todo why pass in the pos/lim when [buf] could incorporate them? */ public int checksum(final ByteBuffer buf, final int pos, final int limit) { assert buf != null; assert pos >= 0; assert limit > pos; // reset before computing the checksum. chk.reset(); // update the checksum. update(buf, pos, limit); // The Adler checksum is a 32-bit value. return (int) chk.getValue(); } /* * protected update methods. */ /** * Reset the checksum. */ protected void reset() { chk.reset(); } /** * Return the Alder checksum, which is a 32bit value. */ protected int getChecksum() { return (int) chk.getValue(); } /** * Updates the {@link Adler32} checksum from the data in the buffer. The * position, mark, and limit are unchanged by this operation. The operation * is optimized when the buffer is backed by an array. * * @param buf */ protected void update(final ByteBuffer buf) { update(buf, buf.position(), buf.limit()); } /** * Core implementation updates the {@link Adler32} checksum from the data in * the buffer. The position, mark, and limit are unchanged by this * operation. The operation is optimized when the buffer is backed by an * array. * * @param buf * @param pos * @param limit */ protected void update(final ByteBuffer buf, final int pos, final int limit) { assert buf != null; assert pos >= 0; assert limit > pos; // reset before computing the checksum. // chk.reset(); if (buf.hasArray()) { /* * Optimized when the buffer is backed by an array. */ final byte[] bytes = buf.array(); final int len = limit - pos; if (pos > bytes.length - len) { throw new BufferUnderflowException(); } chk.update(bytes, pos + buf.arrayOffset(), len); } else { /* * Compute the checksum of a byte[] on the native heap. * * @todo this should be a JNI call. The Adler32 class is already a * JNI implementation. */ if (a == null) { a = new byte[512]; } // isolate changes to (pos,limit). final ByteBuffer b = buf.asReadOnlyBuffer(); // stopping point. final int m = limit; // update checksum a chunk at a time. for (int p = pos; p < m; p += a.length) { // #of bytes to copy into the local byte[]. final int len = Math.min(m - p, a.length); // set the view b.limit(p + len); b.position(p); // copy into Java heap byte[], advancing b.position(). b.get(a, 0/* off */, len); // update the running checksum. chk.update(a, 0/* off */, len); } // This is WAY to expensive since it is a JNI call per byte. // // for (int i = pos; i < limit; i++) { // // chk.update(buf.get(i)); // // } } } /** * Private buffer used to incrementally compute the checksum of the data * as it is received. The purpose of this buffer is to take advantage of * more efficient bulk copy operations from the NIO buffer into a local * byte[] on the Java heap against which we then track the evolving * checksum of the data. */ private volatile byte[] a; public int checksum(final IByteArraySlice slice) { assert slice != null; // reset before computing the checksum. chk.reset(); chk.update(slice.array(), slice.off(), slice.len()); /* * The Adler checksum is a 32-bit value. */ return (int) chk.getValue(); } public int checksum(final byte[] buf) { return checksum(buf, 0, buf.length); } public int checksum(final byte[] buf, int sze) { return checksum(buf, 0, sze); } public int checksum(final byte[] buf, int off, int sze) { assert buf != null; // reset before computing the checksum. chk.reset(); chk.update(buf, off, sze); /* * The Adler checksum is a 32-bit value. */ return (int) chk.getValue(); } }