Merge branch 'master' into llvm9
This commit is contained in:
commit
5f3d59f0ac
@ -6379,7 +6379,7 @@ comptime {
|
|||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|@asyncCall#}
|
{#header_open|@asyncCall#}
|
||||||
<pre>{#syntax#}@asyncCall(frame_buffer: []u8, result_ptr, function_ptr, args: ...) anyframe->T{#endsyntax#}</pre>
|
<pre>{#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: ...) anyframe->T{#endsyntax#}</pre>
|
||||||
<p>
|
<p>
|
||||||
{#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer,
|
{#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer,
|
||||||
which may or may not be an {#link|async function|Async Functions#}.
|
which may or may not be an {#link|async function|Async Functions#}.
|
||||||
@ -6405,7 +6405,7 @@ test "async fn pointer in a struct field" {
|
|||||||
bar: async fn (*i32) void,
|
bar: async fn (*i32) void,
|
||||||
};
|
};
|
||||||
var foo = Foo{ .bar = func };
|
var foo = Foo{ .bar = func };
|
||||||
var bytes: [64]u8 = undefined;
|
var bytes: [64]u8 align(@alignOf(@Frame(func))) = undefined;
|
||||||
const f = @asyncCall(&bytes, {}, foo.bar, &data);
|
const f = @asyncCall(&bytes, {}, foo.bar, &data);
|
||||||
assert(data == 2);
|
assert(data == 2);
|
||||||
resume f;
|
resume f;
|
||||||
@ -7322,17 +7322,22 @@ mem.set(u8, dest, c);{#endsyntax#}</pre>
|
|||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|@newStackCall#}
|
{#header_open|@newStackCall#}
|
||||||
<pre>{#syntax#}@newStackCall(new_stack: []u8, function: var, args: ...) var{#endsyntax#}</pre>
|
<pre>{#syntax#}@newStackCall(new_stack: []align(target_stack_align) u8, function: var, args: ...) var{#endsyntax#}</pre>
|
||||||
<p>
|
<p>
|
||||||
This calls a function, in the same way that invoking an expression with parentheses does. However,
|
This calls a function, in the same way that invoking an expression with parentheses does. However,
|
||||||
instead of using the same stack as the caller, the function uses the stack provided in the {#syntax#}new_stack{#endsyntax#}
|
instead of using the same stack as the caller, the function uses the stack provided in the {#syntax#}new_stack{#endsyntax#}
|
||||||
parameter.
|
parameter.
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
The new stack must be aligned to {#syntax#}target_stack_align{#endsyntax#} bytes. This is a target-specific
|
||||||
|
number. A safe value that will work on all targets is {#syntax#}16{#endsyntax#}. This value can
|
||||||
|
also be obtained by using {#link|@sizeOf#} on the {#link|@Frame#} type of {#link|Async Functions#}.
|
||||||
|
</p>
|
||||||
{#code_begin|test#}
|
{#code_begin|test#}
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
|
||||||
var new_stack_bytes: [1024]u8 = undefined;
|
var new_stack_bytes: [1024]u8 align(16) = undefined;
|
||||||
|
|
||||||
test "calling a function with a new stack" {
|
test "calling a function with a new stack" {
|
||||||
const arg = 1234;
|
const arg = 1234;
|
||||||
@ -9318,6 +9323,13 @@ const c = @cImport({
|
|||||||
<li>Does not support Zig-only pointer attributes such as alignment. Use normal {#link|Pointers#}
|
<li>Does not support Zig-only pointer attributes such as alignment. Use normal {#link|Pointers#}
|
||||||
please!</li>
|
please!</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<p>When a C pointer is pointing to a single struct (not an array), deference the C pointer to
|
||||||
|
access to the struct's fields or member data. That syntax looks like
|
||||||
|
this: </p>
|
||||||
|
<p>{#syntax#}ptr_to_struct.*.struct_member{#endsyntax#}</p>
|
||||||
|
<p>This is comparable to doing {#syntax#}->{#endsyntax#} in C.</p>
|
||||||
|
<p> When a C pointer is pointing to an array of structs, the syntax reverts to this:</p>
|
||||||
|
<p>{#syntax#}ptr_to_struct_array[index].struct_member{#endsyntax#}</p>
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|Exporting a C Library#}
|
{#header_open|Exporting a C Library#}
|
||||||
|
@ -1279,6 +1279,12 @@ struct ZigTypeOpaque {
|
|||||||
struct ZigTypeFnFrame {
|
struct ZigTypeFnFrame {
|
||||||
ZigFn *fn;
|
ZigFn *fn;
|
||||||
ZigType *locals_struct;
|
ZigType *locals_struct;
|
||||||
|
|
||||||
|
// This is set to the type that resolving the frame currently depends on, null if none.
|
||||||
|
// It's for generating a helpful error message.
|
||||||
|
ZigType *resolve_loop_type;
|
||||||
|
AstNode *resolve_loop_src_node;
|
||||||
|
bool reported_loop_err;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ZigTypeAnyFrame {
|
struct ZigTypeAnyFrame {
|
||||||
@ -1396,6 +1402,7 @@ struct ZigFn {
|
|||||||
AstNode *set_cold_node;
|
AstNode *set_cold_node;
|
||||||
const AstNode *inferred_async_node;
|
const AstNode *inferred_async_node;
|
||||||
ZigFn *inferred_async_fn;
|
ZigFn *inferred_async_fn;
|
||||||
|
AstNode *non_async_node;
|
||||||
|
|
||||||
ZigList<GlobalExport> export_list;
|
ZigList<GlobalExport> export_list;
|
||||||
ZigList<IrInstructionCallGen *> call_list;
|
ZigList<IrInstructionCallGen *> call_list;
|
||||||
|
@ -4144,8 +4144,15 @@ void semantic_analyze(CodeGen *g) {
|
|||||||
|
|
||||||
// second pass over functions for detecting async
|
// second pass over functions for detecting async
|
||||||
for (g->fn_defs_index = 0; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) {
|
for (g->fn_defs_index = 0; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) {
|
||||||
ZigFn *fn_entry = g->fn_defs.at(g->fn_defs_index);
|
ZigFn *fn = g->fn_defs.at(g->fn_defs_index);
|
||||||
analyze_fn_async(g, fn_entry, true);
|
analyze_fn_async(g, fn, true);
|
||||||
|
if (fn_is_async(fn) && fn->non_async_node != nullptr) {
|
||||||
|
ErrorMsg *msg = add_node_error(g, fn->proto_node,
|
||||||
|
buf_sprintf("'%s' cannot be async", buf_ptr(&fn->symbol_name)));
|
||||||
|
add_error_note(g, msg, fn->non_async_node,
|
||||||
|
buf_sprintf("required to be non-async here"));
|
||||||
|
add_async_error_notes(g, msg, fn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5190,6 +5197,27 @@ static ZigType *get_async_fn_type(CodeGen *g, ZigType *orig_fn_type) {
|
|||||||
return fn_type;
|
return fn_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void emit_error_notes_for_type_loop(CodeGen *g, ErrorMsg *msg, ZigType *stop_type,
|
||||||
|
ZigType *ty, AstNode *src_node)
|
||||||
|
{
|
||||||
|
ErrorMsg *note = add_error_note(g, msg, src_node,
|
||||||
|
buf_sprintf("when analyzing type '%s' here", buf_ptr(&ty->name)));
|
||||||
|
if (ty == stop_type)
|
||||||
|
return;
|
||||||
|
switch (ty->id) {
|
||||||
|
case ZigTypeIdFnFrame: {
|
||||||
|
ty->data.frame.reported_loop_err = true;
|
||||||
|
ZigType *depending_type = ty->data.frame.resolve_loop_type;
|
||||||
|
if (depending_type == nullptr)
|
||||||
|
return;
|
||||||
|
emit_error_notes_for_type_loop(g, note, stop_type,
|
||||||
|
depending_type, ty->data.frame.resolve_loop_src_node);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
|
static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
|
||||||
Error err;
|
Error err;
|
||||||
|
|
||||||
@ -5199,6 +5227,20 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
|
|||||||
ZigFn *fn = frame_type->data.frame.fn;
|
ZigFn *fn = frame_type->data.frame.fn;
|
||||||
assert(!fn->type_entry->data.fn.is_generic);
|
assert(!fn->type_entry->data.fn.is_generic);
|
||||||
|
|
||||||
|
if (frame_type->data.frame.resolve_loop_type != nullptr) {
|
||||||
|
if (!frame_type->data.frame.reported_loop_err) {
|
||||||
|
frame_type->data.frame.reported_loop_err = true;
|
||||||
|
ErrorMsg *msg = add_node_error(g, fn->proto_node,
|
||||||
|
buf_sprintf("'%s' depends on itself", buf_ptr(&frame_type->name)));
|
||||||
|
emit_error_notes_for_type_loop(g, msg,
|
||||||
|
frame_type,
|
||||||
|
frame_type->data.frame.resolve_loop_type,
|
||||||
|
frame_type->data.frame.resolve_loop_src_node);
|
||||||
|
emit_error_notes_for_ref_stack(g, msg);
|
||||||
|
}
|
||||||
|
return ErrorSemanticAnalyzeFail;
|
||||||
|
}
|
||||||
|
|
||||||
switch (fn->anal_state) {
|
switch (fn->anal_state) {
|
||||||
case FnAnalStateInvalid:
|
case FnAnalStateInvalid:
|
||||||
return ErrorSemanticAnalyzeFail;
|
return ErrorSemanticAnalyzeFail;
|
||||||
@ -5292,6 +5334,10 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
|
|||||||
return ErrorSemanticAnalyzeFail;
|
return ErrorSemanticAnalyzeFail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZigType *callee_frame_type = get_fn_frame_type(g, callee);
|
||||||
|
frame_type->data.frame.resolve_loop_type = callee_frame_type;
|
||||||
|
frame_type->data.frame.resolve_loop_src_node = call->base.source_node;
|
||||||
|
|
||||||
analyze_fn_body(g, callee);
|
analyze_fn_body(g, callee);
|
||||||
if (callee->anal_state == FnAnalStateInvalid) {
|
if (callee->anal_state == FnAnalStateInvalid) {
|
||||||
frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid;
|
frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid;
|
||||||
@ -5301,8 +5347,6 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
|
|||||||
if (!fn_is_async(callee))
|
if (!fn_is_async(callee))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ZigType *callee_frame_type = get_fn_frame_type(g, callee);
|
|
||||||
|
|
||||||
IrInstructionAllocaGen *alloca_gen = allocate<IrInstructionAllocaGen>(1);
|
IrInstructionAllocaGen *alloca_gen = allocate<IrInstructionAllocaGen>(1);
|
||||||
alloca_gen->base.id = IrInstructionIdAllocaGen;
|
alloca_gen->base.id = IrInstructionIdAllocaGen;
|
||||||
alloca_gen->base.source_node = call->base.source_node;
|
alloca_gen->base.source_node = call->base.source_node;
|
||||||
@ -5371,9 +5415,13 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frame_type->data.frame.resolve_loop_type = child_type;
|
||||||
|
frame_type->data.frame.resolve_loop_src_node = instruction->base.source_node;
|
||||||
if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) {
|
if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *name;
|
const char *name;
|
||||||
if (*instruction->name_hint == 0) {
|
if (*instruction->name_hint == 0) {
|
||||||
name = buf_ptr(buf_sprintf("@local%" ZIG_PRI_usize, alloca_i));
|
name = buf_ptr(buf_sprintf("@local%" ZIG_PRI_usize, alloca_i));
|
||||||
|
137
src/ir.cpp
137
src/ir.cpp
@ -6743,6 +6743,25 @@ static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_t
|
|||||||
return ErrorNone;
|
return ErrorNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_template) {
|
||||||
|
const char *ptr = buf_ptr(src_template) + tok->start + 2;
|
||||||
|
size_t len = tok->end - tok->start - 2;
|
||||||
|
size_t result = 0;
|
||||||
|
for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) {
|
||||||
|
AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
|
||||||
|
if (buf_eql_mem(asm_output->asm_symbolic_name, ptr, len)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) {
|
||||||
|
AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
|
||||||
|
if (buf_eql_mem(asm_input->asm_symbolic_name, ptr, len)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SIZE_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
|
static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
|
||||||
Error err;
|
Error err;
|
||||||
assert(node->type == NodeTypeAsmExpr);
|
assert(node->type == NodeTypeAsmExpr);
|
||||||
@ -6830,6 +6849,22 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod
|
|||||||
input_list[i] = input_value;
|
input_list[i] = input_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t token_i = 0; token_i < tok_list.length; token_i += 1) {
|
||||||
|
AsmToken asm_token = tok_list.at(token_i);
|
||||||
|
if (asm_token.id == AsmTokenIdVar) {
|
||||||
|
size_t index = find_asm_index(irb->codegen, node, &asm_token, template_buf);
|
||||||
|
if (index == SIZE_MAX) {
|
||||||
|
const char *ptr = buf_ptr(template_buf) + asm_token.start + 2;
|
||||||
|
uint32_t len = asm_token.end - asm_token.start - 2;
|
||||||
|
|
||||||
|
add_node_error(irb->codegen, node,
|
||||||
|
buf_sprintf("could not find '%.*s' in the inputs or outputs.",
|
||||||
|
len, ptr));
|
||||||
|
return irb->codegen->invalid_instruction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ir_build_asm(irb, scope, node, template_buf, tok_list.items, tok_list.length,
|
return ir_build_asm(irb, scope, node, template_buf, tok_list.items, tok_list.length,
|
||||||
input_list, output_types, output_vars, return_count, is_volatile);
|
input_list, output_types, output_vars, return_count, is_volatile);
|
||||||
}
|
}
|
||||||
@ -9485,10 +9520,6 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
|
|||||||
result.id = ConstCastResultIdFnAlign;
|
result.id = ConstCastResultIdFnAlign;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
|
|
||||||
result.id = ConstCastResultIdFnCC;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {
|
if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {
|
||||||
result.id = ConstCastResultIdFnVarArgs;
|
result.id = ConstCastResultIdFnVarArgs;
|
||||||
return result;
|
return result;
|
||||||
@ -9546,6 +9577,11 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
|
||||||
|
// ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok.
|
||||||
|
result.id = ConstCastResultIdFnCC;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11780,8 +11816,11 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
|
|||||||
add_error_note(ira->codegen, parent_msg, source_node,
|
add_error_note(ira->codegen, parent_msg, source_node,
|
||||||
buf_sprintf("only one of the functions is generic"));
|
buf_sprintf("only one of the functions is generic"));
|
||||||
break;
|
break;
|
||||||
|
case ConstCastResultIdFnCC:
|
||||||
|
add_error_note(ira->codegen, parent_msg, source_node,
|
||||||
|
buf_sprintf("calling convention mismatch"));
|
||||||
|
break;
|
||||||
case ConstCastResultIdFnAlign: // TODO
|
case ConstCastResultIdFnAlign: // TODO
|
||||||
case ConstCastResultIdFnCC: // TODO
|
|
||||||
case ConstCastResultIdFnVarArgs: // TODO
|
case ConstCastResultIdFnVarArgs: // TODO
|
||||||
case ConstCastResultIdFnReturnType: // TODO
|
case ConstCastResultIdFnReturnType: // TODO
|
||||||
case ConstCastResultIdFnArgCount: // TODO
|
case ConstCastResultIdFnArgCount: // TODO
|
||||||
@ -11891,6 +11930,21 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
|
|||||||
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop);
|
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const_cast_result.id == ConstCastResultIdFnCC) {
|
||||||
|
ir_assert(value->value.type->id == ZigTypeIdFn, source_instr);
|
||||||
|
// ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok.
|
||||||
|
if (wanted_type->data.fn.fn_type_id.cc == CallingConventionAsync &&
|
||||||
|
actual_type->data.fn.fn_type_id.cc == CallingConventionUnspecified)
|
||||||
|
{
|
||||||
|
ir_assert(value->value.data.x_ptr.special == ConstPtrSpecialFunction, source_instr);
|
||||||
|
ZigFn *fn = value->value.data.x_ptr.data.fn.fn_entry;
|
||||||
|
if (fn->inferred_async_node == nullptr) {
|
||||||
|
fn->inferred_async_node = source_instr->source_node;
|
||||||
|
}
|
||||||
|
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// cast from T to ?T
|
// cast from T to ?T
|
||||||
// note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism
|
// note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism
|
||||||
if (wanted_type->id == ZigTypeIdOptional) {
|
if (wanted_type->id == ZigTypeIdOptional) {
|
||||||
@ -12110,9 +12164,28 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
|
|||||||
array_type->data.array.child_type, source_node,
|
array_type->data.array.child_type, source_node,
|
||||||
!slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
|
!slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
|
||||||
{
|
{
|
||||||
|
// If the pointers both have ABI align, it works.
|
||||||
|
bool ok_align = slice_ptr_type->data.pointer.explicit_alignment == 0 &&
|
||||||
|
actual_type->data.pointer.explicit_alignment == 0;
|
||||||
|
if (!ok_align) {
|
||||||
|
// If either one has non ABI align, we have to resolve them both
|
||||||
|
if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type,
|
||||||
|
ResolveStatusAlignmentKnown)))
|
||||||
|
{
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
|
}
|
||||||
|
if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type,
|
||||||
|
ResolveStatusAlignmentKnown)))
|
||||||
|
{
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
|
}
|
||||||
|
ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type);
|
||||||
|
}
|
||||||
|
if (ok_align) {
|
||||||
return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type, result_loc);
|
return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type, result_loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// *@Frame(func) to anyframe->T or anyframe
|
// *@Frame(func) to anyframe->T or anyframe
|
||||||
if (actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle &&
|
if (actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle &&
|
||||||
@ -13902,8 +13975,7 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *
|
|||||||
uint64_t old_array_len = array_type->data.array.len;
|
uint64_t old_array_len = array_type->data.array.len;
|
||||||
uint64_t new_array_len;
|
uint64_t new_array_len;
|
||||||
|
|
||||||
if (mul_u64_overflow(old_array_len, mult_amt, &new_array_len))
|
if (mul_u64_overflow(old_array_len, mult_amt, &new_array_len)) {
|
||||||
{
|
|
||||||
ir_add_error(ira, &instruction->base, buf_sprintf("operation results in overflow"));
|
ir_add_error(ira, &instruction->base, buf_sprintf("operation results in overflow"));
|
||||||
return ira->codegen->invalid_instruction;
|
return ira->codegen->invalid_instruction;
|
||||||
}
|
}
|
||||||
@ -13918,6 +13990,15 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (type_has_one_possible_value(ira->codegen, result->value.type)) {
|
||||||
|
case OnePossibleValueInvalid:
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
|
case OnePossibleValueYes:
|
||||||
|
return result;
|
||||||
|
case OnePossibleValueNo:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO optimize the buf case
|
// TODO optimize the buf case
|
||||||
expand_undef_array(ira->codegen, array_val);
|
expand_undef_array(ira->codegen, array_val);
|
||||||
out_val->data.x_array.data.s_none.elements = create_const_vals(new_array_len);
|
out_val->data.x_array.data.s_none.elements = create_const_vals(new_array_len);
|
||||||
@ -13925,8 +14006,11 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *
|
|||||||
uint64_t i = 0;
|
uint64_t i = 0;
|
||||||
for (uint64_t x = 0; x < mult_amt; x += 1) {
|
for (uint64_t x = 0; x < mult_amt; x += 1) {
|
||||||
for (uint64_t y = 0; y < old_array_len; y += 1) {
|
for (uint64_t y = 0; y < old_array_len; y += 1) {
|
||||||
copy_const_val(&out_val->data.x_array.data.s_none.elements[i],
|
ConstExprValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i];
|
||||||
&array_val->data.x_array.data.s_none.elements[y], false);
|
copy_const_val(elem_dest_val, &array_val->data.x_array.data.s_none.elements[y], false);
|
||||||
|
elem_dest_val->parent.id = ConstParentIdArray;
|
||||||
|
elem_dest_val->parent.data.p_array.array_val = out_val;
|
||||||
|
elem_dest_val->parent.data.p_array.elem_index = i;
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -14753,6 +14837,9 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe
|
|||||||
return ira->codegen->invalid_instruction;
|
return ira->codegen->invalid_instruction;
|
||||||
}
|
}
|
||||||
uint64_t parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type);
|
uint64_t parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type);
|
||||||
|
if ((err = type_resolve(ira->codegen, value_type, ResolveStatusAlignmentKnown))) {
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
|
}
|
||||||
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value_type,
|
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value_type,
|
||||||
parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle,
|
parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle,
|
||||||
parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero);
|
parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero);
|
||||||
@ -15141,6 +15228,20 @@ no_mem_slot:
|
|||||||
return var_ptr_instruction;
|
return var_ptr_instruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function is called when a comptime value becomes accessible at runtime.
|
||||||
|
static void mark_comptime_value_escape(IrAnalyze *ira, IrInstruction *source_instr, ConstExprValue *val) {
|
||||||
|
ir_assert(value_is_comptime(val), source_instr);
|
||||||
|
if (val->special == ConstValSpecialUndef)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (val->type->id == ZigTypeIdFn && val->type->data.fn.fn_type_id.cc == CallingConventionUnspecified) {
|
||||||
|
ir_assert(val->data.x_ptr.special == ConstPtrSpecialFunction, source_instr);
|
||||||
|
if (val->data.x_ptr.data.fn.fn_entry->non_async_node == nullptr) {
|
||||||
|
val->data.x_ptr.data.fn.fn_entry->non_async_node = source_instr->source_node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr,
|
static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr,
|
||||||
IrInstruction *ptr, IrInstruction *uncasted_value, bool allow_write_through_const)
|
IrInstruction *ptr, IrInstruction *uncasted_value, bool allow_write_through_const)
|
||||||
{
|
{
|
||||||
@ -15237,6 +15338,10 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (instr_is_comptime(value)) {
|
||||||
|
mark_comptime_value_escape(ira, source_instr, &value->value);
|
||||||
|
}
|
||||||
|
|
||||||
IrInstructionStorePtr *store_ptr = ir_build_store_ptr(&ira->new_irb, source_instr->scope,
|
IrInstructionStorePtr *store_ptr = ir_build_store_ptr(&ira->new_irb, source_instr->scope,
|
||||||
source_instr->source_node, ptr, value);
|
source_instr->source_node, ptr, value);
|
||||||
return &store_ptr->base;
|
return &store_ptr->base;
|
||||||
@ -15421,7 +15526,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
|
|||||||
IrInstruction *casted_new_stack = nullptr;
|
IrInstruction *casted_new_stack = nullptr;
|
||||||
if (call_instruction->new_stack != nullptr) {
|
if (call_instruction->new_stack != nullptr) {
|
||||||
ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
|
ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
|
||||||
false, false, PtrLenUnknown, 0, 0, 0, false);
|
false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false);
|
||||||
ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
|
ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
|
||||||
IrInstruction *new_stack = call_instruction->new_stack->child;
|
IrInstruction *new_stack = call_instruction->new_stack->child;
|
||||||
if (type_is_invalid(new_stack->value.type))
|
if (type_is_invalid(new_stack->value.type))
|
||||||
@ -17123,7 +17228,7 @@ static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name,
|
|||||||
buf_sprintf("dependency on dynamic library '%s' requires enabling Position Independent Code",
|
buf_sprintf("dependency on dynamic library '%s' requires enabling Position Independent Code",
|
||||||
buf_ptr(lib_name)));
|
buf_ptr(lib_name)));
|
||||||
add_error_note(ira->codegen, msg, source_node,
|
add_error_note(ira->codegen, msg, source_node,
|
||||||
buf_sprintf("fixed by `--library %s` or `--enable-pic`", buf_ptr(lib_name)));
|
buf_sprintf("fixed by `--library %s` or `-fPIC`", buf_ptr(lib_name)));
|
||||||
ira->codegen->reported_bad_link_libc_error = true;
|
ira->codegen->reported_bad_link_libc_error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20841,6 +20946,12 @@ static IrInstruction *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstruction
|
|||||||
if (!ir_resolve_atomic_order(ira, order_value, &order))
|
if (!ir_resolve_atomic_order(ira, order_value, &order))
|
||||||
return ira->codegen->invalid_instruction;
|
return ira->codegen->invalid_instruction;
|
||||||
|
|
||||||
|
if (order < AtomicOrderAcquire) {
|
||||||
|
ir_add_error(ira, order_value,
|
||||||
|
buf_sprintf("atomic ordering must be Acquire or stricter"));
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
|
}
|
||||||
|
|
||||||
IrInstruction *result = ir_build_fence(&ira->new_irb,
|
IrInstruction *result = ir_build_fence(&ira->new_irb,
|
||||||
instruction->base.scope, instruction->base.source_node, order_value, order);
|
instruction->base.scope, instruction->base.source_node, order_value, order);
|
||||||
result->value.type = ira->codegen->builtin_types.entry_void;
|
result->value.type = ira->codegen->builtin_types.entry_void;
|
||||||
@ -24491,7 +24602,11 @@ static IrInstruction *ir_analyze_instruction_bit_cast_src(IrAnalyze *ira, IrInst
|
|||||||
if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)))
|
if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)))
|
||||||
return result_loc;
|
return result_loc;
|
||||||
|
|
||||||
|
if (instruction->result_loc_bit_cast->parent->gen_instruction != nullptr) {
|
||||||
return instruction->result_loc_bit_cast->parent->gen_instruction;
|
return instruction->result_loc_bit_cast->parent->gen_instruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result_loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_analyze_instruction_union_init_named_field(IrAnalyze *ira,
|
static IrInstruction *ir_analyze_instruction_union_init_named_field(IrAnalyze *ira,
|
||||||
|
@ -1755,7 +1755,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
|
|||||||
|
|
||||||
|
|
||||||
// libc dep
|
// libc dep
|
||||||
if (g->libc_link_lib != nullptr) {
|
if (g->libc_link_lib != nullptr && g->out_type != OutTypeObj) {
|
||||||
if (g->libc != nullptr) {
|
if (g->libc != nullptr) {
|
||||||
if (!g->have_dynamic_link) {
|
if (!g->have_dynamic_link) {
|
||||||
lj->args.append("--start-group");
|
lj->args.append("--start-group");
|
||||||
|
@ -55,6 +55,7 @@ pub extern "c" fn fclose(stream: *FILE) c_int;
|
|||||||
pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;
|
pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;
|
||||||
pub extern "c" fn fread(ptr: [*]u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;
|
pub extern "c" fn fread(ptr: [*]u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;
|
||||||
|
|
||||||
|
pub extern "c" fn printf(format: [*]const u8, ...) c_int;
|
||||||
pub extern "c" fn abort() noreturn;
|
pub extern "c" fn abort() noreturn;
|
||||||
pub extern "c" fn exit(code: c_int) noreturn;
|
pub extern "c" fn exit(code: c_int) noreturn;
|
||||||
pub extern "c" fn isatty(fd: fd_t) c_int;
|
pub extern "c" fn isatty(fd: fd_t) c_int;
|
||||||
@ -64,10 +65,12 @@ pub extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int;
|
|||||||
pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int;
|
pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int;
|
||||||
pub extern "c" fn lseek(fd: fd_t, offset: isize, whence: c_int) isize;
|
pub extern "c" fn lseek(fd: fd_t, offset: isize, whence: c_int) isize;
|
||||||
pub extern "c" fn open(path: [*]const u8, oflag: c_uint, ...) c_int;
|
pub extern "c" fn open(path: [*]const u8, oflag: c_uint, ...) c_int;
|
||||||
|
pub extern "c" fn openat(fd: c_int, path: [*]const u8, oflag: c_uint, ...) c_int;
|
||||||
pub extern "c" fn raise(sig: c_int) c_int;
|
pub extern "c" fn raise(sig: c_int) c_int;
|
||||||
pub extern "c" fn read(fd: fd_t, buf: [*]u8, nbyte: usize) isize;
|
pub extern "c" fn read(fd: fd_t, buf: [*]u8, nbyte: usize) isize;
|
||||||
pub extern "c" fn pread(fd: fd_t, buf: [*]u8, nbyte: usize, offset: u64) isize;
|
pub extern "c" fn pread(fd: fd_t, buf: [*]u8, nbyte: usize, offset: u64) isize;
|
||||||
pub extern "c" fn preadv(fd: c_int, iov: [*]const iovec, iovcnt: c_uint, offset: usize) isize;
|
pub extern "c" fn preadv(fd: c_int, iov: [*]const iovec, iovcnt: c_uint, offset: usize) isize;
|
||||||
|
pub extern "c" fn writev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint) isize;
|
||||||
pub extern "c" fn pwritev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint, offset: usize) isize;
|
pub extern "c" fn pwritev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint, offset: usize) isize;
|
||||||
pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int;
|
pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int;
|
||||||
pub extern "c" fn write(fd: fd_t, buf: [*]const u8, nbyte: usize) isize;
|
pub extern "c" fn write(fd: fd_t, buf: [*]const u8, nbyte: usize) isize;
|
||||||
@ -112,7 +115,6 @@ pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, fl
|
|||||||
pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int;
|
pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int;
|
||||||
pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;
|
pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;
|
||||||
pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize;
|
pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize;
|
||||||
pub extern "c" fn openat(fd: c_int, path: [*]const u8, flags: c_int) c_int;
|
|
||||||
pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int;
|
pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int;
|
||||||
pub extern "c" fn setuid(uid: c_uint) c_int;
|
pub extern "c" fn setuid(uid: c_uint) c_int;
|
||||||
pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int;
|
pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int;
|
||||||
|
@ -6,4 +6,4 @@ pub const _errno = __error;
|
|||||||
|
|
||||||
pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;
|
pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;
|
||||||
pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
|
pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
|
||||||
pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int;
|
pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;
|
||||||
|
@ -7,7 +7,7 @@ pub const _errno = __errno_location;
|
|||||||
|
|
||||||
pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize));
|
pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize));
|
||||||
|
|
||||||
pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int;
|
pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;
|
||||||
pub extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int;
|
pub extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int;
|
||||||
pub extern "c" fn eventfd(initval: c_uint, flags: c_uint) c_int;
|
pub extern "c" fn eventfd(initval: c_uint, flags: c_uint) c_int;
|
||||||
pub extern "c" fn epoll_ctl(epfd: fd_t, op: c_uint, fd: fd_t, event: ?*epoll_event) c_int;
|
pub extern "c" fn epoll_ctl(epfd: fd_t, op: c_uint, fd: fd_t, event: ?*epoll_event) c_int;
|
||||||
|
135
std/event/fs.zig
135
std/event/fs.zig
@ -23,6 +23,7 @@ pub const Request = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const Msg = union(enum) {
|
pub const Msg = union(enum) {
|
||||||
|
WriteV: WriteV,
|
||||||
PWriteV: PWriteV,
|
PWriteV: PWriteV,
|
||||||
PReadV: PReadV,
|
PReadV: PReadV,
|
||||||
Open: Open,
|
Open: Open,
|
||||||
@ -30,6 +31,14 @@ pub const Request = struct {
|
|||||||
WriteFile: WriteFile,
|
WriteFile: WriteFile,
|
||||||
End, // special - means the fs thread should exit
|
End, // special - means the fs thread should exit
|
||||||
|
|
||||||
|
pub const WriteV = struct {
|
||||||
|
fd: fd_t,
|
||||||
|
iov: []const os.iovec_const,
|
||||||
|
result: Error!void,
|
||||||
|
|
||||||
|
pub const Error = os.WriteError;
|
||||||
|
};
|
||||||
|
|
||||||
pub const PWriteV = struct {
|
pub const PWriteV = struct {
|
||||||
fd: fd_t,
|
fd: fd_t,
|
||||||
iov: []const os.iovec_const,
|
iov: []const os.iovec_const,
|
||||||
@ -77,7 +86,7 @@ pub const Request = struct {
|
|||||||
pub const PWriteVError = error{OutOfMemory} || File.WriteError;
|
pub const PWriteVError = error{OutOfMemory} || File.WriteError;
|
||||||
|
|
||||||
/// data - just the inner references - must live until pwritev frame completes.
|
/// data - just the inner references - must live until pwritev frame completes.
|
||||||
pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void {
|
pub fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void {
|
||||||
switch (builtin.os) {
|
switch (builtin.os) {
|
||||||
.macosx,
|
.macosx,
|
||||||
.linux,
|
.linux,
|
||||||
@ -94,31 +103,31 @@ pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: us
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return await (async pwritevPosix(loop, fd, iovecs, offset) catch unreachable);
|
return pwritevPosix(loop, fd, iovecs, offset);
|
||||||
},
|
},
|
||||||
.windows => {
|
.windows => {
|
||||||
const data_copy = try std.mem.dupe(loop.allocator, []const u8, data);
|
const data_copy = try std.mem.dupe(loop.allocator, []const u8, data);
|
||||||
defer loop.allocator.free(data_copy);
|
defer loop.allocator.free(data_copy);
|
||||||
return await (async pwritevWindows(loop, fd, data, offset) catch unreachable);
|
return pwritevWindows(loop, fd, data, offset);
|
||||||
},
|
},
|
||||||
else => @compileError("Unsupported OS"),
|
else => @compileError("Unsupported OS"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// data must outlive the returned frame
|
/// data must outlive the returned frame
|
||||||
pub async fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void {
|
pub fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void {
|
||||||
if (data.len == 0) return;
|
if (data.len == 0) return;
|
||||||
if (data.len == 1) return await (async pwriteWindows(loop, fd, data[0], offset) catch unreachable);
|
if (data.len == 1) return pwriteWindows(loop, fd, data[0], offset);
|
||||||
|
|
||||||
// TODO do these in parallel
|
// TODO do these in parallel
|
||||||
var off = offset;
|
var off = offset;
|
||||||
for (data) |buf| {
|
for (data) |buf| {
|
||||||
try await (async pwriteWindows(loop, fd, buf, off) catch unreachable);
|
try pwriteWindows(loop, fd, buf, off);
|
||||||
off += buf.len;
|
off += buf.len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void {
|
pub fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void {
|
||||||
var resume_node = Loop.ResumeNode.Basic{
|
var resume_node = Loop.ResumeNode.Basic{
|
||||||
.base = Loop.ResumeNode{
|
.base = Loop.ResumeNode{
|
||||||
.id = Loop.ResumeNode.Id.Basic,
|
.id = Loop.ResumeNode.Id.Basic,
|
||||||
@ -158,7 +167,7 @@ pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// iovecs must live until pwritev frame completes.
|
/// iovecs must live until pwritev frame completes.
|
||||||
pub async fn pwritevPosix(
|
pub fn pwritevPosix(
|
||||||
loop: *Loop,
|
loop: *Loop,
|
||||||
fd: fd_t,
|
fd: fd_t,
|
||||||
iovecs: []const os.iovec_const,
|
iovecs: []const os.iovec_const,
|
||||||
@ -195,10 +204,44 @@ pub async fn pwritevPosix(
|
|||||||
return req_node.data.msg.PWriteV.result;
|
return req_node.data.msg.PWriteV.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// iovecs must live until pwritev frame completes.
|
||||||
|
pub fn writevPosix(
|
||||||
|
loop: *Loop,
|
||||||
|
fd: fd_t,
|
||||||
|
iovecs: []const os.iovec_const,
|
||||||
|
) os.WriteError!void {
|
||||||
|
var req_node = RequestNode{
|
||||||
|
.prev = null,
|
||||||
|
.next = null,
|
||||||
|
.data = Request{
|
||||||
|
.msg = Request.Msg{
|
||||||
|
.WriteV = Request.Msg.WriteV{
|
||||||
|
.fd = fd,
|
||||||
|
.iov = iovecs,
|
||||||
|
.result = undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.finish = Request.Finish{
|
||||||
|
.TickNode = Loop.NextTickNode{
|
||||||
|
.prev = null,
|
||||||
|
.next = null,
|
||||||
|
.data = @frame(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
suspend {
|
||||||
|
loop.posixFsRequest(&req_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return req_node.data.msg.WriteV.result;
|
||||||
|
}
|
||||||
|
|
||||||
pub const PReadVError = error{OutOfMemory} || File.ReadError;
|
pub const PReadVError = error{OutOfMemory} || File.ReadError;
|
||||||
|
|
||||||
/// data - just the inner references - must live until preadv frame completes.
|
/// data - just the inner references - must live until preadv frame completes.
|
||||||
pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize {
|
pub fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize {
|
||||||
assert(data.len != 0);
|
assert(data.len != 0);
|
||||||
switch (builtin.os) {
|
switch (builtin.os) {
|
||||||
.macosx,
|
.macosx,
|
||||||
@ -216,21 +259,21 @@ pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PR
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return await (async preadvPosix(loop, fd, iovecs, offset) catch unreachable);
|
return preadvPosix(loop, fd, iovecs, offset);
|
||||||
},
|
},
|
||||||
.windows => {
|
.windows => {
|
||||||
const data_copy = try std.mem.dupe(loop.allocator, []u8, data);
|
const data_copy = try std.mem.dupe(loop.allocator, []u8, data);
|
||||||
defer loop.allocator.free(data_copy);
|
defer loop.allocator.free(data_copy);
|
||||||
return await (async preadvWindows(loop, fd, data_copy, offset) catch unreachable);
|
return preadvWindows(loop, fd, data_copy, offset);
|
||||||
},
|
},
|
||||||
else => @compileError("Unsupported OS"),
|
else => @compileError("Unsupported OS"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// data must outlive the returned frame
|
/// data must outlive the returned frame
|
||||||
pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize {
|
pub fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize {
|
||||||
assert(data.len != 0);
|
assert(data.len != 0);
|
||||||
if (data.len == 1) return await (async preadWindows(loop, fd, data[0], offset) catch unreachable);
|
if (data.len == 1) return preadWindows(loop, fd, data[0], offset);
|
||||||
|
|
||||||
// TODO do these in parallel?
|
// TODO do these in parallel?
|
||||||
var off: usize = 0;
|
var off: usize = 0;
|
||||||
@ -238,7 +281,7 @@ pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u6
|
|||||||
var inner_off: usize = 0;
|
var inner_off: usize = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
const v = data[iov_i];
|
const v = data[iov_i];
|
||||||
const amt_read = try await (async preadWindows(loop, fd, v[inner_off .. v.len - inner_off], offset + off) catch unreachable);
|
const amt_read = try preadWindows(loop, fd, v[inner_off .. v.len - inner_off], offset + off);
|
||||||
off += amt_read;
|
off += amt_read;
|
||||||
inner_off += amt_read;
|
inner_off += amt_read;
|
||||||
if (inner_off == v.len) {
|
if (inner_off == v.len) {
|
||||||
@ -252,7 +295,7 @@ pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u6
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize {
|
pub fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize {
|
||||||
var resume_node = Loop.ResumeNode.Basic{
|
var resume_node = Loop.ResumeNode.Basic{
|
||||||
.base = Loop.ResumeNode{
|
.base = Loop.ResumeNode{
|
||||||
.id = Loop.ResumeNode.Id.Basic,
|
.id = Loop.ResumeNode.Id.Basic,
|
||||||
@ -291,7 +334,7 @@ pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// iovecs must live until preadv frame completes
|
/// iovecs must live until preadv frame completes
|
||||||
pub async fn preadvPosix(
|
pub fn preadvPosix(
|
||||||
loop: *Loop,
|
loop: *Loop,
|
||||||
fd: fd_t,
|
fd: fd_t,
|
||||||
iovecs: []const os.iovec,
|
iovecs: []const os.iovec,
|
||||||
@ -328,7 +371,7 @@ pub async fn preadvPosix(
|
|||||||
return req_node.data.msg.PReadV.result;
|
return req_node.data.msg.PReadV.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn openPosix(
|
pub fn openPosix(
|
||||||
loop: *Loop,
|
loop: *Loop,
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
flags: u32,
|
flags: u32,
|
||||||
@ -367,11 +410,11 @@ pub async fn openPosix(
|
|||||||
return req_node.data.msg.Open.result;
|
return req_node.data.msg.Open.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t {
|
pub fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t {
|
||||||
switch (builtin.os) {
|
switch (builtin.os) {
|
||||||
.macosx, .linux, .freebsd, .netbsd => {
|
.macosx, .linux, .freebsd, .netbsd => {
|
||||||
const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;
|
const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;
|
||||||
return await (async openPosix(loop, path, flags, File.default_mode) catch unreachable);
|
return openPosix(loop, path, flags, File.default_mode);
|
||||||
},
|
},
|
||||||
|
|
||||||
.windows => return windows.CreateFile(
|
.windows => return windows.CreateFile(
|
||||||
@ -390,12 +433,12 @@ pub async fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t {
|
|||||||
|
|
||||||
/// Creates if does not exist. Truncates the file if it exists.
|
/// Creates if does not exist. Truncates the file if it exists.
|
||||||
/// Uses the default mode.
|
/// Uses the default mode.
|
||||||
pub async fn openWrite(loop: *Loop, path: []const u8) File.OpenError!fd_t {
|
pub fn openWrite(loop: *Loop, path: []const u8) File.OpenError!fd_t {
|
||||||
return await (async openWriteMode(loop, path, File.default_mode) catch unreachable);
|
return openWriteMode(loop, path, File.default_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates if does not exist. Truncates the file if it exists.
|
/// Creates if does not exist. Truncates the file if it exists.
|
||||||
pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenError!fd_t {
|
pub fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenError!fd_t {
|
||||||
switch (builtin.os) {
|
switch (builtin.os) {
|
||||||
.macosx,
|
.macosx,
|
||||||
.linux,
|
.linux,
|
||||||
@ -403,7 +446,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.
|
|||||||
.netbsd,
|
.netbsd,
|
||||||
=> {
|
=> {
|
||||||
const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC;
|
const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC;
|
||||||
return await (async openPosix(loop, path, flags, File.default_mode) catch unreachable);
|
return openPosix(loop, path, flags, File.default_mode);
|
||||||
},
|
},
|
||||||
.windows => return windows.CreateFile(
|
.windows => return windows.CreateFile(
|
||||||
path,
|
path,
|
||||||
@ -419,7 +462,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates if does not exist. Does not truncate.
|
/// Creates if does not exist. Does not truncate.
|
||||||
pub async fn openReadWrite(
|
pub fn openReadWrite(
|
||||||
loop: *Loop,
|
loop: *Loop,
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
mode: File.Mode,
|
mode: File.Mode,
|
||||||
@ -427,7 +470,7 @@ pub async fn openReadWrite(
|
|||||||
switch (builtin.os) {
|
switch (builtin.os) {
|
||||||
.macosx, .linux, .freebsd, .netbsd => {
|
.macosx, .linux, .freebsd, .netbsd => {
|
||||||
const flags = os.O_LARGEFILE | os.O_RDWR | os.O_CREAT | os.O_CLOEXEC;
|
const flags = os.O_LARGEFILE | os.O_RDWR | os.O_CREAT | os.O_CLOEXEC;
|
||||||
return await (async openPosix(loop, path, flags, mode) catch unreachable);
|
return openPosix(loop, path, flags, mode);
|
||||||
},
|
},
|
||||||
|
|
||||||
.windows => return windows.CreateFile(
|
.windows => return windows.CreateFile(
|
||||||
@ -576,24 +619,24 @@ pub const CloseOperation = struct {
|
|||||||
|
|
||||||
/// contents must remain alive until writeFile completes.
|
/// contents must remain alive until writeFile completes.
|
||||||
/// TODO make this atomic or provide writeFileAtomic and rename this one to writeFileTruncate
|
/// TODO make this atomic or provide writeFileAtomic and rename this one to writeFileTruncate
|
||||||
pub async fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void {
|
pub fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void {
|
||||||
return await (async writeFileMode(loop, path, contents, File.default_mode) catch unreachable);
|
return writeFileMode(loop, path, contents, File.default_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// contents must remain alive until writeFile completes.
|
/// contents must remain alive until writeFile completes.
|
||||||
pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void {
|
pub fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void {
|
||||||
switch (builtin.os) {
|
switch (builtin.os) {
|
||||||
.linux,
|
.linux,
|
||||||
.macosx,
|
.macosx,
|
||||||
.freebsd,
|
.freebsd,
|
||||||
.netbsd,
|
.netbsd,
|
||||||
=> return await (async writeFileModeThread(loop, path, contents, mode) catch unreachable),
|
=> return writeFileModeThread(loop, path, contents, mode),
|
||||||
.windows => return await (async writeFileWindows(loop, path, contents) catch unreachable),
|
.windows => return writeFileWindows(loop, path, contents),
|
||||||
else => @compileError("Unsupported OS"),
|
else => @compileError("Unsupported OS"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void {
|
fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void {
|
||||||
const handle = try windows.CreateFile(
|
const handle = try windows.CreateFile(
|
||||||
path,
|
path,
|
||||||
windows.GENERIC_WRITE,
|
windows.GENERIC_WRITE,
|
||||||
@ -605,10 +648,10 @@ async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !
|
|||||||
);
|
);
|
||||||
defer os.close(handle);
|
defer os.close(handle);
|
||||||
|
|
||||||
try await (async pwriteWindows(loop, handle, contents, 0) catch unreachable);
|
try pwriteWindows(loop, handle, contents, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void {
|
fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void {
|
||||||
const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
|
const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
|
||||||
defer loop.allocator.free(path_with_null);
|
defer loop.allocator.free(path_with_null);
|
||||||
|
|
||||||
@ -646,11 +689,11 @@ async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8
|
|||||||
/// The frame resumes when the last data has been confirmed written, but before the file handle
|
/// The frame resumes when the last data has been confirmed written, but before the file handle
|
||||||
/// is closed.
|
/// is closed.
|
||||||
/// Caller owns returned memory.
|
/// Caller owns returned memory.
|
||||||
pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 {
|
pub fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 {
|
||||||
var close_op = try CloseOperation.start(loop);
|
var close_op = try CloseOperation.start(loop);
|
||||||
defer close_op.finish();
|
defer close_op.finish();
|
||||||
|
|
||||||
const fd = try await (async openRead(loop, file_path) catch unreachable);
|
const fd = try openRead(loop, file_path);
|
||||||
close_op.setHandle(fd);
|
close_op.setHandle(fd);
|
||||||
|
|
||||||
var list = std.ArrayList(u8).init(loop.allocator);
|
var list = std.ArrayList(u8).init(loop.allocator);
|
||||||
@ -660,7 +703,7 @@ pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8
|
|||||||
try list.ensureCapacity(list.len + mem.page_size);
|
try list.ensureCapacity(list.len + mem.page_size);
|
||||||
const buf = list.items[list.len..];
|
const buf = list.items[list.len..];
|
||||||
const buf_array = [_][]u8{buf};
|
const buf_array = [_][]u8{buf};
|
||||||
const amt = try await (async preadv(loop, fd, buf_array, list.len) catch unreachable);
|
const amt = try preadv(loop, fd, buf_array, list.len);
|
||||||
list.len += amt;
|
list.len += amt;
|
||||||
if (list.len > max_size) {
|
if (list.len > max_size) {
|
||||||
return error.FileTooBig;
|
return error.FileTooBig;
|
||||||
@ -1273,11 +1316,11 @@ const test_tmp_dir = "std_event_fs_test";
|
|||||||
// return result;
|
// return result;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void {
|
fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void {
|
||||||
result.* = await (async testFsWatch(loop) catch unreachable);
|
result.* = testFsWatch(loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn testFsWatch(loop: *Loop) !void {
|
fn testFsWatch(loop: *Loop) !void {
|
||||||
const file_path = try std.fs.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" });
|
const file_path = try std.fs.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" });
|
||||||
defer loop.allocator.free(file_path);
|
defer loop.allocator.free(file_path);
|
||||||
|
|
||||||
@ -1288,27 +1331,27 @@ async fn testFsWatch(loop: *Loop) !void {
|
|||||||
const line2_offset = 7;
|
const line2_offset = 7;
|
||||||
|
|
||||||
// first just write then read the file
|
// first just write then read the file
|
||||||
try await try async writeFile(loop, file_path, contents);
|
try writeFile(loop, file_path, contents);
|
||||||
|
|
||||||
const read_contents = try await try async readFile(loop, file_path, 1024 * 1024);
|
const read_contents = try readFile(loop, file_path, 1024 * 1024);
|
||||||
testing.expectEqualSlices(u8, contents, read_contents);
|
testing.expectEqualSlices(u8, contents, read_contents);
|
||||||
|
|
||||||
// now watch the file
|
// now watch the file
|
||||||
var watch = try Watch(void).create(loop, 0);
|
var watch = try Watch(void).create(loop, 0);
|
||||||
defer watch.destroy();
|
defer watch.destroy();
|
||||||
|
|
||||||
testing.expect((try await try async watch.addFile(file_path, {})) == null);
|
testing.expect((try watch.addFile(file_path, {})) == null);
|
||||||
|
|
||||||
const ev = try async watch.channel.get();
|
const ev = async watch.channel.get();
|
||||||
var ev_consumed = false;
|
var ev_consumed = false;
|
||||||
defer if (!ev_consumed) await ev;
|
defer if (!ev_consumed) await ev;
|
||||||
|
|
||||||
// overwrite line 2
|
// overwrite line 2
|
||||||
const fd = try await try async openReadWrite(loop, file_path, File.default_mode);
|
const fd = try await openReadWrite(loop, file_path, File.default_mode);
|
||||||
{
|
{
|
||||||
defer os.close(fd);
|
defer os.close(fd);
|
||||||
|
|
||||||
try await try async pwritev(loop, fd, []const []const u8{"lorem ipsum"}, line2_offset);
|
try pwritev(loop, fd, []const []const u8{"lorem ipsum"}, line2_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
ev_consumed = true;
|
ev_consumed = true;
|
||||||
@ -1316,7 +1359,7 @@ async fn testFsWatch(loop: *Loop) !void {
|
|||||||
WatchEventId.CloseWrite => {},
|
WatchEventId.CloseWrite => {},
|
||||||
WatchEventId.Delete => @panic("wrong event"),
|
WatchEventId.Delete => @panic("wrong event"),
|
||||||
}
|
}
|
||||||
const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024);
|
const contents_updated = try readFile(loop, file_path, 1024 * 1024);
|
||||||
testing.expectEqualSlices(u8,
|
testing.expectEqualSlices(u8,
|
||||||
\\line 1
|
\\line 1
|
||||||
\\lorem ipsum
|
\\lorem ipsum
|
||||||
|
@ -97,28 +97,27 @@ test "std.event.Future" {
|
|||||||
loop.run();
|
loop.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn testFuture(loop: *Loop) void {
|
fn testFuture(loop: *Loop) void {
|
||||||
var future = Future(i32).init(loop);
|
var future = Future(i32).init(loop);
|
||||||
|
|
||||||
var a = async waitOnFuture(&future);
|
var a = async waitOnFuture(&future);
|
||||||
var b = async waitOnFuture(&future);
|
var b = async waitOnFuture(&future);
|
||||||
var c = async resolveFuture(&future);
|
resolveFuture(&future);
|
||||||
|
|
||||||
// TODO make this work:
|
// TODO https://github.com/ziglang/zig/issues/3077
|
||||||
//const result = (await a) + (await b);
|
//const result = (await a) + (await b);
|
||||||
const a_result = await a;
|
const a_result = await a;
|
||||||
const b_result = await b;
|
const b_result = await b;
|
||||||
const result = a_result + b_result;
|
const result = a_result + b_result;
|
||||||
|
|
||||||
await c;
|
|
||||||
testing.expect(result == 12);
|
testing.expect(result == 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn waitOnFuture(future: *Future(i32)) i32 {
|
fn waitOnFuture(future: *Future(i32)) i32 {
|
||||||
return future.get().*;
|
return future.get().*;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn resolveFuture(future: *Future(i32)) void {
|
fn resolveFuture(future: *Future(i32)) void {
|
||||||
future.data = 6;
|
future.data = 6;
|
||||||
future.resolve();
|
future.resolve();
|
||||||
}
|
}
|
||||||
|
@ -89,12 +89,15 @@ pub const Loop = struct {
|
|||||||
pub const IoMode = enum {
|
pub const IoMode = enum {
|
||||||
blocking,
|
blocking,
|
||||||
evented,
|
evented,
|
||||||
|
mixed,
|
||||||
};
|
};
|
||||||
pub const io_mode: IoMode = if (@hasDecl(root, "io_mode")) root.io_mode else IoMode.blocking;
|
pub const io_mode: IoMode = if (@hasDecl(root, "io_mode")) root.io_mode else IoMode.blocking;
|
||||||
var global_instance_state: Loop = undefined;
|
var global_instance_state: Loop = undefined;
|
||||||
|
threadlocal var per_thread_instance: ?*Loop = null;
|
||||||
const default_instance: ?*Loop = switch (io_mode) {
|
const default_instance: ?*Loop = switch (io_mode) {
|
||||||
.blocking => null,
|
.blocking => null,
|
||||||
.evented => &global_instance_state,
|
.evented => &global_instance_state,
|
||||||
|
.mixed => per_thread_instance,
|
||||||
};
|
};
|
||||||
pub const instance: ?*Loop = if (@hasDecl(root, "event_loop")) root.event_loop else default_instance;
|
pub const instance: ?*Loop = if (@hasDecl(root, "event_loop")) root.event_loop else default_instance;
|
||||||
|
|
||||||
@ -146,10 +149,12 @@ pub const Loop = struct {
|
|||||||
.overlapped = ResumeNode.overlapped_init,
|
.overlapped = ResumeNode.overlapped_init,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
// We need at least one of these in case the fs thread wants to use onNextTick
|
||||||
const extra_thread_count = thread_count - 1;
|
const extra_thread_count = thread_count - 1;
|
||||||
|
const resume_node_count = std.math.max(extra_thread_count, 1);
|
||||||
self.eventfd_resume_nodes = try self.allocator.alloc(
|
self.eventfd_resume_nodes = try self.allocator.alloc(
|
||||||
std.atomic.Stack(ResumeNode.EventFd).Node,
|
std.atomic.Stack(ResumeNode.EventFd).Node,
|
||||||
extra_thread_count,
|
resume_node_count,
|
||||||
);
|
);
|
||||||
errdefer self.allocator.free(self.eventfd_resume_nodes);
|
errdefer self.allocator.free(self.eventfd_resume_nodes);
|
||||||
|
|
||||||
@ -194,7 +199,7 @@ pub const Loop = struct {
|
|||||||
eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
|
eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
|
||||||
.data = ResumeNode.EventFd{
|
.data = ResumeNode.EventFd{
|
||||||
.base = ResumeNode{
|
.base = ResumeNode{
|
||||||
.id = ResumeNode.Id.EventFd,
|
.id = .EventFd,
|
||||||
.handle = undefined,
|
.handle = undefined,
|
||||||
.overlapped = ResumeNode.overlapped_init,
|
.overlapped = ResumeNode.overlapped_init,
|
||||||
},
|
},
|
||||||
@ -451,12 +456,12 @@ pub const Loop = struct {
|
|||||||
self.finishOneEvent();
|
self.finishOneEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
|
pub fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
|
||||||
defer self.linuxRemoveFd(fd);
|
defer self.linuxRemoveFd(fd);
|
||||||
suspend {
|
suspend {
|
||||||
var resume_node = ResumeNode.Basic{
|
var resume_node = ResumeNode.Basic{
|
||||||
.base = ResumeNode{
|
.base = ResumeNode{
|
||||||
.id = ResumeNode.Id.Basic,
|
.id = .Basic,
|
||||||
.handle = @frame(),
|
.handle = @frame(),
|
||||||
.overlapped = ResumeNode.overlapped_init,
|
.overlapped = ResumeNode.overlapped_init,
|
||||||
},
|
},
|
||||||
@ -790,12 +795,15 @@ pub const Loop = struct {
|
|||||||
|
|
||||||
fn posixFsRun(self: *Loop) void {
|
fn posixFsRun(self: *Loop) void {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (builtin.os == builtin.Os.linux) {
|
if (builtin.os == .linux) {
|
||||||
_ = @atomicRmw(i32, &self.os_data.fs_queue_item, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
|
_ = @atomicRmw(i32, &self.os_data.fs_queue_item, .Xchg, 0, .SeqCst);
|
||||||
}
|
}
|
||||||
while (self.os_data.fs_queue.get()) |node| {
|
while (self.os_data.fs_queue.get()) |node| {
|
||||||
switch (node.data.msg) {
|
switch (node.data.msg) {
|
||||||
.End => return,
|
.End => return,
|
||||||
|
.WriteV => |*msg| {
|
||||||
|
msg.result = os.writev(msg.fd, msg.iov);
|
||||||
|
},
|
||||||
.PWriteV => |*msg| {
|
.PWriteV => |*msg| {
|
||||||
msg.result = os.pwritev(msg.fd, msg.iov, msg.offset);
|
msg.result = os.pwritev(msg.fd, msg.iov, msg.offset);
|
||||||
},
|
},
|
||||||
@ -827,14 +835,14 @@ pub const Loop = struct {
|
|||||||
self.finishOneEvent();
|
self.finishOneEvent();
|
||||||
}
|
}
|
||||||
switch (builtin.os) {
|
switch (builtin.os) {
|
||||||
builtin.Os.linux => {
|
.linux => {
|
||||||
const rc = os.linux.futex_wait(&self.os_data.fs_queue_item, os.linux.FUTEX_WAIT, 0, null);
|
const rc = os.linux.futex_wait(&self.os_data.fs_queue_item, os.linux.FUTEX_WAIT, 0, null);
|
||||||
switch (os.linux.getErrno(rc)) {
|
switch (os.linux.getErrno(rc)) {
|
||||||
0, os.EINTR, os.EAGAIN => continue,
|
0, os.EINTR, os.EAGAIN => continue,
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => {
|
.macosx, .freebsd, .netbsd => {
|
||||||
const fs_kevs = (*const [1]os.Kevent)(&self.os_data.fs_kevent_wait);
|
const fs_kevs = (*const [1]os.Kevent)(&self.os_data.fs_kevent_wait);
|
||||||
var out_kevs: [1]os.Kevent = undefined;
|
var out_kevs: [1]os.Kevent = undefined;
|
||||||
_ = os.kevent(self.os_data.fs_kqfd, fs_kevs, out_kevs[0..], null) catch unreachable;
|
_ = os.kevent(self.os_data.fs_kqfd, fs_kevs, out_kevs[0..], null) catch unreachable;
|
||||||
|
147
std/fmt.zig
147
std/fmt.zig
@ -69,7 +69,6 @@ pub fn format(
|
|||||||
FormatFillAndAlign,
|
FormatFillAndAlign,
|
||||||
FormatWidth,
|
FormatWidth,
|
||||||
FormatPrecision,
|
FormatPrecision,
|
||||||
Pointer,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime var start_index = 0;
|
comptime var start_index = 0;
|
||||||
@ -109,9 +108,6 @@ pub fn format(
|
|||||||
state = .Start;
|
state = .Start;
|
||||||
start_index = i;
|
start_index = i;
|
||||||
},
|
},
|
||||||
'*' => {
|
|
||||||
state = .Pointer;
|
|
||||||
},
|
|
||||||
':' => {
|
':' => {
|
||||||
state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth;
|
state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth;
|
||||||
specifier_end = i;
|
specifier_end = i;
|
||||||
@ -256,19 +252,6 @@ pub fn format(
|
|||||||
@compileError("Unexpected character in precision value: " ++ [_]u8{c});
|
@compileError("Unexpected character in precision value: " ++ [_]u8{c});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.Pointer => switch (c) {
|
|
||||||
'}' => {
|
|
||||||
const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
|
|
||||||
|
|
||||||
try output(context, @typeName(@typeOf(args[arg_to_print]).Child));
|
|
||||||
try output(context, "@");
|
|
||||||
try formatInt(@ptrToInt(args[arg_to_print]), 16, false, 0, context, Errors, output);
|
|
||||||
|
|
||||||
state = .Start;
|
|
||||||
start_index = i + 1;
|
|
||||||
},
|
|
||||||
else => @compileError("Unexpected format character after '*'"),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
comptime {
|
comptime {
|
||||||
@ -293,12 +276,19 @@ pub fn format(
|
|||||||
pub fn formatType(
|
pub fn formatType(
|
||||||
value: var,
|
value: var,
|
||||||
comptime fmt: []const u8,
|
comptime fmt: []const u8,
|
||||||
comptime options: FormatOptions,
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
max_depth: usize,
|
max_depth: usize,
|
||||||
) Errors!void {
|
) Errors!void {
|
||||||
|
if (comptime std.mem.eql(u8, fmt, "*")) {
|
||||||
|
try output(context, @typeName(@typeOf(value).Child));
|
||||||
|
try output(context, "@");
|
||||||
|
try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, context, Errors, output);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const T = @typeOf(value);
|
const T = @typeOf(value);
|
||||||
switch (@typeInfo(T)) {
|
switch (@typeInfo(T)) {
|
||||||
.ComptimeInt, .Int, .Float => {
|
.ComptimeInt, .Int, .Float => {
|
||||||
@ -438,15 +428,15 @@ pub fn formatType(
|
|||||||
fn formatValue(
|
fn formatValue(
|
||||||
value: var,
|
value: var,
|
||||||
comptime fmt: []const u8,
|
comptime fmt: []const u8,
|
||||||
comptime options: FormatOptions,
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
) Errors!void {
|
) Errors!void {
|
||||||
if (comptime std.mem.eql(u8, fmt, "B")) {
|
if (comptime std.mem.eql(u8, fmt, "B")) {
|
||||||
return formatBytes(value, options.width, 1000, context, Errors, output);
|
return formatBytes(value, options, 1000, context, Errors, output);
|
||||||
} else if (comptime std.mem.eql(u8, fmt, "Bi")) {
|
} else if (comptime std.mem.eql(u8, fmt, "Bi")) {
|
||||||
return formatBytes(value, options.width, 1024, context, Errors, output);
|
return formatBytes(value, options, 1024, context, Errors, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
const T = @typeOf(value);
|
const T = @typeOf(value);
|
||||||
@ -460,7 +450,7 @@ fn formatValue(
|
|||||||
pub fn formatIntValue(
|
pub fn formatIntValue(
|
||||||
value: var,
|
value: var,
|
||||||
comptime fmt: []const u8,
|
comptime fmt: []const u8,
|
||||||
comptime options: FormatOptions,
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
@ -479,7 +469,7 @@ pub fn formatIntValue(
|
|||||||
uppercase = false;
|
uppercase = false;
|
||||||
} else if (comptime std.mem.eql(u8, fmt, "c")) {
|
} else if (comptime std.mem.eql(u8, fmt, "c")) {
|
||||||
if (@typeOf(int_value).bit_count <= 8) {
|
if (@typeOf(int_value).bit_count <= 8) {
|
||||||
return formatAsciiChar(u8(int_value), context, Errors, output);
|
return formatAsciiChar(u8(int_value), options, context, Errors, output);
|
||||||
} else {
|
} else {
|
||||||
@compileError("Cannot print integer that is larger than 8 bits as a ascii");
|
@compileError("Cannot print integer that is larger than 8 bits as a ascii");
|
||||||
}
|
}
|
||||||
@ -496,21 +486,21 @@ pub fn formatIntValue(
|
|||||||
@compileError("Unknown format string: '" ++ fmt ++ "'");
|
@compileError("Unknown format string: '" ++ fmt ++ "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatInt(int_value, radix, uppercase, options.width orelse 0, context, Errors, output);
|
return formatInt(int_value, radix, uppercase, options, context, Errors, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn formatFloatValue(
|
fn formatFloatValue(
|
||||||
value: var,
|
value: var,
|
||||||
comptime fmt: []const u8,
|
comptime fmt: []const u8,
|
||||||
comptime options: FormatOptions,
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
) Errors!void {
|
) Errors!void {
|
||||||
if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) {
|
if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) {
|
||||||
return formatFloatScientific(value, options.precision, context, Errors, output);
|
return formatFloatScientific(value, options, context, Errors, output);
|
||||||
} else if (comptime std.mem.eql(u8, fmt, "d")) {
|
} else if (comptime std.mem.eql(u8, fmt, "d")) {
|
||||||
return formatFloatDecimal(value, options.precision, context, Errors, output);
|
return formatFloatDecimal(value, options, context, Errors, output);
|
||||||
} else {
|
} else {
|
||||||
@compileError("Unknown format string: '" ++ fmt ++ "'");
|
@compileError("Unknown format string: '" ++ fmt ++ "'");
|
||||||
}
|
}
|
||||||
@ -519,7 +509,7 @@ fn formatFloatValue(
|
|||||||
pub fn formatText(
|
pub fn formatText(
|
||||||
bytes: []const u8,
|
bytes: []const u8,
|
||||||
comptime fmt: []const u8,
|
comptime fmt: []const u8,
|
||||||
comptime options: FormatOptions,
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
@ -527,11 +517,10 @@ pub fn formatText(
|
|||||||
if (fmt.len == 0) {
|
if (fmt.len == 0) {
|
||||||
return output(context, bytes);
|
return output(context, bytes);
|
||||||
} else if (comptime std.mem.eql(u8, fmt, "s")) {
|
} else if (comptime std.mem.eql(u8, fmt, "s")) {
|
||||||
if (options.width) |w| return formatBuf(bytes, w, context, Errors, output);
|
return formatBuf(bytes, options, context, Errors, output);
|
||||||
return formatBuf(bytes, 0, context, Errors, output);
|
|
||||||
} else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) {
|
} else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) {
|
||||||
for (bytes) |c| {
|
for (bytes) |c| {
|
||||||
try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output);
|
try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, context, Errors, output);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@ -541,6 +530,7 @@ pub fn formatText(
|
|||||||
|
|
||||||
pub fn formatAsciiChar(
|
pub fn formatAsciiChar(
|
||||||
c: u8,
|
c: u8,
|
||||||
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
@ -550,15 +540,16 @@ pub fn formatAsciiChar(
|
|||||||
|
|
||||||
pub fn formatBuf(
|
pub fn formatBuf(
|
||||||
buf: []const u8,
|
buf: []const u8,
|
||||||
width: usize,
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
) Errors!void {
|
) Errors!void {
|
||||||
try output(context, buf);
|
try output(context, buf);
|
||||||
|
|
||||||
|
const width = options.width orelse 0;
|
||||||
var leftover_padding = if (width > buf.len) (width - buf.len) else return;
|
var leftover_padding = if (width > buf.len) (width - buf.len) else return;
|
||||||
const pad_byte: u8 = ' ';
|
const pad_byte: u8 = options.fill;
|
||||||
while (leftover_padding > 0) : (leftover_padding -= 1) {
|
while (leftover_padding > 0) : (leftover_padding -= 1) {
|
||||||
try output(context, (*const [1]u8)(&pad_byte)[0..1]);
|
try output(context, (*const [1]u8)(&pad_byte)[0..1]);
|
||||||
}
|
}
|
||||||
@ -569,7 +560,7 @@ pub fn formatBuf(
|
|||||||
// same type unambiguously.
|
// same type unambiguously.
|
||||||
pub fn formatFloatScientific(
|
pub fn formatFloatScientific(
|
||||||
value: var,
|
value: var,
|
||||||
maybe_precision: ?usize,
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
@ -591,7 +582,7 @@ pub fn formatFloatScientific(
|
|||||||
if (x == 0.0) {
|
if (x == 0.0) {
|
||||||
try output(context, "0");
|
try output(context, "0");
|
||||||
|
|
||||||
if (maybe_precision) |precision| {
|
if (options.precision) |precision| {
|
||||||
if (precision != 0) {
|
if (precision != 0) {
|
||||||
try output(context, ".");
|
try output(context, ".");
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
@ -610,7 +601,7 @@ pub fn formatFloatScientific(
|
|||||||
var buffer: [32]u8 = undefined;
|
var buffer: [32]u8 = undefined;
|
||||||
var float_decimal = errol.errol3(x, buffer[0..]);
|
var float_decimal = errol.errol3(x, buffer[0..]);
|
||||||
|
|
||||||
if (maybe_precision) |precision| {
|
if (options.precision) |precision| {
|
||||||
errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);
|
errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);
|
||||||
|
|
||||||
try output(context, float_decimal.digits[0..1]);
|
try output(context, float_decimal.digits[0..1]);
|
||||||
@ -650,13 +641,13 @@ pub fn formatFloatScientific(
|
|||||||
if (exp > -10 and exp < 10) {
|
if (exp > -10 and exp < 10) {
|
||||||
try output(context, "0");
|
try output(context, "0");
|
||||||
}
|
}
|
||||||
try formatInt(exp, 10, false, 0, context, Errors, output);
|
try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, context, Errors, output);
|
||||||
} else {
|
} else {
|
||||||
try output(context, "-");
|
try output(context, "-");
|
||||||
if (exp > -10 and exp < 10) {
|
if (exp > -10 and exp < 10) {
|
||||||
try output(context, "0");
|
try output(context, "0");
|
||||||
}
|
}
|
||||||
try formatInt(-exp, 10, false, 0, context, Errors, output);
|
try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, context, Errors, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,7 +655,7 @@ pub fn formatFloatScientific(
|
|||||||
// By default floats are printed at full precision (no rounding).
|
// By default floats are printed at full precision (no rounding).
|
||||||
pub fn formatFloatDecimal(
|
pub fn formatFloatDecimal(
|
||||||
value: var,
|
value: var,
|
||||||
maybe_precision: ?usize,
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
@ -686,7 +677,7 @@ pub fn formatFloatDecimal(
|
|||||||
if (x == 0.0) {
|
if (x == 0.0) {
|
||||||
try output(context, "0");
|
try output(context, "0");
|
||||||
|
|
||||||
if (maybe_precision) |precision| {
|
if (options.precision) |precision| {
|
||||||
if (precision != 0) {
|
if (precision != 0) {
|
||||||
try output(context, ".");
|
try output(context, ".");
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
@ -707,7 +698,7 @@ pub fn formatFloatDecimal(
|
|||||||
var buffer: [32]u8 = undefined;
|
var buffer: [32]u8 = undefined;
|
||||||
var float_decimal = errol.errol3(x, buffer[0..]);
|
var float_decimal = errol.errol3(x, buffer[0..]);
|
||||||
|
|
||||||
if (maybe_precision) |precision| {
|
if (options.precision) |precision| {
|
||||||
errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal);
|
errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal);
|
||||||
|
|
||||||
// exp < 0 means the leading is always 0 as errol result is normalized.
|
// exp < 0 means the leading is always 0 as errol result is normalized.
|
||||||
@ -809,7 +800,7 @@ pub fn formatFloatDecimal(
|
|||||||
|
|
||||||
pub fn formatBytes(
|
pub fn formatBytes(
|
||||||
value: var,
|
value: var,
|
||||||
width: ?usize,
|
options: FormatOptions,
|
||||||
comptime radix: usize,
|
comptime radix: usize,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
@ -833,7 +824,7 @@ pub fn formatBytes(
|
|||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
try formatFloatDecimal(new_value, width, context, Errors, output);
|
try formatFloatDecimal(new_value, options, context, Errors, output);
|
||||||
|
|
||||||
if (suffix == ' ') {
|
if (suffix == ' ') {
|
||||||
return output(context, "B");
|
return output(context, "B");
|
||||||
@ -851,7 +842,7 @@ pub fn formatInt(
|
|||||||
value: var,
|
value: var,
|
||||||
base: u8,
|
base: u8,
|
||||||
uppercase: bool,
|
uppercase: bool,
|
||||||
width: usize,
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
@ -863,9 +854,9 @@ pub fn formatInt(
|
|||||||
value;
|
value;
|
||||||
|
|
||||||
if (@typeOf(int_value).is_signed) {
|
if (@typeOf(int_value).is_signed) {
|
||||||
return formatIntSigned(int_value, base, uppercase, width, context, Errors, output);
|
return formatIntSigned(int_value, base, uppercase, options, context, Errors, output);
|
||||||
} else {
|
} else {
|
||||||
return formatIntUnsigned(int_value, base, uppercase, width, context, Errors, output);
|
return formatIntUnsigned(int_value, base, uppercase, options, context, Errors, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -873,26 +864,30 @@ fn formatIntSigned(
|
|||||||
value: var,
|
value: var,
|
||||||
base: u8,
|
base: u8,
|
||||||
uppercase: bool,
|
uppercase: bool,
|
||||||
width: usize,
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
) Errors!void {
|
) Errors!void {
|
||||||
|
const new_options = FormatOptions{
|
||||||
|
.width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null,
|
||||||
|
.precision = options.precision,
|
||||||
|
.fill = options.fill,
|
||||||
|
};
|
||||||
|
|
||||||
const uint = @IntType(false, @typeOf(value).bit_count);
|
const uint = @IntType(false, @typeOf(value).bit_count);
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
const minus_sign: u8 = '-';
|
const minus_sign: u8 = '-';
|
||||||
try output(context, (*const [1]u8)(&minus_sign)[0..]);
|
try output(context, (*const [1]u8)(&minus_sign)[0..]);
|
||||||
const new_value = @intCast(uint, -(value + 1)) + 1;
|
const new_value = @intCast(uint, -(value + 1)) + 1;
|
||||||
const new_width = if (width == 0) 0 else (width - 1);
|
return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output);
|
||||||
return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output);
|
} else if (options.width == null or options.width.? == 0) {
|
||||||
} else if (width == 0) {
|
return formatIntUnsigned(@intCast(uint, value), base, uppercase, options, context, Errors, output);
|
||||||
return formatIntUnsigned(@intCast(uint, value), base, uppercase, width, context, Errors, output);
|
|
||||||
} else {
|
} else {
|
||||||
const plus_sign: u8 = '+';
|
const plus_sign: u8 = '+';
|
||||||
try output(context, (*const [1]u8)(&plus_sign)[0..]);
|
try output(context, (*const [1]u8)(&plus_sign)[0..]);
|
||||||
const new_value = @intCast(uint, value);
|
const new_value = @intCast(uint, value);
|
||||||
const new_width = if (width == 0) 0 else (width - 1);
|
return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output);
|
||||||
return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,7 +895,7 @@ fn formatIntUnsigned(
|
|||||||
value: var,
|
value: var,
|
||||||
base: u8,
|
base: u8,
|
||||||
uppercase: bool,
|
uppercase: bool,
|
||||||
width: usize,
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
@ -921,31 +916,32 @@ fn formatIntUnsigned(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const digits_buf = buf[index..];
|
const digits_buf = buf[index..];
|
||||||
|
const width = options.width orelse 0;
|
||||||
const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0;
|
const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0;
|
||||||
|
|
||||||
if (padding > index) {
|
if (padding > index) {
|
||||||
const zero_byte: u8 = '0';
|
const zero_byte: u8 = options.fill;
|
||||||
var leftover_padding = padding - index;
|
var leftover_padding = padding - index;
|
||||||
while (true) {
|
while (true) {
|
||||||
try output(context, (*const [1]u8)(&zero_byte)[0..]);
|
try output(context, (*const [1]u8)(&zero_byte)[0..]);
|
||||||
leftover_padding -= 1;
|
leftover_padding -= 1;
|
||||||
if (leftover_padding == 0) break;
|
if (leftover_padding == 0) break;
|
||||||
}
|
}
|
||||||
mem.set(u8, buf[0..index], '0');
|
mem.set(u8, buf[0..index], options.fill);
|
||||||
return output(context, buf);
|
return output(context, buf);
|
||||||
} else {
|
} else {
|
||||||
const padded_buf = buf[index - padding ..];
|
const padded_buf = buf[index - padding ..];
|
||||||
mem.set(u8, padded_buf[0..padding], '0');
|
mem.set(u8, padded_buf[0..padding], options.fill);
|
||||||
return output(context, padded_buf);
|
return output(context, padded_buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width: usize) usize {
|
pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize {
|
||||||
var context = FormatIntBuf{
|
var context = FormatIntBuf{
|
||||||
.out_buf = out_buf,
|
.out_buf = out_buf,
|
||||||
.index = 0,
|
.index = 0,
|
||||||
};
|
};
|
||||||
formatInt(value, base, uppercase, width, &context, error{}, formatIntCallback) catch unreachable;
|
formatInt(value, base, uppercase, options, &context, error{}, formatIntCallback) catch unreachable;
|
||||||
return context.index;
|
return context.index;
|
||||||
}
|
}
|
||||||
const FormatIntBuf = struct {
|
const FormatIntBuf = struct {
|
||||||
@ -1088,23 +1084,23 @@ fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
|
|||||||
test "bufPrintInt" {
|
test "bufPrintInt" {
|
||||||
var buffer: [100]u8 = undefined;
|
var buffer: [100]u8 = undefined;
|
||||||
const buf = buffer[0..];
|
const buf = buffer[0..];
|
||||||
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110"));
|
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, FormatOptions{}), "-101111000110000101001110"));
|
||||||
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678"));
|
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, FormatOptions{}), "-12345678"));
|
||||||
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e"));
|
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, FormatOptions{}), "-bc614e"));
|
||||||
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E"));
|
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, FormatOptions{}), "-BC614E"));
|
||||||
|
|
||||||
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678"));
|
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, FormatOptions{}), "12345678"));
|
||||||
|
|
||||||
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666"));
|
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, FormatOptions{ .width = 6 }), " 666"));
|
||||||
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234"));
|
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, FormatOptions{ .width = 6 }), " 1234"));
|
||||||
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234"));
|
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, FormatOptions{ .width = 1 }), "1234"));
|
||||||
|
|
||||||
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42"));
|
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, FormatOptions{ .width = 3 }), "+42"));
|
||||||
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42"));
|
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, FormatOptions{ .width = 3 }), "-42"));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) []u8 {
|
fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) []u8 {
|
||||||
return buf[0..formatIntBuf(buf, value, base, uppercase, width)];
|
return buf[0..formatIntBuf(buf, value, base, uppercase, options)];
|
||||||
}
|
}
|
||||||
|
|
||||||
test "parse u64 digit too big" {
|
test "parse u64 digit too big" {
|
||||||
@ -1162,7 +1158,8 @@ test "int.specifier" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "int.padded" {
|
test "int.padded" {
|
||||||
try testFmt("u8: '0001'", "u8: '{:4}'", u8(1));
|
try testFmt("u8: ' 1'", "u8: '{:4}'", u8(1));
|
||||||
|
try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", u8(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
test "buffer" {
|
test "buffer" {
|
||||||
@ -1237,7 +1234,7 @@ test "cstr" {
|
|||||||
|
|
||||||
test "filesize" {
|
test "filesize" {
|
||||||
try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
|
try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
|
||||||
try testFmt("file size: 66.06MB\n", "file size: {B:2}\n", usize(63 * 1024 * 1024));
|
try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", usize(63 * 1024 * 1024));
|
||||||
}
|
}
|
||||||
|
|
||||||
test "struct" {
|
test "struct" {
|
||||||
@ -1342,7 +1339,7 @@ test "custom" {
|
|||||||
pub fn format(
|
pub fn format(
|
||||||
self: SelfType,
|
self: SelfType,
|
||||||
comptime fmt: []const u8,
|
comptime fmt: []const u8,
|
||||||
comptime options: FormatOptions,
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
@ -1548,7 +1545,7 @@ test "formatType max_depth" {
|
|||||||
pub fn format(
|
pub fn format(
|
||||||
self: SelfType,
|
self: SelfType,
|
||||||
comptime fmt: []const u8,
|
comptime fmt: []const u8,
|
||||||
comptime options: FormatOptions,
|
options: FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime Errors: type,
|
comptime Errors: type,
|
||||||
output: fn (@typeOf(context), []const u8) Errors!void,
|
output: fn (@typeOf(context), []const u8) Errors!void,
|
||||||
|
13
std/fs.zig
13
std/fs.zig
@ -442,6 +442,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO: separate this API into the one that opens directory handles to then subsequently open
|
||||||
|
/// files, and into the one that reads files from an open directory handle.
|
||||||
pub const Dir = struct {
|
pub const Dir = struct {
|
||||||
handle: Handle,
|
handle: Handle,
|
||||||
allocator: *Allocator,
|
allocator: *Allocator,
|
||||||
@ -564,6 +566,17 @@ pub const Dir = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn openRead(self: Dir, file_path: []const u8) os.OpenError!File {
|
||||||
|
const path_c = try os.toPosixPath(file_path);
|
||||||
|
return self.openReadC(&path_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn openReadC(self: Dir, file_path: [*]const u8) OpenError!File {
|
||||||
|
const flags = os.O_LARGEFILE | os.O_RDONLY;
|
||||||
|
const fd = try os.openatC(self.handle.fd, file_path, flags, 0);
|
||||||
|
return File.openHandle(fd);
|
||||||
|
}
|
||||||
|
|
||||||
fn nextDarwin(self: *Dir) !?Entry {
|
fn nextDarwin(self: *Dir) !?Entry {
|
||||||
start_over: while (true) {
|
start_over: while (true) {
|
||||||
if (self.handle.index >= self.handle.end_index) {
|
if (self.handle.index >= self.handle.end_index) {
|
||||||
|
@ -302,6 +302,14 @@ pub const File = struct {
|
|||||||
return os.write(self.handle, bytes);
|
return os.write(self.handle, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn writev_iovec(self: File, iovecs: []const os.iovec_const) WriteError!void {
|
||||||
|
if (std.event.Loop.instance) |loop| {
|
||||||
|
return std.event.fs.writevPosix(loop, self.handle, iovecs);
|
||||||
|
} else {
|
||||||
|
return os.writev(self.handle, iovecs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn inStream(file: File) InStream {
|
pub fn inStream(file: File) InStream {
|
||||||
return InStream{
|
return InStream{
|
||||||
.file = file,
|
.file = file,
|
||||||
|
@ -146,7 +146,7 @@ pub fn InStream(comptime ReadError: type) type {
|
|||||||
|
|
||||||
/// Same as `readFull` but end of stream returns `error.EndOfStream`.
|
/// Same as `readFull` but end of stream returns `error.EndOfStream`.
|
||||||
pub fn readNoEof(self: *Self, buf: []u8) !void {
|
pub fn readNoEof(self: *Self, buf: []u8) !void {
|
||||||
const amt_read = try self.read(buf);
|
const amt_read = try self.readFull(buf);
|
||||||
if (amt_read < buf.len) return error.EndOfStream;
|
if (amt_read < buf.len) return error.EndOfStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,7 +519,7 @@ pub const Int = struct {
|
|||||||
pub fn format(
|
pub fn format(
|
||||||
self: Int,
|
self: Int,
|
||||||
comptime fmt: []const u8,
|
comptime fmt: []const u8,
|
||||||
comptime options: std.fmt.FormatOptions,
|
options: std.fmt.FormatOptions,
|
||||||
context: var,
|
context: var,
|
||||||
comptime FmtError: type,
|
comptime FmtError: type,
|
||||||
output: fn (@typeOf(context), []const u8) FmtError!void,
|
output: fn (@typeOf(context), []const u8) FmtError!void,
|
||||||
|
30
std/net.zig
30
std/net.zig
@ -215,3 +215,33 @@ test "std.net.parseIp6" {
|
|||||||
assert(addr.addr[1] == 0x01);
|
assert(addr.addr[1] == 0x01);
|
||||||
assert(addr.addr[2] == 0x00);
|
assert(addr.addr[2] == 0x00);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn connectUnixSocket(path: []const u8) !std.fs.File {
|
||||||
|
const opt_non_block = if (std.event.Loop.instance != null) os.SOCK_NONBLOCK else 0;
|
||||||
|
const sockfd = try os.socket(
|
||||||
|
os.AF_UNIX,
|
||||||
|
os.SOCK_STREAM | os.SOCK_CLOEXEC | opt_non_block,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
errdefer os.close(sockfd);
|
||||||
|
|
||||||
|
var sock_addr = os.sockaddr{
|
||||||
|
.un = os.sockaddr_un{
|
||||||
|
.family = os.AF_UNIX,
|
||||||
|
.path = undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (path.len > @typeOf(sock_addr.un.path).len) return error.NameTooLong;
|
||||||
|
mem.copy(u8, sock_addr.un.path[0..], path);
|
||||||
|
const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len);
|
||||||
|
if (std.event.Loop.instance) |loop| {
|
||||||
|
try os.connect_async(sockfd, &sock_addr, size);
|
||||||
|
try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
|
||||||
|
try os.getsockoptError(sockfd);
|
||||||
|
} else {
|
||||||
|
try os.connect(sockfd, &sock_addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std.fs.File.openHandle(sockfd);
|
||||||
|
}
|
||||||
|
119
std/os.zig
119
std/os.zig
@ -99,19 +99,30 @@ pub const GetRandomError = OpenError;
|
|||||||
/// When linking against libc, this calls the
|
/// When linking against libc, this calls the
|
||||||
/// appropriate OS-specific library call. Otherwise it uses the zig standard
|
/// appropriate OS-specific library call. Otherwise it uses the zig standard
|
||||||
/// library implementation.
|
/// library implementation.
|
||||||
pub fn getrandom(buf: []u8) GetRandomError!void {
|
pub fn getrandom(buffer: []u8) GetRandomError!void {
|
||||||
if (windows.is_the_target) {
|
if (windows.is_the_target) {
|
||||||
return windows.RtlGenRandom(buf);
|
return windows.RtlGenRandom(buffer);
|
||||||
}
|
}
|
||||||
if (linux.is_the_target) {
|
if (linux.is_the_target or freebsd.is_the_target) {
|
||||||
while (true) {
|
var buf = buffer;
|
||||||
const err = if (std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok) blk: {
|
const use_c = !linux.is_the_target or
|
||||||
break :blk errno(std.c.getrandom(buf.ptr, buf.len, 0));
|
std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok;
|
||||||
|
|
||||||
|
while (buf.len != 0) {
|
||||||
|
var err: u16 = undefined;
|
||||||
|
|
||||||
|
const num_read = if (use_c) blk: {
|
||||||
|
const rc = std.c.getrandom(buf.ptr, buf.len, 0);
|
||||||
|
err = std.c.getErrno(rc);
|
||||||
|
break :blk @bitCast(usize, rc);
|
||||||
} else blk: {
|
} else blk: {
|
||||||
break :blk linux.getErrno(linux.getrandom(buf.ptr, buf.len, 0));
|
const rc = linux.getrandom(buf.ptr, buf.len, 0);
|
||||||
|
err = linux.getErrno(rc);
|
||||||
|
break :blk rc;
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (err) {
|
switch (err) {
|
||||||
0 => return,
|
0 => buf = buf[num_read..],
|
||||||
EINVAL => unreachable,
|
EINVAL => unreachable,
|
||||||
EFAULT => unreachable,
|
EFAULT => unreachable,
|
||||||
EINTR => continue,
|
EINTR => continue,
|
||||||
@ -119,27 +130,15 @@ pub fn getrandom(buf: []u8) GetRandomError!void {
|
|||||||
else => return unexpectedErrno(err),
|
else => return unexpectedErrno(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return;
|
||||||
if (freebsd.is_the_target) {
|
|
||||||
while (true) {
|
|
||||||
const err = std.c.getErrno(std.c.getrandom(buf.ptr, buf.len, 0));
|
|
||||||
|
|
||||||
switch (err) {
|
|
||||||
0 => return,
|
|
||||||
EINVAL => unreachable,
|
|
||||||
EFAULT => unreachable,
|
|
||||||
EINTR => continue,
|
|
||||||
else => return unexpectedErrno(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (wasi.is_the_target) {
|
if (wasi.is_the_target) {
|
||||||
switch (wasi.random_get(buf.ptr, buf.len)) {
|
switch (wasi.random_get(buffer.ptr, buffer.len)) {
|
||||||
0 => return,
|
0 => return,
|
||||||
else => |err| return unexpectedErrno(err),
|
else => |err| return unexpectedErrno(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getRandomBytesDevURandom(buf);
|
return getRandomBytesDevURandom(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getRandomBytesDevURandom(buf: []u8) !void {
|
fn getRandomBytesDevURandom(buf: []u8) !void {
|
||||||
@ -440,6 +439,33 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
|
|||||||
|
|
||||||
/// Write multiple buffers to a file descriptor. Keeps trying if it gets interrupted.
|
/// Write multiple buffers to a file descriptor. Keeps trying if it gets interrupted.
|
||||||
/// This function is for blocking file descriptors only. For non-blocking, see
|
/// This function is for blocking file descriptors only. For non-blocking, see
|
||||||
|
/// `writevAsync`.
|
||||||
|
pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void {
|
||||||
|
while (true) {
|
||||||
|
// TODO handle the case when iov_len is too large and get rid of this @intCast
|
||||||
|
const rc = system.writev(fd, iov.ptr, @intCast(u32, iov.len));
|
||||||
|
switch (errno(rc)) {
|
||||||
|
0 => return,
|
||||||
|
EINTR => continue,
|
||||||
|
EINVAL => unreachable,
|
||||||
|
EFAULT => unreachable,
|
||||||
|
EAGAIN => unreachable, // This function is for blocking writes.
|
||||||
|
EBADF => unreachable, // Always a race condition.
|
||||||
|
EDESTADDRREQ => unreachable, // `connect` was never called.
|
||||||
|
EDQUOT => return error.DiskQuota,
|
||||||
|
EFBIG => return error.FileTooBig,
|
||||||
|
EIO => return error.InputOutput,
|
||||||
|
ENOSPC => return error.NoSpaceLeft,
|
||||||
|
EPERM => return error.AccessDenied,
|
||||||
|
EPIPE => return error.BrokenPipe,
|
||||||
|
else => |err| return unexpectedErrno(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write multiple buffers to a file descriptor, with a position offset.
|
||||||
|
/// Keeps trying if it gets interrupted.
|
||||||
|
/// This function is for blocking file descriptors only. For non-blocking, see
|
||||||
/// `pwritevAsync`.
|
/// `pwritevAsync`.
|
||||||
pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void {
|
pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void {
|
||||||
if (darwin.is_the_target) {
|
if (darwin.is_the_target) {
|
||||||
@ -524,7 +550,6 @@ pub const OpenError = error{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||||
/// `file_path` needs to be copied in memory to add a null terminating byte.
|
|
||||||
/// See also `openC`.
|
/// See also `openC`.
|
||||||
pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t {
|
pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t {
|
||||||
const file_path_c = try toPosixPath(file_path);
|
const file_path_c = try toPosixPath(file_path);
|
||||||
@ -564,6 +589,47 @@ pub fn openC(file_path: [*]const u8, flags: u32, perm: usize) OpenError!fd_t {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||||
|
/// `file_path` is relative to the open directory handle `dir_fd`.
|
||||||
|
/// See also `openatC`.
|
||||||
|
pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: usize) OpenError!fd_t {
|
||||||
|
const file_path_c = try toPosixPath(file_path);
|
||||||
|
return openatC(dir_fd, &file_path_c, flags, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||||
|
/// `file_path` is relative to the open directory handle `dir_fd`.
|
||||||
|
/// See also `openat`.
|
||||||
|
pub fn openatC(dir_fd: fd_t, file_path: [*]const u8, flags: u32, mode: usize) OpenError!fd_t {
|
||||||
|
while (true) {
|
||||||
|
const rc = system.openat(dir_fd, file_path, flags, mode);
|
||||||
|
switch (errno(rc)) {
|
||||||
|
0 => return @intCast(fd_t, rc),
|
||||||
|
EINTR => continue,
|
||||||
|
|
||||||
|
EFAULT => unreachable,
|
||||||
|
EINVAL => unreachable,
|
||||||
|
EACCES => return error.AccessDenied,
|
||||||
|
EFBIG => return error.FileTooBig,
|
||||||
|
EOVERFLOW => return error.FileTooBig,
|
||||||
|
EISDIR => return error.IsDir,
|
||||||
|
ELOOP => return error.SymLinkLoop,
|
||||||
|
EMFILE => return error.ProcessFdQuotaExceeded,
|
||||||
|
ENAMETOOLONG => return error.NameTooLong,
|
||||||
|
ENFILE => return error.SystemFdQuotaExceeded,
|
||||||
|
ENODEV => return error.NoDevice,
|
||||||
|
ENOENT => return error.FileNotFound,
|
||||||
|
ENOMEM => return error.SystemResources,
|
||||||
|
ENOSPC => return error.NoSpaceLeft,
|
||||||
|
ENOTDIR => return error.NotDir,
|
||||||
|
EPERM => return error.AccessDenied,
|
||||||
|
EEXIST => return error.PathAlreadyExists,
|
||||||
|
EBUSY => return error.DeviceBusy,
|
||||||
|
else => |err| return unexpectedErrno(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
|
pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (errno(system.dup2(old_fd, new_fd))) {
|
switch (errno(system.dup2(old_fd, new_fd))) {
|
||||||
@ -1655,7 +1721,7 @@ pub const ConnectError = error{
|
|||||||
/// For non-blocking, see `connect_async`.
|
/// For non-blocking, see `connect_async`.
|
||||||
pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void {
|
pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void {
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (errno(system.connect(sockfd, sock_addr, @sizeOf(sockaddr)))) {
|
switch (errno(system.connect(sockfd, sock_addr, len))) {
|
||||||
0 => return,
|
0 => return,
|
||||||
EACCES => return error.PermissionDenied,
|
EACCES => return error.PermissionDenied,
|
||||||
EPERM => return error.PermissionDenied,
|
EPERM => return error.PermissionDenied,
|
||||||
@ -1683,7 +1749,8 @@ pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!v
|
|||||||
/// It expects to receive EINPROGRESS`.
|
/// It expects to receive EINPROGRESS`.
|
||||||
pub fn connect_async(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void {
|
pub fn connect_async(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void {
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (errno(system.connect(sockfd, sock_addr, @sizeOf(sockaddr)))) {
|
switch (errno(system.connect(sockfd, sock_addr, len))) {
|
||||||
|
EINVAL => unreachable,
|
||||||
EINTR => continue,
|
EINTR => continue,
|
||||||
0, EINPROGRESS => return,
|
0, EINPROGRESS => return,
|
||||||
EACCES => return error.PermissionDenied,
|
EACCES => return error.PermissionDenied,
|
||||||
|
@ -784,6 +784,7 @@ pub const socklen_t = u32;
|
|||||||
pub const sockaddr = extern union {
|
pub const sockaddr = extern union {
|
||||||
in: sockaddr_in,
|
in: sockaddr_in,
|
||||||
in6: sockaddr_in6,
|
in6: sockaddr_in6,
|
||||||
|
un: sockaddr_un,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const sockaddr_in = extern struct {
|
pub const sockaddr_in = extern struct {
|
||||||
|
@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
|||||||
\\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream;
|
\\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream;
|
||||||
\\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable;
|
\\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable;
|
||||||
\\}
|
\\}
|
||||||
, "Hello, world!\n0012 012 a\n");
|
, "Hello, world!\n 12 12 a\n");
|
||||||
|
|
||||||
cases.addC("number literals",
|
cases.addC("number literals",
|
||||||
\\const builtin = @import("builtin");
|
\\const builtin = @import("builtin");
|
||||||
|
@ -2,6 +2,107 @@ const tests = @import("tests.zig");
|
|||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||||
|
cases.addCase(x: {
|
||||||
|
var tc = cases.create("variable in inline assembly template cannot be found",
|
||||||
|
\\export fn entry() void {
|
||||||
|
\\ var sp = asm volatile (
|
||||||
|
\\ "mov %[foo], sp"
|
||||||
|
\\ : [bar] "=r" (-> usize)
|
||||||
|
\\ );
|
||||||
|
\\}
|
||||||
|
, "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs.");
|
||||||
|
tc.target = tests.Target{
|
||||||
|
.Cross = tests.CrossTarget{
|
||||||
|
.arch = .x86_64,
|
||||||
|
.os = .linux,
|
||||||
|
.abi = .gnu,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
break :x tc;
|
||||||
|
});
|
||||||
|
|
||||||
|
cases.add(
|
||||||
|
"indirect recursion of async functions detected",
|
||||||
|
\\var frame: ?anyframe = null;
|
||||||
|
\\
|
||||||
|
\\export fn a() void {
|
||||||
|
\\ _ = async rangeSum(10);
|
||||||
|
\\ while (frame) |f| resume f;
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
\\fn rangeSum(x: i32) i32 {
|
||||||
|
\\ suspend {
|
||||||
|
\\ frame = @frame();
|
||||||
|
\\ }
|
||||||
|
\\ frame = null;
|
||||||
|
\\
|
||||||
|
\\ if (x == 0) return 0;
|
||||||
|
\\ var child = rangeSumIndirect(x - 1);
|
||||||
|
\\ return child + 1;
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
\\fn rangeSumIndirect(x: i32) i32 {
|
||||||
|
\\ suspend {
|
||||||
|
\\ frame = @frame();
|
||||||
|
\\ }
|
||||||
|
\\ frame = null;
|
||||||
|
\\
|
||||||
|
\\ if (x == 0) return 0;
|
||||||
|
\\ var child = rangeSum(x - 1);
|
||||||
|
\\ return child + 1;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
"tmp.zig:8:1: error: '@Frame(rangeSum)' depends on itself",
|
||||||
|
"tmp.zig:15:33: note: when analyzing type '@Frame(rangeSumIndirect)' here",
|
||||||
|
"tmp.zig:26:25: note: when analyzing type '@Frame(rangeSum)' here",
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add(
|
||||||
|
"non-async function pointer eventually is inferred to become async",
|
||||||
|
\\export fn a() void {
|
||||||
|
\\ var non_async_fn: fn () void = undefined;
|
||||||
|
\\ non_async_fn = func;
|
||||||
|
\\}
|
||||||
|
\\fn func() void {
|
||||||
|
\\ suspend;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
"tmp.zig:5:1: error: 'func' cannot be async",
|
||||||
|
"tmp.zig:3:20: note: required to be non-async here",
|
||||||
|
"tmp.zig:6:5: note: suspends here",
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add(
|
||||||
|
"bad alignment in @asyncCall",
|
||||||
|
\\export fn entry() void {
|
||||||
|
\\ var ptr: async fn () void = func;
|
||||||
|
\\ var bytes: [64]u8 = undefined;
|
||||||
|
\\ _ = @asyncCall(&bytes, {}, ptr);
|
||||||
|
\\}
|
||||||
|
\\async fn func() void {}
|
||||||
|
,
|
||||||
|
"tmp.zig:4:21: error: expected type '[]align(16) u8', found '*[64]u8'",
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add(
|
||||||
|
"atomic orderings of fence Acquire or stricter",
|
||||||
|
\\export fn entry() void {
|
||||||
|
\\ @fence(.Monotonic);
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
"tmp.zig:2:12: error: atomic ordering must be Acquire or stricter",
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add(
|
||||||
|
"bad alignment in implicit cast from array pointer to slice",
|
||||||
|
\\export fn a() void {
|
||||||
|
\\ var x: [10]u8 = undefined;
|
||||||
|
\\ var y: []align(16) u8 = &x;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
"tmp.zig:3:30: error: expected type '[]align(16) u8', found '*[10]u8'",
|
||||||
|
);
|
||||||
|
|
||||||
cases.add(
|
cases.add(
|
||||||
"result location incompatibility mismatching handle_is_ptr (generic call)",
|
"result location incompatibility mismatching handle_is_ptr (generic call)",
|
||||||
\\export fn entry() void {
|
\\export fn entry() void {
|
||||||
@ -164,7 +265,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
|||||||
"non async function pointer passed to @asyncCall",
|
"non async function pointer passed to @asyncCall",
|
||||||
\\export fn entry() void {
|
\\export fn entry() void {
|
||||||
\\ var ptr = afunc;
|
\\ var ptr = afunc;
|
||||||
\\ var bytes: [100]u8 = undefined;
|
\\ var bytes: [100]u8 align(16) = undefined;
|
||||||
\\ _ = @asyncCall(&bytes, {}, ptr);
|
\\ _ = @asyncCall(&bytes, {}, ptr);
|
||||||
\\}
|
\\}
|
||||||
\\fn afunc() void { }
|
\\fn afunc() void { }
|
||||||
|
@ -30,7 +30,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
|||||||
\\ @import("std").os.exit(126);
|
\\ @import("std").os.exit(126);
|
||||||
\\}
|
\\}
|
||||||
\\pub fn main() void {
|
\\pub fn main() void {
|
||||||
\\ var bytes: [1]u8 = undefined;
|
\\ var bytes: [1]u8 align(16) = undefined;
|
||||||
\\ var ptr = other;
|
\\ var ptr = other;
|
||||||
\\ var frame = @asyncCall(&bytes, {}, ptr);
|
\\ var frame = @asyncCall(&bytes, {}, ptr);
|
||||||
\\}
|
\\}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const expect = @import("std").testing.expect;
|
const std = @import("std");
|
||||||
const mem = @import("std").mem;
|
const expect = std.testing.expect;
|
||||||
|
const mem = std.mem;
|
||||||
|
|
||||||
test "arrays" {
|
test "arrays" {
|
||||||
var array: [5]u32 = undefined;
|
var array: [5]u32 = undefined;
|
||||||
@ -274,3 +275,20 @@ test "double nested array to const slice cast in array literal" {
|
|||||||
S.entry(2);
|
S.entry(2);
|
||||||
comptime S.entry(2);
|
comptime S.entry(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "read/write through global variable array of struct fields initialized via array mult" {
|
||||||
|
const S = struct {
|
||||||
|
fn doTheTest() void {
|
||||||
|
expect(storage[0].term == 1);
|
||||||
|
storage[0] = MyStruct{ .term = 123 };
|
||||||
|
expect(storage[0].term == 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MyStruct = struct {
|
||||||
|
term: usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1;
|
||||||
|
};
|
||||||
|
S.doTheTest();
|
||||||
|
}
|
||||||
|
@ -280,7 +280,7 @@ test "async fn pointer in a struct field" {
|
|||||||
bar: async fn (*i32) void,
|
bar: async fn (*i32) void,
|
||||||
};
|
};
|
||||||
var foo = Foo{ .bar = simpleAsyncFn2 };
|
var foo = Foo{ .bar = simpleAsyncFn2 };
|
||||||
var bytes: [64]u8 = undefined;
|
var bytes: [64]u8 align(16) = undefined;
|
||||||
const f = @asyncCall(&bytes, {}, foo.bar, &data);
|
const f = @asyncCall(&bytes, {}, foo.bar, &data);
|
||||||
comptime expect(@typeOf(f) == anyframe->void);
|
comptime expect(@typeOf(f) == anyframe->void);
|
||||||
expect(data == 2);
|
expect(data == 2);
|
||||||
@ -317,7 +317,7 @@ test "@asyncCall with return type" {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
var foo = Foo{ .bar = Foo.middle };
|
var foo = Foo{ .bar = Foo.middle };
|
||||||
var bytes: [150]u8 = undefined;
|
var bytes: [150]u8 align(16) = undefined;
|
||||||
var aresult: i32 = 0;
|
var aresult: i32 = 0;
|
||||||
_ = @asyncCall(&bytes, &aresult, foo.bar);
|
_ = @asyncCall(&bytes, &aresult, foo.bar);
|
||||||
expect(aresult == 0);
|
expect(aresult == 0);
|
||||||
@ -817,3 +817,30 @@ test "struct parameter to async function is copied to the frame" {
|
|||||||
};
|
};
|
||||||
S.doTheTest();
|
S.doTheTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "cast fn to async fn when it is inferred to be async" {
|
||||||
|
const S = struct {
|
||||||
|
var frame: anyframe = undefined;
|
||||||
|
var ok = false;
|
||||||
|
|
||||||
|
fn doTheTest() void {
|
||||||
|
var ptr: async fn () i32 = undefined;
|
||||||
|
ptr = func;
|
||||||
|
var buf: [100]u8 align(16) = undefined;
|
||||||
|
var result: i32 = undefined;
|
||||||
|
_ = await @asyncCall(&buf, &result, ptr);
|
||||||
|
expect(result == 1234);
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func() i32 {
|
||||||
|
suspend {
|
||||||
|
frame = @frame();
|
||||||
|
}
|
||||||
|
return 1234;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_ = async S.doTheTest();
|
||||||
|
resume S.frame;
|
||||||
|
expect(S.ok);
|
||||||
|
}
|
||||||
|
@ -125,3 +125,17 @@ test "implicit cast to error union by returning" {
|
|||||||
S.entry();
|
S.entry();
|
||||||
comptime S.entry();
|
comptime S.entry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// issue #3010: compiler segfault
|
||||||
|
test "bitcast literal [4]u8 param to u32" {
|
||||||
|
const ip = @bitCast(u32, [_]u8{ 255, 255, 255, 255 });
|
||||||
|
expect(ip == maxInt(u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "bitcast packed struct literal to byte" {
|
||||||
|
const Foo = packed struct {
|
||||||
|
value: u8,
|
||||||
|
};
|
||||||
|
const casted = @bitCast(u8, Foo{ .value = 0xF });
|
||||||
|
expect(casted == 0xf);
|
||||||
|
}
|
||||||
|
@ -8,8 +8,8 @@ const ET = union(enum) {
|
|||||||
|
|
||||||
pub fn print(a: *const ET, buf: []u8) anyerror!usize {
|
pub fn print(a: *const ET, buf: []u8) anyerror!usize {
|
||||||
return switch (a.*) {
|
return switch (a.*) {
|
||||||
ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),
|
ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, fmt.FormatOptions{}),
|
||||||
ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),
|
ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, fmt.FormatOptions{}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const expect = std.testing.expect;
|
const expect = std.testing.expect;
|
||||||
|
|
||||||
var new_stack_bytes: [1024]u8 = undefined;
|
var new_stack_bytes: [1024]u8 align(16) = undefined;
|
||||||
|
|
||||||
test "calling a function with a new stack" {
|
test "calling a function with a new stack" {
|
||||||
const arg = 1234;
|
const arg = 1234;
|
||||||
|
@ -33,3 +33,8 @@ test "void optional" {
|
|||||||
var x: ?void = {};
|
var x: ?void = {};
|
||||||
expect(x != null);
|
expect(x != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "void array as a local variable initializer" {
|
||||||
|
var x = [_]void{{}} ** 1004;
|
||||||
|
var y = x[0];
|
||||||
|
}
|
||||||
|
@ -2,6 +2,8 @@ const std = @import("std");
|
|||||||
const debug = std.debug;
|
const debug = std.debug;
|
||||||
const warn = debug.warn;
|
const warn = debug.warn;
|
||||||
const build = std.build;
|
const build = std.build;
|
||||||
|
pub const Target = build.Target;
|
||||||
|
pub const CrossTarget = build.CrossTarget;
|
||||||
const Buffer = std.Buffer;
|
const Buffer = std.Buffer;
|
||||||
const io = std.io;
|
const io = std.io;
|
||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
@ -20,24 +22,18 @@ const runtime_safety = @import("runtime_safety.zig");
|
|||||||
const translate_c = @import("translate_c.zig");
|
const translate_c = @import("translate_c.zig");
|
||||||
const gen_h = @import("gen_h.zig");
|
const gen_h = @import("gen_h.zig");
|
||||||
|
|
||||||
const TestTarget = struct {
|
const test_targets = [_]CrossTarget{
|
||||||
os: builtin.Os,
|
CrossTarget{
|
||||||
arch: builtin.Arch,
|
|
||||||
abi: builtin.Abi,
|
|
||||||
};
|
|
||||||
|
|
||||||
const test_targets = [_]TestTarget{
|
|
||||||
TestTarget{
|
|
||||||
.os = .linux,
|
.os = .linux,
|
||||||
.arch = .x86_64,
|
.arch = .x86_64,
|
||||||
.abi = .gnu,
|
.abi = .gnu,
|
||||||
},
|
},
|
||||||
TestTarget{
|
CrossTarget{
|
||||||
.os = .macosx,
|
.os = .macosx,
|
||||||
.arch = .x86_64,
|
.arch = .x86_64,
|
||||||
.abi = .gnu,
|
.abi = .gnu,
|
||||||
},
|
},
|
||||||
TestTarget{
|
CrossTarget{
|
||||||
.os = .windows,
|
.os = .windows,
|
||||||
.arch = .x86_64,
|
.arch = .x86_64,
|
||||||
.abi = .msvc,
|
.abi = .msvc,
|
||||||
@ -568,6 +564,7 @@ pub const CompileErrorContext = struct {
|
|||||||
link_libc: bool,
|
link_libc: bool,
|
||||||
is_exe: bool,
|
is_exe: bool,
|
||||||
is_test: bool,
|
is_test: bool,
|
||||||
|
target: Target = .Native,
|
||||||
|
|
||||||
const SourceFile = struct {
|
const SourceFile = struct {
|
||||||
filename: []const u8,
|
filename: []const u8,
|
||||||
@ -655,6 +652,14 @@ pub const CompileErrorContext = struct {
|
|||||||
zig_args.append("--output-dir") catch unreachable;
|
zig_args.append("--output-dir") catch unreachable;
|
||||||
zig_args.append(b.pathFromRoot(b.cache_root)) catch unreachable;
|
zig_args.append(b.pathFromRoot(b.cache_root)) catch unreachable;
|
||||||
|
|
||||||
|
switch (self.case.target) {
|
||||||
|
.Native => {},
|
||||||
|
.Cross => {
|
||||||
|
try zig_args.append("-target");
|
||||||
|
try zig_args.append(try self.case.target.zigTriple(b.allocator));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
switch (self.build_mode) {
|
switch (self.build_mode) {
|
||||||
Mode.Debug => {},
|
Mode.Debug => {},
|
||||||
Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,
|
Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user