Subversion Repositories mkgmap

Rev

Rev 3680 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (C) 2006 Steve Ratcliffe
 *
 *  This program 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.
 *
 *  This program 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.
 *
 *
 * Author: Steve Ratcliffe
 * Create date: 14-Dec-2006
 */

package uk.me.parabola.imgfmt.app;

import uk.me.parabola.log.Logger;

/**
 * A class to write the bitstream.
 *
 * @author Steve Ratcliffe
 */

public class BitWriter {
        private static final Logger log = Logger.getLogger(BitWriter.class);

        // Choose so that most roads will not fill it.
        private static final int INITIAL_BUF_SIZE = 20;

        // The byte buffer and its current length (allocated length)
        private byte[] buf;  // The buffer
        private int bufsize;  // The allocated size
        private int buflen; // The actual used length

        // The bit offset into the byte array.
        private int bitoff;
        private static final int BUFSIZE_INC = 50;

        public BitWriter() {
                bufsize = INITIAL_BUF_SIZE;
                buf = new byte[bufsize];
        }

        /**
         * Put exactly one bit into the buffer.
         *
         * @param b The bottom bit of the integer is set at the current bit position.
         */

        private void put1(int b) {
                ensureSize(bitoff + 1);

                int off = getByteOffset(bitoff);

                // Get the remaining bits into the byte.
                int rem = bitoff - 8 * off;

                // Or it in, we are assuming that the position is never turned back.
                buf[off] |= (b & 0x1) << rem;

                // Increment position
                bitoff++;

                // If we are in a new byte, increase the byte length.
                if ((bitoff & 0x7) == 1)
                        buflen++;

                debugPrint(b, 1);
        }
       
        public void put1(boolean b) {
                put1(b ? 1 : 0);
        }

        /**
         * Put a number of bits into the buffer, growing it if necessary.
         *
         * @param bval The bits to add, the lowest <b>n</b> bits will be added to
         * the buffer.
         * @param nb The number of bits.
         */

        public void putn(int bval, int nb) {
                int val = bval & ((1<<nb) - 1);
                int n = nb;

                // We need to be able to deal with more than 24 bits, but now we can't yet
                if (n >= 24)
                        throw new IllegalArgumentException();

                ensureSize(bitoff + n);

                // Get each affected byte and set bits into it until we are done.
                while (n > 0) {
                        int ind = getByteOffset(bitoff);
                        int rem = bitoff - 8*ind;

                        buf[ind] |= ((val << rem) & 0xff);

                        // Shift down in preparation for next byte.
                        val >>>= 8-rem;

                        // Account for change so far
                        int nput = 8 - rem;
                        if (nput > n)
                                nput = n;
                        bitoff += nput;
                        n -= nput;
                }

                buflen = (bitoff+7)/8;
        }
       
        /**
         * Write a signed value. If the value doesn't fit into nb bits, write one or more 1 << (nb-1)  
         * as a flag for extended range.
         */


        public void sputn(int bval, int nb) {
                int top = 1 << (nb - 1);
                int mask = top - 1;
                int val = Math.abs(bval);
                while (val > mask) {
                        putn(top, nb);
                        val -= mask;
                }
                if (bval < 0) {
                        putn((top - val) | top, nb);
                } else {
                        putn(val, nb);
                }
        }
       
        public byte[] getBytes() {
                return buf;
        }

        public int getBitPosition() {
                return bitoff;
        }
        /**
         * Get the number of bytes actually used to hold the bit stream. This therefore can be and usually
         * is less than the length of the buffer returned by {@link #getBytes()}.
         * @return Number of bytes required to hold the output.
         */

        public int getLength() {
                return buflen;
        }

        /**
         * Get the byte offset for the given bit number.
         *
         * @param boff The number of the bit in question.
         * @return The index into the byte array where the bit resides.
         */

        private int getByteOffset(int boff) {
                return boff/8;
        }

        /**
         * Set everything up so that the given size can be accommodated.
         * The buffer is re-sized if necessary.
         *
         * @param newlen The new length of the bit buffer in bits.
         */

        private void ensureSize(int newlen) {
                if (newlen/8 >= bufsize)
                        reallocBuffer();
        }

        /**
         * Reallocate the byte buffer.
         */

        private void reallocBuffer() {
                log.debug("reallocating buffer");
                bufsize += BUFSIZE_INC;
                byte[] newbuf = new byte[bufsize];

                System.arraycopy(this.buf, 0, newbuf, 0, this.buf.length);
                this.buf = newbuf;
        }

        private void debugPrint(int b, int i) {
                if (log.isDebugEnabled())
                        log.debug("after put", i, "of", b, " bufsize=", bufsize, ", len=",
                                        buflen, ", pos=", bitoff);
        }


}