001    /*
002    Copyright (c) 1996-2011, Damon Hart-Davis
003    All rights reserved.
004    
005    Redistribution and use in source and binary forms, with or without
006    modification, are permitted provided that the following conditions are
007    met:
008    
009      * Redistributions of source code must retain the above copyright
010        notice, this list of conditions and the following disclaimer.
011    
012      * Redistributions in binary form must reproduce the above copyright
013        notice, this list of conditions and the following disclaimer in the
014        documentation and/or other materials provided with the
015        distribution.
016    
017    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
018    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
019    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
020    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
021    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
022    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
023    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
024    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
025    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
026    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
027    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028    */
029    package org.hd.d.pg2k.svrCore;
030    
031    import java.io.Serializable;
032    import java.util.Arrays;
033    
034    /**An immutable, Serializable, wrapper for an int array.
035     * This enables int array data to be safely shared between multiple users,
036     * thus avoid copies and duplicates.
037     * <p>
038     * We clone at entry and exit and deserialisation to protect our internal data.
039     * <p>
040     * These instances are suitable for pooling with MemoryTools.intern().
041     * <p>
042     * Intended to be efficient on the wire and in memory.
043     */
044    public final class ROIntArray implements Serializable, MemoryTools.Internable
045        {
046        /**Construct with (copy of) non-null data block.
047         * @param data  non-null array of bytes
048         */
049        public ROIntArray(final int[] data)
050            {
051            if(data == null) { throw new IllegalArgumentException(); }
052            payload = data.clone();
053            }
054    
055        /**The (non-null) payload reference. */
056        private final int[] payload;
057    
058        /**Get (a copy of) the entire data block. */
059        public int[] getData() { return(payload.clone()); }
060    
061        /**Get (a copy of) a portion of the data block.
062         */
063        public int[] getData(final int start, final int afterEnd)
064            {
065            final int length = afterEnd - start;
066            final int result[] = new int[length];
067            System.arraycopy(payload, start, result, 0, length);
068            return(result);
069            }
070    
071        /**Get length of the data; non-negative. */
072        public int length() { return(payload.length); }
073    
074        /**Get value at specified index. */
075        public int get(final int index) { return(payload[index]); }
076    
077        /**Compute a hash based on the length and some of the payload.
078         * This tries to fix the time taken to compute the hash
079         * even for large payloads at the cost of possible/probable extra collisions
080         * (as per the String experience).
081         * <p>
082         * This takes a fixed small number of sample points whose relative positions
083         * partly depend on the length, and the length itself is part of the hash.
084         */
085        @Override
086        public int hashCode()
087            {
088            final int len = payload.length;
089            final int dataHash = (len == 0) ? 907 :
090                (
091                (len * 0x303031) ^
092                payload[0] ^
093                (~payload[len/4] << 8) +
094                ((payload[((23603 + payload[len/5]) >>> 1) % len]) * 101) -
095                (payload[len-1] << 3)
096                );
097            return(dataHash);
098            }
099    
100        /**Is equal to an instance with a payload of the same length and content; may be slow to compute. */
101        @Override
102        public boolean equals(final Object obj)
103            {
104            if(this == obj) { return(true); }
105            if(!(obj instanceof ROIntArray)) { return(false); }
106            return(Arrays.equals(payload, ((ROIntArray)obj).payload));
107            }
108    
109    
110        /**Empty array. */
111        public static final ROIntArray EMPTY = new ROIntArray(new int[0]);
112    
113        /**Deserialise: use constructor for validation, defensive copying, etc.
114         * Also resolve all empty instances to a single value as a minor optimisation.
115         */
116        protected Object readResolve()
117            // throws ObjectStreamException
118            {
119            // Avoid duplicates of empty case.
120            if(payload.length == 0) { return(EMPTY); }
121    
122            // Construct new instance of object in normal defensive way.
123            return(new ROIntArray(payload));
124            }
125    
126        /**Unique Serialisation class ID. */
127        private static final long serialVersionUID = 4100310899376658916L;
128        }