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 (Jim_GetWide(interp
, argv
[4], &width
) != JIM_OK
) {
300 if (option
== OPT_STR
) {
302 const char *str
= Jim_GetString(argv
[1], &len
);
304 if (width
% 8 || pos
% 8) {
305 Jim_SetResultString(interp
, "string field is not on a byte boundary", -1);
309 if (pos
>= 0 && width
> 0 && pos
< len
* 8) {
310 if (pos
+ width
> len
* 8) {
311 width
= len
* 8 - pos
;
313 Jim_SetResultString(interp
, str
+ pos
/ 8, width
/ 8);
319 const unsigned char *str
= (const unsigned char *)Jim_GetString(argv
[1], &len
);
322 if (width
> sizeof(jim_wide
) * 8) {
323 Jim_SetResultFormatted(interp
, "int field is too wide: %#s", argv
[4]);
327 if (pos
>= 0 && width
> 0 && pos
< len
* 8) {
328 if (pos
+ width
> len
* 8) {
329 width
= len
* 8 - pos
;
331 if (option
== OPT_INTBE
|| option
== OPT_UINTBE
|| option
== OPT_FLOATBE
) {
332 result
= JimBitIntBigEndian(str
, pos
, width
);
335 result
= JimBitIntLittleEndian(str
, pos
, width
);
337 if (option
== OPT_INTBE
|| option
== OPT_INTLE
) {
338 result
= JimSignExtend(result
, width
);
343 if (option
== OPT_FLOATBE
|| option
== OPT_FLOATLE
) {
346 fresult
= (double) JimIntToFloat(result
);
347 } else if (width
== 64) {
348 fresult
= JimIntToDouble(result
);
350 Jim_SetResultFormatted(interp
, "float field has bad bitwidth: %#s", argv
[4]);
353 Jim_SetResult(interp
, Jim_NewDoubleObj(interp
, fresult
));
355 Jim_SetResultInt(interp
, result
);
364 * Usage: pack varname value -intbe|-intle|-floatle|-floatbe|-str width ?bitoffset?
366 * Packs the binary representation of 'value' into the variable of the given name.
367 * The value is packed according to the given type, width and bitoffset.
368 * The variable is created if necessary (like [append])
369 * The variable is expanded if necessary
371 static int Jim_PackCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
374 static const char * const options
[] = { "-intle", "-intbe", "-floatle", "-floatbe",
376 enum { OPT_LE
, OPT_BE
, OPT_FLOATLE
, OPT_FLOATBE
, OPT_STR
};
381 Jim_Obj
*stringObjPtr
;
385 if (argc
!= 5 && argc
!= 6) {
386 Jim_WrongNumArgs(interp
, 1, argv
,
387 "varName value -intle|-intbe|-floatle|-floatbe|-str bitwidth ?bitoffset?");
390 if (Jim_GetEnum(interp
, argv
[3], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
393 if ((option
== OPT_LE
|| option
== OPT_BE
) &&
394 Jim_GetWide(interp
, argv
[2], &value
) != JIM_OK
) {
397 if ((option
== OPT_FLOATLE
|| option
== OPT_FLOATBE
) &&
398 Jim_GetDouble(interp
, argv
[2], &fvalue
) != JIM_OK
) {
401 if (Jim_GetWide(interp
, argv
[4], &width
) != JIM_OK
) {
404 if (width
<= 0 || (option
== OPT_STR
&& width
% 8) || (option
!= OPT_STR
&& width
> sizeof(jim_wide
) * 8) ||
405 ((option
== OPT_FLOATLE
|| option
== OPT_FLOATBE
) && width
!= 32 && width
!= 64)) {
406 Jim_SetResultFormatted(interp
, "bad bitwidth: %#s", argv
[4]);
410 if (Jim_GetWide(interp
, argv
[5], &pos
) != JIM_OK
) {
413 if (pos
< 0 || (option
== OPT_STR
&& pos
% 8)) {
414 Jim_SetResultFormatted(interp
, "bad bitoffset: %#s", argv
[5]);
419 stringObjPtr
= Jim_GetVariable(interp
, argv
[1], JIM_UNSHARED
);
421 /* Create the string if it doesn't exist */
422 stringObjPtr
= Jim_NewEmptyStringObj(interp
);
425 else if (Jim_IsShared(stringObjPtr
)) {
427 stringObjPtr
= Jim_DuplicateObj(interp
, stringObjPtr
);
430 len
= Jim_Length(stringObjPtr
) * 8;
432 /* Extend the string as necessary first */
433 while (len
< pos
+ width
) {
434 Jim_AppendString(interp
, stringObjPtr
, "", 1);
438 Jim_SetResultInt(interp
, pos
+ width
);
440 /* Now set the bits. Note that the the string *must* have no non-string rep
441 * since we are writing the bytes directly.
443 Jim_AppendString(interp
, stringObjPtr
, "", 0);
445 /* Convert floating point to integer if necessary */
446 if (option
== OPT_FLOATLE
|| option
== OPT_FLOATBE
) {
447 /* Note that the following is slightly incompatible with Tcl behaviour.
448 * In Tcl floating overflow gives FLT_MAX (cf. test binary-13.13).
449 * In Jim Tcl it gives Infinity. This behavior may change.
451 value
= (width
== 32) ? JimFloatToInt((float)fvalue
) : JimDoubleToInt(fvalue
);
454 if (option
== OPT_BE
|| option
== OPT_FLOATBE
) {
455 JimSetBitsIntBigEndian((unsigned char *)stringObjPtr
->bytes
, value
, pos
, width
);
457 else if (option
== OPT_LE
|| option
== OPT_FLOATLE
) {
458 JimSetBitsIntLittleEndian((unsigned char *)stringObjPtr
->bytes
, value
, pos
, width
);
464 if (width
> Jim_Length(argv
[2])) {
465 width
= Jim_Length(argv
[2]);
467 memcpy(stringObjPtr
->bytes
+ pos
, Jim_String(argv
[2]), width
);
468 /* No padding is needed since the string is already extended */
471 if (Jim_SetVariable(interp
, argv
[1], stringObjPtr
) != JIM_OK
) {
473 Jim_FreeNewObj(interp
, stringObjPtr
);
480 int Jim_packInit(Jim_Interp
*interp
)
482 if (Jim_PackageProvide(interp
, "pack", "1.0", JIM_ERRMSG
)) {
486 Jim_CreateCommand(interp
, "unpack", Jim_UnpackCmd
, NULL
, NULL
);
487 Jim_CreateCommand(interp
, "pack", Jim_PackCmd
, NULL
, NULL
);