From: Max msuraev@sysmocom.de
Add bit filling, shifting and other functions necessary for bit compression implementation. Add corresponding tests. --- include/osmocom/core/bitvec.h | 9 +++ src/bitvec.c | 132 ++++++++++++++++++++++++++++++++++++-- tests/bitvec/bitvec_test.c | 143 +++++++++++++++++++++++++++++++++++++++++- tests/bitvec/bitvec_test.ok | 119 +++++++++++++++++++++++++++++++++++ 4 files changed, 396 insertions(+), 7 deletions(-)
diff --git a/include/osmocom/core/bitvec.h b/include/osmocom/core/bitvec.h index a7e6fc4..5314cf2 100644 --- a/include/osmocom/core/bitvec.h +++ b/include/osmocom/core/bitvec.h @@ -4,6 +4,7 @@
/* (C) 2009 by Harald Welte laforge@gnumonks.org * (C) 2012 Ivan Klyuchnikov + * (C) 2015 Sysmocom s.f.m.c. GmbH * * All Rights Reserved * @@ -41,6 +42,7 @@
#include <stdint.h> #include <talloc.h> +#include <stdbool.h>
/*! \brief A single GSM bit * @@ -82,5 +84,12 @@ unsigned int bitvec_pack(const struct bitvec *bv, uint8_t *buffer); unsigned int bitvec_unpack(struct bitvec *bv, const uint8_t *buffer); uint64_t bitvec_read_field(struct bitvec *bv, unsigned int *read_index, unsigned int len); int bitvec_write_field(struct bitvec *bv, unsigned int *write_index, uint64_t val, unsigned int len); +int bitvec_fill(struct bitvec *bv, unsigned int num_bits, enum bit_value fill); +char bit_value_to_char(enum bit_value v); +void bitvec_to_string_r(const struct bitvec *bv, char *str); +void bitvec_zero(struct bitvec *bv); +unsigned bitvec_rl(const struct bitvec *bv, bool b); +void bitvec_shiftl(struct bitvec *bv, unsigned int n); +int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits);
/*! @} */ diff --git a/src/bitvec.c b/src/bitvec.c index b5d2c24..fc016a6 100644 --- a/src/bitvec.c +++ b/src/bitvec.c @@ -34,7 +34,9 @@ #include <stdint.h> #include <string.h> #include <stdio.h> +#include <stdbool.h>
+#include <osmocom/core/bits.h> #include <osmocom/core/bitvec.h>
#define BITNUM_FROM_COMP(byte, bit) ((byte*8)+bit) @@ -224,6 +226,20 @@ int bitvec_set_uint(struct bitvec *bv, unsigned int ui, unsigned int num_bits) return 0; }
+/*! \brief get multiple bits (num_bits) from beginning of vector (MSB side) */ +int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits) +{ + uint8_t tmp[2]; + if (num_bits > 15 || bv->cur_bit < num_bits) + return -EINVAL; + + if (num_bits < 9) + return bv->data[0] >> (8 - num_bits); + + memcpy(tmp, bv->data, 2); + return osmo_load16be(tmp) >> (16 - num_bits); +} + /*! \brief get multiple bits (based on numeric value) from current pos */ int bitvec_get_uint(struct bitvec *bv, unsigned int num_bits) { @@ -242,15 +258,27 @@ int bitvec_get_uint(struct bitvec *bv, unsigned int num_bits) return ui; }
+/*! \brief fill num_bits with \fill starting from the current position + * returns 0 on success, negative otherwise (out of vector boundary) + */ +int bitvec_fill(struct bitvec *bv, unsigned int num_bits, enum bit_value fill) +{ + unsigned i, stop = bv->cur_bit + num_bits; + for (i = bv->cur_bit; i < stop; i++) + if (bitvec_set_bit(bv, fill) < 0) + return -EINVAL; + + return 0; +} + /*! \brief pad all remaining bits up to num_bits */ int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit) { - unsigned int i; - - for (i = bv->cur_bit; i <= up_to_bit; i++) - bitvec_set_bit(bv, L); + int n = up_to_bit - bv->cur_bit + 1; + if (n < 1) + return 0;
- return 0; + return bitvec_fill(bv, n, L); }
/*! \brief find first bit set in bit vector */ @@ -446,4 +474,98 @@ int bitvec_write_field(struct bitvec *bv, unsigned int *write_index, uint64_t va return 0; }
+/*! \brief convert enum to corresponding character */ +char bit_value_to_char(enum bit_value v) +{ + switch (v) { + case ZERO: return '0'; + case ONE: return '1'; + case L: return 'L'; + case H: return 'H'; + } + /* make compiler happy - "avoid control reaches end of non-void function" warning: */ + return '?'; +} + +/*! \brief prints bit vector to provided string + * It's caller's responcibility to ensure that we won't shoot him in the foot. + */ +void bitvec_to_string_r(const struct bitvec *bv, char *str) +{ + unsigned i, pos = 0; + char *cur = str; + for (i = 0; i < bv->cur_bit; i++) { + if (0 == i % 8) + *cur++ = ' '; + *cur++ = bit_value_to_char(bitvec_get_bit_pos(bv, i)); + pos++; + } + *cur = 0; +} + +/* we assume that x have at least 1 non-b bit */ +static inline unsigned _leading_bits(uint8_t x, bool b) +{ + if (b) { + if (x < 0x80) return 0; + if (x < 0xC0) return 1; + if (x < 0xE0) return 2; + if (x < 0xF0) return 3; + if (x < 0xF8) return 4; + if (x < 0xFC) return 5; + if (x < 0xFE) return 6; + } else { + if (x > 0x7F) return 0; + if (x > 0x3F) return 1; + if (x > 0x1F) return 2; + if (x > 0xF) return 3; + if (x > 7) return 4; + if (x > 3) return 5; + if (x > 1) return 6; + } + return 7; +} +/*! \brief force bit vector to all 0 and current bit to the beginnig of the vector */ +void bitvec_zero(struct bitvec *bv) +{ + bv->cur_bit = 0; + memset(bv->data, 0, bv->data_len); +} + +/*! \brief Return number (bits) of uninterrupted run of \b in \bv starting from the MSB */ +unsigned bitvec_rl(const struct bitvec *bv, bool b) +{ + unsigned i; + for (i = 0; i < (bv->cur_bit % 8 ? bv->cur_bit / 8 + 1 : bv->cur_bit / 8); i++) { + if ( (b ? 0xFF : 0) != bv->data[i]) + return i * 8 + _leading_bits(bv->data[i], b); + } + + return bv->cur_bit; +} + +/*! \brief Shifts bitvec to the left, n MSB bits lost */ +void bitvec_shiftl(struct bitvec *bv, unsigned n) +{ + if (0 == n) + return; + if (n >= bv->cur_bit) { + bitvec_zero(bv); + return; + } + + memmove(bv->data, bv->data + n / 8, bv->data_len - n / 8); + + uint8_t tmp[2]; + unsigned i; + for (i = 0; i < bv->data_len - 2; i++) { + uint16_t t = osmo_load16be(bv->data + i); + osmo_store16be(t << (n % 8), &tmp); + bv->data[i] = tmp[0]; + } + + bv->data[bv->data_len - 1] <<= (n % 8); + bv->cur_bit -= n; +} + /*! @} */ diff --git a/tests/bitvec/bitvec_test.c b/tests/bitvec/bitvec_test.c index 7d131e0..4fc9db3 100644 --- a/tests/bitvec/bitvec_test.c +++ b/tests/bitvec/bitvec_test.c @@ -3,9 +3,82 @@ #include <stdlib.h> #include <stdint.h> #include <string.h> +#include <time.h> +#include <stdbool.h> +#include <errno.h>
#include <osmocom/core/utils.h> #include <osmocom/core/bitvec.h> +#include <osmocom/core/bits.h> + +#define BIN_PATTERN "%d%d%d%d%d%d%d%d" +#define BIN(byte) \ + (byte & 0x80 ? 1 : 0), \ + (byte & 0x40 ? 1 : 0), \ + (byte & 0x20 ? 1 : 0), \ + (byte & 0x10 ? 1 : 0), \ + (byte & 0x08 ? 1 : 0), \ + (byte & 0x04 ? 1 : 0), \ + (byte & 0x02 ? 1 : 0), \ + (byte & 0x01 ? 1 : 0) + +static char lol[1024]; // we pollute this with printed vectors +static inline void test_rl(const struct bitvec *bv) +{ + bitvec_to_string_r(bv, lol); + printf("%s [%d] RL0=%d, RL1=%d\n", lol, bv->cur_bit, bitvec_rl(bv, false), bitvec_rl(bv, true)); +} + +static inline void test_shift(struct bitvec *bv, unsigned n) +{ + bitvec_to_string_r(bv, lol); + printf("%s << %d:\n", lol, n); + bitvec_shiftl(bv, n); + bitvec_to_string_r(bv, lol); + printf("%s\n", lol); +} + +static inline void test_get(struct bitvec *bv, unsigned n) +{ + bitvec_to_string_r(bv, lol); + printf("%s [%d]", lol, bv->cur_bit); + int16_t x = bitvec_get_int16_msb(bv, n); + uint8_t tmp[2]; + osmo_store16be(x, &tmp); + printf(" -> %d (%u bit) ["BIN_PATTERN" "BIN_PATTERN"]:\n", x, n, BIN(tmp[0]), BIN(tmp[1])); + bitvec_to_string_r(bv, lol); + printf("%s [%d]\n", lol, bv->cur_bit); +} + +static inline void test_fill(struct bitvec *bv, unsigned n, enum bit_value val) +{ + bitvec_to_string_r(bv, lol); + unsigned bvlen = bv->cur_bit; + int fi = bitvec_fill(bv, n, val); + printf("%c> FILL %s [%d] -%d-> [%d]:\n", bit_value_to_char(val), lol, bvlen, n, fi); + bitvec_to_string_r(bv, lol); + printf(" %s [%d]\n\n", lol, bv->cur_bit); +} + +static inline void test_spare(struct bitvec *bv, unsigned n) +{ + bitvec_to_string_r(bv, lol); + unsigned bvlen = bv->cur_bit; + int sp = bitvec_spare_padding(bv, n); + printf("%c> SPARE %s [%d] -%d-> [%d]:\n", bit_value_to_char(L), lol, bvlen, n, sp); + bitvec_to_string_r(bv, lol); + printf(" %s [%d]\n\n", lol, bv->cur_bit); +} + +static inline void test_set(struct bitvec *bv, enum bit_value bit) +{ + bitvec_to_string_r(bv, lol); + unsigned bvlen = bv->cur_bit; + int set = bitvec_set_bit(bv, bit); + printf("%c> SET %s [%d] ++> [%d]:\n", bit_value_to_char(bit), lol, bvlen, set); + bitvec_to_string_r(bv, lol); + printf(" %s [%d]\n\n", lol, bv->cur_bit); +}
static void test_byte_ops() { @@ -33,7 +106,7 @@ static void test_byte_ops() rc = bitvec_set_uint(&bv, 0x7e, 8); OSMO_ASSERT(rc >= 0);
- fprintf(stderr, "bitvec: %s\n", osmo_hexdump(bv.data, bv.data_len)); + printf("bitvec: %s\n", osmo_hexdump(bv.data, bv.data_len));
/* Read from bitvec */ memset(out, 0xff, sizeof(out)); @@ -45,7 +118,7 @@ static void test_byte_ops() rc = bitvec_get_uint(&bv, 8); OSMO_ASSERT(rc == 0x7e);
- fprintf(stderr, "out: %s\n", osmo_hexdump(out, sizeof(out))); + printf("out: %s\n", osmo_hexdump(out, sizeof(out)));
OSMO_ASSERT(out[0] == 0xff); OSMO_ASSERT(out[in_size+1] == 0xff); @@ -72,11 +145,77 @@ static void test_unhex(const char *hex)
int main(int argc, char **argv) { + srand(time(NULL)); + + struct bitvec bv; + uint8_t i = 8, test[i]; + + memset(test, 0, i); + bv.data_len = i; + bv.data = test; + bv.cur_bit = 0; + + printf("test shifting...\n"); + + bitvec_set_uint(&bv, 0x0E, 7); + test_shift(&bv, 3); + test_shift(&bv, 17); + bitvec_set_uint(&bv, 0, 32); + bitvec_set_uint(&bv, 0x0A, 7); + test_shift(&bv, 24); + + printf("checking RL functions...\n"); + + bitvec_zero(&bv); + test_rl(&bv); + bitvec_set_uint(&bv, 0x000F, 32); + test_rl(&bv); + bitvec_shiftl(&bv, 18); + test_rl(&bv); + bitvec_set_uint(&bv, 0x0F, 8); + test_rl(&bv); + bitvec_zero(&bv); + bitvec_set_uint(&bv, 0xFF, 8); + test_rl(&bv); + bitvec_set_uint(&bv, 0xFE, 7); + test_rl(&bv); + bitvec_set_uint(&bv, 0, 17); + test_rl(&bv); + bitvec_shiftl(&bv, 18); + test_rl(&bv); + + printf("probing bit access...\n"); + + bitvec_zero(&bv); + bitvec_set_uint(&bv, 0x3747817, 32); + bitvec_shiftl(&bv, 10); + + test_get(&bv, 2); + test_get(&bv, 7); + test_get(&bv, 9); + test_get(&bv, 13); + test_get(&bv, 16); + test_get(&bv, 42); + + printf("feeling bit fills...\n"); + + test_set(&bv, ONE); + test_fill(&bv, 3, ZERO); + test_spare(&bv, 38); + test_spare(&bv, 43); + test_spare(&bv, 1); + test_spare(&bv, 7); + test_fill(&bv, 5, ONE); + test_fill(&bv, 3, L); + + printf("byte me...\n"); + test_byte_ops(); test_unhex("48282407a6a074227201000b2b2b2b2b2b2b2b2b2b2b2b"); test_unhex("47240c00400000000000000079eb2ac9402b2b2b2b2b2b"); test_unhex("47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b"); test_unhex("DEADFACE000000000000000000000000000000BEEFFEED"); test_unhex("FFFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"); + return 0; } diff --git a/tests/bitvec/bitvec_test.ok b/tests/bitvec/bitvec_test.ok index 83328b2..1c993d3 100644 --- a/tests/bitvec/bitvec_test.ok +++ b/tests/bitvec/bitvec_test.ok @@ -1,4 +1,123 @@ +test shifting... + 0001110 << 3: + 1110 + 1110 << 17: + + 00000000 00000000 00000000 00000000 0001010 << 24: + 00000000 0001010 +checking RL functions... + [0] RL0=0, RL1=0 + 00000000 00000000 00000000 00001111 [32] RL0=28, RL1=0 + 00000000 001111 [14] RL0=10, RL1=0 + 00000000 00111100 001111 [22] RL0=10, RL1=0 + 11111111 [8] RL0=0, RL1=8 + 11111111 1111110 [15] RL0=0, RL1=14 + 11111111 11111100 00000000 00000000 [32] RL0=0, RL1=14 + 00000000 000000 [14] RL0=14, RL1=0 +probing bit access... + 11010001 11100000 010111 [22] -> 3 (2 bit) [00000000 00000011]: + 11010001 11100000 010111 [22] + 11010001 11100000 010111 [22] -> 104 (7 bit) [00000000 01101000]: + 11010001 11100000 010111 [22] + 11010001 11100000 010111 [22] -> 419 (9 bit) [00000001 10100011]: + 11010001 11100000 010111 [22] + 11010001 11100000 010111 [22] -> 6716 (13 bit) [00011010 00111100]: + 11010001 11100000 010111 [22] + 11010001 11100000 010111 [22] -> -22 (16 bit) [11111111 11101010]: + 11010001 11100000 010111 [22] + 11010001 11100000 010111 [22] -> -22 (42 bit) [11111111 11101010]: + 11010001 11100000 010111 [22] +feeling bit fills... +1> SET 11010001 11100000 010111 [22] ++> [0]: + 11010001 11100000 0101111 [23] + +0> FILL 11010001 11100000 0101111 [23] -3-> [0]: + 11010001 11100000 01011110 00 [26] + +L> SPARE 11010001 11100000 01011110 00 [26] -38-> [0]: + 11010001 11100000 01011110 00101011 0010101 [39] + +L> SPARE 11010001 11100000 01011110 00101011 0010101 [39] -43-> [0]: + 11010001 11100000 01011110 00101011 00101011 0010 [44] + +L> SPARE 11010001 11100000 01011110 00101011 00101011 0010 [44] -1-> [0]: + 11010001 11100000 01011110 00101011 00101011 0010 [44] + +L> SPARE 11010001 11100000 01011110 00101011 00101011 0010 [44] -7-> [0]: + 11010001 11100000 01011110 00101011 00101011 0010 [44] + +1> FILL 11010001 11100000 01011110 00101011 00101011 0010 [44] -5-> [0]: + 11010001 11100000 01011110 00101011 00101011 00101111 1 [49] + +L> FILL 11010001 11100000 01011110 00101011 00101011 00101111 1 [49] -3-> [0]: + 11010001 11100000 01011110 00101011 00101011 00101111 1010 [52] + +byte me... === start test_byte_ops === +bitvec: 7e 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 7e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 3f 20 a1 21 a2 22 a3 23 a4 24 a5 25 a6 26 a7 27 a8 28 a9 29 aa 2a ab 2b ac 2c ad 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 1f 90 50 90 d1 11 51 91 d2 12 52 92 d3 13 53 93 d4 14 54 94 d5 15 55 95 d6 16 56 9f 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 0f c8 28 48 68 88 a8 c8 e9 09 29 49 69 89 a9 c9 ea 0a 2a 4a 6a 8a aa ca eb 0b 2b 4f c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 07 e4 14 24 34 44 54 64 74 84 94 a4 b4 c4 d4 e4 f5 05 15 25 35 45 55 65 75 85 95 a7 e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 03 f2 0a 12 1a 22 2a 32 3a 42 4a 52 5a 62 6a 72 7a 82 8a 92 9a a2 aa b2 ba c2 ca d3 f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 01 f9 05 09 0d 11 15 19 1d 21 25 29 2d 31 35 39 3d 41 45 49 4d 51 55 59 5d 61 65 69 f8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 fc 82 84 86 88 8a 8c 8e 90 92 94 96 98 9a 9c 9e a0 a2 a4 a6 a8 aa ac ae b0 b2 b4 fc 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 7e 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 7e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 3f 20 a1 21 a2 22 a3 23 a4 24 a5 25 a6 26 a7 27 a8 28 a9 29 aa 2a ab 2b ac 2c ad 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 1f 90 50 90 d1 11 51 91 d2 12 52 92 d3 13 53 93 d4 14 54 94 d5 15 55 95 d6 16 56 9f 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 0f c8 28 48 68 88 a8 c8 e9 09 29 49 69 89 a9 c9 ea 0a 2a 4a 6a 8a aa ca eb 0b 2b 4f c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 07 e4 14 24 34 44 54 64 74 84 94 a4 b4 c4 d4 e4 f5 05 15 25 35 45 55 65 75 85 95 a7 e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 03 f2 0a 12 1a 22 2a 32 3a 42 4a 52 5a 62 6a 72 7a 82 8a 92 9a a2 aa b2 ba c2 ca d3 f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 01 f9 05 09 0d 11 15 19 1d 21 25 29 2d 31 35 39 3d 41 45 49 4d 51 55 59 5d 61 65 69 f8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 fc 82 84 86 88 8a 8c 8e 90 92 94 96 98 9a 9c 9e a0 a2 a4 a6 a8 aa ac ae b0 b2 b4 fc 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 7e 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 7e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 3f 20 a1 21 a2 22 a3 23 a4 24 a5 25 a6 26 a7 27 a8 28 a9 29 aa 2a ab 2b ac 2c ad 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 1f 90 50 90 d1 11 51 91 d2 12 52 92 d3 13 53 93 d4 14 54 94 d5 15 55 95 d6 16 56 9f 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 0f c8 28 48 68 88 a8 c8 e9 09 29 49 69 89 a9 c9 ea 0a 2a 4a 6a 8a aa ca eb 0b 2b 4f c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 07 e4 14 24 34 44 54 64 74 84 94 a4 b4 c4 d4 e4 f5 05 15 25 35 45 55 65 75 85 95 a7 e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 03 f2 0a 12 1a 22 2a 32 3a 42 4a 52 5a 62 6a 72 7a 82 8a 92 9a a2 aa b2 ba c2 ca d3 f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 01 f9 05 09 0d 11 15 19 1d 21 25 29 2d 31 35 39 3d 41 45 49 4d 51 55 59 5d 61 65 69 f8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 00 fc 82 84 86 88 8a 8c 8e 90 92 94 96 98 9a 9c 9e a0 a2 a4 a6 a8 aa ac ae b0 b2 b4 fc 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 00 7e 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 7e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 00 3f 20 a1 21 a2 22 a3 23 a4 24 a5 25 a6 26 a7 27 a8 28 a9 29 aa 2a ab 2b ac 2c ad 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 00 1f 90 50 90 d1 11 51 91 d2 12 52 92 d3 13 53 93 d4 14 54 94 d5 15 55 95 d6 16 56 9f 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 00 0f c8 28 48 68 88 a8 c8 e9 09 29 49 69 89 a9 c9 ea 0a 2a 4a 6a 8a aa ca eb 0b 2b 4f c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 00 07 e4 14 24 34 44 54 64 74 84 94 a4 b4 c4 d4 e4 f5 05 15 25 35 45 55 65 75 85 95 a7 e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 00 03 f2 0a 12 1a 22 2a 32 3a 42 4a 52 5a 62 6a 72 7a 82 8a 92 9a a2 aa b2 ba c2 ca d3 f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 00 01 f9 05 09 0d 11 15 19 1d 21 25 29 2d 31 35 39 3d 41 45 49 4d 51 55 59 5d 61 65 69 f8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff +bitvec: 00 00 00 00 fc 82 84 86 88 8a 8c 8e 90 92 94 96 98 9a 9c 9e a0 a2 a4 a6 a8 aa ac ae b0 b2 b4 fc 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff === end test_byte_ops === 1 -=> cur_bit=184 48282407a6a074227201000b2b2b2b2b2b2b2b2b2b2b2b0000000000000000000000000000000000000000000000000000000000000000000000000000000000
From: Max msuraev@sysmocom.de
Add bit map encoder and decoder functions: decoder is fully functional while encoder is good enough for testing - no backtracking to find the best possible compression is implemented. If somebody is willing to implement MS side of EDGE than this has to be expanded. Add corresponding tests. N. B: the encoding is implemented according to ETSI TS 44.060 which is slightly different from T4 used for fax according to CCITT G31D (RFC 804).
Ticket: OW#2407 Sponsored-by: On-Waves ehf --- .gitignore | 1 + include/Makefile.am | 1 + include/osmocom/core/bitcomp.h | 42 ++++ src/Makefile.am | 2 +- src/bitcomp.c | 477 +++++++++++++++++++++++++++++++++++++++++ tests/Makefile.am | 7 +- tests/bits/bitcomp_test.c | 66 ++++++ tests/bits/bitcomp_test.ok | 29 +++ tests/testsuite.at | 6 + 9 files changed, 628 insertions(+), 3 deletions(-) create mode 100644 include/osmocom/core/bitcomp.h create mode 100644 src/bitcomp.c create mode 100644 tests/bits/bitcomp_test.c create mode 100644 tests/bits/bitcomp_test.ok
diff --git a/.gitignore b/.gitignore index 955634e..8904938 100644 --- a/.gitignore +++ b/.gitignore @@ -82,6 +82,7 @@ tests/vty/vty_test tests/gb/gprs_bssgp_test tests/smscb/gsm0341_test tests/bitvec/bitvec_test +tests/bits/bitcomp_test tests/gprs/gprs_test tests/msgb/msgb_test
diff --git a/include/Makefile.am b/include/Makefile.am index 07d6c00..a965fb9 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -7,6 +7,7 @@ nobase_include_HEADERS = \ osmocom/core/bit64gen.h \ osmocom/core/bits.h \ osmocom/core/bitvec.h \ + osmocom/core/bitcomp.h \ osmocom/core/conv.h \ osmocom/core/crc16.h \ osmocom/core/crc16gen.h \ diff --git a/include/osmocom/core/bitcomp.h b/include/osmocom/core/bitcomp.h new file mode 100644 index 0000000..89eccbc --- /dev/null +++ b/include/osmocom/core/bitcomp.h @@ -0,0 +1,42 @@ +#pragma once + +/* bit compression routines */ + +/* (C) 2016 sysmocom s.f.m.c. GmbH by Max Suraev msuraev@sysmocom.de + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/*! \defgroup bitcomp Bit compression + * @{ + */ + +/*! \file bitcomp.h + * \brief Osmocom bit compression routines + */ + +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/bitvec.h> + + +int osmo_t4_encode(struct bitvec *bv); +int osmo_t4_decode(const struct bitvec *in, bool cc, struct bitvec *out); + +/*! @} */ diff --git a/src/Makefile.am b/src/Makefile.am index c46cddf..45a77e3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,7 +9,7 @@ lib_LTLIBRARIES = libosmocore.la
libosmocore_la_LIBADD = $(BACKTRACE_LIB) $(TALLOC_LIBS) libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c bits.c \ - bitvec.c statistics.c \ + bitvec.c bitcomp.c statistics.c \ write_queue.c utils.c socket.c \ logging.c logging_syslog.c rate_ctr.c \ gsmtap_util.c crc16.c panic.c backtrace.c \ diff --git a/src/bitcomp.c b/src/bitcomp.c new file mode 100644 index 0000000..b0aa6de --- /dev/null +++ b/src/bitcomp.c @@ -0,0 +1,477 @@ +/* bit compression routines */ + +/* (C) 2016 sysmocom s.f.m.c. GmbH by Max Suraev msuraev@sysmocom.de + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/*! \defgroup bitcomp Bit compression + * @{ + */ + +/*! \file bitcomp.c + * \brief Osmocom bit compression routines + */ + +#include <stdint.h> +#include <stdbool.h> +#include <errno.h> +#include <string.h> + +#include <osmocom/core/bitvec.h> +#include <osmocom/core/bitcomp.h> + +/* + * Terminating codes for uninterrupted sequences of 0 and 1 up to 64 bit length + * according to TS 44.060 9.1.10 + */ +unsigned _term[2][64] = { + { + 0b0000110111, + 0b10, + 0b11, + 0b010, + 0b011, + 0b0011, + 0b0010, + 0b00011, + 0b000101, + 0b000100, + 0b0000100, + 0b0000101, + 0b0000111, + 0b00000100, + 0b00000111, + 0b000011000, + 0b0000010111, + 0b0000011000, + 0b0000001000, + 0b00001100111, + 0b00001101000, + 0b00001101100, + 0b00000110111, + 0b00000101000, + 0b00000010111, + 0b00000011000, + 0b000011001010, + 0b000011001011, + 0b000011001100, + 0b000011001101, + 0b000001101000, + 0b000001101001, + 0b000001101010, + 0b000001101011, + 0b000011010010, + 0b000011010011, + 0b000011010100, + 0b000011010101, + 0b000011010110, + 0b000011010111, + 0b000001101100, + 0b000001101101, + 0b000011011010, + 0b000011011011, + 0b000001010100, + 0b000001010101, + 0b000001010110, + 0b000001010111, + 0b000001100100, + 0b000001100101, + 0b000001010010, + 0b000001010011, + 0b000000100100, + 0b000000110111, + 0b000000111000, + 0b000000100111, + 0b000000101000, + 0b000001011000, + 0b000001011001, + 0b000000101011, + 0b000000101100, + 0b000001011010, + 0b000001100110, + 0b000001100111 + }, + { + 0b00110101, + 0b000111, + 0b0111, + 0b1000, + 0b1011, + 0b1100, + 0b1110, + 0b1111, + 0b10011, + 0b10100, + 0b00111, + 0b01000, + 0b001000, + 0b000011, + 0b110100, + 0b110101, + 0b101010, + 0b101011, + 0b0100111, + 0b0001100, + 0b0001000, + 0b0010111, + 0b0000011, + 0b0000100, + 0b0101000, + 0b0101011, + 0b0010011, + 0b0100100, + 0b0011000, + 0b00000010, + 0b00000011, + 0b00011010, + 0b00011011, + 0b00010010, + 0b00010011, + 0b00010100, + 0b00010101, + 0b00010110, + 0b00010111, + 0b00101000, + 0b00101001, + 0b00101010, + 0b00101011, + 0b00101100, + 0b00101101, + 0b00000100, + 0b00000101, + 0b00001010, + 0b00001011, + 0b01010010, + 0b01010011, + 0b01010100, + 0b01010101, + 0b00100100, + 0b00100101, + 0b01011000, + 0b01011001, + 0b01011010, + 0b01011011, + 0b01001010, + 0b01001011, + 0b00110010, + 0b00110011, + 0b00110100 + } +}; + +unsigned _term_length[2][64] = { + {10, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 9, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {8, 6, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8} +}; + +unsigned _min_term_length[] = {2, 4}; +unsigned _min_make_up_length[] = {10, 5}; + +unsigned _max_term_length[] = {12, 8}; +unsigned _max_make_up_length[] = {13, 9}; + +unsigned _make_up_length[2][15] = { + {10, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13}, + {5, 5, 6, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9} +}; + +unsigned _make_up_ind[15] = {64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960}; + +unsigned _make_up[2][15] = { + { + 0b0000001111, + 0b000011001000, + 0b000011001001, + 0b000001011011, + 0b000000110011, + 0b000000110100, + 0b000000110101, + 0b0000001101100, + 0b0000001101101, + 0b0000001001010, + 0b0000001001011, + 0b0000001001100, + 0b0000001001101, + 0b0000001110010, + 0b0000001110011 + }, + { + 0b11011, + 0b10010, + 0b010111, + 0b0110111, + 0b00110110, + 0b00110111, + 0b01100100, + 0b01100101, + 0b01101000, + 0b01100111, + 0b011001100, + 0b011001101, + 0b011010010, + 0b011010011, + 0b011010100 + } +}; + +/*! \brief Attempt to decode compressed bit vector + * + * Return length of RLE according to modified ITU-T T.4 from TS 44.060 Table 9.1.10.2 + * or -1 if no applicable RLE found + * N. B: we need explicit bit length to make decoding unambiguous +*/ +static inline int _rle_term(unsigned w, bool b, unsigned bits) +{ + unsigned i; + for (i = 0; i < 64; i++) + if (w == _term[b][i] && bits == _term_length[b][i]) + return i; + return -1; +} + +static inline int _rle_makeup(unsigned w, bool b, unsigned bits) +{ + unsigned i; + for (i = 0; i < 15; i++) + if (w == _make_up[b][i] && bits == _make_up_length[b][i]) + return _make_up_ind[i]; + return -1; +} + +/*! \brief Make-up codes for a given length + * + * Return proper make-up code word for an uninterrupted sequence of b bits + * of length len according to modified ITU-T T.4 from TS 44.060 Table 9.1.10.2 */ +static inline int _rle(struct bitvec *bv, unsigned len, bool b) +{ + if (len >= 960) { + bitvec_set_uint(bv, _make_up[b][14], _make_up_length[b][14]); + return bitvec_set_uint(bv, _term[b][len - 960], _term_length[b][len - 960]); + } + + if (len >= 896) { + bitvec_set_uint(bv, _make_up[b][13], _make_up_length[b][13]); + return bitvec_set_uint(bv, _term[b][len - 896], _term_length[b][len - 896]); + } + + if (len >= 832) { + bitvec_set_uint(bv, _make_up[b][12], _make_up_length[b][12]); + return bitvec_set_uint(bv, _term[b][len - 832], _term_length[b][len - 832]); + } + + if (len >= 768) { + bitvec_set_uint(bv, _make_up[b][11], _make_up_length[b][11]); + return bitvec_set_uint(bv, _term[b][len - 768], _term_length[b][len - 768]); + } + + if (len >= 704) { + bitvec_set_uint(bv, _make_up[b][10], _make_up_length[b][10]); + return bitvec_set_uint(bv, _term[b][len - 704], _term_length[b][len - 704]); + } + + if (len >= 640) { + bitvec_set_uint(bv, _make_up[b][9], _make_up_length[b][9]); + return bitvec_set_uint(bv, _term[b][len - 640], _term_length[b][len - 640]); + } + + if (len >= 576) { + bitvec_set_uint(bv, _make_up[b][8], _make_up_length[b][8]); + return bitvec_set_uint(bv, _term[b][len - 576], _term_length[b][len - 576]); + } + + if (len >= 512) { + bitvec_set_uint(bv, _make_up[b][7], _make_up_length[b][7]); + return bitvec_set_uint(bv, _term[b][len - 512], _term_length[b][len - 512]); + } + + if (len >= 448) { + bitvec_set_uint(bv, _make_up[b][6], _make_up_length[b][6]); + return bitvec_set_uint(bv, _term[b][len - 448], _term_length[b][len - 448]); + } + + if (len >= 384) { + bitvec_set_uint(bv, _make_up[b][5], _make_up_length[b][5]); + return bitvec_set_uint(bv, _term[b][len - 384], _term_length[b][len - 384]); + } + + if (len >= 320) { + bitvec_set_uint(bv, _make_up[b][4], _make_up_length[b][4]); + return bitvec_set_uint(bv, _term[b][len - 320], _term_length[b][len - 320]); + } + + if (len >= 256) { + bitvec_set_uint(bv, _make_up[b][3], _make_up_length[b][3]); + return bitvec_set_uint(bv, _term[b][len - 256], _term_length[b][len - 256]); + } + + if (len >= 192) { + bitvec_set_uint(bv, _make_up[b][2], _make_up_length[b][2]); + return bitvec_set_uint(bv, _term[b][len - 192], _term_length[b][len - 192]); + } + + if (len >= 128) { + bitvec_set_uint(bv, _make_up[b][1], _make_up_length[b][1]); + return bitvec_set_uint(bv, _term[b][len - 128], _term_length[b][len - 128]); + } + + if (len >= 64) { + bitvec_set_uint(bv, _make_up[b][0], _make_up_length[b][0]); + return bitvec_set_uint(bv, _term[b][len - 64], _term_length[b][len - 64]); + } + + return bitvec_set_uint(bv, _term[b][len], _term_length[b][len]); +} + +enum dec_state { + EXPECT_TERM, // only TERM is expected, keep parsing with the same color code + TOO_LONG, // that's what she said + NEED_MORE_BITS, // parsing failed, retry with bigger code word + CORRUPT, // kaboom! how the hell did you made that happen?! + OK // move along, nothing to see here +}; + +static inline enum dec_state _t4_step(struct bitvec *v, uint16_t w, bool b, unsigned bits, bool term_only) +{ + if (bits > _max_make_up_length[b]) + return TOO_LONG; + if (bits < _min_term_length[b]) + return NEED_MORE_BITS; + + if (term_only) { + if (bits > _max_term_length[b]) + return CORRUPT; + int t = _rle_term(w, b, bits); + if (-1 != t) { + bitvec_fill(v, t, b ? ONE : ZERO); + return OK; + } + return NEED_MORE_BITS; + } + + int m = _rle_makeup(w, b, bits); + if (-1 != m) { + bitvec_fill(v, m, b ? ONE : ZERO); + return EXPECT_TERM; + } + + m = _rle_term(w, b, bits); + if (-1 != m) { + bitvec_fill(v, m, b ? ONE : ZERO); + return OK; + } + + return NEED_MORE_BITS; +} + +/*! \brief decode T4-encoded bit vector + * Assumes MSB first encoding. + * \param[in] in bit vector with encoded data + * \param[in] cc color code (whether decoding should start with 1 or 0) + * \param[out] out the bit vector to store result into + * returns 0 on success, negative value otherwise + */ +int osmo_t4_decode(const struct bitvec *in, bool cc, struct bitvec *out) +{ + uint8_t orig[in->data_len]; + struct bitvec vec; + vec.data = orig; + vec.data_len = in->data_len; + bitvec_zero(&vec); + memcpy(vec.data, in->data, in->data_len); + vec.cur_bit = in->cur_bit; + + // init decoder: + unsigned bits = _min_term_length[cc]; + enum dec_state d; + int16_t w = bitvec_get_int16_msb(&vec, bits); + bool b = cc; + bool term_only = false; + + while (vec.cur_bit > 0) { + d = _t4_step(out, w, b, bits, term_only); + + switch (d) { + case EXPECT_TERM: + bitvec_shiftl(&vec, bits); + bits = _min_term_length[b]; + w = bitvec_get_int16_msb(&vec, bits); + term_only = true; + break; + case OK: + bitvec_shiftl(&vec, bits); + bits = _min_term_length[!b]; + w = bitvec_get_int16_msb(&vec, bits); + b = !b; + term_only = false; + break; + case NEED_MORE_BITS: + bits++; + w = bitvec_get_int16_msb(&vec, bits); + break; + case TOO_LONG: + return -EINVAL; + case CORRUPT: + return -EINVAL; + } + } + + return 0; +} + +/*! \brief encode bit vector in-place using T4 encoding + * Assumes MSB first encoding. + * \param[in] bv bit vector to be encoded + * returns color code (if the encoding started with 0 or 1) or -1 on failure (encoded is bigger than original) + */ +int osmo_t4_encode(struct bitvec *bv) +{ + unsigned rl0 = bitvec_rl(bv, false), rl1 = bitvec_rl(bv, true); + int r = (rl0 > rl1) ? 0 : 1; + uint8_t orig[bv->data_len], tmp[bv->data_len * 2]; // FIXME: better estimate max possible encoding overhead + struct bitvec comp, vec; + comp.data = tmp; + comp.data_len = bv->data_len * 2; + bitvec_zero(&comp); + vec.data = orig; + vec.data_len = bv->data_len; + bitvec_zero(&vec); + memcpy(vec.data, bv->data, bv->data_len); + vec.cur_bit = bv->cur_bit; + + while (vec.cur_bit > 0) { + if (rl0 > rl1) { + bitvec_shiftl(&vec, rl0); + _rle(&comp, rl0, false); + } else { + bitvec_shiftl(&vec, rl1); + _rle(&comp, rl1, true); + } +// printf(" -> [%d/%d]", comp.cur_bit + vec.cur_bit, bv->cur_bit); // TODO: implement backtracking + rl0 = bitvec_rl(&vec, false); + rl1 = bitvec_rl(&vec, true); + } + if (comp.cur_bit < bv->cur_bit) { + memcpy(bv->data, tmp, bv->data_len); + bv->cur_bit = comp.cur_bit; + return r; + } + return -1; +} + + diff --git a/tests/Makefile.am b/tests/Makefile.am index a4a6b2e..571e87b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -12,7 +12,7 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \ loggingrb/loggingrb_test strrb/strrb_test \ vty/vty_test comp128/comp128_test utils/utils_test \ smscb/gsm0341_test stats/stats_test \ - bitvec/bitvec_test msgb/msgb_test + bitvec/bitvec_test msgb/msgb_test bits/bitcomp_test
if ENABLE_MSGFILE check_PROGRAMS += msgfile/msgfile_test @@ -42,6 +42,9 @@ bits_bitrev_test_LDADD = $(top_builddir)/src/libosmocore.la bitvec_bitvec_test_SOURCES = bitvec/bitvec_test.c bitvec_bitvec_test_LDADD = $(top_builddir)/src/libosmocore.la
+bits_bitcomp_test_SOURCES = bits/bitcomp_test.c +bits_bitcomp_test_LDADD = $(top_builddir)/src/libosmocore.la + conv_conv_test_SOURCES = conv/conv_test.c conv_conv_test_LDADD = $(top_builddir)/src/libosmocore.la
@@ -136,7 +139,7 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \ loggingrb/logging_test.err strrb/strrb_test.ok \ vty/vty_test.ok comp128/comp128_test.ok \ utils/utils_test.ok stats/stats_test.ok \ - bitvec/bitvec_test.ok msgb/msgb_test.ok + bitvec/bitvec_test.ok msgb/msgb_test.ok bits/bitcomp_test.ok
DISTCLEANFILES = atconfig
diff --git a/tests/bits/bitcomp_test.c b/tests/bits/bitcomp_test.c new file mode 100644 index 0000000..f6895cf --- /dev/null +++ b/tests/bits/bitcomp_test.c @@ -0,0 +1,66 @@ +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <time.h> +#include <stdbool.h> +#include <errno.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/bits.h> +#include <osmocom/core/bitcomp.h> + +static char lol[1024]; // for pretty-printing + +int main(int argc, char **argv) +{ + srand(time(NULL)); + + struct bitvec bv, out; + uint8_t i = 20, test[i], data[i]; + + bv.data_len = i; + bv.data = test; + out.data_len = i; + out.data = data; + bitvec_zero(&bv); + bitvec_zero(&out); + + printf("\nrunning static tests...\n"); + + printf("\nTEST1:\n 00110111 01000111 10000001 1111\n"); + bitvec_zero(&bv); + bitvec_set_uint(&bv, 0x374781F, 28); bitvec_to_string_r(&bv, lol); printf("%s", lol); + + printf("\nEncoded:\n%d", osmo_t4_encode(&bv)); bitvec_to_string_r(&bv, lol); printf("%s", lol); + printf(" [%d]\nExpected:\n0 11011110 10001000 01110101 01100101 100 [35]\n", bv.cur_bit); + + bitvec_zero(&bv); + bitvec_set_uint(&bv, 0xDE887565, 32); + bitvec_set_uint(&bv, 4, 3); + bitvec_to_string_r(&bv, lol); + printf(" %s [%d]\n", lol, bv.cur_bit); + int d = osmo_t4_decode(&bv, 0, &out); + printf("\nDecoded:\n%d", d); + bitvec_to_string_r(&out, lol); + printf("%s [%d]\n", lol, out.cur_bit); + printf("Expected:\n 00110111 01000111 10000001 1111 \n"); + + printf("\nTEST2:\n 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00000000 00\n"); + bitvec_zero(&bv); + bitvec_set_uint(&bv, 0xFFFFFFFF, 32); + bitvec_set_uint(&bv, 0xFFFFFFFF, 32); + bitvec_set_uint(&bv, 0xFFFFFC00, 26); bitvec_to_string_r(&bv, lol); printf("%s", lol); + printf("\nEncoded:\n%d", osmo_t4_encode(&bv)); bitvec_to_string_r(&bv, lol); printf("%s", lol); + printf(" [%d]\nExpected:\n1 11011101 01000001 00 [18]\n", bv.cur_bit); + + bitvec_zero(&out); + d = osmo_t4_decode(&bv, 1, &out); + printf("\nDecoded:\n%d", d); + bitvec_to_string_r(&out, lol); + printf("%s [%d]\n", lol, out.cur_bit); + printf("Expected:\n 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00000000 00\n"); + + return 0; +} diff --git a/tests/bits/bitcomp_test.ok b/tests/bits/bitcomp_test.ok new file mode 100644 index 0000000..238f3c4 --- /dev/null +++ b/tests/bits/bitcomp_test.ok @@ -0,0 +1,29 @@ + +running static tests... + +TEST1: + 00110111 01000111 10000001 1111 + 00110111 01000111 10000001 1111 +Encoded: +-1 00110111 01000111 10000001 1111 [28] +Expected: +0 11011110 10001000 01110101 01100101 100 [35] + 11011110 10001000 01110101 01100101 100 [35] + +Decoded: +0 00110111 01000111 10000001 1111 [28] +Expected: + 00110111 01000111 10000001 1111 + +TEST2: + 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00000000 00 + 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00000000 00 +Encoded: +1 11011101 01000001 00 [18] +Expected: +1 11011101 01000001 00 [18] + +Decoded: +0 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00000000 00 [90] +Expected: + 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00000000 00 diff --git a/tests/testsuite.at b/tests/testsuite.at index 9cda1de..3d4d526 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -27,6 +27,12 @@ cat $abs_srcdir/bitvec/bitvec_test.ok > expout AT_CHECK([$abs_top_builddir/tests/bitvec/bitvec_test], [0], [expout], [ignore]) AT_CLEANUP
+AT_SETUP([bitcomp]) +AT_KEYWORDS([bitcomp]) +cat $abs_srcdir/bits/bitcomp_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/bits/bitcomp_test], [0], [expout]) +AT_CLEANUP + AT_SETUP([conv]) AT_KEYWORDS([conv]) cat $abs_srcdir/conv/conv_test.ok > expout
On 02 Feb 2016, at 12:19, suraev@alumni.ntnu.no wrote:
+unsigned _term[2][64] = {
same comment, _ is a namespace not for us. Please adjust it. Make it static const to not pollute the namespace and keep it in the rodata segment.
+enum dec_state {
- EXPECT_TERM, // only TERM is expected, keep parsing with the same color code
- TOO_LONG, // that's what she said
Try to avoid comments like that. Please don't use C99 comments and have a more technical description. The rest looks fine but I have no knowledge about T4.
thanks holger
On 02 Feb 2016, at 12:19, suraev@alumni.ntnu.no wrote:
Dear Max,
- memcpy(tmp, bv->data, 2);
- return osmo_load16be(tmp) >> (16 - num_bits);
load16be is working byte by byte as well so what do we win by this load?
+/*! \brief fill num_bits with \fill starting from the current position
- returns 0 on success, negative otherwise (out of vector boundary)
- */
+int bitvec_fill(struct bitvec *bv, unsigned int num_bits, enum bit_value fill) +{
- unsigned i, stop = bv->cur_bit + num_bits;
- for (i = bv->cur_bit; i < stop; i++)
if (bitvec_set_bit(bv, fill) < 0)return -EINVAL;- return 0;
+}
/*! \brief pad all remaining bits up to num_bits */ int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit) {
- unsigned int i;
- for (i = bv->cur_bit; i <= up_to_bit; i++)
bitvec_set_bit(bv, L);
- int n = up_to_bit - bv->cur_bit + 1;
- if (n < 1)
return 0;
so we are going from unsigned to int and then 'n' is "converted" to unsigned int as well. Is this what we want?
+/*! \brief convert enum to corresponding character */ +char bit_value_to_char(enum bit_value v) +{
- switch (v) {
- case ZERO: return '0';
- case ONE: return '1';
- case L: return 'L';
- case H: return 'H';
- }
- /* make compiler happy - "avoid control reaches end of non-void function" warning: */
seeing is believing? Which gcc? I had proposed to use __builtin_unreachable here. If we know we don't end up there, put this into the "branch" and the warning is gone. Or abort() or __builtin_trap.
- return '?';
+}
+/*! \brief prints bit vector to provided string
- It's caller's responcibility to ensure that we won't shoot him in the foot.
typo, how long is "str"? So either pass a size or write it in the comment of how long it needs to be depending on cur_bit? And what does the _r stand for? Is it similiar to the libc _r functions where an external buffer/pointer is provided?
+/* we assume that x have at least 1 non-b bit */ +static inline unsigned _leading_bits(uint8_t x, bool b)
_ and __ are reserved for the system, let us not use it. By having this method as static we already don't pollute the global namespace.
+/*! \brief Return number (bits) of uninterrupted run of \b in \bv starting from the MSB */
Are you sure that \b and \bv refer to the params? not like \param b, \param bv? Did you look at the documentation generated by doxygen?
fprintf(stderr, "out: %s\n", osmo_hexdump(out, sizeof(out)));
printf("out: %s\n", osmo_hexdump(out, sizeof(out)));
why? more output to be tracked? we could not ignore stderr if we want to?
OSMO_ASSERT(out[0] == 0xff); OSMO_ASSERT(out[in_size+1] == 0xff);@@ -72,11 +145,77 @@ static void test_unhex(const char *hex)
int main(int argc, char **argv) {
- srand(time(NULL));
why?
04.02.2016 13:39, Holger Freyther пишет:
- memcpy(tmp, bv->data, 2);
- return osmo_load16be(tmp) >> (16 - num_bits);
load16be is working byte by byte as well so what do we win by this load?
I don't really get this - win compared to what? This is new function, not replacement for some old code.
+/*! \brief fill num_bits with \fill starting from the current position
- returns 0 on success, negative otherwise (out of vector boundary)
- */
+int bitvec_fill(struct bitvec *bv, unsigned int num_bits, enum bit_value fill) +{
- unsigned i, stop = bv->cur_bit + num_bits;
- for (i = bv->cur_bit; i < stop; i++)
if (bitvec_set_bit(bv, fill) < 0)return -EINVAL;- return 0;
+}
/*! \brief pad all remaining bits up to num_bits */ int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit) {
- unsigned int i;
- for (i = bv->cur_bit; i <= up_to_bit; i++)
bitvec_set_bit(bv, L);
- int n = up_to_bit - bv->cur_bit + 1;
- if (n < 1)
return 0;so we are going from unsigned to int and then 'n' is "converted" to unsigned int as well. Is this what we want?
Yes. By the time we use n as unsigned we've explicitly checked already that it's positive. On the other hand the diff between 2 unsigned ints can be signed.
+/*! \brief convert enum to corresponding character */ +char bit_value_to_char(enum bit_value v) +{
- switch (v) {
- case ZERO: return '0';
- case ONE: return '1';
- case L: return 'L';
- case H: return 'H';
- }
- /* make compiler happy - "avoid control reaches end of non-void function" warning: */
seeing is believing? Which gcc? I had proposed to use __builtin_unreachable here. If we know we don't end up there, put this into the "branch" and the warning is gone. Or abort() or __builtin_trap.
I think __builtin_unreachable is deprecated but abort() in default: branch will do the trick.
- return '?';
+}
+/*! \brief prints bit vector to provided string
- It's caller's responcibility to ensure that we won't shoot him in the foot.
typo, how long is "str"? So either pass a size or write it in the comment of how long it needs to be depending on cur_bit? And what does the _r stand for? Is it similiar to the libc _r functions where an external buffer/pointer is provided?
The name was suggested by Harald in earlier review. The string should be at least cur_bit+1 bytes long.
+/* we assume that x have at least 1 non-b bit */ +static inline unsigned _leading_bits(uint8_t x, bool b)
_ and __ are reserved for the system, let us not use it. By having this method as static we already don't pollute the global namespace.
So how shall it be called to make it clear that this is internal to implementation/file?
+/*! \brief Return number (bits) of uninterrupted run of \b in \bv starting from the MSB */
Are you sure that \b and \bv refer to the params? not like \param b, \param bv? Did you look at the documentation generated by doxygen?
Indeed, it should be \param, or better yet - full comment instead of brief one.
fprintf(stderr, "out: %s\n", osmo_hexdump(out, sizeof(out)));
printf("out: %s\n", osmo_hexdump(out, sizeof(out)));why? more output to be tracked? we could not ignore stderr if we want to?
We could but since it's tests I'd rather have it under VCS in bitvec_test.ok explicitly. I think we only should ignore volatile things.
OSMO_ASSERT(out[0] == 0xff); OSMO_ASSERT(out[in_size+1] == 0xff);@@ -72,11 +145,77 @@ static void test_unhex(const char *hex)
int main(int argc, char **argv) {
- srand(time(NULL));
why?
Leftover from the time I was thinking of adding tests with random values. Can be safely removed.
cheers, Max.