static int decode_number(void * ctx, const char * numberVal, unsigned int numberLen) { // scan in the input to see if it's a float or int int numberType = 0; // 0 means integer, 1 means float unsigned int i; ErlNifBinary bin; int missingDot = 1; unsigned int expPos; for(i=0; i<numberLen; i++) { switch (numberVal[i]) { case '.': missingDot = 0; numberType = 1; // it's a float goto loopend; case 'E': case 'e': expPos = i; numberType = 1; // it's a float goto loopend; } } loopend: if ((numberType == 1) && missingDot) { if(!enif_alloc_binary_compat(ENV(ctx), numberLen + 2, &bin)) { return CANCEL; } memcpy(bin.data, numberVal, expPos); bin.data[expPos] = '.'; bin.data[expPos + 1] = '0'; memcpy(bin.data + expPos + 2, numberVal + expPos, numberLen - expPos); } else { if(!enif_alloc_binary_compat(ENV(ctx), numberLen, &bin)) { return CANCEL; } memcpy(bin.data, numberVal, numberLen); } add_to_head(ctx, enif_make_tuple(ENV(ctx), 2, enif_make_int(ENV(ctx), numberType), enif_make_binary(ENV(ctx), &bin))); return CONTINUE; }
static int decode_string(void* ctx, const unsigned char* data, unsigned int size) { ErlNifBinary bin; if(!enif_alloc_binary_compat(ENV(ctx), size, &bin)) { return CANCEL; } memcpy(bin.data, data, size); add_to_head(ctx, enif_make_binary(ENV(ctx), &bin)); return CONTINUE; }
ERL_NIF_TERM blake2_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { blake2_handle *handle = NULL; enif_get_resource(env, argv[0], blake2_hashstate, (void**)&handle); ErlNifBinary out; enif_alloc_binary_compat(env, (size_t)(handle->digest_length), &out); int r = blake2b_final(&(handle->state), (uint8_t *)out.data, handle->digest_length); if (r == 0) { return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_binary(env, &out)); } else { return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "finalization_failure")); } }
ERL_NIF_TERM blake2_hash(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { int bits = 0; enif_get_int(env, argv[0], &bits); ErlNifBinary bin, out; enif_inspect_binary(env, argv[1], &bin); enif_alloc_binary_compat(env, (size_t)(bits/8), &out); int r = blake2b((uint8_t *)out.data, (const void *)bin.data, NULL, (bits / 8), bin.size, 0); if (r == 0) { return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_binary(env, &out)); } else { return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "hash_failure")); } }
int hammy_get(ErlNifEnv *env, hammy_db *db, unsigned char *key, int key_size, ErlNifBinary *value) { ham_key_t k; ham_record_t rec; ham_txn_t *txn; int rc; int retval = HAMMY_FALSE; setup_key(&k, key, key_size); memset(&rec, 0, sizeof(ham_record_t)); ham_txn_begin(&txn, db->databases[0], HAM_TXN_READ_ONLY); rc = ham_find(db->databases[0], txn, &k, &rec, 0); if (rc == HAM_SUCCESS) { if (enif_alloc_binary_compat(env, rec.size, value)) { memcpy(value->data, rec.data, rec.size); retval = HAMMY_TRUE; } } ham_txn_commit(txn, 0); return retval; }
ERL_NIF_TERM derive_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ihandle* handle; ERL_NIF_TERM head; ERL_NIF_TERM tail; char fmt[4]; if(enif_get_resource(env, argv[0], EIM_IMAGE_RESOURCE, (void**)&handle) && enif_get_atom_compat(env, argv[1], fmt, 4, ERL_NIF_LATIN1) && enif_get_list_cell(env, argv[2], &head, &tail)) { ErlNifBinary new_binary; size_t new_length; unsigned char *new_blob; do { //todo: error handle int tuplec; const ERL_NIF_TERM* tuple; char type[9]; if(!enif_get_tuple(env, head, &tuplec, &tuple) || !enif_get_atom_compat(env, tuple[0], type, 9, ERL_NIF_LATIN1)) { return enif_make_badarg(env); } long x, y; long width, height; long value; int rotate; char dimension[7]; switch(type[0]) { case 's'://scale if(!enif_get_atom_compat(env, tuple[1], dimension, 7, ERL_NIF_LATIN1) || !enif_get_long(env, tuple[2], &value)) { return enif_make_badarg(env); } if(dimension[0]=='w') { handle->image->scale_width(value); } else if(dimension[0]=='h') { handle->image->scale_height(value); } else { return enif_make_badarg(env); } break; case 'c'://crop if(!enif_get_long(env, tuple[1], &width) || !enif_get_long(env, tuple[2], &height) || !enif_get_long(env, tuple[3], &x) || !enif_get_long(env, tuple[4], &y)) { return enif_make_badarg(env); } handle->image->crop(width, height, x, y); break; case 'm'://max if(!enif_get_atom_compat(env, tuple[1], dimension, 7, ERL_NIF_LATIN1) || !enif_get_long(env, tuple[2], &value)) { return enif_make_badarg(env); } if(dimension[0]=='w') { handle->image->max_width(value); } else if(dimension[0]=='h') { handle->image->max_height(value); } else { return enif_make_badarg(env); } break; case 'f'://fit if(!enif_get_long(env, tuple[1], &width) || !enif_get_long(env, tuple[2], &height)) { return enif_make_badarg(env); } handle->image->fit(width, height); break; case 'r'://rotate if(!enif_get_int(env, tuple[1], &rotate)) { return enif_make_badarg(env); } switch(rotate) { case 1: handle->image->rotate(EIM_ROTATE_90); break; case 2: handle->image->rotate(EIM_ROTATE_180); break; case 3: handle->image->rotate(EIM_ROTATE_270); break; default: return enif_make_badarg(env); }; break; case 'b'://box if(!enif_get_long(env, tuple[1], &width) || !enif_get_long(env, tuple[2], &height)) { return enif_make_badarg(env); } char float_x[7], float_y[7]; char floated; if(enif_get_atom_compat(env, tuple[3], float_x, 7, ERL_NIF_LATIN1)) { if(!enif_get_atom_compat(env, tuple[4], float_y, 7, ERL_NIF_LATIN1)) { return enif_make_badarg(env); } // can actually write top,left instead of left,top if you want // just helps with typos like that. Just works. switch(float_x[0]) { case 'l': floated = EIM_FLOAT_LEFT; break; case 'c': floated = EIM_FLOAT_CENTER; break; case 'r': floated = EIM_FLOAT_RIGHT; break; case 't': floated = EIM_FLOAT_TOP; break; case 'b': floated = EIM_FLOAT_BOTTOM; break; default: return enif_make_badarg(env); } switch(float_y[0]) { case 'l': floated ^= EIM_FLOAT_LEFT; break; case 'c': floated ^= EIM_FLOAT_CENTER; break; case 'r': floated ^= EIM_FLOAT_RIGHT; break; case 't': floated ^= EIM_FLOAT_TOP; break; case 'b': floated ^= EIM_FLOAT_BOTTOM; break; default: return enif_make_badarg(env); } } else { floated = EIM_FLOAT_CENTER | EIM_FLOAT_CENTER; } handle->image->box(width, height, floated); break; default: return enif_make_badarg(env); } } while(enif_get_list_cell(env, tail, &head, &tail)); EIM_FORMAT eim_format; switch(fmt[0]) { case 'j': eim_format = EIM_FORMAT_JPG; break; case 'g': eim_format = EIM_FORMAT_GIF; break; case 'p': eim_format = EIM_FORMAT_PNG; break; default: return enif_make_badarg(env); } try { new_blob = handle->image->process(eim_format, &new_length); enif_alloc_binary_compat(env, new_length, &new_binary); memcpy(new_binary.data, new_blob, new_length); return enif_make_binary(env, &new_binary); } catch(const char* msg) { return enif_make_atom(env, "error"); } } else { return enif_make_badarg(env); } }
ERL_NIF_TERM json_encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { Encoder enc; yajl_gen_config config = {0, NULL}; yajl_gen handle = yajl_gen_alloc(&config, NULL); yajl_gen_status status; ERL_NIF_TERM ret = enif_make_badarg(env); ErlNifBinary bin; const unsigned char* json; unsigned int jsonlen; if(argc != 1) goto done; if(handle == NULL) { ret = enif_make_tuple(env, 2, enif_make_atom(env, "error"), enif_make_atom(env, "memory_error") ); goto done; } enc.env = env; enc.handle = handle; enc.error = 0; if(enc_json(&enc, argv[0]) == ERROR) { if(enc.error == 0) { ret = enif_make_atom(env, "unknown"); } else { ret = enc.error; } ret = enif_make_tuple(env, 2, enif_make_atom(env, "error"), ret ); goto done; } status = yajl_gen_get_buf(handle, &json, &jsonlen); if(status != yajl_gen_status_ok) { ret = enif_make_tuple(env, 2, enif_make_atom(env, "error"), enif_make_atom(env, "unknown") ); goto done; } if(!enif_alloc_binary_compat(env, jsonlen, &bin)) { ret = enif_make_atom(env, "memory_error"); goto done; } memcpy(bin.data, json, jsonlen); ret = enif_make_tuple(env, 2, enif_make_atom(env, "ok"), enif_make_binary(env, &bin) ); done: if(handle != NULL) yajl_gen_free(handle); return ret; }
ERL_NIF_TERM final_encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM head = argv[0]; ERL_NIF_TERM term; double number; encode_ctx ctx; ctx.env = env; ctx.fill_offset = 0; ctx.error = 0; if (!enif_alloc_binary_compat(env, 100, &ctx.bin)) { return no_mem_error(env); } while(enif_get_list_cell(env, head, &term, &head)) { ErlNifBinary termbin; const ERL_NIF_TERM* array; int arity; int code; // We scan the list, looking for things to write into the binary, or // encode and then write into the binary. We encode values that are // tuples tagged with a type and a value: {Type, Value} where Type // is a an Integer and Value is what is to be encoded if (enif_get_tuple(env, term, &arity, &array)) { // It's a tuple to encode and copy if (arity != 2 || !enif_get_int(env, array[0], &code)) { // not arity 2 or the first element isn't an int ctx.error = BADARG; goto done; } if (code == 0) { // {0, String} if (encode_string(&ctx, array[1]) != SUCCESS) { goto done; } } else { // {1, Double} if(!enif_get_double(env, array[1], &number)) { ctx.error = BADARG; goto done; } // We can't encode these. if (isnan(number) || isinf(number)) { ctx.error = BADARG; goto done; } if ((ctx.error = ensure_buffer(&ctx, 32)) != SUCCESS) { goto done; } // write the string into the buffer snprintf((char*)ctx.bin.data+ctx.fill_offset, 32, "%.16g", number); // increment the length ctx.fill_offset += strlen((char*)ctx.bin.data+ctx.fill_offset); } } else if (enif_inspect_binary(env, term, &termbin)) { // this is a regular binary, copy the contents into the buffer fill_buffer(&ctx, (char*)termbin.data, termbin.size); if (ctx.error) { goto done; } } else { //not a binary, not a tuple, wtf! ctx.error = BADARG; goto done; } } done: if (ctx.error == NOMEM) { enif_release_binary_compat(env, &ctx.bin); return no_mem_error(env); } else if (ctx.error == BADARG) { enif_release_binary_compat(env, &ctx.bin); return enif_make_badarg(env); } // Resize the binary to our exact final size if(!enif_realloc_binary_compat(env, &(ctx.bin), ctx.fill_offset)) { enif_release_binary_compat(env, &ctx.bin); return no_mem_error(env); } // make the binary term which transfers ownership return enif_make_binary(env, &ctx.bin); }