/lib/src/elib1_guid_store.erl

https://github.com/midnightskinhead/elib1 · Erlang · 143 lines · 104 code · 25 blank · 14 comment · 3 complexity · ec498abf7bb3155f6f249a4d4cb0920a MD5 · raw file

  1. %% Copyright (c) 2006-2009 Joe Armstrong
  2. %% See MIT-LICENSE for licensing information.
  3. -module(elib1_guid_store).
  4. %% The entire history of a file is kept in an association list
  5. %% as a set of patches
  6. -export([test/0, store/1, fetch/1, fetch/2]).
  7. -import(lists, [reverse/1, reverse/2, filter/2, member/2]).
  8. test() ->
  9. file:delete("myblob.dets"),
  10. elib1_blob_store:open("myblob.dets"),
  11. store([{guid,g1},{name,joe},{content,"abc"}]),
  12. store([{guid,g1},{name,joe1},{content,"abc\ndef"}]),
  13. store([{guid,g1},{date,123},{content,"123"}]),
  14. store([{guid,g1},{date,125},{stuff,123}, {content,"123abc\ndef\n\234"}]),
  15. store([{guid,g1},{nonsense,foodedo}, {content,""}]),
  16. store([{guid,g1},{nonsense,foodedo12313}, {content,"123123"}]),
  17. elib1_blob_store:close().
  18. fetch(Guid) -> fetch(Guid, 0).
  19. fetch(Guid, N) ->
  20. case elib1_blob_store:fetch(Guid) of
  21. {ok, OldBlob} ->
  22. {Meta, Patches} = binary_to_term(OldBlob),
  23. fetch1(Meta, N, Patches);
  24. error ->
  25. exit(eBadGuid)
  26. end.
  27. fetch1(Meta, 0, _) -> Meta;
  28. fetch1(Meta, N, [P|T]) ->
  29. OldMeta = patch(P, Meta),
  30. fetch1(OldMeta, N-1, T);
  31. fetch1(_, N, []) ->
  32. exit({ebadLevel,N}).
  33. store(Assoc) ->
  34. Guid = must(guid, Assoc),
  35. NewBlob = case elib1_blob_store:fetch(Guid) of
  36. {ok, OldBlob} ->
  37. {OldMeta, Patches} = binary_to_term(OldBlob),
  38. P = diff(Assoc, OldMeta),
  39. term_to_binary({Assoc,[P|Patches]});
  40. error ->
  41. P = diff(Assoc, [{content,""}]),
  42. term_to_binary({Assoc,[P]})
  43. end,
  44. elib1_blob_store:store(Guid, NewBlob).
  45. %% patch(P, NewMeta) -> OldMeta'
  46. patch([{k,K,V}|T], Meta) ->
  47. %% key replace
  48. Meta1 = replace(K, V, Meta, []),
  49. patch(T, Meta1);
  50. patch([{d,K}|T], Meta) ->
  51. Meta1 = delete_key(K, Meta),
  52. patch(T, Meta1);
  53. patch([{p,P}|T], Meta) ->
  54. NewContent = must(content, Meta),
  55. OldContent = elib1_diff:patch(NewContent, P),
  56. Meta1 = replace(content, OldContent, Meta, []),
  57. patch(T, Meta1);
  58. patch([], Meta) ->
  59. lists:sort(Meta).
  60. %% diff(NewMeta, OldMeta) -> Patches
  61. %% compute the diffs necessary to turn the current metadata and content
  62. %% into the old metadata and content
  63. diff(NewMeta, OldMeta) ->
  64. Patch = diff1(NewMeta, OldMeta),
  65. %% io:format("Patches=~p~n",[Patch]),
  66. Old = patch(Patch, NewMeta),
  67. case {lists:sort(Old),
  68. lists:sort(OldMeta)} of
  69. {A, A} ->
  70. io:format("patches ok ...~n"),
  71. Patch;
  72. _ ->
  73. io:format("oops debug me"),
  74. elib1_misc:dump("debug",{newMeta,lists:sort(NewMeta),
  75. oldMeta,lists:sort(OldMeta),
  76. patch, Patch,
  77. old, lists:sort(Old)}),
  78. exit(oops)
  79. end.
  80. %% comput a patch to turn New into Old
  81. diff1(NewMeta, OldMeta) ->
  82. L1 = deletions(NewMeta, OldMeta),
  83. diff(OldMeta, NewMeta, L1).
  84. deletions(New, Old) ->
  85. %% keys in new that are just not in Old
  86. NewKeys = [K || {K,_} <- New],
  87. OldKeys = [K || {K,_} <- Old],
  88. L = filter(fun(I) -> not member(I, OldKeys) end, NewKeys),
  89. [{d,I} || I <- L].
  90. diff([{content,OldContent}|T], New, L) ->
  91. NewContent = must(content, New),
  92. P = elib1_diff:diff(OldContent, NewContent),
  93. diff(T, New, [{p,P}|L]);
  94. diff([{K,V}|T], New, L) ->
  95. case lookup(K, New) of
  96. {value, V} ->
  97. %% same value in both no change
  98. diff(T, New, L);
  99. _ ->
  100. %% not the same value or missing
  101. diff(T, New, [{k,K,V}|L])
  102. end;
  103. diff([], _, L) ->
  104. L.
  105. lookup(Key, L) ->
  106. case lists:keysearch(Key, 1, L) of
  107. {value, {_,V}} -> {value, V};
  108. false -> false
  109. end.
  110. must(K, [{K,V}|_]) -> V;
  111. must(K, [_|T]) -> must(K, T);
  112. must(K, []) -> exit({eMissingkey, K}).
  113. replace(K, V, [{K,_}|T], L) -> reverse(L, [{K,V}|T]);
  114. replace(K, V, [H|T], L) -> replace(K, V, T, [H|L]);
  115. replace(K, V, [], L) -> [{K,V}|L].
  116. delete_key(K, [{K,_}|T]) -> T;
  117. delete_key(K, [H|T]) -> [H|delete_key(K, T)];
  118. delete_key(_, []) -> [].