aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/src/ram_file.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/kernel/src/ram_file.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/kernel/src/ram_file.erl')
-rw-r--r--lib/kernel/src/ram_file.erl492
1 files changed, 492 insertions, 0 deletions
diff --git a/lib/kernel/src/ram_file.erl b/lib/kernel/src/ram_file.erl
new file mode 100644
index 0000000000..d996650948
--- /dev/null
+++ b/lib/kernel/src/ram_file.erl
@@ -0,0 +1,492 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(ram_file).
+
+%% Binary RAM file interface
+
+%% Generic file contents operations
+-export([open/2, close/1]).
+-export([write/2, read/2, copy/3,
+ pread/2, pread/3, pwrite/2, pwrite/3,
+ position/2, truncate/1, sync/1]).
+
+%% Specialized file operations
+-export([get_size/1, get_file/1, set_file/2, get_file_close/1]).
+-export([compress/1, uncompress/1, uuencode/1, uudecode/1]).
+
+-export([open_mode/1]). %% used by ftp-file
+
+-export([ipread_s32bu_p32bu/3]).
+
+
+
+%% Includes and defines
+
+-define(RAM_FILE_DRV, "ram_file_drv").
+-define(MAX_I32, (1 bsl 31)).
+-define(G_I32(X), is_integer(X), X >= -?MAX_I32, X < ?MAX_I32).
+
+-include("file.hrl").
+
+
+
+%% --------------------------------------------------------------------------
+%% These operation codes were once identical between efile_drv.c
+%% and ram_file_drv.c, but now these drivers are not depeding on each other.
+%% So, the codes could be changed to more logical values now, but why indeed?
+
+%% Defined "file" functions
+-define(RAM_FILE_OPEN, 1).
+-define(RAM_FILE_READ, 2).
+-define(RAM_FILE_LSEEK, 3).
+-define(RAM_FILE_WRITE, 4).
+-define(RAM_FILE_FSYNC, 9).
+-define(RAM_FILE_TRUNCATE, 14).
+-define(RAM_FILE_PREAD, 17).
+-define(RAM_FILE_PWRITE, 18).
+
+%% Other operations
+-define(RAM_FILE_GET, 30).
+-define(RAM_FILE_SET, 31).
+-define(RAM_FILE_GET_CLOSE, 32).
+-define(RAM_FILE_COMPRESS, 33).
+-define(RAM_FILE_UNCOMPRESS, 34).
+-define(RAM_FILE_UUENCODE, 35).
+-define(RAM_FILE_UUDECODE, 36).
+-define(RAM_FILE_SIZE, 37).
+
+%% Open modes for RAM_FILE_OPEN
+-define(RAM_FILE_MODE_READ, 1).
+-define(RAM_FILE_MODE_WRITE, 2).
+-define(RAM_FILE_MODE_READ_WRITE, 3).
+%% Use this mask to get just the mode bits to be passed to the driver.
+-define(RAM_FILE_MODE_MASK, 3).
+
+%% Seek modes for RAM_FILE_LSEEK
+-define(RAM_FILE_SEEK_SET, 0).
+-define(RAM_FILE_SEEK_CUR, 1).
+-define(RAM_FILE_SEEK_END, 2).
+
+%% Return codes
+-define(RAM_FILE_RESP_OK, 0).
+-define(RAM_FILE_RESP_ERROR, 1).
+-define(RAM_FILE_RESP_DATA, 2).
+-define(RAM_FILE_RESP_NUMBER, 3).
+-define(RAM_FILE_RESP_INFO, 4).
+
+%% --------------------------------------------------------------------------
+%% Generic file contents operations.
+%%
+%% Supposed to be called by applications through module file.
+
+open(Data, ModeList) when is_list(ModeList) ->
+ case open_mode(ModeList) of
+ {Mode,Opts} when is_integer(Mode) ->
+ case ll_open(Data, Mode, Opts) of
+ {ok,Port} ->
+ {ok,#file_descriptor{module=?MODULE, data=Port}};
+ Error ->
+ Error
+ end;
+ {error,_}=Error ->
+ Error
+ end;
+%% Old obsolete mode specification
+open(Data, Mode) ->
+ case mode_list(Mode) of
+ ModeList when is_list(ModeList) ->
+ open(Data, ModeList);
+ Error ->
+ Error
+ end.
+
+close(#file_descriptor{module = ?MODULE, data = Port}) ->
+ ll_close(Port).
+
+read(#file_descriptor{module = ?MODULE, data = Port}, Sz)
+ when is_integer(Sz), Sz >= 0 ->
+ if
+ ?G_I32(Sz) ->
+ Cmd = <<?RAM_FILE_READ:8,Sz:32>>,
+ case call_port(Port, Cmd) of
+ {ok, {0, _Data}} when Sz =/= 0 ->
+ eof;
+ {ok, {_Sz, Data}} ->
+ {ok, Data};
+ {error, enomem} ->
+ %% Garbage collecting here might help if
+ %% the current processes has some old binaries left.
+ erlang:garbage_collect(),
+ case call_port(Port, Cmd) of
+ {ok, {0, _Data}} when Sz =/= 0 ->
+ eof;
+ {ok, {_Sz, Data}} ->
+ {ok, Data};
+ Error ->
+ Error
+ end;
+ Error ->
+ Error
+ end;
+ true ->
+ {error, einval}
+ end.
+
+write(#file_descriptor{module = ?MODULE, data = Port}, Bytes) ->
+ case call_port(Port, [?RAM_FILE_WRITE | Bytes]) of
+ {ok, _Sz} ->
+ ok;
+ Error ->
+ Error
+ end.
+
+
+
+
+copy(#file_descriptor{module = ?MODULE} = Source,
+ #file_descriptor{module = ?MODULE} = Dest,
+ Length)
+ when is_integer(Length), Length >= 0;
+ is_atom(Length) ->
+ %% XXX Should be moved down to the driver for optimization.
+ file:copy_opened(Source, Dest, Length).
+
+
+sync(#file_descriptor{module = ?MODULE, data = Port}) ->
+ call_port(Port, <<?RAM_FILE_FSYNC>>).
+
+truncate(#file_descriptor{module = ?MODULE, data = Port}) ->
+ call_port(Port, <<?RAM_FILE_TRUNCATE>>).
+
+position(#file_descriptor{module = ?MODULE, data = Port}, Pos) ->
+ case lseek_position(Pos) of
+ {ok, Offs, Whence} when ?G_I32(Offs) ->
+ call_port(Port, <<?RAM_FILE_LSEEK:8,Offs:32,Whence:32>>);
+ {ok, _, _} ->
+ {error, einval};
+ Error ->
+ Error
+ end.
+
+
+
+pread(#file_descriptor{module = ?MODULE, data = Port}, L) when is_list(L) ->
+ pread_1(Port, L, []).
+
+pread_1(Port, [], Cs) ->
+ pread_2(Port, lists:reverse(Cs), []);
+pread_1(Port, [{At, Sz} | T], Cs)
+ when is_integer(At), is_integer(Sz), Sz >= 0 ->
+ if
+ ?G_I32(At), ?G_I32(Sz) ->
+ pread_1(Port, T, [{Sz,<<?RAM_FILE_PREAD:8,At:32,Sz:32>>}|Cs]);
+ true ->
+ {error, einval}
+ end;
+pread_1(_, _, _243) ->
+ {error, badarg}.
+
+pread_2(_Port, [], R) ->
+ {ok, lists:reverse(R)};
+pread_2(Port, [{Sz,Command}|Commands], R) ->
+ case call_port(Port, Command) of
+ {ok, {0,_Data}} when Sz =/= 0 ->
+ pread_2(Port, Commands, [eof | R]);
+ {ok, {_Sz,Data}} ->
+ pread_2(Port, Commands, [Data | R]);
+ Error ->
+ Error
+ end.
+
+pread(#file_descriptor{module = ?MODULE, data = Port}, At, Sz)
+ when is_integer(At), is_integer(Sz), Sz >= 0 ->
+ if
+ ?G_I32(At), ?G_I32(Sz) ->
+ case call_port(Port, <<?RAM_FILE_PREAD:8,At:32,Sz:32>>) of
+ {ok, {0,_Data}} when Sz =/= 0 ->
+ eof;
+ {ok, {_Sz,Data}} ->
+ {ok, Data};
+ Error ->
+ Error
+ end;
+ true ->
+ {error, einval}
+ end;
+pread(#file_descriptor{module = ?MODULE}, _, _) ->
+ {error, badarg}.
+
+
+
+pwrite(#file_descriptor{module = ?MODULE, data = Port}, L) when is_list(L) ->
+ pwrite_1(Port, L, 0, []).
+
+pwrite_1(Port, [], _, Cs) ->
+ pwrite_2(Port, lists:reverse(Cs), 0);
+pwrite_1(Port, [{At, Bytes} | T], R, Cs) when is_integer(At) ->
+ if
+ ?G_I32(At), is_binary(Bytes) ->
+ pwrite_1(Port, T, R+1,
+ [<<?RAM_FILE_PWRITE:8,At:32,Bytes/binary>> | Cs]);
+ ?G_I32(At) ->
+ try erlang:iolist_to_binary(Bytes) of
+ Bin ->
+ pwrite_1(Port, T, R+1,
+ [<<?RAM_FILE_PWRITE:8,At:32,Bin/binary>> | Cs])
+ catch
+ error:Reason ->
+ {error, Reason}
+ end;
+ true ->
+ {error, {R, einval}}
+ end;
+pwrite_1(_, _, _, _) ->
+ {error, badarg}.
+
+pwrite_2(_Port, [], _R) ->
+ ok;
+pwrite_2(Port, [Command|Commands], R) ->
+ case call_port(Port, Command) of
+ {ok, _Sz} ->
+ pwrite_2(Port, Commands, R+1);
+ {error, badarg} = Error ->
+ Error;
+ {error, Reason} ->
+ {error, {R, Reason}}
+ end.
+
+pwrite(#file_descriptor{module = ?MODULE, data = Port}, At, Bytes)
+ when is_integer(At) ->
+ if
+ ?G_I32(At) ->
+ case call_port(Port, [<<?RAM_FILE_PWRITE:8,At:32>>|Bytes]) of
+ {ok, _Sz} ->
+ ok;
+ Error ->
+ Error
+ end;
+ true ->
+ {error, einval}
+ end;
+pwrite(#file_descriptor{module = ?MODULE}, _, _) ->
+ {error, badarg}.
+
+
+ipread_s32bu_p32bu(#file_descriptor{module = ?MODULE} = Handle, Pos, MaxSz) ->
+ file:ipread_s32bu_p32bu_int(Handle, Pos, MaxSz).
+
+
+
+%% --------------------------------------------------------------------------
+%% Specialized ram_file API for functions not in file, unique to ram_file.
+%%
+
+
+get_file(#file_descriptor{module = ?MODULE, data = Port}) ->
+ case call_port(Port, [?RAM_FILE_GET]) of
+ {ok, {_Sz, Data}} ->
+ {ok, Data};
+ Error ->
+ Error
+ end;
+get_file(#file_descriptor{}) ->
+ {error, enotsup}.
+
+set_file(#file_descriptor{module = ?MODULE, data = Port}, Data) ->
+ call_port(Port, [?RAM_FILE_SET | Data]);
+set_file(#file_descriptor{}, _) ->
+ {error, enotsup}.
+
+get_file_close(#file_descriptor{module = ?MODULE, data = Port}) ->
+ case call_port(Port, [?RAM_FILE_GET_CLOSE]) of
+ {ok, {_Sz, Data}} ->
+ {ok, Data};
+ Error ->
+ Error
+ end;
+get_file_close(#file_descriptor{}) ->
+ {error, enotsup}.
+
+get_size(#file_descriptor{module = ?MODULE, data = Port}) ->
+ call_port(Port, [?RAM_FILE_SIZE]);
+get_size(#file_descriptor{}) ->
+ {error, enotsup}.
+
+compress(#file_descriptor{module = ?MODULE, data = Port}) ->
+ call_port(Port, [?RAM_FILE_COMPRESS]);
+compress(#file_descriptor{}) ->
+ {error, enotsup}.
+
+uncompress(#file_descriptor{module = ?MODULE, data = Port}) ->
+ call_port(Port, [?RAM_FILE_UNCOMPRESS]);
+uncompress(#file_descriptor{}) ->
+ {error, enotsup}.
+
+
+uuencode(#file_descriptor{module = ?MODULE, data = Port}) ->
+ call_port(Port, [?RAM_FILE_UUENCODE]);
+uuencode(#file_descriptor{}) ->
+ {error, enotsup}.
+
+uudecode(#file_descriptor{module = ?MODULE, data = Port}) ->
+ call_port(Port, [?RAM_FILE_UUDECODE]);
+uudecode(#file_descriptor{}) ->
+ {error, enotsup}.
+
+
+
+%%%-----------------------------------------------------------------
+%%% Functions to communicate with the driver
+
+ll_open(Data, Mode, Opts) ->
+ try erlang:open_port({spawn, ?RAM_FILE_DRV}, Opts) of
+ Port ->
+ case call_port(Port, [<<?RAM_FILE_OPEN:8,Mode:32>>|Data]) of
+ {error, _} = Error ->
+ ll_close(Port),
+ Error;
+ {ok, _} ->
+ {ok, Port}
+ end
+ catch
+ error:Reason ->
+ {error, Reason}
+ end.
+
+call_port(Port, Command) when is_port(Port), is_binary(Command) ->
+ try erlang:port_command(Port, Command) of
+ true ->
+ get_response(Port)
+ catch
+ error:badarg ->
+ {error, einval}; % Since Command is valid, Port must be dead
+ error:Reason ->
+ {error, Reason}
+ end;
+call_port(Port, Command) ->
+ try erlang:iolist_to_binary(Command) of
+ Bin ->
+ call_port(Port, Bin)
+ catch
+ error:Reason ->
+ {error, Reason}
+ end.
+
+get_response(Port) ->
+ receive
+ {Port, {data, [Response|Rest]}} ->
+ translate_response(Response, Rest);
+ {'EXIT', Port, _Reason} ->
+ {error, port_died}
+ end.
+
+ll_close(Port) ->
+ try erlang:port_close(Port) catch error:_ -> ok end,
+ receive %% In case the caller is the owner and traps exits
+ {'EXIT', Port, _} ->
+ ok
+ after 0 ->
+ ok
+ end.
+
+%%%-----------------------------------------------------------------
+%%% Utility functions.
+
+mode_list(read) ->
+ [read];
+mode_list(write) ->
+ [write];
+mode_list(read_write) ->
+ [read, write];
+mode_list({binary, Mode}) when is_atom(Mode) ->
+ [binary | mode_list(Mode)];
+mode_list({character, Mode}) when is_atom(Mode) ->
+ mode_list(Mode);
+mode_list(_) ->
+ {error, badarg}.
+
+
+
+%% Converts a list of mode atoms into an mode word for the driver.
+%% Returns {Mode, Opts} wher Opts is a list of options for
+%% erlang:open_port/2, or {error, einval} upon failure.
+
+open_mode(List) when is_list(List) ->
+ case open_mode(List, {0, []}) of
+ {Mode, Opts} when Mode band
+ (?RAM_FILE_MODE_READ bor ?RAM_FILE_MODE_WRITE)
+ =:= 0 ->
+ {Mode bor ?RAM_FILE_MODE_READ, Opts};
+ Other ->
+ Other
+ end.
+
+open_mode([ram|Rest], {Mode, Opts}) ->
+ open_mode(Rest, {Mode, Opts});
+open_mode([read|Rest], {Mode, Opts}) ->
+ open_mode(Rest, {Mode bor ?RAM_FILE_MODE_READ, Opts});
+open_mode([write|Rest], {Mode, Opts}) ->
+ open_mode(Rest, {Mode bor ?RAM_FILE_MODE_WRITE, Opts});
+open_mode([binary|Rest], {Mode, Opts}) ->
+ open_mode(Rest, {Mode, [binary | Opts]});
+open_mode([], {Mode, Opts}) ->
+ {Mode, Opts};
+open_mode(_, _) ->
+ {error, badarg}.
+
+
+
+%% Converts a position tuple {bof, X} | {cur, X} | {eof, X} into
+%% {ok, Offset, OriginCode} for the driver.
+%% Returns {error, einval} upon failure.
+
+lseek_position(Pos) when is_integer(Pos) ->
+ lseek_position({bof, Pos});
+lseek_position(bof) ->
+ lseek_position({bof, 0});
+lseek_position(cur) ->
+ lseek_position({cur, 0});
+lseek_position(eof) ->
+ lseek_position({eof, 0});
+lseek_position({bof, Offset}) when is_integer(Offset) ->
+ {ok, Offset, ?RAM_FILE_SEEK_SET};
+lseek_position({cur, Offset}) when is_integer(Offset) ->
+ {ok, Offset, ?RAM_FILE_SEEK_CUR};
+lseek_position({eof, Offset}) when is_integer(Offset) ->
+ {ok, Offset, ?RAM_FILE_SEEK_END};
+lseek_position(_) ->
+ {error, badarg}.
+
+
+
+translate_response(?RAM_FILE_RESP_OK, []) ->
+ ok;
+translate_response(?RAM_FILE_RESP_OK, Data) ->
+ {ok, Data};
+translate_response(?RAM_FILE_RESP_ERROR, List) when is_list(List) ->
+ {error, list_to_atom(List)};
+translate_response(?RAM_FILE_RESP_NUMBER, [X1, X2, X3, X4]) ->
+ {ok, i32(X1, X2, X3, X4)};
+translate_response(?RAM_FILE_RESP_DATA, [X1, X2, X3, X4|Data]) ->
+ {ok, {i32(X1, X2, X3, X4), Data}};
+translate_response(X, Data) ->
+ {error, {bad_response_from_port, X, Data}}.
+
+i32(X1,X2,X3,X4) ->
+ (X1 bsl 24) bor (X2 bsl 16) bor (X3 bsl 8) bor X4.