| /* |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); you may not use |
| this file except in compliance with the License. You may obtain a copy of the |
| License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software distributed |
| under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
| CONDITIONS OF ANY KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations under the License. |
| |
| */ |
| |
| #ifdef DARWIN |
| #define U_HIDE_DRAFT_API 1 |
| #define U_DISABLE_RENAMING 1 |
| #endif |
| |
| #include "erl_nif.h" |
| #include "unicode/ucol.h" |
| #include "unicode/ucasemap.h" |
| #include <stdio.h> |
| #include <assert.h> |
| |
| static ERL_NIF_TERM ATOM_TRUE; |
| static ERL_NIF_TERM ATOM_FALSE; |
| static ERL_NIF_TERM ATOM_NULL; |
| |
| typedef struct { |
| ErlNifEnv* env; |
| int error; |
| UCollator* coll; |
| } ctx_t; |
| |
| typedef struct { |
| UCollator** collators; |
| int collStackTop; |
| int numCollators; |
| ErlNifMutex* collMutex; |
| } priv_data_t; |
| |
| static ERL_NIF_TERM collate_nif(ErlNifEnv*, int, const ERL_NIF_TERM []); |
| static int collate_binary(priv_data_t*, ctx_t*, ERL_NIF_TERM, ERL_NIF_TERM, ERL_NIF_TERM); |
| static int on_load(ErlNifEnv*, void**, ERL_NIF_TERM); |
| static void on_unload(ErlNifEnv*, void*); |
| static __inline void reserve_coll(priv_data_t*, ctx_t*); |
| static __inline void release_coll(priv_data_t*, ctx_t*); |
| int on_reload(ErlNifEnv*, void**, ERL_NIF_TERM); |
| int on_upgrade(ErlNifEnv*, void**, void**, ERL_NIF_TERM); |
| |
| void |
| reserve_coll(priv_data_t* pData, ctx_t *ctx) |
| { |
| if (ctx->coll == NULL) { |
| enif_mutex_lock(pData->collMutex); |
| assert(pData->collStackTop < pData->numCollators); |
| ctx->coll = pData->collators[pData->collStackTop]; |
| pData->collStackTop += 1; |
| enif_mutex_unlock(pData->collMutex); |
| } |
| } |
| |
| |
| void |
| release_coll(priv_data_t* pData, ctx_t *ctx) |
| { |
| if (ctx->coll != NULL) { |
| enif_mutex_lock(pData->collMutex); |
| pData->collStackTop -= 1; |
| assert(pData->collStackTop >= 0); |
| enif_mutex_unlock(pData->collMutex); |
| } |
| } |
| |
| /* ------------------------------------------------------------------------- */ |
| |
| static ERL_NIF_TERM |
| collate_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { |
| ERL_NIF_TERM term_a = argv[0]; |
| ERL_NIF_TERM term_b = argv[1]; |
| ERL_NIF_TERM term_has_nocase = argv[2]; |
| |
| ctx_t ctx; |
| int result; |
| priv_data_t* pData; |
| |
| ctx.env = env; |
| ctx.error = 0; |
| ctx.coll = NULL; |
| |
| pData = (priv_data_t*) enif_priv_data(env); |
| |
| result = collate_binary(pData, &ctx, term_a, term_b, term_has_nocase); |
| release_coll(pData, &ctx); |
| |
| return enif_make_int(env, result); |
| } |
| |
| int |
| collate_binary(priv_data_t* pData, ctx_t* ctx, ERL_NIF_TERM term_a, ERL_NIF_TERM term_b, ERL_NIF_TERM term_has_nocase) |
| { |
| ErlNifBinary binA, binB; |
| int has_nocase, response; |
| |
| if(!enif_get_int(ctx->env, term_has_nocase, &has_nocase)) { |
| ctx->error = 1; |
| return 0; |
| } |
| if(!enif_inspect_binary(ctx->env, term_a, &binA)) { |
| ctx->error = 1; |
| return 0; |
| } |
| if(!enif_inspect_binary(ctx->env, term_b, &binB)) { |
| ctx->error = 1; |
| return 0; |
| } |
| |
| switch(has_nocase) { |
| case 0: /* COLLATE */ |
| case 1: /* COLLATE_NO_CASE: */ |
| { |
| UErrorCode status = U_ZERO_ERROR; |
| UCharIterator iterA; |
| UCharIterator iterB; |
| |
| uiter_setUTF8(&iterA, (const char *) binA.data, (uint32_t) binA.size); |
| uiter_setUTF8(&iterB, (const char *) binB.data, (uint32_t) binB.size); |
| |
| /* grab a collator */ |
| reserve_coll(pData, ctx); |
| |
| if (has_nocase == 1) /* switching this collator to case insensitive */ |
| ucol_setAttribute(ctx->coll, UCOL_STRENGTH, UCOL_PRIMARY, &status); |
| |
| /* by default, it will collate case sensitive */ |
| response = ucol_strcollIter(ctx->coll, &iterA, &iterB, &status); |
| |
| if (has_nocase == 1) /* puting back this collator to case sensitive */ |
| ucol_setAttribute(ctx->coll, UCOL_STRENGTH, UCOL_DEFAULT, &status); |
| |
| break; |
| } |
| |
| default: |
| response = -1; |
| } |
| |
| return response; |
| } |
| |
| /* ------------------------------------------------------------------------- */ |
| |
| int |
| on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM info) |
| { |
| UErrorCode status = U_ZERO_ERROR; |
| priv_data_t* pData = (priv_data_t*)enif_alloc(sizeof(priv_data_t)); |
| int i, j; |
| |
| /* Initialize the structure */ |
| pData->collators = NULL; |
| pData->collStackTop = 0; |
| pData->numCollators = 0; |
| pData->collMutex = NULL; |
| |
| if (!enif_get_int(env, info, &(pData->numCollators) )) { |
| enif_free((char*)pData); |
| return 1; |
| } |
| |
| if (pData->numCollators < 1) { |
| enif_free((char*)pData); |
| return 2; |
| } |
| |
| pData->collMutex = enif_mutex_create((char *)"coll_mutex"); |
| |
| if (pData->collMutex == NULL) { |
| enif_free((char*)pData); |
| return 3; |
| } |
| |
| pData->collators = enif_alloc(sizeof(UCollator*) * pData->numCollators); |
| |
| if (pData->collators == NULL) { |
| enif_mutex_destroy(pData->collMutex); |
| enif_free((char*)pData); |
| return 4; |
| } |
| |
| for (i = 0; i < pData->numCollators; i++) { |
| pData->collators[i] = ucol_open("", &status); |
| |
| if (U_FAILURE(status)) { |
| for (j = 0; j < i; j++) { |
| ucol_close(pData->collators[j]); |
| } |
| |
| enif_free(pData->collators); |
| enif_mutex_destroy(pData->collMutex); |
| |
| enif_free((char*)pData); |
| |
| return 5; |
| } |
| } |
| |
| ATOM_TRUE = enif_make_atom(env, "true"); |
| ATOM_FALSE = enif_make_atom(env, "false"); |
| ATOM_NULL = enif_make_atom(env, "null"); |
| |
| *priv_data = pData; |
| |
| return 0; |
| } |
| |
| |
| void |
| on_unload(ErlNifEnv* env, void* priv_data) |
| { |
| priv_data_t* pData = (priv_data_t*)priv_data; |
| if (pData->collators != NULL) { |
| int i; |
| |
| for (i = 0; i < pData->numCollators; i++) { |
| ucol_close(pData->collators[i]); |
| } |
| |
| enif_free(pData->collators); |
| } |
| |
| if (pData->collMutex != NULL) { |
| enif_mutex_destroy(pData->collMutex); |
| } |
| |
| enif_free((char*)pData); |
| } |
| |
| int |
| on_reload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM info) |
| { |
| return 0; |
| } |
| |
| int |
| on_upgrade(ErlNifEnv* env, void** priv_data, void** old_data, ERL_NIF_TERM info) |
| { |
| if (*old_data != NULL) { |
| priv_data_t* pData = (priv_data_t*)old_data; |
| |
| if (pData->collators != NULL) { |
| int i; |
| |
| for (i = 0; i < pData->numCollators; i++) { |
| ucol_close(pData->collators[i]); |
| } |
| |
| enif_free(pData->collators); |
| } |
| |
| if (pData->collMutex != NULL) { |
| enif_mutex_destroy(pData->collMutex); |
| } |
| |
| enif_free((char*)pData); |
| } |
| |
| return on_load(env, priv_data, info); |
| } |
| |
| /* ------------------------------------------------------------------------- */ |
| |
| static ErlNifFunc |
| nif_funcs[] = |
| { |
| {"collate_nif", 3, collate_nif} |
| }; |
| |
| ERL_NIF_INIT(couch_collate, nif_funcs, &on_load, &on_reload, &on_upgrade, &on_unload) |