From 01aa8b82dd0f8229355ffd2bb2bc8e8f496d2df6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 26 Apr 2019 19:43:53 +0200 Subject: erl_interface: Tweak bit string encode/decode API to support zero copy decoding and bit offset arguments for future unaligned bit strings. --- lib/erl_interface/src/encode/encode_binary.c | 101 ++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 9 deletions(-) (limited to 'lib/erl_interface/src/encode/encode_binary.c') diff --git a/lib/erl_interface/src/encode/encode_binary.c b/lib/erl_interface/src/encode/encode_binary.c index 4aa9f6bc16..0562979417 100644 --- a/lib/erl_interface/src/encode/encode_binary.c +++ b/lib/erl_interface/src/encode/encode_binary.c @@ -22,6 +22,10 @@ #include "eiext.h" #include "putget.h" +static void copy_bits(const unsigned char* src, size_t soffs, + unsigned char* dst, size_t n); + + int ei_encode_binary(char *buf, int *index, const void *p, long len) { char *s = buf + *index; @@ -40,23 +44,28 @@ int ei_encode_binary(char *buf, int *index, const void *p, long len) return 0; } -int ei_encode_bitstring(char *buf, int *index, const void *p, size_t bits) +int ei_encode_bitstring(char *buf, int *index, + const char *p, + size_t bitoffs, + size_t bits) { char *s = buf + *index; char *s0 = s; size_t bytes = (bits + 7) / 8; char last_bits = bits % 8; - if (bytes == 0 || last_bits == 0) - return ei_encode_binary(buf, index, p, bytes); - - if (!buf) s += 6; + if (!buf) s += last_bits ? 6 : 5; else { - put8(s, ERL_BIT_BINARY_EXT); + char* tagp = s++; put32be(s, bytes); - put8(s, last_bits); - memcpy(s, p, bytes); - s[bytes-1] &= (0xff << (8-last_bits)); + if (last_bits) { + *tagp = ERL_BIT_BINARY_EXT; + put8(s, last_bits); + } + else + *tagp = ERL_BINARY_EXT; + + copy_bits((const unsigned char*)p, bitoffs, (unsigned char*)s, bits); } s += bytes; @@ -64,3 +73,77 @@ int ei_encode_bitstring(char *buf, int *index, const void *p, size_t bits) return 0; } + + +/* + * MAKE_MASK(n) constructs a mask with n bits. + * Example: MAKE_MASK(3) returns the binary number 00000111. + */ +#define MAKE_MASK(n) ((((unsigned) 1) << (n))-1) + + +static +void copy_bits(const unsigned char* src, /* Base pointer to source. */ + size_t soffs, /* Bit offset for source relative to src. */ + unsigned char* dst, /* Destination. */ + size_t n) /* Number of bits to copy. */ +{ + unsigned rmask; + unsigned count; + unsigned deoffs; + unsigned bits; + unsigned bits1; + unsigned rshift; + + if (n == 0) + return; + + deoffs = n & 7; + rmask = deoffs ? (MAKE_MASK(deoffs) << (8-deoffs)) : 0; + + if (soffs == 0) { + unsigned nbytes = (n + 7) / 8; + memcpy(dst, src, nbytes); + if (rmask) + dst[nbytes-1] &= rmask; + return; + } + + src += soffs / 8; + soffs &= 7; + + if (n < 8) { /* Less than one byte */ + bits = (*src << soffs); + if (soffs+n > 8) { + src++; + bits |= (*src >> (8 - soffs)); + } + *dst = bits & rmask; + return; + } + + count = n >> 3; + + rshift = 8 - soffs; + bits = *src; + if (soffs + n > 8) { + src++; + } + + while (count--) { + bits1 = bits << soffs; + bits = *src; + src++; + *dst = bits1 | (bits >> rshift); + dst++; + } + + if (rmask) { + bits1 = bits << soffs; + if ((rmask << rshift) & 0xff) { + bits = *src; + bits1 |= (bits >> rshift); + } + *dst = bits1 & rmask; + } +} -- cgit v1.2.3