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 | 134 +++++++++++++++++++++++++++++++++++++-- tests/bitvec/bitvec_test.c | 141 +++++++++++++++++++++++++++++++++++++++++- 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..592bfc2 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,18 @@ 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) +{ + if (num_bits > 15 || bv->cur_bit < num_bits) + return -EINVAL; + + if (num_bits < 9) + return bv->data[0] >> (8 - num_bits); + + return osmo_load16be(bv->data) >> (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 +256,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 +472,102 @@ 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'; + default: abort(); + } +} + +/*! \brief prints bit vector to provided string + * It's caller's responsibility to ensure that we won't shoot him in the foot: + * the provided buffer should be at lest cur_bit + 1 bytes long + */ +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 bit run in vector starting from the MSB + * \param[in] bv The boolean vector to work on + * \param[in] b The boolean, sequence of which is looked at from the vector start + * \returns Number of consecutive bits of \p b in \p bv + */ +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..800a040 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,75 @@ static void test_unhex(const char *hex)
int main(int argc, char **argv) { + 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
Signed-off-by: Max msuraev@sysmocom.de --- .gitignore | 1 + include/Makefile.am | 1 + include/osmocom/core/bitcomp.h | 42 ++++ src/Makefile.am | 2 +- src/bitcomp.c | 480 +++++++++++++++++++++++++++++++++++++++++ tests/Makefile.am | 7 +- tests/bits/bitcomp_test.c | 66 ++++++ tests/bits/bitcomp_test.ok | 29 +++ tests/testsuite.at | 6 + 9 files changed, 631 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..bf35927 --- /dev/null +++ b/src/bitcomp.c @@ -0,0 +1,480 @@ +/* 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 + */ +static const unsigned t4_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 + } +}; + +static const unsigned t4_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} +}; + +static const unsigned t4_min_term_length[] = {2, 4}; +static const unsigned t4_min_make_up_length[] = {10, 5}; + +static const unsigned t4_max_term_length[] = {12, 8}; +static const unsigned t4_max_make_up_length[] = {13, 9}; + +static const unsigned t4_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} +}; + +static const unsigned t4_make_up_ind[15] = {64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960}; + +static const unsigned t4_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 t4_rle_term(unsigned w, bool b, unsigned bits) +{ + unsigned i; + for (i = 0; i < 64; i++) + if (w == t4_term[b][i] && bits == t4_term_length[b][i]) + return i; + return -1; +} + +static inline int t4_rle_makeup(unsigned w, bool b, unsigned bits) +{ + unsigned i; + for (i = 0; i < 15; i++) + if (w == t4_make_up[b][i] && bits == t4_make_up_length[b][i]) + return t4_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 t4_rle(struct bitvec *bv, unsigned len, bool b) +{ + if (len >= 960) { + bitvec_set_uint(bv, t4_make_up[b][14], t4_make_up_length[b][14]); + return bitvec_set_uint(bv, t4_term[b][len - 960], t4_term_length[b][len - 960]); + } + + if (len >= 896) { + bitvec_set_uint(bv, t4_make_up[b][13], t4_make_up_length[b][13]); + return bitvec_set_uint(bv, t4_term[b][len - 896], t4_term_length[b][len - 896]); + } + + if (len >= 832) { + bitvec_set_uint(bv, t4_make_up[b][12], t4_make_up_length[b][12]); + return bitvec_set_uint(bv, t4_term[b][len - 832], t4_term_length[b][len - 832]); + } + + if (len >= 768) { + bitvec_set_uint(bv, t4_make_up[b][11], t4_make_up_length[b][11]); + return bitvec_set_uint(bv, t4_term[b][len - 768], t4_term_length[b][len - 768]); + } + + if (len >= 704) { + bitvec_set_uint(bv, t4_make_up[b][10], t4_make_up_length[b][10]); + return bitvec_set_uint(bv, t4_term[b][len - 704], t4_term_length[b][len - 704]); + } + + if (len >= 640) { + bitvec_set_uint(bv, t4_make_up[b][9], t4_make_up_length[b][9]); + return bitvec_set_uint(bv, t4_term[b][len - 640], t4_term_length[b][len - 640]); + } + + if (len >= 576) { + bitvec_set_uint(bv, t4_make_up[b][8], t4_make_up_length[b][8]); + return bitvec_set_uint(bv, t4_term[b][len - 576], t4_term_length[b][len - 576]); + } + + if (len >= 512) { + bitvec_set_uint(bv, t4_make_up[b][7], t4_make_up_length[b][7]); + return bitvec_set_uint(bv, t4_term[b][len - 512], t4_term_length[b][len - 512]); + } + + if (len >= 448) { + bitvec_set_uint(bv, t4_make_up[b][6], t4_make_up_length[b][6]); + return bitvec_set_uint(bv, t4_term[b][len - 448], t4_term_length[b][len - 448]); + } + + if (len >= 384) { + bitvec_set_uint(bv, t4_make_up[b][5], t4_make_up_length[b][5]); + return bitvec_set_uint(bv, t4_term[b][len - 384], t4_term_length[b][len - 384]); + } + + if (len >= 320) { + bitvec_set_uint(bv, t4_make_up[b][4], t4_make_up_length[b][4]); + return bitvec_set_uint(bv, t4_term[b][len - 320], t4_term_length[b][len - 320]); + } + + if (len >= 256) { + bitvec_set_uint(bv, t4_make_up[b][3], t4_make_up_length[b][3]); + return bitvec_set_uint(bv, t4_term[b][len - 256], t4_term_length[b][len - 256]); + } + + if (len >= 192) { + bitvec_set_uint(bv, t4_make_up[b][2], t4_make_up_length[b][2]); + return bitvec_set_uint(bv, t4_term[b][len - 192], t4_term_length[b][len - 192]); + } + + if (len >= 128) { + bitvec_set_uint(bv, t4_make_up[b][1], t4_make_up_length[b][1]); + return bitvec_set_uint(bv, t4_term[b][len - 128], t4_term_length[b][len - 128]); + } + + if (len >= 64) { + bitvec_set_uint(bv, t4_make_up[b][0], t4_make_up_length[b][0]); + return bitvec_set_uint(bv, t4_term[b][len - 64], t4_term_length[b][len - 64]); + } + + return bitvec_set_uint(bv, t4_term[b][len], t4_term_length[b][len]); +} + +enum dec_state { + EXPECT_TERM, + TOO_LONG, + NEED_MORE_BITS, + CORRUPT, + OK +}; + +static inline enum dec_state _t4_step(struct bitvec *v, uint16_t w, bool b, unsigned bits, bool term_only) +{ + if (bits > t4_max_make_up_length[b]) + return TOO_LONG; + if (bits < t4_min_term_length[b]) + return NEED_MORE_BITS; + + if (term_only) { + if (bits > t4_max_term_length[b]) + return CORRUPT; + int t = t4_rle_term(w, b, bits); + if (-1 != t) { + bitvec_fill(v, t, b ? ONE : ZERO); + return OK; + } + return NEED_MORE_BITS; + } + + int m = t4_rle_makeup(w, b, bits); + if (-1 != m) { + bitvec_fill(v, m, b ? ONE : ZERO); + return EXPECT_TERM; + } + + m = t4_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 using known color code: */ + unsigned bits = t4_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 = t4_min_term_length[b]; + w = bitvec_get_int16_msb(&vec, bits); + term_only = true; + break; + case OK: + bitvec_shiftl(&vec, bits); + bits = t4_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 -E2BIG; + 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); + t4_rle(&comp, rl0, false); + } else { + bitvec_shiftl(&vec, rl1); + t4_rle(&comp, rl1, true); + } + /* + TODO: implement backtracking for optimal encoding + printf(" -> [%d/%d]", comp.cur_bit + vec.cur_bit, bv->cur_bit); + */ + 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