aboutsummaryrefslogtreecommitdiffstats
path: root/lib/erl_interface/src/encode/encode_binary.c
diff options
context:
space:
mode:
authorSverker Eriksson <[email protected]>2019-05-02 13:34:04 +0200
committerSverker Eriksson <[email protected]>2019-05-02 13:34:04 +0200
commit6939b6908c39b1a0a68f588cffcafc222a038bb9 (patch)
tree527b3f039ae67892910c09e2806801403512ed00 /lib/erl_interface/src/encode/encode_binary.c
parent37bc04b573bc4a088e98a9cc5586a35c99b9eb00 (diff)
parent01aa8b82dd0f8229355ffd2bb2bc8e8f496d2df6 (diff)
downloadotp-6939b6908c39b1a0a68f588cffcafc222a038bb9.tar.gz
otp-6939b6908c39b1a0a68f588cffcafc222a038bb9.tar.bz2
otp-6939b6908c39b1a0a68f588cffcafc222a038bb9.zip
Merge branch 'sverker/erl_interface/bitstring-api'
* sverker/erl_interface/bitstring-api: erl_interface: Tweak bit string encode/decode API
Diffstat (limited to 'lib/erl_interface/src/encode/encode_binary.c')
-rw-r--r--lib/erl_interface/src/encode/encode_binary.c101
1 files changed, 92 insertions, 9 deletions
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;
+ }
+}