├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── rebar.config ├── rebar.config.script ├── rebar.lock └── src ├── antidotec_counter.erl ├── antidotec_datatype.erl ├── antidotec_pb.app.src ├── antidotec_pb.erl ├── antidotec_pb_management.erl ├── antidotec_pb_socket.erl ├── antidotec_reg.erl └── antidotec_set.erl /.gitignore: -------------------------------------------------------------------------------- 1 | deps/ 2 | ebin/ 3 | _build/ 4 | *.xml 5 | logs/ 6 | *.beam 7 | *.o 8 | erl_crash.dump 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | otp_release: 3 | - 22.0 4 | install: 5 | - make 6 | script: 7 | - make test 8 | - make dialyzer 9 | - make lint 10 | - rebar3 as test coveralls send 11 | sudo: required 12 | dist: trusty 13 | notifications: 14 | email: bieniusa@gmail.com 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REBAR := rebar3 2 | 3 | .PHONY: rel deps test 4 | 5 | all: compile 6 | 7 | compile: 8 | @${REBAR} compile 9 | 10 | clean: 11 | @${REBAR} clean 12 | 13 | distclean: clean 14 | $(REBAR) clean --all 15 | 16 | test: 17 | mkdir -p logs 18 | ${REBAR} eunit 19 | ${REBAR} cover --verbose 20 | 21 | docs: 22 | ${REBAR} doc 23 | 24 | xref: compile 25 | ${REBAR} xref 26 | 27 | dialyzer: 28 | ${REBAR} dialyzer 29 | 30 | # style checks 31 | lint: 32 | ${REBAR} as lint lint 33 | 34 | check: distclean test dialyzer lint 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Archive of Antidote Erlang Client Library 2 | 3 | Current version is included at https://github.com/AntidoteDB/antidote 4 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {lib_dirs,["deps"]}. 2 | {sub_dirs, ["rel"]}. 3 | 4 | {deps, [ 5 | antidote_pb_codec 6 | ]}. 7 | 8 | {erl_opts, [debug_info, warnings_as_errors, {parse_transform}]}. 9 | {cover_enabled, true}. 10 | {plugins, [coveralls]}. 11 | {cover_export_enabled, true}. 12 | {coveralls_coverdata, "_build/test/cover/eunit.coverdata"}. 13 | {coveralls_service_name, "travis-ci"}. 14 | 15 | {eunit_opts, [verbose, {report, {eunit_surefire, [{dir,"."}]}}]}. 16 | {profiles,[ 17 | {lint, [ 18 | {plugins, [{rebar3_lint, {git, "https://github.com/project-fifo/rebar3_lint.git", {tag, "v0.1.10"}}}]} 19 | ]}, 20 | {test, [ 21 | {erl_opts, [warnings_as_errors, debug_info, no_inline_list_funcs]}, 22 | {plugins, [{coveralls, {git, "https://github.com/markusn/coveralls-erl", {branch, "master"}}}]}]} 23 | ]}. 24 | 25 | {elvis, 26 | [#{dirs => ["src"], 27 | filter => "*.erl", 28 | rules => [ 29 | {elvis_style, no_tabs}, 30 | {elvis_style, no_trailing_whitespace}, 31 | {elvis_style, macro_names, #{ignore => []}}, 32 | {elvis_style, operator_spaces, #{rules => [{right, ","}, 33 | {right, "--"}, 34 | {left, "--"}, 35 | {right, "++"}, 36 | {left, "++"}]}}, 37 | {elvis_style, god_modules, 38 | #{limit => 25, 39 | ignore => []}}, 40 | {elvis_style, used_ignored_variable}, 41 | {elvis_style, no_behavior_info}, 42 | { 43 | elvis_style, 44 | module_naming_convention, 45 | #{regex => "^[a-z]([a-z0-9]*_?)*(_SUITE)?$", 46 | ignore => []} 47 | }, 48 | { 49 | elvis_style, 50 | function_naming_convention, 51 | #{regex => "^([a-z][a-z0-9]*_?)*$"} 52 | }, 53 | {elvis_style, state_record_and_type}, 54 | {elvis_style, no_spec_with_records} 55 | ] 56 | }, 57 | #{dirs => ["."], 58 | filter => "Makefile", 59 | rules => [{elvis_project, no_deps_master_erlang_mk, #{ignore => []}}, 60 | {elvis_project, protocol_for_deps_erlang_mk, #{ignore => []}}] 61 | }, 62 | #{dirs => ["."], 63 | filter => "rebar.config", 64 | rules => [{elvis_project, no_deps_master_rebar, #{ignore => []}}, 65 | {elvis_project, protocol_for_deps_rebar, #{ignore => []}}] 66 | } 67 | ] 68 | }. 69 | -------------------------------------------------------------------------------- /rebar.config.script: -------------------------------------------------------------------------------- 1 | case os:getenv("TRAVIS") of 2 | "true" -> 3 | lists:keystore(coveralls_service_job_id, 1, CONFIG, {coveralls_service_job_id, os:getenv("TRAVIS_JOB_ID")}); 4 | _ -> 5 | CONFIG 6 | end. -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | {"1.1.0", 2 | [{<<"antidote_pb_codec">>,{pkg,<<"antidote_pb_codec">>,<<"0.1.2">>},0}]}. 3 | [ 4 | {pkg_hash,[ 5 | {<<"antidote_pb_codec">>, <<"ECF51F08EE1FEE0D6E82D1B4AE68811A89660A3D65DB90694A84275683AEF106">>}]} 6 | ]. 7 | -------------------------------------------------------------------------------- /src/antidotec_counter.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2014 SyncFree Consortium. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | -module(antidotec_counter). 21 | 22 | -include_lib("antidote_pb_codec/include/antidote_pb.hrl"). 23 | -ifdef(TEST). 24 | -include_lib("eunit/include/eunit.hrl"). 25 | -endif. 26 | 27 | 28 | -behaviour(antidotec_datatype). 29 | 30 | -export([new/0, new/1, 31 | value/1, 32 | to_ops/2, 33 | is_type/1, 34 | dirty_value/1, 35 | type/0 36 | ]). 37 | 38 | -export([increment/1, 39 | increment/2, 40 | decrement/1, 41 | decrement/2 42 | ]). 43 | 44 | -record(counter, { 45 | value :: integer(), 46 | increment :: integer() 47 | }). 48 | 49 | -export_type([antidotec_counter/0]). 50 | -opaque antidotec_counter() :: #counter{}. 51 | 52 | -spec new() -> antidotec_counter(). 53 | new() -> 54 | #counter{value = 0, increment = 0}. 55 | 56 | -spec new(integer()) -> antidotec_counter(). 57 | new(Value) -> 58 | #counter{value = Value, increment = 0}. 59 | 60 | -spec value(antidotec_counter()) -> integer(). 61 | value(#counter{value = Value}) -> 62 | Value. 63 | 64 | -spec dirty_value(antidotec_counter()) -> integer(). 65 | dirty_value(#counter{value = Value, increment = Increment}) -> 66 | Value + Increment. 67 | 68 | %% @doc Increments the counter with 1 unit. 69 | -spec increment(antidotec_counter()) -> antidotec_counter(). 70 | increment(Counter) -> 71 | increment(1, Counter). 72 | 73 | %% @doc Increments the counter with Amount units. 74 | -spec increment(integer(), antidotec_counter()) -> antidotec_counter(). 75 | increment(Amount, #counter{increment = Increment} = Counter) 76 | when is_integer(Amount) -> 77 | Counter#counter{increment = Increment + Amount}. 78 | 79 | %% @doc Decrements the counter by 1. 80 | -spec decrement(antidotec_counter()) -> antidotec_counter(). 81 | decrement(Counter) -> 82 | increment(-1, Counter). 83 | 84 | %% @doc Decrements the counter by the passed amount. 85 | -spec decrement(integer(), antidotec_counter()) -> antidotec_counter(). 86 | decrement(Amount, #counter{increment = Value} = Counter) 87 | when is_integer(Amount) -> 88 | Counter#counter{increment = Value - Amount}. 89 | 90 | -spec is_type(term()) -> boolean(). 91 | is_type(T) -> 92 | is_record(T, counter). 93 | 94 | -spec type() -> counter. 95 | type() -> 96 | counter. 97 | 98 | to_ops(_BoundObject, #counter{increment = 0}) -> []; 99 | 100 | to_ops(BoundObject, #counter{increment = Amount}) when Amount < 0 -> 101 | [{BoundObject, decrement, -Amount}]; 102 | 103 | to_ops(BoundObject, #counter{increment = Amount}) -> 104 | [{BoundObject, increment, Amount}]. 105 | 106 | 107 | %% =================================================================== 108 | %% EUnit tests 109 | %% =================================================================== 110 | -ifdef(TEST). 111 | 112 | -define(COMPARE_VALUES(X, Y), ?assertEqual(X, dirty_value(Y))). 113 | 114 | incr_op_test() -> 115 | New = new(), 116 | ?COMPARE_VALUES(0, New), 117 | ?COMPARE_VALUES(1, increment(New)), 118 | ?COMPARE_VALUES(7, increment(7, New)), 119 | ?COMPARE_VALUES(-3, increment(-3, New)), 120 | 121 | ?COMPARE_VALUES(-1, decrement(New)), 122 | ?COMPARE_VALUES(-5, decrement(5, New)). 123 | -endif. -------------------------------------------------------------------------------- /src/antidotec_datatype.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2014 SyncFree Consortium. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | -module(antidotec_datatype). 21 | 22 | -ifdef(EQC). 23 | -include_lib("eqc/include/eqc.hrl"). 24 | -include_lib("eunit/include/eunit.hrl"). 25 | -define(QC_OUT(P), eqc:on_output(fun(Fmt, Args) -> io:format(user, Fmt, Args) end, P)). 26 | -compile(export_all). 27 | -endif. 28 | 29 | 30 | -define(MODULES, [antidotec_counter, antidotec_set, antidotec_reg]). 31 | 32 | -export([module_for_type/1, 33 | module_for_term/1]). 34 | 35 | -export_type([datatype/0, update/0]). 36 | 37 | -type maybe(T) :: T | undefined. 38 | -type datatype() :: term(). 39 | -type typename() :: atom(). 40 | -type update() :: [term()]. 41 | 42 | %% @doc Constructs a new container for the type with the specified 43 | %% value and key. This should only be used internally by the client code. 44 | -callback new(Value::term()) -> datatype(). 45 | 46 | %% @doc Returns the original, unmodified value of the object. This does 47 | %% not include the execution of any locally-queued operations. 48 | -callback value(datatype()) -> term(). 49 | 50 | %% @doc Returns the local value of the object, with the local operations applied. 51 | -callback dirty_value(datatype()) -> term(). 52 | 53 | %% @doc Extracts the list of operations to be append to the object's log. 54 | %% 'undefined' should be returned if the type is unmodified. 55 | -callback to_ops(term(), datatype()) -> update(). 56 | 57 | %% @doc Determines whether the given term is the type managed by the 58 | %% container module. 59 | -callback is_type(datatype()) -> boolean(). 60 | 61 | %% @doc Determines the symbolic name of the container's type, e.g. 62 | %% antidote_set, antidote_map, antidote_counter. 63 | -callback type() -> typename(). 64 | 65 | %% @doc Returns the module name for the container of the given CRDT data-type. 66 | -spec module_for_type(set | counter | reg) -> 67 | antidotec_counter | antidotec_set | antidotec_reg. 68 | module_for_type(set) -> antidotec_set; 69 | module_for_type(counter) -> antidotec_counter; 70 | module_for_type(reg) -> antidotec_reg. 71 | 72 | %% @doc Returns the container module name for the given term. 73 | %% Returns undefined if the module is not known. 74 | -spec module_for_term(datatype()) -> maybe(module()). 75 | module_for_term(T) -> 76 | lists:foldl(fun(Mod, undefined) -> 77 | case Mod:is_type(T) of 78 | true -> Mod; 79 | false -> undefined 80 | end; 81 | (_, Mod) -> 82 | Mod 83 | end, undefined, ?MODULES). 84 | -------------------------------------------------------------------------------- /src/antidotec_pb.app.src: -------------------------------------------------------------------------------- 1 | {application, antidotec_pb, 2 | [ 3 | {description, "Antidote Erlang Client Library"}, 4 | {vsn, "0.2.9"}, 5 | {registered, []}, 6 | {applications, [ 7 | kernel, 8 | stdlib, 9 | antidote_pb_codec 10 | ]}, 11 | {licenses, ["Apache 2.0"]}, 12 | {links,[{"Github","https://github.com/AntidoteDB/antidote-erlang-client"}]}, 13 | {env, []} 14 | ]}. 15 | -------------------------------------------------------------------------------- /src/antidotec_pb.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2014 SyncFree Consortium. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | -module(antidotec_pb). 21 | 22 | -include_lib("antidote_pb_codec/include/antidote_pb.hrl"). 23 | 24 | 25 | -export([start_transaction/2, 26 | start_transaction/3, 27 | abort_transaction/2, 28 | commit_transaction/2, 29 | update_objects/3, 30 | read_objects/3, 31 | read_values/3]). 32 | 33 | -define(TIMEOUT, 10000). 34 | 35 | -spec start_transaction(Pid::pid(), TimeStamp::binary() | ignore) 36 | -> {ok, {interactive, term()} | {static, {term(), term()}}} | {error, term()}. 37 | start_transaction(Pid, TimeStamp) -> 38 | start_transaction(Pid, TimeStamp, []). 39 | 40 | -spec start_transaction(Pid::pid(), TimeStamp::binary() | ignore, TxnProperties::term()) 41 | -> {ok, {interactive, binary()} | {static, {binary(), term()}}} | {error, term()}. 42 | start_transaction(Pid, TimeStamp, TxnProperties) -> 43 | EncTimestamp = case TimeStamp of 44 | ignore -> term_to_binary(ignore); 45 | Binary -> Binary 46 | end, 47 | case is_static(TxnProperties) of 48 | true -> 49 | {ok, {static, {EncTimestamp, TxnProperties}}}; 50 | false -> 51 | EncMsg = antidote_pb_codec:encode_request({start_transaction, EncTimestamp, TxnProperties}), 52 | Result = antidotec_pb_socket:call_infinity(Pid, {req, EncMsg, ?TIMEOUT}), 53 | case Result of 54 | {error, timeout} -> 55 | {error, timeout}; 56 | _ -> 57 | case antidote_pb_codec:decode_response(Result) of 58 | {start_transaction_response, {ok, TxId}} -> 59 | {ok, {interactive, TxId}}; 60 | {start_transaction_response, {error, Reason}} -> 61 | {error, Reason}; 62 | {error_response, Reason} -> 63 | {error, Reason}; 64 | Other -> 65 | {error, Other} 66 | end 67 | end 68 | end. 69 | 70 | -spec abort_transaction(Pid::pid(), {interactive, TxId::binary()}) -> ok | {error, term()}. 71 | abort_transaction(Pid, {interactive, TxId}) -> 72 | EncMsg = antidote_pb_codec:encode_request({abort_transaction, TxId}), 73 | Result = antidotec_pb_socket:call_infinity(Pid, {req, EncMsg, ?TIMEOUT}), 74 | case Result of 75 | {error, timeout} -> {error, timeout}; 76 | _ -> 77 | case antidote_pb_codec:decode_response(Result) of 78 | {operation_response, ok} -> ok; 79 | {operation_response, {error, Reason}} -> {error, Reason}; 80 | {error_response, Reason} -> {error, Reason}; 81 | Other -> {error, Other} 82 | end 83 | end. 84 | 85 | -spec commit_transaction(Pid::pid(), TxId::{interactive, binary()} | {static, binary()}) -> 86 | {ok, binary()} | {error, term()}. 87 | commit_transaction(Pid, {interactive, TxId}) -> 88 | EncMsg = antidote_pb_codec:encode_request({commit_transaction, TxId}), 89 | Result = antidotec_pb_socket:call_infinity(Pid, {req, EncMsg, ?TIMEOUT}), 90 | case Result of 91 | {error, timeout} -> {error, timeout}; 92 | _ -> 93 | case antidote_pb_codec:decode_response(Result) of 94 | {commit_transaction_response, {ok, CommitTimeStamp}} -> {ok, CommitTimeStamp}; 95 | {commit_transaction_response, {error, Reason}} -> {error, Reason}; 96 | {error_response, Reason} -> {error, Reason}; 97 | Other -> {error, Other} 98 | end 99 | end; 100 | commit_transaction(Pid, {static, _TxId}) -> 101 | case antidotec_pb_socket:get_last_commit_time(Pid) of 102 | {ok, CommitTime} -> 103 | {ok, CommitTime} 104 | end. 105 | 106 | -spec update_objects(Pid::pid(), Updates::[{term(), term(), term()}], {interactive | static, TxId::binary()}) -> ok | {error, term()}. 107 | update_objects(Pid, Updates, {interactive, TxId}) -> 108 | EncMsg = antidote_pb_codec: encode_request({update_objects, Updates, TxId}), 109 | Result = antidotec_pb_socket: call_infinity(Pid, {req, EncMsg, ?TIMEOUT}), 110 | case Result of 111 | {error, timeout} -> {error, timeout}; 112 | _ -> 113 | case antidote_pb_codec: decode_response(Result) of 114 | {operation_response, ok} -> ok; 115 | {operation_response, {error, Reason}} -> {error, Reason}; 116 | {error_response, Reason} -> {error, Reason}; 117 | Other -> {error, Other} 118 | end 119 | end; 120 | 121 | update_objects(Pid, Updates, {static, TxId}) -> 122 | {Clock, Properties} = TxId, 123 | EncMsg = antidote_pb_codec:encode_request({static_update_objects, Clock, Properties, Updates}), 124 | Result = antidotec_pb_socket:call_infinity(Pid, {req, EncMsg, ?TIMEOUT}), 125 | case Result of 126 | {error, timeout} -> {error, timeout}; 127 | _ -> 128 | case antidote_pb_codec:decode_response(Result) of 129 | {commit_transaction_response, {ok, CommitTimeStamp}} -> 130 | antidotec_pb_socket:store_commit_time(Pid, CommitTimeStamp), 131 | ok; 132 | {commit_transaction_response, {error, Reason}} -> 133 | {error, Reason}; 134 | {error_response, Reason} -> {error, Reason}; 135 | Other -> {error, Other} 136 | end 137 | end. 138 | 139 | -spec read_objects(Pid::pid(), Objects::[term()], {interactive | static, TxId::binary()}) -> {ok, [term()]} | {error, term()}. 140 | read_objects(Pid, Objects, Transaction) -> 141 | case read_values(Pid, Objects, Transaction) of 142 | {ok, Values} -> 143 | ResObjects = lists:map( 144 | fun({Type, Val}) -> 145 | Mod = antidotec_datatype:module_for_type(Type), 146 | Mod:new(Val) 147 | end, Values), 148 | {ok, ResObjects}; 149 | Other -> 150 | Other 151 | end. 152 | 153 | -spec read_values(Pid::pid(), Objects::[term()], {interactive | static, TxId::binary()}) -> {ok, [term()]} | {error, term()}. 154 | read_values(Pid, Objects, {interactive, TxId}) -> 155 | EncMsg = antidote_pb_codec:encode_request({read_objects, Objects, TxId}), 156 | Result = antidotec_pb_socket:call_infinity(Pid, {req, EncMsg, ?TIMEOUT}), 157 | case Result of 158 | {error, timeout} -> {error, timeout}; 159 | _ -> 160 | case antidote_pb_codec:decode_response(Result) of 161 | {read_objects_response, {ok, Values}} -> 162 | {ok, Values}; 163 | {read_objects_response, {error, Reason}} -> 164 | {error, Reason}; 165 | {error_response, Reason} -> {error, Reason}; 166 | Other -> {error, Other} 167 | end 168 | end; 169 | read_values(Pid, Objects, {static, TxId}) -> 170 | {Clock, Properties} = TxId, 171 | EncMsg = antidote_pb_codec:encode_request({static_read_objects, Clock, Properties, Objects}), 172 | Result = antidotec_pb_socket:call_infinity(Pid, {req, EncMsg, ?TIMEOUT}), 173 | case Result of 174 | {error, timeout} -> {error, timeout}; 175 | _ -> 176 | case antidote_pb_codec:decode_response(Result) of 177 | {static_read_objects_response, {Values, CommitTimeStamp}} -> 178 | antidotec_pb_socket:store_commit_time(Pid, CommitTimeStamp), 179 | {ok, Values}; 180 | {error_response, Reason} -> {error, Reason} 181 | end 182 | end. 183 | 184 | 185 | is_static(TxnProperties) -> 186 | case TxnProperties of 187 | [{static, true}] -> 188 | true; 189 | _ -> false 190 | end. 191 | -------------------------------------------------------------------------------- /src/antidotec_pb_management.erl: -------------------------------------------------------------------------------- 1 | 2 | %% ------------------------------------------------------------------- 3 | %% 4 | %% Copyright (c) 2014 SyncFree Consortium. All Rights Reserved. 5 | %% 6 | %% This file is provided to you under the Apache License, 7 | %% Version 2.0 (the "License"); you may not use this file 8 | %% except in compliance with the License. You may obtain 9 | %% a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, 14 | %% software distributed under the License is distributed on an 15 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | %% KIND, either express or implied. See the License for the 17 | %% specific language governing permissions and limitations 18 | %% under the License. 19 | %% 20 | %% ------------------------------------------------------------------- 21 | -module(antidotec_pb_management). 22 | 23 | -include_lib("antidote_pb_codec/include/antidote_pb.hrl"). 24 | 25 | 26 | -export([create_dc/2, 27 | get_connection_descriptor/1, 28 | connect_to_dcs/2]). 29 | 30 | -define(TIMEOUT, 10000). 31 | 32 | -spec create_dc(pid(), [node()]) -> ok | {error, antidote_pb_codec:error_code() | timeout}. 33 | create_dc(Pid, Nodes) -> 34 | EncMsg = antidote_pb_codec:encode_request({create_dc, Nodes}), 35 | RawResponse = antidotec_pb_socket:call_infinity(Pid, {req, EncMsg, infinity}), 36 | case RawResponse of 37 | {error, timeout} -> {error, timeout}; 38 | Result -> 39 | {create_dc_response, Response} = antidote_pb_codec:decode_response(Result), 40 | Response 41 | end. 42 | 43 | -spec get_connection_descriptor(pid()) -> {ok, binary()} | {error, antidote_pb_codec:error_code() | timeout}. 44 | get_connection_descriptor(Pid) -> 45 | EncMsg = antidote_pb_codec:encode_request(get_connection_descriptor), 46 | RawResponse = antidotec_pb_socket:call_infinity(Pid, {req, EncMsg, ?TIMEOUT}), 47 | case RawResponse of 48 | {error, timeout} -> {error, timeout}; 49 | Result -> 50 | {get_connection_descriptor_response, Response} = antidote_pb_codec:decode_response(Result), 51 | Response 52 | end. 53 | 54 | -spec connect_to_dcs(pid(), [binary()]) -> ok | {error, antidote_pb_codec:error_code()} | {error, timeout}. 55 | connect_to_dcs(Pid, Descriptors) -> 56 | EncMsg = antidote_pb_codec:encode_request({connect_to_dcs, Descriptors}), 57 | RawResponse = antidotec_pb_socket:call_infinity(Pid, {req, EncMsg, ?TIMEOUT}), 58 | case RawResponse of 59 | {error, timeout} -> {error, timeout}; 60 | Result -> 61 | {connect_to_dcs_response, Response} = antidote_pb_codec:decode_response(Result), 62 | Response 63 | end. 64 | -------------------------------------------------------------------------------- /src/antidotec_pb_socket.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2014 SyncFree Consortium. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | -module(antidotec_pb_socket). 21 | 22 | -include_lib("antidote_pb_codec/include/antidote_pb.hrl"). 23 | 24 | -behaviour(gen_server). 25 | 26 | -define(FIRST_RECONNECT_INTERVAL, 100). 27 | -define(TIMEOUT, 5000). 28 | 29 | %% The TCP/IP host name or address of the Riak node 30 | -type address() :: string() | atom() | inet:ip_address(). 31 | %% The TCP port number of the Riak node's Protocol Buffers interface 32 | -type portnum() :: non_neg_integer(). 33 | 34 | -record(request, {ref :: reference(), from, timeout :: timeout(), 35 | tref :: reference() | undefined }). 36 | 37 | -record(state, { 38 | address :: address(), % address to connect to 39 | port :: portnum(), % port to connect to 40 | sock :: port() | undefined, % gen_tcp socket 41 | active :: #request{} | undefined, % active request 42 | connect_timeout = infinity :: timeout(), % timeout of TCP connection 43 | keepalive = false :: boolean(), % if true, enabled TCP keepalive for the socket 44 | last_commit_time :: term() % temporarily store for static transactions 45 | }). 46 | 47 | 48 | -export([start_link/2, 49 | start_link/3, 50 | start/2, 51 | start/3, 52 | stop/1, 53 | handle_call/3, 54 | handle_info/2, 55 | handle_cast/2, 56 | init/1, 57 | code_change/3, 58 | terminate/2]). 59 | 60 | -export([call_infinity/2, 61 | store_commit_time/2, 62 | get_last_commit_time/1 63 | ]). 64 | 65 | %% @private 66 | init([Address, Port, _Options]) -> 67 | State = #state{address = Address, port = Port, active = undefined, sock = undefined}, 68 | case connect(State) of 69 | {error, Reason} -> 70 | {stop, {tcp, Reason}}; 71 | {ok, State2} -> 72 | {ok, State2} 73 | end. 74 | 75 | %% @doc Create a linked process to talk with the riak server on Address:Port 76 | %% Client id will be assigned by the server. 77 | -spec start_link(address(), portnum()) -> {ok, pid()} | {error, term()}. 78 | start_link(Address, Port) -> 79 | start_link(Address, Port, []). 80 | 81 | %% @doc Create a linked process to talk with the riak server on Address:Port with Options. 82 | %% Client id will be assigned by the server. 83 | start_link(Address, Port, Options) when is_list(Options) -> 84 | gen_server:start_link(?MODULE, [Address, Port, Options], []). 85 | 86 | %% @doc Create a process to talk with the riak server on Address:Port. 87 | %% Client id will be assigned by the server. 88 | start(Address, Port) -> 89 | start(Address, Port, []). 90 | 91 | %% @doc Create a process to talk with the riak server on Address:Port with Options. 92 | start(Address, Port, Options) when is_list(Options) -> 93 | gen_server:start(?MODULE, [Address, Port, Options], []). 94 | 95 | %% @doc Disconnect the socket and stop the process. 96 | stop(Pid) -> 97 | call_infinity(Pid, stop). 98 | 99 | %% @private Like `gen_server:call/3', but with the timeout hardcoded 100 | %% to `infinity'. 101 | call_infinity(Pid, Msg) -> 102 | gen_server:call(Pid, Msg, infinity). 103 | 104 | store_commit_time(Pid, TimeStamp) -> 105 | gen_server:call(Pid, {store_commit_time, TimeStamp}, infinity). 106 | 107 | get_last_commit_time(Pid) -> 108 | gen_server:call(Pid, get_commit_time, infinity). 109 | 110 | %% @private 111 | handle_call({req, Msg, Timeout}, From, State) -> 112 | Ref = make_ref(), 113 | Req = #request{ref = Ref, from = From, timeout = Timeout, 114 | tref = create_req_timer(Timeout, Ref)}, 115 | NewState = case gen_tcp:send(State#state.sock, Msg) of 116 | ok -> 117 | maybe_reply({noreply, State#state{active = Req}}); 118 | {error, Reason} -> 119 | logger:warning("Socket error while sending request: ~p.", [Reason]), 120 | gen_tcp:close(State#state.sock) 121 | end, 122 | {noreply, NewState}; 123 | 124 | handle_call({store_commit_time, TimeStamp}, _From, State) -> 125 | {reply, ok, State#state{last_commit_time = TimeStamp}}; 126 | 127 | handle_call(get_commit_time, _From, State=#state{last_commit_time = TimeStamp}) -> 128 | {reply, {ok, TimeStamp}, State#state{last_commit_time = ignore}}; 129 | 130 | handle_call(stop, _From, State) -> 131 | _ = disconnect(State), 132 | {stop, normal, ok, State}. 133 | 134 | %% @private 135 | %% @todo handle timeout 136 | handle_info({_Proto, Sock, Data}, State=#state{active = (Active = #request{})}) -> 137 | cancel_req_timer(Active#request.tref), 138 | _ = send_caller(Data, Active), 139 | NewState = State#state{active = undefined}, 140 | ok = inet:setopts(Sock, [{active, once}]), 141 | {noreply, NewState}; 142 | 143 | handle_info({req_timeout, _Ref}, State=#state{active = Active}) -> 144 | cancel_req_timer(Active#request.tref), 145 | _ = send_caller({error, timeout}, Active), 146 | {noreply, State#state{ active = undefined }}; 147 | 148 | handle_info({tcp_closed, _Socket}, State) -> 149 | disconnect(State); 150 | 151 | handle_info({_Proto, Sock, _Data}, State) -> 152 | ok = inet:setopts(Sock, [{active, once}]), 153 | {noreply, State}. 154 | 155 | %% @private 156 | handle_cast(_Msg, State) -> 157 | {noreply, State}. 158 | 159 | %% @private 160 | terminate(_Reason, _State) -> ok. 161 | 162 | %% @private 163 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 164 | 165 | %% @private 166 | %% Connect the socket if disconnected 167 | connect(State) when State#state.sock =:= undefined -> 168 | #state{address = Address, port = Port} = State, 169 | case gen_tcp:connect(Address, Port, 170 | [binary, {active, once}, {packet, 4}, 171 | {keepalive, State#state.keepalive}], 172 | State#state.connect_timeout) of 173 | {ok, Sock} -> 174 | {ok, State#state{sock = Sock}}; 175 | {error, Reason} -> 176 | {error, Reason} 177 | end. 178 | 179 | 180 | disconnect(State) -> 181 | %% Tell any pending requests we've disconnected 182 | _ = case State#state.active of 183 | undefined -> 184 | ok; 185 | Request -> 186 | send_caller({error, disconnected}, Request) 187 | end, 188 | 189 | %% Make sure the connection is really closed 190 | case State#state.sock of 191 | undefined -> 192 | ok; 193 | Sock -> 194 | gen_tcp:close(Sock) 195 | end, 196 | NewState = State#state{sock = undefined, active = undefined}, 197 | {stop, disconnected, NewState}. 198 | 199 | %% @private 200 | %% Create a request timer if desired, otherwise return undefined. 201 | create_req_timer(infinity, _Ref) -> 202 | undefined; 203 | create_req_timer(undefined, _Ref) -> 204 | undefined; 205 | create_req_timer(Msecs, Ref) -> 206 | erlang:send_after(Msecs, self(), {req_timeout, Ref}). 207 | 208 | 209 | %% maybe_reply({reply, Reply, State = #state{active = Request}}) -> 210 | %% NewRequest = send_caller(Reply, Request), 211 | %% State#state{active = NewRequest}; 212 | 213 | maybe_reply({noreply, State = #state{}}) -> 214 | State. 215 | 216 | %% Replies the message and clears the requester id 217 | send_caller(Msg, #request{from = From}=Request) when From /= undefined -> 218 | gen_server:reply(From, Msg), 219 | Request#request{from = undefined}. 220 | 221 | %% @private 222 | %% Cancel a request timer made by create_timer/2 223 | cancel_req_timer(undefined) -> 224 | ok; 225 | cancel_req_timer(Tref) -> 226 | _ = erlang:cancel_timer(Tref), 227 | ok. 228 | -------------------------------------------------------------------------------- /src/antidotec_reg.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2014 SyncFree Consortium. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | -module(antidotec_reg). 21 | 22 | -include_lib("antidote_pb_codec/include/antidote_pb.hrl"). 23 | 24 | -behaviour(antidotec_datatype). 25 | 26 | -export([new/0, 27 | new/1, 28 | value/1, 29 | dirty_value/1, 30 | is_type/1, 31 | to_ops/2, 32 | type/0 33 | ]). 34 | 35 | -export([assign/2 36 | ]). 37 | 38 | -record(antidote_reg, { 39 | value 40 | }). 41 | 42 | -export_type([antidote_reg/0]). 43 | -opaque antidote_reg() :: #antidote_reg{}. 44 | 45 | -ifdef(TEST). 46 | -include_lib("eunit/include/eunit.hrl"). 47 | -endif. 48 | 49 | 50 | -spec new() -> antidote_reg(). 51 | new() -> 52 | #antidote_reg{value=""}. 53 | 54 | 55 | new(Value) -> 56 | #antidote_reg{value=Value}. 57 | 58 | -spec value(antidote_reg()) -> [term()]. 59 | value(#antidote_reg{value=Value}) -> Value. 60 | 61 | dirty_value(#antidote_reg{value=Value}) -> Value. 62 | 63 | %% @doc Determines whether the passed term is a reg container. 64 | -spec is_type(term()) -> boolean(). 65 | is_type(T) -> 66 | is_record(T, antidote_reg). 67 | 68 | %% @doc Returns the symbolic name of this container. 69 | -spec type() -> reg. 70 | type() -> reg. 71 | 72 | -spec to_ops(term(), term()) -> []. 73 | to_ops(_, _) -> 74 | []. 75 | 76 | assign(_, Value) -> 77 | #antidote_reg{value=Value}. 78 | 79 | 80 | %% =================================================================== 81 | %% EUnit tests 82 | %% =================================================================== 83 | -ifdef(TEST). 84 | -endif. 85 | -------------------------------------------------------------------------------- /src/antidotec_set.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2014 SyncFree Consortium. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | -module(antidotec_set). 21 | 22 | -ifdef(TEST). 23 | -include_lib("eunit/include/eunit.hrl"). 24 | -endif. 25 | 26 | 27 | -include_lib("antidote_pb_codec/include/antidote_pb.hrl"). 28 | 29 | -behaviour(antidotec_datatype). 30 | 31 | -export([new/0, 32 | new/1, 33 | value/1, 34 | dirty_value/1, 35 | to_ops/2, 36 | is_type/1, 37 | type/0 38 | ]). 39 | 40 | -export([add/2, 41 | remove/2, 42 | contains/2 43 | ]). 44 | 45 | -record(antidote_set, { 46 | set :: sets:set(), 47 | adds :: sets:set(), 48 | rems :: sets:set() 49 | }). 50 | 51 | -export_type([antidote_set/0]). 52 | -opaque antidote_set() :: #antidote_set{}. 53 | 54 | 55 | -spec new() -> antidote_set(). 56 | new() -> 57 | #antidote_set{set=sets:new(), adds=sets:new(), rems=sets:new()}. 58 | 59 | -spec new(list() | sets:set()) -> antidote_set(). 60 | new(List) when is_list(List) -> 61 | Set = sets:from_list(List), 62 | #antidote_set{set=Set, adds=sets:new(), rems=sets:new()}; 63 | 64 | new(Set) -> 65 | #antidote_set{set=Set, adds=sets:new(), rems=sets:new()}. 66 | 67 | -spec value(antidote_set()) -> [term()]. 68 | value(#antidote_set{set=Set}) -> sets:to_list(Set). 69 | 70 | -spec dirty_value(antidote_set()) -> [term()]. 71 | dirty_value(#antidote_set{set=Set, adds = Adds, rems = Rems}) -> 72 | sets:to_list(sets:subtract(sets:union(Set, Adds), Rems)). 73 | 74 | %% @doc Adds an element to the local set container. 75 | -spec add(term(), antidote_set()) -> antidote_set(). 76 | add(Elem, #antidote_set{adds=Adds}=Fset) -> 77 | Fset#antidote_set{adds=sets:add_element(Elem, Adds)}. 78 | 79 | -spec remove(term(), antidote_set()) -> antidote_set(). 80 | remove(Elem, #antidote_set{rems=Rems}=Fset) -> 81 | Fset#antidote_set{rems=sets:add_element(Elem, Rems)}. 82 | 83 | -spec contains(term(), antidote_set()) -> boolean(). 84 | contains(Elem, #antidote_set{set = Set}) -> 85 | sets:is_element(Elem, Set). 86 | 87 | %% @doc Determines whether the passed term is a set container. 88 | -spec is_type(term()) -> boolean(). 89 | is_type(T) -> 90 | is_record(T, antidote_set). 91 | 92 | %% @doc Returns the symbolic name of this container. 93 | -spec type() -> set. 94 | type() -> set. 95 | 96 | to_ops(BoundObject, #antidote_set{adds = Adds, rems = Rems}) -> 97 | R = case sets:size(Rems) > 0 of 98 | true -> [{BoundObject, remove_all, sets:to_list(Rems)}]; 99 | false -> [] 100 | end, 101 | case sets:size(Adds) > 0 of 102 | true -> [{BoundObject, add_all, sets:to_list(Adds)} | R]; 103 | false -> R 104 | end. 105 | 106 | 107 | %% =================================================================== 108 | %% EUnit tests 109 | %% =================================================================== 110 | -ifdef(TEST). 111 | add_op_test() -> 112 | New = antidotec_set:new([]), 113 | Set1 = antidotec_set:dirty_value(New), 114 | ?assertEqual([], Set1), 115 | OneElement = antidotec_set:add(<<"value1">>, New), 116 | Set2 = antidotec_set:dirty_value(OneElement), 117 | ?assertEqual([<<"value1">>], Set2). 118 | 119 | add_op_existing_set_test() -> 120 | New = antidotec_set:new([<<"elem1">>, <<"elem2">>, <<"elem3">>]), 121 | ThreeElemSet = antidotec_set:dirty_value(New), 122 | ?assertEqual([<<"elem1">>, <<"elem2">>, <<"elem3">>], lists:sort(ThreeElemSet)), 123 | AddElem = antidotec_set:add(<<"elem4">>, New), 124 | S1 = antidotec_set:remove(<<"elem4">>, AddElem), 125 | S2 = antidotec_set:remove(<<"elem2">>, S1), 126 | TwoElemSet = antidotec_set:dirty_value(S2), 127 | ?assertEqual([<<"elem1">>, <<"elem3">>], lists:sort(TwoElemSet)). 128 | 129 | -endif. 130 | --------------------------------------------------------------------------------