Current section

Files

Jump to
kvs lib fs.ex
Raw

lib/fs.ex

defmodule :kvs_fs do
require Record
require KVS
def db, do: ""
def start, do: :ok
def stop, do: :ok
def destroy, do: :ok
def destroy(_), do: :ok
def match(_), do: []
def index_match(_, _), do: []
def version, do: {:version, "KVS FS"}
def leave, do: :ok
def leave(_), do: :ok
def dir do
for f <- :filelib.wildcard(~c"data/*"), :filelib.is_dir(f) do
{:table, f}
end
end
def join(_node, _) do
:filelib.ensure_dir(to_charlist(dir_name() <> "/"))
initialize()
end
def initialize() do
:mnesia.create_schema([node()])
for m <- :kvs.modules(), do: :kvs.initialize(:kvs_fs, m)
:mnesia.wait_for_tables(for(t <- :kvs.tables(), do: KVS.table(t, :name)), :infinity)
end
def index(_tab, _key, _value), do: []
def get(table_name, key, _) do
hash_key = hashkey(key)
{:ok, root} = dir(table_name)
file = Path.join(root, to_string(hash_key))
case File.read(file) do
{:ok, binary} -> {:ok, :erlang.binary_to_term(binary, [:safe])}
{:error, reason} -> {:error, reason}
end
end
def put(r), do: put(r, db())
def put(records, x) when is_list(records) do
Enum.map(records, &put(&1, x))
end
def put(record, _) do
table_name = elem(record, 0)
hash_key = hashkey(elem(record, 1))
binary_value = :erlang.term_to_binary(record)
{:ok, root} = dir(table_name)
file = Path.join(root, to_string(hash_key))
:filelib.ensure_dir(to_charlist(file))
File.write!(file, binary_value, [:raw, :binary, :sync])
end
def dir_name, do: "data"
def dir(table_name) do
{:ok, cwd} = :file.get_cwd()
table_path = Path.join(dir_name(), to_string(table_name))
case :filelib.safe_relative_path(to_charlist(table_path), cwd) do
:unsafe -> {:error, {:unsafe, table_path}}
target -> {:ok, to_string(target)}
end
end
def hashkey(key) do
key
|> :erlang.term_to_binary()
|> :crypto.hash(:sha)
|> :base64.encode()
|> encode()
end
def delete(table_name, key, _) do
case get(table_name, key, []) do
{:ok, _} ->
{:ok, root} = dir(table_name)
hash_key = hashkey(key)
file = Path.join(root, to_string(hash_key))
File.rm!(file)
:ok
{:error, x} ->
{:error, x}
end
end
def delete_range(_, _, _), do: {:error, :not_found}
def keys(_, _), do: []
def key_match(_, _, _), do: []
def count(record_name) do
dir = Path.join(dir_name(), to_string(record_name))
length(:filelib.fold_files(to_charlist(dir), ~c"", true, fn a, acc -> [a | acc] end, []))
end
def all(r, _) do
dir = Path.join(dir_name(), to_string(r))
files = :filelib.fold_files(to_charlist(dir), ~c"", true, fn a, acc -> [a | acc] end, [])
List.flatten(
for f <- files do
case File.read(f) do
{:ok, binary} -> :erlang.binary_to_term(binary, [:safe])
{:error, _} -> []
end
end
)
end
def seq(record_name, incr), do: :kvs_mnesia.seq(record_name, incr)
def create_table(name, _options) do
{:ok, root} = dir(name)
File.mkdir_p!(root)
end
def add_table_index(_record, _field), do: :ok
def dump, do: :ok
# URL ENCODE
def encode(b) when is_binary(b), do: encode(:erlang.binary_to_list(b))
def encode([c | cs]) when c >= ?a and c <= ?z, do: [c | encode(cs)]
def encode([c | cs]) when c >= ?A and c <= ?Z, do: [c | encode(cs)]
def encode([c | cs]) when c >= ?0 and c <= ?9, do: [c | encode(cs)]
def encode([c | cs]) when c == 0x20, do: [?+ | encode(cs)]
def encode([c | cs]) when c in [?-, ?_, ?., ?!, ?~, ?*, ?', ?(, ?)], do: [c | encode(cs)]
def encode([c | cs]) when c <= 0x7F, do: escape_byte(c) ++ encode(cs)
import Bitwise
def encode([c | cs]) when c >= 0x7F and c <= 0x07FF do
escape_byte((c >>> 6) + 0xC0) ++
escape_byte((c &&& 0x3F) + 0x80) ++
encode(cs)
end
def encode([c | cs]) when c > 0x07FF do
escape_byte((c >>> 12) + 0xE0) ++
escape_byte((0x3F &&& (c >>> 6)) + 0x80) ++
escape_byte((c &&& 0x3F) + 0x80) ++
encode(cs)
end
def encode([c | cs]), do: escape_byte(c) ++ encode(cs)
def encode([]), do: []
def hex_octet(n) when n <= 9, do: [?0 + n]
def hex_octet(n) when n > 15, do: hex_octet(n >>> 4) ++ hex_octet(n &&& 15)
def hex_octet(n), do: [n - 10 + ?a]
def escape_byte(c), do: normalize(hex_octet(c))
def normalize(h) when length(h) == 1, do: ~c"%0" ++ h
def normalize(h), do: ~c"%" ++ h
end