| author | |
| committer | |
| log | bccef441963434b452a623abbb9315fd92c1e255 |
| tree | 4c0ea89b8fa895b3d34749ec089e45438d79466b |
| parent | 0c06a1885fad9a9bb85342632a0b7c8a3a0733e9 |
| parent | 041212a41cfaf029dc3eb9740467b721c76f406c |
Syncing with master branch because I want to re-run
update_clang_options.zig in the llvm12 branch.52 files changed, 2354 insertions(+), 379 deletions(-)
CMakeLists.txt+21-20| ... | ... | @@ -801,31 +801,32 @@ endif() |
| 801 | 801 | |
| 802 | 802 | install(TARGETS zig DESTINATION bin) |
| 803 | 803 | |
| 804 | set(ZIG_INSTALL_ARGS "build" | |
| 805 | --override-lib-dir "${CMAKE_SOURCE_DIR}/lib" | |
| 806 | "-Dlib-files-only" | |
| 807 | --prefix "${CMAKE_INSTALL_PREFIX}" | |
| 808 | "-Dconfig_h=${ZIG_CONFIG_H_OUT}" | |
| 809 | install | |
| 810 | ) | |
| 804 | set(ZIG_SKIP_INSTALL_LIB_FILES off CACHE BOOL | |
| 805 | "Disable copying lib/ files to install prefix during the build phase") | |
| 806 | ||
| 807 | if(NOT ZIG_SKIP_INSTALL_LIB_FILES) | |
| 808 | set(ZIG_INSTALL_ARGS "build" | |
| 809 | --override-lib-dir "${CMAKE_SOURCE_DIR}/lib" | |
| 810 | "-Dlib-files-only" | |
| 811 | --prefix "${CMAKE_INSTALL_PREFIX}" | |
| 812 | "-Dconfig_h=${ZIG_CONFIG_H_OUT}" | |
| 813 | install | |
| 814 | ) | |
| 811 | 815 | |
| 812 | # CODE has no effect with Visual Studio build system generator, therefore | |
| 813 | # when using Visual Studio build system generator we resort to running | |
| 814 | # `zig build install` during the build phase. | |
| 815 | if(MSVC) | |
| 816 | set(ZIG_SKIP_INSTALL_LIB_FILES off CACHE BOOL | |
| 817 | "Windows-only: Disable copying lib/ files to install prefix during the build phase") | |
| 818 | if(NOT ZIG_SKIP_INSTALL_LIB_FILES) | |
| 816 | # CODE has no effect with Visual Studio build system generator, therefore | |
| 817 | # when using Visual Studio build system generator we resort to running | |
| 818 | # `zig build install` during the build phase. | |
| 819 | if(MSVC) | |
| 819 | 820 | add_custom_target(zig_install_lib_files ALL |
| 820 | 821 | COMMAND zig ${ZIG_INSTALL_ARGS} |
| 821 | 822 | DEPENDS zig |
| 822 | 823 | WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" |
| 823 | 824 | ) |
| 825 | else() | |
| 826 | get_target_property(zig_BINARY_DIR zig BINARY_DIR) | |
| 827 | install(CODE "set(zig_EXE \"${ZIG_EXECUTABLE}\")") | |
| 828 | install(CODE "set(ZIG_INSTALL_ARGS \"${ZIG_INSTALL_ARGS}\")") | |
| 829 | install(CODE "set(CMAKE_SOURCE_DIR \"${CMAKE_SOURCE_DIR}\")") | |
| 830 | install(SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/cmake/install.cmake) | |
| 824 | 831 | endif() |
| 825 | else() | |
| 826 | get_target_property(zig_BINARY_DIR zig BINARY_DIR) | |
| 827 | install(CODE "set(zig_EXE \"${ZIG_EXECUTABLE}\")") | |
| 828 | install(CODE "set(ZIG_INSTALL_ARGS \"${ZIG_INSTALL_ARGS}\")") | |
| 829 | install(CODE "set(CMAKE_SOURCE_DIR \"${CMAKE_SOURCE_DIR}\")") | |
| 830 | install(SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/cmake/install.cmake) | |
| 831 | 832 | endif() |
doc/langref.html.in+51-23| ... | ... | @@ -10447,13 +10447,40 @@ fn readU32Be() u32 {} |
| 10447 | 10447 | {#header_close#} |
| 10448 | 10448 | {#header_open|Source Encoding#} |
| 10449 | 10449 | <p>Zig source code is encoded in UTF-8. An invalid UTF-8 byte sequence results in a compile error.</p> |
| 10450 | <p>Throughout all zig source code (including in comments), some codepoints are never allowed:</p> | |
| 10450 | <p>Throughout all zig source code (including in comments), some code points are never allowed:</p> | |
| 10451 | 10451 | <ul> |
| 10452 | <li>Ascii control characters, except for U+000a (LF): U+0000 - U+0009, U+000b - U+0001f, U+007f. (Note that Windows line endings (CRLF) are not allowed, and hard tabs are not allowed.)</li> | |
| 10452 | <li>Ascii control characters, except for U+000a (LF), U+000d (CR), and U+0009 (HT): U+0000 - U+0008, U+000b - U+000c, U+000e - U+0001f, U+007f.</li> | |
| 10453 | 10453 | <li>Non-Ascii Unicode line endings: U+0085 (NEL), U+2028 (LS), U+2029 (PS).</li> |
| 10454 | 10454 | </ul> |
| 10455 | <p>The codepoint U+000a (LF) (which is encoded as the single-byte value 0x0a) is the line terminator character. This character always terminates a line of zig source code (except possibly the last line of the file).</p> | |
| 10456 | <p>For some discussion on the rationale behind these design decisions, see <a href="https://github.com/ziglang/zig/issues/663">issue #663</a></p> | |
| 10455 | <p> | |
| 10456 | LF (byte value 0x0a, code point U+000a, {#syntax#}'\n'{#endsyntax#}) is the line terminator in Zig source code. | |
| 10457 | This byte value terminates every line of zig source code except the last line of the file. | |
| 10458 | It is recommended that non-empty source files end with an empty line, which means the last byte would be 0x0a (LF). | |
| 10459 | </p> | |
| 10460 | <p> | |
| 10461 | Each LF may be immediately preceded by a single CR (byte value 0x0d, code point U+000d, {#syntax#}'\r'{#endsyntax#}) | |
| 10462 | to form a Windows style line ending, but this is discouraged. | |
| 10463 | A CR in any other context is not allowed. | |
| 10464 | </p> | |
| 10465 | <p> | |
| 10466 | HT hard tabs (byte value 0x09, code point U+0009, {#syntax#}'\t'{#endsyntax#}) are interchangeable with | |
| 10467 | SP spaces (byte value 0x20, code point U+0020, {#syntax#}' '{#endsyntax#}) as a token separator, | |
| 10468 | but use of hard tabs is discouraged. See {#link|Grammar#}. | |
| 10469 | </p> | |
| 10470 | <p> | |
| 10471 | Note that running <code>zig fmt</code> on a source file will implement all recommendations mentioned here. | |
| 10472 | Note also that the stage1 compiler does <a href="https://github.com/ziglang/zig/wiki/FAQ#why-does-zig-force-me-to-use-spaces-instead-of-tabs">not yet support CR or HT</a> control characters. | |
| 10473 | </p> | |
| 10474 | <p> | |
| 10475 | Note that a tool reading Zig source code can make assumptions if the source code is assumed to be correct Zig code. | |
| 10476 | For example, when identifying the ends of lines, a tool can use a naive search such as <code>/\n/</code>, | |
| 10477 | or an <a href="https://msdn.microsoft.com/en-us/library/dd409797.aspx">advanced</a> | |
| 10478 | search such as <code>/\r\n?|[\n\u0085\u2028\u2029]/</code>, and in either case line endings will be correctly identified. | |
| 10479 | For another example, when identifying the whitespace before the first token on a line, | |
| 10480 | a tool can either use a naive search such as <code>/[ \t]/</code>, | |
| 10481 | or an <a href="https://tc39.es/ecma262/#sec-characterclassescape">advanced</a> search such as <code>/\s/</code>, | |
| 10482 | and in either case whitespace will be correctly identified. | |
| 10483 | </p> | |
| 10457 | 10484 | {#header_close#} |
| 10458 | 10485 | |
| 10459 | 10486 | {#header_open|Keyword Reference#} |
| ... | ... | @@ -11373,6 +11400,7 @@ ExprList &lt;- (Expr COMMA)* Expr? |
| 11373 | 11400 | |
| 11374 | 11401 | # *** Tokens *** |
| 11375 | 11402 | eof &lt;- !. |
| 11403 | eol &lt;- ('\r'? '\n') | eof | |
| 11376 | 11404 | hex &lt;- [0-9a-fA-F] |
| 11377 | 11405 | hex_ &lt;- ('_'/hex) |
| 11378 | 11406 | dec &lt;- [0-9] |
| ... | ... | @@ -11382,39 +11410,39 @@ dec_int &lt;- dec (dec_* dec)? |
| 11382 | 11410 | hex_int &lt;- hex (hex_* dec)? |
| 11383 | 11411 | |
| 11384 | 11412 | char_escape |
| 11385 | &lt;- &quot;\\x&quot; hex hex | |
| 11386 | / &quot;\\u{&quot; hex+ &quot;}&quot; | |
| 11387 | / &quot;\\&quot; [nr\\t'&quot;] | |
| 11413 | &lt;- '\\x' hex hex | |
| 11414 | / '\\u{' hex+ '}' | |
| 11415 | / '\\' [nr\\t'&quot;] | |
| 11388 | 11416 | char_char |
| 11389 | 11417 | &lt;- char_escape |
| 11390 | / [^\\'\n] | |
| 11418 | / [^\\'\r\n] | |
| 11391 | 11419 | string_char |
| 11392 | 11420 | &lt;- char_escape |
| 11393 | / [^\\&quot;\n] | |
| 11421 | / [^\\&quot;\r\n] | |
| 11394 | 11422 | |
| 11395 | line_comment &lt;- '//'[^\n]* | |
| 11396 | line_string &lt;- (&quot;\\\\&quot; [^\n]* [ \n]*)+ | |
| 11397 | skip &lt;- ([ \n] / line_comment)* | |
| 11423 | line_comment &lt;- '//'[^\r\n]* eol | |
| 11424 | line_string &lt;- ('\\\\' [^\r\n]* eol skip)+ | |
| 11425 | skip &lt;- ([ \t] / eol / line_comment)* | |
| 11398 | 11426 | |
| 11399 | 11427 | CHAR_LITERAL &lt;- &quot;'&quot; char_char &quot;'&quot; skip |
| 11400 | 11428 | FLOAT |
| 11401 | &lt;- &quot;0x&quot; hex_* hex &quot;.&quot; hex_int ([pP] [-+]? hex_int)? skip | |
| 11402 | / dec_int &quot;.&quot; dec_int ([eE] [-+]? dec_int)? skip | |
| 11403 | / &quot;0x&quot; hex_* hex &quot;.&quot;? [pP] [-+]? hex_int skip | |
| 11404 | / dec_int &quot;.&quot;? [eE] [-+]? dec_int skip | |
| 11429 | &lt;- '0x' hex_* hex '.' hex_int ([pP] [-+]? hex_int)? skip | |
| 11430 | / dec_int '.' dec_int ([eE] [-+]? dec_int)? skip | |
| 11431 | / '0x' hex_* hex '.'? [pP] [-+]? hex_int skip | |
| 11432 | / dec_int '.'? [eE] [-+]? dec_int skip | |
| 11405 | 11433 | INTEGER |
| 11406 | &lt;- &quot;0b&quot; [_01]* [01] skip | |
| 11407 | / &quot;0o&quot; [_0-7]* [0-7] skip | |
| 11408 | / &quot;0x&quot; hex_* hex skip | |
| 11434 | &lt;- '0b' [_01]* [01] skip | |
| 11435 | / '0o' [_0-7]* [0-7] skip | |
| 11436 | / '0x' hex_* hex skip | |
| 11409 | 11437 | / dec_int skip |
| 11410 | STRINGLITERALSINGLE &lt;- &quot;\&quot;&quot; string_char* &quot;\&quot;&quot; skip | |
| 11438 | STRINGLITERALSINGLE &lt;- '&quot;' string_char* '&quot;' skip | |
| 11411 | 11439 | STRINGLITERAL |
| 11412 | 11440 | &lt;- STRINGLITERALSINGLE |
| 11413 | / line_string skip | |
| 11441 | / line_string skip | |
| 11414 | 11442 | IDENTIFIER |
| 11415 | 11443 | &lt;- !keyword [A-Za-z_] [A-Za-z0-9_]* skip |
| 11416 | / &quot;@\&quot;&quot; string_char* &quot;\&quot;&quot; skip | |
| 11417 | BUILTINIDENTIFIER &lt;- &quot;@&quot;[A-Za-z_][A-Za-z0-9_]* skip | |
| 11444 | / '@&quot;' string_char* '&quot;' skip | |
| 11445 | BUILTINIDENTIFIER &lt;- '@'[A-Za-z_][A-Za-z0-9_]* skip | |
| 11418 | 11446 | |
| 11419 | 11447 | |
| 11420 | 11448 | AMPERSAND &lt;- '&amp;' ![=] skip |
lib/std/bit_set.zig created+1255| ... | ... | @@ -0,0 +1,1255 @@ |
| 1 | // SPDX-License-Identifier: MIT | |
| 2 | // Copyright (c) 2021 Zig Contributors | |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | |
| 4 | // The MIT license requires this copyright notice to be included in all copies | |
| 5 | // and substantial portions of the software. | |
| 6 | ||
| 7 | //! This file defines several variants of bit sets. A bit set | |
| 8 | //! is a densely stored set of integers with a known maximum, | |
| 9 | //! in which each integer gets a single bit. Bit sets have very | |
| 10 | //! fast presence checks, update operations, and union and intersection | |
| 11 | //! operations. However, if the number of possible items is very | |
| 12 | //! large and the number of actual items in a given set is usually | |
| 13 | //! small, they may be less memory efficient than an array set. | |
| 14 | //! | |
| 15 | //! There are five variants defined here: | |
| 16 | //! | |
| 17 | //! IntegerBitSet: | |
| 18 | //! A bit set with static size, which is backed by a single integer. | |
| 19 | //! This set is good for sets with a small size, but may generate | |
| 20 | //! inefficient code for larger sets, especially in debug mode. | |
| 21 | //! | |
| 22 | //! ArrayBitSet: | |
| 23 | //! A bit set with static size, which is backed by an array of usize. | |
| 24 | //! This set is good for sets with a larger size, but may use | |
| 25 | //! more bytes than necessary if your set is small. | |
| 26 | //! | |
| 27 | //! StaticBitSet: | |
| 28 | //! Picks either IntegerBitSet or ArrayBitSet depending on the requested | |
| 29 | //! size. The interfaces of these two types match exactly, except for fields. | |
| 30 | //! | |
| 31 | //! DynamicBitSet: | |
| 32 | //! A bit set with runtime known size, backed by an allocated slice | |
| 33 | //! of usize. | |
| 34 | //! | |
| 35 | //! DynamicBitSetUnmanaged: | |
| 36 | //! A variant of DynamicBitSet which does not store a pointer to its | |
| 37 | //! allocator, in order to save space. | |
| 38 | ||
| 39 | const std = @import("std"); | |
| 40 | const assert = std.debug.assert; | |
| 41 | const Allocator = std.mem.Allocator; | |
| 42 | ||
| 43 | /// Returns the optimal static bit set type for the specified number | |
| 44 | /// of elements. The returned type will perform no allocations, | |
| 45 | /// can be copied by value, and does not require deinitialization. | |
| 46 | /// Both possible implementations fulfill the same interface. | |
| 47 | pub fn StaticBitSet(comptime size: usize) type { | |
| 48 | if (size <= @bitSizeOf(usize)) { | |
| 49 | return IntegerBitSet(size); | |
| 50 | } else { | |
| 51 | return ArrayBitSet(usize, size); | |
| 52 | } | |
| 53 | } | |
| 54 | ||
| 55 | /// A bit set with static size, which is backed by a single integer. | |
| 56 | /// This set is good for sets with a small size, but may generate | |
| 57 | /// inefficient code for larger sets, especially in debug mode. | |
| 58 | pub fn IntegerBitSet(comptime size: u16) type { | |
| 59 | return struct { | |
| 60 | const Self = @This(); | |
| 61 | ||
| 62 | // TODO: Make this a comptime field once those are fixed | |
| 63 | /// The number of items in this bit set | |
| 64 | pub const bit_length: usize = size; | |
| 65 | ||
| 66 | /// The integer type used to represent a mask in this bit set | |
| 67 | pub const MaskInt = std.meta.Int(.unsigned, size); | |
| 68 | ||
| 69 | /// The integer type used to shift a mask in this bit set | |
| 70 | pub const ShiftInt = std.math.Log2Int(MaskInt); | |
| 71 | ||
| 72 | /// The bit mask, as a single integer | |
| 73 | mask: MaskInt, | |
| 74 | ||
| 75 | /// Creates a bit set with no elements present. | |
| 76 | pub fn initEmpty() Self { | |
| 77 | return .{ .mask = 0 }; | |
| 78 | } | |
| 79 | ||
| 80 | /// Creates a bit set with all elements present. | |
| 81 | pub fn initFull() Self { | |
| 82 | return .{ .mask = ~@as(MaskInt, 0) }; | |
| 83 | } | |
| 84 | ||
| 85 | /// Returns the number of bits in this bit set | |
| 86 | pub fn capacity(self: Self) callconv(.Inline) usize { | |
| 87 | return bit_length; | |
| 88 | } | |
| 89 | ||
| 90 | /// Returns true if the bit at the specified index | |
| 91 | /// is present in the set, false otherwise. | |
| 92 | pub fn isSet(self: Self, index: usize) bool { | |
| 93 | assert(index < bit_length); | |
| 94 | return (self.mask & maskBit(index)) != 0; | |
| 95 | } | |
| 96 | ||
| 97 | /// Returns the total number of set bits in this bit set. | |
| 98 | pub fn count(self: Self) usize { | |
| 99 | return @popCount(MaskInt, self.mask); | |
| 100 | } | |
| 101 | ||
| 102 | /// Changes the value of the specified bit of the bit | |
| 103 | /// set to match the passed boolean. | |
| 104 | pub fn setValue(self: *Self, index: usize, value: bool) void { | |
| 105 | assert(index < bit_length); | |
| 106 | if (MaskInt == u0) return; | |
| 107 | const bit = maskBit(index); | |
| 108 | const new_bit = bit & std.math.boolMask(MaskInt, value); | |
| 109 | self.mask = (self.mask & ~bit) | new_bit; | |
| 110 | } | |
| 111 | ||
| 112 | /// Adds a specific bit to the bit set | |
| 113 | pub fn set(self: *Self, index: usize) void { | |
| 114 | assert(index < bit_length); | |
| 115 | self.mask |= maskBit(index); | |
| 116 | } | |
| 117 | ||
| 118 | /// Removes a specific bit from the bit set | |
| 119 | pub fn unset(self: *Self, index: usize) void { | |
| 120 | assert(index < bit_length); | |
| 121 | // Workaround for #7953 | |
| 122 | if (MaskInt == u0) return; | |
| 123 | self.mask &= ~maskBit(index); | |
| 124 | } | |
| 125 | ||
| 126 | /// Flips a specific bit in the bit set | |
| 127 | pub fn toggle(self: *Self, index: usize) void { | |
| 128 | assert(index < bit_length); | |
| 129 | self.mask ^= maskBit(index); | |
| 130 | } | |
| 131 | ||
| 132 | /// Flips all bits in this bit set which are present | |
| 133 | /// in the toggles bit set. | |
| 134 | pub fn toggleSet(self: *Self, toggles: Self) void { | |
| 135 | self.mask ^= toggles.mask; | |
| 136 | } | |
| 137 | ||
| 138 | /// Flips every bit in the bit set. | |
| 139 | pub fn toggleAll(self: *Self) void { | |
| 140 | self.mask = ~self.mask; | |
| 141 | } | |
| 142 | ||
| 143 | /// Performs a union of two bit sets, and stores the | |
| 144 | /// result in the first one. Bits in the result are | |
| 145 | /// set if the corresponding bits were set in either input. | |
| 146 | pub fn setUnion(self: *Self, other: Self) void { | |
| 147 | self.mask |= other.mask; | |
| 148 | } | |
| 149 | ||
| 150 | /// Performs an intersection of two bit sets, and stores | |
| 151 | /// the result in the first one. Bits in the result are | |
| 152 | /// set if the corresponding bits were set in both inputs. | |
| 153 | pub fn setIntersection(self: *Self, other: Self) void { | |
| 154 | self.mask &= other.mask; | |
| 155 | } | |
| 156 | ||
| 157 | /// Finds the index of the first set bit. | |
| 158 | /// If no bits are set, returns null. | |
| 159 | pub fn findFirstSet(self: Self) ?usize { | |
| 160 | const mask = self.mask; | |
| 161 | if (mask == 0) return null; | |
| 162 | return @ctz(MaskInt, mask); | |
| 163 | } | |
| 164 | ||
| 165 | /// Finds the index of the first set bit, and unsets it. | |
| 166 | /// If no bits are set, returns null. | |
| 167 | pub fn toggleFirstSet(self: *Self) ?usize { | |
| 168 | const mask = self.mask; | |
| 169 | if (mask == 0) return null; | |
| 170 | const index = @ctz(MaskInt, mask); | |
| 171 | self.mask = mask & (mask - 1); | |
| 172 | return index; | |
| 173 | } | |
| 174 | ||
| 175 | /// Iterates through the items in the set, according to the options. | |
| 176 | /// The default options (.{}) will iterate indices of set bits in | |
| 177 | /// ascending order. Modifications to the underlying bit set may | |
| 178 | /// or may not be observed by the iterator. | |
| 179 | pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options.direction) { | |
| 180 | return .{ | |
| 181 | .bits_remain = switch (options.kind) { | |
| 182 | .set => self.mask, | |
| 183 | .unset => ~self.mask, | |
| 184 | }, | |
| 185 | }; | |
| 186 | } | |
| 187 | ||
| 188 | fn Iterator(comptime direction: IteratorOptions.Direction) type { | |
| 189 | return struct { | |
| 190 | const IterSelf = @This(); | |
| 191 | // all bits which have not yet been iterated over | |
| 192 | bits_remain: MaskInt, | |
| 193 | ||
| 194 | /// Returns the index of the next unvisited set bit | |
| 195 | /// in the bit set, in ascending order. | |
| 196 | pub fn next(self: *IterSelf) ?usize { | |
| 197 | if (self.bits_remain == 0) return null; | |
| 198 | ||
| 199 | switch (direction) { | |
| 200 | .forward => { | |
| 201 | const next_index = @ctz(MaskInt, self.bits_remain); | |
| 202 | self.bits_remain &= self.bits_remain - 1; | |
| 203 | return next_index; | |
| 204 | }, | |
| 205 | .reverse => { | |
| 206 | const leading_zeroes = @clz(MaskInt, self.bits_remain); | |
| 207 | const top_bit = (@bitSizeOf(MaskInt) - 1) - leading_zeroes; | |
| 208 | self.bits_remain &= (@as(MaskInt, 1) << @intCast(ShiftInt, top_bit)) - 1; | |
| 209 | return top_bit; | |
| 210 | }, | |
| 211 | } | |
| 212 | } | |
| 213 | }; | |
| 214 | } | |
| 215 | ||
| 216 | fn maskBit(index: usize) MaskInt { | |
| 217 | if (MaskInt == u0) return 0; | |
| 218 | return @as(MaskInt, 1) << @intCast(ShiftInt, index); | |
| 219 | } | |
| 220 | fn boolMaskBit(index: usize, value: bool) MaskInt { | |
| 221 | if (MaskInt == u0) return 0; | |
| 222 | return @as(MaskInt, @boolToInt(value)) << @intCast(ShiftInt, index); | |
| 223 | } | |
| 224 | }; | |
| 225 | } | |
| 226 | ||
| 227 | /// A bit set with static size, which is backed by an array of usize. | |
| 228 | /// This set is good for sets with a larger size, but may use | |
| 229 | /// more bytes than necessary if your set is small. | |
| 230 | pub fn ArrayBitSet(comptime MaskIntType: type, comptime size: usize) type { | |
| 231 | const mask_info: std.builtin.TypeInfo = @typeInfo(MaskIntType); | |
| 232 | ||
| 233 | // Make sure the mask int is indeed an int | |
| 234 | if (mask_info != .Int) @compileError("ArrayBitSet can only operate on integer masks, but was passed " ++ @typeName(MaskIntType)); | |
| 235 | ||
| 236 | // It must also be unsigned. | |
| 237 | if (mask_info.Int.signedness != .unsigned) @compileError("ArrayBitSet requires an unsigned integer mask type, but was passed " ++ @typeName(MaskIntType)); | |
| 238 | ||
| 239 | // And it must not be empty. | |
| 240 | if (MaskIntType == u0) | |
| 241 | @compileError("ArrayBitSet requires a sized integer for its mask int. u0 does not work."); | |
| 242 | ||
| 243 | const byte_size = std.mem.byte_size_in_bits; | |
| 244 | ||
| 245 | // We use shift and truncate to decompose indices into mask indices and bit indices. | |
| 246 | // This operation requires that the mask has an exact power of two number of bits. | |
| 247 | if (!std.math.isPowerOfTwo(@bitSizeOf(MaskIntType))) { | |
| 248 | var desired_bits = std.math.ceilPowerOfTwoAssert(usize, @bitSizeOf(MaskIntType)); | |
| 249 | if (desired_bits < byte_size) desired_bits = byte_size; | |
| 250 | const FixedMaskType = std.meta.Int(.unsigned, desired_bits); | |
| 251 | @compileError("ArrayBitSet was passed integer type " ++ @typeName(MaskIntType) ++ | |
| 252 | ", which is not a power of two. Please round this up to a power of two integer size (i.e. " ++ @typeName(FixedMaskType) ++ ")."); | |
| 253 | } | |
| 254 | ||
| 255 | // Make sure the integer has no padding bits. | |
| 256 | // Those would be wasteful here and are probably a mistake by the user. | |
| 257 | // This case may be hit with small powers of two, like u4. | |
| 258 | if (@bitSizeOf(MaskIntType) != @sizeOf(MaskIntType) * byte_size) { | |
| 259 | var desired_bits = @sizeOf(MaskIntType) * byte_size; | |
| 260 | desired_bits = std.math.ceilPowerOfTwoAssert(usize, desired_bits); | |
| 261 | const FixedMaskType = std.meta.Int(.unsigned, desired_bits); | |
| 262 | @compileError("ArrayBitSet was passed integer type " ++ @typeName(MaskIntType) ++ | |
| 263 | ", which contains padding bits. Please round this up to an unpadded integer size (i.e. " ++ @typeName(FixedMaskType) ++ ")."); | |
| 264 | } | |
| 265 | ||
| 266 | return struct { | |
| 267 | const Self = @This(); | |
| 268 | ||
| 269 | // TODO: Make this a comptime field once those are fixed | |
| 270 | /// The number of items in this bit set | |
| 271 | pub const bit_length: usize = size; | |
| 272 | ||
| 273 | /// The integer type used to represent a mask in this bit set | |
| 274 | pub const MaskInt = MaskIntType; | |
| 275 | ||
| 276 | /// The integer type used to shift a mask in this bit set | |
| 277 | pub const ShiftInt = std.math.Log2Int(MaskInt); | |
| 278 | ||
| 279 | // bits in one mask | |
| 280 | const mask_len = @bitSizeOf(MaskInt); | |
| 281 | // total number of masks | |
| 282 | const num_masks = (size + mask_len - 1) / mask_len; | |
| 283 | // padding bits in the last mask (may be 0) | |
| 284 | const last_pad_bits = mask_len * num_masks - size; | |
| 285 | // Mask of valid bits in the last mask. | |
| 286 | // All functions will ensure that the invalid | |
| 287 | // bits in the last mask are zero. | |
| 288 | pub const last_item_mask = ~@as(MaskInt, 0) >> last_pad_bits; | |
| 289 | ||
| 290 | /// The bit masks, ordered with lower indices first. | |
| 291 | /// Padding bits at the end are undefined. | |
| 292 | masks: [num_masks]MaskInt, | |
| 293 | ||
| 294 | /// Creates a bit set with no elements present. | |
| 295 | pub fn initEmpty() Self { | |
| 296 | return .{ .masks = [_]MaskInt{0} ** num_masks }; | |
| 297 | } | |
| 298 | ||
| 299 | /// Creates a bit set with all elements present. | |
| 300 | pub fn initFull() Self { | |
| 301 | if (num_masks == 0) { | |
| 302 | return .{ .masks = .{} }; | |
| 303 | } else { | |
| 304 | return .{ .masks = [_]MaskInt{~@as(MaskInt, 0)} ** (num_masks - 1) ++ [_]MaskInt{last_item_mask} }; | |
| 305 | } | |
| 306 | } | |
| 307 | ||
| 308 | /// Returns the number of bits in this bit set | |
| 309 | pub fn capacity(self: Self) callconv(.Inline) usize { | |
| 310 | return bit_length; | |
| 311 | } | |
| 312 | ||
| 313 | /// Returns true if the bit at the specified index | |
| 314 | /// is present in the set, false otherwise. | |
| 315 | pub fn isSet(self: Self, index: usize) bool { | |
| 316 | assert(index < bit_length); | |
| 317 | if (num_masks == 0) return false; // doesn't compile in this case | |
| 318 | return (self.masks[maskIndex(index)] & maskBit(index)) != 0; | |
| 319 | } | |
| 320 | ||
| 321 | /// Returns the total number of set bits in this bit set. | |
| 322 | pub fn count(self: Self) usize { | |
| 323 | var total: usize = 0; | |
| 324 | for (self.masks) |mask| { | |
| 325 | total += @popCount(MaskInt, mask); | |
| 326 | } | |
| 327 | return total; | |
| 328 | } | |
| 329 | ||
| 330 | /// Changes the value of the specified bit of the bit | |
| 331 | /// set to match the passed boolean. | |
| 332 | pub fn setValue(self: *Self, index: usize, value: bool) void { | |
| 333 | assert(index < bit_length); | |
| 334 | if (num_masks == 0) return; // doesn't compile in this case | |
| 335 | const bit = maskBit(index); | |
| 336 | const mask_index = maskIndex(index); | |
| 337 | const new_bit = bit & std.math.boolMask(MaskInt, value); | |
| 338 | self.masks[mask_index] = (self.masks[mask_index] & ~bit) | new_bit; | |
| 339 | } | |
| 340 | ||
| 341 | /// Adds a specific bit to the bit set | |
| 342 | pub fn set(self: *Self, index: usize) void { | |
| 343 | assert(index < bit_length); | |
| 344 | if (num_masks == 0) return; // doesn't compile in this case | |
| 345 | self.masks[maskIndex(index)] |= maskBit(index); | |
| 346 | } | |
| 347 | ||
| 348 | /// Removes a specific bit from the bit set | |
| 349 | pub fn unset(self: *Self, index: usize) void { | |
| 350 | assert(index < bit_length); | |
| 351 | if (num_masks == 0) return; // doesn't compile in this case | |
| 352 | self.masks[maskIndex(index)] &= ~maskBit(index); | |
| 353 | } | |
| 354 | ||
| 355 | /// Flips a specific bit in the bit set | |
| 356 | pub fn toggle(self: *Self, index: usize) void { | |
| 357 | assert(index < bit_length); | |
| 358 | if (num_masks == 0) return; // doesn't compile in this case | |
| 359 | self.masks[maskIndex(index)] ^= maskBit(index); | |
| 360 | } | |
| 361 | ||
| 362 | /// Flips all bits in this bit set which are present | |
| 363 | /// in the toggles bit set. | |
| 364 | pub fn toggleSet(self: *Self, toggles: Self) void { | |
| 365 | for (self.masks) |*mask, i| { | |
| 366 | mask.* ^= toggles.masks[i]; | |
| 367 | } | |
| 368 | } | |
| 369 | ||
| 370 | /// Flips every bit in the bit set. | |
| 371 | pub fn toggleAll(self: *Self) void { | |
| 372 | for (self.masks) |*mask, i| { | |
| 373 | mask.* = ~mask.*; | |
| 374 | } | |
| 375 | ||
| 376 | // Zero the padding bits | |
| 377 | if (num_masks > 0) { | |
| 378 | self.masks[num_masks - 1] &= last_item_mask; | |
| 379 | } | |
| 380 | } | |
| 381 | ||
| 382 | /// Performs a union of two bit sets, and stores the | |
| 383 | /// result in the first one. Bits in the result are | |
| 384 | /// set if the corresponding bits were set in either input. | |
| 385 | pub fn setUnion(self: *Self, other: Self) void { | |
| 386 | for (self.masks) |*mask, i| { | |
| 387 | mask.* |= other.masks[i]; | |
| 388 | } | |
| 389 | } | |
| 390 | ||
| 391 | /// Performs an intersection of two bit sets, and stores | |
| 392 | /// the result in the first one. Bits in the result are | |
| 393 | /// set if the corresponding bits were set in both inputs. | |
| 394 | pub fn setIntersection(self: *Self, other: Self) void { | |
| 395 | for (self.masks) |*mask, i| { | |
| 396 | mask.* &= other.masks[i]; | |
| 397 | } | |
| 398 | } | |
| 399 | ||
| 400 | /// Finds the index of the first set bit. | |
| 401 | /// If no bits are set, returns null. | |
| 402 | pub fn findFirstSet(self: Self) ?usize { | |
| 403 | var offset: usize = 0; | |
| 404 | const mask = for (self.masks) |mask| { | |
| 405 | if (mask != 0) break mask; | |
| 406 | offset += @bitSizeOf(MaskInt); | |
| 407 | } else return null; | |
| 408 | return offset + @ctz(MaskInt, mask); | |
| 409 | } | |
| 410 | ||
| 411 | /// Finds the index of the first set bit, and unsets it. | |
| 412 | /// If no bits are set, returns null. | |
| 413 | pub fn toggleFirstSet(self: *Self) ?usize { | |
| 414 | var offset: usize = 0; | |
| 415 | const mask = for (self.masks) |*mask| { | |
| 416 | if (mask.* != 0) break mask; | |
| 417 | offset += @bitSizeOf(MaskInt); | |
| 418 | } else return null; | |
| 419 | const index = @ctz(MaskInt, mask.*); | |
| 420 | mask.* &= (mask.* - 1); | |
| 421 | return offset + index; | |
| 422 | } | |
| 423 | ||
| 424 | /// Iterates through the items in the set, according to the options. | |
| 425 | /// The default options (.{}) will iterate indices of set bits in | |
| 426 | /// ascending order. Modifications to the underlying bit set may | |
| 427 | /// or may not be observed by the iterator. | |
| 428 | pub fn iterator(self: *const Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) { | |
| 429 | return BitSetIterator(MaskInt, options).init(&self.masks, last_item_mask); | |
| 430 | } | |
| 431 | ||
| 432 | fn maskBit(index: usize) MaskInt { | |
| 433 | return @as(MaskInt, 1) << @truncate(ShiftInt, index); | |
| 434 | } | |
| 435 | fn maskIndex(index: usize) usize { | |
| 436 | return index >> @bitSizeOf(ShiftInt); | |
| 437 | } | |
| 438 | fn boolMaskBit(index: usize, value: bool) MaskInt { | |
| 439 | return @as(MaskInt, @boolToInt(value)) << @intCast(ShiftInt, index); | |
| 440 | } | |
| 441 | }; | |
| 442 | } | |
| 443 | ||
| 444 | /// A bit set with runtime known size, backed by an allocated slice | |
| 445 | /// of usize. The allocator must be tracked externally by the user. | |
| 446 | pub const DynamicBitSetUnmanaged = struct { | |
| 447 | const Self = @This(); | |
| 448 | ||
| 449 | /// The integer type used to represent a mask in this bit set | |
| 450 | pub const MaskInt = usize; | |
| 451 | ||
| 452 | /// The integer type used to shift a mask in this bit set | |
| 453 | pub const ShiftInt = std.math.Log2Int(MaskInt); | |
| 454 | ||
| 455 | /// The number of valid items in this bit set | |
| 456 | bit_length: usize = 0, | |
| 457 | ||
| 458 | /// The bit masks, ordered with lower indices first. | |
| 459 | /// Padding bits at the end must be zeroed. | |
| 460 | masks: [*]MaskInt = empty_masks_ptr, | |
| 461 | // This pointer is one usize after the actual allocation. | |
| 462 | // That slot holds the size of the true allocation, which | |
| 463 | // is needed by Zig's allocator interface in case a shrink | |
| 464 | // fails. | |
| 465 | ||
| 466 | // Don't modify this value. Ideally it would go in const data so | |
| 467 | // modifications would cause a bus error, but the only way | |
| 468 | // to discard a const qualifier is through ptrToInt, which | |
| 469 | // cannot currently round trip at comptime. | |
| 470 | var empty_masks_data = [_]MaskInt{ 0, undefined }; | |
| 471 | const empty_masks_ptr = empty_masks_data[1..2]; | |
| 472 | ||
| 473 | /// Creates a bit set with no elements present. | |
| 474 | /// If bit_length is not zero, deinit must eventually be called. | |
| 475 | pub fn initEmpty(bit_length: usize, allocator: *Allocator) !Self { | |
| 476 | var self = Self{}; | |
| 477 | try self.resize(bit_length, false, allocator); | |
| 478 | return self; | |
| 479 | } | |
| 480 | ||
| 481 | /// Creates a bit set with all elements present. | |
| 482 | /// If bit_length is not zero, deinit must eventually be called. | |
| 483 | pub fn initFull(bit_length: usize, allocator: *Allocator) !Self { | |
| 484 | var self = Self{}; | |
| 485 | try self.resize(bit_length, true, allocator); | |
| 486 | return self; | |
| 487 | } | |
| 488 | ||
| 489 | /// Resizes to a new bit_length. If the new length is larger | |
| 490 | /// than the old length, fills any added bits with `fill`. | |
| 491 | /// If new_len is not zero, deinit must eventually be called. | |
| 492 | pub fn resize(self: *@This(), new_len: usize, fill: bool, allocator: *Allocator) !void { | |
| 493 | const old_len = self.bit_length; | |
| 494 | ||
| 495 | const old_masks = numMasks(old_len); | |
| 496 | const new_masks = numMasks(new_len); | |
| 497 | ||
| 498 | const old_allocation = (self.masks - 1)[0..(self.masks - 1)[0]]; | |
| 499 | ||
| 500 | if (new_masks == 0) { | |
| 501 | assert(new_len == 0); | |
| 502 | allocator.free(old_allocation); | |
| 503 | self.masks = empty_masks_ptr; | |
| 504 | self.bit_length = 0; | |
| 505 | return; | |
| 506 | } | |
| 507 | ||
| 508 | if (old_allocation.len != new_masks + 1) realloc: { | |
| 509 | // If realloc fails, it may mean one of two things. | |
| 510 | // If we are growing, it means we are out of memory. | |
| 511 | // If we are shrinking, it means the allocator doesn't | |
| 512 | // want to move the allocation. This means we need to | |
| 513 | // hold on to the extra 8 bytes required to be able to free | |
| 514 | // this allocation properly. | |
| 515 | const new_allocation = allocator.realloc(old_allocation, new_masks + 1) catch |err| { | |
| 516 | if (new_masks + 1 > old_allocation.len) return err; | |
| 517 | break :realloc; | |
| 518 | }; | |
| 519 | ||
| 520 | new_allocation[0] = new_allocation.len; | |
| 521 | self.masks = new_allocation.ptr + 1; | |
| 522 | } | |
| 523 | ||
| 524 | // If we increased in size, we need to set any new bits | |
| 525 | // to the fill value. | |
| 526 | if (new_len > old_len) { | |
| 527 | // set the padding bits in the old last item to 1 | |
| 528 | if (fill and old_masks > 0) { | |
| 529 | const old_padding_bits = old_masks * @bitSizeOf(MaskInt) - old_len; | |
| 530 | const old_mask = (~@as(MaskInt, 0)) >> @intCast(ShiftInt, old_padding_bits); | |
| 531 | self.masks[old_masks - 1] |= ~old_mask; | |
| 532 | } | |
| 533 | ||
| 534 | // fill in any new masks | |
| 535 | if (new_masks > old_masks) { | |
| 536 | const fill_value = std.math.boolMask(MaskInt, fill); | |
| 537 | std.mem.set(MaskInt, self.masks[old_masks..new_masks], fill_value); | |
| 538 | } | |
| 539 | } | |
| 540 | ||
| 541 | // Zero out the padding bits | |
| 542 | if (new_len > 0) { | |
| 543 | const padding_bits = new_masks * @bitSizeOf(MaskInt) - new_len; | |
| 544 | const last_item_mask = (~@as(MaskInt, 0)) >> @intCast(ShiftInt, padding_bits); | |
| 545 | self.masks[new_masks - 1] &= last_item_mask; | |
| 546 | } | |
| 547 | ||
| 548 | // And finally, save the new length. | |
| 549 | self.bit_length = new_len; | |
| 550 | } | |
| 551 | ||
| 552 | /// deinitializes the array and releases its memory. | |
| 553 | /// The passed allocator must be the same one used for | |
| 554 | /// init* or resize in the past. | |
| 555 | pub fn deinit(self: *Self, allocator: *Allocator) void { | |
| 556 | self.resize(0, false, allocator) catch unreachable; | |
| 557 | } | |
| 558 | ||
| 559 | /// Creates a duplicate of this bit set, using the new allocator. | |
| 560 | pub fn clone(self: *const Self, new_allocator: *Allocator) !Self { | |
| 561 | const num_masks = numMasks(self.bit_length); | |
| 562 | var copy = Self{}; | |
| 563 | try copy.resize(self.bit_length, false, new_allocator); | |
| 564 | std.mem.copy(MaskInt, copy.masks[0..num_masks], self.masks[0..num_masks]); | |
| 565 | return copy; | |
| 566 | } | |
| 567 | ||
| 568 | /// Returns the number of bits in this bit set | |
| 569 | pub fn capacity(self: Self) callconv(.Inline) usize { | |
| 570 | return self.bit_length; | |
| 571 | } | |
| 572 | ||
| 573 | /// Returns true if the bit at the specified index | |
| 574 | /// is present in the set, false otherwise. | |
| 575 | pub fn isSet(self: Self, index: usize) bool { | |
| 576 | assert(index < self.bit_length); | |
| 577 | return (self.masks[maskIndex(index)] & maskBit(index)) != 0; | |
| 578 | } | |
| 579 | ||
| 580 | /// Returns the total number of set bits in this bit set. | |
| 581 | pub fn count(self: Self) usize { | |
| 582 | const num_masks = (self.bit_length + (@bitSizeOf(MaskInt) - 1)) / @bitSizeOf(MaskInt); | |
| 583 | var total: usize = 0; | |
| 584 | for (self.masks[0..num_masks]) |mask| { | |
| 585 | // Note: This is where we depend on padding bits being zero | |
| 586 | total += @popCount(MaskInt, mask); | |
| 587 | } | |
| 588 | return total; | |
| 589 | } | |
| 590 | ||
| 591 | /// Changes the value of the specified bit of the bit | |
| 592 | /// set to match the passed boolean. | |
| 593 | pub fn setValue(self: *Self, index: usize, value: bool) void { | |
| 594 | assert(index < self.bit_length); | |
| 595 | const bit = maskBit(index); | |
| 596 | const mask_index = maskIndex(index); | |
| 597 | const new_bit = bit & std.math.boolMask(MaskInt, value); | |
| 598 | self.masks[mask_index] = (self.masks[mask_index] & ~bit) | new_bit; | |
| 599 | } | |
| 600 | ||
| 601 | /// Adds a specific bit to the bit set | |
| 602 | pub fn set(self: *Self, index: usize) void { | |
| 603 | assert(index < self.bit_length); | |
| 604 | self.masks[maskIndex(index)] |= maskBit(index); | |
| 605 | } | |
| 606 | ||
| 607 | /// Removes a specific bit from the bit set | |
| 608 | pub fn unset(self: *Self, index: usize) void { | |
| 609 | assert(index < self.bit_length); | |
| 610 | self.masks[maskIndex(index)] &= ~maskBit(index); | |
| 611 | } | |
| 612 | ||
| 613 | /// Flips a specific bit in the bit set | |
| 614 | pub fn toggle(self: *Self, index: usize) void { | |
| 615 | assert(index < self.bit_length); | |
| 616 | self.masks[maskIndex(index)] ^= maskBit(index); | |
| 617 | } | |
| 618 | ||
| 619 | /// Flips all bits in this bit set which are present | |
| 620 | /// in the toggles bit set. Both sets must have the | |
| 621 | /// same bit_length. | |
| 622 | pub fn toggleSet(self: *Self, toggles: Self) void { | |
| 623 | assert(toggles.bit_length == self.bit_length); | |
| 624 | const num_masks = numMasks(self.bit_length); | |
| 625 | for (self.masks[0..num_masks]) |*mask, i| { | |
| 626 | mask.* ^= toggles.masks[i]; | |
| 627 | } | |
| 628 | } | |
| 629 | ||
| 630 | /// Flips every bit in the bit set. | |
| 631 | pub fn toggleAll(self: *Self) void { | |
| 632 | const bit_length = self.bit_length; | |
| 633 | // avoid underflow if bit_length is zero | |
| 634 | if (bit_length == 0) return; | |
| 635 | ||
| 636 | const num_masks = numMasks(self.bit_length); | |
| 637 | for (self.masks[0..num_masks]) |*mask, i| { | |
| 638 | mask.* = ~mask.*; | |
| 639 | } | |
| 640 | ||
| 641 | const padding_bits = num_masks * @bitSizeOf(MaskInt) - bit_length; | |
| 642 | const last_item_mask = (~@as(MaskInt, 0)) >> @intCast(ShiftInt, padding_bits); | |
| 643 | self.masks[num_masks - 1] &= last_item_mask; | |
| 644 | } | |
| 645 | ||
| 646 | /// Performs a union of two bit sets, and stores the | |
| 647 | /// result in the first one. Bits in the result are | |
| 648 | /// set if the corresponding bits were set in either input. | |
| 649 | /// The two sets must both be the same bit_length. | |
| 650 | pub fn setUnion(self: *Self, other: Self) void { | |
| 651 | assert(other.bit_length == self.bit_length); | |
| 652 | const num_masks = numMasks(self.bit_length); | |
| 653 | for (self.masks[0..num_masks]) |*mask, i| { | |
| 654 | mask.* |= other.masks[i]; | |
| 655 | } | |
| 656 | } | |
| 657 | ||
| 658 | /// Performs an intersection of two bit sets, and stores | |
| 659 | /// the result in the first one. Bits in the result are | |
| 660 | /// set if the corresponding bits were set in both inputs. | |
| 661 | /// The two sets must both be the same bit_length. | |
| 662 | pub fn setIntersection(self: *Self, other: Self) void { | |
| 663 | assert(other.bit_length == self.bit_length); | |
| 664 | const num_masks = numMasks(self.bit_length); | |
| 665 | for (self.masks[0..num_masks]) |*mask, i| { | |
| 666 | mask.* &= other.masks[i]; | |
| 667 | } | |
| 668 | } | |
| 669 | ||
| 670 | /// Finds the index of the first set bit. | |
| 671 | /// If no bits are set, returns null. | |
| 672 | pub fn findFirstSet(self: Self) ?usize { | |
| 673 | var offset: usize = 0; | |
| 674 | var mask = self.masks; | |
| 675 | while (offset < self.bit_length) { | |
| 676 | if (mask[0] != 0) break; | |
| 677 | mask += 1; | |
| 678 | offset += @bitSizeOf(MaskInt); | |
| 679 | } else return null; | |
| 680 | return offset + @ctz(MaskInt, mask[0]); | |
| 681 | } | |
| 682 | ||
| 683 | /// Finds the index of the first set bit, and unsets it. | |
| 684 | /// If no bits are set, returns null. | |
| 685 | pub fn toggleFirstSet(self: *Self) ?usize { | |
| 686 | var offset: usize = 0; | |
| 687 | var mask = self.masks; | |
| 688 | while (offset < self.bit_length) { | |
| 689 | if (mask[0] != 0) break; | |
| 690 | mask += 1; | |
| 691 | offset += @bitSizeOf(MaskInt); | |
| 692 | } else return null; | |
| 693 | const index = @ctz(MaskInt, mask[0]); | |
| 694 | mask[0] &= (mask[0] - 1); | |
| 695 | return offset + index; | |
| 696 | } | |
| 697 | ||
| 698 | /// Iterates through the items in the set, according to the options. | |
| 699 | /// The default options (.{}) will iterate indices of set bits in | |
| 700 | /// ascending order. Modifications to the underlying bit set may | |
| 701 | /// or may not be observed by the iterator. Resizing the underlying | |
| 702 | /// bit set invalidates the iterator. | |
| 703 | pub fn iterator(self: *const Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) { | |
| 704 | const num_masks = numMasks(self.bit_length); | |
| 705 | const padding_bits = num_masks * @bitSizeOf(MaskInt) - self.bit_length; | |
| 706 | const last_item_mask = (~@as(MaskInt, 0)) >> @intCast(ShiftInt, padding_bits); | |
| 707 | return BitSetIterator(MaskInt, options).init(self.masks[0..num_masks], last_item_mask); | |
| 708 | } | |
| 709 | ||
| 710 | fn maskBit(index: usize) MaskInt { | |
| 711 | return @as(MaskInt, 1) << @truncate(ShiftInt, index); | |
| 712 | } | |
| 713 | fn maskIndex(index: usize) usize { | |
| 714 | return index >> @bitSizeOf(ShiftInt); | |
| 715 | } | |
| 716 | fn boolMaskBit(index: usize, value: bool) MaskInt { | |
| 717 | return @as(MaskInt, @boolToInt(value)) << @intCast(ShiftInt, index); | |
| 718 | } | |
| 719 | fn numMasks(bit_length: usize) usize { | |
| 720 | return (bit_length + (@bitSizeOf(MaskInt) - 1)) / @bitSizeOf(MaskInt); | |
| 721 | } | |
| 722 | }; | |
| 723 | ||
| 724 | /// A bit set with runtime known size, backed by an allocated slice | |
| 725 | /// of usize. Thin wrapper around DynamicBitSetUnmanaged which keeps | |
| 726 | /// track of the allocator instance. | |
| 727 | pub const DynamicBitSet = struct { | |
| 728 | const Self = @This(); | |
| 729 | ||
| 730 | /// The integer type used to represent a mask in this bit set | |
| 731 | pub const MaskInt = usize; | |
| 732 | ||
| 733 | /// The integer type used to shift a mask in this bit set | |
| 734 | pub const ShiftInt = std.math.Log2Int(MaskInt); | |
| 735 | ||
| 736 | /// The allocator used by this bit set | |
| 737 | allocator: *Allocator, | |
| 738 | ||
| 739 | /// The number of valid items in this bit set | |
| 740 | unmanaged: DynamicBitSetUnmanaged = .{}, | |
| 741 | ||
| 742 | /// Creates a bit set with no elements present. | |
| 743 | pub fn initEmpty(bit_length: usize, allocator: *Allocator) !Self { | |
| 744 | return Self{ | |
| 745 | .unmanaged = try DynamicBitSetUnmanaged.initEmpty(bit_length, allocator), | |
| 746 | .allocator = allocator, | |
| 747 | }; | |
| 748 | } | |
| 749 | ||
| 750 | /// Creates a bit set with all elements present. | |
| 751 | pub fn initFull(bit_length: usize, allocator: *Allocator) !Self { | |
| 752 | return Self{ | |
| 753 | .unmanaged = try DynamicBitSetUnmanaged.initFull(bit_length, allocator), | |
| 754 | .allocator = allocator, | |
| 755 | }; | |
| 756 | } | |
| 757 | ||
| 758 | /// Resizes to a new length. If the new length is larger | |
| 759 | /// than the old length, fills any added bits with `fill`. | |
| 760 | pub fn resize(self: *@This(), new_len: usize, fill: bool) !void { | |
| 761 | try self.unmanaged.resize(new_len, fill, self.allocator); | |
| 762 | } | |
| 763 | ||
| 764 | /// deinitializes the array and releases its memory. | |
| 765 | /// The passed allocator must be the same one used for | |
| 766 | /// init* or resize in the past. | |
| 767 | pub fn deinit(self: *Self) void { | |
| 768 | self.unmanaged.deinit(self.allocator); | |
| 769 | } | |
| 770 | ||
| 771 | /// Creates a duplicate of this bit set, using the new allocator. | |
| 772 | pub fn clone(self: *const Self, new_allocator: *Allocator) !Self { | |
| 773 | return Self{ | |
| 774 | .unmanaged = try self.unmanaged.clone(new_allocator), | |
| 775 | .allocator = new_allocator, | |
| 776 | }; | |
| 777 | } | |
| 778 | ||
| 779 | /// Returns the number of bits in this bit set | |
| 780 | pub fn capacity(self: Self) callconv(.Inline) usize { | |
| 781 | return self.unmanaged.capacity(); | |
| 782 | } | |
| 783 | ||
| 784 | /// Returns true if the bit at the specified index | |
| 785 | /// is present in the set, false otherwise. | |
| 786 | pub fn isSet(self: Self, index: usize) bool { | |
| 787 | return self.unmanaged.isSet(index); | |
| 788 | } | |
| 789 | ||
| 790 | /// Returns the total number of set bits in this bit set. | |
| 791 | pub fn count(self: Self) usize { | |
| 792 | return self.unmanaged.count(); | |
| 793 | } | |
| 794 | ||
| 795 | /// Changes the value of the specified bit of the bit | |
| 796 | /// set to match the passed boolean. | |
| 797 | pub fn setValue(self: *Self, index: usize, value: bool) void { | |
| 798 | self.unmanaged.setValue(index, value); | |
| 799 | } | |
| 800 | ||
| 801 | /// Adds a specific bit to the bit set | |
| 802 | pub fn set(self: *Self, index: usize) void { | |
| 803 | self.unmanaged.set(index); | |
| 804 | } | |
| 805 | ||
| 806 | /// Removes a specific bit from the bit set | |
| 807 | pub fn unset(self: *Self, index: usize) void { | |
| 808 | self.unmanaged.unset(index); | |
| 809 | } | |
| 810 | ||
| 811 | /// Flips a specific bit in the bit set | |
| 812 | pub fn toggle(self: *Self, index: usize) void { | |
| 813 | self.unmanaged.toggle(index); | |
| 814 | } | |
| 815 | ||
| 816 | /// Flips all bits in this bit set which are present | |
| 817 | /// in the toggles bit set. Both sets must have the | |
| 818 | /// same bit_length. | |
| 819 | pub fn toggleSet(self: *Self, toggles: Self) void { | |
| 820 | self.unmanaged.toggleSet(toggles.unmanaged); | |
| 821 | } | |
| 822 | ||
| 823 | /// Flips every bit in the bit set. | |
| 824 | pub fn toggleAll(self: *Self) void { | |
| 825 | self.unmanaged.toggleAll(); | |
| 826 | } | |
| 827 | ||
| 828 | /// Performs a union of two bit sets, and stores the | |
| 829 | /// result in the first one. Bits in the result are | |
| 830 | /// set if the corresponding bits were set in either input. | |
| 831 | /// The two sets must both be the same bit_length. | |
| 832 | pub fn setUnion(self: *Self, other: Self) void { | |
| 833 | self.unmanaged.setUnion(other.unmanaged); | |
| 834 | } | |
| 835 | ||
| 836 | /// Performs an intersection of two bit sets, and stores | |
| 837 | /// the result in the first one. Bits in the result are | |
| 838 | /// set if the corresponding bits were set in both inputs. | |
| 839 | /// The two sets must both be the same bit_length. | |
| 840 | pub fn setIntersection(self: *Self, other: Self) void { | |
| 841 | self.unmanaged.setIntersection(other.unmanaged); | |
| 842 | } | |
| 843 | ||
| 844 | /// Finds the index of the first set bit. | |
| 845 | /// If no bits are set, returns null. | |
| 846 | pub fn findFirstSet(self: Self) ?usize { | |
| 847 | return self.unmanaged.findFirstSet(); | |
| 848 | } | |
| 849 | ||
| 850 | /// Finds the index of the first set bit, and unsets it. | |
| 851 | /// If no bits are set, returns null. | |
| 852 | pub fn toggleFirstSet(self: *Self) ?usize { | |
| 853 | return self.unmanaged.toggleFirstSet(); | |
| 854 | } | |
| 855 | ||
| 856 | /// Iterates through the items in the set, according to the options. | |
| 857 | /// The default options (.{}) will iterate indices of set bits in | |
| 858 | /// ascending order. Modifications to the underlying bit set may | |
| 859 | /// or may not be observed by the iterator. Resizing the underlying | |
| 860 | /// bit set invalidates the iterator. | |
| 861 | pub fn iterator(self: *Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) { | |
| 862 | return self.unmanaged.iterator(options); | |
| 863 | } | |
| 864 | }; | |
| 865 | ||
| 866 | /// Options for configuring an iterator over a bit set | |
| 867 | pub const IteratorOptions = struct { | |
| 868 | /// determines which bits should be visited | |
| 869 | kind: Type = .set, | |
| 870 | /// determines the order in which bit indices should be visited | |
| 871 | direction: Direction = .forward, | |
| 872 | ||
| 873 | pub const Type = enum { | |
| 874 | /// visit indexes of set bits | |
| 875 | set, | |
| 876 | /// visit indexes of unset bits | |
| 877 | unset, | |
| 878 | }; | |
| 879 | ||
| 880 | pub const Direction = enum { | |
| 881 | /// visit indices in ascending order | |
| 882 | forward, | |
| 883 | /// visit indices in descending order. | |
| 884 | /// Note that this may be slightly more expensive than forward iteration. | |
| 885 | reverse, | |
| 886 | }; | |
| 887 | }; | |
| 888 | ||
| 889 | // The iterator is reusable between several bit set types | |
| 890 | fn BitSetIterator(comptime MaskInt: type, comptime options: IteratorOptions) type { | |
| 891 | const ShiftInt = std.math.Log2Int(MaskInt); | |
| 892 | const kind = options.kind; | |
| 893 | const direction = options.direction; | |
| 894 | return struct { | |
| 895 | const Self = @This(); | |
| 896 | ||
| 897 | // all bits which have not yet been iterated over | |
| 898 | bits_remain: MaskInt, | |
| 899 | // all words which have not yet been iterated over | |
| 900 | words_remain: []const MaskInt, | |
| 901 | // the offset of the current word | |
| 902 | bit_offset: usize, | |
| 903 | // the mask of the last word | |
| 904 | last_word_mask: MaskInt, | |
| 905 | ||
| 906 | fn init(masks: []const MaskInt, last_word_mask: MaskInt) Self { | |
| 907 | if (masks.len == 0) { | |
| 908 | return Self{ | |
| 909 | .bits_remain = 0, | |
| 910 | .words_remain = &[_]MaskInt{}, | |
| 911 | .last_word_mask = last_word_mask, | |
| 912 | .bit_offset = 0, | |
| 913 | }; | |
| 914 | } else { | |
| 915 | var result = Self{ | |
| 916 | .bits_remain = 0, | |
| 917 | .words_remain = masks, | |
| 918 | .last_word_mask = last_word_mask, | |
| 919 | .bit_offset = if (direction == .forward) 0 else (masks.len - 1) * @bitSizeOf(MaskInt), | |
| 920 | }; | |
| 921 | result.nextWord(true); | |
| 922 | return result; | |
| 923 | } | |
| 924 | } | |
| 925 | ||
| 926 | /// Returns the index of the next unvisited set bit | |
| 927 | /// in the bit set, in ascending order. | |
| 928 | pub fn next(self: *Self) ?usize { | |
| 929 | while (self.bits_remain == 0) { | |
| 930 | if (self.words_remain.len == 0) return null; | |
| 931 | self.nextWord(false); | |
| 932 | switch (direction) { | |
| 933 | .forward => self.bit_offset += @bitSizeOf(MaskInt), | |
| 934 | .reverse => self.bit_offset -= @bitSizeOf(MaskInt), | |
| 935 | } | |
| 936 | } | |
| 937 | ||
| 938 | switch (direction) { | |
| 939 | .forward => { | |
| 940 | const next_index = @ctz(MaskInt, self.bits_remain) + self.bit_offset; | |
| 941 | self.bits_remain &= self.bits_remain - 1; | |
| 942 | return next_index; | |
| 943 | }, | |
| 944 | .reverse => { | |
| 945 | const leading_zeroes = @clz(MaskInt, self.bits_remain); | |
| 946 | const top_bit = (@bitSizeOf(MaskInt) - 1) - leading_zeroes; | |
| 947 | const no_top_bit_mask = (@as(MaskInt, 1) << @intCast(ShiftInt, top_bit)) - 1; | |
| 948 | self.bits_remain &= no_top_bit_mask; | |
| 949 | return top_bit + self.bit_offset; | |
| 950 | }, | |
| 951 | } | |
| 952 | } | |
| 953 | ||
| 954 | // Load the next word. Don't call this if there | |
| 955 | // isn't a next word. If the next word is the | |
| 956 | // last word, mask off the padding bits so we | |
| 957 | // don't visit them. | |
| 958 | fn nextWord(self: *Self, comptime is_first_word: bool) callconv(.Inline) void { | |
| 959 | var word = switch (direction) { | |
| 960 | .forward => self.words_remain[0], | |
| 961 | .reverse => self.words_remain[self.words_remain.len - 1], | |
| 962 | }; | |
| 963 | switch (kind) { | |
| 964 | .set => {}, | |
| 965 | .unset => { | |
| 966 | word = ~word; | |
| 967 | if ((direction == .reverse and is_first_word) or | |
| 968 | (direction == .forward and self.words_remain.len == 1)) | |
| 969 | { | |
| 970 | word &= self.last_word_mask; | |
| 971 | } | |
| 972 | }, | |
| 973 | } | |
| 974 | switch (direction) { | |
| 975 | .forward => self.words_remain = self.words_remain[1..], | |
| 976 | .reverse => self.words_remain.len -= 1, | |
| 977 | } | |
| 978 | self.bits_remain = word; | |
| 979 | } | |
| 980 | }; | |
| 981 | } | |
| 982 | ||
| 983 | // ---------------- Tests ----------------- | |
| 984 | ||
| 985 | const testing = std.testing; | |
| 986 | ||
| 987 | fn testBitSet(a: anytype, b: anytype, len: usize) void { | |
| 988 | testing.expectEqual(len, a.capacity()); | |
| 989 | testing.expectEqual(len, b.capacity()); | |
| 990 | ||
| 991 | { | |
| 992 | var i: usize = 0; | |
| 993 | while (i < len) : (i += 1) { | |
| 994 | a.setValue(i, i & 1 == 0); | |
| 995 | b.setValue(i, i & 2 == 0); | |
| 996 | } | |
| 997 | } | |
| 998 | ||
| 999 | testing.expectEqual((len + 1) / 2, a.count()); | |
| 1000 | testing.expectEqual((len + 3) / 4 + (len + 2) / 4, b.count()); | |
| 1001 | ||
| 1002 | { | |
| 1003 | var iter = a.iterator(.{}); | |
| 1004 | var i: usize = 0; | |
| 1005 | while (i < len) : (i += 2) { | |
| 1006 | testing.expectEqual(@as(?usize, i), iter.next()); | |
| 1007 | } | |
| 1008 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1009 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1010 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1011 | } | |
| 1012 | a.toggleAll(); | |
| 1013 | { | |
| 1014 | var iter = a.iterator(.{}); | |
| 1015 | var i: usize = 1; | |
| 1016 | while (i < len) : (i += 2) { | |
| 1017 | testing.expectEqual(@as(?usize, i), iter.next()); | |
| 1018 | } | |
| 1019 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1020 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1021 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1022 | } | |
| 1023 | ||
| 1024 | { | |
| 1025 | var iter = b.iterator(.{ .kind = .unset }); | |
| 1026 | var i: usize = 2; | |
| 1027 | while (i < len) : (i += 4) { | |
| 1028 | testing.expectEqual(@as(?usize, i), iter.next()); | |
| 1029 | if (i + 1 < len) { | |
| 1030 | testing.expectEqual(@as(?usize, i + 1), iter.next()); | |
| 1031 | } | |
| 1032 | } | |
| 1033 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1034 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1035 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1036 | } | |
| 1037 | ||
| 1038 | { | |
| 1039 | var i: usize = 0; | |
| 1040 | while (i < len) : (i += 1) { | |
| 1041 | testing.expectEqual(i & 1 != 0, a.isSet(i)); | |
| 1042 | testing.expectEqual(i & 2 == 0, b.isSet(i)); | |
| 1043 | } | |
| 1044 | } | |
| 1045 | ||
| 1046 | a.setUnion(b.*); | |
| 1047 | { | |
| 1048 | var i: usize = 0; | |
| 1049 | while (i < len) : (i += 1) { | |
| 1050 | testing.expectEqual(i & 1 != 0 or i & 2 == 0, a.isSet(i)); | |
| 1051 | testing.expectEqual(i & 2 == 0, b.isSet(i)); | |
| 1052 | } | |
| 1053 | ||
| 1054 | i = len; | |
| 1055 | var set = a.iterator(.{ .direction = .reverse }); | |
| 1056 | var unset = a.iterator(.{ .kind = .unset, .direction = .reverse }); | |
| 1057 | while (i > 0) { | |
| 1058 | i -= 1; | |
| 1059 | if (i & 1 != 0 or i & 2 == 0) { | |
| 1060 | testing.expectEqual(@as(?usize, i), set.next()); | |
| 1061 | } else { | |
| 1062 | testing.expectEqual(@as(?usize, i), unset.next()); | |
| 1063 | } | |
| 1064 | } | |
| 1065 | testing.expectEqual(@as(?usize, null), set.next()); | |
| 1066 | testing.expectEqual(@as(?usize, null), set.next()); | |
| 1067 | testing.expectEqual(@as(?usize, null), set.next()); | |
| 1068 | testing.expectEqual(@as(?usize, null), unset.next()); | |
| 1069 | testing.expectEqual(@as(?usize, null), unset.next()); | |
| 1070 | testing.expectEqual(@as(?usize, null), unset.next()); | |
| 1071 | } | |
| 1072 | ||
| 1073 | a.toggleSet(b.*); | |
| 1074 | { | |
| 1075 | testing.expectEqual(len / 4, a.count()); | |
| 1076 | ||
| 1077 | var i: usize = 0; | |
| 1078 | while (i < len) : (i += 1) { | |
| 1079 | testing.expectEqual(i & 1 != 0 and i & 2 != 0, a.isSet(i)); | |
| 1080 | testing.expectEqual(i & 2 == 0, b.isSet(i)); | |
| 1081 | if (i & 1 == 0) { | |
| 1082 | a.set(i); | |
| 1083 | } else { | |
| 1084 | a.unset(i); | |
| 1085 | } | |
| 1086 | } | |
| 1087 | } | |
| 1088 | ||
| 1089 | a.setIntersection(b.*); | |
| 1090 | { | |
| 1091 | testing.expectEqual((len + 3) / 4, a.count()); | |
| 1092 | ||
| 1093 | var i: usize = 0; | |
| 1094 | while (i < len) : (i += 1) { | |
| 1095 | testing.expectEqual(i & 1 == 0 and i & 2 == 0, a.isSet(i)); | |
| 1096 | testing.expectEqual(i & 2 == 0, b.isSet(i)); | |
| 1097 | } | |
| 1098 | } | |
| 1099 | ||
| 1100 | a.toggleSet(a.*); | |
| 1101 | { | |
| 1102 | var iter = a.iterator(.{}); | |
| 1103 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1104 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1105 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1106 | testing.expectEqual(@as(usize, 0), a.count()); | |
| 1107 | } | |
| 1108 | { | |
| 1109 | var iter = a.iterator(.{ .direction = .reverse }); | |
| 1110 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1111 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1112 | testing.expectEqual(@as(?usize, null), iter.next()); | |
| 1113 | testing.expectEqual(@as(usize, 0), a.count()); | |
| 1114 | } | |
| 1115 | ||
| 1116 | const test_bits = [_]usize{ | |
| 1117 | 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 22, 31, 32, 63, 64, | |
| 1118 | 66, 95, 127, 160, 192, 1000, | |
| 1119 | }; | |
| 1120 | for (test_bits) |i| { | |
| 1121 | if (i < a.capacity()) { | |
| 1122 | a.set(i); | |
| 1123 | } | |
| 1124 | } | |
| 1125 | ||
| 1126 | for (test_bits) |i| { | |
| 1127 | if (i < a.capacity()) { | |
| 1128 | testing.expectEqual(@as(?usize, i), a.findFirstSet()); | |
| 1129 | testing.expectEqual(@as(?usize, i), a.toggleFirstSet()); | |
| 1130 | } | |
| 1131 | } | |
| 1132 | testing.expectEqual(@as(?usize, null), a.findFirstSet()); | |
| 1133 | testing.expectEqual(@as(?usize, null), a.toggleFirstSet()); | |
| 1134 | testing.expectEqual(@as(?usize, null), a.findFirstSet()); | |
| 1135 | testing.expectEqual(@as(?usize, null), a.toggleFirstSet()); | |
| 1136 | testing.expectEqual(@as(usize, 0), a.count()); | |
| 1137 | } | |
| 1138 | ||
| 1139 | fn testStaticBitSet(comptime Set: type) void { | |
| 1140 | var a = Set.initEmpty(); | |
| 1141 | var b = Set.initFull(); | |
| 1142 | testing.expectEqual(@as(usize, 0), a.count()); | |
| 1143 | testing.expectEqual(@as(usize, Set.bit_length), b.count()); | |
| 1144 | ||
| 1145 | testBitSet(&a, &b, Set.bit_length); | |
| 1146 | } | |
| 1147 | ||
| 1148 | test "IntegerBitSet" { | |
| 1149 | testStaticBitSet(IntegerBitSet(0)); | |
| 1150 | testStaticBitSet(IntegerBitSet(1)); | |
| 1151 | testStaticBitSet(IntegerBitSet(2)); | |
| 1152 | testStaticBitSet(IntegerBitSet(5)); | |
| 1153 | testStaticBitSet(IntegerBitSet(8)); | |
| 1154 | testStaticBitSet(IntegerBitSet(32)); | |
| 1155 | testStaticBitSet(IntegerBitSet(64)); | |
| 1156 | testStaticBitSet(IntegerBitSet(127)); | |
| 1157 | } | |
| 1158 | ||
| 1159 | test "ArrayBitSet" { | |
| 1160 | inline for (.{ 0, 1, 2, 31, 32, 33, 63, 64, 65, 254, 500, 3000 }) |size| { | |
| 1161 | testStaticBitSet(ArrayBitSet(u8, size)); | |
| 1162 | testStaticBitSet(ArrayBitSet(u16, size)); | |
| 1163 | testStaticBitSet(ArrayBitSet(u32, size)); | |
| 1164 | testStaticBitSet(ArrayBitSet(u64, size)); | |
| 1165 | testStaticBitSet(ArrayBitSet(u128, size)); | |
| 1166 | } | |
| 1167 | } | |
| 1168 | ||
| 1169 | test "DynamicBitSetUnmanaged" { | |
| 1170 | const allocator = std.testing.allocator; | |
| 1171 | var a = try DynamicBitSetUnmanaged.initEmpty(300, allocator); | |
| 1172 | testing.expectEqual(@as(usize, 0), a.count()); | |
| 1173 | a.deinit(allocator); | |
| 1174 | ||
| 1175 | a = try DynamicBitSetUnmanaged.initEmpty(0, allocator); | |
| 1176 | defer a.deinit(allocator); | |
| 1177 | for ([_]usize{ 1, 2, 31, 32, 33, 0, 65, 64, 63, 500, 254, 3000 }) |size| { | |
| 1178 | const old_len = a.capacity(); | |
| 1179 | ||
| 1180 | var tmp = try a.clone(allocator); | |
| 1181 | defer tmp.deinit(allocator); | |
| 1182 | testing.expectEqual(old_len, tmp.capacity()); | |
| 1183 | var i: usize = 0; | |
| 1184 | while (i < old_len) : (i += 1) { | |
| 1185 | testing.expectEqual(a.isSet(i), tmp.isSet(i)); | |
| 1186 | } | |
| 1187 | ||
| 1188 | a.toggleSet(a); // zero a | |
| 1189 | tmp.toggleSet(tmp); | |
| 1190 | ||
| 1191 | try a.resize(size, true, allocator); | |
| 1192 | try tmp.resize(size, false, allocator); | |
| 1193 | ||
| 1194 | if (size > old_len) { | |
| 1195 | testing.expectEqual(size - old_len, a.count()); | |
| 1196 | } else { | |
| 1197 | testing.expectEqual(@as(usize, 0), a.count()); | |
| 1198 | } | |
| 1199 | testing.expectEqual(@as(usize, 0), tmp.count()); | |
| 1200 | ||
| 1201 | var b = try DynamicBitSetUnmanaged.initFull(size, allocator); | |
| 1202 | defer b.deinit(allocator); | |
| 1203 | testing.expectEqual(@as(usize, size), b.count()); | |
| 1204 | ||
| 1205 | testBitSet(&a, &b, size); | |
| 1206 | } | |
| 1207 | } | |
| 1208 | ||
| 1209 | test "DynamicBitSet" { | |
| 1210 | const allocator = std.testing.allocator; | |
| 1211 | var a = try DynamicBitSet.initEmpty(300, allocator); | |
| 1212 | testing.expectEqual(@as(usize, 0), a.count()); | |
| 1213 | a.deinit(); | |
| 1214 | ||
| 1215 | a = try DynamicBitSet.initEmpty(0, allocator); | |
| 1216 | defer a.deinit(); | |
| 1217 | for ([_]usize{ 1, 2, 31, 32, 33, 0, 65, 64, 63, 500, 254, 3000 }) |size| { | |
| 1218 | const old_len = a.capacity(); | |
| 1219 | ||
| 1220 | var tmp = try a.clone(allocator); | |
| 1221 | defer tmp.deinit(); | |
| 1222 | testing.expectEqual(old_len, tmp.capacity()); | |
| 1223 | var i: usize = 0; | |
| 1224 | while (i < old_len) : (i += 1) { | |
| 1225 | testing.expectEqual(a.isSet(i), tmp.isSet(i)); | |
| 1226 | } | |
| 1227 | ||
| 1228 | a.toggleSet(a); // zero a | |
| 1229 | tmp.toggleSet(tmp); // zero tmp | |
| 1230 | ||
| 1231 | try a.resize(size, true); | |
| 1232 | try tmp.resize(size, false); | |
| 1233 | ||
| 1234 | if (size > old_len) { | |
| 1235 | testing.expectEqual(size - old_len, a.count()); | |
| 1236 | } else { | |
| 1237 | testing.expectEqual(@as(usize, 0), a.count()); | |
| 1238 | } | |
| 1239 | testing.expectEqual(@as(usize, 0), tmp.count()); | |
| 1240 | ||
| 1241 | var b = try DynamicBitSet.initFull(size, allocator); | |
| 1242 | defer b.deinit(); | |
| 1243 | testing.expectEqual(@as(usize, size), b.count()); | |
| 1244 | ||
| 1245 | testBitSet(&a, &b, size); | |
| 1246 | } | |
| 1247 | } | |
| 1248 | ||
| 1249 | test "StaticBitSet" { | |
| 1250 | testing.expectEqual(IntegerBitSet(0), StaticBitSet(0)); | |
| 1251 | testing.expectEqual(IntegerBitSet(5), StaticBitSet(5)); | |
| 1252 | testing.expectEqual(IntegerBitSet(@bitSizeOf(usize)), StaticBitSet(@bitSizeOf(usize))); | |
| 1253 | testing.expectEqual(ArrayBitSet(usize, @bitSizeOf(usize) + 1), StaticBitSet(@bitSizeOf(usize) + 1)); | |
| 1254 | testing.expectEqual(ArrayBitSet(usize, 500), StaticBitSet(500)); | |
| 1255 | } |
lib/std/build/emit_raw.zig+3-3| ... | ... | @@ -51,9 +51,9 @@ const BinaryElfOutput = struct { |
| 51 | 51 | .segments = ArrayList(*BinaryElfSegment).init(allocator), |
| 52 | 52 | .sections = ArrayList(*BinaryElfSection).init(allocator), |
| 53 | 53 | }; |
| 54 | const elf_hdr = try std.elf.readHeader(elf_file); | |
| 54 | const elf_hdr = try std.elf.Header.read(&elf_file); | |
| 55 | 55 | |
| 56 | var section_headers = elf_hdr.section_header_iterator(elf_file); | |
| 56 | var section_headers = elf_hdr.section_header_iterator(&elf_file); | |
| 57 | 57 | while (try section_headers.next()) |section| { |
| 58 | 58 | if (sectionValidForOutput(section)) { |
| 59 | 59 | const newSection = try allocator.create(BinaryElfSection); |
| ... | ... | @@ -67,7 +67,7 @@ const BinaryElfOutput = struct { |
| 67 | 67 | } |
| 68 | 68 | } |
| 69 | 69 | |
| 70 | var program_headers = elf_hdr.program_header_iterator(elf_file); | |
| 70 | var program_headers = elf_hdr.program_header_iterator(&elf_file); | |
| 71 | 71 | while (try program_headers.next()) |phdr| { |
| 72 | 72 | if (phdr.p_type == elf.PT_LOAD) { |
| 73 | 73 | const newSegment = try allocator.create(BinaryElfSegment); |
lib/std/c/tokenizer.zig+30-2| ... | ... | @@ -401,7 +401,9 @@ pub const Tokenizer = struct { |
| 401 | 401 | Zero, |
| 402 | 402 | IntegerLiteralOct, |
| 403 | 403 | IntegerLiteralBinary, |
| 404 | IntegerLiteralBinaryFirst, | |
| 404 | 405 | IntegerLiteralHex, |
| 406 | IntegerLiteralHexFirst, | |
| 405 | 407 | IntegerLiteral, |
| 406 | 408 | IntegerSuffix, |
| 407 | 409 | IntegerSuffixU, |
| ... | ... | @@ -1046,10 +1048,10 @@ pub const Tokenizer = struct { |
| 1046 | 1048 | state = .IntegerLiteralOct; |
| 1047 | 1049 | }, |
| 1048 | 1050 | 'b', 'B' => { |
| 1049 | state = .IntegerLiteralBinary; | |
| 1051 | state = .IntegerLiteralBinaryFirst; | |
| 1050 | 1052 | }, |
| 1051 | 1053 | 'x', 'X' => { |
| 1052 | state = .IntegerLiteralHex; | |
| 1054 | state = .IntegerLiteralHexFirst; | |
| 1053 | 1055 | }, |
| 1054 | 1056 | '.' => { |
| 1055 | 1057 | state = .FloatFraction; |
| ... | ... | @@ -1066,6 +1068,13 @@ pub const Tokenizer = struct { |
| 1066 | 1068 | self.index -= 1; |
| 1067 | 1069 | }, |
| 1068 | 1070 | }, |
| 1071 | .IntegerLiteralBinaryFirst => switch (c) { | |
| 1072 | '0'...'7' => state = .IntegerLiteralBinary, | |
| 1073 | else => { | |
| 1074 | result.id = .Invalid; | |
| 1075 | break; | |
| 1076 | }, | |
| 1077 | }, | |
| 1069 | 1078 | .IntegerLiteralBinary => switch (c) { |
| 1070 | 1079 | '0', '1' => {}, |
| 1071 | 1080 | else => { |
| ... | ... | @@ -1073,6 +1082,19 @@ pub const Tokenizer = struct { |
| 1073 | 1082 | self.index -= 1; |
| 1074 | 1083 | }, |
| 1075 | 1084 | }, |
| 1085 | .IntegerLiteralHexFirst => switch (c) { | |
| 1086 | '0'...'9', 'a'...'f', 'A'...'F' => state = .IntegerLiteralHex, | |
| 1087 | '.' => { | |
| 1088 | state = .FloatFractionHex; | |
| 1089 | }, | |
| 1090 | 'p', 'P' => { | |
| 1091 | state = .FloatExponent; | |
| 1092 | }, | |
| 1093 | else => { | |
| 1094 | result.id = .Invalid; | |
| 1095 | break; | |
| 1096 | }, | |
| 1097 | }, | |
| 1076 | 1098 | .IntegerLiteralHex => switch (c) { |
| 1077 | 1099 | '0'...'9', 'a'...'f', 'A'...'F' => {}, |
| 1078 | 1100 | '.' => { |
| ... | ... | @@ -1238,6 +1260,8 @@ pub const Tokenizer = struct { |
| 1238 | 1260 | .MultiLineCommentAsterisk, |
| 1239 | 1261 | .FloatExponent, |
| 1240 | 1262 | .MacroString, |
| 1263 | .IntegerLiteralBinaryFirst, | |
| 1264 | .IntegerLiteralHexFirst, | |
| 1241 | 1265 | => result.id = .Invalid, |
| 1242 | 1266 | |
| 1243 | 1267 | .FloatExponentDigits => result.id = if (counter == 0) .Invalid else .{ .FloatLiteral = .none }, |
| ... | ... | @@ -1523,6 +1547,7 @@ test "num suffixes" { |
| 1523 | 1547 | \\ 1.0f 1.0L 1.0 .0 1. |
| 1524 | 1548 | \\ 0l 0lu 0ll 0llu 0 |
| 1525 | 1549 | \\ 1u 1ul 1ull 1 |
| 1550 | \\ 0x 0b | |
| 1526 | 1551 | \\ |
| 1527 | 1552 | , &[_]Token.Id{ |
| 1528 | 1553 | .{ .FloatLiteral = .f }, |
| ... | ... | @@ -1542,6 +1567,9 @@ test "num suffixes" { |
| 1542 | 1567 | .{ .IntegerLiteral = .llu }, |
| 1543 | 1568 | .{ .IntegerLiteral = .none }, |
| 1544 | 1569 | .Nl, |
| 1570 | .Invalid, | |
| 1571 | .Invalid, | |
| 1572 | .Nl, | |
| 1545 | 1573 | }); |
| 1546 | 1574 | } |
| 1547 | 1575 |
lib/std/crypto/25519/curve25519.zig+2-2| ... | ... | @@ -115,9 +115,9 @@ test "curve25519" { |
| 115 | 115 | const p = try Curve25519.basePoint.clampedMul(s); |
| 116 | 116 | try p.rejectIdentity(); |
| 117 | 117 | var buf: [128]u8 = undefined; |
| 118 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{p.toBytes()}), "E6F2A4D1C28EE5C7AD0329268255A468AD407D2672824C0C0EB30EA6EF450145"); | |
| 118 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&p.toBytes())}), "E6F2A4D1C28EE5C7AD0329268255A468AD407D2672824C0C0EB30EA6EF450145"); | |
| 119 | 119 | const q = try p.clampedMul(s); |
| 120 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{q.toBytes()}), "3614E119FFE55EC55B87D6B19971A9F4CBC78EFE80BEC55B96392BABCC712537"); | |
| 120 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&q.toBytes())}), "3614E119FFE55EC55B87D6B19971A9F4CBC78EFE80BEC55B96392BABCC712537"); | |
| 121 | 121 | |
| 122 | 122 | try Curve25519.rejectNonCanonical(s); |
| 123 | 123 | s[31] |= 0x80; |
lib/std/crypto/25519/ed25519.zig+3-3| ... | ... | @@ -210,8 +210,8 @@ test "ed25519 key pair creation" { |
| 210 | 210 | _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); |
| 211 | 211 | const key_pair = try Ed25519.KeyPair.create(seed); |
| 212 | 212 | var buf: [256]u8 = undefined; |
| 213 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair.secret_key}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); | |
| 214 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair.public_key}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); | |
| 213 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&key_pair.secret_key)}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); | |
| 214 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&key_pair.public_key)}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); | |
| 215 | 215 | } |
| 216 | 216 | |
| 217 | 217 | test "ed25519 signature" { |
| ... | ... | @@ -221,7 +221,7 @@ test "ed25519 signature" { |
| 221 | 221 | |
| 222 | 222 | const sig = try Ed25519.sign("test", key_pair, null); |
| 223 | 223 | var buf: [128]u8 = undefined; |
| 224 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{sig}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808"); | |
| 224 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&sig)}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808"); | |
| 225 | 225 | try Ed25519.verify(sig, "test", key_pair.public_key); |
| 226 | 226 | std.testing.expectError(error.InvalidSignature, Ed25519.verify(sig, "TEST", key_pair.public_key)); |
| 227 | 227 | } |
lib/std/crypto/25519/edwards25519.zig+1-1| ... | ... | @@ -450,7 +450,7 @@ test "edwards25519 packing/unpacking" { |
| 450 | 450 | var b = Edwards25519.basePoint; |
| 451 | 451 | const pk = try b.mul(s); |
| 452 | 452 | var buf: [128]u8 = undefined; |
| 453 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{pk.toBytes()}), "074BC7E0FCBD587FDBC0969444245FADC562809C8F6E97E949AF62484B5B81A6"); | |
| 453 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&pk.toBytes())}), "074BC7E0FCBD587FDBC0969444245FADC562809C8F6E97E949AF62484B5B81A6"); | |
| 454 | 454 | |
| 455 | 455 | const small_order_ss: [7][32]u8 = .{ |
| 456 | 456 | .{ |
lib/std/crypto/25519/ristretto255.zig+4-4| ... | ... | @@ -170,21 +170,21 @@ pub const Ristretto255 = struct { |
| 170 | 170 | test "ristretto255" { |
| 171 | 171 | const p = Ristretto255.basePoint; |
| 172 | 172 | var buf: [256]u8 = undefined; |
| 173 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{p.toBytes()}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76"); | |
| 173 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&p.toBytes())}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76"); | |
| 174 | 174 | |
| 175 | 175 | var r: [Ristretto255.encoded_length]u8 = undefined; |
| 176 | 176 | _ = try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"); |
| 177 | 177 | var q = try Ristretto255.fromBytes(r); |
| 178 | 178 | q = q.dbl().add(p); |
| 179 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{q.toBytes()}), "E882B131016B52C1D3337080187CF768423EFCCBB517BB495AB812C4160FF44E"); | |
| 179 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&q.toBytes())}), "E882B131016B52C1D3337080187CF768423EFCCBB517BB495AB812C4160FF44E"); | |
| 180 | 180 | |
| 181 | 181 | const s = [_]u8{15} ++ [_]u8{0} ** 31; |
| 182 | 182 | const w = try p.mul(s); |
| 183 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{w.toBytes()}), "E0C418F7C8D9C4CDD7395B93EA124F3AD99021BB681DFC3302A9D99A2E53E64E"); | |
| 183 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&w.toBytes())}), "E0C418F7C8D9C4CDD7395B93EA124F3AD99021BB681DFC3302A9D99A2E53E64E"); | |
| 184 | 184 | |
| 185 | 185 | std.testing.expect(p.dbl().dbl().dbl().dbl().equivalent(w.add(p))); |
| 186 | 186 | |
| 187 | 187 | const h = [_]u8{69} ** 32 ++ [_]u8{42} ** 32; |
| 188 | 188 | const ph = Ristretto255.fromUniform(h); |
| 189 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{ph.toBytes()}), "DCCA54E037A4311EFBEEF413ACD21D35276518970B7A61DC88F8587B493D5E19"); | |
| 189 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&ph.toBytes())}), "DCCA54E037A4311EFBEEF413ACD21D35276518970B7A61DC88F8587B493D5E19"); | |
| 190 | 190 | } |
lib/std/crypto/25519/scalar.zig+3-3| ... | ... | @@ -771,10 +771,10 @@ test "scalar25519" { |
| 771 | 771 | var y = x.toBytes(); |
| 772 | 772 | try rejectNonCanonical(y); |
| 773 | 773 | var buf: [128]u8 = undefined; |
| 774 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{y}), "1E979B917937F3DE71D18077F961F6CEFF01030405060708010203040506070F"); | |
| 774 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&y)}), "1E979B917937F3DE71D18077F961F6CEFF01030405060708010203040506070F"); | |
| 775 | 775 | |
| 776 | 776 | const reduced = reduce(field_size); |
| 777 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{reduced}), "0000000000000000000000000000000000000000000000000000000000000000"); | |
| 777 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&reduced)}), "0000000000000000000000000000000000000000000000000000000000000000"); | |
| 778 | 778 | } |
| 779 | 779 | |
| 780 | 780 | test "non-canonical scalar25519" { |
| ... | ... | @@ -788,5 +788,5 @@ test "mulAdd overflow check" { |
| 788 | 788 | const c: [32]u8 = [_]u8{0xff} ** 32; |
| 789 | 789 | const x = mulAdd(a, b, c); |
| 790 | 790 | var buf: [128]u8 = undefined; |
| 791 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{x}), "D14DF91389432C25AD60FF9791B9FD1D67BEF517D273ECCE3D9A307C1B419903"); | |
| 791 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&x)}), "D14DF91389432C25AD60FF9791B9FD1D67BEF517D273ECCE3D9A307C1B419903"); | |
| 792 | 792 | } |
lib/std/crypto/chacha20.zig+2-2| ... | ... | @@ -876,7 +876,7 @@ test "crypto.xchacha20" { |
| 876 | 876 | var ciphertext: [input.len]u8 = undefined; |
| 877 | 877 | XChaCha20IETF.xor(ciphertext[0..], input[0..], 0, key, nonce); |
| 878 | 878 | var buf: [2 * ciphertext.len]u8 = undefined; |
| 879 | testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{ciphertext}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D"); | |
| 879 | testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&ciphertext)}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D"); | |
| 880 | 880 | } |
| 881 | 881 | { |
| 882 | 882 | const data = "Additional data"; |
| ... | ... | @@ -885,7 +885,7 @@ test "crypto.xchacha20" { |
| 885 | 885 | var out: [input.len]u8 = undefined; |
| 886 | 886 | try xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce); |
| 887 | 887 | var buf: [2 * ciphertext.len]u8 = undefined; |
| 888 | testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{ciphertext}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234"); | |
| 888 | testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&ciphertext)}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234"); | |
| 889 | 889 | testing.expectEqualSlices(u8, out[0..], input); |
| 890 | 890 | ciphertext[0] += 1; |
| 891 | 891 | testing.expectError(error.AuthenticationFailed, xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce)); |
lib/std/debug.zig+17-3| ... | ... | @@ -360,14 +360,24 @@ pub const StackIterator = struct { |
| 360 | 360 | }; |
| 361 | 361 | } |
| 362 | 362 | |
| 363 | // Negative offset of the saved BP wrt the frame pointer. | |
| 363 | // Offset of the saved BP wrt the frame pointer. | |
| 364 | 364 | const fp_offset = if (builtin.arch.isRISCV()) |
| 365 | 365 | // On RISC-V the frame pointer points to the top of the saved register |
| 366 | 366 | // area, on pretty much every other architecture it points to the stack |
| 367 | 367 | // slot where the previous frame pointer is saved. |
| 368 | 368 | 2 * @sizeOf(usize) |
| 369 | else if (builtin.arch.isSPARC()) | |
| 370 | // On SPARC the previous frame pointer is stored at 14 slots past %fp+BIAS. | |
| 371 | 14 * @sizeOf(usize) | |
| 369 | 372 | else |
| 370 | 373 | 0; |
| 374 | ||
| 375 | const fp_bias = if (builtin.arch.isSPARC()) | |
| 376 | // On SPARC frame pointers are biased by a constant. | |
| 377 | 2047 | |
| 378 | else | |
| 379 | 0; | |
| 380 | ||
| 371 | 381 | // Positive offset of the saved PC wrt the frame pointer. |
| 372 | 382 | const pc_offset = if (builtin.arch == .powerpc64le) |
| 373 | 383 | 2 * @sizeOf(usize) |
| ... | ... | @@ -388,13 +398,17 @@ pub const StackIterator = struct { |
| 388 | 398 | } |
| 389 | 399 | |
| 390 | 400 | fn next_internal(self: *StackIterator) ?usize { |
| 391 | const fp = math.sub(usize, self.fp, fp_offset) catch return null; | |
| 401 | const fp = if (builtin.arch.isSPARC()) | |
| 402 | // On SPARC the offset is positive. (!) | |
| 403 | math.add(usize, self.fp, fp_offset) catch return null | |
| 404 | else | |
| 405 | math.sub(usize, self.fp, fp_offset) catch return null; | |
| 392 | 406 | |
| 393 | 407 | // Sanity check. |
| 394 | 408 | if (fp == 0 or !mem.isAligned(fp, @alignOf(usize))) |
| 395 | 409 | return null; |
| 396 | 410 | |
| 397 | const new_fp = @intToPtr(*const usize, fp).*; | |
| 411 | const new_fp = math.add(usize, @intToPtr(*const usize, fp).*, fp_bias) catch return null; | |
| 398 | 412 | |
| 399 | 413 | // Sanity check: the stack grows down thus all the parent frames must be |
| 400 | 414 | // be at addresses that are greater (or equal) than the previous one. |
lib/std/elf.zig+173-182| ... | ... | @@ -335,7 +335,7 @@ pub const ET = extern enum(u16) { |
| 335 | 335 | }; |
| 336 | 336 | |
| 337 | 337 | /// All integers are native endian. |
| 338 | const Header = struct { | |
| 338 | pub const Header = struct { | |
| 339 | 339 | endian: builtin.Endian, |
| 340 | 340 | is_64: bool, |
| 341 | 341 | entry: u64, |
| ... | ... | @@ -347,187 +347,200 @@ const Header = struct { |
| 347 | 347 | shnum: u16, |
| 348 | 348 | shstrndx: u16, |
| 349 | 349 | |
| 350 | pub fn program_header_iterator(self: Header, file: File) ProgramHeaderIterator { | |
| 351 | return .{ | |
| 350 | pub fn program_header_iterator(self: Header, parse_source: anytype) ProgramHeaderIterator(@TypeOf(parse_source)) { | |
| 351 | return ProgramHeaderIterator(@TypeOf(parse_source)){ | |
| 352 | 352 | .elf_header = self, |
| 353 | .file = file, | |
| 353 | .parse_source = parse_source, | |
| 354 | 354 | }; |
| 355 | 355 | } |
| 356 | 356 | |
| 357 | pub fn section_header_iterator(self: Header, file: File) SectionHeaderIterator { | |
| 358 | return .{ | |
| 357 | pub fn section_header_iterator(self: Header, parse_source: anytype) SectionHeaderIterator(@TypeOf(parse_source)) { | |
| 358 | return SectionHeaderIterator(@TypeOf(parse_source)){ | |
| 359 | 359 | .elf_header = self, |
| 360 | .file = file, | |
| 360 | .parse_source = parse_source, | |
| 361 | 361 | }; |
| 362 | 362 | } |
| 363 | }; | |
| 364 | 363 | |
| 365 | pub fn readHeader(file: File) !Header { | |
| 366 | var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined; | |
| 367 | try preadNoEof(file, &hdr_buf, 0); | |
| 368 | const hdr32 = @ptrCast(*Elf32_Ehdr, &hdr_buf); | |
| 369 | const hdr64 = @ptrCast(*Elf64_Ehdr, &hdr_buf); | |
| 370 | if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; | |
| 371 | if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion; | |
| 372 | ||
| 373 | const endian: std.builtin.Endian = switch (hdr32.e_ident[EI_DATA]) { | |
| 374 | ELFDATA2LSB => .Little, | |
| 375 | ELFDATA2MSB => .Big, | |
| 376 | else => return error.InvalidElfEndian, | |
| 377 | }; | |
| 378 | const need_bswap = endian != std.builtin.endian; | |
| 364 | pub fn read(parse_source: anytype) !Header { | |
| 365 | var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined; | |
| 366 | try parse_source.seekableStream().seekTo(0); | |
| 367 | try parse_source.reader().readNoEof(&hdr_buf); | |
| 368 | return Header.parse(&hdr_buf); | |
| 369 | } | |
| 379 | 370 | |
| 380 | const is_64 = switch (hdr32.e_ident[EI_CLASS]) { | |
| 381 | ELFCLASS32 => false, | |
| 382 | ELFCLASS64 => true, | |
| 383 | else => return error.InvalidElfClass, | |
| 384 | }; | |
| 371 | pub fn parse(hdr_buf: *align(@alignOf(Elf64_Ehdr)) const [@sizeOf(Elf64_Ehdr)]u8) !Header { | |
| 372 | const hdr32 = @ptrCast(*const Elf32_Ehdr, hdr_buf); | |
| 373 | const hdr64 = @ptrCast(*const Elf64_Ehdr, hdr_buf); | |
| 374 | if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; | |
| 375 | if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion; | |
| 385 | 376 | |
| 386 | return @as(Header, .{ | |
| 387 | .endian = endian, | |
| 388 | .is_64 = is_64, | |
| 389 | .entry = int(is_64, need_bswap, hdr32.e_entry, hdr64.e_entry), | |
| 390 | .phoff = int(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff), | |
| 391 | .shoff = int(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff), | |
| 392 | .phentsize = int(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize), | |
| 393 | .phnum = int(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum), | |
| 394 | .shentsize = int(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize), | |
| 395 | .shnum = int(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum), | |
| 396 | .shstrndx = int(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx), | |
| 397 | }); | |
| 398 | } | |
| 377 | const endian: std.builtin.Endian = switch (hdr32.e_ident[EI_DATA]) { | |
| 378 | ELFDATA2LSB => .Little, | |
| 379 | ELFDATA2MSB => .Big, | |
| 380 | else => return error.InvalidElfEndian, | |
| 381 | }; | |
| 382 | const need_bswap = endian != std.builtin.endian; | |
| 399 | 383 | |
| 400 | pub const ProgramHeaderIterator = struct { | |
| 401 | elf_header: Header, | |
| 402 | file: File, | |
| 403 | index: usize = 0, | |
| 384 | const is_64 = switch (hdr32.e_ident[EI_CLASS]) { | |
| 385 | ELFCLASS32 => false, | |
| 386 | ELFCLASS64 => true, | |
| 387 | else => return error.InvalidElfClass, | |
| 388 | }; | |
| 404 | 389 | |
| 405 | pub fn next(self: *ProgramHeaderIterator) !?Elf64_Phdr { | |
| 406 | if (self.index >= self.elf_header.phnum) return null; | |
| 407 | defer self.index += 1; | |
| 390 | return @as(Header, .{ | |
| 391 | .endian = endian, | |
| 392 | .is_64 = is_64, | |
| 393 | .entry = int(is_64, need_bswap, hdr32.e_entry, hdr64.e_entry), | |
| 394 | .phoff = int(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff), | |
| 395 | .shoff = int(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff), | |
| 396 | .phentsize = int(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize), | |
| 397 | .phnum = int(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum), | |
| 398 | .shentsize = int(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize), | |
| 399 | .shnum = int(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum), | |
| 400 | .shstrndx = int(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx), | |
| 401 | }); | |
| 402 | } | |
| 403 | }; | |
| 408 | 404 | |
| 409 | if (self.elf_header.is_64) { | |
| 410 | var phdr: Elf64_Phdr = undefined; | |
| 405 | pub fn ProgramHeaderIterator(ParseSource: anytype) type { | |
| 406 | return struct { | |
| 407 | elf_header: Header, | |
| 408 | parse_source: ParseSource, | |
| 409 | index: usize = 0, | |
| 410 | ||
| 411 | pub fn next(self: *@This()) !?Elf64_Phdr { | |
| 412 | if (self.index >= self.elf_header.phnum) return null; | |
| 413 | defer self.index += 1; | |
| 414 | ||
| 415 | if (self.elf_header.is_64) { | |
| 416 | var phdr: Elf64_Phdr = undefined; | |
| 417 | const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index; | |
| 418 | try self.parse_source.seekableStream().seekTo(offset); | |
| 419 | try self.parse_source.reader().readNoEof(mem.asBytes(&phdr)); | |
| 420 | ||
| 421 | // ELF endianness matches native endianness. | |
| 422 | if (self.elf_header.endian == std.builtin.endian) return phdr; | |
| 423 | ||
| 424 | // Convert fields to native endianness. | |
| 425 | return Elf64_Phdr{ | |
| 426 | .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type), | |
| 427 | .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset), | |
| 428 | .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr), | |
| 429 | .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr), | |
| 430 | .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz), | |
| 431 | .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz), | |
| 432 | .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags), | |
| 433 | .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align), | |
| 434 | }; | |
| 435 | } | |
| 436 | ||
| 437 | var phdr: Elf32_Phdr = undefined; | |
| 411 | 438 | const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index; |
| 412 | try preadNoEof(self.file, mem.asBytes(&phdr), offset); | |
| 413 | ||
| 414 | // ELF endianness matches native endianness. | |
| 415 | if (self.elf_header.endian == std.builtin.endian) return phdr; | |
| 416 | ||
| 417 | // Convert fields to native endianness. | |
| 439 | try self.parse_source.seekableStream().seekTo(offset); | |
| 440 | try self.parse_source.reader().readNoEof(mem.asBytes(&phdr)); | |
| 441 | ||
| 442 | // ELF endianness does NOT match native endianness. | |
| 443 | if (self.elf_header.endian != std.builtin.endian) { | |
| 444 | // Convert fields to native endianness. | |
| 445 | phdr = .{ | |
| 446 | .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type), | |
| 447 | .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset), | |
| 448 | .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr), | |
| 449 | .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr), | |
| 450 | .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz), | |
| 451 | .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz), | |
| 452 | .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags), | |
| 453 | .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align), | |
| 454 | }; | |
| 455 | } | |
| 456 | ||
| 457 | // Convert 32-bit header to 64-bit. | |
| 418 | 458 | return Elf64_Phdr{ |
| 419 | .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type), | |
| 420 | .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset), | |
| 421 | .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr), | |
| 422 | .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr), | |
| 423 | .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz), | |
| 424 | .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz), | |
| 425 | .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags), | |
| 426 | .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align), | |
| 459 | .p_type = phdr.p_type, | |
| 460 | .p_offset = phdr.p_offset, | |
| 461 | .p_vaddr = phdr.p_vaddr, | |
| 462 | .p_paddr = phdr.p_paddr, | |
| 463 | .p_filesz = phdr.p_filesz, | |
| 464 | .p_memsz = phdr.p_memsz, | |
| 465 | .p_flags = phdr.p_flags, | |
| 466 | .p_align = phdr.p_align, | |
| 427 | 467 | }; |
| 428 | 468 | } |
| 469 | }; | |
| 470 | } | |
| 429 | 471 | |
| 430 | var phdr: Elf32_Phdr = undefined; | |
| 431 | const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index; | |
| 432 | try preadNoEof(self.file, mem.asBytes(&phdr), offset); | |
| 433 | ||
| 434 | // ELF endianness does NOT match native endianness. | |
| 435 | if (self.elf_header.endian != std.builtin.endian) { | |
| 436 | // Convert fields to native endianness. | |
| 437 | phdr = .{ | |
| 438 | .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type), | |
| 439 | .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset), | |
| 440 | .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr), | |
| 441 | .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr), | |
| 442 | .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz), | |
| 443 | .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz), | |
| 444 | .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags), | |
| 445 | .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align), | |
| 446 | }; | |
| 447 | } | |
| 448 | ||
| 449 | // Convert 32-bit header to 64-bit. | |
| 450 | return Elf64_Phdr{ | |
| 451 | .p_type = phdr.p_type, | |
| 452 | .p_offset = phdr.p_offset, | |
| 453 | .p_vaddr = phdr.p_vaddr, | |
| 454 | .p_paddr = phdr.p_paddr, | |
| 455 | .p_filesz = phdr.p_filesz, | |
| 456 | .p_memsz = phdr.p_memsz, | |
| 457 | .p_flags = phdr.p_flags, | |
| 458 | .p_align = phdr.p_align, | |
| 459 | }; | |
| 460 | } | |
| 461 | }; | |
| 462 | ||
| 463 | pub const SectionHeaderIterator = struct { | |
| 464 | elf_header: Header, | |
| 465 | file: File, | |
| 466 | index: usize = 0, | |
| 467 | ||
| 468 | pub fn next(self: *SectionHeaderIterator) !?Elf64_Shdr { | |
| 469 | if (self.index >= self.elf_header.shnum) return null; | |
| 470 | defer self.index += 1; | |
| 471 | ||
| 472 | if (self.elf_header.is_64) { | |
| 473 | var shdr: Elf64_Shdr = undefined; | |
| 472 | pub fn SectionHeaderIterator(ParseSource: anytype) type { | |
| 473 | return struct { | |
| 474 | elf_header: Header, | |
| 475 | parse_source: ParseSource, | |
| 476 | index: usize = 0, | |
| 477 | ||
| 478 | pub fn next(self: *@This()) !?Elf64_Shdr { | |
| 479 | if (self.index >= self.elf_header.shnum) return null; | |
| 480 | defer self.index += 1; | |
| 481 | ||
| 482 | if (self.elf_header.is_64) { | |
| 483 | var shdr: Elf64_Shdr = undefined; | |
| 484 | const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index; | |
| 485 | try self.parse_source.seekableStream().seekTo(offset); | |
| 486 | try self.parse_source.reader().readNoEof(mem.asBytes(&shdr)); | |
| 487 | ||
| 488 | // ELF endianness matches native endianness. | |
| 489 | if (self.elf_header.endian == std.builtin.endian) return shdr; | |
| 490 | ||
| 491 | // Convert fields to native endianness. | |
| 492 | return Elf64_Shdr{ | |
| 493 | .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name), | |
| 494 | .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type), | |
| 495 | .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags), | |
| 496 | .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr), | |
| 497 | .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset), | |
| 498 | .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size), | |
| 499 | .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link), | |
| 500 | .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info), | |
| 501 | .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign), | |
| 502 | .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize), | |
| 503 | }; | |
| 504 | } | |
| 505 | ||
| 506 | var shdr: Elf32_Shdr = undefined; | |
| 474 | 507 | const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index; |
| 475 | try preadNoEof(self.file, mem.asBytes(&shdr), offset); | |
| 476 | ||
| 477 | // ELF endianness matches native endianness. | |
| 478 | if (self.elf_header.endian == std.builtin.endian) return shdr; | |
| 479 | ||
| 480 | // Convert fields to native endianness. | |
| 508 | try self.parse_source.seekableStream().seekTo(offset); | |
| 509 | try self.parse_source.reader().readNoEof(mem.asBytes(&shdr)); | |
| 510 | ||
| 511 | // ELF endianness does NOT match native endianness. | |
| 512 | if (self.elf_header.endian != std.builtin.endian) { | |
| 513 | // Convert fields to native endianness. | |
| 514 | shdr = .{ | |
| 515 | .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name), | |
| 516 | .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type), | |
| 517 | .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags), | |
| 518 | .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr), | |
| 519 | .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset), | |
| 520 | .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size), | |
| 521 | .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link), | |
| 522 | .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info), | |
| 523 | .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign), | |
| 524 | .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize), | |
| 525 | }; | |
| 526 | } | |
| 527 | ||
| 528 | // Convert 32-bit header to 64-bit. | |
| 481 | 529 | return Elf64_Shdr{ |
| 482 | .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name), | |
| 483 | .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type), | |
| 484 | .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags), | |
| 485 | .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr), | |
| 486 | .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset), | |
| 487 | .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size), | |
| 488 | .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link), | |
| 489 | .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info), | |
| 490 | .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign), | |
| 491 | .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize), | |
| 530 | .sh_name = shdr.sh_name, | |
| 531 | .sh_type = shdr.sh_type, | |
| 532 | .sh_flags = shdr.sh_flags, | |
| 533 | .sh_addr = shdr.sh_addr, | |
| 534 | .sh_offset = shdr.sh_offset, | |
| 535 | .sh_size = shdr.sh_size, | |
| 536 | .sh_link = shdr.sh_link, | |
| 537 | .sh_info = shdr.sh_info, | |
| 538 | .sh_addralign = shdr.sh_addralign, | |
| 539 | .sh_entsize = shdr.sh_entsize, | |
| 492 | 540 | }; |
| 493 | 541 | } |
| 494 | ||
| 495 | var shdr: Elf32_Shdr = undefined; | |
| 496 | const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index; | |
| 497 | try preadNoEof(self.file, mem.asBytes(&shdr), offset); | |
| 498 | ||
| 499 | // ELF endianness does NOT match native endianness. | |
| 500 | if (self.elf_header.endian != std.builtin.endian) { | |
| 501 | // Convert fields to native endianness. | |
| 502 | shdr = .{ | |
| 503 | .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name), | |
| 504 | .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type), | |
| 505 | .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags), | |
| 506 | .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr), | |
| 507 | .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset), | |
| 508 | .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size), | |
| 509 | .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link), | |
| 510 | .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info), | |
| 511 | .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign), | |
| 512 | .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize), | |
| 513 | }; | |
| 514 | } | |
| 515 | ||
| 516 | // Convert 32-bit header to 64-bit. | |
| 517 | return Elf64_Shdr{ | |
| 518 | .sh_name = shdr.sh_name, | |
| 519 | .sh_type = shdr.sh_type, | |
| 520 | .sh_flags = shdr.sh_flags, | |
| 521 | .sh_addr = shdr.sh_addr, | |
| 522 | .sh_offset = shdr.sh_offset, | |
| 523 | .sh_size = shdr.sh_size, | |
| 524 | .sh_link = shdr.sh_link, | |
| 525 | .sh_info = shdr.sh_info, | |
| 526 | .sh_addralign = shdr.sh_addralign, | |
| 527 | .sh_entsize = shdr.sh_entsize, | |
| 528 | }; | |
| 529 | } | |
| 530 | }; | |
| 542 | }; | |
| 543 | } | |
| 531 | 544 | |
| 532 | 545 | pub fn int(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) { |
| 533 | 546 | if (is_64) { |
| ... | ... | @@ -549,28 +562,6 @@ pub fn int32(need_bswap: bool, int_32: anytype, comptime Int64: anytype) Int64 { |
| 549 | 562 | } |
| 550 | 563 | } |
| 551 | 564 | |
| 552 | fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void { | |
| 553 | var i: usize = 0; | |
| 554 | while (i < buf.len) { | |
| 555 | const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) { | |
| 556 | error.SystemResources => return error.SystemResources, | |
| 557 | error.IsDir => return error.UnableToReadElfFile, | |
| 558 | error.OperationAborted => return error.UnableToReadElfFile, | |
| 559 | error.BrokenPipe => return error.UnableToReadElfFile, | |
| 560 | error.Unseekable => return error.UnableToReadElfFile, | |
| 561 | error.ConnectionResetByPeer => return error.UnableToReadElfFile, | |
| 562 | error.ConnectionTimedOut => return error.UnableToReadElfFile, | |
| 563 | error.InputOutput => return error.FileSystem, | |
| 564 | error.Unexpected => return error.Unexpected, | |
| 565 | error.WouldBlock => return error.Unexpected, | |
| 566 | error.NotOpenForReading => return error.Unexpected, | |
| 567 | error.AccessDenied => return error.Unexpected, | |
| 568 | }; | |
| 569 | if (len == 0) return error.UnexpectedEndOfFile; | |
| 570 | i += len; | |
| 571 | } | |
| 572 | } | |
| 573 | ||
| 574 | 565 | pub const EI_NIDENT = 16; |
| 575 | 566 | |
| 576 | 567 | pub const EI_CLASS = 4; |
lib/std/fmt.zig+104-26| ... | ... | @@ -709,6 +709,87 @@ fn formatFloatValue( |
| 709 | 709 | return formatBuf(buf_stream.getWritten(), options, writer); |
| 710 | 710 | } |
| 711 | 711 | |
| 712 | fn formatSliceHexImpl(comptime uppercase: bool) type { | |
| 713 | const charset = "0123456789" ++ if (uppercase) "ABCDEF" else "abcdef"; | |
| 714 | ||
| 715 | return struct { | |
| 716 | pub fn f( | |
| 717 | bytes: []const u8, | |
| 718 | comptime fmt: []const u8, | |
| 719 | options: std.fmt.FormatOptions, | |
| 720 | writer: anytype, | |
| 721 | ) !void { | |
| 722 | var buf: [2]u8 = undefined; | |
| 723 | ||
| 724 | for (bytes) |c| { | |
| 725 | buf[0] = charset[c >> 4]; | |
| 726 | buf[1] = charset[c & 15]; | |
| 727 | try writer.writeAll(&buf); | |
| 728 | } | |
| 729 | } | |
| 730 | }; | |
| 731 | } | |
| 732 | ||
| 733 | const formatSliceHexLower = formatSliceHexImpl(false).f; | |
| 734 | const formatSliceHexUpper = formatSliceHexImpl(true).f; | |
| 735 | ||
| 736 | /// Return a Formatter for a []const u8 where every byte is formatted as a pair | |
| 737 | /// of lowercase hexadecimal digits. | |
| 738 | pub fn fmtSliceHexLower(bytes: []const u8) std.fmt.Formatter(formatSliceHexLower) { | |
| 739 | return .{ .data = bytes }; | |
| 740 | } | |
| 741 | ||
| 742 | /// Return a Formatter for a []const u8 where every byte is formatted as a pair | |
| 743 | /// of uppercase hexadecimal digits. | |
| 744 | pub fn fmtSliceHexUpper(bytes: []const u8) std.fmt.Formatter(formatSliceHexUpper) { | |
| 745 | return .{ .data = bytes }; | |
| 746 | } | |
| 747 | ||
| 748 | fn formatSliceEscapeImpl(comptime uppercase: bool) type { | |
| 749 | const charset = "0123456789" ++ if (uppercase) "ABCDEF" else "abcdef"; | |
| 750 | ||
| 751 | return struct { | |
| 752 | pub fn f( | |
| 753 | bytes: []const u8, | |
| 754 | comptime fmt: []const u8, | |
| 755 | options: std.fmt.FormatOptions, | |
| 756 | writer: anytype, | |
| 757 | ) !void { | |
| 758 | var buf: [4]u8 = undefined; | |
| 759 | ||
| 760 | buf[0] = '\\'; | |
| 761 | buf[1] = 'x'; | |
| 762 | ||
| 763 | for (bytes) |c| { | |
| 764 | if (std.ascii.isPrint(c)) { | |
| 765 | try writer.writeByte(c); | |
| 766 | } else { | |
| 767 | buf[2] = charset[c >> 4]; | |
| 768 | buf[3] = charset[c & 15]; | |
| 769 | try writer.writeAll(&buf); | |
| 770 | } | |
| 771 | } | |
| 772 | } | |
| 773 | }; | |
| 774 | } | |
| 775 | ||
| 776 | const formatSliceEscapeLower = formatSliceEscapeImpl(false).f; | |
| 777 | const formatSliceEscapeUpper = formatSliceEscapeImpl(true).f; | |
| 778 | ||
| 779 | /// Return a Formatter for a []const u8 where every non-printable ASCII | |
| 780 | /// character is escaped as \xNN, where NN is the character in lowercase | |
| 781 | /// hexadecimal notation. | |
| 782 | pub fn fmtSliceEscapeLower(bytes: []const u8) std.fmt.Formatter(formatSliceEscapeLower) { | |
| 783 | return .{ .data = bytes }; | |
| 784 | } | |
| 785 | ||
| 786 | /// Return a Formatter for a []const u8 where every non-printable ASCII | |
| 787 | /// character is escaped as \xNN, where NN is the character in uppercase | |
| 788 | /// hexadecimal notation. | |
| 789 | pub fn fmtSliceEscapeUpper(bytes: []const u8) std.fmt.Formatter(formatSliceEscapeUpper) { | |
| 790 | return .{ .data = bytes }; | |
| 791 | } | |
| 792 | ||
| 712 | 793 | pub fn formatText( |
| 713 | 794 | bytes: []const u8, |
| 714 | 795 | comptime fmt: []const u8, |
| ... | ... | @@ -717,21 +798,18 @@ pub fn formatText( |
| 717 | 798 | ) !void { |
| 718 | 799 | if (comptime std.mem.eql(u8, fmt, "s")) { |
| 719 | 800 | return formatBuf(bytes, options, writer); |
| 720 | } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) { | |
| 721 | for (bytes) |c| { | |
| 722 | try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, writer); | |
| 723 | } | |
| 724 | return; | |
| 725 | } else if (comptime (std.mem.eql(u8, fmt, "e") or std.mem.eql(u8, fmt, "E"))) { | |
| 726 | for (bytes) |c| { | |
| 727 | if (std.ascii.isPrint(c)) { | |
| 728 | try writer.writeByte(c); | |
| 729 | } else { | |
| 730 | try writer.writeAll("\\x"); | |
| 731 | try formatInt(c, 16, fmt[0] == 'E', FormatOptions{ .width = 2, .fill = '0' }, writer); | |
| 732 | } | |
| 733 | } | |
| 734 | return; | |
| 801 | } else if (comptime (std.mem.eql(u8, fmt, "x"))) { | |
| 802 | @compileError("specifier 'x' has been deprecated, wrap your argument in std.fmt.fmtSliceHexLower instead"); | |
| 803 | } else if (comptime (std.mem.eql(u8, fmt, "X"))) { | |
| 804 | @compileError("specifier 'X' has been deprecated, wrap your argument in std.fmt.fmtSliceHexUpper instead"); | |
| 805 | } else if (comptime (std.mem.eql(u8, fmt, "e"))) { | |
| 806 | @compileError("specifier 'e' has been deprecated, wrap your argument in std.fmt.fmtSliceEscapeLower instead"); | |
| 807 | } else if (comptime (std.mem.eql(u8, fmt, "E"))) { | |
| 808 | @compileError("specifier 'X' has been deprecated, wrap your argument in std.fmt.fmtSliceEscapeUpper instead"); | |
| 809 | } else if (comptime std.mem.eql(u8, fmt, "z")) { | |
| 810 | @compileError("specifier 'z' has been deprecated, wrap your argument in std.zig.fmtId instead"); | |
| 811 | } else if (comptime std.mem.eql(u8, fmt, "Z")) { | |
| 812 | @compileError("specifier 'Z' has been deprecated, wrap your argument in std.zig.fmtEscapes instead"); | |
| 735 | 813 | } else { |
| 736 | 814 | @compileError("Unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'"); |
| 737 | 815 | } |
| ... | ... | @@ -1693,9 +1771,9 @@ test "slice" { |
| 1693 | 1771 | } |
| 1694 | 1772 | |
| 1695 | 1773 | test "escape non-printable" { |
| 1696 | try expectFmt("abc", "{e}", .{"abc"}); | |
| 1697 | try expectFmt("ab\\xffc", "{e}", .{"ab\xffc"}); | |
| 1698 | try expectFmt("ab\\xFFc", "{E}", .{"ab\xffc"}); | |
| 1774 | try expectFmt("abc", "{s}", .{fmtSliceEscapeLower("abc")}); | |
| 1775 | try expectFmt("ab\\xffc", "{s}", .{fmtSliceEscapeLower("ab\xffc")}); | |
| 1776 | try expectFmt("ab\\xFFc", "{s}", .{fmtSliceEscapeUpper("ab\xffc")}); | |
| 1699 | 1777 | } |
| 1700 | 1778 | |
| 1701 | 1779 | test "pointer" { |
| ... | ... | @@ -1968,13 +2046,13 @@ test "struct.zero-size" { |
| 1968 | 2046 | |
| 1969 | 2047 | test "bytes.hex" { |
| 1970 | 2048 | const some_bytes = "\xCA\xFE\xBA\xBE"; |
| 1971 | try expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); | |
| 1972 | try expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); | |
| 2049 | try expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{fmtSliceHexLower(some_bytes)}); | |
| 2050 | try expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{fmtSliceHexUpper(some_bytes)}); | |
| 1973 | 2051 | //Test Slices |
| 1974 | try expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); | |
| 1975 | try expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); | |
| 2052 | try expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{fmtSliceHexUpper(some_bytes[0..2])}); | |
| 2053 | try expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{fmtSliceHexLower(some_bytes[2..])}); | |
| 1976 | 2054 | const bytes_with_zeros = "\x00\x0E\xBA\xBE"; |
| 1977 | try expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); | |
| 2055 | try expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{fmtSliceHexLower(bytes_with_zeros)}); | |
| 1978 | 2056 | } |
| 1979 | 2057 | |
| 1980 | 2058 | pub const trim = @compileError("deprecated; use std.mem.trim with std.ascii.spaces instead"); |
| ... | ... | @@ -2002,9 +2080,9 @@ pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 { |
| 2002 | 2080 | |
| 2003 | 2081 | test "hexToBytes" { |
| 2004 | 2082 | var buf: [32]u8 = undefined; |
| 2005 | try expectFmt("90" ** 32, "{X}", .{try hexToBytes(&buf, "90" ** 32)}); | |
| 2006 | try expectFmt("ABCD", "{X}", .{try hexToBytes(&buf, "ABCD")}); | |
| 2007 | try expectFmt("", "{X}", .{try hexToBytes(&buf, "")}); | |
| 2083 | try expectFmt("90" ** 32, "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, "90" ** 32))}); | |
| 2084 | try expectFmt("ABCD", "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, "ABCD"))}); | |
| 2085 | try expectFmt("", "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, ""))}); | |
| 2008 | 2086 | std.testing.expectError(error.InvalidCharacter, hexToBytes(&buf, "012Z")); |
| 2009 | 2087 | std.testing.expectError(error.InvalidLength, hexToBytes(&buf, "AAA")); |
| 2010 | 2088 | std.testing.expectError(error.NoSpaceLeft, hexToBytes(buf[0..1], "ABAB")); |
lib/std/fs/get_app_data_dir.zig+1-1| ... | ... | @@ -60,7 +60,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD |
| 60 | 60 | var dir_path_ptr: [*:0]u8 = undefined; |
| 61 | 61 | // TODO look into directory_which |
| 62 | 62 | const be_user_settings = 0xbbe; |
| 63 | const rc = os.system.find_directory(be_user_settings, -1, true, dir_path_ptr, 1) ; | |
| 63 | const rc = os.system.find_directory(be_user_settings, -1, true, dir_path_ptr, 1); | |
| 64 | 64 | const settings_dir = try allocator.dupeZ(u8, mem.spanZ(dir_path_ptr)); |
| 65 | 65 | defer allocator.free(settings_dir); |
| 66 | 66 | switch (rc) { |
lib/std/io/writer.zig+7| ... | ... | @@ -4,6 +4,7 @@ |
| 4 | 4 | // The MIT license requires this copyright notice to be included in all copies |
| 5 | 5 | // and substantial portions of the software. |
| 6 | 6 | const std = @import("../std.zig"); |
| 7 | const assert = std.debug.assert; | |
| 7 | 8 | const builtin = std.builtin; |
| 8 | 9 | const mem = std.mem; |
| 9 | 10 | |
| ... | ... | @@ -86,5 +87,11 @@ pub fn Writer( |
| 86 | 87 | mem.writeInt(T, &bytes, value, endian); |
| 87 | 88 | return self.writeAll(&bytes); |
| 88 | 89 | } |
| 90 | ||
| 91 | pub fn writeStruct(self: Self, value: anytype) Error!void { | |
| 92 | // Only extern and packed structs have defined in-memory layout. | |
| 93 | comptime assert(@typeInfo(@TypeOf(value)).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto); | |
| 94 | return self.writeAll(mem.asBytes(&value)); | |
| 95 | } | |
| 89 | 96 | }; |
| 90 | 97 | } |
lib/std/math.zig+56| ... | ... | @@ -1330,3 +1330,59 @@ test "math.comptime" { |
| 1330 | 1330 | comptime const v = sin(@as(f32, 1)) + ln(@as(f32, 5)); |
| 1331 | 1331 | testing.expect(v == sin(@as(f32, 1)) + ln(@as(f32, 5))); |
| 1332 | 1332 | } |
| 1333 | ||
| 1334 | /// Returns a mask of all ones if value is true, | |
| 1335 | /// and a mask of all zeroes if value is false. | |
| 1336 | /// Compiles to one instruction for register sized integers. | |
| 1337 | pub fn boolMask(comptime MaskInt: type, value: bool) callconv(.Inline) MaskInt { | |
| 1338 | if (@typeInfo(MaskInt) != .Int) | |
| 1339 | @compileError("boolMask requires an integer mask type."); | |
| 1340 | ||
| 1341 | if (MaskInt == u0 or MaskInt == i0) | |
| 1342 | @compileError("boolMask cannot convert to u0 or i0, they are too small."); | |
| 1343 | ||
| 1344 | // The u1 and i1 cases tend to overflow, | |
| 1345 | // so we special case them here. | |
| 1346 | if (MaskInt == u1) return @boolToInt(value); | |
| 1347 | if (MaskInt == i1) { | |
| 1348 | // The @as here is a workaround for #7950 | |
| 1349 | return @bitCast(i1, @as(u1, @boolToInt(value))); | |
| 1350 | } | |
| 1351 | ||
| 1352 | // At comptime, -% is disallowed on unsigned values. | |
| 1353 | // So we need to jump through some hoops in that case. | |
| 1354 | // This is a workaround for #7951 | |
| 1355 | if (@typeInfo(@TypeOf(.{value})).Struct.fields[0].is_comptime) { | |
| 1356 | // Since it's comptime, we don't need this to generate nice code. | |
| 1357 | // We can just do a branch here. | |
| 1358 | return if (value) ~@as(MaskInt, 0) else 0; | |
| 1359 | } | |
| 1360 | ||
| 1361 | return -%@intCast(MaskInt, @boolToInt(value)); | |
| 1362 | } | |
| 1363 | ||
| 1364 | test "boolMask" { | |
| 1365 | const runTest = struct { | |
| 1366 | fn runTest() void { | |
| 1367 | testing.expectEqual(@as(u1, 0), boolMask(u1, false)); | |
| 1368 | testing.expectEqual(@as(u1, 1), boolMask(u1, true)); | |
| 1369 | ||
| 1370 | testing.expectEqual(@as(i1, 0), boolMask(i1, false)); | |
| 1371 | testing.expectEqual(@as(i1, -1), boolMask(i1, true)); | |
| 1372 | ||
| 1373 | testing.expectEqual(@as(u13, 0), boolMask(u13, false)); | |
| 1374 | testing.expectEqual(@as(u13, 0x1FFF), boolMask(u13, true)); | |
| 1375 | ||
| 1376 | testing.expectEqual(@as(i13, 0), boolMask(i13, false)); | |
| 1377 | testing.expectEqual(@as(i13, -1), boolMask(i13, true)); | |
| 1378 | ||
| 1379 | testing.expectEqual(@as(u32, 0), boolMask(u32, false)); | |
| 1380 | testing.expectEqual(@as(u32, 0xFFFF_FFFF), boolMask(u32, true)); | |
| 1381 | ||
| 1382 | testing.expectEqual(@as(i32, 0), boolMask(i32, false)); | |
| 1383 | testing.expectEqual(@as(i32, -1), boolMask(i32, true)); | |
| 1384 | } | |
| 1385 | }.runTest; | |
| 1386 | runTest(); | |
| 1387 | comptime runTest(); | |
| 1388 | } |
lib/std/mem.zig+8| ... | ... | @@ -25,6 +25,14 @@ pub const page_size = switch (builtin.arch) { |
| 25 | 25 | else => 4 * 1024, |
| 26 | 26 | }; |
| 27 | 27 | |
| 28 | /// The standard library currently thoroughly depends on byte size | |
| 29 | /// being 8 bits. (see the use of u8 throughout allocation code as | |
| 30 | /// the "byte" type.) Code which depends on this can reference this | |
| 31 | /// declaration. If we ever try to port the standard library to a | |
| 32 | /// non-8-bit-byte platform, this will allow us to search for things | |
| 33 | /// which need to be updated. | |
| 34 | pub const byte_size_in_bits = 8; | |
| 35 | ||
| 28 | 36 | pub const Allocator = @import("mem/Allocator.zig"); |
| 29 | 37 | |
| 30 | 38 | /// Detects and asserts if the std.mem.Allocator interface is violated by the caller |
lib/std/multi_array_list.zig+1-1| ... | ... | @@ -136,7 +136,7 @@ pub fn MultiArrayList(comptime S: type) type { |
| 136 | 136 | const slices = self.slice(); |
| 137 | 137 | var result: S = undefined; |
| 138 | 138 | inline for (fields) |field_info, i| { |
| 139 | @field(elem, field_info.name) = slices.items(@intToEnum(Field, i))[index]; | |
| 139 | @field(result, field_info.name) = slices.items(@intToEnum(Field, i))[index]; | |
| 140 | 140 | } |
| 141 | 141 | return result; |
| 142 | 142 | } |
lib/std/os/bits/haiku.zig+6-7| ... | ... | @@ -180,8 +180,8 @@ pub const dirent = extern struct { |
| 180 | 180 | }; |
| 181 | 181 | |
| 182 | 182 | pub const image_info = extern struct { |
| 183 | id: u32, //image_id | |
| 184 | type: u32, // image_type | |
| 183 | id: u32, | |
| 184 | type: u32, | |
| 185 | 185 | sequence: i32, |
| 186 | 186 | init_order: i32, |
| 187 | 187 | init_routine: *c_void, |
| ... | ... | @@ -806,17 +806,16 @@ pub const Sigaction = extern struct { |
| 806 | 806 | |
| 807 | 807 | pub const _SIG_WORDS = 4; |
| 808 | 808 | pub const _SIG_MAXSIG = 128; |
| 809 | ||
| 810 | pub inline fn _SIG_IDX(sig: usize) usize { | |
| 809 | pub fn _SIG_IDX(sig: usize) callconv(.Inline) usize { | |
| 811 | 810 | return sig - 1; |
| 812 | 811 | } |
| 813 | pub inline fn _SIG_WORD(sig: usize) usize { | |
| 812 | pub fn _SIG_WORD(sig: usize) callconv(.Inline) usize { | |
| 814 | 813 | return_SIG_IDX(sig) >> 5; |
| 815 | 814 | } |
| 816 | pub inline fn _SIG_BIT(sig: usize) usize { | |
| 815 | pub fn _SIG_BIT(sig: usize) callconv(.Inline) usize { | |
| 817 | 816 | return 1 << (_SIG_IDX(sig) & 31); |
| 818 | 817 | } |
| 819 | pub inline fn _SIG_VALID(sig: usize) usize { | |
| 818 | pub fn _SIG_VALID(sig: usize) callconv(.Inline) usize { | |
| 820 | 819 | return sig <= _SIG_MAXSIG and sig > 0; |
| 821 | 820 | } |
| 822 | 821 |
lib/std/os/bits/linux.zig+5| ... | ... | @@ -2244,3 +2244,8 @@ pub const MADV_COLD = 20; |
| 2244 | 2244 | pub const MADV_PAGEOUT = 21; |
| 2245 | 2245 | pub const MADV_HWPOISON = 100; |
| 2246 | 2246 | pub const MADV_SOFT_OFFLINE = 101; |
| 2247 | ||
| 2248 | pub const __kernel_timespec = extern struct { | |
| 2249 | tv_sec: i64, | |
| 2250 | tv_nsec: i64, | |
| 2251 | }; |
lib/std/os/linux/io_uring.zig+5-5| ... | ... | @@ -526,7 +526,7 @@ pub const IO_Uring = struct { |
| 526 | 526 | pub fn timeout( |
| 527 | 527 | self: *IO_Uring, |
| 528 | 528 | user_data: u64, |
| 529 | ts: *const os.timespec, | |
| 529 | ts: *const os.__kernel_timespec, | |
| 530 | 530 | count: u32, |
| 531 | 531 | flags: u32, |
| 532 | 532 | ) !*io_uring_sqe { |
| ... | ... | @@ -884,7 +884,7 @@ pub fn io_uring_prep_close(sqe: *io_uring_sqe, fd: os.fd_t) void { |
| 884 | 884 | |
| 885 | 885 | pub fn io_uring_prep_timeout( |
| 886 | 886 | sqe: *io_uring_sqe, |
| 887 | ts: *const os.timespec, | |
| 887 | ts: *const os.__kernel_timespec, | |
| 888 | 888 | count: u32, |
| 889 | 889 | flags: u32, |
| 890 | 890 | ) void { |
| ... | ... | @@ -1339,7 +1339,7 @@ test "timeout (after a relative time)" { |
| 1339 | 1339 | |
| 1340 | 1340 | const ms = 10; |
| 1341 | 1341 | const margin = 5; |
| 1342 | const ts = os.timespec{ .tv_sec = 0, .tv_nsec = ms * 1000000 }; | |
| 1342 | const ts = os.__kernel_timespec{ .tv_sec = 0, .tv_nsec = ms * 1000000 }; | |
| 1343 | 1343 | |
| 1344 | 1344 | const started = std.time.milliTimestamp(); |
| 1345 | 1345 | const sqe = try ring.timeout(0x55555555, &ts, 0, 0); |
| ... | ... | @@ -1366,7 +1366,7 @@ test "timeout (after a number of completions)" { |
| 1366 | 1366 | }; |
| 1367 | 1367 | defer ring.deinit(); |
| 1368 | 1368 | |
| 1369 | const ts = os.timespec{ .tv_sec = 3, .tv_nsec = 0 }; | |
| 1369 | const ts = os.__kernel_timespec{ .tv_sec = 3, .tv_nsec = 0 }; | |
| 1370 | 1370 | const count_completions: u64 = 1; |
| 1371 | 1371 | const sqe_timeout = try ring.timeout(0x66666666, &ts, count_completions, 0); |
| 1372 | 1372 | testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode); |
| ... | ... | @@ -1399,7 +1399,7 @@ test "timeout_remove" { |
| 1399 | 1399 | }; |
| 1400 | 1400 | defer ring.deinit(); |
| 1401 | 1401 | |
| 1402 | const ts = os.timespec{ .tv_sec = 3, .tv_nsec = 0 }; | |
| 1402 | const ts = os.__kernel_timespec{ .tv_sec = 3, .tv_nsec = 0 }; | |
| 1403 | 1403 | const sqe_timeout = try ring.timeout(0x88888888, &ts, 0, 0); |
| 1404 | 1404 | testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode); |
| 1405 | 1405 | testing.expectEqual(@as(u64, 0x88888888), sqe_timeout.user_data); |
lib/std/std.zig+4| ... | ... | @@ -18,6 +18,8 @@ pub const BufSet = @import("buf_set.zig").BufSet; |
| 18 | 18 | pub const ChildProcess = @import("child_process.zig").ChildProcess; |
| 19 | 19 | pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap; |
| 20 | 20 | pub const DynLib = @import("dynamic_library.zig").DynLib; |
| 21 | pub const DynamicBitSet = bit_set.DynamicBitSet; | |
| 22 | pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged; | |
| 21 | 23 | pub const HashMap = hash_map.HashMap; |
| 22 | 24 | pub const HashMapUnmanaged = hash_map.HashMapUnmanaged; |
| 23 | 25 | pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList; |
| ... | ... | @@ -29,6 +31,7 @@ pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; |
| 29 | 31 | pub const Progress = @import("Progress.zig"); |
| 30 | 32 | pub const SemanticVersion = @import("SemanticVersion.zig"); |
| 31 | 33 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; |
| 34 | pub const StaticBitSet = bit_set.StaticBitSet; | |
| 32 | 35 | pub const StringHashMap = hash_map.StringHashMap; |
| 33 | 36 | pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged; |
| 34 | 37 | pub const StringArrayHashMap = array_hash_map.StringArrayHashMap; |
| ... | ... | @@ -40,6 +43,7 @@ pub const Thread = @import("Thread.zig"); |
| 40 | 43 | pub const array_hash_map = @import("array_hash_map.zig"); |
| 41 | 44 | pub const atomic = @import("atomic.zig"); |
| 42 | 45 | pub const base64 = @import("base64.zig"); |
| 46 | pub const bit_set = @import("bit_set.zig"); | |
| 43 | 47 | pub const build = @import("build.zig"); |
| 44 | 48 | pub const builtin = @import("builtin.zig"); |
| 45 | 49 | pub const c = @import("c.zig"); |
lib/std/zig/parse.zig+12-20| ... | ... | @@ -3714,7 +3714,6 @@ const Parser = struct { |
| 3714 | 3714 | if (p.eatToken(.r_paren)) |_| { |
| 3715 | 3715 | return SmallSpan{ .zero_or_one = 0 }; |
| 3716 | 3716 | } |
| 3717 | continue; | |
| 3718 | 3717 | }, |
| 3719 | 3718 | .r_paren => return SmallSpan{ .zero_or_one = 0 }, |
| 3720 | 3719 | else => { |
| ... | ... | @@ -3728,14 +3727,7 @@ const Parser = struct { |
| 3728 | 3727 | |
| 3729 | 3728 | const param_two = while (true) { |
| 3730 | 3729 | switch (p.token_tags[p.nextToken()]) { |
| 3731 | .comma => { | |
| 3732 | if (p.eatToken(.r_paren)) |_| { | |
| 3733 | return SmallSpan{ .zero_or_one = param_one }; | |
| 3734 | } | |
| 3735 | const param = try p.expectParamDecl(); | |
| 3736 | if (param != 0) break param; | |
| 3737 | continue; | |
| 3738 | }, | |
| 3730 | .comma => {}, | |
| 3739 | 3731 | .r_paren => return SmallSpan{ .zero_or_one = param_one }, |
| 3740 | 3732 | .colon, .r_brace, .r_bracket => { |
| 3741 | 3733 | p.tok_i -= 1; |
| ... | ... | @@ -3748,6 +3740,11 @@ const Parser = struct { |
| 3748 | 3740 | try p.warnExpected(.comma); |
| 3749 | 3741 | }, |
| 3750 | 3742 | } |
| 3743 | if (p.eatToken(.r_paren)) |_| { | |
| 3744 | return SmallSpan{ .zero_or_one = param_one }; | |
| 3745 | } | |
| 3746 | const param = try p.expectParamDecl(); | |
| 3747 | if (param != 0) break param; | |
| 3751 | 3748 | } else unreachable; |
| 3752 | 3749 | |
| 3753 | 3750 | var list = std.ArrayList(Node.Index).init(p.gpa); |
| ... | ... | @@ -3757,17 +3754,7 @@ const Parser = struct { |
| 3757 | 3754 | |
| 3758 | 3755 | while (true) { |
| 3759 | 3756 | switch (p.token_tags[p.nextToken()]) { |
| 3760 | .comma => { | |
| 3761 | if (p.token_tags[p.tok_i] == .r_paren) { | |
| 3762 | p.tok_i += 1; | |
| 3763 | return SmallSpan{ .multi = list.toOwnedSlice() }; | |
| 3764 | } | |
| 3765 | const param = try p.expectParamDecl(); | |
| 3766 | if (param != 0) { | |
| 3767 | try list.append(param); | |
| 3768 | } | |
| 3769 | continue; | |
| 3770 | }, | |
| 3757 | .comma => {}, | |
| 3771 | 3758 | .r_paren => return SmallSpan{ .multi = list.toOwnedSlice() }, |
| 3772 | 3759 | .colon, .r_brace, .r_bracket => { |
| 3773 | 3760 | p.tok_i -= 1; |
| ... | ... | @@ -3780,6 +3767,11 @@ const Parser = struct { |
| 3780 | 3767 | try p.warnExpected(.comma); |
| 3781 | 3768 | }, |
| 3782 | 3769 | } |
| 3770 | if (p.eatToken(.r_paren)) |_| { | |
| 3771 | return SmallSpan{ .multi = list.toOwnedSlice() }; | |
| 3772 | } | |
| 3773 | const param = try p.expectParamDecl(); | |
| 3774 | if (param != 0) try list.append(param); | |
| 3783 | 3775 | } |
| 3784 | 3776 | } |
| 3785 | 3777 |
lib/std/zig/parser_test.zig+31| ... | ... | @@ -1108,6 +1108,25 @@ test "zig fmt: comment to disable/enable zig fmt first" { |
| 1108 | 1108 | ); |
| 1109 | 1109 | } |
| 1110 | 1110 | |
| 1111 | test "zig fmt: 'zig fmt: (off|on)' can be surrounded by arbitrary whitespace" { | |
| 1112 | try testTransform( | |
| 1113 | \\// Test trailing comma syntax | |
| 1114 | \\// zig fmt: off | |
| 1115 | \\ | |
| 1116 | \\const struct_trailing_comma = struct { x: i32, y: i32, }; | |
| 1117 | \\ | |
| 1118 | \\// zig fmt: on | |
| 1119 | , | |
| 1120 | \\// Test trailing comma syntax | |
| 1121 | \\// zig fmt: off | |
| 1122 | \\ | |
| 1123 | \\const struct_trailing_comma = struct { x: i32, y: i32, }; | |
| 1124 | \\ | |
| 1125 | \\// zig fmt: on | |
| 1126 | \\ | |
| 1127 | ); | |
| 1128 | } | |
| 1129 | ||
| 1111 | 1130 | test "zig fmt: comment to disable/enable zig fmt" { |
| 1112 | 1131 | try testTransform( |
| 1113 | 1132 | \\const a = b; |
| ... | ... | @@ -4549,6 +4568,18 @@ test "recovery: missing for payload" { |
| 4549 | 4568 | }); |
| 4550 | 4569 | } |
| 4551 | 4570 | |
| 4571 | test "recovery: missing comma in params" { | |
| 4572 | try testError( | |
| 4573 | \\fn foo(comptime bool what what) void { } | |
| 4574 | \\fn bar(a: i32, b: i32 c) void { } | |
| 4575 | \\ | |
| 4576 | , &[_]Error{ | |
| 4577 | .expected_token, | |
| 4578 | .expected_token, | |
| 4579 | .expected_token, | |
| 4580 | }); | |
| 4581 | } | |
| 4582 | ||
| 4552 | 4583 | const std = @import("std"); |
| 4553 | 4584 | const mem = std.mem; |
| 4554 | 4585 | const warn = std.debug.warn; |
lib/std/zig/render.zig+17-11| ... | ... | @@ -2352,18 +2352,24 @@ fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!boo |
| 2352 | 2352 | } |
| 2353 | 2353 | } |
| 2354 | 2354 | |
| 2355 | try ais.writer().print("{s}\n", .{trimmed_comment}); | |
| 2356 | index = 1 + (newline orelse return true); | |
| 2357 | ||
| 2358 | if (ais.disabled_offset) |disabled_offset| { | |
| 2359 | if (mem.eql(u8, trimmed_comment, "// zig fmt: on")) { | |
| 2360 | // write the source for which formatting was disabled directly | |
| 2361 | // to the underlying writer, fixing up invaild whitespace | |
| 2362 | try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..index]); | |
| 2363 | ais.disabled_offset = null; | |
| 2364 | } | |
| 2365 | } else if (mem.eql(u8, trimmed_comment, "// zig fmt: off")) { | |
| 2355 | index = 1 + (newline orelse end - 1); | |
| 2356 | ||
| 2357 | const comment_content = mem.trimLeft(u8, trimmed_comment["//".len..], &std.ascii.spaces); | |
| 2358 | if (ais.disabled_offset != null and mem.eql(u8, comment_content, "zig fmt: on")) { | |
| 2359 | // Write the source for which formatting was disabled directly | |
| 2360 | // to the underlying writer, fixing up invaild whitespace. | |
| 2361 | const disabled_source = tree.source[ais.disabled_offset.?..comment_start]; | |
| 2362 | try writeFixingWhitespace(ais.underlying_writer, disabled_source); | |
| 2363 | ais.disabled_offset = null; | |
| 2364 | // Write with the canonical single space. | |
| 2365 | try ais.writer().writeAll("// zig fmt: on\n"); | |
| 2366 | } else if (ais.disabled_offset == null and mem.eql(u8, comment_content, "zig fmt: off")) { | |
| 2367 | // Write with the canonical single space. | |
| 2368 | try ais.writer().writeAll("// zig fmt: off\n"); | |
| 2366 | 2369 | ais.disabled_offset = index; |
| 2370 | } else { | |
| 2371 | // Write the comment minus trailing whitespace. | |
| 2372 | try ais.writer().print("{s}\n", .{trimmed_comment}); | |
| 2367 | 2373 | } |
| 2368 | 2374 | } |
| 2369 | 2375 |
src/Cache.zig+20-4| ... | ... | @@ -153,7 +153,11 @@ pub const HashHelper = struct { |
| 153 | 153 | hh.hasher.final(&bin_digest); |
| 154 | 154 | |
| 155 | 155 | var out_digest: [hex_digest_len]u8 = undefined; |
| 156 | _ = std.fmt.bufPrint(&out_digest, "{x}", .{bin_digest}) catch unreachable; | |
| 156 | _ = std.fmt.bufPrint( | |
| 157 | &out_digest, | |
| 158 | "{s}", | |
| 159 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | |
| 160 | ) catch unreachable; | |
| 157 | 161 | return out_digest; |
| 158 | 162 | } |
| 159 | 163 | }; |
| ... | ... | @@ -250,7 +254,11 @@ pub const Manifest = struct { |
| 250 | 254 | var bin_digest: BinDigest = undefined; |
| 251 | 255 | self.hash.hasher.final(&bin_digest); |
| 252 | 256 | |
| 253 | _ = std.fmt.bufPrint(&self.hex_digest, "{x}", .{bin_digest}) catch unreachable; | |
| 257 | _ = std.fmt.bufPrint( | |
| 258 | &self.hex_digest, | |
| 259 | "{s}", | |
| 260 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | |
| 261 | ) catch unreachable; | |
| 254 | 262 | |
| 255 | 263 | self.hash.hasher = hasher_init; |
| 256 | 264 | self.hash.hasher.update(&bin_digest); |
| ... | ... | @@ -549,7 +557,11 @@ pub const Manifest = struct { |
| 549 | 557 | self.hash.hasher.final(&bin_digest); |
| 550 | 558 | |
| 551 | 559 | var out_digest: [hex_digest_len]u8 = undefined; |
| 552 | _ = std.fmt.bufPrint(&out_digest, "{x}", .{bin_digest}) catch unreachable; | |
| 560 | _ = std.fmt.bufPrint( | |
| 561 | &out_digest, | |
| 562 | "{s}", | |
| 563 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | |
| 564 | ) catch unreachable; | |
| 553 | 565 | |
| 554 | 566 | return out_digest; |
| 555 | 567 | } |
| ... | ... | @@ -565,7 +577,11 @@ pub const Manifest = struct { |
| 565 | 577 | var encoded_digest: [hex_digest_len]u8 = undefined; |
| 566 | 578 | |
| 567 | 579 | for (self.files.items) |file| { |
| 568 | _ = std.fmt.bufPrint(&encoded_digest, "{x}", .{file.bin_digest}) catch unreachable; | |
| 580 | _ = std.fmt.bufPrint( | |
| 581 | &encoded_digest, | |
| 582 | "{s}", | |
| 583 | .{std.fmt.fmtSliceHexLower(&file.bin_digest)}, | |
| 584 | ) catch unreachable; | |
| 569 | 585 | try writer.print("{d} {d} {d} {s} {s}\n", .{ |
| 570 | 586 | file.stat.size, |
| 571 | 587 | file.stat.inode, |
src/Module.zig+6-6| ... | ... | @@ -4083,15 +4083,15 @@ pub fn namedFieldPtr( |
| 4083 | 4083 | const child_type = try val.toType(scope.arena()); |
| 4084 | 4084 | switch (child_type.zigTypeTag()) { |
| 4085 | 4085 | .ErrorSet => { |
| 4086 | var name: []const u8 = undefined; | |
| 4086 | 4087 | // TODO resolve inferred error sets |
| 4087 | const entry = if (val.castTag(.error_set)) |payload| | |
| 4088 | (payload.data.fields.getEntry(field_name) orelse | |
| 4089 | return mod.fail(scope, src, "no error named '{s}' in '{}'", .{ field_name, child_type })).* | |
| 4088 | if (val.castTag(.error_set)) |payload| | |
| 4089 | name = (payload.data.fields.getEntry(field_name) orelse return mod.fail(scope, src, "no error named '{s}' in '{}'", .{ field_name, child_type })).key | |
| 4090 | 4090 | else |
| 4091 | try mod.getErrorValue(field_name); | |
| 4091 | name = (try mod.getErrorValue(field_name)).key; | |
| 4092 | 4092 | |
| 4093 | 4093 | const result_type = if (child_type.tag() == .anyerror) |
| 4094 | try Type.Tag.error_set_single.create(scope.arena(), entry.key) | |
| 4094 | try Type.Tag.error_set_single.create(scope.arena(), name) | |
| 4095 | 4095 | else |
| 4096 | 4096 | child_type; |
| 4097 | 4097 | |
| ... | ... | @@ -4100,7 +4100,7 @@ pub fn namedFieldPtr( |
| 4100 | 4100 | .val = try Value.Tag.ref_val.create( |
| 4101 | 4101 | scope.arena(), |
| 4102 | 4102 | try Value.Tag.@"error".create(scope.arena(), .{ |
| 4103 | .name = entry.key, | |
| 4103 | .name = name, | |
| 4104 | 4104 | }), |
| 4105 | 4105 | ), |
| 4106 | 4106 | }); |
src/astgen.zig+48-11| ... | ... | @@ -453,13 +453,23 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In |
| 453 | 453 | return rvalue(mod, scope, rl, result); |
| 454 | 454 | }, |
| 455 | 455 | .unwrap_optional => { |
| 456 | const operand = try expr(mod, scope, rl, node_datas[node].lhs); | |
| 457 | const op: zir.Inst.Tag = switch (rl) { | |
| 458 | .ref => .optional_payload_safe_ptr, | |
| 459 | else => .optional_payload_safe, | |
| 460 | }; | |
| 461 | 456 | const src = token_starts[main_tokens[node]]; |
| 462 | return addZIRUnOp(mod, scope, src, op, operand); | |
| 457 | switch (rl) { | |
| 458 | .ref => return addZIRUnOp( | |
| 459 | mod, | |
| 460 | scope, | |
| 461 | src, | |
| 462 | .optional_payload_safe_ptr, | |
| 463 | try expr(mod, scope, .ref, node_datas[node].lhs), | |
| 464 | ), | |
| 465 | else => return rvalue(mod, scope, rl, try addZIRUnOp( | |
| 466 | mod, | |
| 467 | scope, | |
| 468 | src, | |
| 469 | .optional_payload_safe, | |
| 470 | try expr(mod, scope, .none, node_datas[node].lhs), | |
| 471 | )), | |
| 472 | } | |
| 463 | 473 | }, |
| 464 | 474 | .block_two, .block_two_semicolon => { |
| 465 | 475 | const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; |
| ... | ... | @@ -1645,7 +1655,7 @@ fn errorSetDecl( |
| 1645 | 1655 | switch (token_tags[tok_i]) { |
| 1646 | 1656 | .doc_comment, .comma => {}, |
| 1647 | 1657 | .identifier => count += 1, |
| 1648 | .r_paren => break :count count, | |
| 1658 | .r_brace => break :count count, | |
| 1649 | 1659 | else => unreachable, |
| 1650 | 1660 | } |
| 1651 | 1661 | } else unreachable; // TODO should not need else unreachable here |
| ... | ... | @@ -1662,7 +1672,7 @@ fn errorSetDecl( |
| 1662 | 1672 | fields[field_i] = try mod.identifierTokenString(scope, tok_i); |
| 1663 | 1673 | field_i += 1; |
| 1664 | 1674 | }, |
| 1665 | .r_paren => break, | |
| 1675 | .r_brace => break, | |
| 1666 | 1676 | else => unreachable, |
| 1667 | 1677 | } |
| 1668 | 1678 | } |
| ... | ... | @@ -1699,9 +1709,13 @@ fn orelseCatchExpr( |
| 1699 | 1709 | setBlockResultLoc(&block_scope, rl); |
| 1700 | 1710 | defer block_scope.instructions.deinit(mod.gpa); |
| 1701 | 1711 | |
| 1702 | // This could be a pointer or value depending on the `rl` parameter. | |
| 1712 | // This could be a pointer or value depending on the `operand_rl` parameter. | |
| 1713 | // We cannot use `block_scope.break_result_loc` because that has the bare | |
| 1714 | // type, whereas this expression has the optional type. Later we make | |
| 1715 | // up for this fact by calling rvalue on the else branch. | |
| 1703 | 1716 | block_scope.break_count += 1; |
| 1704 | const operand = try expr(mod, &block_scope.base, block_scope.break_result_loc, lhs); | |
| 1717 | const operand_rl = try makeOptionalTypeResultLoc(mod, &block_scope.base, src, block_scope.break_result_loc); | |
| 1718 | const operand = try expr(mod, &block_scope.base, operand_rl, lhs); | |
| 1705 | 1719 | const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand); |
| 1706 | 1720 | |
| 1707 | 1721 | const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{ |
| ... | ... | @@ -1753,6 +1767,10 @@ fn orelseCatchExpr( |
| 1753 | 1767 | |
| 1754 | 1768 | // This could be a pointer or value depending on `unwrap_op`. |
| 1755 | 1769 | const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand); |
| 1770 | const else_result = switch (rl) { | |
| 1771 | .ref => unwrapped_payload, | |
| 1772 | else => try rvalue(mod, &else_scope.base, block_scope.break_result_loc, unwrapped_payload), | |
| 1773 | }; | |
| 1756 | 1774 | |
| 1757 | 1775 | return finishThenElseBlock( |
| 1758 | 1776 | mod, |
| ... | ... | @@ -1766,7 +1784,7 @@ fn orelseCatchExpr( |
| 1766 | 1784 | src, |
| 1767 | 1785 | src, |
| 1768 | 1786 | then_result, |
| 1769 | unwrapped_payload, | |
| 1787 | else_result, | |
| 1770 | 1788 | block, |
| 1771 | 1789 | block, |
| 1772 | 1790 | ); |
| ... | ... | @@ -3955,6 +3973,25 @@ fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy { |
| 3955 | 3973 | } |
| 3956 | 3974 | } |
| 3957 | 3975 | |
| 3976 | /// If the input ResultLoc is ref, returns ResultLoc.ref. Otherwise: | |
| 3977 | /// Returns ResultLoc.ty, where the type is determined by the input | |
| 3978 | /// ResultLoc type, wrapped in an optional type. If the input ResultLoc | |
| 3979 | /// has no type, .none is returned. | |
| 3980 | fn makeOptionalTypeResultLoc(mod: *Module, scope: *Scope, src: usize, rl: ResultLoc) !ResultLoc { | |
| 3981 | switch (rl) { | |
| 3982 | .ref => return ResultLoc.ref, | |
| 3983 | .discard, .none, .block_ptr, .inferred_ptr, .bitcasted_ptr => return ResultLoc.none, | |
| 3984 | .ty => |elem_ty| { | |
| 3985 | const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type, elem_ty); | |
| 3986 | return ResultLoc{ .ty = wrapped_ty }; | |
| 3987 | }, | |
| 3988 | .ptr => |ptr_ty| { | |
| 3989 | const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type_from_ptr_elem, ptr_ty); | |
| 3990 | return ResultLoc{ .ty = wrapped_ty }; | |
| 3991 | }, | |
| 3992 | } | |
| 3993 | } | |
| 3994 | ||
| 3958 | 3995 | fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void { |
| 3959 | 3996 | // Depending on whether the result location is a pointer or value, different |
| 3960 | 3997 | // ZIR needs to be generated. In the former case we rely on storing to the |
src/clang_options_data.zig+5-5| ... | ... | @@ -46,7 +46,7 @@ flagpd1("M"), |
| 46 | 46 | .{ |
| 47 | 47 | .name = "MM", |
| 48 | 48 | .syntax = .flag, |
| 49 | .zig_equivalent = .dep_file, | |
| 49 | .zig_equivalent = .dep_file_mm, | |
| 50 | 50 | .pd1 = true, |
| 51 | 51 | .pd2 = false, |
| 52 | 52 | .psl = false, |
| ... | ... | @@ -1870,7 +1870,7 @@ flagpsl("MT"), |
| 1870 | 1870 | .{ |
| 1871 | 1871 | .name = "print-missing-file-dependencies", |
| 1872 | 1872 | .syntax = .flag, |
| 1873 | .zig_equivalent = .other, | |
| 1873 | .zig_equivalent = .dep_file, | |
| 1874 | 1874 | .pd1 = false, |
| 1875 | 1875 | .pd2 = true, |
| 1876 | 1876 | .psl = false, |
| ... | ... | @@ -1990,7 +1990,7 @@ flagpsl("MT"), |
| 1990 | 1990 | .{ |
| 1991 | 1991 | .name = "user-dependencies", |
| 1992 | 1992 | .syntax = .flag, |
| 1993 | .zig_equivalent = .other, | |
| 1993 | .zig_equivalent = .dep_file_mm, | |
| 1994 | 1994 | .pd1 = false, |
| 1995 | 1995 | .pd2 = true, |
| 1996 | 1996 | .psl = false, |
| ... | ... | @@ -2014,7 +2014,7 @@ flagpsl("MT"), |
| 2014 | 2014 | .{ |
| 2015 | 2015 | .name = "write-dependencies", |
| 2016 | 2016 | .syntax = .flag, |
| 2017 | .zig_equivalent = .other, | |
| 2017 | .zig_equivalent = .dep_file, | |
| 2018 | 2018 | .pd1 = false, |
| 2019 | 2019 | .pd2 = true, |
| 2020 | 2020 | .psl = false, |
| ... | ... | @@ -2022,7 +2022,7 @@ flagpsl("MT"), |
| 2022 | 2022 | .{ |
| 2023 | 2023 | .name = "write-user-dependencies", |
| 2024 | 2024 | .syntax = .flag, |
| 2025 | .zig_equivalent = .other, | |
| 2025 | .zig_equivalent = .dep_file, | |
| 2026 | 2026 | .pd1 = false, |
| 2027 | 2027 | .pd2 = true, |
| 2028 | 2028 | .psl = false, |
src/codegen.zig+43| ... | ... | @@ -899,6 +899,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 899 | 899 | .load => return self.genLoad(inst.castTag(.load).?), |
| 900 | 900 | .loop => return self.genLoop(inst.castTag(.loop).?), |
| 901 | 901 | .not => return self.genNot(inst.castTag(.not).?), |
| 902 | .mul => return self.genMul(inst.castTag(.mul).?), | |
| 902 | 903 | .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?), |
| 903 | 904 | .ref => return self.genRef(inst.castTag(.ref).?), |
| 904 | 905 | .ret => return self.genRet(inst.castTag(.ret).?), |
| ... | ... | @@ -1128,6 +1129,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1128 | 1129 | } |
| 1129 | 1130 | } |
| 1130 | 1131 | |
| 1132 | fn genMul(self: *Self, inst: *ir.Inst.BinOp) !MCValue { | |
| 1133 | // No side effects, so if it's unreferenced, do nothing. | |
| 1134 | if (inst.base.isUnused()) | |
| 1135 | return MCValue.dead; | |
| 1136 | switch (arch) { | |
| 1137 | .arm, .armeb => return try self.genArmMul(&inst.base, inst.lhs, inst.rhs), | |
| 1138 | else => return self.fail(inst.base.src, "TODO implement mul for {}", .{self.target.cpu.arch}), | |
| 1139 | } | |
| 1140 | } | |
| 1141 | ||
| 1131 | 1142 | fn genBitAnd(self: *Self, inst: *ir.Inst.BinOp) !MCValue { |
| 1132 | 1143 | // No side effects, so if it's unreferenced, do nothing. |
| 1133 | 1144 | if (inst.base.isUnused()) |
| ... | ... | @@ -1478,6 +1489,38 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1478 | 1489 | } |
| 1479 | 1490 | } |
| 1480 | 1491 | |
| 1492 | fn genArmMul(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst) !MCValue { | |
| 1493 | const lhs = try self.resolveInst(op_lhs); | |
| 1494 | const rhs = try self.resolveInst(op_rhs); | |
| 1495 | ||
| 1496 | // Destination must be a register | |
| 1497 | // LHS must be a register | |
| 1498 | // RHS must be a register | |
| 1499 | var dst_mcv: MCValue = undefined; | |
| 1500 | var lhs_mcv: MCValue = undefined; | |
| 1501 | var rhs_mcv: MCValue = undefined; | |
| 1502 | if (self.reuseOperand(inst, 0, lhs)) { | |
| 1503 | // LHS is the destination | |
| 1504 | lhs_mcv = if (lhs != .register) try self.copyToNewRegister(inst, lhs) else lhs; | |
| 1505 | rhs_mcv = if (rhs != .register) try self.copyToNewRegister(inst, rhs) else rhs; | |
| 1506 | dst_mcv = lhs_mcv; | |
| 1507 | } else if (self.reuseOperand(inst, 1, rhs)) { | |
| 1508 | // RHS is the destination | |
| 1509 | lhs_mcv = if (lhs != .register) try self.copyToNewRegister(inst, lhs) else lhs; | |
| 1510 | rhs_mcv = if (rhs != .register) try self.copyToNewRegister(inst, rhs) else rhs; | |
| 1511 | dst_mcv = rhs_mcv; | |
| 1512 | } else { | |
| 1513 | // TODO save 1 copy instruction by directly allocating the destination register | |
| 1514 | // LHS is the destination | |
| 1515 | lhs_mcv = try self.copyToNewRegister(inst, lhs); | |
| 1516 | rhs_mcv = if (rhs != .register) try self.copyToNewRegister(inst, rhs) else rhs; | |
| 1517 | dst_mcv = lhs_mcv; | |
| 1518 | } | |
| 1519 | ||
| 1520 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.mul(.al, dst_mcv.register, lhs_mcv.register, rhs_mcv.register).toU32()); | |
| 1521 | return dst_mcv; | |
| 1522 | } | |
| 1523 | ||
| 1481 | 1524 | /// ADD, SUB, XOR, OR, AND |
| 1482 | 1525 | fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue { |
| 1483 | 1526 | try self.code.ensureCapacity(self.code.items.len + 8); |
src/codegen/llvm.zig+105-8| ... | ... | @@ -400,6 +400,7 @@ pub const LLVMIRModule = struct { |
| 400 | 400 | .block => try self.genBlock(inst.castTag(.block).?), |
| 401 | 401 | .br => try self.genBr(inst.castTag(.br).?), |
| 402 | 402 | .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), |
| 403 | .br_void => try self.genBrVoid(inst.castTag(.br_void).?), | |
| 403 | 404 | .call => try self.genCall(inst.castTag(.call).?), |
| 404 | 405 | .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq), |
| 405 | 406 | .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt), |
| ... | ... | @@ -409,6 +410,10 @@ pub const LLVMIRModule = struct { |
| 409 | 410 | .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq), |
| 410 | 411 | .condbr => try self.genCondBr(inst.castTag(.condbr).?), |
| 411 | 412 | .intcast => try self.genIntCast(inst.castTag(.intcast).?), |
| 413 | .is_non_null => try self.genIsNonNull(inst.castTag(.is_non_null).?, false), | |
| 414 | .is_non_null_ptr => try self.genIsNonNull(inst.castTag(.is_non_null_ptr).?, true), | |
| 415 | .is_null => try self.genIsNull(inst.castTag(.is_null).?, false), | |
| 416 | .is_null_ptr => try self.genIsNull(inst.castTag(.is_null_ptr).?, true), | |
| 412 | 417 | .load => try self.genLoad(inst.castTag(.load).?), |
| 413 | 418 | .loop => try self.genLoop(inst.castTag(.loop).?), |
| 414 | 419 | .not => try self.genNot(inst.castTag(.not).?), |
| ... | ... | @@ -417,6 +422,8 @@ pub const LLVMIRModule = struct { |
| 417 | 422 | .store => try self.genStore(inst.castTag(.store).?), |
| 418 | 423 | .sub => try self.genSub(inst.castTag(.sub).?), |
| 419 | 424 | .unreach => self.genUnreach(inst.castTag(.unreach).?), |
| 425 | .optional_payload => try self.genOptionalPayload(inst.castTag(.optional_payload).?, false), | |
| 426 | .optional_payload_ptr => try self.genOptionalPayload(inst.castTag(.optional_payload_ptr).?, true), | |
| 420 | 427 | .dbg_stmt => blk: { |
| 421 | 428 | // TODO: implement debug info |
| 422 | 429 | break :blk null; |
| ... | ... | @@ -537,21 +544,29 @@ pub const LLVMIRModule = struct { |
| 537 | 544 | } |
| 538 | 545 | |
| 539 | 546 | fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value { |
| 540 | // Get the block that we want to break to. | |
| 541 | 547 | var block = self.blocks.get(inst.block).?; |
| 542 | _ = self.builder.buildBr(block.parent_bb); | |
| 543 | 548 | |
| 544 | 549 | // If the break doesn't break a value, then we don't have to add |
| 545 | 550 | // the values to the lists. |
| 546 | if (!inst.operand.ty.hasCodeGenBits()) return null; | |
| 551 | if (!inst.operand.ty.hasCodeGenBits()) { | |
| 552 | // TODO: in astgen these instructions should turn into `br_void` instructions. | |
| 553 | _ = self.builder.buildBr(block.parent_bb); | |
| 554 | } else { | |
| 555 | const val = try self.resolveInst(inst.operand); | |
| 547 | 556 | |
| 548 | // For the phi node, we need the basic blocks and the values of the | |
| 549 | // break instructions. | |
| 550 | try block.break_bbs.append(self.gpa, self.builder.getInsertBlock()); | |
| 557 | // For the phi node, we need the basic blocks and the values of the | |
| 558 | // break instructions. | |
| 559 | try block.break_bbs.append(self.gpa, self.builder.getInsertBlock()); | |
| 560 | try block.break_vals.append(self.gpa, val); | |
| 551 | 561 | |
| 552 | const val = try self.resolveInst(inst.operand); | |
| 553 | try block.break_vals.append(self.gpa, val); | |
| 562 | _ = self.builder.buildBr(block.parent_bb); | |
| 563 | } | |
| 564 | return null; | |
| 565 | } | |
| 554 | 566 | |
| 567 | fn genBrVoid(self: *LLVMIRModule, inst: *Inst.BrVoid) !?*const llvm.Value { | |
| 568 | var block = self.blocks.get(inst.block).?; | |
| 569 | _ = self.builder.buildBr(block.parent_bb); | |
| 555 | 570 | return null; |
| 556 | 571 | } |
| 557 | 572 | |
| ... | ... | @@ -594,6 +609,44 @@ pub const LLVMIRModule = struct { |
| 594 | 609 | return null; |
| 595 | 610 | } |
| 596 | 611 | |
| 612 | fn genIsNonNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { | |
| 613 | const operand = try self.resolveInst(inst.operand); | |
| 614 | ||
| 615 | if (operand_is_ptr) { | |
| 616 | const index_type = self.context.intType(32); | |
| 617 | ||
| 618 | var indices: [2]*const llvm.Value = .{ | |
| 619 | index_type.constNull(), | |
| 620 | index_type.constInt(1, false), | |
| 621 | }; | |
| 622 | ||
| 623 | return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), ""); | |
| 624 | } else { | |
| 625 | return self.builder.buildExtractValue(operand, 1, ""); | |
| 626 | } | |
| 627 | } | |
| 628 | ||
| 629 | fn genIsNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { | |
| 630 | return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, ""); | |
| 631 | } | |
| 632 | ||
| 633 | fn genOptionalPayload(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { | |
| 634 | const operand = try self.resolveInst(inst.operand); | |
| 635 | ||
| 636 | if (operand_is_ptr) { | |
| 637 | const index_type = self.context.intType(32); | |
| 638 | ||
| 639 | var indices: [2]*const llvm.Value = .{ | |
| 640 | index_type.constNull(), | |
| 641 | index_type.constNull(), | |
| 642 | }; | |
| 643 | ||
| 644 | return self.builder.buildInBoundsGEP(operand, &indices, 2, ""); | |
| 645 | } else { | |
| 646 | return self.builder.buildExtractValue(operand, 0, ""); | |
| 647 | } | |
| 648 | } | |
| 649 | ||
| 597 | 650 | fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { |
| 598 | 651 | const lhs = try self.resolveInst(inst.lhs); |
| 599 | 652 | const rhs = try self.resolveInst(inst.rhs); |
| ... | ... | @@ -754,6 +807,13 @@ pub const LLVMIRModule = struct { |
| 754 | 807 | // TODO: consider using buildInBoundsGEP2 for opaque pointers |
| 755 | 808 | return self.builder.buildInBoundsGEP(val, &indices, 2, ""); |
| 756 | 809 | }, |
| 810 | .ref_val => { | |
| 811 | const elem_value = tv.val.castTag(.ref_val).?.data; | |
| 812 | const elem_type = tv.ty.castPointer().?.data; | |
| 813 | const alloca = self.buildAlloca(try self.getLLVMType(elem_type, src)); | |
| 814 | _ = self.builder.buildStore(try self.genTypedValue(src, .{ .ty = elem_type, .val = elem_value }), alloca); | |
| 815 | return alloca; | |
| 816 | }, | |
| 757 | 817 | else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}), |
| 758 | 818 | }, |
| 759 | 819 | .Array => { |
| ... | ... | @@ -768,6 +828,29 @@ pub const LLVMIRModule = struct { |
| 768 | 828 | return self.fail(src, "TODO handle more array values", .{}); |
| 769 | 829 | } |
| 770 | 830 | }, |
| 831 | .Optional => { | |
| 832 | if (!tv.ty.isPtrLikeOptional()) { | |
| 833 | var buf: Type.Payload.ElemType = undefined; | |
| 834 | const child_type = tv.ty.optionalChild(&buf); | |
| 835 | const llvm_child_type = try self.getLLVMType(child_type, src); | |
| 836 | ||
| 837 | if (tv.val.tag() == .null_value) { | |
| 838 | var optional_values: [2]*const llvm.Value = .{ | |
| 839 | llvm_child_type.constNull(), | |
| 840 | self.context.intType(1).constNull(), | |
| 841 | }; | |
| 842 | return self.context.constStruct(&optional_values, 2, false); | |
| 843 | } else { | |
| 844 | var optional_values: [2]*const llvm.Value = .{ | |
| 845 | try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }), | |
| 846 | self.context.intType(1).constAllOnes(), | |
| 847 | }; | |
| 848 | return self.context.constStruct(&optional_values, 2, false); | |
| 849 | } | |
| 850 | } else { | |
| 851 | return self.fail(src, "TODO implement const of optional pointer", .{}); | |
| 852 | } | |
| 853 | }, | |
| 771 | 854 | else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}), |
| 772 | 855 | } |
| 773 | 856 | } |
| ... | ... | @@ -793,6 +876,20 @@ pub const LLVMIRModule = struct { |
| 793 | 876 | const elem_type = try self.getLLVMType(t.elemType(), src); |
| 794 | 877 | return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget()))); |
| 795 | 878 | }, |
| 879 | .Optional => { | |
| 880 | if (!t.isPtrLikeOptional()) { | |
| 881 | var buf: Type.Payload.ElemType = undefined; | |
| 882 | const child_type = t.optionalChild(&buf); | |
| 883 | ||
| 884 | var optional_types: [2]*const llvm.Type = .{ | |
| 885 | try self.getLLVMType(child_type, src), | |
| 886 | self.context.intType(1), | |
| 887 | }; | |
| 888 | return self.context.structType(&optional_types, 2, false); | |
| 889 | } else { | |
| 890 | return self.fail(src, "TODO implement optional pointers as actual pointers", .{}); | |
| 891 | } | |
| 892 | }, | |
| 796 | 893 | else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}), |
| 797 | 894 | } |
| 798 | 895 | } |
src/codegen/llvm/bindings.zig+9| ... | ... | @@ -21,9 +21,15 @@ pub const Context = opaque { |
| 21 | 21 | pub const voidType = LLVMVoidTypeInContext; |
| 22 | 22 | extern fn LLVMVoidTypeInContext(C: *const Context) *const Type; |
| 23 | 23 | |
| 24 | pub const structType = LLVMStructTypeInContext; | |
| 25 | extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: LLVMBool) *const Type; | |
| 26 | ||
| 24 | 27 | pub const constString = LLVMConstStringInContext; |
| 25 | 28 | extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value; |
| 26 | 29 | |
| 30 | pub const constStruct = LLVMConstStructInContext; | |
| 31 | extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: LLVMBool) *const Value; | |
| 32 | ||
| 27 | 33 | pub const createBasicBlock = LLVMCreateBasicBlockInContext; |
| 28 | 34 | extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock; |
| 29 | 35 | |
| ... | ... | @@ -204,6 +210,9 @@ pub const Builder = opaque { |
| 204 | 210 | |
| 205 | 211 | pub const buildPhi = LLVMBuildPhi; |
| 206 | 212 | extern fn LLVMBuildPhi(*const Builder, Ty: *const Type, Name: [*:0]const u8) *const Value; |
| 213 | ||
| 214 | pub const buildExtractValue = LLVMBuildExtractValue; | |
| 215 | extern fn LLVMBuildExtractValue(*const Builder, AggVal: *const Value, Index: c_uint, Name: [*:0]const u8) *const Value; | |
| 207 | 216 | }; |
| 208 | 217 | |
| 209 | 218 | pub const IntPredicate = extern enum { |
src/ir.zig+2| ... | ... | @@ -106,6 +106,7 @@ pub const Inst = struct { |
| 106 | 106 | store, |
| 107 | 107 | sub, |
| 108 | 108 | unreach, |
| 109 | mul, | |
| 109 | 110 | not, |
| 110 | 111 | floatcast, |
| 111 | 112 | intcast, |
| ... | ... | @@ -165,6 +166,7 @@ pub const Inst = struct { |
| 165 | 166 | |
| 166 | 167 | .add, |
| 167 | 168 | .sub, |
| 169 | .mul, | |
| 168 | 170 | .cmp_lt, |
| 169 | 171 | .cmp_lte, |
| 170 | 172 | .cmp_eq, |
src/link.zig+2-2| ... | ... | @@ -550,11 +550,11 @@ pub const File = struct { |
| 550 | 550 | id_symlink_basename, |
| 551 | 551 | &prev_digest_buf, |
| 552 | 552 | ) catch |err| b: { |
| 553 | log.debug("archive new_digest={x} readFile error: {s}", .{ digest, @errorName(err) }); | |
| 553 | log.debug("archive new_digest={s} readFile error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); | |
| 554 | 554 | break :b prev_digest_buf[0..0]; |
| 555 | 555 | }; |
| 556 | 556 | if (mem.eql(u8, prev_digest, &digest)) { |
| 557 | log.debug("archive digest={x} match - skipping invocation", .{digest}); | |
| 557 | log.debug("archive digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); | |
| 558 | 558 | base.lock = man.toOwnedLock(); |
| 559 | 559 | return; |
| 560 | 560 | } |
src/link/Coff.zig+3-3| ... | ... | @@ -892,17 +892,17 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 892 | 892 | id_symlink_basename, |
| 893 | 893 | &prev_digest_buf, |
| 894 | 894 | ) catch |err| blk: { |
| 895 | log.debug("COFF LLD new_digest={x} error: {s}", .{ digest, @errorName(err) }); | |
| 895 | log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); | |
| 896 | 896 | // Handle this as a cache miss. |
| 897 | 897 | break :blk prev_digest_buf[0..0]; |
| 898 | 898 | }; |
| 899 | 899 | if (mem.eql(u8, prev_digest, &digest)) { |
| 900 | log.debug("COFF LLD digest={x} match - skipping invocation", .{digest}); | |
| 900 | log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); | |
| 901 | 901 | // Hot diggity dog! The output binary is already there. |
| 902 | 902 | self.base.lock = man.toOwnedLock(); |
| 903 | 903 | return; |
| 904 | 904 | } |
| 905 | log.debug("COFF LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest }); | |
| 905 | log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); | |
| 906 | 906 | |
| 907 | 907 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 908 | 908 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
src/link/Elf.zig+3-3| ... | ... | @@ -1365,17 +1365,17 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1365 | 1365 | id_symlink_basename, |
| 1366 | 1366 | &prev_digest_buf, |
| 1367 | 1367 | ) catch |err| blk: { |
| 1368 | log.debug("ELF LLD new_digest={x} error: {s}", .{ digest, @errorName(err) }); | |
| 1368 | log.debug("ELF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); | |
| 1369 | 1369 | // Handle this as a cache miss. |
| 1370 | 1370 | break :blk prev_digest_buf[0..0]; |
| 1371 | 1371 | }; |
| 1372 | 1372 | if (mem.eql(u8, prev_digest, &digest)) { |
| 1373 | log.debug("ELF LLD digest={x} match - skipping invocation", .{digest}); | |
| 1373 | log.debug("ELF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); | |
| 1374 | 1374 | // Hot diggity dog! The output binary is already there. |
| 1375 | 1375 | self.base.lock = man.toOwnedLock(); |
| 1376 | 1376 | return; |
| 1377 | 1377 | } |
| 1378 | log.debug("ELF LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest }); | |
| 1378 | log.debug("ELF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); | |
| 1379 | 1379 | |
| 1380 | 1380 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 1381 | 1381 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
src/link/MachO.zig+3-3| ... | ... | @@ -556,17 +556,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 556 | 556 | id_symlink_basename, |
| 557 | 557 | &prev_digest_buf, |
| 558 | 558 | ) catch |err| blk: { |
| 559 | log.debug("MachO LLD new_digest={x} error: {s}", .{ digest, @errorName(err) }); | |
| 559 | log.debug("MachO LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); | |
| 560 | 560 | // Handle this as a cache miss. |
| 561 | 561 | break :blk prev_digest_buf[0..0]; |
| 562 | 562 | }; |
| 563 | 563 | if (mem.eql(u8, prev_digest, &digest)) { |
| 564 | log.debug("MachO LLD digest={x} match - skipping invocation", .{digest}); | |
| 564 | log.debug("MachO LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); | |
| 565 | 565 | // Hot diggity dog! The output binary is already there. |
| 566 | 566 | self.base.lock = man.toOwnedLock(); |
| 567 | 567 | return; |
| 568 | 568 | } |
| 569 | log.debug("MachO LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest }); | |
| 569 | log.debug("MachO LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); | |
| 570 | 570 | |
| 571 | 571 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 572 | 572 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
src/link/Wasm.zig+3-3| ... | ... | @@ -391,17 +391,17 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 391 | 391 | id_symlink_basename, |
| 392 | 392 | &prev_digest_buf, |
| 393 | 393 | ) catch |err| blk: { |
| 394 | log.debug("WASM LLD new_digest={x} error: {s}", .{ digest, @errorName(err) }); | |
| 394 | log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); | |
| 395 | 395 | // Handle this as a cache miss. |
| 396 | 396 | break :blk prev_digest_buf[0..0]; |
| 397 | 397 | }; |
| 398 | 398 | if (mem.eql(u8, prev_digest, &digest)) { |
| 399 | log.debug("WASM LLD digest={x} match - skipping invocation", .{digest}); | |
| 399 | log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); | |
| 400 | 400 | // Hot diggity dog! The output binary is already there. |
| 401 | 401 | self.base.lock = man.toOwnedLock(); |
| 402 | 402 | return; |
| 403 | 403 | } |
| 404 | log.debug("WASM LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest }); | |
| 404 | log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); | |
| 405 | 405 | |
| 406 | 406 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 407 | 407 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
src/main.zig+18-2| ... | ... | @@ -448,6 +448,15 @@ const Emit = union(enum) { |
| 448 | 448 | } |
| 449 | 449 | }; |
| 450 | 450 | |
| 451 | fn optionalBoolEnvVar(arena: *Allocator, name: []const u8) !bool { | |
| 452 | if (std.process.getEnvVarOwned(arena, name)) |value| { | |
| 453 | return true; | |
| 454 | } else |err| switch (err) { | |
| 455 | error.EnvironmentVariableNotFound => return false, | |
| 456 | else => |e| return e, | |
| 457 | } | |
| 458 | } | |
| 459 | ||
| 451 | 460 | fn optionalStringEnvVar(arena: *Allocator, name: []const u8) !?[]const u8 { |
| 452 | 461 | if (std.process.getEnvVarOwned(arena, name)) |value| { |
| 453 | 462 | return value; |
| ... | ... | @@ -482,8 +491,8 @@ fn buildOutputType( |
| 482 | 491 | var single_threaded = false; |
| 483 | 492 | var function_sections = false; |
| 484 | 493 | var watch = false; |
| 485 | var verbose_link = false; | |
| 486 | var verbose_cc = false; | |
| 494 | var verbose_link = try optionalBoolEnvVar(arena, "ZIG_VERBOSE_LINK"); | |
| 495 | var verbose_cc = try optionalBoolEnvVar(arena, "ZIG_VERBOSE_CC"); | |
| 487 | 496 | var verbose_tokenize = false; |
| 488 | 497 | var verbose_ast = false; |
| 489 | 498 | var verbose_ir = false; |
| ... | ... | @@ -1183,6 +1192,12 @@ fn buildOutputType( |
| 1183 | 1192 | disable_c_depfile = true; |
| 1184 | 1193 | try clang_argv.appendSlice(it.other_args); |
| 1185 | 1194 | }, |
| 1195 | .dep_file_mm => { // -MM | |
| 1196 | // "Like -MMD, but also implies -E and writes to stdout by default" | |
| 1197 | c_out_mode = .preprocessor; | |
| 1198 | disable_c_depfile = true; | |
| 1199 | try clang_argv.appendSlice(it.other_args); | |
| 1200 | }, | |
| 1186 | 1201 | .framework_dir => try framework_dirs.append(it.only_arg), |
| 1187 | 1202 | .framework => try frameworks.append(it.only_arg), |
| 1188 | 1203 | .nostdlibinc => want_native_include_dirs = false, |
| ... | ... | @@ -3046,6 +3061,7 @@ pub const ClangArgIterator = struct { |
| 3046 | 3061 | lib_dir, |
| 3047 | 3062 | mcpu, |
| 3048 | 3063 | dep_file, |
| 3064 | dep_file_mm, | |
| 3049 | 3065 | framework_dir, |
| 3050 | 3066 | framework, |
| 3051 | 3067 | nostdlibinc, |
src/stage1/codegen.cpp+9-1| ... | ... | @@ -4126,7 +4126,15 @@ static LLVMValueRef gen_frame_size(CodeGen *g, LLVMValueRef fn_val) { |
| 4126 | 4126 | LLVMValueRef casted_fn_val = LLVMBuildBitCast(g->builder, fn_val, ptr_usize_llvm_type, ""); |
| 4127 | 4127 | LLVMValueRef negative_one = LLVMConstInt(LLVMInt32Type(), -1, true); |
| 4128 | 4128 | LLVMValueRef prefix_ptr = LLVMBuildInBoundsGEP(g->builder, casted_fn_val, &negative_one, 1, ""); |
| 4129 | return LLVMBuildLoad(g->builder, prefix_ptr, ""); | |
| 4129 | LLVMValueRef load_inst = LLVMBuildLoad(g->builder, prefix_ptr, ""); | |
| 4130 | ||
| 4131 | // Some architectures (e.g SPARCv9) has different alignment requirements between a | |
| 4132 | // function/usize pointer and also require all loads to be aligned. | |
| 4133 | // On those architectures, not explicitly setting the alignment will lead into @frameSize | |
| 4134 | // generating usize-aligned load instruction that could crash if the function pointer | |
| 4135 | // happens to be not usize-aligned. | |
| 4136 | LLVMSetAlignment(load_inst, 1); | |
| 4137 | return load_inst; | |
| 4130 | 4138 | } |
| 4131 | 4139 | |
| 4132 | 4140 | static void gen_init_stack_trace(CodeGen *g, LLVMValueRef trace_field_ptr, LLVMValueRef addrs_field_ptr) { |
src/test.zig+2-2| ... | ... | @@ -1030,8 +1030,8 @@ pub const TestContext = struct { |
| 1030 | 1030 | var file = try tmp_dir.openFile(bin_name, .{ .read = true }); |
| 1031 | 1031 | defer file.close(); |
| 1032 | 1032 | |
| 1033 | const header = try std.elf.readHeader(file); | |
| 1034 | var iterator = header.program_header_iterator(file); | |
| 1033 | const header = try std.elf.Header.read(&file); | |
| 1034 | var iterator = header.program_header_iterator(&file); | |
| 1035 | 1035 | |
| 1036 | 1036 | var none_loaded = true; |
| 1037 | 1037 |
src/translate_c.zig+1-1| ... | ... | @@ -4608,7 +4608,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N |
| 4608 | 4608 | if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) { |
| 4609 | 4609 | return Tag.char_literal.create(c.arena, try zigifyEscapeSequences(c, m)); |
| 4610 | 4610 | } else { |
| 4611 | const str = try std.fmt.allocPrint(c.arena, "0x{x}", .{slice[1 .. slice.len - 1]}); | |
| 4611 | const str = try std.fmt.allocPrint(c.arena, "0x{s}", .{std.fmt.fmtSliceHexLower(slice[1 .. slice.len - 1])}); | |
| 4612 | 4612 | return Tag.integer_literal.create(c.arena, str); |
| 4613 | 4613 | } |
| 4614 | 4614 | }, |
src/value.zig+1-1| ... | ... | @@ -2144,7 +2144,7 @@ pub const Value = extern union { |
| 2144 | 2144 | base: Payload = .{ .tag = base_tag }, |
| 2145 | 2145 | data: struct { |
| 2146 | 2146 | /// TODO revisit this when we have the concept of the error tag type |
| 2147 | fields: std.StringHashMapUnmanaged(u16), | |
| 2147 | fields: std.StringHashMapUnmanaged(void), | |
| 2148 | 2148 | decl: *Module.Decl, |
| 2149 | 2149 | }, |
| 2150 | 2150 | }; |
src/zir.zig+7| ... | ... | @@ -299,6 +299,9 @@ pub const Inst = struct { |
| 299 | 299 | xor, |
| 300 | 300 | /// Create an optional type '?T' |
| 301 | 301 | optional_type, |
| 302 | /// Create an optional type '?T'. The operand is a pointer value. The optional type will | |
| 303 | /// be the type of the pointer element, wrapped in an optional. | |
| 304 | optional_type_from_ptr_elem, | |
| 302 | 305 | /// Create a union type. |
| 303 | 306 | union_type, |
| 304 | 307 | /// ?T => T with safety. |
| ... | ... | @@ -397,6 +400,7 @@ pub const Inst = struct { |
| 397 | 400 | .mut_slice_type, |
| 398 | 401 | .const_slice_type, |
| 399 | 402 | .optional_type, |
| 403 | .optional_type_from_ptr_elem, | |
| 400 | 404 | .optional_payload_safe, |
| 401 | 405 | .optional_payload_unsafe, |
| 402 | 406 | .optional_payload_safe_ptr, |
| ... | ... | @@ -597,6 +601,7 @@ pub const Inst = struct { |
| 597 | 601 | .typeof, |
| 598 | 602 | .xor, |
| 599 | 603 | .optional_type, |
| 604 | .optional_type_from_ptr_elem, | |
| 600 | 605 | .optional_payload_safe, |
| 601 | 606 | .optional_payload_unsafe, |
| 602 | 607 | .optional_payload_safe_ptr, |
| ... | ... | @@ -1649,6 +1654,7 @@ const DumpTzir = struct { |
| 1649 | 1654 | |
| 1650 | 1655 | .add, |
| 1651 | 1656 | .sub, |
| 1657 | .mul, | |
| 1652 | 1658 | .cmp_lt, |
| 1653 | 1659 | .cmp_lte, |
| 1654 | 1660 | .cmp_eq, |
| ... | ... | @@ -1771,6 +1777,7 @@ const DumpTzir = struct { |
| 1771 | 1777 | |
| 1772 | 1778 | .add, |
| 1773 | 1779 | .sub, |
| 1780 | .mul, | |
| 1774 | 1781 | .cmp_lt, |
| 1775 | 1782 | .cmp_lte, |
| 1776 | 1783 | .cmp_eq, |
src/zir_sema.zig+86-2| ... | ... | @@ -131,6 +131,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 131 | 131 | .typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?), |
| 132 | 132 | .typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?), |
| 133 | 133 | .optional_type => return zirOptionalType(mod, scope, old_inst.castTag(.optional_type).?), |
| 134 | .optional_type_from_ptr_elem => return zirOptionalTypeFromPtrElem(mod, scope, old_inst.castTag(.optional_type_from_ptr_elem).?), | |
| 134 | 135 | .optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true), |
| 135 | 136 | .optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false), |
| 136 | 137 | .optional_payload_safe_ptr => return zirOptionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_safe_ptr).?, true), |
| ... | ... | @@ -1093,6 +1094,16 @@ fn zirOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerE |
| 1093 | 1094 | return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type)); |
| 1094 | 1095 | } |
| 1095 | 1096 | |
| 1097 | fn zirOptionalTypeFromPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 1098 | const tracy = trace(@src()); | |
| 1099 | defer tracy.end(); | |
| 1100 | ||
| 1101 | const ptr = try resolveInst(mod, scope, inst.positionals.operand); | |
| 1102 | const elem_ty = ptr.ty.elemType(); | |
| 1103 | ||
| 1104 | return mod.constType(scope, inst.base.src, try mod.optionalType(scope, elem_ty)); | |
| 1105 | } | |
| 1106 | ||
| 1096 | 1107 | fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst { |
| 1097 | 1108 | const tracy = trace(@src()); |
| 1098 | 1109 | defer tracy.end(); |
| ... | ... | @@ -1154,7 +1165,7 @@ fn zirErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) InnerError |
| 1154 | 1165 | |
| 1155 | 1166 | for (inst.positionals.fields) |field_name| { |
| 1156 | 1167 | const entry = try mod.getErrorValue(field_name); |
| 1157 | if (payload.data.fields.fetchPutAssumeCapacity(entry.key, entry.value)) |prev| { | |
| 1168 | if (payload.data.fields.fetchPutAssumeCapacity(entry.key, {})) |_| { | |
| 1158 | 1169 | return mod.fail(scope, inst.base.src, "duplicate error: '{s}'", .{field_name}); |
| 1159 | 1170 | } |
| 1160 | 1171 | } |
| ... | ... | @@ -1185,7 +1196,79 @@ fn zirErrorValue(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorValue) InnerE |
| 1185 | 1196 | fn zirMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { |
| 1186 | 1197 | const tracy = trace(@src()); |
| 1187 | 1198 | defer tracy.end(); |
| 1188 | return mod.fail(scope, inst.base.src, "TODO implement merge_error_sets", .{}); | |
| 1199 | ||
| 1200 | const rhs_ty = try resolveType(mod, scope, inst.positionals.rhs); | |
| 1201 | const lhs_ty = try resolveType(mod, scope, inst.positionals.lhs); | |
| 1202 | if (rhs_ty.zigTypeTag() != .ErrorSet) | |
| 1203 | return mod.fail(scope, inst.positionals.rhs.src, "expected error set type, found {}", .{rhs_ty}); | |
| 1204 | if (lhs_ty.zigTypeTag() != .ErrorSet) | |
| 1205 | return mod.fail(scope, inst.positionals.lhs.src, "expected error set type, found {}", .{lhs_ty}); | |
| 1206 | ||
| 1207 | // anything merged with anyerror is anyerror | |
| 1208 | if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) | |
| 1209 | return mod.constInst(scope, inst.base.src, .{ | |
| 1210 | .ty = Type.initTag(.type), | |
| 1211 | .val = Value.initTag(.anyerror_type), | |
| 1212 | }); | |
| 1213 | // The declarations arena will store the hashmap. | |
| 1214 | var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); | |
| 1215 | errdefer new_decl_arena.deinit(); | |
| 1216 | ||
| 1217 | const payload = try new_decl_arena.allocator.create(Value.Payload.ErrorSet); | |
| 1218 | payload.* = .{ | |
| 1219 | .base = .{ .tag = .error_set }, | |
| 1220 | .data = .{ | |
| 1221 | .fields = .{}, | |
| 1222 | .decl = undefined, // populated below | |
| 1223 | }, | |
| 1224 | }; | |
| 1225 | try payload.data.fields.ensureCapacity(&new_decl_arena.allocator, @intCast(u32, switch (rhs_ty.tag()) { | |
| 1226 | .error_set_single => 1, | |
| 1227 | .error_set => rhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields.size, | |
| 1228 | else => unreachable, | |
| 1229 | } + switch (lhs_ty.tag()) { | |
| 1230 | .error_set_single => 1, | |
| 1231 | .error_set => lhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields.size, | |
| 1232 | else => unreachable, | |
| 1233 | })); | |
| 1234 | ||
| 1235 | switch (lhs_ty.tag()) { | |
| 1236 | .error_set_single => { | |
| 1237 | const name = lhs_ty.castTag(.error_set_single).?.data; | |
| 1238 | payload.data.fields.putAssumeCapacity(name, {}); | |
| 1239 | }, | |
| 1240 | .error_set => { | |
| 1241 | var multiple = lhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields; | |
| 1242 | var it = multiple.iterator(); | |
| 1243 | while (it.next()) |entry| { | |
| 1244 | payload.data.fields.putAssumeCapacity(entry.key, entry.value); | |
| 1245 | } | |
| 1246 | }, | |
| 1247 | else => unreachable, | |
| 1248 | } | |
| 1249 | ||
| 1250 | switch (rhs_ty.tag()) { | |
| 1251 | .error_set_single => { | |
| 1252 | const name = rhs_ty.castTag(.error_set_single).?.data; | |
| 1253 | payload.data.fields.putAssumeCapacity(name, {}); | |
| 1254 | }, | |
| 1255 | .error_set => { | |
| 1256 | var multiple = rhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields; | |
| 1257 | var it = multiple.iterator(); | |
| 1258 | while (it.next()) |entry| { | |
| 1259 | payload.data.fields.putAssumeCapacity(entry.key, entry.value); | |
| 1260 | } | |
| 1261 | }, | |
| 1262 | else => unreachable, | |
| 1263 | } | |
| 1264 | // TODO create name in format "error:line:column" | |
| 1265 | const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{ | |
| 1266 | .ty = Type.initTag(.type), | |
| 1267 | .val = Value.initPayload(&payload.base), | |
| 1268 | }); | |
| 1269 | payload.data.decl = new_decl; | |
| 1270 | ||
| 1271 | return mod.analyzeDeclVal(scope, inst.base.src, new_decl); | |
| 1189 | 1272 | } |
| 1190 | 1273 | |
| 1191 | 1274 | fn zirEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst { |
| ... | ... | @@ -2075,6 +2158,7 @@ fn zirArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError! |
| 2075 | 2158 | const ir_tag = switch (inst.base.tag) { |
| 2076 | 2159 | .add => Inst.Tag.add, |
| 2077 | 2160 | .sub => Inst.Tag.sub, |
| 2161 | .mul => Inst.Tag.mul, | |
| 2078 | 2162 | else => return mod.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{s}''", .{@tagName(inst.base.tag)}), |
| 2079 | 2163 | }; |
| 2080 | 2164 |
test/run_translated_c.zig+2| ... | ... | @@ -27,6 +27,8 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 27 | 27 | \\#define FOO = |
| 28 | 28 | \\#define PtrToPtr64(p) ((void *POINTER_64) p) |
| 29 | 29 | \\#define STRUC_ALIGNED_STACK_COPY(t,s) ((CONST t *)(s)) |
| 30 | \\#define bar = 0x | |
| 31 | \\#define baz = 0b | |
| 30 | 32 | \\int main(void) {} |
| 31 | 33 | , ""); |
| 32 | 34 |
test/stage1/behavior.zig+1-1| ... | ... | @@ -141,5 +141,5 @@ comptime { |
| 141 | 141 | _ = @import("behavior/while.zig"); |
| 142 | 142 | _ = @import("behavior/widening.zig"); |
| 143 | 143 | _ = @import("behavior/src.zig"); |
| 144 | // _ = @import("behavior/translate_c_macros.zig"); | |
| 144 | _ = @import("behavior/translate_c_macros.zig"); | |
| 145 | 145 | } |
test/stage2/arm.zig+34| ... | ... | @@ -344,4 +344,38 @@ pub fn addCases(ctx: *TestContext) !void { |
| 344 | 344 | "", |
| 345 | 345 | ); |
| 346 | 346 | } |
| 347 | ||
| 348 | { | |
| 349 | var case = ctx.exe("integer multiplication", linux_arm); | |
| 350 | // Simple u32 integer multiplication | |
| 351 | case.addCompareOutput( | |
| 352 | \\export fn _start() noreturn { | |
| 353 | \\ assert(mul(1, 1) == 1); | |
| 354 | \\ assert(mul(42, 1) == 42); | |
| 355 | \\ assert(mul(1, 42) == 42); | |
| 356 | \\ assert(mul(123, 42) == 5166); | |
| 357 | \\ exit(); | |
| 358 | \\} | |
| 359 | \\ | |
| 360 | \\fn mul(x: u32, y: u32) u32 { | |
| 361 | \\ return x * y; | |
| 362 | \\} | |
| 363 | \\ | |
| 364 | \\fn assert(ok: bool) void { | |
| 365 | \\ if (!ok) unreachable; | |
| 366 | \\} | |
| 367 | \\ | |
| 368 | \\fn exit() noreturn { | |
| 369 | \\ asm volatile ("svc #0" | |
| 370 | \\ : | |
| 371 | \\ : [number] "{r7}" (1), | |
| 372 | \\ [arg1] "{r0}" (0) | |
| 373 | \\ : "memory" | |
| 374 | \\ ); | |
| 375 | \\ unreachable; | |
| 376 | \\} | |
| 377 | , | |
| 378 | "", | |
| 379 | ); | |
| 380 | } | |
| 347 | 381 | } |
test/stage2/llvm.zig+68| ... | ... | @@ -132,4 +132,72 @@ pub fn addCases(ctx: *TestContext) !void { |
| 132 | 132 | \\} |
| 133 | 133 | , ""); |
| 134 | 134 | } |
| 135 | ||
| 136 | { | |
| 137 | var case = ctx.exeUsingLlvmBackend("optionals", linux_x64); | |
| 138 | ||
| 139 | case.addCompareOutput( | |
| 140 | \\fn assert(ok: bool) void { | |
| 141 | \\ if (!ok) unreachable; | |
| 142 | \\} | |
| 143 | \\ | |
| 144 | \\export fn main() c_int { | |
| 145 | \\ var opt_val: ?i32 = 10; | |
| 146 | \\ var null_val: ?i32 = null; | |
| 147 | \\ | |
| 148 | \\ var val1: i32 = opt_val.?; | |
| 149 | \\ const val1_1: i32 = opt_val.?; | |
| 150 | \\ var ptr_val1 = &(opt_val.?); | |
| 151 | \\ const ptr_val1_1 = &(opt_val.?); | |
| 152 | \\ | |
| 153 | \\ var val2: i32 = null_val orelse 20; | |
| 154 | \\ const val2_2: i32 = null_val orelse 20; | |
| 155 | \\ | |
| 156 | \\ var value: i32 = 20; | |
| 157 | \\ var ptr_val2 = &(null_val orelse value); | |
| 158 | \\ | |
| 159 | \\ const val3 = opt_val orelse 30; | |
| 160 | \\ var val3_var = opt_val orelse 30; | |
| 161 | \\ | |
| 162 | \\ assert(val1 == 10); | |
| 163 | \\ assert(val1_1 == 10); | |
| 164 | \\ assert(ptr_val1.* == 10); | |
| 165 | \\ assert(ptr_val1_1.* == 10); | |
| 166 | \\ | |
| 167 | \\ assert(val2 == 20); | |
| 168 | \\ assert(val2_2 == 20); | |
| 169 | \\ assert(ptr_val2.* == 20); | |
| 170 | \\ | |
| 171 | \\ assert(val3 == 10); | |
| 172 | \\ assert(val3_var == 10); | |
| 173 | \\ | |
| 174 | \\ (null_val orelse val2) = 1234; | |
| 175 | \\ assert(val2 == 1234); | |
| 176 | \\ | |
| 177 | \\ (opt_val orelse val2) = 5678; | |
| 178 | \\ assert(opt_val.? == 5678); | |
| 179 | \\ | |
| 180 | \\ return 0; | |
| 181 | \\} | |
| 182 | , ""); | |
| 183 | } | |
| 184 | ||
| 185 | { | |
| 186 | var case = ctx.exeUsingLlvmBackend("for loop", linux_x64); | |
| 187 | ||
| 188 | case.addCompareOutput( | |
| 189 | \\fn assert(ok: bool) void { | |
| 190 | \\ if (!ok) unreachable; | |
| 191 | \\} | |
| 192 | \\ | |
| 193 | \\export fn main() c_int { | |
| 194 | \\ var x: u32 = 0; | |
| 195 | \\ for ("hello") |_| { | |
| 196 | \\ x += 1; | |
| 197 | \\ } | |
| 198 | \\ assert("hello".len == x); | |
| 199 | \\ return 0; | |
| 200 | \\} | |
| 201 | , ""); | |
| 202 | } | |
| 135 | 203 | } |
test/stage2/test.zig+34-1| ... | ... | @@ -985,7 +985,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 985 | 985 | "Hello, World!\n", |
| 986 | 986 | ); |
| 987 | 987 | try case.files.append(.{ |
| 988 | .src = | |
| 988 | .src = | |
| 989 | 989 | \\pub fn print() void { |
| 990 | 990 | \\ asm volatile ("syscall" |
| 991 | 991 | \\ : |
| ... | ... | @@ -1525,4 +1525,37 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1525 | 1525 | \\} |
| 1526 | 1526 | , ""); |
| 1527 | 1527 | } |
| 1528 | { | |
| 1529 | var case = ctx.exe("merge error sets", linux_x64); | |
| 1530 | ||
| 1531 | case.addCompareOutput( | |
| 1532 | \\export fn _start() noreturn { | |
| 1533 | \\ const E = error{ A, B, D } || error { A, B, C }; | |
| 1534 | \\ const a = E.A; | |
| 1535 | \\ const b = E.B; | |
| 1536 | \\ const c = E.C; | |
| 1537 | \\ const d = E.D; | |
| 1538 | \\ const E2 = error { X, Y } || @TypeOf(error.Z); | |
| 1539 | \\ const x = E2.X; | |
| 1540 | \\ const y = E2.Y; | |
| 1541 | \\ const z = E2.Z; | |
| 1542 | \\ assert(anyerror || error { Z } == anyerror); | |
| 1543 | \\ exit(); | |
| 1544 | \\} | |
| 1545 | \\fn assert(b: bool) void { | |
| 1546 | \\ if (!b) unreachable; | |
| 1547 | \\} | |
| 1548 | \\fn exit() noreturn { | |
| 1549 | \\ asm volatile ("syscall" | |
| 1550 | \\ : | |
| 1551 | \\ : [number] "{rax}" (231), | |
| 1552 | \\ [arg1] "{rdi}" (0) | |
| 1553 | \\ : "rcx", "r11", "memory" | |
| 1554 | \\ ); | |
| 1555 | \\ unreachable; | |
| 1556 | \\} | |
| 1557 | , | |
| 1558 | "", | |
| 1559 | ); | |
| 1560 | } | |
| 1528 | 1561 | } |
tools/update_clang_options.zig+17-1| ... | ... | @@ -268,6 +268,10 @@ const known_options = [_]KnownOpt{ |
| 268 | 268 | .name = "MD", |
| 269 | 269 | .ident = "dep_file", |
| 270 | 270 | }, |
| 271 | .{ | |
| 272 | .name = "write-dependencies", | |
| 273 | .ident = "dep_file", | |
| 274 | }, | |
| 271 | 275 | .{ |
| 272 | 276 | .name = "MV", |
| 273 | 277 | .ident = "dep_file", |
| ... | ... | @@ -284,18 +288,30 @@ const known_options = [_]KnownOpt{ |
| 284 | 288 | .name = "MG", |
| 285 | 289 | .ident = "dep_file", |
| 286 | 290 | }, |
| 291 | .{ | |
| 292 | .name = "print-missing-file-dependencies", | |
| 293 | .ident = "dep_file", | |
| 294 | }, | |
| 287 | 295 | .{ |
| 288 | 296 | .name = "MJ", |
| 289 | 297 | .ident = "dep_file", |
| 290 | 298 | }, |
| 291 | 299 | .{ |
| 292 | 300 | .name = "MM", |
| 293 | .ident = "dep_file", | |
| 301 | .ident = "dep_file_mm", | |
| 302 | }, | |
| 303 | .{ | |
| 304 | .name = "user-dependencies", | |
| 305 | .ident = "dep_file_mm", | |
| 294 | 306 | }, |
| 295 | 307 | .{ |
| 296 | 308 | .name = "MMD", |
| 297 | 309 | .ident = "dep_file", |
| 298 | 310 | }, |
| 311 | .{ | |
| 312 | .name = "write-user-dependencies", | |
| 313 | .ident = "dep_file", | |
| 314 | }, | |
| 299 | 315 | .{ |
| 300 | 316 | .name = "MP", |
| 301 | 317 | .ident = "dep_file", |