zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit d92ae20f459c3ac491a0f5ef8aa42e8ac37634c8 (tree)
parent f1e5be96860406d7a4239b174c896799d8fd6545
Author: Andrew Kelley <superjoe30@gmail.com>
Date:   Mon,  9 May 2016 15:07:38 -0700

add hashmap to standard library

closes #22

Diffstat:
MCMakeLists.txt | 1+
Msrc/analyze.cpp | 23++++++++++++++++++-----
Msrc/codegen.cpp | 67++++++++++++++++++++++++++++++++++++++++++-------------------------
Mstd/hash_map.zig | 70+++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mstd/index.zig | 1+
Mstd/list.zig | 2+-
Mtest/run_tests.cpp | 6+++---
7 files changed, 115 insertions(+), 55 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -216,6 +216,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/linux_x86_64.zig" DESTINATION "${ZIG_STD_ install(FILES "${CMAKE_SOURCE_DIR}/std/linux_i386.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/mem.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/list.zig" DESTINATION "${ZIG_STD_DEST}") +install(FILES "${CMAKE_SOURCE_DIR}/std/hash_map.zig" DESTINATION "${ZIG_STD_DEST}") add_executable(run_tests ${TEST_SOURCES}) target_link_libraries(run_tests) diff --git a/src/analyze.cpp b/src/analyze.cpp @@ -4014,15 +4014,28 @@ static TypeTableEntry *analyze_if(CodeGen *g, ImportTableEntry *import, BlockCon else_context = parent_context; } - TypeTableEntry *then_type = analyze_expression(g, import, then_context, expected_type, *then_node); - TypeTableEntry *else_type = analyze_expression(g, import, else_context, expected_type, *else_node); + TypeTableEntry *then_type = nullptr; + TypeTableEntry *else_type = nullptr; - if (then_type->id == TypeTableEntryIdInvalid || else_type->id == TypeTableEntryIdInvalid) { - return g->builtin_types.entry_invalid; + if (!then_context->codegen_excluded) { + then_type = analyze_expression(g, import, then_context, expected_type, *then_node); + if (then_type->id == TypeTableEntryIdInvalid) { + return g->builtin_types.entry_invalid; + } + } + if (!else_context->codegen_excluded) { + else_type = analyze_expression(g, import, else_context, expected_type, *else_node); + if (else_type->id == TypeTableEntryIdInvalid) { + return g->builtin_types.entry_invalid; + } } TypeTableEntry *result_type; - if (expected_type) { + if (then_context->codegen_excluded) { + result_type = else_type; + } else if (else_context->codegen_excluded) { + result_type = then_type; + } else if (expected_type) { result_type = (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type; } else { AstNode *op_nodes[] = {*then_node, *else_node}; diff --git a/src/codegen.cpp b/src/codegen.cpp @@ -2501,6 +2501,33 @@ static void gen_var_debug_decl(CodeGen *g, VariableTableEntry *var) { LLVMGetInsertBlock(g->builder)); } +static LLVMValueRef gen_if_var_then_block(CodeGen *g, AstNode *node, VariableTableEntry *variable, bool maybe_is_ptr, + LLVMValueRef init_val, TypeTableEntry *child_type, AstNode *then_node) +{ + if (node->data.if_var_expr.var_is_ptr) { + LLVMValueRef payload_ptr; + if (maybe_is_ptr) { + zig_panic("TODO"); + } else { + payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, ""); + } + LLVMBuildStore(g->builder, payload_ptr, variable->value_ref); + } else { + LLVMValueRef payload_val; + if (maybe_is_ptr) { + payload_val = init_val; + } else { + LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, ""); + payload_val = get_handle_value(g, node, payload_ptr, child_type); + } + gen_assign_raw(g, node, BinOpTypeAssign, variable->value_ref, payload_val, + variable->type, child_type); + } + gen_var_debug_decl(g, variable); + + return gen_expr(g, then_node); +} + static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeIfVarExpr); assert(node->data.if_var_expr.var_decl.expr); @@ -2514,8 +2541,21 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { LLVMValueRef init_val = gen_expr(g, var_decl->expr); - LLVMValueRef cond_value; + + AstNode *then_node = node->data.if_var_expr.then_block; + AstNode *else_node = node->data.if_var_expr.else_node; bool maybe_is_ptr = child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn; + + ConstExprValue *const_val = &get_resolved_expr(var_decl->expr)->const_val; + if (const_val->ok) { + if (const_val->data.x_maybe) { + return gen_if_var_then_block(g, node, variable, maybe_is_ptr, init_val, child_type, then_node); + } else { + return gen_expr(g, else_node); + } + } + + LLVMValueRef cond_value; if (maybe_is_ptr) { set_debug_source_node(g, node); cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, init_val, LLVMConstNull(child_type->type_ref), ""); @@ -2525,9 +2565,6 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, ""); } - AstNode *then_node = node->data.if_var_expr.then_block; - AstNode *else_node = node->data.if_var_expr.else_node; - TypeTableEntry *then_type = get_expr_type(then_node); TypeTableEntry *else_type = get_expr_type(else_node); @@ -2548,28 +2585,8 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { LLVMBuildCondBr(g->builder, cond_value, then_block, else_block); LLVMPositionBuilderAtEnd(g->builder, then_block); - if (node->data.if_var_expr.var_is_ptr) { - LLVMValueRef payload_ptr; - if (maybe_is_ptr) { - zig_panic("TODO"); - } else { - payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, ""); - } - LLVMBuildStore(g->builder, payload_ptr, variable->value_ref); - } else { - LLVMValueRef payload_val; - if (maybe_is_ptr) { - payload_val = init_val; - } else { - LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, ""); - payload_val = get_handle_value(g, node, payload_ptr, child_type); - } - gen_assign_raw(g, node, BinOpTypeAssign, variable->value_ref, payload_val, - variable->type, child_type); - } - gen_var_debug_decl(g, variable); + LLVMValueRef then_expr_result = gen_if_var_then_block(g, node, variable, maybe_is_ptr, init_val, child_type, then_node); - LLVMValueRef then_expr_result = gen_expr(g, then_node); if (then_endif_reachable) { LLVMBuildBr(g->builder, endif_block); } diff --git a/std/hash_map.zig b/std/hash_map.zig @@ -4,17 +4,20 @@ const mem = @import("mem.zig"); const Allocator = mem.Allocator; const want_modification_safety = !@compile_var("is_release"); -const debug_u32 = if (want_modification_safety) void else u32; +const debug_u32 = if (want_modification_safety) u32 else void; -pub struct HashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)->bool) { +pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)->bool, STATIC_SIZE: isize) { entries: []Entry, size: isize, max_distance_from_start_index: isize, allocator: &Allocator, + // if the hash map is small enough, we use linear search through these + // entries instead of allocating memory + prealloc_entries: [STATIC_SIZE]Entry, // this is used to detect bugs where a hashtable is edited while an iterator is running. modification_count: debug_u32, - const Self = HashMap(K, V, hash, eql); + const Self = SmallHashMap(K, V, hash, eql, STATIC_SIZE); pub struct Entry { used: bool, @@ -49,14 +52,20 @@ pub struct HashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)- } } - pub fn init(hm: &Self, allocator: &Allocator, capacity: isize) { - assert(capacity > 0); + pub fn init(hm: &Self, allocator: &Allocator) { + hm.entries = hm.prealloc_entries[0...]; hm.allocator = allocator; - hm.init_capacity(capacity); + hm.size = 0; + hm.max_distance_from_start_index = 0; + for (hm.entries) |*entry| { + entry.used = false; + } } pub fn deinit(hm: &Self) { - hm.allocator.free(hm.allocator, ([]u8)(hm.entries)); + if (hm.entries.ptr != &hm.prealloc_entries[0]) { + hm.allocator.free(hm.allocator, ([]u8)(hm.entries)); + } } pub fn clear(hm: &Self) { @@ -68,26 +77,35 @@ pub struct HashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)- hm.increment_modification_count(); } - pub fn put(hm: &Self, key: K, value: V) { + pub fn put(hm: &Self, key: K, value: V) -> %void { hm.increment_modification_count(); - hm.internal_put(key, value); - // if we get too full (60%), double the capacity - if (hm.size * 5 >= hm.entries.len * 3) { + const resize = if (hm.entries.ptr == &hm.prealloc_entries[0]) { + // preallocated entries table is full + hm.size == hm.entries.len + } else { + // if we get too full (60%), double the capacity + hm.size * 5 >= hm.entries.len * 3 + }; + if (resize) { const old_entries = hm.entries; - hm.init_capacity(hm.entries.len * 2); + %return hm.init_capacity(hm.entries.len * 2); // dump all of the old elements into the new table for (old_entries) |*old_entry| { if (old_entry.used) { hm.internal_put(old_entry.key, old_entry.value); } } - hm.allocator.free(hm.allocator, ([]u8)(old_entries)); + if (old_entries.ptr != &hm.prealloc_entries[0]) { + hm.allocator.free(hm.allocator, ([]u8)(old_entries)); + } } + + hm.internal_put(key, value); } - pub fn get(hm: &Self, key: K) { - return internal_get(key); + pub fn get(hm: &Self, key: K) -> ?&Entry { + return hm.internal_get(key); } pub fn remove(hm: &Self, key: K) { @@ -95,7 +113,7 @@ pub struct HashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)- const start_index = hm.key_to_index(key); {var roll_over: isize = 0; while (roll_over <= hm.max_distance_from_start_index; roll_over += 1) { const index = (start_index + roll_over) % hm.entries.len; - const entry = &hm.entries[index]; + var entry = &hm.entries[index]; assert(entry.used); // key not found @@ -127,7 +145,7 @@ pub struct HashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)- }; } - fn init_capacity(hm: &Self, capacity: isize) { + fn init_capacity(hm: &Self, capacity: isize) -> %void { hm.entries = ([]Entry)(%return hm.allocator.alloc(hm.allocator, capacity * @sizeof(Entry))); hm.size = 0; hm.max_distance_from_start_index = 0; @@ -145,7 +163,7 @@ pub struct HashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)- fn internal_put(hm: &Self, orig_key: K, orig_value: V) { var key = orig_key; var value = orig_value; - const start_index = key_to_index(key); + const start_index = hm.key_to_index(key); var roll_over: isize = 0; var distance_from_start_index: isize = 0; while (roll_over < hm.entries.len; {roll_over += 1; distance_from_start_index += 1}) { @@ -190,7 +208,7 @@ pub struct HashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)- } fn internal_get(hm: &Self, key: K) -> ?&Entry { - const start_index = key_to_index(key); + const start_index = hm.key_to_index(key); {var roll_over: isize = 0; while (roll_over <= hm.max_distance_from_start_index; roll_over += 1) { const index = (start_index + roll_over) % hm.entries.len; const entry = &hm.entries[index]; @@ -233,9 +251,19 @@ fn global_free(self: &Allocator, old_mem: []u8) { #attribute("test") fn basic_hash_map_test() { - var map: HashMap(i32, i32, hash_i32, eql_i32) = undefined; - map.init(&global_allocator, 4); + var map: SmallHashMap(i32, i32, hash_i32, eql_i32, 4) = undefined; + map.init(&global_allocator); defer map.deinit(); + + %%map.put(1, 11); + %%map.put(2, 22); + %%map.put(3, 33); + %%map.put(4, 44); + %%map.put(5, 55); + + assert((??map.get(2)).value == 22); + map.remove(2); + assert(if (const entry ?= map.get(2)) false else true); } fn hash_i32(x: i32) -> u32 { diff --git a/std/index.zig b/std/index.zig @@ -6,6 +6,7 @@ pub const str = @import("str.zig"); pub const cstr = @import("cstr.zig"); pub const net = @import("net.zig"); pub const list = @import("list.zig"); +pub const hash_map = @import("hash_map.zig"); pub const mem = @import("mem.zig"); pub fn assert(b: bool) { diff --git a/std/list.zig b/std/list.zig @@ -21,7 +21,7 @@ pub struct SmallList(T: type, STATIC_SIZE: isize) { } pub fn deinit(l: &SmallList(T, STATIC_SIZE)) { - if (l.items.ptr == &l.prealloc_items[0]) { + if (l.items.ptr != &l.prealloc_items[0]) { l.allocator.free(l.allocator, ([]u8)(l.items)); } } diff --git a/test/run_tests.cpp b/test/run_tests.cpp @@ -831,9 +831,9 @@ fn f() { add_compile_fail_case("missing else clause", R"SOURCE( -fn f() { - const x : i32 = if (true) { 1 }; - const y = if (true) { i32(1) }; +fn f(b: bool) { + const x : i32 = if (b) { 1 }; + const y = if (b) { i32(1) }; } )SOURCE", 2, ".tmp_source.zig:3:21: error: expected type 'i32', got 'void'", ".tmp_source.zig:4:15: error: incompatible types: 'i32' and 'void'");