diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/kernel/src/ram_file.erl | |
download | otp-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.erl | 492 |
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. |