"Initial commit to Gerrit"
[profile/ivi/gpsd.git] / bits.c
1 /* bits.c - bitfield extraction code
2  *
3  * This file is Copyright (c)2010 by the GPSD project
4  * BSD terms apply: see the file COPYING in the distribution root for details.
5  *
6  * Bitfield extraction functions.  In each, start is a bit index (not
7  * a byte index) and width is a bit width.  The width bounded above by
8  * the bit width of a long long, which is 64 bits in all standard data
9  * models for 32- and 64-bit processors.
10  *
11  * The sbits() function assumes twos-complement arithmetic.
12  */
13 #include <assert.h>
14
15 #include "bits.h"
16 #ifdef DEBUG
17 #include <stdio.h>
18 #include "gpsd.h"
19 #endif /* DEBUG */
20
21 #define BITS_PER_BYTE   8
22
23 unsigned long long ubits(char buf[], unsigned int start, unsigned int width)
24 /* extract a (zero-origin) bitfield from the buffer as an unsigned big-endian long long */
25 {
26     unsigned long long fld = 0;
27     unsigned int i;
28     unsigned end;
29
30     /*@i1@*/ assert(width <= sizeof(long long) * BITS_PER_BYTE);
31     for (i = start / BITS_PER_BYTE;
32          i < (start + width + BITS_PER_BYTE - 1) / BITS_PER_BYTE; i++) {
33         fld <<= BITS_PER_BYTE;
34         fld |= (unsigned char)buf[i];
35     }
36 #ifdef DEBUG
37     (void)printf("%d:%d from %s:\n", start, width, gpsd_hexdump(buf, 32));
38 #endif
39
40 #ifdef DEBUG
41     (void)printf("    segment=0x%llx,", fld);
42 #endif /* DEBUG */
43     end = (start + width) % BITS_PER_BYTE;
44     if (end != 0) {
45         fld >>= (BITS_PER_BYTE - end);
46 #ifdef DEBUG
47         (void)printf(" after downshifting by %d bits: 0x%llx",
48                      BITS_PER_BYTE - end, fld);
49 #endif /* UDEBUG */
50     }
51 #ifdef DEBUG
52     (void)printf(" = %lld\n", fld);
53 #endif /* UDEBUG */
54
55     /*@ -shiftimplementation @*/
56     fld &= ~(-1LL << width);
57     /*@ +shiftimplementation @*/
58 #ifdef DEBUG
59     (void)
60         printf("    after selecting out the bottom %u bits: 0x%llx = %lld\n",
61                width, fld, fld);
62 #endif /* DEBUG */
63
64     return fld;
65 }
66
67 signed long long sbits(char buf[], unsigned int start, unsigned int width)
68 /* extract a bitfield from the buffer as a signed big-endian long */
69 {
70     unsigned long long fld = ubits(buf, start, width);
71
72 #ifdef SDEBUG
73     (void)fprintf(stderr, "sbits(%d, %d) extracts %llx\n", start, width, fld);
74 #endif /* SDEBUG */
75     /*@ +relaxtypes */
76     if (fld & (1 << (width - 1))) {
77 #ifdef SDEBUG
78         (void)fprintf(stderr, "%llx is signed\n", fld);
79 #endif /* SDEBUG */
80         /*@ -shiftimplementation @*/
81         fld |= (-1LL << (width - 1));
82         /*@ +shiftimplementation @*/
83     }
84 #ifdef SDEBUG
85     (void)fprintf(stderr, "sbits(%d, %d) returns %lld\n", start, width,
86                   (signed long long)fld);
87 #endif /* SDEBUG */
88     return (signed long long)fld;
89     /*@ -relaxtypes */
90 }