bitvec: Add get/set byte sequences

The new functions bitvec_get_bytes and bitvec_set_bytes copy
byte sequences from bitvecs to uint8_t arrays and vice versa.
While the bytes in the bitvecs do not need to be aligned, the uint8_t
arrays always are. In case the bytes in the bitvec are aligned, the
implementation uses memcpy.

Note that the implementation like the other existing functions assume
MSB first encoding.

[hfreyther: Squash the comment fix into this commit as well]

Sponsored-by: On-Waves ehf
This commit is contained in:
Jacob Erlbeck 2015-12-21 16:04:03 +01:00 committed by Holger Hans Peter Freyther
parent 8114294bf2
commit 5f349be820
6 changed files with 162 additions and 2 deletions

View File

@ -28,6 +28,14 @@
/*! \file bitvec.h
* \brief Osmocom bit vector abstraction
*
* These functions assume a MSB (most significant bit) first layout of the
* bits, so that for instance the 5 bit number abcde (a is MSB) can be
* embedded into a byte sequence like in xxxxxxab cdexxxxx. The bit count
* starts with the MSB, so the bits in a byte are numbered (MSB) 01234567 (LSB).
* Note that there are other incompatible encodings, like it is used
* for the EGPRS RLC data block headers (there the bits are numbered from LSB
* to MSB).
*/
#include <stdint.h>
@ -63,5 +71,7 @@ int bitvec_set_uint(struct bitvec *bv, unsigned int in, int count);
int bitvec_get_uint(struct bitvec *bv, int num_bits);
int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, enum bit_value val);
int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit);
int bitvec_get_bytes(struct bitvec *bv, uint8_t *bytes, int count);
int bitvec_set_bytes(struct bitvec *bv, const uint8_t *bytes, int count);
/*! @} */

View File

@ -1,6 +1,7 @@
/* bit vector utility routines */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
* (C) 2015 by Sysmocom s.f.m.c. GmbH
*
* All Rights Reserved
*
@ -30,6 +31,7 @@
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <osmocom/core/bitvec.h>
@ -261,4 +263,77 @@ int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n,
return -1;
}
/*! \brief get multiple bytes from current pos
* Assumes MSB first encoding.
* \param[in] bv bit vector
* \param[in] bytes array
* \param[in] count number of bytes to copy
*/
int bitvec_get_bytes(struct bitvec *bv, uint8_t *bytes, int count)
{
int byte_offs = bytenum_from_bitnum(bv->cur_bit);
int bit_offs = bv->cur_bit % 8;
uint8_t c, last_c;
int i;
uint8_t *src;
if (byte_offs + count + (bit_offs ? 1 : 0) > bv->data_len)
return -EINVAL;
if (bit_offs == 0) {
memcpy(bytes, bv->data + byte_offs, count);
} else {
src = bv->data + byte_offs;
last_c = *(src++);
for (i = count; i > 0; i--) {
c = *(src++);
*(bytes++) =
(last_c << bit_offs) |
(c >> (8 - bit_offs));
last_c = c;
}
}
bv->cur_bit += count * 8;
return 0;
}
/*! \brief set multiple bytes at current pos
* Assumes MSB first encoding.
* \param[in] bv bit vector
* \param[in] bytes array
* \param[in] count number of bytes to copy
*/
int bitvec_set_bytes(struct bitvec *bv, const uint8_t *bytes, int count)
{
int byte_offs = bytenum_from_bitnum(bv->cur_bit);
int bit_offs = bv->cur_bit % 8;
uint8_t c, last_c;
int i;
uint8_t *dst;
if (byte_offs + count + (bit_offs ? 1 : 0) > bv->data_len)
return -EINVAL;
if (bit_offs == 0) {
memcpy(bv->data + byte_offs, bytes, count);
} else if (count > 0) {
dst = bv->data + byte_offs;
/* Get lower bits of first dst byte */
last_c = *dst >> (8 - bit_offs);
for (i = count; i > 0; i--) {
c = *(bytes++);
*(dst++) =
(last_c << (8 - bit_offs)) |
(c >> bit_offs);
last_c = c;
}
/* Overwrite lower bits of N+1 dst byte */
*dst = (*dst & ((1 << (8 - bit_offs)) - 1)) |
(last_c << (8 - bit_offs));
}
bv->cur_bit += count * 8;
return 0;
}
/*! @} */

View File

@ -11,7 +11,8 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \
logging/logging_test fr/fr_test \
loggingrb/loggingrb_test strrb/strrb_test \
vty/vty_test comp128/comp128_test utils/utils_test \
smscb/gsm0341_test stats/stats_test
smscb/gsm0341_test stats/stats_test \
bitvec/bitvec_test
if ENABLE_MSGFILE
check_PROGRAMS += msgfile/msgfile_test
@ -38,6 +39,9 @@ auth_milenage_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/sr
bits_bitrev_test_SOURCES = bits/bitrev_test.c
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
conv_conv_test_SOURCES = conv/conv_test.c
conv_conv_test_LDADD = $(top_builddir)/src/libosmocore.la
@ -128,7 +132,8 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
fr/fr_test.ok loggingrb/logging_test.ok \
loggingrb/logging_test.err strrb/strrb_test.ok \
vty/vty_test.ok comp128/comp128_test.ok \
utils/utils_test.ok stats/stats_test.ok
utils/utils_test.ok stats/stats_test.ok \
bitvec/bitvec_test.ok
DISTCLEANFILES = atconfig

View File

@ -0,0 +1,62 @@
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/bitvec.h>
static void test_byte_ops()
{
struct bitvec bv;
const uint8_t *in = (const uint8_t *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
uint8_t out[26 + 2];
uint8_t data[64];
int i;
int rc;
int in_size = strlen((const char *)in);
printf("=== start %s ===\n", __func__);
bv.data = data;
bv.data_len = sizeof(data);
for (i = 0; i < 32; i++) {
/* Write to bitvec */
memset(data, 0x00, sizeof(data));
bv.cur_bit = i;
rc = bitvec_set_uint(&bv, 0x7e, 8);
OSMO_ASSERT(rc >= 0);
rc = bitvec_set_bytes(&bv, in, in_size);
OSMO_ASSERT(rc >= 0);
rc = bitvec_set_uint(&bv, 0x7e, 8);
OSMO_ASSERT(rc >= 0);
fprintf(stderr, "bitvec: %s\n", osmo_hexdump(bv.data, bv.data_len));
/* Read from bitvec */
memset(out, 0xff, sizeof(out));
bv.cur_bit = i;
rc = bitvec_get_uint(&bv, 8);
OSMO_ASSERT(rc == 0x7e);
rc = bitvec_get_bytes(&bv, out + 1, in_size);
OSMO_ASSERT(rc >= 0);
rc = bitvec_get_uint(&bv, 8);
OSMO_ASSERT(rc == 0x7e);
fprintf(stderr, "out: %s\n", osmo_hexdump(out, sizeof(out)));
OSMO_ASSERT(out[0] == 0xff);
OSMO_ASSERT(out[in_size+1] == 0xff);
OSMO_ASSERT(memcmp(in, out + 1, in_size) == 0);
}
printf("=== end %s ===\n", __func__);
}
int main(int argc, char **argv)
{
test_byte_ops();
return 0;
}

View File

@ -0,0 +1,2 @@
=== start test_byte_ops ===
=== end test_byte_ops ===

View File

@ -21,6 +21,12 @@ cat $abs_srcdir/bits/bitrev_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/bits/bitrev_test], [0], [expout])
AT_CLEANUP
AT_SETUP([bitvec])
AT_KEYWORDS([bitvec])
cat $abs_srcdir/bitvec/bitvec_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/bitvec/bitvec_test], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv])
AT_KEYWORDS([conv])
cat $abs_srcdir/conv/conv_test.ok > expout