Merge branch 'master' into zen_stdlib
commit
135a335ce1
|
@ -1,9 +1,11 @@
|
|||
sudo: required
|
||||
services:
|
||||
- docker
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
dist: trusty
|
||||
osx_image: xcode8.3
|
||||
sudo: required
|
||||
language: cpp
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi
|
||||
|
|
|
@ -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})
|
||||
|
||||
|
@ -30,11 +35,7 @@ if(GIT_EXE)
|
|||
endif()
|
||||
message("Configuring zig version ${ZIG_VERSION}")
|
||||
|
||||
set(ZIG_LIBC_LIB_DIR "" CACHE STRING "Default native target libc directory where crt1.o can be found")
|
||||
set(ZIG_LIBC_STATIC_LIB_DIR "" CACHE STRING "Default native target libc directory where crtbeginT.o can be found")
|
||||
set(ZIG_LIBC_INCLUDE_DIR "/usr/include" CACHE STRING "Default native target libc include directory")
|
||||
set(ZIG_DYNAMIC_LINKER "" CACHE STRING "Override dynamic linker for native target")
|
||||
set(ZIG_EACH_LIB_RPATH off CACHE BOOL "Add each dynamic library to rpath for native target")
|
||||
set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)")
|
||||
|
||||
string(REGEX REPLACE "\\\\" "\\\\\\\\" ZIG_LIBC_LIB_DIR_ESCAPED "${ZIG_LIBC_LIB_DIR}")
|
||||
string(REGEX REPLACE "\\\\" "\\\\\\\\" ZIG_LIBC_STATIC_LIB_DIR_ESCAPED "${ZIG_LIBC_STATIC_LIB_DIR}")
|
||||
|
@ -49,6 +50,22 @@ option(ZIG_FORCE_EXTERNAL_LLD "If your system has the LLD patches use it instead
|
|||
find_package(llvm)
|
||||
find_package(clang)
|
||||
|
||||
if(NOT MSVC)
|
||||
find_library(LIBXML2 NAMES xml2 libxml2)
|
||||
if(${LIBXML2} STREQUAL "LIBXML2-NOTFOUND")
|
||||
message(FATAL_ERROR "Could not find libxml2")
|
||||
else()
|
||||
message("${LIBXML2} found")
|
||||
endif()
|
||||
|
||||
find_library(ZLIB NAMES z zlib libz)
|
||||
if(${ZLIB} STREQUAL "ZLIB-NOTFOUND")
|
||||
message(FATAL_ERROR "Could not find zlib")
|
||||
else()
|
||||
message("${ZLIB} found")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(ZIG_CPP_LIB_DIR "${CMAKE_BINARY_DIR}/zig_cpp")
|
||||
|
||||
if(ZIG_FORCE_EXTERNAL_LLD)
|
||||
|
@ -413,18 +430,24 @@ set(ZIG_STD_FILES
|
|||
"crypto/sha2.zig"
|
||||
"crypto/sha3.zig"
|
||||
"crypto/blake2.zig"
|
||||
"crypto/hmac.zig"
|
||||
"cstr.zig"
|
||||
"debug/failing_allocator.zig"
|
||||
"debug/index.zig"
|
||||
"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"
|
||||
|
@ -485,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"
|
||||
|
@ -493,7 +515,7 @@ set(ZIG_STD_FILES
|
|||
"os/windows/index.zig"
|
||||
"os/windows/util.zig"
|
||||
"os/zen.zig"
|
||||
"rand.zig"
|
||||
"rand/index.zig"
|
||||
"sort.zig"
|
||||
"special/bootstrap.zig"
|
||||
"special/bootstrap_lib.zig"
|
||||
|
@ -682,6 +704,8 @@ if(MINGW)
|
|||
set(EXE_LDFLAGS "-static -static-libgcc -static-libstdc++")
|
||||
elseif(MSVC)
|
||||
set(EXE_LDFLAGS "/STACK:16777216")
|
||||
elseif(ZIG_STATIC)
|
||||
set(EXE_LDFLAGS "-static")
|
||||
else()
|
||||
set(EXE_LDFLAGS " ")
|
||||
endif()
|
||||
|
@ -710,7 +734,7 @@ target_link_libraries(zig LINK_PUBLIC
|
|||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
if(NOT MSVC)
|
||||
target_link_libraries(zig LINK_PUBLIC xml2)
|
||||
target_link_libraries(zig LINK_PUBLIC ${LIBXML2})
|
||||
endif()
|
||||
if(ZIG_DIA_GUIDS_LIB)
|
||||
target_link_libraries(zig LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB})
|
||||
|
|
14
README.md
14
README.md
|
@ -138,31 +138,25 @@ libc. Create demo games using Zig.
|
|||
|
||||
##### POSIX
|
||||
|
||||
If you have gcc or clang installed, you can find out what `ZIG_LIBC_LIB_DIR`,
|
||||
`ZIG_LIBC_STATIC_LIB_DIR`, and `ZIG_LIBC_INCLUDE_DIR` should be set to
|
||||
(example below).
|
||||
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_LIB_DIR=$(dirname $(cc -print-file-name=crt1.o)) -DZIG_LIBC_INCLUDE_DIR=$(echo -n | cc -E -x c - -v 2>&1 | grep -B1 "End of search list." | head -n1 | cut -c 2- | sed "s/ .*//") -DZIG_LIBC_STATIC_LIB_DIR=$(dirname $(cc -print-file-name=crtbegin.o))
|
||||
cmake ..
|
||||
make
|
||||
make install
|
||||
./zig build --build-file ../build.zig test
|
||||
bin/zig build --build-file ../build.zig test
|
||||
```
|
||||
|
||||
##### MacOS
|
||||
|
||||
`ZIG_LIBC_LIB_DIR` and `ZIG_LIBC_STATIC_LIB_DIR` are unused.
|
||||
|
||||
```
|
||||
brew install cmake llvm@6
|
||||
brew outdated llvm@6 || brew upgrade llvm@6
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_PREFIX_PATH=/usr/local/opt/llvm@6/ -DCMAKE_INSTALL_PREFIX=$(pwd)
|
||||
cmake .. -DCMAKE_PREFIX_PATH=/usr/local/opt/llvm@6/
|
||||
make install
|
||||
./zig build --build-file ../build.zig test
|
||||
bin/zig build --build-file ../build.zig test
|
||||
```
|
||||
|
||||
##### Windows
|
||||
|
|
13
build.zig
13
build.zig
|
@ -45,6 +45,11 @@ pub fn build(b: &Builder) !void {
|
|||
|
||||
var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
|
||||
exe.setBuildMode(mode);
|
||||
|
||||
// This is for finding /lib/libz.a on alpine linux.
|
||||
// TODO turn this into -Dextra-lib-path=/lib option
|
||||
exe.addLibPath("/lib");
|
||||
|
||||
exe.addIncludeDir("src");
|
||||
exe.addIncludeDir(cmake_binary_dir);
|
||||
addCppLib(b, exe, cmake_binary_dir, "zig_cpp");
|
||||
|
@ -64,6 +69,14 @@ pub fn build(b: &Builder) !void {
|
|||
if (exe.target.getOs() == builtin.Os.linux) {
|
||||
const libstdcxx_path_padded = try b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"});
|
||||
const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next();
|
||||
if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) {
|
||||
warn(
|
||||
\\Unable to determine path to libstdc++.a
|
||||
\\On Fedora, install libstdc++-static and try again.
|
||||
\\
|
||||
);
|
||||
return error.RequiredLibraryNotFound;
|
||||
}
|
||||
exe.addObjectFile(libstdcxx_path);
|
||||
|
||||
exe.linkSystemLibrary("pthread");
|
||||
|
|
|
@ -20,9 +20,7 @@ call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_
|
|||
|
||||
mkdir %ZIGBUILDDIR%
|
||||
cd %ZIGBUILDDIR%
|
||||
cmake.exe .. -Thost=x64 -G"Visual Studio 14 2015 Win64" "-DCMAKE_INSTALL_PREFIX=%ZIGBUILDDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release "-DZIG_LIBC_INCLUDE_DIR=C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt" "-DZIG_LIBC_LIB_DIR=C:\Program Files (x86)\Windows Kits\10\bin\x64\ucrt" "-DZIG_LIBC_STATIC_LIB_DIR=C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64" || exit /b
|
||||
cmake.exe .. -Thost=x64 -G"Visual Studio 14 2015 Win64" "-DCMAKE_INSTALL_PREFIX=%ZIGBUILDDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release || exit /b
|
||||
msbuild /p:Configuration=Release INSTALL.vcxproj || exit /b
|
||||
|
||||
bin\zig.exe build --build-file ..\build.zig test || exit /b
|
||||
|
||||
@echo "MSVC build succeeded"
|
||||
|
|
|
@ -4,4 +4,4 @@ set -x
|
|||
|
||||
sudo apt-get remove -y llvm-*
|
||||
sudo rm -rf /usr/local/*
|
||||
sudo apt-get install -y clang-6.0 libclang-6.0 libclang-6.0-dev llvm-6.0 llvm-6.0-dev liblld-6.0 liblld-6.0-dev cmake wine1.6-amd64
|
||||
sudo apt-get install -y clang-6.0 libclang-6.0 libclang-6.0-dev llvm-6.0 llvm-6.0-dev liblld-6.0 liblld-6.0-dev cmake wine1.6-amd64 s3cmd
|
||||
|
|
|
@ -8,25 +8,16 @@ export CXX=clang++-6.0
|
|||
echo $PATH
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_LIB_DIR=$(dirname $($CC -print-file-name=crt1.o)) -DZIG_LIBC_INCLUDE_DIR=$(echo -n | $CC -E -x c - -v 2>&1 | grep -B1 "End of search list." | head -n1 | cut -c 2- | sed "s/ .*//") -DZIG_LIBC_STATIC_LIB_DIR=$(dirname $($CC -print-file-name=crtbegin.o))
|
||||
make VERBOSE=1
|
||||
make install
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd)
|
||||
make -j2 install
|
||||
./zig build --build-file ../build.zig test
|
||||
|
||||
./zig test ../test/behavior.zig --target-os windows --target-arch i386 --target-environ msvc
|
||||
wine zig-cache/test.exe
|
||||
|
||||
./zig test ../test/behavior.zig --target-os windows --target-arch i386 --target-environ msvc --release-fast
|
||||
wine zig-cache/test.exe
|
||||
|
||||
./zig test ../test/behavior.zig --target-os windows --target-arch i386 --target-environ msvc --release-safe
|
||||
wine zig-cache/test.exe
|
||||
|
||||
./zig test ../test/behavior.zig --target-os windows --target-arch x86_64 --target-environ msvc
|
||||
wine64 zig-cache/test.exe
|
||||
|
||||
#./zig test ../test/behavior.zig --target-os windows --target-arch x86_64 --target-environ msvc --release-fast
|
||||
#wine64 test.exe
|
||||
#
|
||||
#./zig test ../test/behavior.zig --target-os windows --target-arch x86_64 --target-environ msvc --release-safe
|
||||
#wine64 test.exe
|
||||
if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then
|
||||
mkdir $TRAVIS_BUILD_DIR/artifacts
|
||||
docker run -it --mount type=bind,source="$TRAVIS_BUILD_DIR/artifacts",target=/z ziglang/static-base:llvm6-1 -j2 $TRAVIS_COMMIT
|
||||
echo "access_key = $AWS_ACCESS_KEY_ID" >> ~/.s3cfg
|
||||
echo "secret_key = $AWS_SECRET_ACCESS_KEY" >> ~/.s3cfg
|
||||
s3cmd put -P $TRAVIS_BUILD_DIR/artifacts/* s3://ziglang.org/builds/
|
||||
touch empty
|
||||
s3cmd put -P empty s3://ziglang.org/builds/zig-linux-x86_64-$TRAVIS_BRANCH.tar.xz --add-header=x-amz-website-redirect-location:/builds/$(ls $TRAVIS_BUILD_DIR/artifacts)
|
||||
fi
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
# CLANG_FOUND
|
||||
# CLANG_INCLUDE_DIRS
|
||||
# CLANG_LIBRARIES
|
||||
# CLANG_LIBDIRS
|
||||
|
||||
if(MSVC)
|
||||
find_package(CLANG REQUIRED CONFIG)
|
||||
|
@ -34,6 +35,7 @@ else()
|
|||
string(TOUPPER ${_libname_} _prettylibname_)
|
||||
find_library(CLANG_${_prettylibname_}_LIB NAMES ${_libname_}
|
||||
PATHS
|
||||
${CLANG_LIBDIRS}
|
||||
/usr/lib/llvm/6/lib
|
||||
/usr/lib/llvm-6.0/lib
|
||||
/mingw64/lib
|
||||
|
@ -60,4 +62,4 @@ endif()
|
|||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(CLANG DEFAULT_MSG CLANG_LIBRARIES CLANG_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(CLANG_INCLUDE_DIRS CLANG_LIBRARIES)
|
||||
mark_as_advanced(CLANG_INCLUDE_DIRS CLANG_LIBRARIES CLANG_LIBDIRS)
|
||||
|
|
|
@ -15,7 +15,7 @@ find_program(LLVM_CONFIG_EXE
|
|||
"c:/msys64/mingw64/bin"
|
||||
"C:/Libraries/llvm-6.0.0/bin")
|
||||
|
||||
if(NOT(CMAKE_BUILD_TYPE STREQUAL "Debug"))
|
||||
if(NOT(CMAKE_BUILD_TYPE STREQUAL "Debug") OR ZIG_STATIC)
|
||||
execute_process(
|
||||
COMMAND ${LLVM_CONFIG_EXE} --libfiles --link-static
|
||||
OUTPUT_VARIABLE LLVM_LIBRARIES_SPACES
|
||||
|
@ -66,7 +66,7 @@ if(NOT LLVM_LIBRARIES)
|
|||
endif()
|
||||
|
||||
link_directories("${CMAKE_PREFIX_PATH}/lib")
|
||||
|
||||
link_directories("${LLVM_LIBDIRS}")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(LLVM DEFAULT_MSG LLVM_LIBRARIES LLVM_INCLUDE_DIRS)
|
||||
|
|
|
@ -55,7 +55,7 @@ pub fn main() !void {
|
|||
// TODO issue #709
|
||||
// disabled to pass CI tests, but obviously we want to implement this
|
||||
// and then remove this workaround
|
||||
if (builtin.os == builtin.Os.linux) {
|
||||
if (builtin.os != builtin.Os.windows) {
|
||||
os.deleteTree(allocator, tmp_dir_name) catch {};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -2864,18 +2900,18 @@ const err = (error {FileNotFound}).FileNotFound;
|
|||
assert to make sure the error value is in fact in the destination error set.
|
||||
</p>
|
||||
<p>
|
||||
The global error set should generally be avoided when possible, because it prevents
|
||||
the compiler from knowing what errors are possible at compile-time. Knowing
|
||||
the error set at compile-time is better for generated documentationt and for
|
||||
helpful error messages such as forgetting a possible error value in a {#link|switch#}.
|
||||
The global error set should generally be avoided because it prevents the
|
||||
compiler from knowing what errors are possible at compile-time. Knowing
|
||||
the error set at compile-time is better for generated documentation and
|
||||
helpful error messages, such as forgetting a possible error value in a {#link|switch#}.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_close#}
|
||||
{#header_open|Error Union Type#}
|
||||
<p>
|
||||
Most of the time you will not find yourself using an error set type. Instead,
|
||||
likely you will be using the error union type. This is when you take an error set
|
||||
and a normal type, and create an error union with the <code>!</code> binary operator.
|
||||
An error set type and normal type can be combined with the <code>!</code>
|
||||
binary operator to form an error union type. You are likely to use an
|
||||
error union type more often than an error set type by itself.
|
||||
</p>
|
||||
<p>
|
||||
Here is a function to parse a string into a 64-bit integer:
|
||||
|
@ -5733,13 +5769,13 @@ VariableDeclaration = ("var" | "const") Symbol option(":" TypeExpr) option("alig
|
|||
|
||||
ContainerMember = (ContainerField | FnDef | GlobalVarDecl)
|
||||
|
||||
ContainerField = Symbol option(":" PrefixOpExpression option("=" PrefixOpExpression ","
|
||||
ContainerField = Symbol option(":" PrefixOpExpression) option("=" PrefixOpExpression) ","
|
||||
|
||||
UseDecl = "use" Expression ";"
|
||||
|
||||
ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"
|
||||
|
||||
FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("(" Expression ")"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var")
|
||||
FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("<" Expression ">"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var")
|
||||
|
||||
FnDef = option("inline" | "export") FnProto Block
|
||||
|
||||
|
@ -5751,9 +5787,7 @@ Block = option(Symbol ":") "{" many(Statement) "}"
|
|||
|
||||
Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
|
||||
|
||||
TypeExpr = ErrorSetExpr
|
||||
|
||||
ErrorSetExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression
|
||||
TypeExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression
|
||||
|
||||
BlockOrExpression = Block | Expression
|
||||
|
||||
|
@ -5833,7 +5867,7 @@ BinaryAndExpression = BitShiftExpression "&" BinaryAndExpression | BitShiftE
|
|||
|
||||
BitShiftExpression = AdditionExpression BitShiftOperator BitShiftExpression | AdditionExpression
|
||||
|
||||
BitShiftOperator = "<<" | ">>" | "<<"
|
||||
BitShiftOperator = "<<" | ">>"
|
||||
|
||||
AdditionExpression = MultiplyExpression AdditionOperator AdditionExpression | MultiplyExpression
|
||||
|
||||
|
@ -5845,9 +5879,9 @@ CurlySuffixExpression = TypeExpr option(ContainerInitExpression)
|
|||
|
||||
MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
|
||||
|
||||
PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression
|
||||
PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression
|
||||
|
||||
SuffixOpExpression = ("async" option("(" Expression ")") PrimaryExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
|
||||
SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
|
||||
|
||||
FieldAccessExpression = "." Symbol
|
||||
|
||||
|
@ -5865,7 +5899,9 @@ StructLiteralField = "." Symbol "=" Expression
|
|||
|
||||
PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await"
|
||||
|
||||
PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl
|
||||
PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType
|
||||
|
||||
PromiseType = "promise" option("->" TypeExpr)
|
||||
|
||||
ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr
|
||||
|
||||
|
@ -6033,4 +6069,3 @@ hljs.registerLanguage("zig", function(t) {
|
|||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ const builtin = @import("builtin");
|
|||
const std = @import("std");
|
||||
const io = std.io;
|
||||
const fmt = std.fmt;
|
||||
const Rand = std.rand.Rand;
|
||||
const os = std.os;
|
||||
|
||||
pub fn main() !void {
|
||||
|
@ -10,30 +9,31 @@ pub fn main() !void {
|
|||
var stdout_file_stream = io.FileOutStream.init(&stdout_file);
|
||||
const stdout = &stdout_file_stream.stream;
|
||||
|
||||
var stdin_file = try io.getStdIn();
|
||||
|
||||
try stdout.print("Welcome to the Guess Number Game in Zig.\n");
|
||||
|
||||
var seed_bytes: [@sizeOf(usize)]u8 = undefined;
|
||||
var seed_bytes: [@sizeOf(u64)]u8 = undefined;
|
||||
os.getRandomBytes(seed_bytes[0..]) catch |err| {
|
||||
std.debug.warn("unable to seed random number generator: {}", err);
|
||||
return err;
|
||||
};
|
||||
const seed = std.mem.readInt(seed_bytes, usize, builtin.Endian.Big);
|
||||
var rand = Rand.init(seed);
|
||||
const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);
|
||||
var prng = std.rand.DefaultPrng.init(seed);
|
||||
|
||||
const answer = rand.range(u8, 0, 100) + 1;
|
||||
const answer = prng.random.range(u8, 0, 100) + 1;
|
||||
|
||||
while (true) {
|
||||
try stdout.print("\nGuess a number between 1 and 100: ");
|
||||
var line_buf : [20]u8 = undefined;
|
||||
|
||||
const line_len = stdin_file.read(line_buf[0..]) catch |err| {
|
||||
try stdout.print("Unable to read from stdin: {}\n", @errorName(err));
|
||||
return err;
|
||||
const line_len = io.readLine(line_buf[0..]) catch |err| switch (err) {
|
||||
error.InputTooLong => {
|
||||
try stdout.print("Input too long.\n");
|
||||
continue;
|
||||
},
|
||||
error.EndOfFile, error.StdInUnavailable => return err,
|
||||
};
|
||||
|
||||
const guess = fmt.parseUnsigned(u8, line_buf[0..line_len - 1], 10) catch {
|
||||
const guess = fmt.parseUnsigned(u8, line_buf[0..line_len], 10) catch {
|
||||
try stdout.print("Invalid number.\n");
|
||||
continue;
|
||||
};
|
||||
|
|
|
@ -41,6 +41,10 @@ pub fn main() !void {
|
|||
const args = try os.argsAlloc(allocator);
|
||||
defer os.argsFree(allocator, args);
|
||||
|
||||
if (args.len >= 2 and mem.eql(u8, args[1], "build")) {
|
||||
return buildMain(allocator, args[2..]);
|
||||
}
|
||||
|
||||
if (args.len >= 2 and mem.eql(u8, args[1], "fmt")) {
|
||||
return fmtMain(allocator, args[2..]);
|
||||
}
|
||||
|
@ -560,6 +564,161 @@ fn printZen() !void {
|
|||
);
|
||||
}
|
||||
|
||||
fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void {
|
||||
var build_file: [] const u8 = "build.zig";
|
||||
var cache_dir: ?[] const u8 = null;
|
||||
var zig_install_prefix: ?[] const u8 = null;
|
||||
var asked_for_help = false;
|
||||
var asked_for_init = false;
|
||||
|
||||
var args = ArrayList([] const u8).init(allocator);
|
||||
defer args.deinit();
|
||||
|
||||
var zig_exe_path = try os.selfExePath(allocator);
|
||||
defer allocator.free(zig_exe_path);
|
||||
|
||||
try args.append(""); // Placeholder for zig-cache/build
|
||||
try args.append(""); // Placeholder for zig_exe_path
|
||||
try args.append(""); // Placeholder for build_file_dirname
|
||||
try args.append(""); // Placeholder for full_cache_dir
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < argv.len) : (i += 1) {
|
||||
var arg = argv[i];
|
||||
if (mem.eql(u8, arg, "--help")) {
|
||||
asked_for_help = true;
|
||||
try args.append(argv[i]);
|
||||
} else if (mem.eql(u8, arg, "--init")) {
|
||||
asked_for_init = true;
|
||||
try args.append(argv[i]);
|
||||
} else if (i + 1 < argv.len and mem.eql(u8, arg, "--build-file")) {
|
||||
build_file = argv[i + 1];
|
||||
i += 1;
|
||||
} else if (i + 1 < argv.len and mem.eql(u8, arg, "--cache-dir")) {
|
||||
cache_dir = argv[i + 1];
|
||||
i += 1;
|
||||
} else if (i + 1 < argv.len and mem.eql(u8, arg, "--zig-install-prefix")) {
|
||||
try args.append(arg);
|
||||
i += 1;
|
||||
zig_install_prefix = argv[i];
|
||||
try args.append(argv[i]);
|
||||
} else {
|
||||
try args.append(arg);
|
||||
}
|
||||
}
|
||||
|
||||
const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix);
|
||||
defer allocator.free(zig_lib_dir);
|
||||
|
||||
const zig_std_dir = try os.path.join(allocator, zig_lib_dir, "std");
|
||||
defer allocator.free(zig_std_dir);
|
||||
|
||||
const special_dir = try os.path.join(allocator, zig_std_dir, "special");
|
||||
defer allocator.free(special_dir);
|
||||
|
||||
const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");
|
||||
defer allocator.free(build_runner_path);
|
||||
|
||||
// g = codegen_create(build_runner_path, ...)
|
||||
// codegen_set_out_name(g, "build")
|
||||
|
||||
const build_file_abs = try os.path.resolve(allocator, ".", build_file);
|
||||
defer allocator.free(build_file_abs);
|
||||
|
||||
const build_file_basename = os.path.basename(build_file_abs);
|
||||
const build_file_dirname = os.path.dirname(build_file_abs);
|
||||
|
||||
var full_cache_dir: []u8 = undefined;
|
||||
if (cache_dir == null) {
|
||||
full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache");
|
||||
} else {
|
||||
full_cache_dir = try os.path.resolve(allocator, ".", ??cache_dir, full_cache_dir);
|
||||
}
|
||||
defer allocator.free(full_cache_dir);
|
||||
|
||||
const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build");
|
||||
defer allocator.free(path_to_build_exe);
|
||||
// codegen_set_cache_dir(g, full_cache_dir)
|
||||
|
||||
args.items[0] = path_to_build_exe;
|
||||
args.items[1] = zig_exe_path;
|
||||
args.items[2] = build_file_dirname;
|
||||
args.items[3] = full_cache_dir;
|
||||
|
||||
var build_file_exists: bool = undefined;
|
||||
if (os.File.openRead(allocator, build_file_abs)) |*file| {
|
||||
file.close();
|
||||
build_file_exists = true;
|
||||
} else |_| {
|
||||
build_file_exists = false;
|
||||
}
|
||||
|
||||
if (!build_file_exists and asked_for_help) {
|
||||
// TODO(bnoordhuis) Print help message from std/special/build_runner.zig
|
||||
return;
|
||||
}
|
||||
|
||||
if (!build_file_exists and asked_for_init) {
|
||||
const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig");
|
||||
defer allocator.free(build_template_path);
|
||||
|
||||
var srcfile = try os.File.openRead(allocator, build_template_path);
|
||||
defer srcfile.close();
|
||||
|
||||
var dstfile = try os.File.openWrite(allocator, build_file_abs);
|
||||
defer dstfile.close();
|
||||
|
||||
while (true) {
|
||||
var buffer: [4096]u8 = undefined;
|
||||
const n = try srcfile.read(buffer[0..]);
|
||||
if (n == 0) break;
|
||||
try dstfile.write(buffer[0..n]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!build_file_exists) {
|
||||
warn(
|
||||
\\No 'build.zig' file found.
|
||||
\\Initialize a 'build.zig' template file with `zig build --init`,
|
||||
\\or build an executable directly with `zig build-exe $FILENAME.zig`.
|
||||
\\See: `zig build --help` or `zig help` for more options.
|
||||
\\
|
||||
);
|
||||
os.exit(1);
|
||||
}
|
||||
|
||||
// codegen_build(g)
|
||||
// codegen_link(g, path_to_build_exe)
|
||||
// codegen_destroy(g)
|
||||
|
||||
var proc = try os.ChildProcess.init(args.toSliceConst(), allocator);
|
||||
defer proc.deinit();
|
||||
|
||||
var term = try proc.spawnAndWait();
|
||||
switch (term) {
|
||||
os.ChildProcess.Term.Exited => |status| {
|
||||
if (status != 0) {
|
||||
warn("{} exited with status {}\n", args.at(0), status);
|
||||
os.exit(1);
|
||||
}
|
||||
},
|
||||
os.ChildProcess.Term.Signal => |signal| {
|
||||
warn("{} killed by signal {}\n", args.at(0), signal);
|
||||
os.exit(1);
|
||||
},
|
||||
os.ChildProcess.Term.Stopped => |signal| {
|
||||
warn("{} stopped by signal {}\n", args.at(0), signal);
|
||||
os.exit(1);
|
||||
},
|
||||
os.ChildProcess.Term.Unknown => |status| {
|
||||
warn("{} encountered unknown failure {}\n", args.at(0), status);
|
||||
os.exit(1);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {
|
||||
for (file_paths) |file_path| {
|
||||
var file = try os.File.openRead(allocator, file_path);
|
||||
|
@ -582,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,
|
||||
|
@ -409,6 +408,7 @@ enum NodeType {
|
|||
NodeTypeResume,
|
||||
NodeTypeAwaitExpr,
|
||||
NodeTypeSuspend,
|
||||
NodeTypePromiseType,
|
||||
};
|
||||
|
||||
struct AstNodeRoot {
|
||||
|
@ -452,10 +452,6 @@ struct AstNodeFnDef {
|
|||
AstNode *body;
|
||||
};
|
||||
|
||||
struct AstNodeFnDecl {
|
||||
AstNode *fn_proto;
|
||||
};
|
||||
|
||||
struct AstNodeParamDecl {
|
||||
Buf *name;
|
||||
AstNode *type;
|
||||
|
@ -712,10 +708,6 @@ struct AstNodeSwitchRange {
|
|||
AstNode *end;
|
||||
};
|
||||
|
||||
struct AstNodeLabel {
|
||||
Buf *name;
|
||||
};
|
||||
|
||||
struct AstNodeCompTime {
|
||||
AstNode *expr;
|
||||
};
|
||||
|
@ -879,6 +871,10 @@ struct AstNodeSuspend {
|
|||
AstNode *promise_symbol;
|
||||
};
|
||||
|
||||
struct AstNodePromiseType {
|
||||
AstNode *payload_type; // can be NULL
|
||||
};
|
||||
|
||||
struct AstNode {
|
||||
enum NodeType type;
|
||||
size_t line;
|
||||
|
@ -887,7 +883,6 @@ struct AstNode {
|
|||
union {
|
||||
AstNodeRoot root;
|
||||
AstNodeFnDef fn_def;
|
||||
AstNodeFnDecl fn_decl;
|
||||
AstNodeFnProto fn_proto;
|
||||
AstNodeParamDecl param_decl;
|
||||
AstNodeBlock block;
|
||||
|
@ -912,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;
|
||||
|
@ -939,6 +933,7 @@ struct AstNode {
|
|||
AstNodeResumeExpr resume_expr;
|
||||
AstNodeAwaitExpr await_expr;
|
||||
AstNodeSuspend suspend;
|
||||
AstNodePromiseType promise_type;
|
||||
} data;
|
||||
};
|
||||
|
||||
|
@ -1251,7 +1246,10 @@ struct FnTableEntry {
|
|||
ScopeBlock *def_scope; // parent is child_scope
|
||||
Buf symbol_name;
|
||||
TypeTableEntry *type_entry; // function type
|
||||
TypeTableEntry *implicit_return_type;
|
||||
// in the case of normal functions this is the implicit return type
|
||||
// in the case of async functions this is the implicit return type according to the
|
||||
// zig source code, not according to zig ir
|
||||
TypeTableEntry *src_implicit_return_type;
|
||||
bool is_test;
|
||||
FnInline fn_inline;
|
||||
FnAnalState anal_state;
|
||||
|
@ -1612,7 +1610,8 @@ struct CodeGen {
|
|||
FnTableEntry *panic_fn;
|
||||
LLVMValueRef cur_ret_ptr;
|
||||
LLVMValueRef cur_fn_val;
|
||||
LLVMValueRef cur_err_ret_trace_val;
|
||||
LLVMValueRef cur_err_ret_trace_val_arg;
|
||||
LLVMValueRef cur_err_ret_trace_val_stack;
|
||||
bool c_want_stdint;
|
||||
bool c_want_stdbool;
|
||||
AstNode *root_export_decl;
|
||||
|
@ -1646,6 +1645,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;
|
||||
|
@ -1751,6 +1752,7 @@ enum ScopeId {
|
|||
ScopeIdLoop,
|
||||
ScopeIdFnDef,
|
||||
ScopeIdCompTime,
|
||||
ScopeIdCoroPrelude,
|
||||
};
|
||||
|
||||
struct Scope {
|
||||
|
@ -1858,6 +1860,12 @@ struct ScopeFnDef {
|
|||
FnTableEntry *fn_entry;
|
||||
};
|
||||
|
||||
// This scope is created to indicate that the code in the scope
|
||||
// is auto-generated coroutine prelude stuff.
|
||||
struct ScopeCoroPrelude {
|
||||
Scope base;
|
||||
};
|
||||
|
||||
// synchronized with code in define_builtin_compile_vars
|
||||
enum AtomicOrder {
|
||||
AtomicOrderUnordered,
|
||||
|
@ -1944,6 +1952,7 @@ enum IrInstructionId {
|
|||
IrInstructionIdSetRuntimeSafety,
|
||||
IrInstructionIdSetFloatMode,
|
||||
IrInstructionIdArrayType,
|
||||
IrInstructionIdPromiseType,
|
||||
IrInstructionIdSliceType,
|
||||
IrInstructionIdAsm,
|
||||
IrInstructionIdSizeOf,
|
||||
|
@ -2034,6 +2043,10 @@ enum IrInstructionId {
|
|||
IrInstructionIdAtomicRmw,
|
||||
IrInstructionIdPromiseResultType,
|
||||
IrInstructionIdAwaitBookkeeping,
|
||||
IrInstructionIdSaveErrRetAddr,
|
||||
IrInstructionIdAddImplicitReturnType,
|
||||
IrInstructionIdMergeErrRetTraces,
|
||||
IrInstructionIdMarkErrRetTracePtr,
|
||||
};
|
||||
|
||||
struct IrInstruction {
|
||||
|
@ -2360,6 +2373,12 @@ struct IrInstructionArrayType {
|
|||
IrInstruction *child_type;
|
||||
};
|
||||
|
||||
struct IrInstructionPromiseType {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *payload_type;
|
||||
};
|
||||
|
||||
struct IrInstructionSliceType {
|
||||
IrInstruction base;
|
||||
|
||||
|
@ -2673,6 +2692,7 @@ struct IrInstructionFnProto {
|
|||
IrInstruction **param_types;
|
||||
IrInstruction *align_value;
|
||||
IrInstruction *return_type;
|
||||
IrInstruction *async_allocator_type_value;
|
||||
bool is_var_args;
|
||||
};
|
||||
|
||||
|
@ -2865,6 +2885,11 @@ struct IrInstructionExport {
|
|||
|
||||
struct IrInstructionErrorReturnTrace {
|
||||
IrInstruction base;
|
||||
|
||||
enum Nullable {
|
||||
Null,
|
||||
NonNull,
|
||||
} nullable;
|
||||
};
|
||||
|
||||
struct IrInstructionErrorUnion {
|
||||
|
@ -2987,6 +3012,30 @@ struct IrInstructionAwaitBookkeeping {
|
|||
IrInstruction *promise_result_type;
|
||||
};
|
||||
|
||||
struct IrInstructionSaveErrRetAddr {
|
||||
IrInstruction base;
|
||||
};
|
||||
|
||||
struct IrInstructionAddImplicitReturnType {
|
||||
IrInstruction base;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -2996,10 +3045,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"
|
||||
|
||||
|
||||
|
|
169
src/analyze.cpp
169
src/analyze.cpp
|
@ -170,6 +170,12 @@ Scope *create_comptime_scope(AstNode *node, Scope *parent) {
|
|||
return &scope->base;
|
||||
}
|
||||
|
||||
Scope *create_coro_prelude_scope(AstNode *node, Scope *parent) {
|
||||
ScopeCoroPrelude *scope = allocate<ScopeCoroPrelude>(1);
|
||||
init_scope(&scope->base, ScopeIdCoroPrelude, node, parent);
|
||||
return &scope->base;
|
||||
}
|
||||
|
||||
ImportTableEntry *get_scope_import(Scope *scope) {
|
||||
while (scope) {
|
||||
if (scope->id == ScopeIdDecls) {
|
||||
|
@ -462,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;
|
||||
|
@ -985,7 +1011,8 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
|
|||
// populate the name of the type
|
||||
buf_resize(&fn_type->name, 0);
|
||||
if (fn_type->data.fn.fn_type_id.cc == CallingConventionAsync) {
|
||||
buf_appendf(&fn_type->name, "async(%s) ", buf_ptr(&fn_type_id->async_allocator_type->name));
|
||||
assert(fn_type_id->async_allocator_type != nullptr);
|
||||
buf_appendf(&fn_type->name, "async<%s> ", buf_ptr(&fn_type_id->async_allocator_type->name));
|
||||
} else {
|
||||
const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc);
|
||||
buf_appendf(&fn_type->name, "%s", cc_str);
|
||||
|
@ -3209,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:
|
||||
|
@ -3253,6 +3279,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
|
|||
case NodeTypeResume:
|
||||
case NodeTypeAwaitExpr:
|
||||
case NodeTypeSuspend:
|
||||
case NodeTypePromiseType:
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
@ -3590,6 +3617,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) {
|
|||
case ScopeIdCImport:
|
||||
case ScopeIdLoop:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdCoroPrelude:
|
||||
scope = scope->parent;
|
||||
continue;
|
||||
case ScopeIdFnDef:
|
||||
|
@ -3864,7 +3892,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ
|
|||
|
||||
TypeTableEntry *block_return_type = ir_analyze(g, &fn_table_entry->ir_executable,
|
||||
&fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node);
|
||||
fn_table_entry->implicit_return_type = block_return_type;
|
||||
fn_table_entry->src_implicit_return_type = block_return_type;
|
||||
|
||||
if (type_is_invalid(block_return_type) || fn_table_entry->analyzed_executable.invalid) {
|
||||
assert(g->errors.length > 0);
|
||||
|
@ -3876,10 +3904,10 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ
|
|||
TypeTableEntry *return_err_set_type = fn_type_id->return_type->data.error_union.err_set_type;
|
||||
if (return_err_set_type->data.error_set.infer_fn != nullptr) {
|
||||
TypeTableEntry *inferred_err_set_type;
|
||||
if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorSet) {
|
||||
inferred_err_set_type = fn_table_entry->implicit_return_type;
|
||||
} else if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorUnion) {
|
||||
inferred_err_set_type = fn_table_entry->implicit_return_type->data.error_union.err_set_type;
|
||||
if (fn_table_entry->src_implicit_return_type->id == TypeTableEntryIdErrorSet) {
|
||||
inferred_err_set_type = fn_table_entry->src_implicit_return_type;
|
||||
} else if (fn_table_entry->src_implicit_return_type->id == TypeTableEntryIdErrorUnion) {
|
||||
inferred_err_set_type = fn_table_entry->src_implicit_return_type->data.error_union.err_set_type;
|
||||
} else {
|
||||
add_node_error(g, return_type_node,
|
||||
buf_sprintf("function with inferred error set must return at least one possible error"));
|
||||
|
@ -4276,26 +4304,118 @@ static ZigWindowsSDK *get_windows_sdk(CodeGen *g) {
|
|||
return g->win_sdk;
|
||||
}
|
||||
|
||||
|
||||
Buf *get_linux_libc_lib_path(const char *o_file) {
|
||||
const char *cc_exe = getenv("CC");
|
||||
cc_exe = (cc_exe == nullptr) ? "cc" : cc_exe;
|
||||
ZigList<const char *> args = {};
|
||||
args.append(buf_ptr(buf_sprintf("-print-file-name=%s", o_file)));
|
||||
Termination term;
|
||||
Buf *out_stderr = buf_alloc();
|
||||
Buf *out_stdout = buf_alloc();
|
||||
int err;
|
||||
if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) {
|
||||
zig_panic("unable to determine libc lib path: executing C compiler: %s", err_str(err));
|
||||
}
|
||||
if (term.how != TerminationIdClean || term.code != 0) {
|
||||
zig_panic("unable to determine libc lib path: executing C compiler command failed");
|
||||
}
|
||||
if (buf_ends_with_str(out_stdout, "\n")) {
|
||||
buf_resize(out_stdout, buf_len(out_stdout) - 1);
|
||||
}
|
||||
if (buf_len(out_stdout) == 0 || buf_eql_str(out_stdout, o_file)) {
|
||||
zig_panic("unable to determine libc lib path: C compiler could not find %s", o_file);
|
||||
}
|
||||
Buf *result = buf_alloc();
|
||||
os_path_dirname(out_stdout, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Buf *get_linux_libc_include_path(void) {
|
||||
const char *cc_exe = getenv("CC");
|
||||
cc_exe = (cc_exe == nullptr) ? "cc" : cc_exe;
|
||||
ZigList<const char *> args = {};
|
||||
args.append("-E");
|
||||
args.append("-Wp,-v");
|
||||
args.append("-xc");
|
||||
args.append("/dev/null");
|
||||
Termination term;
|
||||
Buf *out_stderr = buf_alloc();
|
||||
Buf *out_stdout = buf_alloc();
|
||||
int err;
|
||||
if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) {
|
||||
zig_panic("unable to determine libc include path: executing C compiler: %s", err_str(err));
|
||||
}
|
||||
if (term.how != TerminationIdClean || term.code != 0) {
|
||||
zig_panic("unable to determine libc include path: executing C compiler command failed");
|
||||
}
|
||||
char *prev_newline = buf_ptr(out_stderr);
|
||||
ZigList<const char *> search_paths = {};
|
||||
bool found_search_paths = false;
|
||||
for (;;) {
|
||||
char *newline = strchr(prev_newline, '\n');
|
||||
if (newline == nullptr) {
|
||||
zig_panic("unable to determine libc include path: bad output from C compiler command");
|
||||
}
|
||||
*newline = 0;
|
||||
if (found_search_paths) {
|
||||
if (strcmp(prev_newline, "End of search list.") == 0) {
|
||||
break;
|
||||
}
|
||||
search_paths.append(prev_newline);
|
||||
} else {
|
||||
if (strcmp(prev_newline, "#include <...> search starts here:") == 0) {
|
||||
found_search_paths = true;
|
||||
}
|
||||
}
|
||||
prev_newline = newline + 1;
|
||||
}
|
||||
if (search_paths.length == 0) {
|
||||
zig_panic("unable to determine libc include path: even C compiler does not know where libc headers are");
|
||||
}
|
||||
for (size_t i = 0; i < search_paths.length; i += 1) {
|
||||
// search in reverse order
|
||||
const char *search_path = search_paths.items[search_paths.length - i - 1];
|
||||
// cut off spaces
|
||||
while (*search_path == ' ') {
|
||||
search_path += 1;
|
||||
}
|
||||
Buf *stdlib_path = buf_sprintf("%s/stdlib.h", search_path);
|
||||
bool exists;
|
||||
if ((err = os_file_exists(stdlib_path, &exists))) {
|
||||
exists = false;
|
||||
}
|
||||
if (exists) {
|
||||
return buf_create_from_str(search_path);
|
||||
}
|
||||
}
|
||||
zig_panic("unable to determine libc include path: stdlib.h not found in C compiler search paths");
|
||||
}
|
||||
|
||||
void find_libc_include_path(CodeGen *g) {
|
||||
if (!g->libc_include_dir || buf_len(g->libc_include_dir) == 0) {
|
||||
ZigWindowsSDK *sdk = get_windows_sdk(g);
|
||||
if (g->libc_include_dir == nullptr) {
|
||||
|
||||
if (g->zig_target.os == OsWindows) {
|
||||
ZigWindowsSDK *sdk = get_windows_sdk(g);
|
||||
g->libc_include_dir = buf_alloc();
|
||||
if (os_get_win32_ucrt_include_path(sdk, g->libc_include_dir)) {
|
||||
zig_panic("Unable to determine libc include path.");
|
||||
}
|
||||
} else if (g->zig_target.os == OsLinux) {
|
||||
g->libc_include_dir = get_linux_libc_include_path();
|
||||
} else if (g->zig_target.os == OsMacOSX) {
|
||||
g->libc_include_dir = buf_create_from_str("/usr/include");
|
||||
} else {
|
||||
// TODO find libc at runtime for other operating systems
|
||||
zig_panic("Unable to determine libc include path.");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO find libc at runtime for other operating systems
|
||||
if(!g->libc_include_dir || buf_len(g->libc_include_dir) == 0) {
|
||||
zig_panic("Unable to determine libc include path.");
|
||||
}
|
||||
assert(buf_len(g->libc_include_dir) != 0);
|
||||
}
|
||||
|
||||
void find_libc_lib_path(CodeGen *g) {
|
||||
// later we can handle this better by reporting an error via the normal mechanism
|
||||
if (!g->libc_lib_dir || buf_len(g->libc_lib_dir) == 0 ||
|
||||
if (g->libc_lib_dir == nullptr ||
|
||||
(g->zig_target.os == OsWindows && (g->msvc_lib_dir == nullptr || g->kernel32_lib_dir == nullptr)))
|
||||
{
|
||||
if (g->zig_target.os == OsWindows) {
|
||||
|
@ -4319,18 +4439,25 @@ void find_libc_lib_path(CodeGen *g) {
|
|||
g->msvc_lib_dir = vc_lib_dir;
|
||||
g->libc_lib_dir = ucrt_lib_path;
|
||||
g->kernel32_lib_dir = kern_lib_path;
|
||||
} else if (g->zig_target.os == OsLinux) {
|
||||
g->libc_lib_dir = get_linux_libc_lib_path("crt1.o");
|
||||
} else {
|
||||
zig_panic("Unable to determine libc lib path.");
|
||||
}
|
||||
} else {
|
||||
assert(buf_len(g->libc_lib_dir) != 0);
|
||||
}
|
||||
|
||||
if (!g->libc_static_lib_dir || buf_len(g->libc_static_lib_dir) == 0) {
|
||||
if (g->libc_static_lib_dir == nullptr) {
|
||||
if ((g->zig_target.os == OsWindows) && (g->msvc_lib_dir != NULL)) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
} else if (g->zig_target.os == OsLinux) {
|
||||
g->libc_static_lib_dir = get_linux_libc_lib_path("crtbegin.o");
|
||||
} else {
|
||||
zig_panic("Unable to determine libc static lib path.");
|
||||
}
|
||||
} else {
|
||||
assert(buf_len(g->libc_static_lib_dir) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -107,6 +107,7 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent);
|
|||
ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry);
|
||||
ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import);
|
||||
Scope *create_comptime_scope(AstNode *node, Scope *parent);
|
||||
Scope *create_coro_prelude_scope(AstNode *node, Scope *parent);
|
||||
|
||||
void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str);
|
||||
ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str);
|
||||
|
|
|
@ -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:
|
||||
|
@ -250,6 +248,8 @@ static const char *node_type_str(NodeType node_type) {
|
|||
return "AwaitExpr";
|
||||
case NodeTypeSuspend:
|
||||
return "Suspend";
|
||||
case NodeTypePromiseType:
|
||||
return "PromiseType";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
@ -658,6 +658,15 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
|||
if (node->data.fn_call_expr.is_builtin) {
|
||||
fprintf(ar->f, "@");
|
||||
}
|
||||
if (node->data.fn_call_expr.is_async) {
|
||||
fprintf(ar->f, "async");
|
||||
if (node->data.fn_call_expr.async_allocator != nullptr) {
|
||||
fprintf(ar->f, "<");
|
||||
render_node_extra(ar, node->data.fn_call_expr.async_allocator, true);
|
||||
fprintf(ar->f, ">");
|
||||
}
|
||||
fprintf(ar->f, " ");
|
||||
}
|
||||
AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
|
||||
bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypeAddrOfExpr);
|
||||
render_node_extra(ar, fn_ref_node, grouped);
|
||||
|
@ -772,6 +781,15 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
|||
render_node_ungrouped(ar, node->data.array_type.child_type);
|
||||
break;
|
||||
}
|
||||
case NodeTypePromiseType:
|
||||
{
|
||||
fprintf(ar->f, "promise");
|
||||
if (node->data.promise_type.payload_type != nullptr) {
|
||||
fprintf(ar->f, "->");
|
||||
render_node_grouped(ar, node->data.promise_type.payload_type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeTypeErrorType:
|
||||
fprintf(ar->f, "error");
|
||||
break;
|
||||
|
@ -1023,7 +1041,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
|||
case NodeTypeUnwrapErrorExpr:
|
||||
{
|
||||
render_node_ungrouped(ar, node->data.unwrap_err_expr.op1);
|
||||
fprintf(ar->f, " %%%% ");
|
||||
fprintf(ar->f, " catch ");
|
||||
if (node->data.unwrap_err_expr.symbol) {
|
||||
Buf *var_name = node->data.unwrap_err_expr.symbol->data.symbol_expr.symbol;
|
||||
fprintf(ar->f, "|%s| ", buf_ptr(var_name));
|
||||
|
@ -1078,7 +1096,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeTestDecl:
|
||||
case NodeTypeStructField:
|
||||
|
|
458
src/codegen.cpp
458
src/codegen.cpp
|
@ -112,10 +112,10 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
|
|||
// that's for native compilation
|
||||
g->zig_target = *target;
|
||||
resolve_target_object_format(&g->zig_target);
|
||||
g->dynamic_linker = buf_create_from_str("");
|
||||
g->libc_lib_dir = buf_create_from_str("");
|
||||
g->libc_static_lib_dir = buf_create_from_str("");
|
||||
g->libc_include_dir = buf_create_from_str("");
|
||||
g->dynamic_linker = nullptr;
|
||||
g->libc_lib_dir = nullptr;
|
||||
g->libc_static_lib_dir = nullptr;
|
||||
g->libc_include_dir = nullptr;
|
||||
g->msvc_lib_dir = nullptr;
|
||||
g->kernel32_lib_dir = nullptr;
|
||||
g->each_lib_rpath = false;
|
||||
|
@ -123,16 +123,13 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
|
|||
// native compilation, we can rely on the configuration stuff
|
||||
g->is_native_target = true;
|
||||
get_native_target(&g->zig_target);
|
||||
g->dynamic_linker = buf_create_from_str(ZIG_DYNAMIC_LINKER);
|
||||
g->libc_lib_dir = buf_create_from_str(ZIG_LIBC_LIB_DIR);
|
||||
g->libc_static_lib_dir = buf_create_from_str(ZIG_LIBC_STATIC_LIB_DIR);
|
||||
g->libc_include_dir = buf_create_from_str(ZIG_LIBC_INCLUDE_DIR);
|
||||
g->dynamic_linker = nullptr; // find it at runtime
|
||||
g->libc_lib_dir = nullptr; // find it at runtime
|
||||
g->libc_static_lib_dir = nullptr; // find it at runtime
|
||||
g->libc_include_dir = nullptr; // find it at runtime
|
||||
g->msvc_lib_dir = nullptr; // find it at runtime
|
||||
g->kernel32_lib_dir = nullptr; // find it at runtime
|
||||
|
||||
#ifdef ZIG_EACH_LIB_RPATH
|
||||
g->each_lib_rpath = true;
|
||||
#endif
|
||||
|
||||
if (g->zig_target.os == OsMacOSX ||
|
||||
g->zig_target.os == OsIOS)
|
||||
|
@ -411,6 +408,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;
|
||||
|
@ -653,6 +653,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
|
|||
case ScopeIdDeferExpr:
|
||||
case ScopeIdLoop:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdCoroPrelude:
|
||||
return get_di_scope(g, scope->parent);
|
||||
}
|
||||
zig_unreachable();
|
||||
|
@ -1116,6 +1117,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;
|
||||
|
@ -1142,50 +1344,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);
|
||||
|
@ -1318,9 +1494,34 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
|
|||
return fn_val;
|
||||
}
|
||||
|
||||
static void gen_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
|
||||
static bool is_coro_prelude_scope(Scope *scope) {
|
||||
while (scope != nullptr) {
|
||||
if (scope->id == ScopeIdCoroPrelude) {
|
||||
return true;
|
||||
} else if (scope->id == ScopeIdFnDef) {
|
||||
break;
|
||||
}
|
||||
scope = scope->parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static LLVMValueRef get_cur_err_ret_trace_val(CodeGen *g, Scope *scope) {
|
||||
if (!g->have_err_ret_tracing) {
|
||||
return nullptr;
|
||||
}
|
||||
if (g->cur_fn->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync) {
|
||||
return is_coro_prelude_scope(scope) ? g->cur_err_ret_trace_val_arg : g->cur_err_ret_trace_val_stack;
|
||||
}
|
||||
if (g->cur_err_ret_trace_val_stack != nullptr) {
|
||||
return g->cur_err_ret_trace_val_stack;
|
||||
}
|
||||
return g->cur_err_ret_trace_val_arg;
|
||||
}
|
||||
|
||||
static void gen_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val, Scope *scope) {
|
||||
LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);
|
||||
LLVMValueRef err_ret_trace_val = g->cur_err_ret_trace_val;
|
||||
LLVMValueRef err_ret_trace_val = get_cur_err_ret_trace_val(g, scope);
|
||||
if (err_ret_trace_val == nullptr) {
|
||||
TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
|
||||
err_ret_trace_val = LLVMConstNull(ptr_to_stack_trace_type->type_ref);
|
||||
|
@ -1607,32 +1808,24 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
|
|||
return instruction->llvm_value;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionSaveErrRetAddr *save_err_ret_addr_instruction)
|
||||
{
|
||||
assert(g->have_err_ret_tracing);
|
||||
|
||||
LLVMValueRef return_err_fn = get_return_err_fn(g);
|
||||
LLVMValueRef args[] = {
|
||||
get_cur_err_ret_trace_val(g, save_err_ret_addr_instruction->base.scope),
|
||||
};
|
||||
LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1,
|
||||
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
|
||||
return call_instruction;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) {
|
||||
LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
|
||||
TypeTableEntry *return_type = return_instruction->value->value.type;
|
||||
|
||||
if (g->have_err_ret_tracing) {
|
||||
bool is_err_return = false;
|
||||
if (return_type->id == TypeTableEntryIdErrorUnion) {
|
||||
if (return_instruction->value->value.special == ConstValSpecialStatic) {
|
||||
is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr;
|
||||
} else if (return_instruction->value->value.special == ConstValSpecialRuntime) {
|
||||
is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;
|
||||
// TODO: emit a branch to check if the return value is an error
|
||||
}
|
||||
} else if (return_type->id == TypeTableEntryIdErrorSet) {
|
||||
is_err_return = true;
|
||||
}
|
||||
if (is_err_return) {
|
||||
LLVMValueRef return_err_fn = get_return_err_fn(g);
|
||||
LLVMValueRef args[] = {
|
||||
g->cur_err_ret_trace_val,
|
||||
};
|
||||
LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1,
|
||||
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
|
||||
LLVMSetTailCall(call_instruction, true);
|
||||
}
|
||||
}
|
||||
if (handle_is_ptr(return_type)) {
|
||||
if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) {
|
||||
assert(g->cur_ret_ptr);
|
||||
|
@ -2732,7 +2925,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
|
|||
gen_param_index += 1;
|
||||
}
|
||||
if (prefix_arg_err_ret_stack) {
|
||||
gen_param_values[gen_param_index] = g->cur_err_ret_trace_val;
|
||||
gen_param_values[gen_param_index] = get_cur_err_ret_trace_val(g, instruction->base.scope);
|
||||
gen_param_index += 1;
|
||||
}
|
||||
if (instruction->is_async) {
|
||||
|
@ -3299,11 +3492,12 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I
|
|||
static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionErrorReturnTrace *instruction)
|
||||
{
|
||||
if (g->cur_err_ret_trace_val == nullptr) {
|
||||
LLVMValueRef cur_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope);
|
||||
if (cur_err_ret_trace_val == nullptr) {
|
||||
TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
|
||||
return LLVMConstNull(ptr_to_stack_trace_type->type_ref);
|
||||
}
|
||||
return g->cur_err_ret_trace_val;
|
||||
return cur_err_ret_trace_val;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_cancel(CodeGen *g, IrExecutable *executable, IrInstructionCancel *instruction) {
|
||||
|
@ -3733,7 +3927,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu
|
|||
LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, err_block);
|
||||
gen_safety_crash_for_err(g, err_val);
|
||||
gen_safety_crash_for_err(g, err_val, instruction->base.scope);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
}
|
||||
|
@ -3925,7 +4119,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec
|
|||
}
|
||||
|
||||
static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) {
|
||||
gen_panic(g, ir_llvm_value(g, instruction->msg), g->cur_err_ret_trace_val);
|
||||
gen_panic(g, ir_llvm_value(g, instruction->msg), get_cur_err_ret_trace_val(g, instruction->base.scope));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -4187,6 +4381,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;
|
||||
|
@ -4212,6 +4427,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
|||
case IrInstructionIdSetRuntimeSafety:
|
||||
case IrInstructionIdSetFloatMode:
|
||||
case IrInstructionIdArrayType:
|
||||
case IrInstructionIdPromiseType:
|
||||
case IrInstructionIdSliceType:
|
||||
case IrInstructionIdSizeOf:
|
||||
case IrInstructionIdSwitchTarget:
|
||||
|
@ -4252,6 +4468,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
|||
case IrInstructionIdErrorUnion:
|
||||
case IrInstructionIdPromiseResultType:
|
||||
case IrInstructionIdAwaitBookkeeping:
|
||||
case IrInstructionIdAddImplicitReturnType:
|
||||
zig_unreachable();
|
||||
|
||||
case IrInstructionIdReturn:
|
||||
|
@ -4400,6 +4617,12 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
|||
return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction);
|
||||
case IrInstructionIdAtomicRmw:
|
||||
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();
|
||||
}
|
||||
|
@ -5282,39 +5505,23 @@ static void do_code_gen(CodeGen *g) {
|
|||
clear_debug_source_node(g);
|
||||
|
||||
uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
|
||||
if (err_ret_trace_arg_index != UINT32_MAX) {
|
||||
g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index);
|
||||
} else if (g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn) {
|
||||
// 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));
|
||||
g->cur_err_ret_trace_val = 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, (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, (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));
|
||||
bool have_err_ret_trace_arg = err_ret_trace_arg_index != UINT32_MAX;
|
||||
if (have_err_ret_trace_arg) {
|
||||
g->cur_err_ret_trace_val_arg = LLVMGetParam(fn, err_ret_trace_arg_index);
|
||||
} else {
|
||||
g->cur_err_ret_trace_val = nullptr;
|
||||
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;
|
||||
LLVMValueRef err_ret_array_val = nullptr;
|
||||
if (have_err_ret_trace_stack) {
|
||||
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));
|
||||
} else {
|
||||
g->cur_err_ret_trace_val_stack = nullptr;
|
||||
}
|
||||
|
||||
// allocate temporary stack data
|
||||
|
@ -5407,6 +5614,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
|
||||
|
@ -5914,6 +6146,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"
|
||||
|
@ -6178,7 +6412,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),
|
||||
|
@ -6257,7 +6493,7 @@ static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package
|
|||
zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err));
|
||||
}
|
||||
Buf *import_code = buf_alloc();
|
||||
if ((err = os_fetch_file_path(abs_full_path, import_code))) {
|
||||
if ((err = os_fetch_file_path(abs_full_path, import_code, false))) {
|
||||
zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err));
|
||||
}
|
||||
|
||||
|
@ -6345,7 +6581,7 @@ static void gen_root_source(CodeGen *g) {
|
|||
}
|
||||
|
||||
Buf *source_code = buf_alloc();
|
||||
if ((err = os_fetch_file_path(rel_full_path, source_code))) {
|
||||
if ((err = os_fetch_file_path(rel_full_path, source_code, true))) {
|
||||
zig_panic("unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));
|
||||
}
|
||||
|
||||
|
@ -6410,7 +6646,7 @@ static void gen_global_asm(CodeGen *g) {
|
|||
int err;
|
||||
for (size_t i = 0; i < g->assembly_files.length; i += 1) {
|
||||
Buf *asm_file = g->assembly_files.at(i);
|
||||
if ((err = os_fetch_file_path(asm_file, &contents))) {
|
||||
if ((err = os_fetch_file_path(asm_file, &contents, false))) {
|
||||
zig_panic("Unable to read %s: %s", buf_ptr(asm_file), err_str(err));
|
||||
}
|
||||
buf_append_buf(&g->global_asm, &contents);
|
||||
|
@ -6592,6 +6828,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf
|
|||
}
|
||||
}
|
||||
case TypeTableEntryIdStruct:
|
||||
case TypeTableEntryIdOpaque:
|
||||
{
|
||||
buf_init_from_str(out_buf, "struct ");
|
||||
buf_append_buf(out_buf, &type_entry->name);
|
||||
|
@ -6609,11 +6846,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf
|
|||
buf_append_buf(out_buf, &type_entry->name);
|
||||
return;
|
||||
}
|
||||
case TypeTableEntryIdOpaque:
|
||||
{
|
||||
buf_init_from_buf(out_buf, &type_entry->name);
|
||||
return;
|
||||
}
|
||||
case TypeTableEntryIdArray:
|
||||
{
|
||||
TypeTableEntryArray *array_data = &type_entry->data.array;
|
||||
|
|
|
@ -13,14 +13,6 @@
|
|||
#define ZIG_VERSION_PATCH @ZIG_VERSION_PATCH@
|
||||
#define ZIG_VERSION_STRING "@ZIG_VERSION@"
|
||||
|
||||
#define ZIG_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
|
||||
#define ZIG_LIBC_INCLUDE_DIR "@ZIG_LIBC_INCLUDE_DIR_ESCAPED@"
|
||||
#define ZIG_LIBC_LIB_DIR "@ZIG_LIBC_LIB_DIR_ESCAPED@"
|
||||
#define ZIG_LIBC_STATIC_LIB_DIR "@ZIG_LIBC_STATIC_LIB_DIR_ESCAPED@"
|
||||
#define ZIG_DYNAMIC_LINKER "@ZIG_DYNAMIC_LINKER@"
|
||||
|
||||
#cmakedefine ZIG_EACH_LIB_RPATH
|
||||
|
||||
// Only used for running tests before installing.
|
||||
#define ZIG_TEST_DIR "@CMAKE_SOURCE_DIR@/test"
|
||||
|
||||
|
|
490
src/ir.cpp
490
src/ir.cpp
|
@ -34,7 +34,7 @@ struct IrAnalyze {
|
|||
size_t old_bb_index;
|
||||
size_t instruction_index;
|
||||
TypeTableEntry *explicit_return_type;
|
||||
ZigList<IrInstruction *> implicit_return_type_list;
|
||||
ZigList<IrInstruction *> src_implicit_return_type_list;
|
||||
IrBasicBlock *const_predecessor_bb;
|
||||
};
|
||||
|
||||
|
@ -349,6 +349,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) {
|
|||
return IrInstructionIdArrayType;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseType *) {
|
||||
return IrInstructionIdPromiseType;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) {
|
||||
return IrInstructionIdSliceType;
|
||||
}
|
||||
|
@ -713,6 +717,22 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAwaitBookkeeping
|
|||
return IrInstructionIdAwaitBookkeeping;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *) {
|
||||
return IrInstructionIdSaveErrRetAddr;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitReturnType *) {
|
||||
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);
|
||||
|
@ -944,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)
|
||||
{
|
||||
|
@ -1461,6 +1462,17 @@ static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode
|
|||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_promise_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *payload_type)
|
||||
{
|
||||
IrInstructionPromiseType *instruction = ir_build_instruction<IrInstructionPromiseType>(irb, scope, source_node);
|
||||
instruction->payload_type = payload_type;
|
||||
|
||||
if (payload_type != nullptr) ir_ref_instruction(payload_type, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value)
|
||||
{
|
||||
|
@ -2141,12 +2153,14 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc
|
|||
}
|
||||
|
||||
static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type, bool is_var_args)
|
||||
IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type,
|
||||
IrInstruction *async_allocator_type_value, bool is_var_args)
|
||||
{
|
||||
IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node);
|
||||
instruction->param_types = param_types;
|
||||
instruction->align_value = align_value;
|
||||
instruction->return_type = return_type;
|
||||
instruction->async_allocator_type_value = async_allocator_type_value;
|
||||
instruction->is_var_args = is_var_args;
|
||||
|
||||
assert(source_node->type == NodeTypeFnProto);
|
||||
|
@ -2156,6 +2170,7 @@ static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *s
|
|||
if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block);
|
||||
}
|
||||
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
|
||||
if (async_allocator_type_value != nullptr) ir_ref_instruction(async_allocator_type_value, irb->current_basic_block);
|
||||
ir_ref_instruction(return_type, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
|
@ -2469,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;
|
||||
}
|
||||
|
@ -2675,6 +2691,46 @@ static IrInstruction *ir_build_await_bookkeeping(IrBuilder *irb, Scope *scope, A
|
|||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
||||
IrInstructionSaveErrRetAddr *instruction = ir_build_instruction<IrInstructionSaveErrRetAddr>(irb, scope, source_node);
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_add_implicit_return_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *value)
|
||||
{
|
||||
IrInstructionAddImplicitReturnType *instruction = ir_build_instruction<IrInstructionAddImplicitReturnType>(irb, scope, source_node);
|
||||
instruction->value = value;
|
||||
|
||||
ir_ref_instruction(value, irb->current_basic_block);
|
||||
|
||||
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;
|
||||
|
@ -2699,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;
|
||||
|
@ -2714,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) {
|
||||
|
@ -2747,16 +2808,18 @@ static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static bool exec_is_async(IrExecutable *exec) {
|
||||
FnTableEntry *fn_entry = exec_fn_entry(exec);
|
||||
return fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value,
|
||||
bool is_generated_code)
|
||||
{
|
||||
FnTableEntry *fn_entry = exec_fn_entry(irb->exec);
|
||||
bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
|
||||
ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value));
|
||||
|
||||
bool is_async = exec_is_async(irb->exec);
|
||||
if (!is_async) {
|
||||
//if (irb->codegen->have_err_ret_tracing) {
|
||||
// IrInstruction *stack_trace_ptr = ir_build_error_return_trace_nonnull(irb, scope, node);
|
||||
// ir_build_save_err_ret_addr(irb, scope, node, stack_trace_ptr);
|
||||
//}
|
||||
IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value);
|
||||
return_inst->is_gen = is_generated_code;
|
||||
return return_inst;
|
||||
|
@ -2778,22 +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 void ir_gen_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *node, bool is_async) {
|
||||
// if (!irb->codegen->have_err_ret_tracing)
|
||||
// return;
|
||||
//
|
||||
// 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_return_address(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;
|
||||
// }
|
||||
//
|
||||
// IrInstruction *stack_trace_ptr = ir_build_error_return_trace_nonnull(irb, scope, node);
|
||||
// ir_build_save_err_ret_addr(irb, scope, node, stack_trace_ptr);
|
||||
//}
|
||||
|
||||
static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
|
||||
assert(node->type == NodeTypeReturnExpr);
|
||||
|
||||
|
@ -2839,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);
|
||||
|
@ -2853,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, is_async);
|
||||
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);
|
||||
|
@ -2882,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);
|
||||
|
@ -2890,9 +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_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);
|
||||
|
@ -4185,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:
|
||||
{
|
||||
|
@ -5032,6 +5087,22 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n
|
|||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_promise_type(IrBuilder *irb, Scope *scope, AstNode *node) {
|
||||
assert(node->type == NodeTypePromiseType);
|
||||
|
||||
AstNode *payload_type_node = node->data.promise_type.payload_type;
|
||||
IrInstruction *payload_type_value = nullptr;
|
||||
|
||||
if (payload_type_node != nullptr) {
|
||||
payload_type_value = ir_gen_node(irb, payload_type_node, scope);
|
||||
if (payload_type_value == irb->codegen->invalid_instruction)
|
||||
return payload_type_value;
|
||||
|
||||
}
|
||||
|
||||
return ir_build_promise_type(irb, scope, node, payload_type_value);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_undefined_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
|
||||
assert(node->type == NodeTypeUndefinedLiteral);
|
||||
return ir_build_const_undefined(irb, scope, node);
|
||||
|
@ -5630,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) {
|
||||
|
@ -5989,7 +6060,15 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
|
|||
return_type = nullptr;
|
||||
}
|
||||
|
||||
return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, is_var_args);
|
||||
IrInstruction *async_allocator_type_value = nullptr;
|
||||
if (node->data.fn_proto.async_allocator_type != nullptr) {
|
||||
async_allocator_type_value = ir_gen_node(irb, node->data.fn_proto.async_allocator_type, parent_scope);
|
||||
if (async_allocator_type_value == irb->codegen->invalid_instruction)
|
||||
return irb->codegen->invalid_instruction;
|
||||
}
|
||||
|
||||
return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type,
|
||||
async_allocator_type_value, is_var_args);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
|
||||
|
@ -6044,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);
|
||||
|
@ -6067,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);
|
||||
|
@ -6092,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);
|
||||
|
@ -6168,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);
|
||||
|
@ -6187,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:
|
||||
|
@ -6232,6 +6323,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
|
|||
return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval);
|
||||
case NodeTypeArrayType:
|
||||
return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval);
|
||||
case NodeTypePromiseType:
|
||||
return ir_lval_wrap(irb, scope, ir_gen_promise_type(irb, scope, node), lval);
|
||||
case NodeTypeStringLiteral:
|
||||
return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval);
|
||||
case NodeTypeUndefinedLiteral:
|
||||
|
@ -6324,63 +6417,92 @@ 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;
|
||||
if (is_async) {
|
||||
// create the coro promise
|
||||
const_bool_false = ir_build_const_bool(irb, scope, node, false);
|
||||
VariableTableEntry *promise_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false);
|
||||
Scope *coro_scope = create_coro_prelude_scope(node, scope);
|
||||
const_bool_false = ir_build_const_bool(irb, coro_scope, node, false);
|
||||
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, scope, node, return_type);
|
||||
ir_build_var_decl(irb, scope, node, promise_var, nullptr, nullptr, promise_init);
|
||||
IrInstruction *coro_promise_ptr = ir_build_var_ptr(irb, 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, scope, nullptr, false, false, true, const_bool_false);
|
||||
IrInstruction *null_value = ir_build_const_null(irb, scope, node);
|
||||
IrInstruction *await_handle_type_val = ir_build_const_type(irb, scope, node,
|
||||
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);
|
||||
IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node,
|
||||
get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
|
||||
ir_build_var_decl(irb, scope, node, await_handle_var, await_handle_type_val, nullptr, null_value);
|
||||
irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, scope, node,
|
||||
ir_build_var_decl(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value);
|
||||
irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node,
|
||||
await_handle_var, false, false);
|
||||
|
||||
u8_ptr_type = ir_build_const_type(irb, scope, node,
|
||||
u8_ptr_type = ir_build_const_type(irb, coro_scope, node,
|
||||
get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false));
|
||||
IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, coro_promise_ptr);
|
||||
coro_id = ir_build_coro_id(irb, scope, node, promise_as_u8_ptr);
|
||||
coro_size_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false);
|
||||
IrInstruction *coro_size = ir_build_coro_size(irb, scope, node);
|
||||
ir_build_var_decl(irb, scope, node, coro_size_var, nullptr, nullptr, coro_size);
|
||||
IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node,
|
||||
IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr);
|
||||
coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr);
|
||||
coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
|
||||
IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node);
|
||||
ir_build_var_decl(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size);
|
||||
IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node,
|
||||
ImplicitAllocatorIdArg);
|
||||
irb->exec->coro_allocator_var = ir_create_var(irb, node, scope, nullptr, true, true, true, const_bool_false);
|
||||
ir_build_var_decl(irb, scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr);
|
||||
irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false);
|
||||
ir_build_var_decl(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr);
|
||||
Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME);
|
||||
IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, alloc_field_name);
|
||||
IrInstruction *alloc_fn = ir_build_load_ptr(irb, scope, node, alloc_fn_ptr);
|
||||
IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, scope, node, alloc_fn, coro_size);
|
||||
IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, scope, node, maybe_coro_mem_ptr);
|
||||
IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, scope, "AllocError");
|
||||
IrBasicBlock *alloc_ok_block = ir_create_basic_block(irb, scope, "AllocOk");
|
||||
ir_build_cond_br(irb, scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false);
|
||||
IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name);
|
||||
IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr);
|
||||
IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, alloc_fn, coro_size);
|
||||
IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr);
|
||||
IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError");
|
||||
IrBasicBlock *alloc_ok_block = ir_create_basic_block(irb, coro_scope, "AllocOk");
|
||||
ir_build_cond_br(irb, coro_scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false);
|
||||
|
||||
ir_set_cursor_at_end_and_append_block(irb, alloc_err_block);
|
||||
IrInstruction *undef = ir_build_const_undefined(irb, scope, node);
|
||||
ir_build_return(irb, scope, node, undef);
|
||||
// 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.
|
||||
ir_build_return(irb, coro_scope, node, undef);
|
||||
|
||||
ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block);
|
||||
IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, maybe_coro_mem_ptr);
|
||||
irb->exec->coro_handle = ir_build_coro_begin(irb, scope, node, coro_id, coro_mem_ptr);
|
||||
IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr);
|
||||
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, 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, 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, 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");
|
||||
|
@ -6395,6 +6517,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
|
|||
return false;
|
||||
|
||||
if (!instr_is_unreachable(result)) {
|
||||
// no need for save_err_ret_addr because this cannot return error
|
||||
ir_gen_async_return(irb, scope, result->source_node, result, true);
|
||||
}
|
||||
|
||||
|
@ -6430,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);
|
||||
|
@ -10074,13 +10203,26 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
|
|||
return result;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_add_implicit_return_type(IrAnalyze *ira,
|
||||
IrInstructionAddImplicitReturnType *instruction)
|
||||
{
|
||||
IrInstruction *value = instruction->value->other;
|
||||
if (type_is_invalid(value->value.type))
|
||||
return ir_unreach_error(ira);
|
||||
|
||||
ira->src_implicit_return_type_list.append(value);
|
||||
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
|
||||
out_val->type = ira->codegen->builtin_types.entry_void;
|
||||
return out_val->type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira,
|
||||
IrInstructionReturn *return_instruction)
|
||||
{
|
||||
IrInstruction *value = return_instruction->value->other;
|
||||
if (type_is_invalid(value->value.type))
|
||||
return ir_unreach_error(ira);
|
||||
ira->implicit_return_type_list.append(value);
|
||||
|
||||
IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->explicit_return_type);
|
||||
if (casted_value == ira->codegen->invalid_instruction)
|
||||
|
@ -10958,6 +11100,24 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *
|
|||
result_type = get_array_type(ira->codegen, child_type, new_len);
|
||||
|
||||
out_array_val = out_val;
|
||||
} else if (is_slice(op1_type) || is_slice(op2_type)) {
|
||||
TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, child_type, true);
|
||||
result_type = get_slice_type(ira->codegen, ptr_type);
|
||||
out_array_val = create_const_vals(1);
|
||||
out_array_val->special = ConstValSpecialStatic;
|
||||
out_array_val->type = get_array_type(ira->codegen, child_type, new_len);
|
||||
|
||||
out_val->data.x_struct.fields = create_const_vals(2);
|
||||
|
||||
out_val->data.x_struct.fields[slice_ptr_index].type = ptr_type;
|
||||
out_val->data.x_struct.fields[slice_ptr_index].special = ConstValSpecialStatic;
|
||||
out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.special = ConstPtrSpecialBaseArray;
|
||||
out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.data.base_array.array_val = out_array_val;
|
||||
out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.data.base_array.elem_index = 0;
|
||||
|
||||
out_val->data.x_struct.fields[slice_len_index].type = ira->codegen->builtin_types.entry_usize;
|
||||
out_val->data.x_struct.fields[slice_len_index].special = ConstValSpecialStatic;
|
||||
bigint_init_unsigned(&out_val->data.x_struct.fields[slice_len_index].data.x_bigint, new_len);
|
||||
} else {
|
||||
new_len += 1; // null byte
|
||||
|
||||
|
@ -11453,22 +11613,33 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
|
|||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) {
|
||||
FnTableEntry *fn_entry = exec_fn_entry(exec);
|
||||
return fn_entry != nullptr && fn_entry->calls_or_awaits_errorable_fn && g->have_err_ret_tracing;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
|
||||
IrInstructionErrorReturnTrace *instruction)
|
||||
{
|
||||
FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
||||
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 (fn_entry == nullptr || !fn_entry->calls_or_awaits_errorable_fn || !ira->codegen->have_err_ret_tracing) {
|
||||
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,
|
||||
|
@ -12950,6 +13121,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) {
|
||||
|
@ -13999,6 +14171,24 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira,
|
|||
zig_unreachable();
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_promise_type(IrAnalyze *ira, IrInstructionPromiseType *instruction) {
|
||||
TypeTableEntry *promise_type;
|
||||
|
||||
if (instruction->payload_type == nullptr) {
|
||||
promise_type = ira->codegen->builtin_types.entry_promise;
|
||||
} else {
|
||||
TypeTableEntry *payload_type = ir_resolve_type(ira, instruction->payload_type->other);
|
||||
if (type_is_invalid(payload_type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
promise_type = get_promise_type(ira->codegen, payload_type);
|
||||
}
|
||||
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
|
||||
out_val->data.x_type = promise_type;
|
||||
return ira->codegen->builtin_types.entry_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
|
||||
IrInstructionSizeOf *size_of_instruction)
|
||||
{
|
||||
|
@ -14569,7 +14759,7 @@ static TypeTableEntry *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructi
|
|||
return ira->codegen->builtin_types.entry_namespace;
|
||||
}
|
||||
|
||||
if ((err = os_fetch_file_path(abs_full_path, import_code))) {
|
||||
if ((err = os_fetch_file_path(abs_full_path, import_code, true))) {
|
||||
if (err == ErrorFileNotFound) {
|
||||
ir_add_error_node(ira, source_node,
|
||||
buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
|
||||
|
@ -15430,7 +15620,7 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr
|
|||
// load from file system into const expr
|
||||
Buf *file_contents = buf_alloc();
|
||||
int err;
|
||||
if ((err = os_fetch_file_path(&file_path, file_contents))) {
|
||||
if ((err = os_fetch_file_path(&file_path, file_contents, false))) {
|
||||
if (err == ErrorFileNotFound) {
|
||||
ir_add_error(ira, instruction->name, buf_sprintf("unable to find '%s'", buf_ptr(&file_path)));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
@ -16561,6 +16751,18 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc
|
|||
if (type_is_invalid(fn_type_id.return_type))
|
||||
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))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
|
||||
out_val->data.x_type = get_fn_type(ira->codegen, &fn_type_id);
|
||||
return ira->codegen->builtin_types.entry_type;
|
||||
|
@ -16789,18 +16991,18 @@ static TypeTableEntry *ir_analyze_instruction_can_implicit_cast(IrAnalyze *ira,
|
|||
static TypeTableEntry *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic *instruction) {
|
||||
IrInstruction *msg = instruction->msg->other;
|
||||
if (type_is_invalid(msg->value.type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
return ir_unreach_error(ira);
|
||||
|
||||
if (ir_should_inline(ira->new_irb.exec, instruction->base.scope)) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("encountered @panic at compile-time"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
return ir_unreach_error(ira);
|
||||
}
|
||||
|
||||
TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true);
|
||||
TypeTableEntry *str_type = get_slice_type(ira->codegen, u8_ptr_type);
|
||||
IrInstruction *casted_msg = ir_implicit_cast(ira, msg, str_type);
|
||||
if (type_is_invalid(casted_msg->value.type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
return ir_unreach_error(ira);
|
||||
|
||||
IrInstruction *new_instruction = ir_build_panic(&ira->new_irb, instruction->base.scope,
|
||||
instruction->base.source_node, casted_msg);
|
||||
|
@ -17757,6 +17959,59 @@ 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);
|
||||
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_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:
|
||||
|
@ -17822,6 +18077,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
|||
return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction);
|
||||
case IrInstructionIdArrayType:
|
||||
return ir_analyze_instruction_array_type(ira, (IrInstructionArrayType *)instruction);
|
||||
case IrInstructionIdPromiseType:
|
||||
return ir_analyze_instruction_promise_type(ira, (IrInstructionPromiseType *)instruction);
|
||||
case IrInstructionIdSizeOf:
|
||||
return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction);
|
||||
case IrInstructionIdTestNonNull:
|
||||
|
@ -17994,6 +18251,14 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
|||
return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction);
|
||||
case IrInstructionIdAwaitBookkeeping:
|
||||
return ir_analyze_instruction_await_bookkeeping(ira, (IrInstructionAwaitBookkeeping *)instruction);
|
||||
case IrInstructionIdSaveErrRetAddr:
|
||||
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();
|
||||
}
|
||||
|
@ -18067,11 +18332,11 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
|
|||
|
||||
if (new_exec->invalid) {
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
} else if (ira->implicit_return_type_list.length == 0) {
|
||||
} else if (ira->src_implicit_return_type_list.length == 0) {
|
||||
return codegen->builtin_types.entry_unreachable;
|
||||
} else {
|
||||
return ir_resolve_peer_types(ira, expected_type_source_node, ira->implicit_return_type_list.items,
|
||||
ira->implicit_return_type_list.length);
|
||||
return ir_resolve_peer_types(ira, expected_type_source_node, ira->src_implicit_return_type_list.items,
|
||||
ira->src_implicit_return_type_list.length);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18119,6 +18384,10 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
|||
case IrInstructionIdCoroSave:
|
||||
case IrInstructionIdCoroAllocHelper:
|
||||
case IrInstructionIdAwaitBookkeeping:
|
||||
case IrInstructionIdSaveErrRetAddr:
|
||||
case IrInstructionIdAddImplicitReturnType:
|
||||
case IrInstructionIdMergeErrRetTraces:
|
||||
case IrInstructionIdMarkErrRetTracePtr:
|
||||
return true;
|
||||
|
||||
case IrInstructionIdPhi:
|
||||
|
@ -18141,6 +18410,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
|||
case IrInstructionIdStructFieldPtr:
|
||||
case IrInstructionIdUnionFieldPtr:
|
||||
case IrInstructionIdArrayType:
|
||||
case IrInstructionIdPromiseType:
|
||||
case IrInstructionIdSliceType:
|
||||
case IrInstructionIdSizeOf:
|
||||
case IrInstructionIdTestNonNull:
|
||||
|
|
|
@ -201,9 +201,9 @@ static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) {
|
|||
if (call_instruction->is_async) {
|
||||
fprintf(irp->f, "async");
|
||||
if (call_instruction->async_allocator != nullptr) {
|
||||
fprintf(irp->f, "(");
|
||||
fprintf(irp->f, "<");
|
||||
ir_print_other_instruction(irp, call_instruction->async_allocator);
|
||||
fprintf(irp->f, ")");
|
||||
fprintf(irp->f, ">");
|
||||
}
|
||||
fprintf(irp->f, " ");
|
||||
}
|
||||
|
@ -404,6 +404,14 @@ static void ir_print_array_type(IrPrint *irp, IrInstructionArrayType *instructio
|
|||
ir_print_other_instruction(irp, instruction->child_type);
|
||||
}
|
||||
|
||||
static void ir_print_promise_type(IrPrint *irp, IrInstructionPromiseType *instruction) {
|
||||
fprintf(irp->f, "promise");
|
||||
if (instruction->payload_type != nullptr) {
|
||||
fprintf(irp->f, "->");
|
||||
ir_print_other_instruction(irp, instruction->payload_type);
|
||||
}
|
||||
}
|
||||
|
||||
static void ir_print_slice_type(IrPrint *irp, IrInstructionSliceType *instruction) {
|
||||
const char *const_kw = instruction->is_const ? "const " : "";
|
||||
fprintf(irp->f, "[]%s", const_kw);
|
||||
|
@ -1016,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) {
|
||||
|
@ -1161,6 +1178,32 @@ static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeepi
|
|||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_save_err_ret_addr(IrPrint *irp, IrInstructionSaveErrRetAddr *instruction) {
|
||||
fprintf(irp->f, "@saveErrRetAddr()");
|
||||
}
|
||||
|
||||
static void ir_print_add_implicit_return_type(IrPrint *irp, IrInstructionAddImplicitReturnType *instruction) {
|
||||
fprintf(irp->f, "@addImplicitReturnType(");
|
||||
ir_print_other_instruction(irp, instruction->value);
|
||||
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) {
|
||||
|
@ -1253,6 +1296,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
|||
case IrInstructionIdArrayType:
|
||||
ir_print_array_type(irp, (IrInstructionArrayType *)instruction);
|
||||
break;
|
||||
case IrInstructionIdPromiseType:
|
||||
ir_print_promise_type(irp, (IrInstructionPromiseType *)instruction);
|
||||
break;
|
||||
case IrInstructionIdSliceType:
|
||||
ir_print_slice_type(irp, (IrInstructionSliceType *)instruction);
|
||||
break;
|
||||
|
@ -1532,6 +1578,18 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
|||
case IrInstructionIdAwaitBookkeeping:
|
||||
ir_print_await_bookkeeping(irp, (IrInstructionAwaitBookkeeping *)instruction);
|
||||
break;
|
||||
case IrInstructionIdSaveErrRetAddr:
|
||||
ir_print_save_err_ret_addr(irp, (IrInstructionSaveErrRetAddr *)instruction);
|
||||
break;
|
||||
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");
|
||||
}
|
||||
|
|
61
src/link.cpp
61
src/link.cpp
|
@ -164,6 +164,47 @@ static void add_rpath(LinkJob *lj, Buf *rpath) {
|
|||
lj->rpath_table.put(rpath, true);
|
||||
}
|
||||
|
||||
static Buf *try_dynamic_linker_path(const char *ld_name) {
|
||||
const char *cc_exe = getenv("CC");
|
||||
cc_exe = (cc_exe == nullptr) ? "cc" : cc_exe;
|
||||
ZigList<const char *> args = {};
|
||||
args.append(buf_ptr(buf_sprintf("-print-file-name=%s", ld_name)));
|
||||
Termination term;
|
||||
Buf *out_stderr = buf_alloc();
|
||||
Buf *out_stdout = buf_alloc();
|
||||
int err;
|
||||
if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) {
|
||||
return nullptr;
|
||||
}
|
||||
if (term.how != TerminationIdClean || term.code != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (buf_ends_with_str(out_stdout, "\n")) {
|
||||
buf_resize(out_stdout, buf_len(out_stdout) - 1);
|
||||
}
|
||||
if (buf_len(out_stdout) == 0 || buf_eql_str(out_stdout, ld_name)) {
|
||||
return nullptr;
|
||||
}
|
||||
return out_stdout;
|
||||
}
|
||||
|
||||
static Buf *get_dynamic_linker_path(CodeGen *g) {
|
||||
if (g->is_native_target && g->zig_target.arch.arch == ZigLLVM_x86_64) {
|
||||
static const char *ld_names[] = {
|
||||
"ld-linux-x86-64.so.2",
|
||||
"ld-musl-x86_64.so.1",
|
||||
};
|
||||
for (size_t i = 0; i < array_length(ld_names); i += 1) {
|
||||
const char *ld_name = ld_names[i];
|
||||
Buf *result = try_dynamic_linker_path(ld_name);
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return target_dynamic_linker(&g->zig_target);
|
||||
}
|
||||
|
||||
static void construct_linker_job_elf(LinkJob *lj) {
|
||||
CodeGen *g = lj->codegen;
|
||||
|
||||
|
@ -259,12 +300,16 @@ static void construct_linker_job_elf(LinkJob *lj) {
|
|||
lj->args.append(buf_ptr(g->libc_static_lib_dir));
|
||||
}
|
||||
|
||||
if (g->dynamic_linker && buf_len(g->dynamic_linker) > 0) {
|
||||
lj->args.append("-dynamic-linker");
|
||||
lj->args.append(buf_ptr(g->dynamic_linker));
|
||||
} else {
|
||||
lj->args.append("-dynamic-linker");
|
||||
lj->args.append(buf_ptr(target_dynamic_linker(&g->zig_target)));
|
||||
if (!g->is_static) {
|
||||
if (g->dynamic_linker != nullptr) {
|
||||
assert(buf_len(g->dynamic_linker) != 0);
|
||||
lj->args.append("-dynamic-linker");
|
||||
lj->args.append(buf_ptr(g->dynamic_linker));
|
||||
} else {
|
||||
Buf *resolved_dynamic_linker = get_dynamic_linker_path(g);
|
||||
lj->args.append("-dynamic-linker");
|
||||
lj->args.append(buf_ptr(resolved_dynamic_linker));
|
||||
}
|
||||
}
|
||||
|
||||
if (shared) {
|
||||
|
@ -423,7 +468,9 @@ static void construct_linker_job_coff(LinkJob *lj) {
|
|||
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->kernel32_lib_dir))));
|
||||
|
||||
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_lib_dir))));
|
||||
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_static_lib_dir))));
|
||||
if (g->libc_static_lib_dir != nullptr) {
|
||||
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_static_lib_dir))));
|
||||
}
|
||||
}
|
||||
|
||||
if (lj->link_in_crt) {
|
||||
|
|
68
src/main.cpp
68
src/main.cpp
|
@ -23,6 +23,7 @@ static int usage(const char *arg0) {
|
|||
" build-exe [source] create executable from source or object files\n"
|
||||
" build-lib [source] create library from source or object files\n"
|
||||
" build-obj [source] create object from source or assembly\n"
|
||||
" run [source] create executable and run immediately\n"
|
||||
" translate-c [source] convert c code to zig code\n"
|
||||
" targets list available compilation targets\n"
|
||||
" test [source] create and run a test build\n"
|
||||
|
@ -195,13 +196,6 @@ static int find_zig_lib_dir(Buf *out_path) {
|
|||
}
|
||||
}
|
||||
|
||||
if (ZIG_INSTALL_PREFIX != nullptr) {
|
||||
if (test_zig_install_prefix(buf_create_from_str(ZIG_INSTALL_PREFIX), out_path)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return ErrorFileNotFound;
|
||||
}
|
||||
|
||||
|
@ -227,6 +221,7 @@ static Buf *resolve_zig_lib_dir(const char *zig_install_prefix_arg) {
|
|||
enum Cmd {
|
||||
CmdInvalid,
|
||||
CmdBuild,
|
||||
CmdRun,
|
||||
CmdTest,
|
||||
CmdVersion,
|
||||
CmdZen,
|
||||
|
@ -336,6 +331,8 @@ int main(int argc, char **argv) {
|
|||
CliPkg *cur_pkg = allocate<CliPkg>(1);
|
||||
BuildMode build_mode = BuildModeDebug;
|
||||
ZigList<const char *> test_exec_args = {0};
|
||||
int comptime_args_end = 0;
|
||||
int runtime_args_start = argc;
|
||||
|
||||
if (argc >= 2 && strcmp(argv[1], "build") == 0) {
|
||||
const char *zig_exe_path = arg0;
|
||||
|
@ -452,10 +449,11 @@ int main(int argc, char **argv) {
|
|||
|
||||
if ((err = os_copy_file(build_template_path, &build_file_abs))) {
|
||||
fprintf(stderr, "Unable to write build.zig template: %s\n", err_str(err));
|
||||
return EXIT_FAILURE;
|
||||
} else {
|
||||
fprintf(stderr, "Wrote build.zig template\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
|
@ -487,11 +485,15 @@ int main(int argc, char **argv) {
|
|||
return (term.how == TerminationIdClean) ? term.code : -1;
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; i += 1) {
|
||||
for (int i = 1; i < argc; i += 1, comptime_args_end += 1) {
|
||||
char *arg = argv[i];
|
||||
|
||||
if (arg[0] == '-') {
|
||||
if (strcmp(arg, "--release-fast") == 0) {
|
||||
if (strcmp(arg, "--") == 0) {
|
||||
// ignore -- from both compile and runtime arg sets
|
||||
runtime_args_start = i + 1;
|
||||
break;
|
||||
} else if (strcmp(arg, "--release-fast") == 0) {
|
||||
build_mode = BuildModeFastRelease;
|
||||
} else if (strcmp(arg, "--release-safe") == 0) {
|
||||
build_mode = BuildModeSafeRelease;
|
||||
|
@ -658,6 +660,9 @@ int main(int argc, char **argv) {
|
|||
} else if (strcmp(arg, "build-lib") == 0) {
|
||||
cmd = CmdBuild;
|
||||
out_type = OutTypeLib;
|
||||
} else if (strcmp(arg, "run") == 0) {
|
||||
cmd = CmdRun;
|
||||
out_type = OutTypeExe;
|
||||
} else if (strcmp(arg, "version") == 0) {
|
||||
cmd = CmdVersion;
|
||||
} else if (strcmp(arg, "zen") == 0) {
|
||||
|
@ -676,6 +681,7 @@ int main(int argc, char **argv) {
|
|||
} else {
|
||||
switch (cmd) {
|
||||
case CmdBuild:
|
||||
case CmdRun:
|
||||
case CmdTranslateC:
|
||||
case CmdTest:
|
||||
if (!in_file) {
|
||||
|
@ -730,8 +736,8 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
switch (cmd) {
|
||||
case CmdRun:
|
||||
case CmdBuild:
|
||||
case CmdTranslateC:
|
||||
case CmdTest:
|
||||
|
@ -739,7 +745,7 @@ int main(int argc, char **argv) {
|
|||
if (cmd == CmdBuild && !in_file && objects.length == 0 && asm_files.length == 0) {
|
||||
fprintf(stderr, "Expected source file argument or at least one --object or --assembly argument.\n");
|
||||
return usage(arg0);
|
||||
} else if ((cmd == CmdTranslateC || cmd == CmdTest) && !in_file) {
|
||||
} else if ((cmd == CmdTranslateC || cmd == CmdTest || cmd == CmdRun) && !in_file) {
|
||||
fprintf(stderr, "Expected source file argument.\n");
|
||||
return usage(arg0);
|
||||
} else if (cmd == CmdBuild && out_type == OutTypeObj && objects.length != 0) {
|
||||
|
@ -751,6 +757,10 @@ int main(int argc, char **argv) {
|
|||
|
||||
bool need_name = (cmd == CmdBuild || cmd == CmdTranslateC);
|
||||
|
||||
if (cmd == CmdRun) {
|
||||
out_name = "run";
|
||||
}
|
||||
|
||||
Buf *in_file_buf = nullptr;
|
||||
|
||||
Buf *buf_out_name = (cmd == CmdTest) ? buf_create_from_str("test") :
|
||||
|
@ -775,9 +785,23 @@ int main(int argc, char **argv) {
|
|||
Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf;
|
||||
|
||||
Buf *full_cache_dir = buf_alloc();
|
||||
os_path_resolve(buf_create_from_str("."),
|
||||
buf_create_from_str((cache_dir == nullptr) ? default_zig_cache_name : cache_dir),
|
||||
full_cache_dir);
|
||||
Buf *run_exec_path = buf_alloc();
|
||||
if (cmd == CmdRun) {
|
||||
if (buf_out_name == nullptr) {
|
||||
buf_out_name = buf_create_from_str("run");
|
||||
}
|
||||
|
||||
Buf *global_cache_dir = buf_alloc();
|
||||
os_get_global_cache_directory(global_cache_dir);
|
||||
os_path_join(global_cache_dir, buf_out_name, run_exec_path);
|
||||
os_path_resolve(buf_create_from_str("."), global_cache_dir, full_cache_dir);
|
||||
|
||||
out_file = buf_ptr(run_exec_path);
|
||||
} else {
|
||||
os_path_resolve(buf_create_from_str("."),
|
||||
buf_create_from_str((cache_dir == nullptr) ? default_zig_cache_name : cache_dir),
|
||||
full_cache_dir);
|
||||
}
|
||||
|
||||
Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix);
|
||||
|
||||
|
@ -861,7 +885,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
add_package(g, cur_pkg, g->root_package);
|
||||
|
||||
if (cmd == CmdBuild) {
|
||||
if (cmd == CmdBuild || cmd == CmdRun) {
|
||||
codegen_set_emit_file_type(g, emit_file_type);
|
||||
|
||||
for (size_t i = 0; i < objects.length; i += 1) {
|
||||
|
@ -874,6 +898,18 @@ int main(int argc, char **argv) {
|
|||
codegen_link(g, out_file);
|
||||
if (timing_info)
|
||||
codegen_print_timing_report(g, stdout);
|
||||
|
||||
if (cmd == CmdRun) {
|
||||
ZigList<const char*> args = {0};
|
||||
for (int i = runtime_args_start; i < argc; ++i) {
|
||||
args.append(argv[i]);
|
||||
}
|
||||
|
||||
Termination term;
|
||||
os_spawn_process(buf_ptr(run_exec_path), args, &term);
|
||||
return term.code;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
} else if (cmd == CmdTranslateC) {
|
||||
codegen_translate_c(g, in_file_buf);
|
||||
|
|
101
src/os.cpp
101
src/os.cpp
|
@ -45,6 +45,7 @@ typedef SSIZE_T ssize_t;
|
|||
#if defined(__MACH__)
|
||||
#include <mach/clock.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
|
@ -57,10 +58,6 @@ static clock_serv_t cclock;
|
|||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
// these implementations are lazy. But who cares, we'll make a robust
|
||||
// implementation in the zig standard library and then this code all gets
|
||||
// deleted when we self-host. it works for now.
|
||||
|
||||
#if defined(ZIG_OS_POSIX)
|
||||
static void populate_termination(Termination *term, int status) {
|
||||
if (WIFEXITED(status)) {
|
||||
|
@ -291,13 +288,39 @@ void os_path_resolve(Buf *ref_path, Buf *target_path, Buf *out_abs_path) {
|
|||
return;
|
||||
}
|
||||
|
||||
int os_fetch_file(FILE *f, Buf *out_buf) {
|
||||
int os_fetch_file(FILE *f, Buf *out_buf, bool skip_shebang) {
|
||||
static const ssize_t buf_size = 0x2000;
|
||||
buf_resize(out_buf, buf_size);
|
||||
ssize_t actual_buf_len = 0;
|
||||
|
||||
bool first_read = true;
|
||||
|
||||
for (;;) {
|
||||
size_t amt_read = fread(buf_ptr(out_buf) + actual_buf_len, 1, buf_size, f);
|
||||
actual_buf_len += amt_read;
|
||||
|
||||
if (skip_shebang && first_read && buf_starts_with_str(out_buf, "#!")) {
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
if (i > buf_len(out_buf)) {
|
||||
zig_panic("shebang line exceeded %zd characters", buf_size);
|
||||
}
|
||||
|
||||
size_t current_pos = i;
|
||||
i += 1;
|
||||
|
||||
if (out_buf->list.at(current_pos) == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ZigList<char> *list = &out_buf->list;
|
||||
memmove(list->items, list->items + i, list->length - i);
|
||||
list->length -= i;
|
||||
|
||||
actual_buf_len -= i;
|
||||
}
|
||||
|
||||
if (amt_read != buf_size) {
|
||||
if (feof(f)) {
|
||||
buf_resize(out_buf, actual_buf_len);
|
||||
|
@ -308,6 +331,7 @@ int os_fetch_file(FILE *f, Buf *out_buf) {
|
|||
}
|
||||
|
||||
buf_resize(out_buf, actual_buf_len + buf_size);
|
||||
first_read = false;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
@ -377,8 +401,8 @@ static int os_exec_process_posix(const char *exe, ZigList<const char *> &args,
|
|||
|
||||
FILE *stdout_f = fdopen(stdout_pipe[0], "rb");
|
||||
FILE *stderr_f = fdopen(stderr_pipe[0], "rb");
|
||||
os_fetch_file(stdout_f, out_stdout);
|
||||
os_fetch_file(stderr_f, out_stderr);
|
||||
os_fetch_file(stdout_f, out_stdout, false);
|
||||
os_fetch_file(stderr_f, out_stderr, false);
|
||||
|
||||
fclose(stdout_f);
|
||||
fclose(stderr_f);
|
||||
|
@ -591,7 +615,7 @@ int os_copy_file(Buf *src_path, Buf *dest_path) {
|
|||
}
|
||||
}
|
||||
|
||||
int os_fetch_file_path(Buf *full_path, Buf *out_contents) {
|
||||
int os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang) {
|
||||
FILE *f = fopen(buf_ptr(full_path), "rb");
|
||||
if (!f) {
|
||||
switch (errno) {
|
||||
|
@ -610,7 +634,7 @@ int os_fetch_file_path(Buf *full_path, Buf *out_contents) {
|
|||
return ErrorFileSystem;
|
||||
}
|
||||
}
|
||||
int result = os_fetch_file(f, out_contents);
|
||||
int result = os_fetch_file(f, out_contents, skip_shebang);
|
||||
fclose(f);
|
||||
return result;
|
||||
}
|
||||
|
@ -783,6 +807,44 @@ int os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path) {
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined(ZIG_OS_POSIX)
|
||||
int os_get_global_cache_directory(Buf *out_tmp_path) {
|
||||
const char *tmp_dir = getenv("TMPDIR");
|
||||
if (!tmp_dir) {
|
||||
tmp_dir = P_tmpdir;
|
||||
}
|
||||
|
||||
Buf *tmp_dir_buf = buf_create_from_str(tmp_dir);
|
||||
Buf *cache_dirname_buf = buf_create_from_str("zig-cache");
|
||||
|
||||
buf_resize(out_tmp_path, 0);
|
||||
os_path_join(tmp_dir_buf, cache_dirname_buf, out_tmp_path);
|
||||
|
||||
buf_deinit(tmp_dir_buf);
|
||||
buf_deinit(cache_dirname_buf);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
int os_get_global_cache_directory(Buf *out_tmp_path) {
|
||||
char tmp_dir[MAX_PATH + 1];
|
||||
if (GetTempPath(MAX_PATH, tmp_dir) == 0) {
|
||||
zig_panic("GetTempPath failed");
|
||||
}
|
||||
|
||||
Buf *tmp_dir_buf = buf_create_from_str(tmp_dir);
|
||||
Buf *cache_dirname_buf = buf_create_from_str("zig-cache");
|
||||
|
||||
buf_resize(out_tmp_path, 0);
|
||||
os_path_join(tmp_dir_buf, cache_dirname_buf, out_tmp_path);
|
||||
|
||||
buf_deinit(tmp_dir_buf);
|
||||
buf_deinit(cache_dirname_buf);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int os_delete_file(Buf *path) {
|
||||
if (remove(buf_ptr(path))) {
|
||||
return ErrorFileSystem;
|
||||
|
@ -927,9 +989,26 @@ int os_self_exe_path(Buf *out_path) {
|
|||
}
|
||||
|
||||
#elif defined(ZIG_OS_DARWIN)
|
||||
return ErrorFileNotFound;
|
||||
uint32_t u32_len = 0;
|
||||
int ret1 = _NSGetExecutablePath(nullptr, &u32_len);
|
||||
assert(ret1 != 0);
|
||||
buf_resize(out_path, u32_len);
|
||||
int ret2 = _NSGetExecutablePath(buf_ptr(out_path), &u32_len);
|
||||
assert(ret2 == 0);
|
||||
return 0;
|
||||
#elif defined(ZIG_OS_LINUX)
|
||||
return ErrorFileNotFound;
|
||||
buf_resize(out_path, 256);
|
||||
for (;;) {
|
||||
ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path));
|
||||
if (amt == -1) {
|
||||
return ErrorUnexpected;
|
||||
}
|
||||
if (amt == (ssize_t)buf_len(out_path)) {
|
||||
buf_resize(out_path, buf_len(out_path) * 2);
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return ErrorFileNotFound;
|
||||
}
|
||||
|
|
|
@ -51,14 +51,16 @@ int os_path_real(Buf *rel_path, Buf *out_abs_path);
|
|||
void os_path_resolve(Buf *ref_path, Buf *target_path, Buf *out_abs_path);
|
||||
bool os_path_is_absolute(Buf *path);
|
||||
|
||||
int os_get_global_cache_directory(Buf *out_tmp_path);
|
||||
|
||||
int os_make_path(Buf *path);
|
||||
int os_make_dir(Buf *path);
|
||||
|
||||
void os_write_file(Buf *full_path, Buf *contents);
|
||||
int os_copy_file(Buf *src_path, Buf *dest_path);
|
||||
|
||||
int os_fetch_file(FILE *file, Buf *out_contents);
|
||||
int os_fetch_file_path(Buf *full_path, Buf *out_contents);
|
||||
int os_fetch_file(FILE *file, Buf *out_contents, bool skip_shebang);
|
||||
int os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang);
|
||||
|
||||
int os_get_cwd(Buf *out_cwd);
|
||||
|
||||
|
|
162
src/parser.cpp
162
src/parser.cpp
|
@ -705,7 +705,7 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
|
|||
}
|
||||
|
||||
/*
|
||||
PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl
|
||||
PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType
|
||||
KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable" | "suspend"
|
||||
ErrorSetDecl = "error" "{" list(Symbol, ",") "}"
|
||||
*/
|
||||
|
@ -774,6 +774,15 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
|
|||
AstNode *node = ast_create_node(pc, NodeTypeSuspend, token);
|
||||
*token_index += 1;
|
||||
return node;
|
||||
} else if (token->id == TokenIdKeywordPromise) {
|
||||
AstNode *node = ast_create_node(pc, NodeTypePromiseType, token);
|
||||
*token_index += 1;
|
||||
Token *arrow_tok = &pc->tokens->at(*token_index);
|
||||
if (arrow_tok->id == TokenIdArrow) {
|
||||
*token_index += 1;
|
||||
node->data.promise_type.payload_type = ast_parse_type_expr(pc, token_index, true);
|
||||
}
|
||||
return node;
|
||||
} else if (token->id == TokenIdKeywordError) {
|
||||
Token *next_token = &pc->tokens->at(*token_index + 1);
|
||||
if (next_token->id == TokenIdLBrace) {
|
||||
|
@ -955,8 +964,68 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_inde
|
|||
}
|
||||
}
|
||||
|
||||
static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index, Token *fn_token,
|
||||
AstNode *async_allocator_type_node, CallingConvention cc, bool is_extern, VisibMod visib_mod)
|
||||
{
|
||||
AstNode *node = ast_create_node(pc, NodeTypeFnProto, fn_token);
|
||||
node->data.fn_proto.visib_mod = visib_mod;
|
||||
node->data.fn_proto.cc = cc;
|
||||
node->data.fn_proto.is_extern = is_extern;
|
||||
node->data.fn_proto.async_allocator_type = async_allocator_type_node;
|
||||
|
||||
Token *fn_name = &pc->tokens->at(*token_index);
|
||||
|
||||
if (fn_name->id == TokenIdSymbol) {
|
||||
*token_index += 1;
|
||||
node->data.fn_proto.name = token_buf(fn_name);
|
||||
} else {
|
||||
node->data.fn_proto.name = nullptr;
|
||||
}
|
||||
|
||||
ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
|
||||
|
||||
Token *next_token = &pc->tokens->at(*token_index);
|
||||
if (next_token->id == TokenIdKeywordAlign) {
|
||||
*token_index += 1;
|
||||
ast_eat_token(pc, token_index, TokenIdLParen);
|
||||
|
||||
node->data.fn_proto.align_expr = ast_parse_expression(pc, token_index, true);
|
||||
ast_eat_token(pc, token_index, TokenIdRParen);
|
||||
next_token = &pc->tokens->at(*token_index);
|
||||
}
|
||||
if (next_token->id == TokenIdKeywordSection) {
|
||||
*token_index += 1;
|
||||
ast_eat_token(pc, token_index, TokenIdLParen);
|
||||
|
||||
node->data.fn_proto.section_expr = ast_parse_expression(pc, token_index, true);
|
||||
ast_eat_token(pc, token_index, TokenIdRParen);
|
||||
next_token = &pc->tokens->at(*token_index);
|
||||
}
|
||||
if (next_token->id == TokenIdKeywordVar) {
|
||||
node->data.fn_proto.return_var_token = next_token;
|
||||
*token_index += 1;
|
||||
next_token = &pc->tokens->at(*token_index);
|
||||
} else {
|
||||
if (next_token->id == TokenIdKeywordError) {
|
||||
Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1);
|
||||
if (maybe_lbrace_tok->id == TokenIdLBrace) {
|
||||
*token_index += 1;
|
||||
node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token);
|
||||
return node;
|
||||
}
|
||||
} else if (next_token->id == TokenIdBang) {
|
||||
*token_index += 1;
|
||||
node->data.fn_proto.auto_err_set = true;
|
||||
next_token = &pc->tokens->at(*token_index);
|
||||
}
|
||||
node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
SuffixOpExpression = ("async" option("(" Expression ")") PrimaryExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
|
||||
SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
|
||||
FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
|
||||
ArrayAccessExpression : token(LBracket) Expression token(RBracket)
|
||||
SliceExpression = "[" Expression ".." option(Expression) "]"
|
||||
|
@ -972,19 +1041,25 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,
|
|||
|
||||
AstNode *allocator_expr_node = nullptr;
|
||||
Token *async_lparen_tok = &pc->tokens->at(*token_index);
|
||||
if (async_lparen_tok->id == TokenIdLParen) {
|
||||
if (async_lparen_tok->id == TokenIdCmpLessThan) {
|
||||
*token_index += 1;
|
||||
allocator_expr_node = ast_parse_expression(pc, token_index, true);
|
||||
ast_eat_token(pc, token_index, TokenIdRParen);
|
||||
allocator_expr_node = ast_parse_prefix_op_expr(pc, token_index, true);
|
||||
ast_eat_token(pc, token_index, TokenIdCmpGreaterThan);
|
||||
}
|
||||
|
||||
AstNode *fn_ref_expr_node = ast_parse_primary_expr(pc, token_index, true);
|
||||
Token *lparen_tok = ast_eat_token(pc, token_index, TokenIdLParen);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, lparen_tok);
|
||||
Token *fncall_token = &pc->tokens->at(*token_index);
|
||||
if (fncall_token->id == TokenIdKeywordFn) {
|
||||
*token_index += 1;
|
||||
return ast_parse_fn_proto_partial(pc, token_index, fncall_token, allocator_expr_node, CallingConventionAsync,
|
||||
false, VisibModPrivate);
|
||||
}
|
||||
AstNode *node = ast_parse_suffix_op_expr(pc, token_index, true);
|
||||
if (node->type != NodeTypeFnCallExpr) {
|
||||
ast_error(pc, fncall_token, "expected function call, found '%s'", token_name(fncall_token->id));
|
||||
}
|
||||
node->data.fn_call_expr.is_async = true;
|
||||
node->data.fn_call_expr.async_allocator = allocator_expr_node;
|
||||
node->data.fn_call_expr.fn_ref_expr = fn_ref_expr_node;
|
||||
ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params);
|
||||
assert(node->data.fn_call_expr.fn_ref_expr != nullptr);
|
||||
|
||||
primary_expr = node;
|
||||
} else {
|
||||
|
@ -2433,9 +2508,10 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
|
|||
} else if (first_token->id == TokenIdKeywordAsync) {
|
||||
*token_index += 1;
|
||||
Token *next_token = &pc->tokens->at(*token_index);
|
||||
if (next_token->id == TokenIdLParen) {
|
||||
if (next_token->id == TokenIdCmpLessThan) {
|
||||
*token_index += 1;
|
||||
async_allocator_type_node = ast_parse_type_expr(pc, token_index, true);
|
||||
ast_eat_token(pc, token_index, TokenIdRParen);
|
||||
ast_eat_token(pc, token_index, TokenIdCmpGreaterThan);
|
||||
}
|
||||
fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
|
||||
cc = CallingConventionAsync;
|
||||
|
@ -2469,61 +2545,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeFnProto, fn_token);
|
||||
node->data.fn_proto.visib_mod = visib_mod;
|
||||
node->data.fn_proto.cc = cc;
|
||||
node->data.fn_proto.is_extern = is_extern;
|
||||
node->data.fn_proto.async_allocator_type = async_allocator_type_node;
|
||||
|
||||
Token *fn_name = &pc->tokens->at(*token_index);
|
||||
|
||||
if (fn_name->id == TokenIdSymbol) {
|
||||
*token_index += 1;
|
||||
node->data.fn_proto.name = token_buf(fn_name);
|
||||
} else {
|
||||
node->data.fn_proto.name = nullptr;
|
||||
}
|
||||
|
||||
ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
|
||||
|
||||
Token *next_token = &pc->tokens->at(*token_index);
|
||||
if (next_token->id == TokenIdKeywordAlign) {
|
||||
*token_index += 1;
|
||||
ast_eat_token(pc, token_index, TokenIdLParen);
|
||||
|
||||
node->data.fn_proto.align_expr = ast_parse_expression(pc, token_index, true);
|
||||
ast_eat_token(pc, token_index, TokenIdRParen);
|
||||
next_token = &pc->tokens->at(*token_index);
|
||||
}
|
||||
if (next_token->id == TokenIdKeywordSection) {
|
||||
*token_index += 1;
|
||||
ast_eat_token(pc, token_index, TokenIdLParen);
|
||||
|
||||
node->data.fn_proto.section_expr = ast_parse_expression(pc, token_index, true);
|
||||
ast_eat_token(pc, token_index, TokenIdRParen);
|
||||
next_token = &pc->tokens->at(*token_index);
|
||||
}
|
||||
if (next_token->id == TokenIdKeywordVar) {
|
||||
node->data.fn_proto.return_var_token = next_token;
|
||||
*token_index += 1;
|
||||
next_token = &pc->tokens->at(*token_index);
|
||||
} else {
|
||||
if (next_token->id == TokenIdKeywordError) {
|
||||
Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1);
|
||||
if (maybe_lbrace_tok->id == TokenIdLBrace) {
|
||||
*token_index += 1;
|
||||
node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token);
|
||||
return node;
|
||||
}
|
||||
} else if (next_token->id == TokenIdBang) {
|
||||
*token_index += 1;
|
||||
node->data.fn_proto.auto_err_set = true;
|
||||
next_token = &pc->tokens->at(*token_index);
|
||||
}
|
||||
node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true);
|
||||
}
|
||||
|
||||
return node;
|
||||
return ast_parse_fn_proto_partial(pc, token_index, fn_token, async_allocator_type_node, cc, is_extern, visib_mod);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2901,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;
|
||||
|
@ -3068,6 +3087,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
|
|||
visit_field(&node->data.array_type.child_type, visit, context);
|
||||
visit_field(&node->data.array_type.align_expr, visit, context);
|
||||
break;
|
||||
case NodeTypePromiseType:
|
||||
visit_field(&node->data.promise_type.payload_type, visit, context);
|
||||
break;
|
||||
case NodeTypeErrorType:
|
||||
// none
|
||||
break;
|
||||
|
|
|
@ -16,7 +16,6 @@ ATTRIBUTE_PRINTF(2, 3)
|
|||
void ast_token_error(Token *token, const char *format, ...);
|
||||
|
||||
|
||||
// This function is provided by generated code, generated by parsergen.cpp
|
||||
AstNode * ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, ErrColor err_color);
|
||||
|
||||
void ast_print(AstNode *node, int indent);
|
||||
|
|
|
@ -863,6 +863,10 @@ Buf *target_dynamic_linker(ZigTarget *target) {
|
|||
env == ZigLLVM_GNUX32)
|
||||
{
|
||||
return buf_create_from_str("/libx32/ld-linux-x32.so.2");
|
||||
} else if (arch == ZigLLVM_x86_64 &&
|
||||
(env == ZigLLVM_Musl || env == ZigLLVM_MuslEABI || env == ZigLLVM_MuslEABIHF))
|
||||
{
|
||||
return buf_create_from_str("/lib/ld-musl-x86_64.so.1");
|
||||
} else {
|
||||
return buf_create_from_str("/lib64/ld-linux-x86-64.so.2");
|
||||
}
|
||||
|
|
|
@ -135,6 +135,7 @@ static const struct ZigKeyword zig_keywords[] = {
|
|||
{"null", TokenIdKeywordNull},
|
||||
{"or", TokenIdKeywordOr},
|
||||
{"packed", TokenIdKeywordPacked},
|
||||
{"promise", TokenIdKeywordPromise},
|
||||
{"pub", TokenIdKeywordPub},
|
||||
{"resume", TokenIdKeywordResume},
|
||||
{"return", TokenIdKeywordReturn},
|
||||
|
@ -1558,6 +1559,7 @@ const char * token_name(TokenId id) {
|
|||
case TokenIdKeywordNull: return "null";
|
||||
case TokenIdKeywordOr: return "or";
|
||||
case TokenIdKeywordPacked: return "packed";
|
||||
case TokenIdKeywordPromise: return "promise";
|
||||
case TokenIdKeywordPub: return "pub";
|
||||
case TokenIdKeywordReturn: return "return";
|
||||
case TokenIdKeywordSection: return "section";
|
||||
|
|
|
@ -76,6 +76,7 @@ enum TokenId {
|
|||
TokenIdKeywordNull,
|
||||
TokenIdKeywordOr,
|
||||
TokenIdKeywordPacked,
|
||||
TokenIdKeywordPromise,
|
||||
TokenIdKeywordPub,
|
||||
TokenIdKeywordResume,
|
||||
TokenIdKeywordReturn,
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -1627,6 +1627,7 @@ pub const TestStep = struct {
|
|||
filter: ?[]const u8,
|
||||
target: Target,
|
||||
exec_cmd_args: ?[]const ?[]const u8,
|
||||
include_dirs: ArrayList([]const u8),
|
||||
|
||||
pub fn init(builder: &Builder, root_src: []const u8) TestStep {
|
||||
const step_name = builder.fmt("test {}", root_src);
|
||||
|
@ -1641,6 +1642,7 @@ pub const TestStep = struct {
|
|||
.link_libs = BufSet.init(builder.allocator),
|
||||
.target = Target { .Native = {} },
|
||||
.exec_cmd_args = null,
|
||||
.include_dirs = ArrayList([]const u8).init(builder.allocator),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1648,6 +1650,10 @@ pub const TestStep = struct {
|
|||
self.verbose = value;
|
||||
}
|
||||
|
||||
pub fn addIncludeDir(self: &TestStep, path: []const u8) void {
|
||||
self.include_dirs.append(path) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn setBuildMode(self: &TestStep, mode: builtin.Mode) void {
|
||||
self.build_mode = mode;
|
||||
}
|
||||
|
@ -1746,6 +1752,11 @@ pub const TestStep = struct {
|
|||
}
|
||||
}
|
||||
|
||||
for (self.include_dirs.toSliceConst()) |include_path| {
|
||||
try zig_args.append("-isystem");
|
||||
try zig_args.append(builder.pathFromRoot(include_path));
|
||||
}
|
||||
|
||||
for (builder.include_paths.toSliceConst()) |include_path| {
|
||||
try zig_args.append("-isystem");
|
||||
try zig_args.append(builder.pathFromRoot(include_path));
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
extern "c" fn __error() &c_int;
|
||||
pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int;
|
||||
|
||||
pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize;
|
||||
|
||||
pub use @import("../os/darwin_errno.zig");
|
||||
|
||||
|
@ -45,3 +46,20 @@ pub const Sigaction = extern struct {
|
|||
sa_mask: sigset_t,
|
||||
sa_flags: c_int,
|
||||
};
|
||||
|
||||
pub const dirent = extern struct {
|
||||
d_ino: usize,
|
||||
d_seekoff: usize,
|
||||
d_reclen: u16,
|
||||
d_namlen: u16,
|
||||
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;
|
||||
|
|
|
@ -44,6 +44,7 @@ pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias o
|
|||
pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?×pec) c_int;
|
||||
pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
|
||||
pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int;
|
||||
pub extern "c" fn rmdir(path: &const u8) c_int;
|
||||
|
||||
pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?&c_void;
|
||||
pub extern "c" fn malloc(usize) ?&c_void;
|
||||
|
|
|
@ -84,7 +84,7 @@ fn Blake2s(comptime out_len: usize) type { return struct {
|
|||
}
|
||||
|
||||
// Full middle blocks.
|
||||
while (off + 64 < b.len) : (off += 64) {
|
||||
while (off + 64 <= b.len) : (off += 64) {
|
||||
d.t += 64;
|
||||
d.round(b[off..off + 64], false);
|
||||
}
|
||||
|
@ -229,6 +229,15 @@ test "blake2s256 streaming" {
|
|||
htest.assertEqual(h2, out[0..]);
|
||||
}
|
||||
|
||||
test "blake2s256 aligned final" {
|
||||
var block = []u8 {0} ** Blake2s256.block_size;
|
||||
var out: [Blake2s256.digest_size]u8 = undefined;
|
||||
|
||||
var h = Blake2s256.init();
|
||||
h.update(block);
|
||||
h.final(out[0..]);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////
|
||||
// Blake2b
|
||||
|
@ -305,7 +314,7 @@ fn Blake2b(comptime out_len: usize) type { return struct {
|
|||
}
|
||||
|
||||
// Full middle blocks.
|
||||
while (off + 128 < b.len) : (off += 128) {
|
||||
while (off + 128 <= b.len) : (off += 128) {
|
||||
d.t += 128;
|
||||
d.round(b[off..off + 128], false);
|
||||
}
|
||||
|
@ -447,3 +456,12 @@ test "blake2b512 streaming" {
|
|||
h.final(out[0..]);
|
||||
htest.assertEqual(h2, out[0..]);
|
||||
}
|
||||
|
||||
test "blake2b512 aligned final" {
|
||||
var block = []u8 {0} ** Blake2b512.block_size;
|
||||
var out: [Blake2b512.digest_size]u8 = undefined;
|
||||
|
||||
var h = Blake2b512.init();
|
||||
h.update(block);
|
||||
h.final(out[0..]);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
const std = @import("../index.zig");
|
||||
const crypto = std.crypto;
|
||||
const debug = std.debug;
|
||||
const mem = std.mem;
|
||||
|
||||
pub const HmacMd5 = Hmac(crypto.Md5);
|
||||
pub const HmacSha1 = Hmac(crypto.Sha1);
|
||||
pub const HmacSha256 = Hmac(crypto.Sha256);
|
||||
|
||||
pub fn Hmac(comptime H: type) type {
|
||||
return struct {
|
||||
const digest_size = H.digest_size;
|
||||
|
||||
pub fn hash(output: []u8, key: []const u8, message: []const u8) void {
|
||||
debug.assert(output.len >= H.digest_size);
|
||||
debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
|
||||
var scratch: [H.block_size]u8 = undefined;
|
||||
|
||||
// Normalize key length to block size of hash
|
||||
if (key.len > H.block_size) {
|
||||
H.hash(key, scratch[0..H.digest_size]);
|
||||
mem.set(u8, scratch[H.digest_size..H.block_size], 0);
|
||||
} else if (key.len < H.block_size) {
|
||||
mem.copy(u8, scratch[0..key.len], key);
|
||||
mem.set(u8, scratch[key.len..H.block_size], 0);
|
||||
} else {
|
||||
mem.copy(u8, scratch[0..], key);
|
||||
}
|
||||
|
||||
var o_key_pad: [H.block_size]u8 = undefined;
|
||||
for (o_key_pad) |*b, i| {
|
||||
*b = scratch[i] ^ 0x5c;
|
||||
}
|
||||
|
||||
var i_key_pad: [H.block_size]u8 = undefined;
|
||||
for (i_key_pad) |*b, i| {
|
||||
*b = scratch[i] ^ 0x36;
|
||||
}
|
||||
|
||||
// HMAC(k, m) = H(o_key_pad | H(i_key_pad | message)) where | is concatenation
|
||||
var hmac = H.init();
|
||||
hmac.update(i_key_pad[0..]);
|
||||
hmac.update(message);
|
||||
hmac.final(scratch[0..H.digest_size]);
|
||||
|
||||
hmac.reset();
|
||||
hmac.update(o_key_pad[0..]);
|
||||
hmac.update(scratch[0..H.digest_size]);
|
||||
hmac.final(output[0..H.digest_size]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const htest = @import("test.zig");
|
||||
|
||||
test "hmac md5" {
|
||||
var out: [crypto.Md5.digest_size]u8 = undefined;
|
||||
HmacMd5.hash(out[0..], "", "");
|
||||
htest.assertEqual("74e6f7298a9c2d168935f58c001bad88", out[0..]);
|
||||
|
||||
HmacMd5.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");
|
||||
htest.assertEqual("80070713463e7749b90c2dc24911e275", out[0..]);
|
||||
}
|
||||
|
||||
test "hmac sha1" {
|
||||
var out: [crypto.Sha1.digest_size]u8 = undefined;
|
||||
HmacSha1.hash(out[0..], "", "");
|
||||
htest.assertEqual("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d", out[0..]);
|
||||
|
||||
HmacSha1.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");
|
||||
htest.assertEqual("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", out[0..]);
|
||||
}
|
||||
|
||||
test "hmac sha256" {
|
||||
var out: [crypto.Sha256.digest_size]u8 = undefined;
|
||||
HmacSha256.hash(out[0..], "", "");
|
||||
htest.assertEqual("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", out[0..]);
|
||||
|
||||
HmacSha256.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");
|
||||
htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]);
|
||||
}
|
|
@ -19,10 +19,16 @@ pub const Blake2s256 = blake2.Blake2s256;
|
|||
pub const Blake2b384 = blake2.Blake2b384;
|
||||
pub const Blake2b512 = blake2.Blake2b512;
|
||||
|
||||
const hmac = @import("hmac.zig");
|
||||
pub const HmacMd5 = hmac.HmacMd5;
|
||||
pub const HmacSha1 = hmac.Sha1;
|
||||
pub const HmacSha256 = hmac.Sha256;
|
||||
|
||||
test "crypto" {
|
||||
_ = @import("md5.zig");
|
||||
_ = @import("sha1.zig");
|
||||
_ = @import("sha2.zig");
|
||||
_ = @import("sha3.zig");
|
||||
_ = @import("blake2.zig");
|
||||
_ = @import("hmac.zig");
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ pub const Md5 = struct {
|
|||
}
|
||||
|
||||
// Full middle blocks.
|
||||
while (off + 64 < b.len) : (off += 64) {
|
||||
while (off + 64 <= b.len) : (off += 64) {
|
||||
d.round(b[off..off + 64]);
|
||||
}
|
||||
|
||||
|
@ -253,3 +253,12 @@ test "md5 streaming" {
|
|||
|
||||
htest.assertEqual("900150983cd24fb0d6963f7d28e17f72", out[0..]);
|
||||
}
|
||||
|
||||
test "md5 aligned final" {
|
||||
var block = []u8 {0} ** Md5.block_size;
|
||||
var out: [Md5.digest_size]u8 = undefined;
|
||||
|
||||
var h = Md5.init();
|
||||
h.update(block);
|
||||
h.final(out[0..]);
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ pub const Sha1 = struct {
|
|||
}
|
||||
|
||||
// Full middle blocks.
|
||||
while (off + 64 < b.len) : (off += 64) {
|
||||
while (off + 64 <= b.len) : (off += 64) {
|
||||
d.round(b[off..off + 64]);
|
||||
}
|
||||
|
||||
|
@ -284,3 +284,12 @@ test "sha1 streaming" {
|
|||
h.final(out[0..]);
|
||||
htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]);
|
||||
}
|
||||
|
||||
test "sha1 aligned final" {
|
||||
var block = []u8 {0} ** Sha1.block_size;
|
||||
var out: [Sha1.digest_size]u8 = undefined;
|
||||
|
||||
var h = Sha1.init();
|
||||
h.update(block);
|
||||
h.final(out[0..]);
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { return struct {
|
|||
}
|
||||
|
||||
// Full middle blocks.
|
||||
while (off + 64 < b.len) : (off += 64) {
|
||||
while (off + 64 <= b.len) : (off += 64) {
|
||||
d.round(b[off..off + 64]);
|
||||
}
|
||||
|
||||
|
@ -319,6 +319,15 @@ test "sha256 streaming" {
|
|||
htest.assertEqual("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", out[0..]);
|
||||
}
|
||||
|
||||
test "sha256 aligned final" {
|
||||
var block = []u8 {0} ** Sha256.block_size;
|
||||
var out: [Sha256.digest_size]u8 = undefined;
|
||||
|
||||
var h = Sha256.init();
|
||||
h.update(block);
|
||||
h.final(out[0..]);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////
|
||||
// Sha384 + Sha512
|
||||
|
@ -420,7 +429,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { return struct {
|
|||
}
|
||||
|
||||
// Full middle blocks.
|
||||
while (off + 128 < b.len) : (off += 128) {
|
||||
while (off + 128 <= b.len) : (off += 128) {
|
||||
d.round(b[off..off + 128]);
|
||||
}
|
||||
|
||||
|
@ -669,3 +678,12 @@ test "sha512 streaming" {
|
|||
h.final(out[0..]);
|
||||
htest.assertEqual(h2, out[0..]);
|
||||
}
|
||||
|
||||
test "sha512 aligned final" {
|
||||
var block = []u8 {0} ** Sha512.block_size;
|
||||
var out: [Sha512.digest_size]u8 = undefined;
|
||||
|
||||
var h = Sha512.init();
|
||||
h.update(block);
|
||||
h.final(out[0..]);
|
||||
}
|
||||
|
|
|
@ -217,6 +217,15 @@ test "sha3-256 streaming" {
|
|||
htest.assertEqual("3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", out[0..]);
|
||||
}
|
||||
|
||||
test "sha3-256 aligned final" {
|
||||
var block = []u8 {0} ** Sha3_256.block_size;
|
||||
var out: [Sha3_256.digest_size]u8 = undefined;
|
||||
|
||||
var h = Sha3_256.init();
|
||||
h.update(block);
|
||||
h.final(out[0..]);
|
||||
}
|
||||
|
||||
test "sha3-384 single" {
|
||||
const h1 = "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004";
|
||||
htest.assertEqualHash(Sha3_384, h1 , "");
|
||||
|
@ -278,3 +287,12 @@ test "sha3-512 streaming" {
|
|||
h.final(out[0..]);
|
||||
htest.assertEqual(h2, out[0..]);
|
||||
}
|
||||
|
||||
test "sha3-512 aligned final" {
|
||||
var block = []u8 {0} ** Sha3_512.block_size;
|
||||
var out: [Sha3_512.digest_size]u8 = undefined;
|
||||
|
||||
var h = Sha3_512.init();
|
||||
h.update(block);
|
||||
h.final(out[0..]);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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");
|
||||
}
|
|
@ -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");
|
||||
|
@ -26,7 +27,7 @@ pub const math = @import("math/index.zig");
|
|||
pub const mem = @import("mem.zig");
|
||||
pub const net = @import("net.zig");
|
||||
pub const os = @import("os/index.zig");
|
||||
pub const rand = @import("rand.zig");
|
||||
pub const rand = @import("rand/index.zig");
|
||||
pub const sort = @import("sort.zig");
|
||||
pub const unicode = @import("unicode.zig");
|
||||
pub const zig = @import("zig/index.zig");
|
||||
|
@ -49,16 +50,17 @@ 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.zig");
|
||||
_ = @import("rand/index.zig");
|
||||
_ = @import("sort.zig");
|
||||
_ = @import("unicode.zig");
|
||||
_ = @import("zig/index.zig");
|
||||
|
|
30
std/io.zig
30
std/io.zig
|
@ -144,7 +144,7 @@ pub fn InStream(comptime ReadError: type) type {
|
|||
/// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents
|
||||
/// read from the stream so far are lost.
|
||||
pub fn readUntilDelimiterBuffer(self: &Self, buffer: &Buffer, delimiter: u8, max_size: usize) !void {
|
||||
try buf.resize(0);
|
||||
try buffer.resize(0);
|
||||
|
||||
while (true) {
|
||||
var byte: u8 = try self.readByte();
|
||||
|
@ -153,11 +153,11 @@ pub fn InStream(comptime ReadError: type) type {
|
|||
return;
|
||||
}
|
||||
|
||||
if (buf.len() == max_size) {
|
||||
if (buffer.len() == max_size) {
|
||||
return error.StreamTooLong;
|
||||
}
|
||||
|
||||
try buf.appendByte(byte);
|
||||
try buffer.appendByte(byte);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ pub fn InStream(comptime ReadError: type) type {
|
|||
var buf = Buffer.initNull(allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
try self.readUntilDelimiterBuffer(self, &buf, delimiter, max_size);
|
||||
try self.readUntilDelimiterBuffer(&buf, delimiter, max_size);
|
||||
return buf.toOwnedSlice();
|
||||
}
|
||||
|
||||
|
@ -478,3 +478,25 @@ test "import io tests" {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn readLine(buf: []u8) !usize {
|
||||
var stdin = getStdIn() catch return error.StdInUnavailable;
|
||||
var adapter = FileInStream.init(&stdin);
|
||||
var stream = &adapter.stream;
|
||||
var index: usize = 0;
|
||||
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;
|
||||
buf[index] = byte;
|
||||
index += 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const std = @import("index.zig");
|
||||
const io = std.io;
|
||||
const allocator = std.debug.global_allocator;
|
||||
const Rand = std.rand.Rand;
|
||||
const DefaultPrng = std.rand.DefaultPrng;
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
const os = std.os;
|
||||
|
@ -9,8 +9,8 @@ const builtin = @import("builtin");
|
|||
|
||||
test "write a file, read it, then delete it" {
|
||||
var data: [1024]u8 = undefined;
|
||||
var rng = Rand.init(1234);
|
||||
rng.fillBytes(data[0..]);
|
||||
var prng = DefaultPrng.init(1234);
|
||||
prng.random.bytes(data[0..]);
|
||||
const tmp_file_name = "temp_test_file.txt";
|
||||
{
|
||||
var file = try os.File.openWrite(allocator, tmp_file_name);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -22,7 +22,7 @@ const std = @import("../index.zig");
|
|||
const math = std.math;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
fn atan2(comptime T: type, x: T, y: T) T {
|
||||
pub fn atan2(comptime T: type, x: T, y: T) T {
|
||||
return switch (T) {
|
||||
f32 => atan2_32(x, y),
|
||||
f64 => atan2_64(x, y),
|
||||
|
|
|
@ -515,15 +515,28 @@ test "math.negateCast" {
|
|||
|
||||
/// Cast an integer to a different integer type. If the value doesn't fit,
|
||||
/// return an error.
|
||||
pub fn cast(comptime T: type, x: var) !T {
|
||||
pub fn cast(comptime T: type, x: var) (error{Overflow}!T) {
|
||||
comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer
|
||||
if (x > @maxValue(T)) {
|
||||
comptime assert(@typeId(@typeOf(x)) == builtin.TypeId.Int); // must pass an integer
|
||||
if (@maxValue(@typeOf(x)) > @maxValue(T) and x > @maxValue(T)) {
|
||||
return error.Overflow;
|
||||
} else if (@minValue(@typeOf(x)) < @minValue(T) and x < @minValue(T)) {
|
||||
return error.Overflow;
|
||||
} else {
|
||||
return T(x);
|
||||
}
|
||||
}
|
||||
|
||||
test "math.cast" {
|
||||
if (cast(u8, u32(300))) |_| @panic("fail") else |err| assert(err == error.Overflow);
|
||||
if (cast(i8, i32(-200))) |_| @panic("fail") else |err| assert(err == error.Overflow);
|
||||
if (cast(u8, i8(-1))) |_| @panic("fail") else |err| assert(err == error.Overflow);
|
||||
if (cast(u64, i8(-1))) |_| @panic("fail") else |err| assert(err == error.Overflow);
|
||||
|
||||
assert((try cast(u8, u32(255))) == u8(255));
|
||||
assert(@typeOf(try cast(u8, u32(255))) == u8);
|
||||
}
|
||||
|
||||
pub fn floorPowerOfTwo(comptime T: type, value: T) T {
|
||||
var x = value;
|
||||
|
||||
|
|
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);
|
||||
}
|
||||
|
|
|
@ -13,8 +13,6 @@ const builtin = @import("builtin");
|
|||
const Os = builtin.Os;
|
||||
const LinkedList = std.LinkedList;
|
||||
|
||||
var children_nodes = LinkedList(&ChildProcess).init();
|
||||
|
||||
const is_windows = builtin.os == Os.windows;
|
||||
|
||||
pub const ChildProcess = struct {
|
||||
|
@ -296,8 +294,6 @@ pub const ChildProcess = struct {
|
|||
}
|
||||
|
||||
fn cleanupAfterWait(self: &ChildProcess, status: i32) !Term {
|
||||
children_nodes.remove(&self.llnode);
|
||||
|
||||
defer {
|
||||
os.close(self.err_pipe[0]);
|
||||
os.close(self.err_pipe[1]);
|
||||
|
@ -427,9 +423,6 @@ pub const ChildProcess = struct {
|
|||
self.llnode = LinkedList(&ChildProcess).Node.init(self);
|
||||
self.term = null;
|
||||
|
||||
// TODO make this atomic so it works even with threads
|
||||
children_nodes.prepend(&self.llnode);
|
||||
|
||||
if (self.stdin_behavior == StdIo.Pipe) { os.close(stdin_pipe[0]); }
|
||||
if (self.stdout_behavior == StdIo.Pipe) { os.close(stdout_pipe[1]); }
|
||||
if (self.stderr_behavior == StdIo.Pipe) { os.close(stderr_pipe[1]); }
|
||||
|
@ -773,31 +766,3 @@ fn readIntFd(fd: i32) !ErrInt {
|
|||
os.posixRead(fd, bytes[0..]) catch return error.SystemResources;
|
||||
return mem.readInt(bytes[0..], ErrInt, builtin.endian);
|
||||
}
|
||||
|
||||
extern fn sigchld_handler(_: i32) void {
|
||||
while (true) {
|
||||
var status: i32 = undefined;
|
||||
const pid_result = posix.waitpid(-1, &status, posix.WNOHANG);
|
||||
if (pid_result == 0) {
|
||||
return;
|
||||
}
|
||||
const err = posix.getErrno(pid_result);
|
||||
if (err > 0) {
|
||||
if (err == posix.ECHILD) {
|
||||
return;
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
handleTerm(i32(pid_result), status);
|
||||
}
|
||||
}
|
||||
|
||||
fn handleTerm(pid: i32, status: i32) void {
|
||||
var it = children_nodes.first;
|
||||
while (it) |node| : (it = node.next) {
|
||||
if (node.data.pid == pid) {
|
||||
node.data.handleWaitResult(status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,10 +56,32 @@ pub const O_SYMLINK = 0x200000; /// allow open of symlinks
|
|||
pub const O_EVTONLY = 0x8000; /// descriptor requested for event notifications only
|
||||
pub const O_CLOEXEC = 0x1000000; /// mark as close-on-exec
|
||||
|
||||
pub const O_ACCMODE = 3;
|
||||
pub const O_ALERT = 536870912;
|
||||
pub const O_ASYNC = 64;
|
||||
pub const O_DIRECTORY = 1048576;
|
||||
pub const O_DP_GETRAWENCRYPTED = 1;
|
||||
pub const O_DP_GETRAWUNENCRYPTED = 2;
|
||||
pub const O_DSYNC = 4194304;
|
||||
pub const O_FSYNC = O_SYNC;
|
||||
pub const O_NOCTTY = 131072;
|
||||
pub const O_POPUP = 2147483648;
|
||||
pub const O_SYNC = 128;
|
||||
|
||||
pub const SEEK_SET = 0x0;
|
||||
pub const SEEK_CUR = 0x1;
|
||||
pub const SEEK_END = 0x2;
|
||||
|
||||
pub const DT_UNKNOWN = 0;
|
||||
pub const DT_FIFO = 1;
|
||||
pub const DT_CHR = 2;
|
||||
pub const DT_DIR = 4;
|
||||
pub const DT_BLK = 6;
|
||||
pub const DT_REG = 8;
|
||||
pub const DT_LNK = 10;
|
||||
pub const DT_SOCK = 12;
|
||||
pub const DT_WHT = 14;
|
||||
|
||||
pub const SIG_BLOCK = 1; /// block specified signal set
|
||||
pub const SIG_UNBLOCK = 2; /// unblock specified signal set
|
||||
pub const SIG_SETMASK = 3; /// set specified signal set
|
||||
|
@ -192,6 +214,11 @@ pub fn pipe(fds: &[2]i32) usize {
|
|||
return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));
|
||||
}
|
||||
|
||||
|
||||
pub fn getdirentries64(fd: i32, buf_ptr: &u8, buf_len: usize, basep: &i64) usize {
|
||||
return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep)));
|
||||
}
|
||||
|
||||
pub fn mkdir(path: &const u8, mode: u32) usize {
|
||||
return errnoWrap(c.mkdir(path, mode));
|
||||
}
|
||||
|
@ -204,6 +231,10 @@ pub fn rename(old: &const u8, new: &const u8) usize {
|
|||
return errnoWrap(c.rename(old, new));
|
||||
}
|
||||
|
||||
pub fn rmdir(path: &const u8) usize {
|
||||
return errnoWrap(c.rmdir(path));
|
||||
}
|
||||
|
||||
pub fn chdir(path: &const u8) usize {
|
||||
return errnoWrap(c.chdir(path));
|
||||
}
|
||||
|
@ -268,6 +299,10 @@ pub const empty_sigset = sigset_t(0);
|
|||
|
||||
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 {
|
||||
|
|
|
@ -233,7 +233,7 @@ pub const File = struct {
|
|||
Unexpected,
|
||||
};
|
||||
|
||||
fn mode(self: &File) ModeError!FileMode {
|
||||
fn mode(self: &File) ModeError!os.FileMode {
|
||||
if (is_posix) {
|
||||
var stat: posix.Stat = undefined;
|
||||
const err = posix.getErrno(posix.fstat(self.handle, &stat));
|
||||
|
|
640
std/os/index.zig
640
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;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1050,15 +1073,16 @@ const DeleteTreeError = error {
|
|||
};
|
||||
pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!void {
|
||||
start_over: while (true) {
|
||||
var got_access_denied = false;
|
||||
// First, try deleting the item as a file. This way we don't follow sym links.
|
||||
if (deleteFile(allocator, full_path)) {
|
||||
return;
|
||||
} else |err| switch (err) {
|
||||
error.FileNotFound => return,
|
||||
error.IsDir => {},
|
||||
error.AccessDenied => got_access_denied = true,
|
||||
|
||||
error.OutOfMemory,
|
||||
error.AccessDenied,
|
||||
error.SymLinkLoop,
|
||||
error.NameTooLong,
|
||||
error.SystemResources,
|
||||
|
@ -1071,7 +1095,12 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
|
|||
}
|
||||
{
|
||||
var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
|
||||
error.NotDir => continue :start_over,
|
||||
error.NotDir => {
|
||||
if (got_access_denied) {
|
||||
return error.AccessDenied;
|
||||
}
|
||||
continue :start_over;
|
||||
},
|
||||
|
||||
error.OutOfMemory,
|
||||
error.AccessDenied,
|
||||
|
@ -1109,18 +1138,16 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
|
|||
}
|
||||
|
||||
pub const Dir = struct {
|
||||
// See man getdents
|
||||
fd: i32,
|
||||
darwin_seek: darwin_seek_t,
|
||||
allocator: &Allocator,
|
||||
buf: []u8,
|
||||
index: usize,
|
||||
end_index: usize,
|
||||
|
||||
const LinuxEntry = extern struct {
|
||||
d_ino: usize,
|
||||
d_off: usize,
|
||||
d_reclen: u16,
|
||||
d_name: u8, // field address is the address of first byte of name
|
||||
const darwin_seek_t = switch (builtin.os) {
|
||||
Os.macosx, Os.ios => i64,
|
||||
else => void,
|
||||
};
|
||||
|
||||
pub const Entry = struct {
|
||||
|
@ -1135,15 +1162,26 @@ pub const Dir = struct {
|
|||
SymLink,
|
||||
File,
|
||||
UnixDomainSocket,
|
||||
Whiteout,
|
||||
Unknown,
|
||||
};
|
||||
};
|
||||
|
||||
pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {
|
||||
const fd = try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0);
|
||||
const fd = switch (builtin.os) {
|
||||
Os.windows => @compileError("TODO support Dir.open for windows"),
|
||||
Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),
|
||||
Os.macosx, Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_NONBLOCK|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),
|
||||
else => @compileError("Dir.open is not supported for this platform"),
|
||||
};
|
||||
const darwin_seek_init = switch (builtin.os) {
|
||||
Os.macosx, Os.ios => 0,
|
||||
else => {},
|
||||
};
|
||||
return Dir {
|
||||
.allocator = allocator,
|
||||
.fd = fd,
|
||||
.darwin_seek = darwin_seek_init,
|
||||
.index = 0,
|
||||
.end_index = 0,
|
||||
.buf = []u8{},
|
||||
|
@ -1158,6 +1196,15 @@ pub const Dir = struct {
|
|||
/// Memory such as file names referenced in this returned entry becomes invalid
|
||||
/// with subsequent calls to next, as well as when this ::Dir is deinitialized.
|
||||
pub fn next(self: &Dir) !?Entry {
|
||||
switch (builtin.os) {
|
||||
Os.linux => return self.nextLinux(),
|
||||
Os.macosx, Os.ios => return self.nextDarwin(),
|
||||
Os.windows => return self.nextWindows(),
|
||||
else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)),
|
||||
}
|
||||
}
|
||||
|
||||
fn nextDarwin(self: &Dir) !?Entry {
|
||||
start_over: while (true) {
|
||||
if (self.index >= self.end_index) {
|
||||
if (self.buf.len == 0) {
|
||||
|
@ -1165,8 +1212,9 @@ pub const Dir = struct {
|
|||
}
|
||||
|
||||
while (true) {
|
||||
const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
|
||||
const err = linux.getErrno(result);
|
||||
const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len,
|
||||
&self.darwin_seek);
|
||||
const err = posix.getErrno(result);
|
||||
if (err > 0) {
|
||||
switch (err) {
|
||||
posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
|
||||
|
@ -1184,7 +1232,67 @@ pub const Dir = struct {
|
|||
break;
|
||||
}
|
||||
}
|
||||
const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]);
|
||||
const darwin_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);
|
||||
const next_index = self.index + darwin_entry.d_reclen;
|
||||
self.index = next_index;
|
||||
|
||||
const name = (&darwin_entry.d_name)[0..darwin_entry.d_namlen];
|
||||
|
||||
// skip . and .. entries
|
||||
if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
|
||||
continue :start_over;
|
||||
}
|
||||
|
||||
const entry_kind = switch (darwin_entry.d_type) {
|
||||
posix.DT_BLK => Entry.Kind.BlockDevice,
|
||||
posix.DT_CHR => Entry.Kind.CharacterDevice,
|
||||
posix.DT_DIR => Entry.Kind.Directory,
|
||||
posix.DT_FIFO => Entry.Kind.NamedPipe,
|
||||
posix.DT_LNK => Entry.Kind.SymLink,
|
||||
posix.DT_REG => Entry.Kind.File,
|
||||
posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
|
||||
posix.DT_WHT => Entry.Kind.Whiteout,
|
||||
else => Entry.Kind.Unknown,
|
||||
};
|
||||
return Entry {
|
||||
.name = name,
|
||||
.kind = entry_kind,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn nextWindows(self: &Dir) !?Entry {
|
||||
@compileError("TODO support Dir.next for windows");
|
||||
}
|
||||
|
||||
fn nextLinux(self: &Dir) !?Entry {
|
||||
start_over: while (true) {
|
||||
if (self.index >= self.end_index) {
|
||||
if (self.buf.len == 0) {
|
||||
self.buf = try self.allocator.alloc(u8, page_size);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
|
||||
const err = posix.getErrno(result);
|
||||
if (err > 0) {
|
||||
switch (err) {
|
||||
posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
|
||||
posix.EINVAL => {
|
||||
self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
|
||||
continue;
|
||||
},
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
if (result == 0)
|
||||
return null;
|
||||
self.index = 0;
|
||||
self.end_index = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const linux_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);
|
||||
const next_index = self.index + linux_entry.d_reclen;
|
||||
self.index = next_index;
|
||||
|
||||
|
@ -1668,39 +1776,29 @@ 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");
|
||||
}
|
||||
|
||||
|
||||
// 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.dumpStackTrace();
|
||||
debug.dumpCurrentStackTrace(null);
|
||||
}
|
||||
return 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.dumpStackTrace();
|
||||
debug.dumpCurrentStackTrace(null);
|
||||
}
|
||||
return error.Unexpected;
|
||||
}
|
||||
|
@ -1812,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");
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -488,3 +488,11 @@ pub const timespec = extern struct {
|
|||
tv_sec: isize,
|
||||
tv_nsec: isize,
|
||||
};
|
||||
|
||||
pub const dirent = extern struct {
|
||||
d_ino: usize,
|
||||
d_off: usize,
|
||||
d_reclen: u16,
|
||||
d_name: u8, // field address is the address of first byte of name
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
const std = @import("../index.zig");
|
||||
const os = std.os;
|
||||
const assert = std.debug.assert;
|
||||
const io = std.io;
|
||||
|
||||
const a = std.debug.global_allocator;
|
||||
|
||||
const builtin = @import("builtin");
|
||||
|
||||
test "makePath, put some files in it, deleteTree" {
|
||||
if (builtin.os == builtin.Os.windows) {
|
||||
// TODO implement os.Dir for windows
|
||||
// https://github.com/zig-lang/zig/issues/709
|
||||
return;
|
||||
}
|
||||
try os.makePath(a, "os_test_tmp/b/c");
|
||||
try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense");
|
||||
try io.writeFile(a, "os_test_tmp/b/file2.txt", "blah");
|
||||
try os.deleteTree(a, "os_test_tmp");
|
||||
if (os.Dir.open(a, "os_test_tmp")) |dir| {
|
||||
@panic("expected error");
|
||||
} else |err| {
|
||||
assert(err == error.PathNotFound);
|
||||
}
|
||||
}
|
240
std/rand.zig
240
std/rand.zig
|
@ -1,240 +0,0 @@
|
|||
const std = @import("index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const rand_test = @import("rand_test.zig");
|
||||
const mem = std.mem;
|
||||
const math = std.math;
|
||||
|
||||
pub const MT19937_32 = MersenneTwister(
|
||||
u32, 624, 397, 31,
|
||||
0x9908B0DF,
|
||||
11, 0xFFFFFFFF,
|
||||
7, 0x9D2C5680,
|
||||
15, 0xEFC60000,
|
||||
18, 1812433253);
|
||||
|
||||
pub const MT19937_64 = MersenneTwister(
|
||||
u64, 312, 156, 31,
|
||||
0xB5026F5AA96619E9,
|
||||
29, 0x5555555555555555,
|
||||
17, 0x71D67FFFEDA60000,
|
||||
37, 0xFFF7EEE000000000,
|
||||
43, 6364136223846793005);
|
||||
|
||||
/// Use `init` to initialize this state.
|
||||
pub const Rand = struct {
|
||||
const Rng = if (@sizeOf(usize) >= 8) MT19937_64 else MT19937_32;
|
||||
|
||||
rng: Rng,
|
||||
|
||||
/// Initialize random state with the given seed.
|
||||
pub fn init(seed: usize) Rand {
|
||||
return Rand {
|
||||
.rng = Rng.init(seed),
|
||||
};
|
||||
}
|
||||
|
||||
/// Get an integer or boolean with random bits.
|
||||
pub fn scalar(r: &Rand, comptime T: type) T {
|
||||
if (T == usize) {
|
||||
return r.rng.get();
|
||||
} else if (T == bool) {
|
||||
return (r.rng.get() & 0b1) == 0;
|
||||
} else {
|
||||
var result: [@sizeOf(T)]u8 = undefined;
|
||||
r.fillBytes(result[0..]);
|
||||
return mem.readInt(result, T, builtin.Endian.Little);
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill `buf` with randomness.
|
||||
pub fn fillBytes(r: &Rand, buf: []u8) void {
|
||||
var bytes_left = buf.len;
|
||||
while (bytes_left >= @sizeOf(usize)) {
|
||||
mem.writeInt(buf[buf.len - bytes_left..], r.rng.get(), builtin.Endian.Little);
|
||||
bytes_left -= @sizeOf(usize);
|
||||
}
|
||||
if (bytes_left > 0) {
|
||||
var rand_val_array: [@sizeOf(usize)]u8 = undefined;
|
||||
mem.writeInt(rand_val_array[0..], r.rng.get(), builtin.Endian.Little);
|
||||
while (bytes_left > 0) {
|
||||
buf[buf.len - bytes_left] = rand_val_array[@sizeOf(usize) - bytes_left];
|
||||
bytes_left -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a random unsigned integer with even distribution between `start`
|
||||
/// inclusive and `end` exclusive.
|
||||
pub fn range(r: &Rand, comptime T: type, start: T, end: T) T {
|
||||
assert(start <= end);
|
||||
if (T.is_signed) {
|
||||
const uint = @IntType(false, T.bit_count);
|
||||
if (start >= 0 and end >= 0) {
|
||||
return T(r.range(uint, uint(start), uint(end)));
|
||||
} else if (start < 0 and end < 0) {
|
||||
// Can't overflow because the range is over signed ints
|
||||
return math.negateCast(r.range(uint, math.absCast(end), math.absCast(start)) + 1) catch unreachable;
|
||||
} else if (start < 0 and end >= 0) {
|
||||
const end_uint = uint(end);
|
||||
const total_range = math.absCast(start) + end_uint;
|
||||
const value = r.range(uint, 0, total_range);
|
||||
const result = if (value < end_uint) x: {
|
||||
break :x T(value);
|
||||
} else if (value == end_uint) x: {
|
||||
break :x start;
|
||||
} else x: {
|
||||
// Can't overflow because the range is over signed ints
|
||||
break :x math.negateCast(value - end_uint) catch unreachable;
|
||||
};
|
||||
return result;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
} else {
|
||||
const total_range = end - start;
|
||||
const leftover = @maxValue(T) % total_range;
|
||||
const upper_bound = @maxValue(T) - leftover;
|
||||
var rand_val_array: [@sizeOf(T)]u8 = undefined;
|
||||
|
||||
while (true) {
|
||||
r.fillBytes(rand_val_array[0..]);
|
||||
const rand_val = mem.readInt(rand_val_array, T, builtin.Endian.Little);
|
||||
if (rand_val < upper_bound) {
|
||||
return start + (rand_val % total_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a floating point value in the range 0.0..1.0.
|
||||
pub fn float(r: &Rand, comptime T: type) T {
|
||||
// TODO Implement this way instead:
|
||||
// const int = @int_type(false, @sizeOf(T) * 8);
|
||||
// const mask = ((1 << @float_mantissa_bit_count(T)) - 1);
|
||||
// const rand_bits = r.rng.scalar(int) & mask;
|
||||
// return @float_compose(T, false, 0, rand_bits) - 1.0
|
||||
const int_type = @IntType(false, @sizeOf(T) * 8);
|
||||
const precision = if (T == f32)
|
||||
16777216
|
||||
else if (T == f64)
|
||||
9007199254740992
|
||||
else
|
||||
@compileError("unknown floating point type")
|
||||
;
|
||||
return T(r.range(int_type, 0, precision)) / T(precision);
|
||||
}
|
||||
};
|
||||
|
||||
fn MersenneTwister(
|
||||
comptime int: type, comptime n: usize, comptime m: usize, comptime r: int,
|
||||
comptime a: int,
|
||||
comptime u: math.Log2Int(int), comptime d: int,
|
||||
comptime s: math.Log2Int(int), comptime b: int,
|
||||
comptime t: math.Log2Int(int), comptime c: int,
|
||||
comptime l: math.Log2Int(int), comptime f: int) type
|
||||
{
|
||||
return struct {
|
||||
const Self = this;
|
||||
|
||||
array: [n]int,
|
||||
index: usize,
|
||||
|
||||
pub fn init(seed: int) Self {
|
||||
var mt = Self {
|
||||
.array = undefined,
|
||||
.index = n,
|
||||
};
|
||||
|
||||
var prev_value = seed;
|
||||
mt.array[0] = prev_value;
|
||||
var i: usize = 1;
|
||||
while (i < n) : (i += 1) {
|
||||
prev_value = int(i) +% f *% (prev_value ^ (prev_value >> (int.bit_count - 2)));
|
||||
mt.array[i] = prev_value;
|
||||
}
|
||||
return mt;
|
||||
}
|
||||
|
||||
pub fn get(mt: &Self) int {
|
||||
const mag01 = []int{0, a};
|
||||
const LM: int = (1 << r) - 1;
|
||||
const UM = ~LM;
|
||||
|
||||
if (mt.index >= n) {
|
||||
var i: usize = 0;
|
||||
|
||||
while (i < n - m) : (i += 1) {
|
||||
const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM);
|
||||
mt.array[i] = mt.array[i + m] ^ (x >> 1) ^ mag01[usize(x & 0x1)];
|
||||
}
|
||||
|
||||
while (i < n - 1) : (i += 1) {
|
||||
const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM);
|
||||
mt.array[i] = mt.array[i + m - n] ^ (x >> 1) ^ mag01[usize(x & 0x1)];
|
||||
|
||||
}
|
||||
const x = (mt.array[i] & UM) | (mt.array[0] & LM);
|
||||
mt.array[i] = mt.array[m - 1] ^ (x >> 1) ^ mag01[usize(x & 0x1)];
|
||||
|
||||
mt.index = 0;
|
||||
}
|
||||
|
||||
var x = mt.array[mt.index];
|
||||
mt.index += 1;
|
||||
|
||||
x ^= ((x >> u) & d);
|
||||
x ^= ((x << s) & b);
|
||||
x ^= ((x << t) & c);
|
||||
x ^= (x >> l);
|
||||
|
||||
return x;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "rand float 32" {
|
||||
var r = Rand.init(42);
|
||||
var i: usize = 0;
|
||||
while (i < 1000) : (i += 1) {
|
||||
const val = r.float(f32);
|
||||
assert(val >= 0.0);
|
||||
assert(val < 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
test "rand.MT19937_64" {
|
||||
var rng = MT19937_64.init(rand_test.mt64_seed);
|
||||
for (rand_test.mt64_data) |value| {
|
||||
assert(value == rng.get());
|
||||
}
|
||||
}
|
||||
|
||||
test "rand.MT19937_32" {
|
||||
var rng = MT19937_32.init(rand_test.mt32_seed);
|
||||
for (rand_test.mt32_data) |value| {
|
||||
assert(value == rng.get());
|
||||
}
|
||||
}
|
||||
|
||||
test "rand.Rand.range" {
|
||||
var r = Rand.init(42);
|
||||
testRange(&r, -4, 3);
|
||||
testRange(&r, -4, -1);
|
||||
testRange(&r, 10, 14);
|
||||
}
|
||||
|
||||
fn testRange(r: &Rand, start: i32, end: i32) void {
|
||||
const count = usize(end - start);
|
||||
var values_buffer = []bool{false} ** 20;
|
||||
const values = values_buffer[0..count];
|
||||
var i: usize = 0;
|
||||
while (i < count) {
|
||||
const value = r.range(i32, start, end);
|
||||
const index = usize(value - start);
|
||||
if (!values[index]) {
|
||||
i += 1;
|
||||
values[index] = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,652 @@
|
|||
// The engines provided here should be initialized from an external source. For now, getRandomBytes
|
||||
// from the os package is the most suitable. Be sure to use a CSPRNG when required, otherwise using
|
||||
// a normal PRNG will be faster and use substantially less stack space.
|
||||
//
|
||||
// ```
|
||||
// var buf: [8]u8 = undefined;
|
||||
// try std.os.getRandomBytes(buf[0..]);
|
||||
// const seed = mem.readInt(buf[0..8], u64, builtin.Endian.Little);
|
||||
//
|
||||
// var r = DefaultPrng.init(seed);
|
||||
//
|
||||
// const s = r.random.scalar(u64);
|
||||
// ```
|
||||
//
|
||||
// TODO(tiehuis): Benchmark these against other reference implementations.
|
||||
|
||||
const std = @import("../index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
const math = std.math;
|
||||
|
||||
// When you need fast unbiased random numbers
|
||||
pub const DefaultPrng = Xoroshiro128;
|
||||
|
||||
// When you need cryptographically secure random numbers
|
||||
pub const DefaultCsprng = Isaac64;
|
||||
|
||||
pub const Random = struct {
|
||||
fillFn: fn(r: &Random, buf: []u8) void,
|
||||
|
||||
/// Read random bytes into the specified buffer until fill.
|
||||
pub fn bytes(r: &Random, buf: []u8) void {
|
||||
r.fillFn(r, buf);
|
||||
}
|
||||
|
||||
/// Return a random integer/boolean type.
|
||||
pub fn scalar(r: &Random, comptime T: type) T {
|
||||
var rand_bytes: [@sizeOf(T)]u8 = undefined;
|
||||
r.bytes(rand_bytes[0..]);
|
||||
|
||||
if (T == bool) {
|
||||
return rand_bytes[0] & 0b1 == 0;
|
||||
} else {
|
||||
// NOTE: Cannot @bitCast array to integer type.
|
||||
return mem.readInt(rand_bytes, T, builtin.Endian.Little);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a random unsigned integer with even distribution between `start`
|
||||
/// inclusive and `end` exclusive.
|
||||
pub fn range(r: &Random, comptime T: type, start: T, end: T) T {
|
||||
assert(start <= end);
|
||||
if (T.is_signed) {
|
||||
const uint = @IntType(false, T.bit_count);
|
||||
if (start >= 0 and end >= 0) {
|
||||
return T(r.range(uint, uint(start), uint(end)));
|
||||
} else if (start < 0 and end < 0) {
|
||||
// Can't overflow because the range is over signed ints
|
||||
return math.negateCast(r.range(uint, math.absCast(end), math.absCast(start)) + 1) catch unreachable;
|
||||
} else if (start < 0 and end >= 0) {
|
||||
const end_uint = uint(end);
|
||||
const total_range = math.absCast(start) + end_uint;
|
||||
const value = r.range(uint, 0, total_range);
|
||||
const result = if (value < end_uint) x: {
|
||||
break :x T(value);
|
||||
} else if (value == end_uint) x: {
|
||||
break :x start;
|
||||
} else x: {
|
||||
// Can't overflow because the range is over signed ints
|
||||
break :x math.negateCast(value - end_uint) catch unreachable;
|
||||
};
|
||||
return result;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
} else {
|
||||
const total_range = end - start;
|
||||
const leftover = @maxValue(T) % total_range;
|
||||
const upper_bound = @maxValue(T) - leftover;
|
||||
var rand_val_array: [@sizeOf(T)]u8 = undefined;
|
||||
|
||||
while (true) {
|
||||
r.bytes(rand_val_array[0..]);
|
||||
const rand_val = mem.readInt(rand_val_array, T, builtin.Endian.Little);
|
||||
if (rand_val < upper_bound) {
|
||||
return start + (rand_val % total_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a floating point value evenly distributed in the range [0, 1).
|
||||
pub fn float(r: &Random, comptime T: type) T {
|
||||
// Generate a uniform value between [1, 2) and scale down to [0, 1).
|
||||
// Note: The lowest mantissa bit is always set to 0 so we only use half the available range.
|
||||
switch (T) {
|
||||
f32 => {
|
||||
const s = r.scalar(u32);
|
||||
const repr = (0x7f << 23) | (s >> 9);
|
||||
return @bitCast(f32, repr) - 1.0;
|
||||
},
|
||||
f64 => {
|
||||
const s = r.scalar(u64);
|
||||
const repr = (0x3ff << 52) | (s >> 12);
|
||||
return @bitCast(f64, repr) - 1.0;
|
||||
},
|
||||
else => @compileError("unknown floating point type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a floating point value normally distributed in the range [0, 1].
|
||||
pub fn floatNorm(r: &Random, comptime T: type) T {
|
||||
// TODO(tiehuis): See https://www.doornik.com/research/ziggurat.pdf
|
||||
@compileError("floatNorm is unimplemented");
|
||||
}
|
||||
|
||||
/// Return a exponentially distributed float between (0, @maxValue(f64))
|
||||
pub fn floatExp(r: &Random, comptime T: type) T {
|
||||
@compileError("floatExp is unimplemented");
|
||||
}
|
||||
|
||||
/// Shuffle a slice into a random order.
|
||||
pub fn shuffle(r: &Random, comptime T: type, buf: []T) void {
|
||||
if (buf.len < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < buf.len - 1) : (i += 1) {
|
||||
const j = r.range(usize, i, buf.len);
|
||||
mem.swap(T, &buf[i], &buf[j]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Generator to extend 64-bit seed values into longer sequences.
|
||||
//
|
||||
// The number of cycles is thus limited to 64-bits regardless of the engine, but this
|
||||
// is still plenty for practical purposes.
|
||||
const SplitMix64 = struct {
|
||||
s: u64,
|
||||
|
||||
pub fn init(seed: u64) SplitMix64 {
|
||||
return SplitMix64 { .s = seed };
|
||||
}
|
||||
|
||||
pub fn next(self: &SplitMix64) u64 {
|
||||
self.s +%= 0x9e3779b97f4a7c15;
|
||||
|
||||
var z = self.s;
|
||||
z = (z ^ (z >> 30)) *% 0xbf58476d1ce4e5b9;
|
||||
z = (z ^ (z >> 27)) *% 0x94d049bb133111eb;
|
||||
return z ^ (z >> 31);
|
||||
}
|
||||
};
|
||||
|
||||
test "splitmix64 sequence" {
|
||||
var r = SplitMix64.init(0xaeecf86f7878dd75);
|
||||
|
||||
const seq = []const u64 {
|
||||
0x5dbd39db0178eb44,
|
||||
0xa9900fb66b397da3,
|
||||
0x5c1a28b1aeebcf5c,
|
||||
0x64a963238f776912,
|
||||
0xc6d4177b21d1c0ab,
|
||||
0xb2cbdbdb5ea35394,
|
||||
};
|
||||
|
||||
for (seq) |s| {
|
||||
std.debug.assert(s == r.next());
|
||||
}
|
||||
}
|
||||
|
||||
// PCG32 - http://www.pcg-random.org/
|
||||
//
|
||||
// PRNG
|
||||
pub const Pcg = struct {
|
||||
const default_multiplier = 6364136223846793005;
|
||||
|
||||
random: Random,
|
||||
|
||||
s: u64,
|
||||
i: u64,
|
||||
|
||||
pub fn init(init_s: u64) Pcg {
|
||||
var pcg = Pcg {
|
||||
.random = Random { .fillFn = fill },
|
||||
.s = undefined,
|
||||
.i = undefined,
|
||||
};
|
||||
|
||||
pcg.seed(init_s);
|
||||
return pcg;
|
||||
}
|
||||
|
||||
fn next(self: &Pcg) u32 {
|
||||
const l = self.s;
|
||||
self.s = l *% default_multiplier +% (self.i | 1);
|
||||
|
||||
const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27);
|
||||
const rot = u32(l >> 59);
|
||||
|
||||
return (xor_s >> u5(rot)) | (xor_s << u5((0 -% rot) & 31));
|
||||
}
|
||||
|
||||
fn seed(self: &Pcg, init_s: u64) void {
|
||||
// Pcg requires 128-bits of seed.
|
||||
var gen = SplitMix64.init(init_s);
|
||||
self.seedTwo(gen.next(), gen.next());
|
||||
}
|
||||
|
||||
fn seedTwo(self: &Pcg, init_s: u64, init_i: u64) void {
|
||||
self.s = 0;
|
||||
self.i = (init_s << 1) | 1;
|
||||
self.s = self.s *% default_multiplier +% self.i;
|
||||
self.s +%= init_i;
|
||||
self.s = self.s *% default_multiplier +% self.i;
|
||||
}
|
||||
|
||||
fn fill(r: &Random, buf: []u8) void {
|
||||
const self = @fieldParentPtr(Pcg, "random", r);
|
||||
|
||||
var i: usize = 0;
|
||||
const aligned_len = buf.len - (buf.len & 7);
|
||||
|
||||
// Complete 4 byte segments.
|
||||
while (i < aligned_len) : (i += 4) {
|
||||
var n = self.next();
|
||||
comptime var j: usize = 0;
|
||||
inline while (j < 4) : (j += 1) {
|
||||
buf[i + j] = @truncate(u8, n);
|
||||
n >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Remaining. (cuts the stream)
|
||||
if (i != buf.len) {
|
||||
var n = self.next();
|
||||
while (i < buf.len) : (i += 1) {
|
||||
buf[i] = @truncate(u8, n);
|
||||
n >>= 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test "pcg sequence" {
|
||||
var r = Pcg.init(0);
|
||||
const s0: u64 = 0x9394bf54ce5d79de;
|
||||
const s1: u64 = 0x84e9c579ef59bbf7;
|
||||
r.seedTwo(s0, s1);
|
||||
|
||||
const seq = []const u32 {
|
||||
2881561918,
|
||||
3063928540,
|
||||
1199791034,
|
||||
2487695858,
|
||||
1479648952,
|
||||
3247963454,
|
||||
};
|
||||
|
||||
for (seq) |s| {
|
||||
std.debug.assert(s == r.next());
|
||||
}
|
||||
}
|
||||
|
||||
// Xoroshiro128+ - http://xoroshiro.di.unimi.it/
|
||||
//
|
||||
// PRNG
|
||||
pub const Xoroshiro128 = struct {
|
||||
random: Random,
|
||||
|
||||
s: [2]u64,
|
||||
|
||||
pub fn init(init_s: u64) Xoroshiro128 {
|
||||
var x = Xoroshiro128 {
|
||||
.random = Random { .fillFn = fill },
|
||||
.s = undefined,
|
||||
};
|
||||
|
||||
x.seed(init_s);
|
||||
return x;
|
||||
}
|
||||
|
||||
fn next(self: &Xoroshiro128) u64 {
|
||||
const s0 = self.s[0];
|
||||
var s1 = self.s[1];
|
||||
const r = s0 +% s1;
|
||||
|
||||
s1 ^= s0;
|
||||
self.s[0] = math.rotl(u64, s0, u8(55)) ^ s1 ^ (s1 << 14);
|
||||
self.s[1] = math.rotl(u64, s1, u8(36));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// Skip 2^64 places ahead in the sequence
|
||||
fn jump(self: &Xoroshiro128) void {
|
||||
var s0: u64 = 0;
|
||||
var s1: u64 = 0;
|
||||
|
||||
const table = []const u64 {
|
||||
0xbeac0467eba5facb,
|
||||
0xd86b048b86aa9922
|
||||
};
|
||||
|
||||
inline for (table) |entry| {
|
||||
var b: usize = 0;
|
||||
while (b < 64) : (b += 1) {
|
||||
if ((entry & (u64(1) << u6(b))) != 0) {
|
||||
s0 ^= self.s[0];
|
||||
s1 ^= self.s[1];
|
||||
}
|
||||
_ = self.next();
|
||||
}
|
||||
}
|
||||
|
||||
self.s[0] = s0;
|
||||
self.s[1] = s1;
|
||||
}
|
||||
|
||||
fn seed(self: &Xoroshiro128, init_s: u64) void {
|
||||
// Xoroshiro requires 128-bits of seed.
|
||||
var gen = SplitMix64.init(init_s);
|
||||
|
||||
self.s[0] = gen.next();
|
||||
self.s[1] = gen.next();
|
||||
}
|
||||
|
||||
fn fill(r: &Random, buf: []u8) void {
|
||||
const self = @fieldParentPtr(Xoroshiro128, "random", r);
|
||||
|
||||
var i: usize = 0;
|
||||
const aligned_len = buf.len - (buf.len & 7);
|
||||
|
||||
// Complete 8 byte segments.
|
||||
while (i < aligned_len) : (i += 8) {
|
||||
var n = self.next();
|
||||
comptime var j: usize = 0;
|
||||
inline while (j < 8) : (j += 1) {
|
||||
buf[i + j] = @truncate(u8, n);
|
||||
n >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Remaining. (cuts the stream)
|
||||
if (i != buf.len) {
|
||||
var n = self.next();
|
||||
while (i < buf.len) : (i += 1) {
|
||||
buf[i] = @truncate(u8, n);
|
||||
n >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test "xoroshiro sequence" {
|
||||
var r = Xoroshiro128.init(0);
|
||||
r.s[0] = 0xaeecf86f7878dd75;
|
||||
r.s[1] = 0x01cd153642e72622;
|
||||
|
||||
const seq1 = []const u64 {
|
||||
0xb0ba0da5bb600397,
|
||||
0x18a08afde614dccc,
|
||||
0xa2635b956a31b929,
|
||||
0xabe633c971efa045,
|
||||
0x9ac19f9706ca3cac,
|
||||
0xf62b426578c1e3fb,
|
||||
};
|
||||
|
||||
for (seq1) |s| {
|
||||
std.debug.assert(s == r.next());
|
||||
}
|
||||
|
||||
|
||||
r.jump();
|
||||
|
||||
const seq2 = []const u64 {
|
||||
0x95344a13556d3e22,
|
||||
0xb4fb32dafa4d00df,
|
||||
0xb2011d9ccdcfe2dd,
|
||||
0x05679a9b2119b908,
|
||||
0xa860a1da7c9cd8a0,
|
||||
0x658a96efe3f86550,
|
||||
};
|
||||
|
||||
for (seq2) |s| {
|
||||
std.debug.assert(s == r.next());
|
||||
}
|
||||
}
|
||||
|
||||
// ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html
|
||||
//
|
||||
// CSPRNG
|
||||
//
|
||||
// Follows the general idea of the implementation from here with a few shortcuts.
|
||||
// https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html
|
||||
pub const Isaac64 = struct {
|
||||
random: Random,
|
||||
|
||||
r: [256]u64,
|
||||
m: [256]u64,
|
||||
a: u64,
|
||||
b: u64,
|
||||
c: u64,
|
||||
i: usize,
|
||||
|
||||
pub fn init(init_s: u64) Isaac64 {
|
||||
var isaac = Isaac64 {
|
||||
.random = Random { .fillFn = fill },
|
||||
.r = undefined,
|
||||
.m = undefined,
|
||||
.a = undefined,
|
||||
.b = undefined,
|
||||
.c = undefined,
|
||||
.i = undefined,
|
||||
};
|
||||
|
||||
// seed == 0 => same result as the unseeded reference implementation
|
||||
isaac.seed(init_s, 1);
|
||||
return isaac;
|
||||
}
|
||||
|
||||
fn step(self: &Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void {
|
||||
const x = self.m[base + m1];
|
||||
self.a = mix +% self.m[base + m2];
|
||||
|
||||
const y = self.a +% self.b +% self.m[(x >> 3) % self.m.len];
|
||||
self.m[base + m1] = y;
|
||||
|
||||
self.b = x +% self.m[(y >> 11) % self.m.len];
|
||||
self.r[self.r.len - 1 - base - m1] = self.b;
|
||||
}
|
||||
|
||||
fn refill(self: &Isaac64) void {
|
||||
const midpoint = self.r.len / 2;
|
||||
|
||||
self.c +%= 1;
|
||||
self.b +%= self.c;
|
||||
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < midpoint) : (i += 4) {
|
||||
self.step( ~(self.a ^ (self.a << 21)), i + 0, 0, midpoint);
|
||||
self.step( self.a ^ (self.a >> 5) , i + 1, 0, midpoint);
|
||||
self.step( self.a ^ (self.a << 12) , i + 2, 0, midpoint);
|
||||
self.step( self.a ^ (self.a >> 33) , i + 3, 0, midpoint);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < midpoint) : (i += 4) {
|
||||
self.step( ~(self.a ^ (self.a << 21)), i + 0, midpoint, 0);
|
||||
self.step( self.a ^ (self.a >> 5) , i + 1, midpoint, 0);
|
||||
self.step( self.a ^ (self.a << 12) , i + 2, midpoint, 0);
|
||||
self.step( self.a ^ (self.a >> 33) , i + 3, midpoint, 0);
|
||||
}
|
||||
}
|
||||
|
||||
self.i = 0;
|
||||
}
|
||||
|
||||
fn next(self: &Isaac64) u64 {
|
||||
if (self.i >= self.r.len) {
|
||||
self.refill();
|
||||
}
|
||||
|
||||
const value = self.r[self.i];
|
||||
self.i += 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
fn seed(self: &Isaac64, init_s: u64, comptime rounds: usize) void {
|
||||
// We ignore the multi-pass requirement since we don't currently expose full access to
|
||||
// seeding the self.m array completely.
|
||||
mem.set(u64, self.m[0..], 0);
|
||||
self.m[0] = init_s;
|
||||
|
||||
// prescrambled golden ratio constants
|
||||
var a = []const u64 {
|
||||
0x647c4677a2884b7c,
|
||||
0xb9f8b322c73ac862,
|
||||
0x8c0ea5053d4712a0,
|
||||
0xb29b2e824a595524,
|
||||
0x82f053db8355e0ce,
|
||||
0x48fe4a0fa5a09315,
|
||||
0xae985bf2cbfc89ed,
|
||||
0x98f5704f6c44c0ab,
|
||||
};
|
||||
|
||||
comptime var i: usize = 0;
|
||||
inline while (i < rounds) : (i += 1) {
|
||||
var j: usize = 0;
|
||||
while (j < self.m.len) : (j += 8) {
|
||||
comptime var x1: usize = 0;
|
||||
inline while (x1 < 8) : (x1 += 1) {
|
||||
a[x1] +%= self.m[j + x1];
|
||||
}
|
||||
|
||||
a[0] -%= a[4]; a[5] ^= a[7] >> 9; a[7] +%= a[0];
|
||||
a[1] -%= a[5]; a[6] ^= a[0] << 9; a[0] +%= a[1];
|
||||
a[2] -%= a[6]; a[7] ^= a[1] >> 23; a[1] +%= a[2];
|
||||
a[3] -%= a[7]; a[0] ^= a[2] << 15; a[2] +%= a[3];
|
||||
a[4] -%= a[0]; a[1] ^= a[3] >> 14; a[3] +%= a[4];
|
||||
a[5] -%= a[1]; a[2] ^= a[4] << 20; a[4] +%= a[5];
|
||||
a[6] -%= a[2]; a[3] ^= a[5] >> 17; a[5] +%= a[6];
|
||||
a[7] -%= a[3]; a[4] ^= a[6] << 14; a[6] +%= a[7];
|
||||
|
||||
comptime var x2: usize = 0;
|
||||
inline while (x2 < 8) : (x2 += 1) {
|
||||
self.m[j + x2] = a[x2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mem.set(u64, self.r[0..], 0);
|
||||
self.a = 0;
|
||||
self.b = 0;
|
||||
self.c = 0;
|
||||
self.i = self.r.len; // trigger refill on first value
|
||||
}
|
||||
|
||||
fn fill(r: &Random, buf: []u8) void {
|
||||
const self = @fieldParentPtr(Isaac64, "random", r);
|
||||
|
||||
var i: usize = 0;
|
||||
const aligned_len = buf.len - (buf.len & 7);
|
||||
|
||||
// Fill complete 64-byte segments
|
||||
while (i < aligned_len) : (i += 8) {
|
||||
var n = self.next();
|
||||
comptime var j: usize = 0;
|
||||
inline while (j < 8) : (j += 1) {
|
||||
buf[i + j] = @truncate(u8, n);
|
||||
n >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill trailing, ignoring excess (cut the stream).
|
||||
if (i != buf.len) {
|
||||
var n = self.next();
|
||||
while (i < buf.len) : (i += 1) {
|
||||
buf[i] = @truncate(u8, n);
|
||||
n >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test "isaac64 sequence" {
|
||||
var r = Isaac64.init(0);
|
||||
|
||||
// from reference implementation
|
||||
const seq = []const u64 {
|
||||
0xf67dfba498e4937c,
|
||||
0x84a5066a9204f380,
|
||||
0xfee34bd5f5514dbb,
|
||||
0x4d1664739b8f80d6,
|
||||
0x8607459ab52a14aa,
|
||||
0x0e78bc5a98529e49,
|
||||
0xfe5332822ad13777,
|
||||
0x556c27525e33d01a,
|
||||
0x08643ca615f3149f,
|
||||
0xd0771faf3cb04714,
|
||||
0x30e86f68a37b008d,
|
||||
0x3074ebc0488a3adf,
|
||||
0x270645ea7a2790bc,
|
||||
0x5601a0a8d3763c6a,
|
||||
0x2f83071f53f325dd,
|
||||
0xb9090f3d42d2d2ea,
|
||||
};
|
||||
|
||||
for (seq) |s| {
|
||||
std.debug.assert(s == r.next());
|
||||
}
|
||||
}
|
||||
|
||||
// Actual Random helper function tests, pcg engine is assumed correct.
|
||||
test "Random float" {
|
||||
var prng = DefaultPrng.init(0);
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < 1000) : (i += 1) {
|
||||
const val1 = prng.random.float(f32);
|
||||
std.debug.assert(val1 >= 0.0);
|
||||
std.debug.assert(val1 < 1.0);
|
||||
|
||||
const val2 = prng.random.float(f64);
|
||||
std.debug.assert(val2 >= 0.0);
|
||||
std.debug.assert(val2 < 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
test "Random scalar" {
|
||||
var prng = DefaultPrng.init(0);
|
||||
const s = prng .random.scalar(u64);
|
||||
}
|
||||
|
||||
test "Random bytes" {
|
||||
var prng = DefaultPrng.init(0);
|
||||
var buf: [2048]u8 = undefined;
|
||||
prng.random.bytes(buf[0..]);
|
||||
}
|
||||
|
||||
test "Random shuffle" {
|
||||
var prng = DefaultPrng.init(0);
|
||||
|
||||
var seq = []const u8 { 0, 1, 2, 3, 4 };
|
||||
var seen = []bool {false} ** 5;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < 1000) : (i += 1) {
|
||||
prng.random.shuffle(u8, seq[0..]);
|
||||
seen[seq[0]] = true;
|
||||
std.debug.assert(sumArray(seq[0..]) == 10);
|
||||
}
|
||||
|
||||
// we should see every entry at the head at least once
|
||||
for (seen) |e| {
|
||||
std.debug.assert(e == true);
|
||||
}
|
||||
}
|
||||
|
||||
fn sumArray(s: []const u8) u32 {
|
||||
var r: u32 = 0;
|
||||
for (s) |e| r += e;
|
||||
return r;
|
||||
}
|
||||
|
||||
test "Random range" {
|
||||
var prng = DefaultPrng.init(0);
|
||||
testRange(&prng.random, -4, 3);
|
||||
testRange(&prng.random, -4, -1);
|
||||
testRange(&prng.random, 10, 14);
|
||||
}
|
||||
|
||||
fn testRange(r: &Random, start: i32, end: i32) void {
|
||||
const count = usize(end - start);
|
||||
var values_buffer = []bool{false} ** 20;
|
||||
const values = values_buffer[0..count];
|
||||
var i: usize = 0;
|
||||
while (i < count) {
|
||||
const value = r.range(i32, start, end);
|
||||
const index = usize(value - start);
|
||||
if (!values[index]) {
|
||||
i += 1;
|
||||
values[index] = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,507 +0,0 @@
|
|||
pub const mt64_seed = 0xb334e49d0977c37e;
|
||||
pub const mt64_data = []u64 {
|
||||
0x2ad1a63fab6d25a9, 0xb7143aba12569814, 0xc1b60d8d49e53e8, 0x5652adfc8da656dc,
|
||||
0x43e3beb6d9e484a9, 0x17b09b71e9418ff7, 0x541646292686cfa4, 0x260457071268ecfc,
|
||||
0x1627af31774e1dc1, 0x362a49b34ed75bb3, 0x7acf72002fe0f733, 0x3aaaf2e7b9409452,
|
||||
0x9cfc2d9908115c2, 0x6e81a7f16ae613e9, 0xfc4da89c04acf3c7, 0x6984b6adb4feb9ae,
|
||||
0x6a128b334e27b03d, 0xcc45a2b02937871a, 0xe585b229e00b2283, 0x7a92c0664a6f678a,
|
||||
0x972735011bdc0744, 0xb494e743d658a084, 0x1eda3c4e7b1b2d0c, 0x4c7adb3831d87332,
|
||||
0x12f8c7355f5ec631, 0xfc2bcb6be7d60eba, 0x74b95b47895f8687, 0x171de6fe92b97f5a,
|
||||
0x86383730f52719ac, 0xe4e43ce0f61274f6, 0x514f7e072d96f19c, 0xabef324fbc6cb7fe,
|
||||
0x7534b945b742f14f, 0x47f9efe33265adbe, 0x7bcab027a0abf16b, 0x3312a2b34225bff7,
|
||||
0xb61455ce8c2e3e0b, 0x2b81008deeee4d94, 0x743b0b2b4974c7b6, 0xfc219101fd665f7d,
|
||||
0x863d78891cbfe5e5, 0x7531bb1839181778, 0xf614359a65356e72, 0xfcdd1f6e3f250bdd,
|
||||
0x528e10bd536eed5b, 0x9f69386ac60cd618, 0xbf5f242706817f01, 0x8e7da8070072cf64,
|
||||
0xaa318b622da0667f, 0xc9580540fb7efd66, 0xb5d996deccd02e0e, 0x81c6a799ea3f5a41,
|
||||
0xe6b896f4e21a8550, 0xde5206f177a24ceb, 0x53343e81639ec0b6, 0x5a6edb63d08be9f6,
|
||||
0x3602c2892f7da1b9, 0xda84f4259841bdea, 0x5880e169a7746e45, 0x57cddb5ffd3c2423,
|
||||
0x28fe1166fe7b8595, 0x92136c9decb42243, 0xa8c4199818ca7d62, 0x5042cd96f62854dd,
|
||||
0x22b2d38c3f21f8d0, 0x73a2bfccf1b5f7bb, 0xaba3718f40b6984e, 0x9c1f5dc3e399f5f0,
|
||||
0x9cf463f95369c149, 0xa7546d69e4232e18, 0x9ea57317d19ab7fc, 0xfb13c83d830731fb,
|
||||
0x635a123eaa099259, 0x9a2fe7d0ba6e3c5c, 0x40b903cd0d0d3b4e, 0xc8210eb2d2e941cb,
|
||||
0xd2582d4b1e016484, 0x1d048030875af39c, 0xb51c31a6c193d76f, 0x5ce9b801b8d61626,
|
||||
0x2bae30455cbb0022, 0xba54df5998b2443f, 0x927abb9342c9a90a, 0xc431eb7df3e06727,
|
||||
0x726f885d3da88d5, 0x7d85ff1ca4260280, 0xaf3fecf8f019817, 0x31d39105d6fc4fe8,
|
||||
0x262d9842dbafd4bd, 0x54c28a2876e62e39, 0x95a986e24e214dde, 0xbf677a1abd2e553,
|
||||
0x48ac890ff787b2b6, 0x2890ec1c67c539f4, 0x7ce88bf3975882c3, 0x88ef340414a29c88,
|
||||
0xe30de9c88a00805b, 0xe772225e3ee6c68a, 0xa3a7d0921c5d5816, 0x8354957227b1663f,
|
||||
0xb5b65ded7c747cbc, 0x93b4a12ff2e8fcea, 0x6359579c3c438b3c, 0x45b2c12e9722f2bd,
|
||||
0x659a604414f19e1, 0x4ec9a149d4219ca5, 0xd830290dd6aebe2b, 0xabc7874a6b4827f8,
|
||||
0xc91be5dd875847e7, 0x5b761d39f3f96aee, 0x5749dffad692b6c8, 0x86c94840cbd249d2,
|
||||
0x411a466e886ad7, 0x27dca1f51aebb9a0, 0x1cceb093fbab7a42, 0x7140c2d6706e927c,
|
||||
0x6881fdb87299a92f, 0xa81a28de171f3c47, 0x8fa9a1b3bb5dfb2a, 0xae076853e3e0abde,
|
||||
0xe76572308ecd6b54, 0x6cd926c2e2760d8a, 0xdf080266cfbe3dc3, 0xb99b961999765d7b,
|
||||
0xadb5d4e2b896ddf1, 0x8d3aaf4c83c83c56, 0x9b66e4f6eb65bef7, 0x7a81c3bf785eb1df,
|
||||
0xc53f02b3e8c38647, 0xcdfeb25ee787759d, 0xead5e734d64ab5f6, 0x7930d87af1072499,
|
||||
0xf30690a71d88ad6e, 0x73c347923c84728a, 0x3f2b588221003fe4, 0xe747052d0b453af2,
|
||||
0xabe6fa70539b5edf, 0x4db6d1530d628c2, 0x4ec929af434eb1b0, 0x15afbe39886181ff,
|
||||
0xa9141b9c89a07b80, 0x7d33f966c6232057, 0x8ddcb412f34a491d, 0xa74472b8ecc1e2f2,
|
||||
0x34d745de1cb7de2e, 0x6cf67091309e5e93, 0xfb25004efa59450a, 0x3355947066522286,
|
||||
0x5a8cffdf079dac21, 0x419445d6e6825887, 0x6e9c064f84381dcf, 0xbbcaf462a3a8ad76,
|
||||
0x75836c68d6c1a13e, 0xf38141565d5c3759, 0x8c65989142ffa802, 0x106067ec26e6463,
|
||||
0xadfac5e3a80de9c2, 0xc48b16e2df25b9f2, 0xa3257889c33669e6, 0x5bc760d4d65a1745,
|
||||
0x303cd31fead81139, 0xfb97f78cade31e1d, 0xb888b8e05820a469, 0x7ebb8e44d47f54d9,
|
||||
0xce76cdbb5ecdc529, 0xb1eb29949a099d52, 0xb6affc1b240a7eb3, 0x22977eac542906f1,
|
||||
0x9b105b391ff729df, 0x83371186b2834968, 0xd5b893f382ea9e90, 0x5d17aa80a1fd4854,
|
||||
0xe8ed8525eb29210c, 0xc789f3cd36c4dae3, 0x556e50a5f46b73fb, 0xef1d129b523dff77,
|
||||
0x851de0f53f6707f9, 0x8deeeadfb1fa8bfc, 0x3d8c89c0e08c4f2e, 0xecfaaea537123333,
|
||||
0x5bc9053d2dfd7669, 0x408c5bb2e880a9a2, 0x495726b3f3248219, 0x2b23cca4a6ea1ccc,
|
||||
0x1df3663045092d61, 0xaf977a46e965e45b, 0x43a2facfff7f97e, 0x9b7714344c7b51e,
|
||||
0x35643b24efb0559a, 0x502820785dc1af13, 0xbf82d2775b46433d, 0x1db626f2e16ca66,
|
||||
0x744b031447c1e27d, 0x99e79898612f4606, 0xda02a728d234821f, 0xcf00c6fbb637a6e9,
|
||||
0x242f2963196fd8b, 0x7aed8efc2dd562bb, 0x6204fb5d3dc6208a, 0x3e84861182fc7f6,
|
||||
0xd14c4ee5aeef5c7a, 0x3749fbef94378dc1, 0x8fe710ec5cfc8566, 0xf43d7e495d5384d7,
|
||||
0x8ff6396f1f1ce7c4, 0xf1252a6b6f86b42c, 0xddbbd098d6dca83f, 0x4e228724a227232a,
|
||||
0x92a5a52ba2b24fa8, 0xdfc172b03fde669c, 0x34ee55adf7f0711c, 0x21d181e79b8000bc,
|
||||
0xc788b1f48b37b693, 0x544fc4cfed0e0f92, 0xafca0c6de41789cc, 0xbb37bb5107ef97f8,
|
||||
0xb9d62bf1dc0f6c95, 0xc78b5a36110dfb1, 0x1d615b658f39657e, 0x2bb2cd04cabcc360,
|
||||
0xe563488ece6362f0, 0x213b56ce006fecc6, 0xc38207089fed0270, 0xa33199ff4a51d095,
|
||||
0x1802ad28fb1896b1, 0xbead8f18c164a332, 0xceb5149101aa450f, 0x39ad89851ee8b62a,
|
||||
0x317229aafabf37c4, 0xee68b8b9bf3520b3, 0xe4db499288350f04, 0xf8ea27feddb0ae7a,
|
||||
0xa17235067b489c42, 0xbf4a570245f95d78, 0x8065c67e1d1537ab, 0xbd9357fb1b30aee5,
|
||||
0xc224166ebeb24c42, 0xf9baf8ccd01b53bf, 0x5c13775c3fea8038, 0x4ea66f6d650ce62d,
|
||||
0x470592ed81c140f2, 0xc2d0eb6f7999321f, 0x85d762f20290dc0c, 0x9f7d0d13936f6e78,
|
||||
0x41f1fd2d20f2d62d, 0x891cb19ce1af2c2f, 0xe7ff34c3b29c3719, 0x246743f43126c69c,
|
||||
0xea4b2da3195ebab9, 0x4831e4de995187dc, 0x7fb8969bbee45ce5, 0xe35b483da73c44ed,
|
||||
0xf89158ca9af36227, 0x9fc7f34a35469a7a, 0xd02483ebca6564e7, 0xca00da156aaeda03,
|
||||
0x303d1514646822f1, 0x226832ae582b8eac, 0xf772d719e413504e, 0x87603b928c068ab1,
|
||||
0x4dc1552230e9b883, 0xef8c5e9db946fc87, 0x935581290bf7a4ee, 0xca632d2c7674bf2,
|
||||
0xd8a3933b80d39efd, 0xf026574d0ffee6fb, 0xe4412d0dcd2fe94f, 0x668916490a2983ec,
|
||||
0x73b1fe84a995718, 0x729bedefe21cc0e7, 0xd3a770f1c683b98f, 0x5d597a96323a10c2,
|
||||
0xfbb7834bbf5fed23, 0xf3546c805a42ccdd, 0x9ef3e2164bb31a0c, 0x388363ce6c6c2253,
|
||||
0x8120f4a949f017cb, 0x925a61942bbd3d10, 0xa03182d8599c0521, 0x2412e23004b40ebb,
|
||||
0x35010a126bf2aecc, 0x21147869a1a84ca7, 0x53cba503b6127b98, 0x10c89dd62ab3591c,
|
||||
0xf4c7f84faaf9f5f1, 0x8b4a37a2e844b97b, 0x23ddeb236a0bd9af, 0x4fd51d7207f49e62,
|
||||
0x6cdab447c27706b2, 0x9e8f54b9a2d1a790, 0x191aed85d4d77087, 0xf74ecf5015265af6,
|
||||
0x45925e25404922a1, 0xcf5467a0f5b42b98, 0x73590809c85c728c, 0xc16beeda74a1a1b8,
|
||||
0xc3bbe7999803dd6a, 0x1a368bb32eec184, 0xafad2d86b7bb574f, 0xdc7c7b8960dc921a,
|
||||
0xd9b68d854f5e0ae1, 0xe9e1a6a16efe0bea, 0x304a15bd6ca1cb14, 0x713ce3144e0af4b9,
|
||||
0xc50eb410981be1d2, 0xb0fba6119bf7a300, 0x7a107296731fd314, 0xdd764898a90042b3,
|
||||
0x8a69973262da3bc8, 0x29c6b9d048596c44, 0x62581bd20da76f1b, 0x4d1a3941d6d3e4bb,
|
||||
0x19c447306245055, 0xb2978afcd04ba357, 0x7c01cdefcfe24432, 0xc4b268314411deae,
|
||||
0x5ba56d49da714765, 0x33299186ac6dfd09, 0xede087aec096ef0d, 0xf758da2c7bcf9ddb,
|
||||
0x5ea6c40d56824cdd, 0x121ff879d6ba905b, 0xb5fed0c42b616f5c, 0x21029cdc347de152,
|
||||
0xb251d93f4cd7bf4a, 0xaedcb2dc6402cf13, 0x840e5e0d96e89407, 0x6a92fd328efcc6ef,
|
||||
0xc63f5d8f6fcadcbd, 0x405bd64d1621128e, 0xe1318888172f58ed, 0x1009c9764d49da2b,
|
||||
0x4bc0592cbfdf9f91, 0x972f0e080dfecd02, 0xa1cb961958eeb6aa, 0x6ee6467ad8c20aca,
|
||||
0xb3f1c738390d6a83, 0x6504eb7ac498650d, 0xdf7dda67f198f59f, 0x72615652e56a82c3,
|
||||
0x85e0fa2dfb51755a, 0x98b1f92a3d2ad940, 0xce81d51d875c0045, 0x437004d0be0a4d69,
|
||||
0x64065895526f896c, 0xe1e1fea920785d49, 0x7d507ffd56fda19a, 0x17309b625cecb42,
|
||||
0x67b6d83f0fd0f572, 0x5178665a5bcd38f4, 0x3c49fda2d35a8606, 0x7f058d2cb0ad351c,
|
||||
0xfb95691559245416, 0xc991b857662b1b9f, 0x9e6d0f4e19774f96, 0x26cc7502212ca578,
|
||||
0x3466110f03225e49, 0x2ae4958375eab9f3, 0x939a8d94c8871191, 0xa27356548ac6b28d,
|
||||
0xacf86d43ec3aa030, 0xe0d16c7fa0b13a8c, 0x408ec2b2f8da531b, 0xde72494115ee4e83,
|
||||
0xd26d7f79a02c5b1e, 0x2b6f835520c97f6a, 0x1f30f008b109ae7d, 0x698dbf9acaa222f1,
|
||||
0xbd55de40838376d5, 0xebe53822cec7eb80, 0x7ce900793008d2bc, 0x494fc7d10e8331bb,
|
||||
0x53509b90bdb7d588, 0x62aa920e9554b2f2, 0xe103098542011b6f, 0xeb722b9523d68af8,
|
||||
0xb71b1a6ea2c6591b, 0x97cd7da55c940270, 0x8ab70184427e45dc, 0x6cb6907427808465,
|
||||
0xf69232a42dbe7475, 0xbc9816d429fa8909, 0xef1e74244539d41b, 0x5569b6d10440d5c,
|
||||
0x7dda6817985c8ef5, 0xb270ed19cc8161b3, 0x87d80b66c8a15db0, 0x96966684c3ba47fb,
|
||||
0x996669bc87aff3bf, 0x17c015383c793f2, 0xa4b5de41fc69f61a, 0x14a0eb4e3055742f,
|
||||
0x5f0da5a7a2b8bc79, 0x5fe0353728aac023, 0x1554daf4abe92eed, 0x545722dac774b6a4,
|
||||
0x733fc54174f2e0d1, 0x3478ef85dd994316, 0x58ba2ac090ef7575, 0xa66b9b0cbc77b6e2,
|
||||
0xdc78cab5c708a3ee, 0x97a30c27be510f61, 0xb95d0b06fc0910b6, 0xdc80bdc42f79a25f,
|
||||
0x5cadc438e65b3070, 0x6263df49ce691b9c, 0xa7ce160daa64416b, 0xa4f8bfedb57288c1,
|
||||
0xa51714e187bbcfe9, 0xe25df4e9fc44644c, 0xeaf1854b3116ce11, 0xde1b8f810991a604,
|
||||
0xd4fc2e365e99be4b, 0x8d1b0d2799527e06, 0x7cac59eaf46baba, 0x4fa63c74d2dabaf1,
|
||||
0x4f6c5d5e676733a6, 0x7ab7ddb9c1b789b7, 0x5d9beb4877a37034, 0x5e96bc9de3985bcb,
|
||||
0x72bab4dcb75b3228, 0xfa40f33c4d799e1f, 0x73e6f61a69984a6b, 0x7499c9af466cf22f,
|
||||
0x42fab9136bfa64dd, 0xd5e8e39513b6fffd, 0x8eda1fd5ad8cd51d, 0x95338744859dff44,
|
||||
0x4f0c5e5ae768c729, 0x5bc92c60495ae348, 0xcbb48c170ac21168, 0x8374aa2440eeb138,
|
||||
0x70b663d6f6d70ca9, 0x11264ca6dd79e5a0, 0xf058a2c156974514, 0x36820eefc435ba63,
|
||||
0xd7b69f3d0c0c27a1, 0xe1a2eddf3b41d205, 0x80508ec93b038bc8, 0xd7e0429bd511ff37,
|
||||
0x7bf55a4e87e183b8, 0x4cb370ce7edb4bea, 0x26fbd0b31dcef45b, 0xf7acbd6781419fa6,
|
||||
0xf7849659f05c90c, 0xb686271ea57a47c6, 0x16f3f839dbfb4e1b, 0x906872b08b2c61a,
|
||||
0xc30c86d0a0203c15, 0xdaf238a6aa4fc9f7, 0x2399aa09ad2c069a, 0xf133c3aca703f545,
|
||||
0x868a10304a1c98ba, 0x60ef0607f46f7e90, 0xe4e69f26931e11a5, 0x487b8f6bd6d92941,
|
||||
0xd10cb2971798c0c7, 0x7126d81aa4bd0106, 0xcb620311dc84ab82, 0x26f8734a7a356bb2,
|
||||
0xcf9b9eeb02ee978c, 0x8a9ff0285d4d6b30, 0xe30e3b957e2cc7f8, 0x7c15a09e4d275809,
|
||||
0xdf723ae1dda5d167, 0xac212e4f6264bba2, 0xe3d60920ed983308, 0x91b403cbc91e290c,
|
||||
0xcdb905b012aff7c7, 0xb5ee73d45f900897, 0xafacc7cd7f5d52e7, 0xa8653272621165d6,
|
||||
0x90e2efc485ddd0d1, 0x56ef1ca9097b1a96, 0xc7a20f85777eb0a, 0xf4cec0271a50eae9,
|
||||
0x21acd76442024973, 0x19a46be82a4abdbe, 0x1bc12e0b8bf41fbb, 0x3766fe3d8e5119f2,
|
||||
0x7fea355c4c18e8ad, 0xd08b496a24fb8017, 0xe1a7cfaa0877aa2, 0xd37ad9e2a2a3fa96,
|
||||
0xaa362ba0e696f679, 0x7e7de89142c3aca5, 0x2dedb0842a3575b8, 0xb74c1e1d9082fe5b,
|
||||
0xb1ae74323699140f, 0x73623f80c727a6ea, 0x132ed204b0f10441, 0xc3e6ebe8ffc252bf,
|
||||
0x5f15cffb8286dce0, 0x66dab32df780fa8f, 0xeb00da7b25ea99e4, 0x113ad2448fafb671,
|
||||
0xee065c10a8f1924f, 0x2fcb7367cc01fe5, 0x484338f5c2d0aacd, 0xecfacd785e42d7a6,
|
||||
0x11513f845de8af3a, 0xd11be6b29054de0d, 0x2536e5d2856af9b7, 0x60ab519760acd4c4,
|
||||
0x6bfe010250a831ac, 0xb28a93e44b53b21b, 0x281cf9b233858583, 0x4ca6139abc79a710,
|
||||
0x5717d33616d77a95, 0x9ba52d2b7dfe71b5, 0x32e1c543476aa17d, 0xae242cc75806b7fa,
|
||||
0xa1415cb8fde770da, 0x3956c67542dc004d, 0x3a6f51518fdd20ce, 0x448c848f6c936d93,
|
||||
0x8fec38ff51bb5fab, 0x7463816cfc0754ac, 0x83b38e531ba39e73, 0xf9fc84dcf4f8c93e,
|
||||
0xdf20dbe8b91c3d6f, 0xec65939ac9516f9a, 0x888346f6c1aaa94a, 0xe42cabb108f60d95,
|
||||
0x39bb2e46b0599fa9, 0x529335ed75acba9e, 0x6a8767c5d00776c4, 0x8243346104fe61a5,
|
||||
0x7a2bd0339b3e9bac, 0xb68ccdf14473f4ba, 0xa06f389531ab553a, 0xc7c6f074fc2882d3,
|
||||
0x50a5fd6e6d0df962, 0xd7c0000d194139b7, 0x5ae27ef4033f873d, 0x4e7abe8a6d3570f8,
|
||||
0x27011ccd3885e709, 0x3dae53f7b7a8924a, 0xa9086c9b2b86fb71, 0xa3f9e534a399e62c,
|
||||
0x9f2f0379f9a33ec6, 0xceb51af95d4472bd, 0x15aa534182f8465, 0x96373b9cd28a627b,
|
||||
0x9fdce0ad99d41907, 0x2755bb0f52b8c239, 0x2f2e241b6aa7d243, 0xf040afbacb2f6001,
|
||||
0x552267c5f8b4c1b0, 0x22bfb3f0b58f9e48, 0x6bff8de368dbee3a, 0x652025c63d4069ce,
|
||||
0x743eb697b25f9c90, 0x8a3742c9dbf67b1c, 0xaf3bcfc260d7e69, 0x491facaa59b7e1d,
|
||||
0xd07d0761e6535fcd, 0x79ef09d9d3859232, 0xe0e0a013317d9207, 0x4b94baecf6fe4b4e,
|
||||
0x574576ed054cfc38, 0x90e90edd1a26f0aa, 0x616d32a371af78c6, 0x392cea9c34ffb0a8,
|
||||
0x692a6e730c33ac6f, 0x9e4b92ef425b78a6, 0x291d4c962d2f3e7c, 0x5f0f8ebb67f308fc,
|
||||
0xc1a0faf70ec747a3, 0x641da9550cd89392, 0x6adbe38115f09648, 0x3a51980fb324da0d,
|
||||
0xee894f2b5c17a380, 0x58788fa693f767c9, 0xc9f490ff5d88c4c0, 0x2ca6e8b204aa3070,
|
||||
0x7693837d7910c40c, 0x9240e04f16051720, 0xab773072d922faa2, 0xacb6892db8362872,
|
||||
0x5ac8b4d130613c0e, 0x65cd5ac259c653cb, 0x9647e0276864f3f8, 0xe207c57ee9237a53,
|
||||
0xe41f2663482a8fdb, 0xf605931fce8b95bd, 0x5b0247a9009255d2, 0xae949df3ab5a4ab5,
|
||||
0xc0e912dacbb72c42, 0x4226971e6351f33a, 0xd98d41f9f0fdb6c3, 0x64bf6a0af66782d5,
|
||||
0xb4ca689b3959bf46, 0xb4cbf621b4970922, 0xe9f27469c2412fe6, 0xb529d406d27c785e,
|
||||
0xf33be21f2672ec78, 0x34b3c4c4603656cd, 0x4a7efe099a0ae9d1, 0xa6ad5b909667945b,
|
||||
0xe20850e47ab440f7, 0x68739e1b4fa069e5, 0x4c191300a9207cf4, 0x9b613a1a38160c98,
|
||||
0x2cc631eb015081c9, 0x52af80aad7b676f4, 0x904b38943300ca8f, 0x50a515c0d302620f,
|
||||
0x52ae95b8a0c1f36c, 0x6a62ab87786774e2, 0x17ea45367ad58985, 0x92959c57166aa21b,
|
||||
0x2a9d91bd1a9716b4, 0x552c2f8528220174, 0xd665856c96b47d17, 0x14c6cba13b6dca3b,
|
||||
0x865f1c93ec9000d0, 0x23dda8b5e161910e, 0xc191ac4342717953, 0x6783fdb95e098f1d,
|
||||
0x1a4c26a5cad1e8ee, 0xde96e2a1e33e3046, 0xa57e65bedcf9047e, 0xa1cdef163fe7f80c,
|
||||
0xeb0abcc13feeb7b2, 0xaa158c61b0e44470, 0x98dc901679caf85b, 0x954046758f8d2e96,
|
||||
0x56db5e99c5d8c68, 0x7bb8d36962a4ac81, 0x2e301b4ecb03821, 0x121c1b2df70f0e1c,
|
||||
0x3fbdad1e8faa0543, 0x6efb222398a2f88b, 0x65760de4d96338e0, 0x15b2c58a67fb43fa,
|
||||
0x22f1532c89367d77, 0x1f726ffdad411d9e, 0x42572b54dcedf3ba, 0x3f8e0f6e9f0bbb7b,
|
||||
0x4b0e705c86571a1c, 0xf8c9b04f8bd75117, 0x67ed2e9e4545557, 0x57e8853f681bbf8c,
|
||||
0x6f99d1fd5fdbf582, 0xd2aa9bd48ad692e2, 0x2efc88bddecfe616, 0x8e9779a1e119abf4,
|
||||
0x52dfee4722a20b75, 0x79465be3aea146d6, 0x588997dbcbd5f005, 0x79bda8bcc5d4c650,
|
||||
0x2384e131ed3b5330, 0x229cb1d89738aa26, 0x1526d1a020a96507, 0xc7b6ac961a740cb9,
|
||||
0xe78cec14478c4f71, 0xaba61cfd8f1e2a71, 0x24123c8a37ae66f3, 0xbad8b07709aa215d,
|
||||
0x7896fa53fd2418e7, 0x72265842e8c4f955, 0x9f6331fd80527661, 0x7c649eeddb382c9d,
|
||||
0xd3b0708dd6b5ae84, 0xeda51f244551e15, 0xb7822c860b93dd44, 0xdf9afcc3c9cac88f,
|
||||
0x9244b9816573be70, 0xf3d103887cc4ce2d, 0xb3295c2e5bcb0218, 0x85243b7a2e0af441,
|
||||
0xbffd3df508d06098, 0x4908b967f6765c12, 0x8886ac94c0dabb, 0x9c7865af133eb6c0,
|
||||
0x5946fee66e64d7b2, 0x7737bc5af713087f, 0xbec815a80782dd5d, 0xe7672cfe0f7945fb,
|
||||
0xe1f80b6df5beece1, 0x5b749d3a22450fd6, 0xaf34a567bf838668, 0xa72a177d20943ceb,
|
||||
0x257c45c1601c4922, 0x3d7c68f6fe36ce59, 0x1b8ef47186e06c96, 0x8040426656154c17,
|
||||
0xc15f74f980647bda, 0x36389393336a78be, 0x15d174ed5536afe2, 0x51e702e8adb61f63,
|
||||
0xc9c95ca3f1c08f30, 0x6653094c8531c93a, 0xf7be5dfc2eb3b5b, 0xb5e21ec5c63850b0,
|
||||
0x9ab5f082e01021a5, 0x75c4ca38d7678fb1, 0xb20bd13e05e5bd67, 0x3378133e631fbaee,
|
||||
0x17cb3686511aeb6e, 0x4c0dea4c80ec7bc, 0x21fe874bf5509300, 0xece4f84e34d52e54,
|
||||
0x3f2649803ca9918b, 0xdb493adeac5c60f5, 0x7c02a1d153afedd, 0x2d08ceda0fef7967,
|
||||
0x6a32e018e432b0ea, 0x86479f3ea38ad57e, 0x84e3e04f1e5877be, 0x41b898cb02772049,
|
||||
0xd6dded3714d71084, 0x9df5b2f3a0ab275d, 0xd5ab652dfa73ee0a, 0x4633e03fa37d6edb,
|
||||
0x226314b8c4b937ff, 0x45d66a00ab031188, 0xb93ce1cfbcd1eaa0, 0xf88e0756f7e7c1c1,
|
||||
0xf3611966fed51e03, 0x81235048c662d9f7, 0xcc8275f866147b4f, 0xd3b3ca0f5033a863,
|
||||
0x970212eb8c2b3429, 0xec848dd58f3449d6, 0xa1d527af824d09f2, 0x60bd5e91448b9cf4,
|
||||
0x1210ddfac603aa88, 0xcbf4270f3407e25a, 0x212955fec55466a0, 0x3afeaef4c9ccc793,
|
||||
0xdd114286ec304817, 0x849e6ae3c2cf794f, 0x71c08228d6a05310, 0x177e77779d155b11,
|
||||
0xdc59148f219a9c04, 0xb0702a7802d5276d, 0x56085d6761aee015, 0x3f79ce06bcfb4f3,
|
||||
0x459a1f917f8f3e0, 0xe4ea5635e8bcd512, 0xa88e99b63f135a39, 0x95bd628d77d39446,
|
||||
0xe6432158ef4c7d98, 0xb349cd3d1c74369e, 0x25b1a32db58efb0a, 0xc2add3a44cecc0b5,
|
||||
0x5d676629f23010b5, 0x9890b3a62599408e, 0xef68ea8144d97805, 0x429e0fda34046a85,
|
||||
0x7723d9043053bf52, 0xbb78842d9b67ae91, 0x9155ef932192e6a3, 0xdb523ea403d39f6b,
|
||||
0xdb8fa1eee23ea58, 0x7c735492524a3448, 0xc580cb82e81505, 0xb0be6a006414841c,
|
||||
0xdec2e763b5cedaa5, 0x8da58bb23638af5f, 0xb0e6b33f6736e7d0, 0x8146bbcd3dd4df61,
|
||||
0x978080148a8989bb, 0x7f8119caa3308095, 0x4e88c318ea0604f3, 0x6ee5262f16b3cf83,
|
||||
0xc44395c7a578ace9, 0x92016ee635de27e1, 0xb8dc5ceb36e67fd2, 0x95c851d65bd35f8c,
|
||||
0xbe393620503c49fa, 0x42af183b92eac923, 0xfef0e660435135ce, 0x262d67d480451ed1,
|
||||
0x590f92e1ee0502b3, 0x18825c97d49e3700, 0x2cd8c30a848c9acf, 0x1025c7747fda0115,
|
||||
0x54109f23e82590aa, 0x93917bf1f8325981, 0xda674b183fa0e3cf, 0x2a0b2467eb8aefc5,
|
||||
0xb0085eb83468793c, 0x607cabb2c9d3a81b, 0xe7a6b6013804d665, 0x67629a769c1efede,
|
||||
0x2830ab6ef6d10166, 0xffd02b0655332bd4, 0x19bd056c3117568f, 0x385a834785662c6f,
|
||||
0x938b5d56bd5f7248, 0x969afe82dd4829c8, 0xf455d4ace41c797d, 0x23d9cd67eff27512,
|
||||
0x2b0c7037ecb322e2, 0x73df193328258bda, 0x5d7ee05cf2054f93, 0xdabfbd5b46b61cea,
|
||||
0xcea02d82546b96de, 0x2245d5e74d5f60ae, 0x842ca45f8ef2a44, 0x7505cc4e1c3060d8,
|
||||
0x869146ac8e68565e, 0x22ea711fb30e73e3, 0x53cd64736898a0c0, 0xfa88458b920684df,
|
||||
0xc3ae23f451e0616f, 0xe2dd69393141ff32, 0x98863ab129bcd866, 0x8c9756a40dd5b834,
|
||||
0x2eeeef78c36fede5, 0xe84d2eb23e22153b, 0xb0ccc2f7ac541d78, 0x151faba0f513acfa,
|
||||
0x4300e3cea0260717, 0xaba308c6d857d2d0, 0x5eb25dd325256c6a, 0x3342627b68038da4,
|
||||
0x70d7d75526da35, 0x80ff3f5ea2ac3cf1, 0xe7434f2f026394b0, 0xa7c5ff17f7d2cb07,
|
||||
0xdfed0bb33a06ff78, 0x485cc64d38ce2596, 0x88db8580147aa8fc, 0xf52a5111d693b973,
|
||||
0xeeaa031c02b370a8, 0xbb3b1678222d0e81, 0x27b569f2a6630939, 0x2b301fa3e50efeb8,
|
||||
0x4dbc1f85bf3f8972, 0xb37e4f2cb75a825b, 0x3c8848e0f1777cc7, 0x8fae2e6938ba00aa,
|
||||
0xfb4674b50884992c, 0x2b765005b85b7388, 0xaaf0007fb9662ce5, 0x684bd59009a4ad65,
|
||||
0x221ffead3d73ab35, 0xccfe6d02d46856b5, 0x54d3323359e1b114, 0xcb412202ed42f097,
|
||||
0x15d63df422771b9b, 0x71852bca1581d14f, 0xf30dcbb6cf891e63, 0x478fb1eed3cc5a10,
|
||||
0x849a3b52bc5bb196, 0x4c1a98dbc546dd81, 0x846dc8c2258ec4f8, 0xbd4da447c7340bd0,
|
||||
0x8f1ee1d6a85b9db0, 0x123ebfa8aaec07f2, 0xae34948e375d4477, 0xd466a4177842d8e4,
|
||||
0xd5108efeb19cac6, 0x3266f7db8f133bdb, 0xe69af4e5d8d767e4, 0xea0efc0331df64a2,
|
||||
0xb879052746ed72ed, 0x2c8233cc84de4144, 0xdcd5dda825186731, 0xb9e3b679d268f34c,
|
||||
0x7ce12e2fa95bda8b, 0xa34afd12e611a4c1, 0x6043d06ee7619f90, 0xca3a3d4813c0addf,
|
||||
0x6e97a61d1e4b3c4f, 0x8cdeae467a4bb292, 0xf08a6c69e70076b5, 0x97aa5c3180d3edbe,
|
||||
0xc39813e1573904d5, 0x42577549d026e8c8, 0xaf5827ffe259b62a, 0x9e1d48596c4f0b24,
|
||||
0xab9dd230ba8efb64, 0x81769493b85868d4, 0x715b45c5e3952245, 0x84735e138d228f35,
|
||||
0xf4f987da7d19c74d, 0x1bdc77979baf29b, 0x785b3640158e0278, 0x77fc9d00681bcdd,
|
||||
0xb5980cc490dcab93, 0x9062c158196b8244, 0xc16af5418cc97c4a, 0xdd4a4e9e8e00e524,
|
||||
0x870b554b629277f1, 0xf90ff72bb54322c0, 0xd4c273bc2199823, 0xeebc75970d438466,
|
||||
0xe4cad67413074a53, 0x6eccde71bd09dc0c, 0x14278b0ca6b910b4, 0x6e8895f17cdb933c,
|
||||
0xa0b3987821416e11, 0x71b7d24b81fa769f, 0x1d3b1a805b885a58, 0x1bc737b1719736a,
|
||||
0xea4d1dbb8823037, 0xe50ce48c8469adbd, 0x34c2d5e6c41a888e, 0x446a756eb06dc3a4,
|
||||
0xbac5ed8a8f90262, 0x7f1b76e0c707ab9d, 0xb31323309b94a12e, 0xf58269b9852f986e,
|
||||
0x3b74c2b338c244fc, 0x879b46f23a4deae4, 0x3f2591e34cdce1c9, 0x73c6f81eb560ed5c,
|
||||
0xd2aa923c7a5c18a9, 0x7170c1f7621cace9, 0xa18b327b11b951a, 0x2b36315510f56370,
|
||||
0x5cfed2f703dfc0bb, 0x1e43b99175054c07, 0x392dfa3210e8013b, 0x65e0c5c0454ee693,
|
||||
0x2a795f6f493349db, 0x17995ebdfe848db0, 0xf23174b823a52cf7, 0xaac6ef104bfd396e,
|
||||
0xfa0f5b29156eeccd, 0x9ccb3590a6e88c1a, 0x2edae0b1b80aafab, 0x1e1e92baae39aa30,
|
||||
0x24475af89cec7f33, 0xacdbeceaadb9936c, 0x7725948da8586e93, 0xcc05595e17947215,
|
||||
0x1f3cbde17a508faa, 0x2795f58ff5c8919b, 0x309658d1748f30d3, 0xe90c11962e5ed4bb,
|
||||
0xc22b876286d32e27, 0x495c8b667c6ea3dd, 0x84263045f7d6eab5, 0xf8d3a9ab1494a315,
|
||||
0xabc42cf769a21d9d, 0x3fef7fb40bdf3a81, 0xfd35336e188925a3, 0xd472bdaf277ab26d,
|
||||
0x4949e8ff51da2307, 0xbec86dce960ff3b, 0x1aa1ce4f70256b10, 0xd52986ff8cd700ea,
|
||||
0x364d8efc0f5afad6, 0xca3d1958f57a9050, 0x17a0a2ec122dc677, 0x7992695be3363fd6,
|
||||
0x5c66a265e0607da8, 0x7250114e050bb917, 0x30cd3a70bc7b723d, 0x7b77433392b3fbf8,
|
||||
0x295bb7bf46318b38, 0xdb15025af2a71c65, 0x26a82b21ef67f50c, 0x14a5573d4fc798c1,
|
||||
0xd8cad4642ec68e5e, 0x9276d7f60142822c, 0xf7b8efc27522e52f, 0xd0a3f36f6d340bed,
|
||||
0x341260fb11765c02, 0xcd1d394d796702cc, 0x7d483ef031eb3346, 0x74eaab49374576c1,
|
||||
0xa073bea32a71a273, 0xd65ce2552993b5cf, 0x8670afe77caaeb16, 0xb607f1455d072a47,
|
||||
0x30ed96be92809559, 0xa58380a503c23c9c, 0x916aa68fb957a30d, 0x30c5a675bf19738c,
|
||||
0xd4cbad34e4e4b886, 0xd6cb83061f2b0ebf, 0xceddb4040f535fa9, 0x778c586927b1e247,
|
||||
0xe4bb5c4b6e0f3c3d, 0x3e857d671db80667, 0x2b909dd8725f1fa2, 0x558ffd0772db7841,
|
||||
0x710f9638d3edb2c4, 0x21a4ccee53d46556, 0xf76e8e4737b9628b, 0x71cd157f23581c71,
|
||||
0x68d8fded9b66efd6, 0x7d9f5e182c0b9457, 0x2140757748a217ff, 0xdd1e5365520b77a4,
|
||||
0x644d8e4b2f30dcfa, 0xa1de42f4e9791564, 0x70e148ababfe9f86, 0xb97f463e0ac7daec,
|
||||
0x82844f729d9fa554, 0x5c8475e84470c924, 0x3a83de748eac32fd, 0x68725fbc9c202c5b,
|
||||
};
|
||||
|
||||
pub const mt32_seed = 0x7dc0d160;
|
||||
pub const mt32_data = []u32 {
|
||||
0x59327332, 0x200858fa, 0xab53c028, 0x5c442427,
|
||||
0xd8be0287, 0x3b69d304, 0x15fdf62f, 0x59b8ecd,
|
||||
0x6d7ab30c, 0x3a3dd6f1, 0xc1b9773e, 0xa12fb017,
|
||||
0xa805b5c1, 0x4a313ba5, 0xd82c790c, 0x8de311f2,
|
||||
0xe7cb23dd, 0x784b2efb, 0x9743487c, 0x73e2f2fb,
|
||||
0x1a7ac286, 0xaef90d, 0x6c0a4514, 0xae1d83aa,
|
||||
0x412fcca1, 0x3acd2d28, 0xde78292f, 0x13237756,
|
||||
0xdd6cdeba, 0x44ae4df9, 0x3e9902eb, 0x39e1cf20,
|
||||
0x62f561b9, 0x6cbdf531, 0x4a000673, 0xb1c82daa,
|
||||
0x896156ca, 0x75e410f2, 0x9c69e72c, 0x396b42bb,
|
||||
0x25c97ec0, 0xe12173f1, 0x8dcd42e5, 0x82aac3e3,
|
||||
0xcdc1c84d, 0x13509c0c, 0x46a696d2, 0xb89ad987,
|
||||
0x92da5e7f, 0xaa87d8a9, 0xe433ff57, 0x80a7ee49,
|
||||
0xd387cfcc, 0x7dc47d92, 0x21516140, 0x989ca465,
|
||||
0xf2a8e002, 0x73b99ddb, 0x2204108f, 0x27e84890,
|
||||
0x371c81c0, 0x9f581854, 0xc0841c35, 0x770f6804,
|
||||
0x87c55f4e, 0xd29516bb, 0x2b6d6bde, 0x5541f6ee,
|
||||
0x1ec9b182, 0x7d599729, 0x4f4b9a14, 0x6f8c8562,
|
||||
0x2d5151aa, 0xb54f5bc, 0xa252452b, 0x2a4266da,
|
||||
0x25a6b75d, 0x2d11106e, 0xc5d77943, 0xb10b6e0b,
|
||||
0xeb5cae4a, 0x43a0dd53, 0xa40bea1f, 0x63e632c2,
|
||||
0xf420b6ce, 0x8b080233, 0x7f70ae87, 0xf460f0d6,
|
||||
0x147c7e74, 0x710692ea, 0xa0a7d8fb, 0xe7f05808,
|
||||
0xa6173aaf, 0xae608de0, 0x8702036, 0xbf1bfc7b,
|
||||
0xf14cd548, 0xbbc7553d, 0x5358dd1d, 0xcc0c1fe5,
|
||||
0xfab6f78d, 0x9365c118, 0xf64216a3, 0xb4bdcf1b,
|
||||
0xc90b8a7a, 0x8b7b78a, 0x4c7b6854, 0xba7b5628,
|
||||
0xdd728c15, 0xcb1f8905, 0xa63e2342, 0xa78822,
|
||||
0xbda61b18, 0x160a59fa, 0xeccf473b, 0xc5a445b5,
|
||||
0x7aa86430, 0x362e0d7c, 0x8006a0cb, 0x8b11586f,
|
||||
0x6677bba9, 0x6208cf27, 0xeec9b5, 0x3dfedfc9,
|
||||
0x886cc0e8, 0x32ed77ca, 0x43525faf, 0x9786354a,
|
||||
0x1a2eb378, 0xf0e6b168, 0x49064b09, 0x8ab39681,
|
||||
0x7b6655fc, 0x35adb168, 0xc417d430, 0x2784288a,
|
||||
0xea17836, 0xc85006e7, 0x673dfdc3, 0x42765688,
|
||||
0xc2b9251, 0x840a45b, 0xcac98e2f, 0x1a6f9777,
|
||||
0x34959b23, 0xf0dcec81, 0xcaa2c6c8, 0x1cf93061,
|
||||
0x787e598d, 0xd5d9e31e, 0x14e08791, 0xd9d9d782,
|
||||
0xef162f23, 0x238f4113, 0x23f42107, 0x6ed5cc3f,
|
||||
0xa55e5a7c, 0x4650595, 0x5217da8b, 0x6eeaacdc,
|
||||
0xb453d7b1, 0xfa1ff004, 0xb9d17f74, 0x2bd6a53e,
|
||||
0xe4c2d9dd, 0xed66375e, 0xf8215568, 0x9bcadbb3,
|
||||
0x9c4f9d51, 0xff68312, 0x82308422, 0x83e990b0,
|
||||
0x38b6135b, 0x70e2aa13, 0xa30065d2, 0x6396a00,
|
||||
0x77d423bc, 0xa93abf0a, 0xc7bb8c31, 0x5d7bd3d3,
|
||||
0x6a374f2e, 0xe4b5bc88, 0x39f6e512, 0xd6aea995,
|
||||
0x878c1bfa, 0x4636014d, 0x9caa2c09, 0x7ac4758b,
|
||||
0xbd3b957e, 0x518c2fd6, 0xea009a2e, 0x542bf419,
|
||||
0x59090006, 0xb1d94703, 0xe0d9eefc, 0xe7fccb17,
|
||||
0x40111951, 0xf2560485, 0xb50ce9e1, 0xd7a1ee51,
|
||||
0x28dffa99, 0x41d12275, 0xdd89a365, 0xf22eda29,
|
||||
0x104f94ee, 0xe669983b, 0x6346a250, 0x86326fc5,
|
||||
0xb7f347df, 0x3849a39f, 0xf433929a, 0xeea5155,
|
||||
0x4cf9b778, 0x6bd7926a, 0xcda9496, 0xf430d7a2,
|
||||
0x41637670, 0xaf3bbad6, 0xeb66e44e, 0x2499605d,
|
||||
0x9988920d, 0xf9d652ef, 0x67aa80c0, 0x505073c9,
|
||||
0x85cd418f, 0x9f83fb65, 0xf50b3eac, 0x812ba6bd,
|
||||
0x74d61788, 0x86d64f3b, 0xb1f8fc1c, 0x3e2af667,
|
||||
0x4d118a2, 0xd028ffa9, 0x32e88a44, 0x4ed9ba35,
|
||||
0xea3c7030, 0xffe44aaf, 0x5e39c467, 0xeabcfebb,
|
||||
0x53e656ec, 0xced701d0, 0x31020b02, 0x4b4c1dc5,
|
||||
0x8744885c, 0xa8e93656, 0x3ef457e5, 0x272bde23,
|
||||
0xe541477c, 0x3ad3ac04, 0x63eaa692, 0x81055cf9,
|
||||
0x3ff5f782, 0xa8efe6bc, 0x15f37656, 0xaaaebf1d,
|
||||
0xf73d461a, 0xe8b2c0b5, 0x5035ff48, 0x3a95e34b,
|
||||
0x6f21d94f, 0x6f6d1f96, 0xdaf79f37, 0x826f69f3,
|
||||
0x209a00b8, 0x2ad1b2f2, 0x2c64fb45, 0xcf8bf26e,
|
||||
0x9befcff2, 0xc08f6951, 0x96d98205, 0xa267dcb5,
|
||||
0xbc43ec5, 0xee6a7e1c, 0x49224eae, 0x14e820e,
|
||||
0xbb340212, 0x68ed572c, 0x45e9e623, 0x1297f3af,
|
||||
0x49a98ed2, 0xddd34ae8, 0x211838ab, 0x47e7652d,
|
||||
0xb40430c6, 0xc8d3bd7, 0x4352356e, 0xf0e5cac9,
|
||||
0x21880df4, 0xc16b343a, 0xd9ed7350, 0x17fe1f65,
|
||||
0x6637192e, 0xd81c93aa, 0x7d6e17d2, 0xd407b13f,
|
||||
0x425da072, 0x380d423d, 0x6ce57b22, 0x7b17ed17,
|
||||
0x95fbf626, 0x768303d6, 0x76ab6b3e, 0x591491e3,
|
||||
0x259f79ab, 0xd4babeaf, 0x9c7de2f8, 0x4fe6cb58,
|
||||
0xf43680a9, 0x651a1266, 0x730ea3c8, 0x9188d4c5,
|
||||
0x12d01e34, 0x47afb2e9, 0xb4b76d35, 0x5e5164bc,
|
||||
0xc864fc46, 0x5d018aa7, 0x17fac975, 0x5a775fbd,
|
||||
0x40e6fa14, 0x7a00b683, 0x99e4e102, 0x2f933b90,
|
||||
0x474e14ba, 0xde1b0754, 0xe84aba2b, 0xb386cd43,
|
||||
0x17ca77c9, 0x7b4f38ef, 0x803ea1a8, 0x93553947,
|
||||
0x806c8224, 0x2608451e, 0x63157fe3, 0xaf53930e,
|
||||
0x5dfe8c16, 0x65592bda, 0x7086eb3f, 0x838e6a50,
|
||||
0xa27836d9, 0xf2f16d92, 0xdc0a981, 0xfbf8f915,
|
||||
0x2caea00d, 0x86bb3e18, 0x6d94c209, 0x3bbbeb6c,
|
||||
0x114d68f4, 0xc271e48f, 0xa3350dc1, 0xb8d55eb4,
|
||||
0x68be5ee1, 0xbf22ef29, 0xd6e0aa54, 0x48f7219,
|
||||
0x21aca253, 0xfbf07910, 0xfcdd61a8, 0x118a09b,
|
||||
0x3f2bbde6, 0x46eea63f, 0xdb51ed16, 0xf8a9fc36,
|
||||
0x31614dc0, 0xdd84f54d, 0xd2b66065, 0xdae0af99,
|
||||
0x6d071a51, 0xbdbac46c, 0x15deee25, 0xf792e64c,
|
||||
0x910194e8, 0xfc989a8f, 0x919727fd, 0x6f93a56c,
|
||||
0x2df36e9a, 0xd395b948, 0xb026b54a, 0xf0938a5,
|
||||
0xe9c64399, 0xb5cda15b, 0xb7b8dd41, 0x7146f944,
|
||||
0x8d41ce2f, 0x47c74099, 0x2e5a8e5f, 0x28f7a19c,
|
||||
0xef7a8ef9, 0x6a763eb9, 0xf13a3ec4, 0x9f352360,
|
||||
0x42317561, 0x6c6a0ca5, 0x5e40b472, 0x3ddaadd4,
|
||||
0x2f5d14eb, 0x5dd49aeb, 0xc89edb24, 0xa2da269b,
|
||||
0x5cf0a38b, 0x8e2f435c, 0x40970e54, 0xa2cb730e,
|
||||
0xf9d8c301, 0x8ef29fb1, 0xf08b1840, 0x7d45e4a2,
|
||||
0xa0fe4ce1, 0x939a21c4, 0xfdeebfea, 0x4c661550,
|
||||
0xdd304d1c, 0x3cdb078d, 0x94ae8db2, 0x4f6b4287,
|
||||
0xffe64fa8, 0x50384bb0, 0x16cf5ed3, 0xa91a8fec,
|
||||
0xdb8ebb1, 0x59c2898b, 0xd587edc9, 0xdec2e75a,
|
||||
0x496ccdd2, 0x897db91d, 0xf8ea5149, 0x6bed4bad,
|
||||
0xce76e472, 0x43c7f976, 0xb055dc01, 0x7ffd5671,
|
||||
0xe193b86a, 0xe288ce11, 0x514d531e, 0xa42fa47e,
|
||||
0xe7c0e194, 0xffc059ba, 0x26548e36, 0xe1f10d92,
|
||||
0x3ef5d95e, 0xa6e69282, 0xffacb09e, 0xf4a16ff5,
|
||||
0x9b7f03bd, 0x588c54b4, 0xc2b6eaa1, 0x2d83acdc,
|
||||
0x7fdbb606, 0x2b160650, 0x9923e57e, 0x32bd23bd,
|
||||
0x50cd6d4c, 0x205d901f, 0x810a9935, 0x27ce6e7a,
|
||||
0xe0c6c66, 0xac06c99c, 0x4326aa9b, 0xe1af1e90,
|
||||
0xe358c8b1, 0x2f601c2a, 0xefca77e7, 0x1a7ed2f8,
|
||||
0x8ad2e191, 0xe5520809, 0x27084438, 0xe4d8e782,
|
||||
0x5e8a4038, 0x87bba694, 0x65f07eba, 0x616f8f07,
|
||||
0xc5565d9, 0x555955e4, 0xf41c2caa, 0xb085fbf5,
|
||||
0xa5f9d9ff, 0x418fa0df, 0xec5a576d, 0x7fc332ab,
|
||||
0x7683ed33, 0x968ef54b, 0x834d598d, 0x6833f356,
|
||||
0x59dc7e7f, 0x779661dc, 0x58942dd4, 0x80387aab,
|
||||
0xf6dac9e5, 0xe043be04, 0x2ae4f872, 0x881f8d01,
|
||||
0x82cfd69d, 0x931f4648, 0x2a76ab31, 0xa3f1dd7c,
|
||||
0xd7f4826a, 0xe74918da, 0xe4c98636, 0x441164f,
|
||||
0x15a0e9aa, 0xce7480ad, 0xba39076b, 0x233aa8d,
|
||||
0x6c32f0e6, 0x169c62bf, 0xa2cd17f6, 0xb5590084,
|
||||
0xb2036f00, 0x18315935, 0x11e9c9c7, 0x25c77861,
|
||||
0x41596cda, 0x635e5e02, 0x8f396cc, 0x4cd00d8d,
|
||||
0xd665597e, 0x90f891ef, 0x547b93ee, 0x376959c1,
|
||||
0xdc5fa80, 0x9b4797a6, 0x53673041, 0x25ab117a,
|
||||
0x7b8b8292, 0xf4e99584, 0x5139da98, 0x30e2afeb,
|
||||
0xff2664b9, 0x591eb6f0, 0x9e87e602, 0xf5e26193,
|
||||
0x61831f07, 0xabc139f9, 0x984eda0a, 0xaea1b8da,
|
||||
0x65c7410d, 0x2b84800d, 0x1d3cfec3, 0xd05cb8a1,
|
||||
0x4529641b, 0x7d6712e6, 0xc38cbde7, 0xacad7787,
|
||||
0xd8482f3a, 0xa5662eaa, 0x24836ee9, 0xf3b5cc97,
|
||||
0x50a581ae, 0xff6004b6, 0x650fc547, 0x161898b1,
|
||||
0xa7593447, 0x325827dd, 0xf1844a1a, 0x7eb56de2,
|
||||
0x89882452, 0xfebb49a, 0xfe86ae9c, 0x7dba98b1,
|
||||
0x1d65adb5, 0xb71acffa, 0x861215af, 0xc0f1496,
|
||||
0x70967c72, 0x3803d127, 0x6c8fdd84, 0xe40991f1,
|
||||
0x1343e3a, 0xf57b4e73, 0x25f34f76, 0xaebcdee8,
|
||||
0x8752d71f, 0xfc710e54, 0x34f3af44, 0xfa7dea4e,
|
||||
0x477d4d83, 0x42640ff1, 0x2c5c31ce, 0xa82de5e4,
|
||||
0xcc813271, 0x4d40bf86, 0x4e416095, 0xb5ac332c,
|
||||
0xd2d44703, 0xe4c5ef57, 0xde193a29, 0xbf3e7974,
|
||||
0xbb313d75, 0x8dc973d5, 0x301b2657, 0x44dc5064,
|
||||
0x8c58c633, 0x83424c74, 0xb7cbf7ac, 0xa04238c2,
|
||||
0x6ceabd59, 0xd25e6fd0, 0x3409167, 0x42d6ef80,
|
||||
0x1f47c437, 0xdb21e45f, 0x2fd48e29, 0x9498cfb7,
|
||||
0xc9e4cb12, 0xc6dcf0df, 0xa1633c39, 0x1b349670,
|
||||
0xf76d4a64, 0x15ecd8dd, 0x777bb76d, 0xc46008e7,
|
||||
0x23d94e44, 0x78aa07de, 0x2eeac782, 0x3757b114,
|
||||
0x2b22de2a, 0x37726519, 0xf107546d, 0xe9847f74,
|
||||
0x449a4ea6, 0x2e31fa5a, 0xd719ea88, 0xb2115c87,
|
||||
0xfa6b7231, 0xf72fc9ff, 0xcd22bc37, 0x9080778a,
|
||||
0x93430a21, 0x97c24360, 0x6e5b1a76, 0x5e8baa7c,
|
||||
0x300c94f8, 0x2843d9da, 0xdceac0ae, 0xeeed885,
|
||||
0x1898ffd0, 0xa3bbee3c, 0xc16f8fd7, 0x82992b68,
|
||||
0x39c153b6, 0x1b3ba4c8, 0x41e7c3ac, 0xcdf8f06a,
|
||||
0xd40b8ae6, 0x4982b6c2, 0xb32f7437, 0x22ed3691,
|
||||
0x16579a2a, 0xff9de457, 0xc421e8e4, 0x17c8f6cb,
|
||||
0xa5c4a8da, 0x49bd8afa, 0xe2be081c, 0x95170f28,
|
||||
0xd679fbdf, 0xcf39d563, 0x4e2d2ee9, 0x39471096,
|
||||
0x3918bef0, 0x279b7679, 0xa5281a0f, 0x49481d6f,
|
||||
0x11f95ee1, 0xd9df649f, 0x2993eb27, 0x48ad815f,
|
||||
0x99cf306d, 0xca9457e4, 0xc27c51d2, 0xc2a838ec,
|
||||
0x537faf4c, 0x55dccddf, 0x8df5aeb8, 0xabb317ca,
|
||||
0xfc1bcf6b, 0x669c2b1b, 0x719b62d5, 0x6b9325cf,
|
||||
0xc123d0d3, 0x2ddc6ace, 0x27fdc30a, 0xd3f93cd8,
|
||||
0x704f5486, 0xd3f448ec, 0xbbd1e32c, 0x3bcd4c0b,
|
||||
0x86f8166, 0x957db888, 0x899b6a5e, 0x270dc8b7,
|
||||
0xff16222e, 0x51e139a8, 0x3d8b4b9f, 0x68d20818,
|
||||
0xa639ad00, 0x4c2e0fd2, 0xb4949cdc, 0x2ab6eb32,
|
||||
0xdd0c67ad, 0xd2208cbe, 0xcd17a0bc, 0xacc541f7,
|
||||
0xfa9e714f, 0x316d31a7, 0xed79fa91, 0xb5c0e980,
|
||||
0x412ecc9b, 0x9815753, 0xd0df1f43, 0x8e37dbb9,
|
||||
0xe640df75, 0x379c2fb6, 0xc7ed26a4, 0xc5190400,
|
||||
0x1cc81b53, 0xcb0b0cd5, 0x360f061b, 0x6d90284e,
|
||||
0x83c05bd0, 0xbd80bae9, 0xd584ef12, 0x228a46ec,
|
||||
0x657c4fbe, 0x5ca1043c, 0x852aca0f, 0x31ce950,
|
||||
0x33ee2cd8, 0x3cdecbf7, 0x787ef08c, 0xea610ee,
|
||||
0x47c1db89, 0x90eeda11, 0x74f8d429, 0x51d3a4c5,
|
||||
0x3135b401, 0x2e14783c, 0xb9af855c, 0xb66348d9,
|
||||
0xa3a47387, 0x6eb72af1, 0x7bb56088, 0xc664542d,
|
||||
0x7ed96b8, 0x995870a8, 0x385b1fd6, 0xa430680d,
|
||||
0x98a883ec, 0x2497a389, 0x7a880627, 0x8350ba9d,
|
||||
0x4cb35c33, 0x30bf6b14, 0x8695a469, 0x9a81e44b,
|
||||
0x8bb27c9a, 0xbfb6a4dd, 0xbae7cf6e, 0x4ebccc87,
|
||||
0xb712ed3d, 0x31e90365, 0xcc1fa63f, 0x32b93df6,
|
||||
0xbad4c7bc, 0xb2570e17, 0x73fa21be, 0x5c02a8d2,
|
||||
0x94446d75, 0x7265f3ad, 0xd58487a2, 0x919b7a07,
|
||||
0xbe2d0e05, 0xd36ccf4f, 0x6d5c66d7, 0x8448522f,
|
||||
0x8409c294, 0x6f1c7af7, 0x173a13bc, 0x1b3e4a0b,
|
||||
0x705b941b, 0x77eb584f, 0x85b68458, 0x8e3ad1ac,
|
||||
0x4aa99702, 0x7ae1b24c, 0x899ba29c, 0x860a3711,
|
||||
0xabe53a4f, 0x37870133, 0x1ed7cb89, 0xea539762,
|
||||
0x4ba64130, 0x48517a2d, 0xce0a869d, 0x937ba48,
|
||||
0xd0f234c4, 0xf9b2cf26, 0xc3c311f0, 0x153d09a9,
|
||||
0x404d3af9, 0x9f7edbc1, 0xbdcecded, 0x97969ba8,
|
||||
0x3379437, 0xadd3c893, 0x7c024639, 0x459390b,
|
||||
0xcb7c7320, 0xa5c63725, 0x65907e3e, 0xbf70583b,
|
||||
0xcebb601b, 0x4edfb286, 0x9350336f, 0xdfb4be76,
|
||||
0x88b56f39, 0x9937d7f9, 0xa12a286d, 0x34f141c,
|
||||
0xa2e75c15, 0xd69a7060, 0x931340c3, 0x22447f25,
|
||||
0xe8aed82c, 0xd76a9ae7, 0xc967288, 0xe572facd,
|
||||
0xbe82b0ee, 0x10f5dce1, 0x4f03ee35, 0x2340b923,
|
||||
0xf4fb6bd0, 0x64adbf01, 0x3d277a0a, 0x39e76f2c,
|
||||
0xe3c024d9, 0x57869c82, 0x743b7826, 0xf66f1574,
|
||||
0xc93965c, 0xf86a552, 0x13557069, 0x9e0845de,
|
||||
0xaee084f9, 0x5eafaedb, 0xc06f5f3, 0x9051f6ea,
|
||||
0x98fceda2, 0x2af2f8cc, 0x6c41b5a8, 0xc1af74de,
|
||||
0x57302276, 0x253923c9, 0xd79996b3, 0x8ecb3141,
|
||||
0x641387cc, 0xf87a4101, 0x96a50c76, 0xbcf24a11,
|
||||
0x87b6bb6, 0x58ee501b, 0xaa859695, 0xb2eed107,
|
||||
0x554173f0, 0xb12ec0e6, 0x57785c1b, 0x53685c9a,
|
||||
0x114c3163, 0x9383cf19, 0x31fd7cdf, 0xaeb8225c,
|
||||
0x58774fe7, 0x54700ad4, 0xad418726, 0xf055b71c,
|
||||
0x7d31237f, 0xdd97cad5, 0xcdd5325e, 0x42f2acf4,
|
||||
0x4bed262b, 0x7a8faaf2, 0x2b1eafdd, 0xa1b806ac,
|
||||
0x26965c6e, 0xb41b7168, 0x15e2e70b, 0x7daa8e13,
|
||||
0x6198aa5a, 0xc9b8b94c, 0x339b5754, 0xcd3b285c,
|
||||
0xffd1486c, 0xf224979a, 0xafb89ec5, 0x222058c,
|
||||
0xcb4814d0, 0x2b0e7c7d, 0x9eb25b84, 0x271564b0,
|
||||
0xbb72e076, 0x48251020, 0x18008023, 0x48d10005,
|
||||
0x4a452eaa, 0xb2365308, 0x19cdb632, 0x1fd56d04,
|
||||
0xffa5ff2a, 0xaba89e42, 0x388fc17d, 0xea61c00f,
|
||||
0x5156273d, 0x776f1a56, 0x8d539d28, 0x289c01cb,
|
||||
0x857aa71f, 0x348e411f, 0xc9eb3c91, 0x67a61079,
|
||||
0xe4276a0f, 0x45bdc15f, 0x8e0a698e, 0xbdefc310,
|
||||
0x82377ba6, 0x3bfbf404, 0xcbf22c79, 0x35f501bc,
|
||||
0xb16044a7, 0xeffdb8, 0xdbac383d, 0x7816663f,
|
||||
0x18f5a318, 0x3d04f1cb, 0x735da9b4, 0x75e339a1,
|
||||
0x5b6c55f, 0x1c18887e, 0xf698e14f, 0x338a6da1,
|
||||
0xdac85699, 0x1aca7768, 0x8eb0fa7a, 0xc98fa71d,
|
||||
0x3b794408, 0x92913041, 0xf8dc8827, 0x1cf706e9,
|
||||
0x3aeee292, 0x321dbaa8, 0xee1eb8d1, 0x23554be9,
|
||||
0x811c7804, 0xf0f4de6b, 0xd457e382, 0xeda56795,
|
||||
0xeffdfc71, 0xf2a52829, 0xa7460732, 0x2c1321c0,
|
||||
0x2f734db0, 0xf04ecb0b, 0xec7d777e, 0x43c54317,
|
||||
0xccaa74cd, 0xfe49dd9d, 0x4c509829, 0x278f9bd7,
|
||||
0x581dc500, 0x4ad38c2e, 0xcbee1047, 0x13302c1c,
|
||||
0xbc0cb734, 0xc1c8f234, 0x1df52b35, 0xd8815548,
|
||||
0x319edefb, 0x437cebe5, 0x3dcb6026, 0xe9d4f93f,
|
||||
0xb2661154, 0xeb8c15a0, 0xb008505, 0x5f869981,
|
||||
0xf5588ca4, 0xd6929c5b, 0xa3dd13d1, 0xdc863314,
|
||||
0x891a454f, 0x91737e49, 0x5064d4d8, 0x2fd32675,
|
||||
0xadefe9b1, 0xdde32b11, 0x741bbd6, 0x3b4363a9,
|
||||
0xb121d9e8, 0x916ca61d, 0x38c0af15, 0x5e3dfd72,
|
||||
};
|
156
std/sort.zig
156
std/sort.zig
|
@ -67,7 +67,7 @@ const Iterator = struct {
|
|||
self.numerator -= self.denominator;
|
||||
self.decimal += 1;
|
||||
}
|
||||
|
||||
|
||||
return Range {.start = start, .end = self.decimal};
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ const Iterator = struct {
|
|||
self.numerator_step -= self.denominator;
|
||||
self.decimal_step += 1;
|
||||
}
|
||||
|
||||
|
||||
return (self.decimal_step < self.size);
|
||||
}
|
||||
|
||||
|
@ -219,7 +219,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
var B1 = iterator.nextRange();
|
||||
var A2 = iterator.nextRange();
|
||||
var B2 = iterator.nextRange();
|
||||
|
||||
|
||||
if (lessThan(items[B1.end - 1], items[A1.start])) {
|
||||
// the two ranges are in reverse order, so copy them in reverse order into the cache
|
||||
mem.copy(T, cache[B1.length()..], items[A1.start..A1.end]);
|
||||
|
@ -230,13 +230,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
} else {
|
||||
// if A1, B1, A2, and B2 are all in order, skip doing anything else
|
||||
if (!lessThan(items[B2.start], items[A2.end - 1]) and !lessThan(items[A2.start], items[B1.end - 1])) continue;
|
||||
|
||||
|
||||
// copy A1 and B1 into the cache in the same order
|
||||
mem.copy(T, cache[0..], items[A1.start..A1.end]);
|
||||
mem.copy(T, cache[A1.length()..], items[B1.start..B1.end]);
|
||||
}
|
||||
A1 = Range.init(A1.start, B1.end);
|
||||
|
||||
|
||||
// merge A2 and B2 into the cache
|
||||
if (lessThan(items[B2.end - 1], items[A2.start])) {
|
||||
// the two ranges are in reverse order, so copy them in reverse order into the cache
|
||||
|
@ -251,11 +251,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
mem.copy(T, cache[A1.length() + A2.length()..], items[B2.start..B2.end]);
|
||||
}
|
||||
A2 = Range.init(A2.start, B2.end);
|
||||
|
||||
|
||||
// merge A1 and A2 from the cache into the items
|
||||
const A3 = Range.init(0, A1.length());
|
||||
const B3 = Range.init(A1.length(), A1.length() + A2.length());
|
||||
|
||||
|
||||
if (lessThan(cache[B3.end - 1], cache[A3.start])) {
|
||||
// the two ranges are in reverse order, so copy them in reverse order into the items
|
||||
mem.copy(T, items[A1.start + A2.length()..], cache[A3.start..A3.end]);
|
||||
|
@ -269,17 +269,17 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
mem.copy(T, items[A1.start + A1.length()..], cache[B3.start..B3.end]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// we merged two levels at the same time, so we're done with this level already
|
||||
// (iterator.nextLevel() is called again at the bottom of this outer merge loop)
|
||||
_ = iterator.nextLevel();
|
||||
|
||||
|
||||
} else {
|
||||
iterator.begin();
|
||||
while (!iterator.finished()) {
|
||||
var A = iterator.nextRange();
|
||||
var B = iterator.nextRange();
|
||||
|
||||
|
||||
if (lessThan(items[B.end - 1], items[A.start])) {
|
||||
// the two ranges are in reverse order, so a simple rotation should fix it
|
||||
mem.rotate(T, items[A.start..B.end], A.length());
|
||||
|
@ -301,10 +301,10 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
// 6. merge each A block with any B values that follow, using the cache or the second internal buffer
|
||||
// 7. sort the second internal buffer if it exists
|
||||
// 8. redistribute the two internal buffers back into the items
|
||||
|
||||
|
||||
var block_size: usize = math.sqrt(iterator.length());
|
||||
var buffer_size = iterator.length()/block_size + 1;
|
||||
|
||||
|
||||
// as an optimization, we really only need to pull out the internal buffers once for each level of merges
|
||||
// after that we can reuse the same buffers over and over, then redistribute it when we're finished with this level
|
||||
var A: Range = undefined;
|
||||
|
@ -322,11 +322,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
|
||||
var buffer1 = Range.init(0, 0);
|
||||
var buffer2 = Range.init(0, 0);
|
||||
|
||||
|
||||
// find two internal buffers of size 'buffer_size' each
|
||||
find = buffer_size + buffer_size;
|
||||
var find_separately = false;
|
||||
|
||||
|
||||
if (block_size <= cache.len) {
|
||||
// if every A block fits into the cache then we won't need the second internal buffer,
|
||||
// so we really only need to find 'buffer_size' unique values
|
||||
|
@ -336,21 +336,21 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
find = buffer_size;
|
||||
find_separately = true;
|
||||
}
|
||||
|
||||
|
||||
// we need to find either a single contiguous space containing 2√A unique values (which will be split up into two buffers of size √A each),
|
||||
// or we need to find one buffer of < 2√A unique values, and a second buffer of √A unique values,
|
||||
// OR if we couldn't find that many unique values, we need the largest possible buffer we can get
|
||||
|
||||
|
||||
// in the case where it couldn't find a single buffer of at least √A unique values,
|
||||
// all of the Merge steps must be replaced by a different merge algorithm (MergeInPlace)
|
||||
iterator.begin();
|
||||
while (!iterator.finished()) {
|
||||
A = iterator.nextRange();
|
||||
B = iterator.nextRange();
|
||||
|
||||
|
||||
// just store information about where the values will be pulled from and to,
|
||||
// as well as how many values there are, to create the two internal buffers
|
||||
|
||||
|
||||
// check A for the number of unique values we need to fill an internal buffer
|
||||
// these values will be pulled out to the start of A
|
||||
last = A.start;
|
||||
|
@ -360,7 +360,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
if (index == A.end) break;
|
||||
}
|
||||
index = last;
|
||||
|
||||
|
||||
if (count >= buffer_size) {
|
||||
// keep track of the range within the items where we'll need to "pull out" these values to create the internal buffer
|
||||
pull[pull_index] = Pull {
|
||||
|
@ -370,7 +370,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
.to = A.start,
|
||||
};
|
||||
pull_index = 1;
|
||||
|
||||
|
||||
if (count == buffer_size + buffer_size) {
|
||||
// we were able to find a single contiguous section containing 2√A unique values,
|
||||
// so this section can be used to contain both of the internal buffers we'll need
|
||||
|
@ -405,7 +405,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
.to = A.start,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// check B for the number of unique values we need to fill an internal buffer
|
||||
// these values will be pulled out to the end of B
|
||||
last = B.end - 1;
|
||||
|
@ -415,7 +415,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
if (index == B.start) break;
|
||||
}
|
||||
index = last;
|
||||
|
||||
|
||||
if (count >= buffer_size) {
|
||||
// keep track of the range within the items where we'll need to "pull out" these values to create the internal buffe
|
||||
pull[pull_index] = Pull {
|
||||
|
@ -425,7 +425,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
.to = B.end,
|
||||
};
|
||||
pull_index = 1;
|
||||
|
||||
|
||||
if (count == buffer_size + buffer_size) {
|
||||
// we were able to find a single contiguous section containing 2√A unique values,
|
||||
// so this section can be used to contain both of the internal buffers we'll need
|
||||
|
@ -449,7 +449,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
// buffer2 will be pulled out from a 'B' subarray, so if the first buffer was pulled out from the corresponding 'A' subarray,
|
||||
// we need to adjust the end point for that A subarray so it knows to stop redistributing its values before reaching buffer2
|
||||
if (pull[0].range.start == A.start) pull[0].range.end -= pull[1].count;
|
||||
|
||||
|
||||
// we found a second buffer in an 'B' subarray containing √A unique values, so we're done!
|
||||
buffer2 = Range.init(B.end - count, B.end);
|
||||
break;
|
||||
|
@ -465,12 +465,12 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// pull out the two ranges so we can use them as internal buffers
|
||||
pull_index = 0;
|
||||
while (pull_index < 2) : (pull_index += 1) {
|
||||
const length = pull[pull_index].count;
|
||||
|
||||
|
||||
if (pull[pull_index].to < pull[pull_index].from) {
|
||||
// we're pulling the values out to the left, which means the start of an A subarray
|
||||
index = pull[pull_index].from;
|
||||
|
@ -493,27 +493,27 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// adjust block_size and buffer_size based on the values we were able to pull out
|
||||
buffer_size = buffer1.length();
|
||||
block_size = iterator.length()/buffer_size + 1;
|
||||
|
||||
|
||||
// the first buffer NEEDS to be large enough to tag each of the evenly sized A blocks,
|
||||
// so this was originally here to test the math for adjusting block_size above
|
||||
// assert((iterator.length() + 1)/block_size <= buffer_size);
|
||||
|
||||
|
||||
// now that the two internal buffers have been created, it's time to merge each A+B combination at this level of the merge sort!
|
||||
iterator.begin();
|
||||
while (!iterator.finished()) {
|
||||
A = iterator.nextRange();
|
||||
B = iterator.nextRange();
|
||||
|
||||
|
||||
// remove any parts of A or B that are being used by the internal buffers
|
||||
start = A.start;
|
||||
if (start == pull[0].range.start) {
|
||||
if (pull[0].from > pull[0].to) {
|
||||
A.start += pull[0].count;
|
||||
|
||||
|
||||
// if the internal buffer takes up the entire A or B subarray, then there's nothing to merge
|
||||
// this only happens for very small subarrays, like √4 = 2, 2 * (2 internal buffers) = 4,
|
||||
// which also only happens when cache.len is small or 0 since it'd otherwise use MergeExternal
|
||||
|
@ -532,25 +532,25 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
if (B.length() == 0) continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (lessThan(items[B.end - 1], items[A.start])) {
|
||||
// the two ranges are in reverse order, so a simple rotation should fix it
|
||||
mem.rotate(T, items[A.start..B.end], A.length());
|
||||
} else if (lessThan(items[A.end], items[A.end - 1])) {
|
||||
// these two ranges weren't already in order, so we'll need to merge them!
|
||||
var findA: usize = undefined;
|
||||
|
||||
|
||||
// break the remainder of A into blocks. firstA is the uneven-sized first A block
|
||||
var blockA = Range.init(A.start, A.end);
|
||||
var firstA = Range.init(A.start, A.start + blockA.length() % block_size);
|
||||
|
||||
|
||||
// swap the first value of each A block with the value in buffer1
|
||||
var indexA = buffer1.start;
|
||||
index = firstA.end;
|
||||
while (index < blockA.end) : ({indexA += 1; index += block_size;}) {
|
||||
mem.swap(T, &items[indexA], &items[index]);
|
||||
}
|
||||
|
||||
|
||||
// start rolling the A blocks through the B blocks!
|
||||
// whenever we leave an A block behind, we'll need to merge the previous A block with any B blocks that follow it, so track that information as well
|
||||
var lastA = firstA;
|
||||
|
@ -558,7 +558,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
var blockB = Range.init(B.start, B.start + math.min(block_size, B.length()));
|
||||
blockA.start += firstA.length();
|
||||
indexA = buffer1.start;
|
||||
|
||||
|
||||
// if the first unevenly sized A block fits into the cache, copy it there for when we go to Merge it
|
||||
// otherwise, if the second buffer is available, block swap the contents into that
|
||||
if (lastA.length() <= cache.len) {
|
||||
|
@ -566,7 +566,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
} else if (buffer2.length() > 0) {
|
||||
blockSwap(T, items, lastA.start, buffer2.start, lastA.length());
|
||||
}
|
||||
|
||||
|
||||
if (blockA.length() > 0) {
|
||||
while (true) {
|
||||
// if there's a previous B block and the first value of the minimum A block is <= the last value of the previous B block,
|
||||
|
@ -575,7 +575,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
// figure out where to split the previous B block, and rotate it at the split
|
||||
const B_split = binaryFirst(T, items, items[indexA], lastB, lessThan);
|
||||
const B_remaining = lastB.end - B_split;
|
||||
|
||||
|
||||
// swap the minimum A block to the beginning of the rolling A blocks
|
||||
var minA = blockA.start;
|
||||
findA = minA + block_size;
|
||||
|
@ -585,16 +585,16 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
}
|
||||
}
|
||||
blockSwap(T, items, blockA.start, minA, block_size);
|
||||
|
||||
|
||||
// swap the first item of the previous A block back with its original value, which is stored in buffer1
|
||||
mem.swap(T, &items[blockA.start], &items[indexA]);
|
||||
indexA += 1;
|
||||
|
||||
|
||||
// locally merge the previous A block with the B values that follow it
|
||||
// if lastA fits into the external cache we'll use that (with MergeExternal),
|
||||
// or if the second internal buffer exists we'll use that (with MergeInternal),
|
||||
// or failing that we'll use a strictly in-place merge algorithm (MergeInPlace)
|
||||
|
||||
|
||||
if (lastA.length() <= cache.len) {
|
||||
mergeExternal(T, items, lastA, Range.init(lastA.end, B_split), lessThan, cache[0..]);
|
||||
} else if (buffer2.length() > 0) {
|
||||
|
@ -602,7 +602,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
} else {
|
||||
mergeInPlace(T, items, lastA, Range.init(lastA.end, B_split), lessThan);
|
||||
}
|
||||
|
||||
|
||||
if (buffer2.length() > 0 or block_size <= cache.len) {
|
||||
// copy the previous A block into the cache or buffer2, since that's where we need it to be when we go to merge it anyway
|
||||
if (block_size <= cache.len) {
|
||||
|
@ -610,7 +610,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
} else {
|
||||
blockSwap(T, items, blockA.start, buffer2.start, block_size);
|
||||
}
|
||||
|
||||
|
||||
// this is equivalent to rotating, but faster
|
||||
// the area normally taken up by the A block is either the contents of buffer2, or data we don't need anymore since we memcopied it
|
||||
// either way, we don't need to retain the order of those items, so instead of rotating we can just block swap B to where it belongs
|
||||
|
@ -619,21 +619,21 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
// we are unable to use the 'buffer2' trick to speed up the rotation operation since buffer2 doesn't exist, so perform a normal rotation
|
||||
mem.rotate(T, items[B_split..blockA.start + block_size], blockA.start - B_split);
|
||||
}
|
||||
|
||||
|
||||
// update the range for the remaining A blocks, and the range remaining from the B block after it was split
|
||||
lastA = Range.init(blockA.start - B_remaining, blockA.start - B_remaining + block_size);
|
||||
lastB = Range.init(lastA.end, lastA.end + B_remaining);
|
||||
|
||||
|
||||
// if there are no more A blocks remaining, this step is finished!
|
||||
blockA.start += block_size;
|
||||
if (blockA.length() == 0)
|
||||
break;
|
||||
|
||||
|
||||
} else if (blockB.length() < block_size) {
|
||||
// move the last B block, which is unevenly sized, to before the remaining A blocks, by using a rotation
|
||||
// the cache is disabled here since it might contain the contents of the previous A block
|
||||
mem.rotate(T, items[blockA.start..blockB.end], blockB.start - blockA.start);
|
||||
|
||||
|
||||
lastB = Range.init(blockA.start, blockA.start + blockB.length());
|
||||
blockA.start += blockB.length();
|
||||
blockA.end += blockB.length();
|
||||
|
@ -642,11 +642,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
// roll the leftmost A block to the end by swapping it with the next B block
|
||||
blockSwap(T, items, blockA.start, blockB.start, block_size);
|
||||
lastB = Range.init(blockA.start, blockA.start + block_size);
|
||||
|
||||
|
||||
blockA.start += block_size;
|
||||
blockA.end += block_size;
|
||||
blockB.start += block_size;
|
||||
|
||||
|
||||
if (blockB.end > B.end - block_size) {
|
||||
blockB.end = B.end;
|
||||
} else {
|
||||
|
@ -655,7 +655,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// merge the last A block with the remaining B values
|
||||
if (lastA.length() <= cache.len) {
|
||||
mergeExternal(T, items, lastA, Range.init(lastA.end, B.end), lessThan, cache[0..]);
|
||||
|
@ -666,14 +666,14 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// when we're finished with this merge step we should have the one or two internal buffers left over, where the second buffer is all jumbled up
|
||||
// insertion sort the second buffer, then redistribute the buffers back into the items using the opposite process used for creating the buffer
|
||||
|
||||
|
||||
// while an unstable sort like quicksort could be applied here, in benchmarks it was consistently slightly slower than a simple insertion sort,
|
||||
// even for tens of millions of items. this may be because insertion sort is quite fast when the data is already somewhat sorted, like it is here
|
||||
insertionSort(T, items[buffer2.start..buffer2.end], lessThan);
|
||||
|
||||
|
||||
pull_index = 0;
|
||||
while (pull_index < 2) : (pull_index += 1) {
|
||||
var unique = pull[pull_index].count * 2;
|
||||
|
@ -702,7 +702,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// double the size of each A and B subarray that will be merged in the next level
|
||||
if (!iterator.nextLevel()) break;
|
||||
}
|
||||
|
@ -711,37 +711,37 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
|
|||
// merge operation without a buffer
|
||||
fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const Range, lessThan: fn(&const T,&const T)bool) void {
|
||||
if (A_arg.length() == 0 or B_arg.length() == 0) return;
|
||||
|
||||
|
||||
// this just repeatedly binary searches into B and rotates A into position.
|
||||
// the paper suggests using the 'rotation-based Hwang and Lin algorithm' here,
|
||||
// but I decided to stick with this because it had better situational performance
|
||||
//
|
||||
//
|
||||
// (Hwang and Lin is designed for merging subarrays of very different sizes,
|
||||
// but WikiSort almost always uses subarrays that are roughly the same size)
|
||||
//
|
||||
//
|
||||
// normally this is incredibly suboptimal, but this function is only called
|
||||
// when none of the A or B blocks in any subarray contained 2√A unique values,
|
||||
// which places a hard limit on the number of times this will ACTUALLY need
|
||||
// to binary search and rotate.
|
||||
//
|
||||
//
|
||||
// according to my analysis the worst case is √A rotations performed on √A items
|
||||
// once the constant factors are removed, which ends up being O(n)
|
||||
//
|
||||
//
|
||||
// again, this is NOT a general-purpose solution – it only works well in this case!
|
||||
// kind of like how the O(n^2) insertion sort is used in some places
|
||||
|
||||
var A = *A_arg;
|
||||
var B = *B_arg;
|
||||
|
||||
|
||||
while (true) {
|
||||
// find the first place in B where the first item in A needs to be inserted
|
||||
const mid = binaryFirst(T, items, items[A.start], B, lessThan);
|
||||
|
||||
|
||||
// rotate A into place
|
||||
const amount = mid - A.end;
|
||||
mem.rotate(T, items[A.start..mid], A.length());
|
||||
if (B.end == mid) break;
|
||||
|
||||
|
||||
// calculate the new A and B ranges
|
||||
B.start = mid;
|
||||
A = Range.init(A.start + amount, B.start);
|
||||
|
@ -757,7 +757,7 @@ fn mergeInternal(comptime T: type, items: []T, A: &const Range, B: &const Range,
|
|||
var A_count: usize = 0;
|
||||
var B_count: usize = 0;
|
||||
var insert: usize = 0;
|
||||
|
||||
|
||||
if (B.length() > 0 and A.length() > 0) {
|
||||
while (true) {
|
||||
if (!lessThan(items[B.start + B_count], items[buffer.start + A_count])) {
|
||||
|
@ -773,7 +773,7 @@ fn mergeInternal(comptime T: type, items: []T, A: &const Range, B: &const Range,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// swap the remainder of A into the final array
|
||||
blockSwap(T, items, buffer.start + A_count, A.start + insert, A.length() - A_count);
|
||||
}
|
||||
|
@ -790,56 +790,56 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s
|
|||
fn findFirstForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool, unique: usize) usize {
|
||||
if (range.length() == 0) return range.start;
|
||||
const skip = math.max(range.length()/unique, usize(1));
|
||||
|
||||
|
||||
var index = range.start + skip;
|
||||
while (lessThan(items[index - 1], value)) : (index += skip) {
|
||||
if (index >= range.end - skip) {
|
||||
return binaryFirst(T, items, value, Range.init(index, range.end), lessThan);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan);
|
||||
}
|
||||
|
||||
fn findFirstBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool, unique: usize) usize {
|
||||
if (range.length() == 0) return range.start;
|
||||
const skip = math.max(range.length()/unique, usize(1));
|
||||
|
||||
|
||||
var index = range.end - skip;
|
||||
while (index > range.start and !lessThan(items[index - 1], value)) : (index -= skip) {
|
||||
if (index < range.start + skip) {
|
||||
return binaryFirst(T, items, value, Range.init(range.start, index), lessThan);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan);
|
||||
}
|
||||
|
||||
fn findLastForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool, unique: usize) usize {
|
||||
if (range.length() == 0) return range.start;
|
||||
const skip = math.max(range.length()/unique, usize(1));
|
||||
|
||||
|
||||
var index = range.start + skip;
|
||||
while (!lessThan(value, items[index - 1])) : (index += skip) {
|
||||
if (index >= range.end - skip) {
|
||||
return binaryLast(T, items, value, Range.init(index, range.end), lessThan);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return binaryLast(T, items, value, Range.init(index - skip, index), lessThan);
|
||||
}
|
||||
|
||||
fn findLastBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool, unique: usize) usize {
|
||||
if (range.length() == 0) return range.start;
|
||||
const skip = math.max(range.length()/unique, usize(1));
|
||||
|
||||
|
||||
var index = range.end - skip;
|
||||
while (index > range.start and lessThan(value, items[index - 1])) : (index -= skip) {
|
||||
if (index < range.start + skip) {
|
||||
return binaryLast(T, items, value, Range.init(range.start, index), lessThan);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return binaryLast(T, items, value, Range.init(index, index + skip), lessThan);
|
||||
}
|
||||
|
||||
|
@ -885,7 +885,7 @@ fn mergeInto(comptime T: type, from: []T, A: &const Range, B: &const Range, less
|
|||
const A_last = A.end;
|
||||
const B_last = B.end;
|
||||
var insert_index: usize = 0;
|
||||
|
||||
|
||||
while (true) {
|
||||
if (!lessThan(from[B_index], from[A_index])) {
|
||||
into[insert_index] = from[A_index];
|
||||
|
@ -916,7 +916,7 @@ fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range,
|
|||
var insert_index: usize = A.start;
|
||||
const A_last = A.length();
|
||||
const B_last = B.end;
|
||||
|
||||
|
||||
if (B.length() > 0 and A.length() > 0) {
|
||||
while (true) {
|
||||
if (!lessThan(items[B_index], cache[A_index])) {
|
||||
|
@ -932,7 +932,7 @@ fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// copy the remainder of A into the final array
|
||||
mem.copy(T, items[insert_index..], cache[A_index..A_last]);
|
||||
}
|
||||
|
@ -1081,17 +1081,17 @@ test "another sort case" {
|
|||
}
|
||||
|
||||
test "sort fuzz testing" {
|
||||
var rng = std.rand.Rand.init(0x12345678);
|
||||
var prng = std.rand.DefaultPrng.init(0x12345678);
|
||||
const test_case_count = 10;
|
||||
var i: usize = 0;
|
||||
while (i < test_case_count) : (i += 1) {
|
||||
fuzzTest(&rng);
|
||||
fuzzTest(&prng.random);
|
||||
}
|
||||
}
|
||||
|
||||
var fixed_buffer_mem: [100 * 1024]u8 = undefined;
|
||||
|
||||
fn fuzzTest(rng: &std.rand.Rand) void {
|
||||
fn fuzzTest(rng: &std.rand.Random) void {
|
||||
const array_size = rng.range(usize, 0, 1000);
|
||||
var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
|
||||
var array = fixed_allocator.allocator.alloc(IdAndValue, array_size) catch unreachable;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const Builder = @import("std").build.Builder;
|
||||
|
||||
pub fn build(b: &Builder) !void {
|
||||
pub fn build(b: &Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
const exe = b.addExecutable("YOUR_NAME_HERE", "src/main.zig");
|
||||
exe.setBuildMode(mode);
|
||||
|
|
|
@ -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 {
|
||||
|
|
1400
std/zig/ast.zig
1400
std/zig/ast.zig
File diff suppressed because it is too large
Load Diff
4370
std/zig/parser.zig
4370
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,38 +76,74 @@ pub const Token = struct {
|
|||
Invalid,
|
||||
Identifier,
|
||||
StringLiteral: StrLitKind,
|
||||
StringIdentifier,
|
||||
MultilineStringLiteralLine: StrLitKind,
|
||||
CharLiteral,
|
||||
Eof,
|
||||
Builtin,
|
||||
Bang,
|
||||
Pipe,
|
||||
PipePipe,
|
||||
PipeEqual,
|
||||
Equal,
|
||||
EqualEqual,
|
||||
EqualAngleBracketRight,
|
||||
BangEqual,
|
||||
LParen,
|
||||
RParen,
|
||||
Semicolon,
|
||||
Percent,
|
||||
PercentEqual,
|
||||
LBrace,
|
||||
RBrace,
|
||||
LBracket,
|
||||
RBracket,
|
||||
Period,
|
||||
Ellipsis2,
|
||||
Ellipsis3,
|
||||
Caret,
|
||||
CaretEqual,
|
||||
Plus,
|
||||
PlusPlus,
|
||||
PlusEqual,
|
||||
PlusPercent,
|
||||
PlusPercentEqual,
|
||||
Minus,
|
||||
MinusEqual,
|
||||
MinusPercent,
|
||||
MinusPercentEqual,
|
||||
Asterisk,
|
||||
AsteriskEqual,
|
||||
AsteriskAsterisk,
|
||||
AsteriskPercent,
|
||||
AsteriskPercentEqual,
|
||||
Arrow,
|
||||
Colon,
|
||||
Slash,
|
||||
SlashEqual,
|
||||
Comma,
|
||||
Ampersand,
|
||||
AmpersandEqual,
|
||||
QuestionMark,
|
||||
QuestionMarkQuestionMark,
|
||||
AngleBracketLeft,
|
||||
AngleBracketLeftEqual,
|
||||
AngleBracketAngleBracketLeft,
|
||||
AngleBracketAngleBracketLeftEqual,
|
||||
AngleBracketRight,
|
||||
AngleBracketRightEqual,
|
||||
AngleBracketAngleBracketRight,
|
||||
AngleBracketAngleBracketRightEqual,
|
||||
Tilde,
|
||||
IntegerLiteral,
|
||||
FloatLiteral,
|
||||
LineComment,
|
||||
Keyword_align,
|
||||
Keyword_and,
|
||||
Keyword_asm,
|
||||
Keyword_async,
|
||||
Keyword_await,
|
||||
Keyword_break,
|
||||
Keyword_cancel,
|
||||
Keyword_catch,
|
||||
Keyword_comptime,
|
||||
Keyword_const,
|
||||
|
@ -111,6 +151,7 @@ pub const Token = struct {
|
|||
Keyword_defer,
|
||||
Keyword_else,
|
||||
Keyword_enum,
|
||||
Keyword_errdefer,
|
||||
Keyword_error,
|
||||
Keyword_export,
|
||||
Keyword_extern,
|
||||
|
@ -125,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,
|
||||
|
@ -147,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;
|
||||
|
@ -183,8 +232,6 @@ pub const Tokenizer = struct {
|
|||
return Tokenizer {
|
||||
.buffer = buffer,
|
||||
.index = 0,
|
||||
.line = 0,
|
||||
.column = 0,
|
||||
.pending_invalid_token = null,
|
||||
};
|
||||
}
|
||||
|
@ -196,10 +243,19 @@ pub const Tokenizer = struct {
|
|||
C,
|
||||
StringLiteral,
|
||||
StringLiteralBackslash,
|
||||
MultilineStringLiteralLine,
|
||||
MultilineStringLiteralLineBackslash,
|
||||
CharLiteral,
|
||||
CharLiteralBackslash,
|
||||
CharLiteralEnd,
|
||||
Backslash,
|
||||
Equal,
|
||||
Bang,
|
||||
Pipe,
|
||||
Minus,
|
||||
MinusPercent,
|
||||
Asterisk,
|
||||
AsteriskPercent,
|
||||
Slash,
|
||||
LineComment,
|
||||
Zero,
|
||||
|
@ -210,6 +266,15 @@ pub const Tokenizer = struct {
|
|||
FloatExponentUnsigned,
|
||||
FloatExponentNumber,
|
||||
Ampersand,
|
||||
Caret,
|
||||
Percent,
|
||||
QuestionMark,
|
||||
Plus,
|
||||
PlusPercent,
|
||||
AngleBracketLeft,
|
||||
AngleBracketAngleBracketLeft,
|
||||
AngleBracketRight,
|
||||
AngleBracketAngleBracketRight,
|
||||
Period,
|
||||
Period2,
|
||||
SawAtSign,
|
||||
|
@ -220,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;
|
||||
|
@ -249,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;
|
||||
|
@ -275,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;
|
||||
|
@ -291,9 +365,29 @@ pub const Tokenizer = struct {
|
|||
break;
|
||||
},
|
||||
'%' => {
|
||||
result.id = Token.Id.Percent;
|
||||
self.index += 1;
|
||||
break;
|
||||
state = State.Percent;
|
||||
},
|
||||
'*' => {
|
||||
state = State.Asterisk;
|
||||
},
|
||||
'+' => {
|
||||
state = State.Plus;
|
||||
},
|
||||
'?' => {
|
||||
state = State.QuestionMark;
|
||||
},
|
||||
'<' => {
|
||||
state = State.AngleBracketLeft;
|
||||
},
|
||||
'>' => {
|
||||
state = State.AngleBracketRight;
|
||||
},
|
||||
'^' => {
|
||||
state = State.Caret;
|
||||
},
|
||||
'\\' => {
|
||||
state = State.Backslash;
|
||||
result.id = Token.Id { .MultilineStringLiteralLine = Token.StrLitKind.Normal };
|
||||
},
|
||||
'{' => {
|
||||
result.id = Token.Id.LBrace;
|
||||
|
@ -305,6 +399,11 @@ pub const Tokenizer = struct {
|
|||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'~' => {
|
||||
result.id = Token.Id.Tilde;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'.' => {
|
||||
state = State.Period;
|
||||
},
|
||||
|
@ -334,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 => {
|
||||
|
@ -356,6 +455,107 @@ pub const Tokenizer = struct {
|
|||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.Asterisk => switch (c) {
|
||||
'=' => {
|
||||
result.id = Token.Id.AsteriskEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'*' => {
|
||||
result.id = Token.Id.AsteriskAsterisk;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'%' => {
|
||||
state = State.AsteriskPercent;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Asterisk;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
State.AsteriskPercent => switch (c) {
|
||||
'=' => {
|
||||
result.id = Token.Id.AsteriskPercentEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.AsteriskPercent;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
State.QuestionMark => switch (c) {
|
||||
'?' => {
|
||||
result.id = Token.Id.QuestionMarkQuestionMark;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.QuestionMark;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.Percent => switch (c) {
|
||||
'=' => {
|
||||
result.id = Token.Id.PercentEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Percent;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.Plus => switch (c) {
|
||||
'=' => {
|
||||
result.id = Token.Id.PlusEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'+' => {
|
||||
result.id = Token.Id.PlusPlus;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'%' => {
|
||||
state = State.PlusPercent;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Plus;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.PlusPercent => switch (c) {
|
||||
'=' => {
|
||||
result.id = Token.Id.PlusPercentEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.PlusPercent;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.Caret => switch (c) {
|
||||
'=' => {
|
||||
result.id = Token.Id.CaretEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Caret;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
State.Identifier => switch (c) {
|
||||
'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
|
||||
else => {
|
||||
|
@ -369,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 };
|
||||
|
@ -399,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;
|
||||
|
@ -417,6 +684,11 @@ pub const Tokenizer = struct {
|
|||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'|' => {
|
||||
result.id = Token.Id.PipePipe;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Pipe;
|
||||
break;
|
||||
|
@ -429,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;
|
||||
|
@ -441,12 +718,86 @@ pub const Tokenizer = struct {
|
|||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'=' => {
|
||||
result.id = Token.Id.MinusEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'%' => {
|
||||
state = State.MinusPercent;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Minus;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.MinusPercent => switch (c) {
|
||||
'=' => {
|
||||
result.id = Token.Id.MinusPercentEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.MinusPercent;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
State.AngleBracketLeft => switch (c) {
|
||||
'<' => {
|
||||
state = State.AngleBracketAngleBracketLeft;
|
||||
},
|
||||
'=' => {
|
||||
result.id = Token.Id.AngleBracketLeftEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.AngleBracketLeft;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.AngleBracketAngleBracketLeft => switch (c) {
|
||||
'=' => {
|
||||
result.id = Token.Id.AngleBracketAngleBracketLeftEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.AngleBracketAngleBracketLeft;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.AngleBracketRight => switch (c) {
|
||||
'>' => {
|
||||
state = State.AngleBracketAngleBracketRight;
|
||||
},
|
||||
'=' => {
|
||||
result.id = Token.Id.AngleBracketRightEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.AngleBracketRight;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.AngleBracketAngleBracketRight => switch (c) {
|
||||
'=' => {
|
||||
result.id = Token.Id.AngleBracketAngleBracketRightEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.AngleBracketAngleBracketRight;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.Period => switch (c) {
|
||||
'.' => {
|
||||
state = State.Period2;
|
||||
|
@ -474,6 +825,11 @@ pub const Tokenizer = struct {
|
|||
result.id = Token.Id.LineComment;
|
||||
state = State.LineComment;
|
||||
},
|
||||
'=' => {
|
||||
result.id = Token.Id.SlashEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Slash;
|
||||
break;
|
||||
|
@ -547,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,
|
||||
|
@ -564,6 +912,7 @@ pub const Tokenizer = struct {
|
|||
State.FloatFraction,
|
||||
State.FloatExponentNumber,
|
||||
State.StringLiteral, // find this error later
|
||||
State.MultilineStringLiteralLine,
|
||||
State.Builtin => {},
|
||||
|
||||
State.Identifier => {
|
||||
|
@ -578,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;
|
||||
},
|
||||
|
@ -609,8 +963,45 @@ pub const Tokenizer = struct {
|
|||
State.Pipe => {
|
||||
result.id = Token.Id.Pipe;
|
||||
},
|
||||
State.AngleBracketAngleBracketRight => {
|
||||
result.id = Token.Id.AngleBracketAngleBracketRight;
|
||||
},
|
||||
State.AngleBracketRight => {
|
||||
result.id = Token.Id.AngleBracketRight;
|
||||
},
|
||||
State.AngleBracketAngleBracketLeft => {
|
||||
result.id = Token.Id.AngleBracketAngleBracketLeft;
|
||||
},
|
||||
State.AngleBracketLeft => {
|
||||
result.id = Token.Id.AngleBracketLeft;
|
||||
},
|
||||
State.PlusPercent => {
|
||||
result.id = Token.Id.PlusPercent;
|
||||
},
|
||||
State.Plus => {
|
||||
result.id = Token.Id.Plus;
|
||||
},
|
||||
State.QuestionMark => {
|
||||
result.id = Token.Id.QuestionMark;
|
||||
},
|
||||
State.Percent => {
|
||||
result.id = Token.Id.Percent;
|
||||
},
|
||||
State.Caret => {
|
||||
result.id = Token.Id.Caret;
|
||||
},
|
||||
State.AsteriskPercent => {
|
||||
result.id = Token.Id.AsteriskPercent;
|
||||
},
|
||||
State.Asterisk => {
|
||||
result.id = Token.Id.Asterisk;
|
||||
},
|
||||
State.MinusPercent => {
|
||||
result.id = Token.Id.MinusPercent;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (result.id == Token.Id.Eof) {
|
||||
if (self.pending_invalid_token) |token| {
|
||||
self.pending_invalid_token = null;
|
||||
|
@ -634,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,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -685,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" {
|
||||
|
@ -739,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,
|
||||
|
@ -752,8 +1148,8 @@ test "tokenizer - string identifier and builtin fns" {
|
|||
|
||||
test "tokenizer - pipe and then invalid" {
|
||||
testTokenize("||=", []Token.Id{
|
||||
Token.Id.Pipe,
|
||||
Token.Id.PipeEqual,
|
||||
Token.Id.PipePipe,
|
||||
Token.Id.Equal,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ pub fn addCases(cases: &tests.BuildExamplesContext) void {
|
|||
cases.addBuildFile("example/mix_o_files/build.zig");
|
||||
}
|
||||
cases.addBuildFile("test/standalone/issue_339/build.zig");
|
||||
cases.addBuildFile("test/standalone/issue_794/build.zig");
|
||||
cases.addBuildFile("test/standalone/pkg_import/build.zig");
|
||||
cases.addBuildFile("test/standalone/use_alias/build.zig");
|
||||
cases.addBuildFile("test/standalone/brace_expansion/build.zig");
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
var x: i32 = 1;
|
||||
|
||||
test "create a coroutine and cancel it" {
|
||||
const p = try async(std.debug.global_allocator) simpleAsyncFn();
|
||||
const p = try async<std.debug.global_allocator> simpleAsyncFn();
|
||||
comptime assert(@typeOf(p) == promise->void);
|
||||
cancel p;
|
||||
assert(x == 2);
|
||||
}
|
||||
|
@ -17,7 +19,7 @@ async fn simpleAsyncFn() void {
|
|||
|
||||
test "coroutine suspend, resume, cancel" {
|
||||
seq('a');
|
||||
const p = try async(std.debug.global_allocator) testAsyncSeq();
|
||||
const p = try async<std.debug.global_allocator> testAsyncSeq();
|
||||
seq('c');
|
||||
resume p;
|
||||
seq('f');
|
||||
|
@ -43,7 +45,7 @@ fn seq(c: u8) void {
|
|||
}
|
||||
|
||||
test "coroutine suspend with block" {
|
||||
const p = try async(std.debug.global_allocator) testSuspendBlock();
|
||||
const p = try async<std.debug.global_allocator> testSuspendBlock();
|
||||
std.debug.assert(!result);
|
||||
resume a_promise;
|
||||
std.debug.assert(result);
|
||||
|
@ -55,6 +57,7 @@ var result = false;
|
|||
|
||||
async fn testSuspendBlock() void {
|
||||
suspend |p| {
|
||||
comptime assert(@typeOf(p) == promise->void);
|
||||
a_promise = p;
|
||||
}
|
||||
result = true;
|
||||
|
@ -65,7 +68,7 @@ var await_final_result: i32 = 0;
|
|||
|
||||
test "coroutine await" {
|
||||
await_seq('a');
|
||||
const p = async(std.debug.global_allocator) await_amain() catch unreachable;
|
||||
const p = async<std.debug.global_allocator> await_amain() catch unreachable;
|
||||
await_seq('f');
|
||||
resume await_a_promise;
|
||||
await_seq('i');
|
||||
|
@ -104,7 +107,7 @@ var early_final_result: i32 = 0;
|
|||
|
||||
test "coroutine await early return" {
|
||||
early_seq('a');
|
||||
const p = async(std.debug.global_allocator) early_amain() catch unreachable;
|
||||
const p = async<std.debug.global_allocator> early_amain() catch unreachable;
|
||||
early_seq('f');
|
||||
assert(early_final_result == 1234);
|
||||
assert(std.mem.eql(u8, early_points, "abcdef"));
|
||||
|
@ -133,7 +136,7 @@ fn early_seq(c: u8) void {
|
|||
|
||||
test "coro allocation failure" {
|
||||
var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0);
|
||||
if (async(&failing_allocator.allocator) asyncFuncThatNeverGetsRun()) {
|
||||
if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) {
|
||||
@panic("expected allocation failure");
|
||||
} else |err| switch (err) {
|
||||
error.OutOfMemory => {},
|
||||
|
@ -143,3 +146,81 @@ test "coro allocation failure" {
|
|||
async fn asyncFuncThatNeverGetsRun() void {
|
||||
@panic("coro frame allocation should fail");
|
||||
}
|
||||
|
||||
test "async function with dot syntax" {
|
||||
const S = struct {
|
||||
var y: i32 = 1;
|
||||
async fn foo() void {
|
||||
y += 1;
|
||||
suspend;
|
||||
}
|
||||
};
|
||||
const p = try async<std.debug.global_allocator> S.foo();
|
||||
cancel p;
|
||||
assert(S.y == 2);
|
||||
}
|
||||
|
||||
test "async fn pointer in a struct field" {
|
||||
var data: i32 = 1;
|
||||
const Foo = struct {
|
||||
bar: async<&std.mem.Allocator> fn(&i32) void,
|
||||
};
|
||||
var foo = Foo {
|
||||
.bar = simpleAsyncFn2,
|
||||
};
|
||||
const p = (async<std.debug.global_allocator> foo.bar(&data)) catch unreachable;
|
||||
assert(data == 2);
|
||||
cancel p;
|
||||
assert(data == 4);
|
||||
}
|
||||
|
||||
async<&std.mem.Allocator> fn simpleAsyncFn2(y: &i32) void {
|
||||
defer *y += 2;
|
||||
*y += 1;
|
||||
suspend;
|
||||
}
|
||||
|
||||
test "async fn with inferred error set" {
|
||||
const p = (async<std.debug.global_allocator> failing()) catch unreachable;
|
||||
resume p;
|
||||
cancel p;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const assert = @import("std").debug.assert;
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
test "compile time recursion" {
|
||||
|
@ -503,3 +504,12 @@ test "const ptr to comptime mutable data is not memoized" {
|
|||
assert(foo.read_x() == 2);
|
||||
}
|
||||
}
|
||||
|
||||
test "array concat of slices gives slice" {
|
||||
comptime {
|
||||
var a: []const u8 = "aoeu";
|
||||
var b: []const u8 = "asdf";
|
||||
const c = a ++ b;
|
||||
assert(std.mem.eql(u8, c, "aoeuasdf"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
const tests = @import("tests.zig");
|
||||
|
||||
pub fn addCases(cases: &tests.CompileErrorContext) void {
|
||||
cases.add("wrong type passed to @panic",
|
||||
\\export fn entry() void {
|
||||
\\ var e = error.Foo;
|
||||
\\ @panic(e);
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:3:12: error: expected type '[]const u8', found 'error{Foo}'");
|
||||
|
||||
|
||||
cases.add("@tagName used on union with no associated enum tag",
|
||||
\\const FloatInt = extern union {
|
||||
\\ Float: f32,
|
||||
|
@ -17,7 +26,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
|
|||
cases.add("returning error from void async function",
|
||||
\\const std = @import("std");
|
||||
\\export fn entry() void {
|
||||
\\ const p = async(std.debug.global_allocator) amain() catch unreachable;
|
||||
\\ const p = async<std.debug.global_allocator> amain() catch unreachable;
|
||||
\\}
|
||||
\\async fn amain() void {
|
||||
\\ return error.ShouldBeCompileError;
|
||||
|
|
|
@ -51,6 +51,16 @@ pub fn addCases(cases: &tests.GenHContext) void {
|
|||
\\
|
||||
);
|
||||
|
||||
cases.add("declare opaque type",
|
||||
\\export const Foo = @OpaqueType();
|
||||
\\
|
||||
\\export fn entry(foo: ?&Foo) void { }
|
||||
,
|
||||
\\struct Foo;
|
||||
\\
|
||||
\\TEST_EXPORT void entry(struct Foo * foo);
|
||||
);
|
||||
|
||||
cases.add("array field-type",
|
||||
\\const Foo = extern struct {
|
||||
\\ A: [2]i32,
|
||||
|
@ -66,4 +76,5 @@ pub fn addCases(cases: &tests.GenHContext) void {
|
|||
\\TEST_EXPORT void entry(struct Foo foo, uint8_t bar[]);
|
||||
\\
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
@ -281,4 +281,34 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
|
|||
\\ f.float = 12.34;
|
||||
\\}
|
||||
);
|
||||
|
||||
// This case makes sure that the code compiles and runs. There is not actually a special
|
||||
// runtime safety check having to do specifically with error return traces across suspend points.
|
||||
cases.addRuntimeSafety("error return trace across suspend points",
|
||||
\\const std = @import("std");
|
||||
\\
|
||||
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
|
||||
\\ std.os.exit(126);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn main() void {
|
||||
\\ const p = nonFailing();
|
||||
\\ resume p;
|
||||
\\ const p2 = async<std.debug.global_allocator> printTrace(p) catch unreachable;
|
||||
\\ cancel p2;
|
||||
\\}
|
||||
\\
|
||||
\\fn nonFailing() promise->error!void {
|
||||
\\ return async<std.debug.global_allocator> failing() catch unreachable;
|
||||
\\}
|
||||
\\
|
||||
\\async fn failing() error!void {
|
||||
\\ suspend;
|
||||
\\ return error.Fail;
|
||||
\\}
|
||||
\\
|
||||
\\async fn printTrace(p: promise->error!void) void {
|
||||
\\ (await p) catch unreachable;
|
||||
\\}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
#define NUMBER 1234
|
|
@ -0,0 +1,11 @@
|
|||
const Builder = @import("std").build.Builder;
|
||||
|
||||
pub fn build(b: &Builder) void {
|
||||
const test_artifact = b.addTest("main.zig");
|
||||
test_artifact.addIncludeDir("a_directory");
|
||||
|
||||
b.default_step.dependOn(&test_artifact.step);
|
||||
|
||||
const test_step = b.step("test", "Test the program");
|
||||
test_step.dependOn(&test_artifact.step);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
const c = @cImport(@cInclude("foo.h"));
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
test "c import" {
|
||||
comptime assert(c.NUMBER == 1234);
|
||||
}
|
Loading…
Reference in New Issue