Browse Source

Fixed encoder segfault.

I was being an absolute moron and not adjusting my two access
pointers to the data buffer after a realloc.
pull/8/merge
Paul J. Davis 14 years ago
parent
commit
0ec13a0aef
1 changed files with 59 additions and 50 deletions
  1. +59
    -50
      c_src/encoder.c

+ 59
- 50
c_src/encoder.c View File

@ -7,6 +7,7 @@
#define BIN_INC_SIZE 1024 #define BIN_INC_SIZE 1024
typedef struct { typedef struct {
ErlNifEnv* env; ErlNifEnv* env;
jiffy_st* atoms; jiffy_st* atoms;
@ -14,8 +15,7 @@ typedef struct {
int count; int count;
ERL_NIF_TERM iolist; ERL_NIF_TERM iolist;
ErlNifBinary curr;
int cleared;
ErlNifBinary* curr;
char* p; char* p;
unsigned char* u; unsigned char* u;
@ -23,21 +23,23 @@ typedef struct {
} Encoder; } Encoder;
int int
enc_init(Encoder* e, ErlNifEnv* env)
enc_init(Encoder* e, ErlNifEnv* env, ErlNifBinary* bin)
{ {
e->env = env; e->env = env;
e->atoms = enif_priv_data(env); e->atoms = enif_priv_data(env);
e->count = 0; e->count = 0;
e->iolist = enif_make_list(env, 0); e->iolist = enif_make_list(env, 0);
if(!enif_alloc_binary(BIN_INC_SIZE, &(e->curr))) {
e->curr = bin;
if(!enif_alloc_binary(BIN_INC_SIZE, e->curr)) {
return 0; return 0;
} }
e->cleared = 0;
e->p = (char*) e->curr.data;
e->u = (unsigned char*) e->curr.data;
memset(e->curr->data, 0, e->curr->size);
e->p = (char*) e->curr->data;
e->u = (unsigned char*) e->curr->data;
e->i = 0; e->i = 0;
return 1; return 1;
@ -46,8 +48,8 @@ enc_init(Encoder* e, ErlNifEnv* env)
void void
enc_destroy(Encoder* e) enc_destroy(Encoder* e)
{ {
if(!e->cleared) {
enif_release_binary(&(e->curr));
if(e->curr != NULL) {
enif_release_binary(e->curr);
} }
} }
@ -61,14 +63,14 @@ enc_error(Encoder* e, const char* msg)
int int
enc_result(Encoder* e, ERL_NIF_TERM* value) enc_result(Encoder* e, ERL_NIF_TERM* value)
{ {
if(e->i != e->curr.size) {
if(!enif_realloc_binary(&(e->curr), e->i)) {
if(e->i != e->curr->size) {
if(!enif_realloc_binary(e->curr, e->i)) {
return 0; return 0;
} }
} }
*value = enif_make_binary(e->env, &(e->curr));
e->cleared = 1;
*value = enif_make_binary(e->env, e->curr);
e->curr = NULL;
return 1; return 1;
} }
@ -77,19 +79,21 @@ enc_ensure(Encoder* e, size_t req)
{ {
size_t new_sz; size_t new_sz;
if(req < e->curr.size - e->i) {
if(req < e->curr->size - e->i) {
return 1; return 1;
} }
new_sz = req - (e->curr.size - e->i);
new_sz = req - (e->curr->size - e->i) + e->curr->size;
new_sz += BIN_INC_SIZE - (new_sz % BIN_INC_SIZE); new_sz += BIN_INC_SIZE - (new_sz % BIN_INC_SIZE);
assert(new_sz % BIN_INC_SIZE == 0 && "Invalid modulo math.");
if(!enif_realloc_binary(&(e->curr), new_sz)) {
assert(new_sz > e->curr->size && "Invalid size calculation.");
if(!enif_realloc_binary(e->curr, new_sz)) {
return 0; return 0;
} }
memset(&(e->u[e->i]), 0, e->curr.size - e->i);
e->p = (char*) e->curr->data;
e->u = (unsigned char*) e->curr->data;
memset(&(e->u[e->i]), 0, e->curr->size - e->i);
return 1; return 1;
} }
@ -113,6 +117,9 @@ enc_string(Encoder* e, ERL_NIF_TERM val)
ErlNifBinary bin; ErlNifBinary bin;
char atom[512]; char atom[512];
unsigned char* data;
size_t size;
int esc_extra = 0; int esc_extra = 0;
int ulen; int ulen;
int ui; int ui;
@ -122,20 +129,21 @@ enc_string(Encoder* e, ERL_NIF_TERM val)
if(!enif_inspect_binary(e->env, val, &bin)) { if(!enif_inspect_binary(e->env, val, &bin)) {
return 0; return 0;
} }
data = bin.data;
size = bin.size;
} else if(enif_is_atom(e->env, val)) { } else if(enif_is_atom(e->env, val)) {
if(!enif_get_atom(e->env, val, atom, 512, ERL_NIF_LATIN1)) { if(!enif_get_atom(e->env, val, atom, 512, ERL_NIF_LATIN1)) {
return 0; return 0;
} }
// Fake as a binary for code below.
bin.data = (unsigned char*) atom;
bin.size = strlen(atom);
data = (unsigned char*) atom;
size = strlen(atom);
} else { } else {
return 0; return 0;
} }
i = 0; i = 0;
while(i < bin.size) {
switch((char) bin.data[i]) {
while(i < size) {
switch((char) data[i]) {
case '\"': case '\"':
case '\\': case '\\':
case '/': case '/':
@ -148,71 +156,71 @@ enc_string(Encoder* e, ERL_NIF_TERM val)
i++; i++;
continue; continue;
default: default:
if(bin.data[i] < 0x20) {
if(data[i] < 0x20) {
esc_extra += 5; esc_extra += 5;
i++; i++;
continue; continue;
} else if(bin.data[i] < 0x80) {
} else if(data[i] < 0x80) {
i++; i++;
continue; continue;
} }
ulen = -1; ulen = -1;
if((bin.data[i] & 0xE0) == 0xC0) {
if((data[i] & 0xE0) == 0xC0) {
ulen = 1; ulen = 1;
} else if((bin.data[i] & 0xF0) == 0xE0) {
} else if((data[i] & 0xF0) == 0xE0) {
ulen = 2; ulen = 2;
} else if((bin.data[i] & 0xF8) == 0xF0) {
} else if((data[i] & 0xF8) == 0xF0) {
ulen = 3; ulen = 3;
} else if((bin.data[i] & 0xFC) == 0xF8) {
} else if((data[i] & 0xFC) == 0xF8) {
ulen = 4; ulen = 4;
} else if((bin.data[i] & 0xFE) == 0xFC) {
} else if((data[i] & 0xFE) == 0xFC) {
ulen = 5; ulen = 5;
} }
if(ulen < 0) { if(ulen < 0) {
return 0; return 0;
} }
if(i+1+ulen > bin.size) {
if(i+1+ulen > size) {
return 0; return 0;
} }
for(ui = 0; ui < ulen; ui++) { for(ui = 0; ui < ulen; ui++) {
if((bin.data[i+1+ui] & 0xC0) != 0x80) {
if((data[i+1+ui] & 0xC0) != 0x80) {
return 0; return 0;
} }
} }
if(ulen == 1) { if(ulen == 1) {
if((bin.data[i] & 0x1E) == 0)
if((data[i] & 0x1E) == 0)
return 0; return 0;
} else if(ulen == 2) { } else if(ulen == 2) {
if((bin.data[i] & 0x0F) + (bin.data[i+1] & 0x20) == 0)
if((data[i] & 0x0F) + (data[i+1] & 0x20) == 0)
return 0; return 0;
} else if(ulen == 3) { } else if(ulen == 3) {
if((bin.data[i] & 0x07) + (bin.data[i+1] & 0x30) == 0)
if((data[i] & 0x07) + (data[i+1] & 0x30) == 0)
return 0; return 0;
} else if(ulen == 4) { } else if(ulen == 4) {
if((bin.data[i] & 0x03) + (bin.data[i+1] & 0x38) == 0)
if((data[i] & 0x03) + (data[i+1] & 0x38) == 0)
return 0; return 0;
} else if(ulen == 5) { } else if(ulen == 5) {
if((bin.data[i] & 0x01) + (bin.data[i+1] & 0x3C) == 0)
if((data[i] & 0x01) + (data[i+1] & 0x3C) == 0)
return 0; return 0;
} }
i += 1 + ulen; i += 1 + ulen;
} }
} }
if(!enc_ensure(e, bin.size + esc_extra + 2)) {
if(!enc_ensure(e, size + esc_extra + 2)) {
return 0; return 0;
} }
e->p[e->i++] = '\"'; e->p[e->i++] = '\"';
i = 0; i = 0;
while(i < bin.size) {
switch((char) bin.data[i]) {
while(i < size) {
switch((char) data[i]) {
case '\"': case '\"':
case '\\': case '\\':
case '/': case '/':
e->p[e->i++] = '\\'; e->p[e->i++] = '\\';
e->u[e->i++] = bin.data[i];
e->u[e->i++] = data[i];
i++; i++;
continue; continue;
case '\b': case '\b':
@ -241,16 +249,16 @@ enc_string(Encoder* e, ERL_NIF_TERM val)
i++; i++;
continue; continue;
default: default:
if(bin.data[i] < 0x20) {
if(data[i] < 0x20) {
e->p[e->i++] = '\\'; e->p[e->i++] = '\\';
e->p[e->i++] = 'u'; e->p[e->i++] = 'u';
if(!int_to_hex(bin.data[i], &(e->p[e->i]))) {
if(!int_to_hex(data[i], &(e->p[e->i]))) {
return 0; return 0;
} }
e->i += 4; e->i += 4;
i++; i++;
} else { } else {
e->u[e->i++] = bin.data[i++];
e->u[e->i++] = data[i++];
} }
} }
} }
@ -282,7 +290,7 @@ enc_double(Encoder* e, double val)
return 0; return 0;
} }
snprintf(&(e->p[e->i]), 32, "%g", val);
snprintf(&(e->p[e->i]), 31, "%g", val);
e->i += strlen(&(e->p[e->i])); e->i += strlen(&(e->p[e->i]));
e->count++; e->count++;
@ -344,6 +352,7 @@ encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
Encoder enc; Encoder enc;
Encoder* e = &enc; Encoder* e = &enc;
ErlNifBinary bin;
ERL_NIF_TERM ret; ERL_NIF_TERM ret;
ERL_NIF_TERM stack; ERL_NIF_TERM stack;
@ -360,7 +369,7 @@ encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
return enif_make_badarg(env); return enif_make_badarg(env);
} }
if(!enc_init(e, env)) {
if(!enc_init(e, env, &bin)) {
return enif_make_badarg(env); return enif_make_badarg(env);
} }
@ -412,7 +421,7 @@ encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
stack = enif_make_list_cell(env, tuple[1], stack); stack = enif_make_list_cell(env, tuple[1], stack);
} else if(enif_is_identical(curr, e->atoms->ref_array)) { } else if(enif_is_identical(curr, e->atoms->ref_array)) {
if(!enif_get_list_cell(env, stack, &curr, &stack)) { if(!enif_get_list_cell(env, stack, &curr, &stack)) {
ret = enc_error(e, "internal_error.5");
ret = enc_error(e, "internal_error");
goto done; goto done;
} }
if(enif_is_empty_list(env, curr)) { if(enif_is_empty_list(env, curr)) {

Loading…
Cancel
Save