aboutsummaryrefslogtreecommitdiffstats
path: root/lib/asn1
diff options
context:
space:
mode:
authorLukas Larsson <[email protected]>2011-07-20 12:08:05 +0200
committerLukas Larsson <[email protected]>2011-08-01 17:29:45 +0200
commit15cf7f55677f1c89959f4e79fcc7cad16f7b43b2 (patch)
tree73761e5c76d8194b11fd95bb004a5cd671681b3b /lib/asn1
parentdab9296e05c7b1ffa4e12a270c41741adb1da383 (diff)
downloadotp-15cf7f55677f1c89959f4e79fcc7cad16f7b43b2.tar.gz
otp-15cf7f55677f1c89959f4e79fcc7cad16f7b43b2.tar.bz2
otp-15cf7f55677f1c89959f4e79fcc7cad16f7b43b2.zip
Update ber encode nif to use a linked list memry buffer
The encoded ber binary is now a linked list of memory buffers which is written to from the back while allocating more segments as needed
Diffstat (limited to 'lib/asn1')
-rw-r--r--lib/asn1/c_src/asn1_erl_nif.c233
1 files changed, 137 insertions, 96 deletions
diff --git a/lib/asn1/c_src/asn1_erl_nif.c b/lib/asn1/c_src/asn1_erl_nif.c
index eacbade196..721b3dead5 100644
--- a/lib/asn1/c_src/asn1_erl_nif.c
+++ b/lib/asn1/c_src/asn1_erl_nif.c
@@ -94,15 +94,18 @@ int ber_decode_value(ErlNifEnv*, ERL_NIF_TERM *, unsigned char *, int *, int,
int);
/* BER ENCODE */
-int ber_encode(ErlNifEnv *, ERL_NIF_TERM , ErlNifBinary *,
- unsigned char **, unsigned int *);
+typedef struct ber_encode_mem_chunk mem_chunk_t;
-int ber_check_memory(ErlNifBinary *, unsigned char **, unsigned int *, unsigned int);
+int ber_encode(ErlNifEnv *, ERL_NIF_TERM , mem_chunk_t **, unsigned int *);
+
+void ber_free_chunks(mem_chunk_t *chunk);
+mem_chunk_t *ber_new_chunk(unsigned int length);
+int ber_check_memory(mem_chunk_t **curr, unsigned int needed);
int ber_encode_tag(ErlNifEnv *, ERL_NIF_TERM , unsigned int ,
- unsigned char **, unsigned int *);
+ mem_chunk_t **, unsigned int *);
-int ber_encode_length(size_t , ErlNifBinary *, unsigned char **, unsigned int *);
+int ber_encode_length(size_t , mem_chunk_t **, unsigned int *);
/*
*
@@ -1007,8 +1010,14 @@ int ber_decode_value(ErlNifEnv* env, ERL_NIF_TERM *value, unsigned char *in_buf,
return ASN1_OK;
}
-int ber_encode(ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *out_binary,
- unsigned char **curr, unsigned int *unused) {
+struct ber_encode_mem_chunk {
+ mem_chunk_t *next;
+ int length;
+ char *top;
+ char *curr;
+};
+
+int ber_encode(ErlNifEnv *env, ERL_NIF_TERM term, mem_chunk_t **curr, unsigned int *count) {
const ERL_NIF_TERM *tv;
unsigned int form;
@@ -1019,135 +1028,162 @@ int ber_encode(ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *out_binary,
form = enif_is_list(env, tv[1]) ? ASN1_CONSTRUCTED : ASN1_PRIMITIVE;
- // We need atleast 5 bytes to encode the next tlv, so we double the memory available if not enough is available
- if (ber_check_memory(out_binary, curr, unused, 5))
- return ASN1_ERROR;
-
- if (ber_encode_tag(env, tv[0], form, curr, unused))
- return ASN1_ERROR;
-
- if (form == ASN1_PRIMITIVE) {
+ switch (form) {
+ case ASN1_PRIMITIVE: {
ErlNifBinary value;
if (!enif_inspect_binary(env, tv[1], &value))
return ASN1_ERROR;
- if (ber_encode_length(value.size, out_binary, curr, unused))
+
+ if (ber_check_memory(curr, value.size))
return ASN1_ERROR;
- if(ber_check_memory(out_binary, curr, unused, value.size))
+ memcpy((*curr)->curr - value.size + 1, value.data, value.size);
+ (*curr)->curr -= value.size;
+ *count += value.size;
+
+ if (ber_encode_length(value.size, curr, count))
return ASN1_ERROR;
- memcpy(*curr, value.data, value.size);
- *curr += value.size;
- *unused -= value.size;
- } else {
- ErlNifBinary tmp_bin;
- unsigned char *tmp_curr;
- unsigned int tmp_unused = 40;
+
+ break;
+ }
+ case ASN1_CONSTRUCTED: {
ERL_NIF_TERM head, tail;
+ unsigned int tmp_cnt;
if (!enif_get_list_cell(env, tv[1], &head, &tail)) {
if (enif_is_empty_list(env, tv[1])) {
- **curr = 0;
- ++*curr;
- --*unused;
- return ASN1_OK;
+ *((*curr)->curr) = 0;
+ (*curr)->curr -= 1;
+ break;
} else
return ASN1_ERROR;
}
- if (!enif_alloc_binary(40, &tmp_bin))
- return ASN1_ERROR;
-
- tmp_curr = tmp_bin.data;
-
do {
- if (ber_encode(env, head, &tmp_bin, &tmp_curr, &tmp_unused)) {
- enif_release_binary(&tmp_bin);
+ tmp_cnt = 0;
+ if (ber_encode(env, head, curr, &tmp_cnt)) {
return ASN1_ERROR;
}
+ *count += tmp_cnt;
} while (enif_get_list_cell(env, tail, &head, &tail));
- if (ber_encode_length(tmp_bin.size - tmp_unused, out_binary, curr,
- unused)) {
- enif_release_binary(&tmp_bin);
+ if (ber_check_memory(curr, *count)) {
return ASN1_ERROR;
}
- if (ber_check_memory(out_binary, curr, unused,
- tmp_bin.size - tmp_unused)) {
- enif_release_binary(&tmp_bin);
+ if (ber_encode_length(*count, curr, count)) {
return ASN1_ERROR;
}
- memcpy(*curr, tmp_bin.data, tmp_bin.size - tmp_unused);
- *curr += tmp_bin.size - tmp_unused;
- *unused -= tmp_bin.size - tmp_unused;
- enif_release_binary(&tmp_bin);
+ break;
}
+ }
+
+ // We need atleast 5 bytes to encode the next tlv
+ if (ber_check_memory(curr, 3))
+ return ASN1_ERROR;
+
+ if (ber_encode_tag(env, tv[0], form, curr, count))
+ return ASN1_ERROR;
return ASN1_OK;
}
int ber_encode_tag(ErlNifEnv *env, ERL_NIF_TERM tag, unsigned int form,
- unsigned char **curr, unsigned int *unused) {
- unsigned int class_tag_no;
+ mem_chunk_t **curr, unsigned int *count) {
+ unsigned int class_tag_no, head_tag;
if (!enif_get_uint(env, tag, &class_tag_no))
return ASN1_ERROR;
- **curr = form | ((class_tag_no & 196608) >> 10);
- class_tag_no = class_tag_no & 65535;
+ head_tag = form | ((class_tag_no & 0x30000) >> 10);
+ class_tag_no = class_tag_no & 0xFFFF;
if (class_tag_no <= 30) {
- **curr |= class_tag_no;
- ++*curr; --*unused;
+ *(*curr)->curr = head_tag | class_tag_no;
+ (*curr)->curr -= 1;
+ (*count)++;
return ASN1_OK;
} else {
- **curr |= 31;
- ++*curr; --*unused;
- while (class_tag_no > 127) {
- **curr = (class_tag_no & 127) | 128;
- class_tag_no = class_tag_no >> 7;
- ++*curr;
- --*unused;
+ *(*curr)->curr = class_tag_no & 127;
+ class_tag_no = class_tag_no >> 7;
+ (*curr)->curr -= 1;
+ (*count)++;
+
+ while (class_tag_no > 0) {
+ *(*curr)->curr = (class_tag_no & 127) | 0x80;
+ class_tag_no >>= 7;
+ (*curr)->curr -= 1;
+ (*count)++;
}
- **curr = class_tag_no;
- ++*curr;
- --*unused;
+
+ *(*curr)->curr = head_tag | 0x1F;
+ (*curr)->curr -= 1;
+ (*count)++;
+
return ASN1_OK;
}
}
-int ber_encode_length(size_t size, ErlNifBinary *out_binary, unsigned char **curr, unsigned int *unused) {
+int ber_encode_length(size_t size, mem_chunk_t **curr, unsigned int *count) {
if (size < 128) {
- if (ber_check_memory(out_binary, curr, unused, 1u))
+ if (ber_check_memory(curr, 1u))
return ASN1_ERROR;
- **curr = size;
- ++*curr;
- --*unused;
+ *(*curr)->curr = size;
+ (*curr)->curr -= 1;
+ (*count)++;
} else {
int chunks = size / 256 + 1;
- if (ber_check_memory(out_binary, curr, unused, chunks + 1))
+ if (ber_check_memory(curr, chunks + 1))
return ASN1_ERROR;
- **curr = chunks + 128;
- ++*curr;
- --*unused;
- while (chunks > 0)
+
+ while (size > 0)
{
- **curr = ((size >> (8*(chunks-1)))) & 255;
- ++*curr;
- --*unused;
- chunks--;
+ *(*curr)->curr = size & 0xFF;
+ size >>= 8;
+ (*curr)->curr -= 1;
+ (*count)++;
}
+
+ *(*curr)->curr = chunks | 0x80;
+ (*curr)->curr -= 1;
+ (*count)++;
}
return ASN1_OK;
}
-int ber_check_memory(ErlNifBinary *bin, unsigned char **curr, unsigned int *unused,unsigned int needed) {
- int incr;
- if (*unused >= needed)
+mem_chunk_t *ber_new_chunk(unsigned int length) {
+ mem_chunk_t *new = malloc(sizeof(mem_chunk_t));
+ if (new == NULL)
+ return NULL;
+ new->next = NULL;
+ new->top = malloc(sizeof(char) * length);
+ if (new->top == NULL) {
+ free(new);
+ return NULL;
+ }
+ new->curr = new->top + length - 1;
+ new->length = length;
+ return new;
+}
+
+void ber_free_chunks(mem_chunk_t *chunk) {
+ mem_chunk_t *curr, *next = chunk;
+ while (next != NULL) {
+ curr = next;
+ next = curr->next;
+ free(curr->top);
+ free(curr);
+ }
+}
+
+int ber_check_memory(mem_chunk_t **curr, unsigned int needed) {
+ mem_chunk_t *new;
+ if ((*curr)->curr-needed >= (*curr)->top)
return ASN1_OK;
- incr = bin->size > needed ? bin->size : needed;
- if (per_realloc_memory(bin, bin->size + incr, curr))
+
+ if ((new = ber_new_chunk((*curr)->length > needed ? (*curr)->length * 2 : (*curr)->length + needed)) == NULL)
return ASN1_ERROR;
- *unused += incr;
+ new->next = *curr;
+ *curr = new;
return ASN1_OK;
}
@@ -1202,28 +1238,33 @@ static ERL_NIF_TERM decode_ber_tlv(ErlNifEnv* env, int argc,
static ERL_NIF_TERM encode_ber_tlv(ErlNifEnv* env, int argc,
const ERL_NIF_TERM argv[]) {
ErlNifBinary out_binary;
- int complete_len;
- unsigned int unused = 40;
- unsigned char *curr;
+ unsigned int length = 0, pos = 0;
+ mem_chunk_t *curr, *top;
ERL_NIF_TERM err_code;
- if (!enif_alloc_binary(unused, &out_binary))
- return enif_make_atom(env, "alloc_binary_failed");
+ curr = ber_new_chunk(40);
- curr = out_binary.data;
-
- if ((complete_len = ber_encode(env, argv[0], &out_binary, &curr, &unused))
+ if ((ber_encode(env, argv[0], &curr, &length))
<= ASN1_ERROR) {
- enif_release_binary(&out_binary);
- if (complete_len == ASN1_ERROR)
- err_code = enif_make_uint(env, '1');
- else
- err_code = enif_make_uint(env, 0);
+ err_code = enif_make_uint(env, 0);
return enif_make_tuple2(env, enif_make_atom(env, "error"), err_code);
}
- if (unused != 0)
- enif_realloc_binary(&out_binary, out_binary.size - unused);
+ if (!enif_alloc_binary(length, &out_binary)) {
+ return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env,"oom"));
+ }
+
+ top = curr;
+
+ while (curr != NULL) {
+ length = curr->length - (curr->curr-curr->top) -1;
+ if (length > 0)
+ memcpy(out_binary.data + pos, curr->curr+1, length);
+ pos += length;
+ curr = curr->next;
+ }
+
+ ber_free_chunks(top);
return enif_make_binary(env, &out_binary);
}