Resolved merge conflict.
This commit is contained in:
commit
255c0ef406
@ -415,6 +415,9 @@ set(ZIG_CPP_SOURCES
|
||||
|
||||
set(ZIG_STD_FILES
|
||||
"array_list.zig"
|
||||
"atomic/index.zig"
|
||||
"atomic/stack.zig"
|
||||
"atomic/queue.zig"
|
||||
"base64.zig"
|
||||
"buf_map.zig"
|
||||
"buf_set.zig"
|
||||
@ -498,6 +501,28 @@ set(ZIG_STD_FILES
|
||||
"math/tan.zig"
|
||||
"math/tanh.zig"
|
||||
"math/trunc.zig"
|
||||
"math/complex/abs.zig"
|
||||
"math/complex/acosh.zig"
|
||||
"math/complex/acos.zig"
|
||||
"math/complex/arg.zig"
|
||||
"math/complex/asinh.zig"
|
||||
"math/complex/asin.zig"
|
||||
"math/complex/atanh.zig"
|
||||
"math/complex/atan.zig"
|
||||
"math/complex/conj.zig"
|
||||
"math/complex/cosh.zig"
|
||||
"math/complex/cos.zig"
|
||||
"math/complex/exp.zig"
|
||||
"math/complex/index.zig"
|
||||
"math/complex/ldexp.zig"
|
||||
"math/complex/log.zig"
|
||||
"math/complex/pow.zig"
|
||||
"math/complex/proj.zig"
|
||||
"math/complex/sinh.zig"
|
||||
"math/complex/sin.zig"
|
||||
"math/complex/sqrt.zig"
|
||||
"math/complex/tanh.zig"
|
||||
"math/complex/tan.zig"
|
||||
"mem.zig"
|
||||
"net.zig"
|
||||
"os/child_process.zig"
|
||||
|
@ -1258,7 +1258,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou
|
||||
}
|
||||
|
||||
fn_type_id->param_count = fn_proto->params.length;
|
||||
fn_type_id->param_info = allocate_nonzero<FnTypeParamInfo>(param_count_alloc);
|
||||
fn_type_id->param_info = allocate<FnTypeParamInfo>(param_count_alloc);
|
||||
fn_type_id->next_param_index = 0;
|
||||
fn_type_id->is_var_args = fn_proto->is_var_args;
|
||||
}
|
||||
@ -6131,4 +6131,3 @@ bool type_can_fail(TypeTableEntry *type_entry) {
|
||||
bool fn_type_can_fail(FnTypeId *fn_type_id) {
|
||||
return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync;
|
||||
}
|
||||
|
||||
|
77
src/ir.cpp
77
src/ir.cpp
@ -3161,6 +3161,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
|
||||
if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) {
|
||||
return noreturn_return_value;
|
||||
}
|
||||
|
||||
ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
|
||||
return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
|
||||
} else {
|
||||
incoming_blocks.append(irb->current_basic_block);
|
||||
incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node)));
|
||||
@ -6187,16 +6190,10 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A
|
||||
buf_init_from_buf(&err_set_type->name, type_name);
|
||||
err_set_type->is_copyable = true;
|
||||
err_set_type->data.error_set.err_count = err_count;
|
||||
|
||||
if (err_count == 0) {
|
||||
err_set_type->zero_bits = true;
|
||||
err_set_type->di_type = irb->codegen->builtin_types.entry_void->di_type;
|
||||
} else {
|
||||
err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref;
|
||||
err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type;
|
||||
irb->codegen->error_di_types.append(&err_set_type->di_type);
|
||||
err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);
|
||||
}
|
||||
err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref;
|
||||
err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type;
|
||||
irb->codegen->error_di_types.append(&err_set_type->di_type);
|
||||
err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);
|
||||
|
||||
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count);
|
||||
|
||||
@ -8138,7 +8135,7 @@ static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *
|
||||
*errors = reallocate(*errors, old_errors_count, *errors_count);
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) {
|
||||
static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, TypeTableEntry *expected_type, IrInstruction **instructions, size_t instruction_count) {
|
||||
assert(instruction_count >= 1);
|
||||
IrInstruction *prev_inst = instructions[0];
|
||||
if (type_is_invalid(prev_inst->value.type)) {
|
||||
@ -8185,16 +8182,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prev_type->id == TypeTableEntryIdNullLit) {
|
||||
prev_inst = cur_inst;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur_type->id == TypeTableEntryIdNullLit) {
|
||||
any_are_null = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prev_type->id == TypeTableEntryIdErrorSet) {
|
||||
assert(err_set_type != nullptr);
|
||||
if (cur_type->id == TypeTableEntryIdErrorSet) {
|
||||
@ -8454,6 +8441,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
|
||||
}
|
||||
}
|
||||
|
||||
if (prev_type->id == TypeTableEntryIdNullLit) {
|
||||
prev_inst = cur_inst;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur_type->id == TypeTableEntryIdNullLit) {
|
||||
any_are_null = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) {
|
||||
continue;
|
||||
}
|
||||
@ -8637,6 +8634,10 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
|
||||
} else if (err_set_type != nullptr) {
|
||||
if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) {
|
||||
return err_set_type;
|
||||
} else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) {
|
||||
return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type);
|
||||
} else if (expected_type != nullptr && expected_type->id == TypeTableEntryIdErrorUnion) {
|
||||
return get_error_union_type(ira->codegen, err_set_type, expected_type->data.error_union.payload_type);
|
||||
} else {
|
||||
if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||
|
||||
prev_inst->value.type->id == TypeTableEntryIdNumLitFloat)
|
||||
@ -8648,8 +8649,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
|
||||
ir_add_error_node(ira, source_node,
|
||||
buf_sprintf("unable to make error union out of null literal"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
} else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) {
|
||||
return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type);
|
||||
} else {
|
||||
return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type);
|
||||
}
|
||||
@ -10672,7 +10671,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
|
||||
}
|
||||
|
||||
IrInstruction *instructions[] = {op1, op2};
|
||||
TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, instructions, 2);
|
||||
TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2);
|
||||
if (type_is_invalid(resolved_type))
|
||||
return resolved_type;
|
||||
type_ensure_zero_bits_known(ira->codegen, resolved_type);
|
||||
@ -11062,7 +11061,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
|
||||
IrInstruction *op1 = bin_op_instruction->op1->other;
|
||||
IrInstruction *op2 = bin_op_instruction->op2->other;
|
||||
IrInstruction *instructions[] = {op1, op2};
|
||||
TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2);
|
||||
TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, nullptr, instructions, 2);
|
||||
if (type_is_invalid(resolved_type))
|
||||
return resolved_type;
|
||||
IrBinOp op_id = bin_op_instruction->op_id;
|
||||
@ -13031,7 +13030,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
|
||||
return first_value->value.type;
|
||||
}
|
||||
|
||||
TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node,
|
||||
TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, nullptr,
|
||||
new_incoming_values.items, new_incoming_values.length);
|
||||
if (type_is_invalid(resolved_type))
|
||||
return resolved_type;
|
||||
@ -13928,6 +13927,15 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
|
||||
}
|
||||
} else if (child_type->id == TypeTableEntryIdFn) {
|
||||
if (buf_eql_str(field_name, "ReturnType")) {
|
||||
if (child_type->data.fn.fn_type_id.return_type == nullptr) {
|
||||
// Return type can only ever be null, if the function is generic
|
||||
assert(child_type->data.fn.is_generic);
|
||||
|
||||
ir_add_error(ira, &field_ptr_instruction->base,
|
||||
buf_sprintf("ReturnType has not been resolved because '%s' is generic", buf_ptr(&child_type->name)));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
bool ptr_is_const = true;
|
||||
bool ptr_is_volatile = false;
|
||||
return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
|
||||
@ -18816,6 +18824,16 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc
|
||||
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
|
||||
out_val->data.x_type = fn_type_id->param_info[arg_index].type;
|
||||
if (out_val->data.x_type == nullptr) {
|
||||
// Args are only unresolved if our function is generic.
|
||||
assert(fn_type->data.fn.is_generic);
|
||||
|
||||
ir_add_error(ira, arg_index_inst,
|
||||
buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic",
|
||||
arg_index, buf_ptr(&fn_type->name)));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
return ira->codegen->builtin_types.entry_type;
|
||||
}
|
||||
|
||||
@ -19121,6 +19139,11 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr
|
||||
} else {
|
||||
if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
if (ordering == AtomicOrderUnordered) {
|
||||
ir_add_error(ira, instruction->ordering,
|
||||
buf_sprintf("@atomicRmw atomic ordering must not be Unordered"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
|
||||
@ -19656,7 +19679,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
|
||||
} else if (ira->src_implicit_return_type_list.length == 0) {
|
||||
return codegen->builtin_types.entry_unreachable;
|
||||
} else {
|
||||
return ir_resolve_peer_types(ira, expected_type_source_node, ira->src_implicit_return_type_list.items,
|
||||
return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items,
|
||||
ira->src_implicit_return_type_list.length);
|
||||
}
|
||||
}
|
||||
|
7
std/atomic/index.zig
Normal file
7
std/atomic/index.zig
Normal file
@ -0,0 +1,7 @@
|
||||
pub const Stack = @import("stack.zig").Stack;
|
||||
pub const Queue = @import("queue.zig").Queue;
|
||||
|
||||
test "std.atomic" {
|
||||
_ = @import("stack.zig").Stack;
|
||||
_ = @import("queue.zig").Queue;
|
||||
}
|
120
std/atomic/queue.zig
Normal file
120
std/atomic/queue.zig
Normal file
@ -0,0 +1,120 @@
|
||||
const builtin = @import("builtin");
|
||||
const AtomicOrder = builtin.AtomicOrder;
|
||||
const AtomicRmwOp = builtin.AtomicRmwOp;
|
||||
|
||||
/// Many reader, many writer, non-allocating, thread-safe, lock-free
|
||||
pub fn Queue(comptime T: type) type {
|
||||
return struct {
|
||||
head: &Node,
|
||||
tail: &Node,
|
||||
root: Node,
|
||||
|
||||
pub const Self = this;
|
||||
|
||||
pub const Node = struct {
|
||||
next: ?&Node,
|
||||
data: T,
|
||||
};
|
||||
|
||||
// TODO: well defined copy elision: https://github.com/zig-lang/zig/issues/287
|
||||
pub fn init(self: &Self) void {
|
||||
self.root.next = null;
|
||||
self.head = &self.root;
|
||||
self.tail = &self.root;
|
||||
}
|
||||
|
||||
pub fn put(self: &Self, node: &Node) void {
|
||||
node.next = null;
|
||||
|
||||
const tail = @atomicRmw(&Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
|
||||
_ = @atomicRmw(?&Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
|
||||
}
|
||||
|
||||
pub fn get(self: &Self) ?&Node {
|
||||
var head = @atomicLoad(&Node, &self.head, AtomicOrder.Acquire);
|
||||
while (true) {
|
||||
const node = head.next ?? return null;
|
||||
head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? return node;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const Context = struct {
|
||||
allocator: &std.mem.Allocator,
|
||||
queue: &Queue(i32),
|
||||
put_sum: isize,
|
||||
get_sum: isize,
|
||||
get_count: usize,
|
||||
puts_done: u8, // TODO make this a bool
|
||||
};
|
||||
const puts_per_thread = 10000;
|
||||
const put_thread_count = 3;
|
||||
|
||||
test "std.atomic.queue" {
|
||||
var direct_allocator = std.heap.DirectAllocator.init();
|
||||
defer direct_allocator.deinit();
|
||||
|
||||
var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024);
|
||||
defer direct_allocator.allocator.free(plenty_of_memory);
|
||||
|
||||
var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
|
||||
var a = &fixed_buffer_allocator.allocator;
|
||||
|
||||
var queue: Queue(i32) = undefined;
|
||||
queue.init();
|
||||
var context = Context {
|
||||
.allocator = a,
|
||||
.queue = &queue,
|
||||
.put_sum = 0,
|
||||
.get_sum = 0,
|
||||
.puts_done = 0,
|
||||
.get_count = 0,
|
||||
};
|
||||
|
||||
var putters: [put_thread_count]&std.os.Thread = undefined;
|
||||
for (putters) |*t| {
|
||||
*t = try std.os.spawnThread(&context, startPuts);
|
||||
}
|
||||
var getters: [put_thread_count]&std.os.Thread = undefined;
|
||||
for (getters) |*t| {
|
||||
*t = try std.os.spawnThread(&context, startGets);
|
||||
}
|
||||
|
||||
for (putters) |t| t.wait();
|
||||
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||
for (getters) |t| t.wait();
|
||||
|
||||
std.debug.assert(context.put_sum == context.get_sum);
|
||||
std.debug.assert(context.get_count == puts_per_thread * put_thread_count);
|
||||
}
|
||||
|
||||
fn startPuts(ctx: &Context) u8 {
|
||||
var put_count: usize = puts_per_thread;
|
||||
var r = std.rand.DefaultPrng.init(0xdeadbeef);
|
||||
while (put_count != 0) : (put_count -= 1) {
|
||||
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
|
||||
const x = @bitCast(i32, r.random.scalar(u32));
|
||||
const node = ctx.allocator.create(Queue(i32).Node) catch unreachable;
|
||||
node.data = x;
|
||||
ctx.queue.put(node);
|
||||
_ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn startGets(ctx: &Context) u8 {
|
||||
while (true) {
|
||||
while (ctx.queue.get()) |node| {
|
||||
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
|
||||
_ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
|
||||
_ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
|
||||
}
|
||||
|
||||
if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
126
std/atomic/stack.zig
Normal file
126
std/atomic/stack.zig
Normal file
@ -0,0 +1,126 @@
|
||||
const builtin = @import("builtin");
|
||||
const AtomicOrder = builtin.AtomicOrder;
|
||||
|
||||
/// Many reader, many writer, non-allocating, thread-safe, lock-free
|
||||
pub fn Stack(comptime T: type) type {
|
||||
return struct {
|
||||
root: ?&Node,
|
||||
|
||||
pub const Self = this;
|
||||
|
||||
pub const Node = struct {
|
||||
next: ?&Node,
|
||||
data: T,
|
||||
};
|
||||
|
||||
pub fn init() Self {
|
||||
return Self {
|
||||
.root = null,
|
||||
};
|
||||
}
|
||||
|
||||
/// push operation, but only if you are the first item in the stack. if you did not succeed in
|
||||
/// being the first item in the stack, returns the other item that was there.
|
||||
pub fn pushFirst(self: &Self, node: &Node) ?&Node {
|
||||
node.next = null;
|
||||
return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst);
|
||||
}
|
||||
|
||||
pub fn push(self: &Self, node: &Node) void {
|
||||
var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst);
|
||||
while (true) {
|
||||
node.next = root;
|
||||
root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop(self: &Self) ?&Node {
|
||||
var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire);
|
||||
while (true) {
|
||||
root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isEmpty(self: &Self) bool {
|
||||
return @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst) == null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const Context = struct {
|
||||
allocator: &std.mem.Allocator,
|
||||
stack: &Stack(i32),
|
||||
put_sum: isize,
|
||||
get_sum: isize,
|
||||
get_count: usize,
|
||||
puts_done: u8, // TODO make this a bool
|
||||
};
|
||||
const puts_per_thread = 1000;
|
||||
const put_thread_count = 3;
|
||||
|
||||
test "std.atomic.stack" {
|
||||
var direct_allocator = std.heap.DirectAllocator.init();
|
||||
defer direct_allocator.deinit();
|
||||
|
||||
var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024);
|
||||
defer direct_allocator.allocator.free(plenty_of_memory);
|
||||
|
||||
var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
|
||||
var a = &fixed_buffer_allocator.allocator;
|
||||
|
||||
var stack = Stack(i32).init();
|
||||
var context = Context {
|
||||
.allocator = a,
|
||||
.stack = &stack,
|
||||
.put_sum = 0,
|
||||
.get_sum = 0,
|
||||
.puts_done = 0,
|
||||
.get_count = 0,
|
||||
};
|
||||
|
||||
var putters: [put_thread_count]&std.os.Thread = undefined;
|
||||
for (putters) |*t| {
|
||||
*t = try std.os.spawnThread(&context, startPuts);
|
||||
}
|
||||
var getters: [put_thread_count]&std.os.Thread = undefined;
|
||||
for (getters) |*t| {
|
||||
*t = try std.os.spawnThread(&context, startGets);
|
||||
}
|
||||
|
||||
for (putters) |t| t.wait();
|
||||
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||
for (getters) |t| t.wait();
|
||||
|
||||
std.debug.assert(context.put_sum == context.get_sum);
|
||||
std.debug.assert(context.get_count == puts_per_thread * put_thread_count);
|
||||
}
|
||||
|
||||
fn startPuts(ctx: &Context) u8 {
|
||||
var put_count: usize = puts_per_thread;
|
||||
var r = std.rand.DefaultPrng.init(0xdeadbeef);
|
||||
while (put_count != 0) : (put_count -= 1) {
|
||||
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
|
||||
const x = @bitCast(i32, r.random.scalar(u32));
|
||||
const node = ctx.allocator.create(Stack(i32).Node) catch unreachable;
|
||||
node.data = x;
|
||||
ctx.stack.push(node);
|
||||
_ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn startGets(ctx: &Context) u8 {
|
||||
while (true) {
|
||||
while (ctx.stack.pop()) |node| {
|
||||
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
|
||||
_ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
|
||||
_ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
|
||||
}
|
||||
|
||||
if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -81,3 +81,8 @@ pub const sockaddr = extern struct {
|
||||
};
|
||||
|
||||
pub const sa_family_t = u8;
|
||||
|
||||
pub const pthread_attr_t = extern struct {
|
||||
__sig: c_long,
|
||||
__opaque: [56]u8,
|
||||
};
|
||||
|
@ -53,3 +53,13 @@ pub extern "c" fn malloc(usize) ?&c_void;
|
||||
pub extern "c" fn realloc(&c_void, usize) ?&c_void;
|
||||
pub extern "c" fn free(&c_void) void;
|
||||
pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int;
|
||||
|
||||
pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t,
|
||||
noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void,
|
||||
noalias arg: ?&c_void) c_int;
|
||||
pub extern "pthread" fn pthread_attr_init(attr: &pthread_attr_t) c_int;
|
||||
pub extern "pthread" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int;
|
||||
pub extern "pthread" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int;
|
||||
pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int;
|
||||
|
||||
pub const pthread_t = &@OpaqueType();
|
||||
|
@ -3,3 +3,8 @@ pub use @import("../os/linux/errno.zig");
|
||||
pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int;
|
||||
extern "c" fn __errno_location() &c_int;
|
||||
pub const _errno = __errno_location;
|
||||
|
||||
pub const pthread_attr_t = extern struct {
|
||||
__size: [56]u8,
|
||||
__align: c_long,
|
||||
};
|
||||
|
@ -1,17 +1,13 @@
|
||||
// Modify the HashFunction variable to the one wanted to test.
|
||||
//
|
||||
// NOTE: The throughput measurement may be slightly lower than other measurements since we run
|
||||
// through our block alignment functions as well. Be aware when comparing against other tests.
|
||||
//
|
||||
// ```
|
||||
// zig build-exe --release-fast --library c throughput_test.zig
|
||||
// zig build-exe --release-fast throughput_test.zig
|
||||
// ./throughput_test
|
||||
// ```
|
||||
|
||||
const std = @import("std");
|
||||
const c = @cImport({
|
||||
@cInclude("time.h");
|
||||
});
|
||||
const time = std.os.time;
|
||||
const Timer = time.Timer;
|
||||
const HashFunction = @import("md5.zig").Md5;
|
||||
|
||||
const MiB = 1024 * 1024;
|
||||
@ -28,13 +24,14 @@ pub fn main() !void {
|
||||
var h = HashFunction.init();
|
||||
var offset: usize = 0;
|
||||
|
||||
const start = c.clock();
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (offset < BytesToHash) : (offset += block.len) {
|
||||
h.update(block[0..]);
|
||||
}
|
||||
const end = c.clock();
|
||||
const end = timer.read();
|
||||
|
||||
const elapsed_s = f64(end - start) / f64(c.CLOCKS_PER_SEC);
|
||||
const elapsed_s = f64(end - start) / time.ns_per_s;
|
||||
const throughput = u64(BytesToHash / elapsed_s);
|
||||
|
||||
try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB));
|
||||
|
@ -12,13 +12,79 @@ pub const FloatDecimal = struct {
|
||||
exp: i32,
|
||||
};
|
||||
|
||||
pub const RoundMode = enum {
|
||||
// Round only the fractional portion (e.g. 1234.23 has precision 2)
|
||||
Decimal,
|
||||
// Round the entire whole/fractional portion (e.g. 1.23423e3 has precision 5)
|
||||
Scientific,
|
||||
};
|
||||
|
||||
/// Round a FloatDecimal as returned by errol3 to the specified fractional precision.
|
||||
/// All digits after the specified precision should be considered invalid.
|
||||
pub fn roundToPrecision(float_decimal: &FloatDecimal, precision: usize, mode: RoundMode) void {
|
||||
// The round digit refers to the index which we should look at to determine
|
||||
// whether we need to round to match the specified precision.
|
||||
var round_digit: usize = 0;
|
||||
|
||||
switch (mode) {
|
||||
RoundMode.Decimal => {
|
||||
if (float_decimal.exp >= 0) {
|
||||
round_digit = precision + usize(float_decimal.exp);
|
||||
} else {
|
||||
// if a small negative exp, then adjust we need to offset by the number
|
||||
// of leading zeros that will occur.
|
||||
const min_exp_required = usize(-float_decimal.exp);
|
||||
if (precision > min_exp_required) {
|
||||
round_digit = precision - min_exp_required;
|
||||
}
|
||||
}
|
||||
},
|
||||
RoundMode.Scientific => {
|
||||
round_digit = 1 + precision;
|
||||
},
|
||||
}
|
||||
|
||||
// It suffices to look at just this digit. We don't round and propagate say 0.04999 to 0.05
|
||||
// first, and then to 0.1 in the case of a {.1} single precision.
|
||||
|
||||
// Find the digit which will signify the round point and start rounding backwards.
|
||||
if (round_digit < float_decimal.digits.len and float_decimal.digits[round_digit] - '0' >= 5) {
|
||||
assert(round_digit >= 0);
|
||||
|
||||
var i = round_digit;
|
||||
while (true) {
|
||||
if (i == 0) {
|
||||
// Rounded all the way past the start. This was of the form 9.999...
|
||||
// Slot the new digit in place and increase the exponent.
|
||||
float_decimal.exp += 1;
|
||||
|
||||
// Re-size the buffer to use the reserved leading byte.
|
||||
const one_before = @intToPtr(&u8, @ptrToInt(&float_decimal.digits[0]) - 1);
|
||||
float_decimal.digits = one_before[0..float_decimal.digits.len + 1];
|
||||
float_decimal.digits[0] = '1';
|
||||
return;
|
||||
}
|
||||
|
||||
i -= 1;
|
||||
|
||||
const new_value = (float_decimal.digits[i] - '0' + 1) % 10;
|
||||
float_decimal.digits[i] = new_value + '0';
|
||||
|
||||
// must continue rounding until non-9
|
||||
if (new_value != 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Corrected Errol3 double to ASCII conversion.
|
||||
pub fn errol3(value: f64, buffer: []u8) FloatDecimal {
|
||||
const bits = @bitCast(u64, value);
|
||||
const i = tableLowerBound(bits);
|
||||
if (i < enum3.len and enum3[i] == bits) {
|
||||
const data = enum3_data[i];
|
||||
const digits = buffer[0..data.str.len];
|
||||
const digits = buffer[1..data.str.len + 1];
|
||||
mem.copy(u8, digits, data.str);
|
||||
return FloatDecimal {
|
||||
.digits = digits,
|
||||
@ -98,7 +164,11 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
|
||||
}
|
||||
|
||||
// digit generation
|
||||
var buf_index: usize = 0;
|
||||
|
||||
// We generate digits starting at index 1. If rounding a buffer later then it may be
|
||||
// required to generate a preceeding digit in some cases (9.999) in which case we use
|
||||
// the 0-index for this extra digit.
|
||||
var buf_index: usize = 1;
|
||||
while (true) {
|
||||
var hdig = u8(math.floor(high.val));
|
||||
if ((high.val == f64(hdig)) and (high.off < 0))
|
||||
@ -128,7 +198,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
|
||||
buf_index += 1;
|
||||
|
||||
return FloatDecimal {
|
||||
.digits = buffer[0..buf_index],
|
||||
.digits = buffer[1..buf_index],
|
||||
.exp = exp,
|
||||
};
|
||||
}
|
||||
@ -189,6 +259,9 @@ fn gethi(in: f64) f64 {
|
||||
/// Normalize the number by factoring in the error.
|
||||
/// @hp: The float pair.
|
||||
fn hpNormalize(hp: &HP) void {
|
||||
// Required to avoid segfaults causing buffer overrun during errol3 digit output termination.
|
||||
@setFloatMode(this, @import("builtin").FloatMode.Strict);
|
||||
|
||||
const val = hp.val;
|
||||
|
||||
hp.val += hp.off;
|
||||
|
@ -4,7 +4,7 @@ const debug = std.debug;
|
||||
const assert = debug.assert;
|
||||
const mem = std.mem;
|
||||
const builtin = @import("builtin");
|
||||
const errol3 = @import("errol/index.zig").errol3;
|
||||
const errol = @import("errol/index.zig");
|
||||
|
||||
const max_int_digits = 65;
|
||||
|
||||
@ -22,6 +22,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
|
||||
IntegerWidth,
|
||||
Float,
|
||||
FloatWidth,
|
||||
FloatScientific,
|
||||
FloatScientificWidth,
|
||||
Character,
|
||||
Buf,
|
||||
BufWidth,
|
||||
@ -87,6 +89,9 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
|
||||
's' => {
|
||||
state = State.Buf;
|
||||
},
|
||||
'e' => {
|
||||
state = State.FloatScientific;
|
||||
},
|
||||
'.' => {
|
||||
state = State.Float;
|
||||
},
|
||||
@ -133,9 +138,33 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
|
||||
'0' ... '9' => {},
|
||||
else => @compileError("Unexpected character in format string: " ++ []u8{c}),
|
||||
},
|
||||
State.FloatScientific => switch (c) {
|
||||
'}' => {
|
||||
try formatFloatScientific(args[next_arg], null, context, Errors, output);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
},
|
||||
'0' ... '9' => {
|
||||
width_start = i;
|
||||
state = State.FloatScientificWidth;
|
||||
},
|
||||
else => @compileError("Unexpected character in format string: " ++ []u8{c}),
|
||||
},
|
||||
State.FloatScientificWidth => switch (c) {
|
||||
'}' => {
|
||||
width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
|
||||
try formatFloatScientific(args[next_arg], width, context, Errors, output);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
},
|
||||
'0' ... '9' => {},
|
||||
else => @compileError("Unexpected character in format string: " ++ []u8{c}),
|
||||
},
|
||||
State.Float => switch (c) {
|
||||
'}' => {
|
||||
try formatFloatDecimal(args[next_arg], 0, context, Errors, output);
|
||||
try formatFloatDecimal(args[next_arg], null, context, Errors, output);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
@ -199,7 +228,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@
|
||||
return formatInt(value, 10, false, 0, context, Errors, output);
|
||||
},
|
||||
builtin.TypeId.Float => {
|
||||
return formatFloat(value, context, Errors, output);
|
||||
return formatFloatScientific(value, null, context, Errors, output);
|
||||
},
|
||||
builtin.TypeId.Void => {
|
||||
return output(context, "void");
|
||||
@ -257,81 +286,237 @@ pub fn formatBuf(buf: []const u8, width: usize,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
|
||||
// Print a float in scientific notation to the specified precision. Null uses full precision.
|
||||
// It should be the case that every full precision, printed value can be re-parsed back to the
|
||||
// same type unambiguously.
|
||||
pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
|
||||
var x = f64(value);
|
||||
|
||||
// Errol doesn't handle these special cases.
|
||||
if (math.isNan(x)) {
|
||||
return output(context, "NaN");
|
||||
}
|
||||
if (math.signbit(x)) {
|
||||
try output(context, "-");
|
||||
x = -x;
|
||||
}
|
||||
|
||||
if (math.isNan(x)) {
|
||||
return output(context, "nan");
|
||||
}
|
||||
if (math.isPositiveInf(x)) {
|
||||
return output(context, "Infinity");
|
||||
return output(context, "inf");
|
||||
}
|
||||
if (x == 0.0) {
|
||||
return output(context, "0.0");
|
||||
try output(context, "0");
|
||||
|
||||
if (maybe_precision) |precision| {
|
||||
if (precision != 0) {
|
||||
try output(context, ".");
|
||||
var i: usize = 0;
|
||||
while (i < precision) : (i += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try output(context, ".0");
|
||||
}
|
||||
|
||||
try output(context, "e+00");
|
||||
return;
|
||||
}
|
||||
|
||||
var buffer: [32]u8 = undefined;
|
||||
const float_decimal = errol3(x, buffer[0..]);
|
||||
try output(context, float_decimal.digits[0..1]);
|
||||
try output(context, ".");
|
||||
if (float_decimal.digits.len > 1) {
|
||||
const num_digits = if (@typeOf(value) == f32)
|
||||
math.min(usize(9), float_decimal.digits.len)
|
||||
else
|
||||
float_decimal.digits.len;
|
||||
try output(context, float_decimal.digits[1 .. num_digits]);
|
||||
var float_decimal = errol.errol3(x, buffer[0..]);
|
||||
|
||||
if (maybe_precision) |precision| {
|
||||
errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);
|
||||
|
||||
try output(context, float_decimal.digits[0..1]);
|
||||
|
||||
// {e0} case prints no `.`
|
||||
if (precision != 0) {
|
||||
try output(context, ".");
|
||||
|
||||
var printed: usize = 0;
|
||||
if (float_decimal.digits.len > 1) {
|
||||
const num_digits = math.min(float_decimal.digits.len, precision + 1);
|
||||
try output(context, float_decimal.digits[1 .. num_digits]);
|
||||
printed += num_digits - 1;
|
||||
}
|
||||
|
||||
while (printed < precision) : (printed += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try output(context, "0");
|
||||
try output(context, float_decimal.digits[0..1]);
|
||||
try output(context, ".");
|
||||
if (float_decimal.digits.len > 1) {
|
||||
const num_digits = if (@typeOf(value) == f32)
|
||||
math.min(usize(9), float_decimal.digits.len)
|
||||
else
|
||||
float_decimal.digits.len;
|
||||
|
||||
try output(context, float_decimal.digits[1 .. num_digits]);
|
||||
} else {
|
||||
try output(context, "0");
|
||||
}
|
||||
}
|
||||
|
||||
if (float_decimal.exp != 1) {
|
||||
try output(context, "e");
|
||||
try formatInt(float_decimal.exp - 1, 10, false, 0, context, Errors, output);
|
||||
try output(context, "e");
|
||||
const exp = float_decimal.exp - 1;
|
||||
|
||||
if (exp >= 0) {
|
||||
try output(context, "+");
|
||||
if (exp > -10 and exp < 10) {
|
||||
try output(context, "0");
|
||||
}
|
||||
try formatInt(exp, 10, false, 0, context, Errors, output);
|
||||
} else {
|
||||
try output(context, "-");
|
||||
if (exp > -10 and exp < 10) {
|
||||
try output(context, "0");
|
||||
}
|
||||
try formatInt(-exp, 10, false, 0, context, Errors, output);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn formatFloatDecimal(value: var, precision: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
|
||||
// Print a float of the format x.yyyyy where the number of y is specified by the precision argument.
|
||||
// By default floats are printed at full precision (no rounding).
|
||||
pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
|
||||
var x = f64(value);
|
||||
|
||||
// Errol doesn't handle these special cases.
|
||||
if (math.isNan(x)) {
|
||||
return output(context, "NaN");
|
||||
}
|
||||
if (math.signbit(x)) {
|
||||
try output(context, "-");
|
||||
x = -x;
|
||||
}
|
||||
|
||||
if (math.isNan(x)) {
|
||||
return output(context, "nan");
|
||||
}
|
||||
if (math.isPositiveInf(x)) {
|
||||
return output(context, "Infinity");
|
||||
return output(context, "inf");
|
||||
}
|
||||
if (x == 0.0) {
|
||||
return output(context, "0.0");
|
||||
try output(context, "0");
|
||||
|
||||
if (maybe_precision) |precision| {
|
||||
if (precision != 0) {
|
||||
try output(context, ".");
|
||||
var i: usize = 0;
|
||||
while (i < precision) : (i += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
} else {
|
||||
try output(context, ".0");
|
||||
}
|
||||
} else {
|
||||
try output(context, "0");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// non-special case, use errol3
|
||||
var buffer: [32]u8 = undefined;
|
||||
const float_decimal = errol3(x, buffer[0..]);
|
||||
var float_decimal = errol.errol3(x, buffer[0..]);
|
||||
|
||||
const num_left_digits = if (float_decimal.exp > 0) usize(float_decimal.exp) else 1;
|
||||
if (maybe_precision) |precision| {
|
||||
errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal);
|
||||
|
||||
try output(context, float_decimal.digits[0 .. num_left_digits]);
|
||||
try output(context, ".");
|
||||
if (float_decimal.digits.len > 1) {
|
||||
const num_valid_digtis = if (@typeOf(value) == f32) math.min(usize(7), float_decimal.digits.len)
|
||||
else
|
||||
float_decimal.digits.len;
|
||||
// exp < 0 means the leading is always 0 as errol result is normalized.
|
||||
var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0;
|
||||
|
||||
const num_right_digits = if (precision != 0)
|
||||
math.min(precision, (num_valid_digtis-num_left_digits))
|
||||
else
|
||||
num_valid_digtis - num_left_digits;
|
||||
try output(context, float_decimal.digits[num_left_digits .. (num_left_digits + num_right_digits)]);
|
||||
// the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this.
|
||||
var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len);
|
||||
|
||||
if (num_digits_whole > 0) {
|
||||
// We may have to zero pad, for instance 1e4 requires zero padding.
|
||||
try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]);
|
||||
|
||||
var i = num_digits_whole_no_pad;
|
||||
while (i < num_digits_whole) : (i += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
} else {
|
||||
try output(context , "0");
|
||||
}
|
||||
|
||||
// {.0} special case doesn't want a trailing '.'
|
||||
if (precision == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try output(context, ".");
|
||||
|
||||
// Keep track of fractional count printed for case where we pre-pad then post-pad with 0's.
|
||||
var printed: usize = 0;
|
||||
|
||||
// Zero-fill until we reach significant digits or run out of precision.
|
||||
if (float_decimal.exp <= 0) {
|
||||
const zero_digit_count = usize(-float_decimal.exp);
|
||||
const zeros_to_print = math.min(zero_digit_count, precision);
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < zeros_to_print) : (i += 1) {
|
||||
try output(context, "0");
|
||||
printed += 1;
|
||||
}
|
||||
|
||||
if (printed >= precision) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Remaining fractional portion, zero-padding if insufficient.
|
||||
debug.assert(precision >= printed);
|
||||
if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) {
|
||||
try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]);
|
||||
return;
|
||||
} else {
|
||||
try output(context, float_decimal.digits[num_digits_whole_no_pad ..]);
|
||||
printed += float_decimal.digits.len - num_digits_whole_no_pad;
|
||||
|
||||
while (printed < precision) : (printed += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try output(context, "0");
|
||||
// exp < 0 means the leading is always 0 as errol result is normalized.
|
||||
var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0;
|
||||
|
||||
// the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this.
|
||||
var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len);
|
||||
|
||||
if (num_digits_whole > 0) {
|
||||
// We may have to zero pad, for instance 1e4 requires zero padding.
|
||||
try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]);
|
||||
|
||||
var i = num_digits_whole_no_pad;
|
||||
while (i < num_digits_whole) : (i += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
} else {
|
||||
try output(context , "0");
|
||||
}
|
||||
|
||||
// Omit `.` if no fractional portion
|
||||
if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) {
|
||||
return;
|
||||
}
|
||||
|
||||
try output(context, ".");
|
||||
|
||||
// Zero-fill until we reach significant digits or run out of precision.
|
||||
if (float_decimal.exp < 0) {
|
||||
const zero_digit_count = usize(-float_decimal.exp);
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < zero_digit_count) : (i += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
}
|
||||
|
||||
try output(context, float_decimal.digits[num_digits_whole_no_pad ..]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -594,70 +779,210 @@ test "fmt.format" {
|
||||
const result = try bufPrint(buf1[0..], "pointer: {}\n", &value);
|
||||
assert(mem.startsWith(u8, result, "pointer: Struct@"));
|
||||
}
|
||||
|
||||
// TODO get these tests passing in release modes
|
||||
// https://github.com/zig-lang/zig/issues/564
|
||||
if (builtin.mode == builtin.Mode.Debug) {
|
||||
{
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 1.34;
|
||||
const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1.34000003e+00\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 12.34;
|
||||
const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1.23400001e+01\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = -12.34e10;
|
||||
const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: -1.234e+11\n"));
|
||||
}
|
||||
{
|
||||
// This fails on release due to a minor rounding difference.
|
||||
// --release-fast outputs 9.999960000000001e-40 vs. the expected.
|
||||
if (builtin.mode == builtin.Mode.Debug) {
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 12.34;
|
||||
const result = try bufPrint(buf1[0..], "f32: {}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1.23400001e1\n"));
|
||||
const value: f64 = 9.999960e-40;
|
||||
const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 9.99996e-40\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = -12.34e10;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: -1.234e11\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
|
||||
assert(mem.eql(u8, result, "f64: NaN\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
|
||||
assert(mem.eql(u8, result, "f64: Infinity\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
|
||||
assert(mem.eql(u8, result, "f64: -Infinity\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 1.1234;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.1}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1.1\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 1234.567;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1234.56\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = -11.1234;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);
|
||||
// -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
|
||||
// -11.12339... is truncated to -11.1233
|
||||
assert(mem.eql(u8, result, "f32: -11.1233\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 91.12345;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 91.12345\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 91.12345678901235;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 91.1234567890\n"));
|
||||
}
|
||||
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 1.409706e-42;
|
||||
const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 1.40971e-42\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = @bitCast(f32, u32(814313563));
|
||||
const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 1.00000e-09\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = @bitCast(f32, u32(1006632960));
|
||||
const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 7.81250e-03\n"));
|
||||
}
|
||||
{
|
||||
// libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05.
|
||||
// In fact, libc doesn't round a lot of 5 cases up when one past the precision point.
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = @bitCast(f32, u32(1203982400));
|
||||
const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 1.00001e+05\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
|
||||
assert(mem.eql(u8, result, "f64: nan\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64);
|
||||
assert(mem.eql(u8, result, "f64: -nan\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
|
||||
assert(mem.eql(u8, result, "f64: inf\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
|
||||
assert(mem.eql(u8, result, "f64: -inf\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [64]u8 = undefined;
|
||||
const value: f64 = 1.52314e+29;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 1.1234;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.1}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1.1\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 1234.567;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1234.57\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = -11.1234;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);
|
||||
// -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
|
||||
// -11.12339... is rounded back up to -11.1234
|
||||
assert(mem.eql(u8, result, "f32: -11.1234\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 91.12345;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 91.12345\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 91.12345678901235;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 91.1234567890\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 0.0;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.00000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 5.700;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.0}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 6\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 9.999;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.1}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 10.0\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 1.0;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.3}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 1.000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 0.0003;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.8}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.00030000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 1.40130e-45;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.00000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 9.999960e-40;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.00000\n"));
|
||||
}
|
||||
// libc checks
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = f64(@bitCast(f32, u32(916964781)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.00001\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = f64(@bitCast(f32, u32(925353389)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.00001\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = f64(@bitCast(f32, u32(1036831278)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.10000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = f64(@bitCast(f32, u32(1065353133)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 1.00000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = f64(@bitCast(f32, u32(1092616192)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 10.00000\n"));
|
||||
}
|
||||
// libc differences
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
// This is 0.015625 exactly according to gdb. We thus round down,
|
||||
// however glibc rounds up for some reason. This occurs for all
|
||||
// floats of the form x.yyyy25 on a precision point.
|
||||
const value: f64 = f64(@bitCast(f32, u32(1015021568)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.01563\n"));
|
||||
}
|
||||
// std-windows-x86_64-Debug-bare test case fails
|
||||
{
|
||||
// errol3 rounds to ... 630 but libc rounds to ...632. Grisu3
|
||||
// also rounds to 630 so I'm inclined to believe libc is not
|
||||
// optimal here.
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = f64(@bitCast(f32, u32(1518338049)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
70
std/heap.zig
70
std/heap.zig
@ -47,13 +47,6 @@ pub const DirectAllocator = struct {
|
||||
|
||||
const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void;
|
||||
|
||||
//pub const canary_bytes = []u8 {48, 239, 128, 46, 18, 49, 147, 9, 195, 59, 203, 3, 245, 54, 9, 122};
|
||||
//pub const want_safety = switch (builtin.mode) {
|
||||
// builtin.Mode.Debug => true,
|
||||
// builtin.Mode.ReleaseSafe => true,
|
||||
// else => false,
|
||||
//};
|
||||
|
||||
pub fn init() DirectAllocator {
|
||||
return DirectAllocator {
|
||||
.allocator = Allocator {
|
||||
@ -98,7 +91,7 @@ pub const DirectAllocator = struct {
|
||||
const unused_start = addr;
|
||||
const unused_len = aligned_addr - 1 - unused_start;
|
||||
|
||||
var err = p.munmap(@intToPtr(&u8, unused_start), unused_len);
|
||||
var err = p.munmap(unused_start, unused_len);
|
||||
debug.assert(p.getErrno(err) == 0);
|
||||
|
||||
//It is impossible that there is an unoccupied page at the top of our
|
||||
@ -139,7 +132,7 @@ pub const DirectAllocator = struct {
|
||||
const rem = @rem(new_addr_end, os.page_size);
|
||||
const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
|
||||
if (old_addr_end > new_addr_end_rounded) {
|
||||
_ = os.posix.munmap(@intToPtr(&u8, new_addr_end_rounded), old_addr_end - new_addr_end_rounded);
|
||||
_ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
|
||||
}
|
||||
return old_mem[0..new_size];
|
||||
}
|
||||
@ -177,7 +170,7 @@ pub const DirectAllocator = struct {
|
||||
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios => {
|
||||
_ = os.posix.munmap(bytes.ptr, bytes.len);
|
||||
_ = os.posix.munmap(@ptrToInt(bytes.ptr), bytes.len);
|
||||
},
|
||||
Os.windows => {
|
||||
const record_addr = @ptrToInt(bytes.ptr) + bytes.len;
|
||||
@ -298,7 +291,7 @@ pub const FixedBufferAllocator = struct {
|
||||
|
||||
fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
|
||||
const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
|
||||
const addr = @ptrToInt(&self.buffer[self.end_index]);
|
||||
const addr = @ptrToInt(self.buffer.ptr) + self.end_index;
|
||||
const rem = @rem(addr, alignment);
|
||||
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
|
||||
const adjusted_index = self.end_index + march_forward_bytes;
|
||||
@ -325,6 +318,54 @@ pub const FixedBufferAllocator = struct {
|
||||
fn free(allocator: &Allocator, bytes: []u8) void { }
|
||||
};
|
||||
|
||||
/// lock free
|
||||
pub const ThreadSafeFixedBufferAllocator = struct {
|
||||
allocator: Allocator,
|
||||
end_index: usize,
|
||||
buffer: []u8,
|
||||
|
||||
pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator {
|
||||
return ThreadSafeFixedBufferAllocator {
|
||||
.allocator = Allocator {
|
||||
.allocFn = alloc,
|
||||
.reallocFn = realloc,
|
||||
.freeFn = free,
|
||||
},
|
||||
.buffer = buffer,
|
||||
.end_index = 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
|
||||
const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);
|
||||
var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);
|
||||
while (true) {
|
||||
const addr = @ptrToInt(self.buffer.ptr) + end_index;
|
||||
const rem = @rem(addr, alignment);
|
||||
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
|
||||
const adjusted_index = end_index + march_forward_bytes;
|
||||
const new_end_index = adjusted_index + n;
|
||||
if (new_end_index > self.buffer.len) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index,
|
||||
builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index .. new_end_index];
|
||||
}
|
||||
}
|
||||
|
||||
fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
|
||||
if (new_size <= old_mem.len) {
|
||||
return old_mem[0..new_size];
|
||||
} else {
|
||||
const result = try alloc(allocator, new_size, alignment);
|
||||
mem.copy(u8, result, old_mem);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
fn free(allocator: &Allocator, bytes: []u8) void { }
|
||||
};
|
||||
|
||||
|
||||
|
||||
test "c_allocator" {
|
||||
@ -363,6 +404,13 @@ test "FixedBufferAllocator" {
|
||||
try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
|
||||
}
|
||||
|
||||
test "ThreadSafeFixedBufferAllocator" {
|
||||
var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
|
||||
|
||||
try testAllocator(&fixed_buffer_allocator.allocator);
|
||||
try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
|
||||
}
|
||||
|
||||
fn testAllocator(allocator: &mem.Allocator) !void {
|
||||
var slice = try allocator.alloc(&i32, 100);
|
||||
|
||||
|
@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap;
|
||||
pub const LinkedList = @import("linked_list.zig").LinkedList;
|
||||
pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
|
||||
|
||||
pub const atomic = @import("atomic/index.zig");
|
||||
pub const base64 = @import("base64.zig");
|
||||
pub const build = @import("build.zig");
|
||||
pub const c = @import("c/index.zig");
|
||||
@ -34,6 +35,7 @@ pub const zig = @import("zig/index.zig");
|
||||
|
||||
test "std" {
|
||||
// run tests from these
|
||||
_ = @import("atomic/index.zig");
|
||||
_ = @import("array_list.zig");
|
||||
_ = @import("buf_map.zig");
|
||||
_ = @import("buf_set.zig");
|
||||
|
18
std/math/complex/abs.zig
Normal file
18
std/math/complex/abs.zig
Normal file
@ -0,0 +1,18 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn abs(z: var) @typeOf(z.re) {
|
||||
const T = @typeOf(z.re);
|
||||
return math.hypot(T, z.re, z.im);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.cabs" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = abs(a);
|
||||
debug.assert(math.approxEq(f32, c, 5.83095, epsilon));
|
||||
}
|
21
std/math/complex/acos.zig
Normal file
21
std/math/complex/acos.zig
Normal file
@ -0,0 +1,21 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn acos(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const q = cmath.asin(z);
|
||||
return Complex(T).new(T(math.pi) / 2 - q.re, -q.im);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.cacos" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = acos(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 0.546975, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, -2.452914, epsilon));
|
||||
}
|
21
std/math/complex/acosh.zig
Normal file
21
std/math/complex/acosh.zig
Normal file
@ -0,0 +1,21 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn acosh(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const q = cmath.acos(z);
|
||||
return Complex(T).new(-q.im, q.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.cacosh" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = acosh(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 2.452914, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 0.546975, epsilon));
|
||||
}
|
18
std/math/complex/arg.zig
Normal file
18
std/math/complex/arg.zig
Normal file
@ -0,0 +1,18 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn arg(z: var) @typeOf(z.re) {
|
||||
const T = @typeOf(z.re);
|
||||
return math.atan2(T, z.im, z.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.carg" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = arg(a);
|
||||
debug.assert(math.approxEq(f32, c, 0.540420, epsilon));
|
||||
}
|
27
std/math/complex/asin.zig
Normal file
27
std/math/complex/asin.zig
Normal file
@ -0,0 +1,27 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn asin(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const p = Complex(T).new(1.0 - (x - y) * (x + y), -2.0 * x * y);
|
||||
const q = Complex(T).new(-y, x);
|
||||
const r = cmath.log(q.add(cmath.sqrt(p)));
|
||||
|
||||
return Complex(T).new(r.im, -r.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.casin" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = asin(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 1.023822, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 2.452914, epsilon));
|
||||
}
|
22
std/math/complex/asinh.zig
Normal file
22
std/math/complex/asinh.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn asinh(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const q = Complex(T).new(-z.im, z.re);
|
||||
const r = cmath.asin(q);
|
||||
return Complex(T).new(r.im, -r.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.casinh" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = asinh(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 2.459831, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 0.533999, epsilon));
|
||||
}
|
130
std/math/complex/atan.zig
Normal file
130
std/math/complex/atan.zig
Normal file
@ -0,0 +1,130 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn atan(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
return switch (T) {
|
||||
f32 => atan32(z),
|
||||
f64 => atan64(z),
|
||||
else => @compileError("atan not implemented for " ++ @typeName(z)),
|
||||
};
|
||||
}
|
||||
|
||||
fn redupif32(x: f32) f32 {
|
||||
const DP1 = 3.140625;
|
||||
const DP2 = 9.67502593994140625e-4;
|
||||
const DP3 = 1.509957990978376432e-7;
|
||||
|
||||
var t = x / math.pi;
|
||||
if (t >= 0.0) {
|
||||
t += 0.5;
|
||||
} else {
|
||||
t -= 0.5;
|
||||
}
|
||||
|
||||
const u = f32(i32(t));
|
||||
return ((x - u * DP1) - u * DP2) - t * DP3;
|
||||
}
|
||||
|
||||
fn atan32(z: &const Complex(f32)) Complex(f32) {
|
||||
const maxnum = 1.0e38;
|
||||
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
if ((x == 0.0) and (y > 1.0)) {
|
||||
// overflow
|
||||
return Complex(f32).new(maxnum, maxnum);
|
||||
}
|
||||
|
||||
const x2 = x * x;
|
||||
var a = 1.0 - x2 - (y * y);
|
||||
if (a == 0.0) {
|
||||
// overflow
|
||||
return Complex(f32).new(maxnum, maxnum);
|
||||
}
|
||||
|
||||
var t = 0.5 * math.atan2(f32, 2.0 * x, a);
|
||||
var w = redupif32(t);
|
||||
|
||||
t = y - 1.0;
|
||||
a = x2 + t * t;
|
||||
if (a == 0.0) {
|
||||
// overflow
|
||||
return Complex(f32).new(maxnum, maxnum);
|
||||
}
|
||||
|
||||
t = y + 1.0;
|
||||
a = (x2 + (t * t)) / a;
|
||||
return Complex(f32).new(w, 0.25 * math.ln(a));
|
||||
}
|
||||
|
||||
fn redupif64(x: f64) f64 {
|
||||
const DP1 = 3.14159265160560607910;
|
||||
const DP2 = 1.98418714791870343106e-9;
|
||||
const DP3 = 1.14423774522196636802e-17;
|
||||
|
||||
var t = x / math.pi;
|
||||
if (t >= 0.0) {
|
||||
t += 0.5;
|
||||
} else {
|
||||
t -= 0.5;
|
||||
}
|
||||
|
||||
const u = f64(i64(t));
|
||||
return ((x - u * DP1) - u * DP2) - t * DP3;
|
||||
}
|
||||
|
||||
fn atan64(z: &const Complex(f64)) Complex(f64) {
|
||||
const maxnum = 1.0e308;
|
||||
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
if ((x == 0.0) and (y > 1.0)) {
|
||||
// overflow
|
||||
return Complex(f64).new(maxnum, maxnum);
|
||||
}
|
||||
|
||||
const x2 = x * x;
|
||||
var a = 1.0 - x2 - (y * y);
|
||||
if (a == 0.0) {
|
||||
// overflow
|
||||
return Complex(f64).new(maxnum, maxnum);
|
||||
}
|
||||
|
||||
var t = 0.5 * math.atan2(f64, 2.0 * x, a);
|
||||
var w = redupif64(t);
|
||||
|
||||
t = y - 1.0;
|
||||
a = x2 + t * t;
|
||||
if (a == 0.0) {
|
||||
// overflow
|
||||
return Complex(f64).new(maxnum, maxnum);
|
||||
}
|
||||
|
||||
t = y + 1.0;
|
||||
a = (x2 + (t * t)) / a;
|
||||
return Complex(f64).new(w, 0.25 * math.ln(a));
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.catan32" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = atan(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 1.423679, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 0.086569, epsilon));
|
||||
}
|
||||
|
||||
test "complex.catan64" {
|
||||
const a = Complex(f64).new(5, 3);
|
||||
const c = atan(a);
|
||||
|
||||
debug.assert(math.approxEq(f64, c.re, 1.423679, epsilon));
|
||||
debug.assert(math.approxEq(f64, c.im, 0.086569, epsilon));
|
||||
}
|
22
std/math/complex/atanh.zig
Normal file
22
std/math/complex/atanh.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn atanh(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const q = Complex(T).new(-z.im, z.re);
|
||||
const r = cmath.atan(q);
|
||||
return Complex(T).new(r.im, -r.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.catanh" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = atanh(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 0.146947, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 1.480870, epsilon));
|
||||
}
|
17
std/math/complex/conj.zig
Normal file
17
std/math/complex/conj.zig
Normal file
@ -0,0 +1,17 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn conj(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
return Complex(T).new(z.re, -z.im);
|
||||
}
|
||||
|
||||
test "complex.conj" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = a.conjugate();
|
||||
|
||||
debug.assert(c.re == 5 and c.im == -3);
|
||||
}
|
21
std/math/complex/cos.zig
Normal file
21
std/math/complex/cos.zig
Normal file
@ -0,0 +1,21 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn cos(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const p = Complex(T).new(-z.im, z.re);
|
||||
return cmath.cosh(p);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.ccos" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = cos(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 2.855815, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 9.606383, epsilon));
|
||||
}
|
165
std/math/complex/cosh.zig
Normal file
165
std/math/complex/cosh.zig
Normal file
@ -0,0 +1,165 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
|
||||
|
||||
pub fn cosh(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
return switch (T) {
|
||||
f32 => cosh32(z),
|
||||
f64 => cosh64(z),
|
||||
else => @compileError("cosh not implemented for " ++ @typeName(z)),
|
||||
};
|
||||
}
|
||||
|
||||
fn cosh32(z: &const Complex(f32)) Complex(f32) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const hx = @bitCast(u32, x);
|
||||
const ix = hx & 0x7fffffff;
|
||||
|
||||
const hy = @bitCast(u32, y);
|
||||
const iy = hy & 0x7fffffff;
|
||||
|
||||
if (ix < 0x7f800000 and iy < 0x7f800000) {
|
||||
if (iy == 0) {
|
||||
return Complex(f32).new(math.cosh(x), y);
|
||||
}
|
||||
// small x: normal case
|
||||
if (ix < 0x41100000) {
|
||||
return Complex(f32).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y));
|
||||
}
|
||||
|
||||
// |x|>= 9, so cosh(x) ~= exp(|x|)
|
||||
if (ix < 0x42b17218) {
|
||||
// x < 88.7: exp(|x|) won't overflow
|
||||
const h = math.exp(math.fabs(x)) * 0.5;
|
||||
return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));
|
||||
}
|
||||
// x < 192.7: scale to avoid overflow
|
||||
else if (ix < 0x4340b1e7) {
|
||||
const v = Complex(f32).new(math.fabs(x), y);
|
||||
const r = ldexp_cexp(v, -1);
|
||||
return Complex(f32).new(x, y * math.copysign(f32, 1, x));
|
||||
}
|
||||
// x >= 192.7: result always overflows
|
||||
else {
|
||||
const h = 0x1p127 * x;
|
||||
return Complex(f32).new(h * h * math.cos(y), h * math.sin(y));
|
||||
}
|
||||
}
|
||||
|
||||
if (ix == 0 and iy >= 0x7f800000) {
|
||||
return Complex(f32).new(y - y, math.copysign(f32, 0, x * (y - y)));
|
||||
}
|
||||
|
||||
if (iy == 0 and ix >= 0x7f800000) {
|
||||
if (hx & 0x7fffff == 0) {
|
||||
return Complex(f32).new(x * x, math.copysign(f32, 0, x) * y);
|
||||
}
|
||||
return Complex(f32).new(x, math.copysign(f32, 0, (x + x) * y));
|
||||
}
|
||||
|
||||
if (ix < 0x7f800000 and iy >= 0x7f800000) {
|
||||
return Complex(f32).new(y - y, x * (y - y));
|
||||
}
|
||||
|
||||
if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) {
|
||||
if (iy >= 0x7f800000) {
|
||||
return Complex(f32).new(x * x, x * (y - y));
|
||||
}
|
||||
return Complex(f32).new((x * x) * math.cos(y), x * math.sin(y));
|
||||
}
|
||||
|
||||
return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y));
|
||||
}
|
||||
|
||||
fn cosh64(z: &const Complex(f64)) Complex(f64) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const fx = @bitCast(u64, x);
|
||||
const hx = u32(fx >> 32);
|
||||
const lx = @truncate(u32, fx);
|
||||
const ix = hx & 0x7fffffff;
|
||||
|
||||
const fy = @bitCast(u64, y);
|
||||
const hy = u32(fy >> 32);
|
||||
const ly = @truncate(u32, fy);
|
||||
const iy = hy & 0x7fffffff;
|
||||
|
||||
// nearly non-exceptional case where x, y are finite
|
||||
if (ix < 0x7ff00000 and iy < 0x7ff00000) {
|
||||
if (iy | ly == 0) {
|
||||
return Complex(f64).new(math.cosh(x), x * y);
|
||||
}
|
||||
// small x: normal case
|
||||
if (ix < 0x40360000) {
|
||||
return Complex(f64).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y));
|
||||
}
|
||||
|
||||
// |x|>= 22, so cosh(x) ~= exp(|x|)
|
||||
if (ix < 0x40862e42) {
|
||||
// x < 710: exp(|x|) won't overflow
|
||||
const h = math.exp(math.fabs(x)) * 0.5;
|
||||
return Complex(f64).new(h * math.cos(y), math.copysign(f64, h, x) * math.sin(y));
|
||||
}
|
||||
// x < 1455: scale to avoid overflow
|
||||
else if (ix < 0x4096bbaa) {
|
||||
const v = Complex(f64).new(math.fabs(x), y);
|
||||
const r = ldexp_cexp(v, -1);
|
||||
return Complex(f64).new(x, y * math.copysign(f64, 1, x));
|
||||
}
|
||||
// x >= 1455: result always overflows
|
||||
else {
|
||||
const h = 0x1p1023;
|
||||
return Complex(f64).new(h * h * math.cos(y), h * math.sin(y));
|
||||
}
|
||||
}
|
||||
|
||||
if (ix | lx == 0 and iy >= 0x7ff00000) {
|
||||
return Complex(f64).new(y - y, math.copysign(f64, 0, x * (y - y)));
|
||||
}
|
||||
|
||||
if (iy | ly == 0 and ix >= 0x7ff00000) {
|
||||
if ((hx & 0xfffff) | lx == 0) {
|
||||
return Complex(f64).new(x * x, math.copysign(f64, 0, x) * y);
|
||||
}
|
||||
return Complex(f64).new(x * x, math.copysign(f64, 0, (x + x) * y));
|
||||
}
|
||||
|
||||
if (ix < 0x7ff00000 and iy >= 0x7ff00000) {
|
||||
return Complex(f64).new(y - y, x * (y - y));
|
||||
}
|
||||
|
||||
if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) {
|
||||
if (iy >= 0x7ff00000) {
|
||||
return Complex(f64).new(x * x, x * (y - y));
|
||||
}
|
||||
return Complex(f64).new(x * x * math.cos(y), x * math.sin(y));
|
||||
}
|
||||
|
||||
return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y));
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.ccosh32" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = cosh(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, -73.467300, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 10.471557, epsilon));
|
||||
}
|
||||
|
||||
test "complex.ccosh64" {
|
||||
const a = Complex(f64).new(5, 3);
|
||||
const c = cosh(a);
|
||||
|
||||
debug.assert(math.approxEq(f64, c.re, -73.467300, epsilon));
|
||||
debug.assert(math.approxEq(f64, c.im, 10.471557, epsilon));
|
||||
}
|
140
std/math/complex/exp.zig
Normal file
140
std/math/complex/exp.zig
Normal file
@ -0,0 +1,140 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
|
||||
|
||||
pub fn exp(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
|
||||
return switch (T) {
|
||||
f32 => exp32(z),
|
||||
f64 => exp64(z),
|
||||
else => @compileError("exp not implemented for " ++ @typeName(z)),
|
||||
};
|
||||
}
|
||||
|
||||
fn exp32(z: &const Complex(f32)) Complex(f32) {
|
||||
@setFloatMode(this, @import("builtin").FloatMode.Strict);
|
||||
|
||||
const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955
|
||||
const cexp_overflow = 0x43400074; // (max_exp - min_denom_exp) * ln2
|
||||
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const hy = @bitCast(u32, y) & 0x7fffffff;
|
||||
// cexp(x + i0) = exp(x) + i0
|
||||
if (hy == 0) {
|
||||
return Complex(f32).new(math.exp(x), y);
|
||||
}
|
||||
|
||||
const hx = @bitCast(u32, x);
|
||||
// cexp(0 + iy) = cos(y) + isin(y)
|
||||
if ((hx & 0x7fffffff) == 0) {
|
||||
return Complex(f32).new(math.cos(y), math.sin(y));
|
||||
}
|
||||
|
||||
if (hy >= 0x7f800000) {
|
||||
// cexp(finite|nan +- i inf|nan) = nan + i nan
|
||||
if ((hx & 0x7fffffff) != 0x7f800000) {
|
||||
return Complex(f32).new(y - y, y - y);
|
||||
}
|
||||
// cexp(-inf +- i inf|nan) = 0 + i0
|
||||
else if (hx & 0x80000000 != 0) {
|
||||
return Complex(f32).new(0, 0);
|
||||
}
|
||||
// cexp(+inf +- i inf|nan) = inf + i nan
|
||||
else {
|
||||
return Complex(f32).new(x, y - y);
|
||||
}
|
||||
}
|
||||
|
||||
// 88.7 <= x <= 192 so must scale
|
||||
if (hx >= exp_overflow and hx <= cexp_overflow) {
|
||||
return ldexp_cexp(z, 0);
|
||||
}
|
||||
// - x < exp_overflow => exp(x) won't overflow (common)
|
||||
// - x > cexp_overflow, so exp(x) * s overflows for s > 0
|
||||
// - x = +-inf
|
||||
// - x = nan
|
||||
else {
|
||||
const exp_x = math.exp(x);
|
||||
return Complex(f32).new(exp_x * math.cos(y), exp_x * math.sin(y));
|
||||
}
|
||||
}
|
||||
|
||||
fn exp64(z: &const Complex(f64)) Complex(f64) {
|
||||
const exp_overflow = 0x40862e42; // high bits of max_exp * ln2 ~= 710
|
||||
const cexp_overflow = 0x4096b8e4; // (max_exp - min_denorm_exp) * ln2
|
||||
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const fy = @bitCast(u64, y);
|
||||
const hy = u32(fy >> 32) & 0x7fffffff;
|
||||
const ly = @truncate(u32, fy);
|
||||
|
||||
// cexp(x + i0) = exp(x) + i0
|
||||
if (hy | ly == 0) {
|
||||
return Complex(f64).new(math.exp(x), y);
|
||||
}
|
||||
|
||||
const fx = @bitCast(u64, x);
|
||||
const hx = u32(fx >> 32);
|
||||
const lx = @truncate(u32, fx);
|
||||
|
||||
// cexp(0 + iy) = cos(y) + isin(y)
|
||||
if ((hx & 0x7fffffff) | lx == 0) {
|
||||
return Complex(f64).new(math.cos(y), math.sin(y));
|
||||
}
|
||||
|
||||
if (hy >= 0x7ff00000) {
|
||||
// cexp(finite|nan +- i inf|nan) = nan + i nan
|
||||
if (lx != 0 or (hx & 0x7fffffff) != 0x7ff00000) {
|
||||
return Complex(f64).new(y - y, y - y);
|
||||
}
|
||||
// cexp(-inf +- i inf|nan) = 0 + i0
|
||||
else if (hx & 0x80000000 != 0) {
|
||||
return Complex(f64).new(0, 0);
|
||||
}
|
||||
// cexp(+inf +- i inf|nan) = inf + i nan
|
||||
else {
|
||||
return Complex(f64).new(x, y - y);
|
||||
}
|
||||
}
|
||||
|
||||
// 709.7 <= x <= 1454.3 so must scale
|
||||
if (hx >= exp_overflow and hx <= cexp_overflow) {
|
||||
const r = ldexp_cexp(z, 0);
|
||||
return *r;
|
||||
}
|
||||
// - x < exp_overflow => exp(x) won't overflow (common)
|
||||
// - x > cexp_overflow, so exp(x) * s overflows for s > 0
|
||||
// - x = +-inf
|
||||
// - x = nan
|
||||
else {
|
||||
const exp_x = math.exp(x);
|
||||
return Complex(f64).new(exp_x * math.cos(y), exp_x * math.sin(y));
|
||||
}
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.cexp32" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = exp(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, -146.927917, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 20.944065, epsilon));
|
||||
}
|
||||
|
||||
test "complex.cexp64" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = exp(a);
|
||||
|
||||
debug.assert(math.approxEq(f64, c.re, -146.927917, epsilon));
|
||||
debug.assert(math.approxEq(f64, c.im, 20.944065, epsilon));
|
||||
}
|
171
std/math/complex/index.zig
Normal file
171
std/math/complex/index.zig
Normal file
@ -0,0 +1,171 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
|
||||
pub const abs = @import("abs.zig").abs;
|
||||
pub const acosh = @import("acosh.zig").acosh;
|
||||
pub const acos = @import("acos.zig").acos;
|
||||
pub const arg = @import("arg.zig").arg;
|
||||
pub const asinh = @import("asinh.zig").asinh;
|
||||
pub const asin = @import("asin.zig").asin;
|
||||
pub const atanh = @import("atanh.zig").atanh;
|
||||
pub const atan = @import("atan.zig").atan;
|
||||
pub const conj = @import("conj.zig").conj;
|
||||
pub const cosh = @import("cosh.zig").cosh;
|
||||
pub const cos = @import("cos.zig").cos;
|
||||
pub const exp = @import("exp.zig").exp;
|
||||
pub const log = @import("log.zig").log;
|
||||
pub const pow = @import("pow.zig").pow;
|
||||
pub const proj = @import("proj.zig").proj;
|
||||
pub const sinh = @import("sinh.zig").sinh;
|
||||
pub const sin = @import("sin.zig").sin;
|
||||
pub const sqrt = @import("sqrt.zig").sqrt;
|
||||
pub const tanh = @import("tanh.zig").tanh;
|
||||
pub const tan = @import("tan.zig").tan;
|
||||
|
||||
pub fn Complex(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = this;
|
||||
|
||||
re: T,
|
||||
im: T,
|
||||
|
||||
pub fn new(re: T, im: T) Self {
|
||||
return Self {
|
||||
.re = re,
|
||||
.im = im,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add(self: &const Self, other: &const Self) Self {
|
||||
return Self {
|
||||
.re = self.re + other.re,
|
||||
.im = self.im + other.im,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn sub(self: &const Self, other: &const Self) Self {
|
||||
return Self {
|
||||
.re = self.re - other.re,
|
||||
.im = self.im - other.im,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn mul(self: &const Self, other: &const Self) Self {
|
||||
return Self {
|
||||
.re = self.re * other.re - self.im * other.im,
|
||||
.im = self.im * other.re + self.re * other.im,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn div(self: &const Self, other: &const Self) Self {
|
||||
const re_num = self.re * other.re + self.im * other.im;
|
||||
const im_num = self.im * other.re - self.re * other.im;
|
||||
const den = other.re * other.re + other.im * other.im;
|
||||
|
||||
return Self {
|
||||
.re = re_num / den,
|
||||
.im = im_num / den,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn conjugate(self: &const Self) Self {
|
||||
return Self {
|
||||
.re = self.re,
|
||||
.im = -self.im,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn reciprocal(self: &const Self) Self {
|
||||
const m = self.re * self.re + self.im * self.im;
|
||||
return Self {
|
||||
.re = self.re / m,
|
||||
.im = -self.im / m,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn magnitude(self: &const Self) T {
|
||||
return math.sqrt(self.re * self.re + self.im * self.im);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.add" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const b = Complex(f32).new(2, 7);
|
||||
const c = a.add(b);
|
||||
|
||||
debug.assert(c.re == 7 and c.im == 10);
|
||||
}
|
||||
|
||||
test "complex.sub" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const b = Complex(f32).new(2, 7);
|
||||
const c = a.sub(b);
|
||||
|
||||
debug.assert(c.re == 3 and c.im == -4);
|
||||
}
|
||||
|
||||
test "complex.mul" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const b = Complex(f32).new(2, 7);
|
||||
const c = a.mul(b);
|
||||
|
||||
debug.assert(c.re == -11 and c.im == 41);
|
||||
}
|
||||
|
||||
test "complex.div" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const b = Complex(f32).new(2, 7);
|
||||
const c = a.div(b);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, f32(31)/53, epsilon) and
|
||||
math.approxEq(f32, c.im, f32(-29)/53, epsilon));
|
||||
}
|
||||
|
||||
test "complex.conjugate" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = a.conjugate();
|
||||
|
||||
debug.assert(c.re == 5 and c.im == -3);
|
||||
}
|
||||
|
||||
test "complex.reciprocal" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = a.reciprocal();
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, f32(5)/34, epsilon) and
|
||||
math.approxEq(f32, c.im, f32(-3)/34, epsilon));
|
||||
}
|
||||
|
||||
test "complex.magnitude" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = a.magnitude();
|
||||
|
||||
debug.assert(math.approxEq(f32, c, 5.83095, epsilon));
|
||||
}
|
||||
|
||||
test "complex.cmath" {
|
||||
_ = @import("abs.zig");
|
||||
_ = @import("acosh.zig");
|
||||
_ = @import("acos.zig");
|
||||
_ = @import("arg.zig");
|
||||
_ = @import("asinh.zig");
|
||||
_ = @import("asin.zig");
|
||||
_ = @import("atanh.zig");
|
||||
_ = @import("atan.zig");
|
||||
_ = @import("conj.zig");
|
||||
_ = @import("cosh.zig");
|
||||
_ = @import("cos.zig");
|
||||
_ = @import("exp.zig");
|
||||
_ = @import("log.zig");
|
||||
_ = @import("pow.zig");
|
||||
_ = @import("proj.zig");
|
||||
_ = @import("sinh.zig");
|
||||
_ = @import("sin.zig");
|
||||
_ = @import("sqrt.zig");
|
||||
_ = @import("tanh.zig");
|
||||
_ = @import("tan.zig");
|
||||
}
|
75
std/math/complex/ldexp.zig
Normal file
75
std/math/complex/ldexp.zig
Normal file
@ -0,0 +1,75 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn ldexp_cexp(z: var, expt: i32) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
|
||||
return switch (T) {
|
||||
f32 => ldexp_cexp32(z, expt),
|
||||
f64 => ldexp_cexp64(z, expt),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
fn frexp_exp32(x: f32, expt: &i32) f32 {
|
||||
const k = 235; // reduction constant
|
||||
const kln2 = 162.88958740; // k * ln2
|
||||
|
||||
const exp_x = math.exp(x - kln2);
|
||||
const hx = @bitCast(u32, exp_x);
|
||||
*expt = i32(hx >> 23) - (0x7f + 127) + k;
|
||||
return @bitCast(f32, (hx & 0x7fffff) | ((0x7f + 127) << 23));
|
||||
}
|
||||
|
||||
fn ldexp_cexp32(z: &const Complex(f32), expt: i32) Complex(f32) {
|
||||
var ex_expt: i32 = undefined;
|
||||
const exp_x = frexp_exp32(z.re, &ex_expt);
|
||||
const exptf = expt + ex_expt;
|
||||
|
||||
const half_expt1 = @divTrunc(exptf, 2);
|
||||
const scale1 = @bitCast(f32, (0x7f + half_expt1) << 23);
|
||||
|
||||
const half_expt2 = exptf - half_expt1;
|
||||
const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23);
|
||||
|
||||
return Complex(f32).new(
|
||||
math.cos(z.im) * exp_x * scale1 * scale2,
|
||||
math.sin(z.im) * exp_x * scale1 * scale2,
|
||||
);
|
||||
}
|
||||
|
||||
fn frexp_exp64(x: f64, expt: &i32) f64 {
|
||||
const k = 1799; // reduction constant
|
||||
const kln2 = 1246.97177782734161156; // k * ln2
|
||||
|
||||
const exp_x = math.exp(x - kln2);
|
||||
|
||||
const fx = @bitCast(u64, x);
|
||||
const hx = u32(fx >> 32);
|
||||
const lx = @truncate(u32, fx);
|
||||
|
||||
*expt = i32(hx >> 20) - (0x3ff + 1023) + k;
|
||||
|
||||
const high_word = (hx & 0xfffff) | ((0x3ff + 1023) << 20);
|
||||
return @bitCast(f64, (u64(high_word) << 32) | lx);
|
||||
}
|
||||
|
||||
fn ldexp_cexp64(z: &const Complex(f64), expt: i32) Complex(f64) {
|
||||
var ex_expt: i32 = undefined;
|
||||
const exp_x = frexp_exp64(z.re, &ex_expt);
|
||||
const exptf = i64(expt + ex_expt);
|
||||
|
||||
const half_expt1 = @divTrunc(exptf, 2);
|
||||
const scale1 = @bitCast(f64, (0x3ff + half_expt1) << 20);
|
||||
|
||||
const half_expt2 = exptf - half_expt1;
|
||||
const scale2 = @bitCast(f64, (0x3ff + half_expt2) << 20);
|
||||
|
||||
return Complex(f64).new(
|
||||
math.cos(z.im) * exp_x * scale1 * scale2,
|
||||
math.sin(z.im) * exp_x * scale1 * scale2,
|
||||
);
|
||||
}
|
23
std/math/complex/log.zig
Normal file
23
std/math/complex/log.zig
Normal file
@ -0,0 +1,23 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn log(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const r = cmath.abs(z);
|
||||
const phi = cmath.arg(z);
|
||||
|
||||
return Complex(T).new(math.ln(r), phi);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.clog" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = log(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 1.763180, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 0.540419, epsilon));
|
||||
}
|
22
std/math/complex/pow.zig
Normal file
22
std/math/complex/pow.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn pow(comptime T: type, z: &const T, c: &const T) T {
|
||||
const p = cmath.log(z);
|
||||
const q = c.mul(p);
|
||||
return cmath.exp(q);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.cpow" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const b = Complex(f32).new(2.3, -1.3);
|
||||
const c = pow(Complex(f32), a, b);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 58.049110, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, -101.003433, epsilon));
|
||||
}
|
24
std/math/complex/proj.zig
Normal file
24
std/math/complex/proj.zig
Normal file
@ -0,0 +1,24 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn proj(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
|
||||
if (math.isInf(z.re) or math.isInf(z.im)) {
|
||||
return Complex(T).new(math.inf(T), math.copysign(T, 0, z.re));
|
||||
}
|
||||
|
||||
return Complex(T).new(z.re, z.im);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.cproj" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = proj(a);
|
||||
|
||||
debug.assert(c.re == 5 and c.im == 3);
|
||||
}
|
22
std/math/complex/sin.zig
Normal file
22
std/math/complex/sin.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn sin(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const p = Complex(T).new(-z.im, z.re);
|
||||
const q = cmath.sinh(p);
|
||||
return Complex(T).new(q.im, -q.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.csin" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = sin(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, -9.654126, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 2.841692, epsilon));
|
||||
}
|
164
std/math/complex/sinh.zig
Normal file
164
std/math/complex/sinh.zig
Normal file
@ -0,0 +1,164 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
|
||||
|
||||
pub fn sinh(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
return switch (T) {
|
||||
f32 => sinh32(z),
|
||||
f64 => sinh64(z),
|
||||
else => @compileError("tan not implemented for " ++ @typeName(z)),
|
||||
};
|
||||
}
|
||||
|
||||
fn sinh32(z: &const Complex(f32)) Complex(f32) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const hx = @bitCast(u32, x);
|
||||
const ix = hx & 0x7fffffff;
|
||||
|
||||
const hy = @bitCast(u32, y);
|
||||
const iy = hy & 0x7fffffff;
|
||||
|
||||
if (ix < 0x7f800000 and iy < 0x7f800000) {
|
||||
if (iy == 0) {
|
||||
return Complex(f32).new(math.sinh(x), y);
|
||||
}
|
||||
// small x: normal case
|
||||
if (ix < 0x41100000) {
|
||||
return Complex(f32).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
|
||||
}
|
||||
|
||||
// |x|>= 9, so cosh(x) ~= exp(|x|)
|
||||
if (ix < 0x42b17218) {
|
||||
// x < 88.7: exp(|x|) won't overflow
|
||||
const h = math.exp(math.fabs(x)) * 0.5;
|
||||
return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));
|
||||
}
|
||||
// x < 192.7: scale to avoid overflow
|
||||
else if (ix < 0x4340b1e7) {
|
||||
const v = Complex(f32).new(math.fabs(x), y);
|
||||
const r = ldexp_cexp(v, -1);
|
||||
return Complex(f32).new(x * math.copysign(f32, 1, x), y);
|
||||
}
|
||||
// x >= 192.7: result always overflows
|
||||
else {
|
||||
const h = 0x1p127 * x;
|
||||
return Complex(f32).new(h * math.cos(y), h * h * math.sin(y));
|
||||
}
|
||||
}
|
||||
|
||||
if (ix == 0 and iy >= 0x7f800000) {
|
||||
return Complex(f32).new(math.copysign(f32, 0, x * (y - y)), y - y);
|
||||
}
|
||||
|
||||
if (iy == 0 and ix >= 0x7f800000) {
|
||||
if (hx & 0x7fffff == 0) {
|
||||
return Complex(f32).new(x, y);
|
||||
}
|
||||
return Complex(f32).new(x, math.copysign(f32, 0, y));
|
||||
}
|
||||
|
||||
if (ix < 0x7f800000 and iy >= 0x7f800000) {
|
||||
return Complex(f32).new(y - y, x * (y - y));
|
||||
}
|
||||
|
||||
if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) {
|
||||
if (iy >= 0x7f800000) {
|
||||
return Complex(f32).new(x * x, x * (y - y));
|
||||
}
|
||||
return Complex(f32).new(x * math.cos(y), math.inf_f32 * math.sin(y));
|
||||
}
|
||||
|
||||
return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y));
|
||||
}
|
||||
|
||||
fn sinh64(z: &const Complex(f64)) Complex(f64) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const fx = @bitCast(u64, x);
|
||||
const hx = u32(fx >> 32);
|
||||
const lx = @truncate(u32, fx);
|
||||
const ix = hx & 0x7fffffff;
|
||||
|
||||
const fy = @bitCast(u64, y);
|
||||
const hy = u32(fy >> 32);
|
||||
const ly = @truncate(u32, fy);
|
||||
const iy = hy & 0x7fffffff;
|
||||
|
||||
if (ix < 0x7ff00000 and iy < 0x7ff00000) {
|
||||
if (iy | ly == 0) {
|
||||
return Complex(f64).new(math.sinh(x), y);
|
||||
}
|
||||
// small x: normal case
|
||||
if (ix < 0x40360000) {
|
||||
return Complex(f64).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
|
||||
}
|
||||
|
||||
// |x|>= 22, so cosh(x) ~= exp(|x|)
|
||||
if (ix < 0x40862e42) {
|
||||
// x < 710: exp(|x|) won't overflow
|
||||
const h = math.exp(math.fabs(x)) * 0.5;
|
||||
return Complex(f64).new(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y));
|
||||
}
|
||||
// x < 1455: scale to avoid overflow
|
||||
else if (ix < 0x4096bbaa) {
|
||||
const v = Complex(f64).new(math.fabs(x), y);
|
||||
const r = ldexp_cexp(v, -1);
|
||||
return Complex(f64).new(x * math.copysign(f64, 1, x), y);
|
||||
}
|
||||
// x >= 1455: result always overflows
|
||||
else {
|
||||
const h = 0x1p1023 * x;
|
||||
return Complex(f64).new(h * math.cos(y), h * h * math.sin(y));
|
||||
}
|
||||
}
|
||||
|
||||
if (ix | lx == 0 and iy >= 0x7ff00000) {
|
||||
return Complex(f64).new(math.copysign(f64, 0, x * (y - y)), y - y);
|
||||
}
|
||||
|
||||
if (iy | ly == 0 and ix >= 0x7ff00000) {
|
||||
if ((hx & 0xfffff) | lx == 0) {
|
||||
return Complex(f64).new(x, y);
|
||||
}
|
||||
return Complex(f64).new(x, math.copysign(f64, 0, y));
|
||||
}
|
||||
|
||||
if (ix < 0x7ff00000 and iy >= 0x7ff00000) {
|
||||
return Complex(f64).new(y - y, x * (y - y));
|
||||
}
|
||||
|
||||
if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) {
|
||||
if (iy >= 0x7ff00000) {
|
||||
return Complex(f64).new(x * x, x * (y - y));
|
||||
}
|
||||
return Complex(f64).new(x * math.cos(y), math.inf_f64 * math.sin(y));
|
||||
}
|
||||
|
||||
return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y));
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.csinh32" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = sinh(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, -73.460617, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 10.472508, epsilon));
|
||||
}
|
||||
|
||||
test "complex.csinh64" {
|
||||
const a = Complex(f64).new(5, 3);
|
||||
const c = sinh(a);
|
||||
|
||||
debug.assert(math.approxEq(f64, c.re, -73.460617, epsilon));
|
||||
debug.assert(math.approxEq(f64, c.im, 10.472508, epsilon));
|
||||
}
|
133
std/math/complex/sqrt.zig
Normal file
133
std/math/complex/sqrt.zig
Normal file
@ -0,0 +1,133 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
// TODO when #733 is solved this can be @typeOf(z) instead of Complex(@typeOf(z.re))
|
||||
pub fn sqrt(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
|
||||
return switch (T) {
|
||||
f32 => sqrt32(z),
|
||||
f64 => sqrt64(z),
|
||||
else => @compileError("sqrt not implemented for " ++ @typeName(z)),
|
||||
};
|
||||
}
|
||||
|
||||
fn sqrt32(z: &const Complex(f32)) Complex(f32) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
if (x == 0 and y == 0) {
|
||||
return Complex(f32).new(0, y);
|
||||
}
|
||||
if (math.isInf(y)) {
|
||||
return Complex(f32).new(math.inf(f32), y);
|
||||
}
|
||||
if (math.isNan(x)) {
|
||||
// raise invalid if y is not nan
|
||||
const t = (y - y) / (y - y);
|
||||
return Complex(f32).new(x, t);
|
||||
}
|
||||
if (math.isInf(x)) {
|
||||
// sqrt(inf + i nan) = inf + nan i
|
||||
// sqrt(inf + iy) = inf + i0
|
||||
// sqrt(-inf + i nan) = nan +- inf i
|
||||
// sqrt(-inf + iy) = 0 + inf i
|
||||
if (math.signbit(x)) {
|
||||
return Complex(f32).new(math.fabs(x - y), math.copysign(f32, x, y));
|
||||
} else {
|
||||
return Complex(f32).new(x, math.copysign(f32, y - y, y));
|
||||
}
|
||||
}
|
||||
|
||||
// y = nan special case is handled fine below
|
||||
|
||||
// double-precision avoids overflow with correct rounding.
|
||||
const dx = f64(x);
|
||||
const dy = f64(y);
|
||||
|
||||
if (dx >= 0) {
|
||||
const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5);
|
||||
return Complex(f32).new(f32(t), f32(dy / (2.0 * t)));
|
||||
} else {
|
||||
const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5);
|
||||
return Complex(f32).new(f32(math.fabs(y) / (2.0 * t)), f32(math.copysign(f64, t, y)));
|
||||
}
|
||||
}
|
||||
|
||||
fn sqrt64(z: &const Complex(f64)) Complex(f64) {
|
||||
// may encounter overflow for im,re >= DBL_MAX / (1 + sqrt(2))
|
||||
const threshold = 0x1.a827999fcef32p+1022;
|
||||
|
||||
var x = z.re;
|
||||
var y = z.im;
|
||||
|
||||
if (x == 0 and y == 0) {
|
||||
return Complex(f64).new(0, y);
|
||||
}
|
||||
if (math.isInf(y)) {
|
||||
return Complex(f64).new(math.inf(f64), y);
|
||||
}
|
||||
if (math.isNan(x)) {
|
||||
// raise invalid if y is not nan
|
||||
const t = (y - y) / (y - y);
|
||||
return Complex(f64).new(x, t);
|
||||
}
|
||||
if (math.isInf(x)) {
|
||||
// sqrt(inf + i nan) = inf + nan i
|
||||
// sqrt(inf + iy) = inf + i0
|
||||
// sqrt(-inf + i nan) = nan +- inf i
|
||||
// sqrt(-inf + iy) = 0 + inf i
|
||||
if (math.signbit(x)) {
|
||||
return Complex(f64).new(math.fabs(x - y), math.copysign(f64, x, y));
|
||||
} else {
|
||||
return Complex(f64).new(x, math.copysign(f64, y - y, y));
|
||||
}
|
||||
}
|
||||
|
||||
// y = nan special case is handled fine below
|
||||
|
||||
// scale to avoid overflow
|
||||
var scale = false;
|
||||
if (math.fabs(x) >= threshold or math.fabs(y) >= threshold) {
|
||||
x *= 0.25;
|
||||
y *= 0.25;
|
||||
scale = true;
|
||||
}
|
||||
|
||||
var result: Complex(f64) = undefined;
|
||||
if (x >= 0) {
|
||||
const t = math.sqrt((x + math.hypot(f64, x, y)) * 0.5);
|
||||
result = Complex(f64).new(t, y / (2.0 * t));
|
||||
} else {
|
||||
const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5);
|
||||
result = Complex(f64).new(math.fabs(y) / (2.0 * t), math.copysign(f64, t, y));
|
||||
}
|
||||
|
||||
if (scale) {
|
||||
result.re *= 2;
|
||||
result.im *= 2;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.csqrt32" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = sqrt(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 2.327117, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 0.644574, epsilon));
|
||||
}
|
||||
|
||||
test "complex.csqrt64" {
|
||||
const a = Complex(f64).new(5, 3);
|
||||
const c = sqrt(a);
|
||||
|
||||
debug.assert(math.approxEq(f64, c.re, 2.3271175190399496, epsilon));
|
||||
debug.assert(math.approxEq(f64, c.im, 0.6445742373246469, epsilon));
|
||||
}
|
22
std/math/complex/tan.zig
Normal file
22
std/math/complex/tan.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn tan(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const q = Complex(T).new(-z.im, z.re);
|
||||
const r = cmath.tanh(q);
|
||||
return Complex(T).new(r.im, -r.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.ctan" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = tan(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, -0.002708233, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 1.004165, epsilon));
|
||||
}
|
111
std/math/complex/tanh.zig
Normal file
111
std/math/complex/tanh.zig
Normal file
@ -0,0 +1,111 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn tanh(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
return switch (T) {
|
||||
f32 => tanh32(z),
|
||||
f64 => tanh64(z),
|
||||
else => @compileError("tan not implemented for " ++ @typeName(z)),
|
||||
};
|
||||
}
|
||||
|
||||
fn tanh32(z: &const Complex(f32)) Complex(f32) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const hx = @bitCast(u32, x);
|
||||
const ix = hx & 0x7fffffff;
|
||||
|
||||
if (ix >= 0x7f800000) {
|
||||
if (ix & 0x7fffff != 0) {
|
||||
const r = if (y == 0) y else x * y;
|
||||
return Complex(f32).new(x, r);
|
||||
}
|
||||
const xx = @bitCast(f32, hx - 0x40000000);
|
||||
const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y);
|
||||
return Complex(f32).new(xx, math.copysign(f32, 0, r));
|
||||
}
|
||||
|
||||
if (!math.isFinite(y)) {
|
||||
const r = if (ix != 0) y - y else x;
|
||||
return Complex(f32).new(r, y - y);
|
||||
}
|
||||
|
||||
// x >= 11
|
||||
if (ix >= 0x41300000) {
|
||||
const exp_mx = math.exp(-math.fabs(x));
|
||||
return Complex(f32).new(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);
|
||||
}
|
||||
|
||||
// Kahan's algorithm
|
||||
const t = math.tan(y);
|
||||
const beta = 1.0 + t * t;
|
||||
const s = math.sinh(x);
|
||||
const rho = math.sqrt(1 + s * s);
|
||||
const den = 1 + beta * s * s;
|
||||
|
||||
return Complex(f32).new((beta * rho * s) / den, t / den);
|
||||
}
|
||||
|
||||
fn tanh64(z: &const Complex(f64)) Complex(f64) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const fx = @bitCast(u64, x);
|
||||
const hx = u32(fx >> 32);
|
||||
const lx = @truncate(u32, fx);
|
||||
const ix = hx & 0x7fffffff;
|
||||
|
||||
if (ix >= 0x7ff00000) {
|
||||
if ((ix & 0x7fffff) | lx != 0) {
|
||||
const r = if (y == 0) y else x * y;
|
||||
return Complex(f64).new(x, r);
|
||||
}
|
||||
|
||||
const xx = @bitCast(f64, (u64(hx - 0x40000000) << 32) | lx);
|
||||
const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y);
|
||||
return Complex(f64).new(xx, math.copysign(f64, 0, r));
|
||||
}
|
||||
|
||||
if (!math.isFinite(y)) {
|
||||
const r = if (ix != 0) y - y else x;
|
||||
return Complex(f64).new(r, y - y);
|
||||
}
|
||||
|
||||
// x >= 22
|
||||
if (ix >= 0x40360000) {
|
||||
const exp_mx = math.exp(-math.fabs(x));
|
||||
return Complex(f64).new(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);
|
||||
}
|
||||
|
||||
// Kahan's algorithm
|
||||
const t = math.tan(y);
|
||||
const beta = 1.0 + t * t;
|
||||
const s = math.sinh(x);
|
||||
const rho = math.sqrt(1 + s * s);
|
||||
const den = 1 + beta * s * s;
|
||||
|
||||
return Complex(f64).new((beta * rho * s) / den, t / den);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.ctanh32" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = tanh(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 0.999913, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, -0.000025, epsilon));
|
||||
}
|
||||
|
||||
test "complex.ctanh64" {
|
||||
const a = Complex(f64).new(5, 3);
|
||||
const c = tanh(a);
|
||||
|
||||
debug.assert(math.approxEq(f64, c.re, 0.999913, epsilon));
|
||||
debug.assert(math.approxEq(f64, c.im, -0.000025, epsilon));
|
||||
}
|
@ -129,6 +129,9 @@ pub const cos = @import("cos.zig").cos;
|
||||
pub const sin = @import("sin.zig").sin;
|
||||
pub const tan = @import("tan.zig").tan;
|
||||
|
||||
pub const complex = @import("complex/index.zig");
|
||||
pub const Complex = complex.Complex;
|
||||
|
||||
test "math" {
|
||||
_ = @import("nan.zig");
|
||||
_ = @import("isnan.zig");
|
||||
@ -172,6 +175,8 @@ test "math" {
|
||||
_ = @import("sin.zig");
|
||||
_ = @import("cos.zig");
|
||||
_ = @import("tan.zig");
|
||||
|
||||
_ = @import("complex/index.zig");
|
||||
}
|
||||
|
||||
|
||||
|
103
std/mem.zig
103
std/mem.zig
@ -20,7 +20,7 @@ pub const Allocator = struct {
|
||||
/// * alignment >= alignment of old_mem.ptr
|
||||
///
|
||||
/// If `new_byte_count <= old_mem.len`:
|
||||
/// * this function must return successfully.
|
||||
/// * this function must return successfully.
|
||||
/// * alignment <= alignment of old_mem.ptr
|
||||
///
|
||||
/// The returned newly allocated memory is undefined.
|
||||
@ -32,10 +32,25 @@ pub const Allocator = struct {
|
||||
freeFn: fn (self: &Allocator, old_mem: []u8) void,
|
||||
|
||||
fn create(self: &Allocator, comptime T: type) !&T {
|
||||
if (@sizeOf(T) == 0) return &{};
|
||||
const slice = try self.alloc(T, 1);
|
||||
return &slice[0];
|
||||
}
|
||||
|
||||
// TODO once #733 is solved, this will replace create
|
||||
fn construct(self: &Allocator, init: var) t: {
|
||||
// TODO this is a workaround for type getting parsed as Error!&const T
|
||||
const T = @typeOf(init).Child;
|
||||
break :t Error!&T;
|
||||
} {
|
||||
const T = @typeOf(init).Child;
|
||||
if (@sizeOf(T) == 0) return &{};
|
||||
const slice = try self.alloc(T, 1);
|
||||
const ptr = &slice[0];
|
||||
*ptr = *init;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
fn destroy(self: &Allocator, ptr: var) void {
|
||||
self.free(ptr[0..1]);
|
||||
}
|
||||
@ -53,7 +68,7 @@ pub const Allocator = struct {
|
||||
const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
|
||||
const byte_slice = try self.allocFn(self, byte_count, alignment);
|
||||
assert(byte_slice.len == byte_count);
|
||||
// This loop should get optimized out in ReleaseFast mode
|
||||
// This loop gets optimized out in ReleaseFast mode
|
||||
for (byte_slice) |*byte| {
|
||||
*byte = undefined;
|
||||
}
|
||||
@ -80,7 +95,7 @@ pub const Allocator = struct {
|
||||
const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);
|
||||
assert(byte_slice.len == byte_count);
|
||||
if (n > old_mem.len) {
|
||||
// This loop should get optimized out in ReleaseFast mode
|
||||
// This loop gets optimized out in ReleaseFast mode
|
||||
for (byte_slice[old_byte_slice.len..]) |*byte| {
|
||||
*byte = undefined;
|
||||
}
|
||||
@ -174,6 +189,20 @@ pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T {
|
||||
return new_buf;
|
||||
}
|
||||
|
||||
/// Remove values from the beginning of a slice.
|
||||
pub fn trimLeft(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
|
||||
var begin: usize = 0;
|
||||
while (begin < slice.len and indexOfScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {}
|
||||
return slice[begin..];
|
||||
}
|
||||
|
||||
/// Remove values from the end of a slice.
|
||||
pub fn trimRight(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
|
||||
var end: usize = slice.len;
|
||||
while (end > 0 and indexOfScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {}
|
||||
return slice[0..end];
|
||||
}
|
||||
|
||||
/// Remove values from the beginning and end of a slice.
|
||||
pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
|
||||
var begin: usize = 0;
|
||||
@ -184,6 +213,8 @@ pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []co
|
||||
}
|
||||
|
||||
test "mem.trim" {
|
||||
assert(eql(u8, trimLeft(u8, " foo\n ", " \n"), "foo\n "));
|
||||
assert(eql(u8, trimRight(u8, " foo\n ", " \n"), " foo"));
|
||||
assert(eql(u8, trim(u8, " foo\n ", " \n"), "foo"));
|
||||
assert(eql(u8, trim(u8, "foo", " \n"), "foo"));
|
||||
}
|
||||
@ -193,6 +224,17 @@ pub fn indexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {
|
||||
return indexOfScalarPos(T, slice, 0, value);
|
||||
}
|
||||
|
||||
/// Linear search for the last index of a scalar value inside a slice.
|
||||
pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {
|
||||
var i: usize = slice.len;
|
||||
while (i != 0) {
|
||||
i -= 1;
|
||||
if (slice[i] == value)
|
||||
return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize {
|
||||
var i: usize = start_index;
|
||||
while (i < slice.len) : (i += 1) {
|
||||
@ -206,6 +248,18 @@ pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize
|
||||
return indexOfAnyPos(T, slice, 0, values);
|
||||
}
|
||||
|
||||
pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize {
|
||||
var i: usize = slice.len;
|
||||
while (i != 0) {
|
||||
i -= 1;
|
||||
for (values) |value| {
|
||||
if (slice[i] == value)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize {
|
||||
var i: usize = start_index;
|
||||
while (i < slice.len) : (i += 1) {
|
||||
@ -221,6 +275,22 @@ pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize
|
||||
return indexOfPos(T, haystack, 0, needle);
|
||||
}
|
||||
|
||||
/// Find the index in a slice of a sub-slice, searching from the end backwards.
|
||||
/// To start looking at a different index, slice the haystack first.
|
||||
/// TODO is there even a better algorithm for this?
|
||||
pub fn lastIndexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize {
|
||||
if (needle.len > haystack.len)
|
||||
return null;
|
||||
|
||||
var i: usize = haystack.len - needle.len;
|
||||
while (true) : (i -= 1) {
|
||||
if (mem.eql(T, haystack[i..i+needle.len], needle))
|
||||
return i;
|
||||
if (i == 0)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO boyer-moore algorithm
|
||||
pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize {
|
||||
if (needle.len > haystack.len)
|
||||
@ -237,9 +307,19 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee
|
||||
|
||||
test "mem.indexOf" {
|
||||
assert(??indexOf(u8, "one two three four", "four") == 14);
|
||||
assert(??lastIndexOf(u8, "one two three two four", "two") == 14);
|
||||
assert(indexOf(u8, "one two three four", "gour") == null);
|
||||
assert(lastIndexOf(u8, "one two three four", "gour") == null);
|
||||
assert(??indexOf(u8, "foo", "foo") == 0);
|
||||
assert(??lastIndexOf(u8, "foo", "foo") == 0);
|
||||
assert(indexOf(u8, "foo", "fool") == null);
|
||||
assert(lastIndexOf(u8, "foo", "lfoo") == null);
|
||||
assert(lastIndexOf(u8, "foo", "fool") == null);
|
||||
|
||||
assert(??indexOf(u8, "foo foo", "foo") == 0);
|
||||
assert(??lastIndexOf(u8, "foo foo", "foo") == 4);
|
||||
assert(??lastIndexOfAny(u8, "boo, cat", "abo") == 6);
|
||||
assert(??lastIndexOfScalar(u8, "boo", 'o') == 2);
|
||||
}
|
||||
|
||||
/// Reads an integer from memory with size equal to bytes.len.
|
||||
@ -359,9 +439,24 @@ pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool
|
||||
return if (needle.len > haystack.len) false else eql(T, haystack[0 .. needle.len], needle);
|
||||
}
|
||||
|
||||
test "mem.startsWith" {
|
||||
assert(startsWith(u8, "Bob", "Bo"));
|
||||
assert(!startsWith(u8, "Needle in haystack", "haystack"));
|
||||
}
|
||||
|
||||
pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool {
|
||||
return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle);
|
||||
}
|
||||
|
||||
|
||||
test "mem.endsWith" {
|
||||
assert(endsWith(u8, "Needle in haystack", "haystack"));
|
||||
assert(!endsWith(u8, "Bob", "Bo"));
|
||||
}
|
||||
|
||||
pub const SplitIterator = struct {
|
||||
buffer: []const u8,
|
||||
split_bytes: []const u8,
|
||||
split_bytes: []const u8,
|
||||
index: usize,
|
||||
|
||||
pub fn next(self: &SplitIterator) ?[]const u8 {
|
||||
|
@ -184,7 +184,7 @@ pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize {
|
||||
return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte));
|
||||
}
|
||||
|
||||
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
|
||||
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32,
|
||||
offset: isize) usize
|
||||
{
|
||||
const ptr_result = c.mmap(@ptrCast(&c_void, address), length,
|
||||
@ -193,8 +193,8 @@ pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
|
||||
return errnoWrap(isize_result);
|
||||
}
|
||||
|
||||
pub fn munmap(address: &u8, length: usize) usize {
|
||||
return errnoWrap(c.munmap(@ptrCast(&c_void, address), length));
|
||||
pub fn munmap(address: usize, length: usize) usize {
|
||||
return errnoWrap(c.munmap(@intToPtr(&c_void, address), length));
|
||||
}
|
||||
|
||||
pub fn unlink(path: &const u8) usize {
|
||||
@ -341,4 +341,4 @@ pub const timeval = c.timeval;
|
||||
pub const mach_timebase_info_data = c.mach_timebase_info_data;
|
||||
|
||||
pub const mach_absolute_time = c.mach_absolute_time;
|
||||
pub const mach_timebase_info = c.mach_timebase_info;
|
||||
pub const mach_timebase_info = c.mach_timebase_info;
|
||||
|
525
std/os/index.zig
525
std/os/index.zig
@ -2,6 +2,11 @@ const std = @import("../index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const Os = builtin.Os;
|
||||
const is_windows = builtin.os == Os.windows;
|
||||
const is_posix = switch (builtin.os) {
|
||||
builtin.Os.linux,
|
||||
builtin.Os.macosx => true,
|
||||
else => false,
|
||||
};
|
||||
const os = this;
|
||||
|
||||
test "std.os" {
|
||||
@ -20,9 +25,10 @@ pub const windows = @import("windows/index.zig");
|
||||
pub const darwin = @import("darwin.zig");
|
||||
pub const linux = @import("linux/index.zig");
|
||||
pub const zen = @import("zen.zig");
|
||||
pub const posix = switch(builtin.os) {
|
||||
pub const posix = switch (builtin.os) {
|
||||
Os.linux => linux,
|
||||
Os.macosx, Os.ios => darwin,
|
||||
Os.macosx,
|
||||
Os.ios => darwin,
|
||||
Os.zen => zen,
|
||||
else => @compileError("Unsupported OS"),
|
||||
};
|
||||
@ -54,7 +60,7 @@ pub const windowsWrite = windows_util.windowsWrite;
|
||||
pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
|
||||
pub const windowsOpen = windows_util.windowsOpen;
|
||||
pub const windowsLoadDll = windows_util.windowsLoadDll;
|
||||
pub const windowsUnloadDll = windows_util.windowsUnloadDll;
|
||||
pub const windowsUnloadDll = windows_util.windowsUnloadDll;
|
||||
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
|
||||
|
||||
pub const WindowsWaitError = windows_util.WaitError;
|
||||
@ -93,9 +99,9 @@ pub fn getRandomBytes(buf: []u8) !void {
|
||||
switch (err) {
|
||||
posix.EINVAL => unreachable,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINTR => continue,
|
||||
posix.EINTR => continue,
|
||||
posix.ENOSYS => {
|
||||
const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0);
|
||||
const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0);
|
||||
defer close(fd);
|
||||
|
||||
try posixRead(fd, buf);
|
||||
@ -106,8 +112,9 @@ pub fn getRandomBytes(buf: []u8) !void {
|
||||
}
|
||||
return;
|
||||
},
|
||||
Os.macosx, Os.ios => {
|
||||
const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0);
|
||||
Os.macosx,
|
||||
Os.ios => {
|
||||
const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0);
|
||||
defer close(fd);
|
||||
|
||||
try posixRead(fd, buf);
|
||||
@ -130,7 +137,20 @@ pub fn getRandomBytes(buf: []u8) !void {
|
||||
}
|
||||
},
|
||||
Os.zen => {
|
||||
const randomness = []u8 {42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45};
|
||||
const randomness = []u8 {
|
||||
42,
|
||||
1,
|
||||
7,
|
||||
12,
|
||||
22,
|
||||
17,
|
||||
99,
|
||||
16,
|
||||
26,
|
||||
87,
|
||||
41,
|
||||
45,
|
||||
};
|
||||
var i: usize = 0;
|
||||
while (i < buf.len) : (i += 1) {
|
||||
if (i > randomness.len) return error.Unknown;
|
||||
@ -155,7 +175,9 @@ pub fn abort() noreturn {
|
||||
c.abort();
|
||||
}
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios => {
|
||||
Os.linux,
|
||||
Os.macosx,
|
||||
Os.ios => {
|
||||
_ = posix.raise(posix.SIGABRT);
|
||||
_ = posix.raise(posix.SIGKILL);
|
||||
while (true) {}
|
||||
@ -177,7 +199,9 @@ pub fn exit(status: u8) noreturn {
|
||||
c.exit(status);
|
||||
}
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios => {
|
||||
Os.linux,
|
||||
Os.macosx,
|
||||
Os.ios => {
|
||||
posix.exit(status);
|
||||
},
|
||||
Os.windows => {
|
||||
@ -226,12 +250,14 @@ pub fn posixRead(fd: i32, buf: []u8) !void {
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EINTR => continue,
|
||||
posix.EINVAL, posix.EFAULT => unreachable,
|
||||
posix.EINVAL,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EAGAIN => error.WouldBlock,
|
||||
posix.EBADF => error.FileClosed,
|
||||
posix.EIO => error.InputOutput,
|
||||
posix.EISDIR => error.IsDir,
|
||||
posix.ENOBUFS, posix.ENOMEM => error.SystemResources,
|
||||
posix.ENOBUFS,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
else => unexpectedErrorPosix(err),
|
||||
};
|
||||
}
|
||||
@ -265,18 +291,19 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
|
||||
const write_err = posix.getErrno(rc);
|
||||
if (write_err > 0) {
|
||||
return switch (write_err) {
|
||||
posix.EINTR => continue,
|
||||
posix.EINVAL, posix.EFAULT => unreachable,
|
||||
posix.EINTR => continue,
|
||||
posix.EINVAL,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EAGAIN => PosixWriteError.WouldBlock,
|
||||
posix.EBADF => PosixWriteError.FileClosed,
|
||||
posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired,
|
||||
posix.EDQUOT => PosixWriteError.DiskQuota,
|
||||
posix.EFBIG => PosixWriteError.FileTooBig,
|
||||
posix.EIO => PosixWriteError.InputOutput,
|
||||
posix.EFBIG => PosixWriteError.FileTooBig,
|
||||
posix.EIO => PosixWriteError.InputOutput,
|
||||
posix.ENOSPC => PosixWriteError.NoSpaceLeft,
|
||||
posix.EPERM => PosixWriteError.AccessDenied,
|
||||
posix.EPIPE => PosixWriteError.BrokenPipe,
|
||||
else => unexpectedErrorPosix(write_err),
|
||||
posix.EPERM => PosixWriteError.AccessDenied,
|
||||
posix.EPIPE => PosixWriteError.BrokenPipe,
|
||||
else => unexpectedErrorPosix(write_err),
|
||||
};
|
||||
}
|
||||
index += rc;
|
||||
@ -322,7 +349,8 @@ pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 {
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.EACCES => return PosixOpenError.AccessDenied,
|
||||
posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig,
|
||||
posix.EFBIG,
|
||||
posix.EOVERFLOW => return PosixOpenError.FileTooBig,
|
||||
posix.EISDIR => return PosixOpenError.IsDir,
|
||||
posix.ELOOP => return PosixOpenError.SymLinkLoop,
|
||||
posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded,
|
||||
@ -347,7 +375,8 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void {
|
||||
const err = posix.getErrno(posix.dup2(old_fd, new_fd));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EBUSY, posix.EINTR => continue,
|
||||
posix.EBUSY,
|
||||
posix.EINTR => continue,
|
||||
posix.EMFILE => error.ProcessFdQuotaExceeded,
|
||||
posix.EINVAL => unreachable,
|
||||
else => unexpectedErrorPosix(err),
|
||||
@ -382,7 +411,7 @@ pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap)
|
||||
|
||||
pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {
|
||||
for (envp_buf) |env| {
|
||||
const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break;
|
||||
const env_buf = if (env) |ptr| ptr[0..cstr.len(ptr) + 1] else break;
|
||||
allocator.free(env_buf);
|
||||
}
|
||||
allocator.free(envp_buf);
|
||||
@ -393,9 +422,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {
|
||||
/// pointers after the args and after the environment variables.
|
||||
/// `argv[0]` is the executable path.
|
||||
/// This function also uses the PATH environment variable to get the full path to the executable.
|
||||
pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
|
||||
allocator: &Allocator) !void
|
||||
{
|
||||
pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, allocator: &Allocator) !void {
|
||||
const argv_buf = try allocator.alloc(?&u8, argv.len + 1);
|
||||
mem.set(?&u8, argv_buf, null);
|
||||
defer {
|
||||
@ -434,7 +461,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
|
||||
while (it.next()) |search_path| {
|
||||
mem.copy(u8, path_buf, search_path);
|
||||
path_buf[search_path.len] = '/';
|
||||
mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path);
|
||||
mem.copy(u8, path_buf[search_path.len + 1..], exe_path);
|
||||
path_buf[search_path.len + exe_path.len + 1] = 0;
|
||||
err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr));
|
||||
assert(err > 0);
|
||||
@ -466,10 +493,17 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
|
||||
assert(err > 0);
|
||||
return switch (err) {
|
||||
posix.EFAULT => unreachable,
|
||||
posix.E2BIG, posix.EMFILE, posix.ENAMETOOLONG, posix.ENFILE, posix.ENOMEM => error.SystemResources,
|
||||
posix.EACCES, posix.EPERM => error.AccessDenied,
|
||||
posix.EINVAL, posix.ENOEXEC => error.InvalidExe,
|
||||
posix.EIO, posix.ELOOP => error.FileSystem,
|
||||
posix.E2BIG,
|
||||
posix.EMFILE,
|
||||
posix.ENAMETOOLONG,
|
||||
posix.ENFILE,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
posix.EACCES,
|
||||
posix.EPERM => error.AccessDenied,
|
||||
posix.EINVAL,
|
||||
posix.ENOEXEC => error.InvalidExe,
|
||||
posix.EIO,
|
||||
posix.ELOOP => error.FileSystem,
|
||||
posix.EISDIR => error.IsDir,
|
||||
posix.ENOENT => error.FileNotFound,
|
||||
posix.ENOTDIR => error.NotDir,
|
||||
@ -478,7 +512,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
|
||||
};
|
||||
}
|
||||
|
||||
pub var linux_aux_raw = []usize{0} ** 38;
|
||||
pub var linux_aux_raw = []usize {0} ** 38;
|
||||
pub var posix_environ_raw: []&u8 = undefined;
|
||||
|
||||
/// Caller must free result when done.
|
||||
@ -492,8 +526,7 @@ pub fn getEnvMap(allocator: &Allocator) !BufMap {
|
||||
|
||||
var i: usize = 0;
|
||||
while (true) {
|
||||
if (ptr[i] == 0)
|
||||
return result;
|
||||
if (ptr[i] == 0) return result;
|
||||
|
||||
const key_start = i;
|
||||
|
||||
@ -531,8 +564,7 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 {
|
||||
var line_i: usize = 0;
|
||||
while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
|
||||
const this_key = ptr[0..line_i];
|
||||
if (!mem.eql(u8, key, this_key))
|
||||
continue;
|
||||
if (!mem.eql(u8, key, this_key)) continue;
|
||||
|
||||
var end_i: usize = line_i;
|
||||
while (ptr[end_i] != 0) : (end_i += 1) {}
|
||||
@ -685,8 +717,10 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
|
||||
const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EFAULT, posix.EINVAL => unreachable,
|
||||
posix.EACCES, posix.EPERM => error.AccessDenied,
|
||||
posix.EFAULT,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.EACCES,
|
||||
posix.EPERM => error.AccessDenied,
|
||||
posix.EDQUOT => error.DiskQuota,
|
||||
posix.EEXIST => error.PathAlreadyExists,
|
||||
posix.EIO => error.FileSystem,
|
||||
@ -703,9 +737,7 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
|
||||
}
|
||||
|
||||
// here we replace the standard +/ with -_ so that it can be used in a file name
|
||||
const b64_fs_encoder = base64.Base64Encoder.init(
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
|
||||
base64.standard_pad_char);
|
||||
const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
|
||||
|
||||
pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void {
|
||||
if (symLink(allocator, existing_path, new_path)) {
|
||||
@ -724,7 +756,7 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
|
||||
tmp_path[dirname.len] = os.path.sep;
|
||||
while (true) {
|
||||
try getRandomBytes(rand_buf[0..]);
|
||||
b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
|
||||
b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf);
|
||||
|
||||
if (symLink(allocator, existing_path, tmp_path)) {
|
||||
return rename(allocator, tmp_path, new_path);
|
||||
@ -733,7 +765,6 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
|
||||
else => return err, // TODO zig should know this set does not include PathAlreadyExists
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void {
|
||||
@ -756,7 +787,8 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void {
|
||||
return switch (err) {
|
||||
windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
|
||||
windows.ERROR.ACCESS_DENIED => error.AccessDenied,
|
||||
windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong,
|
||||
windows.ERROR.FILENAME_EXCED_RANGE,
|
||||
windows.ERROR.INVALID_PARAMETER => error.NameTooLong,
|
||||
else => unexpectedErrorWindows(err),
|
||||
};
|
||||
}
|
||||
@ -772,9 +804,11 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void {
|
||||
const err = posix.getErrno(posix.unlink(buf.ptr));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EACCES, posix.EPERM => error.AccessDenied,
|
||||
posix.EACCES,
|
||||
posix.EPERM => error.AccessDenied,
|
||||
posix.EBUSY => error.FileBusy,
|
||||
posix.EFAULT, posix.EINVAL => unreachable,
|
||||
posix.EFAULT,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.EIO => error.FileSystem,
|
||||
posix.EISDIR => error.IsDir,
|
||||
posix.ELOOP => error.SymLinkLoop,
|
||||
@ -852,7 +886,7 @@ pub const AtomicFile = struct {
|
||||
|
||||
while (true) {
|
||||
try getRandomBytes(rand_buf[0..]);
|
||||
b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
|
||||
b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf);
|
||||
|
||||
const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => continue,
|
||||
@ -903,7 +937,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
|
||||
new_buf[new_path.len] = 0;
|
||||
|
||||
if (is_windows) {
|
||||
const flags = windows.MOVEFILE_REPLACE_EXISTING|windows.MOVEFILE_WRITE_THROUGH;
|
||||
const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
|
||||
if (windows.MoveFileExA(old_buf.ptr, new_buf.ptr, flags) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
@ -914,10 +948,12 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
|
||||
const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EACCES, posix.EPERM => error.AccessDenied,
|
||||
posix.EACCES,
|
||||
posix.EPERM => error.AccessDenied,
|
||||
posix.EBUSY => error.FileBusy,
|
||||
posix.EDQUOT => error.DiskQuota,
|
||||
posix.EFAULT, posix.EINVAL => unreachable,
|
||||
posix.EFAULT,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.EISDIR => error.IsDir,
|
||||
posix.ELOOP => error.SymLinkLoop,
|
||||
posix.EMLINK => error.LinkQuotaExceeded,
|
||||
@ -926,7 +962,8 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
|
||||
posix.ENOTDIR => error.NotDir,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
posix.ENOSPC => error.NoSpaceLeft,
|
||||
posix.EEXIST, posix.ENOTEMPTY => error.PathAlreadyExists,
|
||||
posix.EEXIST,
|
||||
posix.ENOTEMPTY => error.PathAlreadyExists,
|
||||
posix.EROFS => error.ReadOnlyFileSystem,
|
||||
posix.EXDEV => error.RenameAcrossMountPoints,
|
||||
else => unexpectedErrorPosix(err),
|
||||
@ -964,7 +1001,8 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void {
|
||||
const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EACCES, posix.EPERM => error.AccessDenied,
|
||||
posix.EACCES,
|
||||
posix.EPERM => error.AccessDenied,
|
||||
posix.EDQUOT => error.DiskQuota,
|
||||
posix.EEXIST => error.PathAlreadyExists,
|
||||
posix.EFAULT => unreachable,
|
||||
@ -994,27 +1032,23 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) !void {
|
||||
// TODO stat the file and return an error if it's not a directory
|
||||
// this is important because otherwise a dangling symlink
|
||||
// could cause an infinite loop
|
||||
if (end_index == resolved_path.len)
|
||||
return;
|
||||
if (end_index == resolved_path.len) return;
|
||||
} else if (err == error.FileNotFound) {
|
||||
// march end_index backward until next path component
|
||||
while (true) {
|
||||
end_index -= 1;
|
||||
if (os.path.isSep(resolved_path[end_index]))
|
||||
break;
|
||||
if (os.path.isSep(resolved_path[end_index])) break;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
};
|
||||
if (end_index == resolved_path.len)
|
||||
return;
|
||||
if (end_index == resolved_path.len) return;
|
||||
// march end_index forward until next path component
|
||||
while (true) {
|
||||
end_index += 1;
|
||||
if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index]))
|
||||
break;
|
||||
if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1031,15 +1065,18 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {
|
||||
const err = posix.getErrno(posix.rmdir(path_buf.ptr));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EACCES, posix.EPERM => error.AccessDenied,
|
||||
posix.EACCES,
|
||||
posix.EPERM => error.AccessDenied,
|
||||
posix.EBUSY => error.FileBusy,
|
||||
posix.EFAULT, posix.EINVAL => unreachable,
|
||||
posix.EFAULT,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.ELOOP => error.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => error.NameTooLong,
|
||||
posix.ENOENT => error.FileNotFound,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
posix.ENOTDIR => error.NotDir,
|
||||
posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,
|
||||
posix.EEXIST,
|
||||
posix.ENOTEMPTY => error.DirNotEmpty,
|
||||
posix.EROFS => error.ReadOnlyFileSystem,
|
||||
else => unexpectedErrorPosix(err),
|
||||
};
|
||||
@ -1049,7 +1086,7 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {
|
||||
/// Whether ::full_path describes a symlink, file, or directory, this function
|
||||
/// removes it. If it cannot be removed because it is a non-empty directory,
|
||||
/// this function recursively removes its entries and then tries again.
|
||||
// TODO non-recursive implementation
|
||||
/// TODO non-recursive implementation
|
||||
const DeleteTreeError = error {
|
||||
OutOfMemory,
|
||||
AccessDenied,
|
||||
@ -1091,8 +1128,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
|
||||
error.NotDir,
|
||||
error.FileSystem,
|
||||
error.FileBusy,
|
||||
error.Unexpected
|
||||
=> return err,
|
||||
error.Unexpected => return err,
|
||||
}
|
||||
{
|
||||
var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
|
||||
@ -1116,8 +1152,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
|
||||
error.SystemResources,
|
||||
error.NoSpaceLeft,
|
||||
error.PathAlreadyExists,
|
||||
error.Unexpected
|
||||
=> return err,
|
||||
error.Unexpected => return err,
|
||||
};
|
||||
defer dir.close();
|
||||
|
||||
@ -1147,7 +1182,8 @@ pub const Dir = struct {
|
||||
end_index: usize,
|
||||
|
||||
const darwin_seek_t = switch (builtin.os) {
|
||||
Os.macosx, Os.ios => i64,
|
||||
Os.macosx,
|
||||
Os.ios => i64,
|
||||
else => void,
|
||||
};
|
||||
|
||||
@ -1171,12 +1207,14 @@ pub const Dir = struct {
|
||||
pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {
|
||||
const fd = switch (builtin.os) {
|
||||
Os.windows => @compileError("TODO support Dir.open for windows"),
|
||||
Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),
|
||||
Os.macosx, Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_NONBLOCK|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),
|
||||
Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0),
|
||||
Os.macosx,
|
||||
Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, 0),
|
||||
else => @compileError("Dir.open is not supported for this platform"),
|
||||
};
|
||||
const darwin_seek_init = switch (builtin.os) {
|
||||
Os.macosx, Os.ios => 0,
|
||||
Os.macosx,
|
||||
Os.ios => 0,
|
||||
else => {},
|
||||
};
|
||||
return Dir {
|
||||
@ -1199,7 +1237,8 @@ pub const Dir = struct {
|
||||
pub fn next(self: &Dir) !?Entry {
|
||||
switch (builtin.os) {
|
||||
Os.linux => return self.nextLinux(),
|
||||
Os.macosx, Os.ios => return self.nextDarwin(),
|
||||
Os.macosx,
|
||||
Os.ios => return self.nextDarwin(),
|
||||
Os.windows => return self.nextWindows(),
|
||||
else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)),
|
||||
}
|
||||
@ -1213,12 +1252,13 @@ pub const Dir = struct {
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len,
|
||||
&self.darwin_seek);
|
||||
const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, &self.darwin_seek);
|
||||
const err = posix.getErrno(result);
|
||||
if (err > 0) {
|
||||
switch (err) {
|
||||
posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
|
||||
posix.EBADF,
|
||||
posix.EFAULT,
|
||||
posix.ENOTDIR => unreachable,
|
||||
posix.EINVAL => {
|
||||
self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
|
||||
continue;
|
||||
@ -1226,14 +1266,13 @@ pub const Dir = struct {
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
if (result == 0)
|
||||
return null;
|
||||
if (result == 0) return null;
|
||||
self.index = 0;
|
||||
self.end_index = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const darwin_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);
|
||||
const darwin_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]);
|
||||
const next_index = self.index + darwin_entry.d_reclen;
|
||||
self.index = next_index;
|
||||
|
||||
@ -1278,7 +1317,9 @@ pub const Dir = struct {
|
||||
const err = posix.getErrno(result);
|
||||
if (err > 0) {
|
||||
switch (err) {
|
||||
posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
|
||||
posix.EBADF,
|
||||
posix.EFAULT,
|
||||
posix.ENOTDIR => unreachable,
|
||||
posix.EINVAL => {
|
||||
self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
|
||||
continue;
|
||||
@ -1286,14 +1327,13 @@ pub const Dir = struct {
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
if (result == 0)
|
||||
return null;
|
||||
if (result == 0) return null;
|
||||
self.index = 0;
|
||||
self.end_index = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const linux_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);
|
||||
const linux_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]);
|
||||
const next_index = self.index + linux_entry.d_reclen;
|
||||
self.index = next_index;
|
||||
|
||||
@ -1362,7 +1402,8 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EACCES => error.AccessDenied,
|
||||
posix.EFAULT, posix.EINVAL => unreachable,
|
||||
posix.EFAULT,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.EIO => error.FileSystem,
|
||||
posix.ELOOP => error.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => error.NameTooLong,
|
||||
@ -1455,8 +1496,7 @@ pub const ArgIteratorPosix = struct {
|
||||
}
|
||||
|
||||
pub fn next(self: &ArgIteratorPosix) ?[]const u8 {
|
||||
if (self.index == self.count)
|
||||
return null;
|
||||
if (self.index == self.count) return null;
|
||||
|
||||
const s = raw[self.index];
|
||||
self.index += 1;
|
||||
@ -1464,8 +1504,7 @@ pub const ArgIteratorPosix = struct {
|
||||
}
|
||||
|
||||
pub fn skip(self: &ArgIteratorPosix) bool {
|
||||
if (self.index == self.count)
|
||||
return false;
|
||||
if (self.index == self.count) return false;
|
||||
|
||||
self.index += 1;
|
||||
return true;
|
||||
@ -1483,7 +1522,9 @@ pub const ArgIteratorWindows = struct {
|
||||
quote_count: usize,
|
||||
seen_quote_count: usize,
|
||||
|
||||
pub const NextError = error{OutOfMemory};
|
||||
pub const NextError = error {
|
||||
OutOfMemory,
|
||||
};
|
||||
|
||||
pub fn init() ArgIteratorWindows {
|
||||
return initWithCmdLine(windows.GetCommandLineA());
|
||||
@ -1506,7 +1547,8 @@ pub const ArgIteratorWindows = struct {
|
||||
const byte = self.cmd_line[self.index];
|
||||
switch (byte) {
|
||||
0 => return null,
|
||||
' ', '\t' => continue,
|
||||
' ',
|
||||
'\t' => continue,
|
||||
else => break,
|
||||
}
|
||||
}
|
||||
@ -1520,7 +1562,8 @@ pub const ArgIteratorWindows = struct {
|
||||
const byte = self.cmd_line[self.index];
|
||||
switch (byte) {
|
||||
0 => return false,
|
||||
' ', '\t' => continue,
|
||||
' ',
|
||||
'\t' => continue,
|
||||
else => break,
|
||||
}
|
||||
}
|
||||
@ -1539,7 +1582,8 @@ pub const ArgIteratorWindows = struct {
|
||||
'\\' => {
|
||||
backslash_count += 1;
|
||||
},
|
||||
' ', '\t' => {
|
||||
' ',
|
||||
'\t' => {
|
||||
if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {
|
||||
return true;
|
||||
}
|
||||
@ -1579,7 +1623,8 @@ pub const ArgIteratorWindows = struct {
|
||||
'\\' => {
|
||||
backslash_count += 1;
|
||||
},
|
||||
' ', '\t' => {
|
||||
' ',
|
||||
'\t' => {
|
||||
try self.emitBackslashes(&buf, backslash_count);
|
||||
backslash_count = 0;
|
||||
if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) {
|
||||
@ -1623,7 +1668,6 @@ pub const ArgIteratorWindows = struct {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
pub const ArgIterator = struct {
|
||||
@ -1638,7 +1682,7 @@ pub const ArgIterator = struct {
|
||||
}
|
||||
|
||||
pub const NextError = ArgIteratorWindows.NextError;
|
||||
|
||||
|
||||
/// You must free the returned memory when done.
|
||||
pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) {
|
||||
if (builtin.os == Os.windows) {
|
||||
@ -1713,15 +1757,47 @@ pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void {
|
||||
}
|
||||
|
||||
test "windows arg parsing" {
|
||||
testWindowsCmdLine(c"a b\tc d", [][]const u8{"a", "b", "c", "d"});
|
||||
testWindowsCmdLine(c"\"abc\" d e", [][]const u8{"abc", "d", "e"});
|
||||
testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{"a\\\\\\b", "de fg", "h"});
|
||||
testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{"a\\\"b", "c", "d"});
|
||||
testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{"a\\\\b c", "d", "e"});
|
||||
testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{"a", "b", "c", "\"d", "f"});
|
||||
testWindowsCmdLine(c"a b\tc d", [][]const u8 {
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"d",
|
||||
});
|
||||
testWindowsCmdLine(c"\"abc\" d e", [][]const u8 {
|
||||
"abc",
|
||||
"d",
|
||||
"e",
|
||||
});
|
||||
testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8 {
|
||||
"a\\\\\\b",
|
||||
"de fg",
|
||||
"h",
|
||||
});
|
||||
testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8 {
|
||||
"a\\\"b",
|
||||
"c",
|
||||
"d",
|
||||
});
|
||||
testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8 {
|
||||
"a\\\\b c",
|
||||
"d",
|
||||
"e",
|
||||
});
|
||||
testWindowsCmdLine(c"a b\tc \"d f", [][]const u8 {
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"\"d",
|
||||
"f",
|
||||
});
|
||||
|
||||
testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"",
|
||||
[][]const u8{".\\..\\zig-cache\\build", "bin\\zig.exe", ".\\..", ".\\..\\zig-cache", "--help"});
|
||||
testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8 {
|
||||
".\\..\\zig-cache\\build",
|
||||
"bin\\zig.exe",
|
||||
".\\..",
|
||||
".\\..\\zig-cache",
|
||||
"--help",
|
||||
});
|
||||
}
|
||||
|
||||
fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const u8) void {
|
||||
@ -1768,7 +1844,8 @@ pub fn openSelfExe() !os.File {
|
||||
var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
|
||||
return os.File.openRead(&fixed_allocator.allocator, proc_file_path);
|
||||
},
|
||||
Os.macosx, Os.ios => {
|
||||
Os.macosx,
|
||||
Os.ios => {
|
||||
var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;
|
||||
var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
|
||||
const self_exe_path = try selfExePath(&fixed_allocator.allocator);
|
||||
@ -1780,8 +1857,10 @@ pub fn openSelfExe() !os.File {
|
||||
|
||||
test "openSelfExe" {
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
|
||||
else => return, // Unsupported OS.
|
||||
Os.linux,
|
||||
Os.macosx,
|
||||
Os.ios => (try openSelfExe()).close(),
|
||||
else => return, // Unsupported OS.
|
||||
}
|
||||
}
|
||||
|
||||
@ -1818,7 +1897,8 @@ pub fn selfExePath(allocator: &mem.Allocator) ![]u8 {
|
||||
try out_path.resize(new_len);
|
||||
}
|
||||
},
|
||||
Os.macosx, Os.ios => {
|
||||
Os.macosx,
|
||||
Os.ios => {
|
||||
var u32_len: u32 = 0;
|
||||
const ret1 = c._NSGetExecutablePath(undefined, &u32_len);
|
||||
assert(ret1 != 0);
|
||||
@ -1846,7 +1926,9 @@ pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 {
|
||||
const dir = path.dirname(full_exe_path);
|
||||
return allocator.shrink(u8, full_exe_path, dir.len);
|
||||
},
|
||||
Os.windows, Os.macosx, Os.ios => {
|
||||
Os.windows,
|
||||
Os.macosx,
|
||||
Os.ios => {
|
||||
const self_exe_path = try selfExePath(allocator);
|
||||
errdefer allocator.free(self_exe_path);
|
||||
const dirname = os.path.dirname(self_exe_path);
|
||||
@ -1903,7 +1985,8 @@ pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 {
|
||||
posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable,
|
||||
posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded,
|
||||
posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded,
|
||||
posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources,
|
||||
posix.ENOBUFS,
|
||||
posix.ENOMEM => return PosixSocketError.SystemResources,
|
||||
posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
@ -1934,7 +2017,7 @@ pub const PosixBindError = error {
|
||||
|
||||
/// A nonexistent interface was requested or the requested address was not local.
|
||||
AddressNotAvailable,
|
||||
|
||||
|
||||
/// addr points outside the user's accessible address space.
|
||||
PageFault,
|
||||
|
||||
@ -2023,7 +2106,7 @@ pub const PosixAcceptError = error {
|
||||
FileDescriptorClosed,
|
||||
|
||||
ConnectionAborted,
|
||||
|
||||
|
||||
/// The addr argument is not in a writable part of the user address space.
|
||||
PageFault,
|
||||
|
||||
@ -2036,7 +2119,7 @@ pub const PosixAcceptError = error {
|
||||
|
||||
/// The system-wide limit on the total number of open files has been reached.
|
||||
SystemFdQuotaExceeded,
|
||||
|
||||
|
||||
/// Not enough free memory. This often means that the memory allocation is limited
|
||||
/// by the socket buffer limits, not by the system memory.
|
||||
SystemResources,
|
||||
@ -2072,7 +2155,8 @@ pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError!
|
||||
posix.EINVAL => return PosixAcceptError.InvalidSyscall,
|
||||
posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded,
|
||||
posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded,
|
||||
posix.ENOBUFS, posix.ENOMEM => return PosixAcceptError.SystemResources,
|
||||
posix.ENOBUFS,
|
||||
posix.ENOMEM => return PosixAcceptError.SystemResources,
|
||||
posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket,
|
||||
posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported,
|
||||
posix.EPROTO => return PosixAcceptError.ProtocolFailure,
|
||||
@ -2283,7 +2367,8 @@ pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConn
|
||||
const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0, posix.EINPROGRESS => return,
|
||||
0,
|
||||
posix.EINPROGRESS => return,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
|
||||
posix.EACCES => return PosixConnectError.PermissionDenied,
|
||||
@ -2343,24 +2428,58 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void {
|
||||
}
|
||||
|
||||
pub const Thread = struct {
|
||||
pid: i32,
|
||||
allocator: ?&mem.Allocator,
|
||||
stack: []u8,
|
||||
data: Data,
|
||||
|
||||
pub const use_pthreads = is_posix and builtin.link_libc;
|
||||
const Data = if (use_pthreads) struct {
|
||||
handle: c.pthread_t,
|
||||
stack_addr: usize,
|
||||
stack_len: usize,
|
||||
} else switch (builtin.os) {
|
||||
builtin.Os.linux => struct {
|
||||
pid: i32,
|
||||
stack_addr: usize,
|
||||
stack_len: usize,
|
||||
},
|
||||
builtin.Os.windows => struct {
|
||||
handle: windows.HANDLE,
|
||||
alloc_start: &c_void,
|
||||
heap_handle: windows.HANDLE,
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
};
|
||||
|
||||
pub fn wait(self: &const Thread) void {
|
||||
while (true) {
|
||||
const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst);
|
||||
if (pid_value == 0) break;
|
||||
const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null);
|
||||
switch (linux.getErrno(rc)) {
|
||||
0 => continue,
|
||||
posix.EINTR => continue,
|
||||
posix.EAGAIN => continue,
|
||||
if (use_pthreads) {
|
||||
const err = c.pthread_join(self.data.handle, null);
|
||||
switch (err) {
|
||||
0 => {},
|
||||
posix.EINVAL => unreachable,
|
||||
posix.ESRCH => unreachable,
|
||||
posix.EDEADLK => unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
if (self.allocator) |a| {
|
||||
a.free(self.stack);
|
||||
assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
|
||||
} else switch (builtin.os) {
|
||||
builtin.Os.linux => {
|
||||
while (true) {
|
||||
const pid_value = @atomicLoad(i32, &self.data.pid, builtin.AtomicOrder.SeqCst);
|
||||
if (pid_value == 0) break;
|
||||
const rc = linux.futex_wait(@ptrToInt(&self.data.pid), linux.FUTEX_WAIT, pid_value, null);
|
||||
switch (linux.getErrno(rc)) {
|
||||
0 => continue,
|
||||
posix.EINTR => continue,
|
||||
posix.EAGAIN => continue,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
|
||||
},
|
||||
builtin.Os.windows => {
|
||||
assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
|
||||
assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0);
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -2385,38 +2504,91 @@ pub const SpawnThreadError = error {
|
||||
/// be copied.
|
||||
SystemResources,
|
||||
|
||||
/// Not enough userland memory to spawn the thread.
|
||||
OutOfMemory,
|
||||
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub const SpawnThreadAllocatorError = SpawnThreadError || error{OutOfMemory};
|
||||
|
||||
/// caller must call wait on the returned thread
|
||||
/// fn startFn(@typeOf(context)) T
|
||||
/// where T is u8, noreturn, void, or !void
|
||||
pub fn spawnThreadAllocator(allocator: &mem.Allocator, context: var, comptime startFn: var) SpawnThreadAllocatorError!&Thread {
|
||||
/// caller must call wait on the returned thread
|
||||
pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread {
|
||||
// TODO compile-time call graph analysis to determine stack upper bound
|
||||
// https://github.com/zig-lang/zig/issues/157
|
||||
const default_stack_size = 8 * 1024 * 1024;
|
||||
const stack_bytes = try allocator.alloc(u8, default_stack_size);
|
||||
const thread = try spawnThread(stack_bytes, context, startFn);
|
||||
thread.allocator = allocator;
|
||||
return thread;
|
||||
}
|
||||
|
||||
/// stack must be big enough to store one Thread and one @typeOf(context), each with default alignment, at the end
|
||||
/// fn startFn(@typeOf(context)) T
|
||||
/// where T is u8, noreturn, void, or !void
|
||||
/// caller must call wait on the returned thread
|
||||
pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThreadError!&Thread {
|
||||
const Context = @typeOf(context);
|
||||
comptime assert(@ArgType(@typeOf(startFn), 0) == Context);
|
||||
|
||||
var stack_end: usize = @ptrToInt(stack.ptr) + stack.len;
|
||||
if (builtin.os == builtin.Os.windows) {
|
||||
const WinThread = struct {
|
||||
const OuterContext = struct {
|
||||
thread: Thread,
|
||||
inner: Context,
|
||||
};
|
||||
extern fn threadMain(arg: windows.LPVOID) windows.DWORD {
|
||||
if (@sizeOf(Context) == 0) {
|
||||
return startFn({});
|
||||
} else {
|
||||
return startFn(*@ptrCast(&Context, @alignCast(@alignOf(Context), arg)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory;
|
||||
const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext);
|
||||
const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory;
|
||||
errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0);
|
||||
const bytes = @ptrCast(&u8, bytes_ptr)[0..byte_count];
|
||||
const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;
|
||||
outer_context.inner = context;
|
||||
outer_context.thread.data.heap_handle = heap_handle;
|
||||
outer_context.thread.data.alloc_start = bytes_ptr;
|
||||
|
||||
const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(&c_void, &outer_context.inner);
|
||||
outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
else => os.unexpectedErrorWindows(err),
|
||||
};
|
||||
};
|
||||
return &outer_context.thread;
|
||||
}
|
||||
|
||||
const MainFuncs = struct {
|
||||
extern fn linuxThreadMain(ctx_addr: usize) u8 {
|
||||
if (@sizeOf(Context) == 0) {
|
||||
return startFn({});
|
||||
} else {
|
||||
return startFn(*@intToPtr(&const Context, ctx_addr));
|
||||
}
|
||||
}
|
||||
extern fn posixThreadMain(ctx: ?&c_void) ?&c_void {
|
||||
if (@sizeOf(Context) == 0) {
|
||||
_ = startFn({});
|
||||
return null;
|
||||
} else {
|
||||
_ = startFn(*@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx)));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
|
||||
|
||||
const mmap_len = default_stack_size;
|
||||
const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
|
||||
if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
|
||||
errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);
|
||||
|
||||
var stack_end: usize = stack_addr + mmap_len;
|
||||
var arg: usize = undefined;
|
||||
if (@sizeOf(Context) != 0) {
|
||||
stack_end -= @sizeOf(Context);
|
||||
stack_end -= stack_end % @alignOf(Context);
|
||||
assert(stack_end >= @ptrToInt(stack.ptr));
|
||||
assert(stack_end >= stack_addr);
|
||||
const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end));
|
||||
*context_ptr = context;
|
||||
arg = stack_end;
|
||||
@ -2424,36 +2596,51 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread
|
||||
|
||||
stack_end -= @sizeOf(Thread);
|
||||
stack_end -= stack_end % @alignOf(Thread);
|
||||
assert(stack_end >= @ptrToInt(stack.ptr));
|
||||
assert(stack_end >= stack_addr);
|
||||
const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end));
|
||||
thread_ptr.stack = stack;
|
||||
thread_ptr.allocator = null;
|
||||
|
||||
const threadMain = struct {
|
||||
extern fn threadMain(ctx_addr: usize) u8 {
|
||||
if (@sizeOf(Context) == 0) {
|
||||
return startFn({});
|
||||
} else {
|
||||
return startFn(*@intToPtr(&const Context, ctx_addr));
|
||||
}
|
||||
thread_ptr.data.stack_addr = stack_addr;
|
||||
thread_ptr.data.stack_len = mmap_len;
|
||||
|
||||
if (builtin.os == builtin.Os.windows) {
|
||||
// use windows API directly
|
||||
@compileError("TODO support spawnThread for Windows");
|
||||
} else if (Thread.use_pthreads) {
|
||||
// use pthreads
|
||||
var attr: c.pthread_attr_t = undefined;
|
||||
if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
|
||||
defer assert(c.pthread_attr_destroy(&attr) == 0);
|
||||
|
||||
// align to page
|
||||
stack_end -= stack_end % os.page_size;
|
||||
assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_end - stack_addr) == 0);
|
||||
|
||||
const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg));
|
||||
switch (err) {
|
||||
0 => return thread_ptr,
|
||||
posix.EAGAIN => return SpawnThreadError.SystemResources,
|
||||
posix.EPERM => unreachable,
|
||||
posix.EINVAL => unreachable,
|
||||
else => return unexpectedErrorPosix(usize(err)),
|
||||
}
|
||||
}.threadMain;
|
||||
|
||||
const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND
|
||||
| posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS
|
||||
| posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
|
||||
const newtls: usize = 0;
|
||||
const rc = posix.clone(threadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return thread_ptr,
|
||||
posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.ENOMEM => return SpawnThreadError.SystemResources,
|
||||
posix.ENOSPC => unreachable,
|
||||
posix.EPERM => unreachable,
|
||||
posix.EUSERS => unreachable,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
} else if (builtin.os == builtin.Os.linux) {
|
||||
// use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
|
||||
const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
|
||||
const newtls: usize = 0;
|
||||
const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return thread_ptr,
|
||||
posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.ENOMEM => return SpawnThreadError.SystemResources,
|
||||
posix.ENOSPC => unreachable,
|
||||
posix.EPERM => unreachable,
|
||||
posix.EUSERS => unreachable,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
} else {
|
||||
@compileError("Unsupported OS");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -706,13 +706,13 @@ pub fn umount2(special: &const u8, flags: u32) usize {
|
||||
return syscall2(SYS_umount2, @ptrToInt(special), flags);
|
||||
}
|
||||
|
||||
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize {
|
||||
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
|
||||
return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
|
||||
@bitCast(usize, offset));
|
||||
}
|
||||
|
||||
pub fn munmap(address: &u8, length: usize) usize {
|
||||
return syscall2(SYS_munmap, @ptrToInt(address), length);
|
||||
pub fn munmap(address: usize, length: usize) usize {
|
||||
return syscall2(SYS_munmap, address, length);
|
||||
}
|
||||
|
||||
pub fn read(fd: i32, buf: &u8, count: usize) usize {
|
||||
|
@ -44,24 +44,12 @@ test "access file" {
|
||||
}
|
||||
|
||||
test "spawn threads" {
|
||||
if (builtin.os != builtin.Os.linux) {
|
||||
// TODO implement threads on macos and windows
|
||||
return;
|
||||
}
|
||||
|
||||
var direct_allocator = std.heap.DirectAllocator.init();
|
||||
defer direct_allocator.deinit();
|
||||
|
||||
var shared_ctx: i32 = 1;
|
||||
|
||||
const thread1 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, {}, start1);
|
||||
const thread4 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, &shared_ctx, start2);
|
||||
|
||||
var stack1: [1024]u8 = undefined;
|
||||
var stack2: [1024]u8 = undefined;
|
||||
|
||||
const thread2 = try std.os.spawnThread(stack1[0..], &shared_ctx, start2);
|
||||
const thread3 = try std.os.spawnThread(stack2[0..], &shared_ctx, start2);
|
||||
const thread1 = try std.os.spawnThread({}, start1);
|
||||
const thread2 = try std.os.spawnThread(&shared_ctx, start2);
|
||||
const thread3 = try std.os.spawnThread(&shared_ctx, start2);
|
||||
const thread4 = try std.os.spawnThread(&shared_ctx, start2);
|
||||
|
||||
thread1.wait();
|
||||
thread2.wait();
|
||||
|
@ -281,7 +281,7 @@ test "os.time.Timer" {
|
||||
debug.assert(time_0 > 0 and time_0 < margin);
|
||||
|
||||
const time_1 = timer.lap();
|
||||
debug.assert(time_1 > time_0);
|
||||
debug.assert(time_1 >= time_0);
|
||||
|
||||
timer.reset();
|
||||
debug.assert(timer.read() < time_1);
|
||||
|
@ -28,6 +28,9 @@ pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lp
|
||||
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(lpSymlinkFileName: LPCSTR, lpTargetFileName: LPCSTR,
|
||||
dwFlags: DWORD) BOOLEAN;
|
||||
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
|
||||
@ -318,6 +321,9 @@ pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
|
||||
pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
|
||||
pub const HEAP_NO_SERIALIZE = 0x00000001;
|
||||
|
||||
pub const PTHREAD_START_ROUTINE = extern fn(LPVOID) DWORD;
|
||||
pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
|
||||
|
||||
test "import" {
|
||||
_ = @import("util.zig");
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
// This file is included in the compilation unit when exporting a library on windows.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
comptime {
|
||||
@export("_DllMainCRTStartup", _DllMainCRTStartup);
|
||||
@export("_DllMainCRTStartup", _DllMainCRTStartup, builtin.GlobalLinkage.Strong);
|
||||
}
|
||||
|
||||
stdcallcc fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD,
|
||||
|
@ -32,10 +32,6 @@ comptime {
|
||||
@export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage);
|
||||
|
||||
@export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage);
|
||||
@export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);
|
||||
|
||||
@export("__udivti3", @import("udivti3.zig").__udivti3, linkage);
|
||||
@export("__umodti3", @import("umodti3.zig").__umodti3, linkage);
|
||||
|
||||
@export("__udivsi3", __udivsi3, linkage);
|
||||
@export("__udivdi3", __udivdi3, linkage);
|
||||
@ -62,9 +58,16 @@ comptime {
|
||||
@export("__chkstk", __chkstk, strong_linkage);
|
||||
@export("___chkstk_ms", ___chkstk_ms, linkage);
|
||||
}
|
||||
@export("__udivti3", @import("udivti3.zig").__udivti3_windows_x86_64, linkage);
|
||||
@export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4_windows_x86_64, linkage);
|
||||
@export("__umodti3", @import("umodti3.zig").__umodti3_windows_x86_64, linkage);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
} else {
|
||||
@export("__udivti3", @import("udivti3.zig").__udivti3, linkage);
|
||||
@export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);
|
||||
@export("__umodti3", @import("umodti3.zig").__umodti3, linkage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,6 +86,16 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setXmm0(comptime T: type, value: T) void {
|
||||
comptime assert(builtin.arch == builtin.Arch.x86_64);
|
||||
const aligned_value: T align(16) = value;
|
||||
asm volatile (
|
||||
\\movaps (%[ptr]), %%xmm0
|
||||
:
|
||||
: [ptr] "r" (&aligned_value)
|
||||
: "xmm0");
|
||||
}
|
||||
|
||||
extern fn __udivdi3(a: u64, b: u64) u64 {
|
||||
@setRuntimeSafety(is_test);
|
||||
return __udivmoddi4(a, b, null);
|
||||
|
@ -1,11 +1,17 @@
|
||||
const udivmod = @import("udivmod.zig").udivmod;
|
||||
const builtin = @import("builtin");
|
||||
const compiler_rt = @import("index.zig");
|
||||
|
||||
pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) u128 {
|
||||
@setRuntimeSafety(builtin.is_test);
|
||||
return udivmod(u128, a, b, maybe_rem);
|
||||
}
|
||||
|
||||
pub extern fn __udivmodti4_windows_x86_64(a: &const u128, b: &const u128, maybe_rem: ?&u128) void {
|
||||
@setRuntimeSafety(builtin.is_test);
|
||||
compiler_rt.setXmm0(u128, udivmod(u128, *a, *b, maybe_rem));
|
||||
}
|
||||
|
||||
test "import udivmodti4" {
|
||||
_ = @import("udivmodti4_test.zig");
|
||||
}
|
||||
|
@ -1,7 +1,12 @@
|
||||
const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4;
|
||||
const udivmodti4 = @import("udivmodti4.zig");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub extern fn __udivti3(a: u128, b: u128) u128 {
|
||||
@setRuntimeSafety(builtin.is_test);
|
||||
return __udivmodti4(a, b, null);
|
||||
return udivmodti4.__udivmodti4(a, b, null);
|
||||
}
|
||||
|
||||
pub extern fn __udivti3_windows_x86_64(a: &const u128, b: &const u128) void {
|
||||
@setRuntimeSafety(builtin.is_test);
|
||||
udivmodti4.__udivmodti4_windows_x86_64(a, b, null);
|
||||
}
|
||||
|
@ -1,9 +1,15 @@
|
||||
const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4;
|
||||
const udivmodti4 = @import("udivmodti4.zig");
|
||||
const builtin = @import("builtin");
|
||||
const compiler_rt = @import("index.zig");
|
||||
|
||||
pub extern fn __umodti3(a: u128, b: u128) u128 {
|
||||
@setRuntimeSafety(builtin.is_test);
|
||||
var r: u128 = undefined;
|
||||
_ = __udivmodti4(a, b, &r);
|
||||
_ = udivmodti4.__udivmodti4(a, b, &r);
|
||||
return r;
|
||||
}
|
||||
|
||||
pub extern fn __umodti3_windows_x86_64(a: &const u128, b: &const u128) void {
|
||||
@setRuntimeSafety(builtin.is_test);
|
||||
compiler_rt.setXmm0(u128, __umodti3(*a, *b));
|
||||
}
|
||||
|
163
std/unicode.zig
163
std/unicode.zig
@ -1,6 +1,16 @@
|
||||
const std = @import("./index.zig");
|
||||
const debug = std.debug;
|
||||
|
||||
/// Returns how many bytes the UTF-8 representation would require
|
||||
/// for the given codepoint.
|
||||
pub fn utf8CodepointSequenceLength(c: u32) !u3 {
|
||||
if (c < 0x80) return u3(1);
|
||||
if (c < 0x800) return u3(2);
|
||||
if (c < 0x10000) return u3(3);
|
||||
if (c < 0x110000) return u3(4);
|
||||
return error.CodepointTooLarge;
|
||||
}
|
||||
|
||||
/// Given the first byte of a UTF-8 codepoint,
|
||||
/// returns a number 1-4 indicating the total length of the codepoint in bytes.
|
||||
/// If this byte does not match the form of a UTF-8 start byte, returns Utf8InvalidStartByte.
|
||||
@ -12,11 +22,47 @@ pub fn utf8ByteSequenceLength(first_byte: u8) !u3 {
|
||||
return error.Utf8InvalidStartByte;
|
||||
}
|
||||
|
||||
/// Encodes the given codepoint into a UTF-8 byte sequence.
|
||||
/// c: the codepoint.
|
||||
/// out: the out buffer to write to. Must have a len >= utf8CodepointSequenceLength(c).
|
||||
/// Errors: if c cannot be encoded in UTF-8.
|
||||
/// Returns: the number of bytes written to out.
|
||||
pub fn utf8Encode(c: u32, out: []u8) !u3 {
|
||||
const length = try utf8CodepointSequenceLength(c);
|
||||
debug.assert(out.len >= length);
|
||||
switch (length) {
|
||||
// The pattern for each is the same
|
||||
// - Increasing the initial shift by 6 each time
|
||||
// - Each time after the first shorten the shifted
|
||||
// value to a max of 0b111111 (63)
|
||||
1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range
|
||||
2 => {
|
||||
out[0] = u8(0b11000000 | (c >> 6));
|
||||
out[1] = u8(0b10000000 | (c & 0b111111));
|
||||
},
|
||||
3 => {
|
||||
if (0xd800 <= c and c <= 0xdfff) return error.Utf8CannotEncodeSurrogateHalf;
|
||||
out[0] = u8(0b11100000 | (c >> 12));
|
||||
out[1] = u8(0b10000000 | ((c >> 6) & 0b111111));
|
||||
out[2] = u8(0b10000000 | (c & 0b111111));
|
||||
},
|
||||
4 => {
|
||||
out[0] = u8(0b11110000 | (c >> 18));
|
||||
out[1] = u8(0b10000000 | ((c >> 12) & 0b111111));
|
||||
out[2] = u8(0b10000000 | ((c >> 6) & 0b111111));
|
||||
out[3] = u8(0b10000000 | (c & 0b111111));
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
const Utf8DecodeError = Utf8Decode2Error || Utf8Decode3Error || Utf8Decode4Error;
|
||||
/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.
|
||||
/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.
|
||||
/// If you already know the length at comptime, you can call one of
|
||||
/// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function.
|
||||
pub fn utf8Decode(bytes: []const u8) !u32 {
|
||||
pub fn utf8Decode(bytes: []const u8) Utf8DecodeError!u32 {
|
||||
return switch (bytes.len) {
|
||||
1 => u32(bytes[0]),
|
||||
2 => utf8Decode2(bytes),
|
||||
@ -25,7 +71,12 @@ pub fn utf8Decode(bytes: []const u8) !u32 {
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
pub fn utf8Decode2(bytes: []const u8) !u32 {
|
||||
|
||||
const Utf8Decode2Error = error{
|
||||
Utf8ExpectedContinuation,
|
||||
Utf8OverlongEncoding,
|
||||
};
|
||||
pub fn utf8Decode2(bytes: []const u8) Utf8Decode2Error!u32 {
|
||||
debug.assert(bytes.len == 2);
|
||||
debug.assert(bytes[0] & 0b11100000 == 0b11000000);
|
||||
var value: u32 = bytes[0] & 0b00011111;
|
||||
@ -38,7 +89,13 @@ pub fn utf8Decode2(bytes: []const u8) !u32 {
|
||||
|
||||
return value;
|
||||
}
|
||||
pub fn utf8Decode3(bytes: []const u8) !u32 {
|
||||
|
||||
const Utf8Decode3Error = error{
|
||||
Utf8ExpectedContinuation,
|
||||
Utf8OverlongEncoding,
|
||||
Utf8EncodesSurrogateHalf,
|
||||
};
|
||||
pub fn utf8Decode3(bytes: []const u8) Utf8Decode3Error!u32 {
|
||||
debug.assert(bytes.len == 3);
|
||||
debug.assert(bytes[0] & 0b11110000 == 0b11100000);
|
||||
var value: u32 = bytes[0] & 0b00001111;
|
||||
@ -56,7 +113,13 @@ pub fn utf8Decode3(bytes: []const u8) !u32 {
|
||||
|
||||
return value;
|
||||
}
|
||||
pub fn utf8Decode4(bytes: []const u8) !u32 {
|
||||
|
||||
const Utf8Decode4Error = error{
|
||||
Utf8ExpectedContinuation,
|
||||
Utf8OverlongEncoding,
|
||||
Utf8CodepointTooLarge,
|
||||
};
|
||||
pub fn utf8Decode4(bytes: []const u8) Utf8Decode4Error!u32 {
|
||||
debug.assert(bytes.len == 4);
|
||||
debug.assert(bytes[0] & 0b11111000 == 0b11110000);
|
||||
var value: u32 = bytes[0] & 0b00000111;
|
||||
@ -158,19 +221,67 @@ const Utf8Iterator = struct {
|
||||
pub fn nextCodepoint(it: &Utf8Iterator) ?u32 {
|
||||
const slice = it.nextCodepointSlice() ?? return null;
|
||||
|
||||
const r = switch (slice.len) {
|
||||
1 => u32(slice[0]),
|
||||
2 => utf8Decode2(slice),
|
||||
3 => utf8Decode3(slice),
|
||||
4 => utf8Decode4(slice),
|
||||
switch (slice.len) {
|
||||
1 => return u32(slice[0]),
|
||||
2 => return utf8Decode2(slice) catch unreachable,
|
||||
3 => return utf8Decode3(slice) catch unreachable,
|
||||
4 => return utf8Decode4(slice) catch unreachable,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
return r catch unreachable;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test "utf8 encode" {
|
||||
comptime testUtf8Encode() catch unreachable;
|
||||
try testUtf8Encode();
|
||||
}
|
||||
fn testUtf8Encode() !void {
|
||||
// A few taken from wikipedia a few taken elsewhere
|
||||
var array: [4]u8 = undefined;
|
||||
debug.assert((try utf8Encode(try utf8Decode("€"), array[0..])) == 3);
|
||||
debug.assert(array[0] == 0b11100010);
|
||||
debug.assert(array[1] == 0b10000010);
|
||||
debug.assert(array[2] == 0b10101100);
|
||||
|
||||
debug.assert((try utf8Encode(try utf8Decode("$"), array[0..])) == 1);
|
||||
debug.assert(array[0] == 0b00100100);
|
||||
|
||||
debug.assert((try utf8Encode(try utf8Decode("¢"), array[0..])) == 2);
|
||||
debug.assert(array[0] == 0b11000010);
|
||||
debug.assert(array[1] == 0b10100010);
|
||||
|
||||
debug.assert((try utf8Encode(try utf8Decode("𐍈"), array[0..])) == 4);
|
||||
debug.assert(array[0] == 0b11110000);
|
||||
debug.assert(array[1] == 0b10010000);
|
||||
debug.assert(array[2] == 0b10001101);
|
||||
debug.assert(array[3] == 0b10001000);
|
||||
}
|
||||
|
||||
test "utf8 encode error" {
|
||||
comptime testUtf8EncodeError();
|
||||
testUtf8EncodeError();
|
||||
}
|
||||
fn testUtf8EncodeError() void {
|
||||
var array: [4]u8 = undefined;
|
||||
testErrorEncode(0xd800, array[0..], error.Utf8CannotEncodeSurrogateHalf);
|
||||
testErrorEncode(0xdfff, array[0..], error.Utf8CannotEncodeSurrogateHalf);
|
||||
testErrorEncode(0x110000, array[0..], error.CodepointTooLarge);
|
||||
testErrorEncode(0xffffffff, array[0..], error.CodepointTooLarge);
|
||||
}
|
||||
|
||||
fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void {
|
||||
if (utf8Encode(codePoint, array)) |_| {
|
||||
unreachable;
|
||||
} else |err| {
|
||||
debug.assert(err == expectedErr);
|
||||
}
|
||||
}
|
||||
|
||||
test "utf8 iterator on ascii" {
|
||||
comptime testUtf8IteratorOnAscii();
|
||||
testUtf8IteratorOnAscii();
|
||||
}
|
||||
fn testUtf8IteratorOnAscii() void {
|
||||
const s = Utf8View.initComptime("abc");
|
||||
|
||||
var it1 = s.iterator();
|
||||
@ -187,6 +298,10 @@ test "utf8 iterator on ascii" {
|
||||
}
|
||||
|
||||
test "utf8 view bad" {
|
||||
comptime testUtf8ViewBad();
|
||||
testUtf8ViewBad();
|
||||
}
|
||||
fn testUtf8ViewBad() void {
|
||||
// Compile-time error.
|
||||
// const s3 = Utf8View.initComptime("\xfe\xf2");
|
||||
|
||||
@ -195,6 +310,10 @@ test "utf8 view bad" {
|
||||
}
|
||||
|
||||
test "utf8 view ok" {
|
||||
comptime testUtf8ViewOk();
|
||||
testUtf8ViewOk();
|
||||
}
|
||||
fn testUtf8ViewOk() void {
|
||||
const s = Utf8View.initComptime("東京市");
|
||||
|
||||
var it1 = s.iterator();
|
||||
@ -211,6 +330,10 @@ test "utf8 view ok" {
|
||||
}
|
||||
|
||||
test "bad utf8 slice" {
|
||||
comptime testBadUtf8Slice();
|
||||
testBadUtf8Slice();
|
||||
}
|
||||
fn testBadUtf8Slice() void {
|
||||
debug.assert(utf8ValidateSlice("abc"));
|
||||
debug.assert(!utf8ValidateSlice("abc\xc0"));
|
||||
debug.assert(!utf8ValidateSlice("abc\xc0abc"));
|
||||
@ -218,6 +341,10 @@ test "bad utf8 slice" {
|
||||
}
|
||||
|
||||
test "valid utf8" {
|
||||
comptime testValidUtf8();
|
||||
testValidUtf8();
|
||||
}
|
||||
fn testValidUtf8() void {
|
||||
testValid("\x00", 0x0);
|
||||
testValid("\x20", 0x20);
|
||||
testValid("\x7f", 0x7f);
|
||||
@ -233,6 +360,10 @@ test "valid utf8" {
|
||||
}
|
||||
|
||||
test "invalid utf8 continuation bytes" {
|
||||
comptime testInvalidUtf8ContinuationBytes();
|
||||
testInvalidUtf8ContinuationBytes();
|
||||
}
|
||||
fn testInvalidUtf8ContinuationBytes() void {
|
||||
// unexpected continuation
|
||||
testError("\x80", error.Utf8InvalidStartByte);
|
||||
testError("\xbf", error.Utf8InvalidStartByte);
|
||||
@ -261,6 +392,10 @@ test "invalid utf8 continuation bytes" {
|
||||
}
|
||||
|
||||
test "overlong utf8 codepoint" {
|
||||
comptime testOverlongUtf8Codepoint();
|
||||
testOverlongUtf8Codepoint();
|
||||
}
|
||||
fn testOverlongUtf8Codepoint() void {
|
||||
testError("\xc0\x80", error.Utf8OverlongEncoding);
|
||||
testError("\xc1\xbf", error.Utf8OverlongEncoding);
|
||||
testError("\xe0\x80\x80", error.Utf8OverlongEncoding);
|
||||
@ -270,6 +405,10 @@ test "overlong utf8 codepoint" {
|
||||
}
|
||||
|
||||
test "misc invalid utf8" {
|
||||
comptime testMiscInvalidUtf8();
|
||||
testMiscInvalidUtf8();
|
||||
}
|
||||
fn testMiscInvalidUtf8() void {
|
||||
// codepoint out of bounds
|
||||
testError("\xf4\x90\x80\x80", error.Utf8CodepointTooLarge);
|
||||
testError("\xf7\xbf\xbf\xbf", error.Utf8CodepointTooLarge);
|
||||
|
122
std/zig/ast.zig
122
std/zig/ast.zig
@ -6,6 +6,7 @@ const mem = std.mem;
|
||||
|
||||
pub const Node = struct {
|
||||
id: Id,
|
||||
same_line_comment: ?&Token,
|
||||
|
||||
pub const Id = enum {
|
||||
// Top level
|
||||
@ -34,6 +35,7 @@ pub const Node = struct {
|
||||
VarType,
|
||||
ErrorType,
|
||||
FnProto,
|
||||
PromiseType,
|
||||
|
||||
// Primary expressions
|
||||
IntegerLiteral,
|
||||
@ -57,6 +59,7 @@ pub const Node = struct {
|
||||
|
||||
// Misc
|
||||
LineComment,
|
||||
DocComment,
|
||||
SwitchCase,
|
||||
SwitchElse,
|
||||
Else,
|
||||
@ -66,6 +69,7 @@ pub const Node = struct {
|
||||
StructField,
|
||||
UnionTag,
|
||||
EnumTag,
|
||||
ErrorTag,
|
||||
AsmInput,
|
||||
AsmOutput,
|
||||
AsyncAttribute,
|
||||
@ -73,6 +77,13 @@ pub const Node = struct {
|
||||
FieldInitializer,
|
||||
};
|
||||
|
||||
pub fn cast(base: &Node, comptime T: type) ?&T {
|
||||
if (base.id == comptime typeToId(T)) {
|
||||
return @fieldParentPtr(T, "base", base);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn iterate(base: &Node, index: usize) ?&Node {
|
||||
comptime var i = 0;
|
||||
inline while (i < @memberCount(Id)) : (i += 1) {
|
||||
@ -118,6 +129,7 @@ pub const Node = struct {
|
||||
|
||||
pub const Root = struct {
|
||||
base: Node,
|
||||
doc_comments: ?&DocComment,
|
||||
decls: ArrayList(&Node),
|
||||
eof_token: Token,
|
||||
|
||||
@ -139,7 +151,7 @@ pub const Node = struct {
|
||||
|
||||
pub const VarDecl = struct {
|
||||
base: Node,
|
||||
comments: ?&LineComment,
|
||||
doc_comments: ?&DocComment,
|
||||
visib_token: ?Token,
|
||||
name_token: Token,
|
||||
eq_token: Token,
|
||||
@ -188,6 +200,7 @@ pub const Node = struct {
|
||||
|
||||
pub const Use = struct {
|
||||
base: Node,
|
||||
doc_comments: ?&DocComment,
|
||||
visib_token: ?Token,
|
||||
expr: &Node,
|
||||
semicolon_token: Token,
|
||||
@ -258,7 +271,7 @@ pub const Node = struct {
|
||||
|
||||
const InitArg = union(enum) {
|
||||
None,
|
||||
Enum,
|
||||
Enum: ?&Node,
|
||||
Type: &Node,
|
||||
};
|
||||
|
||||
@ -291,6 +304,7 @@ pub const Node = struct {
|
||||
|
||||
pub const StructField = struct {
|
||||
base: Node,
|
||||
doc_comments: ?&DocComment,
|
||||
visib_token: ?Token,
|
||||
name_token: Token,
|
||||
type_expr: &Node,
|
||||
@ -316,8 +330,10 @@ pub const Node = struct {
|
||||
|
||||
pub const UnionTag = struct {
|
||||
base: Node,
|
||||
doc_comments: ?&DocComment,
|
||||
name_token: Token,
|
||||
type_expr: ?&Node,
|
||||
value_expr: ?&Node,
|
||||
|
||||
pub fn iterate(self: &UnionTag, index: usize) ?&Node {
|
||||
var i = index;
|
||||
@ -327,6 +343,11 @@ pub const Node = struct {
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
if (self.value_expr) |value_expr| {
|
||||
if (i < 1) return value_expr;
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -335,6 +356,9 @@ pub const Node = struct {
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &UnionTag) Token {
|
||||
if (self.value_expr) |value_expr| {
|
||||
return value_expr.lastToken();
|
||||
}
|
||||
if (self.type_expr) |type_expr| {
|
||||
return type_expr.lastToken();
|
||||
}
|
||||
@ -345,6 +369,7 @@ pub const Node = struct {
|
||||
|
||||
pub const EnumTag = struct {
|
||||
base: Node,
|
||||
doc_comments: ?&DocComment,
|
||||
name_token: Token,
|
||||
value: ?&Node,
|
||||
|
||||
@ -372,6 +397,31 @@ pub const Node = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const ErrorTag = struct {
|
||||
base: Node,
|
||||
doc_comments: ?&DocComment,
|
||||
name_token: Token,
|
||||
|
||||
pub fn iterate(self: &ErrorTag, index: usize) ?&Node {
|
||||
var i = index;
|
||||
|
||||
if (self.doc_comments) |comments| {
|
||||
if (i < 1) return &comments.base;
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &ErrorTag) Token {
|
||||
return self.name_token;
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &ErrorTag) Token {
|
||||
return self.name_token;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Identifier = struct {
|
||||
base: Node,
|
||||
token: Token,
|
||||
@ -421,7 +471,7 @@ pub const Node = struct {
|
||||
|
||||
pub const FnProto = struct {
|
||||
base: Node,
|
||||
comments: ?&LineComment,
|
||||
doc_comments: ?&DocComment,
|
||||
visib_token: ?Token,
|
||||
fn_token: Token,
|
||||
name_token: ?Token,
|
||||
@ -494,6 +544,37 @@ pub const Node = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const PromiseType = struct {
|
||||
base: Node,
|
||||
promise_token: Token,
|
||||
result: ?Result,
|
||||
|
||||
pub const Result = struct {
|
||||
arrow_token: Token,
|
||||
return_type: &Node,
|
||||
};
|
||||
|
||||
pub fn iterate(self: &PromiseType, index: usize) ?&Node {
|
||||
var i = index;
|
||||
|
||||
if (self.result) |result| {
|
||||
if (i < 1) return result.return_type;
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &PromiseType) Token {
|
||||
return self.promise_token;
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &PromiseType) Token {
|
||||
if (self.result) |result| return result.return_type.lastToken();
|
||||
return self.promise_token;
|
||||
}
|
||||
};
|
||||
|
||||
pub const ParamDecl = struct {
|
||||
base: Node,
|
||||
comptime_token: ?Token,
|
||||
@ -584,6 +665,7 @@ pub const Node = struct {
|
||||
|
||||
pub const Comptime = struct {
|
||||
base: Node,
|
||||
doc_comments: ?&DocComment,
|
||||
comptime_token: Token,
|
||||
expr: &Node,
|
||||
|
||||
@ -718,7 +800,8 @@ pub const Node = struct {
|
||||
base: Node,
|
||||
switch_token: Token,
|
||||
expr: &Node,
|
||||
cases: ArrayList(&SwitchCase),
|
||||
/// these can be SwitchCase nodes or LineComment nodes
|
||||
cases: ArrayList(&Node),
|
||||
rbrace: Token,
|
||||
|
||||
pub fn iterate(self: &Switch, index: usize) ?&Node {
|
||||
@ -727,7 +810,7 @@ pub const Node = struct {
|
||||
if (i < 1) return self.expr;
|
||||
i -= 1;
|
||||
|
||||
if (i < self.cases.len) return &self.cases.at(i).base;
|
||||
if (i < self.cases.len) return self.cases.at(i);
|
||||
i -= self.cases.len;
|
||||
|
||||
return null;
|
||||
@ -1186,7 +1269,7 @@ pub const Node = struct {
|
||||
ArrayAccess: &Node,
|
||||
Slice: SliceRange,
|
||||
ArrayInitializer: ArrayList(&Node),
|
||||
StructInitializer: ArrayList(&FieldInitializer),
|
||||
StructInitializer: ArrayList(&Node),
|
||||
};
|
||||
|
||||
const CallInfo = struct {
|
||||
@ -1228,7 +1311,7 @@ pub const Node = struct {
|
||||
i -= exprs.len;
|
||||
},
|
||||
Op.StructInitializer => |fields| {
|
||||
if (i < fields.len) return &fields.at(i).base;
|
||||
if (i < fields.len) return fields.at(i);
|
||||
i -= fields.len;
|
||||
},
|
||||
}
|
||||
@ -1337,6 +1420,7 @@ pub const Node = struct {
|
||||
|
||||
pub const Suspend = struct {
|
||||
base: Node,
|
||||
label: ?Token,
|
||||
suspend_token: Token,
|
||||
payload: ?&Node,
|
||||
body: ?&Node,
|
||||
@ -1358,6 +1442,7 @@ pub const Node = struct {
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &Suspend) Token {
|
||||
if (self.label) |label| return label;
|
||||
return self.suspend_token;
|
||||
}
|
||||
|
||||
@ -1715,24 +1800,41 @@ pub const Node = struct {
|
||||
|
||||
pub const LineComment = struct {
|
||||
base: Node,
|
||||
lines: ArrayList(Token),
|
||||
token: Token,
|
||||
|
||||
pub fn iterate(self: &LineComment, index: usize) ?&Node {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &LineComment) Token {
|
||||
return self.lines.at(0);
|
||||
return self.token;
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &LineComment) Token {
|
||||
return self.token;
|
||||
}
|
||||
};
|
||||
|
||||
pub const DocComment = struct {
|
||||
base: Node,
|
||||
lines: ArrayList(Token),
|
||||
|
||||
pub fn iterate(self: &DocComment, index: usize) ?&Node {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &DocComment) Token {
|
||||
return self.lines.at(0);
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &DocComment) Token {
|
||||
return self.lines.at(self.lines.len - 1);
|
||||
}
|
||||
};
|
||||
|
||||
pub const TestDecl = struct {
|
||||
base: Node,
|
||||
comments: ?&LineComment,
|
||||
doc_comments: ?&DocComment,
|
||||
test_token: Token,
|
||||
name: &Node,
|
||||
body_node: &Node,
|
||||
|
1802
std/zig/parser.zig
1802
std/zig/parser.zig
File diff suppressed because it is too large
Load Diff
1139
std/zig/parser_test.zig
Normal file
1139
std/zig/parser_test.zig
Normal file
File diff suppressed because it is too large
Load Diff
@ -40,6 +40,7 @@ pub const Token = struct {
|
||||
KeywordId{.bytes="null", .id = Id.Keyword_null},
|
||||
KeywordId{.bytes="or", .id = Id.Keyword_or},
|
||||
KeywordId{.bytes="packed", .id = Id.Keyword_packed},
|
||||
KeywordId{.bytes="promise", .id = Id.Keyword_promise},
|
||||
KeywordId{.bytes="pub", .id = Id.Keyword_pub},
|
||||
KeywordId{.bytes="resume", .id = Id.Keyword_resume},
|
||||
KeywordId{.bytes="return", .id = Id.Keyword_return},
|
||||
@ -137,6 +138,7 @@ pub const Token = struct {
|
||||
IntegerLiteral,
|
||||
FloatLiteral,
|
||||
LineComment,
|
||||
DocComment,
|
||||
Keyword_align,
|
||||
Keyword_and,
|
||||
Keyword_asm,
|
||||
@ -165,6 +167,7 @@ pub const Token = struct {
|
||||
Keyword_null,
|
||||
Keyword_or,
|
||||
Keyword_packed,
|
||||
Keyword_promise,
|
||||
Keyword_pub,
|
||||
Keyword_resume,
|
||||
Keyword_return,
|
||||
@ -257,7 +260,10 @@ pub const Tokenizer = struct {
|
||||
Asterisk,
|
||||
AsteriskPercent,
|
||||
Slash,
|
||||
LineCommentStart,
|
||||
LineComment,
|
||||
DocCommentStart,
|
||||
DocComment,
|
||||
Zero,
|
||||
IntegerLiteral,
|
||||
IntegerLiteralWithRadix,
|
||||
@ -822,8 +828,8 @@ pub const Tokenizer = struct {
|
||||
|
||||
State.Slash => switch (c) {
|
||||
'/' => {
|
||||
state = State.LineCommentStart;
|
||||
result.id = Token.Id.LineComment;
|
||||
state = State.LineComment;
|
||||
},
|
||||
'=' => {
|
||||
result.id = Token.Id.SlashEqual;
|
||||
@ -835,7 +841,31 @@ pub const Tokenizer = struct {
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.LineComment => switch (c) {
|
||||
State.LineCommentStart => switch (c) {
|
||||
'/' => {
|
||||
state = State.DocCommentStart;
|
||||
},
|
||||
'\n' => break,
|
||||
else => {
|
||||
state = State.LineComment;
|
||||
self.checkLiteralCharacter();
|
||||
},
|
||||
},
|
||||
State.DocCommentStart => switch (c) {
|
||||
'/' => {
|
||||
state = State.LineComment;
|
||||
},
|
||||
'\n' => {
|
||||
result.id = Token.Id.DocComment;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
state = State.DocComment;
|
||||
result.id = Token.Id.DocComment;
|
||||
self.checkLiteralCharacter();
|
||||
},
|
||||
},
|
||||
State.LineComment, State.DocComment => switch (c) {
|
||||
'\n' => break,
|
||||
else => self.checkLiteralCharacter(),
|
||||
},
|
||||
@ -920,8 +950,12 @@ pub const Tokenizer = struct {
|
||||
result.id = id;
|
||||
}
|
||||
},
|
||||
State.LineCommentStart,
|
||||
State.LineComment => {
|
||||
result.id = Token.Id.Eof;
|
||||
result.id = Token.Id.LineComment;
|
||||
},
|
||||
State.DocComment, State.DocCommentStart => {
|
||||
result.id = Token.Id.DocComment;
|
||||
},
|
||||
|
||||
State.NumberDot,
|
||||
@ -1092,41 +1126,77 @@ test "tokenizer - invalid literal/comment characters" {
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
testTokenize("//\x00", []Token.Id {
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
testTokenize("//\x1f", []Token.Id {
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
testTokenize("//\x7f", []Token.Id {
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
}
|
||||
|
||||
test "tokenizer - utf8" {
|
||||
testTokenize("//\xc2\x80", []Token.Id{});
|
||||
testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{});
|
||||
testTokenize("//\xc2\x80", []Token.Id{Token.Id.LineComment});
|
||||
testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{Token.Id.LineComment});
|
||||
}
|
||||
|
||||
test "tokenizer - invalid utf8" {
|
||||
testTokenize("//\x80", []Token.Id{Token.Id.Invalid});
|
||||
testTokenize("//\xbf", []Token.Id{Token.Id.Invalid});
|
||||
testTokenize("//\xf8", []Token.Id{Token.Id.Invalid});
|
||||
testTokenize("//\xff", []Token.Id{Token.Id.Invalid});
|
||||
testTokenize("//\xc2\xc0", []Token.Id{Token.Id.Invalid});
|
||||
testTokenize("//\xe0", []Token.Id{Token.Id.Invalid});
|
||||
testTokenize("//\xf0", []Token.Id{Token.Id.Invalid});
|
||||
testTokenize("//\xf0\x90\x80\xc0", []Token.Id{Token.Id.Invalid});
|
||||
testTokenize("//\x80", []Token.Id{
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
testTokenize("//\xbf", []Token.Id{
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
testTokenize("//\xf8", []Token.Id{
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
testTokenize("//\xff", []Token.Id{
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
testTokenize("//\xc2\xc0", []Token.Id{
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
testTokenize("//\xe0", []Token.Id{
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
testTokenize("//\xf0", []Token.Id{
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
testTokenize("//\xf0\x90\x80\xc0", []Token.Id{
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
}
|
||||
|
||||
test "tokenizer - illegal unicode codepoints" {
|
||||
// unicode newline characters.U+0085, U+2028, U+2029
|
||||
testTokenize("//\xc2\x84", []Token.Id{});
|
||||
testTokenize("//\xc2\x85", []Token.Id{Token.Id.Invalid});
|
||||
testTokenize("//\xc2\x86", []Token.Id{});
|
||||
testTokenize("//\xe2\x80\xa7", []Token.Id{});
|
||||
testTokenize("//\xe2\x80\xa8", []Token.Id{Token.Id.Invalid});
|
||||
testTokenize("//\xe2\x80\xa9", []Token.Id{Token.Id.Invalid});
|
||||
testTokenize("//\xe2\x80\xaa", []Token.Id{});
|
||||
testTokenize("//\xc2\x84", []Token.Id{Token.Id.LineComment});
|
||||
testTokenize("//\xc2\x85", []Token.Id{
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
testTokenize("//\xc2\x86", []Token.Id{Token.Id.LineComment});
|
||||
testTokenize("//\xe2\x80\xa7", []Token.Id{Token.Id.LineComment});
|
||||
testTokenize("//\xe2\x80\xa8", []Token.Id{
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
testTokenize("//\xe2\x80\xa9", []Token.Id{
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Invalid,
|
||||
});
|
||||
testTokenize("//\xe2\x80\xaa", []Token.Id{Token.Id.LineComment});
|
||||
}
|
||||
|
||||
test "tokenizer - string identifier and builtin fns" {
|
||||
@ -1153,11 +1223,36 @@ test "tokenizer - pipe and then invalid" {
|
||||
});
|
||||
}
|
||||
|
||||
test "tokenizer - line comment and doc comment" {
|
||||
testTokenize("//", []Token.Id{Token.Id.LineComment});
|
||||
testTokenize("// a / b", []Token.Id{Token.Id.LineComment});
|
||||
testTokenize("// /", []Token.Id{Token.Id.LineComment});
|
||||
testTokenize("/// a", []Token.Id{Token.Id.DocComment});
|
||||
testTokenize("///", []Token.Id{Token.Id.DocComment});
|
||||
testTokenize("////", []Token.Id{Token.Id.LineComment});
|
||||
}
|
||||
|
||||
test "tokenizer - line comment followed by identifier" {
|
||||
testTokenize(
|
||||
\\ Unexpected,
|
||||
\\ // another
|
||||
\\ Another,
|
||||
, []Token.Id{
|
||||
Token.Id.Identifier,
|
||||
Token.Id.Comma,
|
||||
Token.Id.LineComment,
|
||||
Token.Id.Identifier,
|
||||
Token.Id.Comma,
|
||||
});
|
||||
}
|
||||
|
||||
fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {
|
||||
var tokenizer = Tokenizer.init(source);
|
||||
for (expected_tokens) |expected_token_id| {
|
||||
const token = tokenizer.next();
|
||||
std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id));
|
||||
if (@TagType(Token.Id)(token.id) != @TagType(Token.Id)(expected_token_id)) {
|
||||
std.debug.panic("expected {}, found {}\n", @tagName(@TagType(Token.Id)(expected_token_id)), @tagName(@TagType(Token.Id)(token.id)));
|
||||
}
|
||||
switch (expected_token_id) {
|
||||
Token.Id.StringLiteral => |expected_kind| {
|
||||
std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable });
|
||||
|
@ -41,3 +41,14 @@ fn testBreakContInDefer(x: usize) void {
|
||||
assert(i == 5);
|
||||
}
|
||||
}
|
||||
|
||||
test "defer and labeled break" {
|
||||
var i = usize(0);
|
||||
|
||||
blk: {
|
||||
defer i += 1;
|
||||
break :blk;
|
||||
}
|
||||
|
||||
assert(i == 1);
|
||||
}
|
||||
|
@ -175,3 +175,69 @@ fn baz_1() !i32 {
|
||||
fn quux_1() !i32 {
|
||||
return error.C;
|
||||
}
|
||||
|
||||
|
||||
test "error: fn returning empty error set can be passed as fn returning any error" {
|
||||
entry();
|
||||
comptime entry();
|
||||
}
|
||||
|
||||
fn entry() void {
|
||||
foo2(bar2);
|
||||
}
|
||||
|
||||
fn foo2(f: fn()error!void) void {
|
||||
const x = f();
|
||||
}
|
||||
|
||||
fn bar2() (error{}!void) { }
|
||||
|
||||
|
||||
test "error: Zero sized error set returned with value payload crash" {
|
||||
_ = foo3(0);
|
||||
_ = comptime foo3(0);
|
||||
}
|
||||
|
||||
const Error = error{};
|
||||
fn foo3(b: usize) Error!usize {
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
test "error: Infer error set from literals" {
|
||||
_ = nullLiteral("n") catch |err| handleErrors(err);
|
||||
_ = floatLiteral("n") catch |err| handleErrors(err);
|
||||
_ = intLiteral("n") catch |err| handleErrors(err);
|
||||
_ = comptime nullLiteral("n") catch |err| handleErrors(err);
|
||||
_ = comptime floatLiteral("n") catch |err| handleErrors(err);
|
||||
_ = comptime intLiteral("n") catch |err| handleErrors(err);
|
||||
}
|
||||
|
||||
fn handleErrors(err: var) noreturn {
|
||||
switch (err) {
|
||||
error.T => {}
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
fn nullLiteral(str: []const u8) !?i64 {
|
||||
if (str[0] == 'n')
|
||||
return null;
|
||||
|
||||
return error.T;
|
||||
}
|
||||
|
||||
fn floatLiteral(str: []const u8) !?f64 {
|
||||
if (str[0] == 'n')
|
||||
return 1.0;
|
||||
|
||||
return error.T;
|
||||
}
|
||||
|
||||
fn intLiteral(str: []const u8) !?i64 {
|
||||
if (str[0] == 'n')
|
||||
return 1;
|
||||
|
||||
return error.T;
|
||||
}
|
||||
|
@ -529,3 +529,10 @@ test "comptime shlWithOverflow" {
|
||||
|
||||
assert(ct_shifted == rt_shifted);
|
||||
}
|
||||
|
||||
test "runtime 128 bit integer division" {
|
||||
var a: u128 = 152313999999999991610955792383;
|
||||
var b: u128 = 10000000000000000000;
|
||||
var c = a / b;
|
||||
assert(c == 15231399999);
|
||||
}
|
||||
|
@ -3221,4 +3221,20 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set");
|
||||
|
||||
cases.add("getting return type of generic function",
|
||||
\\fn generic(a: var) void {}
|
||||
\\comptime {
|
||||
\\ _ = @typeOf(generic).ReturnType;
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:3:25: error: ReturnType has not been resolved because 'fn(var)var' is generic");
|
||||
|
||||
cases.add("getting @ArgType of generic function",
|
||||
\\fn generic(a: var) void {}
|
||||
\\comptime {
|
||||
\\ _ = @ArgType(@typeOf(generic), 0);
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user