| author | |
| committer | |
| log | 281a7baaeac6b6b3c8c78124f5e484f7ee101cf0 |
| tree | a8ca0725a42e8940197064a4e6485e2523b9926b |
| parent | 8f469c11275e60f5f1a8ae08fc7596ba366eda16 |
| parent | 175adc0bd738c2e3a55bb71c6a53dcc920c203ba |
Wanted to make sure those new test cases still pass.
Also grab that CI fix so we can get those green check marks.32 files changed, 1087 insertions(+), 502 deletions(-)
CMakeLists.txt+15-7| ... | ... | @@ -89,6 +89,7 @@ set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries |
| 89 | 89 | set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary") |
| 90 | 90 | set(ZIG_SINGLE_THREADED off CACHE BOOL "limit the zig compiler to use only 1 thread") |
| 91 | 91 | set(ZIG_OMIT_STAGE2 off CACHE BOOL "omit the stage2 backend from stage1") |
| 92 | set(ZIG_ENABLE_LOGGING off CACHE BOOL "enable logging") | |
| 92 | 93 | |
| 93 | 94 | if("${ZIG_TARGET_TRIPLE}" STREQUAL "native") |
| 94 | 95 | set(ZIG_USE_LLVM_CONFIG ON CACHE BOOL "use llvm-config to find LLVM libraries") |
| ... | ... | @@ -607,6 +608,12 @@ else() |
| 607 | 608 | set(ZIG_OMIT_STAGE2_BOOL "false") |
| 608 | 609 | endif() |
| 609 | 610 | |
| 611 | if(ZIG_ENABLE_LOGGING) | |
| 612 | set(ZIG_ENABLE_LOGGING_BOOL "true") | |
| 613 | else() | |
| 614 | set(ZIG_ENABLE_LOGGING_BOOL "false") | |
| 615 | endif() | |
| 616 | ||
| 610 | 617 | configure_file ( |
| 611 | 618 | "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in" |
| 612 | 619 | "${ZIG_CONFIG_H_OUT}" |
| ... | ... | @@ -735,12 +742,14 @@ if(MSVC OR MINGW) |
| 735 | 742 | target_link_libraries(zigstage1 LINK_PUBLIC version) |
| 736 | 743 | endif() |
| 737 | 744 | |
| 738 | add_executable(zig0 ${ZIG0_SOURCES}) | |
| 739 | set_target_properties(zig0 PROPERTIES | |
| 740 | COMPILE_FLAGS ${EXE_CFLAGS} | |
| 741 | LINK_FLAGS ${EXE_LDFLAGS} | |
| 742 | ) | |
| 743 | target_link_libraries(zig0 zigstage1) | |
| 745 | if("${ZIG_EXECUTABLE}" STREQUAL "") | |
| 746 | add_executable(zig0 ${ZIG0_SOURCES}) | |
| 747 | set_target_properties(zig0 PROPERTIES | |
| 748 | COMPILE_FLAGS ${EXE_CFLAGS} | |
| 749 | LINK_FLAGS ${EXE_LDFLAGS} | |
| 750 | ) | |
| 751 | target_link_libraries(zig0 zigstage1) | |
| 752 | endif() | |
| 744 | 753 | |
| 745 | 754 | if(MSVC) |
| 746 | 755 | set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.obj") |
| ... | ... | @@ -789,7 +798,6 @@ if("${ZIG_EXECUTABLE}" STREQUAL "") |
| 789 | 798 | else() |
| 790 | 799 | add_custom_command( |
| 791 | 800 | OUTPUT "${ZIG1_OBJECT}" |
| 792 | BYPRODUCTS "${ZIG1_OBJECT}" | |
| 793 | 801 | COMMAND "${ZIG_EXECUTABLE}" "build-obj" ${BUILD_ZIG1_ARGS} |
| 794 | 802 | DEPENDS ${ZIG_STAGE2_SOURCES} |
| 795 | 803 | COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}" |
ci/azure/macos_arm64_script created+132| ... | ... | @@ -0,0 +1,132 @@ |
| 1 | #!/bin/sh | |
| 2 | ||
| 3 | set -x | |
| 4 | set -e | |
| 5 | ||
| 6 | brew install s3cmd ninja gnu-tar | |
| 7 | ||
| 8 | ZIGDIR="$(pwd)" | |
| 9 | ARCH="aarch64" | |
| 10 | # {product}-{os}{sdk_version}-{arch}-{llvm_version}-{cmake_build_type} | |
| 11 | CACHE_HOST_BASENAME="llvm-macos10.15-x86_64-11.0.1-release" | |
| 12 | CACHE_ARM64_BASENAME="llvm-macos11.0-arm64-11.0.1-release" | |
| 13 | PREFIX_HOST="$HOME/$CACHE_HOST_BASENAME" | |
| 14 | PREFIX_ARM64="$HOME/$CACHE_ARM64_BASENAME" | |
| 15 | JOBS="-j2" | |
| 16 | ||
| 17 | rm -rf $PREFIX | |
| 18 | cd $HOME | |
| 19 | wget -nv "https://ziglang.org/deps/$CACHE_HOST_BASENAME.tar.xz" | |
| 20 | wget -nv "https://ziglang.org/deps/$CACHE_ARM64_BASENAME.tar.xz" | |
| 21 | ||
| 22 | gtar xf "$CACHE_HOST_BASENAME.tar.xz" | |
| 23 | gtar xf "$CACHE_ARM64_BASENAME.tar.xz" | |
| 24 | ||
| 25 | cd $ZIGDIR | |
| 26 | ||
| 27 | # Make the `zig version` number consistent. | |
| 28 | # This will affect the cmake command below. | |
| 29 | git config core.abbrev 9 | |
| 30 | git fetch --unshallow || true | |
| 31 | git fetch --tags | |
| 32 | ||
| 33 | # Select xcode: latest version found on vmImage macOS-10.15 . | |
| 34 | DEVELOPER_DIR=/Applications/Xcode_12.4.app | |
| 35 | ||
| 36 | export ZIG_LOCAL_CACHE_DIR="$ZIGDIR/zig-cache" | |
| 37 | export ZIG_GLOBAL_CACHE_DIR="$ZIGDIR/zig-cache" | |
| 38 | ||
| 39 | # Build zig for host and use `Debug` type to make builds a little faster. | |
| 40 | ||
| 41 | cd $ZIGDIR | |
| 42 | mkdir build.host | |
| 43 | cd build.host | |
| 44 | cmake -G "Ninja" .. \ | |
| 45 | -DCMAKE_INSTALL_PREFIX="$(pwd)/release" \ | |
| 46 | -DCMAKE_PREFIX_PATH="$PREFIX_HOST" \ | |
| 47 | -DCMAKE_BUILD_TYPE="Debug" \ | |
| 48 | -DZIG_STATIC="OFF" | |
| 49 | ||
| 50 | # Build but do not install. | |
| 51 | ninja $JOBS | |
| 52 | ||
| 53 | ZIG_EXE="$ZIGDIR/build.host/zig" | |
| 54 | ||
| 55 | # Build zig for arm64 target. | |
| 56 | # - use `Release` type for published tarballs | |
| 57 | # - ad-hoc codesign with linker | |
| 58 | # - note: apple quarantine of downloads (eg. via safari) still apply | |
| 59 | ||
| 60 | cd $ZIGDIR | |
| 61 | mkdir build.arm64 | |
| 62 | cd build.arm64 | |
| 63 | cmake -G "Ninja" .. \ | |
| 64 | -DCMAKE_INSTALL_PREFIX="$(pwd)/release" \ | |
| 65 | -DCMAKE_PREFIX_PATH="$PREFIX_ARM64" \ | |
| 66 | -DCMAKE_BUILD_TYPE="Release" \ | |
| 67 | -DCMAKE_CROSSCOMPILING="True" \ | |
| 68 | -DCMAKE_SYSTEM_NAME="Darwin" \ | |
| 69 | -DCMAKE_C_FLAGS="-arch arm64" \ | |
| 70 | -DCMAKE_CXX_FLAGS="-arch arm64" \ | |
| 71 | -DCMAKE_EXE_LINKER_FLAGS="-lz -Xlinker -adhoc_codesign" \ | |
| 72 | -DZIG_USE_LLVM_CONFIG="OFF" \ | |
| 73 | -DZIG_EXECUTABLE="$ZIG_EXE" \ | |
| 74 | -DZIG_TARGET_TRIPLE="${ARCH}-macos" \ | |
| 75 | -DZIG_STATIC="OFF" | |
| 76 | ||
| 77 | ninja $JOBS install | |
| 78 | ||
| 79 | # Disable test because binary is foreign arch. | |
| 80 | #release/bin/zig build test | |
| 81 | ||
| 82 | if [ "${BUILD_REASON}" != "PullRequest" ]; then | |
| 83 | mv ../LICENSE release/ | |
| 84 | ||
| 85 | # We do not run test suite but still need langref. | |
| 86 | mkdir -p release/docs | |
| 87 | $ZIG_EXE run ../doc/docgen.zig -- $ZIG_EXE ../doc/langref.html.in release/docs/langref.html | |
| 88 | ||
| 89 | # Produce the experimental std lib documentation. | |
| 90 | mkdir -p release/docs/std | |
| 91 | $ZIG_EXE test ../lib/std/std.zig \ | |
| 92 | --override-lib-dir ../lib \ | |
| 93 | -femit-docs=release/docs/std \ | |
| 94 | -fno-emit-bin | |
| 95 | ||
| 96 | # Remove the unnecessary bin dir in $prefix/bin/zig | |
| 97 | mv release/bin/zig release/ | |
| 98 | rmdir release/bin | |
| 99 | ||
| 100 | # Remove the unnecessary zig dir in $prefix/lib/zig/std/std.zig | |
| 101 | mv release/lib/zig release/lib2 | |
| 102 | rmdir release/lib | |
| 103 | mv release/lib2 release/lib | |
| 104 | ||
| 105 | VERSION=$($ZIG_EXE version) | |
| 106 | DIRNAME="zig-macos-$ARCH-$VERSION" | |
| 107 | TARBALL="$DIRNAME.tar.xz" | |
| 108 | gtar cJf "$TARBALL" release/ --owner=root --sort=name --transform="s,^release,${DIRNAME}," | |
| 109 | ln "$TARBALL" "$BUILD_ARTIFACTSTAGINGDIRECTORY/." | |
| 110 | ||
| 111 | mv "$DOWNLOADSECUREFILE_SECUREFILEPATH" "$HOME/.s3cfg" | |
| 112 | s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/ | |
| 113 | ||
| 114 | SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1) | |
| 115 | BYTESIZE=$(wc -c < $TARBALL) | |
| 116 | ||
| 117 | JSONFILE="macos-$GITBRANCH.json" | |
| 118 | touch $JSONFILE | |
| 119 | echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE | |
| 120 | echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE | |
| 121 | echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE | |
| 122 | ||
| 123 | s3cmd put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE" | |
| 124 | s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$ARCH-macos-$VERSION.json" | |
| 125 | ||
| 126 | # `set -x` causes these variables to be mangled. | |
| 127 | # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html | |
| 128 | set +x | |
| 129 | echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL" | |
| 130 | echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM" | |
| 131 | echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE" | |
| 132 | fi |
ci/azure/pipelines.yml+14-1| ... | ... | @@ -12,6 +12,19 @@ jobs: |
| 12 | 12 | - script: ci/azure/macos_script |
| 13 | 13 | name: main |
| 14 | 14 | displayName: 'Build and test' |
| 15 | - job: BuildMacOS_arm64 | |
| 16 | pool: | |
| 17 | vmImage: 'macOS-10.15' | |
| 18 | ||
| 19 | timeoutInMinutes: 60 | |
| 20 | ||
| 21 | steps: | |
| 22 | - task: DownloadSecureFile@1 | |
| 23 | inputs: | |
| 24 | secureFile: s3cfg | |
| 25 | - script: ci/azure/macos_arm64_script | |
| 26 | name: main | |
| 27 | displayName: 'Build and cross-compile' | |
| 15 | 28 | - job: BuildLinux |
| 16 | 29 | pool: |
| 17 | 30 | vmImage: 'ubuntu-18.04' |
| ... | ... | @@ -31,7 +44,7 @@ jobs: |
| 31 | 44 | timeoutInMinutes: 360 |
| 32 | 45 | steps: |
| 33 | 46 | - powershell: | |
| 34 | (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2021-01-05/msys2-base-x86_64-20210105.sfx.exe", "sfx.exe") | |
| 47 | (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2021-02-28/msys2-base-x86_64-20210228.sfx.exe", "sfx.exe") | |
| 35 | 48 | .\sfx.exe -y -o\ |
| 36 | 49 | del sfx.exe |
| 37 | 50 | displayName: Download/Extract/Install MSYS2 |
ci/azure/windows_msvc_install+1-1| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | set -x |
| 4 | 4 | set -e |
| 5 | 5 | |
| 6 | pacman -Su --needed --noconfirm | |
| 6 | pacman -Suy --needed --noconfirm | |
| 7 | 7 | pacman -S --needed --noconfirm wget p7zip python3-pip tar xz |
| 8 | 8 | |
| 9 | 9 | pip install s3cmd |
doc/langref.html.in+3-3| ... | ... | @@ -9952,9 +9952,9 @@ export fn decode_base_64( |
| 9952 | 9952 | ) usize { |
| 9953 | 9953 | const src = source_ptr[0..source_len]; |
| 9954 | 9954 | const dest = dest_ptr[0..dest_len]; |
| 9955 | const base64_decoder = base64.standard_decoder_unsafe; | |
| 9956 | const decoded_size = base64_decoder.calcSize(src); | |
| 9957 | base64_decoder.decode(dest[0..decoded_size], src); | |
| 9955 | const base64_decoder = base64.standard.Decoder; | |
| 9956 | const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable; | |
| 9957 | base64_decoder.decode(dest[0..decoded_size], src) catch unreachable; | |
| 9958 | 9958 | return decoded_size; |
| 9959 | 9959 | } |
| 9960 | 9960 | {#code_end#} |
lib/std/array_hash_map.zig+13-13| ... | ... | @@ -687,8 +687,9 @@ pub fn ArrayHashMapUnmanaged( |
| 687 | 687 | |
| 688 | 688 | /// Removes the last inserted `Entry` in the hash map and returns it. |
| 689 | 689 | pub fn pop(self: *Self) Entry { |
| 690 | const top = self.entries.pop(); | |
| 690 | const top = self.entries.items[self.entries.items.len - 1]; | |
| 691 | 691 | _ = self.removeWithHash(top.key, top.hash, .index_only); |
| 692 | self.entries.items.len -= 1; | |
| 692 | 693 | return top; |
| 693 | 694 | } |
| 694 | 695 | |
| ... | ... | @@ -1258,19 +1259,18 @@ test "pop" { |
| 1258 | 1259 | var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator); |
| 1259 | 1260 | defer map.deinit(); |
| 1260 | 1261 | |
| 1261 | testing.expect((try map.fetchPut(1, 11)) == null); | |
| 1262 | testing.expect((try map.fetchPut(2, 22)) == null); | |
| 1263 | testing.expect((try map.fetchPut(3, 33)) == null); | |
| 1264 | testing.expect((try map.fetchPut(4, 44)) == null); | |
| 1262 | // Insert just enough entries so that the map expands. Afterwards, | |
| 1263 | // pop all entries out of the map. | |
| 1265 | 1264 | |
| 1266 | const pop1 = map.pop(); | |
| 1267 | testing.expect(pop1.key == 4 and pop1.value == 44); | |
| 1268 | const pop2 = map.pop(); | |
| 1269 | testing.expect(pop2.key == 3 and pop2.value == 33); | |
| 1270 | const pop3 = map.pop(); | |
| 1271 | testing.expect(pop3.key == 2 and pop3.value == 22); | |
| 1272 | const pop4 = map.pop(); | |
| 1273 | testing.expect(pop4.key == 1 and pop4.value == 11); | |
| 1265 | var i: i32 = 0; | |
| 1266 | while (i < 9) : (i += 1) { | |
| 1267 | testing.expect((try map.fetchPut(i, i)) == null); | |
| 1268 | } | |
| 1269 | ||
| 1270 | while (i > 0) : (i -= 1) { | |
| 1271 | const pop = map.pop(); | |
| 1272 | testing.expect(pop.key == i - 1 and pop.value == i - 1); | |
| 1273 | } | |
| 1274 | 1274 | } |
| 1275 | 1275 | |
| 1276 | 1276 | test "reIndex" { |
lib/std/base64.zig+322-324| ... | ... | @@ -8,454 +8,452 @@ const assert = std.debug.assert; |
| 8 | 8 | const testing = std.testing; |
| 9 | 9 | const mem = std.mem; |
| 10 | 10 | |
| 11 | pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
| 12 | pub const standard_pad_char = '='; | |
| 13 | pub const standard_encoder = Base64Encoder.init(standard_alphabet_chars, standard_pad_char); | |
| 11 | pub const Error = error{ | |
| 12 | InvalidCharacter, | |
| 13 | InvalidPadding, | |
| 14 | NoSpaceLeft, | |
| 15 | }; | |
| 16 | ||
| 17 | /// Base64 codecs | |
| 18 | pub const Codecs = struct { | |
| 19 | alphabet_chars: [64]u8, | |
| 20 | pad_char: ?u8, | |
| 21 | decoderWithIgnore: fn (ignore: []const u8) Base64DecoderWithIgnore, | |
| 22 | Encoder: Base64Encoder, | |
| 23 | Decoder: Base64Decoder, | |
| 24 | }; | |
| 25 | ||
| 26 | pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".*; | |
| 27 | fn standardBase64DecoderWithIgnore(ignore: []const u8) Base64DecoderWithIgnore { | |
| 28 | return Base64DecoderWithIgnore.init(standard_alphabet_chars, '=', ignore); | |
| 29 | } | |
| 30 | ||
| 31 | /// Standard Base64 codecs, with padding | |
| 32 | pub const standard = Codecs{ | |
| 33 | .alphabet_chars = standard_alphabet_chars, | |
| 34 | .pad_char = '=', | |
| 35 | .decoderWithIgnore = standardBase64DecoderWithIgnore, | |
| 36 | .Encoder = Base64Encoder.init(standard_alphabet_chars, '='), | |
| 37 | .Decoder = Base64Decoder.init(standard_alphabet_chars, '='), | |
| 38 | }; | |
| 39 | ||
| 40 | /// Standard Base64 codecs, without padding | |
| 41 | pub const standard_no_pad = Codecs{ | |
| 42 | .alphabet_chars = standard_alphabet_chars, | |
| 43 | .pad_char = null, | |
| 44 | .decoderWithIgnore = standardBase64DecoderWithIgnore, | |
| 45 | .Encoder = Base64Encoder.init(standard_alphabet_chars, null), | |
| 46 | .Decoder = Base64Decoder.init(standard_alphabet_chars, null), | |
| 47 | }; | |
| 48 | ||
| 49 | pub const url_safe_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*; | |
| 50 | fn urlSafeBase64DecoderWithIgnore(ignore: []const u8) Base64DecoderWithIgnore { | |
| 51 | return Base64DecoderWithIgnore.init(url_safe_alphabet_chars, null, ignore); | |
| 52 | } | |
| 53 | ||
| 54 | /// URL-safe Base64 codecs, with padding | |
| 55 | pub const url_safe = Codecs{ | |
| 56 | .alphabet_chars = url_safe_alphabet_chars, | |
| 57 | .pad_char = '=', | |
| 58 | .decoderWithIgnore = urlSafeBase64DecoderWithIgnore, | |
| 59 | .Encoder = Base64Encoder.init(url_safe_alphabet_chars, '='), | |
| 60 | .Decoder = Base64Decoder.init(url_safe_alphabet_chars, '='), | |
| 61 | }; | |
| 62 | ||
| 63 | /// URL-safe Base64 codecs, without padding | |
| 64 | pub const url_safe_no_pad = Codecs{ | |
| 65 | .alphabet_chars = url_safe_alphabet_chars, | |
| 66 | .pad_char = null, | |
| 67 | .decoderWithIgnore = urlSafeBase64DecoderWithIgnore, | |
| 68 | .Encoder = Base64Encoder.init(url_safe_alphabet_chars, null), | |
| 69 | .Decoder = Base64Decoder.init(url_safe_alphabet_chars, null), | |
| 70 | }; | |
| 71 | ||
| 72 | // Backwards compatibility | |
| 73 | ||
| 74 | /// Deprecated - Use `standard.pad_char` | |
| 75 | pub const standard_pad_char = standard.pad_char; | |
| 76 | /// Deprecated - Use `standard.Encoder` | |
| 77 | pub const standard_encoder = standard.Encoder; | |
| 78 | /// Deprecated - Use `standard.Decoder` | |
| 79 | pub const standard_decoder = standard.Decoder; | |
| 14 | 80 | |
| 15 | 81 | pub const Base64Encoder = struct { |
| 16 | alphabet_chars: []const u8, | |
| 17 | pad_char: u8, | |
| 82 | alphabet_chars: [64]u8, | |
| 83 | pad_char: ?u8, | |
| 18 | 84 | |
| 19 | /// a bunch of assertions, then simply pass the data right through. | |
| 20 | pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64Encoder { | |
| 85 | /// A bunch of assertions, then simply pass the data right through. | |
| 86 | pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Encoder { | |
| 21 | 87 | assert(alphabet_chars.len == 64); |
| 22 | 88 | var char_in_alphabet = [_]bool{false} ** 256; |
| 23 | 89 | for (alphabet_chars) |c| { |
| 24 | 90 | assert(!char_in_alphabet[c]); |
| 25 | assert(c != pad_char); | |
| 91 | assert(pad_char == null or c != pad_char.?); | |
| 26 | 92 | char_in_alphabet[c] = true; |
| 27 | 93 | } |
| 28 | ||
| 29 | 94 | return Base64Encoder{ |
| 30 | 95 | .alphabet_chars = alphabet_chars, |
| 31 | 96 | .pad_char = pad_char, |
| 32 | 97 | }; |
| 33 | 98 | } |
| 34 | 99 | |
| 35 | /// ceil(source_len * 4/3) | |
| 36 | pub fn calcSize(source_len: usize) usize { | |
| 37 | return @divTrunc(source_len + 2, 3) * 4; | |
| 100 | /// Compute the encoded length | |
| 101 | pub fn calcSize(encoder: *const Base64Encoder, source_len: usize) usize { | |
| 102 | if (encoder.pad_char != null) { | |
| 103 | return @divTrunc(source_len + 2, 3) * 4; | |
| 104 | } else { | |
| 105 | const leftover = source_len % 3; | |
| 106 | return @divTrunc(source_len, 3) * 4 + @divTrunc(leftover * 4 + 2, 3); | |
| 107 | } | |
| 38 | 108 | } |
| 39 | 109 | |
| 40 | /// dest.len must be what you get from ::calcSize. | |
| 110 | /// dest.len must at least be what you get from ::calcSize. | |
| 41 | 111 | pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 { |
| 42 | assert(dest.len >= Base64Encoder.calcSize(source.len)); | |
| 43 | ||
| 44 | var i: usize = 0; | |
| 45 | var out_index: usize = 0; | |
| 46 | while (i + 2 < source.len) : (i += 3) { | |
| 47 | dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f]; | |
| 48 | out_index += 1; | |
| 49 | ||
| 50 | dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)]; | |
| 51 | out_index += 1; | |
| 52 | ||
| 53 | dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) | ((source[i + 2] & 0xc0) >> 6)]; | |
| 54 | out_index += 1; | |
| 55 | ||
| 56 | dest[out_index] = encoder.alphabet_chars[source[i + 2] & 0x3f]; | |
| 57 | out_index += 1; | |
| 112 | const out_len = encoder.calcSize(source.len); | |
| 113 | assert(dest.len >= out_len); | |
| 114 | ||
| 115 | const nibbles = source.len / 3; | |
| 116 | const leftover = source.len - 3 * nibbles; | |
| 117 | ||
| 118 | var acc: u12 = 0; | |
| 119 | var acc_len: u4 = 0; | |
| 120 | var out_idx: usize = 0; | |
| 121 | for (source) |v| { | |
| 122 | acc = (acc << 8) + v; | |
| 123 | acc_len += 8; | |
| 124 | while (acc_len >= 6) { | |
| 125 | acc_len -= 6; | |
| 126 | dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc >> acc_len))]; | |
| 127 | out_idx += 1; | |
| 128 | } | |
| 58 | 129 | } |
| 59 | ||
| 60 | if (i < source.len) { | |
| 61 | dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f]; | |
| 62 | out_index += 1; | |
| 63 | ||
| 64 | if (i + 1 == source.len) { | |
| 65 | dest[out_index] = encoder.alphabet_chars[(source[i] & 0x3) << 4]; | |
| 66 | out_index += 1; | |
| 67 | ||
| 68 | dest[out_index] = encoder.pad_char; | |
| 69 | out_index += 1; | |
| 70 | } else { | |
| 71 | dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)]; | |
| 72 | out_index += 1; | |
| 73 | ||
| 74 | dest[out_index] = encoder.alphabet_chars[(source[i + 1] & 0xf) << 2]; | |
| 75 | out_index += 1; | |
| 130 | if (acc_len > 0) { | |
| 131 | dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc << 6 - acc_len))]; | |
| 132 | out_idx += 1; | |
| 133 | } | |
| 134 | if (encoder.pad_char) |pad_char| { | |
| 135 | for (dest[out_idx..]) |*pad| { | |
| 136 | pad.* = pad_char; | |
| 76 | 137 | } |
| 77 | ||
| 78 | dest[out_index] = encoder.pad_char; | |
| 79 | out_index += 1; | |
| 80 | 138 | } |
| 81 | return dest[0..out_index]; | |
| 139 | return dest[0..out_len]; | |
| 82 | 140 | } |
| 83 | 141 | }; |
| 84 | 142 | |
| 85 | pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char); | |
| 86 | ||
| 87 | 143 | pub const Base64Decoder = struct { |
| 144 | const invalid_char: u8 = 0xff; | |
| 145 | ||
| 88 | 146 | /// e.g. 'A' => 0. |
| 89 | /// undefined for any value not in the 64 alphabet chars. | |
| 147 | /// `invalid_char` for any value not in the 64 alphabet chars. | |
| 90 | 148 | char_to_index: [256]u8, |
| 149 | pad_char: ?u8, | |
| 91 | 150 | |
| 92 | /// true only for the 64 chars in the alphabet, not the pad char. | |
| 93 | char_in_alphabet: [256]bool, | |
| 94 | pad_char: u8, | |
| 95 | ||
| 96 | pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64Decoder { | |
| 97 | assert(alphabet_chars.len == 64); | |
| 98 | ||
| 151 | pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Decoder { | |
| 99 | 152 | var result = Base64Decoder{ |
| 100 | .char_to_index = undefined, | |
| 101 | .char_in_alphabet = [_]bool{false} ** 256, | |
| 153 | .char_to_index = [_]u8{invalid_char} ** 256, | |
| 102 | 154 | .pad_char = pad_char, |
| 103 | 155 | }; |
| 104 | 156 | |
| 157 | var char_in_alphabet = [_]bool{false} ** 256; | |
| 105 | 158 | for (alphabet_chars) |c, i| { |
| 106 | assert(!result.char_in_alphabet[c]); | |
| 107 | assert(c != pad_char); | |
| 159 | assert(!char_in_alphabet[c]); | |
| 160 | assert(pad_char == null or c != pad_char.?); | |
| 108 | 161 | |
| 109 | 162 | result.char_to_index[c] = @intCast(u8, i); |
| 110 | result.char_in_alphabet[c] = true; | |
| 163 | char_in_alphabet[c] = true; | |
| 111 | 164 | } |
| 165 | return result; | |
| 166 | } | |
| 112 | 167 | |
| 168 | /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding. | |
| 169 | /// `InvalidPadding` is returned if the input length is not valid. | |
| 170 | pub fn calcSizeUpperBound(decoder: *const Base64Decoder, source_len: usize) Error!usize { | |
| 171 | var result = source_len / 4 * 3; | |
| 172 | const leftover = source_len % 4; | |
| 173 | if (decoder.pad_char != null) { | |
| 174 | if (leftover % 4 != 0) return error.InvalidPadding; | |
| 175 | } else { | |
| 176 | if (leftover % 4 == 1) return error.InvalidPadding; | |
| 177 | result += leftover * 3 / 4; | |
| 178 | } | |
| 113 | 179 | return result; |
| 114 | 180 | } |
| 115 | 181 | |
| 116 | /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding. | |
| 117 | pub fn calcSize(decoder: *const Base64Decoder, source: []const u8) !usize { | |
| 118 | if (source.len % 4 != 0) return error.InvalidPadding; | |
| 119 | return calcDecodedSizeExactUnsafe(source, decoder.pad_char); | |
| 182 | /// Return the exact decoded size for a slice. | |
| 183 | /// `InvalidPadding` is returned if the input length is not valid. | |
| 184 | pub fn calcSizeForSlice(decoder: *const Base64Decoder, source: []const u8) Error!usize { | |
| 185 | const source_len = source.len; | |
| 186 | var result = try decoder.calcSizeUpperBound(source_len); | |
| 187 | if (decoder.pad_char) |pad_char| { | |
| 188 | if (source_len >= 1 and source[source_len - 1] == pad_char) result -= 1; | |
| 189 | if (source_len >= 2 and source[source_len - 2] == pad_char) result -= 1; | |
| 190 | } | |
| 191 | return result; | |
| 120 | 192 | } |
| 121 | 193 | |
| 122 | 194 | /// dest.len must be what you get from ::calcSize. |
| 123 | 195 | /// invalid characters result in error.InvalidCharacter. |
| 124 | 196 | /// invalid padding results in error.InvalidPadding. |
| 125 | pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) !void { | |
| 126 | assert(dest.len == (decoder.calcSize(source) catch unreachable)); | |
| 127 | assert(source.len % 4 == 0); | |
| 128 | ||
| 129 | var src_cursor: usize = 0; | |
| 130 | var dest_cursor: usize = 0; | |
| 131 | ||
| 132 | while (src_cursor < source.len) : (src_cursor += 4) { | |
| 133 | if (!decoder.char_in_alphabet[source[src_cursor + 0]]) return error.InvalidCharacter; | |
| 134 | if (!decoder.char_in_alphabet[source[src_cursor + 1]]) return error.InvalidCharacter; | |
| 135 | if (src_cursor < source.len - 4 or source[src_cursor + 3] != decoder.pad_char) { | |
| 136 | // common case | |
| 137 | if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter; | |
| 138 | if (!decoder.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter; | |
| 139 | dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4; | |
| 140 | dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2; | |
| 141 | dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 | decoder.char_to_index[source[src_cursor + 3]]; | |
| 142 | dest_cursor += 3; | |
| 143 | } else if (source[src_cursor + 2] != decoder.pad_char) { | |
| 144 | // one pad char | |
| 145 | if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter; | |
| 146 | dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4; | |
| 147 | dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2; | |
| 148 | if (decoder.char_to_index[source[src_cursor + 2]] << 6 != 0) return error.InvalidPadding; | |
| 149 | dest_cursor += 2; | |
| 150 | } else { | |
| 151 | // two pad chars | |
| 152 | dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4; | |
| 153 | if (decoder.char_to_index[source[src_cursor + 1]] << 4 != 0) return error.InvalidPadding; | |
| 154 | dest_cursor += 1; | |
| 197 | pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) Error!void { | |
| 198 | if (decoder.pad_char != null and source.len % 4 != 0) return error.InvalidPadding; | |
| 199 | var acc: u12 = 0; | |
| 200 | var acc_len: u4 = 0; | |
| 201 | var dest_idx: usize = 0; | |
| 202 | var leftover_idx: ?usize = null; | |
| 203 | for (source) |c, src_idx| { | |
| 204 | const d = decoder.char_to_index[c]; | |
| 205 | if (d == invalid_char) { | |
| 206 | if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter; | |
| 207 | leftover_idx = src_idx; | |
| 208 | break; | |
| 209 | } | |
| 210 | acc = (acc << 6) + d; | |
| 211 | acc_len += 6; | |
| 212 | if (acc_len >= 8) { | |
| 213 | acc_len -= 8; | |
| 214 | dest[dest_idx] = @truncate(u8, acc >> acc_len); | |
| 215 | dest_idx += 1; | |
| 155 | 216 | } |
| 156 | 217 | } |
| 157 | ||
| 158 | assert(src_cursor == source.len); | |
| 159 | assert(dest_cursor == dest.len); | |
| 218 | if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) { | |
| 219 | return error.InvalidPadding; | |
| 220 | } | |
| 221 | if (leftover_idx == null) return; | |
| 222 | var leftover = source[leftover_idx.?..]; | |
| 223 | if (decoder.pad_char) |pad_char| { | |
| 224 | const padding_len = acc_len / 2; | |
| 225 | var padding_chars: usize = 0; | |
| 226 | var i: usize = 0; | |
| 227 | for (leftover) |c| { | |
| 228 | if (c != pad_char) { | |
| 229 | return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding; | |
| 230 | } | |
| 231 | padding_chars += 1; | |
| 232 | } | |
| 233 | if (padding_chars != padding_len) return error.InvalidPadding; | |
| 234 | } | |
| 160 | 235 | } |
| 161 | 236 | }; |
| 162 | 237 | |
| 163 | 238 | pub const Base64DecoderWithIgnore = struct { |
| 164 | 239 | decoder: Base64Decoder, |
| 165 | 240 | char_is_ignored: [256]bool, |
| 166 | pub fn init(alphabet_chars: []const u8, pad_char: u8, ignore_chars: []const u8) Base64DecoderWithIgnore { | |
| 241 | ||
| 242 | pub fn init(alphabet_chars: [64]u8, pad_char: ?u8, ignore_chars: []const u8) Base64DecoderWithIgnore { | |
| 167 | 243 | var result = Base64DecoderWithIgnore{ |
| 168 | 244 | .decoder = Base64Decoder.init(alphabet_chars, pad_char), |
| 169 | 245 | .char_is_ignored = [_]bool{false} ** 256, |
| 170 | 246 | }; |
| 171 | ||
| 172 | 247 | for (ignore_chars) |c| { |
| 173 | assert(!result.decoder.char_in_alphabet[c]); | |
| 248 | assert(result.decoder.char_to_index[c] == Base64Decoder.invalid_char); | |
| 174 | 249 | assert(!result.char_is_ignored[c]); |
| 175 | 250 | assert(result.decoder.pad_char != c); |
| 176 | 251 | result.char_is_ignored[c] = true; |
| 177 | 252 | } |
| 178 | ||
| 179 | 253 | return result; |
| 180 | 254 | } |
| 181 | 255 | |
| 182 | /// If no characters end up being ignored or padding, this will be the exact decoded size. | |
| 183 | pub fn calcSizeUpperBound(encoded_len: usize) usize { | |
| 184 | return @divTrunc(encoded_len, 4) * 3; | |
| 256 | /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding | |
| 257 | /// `InvalidPadding` is returned if the input length is not valid. | |
| 258 | pub fn calcSizeUpperBound(decoder_with_ignore: *const Base64DecoderWithIgnore, source_len: usize) Error!usize { | |
| 259 | var result = source_len / 4 * 3; | |
| 260 | if (decoder_with_ignore.decoder.pad_char == null) { | |
| 261 | const leftover = source_len % 4; | |
| 262 | result += leftover * 3 / 4; | |
| 263 | } | |
| 264 | return result; | |
| 185 | 265 | } |
| 186 | 266 | |
| 187 | 267 | /// Invalid characters that are not ignored result in error.InvalidCharacter. |
| 188 | 268 | /// Invalid padding results in error.InvalidPadding. |
| 189 | /// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound. | |
| 269 | /// Decoding more data than can fit in dest results in error.NoSpaceLeft. See also ::calcSizeUpperBound. | |
| 190 | 270 | /// Returns the number of bytes written to dest. |
| 191 | pub fn decode(decoder_with_ignore: *const Base64DecoderWithIgnore, dest: []u8, source: []const u8) !usize { | |
| 271 | pub fn decode(decoder_with_ignore: *const Base64DecoderWithIgnore, dest: []u8, source: []const u8) Error!usize { | |
| 192 | 272 | const decoder = &decoder_with_ignore.decoder; |
| 193 | ||
| 194 | var src_cursor: usize = 0; | |
| 195 | var dest_cursor: usize = 0; | |
| 196 | ||
| 197 | while (true) { | |
| 198 | // get the next 4 chars, if available | |
| 199 | var next_4_chars: [4]u8 = undefined; | |
| 200 | var available_chars: usize = 0; | |
| 201 | var pad_char_count: usize = 0; | |
| 202 | while (available_chars < 4 and src_cursor < source.len) { | |
| 203 | var c = source[src_cursor]; | |
| 204 | src_cursor += 1; | |
| 205 | ||
| 206 | if (decoder.char_in_alphabet[c]) { | |
| 207 | // normal char | |
| 208 | next_4_chars[available_chars] = c; | |
| 209 | available_chars += 1; | |
| 210 | } else if (decoder_with_ignore.char_is_ignored[c]) { | |
| 211 | // we're told to skip this one | |
| 212 | continue; | |
| 213 | } else if (c == decoder.pad_char) { | |
| 214 | // the padding has begun. count the pad chars. | |
| 215 | pad_char_count += 1; | |
| 216 | while (src_cursor < source.len) { | |
| 217 | c = source[src_cursor]; | |
| 218 | src_cursor += 1; | |
| 219 | if (c == decoder.pad_char) { | |
| 220 | pad_char_count += 1; | |
| 221 | if (pad_char_count > 2) return error.InvalidCharacter; | |
| 222 | } else if (decoder_with_ignore.char_is_ignored[c]) { | |
| 223 | // we can even ignore chars during the padding | |
| 224 | continue; | |
| 225 | } else return error.InvalidCharacter; | |
| 226 | } | |
| 227 | break; | |
| 228 | } else return error.InvalidCharacter; | |
| 273 | var acc: u12 = 0; | |
| 274 | var acc_len: u4 = 0; | |
| 275 | var dest_idx: usize = 0; | |
| 276 | var leftover_idx: ?usize = null; | |
| 277 | for (source) |c, src_idx| { | |
| 278 | if (decoder_with_ignore.char_is_ignored[c]) continue; | |
| 279 | const d = decoder.char_to_index[c]; | |
| 280 | if (d == Base64Decoder.invalid_char) { | |
| 281 | if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter; | |
| 282 | leftover_idx = src_idx; | |
| 283 | break; | |
| 229 | 284 | } |
| 230 | ||
| 231 | switch (available_chars) { | |
| 232 | 4 => { | |
| 233 | // common case | |
| 234 | if (dest_cursor + 3 > dest.len) return error.OutputTooSmall; | |
| 235 | assert(pad_char_count == 0); | |
| 236 | dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4; | |
| 237 | dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | decoder.char_to_index[next_4_chars[2]] >> 2; | |
| 238 | dest[dest_cursor + 2] = decoder.char_to_index[next_4_chars[2]] << 6 | decoder.char_to_index[next_4_chars[3]]; | |
| 239 | dest_cursor += 3; | |
| 240 | continue; | |
| 241 | }, | |
| 242 | 3 => { | |
| 243 | if (dest_cursor + 2 > dest.len) return error.OutputTooSmall; | |
| 244 | if (pad_char_count != 1) return error.InvalidPadding; | |
| 245 | dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4; | |
| 246 | dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | decoder.char_to_index[next_4_chars[2]] >> 2; | |
| 247 | if (decoder.char_to_index[next_4_chars[2]] << 6 != 0) return error.InvalidPadding; | |
| 248 | dest_cursor += 2; | |
| 249 | break; | |
| 250 | }, | |
| 251 | 2 => { | |
| 252 | if (dest_cursor + 1 > dest.len) return error.OutputTooSmall; | |
| 253 | if (pad_char_count != 2) return error.InvalidPadding; | |
| 254 | dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4; | |
| 255 | if (decoder.char_to_index[next_4_chars[1]] << 4 != 0) return error.InvalidPadding; | |
| 256 | dest_cursor += 1; | |
| 257 | break; | |
| 258 | }, | |
| 259 | 1 => { | |
| 260 | return error.InvalidPadding; | |
| 261 | }, | |
| 262 | 0 => { | |
| 263 | if (pad_char_count != 0) return error.InvalidPadding; | |
| 264 | break; | |
| 265 | }, | |
| 266 | else => unreachable, | |
| 285 | acc = (acc << 6) + d; | |
| 286 | acc_len += 6; | |
| 287 | if (acc_len >= 8) { | |
| 288 | if (dest_idx == dest.len) return error.NoSpaceLeft; | |
| 289 | acc_len -= 8; | |
| 290 | dest[dest_idx] = @truncate(u8, acc >> acc_len); | |
| 291 | dest_idx += 1; | |
| 267 | 292 | } |
| 268 | 293 | } |
| 269 | ||
| 270 | assert(src_cursor == source.len); | |
| 271 | ||
| 272 | return dest_cursor; | |
| 273 | } | |
| 274 | }; | |
| 275 | ||
| 276 | pub const standard_decoder_unsafe = Base64DecoderUnsafe.init(standard_alphabet_chars, standard_pad_char); | |
| 277 | ||
| 278 | pub const Base64DecoderUnsafe = struct { | |
| 279 | /// e.g. 'A' => 0. | |
| 280 | /// undefined for any value not in the 64 alphabet chars. | |
| 281 | char_to_index: [256]u8, | |
| 282 | pad_char: u8, | |
| 283 | ||
| 284 | pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64DecoderUnsafe { | |
| 285 | assert(alphabet_chars.len == 64); | |
| 286 | var result = Base64DecoderUnsafe{ | |
| 287 | .char_to_index = undefined, | |
| 288 | .pad_char = pad_char, | |
| 289 | }; | |
| 290 | for (alphabet_chars) |c, i| { | |
| 291 | assert(c != pad_char); | |
| 292 | result.char_to_index[c] = @intCast(u8, i); | |
| 294 | if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) { | |
| 295 | return error.InvalidPadding; | |
| 293 | 296 | } |
| 294 | return result; | |
| 295 | } | |
| 296 | ||
| 297 | /// The source buffer must be valid. | |
| 298 | pub fn calcSize(decoder: *const Base64DecoderUnsafe, source: []const u8) usize { | |
| 299 | return calcDecodedSizeExactUnsafe(source, decoder.pad_char); | |
| 300 | } | |
| 301 | ||
| 302 | /// dest.len must be what you get from ::calcDecodedSizeExactUnsafe. | |
| 303 | /// invalid characters or padding will result in undefined values. | |
| 304 | pub fn decode(decoder: *const Base64DecoderUnsafe, dest: []u8, source: []const u8) void { | |
| 305 | assert(dest.len == decoder.calcSize(source)); | |
| 306 | ||
| 307 | var src_index: usize = 0; | |
| 308 | var dest_index: usize = 0; | |
| 309 | var in_buf_len: usize = source.len; | |
| 310 | ||
| 311 | while (in_buf_len > 0 and source[in_buf_len - 1] == decoder.pad_char) { | |
| 312 | in_buf_len -= 1; | |
| 297 | const padding_len = acc_len / 2; | |
| 298 | if (leftover_idx == null) { | |
| 299 | if (decoder.pad_char != null and padding_len != 0) return error.InvalidPadding; | |
| 300 | return dest_idx; | |
| 313 | 301 | } |
| 314 | ||
| 315 | while (in_buf_len > 4) { | |
| 316 | dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4; | |
| 317 | dest_index += 1; | |
| 318 | ||
| 319 | dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2; | |
| 320 | dest_index += 1; | |
| 321 | ||
| 322 | dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]]; | |
| 323 | dest_index += 1; | |
| 324 | ||
| 325 | src_index += 4; | |
| 326 | in_buf_len -= 4; | |
| 327 | } | |
| 328 | ||
| 329 | if (in_buf_len > 1) { | |
| 330 | dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4; | |
| 331 | dest_index += 1; | |
| 332 | } | |
| 333 | if (in_buf_len > 2) { | |
| 334 | dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2; | |
| 335 | dest_index += 1; | |
| 336 | } | |
| 337 | if (in_buf_len > 3) { | |
| 338 | dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]]; | |
| 339 | dest_index += 1; | |
| 302 | var leftover = source[leftover_idx.?..]; | |
| 303 | if (decoder.pad_char) |pad_char| { | |
| 304 | var padding_chars: usize = 0; | |
| 305 | var i: usize = 0; | |
| 306 | for (leftover) |c| { | |
| 307 | if (decoder_with_ignore.char_is_ignored[c]) continue; | |
| 308 | if (c != pad_char) { | |
| 309 | return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding; | |
| 310 | } | |
| 311 | padding_chars += 1; | |
| 312 | } | |
| 313 | if (padding_chars != padding_len) return error.InvalidPadding; | |
| 340 | 314 | } |
| 315 | return dest_idx; | |
| 341 | 316 | } |
| 342 | 317 | }; |
| 343 | 318 | |
| 344 | fn calcDecodedSizeExactUnsafe(source: []const u8, pad_char: u8) usize { | |
| 345 | if (source.len == 0) return 0; | |
| 346 | var result = @divExact(source.len, 4) * 3; | |
| 347 | if (source[source.len - 1] == pad_char) { | |
| 348 | result -= 1; | |
| 349 | if (source[source.len - 2] == pad_char) { | |
| 350 | result -= 1; | |
| 351 | } | |
| 352 | } | |
| 353 | return result; | |
| 354 | } | |
| 355 | ||
| 356 | 319 | test "base64" { |
| 357 | 320 | @setEvalBranchQuota(8000); |
| 358 | 321 | testBase64() catch unreachable; |
| 359 | comptime (testBase64() catch unreachable); | |
| 322 | comptime testAllApis(standard, "comptime", "Y29tcHRpbWU=") catch unreachable; | |
| 323 | } | |
| 324 | ||
| 325 | test "base64 url_safe_no_pad" { | |
| 326 | @setEvalBranchQuota(8000); | |
| 327 | testBase64UrlSafeNoPad() catch unreachable; | |
| 328 | comptime testAllApis(url_safe_no_pad, "comptime", "Y29tcHRpbWU") catch unreachable; | |
| 360 | 329 | } |
| 361 | 330 | |
| 362 | 331 | fn testBase64() !void { |
| 363 | try testAllApis("", ""); | |
| 364 | try testAllApis("f", "Zg=="); | |
| 365 | try testAllApis("fo", "Zm8="); | |
| 366 | try testAllApis("foo", "Zm9v"); | |
| 367 | try testAllApis("foob", "Zm9vYg=="); | |
| 368 | try testAllApis("fooba", "Zm9vYmE="); | |
| 369 | try testAllApis("foobar", "Zm9vYmFy"); | |
| 370 | ||
| 371 | try testDecodeIgnoreSpace("", " "); | |
| 372 | try testDecodeIgnoreSpace("f", "Z g= ="); | |
| 373 | try testDecodeIgnoreSpace("fo", " Zm8="); | |
| 374 | try testDecodeIgnoreSpace("foo", "Zm9v "); | |
| 375 | try testDecodeIgnoreSpace("foob", "Zm9vYg = = "); | |
| 376 | try testDecodeIgnoreSpace("fooba", "Zm9v YmE="); | |
| 377 | try testDecodeIgnoreSpace("foobar", " Z m 9 v Y m F y "); | |
| 332 | const codecs = standard; | |
| 333 | ||
| 334 | try testAllApis(codecs, "", ""); | |
| 335 | try testAllApis(codecs, "f", "Zg=="); | |
| 336 | try testAllApis(codecs, "fo", "Zm8="); | |
| 337 | try testAllApis(codecs, "foo", "Zm9v"); | |
| 338 | try testAllApis(codecs, "foob", "Zm9vYg=="); | |
| 339 | try testAllApis(codecs, "fooba", "Zm9vYmE="); | |
| 340 | try testAllApis(codecs, "foobar", "Zm9vYmFy"); | |
| 341 | ||
| 342 | try testDecodeIgnoreSpace(codecs, "", " "); | |
| 343 | try testDecodeIgnoreSpace(codecs, "f", "Z g= ="); | |
| 344 | try testDecodeIgnoreSpace(codecs, "fo", " Zm8="); | |
| 345 | try testDecodeIgnoreSpace(codecs, "foo", "Zm9v "); | |
| 346 | try testDecodeIgnoreSpace(codecs, "foob", "Zm9vYg = = "); | |
| 347 | try testDecodeIgnoreSpace(codecs, "fooba", "Zm9v YmE="); | |
| 348 | try testDecodeIgnoreSpace(codecs, "foobar", " Z m 9 v Y m F y "); | |
| 349 | ||
| 350 | // test getting some api errors | |
| 351 | try testError(codecs, "A", error.InvalidPadding); | |
| 352 | try testError(codecs, "AA", error.InvalidPadding); | |
| 353 | try testError(codecs, "AAA", error.InvalidPadding); | |
| 354 | try testError(codecs, "A..A", error.InvalidCharacter); | |
| 355 | try testError(codecs, "AA=A", error.InvalidPadding); | |
| 356 | try testError(codecs, "AA/=", error.InvalidPadding); | |
| 357 | try testError(codecs, "A/==", error.InvalidPadding); | |
| 358 | try testError(codecs, "A===", error.InvalidPadding); | |
| 359 | try testError(codecs, "====", error.InvalidPadding); | |
| 360 | ||
| 361 | try testNoSpaceLeftError(codecs, "AA=="); | |
| 362 | try testNoSpaceLeftError(codecs, "AAA="); | |
| 363 | try testNoSpaceLeftError(codecs, "AAAA"); | |
| 364 | try testNoSpaceLeftError(codecs, "AAAAAA=="); | |
| 365 | } | |
| 366 | ||
| 367 | fn testBase64UrlSafeNoPad() !void { | |
| 368 | const codecs = url_safe_no_pad; | |
| 369 | ||
| 370 | try testAllApis(codecs, "", ""); | |
| 371 | try testAllApis(codecs, "f", "Zg"); | |
| 372 | try testAllApis(codecs, "fo", "Zm8"); | |
| 373 | try testAllApis(codecs, "foo", "Zm9v"); | |
| 374 | try testAllApis(codecs, "foob", "Zm9vYg"); | |
| 375 | try testAllApis(codecs, "fooba", "Zm9vYmE"); | |
| 376 | try testAllApis(codecs, "foobar", "Zm9vYmFy"); | |
| 377 | ||
| 378 | try testDecodeIgnoreSpace(codecs, "", " "); | |
| 379 | try testDecodeIgnoreSpace(codecs, "f", "Z g "); | |
| 380 | try testDecodeIgnoreSpace(codecs, "fo", " Zm8"); | |
| 381 | try testDecodeIgnoreSpace(codecs, "foo", "Zm9v "); | |
| 382 | try testDecodeIgnoreSpace(codecs, "foob", "Zm9vYg "); | |
| 383 | try testDecodeIgnoreSpace(codecs, "fooba", "Zm9v YmE"); | |
| 384 | try testDecodeIgnoreSpace(codecs, "foobar", " Z m 9 v Y m F y "); | |
| 378 | 385 | |
| 379 | 386 | // test getting some api errors |
| 380 | try testError("A", error.InvalidPadding); | |
| 381 | try testError("AA", error.InvalidPadding); | |
| 382 | try testError("AAA", error.InvalidPadding); | |
| 383 | try testError("A..A", error.InvalidCharacter); | |
| 384 | try testError("AA=A", error.InvalidCharacter); | |
| 385 | try testError("AA/=", error.InvalidPadding); | |
| 386 | try testError("A/==", error.InvalidPadding); | |
| 387 | try testError("A===", error.InvalidCharacter); | |
| 388 | try testError("====", error.InvalidCharacter); | |
| 389 | ||
| 390 | try testOutputTooSmallError("AA=="); | |
| 391 | try testOutputTooSmallError("AAA="); | |
| 392 | try testOutputTooSmallError("AAAA"); | |
| 393 | try testOutputTooSmallError("AAAAAA=="); | |
| 387 | try testError(codecs, "A", error.InvalidPadding); | |
| 388 | try testError(codecs, "AAA=", error.InvalidCharacter); | |
| 389 | try testError(codecs, "A..A", error.InvalidCharacter); | |
| 390 | try testError(codecs, "AA=A", error.InvalidCharacter); | |
| 391 | try testError(codecs, "AA/=", error.InvalidCharacter); | |
| 392 | try testError(codecs, "A/==", error.InvalidCharacter); | |
| 393 | try testError(codecs, "A===", error.InvalidCharacter); | |
| 394 | try testError(codecs, "====", error.InvalidCharacter); | |
| 395 | ||
| 396 | try testNoSpaceLeftError(codecs, "AA"); | |
| 397 | try testNoSpaceLeftError(codecs, "AAA"); | |
| 398 | try testNoSpaceLeftError(codecs, "AAAA"); | |
| 399 | try testNoSpaceLeftError(codecs, "AAAAAA"); | |
| 394 | 400 | } |
| 395 | 401 | |
| 396 | fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void { | |
| 402 | fn testAllApis(codecs: Codecs, expected_decoded: []const u8, expected_encoded: []const u8) !void { | |
| 397 | 403 | // Base64Encoder |
| 398 | 404 | { |
| 399 | 405 | var buffer: [0x100]u8 = undefined; |
| 400 | const encoded = standard_encoder.encode(&buffer, expected_decoded); | |
| 406 | const encoded = codecs.Encoder.encode(&buffer, expected_decoded); | |
| 401 | 407 | testing.expectEqualSlices(u8, expected_encoded, encoded); |
| 402 | 408 | } |
| 403 | 409 | |
| 404 | 410 | // Base64Decoder |
| 405 | 411 | { |
| 406 | 412 | var buffer: [0x100]u8 = undefined; |
| 407 | var decoded = buffer[0..try standard_decoder.calcSize(expected_encoded)]; | |
| 408 | try standard_decoder.decode(decoded, expected_encoded); | |
| 413 | var decoded = buffer[0..try codecs.Decoder.calcSizeForSlice(expected_encoded)]; | |
| 414 | try codecs.Decoder.decode(decoded, expected_encoded); | |
| 409 | 415 | testing.expectEqualSlices(u8, expected_decoded, decoded); |
| 410 | 416 | } |
| 411 | 417 | |
| 412 | 418 | // Base64DecoderWithIgnore |
| 413 | 419 | { |
| 414 | const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, ""); | |
| 420 | const decoder_ignore_nothing = codecs.decoderWithIgnore(""); | |
| 415 | 421 | var buffer: [0x100]u8 = undefined; |
| 416 | var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)]; | |
| 417 | var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded); | |
| 422 | var decoded = buffer[0..try decoder_ignore_nothing.calcSizeUpperBound(expected_encoded.len)]; | |
| 423 | var written = try decoder_ignore_nothing.decode(decoded, expected_encoded); | |
| 418 | 424 | testing.expect(written <= decoded.len); |
| 419 | 425 | testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]); |
| 420 | 426 | } |
| 421 | ||
| 422 | // Base64DecoderUnsafe | |
| 423 | { | |
| 424 | var buffer: [0x100]u8 = undefined; | |
| 425 | var decoded = buffer[0..standard_decoder_unsafe.calcSize(expected_encoded)]; | |
| 426 | standard_decoder_unsafe.decode(decoded, expected_encoded); | |
| 427 | testing.expectEqualSlices(u8, expected_decoded, decoded); | |
| 428 | } | |
| 429 | 427 | } |
| 430 | 428 | |
| 431 | fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !void { | |
| 432 | const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " "); | |
| 429 | fn testDecodeIgnoreSpace(codecs: Codecs, expected_decoded: []const u8, encoded: []const u8) !void { | |
| 430 | const decoder_ignore_space = codecs.decoderWithIgnore(" "); | |
| 433 | 431 | var buffer: [0x100]u8 = undefined; |
| 434 | var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)]; | |
| 435 | var written = try standard_decoder_ignore_space.decode(decoded, encoded); | |
| 432 | var decoded = buffer[0..try decoder_ignore_space.calcSizeUpperBound(encoded.len)]; | |
| 433 | var written = try decoder_ignore_space.decode(decoded, encoded); | |
| 436 | 434 | testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]); |
| 437 | 435 | } |
| 438 | 436 | |
| 439 | fn testError(encoded: []const u8, expected_err: anyerror) !void { | |
| 440 | const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " "); | |
| 437 | fn testError(codecs: Codecs, encoded: []const u8, expected_err: anyerror) !void { | |
| 438 | const decoder_ignore_space = codecs.decoderWithIgnore(" "); | |
| 441 | 439 | var buffer: [0x100]u8 = undefined; |
| 442 | if (standard_decoder.calcSize(encoded)) |decoded_size| { | |
| 440 | if (codecs.Decoder.calcSizeForSlice(encoded)) |decoded_size| { | |
| 443 | 441 | var decoded = buffer[0..decoded_size]; |
| 444 | if (standard_decoder.decode(decoded, encoded)) |_| { | |
| 442 | if (codecs.Decoder.decode(decoded, encoded)) |_| { | |
| 445 | 443 | return error.ExpectedError; |
| 446 | 444 | } else |err| if (err != expected_err) return err; |
| 447 | 445 | } else |err| if (err != expected_err) return err; |
| 448 | 446 | |
| 449 | if (standard_decoder_ignore_space.decode(buffer[0..], encoded)) |_| { | |
| 447 | if (decoder_ignore_space.decode(buffer[0..], encoded)) |_| { | |
| 450 | 448 | return error.ExpectedError; |
| 451 | 449 | } else |err| if (err != expected_err) return err; |
| 452 | 450 | } |
| 453 | 451 | |
| 454 | fn testOutputTooSmallError(encoded: []const u8) !void { | |
| 455 | const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " "); | |
| 452 | fn testNoSpaceLeftError(codecs: Codecs, encoded: []const u8) !void { | |
| 453 | const decoder_ignore_space = codecs.decoderWithIgnore(" "); | |
| 456 | 454 | var buffer: [0x100]u8 = undefined; |
| 457 | var decoded = buffer[0 .. calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1]; | |
| 458 | if (standard_decoder_ignore_space.decode(decoded, encoded)) |_| { | |
| 455 | var decoded = buffer[0 .. (try codecs.Decoder.calcSizeForSlice(encoded)) - 1]; | |
| 456 | if (decoder_ignore_space.decode(decoded, encoded)) |_| { | |
| 459 | 457 | return error.ExpectedError; |
| 460 | } else |err| if (err != error.OutputTooSmall) return err; | |
| 458 | } else |err| if (err != error.NoSpaceLeft) return err; | |
| 461 | 459 | } |
lib/std/build.zig+7-16| ... | ... | @@ -51,7 +51,7 @@ pub const Builder = struct { |
| 51 | 51 | default_step: *Step, |
| 52 | 52 | env_map: *BufMap, |
| 53 | 53 | top_level_steps: ArrayList(*TopLevelStep), |
| 54 | install_prefix: ?[]const u8, | |
| 54 | install_prefix: []const u8, | |
| 55 | 55 | dest_dir: ?[]const u8, |
| 56 | 56 | lib_dir: []const u8, |
| 57 | 57 | exe_dir: []const u8, |
| ... | ... | @@ -156,7 +156,7 @@ pub const Builder = struct { |
| 156 | 156 | .default_step = undefined, |
| 157 | 157 | .env_map = env_map, |
| 158 | 158 | .search_prefixes = ArrayList([]const u8).init(allocator), |
| 159 | .install_prefix = null, | |
| 159 | .install_prefix = undefined, | |
| 160 | 160 | .lib_dir = undefined, |
| 161 | 161 | .exe_dir = undefined, |
| 162 | 162 | .h_dir = undefined, |
| ... | ... | @@ -190,22 +190,13 @@ pub const Builder = struct { |
| 190 | 190 | } |
| 191 | 191 | |
| 192 | 192 | /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file. |
| 193 | pub fn setInstallPrefix(self: *Builder, optional_prefix: ?[]const u8) void { | |
| 194 | self.install_prefix = optional_prefix; | |
| 195 | } | |
| 196 | ||
| 197 | /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file. | |
| 198 | pub fn resolveInstallPrefix(self: *Builder) void { | |
| 193 | pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8) void { | |
| 199 | 194 | if (self.dest_dir) |dest_dir| { |
| 200 | const install_prefix = self.install_prefix orelse "/usr"; | |
| 201 | self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, install_prefix }) catch unreachable; | |
| 195 | self.install_prefix = install_prefix orelse "/usr"; | |
| 196 | self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, self.install_prefix }) catch unreachable; | |
| 202 | 197 | } else { |
| 203 | const install_prefix = self.install_prefix orelse blk: { | |
| 204 | const p = self.cache_root; | |
| 205 | self.install_prefix = p; | |
| 206 | break :blk p; | |
| 207 | }; | |
| 208 | self.install_path = install_prefix; | |
| 198 | self.install_prefix = install_prefix orelse self.cache_root; | |
| 199 | self.install_path = self.install_prefix; | |
| 209 | 200 | } |
| 210 | 201 | self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable; |
| 211 | 202 | self.exe_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "bin" }) catch unreachable; |
lib/std/fmt.zig+7-2| ... | ... | @@ -1250,9 +1250,9 @@ fn formatDuration(ns: u64, comptime fmt: []const u8, options: std.fmt.FormatOpti |
| 1250 | 1250 | const kunits = ns_remaining * 1000 / unit.ns; |
| 1251 | 1251 | if (kunits >= 1000) { |
| 1252 | 1252 | try formatInt(kunits / 1000, 10, false, .{}, writer); |
| 1253 | if (kunits > 1000) { | |
| 1253 | const frac = kunits % 1000; | |
| 1254 | if (frac > 0) { | |
| 1254 | 1255 | // Write up to 3 decimal places |
| 1255 | const frac = kunits % 1000; | |
| 1256 | 1256 | var buf = [_]u8{ '.', 0, 0, 0 }; |
| 1257 | 1257 | _ = formatIntBuf(buf[1..], frac, 10, false, .{ .fill = '0', .width = 3 }); |
| 1258 | 1258 | var end: usize = 4; |
| ... | ... | @@ -1286,9 +1286,14 @@ test "fmtDuration" { |
| 1286 | 1286 | .{ .s = "1us", .d = std.time.ns_per_us }, |
| 1287 | 1287 | .{ .s = "1.45us", .d = 1450 }, |
| 1288 | 1288 | .{ .s = "1.5us", .d = 3 * std.time.ns_per_us / 2 }, |
| 1289 | .{ .s = "14.5us", .d = 14500 }, | |
| 1290 | .{ .s = "145us", .d = 145000 }, | |
| 1289 | 1291 | .{ .s = "999.999us", .d = std.time.ns_per_ms - 1 }, |
| 1290 | 1292 | .{ .s = "1ms", .d = std.time.ns_per_ms + 1 }, |
| 1291 | 1293 | .{ .s = "1.5ms", .d = 3 * std.time.ns_per_ms / 2 }, |
| 1294 | .{ .s = "1.11ms", .d = 1110000 }, | |
| 1295 | .{ .s = "1.111ms", .d = 1111000 }, | |
| 1296 | .{ .s = "1.111ms", .d = 1111100 }, | |
| 1292 | 1297 | .{ .s = "999.999ms", .d = std.time.ns_per_s - 1 }, |
| 1293 | 1298 | .{ .s = "1s", .d = std.time.ns_per_s }, |
| 1294 | 1299 | .{ .s = "59.999s", .d = std.time.ns_per_min - 1 }, |
lib/std/fs.zig+5-5| ... | ... | @@ -50,13 +50,13 @@ pub const MAX_PATH_BYTES = switch (builtin.os.tag) { |
| 50 | 50 | else => @compileError("Unsupported OS"), |
| 51 | 51 | }; |
| 52 | 52 | |
| 53 | pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; | |
| 53 | pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*; | |
| 54 | 54 | |
| 55 | 55 | /// Base64 encoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem. |
| 56 | pub const base64_encoder = base64.Base64Encoder.init(base64_alphabet, base64.standard_pad_char); | |
| 56 | pub const base64_encoder = base64.Base64Encoder.init(base64_alphabet, null); | |
| 57 | 57 | |
| 58 | 58 | /// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem. |
| 59 | pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, base64.standard_pad_char); | |
| 59 | pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, null); | |
| 60 | 60 | |
| 61 | 61 | /// Whether or not async file system syscalls need a dedicated thread because the operating |
| 62 | 62 | /// system does not support non-blocking I/O on the file system. |
| ... | ... | @@ -77,7 +77,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: |
| 77 | 77 | const dirname = path.dirname(new_path) orelse "."; |
| 78 | 78 | |
| 79 | 79 | var rand_buf: [AtomicFile.RANDOM_BYTES]u8 = undefined; |
| 80 | const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len)); | |
| 80 | const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64_encoder.calcSize(rand_buf.len)); | |
| 81 | 81 | defer allocator.free(tmp_path); |
| 82 | 82 | mem.copy(u8, tmp_path[0..], dirname); |
| 83 | 83 | tmp_path[dirname.len] = path.sep; |
| ... | ... | @@ -142,7 +142,7 @@ pub const AtomicFile = struct { |
| 142 | 142 | const InitError = File.OpenError; |
| 143 | 143 | |
| 144 | 144 | const RANDOM_BYTES = 12; |
| 145 | const TMP_PATH_LEN = base64.Base64Encoder.calcSize(RANDOM_BYTES); | |
| 145 | const TMP_PATH_LEN = base64_encoder.calcSize(RANDOM_BYTES); | |
| 146 | 146 | |
| 147 | 147 | /// Note that the `Dir.atomicFile` API may be more handy than this lower-level function. |
| 148 | 148 | pub fn init( |
lib/std/hash/auto_hash.zig+1-1| ... | ... | @@ -95,7 +95,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { |
| 95 | 95 | .EnumLiteral, |
| 96 | 96 | .Frame, |
| 97 | 97 | .Float, |
| 98 | => @compileError("cannot hash this type"), | |
| 98 | => @compileError("unable to hash type " ++ @typeName(Key)), | |
| 99 | 99 | |
| 100 | 100 | // Help the optimizer see that hashing an int is easy by inlining! |
| 101 | 101 | // TODO Check if the situation is better after #561 is resolved. |
lib/std/mem.zig+19| ... | ... | @@ -1373,6 +1373,20 @@ test "mem.tokenize (multibyte)" { |
| 1373 | 1373 | testing.expect(it.next() == null); |
| 1374 | 1374 | } |
| 1375 | 1375 | |
| 1376 | test "mem.tokenize (reset)" { | |
| 1377 | var it = tokenize(" abc def ghi ", " "); | |
| 1378 | testing.expect(eql(u8, it.next().?, "abc")); | |
| 1379 | testing.expect(eql(u8, it.next().?, "def")); | |
| 1380 | testing.expect(eql(u8, it.next().?, "ghi")); | |
| 1381 | ||
| 1382 | it.reset(); | |
| 1383 | ||
| 1384 | testing.expect(eql(u8, it.next().?, "abc")); | |
| 1385 | testing.expect(eql(u8, it.next().?, "def")); | |
| 1386 | testing.expect(eql(u8, it.next().?, "ghi")); | |
| 1387 | testing.expect(it.next() == null); | |
| 1388 | } | |
| 1389 | ||
| 1376 | 1390 | /// Returns an iterator that iterates over the slices of `buffer` that |
| 1377 | 1391 | /// are separated by bytes in `delimiter`. |
| 1378 | 1392 | /// split("abc|def||ghi", "|") |
| ... | ... | @@ -1471,6 +1485,11 @@ pub const TokenIterator = struct { |
| 1471 | 1485 | return self.buffer[index..]; |
| 1472 | 1486 | } |
| 1473 | 1487 | |
| 1488 | /// Resets the iterator to the initial token. | |
| 1489 | pub fn reset(self: *TokenIterator) void { | |
| 1490 | self.index = 0; | |
| 1491 | } | |
| 1492 | ||
| 1474 | 1493 | fn isSplitByte(self: TokenIterator, byte: u8) bool { |
| 1475 | 1494 | for (self.delimiter_bytes) |delimiter_byte| { |
| 1476 | 1495 | if (byte == delimiter_byte) { |
lib/std/os.zig+1| ... | ... | @@ -5610,6 +5610,7 @@ pub fn recvfrom( |
| 5610 | 5610 | EAGAIN => return error.WouldBlock, |
| 5611 | 5611 | ENOMEM => return error.SystemResources, |
| 5612 | 5612 | ECONNREFUSED => return error.ConnectionRefused, |
| 5613 | ECONNRESET => return error.ConnectionResetByPeer, | |
| 5613 | 5614 | else => |err| return unexpectedErrno(err), |
| 5614 | 5615 | } |
| 5615 | 5616 | } |
lib/std/os/linux/mips.zig+37| ... | ... | @@ -115,6 +115,9 @@ pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, |
| 115 | 115 | ); |
| 116 | 116 | } |
| 117 | 117 | |
| 118 | // NOTE: The o32 calling convention requires the callee to reserve 16 bytes for | |
| 119 | // the first four arguments even though they're passed in $a0-$a3. | |
| 120 | ||
| 118 | 121 | pub fn syscall6( |
| 119 | 122 | number: SYS, |
| 120 | 123 | arg1: usize, |
| ... | ... | @@ -146,6 +149,40 @@ pub fn syscall6( |
| 146 | 149 | ); |
| 147 | 150 | } |
| 148 | 151 | |
| 152 | pub fn syscall7( | |
| 153 | number: SYS, | |
| 154 | arg1: usize, | |
| 155 | arg2: usize, | |
| 156 | arg3: usize, | |
| 157 | arg4: usize, | |
| 158 | arg5: usize, | |
| 159 | arg6: usize, | |
| 160 | arg7: usize, | |
| 161 | ) usize { | |
| 162 | return asm volatile ( | |
| 163 | \\ .set noat | |
| 164 | \\ subu $sp, $sp, 32 | |
| 165 | \\ sw %[arg5], 16($sp) | |
| 166 | \\ sw %[arg6], 20($sp) | |
| 167 | \\ sw %[arg7], 24($sp) | |
| 168 | \\ syscall | |
| 169 | \\ addu $sp, $sp, 32 | |
| 170 | \\ blez $7, 1f | |
| 171 | \\ subu $2, $0, $2 | |
| 172 | \\ 1: | |
| 173 | : [ret] "={$2}" (-> usize) | |
| 174 | : [number] "{$2}" (@enumToInt(number)), | |
| 175 | [arg1] "{$4}" (arg1), | |
| 176 | [arg2] "{$5}" (arg2), | |
| 177 | [arg3] "{$6}" (arg3), | |
| 178 | [arg4] "{$7}" (arg4), | |
| 179 | [arg5] "r" (arg5), | |
| 180 | [arg6] "r" (arg6), | |
| 181 | [arg7] "r" (arg7) | |
| 182 | : "memory", "cc", "$7" | |
| 183 | ); | |
| 184 | } | |
| 185 | ||
| 149 | 186 | /// This matches the libc clone function. |
| 150 | 187 | pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; |
| 151 | 188 |
lib/std/os/uefi/tables/boot_services.zig+2-1| ... | ... | @@ -78,7 +78,8 @@ pub const BootServices = extern struct { |
| 78 | 78 | /// Returns an array of handles that support a specified protocol. |
| 79 | 79 | locateHandle: fn (LocateSearchType, ?*align(8) const Guid, ?*const c_void, *usize, [*]Handle) callconv(.C) Status, |
| 80 | 80 | |
| 81 | locateDevicePath: Status, // TODO | |
| 81 | /// Locates the handle to a device on the device path that supports the specified protocol | |
| 82 | locateDevicePath: fn (*align(8) const Guid, **const DevicePathProtocol, *?Handle) callconv(.C) Status, | |
| 82 | 83 | installConfigurationTable: Status, // TODO |
| 83 | 84 | |
| 84 | 85 | /// Loads an EFI image into memory. |
lib/std/os/windows/user32.zig+1-1| ... | ... | @@ -373,7 +373,7 @@ pub fn createWindowExA(dwExStyle: u32, lpClassName: [*:0]const u8, lpWindowName: |
| 373 | 373 | } |
| 374 | 374 | |
| 375 | 375 | pub extern "user32" fn CreateWindowExW(dwExStyle: DWORD, lpClassName: [*:0]const u16, lpWindowName: [*:0]const u16, dwStyle: DWORD, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?LPVOID) callconv(WINAPI) ?HWND; |
| 376 | pub var pfnCreateWindowExW: @TypeOf(RegisterClassExW) = undefined; | |
| 376 | pub var pfnCreateWindowExW: @TypeOf(CreateWindowExW) = undefined; | |
| 377 | 377 | pub fn createWindowExW(dwExStyle: u32, lpClassName: [*:0]const u16, lpWindowName: [*:0]const u16, dwStyle: u32, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?*c_void) !HWND { |
| 378 | 378 | const function = selectSymbol(CreateWindowExW, pfnCreateWindowExW, .win2k); |
| 379 | 379 | const window = function(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWindParent, hMenu, hInstance, lpParam); |
lib/std/special/build_runner.zig+4-4| ... | ... | @@ -60,6 +60,7 @@ pub fn main() !void { |
| 60 | 60 | const stderr_stream = io.getStdErr().writer(); |
| 61 | 61 | const stdout_stream = io.getStdOut().writer(); |
| 62 | 62 | |
| 63 | var install_prefix: ?[]const u8 = null; | |
| 63 | 64 | while (nextArg(args, &arg_idx)) |arg| { |
| 64 | 65 | if (mem.startsWith(u8, arg, "-D")) { |
| 65 | 66 | const option_contents = arg[2..]; |
| ... | ... | @@ -82,7 +83,7 @@ pub fn main() !void { |
| 82 | 83 | } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { |
| 83 | 84 | return usage(builder, false, stdout_stream); |
| 84 | 85 | } else if (mem.eql(u8, arg, "--prefix")) { |
| 85 | builder.install_prefix = nextArg(args, &arg_idx) orelse { | |
| 86 | install_prefix = nextArg(args, &arg_idx) orelse { | |
| 86 | 87 | warn("Expected argument after --prefix\n\n", .{}); |
| 87 | 88 | return usageAndErr(builder, false, stderr_stream); |
| 88 | 89 | }; |
| ... | ... | @@ -134,7 +135,7 @@ pub fn main() !void { |
| 134 | 135 | } |
| 135 | 136 | } |
| 136 | 137 | |
| 137 | builder.resolveInstallPrefix(); | |
| 138 | builder.resolveInstallPrefix(install_prefix); | |
| 138 | 139 | try runBuild(builder); |
| 139 | 140 | |
| 140 | 141 | if (builder.validateUserInputDidItFail()) |
| ... | ... | @@ -162,8 +163,7 @@ fn runBuild(builder: *Builder) anyerror!void { |
| 162 | 163 | fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void { |
| 163 | 164 | // run the build script to collect the options |
| 164 | 165 | if (!already_ran_build) { |
| 165 | builder.setInstallPrefix(null); | |
| 166 | builder.resolveInstallPrefix(); | |
| 166 | builder.resolveInstallPrefix(null); | |
| 167 | 167 | try runBuild(builder); |
| 168 | 168 | } |
| 169 | 169 |
lib/std/testing.zig+1-1| ... | ... | @@ -298,7 +298,7 @@ pub const TmpDir = struct { |
| 298 | 298 | sub_path: [sub_path_len]u8, |
| 299 | 299 | |
| 300 | 300 | const random_bytes_count = 12; |
| 301 | const sub_path_len = std.base64.Base64Encoder.calcSize(random_bytes_count); | |
| 301 | const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); | |
| 302 | 302 | |
| 303 | 303 | pub fn cleanup(self: *TmpDir) void { |
| 304 | 304 | self.dir.close(); |
src/Compilation.zig+17-6| ... | ... | @@ -3188,7 +3188,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 3188 | 3188 | id_symlink_basename, |
| 3189 | 3189 | &prev_digest_buf, |
| 3190 | 3190 | ) catch |err| blk: { |
| 3191 | log.debug("stage1 {s} new_digest={} error: {s}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) }); | |
| 3191 | log.debug("stage1 {s} new_digest={s} error: {s}", .{ | |
| 3192 | mod.root_pkg.root_src_path, | |
| 3193 | std.fmt.fmtSliceHexLower(&digest), | |
| 3194 | @errorName(err), | |
| 3195 | }); | |
| 3192 | 3196 | // Handle this as a cache miss. |
| 3193 | 3197 | break :blk prev_digest_buf[0..0]; |
| 3194 | 3198 | }; |
| ... | ... | @@ -3196,10 +3200,13 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 3196 | 3200 | if (!mem.eql(u8, prev_digest[0..digest.len], &digest)) |
| 3197 | 3201 | break :hit; |
| 3198 | 3202 | |
| 3199 | log.debug("stage1 {s} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest }); | |
| 3203 | log.debug("stage1 {s} digest={s} match - skipping invocation", .{ | |
| 3204 | mod.root_pkg.root_src_path, | |
| 3205 | std.fmt.fmtSliceHexLower(&digest), | |
| 3206 | }); | |
| 3200 | 3207 | var flags_bytes: [1]u8 = undefined; |
| 3201 | 3208 | _ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch { |
| 3202 | log.warn("bad cache stage1 digest: '{s}'", .{prev_digest}); | |
| 3209 | log.warn("bad cache stage1 digest: '{s}'", .{std.fmt.fmtSliceHexLower(prev_digest)}); | |
| 3203 | 3210 | break :hit; |
| 3204 | 3211 | }; |
| 3205 | 3212 | |
| ... | ... | @@ -3219,7 +3226,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 3219 | 3226 | mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]); |
| 3220 | 3227 | return; |
| 3221 | 3228 | } |
| 3222 | log.debug("stage1 {s} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest }); | |
| 3229 | log.debug("stage1 {s} prev_digest={s} new_digest={s}", .{ | |
| 3230 | mod.root_pkg.root_src_path, | |
| 3231 | std.fmt.fmtSliceHexLower(prev_digest), | |
| 3232 | std.fmt.fmtSliceHexLower(&digest), | |
| 3233 | }); | |
| 3223 | 3234 | man.unhit(prev_hash_state, input_file_count); |
| 3224 | 3235 | } |
| 3225 | 3236 | |
| ... | ... | @@ -3366,8 +3377,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 3366 | 3377 | // Update the small file with the digest. If it fails we can continue; it only |
| 3367 | 3378 | // means that the next invocation will have an unnecessary cache miss. |
| 3368 | 3379 | const stage1_flags_byte = @bitCast(u8, mod.stage1_flags); |
| 3369 | log.debug("stage1 {s} final digest={} flags={x}", .{ | |
| 3370 | mod.root_pkg.root_src_path, digest, stage1_flags_byte, | |
| 3380 | log.debug("stage1 {s} final digest={s} flags={x}", .{ | |
| 3381 | mod.root_pkg.root_src_path, std.fmt.fmtSliceHexLower(&digest), stage1_flags_byte, | |
| 3371 | 3382 | }); |
| 3372 | 3383 | var digest_plus_flags: [digest.len + 2]u8 = undefined; |
| 3373 | 3384 | digest_plus_flags[0..digest.len].* = digest; |
src/codegen/wasm.zig+41-3| ... | ... | @@ -94,7 +94,7 @@ pub const Context = struct { |
| 94 | 94 | return switch (ty.tag()) { |
| 95 | 95 | .f32 => wasm.valtype(.f32), |
| 96 | 96 | .f64 => wasm.valtype(.f64), |
| 97 | .u32, .i32 => wasm.valtype(.i32), | |
| 97 | .u32, .i32, .bool => wasm.valtype(.i32), | |
| 98 | 98 | .u64, .i64 => wasm.valtype(.i64), |
| 99 | 99 | else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}), |
| 100 | 100 | }; |
| ... | ... | @@ -207,6 +207,7 @@ pub const Context = struct { |
| 207 | 207 | .alloc => self.genAlloc(inst.castTag(.alloc).?), |
| 208 | 208 | .arg => self.genArg(inst.castTag(.arg).?), |
| 209 | 209 | .block => self.genBlock(inst.castTag(.block).?), |
| 210 | .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?), | |
| 210 | 211 | .br => self.genBr(inst.castTag(.br).?), |
| 211 | 212 | .call => self.genCall(inst.castTag(.call).?), |
| 212 | 213 | .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq), |
| ... | ... | @@ -220,9 +221,11 @@ pub const Context = struct { |
| 220 | 221 | .dbg_stmt => WValue.none, |
| 221 | 222 | .load => self.genLoad(inst.castTag(.load).?), |
| 222 | 223 | .loop => self.genLoop(inst.castTag(.loop).?), |
| 224 | .not => self.genNot(inst.castTag(.not).?), | |
| 223 | 225 | .ret => self.genRet(inst.castTag(.ret).?), |
| 224 | 226 | .retvoid => WValue.none, |
| 225 | 227 | .store => self.genStore(inst.castTag(.store).?), |
| 228 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), | |
| 226 | 229 | else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}), |
| 227 | 230 | }; |
| 228 | 231 | } |
| ... | ... | @@ -328,7 +331,7 @@ pub const Context = struct { |
| 328 | 331 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 329 | 332 | try leb.writeILEB128(writer, inst.val.toUnsignedInt()); |
| 330 | 333 | }, |
| 331 | .i32 => { | |
| 334 | .i32, .bool => { | |
| 332 | 335 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 333 | 336 | try leb.writeILEB128(writer, inst.val.toSignedInt()); |
| 334 | 337 | }, |
| ... | ... | @@ -413,7 +416,14 @@ pub const Context = struct { |
| 413 | 416 | |
| 414 | 417 | // insert blocks at the position of `offset` so |
| 415 | 418 | // the condition can jump to it |
| 416 | const offset = condition.code_offset; | |
| 419 | const offset = switch (condition) { | |
| 420 | .code_offset => |offset| offset, | |
| 421 | else => blk: { | |
| 422 | const offset = self.code.items.len; | |
| 423 | try self.emitWValue(condition); | |
| 424 | break :blk offset; | |
| 425 | }, | |
| 426 | }; | |
| 417 | 427 | const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty); |
| 418 | 428 | try self.startBlock(.block, block_ty, offset); |
| 419 | 429 | |
| ... | ... | @@ -522,4 +532,32 @@ pub const Context = struct { |
| 522 | 532 | |
| 523 | 533 | return .none; |
| 524 | 534 | } |
| 535 | ||
| 536 | fn genNot(self: *Context, not: *Inst.UnOp) InnerError!WValue { | |
| 537 | const offset = self.code.items.len; | |
| 538 | ||
| 539 | const operand = self.resolveInst(not.operand); | |
| 540 | try self.emitWValue(operand); | |
| 541 | ||
| 542 | // wasm does not have booleans nor the `not` instruction, therefore compare with 0 | |
| 543 | // to create the same logic | |
| 544 | const writer = self.code.writer(); | |
| 545 | try writer.writeByte(wasm.opcode(.i32_const)); | |
| 546 | try leb.writeILEB128(writer, @as(i32, 0)); | |
| 547 | ||
| 548 | try writer.writeByte(wasm.opcode(.i32_eq)); | |
| 549 | ||
| 550 | return WValue{ .code_offset = offset }; | |
| 551 | } | |
| 552 | ||
| 553 | fn genBreakpoint(self: *Context, breakpoint: *Inst.NoOp) InnerError!WValue { | |
| 554 | // unsupported by wasm itself. Can be implemented once we support DWARF | |
| 555 | // for wasm | |
| 556 | return .none; | |
| 557 | } | |
| 558 | ||
| 559 | fn genUnreachable(self: *Context, unreach: *Inst.NoOp) InnerError!WValue { | |
| 560 | try self.code.append(wasm.opcode(.@"unreachable")); | |
| 561 | return .none; | |
| 562 | } | |
| 525 | 563 | }; |
src/config.zig.in+1-1| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | pub const have_llvm = true; |
| 2 | 2 | pub const version: [:0]const u8 = "@ZIG_VERSION@"; |
| 3 | 3 | pub const semver = try @import("std").SemanticVersion.parse(version); |
| 4 | pub const enable_logging: bool = false; | |
| 4 | pub const enable_logging: bool = @ZIG_ENABLE_LOGGING_BOOL@; | |
| 5 | 5 | pub const enable_tracy = false; |
| 6 | 6 | pub const is_stage1 = true; |
| 7 | 7 | pub const skip_non_native = false; |
src/introspect.zig+8| ... | ... | @@ -61,6 +61,14 @@ pub fn findZigLibDirFromSelfExe( |
| 61 | 61 | |
| 62 | 62 | /// Caller owns returned memory. |
| 63 | 63 | pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 { |
| 64 | if (std.process.getEnvVarOwned(allocator, "ZIG_GLOBAL_CACHE_DIR")) |value| { | |
| 65 | if (value.len > 0) { | |
| 66 | return value; | |
| 67 | } else { | |
| 68 | allocator.free(value); | |
| 69 | } | |
| 70 | } else |_| {} | |
| 71 | ||
| 64 | 72 | const appname = "zig"; |
| 65 | 73 | |
| 66 | 74 | if (std.Target.current.os.tag != .windows) { |
src/link/MachO/Archive.zig+25-3| ... | ... | @@ -20,6 +20,11 @@ name: []u8, |
| 20 | 20 | |
| 21 | 21 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| 22 | 22 | |
| 23 | /// Parsed table of contents. | |
| 24 | /// Each symbol name points to a list of all definition | |
| 25 | /// sites within the current static archive. | |
| 26 | toc: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(u32)) = .{}, | |
| 27 | ||
| 23 | 28 | // Archive files start with the ARMAG identifying string. Then follows a |
| 24 | 29 | // `struct ar_hdr', and as many bytes of member file data as its `ar_size' |
| 25 | 30 | // member indicates, for each member file. |
| ... | ... | @@ -88,6 +93,11 @@ pub fn deinit(self: *Archive) void { |
| 88 | 93 | object.deinit(); |
| 89 | 94 | } |
| 90 | 95 | self.objects.deinit(self.allocator); |
| 96 | for (self.toc.items()) |*entry| { | |
| 97 | self.allocator.free(entry.key); | |
| 98 | entry.value.deinit(self.allocator); | |
| 99 | } | |
| 100 | self.toc.deinit(self.allocator); | |
| 91 | 101 | self.file.close(); |
| 92 | 102 | } |
| 93 | 103 | |
| ... | ... | @@ -159,8 +169,20 @@ fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 { |
| 159 | 169 | }; |
| 160 | 170 | const object_offset = try symtab_reader.readIntLittle(u32); |
| 161 | 171 | |
| 162 | // TODO Store the table of contents for later reuse. | |
| 172 | const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + n_strx)); | |
| 173 | const owned_name = try self.allocator.dupe(u8, sym_name); | |
| 174 | const res = try self.toc.getOrPut(self.allocator, owned_name); | |
| 175 | defer if (res.found_existing) self.allocator.free(owned_name); | |
| 176 | ||
| 177 | if (!res.found_existing) { | |
| 178 | res.entry.value = .{}; | |
| 179 | } | |
| 180 | ||
| 181 | try res.entry.value.append(self.allocator, object_offset); | |
| 163 | 182 | |
| 183 | // TODO This will go once we properly use archive's TOC to pick | |
| 184 | // an object which defines a missing symbol rather than pasting in | |
| 185 | // all of the objects always. | |
| 164 | 186 | // Here, we assume that symbols are NOT sorted in any way, and |
| 165 | 187 | // they point to objects in sequence. |
| 166 | 188 | if (object_offsets.items[last] != object_offset) { |
| ... | ... | @@ -248,8 +270,8 @@ fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 { |
| 248 | 270 | var n = try allocator.alloc(u8, len); |
| 249 | 271 | defer allocator.free(n); |
| 250 | 272 | try reader.readNoEof(n); |
| 251 | const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0)); | |
| 252 | name = try allocator.dupe(u8, n[0..actual_len.?]); | |
| 273 | const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0)) orelse n.len; | |
| 274 | name = try allocator.dupe(u8, n[0..actual_len]); | |
| 253 | 275 | }, |
| 254 | 276 | } |
| 255 | 277 | return name; |
src/link/MachO/Object.zig+1| ... | ... | @@ -164,6 +164,7 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi |
| 164 | 164 | }, |
| 165 | 165 | macho.LC_DATA_IN_CODE => { |
| 166 | 166 | self.data_in_code_cmd_index = i; |
| 167 | cmd.LinkeditData.dataoff += offset_mod; | |
| 167 | 168 | }, |
| 168 | 169 | else => { |
| 169 | 170 | log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()}); |
src/link/MachO/Zld.zig+105-3| ... | ... | @@ -60,13 +60,17 @@ stub_helper_section_index: ?u16 = null, |
| 60 | 60 | text_const_section_index: ?u16 = null, |
| 61 | 61 | cstring_section_index: ?u16 = null, |
| 62 | 62 | |
| 63 | // __DATA segment sections | |
| 63 | // __DATA_CONST segment sections | |
| 64 | 64 | got_section_index: ?u16 = null, |
| 65 | mod_init_func_section_index: ?u16 = null, | |
| 66 | mod_term_func_section_index: ?u16 = null, | |
| 67 | data_const_section_index: ?u16 = null, | |
| 68 | ||
| 69 | // __DATA segment sections | |
| 65 | 70 | tlv_section_index: ?u16 = null, |
| 66 | 71 | tlv_data_section_index: ?u16 = null, |
| 67 | 72 | tlv_bss_section_index: ?u16 = null, |
| 68 | 73 | la_symbol_ptr_section_index: ?u16 = null, |
| 69 | data_const_section_index: ?u16 = null, | |
| 70 | 74 | data_section_index: ?u16 = null, |
| 71 | 75 | bss_section_index: ?u16 = null, |
| 72 | 76 | |
| ... | ... | @@ -448,6 +452,46 @@ fn updateMetadata(self: *Zld, object_id: u16) !void { |
| 448 | 452 | .reserved3 = 0, |
| 449 | 453 | }); |
| 450 | 454 | }, |
| 455 | macho.S_MOD_INIT_FUNC_POINTERS => { | |
| 456 | if (!mem.eql(u8, segname, "__DATA")) continue; | |
| 457 | if (self.mod_init_func_section_index != null) continue; | |
| 458 | ||
| 459 | self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 460 | try data_const_seg.addSection(self.allocator, .{ | |
| 461 | .sectname = makeStaticString("__mod_init_func"), | |
| 462 | .segname = makeStaticString("__DATA_CONST"), | |
| 463 | .addr = 0, | |
| 464 | .size = 0, | |
| 465 | .offset = 0, | |
| 466 | .@"align" = 0, | |
| 467 | .reloff = 0, | |
| 468 | .nreloc = 0, | |
| 469 | .flags = macho.S_MOD_INIT_FUNC_POINTERS, | |
| 470 | .reserved1 = 0, | |
| 471 | .reserved2 = 0, | |
| 472 | .reserved3 = 0, | |
| 473 | }); | |
| 474 | }, | |
| 475 | macho.S_MOD_TERM_FUNC_POINTERS => { | |
| 476 | if (!mem.eql(u8, segname, "__DATA")) continue; | |
| 477 | if (self.mod_term_func_section_index != null) continue; | |
| 478 | ||
| 479 | self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 480 | try data_const_seg.addSection(self.allocator, .{ | |
| 481 | .sectname = makeStaticString("__mod_term_func"), | |
| 482 | .segname = makeStaticString("__DATA_CONST"), | |
| 483 | .addr = 0, | |
| 484 | .size = 0, | |
| 485 | .offset = 0, | |
| 486 | .@"align" = 0, | |
| 487 | .reloff = 0, | |
| 488 | .nreloc = 0, | |
| 489 | .flags = macho.S_MOD_TERM_FUNC_POINTERS, | |
| 490 | .reserved1 = 0, | |
| 491 | .reserved2 = 0, | |
| 492 | .reserved3 = 0, | |
| 493 | }); | |
| 494 | }, | |
| 451 | 495 | macho.S_ZEROFILL => { |
| 452 | 496 | if (!mem.eql(u8, segname, "__DATA")) continue; |
| 453 | 497 | if (self.bss_section_index != null) continue; |
| ... | ... | @@ -583,6 +627,18 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { |
| 583 | 627 | .sect = self.cstring_section_index.?, |
| 584 | 628 | }; |
| 585 | 629 | }, |
| 630 | macho.S_MOD_INIT_FUNC_POINTERS => { | |
| 631 | break :blk .{ | |
| 632 | .seg = self.data_const_segment_cmd_index.?, | |
| 633 | .sect = self.mod_init_func_section_index.?, | |
| 634 | }; | |
| 635 | }, | |
| 636 | macho.S_MOD_TERM_FUNC_POINTERS => { | |
| 637 | break :blk .{ | |
| 638 | .seg = self.data_const_segment_cmd_index.?, | |
| 639 | .sect = self.mod_term_func_section_index.?, | |
| 640 | }; | |
| 641 | }, | |
| 586 | 642 | macho.S_ZEROFILL => { |
| 587 | 643 | break :blk .{ |
| 588 | 644 | .seg = self.data_segment_cmd_index.?, |
| ... | ... | @@ -684,6 +740,8 @@ fn sortSections(self: *Zld) !void { |
| 684 | 740 | |
| 685 | 741 | const indices = &[_]*?u16{ |
| 686 | 742 | &self.got_section_index, |
| 743 | &self.mod_init_func_section_index, | |
| 744 | &self.mod_term_func_section_index, | |
| 687 | 745 | &self.data_const_section_index, |
| 688 | 746 | }; |
| 689 | 747 | for (indices) |maybe_index| { |
| ... | ... | @@ -2471,6 +2529,42 @@ fn writeRebaseInfoTable(self: *Zld) !void { |
| 2471 | 2529 | } |
| 2472 | 2530 | } |
| 2473 | 2531 | |
| 2532 | if (self.mod_init_func_section_index) |idx| { | |
| 2533 | // TODO audit and investigate this. | |
| 2534 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2535 | const sect = seg.sections.items[idx]; | |
| 2536 | const npointers = sect.size * @sizeOf(u64); | |
| 2537 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2538 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); | |
| 2539 | ||
| 2540 | try pointers.ensureCapacity(pointers.items.len + npointers); | |
| 2541 | var i: usize = 0; | |
| 2542 | while (i < npointers) : (i += 1) { | |
| 2543 | pointers.appendAssumeCapacity(.{ | |
| 2544 | .offset = base_offset + i * @sizeOf(u64), | |
| 2545 | .segment_id = segment_id, | |
| 2546 | }); | |
| 2547 | } | |
| 2548 | } | |
| 2549 | ||
| 2550 | if (self.mod_term_func_section_index) |idx| { | |
| 2551 | // TODO audit and investigate this. | |
| 2552 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2553 | const sect = seg.sections.items[idx]; | |
| 2554 | const npointers = sect.size * @sizeOf(u64); | |
| 2555 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2556 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); | |
| 2557 | ||
| 2558 | try pointers.ensureCapacity(pointers.items.len + npointers); | |
| 2559 | var i: usize = 0; | |
| 2560 | while (i < npointers) : (i += 1) { | |
| 2561 | pointers.appendAssumeCapacity(.{ | |
| 2562 | .offset = base_offset + i * @sizeOf(u64), | |
| 2563 | .segment_id = segment_id, | |
| 2564 | }); | |
| 2565 | } | |
| 2566 | } | |
| 2567 | ||
| 2474 | 2568 | if (self.la_symbol_ptr_section_index) |idx| { |
| 2475 | 2569 | try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len); |
| 2476 | 2570 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| ... | ... | @@ -2707,7 +2801,15 @@ fn writeDebugInfo(self: *Zld) !void { |
| 2707 | 2801 | }; |
| 2708 | 2802 | defer debug_info.deinit(self.allocator); |
| 2709 | 2803 | |
| 2710 | const compile_unit = try debug_info.inner.findCompileUnit(0x0); // We assume there is only one CU. | |
| 2804 | // We assume there is only one CU. | |
| 2805 | const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) { | |
| 2806 | error.MissingDebugInfo => { | |
| 2807 | // TODO audit cases with missing debug info and audit our dwarf.zig module. | |
| 2808 | log.debug("invalid or missing debug info in {s}; skipping", .{object.name}); | |
| 2809 | continue; | |
| 2810 | }, | |
| 2811 | else => |e| return e, | |
| 2812 | }; | |
| 2711 | 2813 | const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name); |
| 2712 | 2814 | const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir); |
| 2713 | 2815 |
src/main.zig+1-1| ... | ... | @@ -557,7 +557,7 @@ fn buildOutputType( |
| 557 | 557 | var test_filter: ?[]const u8 = null; |
| 558 | 558 | var test_name_prefix: ?[]const u8 = null; |
| 559 | 559 | var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR"); |
| 560 | var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR"); | |
| 560 | var override_global_cache_dir: ?[]const u8 = null; | |
| 561 | 561 | var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR"); |
| 562 | 562 | var main_pkg_path: ?[]const u8 = null; |
| 563 | 563 | var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; |
src/stage1/all_types.hpp-4| ... | ... | @@ -2139,10 +2139,6 @@ struct CodeGen { |
| 2139 | 2139 | Buf llvm_ir_file_output_path; |
| 2140 | 2140 | Buf analysis_json_output_path; |
| 2141 | 2141 | Buf docs_output_path; |
| 2142 | Buf *cache_dir; | |
| 2143 | Buf *c_artifact_dir; | |
| 2144 | const char **libc_include_dir_list; | |
| 2145 | size_t libc_include_dir_len; | |
| 2146 | 2142 | |
| 2147 | 2143 | Buf *builtin_zig_path; |
| 2148 | 2144 | Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir. |
src/translate_c.zig+137-60| ... | ... | @@ -270,7 +270,10 @@ pub const Context = struct { |
| 270 | 270 | global_scope: *Scope.Root, |
| 271 | 271 | clang_context: *clang.ASTContext, |
| 272 | 272 | mangle_count: u32 = 0, |
| 273 | /// Table of record decls that have been demoted to opaques. | |
| 273 | 274 | opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{}, |
| 275 | /// Table of unnamed enums and records that are child types of typedefs. | |
| 276 | unnamed_typedefs: std.AutoHashMapUnmanaged(usize, []const u8) = .{}, | |
| 274 | 277 | |
| 275 | 278 | /// This one is different than the root scope's name table. This contains |
| 276 | 279 | /// a list of names that we found by visiting all the top level decls without |
| ... | ... | @@ -338,6 +341,7 @@ pub fn translate( |
| 338 | 341 | context.alias_list.deinit(); |
| 339 | 342 | context.global_names.deinit(gpa); |
| 340 | 343 | context.opaque_demotes.deinit(gpa); |
| 344 | context.unnamed_typedefs.deinit(gpa); | |
| 341 | 345 | context.global_scope.deinit(); |
| 342 | 346 | } |
| 343 | 347 | |
| ... | ... | @@ -401,6 +405,51 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void { |
| 401 | 405 | if (decl.castToNamedDecl()) |named_decl| { |
| 402 | 406 | const decl_name = try c.str(named_decl.getName_bytes_begin()); |
| 403 | 407 | try c.global_names.put(c.gpa, decl_name, {}); |
| 408 | ||
| 409 | // Check for typedefs with unnamed enum/record child types. | |
| 410 | if (decl.getKind() == .Typedef) { | |
| 411 | const typedef_decl = @ptrCast(*const clang.TypedefNameDecl, decl); | |
| 412 | var child_ty = typedef_decl.getUnderlyingType().getTypePtr(); | |
| 413 | const addr: usize = while (true) switch (child_ty.getTypeClass()) { | |
| 414 | .Enum => { | |
| 415 | const enum_ty = @ptrCast(*const clang.EnumType, child_ty); | |
| 416 | const enum_decl = enum_ty.getDecl(); | |
| 417 | // check if this decl is unnamed | |
| 418 | if (@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()[0] != 0) return; | |
| 419 | break @ptrToInt(enum_decl.getCanonicalDecl()); | |
| 420 | }, | |
| 421 | .Record => { | |
| 422 | const record_ty = @ptrCast(*const clang.RecordType, child_ty); | |
| 423 | const record_decl = record_ty.getDecl(); | |
| 424 | // check if this decl is unnamed | |
| 425 | if (@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()[0] != 0) return; | |
| 426 | break @ptrToInt(record_decl.getCanonicalDecl()); | |
| 427 | }, | |
| 428 | .Elaborated => { | |
| 429 | const elaborated_ty = @ptrCast(*const clang.ElaboratedType, child_ty); | |
| 430 | child_ty = elaborated_ty.getNamedType().getTypePtr(); | |
| 431 | }, | |
| 432 | .Decayed => { | |
| 433 | const decayed_ty = @ptrCast(*const clang.DecayedType, child_ty); | |
| 434 | child_ty = decayed_ty.getDecayedType().getTypePtr(); | |
| 435 | }, | |
| 436 | .Attributed => { | |
| 437 | const attributed_ty = @ptrCast(*const clang.AttributedType, child_ty); | |
| 438 | child_ty = attributed_ty.getEquivalentType().getTypePtr(); | |
| 439 | }, | |
| 440 | .MacroQualified => { | |
| 441 | const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, child_ty); | |
| 442 | child_ty = macroqualified_ty.getModifiedType().getTypePtr(); | |
| 443 | }, | |
| 444 | else => return, | |
| 445 | } else unreachable; | |
| 446 | // TODO https://github.com/ziglang/zig/issues/3756 | |
| 447 | // TODO https://github.com/ziglang/zig/issues/1802 | |
| 448 | const name = if (isZigPrimitiveType(decl_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ decl_name, c.getMangle() }) else decl_name; | |
| 449 | try c.unnamed_typedefs.putNoClobber(c.gpa, addr, name); | |
| 450 | // Put this typedef in the decl_table to avoid redefinitions. | |
| 451 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), name); | |
| 452 | } | |
| 404 | 453 | } |
| 405 | 454 | } |
| 406 | 455 | |
| ... | ... | @@ -752,17 +801,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 752 | 801 | const toplevel = scope.id == .root; |
| 753 | 802 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; |
| 754 | 803 | |
| 755 | var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()); | |
| 756 | var is_unnamed = false; | |
| 757 | // Record declarations such as `struct {...} x` have no name but they're not | |
| 758 | // anonymous hence here isAnonymousStructOrUnion is not needed | |
| 759 | if (bare_name.len == 0) { | |
| 760 | bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); | |
| 761 | is_unnamed = true; | |
| 762 | } | |
| 763 | ||
| 764 | var container_kind_name: []const u8 = undefined; | |
| 765 | 804 | var is_union = false; |
| 805 | var container_kind_name: []const u8 = undefined; | |
| 806 | var bare_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()); | |
| 807 | ||
| 766 | 808 | if (record_decl.isUnion()) { |
| 767 | 809 | container_kind_name = "union"; |
| 768 | 810 | is_union = true; |
| ... | ... | @@ -773,7 +815,20 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 773 | 815 | return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name}); |
| 774 | 816 | } |
| 775 | 817 | |
| 776 | var name: []const u8 = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); | |
| 818 | var is_unnamed = false; | |
| 819 | var name = bare_name; | |
| 820 | if (c.unnamed_typedefs.get(@ptrToInt(record_decl.getCanonicalDecl()))) |typedef_name| { | |
| 821 | bare_name = typedef_name; | |
| 822 | name = typedef_name; | |
| 823 | } else { | |
| 824 | // Record declarations such as `struct {...} x` have no name but they're not | |
| 825 | // anonymous hence here isAnonymousStructOrUnion is not needed | |
| 826 | if (bare_name.len == 0) { | |
| 827 | bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); | |
| 828 | is_unnamed = true; | |
| 829 | } | |
| 830 | name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); | |
| 831 | } | |
| 777 | 832 | if (!toplevel) name = try bs.makeMangledName(c, name); |
| 778 | 833 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name); |
| 779 | 834 | |
| ... | ... | @@ -874,14 +929,19 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) E |
| 874 | 929 | const toplevel = scope.id == .root; |
| 875 | 930 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; |
| 876 | 931 | |
| 877 | var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); | |
| 878 | 932 | var is_unnamed = false; |
| 879 | if (bare_name.len == 0) { | |
| 880 | bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); | |
| 881 | is_unnamed = true; | |
| 933 | var bare_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); | |
| 934 | var name = bare_name; | |
| 935 | if (c.unnamed_typedefs.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |typedef_name| { | |
| 936 | bare_name = typedef_name; | |
| 937 | name = typedef_name; | |
| 938 | } else { | |
| 939 | if (bare_name.len == 0) { | |
| 940 | bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); | |
| 941 | is_unnamed = true; | |
| 942 | } | |
| 943 | name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); | |
| 882 | 944 | } |
| 883 | ||
| 884 | var name: []const u8 = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); | |
| 885 | 945 | if (!toplevel) _ = try bs.makeMangledName(c, name); |
| 886 | 946 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name); |
| 887 | 947 | |
| ... | ... | @@ -1063,6 +1123,7 @@ fn transStmt( |
| 1063 | 1123 | const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt); |
| 1064 | 1124 | return transExpr(c, scope, gen_sel.getResultExpr(), result_used); |
| 1065 | 1125 | }, |
| 1126 | // When adding new cases here, see comment for maybeBlockify() | |
| 1066 | 1127 | else => { |
| 1067 | 1128 | return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)}); |
| 1068 | 1129 | }, |
| ... | ... | @@ -2242,6 +2303,35 @@ fn transImplicitValueInitExpr( |
| 2242 | 2303 | return transZeroInitExpr(c, scope, source_loc, ty); |
| 2243 | 2304 | } |
| 2244 | 2305 | |
| 2306 | /// If a statement can possibly translate to a Zig assignment (either directly because it's | |
| 2307 | /// an assignment in C or indirectly via result assignment to `_`) AND it's the sole statement | |
| 2308 | /// in the body of an if statement or loop, then we need to put the statement into its own block. | |
| 2309 | /// The `else` case here corresponds to statements that could result in an assignment. If a statement | |
| 2310 | /// class never needs a block, add its enum to the top prong. | |
| 2311 | fn maybeBlockify(c: *Context, scope: *Scope, stmt: *const clang.Stmt) TransError!Node { | |
| 2312 | switch (stmt.getStmtClass()) { | |
| 2313 | .BreakStmtClass, | |
| 2314 | .CompoundStmtClass, | |
| 2315 | .ContinueStmtClass, | |
| 2316 | .DeclRefExprClass, | |
| 2317 | .DeclStmtClass, | |
| 2318 | .DoStmtClass, | |
| 2319 | .ForStmtClass, | |
| 2320 | .IfStmtClass, | |
| 2321 | .ReturnStmtClass, | |
| 2322 | .NullStmtClass, | |
| 2323 | .WhileStmtClass, | |
| 2324 | => return transStmt(c, scope, stmt, .unused), | |
| 2325 | else => { | |
| 2326 | var block_scope = try Scope.Block.init(c, scope, false); | |
| 2327 | defer block_scope.deinit(); | |
| 2328 | const result = try transStmt(c, &block_scope.base, stmt, .unused); | |
| 2329 | try block_scope.statements.append(result); | |
| 2330 | return block_scope.complete(c); | |
| 2331 | }, | |
| 2332 | } | |
| 2333 | } | |
| 2334 | ||
| 2245 | 2335 | fn transIfStmt( |
| 2246 | 2336 | c: *Context, |
| 2247 | 2337 | scope: *Scope, |
| ... | ... | @@ -2259,9 +2349,10 @@ fn transIfStmt( |
| 2259 | 2349 | const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond()); |
| 2260 | 2350 | const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); |
| 2261 | 2351 | |
| 2262 | const then_body = try transStmt(c, scope, stmt.getThen(), .unused); | |
| 2352 | const then_body = try maybeBlockify(c, scope, stmt.getThen()); | |
| 2353 | ||
| 2263 | 2354 | const else_body = if (stmt.getElse()) |expr| |
| 2264 | try transStmt(c, scope, expr, .unused) | |
| 2355 | try maybeBlockify(c, scope, expr) | |
| 2265 | 2356 | else |
| 2266 | 2357 | null; |
| 2267 | 2358 | return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body }); |
| ... | ... | @@ -2286,7 +2377,7 @@ fn transWhileLoop( |
| 2286 | 2377 | .parent = scope, |
| 2287 | 2378 | .id = .loop, |
| 2288 | 2379 | }; |
| 2289 | const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused); | |
| 2380 | const body = try maybeBlockify(c, &loop_scope, stmt.getBody()); | |
| 2290 | 2381 | return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null }); |
| 2291 | 2382 | } |
| 2292 | 2383 | |
| ... | ... | @@ -2312,7 +2403,7 @@ fn transDoWhileLoop( |
| 2312 | 2403 | const if_not_break = switch (cond.tag()) { |
| 2313 | 2404 | .false_literal => return transStmt(c, scope, stmt.getBody(), .unused), |
| 2314 | 2405 | .true_literal => { |
| 2315 | const body_node = try transStmt(c, scope, stmt.getBody(), .unused); | |
| 2406 | const body_node = try maybeBlockify(c, scope, stmt.getBody()); | |
| 2316 | 2407 | return Tag.while_true.create(c.arena, body_node); |
| 2317 | 2408 | }, |
| 2318 | 2409 | else => try Tag.if_not_break.create(c.arena, cond), |
| ... | ... | @@ -2388,7 +2479,7 @@ fn transForLoop( |
| 2388 | 2479 | else |
| 2389 | 2480 | null; |
| 2390 | 2481 | |
| 2391 | const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused); | |
| 2482 | const body = try maybeBlockify(c, &loop_scope, stmt.getBody()); | |
| 2392 | 2483 | const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr }); |
| 2393 | 2484 | if (block_scope) |*bs| { |
| 2394 | 2485 | try bs.statements.append(while_node); |
| ... | ... | @@ -3106,43 +3197,34 @@ fn transCreateCompoundAssign( |
| 3106 | 3197 | const requires_int_cast = blk: { |
| 3107 | 3198 | const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt); |
| 3108 | 3199 | const are_same_sign = cIsSignedInteger(lhs_qt) == cIsSignedInteger(rhs_qt); |
| 3109 | break :blk are_integers and !are_same_sign; | |
| 3200 | break :blk are_integers and !(are_same_sign and cIntTypeCmp(lhs_qt, rhs_qt) == .eq); | |
| 3110 | 3201 | }; |
| 3202 | ||
| 3111 | 3203 | if (used == .unused) { |
| 3112 | 3204 | // common case |
| 3113 | 3205 | // c: lhs += rhs |
| 3114 | 3206 | // zig: lhs += rhs |
| 3207 | const lhs_node = try transExpr(c, scope, lhs, .used); | |
| 3208 | var rhs_node = try transExpr(c, scope, rhs, .used); | |
| 3209 | if (is_ptr_op_signed) rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); | |
| 3210 | ||
| 3115 | 3211 | if ((is_mod or is_div) and is_signed) { |
| 3116 | const lhs_node = try transExpr(c, scope, lhs, .used); | |
| 3117 | const rhs_node = try transExpr(c, scope, rhs, .used); | |
| 3212 | if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node); | |
| 3213 | const operands = .{ .lhs = lhs_node, .rhs = rhs_node }; | |
| 3118 | 3214 | const builtin = if (is_mod) |
| 3119 | try Tag.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }) | |
| 3215 | try Tag.rem.create(c.arena, operands) | |
| 3120 | 3216 | else |
| 3121 | try Tag.div_trunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); | |
| 3217 | try Tag.div_trunc.create(c.arena, operands); | |
| 3122 | 3218 | |
| 3123 | 3219 | return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used); |
| 3124 | 3220 | } |
| 3125 | 3221 | |
| 3126 | const lhs_node = try transExpr(c, scope, lhs, .used); | |
| 3127 | var rhs_node = if (is_shift or requires_int_cast) | |
| 3128 | try transExprCoercing(c, scope, rhs, .used) | |
| 3129 | else | |
| 3130 | try transExpr(c, scope, rhs, .used); | |
| 3131 | ||
| 3132 | if (is_ptr_op_signed) { | |
| 3133 | rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); | |
| 3134 | } | |
| 3135 | ||
| 3136 | if (is_shift or requires_int_cast) { | |
| 3137 | // @intCast(rhs) | |
| 3138 | const cast_to_type = if (is_shift) | |
| 3139 | try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc) | |
| 3140 | else | |
| 3141 | try transQualType(c, scope, getExprQualType(c, lhs), loc); | |
| 3142 | ||
| 3222 | if (is_shift) { | |
| 3223 | const cast_to_type = try qualTypeToLog2IntRef(c, scope, rhs_qt, loc); | |
| 3143 | 3224 | rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); |
| 3225 | } else if (requires_int_cast) { | |
| 3226 | rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node); | |
| 3144 | 3227 | } |
| 3145 | ||
| 3146 | 3228 | return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used); |
| 3147 | 3229 | } |
| 3148 | 3230 | // worst case |
| ... | ... | @@ -3164,29 +3246,24 @@ fn transCreateCompoundAssign( |
| 3164 | 3246 | const lhs_node = try Tag.identifier.create(c.arena, ref); |
| 3165 | 3247 | const ref_node = try Tag.deref.create(c.arena, lhs_node); |
| 3166 | 3248 | |
| 3249 | var rhs_node = try transExpr(c, &block_scope.base, rhs, .used); | |
| 3250 | if (is_ptr_op_signed) rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); | |
| 3167 | 3251 | if ((is_mod or is_div) and is_signed) { |
| 3168 | const rhs_node = try transExpr(c, &block_scope.base, rhs, .used); | |
| 3252 | if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node); | |
| 3253 | const operands = .{ .lhs = ref_node, .rhs = rhs_node }; | |
| 3169 | 3254 | const builtin = if (is_mod) |
| 3170 | try Tag.rem.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node }) | |
| 3255 | try Tag.rem.create(c.arena, operands) | |
| 3171 | 3256 | else |
| 3172 | try Tag.div_trunc.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node }); | |
| 3257 | try Tag.div_trunc.create(c.arena, operands); | |
| 3173 | 3258 | |
| 3174 | 3259 | const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used); |
| 3175 | 3260 | try block_scope.statements.append(assign); |
| 3176 | 3261 | } else { |
| 3177 | var rhs_node = try transExpr(c, &block_scope.base, rhs, .used); | |
| 3178 | ||
| 3179 | if (is_shift or requires_int_cast) { | |
| 3180 | // @intCast(rhs) | |
| 3181 | const cast_to_type = if (is_shift) | |
| 3182 | try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc) | |
| 3183 | else | |
| 3184 | try transQualType(c, scope, getExprQualType(c, lhs), loc); | |
| 3185 | ||
| 3262 | if (is_shift) { | |
| 3263 | const cast_to_type = try qualTypeToLog2IntRef(c, &block_scope.base, rhs_qt, loc); | |
| 3186 | 3264 | rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); |
| 3187 | } | |
| 3188 | if (is_ptr_op_signed) { | |
| 3189 | rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); | |
| 3265 | } else if (requires_int_cast) { | |
| 3266 | rhs_node = try transCCast(c, &block_scope.base, loc, lhs_qt, rhs_qt, rhs_node); | |
| 3190 | 3267 | } |
| 3191 | 3268 | |
| 3192 | 3269 | const assign = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, rhs_node, .used); |
test/run_translated_c.zig+64| ... | ... | @@ -1244,4 +1244,68 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 1244 | 1244 | \\ return 0; |
| 1245 | 1245 | \\} |
| 1246 | 1246 | , ""); |
| 1247 | ||
| 1248 | cases.add("convert single-statement bodies into blocks for if/else/for/while. issue #8159", | |
| 1249 | \\#include <stdlib.h> | |
| 1250 | \\int foo() { return 1; } | |
| 1251 | \\int main(void) { | |
| 1252 | \\ int i = 0; | |
| 1253 | \\ if (i == 0) if (i == 0) if (i != 0) i = 1; | |
| 1254 | \\ if (i != 0) i = 1; else if (i == 0) if (i == 0) i += 1; | |
| 1255 | \\ for (; i < 10;) for (; i < 10;) i++; | |
| 1256 | \\ while (i == 100) while (i == 100) foo(); | |
| 1257 | \\ if (0) do do "string"; while(1); while(1); | |
| 1258 | \\ return 0; | |
| 1259 | \\} | |
| 1260 | , ""); | |
| 1261 | ||
| 1262 | cases.add("cast RHS of compound assignment if necessary, unused result", | |
| 1263 | \\#include <stdlib.h> | |
| 1264 | \\int main(void) { | |
| 1265 | \\ signed short val = -1; | |
| 1266 | \\ val += 1; if (val != 0) abort(); | |
| 1267 | \\ val -= 1; if (val != -1) abort(); | |
| 1268 | \\ val *= 2; if (val != -2) abort(); | |
| 1269 | \\ val /= 2; if (val != -1) abort(); | |
| 1270 | \\ val %= 2; if (val != -1) abort(); | |
| 1271 | \\ val <<= 1; if (val != -2) abort(); | |
| 1272 | \\ val >>= 1; if (val != -1) abort(); | |
| 1273 | \\ val += 100000000; // compile error if @truncate() not inserted | |
| 1274 | \\ unsigned short uval = 1; | |
| 1275 | \\ uval += 1; if (uval != 2) abort(); | |
| 1276 | \\ uval -= 1; if (uval != 1) abort(); | |
| 1277 | \\ uval *= 2; if (uval != 2) abort(); | |
| 1278 | \\ uval /= 2; if (uval != 1) abort(); | |
| 1279 | \\ uval %= 2; if (uval != 1) abort(); | |
| 1280 | \\ uval <<= 1; if (uval != 2) abort(); | |
| 1281 | \\ uval >>= 1; if (uval != 1) abort(); | |
| 1282 | \\ uval += 100000000; // compile error if @truncate() not inserted | |
| 1283 | \\} | |
| 1284 | , ""); | |
| 1285 | ||
| 1286 | cases.add("cast RHS of compound assignment if necessary, used result", | |
| 1287 | \\#include <stdlib.h> | |
| 1288 | \\int main(void) { | |
| 1289 | \\ signed short foo; | |
| 1290 | \\ signed short val = -1; | |
| 1291 | \\ foo = (val += 1); if (foo != 0) abort(); | |
| 1292 | \\ foo = (val -= 1); if (foo != -1) abort(); | |
| 1293 | \\ foo = (val *= 2); if (foo != -2) abort(); | |
| 1294 | \\ foo = (val /= 2); if (foo != -1) abort(); | |
| 1295 | \\ foo = (val %= 2); if (foo != -1) abort(); | |
| 1296 | \\ foo = (val <<= 1); if (foo != -2) abort(); | |
| 1297 | \\ foo = (val >>= 1); if (foo != -1) abort(); | |
| 1298 | \\ foo = (val += 100000000); // compile error if @truncate() not inserted | |
| 1299 | \\ unsigned short ufoo; | |
| 1300 | \\ unsigned short uval = 1; | |
| 1301 | \\ ufoo = (uval += 1); if (ufoo != 2) abort(); | |
| 1302 | \\ ufoo = (uval -= 1); if (ufoo != 1) abort(); | |
| 1303 | \\ ufoo = (uval *= 2); if (ufoo != 2) abort(); | |
| 1304 | \\ ufoo = (uval /= 2); if (ufoo != 1) abort(); | |
| 1305 | \\ ufoo = (uval %= 2); if (ufoo != 1) abort(); | |
| 1306 | \\ ufoo = (uval <<= 1); if (ufoo != 2) abort(); | |
| 1307 | \\ ufoo = (uval >>= 1); if (ufoo != 1) abort(); | |
| 1308 | \\ ufoo = (uval += 100000000); // compile error if @truncate() not inserted | |
| 1309 | \\} | |
| 1310 | , ""); | |
| 1247 | 1311 | } |
test/stage2/wasm.zig+35| ... | ... | @@ -175,6 +175,41 @@ pub fn addCases(ctx: *TestContext) !void { |
| 175 | 175 | \\ return i; |
| 176 | 176 | \\} |
| 177 | 177 | , "31\n"); |
| 178 | ||
| 179 | case.addCompareOutput( | |
| 180 | \\export fn _start() void { | |
| 181 | \\ assert(foo(true) != @as(i32, 30)); | |
| 182 | \\} | |
| 183 | \\ | |
| 184 | \\fn assert(ok: bool) void { | |
| 185 | \\ if (!ok) unreachable; | |
| 186 | \\} | |
| 187 | \\ | |
| 188 | \\fn foo(ok: bool) i32 { | |
| 189 | \\ const x = if(ok) @as(i32, 20) else @as(i32, 10); | |
| 190 | \\ return x; | |
| 191 | \\} | |
| 192 | , ""); | |
| 193 | ||
| 194 | case.addCompareOutput( | |
| 195 | \\export fn _start() void { | |
| 196 | \\ assert(foo(false) == @as(i32, 20)); | |
| 197 | \\ assert(foo(true) == @as(i32, 30)); | |
| 198 | \\} | |
| 199 | \\ | |
| 200 | \\fn assert(ok: bool) void { | |
| 201 | \\ if (!ok) unreachable; | |
| 202 | \\} | |
| 203 | \\ | |
| 204 | \\fn foo(ok: bool) i32 { | |
| 205 | \\ const val: i32 = blk: { | |
| 206 | \\ var x: i32 = 1; | |
| 207 | \\ if (!ok) break :blk x + @as(i32, 9); | |
| 208 | \\ break :blk x + @as(i32, 19); | |
| 209 | \\ }; | |
| 210 | \\ return val + 10; | |
| 211 | \\} | |
| 212 | , ""); | |
| 178 | 213 | } |
| 179 | 214 | |
| 180 | 215 | { |
test/standalone/mix_o_files/base64.zig+3-3| ... | ... | @@ -3,9 +3,9 @@ const base64 = @import("std").base64; |
| 3 | 3 | export fn decode_base_64(dest_ptr: [*]u8, dest_len: usize, source_ptr: [*]const u8, source_len: usize) usize { |
| 4 | 4 | const src = source_ptr[0..source_len]; |
| 5 | 5 | const dest = dest_ptr[0..dest_len]; |
| 6 | const base64_decoder = base64.standard_decoder_unsafe; | |
| 7 | const decoded_size = base64_decoder.calcSize(src); | |
| 8 | base64_decoder.decode(dest[0..decoded_size], src); | |
| 6 | const base64_decoder = base64.standard.Decoder; | |
| 7 | const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable; | |
| 8 | base64_decoder.decode(dest[0..decoded_size], src) catch unreachable; | |
| 9 | 9 | return decoded_size; |
| 10 | 10 | } |
| 11 | 11 |
test/translate_c.zig+64-38| ... | ... | @@ -3,6 +3,28 @@ const std = @import("std"); |
| 3 | 3 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 4 | |
| 5 | 5 | pub fn addCases(cases: *tests.TranslateCContext) void { |
| 6 | cases.add("unnamed child types of typedef receive typedef's name", | |
| 7 | \\typedef enum { | |
| 8 | \\ FooA, | |
| 9 | \\ FooB, | |
| 10 | \\} Foo; | |
| 11 | \\typedef struct { | |
| 12 | \\ int a, b; | |
| 13 | \\} Bar; | |
| 14 | , &[_][]const u8{ | |
| 15 | \\pub const Foo = extern enum(c_int) { | |
| 16 | \\ A, | |
| 17 | \\ B, | |
| 18 | \\ _, | |
| 19 | \\}; | |
| 20 | \\pub const FooA = @enumToInt(Foo.A); | |
| 21 | \\pub const FooB = @enumToInt(Foo.B); | |
| 22 | \\pub const Bar = extern struct { | |
| 23 | \\ a: c_int, | |
| 24 | \\ b: c_int, | |
| 25 | \\}; | |
| 26 | }); | |
| 27 | ||
| 6 | 28 | cases.add("if as while stmt has semicolon", |
| 7 | 29 | \\void foo() { |
| 8 | 30 | \\ while (1) if (1) { |
| ... | ... | @@ -218,9 +240,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 218 | 240 | \\} Bar; |
| 219 | 241 | , &[_][]const u8{ |
| 220 | 242 | \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo |
| 221 | \\const struct_unnamed_1 = opaque {}; | |
| 222 | \\pub const Foo = struct_unnamed_1; | |
| 223 | \\const struct_unnamed_2 = extern struct { | |
| 243 | \\pub const Foo = opaque {}; | |
| 244 | \\pub const Bar = extern struct { | |
| 224 | 245 | \\ bar: ?*Foo, |
| 225 | 246 | \\}; |
| 226 | 247 | }); |
| ... | ... | @@ -519,17 +540,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 519 | 540 | \\} outer; |
| 520 | 541 | \\void foo(outer *x) { x->y = x->x; } |
| 521 | 542 | , &[_][]const u8{ |
| 522 | \\const struct_unnamed_3 = extern struct { | |
| 543 | \\const struct_unnamed_2 = extern struct { | |
| 523 | 544 | \\ y: c_int, |
| 524 | 545 | \\}; |
| 525 | \\const union_unnamed_2 = extern union { | |
| 546 | \\const union_unnamed_1 = extern union { | |
| 526 | 547 | \\ x: u8, |
| 527 | \\ unnamed_0: struct_unnamed_3, | |
| 548 | \\ unnamed_0: struct_unnamed_2, | |
| 528 | 549 | \\}; |
| 529 | \\const struct_unnamed_1 = extern struct { | |
| 530 | \\ unnamed_0: union_unnamed_2, | |
| 550 | \\pub const outer = extern struct { | |
| 551 | \\ unnamed_0: union_unnamed_1, | |
| 531 | 552 | \\}; |
| 532 | \\pub const outer = struct_unnamed_1; | |
| 533 | 553 | \\pub export fn foo(arg_x: [*c]outer) void { |
| 534 | 554 | \\ var x = arg_x; |
| 535 | 555 | \\ x.*.unnamed_0.unnamed_0.y = @bitCast(c_int, @as(c_uint, x.*.unnamed_0.x)); |
| ... | ... | @@ -565,21 +585,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 565 | 585 | \\struct {int x,y;} s2 = {.y = 2, .x=1}; |
| 566 | 586 | \\foo s3 = { 123 }; |
| 567 | 587 | , &[_][]const u8{ |
| 568 | \\const struct_unnamed_1 = extern struct { | |
| 588 | \\pub const foo = extern struct { | |
| 569 | 589 | \\ x: c_int, |
| 570 | 590 | \\}; |
| 571 | \\pub const foo = struct_unnamed_1; | |
| 572 | \\const struct_unnamed_2 = extern struct { | |
| 591 | \\const struct_unnamed_1 = extern struct { | |
| 573 | 592 | \\ x: f64, |
| 574 | 593 | \\ y: f64, |
| 575 | 594 | \\ z: f64, |
| 576 | 595 | \\}; |
| 577 | \\pub export var s0: struct_unnamed_2 = struct_unnamed_2{ | |
| 596 | \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{ | |
| 578 | 597 | \\ .x = 1.2, |
| 579 | 598 | \\ .y = 1.3, |
| 580 | 599 | \\ .z = 0, |
| 581 | 600 | \\}; |
| 582 | \\const struct_unnamed_3 = extern struct { | |
| 601 | \\const struct_unnamed_2 = extern struct { | |
| 583 | 602 | \\ sec: c_int, |
| 584 | 603 | \\ min: c_int, |
| 585 | 604 | \\ hour: c_int, |
| ... | ... | @@ -587,7 +606,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 587 | 606 | \\ mon: c_int, |
| 588 | 607 | \\ year: c_int, |
| 589 | 608 | \\}; |
| 590 | \\pub export var s1: struct_unnamed_3 = struct_unnamed_3{ | |
| 609 | \\pub export var s1: struct_unnamed_2 = struct_unnamed_2{ | |
| 591 | 610 | \\ .sec = @as(c_int, 30), |
| 592 | 611 | \\ .min = @as(c_int, 15), |
| 593 | 612 | \\ .hour = @as(c_int, 17), |
| ... | ... | @@ -595,11 +614,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 595 | 614 | \\ .mon = @as(c_int, 12), |
| 596 | 615 | \\ .year = @as(c_int, 2014), |
| 597 | 616 | \\}; |
| 598 | \\const struct_unnamed_4 = extern struct { | |
| 617 | \\const struct_unnamed_3 = extern struct { | |
| 599 | 618 | \\ x: c_int, |
| 600 | 619 | \\ y: c_int, |
| 601 | 620 | \\}; |
| 602 | \\pub export var s2: struct_unnamed_4 = struct_unnamed_4{ | |
| 621 | \\pub export var s2: struct_unnamed_3 = struct_unnamed_3{ | |
| 603 | 622 | \\ .x = @as(c_int, 1), |
| 604 | 623 | \\ .y = @as(c_int, 2), |
| 605 | 624 | \\}; |
| ... | ... | @@ -1639,37 +1658,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1639 | 1658 | \\ p, |
| 1640 | 1659 | \\}; |
| 1641 | 1660 | , &[_][]const u8{ |
| 1642 | \\const enum_unnamed_1 = extern enum(c_int) { | |
| 1661 | \\pub const d = extern enum(c_int) { | |
| 1643 | 1662 | \\ a, |
| 1644 | 1663 | \\ b, |
| 1645 | 1664 | \\ c, |
| 1646 | 1665 | \\ _, |
| 1647 | 1666 | \\}; |
| 1648 | \\pub const a = @enumToInt(enum_unnamed_1.a); | |
| 1649 | \\pub const b = @enumToInt(enum_unnamed_1.b); | |
| 1650 | \\pub const c = @enumToInt(enum_unnamed_1.c); | |
| 1651 | \\pub const d = enum_unnamed_1; | |
| 1652 | \\const enum_unnamed_2 = extern enum(c_int) { | |
| 1667 | \\pub const a = @enumToInt(d.a); | |
| 1668 | \\pub const b = @enumToInt(d.b); | |
| 1669 | \\pub const c = @enumToInt(d.c); | |
| 1670 | \\const enum_unnamed_1 = extern enum(c_int) { | |
| 1653 | 1671 | \\ e = 0, |
| 1654 | 1672 | \\ f = 4, |
| 1655 | 1673 | \\ g = 5, |
| 1656 | 1674 | \\ _, |
| 1657 | 1675 | \\}; |
| 1658 | \\pub const e = @enumToInt(enum_unnamed_2.e); | |
| 1659 | \\pub const f = @enumToInt(enum_unnamed_2.f); | |
| 1660 | \\pub const g = @enumToInt(enum_unnamed_2.g); | |
| 1661 | \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e); | |
| 1662 | \\const enum_unnamed_3 = extern enum(c_int) { | |
| 1676 | \\pub const e = @enumToInt(enum_unnamed_1.e); | |
| 1677 | \\pub const f = @enumToInt(enum_unnamed_1.f); | |
| 1678 | \\pub const g = @enumToInt(enum_unnamed_1.g); | |
| 1679 | \\pub export var h: enum_unnamed_1 = @intToEnum(enum_unnamed_1, e); | |
| 1680 | \\const enum_unnamed_2 = extern enum(c_int) { | |
| 1663 | 1681 | \\ i, |
| 1664 | 1682 | \\ j, |
| 1665 | 1683 | \\ k, |
| 1666 | 1684 | \\ _, |
| 1667 | 1685 | \\}; |
| 1668 | \\pub const i = @enumToInt(enum_unnamed_3.i); | |
| 1669 | \\pub const j = @enumToInt(enum_unnamed_3.j); | |
| 1670 | \\pub const k = @enumToInt(enum_unnamed_3.k); | |
| 1686 | \\pub const i = @enumToInt(enum_unnamed_2.i); | |
| 1687 | \\pub const j = @enumToInt(enum_unnamed_2.j); | |
| 1688 | \\pub const k = @enumToInt(enum_unnamed_2.k); | |
| 1671 | 1689 | \\pub const struct_Baz = extern struct { |
| 1672 | \\ l: enum_unnamed_3, | |
| 1690 | \\ l: enum_unnamed_2, | |
| 1673 | 1691 | \\ m: d, |
| 1674 | 1692 | \\}; |
| 1675 | 1693 | \\pub const enum_i = extern enum(c_int) { |
| ... | ... | @@ -1934,7 +1952,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1934 | 1952 | , &[_][]const u8{ |
| 1935 | 1953 | \\pub export fn foo() c_int { |
| 1936 | 1954 | \\ var a: c_int = 5; |
| 1937 | \\ while (true) a = 2; | |
| 1955 | \\ while (true) { | |
| 1956 | \\ a = 2; | |
| 1957 | \\ } | |
| 1938 | 1958 | \\ while (true) { |
| 1939 | 1959 | \\ var a_1: c_int = 4; |
| 1940 | 1960 | \\ a_1 = 9; |
| ... | ... | @@ -1947,7 +1967,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1947 | 1967 | \\ var a_1: c_int = 2; |
| 1948 | 1968 | \\ a_1 = 12; |
| 1949 | 1969 | \\ } |
| 1950 | \\ while (true) a = 7; | |
| 1970 | \\ while (true) { | |
| 1971 | \\ a = 7; | |
| 1972 | \\ } | |
| 1951 | 1973 | \\ return 0; |
| 1952 | 1974 | \\} |
| 1953 | 1975 | }); |
| ... | ... | @@ -2008,7 +2030,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2008 | 2030 | \\} |
| 2009 | 2031 | , &[_][]const u8{ |
| 2010 | 2032 | \\pub export fn bar() c_int { |
| 2011 | \\ if ((if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6)) != 0) _ = @as(c_int, 2); | |
| 2033 | \\ if ((if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6)) != 0) { | |
| 2034 | \\ _ = @as(c_int, 2); | |
| 2035 | \\ } | |
| 2012 | 2036 | \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6); |
| 2013 | 2037 | \\} |
| 2014 | 2038 | }); |
| ... | ... | @@ -2389,7 +2413,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2389 | 2413 | \\pub const yes = [*c]u8; |
| 2390 | 2414 | \\pub export fn foo() void { |
| 2391 | 2415 | \\ var a: yes = undefined; |
| 2392 | \\ if (a != null) _ = @as(c_int, 2); | |
| 2416 | \\ if (a != null) { | |
| 2417 | \\ _ = @as(c_int, 2); | |
| 2418 | \\ } | |
| 2393 | 2419 | \\} |
| 2394 | 2420 | }); |
| 2395 | 2421 | |
| ... | ... | @@ -2740,7 +2766,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2740 | 2766 | \\ var a = arg_a; |
| 2741 | 2767 | \\ var i: c_int = 0; |
| 2742 | 2768 | \\ while (a > @bitCast(c_uint, @as(c_int, 0))) { |
| 2743 | \\ a >>= @intCast(@import("std").math.Log2Int(c_int), 1); | |
| 2769 | \\ a >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); | |
| 2744 | 2770 | \\ } |
| 2745 | 2771 | \\ return i; |
| 2746 | 2772 | \\} |
| ... | ... | @@ -2760,7 +2786,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2760 | 2786 | \\ var a = arg_a; |
| 2761 | 2787 | \\ var i: c_int = 0; |
| 2762 | 2788 | \\ while (a > @bitCast(c_uint, @as(c_int, 0))) { |
| 2763 | \\ a >>= @intCast(@import("std").math.Log2Int(c_int), 1); | |
| 2789 | \\ a >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); | |
| 2764 | 2790 | \\ } |
| 2765 | 2791 | \\ return i; |
| 2766 | 2792 | \\} |