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/ext/bitpack_ext.c
ADDED
@@ -0,0 +1,662 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include "string.h"
|
3
|
+
#include "bitpack.h"
|
4
|
+
|
5
|
+
/* the BitPack class object */
|
6
|
+
static VALUE cBitPack;
|
7
|
+
|
8
|
+
/* mapping of BitPack error codes to ruby exceptions */
|
9
|
+
static VALUE bp_exceptions[6];
|
10
|
+
|
11
|
+
/*
|
12
|
+
* call-seq:
|
13
|
+
* BitPack.new -> a new BitPack object
|
14
|
+
* BitPack.new(n) -> a new BitPack object
|
15
|
+
*
|
16
|
+
* Creates a new BitPack object. The number of bytes used internally
|
17
|
+
* to store the bit string can optionally be set to +n+.
|
18
|
+
*
|
19
|
+
*/
|
20
|
+
static VALUE bp_new(int argc, VALUE *argv, VALUE class)
|
21
|
+
{
|
22
|
+
VALUE bp_obj;
|
23
|
+
bitpack_t bp;
|
24
|
+
|
25
|
+
if (argc == 0) {
|
26
|
+
bp = bitpack_init_default();
|
27
|
+
}
|
28
|
+
else {
|
29
|
+
bp = bitpack_init(NUM2ULONG(argv[0]));
|
30
|
+
}
|
31
|
+
|
32
|
+
if (bp == NULL) {
|
33
|
+
rb_raise(bp_exceptions[BITPACK_ERR_MALLOC_FAILED], "malloc() failed");
|
34
|
+
}
|
35
|
+
|
36
|
+
bp_obj = Data_Wrap_Struct(class, 0, bitpack_destroy, bp);
|
37
|
+
|
38
|
+
return bp_obj;
|
39
|
+
}
|
40
|
+
|
41
|
+
/*
|
42
|
+
* call-seq:
|
43
|
+
* BitPack.from_bytes(string) -> a new BitPack object
|
44
|
+
*
|
45
|
+
* Creates a new BitPack object that is initialzed with the contents
|
46
|
+
* of +string+.
|
47
|
+
*
|
48
|
+
* === Example
|
49
|
+
*
|
50
|
+
* >> bp = BitPack.from_bytes("ruby")
|
51
|
+
* => 01110010011101010110001001111001
|
52
|
+
* >> 4.times { p bp.read_bits(8).chr }
|
53
|
+
* "r"
|
54
|
+
* "u"
|
55
|
+
* "b"
|
56
|
+
* "y"
|
57
|
+
* => 4
|
58
|
+
*/
|
59
|
+
static VALUE bp_from_bytes(VALUE class, VALUE bytes_str)
|
60
|
+
{
|
61
|
+
VALUE bp_obj;
|
62
|
+
VALUE str;;
|
63
|
+
bitpack_t bp;
|
64
|
+
|
65
|
+
str = StringValue(bytes_str);
|
66
|
+
|
67
|
+
bp = bitpack_init_from_bytes((unsigned char *)RSTRING(str)->ptr, RSTRING(str)->len);
|
68
|
+
|
69
|
+
if (bp == NULL) {
|
70
|
+
rb_raise(bp_exceptions[BITPACK_ERR_MALLOC_FAILED], "malloc() failed");
|
71
|
+
}
|
72
|
+
|
73
|
+
bp_obj = Data_Wrap_Struct(class, 0, bitpack_destroy, bp);
|
74
|
+
|
75
|
+
return bp_obj;
|
76
|
+
}
|
77
|
+
|
78
|
+
/*
|
79
|
+
* call-seq:
|
80
|
+
* bp.size -> Integer
|
81
|
+
*
|
82
|
+
* Access the current size of the BitPack object in bits.
|
83
|
+
*/
|
84
|
+
static VALUE bp_size(VALUE self)
|
85
|
+
{
|
86
|
+
bitpack_t bp;
|
87
|
+
unsigned long size;
|
88
|
+
|
89
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
90
|
+
|
91
|
+
size = bitpack_size(bp);
|
92
|
+
|
93
|
+
return ULONG2NUM(size);
|
94
|
+
}
|
95
|
+
|
96
|
+
/*
|
97
|
+
* call-seq:
|
98
|
+
* bp.data_size -> Integer
|
99
|
+
*
|
100
|
+
* Access the number of bytes of memory currently allocated to this
|
101
|
+
* BitPack object.
|
102
|
+
*/
|
103
|
+
static VALUE bp_data_size(VALUE self)
|
104
|
+
{
|
105
|
+
bitpack_t bp;
|
106
|
+
unsigned long data_size;
|
107
|
+
|
108
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
109
|
+
|
110
|
+
data_size = bitpack_data_size(bp);
|
111
|
+
|
112
|
+
return ULONG2NUM(data_size);
|
113
|
+
}
|
114
|
+
|
115
|
+
/*
|
116
|
+
* call-seq:
|
117
|
+
* bp.read_pos -> Integer
|
118
|
+
*
|
119
|
+
* Access the current read position of this BitPack object.
|
120
|
+
*
|
121
|
+
* === Example
|
122
|
+
*
|
123
|
+
* >> bp = BitPack.from_bytes("test")
|
124
|
+
* => 01110100011001010111001101110100
|
125
|
+
* >> bp.read_pos
|
126
|
+
* => 0
|
127
|
+
* >> bp.read_bits(8)
|
128
|
+
* => 116
|
129
|
+
* >> bp.read_pos
|
130
|
+
* => 8
|
131
|
+
*/
|
132
|
+
static VALUE bp_read_pos(VALUE self)
|
133
|
+
{
|
134
|
+
bitpack_t bp;
|
135
|
+
unsigned long read_pos;
|
136
|
+
|
137
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
138
|
+
|
139
|
+
read_pos = bitpack_read_pos(bp);
|
140
|
+
|
141
|
+
return ULONG2NUM(read_pos);
|
142
|
+
}
|
143
|
+
|
144
|
+
/*
|
145
|
+
* call-seq:
|
146
|
+
* bp.reset_read_pos
|
147
|
+
*
|
148
|
+
* Reset the current read position to the beginning of this BitPack
|
149
|
+
* object.
|
150
|
+
*/
|
151
|
+
static VALUE bp_reset_read_pos(VALUE self)
|
152
|
+
{
|
153
|
+
bitpack_t bp;
|
154
|
+
|
155
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
156
|
+
|
157
|
+
bitpack_reset_read_pos(bp);
|
158
|
+
|
159
|
+
return self;
|
160
|
+
}
|
161
|
+
|
162
|
+
/*
|
163
|
+
* call-seq:
|
164
|
+
* bp.on(i)
|
165
|
+
*
|
166
|
+
* Sets the bit at index +i+. If +i+ is greater than the current
|
167
|
+
* size of the BitPack object, then the size is expanded and the
|
168
|
+
* current append position is set to this index.
|
169
|
+
*
|
170
|
+
* === Example
|
171
|
+
*
|
172
|
+
* >> bp = BitPack.new
|
173
|
+
* =>
|
174
|
+
* >> bp.on(0)
|
175
|
+
* => 1
|
176
|
+
* >> bp.on(2)
|
177
|
+
* => 101
|
178
|
+
* >> bp.on(7)
|
179
|
+
* => 10100001
|
180
|
+
* >> bp.on(6)
|
181
|
+
* => 10100011
|
182
|
+
*/
|
183
|
+
static VALUE bp_on(VALUE self, VALUE index)
|
184
|
+
{
|
185
|
+
bitpack_t bp;
|
186
|
+
|
187
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
188
|
+
|
189
|
+
if (!bitpack_on(bp, NUM2ULONG(index))) {
|
190
|
+
rb_raise(bp_exceptions[bitpack_get_error(bp)],
|
191
|
+
bitpack_get_error_str(bp));
|
192
|
+
}
|
193
|
+
|
194
|
+
return self;
|
195
|
+
}
|
196
|
+
|
197
|
+
/*
|
198
|
+
* call-seq:
|
199
|
+
* bp.off(i)
|
200
|
+
*
|
201
|
+
* Unsets the bit at index +i+. If +i+ is greater than the current
|
202
|
+
* size of the BitPack object, then the size is expanded and the
|
203
|
+
* current append position is set to this index.
|
204
|
+
*/
|
205
|
+
static VALUE bp_off(VALUE self, VALUE index)
|
206
|
+
{
|
207
|
+
bitpack_t bp;
|
208
|
+
|
209
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
210
|
+
|
211
|
+
if (!bitpack_off(bp, NUM2ULONG(index))) {
|
212
|
+
rb_raise(bp_exceptions[bitpack_get_error(bp)],
|
213
|
+
bitpack_get_error_str(bp));
|
214
|
+
}
|
215
|
+
|
216
|
+
return self;
|
217
|
+
}
|
218
|
+
|
219
|
+
/*
|
220
|
+
* call-seq:
|
221
|
+
* bp.get(i) -> 0 or 1
|
222
|
+
*
|
223
|
+
* Access the value of the bit at index +i+.
|
224
|
+
*/
|
225
|
+
static VALUE bp_get(VALUE self, VALUE index)
|
226
|
+
{
|
227
|
+
bitpack_t bp;
|
228
|
+
unsigned char bit;
|
229
|
+
|
230
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
231
|
+
|
232
|
+
if (!bitpack_get(bp, NUM2ULONG(index), &bit)) {
|
233
|
+
rb_raise(bp_exceptions[bitpack_get_error(bp)],
|
234
|
+
bitpack_get_error_str(bp));
|
235
|
+
}
|
236
|
+
|
237
|
+
return INT2FIX(bit);
|
238
|
+
}
|
239
|
+
|
240
|
+
/*
|
241
|
+
* call-seq:
|
242
|
+
* bp.set_bits(value, num_bits, i) -> self
|
243
|
+
*
|
244
|
+
* Sets the specified range of bits in a BitPack object.
|
245
|
+
*
|
246
|
+
* Packs the Integer +value+ into +num_bits+ bits starting at index
|
247
|
+
* +i+. The number of bits required to represent +value+ is checked
|
248
|
+
* against the size of the range. If <tt>i + num_bits</tt> is greater
|
249
|
+
* than the current size of the BitPack, then the size is adjusted
|
250
|
+
* appropriately.
|
251
|
+
*
|
252
|
+
* === Example
|
253
|
+
*
|
254
|
+
* >> bp = BitPack.new
|
255
|
+
* =>
|
256
|
+
* >> bp.set_bits(0xff, 8, 0)
|
257
|
+
* => 11111111
|
258
|
+
* >> bp.set_bits(0xaa, 8, 8)
|
259
|
+
* => 1111111110101010
|
260
|
+
*/
|
261
|
+
static VALUE bp_set_bits(VALUE self, VALUE value, VALUE num_bits, VALUE index)
|
262
|
+
{
|
263
|
+
bitpack_t bp;
|
264
|
+
|
265
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
266
|
+
|
267
|
+
if (!bitpack_set_bits(bp, NUM2ULONG(value), NUM2ULONG(num_bits), NUM2ULONG(index))) {
|
268
|
+
rb_raise(bp_exceptions[bitpack_get_error(bp)],
|
269
|
+
bitpack_get_error_str(bp));
|
270
|
+
}
|
271
|
+
|
272
|
+
return self;
|
273
|
+
}
|
274
|
+
|
275
|
+
/*
|
276
|
+
* call-seq:
|
277
|
+
* bp.set_bytes(string, i)
|
278
|
+
*
|
279
|
+
* Sets the specified range of bytes in a BitPack object.
|
280
|
+
*
|
281
|
+
* Packs +string+ into the BitPack object starting at index +i+. The
|
282
|
+
* size of the BitPack object is adjusted appropriately if necessary.
|
283
|
+
*
|
284
|
+
* === Example
|
285
|
+
*
|
286
|
+
* >> bp = BitPack.new
|
287
|
+
* =>
|
288
|
+
* >> bp.set_bytes("ruby", 0)
|
289
|
+
* => 01110010011101010110001001111001
|
290
|
+
* >> 4.times { p bp.read_bits(8).chr }
|
291
|
+
* "r"
|
292
|
+
* "u"
|
293
|
+
* "b"
|
294
|
+
* "y"
|
295
|
+
* => 4
|
296
|
+
*/
|
297
|
+
static VALUE bp_set_bytes(VALUE self, VALUE bytes, VALUE index)
|
298
|
+
{
|
299
|
+
bitpack_t bp;
|
300
|
+
VALUE str;
|
301
|
+
|
302
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
303
|
+
|
304
|
+
str = StringValue(bytes);
|
305
|
+
|
306
|
+
if (!bitpack_set_bytes(bp, (unsigned char *)RSTRING(str)->ptr,
|
307
|
+
RSTRING(str)->len, NUM2ULONG(index))) {
|
308
|
+
rb_raise(bp_exceptions[bitpack_get_error(bp)],
|
309
|
+
bitpack_get_error_str(bp));
|
310
|
+
}
|
311
|
+
|
312
|
+
return self;
|
313
|
+
}
|
314
|
+
|
315
|
+
/*
|
316
|
+
* call-seq:
|
317
|
+
* bp.get_bits(num_bits, i) -> Integer
|
318
|
+
*
|
319
|
+
* Access the value stored in a range of bits starting at index +i+.
|
320
|
+
*
|
321
|
+
* Unpacks +num_bits+ starting from index +i+ and returns the Integer
|
322
|
+
* value.
|
323
|
+
*
|
324
|
+
* === Example
|
325
|
+
*
|
326
|
+
* >> bp = BitPack.new
|
327
|
+
* =>
|
328
|
+
* >> bp.append_bits(1, 4)
|
329
|
+
* => 0001
|
330
|
+
* >> bp.append_bits(2, 4)
|
331
|
+
* => 00010010
|
332
|
+
* >> bp.append_bits(3, 4)
|
333
|
+
* => 000100100011
|
334
|
+
* >> bp.append_bits(4, 4)
|
335
|
+
* => 0001001000110100
|
336
|
+
* >> bp.get_bits(4, 0)
|
337
|
+
* => 1
|
338
|
+
* >> bp.get_bits(4, 4)
|
339
|
+
* => 2
|
340
|
+
* >> bp.get_bits(4, 8)
|
341
|
+
* => 3
|
342
|
+
* >> bp.get_bits(4, 12)
|
343
|
+
* => 4
|
344
|
+
*/
|
345
|
+
static VALUE bp_get_bits(VALUE self, VALUE num_bits, VALUE index)
|
346
|
+
{
|
347
|
+
bitpack_t bp;
|
348
|
+
unsigned long value;
|
349
|
+
|
350
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
351
|
+
|
352
|
+
if (!bitpack_get_bits(bp, NUM2ULONG(num_bits), NUM2ULONG(index), &value)) {
|
353
|
+
rb_raise(bp_exceptions[bitpack_get_error(bp)],
|
354
|
+
bitpack_get_error_str(bp));
|
355
|
+
}
|
356
|
+
|
357
|
+
return ULONG2NUM(value);
|
358
|
+
}
|
359
|
+
|
360
|
+
/*
|
361
|
+
* call-seq:
|
362
|
+
* bp.get_bytes(num_bytes, i) -> String
|
363
|
+
*
|
364
|
+
* Access the value stored in a range of bytes starting at index +i+.
|
365
|
+
*
|
366
|
+
* Unpacks +num_bytes+ starting from bit index +i+ and returns the String
|
367
|
+
* value.
|
368
|
+
*
|
369
|
+
* === Example
|
370
|
+
*
|
371
|
+
* >> bp = BitPack.from_bytes("foobar")
|
372
|
+
* => 011001100110111101101111011000100110000101110010
|
373
|
+
* >> bp.get_bytes(3, 24)
|
374
|
+
* => "bar"
|
375
|
+
*/
|
376
|
+
static VALUE bp_get_bytes(VALUE self, VALUE num_bytes, VALUE index)
|
377
|
+
{
|
378
|
+
bitpack_t bp;
|
379
|
+
unsigned char *bytes;
|
380
|
+
VALUE str;
|
381
|
+
|
382
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
383
|
+
|
384
|
+
if (!bitpack_get_bytes(bp, NUM2ULONG(num_bytes), NUM2ULONG(index), &bytes)) {
|
385
|
+
rb_raise(bp_exceptions[bitpack_get_error(bp)],
|
386
|
+
bitpack_get_error_str(bp));
|
387
|
+
}
|
388
|
+
|
389
|
+
str = rb_str_new((char *)bytes, NUM2ULONG(num_bytes));
|
390
|
+
|
391
|
+
free(bytes);
|
392
|
+
|
393
|
+
return str;
|
394
|
+
}
|
395
|
+
|
396
|
+
/*
|
397
|
+
* call-seq:
|
398
|
+
* bp.append_bits(value, num_bits)
|
399
|
+
*
|
400
|
+
* Append the Integer +value+ to the end of a BitPack object.
|
401
|
+
*
|
402
|
+
* Packs +num_bits+ bits at the end of a BitPack object. The size of
|
403
|
+
* the BitPack object is increased by +num_bits+ as a result.
|
404
|
+
*
|
405
|
+
* === Example
|
406
|
+
*
|
407
|
+
* >> bp = BitPack.new
|
408
|
+
* =>
|
409
|
+
* >> bp.append_bits(1, 3)
|
410
|
+
* => 001
|
411
|
+
* >> bp.append_bits(3, 3)
|
412
|
+
* => 001011
|
413
|
+
* >> bp.append_bits(7, 3)
|
414
|
+
* => 001011111
|
415
|
+
*/
|
416
|
+
static VALUE bp_append_bits(VALUE self, VALUE value, VALUE num_bits)
|
417
|
+
{
|
418
|
+
bitpack_t bp;
|
419
|
+
|
420
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
421
|
+
|
422
|
+
if (!bitpack_append_bits(bp, NUM2ULONG(value), NUM2ULONG(num_bits))) {
|
423
|
+
rb_raise(bp_exceptions[bitpack_get_error(bp)],
|
424
|
+
bitpack_get_error_str(bp));
|
425
|
+
}
|
426
|
+
|
427
|
+
return self;
|
428
|
+
}
|
429
|
+
|
430
|
+
/*
|
431
|
+
* call-seq:
|
432
|
+
* bp.append_bytes(string)
|
433
|
+
*
|
434
|
+
* Append +string+ to the end of a BitPack object.
|
435
|
+
*
|
436
|
+
* Packs +string+ at the end of a BitPack object. The size of the
|
437
|
+
* BitPack object is increased by the length of +string+ as a result.
|
438
|
+
*
|
439
|
+
* === Example
|
440
|
+
*
|
441
|
+
* >> bp = BitPack.new
|
442
|
+
* =>
|
443
|
+
* >> bp.append_bytes("bit")
|
444
|
+
* => 011000100110100101110100
|
445
|
+
* >> bp.append_bytes("pack")
|
446
|
+
* => 01100010011010010111010001110000011000010110001101101011
|
447
|
+
* >> 7.times { p bp.read_bits(8).chr }
|
448
|
+
* "b"
|
449
|
+
* "i"
|
450
|
+
* "t"
|
451
|
+
* "p"
|
452
|
+
* "a"
|
453
|
+
* "c"
|
454
|
+
* "k"
|
455
|
+
* => 7
|
456
|
+
*/
|
457
|
+
static VALUE bp_append_bytes(VALUE self, VALUE value)
|
458
|
+
{
|
459
|
+
bitpack_t bp;
|
460
|
+
VALUE str;
|
461
|
+
|
462
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
463
|
+
|
464
|
+
str = StringValue(value);
|
465
|
+
|
466
|
+
if (!bitpack_append_bytes(bp, (unsigned char *)RSTRING(str)->ptr,
|
467
|
+
RSTRING(str)->len)) {
|
468
|
+
rb_raise(bp_exceptions[bitpack_get_error(bp)],
|
469
|
+
bitpack_get_error_str(bp));
|
470
|
+
}
|
471
|
+
|
472
|
+
return self;
|
473
|
+
}
|
474
|
+
|
475
|
+
/*
|
476
|
+
* call-seq:
|
477
|
+
* bp.read_bits(num_bits) -> Integer
|
478
|
+
*
|
479
|
+
* Access the value of a range of bits at the current read position.
|
480
|
+
*
|
481
|
+
* Unpacks +num_bits+ bits starting at the current read position (see
|
482
|
+
* Bitpack#read_pos) and returns the integer value. The current read
|
483
|
+
* position is advanced by +num_bits+ bits.
|
484
|
+
*
|
485
|
+
* === Example
|
486
|
+
*
|
487
|
+
* >> bp = BitPack.from_bytes("ruby")
|
488
|
+
* => 01110010011101010110001001111001
|
489
|
+
* >> 4.times { p bp.read_bits(8).chr }
|
490
|
+
* "r"
|
491
|
+
* "u"
|
492
|
+
* "b"
|
493
|
+
* "y"
|
494
|
+
* => 4
|
495
|
+
*/
|
496
|
+
static VALUE bp_read_bits(VALUE self, VALUE num_bits)
|
497
|
+
{
|
498
|
+
bitpack_t bp;
|
499
|
+
unsigned long value;
|
500
|
+
|
501
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
502
|
+
|
503
|
+
if (!bitpack_read_bits(bp, NUM2ULONG(num_bits), &value)) {
|
504
|
+
rb_raise(bp_exceptions[bitpack_get_error(bp)],
|
505
|
+
bitpack_get_error_str(bp));
|
506
|
+
}
|
507
|
+
|
508
|
+
return ULONG2NUM(value);
|
509
|
+
}
|
510
|
+
|
511
|
+
/*
|
512
|
+
* call-seq:
|
513
|
+
* bp.read_bytes(num_bytes) -> String
|
514
|
+
*
|
515
|
+
* Access the value of a range of bytes at the current read position.
|
516
|
+
*
|
517
|
+
* Unpacks +num_bytes+ bytes starting at the current read position (see
|
518
|
+
* Bitpack#read_pos) and returns the String value. The current read
|
519
|
+
* position is advanced by <tt>num_bytes * 8</tt> bits.
|
520
|
+
*
|
521
|
+
* === Example
|
522
|
+
*
|
523
|
+
* >> bp = BitPack.from_bytes("foobar")
|
524
|
+
* => 011001100110111101101111011000100110000101110010
|
525
|
+
* >> bp.read_bytes(3)
|
526
|
+
* => "foo"
|
527
|
+
* >> bp.read_bytes(3)
|
528
|
+
* => "bar"
|
529
|
+
*/
|
530
|
+
static VALUE bp_read_bytes(VALUE self, VALUE num_bytes)
|
531
|
+
{
|
532
|
+
bitpack_t bp;
|
533
|
+
unsigned char *value;
|
534
|
+
VALUE str;
|
535
|
+
|
536
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
537
|
+
|
538
|
+
if (!bitpack_read_bytes(bp, NUM2ULONG(num_bytes), &value)) {
|
539
|
+
rb_raise(bp_exceptions[bitpack_get_error(bp)],
|
540
|
+
bitpack_get_error_str(bp));
|
541
|
+
}
|
542
|
+
|
543
|
+
str = rb_str_new((char *)value, NUM2ULONG(num_bytes));
|
544
|
+
|
545
|
+
free(value);
|
546
|
+
|
547
|
+
return str;
|
548
|
+
}
|
549
|
+
|
550
|
+
/*
|
551
|
+
* call-seq:
|
552
|
+
* bp.to_bin -> String
|
553
|
+
*
|
554
|
+
* Converts the BitPack object to a string of 1s and 0s.
|
555
|
+
*/
|
556
|
+
static VALUE bp_to_bin(VALUE self)
|
557
|
+
{
|
558
|
+
bitpack_t bp;
|
559
|
+
char *s;
|
560
|
+
VALUE str;
|
561
|
+
|
562
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
563
|
+
|
564
|
+
if (!bitpack_to_bin(bp, &s)) {
|
565
|
+
rb_raise(bp_exceptions[bitpack_get_error(bp)],
|
566
|
+
bitpack_get_error_str(bp));
|
567
|
+
}
|
568
|
+
|
569
|
+
str = rb_str_new2(s);
|
570
|
+
|
571
|
+
free(s);
|
572
|
+
|
573
|
+
return str;
|
574
|
+
}
|
575
|
+
|
576
|
+
/*
|
577
|
+
* call-seq:
|
578
|
+
* bp.to_bytes -> String
|
579
|
+
*
|
580
|
+
* Converts the BitPack object to a string of bytes. If the current
|
581
|
+
* size of the BitPack object is not a multiple of 8, the last byte
|
582
|
+
* in the returned String will be padded with the appropriate number
|
583
|
+
* of 0 bits.
|
584
|
+
*
|
585
|
+
* === Example
|
586
|
+
*
|
587
|
+
* >> bp = BitPack.new
|
588
|
+
* =>
|
589
|
+
* >> bp.append_bits(?r, 8)
|
590
|
+
* => 01110010
|
591
|
+
* >> bp.append_bits(?u, 8)
|
592
|
+
* => 0111001001110101
|
593
|
+
* >> bp.append_bits(?b, 8)
|
594
|
+
* => 011100100111010101100010
|
595
|
+
* >> bp.append_bits(?y, 8)
|
596
|
+
* => 01110010011101010110001001111001
|
597
|
+
* >> bp.to_bytes
|
598
|
+
* => "ruby"
|
599
|
+
*/
|
600
|
+
static VALUE bp_to_bytes(VALUE self)
|
601
|
+
{
|
602
|
+
bitpack_t bp;
|
603
|
+
unsigned char *s;
|
604
|
+
unsigned long num_bytes;
|
605
|
+
VALUE str;
|
606
|
+
|
607
|
+
Data_Get_Struct(self, struct _bitpack_t, bp);
|
608
|
+
|
609
|
+
if (!bitpack_to_bytes(bp, &s, &num_bytes)) {
|
610
|
+
rb_raise(bp_exceptions[bitpack_get_error(bp)],
|
611
|
+
bitpack_get_error_str(bp));
|
612
|
+
}
|
613
|
+
|
614
|
+
str = rb_str_new((char *)s, num_bytes);
|
615
|
+
|
616
|
+
free(s);
|
617
|
+
|
618
|
+
return str;
|
619
|
+
}
|
620
|
+
|
621
|
+
/*
|
622
|
+
* A library for easily packing and unpacking binary strings with fields of
|
623
|
+
* arbitrary bit lengths.
|
624
|
+
*/
|
625
|
+
void Init_bitpack()
|
626
|
+
{
|
627
|
+
cBitPack = rb_define_class("BitPack", rb_cObject);
|
628
|
+
|
629
|
+
rb_define_singleton_method(cBitPack, "new", bp_new, -1);
|
630
|
+
rb_define_singleton_method(cBitPack, "from_bytes", bp_from_bytes, 1);
|
631
|
+
|
632
|
+
rb_define_method(cBitPack, "size", bp_size, 0);
|
633
|
+
rb_define_method(cBitPack, "data_size", bp_data_size, 0);
|
634
|
+
rb_define_method(cBitPack, "read_pos", bp_read_pos, 0);
|
635
|
+
rb_define_method(cBitPack, "reset_read_pos", bp_reset_read_pos, 0);
|
636
|
+
rb_define_method(cBitPack, "on", bp_on, 1);
|
637
|
+
rb_define_method(cBitPack, "off", bp_off, 1);
|
638
|
+
rb_define_method(cBitPack, "get", bp_get, 1);
|
639
|
+
rb_define_method(cBitPack, "[]", bp_get, 1);
|
640
|
+
rb_define_method(cBitPack, "set_bits", bp_set_bits, 3);
|
641
|
+
rb_define_method(cBitPack, "get_bits", bp_get_bits, 2);
|
642
|
+
rb_define_method(cBitPack, "set_bytes", bp_set_bytes, 2);
|
643
|
+
rb_define_method(cBitPack, "get_bytes", bp_get_bytes, 2);
|
644
|
+
rb_define_method(cBitPack, "append_bits", bp_append_bits, 2);
|
645
|
+
rb_define_method(cBitPack, "append_bytes", bp_append_bytes, 1);
|
646
|
+
rb_define_method(cBitPack, "read_bits", bp_read_bits, 1);
|
647
|
+
rb_define_method(cBitPack, "read_bytes", bp_read_bytes, 1);
|
648
|
+
rb_define_method(cBitPack, "to_bin", bp_to_bin, 0);
|
649
|
+
rb_define_method(cBitPack, "to_s", bp_to_bin, 0);
|
650
|
+
rb_define_method(cBitPack, "to_bytes", bp_to_bytes, 0);
|
651
|
+
|
652
|
+
bp_exceptions[BITPACK_ERR_MALLOC_FAILED] = rb_eNoMemError;
|
653
|
+
bp_exceptions[BITPACK_ERR_INVALID_INDEX] = rb_eRangeError;
|
654
|
+
bp_exceptions[BITPACK_ERR_VALUE_TOO_BIG] = rb_eArgError;
|
655
|
+
bp_exceptions[BITPACK_ERR_RANGE_TOO_BIG] = rb_eRangeError;
|
656
|
+
bp_exceptions[BITPACK_ERR_READ_PAST_END] = rb_eRangeError;
|
657
|
+
bp_exceptions[BITPACK_ERR_EMPTY] = rb_eRangeError;
|
658
|
+
|
659
|
+
/* require the pure ruby methods */
|
660
|
+
rb_require("lib/bitpack.rb");
|
661
|
+
}
|
662
|
+
|
data/ext/extconf.rb
ADDED
data/lib/bitpack.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
class BitPack
|
3
|
+
# Element Assignment. Sets the bit at +index+, or the range of bits indicated
|
4
|
+
# by the Range object +range+, or the range specified by +index+ and +length+.
|
5
|
+
#
|
6
|
+
# :call-seq:
|
7
|
+
# bp[index] = value -> value
|
8
|
+
# bp[range] = value -> value
|
9
|
+
# bp[index, length] = value -> value
|
10
|
+
#
|
11
|
+
# === Example
|
12
|
+
#
|
13
|
+
# >> bp[0] = 1
|
14
|
+
# => 1
|
15
|
+
# >> bp[1] = 0
|
16
|
+
# => 0
|
17
|
+
# >> bp[2] = 1
|
18
|
+
# => 1
|
19
|
+
# >> bp.to_bin
|
20
|
+
# => "101"
|
21
|
+
# >> bp[0..7] = 0xff
|
22
|
+
# => 255
|
23
|
+
# >> bp.to_bin
|
24
|
+
# => "11111111"
|
25
|
+
# >> bp[8, 16] = 0xf0f0
|
26
|
+
# => 61680
|
27
|
+
# >> bp.to_bin
|
28
|
+
# => "111111111111000011110000"
|
29
|
+
#
|
30
|
+
def []=(a, b, c = nil)
|
31
|
+
if c.nil?
|
32
|
+
# only two arguments, so it must be one of the following formats:
|
33
|
+
# bp[index] = value
|
34
|
+
# bp[range] = value
|
35
|
+
if a.kind_of? Integer
|
36
|
+
self.set_bits(b, 1, a)
|
37
|
+
elsif a.kind_of? Range
|
38
|
+
if a.exclude_end?
|
39
|
+
self.set_bits(b, a.end - a.begin, a.begin)
|
40
|
+
else
|
41
|
+
self.set_bits(b, a.end - a.begin + 1, a.begin)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
raise ArgumentError, "index must be an Integer or Range"
|
45
|
+
end
|
46
|
+
else
|
47
|
+
# this must the following format:
|
48
|
+
# bp[index, length] = value
|
49
|
+
self.set_bits(c, b, a)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|