| 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,31 +801,32 @@ endif() |
| 801 | 801 | ||
| 802 | install(TARGETS zig DESTINATION bin) | 802 | install(TARGETS zig DESTINATION bin) |
| 803 | 803 | ||
| 804 | set(ZIG_INSTALL_ARGS "build" | 804 | set(ZIG_SKIP_INSTALL_LIB_FILES off CACHE BOOL |
| 805 | --override-lib-dir "${CMAKE_SOURCE_DIR}/lib" | 805 | "Disable copying lib/ files to install prefix during the build phase") |
| 806 | "-Dlib-files-only" | 806 | |
| 807 | --prefix "${CMAKE_INSTALL_PREFIX}" | 807 | if(NOT ZIG_SKIP_INSTALL_LIB_FILES) |
| 808 | "-Dconfig_h=${ZIG_CONFIG_H_OUT}" | 808 | set(ZIG_INSTALL_ARGS "build" |
| 809 | install | 809 | --override-lib-dir "${CMAKE_SOURCE_DIR}/lib" |
| 810 | ) | 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 | 816 | # CODE has no effect with Visual Studio build system generator, therefore |
| 813 | # when using Visual Studio build system generator we resort to running | 817 | # when using Visual Studio build system generator we resort to running |
| 814 | # `zig build install` during the build phase. | 818 | # `zig build install` during the build phase. |
| 815 | if(MSVC) | 819 | 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) | ||
| 819 | add_custom_target(zig_install_lib_files ALL | 820 | add_custom_target(zig_install_lib_files ALL |
| 820 | COMMAND zig ${ZIG_INSTALL_ARGS} | 821 | COMMAND zig ${ZIG_INSTALL_ARGS} |
| 821 | DEPENDS zig | 822 | DEPENDS zig |
| 822 | WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" | 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 | endif() | 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 | endif() | 832 | endif() |
doc/langref.html.in+51-23| ... | @@ -10447,13 +10447,40 @@ fn readU32Be() u32 {} | ... | @@ -10447,13 +10447,40 @@ fn readU32Be() u32 {} |
| 10447 | {#header_close#} | 10447 | {#header_close#} |
| 10448 | {#header_open|Source Encoding#} | 10448 | {#header_open|Source Encoding#} |
| 10449 | <p>Zig source code is encoded in UTF-8. An invalid UTF-8 byte sequence results in a compile error.</p> | 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 | <ul> | 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 | <li>Non-Ascii Unicode line endings: U+0085 (NEL), U+2028 (LS), U+2029 (PS).</li> | 10453 | <li>Non-Ascii Unicode line endings: U+0085 (NEL), U+2028 (LS), U+2029 (PS).</li> |
| 10454 | </ul> | 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> | 10455 | <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> | 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 | {#header_close#} | 10484 | {#header_close#} |
| 10458 | 10485 | ||
| 10459 | {#header_open|Keyword Reference#} | 10486 | {#header_open|Keyword Reference#} |
| ... | @@ -11373,6 +11400,7 @@ ExprList &lt;- (Expr COMMA)* Expr? | ... | @@ -11373,6 +11400,7 @@ ExprList &lt;- (Expr COMMA)* Expr? |
| 11373 | 11400 | ||
| 11374 | # *** Tokens *** | 11401 | # *** Tokens *** |
| 11375 | eof &lt;- !. | 11402 | eof &lt;- !. |
| 11403 | eol &lt;- ('\r'? '\n') | eof | ||
| 11376 | hex &lt;- [0-9a-fA-F] | 11404 | hex &lt;- [0-9a-fA-F] |
| 11377 | hex_ &lt;- ('_'/hex) | 11405 | hex_ &lt;- ('_'/hex) |
| 11378 | dec &lt;- [0-9] | 11406 | dec &lt;- [0-9] |
| ... | @@ -11382,39 +11410,39 @@ dec_int &lt;- dec (dec_* dec)? | ... | @@ -11382,39 +11410,39 @@ dec_int &lt;- dec (dec_* dec)? |
| 11382 | hex_int &lt;- hex (hex_* dec)? | 11410 | hex_int &lt;- hex (hex_* dec)? |
| 11383 | 11411 | ||
| 11384 | char_escape | 11412 | char_escape |
| 11385 | &lt;- &quot;\\x&quot; hex hex | 11413 | &lt;- '\\x' hex hex |
| 11386 | / &quot;\\u{&quot; hex+ &quot;}&quot; | 11414 | / '\\u{' hex+ '}' |
| 11387 | / &quot;\\&quot; [nr\\t'&quot;] | 11415 | / '\\' [nr\\t'&quot;] |
| 11388 | char_char | 11416 | char_char |
| 11389 | &lt;- char_escape | 11417 | &lt;- char_escape |
| 11390 | / [^\\'\n] | 11418 | / [^\\'\r\n] |
| 11391 | string_char | 11419 | string_char |
| 11392 | &lt;- char_escape | 11420 | &lt;- char_escape |
| 11393 | / [^\\&quot;\n] | 11421 | / [^\\&quot;\r\n] |
| 11394 | 11422 | ||
| 11395 | line_comment &lt;- '//'[^\n]* | 11423 | line_comment &lt;- '//'[^\r\n]* eol |
| 11396 | line_string &lt;- (&quot;\\\\&quot; [^\n]* [ \n]*)+ | 11424 | line_string &lt;- ('\\\\' [^\r\n]* eol skip)+ |
| 11397 | skip &lt;- ([ \n] / line_comment)* | 11425 | skip &lt;- ([ \t] / eol / line_comment)* |
| 11398 | 11426 | ||
| 11399 | CHAR_LITERAL &lt;- &quot;'&quot; char_char &quot;'&quot; skip | 11427 | CHAR_LITERAL &lt;- &quot;'&quot; char_char &quot;'&quot; skip |
| 11400 | FLOAT | 11428 | FLOAT |
| 11401 | &lt;- &quot;0x&quot; hex_* hex &quot;.&quot; hex_int ([pP] [-+]? hex_int)? skip | 11429 | &lt;- '0x' hex_* hex '.' hex_int ([pP] [-+]? hex_int)? skip |
| 11402 | / dec_int &quot;.&quot; dec_int ([eE] [-+]? dec_int)? skip | 11430 | / dec_int '.' dec_int ([eE] [-+]? dec_int)? skip |
| 11403 | / &quot;0x&quot; hex_* hex &quot;.&quot;? [pP] [-+]? hex_int skip | 11431 | / '0x' hex_* hex '.'? [pP] [-+]? hex_int skip |
| 11404 | / dec_int &quot;.&quot;? [eE] [-+]? dec_int skip | 11432 | / dec_int '.'? [eE] [-+]? dec_int skip |
| 11405 | INTEGER | 11433 | INTEGER |
| 11406 | &lt;- &quot;0b&quot; [_01]* [01] skip | 11434 | &lt;- '0b' [_01]* [01] skip |
| 11407 | / &quot;0o&quot; [_0-7]* [0-7] skip | 11435 | / '0o' [_0-7]* [0-7] skip |
| 11408 | / &quot;0x&quot; hex_* hex skip | 11436 | / '0x' hex_* hex skip |
| 11409 | / dec_int skip | 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 | STRINGLITERAL | 11439 | STRINGLITERAL |
| 11412 | &lt;- STRINGLITERALSINGLE | 11440 | &lt;- STRINGLITERALSINGLE |
| 11413 | / line_string skip | 11441 | / line_string skip |
| 11414 | IDENTIFIER | 11442 | IDENTIFIER |
| 11415 | &lt;- !keyword [A-Za-z_] [A-Za-z0-9_]* skip | 11443 | &lt;- !keyword [A-Za-z_] [A-Za-z0-9_]* skip |
| 11416 | / &quot;@\&quot;&quot; string_char* &quot;\&quot;&quot; skip | 11444 | / '@&quot;' string_char* '&quot;' skip |
| 11417 | BUILTINIDENTIFIER &lt;- &quot;@&quot;[A-Za-z_][A-Za-z0-9_]* skip | 11445 | BUILTINIDENTIFIER &lt;- '@'[A-Za-z_][A-Za-z0-9_]* skip |
| 11418 | 11446 | ||
| 11419 | 11447 | ||
| 11420 | AMPERSAND &lt;- '&amp;' ![=] skip | 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,9 +51,9 @@ const BinaryElfOutput = struct { |
| 51 | .segments = ArrayList(*BinaryElfSegment).init(allocator), | 51 | .segments = ArrayList(*BinaryElfSegment).init(allocator), |
| 52 | .sections = ArrayList(*BinaryElfSection).init(allocator), | 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 | while (try section_headers.next()) |section| { | 57 | while (try section_headers.next()) |section| { |
| 58 | if (sectionValidForOutput(section)) { | 58 | if (sectionValidForOutput(section)) { |
| 59 | const newSection = try allocator.create(BinaryElfSection); | 59 | const newSection = try allocator.create(BinaryElfSection); |
| ... | @@ -67,7 +67,7 @@ const BinaryElfOutput = struct { | ... | @@ -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 | while (try program_headers.next()) |phdr| { | 71 | while (try program_headers.next()) |phdr| { |
| 72 | if (phdr.p_type == elf.PT_LOAD) { | 72 | if (phdr.p_type == elf.PT_LOAD) { |
| 73 | const newSegment = try allocator.create(BinaryElfSegment); | 73 | const newSegment = try allocator.create(BinaryElfSegment); |
lib/std/c/tokenizer.zig+30-2| ... | @@ -401,7 +401,9 @@ pub const Tokenizer = struct { | ... | @@ -401,7 +401,9 @@ pub const Tokenizer = struct { |
| 401 | Zero, | 401 | Zero, |
| 402 | IntegerLiteralOct, | 402 | IntegerLiteralOct, |
| 403 | IntegerLiteralBinary, | 403 | IntegerLiteralBinary, |
| 404 | IntegerLiteralBinaryFirst, | ||
| 404 | IntegerLiteralHex, | 405 | IntegerLiteralHex, |
| 406 | IntegerLiteralHexFirst, | ||
| 405 | IntegerLiteral, | 407 | IntegerLiteral, |
| 406 | IntegerSuffix, | 408 | IntegerSuffix, |
| 407 | IntegerSuffixU, | 409 | IntegerSuffixU, |
| ... | @@ -1046,10 +1048,10 @@ pub const Tokenizer = struct { | ... | @@ -1046,10 +1048,10 @@ pub const Tokenizer = struct { |
| 1046 | state = .IntegerLiteralOct; | 1048 | state = .IntegerLiteralOct; |
| 1047 | }, | 1049 | }, |
| 1048 | 'b', 'B' => { | 1050 | 'b', 'B' => { |
| 1049 | state = .IntegerLiteralBinary; | 1051 | state = .IntegerLiteralBinaryFirst; |
| 1050 | }, | 1052 | }, |
| 1051 | 'x', 'X' => { | 1053 | 'x', 'X' => { |
| 1052 | state = .IntegerLiteralHex; | 1054 | state = .IntegerLiteralHexFirst; |
| 1053 | }, | 1055 | }, |
| 1054 | '.' => { | 1056 | '.' => { |
| 1055 | state = .FloatFraction; | 1057 | state = .FloatFraction; |
| ... | @@ -1066,6 +1068,13 @@ pub const Tokenizer = struct { | ... | @@ -1066,6 +1068,13 @@ pub const Tokenizer = struct { |
| 1066 | self.index -= 1; | 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 | .IntegerLiteralBinary => switch (c) { | 1078 | .IntegerLiteralBinary => switch (c) { |
| 1070 | '0', '1' => {}, | 1079 | '0', '1' => {}, |
| 1071 | else => { | 1080 | else => { |
| ... | @@ -1073,6 +1082,19 @@ pub const Tokenizer = struct { | ... | @@ -1073,6 +1082,19 @@ pub const Tokenizer = struct { |
| 1073 | self.index -= 1; | 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 | .IntegerLiteralHex => switch (c) { | 1098 | .IntegerLiteralHex => switch (c) { |
| 1077 | '0'...'9', 'a'...'f', 'A'...'F' => {}, | 1099 | '0'...'9', 'a'...'f', 'A'...'F' => {}, |
| 1078 | '.' => { | 1100 | '.' => { |
| ... | @@ -1238,6 +1260,8 @@ pub const Tokenizer = struct { | ... | @@ -1238,6 +1260,8 @@ pub const Tokenizer = struct { |
| 1238 | .MultiLineCommentAsterisk, | 1260 | .MultiLineCommentAsterisk, |
| 1239 | .FloatExponent, | 1261 | .FloatExponent, |
| 1240 | .MacroString, | 1262 | .MacroString, |
| 1263 | .IntegerLiteralBinaryFirst, | ||
| 1264 | .IntegerLiteralHexFirst, | ||
| 1241 | => result.id = .Invalid, | 1265 | => result.id = .Invalid, |
| 1242 | 1266 | ||
| 1243 | .FloatExponentDigits => result.id = if (counter == 0) .Invalid else .{ .FloatLiteral = .none }, | 1267 | .FloatExponentDigits => result.id = if (counter == 0) .Invalid else .{ .FloatLiteral = .none }, |
| ... | @@ -1523,6 +1547,7 @@ test "num suffixes" { | ... | @@ -1523,6 +1547,7 @@ test "num suffixes" { |
| 1523 | \\ 1.0f 1.0L 1.0 .0 1. | 1547 | \\ 1.0f 1.0L 1.0 .0 1. |
| 1524 | \\ 0l 0lu 0ll 0llu 0 | 1548 | \\ 0l 0lu 0ll 0llu 0 |
| 1525 | \\ 1u 1ul 1ull 1 | 1549 | \\ 1u 1ul 1ull 1 |
| 1550 | \\ 0x 0b | ||
| 1526 | \\ | 1551 | \\ |
| 1527 | , &[_]Token.Id{ | 1552 | , &[_]Token.Id{ |
| 1528 | .{ .FloatLiteral = .f }, | 1553 | .{ .FloatLiteral = .f }, |
| ... | @@ -1542,6 +1567,9 @@ test "num suffixes" { | ... | @@ -1542,6 +1567,9 @@ test "num suffixes" { |
| 1542 | .{ .IntegerLiteral = .llu }, | 1567 | .{ .IntegerLiteral = .llu }, |
| 1543 | .{ .IntegerLiteral = .none }, | 1568 | .{ .IntegerLiteral = .none }, |
| 1544 | .Nl, | 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,9 +115,9 @@ test "curve25519" { |
| 115 | const p = try Curve25519.basePoint.clampedMul(s); | 115 | const p = try Curve25519.basePoint.clampedMul(s); |
| 116 | try p.rejectIdentity(); | 116 | try p.rejectIdentity(); |
| 117 | var buf: [128]u8 = undefined; | 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 | const q = try p.clampedMul(s); | 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 | try Curve25519.rejectNonCanonical(s); | 122 | try Curve25519.rejectNonCanonical(s); |
| 123 | s[31] |= 0x80; | 123 | s[31] |= 0x80; |
lib/std/crypto/25519/ed25519.zig+3-3| ... | @@ -210,8 +210,8 @@ test "ed25519 key pair creation" { | ... | @@ -210,8 +210,8 @@ test "ed25519 key pair creation" { |
| 210 | _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); | 210 | _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); |
| 211 | const key_pair = try Ed25519.KeyPair.create(seed); | 211 | const key_pair = try Ed25519.KeyPair.create(seed); |
| 212 | var buf: [256]u8 = undefined; | 212 | var buf: [256]u8 = undefined; |
| 213 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair.secret_key}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); | 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, "{X}", .{key_pair.public_key}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); | 214 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&key_pair.public_key)}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | test "ed25519 signature" { | 217 | test "ed25519 signature" { |
| ... | @@ -221,7 +221,7 @@ test "ed25519 signature" { | ... | @@ -221,7 +221,7 @@ test "ed25519 signature" { |
| 221 | 221 | ||
| 222 | const sig = try Ed25519.sign("test", key_pair, null); | 222 | const sig = try Ed25519.sign("test", key_pair, null); |
| 223 | var buf: [128]u8 = undefined; | 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 | try Ed25519.verify(sig, "test", key_pair.public_key); | 225 | try Ed25519.verify(sig, "test", key_pair.public_key); |
| 226 | std.testing.expectError(error.InvalidSignature, Ed25519.verify(sig, "TEST", key_pair.public_key)); | 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,7 +450,7 @@ test "edwards25519 packing/unpacking" { |
| 450 | var b = Edwards25519.basePoint; | 450 | var b = Edwards25519.basePoint; |
| 451 | const pk = try b.mul(s); | 451 | const pk = try b.mul(s); |
| 452 | var buf: [128]u8 = undefined; | 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 | const small_order_ss: [7][32]u8 = .{ | 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,21 +170,21 @@ pub const Ristretto255 = struct { |
| 170 | test "ristretto255" { | 170 | test "ristretto255" { |
| 171 | const p = Ristretto255.basePoint; | 171 | const p = Ristretto255.basePoint; |
| 172 | var buf: [256]u8 = undefined; | 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 | var r: [Ristretto255.encoded_length]u8 = undefined; | 175 | var r: [Ristretto255.encoded_length]u8 = undefined; |
| 176 | _ = try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"); | 176 | _ = try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"); |
| 177 | var q = try Ristretto255.fromBytes(r); | 177 | var q = try Ristretto255.fromBytes(r); |
| 178 | q = q.dbl().add(p); | 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 | const s = [_]u8{15} ++ [_]u8{0} ** 31; | 181 | const s = [_]u8{15} ++ [_]u8{0} ** 31; |
| 182 | const w = try p.mul(s); | 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 | std.testing.expect(p.dbl().dbl().dbl().dbl().equivalent(w.add(p))); | 185 | std.testing.expect(p.dbl().dbl().dbl().dbl().equivalent(w.add(p))); |
| 186 | 186 | ||
| 187 | const h = [_]u8{69} ** 32 ++ [_]u8{42} ** 32; | 187 | const h = [_]u8{69} ** 32 ++ [_]u8{42} ** 32; |
| 188 | const ph = Ristretto255.fromUniform(h); | 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,10 +771,10 @@ test "scalar25519" { |
| 771 | var y = x.toBytes(); | 771 | var y = x.toBytes(); |
| 772 | try rejectNonCanonical(y); | 772 | try rejectNonCanonical(y); |
| 773 | var buf: [128]u8 = undefined; | 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 | const reduced = reduce(field_size); | 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 | test "non-canonical scalar25519" { | 780 | test "non-canonical scalar25519" { |
| ... | @@ -788,5 +788,5 @@ test "mulAdd overflow check" { | ... | @@ -788,5 +788,5 @@ test "mulAdd overflow check" { |
| 788 | const c: [32]u8 = [_]u8{0xff} ** 32; | 788 | const c: [32]u8 = [_]u8{0xff} ** 32; |
| 789 | const x = mulAdd(a, b, c); | 789 | const x = mulAdd(a, b, c); |
| 790 | var buf: [128]u8 = undefined; | 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,7 +876,7 @@ test "crypto.xchacha20" { |
| 876 | var ciphertext: [input.len]u8 = undefined; | 876 | var ciphertext: [input.len]u8 = undefined; |
| 877 | XChaCha20IETF.xor(ciphertext[0..], input[0..], 0, key, nonce); | 877 | XChaCha20IETF.xor(ciphertext[0..], input[0..], 0, key, nonce); |
| 878 | var buf: [2 * ciphertext.len]u8 = undefined; | 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 | const data = "Additional data"; | 882 | const data = "Additional data"; |
| ... | @@ -885,7 +885,7 @@ test "crypto.xchacha20" { | ... | @@ -885,7 +885,7 @@ test "crypto.xchacha20" { |
| 885 | var out: [input.len]u8 = undefined; | 885 | var out: [input.len]u8 = undefined; |
| 886 | try xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce); | 886 | try xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce); |
| 887 | var buf: [2 * ciphertext.len]u8 = undefined; | 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 | testing.expectEqualSlices(u8, out[0..], input); | 889 | testing.expectEqualSlices(u8, out[0..], input); |
| 890 | ciphertext[0] += 1; | 890 | ciphertext[0] += 1; |
| 891 | testing.expectError(error.AuthenticationFailed, xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce)); | 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,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 | const fp_offset = if (builtin.arch.isRISCV()) | 364 | const fp_offset = if (builtin.arch.isRISCV()) |
| 365 | // On RISC-V the frame pointer points to the top of the saved register | 365 | // On RISC-V the frame pointer points to the top of the saved register |
| 366 | // area, on pretty much every other architecture it points to the stack | 366 | // area, on pretty much every other architecture it points to the stack |
| 367 | // slot where the previous frame pointer is saved. | 367 | // slot where the previous frame pointer is saved. |
| 368 | 2 * @sizeOf(usize) | 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 | else | 372 | else |
| 370 | 0; | 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 | // Positive offset of the saved PC wrt the frame pointer. | 381 | // Positive offset of the saved PC wrt the frame pointer. |
| 372 | const pc_offset = if (builtin.arch == .powerpc64le) | 382 | const pc_offset = if (builtin.arch == .powerpc64le) |
| 373 | 2 * @sizeOf(usize) | 383 | 2 * @sizeOf(usize) |
| ... | @@ -388,13 +398,17 @@ pub const StackIterator = struct { | ... | @@ -388,13 +398,17 @@ pub const StackIterator = struct { |
| 388 | } | 398 | } |
| 389 | 399 | ||
| 390 | fn next_internal(self: *StackIterator) ?usize { | 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 | // Sanity check. | 407 | // Sanity check. |
| 394 | if (fp == 0 or !mem.isAligned(fp, @alignOf(usize))) | 408 | if (fp == 0 or !mem.isAligned(fp, @alignOf(usize))) |
| 395 | return null; | 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 | // Sanity check: the stack grows down thus all the parent frames must be | 413 | // Sanity check: the stack grows down thus all the parent frames must be |
| 400 | // be at addresses that are greater (or equal) than the previous one. | 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,7 +335,7 @@ pub const ET = extern enum(u16) { |
| 335 | }; | 335 | }; |
| 336 | 336 | ||
| 337 | /// All integers are native endian. | 337 | /// All integers are native endian. |
| 338 | const Header = struct { | 338 | pub const Header = struct { |
| 339 | endian: builtin.Endian, | 339 | endian: builtin.Endian, |
| 340 | is_64: bool, | 340 | is_64: bool, |
| 341 | entry: u64, | 341 | entry: u64, |
| ... | @@ -347,187 +347,200 @@ const Header = struct { | ... | @@ -347,187 +347,200 @@ const Header = struct { |
| 347 | shnum: u16, | 347 | shnum: u16, |
| 348 | shstrndx: u16, | 348 | shstrndx: u16, |
| 349 | 349 | ||
| 350 | pub fn program_header_iterator(self: Header, file: File) ProgramHeaderIterator { | 350 | pub fn program_header_iterator(self: Header, parse_source: anytype) ProgramHeaderIterator(@TypeOf(parse_source)) { |
| 351 | return .{ | 351 | return ProgramHeaderIterator(@TypeOf(parse_source)){ |
| 352 | .elf_header = self, | 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 { | 357 | pub fn section_header_iterator(self: Header, parse_source: anytype) SectionHeaderIterator(@TypeOf(parse_source)) { |
| 358 | return .{ | 358 | return SectionHeaderIterator(@TypeOf(parse_source)){ |
| 359 | .elf_header = self, | 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 { | 364 | pub fn read(parse_source: anytype) !Header { |
| 366 | var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined; | 365 | var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined; |
| 367 | try preadNoEof(file, &hdr_buf, 0); | 366 | try parse_source.seekableStream().seekTo(0); |
| 368 | const hdr32 = @ptrCast(*Elf32_Ehdr, &hdr_buf); | 367 | try parse_source.reader().readNoEof(&hdr_buf); |
| 369 | const hdr64 = @ptrCast(*Elf64_Ehdr, &hdr_buf); | 368 | return Header.parse(&hdr_buf); |
| 370 | if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; | 369 | } |
| 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; | ||
| 379 | 370 | ||
| 380 | const is_64 = switch (hdr32.e_ident[EI_CLASS]) { | 371 | pub fn parse(hdr_buf: *align(@alignOf(Elf64_Ehdr)) const [@sizeOf(Elf64_Ehdr)]u8) !Header { |
| 381 | ELFCLASS32 => false, | 372 | const hdr32 = @ptrCast(*const Elf32_Ehdr, hdr_buf); |
| 382 | ELFCLASS64 => true, | 373 | const hdr64 = @ptrCast(*const Elf64_Ehdr, hdr_buf); |
| 383 | else => return error.InvalidElfClass, | 374 | if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; |
| 384 | }; | 375 | if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion; |
| 385 | 376 | ||
| 386 | return @as(Header, .{ | 377 | const endian: std.builtin.Endian = switch (hdr32.e_ident[EI_DATA]) { |
| 387 | .endian = endian, | 378 | ELFDATA2LSB => .Little, |
| 388 | .is_64 = is_64, | 379 | ELFDATA2MSB => .Big, |
| 389 | .entry = int(is_64, need_bswap, hdr32.e_entry, hdr64.e_entry), | 380 | else => return error.InvalidElfEndian, |
| 390 | .phoff = int(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff), | 381 | }; |
| 391 | .shoff = int(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff), | 382 | const need_bswap = endian != std.builtin.endian; |
| 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 | } | ||
| 399 | 383 | ||
| 400 | pub const ProgramHeaderIterator = struct { | 384 | const is_64 = switch (hdr32.e_ident[EI_CLASS]) { |
| 401 | elf_header: Header, | 385 | ELFCLASS32 => false, |
| 402 | file: File, | 386 | ELFCLASS64 => true, |
| 403 | index: usize = 0, | 387 | else => return error.InvalidElfClass, |
| 388 | }; | ||
| 404 | 389 | ||
| 405 | pub fn next(self: *ProgramHeaderIterator) !?Elf64_Phdr { | 390 | return @as(Header, .{ |
| 406 | if (self.index >= self.elf_header.phnum) return null; | 391 | .endian = endian, |
| 407 | defer self.index += 1; | 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) { | 405 | pub fn ProgramHeaderIterator(ParseSource: anytype) type { |
| 410 | var phdr: Elf64_Phdr = undefined; | 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 | const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index; | 438 | const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index; |
| 412 | try preadNoEof(self.file, mem.asBytes(&phdr), offset); | 439 | try self.parse_source.seekableStream().seekTo(offset); |
| 413 | 440 | try self.parse_source.reader().readNoEof(mem.asBytes(&phdr)); | |
| 414 | // ELF endianness matches native endianness. | 441 | |
| 415 | if (self.elf_header.endian == std.builtin.endian) return phdr; | 442 | // ELF endianness does NOT match native endianness. |
| 416 | 443 | if (self.elf_header.endian != std.builtin.endian) { | |
| 417 | // Convert fields to native endianness. | 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 | return Elf64_Phdr{ | 458 | return Elf64_Phdr{ |
| 419 | .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type), | 459 | .p_type = phdr.p_type, |
| 420 | .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset), | 460 | .p_offset = phdr.p_offset, |
| 421 | .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr), | 461 | .p_vaddr = phdr.p_vaddr, |
| 422 | .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr), | 462 | .p_paddr = phdr.p_paddr, |
| 423 | .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz), | 463 | .p_filesz = phdr.p_filesz, |
| 424 | .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz), | 464 | .p_memsz = phdr.p_memsz, |
| 425 | .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags), | 465 | .p_flags = phdr.p_flags, |
| 426 | .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align), | 466 | .p_align = phdr.p_align, |
| 427 | }; | 467 | }; |
| 428 | } | 468 | } |
| 469 | }; | ||
| 470 | } | ||
| 429 | 471 | ||
| 430 | var phdr: Elf32_Phdr = undefined; | 472 | pub fn SectionHeaderIterator(ParseSource: anytype) type { |
| 431 | const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index; | 473 | return struct { |
| 432 | try preadNoEof(self.file, mem.asBytes(&phdr), offset); | 474 | elf_header: Header, |
| 433 | 475 | parse_source: ParseSource, | |
| 434 | // ELF endianness does NOT match native endianness. | 476 | index: usize = 0, |
| 435 | if (self.elf_header.endian != std.builtin.endian) { | 477 | |
| 436 | // Convert fields to native endianness. | 478 | pub fn next(self: *@This()) !?Elf64_Shdr { |
| 437 | phdr = .{ | 479 | if (self.index >= self.elf_header.shnum) return null; |
| 438 | .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type), | 480 | defer self.index += 1; |
| 439 | .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset), | 481 | |
| 440 | .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr), | 482 | if (self.elf_header.is_64) { |
| 441 | .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr), | 483 | var shdr: Elf64_Shdr = undefined; |
| 442 | .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz), | 484 | const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index; |
| 443 | .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz), | 485 | try self.parse_source.seekableStream().seekTo(offset); |
| 444 | .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags), | 486 | try self.parse_source.reader().readNoEof(mem.asBytes(&shdr)); |
| 445 | .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align), | 487 | |
| 446 | }; | 488 | // ELF endianness matches native endianness. |
| 447 | } | 489 | if (self.elf_header.endian == std.builtin.endian) return shdr; |
| 448 | 490 | ||
| 449 | // Convert 32-bit header to 64-bit. | 491 | // Convert fields to native endianness. |
| 450 | return Elf64_Phdr{ | 492 | return Elf64_Shdr{ |
| 451 | .p_type = phdr.p_type, | 493 | .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name), |
| 452 | .p_offset = phdr.p_offset, | 494 | .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type), |
| 453 | .p_vaddr = phdr.p_vaddr, | 495 | .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags), |
| 454 | .p_paddr = phdr.p_paddr, | 496 | .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr), |
| 455 | .p_filesz = phdr.p_filesz, | 497 | .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset), |
| 456 | .p_memsz = phdr.p_memsz, | 498 | .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size), |
| 457 | .p_flags = phdr.p_flags, | 499 | .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link), |
| 458 | .p_align = phdr.p_align, | 500 | .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info), |
| 459 | }; | 501 | .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign), |
| 460 | } | 502 | .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize), |
| 461 | }; | 503 | }; |
| 462 | 504 | } | |
| 463 | pub const SectionHeaderIterator = struct { | 505 | |
| 464 | elf_header: Header, | 506 | var shdr: Elf32_Shdr = undefined; |
| 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; | ||
| 474 | const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index; | 507 | const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index; |
| 475 | try preadNoEof(self.file, mem.asBytes(&shdr), offset); | 508 | try self.parse_source.seekableStream().seekTo(offset); |
| 476 | 509 | try self.parse_source.reader().readNoEof(mem.asBytes(&shdr)); | |
| 477 | // ELF endianness matches native endianness. | 510 | |
| 478 | if (self.elf_header.endian == std.builtin.endian) return shdr; | 511 | // ELF endianness does NOT match native endianness. |
| 479 | 512 | if (self.elf_header.endian != std.builtin.endian) { | |
| 480 | // Convert fields to native endianness. | 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 | return Elf64_Shdr{ | 529 | return Elf64_Shdr{ |
| 482 | .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name), | 530 | .sh_name = shdr.sh_name, |
| 483 | .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type), | 531 | .sh_type = shdr.sh_type, |
| 484 | .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags), | 532 | .sh_flags = shdr.sh_flags, |
| 485 | .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr), | 533 | .sh_addr = shdr.sh_addr, |
| 486 | .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset), | 534 | .sh_offset = shdr.sh_offset, |
| 487 | .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size), | 535 | .sh_size = shdr.sh_size, |
| 488 | .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link), | 536 | .sh_link = shdr.sh_link, |
| 489 | .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info), | 537 | .sh_info = shdr.sh_info, |
| 490 | .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign), | 538 | .sh_addralign = shdr.sh_addralign, |
| 491 | .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize), | 539 | .sh_entsize = shdr.sh_entsize, |
| 492 | }; | 540 | }; |
| 493 | } | 541 | } |
| 494 | 542 | }; | |
| 495 | var shdr: Elf32_Shdr = undefined; | 543 | } |
| 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 | }; | ||
| 531 | 544 | ||
| 532 | pub fn int(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) { | 545 | pub fn int(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) { |
| 533 | if (is_64) { | 546 | if (is_64) { |
| ... | @@ -549,28 +562,6 @@ pub fn int32(need_bswap: bool, int_32: anytype, comptime Int64: anytype) Int64 { | ... | @@ -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 | pub const EI_NIDENT = 16; | 565 | pub const EI_NIDENT = 16; |
| 575 | 566 | ||
| 576 | pub const EI_CLASS = 4; | 567 | pub const EI_CLASS = 4; |
lib/std/fmt.zig+104-26| ... | @@ -709,6 +709,87 @@ fn formatFloatValue( | ... | @@ -709,6 +709,87 @@ fn formatFloatValue( |
| 709 | return formatBuf(buf_stream.getWritten(), options, writer); | 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 | pub fn formatText( | 793 | pub fn formatText( |
| 713 | bytes: []const u8, | 794 | bytes: []const u8, |
| 714 | comptime fmt: []const u8, | 795 | comptime fmt: []const u8, |
| ... | @@ -717,21 +798,18 @@ pub fn formatText( | ... | @@ -717,21 +798,18 @@ pub fn formatText( |
| 717 | ) !void { | 798 | ) !void { |
| 718 | if (comptime std.mem.eql(u8, fmt, "s")) { | 799 | if (comptime std.mem.eql(u8, fmt, "s")) { |
| 719 | return formatBuf(bytes, options, writer); | 800 | return formatBuf(bytes, options, writer); |
| 720 | } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) { | 801 | } else if (comptime (std.mem.eql(u8, fmt, "x"))) { |
| 721 | for (bytes) |c| { | 802 | @compileError("specifier 'x' has been deprecated, wrap your argument in std.fmt.fmtSliceHexLower instead"); |
| 722 | try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, writer); | 803 | } else if (comptime (std.mem.eql(u8, fmt, "X"))) { |
| 723 | } | 804 | @compileError("specifier 'X' has been deprecated, wrap your argument in std.fmt.fmtSliceHexUpper instead"); |
| 724 | return; | 805 | } else if (comptime (std.mem.eql(u8, fmt, "e"))) { |
| 725 | } else if (comptime (std.mem.eql(u8, fmt, "e") or std.mem.eql(u8, fmt, "E"))) { | 806 | @compileError("specifier 'e' has been deprecated, wrap your argument in std.fmt.fmtSliceEscapeLower instead"); |
| 726 | for (bytes) |c| { | 807 | } else if (comptime (std.mem.eql(u8, fmt, "E"))) { |
| 727 | if (std.ascii.isPrint(c)) { | 808 | @compileError("specifier 'X' has been deprecated, wrap your argument in std.fmt.fmtSliceEscapeUpper instead"); |
| 728 | try writer.writeByte(c); | 809 | } else if (comptime std.mem.eql(u8, fmt, "z")) { |
| 729 | } else { | 810 | @compileError("specifier 'z' has been deprecated, wrap your argument in std.zig.fmtId instead"); |
| 730 | try writer.writeAll("\\x"); | 811 | } else if (comptime std.mem.eql(u8, fmt, "Z")) { |
| 731 | try formatInt(c, 16, fmt[0] == 'E', FormatOptions{ .width = 2, .fill = '0' }, writer); | 812 | @compileError("specifier 'Z' has been deprecated, wrap your argument in std.zig.fmtEscapes instead"); |
| 732 | } | ||
| 733 | } | ||
| 734 | return; | ||
| 735 | } else { | 813 | } else { |
| 736 | @compileError("Unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'"); | 814 | @compileError("Unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'"); |
| 737 | } | 815 | } |
| ... | @@ -1693,9 +1771,9 @@ test "slice" { | ... | @@ -1693,9 +1771,9 @@ test "slice" { |
| 1693 | } | 1771 | } |
| 1694 | 1772 | ||
| 1695 | test "escape non-printable" { | 1773 | test "escape non-printable" { |
| 1696 | try expectFmt("abc", "{e}", .{"abc"}); | 1774 | try expectFmt("abc", "{s}", .{fmtSliceEscapeLower("abc")}); |
| 1697 | try expectFmt("ab\\xffc", "{e}", .{"ab\xffc"}); | 1775 | try expectFmt("ab\\xffc", "{s}", .{fmtSliceEscapeLower("ab\xffc")}); |
| 1698 | try expectFmt("ab\\xFFc", "{E}", .{"ab\xffc"}); | 1776 | try expectFmt("ab\\xFFc", "{s}", .{fmtSliceEscapeUpper("ab\xffc")}); |
| 1699 | } | 1777 | } |
| 1700 | 1778 | ||
| 1701 | test "pointer" { | 1779 | test "pointer" { |
| ... | @@ -1968,13 +2046,13 @@ test "struct.zero-size" { | ... | @@ -1968,13 +2046,13 @@ test "struct.zero-size" { |
| 1968 | 2046 | ||
| 1969 | test "bytes.hex" { | 2047 | test "bytes.hex" { |
| 1970 | const some_bytes = "\xCA\xFE\xBA\xBE"; | 2048 | const some_bytes = "\xCA\xFE\xBA\xBE"; |
| 1971 | try expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); | 2049 | try expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{fmtSliceHexLower(some_bytes)}); |
| 1972 | try expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); | 2050 | try expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{fmtSliceHexUpper(some_bytes)}); |
| 1973 | //Test Slices | 2051 | //Test Slices |
| 1974 | try expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); | 2052 | try expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{fmtSliceHexUpper(some_bytes[0..2])}); |
| 1975 | try expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); | 2053 | try expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{fmtSliceHexLower(some_bytes[2..])}); |
| 1976 | const bytes_with_zeros = "\x00\x0E\xBA\xBE"; | 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 | pub const trim = @compileError("deprecated; use std.mem.trim with std.ascii.spaces instead"); | 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,9 +2080,9 @@ pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 { |
| 2002 | 2080 | ||
| 2003 | test "hexToBytes" { | 2081 | test "hexToBytes" { |
| 2004 | var buf: [32]u8 = undefined; | 2082 | var buf: [32]u8 = undefined; |
| 2005 | try expectFmt("90" ** 32, "{X}", .{try hexToBytes(&buf, "90" ** 32)}); | 2083 | try expectFmt("90" ** 32, "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, "90" ** 32))}); |
| 2006 | try expectFmt("ABCD", "{X}", .{try hexToBytes(&buf, "ABCD")}); | 2084 | try expectFmt("ABCD", "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, "ABCD"))}); |
| 2007 | try expectFmt("", "{X}", .{try hexToBytes(&buf, "")}); | 2085 | try expectFmt("", "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, ""))}); |
| 2008 | std.testing.expectError(error.InvalidCharacter, hexToBytes(&buf, "012Z")); | 2086 | std.testing.expectError(error.InvalidCharacter, hexToBytes(&buf, "012Z")); |
| 2009 | std.testing.expectError(error.InvalidLength, hexToBytes(&buf, "AAA")); | 2087 | std.testing.expectError(error.InvalidLength, hexToBytes(&buf, "AAA")); |
| 2010 | std.testing.expectError(error.NoSpaceLeft, hexToBytes(buf[0..1], "ABAB")); | 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,7 +60,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD |
| 60 | var dir_path_ptr: [*:0]u8 = undefined; | 60 | var dir_path_ptr: [*:0]u8 = undefined; |
| 61 | // TODO look into directory_which | 61 | // TODO look into directory_which |
| 62 | const be_user_settings = 0xbbe; | 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 | const settings_dir = try allocator.dupeZ(u8, mem.spanZ(dir_path_ptr)); | 64 | const settings_dir = try allocator.dupeZ(u8, mem.spanZ(dir_path_ptr)); |
| 65 | defer allocator.free(settings_dir); | 65 | defer allocator.free(settings_dir); |
| 66 | switch (rc) { | 66 | switch (rc) { |
lib/std/io/writer.zig+7| ... | @@ -4,6 +4,7 @@ | ... | @@ -4,6 +4,7 @@ |
| 4 | // The MIT license requires this copyright notice to be included in all copies | 4 | // The MIT license requires this copyright notice to be included in all copies |
| 5 | // and substantial portions of the software. | 5 | // and substantial portions of the software. |
| 6 | const std = @import("../std.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const assert = std.debug.assert; | ||
| 7 | const builtin = std.builtin; | 8 | const builtin = std.builtin; |
| 8 | const mem = std.mem; | 9 | const mem = std.mem; |
| 9 | 10 | ||
| ... | @@ -86,5 +87,11 @@ pub fn Writer( | ... | @@ -86,5 +87,11 @@ pub fn Writer( |
| 86 | mem.writeInt(T, &bytes, value, endian); | 87 | mem.writeInt(T, &bytes, value, endian); |
| 87 | return self.writeAll(&bytes); | 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,3 +1330,59 @@ test "math.comptime" { |
| 1330 | comptime const v = sin(@as(f32, 1)) + ln(@as(f32, 5)); | 1330 | comptime const v = sin(@as(f32, 1)) + ln(@as(f32, 5)); |
| 1331 | testing.expect(v == sin(@as(f32, 1)) + ln(@as(f32, 5))); | 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,6 +25,14 @@ pub const page_size = switch (builtin.arch) { |
| 25 | else => 4 * 1024, | 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 | pub const Allocator = @import("mem/Allocator.zig"); | 36 | pub const Allocator = @import("mem/Allocator.zig"); |
| 29 | 37 | ||
| 30 | /// Detects and asserts if the std.mem.Allocator interface is violated by the caller | 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,7 +136,7 @@ pub fn MultiArrayList(comptime S: type) type { |
| 136 | const slices = self.slice(); | 136 | const slices = self.slice(); |
| 137 | var result: S = undefined; | 137 | var result: S = undefined; |
| 138 | inline for (fields) |field_info, i| { | 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 | return result; | 141 | return result; |
| 142 | } | 142 | } |
lib/std/os/bits/haiku.zig+6-7| ... | @@ -180,8 +180,8 @@ pub const dirent = extern struct { | ... | @@ -180,8 +180,8 @@ pub const dirent = extern struct { |
| 180 | }; | 180 | }; |
| 181 | 181 | ||
| 182 | pub const image_info = extern struct { | 182 | pub const image_info = extern struct { |
| 183 | id: u32, //image_id | 183 | id: u32, |
| 184 | type: u32, // image_type | 184 | type: u32, |
| 185 | sequence: i32, | 185 | sequence: i32, |
| 186 | init_order: i32, | 186 | init_order: i32, |
| 187 | init_routine: *c_void, | 187 | init_routine: *c_void, |
| ... | @@ -806,17 +806,16 @@ pub const Sigaction = extern struct { | ... | @@ -806,17 +806,16 @@ pub const Sigaction = extern struct { |
| 806 | 806 | ||
| 807 | pub const _SIG_WORDS = 4; | 807 | pub const _SIG_WORDS = 4; |
| 808 | pub const _SIG_MAXSIG = 128; | 808 | pub const _SIG_MAXSIG = 128; |
| 809 | 809 | pub fn _SIG_IDX(sig: usize) callconv(.Inline) usize { | |
| 810 | pub inline fn _SIG_IDX(sig: usize) usize { | ||
| 811 | return sig - 1; | 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 | return_SIG_IDX(sig) >> 5; | 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 | return 1 << (_SIG_IDX(sig) & 31); | 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 | return sig <= _SIG_MAXSIG and sig > 0; | 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,3 +2244,8 @@ pub const MADV_COLD = 20; |
| 2244 | pub const MADV_PAGEOUT = 21; | 2244 | pub const MADV_PAGEOUT = 21; |
| 2245 | pub const MADV_HWPOISON = 100; | 2245 | pub const MADV_HWPOISON = 100; |
| 2246 | pub const MADV_SOFT_OFFLINE = 101; | 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,7 +526,7 @@ pub const IO_Uring = struct { |
| 526 | pub fn timeout( | 526 | pub fn timeout( |
| 527 | self: *IO_Uring, | 527 | self: *IO_Uring, |
| 528 | user_data: u64, | 528 | user_data: u64, |
| 529 | ts: *const os.timespec, | 529 | ts: *const os.__kernel_timespec, |
| 530 | count: u32, | 530 | count: u32, |
| 531 | flags: u32, | 531 | flags: u32, |
| 532 | ) !*io_uring_sqe { | 532 | ) !*io_uring_sqe { |
| ... | @@ -884,7 +884,7 @@ pub fn io_uring_prep_close(sqe: *io_uring_sqe, fd: os.fd_t) void { | ... | @@ -884,7 +884,7 @@ pub fn io_uring_prep_close(sqe: *io_uring_sqe, fd: os.fd_t) void { |
| 884 | 884 | ||
| 885 | pub fn io_uring_prep_timeout( | 885 | pub fn io_uring_prep_timeout( |
| 886 | sqe: *io_uring_sqe, | 886 | sqe: *io_uring_sqe, |
| 887 | ts: *const os.timespec, | 887 | ts: *const os.__kernel_timespec, |
| 888 | count: u32, | 888 | count: u32, |
| 889 | flags: u32, | 889 | flags: u32, |
| 890 | ) void { | 890 | ) void { |
| ... | @@ -1339,7 +1339,7 @@ test "timeout (after a relative time)" { | ... | @@ -1339,7 +1339,7 @@ test "timeout (after a relative time)" { |
| 1339 | 1339 | ||
| 1340 | const ms = 10; | 1340 | const ms = 10; |
| 1341 | const margin = 5; | 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 | const started = std.time.milliTimestamp(); | 1344 | const started = std.time.milliTimestamp(); |
| 1345 | const sqe = try ring.timeout(0x55555555, &ts, 0, 0); | 1345 | const sqe = try ring.timeout(0x55555555, &ts, 0, 0); |
| ... | @@ -1366,7 +1366,7 @@ test "timeout (after a number of completions)" { | ... | @@ -1366,7 +1366,7 @@ test "timeout (after a number of completions)" { |
| 1366 | }; | 1366 | }; |
| 1367 | defer ring.deinit(); | 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 | const count_completions: u64 = 1; | 1370 | const count_completions: u64 = 1; |
| 1371 | const sqe_timeout = try ring.timeout(0x66666666, &ts, count_completions, 0); | 1371 | const sqe_timeout = try ring.timeout(0x66666666, &ts, count_completions, 0); |
| 1372 | testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode); | 1372 | testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode); |
| ... | @@ -1399,7 +1399,7 @@ test "timeout_remove" { | ... | @@ -1399,7 +1399,7 @@ test "timeout_remove" { |
| 1399 | }; | 1399 | }; |
| 1400 | defer ring.deinit(); | 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 | const sqe_timeout = try ring.timeout(0x88888888, &ts, 0, 0); | 1403 | const sqe_timeout = try ring.timeout(0x88888888, &ts, 0, 0); |
| 1404 | testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode); | 1404 | testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode); |
| 1405 | testing.expectEqual(@as(u64, 0x88888888), sqe_timeout.user_data); | 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,6 +18,8 @@ pub const BufSet = @import("buf_set.zig").BufSet; |
| 18 | pub const ChildProcess = @import("child_process.zig").ChildProcess; | 18 | pub const ChildProcess = @import("child_process.zig").ChildProcess; |
| 19 | pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap; | 19 | pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap; |
| 20 | pub const DynLib = @import("dynamic_library.zig").DynLib; | 20 | pub const DynLib = @import("dynamic_library.zig").DynLib; |
| 21 | pub const DynamicBitSet = bit_set.DynamicBitSet; | ||
| 22 | pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged; | ||
| 21 | pub const HashMap = hash_map.HashMap; | 23 | pub const HashMap = hash_map.HashMap; |
| 22 | pub const HashMapUnmanaged = hash_map.HashMapUnmanaged; | 24 | pub const HashMapUnmanaged = hash_map.HashMapUnmanaged; |
| 23 | pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList; | 25 | pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList; |
| ... | @@ -29,6 +31,7 @@ pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; | ... | @@ -29,6 +31,7 @@ pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; |
| 29 | pub const Progress = @import("Progress.zig"); | 31 | pub const Progress = @import("Progress.zig"); |
| 30 | pub const SemanticVersion = @import("SemanticVersion.zig"); | 32 | pub const SemanticVersion = @import("SemanticVersion.zig"); |
| 31 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; | 33 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; |
| 34 | pub const StaticBitSet = bit_set.StaticBitSet; | ||
| 32 | pub const StringHashMap = hash_map.StringHashMap; | 35 | pub const StringHashMap = hash_map.StringHashMap; |
| 33 | pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged; | 36 | pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged; |
| 34 | pub const StringArrayHashMap = array_hash_map.StringArrayHashMap; | 37 | pub const StringArrayHashMap = array_hash_map.StringArrayHashMap; |
| ... | @@ -40,6 +43,7 @@ pub const Thread = @import("Thread.zig"); | ... | @@ -40,6 +43,7 @@ pub const Thread = @import("Thread.zig"); |
| 40 | pub const array_hash_map = @import("array_hash_map.zig"); | 43 | pub const array_hash_map = @import("array_hash_map.zig"); |
| 41 | pub const atomic = @import("atomic.zig"); | 44 | pub const atomic = @import("atomic.zig"); |
| 42 | pub const base64 = @import("base64.zig"); | 45 | pub const base64 = @import("base64.zig"); |
| 46 | pub const bit_set = @import("bit_set.zig"); | ||
| 43 | pub const build = @import("build.zig"); | 47 | pub const build = @import("build.zig"); |
| 44 | pub const builtin = @import("builtin.zig"); | 48 | pub const builtin = @import("builtin.zig"); |
| 45 | pub const c = @import("c.zig"); | 49 | pub const c = @import("c.zig"); |
lib/std/zig/parse.zig+12-20| ... | @@ -3714,7 +3714,6 @@ const Parser = struct { | ... | @@ -3714,7 +3714,6 @@ const Parser = struct { |
| 3714 | if (p.eatToken(.r_paren)) |_| { | 3714 | if (p.eatToken(.r_paren)) |_| { |
| 3715 | return SmallSpan{ .zero_or_one = 0 }; | 3715 | return SmallSpan{ .zero_or_one = 0 }; |
| 3716 | } | 3716 | } |
| 3717 | continue; | ||
| 3718 | }, | 3717 | }, |
| 3719 | .r_paren => return SmallSpan{ .zero_or_one = 0 }, | 3718 | .r_paren => return SmallSpan{ .zero_or_one = 0 }, |
| 3720 | else => { | 3719 | else => { |
| ... | @@ -3728,14 +3727,7 @@ const Parser = struct { | ... | @@ -3728,14 +3727,7 @@ const Parser = struct { |
| 3728 | 3727 | ||
| 3729 | const param_two = while (true) { | 3728 | const param_two = while (true) { |
| 3730 | switch (p.token_tags[p.nextToken()]) { | 3729 | switch (p.token_tags[p.nextToken()]) { |
| 3731 | .comma => { | 3730 | .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 | }, | ||
| 3739 | .r_paren => return SmallSpan{ .zero_or_one = param_one }, | 3731 | .r_paren => return SmallSpan{ .zero_or_one = param_one }, |
| 3740 | .colon, .r_brace, .r_bracket => { | 3732 | .colon, .r_brace, .r_bracket => { |
| 3741 | p.tok_i -= 1; | 3733 | p.tok_i -= 1; |
| ... | @@ -3748,6 +3740,11 @@ const Parser = struct { | ... | @@ -3748,6 +3740,11 @@ const Parser = struct { |
| 3748 | try p.warnExpected(.comma); | 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 | } else unreachable; | 3748 | } else unreachable; |
| 3752 | 3749 | ||
| 3753 | var list = std.ArrayList(Node.Index).init(p.gpa); | 3750 | var list = std.ArrayList(Node.Index).init(p.gpa); |
| ... | @@ -3757,17 +3754,7 @@ const Parser = struct { | ... | @@ -3757,17 +3754,7 @@ const Parser = struct { |
| 3757 | 3754 | ||
| 3758 | while (true) { | 3755 | while (true) { |
| 3759 | switch (p.token_tags[p.nextToken()]) { | 3756 | switch (p.token_tags[p.nextToken()]) { |
| 3760 | .comma => { | 3757 | .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 | }, | ||
| 3771 | .r_paren => return SmallSpan{ .multi = list.toOwnedSlice() }, | 3758 | .r_paren => return SmallSpan{ .multi = list.toOwnedSlice() }, |
| 3772 | .colon, .r_brace, .r_bracket => { | 3759 | .colon, .r_brace, .r_bracket => { |
| 3773 | p.tok_i -= 1; | 3760 | p.tok_i -= 1; |
| ... | @@ -3780,6 +3767,11 @@ const Parser = struct { | ... | @@ -3780,6 +3767,11 @@ const Parser = struct { |
| 3780 | try p.warnExpected(.comma); | 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,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 | test "zig fmt: comment to disable/enable zig fmt" { | 1130 | test "zig fmt: comment to disable/enable zig fmt" { |
| 1112 | try testTransform( | 1131 | try testTransform( |
| 1113 | \\const a = b; | 1132 | \\const a = b; |
| ... | @@ -4549,6 +4568,18 @@ test "recovery: missing for payload" { | ... | @@ -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 | const std = @import("std"); | 4583 | const std = @import("std"); |
| 4553 | const mem = std.mem; | 4584 | const mem = std.mem; |
| 4554 | const warn = std.debug.warn; | 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,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}); | 2355 | index = 1 + (newline orelse end - 1); |
| 2356 | index = 1 + (newline orelse return true); | 2356 | |
| 2357 | 2357 | const comment_content = mem.trimLeft(u8, trimmed_comment["//".len..], &std.ascii.spaces); | |
| 2358 | if (ais.disabled_offset) |disabled_offset| { | 2358 | if (ais.disabled_offset != null and mem.eql(u8, comment_content, "zig fmt: on")) { |
| 2359 | if (mem.eql(u8, trimmed_comment, "// zig fmt: on")) { | 2359 | // Write the source for which formatting was disabled directly |
| 2360 | // write the source for which formatting was disabled directly | 2360 | // to the underlying writer, fixing up invaild whitespace. |
| 2361 | // 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, tree.source[disabled_offset..index]); | 2362 | try writeFixingWhitespace(ais.underlying_writer, disabled_source); |
| 2363 | ais.disabled_offset = null; | 2363 | ais.disabled_offset = null; |
| 2364 | } | 2364 | // Write with the canonical single space. |
| 2365 | } else if (mem.eql(u8, trimmed_comment, "// zig fmt: off")) { | 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 | ais.disabled_offset = index; | 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,7 +153,11 @@ pub const HashHelper = struct { |
| 153 | hh.hasher.final(&bin_digest); | 153 | hh.hasher.final(&bin_digest); |
| 154 | 154 | ||
| 155 | var out_digest: [hex_digest_len]u8 = undefined; | 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 | return out_digest; | 161 | return out_digest; |
| 158 | } | 162 | } |
| 159 | }; | 163 | }; |
| ... | @@ -250,7 +254,11 @@ pub const Manifest = struct { | ... | @@ -250,7 +254,11 @@ pub const Manifest = struct { |
| 250 | var bin_digest: BinDigest = undefined; | 254 | var bin_digest: BinDigest = undefined; |
| 251 | self.hash.hasher.final(&bin_digest); | 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 | self.hash.hasher = hasher_init; | 263 | self.hash.hasher = hasher_init; |
| 256 | self.hash.hasher.update(&bin_digest); | 264 | self.hash.hasher.update(&bin_digest); |
| ... | @@ -549,7 +557,11 @@ pub const Manifest = struct { | ... | @@ -549,7 +557,11 @@ pub const Manifest = struct { |
| 549 | self.hash.hasher.final(&bin_digest); | 557 | self.hash.hasher.final(&bin_digest); |
| 550 | 558 | ||
| 551 | var out_digest: [hex_digest_len]u8 = undefined; | 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 | return out_digest; | 566 | return out_digest; |
| 555 | } | 567 | } |
| ... | @@ -565,7 +577,11 @@ pub const Manifest = struct { | ... | @@ -565,7 +577,11 @@ pub const Manifest = struct { |
| 565 | var encoded_digest: [hex_digest_len]u8 = undefined; | 577 | var encoded_digest: [hex_digest_len]u8 = undefined; |
| 566 | 578 | ||
| 567 | for (self.files.items) |file| { | 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 | try writer.print("{d} {d} {d} {s} {s}\n", .{ | 585 | try writer.print("{d} {d} {d} {s} {s}\n", .{ |
| 570 | file.stat.size, | 586 | file.stat.size, |
| 571 | file.stat.inode, | 587 | file.stat.inode, |
src/Module.zig+6-6| ... | @@ -4083,15 +4083,15 @@ pub fn namedFieldPtr( | ... | @@ -4083,15 +4083,15 @@ pub fn namedFieldPtr( |
| 4083 | const child_type = try val.toType(scope.arena()); | 4083 | const child_type = try val.toType(scope.arena()); |
| 4084 | switch (child_type.zigTypeTag()) { | 4084 | switch (child_type.zigTypeTag()) { |
| 4085 | .ErrorSet => { | 4085 | .ErrorSet => { |
| 4086 | var name: []const u8 = undefined; | ||
| 4086 | // TODO resolve inferred error sets | 4087 | // TODO resolve inferred error sets |
| 4087 | const entry = if (val.castTag(.error_set)) |payload| | 4088 | if (val.castTag(.error_set)) |payload| |
| 4088 | (payload.data.fields.getEntry(field_name) orelse | 4089 | name = (payload.data.fields.getEntry(field_name) orelse return mod.fail(scope, src, "no error named '{s}' in '{}'", .{ field_name, child_type })).key |
| 4089 | return mod.fail(scope, src, "no error named '{s}' in '{}'", .{ field_name, child_type })).* | ||
| 4090 | else | 4090 | else |
| 4091 | try mod.getErrorValue(field_name); | 4091 | name = (try mod.getErrorValue(field_name)).key; |
| 4092 | 4092 | ||
| 4093 | const result_type = if (child_type.tag() == .anyerror) | 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 | else | 4095 | else |
| 4096 | child_type; | 4096 | child_type; |
| 4097 | 4097 | ||
| ... | @@ -4100,7 +4100,7 @@ pub fn namedFieldPtr( | ... | @@ -4100,7 +4100,7 @@ pub fn namedFieldPtr( |
| 4100 | .val = try Value.Tag.ref_val.create( | 4100 | .val = try Value.Tag.ref_val.create( |
| 4101 | scope.arena(), | 4101 | scope.arena(), |
| 4102 | try Value.Tag.@"error".create(scope.arena(), .{ | 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,13 +453,23 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In |
| 453 | return rvalue(mod, scope, rl, result); | 453 | return rvalue(mod, scope, rl, result); |
| 454 | }, | 454 | }, |
| 455 | .unwrap_optional => { | 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 | const src = token_starts[main_tokens[node]]; | 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 | .block_two, .block_two_semicolon => { | 474 | .block_two, .block_two_semicolon => { |
| 465 | const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; | 475 | const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; |
| ... | @@ -1645,7 +1655,7 @@ fn errorSetDecl( | ... | @@ -1645,7 +1655,7 @@ fn errorSetDecl( |
| 1645 | switch (token_tags[tok_i]) { | 1655 | switch (token_tags[tok_i]) { |
| 1646 | .doc_comment, .comma => {}, | 1656 | .doc_comment, .comma => {}, |
| 1647 | .identifier => count += 1, | 1657 | .identifier => count += 1, |
| 1648 | .r_paren => break :count count, | 1658 | .r_brace => break :count count, |
| 1649 | else => unreachable, | 1659 | else => unreachable, |
| 1650 | } | 1660 | } |
| 1651 | } else unreachable; // TODO should not need else unreachable here | 1661 | } else unreachable; // TODO should not need else unreachable here |
| ... | @@ -1662,7 +1672,7 @@ fn errorSetDecl( | ... | @@ -1662,7 +1672,7 @@ fn errorSetDecl( |
| 1662 | fields[field_i] = try mod.identifierTokenString(scope, tok_i); | 1672 | fields[field_i] = try mod.identifierTokenString(scope, tok_i); |
| 1663 | field_i += 1; | 1673 | field_i += 1; |
| 1664 | }, | 1674 | }, |
| 1665 | .r_paren => break, | 1675 | .r_brace => break, |
| 1666 | else => unreachable, | 1676 | else => unreachable, |
| 1667 | } | 1677 | } |
| 1668 | } | 1678 | } |
| ... | @@ -1699,9 +1709,13 @@ fn orelseCatchExpr( | ... | @@ -1699,9 +1709,13 @@ fn orelseCatchExpr( |
| 1699 | setBlockResultLoc(&block_scope, rl); | 1709 | setBlockResultLoc(&block_scope, rl); |
| 1700 | defer block_scope.instructions.deinit(mod.gpa); | 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 | block_scope.break_count += 1; | 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 | const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand); | 1719 | const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand); |
| 1706 | 1720 | ||
| 1707 | const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{ | 1721 | const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{ |
| ... | @@ -1753,6 +1767,10 @@ fn orelseCatchExpr( | ... | @@ -1753,6 +1767,10 @@ fn orelseCatchExpr( |
| 1753 | 1767 | ||
| 1754 | // This could be a pointer or value depending on `unwrap_op`. | 1768 | // This could be a pointer or value depending on `unwrap_op`. |
| 1755 | const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand); | 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 | return finishThenElseBlock( | 1775 | return finishThenElseBlock( |
| 1758 | mod, | 1776 | mod, |
| ... | @@ -1766,7 +1784,7 @@ fn orelseCatchExpr( | ... | @@ -1766,7 +1784,7 @@ fn orelseCatchExpr( |
| 1766 | src, | 1784 | src, |
| 1767 | src, | 1785 | src, |
| 1768 | then_result, | 1786 | then_result, |
| 1769 | unwrapped_payload, | 1787 | else_result, |
| 1770 | block, | 1788 | block, |
| 1771 | block, | 1789 | block, |
| 1772 | ); | 1790 | ); |
| ... | @@ -3955,6 +3973,25 @@ fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy { | ... | @@ -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 | fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void { | 3995 | fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void { |
| 3959 | // Depending on whether the result location is a pointer or value, different | 3996 | // Depending on whether the result location is a pointer or value, different |
| 3960 | // ZIR needs to be generated. In the former case we rely on storing to the | 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,7 +46,7 @@ flagpd1("M"), |
| 46 | .{ | 46 | .{ |
| 47 | .name = "MM", | 47 | .name = "MM", |
| 48 | .syntax = .flag, | 48 | .syntax = .flag, |
| 49 | .zig_equivalent = .dep_file, | 49 | .zig_equivalent = .dep_file_mm, |
| 50 | .pd1 = true, | 50 | .pd1 = true, |
| 51 | .pd2 = false, | 51 | .pd2 = false, |
| 52 | .psl = false, | 52 | .psl = false, |
| ... | @@ -1870,7 +1870,7 @@ flagpsl("MT"), | ... | @@ -1870,7 +1870,7 @@ flagpsl("MT"), |
| 1870 | .{ | 1870 | .{ |
| 1871 | .name = "print-missing-file-dependencies", | 1871 | .name = "print-missing-file-dependencies", |
| 1872 | .syntax = .flag, | 1872 | .syntax = .flag, |
| 1873 | .zig_equivalent = .other, | 1873 | .zig_equivalent = .dep_file, |
| 1874 | .pd1 = false, | 1874 | .pd1 = false, |
| 1875 | .pd2 = true, | 1875 | .pd2 = true, |
| 1876 | .psl = false, | 1876 | .psl = false, |
| ... | @@ -1990,7 +1990,7 @@ flagpsl("MT"), | ... | @@ -1990,7 +1990,7 @@ flagpsl("MT"), |
| 1990 | .{ | 1990 | .{ |
| 1991 | .name = "user-dependencies", | 1991 | .name = "user-dependencies", |
| 1992 | .syntax = .flag, | 1992 | .syntax = .flag, |
| 1993 | .zig_equivalent = .other, | 1993 | .zig_equivalent = .dep_file_mm, |
| 1994 | .pd1 = false, | 1994 | .pd1 = false, |
| 1995 | .pd2 = true, | 1995 | .pd2 = true, |
| 1996 | .psl = false, | 1996 | .psl = false, |
| ... | @@ -2014,7 +2014,7 @@ flagpsl("MT"), | ... | @@ -2014,7 +2014,7 @@ flagpsl("MT"), |
| 2014 | .{ | 2014 | .{ |
| 2015 | .name = "write-dependencies", | 2015 | .name = "write-dependencies", |
| 2016 | .syntax = .flag, | 2016 | .syntax = .flag, |
| 2017 | .zig_equivalent = .other, | 2017 | .zig_equivalent = .dep_file, |
| 2018 | .pd1 = false, | 2018 | .pd1 = false, |
| 2019 | .pd2 = true, | 2019 | .pd2 = true, |
| 2020 | .psl = false, | 2020 | .psl = false, |
| ... | @@ -2022,7 +2022,7 @@ flagpsl("MT"), | ... | @@ -2022,7 +2022,7 @@ flagpsl("MT"), |
| 2022 | .{ | 2022 | .{ |
| 2023 | .name = "write-user-dependencies", | 2023 | .name = "write-user-dependencies", |
| 2024 | .syntax = .flag, | 2024 | .syntax = .flag, |
| 2025 | .zig_equivalent = .other, | 2025 | .zig_equivalent = .dep_file, |
| 2026 | .pd1 = false, | 2026 | .pd1 = false, |
| 2027 | .pd2 = true, | 2027 | .pd2 = true, |
| 2028 | .psl = false, | 2028 | .psl = false, |
src/codegen.zig+43| ... | @@ -899,6 +899,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -899,6 +899,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 899 | .load => return self.genLoad(inst.castTag(.load).?), | 899 | .load => return self.genLoad(inst.castTag(.load).?), |
| 900 | .loop => return self.genLoop(inst.castTag(.loop).?), | 900 | .loop => return self.genLoop(inst.castTag(.loop).?), |
| 901 | .not => return self.genNot(inst.castTag(.not).?), | 901 | .not => return self.genNot(inst.castTag(.not).?), |
| 902 | .mul => return self.genMul(inst.castTag(.mul).?), | ||
| 902 | .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?), | 903 | .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?), |
| 903 | .ref => return self.genRef(inst.castTag(.ref).?), | 904 | .ref => return self.genRef(inst.castTag(.ref).?), |
| 904 | .ret => return self.genRet(inst.castTag(.ret).?), | 905 | .ret => return self.genRet(inst.castTag(.ret).?), |
| ... | @@ -1128,6 +1129,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -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 | fn genBitAnd(self: *Self, inst: *ir.Inst.BinOp) !MCValue { | 1142 | fn genBitAnd(self: *Self, inst: *ir.Inst.BinOp) !MCValue { |
| 1132 | // No side effects, so if it's unreferenced, do nothing. | 1143 | // No side effects, so if it's unreferenced, do nothing. |
| 1133 | if (inst.base.isUnused()) | 1144 | if (inst.base.isUnused()) |
| ... | @@ -1478,6 +1489,38 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -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 | /// ADD, SUB, XOR, OR, AND | 1524 | /// ADD, SUB, XOR, OR, AND |
| 1482 | fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue { | 1525 | fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue { |
| 1483 | try self.code.ensureCapacity(self.code.items.len + 8); | 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,6 +400,7 @@ pub const LLVMIRModule = struct { |
| 400 | .block => try self.genBlock(inst.castTag(.block).?), | 400 | .block => try self.genBlock(inst.castTag(.block).?), |
| 401 | .br => try self.genBr(inst.castTag(.br).?), | 401 | .br => try self.genBr(inst.castTag(.br).?), |
| 402 | .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), | 402 | .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), |
| 403 | .br_void => try self.genBrVoid(inst.castTag(.br_void).?), | ||
| 403 | .call => try self.genCall(inst.castTag(.call).?), | 404 | .call => try self.genCall(inst.castTag(.call).?), |
| 404 | .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq), | 405 | .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq), |
| 405 | .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt), | 406 | .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt), |
| ... | @@ -409,6 +410,10 @@ pub const LLVMIRModule = struct { | ... | @@ -409,6 +410,10 @@ pub const LLVMIRModule = struct { |
| 409 | .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq), | 410 | .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq), |
| 410 | .condbr => try self.genCondBr(inst.castTag(.condbr).?), | 411 | .condbr => try self.genCondBr(inst.castTag(.condbr).?), |
| 411 | .intcast => try self.genIntCast(inst.castTag(.intcast).?), | 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 | .load => try self.genLoad(inst.castTag(.load).?), | 417 | .load => try self.genLoad(inst.castTag(.load).?), |
| 413 | .loop => try self.genLoop(inst.castTag(.loop).?), | 418 | .loop => try self.genLoop(inst.castTag(.loop).?), |
| 414 | .not => try self.genNot(inst.castTag(.not).?), | 419 | .not => try self.genNot(inst.castTag(.not).?), |
| ... | @@ -417,6 +422,8 @@ pub const LLVMIRModule = struct { | ... | @@ -417,6 +422,8 @@ pub const LLVMIRModule = struct { |
| 417 | .store => try self.genStore(inst.castTag(.store).?), | 422 | .store => try self.genStore(inst.castTag(.store).?), |
| 418 | .sub => try self.genSub(inst.castTag(.sub).?), | 423 | .sub => try self.genSub(inst.castTag(.sub).?), |
| 419 | .unreach => self.genUnreach(inst.castTag(.unreach).?), | 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 | .dbg_stmt => blk: { | 427 | .dbg_stmt => blk: { |
| 421 | // TODO: implement debug info | 428 | // TODO: implement debug info |
| 422 | break :blk null; | 429 | break :blk null; |
| ... | @@ -537,21 +544,29 @@ pub const LLVMIRModule = struct { | ... | @@ -537,21 +544,29 @@ pub const LLVMIRModule = struct { |
| 537 | } | 544 | } |
| 538 | 545 | ||
| 539 | fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value { | 546 | fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value { |
| 540 | // Get the block that we want to break to. | ||
| 541 | var block = self.blocks.get(inst.block).?; | 547 | var block = self.blocks.get(inst.block).?; |
| 542 | _ = self.builder.buildBr(block.parent_bb); | ||
| 543 | 548 | ||
| 544 | // If the break doesn't break a value, then we don't have to add | 549 | // If the break doesn't break a value, then we don't have to add |
| 545 | // the values to the lists. | 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 | 557 | // For the phi node, we need the basic blocks and the values of the |
| 549 | // break instructions. | 558 | // break instructions. |
| 550 | try block.break_bbs.append(self.gpa, self.builder.getInsertBlock()); | 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); | 562 | _ = self.builder.buildBr(block.parent_bb); |
| 553 | try block.break_vals.append(self.gpa, val); | 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 | return null; | 570 | return null; |
| 556 | } | 571 | } |
| 557 | 572 | ||
| ... | @@ -594,6 +609,44 @@ pub const LLVMIRModule = struct { | ... | @@ -594,6 +609,44 @@ pub const LLVMIRModule = struct { |
| 594 | return null; | 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 | fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { | 650 | fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { |
| 598 | const lhs = try self.resolveInst(inst.lhs); | 651 | const lhs = try self.resolveInst(inst.lhs); |
| 599 | const rhs = try self.resolveInst(inst.rhs); | 652 | const rhs = try self.resolveInst(inst.rhs); |
| ... | @@ -754,6 +807,13 @@ pub const LLVMIRModule = struct { | ... | @@ -754,6 +807,13 @@ pub const LLVMIRModule = struct { |
| 754 | // TODO: consider using buildInBoundsGEP2 for opaque pointers | 807 | // TODO: consider using buildInBoundsGEP2 for opaque pointers |
| 755 | return self.builder.buildInBoundsGEP(val, &indices, 2, ""); | 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 | else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}), | 817 | else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}), |
| 758 | }, | 818 | }, |
| 759 | .Array => { | 819 | .Array => { |
| ... | @@ -768,6 +828,29 @@ pub const LLVMIRModule = struct { | ... | @@ -768,6 +828,29 @@ pub const LLVMIRModule = struct { |
| 768 | return self.fail(src, "TODO handle more array values", .{}); | 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 | else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}), | 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,6 +876,20 @@ pub const LLVMIRModule = struct { |
| 793 | const elem_type = try self.getLLVMType(t.elemType(), src); | 876 | const elem_type = try self.getLLVMType(t.elemType(), src); |
| 794 | return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget()))); | 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 | else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}), | 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,9 +21,15 @@ pub const Context = opaque { |
| 21 | pub const voidType = LLVMVoidTypeInContext; | 21 | pub const voidType = LLVMVoidTypeInContext; |
| 22 | extern fn LLVMVoidTypeInContext(C: *const Context) *const Type; | 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 | pub const constString = LLVMConstStringInContext; | 27 | pub const constString = LLVMConstStringInContext; |
| 25 | extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value; | 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 | pub const createBasicBlock = LLVMCreateBasicBlockInContext; | 33 | pub const createBasicBlock = LLVMCreateBasicBlockInContext; |
| 28 | extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock; | 34 | extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock; |
| 29 | 35 | ||
| ... | @@ -204,6 +210,9 @@ pub const Builder = opaque { | ... | @@ -204,6 +210,9 @@ pub const Builder = opaque { |
| 204 | 210 | ||
| 205 | pub const buildPhi = LLVMBuildPhi; | 211 | pub const buildPhi = LLVMBuildPhi; |
| 206 | extern fn LLVMBuildPhi(*const Builder, Ty: *const Type, Name: [*:0]const u8) *const Value; | 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 | pub const IntPredicate = extern enum { | 218 | pub const IntPredicate = extern enum { |
src/ir.zig+2| ... | @@ -106,6 +106,7 @@ pub const Inst = struct { | ... | @@ -106,6 +106,7 @@ pub const Inst = struct { |
| 106 | store, | 106 | store, |
| 107 | sub, | 107 | sub, |
| 108 | unreach, | 108 | unreach, |
| 109 | mul, | ||
| 109 | not, | 110 | not, |
| 110 | floatcast, | 111 | floatcast, |
| 111 | intcast, | 112 | intcast, |
| ... | @@ -165,6 +166,7 @@ pub const Inst = struct { | ... | @@ -165,6 +166,7 @@ pub const Inst = struct { |
| 165 | 166 | ||
| 166 | .add, | 167 | .add, |
| 167 | .sub, | 168 | .sub, |
| 169 | .mul, | ||
| 168 | .cmp_lt, | 170 | .cmp_lt, |
| 169 | .cmp_lte, | 171 | .cmp_lte, |
| 170 | .cmp_eq, | 172 | .cmp_eq, |
src/link.zig+2-2| ... | @@ -550,11 +550,11 @@ pub const File = struct { | ... | @@ -550,11 +550,11 @@ pub const File = struct { |
| 550 | id_symlink_basename, | 550 | id_symlink_basename, |
| 551 | &prev_digest_buf, | 551 | &prev_digest_buf, |
| 552 | ) catch |err| b: { | 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 | break :b prev_digest_buf[0..0]; | 554 | break :b prev_digest_buf[0..0]; |
| 555 | }; | 555 | }; |
| 556 | if (mem.eql(u8, prev_digest, &digest)) { | 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 | base.lock = man.toOwnedLock(); | 558 | base.lock = man.toOwnedLock(); |
| 559 | return; | 559 | return; |
| 560 | } | 560 | } |
src/link/Coff.zig+3-3| ... | @@ -892,17 +892,17 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { | ... | @@ -892,17 +892,17 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 892 | id_symlink_basename, | 892 | id_symlink_basename, |
| 893 | &prev_digest_buf, | 893 | &prev_digest_buf, |
| 894 | ) catch |err| blk: { | 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 | // Handle this as a cache miss. | 896 | // Handle this as a cache miss. |
| 897 | break :blk prev_digest_buf[0..0]; | 897 | break :blk prev_digest_buf[0..0]; |
| 898 | }; | 898 | }; |
| 899 | if (mem.eql(u8, prev_digest, &digest)) { | 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 | // Hot diggity dog! The output binary is already there. | 901 | // Hot diggity dog! The output binary is already there. |
| 902 | self.base.lock = man.toOwnedLock(); | 902 | self.base.lock = man.toOwnedLock(); |
| 903 | return; | 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 | // We are about to change the output file to be different, so we invalidate the build hash now. | 907 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 908 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | 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,17 +1365,17 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1365 | id_symlink_basename, | 1365 | id_symlink_basename, |
| 1366 | &prev_digest_buf, | 1366 | &prev_digest_buf, |
| 1367 | ) catch |err| blk: { | 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 | // Handle this as a cache miss. | 1369 | // Handle this as a cache miss. |
| 1370 | break :blk prev_digest_buf[0..0]; | 1370 | break :blk prev_digest_buf[0..0]; |
| 1371 | }; | 1371 | }; |
| 1372 | if (mem.eql(u8, prev_digest, &digest)) { | 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 | // Hot diggity dog! The output binary is already there. | 1374 | // Hot diggity dog! The output binary is already there. |
| 1375 | self.base.lock = man.toOwnedLock(); | 1375 | self.base.lock = man.toOwnedLock(); |
| 1376 | return; | 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 | // We are about to change the output file to be different, so we invalidate the build hash now. | 1380 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 1381 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | 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,17 +556,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 556 | id_symlink_basename, | 556 | id_symlink_basename, |
| 557 | &prev_digest_buf, | 557 | &prev_digest_buf, |
| 558 | ) catch |err| blk: { | 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 | // Handle this as a cache miss. | 560 | // Handle this as a cache miss. |
| 561 | break :blk prev_digest_buf[0..0]; | 561 | break :blk prev_digest_buf[0..0]; |
| 562 | }; | 562 | }; |
| 563 | if (mem.eql(u8, prev_digest, &digest)) { | 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 | // Hot diggity dog! The output binary is already there. | 565 | // Hot diggity dog! The output binary is already there. |
| 566 | self.base.lock = man.toOwnedLock(); | 566 | self.base.lock = man.toOwnedLock(); |
| 567 | return; | 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 | // We are about to change the output file to be different, so we invalidate the build hash now. | 571 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 572 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | 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,17 +391,17 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 391 | id_symlink_basename, | 391 | id_symlink_basename, |
| 392 | &prev_digest_buf, | 392 | &prev_digest_buf, |
| 393 | ) catch |err| blk: { | 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 | // Handle this as a cache miss. | 395 | // Handle this as a cache miss. |
| 396 | break :blk prev_digest_buf[0..0]; | 396 | break :blk prev_digest_buf[0..0]; |
| 397 | }; | 397 | }; |
| 398 | if (mem.eql(u8, prev_digest, &digest)) { | 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 | // Hot diggity dog! The output binary is already there. | 400 | // Hot diggity dog! The output binary is already there. |
| 401 | self.base.lock = man.toOwnedLock(); | 401 | self.base.lock = man.toOwnedLock(); |
| 402 | return; | 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 | // We are about to change the output file to be different, so we invalidate the build hash now. | 406 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 407 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | 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,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 | fn optionalStringEnvVar(arena: *Allocator, name: []const u8) !?[]const u8 { | 460 | fn optionalStringEnvVar(arena: *Allocator, name: []const u8) !?[]const u8 { |
| 452 | if (std.process.getEnvVarOwned(arena, name)) |value| { | 461 | if (std.process.getEnvVarOwned(arena, name)) |value| { |
| 453 | return value; | 462 | return value; |
| ... | @@ -482,8 +491,8 @@ fn buildOutputType( | ... | @@ -482,8 +491,8 @@ fn buildOutputType( |
| 482 | var single_threaded = false; | 491 | var single_threaded = false; |
| 483 | var function_sections = false; | 492 | var function_sections = false; |
| 484 | var watch = false; | 493 | var watch = false; |
| 485 | var verbose_link = false; | 494 | var verbose_link = try optionalBoolEnvVar(arena, "ZIG_VERBOSE_LINK"); |
| 486 | var verbose_cc = false; | 495 | var verbose_cc = try optionalBoolEnvVar(arena, "ZIG_VERBOSE_CC"); |
| 487 | var verbose_tokenize = false; | 496 | var verbose_tokenize = false; |
| 488 | var verbose_ast = false; | 497 | var verbose_ast = false; |
| 489 | var verbose_ir = false; | 498 | var verbose_ir = false; |
| ... | @@ -1183,6 +1192,12 @@ fn buildOutputType( | ... | @@ -1183,6 +1192,12 @@ fn buildOutputType( |
| 1183 | disable_c_depfile = true; | 1192 | disable_c_depfile = true; |
| 1184 | try clang_argv.appendSlice(it.other_args); | 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 | .framework_dir => try framework_dirs.append(it.only_arg), | 1201 | .framework_dir => try framework_dirs.append(it.only_arg), |
| 1187 | .framework => try frameworks.append(it.only_arg), | 1202 | .framework => try frameworks.append(it.only_arg), |
| 1188 | .nostdlibinc => want_native_include_dirs = false, | 1203 | .nostdlibinc => want_native_include_dirs = false, |
| ... | @@ -3046,6 +3061,7 @@ pub const ClangArgIterator = struct { | ... | @@ -3046,6 +3061,7 @@ pub const ClangArgIterator = struct { |
| 3046 | lib_dir, | 3061 | lib_dir, |
| 3047 | mcpu, | 3062 | mcpu, |
| 3048 | dep_file, | 3063 | dep_file, |
| 3064 | dep_file_mm, | ||
| 3049 | framework_dir, | 3065 | framework_dir, |
| 3050 | framework, | 3066 | framework, |
| 3051 | nostdlibinc, | 3067 | nostdlibinc, |
src/stage1/codegen.cpp+9-1| ... | @@ -4126,7 +4126,15 @@ static LLVMValueRef gen_frame_size(CodeGen *g, LLVMValueRef fn_val) { | ... | @@ -4126,7 +4126,15 @@ static LLVMValueRef gen_frame_size(CodeGen *g, LLVMValueRef fn_val) { |
| 4126 | LLVMValueRef casted_fn_val = LLVMBuildBitCast(g->builder, fn_val, ptr_usize_llvm_type, ""); | 4126 | LLVMValueRef casted_fn_val = LLVMBuildBitCast(g->builder, fn_val, ptr_usize_llvm_type, ""); |
| 4127 | LLVMValueRef negative_one = LLVMConstInt(LLVMInt32Type(), -1, true); | 4127 | LLVMValueRef negative_one = LLVMConstInt(LLVMInt32Type(), -1, true); |
| 4128 | LLVMValueRef prefix_ptr = LLVMBuildInBoundsGEP(g->builder, casted_fn_val, &negative_one, 1, ""); | 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 | static void gen_init_stack_trace(CodeGen *g, LLVMValueRef trace_field_ptr, LLVMValueRef addrs_field_ptr) { | 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,8 +1030,8 @@ pub const TestContext = struct { |
| 1030 | var file = try tmp_dir.openFile(bin_name, .{ .read = true }); | 1030 | var file = try tmp_dir.openFile(bin_name, .{ .read = true }); |
| 1031 | defer file.close(); | 1031 | defer file.close(); |
| 1032 | 1032 | ||
| 1033 | const header = try std.elf.readHeader(file); | 1033 | const header = try std.elf.Header.read(&file); |
| 1034 | var iterator = header.program_header_iterator(file); | 1034 | var iterator = header.program_header_iterator(&file); |
| 1035 | 1035 | ||
| 1036 | var none_loaded = true; | 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,7 +4608,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N |
| 4608 | if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) { | 4608 | if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) { |
| 4609 | return Tag.char_literal.create(c.arena, try zigifyEscapeSequences(c, m)); | 4609 | return Tag.char_literal.create(c.arena, try zigifyEscapeSequences(c, m)); |
| 4610 | } else { | 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 | return Tag.integer_literal.create(c.arena, str); | 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,7 +2144,7 @@ pub const Value = extern union { |
| 2144 | base: Payload = .{ .tag = base_tag }, | 2144 | base: Payload = .{ .tag = base_tag }, |
| 2145 | data: struct { | 2145 | data: struct { |
| 2146 | /// TODO revisit this when we have the concept of the error tag type | 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 | decl: *Module.Decl, | 2148 | decl: *Module.Decl, |
| 2149 | }, | 2149 | }, |
| 2150 | }; | 2150 | }; |
src/zir.zig+7| ... | @@ -299,6 +299,9 @@ pub const Inst = struct { | ... | @@ -299,6 +299,9 @@ pub const Inst = struct { |
| 299 | xor, | 299 | xor, |
| 300 | /// Create an optional type '?T' | 300 | /// Create an optional type '?T' |
| 301 | optional_type, | 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 | /// Create a union type. | 305 | /// Create a union type. |
| 303 | union_type, | 306 | union_type, |
| 304 | /// ?T => T with safety. | 307 | /// ?T => T with safety. |
| ... | @@ -397,6 +400,7 @@ pub const Inst = struct { | ... | @@ -397,6 +400,7 @@ pub const Inst = struct { |
| 397 | .mut_slice_type, | 400 | .mut_slice_type, |
| 398 | .const_slice_type, | 401 | .const_slice_type, |
| 399 | .optional_type, | 402 | .optional_type, |
| 403 | .optional_type_from_ptr_elem, | ||
| 400 | .optional_payload_safe, | 404 | .optional_payload_safe, |
| 401 | .optional_payload_unsafe, | 405 | .optional_payload_unsafe, |
| 402 | .optional_payload_safe_ptr, | 406 | .optional_payload_safe_ptr, |
| ... | @@ -597,6 +601,7 @@ pub const Inst = struct { | ... | @@ -597,6 +601,7 @@ pub const Inst = struct { |
| 597 | .typeof, | 601 | .typeof, |
| 598 | .xor, | 602 | .xor, |
| 599 | .optional_type, | 603 | .optional_type, |
| 604 | .optional_type_from_ptr_elem, | ||
| 600 | .optional_payload_safe, | 605 | .optional_payload_safe, |
| 601 | .optional_payload_unsafe, | 606 | .optional_payload_unsafe, |
| 602 | .optional_payload_safe_ptr, | 607 | .optional_payload_safe_ptr, |
| ... | @@ -1649,6 +1654,7 @@ const DumpTzir = struct { | ... | @@ -1649,6 +1654,7 @@ const DumpTzir = struct { |
| 1649 | 1654 | ||
| 1650 | .add, | 1655 | .add, |
| 1651 | .sub, | 1656 | .sub, |
| 1657 | .mul, | ||
| 1652 | .cmp_lt, | 1658 | .cmp_lt, |
| 1653 | .cmp_lte, | 1659 | .cmp_lte, |
| 1654 | .cmp_eq, | 1660 | .cmp_eq, |
| ... | @@ -1771,6 +1777,7 @@ const DumpTzir = struct { | ... | @@ -1771,6 +1777,7 @@ const DumpTzir = struct { |
| 1771 | 1777 | ||
| 1772 | .add, | 1778 | .add, |
| 1773 | .sub, | 1779 | .sub, |
| 1780 | .mul, | ||
| 1774 | .cmp_lt, | 1781 | .cmp_lt, |
| 1775 | .cmp_lte, | 1782 | .cmp_lte, |
| 1776 | .cmp_eq, | 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,6 +131,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 131 | .typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?), | 131 | .typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?), |
| 132 | .typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?), | 132 | .typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?), |
| 133 | .optional_type => return zirOptionalType(mod, scope, old_inst.castTag(.optional_type).?), | 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 | .optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true), | 135 | .optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true), |
| 135 | .optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false), | 136 | .optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false), |
| 136 | .optional_payload_safe_ptr => return zirOptionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_safe_ptr).?, true), | 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,6 +1094,16 @@ fn zirOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerE |
| 1093 | return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type)); | 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 | fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst { | 1107 | fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst { |
| 1097 | const tracy = trace(@src()); | 1108 | const tracy = trace(@src()); |
| 1098 | defer tracy.end(); | 1109 | defer tracy.end(); |
| ... | @@ -1154,7 +1165,7 @@ fn zirErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) InnerError | ... | @@ -1154,7 +1165,7 @@ fn zirErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) InnerError |
| 1154 | 1165 | ||
| 1155 | for (inst.positionals.fields) |field_name| { | 1166 | for (inst.positionals.fields) |field_name| { |
| 1156 | const entry = try mod.getErrorValue(field_name); | 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 | return mod.fail(scope, inst.base.src, "duplicate error: '{s}'", .{field_name}); | 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,7 +1196,79 @@ fn zirErrorValue(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorValue) InnerE |
| 1185 | fn zirMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | 1196 | fn zirMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { |
| 1186 | const tracy = trace(@src()); | 1197 | const tracy = trace(@src()); |
| 1187 | defer tracy.end(); | 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 | fn zirEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst { | 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,6 +2158,7 @@ fn zirArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError! |
| 2075 | const ir_tag = switch (inst.base.tag) { | 2158 | const ir_tag = switch (inst.base.tag) { |
| 2076 | .add => Inst.Tag.add, | 2159 | .add => Inst.Tag.add, |
| 2077 | .sub => Inst.Tag.sub, | 2160 | .sub => Inst.Tag.sub, |
| 2161 | .mul => Inst.Tag.mul, | ||
| 2078 | else => return mod.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{s}''", .{@tagName(inst.base.tag)}), | 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,6 +27,8 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 27 | \\#define FOO = | 27 | \\#define FOO = |
| 28 | \\#define PtrToPtr64(p) ((void *POINTER_64) p) | 28 | \\#define PtrToPtr64(p) ((void *POINTER_64) p) |
| 29 | \\#define STRUC_ALIGNED_STACK_COPY(t,s) ((CONST t *)(s)) | 29 | \\#define STRUC_ALIGNED_STACK_COPY(t,s) ((CONST t *)(s)) |
| 30 | \\#define bar = 0x | ||
| 31 | \\#define baz = 0b | ||
| 30 | \\int main(void) {} | 32 | \\int main(void) {} |
| 31 | , ""); | 33 | , ""); |
| 32 | 34 |
test/stage1/behavior.zig+1-1| ... | @@ -141,5 +141,5 @@ comptime { | ... | @@ -141,5 +141,5 @@ comptime { |
| 141 | _ = @import("behavior/while.zig"); | 141 | _ = @import("behavior/while.zig"); |
| 142 | _ = @import("behavior/widening.zig"); | 142 | _ = @import("behavior/widening.zig"); |
| 143 | _ = @import("behavior/src.zig"); | 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,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,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,7 +985,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 985 | "Hello, World!\n", | 985 | "Hello, World!\n", |
| 986 | ); | 986 | ); |
| 987 | try case.files.append(.{ | 987 | try case.files.append(.{ |
| 988 | .src = | 988 | .src = |
| 989 | \\pub fn print() void { | 989 | \\pub fn print() void { |
| 990 | \\ asm volatile ("syscall" | 990 | \\ asm volatile ("syscall" |
| 991 | \\ : | 991 | \\ : |
| ... | @@ -1525,4 +1525,37 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -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,6 +268,10 @@ const known_options = [_]KnownOpt{ |
| 268 | .name = "MD", | 268 | .name = "MD", |
| 269 | .ident = "dep_file", | 269 | .ident = "dep_file", |
| 270 | }, | 270 | }, |
| 271 | .{ | ||
| 272 | .name = "write-dependencies", | ||
| 273 | .ident = "dep_file", | ||
| 274 | }, | ||
| 271 | .{ | 275 | .{ |
| 272 | .name = "MV", | 276 | .name = "MV", |
| 273 | .ident = "dep_file", | 277 | .ident = "dep_file", |
| ... | @@ -284,18 +288,30 @@ const known_options = [_]KnownOpt{ | ... | @@ -284,18 +288,30 @@ const known_options = [_]KnownOpt{ |
| 284 | .name = "MG", | 288 | .name = "MG", |
| 285 | .ident = "dep_file", | 289 | .ident = "dep_file", |
| 286 | }, | 290 | }, |
| 291 | .{ | ||
| 292 | .name = "print-missing-file-dependencies", | ||
| 293 | .ident = "dep_file", | ||
| 294 | }, | ||
| 287 | .{ | 295 | .{ |
| 288 | .name = "MJ", | 296 | .name = "MJ", |
| 289 | .ident = "dep_file", | 297 | .ident = "dep_file", |
| 290 | }, | 298 | }, |
| 291 | .{ | 299 | .{ |
| 292 | .name = "MM", | 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 | .name = "MMD", | 308 | .name = "MMD", |
| 297 | .ident = "dep_file", | 309 | .ident = "dep_file", |
| 298 | }, | 310 | }, |
| 311 | .{ | ||
| 312 | .name = "write-user-dependencies", | ||
| 313 | .ident = "dep_file", | ||
| 314 | }, | ||
| 299 | .{ | 315 | .{ |
| 300 | .name = "MP", | 316 | .name = "MP", |
| 301 | .ident = "dep_file", | 317 | .ident = "dep_file", |