authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-28 19:42:08-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-28 19:42:43-07:00
log281a7baaeac6b6b3c8c78124f5e484f7ee101cf0
treea8ca0725a42e8940197064a4e6485e2523b9926b
parent8f469c11275e60f5f1a8ae08fc7596ba366eda16
parent175adc0bd738c2e3a55bb71c6a53dcc920c203ba

Merge remote-tracking branch 'origin/master' into zir-memory-layout

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
8989set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary")
9090set(ZIG_SINGLE_THREADED off CACHE BOOL "limit the zig compiler to use only 1 thread")
9191set(ZIG_OMIT_STAGE2 off CACHE BOOL "omit the stage2 backend from stage1")
92set(ZIG_ENABLE_LOGGING off CACHE BOOL "enable logging")
9293
9394if("${ZIG_TARGET_TRIPLE}" STREQUAL "native")
9495 set(ZIG_USE_LLVM_CONFIG ON CACHE BOOL "use llvm-config to find LLVM libraries")
......@@ -607,6 +608,12 @@ else()
607608 set(ZIG_OMIT_STAGE2_BOOL "false")
608609endif()
609610
611if(ZIG_ENABLE_LOGGING)
612 set(ZIG_ENABLE_LOGGING_BOOL "true")
613else()
614 set(ZIG_ENABLE_LOGGING_BOOL "false")
615endif()
616
610617configure_file (
611618 "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in"
612619 "${ZIG_CONFIG_H_OUT}"
......@@ -735,12 +742,14 @@ if(MSVC OR MINGW)
735742 target_link_libraries(zigstage1 LINK_PUBLIC version)
736743endif()
737744
738add_executable(zig0 ${ZIG0_SOURCES})
739set_target_properties(zig0 PROPERTIES
740 COMPILE_FLAGS ${EXE_CFLAGS}
741 LINK_FLAGS ${EXE_LDFLAGS}
742)
743target_link_libraries(zig0 zigstage1)
745if("${ZIG_EXECUTABLE}" STREQUAL "")
746 add_executable(zig0 ${ZIG0_SOURCES})
747 set_target_properties(zig0 PROPERTIES
748 COMPILE_FLAGS ${EXE_CFLAGS}
749 LINK_FLAGS ${EXE_LDFLAGS}
750 )
751 target_link_libraries(zig0 zigstage1)
752endif()
744753
745754if(MSVC)
746755 set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.obj")
......@@ -789,7 +798,6 @@ if("${ZIG_EXECUTABLE}" STREQUAL "")
789798else()
790799 add_custom_command(
791800 OUTPUT "${ZIG1_OBJECT}"
792 BYPRODUCTS "${ZIG1_OBJECT}"
793801 COMMAND "${ZIG_EXECUTABLE}" "build-obj" ${BUILD_ZIG1_ARGS}
794802 DEPENDS ${ZIG_STAGE2_SOURCES}
795803 COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}"
ci/azure/macos_arm64_script created+132
......@@ -0,0 +1,132 @@
1#!/bin/sh
2
3set -x
4set -e
5
6brew install s3cmd ninja gnu-tar
7
8ZIGDIR="$(pwd)"
9ARCH="aarch64"
10# {product}-{os}{sdk_version}-{arch}-{llvm_version}-{cmake_build_type}
11CACHE_HOST_BASENAME="llvm-macos10.15-x86_64-11.0.1-release"
12CACHE_ARM64_BASENAME="llvm-macos11.0-arm64-11.0.1-release"
13PREFIX_HOST="$HOME/$CACHE_HOST_BASENAME"
14PREFIX_ARM64="$HOME/$CACHE_ARM64_BASENAME"
15JOBS="-j2"
16
17rm -rf $PREFIX
18cd $HOME
19wget -nv "https://ziglang.org/deps/$CACHE_HOST_BASENAME.tar.xz"
20wget -nv "https://ziglang.org/deps/$CACHE_ARM64_BASENAME.tar.xz"
21
22gtar xf "$CACHE_HOST_BASENAME.tar.xz"
23gtar xf "$CACHE_ARM64_BASENAME.tar.xz"
24
25cd $ZIGDIR
26
27# Make the `zig version` number consistent.
28# This will affect the cmake command below.
29git config core.abbrev 9
30git fetch --unshallow || true
31git fetch --tags
32
33# Select xcode: latest version found on vmImage macOS-10.15 .
34DEVELOPER_DIR=/Applications/Xcode_12.4.app
35
36export ZIG_LOCAL_CACHE_DIR="$ZIGDIR/zig-cache"
37export ZIG_GLOBAL_CACHE_DIR="$ZIGDIR/zig-cache"
38
39# Build zig for host and use `Debug` type to make builds a little faster.
40
41cd $ZIGDIR
42mkdir build.host
43cd build.host
44cmake -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.
51ninja $JOBS
52
53ZIG_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
60cd $ZIGDIR
61mkdir build.arm64
62cd build.arm64
63cmake -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
77ninja $JOBS install
78
79# Disable test because binary is foreign arch.
80#release/bin/zig build test
81
82if [ "${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"
132fi
ci/azure/pipelines.yml+14-1
......@@ -12,6 +12,19 @@ jobs:
1212 - script: ci/azure/macos_script
1313 name: main
1414 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'
1528- job: BuildLinux
1629 pool:
1730 vmImage: 'ubuntu-18.04'
......@@ -31,7 +44,7 @@ jobs:
3144 timeoutInMinutes: 360
3245 steps:
3346 - 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")
3548 .\sfx.exe -y -o\
3649 del sfx.exe
3750 displayName: Download/Extract/Install MSYS2
ci/azure/windows_msvc_install+1-1
......@@ -3,7 +3,7 @@
33set -x
44set -e
55
6pacman -Su --needed --noconfirm
6pacman -Suy --needed --noconfirm
77pacman -S --needed --noconfirm wget p7zip python3-pip tar xz
88
99pip install s3cmd
doc/langref.html.in+3-3
......@@ -9952,9 +9952,9 @@ export fn decode_base_64(
99529952) usize {
99539953 const src = source_ptr[0..source_len];
99549954 const dest = dest_ptr[0..dest_len];
9955 const base64_decoder = base64.standard_decoder_unsafe;
9956 const decoded_size = base64_decoder.calcSize(src);
9957 base64_decoder.decode(dest[0..decoded_size], src);
9955 const base64_decoder = base64.standard.Decoder;
9956 const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable;
9957 base64_decoder.decode(dest[0..decoded_size], src) catch unreachable;
99589958 return decoded_size;
99599959}
99609960 {#code_end#}
lib/std/array_hash_map.zig+13-13
......@@ -687,8 +687,9 @@ pub fn ArrayHashMapUnmanaged(
687687
688688 /// Removes the last inserted `Entry` in the hash map and returns it.
689689 pub fn pop(self: *Self) Entry {
690 const top = self.entries.pop();
690 const top = self.entries.items[self.entries.items.len - 1];
691691 _ = self.removeWithHash(top.key, top.hash, .index_only);
692 self.entries.items.len -= 1;
692693 return top;
693694 }
694695
......@@ -1258,19 +1259,18 @@ test "pop" {
12581259 var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
12591260 defer map.deinit();
12601261
1261 testing.expect((try map.fetchPut(1, 11)) == null);
1262 testing.expect((try map.fetchPut(2, 22)) == null);
1263 testing.expect((try map.fetchPut(3, 33)) == null);
1264 testing.expect((try map.fetchPut(4, 44)) == null);
1262 // Insert just enough entries so that the map expands. Afterwards,
1263 // pop all entries out of the map.
12651264
1266 const pop1 = map.pop();
1267 testing.expect(pop1.key == 4 and pop1.value == 44);
1268 const pop2 = map.pop();
1269 testing.expect(pop2.key == 3 and pop2.value == 33);
1270 const pop3 = map.pop();
1271 testing.expect(pop3.key == 2 and pop3.value == 22);
1272 const pop4 = map.pop();
1273 testing.expect(pop4.key == 1 and pop4.value == 11);
1265 var i: i32 = 0;
1266 while (i < 9) : (i += 1) {
1267 testing.expect((try map.fetchPut(i, i)) == null);
1268 }
1269
1270 while (i > 0) : (i -= 1) {
1271 const pop = map.pop();
1272 testing.expect(pop.key == i - 1 and pop.value == i - 1);
1273 }
12741274}
12751275
12761276test "reIndex" {
lib/std/base64.zig+322-324
......@@ -8,454 +8,452 @@ const assert = std.debug.assert;
88const testing = std.testing;
99const mem = std.mem;
1010
11pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
12pub const standard_pad_char = '=';
13pub const standard_encoder = Base64Encoder.init(standard_alphabet_chars, standard_pad_char);
11pub const Error = error{
12 InvalidCharacter,
13 InvalidPadding,
14 NoSpaceLeft,
15};
16
17/// Base64 codecs
18pub 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
26pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".*;
27fn standardBase64DecoderWithIgnore(ignore: []const u8) Base64DecoderWithIgnore {
28 return Base64DecoderWithIgnore.init(standard_alphabet_chars, '=', ignore);
29}
30
31/// Standard Base64 codecs, with padding
32pub 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
41pub 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
49pub const url_safe_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;
50fn urlSafeBase64DecoderWithIgnore(ignore: []const u8) Base64DecoderWithIgnore {
51 return Base64DecoderWithIgnore.init(url_safe_alphabet_chars, null, ignore);
52}
53
54/// URL-safe Base64 codecs, with padding
55pub 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
64pub 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`
75pub const standard_pad_char = standard.pad_char;
76/// Deprecated - Use `standard.Encoder`
77pub const standard_encoder = standard.Encoder;
78/// Deprecated - Use `standard.Decoder`
79pub const standard_decoder = standard.Decoder;
1480
1581pub const Base64Encoder = struct {
16 alphabet_chars: []const u8,
17 pad_char: u8,
82 alphabet_chars: [64]u8,
83 pad_char: ?u8,
1884
19 /// a bunch of assertions, then simply pass the data right through.
20 pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64Encoder {
85 /// A bunch of assertions, then simply pass the data right through.
86 pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Encoder {
2187 assert(alphabet_chars.len == 64);
2288 var char_in_alphabet = [_]bool{false} ** 256;
2389 for (alphabet_chars) |c| {
2490 assert(!char_in_alphabet[c]);
25 assert(c != pad_char);
91 assert(pad_char == null or c != pad_char.?);
2692 char_in_alphabet[c] = true;
2793 }
28
2994 return Base64Encoder{
3095 .alphabet_chars = alphabet_chars,
3196 .pad_char = pad_char,
3297 };
3398 }
3499
35 /// ceil(source_len * 4/3)
36 pub fn calcSize(source_len: usize) usize {
37 return @divTrunc(source_len + 2, 3) * 4;
100 /// Compute the encoded length
101 pub fn calcSize(encoder: *const Base64Encoder, source_len: usize) usize {
102 if (encoder.pad_char != null) {
103 return @divTrunc(source_len + 2, 3) * 4;
104 } else {
105 const leftover = source_len % 3;
106 return @divTrunc(source_len, 3) * 4 + @divTrunc(leftover * 4 + 2, 3);
107 }
38108 }
39109
40 /// dest.len must be what you get from ::calcSize.
110 /// dest.len must at least be what you get from ::calcSize.
41111 pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 {
42 assert(dest.len >= Base64Encoder.calcSize(source.len));
43
44 var i: usize = 0;
45 var out_index: usize = 0;
46 while (i + 2 < source.len) : (i += 3) {
47 dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];
48 out_index += 1;
49
50 dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)];
51 out_index += 1;
52
53 dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) | ((source[i + 2] & 0xc0) >> 6)];
54 out_index += 1;
55
56 dest[out_index] = encoder.alphabet_chars[source[i + 2] & 0x3f];
57 out_index += 1;
112 const out_len = encoder.calcSize(source.len);
113 assert(dest.len >= out_len);
114
115 const nibbles = source.len / 3;
116 const leftover = source.len - 3 * nibbles;
117
118 var acc: u12 = 0;
119 var acc_len: u4 = 0;
120 var out_idx: usize = 0;
121 for (source) |v| {
122 acc = (acc << 8) + v;
123 acc_len += 8;
124 while (acc_len >= 6) {
125 acc_len -= 6;
126 dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc >> acc_len))];
127 out_idx += 1;
128 }
58129 }
59
60 if (i < source.len) {
61 dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];
62 out_index += 1;
63
64 if (i + 1 == source.len) {
65 dest[out_index] = encoder.alphabet_chars[(source[i] & 0x3) << 4];
66 out_index += 1;
67
68 dest[out_index] = encoder.pad_char;
69 out_index += 1;
70 } else {
71 dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)];
72 out_index += 1;
73
74 dest[out_index] = encoder.alphabet_chars[(source[i + 1] & 0xf) << 2];
75 out_index += 1;
130 if (acc_len > 0) {
131 dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc << 6 - acc_len))];
132 out_idx += 1;
133 }
134 if (encoder.pad_char) |pad_char| {
135 for (dest[out_idx..]) |*pad| {
136 pad.* = pad_char;
76137 }
77
78 dest[out_index] = encoder.pad_char;
79 out_index += 1;
80138 }
81 return dest[0..out_index];
139 return dest[0..out_len];
82140 }
83141};
84142
85pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char);
86
87143pub const Base64Decoder = struct {
144 const invalid_char: u8 = 0xff;
145
88146 /// 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.
90148 char_to_index: [256]u8,
149 pad_char: ?u8,
91150
92 /// true only for the 64 chars in the alphabet, not the pad char.
93 char_in_alphabet: [256]bool,
94 pad_char: u8,
95
96 pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64Decoder {
97 assert(alphabet_chars.len == 64);
98
151 pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Decoder {
99152 var result = Base64Decoder{
100 .char_to_index = undefined,
101 .char_in_alphabet = [_]bool{false} ** 256,
153 .char_to_index = [_]u8{invalid_char} ** 256,
102154 .pad_char = pad_char,
103155 };
104156
157 var char_in_alphabet = [_]bool{false} ** 256;
105158 for (alphabet_chars) |c, i| {
106 assert(!result.char_in_alphabet[c]);
107 assert(c != pad_char);
159 assert(!char_in_alphabet[c]);
160 assert(pad_char == null or c != pad_char.?);
108161
109162 result.char_to_index[c] = @intCast(u8, i);
110 result.char_in_alphabet[c] = true;
163 char_in_alphabet[c] = true;
111164 }
165 return result;
166 }
112167
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 }
113179 return result;
114180 }
115181
116 /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.
117 pub fn calcSize(decoder: *const Base64Decoder, source: []const u8) !usize {
118 if (source.len % 4 != 0) return error.InvalidPadding;
119 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
182 /// Return the exact decoded size for a slice.
183 /// `InvalidPadding` is returned if the input length is not valid.
184 pub fn calcSizeForSlice(decoder: *const Base64Decoder, source: []const u8) Error!usize {
185 const source_len = source.len;
186 var result = try decoder.calcSizeUpperBound(source_len);
187 if (decoder.pad_char) |pad_char| {
188 if (source_len >= 1 and source[source_len - 1] == pad_char) result -= 1;
189 if (source_len >= 2 and source[source_len - 2] == pad_char) result -= 1;
190 }
191 return result;
120192 }
121193
122194 /// dest.len must be what you get from ::calcSize.
123195 /// invalid characters result in error.InvalidCharacter.
124196 /// invalid padding results in error.InvalidPadding.
125 pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) !void {
126 assert(dest.len == (decoder.calcSize(source) catch unreachable));
127 assert(source.len % 4 == 0);
128
129 var src_cursor: usize = 0;
130 var dest_cursor: usize = 0;
131
132 while (src_cursor < source.len) : (src_cursor += 4) {
133 if (!decoder.char_in_alphabet[source[src_cursor + 0]]) return error.InvalidCharacter;
134 if (!decoder.char_in_alphabet[source[src_cursor + 1]]) return error.InvalidCharacter;
135 if (src_cursor < source.len - 4 or source[src_cursor + 3] != decoder.pad_char) {
136 // common case
137 if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
138 if (!decoder.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter;
139 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4;
140 dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2;
141 dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 | decoder.char_to_index[source[src_cursor + 3]];
142 dest_cursor += 3;
143 } else if (source[src_cursor + 2] != decoder.pad_char) {
144 // one pad char
145 if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
146 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4;
147 dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2;
148 if (decoder.char_to_index[source[src_cursor + 2]] << 6 != 0) return error.InvalidPadding;
149 dest_cursor += 2;
150 } else {
151 // two pad chars
152 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4;
153 if (decoder.char_to_index[source[src_cursor + 1]] << 4 != 0) return error.InvalidPadding;
154 dest_cursor += 1;
197 pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) Error!void {
198 if (decoder.pad_char != null and source.len % 4 != 0) return error.InvalidPadding;
199 var acc: u12 = 0;
200 var acc_len: u4 = 0;
201 var dest_idx: usize = 0;
202 var leftover_idx: ?usize = null;
203 for (source) |c, src_idx| {
204 const d = decoder.char_to_index[c];
205 if (d == invalid_char) {
206 if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter;
207 leftover_idx = src_idx;
208 break;
209 }
210 acc = (acc << 6) + d;
211 acc_len += 6;
212 if (acc_len >= 8) {
213 acc_len -= 8;
214 dest[dest_idx] = @truncate(u8, acc >> acc_len);
215 dest_idx += 1;
155216 }
156217 }
157
158 assert(src_cursor == source.len);
159 assert(dest_cursor == dest.len);
218 if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) {
219 return error.InvalidPadding;
220 }
221 if (leftover_idx == null) return;
222 var leftover = source[leftover_idx.?..];
223 if (decoder.pad_char) |pad_char| {
224 const padding_len = acc_len / 2;
225 var padding_chars: usize = 0;
226 var i: usize = 0;
227 for (leftover) |c| {
228 if (c != pad_char) {
229 return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding;
230 }
231 padding_chars += 1;
232 }
233 if (padding_chars != padding_len) return error.InvalidPadding;
234 }
160235 }
161236};
162237
163238pub const Base64DecoderWithIgnore = struct {
164239 decoder: Base64Decoder,
165240 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 {
167243 var result = Base64DecoderWithIgnore{
168244 .decoder = Base64Decoder.init(alphabet_chars, pad_char),
169245 .char_is_ignored = [_]bool{false} ** 256,
170246 };
171
172247 for (ignore_chars) |c| {
173 assert(!result.decoder.char_in_alphabet[c]);
248 assert(result.decoder.char_to_index[c] == Base64Decoder.invalid_char);
174249 assert(!result.char_is_ignored[c]);
175250 assert(result.decoder.pad_char != c);
176251 result.char_is_ignored[c] = true;
177252 }
178
179253 return result;
180254 }
181255
182 /// If no characters end up being ignored or padding, this will be the exact decoded size.
183 pub fn calcSizeUpperBound(encoded_len: usize) usize {
184 return @divTrunc(encoded_len, 4) * 3;
256 /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding
257 /// `InvalidPadding` is returned if the input length is not valid.
258 pub fn calcSizeUpperBound(decoder_with_ignore: *const Base64DecoderWithIgnore, source_len: usize) Error!usize {
259 var result = source_len / 4 * 3;
260 if (decoder_with_ignore.decoder.pad_char == null) {
261 const leftover = source_len % 4;
262 result += leftover * 3 / 4;
263 }
264 return result;
185265 }
186266
187267 /// Invalid characters that are not ignored result in error.InvalidCharacter.
188268 /// 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.
190270 /// 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 {
192272 const decoder = &decoder_with_ignore.decoder;
193
194 var src_cursor: usize = 0;
195 var dest_cursor: usize = 0;
196
197 while (true) {
198 // get the next 4 chars, if available
199 var next_4_chars: [4]u8 = undefined;
200 var available_chars: usize = 0;
201 var pad_char_count: usize = 0;
202 while (available_chars < 4 and src_cursor < source.len) {
203 var c = source[src_cursor];
204 src_cursor += 1;
205
206 if (decoder.char_in_alphabet[c]) {
207 // normal char
208 next_4_chars[available_chars] = c;
209 available_chars += 1;
210 } else if (decoder_with_ignore.char_is_ignored[c]) {
211 // we're told to skip this one
212 continue;
213 } else if (c == decoder.pad_char) {
214 // the padding has begun. count the pad chars.
215 pad_char_count += 1;
216 while (src_cursor < source.len) {
217 c = source[src_cursor];
218 src_cursor += 1;
219 if (c == decoder.pad_char) {
220 pad_char_count += 1;
221 if (pad_char_count > 2) return error.InvalidCharacter;
222 } else if (decoder_with_ignore.char_is_ignored[c]) {
223 // we can even ignore chars during the padding
224 continue;
225 } else return error.InvalidCharacter;
226 }
227 break;
228 } else return error.InvalidCharacter;
273 var acc: u12 = 0;
274 var acc_len: u4 = 0;
275 var dest_idx: usize = 0;
276 var leftover_idx: ?usize = null;
277 for (source) |c, src_idx| {
278 if (decoder_with_ignore.char_is_ignored[c]) continue;
279 const d = decoder.char_to_index[c];
280 if (d == Base64Decoder.invalid_char) {
281 if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter;
282 leftover_idx = src_idx;
283 break;
229284 }
230
231 switch (available_chars) {
232 4 => {
233 // common case
234 if (dest_cursor + 3 > dest.len) return error.OutputTooSmall;
235 assert(pad_char_count == 0);
236 dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4;
237 dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | decoder.char_to_index[next_4_chars[2]] >> 2;
238 dest[dest_cursor + 2] = decoder.char_to_index[next_4_chars[2]] << 6 | decoder.char_to_index[next_4_chars[3]];
239 dest_cursor += 3;
240 continue;
241 },
242 3 => {
243 if (dest_cursor + 2 > dest.len) return error.OutputTooSmall;
244 if (pad_char_count != 1) return error.InvalidPadding;
245 dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4;
246 dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | decoder.char_to_index[next_4_chars[2]] >> 2;
247 if (decoder.char_to_index[next_4_chars[2]] << 6 != 0) return error.InvalidPadding;
248 dest_cursor += 2;
249 break;
250 },
251 2 => {
252 if (dest_cursor + 1 > dest.len) return error.OutputTooSmall;
253 if (pad_char_count != 2) return error.InvalidPadding;
254 dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4;
255 if (decoder.char_to_index[next_4_chars[1]] << 4 != 0) return error.InvalidPadding;
256 dest_cursor += 1;
257 break;
258 },
259 1 => {
260 return error.InvalidPadding;
261 },
262 0 => {
263 if (pad_char_count != 0) return error.InvalidPadding;
264 break;
265 },
266 else => unreachable,
285 acc = (acc << 6) + d;
286 acc_len += 6;
287 if (acc_len >= 8) {
288 if (dest_idx == dest.len) return error.NoSpaceLeft;
289 acc_len -= 8;
290 dest[dest_idx] = @truncate(u8, acc >> acc_len);
291 dest_idx += 1;
267292 }
268293 }
269
270 assert(src_cursor == source.len);
271
272 return dest_cursor;
273 }
274};
275
276pub const standard_decoder_unsafe = Base64DecoderUnsafe.init(standard_alphabet_chars, standard_pad_char);
277
278pub const Base64DecoderUnsafe = struct {
279 /// e.g. 'A' => 0.
280 /// undefined for any value not in the 64 alphabet chars.
281 char_to_index: [256]u8,
282 pad_char: u8,
283
284 pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64DecoderUnsafe {
285 assert(alphabet_chars.len == 64);
286 var result = Base64DecoderUnsafe{
287 .char_to_index = undefined,
288 .pad_char = pad_char,
289 };
290 for (alphabet_chars) |c, i| {
291 assert(c != pad_char);
292 result.char_to_index[c] = @intCast(u8, i);
294 if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) {
295 return error.InvalidPadding;
293296 }
294 return result;
295 }
296
297 /// The source buffer must be valid.
298 pub fn calcSize(decoder: *const Base64DecoderUnsafe, source: []const u8) usize {
299 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
300 }
301
302 /// dest.len must be what you get from ::calcDecodedSizeExactUnsafe.
303 /// invalid characters or padding will result in undefined values.
304 pub fn decode(decoder: *const Base64DecoderUnsafe, dest: []u8, source: []const u8) void {
305 assert(dest.len == decoder.calcSize(source));
306
307 var src_index: usize = 0;
308 var dest_index: usize = 0;
309 var in_buf_len: usize = source.len;
310
311 while (in_buf_len > 0 and source[in_buf_len - 1] == decoder.pad_char) {
312 in_buf_len -= 1;
297 const padding_len = acc_len / 2;
298 if (leftover_idx == null) {
299 if (decoder.pad_char != null and padding_len != 0) return error.InvalidPadding;
300 return dest_idx;
313301 }
314
315 while (in_buf_len > 4) {
316 dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4;
317 dest_index += 1;
318
319 dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2;
320 dest_index += 1;
321
322 dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]];
323 dest_index += 1;
324
325 src_index += 4;
326 in_buf_len -= 4;
327 }
328
329 if (in_buf_len > 1) {
330 dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4;
331 dest_index += 1;
332 }
333 if (in_buf_len > 2) {
334 dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2;
335 dest_index += 1;
336 }
337 if (in_buf_len > 3) {
338 dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]];
339 dest_index += 1;
302 var leftover = source[leftover_idx.?..];
303 if (decoder.pad_char) |pad_char| {
304 var padding_chars: usize = 0;
305 var i: usize = 0;
306 for (leftover) |c| {
307 if (decoder_with_ignore.char_is_ignored[c]) continue;
308 if (c != pad_char) {
309 return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding;
310 }
311 padding_chars += 1;
312 }
313 if (padding_chars != padding_len) return error.InvalidPadding;
340314 }
315 return dest_idx;
341316 }
342317};
343318
344fn 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
356319test "base64" {
357320 @setEvalBranchQuota(8000);
358321 testBase64() catch unreachable;
359 comptime (testBase64() catch unreachable);
322 comptime testAllApis(standard, "comptime", "Y29tcHRpbWU=") catch unreachable;
323}
324
325test "base64 url_safe_no_pad" {
326 @setEvalBranchQuota(8000);
327 testBase64UrlSafeNoPad() catch unreachable;
328 comptime testAllApis(url_safe_no_pad, "comptime", "Y29tcHRpbWU") catch unreachable;
360329}
361330
362331fn testBase64() !void {
363 try testAllApis("", "");
364 try testAllApis("f", "Zg==");
365 try testAllApis("fo", "Zm8=");
366 try testAllApis("foo", "Zm9v");
367 try testAllApis("foob", "Zm9vYg==");
368 try testAllApis("fooba", "Zm9vYmE=");
369 try testAllApis("foobar", "Zm9vYmFy");
370
371 try testDecodeIgnoreSpace("", " ");
372 try testDecodeIgnoreSpace("f", "Z g= =");
373 try testDecodeIgnoreSpace("fo", " Zm8=");
374 try testDecodeIgnoreSpace("foo", "Zm9v ");
375 try testDecodeIgnoreSpace("foob", "Zm9vYg = = ");
376 try testDecodeIgnoreSpace("fooba", "Zm9v YmE=");
377 try testDecodeIgnoreSpace("foobar", " Z m 9 v Y m F y ");
332 const codecs = standard;
333
334 try testAllApis(codecs, "", "");
335 try testAllApis(codecs, "f", "Zg==");
336 try testAllApis(codecs, "fo", "Zm8=");
337 try testAllApis(codecs, "foo", "Zm9v");
338 try testAllApis(codecs, "foob", "Zm9vYg==");
339 try testAllApis(codecs, "fooba", "Zm9vYmE=");
340 try testAllApis(codecs, "foobar", "Zm9vYmFy");
341
342 try testDecodeIgnoreSpace(codecs, "", " ");
343 try testDecodeIgnoreSpace(codecs, "f", "Z g= =");
344 try testDecodeIgnoreSpace(codecs, "fo", " Zm8=");
345 try testDecodeIgnoreSpace(codecs, "foo", "Zm9v ");
346 try testDecodeIgnoreSpace(codecs, "foob", "Zm9vYg = = ");
347 try testDecodeIgnoreSpace(codecs, "fooba", "Zm9v YmE=");
348 try testDecodeIgnoreSpace(codecs, "foobar", " Z m 9 v Y m F y ");
349
350 // test getting some api errors
351 try testError(codecs, "A", error.InvalidPadding);
352 try testError(codecs, "AA", error.InvalidPadding);
353 try testError(codecs, "AAA", error.InvalidPadding);
354 try testError(codecs, "A..A", error.InvalidCharacter);
355 try testError(codecs, "AA=A", error.InvalidPadding);
356 try testError(codecs, "AA/=", error.InvalidPadding);
357 try testError(codecs, "A/==", error.InvalidPadding);
358 try testError(codecs, "A===", error.InvalidPadding);
359 try testError(codecs, "====", error.InvalidPadding);
360
361 try testNoSpaceLeftError(codecs, "AA==");
362 try testNoSpaceLeftError(codecs, "AAA=");
363 try testNoSpaceLeftError(codecs, "AAAA");
364 try testNoSpaceLeftError(codecs, "AAAAAA==");
365}
366
367fn 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 ");
378385
379386 // test getting some api errors
380 try testError("A", error.InvalidPadding);
381 try testError("AA", error.InvalidPadding);
382 try testError("AAA", error.InvalidPadding);
383 try testError("A..A", error.InvalidCharacter);
384 try testError("AA=A", error.InvalidCharacter);
385 try testError("AA/=", error.InvalidPadding);
386 try testError("A/==", error.InvalidPadding);
387 try testError("A===", error.InvalidCharacter);
388 try testError("====", error.InvalidCharacter);
389
390 try testOutputTooSmallError("AA==");
391 try testOutputTooSmallError("AAA=");
392 try testOutputTooSmallError("AAAA");
393 try testOutputTooSmallError("AAAAAA==");
387 try testError(codecs, "A", error.InvalidPadding);
388 try testError(codecs, "AAA=", error.InvalidCharacter);
389 try testError(codecs, "A..A", error.InvalidCharacter);
390 try testError(codecs, "AA=A", error.InvalidCharacter);
391 try testError(codecs, "AA/=", error.InvalidCharacter);
392 try testError(codecs, "A/==", error.InvalidCharacter);
393 try testError(codecs, "A===", error.InvalidCharacter);
394 try testError(codecs, "====", error.InvalidCharacter);
395
396 try testNoSpaceLeftError(codecs, "AA");
397 try testNoSpaceLeftError(codecs, "AAA");
398 try testNoSpaceLeftError(codecs, "AAAA");
399 try testNoSpaceLeftError(codecs, "AAAAAA");
394400}
395401
396fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void {
402fn testAllApis(codecs: Codecs, expected_decoded: []const u8, expected_encoded: []const u8) !void {
397403 // Base64Encoder
398404 {
399405 var buffer: [0x100]u8 = undefined;
400 const encoded = standard_encoder.encode(&buffer, expected_decoded);
406 const encoded = codecs.Encoder.encode(&buffer, expected_decoded);
401407 testing.expectEqualSlices(u8, expected_encoded, encoded);
402408 }
403409
404410 // Base64Decoder
405411 {
406412 var buffer: [0x100]u8 = undefined;
407 var decoded = buffer[0..try standard_decoder.calcSize(expected_encoded)];
408 try standard_decoder.decode(decoded, expected_encoded);
413 var decoded = buffer[0..try codecs.Decoder.calcSizeForSlice(expected_encoded)];
414 try codecs.Decoder.decode(decoded, expected_encoded);
409415 testing.expectEqualSlices(u8, expected_decoded, decoded);
410416 }
411417
412418 // Base64DecoderWithIgnore
413419 {
414 const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, "");
420 const decoder_ignore_nothing = codecs.decoderWithIgnore("");
415421 var buffer: [0x100]u8 = undefined;
416 var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];
417 var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded);
422 var decoded = buffer[0..try decoder_ignore_nothing.calcSizeUpperBound(expected_encoded.len)];
423 var written = try decoder_ignore_nothing.decode(decoded, expected_encoded);
418424 testing.expect(written <= decoded.len);
419425 testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);
420426 }
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 }
429427}
430428
431fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !void {
432 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");
429fn testDecodeIgnoreSpace(codecs: Codecs, expected_decoded: []const u8, encoded: []const u8) !void {
430 const decoder_ignore_space = codecs.decoderWithIgnore(" ");
433431 var buffer: [0x100]u8 = undefined;
434 var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];
435 var written = try standard_decoder_ignore_space.decode(decoded, encoded);
432 var decoded = buffer[0..try decoder_ignore_space.calcSizeUpperBound(encoded.len)];
433 var written = try decoder_ignore_space.decode(decoded, encoded);
436434 testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);
437435}
438436
439fn testError(encoded: []const u8, expected_err: anyerror) !void {
440 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");
437fn testError(codecs: Codecs, encoded: []const u8, expected_err: anyerror) !void {
438 const decoder_ignore_space = codecs.decoderWithIgnore(" ");
441439 var buffer: [0x100]u8 = undefined;
442 if (standard_decoder.calcSize(encoded)) |decoded_size| {
440 if (codecs.Decoder.calcSizeForSlice(encoded)) |decoded_size| {
443441 var decoded = buffer[0..decoded_size];
444 if (standard_decoder.decode(decoded, encoded)) |_| {
442 if (codecs.Decoder.decode(decoded, encoded)) |_| {
445443 return error.ExpectedError;
446444 } else |err| if (err != expected_err) return err;
447445 } else |err| if (err != expected_err) return err;
448446
449 if (standard_decoder_ignore_space.decode(buffer[0..], encoded)) |_| {
447 if (decoder_ignore_space.decode(buffer[0..], encoded)) |_| {
450448 return error.ExpectedError;
451449 } else |err| if (err != expected_err) return err;
452450}
453451
454fn testOutputTooSmallError(encoded: []const u8) !void {
455 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");
452fn testNoSpaceLeftError(codecs: Codecs, encoded: []const u8) !void {
453 const decoder_ignore_space = codecs.decoderWithIgnore(" ");
456454 var buffer: [0x100]u8 = undefined;
457 var decoded = buffer[0 .. calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1];
458 if (standard_decoder_ignore_space.decode(decoded, encoded)) |_| {
455 var decoded = buffer[0 .. (try codecs.Decoder.calcSizeForSlice(encoded)) - 1];
456 if (decoder_ignore_space.decode(decoded, encoded)) |_| {
459457 return error.ExpectedError;
460 } else |err| if (err != error.OutputTooSmall) return err;
458 } else |err| if (err != error.NoSpaceLeft) return err;
461459}
lib/std/build.zig+7-16
......@@ -51,7 +51,7 @@ pub const Builder = struct {
5151 default_step: *Step,
5252 env_map: *BufMap,
5353 top_level_steps: ArrayList(*TopLevelStep),
54 install_prefix: ?[]const u8,
54 install_prefix: []const u8,
5555 dest_dir: ?[]const u8,
5656 lib_dir: []const u8,
5757 exe_dir: []const u8,
......@@ -156,7 +156,7 @@ pub const Builder = struct {
156156 .default_step = undefined,
157157 .env_map = env_map,
158158 .search_prefixes = ArrayList([]const u8).init(allocator),
159 .install_prefix = null,
159 .install_prefix = undefined,
160160 .lib_dir = undefined,
161161 .exe_dir = undefined,
162162 .h_dir = undefined,
......@@ -190,22 +190,13 @@ pub const Builder = struct {
190190 }
191191
192192 /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file.
193 pub fn setInstallPrefix(self: *Builder, optional_prefix: ?[]const u8) void {
194 self.install_prefix = optional_prefix;
195 }
196
197 /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file.
198 pub fn resolveInstallPrefix(self: *Builder) void {
193 pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8) void {
199194 if (self.dest_dir) |dest_dir| {
200 const install_prefix = self.install_prefix orelse "/usr";
201 self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, install_prefix }) catch unreachable;
195 self.install_prefix = install_prefix orelse "/usr";
196 self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, self.install_prefix }) catch unreachable;
202197 } else {
203 const install_prefix = self.install_prefix orelse blk: {
204 const p = self.cache_root;
205 self.install_prefix = p;
206 break :blk p;
207 };
208 self.install_path = install_prefix;
198 self.install_prefix = install_prefix orelse self.cache_root;
199 self.install_path = self.install_prefix;
209200 }
210201 self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable;
211202 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
12501250 const kunits = ns_remaining * 1000 / unit.ns;
12511251 if (kunits >= 1000) {
12521252 try formatInt(kunits / 1000, 10, false, .{}, writer);
1253 if (kunits > 1000) {
1253 const frac = kunits % 1000;
1254 if (frac > 0) {
12541255 // Write up to 3 decimal places
1255 const frac = kunits % 1000;
12561256 var buf = [_]u8{ '.', 0, 0, 0 };
12571257 _ = formatIntBuf(buf[1..], frac, 10, false, .{ .fill = '0', .width = 3 });
12581258 var end: usize = 4;
......@@ -1286,9 +1286,14 @@ test "fmtDuration" {
12861286 .{ .s = "1us", .d = std.time.ns_per_us },
12871287 .{ .s = "1.45us", .d = 1450 },
12881288 .{ .s = "1.5us", .d = 3 * std.time.ns_per_us / 2 },
1289 .{ .s = "14.5us", .d = 14500 },
1290 .{ .s = "145us", .d = 145000 },
12891291 .{ .s = "999.999us", .d = std.time.ns_per_ms - 1 },
12901292 .{ .s = "1ms", .d = std.time.ns_per_ms + 1 },
12911293 .{ .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 },
12921297 .{ .s = "999.999ms", .d = std.time.ns_per_s - 1 },
12931298 .{ .s = "1s", .d = std.time.ns_per_s },
12941299 .{ .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) {
5050 else => @compileError("Unsupported OS"),
5151};
5252
53pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
53pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;
5454
5555/// Base64 encoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.
56pub const base64_encoder = base64.Base64Encoder.init(base64_alphabet, base64.standard_pad_char);
56pub const base64_encoder = base64.Base64Encoder.init(base64_alphabet, null);
5757
5858/// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.
59pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, base64.standard_pad_char);
59pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, null);
6060
6161/// Whether or not async file system syscalls need a dedicated thread because the operating
6262/// 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:
7777 const dirname = path.dirname(new_path) orelse ".";
7878
7979 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));
8181 defer allocator.free(tmp_path);
8282 mem.copy(u8, tmp_path[0..], dirname);
8383 tmp_path[dirname.len] = path.sep;
......@@ -142,7 +142,7 @@ pub const AtomicFile = struct {
142142 const InitError = File.OpenError;
143143
144144 const RANDOM_BYTES = 12;
145 const TMP_PATH_LEN = base64.Base64Encoder.calcSize(RANDOM_BYTES);
145 const TMP_PATH_LEN = base64_encoder.calcSize(RANDOM_BYTES);
146146
147147 /// Note that the `Dir.atomicFile` API may be more handy than this lower-level function.
148148 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 {
9595 .EnumLiteral,
9696 .Frame,
9797 .Float,
98 => @compileError("cannot hash this type"),
98 => @compileError("unable to hash type " ++ @typeName(Key)),
9999
100100 // Help the optimizer see that hashing an int is easy by inlining!
101101 // TODO Check if the situation is better after #561 is resolved.
lib/std/mem.zig+19
......@@ -1373,6 +1373,20 @@ test "mem.tokenize (multibyte)" {
13731373 testing.expect(it.next() == null);
13741374}
13751375
1376test "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
13761390/// Returns an iterator that iterates over the slices of `buffer` that
13771391/// are separated by bytes in `delimiter`.
13781392/// split("abc|def||ghi", "|")
......@@ -1471,6 +1485,11 @@ pub const TokenIterator = struct {
14711485 return self.buffer[index..];
14721486 }
14731487
1488 /// Resets the iterator to the initial token.
1489 pub fn reset(self: *TokenIterator) void {
1490 self.index = 0;
1491 }
1492
14741493 fn isSplitByte(self: TokenIterator, byte: u8) bool {
14751494 for (self.delimiter_bytes) |delimiter_byte| {
14761495 if (byte == delimiter_byte) {
lib/std/os.zig+1
......@@ -5610,6 +5610,7 @@ pub fn recvfrom(
56105610 EAGAIN => return error.WouldBlock,
56115611 ENOMEM => return error.SystemResources,
56125612 ECONNREFUSED => return error.ConnectionRefused,
5613 ECONNRESET => return error.ConnectionResetByPeer,
56135614 else => |err| return unexpectedErrno(err),
56145615 }
56155616 }
lib/std/os/linux/mips.zig+37
......@@ -115,6 +115,9 @@ pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize,
115115 );
116116}
117117
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
118121pub fn syscall6(
119122 number: SYS,
120123 arg1: usize,
......@@ -146,6 +149,40 @@ pub fn syscall6(
146149 );
147150}
148151
152pub 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
149186/// This matches the libc clone function.
150187pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
151188
lib/std/os/uefi/tables/boot_services.zig+2-1
......@@ -78,7 +78,8 @@ pub const BootServices = extern struct {
7878 /// Returns an array of handles that support a specified protocol.
7979 locateHandle: fn (LocateSearchType, ?*align(8) const Guid, ?*const c_void, *usize, [*]Handle) callconv(.C) Status,
8080
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,
8283 installConfigurationTable: Status, // TODO
8384
8485 /// 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:
373373}
374374
375375pub 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;
376pub var pfnCreateWindowExW: @TypeOf(RegisterClassExW) = undefined;
376pub var pfnCreateWindowExW: @TypeOf(CreateWindowExW) = undefined;
377377pub 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 {
378378 const function = selectSymbol(CreateWindowExW, pfnCreateWindowExW, .win2k);
379379 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 {
6060 const stderr_stream = io.getStdErr().writer();
6161 const stdout_stream = io.getStdOut().writer();
6262
63 var install_prefix: ?[]const u8 = null;
6364 while (nextArg(args, &arg_idx)) |arg| {
6465 if (mem.startsWith(u8, arg, "-D")) {
6566 const option_contents = arg[2..];
......@@ -82,7 +83,7 @@ pub fn main() !void {
8283 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
8384 return usage(builder, false, stdout_stream);
8485 } else if (mem.eql(u8, arg, "--prefix")) {
85 builder.install_prefix = nextArg(args, &arg_idx) orelse {
86 install_prefix = nextArg(args, &arg_idx) orelse {
8687 warn("Expected argument after --prefix\n\n", .{});
8788 return usageAndErr(builder, false, stderr_stream);
8889 };
......@@ -134,7 +135,7 @@ pub fn main() !void {
134135 }
135136 }
136137
137 builder.resolveInstallPrefix();
138 builder.resolveInstallPrefix(install_prefix);
138139 try runBuild(builder);
139140
140141 if (builder.validateUserInputDidItFail())
......@@ -162,8 +163,7 @@ fn runBuild(builder: *Builder) anyerror!void {
162163fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {
163164 // run the build script to collect the options
164165 if (!already_ran_build) {
165 builder.setInstallPrefix(null);
166 builder.resolveInstallPrefix();
166 builder.resolveInstallPrefix(null);
167167 try runBuild(builder);
168168 }
169169
lib/std/testing.zig+1-1
......@@ -298,7 +298,7 @@ pub const TmpDir = struct {
298298 sub_path: [sub_path_len]u8,
299299
300300 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);
302302
303303 pub fn cleanup(self: *TmpDir) void {
304304 self.dir.close();
src/Compilation.zig+17-6
......@@ -3188,7 +3188,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
31883188 id_symlink_basename,
31893189 &prev_digest_buf,
31903190 ) 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 });
31923196 // Handle this as a cache miss.
31933197 break :blk prev_digest_buf[0..0];
31943198 };
......@@ -3196,10 +3200,13 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
31963200 if (!mem.eql(u8, prev_digest[0..digest.len], &digest))
31973201 break :hit;
31983202
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 });
32003207 var flags_bytes: [1]u8 = undefined;
32013208 _ = 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)});
32033210 break :hit;
32043211 };
32053212
......@@ -3219,7 +3226,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
32193226 mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]);
32203227 return;
32213228 }
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 });
32233234 man.unhit(prev_hash_state, input_file_count);
32243235 }
32253236
......@@ -3366,8 +3377,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
33663377 // Update the small file with the digest. If it fails we can continue; it only
33673378 // means that the next invocation will have an unnecessary cache miss.
33683379 const stage1_flags_byte = @bitCast(u8, mod.stage1_flags);
3369 log.debug("stage1 {s} final digest={} flags={x}", .{
3370 mod.root_pkg.root_src_path, digest, stage1_flags_byte,
3380 log.debug("stage1 {s} final digest={s} flags={x}", .{
3381 mod.root_pkg.root_src_path, std.fmt.fmtSliceHexLower(&digest), stage1_flags_byte,
33713382 });
33723383 var digest_plus_flags: [digest.len + 2]u8 = undefined;
33733384 digest_plus_flags[0..digest.len].* = digest;
src/codegen/wasm.zig+41-3
......@@ -94,7 +94,7 @@ pub const Context = struct {
9494 return switch (ty.tag()) {
9595 .f32 => wasm.valtype(.f32),
9696 .f64 => wasm.valtype(.f64),
97 .u32, .i32 => wasm.valtype(.i32),
97 .u32, .i32, .bool => wasm.valtype(.i32),
9898 .u64, .i64 => wasm.valtype(.i64),
9999 else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}),
100100 };
......@@ -207,6 +207,7 @@ pub const Context = struct {
207207 .alloc => self.genAlloc(inst.castTag(.alloc).?),
208208 .arg => self.genArg(inst.castTag(.arg).?),
209209 .block => self.genBlock(inst.castTag(.block).?),
210 .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),
210211 .br => self.genBr(inst.castTag(.br).?),
211212 .call => self.genCall(inst.castTag(.call).?),
212213 .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
......@@ -220,9 +221,11 @@ pub const Context = struct {
220221 .dbg_stmt => WValue.none,
221222 .load => self.genLoad(inst.castTag(.load).?),
222223 .loop => self.genLoop(inst.castTag(.loop).?),
224 .not => self.genNot(inst.castTag(.not).?),
223225 .ret => self.genRet(inst.castTag(.ret).?),
224226 .retvoid => WValue.none,
225227 .store => self.genStore(inst.castTag(.store).?),
228 .unreach => self.genUnreachable(inst.castTag(.unreach).?),
226229 else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}),
227230 };
228231 }
......@@ -328,7 +331,7 @@ pub const Context = struct {
328331 try writer.writeByte(wasm.opcode(.i32_const));
329332 try leb.writeILEB128(writer, inst.val.toUnsignedInt());
330333 },
331 .i32 => {
334 .i32, .bool => {
332335 try writer.writeByte(wasm.opcode(.i32_const));
333336 try leb.writeILEB128(writer, inst.val.toSignedInt());
334337 },
......@@ -413,7 +416,14 @@ pub const Context = struct {
413416
414417 // insert blocks at the position of `offset` so
415418 // 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 };
417427 const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty);
418428 try self.startBlock(.block, block_ty, offset);
419429
......@@ -522,4 +532,32 @@ pub const Context = struct {
522532
523533 return .none;
524534 }
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 }
525563};
src/config.zig.in+1-1
......@@ -1,7 +1,7 @@
11pub const have_llvm = true;
22pub const version: [:0]const u8 = "@ZIG_VERSION@";
33pub const semver = try @import("std").SemanticVersion.parse(version);
4pub const enable_logging: bool = false;
4pub const enable_logging: bool = @ZIG_ENABLE_LOGGING_BOOL@;
55pub const enable_tracy = false;
66pub const is_stage1 = true;
77pub const skip_non_native = false;
src/introspect.zig+8
......@@ -61,6 +61,14 @@ pub fn findZigLibDirFromSelfExe(
6161
6262/// Caller owns returned memory.
6363pub 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
6472 const appname = "zig";
6573
6674 if (std.Target.current.os.tag != .windows) {
src/link/MachO/Archive.zig+25-3
......@@ -20,6 +20,11 @@ name: []u8,
2020
2121objects: std.ArrayListUnmanaged(Object) = .{},
2222
23/// Parsed table of contents.
24/// Each symbol name points to a list of all definition
25/// sites within the current static archive.
26toc: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(u32)) = .{},
27
2328// Archive files start with the ARMAG identifying string. Then follows a
2429// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
2530// member indicates, for each member file.
......@@ -88,6 +93,11 @@ pub fn deinit(self: *Archive) void {
8893 object.deinit();
8994 }
9095 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);
91101 self.file.close();
92102}
93103
......@@ -159,8 +169,20 @@ fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {
159169 };
160170 const object_offset = try symtab_reader.readIntLittle(u32);
161171
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);
163182
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.
164186 // Here, we assume that symbols are NOT sorted in any way, and
165187 // they point to objects in sequence.
166188 if (object_offsets.items[last] != object_offset) {
......@@ -248,8 +270,8 @@ fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 {
248270 var n = try allocator.alloc(u8, len);
249271 defer allocator.free(n);
250272 try reader.readNoEof(n);
251 const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0));
252 name = try allocator.dupe(u8, n[0..actual_len.?]);
273 const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0)) orelse n.len;
274 name = try allocator.dupe(u8, n[0..actual_len]);
253275 },
254276 }
255277 return name;
src/link/MachO/Object.zig+1
......@@ -164,6 +164,7 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi
164164 },
165165 macho.LC_DATA_IN_CODE => {
166166 self.data_in_code_cmd_index = i;
167 cmd.LinkeditData.dataoff += offset_mod;
167168 },
168169 else => {
169170 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,
6060text_const_section_index: ?u16 = null,
6161cstring_section_index: ?u16 = null,
6262
63// __DATA segment sections
63// __DATA_CONST segment sections
6464got_section_index: ?u16 = null,
65mod_init_func_section_index: ?u16 = null,
66mod_term_func_section_index: ?u16 = null,
67data_const_section_index: ?u16 = null,
68
69// __DATA segment sections
6570tlv_section_index: ?u16 = null,
6671tlv_data_section_index: ?u16 = null,
6772tlv_bss_section_index: ?u16 = null,
6873la_symbol_ptr_section_index: ?u16 = null,
69data_const_section_index: ?u16 = null,
7074data_section_index: ?u16 = null,
7175bss_section_index: ?u16 = null,
7276
......@@ -448,6 +452,46 @@ fn updateMetadata(self: *Zld, object_id: u16) !void {
448452 .reserved3 = 0,
449453 });
450454 },
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 },
451495 macho.S_ZEROFILL => {
452496 if (!mem.eql(u8, segname, "__DATA")) continue;
453497 if (self.bss_section_index != null) continue;
......@@ -583,6 +627,18 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection {
583627 .sect = self.cstring_section_index.?,
584628 };
585629 },
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 },
586642 macho.S_ZEROFILL => {
587643 break :blk .{
588644 .seg = self.data_segment_cmd_index.?,
......@@ -684,6 +740,8 @@ fn sortSections(self: *Zld) !void {
684740
685741 const indices = &[_]*?u16{
686742 &self.got_section_index,
743 &self.mod_init_func_section_index,
744 &self.mod_term_func_section_index,
687745 &self.data_const_section_index,
688746 };
689747 for (indices) |maybe_index| {
......@@ -2471,6 +2529,42 @@ fn writeRebaseInfoTable(self: *Zld) !void {
24712529 }
24722530 }
24732531
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
24742568 if (self.la_symbol_ptr_section_index) |idx| {
24752569 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);
24762570 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
......@@ -2707,7 +2801,15 @@ fn writeDebugInfo(self: *Zld) !void {
27072801 };
27082802 defer debug_info.deinit(self.allocator);
27092803
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 };
27112813 const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name);
27122814 const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir);
27132815
src/main.zig+1-1
......@@ -557,7 +557,7 @@ fn buildOutputType(
557557 var test_filter: ?[]const u8 = null;
558558 var test_name_prefix: ?[]const u8 = null;
559559 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;
561561 var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR");
562562 var main_pkg_path: ?[]const u8 = null;
563563 var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
src/stage1/all_types.hpp-4
......@@ -2139,10 +2139,6 @@ struct CodeGen {
21392139 Buf llvm_ir_file_output_path;
21402140 Buf analysis_json_output_path;
21412141 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;
21462142
21472143 Buf *builtin_zig_path;
21482144 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 {
270270 global_scope: *Scope.Root,
271271 clang_context: *clang.ASTContext,
272272 mangle_count: u32 = 0,
273 /// Table of record decls that have been demoted to opaques.
273274 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) = .{},
274277
275278 /// This one is different than the root scope's name table. This contains
276279 /// a list of names that we found by visiting all the top level decls without
......@@ -338,6 +341,7 @@ pub fn translate(
338341 context.alias_list.deinit();
339342 context.global_names.deinit(gpa);
340343 context.opaque_demotes.deinit(gpa);
344 context.unnamed_typedefs.deinit(gpa);
341345 context.global_scope.deinit();
342346 }
343347
......@@ -401,6 +405,51 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void {
401405 if (decl.castToNamedDecl()) |named_decl| {
402406 const decl_name = try c.str(named_decl.getName_bytes_begin());
403407 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 }
404453 }
405454}
406455
......@@ -752,17 +801,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
752801 const toplevel = scope.id == .root;
753802 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
754803
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;
765804 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
766808 if (record_decl.isUnion()) {
767809 container_kind_name = "union";
768810 is_union = true;
......@@ -773,7 +815,20 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
773815 return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name});
774816 }
775817
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 }
777832 if (!toplevel) name = try bs.makeMangledName(c, name);
778833 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name);
779834
......@@ -874,14 +929,19 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) E
874929 const toplevel = scope.id == .root;
875930 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
876931
877 var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin());
878932 var is_unnamed = false;
879 if (bare_name.len == 0) {
880 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
881 is_unnamed = true;
933 var bare_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin());
934 var name = bare_name;
935 if (c.unnamed_typedefs.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |typedef_name| {
936 bare_name = typedef_name;
937 name = typedef_name;
938 } else {
939 if (bare_name.len == 0) {
940 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
941 is_unnamed = true;
942 }
943 name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
882944 }
883
884 var name: []const u8 = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
885945 if (!toplevel) _ = try bs.makeMangledName(c, name);
886946 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name);
887947
......@@ -1063,6 +1123,7 @@ fn transStmt(
10631123 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt);
10641124 return transExpr(c, scope, gen_sel.getResultExpr(), result_used);
10651125 },
1126 // When adding new cases here, see comment for maybeBlockify()
10661127 else => {
10671128 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
10681129 },
......@@ -2242,6 +2303,35 @@ fn transImplicitValueInitExpr(
22422303 return transZeroInitExpr(c, scope, source_loc, ty);
22432304}
22442305
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.
2311fn 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
22452335fn transIfStmt(
22462336 c: *Context,
22472337 scope: *Scope,
......@@ -2259,9 +2349,10 @@ fn transIfStmt(
22592349 const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond());
22602350 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);
22612351
2262 const then_body = try transStmt(c, scope, stmt.getThen(), .unused);
2352 const then_body = try maybeBlockify(c, scope, stmt.getThen());
2353
22632354 const else_body = if (stmt.getElse()) |expr|
2264 try transStmt(c, scope, expr, .unused)
2355 try maybeBlockify(c, scope, expr)
22652356 else
22662357 null;
22672358 return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body });
......@@ -2286,7 +2377,7 @@ fn transWhileLoop(
22862377 .parent = scope,
22872378 .id = .loop,
22882379 };
2289 const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
2380 const body = try maybeBlockify(c, &loop_scope, stmt.getBody());
22902381 return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null });
22912382}
22922383
......@@ -2312,7 +2403,7 @@ fn transDoWhileLoop(
23122403 const if_not_break = switch (cond.tag()) {
23132404 .false_literal => return transStmt(c, scope, stmt.getBody(), .unused),
23142405 .true_literal => {
2315 const body_node = try transStmt(c, scope, stmt.getBody(), .unused);
2406 const body_node = try maybeBlockify(c, scope, stmt.getBody());
23162407 return Tag.while_true.create(c.arena, body_node);
23172408 },
23182409 else => try Tag.if_not_break.create(c.arena, cond),
......@@ -2388,7 +2479,7 @@ fn transForLoop(
23882479 else
23892480 null;
23902481
2391 const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
2482 const body = try maybeBlockify(c, &loop_scope, stmt.getBody());
23922483 const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr });
23932484 if (block_scope) |*bs| {
23942485 try bs.statements.append(while_node);
......@@ -3106,43 +3197,34 @@ fn transCreateCompoundAssign(
31063197 const requires_int_cast = blk: {
31073198 const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);
31083199 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);
31103201 };
3202
31113203 if (used == .unused) {
31123204 // common case
31133205 // c: lhs += rhs
31143206 // 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
31153211 if ((is_mod or is_div) and is_signed) {
3116 const lhs_node = try transExpr(c, scope, lhs, .used);
3117 const rhs_node = try transExpr(c, scope, rhs, .used);
3212 if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
3213 const operands = .{ .lhs = lhs_node, .rhs = rhs_node };
31183214 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)
31203216 else
3121 try Tag.div_trunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node });
3217 try Tag.div_trunc.create(c.arena, operands);
31223218
31233219 return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used);
31243220 }
31253221
3126 const lhs_node = try transExpr(c, scope, lhs, .used);
3127 var rhs_node = if (is_shift or requires_int_cast)
3128 try transExprCoercing(c, scope, rhs, .used)
3129 else
3130 try transExpr(c, scope, rhs, .used);
3131
3132 if (is_ptr_op_signed) {
3133 rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
3134 }
3135
3136 if (is_shift or requires_int_cast) {
3137 // @intCast(rhs)
3138 const cast_to_type = if (is_shift)
3139 try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc)
3140 else
3141 try transQualType(c, scope, getExprQualType(c, lhs), loc);
3142
3222 if (is_shift) {
3223 const cast_to_type = try qualTypeToLog2IntRef(c, scope, rhs_qt, loc);
31433224 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);
31443227 }
3145
31463228 return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used);
31473229 }
31483230 // worst case
......@@ -3164,29 +3246,24 @@ fn transCreateCompoundAssign(
31643246 const lhs_node = try Tag.identifier.create(c.arena, ref);
31653247 const ref_node = try Tag.deref.create(c.arena, lhs_node);
31663248
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);
31673251 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 };
31693254 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)
31713256 else
3172 try Tag.div_trunc.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node });
3257 try Tag.div_trunc.create(c.arena, operands);
31733258
31743259 const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used);
31753260 try block_scope.statements.append(assign);
31763261 } else {
3177 var rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
3178
3179 if (is_shift or requires_int_cast) {
3180 // @intCast(rhs)
3181 const cast_to_type = if (is_shift)
3182 try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc)
3183 else
3184 try transQualType(c, scope, getExprQualType(c, lhs), loc);
3185
3262 if (is_shift) {
3263 const cast_to_type = try qualTypeToLog2IntRef(c, &block_scope.base, rhs_qt, loc);
31863264 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
3187 }
3188 if (is_ptr_op_signed) {
3189 rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
3265 } else if (requires_int_cast) {
3266 rhs_node = try transCCast(c, &block_scope.base, loc, lhs_qt, rhs_qt, rhs_node);
31903267 }
31913268
31923269 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 {
12441244 \\ return 0;
12451245 \\}
12461246 , "");
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 , "");
12471311}
test/stage2/wasm.zig+35
......@@ -175,6 +175,41 @@ pub fn addCases(ctx: *TestContext) !void {
175175 \\ return i;
176176 \\}
177177 , "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 , "");
178213 }
179214
180215 {
test/standalone/mix_o_files/base64.zig+3-3
......@@ -3,9 +3,9 @@ const base64 = @import("std").base64;
33export fn decode_base_64(dest_ptr: [*]u8, dest_len: usize, source_ptr: [*]const u8, source_len: usize) usize {
44 const src = source_ptr[0..source_len];
55 const dest = dest_ptr[0..dest_len];
6 const base64_decoder = base64.standard_decoder_unsafe;
7 const decoded_size = base64_decoder.calcSize(src);
8 base64_decoder.decode(dest[0..decoded_size], src);
6 const base64_decoder = base64.standard.Decoder;
7 const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable;
8 base64_decoder.decode(dest[0..decoded_size], src) catch unreachable;
99 return decoded_size;
1010}
1111
test/translate_c.zig+64-38
......@@ -3,6 +3,28 @@ const std = @import("std");
33const CrossTarget = std.zig.CrossTarget;
44
55pub 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
628 cases.add("if as while stmt has semicolon",
729 \\void foo() {
830 \\ while (1) if (1) {
......@@ -218,9 +240,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
218240 \\} Bar;
219241 , &[_][]const u8{
220242 \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo
221 \\const struct_unnamed_1 = opaque {};
222 \\pub const Foo = struct_unnamed_1;
223 \\const struct_unnamed_2 = extern struct {
243 \\pub const Foo = opaque {};
244 \\pub const Bar = extern struct {
224245 \\ bar: ?*Foo,
225246 \\};
226247 });
......@@ -519,17 +540,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
519540 \\} outer;
520541 \\void foo(outer *x) { x->y = x->x; }
521542 , &[_][]const u8{
522 \\const struct_unnamed_3 = extern struct {
543 \\const struct_unnamed_2 = extern struct {
523544 \\ y: c_int,
524545 \\};
525 \\const union_unnamed_2 = extern union {
546 \\const union_unnamed_1 = extern union {
526547 \\ x: u8,
527 \\ unnamed_0: struct_unnamed_3,
548 \\ unnamed_0: struct_unnamed_2,
528549 \\};
529 \\const struct_unnamed_1 = extern struct {
530 \\ unnamed_0: union_unnamed_2,
550 \\pub const outer = extern struct {
551 \\ unnamed_0: union_unnamed_1,
531552 \\};
532 \\pub const outer = struct_unnamed_1;
533553 \\pub export fn foo(arg_x: [*c]outer) void {
534554 \\ var x = arg_x;
535555 \\ 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 {
565585 \\struct {int x,y;} s2 = {.y = 2, .x=1};
566586 \\foo s3 = { 123 };
567587 , &[_][]const u8{
568 \\const struct_unnamed_1 = extern struct {
588 \\pub const foo = extern struct {
569589 \\ x: c_int,
570590 \\};
571 \\pub const foo = struct_unnamed_1;
572 \\const struct_unnamed_2 = extern struct {
591 \\const struct_unnamed_1 = extern struct {
573592 \\ x: f64,
574593 \\ y: f64,
575594 \\ z: f64,
576595 \\};
577 \\pub export var s0: struct_unnamed_2 = struct_unnamed_2{
596 \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{
578597 \\ .x = 1.2,
579598 \\ .y = 1.3,
580599 \\ .z = 0,
581600 \\};
582 \\const struct_unnamed_3 = extern struct {
601 \\const struct_unnamed_2 = extern struct {
583602 \\ sec: c_int,
584603 \\ min: c_int,
585604 \\ hour: c_int,
......@@ -587,7 +606,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
587606 \\ mon: c_int,
588607 \\ year: c_int,
589608 \\};
590 \\pub export var s1: struct_unnamed_3 = struct_unnamed_3{
609 \\pub export var s1: struct_unnamed_2 = struct_unnamed_2{
591610 \\ .sec = @as(c_int, 30),
592611 \\ .min = @as(c_int, 15),
593612 \\ .hour = @as(c_int, 17),
......@@ -595,11 +614,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
595614 \\ .mon = @as(c_int, 12),
596615 \\ .year = @as(c_int, 2014),
597616 \\};
598 \\const struct_unnamed_4 = extern struct {
617 \\const struct_unnamed_3 = extern struct {
599618 \\ x: c_int,
600619 \\ y: c_int,
601620 \\};
602 \\pub export var s2: struct_unnamed_4 = struct_unnamed_4{
621 \\pub export var s2: struct_unnamed_3 = struct_unnamed_3{
603622 \\ .x = @as(c_int, 1),
604623 \\ .y = @as(c_int, 2),
605624 \\};
......@@ -1639,37 +1658,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
16391658 \\ p,
16401659 \\};
16411660 , &[_][]const u8{
1642 \\const enum_unnamed_1 = extern enum(c_int) {
1661 \\pub const d = extern enum(c_int) {
16431662 \\ a,
16441663 \\ b,
16451664 \\ c,
16461665 \\ _,
16471666 \\};
1648 \\pub const a = @enumToInt(enum_unnamed_1.a);
1649 \\pub const b = @enumToInt(enum_unnamed_1.b);
1650 \\pub const c = @enumToInt(enum_unnamed_1.c);
1651 \\pub const d = enum_unnamed_1;
1652 \\const enum_unnamed_2 = extern enum(c_int) {
1667 \\pub const a = @enumToInt(d.a);
1668 \\pub const b = @enumToInt(d.b);
1669 \\pub const c = @enumToInt(d.c);
1670 \\const enum_unnamed_1 = extern enum(c_int) {
16531671 \\ e = 0,
16541672 \\ f = 4,
16551673 \\ g = 5,
16561674 \\ _,
16571675 \\};
1658 \\pub const e = @enumToInt(enum_unnamed_2.e);
1659 \\pub const f = @enumToInt(enum_unnamed_2.f);
1660 \\pub const g = @enumToInt(enum_unnamed_2.g);
1661 \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e);
1662 \\const enum_unnamed_3 = extern enum(c_int) {
1676 \\pub const e = @enumToInt(enum_unnamed_1.e);
1677 \\pub const f = @enumToInt(enum_unnamed_1.f);
1678 \\pub const g = @enumToInt(enum_unnamed_1.g);
1679 \\pub export var h: enum_unnamed_1 = @intToEnum(enum_unnamed_1, e);
1680 \\const enum_unnamed_2 = extern enum(c_int) {
16631681 \\ i,
16641682 \\ j,
16651683 \\ k,
16661684 \\ _,
16671685 \\};
1668 \\pub const i = @enumToInt(enum_unnamed_3.i);
1669 \\pub const j = @enumToInt(enum_unnamed_3.j);
1670 \\pub const k = @enumToInt(enum_unnamed_3.k);
1686 \\pub const i = @enumToInt(enum_unnamed_2.i);
1687 \\pub const j = @enumToInt(enum_unnamed_2.j);
1688 \\pub const k = @enumToInt(enum_unnamed_2.k);
16711689 \\pub const struct_Baz = extern struct {
1672 \\ l: enum_unnamed_3,
1690 \\ l: enum_unnamed_2,
16731691 \\ m: d,
16741692 \\};
16751693 \\pub const enum_i = extern enum(c_int) {
......@@ -1934,7 +1952,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
19341952 , &[_][]const u8{
19351953 \\pub export fn foo() c_int {
19361954 \\ var a: c_int = 5;
1937 \\ while (true) a = 2;
1955 \\ while (true) {
1956 \\ a = 2;
1957 \\ }
19381958 \\ while (true) {
19391959 \\ var a_1: c_int = 4;
19401960 \\ a_1 = 9;
......@@ -1947,7 +1967,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
19471967 \\ var a_1: c_int = 2;
19481968 \\ a_1 = 12;
19491969 \\ }
1950 \\ while (true) a = 7;
1970 \\ while (true) {
1971 \\ a = 7;
1972 \\ }
19511973 \\ return 0;
19521974 \\}
19531975 });
......@@ -2008,7 +2030,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
20082030 \\}
20092031 , &[_][]const u8{
20102032 \\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 \\ }
20122036 \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);
20132037 \\}
20142038 });
......@@ -2389,7 +2413,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
23892413 \\pub const yes = [*c]u8;
23902414 \\pub export fn foo() void {
23912415 \\ var a: yes = undefined;
2392 \\ if (a != null) _ = @as(c_int, 2);
2416 \\ if (a != null) {
2417 \\ _ = @as(c_int, 2);
2418 \\ }
23932419 \\}
23942420 });
23952421
......@@ -2740,7 +2766,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
27402766 \\ var a = arg_a;
27412767 \\ var i: c_int = 0;
27422768 \\ 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));
27442770 \\ }
27452771 \\ return i;
27462772 \\}
......@@ -2760,7 +2786,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
27602786 \\ var a = arg_a;
27612787 \\ var i: c_int = 0;
27622788 \\ 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));
27642790 \\ }
27652791 \\ return i;
27662792 \\}