| 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,6 +89,7 @@ set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries |
| 89 | set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary") | 89 | set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary") |
| 90 | set(ZIG_SINGLE_THREADED off CACHE BOOL "limit the zig compiler to use only 1 thread") | 90 | set(ZIG_SINGLE_THREADED off CACHE BOOL "limit the zig compiler to use only 1 thread") |
| 91 | set(ZIG_OMIT_STAGE2 off CACHE BOOL "omit the stage2 backend from stage1") | 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 | if("${ZIG_TARGET_TRIPLE}" STREQUAL "native") | 94 | if("${ZIG_TARGET_TRIPLE}" STREQUAL "native") |
| 94 | set(ZIG_USE_LLVM_CONFIG ON CACHE BOOL "use llvm-config to find LLVM libraries") | 95 | set(ZIG_USE_LLVM_CONFIG ON CACHE BOOL "use llvm-config to find LLVM libraries") |
| ... | @@ -607,6 +608,12 @@ else() | ... | @@ -607,6 +608,12 @@ else() |
| 607 | set(ZIG_OMIT_STAGE2_BOOL "false") | 608 | set(ZIG_OMIT_STAGE2_BOOL "false") |
| 608 | endif() | 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 | configure_file ( | 617 | configure_file ( |
| 611 | "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in" | 618 | "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in" |
| 612 | "${ZIG_CONFIG_H_OUT}" | 619 | "${ZIG_CONFIG_H_OUT}" |
| ... | @@ -735,12 +742,14 @@ if(MSVC OR MINGW) | ... | @@ -735,12 +742,14 @@ if(MSVC OR MINGW) |
| 735 | target_link_libraries(zigstage1 LINK_PUBLIC version) | 742 | target_link_libraries(zigstage1 LINK_PUBLIC version) |
| 736 | endif() | 743 | endif() |
| 737 | 744 | ||
| 738 | add_executable(zig0 ${ZIG0_SOURCES}) | 745 | if("${ZIG_EXECUTABLE}" STREQUAL "") |
| 739 | set_target_properties(zig0 PROPERTIES | 746 | add_executable(zig0 ${ZIG0_SOURCES}) |
| 740 | COMPILE_FLAGS ${EXE_CFLAGS} | 747 | set_target_properties(zig0 PROPERTIES |
| 741 | LINK_FLAGS ${EXE_LDFLAGS} | 748 | COMPILE_FLAGS ${EXE_CFLAGS} |
| 742 | ) | 749 | LINK_FLAGS ${EXE_LDFLAGS} |
| 743 | target_link_libraries(zig0 zigstage1) | 750 | ) |
| 751 | target_link_libraries(zig0 zigstage1) | ||
| 752 | endif() | ||
| 744 | 753 | ||
| 745 | if(MSVC) | 754 | if(MSVC) |
| 746 | set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.obj") | 755 | set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.obj") |
| ... | @@ -789,7 +798,6 @@ if("${ZIG_EXECUTABLE}" STREQUAL "") | ... | @@ -789,7 +798,6 @@ if("${ZIG_EXECUTABLE}" STREQUAL "") |
| 789 | else() | 798 | else() |
| 790 | add_custom_command( | 799 | add_custom_command( |
| 791 | OUTPUT "${ZIG1_OBJECT}" | 800 | OUTPUT "${ZIG1_OBJECT}" |
| 792 | BYPRODUCTS "${ZIG1_OBJECT}" | ||
| 793 | COMMAND "${ZIG_EXECUTABLE}" "build-obj" ${BUILD_ZIG1_ARGS} | 801 | COMMAND "${ZIG_EXECUTABLE}" "build-obj" ${BUILD_ZIG1_ARGS} |
| 794 | DEPENDS ${ZIG_STAGE2_SOURCES} | 802 | DEPENDS ${ZIG_STAGE2_SOURCES} |
| 795 | COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}" | 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,6 +12,19 @@ jobs: |
| 12 | - script: ci/azure/macos_script | 12 | - script: ci/azure/macos_script |
| 13 | name: main | 13 | name: main |
| 14 | displayName: 'Build and test' | 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 | - job: BuildLinux | 28 | - job: BuildLinux |
| 16 | pool: | 29 | pool: |
| 17 | vmImage: 'ubuntu-18.04' | 30 | vmImage: 'ubuntu-18.04' |
| ... | @@ -31,7 +44,7 @@ jobs: | ... | @@ -31,7 +44,7 @@ jobs: |
| 31 | timeoutInMinutes: 360 | 44 | timeoutInMinutes: 360 |
| 32 | steps: | 45 | steps: |
| 33 | - powershell: | | 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 | .\sfx.exe -y -o\ | 48 | .\sfx.exe -y -o\ |
| 36 | del sfx.exe | 49 | del sfx.exe |
| 37 | displayName: Download/Extract/Install MSYS2 | 50 | displayName: Download/Extract/Install MSYS2 |
ci/azure/windows_msvc_install+1-1| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | set -x | 3 | set -x |
| 4 | set -e | 4 | set -e |
| 5 | 5 | ||
| 6 | pacman -Su --needed --noconfirm | 6 | pacman -Suy --needed --noconfirm |
| 7 | pacman -S --needed --noconfirm wget p7zip python3-pip tar xz | 7 | pacman -S --needed --noconfirm wget p7zip python3-pip tar xz |
| 8 | 8 | ||
| 9 | pip install s3cmd | 9 | pip install s3cmd |
doc/langref.html.in+3-3| ... | @@ -9952,9 +9952,9 @@ export fn decode_base_64( | ... | @@ -9952,9 +9952,9 @@ export fn decode_base_64( |
| 9952 | ) usize { | 9952 | ) usize { |
| 9953 | const src = source_ptr[0..source_len]; | 9953 | const src = source_ptr[0..source_len]; |
| 9954 | const dest = dest_ptr[0..dest_len]; | 9954 | const dest = dest_ptr[0..dest_len]; |
| 9955 | const base64_decoder = base64.standard_decoder_unsafe; | 9955 | const base64_decoder = base64.standard.Decoder; |
| 9956 | const decoded_size = base64_decoder.calcSize(src); | 9956 | const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable; |
| 9957 | base64_decoder.decode(dest[0..decoded_size], src); | 9957 | base64_decoder.decode(dest[0..decoded_size], src) catch unreachable; |
| 9958 | return decoded_size; | 9958 | return decoded_size; |
| 9959 | } | 9959 | } |
| 9960 | {#code_end#} | 9960 | {#code_end#} |
lib/std/array_hash_map.zig+13-13| ... | @@ -687,8 +687,9 @@ pub fn ArrayHashMapUnmanaged( | ... | @@ -687,8 +687,9 @@ pub fn ArrayHashMapUnmanaged( |
| 687 | 687 | ||
| 688 | /// Removes the last inserted `Entry` in the hash map and returns it. | 688 | /// Removes the last inserted `Entry` in the hash map and returns it. |
| 689 | pub fn pop(self: *Self) Entry { | 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 | _ = self.removeWithHash(top.key, top.hash, .index_only); | 691 | _ = self.removeWithHash(top.key, top.hash, .index_only); |
| 692 | self.entries.items.len -= 1; | ||
| 692 | return top; | 693 | return top; |
| 693 | } | 694 | } |
| 694 | 695 | ||
| ... | @@ -1258,19 +1259,18 @@ test "pop" { | ... | @@ -1258,19 +1259,18 @@ test "pop" { |
| 1258 | var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator); | 1259 | var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator); |
| 1259 | defer map.deinit(); | 1260 | defer map.deinit(); |
| 1260 | 1261 | ||
| 1261 | testing.expect((try map.fetchPut(1, 11)) == null); | 1262 | // Insert just enough entries so that the map expands. Afterwards, |
| 1262 | testing.expect((try map.fetchPut(2, 22)) == null); | 1263 | // pop all entries out of the map. |
| 1263 | testing.expect((try map.fetchPut(3, 33)) == null); | ||
| 1264 | testing.expect((try map.fetchPut(4, 44)) == null); | ||
| 1265 | 1264 | ||
| 1266 | const pop1 = map.pop(); | 1265 | var i: i32 = 0; |
| 1267 | testing.expect(pop1.key == 4 and pop1.value == 44); | 1266 | while (i < 9) : (i += 1) { |
| 1268 | const pop2 = map.pop(); | 1267 | testing.expect((try map.fetchPut(i, i)) == null); |
| 1269 | testing.expect(pop2.key == 3 and pop2.value == 33); | 1268 | } |
| 1270 | const pop3 = map.pop(); | 1269 | |
| 1271 | testing.expect(pop3.key == 2 and pop3.value == 22); | 1270 | while (i > 0) : (i -= 1) { |
| 1272 | const pop4 = map.pop(); | 1271 | const pop = map.pop(); |
| 1273 | testing.expect(pop4.key == 1 and pop4.value == 11); | 1272 | testing.expect(pop.key == i - 1 and pop.value == i - 1); |
| 1273 | } | ||
| 1274 | } | 1274 | } |
| 1275 | 1275 | ||
| 1276 | test "reIndex" { | 1276 | test "reIndex" { |
lib/std/base64.zig+322-324| ... | @@ -8,454 +8,452 @@ const assert = std.debug.assert; | ... | @@ -8,454 +8,452 @@ const assert = std.debug.assert; |
| 8 | const testing = std.testing; | 8 | const testing = std.testing; |
| 9 | const mem = std.mem; | 9 | const mem = std.mem; |
| 10 | 10 | ||
| 11 | pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | 11 | pub const Error = error{ |
| 12 | pub const standard_pad_char = '='; | 12 | InvalidCharacter, |
| 13 | pub const standard_encoder = Base64Encoder.init(standard_alphabet_chars, standard_pad_char); | 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 | pub const Base64Encoder = struct { | 81 | pub const Base64Encoder = struct { |
| 16 | alphabet_chars: []const u8, | 82 | alphabet_chars: [64]u8, |
| 17 | pad_char: u8, | 83 | pad_char: ?u8, |
| 18 | 84 | ||
| 19 | /// a bunch of assertions, then simply pass the data right through. | 85 | /// A bunch of assertions, then simply pass the data right through. |
| 20 | pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64Encoder { | 86 | pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Encoder { |
| 21 | assert(alphabet_chars.len == 64); | 87 | assert(alphabet_chars.len == 64); |
| 22 | var char_in_alphabet = [_]bool{false} ** 256; | 88 | var char_in_alphabet = [_]bool{false} ** 256; |
| 23 | for (alphabet_chars) |c| { | 89 | for (alphabet_chars) |c| { |
| 24 | assert(!char_in_alphabet[c]); | 90 | assert(!char_in_alphabet[c]); |
| 25 | assert(c != pad_char); | 91 | assert(pad_char == null or c != pad_char.?); |
| 26 | char_in_alphabet[c] = true; | 92 | char_in_alphabet[c] = true; |
| 27 | } | 93 | } |
| 28 | |||
| 29 | return Base64Encoder{ | 94 | return Base64Encoder{ |
| 30 | .alphabet_chars = alphabet_chars, | 95 | .alphabet_chars = alphabet_chars, |
| 31 | .pad_char = pad_char, | 96 | .pad_char = pad_char, |
| 32 | }; | 97 | }; |
| 33 | } | 98 | } |
| 34 | 99 | ||
| 35 | /// ceil(source_len * 4/3) | 100 | /// Compute the encoded length |
| 36 | pub fn calcSize(source_len: usize) usize { | 101 | pub fn calcSize(encoder: *const Base64Encoder, source_len: usize) usize { |
| 37 | return @divTrunc(source_len + 2, 3) * 4; | 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 | pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 { | 111 | pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 { |
| 42 | assert(dest.len >= Base64Encoder.calcSize(source.len)); | 112 | const out_len = encoder.calcSize(source.len); |
| 43 | 113 | assert(dest.len >= out_len); | |
| 44 | var i: usize = 0; | 114 | |
| 45 | var out_index: usize = 0; | 115 | const nibbles = source.len / 3; |
| 46 | while (i + 2 < source.len) : (i += 3) { | 116 | const leftover = source.len - 3 * nibbles; |
| 47 | dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f]; | 117 | |
| 48 | out_index += 1; | 118 | var acc: u12 = 0; |
| 49 | 119 | var acc_len: u4 = 0; | |
| 50 | dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)]; | 120 | var out_idx: usize = 0; |
| 51 | out_index += 1; | 121 | for (source) |v| { |
| 52 | 122 | acc = (acc << 8) + v; | |
| 53 | dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) | ((source[i + 2] & 0xc0) >> 6)]; | 123 | acc_len += 8; |
| 54 | out_index += 1; | 124 | while (acc_len >= 6) { |
| 55 | 125 | acc_len -= 6; | |
| 56 | dest[out_index] = encoder.alphabet_chars[source[i + 2] & 0x3f]; | 126 | dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc >> acc_len))]; |
| 57 | out_index += 1; | 127 | out_idx += 1; |
| 128 | } | ||
| 58 | } | 129 | } |
| 59 | 130 | if (acc_len > 0) { | |
| 60 | if (i < source.len) { | 131 | dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc << 6 - acc_len))]; |
| 61 | dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f]; | 132 | out_idx += 1; |
| 62 | out_index += 1; | 133 | } |
| 63 | 134 | if (encoder.pad_char) |pad_char| { | |
| 64 | if (i + 1 == source.len) { | 135 | for (dest[out_idx..]) |*pad| { |
| 65 | dest[out_index] = encoder.alphabet_chars[(source[i] & 0x3) << 4]; | 136 | pad.* = pad_char; |
| 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; | ||
| 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 | pub const Base64Decoder = struct { | 143 | pub const Base64Decoder = struct { |
| 144 | const invalid_char: u8 = 0xff; | ||
| 145 | |||
| 88 | /// e.g. 'A' => 0. | 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 | char_to_index: [256]u8, | 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. | 151 | pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Decoder { |
| 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 | |||
| 99 | var result = Base64Decoder{ | 152 | var result = Base64Decoder{ |
| 100 | .char_to_index = undefined, | 153 | .char_to_index = [_]u8{invalid_char} ** 256, |
| 101 | .char_in_alphabet = [_]bool{false} ** 256, | ||
| 102 | .pad_char = pad_char, | 154 | .pad_char = pad_char, |
| 103 | }; | 155 | }; |
| 104 | 156 | ||
| 157 | var char_in_alphabet = [_]bool{false} ** 256; | ||
| 105 | for (alphabet_chars) |c, i| { | 158 | for (alphabet_chars) |c, i| { |
| 106 | assert(!result.char_in_alphabet[c]); | 159 | assert(!char_in_alphabet[c]); |
| 107 | assert(c != pad_char); | 160 | assert(pad_char == null or c != pad_char.?); |
| 108 | 161 | ||
| 109 | result.char_to_index[c] = @intCast(u8, i); | 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 | return result; | 179 | return result; |
| 114 | } | 180 | } |
| 115 | 181 | ||
| 116 | /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding. | 182 | /// Return the exact decoded size for a slice. |
| 117 | pub fn calcSize(decoder: *const Base64Decoder, source: []const u8) !usize { | 183 | /// `InvalidPadding` is returned if the input length is not valid. |
| 118 | if (source.len % 4 != 0) return error.InvalidPadding; | 184 | pub fn calcSizeForSlice(decoder: *const Base64Decoder, source: []const u8) Error!usize { |
| 119 | return calcDecodedSizeExactUnsafe(source, decoder.pad_char); | 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 | /// dest.len must be what you get from ::calcSize. | 194 | /// dest.len must be what you get from ::calcSize. |
| 123 | /// invalid characters result in error.InvalidCharacter. | 195 | /// invalid characters result in error.InvalidCharacter. |
| 124 | /// invalid padding results in error.InvalidPadding. | 196 | /// invalid padding results in error.InvalidPadding. |
| 125 | pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) !void { | 197 | pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) Error!void { |
| 126 | assert(dest.len == (decoder.calcSize(source) catch unreachable)); | 198 | if (decoder.pad_char != null and source.len % 4 != 0) return error.InvalidPadding; |
| 127 | assert(source.len % 4 == 0); | 199 | var acc: u12 = 0; |
| 128 | 200 | var acc_len: u4 = 0; | |
| 129 | var src_cursor: usize = 0; | 201 | var dest_idx: usize = 0; |
| 130 | var dest_cursor: usize = 0; | 202 | var leftover_idx: ?usize = null; |
| 131 | 203 | for (source) |c, src_idx| { | |
| 132 | while (src_cursor < source.len) : (src_cursor += 4) { | 204 | const d = decoder.char_to_index[c]; |
| 133 | if (!decoder.char_in_alphabet[source[src_cursor + 0]]) return error.InvalidCharacter; | 205 | if (d == invalid_char) { |
| 134 | if (!decoder.char_in_alphabet[source[src_cursor + 1]]) return error.InvalidCharacter; | 206 | if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter; |
| 135 | if (src_cursor < source.len - 4 or source[src_cursor + 3] != decoder.pad_char) { | 207 | leftover_idx = src_idx; |
| 136 | // common case | 208 | break; |
| 137 | if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter; | 209 | } |
| 138 | if (!decoder.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter; | 210 | acc = (acc << 6) + d; |
| 139 | dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4; | 211 | acc_len += 6; |
| 140 | dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2; | 212 | if (acc_len >= 8) { |
| 141 | dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 | decoder.char_to_index[source[src_cursor + 3]]; | 213 | acc_len -= 8; |
| 142 | dest_cursor += 3; | 214 | dest[dest_idx] = @truncate(u8, acc >> acc_len); |
| 143 | } else if (source[src_cursor + 2] != decoder.pad_char) { | 215 | dest_idx += 1; |
| 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; | ||
| 155 | } | 216 | } |
| 156 | } | 217 | } |
| 157 | 218 | if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) { | |
| 158 | assert(src_cursor == source.len); | 219 | return error.InvalidPadding; |
| 159 | assert(dest_cursor == dest.len); | 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 | pub const Base64DecoderWithIgnore = struct { | 238 | pub const Base64DecoderWithIgnore = struct { |
| 164 | decoder: Base64Decoder, | 239 | decoder: Base64Decoder, |
| 165 | char_is_ignored: [256]bool, | 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 | var result = Base64DecoderWithIgnore{ | 243 | var result = Base64DecoderWithIgnore{ |
| 168 | .decoder = Base64Decoder.init(alphabet_chars, pad_char), | 244 | .decoder = Base64Decoder.init(alphabet_chars, pad_char), |
| 169 | .char_is_ignored = [_]bool{false} ** 256, | 245 | .char_is_ignored = [_]bool{false} ** 256, |
| 170 | }; | 246 | }; |
| 171 | |||
| 172 | for (ignore_chars) |c| { | 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 | assert(!result.char_is_ignored[c]); | 249 | assert(!result.char_is_ignored[c]); |
| 175 | assert(result.decoder.pad_char != c); | 250 | assert(result.decoder.pad_char != c); |
| 176 | result.char_is_ignored[c] = true; | 251 | result.char_is_ignored[c] = true; |
| 177 | } | 252 | } |
| 178 | |||
| 179 | return result; | 253 | return result; |
| 180 | } | 254 | } |
| 181 | 255 | ||
| 182 | /// If no characters end up being ignored or padding, this will be the exact decoded size. | 256 | /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding |
| 183 | pub fn calcSizeUpperBound(encoded_len: usize) usize { | 257 | /// `InvalidPadding` is returned if the input length is not valid. |
| 184 | return @divTrunc(encoded_len, 4) * 3; | 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 | /// Invalid characters that are not ignored result in error.InvalidCharacter. | 267 | /// Invalid characters that are not ignored result in error.InvalidCharacter. |
| 188 | /// Invalid padding results in error.InvalidPadding. | 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 | /// Returns the number of bytes written to dest. | 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 | const decoder = &decoder_with_ignore.decoder; | 272 | const decoder = &decoder_with_ignore.decoder; |
| 193 | 273 | var acc: u12 = 0; | |
| 194 | var src_cursor: usize = 0; | 274 | var acc_len: u4 = 0; |
| 195 | var dest_cursor: usize = 0; | 275 | var dest_idx: usize = 0; |
| 196 | 276 | var leftover_idx: ?usize = null; | |
| 197 | while (true) { | 277 | for (source) |c, src_idx| { |
| 198 | // get the next 4 chars, if available | 278 | if (decoder_with_ignore.char_is_ignored[c]) continue; |
| 199 | var next_4_chars: [4]u8 = undefined; | 279 | const d = decoder.char_to_index[c]; |
| 200 | var available_chars: usize = 0; | 280 | if (d == Base64Decoder.invalid_char) { |
| 201 | var pad_char_count: usize = 0; | 281 | if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter; |
| 202 | while (available_chars < 4 and src_cursor < source.len) { | 282 | leftover_idx = src_idx; |
| 203 | var c = source[src_cursor]; | 283 | break; |
| 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; | ||
| 229 | } | 284 | } |
| 230 | 285 | acc = (acc << 6) + d; | |
| 231 | switch (available_chars) { | 286 | acc_len += 6; |
| 232 | 4 => { | 287 | if (acc_len >= 8) { |
| 233 | // common case | 288 | if (dest_idx == dest.len) return error.NoSpaceLeft; |
| 234 | if (dest_cursor + 3 > dest.len) return error.OutputTooSmall; | 289 | acc_len -= 8; |
| 235 | assert(pad_char_count == 0); | 290 | dest[dest_idx] = @truncate(u8, acc >> acc_len); |
| 236 | dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4; | 291 | dest_idx += 1; |
| 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, | ||
| 267 | } | 292 | } |
| 268 | } | 293 | } |
| 269 | 294 | if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) { | |
| 270 | assert(src_cursor == source.len); | 295 | return error.InvalidPadding; |
| 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); | ||
| 293 | } | 296 | } |
| 294 | return result; | 297 | const padding_len = acc_len / 2; |
| 295 | } | 298 | if (leftover_idx == null) { |
| 296 | 299 | if (decoder.pad_char != null and padding_len != 0) return error.InvalidPadding; | |
| 297 | /// The source buffer must be valid. | 300 | return dest_idx; |
| 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; | ||
| 313 | } | 301 | } |
| 314 | 302 | var leftover = source[leftover_idx.?..]; | |
| 315 | while (in_buf_len > 4) { | 303 | if (decoder.pad_char) |pad_char| { |
| 316 | dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4; | 304 | var padding_chars: usize = 0; |
| 317 | dest_index += 1; | 305 | var i: usize = 0; |
| 318 | 306 | for (leftover) |c| { | |
| 319 | dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2; | 307 | if (decoder_with_ignore.char_is_ignored[c]) continue; |
| 320 | dest_index += 1; | 308 | if (c != pad_char) { |
| 321 | 309 | return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding; | |
| 322 | dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]]; | 310 | } |
| 323 | dest_index += 1; | 311 | padding_chars += 1; |
| 324 | 312 | } | |
| 325 | src_index += 4; | 313 | if (padding_chars != padding_len) return error.InvalidPadding; |
| 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; | ||
| 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 | test "base64" { | 319 | test "base64" { |
| 357 | @setEvalBranchQuota(8000); | 320 | @setEvalBranchQuota(8000); |
| 358 | testBase64() catch unreachable; | 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 | fn testBase64() !void { | 331 | fn testBase64() !void { |
| 363 | try testAllApis("", ""); | 332 | const codecs = standard; |
| 364 | try testAllApis("f", "Zg=="); | 333 | |
| 365 | try testAllApis("fo", "Zm8="); | 334 | try testAllApis(codecs, "", ""); |
| 366 | try testAllApis("foo", "Zm9v"); | 335 | try testAllApis(codecs, "f", "Zg=="); |
| 367 | try testAllApis("foob", "Zm9vYg=="); | 336 | try testAllApis(codecs, "fo", "Zm8="); |
| 368 | try testAllApis("fooba", "Zm9vYmE="); | 337 | try testAllApis(codecs, "foo", "Zm9v"); |
| 369 | try testAllApis("foobar", "Zm9vYmFy"); | 338 | try testAllApis(codecs, "foob", "Zm9vYg=="); |
| 370 | 339 | try testAllApis(codecs, "fooba", "Zm9vYmE="); | |
| 371 | try testDecodeIgnoreSpace("", " "); | 340 | try testAllApis(codecs, "foobar", "Zm9vYmFy"); |
| 372 | try testDecodeIgnoreSpace("f", "Z g= ="); | 341 | |
| 373 | try testDecodeIgnoreSpace("fo", " Zm8="); | 342 | try testDecodeIgnoreSpace(codecs, "", " "); |
| 374 | try testDecodeIgnoreSpace("foo", "Zm9v "); | 343 | try testDecodeIgnoreSpace(codecs, "f", "Z g= ="); |
| 375 | try testDecodeIgnoreSpace("foob", "Zm9vYg = = "); | 344 | try testDecodeIgnoreSpace(codecs, "fo", " Zm8="); |
| 376 | try testDecodeIgnoreSpace("fooba", "Zm9v YmE="); | 345 | try testDecodeIgnoreSpace(codecs, "foo", "Zm9v "); |
| 377 | try testDecodeIgnoreSpace("foobar", " Z m 9 v Y m F y "); | 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 | // test getting some api errors | 386 | // test getting some api errors |
| 380 | try testError("A", error.InvalidPadding); | 387 | try testError(codecs, "A", error.InvalidPadding); |
| 381 | try testError("AA", error.InvalidPadding); | 388 | try testError(codecs, "AAA=", error.InvalidCharacter); |
| 382 | try testError("AAA", error.InvalidPadding); | 389 | try testError(codecs, "A..A", error.InvalidCharacter); |
| 383 | try testError("A..A", error.InvalidCharacter); | 390 | try testError(codecs, "AA=A", error.InvalidCharacter); |
| 384 | try testError("AA=A", error.InvalidCharacter); | 391 | try testError(codecs, "AA/=", error.InvalidCharacter); |
| 385 | try testError("AA/=", error.InvalidPadding); | 392 | try testError(codecs, "A/==", error.InvalidCharacter); |
| 386 | try testError("A/==", error.InvalidPadding); | 393 | try testError(codecs, "A===", error.InvalidCharacter); |
| 387 | try testError("A===", error.InvalidCharacter); | 394 | try testError(codecs, "====", error.InvalidCharacter); |
| 388 | try testError("====", error.InvalidCharacter); | 395 | |
| 389 | 396 | try testNoSpaceLeftError(codecs, "AA"); | |
| 390 | try testOutputTooSmallError("AA=="); | 397 | try testNoSpaceLeftError(codecs, "AAA"); |
| 391 | try testOutputTooSmallError("AAA="); | 398 | try testNoSpaceLeftError(codecs, "AAAA"); |
| 392 | try testOutputTooSmallError("AAAA"); | 399 | try testNoSpaceLeftError(codecs, "AAAAAA"); |
| 393 | try testOutputTooSmallError("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 | // Base64Encoder | 403 | // Base64Encoder |
| 398 | { | 404 | { |
| 399 | var buffer: [0x100]u8 = undefined; | 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 | testing.expectEqualSlices(u8, expected_encoded, encoded); | 407 | testing.expectEqualSlices(u8, expected_encoded, encoded); |
| 402 | } | 408 | } |
| 403 | 409 | ||
| 404 | // Base64Decoder | 410 | // Base64Decoder |
| 405 | { | 411 | { |
| 406 | var buffer: [0x100]u8 = undefined; | 412 | var buffer: [0x100]u8 = undefined; |
| 407 | var decoded = buffer[0..try standard_decoder.calcSize(expected_encoded)]; | 413 | var decoded = buffer[0..try codecs.Decoder.calcSizeForSlice(expected_encoded)]; |
| 408 | try standard_decoder.decode(decoded, expected_encoded); | 414 | try codecs.Decoder.decode(decoded, expected_encoded); |
| 409 | testing.expectEqualSlices(u8, expected_decoded, decoded); | 415 | testing.expectEqualSlices(u8, expected_decoded, decoded); |
| 410 | } | 416 | } |
| 411 | 417 | ||
| 412 | // Base64DecoderWithIgnore | 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 | var buffer: [0x100]u8 = undefined; | 421 | var buffer: [0x100]u8 = undefined; |
| 416 | var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)]; | 422 | var decoded = buffer[0..try decoder_ignore_nothing.calcSizeUpperBound(expected_encoded.len)]; |
| 417 | var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded); | 423 | var written = try decoder_ignore_nothing.decode(decoded, expected_encoded); |
| 418 | testing.expect(written <= decoded.len); | 424 | testing.expect(written <= decoded.len); |
| 419 | testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]); | 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 { | 429 | fn testDecodeIgnoreSpace(codecs: Codecs, expected_decoded: []const u8, encoded: []const u8) !void { |
| 432 | const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " "); | 430 | const decoder_ignore_space = codecs.decoderWithIgnore(" "); |
| 433 | var buffer: [0x100]u8 = undefined; | 431 | var buffer: [0x100]u8 = undefined; |
| 434 | var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)]; | 432 | var decoded = buffer[0..try decoder_ignore_space.calcSizeUpperBound(encoded.len)]; |
| 435 | var written = try standard_decoder_ignore_space.decode(decoded, encoded); | 433 | var written = try decoder_ignore_space.decode(decoded, encoded); |
| 436 | testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]); | 434 | testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]); |
| 437 | } | 435 | } |
| 438 | 436 | ||
| 439 | fn testError(encoded: []const u8, expected_err: anyerror) !void { | 437 | fn testError(codecs: Codecs, encoded: []const u8, expected_err: anyerror) !void { |
| 440 | const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " "); | 438 | const decoder_ignore_space = codecs.decoderWithIgnore(" "); |
| 441 | var buffer: [0x100]u8 = undefined; | 439 | var buffer: [0x100]u8 = undefined; |
| 442 | if (standard_decoder.calcSize(encoded)) |decoded_size| { | 440 | if (codecs.Decoder.calcSizeForSlice(encoded)) |decoded_size| { |
| 443 | var decoded = buffer[0..decoded_size]; | 441 | var decoded = buffer[0..decoded_size]; |
| 444 | if (standard_decoder.decode(decoded, encoded)) |_| { | 442 | if (codecs.Decoder.decode(decoded, encoded)) |_| { |
| 445 | return error.ExpectedError; | 443 | return error.ExpectedError; |
| 446 | } else |err| if (err != expected_err) return err; | 444 | } else |err| if (err != expected_err) return err; |
| 447 | } else |err| if (err != expected_err) return err; | 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 | return error.ExpectedError; | 448 | return error.ExpectedError; |
| 451 | } else |err| if (err != expected_err) return err; | 449 | } else |err| if (err != expected_err) return err; |
| 452 | } | 450 | } |
| 453 | 451 | ||
| 454 | fn testOutputTooSmallError(encoded: []const u8) !void { | 452 | fn testNoSpaceLeftError(codecs: Codecs, encoded: []const u8) !void { |
| 455 | const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " "); | 453 | const decoder_ignore_space = codecs.decoderWithIgnore(" "); |
| 456 | var buffer: [0x100]u8 = undefined; | 454 | var buffer: [0x100]u8 = undefined; |
| 457 | var decoded = buffer[0 .. calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1]; | 455 | var decoded = buffer[0 .. (try codecs.Decoder.calcSizeForSlice(encoded)) - 1]; |
| 458 | if (standard_decoder_ignore_space.decode(decoded, encoded)) |_| { | 456 | if (decoder_ignore_space.decode(decoded, encoded)) |_| { |
| 459 | return error.ExpectedError; | 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,7 +51,7 @@ pub const Builder = struct { |
| 51 | default_step: *Step, | 51 | default_step: *Step, |
| 52 | env_map: *BufMap, | 52 | env_map: *BufMap, |
| 53 | top_level_steps: ArrayList(*TopLevelStep), | 53 | top_level_steps: ArrayList(*TopLevelStep), |
| 54 | install_prefix: ?[]const u8, | 54 | install_prefix: []const u8, |
| 55 | dest_dir: ?[]const u8, | 55 | dest_dir: ?[]const u8, |
| 56 | lib_dir: []const u8, | 56 | lib_dir: []const u8, |
| 57 | exe_dir: []const u8, | 57 | exe_dir: []const u8, |
| ... | @@ -156,7 +156,7 @@ pub const Builder = struct { | ... | @@ -156,7 +156,7 @@ pub const Builder = struct { |
| 156 | .default_step = undefined, | 156 | .default_step = undefined, |
| 157 | .env_map = env_map, | 157 | .env_map = env_map, |
| 158 | .search_prefixes = ArrayList([]const u8).init(allocator), | 158 | .search_prefixes = ArrayList([]const u8).init(allocator), |
| 159 | .install_prefix = null, | 159 | .install_prefix = undefined, |
| 160 | .lib_dir = undefined, | 160 | .lib_dir = undefined, |
| 161 | .exe_dir = undefined, | 161 | .exe_dir = undefined, |
| 162 | .h_dir = undefined, | 162 | .h_dir = undefined, |
| ... | @@ -190,22 +190,13 @@ pub const Builder = struct { | ... | @@ -190,22 +190,13 @@ pub const Builder = struct { |
| 190 | } | 190 | } |
| 191 | 191 | ||
| 192 | /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file. | 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 { | 193 | pub fn resolveInstallPrefix(self: *Builder, install_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 { | ||
| 199 | if (self.dest_dir) |dest_dir| { | 194 | if (self.dest_dir) |dest_dir| { |
| 200 | const install_prefix = self.install_prefix orelse "/usr"; | 195 | self.install_prefix = install_prefix orelse "/usr"; |
| 201 | self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, install_prefix }) catch unreachable; | 196 | self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, self.install_prefix }) catch unreachable; |
| 202 | } else { | 197 | } else { |
| 203 | const install_prefix = self.install_prefix orelse blk: { | 198 | self.install_prefix = install_prefix orelse self.cache_root; |
| 204 | const p = self.cache_root; | 199 | self.install_path = self.install_prefix; |
| 205 | self.install_prefix = p; | ||
| 206 | break :blk p; | ||
| 207 | }; | ||
| 208 | self.install_path = install_prefix; | ||
| 209 | } | 200 | } |
| 210 | self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable; | 201 | self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable; |
| 211 | self.exe_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "bin" }) catch unreachable; | 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,9 +1250,9 @@ fn formatDuration(ns: u64, comptime fmt: []const u8, options: std.fmt.FormatOpti |
| 1250 | const kunits = ns_remaining * 1000 / unit.ns; | 1250 | const kunits = ns_remaining * 1000 / unit.ns; |
| 1251 | if (kunits >= 1000) { | 1251 | if (kunits >= 1000) { |
| 1252 | try formatInt(kunits / 1000, 10, false, .{}, writer); | 1252 | try formatInt(kunits / 1000, 10, false, .{}, writer); |
| 1253 | if (kunits > 1000) { | 1253 | const frac = kunits % 1000; |
| 1254 | if (frac > 0) { | ||
| 1254 | // Write up to 3 decimal places | 1255 | // Write up to 3 decimal places |
| 1255 | const frac = kunits % 1000; | ||
| 1256 | var buf = [_]u8{ '.', 0, 0, 0 }; | 1256 | var buf = [_]u8{ '.', 0, 0, 0 }; |
| 1257 | _ = formatIntBuf(buf[1..], frac, 10, false, .{ .fill = '0', .width = 3 }); | 1257 | _ = formatIntBuf(buf[1..], frac, 10, false, .{ .fill = '0', .width = 3 }); |
| 1258 | var end: usize = 4; | 1258 | var end: usize = 4; |
| ... | @@ -1286,9 +1286,14 @@ test "fmtDuration" { | ... | @@ -1286,9 +1286,14 @@ test "fmtDuration" { |
| 1286 | .{ .s = "1us", .d = std.time.ns_per_us }, | 1286 | .{ .s = "1us", .d = std.time.ns_per_us }, |
| 1287 | .{ .s = "1.45us", .d = 1450 }, | 1287 | .{ .s = "1.45us", .d = 1450 }, |
| 1288 | .{ .s = "1.5us", .d = 3 * std.time.ns_per_us / 2 }, | 1288 | .{ .s = "1.5us", .d = 3 * std.time.ns_per_us / 2 }, |
| 1289 | .{ .s = "14.5us", .d = 14500 }, | ||
| 1290 | .{ .s = "145us", .d = 145000 }, | ||
| 1289 | .{ .s = "999.999us", .d = std.time.ns_per_ms - 1 }, | 1291 | .{ .s = "999.999us", .d = std.time.ns_per_ms - 1 }, |
| 1290 | .{ .s = "1ms", .d = std.time.ns_per_ms + 1 }, | 1292 | .{ .s = "1ms", .d = std.time.ns_per_ms + 1 }, |
| 1291 | .{ .s = "1.5ms", .d = 3 * std.time.ns_per_ms / 2 }, | 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 | .{ .s = "999.999ms", .d = std.time.ns_per_s - 1 }, | 1297 | .{ .s = "999.999ms", .d = std.time.ns_per_s - 1 }, |
| 1293 | .{ .s = "1s", .d = std.time.ns_per_s }, | 1298 | .{ .s = "1s", .d = std.time.ns_per_s }, |
| 1294 | .{ .s = "59.999s", .d = std.time.ns_per_min - 1 }, | 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,13 +50,13 @@ pub const MAX_PATH_BYTES = switch (builtin.os.tag) { |
| 50 | else => @compileError("Unsupported OS"), | 50 | else => @compileError("Unsupported OS"), |
| 51 | }; | 51 | }; |
| 52 | 52 | ||
| 53 | pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; | 53 | pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*; |
| 54 | 54 | ||
| 55 | /// Base64 encoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem. | 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 | /// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem. | 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 | /// Whether or not async file system syscalls need a dedicated thread because the operating | 61 | /// Whether or not async file system syscalls need a dedicated thread because the operating |
| 62 | /// system does not support non-blocking I/O on the file system. | 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,7 +77,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: |
| 77 | const dirname = path.dirname(new_path) orelse "."; | 77 | const dirname = path.dirname(new_path) orelse "."; |
| 78 | 78 | ||
| 79 | var rand_buf: [AtomicFile.RANDOM_BYTES]u8 = undefined; | 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 | defer allocator.free(tmp_path); | 81 | defer allocator.free(tmp_path); |
| 82 | mem.copy(u8, tmp_path[0..], dirname); | 82 | mem.copy(u8, tmp_path[0..], dirname); |
| 83 | tmp_path[dirname.len] = path.sep; | 83 | tmp_path[dirname.len] = path.sep; |
| ... | @@ -142,7 +142,7 @@ pub const AtomicFile = struct { | ... | @@ -142,7 +142,7 @@ pub const AtomicFile = struct { |
| 142 | const InitError = File.OpenError; | 142 | const InitError = File.OpenError; |
| 143 | 143 | ||
| 144 | const RANDOM_BYTES = 12; | 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 | /// Note that the `Dir.atomicFile` API may be more handy than this lower-level function. | 147 | /// Note that the `Dir.atomicFile` API may be more handy than this lower-level function. |
| 148 | pub fn init( | 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,7 +95,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { |
| 95 | .EnumLiteral, | 95 | .EnumLiteral, |
| 96 | .Frame, | 96 | .Frame, |
| 97 | .Float, | 97 | .Float, |
| 98 | => @compileError("cannot hash this type"), | 98 | => @compileError("unable to hash type " ++ @typeName(Key)), |
| 99 | 99 | ||
| 100 | // Help the optimizer see that hashing an int is easy by inlining! | 100 | // Help the optimizer see that hashing an int is easy by inlining! |
| 101 | // TODO Check if the situation is better after #561 is resolved. | 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,6 +1373,20 @@ test "mem.tokenize (multibyte)" { |
| 1373 | testing.expect(it.next() == null); | 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 | /// Returns an iterator that iterates over the slices of `buffer` that | 1390 | /// Returns an iterator that iterates over the slices of `buffer` that |
| 1377 | /// are separated by bytes in `delimiter`. | 1391 | /// are separated by bytes in `delimiter`. |
| 1378 | /// split("abc|def||ghi", "|") | 1392 | /// split("abc|def||ghi", "|") |
| ... | @@ -1471,6 +1485,11 @@ pub const TokenIterator = struct { | ... | @@ -1471,6 +1485,11 @@ pub const TokenIterator = struct { |
| 1471 | return self.buffer[index..]; | 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 | fn isSplitByte(self: TokenIterator, byte: u8) bool { | 1493 | fn isSplitByte(self: TokenIterator, byte: u8) bool { |
| 1475 | for (self.delimiter_bytes) |delimiter_byte| { | 1494 | for (self.delimiter_bytes) |delimiter_byte| { |
| 1476 | if (byte == delimiter_byte) { | 1495 | if (byte == delimiter_byte) { |
lib/std/os.zig+1| ... | @@ -5610,6 +5610,7 @@ pub fn recvfrom( | ... | @@ -5610,6 +5610,7 @@ pub fn recvfrom( |
| 5610 | EAGAIN => return error.WouldBlock, | 5610 | EAGAIN => return error.WouldBlock, |
| 5611 | ENOMEM => return error.SystemResources, | 5611 | ENOMEM => return error.SystemResources, |
| 5612 | ECONNREFUSED => return error.ConnectionRefused, | 5612 | ECONNREFUSED => return error.ConnectionRefused, |
| 5613 | ECONNRESET => return error.ConnectionResetByPeer, | ||
| 5613 | else => |err| return unexpectedErrno(err), | 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,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 | pub fn syscall6( | 121 | pub fn syscall6( |
| 119 | number: SYS, | 122 | number: SYS, |
| 120 | arg1: usize, | 123 | arg1: usize, |
| ... | @@ -146,6 +149,40 @@ pub fn syscall6( | ... | @@ -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 | /// This matches the libc clone function. | 186 | /// This matches the libc clone function. |
| 150 | pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; | 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,7 +78,8 @@ pub const BootServices = extern struct { |
| 78 | /// Returns an array of handles that support a specified protocol. | 78 | /// Returns an array of handles that support a specified protocol. |
| 79 | locateHandle: fn (LocateSearchType, ?*align(8) const Guid, ?*const c_void, *usize, [*]Handle) callconv(.C) Status, | 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 | installConfigurationTable: Status, // TODO | 83 | installConfigurationTable: Status, // TODO |
| 83 | 84 | ||
| 84 | /// Loads an EFI image into memory. | 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,7 +373,7 @@ pub fn createWindowExA(dwExStyle: u32, lpClassName: [*:0]const u8, lpWindowName: |
| 373 | } | 373 | } |
| 374 | 374 | ||
| 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; | 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 | 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 { | 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 | const function = selectSymbol(CreateWindowExW, pfnCreateWindowExW, .win2k); | 378 | const function = selectSymbol(CreateWindowExW, pfnCreateWindowExW, .win2k); |
| 379 | const window = function(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWindParent, hMenu, hInstance, lpParam); | 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,6 +60,7 @@ pub fn main() !void { |
| 60 | const stderr_stream = io.getStdErr().writer(); | 60 | const stderr_stream = io.getStdErr().writer(); |
| 61 | const stdout_stream = io.getStdOut().writer(); | 61 | const stdout_stream = io.getStdOut().writer(); |
| 62 | 62 | ||
| 63 | var install_prefix: ?[]const u8 = null; | ||
| 63 | while (nextArg(args, &arg_idx)) |arg| { | 64 | while (nextArg(args, &arg_idx)) |arg| { |
| 64 | if (mem.startsWith(u8, arg, "-D")) { | 65 | if (mem.startsWith(u8, arg, "-D")) { |
| 65 | const option_contents = arg[2..]; | 66 | const option_contents = arg[2..]; |
| ... | @@ -82,7 +83,7 @@ pub fn main() !void { | ... | @@ -82,7 +83,7 @@ pub fn main() !void { |
| 82 | } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { | 83 | } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { |
| 83 | return usage(builder, false, stdout_stream); | 84 | return usage(builder, false, stdout_stream); |
| 84 | } else if (mem.eql(u8, arg, "--prefix")) { | 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 | warn("Expected argument after --prefix\n\n", .{}); | 87 | warn("Expected argument after --prefix\n\n", .{}); |
| 87 | return usageAndErr(builder, false, stderr_stream); | 88 | return usageAndErr(builder, false, stderr_stream); |
| 88 | }; | 89 | }; |
| ... | @@ -134,7 +135,7 @@ pub fn main() !void { | ... | @@ -134,7 +135,7 @@ pub fn main() !void { |
| 134 | } | 135 | } |
| 135 | } | 136 | } |
| 136 | 137 | ||
| 137 | builder.resolveInstallPrefix(); | 138 | builder.resolveInstallPrefix(install_prefix); |
| 138 | try runBuild(builder); | 139 | try runBuild(builder); |
| 139 | 140 | ||
| 140 | if (builder.validateUserInputDidItFail()) | 141 | if (builder.validateUserInputDidItFail()) |
| ... | @@ -162,8 +163,7 @@ fn runBuild(builder: *Builder) anyerror!void { | ... | @@ -162,8 +163,7 @@ fn runBuild(builder: *Builder) anyerror!void { |
| 162 | fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void { | 163 | fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void { |
| 163 | // run the build script to collect the options | 164 | // run the build script to collect the options |
| 164 | if (!already_ran_build) { | 165 | if (!already_ran_build) { |
| 165 | builder.setInstallPrefix(null); | 166 | builder.resolveInstallPrefix(null); |
| 166 | builder.resolveInstallPrefix(); | ||
| 167 | try runBuild(builder); | 167 | try runBuild(builder); |
| 168 | } | 168 | } |
| 169 | 169 |
lib/std/testing.zig+1-1| ... | @@ -298,7 +298,7 @@ pub const TmpDir = struct { | ... | @@ -298,7 +298,7 @@ pub const TmpDir = struct { |
| 298 | sub_path: [sub_path_len]u8, | 298 | sub_path: [sub_path_len]u8, |
| 299 | 299 | ||
| 300 | const random_bytes_count = 12; | 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 | pub fn cleanup(self: *TmpDir) void { | 303 | pub fn cleanup(self: *TmpDir) void { |
| 304 | self.dir.close(); | 304 | self.dir.close(); |
src/Compilation.zig+17-6| ... | @@ -3188,7 +3188,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node | ... | @@ -3188,7 +3188,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 3188 | id_symlink_basename, | 3188 | id_symlink_basename, |
| 3189 | &prev_digest_buf, | 3189 | &prev_digest_buf, |
| 3190 | ) catch |err| blk: { | 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 | // Handle this as a cache miss. | 3196 | // Handle this as a cache miss. |
| 3193 | break :blk prev_digest_buf[0..0]; | 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,10 +3200,13 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 3196 | if (!mem.eql(u8, prev_digest[0..digest.len], &digest)) | 3200 | if (!mem.eql(u8, prev_digest[0..digest.len], &digest)) |
| 3197 | break :hit; | 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 | var flags_bytes: [1]u8 = undefined; | 3207 | var flags_bytes: [1]u8 = undefined; |
| 3201 | _ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch { | 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 | break :hit; | 3210 | break :hit; |
| 3204 | }; | 3211 | }; |
| 3205 | 3212 | ||
| ... | @@ -3219,7 +3226,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node | ... | @@ -3219,7 +3226,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 3219 | mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]); | 3226 | mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]); |
| 3220 | return; | 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 | man.unhit(prev_hash_state, input_file_count); | 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,8 +3377,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 3366 | // Update the small file with the digest. If it fails we can continue; it only | 3377 | // Update the small file with the digest. If it fails we can continue; it only |
| 3367 | // means that the next invocation will have an unnecessary cache miss. | 3378 | // means that the next invocation will have an unnecessary cache miss. |
| 3368 | const stage1_flags_byte = @bitCast(u8, mod.stage1_flags); | 3379 | const stage1_flags_byte = @bitCast(u8, mod.stage1_flags); |
| 3369 | log.debug("stage1 {s} final digest={} flags={x}", .{ | 3380 | log.debug("stage1 {s} final digest={s} flags={x}", .{ |
| 3370 | mod.root_pkg.root_src_path, digest, stage1_flags_byte, | 3381 | mod.root_pkg.root_src_path, std.fmt.fmtSliceHexLower(&digest), stage1_flags_byte, |
| 3371 | }); | 3382 | }); |
| 3372 | var digest_plus_flags: [digest.len + 2]u8 = undefined; | 3383 | var digest_plus_flags: [digest.len + 2]u8 = undefined; |
| 3373 | digest_plus_flags[0..digest.len].* = digest; | 3384 | digest_plus_flags[0..digest.len].* = digest; |
src/codegen/wasm.zig+41-3| ... | @@ -94,7 +94,7 @@ pub const Context = struct { | ... | @@ -94,7 +94,7 @@ pub const Context = struct { |
| 94 | return switch (ty.tag()) { | 94 | return switch (ty.tag()) { |
| 95 | .f32 => wasm.valtype(.f32), | 95 | .f32 => wasm.valtype(.f32), |
| 96 | .f64 => wasm.valtype(.f64), | 96 | .f64 => wasm.valtype(.f64), |
| 97 | .u32, .i32 => wasm.valtype(.i32), | 97 | .u32, .i32, .bool => wasm.valtype(.i32), |
| 98 | .u64, .i64 => wasm.valtype(.i64), | 98 | .u64, .i64 => wasm.valtype(.i64), |
| 99 | else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}), | 99 | else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}), |
| 100 | }; | 100 | }; |
| ... | @@ -207,6 +207,7 @@ pub const Context = struct { | ... | @@ -207,6 +207,7 @@ pub const Context = struct { |
| 207 | .alloc => self.genAlloc(inst.castTag(.alloc).?), | 207 | .alloc => self.genAlloc(inst.castTag(.alloc).?), |
| 208 | .arg => self.genArg(inst.castTag(.arg).?), | 208 | .arg => self.genArg(inst.castTag(.arg).?), |
| 209 | .block => self.genBlock(inst.castTag(.block).?), | 209 | .block => self.genBlock(inst.castTag(.block).?), |
| 210 | .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?), | ||
| 210 | .br => self.genBr(inst.castTag(.br).?), | 211 | .br => self.genBr(inst.castTag(.br).?), |
| 211 | .call => self.genCall(inst.castTag(.call).?), | 212 | .call => self.genCall(inst.castTag(.call).?), |
| 212 | .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq), | 213 | .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq), |
| ... | @@ -220,9 +221,11 @@ pub const Context = struct { | ... | @@ -220,9 +221,11 @@ pub const Context = struct { |
| 220 | .dbg_stmt => WValue.none, | 221 | .dbg_stmt => WValue.none, |
| 221 | .load => self.genLoad(inst.castTag(.load).?), | 222 | .load => self.genLoad(inst.castTag(.load).?), |
| 222 | .loop => self.genLoop(inst.castTag(.loop).?), | 223 | .loop => self.genLoop(inst.castTag(.loop).?), |
| 224 | .not => self.genNot(inst.castTag(.not).?), | ||
| 223 | .ret => self.genRet(inst.castTag(.ret).?), | 225 | .ret => self.genRet(inst.castTag(.ret).?), |
| 224 | .retvoid => WValue.none, | 226 | .retvoid => WValue.none, |
| 225 | .store => self.genStore(inst.castTag(.store).?), | 227 | .store => self.genStore(inst.castTag(.store).?), |
| 228 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), | ||
| 226 | else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}), | 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,7 +331,7 @@ pub const Context = struct { |
| 328 | try writer.writeByte(wasm.opcode(.i32_const)); | 331 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 329 | try leb.writeILEB128(writer, inst.val.toUnsignedInt()); | 332 | try leb.writeILEB128(writer, inst.val.toUnsignedInt()); |
| 330 | }, | 333 | }, |
| 331 | .i32 => { | 334 | .i32, .bool => { |
| 332 | try writer.writeByte(wasm.opcode(.i32_const)); | 335 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 333 | try leb.writeILEB128(writer, inst.val.toSignedInt()); | 336 | try leb.writeILEB128(writer, inst.val.toSignedInt()); |
| 334 | }, | 337 | }, |
| ... | @@ -413,7 +416,14 @@ pub const Context = struct { | ... | @@ -413,7 +416,14 @@ pub const Context = struct { |
| 413 | 416 | ||
| 414 | // insert blocks at the position of `offset` so | 417 | // insert blocks at the position of `offset` so |
| 415 | // the condition can jump to it | 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 | const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty); | 427 | const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty); |
| 418 | try self.startBlock(.block, block_ty, offset); | 428 | try self.startBlock(.block, block_ty, offset); |
| 419 | 429 | ||
| ... | @@ -522,4 +532,32 @@ pub const Context = struct { | ... | @@ -522,4 +532,32 @@ pub const Context = struct { |
| 522 | 532 | ||
| 523 | return .none; | 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,7 +1,7 @@ |
| 1 | pub const have_llvm = true; | 1 | pub const have_llvm = true; |
| 2 | pub const version: [:0]const u8 = "@ZIG_VERSION@"; | 2 | pub const version: [:0]const u8 = "@ZIG_VERSION@"; |
| 3 | pub const semver = try @import("std").SemanticVersion.parse(version); | 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 | pub const enable_tracy = false; | 5 | pub const enable_tracy = false; |
| 6 | pub const is_stage1 = true; | 6 | pub const is_stage1 = true; |
| 7 | pub const skip_non_native = false; | 7 | pub const skip_non_native = false; |
src/introspect.zig+8| ... | @@ -61,6 +61,14 @@ pub fn findZigLibDirFromSelfExe( | ... | @@ -61,6 +61,14 @@ pub fn findZigLibDirFromSelfExe( |
| 61 | 61 | ||
| 62 | /// Caller owns returned memory. | 62 | /// Caller owns returned memory. |
| 63 | pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 { | 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 | const appname = "zig"; | 72 | const appname = "zig"; |
| 65 | 73 | ||
| 66 | if (std.Target.current.os.tag != .windows) { | 74 | if (std.Target.current.os.tag != .windows) { |
src/link/MachO/Archive.zig+25-3| ... | @@ -20,6 +20,11 @@ name: []u8, | ... | @@ -20,6 +20,11 @@ name: []u8, |
| 20 | 20 | ||
| 21 | objects: std.ArrayListUnmanaged(Object) = .{}, | 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 | // Archive files start with the ARMAG identifying string. Then follows a | 28 | // Archive files start with the ARMAG identifying string. Then follows a |
| 24 | // `struct ar_hdr', and as many bytes of member file data as its `ar_size' | 29 | // `struct ar_hdr', and as many bytes of member file data as its `ar_size' |
| 25 | // member indicates, for each member file. | 30 | // member indicates, for each member file. |
| ... | @@ -88,6 +93,11 @@ pub fn deinit(self: *Archive) void { | ... | @@ -88,6 +93,11 @@ pub fn deinit(self: *Archive) void { |
| 88 | object.deinit(); | 93 | object.deinit(); |
| 89 | } | 94 | } |
| 90 | self.objects.deinit(self.allocator); | 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 | self.file.close(); | 101 | self.file.close(); |
| 92 | } | 102 | } |
| 93 | 103 | ||
| ... | @@ -159,8 +169,20 @@ fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 { | ... | @@ -159,8 +169,20 @@ fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 { |
| 159 | }; | 169 | }; |
| 160 | const object_offset = try symtab_reader.readIntLittle(u32); | 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 | // Here, we assume that symbols are NOT sorted in any way, and | 186 | // Here, we assume that symbols are NOT sorted in any way, and |
| 165 | // they point to objects in sequence. | 187 | // they point to objects in sequence. |
| 166 | if (object_offsets.items[last] != object_offset) { | 188 | if (object_offsets.items[last] != object_offset) { |
| ... | @@ -248,8 +270,8 @@ fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 { | ... | @@ -248,8 +270,8 @@ fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 { |
| 248 | var n = try allocator.alloc(u8, len); | 270 | var n = try allocator.alloc(u8, len); |
| 249 | defer allocator.free(n); | 271 | defer allocator.free(n); |
| 250 | try reader.readNoEof(n); | 272 | try reader.readNoEof(n); |
| 251 | const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0)); | 273 | const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0)) orelse n.len; |
| 252 | name = try allocator.dupe(u8, n[0..actual_len.?]); | 274 | name = try allocator.dupe(u8, n[0..actual_len]); |
| 253 | }, | 275 | }, |
| 254 | } | 276 | } |
| 255 | return name; | 277 | return name; |
src/link/MachO/Object.zig+1| ... | @@ -164,6 +164,7 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi | ... | @@ -164,6 +164,7 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi |
| 164 | }, | 164 | }, |
| 165 | macho.LC_DATA_IN_CODE => { | 165 | macho.LC_DATA_IN_CODE => { |
| 166 | self.data_in_code_cmd_index = i; | 166 | self.data_in_code_cmd_index = i; |
| 167 | cmd.LinkeditData.dataoff += offset_mod; | ||
| 167 | }, | 168 | }, |
| 168 | else => { | 169 | else => { |
| 169 | log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()}); | 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,13 +60,17 @@ stub_helper_section_index: ?u16 = null, |
| 60 | text_const_section_index: ?u16 = null, | 60 | text_const_section_index: ?u16 = null, |
| 61 | cstring_section_index: ?u16 = null, | 61 | cstring_section_index: ?u16 = null, |
| 62 | 62 | ||
| 63 | // __DATA segment sections | 63 | // __DATA_CONST segment sections |
| 64 | got_section_index: ?u16 = null, | 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 | tlv_section_index: ?u16 = null, | 70 | tlv_section_index: ?u16 = null, |
| 66 | tlv_data_section_index: ?u16 = null, | 71 | tlv_data_section_index: ?u16 = null, |
| 67 | tlv_bss_section_index: ?u16 = null, | 72 | tlv_bss_section_index: ?u16 = null, |
| 68 | la_symbol_ptr_section_index: ?u16 = null, | 73 | la_symbol_ptr_section_index: ?u16 = null, |
| 69 | data_const_section_index: ?u16 = null, | ||
| 70 | data_section_index: ?u16 = null, | 74 | data_section_index: ?u16 = null, |
| 71 | bss_section_index: ?u16 = null, | 75 | bss_section_index: ?u16 = null, |
| 72 | 76 | ||
| ... | @@ -448,6 +452,46 @@ fn updateMetadata(self: *Zld, object_id: u16) !void { | ... | @@ -448,6 +452,46 @@ fn updateMetadata(self: *Zld, object_id: u16) !void { |
| 448 | .reserved3 = 0, | 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 | macho.S_ZEROFILL => { | 495 | macho.S_ZEROFILL => { |
| 452 | if (!mem.eql(u8, segname, "__DATA")) continue; | 496 | if (!mem.eql(u8, segname, "__DATA")) continue; |
| 453 | if (self.bss_section_index != null) continue; | 497 | if (self.bss_section_index != null) continue; |
| ... | @@ -583,6 +627,18 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { | ... | @@ -583,6 +627,18 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { |
| 583 | .sect = self.cstring_section_index.?, | 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 | macho.S_ZEROFILL => { | 642 | macho.S_ZEROFILL => { |
| 587 | break :blk .{ | 643 | break :blk .{ |
| 588 | .seg = self.data_segment_cmd_index.?, | 644 | .seg = self.data_segment_cmd_index.?, |
| ... | @@ -684,6 +740,8 @@ fn sortSections(self: *Zld) !void { | ... | @@ -684,6 +740,8 @@ fn sortSections(self: *Zld) !void { |
| 684 | 740 | ||
| 685 | const indices = &[_]*?u16{ | 741 | const indices = &[_]*?u16{ |
| 686 | &self.got_section_index, | 742 | &self.got_section_index, |
| 743 | &self.mod_init_func_section_index, | ||
| 744 | &self.mod_term_func_section_index, | ||
| 687 | &self.data_const_section_index, | 745 | &self.data_const_section_index, |
| 688 | }; | 746 | }; |
| 689 | for (indices) |maybe_index| { | 747 | for (indices) |maybe_index| { |
| ... | @@ -2471,6 +2529,42 @@ fn writeRebaseInfoTable(self: *Zld) !void { | ... | @@ -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 | if (self.la_symbol_ptr_section_index) |idx| { | 2568 | if (self.la_symbol_ptr_section_index) |idx| { |
| 2475 | try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len); | 2569 | try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len); |
| 2476 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | 2570 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| ... | @@ -2707,7 +2801,15 @@ fn writeDebugInfo(self: *Zld) !void { | ... | @@ -2707,7 +2801,15 @@ fn writeDebugInfo(self: *Zld) !void { |
| 2707 | }; | 2801 | }; |
| 2708 | defer debug_info.deinit(self.allocator); | 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 | const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name); | 2813 | const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name); |
| 2712 | const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir); | 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,7 +557,7 @@ fn buildOutputType( |
| 557 | var test_filter: ?[]const u8 = null; | 557 | var test_filter: ?[]const u8 = null; |
| 558 | var test_name_prefix: ?[]const u8 = null; | 558 | var test_name_prefix: ?[]const u8 = null; |
| 559 | var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR"); | 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 | var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR"); | 561 | var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR"); |
| 562 | var main_pkg_path: ?[]const u8 = null; | 562 | var main_pkg_path: ?[]const u8 = null; |
| 563 | var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; | 563 | var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; |
src/stage1/all_types.hpp-4| ... | @@ -2139,10 +2139,6 @@ struct CodeGen { | ... | @@ -2139,10 +2139,6 @@ struct CodeGen { |
| 2139 | Buf llvm_ir_file_output_path; | 2139 | Buf llvm_ir_file_output_path; |
| 2140 | Buf analysis_json_output_path; | 2140 | Buf analysis_json_output_path; |
| 2141 | Buf docs_output_path; | 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 | Buf *builtin_zig_path; | 2143 | Buf *builtin_zig_path; |
| 2148 | Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir. | 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,7 +270,10 @@ pub const Context = struct { |
| 270 | global_scope: *Scope.Root, | 270 | global_scope: *Scope.Root, |
| 271 | clang_context: *clang.ASTContext, | 271 | clang_context: *clang.ASTContext, |
| 272 | mangle_count: u32 = 0, | 272 | mangle_count: u32 = 0, |
| 273 | /// Table of record decls that have been demoted to opaques. | ||
| 273 | opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{}, | 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 | /// This one is different than the root scope's name table. This contains | 278 | /// This one is different than the root scope's name table. This contains |
| 276 | /// a list of names that we found by visiting all the top level decls without | 279 | /// a list of names that we found by visiting all the top level decls without |
| ... | @@ -338,6 +341,7 @@ pub fn translate( | ... | @@ -338,6 +341,7 @@ pub fn translate( |
| 338 | context.alias_list.deinit(); | 341 | context.alias_list.deinit(); |
| 339 | context.global_names.deinit(gpa); | 342 | context.global_names.deinit(gpa); |
| 340 | context.opaque_demotes.deinit(gpa); | 343 | context.opaque_demotes.deinit(gpa); |
| 344 | context.unnamed_typedefs.deinit(gpa); | ||
| 341 | context.global_scope.deinit(); | 345 | context.global_scope.deinit(); |
| 342 | } | 346 | } |
| 343 | 347 | ||
| ... | @@ -401,6 +405,51 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void { | ... | @@ -401,6 +405,51 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void { |
| 401 | if (decl.castToNamedDecl()) |named_decl| { | 405 | if (decl.castToNamedDecl()) |named_decl| { |
| 402 | const decl_name = try c.str(named_decl.getName_bytes_begin()); | 406 | const decl_name = try c.str(named_decl.getName_bytes_begin()); |
| 403 | try c.global_names.put(c.gpa, decl_name, {}); | 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,17 +801,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 752 | const toplevel = scope.id == .root; | 801 | const toplevel = scope.id == .root; |
| 753 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; | 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 | var is_union = false; | 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 | if (record_decl.isUnion()) { | 808 | if (record_decl.isUnion()) { |
| 767 | container_kind_name = "union"; | 809 | container_kind_name = "union"; |
| 768 | is_union = true; | 810 | is_union = true; |
| ... | @@ -773,7 +815,20 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD | ... | @@ -773,7 +815,20 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 773 | return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name}); | 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 | if (!toplevel) name = try bs.makeMangledName(c, name); | 832 | if (!toplevel) name = try bs.makeMangledName(c, name); |
| 778 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name); | 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,14 +929,19 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) E |
| 874 | const toplevel = scope.id == .root; | 929 | const toplevel = scope.id == .root; |
| 875 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; | 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 | var is_unnamed = false; | 932 | var is_unnamed = false; |
| 879 | if (bare_name.len == 0) { | 933 | var bare_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); |
| 880 | bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); | 934 | var name = bare_name; |
| 881 | is_unnamed = true; | 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 | if (!toplevel) _ = try bs.makeMangledName(c, name); | 945 | if (!toplevel) _ = try bs.makeMangledName(c, name); |
| 886 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name); | 946 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name); |
| 887 | 947 | ||
| ... | @@ -1063,6 +1123,7 @@ fn transStmt( | ... | @@ -1063,6 +1123,7 @@ fn transStmt( |
| 1063 | const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt); | 1123 | const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt); |
| 1064 | return transExpr(c, scope, gen_sel.getResultExpr(), result_used); | 1124 | return transExpr(c, scope, gen_sel.getResultExpr(), result_used); |
| 1065 | }, | 1125 | }, |
| 1126 | // When adding new cases here, see comment for maybeBlockify() | ||
| 1066 | else => { | 1127 | else => { |
| 1067 | return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)}); | 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,6 +2303,35 @@ fn transImplicitValueInitExpr( |
| 2242 | return transZeroInitExpr(c, scope, source_loc, ty); | 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 | fn transIfStmt( | 2335 | fn transIfStmt( |
| 2246 | c: *Context, | 2336 | c: *Context, |
| 2247 | scope: *Scope, | 2337 | scope: *Scope, |
| ... | @@ -2259,9 +2349,10 @@ fn transIfStmt( | ... | @@ -2259,9 +2349,10 @@ fn transIfStmt( |
| 2259 | const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond()); | 2349 | const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond()); |
| 2260 | const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); | 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 | const else_body = if (stmt.getElse()) |expr| | 2354 | const else_body = if (stmt.getElse()) |expr| |
| 2264 | try transStmt(c, scope, expr, .unused) | 2355 | try maybeBlockify(c, scope, expr) |
| 2265 | else | 2356 | else |
| 2266 | null; | 2357 | null; |
| 2267 | return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body }); | 2358 | return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body }); |
| ... | @@ -2286,7 +2377,7 @@ fn transWhileLoop( | ... | @@ -2286,7 +2377,7 @@ fn transWhileLoop( |
| 2286 | .parent = scope, | 2377 | .parent = scope, |
| 2287 | .id = .loop, | 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 | return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null }); | 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,7 +2403,7 @@ fn transDoWhileLoop( |
| 2312 | const if_not_break = switch (cond.tag()) { | 2403 | const if_not_break = switch (cond.tag()) { |
| 2313 | .false_literal => return transStmt(c, scope, stmt.getBody(), .unused), | 2404 | .false_literal => return transStmt(c, scope, stmt.getBody(), .unused), |
| 2314 | .true_literal => { | 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 | return Tag.while_true.create(c.arena, body_node); | 2407 | return Tag.while_true.create(c.arena, body_node); |
| 2317 | }, | 2408 | }, |
| 2318 | else => try Tag.if_not_break.create(c.arena, cond), | 2409 | else => try Tag.if_not_break.create(c.arena, cond), |
| ... | @@ -2388,7 +2479,7 @@ fn transForLoop( | ... | @@ -2388,7 +2479,7 @@ fn transForLoop( |
| 2388 | else | 2479 | else |
| 2389 | null; | 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 | const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr }); | 2483 | const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr }); |
| 2393 | if (block_scope) |*bs| { | 2484 | if (block_scope) |*bs| { |
| 2394 | try bs.statements.append(while_node); | 2485 | try bs.statements.append(while_node); |
| ... | @@ -3106,43 +3197,34 @@ fn transCreateCompoundAssign( | ... | @@ -3106,43 +3197,34 @@ fn transCreateCompoundAssign( |
| 3106 | const requires_int_cast = blk: { | 3197 | const requires_int_cast = blk: { |
| 3107 | const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt); | 3198 | const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt); |
| 3108 | const are_same_sign = cIsSignedInteger(lhs_qt) == cIsSignedInteger(rhs_qt); | 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 | if (used == .unused) { | 3203 | if (used == .unused) { |
| 3112 | // common case | 3204 | // common case |
| 3113 | // c: lhs += rhs | 3205 | // c: lhs += rhs |
| 3114 | // zig: lhs += rhs | 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 | if ((is_mod or is_div) and is_signed) { | 3211 | if ((is_mod or is_div) and is_signed) { |
| 3116 | const lhs_node = try transExpr(c, scope, lhs, .used); | 3212 | if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node); |
| 3117 | const rhs_node = try transExpr(c, scope, rhs, .used); | 3213 | const operands = .{ .lhs = lhs_node, .rhs = rhs_node }; |
| 3118 | const builtin = if (is_mod) | 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 | else | 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 | return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used); | 3219 | return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used); |
| 3124 | } | 3220 | } |
| 3125 | 3221 | ||
| 3126 | const lhs_node = try transExpr(c, scope, lhs, .used); | 3222 | if (is_shift) { |
| 3127 | var rhs_node = if (is_shift or requires_int_cast) | 3223 | const cast_to_type = try qualTypeToLog2IntRef(c, scope, rhs_qt, loc); |
| 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 | |||
| 3143 | rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); | 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 | return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used); | 3228 | return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used); |
| 3147 | } | 3229 | } |
| 3148 | // worst case | 3230 | // worst case |
| ... | @@ -3164,29 +3246,24 @@ fn transCreateCompoundAssign( | ... | @@ -3164,29 +3246,24 @@ fn transCreateCompoundAssign( |
| 3164 | const lhs_node = try Tag.identifier.create(c.arena, ref); | 3246 | const lhs_node = try Tag.identifier.create(c.arena, ref); |
| 3165 | const ref_node = try Tag.deref.create(c.arena, lhs_node); | 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 | if ((is_mod or is_div) and is_signed) { | 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 | const builtin = if (is_mod) | 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 | else | 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 | const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used); | 3259 | const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used); |
| 3175 | try block_scope.statements.append(assign); | 3260 | try block_scope.statements.append(assign); |
| 3176 | } else { | 3261 | } else { |
| 3177 | var rhs_node = try transExpr(c, &block_scope.base, rhs, .used); | 3262 | if (is_shift) { |
| 3178 | 3263 | const cast_to_type = try qualTypeToLog2IntRef(c, &block_scope.base, rhs_qt, loc); | |
| 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 | |||
| 3186 | rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); | 3264 | rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); |
| 3187 | } | 3265 | } else if (requires_int_cast) { |
| 3188 | if (is_ptr_op_signed) { | 3266 | rhs_node = try transCCast(c, &block_scope.base, loc, lhs_qt, rhs_qt, rhs_node); |
| 3189 | rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); | ||
| 3190 | } | 3267 | } |
| 3191 | 3268 | ||
| 3192 | const assign = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, rhs_node, .used); | 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,4 +1244,68 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 1244 | \\ return 0; | 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,6 +175,41 @@ pub fn addCases(ctx: *TestContext) !void { |
| 175 | \\ return i; | 175 | \\ return i; |
| 176 | \\} | 176 | \\} |
| 177 | , "31\n"); | 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,9 +3,9 @@ const base64 = @import("std").base64; |
| 3 | export fn decode_base_64(dest_ptr: [*]u8, dest_len: usize, source_ptr: [*]const u8, source_len: usize) usize { | 3 | export fn decode_base_64(dest_ptr: [*]u8, dest_len: usize, source_ptr: [*]const u8, source_len: usize) usize { |
| 4 | const src = source_ptr[0..source_len]; | 4 | const src = source_ptr[0..source_len]; |
| 5 | const dest = dest_ptr[0..dest_len]; | 5 | const dest = dest_ptr[0..dest_len]; |
| 6 | const base64_decoder = base64.standard_decoder_unsafe; | 6 | const base64_decoder = base64.standard.Decoder; |
| 7 | const decoded_size = base64_decoder.calcSize(src); | 7 | const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable; |
| 8 | base64_decoder.decode(dest[0..decoded_size], src); | 8 | base64_decoder.decode(dest[0..decoded_size], src) catch unreachable; |
| 9 | return decoded_size; | 9 | return decoded_size; |
| 10 | } | 10 | } |
| 11 | 11 |
test/translate_c.zig+64-38| ... | @@ -3,6 +3,28 @@ const std = @import("std"); | ... | @@ -3,6 +3,28 @@ const std = @import("std"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | 3 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 4 | ||
| 5 | pub fn addCases(cases: *tests.TranslateCContext) void { | 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 | cases.add("if as while stmt has semicolon", | 28 | cases.add("if as while stmt has semicolon", |
| 7 | \\void foo() { | 29 | \\void foo() { |
| 8 | \\ while (1) if (1) { | 30 | \\ while (1) if (1) { |
| ... | @@ -218,9 +240,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -218,9 +240,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 218 | \\} Bar; | 240 | \\} Bar; |
| 219 | , &[_][]const u8{ | 241 | , &[_][]const u8{ |
| 220 | \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo | 242 | \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo |
| 221 | \\const struct_unnamed_1 = opaque {}; | 243 | \\pub const Foo = opaque {}; |
| 222 | \\pub const Foo = struct_unnamed_1; | 244 | \\pub const Bar = extern struct { |
| 223 | \\const struct_unnamed_2 = extern struct { | ||
| 224 | \\ bar: ?*Foo, | 245 | \\ bar: ?*Foo, |
| 225 | \\}; | 246 | \\}; |
| 226 | }); | 247 | }); |
| ... | @@ -519,17 +540,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -519,17 +540,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 519 | \\} outer; | 540 | \\} outer; |
| 520 | \\void foo(outer *x) { x->y = x->x; } | 541 | \\void foo(outer *x) { x->y = x->x; } |
| 521 | , &[_][]const u8{ | 542 | , &[_][]const u8{ |
| 522 | \\const struct_unnamed_3 = extern struct { | 543 | \\const struct_unnamed_2 = extern struct { |
| 523 | \\ y: c_int, | 544 | \\ y: c_int, |
| 524 | \\}; | 545 | \\}; |
| 525 | \\const union_unnamed_2 = extern union { | 546 | \\const union_unnamed_1 = extern union { |
| 526 | \\ x: u8, | 547 | \\ x: u8, |
| 527 | \\ unnamed_0: struct_unnamed_3, | 548 | \\ unnamed_0: struct_unnamed_2, |
| 528 | \\}; | 549 | \\}; |
| 529 | \\const struct_unnamed_1 = extern struct { | 550 | \\pub const outer = extern struct { |
| 530 | \\ unnamed_0: union_unnamed_2, | 551 | \\ unnamed_0: union_unnamed_1, |
| 531 | \\}; | 552 | \\}; |
| 532 | \\pub const outer = struct_unnamed_1; | ||
| 533 | \\pub export fn foo(arg_x: [*c]outer) void { | 553 | \\pub export fn foo(arg_x: [*c]outer) void { |
| 534 | \\ var x = arg_x; | 554 | \\ var x = arg_x; |
| 535 | \\ x.*.unnamed_0.unnamed_0.y = @bitCast(c_int, @as(c_uint, x.*.unnamed_0.x)); | 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,21 +585,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 565 | \\struct {int x,y;} s2 = {.y = 2, .x=1}; | 585 | \\struct {int x,y;} s2 = {.y = 2, .x=1}; |
| 566 | \\foo s3 = { 123 }; | 586 | \\foo s3 = { 123 }; |
| 567 | , &[_][]const u8{ | 587 | , &[_][]const u8{ |
| 568 | \\const struct_unnamed_1 = extern struct { | 588 | \\pub const foo = extern struct { |
| 569 | \\ x: c_int, | 589 | \\ x: c_int, |
| 570 | \\}; | 590 | \\}; |
| 571 | \\pub const foo = struct_unnamed_1; | 591 | \\const struct_unnamed_1 = extern struct { |
| 572 | \\const struct_unnamed_2 = extern struct { | ||
| 573 | \\ x: f64, | 592 | \\ x: f64, |
| 574 | \\ y: f64, | 593 | \\ y: f64, |
| 575 | \\ z: f64, | 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 | \\ .x = 1.2, | 597 | \\ .x = 1.2, |
| 579 | \\ .y = 1.3, | 598 | \\ .y = 1.3, |
| 580 | \\ .z = 0, | 599 | \\ .z = 0, |
| 581 | \\}; | 600 | \\}; |
| 582 | \\const struct_unnamed_3 = extern struct { | 601 | \\const struct_unnamed_2 = extern struct { |
| 583 | \\ sec: c_int, | 602 | \\ sec: c_int, |
| 584 | \\ min: c_int, | 603 | \\ min: c_int, |
| 585 | \\ hour: c_int, | 604 | \\ hour: c_int, |
| ... | @@ -587,7 +606,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -587,7 +606,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 587 | \\ mon: c_int, | 606 | \\ mon: c_int, |
| 588 | \\ year: c_int, | 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 | \\ .sec = @as(c_int, 30), | 610 | \\ .sec = @as(c_int, 30), |
| 592 | \\ .min = @as(c_int, 15), | 611 | \\ .min = @as(c_int, 15), |
| 593 | \\ .hour = @as(c_int, 17), | 612 | \\ .hour = @as(c_int, 17), |
| ... | @@ -595,11 +614,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -595,11 +614,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 595 | \\ .mon = @as(c_int, 12), | 614 | \\ .mon = @as(c_int, 12), |
| 596 | \\ .year = @as(c_int, 2014), | 615 | \\ .year = @as(c_int, 2014), |
| 597 | \\}; | 616 | \\}; |
| 598 | \\const struct_unnamed_4 = extern struct { | 617 | \\const struct_unnamed_3 = extern struct { |
| 599 | \\ x: c_int, | 618 | \\ x: c_int, |
| 600 | \\ y: c_int, | 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 | \\ .x = @as(c_int, 1), | 622 | \\ .x = @as(c_int, 1), |
| 604 | \\ .y = @as(c_int, 2), | 623 | \\ .y = @as(c_int, 2), |
| 605 | \\}; | 624 | \\}; |
| ... | @@ -1639,37 +1658,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1639,37 +1658,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1639 | \\ p, | 1658 | \\ p, |
| 1640 | \\}; | 1659 | \\}; |
| 1641 | , &[_][]const u8{ | 1660 | , &[_][]const u8{ |
| 1642 | \\const enum_unnamed_1 = extern enum(c_int) { | 1661 | \\pub const d = extern enum(c_int) { |
| 1643 | \\ a, | 1662 | \\ a, |
| 1644 | \\ b, | 1663 | \\ b, |
| 1645 | \\ c, | 1664 | \\ c, |
| 1646 | \\ _, | 1665 | \\ _, |
| 1647 | \\}; | 1666 | \\}; |
| 1648 | \\pub const a = @enumToInt(enum_unnamed_1.a); | 1667 | \\pub const a = @enumToInt(d.a); |
| 1649 | \\pub const b = @enumToInt(enum_unnamed_1.b); | 1668 | \\pub const b = @enumToInt(d.b); |
| 1650 | \\pub const c = @enumToInt(enum_unnamed_1.c); | 1669 | \\pub const c = @enumToInt(d.c); |
| 1651 | \\pub const d = enum_unnamed_1; | 1670 | \\const enum_unnamed_1 = extern enum(c_int) { |
| 1652 | \\const enum_unnamed_2 = extern enum(c_int) { | ||
| 1653 | \\ e = 0, | 1671 | \\ e = 0, |
| 1654 | \\ f = 4, | 1672 | \\ f = 4, |
| 1655 | \\ g = 5, | 1673 | \\ g = 5, |
| 1656 | \\ _, | 1674 | \\ _, |
| 1657 | \\}; | 1675 | \\}; |
| 1658 | \\pub const e = @enumToInt(enum_unnamed_2.e); | 1676 | \\pub const e = @enumToInt(enum_unnamed_1.e); |
| 1659 | \\pub const f = @enumToInt(enum_unnamed_2.f); | 1677 | \\pub const f = @enumToInt(enum_unnamed_1.f); |
| 1660 | \\pub const g = @enumToInt(enum_unnamed_2.g); | 1678 | \\pub const g = @enumToInt(enum_unnamed_1.g); |
| 1661 | \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e); | 1679 | \\pub export var h: enum_unnamed_1 = @intToEnum(enum_unnamed_1, e); |
| 1662 | \\const enum_unnamed_3 = extern enum(c_int) { | 1680 | \\const enum_unnamed_2 = extern enum(c_int) { |
| 1663 | \\ i, | 1681 | \\ i, |
| 1664 | \\ j, | 1682 | \\ j, |
| 1665 | \\ k, | 1683 | \\ k, |
| 1666 | \\ _, | 1684 | \\ _, |
| 1667 | \\}; | 1685 | \\}; |
| 1668 | \\pub const i = @enumToInt(enum_unnamed_3.i); | 1686 | \\pub const i = @enumToInt(enum_unnamed_2.i); |
| 1669 | \\pub const j = @enumToInt(enum_unnamed_3.j); | 1687 | \\pub const j = @enumToInt(enum_unnamed_2.j); |
| 1670 | \\pub const k = @enumToInt(enum_unnamed_3.k); | 1688 | \\pub const k = @enumToInt(enum_unnamed_2.k); |
| 1671 | \\pub const struct_Baz = extern struct { | 1689 | \\pub const struct_Baz = extern struct { |
| 1672 | \\ l: enum_unnamed_3, | 1690 | \\ l: enum_unnamed_2, |
| 1673 | \\ m: d, | 1691 | \\ m: d, |
| 1674 | \\}; | 1692 | \\}; |
| 1675 | \\pub const enum_i = extern enum(c_int) { | 1693 | \\pub const enum_i = extern enum(c_int) { |
| ... | @@ -1934,7 +1952,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1934,7 +1952,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1934 | , &[_][]const u8{ | 1952 | , &[_][]const u8{ |
| 1935 | \\pub export fn foo() c_int { | 1953 | \\pub export fn foo() c_int { |
| 1936 | \\ var a: c_int = 5; | 1954 | \\ var a: c_int = 5; |
| 1937 | \\ while (true) a = 2; | 1955 | \\ while (true) { |
| 1956 | \\ a = 2; | ||
| 1957 | \\ } | ||
| 1938 | \\ while (true) { | 1958 | \\ while (true) { |
| 1939 | \\ var a_1: c_int = 4; | 1959 | \\ var a_1: c_int = 4; |
| 1940 | \\ a_1 = 9; | 1960 | \\ a_1 = 9; |
| ... | @@ -1947,7 +1967,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1947,7 +1967,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1947 | \\ var a_1: c_int = 2; | 1967 | \\ var a_1: c_int = 2; |
| 1948 | \\ a_1 = 12; | 1968 | \\ a_1 = 12; |
| 1949 | \\ } | 1969 | \\ } |
| 1950 | \\ while (true) a = 7; | 1970 | \\ while (true) { |
| 1971 | \\ a = 7; | ||
| 1972 | \\ } | ||
| 1951 | \\ return 0; | 1973 | \\ return 0; |
| 1952 | \\} | 1974 | \\} |
| 1953 | }); | 1975 | }); |
| ... | @@ -2008,7 +2030,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2008,7 +2030,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2008 | \\} | 2030 | \\} |
| 2009 | , &[_][]const u8{ | 2031 | , &[_][]const u8{ |
| 2010 | \\pub export fn bar() c_int { | 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 | \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6); | 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,7 +2413,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2389 | \\pub const yes = [*c]u8; | 2413 | \\pub const yes = [*c]u8; |
| 2390 | \\pub export fn foo() void { | 2414 | \\pub export fn foo() void { |
| 2391 | \\ var a: yes = undefined; | 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,7 +2766,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2740 | \\ var a = arg_a; | 2766 | \\ var a = arg_a; |
| 2741 | \\ var i: c_int = 0; | 2767 | \\ var i: c_int = 0; |
| 2742 | \\ while (a > @bitCast(c_uint, @as(c_int, 0))) { | 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 | \\ return i; | 2771 | \\ return i; |
| 2746 | \\} | 2772 | \\} |
| ... | @@ -2760,7 +2786,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2760,7 +2786,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2760 | \\ var a = arg_a; | 2786 | \\ var a = arg_a; |
| 2761 | \\ var i: c_int = 0; | 2787 | \\ var i: c_int = 0; |
| 2762 | \\ while (a > @bitCast(c_uint, @as(c_int, 0))) { | 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 | \\ return i; | 2791 | \\ return i; |
| 2766 | \\} | 2792 | \\} |