File: multibitnums.c

package info (click to toggle)
ruby-multibitnums 0.1.4-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, jessie, jessie-kfreebsd, sid, stretch
  • size: 92 kB
  • ctags: 26
  • sloc: ansic: 125; ruby: 122; makefile: 4
file content (217 lines) | stat: -rw-r--r-- 5,875 bytes parent folder | download | duplicates (2)
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
/*
=begin
= Class MultiBitNums

Class of multiple multi-bit data packed in a C string.
The number of bits is fixed to an arbitrary value.
For example, it can accommodate 11-bit integers (but cannot
a mixture of integers with different lengths).

=== Function overview

Integer encoding/decoding (so far only decoding has been implemented):

  * Each segment of data is assumed to be multi-bit (fixed-length)
    unsigned integer in network byte order (big endian).

  * Note: integers of the system does not have to be big-endian (Only 
    the binary data is interpreted as unsigned big-endian).


===Class Methods
---MultiBitNums.new( str, nbit  [, nint] )
     Creates a new object from a binary data (string) containing multi-bit 
     segments.

     ARGUMENTS
     * str (String): binary data containing multi-bit data
     * nbit (Integer): length in bits of each segments contained in str
     * nint (Integer, can be omitted): number of nbit-bit data in str. If
       omitted, derived automatically from the length of str.

     RETURN VALUE
     * a MultiBitNums object

     ERRORS
     * exception is raised if nint is too large for the length of str.

===Instance Methods
---to_int32str
     Converts into a string containing binary data of 32-bit integers
     of the system. Useful with NArray.

     ARGUMENTS
       (none)

     RETURN VALUE
     * a String (Its binary expression depends on the integer
       expression of the system). 
     
     ERRORS
     * exception is raised if nbit (see ((<MultiBitNums.new>))) is greater than 32.

     EXAMPLE
       mb = MultiBitNums.new(str, nbits)
       require "narray"           # download it from RAA at www.ruby-lang.org
       ary = NArray.to_na( mb, "int" )  # the data is read into a NArray ary

=end
 */

/* ruby.h for rb_raise */
#include "ruby.h"

/* sys/typed.h : should be configured whether the system has this or not */
#include <sys/types.h>

#ifndef HAVE_INT32_T
typedef long int32_t;
#endif

#ifndef RSTRING_PTR
#define RSTRNG_PTR(s) (RSTRING(s)->ptr)
#endif
#ifndef RSTRING_LEN
#define RSTRING_LEN(a) (RSTRING(a)->len)
#endif

static VALUE mNumRu;
static VALUE cMultiBitNums;

struct MultiBitNums {
     unsigned char *ptr;  /* holds data */
     long len_ptr;        /* length of ptr (in bytes) */
     int  nbit;           /* length in bits of each integer in str */
     long nint;           /* number of data contained (<= len_str*8/static) */
};

static void
mltbtnm_free(struct MultiBitNums *mbs)
{
    xfree(mbs->ptr); 
    mbs->len_ptr = 0;
    mbs->nbit = 0;
    mbs->nint = 0;
    xfree(mbs);
}

static VALUE
mltbtnm_s_new(int argc, VALUE *argv, VALUE klass)
{
    /* [ actual arguments --> */
    VALUE str;   
    VALUE nbit;
    VALUE nint;         /* can be omitted */
    /* <-- actual arguments ] */

    struct MultiBitNums *mbs;
    long c_nint, nintmax;

    if (argc < 2 || argc >3){
	rb_raise(rb_eArgError, 
	  "Usage: MultiBitNums.new(str, nbit [,nint]) -- nint is omittable");
    }
    str = argv[0];
    nbit = argv[1];

    mbs = ALLOC(struct MultiBitNums);
    mbs->nbit = NUM2INT(nbit);
    mbs->len_ptr = RSTRING_LEN(str);
    mbs->ptr = ALLOC_N(char, mbs->len_ptr);
    memcpy( mbs->ptr, RSTRING_PTR(str), mbs->len_ptr);

    nintmax = ((mbs->len_ptr*8.0)/mbs->nbit + 0.1);
    if ( argc != 3 ){
	mbs->nint = nintmax;
    } else {
	nint = argv[2];
	c_nint = NUM2LONG(nint);
	if (c_nint > nintmax) {
	    rb_raise(rb_eArgError, "nint is too long");
	} else {
	    mbs->nint = c_nint;
	}
    }
    return Data_Wrap_Struct(klass, 0, mltbtnm_free, mbs);
}

static int32_t *
mltbtnm_read_int32(unsigned char *ptr, long len_ptr, int nbit, long *nint)
     /*
     unsigned char *ptr  // holds data
     long len_ptr        // length of ptr (in bytes)
     int  nbit           // number of bits
     long *nint          // number of integers to read (if <= 0, determined 
                         // from len_ptr)
     */
{
    int32_t *result;
    long nintmax, i, n0, n1, k0, k1, w;
    long j, j2, nm;
    unsigned char cn[4],mk0,mb;

    nintmax = ((len_ptr*8.0)/nbit + 0.1);
          /* double was used here to prevent overflow. 0.1 is for round errs */
    if ( *nint <= 0 ){
	*nint = nintmax;
    } else if (*nint > nintmax) {
	rb_raise(rb_eArgError, "*nint is too long");
    }
    if ( nbit > 32 ){
	rb_raise(rb_eArgError, "nbit must be 32 or less");
    }

    result = (int32_t *) malloc((*nint)*sizeof(int32_t));

    for(i=0; i<(*nint); i++){
	w = (i % 8) * nbit;
	n0 = (i / 8) * nbit + (w / 8);     /* overflow-free i*nbit/8 */
	k0 = w % 8;                        /* == (i*nbit) % 8 */
	w = k0 + nbit - 1;
	n1 = n0 + (w/8);
	k1 = w % 8;
	mk0 = 0xff >> k0;   /* to mask 0..k0-1 bits */
	for(j=n1; j>= n1-3; j--){
	    j2 = j - n1 + 3;  /* 3,2,1,0 */
	    mb = (nm = (8-nbit+(3-j2)*8))  > 0 ? (0xff>>nm): 0xff;
	    if (j > n0) {
		cn[j2] = ptr[j] >> (7-k1);
		if (j != n0+1){
		    cn[j2] += ptr[j-1] << (k1+1);
		} else {
		    cn[j2] += (ptr[j-1] & mk0) << (k1+1); 
		}
		cn[j2] &= mb;
	    } else if (j == n0) {
		cn[j2] = (ptr[j] & mk0) >> (7-k1);
		cn[j2] &= mb;
	    } else {
		/* j < n0 --> blank */
		cn[j2] = 0x00;
	    }
	}
	result[i] = 0x1000000*cn[0] + 0x10000*cn[1] + 0x100*cn[2] + cn[3];
    }
    return(result);
}

static VALUE
mltbtnm_to_int32str(VALUE obj){
    struct MultiBitNums *mbs;
    int32_t *buff;

    Data_Get_Struct(obj, struct MultiBitNums, mbs);
    buff = mltbtnm_read_int32(mbs->ptr, mbs->len_ptr, mbs->nbit, &(mbs->nint));
    return rb_str_new( (char *) buff, mbs->nint * sizeof(int32_t) );
}


void
Init_multibitnums()
{
    extern VALUE ruby_class;
    mNumRu = rb_define_module("NumRu");
    cMultiBitNums = rb_define_class_under(mNumRu, "MultiBitNums", rb_cObject);
    rb_define_singleton_method(cMultiBitNums,"new",mltbtnm_s_new,-1);
    rb_define_method(cMultiBitNums, "to_int32str", mltbtnm_to_int32str,0);
}