001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.compress.archivers.zip;
020
021import java.io.Serializable;
022
023import org.apache.commons.compress.utils.ByteUtils;
024import org.apache.commons.lang3.ArrayUtils;
025
026/**
027 * Utility class that represents a two byte integer with conversion rules for the little-endian byte order of ZIP files.
028 *
029 * @Immutable
030 */
031public final class ZipShort implements Cloneable, Serializable {
032
033    private static final int SIZE = 2;
034
035    /**
036     * ZipShort with a value of 0.
037     *
038     * @since 1.14
039     */
040    public static final ZipShort ZERO = new ZipShort(0);
041
042    private static final long serialVersionUID = 1L;
043
044    /**
045     * Gets value as two bytes in big-endian byte order.
046     *
047     * @param value the Java int to convert to bytes
048     * @return the converted int as a byte array in big-endian byte order
049     */
050    public static byte[] getBytes(final int value) {
051        final byte[] result = new byte[SIZE];
052        putShort(value, result, 0);
053        return result;
054    }
055
056    /**
057     * Helper method to get the value as a Java int from a two-byte array
058     *
059     * @param bytes the array of bytes
060     * @return the corresponding Java int value
061     */
062    public static int getValue(final byte[] bytes) {
063        return getValue(bytes, 0);
064    }
065
066    /**
067     * Helper method to get the value as a Java int from two bytes starting at given array offset
068     *
069     * @param bytes  the array of bytes
070     * @param offset the offset to start
071     * @return the corresponding Java int value
072     */
073    public static int getValue(final byte[] bytes, final int offset) {
074        return (int) ByteUtils.fromLittleEndian(bytes, offset, SIZE);
075    }
076
077    static ZipShort lengthOf(final byte[] array) {
078        return new ZipShort(ArrayUtils.getLength(array));
079    }
080
081    /**
082     * put the value as two bytes in big-endian byte order.
083     *
084     * @param value  the Java int to convert to bytes
085     * @param buf    the output buffer
086     * @param offset The offset within the output buffer of the first byte to be written. must be non-negative and no larger than {@code buf.length-2}
087     */
088    public static void putShort(final int value, final byte[] buf, final int offset) {
089        ByteUtils.toLittleEndian(buf, value, offset, SIZE);
090    }
091
092    private final int value;
093
094    /**
095     * Constructs a new instance from bytes.
096     *
097     * @param bytes the bytes to store as a ZipShort
098     */
099    public ZipShort(final byte[] bytes) {
100        this(bytes, 0);
101    }
102
103    /**
104     * Constructs a new instance from the two bytes starting at offset.
105     *
106     * @param bytes  the bytes to store as a ZipShort
107     * @param offset the offset to start
108     */
109    public ZipShort(final byte[] bytes, final int offset) {
110        value = getValue(bytes, offset);
111    }
112
113    /**
114     * Constructs a new instance from a number.
115     *
116     * @param value the int to store as a ZipShort
117     */
118    public ZipShort(final int value) {
119        this.value = value;
120    }
121
122    @Override
123    public Object clone() {
124        try {
125            return super.clone();
126        } catch (final CloneNotSupportedException cnfe) {
127            // impossible
128            throw new UnsupportedOperationException(cnfe); // NOSONAR
129        }
130    }
131
132    /**
133     * Override to make two instances with same value equal.
134     *
135     * @param o an object to compare
136     * @return true if the objects are equal
137     */
138    @Override
139    public boolean equals(final Object o) {
140        if (!(o instanceof ZipShort)) {
141            return false;
142        }
143        return value == ((ZipShort) o).getValue();
144    }
145
146    /**
147     * Gets value as two bytes in big-endian byte order.
148     *
149     * @return the value as a two byte array in big-endian byte order
150     */
151    public byte[] getBytes() {
152        final byte[] result = new byte[SIZE];
153        ByteUtils.toLittleEndian(result, value, 0, SIZE);
154        return result;
155    }
156
157    /**
158     * Gets value as Java int.
159     *
160     * @return value as a Java int
161     */
162    public int getValue() {
163        return value;
164    }
165
166    /**
167     * Override to make two instances with same value equal.
168     *
169     * @return the value stored in the ZipShort
170     */
171    @Override
172    public int hashCode() {
173        return value;
174    }
175
176    @Override
177    public String toString() {
178        return "ZipShort value: " + value;
179    }
180}