4 /* Provides the [pack] and [unpack] commands to pack and unpack
5 * a binary string to/from arbitrary width integers and strings.
7 * This may be used to implement the [binary] command.
11 * Big endian bit test.
13 * Considers 'bitvect' as a big endian bit stream and returns
14 * bit 'b' as zero or non-zero.
16 static int JimTestBitBigEndian(const unsigned char *bitvec
, int b
)
18 div_t pos
= div(b
, 8);
19 return bitvec
[pos
.quot
] & (1 << (7 - pos
.rem
));
23 * Little endian bit test.
25 * Considers 'bitvect' as a little endian bit stream and returns
26 * bit 'b' as zero or non-zero.
28 static int JimTestBitLittleEndian(const unsigned char *bitvec
, int b
)
30 div_t pos
= div(b
, 8);
31 return bitvec
[pos
.quot
] & (1 << pos
.rem
);
35 * Sign extends the given value, 'n' of width 'width' bits.
37 * For example, sign extending 0x80 with a width of 8, produces -128
39 static jim_wide
JimSignExtend(jim_wide n
, int width
)
41 if (width
== sizeof(jim_wide
) * 8) {
42 /* Can't sign extend the maximum size integer */
45 if (n
& ((jim_wide
)1 << (width
- 1))) {
47 n
-= ((jim_wide
)1 << width
);
54 * Big endian integer extraction.
56 * Considers 'bitvect' as a big endian bit stream.
57 * Returns an integer of the given width (in bits)
58 * starting at the given position (in bits).
60 * The pos/width must represent bits inside bitvec,
61 * and the width be no more than the width of jim_wide.
63 static jim_wide
JimBitIntBigEndian(const unsigned char *bitvec
, int pos
, int width
)
68 /* Aligned, byte extraction */
69 if (pos
% 8 == 0 && width
% 8 == 0) {
70 for (i
= 0; i
< width
; i
+= 8) {
71 result
= (result
<< 8) + bitvec
[(pos
+ i
) / 8];
77 for (i
= 0; i
< width
; i
++) {
78 if (JimTestBitBigEndian(bitvec
, pos
+ width
- i
- 1)) {
79 result
|= ((jim_wide
)1 << i
);
87 * Little endian integer extraction.
89 * Like JimBitIntBigEndian() but considers 'bitvect' as a little endian bit stream.
91 static jim_wide
JimBitIntLittleEndian(const unsigned char *bitvec
, int pos
, int width
)
96 /* Aligned, byte extraction */
97 if (pos
% 8 == 0 && width
% 8 == 0) {
98 for (i
= 0; i
< width
; i
+= 8) {
99 result
+= (jim_wide
)bitvec
[(pos
+ i
) / 8] << i
;
105 for (i
= 0; i
< width
; i
++) {
106 if (JimTestBitLittleEndian(bitvec
, pos
+ i
)) {
107 result
|= ((jim_wide
)1 << i
);
115 * Big endian bit set.
117 * Considers 'bitvect' as a big endian bit stream and sets
120 static void JimSetBitBigEndian(unsigned char *bitvec
, int b
, int bit
)
122 div_t pos
= div(b
, 8);
124 bitvec
[pos
.quot
] |= (1 << (7 - pos
.rem
));
127 bitvec
[pos
.quot
] &= ~(1 << (7 - pos
.rem
));
132 * Little endian bit set.
134 * Considers 'bitvect' as a little endian bit stream and sets
137 static void JimSetBitLittleEndian(unsigned char *bitvec
, int b
, int bit
)
139 div_t pos
= div(b
, 8);
141 bitvec
[pos
.quot
] |= (1 << pos
.rem
);
144 bitvec
[pos
.quot
] &= ~(1 << pos
.rem
);
149 * Big endian integer packing.
151 * Considers 'bitvect' as a big endian bit stream.
152 * Packs integer 'value' of the given width (in bits)
153 * starting at the given position (in bits).
155 * The pos/width must represent bits inside bitvec,
156 * and the width be no more than the width of jim_wide.
158 static void JimSetBitsIntBigEndian(unsigned char *bitvec
, jim_wide value
, int pos
, int width
)
162 /* Common fast option */
163 if (pos
% 8 == 0 && width
== 8) {
164 bitvec
[pos
/ 8] = value
;
168 for (i
= 0; i
< width
; i
++) {
169 int bit
= !!(value
& ((jim_wide
)1 << i
));
170 JimSetBitBigEndian(bitvec
, pos
+ width
- i
- 1, bit
);
175 * Little endian version of JimSetBitsIntBigEndian()
177 static void JimSetBitsIntLittleEndian(unsigned char *bitvec
, jim_wide value
, int pos
, int width
)
181 /* Common fast option */
182 if (pos
% 8 == 0 && width
== 8) {
183 bitvec
[pos
/ 8] = value
;
187 for (i
= 0; i
< width
; i
++) {
188 int bit
= !!(value
& ((jim_wide
)1 << i
));
189 JimSetBitLittleEndian(bitvec
, pos
+ i
, bit
);
196 * Usage: unpack binvalue -intbe|-intle|-uintbe|-uintle|-str bitpos bitwidth
198 * Unpacks bits from $binvalue at bit position $bitpos and with $bitwidth.
199 * Interprets the value according to the type and returns it.
201 static int Jim_UnpackCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
204 static const char * const options
[] = { "-intbe", "-intle", "-uintbe", "-uintle", "-str", NULL
};
205 enum { OPT_INTBE
, OPT_INTLE
, OPT_UINTBE
, OPT_UINTLE
, OPT_STR
, };
210 Jim_WrongNumArgs(interp
, 1, argv
, "binvalue -intbe|-intle|-uintbe|-uintle|-str bitpos bitwidth");
213 if (Jim_GetEnum(interp
, argv
[2], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
217 if (Jim_GetWide(interp
, argv
[3], &pos
) != JIM_OK
) {
220 if (Jim_GetWide(interp
, argv
[4], &width
) != JIM_OK
) {
224 if (option
== OPT_STR
) {
226 const char *str
= Jim_GetString(argv
[1], &len
);
228 if (width
% 8 || pos
% 8) {
229 Jim_SetResultString(interp
, "string field is not on a byte boundary", -1);
233 if (pos
>= 0 && width
> 0 && pos
< len
* 8) {
234 if (pos
+ width
> len
* 8) {
235 width
= len
* 8 - pos
;
237 Jim_SetResultString(interp
, str
+ pos
/ 8, width
/ 8);
243 const unsigned char *str
= (const unsigned char *)Jim_GetString(argv
[1], &len
);
246 if (width
> sizeof(jim_wide
) * 8) {
247 Jim_SetResultFormatted(interp
, "int field is too wide: %#s", argv
[4]);
251 if (pos
>= 0 && width
> 0 && pos
< len
* 8) {
252 if (pos
+ width
> len
* 8) {
253 width
= len
* 8 - pos
;
255 if (option
== OPT_INTBE
|| option
== OPT_UINTBE
) {
256 result
= JimBitIntBigEndian(str
, pos
, width
);
259 result
= JimBitIntLittleEndian(str
, pos
, width
);
261 if (option
== OPT_INTBE
|| option
== OPT_INTLE
) {
262 result
= JimSignExtend(result
, width
);
265 Jim_SetResultInt(interp
, result
);
273 * Usage: pack varname value -intle|-intbe|-str width ?bitoffset?
275 * Packs the binary representation of 'value' into the variable of the given name.
276 * The value is packed according to the given type, width and bitoffset.
277 * The variable is created if necessary (like [append])
278 * Ihe variable is expanded if necessary
280 static int Jim_PackCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
283 static const char * const options
[] = { "-intle", "-intbe", "-str", NULL
};
284 enum { OPT_LE
, OPT_BE
, OPT_STR
};
288 Jim_Obj
*stringObjPtr
;
292 if (argc
!= 5 && argc
!= 6) {
293 Jim_WrongNumArgs(interp
, 1, argv
, "varName value -intle|-intbe|-str bitwidth ?bitoffset?");
296 if (Jim_GetEnum(interp
, argv
[3], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
299 if (option
!= OPT_STR
&& Jim_GetWide(interp
, argv
[2], &value
) != JIM_OK
) {
302 if (Jim_GetWide(interp
, argv
[4], &width
) != JIM_OK
) {
305 if (width
<= 0 || (option
== OPT_STR
&& width
% 8) || (option
!= OPT_STR
&& width
> sizeof(jim_wide
) * 8)) {
306 Jim_SetResultFormatted(interp
, "bad bitwidth: %#s", argv
[5]);
310 if (Jim_GetWide(interp
, argv
[5], &pos
) != JIM_OK
) {
313 if (pos
< 0 || (option
== OPT_STR
&& pos
% 8)) {
314 Jim_SetResultFormatted(interp
, "bad bitoffset: %#s", argv
[5]);
319 stringObjPtr
= Jim_GetVariable(interp
, argv
[1], JIM_UNSHARED
);
321 /* Create the string if it doesn't exist */
322 stringObjPtr
= Jim_NewEmptyStringObj(interp
);
325 else if (Jim_IsShared(stringObjPtr
)) {
327 stringObjPtr
= Jim_DuplicateObj(interp
, stringObjPtr
);
330 len
= Jim_Length(stringObjPtr
) * 8;
332 /* Extend the string as necessary first */
333 while (len
< pos
+ width
) {
334 Jim_AppendString(interp
, stringObjPtr
, "", 1);
338 Jim_SetResultInt(interp
, pos
+ width
);
340 /* Now set the bits. Note that the the string *must* have no non-string rep
341 * since we are writing the bytes directly.
343 Jim_AppendString(interp
, stringObjPtr
, "", 0);
345 if (option
== OPT_BE
) {
346 JimSetBitsIntBigEndian((unsigned char *)stringObjPtr
->bytes
, value
, pos
, width
);
348 else if (option
== OPT_LE
) {
349 JimSetBitsIntLittleEndian((unsigned char *)stringObjPtr
->bytes
, value
, pos
, width
);
355 if (width
> Jim_Length(argv
[2])) {
356 width
= Jim_Length(argv
[2]);
358 memcpy(stringObjPtr
->bytes
+ pos
, Jim_GetString(argv
[2], NULL
), width
);
359 /* No padding is needed since the string is already extended */
362 if (Jim_SetVariable(interp
, argv
[1], stringObjPtr
) != JIM_OK
) {
364 Jim_FreeNewObj(interp
, stringObjPtr
);
371 int Jim_packInit(Jim_Interp
*interp
)
373 if (Jim_PackageProvide(interp
, "pack", "1.0", JIM_ERRMSG
)) {
377 Jim_CreateCommand(interp
, "unpack", Jim_UnpackCmd
, NULL
, NULL
);
378 Jim_CreateCommand(interp
, "pack", Jim_PackCmd
, NULL
, NULL
);