Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/news.d/+dict_key_iteration.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added ``get_key`` and ``get_value_type`` to ``dict_pkg`` for enumerating a ``dict_t`` and reading each value by type.
1 change: 1 addition & 0 deletions docs/news.d/+dict_new_after_deallocate.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed ``new_dict`` crashing after ``deallocate`` of a dict that had grown beyond one bucket, caused by recycled pointers being longer than requested.
61 changes: 50 additions & 11 deletions vunit/vhdl/data_types/src/dict_pkg-body.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,36 @@ package body dict_pkg is
constant meta_length : natural := meta_num_keys+1;
constant new_bucket_size : natural := 1;

-- The pool recycles pointers that can be longer than requested but dict_t uses length as bucket count and size
impure function new_exact_ptr (
length : natural
) return integer_vector_ptr_t is
variable ptr : integer_vector_ptr_t := new_integer_vector_ptr(int_pool, length);
begin
resize(ptr, length);
return ptr;
end;

impure function new_dict
return dict_t is
variable dict : dict_t;
variable tmp : integer_vector_ptr_t;
constant num_buckets : natural := 1;
begin
dict := (p_meta => new_integer_vector_ptr(int_pool, meta_length),
p_bucket_lengths => new_integer_vector_ptr(int_pool, num_buckets),
p_bucket_keys => new_integer_vector_ptr(int_pool, num_buckets),
p_bucket_values => new_integer_vector_ptr(int_pool, num_buckets),
p_bucket_value_types => new_integer_vector_ptr(int_pool, num_buckets));
dict := (p_meta => new_exact_ptr(meta_length),
p_bucket_lengths => new_exact_ptr(num_buckets),
p_bucket_keys => new_exact_ptr(num_buckets),
p_bucket_values => new_exact_ptr(num_buckets),
p_bucket_value_types => new_exact_ptr(num_buckets));
set(dict.p_meta, meta_num_keys, 0);
for i in 0 to length(dict.p_bucket_lengths)-1 loop
-- Zero items in bucket
set(dict.p_bucket_lengths, i, 0);
tmp := new_integer_vector_ptr(int_pool, new_bucket_size);
tmp := new_exact_ptr(new_bucket_size);
set(dict.p_bucket_keys, i, to_integer(tmp));
tmp := new_integer_vector_ptr(int_pool, new_bucket_size);
tmp := new_exact_ptr(new_bucket_size);
set(dict.p_bucket_values, i, to_integer(tmp));
tmp := new_integer_vector_ptr(int_pool, new_bucket_size);
tmp := new_exact_ptr(new_bucket_size);
set(dict.p_bucket_value_types, i, to_integer(tmp));
end loop;
return dict;
Expand Down Expand Up @@ -227,9 +237,9 @@ package body dict_pkg is

-- Create new buckets
for i in old_num_buckets to num_buckets-1 loop
set(dict.p_bucket_keys, i, to_integer(new_integer_vector_ptr(int_pool, new_bucket_size)));
set(dict.p_bucket_values, i, to_integer(new_integer_vector_ptr(int_pool, new_bucket_size)));
set(dict.p_bucket_value_types, i, to_integer(new_integer_vector_ptr(int_pool, new_bucket_size)));
set(dict.p_bucket_keys, i, to_integer(new_exact_ptr(new_bucket_size)));
set(dict.p_bucket_values, i, to_integer(new_exact_ptr(new_bucket_size)));
set(dict.p_bucket_value_types, i, to_integer(new_exact_ptr(new_bucket_size)));
set(dict.p_bucket_lengths, i, 0);
end loop;

Expand Down Expand Up @@ -280,6 +290,35 @@ package body dict_pkg is
return get_value_ptr(dict, key_hash, key) /= null_string_ptr;
end;

impure function get_key (
dict : dict_t;
index : natural
) return string is
variable offset : natural := index;
variable bucket_length : natural;
variable bucket_keys : integer_vector_ptr_t;
begin
assert index < num_keys(dict) report "key index " & integer'image(index) & " out of range";
for bucket_idx in 0 to length(dict.p_bucket_lengths)-1 loop
bucket_length := get(dict.p_bucket_lengths, bucket_idx);
if offset < bucket_length then
bucket_keys := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx));
return to_string(to_string_ptr(get(bucket_keys, offset)));
end if;
offset := offset - bucket_length;
end loop;
return "";
end;

impure function get_value_type (
dict : dict_t;
key : string
) return data_type_t is
begin
assert has_key(dict, key) report "missing key '" & key & "'";
return get_value_type(dict, hash(key), key);
end;

procedure remove (
dict : dict_t;
key : string
Expand Down
13 changes: 13 additions & 0 deletions vunit/vhdl/data_types/src/dict_pkg.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,19 @@ package dict_pkg is
dict : dict_t
) return natural;

-- Key at position index, 0 <= index < num_keys(dict). The order is unspecified but stable
-- as long as the dict is not modified. Fails if index is out of range.
impure function get_key (
dict : dict_t;
index : natural
) return string;

-- The data type of the value stored for key. Fails if key doesn't exist.
impure function get_value_type (
dict : dict_t;
key : string
) return data_type_t;

procedure remove (
dict : dict_t;
key : string
Expand Down
60 changes: 60 additions & 0 deletions vunit/vhdl/data_types/test/tb_dict.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use ieee.numeric_bit.all;
use ieee.numeric_std.all;

use work.dict_pkg.all;
use work.data_types_private_pkg.all;
use work.queue_pkg.all;
use work.integer_vector_ptr_pkg.all;
use work.string_ptr_pkg.all;
Expand All @@ -29,6 +30,31 @@ architecture a of tb_dict is
begin

main : process
procedure check_enumeration(dict : dict_t; expected : natural) is
variable seen : natural := 0;
begin
check_equal(num_keys(dict), expected);
for i in 0 to num_keys(dict)-1 loop
assert has_key(dict, get_key(dict, i));
for j in 0 to i-1 loop
assert get_key(dict, j) /= get_key(dict, i) report "duplicate key";
end loop;
if get_key(dict, i) = "s" then
check(get_value_type(dict, "s") = vhdl_string); seen := seen + 1;
elsif get_key(dict, i) = "b" then
check(get_value_type(dict, "b") = vhdl_boolean); seen := seen + 1;
elsif get_key(dict, i) = "r" then
check(get_value_type(dict, "r") = vhdl_real); seen := seen + 1;
elsif get_key(dict, i) = "t" then
check(get_value_type(dict, "t") = vhdl_time); seen := seen + 1;
elsif get_key(dict, i) = "i" then
check(get_value_type(dict, "i") = vhdl_integer); seen := seen + 1;
else
check(get_value_type(dict, get_key(dict, i)) = vhdl_integer);
end if;
end loop;
check_equal(seen, 4 + boolean'pos(has_key(dict, "i")));
end;
variable dict : dict_t;
variable dict_value : dict_t;
variable integer_vector_ptr : integer_vector_ptr_t;
Expand Down Expand Up @@ -254,6 +280,40 @@ begin
queue := get_queue_t_ref(dict, "key");
check_equal(pop_integer(queue), 17);

elsif run("test enumerate keys with types") then
dict := new_dict;
set_string(dict, "s", "value");
set_integer(dict, "i", 1);
set_boolean(dict, "b", true);
set_real(dict, "r", 1.5);
set_time(dict, "t", 1 ns);
check_enumeration(dict, 5);
remove(dict, "i");
check_enumeration(dict, 4);
check_false(has_key(dict, "i"));
for i in 1 to 200 loop
set_integer(dict, "k" & integer'image(i), i);
end loop;
check_enumeration(dict, 4 + 200);
for i in 1 to 200 loop
remove(dict, "k" & integer'image(i));
end loop;
check_enumeration(dict, 4);

elsif run("test new dict after deallocate of a grown dict") then
dict := new_dict;
for i in 1 to 200 loop
set_integer(dict, "k" & integer'image(i), i);
end loop;
deallocate(dict);
dict := new_dict;
check_equal(num_keys(dict), 0);
for i in 1 to 200 loop
set_integer(dict, "k" & integer'image(i), i);
end loop;
check_equal(num_keys(dict), 200);
check_equal(get_integer(dict, "k17"), 17);

elsif run("Test push and pop dict_t") then
queue := new_queue;
dict := new_dict;
Expand Down
Loading