001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one or more
003 *  contributor license agreements.  See the NOTICE file distributed with
004 *  this work for additional information regarding copyright ownership.
005 *  The ASF licenses this file to You under the Apache License, Version 2.0
006 *  (the "License"); you may not use this file except in compliance with
007 *  the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 *
017 */
018package org.apache.commons.compress.archivers.zip;
019
020import java.io.Serializable;
021import java.math.BigInteger;
022
023import static org.apache.commons.compress.archivers.zip.ZipConstants.BYTE_MASK;
024
025/**
026 * Utility class that represents an eight byte integer with conversion
027 * rules for the little endian byte order of ZIP files.
028 * @Immutable
029 *
030 * @since 1.2
031 */
032public final class ZipEightByteInteger implements Serializable {
033    private static final long serialVersionUID = 1L;
034
035    private static final int BYTE_1 = 1;
036    private static final int BYTE_1_MASK = 0xFF00;
037    private static final int BYTE_1_SHIFT = 8;
038
039    private static final int BYTE_2 = 2;
040    private static final int BYTE_2_MASK = 0xFF0000;
041    private static final int BYTE_2_SHIFT = 16;
042
043    private static final int BYTE_3 = 3;
044    private static final long BYTE_3_MASK = 0xFF000000L;
045    private static final int BYTE_3_SHIFT = 24;
046
047    private static final int BYTE_4 = 4;
048    private static final long BYTE_4_MASK = 0xFF00000000L;
049    private static final int BYTE_4_SHIFT = 32;
050
051    private static final int BYTE_5 = 5;
052    private static final long BYTE_5_MASK = 0xFF0000000000L;
053    private static final int BYTE_5_SHIFT = 40;
054
055    private static final int BYTE_6 = 6;
056    private static final long BYTE_6_MASK = 0xFF000000000000L;
057    private static final int BYTE_6_SHIFT = 48;
058
059    private static final int BYTE_7 = 7;
060    private static final long BYTE_7_MASK = 0x7F00000000000000L;
061    private static final int BYTE_7_SHIFT = 56;
062
063    private static final int LEFTMOST_BIT_SHIFT = 63;
064    private static final byte LEFTMOST_BIT = (byte) 0x80;
065
066    private final BigInteger value;
067
068    public static final ZipEightByteInteger ZERO = new ZipEightByteInteger(0);
069
070    /**
071     * Create instance from a number.
072     * @param value the long to store as a ZipEightByteInteger
073     */
074    public ZipEightByteInteger(final long value) {
075        this(BigInteger.valueOf(value));
076    }
077
078    /**
079     * Create instance from a number.
080     * @param value the BigInteger to store as a ZipEightByteInteger
081     */
082    public ZipEightByteInteger(final BigInteger value) {
083        this.value = value;
084    }
085
086    /**
087     * Create instance from bytes.
088     * @param bytes the bytes to store as a ZipEightByteInteger
089     */
090    public ZipEightByteInteger (final byte[] bytes) {
091        this(bytes, 0);
092    }
093
094    /**
095     * Create instance from the eight bytes starting at offset.
096     * @param bytes the bytes to store as a ZipEightByteInteger
097     * @param offset the offset to start
098     */
099    public ZipEightByteInteger (final byte[] bytes, final int offset) {
100        value = ZipEightByteInteger.getValue(bytes, offset);
101    }
102
103    /**
104     * Get value as eight bytes in big endian byte order.
105     * @return value as eight bytes in big endian order
106     */
107    public byte[] getBytes() {
108        return ZipEightByteInteger.getBytes(value);
109    }
110
111    /**
112     * Get value as Java long.
113     * @return value as a long
114     */
115    public long getLongValue() {
116        return value.longValue();
117    }
118
119    /**
120     * Get value as Java long.
121     * @return value as a long
122     */
123    public BigInteger getValue() {
124        return value;
125    }
126
127    /**
128     * Get value as eight bytes in big endian byte order.
129     * @param value the value to convert
130     * @return value as eight bytes in big endian byte order
131     */
132    public static byte[] getBytes(final long value) {
133        return getBytes(BigInteger.valueOf(value));
134    }
135
136    /**
137     * Get value as eight bytes in big endian byte order.
138     * @param value the value to convert
139     * @return value as eight bytes in big endian byte order
140     */
141    public static byte[] getBytes(final BigInteger value) {
142        final byte[] result = new byte[8];
143        final long val = value.longValue();
144        result[0] = (byte) ((val & BYTE_MASK));
145        result[BYTE_1] = (byte) ((val & BYTE_1_MASK) >> BYTE_1_SHIFT);
146        result[BYTE_2] = (byte) ((val & BYTE_2_MASK) >> BYTE_2_SHIFT);
147        result[BYTE_3] = (byte) ((val & BYTE_3_MASK) >> BYTE_3_SHIFT);
148        result[BYTE_4] = (byte) ((val & BYTE_4_MASK) >> BYTE_4_SHIFT);
149        result[BYTE_5] = (byte) ((val & BYTE_5_MASK) >> BYTE_5_SHIFT);
150        result[BYTE_6] = (byte) ((val & BYTE_6_MASK) >> BYTE_6_SHIFT);
151        result[BYTE_7] = (byte) ((val & BYTE_7_MASK) >> BYTE_7_SHIFT);
152        if (value.testBit(LEFTMOST_BIT_SHIFT)) {
153            result[BYTE_7] |= LEFTMOST_BIT;
154        }
155        return result;
156    }
157
158    /**
159     * Helper method to get the value as a Java long from eight bytes
160     * starting at given array offset
161     * @param bytes the array of bytes
162     * @param offset the offset to start
163     * @return the corresponding Java long value
164     */
165    public static long getLongValue(final byte[] bytes, final int offset) {
166        return getValue(bytes, offset).longValue();
167    }
168
169    /**
170     * Helper method to get the value as a Java BigInteger from eight
171     * bytes starting at given array offset
172     * @param bytes the array of bytes
173     * @param offset the offset to start
174     * @return the corresponding Java BigInteger value
175     */
176    public static BigInteger getValue(final byte[] bytes, final int offset) {
177        long value = ((long) bytes[offset + BYTE_7] << BYTE_7_SHIFT) & BYTE_7_MASK;
178        value += ((long) bytes[offset + BYTE_6] << BYTE_6_SHIFT) & BYTE_6_MASK;
179        value += ((long) bytes[offset + BYTE_5] << BYTE_5_SHIFT) & BYTE_5_MASK;
180        value += ((long) bytes[offset + BYTE_4] << BYTE_4_SHIFT) & BYTE_4_MASK;
181        value += ((long) bytes[offset + BYTE_3] << BYTE_3_SHIFT) & BYTE_3_MASK;
182        value += ((long) bytes[offset + BYTE_2] << BYTE_2_SHIFT) & BYTE_2_MASK;
183        value += ((long) bytes[offset + BYTE_1] << BYTE_1_SHIFT) & BYTE_1_MASK;
184        value += ((long) bytes[offset] & BYTE_MASK);
185        final BigInteger val = BigInteger.valueOf(value);
186        return (bytes[offset + BYTE_7] & LEFTMOST_BIT) == LEFTMOST_BIT
187            ? val.setBit(LEFTMOST_BIT_SHIFT) : val;
188    }
189
190    /**
191     * Helper method to get the value as a Java long from an eight-byte array
192     * @param bytes the array of bytes
193     * @return the corresponding Java long value
194     */
195    public static long getLongValue(final byte[] bytes) {
196        return getLongValue(bytes, 0);
197    }
198
199    /**
200     * Helper method to get the value as a Java long from an eight-byte array
201     * @param bytes the array of bytes
202     * @return the corresponding Java BigInteger value
203     */
204    public static BigInteger getValue(final byte[] bytes) {
205        return getValue(bytes, 0);
206    }
207
208    /**
209     * Override to make two instances with same value equal.
210     * @param o an object to compare
211     * @return true if the objects are equal
212     */
213    @Override
214    public boolean equals(final Object o) {
215        if (o == null || !(o instanceof ZipEightByteInteger)) {
216            return false;
217        }
218        return value.equals(((ZipEightByteInteger) o).getValue());
219    }
220
221    /**
222     * Override to make two instances with same value equal.
223     * @return the hashCode of the value stored in the ZipEightByteInteger
224     */
225    @Override
226    public int hashCode() {
227        return value.hashCode();
228    }
229
230    @Override
231    public String toString() {
232        return "ZipEightByteInteger value: " + value;
233    }
234}