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
);
194 * Binary conversion of jim_wide integer to float
196 * Considers the least significant bits of
197 * jim_wide 'value' as a IEEE float.
199 * Should work for both little- and big-endian platforms.
201 static float JimIntToFloat(jim_wide value
)
206 /* Skip offs to get to least significant bytes */
207 offs
= Jim_IsBigEndian() ? (sizeof(jim_wide
) - sizeof(float)) : 0;
209 memcpy(&val
, (unsigned char *) &value
+ offs
, sizeof(float));
214 * Binary conversion of jim_wide integer to double
216 * Double precision version of JimIntToFloat
218 static double JimIntToDouble(jim_wide value
)
223 /* Skip offs to get to least significant bytes */
224 offs
= Jim_IsBigEndian() ? (sizeof(jim_wide
) - sizeof(double)) : 0;
226 memcpy(&val
, (unsigned char *) &value
+ offs
, sizeof(double));
231 * Binary conversion of float to jim_wide integer
233 * Considers the bits of IEEE float 'value' as integer.
234 * The integer is zero-extended to jim_wide.
236 * Should work for both little- and big-endian platforms.
238 static jim_wide
JimFloatToInt(float value
)
243 /* Skip offs to get to least significant bytes */
244 offs
= Jim_IsBigEndian() ? (sizeof(jim_wide
) - sizeof(float)) : 0;
246 memcpy((unsigned char *) &val
+ offs
, &value
, sizeof(float));
251 * Binary conversion of double to jim_wide integer
253 * Double precision version of JimFloatToInt
255 static jim_wide
JimDoubleToInt(double value
)
260 /* Skip offs to get to least significant bytes */
261 offs
= Jim_IsBigEndian() ? (sizeof(jim_wide
) - sizeof(double)) : 0;
263 memcpy((unsigned char *) &val
+ offs
, &value
, sizeof(double));
270 * Usage: unpack binvalue -intbe|-intle|-uintbe|-uintle|-floatbe|-floatle|-str bitpos bitwidth
272 * Unpacks bits from $binvalue at bit position $bitpos and with $bitwidth.
273 * Interprets the value according to the type and returns it.
275 static int Jim_UnpackCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
278 static const char * const options
[] = { "-intbe", "-intle", "-uintbe", "-uintle",
279 "-floatbe", "-floatle", "-str", NULL
};
280 enum { OPT_INTBE
, OPT_INTLE
, OPT_UINTBE
, OPT_UINTLE
, OPT_FLOATBE
, OPT_FLOATLE
, OPT_STR
, };
285 Jim_WrongNumArgs(interp
, 1, argv
,
286 "binvalue -intbe|-intle|-uintbe|-uintle|-floatbe|-floatle|-str bitpos bitwidth");
289 if (Jim_GetEnum(interp
, argv
[2], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
293 if (Jim_GetWide(interp
, argv
[3], &pos
) != JIM_OK
) {
296 if (pos
< 0 || (option
== OPT_STR
&& pos
% 8)) {
297 Jim_SetResultFormatted(interp
, "bad bitoffset: %#s", argv
[3]);
300 if (Jim_GetWide(interp
, argv
[4], &width
) != JIM_OK
) {
303 if (width
< 0 || (option
== OPT_STR
&& width
% 8) || (option
!= OPT_STR
&& width
> sizeof(jim_wide
) * 8) ||
304 ((option
== OPT_FLOATLE
|| option
== OPT_FLOATBE
) && width
!= 32 && width
!= 64)) {
305 Jim_SetResultFormatted(interp
, "bad bitwidth: %#s", argv
[4]);
309 if (option
== OPT_STR
) {
311 const char *str
= Jim_GetString(argv
[1], &len
);
314 if (pos
+ width
> len
* 8) {
315 width
= len
* 8 - pos
;
317 Jim_SetResultString(interp
, str
+ pos
/ 8, width
/ 8);
323 const unsigned char *str
= (const unsigned char *)Jim_GetString(argv
[1], &len
);
327 if (pos
+ width
> len
* 8) {
328 width
= len
* 8 - pos
;
330 if (option
== OPT_INTBE
|| option
== OPT_UINTBE
|| option
== OPT_FLOATBE
) {
331 result
= JimBitIntBigEndian(str
, pos
, width
);
334 result
= JimBitIntLittleEndian(str
, pos
, width
);
336 if (option
== OPT_INTBE
|| option
== OPT_INTLE
) {
337 result
= JimSignExtend(result
, width
);
342 if (option
== OPT_FLOATBE
|| option
== OPT_FLOATLE
) {
345 fresult
= (double) JimIntToFloat(result
);
347 fresult
= JimIntToDouble(result
);
349 Jim_SetResult(interp
, Jim_NewDoubleObj(interp
, fresult
));
351 Jim_SetResultInt(interp
, result
);
360 * Usage: pack varname value -intbe|-intle|-floatle|-floatbe|-str width ?bitoffset?
362 * Packs the binary representation of 'value' into the variable of the given name.
363 * The value is packed according to the given type, width and bitoffset.
364 * The variable is created if necessary (like [append])
365 * The variable is expanded if necessary
367 static int Jim_PackCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
370 static const char * const options
[] = { "-intle", "-intbe", "-floatle", "-floatbe",
372 enum { OPT_LE
, OPT_BE
, OPT_FLOATLE
, OPT_FLOATBE
, OPT_STR
};
377 Jim_Obj
*stringObjPtr
;
381 if (argc
!= 5 && argc
!= 6) {
382 Jim_WrongNumArgs(interp
, 1, argv
,
383 "varName value -intle|-intbe|-floatle|-floatbe|-str bitwidth ?bitoffset?");
386 if (Jim_GetEnum(interp
, argv
[3], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
389 if ((option
== OPT_LE
|| option
== OPT_BE
) &&
390 Jim_GetWide(interp
, argv
[2], &value
) != JIM_OK
) {
393 if ((option
== OPT_FLOATLE
|| option
== OPT_FLOATBE
) &&
394 Jim_GetDouble(interp
, argv
[2], &fvalue
) != JIM_OK
) {
397 if (Jim_GetWide(interp
, argv
[4], &width
) != JIM_OK
) {
400 if (width
<= 0 || (option
== OPT_STR
&& width
% 8) || (option
!= OPT_STR
&& width
> sizeof(jim_wide
) * 8) ||
401 ((option
== OPT_FLOATLE
|| option
== OPT_FLOATBE
) && width
!= 32 && width
!= 64)) {
402 Jim_SetResultFormatted(interp
, "bad bitwidth: %#s", argv
[4]);
406 if (Jim_GetWide(interp
, argv
[5], &pos
) != JIM_OK
) {
409 if (pos
< 0 || (option
== OPT_STR
&& pos
% 8)) {
410 Jim_SetResultFormatted(interp
, "bad bitoffset: %#s", argv
[5]);
415 stringObjPtr
= Jim_GetVariable(interp
, argv
[1], JIM_UNSHARED
);
417 /* Create the string if it doesn't exist */
418 stringObjPtr
= Jim_NewEmptyStringObj(interp
);
421 else if (Jim_IsShared(stringObjPtr
)) {
423 stringObjPtr
= Jim_DuplicateObj(interp
, stringObjPtr
);
426 len
= Jim_Length(stringObjPtr
) * 8;
428 /* Extend the string as necessary first */
429 while (len
< pos
+ width
) {
430 Jim_AppendString(interp
, stringObjPtr
, "", 1);
434 Jim_SetResultInt(interp
, pos
+ width
);
436 /* Now set the bits. Note that the the string *must* have no non-string rep
437 * since we are writing the bytes directly.
439 Jim_AppendString(interp
, stringObjPtr
, "", 0);
441 /* Convert floating point to integer if necessary */
442 if (option
== OPT_FLOATLE
|| option
== OPT_FLOATBE
) {
443 /* Note that the following is slightly incompatible with Tcl behaviour.
444 * In Tcl floating overflow gives FLT_MAX (cf. test binary-13.13).
445 * In Jim Tcl it gives Infinity. This behavior may change.
447 value
= (width
== 32) ? JimFloatToInt((float)fvalue
) : JimDoubleToInt(fvalue
);
450 if (option
== OPT_BE
|| option
== OPT_FLOATBE
) {
451 JimSetBitsIntBigEndian((unsigned char *)stringObjPtr
->bytes
, value
, pos
, width
);
453 else if (option
== OPT_LE
|| option
== OPT_FLOATLE
) {
454 JimSetBitsIntLittleEndian((unsigned char *)stringObjPtr
->bytes
, value
, pos
, width
);
460 if (width
> Jim_Length(argv
[2])) {
461 width
= Jim_Length(argv
[2]);
463 memcpy(stringObjPtr
->bytes
+ pos
, Jim_String(argv
[2]), width
);
464 /* No padding is needed since the string is already extended */
467 if (Jim_SetVariable(interp
, argv
[1], stringObjPtr
) != JIM_OK
) {
469 Jim_FreeNewObj(interp
, stringObjPtr
);
476 int Jim_packInit(Jim_Interp
*interp
)
478 if (Jim_PackageProvide(interp
, "pack", "1.0", JIM_ERRMSG
)) {
482 Jim_CreateCommand(interp
, "unpack", Jim_UnpackCmd
, NULL
, NULL
);
483 Jim_CreateCommand(interp
, "pack", Jim_PackCmd
, NULL
, NULL
);