From 6cb62e44eba1db8d1917ebb0db84298e91582c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 8 Sep 2017 15:01:01 +0200 Subject: Add a mutable binary buffer type (prim_buffer) --- erts/preloaded/src/Makefile | 3 +- erts/preloaded/src/erts.app.src | 1 + erts/preloaded/src/prim_buffer.erl | 140 +++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 erts/preloaded/src/prim_buffer.erl (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index edb9f35258..bb8d272793 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -46,7 +46,8 @@ PRE_LOADED_ERL_MODULES = \ erts_internal \ erl_tracer \ erts_literal_area_collector \ - erts_dirty_process_code_checker + erts_dirty_process_code_checker \ + prim_buffer PRE_LOADED_BEAM_MODULES = \ prim_eval diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index e2ab8686d2..338f168158 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -28,6 +28,7 @@ init, otp_ring0, erts_code_purger, + prim_buffer, prim_eval, prim_file, prim_inet, 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). -- cgit v1.2.3