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 */
017package org.apache.commons.compress.archivers.zip;
018
019import static org.apache.commons.compress.archivers.zip.ZipConstants.BYTE_MASK;
020
021import java.io.Serializable;
022import java.math.BigInteger;
023
024/**
025 * Utility class that represents an eight byte integer with conversion rules for the little-endian byte order of ZIP files.
026 *
027 * @Immutable
028 *
029 * @since 1.2
030 */
031public final class ZipEightByteInteger implements Serializable {
032    private static final long serialVersionUID = 1L;
033
034    private static final int BYTE_1 = 1;
035    private static final int BYTE_1_MASK = 0xFF00;
036    private static final int BYTE_1_SHIFT = 8;
037
038    private static final int BYTE_2 = 2;
039    private static final int BYTE_2_MASK = 0xFF0000;
040    private static final int BYTE_2_SHIFT = 16;
041
042    private static final int BYTE_3 = 3;
043    private static final long BYTE_3_MASK = 0xFF000000L;
044    private static final int BYTE_3_SHIFT = 24;
045
046    private static final int BYTE_4 = 4;
047    private static final long BYTE_4_MASK = 0xFF00000000L;
048    private static final int BYTE_4_SHIFT = 32;
049
050    private static final int BYTE_5 = 5;
051    private static final long BYTE_5_MASK = 0xFF0000000000L;
052    private static final int BYTE_5_SHIFT = 40;
053
054    private static final int BYTE_6 = 6;
055    private static final long BYTE_6_MASK = 0xFF000000000000L;
056    private static final int BYTE_6_SHIFT = 48;
057
058    private static final int BYTE_7 = 7;
059    private static final long BYTE_7_MASK = 0x7F00000000000000L;
060    private static final int BYTE_7_SHIFT = 56;
061
062    private static final int LEFTMOST_BIT_SHIFT = 63;
063    private static final byte LEFTMOST_BIT = (byte) 0x80;
064
065    public static final ZipEightByteInteger ZERO = new ZipEightByteInteger(0);
066
067    /**
068     * Gets value as eight bytes in big-endian byte order.
069     *
070     * @param value the value to convert
071     * @return value as eight bytes in big-endian byte order
072     */
073    public static byte[] getBytes(final BigInteger value) {
074        final byte[] result = new byte[8];
075        final long val = value.longValue();
076        result[0] = (byte) (val & BYTE_MASK);
077        result[BYTE_1] = (byte) ((val & BYTE_1_MASK) >> BYTE_1_SHIFT);
078        result[BYTE_2] = (byte) ((val & BYTE_2_MASK) >> BYTE_2_SHIFT);
079        result[BYTE_3] = (byte) ((val & BYTE_3_MASK) >> BYTE_3_SHIFT);
080        result[BYTE_4] = (byte) ((val & BYTE_4_MASK) >> BYTE_4_SHIFT);
081        result[BYTE_5] = (byte) ((val & BYTE_5_MASK) >> BYTE_5_SHIFT);
082        result[BYTE_6] = (byte) ((val & BYTE_6_MASK) >> BYTE_6_SHIFT);
083        result[BYTE_7] = (byte) ((val & BYTE_7_MASK) >> BYTE_7_SHIFT);
084        if (value.testBit(LEFTMOST_BIT_SHIFT)) {
085            result[BYTE_7] |= LEFTMOST_BIT;
086        }
087        return result;
088    }
089
090    /**
091     * Gets value as eight bytes in big-endian byte order.
092     *
093     * @param value the value to convert
094     * @return value as eight bytes in big-endian byte order
095     */
096    public static byte[] getBytes(final long value) {
097        return getBytes(BigInteger.valueOf(value));
098    }
099
100    /**
101     * Helper method to get the value as a Java long from an eight-byte array
102     *
103     * @param bytes the array of bytes
104     * @return the corresponding Java long value
105     */
106    public static long getLongValue(final byte[] bytes) {
107        return getLongValue(bytes, 0);
108    }
109
110    /**
111     * Helper method to get the value as a Java long from eight bytes starting at given array offset
112     *
113     * @param bytes  the array of bytes
114     * @param offset the offset to start
115     * @return the corresponding Java long value
116     */
117    public static long getLongValue(final byte[] bytes, final int offset) {
118        return getValue(bytes, offset).longValue();
119    }
120
121    /**
122     * Helper method to get the value as a Java long from an eight-byte array
123     *
124     * @param bytes the array of bytes
125     * @return the corresponding Java BigInteger value
126     */
127    public static BigInteger getValue(final byte[] bytes) {
128        return getValue(bytes, 0);
129    }
130
131    /**
132     * Helper method to get the value as a Java BigInteger from eight bytes starting at given array offset
133     *
134     * @param bytes  the array of bytes
135     * @param offset the offset to start
136     * @return the corresponding Java BigInteger value
137     */
138    public static BigInteger getValue(final byte[] bytes, final int offset) {
139        long value = (long) bytes[offset + BYTE_7] << BYTE_7_SHIFT & BYTE_7_MASK;
140        value += (long) bytes[offset + BYTE_6] << BYTE_6_SHIFT & BYTE_6_MASK;
141        value += (long) bytes[offset + BYTE_5] << BYTE_5_SHIFT & BYTE_5_MASK;
142        value += (long) bytes[offset + BYTE_4] << BYTE_4_SHIFT & BYTE_4_MASK;
143        value += (long) bytes[offset + BYTE_3] << BYTE_3_SHIFT & BYTE_3_MASK;
144        value += (long) bytes[offset + BYTE_2] << BYTE_2_SHIFT & BYTE_2_MASK;
145        value += (long) bytes[offset + BYTE_1] << BYTE_1_SHIFT & BYTE_1_MASK;
146        value += (long) bytes[offset] & BYTE_MASK;
147        final BigInteger val = BigInteger.valueOf(value);
148        return (bytes[offset + BYTE_7] & LEFTMOST_BIT) == LEFTMOST_BIT ? val.setBit(LEFTMOST_BIT_SHIFT) : val;
149    }
150
151    private final BigInteger value;
152
153    /**
154     * Constructs a new instance from a number.
155     *
156     * @param value the BigInteger to store as a ZipEightByteInteger
157     */
158    public ZipEightByteInteger(final BigInteger value) {
159        this.value = value;
160    }
161
162    /**
163     * Constructs a new instance from bytes.
164     *
165     * @param bytes the bytes to store as a ZipEightByteInteger
166     */
167    public ZipEightByteInteger(final byte[] bytes) {
168        this(bytes, 0);
169    }
170
171    /**
172     * Constructs a new instance from the eight bytes starting at offset.
173     *
174     * @param bytes  the bytes to store as a ZipEightByteInteger
175     * @param offset the offset to start
176     */
177    public ZipEightByteInteger(final byte[] bytes, final int offset) {
178        value = ZipEightByteInteger.getValue(bytes, offset);
179    }
180
181    /**
182     * Constructs a new instance from a number.
183     *
184     * @param value the long to store as a ZipEightByteInteger
185     */
186    public ZipEightByteInteger(final long value) {
187        this(BigInteger.valueOf(value));
188    }
189
190    /**
191     * Override to make two instances with same value equal.
192     *
193     * @param o an object to compare
194     * @return true if the objects are equal
195     */
196    @Override
197    public boolean equals(final Object o) {
198        if (!(o instanceof ZipEightByteInteger)) {
199            return false;
200        }
201        return value.equals(((ZipEightByteInteger) o).getValue());
202    }
203
204    /**
205     * Gets value as eight bytes in big-endian byte order.
206     *
207     * @return value as eight bytes in big-endian order
208     */
209    public byte[] getBytes() {
210        return ZipEightByteInteger.getBytes(value);
211    }
212
213    /**
214     * Gets value as Java long.
215     *
216     * @return value as a long
217     */
218    public long getLongValue() {
219        return value.longValue();
220    }
221
222    /**
223     * Gets value as Java BigInteger.
224     *
225     * @return value as a BigInteger
226     */
227    public BigInteger getValue() {
228        return value;
229    }
230
231    /**
232     * Override to make two instances with same value equal.
233     *
234     * @return the hash code of the value stored in the ZipEightByteInteger
235     */
236    @Override
237    public int hashCode() {
238        return value.hashCode();
239    }
240
241    @Override
242    public String toString() {
243        return "ZipEightByteInteger value: " + value;
244    }
245}