Merge pull request #142 from martinsumner/mas-i14-undefinedoldobjects
Mas i14 undefinedoldobjects
This commit is contained in:
commit
c827c60042
1 changed files with 126 additions and 29 deletions
|
@ -58,6 +58,8 @@
|
||||||
new_tree/1,
|
new_tree/1,
|
||||||
new_tree/2,
|
new_tree/2,
|
||||||
add_kv/4,
|
add_kv/4,
|
||||||
|
add_kv/5,
|
||||||
|
alter_segment/3,
|
||||||
find_dirtyleaves/2,
|
find_dirtyleaves/2,
|
||||||
find_dirtysegments/2,
|
find_dirtysegments/2,
|
||||||
fetch_root/1,
|
fetch_root/1,
|
||||||
|
@ -106,9 +108,11 @@
|
||||||
|
|
||||||
-type tictactree() :: #tictactree{}.
|
-type tictactree() :: #tictactree{}.
|
||||||
-type segment48() :: {segment_hash, integer(), integer()}.
|
-type segment48() :: {segment_hash, integer(), integer()}.
|
||||||
|
-type tree_extract() :: {binary(), integer(), integer(), integer(), binary()}.
|
||||||
|
|
||||||
-export_type([tictactree/0, segment48/0]).
|
-export_type([tictactree/0, segment48/0]).
|
||||||
|
|
||||||
|
|
||||||
%%%============================================================================
|
%%%============================================================================
|
||||||
%%% External functions
|
%%% External functions
|
||||||
%%%============================================================================
|
%%%============================================================================
|
||||||
|
@ -189,42 +193,43 @@ import_tree(ExportedTree) ->
|
||||||
%% BinExtractFun will also need to do any canonicalisation necessary to make
|
%% BinExtractFun will also need to do any canonicalisation necessary to make
|
||||||
%% the hash consistent (such as whitespace removal, or sorting)
|
%% the hash consistent (such as whitespace removal, or sorting)
|
||||||
add_kv(TicTacTree, Key, Value, BinExtractFun) ->
|
add_kv(TicTacTree, Key, Value, BinExtractFun) ->
|
||||||
|
add_kv(TicTacTree, Key, Value, BinExtractFun, false).
|
||||||
|
|
||||||
|
-spec add_kv(tictactree(), tuple(), tuple(), fun(), boolean())
|
||||||
|
-> tictactree()|{tictactree(), integer()}.
|
||||||
|
%% @doc
|
||||||
|
%% add_kv with ability to return segment ID of Key added
|
||||||
|
add_kv(TicTacTree, Key, Value, BinExtractFun, ReturnSegment) ->
|
||||||
{BinK, BinV} = BinExtractFun(Key, Value),
|
{BinK, BinV} = BinExtractFun(Key, Value),
|
||||||
{SegHash, SegChangeHash} = tictac_hash(BinK, BinV),
|
{SegHash, SegChangeHash} = tictac_hash(BinK, BinV),
|
||||||
Segment = get_segment(SegHash, TicTacTree#tictactree.segment_count),
|
Segment = get_segment(SegHash, TicTacTree#tictactree.segment_count),
|
||||||
|
|
||||||
Level2Pos =
|
{SegLeaf1, SegLeaf2, L1Extract, L2Extract}
|
||||||
Segment band (?L2_CHUNKSIZE - 1),
|
= extract_segment(Segment, TicTacTree),
|
||||||
Level1Pos =
|
|
||||||
(Segment bsr ?L2_BITSIZE)
|
|
||||||
band (TicTacTree#tictactree.width - 1),
|
|
||||||
|
|
||||||
Level2BytePos = ?HASH_SIZE * Level2Pos,
|
|
||||||
Level1BytePos = ?HASH_SIZE * Level1Pos,
|
|
||||||
|
|
||||||
Level2 = get_level2(TicTacTree, Level1Pos),
|
|
||||||
|
|
||||||
HashIntLength = ?HASH_SIZE * 8,
|
|
||||||
<<PreL2:Level2BytePos/binary,
|
|
||||||
SegLeaf2:HashIntLength/integer,
|
|
||||||
PostL2/binary>> = Level2,
|
|
||||||
<<PreL1:Level1BytePos/binary,
|
|
||||||
SegLeaf1:HashIntLength/integer,
|
|
||||||
PostL1/binary>> = TicTacTree#tictactree.level1,
|
|
||||||
|
|
||||||
SegLeaf2Upd = SegLeaf2 bxor SegChangeHash,
|
SegLeaf2Upd = SegLeaf2 bxor SegChangeHash,
|
||||||
SegLeaf1Upd = SegLeaf1 bxor SegChangeHash,
|
SegLeaf1Upd = SegLeaf1 bxor SegChangeHash,
|
||||||
|
|
||||||
|
case ReturnSegment of
|
||||||
|
true ->
|
||||||
|
{replace_segment(SegLeaf1Upd, SegLeaf2Upd,
|
||||||
|
L1Extract, L2Extract, TicTacTree),
|
||||||
|
Segment};
|
||||||
|
false ->
|
||||||
|
replace_segment(SegLeaf1Upd, SegLeaf2Upd,
|
||||||
|
L1Extract, L2Extract, TicTacTree)
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec alter_segment(integer(), integer(), tictactree()) -> tictactree().
|
||||||
|
%% @doc
|
||||||
|
%% Replace the value of a segment in the tree with a new value - for example
|
||||||
|
%% to be used in partial rebuilds of trees
|
||||||
|
alter_segment(Segment, Hash, Tree) ->
|
||||||
|
{SegLeaf1, SegLeaf2, L1Extract, L2Extract}
|
||||||
|
= extract_segment(Segment, Tree),
|
||||||
|
|
||||||
Level1Upd = <<PreL1:Level1BytePos/binary,
|
SegLeaf1Upd = SegLeaf1 bxor SegLeaf2 bxor Hash,
|
||||||
SegLeaf1Upd:HashIntLength/integer,
|
replace_segment(SegLeaf1Upd, Hash, L1Extract, L2Extract, Tree).
|
||||||
PostL1/binary>>,
|
|
||||||
Level2Upd = <<PreL2:Level2BytePos/binary,
|
|
||||||
SegLeaf2Upd:HashIntLength/integer,
|
|
||||||
PostL2/binary>>,
|
|
||||||
TicTacTree#tictactree{level1 = Level1Upd,
|
|
||||||
level2 = array:set(Level1Pos,
|
|
||||||
Level2Upd,
|
|
||||||
TicTacTree#tictactree.level2)}.
|
|
||||||
|
|
||||||
-spec find_dirtyleaves(tictactree(), tictactree()) -> list(integer()).
|
-spec find_dirtyleaves(tictactree(), tictactree()) -> list(integer()).
|
||||||
%% @doc
|
%% @doc
|
||||||
|
@ -386,6 +391,55 @@ join_segment(BranchID, LeafID) ->
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%============================================================================
|
%%%============================================================================
|
||||||
|
|
||||||
|
-spec extract_segment(integer(), tictactree()) ->
|
||||||
|
{integer(), integer(), tree_extract(), tree_extract()}.
|
||||||
|
%% @doc
|
||||||
|
%% Extract the Level 1 and Level 2 slices from a tree to prepare an update
|
||||||
|
extract_segment(Segment, TicTacTree) ->
|
||||||
|
Level2Pos =
|
||||||
|
Segment band (?L2_CHUNKSIZE - 1),
|
||||||
|
Level1Pos =
|
||||||
|
(Segment bsr ?L2_BITSIZE)
|
||||||
|
band (TicTacTree#tictactree.width - 1),
|
||||||
|
|
||||||
|
Level2BytePos = ?HASH_SIZE * Level2Pos,
|
||||||
|
Level1BytePos = ?HASH_SIZE * Level1Pos,
|
||||||
|
|
||||||
|
Level2 = get_level2(TicTacTree, Level1Pos),
|
||||||
|
|
||||||
|
HashIntLength = ?HASH_SIZE * 8,
|
||||||
|
<<PreL2:Level2BytePos/binary,
|
||||||
|
SegLeaf2:HashIntLength/integer,
|
||||||
|
PostL2/binary>> = Level2,
|
||||||
|
<<PreL1:Level1BytePos/binary,
|
||||||
|
SegLeaf1:HashIntLength/integer,
|
||||||
|
PostL1/binary>> = TicTacTree#tictactree.level1,
|
||||||
|
|
||||||
|
{SegLeaf1,
|
||||||
|
SegLeaf2,
|
||||||
|
{PreL1, Level1BytePos, Level1Pos, HashIntLength, PostL1},
|
||||||
|
{PreL2, Level2BytePos, Level2Pos, HashIntLength, PostL2}}.
|
||||||
|
|
||||||
|
|
||||||
|
-spec replace_segment(integer(), integer(),
|
||||||
|
tree_extract(), tree_extract(),
|
||||||
|
tictactree()) -> tictactree().
|
||||||
|
%% @doc
|
||||||
|
%% Replace a slice of a tree
|
||||||
|
replace_segment(L1Hash, L2Hash, L1Extract, L2Extract, TicTacTree) ->
|
||||||
|
{PreL1, Level1BytePos, Level1Pos, HashIntLength, PostL1} = L1Extract,
|
||||||
|
{PreL2, Level2BytePos, _Level2Pos, HashIntLength, PostL2} = L2Extract,
|
||||||
|
|
||||||
|
Level1Upd = <<PreL1:Level1BytePos/binary,
|
||||||
|
L1Hash:HashIntLength/integer,
|
||||||
|
PostL1/binary>>,
|
||||||
|
Level2Upd = <<PreL2:Level2BytePos/binary,
|
||||||
|
L2Hash:HashIntLength/integer,
|
||||||
|
PostL2/binary>>,
|
||||||
|
TicTacTree#tictactree{level1 = Level1Upd,
|
||||||
|
level2 = array:set(Level1Pos,
|
||||||
|
Level2Upd,
|
||||||
|
TicTacTree#tictactree.level2)}.
|
||||||
|
|
||||||
get_level2(TicTacTree, L1Pos) ->
|
get_level2(TicTacTree, L1Pos) ->
|
||||||
case array:get(L1Pos, TicTacTree#tictactree.level2) of
|
case array:get(L1Pos, TicTacTree#tictactree.level2) of
|
||||||
|
@ -585,6 +639,49 @@ merge_emptytree_test() ->
|
||||||
TreeC = merge_trees(TreeA, TreeB),
|
TreeC = merge_trees(TreeA, TreeB),
|
||||||
?assertMatch([], find_dirtyleaves(TreeA, TreeC)).
|
?assertMatch([], find_dirtyleaves(TreeA, TreeC)).
|
||||||
|
|
||||||
|
alter_segment_test() ->
|
||||||
|
BinFun = fun(K, V) -> {term_to_binary(K), term_to_binary(V)} end,
|
||||||
|
|
||||||
|
TreeX0 = new_tree(0, small),
|
||||||
|
TreeX1 = add_kv(TreeX0, {o, "B1", "X1", null}, {caine, 1}, BinFun),
|
||||||
|
TreeX2 = add_kv(TreeX1, {o, "B1", "X2", null}, {caine, 2}, BinFun),
|
||||||
|
TreeX3 = add_kv(TreeX2, {o, "B1", "X3", null}, {caine, 3}, BinFun),
|
||||||
|
TreeX4 = add_kv(TreeX3, {o, "B1", "X3", null}, {caine, 4}, BinFun),
|
||||||
|
|
||||||
|
TreeY5 = add_kv(TreeX4, {o, "B1", "Y4", null}, {caine, 5}, BinFun),
|
||||||
|
|
||||||
|
[{DeltaBranch, DeltaLeaf}] = compare_trees_maxonedelta(TreeX4, TreeY5),
|
||||||
|
DeltaSegment = DeltaBranch * ?SMALL + DeltaLeaf,
|
||||||
|
io:format("DeltaSegment ~w", [DeltaSegment]),
|
||||||
|
TreeX4A = alter_segment(DeltaSegment, 0, TreeX4),
|
||||||
|
TreeY5A = alter_segment(DeltaSegment, 0, TreeY5),
|
||||||
|
CompareResult = compare_trees_maxonedelta(TreeX4A, TreeY5A),
|
||||||
|
?assertMatch([], CompareResult).
|
||||||
|
|
||||||
|
return_segment_test() ->
|
||||||
|
BinFun = fun(K, V) -> {term_to_binary(K), term_to_binary(V)} end,
|
||||||
|
|
||||||
|
TreeX0 = new_tree(0, small),
|
||||||
|
{TreeX1, SegID}
|
||||||
|
= add_kv(TreeX0, {o, "B1", "X1", null}, {caine, 1}, BinFun, true),
|
||||||
|
TreeX2 = alter_segment(SegID, 0, TreeX1),
|
||||||
|
?assertMatch(1, length(compare_trees_maxonedelta(TreeX1, TreeX0))),
|
||||||
|
?assertMatch(1, length(compare_trees_maxonedelta(TreeX1, TreeX2))).
|
||||||
|
|
||||||
|
|
||||||
|
compare_trees_maxonedelta(Tree0, Tree1) ->
|
||||||
|
Root1 = fetch_root(Tree1),
|
||||||
|
Root0 = fetch_root(Tree0),
|
||||||
|
case find_dirtysegments(Root0, Root1) of
|
||||||
|
[BranchID] ->
|
||||||
|
[{BranchID, Branch1}] = fetch_leaves(Tree1, [BranchID]),
|
||||||
|
[{BranchID, Branch0}] = fetch_leaves(Tree0, [BranchID]),
|
||||||
|
[LeafID] = find_dirtysegments(Branch0, Branch1),
|
||||||
|
[{BranchID, LeafID}];
|
||||||
|
[] ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue