Skip to content
This repository was archived by the owner on May 27, 2022. It is now read-only.

Commit d2b6c52

Browse files
authored
Merge pull request #1 from AntidoteDB/map-based
Map based
2 parents 949922a + 5ca3200 commit d2b6c52

7 files changed

Lines changed: 105 additions & 35 deletions

File tree

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
language: erlang
22
otp_release:
3-
- 19.3
4-
- 20.3
5-
- 21.0
3+
- 21.2
64
install:
75
- make
86
script:
97
- make test
108
- make dialyzer
119
- make lint
10+
- rebar3 as test coveralls send
1211
sudo: required
1312
dist: trusty

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ compile:
88
clean:
99
$(REBAR) clean
1010

11-
distclean: clean relclean
11+
distclean: clean
1212
$(REBAR) clean --all
1313

1414
cleantests:
@@ -18,7 +18,7 @@ cleantests:
1818
test:
1919
mkdir -p logs
2020
${REBAR} eunit
21-
${REBAR} cover
21+
${REBAR} cover --verbose
2222

2323
docs:
2424
${REBAR} doc

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
vectorclock
2-
=====
2+
===========
3+
34
[![Build Status](https://travis-ci.org/AntidoteDB/vectorclock.svg?branch=master)](https://travis-ci.org/AntidoteDB/vectorclock)
5+
[![Coverage Status](https://coveralls.io/repos/github/AntidoteDB/vectorclock/badge.svg?branch=master)](https://coveralls.io/github/AntidoteDB/vectorclock?branch=master)
46

57
A vectorclock library for Erlang.

rebar.config

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,17 @@
55
{profiles,[
66
{lint, [
77
{plugins, [{rebar3_lint, {git, "https://github.com/project-fifo/rebar3_lint.git", {tag, "0.1.2"}}}]}
8-
]}
8+
]},
9+
{test, [
10+
{erl_opts, [warnings_as_errors, debug_info, no_inline_list_funcs]},
11+
{plugins, [{coveralls, {git, "https://github.com/markusn/coveralls-erl", {branch, "master"}}}]}]}
912
]}.
1013

1114
{cover_enabled, true}.
15+
{plugins, [coveralls]}.
16+
{cover_export_enabled, true}.
17+
{coveralls_coverdata, "_build/test/cover/eunit.coverdata"}.
18+
{coveralls_service_name, "travis-ci"}.
1219

1320
% configuration of style rules
1421
{elvis,
@@ -34,12 +41,11 @@
3441
#{regex => "^[a-z]([a-z0-9]*_?)*(_SUITE)?$",
3542
ignore => []}
3643
},
37-
% Can be added back if antidote_crdt_counter_b:localPermissions is renamed
38-
%{
39-
% elvis_style,
40-
% function_naming_convention,
41-
% #{regex => "^([a-z][a-z0-9]*_?)*$"}
42-
%},
44+
{
45+
elvis_style,
46+
function_naming_convention,
47+
#{regex => "^([a-z][a-z0-9]*_?)*$"}
48+
},
4349
{elvis_style, state_record_and_type},
4450
{elvis_style, no_spec_with_records}
4551
]

rebar.config.script

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
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.

src/vectorclock.app.src

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{application, vectorclock,
2-
[{description, "An vectorclock library for Erlang"},
3-
{vsn, "0.1.0"},
2+
[{description, "A vectorclock library for Erlang"},
3+
{vsn, "0.0.2"},
44
{registered, []},
55
{applications,
66
[kernel,

src/vectorclock.erl

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,34 +39,43 @@
3939
min/1,
4040
conc/2]).
4141

42-
4342
-type actor() :: any().
44-
-type vectorclock() :: dict:dict(actor(), non_neg_integer()).
43+
-type vectorclock() :: #{actor() => non_neg_integer()}.
4544
-export_type([vectorclock/0]).
4645

4746
-spec new() -> vectorclock().
4847
new() ->
49-
dict:new().
48+
maps:new().
5049

5150
-spec get_clock_of_dc(any(), vectorclock()) -> non_neg_integer().
5251
get_clock_of_dc(Key, VectorClock) ->
53-
case dict:find(Key, VectorClock) of
54-
{ok, Value} -> Value;
55-
error -> 0
56-
end.
52+
maps:get(Key, VectorClock, 0).
5753

5854
-spec set_clock_of_dc(any(), non_neg_integer(), vectorclock()) -> vectorclock().
5955
set_clock_of_dc(Key, Value, VectorClock) ->
60-
dict:store(Key, Value, VectorClock).
56+
VectorClock#{Key => Value}.
6157

6258
-spec from_list([{any(), non_neg_integer()}]) -> vectorclock().
6359
from_list(List) ->
64-
dict:from_list(List).
60+
maps:from_list(List).
6561

6662
-spec max([vectorclock()]) -> vectorclock().
6763
max([]) -> new();
6864
max([V]) -> V;
69-
max([V1, V2|T]) -> max([merge(fun erlang:max/2, V1, V2)|T]).
65+
max([V1, V2|T]) -> max([max2(V1, V2)|T]).
66+
67+
%% component-wise maximum of two clocks
68+
-spec max2(vectorclock(), vectorclock()) -> vectorclock().
69+
max2(V1, V2) ->
70+
FoldFun =
71+
fun(DC, A, Acc) ->
72+
B = get_clock_of_dc(DC, Acc),
73+
case A > B of
74+
true -> Acc#{DC => A};
75+
false -> Acc
76+
end
77+
end,
78+
maps:fold(FoldFun, V2, V1).
7079

7180
-spec min([vectorclock()]) -> vectorclock().
7281
min([]) -> new();
@@ -75,7 +84,7 @@ min([V1, V2|T]) -> min([merge(fun erlang:min/2, V1, V2)|T]).
7584

7685
-spec merge(fun((non_neg_integer(), non_neg_integer()) -> non_neg_integer()), vectorclock(), vectorclock()) -> vectorclock().
7786
merge(F, V1, V2) ->
78-
AllDCs = dict:fetch_keys(V1) ++ dict:fetch_keys(V2),
87+
AllDCs = maps:keys(maps:merge(V1, V2)),
7988
Func = fun(DC) ->
8089
A = get_clock_of_dc(DC, V1),
8190
B = get_clock_of_dc(DC, V2),
@@ -85,8 +94,7 @@ merge(F, V1, V2) ->
8594

8695
-spec for_all_keys(fun((non_neg_integer(), non_neg_integer()) -> boolean()), vectorclock(), vectorclock()) -> boolean().
8796
for_all_keys(F, V1, V2) ->
88-
%% We could but do not care about duplicate DC keys - finding duplicates is not worth the effort
89-
AllDCs = dict:fetch_keys(V1) ++ dict:fetch_keys(V2),
97+
AllDCs = maps:keys(maps:merge(V1, V2)),
9098
Func = fun(DC) ->
9199
A = get_clock_of_dc(DC, V1),
92100
B = get_clock_of_dc(DC, V2),
@@ -95,13 +103,23 @@ for_all_keys(F, V1, V2) ->
95103
lists:all(Func, AllDCs).
96104

97105
-spec eq(vectorclock(), vectorclock()) -> boolean().
98-
eq(V1, V2) -> for_all_keys(fun(A, B) -> A == B end, V1, V2).
106+
eq(V1, V2) -> le(V1, V2) andalso le(V2, V1).
99107

100108
-spec le(vectorclock(), vectorclock()) -> boolean().
101-
le(V1, V2) -> for_all_keys(fun(A, B) -> A =< B end, V1, V2).
109+
le(V1, V2) ->
110+
try
111+
maps:fold(fun (DC, V, true) ->
112+
case V =< get_clock_of_dc(DC, V2) of
113+
true -> true;
114+
false -> throw(false)
115+
end
116+
end, true, V1)
117+
catch
118+
false -> false
119+
end .
102120

103121
-spec ge(vectorclock(), vectorclock()) -> boolean().
104-
ge(V1, V2) -> for_all_keys(fun(A, B) -> A >= B end, V1, V2).
122+
ge(V1, V2) -> le(V2, V1).
105123

106124
-spec all_dots_smaller(vectorclock(), vectorclock()) -> boolean().
107125
all_dots_smaller(V1, V2) -> for_all_keys(fun(A, B) -> A < B end, V1, V2).
@@ -110,16 +128,41 @@ all_dots_smaller(V1, V2) -> for_all_keys(fun(A, B) -> A < B end, V1, V2).
110128
all_dots_greater(V1, V2) -> for_all_keys(fun(A, B) -> A > B end, V1, V2).
111129

112130
-spec gt(vectorclock(), vectorclock()) -> boolean().
113-
gt(V1, V2) -> ge(V1, V2) and (not eq(V1, V2)).
131+
gt(V1, V2) -> lt(V2, V1).
114132

115133
-spec lt(vectorclock(), vectorclock()) -> boolean().
116-
lt(V1, V2) -> le(V1, V2) and (not eq(V1, V2)).
134+
lt(V1, V2) ->
135+
try
136+
maps:fold(fun (DC, V, Acc) ->
137+
X = get_clock_of_dc(DC, V2),
138+
case V =< X of
139+
true -> Acc orelse V < X;
140+
false -> throw(false)
141+
end
142+
end, false, V1)
143+
orelse
144+
maps:fold(fun (DC, V, _) ->
145+
X = get_clock_of_dc(DC, V1),
146+
case V > X of
147+
true -> throw(true);
148+
false -> false
149+
end
150+
end, false, V2)
151+
catch
152+
R -> R
153+
end .
117154

118155
-spec conc(vectorclock(), vectorclock()) -> boolean().
119156
conc(V1, V2) -> (not ge(V1, V2)) andalso (not le(V1, V2)).
120157

121158
-ifdef(TEST).
122159

160+
vectorclock_empty_test() ->
161+
V1 = vectorclock:new(),
162+
V2 = vectorclock:from_list([]),
163+
?assertEqual(V1, V2),
164+
?assertEqual(eq(vectorclock:min([]), vectorclock:max([])), true).
165+
123166
vectorclock_test() ->
124167
V1 = vectorclock:from_list([{1, 5}, {2, 4}, {3, 5}, {4, 6}]),
125168
V2 = vectorclock:from_list([{1, 4}, {2, 3}, {3, 4}, {4, 5}]),
@@ -136,6 +179,11 @@ vectorclock_test() ->
136179
?assertEqual(eq(V1, V4), false),
137180
?assertEqual(ge(V1, V5), false).
138181

182+
vectorclock_lt_test() ->
183+
?assertEqual(lt(from_list([{a, 1}]), from_list([{a, 1}, {b, 1}])), true),
184+
?assertEqual(lt(from_list([{a, 1}]), from_list([{a, 1}])), false),
185+
?assertEqual(lt(from_list([{a, 2}]), from_list([{a, 1}])), false).
186+
139187
vectorclock_max_test() ->
140188
V1 = vectorclock:from_list([{1, 5}, {2, 4}]),
141189
V2 = vectorclock:from_list([{1, 6}, {2, 3}]),
@@ -153,7 +201,6 @@ vectorclock_max_test() ->
153201
?assertEqual(eq(max([V1, V2, V3]), Expected123), true),
154202
?assertEqual(eq(max([V1, V2, V3]), Unexpected123), false).
155203

156-
157204
vectorclock_min_test() ->
158205
V1 = vectorclock:from_list([{1, 5}, {2, 4}]),
159206
V2 = vectorclock:from_list([{1, 6}, {2, 3}]),
@@ -169,7 +216,8 @@ vectorclock_min_test() ->
169216
?assertEqual(eq(min([V2, V3]), Expected23), true),
170217
?assertEqual(eq(min([V1, V3]), Expected13), true),
171218
?assertEqual(eq(min([V1, V2, V3]), Expected123), true),
172-
?assertEqual(eq(min([V1, V2, V3]), Unexpected123), false).
219+
?assertEqual(eq(min([V1, V2, V3]), Unexpected123), false),
220+
?assertEqual(eq(vectorclock:min([V1]), vectorclock:max([V1])), true).
173221

174222
vectorclock_conc_test() ->
175223
V1 = vectorclock:from_list([{1, 5}, {2, 4}]),
@@ -182,4 +230,13 @@ vectorclock_conc_test() ->
182230
?assertEqual(conc(V2, V3), true),
183231
?assertEqual(conc(V3, V4), false),
184232
?assertEqual(conc(V5, V4), false).
233+
234+
vectorclock_set_test() ->
235+
V1 = vectorclock:from_list([{1, 1}, {2, 2}]),
236+
V2 = vectorclock:from_list([{1, 1}, {2, 2}, {3, 3}]),
237+
V3 = vectorclock:from_list([{1, 1}, {2, 4}]),
238+
239+
?assertEqual(eq(V2, vectorclock:set_clock_of_dc(3, 3, V1)), true),
240+
?assertEqual(eq(V3, vectorclock:set_clock_of_dc(2, 4, V1)), true).
241+
185242
-endif.

0 commit comments

Comments
 (0)