this repo has no description
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Write pairs with CoW

garrison faff76bf 80723a34

+85 -17
+85 -17
lib/btree/btree.ex
··· 18 18 opts: Opts.t, 19 19 page_store: :ets.table, 20 20 free_list: FreeList.t, 21 + root_store: :ets.table, 21 22 } 22 23 @enforce_keys [ 23 24 :opts, 24 25 :page_store, 25 26 :free_list, 26 - :root_address, 27 + :root_store, 27 28 ] 28 29 defstruct @enforce_keys 29 30 ··· 43 44 opts: opts, 44 45 page_store: :ets.new(__MODULE__, [:set, :protected]), 45 46 free_list: FreeList.new(), 46 - root_address: nil, 47 + root_store: :ets.new(__MODULE__, [:ordered_set, :protected]), 47 48 } 48 - btree = create_root_pages(btree) 49 + :ok = create_root_pages(btree) 49 50 50 51 btree 51 52 end 52 53 53 54 defp create_root_pages(%BTree{} = btree) do 54 - assert btree.root_address == nil 55 - 56 55 %{ 57 56 free_list: free_list, 58 57 page_store: page_store, 58 + root_store: root_store, 59 59 opts: %{ 60 60 page_size: opt_page_size, 61 61 }, 62 62 } = btree 63 + 64 + assert not :ets.member(btree.root_store, :root_address) 63 65 64 66 [root_index, leaf_index] = FreeList.reserve_pages(free_list, 2) 65 67 ··· 75 77 root_checksum = checksum(root_data) 76 78 :ok = write_page(page_store, root_index, root_data) 77 79 78 - %{btree | 79 - root_address: {root_index, root_checksum}, 80 - } 80 + :ets.insert(root_store, {:root_address, {root_index, root_checksum}}) 81 + :ok 81 82 end 82 83 83 84 defp checksum(data) do ··· 90 91 :ok 91 92 end 92 93 93 - # TODO: defp 94 - def read_page(page_store, page_index) do 94 + defp read_page(page_store, page_index, page_checksum) do 95 95 case :ets.lookup(page_store, page_index) do 96 - [{_index, data}] -> data 97 - [] -> nil 96 + [{_index, data}] -> 97 + assert checksum(data) == page_checksum 98 + data 99 + [] -> 100 + assert false 98 101 end 99 102 end 100 103 101 - def apply_batch(%BTree{} = btree, _mutations) do 102 - {root_index, _checksum} = btree.root_address 103 - root_page_data = read_page(btree.page_store, root_index) 104 - {:inner, root_pairs} = Page.decode_page(root_page_data) 105 - dbg root_pairs 104 + def apply_batch(%BTree{} = btree, mutations) do 105 + %{ 106 + page_store: page_store, 107 + free_list: free_list, 108 + root_store: root_store, 109 + opts: %{ 110 + page_size: opt_page_size, 111 + }, 112 + } = btree 113 + 114 + Enum.each(mutations, fn 115 + {:write, k, v} -> 116 + [{:root_address, {root_index, root_checksum}}] = :ets.lookup(root_store, :root_address) 117 + new_root_address = write_recursive(page_store, free_list, opt_page_size, root_index, root_checksum, k, v) 118 + :ets.insert(root_store, {:root_address, new_root_address}) 119 + end) 106 120 107 121 :ok 122 + end 123 + 124 + defp write_recursive(page_store, free_list, opt_page_size, page_index, page_checksum, key, value) do 125 + {page_type, pairs} = 126 + read_page(page_store, page_index, page_checksum) 127 + |> Page.decode_page() 128 + 129 + pairs = 130 + case page_type do 131 + :inner -> 132 + assert pairs != [] 133 + {child_key, child_address} = find_pointer(pairs, key) 134 + <<child_index::integer-64, child_checksum::binary-16>> = child_address 135 + 136 + {new_child_index, new_child_checksum} = write_recursive(page_store, free_list, opt_page_size, child_index, child_checksum, key, value) 137 + new_child_value = <<new_child_index::integer-64, new_child_checksum::binary-16>> 138 + 139 + write_pair(pairs, child_key, new_child_value) 140 + 141 + :leaf -> 142 + write_pair(pairs, key, value) 143 + end 144 + 145 + new_page_data = Page.encode_page_from_pairs(:leaf, opt_page_size, pairs) 146 + [new_page_index] = FreeList.reserve_pages(free_list, 1) 147 + new_page_checksum = checksum(new_page_data) 148 + :ok = write_page(page_store, new_page_index, new_page_data) 149 + 150 + {new_page_index, new_page_checksum} 151 + end 152 + 153 + defp write_pair(pairs, key, value, acc \\ []) 154 + defp write_pair([], key, value, acc), do: Enum.reverse(acc, [{key, value}]) 155 + defp write_pair([{k, _v} = p | pairs_rest], key, value, acc) do 156 + cond do 157 + k < key -> write_pair(pairs_rest, key, value, [p | acc]) 158 + k == key -> Enum.reverse([{key, value} | acc], pairs_rest) 159 + k > key -> Enum.reverse([{key, value} | acc], [p | pairs_rest]) 160 + end 161 + end 162 + 163 + defp find_pointer(pairs, search_key, last_pair \\ nil) 164 + 165 + defp find_pointer([], _key, last_pair) do 166 + # if last_pair == nil, pairs was empty 167 + assert last_pair != nil 168 + last_pair 169 + end 170 + 171 + defp find_pointer([{fence_key, _address} = pair | pairs_rest], search_key, last_pair) do 172 + case fence_key > search_key do 173 + true -> last_pair 174 + false -> find_pointer(pairs_rest, search_key, pair) 175 + end 108 176 end 109 177 end