aboutsummaryrefslogtreecommitdiffstats
path: root/erts/preloaded/src/prim_buffer.erl
diff options
context:
space:
mode:
Diffstat (limited to 'erts/preloaded/src/prim_buffer.erl')
-rw-r--r--erts/preloaded/src/prim_buffer.erl140
1 files changed, 140 insertions, 0 deletions
diff --git a/erts/preloaded/src/prim_buffer.erl b/erts/preloaded/src/prim_buffer.erl
new file mode 100644
index 0000000000..e0d35a6792
--- /dev/null
+++ b/erts/preloaded/src/prim_buffer.erl
@@ -0,0 +1,140 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017. 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.
+%%
+%% %CopyrightEnd%
+%%
+-module(prim_buffer).
+
+-export([on_load/0]).
+
+%% This is a mutable binary buffer that helps break out buffering logic from
+%% NIFs/drivers, which is often the only thing that prevents the C code from
+%% being reduced to bare system call wrappers.
+%%
+%% All operations in this file are thread-unsafe and risk crashing the emulator
+%% if you're not careful.
+
+-export([new/0, size/1, wipe/1, read/2, read_iovec/2, write/2, skip/2]).
+
+-export([find_byte_index/2]).
+
+-export([try_lock/1, unlock/1]).
+
+-type prim_buffer() :: term().
+
+%% Controls when to copy rather than extract sub-binaries from the buffer,
+%% reducing the risk of small reads keeping a large binary alive.
+-define(COPYING_READ_LIMIT, 512).
+
+%% Reads that fit into heap binaries are always copied since the cost of
+%% peeking binaries that short is largely equivalent to copying.
+-define(ERL_ONHEAP_BIN_LIMIT, 64).
+
+on_load() ->
+ case erlang:load_nif(atom_to_list(?MODULE), 0) of
+ ok -> ok
+ end.
+
+-spec new() -> prim_buffer().
+new() ->
+ erlang:nif_error(undef).
+
+-spec size(Buffer :: prim_buffer()) -> non_neg_integer().
+size(_Buffer) ->
+ erlang:nif_error(undef).
+
+%% Reads data as a binary from the front of the buffer. This will almost always
+%% result in copying so it should be avoided unless you absolutely must have a
+%% binary.
+-spec read(Buffer :: prim_buffer(), Size :: non_neg_integer()) -> binary().
+read(Buffer, Size) when Size =< ?ERL_ONHEAP_BIN_LIMIT ->
+ copying_read(Buffer, Size);
+read(Buffer, Size) when Size > ?ERL_ONHEAP_BIN_LIMIT ->
+ iolist_to_binary(read_iovec(Buffer, Size)).
+
+%% Reads data as an erlang:iovec() binary from the front of the buffer,
+%% avoiding copying if reasonable.
+-spec read_iovec(Buffer, Size) -> IOVec when
+ Buffer :: prim_buffer(),
+ Size :: non_neg_integer(),
+ IOVec :: erlang:iovec().
+read_iovec(Buffer, Size) when Size =< ?ERL_ONHEAP_BIN_LIMIT ->
+ [copying_read(Buffer, Size)];
+read_iovec(Buffer, Size) when Size > ?ERL_ONHEAP_BIN_LIMIT ->
+ Head = peek_head(Buffer),
+ HeadSize = byte_size(Head),
+ if
+ (HeadSize - Size) > ?COPYING_READ_LIMIT, Size =< ?COPYING_READ_LIMIT ->
+ [copying_read(Buffer, Size)];
+ HeadSize > Size ->
+ skip(Buffer, Size),
+ {First, _Rest} = split_binary(Head, Size),
+ [First];
+ HeadSize < Size ->
+ skip(Buffer, HeadSize),
+ [Head | read_iovec(Buffer, Size - HeadSize)];
+ HeadSize =:= Size ->
+ skip(Buffer, Size),
+ [Head]
+ end.
+
+%% Writes an erlang:iovec() to the back of the buffer.
+-spec write(Buffer :: prim_buffer(), IOVec :: erlang:iovec()) -> ok.
+write(_Buffer, _IOVec) ->
+ erlang:nif_error(undef).
+
+%% Removes data from the front of the buffer without reading it.
+-spec skip(Buffer :: prim_buffer(), Size :: non_neg_integer()) -> ok.
+skip(_Buffer, _Size) ->
+ erlang:nif_error(undef).
+
+-spec wipe(Buffer :: prim_buffer()) -> ok.
+wipe(Buffer) ->
+ skip(Buffer, prim_buffer:size(Buffer)).
+
+%% Finds the start-index of the first occurence of Needle, for implementing
+%% read_line and similar.
+-spec find_byte_index(Buffer, Needle) -> Result when
+ Buffer :: prim_buffer(),
+ Needle :: non_neg_integer(),
+ Result :: {ok, non_neg_integer()} |
+ not_found.
+find_byte_index(_Buffer, _Needle) ->
+ erlang:nif_error(undef).
+
+%% Attempts to take a unique lock on the buffer. Failure handling is left to
+%% the user.
+-spec try_lock(Buffer :: prim_buffer()) -> acquired | busy.
+try_lock(_Buffer) ->
+ erlang:nif_error(undef).
+
+-spec unlock(Buffer :: prim_buffer()) -> ok.
+unlock(_Buffer) ->
+ erlang:nif_error(undef).
+
+%% Unexported helper functions:
+
+%% Reads data from the front of the buffer, returning a copy of the data to
+%% avoid holding references.
+-spec copying_read(Buffer :: prim_buffer(), Size :: non_neg_integer()) -> binary().
+copying_read(_Buffer, _Size) ->
+ erlang:nif_error(undef).
+
+%% Returns the binary at the front of the buffer without modifying the buffer.
+-spec peek_head(Buffer :: prim_buffer()) -> binary().
+peek_head(_Buffer) ->
+ erlang:nif_error(undef).