/*
 * 
 * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * The contents of this file are subject to the Sun Community Source
 * License v3.0/Jini Technology Specific Attachment v 1.0 (the
 * "License"). You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 * http://wwws.sun.com/software/jini/licensing/SCSL3_JiniTSA1.html.
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Reference Code is Jini Technology Core Platform code, v2.0. The
 * Developer of the Reference Code is Sun Microsystems, Inc.
 * 
 * Contributor(s): Sun Microsystems, Inc.
 * 
 * The contents of this file comply with the Jini Technology Core
 * Platform Compatibility Kit, v2.0A.
 * 
 * Tester(s): Sun Microsystems, Inc.
 * 
 * Test Platform(s):
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Solaris(TM)SPARC/x86
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Linux (Intel x86)
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Windows (Intel Platform)
 * 
 */
package com.sun.jini.outrigger.logstore;

import java.util.Arrays;
import net.jini.id.Uuid;
import net.jini.id.UuidFactory;

/**
 * In the backend <code>Uuid</code>s are represented using
 * <code>byte[16]</code>s. This works fine is most places, but
 * sometimes we need to use a <code>Uuid</code> as a key in a hash
 * table. Arrays do not make good hash table keys so we wrap the array
 * in one of these to provide suitable implementations of
 * <code>hashCode</code> and <code>equals</code>.
 * <p> 
 * This method also has utility methods for converting
 * <code>Uuid</code>s to and from <code>byte[16]</code>s.
 */
class ByteArrayWrapper {
    /** The 16 bytes being wrapped */
    private byte[] uuid;

    /** A 32 bit hash of uuid */
    private int hash;

    /** 
     * Create a new <code>ByteArrayWrapper</code> that
     * wraps the provided array.
     * @param v The array to wrap.
     * @throws IllegalArgumentException if <code>v.length</code> is
     * not 16.
     */
    ByteArrayWrapper(byte v[]) {
	uuid = v;
	// Same hash Uuid uses
	hash = hashFor(v);
    }
    
    public boolean equals(Object o) {
	if (!(o instanceof ByteArrayWrapper))
	    return false;

	if (o == null)
	    return false;

	final byte[] ouuid = ((ByteArrayWrapper)o).uuid;
	return Arrays.equals(uuid, ouuid);
    }

    public int hashCode() {
	return hash;
    }

    /**
     * Encode the passed <code>Uuid</code> in to a newly allocated
     * <code>byte[16]</code> in big-endian byte order.
     * @param uuid the <code>Uuid</code> to encode.
     * @return A new <code>byte[16]</code> initialized to the
     *         same bit pattern as <code>uuid</code>.
     * @throws NullPointerException if <code>uuid</code> is 
     *         <code>null</code>.    
     */
    static byte[] toByteArray(Uuid uuid) {
	final byte rslt[] = new byte[16];
	long bits0 = uuid.getMostSignificantBits();
	for (int i=7; i>=0; i--) {
	    rslt[i] = (byte)bits0;	    
	    bits0 = bits0 >>> 8;
	}

	long bits1 = uuid.getLeastSignificantBits();
	for (int i=15; i>=8; i--) {
	    rslt[i] = (byte)bits1;	    
	    bits1 = bits1 >>> 8;
	}

	return rslt;
    }

    /**
     * Create a new <code>Uuid</code> that matches the bit pattern 
     * in the passed <code>byte[]</code>. Assumes the bit pattern
     * is in big-endian byte order.
     * @param bits the <code>byte[]</code> with bit pattern
     *
     * @return A new <code>Uuid</code> that matches the 
     *         passed <code>byte[]</code>.
     * @throws NullPointerException if <code>uuid</code> is 
     *         <code>null</code>.    
     * @throws IllegalArgumentException if <code>bits.length</code>
     *         is not 16.
     */
    static Uuid toUuid(byte bits[]) {
	if (bits.length != 16)
	    throw new IllegalArgumentException("uuid.length must be 16");

	long bits0 = 0;
	for (int i=0; i<7; i++) {
	    bits0 = bits0 | (bits[i] & 0xFF);
	    bits0 = bits0 << 8;
	}
	bits0 = bits0 | (bits[7] & 0xFF);

	long bits1 = 0;
	for (int i=8; i<15; i++) {
	    bits1 = bits1 | (bits[i] & 0xFF);
	    bits1 = bits1 << 8;
	}
	bits1 = bits1 | (bits[15] & 0xFF);

	return UuidFactory.create(bits0, bits1);
    }

    /** 
     * Compute an equivalent hash to <code>Uuid</code>.
     * @throws IllegalArgumentException if <code>uuid.length</code>
     *         is not 16.
     */
    static int hashFor(byte uuid[]) {
	if (uuid.length != 16) 
	    throw new IllegalArgumentException("uuid.length must be 16");
	return 
	    ((uuid[15] ^ uuid[11] ^ uuid[7] ^ uuid[3]) << 24) &
	    ((uuid[14] ^ uuid[10] ^ uuid[6] ^ uuid[2]) << 16) &
	    ((uuid[13] ^ uuid[9]  ^ uuid[5] ^ uuid[1]) << 8) &
	    ((uuid[12] ^ uuid[8]  ^ uuid[4] ^ uuid[0]) << 0);	    
    }
}
