diff --git a/Makefile b/Makefile index 4b9c868..d7aa1a8 100644 --- a/Makefile +++ b/Makefile @@ -2,5 +2,8 @@ all: ./rebar compile eunit +app: + ./rebar compile + clean: - ./rebar clean \ No newline at end of file + ./rebar clean diff --git a/c_src/emmap.cpp b/c_src/emmap.cpp index 9c6897a..65415b7 100644 --- a/c_src/emmap.cpp +++ b/c_src/emmap.cpp @@ -78,6 +78,7 @@ static ERL_NIF_TERM ATOM_EOF; static ERL_NIF_TERM emmap_open(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM emmap_read(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM emmap_read_line(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM emmap_close(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM emmap_pread(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM emmap_pwrite(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -93,6 +94,7 @@ extern "C" { {"pwrite_nif", 3, emmap_pwrite}, {"position_nif", 3, emmap_position}, {"read_nif", 2, emmap_read}, + {"read_line_nif", 1, emmap_read_line}, }; ERL_NIF_INIT(emmap, nif_funcs, &on_load, NULL, NULL, NULL); @@ -316,9 +318,12 @@ static ERL_NIF_TERM emmap_pread(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg && enif_get_ulong(env, argv[2], &bytes) && pos >= 0 && bytes >= 0 - && (pos + bytes) <= handle->len + && pos <= handle->len ) { + // Adjust bytes to behave like original file:pread/3 + if (pos + bytes > handle->len) bytes = handle->len - pos; + ErlNifBinary bin; if ((handle->prot & PROT_READ) == 0) { @@ -442,6 +447,85 @@ static ERL_NIF_TERM emmap_read(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv } } +static ERL_NIF_TERM emmap_read_line(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + mhandle *handle; + + if (enif_get_resource(env, argv[0], MMAP_RESOURCE, (void**)&handle)) { + + RW_LOCK; + + if (handle->position == handle->len) { + RW_UNLOCK; + return ATOM_EOF; + } + + long start = handle->position; + char *current = ((char*)handle->mem) + handle->position; + long linelen = 0; // Length of line without trailing \n + long no_crlf_len = 0; + bool have_cr = false; + bool got_eof = false; + + // Read buffer until \n or EOF is reached + while (*current != '\n') { + handle->position ++; + current ++; + if (handle->position == handle->len) { + got_eof = true; + break; + } + } + // Step to next byte if EOF is not reached + if (not got_eof) { + handle->position ++; + } + + no_crlf_len = linelen = handle->position - start; + + if (not got_eof) { + // Found LF -- exclude it from line + no_crlf_len --; + // If line length before \n is non-zero check if we have \r before \n + if ((no_crlf_len > 0) && (*(current - 1) == '\r')) { + have_cr = true; + no_crlf_len --; + } + } + + RW_UNLOCK; + + // We must not include CR before LF in result, so use direct only without CR + if ((handle->direct) && (not have_cr)) { + + // Include trailing LF if we have it + ERL_NIF_TERM res = enif_make_resource_binary + (env, handle, (void*) (((char*)handle->mem) + start), linelen); + + return enif_make_tuple2(env, ATOM_OK, res); + + } else { + if (not got_eof) linelen = no_crlf_len + 1; + + ErlNifBinary bin; + // When it is non-direct, we have to allocate the binary + if (!enif_alloc_binary((size_t) linelen, &bin)) { + return make_error_tuple(env, ENOMEM); + } + + memcpy(bin.data, (void*) (((char*)handle->mem) + start), no_crlf_len); + // Set trailing \n if needed + if (not got_eof) *(((char*)bin.data) + no_crlf_len) = '\n'; + + ERL_NIF_TERM res = enif_make_binary(env, &bin); + return enif_make_tuple2(env, ATOM_OK, res); + } + + } else { + return enif_make_badarg(env); + } +} + static ERL_NIF_TERM emmap_position(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { mhandle *handle; @@ -460,6 +544,8 @@ static ERL_NIF_TERM emmap_position(ErlNifEnv* env, int argc, const ERL_NIF_TERM position = handle->position + relpos; } else if (argv[1] == ATOM_EOF) { position = handle->len - relpos; + } else { + position = -1; } if (position < 0L || ((unsigned long)position) > handle->len) { diff --git a/rebar b/rebar index 8c0726a..44053a5 100755 Binary files a/rebar and b/rebar differ diff --git a/rebar.config b/rebar.config index 9c09b72..b734eb0 100644 --- a/rebar.config +++ b/rebar.config @@ -1,7 +1,6 @@ -{port_sources, ["c_src/*.cpp"]}. -{so_name, "emmap_nifs.so"}. +{port_specs, [{"priv/emmap_nifs.so", ["c_src/*.cpp"]}]}. -{port_envs, [ +{port_env, [ %% Make sure to link -lstdc++ {"(linux|solaris|freebsd|netbsd|openbsd|dragonfly|darwin)", "LDFLAGS", "$LDFLAGS -lstdc++"}, diff --git a/src/emmap.erl b/src/emmap.erl index 7351431..98c15f5 100644 --- a/src/emmap.erl +++ b/src/emmap.erl @@ -1,6 +1,6 @@ -module(emmap). --export([open/2, open/4, close/1, pread/3, pwrite/3, read/2, position/2]). +-export([open/2, open/4, close/1, pread/3, pwrite/3, read/2, read_line/1, position/2]). -on_load(init/0). -ifdef(TEST). @@ -86,6 +86,16 @@ read_nif(_,_) -> {ok, <<>>}. +-spec read_line(File::mmap_file()) -> + {ok, binary()} | {error, term()} | eof. + +read_line(#file_descriptor{ module=?MODULE, data=Mem }) -> + read_line_nif(Mem). + +read_line_nif(_) -> + {ok, <<>>}. + + -spec pwrite(File::mmap_file(), Position::pos_integer(), Data::binary()) -> ok | {error, term()}. @@ -101,6 +111,9 @@ pwrite_nif(_,_,_) -> position(#file_descriptor{ module=?MODULE, data=Mem}, At) when is_integer(At) -> position_nif(Mem, bof, At); +position(#file_descriptor{ module=?MODULE, data=Mem}, From) + when From == 'bof'; From == 'cur'; From == 'eof' -> + position_nif(Mem, From, 0); position(#file_descriptor{ module=?MODULE, data=Mem}, {From, Off}) when From == 'bof'; From == 'cur'; From == 'eof' -> position_nif(Mem, From, Off).