    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}