File: BitBuffer.h

package info (click to toggle)
maria 1.3.5-2
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 3,980 kB
  • ctags: 5,458
  • sloc: cpp: 43,402; yacc: 8,080; ansic: 436; sh: 404; lisp: 395; makefile: 291; perl: 21
file content (300 lines) | stat: -rw-r--r-- 9,364 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
// Buffer for packing bits and values -*- c++ -*-

#ifndef BITBUFFER_H_
# define BITBUFFER_H_
# ifdef __GNUC__
#  pragma interface
# endif // __GNUC__

/** @file BitBuffer.h
 * Encoding and decoding numbers and values in bit strings
 */

/* Copyright  1999-2002 Marko Mkel (msmakela@tcs.hut.fi).

   This file is part of MARIA, a reachability analyzer and model checker
   for high-level Petri nets.

   MARIA is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   MARIA 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.

   The GNU General Public License is often shipped with GNU software, and
   is generally kept in a file called COPYING or LICENSE.  If you do not
   have a copy of the license, write to the Free Software Foundation,
   59 Temple Place, Suite 330, Boston, MA 02111 USA. */

# include "typedefs.h"
# include <string.h>
# include <assert.h>
# if defined (__linux__)
#  include <endian.h>
#  if !defined BYTE_ORDER
#   define BYTE_ORDER __BYTE_ORDER
#   define LITTLE_ENDIAN __LITTLE_ENDIAN
#   define BIG_ENDIAN __BIG_ENDIAN
#  endif /* !defined BYTE_ORDER */
# elif defined (_AIX)
#  include <sys/machine.h>
# elif defined (__alpha)
#  include <machine/endian.h>
# elif defined (__sun) || defined (__hpux)
#  include <sys/byteorder.h>
#  if !defined (BYTE_ORDER)
#   define LITTLE_ENDIAN 1234
#   define BIG_ENDIAN 4321
#   if !defined (_LITTLE_ENDIAN) && !defined (_BIG_ENDIAN)
#    ifdef ntohl
#     define _LITTLE_ENDIAN
#    else
#     define _BIG_ENDIAN
#    endif
#   endif
#   if defined (_LITTLE_ENDIAN)
#    define BYTE_ORDER LITTLE_ENDIAN
#   elif defined (_BIG_ENDIAN)
#    define BYTE_ORDER BIG_ENDIAN
#   endif // little/big endian
#  endif // !defined(BYTE_ORDER)
# elif defined (__sgi)
#  include <standards.h>
#  include <sys/endian.h>
# elif !defined BYTE_ORDER
#  define LITTLE_ENDIAN 1234
#  define BIG_ENDIAN 4321
#  if defined __i386
#   define BYTE_ORDER LITTLE_ENDIAN
#  else
#   error "cannot determine the byte order"
#  endif
# endif

/** Machine word */
# if (BYTE_ORDER == BIG_ENDIAN || BYTE_ORDER == LITTLE_ENDIAN)
typedef unsigned long word_t;
# else // middle-endian
typedef unsigned char word_t;
# endif // BYTE_ORDER
/** Length of the machine word in bits */
# define WORD_T_BIT (CHAR_BIT * sizeof (word_t))

/** Buffer for packing bits */
class BitPacker
{
public:
  /** Constructor
   * @param increment	Buffer allocation granularity (in WORD_T_BITs)
   */
  explicit BitPacker (card_t increment = 16) :
    myBuf (0), myAllocated (0), myLength (0), myIncrement (increment) {}
private:
  /** Copy constructor */
  explicit BitPacker (const class BitPacker& old);
  /** Assignment operator */
  class BitPacker& operator= (const class BitPacker& old);
public:
  /** Destructor */
  ~BitPacker () { delete[] myBuf; }

  /** Append data to the buffer
   * @param data	Data to be appended
   * @param bits	Length of data in bits
   */
  void append (card_t data, unsigned bits) {
    assert (myIncrement);
    assert (bits && bits <= CARD_T_BIT);
    assert (bits == CARD_T_BIT || !(data >> bits));
    // enlarge the buffer if necessary
    if (myLength + bits > myAllocated * WORD_T_BIT) {
      do
	myAllocated += myIncrement;
      while (myLength + bits > myAllocated * WORD_T_BIT);
      word_t* buf = new word_t[myAllocated];
      size_t offset = (myLength + WORD_T_BIT - 1) / WORD_T_BIT;
      memcpy (buf, myBuf, offset * sizeof *buf);
      memset (buf + offset, 0, (myAllocated - offset) * sizeof *buf);
      delete[] myBuf;
      myBuf = buf;
    }

    // append the data to the buffer
    unsigned wordpos = myLength / WORD_T_BIT, bitpos = myLength % WORD_T_BIT;
    assert (!(myBuf[wordpos] >> bitpos));
    myLength += bits;
    myBuf[wordpos] |= word_t (data) << bitpos;
    if (bitpos + bits > WORD_T_BIT) {
      bitpos = WORD_T_BIT - bitpos;
      myBuf[++wordpos] = data >>= bitpos;
# if !(BYTE_ORDER == BIG_ENDIAN || BYTE_ORDER == LITTLE_ENDIAN)
      for (bits -= bitpos; bits > WORD_T_BIT; bits -= WORD_T_BIT)
	myBuf[++wordpos] = data >>= WORD_T_BIT;
# endif
    }
  }
  /** Remove data from the buffer
   * @param bits	number of bits to remove
   */
  void remove (unsigned bits) {
    assert (myLength >= bits);
    myLength -= bits;
    // calculate the word and bit offsets
    unsigned wordpos = myLength / WORD_T_BIT, bitpos = myLength % WORD_T_BIT;
    // clear the remaining bits of the last word
    if (bitpos)
      myBuf[wordpos] &= (word_t (1) << bitpos) - 1;
    // clear the remaining whole words
    memset (myBuf + wordpos, 0,
	    ((myLength + bits) / WORD_T_BIT - wordpos) * sizeof *myBuf);
  }

  /** Append a value to the buffer
   * @param value	Value to be appended
   */
  void append (const class Value& value);

  /** Clear the buffer */
  void clear () {
    myLength = 0;
    memset (myBuf, 0, myAllocated * sizeof *myBuf);
  }
  /** Determine whether the buffer is empty */
  bool empty () const { return myLength == 0; }
  /** Determine the length of the encoded data in bits */
  unsigned getLength () const { return myLength; }
  /** Determine the length of the encoded data in blocks of bits
   * @param size	the block size in bits
   * @return		number of blocks
   */
  unsigned getNumBlocks (unsigned size) const {
    return (myLength + size - 1) / size;
  }
  /** Determine the length of the encoded data in bytes */
  unsigned getNumBytes () const { return getNumBlocks (CHAR_T_BIT); }
  /** Determine the length of the encoded data in words */
  unsigned getNumWords () const { return getNumBlocks (WORD_T_BIT); }

  /** Get the buffer contents */
  const word_t* getBuf () const { return myBuf; }

  /** Remove leading zero bytes from the last word */
  void deflate () {
# if BYTE_ORDER == BIG_ENDIAN
    if (myLength % WORD_T_BIT)
      deflate (myBuf[myLength / WORD_T_BIT],
	       unsigned (-getNumBytes ()) % sizeof (word_t));
# endif // big endian
  }

  /** Restore leading zero bytes to the last word */
  void inflate () {
# if BYTE_ORDER == BIG_ENDIAN
    if (myLength % WORD_T_BIT)
      inflate (myBuf[myLength / WORD_T_BIT],
	       unsigned (-getNumBytes ()) % sizeof (word_t));
# endif // big endian
  }

  /** Remove leading zero bytes from a word
   * @param word	word to be deflated
   * @param bytes	number of zero bytes to remove
   */
  inline static void deflate (word_t& word, unsigned bytes) {
    assert (bytes < sizeof (word_t));
    assert (!bytes ||
	    !(word & ~((word_t (1) << (WORD_T_BIT - CHAR_BIT * bytes)) - 1)));
# if BYTE_ORDER == BIG_ENDIAN
    word <<= CHAR_BIT * bytes;
# endif // big endian
  }

  /** Pad a deflated word with leading zero bytes
   * @param word	word to be inflated
   * @param bytes	number of zero bytes to add
   */
  inline static void inflate (word_t& word, unsigned bytes) {
    assert (bytes < sizeof (word_t));
# if BYTE_ORDER == LITTLE_ENDIAN
    if (bytes)
      word &= (word_t (1) << (WORD_T_BIT - CHAR_BIT * bytes)) - 1;
# elif BYTE_ORDER == BIG_ENDIAN
    word >>= CHAR_BIT * bytes;
# endif
  }

private:
  /** The buffer */
  word_t* myBuf;
  /** Actual size of the buffer in WORD_T_BITs */
  unsigned myAllocated;
  /** Number of bits used of the buffer */
  unsigned myLength;
  /** Amount of WORD_T_BITs to allocate when more memory is needed */
  unsigned myIncrement;
};

/** Class for unpacking bits from a read-only buffer */
class BitUnpacker
{
public:
  /** Constructor for read-only access
   * @param buf		a previously encoded string (read-only)
   */
  explicit BitUnpacker (const word_t* buf) : myBuf (buf), myOffset (0) {}
private:
  /** Copy constructor */
  explicit BitUnpacker (const class BitUnpacker& old);
  /** Assignment operator */
  class BitUnpacker& operator= (const class BitUnpacker& old);
public:
  /** Destructor */
  ~BitUnpacker () {}

  /** Read the number of decoded bits */
  unsigned getNumBits () const { return myOffset; }

  /** Extract data from the encoded string
   * @param bits	length of data in bits
   * @return		the data
   */
  card_t extract (unsigned bits) {
    assert (bits && bits <= CARD_T_BIT);
    unsigned wordpos = myOffset / WORD_T_BIT, bitpos = myOffset % WORD_T_BIT;
    myOffset += bits;

    const card_t mask =
      bits < CARD_T_BIT ? (card_t (1) << bits) - 1 : card_t (-1);
    card_t data = (myBuf[wordpos] >> bitpos) & mask;
    if (bitpos + bits > WORD_T_BIT) {
      bitpos = WORD_T_BIT - bitpos;
      data |= (card_t (myBuf[++wordpos]) << bitpos) & mask;
# if !(BYTE_ORDER == BIG_ENDIAN || BYTE_ORDER == LITTLE_ENDIAN)
      for (bits -= bitpos, bitpos = WORD_T_BIT;
	   bits > WORD_T_BIT;
	   bits -= WORD_T_BIT, bitpos += WORD_T_BIT)
	data |= (card_t (myBuf[++wordpos]) << bitpos) & mask;
# endif
    }

    return data;
  }

  /** Extract a value from the encoded string
   * @param type	type of the value
   * @return		the value
   */
  class Value* extract (const class Type& type);

private:
  /** the encoded bit string */
  const word_t* const myBuf;
  /** offset to the encoded string */
  unsigned myOffset;
};

#endif // BITBUFFER_H_