mirror of
https://github.com/ziglang/zig.git
synced 2024-12-04 02:48:50 +00:00
Merge remote-tracking branch 'origin/master' into llvm7
This commit is contained in:
commit
ce68dda4b6
@ -5,6 +5,11 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_INSTALL_PREFIX)
|
||||
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}" CACHE STRING
|
||||
"Directory to install zig to" FORCE)
|
||||
endif()
|
||||
|
||||
project(zig C CXX)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
|
||||
|
||||
@ -432,12 +437,17 @@ set(ZIG_STD_FILES
|
||||
"dwarf.zig"
|
||||
"elf.zig"
|
||||
"empty.zig"
|
||||
"endian.zig"
|
||||
"event.zig"
|
||||
"fmt/errol/enum3.zig"
|
||||
"fmt/errol/index.zig"
|
||||
"fmt/errol/lookup.zig"
|
||||
"fmt/index.zig"
|
||||
"hash_map.zig"
|
||||
"hash/index.zig"
|
||||
"hash/adler.zig"
|
||||
"hash/crc.zig"
|
||||
"hash/fnv.zig"
|
||||
"hash/siphash.zig"
|
||||
"heap.zig"
|
||||
"index.zig"
|
||||
"io.zig"
|
||||
@ -498,7 +508,6 @@ set(ZIG_STD_FILES
|
||||
"os/get_user_id.zig"
|
||||
"os/index.zig"
|
||||
"os/linux/errno.zig"
|
||||
"os/linux/i386.zig"
|
||||
"os/linux/index.zig"
|
||||
"os/linux/x86_64.zig"
|
||||
"os/path.zig"
|
||||
|
@ -141,10 +141,10 @@ libc. Create demo games using Zig.
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd)
|
||||
cmake ..
|
||||
make
|
||||
make install
|
||||
./zig build --build-file ../build.zig test
|
||||
bin/zig build --build-file ../build.zig test
|
||||
```
|
||||
|
||||
##### MacOS
|
||||
@ -154,9 +154,9 @@ brew install cmake llvm@7
|
||||
brew outdated llvm@7 || brew upgrade llvm@7
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_PREFIX_PATH=/usr/local/opt/llvm@7/ -DCMAKE_INSTALL_PREFIX=$(pwd)
|
||||
cmake .. -DCMAKE_PREFIX_PATH=/usr/local/opt/llvm@7/
|
||||
make install
|
||||
./zig build --build-file ../build.zig test
|
||||
bin/zig build --build-file ../build.zig test
|
||||
```
|
||||
|
||||
##### Windows
|
||||
|
@ -1947,8 +1947,24 @@ const Foo = extern enum { A, B, C };
|
||||
export fn entry(foo: Foo) void { }
|
||||
{#code_end#}
|
||||
{#header_close#}
|
||||
<p>TODO packed enum</p>
|
||||
{#see_also|@memberName|@memberCount|@tagName#}
|
||||
{#header_open|packed enum#}
|
||||
<p>By default, the size of enums is not guaranteed.</p>
|
||||
<p><code>packed enum</code> causes the size of the enum to be the same as the size of the integer tag type
|
||||
of the enum:</p>
|
||||
{#code_begin|test#}
|
||||
const std = @import("std");
|
||||
|
||||
test "packed enum" {
|
||||
const Number = packed enum(u8) {
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
};
|
||||
std.debug.assert(@sizeOf(Number) == @sizeOf(u8));
|
||||
}
|
||||
{#code_end#}
|
||||
{#header_close#}
|
||||
{#see_also|@memberName|@memberCount|@tagName|@sizeOf#}
|
||||
{#header_close#}
|
||||
{#header_open|union#}
|
||||
{#code_begin|test|union#}
|
||||
@ -2017,7 +2033,27 @@ test "union variant switch" {
|
||||
assert(mem.eql(u8, what_is_it, "this is a number"));
|
||||
}
|
||||
|
||||
// TODO union methods
|
||||
// Unions can have methods just like structs and enums:
|
||||
|
||||
const Variant = union(enum) {
|
||||
Int: i32,
|
||||
Bool: bool,
|
||||
|
||||
fn truthy(self: &const Variant) bool {
|
||||
return switch (*self) {
|
||||
Variant.Int => |x_int| x_int != 0,
|
||||
Variant.Bool => |x_bool| x_bool,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
test "union method" {
|
||||
var v1 = Variant { .Int = 1 };
|
||||
var v2 = Variant { .Bool = false };
|
||||
|
||||
assert(v1.truthy());
|
||||
assert(!v2.truthy());
|
||||
}
|
||||
|
||||
|
||||
const Small = union {
|
||||
|
@ -741,6 +741,7 @@ fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {
|
||||
defer baf.destroy();
|
||||
|
||||
try parser.renderSource(baf.stream(), tree.root_node);
|
||||
try baf.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,7 +359,6 @@ enum NodeType {
|
||||
NodeTypeRoot,
|
||||
NodeTypeFnProto,
|
||||
NodeTypeFnDef,
|
||||
NodeTypeFnDecl,
|
||||
NodeTypeParamDecl,
|
||||
NodeTypeBlock,
|
||||
NodeTypeGroupedExpr,
|
||||
@ -453,10 +452,6 @@ struct AstNodeFnDef {
|
||||
AstNode *body;
|
||||
};
|
||||
|
||||
struct AstNodeFnDecl {
|
||||
AstNode *fn_proto;
|
||||
};
|
||||
|
||||
struct AstNodeParamDecl {
|
||||
Buf *name;
|
||||
AstNode *type;
|
||||
@ -713,10 +708,6 @@ struct AstNodeSwitchRange {
|
||||
AstNode *end;
|
||||
};
|
||||
|
||||
struct AstNodeLabel {
|
||||
Buf *name;
|
||||
};
|
||||
|
||||
struct AstNodeCompTime {
|
||||
AstNode *expr;
|
||||
};
|
||||
@ -892,7 +883,6 @@ struct AstNode {
|
||||
union {
|
||||
AstNodeRoot root;
|
||||
AstNodeFnDef fn_def;
|
||||
AstNodeFnDecl fn_decl;
|
||||
AstNodeFnProto fn_proto;
|
||||
AstNodeParamDecl param_decl;
|
||||
AstNodeBlock block;
|
||||
@ -917,7 +907,6 @@ struct AstNode {
|
||||
AstNodeSwitchExpr switch_expr;
|
||||
AstNodeSwitchProng switch_prong;
|
||||
AstNodeSwitchRange switch_range;
|
||||
AstNodeLabel label;
|
||||
AstNodeCompTime comptime_expr;
|
||||
AstNodeAsmExpr asm_expr;
|
||||
AstNodeFieldAccessExpr field_access_expr;
|
||||
@ -1654,6 +1643,8 @@ struct CodeGen {
|
||||
LLVMValueRef coro_save_fn_val;
|
||||
LLVMValueRef coro_promise_fn_val;
|
||||
LLVMValueRef coro_alloc_helper_fn_val;
|
||||
LLVMValueRef merge_err_ret_traces_fn_val;
|
||||
LLVMValueRef add_error_return_trace_addr_fn_val;
|
||||
bool error_during_imports;
|
||||
|
||||
const char **clang_argv;
|
||||
@ -2052,6 +2043,8 @@ enum IrInstructionId {
|
||||
IrInstructionIdAwaitBookkeeping,
|
||||
IrInstructionIdSaveErrRetAddr,
|
||||
IrInstructionIdAddImplicitReturnType,
|
||||
IrInstructionIdMergeErrRetTraces,
|
||||
IrInstructionIdMarkErrRetTracePtr,
|
||||
};
|
||||
|
||||
struct IrInstruction {
|
||||
@ -2890,6 +2883,11 @@ struct IrInstructionExport {
|
||||
|
||||
struct IrInstructionErrorReturnTrace {
|
||||
IrInstruction base;
|
||||
|
||||
enum Nullable {
|
||||
Null,
|
||||
NonNull,
|
||||
} nullable;
|
||||
};
|
||||
|
||||
struct IrInstructionErrorUnion {
|
||||
@ -3022,6 +3020,20 @@ struct IrInstructionAddImplicitReturnType {
|
||||
IrInstruction *value;
|
||||
};
|
||||
|
||||
struct IrInstructionMergeErrRetTraces {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *coro_promise_ptr;
|
||||
IrInstruction *src_err_ret_trace_ptr;
|
||||
IrInstruction *dest_err_ret_trace_ptr;
|
||||
};
|
||||
|
||||
struct IrInstructionMarkErrRetTracePtr {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *err_ret_trace_ptr;
|
||||
};
|
||||
|
||||
static const size_t slice_ptr_index = 0;
|
||||
static const size_t slice_len_index = 1;
|
||||
|
||||
@ -3031,10 +3043,18 @@ static const size_t maybe_null_index = 1;
|
||||
static const size_t err_union_err_index = 0;
|
||||
static const size_t err_union_payload_index = 1;
|
||||
|
||||
// TODO call graph analysis to find out what this number needs to be for every function
|
||||
static const size_t stack_trace_ptr_count = 30;
|
||||
|
||||
// these belong to the async function
|
||||
#define RETURN_ADDRESSES_FIELD_NAME "return_addresses"
|
||||
#define ERR_RET_TRACE_FIELD_NAME "err_ret_trace"
|
||||
#define RESULT_FIELD_NAME "result"
|
||||
#define ASYNC_ALLOC_FIELD_NAME "allocFn"
|
||||
#define ASYNC_FREE_FIELD_NAME "freeFn"
|
||||
#define AWAITER_HANDLE_FIELD_NAME "awaiter_handle"
|
||||
#define RESULT_FIELD_NAME "result"
|
||||
// these point to data belonging to the awaiter
|
||||
#define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr"
|
||||
#define RESULT_PTR_FIELD_NAME "result_ptr"
|
||||
|
||||
|
||||
|
@ -468,10 +468,30 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)
|
||||
|
||||
TypeTableEntry *awaiter_handle_type = get_maybe_type(g, g->builtin_types.entry_promise);
|
||||
TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false);
|
||||
const char *field_names[] = {AWAITER_HANDLE_FIELD_NAME, RESULT_FIELD_NAME, RESULT_PTR_FIELD_NAME};
|
||||
TypeTableEntry *field_types[] = {awaiter_handle_type, return_type, result_ptr_type};
|
||||
|
||||
ZigList<const char *> field_names = {};
|
||||
field_names.append(AWAITER_HANDLE_FIELD_NAME);
|
||||
field_names.append(RESULT_FIELD_NAME);
|
||||
field_names.append(RESULT_PTR_FIELD_NAME);
|
||||
if (g->have_err_ret_tracing) {
|
||||
field_names.append(ERR_RET_TRACE_PTR_FIELD_NAME);
|
||||
field_names.append(ERR_RET_TRACE_FIELD_NAME);
|
||||
field_names.append(RETURN_ADDRESSES_FIELD_NAME);
|
||||
}
|
||||
|
||||
ZigList<TypeTableEntry *> field_types = {};
|
||||
field_types.append(awaiter_handle_type);
|
||||
field_types.append(return_type);
|
||||
field_types.append(result_ptr_type);
|
||||
if (g->have_err_ret_tracing) {
|
||||
field_types.append(get_ptr_to_stack_trace_type(g));
|
||||
field_types.append(g->stack_trace_type);
|
||||
field_types.append(get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count));
|
||||
}
|
||||
|
||||
assert(field_names.length == field_types.length);
|
||||
Buf *name = buf_sprintf("AsyncFramePromise(%s)", buf_ptr(&return_type->name));
|
||||
TypeTableEntry *entry = get_struct_type(g, buf_ptr(name), field_names, field_types, 3);
|
||||
TypeTableEntry *entry = get_struct_type(g, buf_ptr(name), field_names.items, field_types.items, field_names.length);
|
||||
|
||||
return_type->promise_frame_parent = entry;
|
||||
return entry;
|
||||
@ -3216,7 +3236,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
|
||||
break;
|
||||
case NodeTypeContainerDecl:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeDefer:
|
||||
case NodeTypeBlock:
|
||||
|
@ -148,8 +148,6 @@ static const char *node_type_str(NodeType node_type) {
|
||||
return "Root";
|
||||
case NodeTypeFnDef:
|
||||
return "FnDef";
|
||||
case NodeTypeFnDecl:
|
||||
return "FnDecl";
|
||||
case NodeTypeFnProto:
|
||||
return "FnProto";
|
||||
case NodeTypeParamDecl:
|
||||
@ -1098,7 +1096,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeTestDecl:
|
||||
case NodeTypeStructField:
|
||||
|
333
src/codegen.cpp
333
src/codegen.cpp
@ -412,6 +412,9 @@ static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_e
|
||||
if (!g->have_err_ret_tracing) {
|
||||
return UINT32_MAX;
|
||||
}
|
||||
if (fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync) {
|
||||
return 0;
|
||||
}
|
||||
TypeTableEntry *fn_type = fn_table_entry->type_entry;
|
||||
if (!fn_type_can_fail(&fn_type->data.fn.fn_type_id)) {
|
||||
return UINT32_MAX;
|
||||
@ -1099,6 +1102,207 @@ static LLVMValueRef get_return_address_fn_val(CodeGen *g) {
|
||||
return g->return_address_fn_val;
|
||||
}
|
||||
|
||||
static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) {
|
||||
if (g->add_error_return_trace_addr_fn_val != nullptr)
|
||||
return g->add_error_return_trace_addr_fn_val;
|
||||
|
||||
LLVMTypeRef arg_types[] = {
|
||||
get_ptr_to_stack_trace_type(g)->type_ref,
|
||||
g->builtin_types.entry_usize->type_ref,
|
||||
};
|
||||
LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false);
|
||||
|
||||
Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_add_err_ret_trace_addr"), false);
|
||||
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
|
||||
addLLVMFnAttr(fn_val, "alwaysinline");
|
||||
LLVMSetLinkage(fn_val, LLVMInternalLinkage);
|
||||
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
|
||||
addLLVMFnAttr(fn_val, "nounwind");
|
||||
add_uwtable_attr(g, fn_val);
|
||||
addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
|
||||
if (g->build_mode == BuildModeDebug) {
|
||||
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
|
||||
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr);
|
||||
}
|
||||
|
||||
LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry");
|
||||
LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
|
||||
LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder);
|
||||
LLVMPositionBuilderAtEnd(g->builder, entry_block);
|
||||
ZigLLVMClearCurrentDebugLocation(g->builder);
|
||||
|
||||
LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
|
||||
|
||||
// stack_trace.instruction_addresses[stack_trace.index % stack_trace.instruction_addresses.len] = return_address;
|
||||
|
||||
LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0);
|
||||
LLVMValueRef address_value = LLVMGetParam(fn_val, 1);
|
||||
|
||||
size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
|
||||
LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, "");
|
||||
size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
|
||||
LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)addresses_field_index, "");
|
||||
|
||||
TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
|
||||
size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, "");
|
||||
size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
|
||||
|
||||
LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, "");
|
||||
LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, "");
|
||||
LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, len_value, "");
|
||||
LLVMValueRef address_indices[] = {
|
||||
modded_val,
|
||||
};
|
||||
|
||||
LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, "");
|
||||
LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, "");
|
||||
|
||||
gen_store_untyped(g, address_value, address_slot, 0, false);
|
||||
|
||||
// stack_trace.index += 1;
|
||||
LLVMValueRef index_plus_one_val = LLVMBuildNUWAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), "");
|
||||
gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false);
|
||||
|
||||
// return;
|
||||
LLVMBuildRetVoid(g->builder);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, prev_block);
|
||||
LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
|
||||
|
||||
g->add_error_return_trace_addr_fn_val = fn_val;
|
||||
return fn_val;
|
||||
}
|
||||
|
||||
static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) {
|
||||
if (g->merge_err_ret_traces_fn_val)
|
||||
return g->merge_err_ret_traces_fn_val;
|
||||
|
||||
assert(g->stack_trace_type != nullptr);
|
||||
|
||||
LLVMTypeRef param_types[] = {
|
||||
get_ptr_to_stack_trace_type(g)->type_ref,
|
||||
get_ptr_to_stack_trace_type(g)->type_ref,
|
||||
};
|
||||
LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), param_types, 2, false);
|
||||
|
||||
Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_merge_error_return_traces"), false);
|
||||
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
|
||||
LLVMSetLinkage(fn_val, LLVMInternalLinkage);
|
||||
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
|
||||
addLLVMFnAttr(fn_val, "nounwind");
|
||||
add_uwtable_attr(g, fn_val);
|
||||
addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
|
||||
addLLVMArgAttr(fn_val, (unsigned)0, "noalias");
|
||||
addLLVMArgAttr(fn_val, (unsigned)0, "writeonly");
|
||||
addLLVMArgAttr(fn_val, (unsigned)1, "nonnull");
|
||||
addLLVMArgAttr(fn_val, (unsigned)1, "noalias");
|
||||
addLLVMArgAttr(fn_val, (unsigned)1, "readonly");
|
||||
if (g->build_mode == BuildModeDebug) {
|
||||
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
|
||||
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr);
|
||||
}
|
||||
|
||||
// this is above the ZigLLVMClearCurrentDebugLocation
|
||||
LLVMValueRef add_error_return_trace_addr_fn_val = get_add_error_return_trace_addr_fn(g);
|
||||
|
||||
LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry");
|
||||
LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
|
||||
LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder);
|
||||
LLVMPositionBuilderAtEnd(g->builder, entry_block);
|
||||
ZigLLVMClearCurrentDebugLocation(g->builder);
|
||||
|
||||
// var frame_index: usize = undefined;
|
||||
// var frames_left: usize = undefined;
|
||||
// if (src_stack_trace.index < src_stack_trace.instruction_addresses.len) {
|
||||
// frame_index = 0;
|
||||
// frames_left = src_stack_trace.index;
|
||||
// if (frames_left == 0) return;
|
||||
// } else {
|
||||
// frame_index = (src_stack_trace.index + 1) % src_stack_trace.instruction_addresses.len;
|
||||
// frames_left = src_stack_trace.instruction_addresses.len;
|
||||
// }
|
||||
// while (true) {
|
||||
// __zig_add_err_ret_trace_addr(dest_stack_trace, src_stack_trace.instruction_addresses[frame_index]);
|
||||
// frames_left -= 1;
|
||||
// if (frames_left == 0) return;
|
||||
// frame_index = (frame_index + 1) % src_stack_trace.instruction_addresses.len;
|
||||
// }
|
||||
LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(fn_val, "Return");
|
||||
|
||||
LLVMValueRef frame_index_ptr = LLVMBuildAlloca(g->builder, g->builtin_types.entry_usize->type_ref, "frame_index");
|
||||
LLVMValueRef frames_left_ptr = LLVMBuildAlloca(g->builder, g->builtin_types.entry_usize->type_ref, "frames_left");
|
||||
|
||||
LLVMValueRef dest_stack_trace_ptr = LLVMGetParam(fn_val, 0);
|
||||
LLVMValueRef src_stack_trace_ptr = LLVMGetParam(fn_val, 1);
|
||||
|
||||
size_t src_index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
|
||||
size_t src_addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
|
||||
LLVMValueRef src_index_field_ptr = LLVMBuildStructGEP(g->builder, src_stack_trace_ptr,
|
||||
(unsigned)src_index_field_index, "");
|
||||
LLVMValueRef src_addresses_field_ptr = LLVMBuildStructGEP(g->builder, src_stack_trace_ptr,
|
||||
(unsigned)src_addresses_field_index, "");
|
||||
TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
|
||||
size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
|
||||
LLVMValueRef src_ptr_field_ptr = LLVMBuildStructGEP(g->builder, src_addresses_field_ptr, (unsigned)ptr_field_index, "");
|
||||
size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
|
||||
LLVMValueRef src_len_field_ptr = LLVMBuildStructGEP(g->builder, src_addresses_field_ptr, (unsigned)len_field_index, "");
|
||||
LLVMValueRef src_index_val = LLVMBuildLoad(g->builder, src_index_field_ptr, "");
|
||||
LLVMValueRef src_ptr_val = LLVMBuildLoad(g->builder, src_ptr_field_ptr, "");
|
||||
LLVMValueRef src_len_val = LLVMBuildLoad(g->builder, src_len_field_ptr, "");
|
||||
LLVMValueRef no_wrap_bit = LLVMBuildICmp(g->builder, LLVMIntULT, src_index_val, src_len_val, "");
|
||||
LLVMBasicBlockRef no_wrap_block = LLVMAppendBasicBlock(fn_val, "NoWrap");
|
||||
LLVMBasicBlockRef yes_wrap_block = LLVMAppendBasicBlock(fn_val, "YesWrap");
|
||||
LLVMBasicBlockRef loop_block = LLVMAppendBasicBlock(fn_val, "Loop");
|
||||
LLVMBuildCondBr(g->builder, no_wrap_bit, no_wrap_block, yes_wrap_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, no_wrap_block);
|
||||
LLVMValueRef usize_zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref);
|
||||
LLVMBuildStore(g->builder, usize_zero, frame_index_ptr);
|
||||
LLVMBuildStore(g->builder, src_index_val, frames_left_ptr);
|
||||
LLVMValueRef frames_left_eq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, src_index_val, usize_zero, "");
|
||||
LLVMBuildCondBr(g->builder, frames_left_eq_zero_bit, return_block, loop_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, yes_wrap_block);
|
||||
LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->type_ref, 1, false);
|
||||
LLVMValueRef plus_one = LLVMBuildNUWAdd(g->builder, src_index_val, usize_one, "");
|
||||
LLVMValueRef mod_len = LLVMBuildURem(g->builder, plus_one, src_len_val, "");
|
||||
LLVMBuildStore(g->builder, mod_len, frame_index_ptr);
|
||||
LLVMBuildStore(g->builder, src_len_val, frames_left_ptr);
|
||||
LLVMBuildBr(g->builder, loop_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, loop_block);
|
||||
LLVMValueRef ptr_index = LLVMBuildLoad(g->builder, frame_index_ptr, "");
|
||||
LLVMValueRef addr_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr_val, &ptr_index, 1, "");
|
||||
LLVMValueRef this_addr_val = LLVMBuildLoad(g->builder, addr_ptr, "");
|
||||
LLVMValueRef args[] = {dest_stack_trace_ptr, this_addr_val};
|
||||
ZigLLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAlways, "");
|
||||
LLVMValueRef prev_frames_left = LLVMBuildLoad(g->builder, frames_left_ptr, "");
|
||||
LLVMValueRef new_frames_left = LLVMBuildNUWSub(g->builder, prev_frames_left, usize_one, "");
|
||||
LLVMValueRef done_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, new_frames_left, usize_zero, "");
|
||||
LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(fn_val, "Continue");
|
||||
LLVMBuildCondBr(g->builder, done_bit, return_block, continue_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, return_block);
|
||||
LLVMBuildRetVoid(g->builder);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, continue_block);
|
||||
LLVMBuildStore(g->builder, new_frames_left, frames_left_ptr);
|
||||
LLVMValueRef prev_index = LLVMBuildLoad(g->builder, frame_index_ptr, "");
|
||||
LLVMValueRef index_plus_one = LLVMBuildNUWAdd(g->builder, prev_index, usize_one, "");
|
||||
LLVMValueRef index_mod_len = LLVMBuildURem(g->builder, index_plus_one, src_len_val, "");
|
||||
LLVMBuildStore(g->builder, index_mod_len, frame_index_ptr);
|
||||
LLVMBuildBr(g->builder, loop_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, prev_block);
|
||||
LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
|
||||
|
||||
g->merge_err_ret_traces_fn_val = fn_val;
|
||||
return fn_val;
|
||||
|
||||
}
|
||||
|
||||
static LLVMValueRef get_return_err_fn(CodeGen *g) {
|
||||
if (g->return_err_fn != nullptr)
|
||||
return g->return_err_fn;
|
||||
@ -1125,50 +1329,24 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) {
|
||||
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr);
|
||||
}
|
||||
|
||||
// this is above the ZigLLVMClearCurrentDebugLocation
|
||||
LLVMValueRef add_error_return_trace_addr_fn_val = get_add_error_return_trace_addr_fn(g);
|
||||
|
||||
LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry");
|
||||
LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
|
||||
LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder);
|
||||
LLVMPositionBuilderAtEnd(g->builder, entry_block);
|
||||
ZigLLVMClearCurrentDebugLocation(g->builder);
|
||||
|
||||
LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
|
||||
|
||||
// stack_trace.instruction_addresses[stack_trace.index % stack_trace.instruction_addresses.len] = return_address;
|
||||
|
||||
LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0);
|
||||
size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
|
||||
LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, "");
|
||||
size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
|
||||
LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)addresses_field_index, "");
|
||||
|
||||
TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
|
||||
size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, "");
|
||||
size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
|
||||
|
||||
LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, "");
|
||||
LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, "");
|
||||
LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, len_value, "");
|
||||
LLVMValueRef address_indices[] = {
|
||||
modded_val,
|
||||
};
|
||||
|
||||
LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, "");
|
||||
LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, "");
|
||||
|
||||
LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
|
||||
LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref);
|
||||
LLVMValueRef return_address_ptr = LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, "");
|
||||
LLVMValueRef return_address = LLVMBuildPtrToInt(g->builder, return_address_ptr, usize_type_ref, "");
|
||||
|
||||
LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, return_address, usize_type_ref, "");
|
||||
gen_store_untyped(g, address_value, address_slot, 0, false);
|
||||
|
||||
// stack_trace.index += 1;
|
||||
LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), "");
|
||||
gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false);
|
||||
|
||||
// return;
|
||||
LLVMValueRef args[] = { err_ret_trace_ptr, return_address };
|
||||
ZigLLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAlways, "");
|
||||
LLVMBuildRetVoid(g->builder);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, prev_block);
|
||||
@ -1608,7 +1786,6 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut
|
||||
};
|
||||
LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1,
|
||||
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
|
||||
LLVMSetTailCall(call_instruction, true);
|
||||
return call_instruction;
|
||||
}
|
||||
|
||||
@ -4119,6 +4296,27 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,
|
||||
return LLVMBuildIntToPtr(g->builder, uncasted_result, operand_type->type_ref, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_merge_err_ret_traces(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionMergeErrRetTraces *instruction)
|
||||
{
|
||||
assert(g->have_err_ret_tracing);
|
||||
|
||||
LLVMValueRef src_trace_ptr = ir_llvm_value(g, instruction->src_err_ret_trace_ptr);
|
||||
LLVMValueRef dest_trace_ptr = ir_llvm_value(g, instruction->dest_err_ret_trace_ptr);
|
||||
|
||||
LLVMValueRef args[] = { dest_trace_ptr, src_trace_ptr };
|
||||
ZigLLVMBuildCall(g->builder, get_merge_err_ret_traces_fn_val(g), args, 2, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionMarkErrRetTracePtr *instruction)
|
||||
{
|
||||
assert(g->have_err_ret_tracing);
|
||||
g->cur_err_ret_trace_val_stack = ir_llvm_value(g, instruction->err_ret_trace_ptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
|
||||
AstNode *source_node = instruction->source_node;
|
||||
Scope *scope = instruction->scope;
|
||||
@ -4336,6 +4534,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction);
|
||||
case IrInstructionIdSaveErrRetAddr:
|
||||
return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction);
|
||||
case IrInstructionIdMergeErrRetTraces:
|
||||
return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction);
|
||||
case IrInstructionIdMarkErrRetTracePtr:
|
||||
return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -5225,38 +5427,14 @@ static void do_code_gen(CodeGen *g) {
|
||||
g->cur_err_ret_trace_val_arg = nullptr;
|
||||
}
|
||||
|
||||
// error return tracing setup
|
||||
bool is_async = fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
|
||||
bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn &&
|
||||
(is_async || !have_err_ret_trace_arg);
|
||||
bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn && !is_async && !have_err_ret_trace_arg;
|
||||
LLVMValueRef err_ret_array_val = nullptr;
|
||||
if (have_err_ret_trace_stack) {
|
||||
// TODO call graph analysis to find out what this number needs to be for every function
|
||||
static const size_t stack_trace_ptr_count = 30;
|
||||
|
||||
TypeTableEntry *usize = g->builtin_types.entry_usize;
|
||||
TypeTableEntry *array_type = get_array_type(g, usize, stack_trace_ptr_count);
|
||||
LLVMValueRef err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses",
|
||||
get_abi_alignment(g, array_type));
|
||||
TypeTableEntry *array_type = get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count);
|
||||
err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses", get_abi_alignment(g, array_type));
|
||||
g->cur_err_ret_trace_val_stack = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type));
|
||||
size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
|
||||
LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)index_field_index, "");
|
||||
gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false);
|
||||
|
||||
size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
|
||||
LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)addresses_field_index, "");
|
||||
|
||||
TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
|
||||
size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, "");
|
||||
LLVMValueRef zero = LLVMConstNull(usize->type_ref);
|
||||
LLVMValueRef indices[] = {zero, zero};
|
||||
LLVMValueRef err_ret_array_val_elem0_ptr = LLVMBuildInBoundsGEP(g->builder, err_ret_array_val,
|
||||
indices, 2, "");
|
||||
gen_store(g, err_ret_array_val_elem0_ptr, ptr_field_ptr,
|
||||
get_pointer_to_type(g, get_pointer_to_type(g, usize, false), false));
|
||||
|
||||
size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
|
||||
gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false));
|
||||
} else {
|
||||
g->cur_err_ret_trace_val_stack = nullptr;
|
||||
}
|
||||
@ -5351,6 +5529,31 @@ static void do_code_gen(CodeGen *g) {
|
||||
}
|
||||
}
|
||||
|
||||
// finishing error return trace setup. we have to do this after all the allocas.
|
||||
if (have_err_ret_trace_stack) {
|
||||
TypeTableEntry *usize = g->builtin_types.entry_usize;
|
||||
size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
|
||||
LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)index_field_index, "");
|
||||
gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false);
|
||||
|
||||
size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
|
||||
LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)addresses_field_index, "");
|
||||
|
||||
TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
|
||||
size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, "");
|
||||
LLVMValueRef zero = LLVMConstNull(usize->type_ref);
|
||||
LLVMValueRef indices[] = {zero, zero};
|
||||
LLVMValueRef err_ret_array_val_elem0_ptr = LLVMBuildInBoundsGEP(g->builder, err_ret_array_val,
|
||||
indices, 2, "");
|
||||
TypeTableEntry *ptr_ptr_usize_type = get_pointer_to_type(g, get_pointer_to_type(g, usize, false), false);
|
||||
gen_store(g, err_ret_array_val_elem0_ptr, ptr_field_ptr, ptr_ptr_usize_type);
|
||||
|
||||
size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
|
||||
gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false));
|
||||
}
|
||||
|
||||
FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;
|
||||
|
||||
// create debug variable declarations for parameters
|
||||
@ -5858,6 +6061,8 @@ static void define_builtin_compile_vars(CodeGen *g) {
|
||||
os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);
|
||||
Buf *contents = buf_alloc();
|
||||
|
||||
// Modifications to this struct must be coordinated with code that does anything with
|
||||
// g->stack_trace_type. There are hard-coded references to the field indexes.
|
||||
buf_append_str(contents,
|
||||
"pub const StackTrace = struct {\n"
|
||||
" index: usize,\n"
|
||||
@ -6122,7 +6327,9 @@ static void init(CodeGen *g) {
|
||||
g->builder = LLVMCreateBuilder();
|
||||
g->dbuilder = ZigLLVMCreateDIBuilder(g->module, true);
|
||||
|
||||
Buf *producer = buf_sprintf("zig %s", ZIG_VERSION_STRING);
|
||||
// Don't use ZIG_VERSION_STRING here, llvm misparses it when it includes
|
||||
// the git revision.
|
||||
Buf *producer = buf_sprintf("zig %d.%d.%d", ZIG_VERSION_MAJOR, ZIG_VERSION_MINOR, ZIG_VERSION_PATCH);
|
||||
const char *flags = "";
|
||||
unsigned runtime_version = 0;
|
||||
ZigLLVMDIFile *compile_unit_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(g->root_out_name),
|
||||
|
279
src/ir.cpp
279
src/ir.cpp
@ -725,6 +725,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitRetur
|
||||
return IrInstructionIdAddImplicitReturnType;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionMergeErrRetTraces *) {
|
||||
return IrInstructionIdMergeErrRetTraces;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTracePtr *) {
|
||||
return IrInstructionIdMarkErrRetTracePtr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
||||
T *special_instruction = allocate<T>(1);
|
||||
@ -956,25 +964,6 @@ static IrInstruction *ir_build_const_c_str_lit(IrBuilder *irb, Scope *scope, Ast
|
||||
return &const_instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_const_promise_init(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
TypeTableEntry *return_type)
|
||||
{
|
||||
TypeTableEntry *struct_type = get_promise_frame_type(irb->codegen, return_type);
|
||||
|
||||
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
||||
const_instruction->base.value.type = struct_type;
|
||||
const_instruction->base.value.special = ConstValSpecialStatic;
|
||||
const_instruction->base.value.data.x_struct.fields = allocate<ConstExprValue>(struct_type->data.structure.src_field_count);
|
||||
const_instruction->base.value.data.x_struct.fields[0].type = struct_type->data.structure.fields[0].type_entry;
|
||||
const_instruction->base.value.data.x_struct.fields[0].special = ConstValSpecialStatic;
|
||||
const_instruction->base.value.data.x_struct.fields[0].data.x_maybe = nullptr;
|
||||
const_instruction->base.value.data.x_struct.fields[1].type = return_type;
|
||||
const_instruction->base.value.data.x_struct.fields[1].special = ConstValSpecialUndef;
|
||||
const_instruction->base.value.data.x_struct.fields[2].type = struct_type->data.structure.fields[2].type_entry;
|
||||
const_instruction->base.value.data.x_struct.fields[2].special = ConstValSpecialUndef;
|
||||
return &const_instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_bin_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBinOp op_id,
|
||||
IrInstruction *op1, IrInstruction *op2, bool safety_check_on)
|
||||
{
|
||||
@ -2495,8 +2484,9 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
||||
static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Nullable nullable) {
|
||||
IrInstructionErrorReturnTrace *instruction = ir_build_instruction<IrInstructionErrorReturnTrace>(irb, scope, source_node);
|
||||
instruction->nullable = nullable;
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
@ -2717,6 +2707,30 @@ static IrInstruction *ir_build_add_implicit_return_type(IrBuilder *irb, Scope *s
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_merge_err_ret_traces(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *coro_promise_ptr, IrInstruction *src_err_ret_trace_ptr, IrInstruction *dest_err_ret_trace_ptr)
|
||||
{
|
||||
IrInstructionMergeErrRetTraces *instruction = ir_build_instruction<IrInstructionMergeErrRetTraces>(irb, scope, source_node);
|
||||
instruction->coro_promise_ptr = coro_promise_ptr;
|
||||
instruction->src_err_ret_trace_ptr = src_err_ret_trace_ptr;
|
||||
instruction->dest_err_ret_trace_ptr = dest_err_ret_trace_ptr;
|
||||
|
||||
ir_ref_instruction(coro_promise_ptr, irb->current_basic_block);
|
||||
ir_ref_instruction(src_err_ret_trace_ptr, irb->current_basic_block);
|
||||
ir_ref_instruction(dest_err_ret_trace_ptr, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *err_ret_trace_ptr) {
|
||||
IrInstructionMarkErrRetTracePtr *instruction = ir_build_instruction<IrInstructionMarkErrRetTracePtr>(irb, scope, source_node);
|
||||
instruction->err_ret_trace_ptr = err_ret_trace_ptr;
|
||||
|
||||
ir_ref_instruction(err_ret_trace_ptr, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
|
||||
results[ReturnKindUnconditional] = 0;
|
||||
results[ReturnKindError] = 0;
|
||||
@ -2741,9 +2755,10 @@ static IrInstruction *ir_mark_gen(IrInstruction *instruction) {
|
||||
|
||||
static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers) {
|
||||
Scope *scope = inner_scope;
|
||||
bool is_noreturn = false;
|
||||
while (scope != outer_scope) {
|
||||
if (!scope)
|
||||
return false;
|
||||
return is_noreturn;
|
||||
|
||||
if (scope->id == ScopeIdDefer) {
|
||||
AstNode *defer_node = scope->source_node;
|
||||
@ -2756,14 +2771,18 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
|
||||
Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
|
||||
IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
|
||||
if (defer_expr_value != irb->codegen->invalid_instruction) {
|
||||
ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value));
|
||||
if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) {
|
||||
is_noreturn = true;
|
||||
} else {
|
||||
ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
scope = scope->parent;
|
||||
}
|
||||
return true;
|
||||
return is_noreturn;
|
||||
}
|
||||
|
||||
static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
|
||||
@ -2822,34 +2841,6 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode
|
||||
// the above blocks are rendered by ir_gen after the rest of codegen
|
||||
}
|
||||
|
||||
static bool exec_have_err_ret_trace(CodeGen *g, IrExecutable *exec) {
|
||||
if (!g->have_err_ret_tracing)
|
||||
return false;
|
||||
FnTableEntry *fn_entry = exec_fn_entry(exec);
|
||||
if (fn_entry == nullptr)
|
||||
return false;
|
||||
if (exec->is_inline)
|
||||
return false;
|
||||
return type_can_fail(fn_entry->type_entry->data.fn.fn_type_id.return_type);
|
||||
}
|
||||
|
||||
static void ir_gen_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *node) {
|
||||
if (!exec_have_err_ret_trace(irb->codegen, irb->exec))
|
||||
return;
|
||||
|
||||
bool is_async = exec_is_async(irb->exec);
|
||||
|
||||
if (is_async) {
|
||||
//IrInstruction *err_ret_addr_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_err_ret_addr_ptr);
|
||||
//IrInstruction *return_address_ptr = ir_build_instr_addr(irb, scope, node);
|
||||
//IrInstruction *return_address_usize = ir_build_ptr_to_int(irb, scope, node, return_address_ptr);
|
||||
//ir_build_store_ptr(irb, scope, node, err_ret_addr_ptr, return_address_usize);
|
||||
return;
|
||||
}
|
||||
|
||||
ir_build_save_err_ret_addr(irb, scope, node);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
|
||||
assert(node->type == NodeTypeReturnExpr);
|
||||
|
||||
@ -2895,8 +2886,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
|
||||
|
||||
IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value);
|
||||
|
||||
bool should_inline = ir_should_inline(irb->exec, scope);
|
||||
IrInstruction *is_comptime;
|
||||
if (ir_should_inline(irb->exec, scope)) {
|
||||
if (should_inline) {
|
||||
is_comptime = ir_build_const_bool(irb, scope, node, true);
|
||||
} else {
|
||||
is_comptime = ir_build_test_comptime(irb, scope, node, is_err);
|
||||
@ -2909,7 +2901,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
|
||||
if (have_err_defers) {
|
||||
ir_gen_defers_for_block(irb, scope, outer_scope, true);
|
||||
}
|
||||
ir_gen_save_err_ret_addr(irb, scope, node);
|
||||
if (irb->codegen->have_err_ret_tracing && !should_inline) {
|
||||
ir_build_save_err_ret_addr(irb, scope, node);
|
||||
}
|
||||
ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
|
||||
|
||||
ir_set_cursor_at_end_and_append_block(irb, ok_block);
|
||||
@ -2938,7 +2932,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
|
||||
IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn");
|
||||
IrBasicBlock *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue");
|
||||
IrInstruction *is_comptime;
|
||||
if (ir_should_inline(irb->exec, scope)) {
|
||||
bool should_inline = ir_should_inline(irb->exec, scope);
|
||||
if (should_inline) {
|
||||
is_comptime = ir_build_const_bool(irb, scope, node, true);
|
||||
} else {
|
||||
is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val);
|
||||
@ -2946,10 +2941,13 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
|
||||
ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime));
|
||||
|
||||
ir_set_cursor_at_end_and_append_block(irb, return_block);
|
||||
ir_gen_defers_for_block(irb, scope, outer_scope, true);
|
||||
IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);
|
||||
ir_gen_save_err_ret_addr(irb, scope, node);
|
||||
ir_gen_async_return(irb, scope, node, err_val, false);
|
||||
if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) {
|
||||
IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);
|
||||
if (irb->codegen->have_err_ret_tracing && !should_inline) {
|
||||
ir_build_save_err_ret_addr(irb, scope, node);
|
||||
}
|
||||
ir_gen_async_return(irb, scope, node, err_val, false);
|
||||
}
|
||||
|
||||
ir_set_cursor_at_end_and_append_block(irb, continue_block);
|
||||
IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false);
|
||||
@ -4242,7 +4240,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
||||
}
|
||||
case BuiltinFnIdErrorReturnTrace:
|
||||
{
|
||||
return ir_build_error_return_trace(irb, scope, node);
|
||||
return ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null);
|
||||
}
|
||||
case BuiltinFnIdAtomicRmw:
|
||||
{
|
||||
@ -5703,7 +5701,7 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast
|
||||
|
||||
IrBasicBlock *dest_block = loop_scope->continue_block;
|
||||
ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, false);
|
||||
return ir_build_br(irb, continue_scope, node, dest_block, is_comptime);
|
||||
return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime));
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) {
|
||||
@ -6125,6 +6123,13 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
|
||||
Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
|
||||
IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_ptr_field_name);
|
||||
|
||||
if (irb->codegen->have_err_ret_tracing) {
|
||||
IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull);
|
||||
Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME);
|
||||
IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);
|
||||
ir_build_store_ptr(irb, parent_scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr);
|
||||
}
|
||||
|
||||
Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);
|
||||
IrInstruction *awaiter_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr,
|
||||
awaiter_handle_field_name);
|
||||
@ -6148,10 +6153,16 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
|
||||
IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_await_handle);
|
||||
IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend");
|
||||
IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, parent_scope, "NoSuspend");
|
||||
IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "Merge");
|
||||
IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "MergeSuspend");
|
||||
ir_build_cond_br(irb, parent_scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false);
|
||||
|
||||
ir_set_cursor_at_end_and_append_block(irb, no_suspend_block);
|
||||
if (irb->codegen->have_err_ret_tracing) {
|
||||
Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME);
|
||||
IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_field_name);
|
||||
IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull);
|
||||
ir_build_merge_err_ret_traces(irb, parent_scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);
|
||||
}
|
||||
Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
|
||||
IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name);
|
||||
IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr);
|
||||
@ -6173,7 +6184,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
|
||||
|
||||
ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
|
||||
ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
|
||||
ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false);
|
||||
ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));
|
||||
|
||||
ir_set_cursor_at_end_and_append_block(irb, resume_block);
|
||||
IrInstruction *yes_suspend_result = ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr);
|
||||
@ -6249,7 +6260,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
|
||||
|
||||
ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
|
||||
ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
|
||||
ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false);
|
||||
ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));
|
||||
|
||||
ir_set_cursor_at_end_and_append_block(irb, resume_block);
|
||||
return ir_build_const_void(irb, parent_scope, node);
|
||||
@ -6268,7 +6279,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
|
||||
case NodeTypeSwitchRange:
|
||||
case NodeTypeStructField:
|
||||
case NodeTypeFnDef:
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeTestDecl:
|
||||
zig_unreachable();
|
||||
case NodeTypeBlock:
|
||||
@ -6407,6 +6417,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
|
||||
IrInstruction *coro_id;
|
||||
IrInstruction *u8_ptr_type;
|
||||
IrInstruction *const_bool_false;
|
||||
IrInstruction *coro_promise_ptr;
|
||||
IrInstruction *err_ret_trace_ptr;
|
||||
TypeTableEntry *return_type;
|
||||
Buf *result_ptr_field_name;
|
||||
VariableTableEntry *coro_size_var;
|
||||
@ -6417,9 +6429,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
|
||||
VariableTableEntry *promise_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
|
||||
|
||||
return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type;
|
||||
IrInstruction *promise_init = ir_build_const_promise_init(irb, coro_scope, node, return_type);
|
||||
ir_build_var_decl(irb, coro_scope, node, promise_var, nullptr, nullptr, promise_init);
|
||||
IrInstruction *coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var, false, false);
|
||||
IrInstruction *undef = ir_build_const_undefined(irb, coro_scope, node);
|
||||
TypeTableEntry *coro_frame_type = get_promise_frame_type(irb->codegen, return_type);
|
||||
IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type);
|
||||
// TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa
|
||||
ir_build_var_decl(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef);
|
||||
coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var, false, false);
|
||||
|
||||
VariableTableEntry *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
|
||||
IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node);
|
||||
@ -6452,7 +6467,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
|
||||
ir_set_cursor_at_end_and_append_block(irb, alloc_err_block);
|
||||
// we can return undefined here, because the caller passes a pointer to the error struct field
|
||||
// in the error union result, and we populate it in case of allocation failure.
|
||||
IrInstruction *undef = ir_build_const_undefined(irb, coro_scope, node);
|
||||
ir_build_return(irb, coro_scope, node, undef);
|
||||
|
||||
ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block);
|
||||
@ -6460,13 +6474,35 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
|
||||
irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);
|
||||
|
||||
Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);
|
||||
irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr,
|
||||
irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
|
||||
awaiter_handle_field_name);
|
||||
ir_build_store_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr, null_value);
|
||||
Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
|
||||
irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr, result_field_name);
|
||||
irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
|
||||
result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
|
||||
irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr, result_ptr_field_name);
|
||||
ir_build_store_ptr(irb, coro_scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr);
|
||||
irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name);
|
||||
ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr);
|
||||
if (irb->codegen->have_err_ret_tracing) {
|
||||
// initialize the error return trace
|
||||
Buf *return_addresses_field_name = buf_create_from_str(RETURN_ADDRESSES_FIELD_NAME);
|
||||
IrInstruction *return_addresses_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, return_addresses_field_name);
|
||||
|
||||
Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME);
|
||||
err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name);
|
||||
ir_build_mark_err_ret_trace_ptr(irb, scope, node, err_ret_trace_ptr);
|
||||
|
||||
// coordinate with builtin.zig
|
||||
Buf *index_name = buf_create_from_str("index");
|
||||
IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name);
|
||||
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
|
||||
ir_build_store_ptr(irb, scope, node, index_ptr, zero);
|
||||
|
||||
Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses");
|
||||
IrInstruction *addrs_slice_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, instruction_addresses_name);
|
||||
|
||||
IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, nullptr, false);
|
||||
ir_build_store_ptr(irb, scope, node, addrs_slice_ptr, slice_value);
|
||||
}
|
||||
|
||||
|
||||
irb->exec->coro_early_final = ir_create_basic_block(irb, scope, "CoroEarlyFinal");
|
||||
@ -6517,6 +6553,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
|
||||
IrInstruction *size_of_ret_val = ir_build_size_of(irb, scope, node, return_type_inst);
|
||||
ir_build_memcpy(irb, scope, node, result_ptr_as_u8_ptr, return_value_ptr_as_u8_ptr, size_of_ret_val);
|
||||
}
|
||||
if (irb->codegen->have_err_ret_tracing) {
|
||||
Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME);
|
||||
IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);
|
||||
IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr);
|
||||
ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr);
|
||||
}
|
||||
ir_build_br(irb, scope, node, check_free_block, const_bool_false);
|
||||
|
||||
ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block);
|
||||
@ -11579,18 +11621,25 @@ static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) {
|
||||
static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
|
||||
IrInstructionErrorReturnTrace *instruction)
|
||||
{
|
||||
TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);
|
||||
TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);
|
||||
if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) {
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
|
||||
out_val->data.x_maybe = nullptr;
|
||||
if (instruction->nullable == IrInstructionErrorReturnTrace::Null) {
|
||||
TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);
|
||||
TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);
|
||||
if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) {
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
|
||||
out_val->data.x_maybe = nullptr;
|
||||
return nullable_type;
|
||||
}
|
||||
IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
|
||||
instruction->base.source_node, instruction->nullable);
|
||||
ir_link_new_instruction(new_instruction, &instruction->base);
|
||||
return nullable_type;
|
||||
} else {
|
||||
assert(ira->codegen->have_err_ret_tracing);
|
||||
IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
|
||||
instruction->base.source_node, instruction->nullable);
|
||||
ir_link_new_instruction(new_instruction, &instruction->base);
|
||||
return get_ptr_to_stack_trace_type(ira->codegen);
|
||||
}
|
||||
|
||||
IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
|
||||
instruction->base.source_node);
|
||||
ir_link_new_instruction(new_instruction, &instruction->base);
|
||||
return nullable_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_error_union(IrAnalyze *ira,
|
||||
@ -11755,7 +11804,8 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
|
||||
}
|
||||
}
|
||||
|
||||
bool comptime_arg = param_decl_node->data.param_decl.is_inline;
|
||||
bool comptime_arg = param_decl_node->data.param_decl.is_inline ||
|
||||
casted_arg->value.type->id == TypeTableEntryIdNumLitInt || casted_arg->value.type->id == TypeTableEntryIdNumLitFloat;
|
||||
|
||||
ConstExprValue *arg_val;
|
||||
|
||||
@ -11780,6 +11830,12 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
|
||||
var->shadowable = !comptime_arg;
|
||||
|
||||
*next_proto_i += 1;
|
||||
} else if (casted_arg->value.type->id == TypeTableEntryIdNumLitInt ||
|
||||
casted_arg->value.type->id == TypeTableEntryIdNumLitFloat)
|
||||
{
|
||||
ir_add_error(ira, casted_arg,
|
||||
buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/zig-lang/zig/issues/557"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!comptime_arg) {
|
||||
@ -13072,6 +13128,7 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira,
|
||||
{
|
||||
if (!is_slice(bare_struct_type)) {
|
||||
ScopeDecls *container_scope = get_container_scope(bare_struct_type);
|
||||
assert(container_scope != nullptr);
|
||||
auto entry = container_scope->decl_table.maybe_get(field_name);
|
||||
Tld *tld = entry ? entry->value : nullptr;
|
||||
if (tld && tld->id == TldIdFn) {
|
||||
@ -16702,6 +16759,11 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
if (fn_type_id.cc == CallingConventionAsync) {
|
||||
if (instruction->async_allocator_type_value == nullptr) {
|
||||
ir_add_error(ira, &instruction->base,
|
||||
buf_sprintf("async fn proto missing allocator type"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->other;
|
||||
fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value);
|
||||
if (type_is_invalid(fn_type_id.async_allocator_type))
|
||||
@ -17904,6 +17966,39 @@ static TypeTableEntry *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira,
|
||||
return out_val->type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_merge_err_ret_traces(IrAnalyze *ira,
|
||||
IrInstructionMergeErrRetTraces *instruction)
|
||||
{
|
||||
IrInstruction *coro_promise_ptr = instruction->coro_promise_ptr->other;
|
||||
if (type_is_invalid(coro_promise_ptr->value.type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
assert(coro_promise_ptr->value.type->id == TypeTableEntryIdPointer);
|
||||
TypeTableEntry *promise_frame_type = coro_promise_ptr->value.type->data.pointer.child_type;
|
||||
assert(promise_frame_type->id == TypeTableEntryIdStruct);
|
||||
TypeTableEntry *promise_result_type = promise_frame_type->data.structure.fields[1].type_entry;
|
||||
|
||||
if (!type_can_fail(promise_result_type)) {
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
|
||||
out_val->type = ira->codegen->builtin_types.entry_void;
|
||||
return out_val->type;
|
||||
}
|
||||
|
||||
IrInstruction *src_err_ret_trace_ptr = instruction->src_err_ret_trace_ptr->other;
|
||||
if (type_is_invalid(src_err_ret_trace_ptr->value.type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *dest_err_ret_trace_ptr = instruction->dest_err_ret_trace_ptr->other;
|
||||
if (type_is_invalid(dest_err_ret_trace_ptr->value.type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *result = ir_build_merge_err_ret_traces(&ira->new_irb, instruction->base.scope,
|
||||
instruction->base.source_node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);
|
||||
ir_link_new_instruction(result, &instruction->base);
|
||||
result->value.type = ira->codegen->builtin_types.entry_void;
|
||||
return result->value.type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) {
|
||||
IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope,
|
||||
instruction->base.source_node);
|
||||
@ -17912,6 +18007,18 @@ static TypeTableEntry *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira,
|
||||
return result->value.type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *ira, IrInstructionMarkErrRetTracePtr *instruction) {
|
||||
IrInstruction *err_ret_trace_ptr = instruction->err_ret_trace_ptr->other;
|
||||
if (type_is_invalid(err_ret_trace_ptr->value.type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *result = ir_build_mark_err_ret_trace_ptr(&ira->new_irb, instruction->base.scope,
|
||||
instruction->base.source_node, err_ret_trace_ptr);
|
||||
ir_link_new_instruction(result, &instruction->base);
|
||||
result->value.type = ira->codegen->builtin_types.entry_void;
|
||||
return result->value.type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
|
||||
switch (instruction->id) {
|
||||
case IrInstructionIdInvalid:
|
||||
@ -18155,6 +18262,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||
return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction);
|
||||
case IrInstructionIdAddImplicitReturnType:
|
||||
return ir_analyze_instruction_add_implicit_return_type(ira, (IrInstructionAddImplicitReturnType *)instruction);
|
||||
case IrInstructionIdMergeErrRetTraces:
|
||||
return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction);
|
||||
case IrInstructionIdMarkErrRetTracePtr:
|
||||
return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -18282,6 +18393,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
case IrInstructionIdAwaitBookkeeping:
|
||||
case IrInstructionIdSaveErrRetAddr:
|
||||
case IrInstructionIdAddImplicitReturnType:
|
||||
case IrInstructionIdMergeErrRetTraces:
|
||||
case IrInstructionIdMarkErrRetTracePtr:
|
||||
return true;
|
||||
|
||||
case IrInstructionIdPhi:
|
||||
|
@ -1024,7 +1024,16 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {
|
||||
}
|
||||
|
||||
static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) {
|
||||
fprintf(irp->f, "@errorReturnTrace()");
|
||||
fprintf(irp->f, "@errorReturnTrace(");
|
||||
switch (instruction->nullable) {
|
||||
case IrInstructionErrorReturnTrace::Null:
|
||||
fprintf(irp->f, "Null");
|
||||
break;
|
||||
case IrInstructionErrorReturnTrace::NonNull:
|
||||
fprintf(irp->f, "NonNull");
|
||||
break;
|
||||
}
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_error_union(IrPrint *irp, IrInstructionErrorUnion *instruction) {
|
||||
@ -1179,6 +1188,22 @@ static void ir_print_add_implicit_return_type(IrPrint *irp, IrInstructionAddImpl
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_merge_err_ret_traces(IrPrint *irp, IrInstructionMergeErrRetTraces *instruction) {
|
||||
fprintf(irp->f, "@mergeErrRetTraces(");
|
||||
ir_print_other_instruction(irp, instruction->coro_promise_ptr);
|
||||
fprintf(irp->f, ",");
|
||||
ir_print_other_instruction(irp, instruction->src_err_ret_trace_ptr);
|
||||
fprintf(irp->f, ",");
|
||||
ir_print_other_instruction(irp, instruction->dest_err_ret_trace_ptr);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRetTracePtr *instruction) {
|
||||
fprintf(irp->f, "@markErrRetTracePtr(");
|
||||
ir_print_other_instruction(irp, instruction->err_ret_trace_ptr);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
ir_print_prefix(irp, instruction);
|
||||
switch (instruction->id) {
|
||||
@ -1559,6 +1584,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdAddImplicitReturnType:
|
||||
ir_print_add_implicit_return_type(irp, (IrInstructionAddImplicitReturnType *)instruction);
|
||||
break;
|
||||
case IrInstructionIdMergeErrRetTraces:
|
||||
ir_print_merge_err_ret_traces(irp, (IrInstructionMergeErrRetTraces *)instruction);
|
||||
break;
|
||||
case IrInstructionIdMarkErrRetTracePtr:
|
||||
ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction);
|
||||
break;
|
||||
}
|
||||
fprintf(irp->f, "\n");
|
||||
}
|
||||
|
@ -2923,9 +2923,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
|
||||
visit_field(&node->data.fn_def.fn_proto, visit, context);
|
||||
visit_field(&node->data.fn_def.body, visit, context);
|
||||
break;
|
||||
case NodeTypeFnDecl:
|
||||
visit_field(&node->data.fn_decl.fn_proto, visit, context);
|
||||
break;
|
||||
case NodeTypeParamDecl:
|
||||
visit_field(&node->data.param_decl.type, visit, context);
|
||||
break;
|
||||
|
@ -1,6 +1,8 @@
|
||||
const HashMap = @import("hash_map.zig").HashMap;
|
||||
const mem = @import("mem.zig");
|
||||
const std = @import("index.zig");
|
||||
const HashMap = std.HashMap;
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
/// BufMap copies keys and values before they go into the map, and
|
||||
/// frees them when they get removed.
|
||||
@ -28,18 +30,12 @@ pub const BufMap = struct {
|
||||
}
|
||||
|
||||
pub fn set(self: &BufMap, key: []const u8, value: []const u8) !void {
|
||||
if (self.hash_map.get(key)) |entry| {
|
||||
const value_copy = try self.copy(value);
|
||||
errdefer self.free(value_copy);
|
||||
_ = try self.hash_map.put(key, value_copy);
|
||||
self.free(entry.value);
|
||||
} else {
|
||||
const key_copy = try self.copy(key);
|
||||
errdefer self.free(key_copy);
|
||||
const value_copy = try self.copy(value);
|
||||
errdefer self.free(value_copy);
|
||||
_ = try self.hash_map.put(key_copy, value_copy);
|
||||
}
|
||||
self.delete(key);
|
||||
const key_copy = try self.copy(key);
|
||||
errdefer self.free(key_copy);
|
||||
const value_copy = try self.copy(value);
|
||||
errdefer self.free(value_copy);
|
||||
_ = try self.hash_map.put(key_copy, value_copy);
|
||||
}
|
||||
|
||||
pub fn get(self: &BufMap, key: []const u8) ?[]const u8 {
|
||||
@ -66,8 +62,29 @@ pub const BufMap = struct {
|
||||
}
|
||||
|
||||
fn copy(self: &BufMap, value: []const u8) ![]const u8 {
|
||||
const result = try self.hash_map.allocator.alloc(u8, value.len);
|
||||
mem.copy(u8, result, value);
|
||||
return result;
|
||||
return mem.dupe(self.hash_map.allocator, u8, value);
|
||||
}
|
||||
};
|
||||
|
||||
test "BufMap" {
|
||||
var direct_allocator = std.heap.DirectAllocator.init();
|
||||
defer direct_allocator.deinit();
|
||||
|
||||
var bufmap = BufMap.init(&direct_allocator.allocator);
|
||||
defer bufmap.deinit();
|
||||
|
||||
try bufmap.set("x", "1");
|
||||
assert(mem.eql(u8, ??bufmap.get("x"), "1"));
|
||||
assert(1 == bufmap.count());
|
||||
|
||||
try bufmap.set("x", "2");
|
||||
assert(mem.eql(u8, ??bufmap.get("x"), "2"));
|
||||
assert(1 == bufmap.count());
|
||||
|
||||
try bufmap.set("x", "3");
|
||||
assert(mem.eql(u8, ??bufmap.get("x"), "3"));
|
||||
assert(1 == bufmap.count());
|
||||
|
||||
bufmap.delete("x");
|
||||
assert(0 == bufmap.count());
|
||||
}
|
||||
|
@ -55,3 +55,11 @@ pub const dirent = extern struct {
|
||||
d_type: u8,
|
||||
d_name: u8, // field address is address of first byte of name
|
||||
};
|
||||
|
||||
pub const sockaddr = extern struct {
|
||||
sa_len: u8,
|
||||
sa_family: sa_family_t,
|
||||
sa_data: [14]u8,
|
||||
};
|
||||
|
||||
pub const sa_family_t = u8;
|
||||
|
@ -1,25 +0,0 @@
|
||||
const mem = @import("mem.zig");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub fn swapIfLe(comptime T: type, x: T) T {
|
||||
return swapIf(builtin.Endian.Little, T, x);
|
||||
}
|
||||
|
||||
pub fn swapIfBe(comptime T: type, x: T) T {
|
||||
return swapIf(builtin.Endian.Big, T, x);
|
||||
}
|
||||
|
||||
pub fn swapIf(endian: builtin.Endian, comptime T: type, x: T) T {
|
||||
return if (builtin.endian == endian) swap(T, x) else x;
|
||||
}
|
||||
|
||||
pub fn swap(comptime T: type, x: T) T {
|
||||
var buf: [@sizeOf(T)]u8 = undefined;
|
||||
mem.writeInt(buf[0..], x, builtin.Endian.Little);
|
||||
return mem.readInt(buf, T, builtin.Endian.Big);
|
||||
}
|
||||
|
||||
test "swap" {
|
||||
const debug = @import("debug/index.zig");
|
||||
debug.assert(swap(u32, 0xDEADBEEF) == 0xEFBEADDE);
|
||||
}
|
235
std/event.zig
Normal file
235
std/event.zig
Normal file
@ -0,0 +1,235 @@
|
||||
const std = @import("index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const event = this;
|
||||
const mem = std.mem;
|
||||
const posix = std.os.posix;
|
||||
|
||||
pub const TcpServer = struct {
|
||||
handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File) void,
|
||||
|
||||
loop: &Loop,
|
||||
sockfd: i32,
|
||||
accept_coro: ?promise,
|
||||
listen_address: std.net.Address,
|
||||
|
||||
waiting_for_emfile_node: PromiseNode,
|
||||
|
||||
const PromiseNode = std.LinkedList(promise).Node;
|
||||
|
||||
pub fn init(loop: &Loop) !TcpServer {
|
||||
const sockfd = try std.os.posixSocket(posix.AF_INET,
|
||||
posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK,
|
||||
posix.PROTO_tcp);
|
||||
errdefer std.os.close(sockfd);
|
||||
|
||||
// TODO can't initialize handler coroutine here because we need well defined copy elision
|
||||
return TcpServer {
|
||||
.loop = loop,
|
||||
.sockfd = sockfd,
|
||||
.accept_coro = null,
|
||||
.handleRequestFn = undefined,
|
||||
.waiting_for_emfile_node = undefined,
|
||||
.listen_address = undefined,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn listen(self: &TcpServer, address: &const std.net.Address,
|
||||
handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File)void) !void
|
||||
{
|
||||
self.handleRequestFn = handleRequestFn;
|
||||
|
||||
try std.os.posixBind(self.sockfd, &address.os_addr);
|
||||
try std.os.posixListen(self.sockfd, posix.SOMAXCONN);
|
||||
self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd));
|
||||
|
||||
self.accept_coro = try async<self.loop.allocator> TcpServer.handler(self);
|
||||
errdefer cancel ??self.accept_coro;
|
||||
|
||||
try self.loop.addFd(self.sockfd, ??self.accept_coro);
|
||||
errdefer self.loop.removeFd(self.sockfd);
|
||||
|
||||
}
|
||||
|
||||
pub fn deinit(self: &TcpServer) void {
|
||||
self.loop.removeFd(self.sockfd);
|
||||
if (self.accept_coro) |accept_coro| cancel accept_coro;
|
||||
std.os.close(self.sockfd);
|
||||
}
|
||||
|
||||
pub async fn handler(self: &TcpServer) void {
|
||||
while (true) {
|
||||
var accepted_addr: std.net.Address = undefined;
|
||||
if (std.os.posixAccept(self.sockfd, &accepted_addr.os_addr,
|
||||
posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd|
|
||||
{
|
||||
var socket = std.os.File.openHandle(accepted_fd);
|
||||
_ = async<self.loop.allocator> self.handleRequestFn(self, accepted_addr, socket) catch |err| switch (err) {
|
||||
error.OutOfMemory => {
|
||||
socket.close();
|
||||
continue;
|
||||
},
|
||||
};
|
||||
} else |err| switch (err) {
|
||||
error.WouldBlock => {
|
||||
suspend; // we will get resumed by epoll_wait in the event loop
|
||||
continue;
|
||||
},
|
||||
error.ProcessFdQuotaExceeded => {
|
||||
errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
|
||||
suspend |p| {
|
||||
self.waiting_for_emfile_node = PromiseNode.init(p);
|
||||
std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
|
||||
}
|
||||
continue;
|
||||
},
|
||||
error.ConnectionAborted,
|
||||
error.FileDescriptorClosed => continue,
|
||||
|
||||
error.PageFault => unreachable,
|
||||
error.InvalidSyscall => unreachable,
|
||||
error.FileDescriptorNotASocket => unreachable,
|
||||
error.OperationNotSupported => unreachable,
|
||||
|
||||
error.SystemFdQuotaExceeded,
|
||||
error.SystemResources,
|
||||
error.ProtocolFailure,
|
||||
error.BlockedByFirewall,
|
||||
error.Unexpected => {
|
||||
@panic("TODO handle this error");
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Loop = struct {
|
||||
allocator: &mem.Allocator,
|
||||
epollfd: i32,
|
||||
keep_running: bool,
|
||||
|
||||
fn init(allocator: &mem.Allocator) !Loop {
|
||||
const epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC);
|
||||
return Loop {
|
||||
.keep_running = true,
|
||||
.allocator = allocator,
|
||||
.epollfd = epollfd,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addFd(self: &Loop, fd: i32, prom: promise) !void {
|
||||
var ev = std.os.linux.epoll_event {
|
||||
.events = std.os.linux.EPOLLIN|std.os.linux.EPOLLOUT|std.os.linux.EPOLLET,
|
||||
.data = std.os.linux.epoll_data {
|
||||
.ptr = @ptrToInt(prom),
|
||||
},
|
||||
};
|
||||
try std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev);
|
||||
}
|
||||
|
||||
pub fn removeFd(self: &Loop, fd: i32) void {
|
||||
std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
|
||||
}
|
||||
|
||||
async fn waitFd(self: &Loop, fd: i32) !void {
|
||||
defer self.removeFd(fd);
|
||||
suspend |p| {
|
||||
try self.addFd(fd, p);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop(self: &Loop) void {
|
||||
// TODO make atomic
|
||||
self.keep_running = false;
|
||||
// TODO activate an fd in the epoll set
|
||||
}
|
||||
|
||||
pub fn run(self: &Loop) void {
|
||||
while (self.keep_running) {
|
||||
var events: [16]std.os.linux.epoll_event = undefined;
|
||||
const count = std.os.linuxEpollWait(self.epollfd, events[0..], -1);
|
||||
for (events[0..count]) |ev| {
|
||||
const p = @intToPtr(promise, ev.data.ptr);
|
||||
resume p;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub async fn connect(loop: &Loop, _address: &const std.net.Address) !std.os.File {
|
||||
var address = *_address; // TODO https://github.com/zig-lang/zig/issues/733
|
||||
|
||||
const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK, posix.PROTO_tcp);
|
||||
errdefer std.os.close(sockfd);
|
||||
|
||||
try std.os.posixConnectAsync(sockfd, &address.os_addr);
|
||||
try await try async loop.waitFd(sockfd);
|
||||
try std.os.posixGetSockOptConnectError(sockfd);
|
||||
|
||||
return std.os.File.openHandle(sockfd);
|
||||
}
|
||||
|
||||
test "listen on a port, send bytes, receive bytes" {
|
||||
if (builtin.os != builtin.Os.linux) {
|
||||
// TODO build abstractions for other operating systems
|
||||
return;
|
||||
}
|
||||
const MyServer = struct {
|
||||
tcp_server: TcpServer,
|
||||
|
||||
const Self = this;
|
||||
|
||||
async<&mem.Allocator> fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address,
|
||||
_socket: &const std.os.File) void
|
||||
{
|
||||
const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
|
||||
var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733
|
||||
defer socket.close();
|
||||
const next_handler = async errorableHandler(self, _addr, socket) catch |err| switch (err) {
|
||||
error.OutOfMemory => @panic("unable to handle connection: out of memory"),
|
||||
};
|
||||
(await next_handler) catch |err| {
|
||||
std.debug.panic("unable to handle connection: {}\n", err);
|
||||
};
|
||||
suspend |p| { cancel p; }
|
||||
}
|
||||
|
||||
async fn errorableHandler(self: &Self, _addr: &const std.net.Address,
|
||||
_socket: &const std.os.File) !void
|
||||
{
|
||||
const addr = *_addr; // TODO https://github.com/zig-lang/zig/issues/733
|
||||
var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733
|
||||
|
||||
var adapter = std.io.FileOutStream.init(&socket);
|
||||
var stream = &adapter.stream;
|
||||
try stream.print("hello from server\n");
|
||||
}
|
||||
};
|
||||
|
||||
const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable;
|
||||
const addr = std.net.Address.initIp4(ip4addr, 0);
|
||||
|
||||
var loop = try Loop.init(std.debug.global_allocator);
|
||||
var server = MyServer {
|
||||
.tcp_server = try TcpServer.init(&loop),
|
||||
};
|
||||
defer server.tcp_server.deinit();
|
||||
try server.tcp_server.listen(addr, MyServer.handler);
|
||||
|
||||
const p = try async<std.debug.global_allocator> doAsyncTest(&loop, server.tcp_server.listen_address);
|
||||
defer cancel p;
|
||||
loop.run();
|
||||
}
|
||||
|
||||
async fn doAsyncTest(loop: &Loop, address: &const std.net.Address) void {
|
||||
errdefer @panic("test failure");
|
||||
|
||||
var socket_file = try await try async event.connect(loop, address);
|
||||
defer socket_file.close();
|
||||
|
||||
var buf: [512]u8 = undefined;
|
||||
const amt_read = try socket_file.read(buf[0..]);
|
||||
const msg = buf[0..amt_read];
|
||||
assert(mem.eql(u8, msg, "hello from server\n"));
|
||||
loop.stop();
|
||||
}
|
@ -465,7 +465,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned
|
||||
return x;
|
||||
}
|
||||
|
||||
fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
|
||||
pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
|
||||
const value = switch (c) {
|
||||
'0' ... '9' => c - '0',
|
||||
'A' ... 'Z' => c - 'A' + 10,
|
||||
|
112
std/hash/adler.zig
Normal file
112
std/hash/adler.zig
Normal file
@ -0,0 +1,112 @@
|
||||
// Adler32 checksum.
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc1950#section-9
|
||||
// https://github.com/madler/zlib/blob/master/adler32.c
|
||||
|
||||
const std = @import("../index.zig");
|
||||
const debug = std.debug;
|
||||
|
||||
pub const Adler32 = struct {
|
||||
const base = 65521;
|
||||
const nmax = 5552;
|
||||
|
||||
adler: u32,
|
||||
|
||||
pub fn init() Adler32 {
|
||||
return Adler32 {
|
||||
.adler = 1,
|
||||
};
|
||||
}
|
||||
|
||||
// This fast variant is taken from zlib. It reduces the required modulos and unrolls longer
|
||||
// buffer inputs and should be much quicker.
|
||||
pub fn update(self: &Adler32, input: []const u8) void {
|
||||
var s1 = self.adler & 0xffff;
|
||||
var s2 = (self.adler >> 16) & 0xffff;
|
||||
|
||||
if (input.len == 1) {
|
||||
s1 +%= input[0];
|
||||
if (s1 >= base) {
|
||||
s1 -= base;
|
||||
}
|
||||
s2 +%= s1;
|
||||
if (s2 >= base) {
|
||||
s2 -= base;
|
||||
}
|
||||
}
|
||||
else if (input.len < 16) {
|
||||
for (input) |b| {
|
||||
s1 +%= b;
|
||||
s2 +%= s1;
|
||||
}
|
||||
if (s1 >= base) {
|
||||
s1 -= base;
|
||||
}
|
||||
|
||||
s2 %= base;
|
||||
}
|
||||
else {
|
||||
var i: usize = 0;
|
||||
while (i + nmax <= input.len) : (i += nmax) {
|
||||
const n = nmax / 16; // note: 16 | nmax
|
||||
|
||||
var rounds: usize = 0;
|
||||
while (rounds < n) : (rounds += 1) {
|
||||
comptime var j: usize = 0;
|
||||
inline while (j < 16) : (j += 1) {
|
||||
s1 +%= input[i + n * j];
|
||||
s2 +%= s1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i < input.len) {
|
||||
while (i + 16 <= input.len) : (i += 16) {
|
||||
comptime var j: usize = 0;
|
||||
inline while (j < 16) : (j += 1) {
|
||||
s1 +%= input[i + j];
|
||||
s2 +%= s1;
|
||||
}
|
||||
}
|
||||
while (i < input.len) : (i += 1) {
|
||||
s1 +%= input[i];
|
||||
s2 +%= s1;
|
||||
}
|
||||
|
||||
s1 %= base;
|
||||
s2 %= base;
|
||||
}
|
||||
}
|
||||
|
||||
self.adler = s1 | (s2 << 16);
|
||||
}
|
||||
|
||||
pub fn final(self: &Adler32) u32 {
|
||||
return self.adler;
|
||||
}
|
||||
|
||||
pub fn hash(input: []const u8) u32 {
|
||||
var c = Adler32.init();
|
||||
c.update(input);
|
||||
return c.final();
|
||||
}
|
||||
};
|
||||
|
||||
test "adler32 sanity" {
|
||||
debug.assert(Adler32.hash("a") == 0x620062);
|
||||
debug.assert(Adler32.hash("example") == 0xbc002ed);
|
||||
}
|
||||
|
||||
test "adler32 long" {
|
||||
const long1 = []u8 {1} ** 1024;
|
||||
debug.assert(Adler32.hash(long1[0..]) == 0x06780401);
|
||||
|
||||
const long2 = []u8 {1} ** 1025;
|
||||
debug.assert(Adler32.hash(long2[0..]) == 0x0a7a0402);
|
||||
}
|
||||
|
||||
test "adler32 very long" {
|
||||
const long = []u8 {1} ** 5553;
|
||||
debug.assert(Adler32.hash(long[0..]) == 0x707f15b2);
|
||||
}
|
||||
|
180
std/hash/crc.zig
Normal file
180
std/hash/crc.zig
Normal file
@ -0,0 +1,180 @@
|
||||
// There are two implementations of CRC32 implemented with the following key characteristics:
|
||||
//
|
||||
// - Crc32WithPoly uses 8Kb of tables but is ~10x faster than the small method.
|
||||
//
|
||||
// - Crc32SmallWithPoly uses only 64 bytes of memory but is slower. Be aware that this is
|
||||
// still moderately fast just slow relative to the slicing approach.
|
||||
|
||||
const std = @import("../index.zig");
|
||||
const debug = std.debug;
|
||||
|
||||
pub const Polynomial = struct {
|
||||
const IEEE = 0xedb88320;
|
||||
const Castagnoli = 0x82f63b78;
|
||||
const Koopman = 0xeb31d82e;
|
||||
};
|
||||
|
||||
// IEEE is by far the most common CRC and so is aliased by default.
|
||||
pub const Crc32 = Crc32WithPoly(Polynomial.IEEE);
|
||||
|
||||
// slicing-by-8 crc32 implementation.
|
||||
pub fn Crc32WithPoly(comptime poly: u32) type {
|
||||
return struct {
|
||||
const Self = this;
|
||||
const lookup_tables = comptime block: {
|
||||
@setEvalBranchQuota(20000);
|
||||
var tables: [8][256]u32 = undefined;
|
||||
|
||||
for (tables[0]) |*e, i| {
|
||||
var crc = u32(i);
|
||||
var j: usize = 0; while (j < 8) : (j += 1) {
|
||||
if (crc & 1 == 1) {
|
||||
crc = (crc >> 1) ^ poly;
|
||||
} else {
|
||||
crc = (crc >> 1);
|
||||
}
|
||||
}
|
||||
*e = crc;
|
||||
}
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < 256) : (i += 1) {
|
||||
var crc = tables[0][i];
|
||||
var j: usize = 1; while (j < 8) : (j += 1) {
|
||||
const index = @truncate(u8, crc);
|
||||
crc = tables[0][index] ^ (crc >> 8);
|
||||
tables[j][i] = crc;
|
||||
}
|
||||
}
|
||||
|
||||
break :block tables;
|
||||
};
|
||||
|
||||
crc: u32,
|
||||
|
||||
pub fn init() Self {
|
||||
return Self {
|
||||
.crc = 0xffffffff,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn update(self: &Self, input: []const u8) void {
|
||||
var i: usize = 0;
|
||||
while (i + 8 <= input.len) : (i += 8) {
|
||||
const p = input[i..i+8];
|
||||
|
||||
// Unrolling this way gives ~50Mb/s increase
|
||||
self.crc ^= (u32(p[0]) << 0);
|
||||
self.crc ^= (u32(p[1]) << 8);
|
||||
self.crc ^= (u32(p[2]) << 16);
|
||||
self.crc ^= (u32(p[3]) << 24);
|
||||
|
||||
self.crc =
|
||||
lookup_tables[0][p[7]] ^
|
||||
lookup_tables[1][p[6]] ^
|
||||
lookup_tables[2][p[5]] ^
|
||||
lookup_tables[3][p[4]] ^
|
||||
lookup_tables[4][@truncate(u8, self.crc >> 24)] ^
|
||||
lookup_tables[5][@truncate(u8, self.crc >> 16)] ^
|
||||
lookup_tables[6][@truncate(u8, self.crc >> 8)] ^
|
||||
lookup_tables[7][@truncate(u8, self.crc >> 0)];
|
||||
}
|
||||
|
||||
while (i < input.len) : (i += 1) {
|
||||
const index = @truncate(u8, self.crc) ^ input[i];
|
||||
self.crc = (self.crc >> 8) ^ lookup_tables[0][index];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn final(self: &Self) u32 {
|
||||
return ~self.crc;
|
||||
}
|
||||
|
||||
pub fn hash(input: []const u8) u32 {
|
||||
var c = Self.init();
|
||||
c.update(input);
|
||||
return c.final();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "crc32 ieee" {
|
||||
const Crc32Ieee = Crc32WithPoly(Polynomial.IEEE);
|
||||
|
||||
debug.assert(Crc32Ieee.hash("") == 0x00000000);
|
||||
debug.assert(Crc32Ieee.hash("a") == 0xe8b7be43);
|
||||
debug.assert(Crc32Ieee.hash("abc") == 0x352441c2);
|
||||
}
|
||||
|
||||
test "crc32 castagnoli" {
|
||||
const Crc32Castagnoli = Crc32WithPoly(Polynomial.Castagnoli);
|
||||
|
||||
debug.assert(Crc32Castagnoli.hash("") == 0x00000000);
|
||||
debug.assert(Crc32Castagnoli.hash("a") == 0xc1d04330);
|
||||
debug.assert(Crc32Castagnoli.hash("abc") == 0x364b3fb7);
|
||||
}
|
||||
|
||||
// half-byte lookup table implementation.
|
||||
pub fn Crc32SmallWithPoly(comptime poly: u32) type {
|
||||
return struct {
|
||||
const Self = this;
|
||||
const lookup_table = comptime block: {
|
||||
var table: [16]u32 = undefined;
|
||||
|
||||
for (table) |*e, i| {
|
||||
var crc = u32(i * 16);
|
||||
var j: usize = 0; while (j < 8) : (j += 1) {
|
||||
if (crc & 1 == 1) {
|
||||
crc = (crc >> 1) ^ poly;
|
||||
} else {
|
||||
crc = (crc >> 1);
|
||||
}
|
||||
}
|
||||
*e = crc;
|
||||
}
|
||||
|
||||
break :block table;
|
||||
};
|
||||
|
||||
crc: u32,
|
||||
|
||||
pub fn init() Self {
|
||||
return Self {
|
||||
.crc = 0xffffffff,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn update(self: &Self, input: []const u8) void {
|
||||
for (input) |b| {
|
||||
self.crc = lookup_table[@truncate(u4, self.crc ^ (b >> 0))] ^ (self.crc >> 4);
|
||||
self.crc = lookup_table[@truncate(u4, self.crc ^ (b >> 4))] ^ (self.crc >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn final(self: &Self) u32 {
|
||||
return ~self.crc;
|
||||
}
|
||||
|
||||
pub fn hash(input: []const u8) u32 {
|
||||
var c = Self.init();
|
||||
c.update(input);
|
||||
return c.final();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "small crc32 ieee" {
|
||||
const Crc32Ieee = Crc32SmallWithPoly(Polynomial.IEEE);
|
||||
|
||||
debug.assert(Crc32Ieee.hash("") == 0x00000000);
|
||||
debug.assert(Crc32Ieee.hash("a") == 0xe8b7be43);
|
||||
debug.assert(Crc32Ieee.hash("abc") == 0x352441c2);
|
||||
}
|
||||
|
||||
test "small crc32 castagnoli" {
|
||||
const Crc32Castagnoli = Crc32SmallWithPoly(Polynomial.Castagnoli);
|
||||
|
||||
debug.assert(Crc32Castagnoli.hash("") == 0x00000000);
|
||||
debug.assert(Crc32Castagnoli.hash("a") == 0xc1d04330);
|
||||
debug.assert(Crc32Castagnoli.hash("abc") == 0x364b3fb7);
|
||||
}
|
60
std/hash/fnv.zig
Normal file
60
std/hash/fnv.zig
Normal file
@ -0,0 +1,60 @@
|
||||
// FNV1a - Fowler-Noll-Vo hash function
|
||||
//
|
||||
// FNV1a is a fast, non-cryptographic hash function with fairly good distribution properties.
|
||||
//
|
||||
// https://tools.ietf.org/html/draft-eastlake-fnv-14
|
||||
|
||||
const std = @import("../index.zig");
|
||||
const debug = std.debug;
|
||||
|
||||
pub const Fnv1a_32 = Fnv1a(u32, 0x01000193 , 0x811c9dc5);
|
||||
pub const Fnv1a_64 = Fnv1a(u64, 0x100000001b3, 0xcbf29ce484222325);
|
||||
pub const Fnv1a_128 = Fnv1a(u128, 0x1000000000000000000013b, 0x6c62272e07bb014262b821756295c58d);
|
||||
|
||||
fn Fnv1a(comptime T: type, comptime prime: T, comptime offset: T) type {
|
||||
return struct {
|
||||
const Self = this;
|
||||
|
||||
value: T,
|
||||
|
||||
pub fn init() Self {
|
||||
return Self {
|
||||
.value = offset,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn update(self: &Self, input: []const u8) void {
|
||||
for (input) |b| {
|
||||
self.value ^= b;
|
||||
self.value *%= prime;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn final(self: &Self) T {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
pub fn hash(input: []const u8) T {
|
||||
var c = Self.init();
|
||||
c.update(input);
|
||||
return c.final();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "fnv1a-32" {
|
||||
debug.assert(Fnv1a_32.hash("") == 0x811c9dc5);
|
||||
debug.assert(Fnv1a_32.hash("a") == 0xe40c292c);
|
||||
debug.assert(Fnv1a_32.hash("foobar") == 0xbf9cf968);
|
||||
}
|
||||
|
||||
test "fnv1a-64" {
|
||||
debug.assert(Fnv1a_64.hash("") == 0xcbf29ce484222325);
|
||||
debug.assert(Fnv1a_64.hash("a") == 0xaf63dc4c8601ec8c);
|
||||
debug.assert(Fnv1a_64.hash("foobar") == 0x85944171f73967e8);
|
||||
}
|
||||
|
||||
test "fnv1a-128" {
|
||||
debug.assert(Fnv1a_128.hash("") == 0x6c62272e07bb014262b821756295c58d);
|
||||
debug.assert(Fnv1a_128.hash("a") == 0xd228cb696f1a8caf78912b704e4a8964);
|
||||
}
|
22
std/hash/index.zig
Normal file
22
std/hash/index.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const adler = @import("adler.zig");
|
||||
pub const Adler32 = adler.Adler32;
|
||||
|
||||
// pub for polynomials + generic crc32 construction
|
||||
pub const crc = @import("crc.zig");
|
||||
pub const Crc32 = crc.Crc32;
|
||||
|
||||
const fnv = @import("fnv.zig");
|
||||
pub const Fnv1a_32 = fnv.Fnv1a_32;
|
||||
pub const Fnv1a_64 = fnv.Fnv1a_64;
|
||||
pub const Fnv1a_128 = fnv.Fnv1a_128;
|
||||
|
||||
const siphash = @import("siphash.zig");
|
||||
pub const SipHash64 = siphash.SipHash64;
|
||||
pub const SipHash128 = siphash.SipHash128;
|
||||
|
||||
test "hash" {
|
||||
_ = @import("adler.zig");
|
||||
_ = @import("crc.zig");
|
||||
_ = @import("fnv.zig");
|
||||
_ = @import("siphash.zig");
|
||||
}
|
320
std/hash/siphash.zig
Normal file
320
std/hash/siphash.zig
Normal file
@ -0,0 +1,320 @@
|
||||
// Siphash
|
||||
//
|
||||
// SipHash is a moderately fast, non-cryptographic keyed hash function designed for resistance
|
||||
// against hash flooding DoS attacks.
|
||||
//
|
||||
// https://131002.net/siphash/
|
||||
|
||||
const std = @import("../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
|
||||
const Endian = @import("builtin").Endian;
|
||||
|
||||
pub fn SipHash64(comptime c_rounds: usize, comptime d_rounds: usize) type {
|
||||
return SipHash(u64, c_rounds, d_rounds);
|
||||
}
|
||||
|
||||
pub fn SipHash128(comptime c_rounds: usize, comptime d_rounds: usize) type {
|
||||
return SipHash(u128, c_rounds, d_rounds);
|
||||
}
|
||||
|
||||
fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type {
|
||||
debug.assert(T == u64 or T == u128);
|
||||
debug.assert(c_rounds > 0 and d_rounds > 0);
|
||||
|
||||
return struct {
|
||||
const Self = this;
|
||||
const digest_size = 64;
|
||||
const block_size = 64;
|
||||
|
||||
v0: u64,
|
||||
v1: u64,
|
||||
v2: u64,
|
||||
v3: u64,
|
||||
|
||||
// streaming cache
|
||||
buf: [8]u8,
|
||||
buf_len: usize,
|
||||
msg_len: u8,
|
||||
|
||||
pub fn init(key: []const u8) Self {
|
||||
debug.assert(key.len >= 16);
|
||||
|
||||
const k0 = mem.readInt(key[0..8], u64, Endian.Little);
|
||||
const k1 = mem.readInt(key[8..16], u64, Endian.Little);
|
||||
|
||||
var d = Self {
|
||||
.v0 = k0 ^ 0x736f6d6570736575,
|
||||
.v1 = k1 ^ 0x646f72616e646f6d,
|
||||
.v2 = k0 ^ 0x6c7967656e657261,
|
||||
.v3 = k1 ^ 0x7465646279746573,
|
||||
|
||||
.buf = undefined,
|
||||
.buf_len = 0,
|
||||
.msg_len = 0,
|
||||
};
|
||||
|
||||
if (T == u128) {
|
||||
d.v1 ^= 0xee;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
pub fn update(d: &Self, b: []const u8) void {
|
||||
var off: usize = 0;
|
||||
|
||||
// Partial from previous.
|
||||
if (d.buf_len != 0 and d.buf_len + b.len > 8) {
|
||||
off += 8 - d.buf_len;
|
||||
mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
|
||||
d.round(d.buf[0..]);
|
||||
d.buf_len = 0;
|
||||
}
|
||||
|
||||
// Full middle blocks.
|
||||
while (off + 8 <= b.len) : (off += 8) {
|
||||
d.round(b[off..off + 8]);
|
||||
}
|
||||
|
||||
// Remainder for next pass.
|
||||
mem.copy(u8, d.buf[d.buf_len..], b[off..]);
|
||||
d.buf_len += u8(b[off..].len);
|
||||
d.msg_len +%= @truncate(u8, b.len);
|
||||
}
|
||||
|
||||
pub fn final(d: &Self) T {
|
||||
// Padding
|
||||
mem.set(u8, d.buf[d.buf_len..], 0);
|
||||
d.buf[7] = d.msg_len;
|
||||
d.round(d.buf[0..]);
|
||||
|
||||
if (T == u128) {
|
||||
d.v2 ^= 0xee;
|
||||
} else {
|
||||
d.v2 ^= 0xff;
|
||||
}
|
||||
|
||||
comptime var i: usize = 0;
|
||||
inline while (i < d_rounds) : (i += 1) {
|
||||
@inlineCall(sipRound, d);
|
||||
}
|
||||
|
||||
const b1 = d.v0 ^ d.v1 ^ d.v2 ^ d.v3;
|
||||
if (T == u64) {
|
||||
return b1;
|
||||
}
|
||||
|
||||
d.v1 ^= 0xdd;
|
||||
|
||||
comptime var j: usize = 0;
|
||||
inline while (j < d_rounds) : (j += 1) {
|
||||
@inlineCall(sipRound, d);
|
||||
}
|
||||
|
||||
const b2 = d.v0 ^ d.v1 ^ d.v2 ^ d.v3;
|
||||
return (u128(b2) << 64) | b1;
|
||||
}
|
||||
|
||||
fn round(d: &Self, b: []const u8) void {
|
||||
debug.assert(b.len == 8);
|
||||
|
||||
const m = mem.readInt(b[0..], u64, Endian.Little);
|
||||
d.v3 ^= m;
|
||||
|
||||
comptime var i: usize = 0;
|
||||
inline while (i < c_rounds) : (i += 1) {
|
||||
@inlineCall(sipRound, d);
|
||||
}
|
||||
|
||||
d.v0 ^= m;
|
||||
}
|
||||
|
||||
fn sipRound(d: &Self) void {
|
||||
d.v0 +%= d.v1;
|
||||
d.v1 = math.rotl(u64, d.v1, u64(13));
|
||||
d.v1 ^= d.v0;
|
||||
d.v0 = math.rotl(u64, d.v0, u64(32));
|
||||
d.v2 +%= d.v3;
|
||||
d.v3 = math.rotl(u64, d.v3, u64(16));
|
||||
d.v3 ^= d.v2;
|
||||
d.v0 +%= d.v3;
|
||||
d.v3 = math.rotl(u64, d.v3, u64(21));
|
||||
d.v3 ^= d.v0;
|
||||
d.v2 +%= d.v1;
|
||||
d.v1 = math.rotl(u64, d.v1, u64(17));
|
||||
d.v1 ^= d.v2;
|
||||
d.v2 = math.rotl(u64, d.v2, u64(32));
|
||||
}
|
||||
|
||||
pub fn hash(key: []const u8, input: []const u8) T {
|
||||
var c = Self.init(key);
|
||||
c.update(input);
|
||||
return c.final();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Test vectors from reference implementation.
|
||||
// https://github.com/veorq/SipHash/blob/master/vectors.h
|
||||
const test_key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
|
||||
|
||||
test "siphash64-2-4 sanity" {
|
||||
const vectors = [][]const u8 {
|
||||
"\x31\x0e\x0e\xdd\x47\xdb\x6f\x72", // ""
|
||||
"\xfd\x67\xdc\x93\xc5\x39\xf8\x74", // "\x00"
|
||||
"\x5a\x4f\xa9\xd9\x09\x80\x6c\x0d", // "\x00\x01" ... etc
|
||||
"\x2d\x7e\xfb\xd7\x96\x66\x67\x85",
|
||||
"\xb7\x87\x71\x27\xe0\x94\x27\xcf",
|
||||
"\x8d\xa6\x99\xcd\x64\x55\x76\x18",
|
||||
"\xce\xe3\xfe\x58\x6e\x46\xc9\xcb",
|
||||
"\x37\xd1\x01\x8b\xf5\x00\x02\xab",
|
||||
"\x62\x24\x93\x9a\x79\xf5\xf5\x93",
|
||||
"\xb0\xe4\xa9\x0b\xdf\x82\x00\x9e",
|
||||
"\xf3\xb9\xdd\x94\xc5\xbb\x5d\x7a",
|
||||
"\xa7\xad\x6b\x22\x46\x2f\xb3\xf4",
|
||||
"\xfb\xe5\x0e\x86\xbc\x8f\x1e\x75",
|
||||
"\x90\x3d\x84\xc0\x27\x56\xea\x14",
|
||||
"\xee\xf2\x7a\x8e\x90\xca\x23\xf7",
|
||||
"\xe5\x45\xbe\x49\x61\xca\x29\xa1",
|
||||
"\xdb\x9b\xc2\x57\x7f\xcc\x2a\x3f",
|
||||
"\x94\x47\xbe\x2c\xf5\xe9\x9a\x69",
|
||||
"\x9c\xd3\x8d\x96\xf0\xb3\xc1\x4b",
|
||||
"\xbd\x61\x79\xa7\x1d\xc9\x6d\xbb",
|
||||
"\x98\xee\xa2\x1a\xf2\x5c\xd6\xbe",
|
||||
"\xc7\x67\x3b\x2e\xb0\xcb\xf2\xd0",
|
||||
"\x88\x3e\xa3\xe3\x95\x67\x53\x93",
|
||||
"\xc8\xce\x5c\xcd\x8c\x03\x0c\xa8",
|
||||
"\x94\xaf\x49\xf6\xc6\x50\xad\xb8",
|
||||
"\xea\xb8\x85\x8a\xde\x92\xe1\xbc",
|
||||
"\xf3\x15\xbb\x5b\xb8\x35\xd8\x17",
|
||||
"\xad\xcf\x6b\x07\x63\x61\x2e\x2f",
|
||||
"\xa5\xc9\x1d\xa7\xac\xaa\x4d\xde",
|
||||
"\x71\x65\x95\x87\x66\x50\xa2\xa6",
|
||||
"\x28\xef\x49\x5c\x53\xa3\x87\xad",
|
||||
"\x42\xc3\x41\xd8\xfa\x92\xd8\x32",
|
||||
"\xce\x7c\xf2\x72\x2f\x51\x27\x71",
|
||||
"\xe3\x78\x59\xf9\x46\x23\xf3\xa7",
|
||||
"\x38\x12\x05\xbb\x1a\xb0\xe0\x12",
|
||||
"\xae\x97\xa1\x0f\xd4\x34\xe0\x15",
|
||||
"\xb4\xa3\x15\x08\xbe\xff\x4d\x31",
|
||||
"\x81\x39\x62\x29\xf0\x90\x79\x02",
|
||||
"\x4d\x0c\xf4\x9e\xe5\xd4\xdc\xca",
|
||||
"\x5c\x73\x33\x6a\x76\xd8\xbf\x9a",
|
||||
"\xd0\xa7\x04\x53\x6b\xa9\x3e\x0e",
|
||||
"\x92\x59\x58\xfc\xd6\x42\x0c\xad",
|
||||
"\xa9\x15\xc2\x9b\xc8\x06\x73\x18",
|
||||
"\x95\x2b\x79\xf3\xbc\x0a\xa6\xd4",
|
||||
"\xf2\x1d\xf2\xe4\x1d\x45\x35\xf9",
|
||||
"\x87\x57\x75\x19\x04\x8f\x53\xa9",
|
||||
"\x10\xa5\x6c\xf5\xdf\xcd\x9a\xdb",
|
||||
"\xeb\x75\x09\x5c\xcd\x98\x6c\xd0",
|
||||
"\x51\xa9\xcb\x9e\xcb\xa3\x12\xe6",
|
||||
"\x96\xaf\xad\xfc\x2c\xe6\x66\xc7",
|
||||
"\x72\xfe\x52\x97\x5a\x43\x64\xee",
|
||||
"\x5a\x16\x45\xb2\x76\xd5\x92\xa1",
|
||||
"\xb2\x74\xcb\x8e\xbf\x87\x87\x0a",
|
||||
"\x6f\x9b\xb4\x20\x3d\xe7\xb3\x81",
|
||||
"\xea\xec\xb2\xa3\x0b\x22\xa8\x7f",
|
||||
"\x99\x24\xa4\x3c\xc1\x31\x57\x24",
|
||||
"\xbd\x83\x8d\x3a\xaf\xbf\x8d\xb7",
|
||||
"\x0b\x1a\x2a\x32\x65\xd5\x1a\xea",
|
||||
"\x13\x50\x79\xa3\x23\x1c\xe6\x60",
|
||||
"\x93\x2b\x28\x46\xe4\xd7\x06\x66",
|
||||
"\xe1\x91\x5f\x5c\xb1\xec\xa4\x6c",
|
||||
"\xf3\x25\x96\x5c\xa1\x6d\x62\x9f",
|
||||
"\x57\x5f\xf2\x8e\x60\x38\x1b\xe5",
|
||||
"\x72\x45\x06\xeb\x4c\x32\x8a\x95",
|
||||
};
|
||||
|
||||
const siphash = SipHash64(2, 4);
|
||||
|
||||
var buffer: [64]u8 = undefined;
|
||||
for (vectors) |vector, i| {
|
||||
buffer[i] = u8(i);
|
||||
|
||||
const expected = mem.readInt(vector, u64, Endian.Little);
|
||||
debug.assert(siphash.hash(test_key, buffer[0..i]) == expected);
|
||||
}
|
||||
}
|
||||
|
||||
test "siphash128-2-4 sanity" {
|
||||
const vectors = [][]const u8 {
|
||||
"\xa3\x81\x7f\x04\xba\x25\xa8\xe6\x6d\xf6\x72\x14\xc7\x55\x02\x93",
|
||||
"\xda\x87\xc1\xd8\x6b\x99\xaf\x44\x34\x76\x59\x11\x9b\x22\xfc\x45",
|
||||
"\x81\x77\x22\x8d\xa4\xa4\x5d\xc7\xfc\xa3\x8b\xde\xf6\x0a\xff\xe4",
|
||||
"\x9c\x70\xb6\x0c\x52\x67\xa9\x4e\x5f\x33\xb6\xb0\x29\x85\xed\x51",
|
||||
"\xf8\x81\x64\xc1\x2d\x9c\x8f\xaf\x7d\x0f\x6e\x7c\x7b\xcd\x55\x79",
|
||||
"\x13\x68\x87\x59\x80\x77\x6f\x88\x54\x52\x7a\x07\x69\x0e\x96\x27",
|
||||
"\x14\xee\xca\x33\x8b\x20\x86\x13\x48\x5e\xa0\x30\x8f\xd7\xa1\x5e",
|
||||
"\xa1\xf1\xeb\xbe\xd8\xdb\xc1\x53\xc0\xb8\x4a\xa6\x1f\xf0\x82\x39",
|
||||
"\x3b\x62\xa9\xba\x62\x58\xf5\x61\x0f\x83\xe2\x64\xf3\x14\x97\xb4",
|
||||
"\x26\x44\x99\x06\x0a\xd9\xba\xab\xc4\x7f\x8b\x02\xbb\x6d\x71\xed",
|
||||
"\x00\x11\x0d\xc3\x78\x14\x69\x56\xc9\x54\x47\xd3\xf3\xd0\xfb\xba",
|
||||
"\x01\x51\xc5\x68\x38\x6b\x66\x77\xa2\xb4\xdc\x6f\x81\xe5\xdc\x18",
|
||||
"\xd6\x26\xb2\x66\x90\x5e\xf3\x58\x82\x63\x4d\xf6\x85\x32\xc1\x25",
|
||||
"\x98\x69\xe2\x47\xe9\xc0\x8b\x10\xd0\x29\x93\x4f\xc4\xb9\x52\xf7",
|
||||
"\x31\xfc\xef\xac\x66\xd7\xde\x9c\x7e\xc7\x48\x5f\xe4\x49\x49\x02",
|
||||
"\x54\x93\xe9\x99\x33\xb0\xa8\x11\x7e\x08\xec\x0f\x97\xcf\xc3\xd9",
|
||||
"\x6e\xe2\xa4\xca\x67\xb0\x54\xbb\xfd\x33\x15\xbf\x85\x23\x05\x77",
|
||||
"\x47\x3d\x06\xe8\x73\x8d\xb8\x98\x54\xc0\x66\xc4\x7a\xe4\x77\x40",
|
||||
"\xa4\x26\xe5\xe4\x23\xbf\x48\x85\x29\x4d\xa4\x81\xfe\xae\xf7\x23",
|
||||
"\x78\x01\x77\x31\xcf\x65\xfa\xb0\x74\xd5\x20\x89\x52\x51\x2e\xb1",
|
||||
"\x9e\x25\xfc\x83\x3f\x22\x90\x73\x3e\x93\x44\xa5\xe8\x38\x39\xeb",
|
||||
"\x56\x8e\x49\x5a\xbe\x52\x5a\x21\x8a\x22\x14\xcd\x3e\x07\x1d\x12",
|
||||
"\x4a\x29\xb5\x45\x52\xd1\x6b\x9a\x46\x9c\x10\x52\x8e\xff\x0a\xae",
|
||||
"\xc9\xd1\x84\xdd\xd5\xa9\xf5\xe0\xcf\x8c\xe2\x9a\x9a\xbf\x69\x1c",
|
||||
"\x2d\xb4\x79\xae\x78\xbd\x50\xd8\x88\x2a\x8a\x17\x8a\x61\x32\xad",
|
||||
"\x8e\xce\x5f\x04\x2d\x5e\x44\x7b\x50\x51\xb9\xea\xcb\x8d\x8f\x6f",
|
||||
"\x9c\x0b\x53\xb4\xb3\xc3\x07\xe8\x7e\xae\xe0\x86\x78\x14\x1f\x66",
|
||||
"\xab\xf2\x48\xaf\x69\xa6\xea\xe4\xbf\xd3\xeb\x2f\x12\x9e\xeb\x94",
|
||||
"\x06\x64\xda\x16\x68\x57\x4b\x88\xb9\x35\xf3\x02\x73\x58\xae\xf4",
|
||||
"\xaa\x4b\x9d\xc4\xbf\x33\x7d\xe9\x0c\xd4\xfd\x3c\x46\x7c\x6a\xb7",
|
||||
"\xea\x5c\x7f\x47\x1f\xaf\x6b\xde\x2b\x1a\xd7\xd4\x68\x6d\x22\x87",
|
||||
"\x29\x39\xb0\x18\x32\x23\xfa\xfc\x17\x23\xde\x4f\x52\xc4\x3d\x35",
|
||||
"\x7c\x39\x56\xca\x5e\xea\xfc\x3e\x36\x3e\x9d\x55\x65\x46\xeb\x68",
|
||||
"\x77\xc6\x07\x71\x46\xf0\x1c\x32\xb6\xb6\x9d\x5f\x4e\xa9\xff\xcf",
|
||||
"\x37\xa6\x98\x6c\xb8\x84\x7e\xdf\x09\x25\xf0\xf1\x30\x9b\x54\xde",
|
||||
"\xa7\x05\xf0\xe6\x9d\xa9\xa8\xf9\x07\x24\x1a\x2e\x92\x3c\x8c\xc8",
|
||||
"\x3d\xc4\x7d\x1f\x29\xc4\x48\x46\x1e\x9e\x76\xed\x90\x4f\x67\x11",
|
||||
"\x0d\x62\xbf\x01\xe6\xfc\x0e\x1a\x0d\x3c\x47\x51\xc5\xd3\x69\x2b",
|
||||
"\x8c\x03\x46\x8b\xca\x7c\x66\x9e\xe4\xfd\x5e\x08\x4b\xbe\xe7\xb5",
|
||||
"\x52\x8a\x5b\xb9\x3b\xaf\x2c\x9c\x44\x73\xcc\xe5\xd0\xd2\x2b\xd9",
|
||||
"\xdf\x6a\x30\x1e\x95\xc9\x5d\xad\x97\xae\x0c\xc8\xc6\x91\x3b\xd8",
|
||||
"\x80\x11\x89\x90\x2c\x85\x7f\x39\xe7\x35\x91\x28\x5e\x70\xb6\xdb",
|
||||
"\xe6\x17\x34\x6a\xc9\xc2\x31\xbb\x36\x50\xae\x34\xcc\xca\x0c\x5b",
|
||||
"\x27\xd9\x34\x37\xef\xb7\x21\xaa\x40\x18\x21\xdc\xec\x5a\xdf\x89",
|
||||
"\x89\x23\x7d\x9d\xed\x9c\x5e\x78\xd8\xb1\xc9\xb1\x66\xcc\x73\x42",
|
||||
"\x4a\x6d\x80\x91\xbf\x5e\x7d\x65\x11\x89\xfa\x94\xa2\x50\xb1\x4c",
|
||||
"\x0e\x33\xf9\x60\x55\xe7\xae\x89\x3f\xfc\x0e\x3d\xcf\x49\x29\x02",
|
||||
"\xe6\x1c\x43\x2b\x72\x0b\x19\xd1\x8e\xc8\xd8\x4b\xdc\x63\x15\x1b",
|
||||
"\xf7\xe5\xae\xf5\x49\xf7\x82\xcf\x37\x90\x55\xa6\x08\x26\x9b\x16",
|
||||
"\x43\x8d\x03\x0f\xd0\xb7\xa5\x4f\xa8\x37\xf2\xad\x20\x1a\x64\x03",
|
||||
"\xa5\x90\xd3\xee\x4f\xbf\x04\xe3\x24\x7e\x0d\x27\xf2\x86\x42\x3f",
|
||||
"\x5f\xe2\xc1\xa1\x72\xfe\x93\xc4\xb1\x5c\xd3\x7c\xae\xf9\xf5\x38",
|
||||
"\x2c\x97\x32\x5c\xbd\x06\xb3\x6e\xb2\x13\x3d\xd0\x8b\x3a\x01\x7c",
|
||||
"\x92\xc8\x14\x22\x7a\x6b\xca\x94\x9f\xf0\x65\x9f\x00\x2a\xd3\x9e",
|
||||
"\xdc\xe8\x50\x11\x0b\xd8\x32\x8c\xfb\xd5\x08\x41\xd6\x91\x1d\x87",
|
||||
"\x67\xf1\x49\x84\xc7\xda\x79\x12\x48\xe3\x2b\xb5\x92\x25\x83\xda",
|
||||
"\x19\x38\xf2\xcf\x72\xd5\x4e\xe9\x7e\x94\x16\x6f\xa9\x1d\x2a\x36",
|
||||
"\x74\x48\x1e\x96\x46\xed\x49\xfe\x0f\x62\x24\x30\x16\x04\x69\x8e",
|
||||
"\x57\xfc\xa5\xde\x98\xa9\xd6\xd8\x00\x64\x38\xd0\x58\x3d\x8a\x1d",
|
||||
"\x9f\xec\xde\x1c\xef\xdc\x1c\xbe\xd4\x76\x36\x74\xd9\x57\x53\x59",
|
||||
"\xe3\x04\x0c\x00\xeb\x28\xf1\x53\x66\xca\x73\xcb\xd8\x72\xe7\x40",
|
||||
"\x76\x97\x00\x9a\x6a\x83\x1d\xfe\xcc\xa9\x1c\x59\x93\x67\x0f\x7a",
|
||||
"\x58\x53\x54\x23\x21\xf5\x67\xa0\x05\xd5\x47\xa4\xf0\x47\x59\xbd",
|
||||
"\x51\x50\xd1\x77\x2f\x50\x83\x4a\x50\x3e\x06\x9a\x97\x3f\xbd\x7c",
|
||||
};
|
||||
|
||||
const siphash = SipHash128(2, 4);
|
||||
|
||||
var buffer: [64]u8 = undefined;
|
||||
for (vectors) |vector, i| {
|
||||
buffer[i] = u8(i);
|
||||
|
||||
const expected = mem.readInt(vector, u128, Endian.Little);
|
||||
debug.assert(siphash.hash(test_key, buffer[0..i]) == expected);
|
||||
}
|
||||
}
|
@ -114,6 +114,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
}
|
||||
|
||||
pub fn remove(hm: &Self, key: K) ?&Entry {
|
||||
if (hm.entries.len == 0) return null;
|
||||
hm.incrementModificationCount();
|
||||
const start_index = hm.keyToIndex(key);
|
||||
{var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
|
||||
@ -236,7 +237,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
}
|
||||
|
||||
test "basic hash map usage" {
|
||||
var map = HashMap(i32, i32, hash_i32, eql_i32).init(debug.global_allocator);
|
||||
var direct_allocator = std.heap.DirectAllocator.init();
|
||||
defer direct_allocator.deinit();
|
||||
|
||||
var map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
|
||||
defer map.deinit();
|
||||
|
||||
assert((map.put(1, 11) catch unreachable) == null);
|
||||
|
@ -17,8 +17,9 @@ pub const debug = @import("debug/index.zig");
|
||||
pub const dwarf = @import("dwarf.zig");
|
||||
pub const elf = @import("elf.zig");
|
||||
pub const empty_import = @import("empty.zig");
|
||||
pub const endian = @import("endian.zig");
|
||||
pub const event = @import("event.zig");
|
||||
pub const fmt = @import("fmt/index.zig");
|
||||
pub const hash = @import("hash/index.zig");
|
||||
pub const heap = @import("heap.zig");
|
||||
pub const io = @import("io.zig");
|
||||
pub const macho = @import("macho.zig");
|
||||
@ -49,14 +50,15 @@ test "std" {
|
||||
_ = @import("dwarf.zig");
|
||||
_ = @import("elf.zig");
|
||||
_ = @import("empty.zig");
|
||||
_ = @import("endian.zig");
|
||||
_ = @import("event.zig");
|
||||
_ = @import("fmt/index.zig");
|
||||
_ = @import("hash/index.zig");
|
||||
_ = @import("io.zig");
|
||||
_ = @import("macho.zig");
|
||||
_ = @import("math/index.zig");
|
||||
_ = @import("mem.zig");
|
||||
_ = @import("heap.zig");
|
||||
_ = @import("net.zig");
|
||||
_ = @import("heap.zig");
|
||||
_ = @import("os/index.zig");
|
||||
_ = @import("rand/index.zig");
|
||||
_ = @import("sort.zig");
|
||||
|
@ -486,6 +486,11 @@ pub fn readLine(buf: []u8) !usize {
|
||||
while (true) {
|
||||
const byte = stream.readByte() catch return error.EndOfFile;
|
||||
switch (byte) {
|
||||
'\r' => {
|
||||
// trash the following \n
|
||||
_ = stream.readByte() catch return error.EndOfFile;
|
||||
return index;
|
||||
},
|
||||
'\n' => return index,
|
||||
else => {
|
||||
if (index == buf.len) return error.InputTooLong;
|
||||
|
@ -161,6 +161,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
|
||||
}
|
||||
|
||||
list.len -= 1;
|
||||
assert(list.len == 0 or (list.first != null and list.last != null));
|
||||
}
|
||||
|
||||
/// Remove and return the last node in the list.
|
||||
|
26
std/mem.zig
26
std/mem.zig
@ -3,6 +3,7 @@ const debug = std.debug;
|
||||
const assert = debug.assert;
|
||||
const math = std.math;
|
||||
const builtin = @import("builtin");
|
||||
const mem = this;
|
||||
|
||||
pub const Allocator = struct {
|
||||
const Error = error {OutOfMemory};
|
||||
@ -550,3 +551,28 @@ test "std.mem.rotate" {
|
||||
|
||||
assert(eql(i32, arr, []i32{ 1, 2, 4, 5, 3 }));
|
||||
}
|
||||
|
||||
// TODO: When https://github.com/zig-lang/zig/issues/649 is solved these can be done by
|
||||
// endian-casting the pointer and then dereferencing
|
||||
|
||||
pub fn endianSwapIfLe(comptime T: type, x: T) T {
|
||||
return endianSwapIf(builtin.Endian.Little, T, x);
|
||||
}
|
||||
|
||||
pub fn endianSwapIfBe(comptime T: type, x: T) T {
|
||||
return endianSwapIf(builtin.Endian.Big, T, x);
|
||||
}
|
||||
|
||||
pub fn endianSwapIf(endian: builtin.Endian, comptime T: type, x: T) T {
|
||||
return if (builtin.endian == endian) endianSwap(T, x) else x;
|
||||
}
|
||||
|
||||
pub fn endianSwap(comptime T: type, x: T) T {
|
||||
var buf: [@sizeOf(T)]u8 = undefined;
|
||||
mem.writeInt(buf[0..], x, builtin.Endian.Little);
|
||||
return mem.readInt(buf, T, builtin.Endian.Big);
|
||||
}
|
||||
|
||||
test "std.mem.endianSwap" {
|
||||
assert(endianSwap(u32, 0xDEADBEEF) == 0xEFBEADDE);
|
||||
}
|
||||
|
295
std/net.zig
295
std/net.zig
@ -1,143 +1,120 @@
|
||||
const std = @import("index.zig");
|
||||
const linux = std.os.linux;
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const endian = std.endian;
|
||||
const net = this;
|
||||
const posix = std.os.posix;
|
||||
const mem = std.mem;
|
||||
|
||||
// TODO don't trust this file, it bit rotted. start over
|
||||
pub const TmpWinAddr = struct {
|
||||
family: u8,
|
||||
data: [14]u8,
|
||||
};
|
||||
|
||||
const Connection = struct {
|
||||
socket_fd: i32,
|
||||
pub const OsAddress = switch (builtin.os) {
|
||||
builtin.Os.windows => TmpWinAddr,
|
||||
else => posix.sockaddr,
|
||||
};
|
||||
|
||||
pub fn send(c: Connection, buf: []const u8) !usize {
|
||||
const send_ret = linux.sendto(c.socket_fd, buf.ptr, buf.len, 0, null, 0);
|
||||
const send_err = linux.getErrno(send_ret);
|
||||
switch (send_err) {
|
||||
0 => return send_ret,
|
||||
linux.EINVAL => unreachable,
|
||||
linux.EFAULT => unreachable,
|
||||
linux.ECONNRESET => return error.ConnectionReset,
|
||||
linux.EINTR => return error.SigInterrupt,
|
||||
// TODO there are more possible errors
|
||||
else => return error.Unexpected,
|
||||
}
|
||||
pub const Address = struct {
|
||||
os_addr: OsAddress,
|
||||
|
||||
pub fn initIp4(ip4: u32, port: u16) Address {
|
||||
return Address {
|
||||
.os_addr = posix.sockaddr {
|
||||
.in = posix.sockaddr_in {
|
||||
.family = posix.AF_INET,
|
||||
.port = std.mem.endianSwapIfLe(u16, port),
|
||||
.addr = ip4,
|
||||
.zero = []u8{0} ** 8,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn recv(c: Connection, buf: []u8) ![]u8 {
|
||||
const recv_ret = linux.recvfrom(c.socket_fd, buf.ptr, buf.len, 0, null, null);
|
||||
const recv_err = linux.getErrno(recv_ret);
|
||||
switch (recv_err) {
|
||||
0 => return buf[0..recv_ret],
|
||||
linux.EINVAL => unreachable,
|
||||
linux.EFAULT => unreachable,
|
||||
linux.ENOTSOCK => return error.NotSocket,
|
||||
linux.EINTR => return error.SigInterrupt,
|
||||
linux.ENOMEM => return error.OutOfMemory,
|
||||
linux.ECONNREFUSED => return error.ConnectionRefused,
|
||||
linux.EBADF => return error.BadFd,
|
||||
// TODO more error values
|
||||
else => return error.Unexpected,
|
||||
}
|
||||
pub fn initIp6(ip6: &const Ip6Addr, port: u16) Address {
|
||||
return Address {
|
||||
.family = posix.AF_INET6,
|
||||
.os_addr = posix.sockaddr {
|
||||
.in6 = posix.sockaddr_in6 {
|
||||
.family = posix.AF_INET6,
|
||||
.port = std.mem.endianSwapIfLe(u16, port),
|
||||
.flowinfo = 0,
|
||||
.addr = ip6.addr,
|
||||
.scope_id = ip6.scope_id,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn close(c: Connection) !void {
|
||||
switch (linux.getErrno(linux.close(c.socket_fd))) {
|
||||
0 => return,
|
||||
linux.EBADF => unreachable,
|
||||
linux.EINTR => return error.SigInterrupt,
|
||||
linux.EIO => return error.Io,
|
||||
else => return error.Unexpected,
|
||||
pub fn initPosix(addr: &const posix.sockaddr) Address {
|
||||
return Address {
|
||||
.os_addr = *addr,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn format(self: &const Address, out_stream: var) !void {
|
||||
switch (self.os_addr.in.family) {
|
||||
posix.AF_INET => {
|
||||
const native_endian_port = std.mem.endianSwapIfLe(u16, self.os_addr.in.port);
|
||||
const bytes = ([]const u8)((&self.os_addr.in.addr)[0..1]);
|
||||
try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port);
|
||||
},
|
||||
posix.AF_INET6 => {
|
||||
const native_endian_port = std.mem.endianSwapIfLe(u16, self.os_addr.in6.port);
|
||||
try out_stream.print("[TODO render ip6 address]:{}", native_endian_port);
|
||||
},
|
||||
else => try out_stream.write("(unrecognized address family)"),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Address = struct {
|
||||
family: u16,
|
||||
pub fn parseIp4(buf: []const u8) !u32 {
|
||||
var result: u32 = undefined;
|
||||
const out_ptr = ([]u8)((&result)[0..1]);
|
||||
|
||||
var x: u8 = 0;
|
||||
var index: u8 = 0;
|
||||
var saw_any_digits = false;
|
||||
for (buf) |c| {
|
||||
if (c == '.') {
|
||||
if (!saw_any_digits) {
|
||||
return error.InvalidCharacter;
|
||||
}
|
||||
if (index == 3) {
|
||||
return error.InvalidEnd;
|
||||
}
|
||||
out_ptr[index] = x;
|
||||
index += 1;
|
||||
x = 0;
|
||||
saw_any_digits = false;
|
||||
} else if (c >= '0' and c <= '9') {
|
||||
saw_any_digits = true;
|
||||
const digit = c - '0';
|
||||
if (@mulWithOverflow(u8, x, 10, &x)) {
|
||||
return error.Overflow;
|
||||
}
|
||||
if (@addWithOverflow(u8, x, digit, &x)) {
|
||||
return error.Overflow;
|
||||
}
|
||||
} else {
|
||||
return error.InvalidCharacter;
|
||||
}
|
||||
}
|
||||
if (index == 3 and saw_any_digits) {
|
||||
out_ptr[index] = x;
|
||||
return result;
|
||||
}
|
||||
|
||||
return error.Incomplete;
|
||||
}
|
||||
|
||||
pub const Ip6Addr = struct {
|
||||
scope_id: u32,
|
||||
addr: [16]u8,
|
||||
sort_key: i32,
|
||||
};
|
||||
|
||||
pub fn lookup(hostname: []const u8, out_addrs: []Address) ![]Address {
|
||||
if (hostname.len == 0) {
|
||||
|
||||
unreachable; // TODO
|
||||
}
|
||||
|
||||
unreachable; // TODO
|
||||
}
|
||||
|
||||
pub fn connectAddr(addr: &Address, port: u16) !Connection {
|
||||
const socket_ret = linux.socket(addr.family, linux.SOCK_STREAM, linux.PROTO_tcp);
|
||||
const socket_err = linux.getErrno(socket_ret);
|
||||
if (socket_err > 0) {
|
||||
// TODO figure out possible errors from socket()
|
||||
return error.Unexpected;
|
||||
}
|
||||
const socket_fd = i32(socket_ret);
|
||||
|
||||
const connect_ret = if (addr.family == linux.AF_INET) x: {
|
||||
var os_addr: linux.sockaddr_in = undefined;
|
||||
os_addr.family = addr.family;
|
||||
os_addr.port = endian.swapIfLe(u16, port);
|
||||
@memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4);
|
||||
@memset(&os_addr.zero[0], 0, @sizeOf(@typeOf(os_addr.zero)));
|
||||
break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in));
|
||||
} else if (addr.family == linux.AF_INET6) x: {
|
||||
var os_addr: linux.sockaddr_in6 = undefined;
|
||||
os_addr.family = addr.family;
|
||||
os_addr.port = endian.swapIfLe(u16, port);
|
||||
os_addr.flowinfo = 0;
|
||||
os_addr.scope_id = addr.scope_id;
|
||||
@memcpy(&os_addr.addr[0], &addr.addr[0], 16);
|
||||
break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in6));
|
||||
} else {
|
||||
unreachable;
|
||||
};
|
||||
const connect_err = linux.getErrno(connect_ret);
|
||||
if (connect_err > 0) {
|
||||
switch (connect_err) {
|
||||
linux.ETIMEDOUT => return error.TimedOut,
|
||||
else => {
|
||||
// TODO figure out possible errors from connect()
|
||||
return error.Unexpected;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return Connection {
|
||||
.socket_fd = socket_fd,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn connect(hostname: []const u8, port: u16) !Connection {
|
||||
var addrs_buf: [1]Address = undefined;
|
||||
const addrs_slice = try lookup(hostname, addrs_buf[0..]);
|
||||
const main_addr = &addrs_slice[0];
|
||||
|
||||
return connectAddr(main_addr, port);
|
||||
}
|
||||
|
||||
pub fn parseIpLiteral(buf: []const u8) !Address {
|
||||
|
||||
return error.InvalidIpLiteral;
|
||||
}
|
||||
|
||||
fn hexDigit(c: u8) u8 {
|
||||
// TODO use switch with range
|
||||
if ('0' <= c and c <= '9') {
|
||||
return c - '0';
|
||||
} else if ('A' <= c and c <= 'Z') {
|
||||
return c - 'A' + 10;
|
||||
} else if ('a' <= c and c <= 'z') {
|
||||
return c - 'a' + 10;
|
||||
} else {
|
||||
return @maxValue(u8);
|
||||
}
|
||||
}
|
||||
|
||||
fn parseIp6(buf: []const u8) !Address {
|
||||
var result: Address = undefined;
|
||||
result.family = linux.AF_INET6;
|
||||
pub fn parseIp6(buf: []const u8) !Ip6Addr {
|
||||
var result: Ip6Addr = undefined;
|
||||
result.scope_id = 0;
|
||||
const ip_slice = result.addr[0..];
|
||||
|
||||
@ -156,14 +133,14 @@ fn parseIp6(buf: []const u8) !Address {
|
||||
return error.Overflow;
|
||||
}
|
||||
} else {
|
||||
return error.InvalidChar;
|
||||
return error.InvalidCharacter;
|
||||
}
|
||||
} else if (c == ':') {
|
||||
if (!saw_any_digits) {
|
||||
return error.InvalidChar;
|
||||
return error.InvalidCharacter;
|
||||
}
|
||||
if (index == 14) {
|
||||
return error.JunkAtEnd;
|
||||
return error.InvalidEnd;
|
||||
}
|
||||
ip_slice[index] = @truncate(u8, x >> 8);
|
||||
index += 1;
|
||||
@ -174,7 +151,7 @@ fn parseIp6(buf: []const u8) !Address {
|
||||
saw_any_digits = false;
|
||||
} else if (c == '%') {
|
||||
if (!saw_any_digits) {
|
||||
return error.InvalidChar;
|
||||
return error.InvalidCharacter;
|
||||
}
|
||||
if (index == 14) {
|
||||
ip_slice[index] = @truncate(u8, x >> 8);
|
||||
@ -185,10 +162,7 @@ fn parseIp6(buf: []const u8) !Address {
|
||||
scope_id = true;
|
||||
saw_any_digits = false;
|
||||
} else {
|
||||
const digit = hexDigit(c);
|
||||
if (digit == @maxValue(u8)) {
|
||||
return error.InvalidChar;
|
||||
}
|
||||
const digit = try std.fmt.charToDigit(c, 16);
|
||||
if (@mulWithOverflow(u16, x, 16, &x)) {
|
||||
return error.Overflow;
|
||||
}
|
||||
@ -216,42 +190,27 @@ fn parseIp6(buf: []const u8) !Address {
|
||||
return error.Incomplete;
|
||||
}
|
||||
|
||||
fn parseIp4(buf: []const u8) !u32 {
|
||||
var result: u32 = undefined;
|
||||
const out_ptr = ([]u8)((&result)[0..1]);
|
||||
test "std.net.parseIp4" {
|
||||
assert((try parseIp4("127.0.0.1")) == std.mem.endianSwapIfLe(u32, 0x7f000001));
|
||||
|
||||
var x: u8 = 0;
|
||||
var index: u8 = 0;
|
||||
var saw_any_digits = false;
|
||||
for (buf) |c| {
|
||||
if (c == '.') {
|
||||
if (!saw_any_digits) {
|
||||
return error.InvalidChar;
|
||||
}
|
||||
if (index == 3) {
|
||||
return error.JunkAtEnd;
|
||||
}
|
||||
out_ptr[index] = x;
|
||||
index += 1;
|
||||
x = 0;
|
||||
saw_any_digits = false;
|
||||
} else if (c >= '0' and c <= '9') {
|
||||
saw_any_digits = true;
|
||||
const digit = c - '0';
|
||||
if (@mulWithOverflow(u8, x, 10, &x)) {
|
||||
return error.Overflow;
|
||||
}
|
||||
if (@addWithOverflow(u8, x, digit, &x)) {
|
||||
return error.Overflow;
|
||||
}
|
||||
} else {
|
||||
return error.InvalidChar;
|
||||
}
|
||||
}
|
||||
if (index == 3 and saw_any_digits) {
|
||||
out_ptr[index] = x;
|
||||
return result;
|
||||
}
|
||||
|
||||
return error.Incomplete;
|
||||
testParseIp4Fail("256.0.0.1", error.Overflow);
|
||||
testParseIp4Fail("x.0.0.1", error.InvalidCharacter);
|
||||
testParseIp4Fail("127.0.0.1.1", error.InvalidEnd);
|
||||
testParseIp4Fail("127.0.0.", error.Incomplete);
|
||||
testParseIp4Fail("100..0.1", error.InvalidCharacter);
|
||||
}
|
||||
|
||||
fn testParseIp4Fail(buf: []const u8, expected_err: error) void {
|
||||
if (parseIp4(buf)) |_| {
|
||||
@panic("expected error");
|
||||
} else |e| {
|
||||
assert(e == expected_err);
|
||||
}
|
||||
}
|
||||
|
||||
test "std.net.parseIp6" {
|
||||
const addr = try parseIp6("FF01:0:0:0:0:0:0:FB");
|
||||
assert(addr.addr[0] == 0xff);
|
||||
assert(addr.addr[1] == 0x01);
|
||||
assert(addr.addr[2] == 0x00);
|
||||
}
|
||||
|
@ -301,6 +301,9 @@ pub const timespec = c.timespec;
|
||||
pub const Stat = c.Stat;
|
||||
pub const dirent = c.dirent;
|
||||
|
||||
pub const sa_family_t = c.sa_family_t;
|
||||
pub const sockaddr = c.sockaddr;
|
||||
|
||||
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
|
||||
pub const Sigaction = struct {
|
||||
handler: extern fn(i32)void,
|
||||
|
528
std/os/index.zig
528
std/os/index.zig
@ -4,6 +4,19 @@ const Os = builtin.Os;
|
||||
const is_windows = builtin.os == Os.windows;
|
||||
const os = this;
|
||||
|
||||
test "std.os" {
|
||||
_ = @import("child_process.zig");
|
||||
_ = @import("darwin.zig");
|
||||
_ = @import("darwin_errno.zig");
|
||||
_ = @import("get_user_id.zig");
|
||||
_ = @import("linux/errno.zig");
|
||||
_ = @import("linux/index.zig");
|
||||
_ = @import("linux/x86_64.zig");
|
||||
_ = @import("path.zig");
|
||||
_ = @import("test.zig");
|
||||
_ = @import("windows/index.zig");
|
||||
}
|
||||
|
||||
pub const windows = @import("windows/index.zig");
|
||||
pub const darwin = @import("darwin.zig");
|
||||
pub const linux = @import("linux/index.zig");
|
||||
@ -14,6 +27,7 @@ pub const posix = switch(builtin.os) {
|
||||
Os.zen => zen,
|
||||
else => @compileError("Unsupported OS"),
|
||||
};
|
||||
pub const net = @import("net.zig");
|
||||
|
||||
pub const ChildProcess = @import("child_process.zig").ChildProcess;
|
||||
pub const path = @import("path.zig");
|
||||
@ -173,6 +187,13 @@ pub fn exit(status: u8) noreturn {
|
||||
}
|
||||
}
|
||||
|
||||
/// When a file descriptor is closed on linux, it pops the first
|
||||
/// node from this queue and resumes it.
|
||||
/// Async functions which get the EMFILE error code can suspend,
|
||||
/// putting their coroutine handle into this list.
|
||||
/// TODO make this an atomic linked list
|
||||
pub var emfile_promise_queue = std.LinkedList(promise).init();
|
||||
|
||||
/// Closes the file handle. Keeps trying if it gets interrupted by a signal.
|
||||
pub fn close(handle: FileHandle) void {
|
||||
if (is_windows) {
|
||||
@ -180,10 +201,12 @@ pub fn close(handle: FileHandle) void {
|
||||
} else {
|
||||
while (true) {
|
||||
const err = posix.getErrno(posix.close(handle));
|
||||
if (err == posix.EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
return;
|
||||
switch (err) {
|
||||
posix.EINTR => continue,
|
||||
else => {
|
||||
if (emfile_promise_queue.popFirst()) |p| resume p.data;
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1753,27 +1776,16 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const
|
||||
assert(it.next(debug.global_allocator) == null);
|
||||
}
|
||||
|
||||
test "std.os" {
|
||||
_ = @import("child_process.zig");
|
||||
_ = @import("darwin_errno.zig");
|
||||
_ = @import("darwin.zig");
|
||||
_ = @import("get_user_id.zig");
|
||||
_ = @import("linux/errno.zig");
|
||||
//_ = @import("linux_i386.zig");
|
||||
_ = @import("linux/x86_64.zig");
|
||||
_ = @import("linux/index.zig");
|
||||
_ = @import("path.zig");
|
||||
_ = @import("windows/index.zig");
|
||||
_ = @import("test.zig");
|
||||
}
|
||||
|
||||
|
||||
// TODO make this a build variable that you can set
|
||||
const unexpected_error_tracing = false;
|
||||
const UnexpectedError = error {
|
||||
/// The Operating System returned an undocumented error code.
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
/// Call this when you made a syscall or something that sets errno
|
||||
/// and you get an unexpected error.
|
||||
pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) {
|
||||
pub fn unexpectedErrorPosix(errno: usize) UnexpectedError {
|
||||
if (unexpected_error_tracing) {
|
||||
debug.warn("unexpected errno: {}\n", errno);
|
||||
debug.dumpCurrentStackTrace(null);
|
||||
@ -1783,7 +1795,7 @@ pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) {
|
||||
|
||||
/// Call this when you made a windows DLL call or something that does SetLastError
|
||||
/// and you get an unexpected error.
|
||||
pub fn unexpectedErrorWindows(err: windows.DWORD) (error{Unexpected}) {
|
||||
pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
|
||||
if (unexpected_error_tracing) {
|
||||
debug.warn("unexpected GetLastError(): {}\n", err);
|
||||
debug.dumpCurrentStackTrace(null);
|
||||
@ -1898,3 +1910,477 @@ pub fn isTty(handle: FileHandle) bool {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const PosixSocketError = error {
|
||||
/// Permission to create a socket of the specified type and/or
|
||||
/// pro‐tocol is denied.
|
||||
PermissionDenied,
|
||||
|
||||
/// The implementation does not support the specified address family.
|
||||
AddressFamilyNotSupported,
|
||||
|
||||
/// Unknown protocol, or protocol family not available.
|
||||
ProtocolFamilyNotAvailable,
|
||||
|
||||
/// The per-process limit on the number of open file descriptors has been reached.
|
||||
ProcessFdQuotaExceeded,
|
||||
|
||||
/// The system-wide limit on the total number of open files has been reached.
|
||||
SystemFdQuotaExceeded,
|
||||
|
||||
/// Insufficient memory is available. The socket cannot be created until sufficient
|
||||
/// resources are freed.
|
||||
SystemResources,
|
||||
|
||||
/// The protocol type or the specified protocol is not supported within this domain.
|
||||
ProtocolNotSupported,
|
||||
};
|
||||
|
||||
pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 {
|
||||
const rc = posix.socket(domain, socket_type, protocol);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return i32(rc),
|
||||
posix.EACCES => return PosixSocketError.PermissionDenied,
|
||||
posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported,
|
||||
posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable,
|
||||
posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded,
|
||||
posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded,
|
||||
posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources,
|
||||
posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub const PosixBindError = error {
|
||||
/// The address is protected, and the user is not the superuser.
|
||||
/// For UNIX domain sockets: Search permission is denied on a component
|
||||
/// of the path prefix.
|
||||
AccessDenied,
|
||||
|
||||
/// The given address is already in use, or in the case of Internet domain sockets,
|
||||
/// The port number was specified as zero in the socket
|
||||
/// address structure, but, upon attempting to bind to an ephemeral port, it was
|
||||
/// determined that all port numbers in the ephemeral port range are currently in
|
||||
/// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7).
|
||||
AddressInUse,
|
||||
|
||||
/// sockfd is not a valid file descriptor.
|
||||
InvalidFileDescriptor,
|
||||
|
||||
/// The socket is already bound to an address, or addrlen is wrong, or addr is not
|
||||
/// a valid address for this socket's domain.
|
||||
InvalidSocketOrAddress,
|
||||
|
||||
/// The file descriptor sockfd does not refer to a socket.
|
||||
FileDescriptorNotASocket,
|
||||
|
||||
/// A nonexistent interface was requested or the requested address was not local.
|
||||
AddressNotAvailable,
|
||||
|
||||
/// addr points outside the user's accessible address space.
|
||||
PageFault,
|
||||
|
||||
/// Too many symbolic links were encountered in resolving addr.
|
||||
SymLinkLoop,
|
||||
|
||||
/// addr is too long.
|
||||
NameTooLong,
|
||||
|
||||
/// A component in the directory prefix of the socket pathname does not exist.
|
||||
FileNotFound,
|
||||
|
||||
/// Insufficient kernel memory was available.
|
||||
SystemResources,
|
||||
|
||||
/// A component of the path prefix is not a directory.
|
||||
NotDir,
|
||||
|
||||
/// The socket inode would reside on a read-only filesystem.
|
||||
ReadOnlyFileSystem,
|
||||
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
/// addr is `&const T` where T is one of the sockaddr
|
||||
pub fn posixBind(fd: i32, addr: &const posix.sockaddr) PosixBindError!void {
|
||||
const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr));
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return,
|
||||
posix.EACCES => return PosixBindError.AccessDenied,
|
||||
posix.EADDRINUSE => return PosixBindError.AddressInUse,
|
||||
posix.EBADF => return PosixBindError.InvalidFileDescriptor,
|
||||
posix.EINVAL => return PosixBindError.InvalidSocketOrAddress,
|
||||
posix.ENOTSOCK => return PosixBindError.FileDescriptorNotASocket,
|
||||
posix.EADDRNOTAVAIL => return PosixBindError.AddressNotAvailable,
|
||||
posix.EFAULT => return PosixBindError.PageFault,
|
||||
posix.ELOOP => return PosixBindError.SymLinkLoop,
|
||||
posix.ENAMETOOLONG => return PosixBindError.NameTooLong,
|
||||
posix.ENOENT => return PosixBindError.FileNotFound,
|
||||
posix.ENOMEM => return PosixBindError.SystemResources,
|
||||
posix.ENOTDIR => return PosixBindError.NotDir,
|
||||
posix.EROFS => return PosixBindError.ReadOnlyFileSystem,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
|
||||
const PosixListenError = error {
|
||||
/// Another socket is already listening on the same port.
|
||||
/// For Internet domain sockets, the socket referred to by sockfd had not previously
|
||||
/// been bound to an address and, upon attempting to bind it to an ephemeral port, it
|
||||
/// was determined that all port numbers in the ephemeral port range are currently in
|
||||
/// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
|
||||
AddressInUse,
|
||||
|
||||
/// The argument sockfd is not a valid file descriptor.
|
||||
InvalidFileDescriptor,
|
||||
|
||||
/// The file descriptor sockfd does not refer to a socket.
|
||||
FileDescriptorNotASocket,
|
||||
|
||||
/// The socket is not of a type that supports the listen() operation.
|
||||
OperationNotSupported,
|
||||
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void {
|
||||
const rc = posix.listen(sockfd, backlog);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return,
|
||||
posix.EADDRINUSE => return PosixListenError.AddressInUse,
|
||||
posix.EBADF => return PosixListenError.InvalidFileDescriptor,
|
||||
posix.ENOTSOCK => return PosixListenError.FileDescriptorNotASocket,
|
||||
posix.EOPNOTSUPP => return PosixListenError.OperationNotSupported,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub const PosixAcceptError = error {
|
||||
/// The socket is marked nonblocking and no connections are present to be accepted.
|
||||
WouldBlock,
|
||||
|
||||
/// sockfd is not an open file descriptor.
|
||||
FileDescriptorClosed,
|
||||
|
||||
ConnectionAborted,
|
||||
|
||||
/// The addr argument is not in a writable part of the user address space.
|
||||
PageFault,
|
||||
|
||||
/// Socket is not listening for connections, or addrlen is invalid (e.g., is negative),
|
||||
/// or invalid value in flags.
|
||||
InvalidSyscall,
|
||||
|
||||
/// The per-process limit on the number of open file descriptors has been reached.
|
||||
ProcessFdQuotaExceeded,
|
||||
|
||||
/// The system-wide limit on the total number of open files has been reached.
|
||||
SystemFdQuotaExceeded,
|
||||
|
||||
/// Not enough free memory. This often means that the memory allocation is limited
|
||||
/// by the socket buffer limits, not by the system memory.
|
||||
SystemResources,
|
||||
|
||||
/// The file descriptor sockfd does not refer to a socket.
|
||||
FileDescriptorNotASocket,
|
||||
|
||||
/// The referenced socket is not of type SOCK_STREAM.
|
||||
OperationNotSupported,
|
||||
|
||||
ProtocolFailure,
|
||||
|
||||
/// Firewall rules forbid connection.
|
||||
BlockedByFirewall,
|
||||
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError!i32 {
|
||||
while (true) {
|
||||
var sockaddr_size = u32(@sizeOf(posix.sockaddr));
|
||||
const rc = posix.accept4(fd, addr, &sockaddr_size, flags);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return i32(rc),
|
||||
posix.EINTR => continue,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
|
||||
posix.EAGAIN => return PosixAcceptError.WouldBlock,
|
||||
posix.EBADF => return PosixAcceptError.FileDescriptorClosed,
|
||||
posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted,
|
||||
posix.EFAULT => return PosixAcceptError.PageFault,
|
||||
posix.EINVAL => return PosixAcceptError.InvalidSyscall,
|
||||
posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded,
|
||||
posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded,
|
||||
posix.ENOBUFS, posix.ENOMEM => return PosixAcceptError.SystemResources,
|
||||
posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket,
|
||||
posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported,
|
||||
posix.EPROTO => return PosixAcceptError.ProtocolFailure,
|
||||
posix.EPERM => return PosixAcceptError.BlockedByFirewall,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const LinuxEpollCreateError = error {
|
||||
/// Invalid value specified in flags.
|
||||
InvalidSyscall,
|
||||
|
||||
/// The per-user limit on the number of epoll instances imposed by
|
||||
/// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further
|
||||
/// details.
|
||||
/// Or, The per-process limit on the number of open file descriptors has been reached.
|
||||
ProcessFdQuotaExceeded,
|
||||
|
||||
/// The system-wide limit on the total number of open files has been reached.
|
||||
SystemFdQuotaExceeded,
|
||||
|
||||
/// There was insufficient memory to create the kernel object.
|
||||
SystemResources,
|
||||
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 {
|
||||
const rc = posix.epoll_create1(flags);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return i32(rc),
|
||||
else => return unexpectedErrorPosix(err),
|
||||
|
||||
posix.EINVAL => return LinuxEpollCreateError.InvalidSyscall,
|
||||
posix.EMFILE => return LinuxEpollCreateError.ProcessFdQuotaExceeded,
|
||||
posix.ENFILE => return LinuxEpollCreateError.SystemFdQuotaExceeded,
|
||||
posix.ENOMEM => return LinuxEpollCreateError.SystemResources,
|
||||
}
|
||||
}
|
||||
|
||||
pub const LinuxEpollCtlError = error {
|
||||
/// epfd or fd is not a valid file descriptor.
|
||||
InvalidFileDescriptor,
|
||||
|
||||
/// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered
|
||||
/// with this epoll instance.
|
||||
FileDescriptorAlreadyPresentInSet,
|
||||
|
||||
/// epfd is not an epoll file descriptor, or fd is the same as epfd, or the requested
|
||||
/// operation op is not supported by this interface, or
|
||||
/// An invalid event type was specified along with EPOLLEXCLUSIVE in events, or
|
||||
/// op was EPOLL_CTL_MOD and events included EPOLLEXCLUSIVE, or
|
||||
/// op was EPOLL_CTL_MOD and the EPOLLEXCLUSIVE flag has previously been applied to
|
||||
/// this epfd, fd pair, or
|
||||
/// EPOLLEXCLUSIVE was specified in event and fd refers to an epoll instance.
|
||||
InvalidSyscall,
|
||||
|
||||
/// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a
|
||||
/// circular loop of epoll instances monitoring one another.
|
||||
OperationCausesCircularLoop,
|
||||
|
||||
/// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll
|
||||
/// instance.
|
||||
FileDescriptorNotRegistered,
|
||||
|
||||
/// There was insufficient memory to handle the requested op control operation.
|
||||
SystemResources,
|
||||
|
||||
/// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while
|
||||
/// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance.
|
||||
/// See epoll(7) for further details.
|
||||
UserResourceLimitReached,
|
||||
|
||||
/// The target file fd does not support epoll. This error can occur if fd refers to,
|
||||
/// for example, a regular file or a directory.
|
||||
FileDescriptorIncompatibleWithEpoll,
|
||||
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: &linux.epoll_event) LinuxEpollCtlError!void {
|
||||
const rc = posix.epoll_ctl(epfd, op, fd, event);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
|
||||
posix.EBADF => return LinuxEpollCtlError.InvalidFileDescriptor,
|
||||
posix.EEXIST => return LinuxEpollCtlError.FileDescriptorAlreadyPresentInSet,
|
||||
posix.EINVAL => return LinuxEpollCtlError.InvalidSyscall,
|
||||
posix.ELOOP => return LinuxEpollCtlError.OperationCausesCircularLoop,
|
||||
posix.ENOENT => return LinuxEpollCtlError.FileDescriptorNotRegistered,
|
||||
posix.ENOMEM => return LinuxEpollCtlError.SystemResources,
|
||||
posix.ENOSPC => return LinuxEpollCtlError.UserResourceLimitReached,
|
||||
posix.EPERM => return LinuxEpollCtlError.FileDescriptorIncompatibleWithEpoll,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize {
|
||||
while (true) {
|
||||
const rc = posix.epoll_wait(epfd, events.ptr, u32(events.len), timeout);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return rc,
|
||||
posix.EINTR => continue,
|
||||
posix.EBADF => unreachable,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINVAL => unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const PosixGetSockNameError = error {
|
||||
/// Insufficient resources were available in the system to perform the operation.
|
||||
SystemResources,
|
||||
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr {
|
||||
var addr: posix.sockaddr = undefined;
|
||||
var addrlen: posix.socklen_t = @sizeOf(posix.sockaddr);
|
||||
const rc = posix.getsockname(sockfd, &addr, &addrlen);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return addr,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
|
||||
posix.EBADF => unreachable,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.ENOTSOCK => unreachable,
|
||||
posix.ENOBUFS => return PosixGetSockNameError.SystemResources,
|
||||
}
|
||||
}
|
||||
|
||||
pub const PosixConnectError = error {
|
||||
/// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket
|
||||
/// file, or search permission is denied for one of the directories in the path prefix.
|
||||
/// or
|
||||
/// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or
|
||||
/// the connection request failed because of a local firewall rule.
|
||||
PermissionDenied,
|
||||
|
||||
/// Local address is already in use.
|
||||
AddressInUse,
|
||||
|
||||
/// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an
|
||||
/// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers
|
||||
/// in the ephemeral port range are currently in use. See the discussion of
|
||||
/// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
|
||||
AddressNotAvailable,
|
||||
|
||||
/// The passed address didn't have the correct address family in its sa_family field.
|
||||
AddressFamilyNotSupported,
|
||||
|
||||
/// Insufficient entries in the routing cache.
|
||||
SystemResources,
|
||||
|
||||
/// A connect() on a stream socket found no one listening on the remote address.
|
||||
ConnectionRefused,
|
||||
|
||||
/// Network is unreachable.
|
||||
NetworkUnreachable,
|
||||
|
||||
/// Timeout while attempting connection. The server may be too busy to accept new connections. Note
|
||||
/// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
|
||||
ConnectionTimedOut,
|
||||
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn posixConnect(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void {
|
||||
while (true) {
|
||||
const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
|
||||
posix.EACCES => return PosixConnectError.PermissionDenied,
|
||||
posix.EPERM => return PosixConnectError.PermissionDenied,
|
||||
posix.EADDRINUSE => return PosixConnectError.AddressInUse,
|
||||
posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
|
||||
posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
|
||||
posix.EAGAIN => return PosixConnectError.SystemResources,
|
||||
posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
|
||||
posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
|
||||
posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
|
||||
posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
|
||||
posix.EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately.
|
||||
posix.EINTR => continue,
|
||||
posix.EISCONN => unreachable, // The socket is already connected.
|
||||
posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
|
||||
posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
|
||||
posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
|
||||
posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as posixConnect except it is for blocking socket file descriptors.
|
||||
/// It expects to receive EINPROGRESS.
|
||||
pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void {
|
||||
while (true) {
|
||||
const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0, posix.EINPROGRESS => return,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
|
||||
posix.EACCES => return PosixConnectError.PermissionDenied,
|
||||
posix.EPERM => return PosixConnectError.PermissionDenied,
|
||||
posix.EADDRINUSE => return PosixConnectError.AddressInUse,
|
||||
posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
|
||||
posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
|
||||
posix.EAGAIN => return PosixConnectError.SystemResources,
|
||||
posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
|
||||
posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
|
||||
posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
|
||||
posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
|
||||
posix.EINTR => continue,
|
||||
posix.EISCONN => unreachable, // The socket is already connected.
|
||||
posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
|
||||
posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
|
||||
posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
|
||||
posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void {
|
||||
var err_code: i32 = undefined;
|
||||
var size: u32 = @sizeOf(i32);
|
||||
const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast(&u8, &err_code), &size);
|
||||
assert(size == 4);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => switch (err_code) {
|
||||
0 => return,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
|
||||
posix.EACCES => return PosixConnectError.PermissionDenied,
|
||||
posix.EPERM => return PosixConnectError.PermissionDenied,
|
||||
posix.EADDRINUSE => return PosixConnectError.AddressInUse,
|
||||
posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
|
||||
posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
|
||||
posix.EAGAIN => return PosixConnectError.SystemResources,
|
||||
posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
|
||||
posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
|
||||
posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
|
||||
posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
|
||||
posix.EISCONN => unreachable, // The socket is already connected.
|
||||
posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
|
||||
posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
|
||||
posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
|
||||
posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
|
||||
},
|
||||
else => return unexpectedErrorPosix(err),
|
||||
posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor.
|
||||
posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
|
||||
posix.EINVAL => unreachable,
|
||||
posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated.
|
||||
posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
|
||||
}
|
||||
}
|
||||
|
@ -1,505 +0,0 @@
|
||||
const std = @import("../../index.zig");
|
||||
const linux = std.os.linux;
|
||||
const socklen_t = linux.socklen_t;
|
||||
const iovec = linux.iovec;
|
||||
|
||||
pub const SYS_restart_syscall = 0;
|
||||
pub const SYS_exit = 1;
|
||||
pub const SYS_fork = 2;
|
||||
pub const SYS_read = 3;
|
||||
pub const SYS_write = 4;
|
||||
pub const SYS_open = 5;
|
||||
pub const SYS_close = 6;
|
||||
pub const SYS_waitpid = 7;
|
||||
pub const SYS_creat = 8;
|
||||
pub const SYS_link = 9;
|
||||
pub const SYS_unlink = 10;
|
||||
pub const SYS_execve = 11;
|
||||
pub const SYS_chdir = 12;
|
||||
pub const SYS_time = 13;
|
||||
pub const SYS_mknod = 14;
|
||||
pub const SYS_chmod = 15;
|
||||
pub const SYS_lchown = 16;
|
||||
pub const SYS_break = 17;
|
||||
pub const SYS_oldstat = 18;
|
||||
pub const SYS_lseek = 19;
|
||||
pub const SYS_getpid = 20;
|
||||
pub const SYS_mount = 21;
|
||||
pub const SYS_umount = 22;
|
||||
pub const SYS_setuid = 23;
|
||||
pub const SYS_getuid = 24;
|
||||
pub const SYS_stime = 25;
|
||||
pub const SYS_ptrace = 26;
|
||||
pub const SYS_alarm = 27;
|
||||
pub const SYS_oldfstat = 28;
|
||||
pub const SYS_pause = 29;
|
||||
pub const SYS_utime = 30;
|
||||
pub const SYS_stty = 31;
|
||||
pub const SYS_gtty = 32;
|
||||
pub const SYS_access = 33;
|
||||
pub const SYS_nice = 34;
|
||||
pub const SYS_ftime = 35;
|
||||
pub const SYS_sync = 36;
|
||||
pub const SYS_kill = 37;
|
||||
pub const SYS_rename = 38;
|
||||
pub const SYS_mkdir = 39;
|
||||
pub const SYS_rmdir = 40;
|
||||
pub const SYS_dup = 41;
|
||||
pub const SYS_pipe = 42;
|
||||
pub const SYS_times = 43;
|
||||
pub const SYS_prof = 44;
|
||||
pub const SYS_brk = 45;
|
||||
pub const SYS_setgid = 46;
|
||||
pub const SYS_getgid = 47;
|
||||
pub const SYS_signal = 48;
|
||||
pub const SYS_geteuid = 49;
|
||||
pub const SYS_getegid = 50;
|
||||
pub const SYS_acct = 51;
|
||||
pub const SYS_umount2 = 52;
|
||||
pub const SYS_lock = 53;
|
||||
pub const SYS_ioctl = 54;
|
||||
pub const SYS_fcntl = 55;
|
||||
pub const SYS_mpx = 56;
|
||||
pub const SYS_setpgid = 57;
|
||||
pub const SYS_ulimit = 58;
|
||||
pub const SYS_oldolduname = 59;
|
||||
pub const SYS_umask = 60;
|
||||
pub const SYS_chroot = 61;
|
||||
pub const SYS_ustat = 62;
|
||||
pub const SYS_dup2 = 63;
|
||||
pub const SYS_getppid = 64;
|
||||
pub const SYS_getpgrp = 65;
|
||||
pub const SYS_setsid = 66;
|
||||
pub const SYS_sigaction = 67;
|
||||
pub const SYS_sgetmask = 68;
|
||||
pub const SYS_ssetmask = 69;
|
||||
pub const SYS_setreuid = 70;
|
||||
pub const SYS_setregid = 71;
|
||||
pub const SYS_sigsuspend = 72;
|
||||
pub const SYS_sigpending = 73;
|
||||
pub const SYS_sethostname = 74;
|
||||
pub const SYS_setrlimit = 75;
|
||||
pub const SYS_getrlimit = 76;
|
||||
pub const SYS_getrusage = 77;
|
||||
pub const SYS_gettimeofday = 78;
|
||||
pub const SYS_settimeofday = 79;
|
||||
pub const SYS_getgroups = 80;
|
||||
pub const SYS_setgroups = 81;
|
||||
pub const SYS_select = 82;
|
||||
pub const SYS_symlink = 83;
|
||||
pub const SYS_oldlstat = 84;
|
||||
pub const SYS_readlink = 85;
|
||||
pub const SYS_uselib = 86;
|
||||
pub const SYS_swapon = 87;
|
||||
pub const SYS_reboot = 88;
|
||||
pub const SYS_readdir = 89;
|
||||
pub const SYS_mmap = 90;
|
||||
pub const SYS_munmap = 91;
|
||||
pub const SYS_truncate = 92;
|
||||
pub const SYS_ftruncate = 93;
|
||||
pub const SYS_fchmod = 94;
|
||||
pub const SYS_fchown = 95;
|
||||
pub const SYS_getpriority = 96;
|
||||
pub const SYS_setpriority = 97;
|
||||
pub const SYS_profil = 98;
|
||||
pub const SYS_statfs = 99;
|
||||
pub const SYS_fstatfs = 100;
|
||||
pub const SYS_ioperm = 101;
|
||||
pub const SYS_socketcall = 102;
|
||||
pub const SYS_syslog = 103;
|
||||
pub const SYS_setitimer = 104;
|
||||
pub const SYS_getitimer = 105;
|
||||
pub const SYS_stat = 106;
|
||||
pub const SYS_lstat = 107;
|
||||
pub const SYS_fstat = 108;
|
||||
pub const SYS_olduname = 109;
|
||||
pub const SYS_iopl = 110;
|
||||
pub const SYS_vhangup = 111;
|
||||
pub const SYS_idle = 112;
|
||||
pub const SYS_vm86old = 113;
|
||||
pub const SYS_wait4 = 114;
|
||||
pub const SYS_swapoff = 115;
|
||||
pub const SYS_sysinfo = 116;
|
||||
pub const SYS_ipc = 117;
|
||||
pub const SYS_fsync = 118;
|
||||
pub const SYS_sigreturn = 119;
|
||||
pub const SYS_clone = 120;
|
||||
pub const SYS_setdomainname = 121;
|
||||
pub const SYS_uname = 122;
|
||||
pub const SYS_modify_ldt = 123;
|
||||
pub const SYS_adjtimex = 124;
|
||||
pub const SYS_mprotect = 125;
|
||||
pub const SYS_sigprocmask = 126;
|
||||
pub const SYS_create_module = 127;
|
||||
pub const SYS_init_module = 128;
|
||||
pub const SYS_delete_module = 129;
|
||||
pub const SYS_get_kernel_syms = 130;
|
||||
pub const SYS_quotactl = 131;
|
||||
pub const SYS_getpgid = 132;
|
||||
pub const SYS_fchdir = 133;
|
||||
pub const SYS_bdflush = 134;
|
||||
pub const SYS_sysfs = 135;
|
||||
pub const SYS_personality = 136;
|
||||
pub const SYS_afs_syscall = 137;
|
||||
pub const SYS_setfsuid = 138;
|
||||
pub const SYS_setfsgid = 139;
|
||||
pub const SYS__llseek = 140;
|
||||
pub const SYS_getdents = 141;
|
||||
pub const SYS__newselect = 142;
|
||||
pub const SYS_flock = 143;
|
||||
pub const SYS_msync = 144;
|
||||
pub const SYS_readv = 145;
|
||||
pub const SYS_writev = 146;
|
||||
pub const SYS_getsid = 147;
|
||||
pub const SYS_fdatasync = 148;
|
||||
pub const SYS__sysctl = 149;
|
||||
pub const SYS_mlock = 150;
|
||||
pub const SYS_munlock = 151;
|
||||
pub const SYS_mlockall = 152;
|
||||
pub const SYS_munlockall = 153;
|
||||
pub const SYS_sched_setparam = 154;
|
||||
pub const SYS_sched_getparam = 155;
|
||||
pub const SYS_sched_setscheduler = 156;
|
||||
pub const SYS_sched_getscheduler = 157;
|
||||
pub const SYS_sched_yield = 158;
|
||||
pub const SYS_sched_get_priority_max = 159;
|
||||
pub const SYS_sched_get_priority_min = 160;
|
||||
pub const SYS_sched_rr_get_interval = 161;
|
||||
pub const SYS_nanosleep = 162;
|
||||
pub const SYS_mremap = 163;
|
||||
pub const SYS_setresuid = 164;
|
||||
pub const SYS_getresuid = 165;
|
||||
pub const SYS_vm86 = 166;
|
||||
pub const SYS_query_module = 167;
|
||||
pub const SYS_poll = 168;
|
||||
pub const SYS_nfsservctl = 169;
|
||||
pub const SYS_setresgid = 170;
|
||||
pub const SYS_getresgid = 171;
|
||||
pub const SYS_prctl = 172;
|
||||
pub const SYS_rt_sigreturn = 173;
|
||||
pub const SYS_rt_sigaction = 174;
|
||||
pub const SYS_rt_sigprocmask = 175;
|
||||
pub const SYS_rt_sigpending = 176;
|
||||
pub const SYS_rt_sigtimedwait = 177;
|
||||
pub const SYS_rt_sigqueueinfo = 178;
|
||||
pub const SYS_rt_sigsuspend = 179;
|
||||
pub const SYS_pread64 = 180;
|
||||
pub const SYS_pwrite64 = 181;
|
||||
pub const SYS_chown = 182;
|
||||
pub const SYS_getcwd = 183;
|
||||
pub const SYS_capget = 184;
|
||||
pub const SYS_capset = 185;
|
||||
pub const SYS_sigaltstack = 186;
|
||||
pub const SYS_sendfile = 187;
|
||||
pub const SYS_getpmsg = 188;
|
||||
pub const SYS_putpmsg = 189;
|
||||
pub const SYS_vfork = 190;
|
||||
pub const SYS_ugetrlimit = 191;
|
||||
pub const SYS_mmap2 = 192;
|
||||
pub const SYS_truncate64 = 193;
|
||||
pub const SYS_ftruncate64 = 194;
|
||||
pub const SYS_stat64 = 195;
|
||||
pub const SYS_lstat64 = 196;
|
||||
pub const SYS_fstat64 = 197;
|
||||
pub const SYS_lchown32 = 198;
|
||||
pub const SYS_getuid32 = 199;
|
||||
pub const SYS_getgid32 = 200;
|
||||
pub const SYS_geteuid32 = 201;
|
||||
pub const SYS_getegid32 = 202;
|
||||
pub const SYS_setreuid32 = 203;
|
||||
pub const SYS_setregid32 = 204;
|
||||
pub const SYS_getgroups32 = 205;
|
||||
pub const SYS_setgroups32 = 206;
|
||||
pub const SYS_fchown32 = 207;
|
||||
pub const SYS_setresuid32 = 208;
|
||||
pub const SYS_getresuid32 = 209;
|
||||
pub const SYS_setresgid32 = 210;
|
||||
pub const SYS_getresgid32 = 211;
|
||||
pub const SYS_chown32 = 212;
|
||||
pub const SYS_setuid32 = 213;
|
||||
pub const SYS_setgid32 = 214;
|
||||
pub const SYS_setfsuid32 = 215;
|
||||
pub const SYS_setfsgid32 = 216;
|
||||
pub const SYS_pivot_root = 217;
|
||||
pub const SYS_mincore = 218;
|
||||
pub const SYS_madvise = 219;
|
||||
pub const SYS_madvise1 = 219;
|
||||
pub const SYS_getdents64 = 220;
|
||||
pub const SYS_fcntl64 = 221;
|
||||
pub const SYS_gettid = 224;
|
||||
pub const SYS_readahead = 225;
|
||||
pub const SYS_setxattr = 226;
|
||||
pub const SYS_lsetxattr = 227;
|
||||
pub const SYS_fsetxattr = 228;
|
||||
pub const SYS_getxattr = 229;
|
||||
pub const SYS_lgetxattr = 230;
|
||||
pub const SYS_fgetxattr = 231;
|
||||
pub const SYS_listxattr = 232;
|
||||
pub const SYS_llistxattr = 233;
|
||||
pub const SYS_flistxattr = 234;
|
||||
pub const SYS_removexattr = 235;
|
||||
pub const SYS_lremovexattr = 236;
|
||||
pub const SYS_fremovexattr = 237;
|
||||
pub const SYS_tkill = 238;
|
||||
pub const SYS_sendfile64 = 239;
|
||||
pub const SYS_futex = 240;
|
||||
pub const SYS_sched_setaffinity = 241;
|
||||
pub const SYS_sched_getaffinity = 242;
|
||||
pub const SYS_set_thread_area = 243;
|
||||
pub const SYS_get_thread_area = 244;
|
||||
pub const SYS_io_setup = 245;
|
||||
pub const SYS_io_destroy = 246;
|
||||
pub const SYS_io_getevents = 247;
|
||||
pub const SYS_io_submit = 248;
|
||||
pub const SYS_io_cancel = 249;
|
||||
pub const SYS_fadvise64 = 250;
|
||||
pub const SYS_exit_group = 252;
|
||||
pub const SYS_lookup_dcookie = 253;
|
||||
pub const SYS_epoll_create = 254;
|
||||
pub const SYS_epoll_ctl = 255;
|
||||
pub const SYS_epoll_wait = 256;
|
||||
pub const SYS_remap_file_pages = 257;
|
||||
pub const SYS_set_tid_address = 258;
|
||||
pub const SYS_timer_create = 259;
|
||||
pub const SYS_timer_settime = SYS_timer_create+1;
|
||||
pub const SYS_timer_gettime = SYS_timer_create+2;
|
||||
pub const SYS_timer_getoverrun = SYS_timer_create+3;
|
||||
pub const SYS_timer_delete = SYS_timer_create+4;
|
||||
pub const SYS_clock_settime = SYS_timer_create+5;
|
||||
pub const SYS_clock_gettime = SYS_timer_create+6;
|
||||
pub const SYS_clock_getres = SYS_timer_create+7;
|
||||
pub const SYS_clock_nanosleep = SYS_timer_create+8;
|
||||
pub const SYS_statfs64 = 268;
|
||||
pub const SYS_fstatfs64 = 269;
|
||||
pub const SYS_tgkill = 270;
|
||||
pub const SYS_utimes = 271;
|
||||
pub const SYS_fadvise64_64 = 272;
|
||||
pub const SYS_vserver = 273;
|
||||
pub const SYS_mbind = 274;
|
||||
pub const SYS_get_mempolicy = 275;
|
||||
pub const SYS_set_mempolicy = 276;
|
||||
pub const SYS_mq_open = 277;
|
||||
pub const SYS_mq_unlink = SYS_mq_open+1;
|
||||
pub const SYS_mq_timedsend = SYS_mq_open+2;
|
||||
pub const SYS_mq_timedreceive = SYS_mq_open+3;
|
||||
pub const SYS_mq_notify = SYS_mq_open+4;
|
||||
pub const SYS_mq_getsetattr = SYS_mq_open+5;
|
||||
pub const SYS_kexec_load = 283;
|
||||
pub const SYS_waitid = 284;
|
||||
pub const SYS_add_key = 286;
|
||||
pub const SYS_request_key = 287;
|
||||
pub const SYS_keyctl = 288;
|
||||
pub const SYS_ioprio_set = 289;
|
||||
pub const SYS_ioprio_get = 290;
|
||||
pub const SYS_inotify_init = 291;
|
||||
pub const SYS_inotify_add_watch = 292;
|
||||
pub const SYS_inotify_rm_watch = 293;
|
||||
pub const SYS_migrate_pages = 294;
|
||||
pub const SYS_openat = 295;
|
||||
pub const SYS_mkdirat = 296;
|
||||
pub const SYS_mknodat = 297;
|
||||
pub const SYS_fchownat = 298;
|
||||
pub const SYS_futimesat = 299;
|
||||
pub const SYS_fstatat64 = 300;
|
||||
pub const SYS_unlinkat = 301;
|
||||
pub const SYS_renameat = 302;
|
||||
pub const SYS_linkat = 303;
|
||||
pub const SYS_symlinkat = 304;
|
||||
pub const SYS_readlinkat = 305;
|
||||
pub const SYS_fchmodat = 306;
|
||||
pub const SYS_faccessat = 307;
|
||||
pub const SYS_pselect6 = 308;
|
||||
pub const SYS_ppoll = 309;
|
||||
pub const SYS_unshare = 310;
|
||||
pub const SYS_set_robust_list = 311;
|
||||
pub const SYS_get_robust_list = 312;
|
||||
pub const SYS_splice = 313;
|
||||
pub const SYS_sync_file_range = 314;
|
||||
pub const SYS_tee = 315;
|
||||
pub const SYS_vmsplice = 316;
|
||||
pub const SYS_move_pages = 317;
|
||||
pub const SYS_getcpu = 318;
|
||||
pub const SYS_epoll_pwait = 319;
|
||||
pub const SYS_utimensat = 320;
|
||||
pub const SYS_signalfd = 321;
|
||||
pub const SYS_timerfd_create = 322;
|
||||
pub const SYS_eventfd = 323;
|
||||
pub const SYS_fallocate = 324;
|
||||
pub const SYS_timerfd_settime = 325;
|
||||
pub const SYS_timerfd_gettime = 326;
|
||||
pub const SYS_signalfd4 = 327;
|
||||
pub const SYS_eventfd2 = 328;
|
||||
pub const SYS_epoll_create1 = 329;
|
||||
pub const SYS_dup3 = 330;
|
||||
pub const SYS_pipe2 = 331;
|
||||
pub const SYS_inotify_init1 = 332;
|
||||
pub const SYS_preadv = 333;
|
||||
pub const SYS_pwritev = 334;
|
||||
pub const SYS_rt_tgsigqueueinfo = 335;
|
||||
pub const SYS_perf_event_open = 336;
|
||||
pub const SYS_recvmmsg = 337;
|
||||
pub const SYS_fanotify_init = 338;
|
||||
pub const SYS_fanotify_mark = 339;
|
||||
pub const SYS_prlimit64 = 340;
|
||||
pub const SYS_name_to_handle_at = 341;
|
||||
pub const SYS_open_by_handle_at = 342;
|
||||
pub const SYS_clock_adjtime = 343;
|
||||
pub const SYS_syncfs = 344;
|
||||
pub const SYS_sendmmsg = 345;
|
||||
pub const SYS_setns = 346;
|
||||
pub const SYS_process_vm_readv = 347;
|
||||
pub const SYS_process_vm_writev = 348;
|
||||
pub const SYS_kcmp = 349;
|
||||
pub const SYS_finit_module = 350;
|
||||
pub const SYS_sched_setattr = 351;
|
||||
pub const SYS_sched_getattr = 352;
|
||||
pub const SYS_renameat2 = 353;
|
||||
pub const SYS_seccomp = 354;
|
||||
pub const SYS_getrandom = 355;
|
||||
pub const SYS_memfd_create = 356;
|
||||
pub const SYS_bpf = 357;
|
||||
pub const SYS_execveat = 358;
|
||||
pub const SYS_socket = 359;
|
||||
pub const SYS_socketpair = 360;
|
||||
pub const SYS_bind = 361;
|
||||
pub const SYS_connect = 362;
|
||||
pub const SYS_listen = 363;
|
||||
pub const SYS_accept4 = 364;
|
||||
pub const SYS_getsockopt = 365;
|
||||
pub const SYS_setsockopt = 366;
|
||||
pub const SYS_getsockname = 367;
|
||||
pub const SYS_getpeername = 368;
|
||||
pub const SYS_sendto = 369;
|
||||
pub const SYS_sendmsg = 370;
|
||||
pub const SYS_recvfrom = 371;
|
||||
pub const SYS_recvmsg = 372;
|
||||
pub const SYS_shutdown = 373;
|
||||
pub const SYS_userfaultfd = 374;
|
||||
pub const SYS_membarrier = 375;
|
||||
pub const SYS_mlock2 = 376;
|
||||
|
||||
|
||||
pub const O_CREAT = 0o100;
|
||||
pub const O_EXCL = 0o200;
|
||||
pub const O_NOCTTY = 0o400;
|
||||
pub const O_TRUNC = 0o1000;
|
||||
pub const O_APPEND = 0o2000;
|
||||
pub const O_NONBLOCK = 0o4000;
|
||||
pub const O_DSYNC = 0o10000;
|
||||
pub const O_SYNC = 0o4010000;
|
||||
pub const O_RSYNC = 0o4010000;
|
||||
pub const O_DIRECTORY = 0o200000;
|
||||
pub const O_NOFOLLOW = 0o400000;
|
||||
pub const O_CLOEXEC = 0o2000000;
|
||||
|
||||
pub const O_ASYNC = 0o20000;
|
||||
pub const O_DIRECT = 0o40000;
|
||||
pub const O_LARGEFILE = 0o100000;
|
||||
pub const O_NOATIME = 0o1000000;
|
||||
pub const O_PATH = 0o10000000;
|
||||
pub const O_TMPFILE = 0o20200000;
|
||||
pub const O_NDELAY = O_NONBLOCK;
|
||||
|
||||
pub const F_DUPFD = 0;
|
||||
pub const F_GETFD = 1;
|
||||
pub const F_SETFD = 2;
|
||||
pub const F_GETFL = 3;
|
||||
pub const F_SETFL = 4;
|
||||
|
||||
pub const F_SETOWN = 8;
|
||||
pub const F_GETOWN = 9;
|
||||
pub const F_SETSIG = 10;
|
||||
pub const F_GETSIG = 11;
|
||||
|
||||
pub const F_GETLK = 12;
|
||||
pub const F_SETLK = 13;
|
||||
pub const F_SETLKW = 14;
|
||||
|
||||
pub const F_SETOWN_EX = 15;
|
||||
pub const F_GETOWN_EX = 16;
|
||||
|
||||
pub const F_GETOWNER_UIDS = 17;
|
||||
|
||||
pub inline fn syscall0(number: usize) usize {
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number));
|
||||
}
|
||||
|
||||
pub inline fn syscall1(number: usize, arg1: usize) usize {
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number),
|
||||
[arg1] "{ebx}" (arg1));
|
||||
}
|
||||
|
||||
pub inline fn syscall2(number: usize, arg1: usize, arg2: usize) usize {
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number),
|
||||
[arg1] "{ebx}" (arg1),
|
||||
[arg2] "{ecx}" (arg2));
|
||||
}
|
||||
|
||||
pub inline fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number),
|
||||
[arg1] "{ebx}" (arg1),
|
||||
[arg2] "{ecx}" (arg2),
|
||||
[arg3] "{edx}" (arg3));
|
||||
}
|
||||
|
||||
pub inline fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize {
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number),
|
||||
[arg1] "{ebx}" (arg1),
|
||||
[arg2] "{ecx}" (arg2),
|
||||
[arg3] "{edx}" (arg3),
|
||||
[arg4] "{esi}" (arg4));
|
||||
}
|
||||
|
||||
pub inline fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize,
|
||||
arg4: usize, arg5: usize) usize
|
||||
{
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number),
|
||||
[arg1] "{ebx}" (arg1),
|
||||
[arg2] "{ecx}" (arg2),
|
||||
[arg3] "{edx}" (arg3),
|
||||
[arg4] "{esi}" (arg4),
|
||||
[arg5] "{edi}" (arg5));
|
||||
}
|
||||
|
||||
pub inline fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize,
|
||||
arg4: usize, arg5: usize, arg6: usize) usize
|
||||
{
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number),
|
||||
[arg1] "{ebx}" (arg1),
|
||||
[arg2] "{ecx}" (arg2),
|
||||
[arg3] "{edx}" (arg3),
|
||||
[arg4] "{esi}" (arg4),
|
||||
[arg5] "{edi}" (arg5),
|
||||
[arg6] "{ebp}" (arg6));
|
||||
}
|
||||
|
||||
pub nakedcc fn restore() void {
|
||||
asm volatile (
|
||||
\\popl %%eax
|
||||
\\movl $119, %%eax
|
||||
\\int $0x80
|
||||
:
|
||||
:
|
||||
: "rcx", "r11");
|
||||
}
|
||||
|
||||
pub nakedcc fn restore_rt() void {
|
||||
asm volatile ("int $0x80"
|
||||
:
|
||||
: [number] "{eax}" (usize(SYS_rt_sigreturn))
|
||||
: "rcx", "r11");
|
||||
}
|
@ -101,17 +101,6 @@ pub const SIG_BLOCK = 0;
|
||||
pub const SIG_UNBLOCK = 1;
|
||||
pub const SIG_SETMASK = 2;
|
||||
|
||||
pub const SOCK_STREAM = 1;
|
||||
pub const SOCK_DGRAM = 2;
|
||||
pub const SOCK_RAW = 3;
|
||||
pub const SOCK_RDM = 4;
|
||||
pub const SOCK_SEQPACKET = 5;
|
||||
pub const SOCK_DCCP = 6;
|
||||
pub const SOCK_PACKET = 10;
|
||||
pub const SOCK_CLOEXEC = 0o2000000;
|
||||
pub const SOCK_NONBLOCK = 0o4000;
|
||||
|
||||
|
||||
pub const PROTO_ip = 0o000;
|
||||
pub const PROTO_icmp = 0o001;
|
||||
pub const PROTO_igmp = 0o002;
|
||||
@ -149,6 +138,20 @@ pub const PROTO_encap = 0o142;
|
||||
pub const PROTO_pim = 0o147;
|
||||
pub const PROTO_raw = 0o377;
|
||||
|
||||
pub const SHUT_RD = 0;
|
||||
pub const SHUT_WR = 1;
|
||||
pub const SHUT_RDWR = 2;
|
||||
|
||||
pub const SOCK_STREAM = 1;
|
||||
pub const SOCK_DGRAM = 2;
|
||||
pub const SOCK_RAW = 3;
|
||||
pub const SOCK_RDM = 4;
|
||||
pub const SOCK_SEQPACKET = 5;
|
||||
pub const SOCK_DCCP = 6;
|
||||
pub const SOCK_PACKET = 10;
|
||||
pub const SOCK_CLOEXEC = 0o2000000;
|
||||
pub const SOCK_NONBLOCK = 0o4000;
|
||||
|
||||
pub const PF_UNSPEC = 0;
|
||||
pub const PF_LOCAL = 1;
|
||||
pub const PF_UNIX = PF_LOCAL;
|
||||
@ -193,7 +196,10 @@ pub const PF_CAIF = 37;
|
||||
pub const PF_ALG = 38;
|
||||
pub const PF_NFC = 39;
|
||||
pub const PF_VSOCK = 40;
|
||||
pub const PF_MAX = 41;
|
||||
pub const PF_KCM = 41;
|
||||
pub const PF_QIPCRTR = 42;
|
||||
pub const PF_SMC = 43;
|
||||
pub const PF_MAX = 44;
|
||||
|
||||
pub const AF_UNSPEC = PF_UNSPEC;
|
||||
pub const AF_LOCAL = PF_LOCAL;
|
||||
@ -239,8 +245,137 @@ pub const AF_CAIF = PF_CAIF;
|
||||
pub const AF_ALG = PF_ALG;
|
||||
pub const AF_NFC = PF_NFC;
|
||||
pub const AF_VSOCK = PF_VSOCK;
|
||||
pub const AF_KCM = PF_KCM;
|
||||
pub const AF_QIPCRTR = PF_QIPCRTR;
|
||||
pub const AF_SMC = PF_SMC;
|
||||
pub const AF_MAX = PF_MAX;
|
||||
|
||||
pub const SO_DEBUG = 1;
|
||||
pub const SO_REUSEADDR = 2;
|
||||
pub const SO_TYPE = 3;
|
||||
pub const SO_ERROR = 4;
|
||||
pub const SO_DONTROUTE = 5;
|
||||
pub const SO_BROADCAST = 6;
|
||||
pub const SO_SNDBUF = 7;
|
||||
pub const SO_RCVBUF = 8;
|
||||
pub const SO_KEEPALIVE = 9;
|
||||
pub const SO_OOBINLINE = 10;
|
||||
pub const SO_NO_CHECK = 11;
|
||||
pub const SO_PRIORITY = 12;
|
||||
pub const SO_LINGER = 13;
|
||||
pub const SO_BSDCOMPAT = 14;
|
||||
pub const SO_REUSEPORT = 15;
|
||||
pub const SO_PASSCRED = 16;
|
||||
pub const SO_PEERCRED = 17;
|
||||
pub const SO_RCVLOWAT = 18;
|
||||
pub const SO_SNDLOWAT = 19;
|
||||
pub const SO_RCVTIMEO = 20;
|
||||
pub const SO_SNDTIMEO = 21;
|
||||
pub const SO_ACCEPTCONN = 30;
|
||||
pub const SO_SNDBUFFORCE = 32;
|
||||
pub const SO_RCVBUFFORCE = 33;
|
||||
pub const SO_PROTOCOL = 38;
|
||||
pub const SO_DOMAIN = 39;
|
||||
|
||||
pub const SO_SECURITY_AUTHENTICATION = 22;
|
||||
pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23;
|
||||
pub const SO_SECURITY_ENCRYPTION_NETWORK = 24;
|
||||
|
||||
pub const SO_BINDTODEVICE = 25;
|
||||
|
||||
pub const SO_ATTACH_FILTER = 26;
|
||||
pub const SO_DETACH_FILTER = 27;
|
||||
pub const SO_GET_FILTER = SO_ATTACH_FILTER;
|
||||
|
||||
pub const SO_PEERNAME = 28;
|
||||
pub const SO_TIMESTAMP = 29;
|
||||
pub const SCM_TIMESTAMP = SO_TIMESTAMP;
|
||||
|
||||
pub const SO_PEERSEC = 31;
|
||||
pub const SO_PASSSEC = 34;
|
||||
pub const SO_TIMESTAMPNS = 35;
|
||||
pub const SCM_TIMESTAMPNS = SO_TIMESTAMPNS;
|
||||
pub const SO_MARK = 36;
|
||||
pub const SO_TIMESTAMPING = 37;
|
||||
pub const SCM_TIMESTAMPING = SO_TIMESTAMPING;
|
||||
pub const SO_RXQ_OVFL = 40;
|
||||
pub const SO_WIFI_STATUS = 41;
|
||||
pub const SCM_WIFI_STATUS = SO_WIFI_STATUS;
|
||||
pub const SO_PEEK_OFF = 42;
|
||||
pub const SO_NOFCS = 43;
|
||||
pub const SO_LOCK_FILTER = 44;
|
||||
pub const SO_SELECT_ERR_QUEUE = 45;
|
||||
pub const SO_BUSY_POLL = 46;
|
||||
pub const SO_MAX_PACING_RATE = 47;
|
||||
pub const SO_BPF_EXTENSIONS = 48;
|
||||
pub const SO_INCOMING_CPU = 49;
|
||||
pub const SO_ATTACH_BPF = 50;
|
||||
pub const SO_DETACH_BPF = SO_DETACH_FILTER;
|
||||
pub const SO_ATTACH_REUSEPORT_CBPF = 51;
|
||||
pub const SO_ATTACH_REUSEPORT_EBPF = 52;
|
||||
pub const SO_CNX_ADVICE = 53;
|
||||
pub const SCM_TIMESTAMPING_OPT_STATS = 54;
|
||||
pub const SO_MEMINFO = 55;
|
||||
pub const SO_INCOMING_NAPI_ID = 56;
|
||||
pub const SO_COOKIE = 57;
|
||||
pub const SCM_TIMESTAMPING_PKTINFO = 58;
|
||||
pub const SO_PEERGROUPS = 59;
|
||||
pub const SO_ZEROCOPY = 60;
|
||||
|
||||
pub const SOL_SOCKET = 1;
|
||||
|
||||
pub const SOL_IP = 0;
|
||||
pub const SOL_IPV6 = 41;
|
||||
pub const SOL_ICMPV6 = 58;
|
||||
|
||||
pub const SOL_RAW = 255;
|
||||
pub const SOL_DECNET = 261;
|
||||
pub const SOL_X25 = 262;
|
||||
pub const SOL_PACKET = 263;
|
||||
pub const SOL_ATM = 264;
|
||||
pub const SOL_AAL = 265;
|
||||
pub const SOL_IRDA = 266;
|
||||
pub const SOL_NETBEUI = 267;
|
||||
pub const SOL_LLC = 268;
|
||||
pub const SOL_DCCP = 269;
|
||||
pub const SOL_NETLINK = 270;
|
||||
pub const SOL_TIPC = 271;
|
||||
pub const SOL_RXRPC = 272;
|
||||
pub const SOL_PPPOL2TP = 273;
|
||||
pub const SOL_BLUETOOTH = 274;
|
||||
pub const SOL_PNPIPE = 275;
|
||||
pub const SOL_RDS = 276;
|
||||
pub const SOL_IUCV = 277;
|
||||
pub const SOL_CAIF = 278;
|
||||
pub const SOL_ALG = 279;
|
||||
pub const SOL_NFC = 280;
|
||||
pub const SOL_KCM = 281;
|
||||
pub const SOL_TLS = 282;
|
||||
|
||||
pub const SOMAXCONN = 128;
|
||||
|
||||
pub const MSG_OOB = 0x0001;
|
||||
pub const MSG_PEEK = 0x0002;
|
||||
pub const MSG_DONTROUTE = 0x0004;
|
||||
pub const MSG_CTRUNC = 0x0008;
|
||||
pub const MSG_PROXY = 0x0010;
|
||||
pub const MSG_TRUNC = 0x0020;
|
||||
pub const MSG_DONTWAIT = 0x0040;
|
||||
pub const MSG_EOR = 0x0080;
|
||||
pub const MSG_WAITALL = 0x0100;
|
||||
pub const MSG_FIN = 0x0200;
|
||||
pub const MSG_SYN = 0x0400;
|
||||
pub const MSG_CONFIRM = 0x0800;
|
||||
pub const MSG_RST = 0x1000;
|
||||
pub const MSG_ERRQUEUE = 0x2000;
|
||||
pub const MSG_NOSIGNAL = 0x4000;
|
||||
pub const MSG_MORE = 0x8000;
|
||||
pub const MSG_WAITFORONE = 0x10000;
|
||||
pub const MSG_BATCH = 0x40000;
|
||||
pub const MSG_ZEROCOPY = 0x4000000;
|
||||
pub const MSG_FASTOPEN = 0x20000000;
|
||||
pub const MSG_CMSG_CLOEXEC = 0x40000000;
|
||||
|
||||
pub const DT_UNKNOWN = 0;
|
||||
pub const DT_FIFO = 1;
|
||||
pub const DT_CHR = 2;
|
||||
@ -343,6 +478,126 @@ pub const CLOCK_BOOTTIME_ALARM = 9;
|
||||
pub const CLOCK_SGI_CYCLE = 10;
|
||||
pub const CLOCK_TAI = 11;
|
||||
|
||||
pub const CSIGNAL = 0x000000ff;
|
||||
pub const CLONE_VM = 0x00000100;
|
||||
pub const CLONE_FS = 0x00000200;
|
||||
pub const CLONE_FILES = 0x00000400;
|
||||
pub const CLONE_SIGHAND = 0x00000800;
|
||||
pub const CLONE_PTRACE = 0x00002000;
|
||||
pub const CLONE_VFORK = 0x00004000;
|
||||
pub const CLONE_PARENT = 0x00008000;
|
||||
pub const CLONE_THREAD = 0x00010000;
|
||||
pub const CLONE_NEWNS = 0x00020000;
|
||||
pub const CLONE_SYSVSEM = 0x00040000;
|
||||
pub const CLONE_SETTLS = 0x00080000;
|
||||
pub const CLONE_PARENT_SETTID = 0x00100000;
|
||||
pub const CLONE_CHILD_CLEARTID = 0x00200000;
|
||||
pub const CLONE_DETACHED = 0x00400000;
|
||||
pub const CLONE_UNTRACED = 0x00800000;
|
||||
pub const CLONE_CHILD_SETTID = 0x01000000;
|
||||
pub const CLONE_NEWCGROUP = 0x02000000;
|
||||
pub const CLONE_NEWUTS = 0x04000000;
|
||||
pub const CLONE_NEWIPC = 0x08000000;
|
||||
pub const CLONE_NEWUSER = 0x10000000;
|
||||
pub const CLONE_NEWPID = 0x20000000;
|
||||
pub const CLONE_NEWNET = 0x40000000;
|
||||
pub const CLONE_IO = 0x80000000;
|
||||
|
||||
pub const MS_RDONLY = 1;
|
||||
pub const MS_NOSUID = 2;
|
||||
pub const MS_NODEV = 4;
|
||||
pub const MS_NOEXEC = 8;
|
||||
pub const MS_SYNCHRONOUS = 16;
|
||||
pub const MS_REMOUNT = 32;
|
||||
pub const MS_MANDLOCK = 64;
|
||||
pub const MS_DIRSYNC = 128;
|
||||
pub const MS_NOATIME = 1024;
|
||||
pub const MS_NODIRATIME = 2048;
|
||||
pub const MS_BIND = 4096;
|
||||
pub const MS_MOVE = 8192;
|
||||
pub const MS_REC = 16384;
|
||||
pub const MS_SILENT = 32768;
|
||||
pub const MS_POSIXACL = (1<<16);
|
||||
pub const MS_UNBINDABLE = (1<<17);
|
||||
pub const MS_PRIVATE = (1<<18);
|
||||
pub const MS_SLAVE = (1<<19);
|
||||
pub const MS_SHARED = (1<<20);
|
||||
pub const MS_RELATIME = (1<<21);
|
||||
pub const MS_KERNMOUNT = (1<<22);
|
||||
pub const MS_I_VERSION = (1<<23);
|
||||
pub const MS_STRICTATIME = (1<<24);
|
||||
pub const MS_LAZYTIME = (1<<25);
|
||||
pub const MS_NOREMOTELOCK = (1<<27);
|
||||
pub const MS_NOSEC = (1<<28);
|
||||
pub const MS_BORN = (1<<29);
|
||||
pub const MS_ACTIVE = (1<<30);
|
||||
pub const MS_NOUSER = (1<<31);
|
||||
|
||||
pub const MS_RMT_MASK = (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|MS_LAZYTIME);
|
||||
|
||||
pub const MS_MGC_VAL = 0xc0ed0000;
|
||||
pub const MS_MGC_MSK = 0xffff0000;
|
||||
|
||||
pub const MNT_FORCE = 1;
|
||||
pub const MNT_DETACH = 2;
|
||||
pub const MNT_EXPIRE = 4;
|
||||
pub const UMOUNT_NOFOLLOW = 8;
|
||||
|
||||
|
||||
pub const S_IFMT = 0o170000;
|
||||
|
||||
pub const S_IFDIR = 0o040000;
|
||||
pub const S_IFCHR = 0o020000;
|
||||
pub const S_IFBLK = 0o060000;
|
||||
pub const S_IFREG = 0o100000;
|
||||
pub const S_IFIFO = 0o010000;
|
||||
pub const S_IFLNK = 0o120000;
|
||||
pub const S_IFSOCK = 0o140000;
|
||||
|
||||
pub const S_ISUID = 0o4000;
|
||||
pub const S_ISGID = 0o2000;
|
||||
pub const S_ISVTX = 0o1000;
|
||||
pub const S_IRUSR = 0o400;
|
||||
pub const S_IWUSR = 0o200;
|
||||
pub const S_IXUSR = 0o100;
|
||||
pub const S_IRWXU = 0o700;
|
||||
pub const S_IRGRP = 0o040;
|
||||
pub const S_IWGRP = 0o020;
|
||||
pub const S_IXGRP = 0o010;
|
||||
pub const S_IRWXG = 0o070;
|
||||
pub const S_IROTH = 0o004;
|
||||
pub const S_IWOTH = 0o002;
|
||||
pub const S_IXOTH = 0o001;
|
||||
pub const S_IRWXO = 0o007;
|
||||
|
||||
pub fn S_ISREG(m: u32) bool {
|
||||
return m & S_IFMT == S_IFREG;
|
||||
}
|
||||
|
||||
pub fn S_ISDIR(m: u32) bool {
|
||||
return m & S_IFMT == S_IFDIR;
|
||||
}
|
||||
|
||||
pub fn S_ISCHR(m: u32) bool {
|
||||
return m & S_IFMT == S_IFCHR;
|
||||
}
|
||||
|
||||
pub fn S_ISBLK(m: u32) bool {
|
||||
return m & S_IFMT == S_IFBLK;
|
||||
}
|
||||
|
||||
pub fn S_ISFIFO(m: u32) bool {
|
||||
return m & S_IFMT == S_IFIFO;
|
||||
}
|
||||
|
||||
pub fn S_ISLNK(m: u32) bool {
|
||||
return m & S_IFMT == S_IFLNK;
|
||||
}
|
||||
|
||||
pub fn S_ISSOCK(m: u32) bool {
|
||||
return m & S_IFMT == S_IFSOCK;
|
||||
}
|
||||
|
||||
pub const TFD_NONBLOCK = O_NONBLOCK;
|
||||
pub const TFD_CLOEXEC = O_CLOEXEC;
|
||||
|
||||
@ -380,6 +635,10 @@ pub fn chdir(path: &const u8) usize {
|
||||
return syscall1(SYS_chdir, @ptrToInt(path));
|
||||
}
|
||||
|
||||
pub fn chroot(path: &const u8) usize {
|
||||
return syscall1(SYS_chroot, @ptrToInt(path));
|
||||
}
|
||||
|
||||
pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) usize {
|
||||
return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));
|
||||
}
|
||||
@ -409,6 +668,18 @@ pub fn mkdir(path: &const u8, mode: u32) usize {
|
||||
return syscall2(SYS_mkdir, @ptrToInt(path), mode);
|
||||
}
|
||||
|
||||
pub fn mount(special: &const u8, dir: &const u8, fstype: &const u8, flags: usize, data: usize) usize {
|
||||
return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data);
|
||||
}
|
||||
|
||||
pub fn umount(special: &const u8) usize {
|
||||
return syscall2(SYS_umount2, @ptrToInt(special), 0);
|
||||
}
|
||||
|
||||
pub fn umount2(special: &const u8, flags: u32) usize {
|
||||
return syscall2(SYS_umount2, @ptrToInt(special), flags);
|
||||
}
|
||||
|
||||
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize {
|
||||
return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
|
||||
@bitCast(usize, offset));
|
||||
@ -515,6 +786,58 @@ pub fn setregid(rgid: u32, egid: u32) usize {
|
||||
return syscall2(SYS_setregid, rgid, egid);
|
||||
}
|
||||
|
||||
pub fn getuid() u32 {
|
||||
return u32(syscall0(SYS_getuid));
|
||||
}
|
||||
|
||||
pub fn getgid() u32 {
|
||||
return u32(syscall0(SYS_getgid));
|
||||
}
|
||||
|
||||
pub fn geteuid() u32 {
|
||||
return u32(syscall0(SYS_geteuid));
|
||||
}
|
||||
|
||||
pub fn getegid() u32 {
|
||||
return u32(syscall0(SYS_getegid));
|
||||
}
|
||||
|
||||
pub fn seteuid(euid: u32) usize {
|
||||
return syscall1(SYS_seteuid, euid);
|
||||
}
|
||||
|
||||
pub fn setegid(egid: u32) usize {
|
||||
return syscall1(SYS_setegid, egid);
|
||||
}
|
||||
|
||||
pub fn getresuid(ruid: &u32, euid: &u32, suid: &u32) usize {
|
||||
return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid));
|
||||
}
|
||||
|
||||
pub fn getresgid(rgid: &u32, egid: &u32, sgid: &u32) usize {
|
||||
return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid));
|
||||
}
|
||||
|
||||
pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize {
|
||||
return syscall3(SYS_setresuid, ruid, euid, suid);
|
||||
}
|
||||
|
||||
pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize {
|
||||
return syscall3(SYS_setresgid, rgid, egid, sgid);
|
||||
}
|
||||
|
||||
pub fn getgroups(size: usize, list: &u32) usize {
|
||||
return syscall2(SYS_getgroups, size, @ptrToInt(list));
|
||||
}
|
||||
|
||||
pub fn setgroups(size: usize, list: &const u32) usize {
|
||||
return syscall2(SYS_setgroups, size, @ptrToInt(list));
|
||||
}
|
||||
|
||||
pub fn getpid() i32 {
|
||||
return @bitCast(i32, u32(syscall0(SYS_getpid)));
|
||||
}
|
||||
|
||||
pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize {
|
||||
return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8);
|
||||
}
|
||||
@ -599,30 +922,27 @@ pub fn sigismember(set: &const sigset_t, sig: u6) bool {
|
||||
return ((*set)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0;
|
||||
}
|
||||
|
||||
|
||||
pub const in_port_t = u16;
|
||||
pub const sa_family_t = u16;
|
||||
pub const socklen_t = u32;
|
||||
pub const in_addr = u32;
|
||||
pub const in6_addr = [16]u8;
|
||||
|
||||
pub const sockaddr = extern struct {
|
||||
family: sa_family_t,
|
||||
port: u16,
|
||||
data: [12]u8,
|
||||
pub const sockaddr = extern union {
|
||||
in: sockaddr_in,
|
||||
in6: sockaddr_in6,
|
||||
};
|
||||
|
||||
pub const sockaddr_in = extern struct {
|
||||
family: sa_family_t,
|
||||
port: u16,
|
||||
addr: in_addr,
|
||||
port: in_port_t,
|
||||
addr: u32,
|
||||
zero: [8]u8,
|
||||
};
|
||||
|
||||
pub const sockaddr_in6 = extern struct {
|
||||
family: sa_family_t,
|
||||
port: u16,
|
||||
port: in_port_t,
|
||||
flowinfo: u32,
|
||||
addr: in6_addr,
|
||||
addr: [16]u8,
|
||||
scope_id: u32,
|
||||
};
|
||||
|
||||
@ -639,16 +959,16 @@ pub fn getpeername(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) us
|
||||
return syscall3(SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len));
|
||||
}
|
||||
|
||||
pub fn socket(domain: i32, socket_type: i32, protocol: i32) usize {
|
||||
return syscall3(SYS_socket, usize(domain), usize(socket_type), usize(protocol));
|
||||
pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
|
||||
return syscall3(SYS_socket, domain, socket_type, protocol);
|
||||
}
|
||||
|
||||
pub fn setsockopt(fd: i32, level: i32, optname: i32, optval: &const u8, optlen: socklen_t) usize {
|
||||
return syscall5(SYS_setsockopt, usize(fd), usize(level), usize(optname), usize(optval), @ptrToInt(optlen));
|
||||
pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: &const u8, optlen: socklen_t) usize {
|
||||
return syscall5(SYS_setsockopt, usize(fd), level, optname, usize(optval), @ptrToInt(optlen));
|
||||
}
|
||||
|
||||
pub fn getsockopt(fd: i32, level: i32, optname: i32, noalias optval: &u8, noalias optlen: &socklen_t) usize {
|
||||
return syscall5(SYS_getsockopt, usize(fd), usize(level), usize(optname), @ptrToInt(optval), @ptrToInt(optlen));
|
||||
pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: &u8, noalias optlen: &socklen_t) usize {
|
||||
return syscall5(SYS_getsockopt, usize(fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
|
||||
}
|
||||
|
||||
pub fn sendmsg(fd: i32, msg: &const msghdr, flags: u32) usize {
|
||||
@ -677,8 +997,8 @@ pub fn bind(fd: i32, addr: &const sockaddr, len: socklen_t) usize {
|
||||
return syscall3(SYS_bind, usize(fd), @ptrToInt(addr), usize(len));
|
||||
}
|
||||
|
||||
pub fn listen(fd: i32, backlog: i32) usize {
|
||||
return syscall2(SYS_listen, usize(fd), usize(backlog));
|
||||
pub fn listen(fd: i32, backlog: u32) usize {
|
||||
return syscall2(SYS_listen, usize(fd), backlog);
|
||||
}
|
||||
|
||||
pub fn sendto(fd: i32, buf: &const u8, len: usize, flags: u32, addr: ?&const sockaddr, alen: socklen_t) usize {
|
||||
@ -697,46 +1017,83 @@ pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags:
|
||||
return syscall4(SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags);
|
||||
}
|
||||
|
||||
// error NameTooLong;
|
||||
// error SystemResources;
|
||||
// error Io;
|
||||
//
|
||||
// pub fn if_nametoindex(name: []u8) !u32 {
|
||||
// var ifr: ifreq = undefined;
|
||||
//
|
||||
// if (name.len >= ifr.ifr_name.len) {
|
||||
// return error.NameTooLong;
|
||||
// }
|
||||
//
|
||||
// const socket_ret = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
|
||||
// const socket_err = getErrno(socket_ret);
|
||||
// if (socket_err > 0) {
|
||||
// return error.SystemResources;
|
||||
// }
|
||||
// const socket_fd = i32(socket_ret);
|
||||
// @memcpy(&ifr.ifr_name[0], &name[0], name.len);
|
||||
// ifr.ifr_name[name.len] = 0;
|
||||
// const ioctl_ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr);
|
||||
// close(socket_fd);
|
||||
// const ioctl_err = getErrno(ioctl_ret);
|
||||
// if (ioctl_err > 0) {
|
||||
// return error.Io;
|
||||
// }
|
||||
// return ifr.ifr_ifindex;
|
||||
// }
|
||||
|
||||
pub fn fstat(fd: i32, stat_buf: &Stat) usize {
|
||||
return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf));
|
||||
}
|
||||
|
||||
pub const epoll_data = extern union {
|
||||
pub fn stat(pathname: &const u8, statbuf: &Stat) usize {
|
||||
return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf));
|
||||
}
|
||||
|
||||
pub fn lstat(pathname: &const u8, statbuf: &Stat) usize {
|
||||
return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf));
|
||||
}
|
||||
|
||||
pub fn listxattr(path: &const u8, list: &u8, size: usize) usize {
|
||||
return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size);
|
||||
}
|
||||
|
||||
pub fn llistxattr(path: &const u8, list: &u8, size: usize) usize {
|
||||
return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size);
|
||||
}
|
||||
|
||||
pub fn flistxattr(fd: usize, list: &u8, size: usize) usize {
|
||||
return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size);
|
||||
}
|
||||
|
||||
pub fn getxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize {
|
||||
return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
|
||||
}
|
||||
|
||||
pub fn lgetxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize {
|
||||
return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
|
||||
}
|
||||
|
||||
pub fn fgetxattr(fd: usize, name: &const u8, value: &void, size: usize) usize {
|
||||
return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size);
|
||||
}
|
||||
|
||||
pub fn setxattr(path: &const u8, name: &const u8, value: &const void,
|
||||
size: usize, flags: usize) usize {
|
||||
|
||||
return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value),
|
||||
size, flags);
|
||||
}
|
||||
|
||||
pub fn lsetxattr(path: &const u8, name: &const u8, value: &const void,
|
||||
size: usize, flags: usize) usize {
|
||||
|
||||
return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value),
|
||||
size, flags);
|
||||
}
|
||||
|
||||
pub fn fsetxattr(fd: usize, name: &const u8, value: &const void,
|
||||
size: usize, flags: usize) usize {
|
||||
|
||||
return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value),
|
||||
size, flags);
|
||||
}
|
||||
|
||||
pub fn removexattr(path: &const u8, name: &const u8) usize {
|
||||
return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name));
|
||||
}
|
||||
|
||||
pub fn lremovexattr(path: &const u8, name: &const u8) usize {
|
||||
return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name));
|
||||
}
|
||||
|
||||
pub fn fremovexattr(fd: usize, name: &const u8) usize {
|
||||
return syscall2(SYS_fremovexattr, fd, @ptrToInt(name));
|
||||
}
|
||||
|
||||
pub const epoll_data = packed union {
|
||||
ptr: usize,
|
||||
fd: i32,
|
||||
@"u32": u32,
|
||||
@"u64": u64,
|
||||
};
|
||||
|
||||
pub const epoll_event = extern struct {
|
||||
pub const epoll_event = packed struct {
|
||||
events: u32,
|
||||
data: epoll_data,
|
||||
};
|
||||
@ -749,7 +1106,7 @@ pub fn epoll_create1(flags: usize) usize {
|
||||
return syscall1(SYS_epoll_create1, flags);
|
||||
}
|
||||
|
||||
pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) usize {
|
||||
pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: &epoll_event) usize {
|
||||
return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
|
||||
}
|
||||
|
||||
@ -774,6 +1131,125 @@ pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_va
|
||||
return syscall4(SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value));
|
||||
}
|
||||
|
||||
pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330;
|
||||
pub const _LINUX_CAPABILITY_U32S_1 = 1;
|
||||
|
||||
pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026;
|
||||
pub const _LINUX_CAPABILITY_U32S_2 = 2;
|
||||
|
||||
pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522;
|
||||
pub const _LINUX_CAPABILITY_U32S_3 = 2;
|
||||
|
||||
pub const VFS_CAP_REVISION_MASK = 0xFF000000;
|
||||
pub const VFS_CAP_REVISION_SHIFT = 24;
|
||||
pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK;
|
||||
pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001;
|
||||
|
||||
pub const VFS_CAP_REVISION_1 = 0x01000000;
|
||||
pub const VFS_CAP_U32_1 = 1;
|
||||
pub const XATTR_CAPS_SZ_1 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_1);
|
||||
|
||||
pub const VFS_CAP_REVISION_2 = 0x02000000;
|
||||
pub const VFS_CAP_U32_2 = 2;
|
||||
pub const XATTR_CAPS_SZ_2 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_2);
|
||||
|
||||
pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2;
|
||||
pub const VFS_CAP_U32 = VFS_CAP_U32_2;
|
||||
pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2;
|
||||
|
||||
pub const vfs_cap_data = extern struct {
|
||||
//all of these are mandated as little endian
|
||||
//when on disk.
|
||||
const Data = struct {
|
||||
permitted: u32,
|
||||
inheritable: u32,
|
||||
};
|
||||
|
||||
magic_etc: u32,
|
||||
data: [VFS_CAP_U32]Data,
|
||||
};
|
||||
|
||||
|
||||
pub const CAP_CHOWN = 0;
|
||||
pub const CAP_DAC_OVERRIDE = 1;
|
||||
pub const CAP_DAC_READ_SEARCH = 2;
|
||||
pub const CAP_FOWNER = 3;
|
||||
pub const CAP_FSETID = 4;
|
||||
pub const CAP_KILL = 5;
|
||||
pub const CAP_SETGID = 6;
|
||||
pub const CAP_SETUID = 7;
|
||||
pub const CAP_SETPCAP = 8;
|
||||
pub const CAP_LINUX_IMMUTABLE = 9;
|
||||
pub const CAP_NET_BIND_SERVICE = 10;
|
||||
pub const CAP_NET_BROADCAST = 11;
|
||||
pub const CAP_NET_ADMIN = 12;
|
||||
pub const CAP_NET_RAW = 13;
|
||||
pub const CAP_IPC_LOCK = 14;
|
||||
pub const CAP_IPC_OWNER = 15;
|
||||
pub const CAP_SYS_MODULE = 16;
|
||||
pub const CAP_SYS_RAWIO = 17;
|
||||
pub const CAP_SYS_CHROOT = 18;
|
||||
pub const CAP_SYS_PTRACE = 19;
|
||||
pub const CAP_SYS_PACCT = 20;
|
||||
pub const CAP_SYS_ADMIN = 21;
|
||||
pub const CAP_SYS_BOOT = 22;
|
||||
pub const CAP_SYS_NICE = 23;
|
||||
pub const CAP_SYS_RESOURCE = 24;
|
||||
pub const CAP_SYS_TIME = 25;
|
||||
pub const CAP_SYS_TTY_CONFIG = 26;
|
||||
pub const CAP_MKNOD = 27;
|
||||
pub const CAP_LEASE = 28;
|
||||
pub const CAP_AUDIT_WRITE = 29;
|
||||
pub const CAP_AUDIT_CONTROL = 30;
|
||||
pub const CAP_SETFCAP = 31;
|
||||
pub const CAP_MAC_OVERRIDE = 32;
|
||||
pub const CAP_MAC_ADMIN = 33;
|
||||
pub const CAP_SYSLOG = 34;
|
||||
pub const CAP_WAKE_ALARM = 35;
|
||||
pub const CAP_BLOCK_SUSPEND = 36;
|
||||
pub const CAP_AUDIT_READ = 37;
|
||||
pub const CAP_LAST_CAP = CAP_AUDIT_READ;
|
||||
|
||||
pub fn cap_valid(u8: x) bool {
|
||||
return x >= 0 and x <= CAP_LAST_CAP;
|
||||
}
|
||||
|
||||
pub fn CAP_TO_MASK(cap: u8) u32 {
|
||||
return u32(1) << u5(cap & 31);
|
||||
}
|
||||
|
||||
pub fn CAP_TO_INDEX(cap: u8) u8 {
|
||||
return cap >> 5;
|
||||
}
|
||||
|
||||
pub const cap_t = extern struct {
|
||||
hdrp: &cap_user_header_t,
|
||||
datap: &cap_user_data_t,
|
||||
};
|
||||
|
||||
pub const cap_user_header_t = extern struct {
|
||||
version: u32,
|
||||
pid: usize,
|
||||
};
|
||||
|
||||
pub const cap_user_data_t = extern struct {
|
||||
effective: u32,
|
||||
permitted: u32,
|
||||
inheritable: u32,
|
||||
};
|
||||
|
||||
pub fn unshare(flags: usize) usize {
|
||||
return syscall1(SYS_unshare, usize(flags));
|
||||
}
|
||||
|
||||
pub fn capget(hdrp: &cap_user_header_t, datap: &cap_user_data_t) usize {
|
||||
return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap));
|
||||
}
|
||||
|
||||
pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize {
|
||||
return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
|
||||
}
|
||||
|
||||
test "import linux test" {
|
||||
// TODO lazy analysis should prevent this test from being compiled on windows, but
|
||||
// it is still compiled on windows
|
||||
|
@ -14,26 +14,43 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn
|
||||
}
|
||||
}
|
||||
|
||||
// Note that memset does not return `dest`, like the libc API.
|
||||
// The semantics of memset is dictated by the corresponding
|
||||
// LLVM intrinsics, not by the libc API.
|
||||
export fn memset(dest: ?&u8, c: u8, n: usize) void {
|
||||
export fn memset(dest: ?&u8, c: u8, n: usize) ?&u8 {
|
||||
@setRuntimeSafety(false);
|
||||
|
||||
var index: usize = 0;
|
||||
while (index != n) : (index += 1)
|
||||
(??dest)[index] = c;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Note that memcpy does not return `dest`, like the libc API.
|
||||
// The semantics of memcpy is dictated by the corresponding
|
||||
// LLVM intrinsics, not by the libc API.
|
||||
export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) void {
|
||||
export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) ?&u8 {
|
||||
@setRuntimeSafety(false);
|
||||
|
||||
var index: usize = 0;
|
||||
while (index != n) : (index += 1)
|
||||
(??dest)[index] = (??src)[index];
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
export fn memmove(dest: ?&u8, src: ?&const u8, n: usize) ?&u8 {
|
||||
@setRuntimeSafety(false);
|
||||
|
||||
if (@ptrToInt(dest) < @ptrToInt(src)) {
|
||||
var index: usize = 0;
|
||||
while (index != n) : (index += 1) {
|
||||
(??dest)[index] = (??src)[index];
|
||||
}
|
||||
} else {
|
||||
var index = n;
|
||||
while (index != 0) {
|
||||
index -= 1;
|
||||
(??dest)[index] = (??src)[index];
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
comptime {
|
||||
|
1376
std/zig/ast.zig
1376
std/zig/ast.zig
File diff suppressed because it is too large
Load Diff
5000
std/zig/parser.zig
5000
std/zig/parser.zig
File diff suppressed because it is too large
Load Diff
@ -5,8 +5,6 @@ pub const Token = struct {
|
||||
id: Id,
|
||||
start: usize,
|
||||
end: usize,
|
||||
line: usize,
|
||||
column: usize,
|
||||
|
||||
const KeywordId = struct {
|
||||
bytes: []const u8,
|
||||
@ -17,14 +15,18 @@ pub const Token = struct {
|
||||
KeywordId{.bytes="align", .id = Id.Keyword_align},
|
||||
KeywordId{.bytes="and", .id = Id.Keyword_and},
|
||||
KeywordId{.bytes="asm", .id = Id.Keyword_asm},
|
||||
KeywordId{.bytes="async", .id = Id.Keyword_async},
|
||||
KeywordId{.bytes="await", .id = Id.Keyword_await},
|
||||
KeywordId{.bytes="break", .id = Id.Keyword_break},
|
||||
KeywordId{.bytes="catch", .id = Id.Keyword_catch},
|
||||
KeywordId{.bytes="cancel", .id = Id.Keyword_cancel},
|
||||
KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
|
||||
KeywordId{.bytes="const", .id = Id.Keyword_const},
|
||||
KeywordId{.bytes="continue", .id = Id.Keyword_continue},
|
||||
KeywordId{.bytes="defer", .id = Id.Keyword_defer},
|
||||
KeywordId{.bytes="else", .id = Id.Keyword_else},
|
||||
KeywordId{.bytes="enum", .id = Id.Keyword_enum},
|
||||
KeywordId{.bytes="errdefer", .id = Id.Keyword_errdefer},
|
||||
KeywordId{.bytes="error", .id = Id.Keyword_error},
|
||||
KeywordId{.bytes="export", .id = Id.Keyword_export},
|
||||
KeywordId{.bytes="extern", .id = Id.Keyword_extern},
|
||||
@ -39,10 +41,12 @@ pub const Token = struct {
|
||||
KeywordId{.bytes="or", .id = Id.Keyword_or},
|
||||
KeywordId{.bytes="packed", .id = Id.Keyword_packed},
|
||||
KeywordId{.bytes="pub", .id = Id.Keyword_pub},
|
||||
KeywordId{.bytes="resume", .id = Id.Keyword_resume},
|
||||
KeywordId{.bytes="return", .id = Id.Keyword_return},
|
||||
KeywordId{.bytes="section", .id = Id.Keyword_section},
|
||||
KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
|
||||
KeywordId{.bytes="struct", .id = Id.Keyword_struct},
|
||||
KeywordId{.bytes="suspend", .id = Id.Keyword_suspend},
|
||||
KeywordId{.bytes="switch", .id = Id.Keyword_switch},
|
||||
KeywordId{.bytes="test", .id = Id.Keyword_test},
|
||||
KeywordId{.bytes="this", .id = Id.Keyword_this},
|
||||
@ -72,7 +76,8 @@ pub const Token = struct {
|
||||
Invalid,
|
||||
Identifier,
|
||||
StringLiteral: StrLitKind,
|
||||
StringIdentifier,
|
||||
MultilineStringLiteralLine: StrLitKind,
|
||||
CharLiteral,
|
||||
Eof,
|
||||
Builtin,
|
||||
Bang,
|
||||
@ -81,6 +86,7 @@ pub const Token = struct {
|
||||
PipeEqual,
|
||||
Equal,
|
||||
EqualEqual,
|
||||
EqualAngleBracketRight,
|
||||
BangEqual,
|
||||
LParen,
|
||||
RParen,
|
||||
@ -89,6 +95,8 @@ pub const Token = struct {
|
||||
PercentEqual,
|
||||
LBrace,
|
||||
RBrace,
|
||||
LBracket,
|
||||
RBracket,
|
||||
Period,
|
||||
Ellipsis2,
|
||||
Ellipsis3,
|
||||
@ -132,7 +140,10 @@ pub const Token = struct {
|
||||
Keyword_align,
|
||||
Keyword_and,
|
||||
Keyword_asm,
|
||||
Keyword_async,
|
||||
Keyword_await,
|
||||
Keyword_break,
|
||||
Keyword_cancel,
|
||||
Keyword_catch,
|
||||
Keyword_comptime,
|
||||
Keyword_const,
|
||||
@ -140,6 +151,7 @@ pub const Token = struct {
|
||||
Keyword_defer,
|
||||
Keyword_else,
|
||||
Keyword_enum,
|
||||
Keyword_errdefer,
|
||||
Keyword_error,
|
||||
Keyword_export,
|
||||
Keyword_extern,
|
||||
@ -154,10 +166,12 @@ pub const Token = struct {
|
||||
Keyword_or,
|
||||
Keyword_packed,
|
||||
Keyword_pub,
|
||||
Keyword_resume,
|
||||
Keyword_return,
|
||||
Keyword_section,
|
||||
Keyword_stdcallcc,
|
||||
Keyword_struct,
|
||||
Keyword_suspend,
|
||||
Keyword_switch,
|
||||
Keyword_test,
|
||||
Keyword_this,
|
||||
@ -176,28 +190,34 @@ pub const Token = struct {
|
||||
pub const Tokenizer = struct {
|
||||
buffer: []const u8,
|
||||
index: usize,
|
||||
line: usize,
|
||||
column: usize,
|
||||
pending_invalid_token: ?Token,
|
||||
|
||||
pub const LineLocation = struct {
|
||||
pub const Location = struct {
|
||||
line: usize,
|
||||
column: usize,
|
||||
line_start: usize,
|
||||
line_end: usize,
|
||||
};
|
||||
|
||||
pub fn getTokenLocation(self: &Tokenizer, token: &const Token) LineLocation {
|
||||
var loc = LineLocation {
|
||||
.line_start = 0,
|
||||
pub fn getTokenLocation(self: &Tokenizer, start_index: usize, token: &const Token) Location {
|
||||
var loc = Location {
|
||||
.line = 0,
|
||||
.column = 0,
|
||||
.line_start = start_index,
|
||||
.line_end = self.buffer.len,
|
||||
};
|
||||
for (self.buffer) |c, i| {
|
||||
if (i == token.start) {
|
||||
loc.line_end = i;
|
||||
for (self.buffer[start_index..]) |c, i| {
|
||||
if (i + start_index == token.start) {
|
||||
loc.line_end = i + start_index;
|
||||
while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {}
|
||||
return loc;
|
||||
}
|
||||
if (c == '\n') {
|
||||
loc.line += 1;
|
||||
loc.column = 0;
|
||||
loc.line_start = i + 1;
|
||||
} else {
|
||||
loc.column += 1;
|
||||
}
|
||||
}
|
||||
return loc;
|
||||
@ -212,8 +232,6 @@ pub const Tokenizer = struct {
|
||||
return Tokenizer {
|
||||
.buffer = buffer,
|
||||
.index = 0,
|
||||
.line = 0,
|
||||
.column = 0,
|
||||
.pending_invalid_token = null,
|
||||
};
|
||||
}
|
||||
@ -225,6 +243,12 @@ pub const Tokenizer = struct {
|
||||
C,
|
||||
StringLiteral,
|
||||
StringLiteralBackslash,
|
||||
MultilineStringLiteralLine,
|
||||
MultilineStringLiteralLineBackslash,
|
||||
CharLiteral,
|
||||
CharLiteralBackslash,
|
||||
CharLiteralEnd,
|
||||
Backslash,
|
||||
Equal,
|
||||
Bang,
|
||||
Pipe,
|
||||
@ -261,26 +285,22 @@ pub const Tokenizer = struct {
|
||||
self.pending_invalid_token = null;
|
||||
return token;
|
||||
}
|
||||
const start_index = self.index;
|
||||
var state = State.Start;
|
||||
var result = Token {
|
||||
.id = Token.Id.Eof,
|
||||
.start = self.index,
|
||||
.end = undefined,
|
||||
.line = self.line,
|
||||
.column = self.column,
|
||||
};
|
||||
while (self.index < self.buffer.len) {
|
||||
while (self.index < self.buffer.len) : (self.index += 1) {
|
||||
const c = self.buffer[self.index];
|
||||
switch (state) {
|
||||
State.Start => switch (c) {
|
||||
' ' => {
|
||||
result.start = self.index + 1;
|
||||
result.column += 1;
|
||||
},
|
||||
'\n' => {
|
||||
result.start = self.index + 1;
|
||||
result.line += 1;
|
||||
result.column = 0;
|
||||
},
|
||||
'c' => {
|
||||
state = State.C;
|
||||
@ -290,6 +310,9 @@ pub const Tokenizer = struct {
|
||||
state = State.StringLiteral;
|
||||
result.id = Token.Id { .StringLiteral = Token.StrLitKind.Normal };
|
||||
},
|
||||
'\'' => {
|
||||
state = State.CharLiteral;
|
||||
},
|
||||
'a'...'b', 'd'...'z', 'A'...'Z', '_' => {
|
||||
state = State.Identifier;
|
||||
result.id = Token.Id.Identifier;
|
||||
@ -316,6 +339,16 @@ pub const Tokenizer = struct {
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'[' => {
|
||||
result.id = Token.Id.LBracket;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
']' => {
|
||||
result.id = Token.Id.RBracket;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
';' => {
|
||||
result.id = Token.Id.Semicolon;
|
||||
self.index += 1;
|
||||
@ -352,6 +385,10 @@ pub const Tokenizer = struct {
|
||||
'^' => {
|
||||
state = State.Caret;
|
||||
},
|
||||
'\\' => {
|
||||
state = State.Backslash;
|
||||
result.id = Token.Id { .MultilineStringLiteralLine = Token.StrLitKind.Normal };
|
||||
},
|
||||
'{' => {
|
||||
result.id = Token.Id.LBrace;
|
||||
self.index += 1;
|
||||
@ -396,7 +433,7 @@ pub const Tokenizer = struct {
|
||||
|
||||
State.SawAtSign => switch (c) {
|
||||
'"' => {
|
||||
result.id = Token.Id.StringIdentifier;
|
||||
result.id = Token.Id.Identifier;
|
||||
state = State.StringLiteral;
|
||||
},
|
||||
else => {
|
||||
@ -532,8 +569,17 @@ pub const Tokenizer = struct {
|
||||
'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
|
||||
else => break,
|
||||
},
|
||||
State.Backslash => switch (c) {
|
||||
'\\' => {
|
||||
state = State.MultilineStringLiteralLine;
|
||||
},
|
||||
else => break,
|
||||
},
|
||||
State.C => switch (c) {
|
||||
'\\' => @panic("TODO"),
|
||||
'\\' => {
|
||||
state = State.Backslash;
|
||||
result.id = Token.Id { .MultilineStringLiteralLine = Token.StrLitKind.C };
|
||||
},
|
||||
'"' => {
|
||||
state = State.StringLiteral;
|
||||
result.id = Token.Id { .StringLiteral = Token.StrLitKind.C };
|
||||
@ -562,6 +608,64 @@ pub const Tokenizer = struct {
|
||||
},
|
||||
},
|
||||
|
||||
State.CharLiteral => switch (c) {
|
||||
'\\' => {
|
||||
state = State.CharLiteralBackslash;
|
||||
},
|
||||
'\'' => {
|
||||
result.id = Token.Id.Invalid;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
if (c < 0x20 or c == 0x7f) {
|
||||
result.id = Token.Id.Invalid;
|
||||
break;
|
||||
}
|
||||
|
||||
state = State.CharLiteralEnd;
|
||||
}
|
||||
},
|
||||
|
||||
State.CharLiteralBackslash => switch (c) {
|
||||
'\n' => {
|
||||
result.id = Token.Id.Invalid;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
state = State.CharLiteralEnd;
|
||||
},
|
||||
},
|
||||
|
||||
State.CharLiteralEnd => switch (c) {
|
||||
'\'' => {
|
||||
result.id = Token.Id.CharLiteral;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Invalid;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.MultilineStringLiteralLine => switch (c) {
|
||||
'\\' => {
|
||||
state = State.MultilineStringLiteralLineBackslash;
|
||||
},
|
||||
'\n' => {
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => self.checkLiteralCharacter(),
|
||||
},
|
||||
|
||||
State.MultilineStringLiteralLineBackslash => switch (c) {
|
||||
'\n' => break, // Look for this error later.
|
||||
else => {
|
||||
state = State.MultilineStringLiteralLine;
|
||||
},
|
||||
},
|
||||
|
||||
State.Bang => switch (c) {
|
||||
'=' => {
|
||||
result.id = Token.Id.BangEqual;
|
||||
@ -597,6 +701,11 @@ pub const Tokenizer = struct {
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'>' => {
|
||||
result.id = Token.Id.EqualAngleBracketRight;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Equal;
|
||||
break;
|
||||
@ -794,14 +903,6 @@ pub const Tokenizer = struct {
|
||||
else => break,
|
||||
},
|
||||
}
|
||||
|
||||
self.index += 1;
|
||||
if (c == '\n') {
|
||||
self.line += 1;
|
||||
self.column = 0;
|
||||
} else {
|
||||
self.column += 1;
|
||||
}
|
||||
} else if (self.index == self.buffer.len) {
|
||||
switch (state) {
|
||||
State.Start,
|
||||
@ -811,6 +912,7 @@ pub const Tokenizer = struct {
|
||||
State.FloatFraction,
|
||||
State.FloatExponentNumber,
|
||||
State.StringLiteral, // find this error later
|
||||
State.MultilineStringLiteralLine,
|
||||
State.Builtin => {},
|
||||
|
||||
State.Identifier => {
|
||||
@ -825,6 +927,11 @@ pub const Tokenizer = struct {
|
||||
State.NumberDot,
|
||||
State.FloatExponentUnsigned,
|
||||
State.SawAtSign,
|
||||
State.Backslash,
|
||||
State.MultilineStringLiteralLineBackslash,
|
||||
State.CharLiteral,
|
||||
State.CharLiteralBackslash,
|
||||
State.CharLiteralEnd,
|
||||
State.StringLiteralBackslash => {
|
||||
result.id = Token.Id.Invalid;
|
||||
},
|
||||
@ -894,6 +1001,7 @@ pub const Tokenizer = struct {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (result.id == Token.Id.Eof) {
|
||||
if (self.pending_invalid_token) |token| {
|
||||
self.pending_invalid_token = null;
|
||||
@ -917,8 +1025,6 @@ pub const Tokenizer = struct {
|
||||
.id = Token.Id.Invalid,
|
||||
.start = self.index,
|
||||
.end = self.index + invalid_length,
|
||||
.line = self.line,
|
||||
.column = self.column,
|
||||
};
|
||||
}
|
||||
|
||||
@ -968,9 +1074,16 @@ test "tokenizer" {
|
||||
});
|
||||
}
|
||||
|
||||
test "tokenizer - chars" {
|
||||
testTokenize("'c'", []Token.Id {Token.Id.CharLiteral});
|
||||
}
|
||||
|
||||
test "tokenizer - invalid token characters" {
|
||||
testTokenize("#", []Token.Id{Token.Id.Invalid});
|
||||
testTokenize("`", []Token.Id{Token.Id.Invalid});
|
||||
testTokenize("'c", []Token.Id {Token.Id.Invalid});
|
||||
testTokenize("'", []Token.Id {Token.Id.Invalid});
|
||||
testTokenize("''", []Token.Id {Token.Id.Invalid, Token.Id.Invalid});
|
||||
}
|
||||
|
||||
test "tokenizer - invalid literal/comment characters" {
|
||||
@ -1022,7 +1135,7 @@ test "tokenizer - string identifier and builtin fns" {
|
||||
,
|
||||
[]Token.Id{
|
||||
Token.Id.Keyword_const,
|
||||
Token.Id.StringIdentifier,
|
||||
Token.Id.Identifier,
|
||||
Token.Id.Equal,
|
||||
Token.Id.Builtin,
|
||||
Token.Id.LParen,
|
||||
|
@ -1,4 +1,5 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
var x: i32 = 1;
|
||||
@ -189,3 +190,37 @@ async fn failing() !void {
|
||||
suspend;
|
||||
return error.Fail;
|
||||
}
|
||||
|
||||
test "error return trace across suspend points - early return" {
|
||||
const p = nonFailing();
|
||||
resume p;
|
||||
const p2 = try async<std.debug.global_allocator> printTrace(p);
|
||||
cancel p2;
|
||||
}
|
||||
|
||||
test "error return trace across suspend points - async return" {
|
||||
const p = nonFailing();
|
||||
const p2 = try async<std.debug.global_allocator> printTrace(p);
|
||||
resume p;
|
||||
cancel p2;
|
||||
}
|
||||
|
||||
fn nonFailing() promise->error!void {
|
||||
return async<std.debug.global_allocator> suspendThenFail() catch unreachable;
|
||||
}
|
||||
|
||||
async fn suspendThenFail() error!void {
|
||||
suspend;
|
||||
return error.Fail;
|
||||
}
|
||||
|
||||
async fn printTrace(p: promise->error!void) void {
|
||||
(await p) catch |e| {
|
||||
std.debug.assert(e == error.Fail);
|
||||
if (@errorReturnTrace()) |trace| {
|
||||
assert(trace.index == 1);
|
||||
} else if (builtin.mode != builtin.Mode.ReleaseFast) {
|
||||
@panic("expected return trace");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -94,3 +94,13 @@ test "inline function call" {
|
||||
}
|
||||
|
||||
fn add(a: i32, b: i32) i32 { return a + b; }
|
||||
|
||||
|
||||
test "number literal as an argument" {
|
||||
numberLiteralArg(3);
|
||||
comptime numberLiteralArg(3);
|
||||
}
|
||||
|
||||
fn numberLiteralArg(a: var) void {
|
||||
assert(a == 3);
|
||||
}
|
||||
|
@ -1723,7 +1723,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
|
||||
\\}
|
||||
\\
|
||||
\\export fn entry() usize { return @sizeOf(@typeOf(bar)); }
|
||||
, ".tmp_source.zig:10:16: error: parameter of type '(integer literal)' requires comptime");
|
||||
, ".tmp_source.zig:10:16: error: compiler bug: integer and float literals in var args function must be casted");
|
||||
|
||||
cases.add("assign too big number to u16",
|
||||
\\export fn foo() void {
|
||||
|
Loading…
Reference in New Issue
Block a user