bitpack 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +7 -0
- data/LICENSE +20 -0
- data/README +98 -0
- data/Rakefile +87 -0
- data/ext/bitpack.c +483 -0
- data/ext/bitpack.h +350 -0
- data/ext/bitpack_ext.c +662 -0
- data/ext/extconf.rb +8 -0
- data/lib/bitpack.rb +53 -0
- data/test/bitpack_tests.rb +405 -0
- metadata +57 -0
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 Corey Burrows
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
= BitPack: Library for packing and unpacking binary strings
|
2
|
+
|
3
|
+
BitPack is a library that provides an easy to use API for packing and
|
4
|
+
unpacking arbitrary binary strings. Unlike Array#pack and String#unpack,
|
5
|
+
BitPack objects allow you to pack and unpack fields of arbitrary bit lengths.
|
6
|
+
|
7
|
+
= Installing BitPack
|
8
|
+
|
9
|
+
Get BitPack from RubyForge.
|
10
|
+
|
11
|
+
$ gem install bitpack
|
12
|
+
|
13
|
+
= Example
|
14
|
+
|
15
|
+
In this example, we will see how to pack and unpack a contrived message format.
|
16
|
+
|
17
|
+
The format is as follows:
|
18
|
+
|
19
|
+
* field +foo+: unsigned integer, 3 bits in length
|
20
|
+
* field +bar+: unsigned integer, 13 bits in length
|
21
|
+
* field +baz+: octet string, +bar+ bytes in length
|
22
|
+
|
23
|
+
|
24
|
+
require 'rubygems'
|
25
|
+
require 'bitpack'
|
26
|
+
|
27
|
+
class Message
|
28
|
+
attr_accessor :foo, :bar, :baz
|
29
|
+
def initialize(foo=nil, bar=nil, baz=nil)
|
30
|
+
@foo = foo
|
31
|
+
@bar = bar
|
32
|
+
@baz = baz
|
33
|
+
end
|
34
|
+
|
35
|
+
def pack
|
36
|
+
# create a new BitPack object to pack the message into
|
37
|
+
bp = BitPack.new
|
38
|
+
|
39
|
+
# pack field foo into 3 bits
|
40
|
+
bp.append_bits(@foo, 3)
|
41
|
+
|
42
|
+
# pack field bar into 13 bits
|
43
|
+
bp.append_bits(@bar, 13)
|
44
|
+
|
45
|
+
# finally, pack baz as a string
|
46
|
+
bp.append_bytes(@baz)
|
47
|
+
|
48
|
+
# convert the BitPack to a string
|
49
|
+
bp.to_bytes
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.unpack(bytes)
|
53
|
+
m = self.new
|
54
|
+
|
55
|
+
# create a new BitPack from the packed message string
|
56
|
+
bp = BitPack.from_bytes(bytes)
|
57
|
+
|
58
|
+
# unpack field foo from the first 3 bits
|
59
|
+
m.foo = bp.read_bits(3)
|
60
|
+
|
61
|
+
# unpack field bar from the next 13 bits
|
62
|
+
m.bar = bp.read_bits(13)
|
63
|
+
|
64
|
+
# finally, unpack the string baz from the next bar bytes
|
65
|
+
m.baz = bp.read_bytes(m.bar)
|
66
|
+
|
67
|
+
m
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
m1 = Message.new
|
72
|
+
m1.foo = 5
|
73
|
+
s = "BitPack makes packing and unpacking binary strings easy!"
|
74
|
+
m1.bar = s.length
|
75
|
+
m1.baz = s
|
76
|
+
|
77
|
+
bytes = m1.pack
|
78
|
+
|
79
|
+
p bytes
|
80
|
+
#=> "\2408BitPack makes packing and unpacking binary strings easy!"
|
81
|
+
|
82
|
+
m2 = Message.unpack(bytes)
|
83
|
+
p m2.foo
|
84
|
+
#=> 5
|
85
|
+
p m2.bar
|
86
|
+
#=> 56
|
87
|
+
p m2.baz
|
88
|
+
#=> "BitPack makes packing and unpacking binary strings easy!"
|
89
|
+
|
90
|
+
|
91
|
+
= Notes
|
92
|
+
|
93
|
+
BitPack is almost entirely implemented as a C library. If you would like to
|
94
|
+
use BitPack from a C program, just grab the bitpack.c and bitpack.h files from
|
95
|
+
the ext/ directory of the gem and include them in your project. Documentation
|
96
|
+
can be found in bitpack.h and example usage can be seen in test/bitpack_tests.c.
|
97
|
+
|
98
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
Gem::manage_gems
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'rake/clean'
|
6
|
+
require 'rake/rdoctask'
|
7
|
+
|
8
|
+
NAME = 'bitpack'
|
9
|
+
VERS = '0.1'
|
10
|
+
GEM_NAME = "#{NAME}-#{VERS}.gem"
|
11
|
+
|
12
|
+
RDOC_MAIN = "README"
|
13
|
+
|
14
|
+
spec = Gem::Specification.new do |s|
|
15
|
+
s.name = NAME
|
16
|
+
s.version = VERS
|
17
|
+
s.author = "Corey Burrows"
|
18
|
+
s.email = "corey.burrows@gmail.com"
|
19
|
+
s.platform = Gem::Platform::RUBY
|
20
|
+
s.summary = "Library for packing and unpacking binary strings."
|
21
|
+
s.files = %w{README CHANGELOG LICENSE Rakefile} +
|
22
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
23
|
+
Dir.glob("lib/**/*.{rb}") +
|
24
|
+
Dir.glob("test/**/*.rb")
|
25
|
+
s.require_path = "."
|
26
|
+
s.autorequire = "bitpack"
|
27
|
+
s.extensions = ["ext/extconf.rb"]
|
28
|
+
#s.test_file = ""
|
29
|
+
s.has_rdoc = true
|
30
|
+
s.extra_rdoc_files = [ RDOC_MAIN, "CHANGELOG", "LICENSE" ]
|
31
|
+
end
|
32
|
+
|
33
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
34
|
+
pkg.need_tar = true
|
35
|
+
end
|
36
|
+
|
37
|
+
Rake::RDocTask.new do |rd|
|
38
|
+
rd.main = RDOC_MAIN
|
39
|
+
rd.rdoc_files.include(RDOC_MAIN, "CHANGELOG", "LICENSE", "ext/**/*.c", "lib/**/*.rb")
|
40
|
+
rd.options << "--all"
|
41
|
+
end
|
42
|
+
|
43
|
+
CLEAN.include FileList["ext/**/*.o",
|
44
|
+
"ext/**/*.so",
|
45
|
+
"ext/**/*.bundle",
|
46
|
+
"ext/**/Makefile",
|
47
|
+
"ext/**/mkmf.log",
|
48
|
+
"pkg/*.gem",
|
49
|
+
"test/*.o",
|
50
|
+
"test/test_driver",
|
51
|
+
"html"
|
52
|
+
]
|
53
|
+
|
54
|
+
task :build do
|
55
|
+
Dir.chdir('ext')
|
56
|
+
sh('ruby extconf.rb')
|
57
|
+
sh('make')
|
58
|
+
Dir.chdir('..')
|
59
|
+
end
|
60
|
+
|
61
|
+
task :c_test do
|
62
|
+
Dir.chdir('test')
|
63
|
+
sh('make')
|
64
|
+
Dir.chdir('..')
|
65
|
+
end
|
66
|
+
|
67
|
+
task :ruby_test do
|
68
|
+
Dir.chdir('test')
|
69
|
+
sh('ruby bitpack_tests.rb')
|
70
|
+
Dir.chdir('..')
|
71
|
+
end
|
72
|
+
|
73
|
+
task :test => [ :build, :c_test, :ruby_test ] do
|
74
|
+
end
|
75
|
+
|
76
|
+
task :gem do
|
77
|
+
sh %{rake pkg/#{GEM_NAME}}
|
78
|
+
end
|
79
|
+
|
80
|
+
task :install => :gem do
|
81
|
+
sh %{sudo gem install pkg/#{GEM_NAME}}
|
82
|
+
end
|
83
|
+
|
84
|
+
task :uninstall do
|
85
|
+
sh %{sudo gem uninstall #{NAME}}
|
86
|
+
end
|
87
|
+
|
data/ext/bitpack.c
ADDED
@@ -0,0 +1,483 @@
|
|
1
|
+
#include <math.h>
|
2
|
+
#include <stdio.h>
|
3
|
+
#include <stdlib.h>
|
4
|
+
#include <string.h>
|
5
|
+
|
6
|
+
#include "bitpack.h"
|
7
|
+
|
8
|
+
/* round up to the nearest multiple of 8 */
|
9
|
+
static unsigned long round8(unsigned long v)
|
10
|
+
{
|
11
|
+
if (v % 8 != 0) {
|
12
|
+
v += 8 - (v % 8);
|
13
|
+
}
|
14
|
+
|
15
|
+
return v;
|
16
|
+
}
|
17
|
+
|
18
|
+
/* clear any previous errors on a bitpack object */
|
19
|
+
static void _bitpack_err_clear(bitpack_t bp)
|
20
|
+
{
|
21
|
+
if (bp->error != BITPACK_ERR_CLEAR) {
|
22
|
+
bp->error = BITPACK_ERR_CLEAR;
|
23
|
+
memset(bp->error_str, '\0', BITPACK_ERR_BUF_SIZE);
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
/* increase the size of a bitpack object, allocating more memory if necessary */
|
28
|
+
static int _bitpack_resize(bitpack_t bp, unsigned long new_size)
|
29
|
+
{
|
30
|
+
unsigned long new_data_size = round8(new_size) / 8;
|
31
|
+
|
32
|
+
if (new_data_size > bp->data_size) {
|
33
|
+
bp->data = realloc(bp->data, new_data_size);
|
34
|
+
|
35
|
+
if (bp->data == NULL) {
|
36
|
+
bp->error = BITPACK_ERR_MALLOC_FAILED;
|
37
|
+
strncpy(bp->error_str, "memory allocation failed", BITPACK_ERR_BUF_SIZE);
|
38
|
+
return BITPACK_RV_ERROR;
|
39
|
+
}
|
40
|
+
|
41
|
+
memset(bp->data + bp->data_size, 0, new_data_size - bp->data_size);
|
42
|
+
|
43
|
+
bp->data_size = new_data_size;
|
44
|
+
}
|
45
|
+
|
46
|
+
bp->size = new_size;
|
47
|
+
|
48
|
+
return BITPACK_RV_SUCCESS;
|
49
|
+
}
|
50
|
+
|
51
|
+
bitpack_t bitpack_init(unsigned long num_bytes)
|
52
|
+
{
|
53
|
+
bitpack_t bp;
|
54
|
+
unsigned char *data;
|
55
|
+
|
56
|
+
bp = malloc(sizeof(struct _bitpack_t));
|
57
|
+
if (bp == NULL) return NULL;
|
58
|
+
|
59
|
+
data = malloc(num_bytes);
|
60
|
+
|
61
|
+
if (data == NULL) {
|
62
|
+
free(bp);
|
63
|
+
return NULL;
|
64
|
+
}
|
65
|
+
|
66
|
+
memset(data, 0, num_bytes);
|
67
|
+
|
68
|
+
bp->size = 0;
|
69
|
+
bp->read_pos = 0;
|
70
|
+
bp->data_size = num_bytes;
|
71
|
+
bp->data = data;
|
72
|
+
bp->error = BITPACK_ERR_CLEAR;
|
73
|
+
memset(bp->error_str, '\0', BITPACK_ERR_BUF_SIZE);
|
74
|
+
|
75
|
+
return bp;
|
76
|
+
}
|
77
|
+
|
78
|
+
bitpack_t bitpack_init_from_bytes(unsigned char *bytes, unsigned long num_bytes)
|
79
|
+
{
|
80
|
+
bitpack_t bp;
|
81
|
+
|
82
|
+
bp = bitpack_init(num_bytes);
|
83
|
+
|
84
|
+
if (bp == NULL) {
|
85
|
+
return NULL;
|
86
|
+
}
|
87
|
+
|
88
|
+
memcpy(bp->data, bytes, num_bytes);
|
89
|
+
bp->size = num_bytes * 8;
|
90
|
+
|
91
|
+
return bp;
|
92
|
+
}
|
93
|
+
|
94
|
+
void bitpack_destroy(bitpack_t bp)
|
95
|
+
{
|
96
|
+
free(bp->data);
|
97
|
+
free(bp);
|
98
|
+
}
|
99
|
+
|
100
|
+
unsigned long bitpack_size(bitpack_t bp)
|
101
|
+
{
|
102
|
+
return bp->size;
|
103
|
+
}
|
104
|
+
|
105
|
+
unsigned long bitpack_data_size(bitpack_t bp)
|
106
|
+
{
|
107
|
+
return bp->data_size;
|
108
|
+
}
|
109
|
+
|
110
|
+
unsigned long bitpack_read_pos(bitpack_t bp)
|
111
|
+
{
|
112
|
+
return bp->read_pos;
|
113
|
+
}
|
114
|
+
|
115
|
+
void bitpack_reset_read_pos(bitpack_t bp)
|
116
|
+
{
|
117
|
+
bp->read_pos = 0;
|
118
|
+
}
|
119
|
+
|
120
|
+
bitpack_err_t bitpack_get_error(bitpack_t bp)
|
121
|
+
{
|
122
|
+
return bp->error;
|
123
|
+
}
|
124
|
+
|
125
|
+
char *bitpack_get_error_str(bitpack_t bp)
|
126
|
+
{
|
127
|
+
return bp->error_str;
|
128
|
+
}
|
129
|
+
|
130
|
+
int bitpack_on(bitpack_t bp, unsigned long index)
|
131
|
+
{
|
132
|
+
unsigned long byte_offset;
|
133
|
+
unsigned long bit_offset;
|
134
|
+
|
135
|
+
_bitpack_err_clear(bp);
|
136
|
+
|
137
|
+
if (bitpack_size(bp) == 0 || index > bitpack_size(bp) - 1) {
|
138
|
+
if (!_bitpack_resize(bp, index + 1)) {
|
139
|
+
return BITPACK_RV_ERROR;
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
byte_offset = index / 8;
|
144
|
+
bit_offset = index % 8;
|
145
|
+
|
146
|
+
bp->data[byte_offset] |= (0x80 >> bit_offset);
|
147
|
+
|
148
|
+
return BITPACK_RV_SUCCESS;
|
149
|
+
}
|
150
|
+
|
151
|
+
int bitpack_off(bitpack_t bp, unsigned long index)
|
152
|
+
{
|
153
|
+
unsigned long byte_offset;
|
154
|
+
unsigned long bit_offset;
|
155
|
+
|
156
|
+
_bitpack_err_clear(bp);
|
157
|
+
|
158
|
+
if (bitpack_size(bp) == 0 || index > bitpack_size(bp) - 1) {
|
159
|
+
if (!_bitpack_resize(bp, index + 1)) {
|
160
|
+
return BITPACK_RV_ERROR;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
byte_offset = index / 8;
|
165
|
+
bit_offset = index % 8;
|
166
|
+
|
167
|
+
bp->data[byte_offset] &= ~(0x80 >> bit_offset);
|
168
|
+
|
169
|
+
return BITPACK_RV_SUCCESS;
|
170
|
+
}
|
171
|
+
|
172
|
+
int bitpack_get(bitpack_t bp, unsigned long index, unsigned char *bit)
|
173
|
+
{
|
174
|
+
unsigned long byte_offset;
|
175
|
+
unsigned long bit_offset;
|
176
|
+
|
177
|
+
_bitpack_err_clear(bp);
|
178
|
+
|
179
|
+
if (bitpack_size(bp) == 0) {
|
180
|
+
bp->error = BITPACK_ERR_EMPTY;
|
181
|
+
strncpy(bp->error_str, "bitpack is empty", BITPACK_ERR_BUF_SIZE);
|
182
|
+
return BITPACK_RV_ERROR;
|
183
|
+
}
|
184
|
+
|
185
|
+
if (index > bitpack_size(bp) - 1) {
|
186
|
+
bp->error = BITPACK_ERR_INVALID_INDEX;
|
187
|
+
snprintf(bp->error_str, BITPACK_ERR_BUF_SIZE,
|
188
|
+
"invalid index (%lu), max index is %lu",
|
189
|
+
index, bitpack_size(bp) - 1);
|
190
|
+
return BITPACK_RV_ERROR;
|
191
|
+
}
|
192
|
+
|
193
|
+
byte_offset = index / 8;
|
194
|
+
bit_offset = index % 8;
|
195
|
+
|
196
|
+
if (bp->data[byte_offset] & (0x80 >> bit_offset)) {
|
197
|
+
*bit = 1;
|
198
|
+
}
|
199
|
+
else {
|
200
|
+
*bit = 0;
|
201
|
+
}
|
202
|
+
|
203
|
+
return BITPACK_RV_SUCCESS;
|
204
|
+
}
|
205
|
+
|
206
|
+
int bitpack_set_bits(bitpack_t bp, unsigned long value, unsigned long num_bits, unsigned long index)
|
207
|
+
{
|
208
|
+
unsigned long i;
|
209
|
+
unsigned long mask;
|
210
|
+
|
211
|
+
_bitpack_err_clear(bp);
|
212
|
+
|
213
|
+
/* make sure the range isn't bigger than the size of an unsigned long */
|
214
|
+
if (num_bits > sizeof(unsigned long) * 8) {
|
215
|
+
bp->error = BITPACK_ERR_RANGE_TOO_BIG;
|
216
|
+
snprintf(bp->error_str, BITPACK_ERR_BUF_SIZE,
|
217
|
+
"range size %lu bits is too large (maximum size is %lu bits)",
|
218
|
+
num_bits, sizeof(unsigned long) * 8);
|
219
|
+
return BITPACK_RV_ERROR;
|
220
|
+
}
|
221
|
+
|
222
|
+
/* make sure that the range is large enough to pack value */
|
223
|
+
if (value > pow(2, num_bits) - 1) {
|
224
|
+
bp->error = BITPACK_ERR_VALUE_TOO_BIG;
|
225
|
+
snprintf(bp->error_str, BITPACK_ERR_BUF_SIZE,
|
226
|
+
"value %lu does not fit in %lu bits",
|
227
|
+
value, num_bits);
|
228
|
+
return BITPACK_RV_ERROR;
|
229
|
+
}
|
230
|
+
|
231
|
+
if (bitpack_size(bp) < index + num_bits) {
|
232
|
+
if (!_bitpack_resize(bp, index + num_bits)) {
|
233
|
+
return BITPACK_RV_ERROR;
|
234
|
+
}
|
235
|
+
}
|
236
|
+
|
237
|
+
for (i = num_bits; i != 0; i--) {
|
238
|
+
mask = 1 << (i - 1);
|
239
|
+
if (value & mask) {
|
240
|
+
if (!bitpack_on(bp, index)) {
|
241
|
+
return BITPACK_RV_ERROR;
|
242
|
+
}
|
243
|
+
}
|
244
|
+
else {
|
245
|
+
if (!bitpack_off(bp, index)) {
|
246
|
+
return BITPACK_RV_ERROR;
|
247
|
+
}
|
248
|
+
}
|
249
|
+
index++;
|
250
|
+
}
|
251
|
+
|
252
|
+
return BITPACK_RV_SUCCESS;
|
253
|
+
}
|
254
|
+
|
255
|
+
int bitpack_set_bytes(bitpack_t bp, unsigned char *value, unsigned long num_bytes, unsigned long index)
|
256
|
+
{
|
257
|
+
unsigned long i;
|
258
|
+
|
259
|
+
_bitpack_err_clear(bp);
|
260
|
+
|
261
|
+
if (bitpack_size(bp) < index + num_bytes * 8) {
|
262
|
+
if (!_bitpack_resize(bp, index + num_bytes * 8)) {
|
263
|
+
return BITPACK_RV_ERROR;
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
if (index % 8 == 0) {
|
268
|
+
/* index is at the beginning of a byte, so just do a memcpy */
|
269
|
+
memcpy(bp->data + index / 8, value, num_bytes);
|
270
|
+
}
|
271
|
+
else {
|
272
|
+
/* need to set each bit individually */
|
273
|
+
for (i = 0; i < num_bytes; i++) {
|
274
|
+
if (!bitpack_set_bits(bp, value[i], 8, index + i * 8)) {
|
275
|
+
return BITPACK_RV_ERROR;
|
276
|
+
}
|
277
|
+
}
|
278
|
+
}
|
279
|
+
|
280
|
+
return BITPACK_RV_SUCCESS;
|
281
|
+
}
|
282
|
+
|
283
|
+
int bitpack_get_bits(bitpack_t bp, unsigned long num_bits, unsigned long index, unsigned long *value)
|
284
|
+
{
|
285
|
+
unsigned long i, v = 0;
|
286
|
+
unsigned char bit;
|
287
|
+
|
288
|
+
_bitpack_err_clear(bp);
|
289
|
+
|
290
|
+
if (index >= bitpack_size(bp)) {
|
291
|
+
bp->error = BITPACK_ERR_INVALID_INDEX;
|
292
|
+
snprintf(bp->error_str, BITPACK_ERR_BUF_SIZE,
|
293
|
+
"invalid index (%lu), max index is %lu",
|
294
|
+
index, bitpack_size(bp) - 1);
|
295
|
+
return BITPACK_RV_ERROR;
|
296
|
+
}
|
297
|
+
|
298
|
+
if (index + num_bits > bitpack_size(bp)) {
|
299
|
+
bp->error = BITPACK_ERR_READ_PAST_END;
|
300
|
+
snprintf(bp->error_str, BITPACK_ERR_BUF_SIZE,
|
301
|
+
"attempted to read past end of bitpack (last index is %lu)",
|
302
|
+
bitpack_size(bp) - 1);
|
303
|
+
return BITPACK_RV_ERROR;
|
304
|
+
}
|
305
|
+
|
306
|
+
if (num_bits > sizeof(unsigned long) * 8) {
|
307
|
+
bp->error = BITPACK_ERR_RANGE_TOO_BIG;
|
308
|
+
snprintf(bp->error_str, BITPACK_ERR_BUF_SIZE,
|
309
|
+
"range size %lu bits is too large (maximum size is %lu bits)",
|
310
|
+
num_bits, sizeof(unsigned long) * 8);
|
311
|
+
return BITPACK_RV_ERROR;
|
312
|
+
}
|
313
|
+
|
314
|
+
for (i = 0; i < num_bits; i++) {
|
315
|
+
bitpack_get(bp, index + i, &bit);
|
316
|
+
|
317
|
+
if (bit == 1) {
|
318
|
+
v |= bit << (num_bits - i - 1);
|
319
|
+
}
|
320
|
+
}
|
321
|
+
|
322
|
+
*value = v;
|
323
|
+
|
324
|
+
return BITPACK_RV_SUCCESS;
|
325
|
+
}
|
326
|
+
|
327
|
+
int bitpack_get_bytes(bitpack_t bp, unsigned long num_bytes, unsigned long index, unsigned char **value)
|
328
|
+
{
|
329
|
+
unsigned long i;
|
330
|
+
unsigned long byte;
|
331
|
+
unsigned char *unpacked;
|
332
|
+
|
333
|
+
_bitpack_err_clear(bp);
|
334
|
+
|
335
|
+
if (index >= bitpack_size(bp)) {
|
336
|
+
bp->error = BITPACK_ERR_INVALID_INDEX;
|
337
|
+
snprintf(bp->error_str, BITPACK_ERR_BUF_SIZE,
|
338
|
+
"invalid index (%lu), max index is %lu",
|
339
|
+
index, bitpack_size(bp) - 1);
|
340
|
+
return BITPACK_RV_ERROR;
|
341
|
+
}
|
342
|
+
|
343
|
+
if (index + num_bytes * 8 > bitpack_size(bp)) {
|
344
|
+
bp->error = BITPACK_ERR_READ_PAST_END;
|
345
|
+
snprintf(bp->error_str, BITPACK_ERR_BUF_SIZE,
|
346
|
+
"attempted to read past end of bitpack (last index is %lu)",
|
347
|
+
bitpack_size(bp) - 1);
|
348
|
+
return BITPACK_RV_ERROR;
|
349
|
+
}
|
350
|
+
|
351
|
+
unpacked = malloc(num_bytes);
|
352
|
+
if (unpacked == NULL) {
|
353
|
+
bp->error = BITPACK_ERR_MALLOC_FAILED;
|
354
|
+
strncpy(bp->error_str, "memory allocation failed", BITPACK_ERR_BUF_SIZE);
|
355
|
+
return BITPACK_RV_ERROR;
|
356
|
+
}
|
357
|
+
|
358
|
+
if (index % 8 == 0) {
|
359
|
+
/* index is the start of a byte, so just do a memcpy */
|
360
|
+
memcpy(unpacked, bp->data + index / 8, num_bytes);
|
361
|
+
}
|
362
|
+
else {
|
363
|
+
/* need to unpack a byte at a time */
|
364
|
+
for (i = 0; i < num_bytes; i++) {
|
365
|
+
bitpack_get_bits(bp, 8, index + i * 8, &byte);
|
366
|
+
unpacked[i] = byte;
|
367
|
+
}
|
368
|
+
}
|
369
|
+
|
370
|
+
*value = unpacked;
|
371
|
+
|
372
|
+
return BITPACK_RV_SUCCESS;
|
373
|
+
}
|
374
|
+
|
375
|
+
int bitpack_read_bits(bitpack_t bp, unsigned long num_bits, unsigned long *value)
|
376
|
+
{
|
377
|
+
int rv;
|
378
|
+
|
379
|
+
_bitpack_err_clear(bp);
|
380
|
+
|
381
|
+
if (bp->read_pos + num_bits > bitpack_size(bp)) {
|
382
|
+
bp->error = BITPACK_ERR_READ_PAST_END;
|
383
|
+
snprintf(bp->error_str, BITPACK_ERR_BUF_SIZE,
|
384
|
+
"attempted to read past end of bitpack (last index is %lu)",
|
385
|
+
bitpack_size(bp) - 1);
|
386
|
+
return BITPACK_RV_ERROR;
|
387
|
+
}
|
388
|
+
|
389
|
+
rv = bitpack_get_bits(bp, num_bits, bp->read_pos, value);
|
390
|
+
|
391
|
+
if (rv == BITPACK_RV_SUCCESS) {
|
392
|
+
bp->read_pos += num_bits;
|
393
|
+
}
|
394
|
+
else {
|
395
|
+
return rv;
|
396
|
+
}
|
397
|
+
|
398
|
+
return BITPACK_RV_SUCCESS;
|
399
|
+
}
|
400
|
+
|
401
|
+
int bitpack_read_bytes(bitpack_t bp, unsigned long num_bytes, unsigned char **value)
|
402
|
+
{
|
403
|
+
int rv;
|
404
|
+
|
405
|
+
_bitpack_err_clear(bp);
|
406
|
+
|
407
|
+
if (bp->read_pos + num_bytes * 8 > bitpack_size(bp)) {
|
408
|
+
bp->error = BITPACK_ERR_READ_PAST_END;
|
409
|
+
snprintf(bp->error_str, BITPACK_ERR_BUF_SIZE,
|
410
|
+
"attempted to read past end of bitpack (last index is %lu)",
|
411
|
+
bitpack_size(bp) - 1);
|
412
|
+
return BITPACK_RV_ERROR;
|
413
|
+
}
|
414
|
+
|
415
|
+
rv = bitpack_get_bytes(bp, num_bytes, bp->read_pos, value);
|
416
|
+
|
417
|
+
if (rv == BITPACK_RV_SUCCESS) {
|
418
|
+
bp->read_pos += num_bytes * 8;
|
419
|
+
}
|
420
|
+
else {
|
421
|
+
return rv;
|
422
|
+
}
|
423
|
+
|
424
|
+
return BITPACK_RV_SUCCESS;
|
425
|
+
}
|
426
|
+
|
427
|
+
int bitpack_to_bin(bitpack_t bp, char **str)
|
428
|
+
{
|
429
|
+
unsigned long i;
|
430
|
+
char *string;
|
431
|
+
char tmp[3];
|
432
|
+
unsigned char bit;
|
433
|
+
|
434
|
+
_bitpack_err_clear(bp);
|
435
|
+
|
436
|
+
string = malloc(bitpack_size(bp) + 1);
|
437
|
+
|
438
|
+
if (string == NULL) {
|
439
|
+
*str = NULL;
|
440
|
+
bp->error = BITPACK_ERR_MALLOC_FAILED;
|
441
|
+
strncpy(bp->error_str, "memory allocation failed", BITPACK_ERR_BUF_SIZE);
|
442
|
+
return BITPACK_RV_ERROR;
|
443
|
+
}
|
444
|
+
|
445
|
+
string[0] = '\0';
|
446
|
+
|
447
|
+
for (i = 0; i < bitpack_size(bp); i++) {
|
448
|
+
bitpack_get(bp, i, &bit);
|
449
|
+
sprintf(tmp, "%d", bit);
|
450
|
+
strncat(string, tmp, 1);
|
451
|
+
}
|
452
|
+
|
453
|
+
*str = string;
|
454
|
+
|
455
|
+
return BITPACK_RV_SUCCESS;
|
456
|
+
}
|
457
|
+
|
458
|
+
int bitpack_to_bytes(bitpack_t bp, unsigned char **value, unsigned long *num_bytes)
|
459
|
+
{
|
460
|
+
unsigned char *bytes;
|
461
|
+
unsigned long bytes_size = round8(bp->size) / 8;
|
462
|
+
|
463
|
+
_bitpack_err_clear(bp);
|
464
|
+
|
465
|
+
bytes = malloc(bytes_size);
|
466
|
+
|
467
|
+
if (bytes == NULL) {
|
468
|
+
bp->error = BITPACK_ERR_MALLOC_FAILED;
|
469
|
+
strncpy(bp->error_str, "memory allocation failed", BITPACK_ERR_BUF_SIZE);
|
470
|
+
return BITPACK_RV_ERROR;
|
471
|
+
}
|
472
|
+
|
473
|
+
memcpy(bytes, bp->data, bytes_size);
|
474
|
+
|
475
|
+
*value = bytes;
|
476
|
+
|
477
|
+
if (num_bytes) {
|
478
|
+
*num_bytes = bytes_size;
|
479
|
+
}
|
480
|
+
|
481
|
+
return BITPACK_RV_SUCCESS;
|
482
|
+
}
|
483
|
+
|