/* * Copyright 2007-2010 Sun Microsystems, Inc. * * This file is part of Project Darkstar Server. * * Project Darkstar Server is free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation and * distributed hereunder to you. * * Project Darkstar Server is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * -- */ package com.sun.sgs.impl.util; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Provides utilities for reading and writing 30-bit integers in a compressed * format that is smaller for smaller integers. Does not support negative * integers or values above 2^30 - 1 or {@value #MAX_VALUE}. <p> * * The high order two bits of the first byte specify the number of additional * bytes beyond the first byte that will be needed to store the entire integer. * The bottom 6 bits of the first byte hold the highest order bits of the * integer, with the following bytes holding the remaining bytes. <p> * * Values less than 2^6 are represented by just storing the value in a single * byte, since the top two marking bits are zero: <p> * * <ul> * <li> 53 (0x35) => 0x35 * </ul> <p> * * Values less than 2^14 are represented in two bytes, with the top two bits of * the first byte set to 01 binary: <p> * * <ul> * <li> 4000 (0xfa0) => 0x4f 0xa0 * </ul> <p> * * Values less than 2^22 take three bytes, with 10 binary in the top bits, and * less than 2^30 take four, with 11 binary in the top bits: <p> * * <ul> * <li> 123456 (0x1e240) => 0x81 0xe2 0x40 * <li> 123456789 (0x75bcd15) => 0xc7 0x5b 0xcd 0x15 * <li> 456789012 (0x1b3a0c14) => 0xdb 0x3a 0x0c 0x14 * </ul> <p> */ public final class Int30 { /** The maximum integer that can be encoded. */ public static final int MAX_VALUE = (1 << 30) - 1; /* * These fields represent the integer bits that are not allowed to be set * in order to represent the integer in the specified number of bytes. */ private static final int FORBIDDEN4 = 0xc0000000; private static final int FORBIDDEN3 = 0xffc00000; private static final int FORBIDDEN2 = 0xffffc000; private static final int FORBIDDEN1 = 0xffffffc0; /* * These fields represent the value stored in the upper two bits of the * first byte that says how many bytes follow. */ private static final byte MARKER3 = (byte) 0xc0; private static final byte MARKER2 = (byte) 0x80; private static final byte MARKER1 = (byte) 0x40; /** This class should not be instantiated. */ private Int30() { } /** * Writes a 30-bit integer to an output stream. * * @param n the integer * @param out the output stream * @throws IllegalArgumentException if {@code n} is less than {@code 0} or * greater than {@value #MAX_VALUE} * @throws IOException if an I/O error occurs */ public static void write(int n, OutputStream out) throws IOException { if ((n & FORBIDDEN4) != 0) { throw new IllegalArgumentException("Bad argument: " + n); } else if ((n & FORBIDDEN3) != 0) { out.write(MARKER3 | (n >>> 24)); out.write((n >>> 16) & 0xff); out.write((n >>> 8) & 0xff); out.write(n & 0xff); } else if ((n & FORBIDDEN2) != 0) { out.write(MARKER2 | (n >>> 16)); out.write((n >>> 8) & 0xff); out.write(n & 0xff); } else if ((n & FORBIDDEN1) != 0) { out.write(MARKER1 | (n >>> 8)); out.write(n & 0xff); } else { out.write(n & 0xff); } } /** * Reads a 30-bit integer from an input stream. * * @param in the input stream * @return the integer * @throws IOException if an I/O error occurs */ public static int read(InputStream in) throws IOException { int b = in.read(); if (b == -1) { throw new EOFException("Encountered end of file"); } int count = b >>> 6; int n = b & 0x3f; for (int i = 0; i < count; i++) { n <<= 8; b = in.read(); if (b == -1) { throw new EOFException("Encountered end of file"); } n += (b & 0xff); } return n; } }