aboutsummaryrefslogtreecommitdiffstats
path: root/lib/erl_interface/src
diff options
context:
space:
mode:
authorSverker Eriksson <[email protected]>2019-04-26 19:43:53 +0200
committerSverker Eriksson <[email protected]>2019-04-26 19:43:53 +0200
commit01aa8b82dd0f8229355ffd2bb2bc8e8f496d2df6 (patch)
tree0a3ae48dfe7f442aa138d7c06fbf5444e9f7c8b6 /lib/erl_interface/src
parent8f6d45ddc8b2b12376c252a30b267a822cad171a (diff)
downloadotp-01aa8b82dd0f8229355ffd2bb2bc8e8f496d2df6.tar.gz
otp-01aa8b82dd0f8229355ffd2bb2bc8e8f496d2df6.tar.bz2
otp-01aa8b82dd0f8229355ffd2bb2bc8e8f496d2df6.zip
erl_interface: Tweak bit string encode/decode API
to support zero copy decoding and bit offset arguments for future unaligned bit strings.
Diffstat (limited to 'lib/erl_interface/src')
-rw-r--r--lib/erl_interface/src/decode/decode_binary.c61
-rw-r--r--lib/erl_interface/src/decode/decode_skip.c10
-rw-r--r--lib/erl_interface/src/encode/encode_binary.c101
-rw-r--r--lib/erl_interface/src/misc/ei_printterm.c14
-rw-r--r--lib/erl_interface/src/misc/ei_x_encode.c6
-rw-r--r--lib/erl_interface/src/misc/show_msg.c9
6 files changed, 135 insertions, 66 deletions
diff --git a/lib/erl_interface/src/decode/decode_binary.c b/lib/erl_interface/src/decode/decode_binary.c
index 2799438bef..0d28c67230 100644
--- a/lib/erl_interface/src/decode/decode_binary.c
+++ b/lib/erl_interface/src/decode/decode_binary.c
@@ -40,40 +40,41 @@ int ei_decode_binary(const char *buf, int *index, void *p, long *lenp)
return 0;
}
-int ei_decode_bitstring(const char *buf, int *index, void *p, size_t plen,
- size_t *bitsp)
+int ei_decode_bitstring(const char *buf, int *index,
+ const char** pp,
+ unsigned int* bitoffsp,
+ size_t *nbitsp)
{
- const char *s = buf + *index;
- const char *s0 = s;
- unsigned long len;
- unsigned char last_bits;
- const unsigned char tag = get8(s);
+ const char *s = buf + *index;
+ const char *s0 = s;
+ unsigned char last_bits;
+ const unsigned char tag = get8(s);
+ size_t len = get32be(s);
- if (tag == ERL_BINARY_EXT) {
- long bytes;
- int ret = ei_decode_binary(buf, index, p, &bytes);
- if (bitsp)
- *bitsp = (size_t)bytes * 8;
- return ret;
- }
+ switch(tag) {
+ case ERL_BINARY_EXT:
+ if (nbitsp)
+ *nbitsp = len * 8;
+ break;
+ case ERL_BIT_BINARY_EXT:
+ last_bits = get8(s);
+ if (((last_bits==0) != (len==0)) || last_bits > 8)
+ return -1;
- if (tag != ERL_BIT_BINARY_EXT)
- return -1;
-
- len = get32be(s);
- last_bits = get8(s);
-
- if (len > plen || ((last_bits==0) != (len==0)) || last_bits > 8)
- return -1;
-
- if (p)
- memcpy(p, s, len);
- s += len;
+ if (nbitsp)
+ *nbitsp = (len == 0) ? 0 : ((len-1) * 8) + last_bits;
+ break;
+ default:
+ return -1;
+ }
- if (bitsp)
- *bitsp = (len == 0) ? 0 : ((len-1) * 8) + last_bits;
+ if (pp)
+ *pp = s;
+ if (bitoffsp)
+ *bitoffsp = 0;
- *index += s-s0;
- return 0;
+ s += len;
+ *index += s-s0;
+ return 0;
}
diff --git a/lib/erl_interface/src/decode/decode_skip.c b/lib/erl_interface/src/decode/decode_skip.c
index 11d3bc1786..736c00e074 100644
--- a/lib/erl_interface/src/decode/decode_skip.c
+++ b/lib/erl_interface/src/decode/decode_skip.c
@@ -21,14 +21,6 @@
#include "eiext.h"
#include "decode_skip.h"
-#ifdef HAVE_STDINT_H
-# include <stdint.h>
-#endif
-
-#ifndef SIZE_MAX
-# define SIZE_MAX (~((size_t)0))
-#endif
-
int ei_skip_term(const char* buf, int* index)
{
int i, n, ty;
@@ -88,7 +80,7 @@ int ei_skip_term(const char* buf, int* index)
return -1;
break;
case ERL_BIT_BINARY_EXT:
- if (ei_decode_bitstring(buf, index, NULL, SIZE_MAX, NULL) < 0)
+ if (ei_decode_bitstring(buf, index, NULL, NULL, NULL) < 0)
return -1;
break;
case ERL_SMALL_INTEGER_EXT:
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;
+ }
+}
diff --git a/lib/erl_interface/src/misc/ei_printterm.c b/lib/erl_interface/src/misc/ei_printterm.c
index a89b990ac1..5c40fb7747 100644
--- a/lib/erl_interface/src/misc/ei_printterm.c
+++ b/lib/erl_interface/src/misc/ei_printterm.c
@@ -250,12 +250,13 @@ static int print_term(FILE* fp, ei_x_buff* x,
ei_free(p);
break;
case ERL_BIT_BINARY_EXT: {
+ const char* cp;
size_t bits;
+ unsigned int bitoffs;
int trunc = 0;
- p = ei_malloc(n);
- if (p == NULL) goto err;
- if (ei_decode_bitstring(buf, index, p, n, &bits) < 0) {
- ei_free(p);
+
+ if (ei_decode_bitstring(buf, index, &cp, &bitoffs, &bits) < 0
+ || bitoffs != 0) {
goto err;
}
ch_written += xprintf(fp, x, "#Bits<");
@@ -266,15 +267,14 @@ static int print_term(FILE* fp, ei_x_buff* x,
}
--m;
for (i = 0; i < m; ++i) {
- ch_written += xprintf(fp, x, "%d,", p[i]);
+ ch_written += xprintf(fp, x, "%d,", cp[i]);
}
- ch_written += xprintf(fp, x, "%d", p[i]);
+ ch_written += xprintf(fp, x, "%d", cp[i]);
if (trunc)
ch_written += xprintf(fp, x, ",...");
else if (bits % 8 != 0)
ch_written += xprintf(fp, x, ":%u", (unsigned)(bits % 8));
xputc('>', fp, x); ++ch_written;
- ei_free(p);
break;
}
case ERL_SMALL_INTEGER_EXT:
diff --git a/lib/erl_interface/src/misc/ei_x_encode.c b/lib/erl_interface/src/misc/ei_x_encode.c
index 2da271795f..8e77679d2a 100644
--- a/lib/erl_interface/src/misc/ei_x_encode.c
+++ b/lib/erl_interface/src/misc/ei_x_encode.c
@@ -117,14 +117,14 @@ int ei_x_encode_binary(ei_x_buff* x, const void* p, int len)
return ei_encode_binary(x->buff, &x->index, p, len);
}
-int ei_x_encode_bitstring(ei_x_buff* x, const void* p, size_t bits)
+int ei_x_encode_bitstring(ei_x_buff* x, const char* p, size_t bitoffs, size_t bits)
{
int i = x->index;
- if (ei_encode_bitstring(NULL, &i, p, bits) == -1)
+ if (ei_encode_bitstring(NULL, &i, p, bitoffs, bits) == -1)
return -1;
if (!x_fix_buff(x, i))
return -1;
- return ei_encode_bitstring(x->buff, &x->index, p, bits);
+ return ei_encode_bitstring(x->buff, &x->index, p, bitoffs, bits);
}
int ei_x_encode_long(ei_x_buff* x, long n)
diff --git a/lib/erl_interface/src/misc/show_msg.c b/lib/erl_interface/src/misc/show_msg.c
index 2d49eb6449..805d69e9b3 100644
--- a/lib/erl_interface/src/misc/show_msg.c
+++ b/lib/erl_interface/src/misc/show_msg.c
@@ -24,13 +24,6 @@
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
-#ifdef HAVE_STDINT_H
-# include <stdint.h>
-#endif
-
-#ifndef SIZE_MAX
-# define SIZE_MAX (~((size_t)0))
-#endif
#include <sys/types.h>
@@ -464,7 +457,7 @@ static void show_term(const char *termbuf, int *index, FILE *stream)
case ERL_BIT_BINARY_EXT: {
size_t bits;
- ei_decode_bitstring(termbuf, index, NULL, SIZE_MAX, &bits);
+ ei_decode_bitstring(termbuf, index, NULL, NULL, &bits);
fprintf(stream, "#Bits<%lu>", (unsigned long)bits);
break;
}