this repo has no description
2
fork

Configure Feed

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

Build initial btree

garrison d2cd3747 8c926942

+140 -4
+57 -4
lib/btree/btree.ex
··· 1 1 defmodule Hobbes.BTree do 2 2 alias Hobbes.BTree 3 + alias Hobbes.BTree.{FreeList, Page} 3 4 4 5 import ExUnit.Assertions, only: [assert: 1] 5 6 ··· 16 17 @type t :: %__MODULE__{ 17 18 opts: Opts.t, 18 19 page_store: :ets.table, 20 + free_list: FreeList.t, 19 21 } 20 22 @enforce_keys [ 21 23 :opts, 22 24 :page_store, 25 + :free_list, 26 + :root_address, 23 27 ] 24 28 defstruct @enforce_keys 25 29 26 30 defp parse_opts(opts) do 27 - page_size = Keyword.get(opts, :page_size, 4096) 31 + page_size = Keyword.get(opts, :page_size, 1024) 28 32 assert page_size > 0 29 33 30 34 %Opts{ ··· 34 38 35 39 def new(opts \\ []) do 36 40 opts = parse_opts(opts) 37 - 38 - page_store = :ets.new(__MODULE__, [:set, :protected]) 39 41 40 42 btree = %BTree{ 41 43 opts: opts, 44 + page_store: :ets.new(__MODULE__, [:set, :protected]), 45 + free_list: FreeList.new(), 46 + root_address: nil, 47 + } 48 + btree = create_root_pages(btree) 49 + 50 + btree 51 + end 52 + 53 + defp create_root_pages(%BTree{} = btree) do 54 + assert btree.root_address == nil 55 + 56 + %{ 57 + free_list: free_list, 42 58 page_store: page_store, 59 + opts: %{ 60 + page_size: opt_page_size, 61 + }, 62 + } = btree 63 + 64 + [root_index, leaf_index] = FreeList.reserve_pages(free_list, 2) 65 + 66 + leaf_data = Page.encode_page_from_pairs(:leaf, opt_page_size, []) 67 + leaf_checksum = checksum(leaf_data) 68 + :ok = write_page(page_store, leaf_index, leaf_data) 69 + 70 + leaf_start_key = "" 71 + leaf_address = <<leaf_index::integer-64, leaf_checksum::binary-16>> 72 + 73 + root_pairs = [{leaf_start_key, leaf_address}] 74 + root_data = Page.encode_page_from_pairs(:inner, opt_page_size, root_pairs) 75 + root_checksum = checksum(root_data) 76 + :ok = write_page(page_store, root_index, root_data) 77 + 78 + %{btree | 79 + root_address: {root_index, root_checksum}, 43 80 } 81 + end 44 82 45 - btree 83 + defp checksum(data) do 84 + <<hash::binary-16, _rest::binary-16>> = :crypto.hash(:sha256, data) 85 + hash 86 + end 87 + 88 + defp write_page(page_store, page_index, data) do 89 + :ets.insert(page_store, {page_index, data}) 90 + :ok 91 + end 92 + 93 + # TODO: defp 94 + def read_page(page_store, page_index) do 95 + case :ets.lookup(page_store, page_index) do 96 + [{_index, data}] -> data 97 + [] -> nil 98 + end 46 99 end 47 100 48 101 def apply_batch(%BTree{} = _btree, _mutations) do
+19
lib/btree/free_list.ex
··· 1 + defmodule Hobbes.BTree.FreeList do 2 + @type t :: :ets.table 3 + 4 + @spec new :: t 5 + def new do 6 + free_list = :ets.new(__MODULE__, [:ordered_set, :private]) 7 + :ets.insert(free_list, {:max_index, 0}) 8 + 9 + free_list 10 + end 11 + 12 + @spec reserve_pages(t, non_neg_integer) :: [non_neg_integer] 13 + def reserve_pages(free_list, count) do 14 + [{:max_index, max_index}] = :ets.lookup(free_list, :max_index) 15 + 16 + :ets.insert(free_list, {:max_index, max_index + count}) 17 + Enum.to_list((max_index + 1)..(max_index + count)) 18 + end 19 + end
+64
lib/btree/page.ex
··· 1 + defmodule Hobbes.BTree.Page do 2 + import ExUnit.Assertions, only: [assert: 1] 3 + 4 + @type page_type :: :inner | :leaf 5 + 6 + defp encode_type(:inner), do: 0x00 7 + defp encode_type(:leaf), do: 0x01 8 + 9 + def encode_page_from_pairs(type, opt_page_size, pairs) do 10 + type_byte = encode_type(type) 11 + {body, slots, count} = do_encode_page(pairs, "", "", 0) 12 + 13 + # TODO: probably large enough? 14 + assert count < (2 ** 16) 15 + trailer = << 16 + count::integer-16, 17 + type_byte::integer-8, 18 + >> 19 + 20 + pad_bytes = opt_page_size - byte_size(body) - byte_size(slots) - byte_size(trailer) 21 + assert pad_bytes >= 0 22 + 23 + page_data = << 24 + body::binary, 25 + 0::integer-unit(8)-size(pad_bytes), 26 + slots::binary, 27 + trailer::binary, 28 + >> 29 + assert byte_size(page_data) == opt_page_size 30 + 31 + page_data 32 + end 33 + 34 + defp do_encode_page([], body_acc, slots_acc, count_acc) do 35 + {body_acc, slots_acc, count_acc} 36 + end 37 + 38 + defp do_encode_page([{key, value} | pairs_rest], body_acc, slots_acc, count_acc) do 39 + pos = byte_size(body_acc) 40 + key_size = byte_size(key) 41 + value_size = byte_size(value) 42 + 43 + # TODO: larger 44 + assert key_size < (2 ** 16) 45 + assert value_size < (2 ** 16) 46 + body_acc = << 47 + body_acc::binary, 48 + key_size::integer-16, 49 + value_size::integer-16, 50 + key::binary, 51 + value::binary, 52 + >> 53 + 54 + # TODO: support >64KiB pages? 55 + assert pos < (2 ** 16) 56 + slots_acc = << 57 + slots_acc::binary, 58 + pos::integer-16, 59 + >> 60 + 61 + count_acc = count_acc + 1 62 + do_encode_page(pairs_rest, body_acc, slots_acc, count_acc) 63 + end 64 + end