Current section

Files

Jump to
esip c_src esip_codec.c
Raw

c_src/esip_codec.c

/*
* Copyright (C) 2002-2022 ProcessOne, SARL. All Rights Reserved.
*
* 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.
*
*/
#include <erl_nif.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifdef NIF_OLD
#define ENIF_ALLOC(SIZE) enif_alloc(env, SIZE)
#define ENIF_FREE(PTR) enif_free(env, PTR)
#define ENIF_REALLOC(PTR, SIZE) enif_realloc(env, PTR, SIZE)
#define ENIF_ALLOC_BINARY(SIZE, BIN) enif_alloc_binary(env, SIZE, BIN)
#define ENIF_COMPARE(TERM1, TERM2) enif_compare(env, TERM1, TERM2)
#else
#define ENIF_ALLOC(SIZE) enif_alloc(SIZE)
#define ENIF_FREE(PTR) enif_free(PTR)
#define ENIF_REALLOC(PTR, SIZE) enif_realloc(PTR, SIZE)
#define ENIF_ALLOC_BINARY(SIZE, BIN) enif_alloc_binary(SIZE, BIN)
#define ENIF_COMPARE(TERM1, TERM2) enif_compare(TERM1, TERM2)
#endif
#define BUF_LIMIT 64
#define WSP 256
#define OUT 0
#define IN 1
static ERL_NIF_TERM atom_wsp;
static ERL_NIF_TERM atom_true;
static ERL_NIF_TERM atom_false;
struct buf {
int limit;
int len;
unsigned char *b;
};
struct list {
ERL_NIF_TERM term;
struct list *next;
};
static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info)
{
atom_wsp = enif_make_atom(env, "wsp");
atom_true = enif_make_atom(env, "true");
atom_false = enif_make_atom(env, "false");
return 0;
}
static struct buf *init_buf(ErlNifEnv* env)
{
struct buf *rbuf = ENIF_ALLOC(sizeof(struct buf));
rbuf->limit = BUF_LIMIT;
rbuf->len = 0;
rbuf->b = ENIF_ALLOC(rbuf->limit);
return rbuf;
}
static void destroy_buf(ErlNifEnv* env, struct buf *rbuf)
{
if (rbuf) {
if (rbuf->b) {
ENIF_FREE(rbuf->b);
};
ENIF_FREE(rbuf);
};
}
inline void resize_buf(ErlNifEnv* env, struct buf *rbuf, int len_to_add)
{
int new_len = rbuf->len + len_to_add;
if (new_len >= rbuf->limit) {
rbuf->limit = ((new_len / BUF_LIMIT) + 1) * BUF_LIMIT;
rbuf->b = ENIF_REALLOC(rbuf->b, rbuf->limit);
};
}
static void buf_add_char(ErlNifEnv* env, struct buf *rbuf, unsigned char c)
{
resize_buf(env, rbuf, 1);
(rbuf->b)[rbuf->len] = c;
rbuf->len += 1;
}
static void buf_add_str(ErlNifEnv* env, struct buf *rbuf, char *data, int len)
{
resize_buf(env, rbuf, len);
memcpy(rbuf->b + rbuf->len, data, len);
rbuf->len += len;
}
static ERL_NIF_TERM to_lower(ErlNifEnv* env, int argc,
const ERL_NIF_TERM argv[])
{
ErlNifBinary input, output;
int i;
if (argc == 1) {
if (enif_inspect_iolist_as_binary(env, argv[0], &input)) {
if (ENIF_ALLOC_BINARY(input.size, &output)) {
if (input.size > 0) {
for (i=0; i<input.size; i++) {
output.data[i] = tolower(input.data[i]);
};
};
return enif_make_binary(env, &output);
};
};
};
return enif_make_badarg(env);
}
static ERL_NIF_TERM to_upper(ErlNifEnv* env, int argc,
const ERL_NIF_TERM argv[])
{
ErlNifBinary input, output;
int i;
if (argc == 1) {
if (enif_inspect_iolist_as_binary(env, argv[0], &input)) {
if (ENIF_ALLOC_BINARY(input.size, &output)) {
if (input.size > 0) {
for (i=0; i<input.size; i++) {
output.data[i] = toupper(input.data[i]);
};
};
return enif_make_binary(env, &output);
};
};
};
return enif_make_badarg(env);
}
static ERL_NIF_TERM reverse(ErlNifEnv* env, int argc,
const ERL_NIF_TERM argv[])
{
ErlNifBinary input, output;
int i;
if (argc == 1) {
if (enif_inspect_iolist_as_binary(env, argv[0], &input)) {
if (ENIF_ALLOC_BINARY(input.size, &output)) {
if (input.size > 0) {
for (i=0; i<input.size; i++) {
output.data[input.size-i-1] = input.data[i];
};
};
return enif_make_binary(env, &output);
};
};
};
return enif_make_badarg(env);
}
static ERL_NIF_TERM strip_wsp_left(ErlNifEnv* env, int argc,
const ERL_NIF_TERM argv[])
{
ErlNifBinary input, output;
int i = 0;
unsigned char c;
if (argc == 1) {
if (enif_inspect_iolist_as_binary(env, argv[0], &input)) {
if (input.size > 0) {
while (i<input.size) {
c = input.data[i];
if (!isspace(c)) break;
i++;
};
if (ENIF_ALLOC_BINARY(input.size - i, &output)) {
memcpy(output.data, input.data + i, input.size - i);
return enif_make_binary(env, &output);
};
} else {
return enif_make_binary(env, &input);
};
};
};
return enif_make_badarg(env);
}
static ERL_NIF_TERM strip_wsp_right(ErlNifEnv* env, int argc,
const ERL_NIF_TERM argv[])
{
ErlNifBinary input, output;
int i;
unsigned char c;
if (argc == 1) {
if (enif_inspect_iolist_as_binary(env, argv[0], &input)) {
if (input.size > 0) {
i = input.size - 1;
while (i>=0) {
c = input.data[i];
if (!isspace(c)) break;
i--;
};
if (ENIF_ALLOC_BINARY(i+1, &output)) {
memcpy(output.data, input.data, i+1);
return enif_make_binary(env, &output);
};
} else {
return enif_make_binary(env, &input);
};
};
};
return enif_make_badarg(env);
}
static ERL_NIF_TERM strip_wsp(ErlNifEnv* env, int argc,
const ERL_NIF_TERM argv[])
{
ErlNifBinary input, output;
int start = 0, end;
unsigned char c;
if (argc == 1) {
if (enif_inspect_iolist_as_binary(env, argv[0], &input)) {
while (start < input.size) {
c = input.data[start];
if (!isspace(c)) break;
start++;
};
end = input.size - 1;
while (end >= start) {
c = input.data[end];
if (!isspace(c)) break;
end--;
};
if (ENIF_ALLOC_BINARY(end - start + 1, &output)) {
memcpy(output.data, input.data + start, end - start + 1);
return enif_make_binary(env, &output);
};
};
};
return enif_make_badarg(env);
}
static ERL_NIF_TERM str(ErlNifEnv* env, int argc,
const ERL_NIF_TERM argv[])
{
ErlNifBinary input, sep;
unsigned i = 0, j;
if (argc == 2) {
if (enif_inspect_iolist_as_binary(env, argv[0], &input) &&
enif_inspect_iolist_as_binary(env, argv[1], &sep)) {
if (sep.size > 0) {
while (i<input.size && input.size-i >= sep.size) {
j = 0;
while (j<sep.size && input.data[i+j] == sep.data[j]) j++;
if (j == sep.size) return enif_make_int(env, i);
i++;
};
return enif_make_atom(env, "nomatch");
};
return enif_make_int(env, 0);
};
};
return enif_make_badarg(env);
}
static ERL_NIF_TERM strcasecmp_erl(ErlNifEnv* env, int argc,
const ERL_NIF_TERM argv[])
{
ErlNifBinary b1, b2;
if (argc == 2) {
if (enif_inspect_iolist_as_binary(env, argv[0], &b1) &&
enif_inspect_iolist_as_binary(env, argv[1], &b2)) {
if (b1.size == b2.size) {
if (!strncasecmp((char *)b1.data, (char *)b2.data, b1.size))
return atom_true;
else
return atom_false;
} else
return atom_false;
}
}
return enif_make_badarg(env);
}
inline struct list *add_to_acc(ErlNifEnv* env, struct buf *buf,
struct list *acc, unsigned chr)
{
int start = 0, end;
unsigned char c;
ErlNifBinary output;
while (start < buf->len) {
c = buf->b[start];
if (!isspace(c)) break;
start++;
};
end = buf->len - 1;
while (end >= start) {
c = buf->b[end];
if (!isspace(c)) break;
end--;
};
if (end < start && chr == WSP) {
destroy_buf(env, buf);
return acc;
} else {
struct list *new_acc = ENIF_ALLOC(sizeof(struct list));
ENIF_ALLOC_BINARY(end - start + 1, &output);
memcpy(output.data, buf->b + start, end - start + 1);
destroy_buf(env, buf);
new_acc->next = acc;
new_acc->term = enif_make_binary(env, &output);
return new_acc;
};
}
static ERL_NIF_TERM do_split(ErlNifEnv* env, ErlNifBinary *input,
unsigned pos, unsigned chr,
struct buf *buf, struct list *acc,
unsigned state, unsigned prev_chr,
signed iter)
{
struct list *new_acc;
unsigned c;
ERL_NIF_TERM result;
if (pos < input->size && iter != 0) {
c = input->data[pos];
if (state == IN) {
/* We are in quoted string here */
buf_add_char(env, buf, c);
if (c == '"' && prev_chr != '\\') {
/* Leaving quoted string */
return do_split(env, input, pos+1, chr, buf, acc, OUT, c, iter);
} else {
/* Ignore any other characters */
return do_split(env, input, pos+1, chr, buf, acc, IN, c, iter);
};
} else {
if (c == '"') {
/* Entering quoted string */
buf_add_char(env, buf, c);
return do_split(env, input, pos+1, chr, buf, acc, IN, c, iter);
};
if (c == chr || (chr == WSP && (isspace(c)))) {
acc = add_to_acc(env, buf, acc, chr);
return do_split(env, input, pos+1, chr, init_buf(env), acc, OUT, c, iter-1);
};
buf_add_char(env, buf, c);
return do_split(env, input, pos+1, chr, buf, acc, OUT, c, iter);
};
} else {
if (state == IN) {
destroy_buf(env, buf);
} else {
if (iter == 0) {
buf_add_str(env, buf, (char *)input->data + pos, input->size - pos);
};
acc = add_to_acc(env, buf, acc, chr);
};
result = enif_make_list(env, 0, NULL);
while (acc) {
result = enif_make_list_cell(env, acc->term, result);
new_acc = acc->next;
ENIF_FREE(acc);
acc = new_acc;
};
return result;
};
}
static ERL_NIF_TERM split(ErlNifEnv* env, int argc,
const ERL_NIF_TERM argv[])
{
ErlNifBinary input;
unsigned chr;
signed iter;
if (argc == 3) {
if (enif_inspect_iolist_as_binary(env, argv[0], &input) &&
enif_get_int(env, argv[2], &iter)) {
if (enif_get_uint(env, argv[1], &chr)) {
if (chr >= 0 && chr <= 255) {
return do_split(env, &input, 0, chr, init_buf(env), NULL, OUT, 0, iter);
};
} else {
if (!ENIF_COMPARE(argv[1], atom_wsp)) {
chr = WSP;
return do_split(env, &input, 0, chr, init_buf(env), NULL, OUT, 0, iter);
};
};
};
};
return enif_make_badarg(env);
}
static ErlNifFunc nif_funcs[] =
{
{"to_lower", 1, to_lower},
{"to_upper", 1, to_upper},
{"reverse", 1, reverse},
{"strip_wsp", 1, strip_wsp},
{"strip_wsp_left", 1, strip_wsp_left},
{"strip_wsp_right", 1, strip_wsp_right},
{"str", 2, str},
{"strcasecmp", 2, strcasecmp_erl},
{"split", 3, split}
};
ERL_NIF_INIT(esip_codec, nif_funcs, load, NULL, NULL, NULL)