mirror of
https://github.com/ziglang/zig.git
synced 2026-02-04 13:43:46 +00:00
Merge pull request #1 from zig-lang/master
Sync with zig-lang/zig master
This commit is contained in:
commit
8c1872543c
22
.travis.yml
22
.travis.yml
@ -1,18 +1,22 @@
|
||||
sudo: required
|
||||
services:
|
||||
- docker
|
||||
- docker
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
- linux
|
||||
- osx
|
||||
dist: trusty
|
||||
osx_image: xcode8.3
|
||||
language: cpp
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_before_install; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_before_install; fi
|
||||
install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_install; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_install; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_install; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_install; fi
|
||||
script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_script; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_script; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_script; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_script; fi
|
||||
env:
|
||||
global:
|
||||
- secure: QmJ+eLOxj3Irl5SHxt6lQvrj7++1AIz8bYri6RScAQGHQPIztkmbpBjAkpFgYaWPkZ04ROtamFXdS7oHtJHSECesgPoqM/CHIychQkgpDq30+TsFyYbBpDGHY+N6r2WnQTvg+9EuAp6P365us6qFS0D5zQ3P40c56uMbazFu3J4W1HZP+pLWlLjEXaN88ePhHWqNZyvwGMkLpYl3ghcrE9H4vGZQ7jenRW4UmskLEkuhUPJbQiow3Td8arJiRmLVISzWqneqNraLUpGyUVr4F3Rbjzacfoo3r9ZZynhY0mFsEye82x6TMGgH2xsNGkd91zpQuckWUT+pQv/G6FXpnEnjIJSO2Z5WAxXrx6xB1k2HZ17/4NWLF3fJVhdQJm3mS6odeGzUjgGrl1A42evxU+7VbcofEJq1aMiLgU1jUT2pt+pefCwmKJYLpEsSzuyrVxgvskQz0QpC053TAYSNf2Jj6Qhg9YDWyOeemYmDgffTqErF7AYhc6NKH0s0XKkIiNFSxorkEsfG/Ck1o+15slHNmWZXlmXToxDqFkLDoPvfGKg7koU5YTGvci/F9ZKb1juhGLxZbwap/18zN40BqA+Ip2yDBJAKxsIiwSjSIguy6g/Z1I50s0xNGOr36urfRRQX5H+rqr/xCZ63B6WSe6qBcZboWAQMDn8HLS9Xiwc=
|
||||
- secure: dnb7r5guUeMOX9e7XlPUSZzmga8VW3G9Q1aa7LxEKiTjSnWhu5KpPDe8o1X3Rj6nc5iXDqmBH/C/7eNXPDyXJJWPvpE2YRpGymyUkRaakul0QBKJEaMvwy2SuAfS69CWC+TSzfGRvtSYkdpBhhLvs0h5S819S5jYbCNSCmOKfFucaP5NsHNIZ/I19oIeTPTa0/UnVm7DLFZXZjvbS+czkdyH1DhbT85sLj+XqNTzLePImE68efrjaHnlSy/CzBVJzj55UgD5i9fxNCQWzGWim/SD5xZ0zKtLycSOf6wQN2lCo0lkjw9rDlYz69mM5L9ikfYL9oHDPZnh84oXKglQ5miOHCgqs/qs4439I05lIu8i/EfbFA55YG4NyO3rL9YVOOt5gwiwvJYhDcnkVVzSl0o5bsoZgQfYvPWaIQKNkl3C53zfDQjgqS54CeDzlZpFrQTDQ1RrH8oeVC1gfYAeMabMDadox5rfZmLIN5JTf/F8iD/QdxGcoUvkEENcQgfP9PnubExtexgHGsEmqbm6ORSZ1MkEh2m3fo0f8KE6TbN1UigmcQ8nTkWBHsSmfHnB8HwJQp8mwQmDamXA+Hl3e3w4LOdYkJVlNW1/TTyJJOOvjMQCjF8SJmPHuh+QpqKbSaT9XM/vBhxbIZEufH8kawJKCBBcCNspGMNjhXfNjM0=
|
||||
|
||||
@ -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})
|
||||
|
||||
@ -410,6 +415,9 @@ set(ZIG_CPP_SOURCES
|
||||
|
||||
set(ZIG_STD_FILES
|
||||
"array_list.zig"
|
||||
"atomic/index.zig"
|
||||
"atomic/queue.zig"
|
||||
"atomic/stack.zig"
|
||||
"base64.zig"
|
||||
"buf_map.zig"
|
||||
"buf_set.zig"
|
||||
@ -419,33 +427,34 @@ set(ZIG_STD_FILES
|
||||
"c/index.zig"
|
||||
"c/linux.zig"
|
||||
"c/windows.zig"
|
||||
"crypto/blake2.zig"
|
||||
"crypto/hmac.zig"
|
||||
"crypto/index.zig"
|
||||
"crypto/md5.zig"
|
||||
"crypto/sha1.zig"
|
||||
"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/index.zig"
|
||||
"hash/siphash.zig"
|
||||
"hash_map.zig"
|
||||
"heap.zig"
|
||||
"index.zig"
|
||||
"io.zig"
|
||||
"json.zig"
|
||||
"linked_list.zig"
|
||||
"macho.zig"
|
||||
"math/acos.zig"
|
||||
@ -457,6 +466,28 @@ set(ZIG_STD_FILES
|
||||
"math/atanh.zig"
|
||||
"math/cbrt.zig"
|
||||
"math/ceil.zig"
|
||||
"math/complex/abs.zig"
|
||||
"math/complex/acos.zig"
|
||||
"math/complex/acosh.zig"
|
||||
"math/complex/arg.zig"
|
||||
"math/complex/asin.zig"
|
||||
"math/complex/asinh.zig"
|
||||
"math/complex/atan.zig"
|
||||
"math/complex/atanh.zig"
|
||||
"math/complex/conj.zig"
|
||||
"math/complex/cos.zig"
|
||||
"math/complex/cosh.zig"
|
||||
"math/complex/exp.zig"
|
||||
"math/complex/index.zig"
|
||||
"math/complex/ldexp.zig"
|
||||
"math/complex/log.zig"
|
||||
"math/complex/pow.zig"
|
||||
"math/complex/proj.zig"
|
||||
"math/complex/sin.zig"
|
||||
"math/complex/sinh.zig"
|
||||
"math/complex/sqrt.zig"
|
||||
"math/complex/tan.zig"
|
||||
"math/complex/tanh.zig"
|
||||
"math/copysign.zig"
|
||||
"math/cos.zig"
|
||||
"math/cosh.zig"
|
||||
@ -493,25 +524,28 @@ set(ZIG_STD_FILES
|
||||
"math/tan.zig"
|
||||
"math/tanh.zig"
|
||||
"math/trunc.zig"
|
||||
"math/x86_64/sqrt.zig"
|
||||
"mem.zig"
|
||||
"net.zig"
|
||||
"os/child_process.zig"
|
||||
"os/darwin.zig"
|
||||
"os/darwin_errno.zig"
|
||||
"os/epoch.zig"
|
||||
"os/file.zig"
|
||||
"os/get_user_id.zig"
|
||||
"os/index.zig"
|
||||
"os/linux/errno.zig"
|
||||
"os/linux/i386.zig"
|
||||
"os/linux/index.zig"
|
||||
"os/linux/vdso.zig"
|
||||
"os/linux/x86_64.zig"
|
||||
"os/path.zig"
|
||||
"os/time.zig"
|
||||
"os/windows/error.zig"
|
||||
"os/windows/index.zig"
|
||||
"os/windows/util.zig"
|
||||
"os/zen.zig"
|
||||
"rand/index.zig"
|
||||
"rand/ziggurat.zig"
|
||||
"segmented_list.zig"
|
||||
"sort.zig"
|
||||
"special/bootstrap.zig"
|
||||
"special/bootstrap_lib.zig"
|
||||
@ -542,7 +576,8 @@ set(ZIG_STD_FILES
|
||||
"unicode.zig"
|
||||
"zig/ast.zig"
|
||||
"zig/index.zig"
|
||||
"zig/parser.zig"
|
||||
"zig/parse.zig"
|
||||
"zig/render.zig"
|
||||
"zig/tokenizer.zig"
|
||||
)
|
||||
|
||||
|
||||
@ -141,10 +141,10 @@ libc. Create demo games using Zig.
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd)
|
||||
cmake ..
|
||||
make
|
||||
make install
|
||||
./zig build --build-file ../build.zig test
|
||||
bin/zig build --build-file ../build.zig test
|
||||
```
|
||||
|
||||
##### MacOS
|
||||
@ -154,9 +154,9 @@ brew install cmake llvm@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
|
||||
|
||||
@ -19,5 +19,5 @@ if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then
|
||||
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)
|
||||
s3cmd put -P empty s3://ziglang.org/builds/zig-linux-x86_64-$TRAVIS_BRANCH.tar.xz --add-header="Cache-Control: max-age=0, must-revalidate" --add-header=x-amz-website-redirect-location:/builds/$(ls $TRAVIS_BUILD_DIR/artifacts)
|
||||
fi
|
||||
|
||||
9
deps/lld/ELF/MarkLive.cpp
vendored
9
deps/lld/ELF/MarkLive.cpp
vendored
@ -301,6 +301,15 @@ template <class ELFT> void elf::markLive() {
|
||||
// Follow the graph to mark all live sections.
|
||||
doGcSections<ELFT>();
|
||||
|
||||
// If all references to a DSO happen to be weak, the DSO is removed from
|
||||
// DT_NEEDED, which creates dangling shared symbols to non-existent DSO.
|
||||
// We'll replace such symbols with undefined ones to fix it.
|
||||
for (Symbol *Sym : Symtab->getSymbols())
|
||||
if (auto *S = dyn_cast<SharedSymbol>(Sym))
|
||||
if (S->isWeak() && !S->getFile<ELFT>().IsNeeded)
|
||||
replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_WEAK, S->StOther,
|
||||
S->Type);
|
||||
|
||||
// Report garbage-collected sections.
|
||||
if (Config->PrintGcSections)
|
||||
for (InputSectionBase *Sec : InputSections)
|
||||
|
||||
@ -749,6 +749,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
|
||||
try build_args.append("--release-fast");
|
||||
try out.print(" --release-fast");
|
||||
},
|
||||
builtin.Mode.ReleaseSmall => {
|
||||
try build_args.append("--release-small");
|
||||
try out.print(" --release-small");
|
||||
},
|
||||
}
|
||||
for (code.link_objects) |link_object| {
|
||||
const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext);
|
||||
@ -810,6 +814,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
|
||||
try test_args.append("--release-fast");
|
||||
try out.print(" --release-fast");
|
||||
},
|
||||
builtin.Mode.ReleaseSmall => {
|
||||
try test_args.append("--release-small");
|
||||
try out.print(" --release-small");
|
||||
},
|
||||
}
|
||||
if (code.target_windows) {
|
||||
try test_args.appendSlice([][]const u8{
|
||||
@ -840,6 +848,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
|
||||
try test_args.append("--release-fast");
|
||||
try out.print(" --release-fast");
|
||||
},
|
||||
builtin.Mode.ReleaseSmall => {
|
||||
try test_args.append("--release-small");
|
||||
try out.print(" --release-small");
|
||||
},
|
||||
}
|
||||
const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);
|
||||
switch (result.term) {
|
||||
@ -874,6 +886,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
|
||||
builtin.Mode.Debug => {},
|
||||
builtin.Mode.ReleaseSafe => try test_args.append("--release-safe"),
|
||||
builtin.Mode.ReleaseFast => try test_args.append("--release-fast"),
|
||||
builtin.Mode.ReleaseSmall => try test_args.append("--release-small"),
|
||||
}
|
||||
|
||||
const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);
|
||||
@ -927,6 +940,12 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
|
||||
try out.print(" --release-fast");
|
||||
}
|
||||
},
|
||||
builtin.Mode.ReleaseSmall => {
|
||||
try build_args.append("--release-small");
|
||||
if (!code.is_inline) {
|
||||
try out.print(" --release-small");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if (maybe_error_match) |error_match| {
|
||||
|
||||
@ -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 {
|
||||
@ -3844,6 +3880,25 @@ pub fn main() void {
|
||||
{#header_open|@ArgType#}
|
||||
<p>TODO</p>
|
||||
{#header_close#}
|
||||
{#header_open|@atomicLoad#}
|
||||
<pre><code class="zig">@atomicLoad(comptime T: type, ptr: &const T, comptime ordering: builtin.AtomicOrder) -> T</code></pre>
|
||||
<p>
|
||||
This builtin function atomically dereferences a pointer and returns the value.
|
||||
</p>
|
||||
<p>
|
||||
<code>T</code> must be a pointer type, a <code>bool</code>,
|
||||
or an integer whose bit count meets these requirements:
|
||||
</p>
|
||||
<ul>
|
||||
<li>At least 8</li>
|
||||
<li>At most the same as usize</li>
|
||||
<li>Power of 2</li>
|
||||
</ul>
|
||||
<p>
|
||||
TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe
|
||||
we can remove this restriction
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@atomicRmw#}
|
||||
<pre><code class="zig">@atomicRmw(comptime T: type, ptr: &T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) -> T</code></pre>
|
||||
<p>
|
||||
@ -4010,16 +4065,60 @@ comptime {
|
||||
</p>
|
||||
|
||||
{#header_close#}
|
||||
{#header_open|@cmpxchg#}
|
||||
<pre><code class="zig">@cmpxchg(ptr: &T, cmp: T, new: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> bool</code></pre>
|
||||
{#header_open|@cmpxchgStrong#}
|
||||
<pre><code class="zig">@cmpxchgStrong(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> ?T</code></pre>
|
||||
<p>
|
||||
This function performs an atomic compare exchange operation.
|
||||
This function performs a strong atomic compare exchange operation. It's the equivalent of this code,
|
||||
except atomic:
|
||||
</p>
|
||||
{#code_begin|syntax#}
|
||||
fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T {
|
||||
const old_value = *ptr;
|
||||
if (old_value == expected_value) {
|
||||
*ptr = new_value;
|
||||
return null;
|
||||
} else {
|
||||
return old_value;
|
||||
}
|
||||
}
|
||||
{#code_end#}
|
||||
<p>
|
||||
If you are using cmpxchg in a loop, {#link|@cmpxchgWeak#} is the better choice, because it can be implemented
|
||||
more efficiently in machine instructions.
|
||||
</p>
|
||||
<p>
|
||||
<code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>.
|
||||
</p>
|
||||
<p><code>@typeOf(ptr).alignment</code> must be <code>>= @sizeOf(T).</code></p>
|
||||
{#see_also|Compile Variables#}
|
||||
{#see_also|Compile Variables|cmpxchgWeak#}
|
||||
{#header_close#}
|
||||
{#header_open|@cmpxchgWeak#}
|
||||
<pre><code class="zig">@cmpxchgWeak(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> ?T</code></pre>
|
||||
<p>
|
||||
This function performs a weak atomic compare exchange operation. It's the equivalent of this code,
|
||||
except atomic:
|
||||
</p>
|
||||
{#code_begin|syntax#}
|
||||
fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T {
|
||||
const old_value = *ptr;
|
||||
if (old_value == expected_value and usuallyTrueButSometimesFalse()) {
|
||||
*ptr = new_value;
|
||||
return null;
|
||||
} else {
|
||||
return old_value;
|
||||
}
|
||||
}
|
||||
{#code_end#}
|
||||
<p>
|
||||
If you are using cmpxchg in a loop, the sporadic failure will be no problem, and <code>cmpxchgWeak</code>
|
||||
is the better choice, because it can be implemented more efficiently in machine instructions.
|
||||
However if you need a stronger guarantee, use {#link|@cmpxchgStrong#}.
|
||||
</p>
|
||||
<p>
|
||||
<code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>.
|
||||
</p>
|
||||
<p><code>@typeOf(ptr).alignment</code> must be <code>>= @sizeOf(T).</code></p>
|
||||
{#see_also|Compile Variables|cmpxchgStrong#}
|
||||
{#header_close#}
|
||||
{#header_open|@compileError#}
|
||||
<pre><code class="zig">@compileError(comptime msg: []u8)</code></pre>
|
||||
@ -4313,6 +4412,10 @@ fn add(a: i32, b: i32) i32 { return a + b; }
|
||||
It does not include functions, variables, or constants.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@field#}
|
||||
<pre><code class="zig">@field(lhs: var, comptime field_name: []const u8) -> (field)</code></pre>
|
||||
<p>Preforms field access equivalent to <code>lhs.->field_name-<</code>.</p>
|
||||
{#header_close#}
|
||||
{#header_open|@memberType#}
|
||||
<pre><code class="zig">@memberType(comptime T: type, comptime index: usize) -> type</code></pre>
|
||||
<p>Returns the field type of a struct or union.</p>
|
||||
@ -4633,6 +4736,16 @@ pub const FloatMode = enum {
|
||||
The result is a target-specific compile time constant.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@sqrt#}
|
||||
<pre><code class="zig">@sqrt(comptime T: type, value: T) -> T</code></pre>
|
||||
<p>
|
||||
Performs the square root of a floating point number. Uses a dedicated hardware instruction
|
||||
when available. Currently only supports f32 and f64 at runtime. f128 at runtime is TODO.
|
||||
</p>
|
||||
<p>
|
||||
This is a low-level intrinsic. Most code can use <code>std.math.sqrt</code> instead.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@subWithOverflow#}
|
||||
<pre><code class="zig">@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool</code></pre>
|
||||
<p>
|
||||
@ -4696,6 +4809,182 @@ pub const TypeId = enum {
|
||||
BoundFn,
|
||||
ArgTuple,
|
||||
Opaque,
|
||||
};
|
||||
{#code_end#}
|
||||
{#header_close#}
|
||||
{#header_open|@typeInfo#}
|
||||
<pre><code class="zig">@typeInfo(comptime T: type) -> @import("builtin").TypeInfo</code></pre>
|
||||
<p>
|
||||
Returns information on the type. Returns a value of the following union:
|
||||
</p>
|
||||
{#code_begin|syntax#}
|
||||
pub const TypeInfo = union(TypeId) {
|
||||
Type: void,
|
||||
Void: void,
|
||||
Bool: void,
|
||||
NoReturn: void,
|
||||
Int: Int,
|
||||
Float: Float,
|
||||
Pointer: Pointer,
|
||||
Array: Array,
|
||||
Struct: Struct,
|
||||
FloatLiteral: void,
|
||||
IntLiteral: void,
|
||||
UndefinedLiteral: void,
|
||||
NullLiteral: void,
|
||||
Nullable: Nullable,
|
||||
ErrorUnion: ErrorUnion,
|
||||
ErrorSet: ErrorSet,
|
||||
Enum: Enum,
|
||||
Union: Union,
|
||||
Fn: Fn,
|
||||
Namespace: void,
|
||||
Block: void,
|
||||
BoundFn: Fn,
|
||||
ArgTuple: void,
|
||||
Opaque: void,
|
||||
Promise: Promise,
|
||||
|
||||
|
||||
pub const Int = struct {
|
||||
is_signed: bool,
|
||||
bits: u8,
|
||||
};
|
||||
|
||||
pub const Float = struct {
|
||||
bits: u8,
|
||||
};
|
||||
|
||||
pub const Pointer = struct {
|
||||
is_const: bool,
|
||||
is_volatile: bool,
|
||||
alignment: u32,
|
||||
child: type,
|
||||
};
|
||||
|
||||
pub const Array = struct {
|
||||
len: usize,
|
||||
child: type,
|
||||
};
|
||||
|
||||
pub const ContainerLayout = enum {
|
||||
Auto,
|
||||
Extern,
|
||||
Packed,
|
||||
};
|
||||
|
||||
pub const StructField = struct {
|
||||
name: []const u8,
|
||||
offset: ?usize,
|
||||
field_type: type,
|
||||
};
|
||||
|
||||
pub const Struct = struct {
|
||||
layout: ContainerLayout,
|
||||
fields: []StructField,
|
||||
defs: []Definition,
|
||||
};
|
||||
|
||||
pub const Nullable = struct {
|
||||
child: type,
|
||||
};
|
||||
|
||||
pub const ErrorUnion = struct {
|
||||
error_set: type,
|
||||
payload: type,
|
||||
};
|
||||
|
||||
pub const Error = struct {
|
||||
name: []const u8,
|
||||
value: usize,
|
||||
};
|
||||
|
||||
pub const ErrorSet = struct {
|
||||
errors: []Error,
|
||||
};
|
||||
|
||||
pub const EnumField = struct {
|
||||
name: []const u8,
|
||||
value: usize,
|
||||
};
|
||||
|
||||
pub const Enum = struct {
|
||||
layout: ContainerLayout,
|
||||
tag_type: type,
|
||||
fields: []EnumField,
|
||||
defs: []Definition,
|
||||
};
|
||||
|
||||
pub const UnionField = struct {
|
||||
name: []const u8,
|
||||
enum_field: ?EnumField,
|
||||
field_type: type,
|
||||
};
|
||||
|
||||
pub const Union = struct {
|
||||
layout: ContainerLayout,
|
||||
tag_type: type,
|
||||
fields: []UnionField,
|
||||
defs: []Definition,
|
||||
};
|
||||
|
||||
pub const CallingConvention = enum {
|
||||
Unspecified,
|
||||
C,
|
||||
Cold,
|
||||
Naked,
|
||||
Stdcall,
|
||||
Async,
|
||||
};
|
||||
|
||||
pub const FnArg = struct {
|
||||
is_generic: bool,
|
||||
is_noalias: bool,
|
||||
arg_type: type,
|
||||
};
|
||||
|
||||
pub const Fn = struct {
|
||||
calling_convention: CallingConvention,
|
||||
is_generic: bool,
|
||||
is_var_args: bool,
|
||||
return_type: type,
|
||||
async_allocator_type: type,
|
||||
args: []FnArg,
|
||||
};
|
||||
|
||||
pub const Promise = struct {
|
||||
child: type,
|
||||
};
|
||||
|
||||
pub const Definition = struct {
|
||||
name: []const u8,
|
||||
is_pub: bool,
|
||||
data: Data,
|
||||
|
||||
pub const Data = union(enum) {
|
||||
Type: type,
|
||||
Var: type,
|
||||
Fn: FnDef,
|
||||
|
||||
pub const FnDef = struct {
|
||||
fn_type: type,
|
||||
inline_type: Inline,
|
||||
calling_convention: CallingConvention,
|
||||
is_var_args: bool,
|
||||
is_extern: bool,
|
||||
is_export: bool,
|
||||
lib_name: ?[]const u8,
|
||||
return_type: type,
|
||||
arg_names: [][] const u8,
|
||||
|
||||
pub const Inline = enum {
|
||||
Auto,
|
||||
Always,
|
||||
Never,
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
{#code_end#}
|
||||
{#header_close#}
|
||||
@ -5113,7 +5402,6 @@ pub const Os = enum {
|
||||
rtems,
|
||||
nacl,
|
||||
cnk,
|
||||
bitrig,
|
||||
aix,
|
||||
cuda,
|
||||
nvcl,
|
||||
@ -5124,10 +5412,12 @@ pub const Os = enum {
|
||||
watchos,
|
||||
mesa3d,
|
||||
contiki,
|
||||
amdpal,
|
||||
zen,
|
||||
};
|
||||
|
||||
pub const Arch = enum {
|
||||
armv8_3a,
|
||||
armv8_2a,
|
||||
armv8_1a,
|
||||
armv8,
|
||||
@ -5147,9 +5437,29 @@ pub const Arch = enum {
|
||||
armv5,
|
||||
armv5te,
|
||||
armv4t,
|
||||
armeb,
|
||||
armebv8_3a,
|
||||
armebv8_2a,
|
||||
armebv8_1a,
|
||||
armebv8,
|
||||
armebv8r,
|
||||
armebv8m_baseline,
|
||||
armebv8m_mainline,
|
||||
armebv7,
|
||||
armebv7em,
|
||||
armebv7m,
|
||||
armebv7s,
|
||||
armebv7k,
|
||||
armebv7ve,
|
||||
armebv6,
|
||||
armebv6m,
|
||||
armebv6k,
|
||||
armebv6t2,
|
||||
armebv5,
|
||||
armebv5te,
|
||||
armebv4t,
|
||||
aarch64,
|
||||
aarch64_be,
|
||||
arc,
|
||||
avr,
|
||||
bpfel,
|
||||
bpfeb,
|
||||
@ -5202,6 +5512,7 @@ pub const Arch = enum {
|
||||
pub const Environ = enum {
|
||||
unknown,
|
||||
gnu,
|
||||
gnuabin32,
|
||||
gnuabi64,
|
||||
gnueabi,
|
||||
gnueabihf,
|
||||
@ -5219,6 +5530,7 @@ pub const Environ = enum {
|
||||
amdopencl,
|
||||
coreclr,
|
||||
opencl,
|
||||
simulator,
|
||||
};
|
||||
|
||||
pub const ObjectFormat = enum {
|
||||
@ -5245,10 +5557,23 @@ pub const AtomicOrder = enum {
|
||||
SeqCst,
|
||||
};
|
||||
|
||||
pub const AtomicRmwOp = enum {
|
||||
Xchg,
|
||||
Add,
|
||||
Sub,
|
||||
And,
|
||||
Nand,
|
||||
Or,
|
||||
Xor,
|
||||
Max,
|
||||
Min,
|
||||
};
|
||||
|
||||
pub const Mode = enum {
|
||||
Debug,
|
||||
ReleaseSafe,
|
||||
ReleaseFast,
|
||||
ReleaseSmall,
|
||||
};
|
||||
|
||||
pub const TypeId = enum {
|
||||
@ -5267,7 +5592,7 @@ pub const TypeId = enum {
|
||||
NullLiteral,
|
||||
Nullable,
|
||||
ErrorUnion,
|
||||
Error,
|
||||
ErrorSet,
|
||||
Enum,
|
||||
Union,
|
||||
Fn,
|
||||
@ -5276,6 +5601,176 @@ pub const TypeId = enum {
|
||||
BoundFn,
|
||||
ArgTuple,
|
||||
Opaque,
|
||||
Promise,
|
||||
};
|
||||
|
||||
pub const TypeInfo = union(TypeId) {
|
||||
Type: void,
|
||||
Void: void,
|
||||
Bool: void,
|
||||
NoReturn: void,
|
||||
Int: Int,
|
||||
Float: Float,
|
||||
Pointer: Pointer,
|
||||
Array: Array,
|
||||
Struct: Struct,
|
||||
FloatLiteral: void,
|
||||
IntLiteral: void,
|
||||
UndefinedLiteral: void,
|
||||
NullLiteral: void,
|
||||
Nullable: Nullable,
|
||||
ErrorUnion: ErrorUnion,
|
||||
ErrorSet: ErrorSet,
|
||||
Enum: Enum,
|
||||
Union: Union,
|
||||
Fn: Fn,
|
||||
Namespace: void,
|
||||
Block: void,
|
||||
BoundFn: Fn,
|
||||
ArgTuple: void,
|
||||
Opaque: void,
|
||||
Promise: Promise,
|
||||
|
||||
|
||||
pub const Int = struct {
|
||||
is_signed: bool,
|
||||
bits: u8,
|
||||
};
|
||||
|
||||
pub const Float = struct {
|
||||
bits: u8,
|
||||
};
|
||||
|
||||
pub const Pointer = struct {
|
||||
is_const: bool,
|
||||
is_volatile: bool,
|
||||
alignment: u32,
|
||||
child: type,
|
||||
};
|
||||
|
||||
pub const Array = struct {
|
||||
len: usize,
|
||||
child: type,
|
||||
};
|
||||
|
||||
pub const ContainerLayout = enum {
|
||||
Auto,
|
||||
Extern,
|
||||
Packed,
|
||||
};
|
||||
|
||||
pub const StructField = struct {
|
||||
name: []const u8,
|
||||
offset: ?usize,
|
||||
field_type: type,
|
||||
};
|
||||
|
||||
pub const Struct = struct {
|
||||
layout: ContainerLayout,
|
||||
fields: []StructField,
|
||||
defs: []Definition,
|
||||
};
|
||||
|
||||
pub const Nullable = struct {
|
||||
child: type,
|
||||
};
|
||||
|
||||
pub const ErrorUnion = struct {
|
||||
error_set: type,
|
||||
payload: type,
|
||||
};
|
||||
|
||||
pub const Error = struct {
|
||||
name: []const u8,
|
||||
value: usize,
|
||||
};
|
||||
|
||||
pub const ErrorSet = struct {
|
||||
errors: []Error,
|
||||
};
|
||||
|
||||
pub const EnumField = struct {
|
||||
name: []const u8,
|
||||
value: usize,
|
||||
};
|
||||
|
||||
pub const Enum = struct {
|
||||
layout: ContainerLayout,
|
||||
tag_type: type,
|
||||
fields: []EnumField,
|
||||
defs: []Definition,
|
||||
};
|
||||
|
||||
pub const UnionField = struct {
|
||||
name: []const u8,
|
||||
enum_field: ?EnumField,
|
||||
field_type: type,
|
||||
};
|
||||
|
||||
pub const Union = struct {
|
||||
layout: ContainerLayout,
|
||||
tag_type: type,
|
||||
fields: []UnionField,
|
||||
defs: []Definition,
|
||||
};
|
||||
|
||||
pub const CallingConvention = enum {
|
||||
Unspecified,
|
||||
C,
|
||||
Cold,
|
||||
Naked,
|
||||
Stdcall,
|
||||
Async,
|
||||
};
|
||||
|
||||
pub const FnArg = struct {
|
||||
is_generic: bool,
|
||||
is_noalias: bool,
|
||||
arg_type: type,
|
||||
};
|
||||
|
||||
pub const Fn = struct {
|
||||
calling_convention: CallingConvention,
|
||||
is_generic: bool,
|
||||
is_var_args: bool,
|
||||
return_type: type,
|
||||
async_allocator_type: type,
|
||||
args: []FnArg,
|
||||
};
|
||||
|
||||
pub const Promise = struct {
|
||||
child: type,
|
||||
};
|
||||
|
||||
pub const Definition = struct {
|
||||
name: []const u8,
|
||||
is_pub: bool,
|
||||
data: Data,
|
||||
|
||||
pub const Data = union(enum) {
|
||||
Type: type,
|
||||
Var: type,
|
||||
Fn: FnDef,
|
||||
|
||||
pub const FnDef = struct {
|
||||
fn_type: type,
|
||||
inline_type: Inline,
|
||||
calling_convention: CallingConvention,
|
||||
is_var_args: bool,
|
||||
is_extern: bool,
|
||||
is_export: bool,
|
||||
lib_name: ?[]const u8,
|
||||
return_type: type,
|
||||
arg_names: [][] const u8,
|
||||
|
||||
pub const Inline = enum {
|
||||
Auto,
|
||||
Always,
|
||||
Never,
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pub const FloatMode = enum {
|
||||
@ -5289,7 +5784,7 @@ pub const Endian = enum {
|
||||
};
|
||||
|
||||
pub const endian = Endian.Little;
|
||||
pub const is_test = false;
|
||||
pub const is_test = true;
|
||||
pub const os = Os.linux;
|
||||
pub const arch = Arch.x86_64;
|
||||
pub const environ = Environ.gnu;
|
||||
@ -5297,6 +5792,7 @@ pub const object_format = ObjectFormat.elf;
|
||||
pub const mode = Mode.Debug;
|
||||
pub const link_libc = false;
|
||||
pub const have_error_return_tracing = true;
|
||||
pub const __zig_test_fn_slice = {}; // overwritten later
|
||||
{#code_end#}
|
||||
{#see_also|Build Mode#}
|
||||
{#header_close#}
|
||||
@ -5809,7 +6305,7 @@ Defer(body) = ("defer" | "deferror") body
|
||||
|
||||
IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
|
||||
|
||||
SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
|
||||
SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))
|
||||
|
||||
IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
|
||||
|
||||
@ -5955,7 +6451,7 @@ hljs.registerLanguage("zig", function(t) {
|
||||
a = t.IR + "\\s*\\(",
|
||||
c = {
|
||||
keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",
|
||||
built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw",
|
||||
built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo",
|
||||
literal: "true false null undefined"
|
||||
},
|
||||
n = [e, t.CLCM, t.CBCM, s, r];
|
||||
|
||||
284
src-self-hosted/arg.zig
Normal file
284
src-self-hosted/arg.zig
Normal file
@ -0,0 +1,284 @@
|
||||
const std = @import("std");
|
||||
const debug = std.debug;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const ArrayList = std.ArrayList;
|
||||
const HashMap = std.HashMap;
|
||||
|
||||
fn trimStart(slice: []const u8, ch: u8) []const u8 {
|
||||
var i: usize = 0;
|
||||
for (slice) |b| {
|
||||
if (b != '-') break;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return slice[i..];
|
||||
}
|
||||
|
||||
fn argInAllowedSet(maybe_set: ?[]const []const u8, arg: []const u8) bool {
|
||||
if (maybe_set) |set| {
|
||||
for (set) |possible| {
|
||||
if (mem.eql(u8, arg, possible)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Modifies the current argument index during iteration
|
||||
fn readFlagArguments(allocator: &Allocator, args: []const []const u8, required: usize,
|
||||
allowed_set: ?[]const []const u8, index: &usize) !FlagArg {
|
||||
|
||||
switch (required) {
|
||||
0 => return FlagArg { .None = undefined }, // TODO: Required to force non-tag but value?
|
||||
1 => {
|
||||
if (*index + 1 >= args.len) {
|
||||
return error.MissingFlagArguments;
|
||||
}
|
||||
|
||||
*index += 1;
|
||||
const arg = args[*index];
|
||||
|
||||
if (!argInAllowedSet(allowed_set, arg)) {
|
||||
return error.ArgumentNotInAllowedSet;
|
||||
}
|
||||
|
||||
return FlagArg { .Single = arg };
|
||||
},
|
||||
else => |needed| {
|
||||
var extra = ArrayList([]const u8).init(allocator);
|
||||
errdefer extra.deinit();
|
||||
|
||||
var j: usize = 0;
|
||||
while (j < needed) : (j += 1) {
|
||||
if (*index + 1 >= args.len) {
|
||||
return error.MissingFlagArguments;
|
||||
}
|
||||
|
||||
*index += 1;
|
||||
const arg = args[*index];
|
||||
|
||||
if (!argInAllowedSet(allowed_set, arg)) {
|
||||
return error.ArgumentNotInAllowedSet;
|
||||
}
|
||||
|
||||
try extra.append(arg);
|
||||
}
|
||||
|
||||
return FlagArg { .Many = extra };
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const HashMapFlags = HashMap([]const u8, FlagArg, std.hash.Fnv1a_32.hash, mem.eql_slice_u8);
|
||||
|
||||
// A store for querying found flags and positional arguments.
|
||||
pub const Args = struct {
|
||||
flags: HashMapFlags,
|
||||
positionals: ArrayList([]const u8),
|
||||
|
||||
pub fn parse(allocator: &Allocator, comptime spec: []const Flag, args: []const []const u8) !Args {
|
||||
var parsed = Args {
|
||||
.flags = HashMapFlags.init(allocator),
|
||||
.positionals = ArrayList([]const u8).init(allocator),
|
||||
};
|
||||
|
||||
var i: usize = 0;
|
||||
next: while (i < args.len) : (i += 1) {
|
||||
const arg = args[i];
|
||||
|
||||
if (arg.len != 0 and arg[0] == '-') {
|
||||
// TODO: hashmap, although the linear scan is okay for small argument sets as is
|
||||
for (spec) |flag| {
|
||||
if (mem.eql(u8, arg, flag.name)) {
|
||||
const flag_name_trimmed = trimStart(flag.name, '-');
|
||||
const flag_args = readFlagArguments(allocator, args, flag.required, flag.allowed_set, &i) catch |err| {
|
||||
switch (err) {
|
||||
error.ArgumentNotInAllowedSet => {
|
||||
std.debug.warn("argument '{}' is invalid for flag '{}'\n", args[i], arg);
|
||||
std.debug.warn("allowed options are ");
|
||||
for (??flag.allowed_set) |possible| {
|
||||
std.debug.warn("'{}' ", possible);
|
||||
}
|
||||
std.debug.warn("\n");
|
||||
},
|
||||
error.MissingFlagArguments => {
|
||||
std.debug.warn("missing argument for flag: {}\n", arg);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
return err;
|
||||
};
|
||||
|
||||
if (flag.mergable) {
|
||||
var prev =
|
||||
if (parsed.flags.get(flag_name_trimmed)) |entry|
|
||||
entry.value.Many
|
||||
else
|
||||
ArrayList([]const u8).init(allocator);
|
||||
|
||||
// MergeN creation disallows 0 length flag entry (doesn't make sense)
|
||||
switch (flag_args) {
|
||||
FlagArg.None => unreachable,
|
||||
FlagArg.Single => |inner| try prev.append(inner),
|
||||
FlagArg.Many => |inner| try prev.appendSlice(inner.toSliceConst()),
|
||||
}
|
||||
|
||||
_ = try parsed.flags.put(flag_name_trimmed, FlagArg { .Many = prev });
|
||||
} else {
|
||||
_ = try parsed.flags.put(flag_name_trimmed, flag_args);
|
||||
}
|
||||
|
||||
continue :next;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Better errors with context, global error state and return is sufficient.
|
||||
std.debug.warn("could not match flag: {}\n", arg);
|
||||
return error.UnknownFlag;
|
||||
} else {
|
||||
try parsed.positionals.append(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
pub fn deinit(self: &Args) void {
|
||||
self.flags.deinit();
|
||||
self.positionals.deinit();
|
||||
}
|
||||
|
||||
// e.g. --help
|
||||
pub fn present(self: &Args, name: []const u8) bool {
|
||||
return self.flags.contains(name);
|
||||
}
|
||||
|
||||
// e.g. --name value
|
||||
pub fn single(self: &Args, name: []const u8) ?[]const u8 {
|
||||
if (self.flags.get(name)) |entry| {
|
||||
switch (entry.value) {
|
||||
FlagArg.Single => |inner| { return inner; },
|
||||
else => @panic("attempted to retrieve flag with wrong type"),
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// e.g. --names value1 value2 value3
|
||||
pub fn many(self: &Args, name: []const u8) ?[]const []const u8 {
|
||||
if (self.flags.get(name)) |entry| {
|
||||
switch (entry.value) {
|
||||
FlagArg.Many => |inner| { return inner.toSliceConst(); },
|
||||
else => @panic("attempted to retrieve flag with wrong type"),
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Arguments for a flag. e.g. arg1, arg2 in `--command arg1 arg2`.
|
||||
const FlagArg = union(enum) {
|
||||
None,
|
||||
Single: []const u8,
|
||||
Many: ArrayList([]const u8),
|
||||
};
|
||||
|
||||
// Specification for how a flag should be parsed.
|
||||
pub const Flag = struct {
|
||||
name: []const u8,
|
||||
required: usize,
|
||||
mergable: bool,
|
||||
allowed_set: ?[]const []const u8,
|
||||
|
||||
pub fn Bool(comptime name: []const u8) Flag {
|
||||
return ArgN(name, 0);
|
||||
}
|
||||
|
||||
pub fn Arg1(comptime name: []const u8) Flag {
|
||||
return ArgN(name, 1);
|
||||
}
|
||||
|
||||
pub fn ArgN(comptime name: []const u8, comptime n: usize) Flag {
|
||||
return Flag {
|
||||
.name = name,
|
||||
.required = n,
|
||||
.mergable = false,
|
||||
.allowed_set = null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn ArgMergeN(comptime name: []const u8, comptime n: usize) Flag {
|
||||
if (n == 0) {
|
||||
@compileError("n must be greater than 0");
|
||||
}
|
||||
|
||||
return Flag {
|
||||
.name = name,
|
||||
.required = n,
|
||||
.mergable = true,
|
||||
.allowed_set = null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn Option(comptime name: []const u8, comptime set: []const []const u8) Flag {
|
||||
return Flag {
|
||||
.name = name,
|
||||
.required = 1,
|
||||
.mergable = false,
|
||||
.allowed_set = set,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
test "parse arguments" {
|
||||
const spec1 = comptime []const Flag {
|
||||
Flag.Bool("--help"),
|
||||
Flag.Bool("--init"),
|
||||
Flag.Arg1("--build-file"),
|
||||
Flag.Option("--color", []const []const u8 { "on", "off", "auto" }),
|
||||
Flag.ArgN("--pkg-begin", 2),
|
||||
Flag.ArgMergeN("--object", 1),
|
||||
Flag.ArgN("--library", 1),
|
||||
};
|
||||
|
||||
const cliargs = []const []const u8 {
|
||||
"build",
|
||||
"--help",
|
||||
"pos1",
|
||||
"--build-file", "build.zig",
|
||||
"--object", "obj1",
|
||||
"--object", "obj2",
|
||||
"--library", "lib1",
|
||||
"--library", "lib2",
|
||||
"--color", "on",
|
||||
"pos2",
|
||||
};
|
||||
|
||||
var args = try Args.parse(std.debug.global_allocator, spec1, cliargs);
|
||||
|
||||
debug.assert(args.present("help"));
|
||||
debug.assert(!args.present("help2"));
|
||||
debug.assert(!args.present("init"));
|
||||
|
||||
debug.assert(mem.eql(u8, ??args.single("build-file"), "build.zig"));
|
||||
debug.assert(mem.eql(u8, ??args.single("color"), "on"));
|
||||
|
||||
const objects = ??args.many("object");
|
||||
debug.assert(mem.eql(u8, objects[0], "obj1"));
|
||||
debug.assert(mem.eql(u8, objects[1], "obj2"));
|
||||
|
||||
debug.assert(mem.eql(u8, ??args.single("library"), "lib2"));
|
||||
|
||||
const pos = args.positionals.toSliceConst();
|
||||
debug.assert(mem.eql(u8, pos[0], "build"));
|
||||
debug.assert(mem.eql(u8, pos[1], "pos1"));
|
||||
debug.assert(mem.eql(u8, pos[2], "pos2"));
|
||||
}
|
||||
57
src-self-hosted/introspect.zig
Normal file
57
src-self-hosted/introspect.zig
Normal file
@ -0,0 +1,57 @@
|
||||
// Introspection and determination of system libraries needed by zig.
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const os = std.os;
|
||||
|
||||
const warn = std.debug.warn;
|
||||
|
||||
/// Caller must free result
|
||||
pub fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {
|
||||
const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
|
||||
errdefer allocator.free(test_zig_dir);
|
||||
|
||||
const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
|
||||
defer allocator.free(test_index_file);
|
||||
|
||||
var file = try os.File.openRead(allocator, test_index_file);
|
||||
file.close();
|
||||
|
||||
return test_zig_dir;
|
||||
}
|
||||
|
||||
/// Caller must free result
|
||||
pub fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
|
||||
const self_exe_path = try os.selfExeDirPath(allocator);
|
||||
defer allocator.free(self_exe_path);
|
||||
|
||||
var cur_path: []const u8 = self_exe_path;
|
||||
while (true) {
|
||||
const test_dir = os.path.dirname(cur_path);
|
||||
|
||||
if (mem.eql(u8, test_dir, cur_path)) {
|
||||
break;
|
||||
}
|
||||
|
||||
return testZigInstallPrefix(allocator, test_dir) catch |err| {
|
||||
cur_path = test_dir;
|
||||
continue;
|
||||
};
|
||||
}
|
||||
|
||||
return error.FileNotFound;
|
||||
}
|
||||
|
||||
pub fn resolveZigLibDir(allocator: &mem.Allocator) ![]u8 {
|
||||
return findZigLibDir(allocator) catch |err| {
|
||||
warn(
|
||||
\\Unable to find zig lib directory: {}.
|
||||
\\Reinstall Zig or use --zig-install-prefix.
|
||||
\\
|
||||
,
|
||||
@errorName(err)
|
||||
);
|
||||
|
||||
return error.ZigLibDirNotFound;
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -8,9 +8,7 @@ const c = @import("c.zig");
|
||||
const builtin = @import("builtin");
|
||||
const Target = @import("target.zig").Target;
|
||||
const warn = std.debug.warn;
|
||||
const Tokenizer = std.zig.Tokenizer;
|
||||
const Token = std.zig.Token;
|
||||
const Parser = std.zig.Parser;
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
pub const Module = struct {
|
||||
@ -109,6 +107,29 @@ pub const Module = struct {
|
||||
LlvmIr,
|
||||
};
|
||||
|
||||
pub const CliPkg = struct {
|
||||
name: []const u8,
|
||||
path: []const u8,
|
||||
children: ArrayList(&CliPkg),
|
||||
parent: ?&CliPkg,
|
||||
|
||||
pub fn init(allocator: &mem.Allocator, name: []const u8, path: []const u8, parent: ?&CliPkg) !&CliPkg {
|
||||
var pkg = try allocator.create(CliPkg);
|
||||
pkg.name = name;
|
||||
pkg.path = path;
|
||||
pkg.children = ArrayList(&CliPkg).init(allocator);
|
||||
pkg.parent = parent;
|
||||
return pkg;
|
||||
}
|
||||
|
||||
pub fn deinit(self: &CliPkg) void {
|
||||
for (self.children.toSliceConst()) |child| {
|
||||
child.deinit();
|
||||
}
|
||||
self.children.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,
|
||||
kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module
|
||||
{
|
||||
@ -223,34 +244,17 @@ pub const Module = struct {
|
||||
|
||||
warn("{}", source_code);
|
||||
|
||||
warn("====tokenization:====\n");
|
||||
{
|
||||
var tokenizer = Tokenizer.init(source_code);
|
||||
while (true) {
|
||||
const token = tokenizer.next();
|
||||
tokenizer.dump(token);
|
||||
if (token.id == Token.Id.Eof) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
warn("====parse:====\n");
|
||||
|
||||
var tokenizer = Tokenizer.init(source_code);
|
||||
var parser = Parser.init(&tokenizer, self.allocator, root_src_real_path);
|
||||
defer parser.deinit();
|
||||
|
||||
var tree = try parser.parse();
|
||||
var tree = try std.zig.parse(self.allocator, source_code);
|
||||
defer tree.deinit();
|
||||
|
||||
var stderr_file = try std.io.getStdErr();
|
||||
var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
|
||||
const out_stream = &stderr_file_out_stream.stream;
|
||||
try parser.renderAst(out_stream, tree.root_node);
|
||||
|
||||
warn("====fmt:====\n");
|
||||
try parser.renderSource(out_stream, tree.root_node);
|
||||
try std.zig.render(self.allocator, out_stream, &tree);
|
||||
|
||||
warn("====ir:====\n");
|
||||
warn("TODO\n\n");
|
||||
|
||||
@ -359,7 +359,6 @@ enum NodeType {
|
||||
NodeTypeRoot,
|
||||
NodeTypeFnProto,
|
||||
NodeTypeFnDef,
|
||||
NodeTypeFnDecl,
|
||||
NodeTypeParamDecl,
|
||||
NodeTypeBlock,
|
||||
NodeTypeGroupedExpr,
|
||||
@ -453,10 +452,6 @@ struct AstNodeFnDef {
|
||||
AstNode *body;
|
||||
};
|
||||
|
||||
struct AstNodeFnDecl {
|
||||
AstNode *fn_proto;
|
||||
};
|
||||
|
||||
struct AstNodeParamDecl {
|
||||
Buf *name;
|
||||
AstNode *type;
|
||||
@ -713,10 +708,6 @@ struct AstNodeSwitchRange {
|
||||
AstNode *end;
|
||||
};
|
||||
|
||||
struct AstNodeLabel {
|
||||
Buf *name;
|
||||
};
|
||||
|
||||
struct AstNodeCompTime {
|
||||
AstNode *expr;
|
||||
};
|
||||
@ -876,6 +867,7 @@ struct AstNodeAwaitExpr {
|
||||
};
|
||||
|
||||
struct AstNodeSuspend {
|
||||
Buf *name;
|
||||
AstNode *block;
|
||||
AstNode *promise_symbol;
|
||||
};
|
||||
@ -892,7 +884,6 @@ struct AstNode {
|
||||
union {
|
||||
AstNodeRoot root;
|
||||
AstNodeFnDef fn_def;
|
||||
AstNodeFnDecl fn_decl;
|
||||
AstNodeFnProto fn_proto;
|
||||
AstNodeParamDecl param_decl;
|
||||
AstNodeBlock block;
|
||||
@ -917,7 +908,6 @@ struct AstNode {
|
||||
AstNodeSwitchExpr switch_expr;
|
||||
AstNodeSwitchProng switch_prong;
|
||||
AstNodeSwitchRange switch_range;
|
||||
AstNodeLabel label;
|
||||
AstNodeCompTime comptime_expr;
|
||||
AstNodeAsmExpr asm_expr;
|
||||
AstNodeFieldAccessExpr field_access_expr;
|
||||
@ -1302,6 +1292,8 @@ enum BuiltinFnId {
|
||||
BuiltinFnIdMemberCount,
|
||||
BuiltinFnIdMemberType,
|
||||
BuiltinFnIdMemberName,
|
||||
BuiltinFnIdField,
|
||||
BuiltinFnIdTypeInfo,
|
||||
BuiltinFnIdTypeof,
|
||||
BuiltinFnIdAddWithOverflow,
|
||||
BuiltinFnIdSubWithOverflow,
|
||||
@ -1321,13 +1313,15 @@ enum BuiltinFnId {
|
||||
BuiltinFnIdReturnAddress,
|
||||
BuiltinFnIdFrameAddress,
|
||||
BuiltinFnIdEmbedFile,
|
||||
BuiltinFnIdCmpExchange,
|
||||
BuiltinFnIdCmpxchgWeak,
|
||||
BuiltinFnIdCmpxchgStrong,
|
||||
BuiltinFnIdFence,
|
||||
BuiltinFnIdDivExact,
|
||||
BuiltinFnIdDivTrunc,
|
||||
BuiltinFnIdDivFloor,
|
||||
BuiltinFnIdRem,
|
||||
BuiltinFnIdMod,
|
||||
BuiltinFnIdSqrt,
|
||||
BuiltinFnIdTruncate,
|
||||
BuiltinFnIdIntType,
|
||||
BuiltinFnIdSetCold,
|
||||
@ -1357,6 +1351,7 @@ enum BuiltinFnId {
|
||||
BuiltinFnIdExport,
|
||||
BuiltinFnIdErrorReturnTrace,
|
||||
BuiltinFnIdAtomicRmw,
|
||||
BuiltinFnIdAtomicLoad,
|
||||
};
|
||||
|
||||
struct BuiltinFnEntry {
|
||||
@ -1424,6 +1419,7 @@ enum ZigLLVMFnId {
|
||||
ZigLLVMFnIdOverflowArithmetic,
|
||||
ZigLLVMFnIdFloor,
|
||||
ZigLLVMFnIdCeil,
|
||||
ZigLLVMFnIdSqrt,
|
||||
};
|
||||
|
||||
enum AddSubMul {
|
||||
@ -1444,7 +1440,7 @@ struct ZigLLVMFnKey {
|
||||
} clz;
|
||||
struct {
|
||||
uint32_t bit_count;
|
||||
} floor_ceil;
|
||||
} floating;
|
||||
struct {
|
||||
AddSubMul add_sub_mul;
|
||||
uint32_t bit_count;
|
||||
@ -1465,6 +1461,7 @@ enum BuildMode {
|
||||
BuildModeDebug,
|
||||
BuildModeFastRelease,
|
||||
BuildModeSafeRelease,
|
||||
BuildModeSmallRelease,
|
||||
};
|
||||
|
||||
enum EmitFileType {
|
||||
@ -1510,6 +1507,7 @@ struct CodeGen {
|
||||
HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> exported_symbol_names;
|
||||
HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> external_prototypes;
|
||||
HashMap<Buf *, ConstExprValue *, buf_hash, buf_eql_buf> string_literals_table;
|
||||
HashMap<const TypeTableEntry *, ConstExprValue *, type_ptr_hash, type_ptr_eql> type_info_cache;
|
||||
|
||||
|
||||
ZigList<ImportTableEntry *> import_queue;
|
||||
@ -1656,6 +1654,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;
|
||||
@ -1709,6 +1709,8 @@ struct CodeGen {
|
||||
ZigList<ZigLLVMDIType **> error_di_types;
|
||||
|
||||
ZigList<Buf *> forbidden_libs;
|
||||
|
||||
bool no_rosegment_workaround;
|
||||
};
|
||||
|
||||
enum VarLinkage {
|
||||
@ -1759,6 +1761,7 @@ enum ScopeId {
|
||||
ScopeIdVarDecl,
|
||||
ScopeIdCImport,
|
||||
ScopeIdLoop,
|
||||
ScopeIdSuspend,
|
||||
ScopeIdFnDef,
|
||||
ScopeIdCompTime,
|
||||
ScopeIdCoroPrelude,
|
||||
@ -1854,6 +1857,17 @@ struct ScopeLoop {
|
||||
ZigList<IrBasicBlock *> *incoming_blocks;
|
||||
};
|
||||
|
||||
// This scope is created for a suspend block in order to have labeled
|
||||
// suspend for breaking out of a suspend and for detecting if a suspend
|
||||
// block is inside a suspend block.
|
||||
struct ScopeSuspend {
|
||||
Scope base;
|
||||
|
||||
Buf *name;
|
||||
IrBasicBlock *resume_block;
|
||||
bool reported_err;
|
||||
};
|
||||
|
||||
// This scope is created for a comptime expression.
|
||||
// NodeTypeCompTime, NodeTypeSwitchExpr
|
||||
struct ScopeCompTime {
|
||||
@ -2025,6 +2039,7 @@ enum IrInstructionId {
|
||||
IrInstructionIdTagType,
|
||||
IrInstructionIdFieldParentPtr,
|
||||
IrInstructionIdOffsetOf,
|
||||
IrInstructionIdTypeInfo,
|
||||
IrInstructionIdTypeId,
|
||||
IrInstructionIdSetEvalBranchQuota,
|
||||
IrInstructionIdPtrTypeOf,
|
||||
@ -2050,10 +2065,14 @@ enum IrInstructionId {
|
||||
IrInstructionIdCoroPromise,
|
||||
IrInstructionIdCoroAllocHelper,
|
||||
IrInstructionIdAtomicRmw,
|
||||
IrInstructionIdAtomicLoad,
|
||||
IrInstructionIdPromiseResultType,
|
||||
IrInstructionIdAwaitBookkeeping,
|
||||
IrInstructionIdSaveErrRetAddr,
|
||||
IrInstructionIdAddImplicitReturnType,
|
||||
IrInstructionIdMergeErrRetTraces,
|
||||
IrInstructionIdMarkErrRetTracePtr,
|
||||
IrInstructionIdSqrt,
|
||||
};
|
||||
|
||||
struct IrInstruction {
|
||||
@ -2210,7 +2229,8 @@ struct IrInstructionFieldPtr {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *container_ptr;
|
||||
Buf *field_name;
|
||||
Buf *field_name_buffer;
|
||||
IrInstruction *field_name_expr;
|
||||
bool is_const;
|
||||
};
|
||||
|
||||
@ -2529,6 +2549,7 @@ struct IrInstructionEmbedFile {
|
||||
struct IrInstructionCmpxchg {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *type_value;
|
||||
IrInstruction *ptr;
|
||||
IrInstruction *cmp_value;
|
||||
IrInstruction *new_value;
|
||||
@ -2536,8 +2557,13 @@ struct IrInstructionCmpxchg {
|
||||
IrInstruction *failure_order_value;
|
||||
|
||||
// if this instruction gets to runtime then we know these values:
|
||||
TypeTableEntry *type;
|
||||
AtomicOrder success_order;
|
||||
AtomicOrder failure_order;
|
||||
|
||||
bool is_weak;
|
||||
|
||||
LLVMValueRef tmp_ptr;
|
||||
};
|
||||
|
||||
struct IrInstructionFence {
|
||||
@ -2835,6 +2861,12 @@ struct IrInstructionOffsetOf {
|
||||
IrInstruction *field_name;
|
||||
};
|
||||
|
||||
struct IrInstructionTypeInfo {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *type_value;
|
||||
};
|
||||
|
||||
struct IrInstructionTypeId {
|
||||
IrInstruction base;
|
||||
|
||||
@ -2892,6 +2924,11 @@ struct IrInstructionExport {
|
||||
|
||||
struct IrInstructionErrorReturnTrace {
|
||||
IrInstruction base;
|
||||
|
||||
enum Nullable {
|
||||
Null,
|
||||
NonNull,
|
||||
} nullable;
|
||||
};
|
||||
|
||||
struct IrInstructionErrorUnion {
|
||||
@ -3002,6 +3039,15 @@ struct IrInstructionAtomicRmw {
|
||||
AtomicOrder resolved_ordering;
|
||||
};
|
||||
|
||||
struct IrInstructionAtomicLoad {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *operand_type;
|
||||
IrInstruction *ptr;
|
||||
IrInstruction *ordering;
|
||||
AtomicOrder resolved_ordering;
|
||||
};
|
||||
|
||||
struct IrInstructionPromiseResultType {
|
||||
IrInstruction base;
|
||||
|
||||
@ -3024,6 +3070,27 @@ struct IrInstructionAddImplicitReturnType {
|
||||
IrInstruction *value;
|
||||
};
|
||||
|
||||
struct IrInstructionMergeErrRetTraces {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *coro_promise_ptr;
|
||||
IrInstruction *src_err_ret_trace_ptr;
|
||||
IrInstruction *dest_err_ret_trace_ptr;
|
||||
};
|
||||
|
||||
struct IrInstructionMarkErrRetTracePtr {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *err_ret_trace_ptr;
|
||||
};
|
||||
|
||||
struct IrInstructionSqrt {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *type;
|
||||
IrInstruction *op;
|
||||
};
|
||||
|
||||
static const size_t slice_ptr_index = 0;
|
||||
static const size_t slice_len_index = 1;
|
||||
|
||||
@ -3033,10 +3100,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"
|
||||
|
||||
|
||||
|
||||
101
src/analyze.cpp
101
src/analyze.cpp
@ -156,6 +156,14 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {
|
||||
return scope;
|
||||
}
|
||||
|
||||
ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) {
|
||||
assert(node->type == NodeTypeSuspend);
|
||||
ScopeSuspend *scope = allocate<ScopeSuspend>(1);
|
||||
init_scope(&scope->base, ScopeIdSuspend, node, parent);
|
||||
scope->name = node->data.suspend.name;
|
||||
return scope;
|
||||
}
|
||||
|
||||
ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) {
|
||||
ScopeFnDef *scope = allocate<ScopeFnDef>(1);
|
||||
init_scope(&scope->base, ScopeIdFnDef, node, parent);
|
||||
@ -468,10 +476,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;
|
||||
@ -1230,7 +1258,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou
|
||||
}
|
||||
|
||||
fn_type_id->param_count = fn_proto->params.length;
|
||||
fn_type_id->param_info = allocate_nonzero<FnTypeParamInfo>(param_count_alloc);
|
||||
fn_type_id->param_info = allocate<FnTypeParamInfo>(param_count_alloc);
|
||||
fn_type_id->next_param_index = 0;
|
||||
fn_type_id->is_var_args = fn_proto->is_var_args;
|
||||
}
|
||||
@ -2297,8 +2325,14 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
|
||||
HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
|
||||
occupied_tag_values.init(field_count);
|
||||
|
||||
TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
|
||||
TypeTableEntry *tag_int_type;
|
||||
if (enum_type->data.enumeration.layout == ContainerLayoutExtern) {
|
||||
tag_int_type = get_c_int_type(g, CIntTypeInt);
|
||||
} else {
|
||||
tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
|
||||
}
|
||||
|
||||
// TODO: Are extern enums allowed to have an init_arg_expr?
|
||||
if (decl_node->data.container_decl.init_arg_expr != nullptr) {
|
||||
TypeTableEntry *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);
|
||||
if (type_is_invalid(wanted_tag_int_type)) {
|
||||
@ -3216,7 +3250,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
|
||||
break;
|
||||
case NodeTypeContainerDecl:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeDefer:
|
||||
case NodeTypeBlock:
|
||||
@ -3597,6 +3630,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) {
|
||||
case ScopeIdVarDecl:
|
||||
case ScopeIdCImport:
|
||||
case ScopeIdLoop:
|
||||
case ScopeIdSuspend:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdCoroPrelude:
|
||||
scope = scope->parent;
|
||||
@ -4278,7 +4312,8 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
|
||||
static ZigWindowsSDK *get_windows_sdk(CodeGen *g) {
|
||||
if (g->win_sdk == nullptr) {
|
||||
if (os_find_windows_sdk(&g->win_sdk)) {
|
||||
zig_panic("Unable to determine Windows SDK path.");
|
||||
fprintf(stderr, "unable to determine windows sdk path\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
assert(g->win_sdk != nullptr);
|
||||
@ -4380,7 +4415,8 @@ void find_libc_include_path(CodeGen *g) {
|
||||
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.");
|
||||
fprintf(stderr, "Unable to determine libc include path. --libc-include-dir");
|
||||
exit(1);
|
||||
}
|
||||
} else if (g->zig_target.os == OsLinux) {
|
||||
g->libc_include_dir = get_linux_libc_include_path();
|
||||
@ -4402,24 +4438,33 @@ void find_libc_lib_path(CodeGen *g) {
|
||||
if (g->zig_target.os == OsWindows) {
|
||||
ZigWindowsSDK *sdk = get_windows_sdk(g);
|
||||
|
||||
Buf* vc_lib_dir = buf_alloc();
|
||||
if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {
|
||||
zig_panic("Unable to determine vcruntime path.");
|
||||
if (g->msvc_lib_dir == nullptr) {
|
||||
Buf* vc_lib_dir = buf_alloc();
|
||||
if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {
|
||||
fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir");
|
||||
exit(1);
|
||||
}
|
||||
g->msvc_lib_dir = vc_lib_dir;
|
||||
}
|
||||
|
||||
Buf* ucrt_lib_path = buf_alloc();
|
||||
if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) {
|
||||
zig_panic("Unable to determine ucrt path.");
|
||||
if (g->libc_lib_dir == nullptr) {
|
||||
Buf* ucrt_lib_path = buf_alloc();
|
||||
if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) {
|
||||
fprintf(stderr, "Unable to determine ucrt path. --libc-lib-dir");
|
||||
exit(1);
|
||||
}
|
||||
g->libc_lib_dir = ucrt_lib_path;
|
||||
}
|
||||
|
||||
Buf* kern_lib_path = buf_alloc();
|
||||
if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) {
|
||||
zig_panic("Unable to determine kernel32 path.");
|
||||
if (g->kernel32_lib_dir == nullptr) {
|
||||
Buf* kern_lib_path = buf_alloc();
|
||||
if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) {
|
||||
fprintf(stderr, "Unable to determine kernel32 path. --kernel32-lib-dir");
|
||||
exit(1);
|
||||
}
|
||||
g->kernel32_lib_dir = kern_lib_path;
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -5782,9 +5827,11 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
|
||||
case ZigLLVMFnIdClz:
|
||||
return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;
|
||||
case ZigLLVMFnIdFloor:
|
||||
return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1899859168;
|
||||
return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168;
|
||||
case ZigLLVMFnIdCeil:
|
||||
return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1953839089;
|
||||
return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1953839089;
|
||||
case ZigLLVMFnIdSqrt:
|
||||
return (uint32_t)(x.data.floating.bit_count) * (uint32_t)2225366385;
|
||||
case ZigLLVMFnIdOverflowArithmetic:
|
||||
return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
|
||||
((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
|
||||
@ -5803,7 +5850,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
|
||||
return a.data.clz.bit_count == b.data.clz.bit_count;
|
||||
case ZigLLVMFnIdFloor:
|
||||
case ZigLLVMFnIdCeil:
|
||||
return a.data.floor_ceil.bit_count == b.data.floor_ceil.bit_count;
|
||||
case ZigLLVMFnIdSqrt:
|
||||
return a.data.floating.bit_count == b.data.floating.bit_count;
|
||||
case ZigLLVMFnIdOverflowArithmetic:
|
||||
return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
|
||||
(a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
|
||||
@ -5883,8 +5931,8 @@ size_t type_id_len() {
|
||||
return array_length(all_type_ids);
|
||||
}
|
||||
|
||||
size_t type_id_index(TypeTableEntryId id) {
|
||||
switch (id) {
|
||||
size_t type_id_index(TypeTableEntry *entry) {
|
||||
switch (entry->id) {
|
||||
case TypeTableEntryIdInvalid:
|
||||
zig_unreachable();
|
||||
case TypeTableEntryIdMetaType:
|
||||
@ -5904,6 +5952,8 @@ size_t type_id_index(TypeTableEntryId id) {
|
||||
case TypeTableEntryIdArray:
|
||||
return 7;
|
||||
case TypeTableEntryIdStruct:
|
||||
if (entry->data.structure.is_slice)
|
||||
return 25;
|
||||
return 8;
|
||||
case TypeTableEntryIdNumLitFloat:
|
||||
return 9;
|
||||
@ -6089,4 +6139,3 @@ bool type_can_fail(TypeTableEntry *type_entry) {
|
||||
bool fn_type_can_fail(FnTypeId *fn_type_id) {
|
||||
return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync;
|
||||
}
|
||||
|
||||
|
||||
@ -104,6 +104,7 @@ ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent);
|
||||
Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var);
|
||||
ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent);
|
||||
ScopeLoop *create_loop_scope(AstNode *node, Scope *parent);
|
||||
ScopeSuspend *create_suspend_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);
|
||||
@ -173,7 +174,7 @@ void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value);
|
||||
const char *type_id_name(TypeTableEntryId id);
|
||||
TypeTableEntryId type_id_at_index(size_t index);
|
||||
size_t type_id_len();
|
||||
size_t type_id_index(TypeTableEntryId id);
|
||||
size_t type_id_index(TypeTableEntry *entry);
|
||||
TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id);
|
||||
bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry);
|
||||
LinkLib *create_link_lib(Buf *name);
|
||||
|
||||
@ -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:
|
||||
@ -730,7 +728,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
render_node_grouped(ar, field_node->data.struct_field.type);
|
||||
}
|
||||
if (field_node->data.struct_field.value != nullptr) {
|
||||
fprintf(ar->f, "= ");
|
||||
fprintf(ar->f, " = ");
|
||||
render_node_grouped(ar, field_node->data.struct_field.value);
|
||||
}
|
||||
fprintf(ar->f, ",\n");
|
||||
@ -1098,7 +1096,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeTestDecl:
|
||||
case NodeTypeStructField:
|
||||
|
||||
@ -181,3 +181,7 @@ bool bigfloat_has_fraction(const BigFloat *bigfloat) {
|
||||
f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored);
|
||||
return !f128M_eq(&floored, &bigfloat->value);
|
||||
}
|
||||
|
||||
void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) {
|
||||
f128M_sqrt(&op->value, &dest->value);
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2
|
||||
void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
|
||||
void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
|
||||
void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
|
||||
void bigfloat_sqrt(BigFloat *dest, const BigFloat *op);
|
||||
void bigfloat_append_buf(Buf *buf, const BigFloat *op);
|
||||
Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);
|
||||
|
||||
|
||||
@ -86,6 +86,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)
|
||||
size_t digits_to_copy = bit_count / 64;
|
||||
size_t leftover_bits = bit_count % 64;
|
||||
dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1);
|
||||
if (dest->digit_count == 1 && leftover_bits == 0) {
|
||||
dest->data.digit = op_digits[0];
|
||||
if (dest->data.digit == 0) dest->digit_count = 0;
|
||||
return;
|
||||
}
|
||||
dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
|
||||
for (size_t i = 0; i < digits_to_copy; i += 1) {
|
||||
uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0;
|
||||
@ -1254,12 +1259,11 @@ void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) {
|
||||
bigint_normalize(dest);
|
||||
return;
|
||||
}
|
||||
// TODO this code path is untested
|
||||
uint64_t first_digit = dest->data.digit;
|
||||
|
||||
dest->digit_count = max(op1->digit_count, op2->digit_count);
|
||||
dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
|
||||
dest->data.digits[0] = first_digit;
|
||||
size_t i = 1;
|
||||
|
||||
size_t i = 0;
|
||||
for (; i < op1->digit_count && i < op2->digit_count; i += 1) {
|
||||
dest->data.digits[i] = op1_digits[i] & op2_digits[i];
|
||||
}
|
||||
@ -1407,7 +1411,6 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO this code path is untested
|
||||
size_t digit_shift_count = shift_amt / 64;
|
||||
size_t leftover_shift_count = shift_amt % 64;
|
||||
|
||||
@ -1422,7 +1425,7 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) {
|
||||
uint64_t digit = op1_digits[op_digit_index];
|
||||
size_t dest_digit_index = op_digit_index - digit_shift_count;
|
||||
dest->data.digits[dest_digit_index] = carry | (digit >> leftover_shift_count);
|
||||
carry = (0xffffffffffffffffULL << leftover_shift_count) & digit;
|
||||
carry = digit << (64 - leftover_shift_count);
|
||||
|
||||
if (dest_digit_index == 0) { break; }
|
||||
op_digit_index -= 1;
|
||||
|
||||
620
src/codegen.cpp
620
src/codegen.cpp
@ -88,6 +88,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
|
||||
g->exported_symbol_names.init(8);
|
||||
g->external_prototypes.init(8);
|
||||
g->string_literals_table.init(16);
|
||||
g->type_info_cache.init(32);
|
||||
g->is_test_build = false;
|
||||
g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib);
|
||||
buf_resize(&g->global_asm, 0);
|
||||
@ -408,6 +409,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;
|
||||
@ -464,7 +468,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
|
||||
fn_table_entry->llvm_value, buf_ptr(&fn_export->name));
|
||||
}
|
||||
}
|
||||
fn_table_entry->llvm_name = LLVMGetValueName(fn_table_entry->llvm_value);
|
||||
fn_table_entry->llvm_name = strdup(LLVMGetValueName(fn_table_entry->llvm_value));
|
||||
|
||||
switch (fn_table_entry->fn_inline) {
|
||||
case FnInlineAlways:
|
||||
@ -509,7 +513,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
|
||||
}
|
||||
|
||||
if (fn_table_entry->body_node != nullptr) {
|
||||
bool want_fn_safety = g->build_mode != BuildModeFastRelease && !fn_table_entry->def_scope->safety_off;
|
||||
bool want_fn_safety = g->build_mode != BuildModeFastRelease &&
|
||||
g->build_mode != BuildModeSmallRelease &&
|
||||
!fn_table_entry->def_scope->safety_off;
|
||||
if (want_fn_safety) {
|
||||
if (g->libc_link_lib != nullptr) {
|
||||
addLLVMFnAttr(fn_table_entry->llvm_value, "sspstrong");
|
||||
@ -649,6 +655,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
|
||||
}
|
||||
case ScopeIdDeferExpr:
|
||||
case ScopeIdLoop:
|
||||
case ScopeIdSuspend:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdCoroPrelude:
|
||||
return get_di_scope(g, scope->parent);
|
||||
@ -714,12 +721,12 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
|
||||
return fn_val;
|
||||
}
|
||||
|
||||
static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) {
|
||||
static LLVMValueRef get_float_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) {
|
||||
assert(type_entry->id == TypeTableEntryIdFloat);
|
||||
|
||||
ZigLLVMFnKey key = {};
|
||||
key.id = fn_id;
|
||||
key.data.floor_ceil.bit_count = (uint32_t)type_entry->data.floating.bit_count;
|
||||
key.data.floating.bit_count = (uint32_t)type_entry->data.floating.bit_count;
|
||||
|
||||
auto existing_entry = g->llvm_fn_table.maybe_get(key);
|
||||
if (existing_entry)
|
||||
@ -730,6 +737,8 @@ static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, Zi
|
||||
name = "floor";
|
||||
} else if (fn_id == ZigLLVMFnIdCeil) {
|
||||
name = "ceil";
|
||||
} else if (fn_id == ZigLLVMFnIdSqrt) {
|
||||
name = "sqrt";
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -812,7 +821,7 @@ static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) {
|
||||
}
|
||||
|
||||
static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) {
|
||||
if (g->build_mode == BuildModeFastRelease)
|
||||
if (g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease)
|
||||
return false;
|
||||
|
||||
// TODO memoize
|
||||
@ -1114,6 +1123,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;
|
||||
@ -1140,50 +1350,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);
|
||||
@ -1641,7 +1825,6 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut
|
||||
};
|
||||
LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1,
|
||||
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
|
||||
LLVMSetTailCall(call_instruction, true);
|
||||
return call_instruction;
|
||||
}
|
||||
|
||||
@ -1723,7 +1906,7 @@ static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, TypeTableEntry *type
|
||||
if (type_entry->id == TypeTableEntryIdInt)
|
||||
return val;
|
||||
|
||||
LLVMValueRef floor_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdFloor);
|
||||
LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor);
|
||||
return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");
|
||||
}
|
||||
|
||||
@ -1731,7 +1914,7 @@ static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_
|
||||
if (type_entry->id == TypeTableEntryIdInt)
|
||||
return val;
|
||||
|
||||
LLVMValueRef ceil_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdCeil);
|
||||
LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil);
|
||||
return LLVMBuildCall(g->builder, ceil_fn, &val, 1, "");
|
||||
}
|
||||
|
||||
@ -3070,10 +3253,12 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui
|
||||
fn_name = "cttz";
|
||||
key.id = ZigLLVMFnIdCtz;
|
||||
key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count;
|
||||
} else {
|
||||
} else if (fn_id == BuiltinFnIdClz) {
|
||||
fn_name = "ctlz";
|
||||
key.id = ZigLLVMFnIdClz;
|
||||
key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
auto existing_entry = g->llvm_fn_table.maybe_get(key);
|
||||
@ -3375,9 +3560,30 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn
|
||||
LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order);
|
||||
|
||||
LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,
|
||||
success_order, failure_order);
|
||||
success_order, failure_order, instruction->is_weak);
|
||||
|
||||
return LLVMBuildExtractValue(g->builder, result_val, 1, "");
|
||||
TypeTableEntry *maybe_type = instruction->base.value.type;
|
||||
assert(maybe_type->id == TypeTableEntryIdMaybe);
|
||||
TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
|
||||
|
||||
if (type_is_codegen_pointer(child_type)) {
|
||||
LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, "");
|
||||
LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, "");
|
||||
return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, "");
|
||||
}
|
||||
|
||||
assert(instruction->tmp_ptr != nullptr);
|
||||
assert(type_has_bits(instruction->type));
|
||||
|
||||
LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, "");
|
||||
LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, "");
|
||||
gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val);
|
||||
|
||||
LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, "");
|
||||
LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, "");
|
||||
LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, "");
|
||||
gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false);
|
||||
return instruction->tmp_ptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) {
|
||||
@ -4204,6 +4410,44 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,
|
||||
return LLVMBuildIntToPtr(g->builder, uncasted_result, operand_type->type_ref, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_atomic_load(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionAtomicLoad *instruction)
|
||||
{
|
||||
LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering);
|
||||
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
|
||||
LLVMValueRef load_inst = gen_load(g, ptr, instruction->ptr->value.type, "");
|
||||
LLVMSetOrdering(load_inst, ordering);
|
||||
return load_inst;
|
||||
}
|
||||
|
||||
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 LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) {
|
||||
LLVMValueRef op = ir_llvm_value(g, instruction->op);
|
||||
assert(instruction->base.value.type->id == TypeTableEntryIdFloat);
|
||||
LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdSqrt);
|
||||
return LLVMBuildCall(g->builder, fn_val, &op, 1, "");
|
||||
}
|
||||
|
||||
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
|
||||
AstNode *source_node = instruction->source_node;
|
||||
Scope *scope = instruction->scope;
|
||||
@ -4259,6 +4503,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
case IrInstructionIdDeclRef:
|
||||
case IrInstructionIdSwitchVar:
|
||||
case IrInstructionIdOffsetOf:
|
||||
case IrInstructionIdTypeInfo:
|
||||
case IrInstructionIdTypeId:
|
||||
case IrInstructionIdSetEvalBranchQuota:
|
||||
case IrInstructionIdPtrTypeOf:
|
||||
@ -4419,8 +4664,16 @@ 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 IrInstructionIdAtomicLoad:
|
||||
return ir_render_atomic_load(g, executable, (IrInstructionAtomicLoad *)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);
|
||||
case IrInstructionIdSqrt:
|
||||
return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -5310,38 +5563,14 @@ static void do_code_gen(CodeGen *g) {
|
||||
g->cur_err_ret_trace_val_arg = nullptr;
|
||||
}
|
||||
|
||||
// error return tracing setup
|
||||
bool is_async = fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
|
||||
bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn &&
|
||||
(is_async || !have_err_ret_trace_arg);
|
||||
bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn && !is_async && !have_err_ret_trace_arg;
|
||||
LLVMValueRef err_ret_array_val = nullptr;
|
||||
if (have_err_ret_trace_stack) {
|
||||
// TODO call graph analysis to find out what this number needs to be for every function
|
||||
static const size_t stack_trace_ptr_count = 30;
|
||||
|
||||
TypeTableEntry *usize = g->builtin_types.entry_usize;
|
||||
TypeTableEntry *array_type = get_array_type(g, usize, stack_trace_ptr_count);
|
||||
LLVMValueRef err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses",
|
||||
get_abi_alignment(g, array_type));
|
||||
TypeTableEntry *array_type = get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count);
|
||||
err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses", get_abi_alignment(g, array_type));
|
||||
g->cur_err_ret_trace_val_stack = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type));
|
||||
size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
|
||||
LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)index_field_index, "");
|
||||
gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false);
|
||||
|
||||
size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
|
||||
LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)addresses_field_index, "");
|
||||
|
||||
TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
|
||||
size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, "");
|
||||
LLVMValueRef zero = LLVMConstNull(usize->type_ref);
|
||||
LLVMValueRef indices[] = {zero, zero};
|
||||
LLVMValueRef err_ret_array_val_elem0_ptr = LLVMBuildInBoundsGEP(g->builder, err_ret_array_val,
|
||||
indices, 2, "");
|
||||
gen_store(g, err_ret_array_val_elem0_ptr, ptr_field_ptr,
|
||||
get_pointer_to_type(g, get_pointer_to_type(g, usize, false), false));
|
||||
|
||||
size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
|
||||
gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false));
|
||||
} else {
|
||||
g->cur_err_ret_trace_val_stack = nullptr;
|
||||
}
|
||||
@ -5383,6 +5612,9 @@ static void do_code_gen(CodeGen *g) {
|
||||
} else if (instruction->id == IrInstructionIdErrWrapCode) {
|
||||
IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction;
|
||||
slot = &err_wrap_code_instruction->tmp_ptr;
|
||||
} else if (instruction->id == IrInstructionIdCmpxchg) {
|
||||
IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction;
|
||||
slot = &cmpxchg_instruction->tmp_ptr;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -5436,6 +5668,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
|
||||
@ -5519,10 +5776,12 @@ static void do_code_gen(CodeGen *g) {
|
||||
os_path_join(g->cache_dir, o_basename, output_path);
|
||||
ensure_cache_dir(g);
|
||||
|
||||
bool is_small = g->build_mode == BuildModeSmallRelease;
|
||||
|
||||
switch (g->emit_file_type) {
|
||||
case EmitFileTypeBinary:
|
||||
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
|
||||
ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug))
|
||||
ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug, is_small))
|
||||
{
|
||||
zig_panic("unable to write object file %s: %s", buf_ptr(output_path), err_msg);
|
||||
}
|
||||
@ -5532,7 +5791,7 @@ static void do_code_gen(CodeGen *g) {
|
||||
|
||||
case EmitFileTypeAssembly:
|
||||
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
|
||||
ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug))
|
||||
ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug, is_small))
|
||||
{
|
||||
zig_panic("unable to write assembly file %s: %s", buf_ptr(output_path), err_msg);
|
||||
}
|
||||
@ -5541,7 +5800,7 @@ static void do_code_gen(CodeGen *g) {
|
||||
|
||||
case EmitFileTypeLLVMIr:
|
||||
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
|
||||
ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug))
|
||||
ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug, is_small))
|
||||
{
|
||||
zig_panic("unable to write llvm-ir file %s: %s", buf_ptr(output_path), err_msg);
|
||||
}
|
||||
@ -5867,6 +6126,8 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdMemberType, "memberType", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdField, "field", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf
|
||||
create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4);
|
||||
create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4);
|
||||
@ -5883,7 +6144,8 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);
|
||||
create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6);
|
||||
create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6);
|
||||
create_builtin_fn(g, BuiltinFnIdFence, "fence", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);
|
||||
@ -5906,6 +6168,7 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
|
||||
create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
|
||||
create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
|
||||
@ -5919,6 +6182,7 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdExport, "export", 3);
|
||||
create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);
|
||||
create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5);
|
||||
create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3);
|
||||
}
|
||||
|
||||
static const char *bool_to_str(bool b) {
|
||||
@ -5930,6 +6194,7 @@ static const char *build_mode_to_str(BuildMode build_mode) {
|
||||
case BuildModeDebug: return "Mode.Debug";
|
||||
case BuildModeSafeRelease: return "Mode.ReleaseSafe";
|
||||
case BuildModeFastRelease: return "Mode.ReleaseFast";
|
||||
case BuildModeSmallRelease: return "Mode.ReleaseSmall";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -5943,6 +6208,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"
|
||||
@ -6068,6 +6335,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
|
||||
" Debug,\n"
|
||||
" ReleaseSafe,\n"
|
||||
" ReleaseFast,\n"
|
||||
" ReleaseSmall,\n"
|
||||
"};\n\n");
|
||||
}
|
||||
{
|
||||
@ -6077,8 +6345,196 @@ static void define_builtin_compile_vars(CodeGen *g) {
|
||||
const TypeTableEntryId id = type_id_at_index(i);
|
||||
buf_appendf(contents, " %s,\n", type_id_name(id));
|
||||
}
|
||||
buf_appendf(contents, " Slice,\n");
|
||||
buf_appendf(contents, "};\n\n");
|
||||
}
|
||||
{
|
||||
buf_appendf(contents,
|
||||
"pub const TypeInfo = union(TypeId) {\n"
|
||||
" Type: void,\n"
|
||||
" Void: void,\n"
|
||||
" Bool: void,\n"
|
||||
" NoReturn: void,\n"
|
||||
" Int: Int,\n"
|
||||
" Float: Float,\n"
|
||||
" Pointer: Pointer,\n"
|
||||
" Slice: Slice,\n"
|
||||
" Array: Array,\n"
|
||||
" Struct: Struct,\n"
|
||||
" FloatLiteral: void,\n"
|
||||
" IntLiteral: void,\n"
|
||||
" UndefinedLiteral: void,\n"
|
||||
" NullLiteral: void,\n"
|
||||
" Nullable: Nullable,\n"
|
||||
" ErrorUnion: ErrorUnion,\n"
|
||||
" ErrorSet: ErrorSet,\n"
|
||||
" Enum: Enum,\n"
|
||||
" Union: Union,\n"
|
||||
" Fn: Fn,\n"
|
||||
" Namespace: void,\n"
|
||||
" Block: void,\n"
|
||||
" BoundFn: Fn,\n"
|
||||
" ArgTuple: void,\n"
|
||||
" Opaque: void,\n"
|
||||
" Promise: Promise,\n"
|
||||
"\n\n"
|
||||
" pub const Int = struct {\n"
|
||||
" is_signed: bool,\n"
|
||||
" bits: u8,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const Float = struct {\n"
|
||||
" bits: u8,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const Pointer = struct {\n"
|
||||
" is_const: bool,\n"
|
||||
" is_volatile: bool,\n"
|
||||
" alignment: u32,\n"
|
||||
" child: type,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const Slice = Pointer;\n"
|
||||
"\n"
|
||||
" pub const Array = struct {\n"
|
||||
" len: usize,\n"
|
||||
" child: type,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const ContainerLayout = enum {\n"
|
||||
" Auto,\n"
|
||||
" Extern,\n"
|
||||
" Packed,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const StructField = struct {\n"
|
||||
" name: []const u8,\n"
|
||||
" offset: ?usize,\n"
|
||||
" field_type: type,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const Struct = struct {\n"
|
||||
" layout: ContainerLayout,\n"
|
||||
" fields: []StructField,\n"
|
||||
" defs: []Definition,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const Nullable = struct {\n"
|
||||
" child: type,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const ErrorUnion = struct {\n"
|
||||
" error_set: type,\n"
|
||||
" payload: type,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const Error = struct {\n"
|
||||
" name: []const u8,\n"
|
||||
" value: usize,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const ErrorSet = struct {\n"
|
||||
" errors: []Error,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const EnumField = struct {\n"
|
||||
" name: []const u8,\n"
|
||||
" value: usize,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const Enum = struct {\n"
|
||||
" layout: ContainerLayout,\n"
|
||||
" tag_type: type,\n"
|
||||
" fields: []EnumField,\n"
|
||||
" defs: []Definition,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const UnionField = struct {\n"
|
||||
" name: []const u8,\n"
|
||||
" enum_field: ?EnumField,\n"
|
||||
" field_type: type,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const Union = struct {\n"
|
||||
" layout: ContainerLayout,\n"
|
||||
" tag_type: type,\n"
|
||||
" fields: []UnionField,\n"
|
||||
" defs: []Definition,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const CallingConvention = enum {\n"
|
||||
" Unspecified,\n"
|
||||
" C,\n"
|
||||
" Cold,\n"
|
||||
" Naked,\n"
|
||||
" Stdcall,\n"
|
||||
" Async,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const FnArg = struct {\n"
|
||||
" is_generic: bool,\n"
|
||||
" is_noalias: bool,\n"
|
||||
" arg_type: type,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const Fn = struct {\n"
|
||||
" calling_convention: CallingConvention,\n"
|
||||
" is_generic: bool,\n"
|
||||
" is_var_args: bool,\n"
|
||||
" return_type: type,\n"
|
||||
" async_allocator_type: type,\n"
|
||||
" args: []FnArg,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const Promise = struct {\n"
|
||||
" child: type,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const Definition = struct {\n"
|
||||
" name: []const u8,\n"
|
||||
" is_pub: bool,\n"
|
||||
" data: Data,\n"
|
||||
"\n"
|
||||
" pub const Data = union(enum) {\n"
|
||||
" Type: type,\n"
|
||||
" Var: type,\n"
|
||||
" Fn: FnDef,\n"
|
||||
"\n"
|
||||
" pub const FnDef = struct {\n"
|
||||
" fn_type: type,\n"
|
||||
" inline_type: Inline,\n"
|
||||
" calling_convention: CallingConvention,\n"
|
||||
" is_var_args: bool,\n"
|
||||
" is_extern: bool,\n"
|
||||
" is_export: bool,\n"
|
||||
" lib_name: ?[]const u8,\n"
|
||||
" return_type: type,\n"
|
||||
" arg_names: [][] const u8,\n"
|
||||
"\n"
|
||||
" pub const Inline = enum {\n"
|
||||
" Auto,\n"
|
||||
" Always,\n"
|
||||
" Never,\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
"};\n\n");
|
||||
assert(ContainerLayoutAuto == 0);
|
||||
assert(ContainerLayoutExtern == 1);
|
||||
assert(ContainerLayoutPacked == 2);
|
||||
|
||||
assert(CallingConventionUnspecified == 0);
|
||||
assert(CallingConventionC == 1);
|
||||
assert(CallingConventionCold == 2);
|
||||
assert(CallingConventionNaked == 3);
|
||||
assert(CallingConventionStdcall == 4);
|
||||
assert(CallingConventionAsync == 5);
|
||||
|
||||
assert(FnInlineAuto == 0);
|
||||
assert(FnInlineAlways == 1);
|
||||
assert(FnInlineNever == 2);
|
||||
}
|
||||
{
|
||||
buf_appendf(contents,
|
||||
"pub const FloatMode = enum {\n"
|
||||
@ -6118,7 +6574,8 @@ static void define_builtin_compile_vars(CodeGen *g) {
|
||||
int err;
|
||||
Buf *abs_full_path = buf_alloc();
|
||||
if ((err = os_path_real(builtin_zig_path, abs_full_path))) {
|
||||
zig_panic("unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err));
|
||||
fprintf(stderr, "unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
assert(g->root_package);
|
||||
@ -6238,7 +6695,7 @@ static void init(CodeGen *g) {
|
||||
}
|
||||
}
|
||||
|
||||
g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease;
|
||||
g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease && g->build_mode != BuildModeSmallRelease;
|
||||
|
||||
define_builtin_fns(g);
|
||||
define_builtin_compile_vars(g);
|
||||
@ -6372,12 +6829,14 @@ static void gen_root_source(CodeGen *g) {
|
||||
Buf *abs_full_path = buf_alloc();
|
||||
int err;
|
||||
if ((err = os_path_real(rel_full_path, abs_full_path))) {
|
||||
zig_panic("unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));
|
||||
fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Buf *source_code = buf_alloc();
|
||||
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));
|
||||
fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
g->root_import = add_source_file(g, g->root_package, abs_full_path, source_code);
|
||||
@ -6930,4 +7389,3 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir,
|
||||
}
|
||||
return pkg;
|
||||
}
|
||||
|
||||
|
||||
1989
src/ir.cpp
1989
src/ir.cpp
File diff suppressed because it is too large
Load Diff
@ -358,9 +358,18 @@ static void ir_print_ptr_type_child(IrPrint *irp, IrInstructionPtrTypeChild *ins
|
||||
}
|
||||
|
||||
static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction) {
|
||||
fprintf(irp->f, "fieldptr ");
|
||||
ir_print_other_instruction(irp, instruction->container_ptr);
|
||||
fprintf(irp->f, ".%s", buf_ptr(instruction->field_name));
|
||||
if (instruction->field_name_buffer) {
|
||||
fprintf(irp->f, "fieldptr ");
|
||||
ir_print_other_instruction(irp, instruction->container_ptr);
|
||||
fprintf(irp->f, ".%s", buf_ptr(instruction->field_name_buffer));
|
||||
} else {
|
||||
assert(instruction->field_name_expr);
|
||||
fprintf(irp->f, "@field(");
|
||||
ir_print_other_instruction(irp, instruction->container_ptr);
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, instruction->field_name_expr);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
}
|
||||
|
||||
static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr *instruction) {
|
||||
@ -957,6 +966,12 @@ static void ir_print_offset_of(IrPrint *irp, IrInstructionOffsetOf *instruction)
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_type_info(IrPrint *irp, IrInstructionTypeInfo *instruction) {
|
||||
fprintf(irp->f, "@typeInfo(");
|
||||
ir_print_other_instruction(irp, instruction->type_value);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_type_id(IrPrint *irp, IrInstructionTypeId *instruction) {
|
||||
fprintf(irp->f, "@typeId(");
|
||||
ir_print_other_instruction(irp, instruction->type_value);
|
||||
@ -1024,7 +1039,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) {
|
||||
@ -1163,6 +1187,24 @@ static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instructio
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_atomic_load(IrPrint *irp, IrInstructionAtomicLoad *instruction) {
|
||||
fprintf(irp->f, "@atomicLoad(");
|
||||
if (instruction->operand_type != nullptr) {
|
||||
ir_print_other_instruction(irp, instruction->operand_type);
|
||||
} else {
|
||||
fprintf(irp->f, "[TODO print]");
|
||||
}
|
||||
fprintf(irp->f, ",");
|
||||
ir_print_other_instruction(irp, instruction->ptr);
|
||||
fprintf(irp->f, ",");
|
||||
if (instruction->ordering != nullptr) {
|
||||
ir_print_other_instruction(irp, instruction->ordering);
|
||||
} else {
|
||||
fprintf(irp->f, "[TODO print]");
|
||||
}
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeeping *instruction) {
|
||||
fprintf(irp->f, "@awaitBookkeeping(");
|
||||
ir_print_other_instruction(irp, instruction->promise_result_type);
|
||||
@ -1179,6 +1221,34 @@ static void ir_print_add_implicit_return_type(IrPrint *irp, IrInstructionAddImpl
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_merge_err_ret_traces(IrPrint *irp, IrInstructionMergeErrRetTraces *instruction) {
|
||||
fprintf(irp->f, "@mergeErrRetTraces(");
|
||||
ir_print_other_instruction(irp, instruction->coro_promise_ptr);
|
||||
fprintf(irp->f, ",");
|
||||
ir_print_other_instruction(irp, instruction->src_err_ret_trace_ptr);
|
||||
fprintf(irp->f, ",");
|
||||
ir_print_other_instruction(irp, instruction->dest_err_ret_trace_ptr);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRetTracePtr *instruction) {
|
||||
fprintf(irp->f, "@markErrRetTracePtr(");
|
||||
ir_print_other_instruction(irp, instruction->err_ret_trace_ptr);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) {
|
||||
fprintf(irp->f, "@sqrt(");
|
||||
if (instruction->type != nullptr) {
|
||||
ir_print_other_instruction(irp, instruction->type);
|
||||
} else {
|
||||
fprintf(irp->f, "null");
|
||||
}
|
||||
fprintf(irp->f, ",");
|
||||
ir_print_other_instruction(irp, instruction->op);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
ir_print_prefix(irp, instruction);
|
||||
switch (instruction->id) {
|
||||
@ -1472,6 +1542,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdOffsetOf:
|
||||
ir_print_offset_of(irp, (IrInstructionOffsetOf *)instruction);
|
||||
break;
|
||||
case IrInstructionIdTypeInfo:
|
||||
ir_print_type_info(irp, (IrInstructionTypeInfo *)instruction);
|
||||
break;
|
||||
case IrInstructionIdTypeId:
|
||||
ir_print_type_id(irp, (IrInstructionTypeId *)instruction);
|
||||
break;
|
||||
@ -1559,6 +1632,18 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdAddImplicitReturnType:
|
||||
ir_print_add_implicit_return_type(irp, (IrInstructionAddImplicitReturnType *)instruction);
|
||||
break;
|
||||
case IrInstructionIdMergeErrRetTraces:
|
||||
ir_print_merge_err_ret_traces(irp, (IrInstructionMergeErrRetTraces *)instruction);
|
||||
break;
|
||||
case IrInstructionIdMarkErrRetTracePtr:
|
||||
ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction);
|
||||
break;
|
||||
case IrInstructionIdSqrt:
|
||||
ir_print_sqrt(irp, (IrInstructionSqrt *)instruction);
|
||||
break;
|
||||
case IrInstructionIdAtomicLoad:
|
||||
ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction);
|
||||
break;
|
||||
}
|
||||
fprintf(irp->f, "\n");
|
||||
}
|
||||
|
||||
@ -217,6 +217,9 @@ static void construct_linker_job_elf(LinkJob *lj) {
|
||||
lj->args.append(g->linker_script);
|
||||
}
|
||||
|
||||
if (g->no_rosegment_workaround) {
|
||||
lj->args.append("--no-rosegment");
|
||||
}
|
||||
lj->args.append("--gc-sections");
|
||||
|
||||
lj->args.append("-m");
|
||||
|
||||
41
src/main.cpp
41
src/main.cpp
@ -43,6 +43,7 @@ static int usage(const char *arg0) {
|
||||
" --pkg-end pop current pkg\n"
|
||||
" --release-fast build with optimizations on and safety off\n"
|
||||
" --release-safe build with optimizations on and safety on\n"
|
||||
" --release-small build with size optimizations on and safety off\n"
|
||||
" --static output will be statically linked\n"
|
||||
" --strip exclude debug symbols\n"
|
||||
" --target-arch [name] specify target architecture\n"
|
||||
@ -54,7 +55,6 @@ static int usage(const char *arg0) {
|
||||
" --verbose-ir turn on compiler debug output for Zig IR\n"
|
||||
" --verbose-llvm-ir turn on compiler debug output for LLVM IR\n"
|
||||
" --verbose-cimport turn on compiler debug output for C imports\n"
|
||||
" --zig-install-prefix [path] override directory where zig thinks it is installed\n"
|
||||
" -dirafter [dir] same as -isystem but do it last\n"
|
||||
" -isystem [dir] add additional search path for other .h files\n"
|
||||
" -mllvm [arg] additional arguments to forward to LLVM's option processing\n"
|
||||
@ -74,6 +74,7 @@ static int usage(const char *arg0) {
|
||||
" -L[dir] alias for --library-path\n"
|
||||
" -rdynamic add all symbols to the dynamic symbol table\n"
|
||||
" -rpath [path] add directory to the runtime library search path\n"
|
||||
" --no-rosegment compromise security to workaround valgrind bug\n"
|
||||
" -mconsole (windows) --subsystem console to the linker\n"
|
||||
" -mwindows (windows) --subsystem windows to the linker\n"
|
||||
" -framework [name] (darwin) link against framework\n"
|
||||
@ -177,6 +178,7 @@ static int find_zig_lib_dir(Buf *out_path) {
|
||||
int err;
|
||||
|
||||
Buf self_exe_path = BUF_INIT;
|
||||
buf_resize(&self_exe_path, 0);
|
||||
if (!(err = os_self_exe_path(&self_exe_path))) {
|
||||
Buf *cur_path = &self_exe_path;
|
||||
|
||||
@ -199,23 +201,14 @@ static int find_zig_lib_dir(Buf *out_path) {
|
||||
return ErrorFileNotFound;
|
||||
}
|
||||
|
||||
static Buf *resolve_zig_lib_dir(const char *zig_install_prefix_arg) {
|
||||
static Buf *resolve_zig_lib_dir(void) {
|
||||
int err;
|
||||
Buf *result = buf_alloc();
|
||||
if (zig_install_prefix_arg == nullptr) {
|
||||
if ((err = find_zig_lib_dir(result))) {
|
||||
fprintf(stderr, "Unable to find zig lib directory. Reinstall Zig or use --zig-install-prefix.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return result;
|
||||
if ((err = find_zig_lib_dir(result))) {
|
||||
fprintf(stderr, "Unable to find zig lib directory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
Buf *zig_lib_dir_buf = buf_create_from_str(zig_install_prefix_arg);
|
||||
if (test_zig_install_prefix(zig_lib_dir_buf, result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
fprintf(stderr, "No Zig installation found at prefix: %s\n", zig_install_prefix_arg);
|
||||
exit(EXIT_FAILURE);
|
||||
return result;
|
||||
}
|
||||
|
||||
enum Cmd {
|
||||
@ -299,7 +292,6 @@ int main(int argc, char **argv) {
|
||||
const char *libc_include_dir = nullptr;
|
||||
const char *msvc_lib_dir = nullptr;
|
||||
const char *kernel32_lib_dir = nullptr;
|
||||
const char *zig_install_prefix = nullptr;
|
||||
const char *dynamic_linker = nullptr;
|
||||
ZigList<const char *> clang_argv = {0};
|
||||
ZigList<const char *> llvm_argv = {0};
|
||||
@ -333,6 +325,7 @@ int main(int argc, char **argv) {
|
||||
ZigList<const char *> test_exec_args = {0};
|
||||
int comptime_args_end = 0;
|
||||
int runtime_args_start = argc;
|
||||
bool no_rosegment_workaround = false;
|
||||
|
||||
if (argc >= 2 && strcmp(argv[1], "build") == 0) {
|
||||
const char *zig_exe_path = arg0;
|
||||
@ -359,17 +352,12 @@ int main(int argc, char **argv) {
|
||||
} else if (i + 1 < argc && strcmp(argv[i], "--cache-dir") == 0) {
|
||||
cache_dir = argv[i + 1];
|
||||
i += 1;
|
||||
} else if (i + 1 < argc && strcmp(argv[i], "--zig-install-prefix") == 0) {
|
||||
args.append(argv[i]);
|
||||
i += 1;
|
||||
zig_install_prefix = argv[i];
|
||||
args.append(zig_install_prefix);
|
||||
} else {
|
||||
args.append(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix);
|
||||
Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
|
||||
|
||||
Buf *zig_std_dir = buf_alloc();
|
||||
os_path_join(zig_lib_dir_buf, buf_create_from_str("std"), zig_std_dir);
|
||||
@ -497,6 +485,8 @@ int main(int argc, char **argv) {
|
||||
build_mode = BuildModeFastRelease;
|
||||
} else if (strcmp(arg, "--release-safe") == 0) {
|
||||
build_mode = BuildModeSafeRelease;
|
||||
} else if (strcmp(arg, "--release-small") == 0) {
|
||||
build_mode = BuildModeSmallRelease;
|
||||
} else if (strcmp(arg, "--strip") == 0) {
|
||||
strip = true;
|
||||
} else if (strcmp(arg, "--static") == 0) {
|
||||
@ -519,6 +509,8 @@ int main(int argc, char **argv) {
|
||||
mconsole = true;
|
||||
} else if (strcmp(arg, "-rdynamic") == 0) {
|
||||
rdynamic = true;
|
||||
} else if (strcmp(arg, "--no-rosegment") == 0) {
|
||||
no_rosegment_workaround = true;
|
||||
} else if (strcmp(arg, "--each-lib-rpath") == 0) {
|
||||
each_lib_rpath = true;
|
||||
} else if (strcmp(arg, "--enable-timing-info") == 0) {
|
||||
@ -590,8 +582,6 @@ int main(int argc, char **argv) {
|
||||
msvc_lib_dir = argv[i];
|
||||
} else if (strcmp(arg, "--kernel32-lib-dir") == 0) {
|
||||
kernel32_lib_dir = argv[i];
|
||||
} else if (strcmp(arg, "--zig-install-prefix") == 0) {
|
||||
zig_install_prefix = argv[i];
|
||||
} else if (strcmp(arg, "--dynamic-linker") == 0) {
|
||||
dynamic_linker = argv[i];
|
||||
} else if (strcmp(arg, "-isystem") == 0) {
|
||||
@ -803,7 +793,7 @@ int main(int argc, char **argv) {
|
||||
full_cache_dir);
|
||||
}
|
||||
|
||||
Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix);
|
||||
Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
|
||||
|
||||
CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, zig_lib_dir_buf);
|
||||
codegen_set_out_name(g, buf_out_name);
|
||||
@ -858,6 +848,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
codegen_set_windows_subsystem(g, mwindows, mconsole);
|
||||
codegen_set_rdynamic(g, rdynamic);
|
||||
g->no_rosegment_workaround = no_rosegment_workaround;
|
||||
if (mmacosx_version_min && mios_version_min) {
|
||||
fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n");
|
||||
return EXIT_FAILURE;
|
||||
|
||||
@ -1007,6 +1007,7 @@ int os_self_exe_path(Buf *out_path) {
|
||||
buf_resize(out_path, buf_len(out_path) * 2);
|
||||
continue;
|
||||
}
|
||||
buf_resize(out_path, amt);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -648,12 +648,30 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
|
||||
}
|
||||
|
||||
/*
|
||||
SuspendExpression(body) = "suspend" "|" Symbol "|" body
|
||||
SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))
|
||||
*/
|
||||
static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
|
||||
size_t orig_token_index = *token_index;
|
||||
|
||||
Token *suspend_token = &pc->tokens->at(*token_index);
|
||||
Token *name_token = nullptr;
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
if (token->id == TokenIdSymbol) {
|
||||
*token_index += 1;
|
||||
Token *colon_token = &pc->tokens->at(*token_index);
|
||||
if (colon_token->id == TokenIdColon) {
|
||||
*token_index += 1;
|
||||
name_token = token;
|
||||
token = &pc->tokens->at(*token_index);
|
||||
} else if (mandatory) {
|
||||
ast_expect_token(pc, colon_token, TokenIdColon);
|
||||
zig_unreachable();
|
||||
} else {
|
||||
*token_index = orig_token_index;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Token *suspend_token = token;
|
||||
if (suspend_token->id == TokenIdKeywordSuspend) {
|
||||
*token_index += 1;
|
||||
} else if (mandatory) {
|
||||
@ -675,6 +693,9 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b
|
||||
}
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
|
||||
if (name_token != nullptr) {
|
||||
node->data.suspend.name = token_buf(name_token);
|
||||
}
|
||||
node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);
|
||||
ast_eat_token(pc, token_index, TokenIdBinOr);
|
||||
node->data.suspend.block = ast_parse_block(pc, token_index, true);
|
||||
@ -2923,9 +2944,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;
|
||||
|
||||
@ -3667,6 +3667,7 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_
|
||||
if (existing_entry) {
|
||||
return existing_entry->value;
|
||||
}
|
||||
|
||||
QualType child_qt = typedef_decl->getUnderlyingType();
|
||||
Buf *type_name = buf_create_from_str(decl_name(typedef_decl));
|
||||
|
||||
@ -3700,16 +3701,19 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_
|
||||
// use the name of this typedef
|
||||
// TODO
|
||||
|
||||
// trans_qual_type here might cause us to look at this typedef again so we put the item in the map first
|
||||
AstNode *symbol_node = trans_create_node_symbol(c, type_name);
|
||||
c->decl_table.put(typedef_decl->getCanonicalDecl(), symbol_node);
|
||||
|
||||
AstNode *type_node = trans_qual_type(c, child_qt, typedef_decl->getLocation());
|
||||
if (type_node == nullptr) {
|
||||
emit_warning(c, typedef_decl->getLocation(), "typedef %s - unresolved child type", buf_ptr(type_name));
|
||||
c->decl_table.put(typedef_decl, nullptr);
|
||||
// TODO add global var with type_name equal to @compileError("unable to resolve C type")
|
||||
return nullptr;
|
||||
}
|
||||
add_global_var(c, type_name, type_node);
|
||||
|
||||
AstNode *symbol_node = trans_create_node_symbol(c, type_name);
|
||||
c->decl_table.put(typedef_decl->getCanonicalDecl(), symbol_node);
|
||||
return symbol_node;
|
||||
}
|
||||
|
||||
@ -3744,6 +3748,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
|
||||
return demote_enum_to_opaque(c, enum_decl, full_type_name, bare_name);
|
||||
}
|
||||
|
||||
|
||||
bool pure_enum = true;
|
||||
uint32_t field_count = 0;
|
||||
for (auto it = enum_def->enumerator_begin(),
|
||||
@ -3755,84 +3760,53 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
|
||||
pure_enum = false;
|
||||
}
|
||||
}
|
||||
|
||||
AstNode *tag_int_type = trans_qual_type(c, enum_decl->getIntegerType(), enum_decl->getLocation());
|
||||
assert(tag_int_type);
|
||||
|
||||
if (pure_enum) {
|
||||
AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl);
|
||||
enum_node->data.container_decl.kind = ContainerKindEnum;
|
||||
enum_node->data.container_decl.layout = ContainerLayoutExtern;
|
||||
// TODO only emit this tag type if the enum tag type is not the default.
|
||||
// I don't know what the default is, need to figure out how clang is deciding.
|
||||
// it appears to at least be different across gcc/msvc
|
||||
if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) &&
|
||||
!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int))
|
||||
{
|
||||
enum_node->data.container_decl.init_arg_expr = tag_int_type;
|
||||
}
|
||||
|
||||
enum_node->data.container_decl.fields.resize(field_count);
|
||||
uint32_t i = 0;
|
||||
for (auto it = enum_def->enumerator_begin(),
|
||||
it_end = enum_def->enumerator_end();
|
||||
it != it_end; ++it, i += 1)
|
||||
{
|
||||
const EnumConstantDecl *enum_const = *it;
|
||||
|
||||
Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
|
||||
Buf *field_name;
|
||||
if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) {
|
||||
field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name));
|
||||
} else {
|
||||
field_name = enum_val_name;
|
||||
}
|
||||
|
||||
AstNode *field_node = trans_create_node(c, NodeTypeStructField);
|
||||
field_node->data.struct_field.name = field_name;
|
||||
field_node->data.struct_field.type = nullptr;
|
||||
enum_node->data.container_decl.fields.items[i] = field_node;
|
||||
|
||||
// in C each enum value is in the global namespace. so we put them there too.
|
||||
// at this point we can rely on the enum emitting successfully
|
||||
if (is_anonymous) {
|
||||
AstNode *lit_node = trans_create_node_unsigned(c, i);
|
||||
add_global_var(c, enum_val_name, lit_node);
|
||||
} else {
|
||||
AstNode *field_access_node = trans_create_node_field_access(c,
|
||||
trans_create_node_symbol(c, full_type_name), field_name);
|
||||
add_global_var(c, enum_val_name, field_access_node);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_anonymous) {
|
||||
c->decl_table.put(enum_decl->getCanonicalDecl(), enum_node);
|
||||
return enum_node;
|
||||
} else {
|
||||
AstNode *symbol_node = trans_create_node_symbol(c, full_type_name);
|
||||
add_global_weak_alias(c, bare_name, full_type_name);
|
||||
add_global_var(c, full_type_name, enum_node);
|
||||
c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node);
|
||||
return enum_node;
|
||||
}
|
||||
AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl);
|
||||
enum_node->data.container_decl.kind = ContainerKindEnum;
|
||||
enum_node->data.container_decl.layout = ContainerLayoutExtern;
|
||||
// TODO only emit this tag type if the enum tag type is not the default.
|
||||
// I don't know what the default is, need to figure out how clang is deciding.
|
||||
// it appears to at least be different across gcc/msvc
|
||||
if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) &&
|
||||
!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int))
|
||||
{
|
||||
enum_node->data.container_decl.init_arg_expr = tag_int_type;
|
||||
}
|
||||
|
||||
// TODO after issue #305 is solved, make this be an enum with tag_int_type
|
||||
// as the integer type and set the custom enum values
|
||||
AstNode *enum_node = tag_int_type;
|
||||
|
||||
|
||||
// add variables for all the values with enum_node
|
||||
enum_node->data.container_decl.fields.resize(field_count);
|
||||
uint32_t i = 0;
|
||||
for (auto it = enum_def->enumerator_begin(),
|
||||
it_end = enum_def->enumerator_end();
|
||||
it != it_end; ++it)
|
||||
it != it_end; ++it, i += 1)
|
||||
{
|
||||
const EnumConstantDecl *enum_const = *it;
|
||||
|
||||
Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
|
||||
AstNode *int_node = trans_create_node_apint(c, enum_const->getInitVal());
|
||||
AstNode *var_node = add_global_var(c, enum_val_name, int_node);
|
||||
var_node->data.variable_declaration.type = tag_int_type;
|
||||
Buf *field_name;
|
||||
if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) {
|
||||
field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name));
|
||||
} else {
|
||||
field_name = enum_val_name;
|
||||
}
|
||||
|
||||
AstNode *int_node = pure_enum && !is_anonymous ? nullptr : trans_create_node_apint(c, enum_const->getInitVal());
|
||||
AstNode *field_node = trans_create_node(c, NodeTypeStructField);
|
||||
field_node->data.struct_field.name = field_name;
|
||||
field_node->data.struct_field.type = nullptr;
|
||||
field_node->data.struct_field.value = int_node;
|
||||
enum_node->data.container_decl.fields.items[i] = field_node;
|
||||
|
||||
// in C each enum value is in the global namespace. so we put them there too.
|
||||
// at this point we can rely on the enum emitting successfully
|
||||
if (is_anonymous) {
|
||||
Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
|
||||
add_global_var(c, enum_val_name, int_node);
|
||||
} else {
|
||||
AstNode *field_access_node = trans_create_node_field_access(c,
|
||||
trans_create_node_symbol(c, full_type_name), field_name);
|
||||
add_global_var(c, enum_val_name, field_access_node);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_anonymous) {
|
||||
@ -3843,7 +3817,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
|
||||
add_global_weak_alias(c, bare_name, full_type_name);
|
||||
add_global_var(c, full_type_name, enum_node);
|
||||
c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node);
|
||||
return symbol_node;
|
||||
return enum_node;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -81,7 +81,7 @@ static const bool assertions_on = false;
|
||||
#endif
|
||||
|
||||
bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
|
||||
const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug)
|
||||
const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small)
|
||||
{
|
||||
std::error_code EC;
|
||||
raw_fd_ostream dest(filename, EC, sys::fs::F_None);
|
||||
@ -100,7 +100,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
|
||||
return true;
|
||||
}
|
||||
PMBuilder->OptLevel = target_machine->getOptLevel();
|
||||
PMBuilder->SizeLevel = 0;
|
||||
PMBuilder->SizeLevel = is_small ? 2 : 0;
|
||||
|
||||
PMBuilder->DisableTailCalls = is_debug;
|
||||
PMBuilder->DisableUnitAtATime = is_debug;
|
||||
@ -765,10 +765,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) {
|
||||
|
||||
LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
|
||||
LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
|
||||
LLVMAtomicOrdering failure_ordering)
|
||||
LLVMAtomicOrdering failure_ordering, bool is_weak)
|
||||
{
|
||||
return wrap(unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), unwrap(new_val),
|
||||
mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering)));
|
||||
AtomicCmpXchgInst *inst = unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp),
|
||||
unwrap(new_val), mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering));
|
||||
inst->setWeak(is_weak);
|
||||
return wrap(inst);
|
||||
}
|
||||
|
||||
LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
|
||||
|
||||
@ -52,7 +52,7 @@ enum ZigLLVM_EmitOutputType {
|
||||
};
|
||||
|
||||
ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
|
||||
const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug);
|
||||
const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small);
|
||||
|
||||
ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);
|
||||
|
||||
@ -66,7 +66,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LL
|
||||
|
||||
ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
|
||||
LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
|
||||
LLVMAtomicOrdering failure_ordering);
|
||||
LLVMAtomicOrdering failure_ordering, bool is_weak);
|
||||
|
||||
ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
|
||||
const char *name);
|
||||
|
||||
@ -28,11 +28,11 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(l: &Self) void {
|
||||
pub fn deinit(l: &const Self) void {
|
||||
l.allocator.free(l.items);
|
||||
}
|
||||
|
||||
pub fn toSlice(l: &Self) []align(A) T {
|
||||
pub fn toSlice(l: &const Self) []align(A) T {
|
||||
return l.items[0..l.len];
|
||||
}
|
||||
|
||||
@ -44,6 +44,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
|
||||
return l.toSliceConst()[n];
|
||||
}
|
||||
|
||||
pub fn count(self: &const Self) usize {
|
||||
return self.len;
|
||||
}
|
||||
|
||||
/// ArrayList takes ownership of the passed in slice. The slice must have been
|
||||
/// allocated with `allocator`.
|
||||
/// Deinitialize with `deinit` or use `toOwnedSlice`.
|
||||
@ -128,6 +132,27 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
|
||||
return null;
|
||||
return self.pop();
|
||||
}
|
||||
|
||||
pub const Iterator = struct {
|
||||
list: &const Self,
|
||||
// how many items have we returned
|
||||
count: usize,
|
||||
|
||||
pub fn next(it: &Iterator) ?T {
|
||||
if (it.count >= it.list.len) return null;
|
||||
const val = it.list.at(it.count);
|
||||
it.count += 1;
|
||||
return val;
|
||||
}
|
||||
|
||||
pub fn reset(it: &Iterator) void {
|
||||
it.count = 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn iterator(self: &const Self) Iterator {
|
||||
return Iterator { .list = self, .count = 0 };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -143,6 +168,14 @@ test "basic ArrayList test" {
|
||||
assert(list.items[i] == i32(i + 1));
|
||||
}}
|
||||
|
||||
for (list.toSlice()) |v, i| {
|
||||
assert(v == i32(i + 1));
|
||||
}
|
||||
|
||||
for (list.toSliceConst()) |v, i| {
|
||||
assert(v == i32(i + 1));
|
||||
}
|
||||
|
||||
assert(list.pop() == 10);
|
||||
assert(list.len == 9);
|
||||
|
||||
@ -157,6 +190,35 @@ test "basic ArrayList test" {
|
||||
assert(list.len == 9);
|
||||
}
|
||||
|
||||
test "iterator ArrayList test" {
|
||||
var list = ArrayList(i32).init(debug.global_allocator);
|
||||
defer list.deinit();
|
||||
|
||||
try list.append(1);
|
||||
try list.append(2);
|
||||
try list.append(3);
|
||||
|
||||
var count : i32 = 0;
|
||||
var it = list.iterator();
|
||||
while (it.next()) |next| {
|
||||
assert(next == count + 1);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
assert(count == 3);
|
||||
assert(it.next() == null);
|
||||
it.reset();
|
||||
count = 0;
|
||||
while (it.next()) |next| {
|
||||
assert(next == count + 1);
|
||||
count += 1;
|
||||
if (count == 2) break;
|
||||
}
|
||||
|
||||
it.reset();
|
||||
assert(?? it.next() == 1);
|
||||
}
|
||||
|
||||
test "insert ArrayList test" {
|
||||
var list = ArrayList(i32).init(debug.global_allocator);
|
||||
defer list.deinit();
|
||||
|
||||
7
std/atomic/index.zig
Normal file
7
std/atomic/index.zig
Normal file
@ -0,0 +1,7 @@
|
||||
pub const Stack = @import("stack.zig").Stack;
|
||||
pub const Queue = @import("queue.zig").Queue;
|
||||
|
||||
test "std.atomic" {
|
||||
_ = @import("stack.zig").Stack;
|
||||
_ = @import("queue.zig").Queue;
|
||||
}
|
||||
126
std/atomic/queue.zig
Normal file
126
std/atomic/queue.zig
Normal file
@ -0,0 +1,126 @@
|
||||
const builtin = @import("builtin");
|
||||
const AtomicOrder = builtin.AtomicOrder;
|
||||
const AtomicRmwOp = builtin.AtomicRmwOp;
|
||||
|
||||
/// Many reader, many writer, non-allocating, thread-safe, lock-free
|
||||
pub fn Queue(comptime T: type) type {
|
||||
return struct {
|
||||
head: &Node,
|
||||
tail: &Node,
|
||||
root: Node,
|
||||
|
||||
pub const Self = this;
|
||||
|
||||
pub const Node = struct {
|
||||
next: ?&Node,
|
||||
data: T,
|
||||
};
|
||||
|
||||
// TODO: well defined copy elision: https://github.com/zig-lang/zig/issues/287
|
||||
pub fn init(self: &Self) void {
|
||||
self.root.next = null;
|
||||
self.head = &self.root;
|
||||
self.tail = &self.root;
|
||||
}
|
||||
|
||||
pub fn put(self: &Self, node: &Node) void {
|
||||
node.next = null;
|
||||
|
||||
const tail = @atomicRmw(&Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
|
||||
_ = @atomicRmw(?&Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
|
||||
}
|
||||
|
||||
pub fn get(self: &Self) ?&Node {
|
||||
var head = @atomicLoad(&Node, &self.head, AtomicOrder.SeqCst);
|
||||
while (true) {
|
||||
const node = head.next ?? return null;
|
||||
head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return node;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const Context = struct {
|
||||
allocator: &std.mem.Allocator,
|
||||
queue: &Queue(i32),
|
||||
put_sum: isize,
|
||||
get_sum: isize,
|
||||
get_count: usize,
|
||||
puts_done: u8, // TODO make this a bool
|
||||
};
|
||||
|
||||
// TODO add lazy evaluated build options and then put puts_per_thread behind
|
||||
// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor
|
||||
// CI we would use a less aggressive setting since at 1 core, while we still
|
||||
// want this test to pass, we need a smaller value since there is so much thrashing
|
||||
// we would also use a less aggressive setting when running in valgrind
|
||||
const puts_per_thread = 500;
|
||||
const put_thread_count = 3;
|
||||
|
||||
test "std.atomic.queue" {
|
||||
var direct_allocator = std.heap.DirectAllocator.init();
|
||||
defer direct_allocator.deinit();
|
||||
|
||||
var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
|
||||
defer direct_allocator.allocator.free(plenty_of_memory);
|
||||
|
||||
var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
|
||||
var a = &fixed_buffer_allocator.allocator;
|
||||
|
||||
var queue: Queue(i32) = undefined;
|
||||
queue.init();
|
||||
var context = Context {
|
||||
.allocator = a,
|
||||
.queue = &queue,
|
||||
.put_sum = 0,
|
||||
.get_sum = 0,
|
||||
.puts_done = 0,
|
||||
.get_count = 0,
|
||||
};
|
||||
|
||||
var putters: [put_thread_count]&std.os.Thread = undefined;
|
||||
for (putters) |*t| {
|
||||
*t = try std.os.spawnThread(&context, startPuts);
|
||||
}
|
||||
var getters: [put_thread_count]&std.os.Thread = undefined;
|
||||
for (getters) |*t| {
|
||||
*t = try std.os.spawnThread(&context, startGets);
|
||||
}
|
||||
|
||||
for (putters) |t| t.wait();
|
||||
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||
for (getters) |t| t.wait();
|
||||
|
||||
std.debug.assert(context.put_sum == context.get_sum);
|
||||
std.debug.assert(context.get_count == puts_per_thread * put_thread_count);
|
||||
}
|
||||
|
||||
fn startPuts(ctx: &Context) u8 {
|
||||
var put_count: usize = puts_per_thread;
|
||||
var r = std.rand.DefaultPrng.init(0xdeadbeef);
|
||||
while (put_count != 0) : (put_count -= 1) {
|
||||
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
|
||||
const x = @bitCast(i32, r.random.scalar(u32));
|
||||
const node = ctx.allocator.create(Queue(i32).Node) catch unreachable;
|
||||
node.data = x;
|
||||
ctx.queue.put(node);
|
||||
_ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn startGets(ctx: &Context) u8 {
|
||||
while (true) {
|
||||
while (ctx.queue.get()) |node| {
|
||||
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
|
||||
_ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
|
||||
_ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
|
||||
}
|
||||
|
||||
if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
131
std/atomic/stack.zig
Normal file
131
std/atomic/stack.zig
Normal file
@ -0,0 +1,131 @@
|
||||
const builtin = @import("builtin");
|
||||
const AtomicOrder = builtin.AtomicOrder;
|
||||
|
||||
/// Many reader, many writer, non-allocating, thread-safe, lock-free
|
||||
pub fn Stack(comptime T: type) type {
|
||||
return struct {
|
||||
root: ?&Node,
|
||||
|
||||
pub const Self = this;
|
||||
|
||||
pub const Node = struct {
|
||||
next: ?&Node,
|
||||
data: T,
|
||||
};
|
||||
|
||||
pub fn init() Self {
|
||||
return Self {
|
||||
.root = null,
|
||||
};
|
||||
}
|
||||
|
||||
/// push operation, but only if you are the first item in the stack. if you did not succeed in
|
||||
/// being the first item in the stack, returns the other item that was there.
|
||||
pub fn pushFirst(self: &Self, node: &Node) ?&Node {
|
||||
node.next = null;
|
||||
return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst);
|
||||
}
|
||||
|
||||
pub fn push(self: &Self, node: &Node) void {
|
||||
var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst);
|
||||
while (true) {
|
||||
node.next = root;
|
||||
root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop(self: &Self) ?&Node {
|
||||
var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst);
|
||||
while (true) {
|
||||
root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isEmpty(self: &Self) bool {
|
||||
return @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst) == null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const Context = struct {
|
||||
allocator: &std.mem.Allocator,
|
||||
stack: &Stack(i32),
|
||||
put_sum: isize,
|
||||
get_sum: isize,
|
||||
get_count: usize,
|
||||
puts_done: u8, // TODO make this a bool
|
||||
};
|
||||
// TODO add lazy evaluated build options and then put puts_per_thread behind
|
||||
// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor
|
||||
// CI we would use a less aggressive setting since at 1 core, while we still
|
||||
// want this test to pass, we need a smaller value since there is so much thrashing
|
||||
// we would also use a less aggressive setting when running in valgrind
|
||||
const puts_per_thread = 500;
|
||||
const put_thread_count = 3;
|
||||
|
||||
test "std.atomic.stack" {
|
||||
var direct_allocator = std.heap.DirectAllocator.init();
|
||||
defer direct_allocator.deinit();
|
||||
|
||||
var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
|
||||
defer direct_allocator.allocator.free(plenty_of_memory);
|
||||
|
||||
var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
|
||||
var a = &fixed_buffer_allocator.allocator;
|
||||
|
||||
var stack = Stack(i32).init();
|
||||
var context = Context {
|
||||
.allocator = a,
|
||||
.stack = &stack,
|
||||
.put_sum = 0,
|
||||
.get_sum = 0,
|
||||
.puts_done = 0,
|
||||
.get_count = 0,
|
||||
};
|
||||
|
||||
var putters: [put_thread_count]&std.os.Thread = undefined;
|
||||
for (putters) |*t| {
|
||||
*t = try std.os.spawnThread(&context, startPuts);
|
||||
}
|
||||
var getters: [put_thread_count]&std.os.Thread = undefined;
|
||||
for (getters) |*t| {
|
||||
*t = try std.os.spawnThread(&context, startGets);
|
||||
}
|
||||
|
||||
for (putters) |t| t.wait();
|
||||
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||
for (getters) |t| t.wait();
|
||||
|
||||
std.debug.assert(context.put_sum == context.get_sum);
|
||||
std.debug.assert(context.get_count == puts_per_thread * put_thread_count);
|
||||
}
|
||||
|
||||
fn startPuts(ctx: &Context) u8 {
|
||||
var put_count: usize = puts_per_thread;
|
||||
var r = std.rand.DefaultPrng.init(0xdeadbeef);
|
||||
while (put_count != 0) : (put_count -= 1) {
|
||||
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
|
||||
const x = @bitCast(i32, r.random.scalar(u32));
|
||||
const node = ctx.allocator.create(Stack(i32).Node) catch unreachable;
|
||||
node.data = x;
|
||||
ctx.stack.push(node);
|
||||
_ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn startGets(ctx: &Context) u8 {
|
||||
while (true) {
|
||||
while (ctx.stack.pop()) |node| {
|
||||
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
|
||||
_ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
|
||||
_ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
|
||||
}
|
||||
|
||||
if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -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.
|
||||
@ -16,10 +18,10 @@ pub const BufMap = struct {
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: &BufMap) void {
|
||||
pub fn deinit(self: &const BufMap) void {
|
||||
var it = self.hash_map.iterator();
|
||||
while (true) {
|
||||
const entry = it.next() ?? break;
|
||||
const entry = it.next() ?? break;
|
||||
self.free(entry.key);
|
||||
self.free(entry.value);
|
||||
}
|
||||
@ -28,21 +30,15 @@ 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 {
|
||||
pub fn get(self: &const BufMap, key: []const u8) ?[]const u8 {
|
||||
const entry = self.hash_map.get(key) ?? return null;
|
||||
return entry.value;
|
||||
}
|
||||
@ -54,20 +50,41 @@ pub const BufMap = struct {
|
||||
}
|
||||
|
||||
pub fn count(self: &const BufMap) usize {
|
||||
return self.hash_map.size;
|
||||
return self.hash_map.count();
|
||||
}
|
||||
|
||||
pub fn iterator(self: &const BufMap) BufMapHashMap.Iterator {
|
||||
return self.hash_map.iterator();
|
||||
}
|
||||
|
||||
fn free(self: &BufMap, value: []const u8) void {
|
||||
fn free(self: &const BufMap, value: []const u8) void {
|
||||
self.hash_map.allocator.free(value);
|
||||
}
|
||||
|
||||
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;
|
||||
fn copy(self: &const BufMap, value: []const u8) ![]const u8 {
|
||||
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());
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
const std = @import("index.zig");
|
||||
const HashMap = @import("hash_map.zig").HashMap;
|
||||
const mem = @import("mem.zig");
|
||||
const Allocator = mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
pub const BufSet = struct {
|
||||
hash_map: BufSetHashMap,
|
||||
@ -14,10 +16,10 @@ pub const BufSet = struct {
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: &BufSet) void {
|
||||
pub fn deinit(self: &const BufSet) void {
|
||||
var it = self.hash_map.iterator();
|
||||
while (true) {
|
||||
const entry = it.next() ?? break;
|
||||
const entry = it.next() ?? break;
|
||||
self.free(entry.key);
|
||||
}
|
||||
|
||||
@ -38,7 +40,7 @@ pub const BufSet = struct {
|
||||
}
|
||||
|
||||
pub fn count(self: &const BufSet) usize {
|
||||
return self.hash_map.size;
|
||||
return self.hash_map.count();
|
||||
}
|
||||
|
||||
pub fn iterator(self: &const BufSet) BufSetHashMap.Iterator {
|
||||
@ -49,14 +51,30 @@ pub const BufSet = struct {
|
||||
return self.hash_map.allocator;
|
||||
}
|
||||
|
||||
fn free(self: &BufSet, value: []const u8) void {
|
||||
fn free(self: &const BufSet, value: []const u8) void {
|
||||
self.hash_map.allocator.free(value);
|
||||
}
|
||||
|
||||
fn copy(self: &BufSet, value: []const u8) ![]const u8 {
|
||||
fn copy(self: &const BufSet, value: []const u8) ![]const u8 {
|
||||
const result = try self.hash_map.allocator.alloc(u8, value.len);
|
||||
mem.copy(u8, result, value);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
test "BufSet" {
|
||||
var direct_allocator = std.heap.DirectAllocator.init();
|
||||
defer direct_allocator.deinit();
|
||||
|
||||
var bufset = BufSet.init(&direct_allocator.allocator);
|
||||
defer bufset.deinit();
|
||||
|
||||
try bufset.put("x");
|
||||
assert(bufset.count() == 1);
|
||||
bufset.delete("x");
|
||||
assert(bufset.count() == 0);
|
||||
|
||||
try bufset.put("x");
|
||||
try bufset.put("y");
|
||||
try bufset.put("z");
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ pub const Buffer = struct {
|
||||
self.list.deinit();
|
||||
}
|
||||
|
||||
pub fn toSlice(self: &Buffer) []u8 {
|
||||
pub fn toSlice(self: &const Buffer) []u8 {
|
||||
return self.list.toSlice()[0..self.len()];
|
||||
}
|
||||
|
||||
@ -99,26 +99,10 @@ pub const Buffer = struct {
|
||||
mem.copy(u8, self.list.toSlice()[old_len..], m);
|
||||
}
|
||||
|
||||
// TODO: remove, use OutStream for this
|
||||
pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) !void {
|
||||
return fmt.format(self, append, format, args);
|
||||
}
|
||||
|
||||
// TODO: remove, use OutStream for this
|
||||
pub fn appendByte(self: &Buffer, byte: u8) !void {
|
||||
return self.appendByteNTimes(byte, 1);
|
||||
}
|
||||
|
||||
// TODO: remove, use OutStream for this
|
||||
pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) !void {
|
||||
var prev_size: usize = self.len();
|
||||
const new_size = prev_size + count;
|
||||
try self.resize(new_size);
|
||||
|
||||
var i: usize = prev_size;
|
||||
while (i < new_size) : (i += 1) {
|
||||
self.list.items[i] = byte;
|
||||
}
|
||||
const old_len = self.len();
|
||||
try self.resize(old_len + 1);
|
||||
self.list.toSlice()[old_len] = byte;
|
||||
}
|
||||
|
||||
pub fn eql(self: &const Buffer, m: []const u8) bool {
|
||||
@ -154,7 +138,7 @@ test "simple Buffer" {
|
||||
var buf = try Buffer.init(debug.global_allocator, "");
|
||||
assert(buf.len() == 0);
|
||||
try buf.append("hello");
|
||||
try buf.appendByte(' ');
|
||||
try buf.append(" ");
|
||||
try buf.append("world");
|
||||
assert(buf.eql("hello world"));
|
||||
assert(mem.eql(u8, cstr.toSliceConst(buf.toSliceConst().ptr), buf.toSliceConst()));
|
||||
@ -166,5 +150,5 @@ test "simple Buffer" {
|
||||
assert(buf.endsWith("orld"));
|
||||
|
||||
try buf2.resize(4);
|
||||
assert(buf.startsWith(buf2.toSliceConst()));
|
||||
assert(buf.startsWith(buf2.toSlice()));
|
||||
}
|
||||
|
||||
@ -426,15 +426,18 @@ pub const Builder = struct {
|
||||
|
||||
const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false;
|
||||
const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false;
|
||||
const release_small = self.option(bool, "release-small", "size optimizations on and safety off") ?? false;
|
||||
|
||||
const mode = if (release_safe and !release_fast)
|
||||
const mode = if (release_safe and !release_fast and !release_small)
|
||||
builtin.Mode.ReleaseSafe
|
||||
else if (release_fast and !release_safe)
|
||||
else if (release_fast and !release_safe and !release_small)
|
||||
builtin.Mode.ReleaseFast
|
||||
else if (!release_fast and !release_safe)
|
||||
else if (release_small and !release_fast and !release_safe)
|
||||
builtin.Mode.ReleaseSmall
|
||||
else if (!release_fast and !release_safe and !release_small)
|
||||
builtin.Mode.Debug
|
||||
else x: {
|
||||
warn("Both -Drelease-safe and -Drelease-fast specified");
|
||||
warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)");
|
||||
self.markInvalidUserInput();
|
||||
break :x builtin.Mode.Debug;
|
||||
};
|
||||
@ -1229,6 +1232,7 @@ pub const LibExeObjStep = struct {
|
||||
builtin.Mode.Debug => {},
|
||||
builtin.Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,
|
||||
builtin.Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable,
|
||||
builtin.Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable,
|
||||
}
|
||||
|
||||
zig_args.append("--cache-dir") catch unreachable;
|
||||
@ -1369,7 +1373,7 @@ pub const LibExeObjStep = struct {
|
||||
args.append("ssp-buffer-size=4") catch unreachable;
|
||||
}
|
||||
},
|
||||
builtin.Mode.ReleaseFast => {
|
||||
builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {
|
||||
args.append("-O2") catch unreachable;
|
||||
args.append("-fno-stack-protector") catch unreachable;
|
||||
},
|
||||
@ -1706,6 +1710,7 @@ pub const TestStep = struct {
|
||||
builtin.Mode.Debug => {},
|
||||
builtin.Mode.ReleaseSafe => try zig_args.append("--release-safe"),
|
||||
builtin.Mode.ReleaseFast => try zig_args.append("--release-fast"),
|
||||
builtin.Mode.ReleaseSmall => try zig_args.append("--release-small"),
|
||||
}
|
||||
|
||||
switch (self.target) {
|
||||
|
||||
@ -3,10 +3,28 @@ 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 extern "c" fn mach_absolute_time() u64;
|
||||
pub extern "c" fn mach_timebase_info(tinfo: ?&mach_timebase_info_data) void;
|
||||
|
||||
pub use @import("../os/darwin_errno.zig");
|
||||
|
||||
pub const _errno = __error;
|
||||
|
||||
pub const timeval = extern struct {
|
||||
tv_sec: isize,
|
||||
tv_usec: isize,
|
||||
};
|
||||
|
||||
pub const timezone = extern struct {
|
||||
tz_minuteswest: i32,
|
||||
tz_dsttime: i32,
|
||||
};
|
||||
|
||||
pub const mach_timebase_info_data = struct {
|
||||
numer: u32,
|
||||
denom: u32,
|
||||
};
|
||||
|
||||
/// Renamed to Stat to not conflict with the stat function.
|
||||
pub const Stat = extern struct {
|
||||
dev: i32,
|
||||
@ -55,3 +73,16 @@ pub const dirent = extern struct {
|
||||
d_type: u8,
|
||||
d_name: u8, // field address is address of first byte of name
|
||||
};
|
||||
|
||||
pub const sockaddr = extern struct {
|
||||
sa_len: u8,
|
||||
sa_family: sa_family_t,
|
||||
sa_data: [14]u8,
|
||||
};
|
||||
|
||||
pub const sa_family_t = u8;
|
||||
|
||||
pub const pthread_attr_t = extern struct {
|
||||
__sig: c_long,
|
||||
__opaque: [56]u8,
|
||||
};
|
||||
|
||||
@ -28,6 +28,7 @@ pub extern "c" fn unlink(path: &const u8) c_int;
|
||||
pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8;
|
||||
pub extern "c" fn waitpid(pid: c_int, stat_loc: &c_int, options: c_int) c_int;
|
||||
pub extern "c" fn fork() c_int;
|
||||
pub extern "c" fn access(path: &const u8, mode: c_uint) c_int;
|
||||
pub extern "c" fn pipe(fds: &c_int) c_int;
|
||||
pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int;
|
||||
pub extern "c" fn symlink(existing: &const u8, new: &const u8) c_int;
|
||||
@ -40,6 +41,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int;
|
||||
pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize;
|
||||
pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8;
|
||||
pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int;
|
||||
pub extern "c" fn gettimeofday(tv: ?&timeval, tz: ?&timezone) c_int;
|
||||
pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int;
|
||||
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;
|
||||
@ -51,3 +53,13 @@ pub extern "c" fn malloc(usize) ?&c_void;
|
||||
pub extern "c" fn realloc(&c_void, usize) ?&c_void;
|
||||
pub extern "c" fn free(&c_void) void;
|
||||
pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int;
|
||||
|
||||
pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t,
|
||||
noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void,
|
||||
noalias arg: ?&c_void) c_int;
|
||||
pub extern "pthread" fn pthread_attr_init(attr: &pthread_attr_t) c_int;
|
||||
pub extern "pthread" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int;
|
||||
pub extern "pthread" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int;
|
||||
pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int;
|
||||
|
||||
pub const pthread_t = &@OpaqueType();
|
||||
|
||||
@ -3,3 +3,8 @@ pub use @import("../os/linux/errno.zig");
|
||||
pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int;
|
||||
extern "c" fn __errno_location() &c_int;
|
||||
pub const _errno = __errno_location;
|
||||
|
||||
pub const pthread_attr_t = extern struct {
|
||||
__size: [56]u8,
|
||||
__align: c_long,
|
||||
};
|
||||
|
||||
@ -1,22 +1,17 @@
|
||||
// Modify the HashFunction variable to the one wanted to test.
|
||||
//
|
||||
// NOTE: The throughput measurement may be slightly lower than other measurements since we run
|
||||
// through our block alignment functions as well. Be aware when comparing against other tests.
|
||||
//
|
||||
// ```
|
||||
// zig build-exe --release-fast --library c throughput_test.zig
|
||||
// zig build-exe --release-fast throughput_test.zig
|
||||
// ./throughput_test
|
||||
// ```
|
||||
const HashFunction = @import("md5.zig").Md5;
|
||||
const BytesToHash = 1024 * Mb;
|
||||
|
||||
const std = @import("std");
|
||||
const time = std.os.time;
|
||||
const Timer = time.Timer;
|
||||
const HashFunction = @import("md5.zig").Md5;
|
||||
|
||||
const c = @cImport({
|
||||
@cInclude("time.h");
|
||||
});
|
||||
|
||||
const Mb = 1024 * 1024;
|
||||
const MiB = 1024 * 1024;
|
||||
const BytesToHash = 1024 * MiB;
|
||||
|
||||
pub fn main() !void {
|
||||
var stdout_file = try std.io.getStdOut();
|
||||
@ -29,15 +24,15 @@ pub fn main() !void {
|
||||
var h = HashFunction.init();
|
||||
var offset: usize = 0;
|
||||
|
||||
const start = c.clock();
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (offset < BytesToHash) : (offset += block.len) {
|
||||
h.update(block[0..]);
|
||||
}
|
||||
const end = c.clock();
|
||||
const end = timer.read();
|
||||
|
||||
const elapsed_s = f64((end - start) * c.CLOCKS_PER_SEC) / 1000000;
|
||||
const elapsed_s = f64(end - start) / time.ns_per_s;
|
||||
const throughput = u64(BytesToHash / elapsed_s);
|
||||
|
||||
try stdout.print("{}: ", @typeName(HashFunction));
|
||||
try stdout.print("{} Mb/s\n", throughput);
|
||||
try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB));
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ pub fn getSelfDebugInfo() !&ElfStackTrace {
|
||||
if (self_debug_info) |info| {
|
||||
return info;
|
||||
} else {
|
||||
const info = try openSelfDebugInfo(global_allocator);
|
||||
const info = try openSelfDebugInfo(getDebugInfoAllocator());
|
||||
self_debug_info = info;
|
||||
return info;
|
||||
}
|
||||
@ -51,7 +51,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
|
||||
stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
|
||||
return;
|
||||
};
|
||||
writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), start_addr) catch |err| {
|
||||
writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty(), start_addr) catch |err| {
|
||||
stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
|
||||
return;
|
||||
};
|
||||
@ -64,7 +64,7 @@ pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void {
|
||||
stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
|
||||
return;
|
||||
};
|
||||
writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| {
|
||||
writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty()) catch |err| {
|
||||
stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
|
||||
return;
|
||||
};
|
||||
@ -592,8 +592,8 @@ fn getString(st: &ElfStackTrace, offset: u64) ![]u8 {
|
||||
}
|
||||
|
||||
fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 {
|
||||
const buf = try global_allocator.alloc(u8, size);
|
||||
errdefer global_allocator.free(buf);
|
||||
const buf = try allocator.alloc(u8, size);
|
||||
errdefer allocator.free(buf);
|
||||
if ((try in_stream.read(buf)) < size) return error.EndOfFile;
|
||||
return buf;
|
||||
}
|
||||
@ -1126,6 +1126,21 @@ fn readILeb128(in_stream: var) !i64 {
|
||||
}
|
||||
}
|
||||
|
||||
/// This should only be used in temporary test programs.
|
||||
pub const global_allocator = &global_fixed_allocator.allocator;
|
||||
var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]);
|
||||
var global_allocator_mem: [100 * 1024]u8 = undefined;
|
||||
|
||||
|
||||
// TODO make thread safe
|
||||
var debug_info_allocator: ?&mem.Allocator = null;
|
||||
var debug_info_direct_allocator: std.heap.DirectAllocator = undefined;
|
||||
var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
|
||||
fn getDebugInfoAllocator() &mem.Allocator {
|
||||
if (debug_info_allocator) |a| return a;
|
||||
|
||||
debug_info_direct_allocator = std.heap.DirectAllocator.init();
|
||||
debug_info_arena_allocator = std.heap.ArenaAllocator.init(&debug_info_direct_allocator.allocator);
|
||||
debug_info_allocator = &debug_info_arena_allocator.allocator;
|
||||
return &debug_info_arena_allocator.allocator;
|
||||
}
|
||||
|
||||
611
std/elf.zig
611
std/elf.zig
@ -7,6 +7,246 @@ const mem = std.mem;
|
||||
const debug = std.debug;
|
||||
const InStream = std.stream.InStream;
|
||||
|
||||
pub const AT_NULL = 0;
|
||||
pub const AT_IGNORE = 1;
|
||||
pub const AT_EXECFD = 2;
|
||||
pub const AT_PHDR = 3;
|
||||
pub const AT_PHENT = 4;
|
||||
pub const AT_PHNUM = 5;
|
||||
pub const AT_PAGESZ = 6;
|
||||
pub const AT_BASE = 7;
|
||||
pub const AT_FLAGS = 8;
|
||||
pub const AT_ENTRY = 9;
|
||||
pub const AT_NOTELF = 10;
|
||||
pub const AT_UID = 11;
|
||||
pub const AT_EUID = 12;
|
||||
pub const AT_GID = 13;
|
||||
pub const AT_EGID = 14;
|
||||
pub const AT_CLKTCK = 17;
|
||||
pub const AT_PLATFORM = 15;
|
||||
pub const AT_HWCAP = 16;
|
||||
pub const AT_FPUCW = 18;
|
||||
pub const AT_DCACHEBSIZE = 19;
|
||||
pub const AT_ICACHEBSIZE = 20;
|
||||
pub const AT_UCACHEBSIZE = 21;
|
||||
pub const AT_IGNOREPPC = 22;
|
||||
pub const AT_SECURE = 23;
|
||||
pub const AT_BASE_PLATFORM = 24;
|
||||
pub const AT_RANDOM = 25;
|
||||
pub const AT_HWCAP2 = 26;
|
||||
pub const AT_EXECFN = 31;
|
||||
pub const AT_SYSINFO = 32;
|
||||
pub const AT_SYSINFO_EHDR = 33;
|
||||
pub const AT_L1I_CACHESHAPE = 34;
|
||||
pub const AT_L1D_CACHESHAPE = 35;
|
||||
pub const AT_L2_CACHESHAPE = 36;
|
||||
pub const AT_L3_CACHESHAPE = 37;
|
||||
pub const AT_L1I_CACHESIZE = 40;
|
||||
pub const AT_L1I_CACHEGEOMETRY = 41;
|
||||
pub const AT_L1D_CACHESIZE = 42;
|
||||
pub const AT_L1D_CACHEGEOMETRY = 43;
|
||||
pub const AT_L2_CACHESIZE = 44;
|
||||
pub const AT_L2_CACHEGEOMETRY = 45;
|
||||
pub const AT_L3_CACHESIZE = 46;
|
||||
pub const AT_L3_CACHEGEOMETRY = 47;
|
||||
|
||||
pub const DT_NULL = 0;
|
||||
pub const DT_NEEDED = 1;
|
||||
pub const DT_PLTRELSZ = 2;
|
||||
pub const DT_PLTGOT = 3;
|
||||
pub const DT_HASH = 4;
|
||||
pub const DT_STRTAB = 5;
|
||||
pub const DT_SYMTAB = 6;
|
||||
pub const DT_RELA = 7;
|
||||
pub const DT_RELASZ = 8;
|
||||
pub const DT_RELAENT = 9;
|
||||
pub const DT_STRSZ = 10;
|
||||
pub const DT_SYMENT = 11;
|
||||
pub const DT_INIT = 12;
|
||||
pub const DT_FINI = 13;
|
||||
pub const DT_SONAME = 14;
|
||||
pub const DT_RPATH = 15;
|
||||
pub const DT_SYMBOLIC = 16;
|
||||
pub const DT_REL = 17;
|
||||
pub const DT_RELSZ = 18;
|
||||
pub const DT_RELENT = 19;
|
||||
pub const DT_PLTREL = 20;
|
||||
pub const DT_DEBUG = 21;
|
||||
pub const DT_TEXTREL = 22;
|
||||
pub const DT_JMPREL = 23;
|
||||
pub const DT_BIND_NOW = 24;
|
||||
pub const DT_INIT_ARRAY = 25;
|
||||
pub const DT_FINI_ARRAY = 26;
|
||||
pub const DT_INIT_ARRAYSZ = 27;
|
||||
pub const DT_FINI_ARRAYSZ = 28;
|
||||
pub const DT_RUNPATH = 29;
|
||||
pub const DT_FLAGS = 30;
|
||||
pub const DT_ENCODING = 32;
|
||||
pub const DT_PREINIT_ARRAY = 32;
|
||||
pub const DT_PREINIT_ARRAYSZ = 33;
|
||||
pub const DT_SYMTAB_SHNDX = 34;
|
||||
pub const DT_NUM = 35;
|
||||
pub const DT_LOOS = 0x6000000d;
|
||||
pub const DT_HIOS = 0x6ffff000;
|
||||
pub const DT_LOPROC = 0x70000000;
|
||||
pub const DT_HIPROC = 0x7fffffff;
|
||||
pub const DT_PROCNUM = DT_MIPS_NUM;
|
||||
|
||||
pub const DT_VALRNGLO = 0x6ffffd00;
|
||||
pub const DT_GNU_PRELINKED = 0x6ffffdf5;
|
||||
pub const DT_GNU_CONFLICTSZ = 0x6ffffdf6;
|
||||
pub const DT_GNU_LIBLISTSZ = 0x6ffffdf7;
|
||||
pub const DT_CHECKSUM = 0x6ffffdf8;
|
||||
pub const DT_PLTPADSZ = 0x6ffffdf9;
|
||||
pub const DT_MOVEENT = 0x6ffffdfa;
|
||||
pub const DT_MOVESZ = 0x6ffffdfb;
|
||||
pub const DT_FEATURE_1 = 0x6ffffdfc;
|
||||
pub const DT_POSFLAG_1 = 0x6ffffdfd;
|
||||
|
||||
pub const DT_SYMINSZ = 0x6ffffdfe;
|
||||
pub const DT_SYMINENT = 0x6ffffdff;
|
||||
pub const DT_VALRNGHI = 0x6ffffdff;
|
||||
pub const DT_VALNUM = 12;
|
||||
|
||||
pub const DT_ADDRRNGLO = 0x6ffffe00;
|
||||
pub const DT_GNU_HASH = 0x6ffffef5;
|
||||
pub const DT_TLSDESC_PLT = 0x6ffffef6;
|
||||
pub const DT_TLSDESC_GOT = 0x6ffffef7;
|
||||
pub const DT_GNU_CONFLICT = 0x6ffffef8;
|
||||
pub const DT_GNU_LIBLIST = 0x6ffffef9;
|
||||
pub const DT_CONFIG = 0x6ffffefa;
|
||||
pub const DT_DEPAUDIT = 0x6ffffefb;
|
||||
pub const DT_AUDIT = 0x6ffffefc;
|
||||
pub const DT_PLTPAD = 0x6ffffefd;
|
||||
pub const DT_MOVETAB = 0x6ffffefe;
|
||||
pub const DT_SYMINFO = 0x6ffffeff;
|
||||
pub const DT_ADDRRNGHI = 0x6ffffeff;
|
||||
pub const DT_ADDRNUM = 11;
|
||||
|
||||
|
||||
pub const DT_VERSYM = 0x6ffffff0;
|
||||
|
||||
pub const DT_RELACOUNT = 0x6ffffff9;
|
||||
pub const DT_RELCOUNT = 0x6ffffffa;
|
||||
|
||||
|
||||
pub const DT_FLAGS_1 = 0x6ffffffb;
|
||||
pub const DT_VERDEF = 0x6ffffffc;
|
||||
|
||||
pub const DT_VERDEFNUM = 0x6ffffffd;
|
||||
pub const DT_VERNEED = 0x6ffffffe;
|
||||
|
||||
pub const DT_VERNEEDNUM = 0x6fffffff;
|
||||
pub const DT_VERSIONTAGNUM = 16;
|
||||
|
||||
|
||||
|
||||
pub const DT_AUXILIARY = 0x7ffffffd;
|
||||
pub const DT_FILTER = 0x7fffffff;
|
||||
pub const DT_EXTRANUM = 3;
|
||||
|
||||
|
||||
pub const DT_SPARC_REGISTER = 0x70000001;
|
||||
pub const DT_SPARC_NUM = 2;
|
||||
|
||||
pub const DT_MIPS_RLD_VERSION = 0x70000001;
|
||||
pub const DT_MIPS_TIME_STAMP = 0x70000002;
|
||||
pub const DT_MIPS_ICHECKSUM = 0x70000003;
|
||||
pub const DT_MIPS_IVERSION = 0x70000004;
|
||||
pub const DT_MIPS_FLAGS = 0x70000005;
|
||||
pub const DT_MIPS_BASE_ADDRESS = 0x70000006;
|
||||
pub const DT_MIPS_MSYM = 0x70000007;
|
||||
pub const DT_MIPS_CONFLICT = 0x70000008;
|
||||
pub const DT_MIPS_LIBLIST = 0x70000009;
|
||||
pub const DT_MIPS_LOCAL_GOTNO = 0x7000000a;
|
||||
pub const DT_MIPS_CONFLICTNO = 0x7000000b;
|
||||
pub const DT_MIPS_LIBLISTNO = 0x70000010;
|
||||
pub const DT_MIPS_SYMTABNO = 0x70000011;
|
||||
pub const DT_MIPS_UNREFEXTNO = 0x70000012;
|
||||
pub const DT_MIPS_GOTSYM = 0x70000013;
|
||||
pub const DT_MIPS_HIPAGENO = 0x70000014;
|
||||
pub const DT_MIPS_RLD_MAP = 0x70000016;
|
||||
pub const DT_MIPS_DELTA_CLASS = 0x70000017;
|
||||
pub const DT_MIPS_DELTA_CLASS_NO = 0x70000018;
|
||||
|
||||
pub const DT_MIPS_DELTA_INSTANCE = 0x70000019;
|
||||
pub const DT_MIPS_DELTA_INSTANCE_NO = 0x7000001a;
|
||||
|
||||
pub const DT_MIPS_DELTA_RELOC = 0x7000001b;
|
||||
pub const DT_MIPS_DELTA_RELOC_NO = 0x7000001c;
|
||||
|
||||
pub const DT_MIPS_DELTA_SYM = 0x7000001d;
|
||||
|
||||
pub const DT_MIPS_DELTA_SYM_NO = 0x7000001e;
|
||||
|
||||
pub const DT_MIPS_DELTA_CLASSSYM = 0x70000020;
|
||||
|
||||
pub const DT_MIPS_DELTA_CLASSSYM_NO = 0x70000021;
|
||||
|
||||
pub const DT_MIPS_CXX_FLAGS = 0x70000022;
|
||||
pub const DT_MIPS_PIXIE_INIT = 0x70000023;
|
||||
pub const DT_MIPS_SYMBOL_LIB = 0x70000024;
|
||||
pub const DT_MIPS_LOCALPAGE_GOTIDX = 0x70000025;
|
||||
pub const DT_MIPS_LOCAL_GOTIDX = 0x70000026;
|
||||
pub const DT_MIPS_HIDDEN_GOTIDX = 0x70000027;
|
||||
pub const DT_MIPS_PROTECTED_GOTIDX = 0x70000028;
|
||||
pub const DT_MIPS_OPTIONS = 0x70000029;
|
||||
pub const DT_MIPS_INTERFACE = 0x7000002a;
|
||||
pub const DT_MIPS_DYNSTR_ALIGN = 0x7000002b;
|
||||
pub const DT_MIPS_INTERFACE_SIZE = 0x7000002c;
|
||||
pub const DT_MIPS_RLD_TEXT_RESOLVE_ADDR = 0x7000002d;
|
||||
|
||||
pub const DT_MIPS_PERF_SUFFIX = 0x7000002e;
|
||||
|
||||
pub const DT_MIPS_COMPACT_SIZE = 0x7000002f;
|
||||
pub const DT_MIPS_GP_VALUE = 0x70000030;
|
||||
pub const DT_MIPS_AUX_DYNAMIC = 0x70000031;
|
||||
|
||||
pub const DT_MIPS_PLTGOT = 0x70000032;
|
||||
|
||||
pub const DT_MIPS_RWPLT = 0x70000034;
|
||||
pub const DT_MIPS_RLD_MAP_REL = 0x70000035;
|
||||
pub const DT_MIPS_NUM = 0x36;
|
||||
|
||||
pub const DT_ALPHA_PLTRO = (DT_LOPROC + 0);
|
||||
pub const DT_ALPHA_NUM = 1;
|
||||
|
||||
pub const DT_PPC_GOT = (DT_LOPROC + 0);
|
||||
pub const DT_PPC_OPT = (DT_LOPROC + 1);
|
||||
pub const DT_PPC_NUM = 2;
|
||||
|
||||
pub const DT_PPC64_GLINK = (DT_LOPROC + 0);
|
||||
pub const DT_PPC64_OPD = (DT_LOPROC + 1);
|
||||
pub const DT_PPC64_OPDSZ = (DT_LOPROC + 2);
|
||||
pub const DT_PPC64_OPT = (DT_LOPROC + 3);
|
||||
pub const DT_PPC64_NUM = 4;
|
||||
|
||||
pub const DT_IA_64_PLT_RESERVE = (DT_LOPROC + 0);
|
||||
pub const DT_IA_64_NUM = 1;
|
||||
|
||||
pub const DT_NIOS2_GP = 0x70000002;
|
||||
|
||||
pub const PT_NULL = 0;
|
||||
pub const PT_LOAD = 1;
|
||||
pub const PT_DYNAMIC = 2;
|
||||
pub const PT_INTERP = 3;
|
||||
pub const PT_NOTE = 4;
|
||||
pub const PT_SHLIB = 5;
|
||||
pub const PT_PHDR = 6;
|
||||
pub const PT_TLS = 7;
|
||||
pub const PT_NUM = 8;
|
||||
pub const PT_LOOS = 0x60000000;
|
||||
pub const PT_GNU_EH_FRAME = 0x6474e550;
|
||||
pub const PT_GNU_STACK = 0x6474e551;
|
||||
pub const PT_GNU_RELRO = 0x6474e552;
|
||||
pub const PT_LOSUNW = 0x6ffffffa;
|
||||
pub const PT_SUNWBSS = 0x6ffffffa;
|
||||
pub const PT_SUNWSTACK = 0x6ffffffb;
|
||||
pub const PT_HISUNW = 0x6fffffff;
|
||||
pub const PT_HIOS = 0x6fffffff;
|
||||
pub const PT_LOPROC = 0x70000000;
|
||||
pub const PT_HIPROC = 0x7fffffff;
|
||||
|
||||
pub const SHT_NULL = 0;
|
||||
pub const SHT_PROGBITS = 1;
|
||||
pub const SHT_SYMTAB = 2;
|
||||
@ -31,6 +271,45 @@ pub const SHT_HIPROC = 0x7fffffff;
|
||||
pub const SHT_LOUSER = 0x80000000;
|
||||
pub const SHT_HIUSER = 0xffffffff;
|
||||
|
||||
pub const STB_LOCAL = 0;
|
||||
pub const STB_GLOBAL = 1;
|
||||
pub const STB_WEAK = 2;
|
||||
pub const STB_NUM = 3;
|
||||
pub const STB_LOOS = 10;
|
||||
pub const STB_GNU_UNIQUE = 10;
|
||||
pub const STB_HIOS = 12;
|
||||
pub const STB_LOPROC = 13;
|
||||
pub const STB_HIPROC = 15;
|
||||
|
||||
pub const STB_MIPS_SPLIT_COMMON = 13;
|
||||
|
||||
pub const STT_NOTYPE = 0;
|
||||
pub const STT_OBJECT = 1;
|
||||
pub const STT_FUNC = 2;
|
||||
pub const STT_SECTION = 3;
|
||||
pub const STT_FILE = 4;
|
||||
pub const STT_COMMON = 5;
|
||||
pub const STT_TLS = 6;
|
||||
pub const STT_NUM = 7;
|
||||
pub const STT_LOOS = 10;
|
||||
pub const STT_GNU_IFUNC = 10;
|
||||
pub const STT_HIOS = 12;
|
||||
pub const STT_LOPROC = 13;
|
||||
pub const STT_HIPROC = 15;
|
||||
|
||||
pub const STT_SPARC_REGISTER = 13;
|
||||
|
||||
pub const STT_PARISC_MILLICODE = 13;
|
||||
|
||||
pub const STT_HP_OPAQUE = (STT_LOOS + 0x1);
|
||||
pub const STT_HP_STUB = (STT_LOOS + 0x2);
|
||||
|
||||
pub const STT_ARM_TFUNC = STT_LOPROC;
|
||||
pub const STT_ARM_16BIT = STT_HIPROC;
|
||||
|
||||
pub const VER_FLG_BASE = 0x1;
|
||||
pub const VER_FLG_WEAK = 0x2;
|
||||
|
||||
pub const FileType = enum {
|
||||
Relocatable,
|
||||
Executable,
|
||||
@ -266,3 +545,335 @@ pub const Elf = struct {
|
||||
try elf.in_file.seekTo(elf_section.offset);
|
||||
}
|
||||
};
|
||||
|
||||
pub const EI_NIDENT = 16;
|
||||
pub const Elf32_Half = u16;
|
||||
pub const Elf64_Half = u16;
|
||||
pub const Elf32_Word = u32;
|
||||
pub const Elf32_Sword = i32;
|
||||
pub const Elf64_Word = u32;
|
||||
pub const Elf64_Sword = i32;
|
||||
pub const Elf32_Xword = u64;
|
||||
pub const Elf32_Sxword = i64;
|
||||
pub const Elf64_Xword = u64;
|
||||
pub const Elf64_Sxword = i64;
|
||||
pub const Elf32_Addr = u32;
|
||||
pub const Elf64_Addr = u64;
|
||||
pub const Elf32_Off = u32;
|
||||
pub const Elf64_Off = u64;
|
||||
pub const Elf32_Section = u16;
|
||||
pub const Elf64_Section = u16;
|
||||
pub const Elf32_Versym = Elf32_Half;
|
||||
pub const Elf64_Versym = Elf64_Half;
|
||||
pub const Elf32_Ehdr = extern struct {
|
||||
e_ident: [EI_NIDENT]u8,
|
||||
e_type: Elf32_Half,
|
||||
e_machine: Elf32_Half,
|
||||
e_version: Elf32_Word,
|
||||
e_entry: Elf32_Addr,
|
||||
e_phoff: Elf32_Off,
|
||||
e_shoff: Elf32_Off,
|
||||
e_flags: Elf32_Word,
|
||||
e_ehsize: Elf32_Half,
|
||||
e_phentsize: Elf32_Half,
|
||||
e_phnum: Elf32_Half,
|
||||
e_shentsize: Elf32_Half,
|
||||
e_shnum: Elf32_Half,
|
||||
e_shstrndx: Elf32_Half,
|
||||
};
|
||||
pub const Elf64_Ehdr = extern struct {
|
||||
e_ident: [EI_NIDENT]u8,
|
||||
e_type: Elf64_Half,
|
||||
e_machine: Elf64_Half,
|
||||
e_version: Elf64_Word,
|
||||
e_entry: Elf64_Addr,
|
||||
e_phoff: Elf64_Off,
|
||||
e_shoff: Elf64_Off,
|
||||
e_flags: Elf64_Word,
|
||||
e_ehsize: Elf64_Half,
|
||||
e_phentsize: Elf64_Half,
|
||||
e_phnum: Elf64_Half,
|
||||
e_shentsize: Elf64_Half,
|
||||
e_shnum: Elf64_Half,
|
||||
e_shstrndx: Elf64_Half,
|
||||
};
|
||||
pub const Elf32_Shdr = extern struct {
|
||||
sh_name: Elf32_Word,
|
||||
sh_type: Elf32_Word,
|
||||
sh_flags: Elf32_Word,
|
||||
sh_addr: Elf32_Addr,
|
||||
sh_offset: Elf32_Off,
|
||||
sh_size: Elf32_Word,
|
||||
sh_link: Elf32_Word,
|
||||
sh_info: Elf32_Word,
|
||||
sh_addralign: Elf32_Word,
|
||||
sh_entsize: Elf32_Word,
|
||||
};
|
||||
pub const Elf64_Shdr = extern struct {
|
||||
sh_name: Elf64_Word,
|
||||
sh_type: Elf64_Word,
|
||||
sh_flags: Elf64_Xword,
|
||||
sh_addr: Elf64_Addr,
|
||||
sh_offset: Elf64_Off,
|
||||
sh_size: Elf64_Xword,
|
||||
sh_link: Elf64_Word,
|
||||
sh_info: Elf64_Word,
|
||||
sh_addralign: Elf64_Xword,
|
||||
sh_entsize: Elf64_Xword,
|
||||
};
|
||||
pub const Elf32_Chdr = extern struct {
|
||||
ch_type: Elf32_Word,
|
||||
ch_size: Elf32_Word,
|
||||
ch_addralign: Elf32_Word,
|
||||
};
|
||||
pub const Elf64_Chdr = extern struct {
|
||||
ch_type: Elf64_Word,
|
||||
ch_reserved: Elf64_Word,
|
||||
ch_size: Elf64_Xword,
|
||||
ch_addralign: Elf64_Xword,
|
||||
};
|
||||
pub const Elf32_Sym = extern struct {
|
||||
st_name: Elf32_Word,
|
||||
st_value: Elf32_Addr,
|
||||
st_size: Elf32_Word,
|
||||
st_info: u8,
|
||||
st_other: u8,
|
||||
st_shndx: Elf32_Section,
|
||||
};
|
||||
pub const Elf64_Sym = extern struct {
|
||||
st_name: Elf64_Word,
|
||||
st_info: u8,
|
||||
st_other: u8,
|
||||
st_shndx: Elf64_Section,
|
||||
st_value: Elf64_Addr,
|
||||
st_size: Elf64_Xword,
|
||||
};
|
||||
pub const Elf32_Syminfo = extern struct {
|
||||
si_boundto: Elf32_Half,
|
||||
si_flags: Elf32_Half,
|
||||
};
|
||||
pub const Elf64_Syminfo = extern struct {
|
||||
si_boundto: Elf64_Half,
|
||||
si_flags: Elf64_Half,
|
||||
};
|
||||
pub const Elf32_Rel = extern struct {
|
||||
r_offset: Elf32_Addr,
|
||||
r_info: Elf32_Word,
|
||||
};
|
||||
pub const Elf64_Rel = extern struct {
|
||||
r_offset: Elf64_Addr,
|
||||
r_info: Elf64_Xword,
|
||||
};
|
||||
pub const Elf32_Rela = extern struct {
|
||||
r_offset: Elf32_Addr,
|
||||
r_info: Elf32_Word,
|
||||
r_addend: Elf32_Sword,
|
||||
};
|
||||
pub const Elf64_Rela = extern struct {
|
||||
r_offset: Elf64_Addr,
|
||||
r_info: Elf64_Xword,
|
||||
r_addend: Elf64_Sxword,
|
||||
};
|
||||
pub const Elf32_Phdr = extern struct {
|
||||
p_type: Elf32_Word,
|
||||
p_offset: Elf32_Off,
|
||||
p_vaddr: Elf32_Addr,
|
||||
p_paddr: Elf32_Addr,
|
||||
p_filesz: Elf32_Word,
|
||||
p_memsz: Elf32_Word,
|
||||
p_flags: Elf32_Word,
|
||||
p_align: Elf32_Word,
|
||||
};
|
||||
pub const Elf64_Phdr = extern struct {
|
||||
p_type: Elf64_Word,
|
||||
p_flags: Elf64_Word,
|
||||
p_offset: Elf64_Off,
|
||||
p_vaddr: Elf64_Addr,
|
||||
p_paddr: Elf64_Addr,
|
||||
p_filesz: Elf64_Xword,
|
||||
p_memsz: Elf64_Xword,
|
||||
p_align: Elf64_Xword,
|
||||
};
|
||||
pub const Elf32_Dyn = extern struct {
|
||||
d_tag: Elf32_Sword,
|
||||
d_un: extern union {
|
||||
d_val: Elf32_Word,
|
||||
d_ptr: Elf32_Addr,
|
||||
},
|
||||
};
|
||||
pub const Elf64_Dyn = extern struct {
|
||||
d_tag: Elf64_Sxword,
|
||||
d_un: extern union {
|
||||
d_val: Elf64_Xword,
|
||||
d_ptr: Elf64_Addr,
|
||||
},
|
||||
};
|
||||
pub const Elf32_Verdef = extern struct {
|
||||
vd_version: Elf32_Half,
|
||||
vd_flags: Elf32_Half,
|
||||
vd_ndx: Elf32_Half,
|
||||
vd_cnt: Elf32_Half,
|
||||
vd_hash: Elf32_Word,
|
||||
vd_aux: Elf32_Word,
|
||||
vd_next: Elf32_Word,
|
||||
};
|
||||
pub const Elf64_Verdef = extern struct {
|
||||
vd_version: Elf64_Half,
|
||||
vd_flags: Elf64_Half,
|
||||
vd_ndx: Elf64_Half,
|
||||
vd_cnt: Elf64_Half,
|
||||
vd_hash: Elf64_Word,
|
||||
vd_aux: Elf64_Word,
|
||||
vd_next: Elf64_Word,
|
||||
};
|
||||
pub const Elf32_Verdaux = extern struct {
|
||||
vda_name: Elf32_Word,
|
||||
vda_next: Elf32_Word,
|
||||
};
|
||||
pub const Elf64_Verdaux = extern struct {
|
||||
vda_name: Elf64_Word,
|
||||
vda_next: Elf64_Word,
|
||||
};
|
||||
pub const Elf32_Verneed = extern struct {
|
||||
vn_version: Elf32_Half,
|
||||
vn_cnt: Elf32_Half,
|
||||
vn_file: Elf32_Word,
|
||||
vn_aux: Elf32_Word,
|
||||
vn_next: Elf32_Word,
|
||||
};
|
||||
pub const Elf64_Verneed = extern struct {
|
||||
vn_version: Elf64_Half,
|
||||
vn_cnt: Elf64_Half,
|
||||
vn_file: Elf64_Word,
|
||||
vn_aux: Elf64_Word,
|
||||
vn_next: Elf64_Word,
|
||||
};
|
||||
pub const Elf32_Vernaux = extern struct {
|
||||
vna_hash: Elf32_Word,
|
||||
vna_flags: Elf32_Half,
|
||||
vna_other: Elf32_Half,
|
||||
vna_name: Elf32_Word,
|
||||
vna_next: Elf32_Word,
|
||||
};
|
||||
pub const Elf64_Vernaux = extern struct {
|
||||
vna_hash: Elf64_Word,
|
||||
vna_flags: Elf64_Half,
|
||||
vna_other: Elf64_Half,
|
||||
vna_name: Elf64_Word,
|
||||
vna_next: Elf64_Word,
|
||||
};
|
||||
pub const Elf32_auxv_t = extern struct {
|
||||
a_type: u32,
|
||||
a_un: extern union {
|
||||
a_val: u32,
|
||||
},
|
||||
};
|
||||
pub const Elf64_auxv_t = extern struct {
|
||||
a_type: u64,
|
||||
a_un: extern union {
|
||||
a_val: u64,
|
||||
},
|
||||
};
|
||||
pub const Elf32_Nhdr = extern struct {
|
||||
n_namesz: Elf32_Word,
|
||||
n_descsz: Elf32_Word,
|
||||
n_type: Elf32_Word,
|
||||
};
|
||||
pub const Elf64_Nhdr = extern struct {
|
||||
n_namesz: Elf64_Word,
|
||||
n_descsz: Elf64_Word,
|
||||
n_type: Elf64_Word,
|
||||
};
|
||||
pub const Elf32_Move = extern struct {
|
||||
m_value: Elf32_Xword,
|
||||
m_info: Elf32_Word,
|
||||
m_poffset: Elf32_Word,
|
||||
m_repeat: Elf32_Half,
|
||||
m_stride: Elf32_Half,
|
||||
};
|
||||
pub const Elf64_Move = extern struct {
|
||||
m_value: Elf64_Xword,
|
||||
m_info: Elf64_Xword,
|
||||
m_poffset: Elf64_Xword,
|
||||
m_repeat: Elf64_Half,
|
||||
m_stride: Elf64_Half,
|
||||
};
|
||||
pub const Elf32_gptab = extern union {
|
||||
gt_header: extern struct {
|
||||
gt_current_g_value: Elf32_Word,
|
||||
gt_unused: Elf32_Word,
|
||||
},
|
||||
gt_entry: extern struct {
|
||||
gt_g_value: Elf32_Word,
|
||||
gt_bytes: Elf32_Word,
|
||||
},
|
||||
};
|
||||
pub const Elf32_RegInfo = extern struct {
|
||||
ri_gprmask: Elf32_Word,
|
||||
ri_cprmask: [4]Elf32_Word,
|
||||
ri_gp_value: Elf32_Sword,
|
||||
};
|
||||
pub const Elf_Options = extern struct {
|
||||
kind: u8,
|
||||
size: u8,
|
||||
@"section": Elf32_Section,
|
||||
info: Elf32_Word,
|
||||
};
|
||||
pub const Elf_Options_Hw = extern struct {
|
||||
hwp_flags1: Elf32_Word,
|
||||
hwp_flags2: Elf32_Word,
|
||||
};
|
||||
pub const Elf32_Lib = extern struct {
|
||||
l_name: Elf32_Word,
|
||||
l_time_stamp: Elf32_Word,
|
||||
l_checksum: Elf32_Word,
|
||||
l_version: Elf32_Word,
|
||||
l_flags: Elf32_Word,
|
||||
};
|
||||
pub const Elf64_Lib = extern struct {
|
||||
l_name: Elf64_Word,
|
||||
l_time_stamp: Elf64_Word,
|
||||
l_checksum: Elf64_Word,
|
||||
l_version: Elf64_Word,
|
||||
l_flags: Elf64_Word,
|
||||
};
|
||||
pub const Elf32_Conflict = Elf32_Addr;
|
||||
pub const Elf_MIPS_ABIFlags_v0 = extern struct {
|
||||
version: Elf32_Half,
|
||||
isa_level: u8,
|
||||
isa_rev: u8,
|
||||
gpr_size: u8,
|
||||
cpr1_size: u8,
|
||||
cpr2_size: u8,
|
||||
fp_abi: u8,
|
||||
isa_ext: Elf32_Word,
|
||||
ases: Elf32_Word,
|
||||
flags1: Elf32_Word,
|
||||
flags2: Elf32_Word,
|
||||
};
|
||||
|
||||
pub const Ehdr = switch(@sizeOf(usize)) {
|
||||
4 => Elf32_Ehdr,
|
||||
8 => Elf64_Ehdr,
|
||||
else => @compileError("expected pointer size of 32 or 64"),
|
||||
};
|
||||
pub const Phdr = switch(@sizeOf(usize)) {
|
||||
4 => Elf32_Phdr,
|
||||
8 => Elf64_Phdr,
|
||||
else => @compileError("expected pointer size of 32 or 64"),
|
||||
};
|
||||
pub const Sym = switch(@sizeOf(usize)) {
|
||||
4 => Elf32_Sym,
|
||||
8 => Elf64_Sym,
|
||||
else => @compileError("expected pointer size of 32 or 64"),
|
||||
};
|
||||
pub const Verdef = switch(@sizeOf(usize)) {
|
||||
4 => Elf32_Verdef,
|
||||
8 => Elf64_Verdef,
|
||||
else => @compileError("expected pointer size of 32 or 64"),
|
||||
};
|
||||
pub const Verdaux = switch(@sizeOf(usize)) {
|
||||
4 => Elf32_Verdaux,
|
||||
8 => Elf64_Verdaux,
|
||||
else => @compileError("expected pointer size of 32 or 64"),
|
||||
};
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
const mem = @import("mem.zig");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub fn swapIfLe(comptime T: type, x: T) T {
|
||||
return swapIf(builtin.Endian.Little, T, x);
|
||||
}
|
||||
|
||||
pub fn swapIfBe(comptime T: type, x: T) T {
|
||||
return swapIf(builtin.Endian.Big, T, x);
|
||||
}
|
||||
|
||||
pub fn swapIf(endian: builtin.Endian, comptime T: type, x: T) T {
|
||||
return if (builtin.endian == endian) swap(T, x) else x;
|
||||
}
|
||||
|
||||
pub fn swap(comptime T: type, x: T) T {
|
||||
var buf: [@sizeOf(T)]u8 = undefined;
|
||||
mem.writeInt(buf[0..], x, builtin.Endian.Little);
|
||||
return mem.readInt(buf, T, builtin.Endian.Big);
|
||||
}
|
||||
|
||||
test "swap" {
|
||||
const debug = @import("debug/index.zig");
|
||||
debug.assert(swap(u32, 0xDEADBEEF) == 0xEFBEADDE);
|
||||
}
|
||||
235
std/event.zig
Normal file
235
std/event.zig
Normal file
@ -0,0 +1,235 @@
|
||||
const std = @import("index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const event = this;
|
||||
const mem = std.mem;
|
||||
const posix = std.os.posix;
|
||||
|
||||
pub const TcpServer = struct {
|
||||
handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File) void,
|
||||
|
||||
loop: &Loop,
|
||||
sockfd: i32,
|
||||
accept_coro: ?promise,
|
||||
listen_address: std.net.Address,
|
||||
|
||||
waiting_for_emfile_node: PromiseNode,
|
||||
|
||||
const PromiseNode = std.LinkedList(promise).Node;
|
||||
|
||||
pub fn init(loop: &Loop) !TcpServer {
|
||||
const sockfd = try std.os.posixSocket(posix.AF_INET,
|
||||
posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK,
|
||||
posix.PROTO_tcp);
|
||||
errdefer std.os.close(sockfd);
|
||||
|
||||
// TODO can't initialize handler coroutine here because we need well defined copy elision
|
||||
return TcpServer {
|
||||
.loop = loop,
|
||||
.sockfd = sockfd,
|
||||
.accept_coro = null,
|
||||
.handleRequestFn = undefined,
|
||||
.waiting_for_emfile_node = undefined,
|
||||
.listen_address = undefined,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn listen(self: &TcpServer, address: &const std.net.Address,
|
||||
handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File)void) !void
|
||||
{
|
||||
self.handleRequestFn = handleRequestFn;
|
||||
|
||||
try std.os.posixBind(self.sockfd, &address.os_addr);
|
||||
try std.os.posixListen(self.sockfd, posix.SOMAXCONN);
|
||||
self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd));
|
||||
|
||||
self.accept_coro = try async<self.loop.allocator> TcpServer.handler(self);
|
||||
errdefer cancel ??self.accept_coro;
|
||||
|
||||
try self.loop.addFd(self.sockfd, ??self.accept_coro);
|
||||
errdefer self.loop.removeFd(self.sockfd);
|
||||
|
||||
}
|
||||
|
||||
pub fn deinit(self: &TcpServer) void {
|
||||
self.loop.removeFd(self.sockfd);
|
||||
if (self.accept_coro) |accept_coro| cancel accept_coro;
|
||||
std.os.close(self.sockfd);
|
||||
}
|
||||
|
||||
pub async fn handler(self: &TcpServer) void {
|
||||
while (true) {
|
||||
var accepted_addr: std.net.Address = undefined;
|
||||
if (std.os.posixAccept(self.sockfd, &accepted_addr.os_addr,
|
||||
posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd|
|
||||
{
|
||||
var socket = std.os.File.openHandle(accepted_fd);
|
||||
_ = async<self.loop.allocator> self.handleRequestFn(self, accepted_addr, socket) catch |err| switch (err) {
|
||||
error.OutOfMemory => {
|
||||
socket.close();
|
||||
continue;
|
||||
},
|
||||
};
|
||||
} else |err| switch (err) {
|
||||
error.WouldBlock => {
|
||||
suspend; // we will get resumed by epoll_wait in the event loop
|
||||
continue;
|
||||
},
|
||||
error.ProcessFdQuotaExceeded => {
|
||||
errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
|
||||
suspend |p| {
|
||||
self.waiting_for_emfile_node = PromiseNode.init(p);
|
||||
std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
|
||||
}
|
||||
continue;
|
||||
},
|
||||
error.ConnectionAborted,
|
||||
error.FileDescriptorClosed => continue,
|
||||
|
||||
error.PageFault => unreachable,
|
||||
error.InvalidSyscall => unreachable,
|
||||
error.FileDescriptorNotASocket => unreachable,
|
||||
error.OperationNotSupported => unreachable,
|
||||
|
||||
error.SystemFdQuotaExceeded,
|
||||
error.SystemResources,
|
||||
error.ProtocolFailure,
|
||||
error.BlockedByFirewall,
|
||||
error.Unexpected => {
|
||||
@panic("TODO handle this error");
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Loop = struct {
|
||||
allocator: &mem.Allocator,
|
||||
epollfd: i32,
|
||||
keep_running: bool,
|
||||
|
||||
fn init(allocator: &mem.Allocator) !Loop {
|
||||
const epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC);
|
||||
return Loop {
|
||||
.keep_running = true,
|
||||
.allocator = allocator,
|
||||
.epollfd = epollfd,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addFd(self: &Loop, fd: i32, prom: promise) !void {
|
||||
var ev = std.os.linux.epoll_event {
|
||||
.events = std.os.linux.EPOLLIN|std.os.linux.EPOLLOUT|std.os.linux.EPOLLET,
|
||||
.data = std.os.linux.epoll_data {
|
||||
.ptr = @ptrToInt(prom),
|
||||
},
|
||||
};
|
||||
try std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev);
|
||||
}
|
||||
|
||||
pub fn removeFd(self: &Loop, fd: i32) void {
|
||||
std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
|
||||
}
|
||||
|
||||
async fn waitFd(self: &Loop, fd: i32) !void {
|
||||
defer self.removeFd(fd);
|
||||
suspend |p| {
|
||||
try self.addFd(fd, p);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop(self: &Loop) void {
|
||||
// TODO make atomic
|
||||
self.keep_running = false;
|
||||
// TODO activate an fd in the epoll set
|
||||
}
|
||||
|
||||
pub fn run(self: &Loop) void {
|
||||
while (self.keep_running) {
|
||||
var events: [16]std.os.linux.epoll_event = undefined;
|
||||
const count = std.os.linuxEpollWait(self.epollfd, events[0..], -1);
|
||||
for (events[0..count]) |ev| {
|
||||
const p = @intToPtr(promise, ev.data.ptr);
|
||||
resume p;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub async fn connect(loop: &Loop, _address: &const std.net.Address) !std.os.File {
|
||||
var address = *_address; // TODO https://github.com/zig-lang/zig/issues/733
|
||||
|
||||
const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK, posix.PROTO_tcp);
|
||||
errdefer std.os.close(sockfd);
|
||||
|
||||
try std.os.posixConnectAsync(sockfd, &address.os_addr);
|
||||
try await try async loop.waitFd(sockfd);
|
||||
try std.os.posixGetSockOptConnectError(sockfd);
|
||||
|
||||
return std.os.File.openHandle(sockfd);
|
||||
}
|
||||
|
||||
test "listen on a port, send bytes, receive bytes" {
|
||||
if (builtin.os != builtin.Os.linux) {
|
||||
// TODO build abstractions for other operating systems
|
||||
return;
|
||||
}
|
||||
const MyServer = struct {
|
||||
tcp_server: TcpServer,
|
||||
|
||||
const Self = this;
|
||||
|
||||
async<&mem.Allocator> fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address,
|
||||
_socket: &const std.os.File) void
|
||||
{
|
||||
const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
|
||||
var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733
|
||||
defer socket.close();
|
||||
const next_handler = async errorableHandler(self, _addr, socket) catch |err| switch (err) {
|
||||
error.OutOfMemory => @panic("unable to handle connection: out of memory"),
|
||||
};
|
||||
(await next_handler) catch |err| {
|
||||
std.debug.panic("unable to handle connection: {}\n", err);
|
||||
};
|
||||
suspend |p| { cancel p; }
|
||||
}
|
||||
|
||||
async fn errorableHandler(self: &Self, _addr: &const std.net.Address,
|
||||
_socket: &const std.os.File) !void
|
||||
{
|
||||
const addr = *_addr; // TODO https://github.com/zig-lang/zig/issues/733
|
||||
var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733
|
||||
|
||||
var adapter = std.io.FileOutStream.init(&socket);
|
||||
var stream = &adapter.stream;
|
||||
try stream.print("hello from server\n");
|
||||
}
|
||||
};
|
||||
|
||||
const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable;
|
||||
const addr = std.net.Address.initIp4(ip4addr, 0);
|
||||
|
||||
var loop = try Loop.init(std.debug.global_allocator);
|
||||
var server = MyServer {
|
||||
.tcp_server = try TcpServer.init(&loop),
|
||||
};
|
||||
defer server.tcp_server.deinit();
|
||||
try server.tcp_server.listen(addr, MyServer.handler);
|
||||
|
||||
const p = try async<std.debug.global_allocator> doAsyncTest(&loop, server.tcp_server.listen_address);
|
||||
defer cancel p;
|
||||
loop.run();
|
||||
}
|
||||
|
||||
async fn doAsyncTest(loop: &Loop, address: &const std.net.Address) void {
|
||||
errdefer @panic("test failure");
|
||||
|
||||
var socket_file = try await try async event.connect(loop, address);
|
||||
defer socket_file.close();
|
||||
|
||||
var buf: [512]u8 = undefined;
|
||||
const amt_read = try socket_file.read(buf[0..]);
|
||||
const msg = buf[0..amt_read];
|
||||
assert(mem.eql(u8, msg, "hello from server\n"));
|
||||
loop.stop();
|
||||
}
|
||||
@ -12,13 +12,79 @@ pub const FloatDecimal = struct {
|
||||
exp: i32,
|
||||
};
|
||||
|
||||
pub const RoundMode = enum {
|
||||
// Round only the fractional portion (e.g. 1234.23 has precision 2)
|
||||
Decimal,
|
||||
// Round the entire whole/fractional portion (e.g. 1.23423e3 has precision 5)
|
||||
Scientific,
|
||||
};
|
||||
|
||||
/// Round a FloatDecimal as returned by errol3 to the specified fractional precision.
|
||||
/// All digits after the specified precision should be considered invalid.
|
||||
pub fn roundToPrecision(float_decimal: &FloatDecimal, precision: usize, mode: RoundMode) void {
|
||||
// The round digit refers to the index which we should look at to determine
|
||||
// whether we need to round to match the specified precision.
|
||||
var round_digit: usize = 0;
|
||||
|
||||
switch (mode) {
|
||||
RoundMode.Decimal => {
|
||||
if (float_decimal.exp >= 0) {
|
||||
round_digit = precision + usize(float_decimal.exp);
|
||||
} else {
|
||||
// if a small negative exp, then adjust we need to offset by the number
|
||||
// of leading zeros that will occur.
|
||||
const min_exp_required = usize(-float_decimal.exp);
|
||||
if (precision > min_exp_required) {
|
||||
round_digit = precision - min_exp_required;
|
||||
}
|
||||
}
|
||||
},
|
||||
RoundMode.Scientific => {
|
||||
round_digit = 1 + precision;
|
||||
},
|
||||
}
|
||||
|
||||
// It suffices to look at just this digit. We don't round and propagate say 0.04999 to 0.05
|
||||
// first, and then to 0.1 in the case of a {.1} single precision.
|
||||
|
||||
// Find the digit which will signify the round point and start rounding backwards.
|
||||
if (round_digit < float_decimal.digits.len and float_decimal.digits[round_digit] - '0' >= 5) {
|
||||
assert(round_digit >= 0);
|
||||
|
||||
var i = round_digit;
|
||||
while (true) {
|
||||
if (i == 0) {
|
||||
// Rounded all the way past the start. This was of the form 9.999...
|
||||
// Slot the new digit in place and increase the exponent.
|
||||
float_decimal.exp += 1;
|
||||
|
||||
// Re-size the buffer to use the reserved leading byte.
|
||||
const one_before = @intToPtr(&u8, @ptrToInt(&float_decimal.digits[0]) - 1);
|
||||
float_decimal.digits = one_before[0..float_decimal.digits.len + 1];
|
||||
float_decimal.digits[0] = '1';
|
||||
return;
|
||||
}
|
||||
|
||||
i -= 1;
|
||||
|
||||
const new_value = (float_decimal.digits[i] - '0' + 1) % 10;
|
||||
float_decimal.digits[i] = new_value + '0';
|
||||
|
||||
// must continue rounding until non-9
|
||||
if (new_value != 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Corrected Errol3 double to ASCII conversion.
|
||||
pub fn errol3(value: f64, buffer: []u8) FloatDecimal {
|
||||
const bits = @bitCast(u64, value);
|
||||
const i = tableLowerBound(bits);
|
||||
if (i < enum3.len and enum3[i] == bits) {
|
||||
const data = enum3_data[i];
|
||||
const digits = buffer[0..data.str.len];
|
||||
const digits = buffer[1..data.str.len + 1];
|
||||
mem.copy(u8, digits, data.str);
|
||||
return FloatDecimal {
|
||||
.digits = digits,
|
||||
@ -98,7 +164,11 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
|
||||
}
|
||||
|
||||
// digit generation
|
||||
var buf_index: usize = 0;
|
||||
|
||||
// We generate digits starting at index 1. If rounding a buffer later then it may be
|
||||
// required to generate a preceeding digit in some cases (9.999) in which case we use
|
||||
// the 0-index for this extra digit.
|
||||
var buf_index: usize = 1;
|
||||
while (true) {
|
||||
var hdig = u8(math.floor(high.val));
|
||||
if ((high.val == f64(hdig)) and (high.off < 0))
|
||||
@ -128,7 +198,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
|
||||
buf_index += 1;
|
||||
|
||||
return FloatDecimal {
|
||||
.digits = buffer[0..buf_index],
|
||||
.digits = buffer[1..buf_index],
|
||||
.exp = exp,
|
||||
};
|
||||
}
|
||||
@ -189,6 +259,9 @@ fn gethi(in: f64) f64 {
|
||||
/// Normalize the number by factoring in the error.
|
||||
/// @hp: The float pair.
|
||||
fn hpNormalize(hp: &HP) void {
|
||||
// Required to avoid segfaults causing buffer overrun during errol3 digit output termination.
|
||||
@setFloatMode(this, @import("builtin").FloatMode.Strict);
|
||||
|
||||
const val = hp.val;
|
||||
|
||||
hp.val += hp.off;
|
||||
|
||||
@ -4,29 +4,31 @@ const debug = std.debug;
|
||||
const assert = debug.assert;
|
||||
const mem = std.mem;
|
||||
const builtin = @import("builtin");
|
||||
const errol3 = @import("errol/index.zig").errol3;
|
||||
const errol = @import("errol/index.zig");
|
||||
|
||||
const max_int_digits = 65;
|
||||
|
||||
const State = enum { // TODO put inside format function and make sure the name and debug info is correct
|
||||
Start,
|
||||
OpenBrace,
|
||||
CloseBrace,
|
||||
Integer,
|
||||
IntegerWidth,
|
||||
Float,
|
||||
FloatWidth,
|
||||
Character,
|
||||
Buf,
|
||||
BufWidth,
|
||||
};
|
||||
|
||||
/// Renders fmt string with args, calling output with slices of bytes.
|
||||
/// If `output` returns an error, the error is returned from `format` and
|
||||
/// `output` is not called again.
|
||||
pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void,
|
||||
comptime fmt: []const u8, args: ...) Errors!void
|
||||
{
|
||||
const State = enum {
|
||||
Start,
|
||||
OpenBrace,
|
||||
CloseBrace,
|
||||
Integer,
|
||||
IntegerWidth,
|
||||
Float,
|
||||
FloatWidth,
|
||||
FloatScientific,
|
||||
FloatScientificWidth,
|
||||
Character,
|
||||
Buf,
|
||||
BufWidth,
|
||||
};
|
||||
|
||||
comptime var start_index = 0;
|
||||
comptime var state = State.Start;
|
||||
comptime var next_arg = 0;
|
||||
@ -86,7 +88,11 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
|
||||
},
|
||||
's' => {
|
||||
state = State.Buf;
|
||||
},'.' => {
|
||||
},
|
||||
'e' => {
|
||||
state = State.FloatScientific;
|
||||
},
|
||||
'.' => {
|
||||
state = State.Float;
|
||||
},
|
||||
else => @compileError("Unknown format character: " ++ []u8{c}),
|
||||
@ -132,9 +138,33 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
|
||||
'0' ... '9' => {},
|
||||
else => @compileError("Unexpected character in format string: " ++ []u8{c}),
|
||||
},
|
||||
State.FloatScientific => switch (c) {
|
||||
'}' => {
|
||||
try formatFloatScientific(args[next_arg], null, context, Errors, output);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
},
|
||||
'0' ... '9' => {
|
||||
width_start = i;
|
||||
state = State.FloatScientificWidth;
|
||||
},
|
||||
else => @compileError("Unexpected character in format string: " ++ []u8{c}),
|
||||
},
|
||||
State.FloatScientificWidth => switch (c) {
|
||||
'}' => {
|
||||
width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
|
||||
try formatFloatScientific(args[next_arg], width, context, Errors, output);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
},
|
||||
'0' ... '9' => {},
|
||||
else => @compileError("Unexpected character in format string: " ++ []u8{c}),
|
||||
},
|
||||
State.Float => switch (c) {
|
||||
'}' => {
|
||||
try formatFloatDecimal(args[next_arg], 0, context, Errors, output);
|
||||
try formatFloatDecimal(args[next_arg], null, context, Errors, output);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
@ -198,7 +228,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@
|
||||
return formatInt(value, 10, false, 0, context, Errors, output);
|
||||
},
|
||||
builtin.TypeId.Float => {
|
||||
return formatFloat(value, context, Errors, output);
|
||||
return formatFloatScientific(value, null, context, Errors, output);
|
||||
},
|
||||
builtin.TypeId.Void => {
|
||||
return output(context, "void");
|
||||
@ -256,81 +286,237 @@ pub fn formatBuf(buf: []const u8, width: usize,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
|
||||
// Print a float in scientific notation to the specified precision. Null uses full precision.
|
||||
// It should be the case that every full precision, printed value can be re-parsed back to the
|
||||
// same type unambiguously.
|
||||
pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
|
||||
var x = f64(value);
|
||||
|
||||
// Errol doesn't handle these special cases.
|
||||
if (math.isNan(x)) {
|
||||
return output(context, "NaN");
|
||||
}
|
||||
if (math.signbit(x)) {
|
||||
try output(context, "-");
|
||||
x = -x;
|
||||
}
|
||||
|
||||
if (math.isNan(x)) {
|
||||
return output(context, "nan");
|
||||
}
|
||||
if (math.isPositiveInf(x)) {
|
||||
return output(context, "Infinity");
|
||||
return output(context, "inf");
|
||||
}
|
||||
if (x == 0.0) {
|
||||
return output(context, "0.0");
|
||||
try output(context, "0");
|
||||
|
||||
if (maybe_precision) |precision| {
|
||||
if (precision != 0) {
|
||||
try output(context, ".");
|
||||
var i: usize = 0;
|
||||
while (i < precision) : (i += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try output(context, ".0");
|
||||
}
|
||||
|
||||
try output(context, "e+00");
|
||||
return;
|
||||
}
|
||||
|
||||
var buffer: [32]u8 = undefined;
|
||||
const float_decimal = errol3(x, buffer[0..]);
|
||||
try output(context, float_decimal.digits[0..1]);
|
||||
try output(context, ".");
|
||||
if (float_decimal.digits.len > 1) {
|
||||
const num_digits = if (@typeOf(value) == f32)
|
||||
math.min(usize(9), float_decimal.digits.len)
|
||||
else
|
||||
float_decimal.digits.len;
|
||||
try output(context, float_decimal.digits[1 .. num_digits]);
|
||||
var float_decimal = errol.errol3(x, buffer[0..]);
|
||||
|
||||
if (maybe_precision) |precision| {
|
||||
errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);
|
||||
|
||||
try output(context, float_decimal.digits[0..1]);
|
||||
|
||||
// {e0} case prints no `.`
|
||||
if (precision != 0) {
|
||||
try output(context, ".");
|
||||
|
||||
var printed: usize = 0;
|
||||
if (float_decimal.digits.len > 1) {
|
||||
const num_digits = math.min(float_decimal.digits.len, precision + 1);
|
||||
try output(context, float_decimal.digits[1 .. num_digits]);
|
||||
printed += num_digits - 1;
|
||||
}
|
||||
|
||||
while (printed < precision) : (printed += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try output(context, "0");
|
||||
try output(context, float_decimal.digits[0..1]);
|
||||
try output(context, ".");
|
||||
if (float_decimal.digits.len > 1) {
|
||||
const num_digits = if (@typeOf(value) == f32)
|
||||
math.min(usize(9), float_decimal.digits.len)
|
||||
else
|
||||
float_decimal.digits.len;
|
||||
|
||||
try output(context, float_decimal.digits[1 .. num_digits]);
|
||||
} else {
|
||||
try output(context, "0");
|
||||
}
|
||||
}
|
||||
|
||||
if (float_decimal.exp != 1) {
|
||||
try output(context, "e");
|
||||
try formatInt(float_decimal.exp - 1, 10, false, 0, context, Errors, output);
|
||||
try output(context, "e");
|
||||
const exp = float_decimal.exp - 1;
|
||||
|
||||
if (exp >= 0) {
|
||||
try output(context, "+");
|
||||
if (exp > -10 and exp < 10) {
|
||||
try output(context, "0");
|
||||
}
|
||||
try formatInt(exp, 10, false, 0, context, Errors, output);
|
||||
} else {
|
||||
try output(context, "-");
|
||||
if (exp > -10 and exp < 10) {
|
||||
try output(context, "0");
|
||||
}
|
||||
try formatInt(-exp, 10, false, 0, context, Errors, output);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn formatFloatDecimal(value: var, precision: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
|
||||
// Print a float of the format x.yyyyy where the number of y is specified by the precision argument.
|
||||
// By default floats are printed at full precision (no rounding).
|
||||
pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
|
||||
var x = f64(value);
|
||||
|
||||
// Errol doesn't handle these special cases.
|
||||
if (math.isNan(x)) {
|
||||
return output(context, "NaN");
|
||||
}
|
||||
if (math.signbit(x)) {
|
||||
try output(context, "-");
|
||||
x = -x;
|
||||
}
|
||||
|
||||
if (math.isNan(x)) {
|
||||
return output(context, "nan");
|
||||
}
|
||||
if (math.isPositiveInf(x)) {
|
||||
return output(context, "Infinity");
|
||||
return output(context, "inf");
|
||||
}
|
||||
if (x == 0.0) {
|
||||
return output(context, "0.0");
|
||||
try output(context, "0");
|
||||
|
||||
if (maybe_precision) |precision| {
|
||||
if (precision != 0) {
|
||||
try output(context, ".");
|
||||
var i: usize = 0;
|
||||
while (i < precision) : (i += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
} else {
|
||||
try output(context, ".0");
|
||||
}
|
||||
} else {
|
||||
try output(context, "0");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// non-special case, use errol3
|
||||
var buffer: [32]u8 = undefined;
|
||||
const float_decimal = errol3(x, buffer[0..]);
|
||||
var float_decimal = errol.errol3(x, buffer[0..]);
|
||||
|
||||
const num_left_digits = if (float_decimal.exp > 0) usize(float_decimal.exp) else 1;
|
||||
if (maybe_precision) |precision| {
|
||||
errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal);
|
||||
|
||||
try output(context, float_decimal.digits[0 .. num_left_digits]);
|
||||
try output(context, ".");
|
||||
if (float_decimal.digits.len > 1) {
|
||||
const num_valid_digtis = if (@typeOf(value) == f32) math.min(usize(7), float_decimal.digits.len)
|
||||
else
|
||||
float_decimal.digits.len;
|
||||
// exp < 0 means the leading is always 0 as errol result is normalized.
|
||||
var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0;
|
||||
|
||||
const num_right_digits = if (precision != 0)
|
||||
math.min(precision, (num_valid_digtis-num_left_digits))
|
||||
else
|
||||
num_valid_digtis - num_left_digits;
|
||||
try output(context, float_decimal.digits[num_left_digits .. (num_left_digits + num_right_digits)]);
|
||||
// the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this.
|
||||
var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len);
|
||||
|
||||
if (num_digits_whole > 0) {
|
||||
// We may have to zero pad, for instance 1e4 requires zero padding.
|
||||
try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]);
|
||||
|
||||
var i = num_digits_whole_no_pad;
|
||||
while (i < num_digits_whole) : (i += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
} else {
|
||||
try output(context , "0");
|
||||
}
|
||||
|
||||
// {.0} special case doesn't want a trailing '.'
|
||||
if (precision == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try output(context, ".");
|
||||
|
||||
// Keep track of fractional count printed for case where we pre-pad then post-pad with 0's.
|
||||
var printed: usize = 0;
|
||||
|
||||
// Zero-fill until we reach significant digits or run out of precision.
|
||||
if (float_decimal.exp <= 0) {
|
||||
const zero_digit_count = usize(-float_decimal.exp);
|
||||
const zeros_to_print = math.min(zero_digit_count, precision);
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < zeros_to_print) : (i += 1) {
|
||||
try output(context, "0");
|
||||
printed += 1;
|
||||
}
|
||||
|
||||
if (printed >= precision) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Remaining fractional portion, zero-padding if insufficient.
|
||||
debug.assert(precision >= printed);
|
||||
if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) {
|
||||
try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]);
|
||||
return;
|
||||
} else {
|
||||
try output(context, float_decimal.digits[num_digits_whole_no_pad ..]);
|
||||
printed += float_decimal.digits.len - num_digits_whole_no_pad;
|
||||
|
||||
while (printed < precision) : (printed += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try output(context, "0");
|
||||
// exp < 0 means the leading is always 0 as errol result is normalized.
|
||||
var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0;
|
||||
|
||||
// the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this.
|
||||
var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len);
|
||||
|
||||
if (num_digits_whole > 0) {
|
||||
// We may have to zero pad, for instance 1e4 requires zero padding.
|
||||
try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]);
|
||||
|
||||
var i = num_digits_whole_no_pad;
|
||||
while (i < num_digits_whole) : (i += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
} else {
|
||||
try output(context , "0");
|
||||
}
|
||||
|
||||
// Omit `.` if no fractional portion
|
||||
if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) {
|
||||
return;
|
||||
}
|
||||
|
||||
try output(context, ".");
|
||||
|
||||
// Zero-fill until we reach significant digits or run out of precision.
|
||||
if (float_decimal.exp < 0) {
|
||||
const zero_digit_count = usize(-float_decimal.exp);
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < zero_digit_count) : (i += 1) {
|
||||
try output(context, "0");
|
||||
}
|
||||
}
|
||||
|
||||
try output(context, float_decimal.digits[num_digits_whole_no_pad ..]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -465,7 +651,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,
|
||||
@ -593,70 +779,210 @@ test "fmt.format" {
|
||||
const result = try bufPrint(buf1[0..], "pointer: {}\n", &value);
|
||||
assert(mem.startsWith(u8, result, "pointer: Struct@"));
|
||||
}
|
||||
|
||||
// TODO get these tests passing in release modes
|
||||
// https://github.com/zig-lang/zig/issues/564
|
||||
if (builtin.mode == builtin.Mode.Debug) {
|
||||
{
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 1.34;
|
||||
const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1.34000003e+00\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 12.34;
|
||||
const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1.23400001e+01\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = -12.34e10;
|
||||
const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: -1.234e+11\n"));
|
||||
}
|
||||
{
|
||||
// This fails on release due to a minor rounding difference.
|
||||
// --release-fast outputs 9.999960000000001e-40 vs. the expected.
|
||||
if (builtin.mode == builtin.Mode.Debug) {
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 12.34;
|
||||
const result = try bufPrint(buf1[0..], "f32: {}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1.23400001e1\n"));
|
||||
const value: f64 = 9.999960e-40;
|
||||
const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 9.99996e-40\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = -12.34e10;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: -1.234e11\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
|
||||
assert(mem.eql(u8, result, "f64: NaN\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
|
||||
assert(mem.eql(u8, result, "f64: Infinity\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
|
||||
assert(mem.eql(u8, result, "f64: -Infinity\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 1.1234;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.1}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1.1\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 1234.567;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1234.56\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = -11.1234;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);
|
||||
// -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
|
||||
// -11.12339... is truncated to -11.1233
|
||||
assert(mem.eql(u8, result, "f32: -11.1233\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 91.12345;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 91.12345\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 91.12345678901235;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 91.1234567890\n"));
|
||||
}
|
||||
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 1.409706e-42;
|
||||
const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 1.40971e-42\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = @bitCast(f32, u32(814313563));
|
||||
const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 1.00000e-09\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = @bitCast(f32, u32(1006632960));
|
||||
const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 7.81250e-03\n"));
|
||||
}
|
||||
{
|
||||
// libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05.
|
||||
// In fact, libc doesn't round a lot of 5 cases up when one past the precision point.
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = @bitCast(f32, u32(1203982400));
|
||||
const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 1.00001e+05\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
|
||||
assert(mem.eql(u8, result, "f64: nan\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64);
|
||||
assert(mem.eql(u8, result, "f64: -nan\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
|
||||
assert(mem.eql(u8, result, "f64: inf\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
|
||||
assert(mem.eql(u8, result, "f64: -inf\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [64]u8 = undefined;
|
||||
const value: f64 = 1.52314e+29;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 1.1234;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.1}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1.1\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 1234.567;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1234.57\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = -11.1234;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);
|
||||
// -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
|
||||
// -11.12339... is rounded back up to -11.1234
|
||||
assert(mem.eql(u8, result, "f32: -11.1234\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 91.12345;
|
||||
const result = try bufPrint(buf1[0..], "f32: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 91.12345\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 91.12345678901235;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 91.1234567890\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 0.0;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.00000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 5.700;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.0}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 6\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 9.999;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.1}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 10.0\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 1.0;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.3}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 1.000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 0.0003;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.8}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.00030000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 1.40130e-45;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.00000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = 9.999960e-40;
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.00000\n"));
|
||||
}
|
||||
// libc checks
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = f64(@bitCast(f32, u32(916964781)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.00001\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = f64(@bitCast(f32, u32(925353389)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.00001\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = f64(@bitCast(f32, u32(1036831278)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.10000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = f64(@bitCast(f32, u32(1065353133)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 1.00000\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = f64(@bitCast(f32, u32(1092616192)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 10.00000\n"));
|
||||
}
|
||||
// libc differences
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
// This is 0.015625 exactly according to gdb. We thus round down,
|
||||
// however glibc rounds up for some reason. This occurs for all
|
||||
// floats of the form x.yyyy25 on a precision point.
|
||||
const value: f64 = f64(@bitCast(f32, u32(1015021568)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 0.01563\n"));
|
||||
}
|
||||
// std-windows-x86_64-Debug-bare test case fails
|
||||
{
|
||||
// errol3 rounds to ... 630 but libc rounds to ...632. Grisu3
|
||||
// also rounds to 630 so I'm inclined to believe libc is not
|
||||
// optimal here.
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = f64(@bitCast(f32, u32(1518338049)));
|
||||
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -54,6 +54,14 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
}
|
||||
unreachable; // no next item
|
||||
}
|
||||
|
||||
// Reset the iterator to the initial index
|
||||
pub fn reset(it: &Iterator) void {
|
||||
it.count = 0;
|
||||
it.index = 0;
|
||||
// Resetting the modification count too
|
||||
it.initial_modification_count = it.hm.modification_count;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init(allocator: &Allocator) Self {
|
||||
@ -66,7 +74,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(hm: &Self) void {
|
||||
pub fn deinit(hm: &const Self) void {
|
||||
hm.allocator.free(hm.entries);
|
||||
}
|
||||
|
||||
@ -79,6 +87,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
hm.incrementModificationCount();
|
||||
}
|
||||
|
||||
pub fn count(hm: &const Self) usize {
|
||||
return hm.size;
|
||||
}
|
||||
|
||||
/// Returns the value that was already there.
|
||||
pub fn put(hm: &Self, key: K, value: &const V) !?V {
|
||||
if (hm.entries.len == 0) {
|
||||
@ -102,18 +114,19 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
return hm.internalPut(key, value);
|
||||
}
|
||||
|
||||
pub fn get(hm: &Self, key: K) ?&Entry {
|
||||
pub fn get(hm: &const Self, key: K) ?&Entry {
|
||||
if (hm.entries.len == 0) {
|
||||
return null;
|
||||
}
|
||||
return hm.internalGet(key);
|
||||
}
|
||||
|
||||
pub fn contains(hm: &Self, key: K) bool {
|
||||
pub fn contains(hm: &const Self, key: K) bool {
|
||||
return hm.get(key) != null;
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -217,7 +230,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
unreachable; // put into a full map
|
||||
}
|
||||
|
||||
fn internalGet(hm: &Self, key: K) ?&Entry {
|
||||
fn internalGet(hm: &const Self, key: K) ?&Entry {
|
||||
const start_index = hm.keyToIndex(key);
|
||||
{var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
|
||||
const index = (start_index + roll_over) % hm.entries.len;
|
||||
@ -229,14 +242,17 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
return null;
|
||||
}
|
||||
|
||||
fn keyToIndex(hm: &Self, key: K) usize {
|
||||
fn keyToIndex(hm: &const Self, key: K) usize {
|
||||
return usize(hash(key)) % hm.entries.len;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
@ -248,12 +264,52 @@ test "basic hash map usage" {
|
||||
assert(??(map.put(5, 66) catch unreachable) == 55);
|
||||
assert(??(map.put(5, 55) catch unreachable) == 66);
|
||||
|
||||
assert(map.contains(2));
|
||||
assert((??map.get(2)).value == 22);
|
||||
_ = map.remove(2);
|
||||
assert(map.remove(2) == null);
|
||||
assert(map.get(2) == null);
|
||||
}
|
||||
|
||||
test "iterator hash map" {
|
||||
var direct_allocator = std.heap.DirectAllocator.init();
|
||||
defer direct_allocator.deinit();
|
||||
|
||||
var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
|
||||
defer reset_map.deinit();
|
||||
|
||||
assert((reset_map.put(1, 11) catch unreachable) == null);
|
||||
assert((reset_map.put(2, 22) catch unreachable) == null);
|
||||
assert((reset_map.put(3, 33) catch unreachable) == null);
|
||||
|
||||
var keys = []i32 { 1, 2, 3 };
|
||||
var values = []i32 { 11, 22, 33 };
|
||||
|
||||
var it = reset_map.iterator();
|
||||
var count : usize = 0;
|
||||
while (it.next()) |next| {
|
||||
assert(next.key == keys[count]);
|
||||
assert(next.value == values[count]);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
assert(count == 3);
|
||||
assert(it.next() == null);
|
||||
it.reset();
|
||||
count = 0;
|
||||
while (it.next()) |next| {
|
||||
assert(next.key == keys[count]);
|
||||
assert(next.value == values[count]);
|
||||
count += 1;
|
||||
if (count == 2) break;
|
||||
}
|
||||
|
||||
it.reset();
|
||||
var entry = ?? it.next();
|
||||
assert(entry.key == keys[0]);
|
||||
assert(entry.value == values[0]);
|
||||
}
|
||||
|
||||
fn hash_i32(x: i32) u32 {
|
||||
return @bitCast(u32, x);
|
||||
}
|
||||
|
||||
135
std/heap.zig
135
std/heap.zig
@ -47,13 +47,6 @@ pub const DirectAllocator = struct {
|
||||
|
||||
const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void;
|
||||
|
||||
//pub const canary_bytes = []u8 {48, 239, 128, 46, 18, 49, 147, 9, 195, 59, 203, 3, 245, 54, 9, 122};
|
||||
//pub const want_safety = switch (builtin.mode) {
|
||||
// builtin.Mode.Debug => true,
|
||||
// builtin.Mode.ReleaseSafe => true,
|
||||
// else => false,
|
||||
//};
|
||||
|
||||
pub fn init() DirectAllocator {
|
||||
return DirectAllocator {
|
||||
.allocator = Allocator {
|
||||
@ -79,19 +72,38 @@ pub const DirectAllocator = struct {
|
||||
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios => {
|
||||
assert(alignment <= os.page_size);
|
||||
const p = os.posix;
|
||||
const addr = p.mmap(null, n, p.PROT_READ|p.PROT_WRITE,
|
||||
p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0);
|
||||
if (addr == p.MAP_FAILED) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
return @intToPtr(&u8, addr)[0..n];
|
||||
const alloc_size = if(alignment <= os.page_size) n else n + alignment;
|
||||
const addr = p.mmap(null, alloc_size, p.PROT_READ|p.PROT_WRITE,
|
||||
p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0);
|
||||
if(addr == p.MAP_FAILED) return error.OutOfMemory;
|
||||
|
||||
if(alloc_size == n) return @intToPtr(&u8, addr)[0..n];
|
||||
|
||||
var aligned_addr = addr & ~usize(alignment - 1);
|
||||
aligned_addr += alignment;
|
||||
|
||||
//We can unmap the unused portions of our mmap, but we must only
|
||||
// pass munmap bytes that exist outside our allocated pages or it
|
||||
// will happily eat us too
|
||||
|
||||
//Since alignment > page_size, we are by definition on a page boundry
|
||||
const unused_start = addr;
|
||||
const unused_len = aligned_addr - 1 - unused_start;
|
||||
|
||||
var err = p.munmap(unused_start, unused_len);
|
||||
debug.assert(p.getErrno(err) == 0);
|
||||
|
||||
//It is impossible that there is an unoccupied page at the top of our
|
||||
// mmap.
|
||||
|
||||
return @intToPtr(&u8, aligned_addr)[0..n];
|
||||
},
|
||||
Os.windows => {
|
||||
const amt = n + alignment + @sizeOf(usize);
|
||||
const heap_handle = self.heap_handle ?? blk: {
|
||||
const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) ?? return error.OutOfMemory;
|
||||
const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0)
|
||||
?? return error.OutOfMemory;
|
||||
self.heap_handle = hh;
|
||||
break :blk hh;
|
||||
};
|
||||
@ -120,7 +132,7 @@ pub const DirectAllocator = struct {
|
||||
const rem = @rem(new_addr_end, os.page_size);
|
||||
const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
|
||||
if (old_addr_end > new_addr_end_rounded) {
|
||||
_ = os.posix.munmap(@intToPtr(&u8, new_addr_end_rounded), old_addr_end - new_addr_end_rounded);
|
||||
_ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
|
||||
}
|
||||
return old_mem[0..new_size];
|
||||
}
|
||||
@ -158,7 +170,7 @@ pub const DirectAllocator = struct {
|
||||
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios => {
|
||||
_ = os.posix.munmap(bytes.ptr, bytes.len);
|
||||
_ = os.posix.munmap(@ptrToInt(bytes.ptr), bytes.len);
|
||||
},
|
||||
Os.windows => {
|
||||
const record_addr = @ptrToInt(bytes.ptr) + bytes.len;
|
||||
@ -279,7 +291,7 @@ pub const FixedBufferAllocator = struct {
|
||||
|
||||
fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
|
||||
const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
|
||||
const addr = @ptrToInt(&self.buffer[self.end_index]);
|
||||
const addr = @ptrToInt(self.buffer.ptr) + self.end_index;
|
||||
const rem = @rem(addr, alignment);
|
||||
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
|
||||
const adjusted_index = self.end_index + march_forward_bytes;
|
||||
@ -306,6 +318,54 @@ pub const FixedBufferAllocator = struct {
|
||||
fn free(allocator: &Allocator, bytes: []u8) void { }
|
||||
};
|
||||
|
||||
/// lock free
|
||||
pub const ThreadSafeFixedBufferAllocator = struct {
|
||||
allocator: Allocator,
|
||||
end_index: usize,
|
||||
buffer: []u8,
|
||||
|
||||
pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator {
|
||||
return ThreadSafeFixedBufferAllocator {
|
||||
.allocator = Allocator {
|
||||
.allocFn = alloc,
|
||||
.reallocFn = realloc,
|
||||
.freeFn = free,
|
||||
},
|
||||
.buffer = buffer,
|
||||
.end_index = 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
|
||||
const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);
|
||||
var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);
|
||||
while (true) {
|
||||
const addr = @ptrToInt(self.buffer.ptr) + end_index;
|
||||
const rem = @rem(addr, alignment);
|
||||
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
|
||||
const adjusted_index = end_index + march_forward_bytes;
|
||||
const new_end_index = adjusted_index + n;
|
||||
if (new_end_index > self.buffer.len) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index,
|
||||
builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index .. new_end_index];
|
||||
}
|
||||
}
|
||||
|
||||
fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
|
||||
if (new_size <= old_mem.len) {
|
||||
return old_mem[0..new_size];
|
||||
} else {
|
||||
const result = try alloc(allocator, new_size, alignment);
|
||||
mem.copy(u8, result, old_mem);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
fn free(allocator: &Allocator, bytes: []u8) void { }
|
||||
};
|
||||
|
||||
|
||||
|
||||
test "c_allocator" {
|
||||
@ -322,6 +382,7 @@ test "DirectAllocator" {
|
||||
|
||||
const allocator = &direct_allocator.allocator;
|
||||
try testAllocator(allocator);
|
||||
try testAllocatorLargeAlignment(allocator);
|
||||
}
|
||||
|
||||
test "ArenaAllocator" {
|
||||
@ -332,6 +393,7 @@ test "ArenaAllocator" {
|
||||
defer arena_allocator.deinit();
|
||||
|
||||
try testAllocator(&arena_allocator.allocator);
|
||||
try testAllocatorLargeAlignment(&arena_allocator.allocator);
|
||||
}
|
||||
|
||||
var test_fixed_buffer_allocator_memory: [30000 * @sizeOf(usize)]u8 = undefined;
|
||||
@ -339,6 +401,14 @@ test "FixedBufferAllocator" {
|
||||
var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
|
||||
|
||||
try testAllocator(&fixed_buffer_allocator.allocator);
|
||||
try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
|
||||
}
|
||||
|
||||
test "ThreadSafeFixedBufferAllocator" {
|
||||
var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
|
||||
|
||||
try testAllocator(&fixed_buffer_allocator.allocator);
|
||||
try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
|
||||
}
|
||||
|
||||
fn testAllocator(allocator: &mem.Allocator) !void {
|
||||
@ -360,3 +430,32 @@ fn testAllocator(allocator: &mem.Allocator) !void {
|
||||
|
||||
allocator.free(slice);
|
||||
}
|
||||
|
||||
fn testAllocatorLargeAlignment(allocator: &mem.Allocator) mem.Allocator.Error!void {
|
||||
//Maybe a platform's page_size is actually the same as or
|
||||
// very near usize?
|
||||
if(os.page_size << 2 > @maxValue(usize)) return;
|
||||
|
||||
const USizeShift = @IntType(false, std.math.log2(usize.bit_count));
|
||||
const large_align = u29(os.page_size << 2);
|
||||
|
||||
var align_mask: usize = undefined;
|
||||
_ = @shlWithOverflow(usize, ~usize(0), USizeShift(@ctz(large_align)), &align_mask);
|
||||
|
||||
var slice = try allocator.allocFn(allocator, 500, large_align);
|
||||
debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
|
||||
|
||||
slice = try allocator.reallocFn(allocator, slice, 100, large_align);
|
||||
debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
|
||||
|
||||
slice = try allocator.reallocFn(allocator, slice, 5000, large_align);
|
||||
debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
|
||||
|
||||
slice = try allocator.reallocFn(allocator, slice, 10, large_align);
|
||||
debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
|
||||
|
||||
slice = try allocator.reallocFn(allocator, slice, 20000, large_align);
|
||||
debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
|
||||
|
||||
allocator.free(slice);
|
||||
}
|
||||
|
||||
@ -7,7 +7,9 @@ pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
|
||||
pub const HashMap = @import("hash_map.zig").HashMap;
|
||||
pub const LinkedList = @import("linked_list.zig").LinkedList;
|
||||
pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
|
||||
pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
|
||||
|
||||
pub const atomic = @import("atomic/index.zig");
|
||||
pub const base64 = @import("base64.zig");
|
||||
pub const build = @import("build.zig");
|
||||
pub const c = @import("c/index.zig");
|
||||
@ -17,11 +19,12 @@ 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 json = @import("json.zig");
|
||||
pub const macho = @import("macho.zig");
|
||||
pub const math = @import("math/index.zig");
|
||||
pub const mem = @import("mem.zig");
|
||||
@ -34,12 +37,14 @@ pub const zig = @import("zig/index.zig");
|
||||
|
||||
test "std" {
|
||||
// run tests from these
|
||||
_ = @import("atomic/index.zig");
|
||||
_ = @import("array_list.zig");
|
||||
_ = @import("buf_map.zig");
|
||||
_ = @import("buf_set.zig");
|
||||
_ = @import("buffer.zig");
|
||||
_ = @import("hash_map.zig");
|
||||
_ = @import("linked_list.zig");
|
||||
_ = @import("segmented_list.zig");
|
||||
|
||||
_ = @import("base64.zig");
|
||||
_ = @import("build.zig");
|
||||
@ -50,15 +55,16 @@ 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("json.zig");
|
||||
_ = @import("macho.zig");
|
||||
_ = @import("math/index.zig");
|
||||
_ = @import("mem.zig");
|
||||
_ = @import("heap.zig");
|
||||
_ = @import("net.zig");
|
||||
_ = @import("heap.zig");
|
||||
_ = @import("os/index.zig");
|
||||
_ = @import("rand/index.zig");
|
||||
_ = @import("sort.zig");
|
||||
|
||||
@ -486,6 +486,11 @@ pub fn readLine(buf: []u8) !usize {
|
||||
while (true) {
|
||||
const byte = stream.readByte() catch return error.EndOfFile;
|
||||
switch (byte) {
|
||||
'\r' => {
|
||||
// trash the following \n
|
||||
_ = stream.readByte() catch return error.EndOfFile;
|
||||
return index;
|
||||
},
|
||||
'\n' => return index,
|
||||
else => {
|
||||
if (index == buf.len) return error.InputTooLong;
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
const std = @import("index.zig");
|
||||
const io = std.io;
|
||||
const allocator = std.debug.global_allocator;
|
||||
const DefaultPrng = std.rand.DefaultPrng;
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
@ -8,6 +7,9 @@ const os = std.os;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
test "write a file, read it, then delete it" {
|
||||
var raw_bytes: [200 * 1024]u8 = undefined;
|
||||
var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator;
|
||||
|
||||
var data: [1024]u8 = undefined;
|
||||
var prng = DefaultPrng.init(1234);
|
||||
prng.random.bytes(data[0..]);
|
||||
@ -44,3 +46,17 @@ test "write a file, read it, then delete it" {
|
||||
}
|
||||
try os.deleteFile(allocator, tmp_file_name);
|
||||
}
|
||||
|
||||
test "BufferOutStream" {
|
||||
var bytes: [100]u8 = undefined;
|
||||
var allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
|
||||
|
||||
var buffer = try std.Buffer.initSize(allocator, 0);
|
||||
var buf_stream = &std.io.BufferOutStream.init(&buffer).stream;
|
||||
|
||||
const x: i32 = 42;
|
||||
const y: i32 = 1234;
|
||||
try buf_stream.print("x: {}\ny: {}\n", x, y);
|
||||
|
||||
assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
|
||||
}
|
||||
|
||||
1304
std/json.zig
Normal file
1304
std/json.zig
Normal file
File diff suppressed because it is too large
Load Diff
1942
std/json_test.zig
Normal file
1942
std/json_test.zig
Normal file
File diff suppressed because it is too large
Load Diff
@ -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.
|
||||
|
||||
18
std/math/complex/abs.zig
Normal file
18
std/math/complex/abs.zig
Normal file
@ -0,0 +1,18 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn abs(z: var) @typeOf(z.re) {
|
||||
const T = @typeOf(z.re);
|
||||
return math.hypot(T, z.re, z.im);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.cabs" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = abs(a);
|
||||
debug.assert(math.approxEq(f32, c, 5.83095, epsilon));
|
||||
}
|
||||
21
std/math/complex/acos.zig
Normal file
21
std/math/complex/acos.zig
Normal file
@ -0,0 +1,21 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn acos(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const q = cmath.asin(z);
|
||||
return Complex(T).new(T(math.pi) / 2 - q.re, -q.im);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.cacos" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = acos(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 0.546975, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, -2.452914, epsilon));
|
||||
}
|
||||
21
std/math/complex/acosh.zig
Normal file
21
std/math/complex/acosh.zig
Normal file
@ -0,0 +1,21 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn acosh(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const q = cmath.acos(z);
|
||||
return Complex(T).new(-q.im, q.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.cacosh" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = acosh(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 2.452914, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 0.546975, epsilon));
|
||||
}
|
||||
18
std/math/complex/arg.zig
Normal file
18
std/math/complex/arg.zig
Normal file
@ -0,0 +1,18 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn arg(z: var) @typeOf(z.re) {
|
||||
const T = @typeOf(z.re);
|
||||
return math.atan2(T, z.im, z.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.carg" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = arg(a);
|
||||
debug.assert(math.approxEq(f32, c, 0.540420, epsilon));
|
||||
}
|
||||
27
std/math/complex/asin.zig
Normal file
27
std/math/complex/asin.zig
Normal file
@ -0,0 +1,27 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn asin(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const p = Complex(T).new(1.0 - (x - y) * (x + y), -2.0 * x * y);
|
||||
const q = Complex(T).new(-y, x);
|
||||
const r = cmath.log(q.add(cmath.sqrt(p)));
|
||||
|
||||
return Complex(T).new(r.im, -r.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.casin" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = asin(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 1.023822, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 2.452914, epsilon));
|
||||
}
|
||||
22
std/math/complex/asinh.zig
Normal file
22
std/math/complex/asinh.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn asinh(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const q = Complex(T).new(-z.im, z.re);
|
||||
const r = cmath.asin(q);
|
||||
return Complex(T).new(r.im, -r.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.casinh" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = asinh(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 2.459831, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 0.533999, epsilon));
|
||||
}
|
||||
130
std/math/complex/atan.zig
Normal file
130
std/math/complex/atan.zig
Normal file
@ -0,0 +1,130 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn atan(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
return switch (T) {
|
||||
f32 => atan32(z),
|
||||
f64 => atan64(z),
|
||||
else => @compileError("atan not implemented for " ++ @typeName(z)),
|
||||
};
|
||||
}
|
||||
|
||||
fn redupif32(x: f32) f32 {
|
||||
const DP1 = 3.140625;
|
||||
const DP2 = 9.67502593994140625e-4;
|
||||
const DP3 = 1.509957990978376432e-7;
|
||||
|
||||
var t = x / math.pi;
|
||||
if (t >= 0.0) {
|
||||
t += 0.5;
|
||||
} else {
|
||||
t -= 0.5;
|
||||
}
|
||||
|
||||
const u = f32(i32(t));
|
||||
return ((x - u * DP1) - u * DP2) - t * DP3;
|
||||
}
|
||||
|
||||
fn atan32(z: &const Complex(f32)) Complex(f32) {
|
||||
const maxnum = 1.0e38;
|
||||
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
if ((x == 0.0) and (y > 1.0)) {
|
||||
// overflow
|
||||
return Complex(f32).new(maxnum, maxnum);
|
||||
}
|
||||
|
||||
const x2 = x * x;
|
||||
var a = 1.0 - x2 - (y * y);
|
||||
if (a == 0.0) {
|
||||
// overflow
|
||||
return Complex(f32).new(maxnum, maxnum);
|
||||
}
|
||||
|
||||
var t = 0.5 * math.atan2(f32, 2.0 * x, a);
|
||||
var w = redupif32(t);
|
||||
|
||||
t = y - 1.0;
|
||||
a = x2 + t * t;
|
||||
if (a == 0.0) {
|
||||
// overflow
|
||||
return Complex(f32).new(maxnum, maxnum);
|
||||
}
|
||||
|
||||
t = y + 1.0;
|
||||
a = (x2 + (t * t)) / a;
|
||||
return Complex(f32).new(w, 0.25 * math.ln(a));
|
||||
}
|
||||
|
||||
fn redupif64(x: f64) f64 {
|
||||
const DP1 = 3.14159265160560607910;
|
||||
const DP2 = 1.98418714791870343106e-9;
|
||||
const DP3 = 1.14423774522196636802e-17;
|
||||
|
||||
var t = x / math.pi;
|
||||
if (t >= 0.0) {
|
||||
t += 0.5;
|
||||
} else {
|
||||
t -= 0.5;
|
||||
}
|
||||
|
||||
const u = f64(i64(t));
|
||||
return ((x - u * DP1) - u * DP2) - t * DP3;
|
||||
}
|
||||
|
||||
fn atan64(z: &const Complex(f64)) Complex(f64) {
|
||||
const maxnum = 1.0e308;
|
||||
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
if ((x == 0.0) and (y > 1.0)) {
|
||||
// overflow
|
||||
return Complex(f64).new(maxnum, maxnum);
|
||||
}
|
||||
|
||||
const x2 = x * x;
|
||||
var a = 1.0 - x2 - (y * y);
|
||||
if (a == 0.0) {
|
||||
// overflow
|
||||
return Complex(f64).new(maxnum, maxnum);
|
||||
}
|
||||
|
||||
var t = 0.5 * math.atan2(f64, 2.0 * x, a);
|
||||
var w = redupif64(t);
|
||||
|
||||
t = y - 1.0;
|
||||
a = x2 + t * t;
|
||||
if (a == 0.0) {
|
||||
// overflow
|
||||
return Complex(f64).new(maxnum, maxnum);
|
||||
}
|
||||
|
||||
t = y + 1.0;
|
||||
a = (x2 + (t * t)) / a;
|
||||
return Complex(f64).new(w, 0.25 * math.ln(a));
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.catan32" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = atan(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 1.423679, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 0.086569, epsilon));
|
||||
}
|
||||
|
||||
test "complex.catan64" {
|
||||
const a = Complex(f64).new(5, 3);
|
||||
const c = atan(a);
|
||||
|
||||
debug.assert(math.approxEq(f64, c.re, 1.423679, epsilon));
|
||||
debug.assert(math.approxEq(f64, c.im, 0.086569, epsilon));
|
||||
}
|
||||
22
std/math/complex/atanh.zig
Normal file
22
std/math/complex/atanh.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn atanh(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const q = Complex(T).new(-z.im, z.re);
|
||||
const r = cmath.atan(q);
|
||||
return Complex(T).new(r.im, -r.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.catanh" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = atanh(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 0.146947, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 1.480870, epsilon));
|
||||
}
|
||||
17
std/math/complex/conj.zig
Normal file
17
std/math/complex/conj.zig
Normal file
@ -0,0 +1,17 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn conj(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
return Complex(T).new(z.re, -z.im);
|
||||
}
|
||||
|
||||
test "complex.conj" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = a.conjugate();
|
||||
|
||||
debug.assert(c.re == 5 and c.im == -3);
|
||||
}
|
||||
21
std/math/complex/cos.zig
Normal file
21
std/math/complex/cos.zig
Normal file
@ -0,0 +1,21 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn cos(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const p = Complex(T).new(-z.im, z.re);
|
||||
return cmath.cosh(p);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.ccos" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = cos(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 2.855815, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 9.606383, epsilon));
|
||||
}
|
||||
165
std/math/complex/cosh.zig
Normal file
165
std/math/complex/cosh.zig
Normal file
@ -0,0 +1,165 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
|
||||
|
||||
pub fn cosh(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
return switch (T) {
|
||||
f32 => cosh32(z),
|
||||
f64 => cosh64(z),
|
||||
else => @compileError("cosh not implemented for " ++ @typeName(z)),
|
||||
};
|
||||
}
|
||||
|
||||
fn cosh32(z: &const Complex(f32)) Complex(f32) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const hx = @bitCast(u32, x);
|
||||
const ix = hx & 0x7fffffff;
|
||||
|
||||
const hy = @bitCast(u32, y);
|
||||
const iy = hy & 0x7fffffff;
|
||||
|
||||
if (ix < 0x7f800000 and iy < 0x7f800000) {
|
||||
if (iy == 0) {
|
||||
return Complex(f32).new(math.cosh(x), y);
|
||||
}
|
||||
// small x: normal case
|
||||
if (ix < 0x41100000) {
|
||||
return Complex(f32).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y));
|
||||
}
|
||||
|
||||
// |x|>= 9, so cosh(x) ~= exp(|x|)
|
||||
if (ix < 0x42b17218) {
|
||||
// x < 88.7: exp(|x|) won't overflow
|
||||
const h = math.exp(math.fabs(x)) * 0.5;
|
||||
return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));
|
||||
}
|
||||
// x < 192.7: scale to avoid overflow
|
||||
else if (ix < 0x4340b1e7) {
|
||||
const v = Complex(f32).new(math.fabs(x), y);
|
||||
const r = ldexp_cexp(v, -1);
|
||||
return Complex(f32).new(x, y * math.copysign(f32, 1, x));
|
||||
}
|
||||
// x >= 192.7: result always overflows
|
||||
else {
|
||||
const h = 0x1p127 * x;
|
||||
return Complex(f32).new(h * h * math.cos(y), h * math.sin(y));
|
||||
}
|
||||
}
|
||||
|
||||
if (ix == 0 and iy >= 0x7f800000) {
|
||||
return Complex(f32).new(y - y, math.copysign(f32, 0, x * (y - y)));
|
||||
}
|
||||
|
||||
if (iy == 0 and ix >= 0x7f800000) {
|
||||
if (hx & 0x7fffff == 0) {
|
||||
return Complex(f32).new(x * x, math.copysign(f32, 0, x) * y);
|
||||
}
|
||||
return Complex(f32).new(x, math.copysign(f32, 0, (x + x) * y));
|
||||
}
|
||||
|
||||
if (ix < 0x7f800000 and iy >= 0x7f800000) {
|
||||
return Complex(f32).new(y - y, x * (y - y));
|
||||
}
|
||||
|
||||
if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) {
|
||||
if (iy >= 0x7f800000) {
|
||||
return Complex(f32).new(x * x, x * (y - y));
|
||||
}
|
||||
return Complex(f32).new((x * x) * math.cos(y), x * math.sin(y));
|
||||
}
|
||||
|
||||
return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y));
|
||||
}
|
||||
|
||||
fn cosh64(z: &const Complex(f64)) Complex(f64) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const fx = @bitCast(u64, x);
|
||||
const hx = u32(fx >> 32);
|
||||
const lx = @truncate(u32, fx);
|
||||
const ix = hx & 0x7fffffff;
|
||||
|
||||
const fy = @bitCast(u64, y);
|
||||
const hy = u32(fy >> 32);
|
||||
const ly = @truncate(u32, fy);
|
||||
const iy = hy & 0x7fffffff;
|
||||
|
||||
// nearly non-exceptional case where x, y are finite
|
||||
if (ix < 0x7ff00000 and iy < 0x7ff00000) {
|
||||
if (iy | ly == 0) {
|
||||
return Complex(f64).new(math.cosh(x), x * y);
|
||||
}
|
||||
// small x: normal case
|
||||
if (ix < 0x40360000) {
|
||||
return Complex(f64).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y));
|
||||
}
|
||||
|
||||
// |x|>= 22, so cosh(x) ~= exp(|x|)
|
||||
if (ix < 0x40862e42) {
|
||||
// x < 710: exp(|x|) won't overflow
|
||||
const h = math.exp(math.fabs(x)) * 0.5;
|
||||
return Complex(f64).new(h * math.cos(y), math.copysign(f64, h, x) * math.sin(y));
|
||||
}
|
||||
// x < 1455: scale to avoid overflow
|
||||
else if (ix < 0x4096bbaa) {
|
||||
const v = Complex(f64).new(math.fabs(x), y);
|
||||
const r = ldexp_cexp(v, -1);
|
||||
return Complex(f64).new(x, y * math.copysign(f64, 1, x));
|
||||
}
|
||||
// x >= 1455: result always overflows
|
||||
else {
|
||||
const h = 0x1p1023;
|
||||
return Complex(f64).new(h * h * math.cos(y), h * math.sin(y));
|
||||
}
|
||||
}
|
||||
|
||||
if (ix | lx == 0 and iy >= 0x7ff00000) {
|
||||
return Complex(f64).new(y - y, math.copysign(f64, 0, x * (y - y)));
|
||||
}
|
||||
|
||||
if (iy | ly == 0 and ix >= 0x7ff00000) {
|
||||
if ((hx & 0xfffff) | lx == 0) {
|
||||
return Complex(f64).new(x * x, math.copysign(f64, 0, x) * y);
|
||||
}
|
||||
return Complex(f64).new(x * x, math.copysign(f64, 0, (x + x) * y));
|
||||
}
|
||||
|
||||
if (ix < 0x7ff00000 and iy >= 0x7ff00000) {
|
||||
return Complex(f64).new(y - y, x * (y - y));
|
||||
}
|
||||
|
||||
if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) {
|
||||
if (iy >= 0x7ff00000) {
|
||||
return Complex(f64).new(x * x, x * (y - y));
|
||||
}
|
||||
return Complex(f64).new(x * x * math.cos(y), x * math.sin(y));
|
||||
}
|
||||
|
||||
return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y));
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.ccosh32" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = cosh(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, -73.467300, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 10.471557, epsilon));
|
||||
}
|
||||
|
||||
test "complex.ccosh64" {
|
||||
const a = Complex(f64).new(5, 3);
|
||||
const c = cosh(a);
|
||||
|
||||
debug.assert(math.approxEq(f64, c.re, -73.467300, epsilon));
|
||||
debug.assert(math.approxEq(f64, c.im, 10.471557, epsilon));
|
||||
}
|
||||
140
std/math/complex/exp.zig
Normal file
140
std/math/complex/exp.zig
Normal file
@ -0,0 +1,140 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
|
||||
|
||||
pub fn exp(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
|
||||
return switch (T) {
|
||||
f32 => exp32(z),
|
||||
f64 => exp64(z),
|
||||
else => @compileError("exp not implemented for " ++ @typeName(z)),
|
||||
};
|
||||
}
|
||||
|
||||
fn exp32(z: &const Complex(f32)) Complex(f32) {
|
||||
@setFloatMode(this, @import("builtin").FloatMode.Strict);
|
||||
|
||||
const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955
|
||||
const cexp_overflow = 0x43400074; // (max_exp - min_denom_exp) * ln2
|
||||
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const hy = @bitCast(u32, y) & 0x7fffffff;
|
||||
// cexp(x + i0) = exp(x) + i0
|
||||
if (hy == 0) {
|
||||
return Complex(f32).new(math.exp(x), y);
|
||||
}
|
||||
|
||||
const hx = @bitCast(u32, x);
|
||||
// cexp(0 + iy) = cos(y) + isin(y)
|
||||
if ((hx & 0x7fffffff) == 0) {
|
||||
return Complex(f32).new(math.cos(y), math.sin(y));
|
||||
}
|
||||
|
||||
if (hy >= 0x7f800000) {
|
||||
// cexp(finite|nan +- i inf|nan) = nan + i nan
|
||||
if ((hx & 0x7fffffff) != 0x7f800000) {
|
||||
return Complex(f32).new(y - y, y - y);
|
||||
}
|
||||
// cexp(-inf +- i inf|nan) = 0 + i0
|
||||
else if (hx & 0x80000000 != 0) {
|
||||
return Complex(f32).new(0, 0);
|
||||
}
|
||||
// cexp(+inf +- i inf|nan) = inf + i nan
|
||||
else {
|
||||
return Complex(f32).new(x, y - y);
|
||||
}
|
||||
}
|
||||
|
||||
// 88.7 <= x <= 192 so must scale
|
||||
if (hx >= exp_overflow and hx <= cexp_overflow) {
|
||||
return ldexp_cexp(z, 0);
|
||||
}
|
||||
// - x < exp_overflow => exp(x) won't overflow (common)
|
||||
// - x > cexp_overflow, so exp(x) * s overflows for s > 0
|
||||
// - x = +-inf
|
||||
// - x = nan
|
||||
else {
|
||||
const exp_x = math.exp(x);
|
||||
return Complex(f32).new(exp_x * math.cos(y), exp_x * math.sin(y));
|
||||
}
|
||||
}
|
||||
|
||||
fn exp64(z: &const Complex(f64)) Complex(f64) {
|
||||
const exp_overflow = 0x40862e42; // high bits of max_exp * ln2 ~= 710
|
||||
const cexp_overflow = 0x4096b8e4; // (max_exp - min_denorm_exp) * ln2
|
||||
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const fy = @bitCast(u64, y);
|
||||
const hy = u32(fy >> 32) & 0x7fffffff;
|
||||
const ly = @truncate(u32, fy);
|
||||
|
||||
// cexp(x + i0) = exp(x) + i0
|
||||
if (hy | ly == 0) {
|
||||
return Complex(f64).new(math.exp(x), y);
|
||||
}
|
||||
|
||||
const fx = @bitCast(u64, x);
|
||||
const hx = u32(fx >> 32);
|
||||
const lx = @truncate(u32, fx);
|
||||
|
||||
// cexp(0 + iy) = cos(y) + isin(y)
|
||||
if ((hx & 0x7fffffff) | lx == 0) {
|
||||
return Complex(f64).new(math.cos(y), math.sin(y));
|
||||
}
|
||||
|
||||
if (hy >= 0x7ff00000) {
|
||||
// cexp(finite|nan +- i inf|nan) = nan + i nan
|
||||
if (lx != 0 or (hx & 0x7fffffff) != 0x7ff00000) {
|
||||
return Complex(f64).new(y - y, y - y);
|
||||
}
|
||||
// cexp(-inf +- i inf|nan) = 0 + i0
|
||||
else if (hx & 0x80000000 != 0) {
|
||||
return Complex(f64).new(0, 0);
|
||||
}
|
||||
// cexp(+inf +- i inf|nan) = inf + i nan
|
||||
else {
|
||||
return Complex(f64).new(x, y - y);
|
||||
}
|
||||
}
|
||||
|
||||
// 709.7 <= x <= 1454.3 so must scale
|
||||
if (hx >= exp_overflow and hx <= cexp_overflow) {
|
||||
const r = ldexp_cexp(z, 0);
|
||||
return *r;
|
||||
}
|
||||
// - x < exp_overflow => exp(x) won't overflow (common)
|
||||
// - x > cexp_overflow, so exp(x) * s overflows for s > 0
|
||||
// - x = +-inf
|
||||
// - x = nan
|
||||
else {
|
||||
const exp_x = math.exp(x);
|
||||
return Complex(f64).new(exp_x * math.cos(y), exp_x * math.sin(y));
|
||||
}
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.cexp32" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = exp(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, -146.927917, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 20.944065, epsilon));
|
||||
}
|
||||
|
||||
test "complex.cexp64" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = exp(a);
|
||||
|
||||
debug.assert(math.approxEq(f64, c.re, -146.927917, epsilon));
|
||||
debug.assert(math.approxEq(f64, c.im, 20.944065, epsilon));
|
||||
}
|
||||
171
std/math/complex/index.zig
Normal file
171
std/math/complex/index.zig
Normal file
@ -0,0 +1,171 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
|
||||
pub const abs = @import("abs.zig").abs;
|
||||
pub const acosh = @import("acosh.zig").acosh;
|
||||
pub const acos = @import("acos.zig").acos;
|
||||
pub const arg = @import("arg.zig").arg;
|
||||
pub const asinh = @import("asinh.zig").asinh;
|
||||
pub const asin = @import("asin.zig").asin;
|
||||
pub const atanh = @import("atanh.zig").atanh;
|
||||
pub const atan = @import("atan.zig").atan;
|
||||
pub const conj = @import("conj.zig").conj;
|
||||
pub const cosh = @import("cosh.zig").cosh;
|
||||
pub const cos = @import("cos.zig").cos;
|
||||
pub const exp = @import("exp.zig").exp;
|
||||
pub const log = @import("log.zig").log;
|
||||
pub const pow = @import("pow.zig").pow;
|
||||
pub const proj = @import("proj.zig").proj;
|
||||
pub const sinh = @import("sinh.zig").sinh;
|
||||
pub const sin = @import("sin.zig").sin;
|
||||
pub const sqrt = @import("sqrt.zig").sqrt;
|
||||
pub const tanh = @import("tanh.zig").tanh;
|
||||
pub const tan = @import("tan.zig").tan;
|
||||
|
||||
pub fn Complex(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = this;
|
||||
|
||||
re: T,
|
||||
im: T,
|
||||
|
||||
pub fn new(re: T, im: T) Self {
|
||||
return Self {
|
||||
.re = re,
|
||||
.im = im,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add(self: &const Self, other: &const Self) Self {
|
||||
return Self {
|
||||
.re = self.re + other.re,
|
||||
.im = self.im + other.im,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn sub(self: &const Self, other: &const Self) Self {
|
||||
return Self {
|
||||
.re = self.re - other.re,
|
||||
.im = self.im - other.im,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn mul(self: &const Self, other: &const Self) Self {
|
||||
return Self {
|
||||
.re = self.re * other.re - self.im * other.im,
|
||||
.im = self.im * other.re + self.re * other.im,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn div(self: &const Self, other: &const Self) Self {
|
||||
const re_num = self.re * other.re + self.im * other.im;
|
||||
const im_num = self.im * other.re - self.re * other.im;
|
||||
const den = other.re * other.re + other.im * other.im;
|
||||
|
||||
return Self {
|
||||
.re = re_num / den,
|
||||
.im = im_num / den,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn conjugate(self: &const Self) Self {
|
||||
return Self {
|
||||
.re = self.re,
|
||||
.im = -self.im,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn reciprocal(self: &const Self) Self {
|
||||
const m = self.re * self.re + self.im * self.im;
|
||||
return Self {
|
||||
.re = self.re / m,
|
||||
.im = -self.im / m,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn magnitude(self: &const Self) T {
|
||||
return math.sqrt(self.re * self.re + self.im * self.im);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.add" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const b = Complex(f32).new(2, 7);
|
||||
const c = a.add(b);
|
||||
|
||||
debug.assert(c.re == 7 and c.im == 10);
|
||||
}
|
||||
|
||||
test "complex.sub" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const b = Complex(f32).new(2, 7);
|
||||
const c = a.sub(b);
|
||||
|
||||
debug.assert(c.re == 3 and c.im == -4);
|
||||
}
|
||||
|
||||
test "complex.mul" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const b = Complex(f32).new(2, 7);
|
||||
const c = a.mul(b);
|
||||
|
||||
debug.assert(c.re == -11 and c.im == 41);
|
||||
}
|
||||
|
||||
test "complex.div" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const b = Complex(f32).new(2, 7);
|
||||
const c = a.div(b);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, f32(31)/53, epsilon) and
|
||||
math.approxEq(f32, c.im, f32(-29)/53, epsilon));
|
||||
}
|
||||
|
||||
test "complex.conjugate" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = a.conjugate();
|
||||
|
||||
debug.assert(c.re == 5 and c.im == -3);
|
||||
}
|
||||
|
||||
test "complex.reciprocal" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = a.reciprocal();
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, f32(5)/34, epsilon) and
|
||||
math.approxEq(f32, c.im, f32(-3)/34, epsilon));
|
||||
}
|
||||
|
||||
test "complex.magnitude" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = a.magnitude();
|
||||
|
||||
debug.assert(math.approxEq(f32, c, 5.83095, epsilon));
|
||||
}
|
||||
|
||||
test "complex.cmath" {
|
||||
_ = @import("abs.zig");
|
||||
_ = @import("acosh.zig");
|
||||
_ = @import("acos.zig");
|
||||
_ = @import("arg.zig");
|
||||
_ = @import("asinh.zig");
|
||||
_ = @import("asin.zig");
|
||||
_ = @import("atanh.zig");
|
||||
_ = @import("atan.zig");
|
||||
_ = @import("conj.zig");
|
||||
_ = @import("cosh.zig");
|
||||
_ = @import("cos.zig");
|
||||
_ = @import("exp.zig");
|
||||
_ = @import("log.zig");
|
||||
_ = @import("pow.zig");
|
||||
_ = @import("proj.zig");
|
||||
_ = @import("sinh.zig");
|
||||
_ = @import("sin.zig");
|
||||
_ = @import("sqrt.zig");
|
||||
_ = @import("tanh.zig");
|
||||
_ = @import("tan.zig");
|
||||
}
|
||||
75
std/math/complex/ldexp.zig
Normal file
75
std/math/complex/ldexp.zig
Normal file
@ -0,0 +1,75 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn ldexp_cexp(z: var, expt: i32) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
|
||||
return switch (T) {
|
||||
f32 => ldexp_cexp32(z, expt),
|
||||
f64 => ldexp_cexp64(z, expt),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
fn frexp_exp32(x: f32, expt: &i32) f32 {
|
||||
const k = 235; // reduction constant
|
||||
const kln2 = 162.88958740; // k * ln2
|
||||
|
||||
const exp_x = math.exp(x - kln2);
|
||||
const hx = @bitCast(u32, exp_x);
|
||||
*expt = i32(hx >> 23) - (0x7f + 127) + k;
|
||||
return @bitCast(f32, (hx & 0x7fffff) | ((0x7f + 127) << 23));
|
||||
}
|
||||
|
||||
fn ldexp_cexp32(z: &const Complex(f32), expt: i32) Complex(f32) {
|
||||
var ex_expt: i32 = undefined;
|
||||
const exp_x = frexp_exp32(z.re, &ex_expt);
|
||||
const exptf = expt + ex_expt;
|
||||
|
||||
const half_expt1 = @divTrunc(exptf, 2);
|
||||
const scale1 = @bitCast(f32, (0x7f + half_expt1) << 23);
|
||||
|
||||
const half_expt2 = exptf - half_expt1;
|
||||
const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23);
|
||||
|
||||
return Complex(f32).new(
|
||||
math.cos(z.im) * exp_x * scale1 * scale2,
|
||||
math.sin(z.im) * exp_x * scale1 * scale2,
|
||||
);
|
||||
}
|
||||
|
||||
fn frexp_exp64(x: f64, expt: &i32) f64 {
|
||||
const k = 1799; // reduction constant
|
||||
const kln2 = 1246.97177782734161156; // k * ln2
|
||||
|
||||
const exp_x = math.exp(x - kln2);
|
||||
|
||||
const fx = @bitCast(u64, x);
|
||||
const hx = u32(fx >> 32);
|
||||
const lx = @truncate(u32, fx);
|
||||
|
||||
*expt = i32(hx >> 20) - (0x3ff + 1023) + k;
|
||||
|
||||
const high_word = (hx & 0xfffff) | ((0x3ff + 1023) << 20);
|
||||
return @bitCast(f64, (u64(high_word) << 32) | lx);
|
||||
}
|
||||
|
||||
fn ldexp_cexp64(z: &const Complex(f64), expt: i32) Complex(f64) {
|
||||
var ex_expt: i32 = undefined;
|
||||
const exp_x = frexp_exp64(z.re, &ex_expt);
|
||||
const exptf = i64(expt + ex_expt);
|
||||
|
||||
const half_expt1 = @divTrunc(exptf, 2);
|
||||
const scale1 = @bitCast(f64, (0x3ff + half_expt1) << 20);
|
||||
|
||||
const half_expt2 = exptf - half_expt1;
|
||||
const scale2 = @bitCast(f64, (0x3ff + half_expt2) << 20);
|
||||
|
||||
return Complex(f64).new(
|
||||
math.cos(z.im) * exp_x * scale1 * scale2,
|
||||
math.sin(z.im) * exp_x * scale1 * scale2,
|
||||
);
|
||||
}
|
||||
23
std/math/complex/log.zig
Normal file
23
std/math/complex/log.zig
Normal file
@ -0,0 +1,23 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn log(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const r = cmath.abs(z);
|
||||
const phi = cmath.arg(z);
|
||||
|
||||
return Complex(T).new(math.ln(r), phi);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.clog" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = log(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 1.763180, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 0.540419, epsilon));
|
||||
}
|
||||
22
std/math/complex/pow.zig
Normal file
22
std/math/complex/pow.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn pow(comptime T: type, z: &const T, c: &const T) T {
|
||||
const p = cmath.log(z);
|
||||
const q = c.mul(p);
|
||||
return cmath.exp(q);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.cpow" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const b = Complex(f32).new(2.3, -1.3);
|
||||
const c = pow(Complex(f32), a, b);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 58.049110, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, -101.003433, epsilon));
|
||||
}
|
||||
24
std/math/complex/proj.zig
Normal file
24
std/math/complex/proj.zig
Normal file
@ -0,0 +1,24 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn proj(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
|
||||
if (math.isInf(z.re) or math.isInf(z.im)) {
|
||||
return Complex(T).new(math.inf(T), math.copysign(T, 0, z.re));
|
||||
}
|
||||
|
||||
return Complex(T).new(z.re, z.im);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.cproj" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = proj(a);
|
||||
|
||||
debug.assert(c.re == 5 and c.im == 3);
|
||||
}
|
||||
22
std/math/complex/sin.zig
Normal file
22
std/math/complex/sin.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn sin(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const p = Complex(T).new(-z.im, z.re);
|
||||
const q = cmath.sinh(p);
|
||||
return Complex(T).new(q.im, -q.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.csin" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = sin(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, -9.654126, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 2.841692, epsilon));
|
||||
}
|
||||
164
std/math/complex/sinh.zig
Normal file
164
std/math/complex/sinh.zig
Normal file
@ -0,0 +1,164 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
|
||||
|
||||
pub fn sinh(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
return switch (T) {
|
||||
f32 => sinh32(z),
|
||||
f64 => sinh64(z),
|
||||
else => @compileError("tan not implemented for " ++ @typeName(z)),
|
||||
};
|
||||
}
|
||||
|
||||
fn sinh32(z: &const Complex(f32)) Complex(f32) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const hx = @bitCast(u32, x);
|
||||
const ix = hx & 0x7fffffff;
|
||||
|
||||
const hy = @bitCast(u32, y);
|
||||
const iy = hy & 0x7fffffff;
|
||||
|
||||
if (ix < 0x7f800000 and iy < 0x7f800000) {
|
||||
if (iy == 0) {
|
||||
return Complex(f32).new(math.sinh(x), y);
|
||||
}
|
||||
// small x: normal case
|
||||
if (ix < 0x41100000) {
|
||||
return Complex(f32).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
|
||||
}
|
||||
|
||||
// |x|>= 9, so cosh(x) ~= exp(|x|)
|
||||
if (ix < 0x42b17218) {
|
||||
// x < 88.7: exp(|x|) won't overflow
|
||||
const h = math.exp(math.fabs(x)) * 0.5;
|
||||
return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));
|
||||
}
|
||||
// x < 192.7: scale to avoid overflow
|
||||
else if (ix < 0x4340b1e7) {
|
||||
const v = Complex(f32).new(math.fabs(x), y);
|
||||
const r = ldexp_cexp(v, -1);
|
||||
return Complex(f32).new(x * math.copysign(f32, 1, x), y);
|
||||
}
|
||||
// x >= 192.7: result always overflows
|
||||
else {
|
||||
const h = 0x1p127 * x;
|
||||
return Complex(f32).new(h * math.cos(y), h * h * math.sin(y));
|
||||
}
|
||||
}
|
||||
|
||||
if (ix == 0 and iy >= 0x7f800000) {
|
||||
return Complex(f32).new(math.copysign(f32, 0, x * (y - y)), y - y);
|
||||
}
|
||||
|
||||
if (iy == 0 and ix >= 0x7f800000) {
|
||||
if (hx & 0x7fffff == 0) {
|
||||
return Complex(f32).new(x, y);
|
||||
}
|
||||
return Complex(f32).new(x, math.copysign(f32, 0, y));
|
||||
}
|
||||
|
||||
if (ix < 0x7f800000 and iy >= 0x7f800000) {
|
||||
return Complex(f32).new(y - y, x * (y - y));
|
||||
}
|
||||
|
||||
if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) {
|
||||
if (iy >= 0x7f800000) {
|
||||
return Complex(f32).new(x * x, x * (y - y));
|
||||
}
|
||||
return Complex(f32).new(x * math.cos(y), math.inf_f32 * math.sin(y));
|
||||
}
|
||||
|
||||
return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y));
|
||||
}
|
||||
|
||||
fn sinh64(z: &const Complex(f64)) Complex(f64) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const fx = @bitCast(u64, x);
|
||||
const hx = u32(fx >> 32);
|
||||
const lx = @truncate(u32, fx);
|
||||
const ix = hx & 0x7fffffff;
|
||||
|
||||
const fy = @bitCast(u64, y);
|
||||
const hy = u32(fy >> 32);
|
||||
const ly = @truncate(u32, fy);
|
||||
const iy = hy & 0x7fffffff;
|
||||
|
||||
if (ix < 0x7ff00000 and iy < 0x7ff00000) {
|
||||
if (iy | ly == 0) {
|
||||
return Complex(f64).new(math.sinh(x), y);
|
||||
}
|
||||
// small x: normal case
|
||||
if (ix < 0x40360000) {
|
||||
return Complex(f64).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
|
||||
}
|
||||
|
||||
// |x|>= 22, so cosh(x) ~= exp(|x|)
|
||||
if (ix < 0x40862e42) {
|
||||
// x < 710: exp(|x|) won't overflow
|
||||
const h = math.exp(math.fabs(x)) * 0.5;
|
||||
return Complex(f64).new(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y));
|
||||
}
|
||||
// x < 1455: scale to avoid overflow
|
||||
else if (ix < 0x4096bbaa) {
|
||||
const v = Complex(f64).new(math.fabs(x), y);
|
||||
const r = ldexp_cexp(v, -1);
|
||||
return Complex(f64).new(x * math.copysign(f64, 1, x), y);
|
||||
}
|
||||
// x >= 1455: result always overflows
|
||||
else {
|
||||
const h = 0x1p1023 * x;
|
||||
return Complex(f64).new(h * math.cos(y), h * h * math.sin(y));
|
||||
}
|
||||
}
|
||||
|
||||
if (ix | lx == 0 and iy >= 0x7ff00000) {
|
||||
return Complex(f64).new(math.copysign(f64, 0, x * (y - y)), y - y);
|
||||
}
|
||||
|
||||
if (iy | ly == 0 and ix >= 0x7ff00000) {
|
||||
if ((hx & 0xfffff) | lx == 0) {
|
||||
return Complex(f64).new(x, y);
|
||||
}
|
||||
return Complex(f64).new(x, math.copysign(f64, 0, y));
|
||||
}
|
||||
|
||||
if (ix < 0x7ff00000 and iy >= 0x7ff00000) {
|
||||
return Complex(f64).new(y - y, x * (y - y));
|
||||
}
|
||||
|
||||
if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) {
|
||||
if (iy >= 0x7ff00000) {
|
||||
return Complex(f64).new(x * x, x * (y - y));
|
||||
}
|
||||
return Complex(f64).new(x * math.cos(y), math.inf_f64 * math.sin(y));
|
||||
}
|
||||
|
||||
return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y));
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.csinh32" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = sinh(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, -73.460617, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 10.472508, epsilon));
|
||||
}
|
||||
|
||||
test "complex.csinh64" {
|
||||
const a = Complex(f64).new(5, 3);
|
||||
const c = sinh(a);
|
||||
|
||||
debug.assert(math.approxEq(f64, c.re, -73.460617, epsilon));
|
||||
debug.assert(math.approxEq(f64, c.im, 10.472508, epsilon));
|
||||
}
|
||||
133
std/math/complex/sqrt.zig
Normal file
133
std/math/complex/sqrt.zig
Normal file
@ -0,0 +1,133 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
// TODO when #733 is solved this can be @typeOf(z) instead of Complex(@typeOf(z.re))
|
||||
pub fn sqrt(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
|
||||
return switch (T) {
|
||||
f32 => sqrt32(z),
|
||||
f64 => sqrt64(z),
|
||||
else => @compileError("sqrt not implemented for " ++ @typeName(z)),
|
||||
};
|
||||
}
|
||||
|
||||
fn sqrt32(z: &const Complex(f32)) Complex(f32) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
if (x == 0 and y == 0) {
|
||||
return Complex(f32).new(0, y);
|
||||
}
|
||||
if (math.isInf(y)) {
|
||||
return Complex(f32).new(math.inf(f32), y);
|
||||
}
|
||||
if (math.isNan(x)) {
|
||||
// raise invalid if y is not nan
|
||||
const t = (y - y) / (y - y);
|
||||
return Complex(f32).new(x, t);
|
||||
}
|
||||
if (math.isInf(x)) {
|
||||
// sqrt(inf + i nan) = inf + nan i
|
||||
// sqrt(inf + iy) = inf + i0
|
||||
// sqrt(-inf + i nan) = nan +- inf i
|
||||
// sqrt(-inf + iy) = 0 + inf i
|
||||
if (math.signbit(x)) {
|
||||
return Complex(f32).new(math.fabs(x - y), math.copysign(f32, x, y));
|
||||
} else {
|
||||
return Complex(f32).new(x, math.copysign(f32, y - y, y));
|
||||
}
|
||||
}
|
||||
|
||||
// y = nan special case is handled fine below
|
||||
|
||||
// double-precision avoids overflow with correct rounding.
|
||||
const dx = f64(x);
|
||||
const dy = f64(y);
|
||||
|
||||
if (dx >= 0) {
|
||||
const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5);
|
||||
return Complex(f32).new(f32(t), f32(dy / (2.0 * t)));
|
||||
} else {
|
||||
const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5);
|
||||
return Complex(f32).new(f32(math.fabs(y) / (2.0 * t)), f32(math.copysign(f64, t, y)));
|
||||
}
|
||||
}
|
||||
|
||||
fn sqrt64(z: &const Complex(f64)) Complex(f64) {
|
||||
// may encounter overflow for im,re >= DBL_MAX / (1 + sqrt(2))
|
||||
const threshold = 0x1.a827999fcef32p+1022;
|
||||
|
||||
var x = z.re;
|
||||
var y = z.im;
|
||||
|
||||
if (x == 0 and y == 0) {
|
||||
return Complex(f64).new(0, y);
|
||||
}
|
||||
if (math.isInf(y)) {
|
||||
return Complex(f64).new(math.inf(f64), y);
|
||||
}
|
||||
if (math.isNan(x)) {
|
||||
// raise invalid if y is not nan
|
||||
const t = (y - y) / (y - y);
|
||||
return Complex(f64).new(x, t);
|
||||
}
|
||||
if (math.isInf(x)) {
|
||||
// sqrt(inf + i nan) = inf + nan i
|
||||
// sqrt(inf + iy) = inf + i0
|
||||
// sqrt(-inf + i nan) = nan +- inf i
|
||||
// sqrt(-inf + iy) = 0 + inf i
|
||||
if (math.signbit(x)) {
|
||||
return Complex(f64).new(math.fabs(x - y), math.copysign(f64, x, y));
|
||||
} else {
|
||||
return Complex(f64).new(x, math.copysign(f64, y - y, y));
|
||||
}
|
||||
}
|
||||
|
||||
// y = nan special case is handled fine below
|
||||
|
||||
// scale to avoid overflow
|
||||
var scale = false;
|
||||
if (math.fabs(x) >= threshold or math.fabs(y) >= threshold) {
|
||||
x *= 0.25;
|
||||
y *= 0.25;
|
||||
scale = true;
|
||||
}
|
||||
|
||||
var result: Complex(f64) = undefined;
|
||||
if (x >= 0) {
|
||||
const t = math.sqrt((x + math.hypot(f64, x, y)) * 0.5);
|
||||
result = Complex(f64).new(t, y / (2.0 * t));
|
||||
} else {
|
||||
const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5);
|
||||
result = Complex(f64).new(math.fabs(y) / (2.0 * t), math.copysign(f64, t, y));
|
||||
}
|
||||
|
||||
if (scale) {
|
||||
result.re *= 2;
|
||||
result.im *= 2;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.csqrt32" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = sqrt(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 2.327117, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 0.644574, epsilon));
|
||||
}
|
||||
|
||||
test "complex.csqrt64" {
|
||||
const a = Complex(f64).new(5, 3);
|
||||
const c = sqrt(a);
|
||||
|
||||
debug.assert(math.approxEq(f64, c.re, 2.3271175190399496, epsilon));
|
||||
debug.assert(math.approxEq(f64, c.im, 0.6445742373246469, epsilon));
|
||||
}
|
||||
22
std/math/complex/tan.zig
Normal file
22
std/math/complex/tan.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn tan(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
const q = Complex(T).new(-z.im, z.re);
|
||||
const r = cmath.tanh(q);
|
||||
return Complex(T).new(r.im, -r.re);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.ctan" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = tan(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, -0.002708233, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, 1.004165, epsilon));
|
||||
}
|
||||
111
std/math/complex/tanh.zig
Normal file
111
std/math/complex/tanh.zig
Normal file
@ -0,0 +1,111 @@
|
||||
const std = @import("../../index.zig");
|
||||
const debug = std.debug;
|
||||
const math = std.math;
|
||||
const cmath = math.complex;
|
||||
const Complex = cmath.Complex;
|
||||
|
||||
pub fn tanh(z: var) Complex(@typeOf(z.re)) {
|
||||
const T = @typeOf(z.re);
|
||||
return switch (T) {
|
||||
f32 => tanh32(z),
|
||||
f64 => tanh64(z),
|
||||
else => @compileError("tan not implemented for " ++ @typeName(z)),
|
||||
};
|
||||
}
|
||||
|
||||
fn tanh32(z: &const Complex(f32)) Complex(f32) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const hx = @bitCast(u32, x);
|
||||
const ix = hx & 0x7fffffff;
|
||||
|
||||
if (ix >= 0x7f800000) {
|
||||
if (ix & 0x7fffff != 0) {
|
||||
const r = if (y == 0) y else x * y;
|
||||
return Complex(f32).new(x, r);
|
||||
}
|
||||
const xx = @bitCast(f32, hx - 0x40000000);
|
||||
const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y);
|
||||
return Complex(f32).new(xx, math.copysign(f32, 0, r));
|
||||
}
|
||||
|
||||
if (!math.isFinite(y)) {
|
||||
const r = if (ix != 0) y - y else x;
|
||||
return Complex(f32).new(r, y - y);
|
||||
}
|
||||
|
||||
// x >= 11
|
||||
if (ix >= 0x41300000) {
|
||||
const exp_mx = math.exp(-math.fabs(x));
|
||||
return Complex(f32).new(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);
|
||||
}
|
||||
|
||||
// Kahan's algorithm
|
||||
const t = math.tan(y);
|
||||
const beta = 1.0 + t * t;
|
||||
const s = math.sinh(x);
|
||||
const rho = math.sqrt(1 + s * s);
|
||||
const den = 1 + beta * s * s;
|
||||
|
||||
return Complex(f32).new((beta * rho * s) / den, t / den);
|
||||
}
|
||||
|
||||
fn tanh64(z: &const Complex(f64)) Complex(f64) {
|
||||
const x = z.re;
|
||||
const y = z.im;
|
||||
|
||||
const fx = @bitCast(u64, x);
|
||||
const hx = u32(fx >> 32);
|
||||
const lx = @truncate(u32, fx);
|
||||
const ix = hx & 0x7fffffff;
|
||||
|
||||
if (ix >= 0x7ff00000) {
|
||||
if ((ix & 0x7fffff) | lx != 0) {
|
||||
const r = if (y == 0) y else x * y;
|
||||
return Complex(f64).new(x, r);
|
||||
}
|
||||
|
||||
const xx = @bitCast(f64, (u64(hx - 0x40000000) << 32) | lx);
|
||||
const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y);
|
||||
return Complex(f64).new(xx, math.copysign(f64, 0, r));
|
||||
}
|
||||
|
||||
if (!math.isFinite(y)) {
|
||||
const r = if (ix != 0) y - y else x;
|
||||
return Complex(f64).new(r, y - y);
|
||||
}
|
||||
|
||||
// x >= 22
|
||||
if (ix >= 0x40360000) {
|
||||
const exp_mx = math.exp(-math.fabs(x));
|
||||
return Complex(f64).new(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);
|
||||
}
|
||||
|
||||
// Kahan's algorithm
|
||||
const t = math.tan(y);
|
||||
const beta = 1.0 + t * t;
|
||||
const s = math.sinh(x);
|
||||
const rho = math.sqrt(1 + s * s);
|
||||
const den = 1 + beta * s * s;
|
||||
|
||||
return Complex(f64).new((beta * rho * s) / den, t / den);
|
||||
}
|
||||
|
||||
const epsilon = 0.0001;
|
||||
|
||||
test "complex.ctanh32" {
|
||||
const a = Complex(f32).new(5, 3);
|
||||
const c = tanh(a);
|
||||
|
||||
debug.assert(math.approxEq(f32, c.re, 0.999913, epsilon));
|
||||
debug.assert(math.approxEq(f32, c.im, -0.000025, epsilon));
|
||||
}
|
||||
|
||||
test "complex.ctanh64" {
|
||||
const a = Complex(f64).new(5, 3);
|
||||
const c = tanh(a);
|
||||
|
||||
debug.assert(math.approxEq(f64, c.re, 0.999913, epsilon));
|
||||
debug.assert(math.approxEq(f64, c.im, -0.000025, epsilon));
|
||||
}
|
||||
@ -6,6 +6,7 @@
|
||||
const std = @import("../index.zig");
|
||||
const math = std.math;
|
||||
const assert = std.debug.assert;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub fn exp(x: var) @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
@ -17,6 +18,8 @@ pub fn exp(x: var) @typeOf(x) {
|
||||
}
|
||||
|
||||
fn exp32(x_: f32) f32 {
|
||||
@setFloatMode(this, builtin.FloatMode.Strict);
|
||||
|
||||
const half = []f32 { 0.5, -0.5 };
|
||||
const ln2hi = 6.9314575195e-1;
|
||||
const ln2lo = 1.4286067653e-6;
|
||||
@ -94,6 +97,8 @@ fn exp32(x_: f32) f32 {
|
||||
}
|
||||
|
||||
fn exp64(x_: f64) f64 {
|
||||
@setFloatMode(this, builtin.FloatMode.Strict);
|
||||
|
||||
const half = []const f64 { 0.5, -0.5 };
|
||||
const ln2hi: f64 = 6.93147180369123816490e-01;
|
||||
const ln2lo: f64 = 1.90821492927058770002e-10;
|
||||
|
||||
@ -129,6 +129,9 @@ pub const cos = @import("cos.zig").cos;
|
||||
pub const sin = @import("sin.zig").sin;
|
||||
pub const tan = @import("tan.zig").tan;
|
||||
|
||||
pub const complex = @import("complex/index.zig");
|
||||
pub const Complex = complex.Complex;
|
||||
|
||||
test "math" {
|
||||
_ = @import("nan.zig");
|
||||
_ = @import("isnan.zig");
|
||||
@ -172,6 +175,8 @@ test "math" {
|
||||
_ = @import("sin.zig");
|
||||
_ = @import("cos.zig");
|
||||
_ = @import("tan.zig");
|
||||
|
||||
_ = @import("complex/index.zig");
|
||||
}
|
||||
|
||||
|
||||
@ -553,6 +558,32 @@ test "math.floorPowerOfTwo" {
|
||||
comptime testFloorPowerOfTwo();
|
||||
}
|
||||
|
||||
pub fn log2_int(comptime T: type, x: T) Log2Int(T) {
|
||||
assert(x != 0);
|
||||
return Log2Int(T)(T.bit_count - 1 - @clz(x));
|
||||
}
|
||||
|
||||
pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) {
|
||||
assert(x != 0);
|
||||
const log2_val = log2_int(T, x);
|
||||
if (T(1) << log2_val == x)
|
||||
return log2_val;
|
||||
return log2_val + 1;
|
||||
}
|
||||
|
||||
test "std.math.log2_int_ceil" {
|
||||
assert(log2_int_ceil(u32, 1) == 0);
|
||||
assert(log2_int_ceil(u32, 2) == 1);
|
||||
assert(log2_int_ceil(u32, 3) == 2);
|
||||
assert(log2_int_ceil(u32, 4) == 2);
|
||||
assert(log2_int_ceil(u32, 5) == 3);
|
||||
assert(log2_int_ceil(u32, 6) == 3);
|
||||
assert(log2_int_ceil(u32, 7) == 3);
|
||||
assert(log2_int_ceil(u32, 8) == 3);
|
||||
assert(log2_int_ceil(u32, 9) == 4);
|
||||
assert(log2_int_ceil(u32, 10) == 4);
|
||||
}
|
||||
|
||||
fn testFloorPowerOfTwo() void {
|
||||
assert(floorPowerOfTwo(u32, 63) == 32);
|
||||
assert(floorPowerOfTwo(u32, 64) == 64);
|
||||
|
||||
@ -89,6 +89,8 @@ pub fn ln_32(x_: f32) f32 {
|
||||
}
|
||||
|
||||
pub fn ln_64(x_: f64) f64 {
|
||||
@setFloatMode(this, @import("builtin").FloatMode.Strict);
|
||||
|
||||
const ln2_hi: f64 = 6.93147180369123816490e-01;
|
||||
const ln2_lo: f64 = 1.90821492927058770002e-10;
|
||||
const Lg1: f64 = 6.666666666666735130e-01;
|
||||
|
||||
@ -31,17 +31,12 @@ pub fn log2(x: var) @typeOf(x) {
|
||||
return result;
|
||||
},
|
||||
TypeId.Int => {
|
||||
return log2_int(T, x);
|
||||
return math.log2_int(T, x);
|
||||
},
|
||||
else => @compileError("log2 not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log2_int(comptime T: type, x: T) T {
|
||||
assert(x != 0);
|
||||
return T.bit_count - 1 - T(@clz(x));
|
||||
}
|
||||
|
||||
pub fn log2_32(x_: f32) f32 {
|
||||
const ivln2hi: f32 = 1.4428710938e+00;
|
||||
const ivln2lo: f32 = -1.7605285393e-04;
|
||||
|
||||
@ -14,26 +14,8 @@ const TypeId = builtin.TypeId;
|
||||
pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) {
|
||||
const T = @typeOf(x);
|
||||
switch (@typeId(T)) {
|
||||
TypeId.FloatLiteral => {
|
||||
return T(sqrt64(x));
|
||||
},
|
||||
TypeId.Float => {
|
||||
switch (T) {
|
||||
f32 => {
|
||||
switch (builtin.arch) {
|
||||
builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt32(x),
|
||||
else => return sqrt32(x),
|
||||
}
|
||||
},
|
||||
f64 => {
|
||||
switch (builtin.arch) {
|
||||
builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt64(x),
|
||||
else => return sqrt64(x),
|
||||
}
|
||||
},
|
||||
else => @compileError("sqrt not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
},
|
||||
TypeId.FloatLiteral => return T(@sqrt(f64, x)), // TODO upgrade to f128
|
||||
TypeId.Float => return @sqrt(T, x),
|
||||
TypeId.IntLiteral => comptime {
|
||||
if (x > @maxValue(u128)) {
|
||||
@compileError("sqrt not implemented for comptime_int greater than 128 bits");
|
||||
@ -43,269 +25,58 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ
|
||||
}
|
||||
return T(sqrt_int(u128, x));
|
||||
},
|
||||
TypeId.Int => {
|
||||
return sqrt_int(T, x);
|
||||
},
|
||||
TypeId.Int => return sqrt_int(T, x),
|
||||
else => @compileError("sqrt not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
}
|
||||
|
||||
fn sqrt32(x: f32) f32 {
|
||||
const tiny: f32 = 1.0e-30;
|
||||
const sign: i32 = @bitCast(i32, u32(0x80000000));
|
||||
var ix: i32 = @bitCast(i32, x);
|
||||
|
||||
if ((ix & 0x7F800000) == 0x7F800000) {
|
||||
return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
|
||||
}
|
||||
|
||||
// zero
|
||||
if (ix <= 0) {
|
||||
if (ix & ~sign == 0) {
|
||||
return x; // sqrt (+-0) = +-0
|
||||
}
|
||||
if (ix < 0) {
|
||||
return math.snan(f32);
|
||||
}
|
||||
}
|
||||
|
||||
// normalize
|
||||
var m = ix >> 23;
|
||||
if (m == 0) {
|
||||
// subnormal
|
||||
var i: i32 = 0;
|
||||
while (ix & 0x00800000 == 0) : (i += 1) {
|
||||
ix <<= 1;
|
||||
}
|
||||
m -= i - 1;
|
||||
}
|
||||
|
||||
m -= 127; // unbias exponent
|
||||
ix = (ix & 0x007FFFFF) | 0x00800000;
|
||||
|
||||
if (m & 1 != 0) { // odd m, double x to even
|
||||
ix += ix;
|
||||
}
|
||||
|
||||
m >>= 1; // m = [m / 2]
|
||||
|
||||
// sqrt(x) bit by bit
|
||||
ix += ix;
|
||||
var q: i32 = 0; // q = sqrt(x)
|
||||
var s: i32 = 0;
|
||||
var r: i32 = 0x01000000; // r = moving bit right -> left
|
||||
|
||||
while (r != 0) {
|
||||
const t = s + r;
|
||||
if (t <= ix) {
|
||||
s = t + r;
|
||||
ix -= t;
|
||||
q += r;
|
||||
}
|
||||
ix += ix;
|
||||
r >>= 1;
|
||||
}
|
||||
|
||||
// floating add to find rounding direction
|
||||
if (ix != 0) {
|
||||
var z = 1.0 - tiny; // inexact
|
||||
if (z >= 1.0) {
|
||||
z = 1.0 + tiny;
|
||||
if (z > 1.0) {
|
||||
q += 2;
|
||||
} else {
|
||||
if (q & 1 != 0) {
|
||||
q += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ix = (q >> 1) + 0x3f000000;
|
||||
ix += m << 23;
|
||||
return @bitCast(f32, ix);
|
||||
}
|
||||
|
||||
// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
|
||||
// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
|
||||
// potentially some edge cases remaining that are not handled in the same way.
|
||||
fn sqrt64(x: f64) f64 {
|
||||
const tiny: f64 = 1.0e-300;
|
||||
const sign: u32 = 0x80000000;
|
||||
const u = @bitCast(u64, x);
|
||||
|
||||
var ix0 = u32(u >> 32);
|
||||
var ix1 = u32(u & 0xFFFFFFFF);
|
||||
|
||||
// sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
|
||||
if (ix0 & 0x7FF00000 == 0x7FF00000) {
|
||||
return x * x + x;
|
||||
}
|
||||
|
||||
// sqrt(+-0) = +-0
|
||||
if (x == 0.0) {
|
||||
return x;
|
||||
}
|
||||
// sqrt(-ve) = snan
|
||||
if (ix0 & sign != 0) {
|
||||
return math.snan(f64);
|
||||
}
|
||||
|
||||
// normalize x
|
||||
var m = i32(ix0 >> 20);
|
||||
if (m == 0) {
|
||||
// subnormal
|
||||
while (ix0 == 0) {
|
||||
m -= 21;
|
||||
ix0 |= ix1 >> 11;
|
||||
ix1 <<= 21;
|
||||
}
|
||||
|
||||
// subnormal
|
||||
var i: u32 = 0;
|
||||
while (ix0 & 0x00100000 == 0) : (i += 1) {
|
||||
ix0 <<= 1;
|
||||
}
|
||||
m -= i32(i) - 1;
|
||||
ix0 |= ix1 >> u5(32 - i);
|
||||
ix1 <<= u5(i);
|
||||
}
|
||||
|
||||
// unbias exponent
|
||||
m -= 1023;
|
||||
ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
|
||||
if (m & 1 != 0) {
|
||||
ix0 += ix0 + (ix1 >> 31);
|
||||
ix1 = ix1 +% ix1;
|
||||
}
|
||||
m >>= 1;
|
||||
|
||||
// sqrt(x) bit by bit
|
||||
ix0 += ix0 + (ix1 >> 31);
|
||||
ix1 = ix1 +% ix1;
|
||||
|
||||
var q: u32 = 0;
|
||||
var q1: u32 = 0;
|
||||
var s0: u32 = 0;
|
||||
var s1: u32 = 0;
|
||||
var r: u32 = 0x00200000;
|
||||
var t: u32 = undefined;
|
||||
var t1: u32 = undefined;
|
||||
|
||||
while (r != 0) {
|
||||
t = s0 +% r;
|
||||
if (t <= ix0) {
|
||||
s0 = t + r;
|
||||
ix0 -= t;
|
||||
q += r;
|
||||
}
|
||||
ix0 = ix0 +% ix0 +% (ix1 >> 31);
|
||||
ix1 = ix1 +% ix1;
|
||||
r >>= 1;
|
||||
}
|
||||
|
||||
r = sign;
|
||||
while (r != 0) {
|
||||
t = s1 +% r;
|
||||
t = s0;
|
||||
if (t < ix0 or (t == ix0 and t1 <= ix1)) {
|
||||
s1 = t1 +% r;
|
||||
if (t1 & sign == sign and s1 & sign == 0) {
|
||||
s0 += 1;
|
||||
}
|
||||
ix0 -= t;
|
||||
if (ix1 < t1) {
|
||||
ix0 -= 1;
|
||||
}
|
||||
ix1 = ix1 -% t1;
|
||||
q1 += r;
|
||||
}
|
||||
ix0 = ix0 +% ix0 +% (ix1 >> 31);
|
||||
ix1 = ix1 +% ix1;
|
||||
r >>= 1;
|
||||
}
|
||||
|
||||
// rounding direction
|
||||
if (ix0 | ix1 != 0) {
|
||||
var z = 1.0 - tiny; // raise inexact
|
||||
if (z >= 1.0) {
|
||||
z = 1.0 + tiny;
|
||||
if (q1 == 0xFFFFFFFF) {
|
||||
q1 = 0;
|
||||
q += 1;
|
||||
} else if (z > 1.0) {
|
||||
if (q1 == 0xFFFFFFFE) {
|
||||
q += 1;
|
||||
}
|
||||
q1 += 2;
|
||||
} else {
|
||||
q1 += q1 & 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ix0 = (q >> 1) + 0x3FE00000;
|
||||
ix1 = q1 >> 1;
|
||||
if (q & 1 != 0) {
|
||||
ix1 |= 0x80000000;
|
||||
}
|
||||
|
||||
// NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
|
||||
// behaviour at least.
|
||||
var iix0 = i32(ix0);
|
||||
iix0 = iix0 +% (m << 20);
|
||||
|
||||
const uz = (u64(iix0) << 32) | ix1;
|
||||
return @bitCast(f64, uz);
|
||||
}
|
||||
|
||||
test "math.sqrt" {
|
||||
assert(sqrt(f32(0.0)) == sqrt32(0.0));
|
||||
assert(sqrt(f64(0.0)) == sqrt64(0.0));
|
||||
assert(sqrt(f32(0.0)) == @sqrt(f32, 0.0));
|
||||
assert(sqrt(f64(0.0)) == @sqrt(f64, 0.0));
|
||||
}
|
||||
|
||||
test "math.sqrt32" {
|
||||
const epsilon = 0.000001;
|
||||
|
||||
assert(sqrt32(0.0) == 0.0);
|
||||
assert(math.approxEq(f32, sqrt32(2.0), 1.414214, epsilon));
|
||||
assert(math.approxEq(f32, sqrt32(3.6), 1.897367, epsilon));
|
||||
assert(sqrt32(4.0) == 2.0);
|
||||
assert(math.approxEq(f32, sqrt32(7.539840), 2.745877, epsilon));
|
||||
assert(math.approxEq(f32, sqrt32(19.230934), 4.385309, epsilon));
|
||||
assert(sqrt32(64.0) == 8.0);
|
||||
assert(math.approxEq(f32, sqrt32(64.1), 8.006248, epsilon));
|
||||
assert(math.approxEq(f32, sqrt32(8942.230469), 94.563370, epsilon));
|
||||
assert(@sqrt(f32, 0.0) == 0.0);
|
||||
assert(math.approxEq(f32, @sqrt(f32, 2.0), 1.414214, epsilon));
|
||||
assert(math.approxEq(f32, @sqrt(f32, 3.6), 1.897367, epsilon));
|
||||
assert(@sqrt(f32, 4.0) == 2.0);
|
||||
assert(math.approxEq(f32, @sqrt(f32, 7.539840), 2.745877, epsilon));
|
||||
assert(math.approxEq(f32, @sqrt(f32, 19.230934), 4.385309, epsilon));
|
||||
assert(@sqrt(f32, 64.0) == 8.0);
|
||||
assert(math.approxEq(f32, @sqrt(f32, 64.1), 8.006248, epsilon));
|
||||
assert(math.approxEq(f32, @sqrt(f32, 8942.230469), 94.563370, epsilon));
|
||||
}
|
||||
|
||||
test "math.sqrt64" {
|
||||
const epsilon = 0.000001;
|
||||
|
||||
assert(sqrt64(0.0) == 0.0);
|
||||
assert(math.approxEq(f64, sqrt64(2.0), 1.414214, epsilon));
|
||||
assert(math.approxEq(f64, sqrt64(3.6), 1.897367, epsilon));
|
||||
assert(sqrt64(4.0) == 2.0);
|
||||
assert(math.approxEq(f64, sqrt64(7.539840), 2.745877, epsilon));
|
||||
assert(math.approxEq(f64, sqrt64(19.230934), 4.385309, epsilon));
|
||||
assert(sqrt64(64.0) == 8.0);
|
||||
assert(math.approxEq(f64, sqrt64(64.1), 8.006248, epsilon));
|
||||
assert(math.approxEq(f64, sqrt64(8942.230469), 94.563367, epsilon));
|
||||
assert(@sqrt(f64, 0.0) == 0.0);
|
||||
assert(math.approxEq(f64, @sqrt(f64, 2.0), 1.414214, epsilon));
|
||||
assert(math.approxEq(f64, @sqrt(f64, 3.6), 1.897367, epsilon));
|
||||
assert(@sqrt(f64, 4.0) == 2.0);
|
||||
assert(math.approxEq(f64, @sqrt(f64, 7.539840), 2.745877, epsilon));
|
||||
assert(math.approxEq(f64, @sqrt(f64, 19.230934), 4.385309, epsilon));
|
||||
assert(@sqrt(f64, 64.0) == 8.0);
|
||||
assert(math.approxEq(f64, @sqrt(f64, 64.1), 8.006248, epsilon));
|
||||
assert(math.approxEq(f64, @sqrt(f64, 8942.230469), 94.563367, epsilon));
|
||||
}
|
||||
|
||||
test "math.sqrt32.special" {
|
||||
assert(math.isPositiveInf(sqrt32(math.inf(f32))));
|
||||
assert(sqrt32(0.0) == 0.0);
|
||||
assert(sqrt32(-0.0) == -0.0);
|
||||
assert(math.isNan(sqrt32(-1.0)));
|
||||
assert(math.isNan(sqrt32(math.nan(f32))));
|
||||
assert(math.isPositiveInf(@sqrt(f32, math.inf(f32))));
|
||||
assert(@sqrt(f32, 0.0) == 0.0);
|
||||
assert(@sqrt(f32, -0.0) == -0.0);
|
||||
assert(math.isNan(@sqrt(f32, -1.0)));
|
||||
assert(math.isNan(@sqrt(f32, math.nan(f32))));
|
||||
}
|
||||
|
||||
test "math.sqrt64.special" {
|
||||
assert(math.isPositiveInf(sqrt64(math.inf(f64))));
|
||||
assert(sqrt64(0.0) == 0.0);
|
||||
assert(sqrt64(-0.0) == -0.0);
|
||||
assert(math.isNan(sqrt64(-1.0)));
|
||||
assert(math.isNan(sqrt64(math.nan(f64))));
|
||||
assert(math.isPositiveInf(@sqrt(f64, math.inf(f64))));
|
||||
assert(@sqrt(f64, 0.0) == 0.0);
|
||||
assert(@sqrt(f64, -0.0) == -0.0);
|
||||
assert(math.isNan(@sqrt(f64, -1.0)));
|
||||
assert(math.isNan(@sqrt(f64, math.nan(f64))));
|
||||
}
|
||||
|
||||
fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) {
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
pub fn sqrt32(x: f32) f32 {
|
||||
return asm (
|
||||
\\sqrtss %%xmm0, %%xmm0
|
||||
: [ret] "={xmm0}" (-> f32)
|
||||
: [x] "{xmm0}" (x)
|
||||
);
|
||||
}
|
||||
|
||||
pub fn sqrt64(x: f64) f64 {
|
||||
return asm (
|
||||
\\sqrtsd %%xmm0, %%xmm0
|
||||
: [ret] "={xmm0}" (-> f64)
|
||||
: [x] "{xmm0}" (x)
|
||||
);
|
||||
}
|
||||
133
std/mem.zig
133
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};
|
||||
@ -10,6 +11,8 @@ pub const Allocator = struct {
|
||||
/// Allocate byte_count bytes and return them in a slice, with the
|
||||
/// slice's pointer aligned at least to alignment bytes.
|
||||
/// The returned newly allocated memory is undefined.
|
||||
/// `alignment` is guaranteed to be >= 1
|
||||
/// `alignment` is guaranteed to be a power of 2
|
||||
allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8,
|
||||
|
||||
/// If `new_byte_count > old_mem.len`:
|
||||
@ -17,20 +20,37 @@ pub const Allocator = struct {
|
||||
/// * alignment >= alignment of old_mem.ptr
|
||||
///
|
||||
/// If `new_byte_count <= old_mem.len`:
|
||||
/// * this function must return successfully.
|
||||
/// * this function must return successfully.
|
||||
/// * alignment <= alignment of old_mem.ptr
|
||||
///
|
||||
/// The returned newly allocated memory is undefined.
|
||||
/// `alignment` is guaranteed to be >= 1
|
||||
/// `alignment` is guaranteed to be a power of 2
|
||||
reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,
|
||||
|
||||
/// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
|
||||
freeFn: fn (self: &Allocator, old_mem: []u8) void,
|
||||
|
||||
fn create(self: &Allocator, comptime T: type) !&T {
|
||||
if (@sizeOf(T) == 0) return &{};
|
||||
const slice = try self.alloc(T, 1);
|
||||
return &slice[0];
|
||||
}
|
||||
|
||||
// TODO once #733 is solved, this will replace create
|
||||
fn construct(self: &Allocator, init: var) t: {
|
||||
// TODO this is a workaround for type getting parsed as Error!&const T
|
||||
const T = @typeOf(init).Child;
|
||||
break :t Error!&T;
|
||||
} {
|
||||
const T = @typeOf(init).Child;
|
||||
if (@sizeOf(T) == 0) return &{};
|
||||
const slice = try self.alloc(T, 1);
|
||||
const ptr = &slice[0];
|
||||
*ptr = *init;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
fn destroy(self: &Allocator, ptr: var) void {
|
||||
self.free(ptr[0..1]);
|
||||
}
|
||||
@ -48,7 +68,7 @@ pub const Allocator = struct {
|
||||
const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
|
||||
const byte_slice = try self.allocFn(self, byte_count, alignment);
|
||||
assert(byte_slice.len == byte_count);
|
||||
// This loop should get optimized out in ReleaseFast mode
|
||||
// This loop gets optimized out in ReleaseFast mode
|
||||
for (byte_slice) |*byte| {
|
||||
*byte = undefined;
|
||||
}
|
||||
@ -75,7 +95,7 @@ pub const Allocator = struct {
|
||||
const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);
|
||||
assert(byte_slice.len == byte_count);
|
||||
if (n > old_mem.len) {
|
||||
// This loop should get optimized out in ReleaseFast mode
|
||||
// This loop gets optimized out in ReleaseFast mode
|
||||
for (byte_slice[old_byte_slice.len..]) |*byte| {
|
||||
*byte = undefined;
|
||||
}
|
||||
@ -169,6 +189,20 @@ pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T {
|
||||
return new_buf;
|
||||
}
|
||||
|
||||
/// Remove values from the beginning of a slice.
|
||||
pub fn trimLeft(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
|
||||
var begin: usize = 0;
|
||||
while (begin < slice.len and indexOfScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {}
|
||||
return slice[begin..];
|
||||
}
|
||||
|
||||
/// Remove values from the end of a slice.
|
||||
pub fn trimRight(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
|
||||
var end: usize = slice.len;
|
||||
while (end > 0 and indexOfScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {}
|
||||
return slice[0..end];
|
||||
}
|
||||
|
||||
/// Remove values from the beginning and end of a slice.
|
||||
pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
|
||||
var begin: usize = 0;
|
||||
@ -179,6 +213,8 @@ pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []co
|
||||
}
|
||||
|
||||
test "mem.trim" {
|
||||
assert(eql(u8, trimLeft(u8, " foo\n ", " \n"), "foo\n "));
|
||||
assert(eql(u8, trimRight(u8, " foo\n ", " \n"), " foo"));
|
||||
assert(eql(u8, trim(u8, " foo\n ", " \n"), "foo"));
|
||||
assert(eql(u8, trim(u8, "foo", " \n"), "foo"));
|
||||
}
|
||||
@ -188,6 +224,17 @@ pub fn indexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {
|
||||
return indexOfScalarPos(T, slice, 0, value);
|
||||
}
|
||||
|
||||
/// Linear search for the last index of a scalar value inside a slice.
|
||||
pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {
|
||||
var i: usize = slice.len;
|
||||
while (i != 0) {
|
||||
i -= 1;
|
||||
if (slice[i] == value)
|
||||
return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize {
|
||||
var i: usize = start_index;
|
||||
while (i < slice.len) : (i += 1) {
|
||||
@ -201,6 +248,18 @@ pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize
|
||||
return indexOfAnyPos(T, slice, 0, values);
|
||||
}
|
||||
|
||||
pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize {
|
||||
var i: usize = slice.len;
|
||||
while (i != 0) {
|
||||
i -= 1;
|
||||
for (values) |value| {
|
||||
if (slice[i] == value)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize {
|
||||
var i: usize = start_index;
|
||||
while (i < slice.len) : (i += 1) {
|
||||
@ -216,6 +275,22 @@ pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize
|
||||
return indexOfPos(T, haystack, 0, needle);
|
||||
}
|
||||
|
||||
/// Find the index in a slice of a sub-slice, searching from the end backwards.
|
||||
/// To start looking at a different index, slice the haystack first.
|
||||
/// TODO is there even a better algorithm for this?
|
||||
pub fn lastIndexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize {
|
||||
if (needle.len > haystack.len)
|
||||
return null;
|
||||
|
||||
var i: usize = haystack.len - needle.len;
|
||||
while (true) : (i -= 1) {
|
||||
if (mem.eql(T, haystack[i..i+needle.len], needle))
|
||||
return i;
|
||||
if (i == 0)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO boyer-moore algorithm
|
||||
pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize {
|
||||
if (needle.len > haystack.len)
|
||||
@ -232,9 +307,19 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee
|
||||
|
||||
test "mem.indexOf" {
|
||||
assert(??indexOf(u8, "one two three four", "four") == 14);
|
||||
assert(??lastIndexOf(u8, "one two three two four", "two") == 14);
|
||||
assert(indexOf(u8, "one two three four", "gour") == null);
|
||||
assert(lastIndexOf(u8, "one two three four", "gour") == null);
|
||||
assert(??indexOf(u8, "foo", "foo") == 0);
|
||||
assert(??lastIndexOf(u8, "foo", "foo") == 0);
|
||||
assert(indexOf(u8, "foo", "fool") == null);
|
||||
assert(lastIndexOf(u8, "foo", "lfoo") == null);
|
||||
assert(lastIndexOf(u8, "foo", "fool") == null);
|
||||
|
||||
assert(??indexOf(u8, "foo foo", "foo") == 0);
|
||||
assert(??lastIndexOf(u8, "foo foo", "foo") == 4);
|
||||
assert(??lastIndexOfAny(u8, "boo, cat", "abo") == 6);
|
||||
assert(??lastIndexOfScalar(u8, "boo", 'o') == 2);
|
||||
}
|
||||
|
||||
/// Reads an integer from memory with size equal to bytes.len.
|
||||
@ -354,9 +439,24 @@ pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool
|
||||
return if (needle.len > haystack.len) false else eql(T, haystack[0 .. needle.len], needle);
|
||||
}
|
||||
|
||||
test "mem.startsWith" {
|
||||
assert(startsWith(u8, "Bob", "Bo"));
|
||||
assert(!startsWith(u8, "Needle in haystack", "haystack"));
|
||||
}
|
||||
|
||||
pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool {
|
||||
return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle);
|
||||
}
|
||||
|
||||
|
||||
test "mem.endsWith" {
|
||||
assert(endsWith(u8, "Needle in haystack", "haystack"));
|
||||
assert(!endsWith(u8, "Bob", "Bo"));
|
||||
}
|
||||
|
||||
pub const SplitIterator = struct {
|
||||
buffer: []const u8,
|
||||
split_bytes: []const u8,
|
||||
split_bytes: []const u8,
|
||||
index: usize,
|
||||
|
||||
pub fn next(self: &SplitIterator) ?[]const u8 {
|
||||
@ -550,3 +650,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);
|
||||
}
|
||||
|
||||
@ -41,6 +41,11 @@ pub const SA_64REGSET = 0x0200; /// signal handler with SA_SIGINFO args with 64
|
||||
pub const O_LARGEFILE = 0x0000;
|
||||
pub const O_PATH = 0x0000;
|
||||
|
||||
pub const F_OK = 0;
|
||||
pub const X_OK = 1;
|
||||
pub const W_OK = 2;
|
||||
pub const R_OK = 4;
|
||||
|
||||
pub const O_RDONLY = 0x0000; /// open for reading only
|
||||
pub const O_WRONLY = 0x0001; /// open for writing only
|
||||
pub const O_RDWR = 0x0002; /// open for reading and writing
|
||||
@ -179,7 +184,7 @@ pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize {
|
||||
return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte));
|
||||
}
|
||||
|
||||
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
|
||||
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32,
|
||||
offset: isize) usize
|
||||
{
|
||||
const ptr_result = c.mmap(@ptrCast(&c_void, address), length,
|
||||
@ -188,8 +193,8 @@ pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
|
||||
return errnoWrap(isize_result);
|
||||
}
|
||||
|
||||
pub fn munmap(address: &u8, length: usize) usize {
|
||||
return errnoWrap(c.munmap(@ptrCast(&c_void, address), length));
|
||||
pub fn munmap(address: usize, length: usize) usize {
|
||||
return errnoWrap(c.munmap(@intToPtr(&c_void, address), length));
|
||||
}
|
||||
|
||||
pub fn unlink(path: &const u8) usize {
|
||||
@ -209,6 +214,10 @@ pub fn fork() usize {
|
||||
return errnoWrap(c.fork());
|
||||
}
|
||||
|
||||
pub fn access(path: &const u8, mode: u32) usize {
|
||||
return errnoWrap(c.access(path, mode));
|
||||
}
|
||||
|
||||
pub fn pipe(fds: &[2]i32) usize {
|
||||
comptime assert(i32.bit_count == c_int.bit_count);
|
||||
return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));
|
||||
@ -251,6 +260,10 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) u
|
||||
return errnoWrap(c.readlink(path, buf_ptr, buf_len));
|
||||
}
|
||||
|
||||
pub fn gettimeofday(tv: ?&timeval, tz: ?&timezone) usize {
|
||||
return errnoWrap(c.gettimeofday(tv, tz));
|
||||
}
|
||||
|
||||
pub fn nanosleep(req: &const timespec, rem: ?×pec) usize {
|
||||
return errnoWrap(c.nanosleep(req, rem));
|
||||
}
|
||||
@ -301,6 +314,9 @@ pub const timespec = c.timespec;
|
||||
pub const Stat = c.Stat;
|
||||
pub const dirent = c.dirent;
|
||||
|
||||
pub const sa_family_t = c.sa_family_t;
|
||||
pub const sockaddr = c.sockaddr;
|
||||
|
||||
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
|
||||
pub const Sigaction = struct {
|
||||
handler: extern fn(i32)void,
|
||||
@ -318,3 +334,11 @@ pub fn sigaddset(set: &sigset_t, signo: u5) void {
|
||||
fn errnoWrap(value: isize) usize {
|
||||
return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value);
|
||||
}
|
||||
|
||||
|
||||
pub const timezone = c.timezone;
|
||||
pub const timeval = c.timeval;
|
||||
pub const mach_timebase_info_data = c.mach_timebase_info_data;
|
||||
|
||||
pub const mach_absolute_time = c.mach_absolute_time;
|
||||
pub const mach_timebase_info = c.mach_timebase_info;
|
||||
|
||||
26
std/os/epoch.zig
Normal file
26
std/os/epoch.zig
Normal file
@ -0,0 +1,26 @@
|
||||
/// Epoch reference times in terms of their difference from
|
||||
/// posix epoch in seconds.
|
||||
pub const posix = 0; //Jan 01, 1970 AD
|
||||
pub const dos = 315532800; //Jan 01, 1980 AD
|
||||
pub const ios = 978307200; //Jan 01, 2001 AD
|
||||
pub const openvms = -3506716800; //Nov 17, 1858 AD
|
||||
pub const zos = -2208988800; //Jan 01, 1900 AD
|
||||
pub const windows = -11644473600; //Jan 01, 1601 AD
|
||||
pub const amiga = 252460800; //Jan 01, 1978 AD
|
||||
pub const pickos = -63244800; //Dec 31, 1967 AD
|
||||
pub const gps = 315964800; //Jan 06, 1980 AD
|
||||
pub const clr = -62135769600; //Jan 01, 0001 AD
|
||||
|
||||
pub const unix = posix;
|
||||
pub const android = posix;
|
||||
pub const os2 = dos;
|
||||
pub const bios = dos;
|
||||
pub const vfat = dos;
|
||||
pub const ntfs = windows;
|
||||
pub const ntp = zos;
|
||||
pub const jbase = pickos;
|
||||
pub const aros = amiga;
|
||||
pub const morphos = amiga;
|
||||
pub const brew = gps;
|
||||
pub const atsc = gps;
|
||||
pub const go = clr;
|
||||
@ -85,6 +85,47 @@ pub const File = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn access(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool {
|
||||
const path_with_null = try std.cstr.addNullByte(allocator, path);
|
||||
defer allocator.free(path_with_null);
|
||||
|
||||
if (is_posix) {
|
||||
// mode is ignored and is always F_OK for now
|
||||
const result = posix.access(path_with_null.ptr, posix.F_OK);
|
||||
const err = posix.getErrno(result);
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EACCES => error.PermissionDenied,
|
||||
posix.EROFS => error.PermissionDenied,
|
||||
posix.ELOOP => error.PermissionDenied,
|
||||
posix.ETXTBSY => error.PermissionDenied,
|
||||
posix.ENOTDIR => error.NotFound,
|
||||
posix.ENOENT => error.NotFound,
|
||||
|
||||
posix.ENAMETOOLONG => error.NameTooLong,
|
||||
posix.EINVAL => error.BadMode,
|
||||
posix.EFAULT => error.BadPathName,
|
||||
posix.EIO => error.Io,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
else => os.unexpectedErrorPosix(err),
|
||||
};
|
||||
}
|
||||
return true;
|
||||
} else if (is_windows) {
|
||||
if (os.windows.PathFileExists(path_with_null.ptr) == os.windows.TRUE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
windows.ERROR.FILE_NOT_FOUND => error.NotFound,
|
||||
windows.ERROR.ACCESS_DENIED => error.PermissionDenied,
|
||||
else => os.unexpectedErrorWindows(err),
|
||||
};
|
||||
} else {
|
||||
@compileError("TODO implement access for this OS");
|
||||
}
|
||||
}
|
||||
|
||||
/// Upon success, the stream is in an uninitialized state. To continue using it,
|
||||
/// you must use the open() function.
|
||||
@ -245,7 +286,9 @@ pub const File = struct {
|
||||
};
|
||||
}
|
||||
|
||||
return stat.mode;
|
||||
// TODO: we should be able to cast u16 to ModeError!u32, making this
|
||||
// explicit cast not necessary
|
||||
return os.FileMode(stat.mode);
|
||||
} else if (is_windows) {
|
||||
return {};
|
||||
} else {
|
||||
|
||||
1102
std/os/index.zig
1102
std/os/index.zig
File diff suppressed because it is too large
Load Diff
@ -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");
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
const std = @import("../../index.zig");
|
||||
const assert = std.debug.assert;
|
||||
const builtin = @import("builtin");
|
||||
const vdso = @import("vdso.zig");
|
||||
pub use switch (builtin.arch) {
|
||||
builtin.Arch.x86_64 => @import("x86_64.zig"),
|
||||
builtin.Arch.i386 => @import("i386.zig"),
|
||||
@ -14,6 +15,22 @@ pub const STDIN_FILENO = 0;
|
||||
pub const STDOUT_FILENO = 1;
|
||||
pub const STDERR_FILENO = 2;
|
||||
|
||||
pub const FUTEX_WAIT = 0;
|
||||
pub const FUTEX_WAKE = 1;
|
||||
pub const FUTEX_FD = 2;
|
||||
pub const FUTEX_REQUEUE = 3;
|
||||
pub const FUTEX_CMP_REQUEUE = 4;
|
||||
pub const FUTEX_WAKE_OP = 5;
|
||||
pub const FUTEX_LOCK_PI = 6;
|
||||
pub const FUTEX_UNLOCK_PI = 7;
|
||||
pub const FUTEX_TRYLOCK_PI = 8;
|
||||
pub const FUTEX_WAIT_BITSET = 9;
|
||||
|
||||
pub const FUTEX_PRIVATE_FLAG = 128;
|
||||
|
||||
pub const FUTEX_CLOCK_REALTIME = 256;
|
||||
|
||||
|
||||
pub const PROT_NONE = 0;
|
||||
pub const PROT_READ = 1;
|
||||
pub const PROT_WRITE = 2;
|
||||
@ -38,6 +55,11 @@ pub const MAP_STACK = 0x20000;
|
||||
pub const MAP_HUGETLB = 0x40000;
|
||||
pub const MAP_FILE = 0;
|
||||
|
||||
pub const F_OK = 0;
|
||||
pub const X_OK = 1;
|
||||
pub const W_OK = 2;
|
||||
pub const R_OK = 4;
|
||||
|
||||
pub const WNOHANG = 1;
|
||||
pub const WUNTRACED = 2;
|
||||
pub const WSTOPPED = 2;
|
||||
@ -101,17 +123,6 @@ pub const SIG_BLOCK = 0;
|
||||
pub const SIG_UNBLOCK = 1;
|
||||
pub const SIG_SETMASK = 2;
|
||||
|
||||
pub const SOCK_STREAM = 1;
|
||||
pub const SOCK_DGRAM = 2;
|
||||
pub const SOCK_RAW = 3;
|
||||
pub const SOCK_RDM = 4;
|
||||
pub const SOCK_SEQPACKET = 5;
|
||||
pub const SOCK_DCCP = 6;
|
||||
pub const SOCK_PACKET = 10;
|
||||
pub const SOCK_CLOEXEC = 0o2000000;
|
||||
pub const SOCK_NONBLOCK = 0o4000;
|
||||
|
||||
|
||||
pub const PROTO_ip = 0o000;
|
||||
pub const PROTO_icmp = 0o001;
|
||||
pub const PROTO_igmp = 0o002;
|
||||
@ -149,6 +160,20 @@ pub const PROTO_encap = 0o142;
|
||||
pub const PROTO_pim = 0o147;
|
||||
pub const PROTO_raw = 0o377;
|
||||
|
||||
pub const SHUT_RD = 0;
|
||||
pub const SHUT_WR = 1;
|
||||
pub const SHUT_RDWR = 2;
|
||||
|
||||
pub const SOCK_STREAM = 1;
|
||||
pub const SOCK_DGRAM = 2;
|
||||
pub const SOCK_RAW = 3;
|
||||
pub const SOCK_RDM = 4;
|
||||
pub const SOCK_SEQPACKET = 5;
|
||||
pub const SOCK_DCCP = 6;
|
||||
pub const SOCK_PACKET = 10;
|
||||
pub const SOCK_CLOEXEC = 0o2000000;
|
||||
pub const SOCK_NONBLOCK = 0o4000;
|
||||
|
||||
pub const PF_UNSPEC = 0;
|
||||
pub const PF_LOCAL = 1;
|
||||
pub const PF_UNIX = PF_LOCAL;
|
||||
@ -193,7 +218,10 @@ pub const PF_CAIF = 37;
|
||||
pub const PF_ALG = 38;
|
||||
pub const PF_NFC = 39;
|
||||
pub const PF_VSOCK = 40;
|
||||
pub const PF_MAX = 41;
|
||||
pub const PF_KCM = 41;
|
||||
pub const PF_QIPCRTR = 42;
|
||||
pub const PF_SMC = 43;
|
||||
pub const PF_MAX = 44;
|
||||
|
||||
pub const AF_UNSPEC = PF_UNSPEC;
|
||||
pub const AF_LOCAL = PF_LOCAL;
|
||||
@ -239,8 +267,137 @@ pub const AF_CAIF = PF_CAIF;
|
||||
pub const AF_ALG = PF_ALG;
|
||||
pub const AF_NFC = PF_NFC;
|
||||
pub const AF_VSOCK = PF_VSOCK;
|
||||
pub const AF_KCM = PF_KCM;
|
||||
pub const AF_QIPCRTR = PF_QIPCRTR;
|
||||
pub const AF_SMC = PF_SMC;
|
||||
pub const AF_MAX = PF_MAX;
|
||||
|
||||
pub const SO_DEBUG = 1;
|
||||
pub const SO_REUSEADDR = 2;
|
||||
pub const SO_TYPE = 3;
|
||||
pub const SO_ERROR = 4;
|
||||
pub const SO_DONTROUTE = 5;
|
||||
pub const SO_BROADCAST = 6;
|
||||
pub const SO_SNDBUF = 7;
|
||||
pub const SO_RCVBUF = 8;
|
||||
pub const SO_KEEPALIVE = 9;
|
||||
pub const SO_OOBINLINE = 10;
|
||||
pub const SO_NO_CHECK = 11;
|
||||
pub const SO_PRIORITY = 12;
|
||||
pub const SO_LINGER = 13;
|
||||
pub const SO_BSDCOMPAT = 14;
|
||||
pub const SO_REUSEPORT = 15;
|
||||
pub const SO_PASSCRED = 16;
|
||||
pub const SO_PEERCRED = 17;
|
||||
pub const SO_RCVLOWAT = 18;
|
||||
pub const SO_SNDLOWAT = 19;
|
||||
pub const SO_RCVTIMEO = 20;
|
||||
pub const SO_SNDTIMEO = 21;
|
||||
pub const SO_ACCEPTCONN = 30;
|
||||
pub const SO_SNDBUFFORCE = 32;
|
||||
pub const SO_RCVBUFFORCE = 33;
|
||||
pub const SO_PROTOCOL = 38;
|
||||
pub const SO_DOMAIN = 39;
|
||||
|
||||
pub const SO_SECURITY_AUTHENTICATION = 22;
|
||||
pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23;
|
||||
pub const SO_SECURITY_ENCRYPTION_NETWORK = 24;
|
||||
|
||||
pub const SO_BINDTODEVICE = 25;
|
||||
|
||||
pub const SO_ATTACH_FILTER = 26;
|
||||
pub const SO_DETACH_FILTER = 27;
|
||||
pub const SO_GET_FILTER = SO_ATTACH_FILTER;
|
||||
|
||||
pub const SO_PEERNAME = 28;
|
||||
pub const SO_TIMESTAMP = 29;
|
||||
pub const SCM_TIMESTAMP = SO_TIMESTAMP;
|
||||
|
||||
pub const SO_PEERSEC = 31;
|
||||
pub const SO_PASSSEC = 34;
|
||||
pub const SO_TIMESTAMPNS = 35;
|
||||
pub const SCM_TIMESTAMPNS = SO_TIMESTAMPNS;
|
||||
pub const SO_MARK = 36;
|
||||
pub const SO_TIMESTAMPING = 37;
|
||||
pub const SCM_TIMESTAMPING = SO_TIMESTAMPING;
|
||||
pub const SO_RXQ_OVFL = 40;
|
||||
pub const SO_WIFI_STATUS = 41;
|
||||
pub const SCM_WIFI_STATUS = SO_WIFI_STATUS;
|
||||
pub const SO_PEEK_OFF = 42;
|
||||
pub const SO_NOFCS = 43;
|
||||
pub const SO_LOCK_FILTER = 44;
|
||||
pub const SO_SELECT_ERR_QUEUE = 45;
|
||||
pub const SO_BUSY_POLL = 46;
|
||||
pub const SO_MAX_PACING_RATE = 47;
|
||||
pub const SO_BPF_EXTENSIONS = 48;
|
||||
pub const SO_INCOMING_CPU = 49;
|
||||
pub const SO_ATTACH_BPF = 50;
|
||||
pub const SO_DETACH_BPF = SO_DETACH_FILTER;
|
||||
pub const SO_ATTACH_REUSEPORT_CBPF = 51;
|
||||
pub const SO_ATTACH_REUSEPORT_EBPF = 52;
|
||||
pub const SO_CNX_ADVICE = 53;
|
||||
pub const SCM_TIMESTAMPING_OPT_STATS = 54;
|
||||
pub const SO_MEMINFO = 55;
|
||||
pub const SO_INCOMING_NAPI_ID = 56;
|
||||
pub const SO_COOKIE = 57;
|
||||
pub const SCM_TIMESTAMPING_PKTINFO = 58;
|
||||
pub const SO_PEERGROUPS = 59;
|
||||
pub const SO_ZEROCOPY = 60;
|
||||
|
||||
pub const SOL_SOCKET = 1;
|
||||
|
||||
pub const SOL_IP = 0;
|
||||
pub const SOL_IPV6 = 41;
|
||||
pub const SOL_ICMPV6 = 58;
|
||||
|
||||
pub const SOL_RAW = 255;
|
||||
pub const SOL_DECNET = 261;
|
||||
pub const SOL_X25 = 262;
|
||||
pub const SOL_PACKET = 263;
|
||||
pub const SOL_ATM = 264;
|
||||
pub const SOL_AAL = 265;
|
||||
pub const SOL_IRDA = 266;
|
||||
pub const SOL_NETBEUI = 267;
|
||||
pub const SOL_LLC = 268;
|
||||
pub const SOL_DCCP = 269;
|
||||
pub const SOL_NETLINK = 270;
|
||||
pub const SOL_TIPC = 271;
|
||||
pub const SOL_RXRPC = 272;
|
||||
pub const SOL_PPPOL2TP = 273;
|
||||
pub const SOL_BLUETOOTH = 274;
|
||||
pub const SOL_PNPIPE = 275;
|
||||
pub const SOL_RDS = 276;
|
||||
pub const SOL_IUCV = 277;
|
||||
pub const SOL_CAIF = 278;
|
||||
pub const SOL_ALG = 279;
|
||||
pub const SOL_NFC = 280;
|
||||
pub const SOL_KCM = 281;
|
||||
pub const SOL_TLS = 282;
|
||||
|
||||
pub const SOMAXCONN = 128;
|
||||
|
||||
pub const MSG_OOB = 0x0001;
|
||||
pub const MSG_PEEK = 0x0002;
|
||||
pub const MSG_DONTROUTE = 0x0004;
|
||||
pub const MSG_CTRUNC = 0x0008;
|
||||
pub const MSG_PROXY = 0x0010;
|
||||
pub const MSG_TRUNC = 0x0020;
|
||||
pub const MSG_DONTWAIT = 0x0040;
|
||||
pub const MSG_EOR = 0x0080;
|
||||
pub const MSG_WAITALL = 0x0100;
|
||||
pub const MSG_FIN = 0x0200;
|
||||
pub const MSG_SYN = 0x0400;
|
||||
pub const MSG_CONFIRM = 0x0800;
|
||||
pub const MSG_RST = 0x1000;
|
||||
pub const MSG_ERRQUEUE = 0x2000;
|
||||
pub const MSG_NOSIGNAL = 0x4000;
|
||||
pub const MSG_MORE = 0x8000;
|
||||
pub const MSG_WAITFORONE = 0x10000;
|
||||
pub const MSG_BATCH = 0x40000;
|
||||
pub const MSG_ZEROCOPY = 0x4000000;
|
||||
pub const MSG_FASTOPEN = 0x20000000;
|
||||
pub const MSG_CMSG_CLOEXEC = 0x40000000;
|
||||
|
||||
pub const DT_UNKNOWN = 0;
|
||||
pub const DT_FIFO = 1;
|
||||
pub const DT_CHR = 2;
|
||||
@ -343,6 +500,126 @@ pub const CLOCK_BOOTTIME_ALARM = 9;
|
||||
pub const CLOCK_SGI_CYCLE = 10;
|
||||
pub const CLOCK_TAI = 11;
|
||||
|
||||
pub const CSIGNAL = 0x000000ff;
|
||||
pub const CLONE_VM = 0x00000100;
|
||||
pub const CLONE_FS = 0x00000200;
|
||||
pub const CLONE_FILES = 0x00000400;
|
||||
pub const CLONE_SIGHAND = 0x00000800;
|
||||
pub const CLONE_PTRACE = 0x00002000;
|
||||
pub const CLONE_VFORK = 0x00004000;
|
||||
pub const CLONE_PARENT = 0x00008000;
|
||||
pub const CLONE_THREAD = 0x00010000;
|
||||
pub const CLONE_NEWNS = 0x00020000;
|
||||
pub const CLONE_SYSVSEM = 0x00040000;
|
||||
pub const CLONE_SETTLS = 0x00080000;
|
||||
pub const CLONE_PARENT_SETTID = 0x00100000;
|
||||
pub const CLONE_CHILD_CLEARTID = 0x00200000;
|
||||
pub const CLONE_DETACHED = 0x00400000;
|
||||
pub const CLONE_UNTRACED = 0x00800000;
|
||||
pub const CLONE_CHILD_SETTID = 0x01000000;
|
||||
pub const CLONE_NEWCGROUP = 0x02000000;
|
||||
pub const CLONE_NEWUTS = 0x04000000;
|
||||
pub const CLONE_NEWIPC = 0x08000000;
|
||||
pub const CLONE_NEWUSER = 0x10000000;
|
||||
pub const CLONE_NEWPID = 0x20000000;
|
||||
pub const CLONE_NEWNET = 0x40000000;
|
||||
pub const CLONE_IO = 0x80000000;
|
||||
|
||||
pub const MS_RDONLY = 1;
|
||||
pub const MS_NOSUID = 2;
|
||||
pub const MS_NODEV = 4;
|
||||
pub const MS_NOEXEC = 8;
|
||||
pub const MS_SYNCHRONOUS = 16;
|
||||
pub const MS_REMOUNT = 32;
|
||||
pub const MS_MANDLOCK = 64;
|
||||
pub const MS_DIRSYNC = 128;
|
||||
pub const MS_NOATIME = 1024;
|
||||
pub const MS_NODIRATIME = 2048;
|
||||
pub const MS_BIND = 4096;
|
||||
pub const MS_MOVE = 8192;
|
||||
pub const MS_REC = 16384;
|
||||
pub const MS_SILENT = 32768;
|
||||
pub const MS_POSIXACL = (1<<16);
|
||||
pub const MS_UNBINDABLE = (1<<17);
|
||||
pub const MS_PRIVATE = (1<<18);
|
||||
pub const MS_SLAVE = (1<<19);
|
||||
pub const MS_SHARED = (1<<20);
|
||||
pub const MS_RELATIME = (1<<21);
|
||||
pub const MS_KERNMOUNT = (1<<22);
|
||||
pub const MS_I_VERSION = (1<<23);
|
||||
pub const MS_STRICTATIME = (1<<24);
|
||||
pub const MS_LAZYTIME = (1<<25);
|
||||
pub const MS_NOREMOTELOCK = (1<<27);
|
||||
pub const MS_NOSEC = (1<<28);
|
||||
pub const MS_BORN = (1<<29);
|
||||
pub const MS_ACTIVE = (1<<30);
|
||||
pub const MS_NOUSER = (1<<31);
|
||||
|
||||
pub const MS_RMT_MASK = (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|MS_LAZYTIME);
|
||||
|
||||
pub const MS_MGC_VAL = 0xc0ed0000;
|
||||
pub const MS_MGC_MSK = 0xffff0000;
|
||||
|
||||
pub const MNT_FORCE = 1;
|
||||
pub const MNT_DETACH = 2;
|
||||
pub const MNT_EXPIRE = 4;
|
||||
pub const UMOUNT_NOFOLLOW = 8;
|
||||
|
||||
|
||||
pub const S_IFMT = 0o170000;
|
||||
|
||||
pub const S_IFDIR = 0o040000;
|
||||
pub const S_IFCHR = 0o020000;
|
||||
pub const S_IFBLK = 0o060000;
|
||||
pub const S_IFREG = 0o100000;
|
||||
pub const S_IFIFO = 0o010000;
|
||||
pub const S_IFLNK = 0o120000;
|
||||
pub const S_IFSOCK = 0o140000;
|
||||
|
||||
pub const S_ISUID = 0o4000;
|
||||
pub const S_ISGID = 0o2000;
|
||||
pub const S_ISVTX = 0o1000;
|
||||
pub const S_IRUSR = 0o400;
|
||||
pub const S_IWUSR = 0o200;
|
||||
pub const S_IXUSR = 0o100;
|
||||
pub const S_IRWXU = 0o700;
|
||||
pub const S_IRGRP = 0o040;
|
||||
pub const S_IWGRP = 0o020;
|
||||
pub const S_IXGRP = 0o010;
|
||||
pub const S_IRWXG = 0o070;
|
||||
pub const S_IROTH = 0o004;
|
||||
pub const S_IWOTH = 0o002;
|
||||
pub const S_IXOTH = 0o001;
|
||||
pub const S_IRWXO = 0o007;
|
||||
|
||||
pub fn S_ISREG(m: u32) bool {
|
||||
return m & S_IFMT == S_IFREG;
|
||||
}
|
||||
|
||||
pub fn S_ISDIR(m: u32) bool {
|
||||
return m & S_IFMT == S_IFDIR;
|
||||
}
|
||||
|
||||
pub fn S_ISCHR(m: u32) bool {
|
||||
return m & S_IFMT == S_IFCHR;
|
||||
}
|
||||
|
||||
pub fn S_ISBLK(m: u32) bool {
|
||||
return m & S_IFMT == S_IFBLK;
|
||||
}
|
||||
|
||||
pub fn S_ISFIFO(m: u32) bool {
|
||||
return m & S_IFMT == S_IFIFO;
|
||||
}
|
||||
|
||||
pub fn S_ISLNK(m: u32) bool {
|
||||
return m & S_IFMT == S_IFLNK;
|
||||
}
|
||||
|
||||
pub fn S_ISSOCK(m: u32) bool {
|
||||
return m & S_IFMT == S_IFSOCK;
|
||||
}
|
||||
|
||||
pub const TFD_NONBLOCK = O_NONBLOCK;
|
||||
pub const TFD_CLOEXEC = O_CLOEXEC;
|
||||
|
||||
@ -380,6 +657,10 @@ pub fn chdir(path: &const u8) usize {
|
||||
return syscall1(SYS_chdir, @ptrToInt(path));
|
||||
}
|
||||
|
||||
pub fn chroot(path: &const u8) usize {
|
||||
return syscall1(SYS_chroot, @ptrToInt(path));
|
||||
}
|
||||
|
||||
pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) usize {
|
||||
return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));
|
||||
}
|
||||
@ -388,6 +669,10 @@ pub fn fork() usize {
|
||||
return syscall0(SYS_fork);
|
||||
}
|
||||
|
||||
pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?×pec) usize {
|
||||
return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));
|
||||
}
|
||||
|
||||
pub fn getcwd(buf: &u8, size: usize) usize {
|
||||
return syscall2(SYS_getcwd, @ptrToInt(buf), size);
|
||||
}
|
||||
@ -409,13 +694,25 @@ pub fn mkdir(path: &const u8, mode: u32) usize {
|
||||
return syscall2(SYS_mkdir, @ptrToInt(path), mode);
|
||||
}
|
||||
|
||||
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize {
|
||||
pub fn mount(special: &const u8, dir: &const u8, fstype: &const u8, flags: usize, data: usize) usize {
|
||||
return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data);
|
||||
}
|
||||
|
||||
pub fn umount(special: &const u8) usize {
|
||||
return syscall2(SYS_umount2, @ptrToInt(special), 0);
|
||||
}
|
||||
|
||||
pub fn umount2(special: &const u8, flags: u32) usize {
|
||||
return syscall2(SYS_umount2, @ptrToInt(special), flags);
|
||||
}
|
||||
|
||||
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
|
||||
return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
|
||||
@bitCast(usize, offset));
|
||||
}
|
||||
|
||||
pub fn munmap(address: &u8, length: usize) usize {
|
||||
return syscall2(SYS_munmap, @ptrToInt(address), length);
|
||||
pub fn munmap(address: usize, length: usize) usize {
|
||||
return syscall2(SYS_munmap, address, length);
|
||||
}
|
||||
|
||||
pub fn read(fd: i32, buf: &u8, count: usize) usize {
|
||||
@ -434,6 +731,10 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize {
|
||||
return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset);
|
||||
}
|
||||
|
||||
pub fn access(path: &const u8, mode: u32) usize {
|
||||
return syscall2(SYS_access, @ptrToInt(path), mode);
|
||||
}
|
||||
|
||||
pub fn pipe(fd: &[2]i32) usize {
|
||||
return pipe2(fd, 0);
|
||||
}
|
||||
@ -466,6 +767,16 @@ pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) usize {
|
||||
return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode);
|
||||
}
|
||||
|
||||
/// See also `clone` (from the arch-specific include)
|
||||
pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: &i32, child_tid: &i32, newtls: usize) usize {
|
||||
return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls);
|
||||
}
|
||||
|
||||
/// See also `clone` (from the arch-specific include)
|
||||
pub fn clone2(flags: usize, child_stack_ptr: usize) usize {
|
||||
return syscall2(SYS_clone, flags, child_stack_ptr);
|
||||
}
|
||||
|
||||
pub fn close(fd: i32) usize {
|
||||
return syscall1(SYS_close, usize(fd));
|
||||
}
|
||||
@ -495,6 +806,45 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize {
|
||||
return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
|
||||
}
|
||||
|
||||
pub fn clock_gettime(clk_id: i32, tp: ×pec) usize {
|
||||
if (VDSO_CGT_SYM.len != 0) {
|
||||
const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered);
|
||||
if (@ptrToInt(f) != 0) {
|
||||
const rc = f(clk_id, tp);
|
||||
switch (rc) {
|
||||
0, @bitCast(usize, isize(-EINVAL)) => return rc,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
|
||||
}
|
||||
var vdso_clock_gettime = init_vdso_clock_gettime;
|
||||
extern fn init_vdso_clock_gettime(clk: i32, ts: ×pec) usize {
|
||||
const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM);
|
||||
var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr);
|
||||
_ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f,
|
||||
builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic);
|
||||
if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS));
|
||||
return f(clk, ts);
|
||||
}
|
||||
|
||||
pub fn clock_getres(clk_id: i32, tp: ×pec) usize {
|
||||
return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
|
||||
}
|
||||
|
||||
pub fn clock_settime(clk_id: i32, tp: &const timespec) usize {
|
||||
return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
|
||||
}
|
||||
|
||||
pub fn gettimeofday(tv: &timeval, tz: &timezone) usize {
|
||||
return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz));
|
||||
}
|
||||
|
||||
pub fn settimeofday(tv: &const timeval, tz: &const timezone) usize {
|
||||
return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz));
|
||||
}
|
||||
|
||||
pub fn nanosleep(req: &const timespec, rem: ?×pec) usize {
|
||||
return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));
|
||||
}
|
||||
@ -515,6 +865,58 @@ pub fn setregid(rgid: u32, egid: u32) usize {
|
||||
return syscall2(SYS_setregid, rgid, egid);
|
||||
}
|
||||
|
||||
pub fn getuid() u32 {
|
||||
return u32(syscall0(SYS_getuid));
|
||||
}
|
||||
|
||||
pub fn getgid() u32 {
|
||||
return u32(syscall0(SYS_getgid));
|
||||
}
|
||||
|
||||
pub fn geteuid() u32 {
|
||||
return u32(syscall0(SYS_geteuid));
|
||||
}
|
||||
|
||||
pub fn getegid() u32 {
|
||||
return u32(syscall0(SYS_getegid));
|
||||
}
|
||||
|
||||
pub fn seteuid(euid: u32) usize {
|
||||
return syscall1(SYS_seteuid, euid);
|
||||
}
|
||||
|
||||
pub fn setegid(egid: u32) usize {
|
||||
return syscall1(SYS_setegid, egid);
|
||||
}
|
||||
|
||||
pub fn getresuid(ruid: &u32, euid: &u32, suid: &u32) usize {
|
||||
return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid));
|
||||
}
|
||||
|
||||
pub fn getresgid(rgid: &u32, egid: &u32, sgid: &u32) usize {
|
||||
return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid));
|
||||
}
|
||||
|
||||
pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize {
|
||||
return syscall3(SYS_setresuid, ruid, euid, suid);
|
||||
}
|
||||
|
||||
pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize {
|
||||
return syscall3(SYS_setresgid, rgid, egid, sgid);
|
||||
}
|
||||
|
||||
pub fn getgroups(size: usize, list: &u32) usize {
|
||||
return syscall2(SYS_getgroups, size, @ptrToInt(list));
|
||||
}
|
||||
|
||||
pub fn setgroups(size: usize, list: &const u32) usize {
|
||||
return syscall2(SYS_setgroups, size, @ptrToInt(list));
|
||||
}
|
||||
|
||||
pub fn getpid() i32 {
|
||||
return @bitCast(i32, u32(syscall0(SYS_getpid)));
|
||||
}
|
||||
|
||||
pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize {
|
||||
return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8);
|
||||
}
|
||||
@ -599,30 +1001,27 @@ pub fn sigismember(set: &const sigset_t, sig: u6) bool {
|
||||
return ((*set)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0;
|
||||
}
|
||||
|
||||
|
||||
pub const in_port_t = u16;
|
||||
pub const sa_family_t = u16;
|
||||
pub const socklen_t = u32;
|
||||
pub const in_addr = u32;
|
||||
pub const in6_addr = [16]u8;
|
||||
|
||||
pub const sockaddr = extern struct {
|
||||
family: sa_family_t,
|
||||
port: u16,
|
||||
data: [12]u8,
|
||||
pub const sockaddr = extern union {
|
||||
in: sockaddr_in,
|
||||
in6: sockaddr_in6,
|
||||
};
|
||||
|
||||
pub const sockaddr_in = extern struct {
|
||||
family: sa_family_t,
|
||||
port: u16,
|
||||
addr: in_addr,
|
||||
port: in_port_t,
|
||||
addr: u32,
|
||||
zero: [8]u8,
|
||||
};
|
||||
|
||||
pub const sockaddr_in6 = extern struct {
|
||||
family: sa_family_t,
|
||||
port: u16,
|
||||
port: in_port_t,
|
||||
flowinfo: u32,
|
||||
addr: in6_addr,
|
||||
addr: [16]u8,
|
||||
scope_id: u32,
|
||||
};
|
||||
|
||||
@ -639,16 +1038,16 @@ pub fn getpeername(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) us
|
||||
return syscall3(SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len));
|
||||
}
|
||||
|
||||
pub fn socket(domain: i32, socket_type: i32, protocol: i32) usize {
|
||||
return syscall3(SYS_socket, usize(domain), usize(socket_type), usize(protocol));
|
||||
pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
|
||||
return syscall3(SYS_socket, domain, socket_type, protocol);
|
||||
}
|
||||
|
||||
pub fn setsockopt(fd: i32, level: i32, optname: i32, optval: &const u8, optlen: socklen_t) usize {
|
||||
return syscall5(SYS_setsockopt, usize(fd), usize(level), usize(optname), usize(optval), @ptrToInt(optlen));
|
||||
pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: &const u8, optlen: socklen_t) usize {
|
||||
return syscall5(SYS_setsockopt, usize(fd), level, optname, usize(optval), @ptrToInt(optlen));
|
||||
}
|
||||
|
||||
pub fn getsockopt(fd: i32, level: i32, optname: i32, noalias optval: &u8, noalias optlen: &socklen_t) usize {
|
||||
return syscall5(SYS_getsockopt, usize(fd), usize(level), usize(optname), @ptrToInt(optval), @ptrToInt(optlen));
|
||||
pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: &u8, noalias optlen: &socklen_t) usize {
|
||||
return syscall5(SYS_getsockopt, usize(fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
|
||||
}
|
||||
|
||||
pub fn sendmsg(fd: i32, msg: &const msghdr, flags: u32) usize {
|
||||
@ -677,8 +1076,8 @@ pub fn bind(fd: i32, addr: &const sockaddr, len: socklen_t) usize {
|
||||
return syscall3(SYS_bind, usize(fd), @ptrToInt(addr), usize(len));
|
||||
}
|
||||
|
||||
pub fn listen(fd: i32, backlog: i32) usize {
|
||||
return syscall2(SYS_listen, usize(fd), usize(backlog));
|
||||
pub fn listen(fd: i32, backlog: u32) usize {
|
||||
return syscall2(SYS_listen, usize(fd), backlog);
|
||||
}
|
||||
|
||||
pub fn sendto(fd: i32, buf: &const u8, len: usize, flags: u32, addr: ?&const sockaddr, alen: socklen_t) usize {
|
||||
@ -697,46 +1096,83 @@ pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags:
|
||||
return syscall4(SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags);
|
||||
}
|
||||
|
||||
// error NameTooLong;
|
||||
// error SystemResources;
|
||||
// error Io;
|
||||
//
|
||||
// pub fn if_nametoindex(name: []u8) !u32 {
|
||||
// var ifr: ifreq = undefined;
|
||||
//
|
||||
// if (name.len >= ifr.ifr_name.len) {
|
||||
// return error.NameTooLong;
|
||||
// }
|
||||
//
|
||||
// const socket_ret = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
|
||||
// const socket_err = getErrno(socket_ret);
|
||||
// if (socket_err > 0) {
|
||||
// return error.SystemResources;
|
||||
// }
|
||||
// const socket_fd = i32(socket_ret);
|
||||
// @memcpy(&ifr.ifr_name[0], &name[0], name.len);
|
||||
// ifr.ifr_name[name.len] = 0;
|
||||
// const ioctl_ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr);
|
||||
// close(socket_fd);
|
||||
// const ioctl_err = getErrno(ioctl_ret);
|
||||
// if (ioctl_err > 0) {
|
||||
// return error.Io;
|
||||
// }
|
||||
// return ifr.ifr_ifindex;
|
||||
// }
|
||||
|
||||
pub fn fstat(fd: i32, stat_buf: &Stat) usize {
|
||||
return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf));
|
||||
}
|
||||
|
||||
pub const epoll_data = extern union {
|
||||
pub fn stat(pathname: &const u8, statbuf: &Stat) usize {
|
||||
return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf));
|
||||
}
|
||||
|
||||
pub fn lstat(pathname: &const u8, statbuf: &Stat) usize {
|
||||
return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf));
|
||||
}
|
||||
|
||||
pub fn listxattr(path: &const u8, list: &u8, size: usize) usize {
|
||||
return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size);
|
||||
}
|
||||
|
||||
pub fn llistxattr(path: &const u8, list: &u8, size: usize) usize {
|
||||
return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size);
|
||||
}
|
||||
|
||||
pub fn flistxattr(fd: usize, list: &u8, size: usize) usize {
|
||||
return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size);
|
||||
}
|
||||
|
||||
pub fn getxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize {
|
||||
return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
|
||||
}
|
||||
|
||||
pub fn lgetxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize {
|
||||
return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
|
||||
}
|
||||
|
||||
pub fn fgetxattr(fd: usize, name: &const u8, value: &void, size: usize) usize {
|
||||
return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size);
|
||||
}
|
||||
|
||||
pub fn setxattr(path: &const u8, name: &const u8, value: &const void,
|
||||
size: usize, flags: usize) usize {
|
||||
|
||||
return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value),
|
||||
size, flags);
|
||||
}
|
||||
|
||||
pub fn lsetxattr(path: &const u8, name: &const u8, value: &const void,
|
||||
size: usize, flags: usize) usize {
|
||||
|
||||
return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value),
|
||||
size, flags);
|
||||
}
|
||||
|
||||
pub fn fsetxattr(fd: usize, name: &const u8, value: &const void,
|
||||
size: usize, flags: usize) usize {
|
||||
|
||||
return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value),
|
||||
size, flags);
|
||||
}
|
||||
|
||||
pub fn removexattr(path: &const u8, name: &const u8) usize {
|
||||
return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name));
|
||||
}
|
||||
|
||||
pub fn lremovexattr(path: &const u8, name: &const u8) usize {
|
||||
return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name));
|
||||
}
|
||||
|
||||
pub fn fremovexattr(fd: usize, name: &const u8) usize {
|
||||
return syscall2(SYS_fremovexattr, fd, @ptrToInt(name));
|
||||
}
|
||||
|
||||
pub const epoll_data = packed union {
|
||||
ptr: usize,
|
||||
fd: i32,
|
||||
@"u32": u32,
|
||||
@"u64": u64,
|
||||
};
|
||||
|
||||
pub const epoll_event = extern struct {
|
||||
pub const epoll_event = packed struct {
|
||||
events: u32,
|
||||
data: epoll_data,
|
||||
};
|
||||
@ -749,7 +1185,7 @@ pub fn epoll_create1(flags: usize) usize {
|
||||
return syscall1(SYS_epoll_create1, flags);
|
||||
}
|
||||
|
||||
pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) usize {
|
||||
pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: &epoll_event) usize {
|
||||
return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
|
||||
}
|
||||
|
||||
@ -774,9 +1210,126 @@ pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_va
|
||||
return syscall4(SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value));
|
||||
}
|
||||
|
||||
test "import linux test" {
|
||||
// TODO lazy analysis should prevent this test from being compiled on windows, but
|
||||
// it is still compiled on windows
|
||||
pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330;
|
||||
pub const _LINUX_CAPABILITY_U32S_1 = 1;
|
||||
|
||||
pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026;
|
||||
pub const _LINUX_CAPABILITY_U32S_2 = 2;
|
||||
|
||||
pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522;
|
||||
pub const _LINUX_CAPABILITY_U32S_3 = 2;
|
||||
|
||||
pub const VFS_CAP_REVISION_MASK = 0xFF000000;
|
||||
pub const VFS_CAP_REVISION_SHIFT = 24;
|
||||
pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK;
|
||||
pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001;
|
||||
|
||||
pub const VFS_CAP_REVISION_1 = 0x01000000;
|
||||
pub const VFS_CAP_U32_1 = 1;
|
||||
pub const XATTR_CAPS_SZ_1 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_1);
|
||||
|
||||
pub const VFS_CAP_REVISION_2 = 0x02000000;
|
||||
pub const VFS_CAP_U32_2 = 2;
|
||||
pub const XATTR_CAPS_SZ_2 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_2);
|
||||
|
||||
pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2;
|
||||
pub const VFS_CAP_U32 = VFS_CAP_U32_2;
|
||||
pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2;
|
||||
|
||||
pub const vfs_cap_data = extern struct {
|
||||
//all of these are mandated as little endian
|
||||
//when on disk.
|
||||
const Data = struct {
|
||||
permitted: u32,
|
||||
inheritable: u32,
|
||||
};
|
||||
|
||||
magic_etc: u32,
|
||||
data: [VFS_CAP_U32]Data,
|
||||
};
|
||||
|
||||
|
||||
pub const CAP_CHOWN = 0;
|
||||
pub const CAP_DAC_OVERRIDE = 1;
|
||||
pub const CAP_DAC_READ_SEARCH = 2;
|
||||
pub const CAP_FOWNER = 3;
|
||||
pub const CAP_FSETID = 4;
|
||||
pub const CAP_KILL = 5;
|
||||
pub const CAP_SETGID = 6;
|
||||
pub const CAP_SETUID = 7;
|
||||
pub const CAP_SETPCAP = 8;
|
||||
pub const CAP_LINUX_IMMUTABLE = 9;
|
||||
pub const CAP_NET_BIND_SERVICE = 10;
|
||||
pub const CAP_NET_BROADCAST = 11;
|
||||
pub const CAP_NET_ADMIN = 12;
|
||||
pub const CAP_NET_RAW = 13;
|
||||
pub const CAP_IPC_LOCK = 14;
|
||||
pub const CAP_IPC_OWNER = 15;
|
||||
pub const CAP_SYS_MODULE = 16;
|
||||
pub const CAP_SYS_RAWIO = 17;
|
||||
pub const CAP_SYS_CHROOT = 18;
|
||||
pub const CAP_SYS_PTRACE = 19;
|
||||
pub const CAP_SYS_PACCT = 20;
|
||||
pub const CAP_SYS_ADMIN = 21;
|
||||
pub const CAP_SYS_BOOT = 22;
|
||||
pub const CAP_SYS_NICE = 23;
|
||||
pub const CAP_SYS_RESOURCE = 24;
|
||||
pub const CAP_SYS_TIME = 25;
|
||||
pub const CAP_SYS_TTY_CONFIG = 26;
|
||||
pub const CAP_MKNOD = 27;
|
||||
pub const CAP_LEASE = 28;
|
||||
pub const CAP_AUDIT_WRITE = 29;
|
||||
pub const CAP_AUDIT_CONTROL = 30;
|
||||
pub const CAP_SETFCAP = 31;
|
||||
pub const CAP_MAC_OVERRIDE = 32;
|
||||
pub const CAP_MAC_ADMIN = 33;
|
||||
pub const CAP_SYSLOG = 34;
|
||||
pub const CAP_WAKE_ALARM = 35;
|
||||
pub const CAP_BLOCK_SUSPEND = 36;
|
||||
pub const CAP_AUDIT_READ = 37;
|
||||
pub const CAP_LAST_CAP = CAP_AUDIT_READ;
|
||||
|
||||
pub fn cap_valid(u8: x) bool {
|
||||
return x >= 0 and x <= CAP_LAST_CAP;
|
||||
}
|
||||
|
||||
pub fn CAP_TO_MASK(cap: u8) u32 {
|
||||
return u32(1) << u5(cap & 31);
|
||||
}
|
||||
|
||||
pub fn CAP_TO_INDEX(cap: u8) u8 {
|
||||
return cap >> 5;
|
||||
}
|
||||
|
||||
pub const cap_t = extern struct {
|
||||
hdrp: &cap_user_header_t,
|
||||
datap: &cap_user_data_t,
|
||||
};
|
||||
|
||||
pub const cap_user_header_t = extern struct {
|
||||
version: u32,
|
||||
pid: usize,
|
||||
};
|
||||
|
||||
pub const cap_user_data_t = extern struct {
|
||||
effective: u32,
|
||||
permitted: u32,
|
||||
inheritable: u32,
|
||||
};
|
||||
|
||||
pub fn unshare(flags: usize) usize {
|
||||
return syscall1(SYS_unshare, usize(flags));
|
||||
}
|
||||
|
||||
pub fn capget(hdrp: &cap_user_header_t, datap: &cap_user_data_t) usize {
|
||||
return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap));
|
||||
}
|
||||
|
||||
pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize {
|
||||
return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
|
||||
}
|
||||
|
||||
test "import" {
|
||||
if (builtin.os == builtin.Os.linux) {
|
||||
_ = @import("test.zig");
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
const std = @import("../../index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const linux = std.os.linux;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
|
||||
89
std/os/linux/vdso.zig
Normal file
89
std/os/linux/vdso.zig
Normal file
@ -0,0 +1,89 @@
|
||||
const std = @import("../../index.zig");
|
||||
const elf = std.elf;
|
||||
const linux = std.os.linux;
|
||||
const cstr = std.cstr;
|
||||
const mem = std.mem;
|
||||
|
||||
pub fn lookup(vername: []const u8, name: []const u8) usize {
|
||||
const vdso_addr = std.os.linux_aux_raw[std.elf.AT_SYSINFO_EHDR];
|
||||
if (vdso_addr == 0) return 0;
|
||||
|
||||
const eh = @intToPtr(&elf.Ehdr, vdso_addr);
|
||||
var ph_addr: usize = vdso_addr + eh.e_phoff;
|
||||
const ph = @intToPtr(&elf.Phdr, ph_addr);
|
||||
|
||||
var maybe_dynv: ?&usize = null;
|
||||
var base: usize = @maxValue(usize);
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < eh.e_phnum) : ({i += 1; ph_addr += eh.e_phentsize;}) {
|
||||
const this_ph = @intToPtr(&elf.Phdr, ph_addr);
|
||||
switch (this_ph.p_type) {
|
||||
elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr,
|
||||
elf.PT_DYNAMIC => maybe_dynv = @intToPtr(&usize, vdso_addr + this_ph.p_offset),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
const dynv = maybe_dynv ?? return 0;
|
||||
if (base == @maxValue(usize)) return 0;
|
||||
|
||||
var maybe_strings: ?&u8 = null;
|
||||
var maybe_syms: ?&elf.Sym = null;
|
||||
var maybe_hashtab: ?&linux.Elf_Symndx = null;
|
||||
var maybe_versym: ?&u16 = null;
|
||||
var maybe_verdef: ?&elf.Verdef = null;
|
||||
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (dynv[i] != 0) : (i += 2) {
|
||||
const p = base + dynv[i + 1];
|
||||
switch (dynv[i]) {
|
||||
elf.DT_STRTAB => maybe_strings = @intToPtr(&u8, p),
|
||||
elf.DT_SYMTAB => maybe_syms = @intToPtr(&elf.Sym, p),
|
||||
elf.DT_HASH => maybe_hashtab = @intToPtr(&linux.Elf_Symndx, p),
|
||||
elf.DT_VERSYM => maybe_versym = @intToPtr(&u16, p),
|
||||
elf.DT_VERDEF => maybe_verdef = @intToPtr(&elf.Verdef, p),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const strings = maybe_strings ?? return 0;
|
||||
const syms = maybe_syms ?? return 0;
|
||||
const hashtab = maybe_hashtab ?? return 0;
|
||||
if (maybe_verdef == null) maybe_versym = null;
|
||||
|
||||
|
||||
const OK_TYPES = (1<<elf.STT_NOTYPE | 1<<elf.STT_OBJECT | 1<<elf.STT_FUNC | 1<<elf.STT_COMMON);
|
||||
const OK_BINDS = (1<<elf.STB_GLOBAL | 1<<elf.STB_WEAK | 1<<elf.STB_GNU_UNIQUE);
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < hashtab[1]) : (i += 1) {
|
||||
if (0==(u32(1)<<u5(syms[i].st_info&0xf) & OK_TYPES)) continue;
|
||||
if (0==(u32(1)<<u5(syms[i].st_info>>4) & OK_BINDS)) continue;
|
||||
if (0==syms[i].st_shndx) continue;
|
||||
if (!mem.eql(u8, name, cstr.toSliceConst(&strings[syms[i].st_name]))) continue;
|
||||
if (maybe_versym) |versym| {
|
||||
if (!checkver(??maybe_verdef, versym[i], vername, strings))
|
||||
continue;
|
||||
}
|
||||
return base + syms[i].st_value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn checkver(def_arg: &elf.Verdef, vsym_arg: i32, vername: []const u8, strings: &u8) bool {
|
||||
var def = def_arg;
|
||||
const vsym = @bitCast(u32, vsym_arg) & 0x7fff;
|
||||
while (true) {
|
||||
if (0==(def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym)
|
||||
break;
|
||||
if (def.vd_next == 0)
|
||||
return false;
|
||||
def = @intToPtr(&elf.Verdef, @ptrToInt(def) + def.vd_next);
|
||||
}
|
||||
const aux = @intToPtr(&elf.Verdaux, @ptrToInt(def ) + def.vd_aux);
|
||||
return mem.eql(u8, vername, cstr.toSliceConst(&strings[aux.vda_name]));
|
||||
}
|
||||
@ -371,6 +371,13 @@ pub const F_GETOWN_EX = 16;
|
||||
|
||||
pub const F_GETOWNER_UIDS = 17;
|
||||
|
||||
|
||||
pub const VDSO_USEFUL = true;
|
||||
pub const VDSO_CGT_SYM = "__vdso_clock_gettime";
|
||||
pub const VDSO_CGT_VER = "LINUX_2.6";
|
||||
pub const VDSO_GETCPU_SYM = "__vdso_getcpu";
|
||||
pub const VDSO_GETCPU_VER = "LINUX_2.6";
|
||||
|
||||
pub fn syscall0(number: usize) usize {
|
||||
return asm volatile ("syscall"
|
||||
: [ret] "={rax}" (-> usize)
|
||||
@ -443,6 +450,9 @@ pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz
|
||||
: "rcx", "r11");
|
||||
}
|
||||
|
||||
/// This matches the libc clone function.
|
||||
pub extern fn clone(func: extern fn(arg: usize) u8, stack: usize, flags: usize, arg: usize, ptid: &i32, tls: usize, ctid: &i32) usize;
|
||||
|
||||
pub nakedcc fn restore_rt() void {
|
||||
return asm volatile ("syscall"
|
||||
:
|
||||
@ -489,6 +499,16 @@ pub const timespec = extern struct {
|
||||
tv_nsec: isize,
|
||||
};
|
||||
|
||||
pub const timeval = extern struct {
|
||||
tv_sec: isize,
|
||||
tv_usec: isize,
|
||||
};
|
||||
|
||||
pub const timezone = extern struct {
|
||||
tz_minuteswest: i32,
|
||||
tz_dsttime: i32,
|
||||
};
|
||||
|
||||
pub const dirent = extern struct {
|
||||
d_ino: usize,
|
||||
d_off: usize,
|
||||
@ -496,3 +516,4 @@ pub const dirent = extern struct {
|
||||
d_name: u8, // field address is the address of first byte of name
|
||||
};
|
||||
|
||||
pub const Elf_Symndx = u32;
|
||||
|
||||
@ -6,6 +6,8 @@ const io = std.io;
|
||||
const a = std.debug.global_allocator;
|
||||
|
||||
const builtin = @import("builtin");
|
||||
const AtomicRmwOp = builtin.AtomicRmwOp;
|
||||
const AtomicOrder = builtin.AtomicOrder;
|
||||
|
||||
test "makePath, put some files in it, deleteTree" {
|
||||
if (builtin.os == builtin.Os.windows) {
|
||||
@ -23,3 +25,45 @@ test "makePath, put some files in it, deleteTree" {
|
||||
assert(err == error.PathNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
test "access file" {
|
||||
if (builtin.os == builtin.Os.windows) {
|
||||
return;
|
||||
}
|
||||
|
||||
try os.makePath(a, "os_test_tmp");
|
||||
if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
|
||||
unreachable;
|
||||
} else |err| {
|
||||
assert(err == error.NotFound);
|
||||
}
|
||||
|
||||
try io.writeFile(a, "os_test_tmp/file.txt", "");
|
||||
assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true);
|
||||
try os.deleteTree(a, "os_test_tmp");
|
||||
}
|
||||
|
||||
test "spawn threads" {
|
||||
var shared_ctx: i32 = 1;
|
||||
|
||||
const thread1 = try std.os.spawnThread({}, start1);
|
||||
const thread2 = try std.os.spawnThread(&shared_ctx, start2);
|
||||
const thread3 = try std.os.spawnThread(&shared_ctx, start2);
|
||||
const thread4 = try std.os.spawnThread(&shared_ctx, start2);
|
||||
|
||||
thread1.wait();
|
||||
thread2.wait();
|
||||
thread3.wait();
|
||||
thread4.wait();
|
||||
|
||||
assert(shared_ctx == 4);
|
||||
}
|
||||
|
||||
fn start1(ctx: void) u8 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn start2(ctx: &i32) u8 {
|
||||
_ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
288
std/os/time.zig
Normal file
288
std/os/time.zig
Normal file
@ -0,0 +1,288 @@
|
||||
const std = @import("../index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const Os = builtin.Os;
|
||||
const debug = std.debug;
|
||||
|
||||
const windows = std.os.windows;
|
||||
const linux = std.os.linux;
|
||||
const darwin = std.os.darwin;
|
||||
const posix = std.os.posix;
|
||||
|
||||
pub const epoch = @import("epoch.zig");
|
||||
|
||||
/// Sleep for the specified duration
|
||||
pub fn sleep(seconds: usize, nanoseconds: usize) void {
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios => {
|
||||
posixSleep(u63(seconds), u63(nanoseconds));
|
||||
},
|
||||
Os.windows => {
|
||||
const ns_per_ms = ns_per_s / ms_per_s;
|
||||
const milliseconds = seconds * ms_per_s + nanoseconds / ns_per_ms;
|
||||
windows.Sleep(windows.DWORD(milliseconds));
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
|
||||
const u63 = @IntType(false, 63);
|
||||
pub fn posixSleep(seconds: u63, nanoseconds: u63) void {
|
||||
var req = posix.timespec {
|
||||
.tv_sec = seconds,
|
||||
.tv_nsec = nanoseconds,
|
||||
};
|
||||
var rem: posix.timespec = undefined;
|
||||
while (true) {
|
||||
const ret_val = posix.nanosleep(&req, &rem);
|
||||
const err = posix.getErrno(ret_val);
|
||||
if (err == 0) return;
|
||||
switch (err) {
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINVAL => {
|
||||
// Sometimes Darwin returns EINVAL for no reason.
|
||||
// We treat it as a spurious wakeup.
|
||||
return;
|
||||
},
|
||||
posix.EINTR => {
|
||||
req = rem;
|
||||
continue;
|
||||
},
|
||||
else => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the posix timestamp, UTC, in seconds
|
||||
pub fn timestamp() u64 {
|
||||
return @divFloor(milliTimestamp(), ms_per_s);
|
||||
}
|
||||
|
||||
/// Get the posix timestamp, UTC, in milliseconds
|
||||
pub const milliTimestamp = switch (builtin.os) {
|
||||
Os.windows => milliTimestampWindows,
|
||||
Os.linux => milliTimestampPosix,
|
||||
Os.macosx, Os.ios => milliTimestampDarwin,
|
||||
else => @compileError("Unsupported OS"),
|
||||
};
|
||||
|
||||
fn milliTimestampWindows() u64 {
|
||||
//FileTime has a granularity of 100 nanoseconds
|
||||
// and uses the NTFS/Windows epoch
|
||||
var ft: i64 = undefined;
|
||||
windows.GetSystemTimeAsFileTime(&ft);
|
||||
const hns_per_ms = (ns_per_s / 100) / ms_per_s;
|
||||
const epoch_adj = epoch.windows * ms_per_s;
|
||||
return u64(@divFloor(ft, hns_per_ms) + epoch_adj);
|
||||
}
|
||||
|
||||
fn milliTimestampDarwin() u64 {
|
||||
//Sources suggest MacOS 10.12 has support for
|
||||
// posix clock_gettime.
|
||||
var tv: darwin.timeval = undefined;
|
||||
var err = darwin.gettimeofday(&tv, null);
|
||||
debug.assert(err == 0);
|
||||
const sec_ms = u64(tv.tv_sec) * ms_per_s;
|
||||
const usec_ms = @divFloor(u64(tv.tv_usec), us_per_s / ms_per_s);
|
||||
return u64(sec_ms) + u64(usec_ms);
|
||||
}
|
||||
|
||||
fn milliTimestampPosix() u64 {
|
||||
//From what I can tell there's no reason clock_gettime
|
||||
// should ever fail for us with CLOCK_REALTIME,
|
||||
// seccomp aside.
|
||||
var ts: posix.timespec = undefined;
|
||||
const err = posix.clock_gettime(posix.CLOCK_REALTIME, &ts);
|
||||
debug.assert(err == 0);
|
||||
const sec_ms = u64(ts.tv_sec) * ms_per_s;
|
||||
const nsec_ms = @divFloor(u64(ts.tv_nsec), ns_per_s / ms_per_s);
|
||||
return sec_ms + nsec_ms;
|
||||
}
|
||||
|
||||
/// Divisions of a second
|
||||
pub const ns_per_s = 1000000000;
|
||||
pub const us_per_s = 1000000;
|
||||
pub const ms_per_s = 1000;
|
||||
pub const cs_per_s = 100;
|
||||
|
||||
/// Common time divisions
|
||||
pub const s_per_min = 60;
|
||||
pub const s_per_hour = s_per_min * 60;
|
||||
pub const s_per_day = s_per_hour * 24;
|
||||
pub const s_per_week = s_per_day * 7;
|
||||
|
||||
|
||||
/// A monotonic high-performance timer.
|
||||
/// Timer.start() must be called to initialize the struct, which captures
|
||||
/// the counter frequency on windows and darwin, records the resolution,
|
||||
/// and gives the user an oportunity to check for the existnece of
|
||||
/// monotonic clocks without forcing them to check for error on each read.
|
||||
/// .resolution is in nanoseconds on all platforms but .start_time's meaning
|
||||
/// depends on the OS. On Windows and Darwin it is a hardware counter
|
||||
/// value that requires calculation to convert to a meaninful unit.
|
||||
pub const Timer = struct {
|
||||
|
||||
//if we used resolution's value when performing the
|
||||
// performance counter calc on windows/darwin, it would
|
||||
// be less precise
|
||||
frequency: switch (builtin.os) {
|
||||
Os.windows => u64,
|
||||
Os.macosx, Os.ios => darwin.mach_timebase_info_data,
|
||||
else => void,
|
||||
},
|
||||
resolution: u64,
|
||||
start_time: u64,
|
||||
|
||||
|
||||
//At some point we may change our minds on RAW, but for now we're
|
||||
// sticking with posix standard MONOTONIC. For more information, see:
|
||||
// https://github.com/zig-lang/zig/pull/933
|
||||
//
|
||||
//const monotonic_clock_id = switch(builtin.os) {
|
||||
// Os.linux => linux.CLOCK_MONOTONIC_RAW,
|
||||
// else => posix.CLOCK_MONOTONIC,
|
||||
//};
|
||||
const monotonic_clock_id = posix.CLOCK_MONOTONIC;
|
||||
|
||||
|
||||
/// Initialize the timer structure.
|
||||
//This gives us an oportunity to grab the counter frequency in windows.
|
||||
//On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000.
|
||||
//On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not
|
||||
// supported, or if the timespec pointer is out of bounds, which should be
|
||||
// impossible here barring cosmic rays or other such occurances of
|
||||
// incredibly bad luck.
|
||||
//On Darwin: This cannot fail, as far as I am able to tell.
|
||||
const TimerError = error{TimerUnsupported, Unexpected};
|
||||
pub fn start() TimerError!Timer {
|
||||
var self: Timer = undefined;
|
||||
|
||||
switch (builtin.os) {
|
||||
Os.windows => {
|
||||
var freq: i64 = undefined;
|
||||
var err = windows.QueryPerformanceFrequency(&freq);
|
||||
if (err == windows.FALSE) return error.TimerUnsupported;
|
||||
self.frequency = u64(freq);
|
||||
self.resolution = @divFloor(ns_per_s, self.frequency);
|
||||
|
||||
var start_time: i64 = undefined;
|
||||
err = windows.QueryPerformanceCounter(&start_time);
|
||||
debug.assert(err != windows.FALSE);
|
||||
self.start_time = u64(start_time);
|
||||
},
|
||||
Os.linux => {
|
||||
//On Linux, seccomp can do arbitrary things to our ability to call
|
||||
// syscalls, including return any errno value it wants and
|
||||
// inconsistently throwing errors. Since we can't account for
|
||||
// abuses of seccomp in a reasonable way, we'll assume that if
|
||||
// seccomp is going to block us it will at least do so consistently
|
||||
var ts: posix.timespec = undefined;
|
||||
var result = posix.clock_getres(monotonic_clock_id, &ts);
|
||||
var errno = posix.getErrno(result);
|
||||
switch (errno) {
|
||||
0 => {},
|
||||
posix.EINVAL => return error.TimerUnsupported,
|
||||
else => return std.os.unexpectedErrorPosix(errno),
|
||||
}
|
||||
self.resolution = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
|
||||
|
||||
result = posix.clock_gettime(monotonic_clock_id, &ts);
|
||||
errno = posix.getErrno(result);
|
||||
if (errno != 0) return std.os.unexpectedErrorPosix(errno);
|
||||
self.start_time = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
|
||||
},
|
||||
Os.macosx, Os.ios => {
|
||||
darwin.mach_timebase_info(&self.frequency);
|
||||
self.resolution = @divFloor(self.frequency.numer, self.frequency.denom);
|
||||
self.start_time = darwin.mach_absolute_time();
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Reads the timer value since start or the last reset in nanoseconds
|
||||
pub fn read(self: &Timer) u64 {
|
||||
var clock = clockNative() - self.start_time;
|
||||
return switch (builtin.os) {
|
||||
Os.windows => @divFloor(clock * ns_per_s, self.frequency),
|
||||
Os.linux => clock,
|
||||
Os.macosx, Os.ios => @divFloor(clock * self.frequency.numer, self.frequency.denom),
|
||||
else => @compileError("Unsupported OS"),
|
||||
};
|
||||
}
|
||||
|
||||
/// Resets the timer value to 0/now.
|
||||
pub fn reset(self: &Timer) void
|
||||
{
|
||||
self.start_time = clockNative();
|
||||
}
|
||||
|
||||
/// Returns the current value of the timer in nanoseconds, then resets it
|
||||
pub fn lap(self: &Timer) u64 {
|
||||
var now = clockNative();
|
||||
var lap_time = self.read();
|
||||
self.start_time = now;
|
||||
return lap_time;
|
||||
}
|
||||
|
||||
|
||||
const clockNative = switch (builtin.os) {
|
||||
Os.windows => clockWindows,
|
||||
Os.linux => clockLinux,
|
||||
Os.macosx, Os.ios => clockDarwin,
|
||||
else => @compileError("Unsupported OS"),
|
||||
};
|
||||
|
||||
fn clockWindows() u64 {
|
||||
var result: i64 = undefined;
|
||||
var err = windows.QueryPerformanceCounter(&result);
|
||||
debug.assert(err != windows.FALSE);
|
||||
return u64(result);
|
||||
}
|
||||
|
||||
fn clockDarwin() u64 {
|
||||
return darwin.mach_absolute_time();
|
||||
}
|
||||
|
||||
fn clockLinux() u64 {
|
||||
var ts: posix.timespec = undefined;
|
||||
var result = posix.clock_gettime(monotonic_clock_id, &ts);
|
||||
debug.assert(posix.getErrno(result) == 0);
|
||||
return u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
test "os.time.sleep" {
|
||||
sleep(0, 1);
|
||||
}
|
||||
|
||||
test "os.time.timestamp" {
|
||||
const ns_per_ms = (ns_per_s / ms_per_s);
|
||||
const margin = 50;
|
||||
|
||||
const time_0 = milliTimestamp();
|
||||
sleep(0, ns_per_ms);
|
||||
const time_1 = milliTimestamp();
|
||||
const interval = time_1 - time_0;
|
||||
debug.assert(interval > 0 and interval < margin);
|
||||
}
|
||||
|
||||
test "os.time.Timer" {
|
||||
const ns_per_ms = (ns_per_s / ms_per_s);
|
||||
const margin = ns_per_ms * 50;
|
||||
|
||||
var timer = try Timer.start();
|
||||
sleep(0, 10 * ns_per_ms);
|
||||
const time_0 = timer.read();
|
||||
debug.assert(time_0 > 0 and time_0 < margin);
|
||||
|
||||
const time_1 = timer.lap();
|
||||
debug.assert(time_1 >= time_0);
|
||||
|
||||
timer.reset();
|
||||
debug.assert(timer.read() < time_1);
|
||||
}
|
||||
@ -28,6 +28,9 @@ pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lp
|
||||
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(lpSymlinkFileName: LPCSTR, lpTargetFileName: LPCSTR,
|
||||
dwFlags: DWORD) BOOLEAN;
|
||||
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
|
||||
@ -61,6 +64,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?&FILETIME) void;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
|
||||
pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void;
|
||||
@ -77,6 +82,12 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
|
||||
|
||||
pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR,
|
||||
dwFlags: DWORD) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: &LARGE_INTEGER) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: &LARGE_INTEGER) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void,
|
||||
in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,
|
||||
@ -137,6 +148,7 @@ pub const UNICODE = false;
|
||||
pub const WCHAR = u16;
|
||||
pub const WORD = u16;
|
||||
pub const LARGE_INTEGER = i64;
|
||||
pub const FILETIME = i64;
|
||||
|
||||
pub const TRUE = 1;
|
||||
pub const FALSE = 0;
|
||||
@ -308,3 +320,10 @@ pub const FILE_END = 2;
|
||||
pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
|
||||
pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
|
||||
pub const HEAP_NO_SERIALIZE = 0x00000001;
|
||||
|
||||
pub const PTHREAD_START_ROUTINE = extern fn(LPVOID) DWORD;
|
||||
pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
|
||||
|
||||
test "import" {
|
||||
_ = @import("util.zig");
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
const math = std.math;
|
||||
const ziggurat = @import("ziggurat.zig");
|
||||
|
||||
// When you need fast unbiased random numbers
|
||||
pub const DefaultPrng = Xoroshiro128;
|
||||
@ -109,15 +110,28 @@ pub const Random = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a floating point value normally distributed in the range [0, 1].
|
||||
/// Return a floating point value normally distributed with mean = 0, stddev = 1.
|
||||
///
|
||||
/// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean.
|
||||
pub fn floatNorm(r: &Random, comptime T: type) T {
|
||||
// TODO(tiehuis): See https://www.doornik.com/research/ziggurat.pdf
|
||||
@compileError("floatNorm is unimplemented");
|
||||
const value = ziggurat.next_f64(r, ziggurat.NormDist);
|
||||
switch (T) {
|
||||
f32 => return f32(value),
|
||||
f64 => return value,
|
||||
else => @compileError("unknown floating point type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a exponentially distributed float between (0, @maxValue(f64))
|
||||
/// Return an exponentially distributed float with a rate parameter of 1.
|
||||
///
|
||||
/// To use a different rate parameter, use: floatExp(...) / desiredRate.
|
||||
pub fn floatExp(r: &Random, comptime T: type) T {
|
||||
@compileError("floatExp is unimplemented");
|
||||
const value = ziggurat.next_f64(r, ziggurat.ExpDist);
|
||||
switch (T) {
|
||||
f32 => return f32(value),
|
||||
f64 => return value,
|
||||
else => @compileError("unknown floating point type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Shuffle a slice into a random order.
|
||||
|
||||
146
std/rand/ziggurat.zig
Normal file
146
std/rand/ziggurat.zig
Normal file
@ -0,0 +1,146 @@
|
||||
// Implements ZIGNOR [1].
|
||||
//
|
||||
// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to Generate Normal Random Samples*]
|
||||
// (https://www.doornik.com/research/ziggurat.pdf). Nuffield College, Oxford.
|
||||
//
|
||||
// rust/rand used as a reference;
|
||||
//
|
||||
// NOTE: This seems interesting but reference code is a bit hard to grok:
|
||||
// https://sbarral.github.io/etf.
|
||||
|
||||
const std = @import("../index.zig");
|
||||
const math = std.math;
|
||||
const Random = std.rand.Random;
|
||||
|
||||
pub fn next_f64(random: &Random, comptime tables: &const ZigTable) f64 {
|
||||
while (true) {
|
||||
// We manually construct a float from parts as we can avoid an extra random lookup here by
|
||||
// using the unused exponent for the lookup table entry.
|
||||
const bits = random.scalar(u64);
|
||||
const i = usize(bits & 0xff);
|
||||
|
||||
const u = blk: {
|
||||
if (tables.is_symmetric) {
|
||||
// Generate a value in the range [2, 4) and scale into [-1, 1)
|
||||
const repr = ((0x3ff + 1) << 52) | (bits >> 12);
|
||||
break :blk @bitCast(f64, repr) - 3.0;
|
||||
} else {
|
||||
// Generate a value in the range [1, 2) and scale into (0, 1)
|
||||
const repr = (0x3ff << 52) | (bits >> 12);
|
||||
break :blk @bitCast(f64, repr) - (1.0 - math.f64_epsilon / 2.0);
|
||||
}
|
||||
};
|
||||
|
||||
const x = u * tables.x[i];
|
||||
const test_x = if (tables.is_symmetric) math.fabs(x) else x;
|
||||
|
||||
// equivalent to |u| < tables.x[i+1] / tables.x[i] (or u < tables.x[i+1] / tables.x[i])
|
||||
if (test_x < tables.x[i + 1]) {
|
||||
return x;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
return tables.zero_case(random, u);
|
||||
}
|
||||
|
||||
// equivalent to f1 + DRanU() * (f0 - f1) < 1
|
||||
if (tables.f[i + 1] + (tables.f[i] - tables.f[i + 1]) * random.float(f64) < tables.pdf(x)) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const ZigTable = struct {
|
||||
r: f64,
|
||||
x: [257]f64,
|
||||
f: [257]f64,
|
||||
|
||||
// probability density function used as a fallback
|
||||
pdf: fn(f64) f64,
|
||||
// whether the distribution is symmetric
|
||||
is_symmetric: bool,
|
||||
// fallback calculation in the case we are in the 0 block
|
||||
zero_case: fn(&Random, f64) f64,
|
||||
};
|
||||
|
||||
// zigNorInit
|
||||
fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64,
|
||||
comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable {
|
||||
var tables: ZigTable = undefined;
|
||||
|
||||
tables.is_symmetric = is_symmetric;
|
||||
tables.r = r;
|
||||
tables.pdf = f;
|
||||
tables.zero_case = zero_case;
|
||||
|
||||
tables.x[0] = v / f(r);
|
||||
tables.x[1] = r;
|
||||
|
||||
for (tables.x[2..256]) |*entry, i| {
|
||||
const last = tables.x[2 + i - 1];
|
||||
*entry = f_inv(v / last + f(last));
|
||||
}
|
||||
tables.x[256] = 0;
|
||||
|
||||
for (tables.f[0..]) |*entry, i| {
|
||||
*entry = f(tables.x[i]);
|
||||
}
|
||||
|
||||
return tables;
|
||||
}
|
||||
|
||||
// N(0, 1)
|
||||
pub const NormDist = blk: {
|
||||
@setEvalBranchQuota(30000);
|
||||
break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case);
|
||||
};
|
||||
|
||||
const norm_r = 3.6541528853610088;
|
||||
const norm_v = 0.00492867323399;
|
||||
|
||||
fn norm_f(x: f64) f64 { return math.exp(-x * x / 2.0); }
|
||||
fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); }
|
||||
fn norm_zero_case(random: &Random, u: f64) f64 {
|
||||
var x: f64 = 1;
|
||||
var y: f64 = 0;
|
||||
|
||||
while (-2.0 * y < x * x) {
|
||||
x = math.ln(random.float(f64)) / norm_r;
|
||||
y = math.ln(random.float(f64));
|
||||
}
|
||||
|
||||
if (u < 0) {
|
||||
return x - norm_r;
|
||||
} else {
|
||||
return norm_r - x;
|
||||
}
|
||||
}
|
||||
|
||||
test "ziggurant normal dist sanity" {
|
||||
var prng = std.rand.DefaultPrng.init(0);
|
||||
var i: usize = 0;
|
||||
while (i < 1000) : (i += 1) {
|
||||
_ = prng.random.floatNorm(f64);
|
||||
}
|
||||
}
|
||||
|
||||
// Exp(1)
|
||||
pub const ExpDist = blk: {
|
||||
@setEvalBranchQuota(30000);
|
||||
break :blk ZigTableGen(false, exp_r, exp_v, exp_f, exp_f_inv, exp_zero_case);
|
||||
};
|
||||
|
||||
const exp_r = 7.69711747013104972;
|
||||
const exp_v = 0.0039496598225815571993;
|
||||
|
||||
fn exp_f(x: f64) f64 { return math.exp(-x); }
|
||||
fn exp_f_inv(y: f64) f64 { return -math.ln(y); }
|
||||
fn exp_zero_case(random: &Random, _: f64) f64 { return exp_r - math.ln(random.float(f64)); }
|
||||
|
||||
test "ziggurant exp dist sanity" {
|
||||
var prng = std.rand.DefaultPrng.init(0);
|
||||
var i: usize = 0;
|
||||
while (i < 1000) : (i += 1) {
|
||||
_ = prng.random.floatExp(f64);
|
||||
}
|
||||
}
|
||||
379
std/segmented_list.zig
Normal file
379
std/segmented_list.zig
Normal file
@ -0,0 +1,379 @@
|
||||
const std = @import("index.zig");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
// Imagine that `fn at(self: &Self, index: usize) &T` is a customer asking for a box
|
||||
// from a warehouse, based on a flat array, boxes ordered from 0 to N - 1.
|
||||
// But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes.
|
||||
// So when the customer requests a box index, we have to translate it to shelf index
|
||||
// and box index within that shelf. Illustration:
|
||||
//
|
||||
// customer indexes:
|
||||
// shelf 0: 0
|
||||
// shelf 1: 1 2
|
||||
// shelf 2: 3 4 5 6
|
||||
// shelf 3: 7 8 9 10 11 12 13 14
|
||||
// shelf 4: 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
||||
// shelf 5: 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
|
||||
// ...
|
||||
//
|
||||
// warehouse indexes:
|
||||
// shelf 0: 0
|
||||
// shelf 1: 0 1
|
||||
// shelf 2: 0 1 2 3
|
||||
// shelf 3: 0 1 2 3 4 5 6 7
|
||||
// shelf 4: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||||
// shelf 5: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
||||
// ...
|
||||
//
|
||||
// With this arrangement, here are the equations to get the shelf index and
|
||||
// box index based on customer box index:
|
||||
//
|
||||
// shelf_index = floor(log2(customer_index + 1))
|
||||
// shelf_count = ceil(log2(box_count + 1))
|
||||
// box_index = customer_index + 1 - 2 ** shelf
|
||||
// shelf_size = 2 ** shelf_index
|
||||
//
|
||||
// Now we complicate it a little bit further by adding a preallocated shelf, which must be
|
||||
// a power of 2:
|
||||
// prealloc=4
|
||||
//
|
||||
// customer indexes:
|
||||
// prealloc: 0 1 2 3
|
||||
// shelf 0: 4 5 6 7 8 9 10 11
|
||||
// shelf 1: 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
||||
// shelf 2: 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
||||
// ...
|
||||
//
|
||||
// warehouse indexes:
|
||||
// prealloc: 0 1 2 3
|
||||
// shelf 0: 0 1 2 3 4 5 6 7
|
||||
// shelf 1: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||||
// shelf 2: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
||||
// ...
|
||||
//
|
||||
// Now the equations are:
|
||||
//
|
||||
// shelf_index = floor(log2(customer_index + prealloc)) - log2(prealloc) - 1
|
||||
// shelf_count = ceil(log2(box_count + prealloc)) - log2(prealloc) - 1
|
||||
// box_index = customer_index + prealloc - 2 ** (log2(prealloc) + 1 + shelf)
|
||||
// shelf_size = prealloc * 2 ** (shelf_index + 1)
|
||||
|
||||
/// This is a stack data structure where pointers to indexes have the same lifetime as the data structure
|
||||
/// itself, unlike ArrayList where push() invalidates all existing element pointers.
|
||||
/// The tradeoff is that elements are not guaranteed to be contiguous. For that, use ArrayList.
|
||||
/// Note however that most elements are contiguous, making this data structure cache-friendly.
|
||||
///
|
||||
/// Because it never has to copy elements from an old location to a new location, it does not require
|
||||
/// its elements to be copyable, and it avoids wasting memory when backed by an ArenaAllocator.
|
||||
/// Note that the push() and pop() convenience methods perform a copy, but you can instead use
|
||||
/// addOne(), at(), setCapacity(), and shrinkCapacity() to avoid copying items.
|
||||
///
|
||||
/// This data structure has O(1) push and O(1) pop.
|
||||
///
|
||||
/// It supports preallocated elements, making it especially well suited when the expected maximum
|
||||
/// size is small. `prealloc_item_count` must be 0, or a power of 2.
|
||||
pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type {
|
||||
return struct {
|
||||
const Self = this;
|
||||
const prealloc_exp = blk: {
|
||||
// we don't use the prealloc_exp constant when prealloc_item_count is 0.
|
||||
assert(prealloc_item_count != 0);
|
||||
|
||||
const value = std.math.log2_int(usize, prealloc_item_count);
|
||||
assert((1 << value) == prealloc_item_count); // prealloc_item_count must be a power of 2
|
||||
break :blk @typeOf(1)(value);
|
||||
};
|
||||
const ShelfIndex = std.math.Log2Int(usize);
|
||||
|
||||
prealloc_segment: [prealloc_item_count]T,
|
||||
dynamic_segments: []&T,
|
||||
allocator: &Allocator,
|
||||
len: usize,
|
||||
|
||||
pub const prealloc_count = prealloc_item_count;
|
||||
|
||||
/// Deinitialize with `deinit`
|
||||
pub fn init(allocator: &Allocator) Self {
|
||||
return Self {
|
||||
.allocator = allocator,
|
||||
.len = 0,
|
||||
.prealloc_segment = undefined,
|
||||
.dynamic_segments = []&T{},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: &Self) void {
|
||||
self.freeShelves(ShelfIndex(self.dynamic_segments.len), 0);
|
||||
self.allocator.free(self.dynamic_segments);
|
||||
*self = undefined;
|
||||
}
|
||||
|
||||
pub fn at(self: &Self, i: usize) &T {
|
||||
assert(i < self.len);
|
||||
return self.uncheckedAt(i);
|
||||
}
|
||||
|
||||
pub fn count(self: &const Self) usize {
|
||||
return self.len;
|
||||
}
|
||||
|
||||
pub fn push(self: &Self, item: &const T) !void {
|
||||
const new_item_ptr = try self.addOne();
|
||||
*new_item_ptr = *item;
|
||||
}
|
||||
|
||||
pub fn pushMany(self: &Self, items: []const T) !void {
|
||||
for (items) |item| {
|
||||
try self.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop(self: &Self) ?T {
|
||||
if (self.len == 0)
|
||||
return null;
|
||||
|
||||
const index = self.len - 1;
|
||||
const result = *self.uncheckedAt(index);
|
||||
self.len = index;
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn addOne(self: &Self) !&T {
|
||||
const new_length = self.len + 1;
|
||||
try self.growCapacity(new_length);
|
||||
const result = self.uncheckedAt(self.len);
|
||||
self.len = new_length;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Grows or shrinks capacity to match usage.
|
||||
pub fn setCapacity(self: &Self, new_capacity: usize) !void {
|
||||
if (new_capacity <= usize(1) << (prealloc_exp + self.dynamic_segments.len)) {
|
||||
return self.shrinkCapacity(new_capacity);
|
||||
} else {
|
||||
return self.growCapacity(new_capacity);
|
||||
}
|
||||
}
|
||||
|
||||
/// Only grows capacity, or retains current capacity
|
||||
pub fn growCapacity(self: &Self, new_capacity: usize) !void {
|
||||
const new_cap_shelf_count = shelfCount(new_capacity);
|
||||
const old_shelf_count = ShelfIndex(self.dynamic_segments.len);
|
||||
if (new_cap_shelf_count > old_shelf_count) {
|
||||
self.dynamic_segments = try self.allocator.realloc(&T, self.dynamic_segments, new_cap_shelf_count);
|
||||
var i = old_shelf_count;
|
||||
errdefer {
|
||||
self.freeShelves(i, old_shelf_count);
|
||||
self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, old_shelf_count);
|
||||
}
|
||||
while (i < new_cap_shelf_count) : (i += 1) {
|
||||
self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Only shrinks capacity or retains current capacity
|
||||
pub fn shrinkCapacity(self: &Self, new_capacity: usize) void {
|
||||
if (new_capacity <= prealloc_item_count) {
|
||||
const len = ShelfIndex(self.dynamic_segments.len);
|
||||
self.freeShelves(len, 0);
|
||||
self.allocator.free(self.dynamic_segments);
|
||||
self.dynamic_segments = []&T{};
|
||||
return;
|
||||
}
|
||||
|
||||
const new_cap_shelf_count = shelfCount(new_capacity);
|
||||
const old_shelf_count = ShelfIndex(self.dynamic_segments.len);
|
||||
assert(new_cap_shelf_count <= old_shelf_count);
|
||||
if (new_cap_shelf_count == old_shelf_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.freeShelves(old_shelf_count, new_cap_shelf_count);
|
||||
self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, new_cap_shelf_count);
|
||||
}
|
||||
|
||||
pub fn uncheckedAt(self: &Self, index: usize) &T {
|
||||
if (index < prealloc_item_count) {
|
||||
return &self.prealloc_segment[index];
|
||||
}
|
||||
const shelf_index = shelfIndex(index);
|
||||
const box_index = boxIndex(index, shelf_index);
|
||||
return &self.dynamic_segments[shelf_index][box_index];
|
||||
}
|
||||
|
||||
fn shelfCount(box_count: usize) ShelfIndex {
|
||||
if (prealloc_item_count == 0) {
|
||||
return std.math.log2_int_ceil(usize, box_count + 1);
|
||||
}
|
||||
return std.math.log2_int_ceil(usize, box_count + prealloc_item_count) - prealloc_exp - 1;
|
||||
}
|
||||
|
||||
fn shelfSize(shelf_index: ShelfIndex) usize {
|
||||
if (prealloc_item_count == 0) {
|
||||
return usize(1) << shelf_index;
|
||||
}
|
||||
return usize(1) << (shelf_index + (prealloc_exp + 1));
|
||||
}
|
||||
|
||||
fn shelfIndex(list_index: usize) ShelfIndex {
|
||||
if (prealloc_item_count == 0) {
|
||||
return std.math.log2_int(usize, list_index + 1);
|
||||
}
|
||||
return std.math.log2_int(usize, list_index + prealloc_item_count) - prealloc_exp - 1;
|
||||
}
|
||||
|
||||
fn boxIndex(list_index: usize, shelf_index: ShelfIndex) usize {
|
||||
if (prealloc_item_count == 0) {
|
||||
return (list_index + 1) - (usize(1) << shelf_index);
|
||||
}
|
||||
return list_index + prealloc_item_count - (usize(1) << ((prealloc_exp + 1) + shelf_index));
|
||||
}
|
||||
|
||||
fn freeShelves(self: &Self, from_count: ShelfIndex, to_count: ShelfIndex) void {
|
||||
var i = from_count;
|
||||
while (i != to_count) {
|
||||
i -= 1;
|
||||
self.allocator.free(self.dynamic_segments[i][0..shelfSize(i)]);
|
||||
}
|
||||
}
|
||||
|
||||
pub const Iterator = struct {
|
||||
list: &Self,
|
||||
index: usize,
|
||||
box_index: usize,
|
||||
shelf_index: ShelfIndex,
|
||||
shelf_size: usize,
|
||||
|
||||
pub fn next(it: &Iterator) ?&T {
|
||||
if (it.index >= it.list.len)
|
||||
return null;
|
||||
if (it.index < prealloc_item_count) {
|
||||
const ptr = &it.list.prealloc_segment[it.index];
|
||||
it.index += 1;
|
||||
if (it.index == prealloc_item_count) {
|
||||
it.box_index = 0;
|
||||
it.shelf_index = 0;
|
||||
it.shelf_size = prealloc_item_count * 2;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
const ptr = &it.list.dynamic_segments[it.shelf_index][it.box_index];
|
||||
it.index += 1;
|
||||
it.box_index += 1;
|
||||
if (it.box_index == it.shelf_size) {
|
||||
it.shelf_index += 1;
|
||||
it.box_index = 0;
|
||||
it.shelf_size *= 2;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
pub fn prev(it: &Iterator) ?&T {
|
||||
if (it.index == 0)
|
||||
return null;
|
||||
|
||||
it.index -= 1;
|
||||
if (it.index < prealloc_item_count)
|
||||
return &it.list.prealloc_segment[it.index];
|
||||
|
||||
if (it.box_index == 0) {
|
||||
it.shelf_index -= 1;
|
||||
it.shelf_size /= 2;
|
||||
it.box_index = it.shelf_size - 1;
|
||||
} else {
|
||||
it.box_index -= 1;
|
||||
}
|
||||
|
||||
return &it.list.dynamic_segments[it.shelf_index][it.box_index];
|
||||
}
|
||||
|
||||
pub fn peek(it: &Iterator) ?&T {
|
||||
if (it.index >= it.list.len)
|
||||
return null;
|
||||
if (it.index < prealloc_item_count)
|
||||
return &it.list.prealloc_segment[it.index];
|
||||
|
||||
return &it.list.dynamic_segments[it.shelf_index][it.box_index];
|
||||
}
|
||||
};
|
||||
|
||||
pub fn iterator(self: &Self, start_index: usize) Iterator {
|
||||
var it = Iterator {
|
||||
.list = self,
|
||||
.index = start_index,
|
||||
.shelf_index = undefined,
|
||||
.box_index = undefined,
|
||||
.shelf_size = undefined,
|
||||
};
|
||||
if (start_index >= prealloc_item_count) {
|
||||
it.shelf_index = shelfIndex(start_index);
|
||||
it.box_index = boxIndex(start_index, it.shelf_index);
|
||||
it.shelf_size = shelfSize(it.shelf_index);
|
||||
}
|
||||
return it;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "std.SegmentedList" {
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
var a = &da.allocator;
|
||||
|
||||
try testSegmentedList(0, a);
|
||||
try testSegmentedList(1, a);
|
||||
try testSegmentedList(2, a);
|
||||
try testSegmentedList(4, a);
|
||||
try testSegmentedList(8, a);
|
||||
try testSegmentedList(16, a);
|
||||
}
|
||||
|
||||
fn testSegmentedList(comptime prealloc: usize, allocator: &Allocator) !void {
|
||||
var list = SegmentedList(i32, prealloc).init(allocator);
|
||||
defer list.deinit();
|
||||
|
||||
{var i: usize = 0; while (i < 100) : (i += 1) {
|
||||
try list.push(i32(i + 1));
|
||||
assert(list.len == i + 1);
|
||||
}}
|
||||
|
||||
{var i: usize = 0; while (i < 100) : (i += 1) {
|
||||
assert(*list.at(i) == i32(i + 1));
|
||||
}}
|
||||
|
||||
{
|
||||
var it = list.iterator(0);
|
||||
var x: i32 = 0;
|
||||
while (it.next()) |item| {
|
||||
x += 1;
|
||||
assert(*item == x);
|
||||
}
|
||||
assert(x == 100);
|
||||
while (it.prev()) |item| : (x -= 1) {
|
||||
assert(*item == x);
|
||||
}
|
||||
assert(x == 0);
|
||||
}
|
||||
|
||||
assert(??list.pop() == 100);
|
||||
assert(list.len == 99);
|
||||
|
||||
try list.pushMany([]i32 { 1, 2, 3 });
|
||||
assert(list.len == 102);
|
||||
assert(??list.pop() == 3);
|
||||
assert(??list.pop() == 2);
|
||||
assert(??list.pop() == 1);
|
||||
assert(list.len == 99);
|
||||
|
||||
try list.pushMany([]const i32 {});
|
||||
assert(list.len == 99);
|
||||
|
||||
var i: i32 = 99;
|
||||
while (list.pop()) |item| : (i -= 1) {
|
||||
assert(item == i);
|
||||
list.shrinkCapacity(list.len);
|
||||
}
|
||||
}
|
||||
@ -48,22 +48,33 @@ extern fn WinMainCRTStartup() noreturn {
|
||||
fn posixCallMainAndExit() noreturn {
|
||||
const argc = *argc_ptr;
|
||||
const argv = @ptrCast(&&u8, &argc_ptr[1]);
|
||||
const envp = @ptrCast(&?&u8, &argv[argc + 1]);
|
||||
const envp_nullable = @ptrCast(&?&u8, &argv[argc + 1]);
|
||||
var envp_count: usize = 0;
|
||||
while (envp_nullable[envp_count]) |_| : (envp_count += 1) {}
|
||||
const envp = @ptrCast(&&u8, envp_nullable)[0..envp_count];
|
||||
if (builtin.os == builtin.Os.linux) {
|
||||
const auxv = &@ptrCast(&usize, envp.ptr)[envp_count + 1];
|
||||
var i: usize = 0;
|
||||
while (auxv[i] != 0) : (i += 2) {
|
||||
if (auxv[i] < std.os.linux_aux_raw.len) std.os.linux_aux_raw[auxv[i]] = auxv[i+1];
|
||||
}
|
||||
std.debug.assert(std.os.linux_aux_raw[std.elf.AT_PAGESZ] == std.os.page_size);
|
||||
}
|
||||
|
||||
std.os.posix.exit(callMainWithArgs(argc, argv, envp));
|
||||
}
|
||||
|
||||
fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) u8 {
|
||||
fn callMainWithArgs(argc: usize, argv: &&u8, envp: []&u8) u8 {
|
||||
std.os.ArgIteratorPosix.raw = argv[0..argc];
|
||||
|
||||
var env_count: usize = 0;
|
||||
while (envp[env_count] != null) : (env_count += 1) {}
|
||||
std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count];
|
||||
|
||||
std.os.posix_environ_raw = envp;
|
||||
return callMain();
|
||||
}
|
||||
|
||||
extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) i32 {
|
||||
return callMainWithArgs(usize(c_argc), c_argv, c_envp);
|
||||
var env_count: usize = 0;
|
||||
while (c_envp[env_count] != null) : (env_count += 1) {}
|
||||
const envp = @ptrCast(&&u8, c_envp)[0..env_count];
|
||||
return callMainWithArgs(usize(c_argc), c_argv, envp);
|
||||
}
|
||||
|
||||
fn callMain() u8 {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user