mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
add dict tests with string keys/values
This commit is contained in:
parent
7f8ca150f5
commit
02e161f839
3 changed files with 112 additions and 62 deletions
|
@ -5,8 +5,6 @@ const mem = std.mem;
|
||||||
const Allocator = mem.Allocator;
|
const Allocator = mem.Allocator;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
|
||||||
const level_size = 32;
|
|
||||||
|
|
||||||
const INITIAL_SEED = 0xc70f6907;
|
const INITIAL_SEED = 0xc70f6907;
|
||||||
const REFCOUNT_ONE_ISIZE: comptime isize = std.math.minInt(isize);
|
const REFCOUNT_ONE_ISIZE: comptime isize = std.math.minInt(isize);
|
||||||
const REFCOUNT_ONE: usize = @bitCast(usize, REFCOUNT_ONE_ISIZE);
|
const REFCOUNT_ONE: usize = @bitCast(usize, REFCOUNT_ONE_ISIZE);
|
||||||
|
@ -31,10 +29,11 @@ const MaybeIndex = union(MaybeIndexTag) {
|
||||||
};
|
};
|
||||||
|
|
||||||
fn nextSeed(seed: u64) u64 {
|
fn nextSeed(seed: u64) u64 {
|
||||||
|
// TODO is this a valid way to get a new seed? are there better ways?
|
||||||
return seed + 1;
|
return seed + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn total_slots_at_level(input: usize) usize {
|
fn totalCapacityAtLevel(input: usize) usize {
|
||||||
if (input == 0) {
|
if (input == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -106,17 +105,6 @@ pub const RocDict = extern struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: RocDict, allocator: *Allocator, key_size: usize, value_size: usize) void {
|
|
||||||
if (!self.isEmpty()) {
|
|
||||||
const slot_size = slotSize(key_size, value_size);
|
|
||||||
|
|
||||||
const dict_bytes_ptr: [*]u8 = self.dict_bytes orelse unreachable;
|
|
||||||
|
|
||||||
const dict_bytes: []u8 = dict_bytes_ptr[0..(self.number_of_levels)];
|
|
||||||
allocator.free(dict_bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn allocate(
|
pub fn allocate(
|
||||||
allocator: *Allocator,
|
allocator: *Allocator,
|
||||||
number_of_levels: usize,
|
number_of_levels: usize,
|
||||||
|
@ -125,7 +113,7 @@ pub const RocDict = extern struct {
|
||||||
key_size: usize,
|
key_size: usize,
|
||||||
value_size: usize,
|
value_size: usize,
|
||||||
) RocDict {
|
) RocDict {
|
||||||
const number_of_slots = total_slots_at_level(number_of_levels);
|
const number_of_slots = totalCapacityAtLevel(number_of_levels);
|
||||||
const slot_size = slotSize(key_size, value_size);
|
const slot_size = slotSize(key_size, value_size);
|
||||||
const data_bytes = number_of_slots * slot_size;
|
const data_bytes = number_of_slots * slot_size;
|
||||||
|
|
||||||
|
@ -147,7 +135,7 @@ pub const RocDict = extern struct {
|
||||||
const slot_size = slotSize(key_width, value_width);
|
const slot_size = slotSize(key_width, value_width);
|
||||||
|
|
||||||
const old_capacity = self.capacity();
|
const old_capacity = self.capacity();
|
||||||
const new_capacity = total_slots_at_level(new_level);
|
const new_capacity = totalCapacityAtLevel(new_level);
|
||||||
const delta_capacity = new_capacity - old_capacity;
|
const delta_capacity = new_capacity - old_capacity;
|
||||||
|
|
||||||
const data_bytes = new_capacity * slot_size;
|
const data_bytes = new_capacity * slot_size;
|
||||||
|
@ -155,8 +143,7 @@ pub const RocDict = extern struct {
|
||||||
|
|
||||||
// transfer the memory
|
// transfer the memory
|
||||||
|
|
||||||
if (old_capacity > 0) {
|
if (self.dict_bytes) |source_ptr| {
|
||||||
const source_ptr = self.dict_bytes orelse unreachable;
|
|
||||||
const dest_ptr = first_slot;
|
const dest_ptr = first_slot;
|
||||||
|
|
||||||
var source_offset: usize = 0;
|
var source_offset: usize = 0;
|
||||||
|
@ -208,10 +195,6 @@ pub const RocDict = extern struct {
|
||||||
return @ptrCast([*]u8, self.dict_bytes);
|
return @ptrCast([*]u8, self.dict_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains(self: RocDict, key_size: usize, key_ptr: *const c_void, hash_code: u64) bool {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(self: RocDict) usize {
|
pub fn len(self: RocDict) usize {
|
||||||
return self.dict_entries_len;
|
return self.dict_entries_len;
|
||||||
}
|
}
|
||||||
|
@ -221,17 +204,18 @@ pub const RocDict = extern struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isUnique(self: RocDict) bool {
|
pub fn isUnique(self: RocDict) bool {
|
||||||
|
// the empty dict is unique (in the sense that copying it will not leak memory)
|
||||||
if (self.isEmpty()) {
|
if (self.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// otherwise, check if the refcount is one
|
||||||
const ptr: [*]usize = @ptrCast([*]usize, @alignCast(8, self.dict_bytes));
|
const ptr: [*]usize = @ptrCast([*]usize, @alignCast(8, self.dict_bytes));
|
||||||
|
|
||||||
return (ptr - 1)[0] == REFCOUNT_ONE;
|
return (ptr - 1)[0] == REFCOUNT_ONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn capacity(self: RocDict) usize {
|
pub fn capacity(self: RocDict) usize {
|
||||||
return total_slots_at_level(self.number_of_levels);
|
return totalCapacityAtLevel(self.number_of_levels);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn makeUnique(self: RocDict, allocator: *Allocator, alignment: Alignment, key_width: usize, value_width: usize) RocDict {
|
pub fn makeUnique(self: RocDict, allocator: *Allocator, alignment: Alignment, key_width: usize, value_width: usize) RocDict {
|
||||||
|
@ -264,21 +248,15 @@ pub const RocDict = extern struct {
|
||||||
fn getSlot(self: *const RocDict, index: usize, key_width: usize, value_width: usize) Slot {
|
fn getSlot(self: *const RocDict, index: usize, key_width: usize, value_width: usize) Slot {
|
||||||
const offset = self.capacity() * (key_width + value_width) + index * @sizeOf(Slot);
|
const offset = self.capacity() * (key_width + value_width) + index * @sizeOf(Slot);
|
||||||
|
|
||||||
if (self.dict_bytes) |u8_ptr| {
|
const ptr = self.dict_bytes orelse unreachable;
|
||||||
return @intToEnum(Slot, u8_ptr[offset]);
|
return @intToEnum(Slot, ptr[offset]);
|
||||||
} else {
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setSlot(self: *RocDict, index: usize, key_width: usize, value_width: usize, slot: Slot) void {
|
fn setSlot(self: *RocDict, index: usize, key_width: usize, value_width: usize, slot: Slot) void {
|
||||||
const offset = self.capacity() * (key_width + value_width) + index * @sizeOf(Slot);
|
const offset = self.capacity() * (key_width + value_width) + index * @sizeOf(Slot);
|
||||||
|
|
||||||
if (self.dict_bytes) |u8_ptr| {
|
const ptr = self.dict_bytes orelse unreachable;
|
||||||
u8_ptr[offset] = @enumToInt(slot);
|
ptr[offset] = @enumToInt(slot);
|
||||||
} else {
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setKey(self: *RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize, data: Opaque) void {
|
fn setKey(self: *RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize, data: Opaque) void {
|
||||||
|
@ -290,12 +268,11 @@ pub const RocDict = extern struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (self.dict_bytes) |u8_ptr| {
|
const ptr = self.dict_bytes orelse unreachable;
|
||||||
const source = data orelse unreachable;
|
|
||||||
@memcpy(u8_ptr + offset, source, key_width);
|
const source = data orelse unreachable;
|
||||||
} else {
|
const dest = ptr + offset;
|
||||||
unreachable;
|
@memcpy(dest, source, key_width);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getKey(self: *const RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize) Opaque {
|
fn getKey(self: *const RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize) Opaque {
|
||||||
|
@ -307,11 +284,8 @@ pub const RocDict = extern struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (self.dict_bytes) |u8_ptr| {
|
const ptr = self.dict_bytes orelse unreachable;
|
||||||
return u8_ptr + offset;
|
return ptr + offset;
|
||||||
} else {
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setValue(self: *RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize, data: Opaque) void {
|
fn setValue(self: *RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize, data: Opaque) void {
|
||||||
|
@ -323,12 +297,11 @@ pub const RocDict = extern struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (self.dict_bytes) |u8_ptr| {
|
const ptr = self.dict_bytes orelse unreachable;
|
||||||
const source = data orelse unreachable;
|
|
||||||
@memcpy(u8_ptr + offset, source, key_width);
|
const source = data orelse unreachable;
|
||||||
} else {
|
const dest = ptr + offset;
|
||||||
unreachable;
|
@memcpy(dest, source, value_width);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getValue(self: *const RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize) Opaque {
|
fn getValue(self: *const RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize) Opaque {
|
||||||
|
@ -340,11 +313,8 @@ pub const RocDict = extern struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (self.dict_bytes) |u8_ptr| {
|
const ptr = self.dict_bytes orelse unreachable;
|
||||||
return u8_ptr + offset;
|
return ptr + offset;
|
||||||
} else {
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn findIndex(self: *const RocDict, alignment: Alignment, key: Opaque, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn) MaybeIndex {
|
fn findIndex(self: *const RocDict, alignment: Alignment, key: Opaque, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn) MaybeIndex {
|
||||||
|
|
|
@ -13,6 +13,8 @@ mod helpers;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod gen_dict {
|
mod gen_dict {
|
||||||
|
use roc_std::RocStr;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dict_empty_len() {
|
fn dict_empty_len() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
@ -156,7 +158,7 @@ mod gen_dict {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
myDict : Dict I64 I64
|
myDict : Dict I64 I64
|
||||||
myDict =
|
myDict =
|
||||||
Dict.empty
|
Dict.empty
|
||||||
|> Dict.insert 0 100
|
|> Dict.insert 0 100
|
||||||
|> Dict.insert 1 100
|
|> Dict.insert 1 100
|
||||||
|
@ -177,7 +179,7 @@ mod gen_dict {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
myDict : Dict I64 I64
|
myDict : Dict I64 I64
|
||||||
myDict =
|
myDict =
|
||||||
Dict.empty
|
Dict.empty
|
||||||
|> Dict.insert 0 100
|
|> Dict.insert 0 100
|
||||||
|> Dict.insert 1 200
|
|> Dict.insert 1 200
|
||||||
|
@ -198,9 +200,9 @@ mod gen_dict {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
myDict : Dict I64 I64
|
myDict : Dict I64 I64
|
||||||
myDict =
|
myDict =
|
||||||
[1,2,3]
|
[1,2,3]
|
||||||
|> List.walk (\value, accum -> Dict.insert accum value value) Dict.empty
|
|> List.walk (\value, accum -> Dict.insert accum value value) Dict.empty
|
||||||
|
|
||||||
Dict.values myDict
|
Dict.values myDict
|
||||||
"#
|
"#
|
||||||
|
@ -220,10 +222,10 @@ mod gen_dict {
|
||||||
accum
|
accum
|
||||||
|
|
||||||
myDict : Dict I64 I64
|
myDict : Dict I64 I64
|
||||||
myDict =
|
myDict =
|
||||||
# 25 elements (8 + 16 + 1) is guaranteed to overflow/reallocate at least twice
|
# 25 elements (8 + 16 + 1) is guaranteed to overflow/reallocate at least twice
|
||||||
range 0 25 []
|
range 0 25 []
|
||||||
|> List.walk (\value, accum -> Dict.insert accum value value) Dict.empty
|
|> List.walk (\value, accum -> Dict.insert accum value value) Dict.empty
|
||||||
|
|
||||||
Dict.values myDict
|
Dict.values myDict
|
||||||
|> List.len
|
|> List.len
|
||||||
|
@ -233,4 +235,78 @@ mod gen_dict {
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn small_str_keys() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
myDict : Dict Str I64
|
||||||
|
myDict =
|
||||||
|
Dict.empty
|
||||||
|
|> Dict.insert "a" 100
|
||||||
|
|> Dict.insert "b" 100
|
||||||
|
|> Dict.insert "c" 100
|
||||||
|
|
||||||
|
|
||||||
|
Dict.keys myDict
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
&[
|
||||||
|
RocStr::from_str("c"),
|
||||||
|
RocStr::from_str("a"),
|
||||||
|
RocStr::from_str("b"),
|
||||||
|
],
|
||||||
|
&[RocStr]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn big_str_keys() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
myDict : Dict Str I64
|
||||||
|
myDict =
|
||||||
|
Dict.empty
|
||||||
|
|> Dict.insert "Leverage agile frameworks to provide a robust" 100
|
||||||
|
|> Dict.insert "synopsis for high level overviews. Iterative approaches" 200
|
||||||
|
|> Dict.insert "to corporate strategy foster collaborative thinking to" 300
|
||||||
|
|
||||||
|
|
||||||
|
Dict.keys myDict
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
&[
|
||||||
|
RocStr::from_str("Leverage agile frameworks to provide a robust"),
|
||||||
|
RocStr::from_str("to corporate strategy foster collaborative thinking to"),
|
||||||
|
RocStr::from_str("synopsis for high level overviews. Iterative approaches"),
|
||||||
|
],
|
||||||
|
&[RocStr]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn big_str_values() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
myDict : Dict I64 Str
|
||||||
|
myDict =
|
||||||
|
Dict.empty
|
||||||
|
|> Dict.insert 100 "Leverage agile frameworks to provide a robust"
|
||||||
|
|> Dict.insert 200 "synopsis for high level overviews. Iterative approaches"
|
||||||
|
|> Dict.insert 300 "to corporate strategy foster collaborative thinking to"
|
||||||
|
|
||||||
|
Dict.values myDict
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
&[
|
||||||
|
RocStr::from_str("Leverage agile frameworks to provide a robust"),
|
||||||
|
RocStr::from_str("to corporate strategy foster collaborative thinking to"),
|
||||||
|
RocStr::from_str("synopsis for high level overviews. Iterative approaches"),
|
||||||
|
],
|
||||||
|
&[RocStr]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -401,6 +401,10 @@ impl RocStr {
|
||||||
Self::from_slice_with_capacity_str(slice, slice.len())
|
Self::from_slice_with_capacity_str(slice, slice.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_str(slice: &str) -> RocStr {
|
||||||
|
Self::from_slice_with_capacity_str(slice.as_bytes(), slice.len())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_slice(&self) -> &[u8] {
|
pub fn as_slice(&self) -> &[u8] {
|
||||||
if self.is_small_str() {
|
if self.is_small_str() {
|
||||||
unsafe { core::slice::from_raw_parts(self.get_small_str_ptr(), self.len()) }
|
unsafe { core::slice::from_raw_parts(self.get_small_str_ptr(), self.len()) }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue