this repo has no description
2
fork

Configure Feed

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

Add btree iterator

garrison a7f744c0 105778bf

+176 -1
+35 -1
lib/btree/fuzz/model_fuzz.ex
··· 1 1 defmodule Hobbes.BTree.Fuzz.ModelFuzz do 2 2 alias Hobbes.BTree 3 + alias Hobbes.BTree.Iterator 3 4 4 5 import Hobbes.FuzzUtils 5 6 ··· 49 50 end 50 51 51 52 defp random_op do 52 - case Enum.random(1..1) do 53 + case Enum.random(1..2) do 53 54 1 -> :apply_batch 55 + 2 -> :scan 54 56 end 55 57 end 56 58 ··· 64 66 :ok = BTree.Writer.apply_batch(btree, mutations) 65 67 66 68 state 69 + end 70 + 71 + defp execute(:scan, %State{} = state) do 72 + %{ 73 + btree: btree, 74 + opts: %{ 75 + key_bits: opt_key_bits, 76 + }, 77 + } = state 78 + 79 + start_key = make_key(opt_key_bits) 80 + count = Enum.random(1..10) 81 + reverse? = false 82 + 83 + _pairs = scan(btree, start_key, count, reverse?) 84 + #dbg pairs 85 + 86 + state 87 + end 88 + 89 + defp scan(btree, start_key, count, reverse?) do 90 + it = Iterator.new(btree, start_key, reverse?) 91 + do_scan(it, [], count) 92 + end 93 + 94 + defp do_scan(_it, acc, 0), do: Enum.reverse(acc) 95 + defp do_scan(it, acc, count) do 96 + it = Iterator.next(it) 97 + case it.current_pair do 98 + :infinity -> Enum.reverse(acc) 99 + pair -> do_scan(it, [pair | acc], count - 1) 100 + end 67 101 end 68 102 69 103 defp make_batch(opts) do
+141
lib/btree/iterator.ex
··· 1 + defmodule Hobbes.BTree.Iterator do 2 + alias Hobbes.BTree 3 + alias Hobbes.BTree.{Page, Iterator} 4 + 5 + import ExUnit.Assertions, only: [assert: 1] 6 + 7 + @type t :: %__MODULE__{ 8 + } 9 + @enforce_keys [ 10 + :btree, 11 + :page_stack, 12 + :current_page, 13 + :current_i, 14 + :current_pair, 15 + :reverse?, 16 + ] 17 + defstruct @enforce_keys 18 + 19 + @spec new(BTree.t, binary, boolean) :: t 20 + def new(%BTree{} = btree, start_key, reverse?) do 21 + %{ 22 + page_store: page_store, 23 + root_store: root_store, 24 + } = btree 25 + 26 + [{:root_address, {root_index, root_checksum}}] = :ets.lookup(root_store, :root_address) 27 + root_data = BTree.read_page(page_store, root_index, root_checksum) 28 + 29 + iterator = %Iterator{ 30 + btree: btree, 31 + page_stack: [], 32 + current_page: root_data, 33 + current_i: 0, 34 + current_pair: nil, 35 + reverse?: reverse?, 36 + } 37 + seek_forward(iterator, start_key) 38 + end 39 + 40 + @spec next(t) :: t 41 + def next(%Iterator{reverse?: false} = it), do: next_forward(it) 42 + 43 + defp seek_forward(%Iterator{} = it, search_key) do 44 + page_data = it.current_page 45 + {page_type, pairs} = Page.decode_page(page_data) 46 + 47 + case page_type do 48 + :inner -> 49 + {i, {_end_key, child_address}} = seek_inner(pairs, search_key, 0) 50 + <<child_index::integer-64, child_checksum::binary-16>> = child_address 51 + child_data = BTree.read_page(it.btree.page_store, child_index, child_checksum) 52 + 53 + it = %{it | 54 + page_stack: [{page_data, i} | it.page_stack], 55 + current_page: child_data, 56 + current_i: 0, 57 + } 58 + seek_forward(it, search_key) 59 + 60 + :leaf -> 61 + case seek_leaf(pairs, search_key, 0) do 62 + {i, {_k, _v} = pair} -> 63 + %{it | 64 + current_i: i, 65 + current_pair: pair, 66 + } 67 + 68 + :not_found -> 69 + it = %{it | 70 + # TODO: kinda sus to use an out-of-bounds i but the page could be empty 71 + current_i: length(pairs), 72 + } 73 + next_forward(it) 74 + end 75 + end 76 + end 77 + 78 + defp seek_inner([], _search_key, _i), do: assert false 79 + defp seek_inner([{k, _v} = p | pairs_rest], search_key, i) do 80 + case search_key < k do 81 + true -> {i, p} 82 + false -> seek_inner(pairs_rest, search_key, i + 1) 83 + end 84 + end 85 + 86 + defp seek_leaf([], _search_key, _i), do: :not_found 87 + defp seek_leaf([{k, _v} = p | pairs_rest], search_key, i) do 88 + case search_key >= k do 89 + true -> {i, p} 90 + false -> seek_leaf(pairs_rest, search_key, i) 91 + end 92 + end 93 + 94 + defp next_forward(%Iterator{} = it) do 95 + %{ 96 + current_page: current_page, 97 + current_i: current_i, 98 + } = it 99 + {page_type, pairs} = Page.decode_page(current_page) 100 + pair_count = length(pairs) 101 + 102 + case current_i < (pair_count - 1) do 103 + true -> 104 + current_i = current_i + 1 105 + current_pair = Enum.at(pairs, current_i) 106 + case page_type do 107 + :inner -> 108 + {_k, <<child_index::integer-64, child_checksum::binary-16>>} = current_pair 109 + child_page = BTree.read_page(it.btree.page_store, child_index, child_checksum) 110 + it = %{it | 111 + page_stack: [{current_i, current_page} | it.page_stack], 112 + current_i: 0, 113 + current_page: child_page, 114 + } 115 + next_forward(it) 116 + 117 + :leaf -> 118 + %{it | 119 + current_i: current_i, 120 + current_pair: current_pair, 121 + } 122 + end 123 + 124 + false -> 125 + case it.page_stack do 126 + [] -> 127 + %{it | 128 + current_pair: :infinity, 129 + } 130 + 131 + [{page, i} | stack_rest] -> 132 + it = %{it | 133 + page_stack: stack_rest, 134 + current_page: page, 135 + current_i: i, 136 + } 137 + next_forward(it) 138 + end 139 + end 140 + end 141 + end