| ... | @@ -1,724 +0,0 @@ |
| 1 | <!doctype html> |
| 2 | <html> |
| 3 | <head> |
| 4 | <meta charset="utf-8"> |
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> |
| 6 | <title>The Zig Programming Language</title> |
| 7 | <link rel="stylesheet" type="text/css" href="highlight/styles/default.css"> |
| 8 | <style type="text/css"> |
| 9 | img { |
| 10 | max-width: 100%; |
| 11 | } |
| 12 | </style> |
| 13 | </head> |
| 14 | <body> |
| 15 | <img src="zig-logo.svg"> |
| 16 | <p> |
| 17 | Zig is an open-source programming language designed for <strong>robustness</strong>, |
| 18 | <strong>optimality</strong>, and <strong>clarity</strong>. |
| 19 | </p> |
| 20 | <p> |
| 21 | <a href="download/">Download</a> | |
| 22 | <a href="documentation/master/">Documentation</a> | |
| 23 | <a href="https://github.com/zig-lang/zig">Source Code</a> | |
| 24 | <a href="https://github.com/zig-lang/zig/issues">Bug Tracker</a> | |
| 25 | <a href="https://webchat.freenode.net/?channels=%23zig">IRC</a> | |
| 26 | <a href="https://www.patreon.com/andrewrk">Donate $1/month</a> |
| 27 | </p> |
| 28 | <h2>Feature Highlights</h2> |
| 29 | <ul> |
| 30 | <li>Manual memory management. Memory allocation failure is handled correctly. Edge cases matter!</li> |
| 31 | <li>Zig competes with C instead of depending on it. The Zig Standard Library does not depend on libc.</li> |
| 32 | <li>Small, simple language. Focus on debugging your application rather than debugging your knowledge of your programming language.</li> |
| 33 | <li>A fresh take on error handling that resembles what well-written C error handling looks like, |
| 34 | minus the boilerplate and verbosity.</li> |
| 35 | <li>Debug mode optimizes for fast compilation time and crashing with a stack trace when undefined behavior |
| 36 | <em>would</em> happen.</li> |
| 37 | <li>ReleaseFast mode produces heavily optimized code. What other projects call |
| 38 | "Link Time Optimization" Zig does automatically.</li> |
| 39 | <li>ReleaseSafe mode produces optimized code but keeps safety checks enabled. Disable safety checks in the bottlenecks of your code.</li> |
| 40 | <li>Generic data structures and functions.</li> |
| 41 | <li>Compile-time reflection and compile-time code execution.</li> |
| 42 | <li>Import .h files and directly use C types, variables, and functions.</li> |
| 43 | <li>Export functions, variables, and types for C code to depend on. Automatically generate .h files.</li> |
| 44 | <li>Nullable type instead of null pointers.</li> |
| 45 | <li>Order independent top level declarations.</li> |
| 46 | <li>Friendly toward package maintainers. Reproducible build, bootstrapping process carefully documented. Issues filed by package maintainers are considered especially important.</li> |
| 47 | <li>Cross-compiling is a first-class use case.</li> |
| 48 | <li>No preprocessor. Instead Zig has a few carefully designed features that |
| 49 | provide a way to accomplish things you might do with a preprocessor.</li> |
| 50 | </ul> |
| 51 | <h2 id="reading-material">Reading Material</h2> |
| 52 | <ul> |
| 53 | <li>2018-01-03 - <a href="http://andrewkelley.me/post/zig-december-2017-in-review.html">December 2017 in Review</a></li> |
| 54 | <li>2017-10-17 - <a href="download/0.1.1/release-notes.html">Zig 0.1.1 Release Notes</a></li> |
| 55 | <li>2017-07-19 - <a href="http://tiehuis.github.io/iterative-replacement-of-c-with-zig">Iterative Replacement of C with Zig</a></li> |
| 56 | <li>2017-02-16 - <a href="http://andrewkelley.me/post/a-better-way-to-implement-bit-fields.html">A Better Way to Implement Bit-Fields</a></li> |
| 57 | <li>2017-02-13 - <a href="http://andrewkelley.me/post/zig-already-more-knowable-than-c.html">Zig: Already More Knowable Than C</a></li> |
| 58 | <li>2017-01-30 - <a href="http://andrewkelley.me/post/zig-programming-language-blurs-line-compile-time-run-time.html">Zig Programming Language Blurs the Line Between Compile-Time and Run-Time</a></li> |
| 59 | <li>2016-02-08 - <a href="http://andrewkelley.me/post/intro-to-zig.html">Introduction to the Zig Programming Language</a></li> |
| 60 | </ul> |
| 61 | <h2 id="source-examples">Source Code Examples</h2> |
| 62 | <ul> |
| 63 | <li><a href="#hello">Hello World</a></li> |
| 64 | <li><a href="#hello_libc">Hello World with libc</a></li> |
| 65 | <li><a href="#parse">Parsing Unsigned Integers</a></li> |
| 66 | <li><a href="#hashmap">HashMap with Custom Allocator</a></li> |
| 67 | <li><a href="#tetris">Tetris Clone</a></li> |
| 68 | <li><a href="#clashos">Bare Bones Operating System</a></li> |
| 69 | <li><a href="#cat">Cat Utility</a></li> |
| 70 | <li><a href="#multiline-strings">Multiline String Syntax</a></li> |
| 71 | <li><a href="#mersenne">Mersenne Twister Random Number Generator</a></li> |
| 72 | </ul> |
| 73 | <h3 id="hello">Hello World</h3> |
| 74 | <pre><code class="zig">const std = @import("std"); |
| 75 | |
| 76 | pub fn main() -&gt; %void { |
| 77 | // If this program is run without stdout attached, exit with an error. |
| 78 | var stdout_file = try std.io.getStdOut(); |
| 79 | // If this program encounters pipe failure when printing to stdout, exit |
| 80 | // with an error. |
| 81 | try stdout_file.write("Hello, world!\n"); |
| 82 | }</code></pre> |
| 83 | <p>Build this with:</p> |
| 84 | <pre>zig build-exe hello.zig</pre> |
| 85 | <h3 id="hello_libc">Hello World with libc</h3> |
| 86 | <pre><code class="zig">const c = @cImport({ |
| 87 | // See https://github.com/zig-lang/zig/issues/515 |
| 88 | @cDefine("_NO_CRT_STDIO_INLINE", "1"); |
| 89 | @cInclude("stdio.h"); |
| 90 | @cInclude("string.h"); |
| 91 | }); |
| 92 | |
| 93 | const msg = c"Hello, world!\n"; |
| 94 | |
| 95 | export fn main(argc: c_int, argv: &amp;&amp;u8) -&gt; c_int { |
| 96 | if (c.printf(msg) != c_int(c.strlen(msg))) |
| 97 | return -1; |
| 98 | |
| 99 | return 0; |
| 100 | }</code></pre> |
| 101 | <p>Build this with:</p> |
| 102 | <pre>zig build-exe hello.zig --library c</pre> |
| 103 | <h3 id="parse">Parsing Unsigned Integers</h3> |
| 104 | <pre><code class="zig">pub fn parseUnsigned(comptime T: type, buf: []u8, radix: u8) -&gt; %T { |
| 105 | var x: T = 0; |
| 106 | |
| 107 | for (buf) |c| { |
| 108 | const digit = try charToDigit(c, radix); |
| 109 | x = try mulOverflow(T, x, radix); |
| 110 | x = try addOverflow(T, x, digit); |
| 111 | } |
| 112 | |
| 113 | return x; |
| 114 | } |
| 115 | |
| 116 | error InvalidChar; |
| 117 | |
| 118 | fn charToDigit(c: u8, radix: u8) -&gt; %u8 { |
| 119 | const value = switch (c) { |
| 120 | '0' ... '9' =&gt; c - '0', |
| 121 | 'A' ... 'Z' =&gt; c - 'A' + 10, |
| 122 | 'a' ... 'z' =&gt; c - 'a' + 10, |
| 123 | else =&gt; return error.InvalidChar, |
| 124 | }; |
| 125 | |
| 126 | if (value &gt;= radix) |
| 127 | return error.InvalidChar; |
| 128 | |
| 129 | return value; |
| 130 | } |
| 131 | |
| 132 | error Overflow; |
| 133 | |
| 134 | pub fn mulOverflow(comptime T: type, a: T, b: T) -&gt; %T { |
| 135 | var answer: T = undefined; |
| 136 | if (@mulWithOverflow(T, a, b, &amp;answer)) error.Overflow else answer |
| 137 | } |
| 138 | |
| 139 | pub fn addOverflow(comptime T: type, a: T, b: T) -&gt; %T { |
| 140 | var answer: T = undefined; |
| 141 | if (@addWithOverflow(T, a, b, &amp;answer)) error.Overflow else answer |
| 142 | } |
| 143 | |
| 144 | fn getNumberWithDefault(s: []u8) -&gt; u32 { |
| 145 | parseUnsigned(u32, s, 10) catch 42 |
| 146 | } |
| 147 | |
| 148 | fn getNumberOrCrash(s: []u8) -&gt; u32 { |
| 149 | %%parseUnsigned(u32, s, 10) |
| 150 | } |
| 151 | |
| 152 | fn addTwoTogetherOrReturnErr(a_str: []u8, b_str: []u8) -&gt; %u32 { |
| 153 | const a = parseUnsigned(u32, a_str, 10) catch |err| return err; |
| 154 | const b = parseUnsigned(u32, b_str, 10) catch |err| return err; |
| 155 | return a + b; |
| 156 | }</code></pre> |
| 157 | <h3 id="hashmap">HashMap with Custom Allocator</h3> |
| 158 | <pre><code class="zig">const debug = @import(&quot;debug.zig&quot;); |
| 159 | const assert = debug.assert; |
| 160 | const math = @import(&quot;math.zig&quot;); |
| 161 | const mem = @import(&quot;mem.zig&quot;); |
| 162 | const Allocator = mem.Allocator; |
| 163 | |
| 164 | const want_modification_safety = !@compileVar(&quot;is_release&quot;); |
| 165 | const debug_u32 = if (want_modification_safety) u32 else void; |
| 166 | |
| 167 | pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn(key: K)-&gt;u32, |
| 168 | comptime eql: fn(a: K, b: K)-&gt;bool) -&gt; type |
| 169 | { |
| 170 | struct { |
| 171 | entries: []Entry, |
| 172 | size: usize, |
| 173 | max_distance_from_start_index: usize, |
| 174 | allocator: &amp;Allocator, |
| 175 | // this is used to detect bugs where a hashtable is edited while an iterator is running. |
| 176 | modification_count: debug_u32, |
| 177 | |
| 178 | const Self = this; |
| 179 | |
| 180 | pub const Entry = struct { |
| 181 | used: bool, |
| 182 | distance_from_start_index: usize, |
| 183 | key: K, |
| 184 | value: V, |
| 185 | }; |
| 186 | |
| 187 | pub const Iterator = struct { |
| 188 | hm: &amp;Self, |
| 189 | // how many items have we returned |
| 190 | count: usize, |
| 191 | // iterator through the entry array |
| 192 | index: usize, |
| 193 | // used to detect concurrent modification |
| 194 | initial_modification_count: debug_u32, |
| 195 | |
| 196 | pub fn next(it: &amp;Iterator) -&gt; ?&amp;Entry { |
| 197 | if (want_modification_safety) { |
| 198 | assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification |
| 199 | } |
| 200 | if (it.count &gt;= it.hm.size) return null; |
| 201 | while (it.index &lt; it.hm.entries.len) : (it.index += 1) { |
| 202 | const entry = &amp;it.hm.entries[it.index]; |
| 203 | if (entry.used) { |
| 204 | it.index += 1; |
| 205 | it.count += 1; |
| 206 | return entry; |
| 207 | } |
| 208 | } |
| 209 | unreachable // no next item |
| 210 | } |
| 211 | }; |
| 212 | |
| 213 | pub fn init(hm: &amp;Self, allocator: &amp;Allocator) { |
| 214 | hm.entries = []Entry{}; |
| 215 | hm.allocator = allocator; |
| 216 | hm.size = 0; |
| 217 | hm.max_distance_from_start_index = 0; |
| 218 | // it doesn't actually matter what we set this to since we use wrapping integer arithmetic |
| 219 | hm.modification_count = undefined; |
| 220 | } |
| 221 | |
| 222 | pub fn deinit(hm: &amp;Self) { |
| 223 | hm.allocator.free(Entry, hm.entries); |
| 224 | } |
| 225 | |
| 226 | pub fn clear(hm: &amp;Self) { |
| 227 | for (hm.entries) |*entry| { |
| 228 | entry.used = false; |
| 229 | } |
| 230 | hm.size = 0; |
| 231 | hm.max_distance_from_start_index = 0; |
| 232 | hm.incrementModificationCount(); |
| 233 | } |
| 234 | |
| 235 | pub fn put(hm: &amp;Self, key: K, value: V) -&gt; %void { |
| 236 | if (hm.entries.len == 0) { |
| 237 | try hm.initCapacity(16); |
| 238 | } |
| 239 | hm.incrementModificationCount(); |
| 240 | |
| 241 | // if we get too full (60%), double the capacity |
| 242 | if (hm.size * 5 &gt;= hm.entries.len * 3) { |
| 243 | const old_entries = hm.entries; |
| 244 | try hm.initCapacity(hm.entries.len * 2); |
| 245 | // dump all of the old elements into the new table |
| 246 | for (old_entries) |*old_entry| { |
| 247 | if (old_entry.used) { |
| 248 | hm.internalPut(old_entry.key, old_entry.value); |
| 249 | } |
| 250 | } |
| 251 | hm.allocator.free(Entry, old_entries); |
| 252 | } |
| 253 | |
| 254 | hm.internalPut(key, value); |
| 255 | } |
| 256 | |
| 257 | pub fn get(hm: &amp;Self, key: K) -&gt; ?&amp;Entry { |
| 258 | return hm.internalGet(key); |
| 259 | } |
| 260 | |
| 261 | pub fn remove(hm: &amp;Self, key: K) { |
| 262 | hm.incrementModificationCount(); |
| 263 | const start_index = hm.keyToIndex(key); |
| 264 | {var roll_over: usize = 0; while (roll_over &lt;= hm.max_distance_from_start_index) : (roll_over += 1) { |
| 265 | const index = (start_index + roll_over) % hm.entries.len; |
| 266 | var entry = &amp;hm.entries[index]; |
| 267 | |
| 268 | assert(entry.used); // key not found |
| 269 | |
| 270 | if (!eql(entry.key, key)) continue; |
| 271 | |
| 272 | while (roll_over &lt; hm.entries.len) : (roll_over += 1) { |
| 273 | const next_index = (start_index + roll_over + 1) % hm.entries.len; |
| 274 | const next_entry = &amp;hm.entries[next_index]; |
| 275 | if (!next_entry.used or next_entry.distance_from_start_index == 0) { |
| 276 | entry.used = false; |
| 277 | hm.size -= 1; |
| 278 | return; |
| 279 | } |
| 280 | *entry = *next_entry; |
| 281 | entry.distance_from_start_index -= 1; |
| 282 | entry = next_entry; |
| 283 | } |
| 284 | unreachable // shifting everything in the table |
| 285 | }} |
| 286 | unreachable // key not found |
| 287 | } |
| 288 | |
| 289 | pub fn entryIterator(hm: &amp;Self) -&gt; Iterator { |
| 290 | return Iterator { |
| 291 | .hm = hm, |
| 292 | .count = 0, |
| 293 | .index = 0, |
| 294 | .initial_modification_count = hm.modification_count, |
| 295 | }; |
| 296 | } |
| 297 | |
| 298 | fn initCapacity(hm: &amp;Self, capacity: usize) -&gt; %void { |
| 299 | hm.entries = try hm.allocator.alloc(Entry, capacity); |
| 300 | hm.size = 0; |
| 301 | hm.max_distance_from_start_index = 0; |
| 302 | for (hm.entries) |*entry| { |
| 303 | entry.used = false; |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | fn incrementModificationCount(hm: &amp;Self) { |
| 308 | if (want_modification_safety) { |
| 309 | hm.modification_count +%= 1; |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | fn internalPut(hm: &amp;Self, orig_key: K, orig_value: V) { |
| 314 | var key = orig_key; |
| 315 | var value = orig_value; |
| 316 | const start_index = hm.keyToIndex(key); |
| 317 | var roll_over: usize = 0; |
| 318 | var distance_from_start_index: usize = 0; |
| 319 | while (roll_over &lt; hm.entries.len) : ({roll_over += 1; distance_from_start_index += 1}) { |
| 320 | const index = (start_index + roll_over) % hm.entries.len; |
| 321 | const entry = &amp;hm.entries[index]; |
| 322 | |
| 323 | if (entry.used and !eql(entry.key, key)) { |
| 324 | if (entry.distance_from_start_index &lt; distance_from_start_index) { |
| 325 | // robin hood to the rescue |
| 326 | const tmp = *entry; |
| 327 | hm.max_distance_from_start_index = math.max(hm.max_distance_from_start_index, |
| 328 | distance_from_start_index); |
| 329 | *entry = Entry { |
| 330 | .used = true, |
| 331 | .distance_from_start_index = distance_from_start_index, |
| 332 | .key = key, |
| 333 | .value = value, |
| 334 | }; |
| 335 | key = tmp.key; |
| 336 | value = tmp.value; |
| 337 | distance_from_start_index = tmp.distance_from_start_index; |
| 338 | } |
| 339 | continue; |
| 340 | } |
| 341 | |
| 342 | if (!entry.used) { |
| 343 | // adding an entry. otherwise overwriting old value with |
| 344 | // same key |
| 345 | hm.size += 1; |
| 346 | } |
| 347 | |
| 348 | hm.max_distance_from_start_index = math.max(distance_from_start_index, hm.max_distance_from_start_index); |
| 349 | *entry = Entry { |
| 350 | .used = true, |
| 351 | .distance_from_start_index = distance_from_start_index, |
| 352 | .key = key, |
| 353 | .value = value, |
| 354 | }; |
| 355 | return; |
| 356 | } |
| 357 | unreachable // put into a full map |
| 358 | } |
| 359 | |
| 360 | fn internalGet(hm: &amp;Self, key: K) -&gt; ?&amp;Entry { |
| 361 | const start_index = hm.keyToIndex(key); |
| 362 | {var roll_over: usize = 0; while (roll_over &lt;= hm.max_distance_from_start_index) : (roll_over += 1) { |
| 363 | const index = (start_index + roll_over) % hm.entries.len; |
| 364 | const entry = &amp;hm.entries[index]; |
| 365 | |
| 366 | if (!entry.used) return null; |
| 367 | if (eql(entry.key, key)) return entry; |
| 368 | }} |
| 369 | return null; |
| 370 | } |
| 371 | |
| 372 | fn keyToIndex(hm: &amp;Self, key: K) -&gt; usize { |
| 373 | return usize(hash(key)) % hm.entries.len; |
| 374 | } |
| 375 | } |
| 376 | } |
| 377 | |
| 378 | test "basic hash map test" { |
| 379 | var map: HashMap(i32, i32, hash_i32, eql_i32) = undefined; |
| 380 | map.init(&amp;debug.global_allocator); |
| 381 | defer map.deinit(); |
| 382 | |
| 383 | %%map.put(1, 11); |
| 384 | %%map.put(2, 22); |
| 385 | %%map.put(3, 33); |
| 386 | %%map.put(4, 44); |
| 387 | %%map.put(5, 55); |
| 388 | |
| 389 | assert((??map.get(2)).value == 22); |
| 390 | map.remove(2); |
| 391 | assert(if (const entry ?= map.get(2)) false else true); |
| 392 | } |
| 393 | |
| 394 | fn hash_i32(x: i32) -&gt; u32 { |
| 395 | *(&amp;u32)(&amp;x) |
| 396 | } |
| 397 | fn eql_i32(a: i32, b: i32) -&gt; bool { |
| 398 | a == b |
| 399 | }</code></pre> |
| 400 | <h3 id="tetris">Tetris Clone</h3> |
| 401 | <img src="tetris-screenshot.png"> |
| 402 | <p> |
| 403 | <a href="https://github.com/andrewrk/tetris">Source Code on GitHub</a> |
| 404 | </p> |
| 405 | <h3 id="clashos">Bare Bones Operating System</h3> |
| 406 | <p> |
| 407 | <a href="https://github.com/andrewrk/clashos">Source Code on GitHub</a> |
| 408 | </p> |
| 409 | <h3 id="cat">Cat Utility</h3> |
| 410 | <pre><code class="zig">const std = @import("std"); |
| 411 | const io = std.io; |
| 412 | const mem = std.mem; |
| 413 | const os = std.os; |
| 414 | |
| 415 | pub fn main() -&gt; %void { |
| 416 | const exe = os.args.at(0); |
| 417 | var catted_anything = false; |
| 418 | var arg_i: usize = 1; |
| 419 | while (arg_i &lt; os.args.count()) : (arg_i += 1) { |
| 420 | const arg = os.args.at(arg_i); |
| 421 | if (mem.eql(u8, arg, "-")) { |
| 422 | catted_anything = true; |
| 423 | try cat_stream(&amp;io.stdin); |
| 424 | } else if (arg[0] == '-') { |
| 425 | return usage(exe); |
| 426 | } else { |
| 427 | var is = io.InStream.open(arg, null) catch |err| { |
| 428 | %%io.stderr.printf("Unable to open file: {}\n", @errorName(err)); |
| 429 | return err; |
| 430 | }; |
| 431 | defer is.close(); |
| 432 | |
| 433 | catted_anything = true; |
| 434 | try cat_stream(&amp;is); |
| 435 | } |
| 436 | } |
| 437 | if (!catted_anything) { |
| 438 | try cat_stream(&amp;io.stdin); |
| 439 | } |
| 440 | try io.stdout.flush(); |
| 441 | } |
| 442 | |
| 443 | fn usage(exe: []const u8) -&gt; %void { |
| 444 | %%io.stderr.printf("Usage: {} [FILE]...\n", exe); |
| 445 | return error.Invalid; |
| 446 | } |
| 447 | |
| 448 | fn cat_stream(is: &amp;io.InStream) -&gt; %void { |
| 449 | var buf: [1024 * 4]u8 = undefined; |
| 450 | |
| 451 | while (true) { |
| 452 | const bytes_read = is.read(buf[0..]) catch |err| { |
| 453 | %%io.stderr.printf("Unable to read from stream: {}\n", @errorName(err)); |
| 454 | return err; |
| 455 | }; |
| 456 | |
| 457 | if (bytes_read == 0) { |
| 458 | break; |
| 459 | } |
| 460 | |
| 461 | io.stdout.write(buf[0..bytes_read]) catch |err| { |
| 462 | %%io.stderr.printf("Unable to write to stdout: {}\n", @errorName(err)); |
| 463 | return err; |
| 464 | }; |
| 465 | } |
| 466 | }</code></pre> |
| 467 | <h3 id="multiline-strings">Multiline String Syntax</h3> |
| 468 | <pre><code class="zig">pub fn createAllShaders() -&gt; AllShaders { |
| 469 | var as : AllShaders = undefined; |
| 470 | |
| 471 | as.primitive = createShader( |
| 472 | \\#version 150 core |
| 473 | \\ |
| 474 | \\in vec3 VertexPosition; |
| 475 | \\ |
| 476 | \\uniform mat4 MVP; |
| 477 | \\ |
| 478 | \\void main(void) { |
| 479 | \\ gl_Position = vec4(VertexPosition, 1.0) * MVP; |
| 480 | \\} |
| 481 | , |
| 482 | \\#version 150 core |
| 483 | \\ |
| 484 | \\out vec4 FragColor; |
| 485 | \\ |
| 486 | \\uniform vec4 Color; |
| 487 | \\ |
| 488 | \\void main(void) { |
| 489 | \\ FragColor = Color; |
| 490 | \\} |
| 491 | , null); |
| 492 | |
| 493 | as.primitive_attrib_position = as.primitive.attrib_location(c&quot;VertexPosition&quot;); |
| 494 | as.primitive_uniform_mvp = as.primitive.uniform_location(c&quot;MVP&quot;); |
| 495 | as.primitive_uniform_color = as.primitive.uniform_location(c&quot;Color&quot;); |
| 496 | |
| 497 | |
| 498 | |
| 499 | as.texture = createShader( |
| 500 | \\#version 150 core |
| 501 | \\ |
| 502 | \\in vec3 VertexPosition; |
| 503 | \\in vec2 TexCoord; |
| 504 | \\ |
| 505 | \\out vec2 FragTexCoord; |
| 506 | \\ |
| 507 | \\uniform mat4 MVP; |
| 508 | \\ |
| 509 | \\void main(void) |
| 510 | \\{ |
| 511 | \\ FragTexCoord = TexCoord; |
| 512 | \\ gl_Position = vec4(VertexPosition, 1.0) * MVP; |
| 513 | \\} |
| 514 | , |
| 515 | \\#version 150 core |
| 516 | \\ |
| 517 | \\in vec2 FragTexCoord; |
| 518 | \\out vec4 FragColor; |
| 519 | \\ |
| 520 | \\uniform sampler2D Tex; |
| 521 | \\ |
| 522 | \\void main(void) |
| 523 | \\{ |
| 524 | \\ FragColor = texture(Tex, FragTexCoord); |
| 525 | \\} |
| 526 | , null); |
| 527 | |
| 528 | as.texture_attrib_tex_coord = as.texture.attrib_location(c&quot;TexCoord&quot;); |
| 529 | as.texture_attrib_position = as.texture.attrib_location(c&quot;VertexPosition&quot;); |
| 530 | as.texture_uniform_mvp = as.texture.uniform_location(c&quot;MVP&quot;); |
| 531 | as.texture_uniform_tex = as.texture.uniform_location(c&quot;Tex&quot;); |
| 532 | |
| 533 | debug_gl.assert_no_error(); |
| 534 | |
| 535 | return as; |
| 536 | }</code></pre> |
| 537 | <h3 id="mersenne">Mersenne Twister Random Number Generator</h3> |
| 538 | <pre><code class="zig">const assert = @import(&quot;debug.zig&quot;).assert; |
| 539 | const rand_test = @import(&quot;rand_test.zig&quot;); |
| 540 | |
| 541 | pub const MT19937_32 = MersenneTwister( |
| 542 | u32, 624, 397, 31, |
| 543 | 0x9908B0DF, |
| 544 | 11, 0xFFFFFFFF, |
| 545 | 7, 0x9D2C5680, |
| 546 | 15, 0xEFC60000, |
| 547 | 18, 1812433253); |
| 548 | |
| 549 | pub const MT19937_64 = MersenneTwister( |
| 550 | u64, 312, 156, 31, |
| 551 | 0xB5026F5AA96619E9, |
| 552 | 29, 0x5555555555555555, |
| 553 | 17, 0x71D67FFFEDA60000, |
| 554 | 37, 0xFFF7EEE000000000, |
| 555 | 43, 6364136223846793005); |
| 556 | |
| 557 | /// Use `init` to initialize this state. |
| 558 | pub const Rand = struct { |
| 559 | const Rng = if (@sizeOf(usize) &gt;= 8) MT19937_64 else MT19937_32; |
| 560 | |
| 561 | rng: Rng, |
| 562 | |
| 563 | /// Initialize random state with the given seed. |
| 564 | pub fn init(r: &amp;Rand, seed: usize) { |
| 565 | r.rng.init(seed); |
| 566 | } |
| 567 | |
| 568 | /// Get an integer with random bits. |
| 569 | pub fn scalar(r: &amp;Rand, comptime T: type) -&gt; T { |
| 570 | if (T == usize) { |
| 571 | return r.rng.get(); |
| 572 | } else { |
| 573 | var result: [@sizeOf(T)]u8 = undefined; |
| 574 | r.fillBytes(result); |
| 575 | return ([]T)(result)[0]; |
| 576 | } |
| 577 | } |
| 578 | |
| 579 | /// Fill `buf` with randomness. |
| 580 | pub fn fillBytes(r: &amp;Rand, buf: []u8) { |
| 581 | var bytes_left = buf.len; |
| 582 | while (bytes_left &gt;= @sizeOf(usize)) { |
| 583 | ([]usize)(buf[buf.len - bytes_left...])[0] = r.rng.get(); |
| 584 | bytes_left -= @sizeOf(usize); |
| 585 | } |
| 586 | if (bytes_left &gt; 0) { |
| 587 | var rand_val_array : [@sizeOf(usize)]u8 = undefined; |
| 588 | ([]usize)(rand_val_array)[0] = r.rng.get(); |
| 589 | while (bytes_left &gt; 0) { |
| 590 | buf[buf.len - bytes_left] = rand_val_array[@sizeOf(usize) - bytes_left]; |
| 591 | bytes_left -= 1; |
| 592 | } |
| 593 | } |
| 594 | } |
| 595 | |
| 596 | /// Get a random unsigned integer with even distribution between `start` |
| 597 | /// inclusive and `end` exclusive. |
| 598 | // TODO support signed integers and then rename to &quot;range&quot; |
| 599 | pub fn rangeUnsigned(r: &amp;Rand, comptime T: type, start: T, end: T) -&gt; T { |
| 600 | const range = end - start; |
| 601 | const leftover = @maxValue(T) % range; |
| 602 | const upper_bound = @maxValue(T) - leftover; |
| 603 | var rand_val_array : [@sizeOf(T)]u8 = undefined; |
| 604 | |
| 605 | while (true) { |
| 606 | r.fillBytes(rand_val_array); |
| 607 | const rand_val = ([]T)(rand_val_array)[0]; |
| 608 | if (rand_val &lt; upper_bound) { |
| 609 | return start + (rand_val % range); |
| 610 | } |
| 611 | } |
| 612 | } |
| 613 | |
| 614 | /// Get a floating point value in the range 0.0..1.0. |
| 615 | pub fn float(r: &amp;Rand, comptime T: type) -&gt; T { |
| 616 | // TODO Implement this way instead: |
| 617 | // const int = @int_type(false, @sizeOf(T) * 8); |
| 618 | // const mask = ((1 &lt;&lt; @float_mantissa_bit_count(T)) - 1); |
| 619 | // const rand_bits = r.rng.scalar(int) &amp; mask; |
| 620 | // return @float_compose(T, false, 0, rand_bits) - 1.0 |
| 621 | const int_type = @intType(false, @sizeOf(T) * 8); |
| 622 | const precision = if (T == f32) { |
| 623 | 16777216 |
| 624 | } else if (T == f64) { |
| 625 | 9007199254740992 |
| 626 | } else { |
| 627 | @compileError(&quot;unknown floating point type&quot;) |
| 628 | }; |
| 629 | return T(r.rangeUnsigned(int_type, 0, precision)) / T(precision); |
| 630 | } |
| 631 | }; |
| 632 | |
| 633 | fn MersenneTwister( |
| 634 | comptime int: type, comptime n: usize, comptime m: usize, comptime r: int, |
| 635 | comptime a: int, |
| 636 | comptime u: int, comptime d: int, |
| 637 | comptime s: int, comptime b: int, |
| 638 | comptime t: int, comptime c: int, |
| 639 | comptime l: int, comptime f: int) -&gt; type |
| 640 | { |
| 641 | struct { |
| 642 | const Self = this; |
| 643 | |
| 644 | array: [n]int, |
| 645 | index: usize, |
| 646 | |
| 647 | pub fn init(mt: &amp;Self, seed: int) { |
| 648 | mt.index = n; |
| 649 | |
| 650 | var prev_value = seed; |
| 651 | mt.array[0] = prev_value; |
| 652 | {var i: usize = 1; while (i &lt; n) : (i += 1) { |
| 653 | prev_value = int(i) +% f *% (prev_value ^ (prev_value &gt;&gt; (int.bit_count - 2))); |
| 654 | mt.array[i] = prev_value; |
| 655 | }}; |
| 656 | } |
| 657 | |
| 658 | pub fn get(mt: &amp;Self) -&gt; int { |
| 659 | const mag01 = []int{0, a}; |
| 660 | const LM: int = (1 &lt;&lt; r) - 1; |
| 661 | const UM = ~LM; |
| 662 | |
| 663 | if (mt.index &gt;= n) { |
| 664 | var i: usize = 0; |
| 665 | |
| 666 | while (i &lt; n - m) : (i += 1) { |
| 667 | const x = (mt.array[i] &amp; UM) | (mt.array[i + 1] &amp; LM); |
| 668 | mt.array[i] = mt.array[i + m] ^ (x &gt;&gt; 1) ^ mag01[x &amp; 0x1]; |
| 669 | } |
| 670 | |
| 671 | while (i &lt; n - 1) : (i += 1) { |
| 672 | const x = (mt.array[i] &amp; UM) | (mt.array[i + 1] &amp; LM); |
| 673 | mt.array[i] = mt.array[i + m - n] ^ (x &gt;&gt; 1) ^ mag01[x &amp; 0x1]; |
| 674 | |
| 675 | } |
| 676 | const x = (mt.array[i] &amp; UM) | (mt.array[0] &amp; LM); |
| 677 | mt.array[i] = mt.array[m - 1] ^ (x &gt;&gt; 1) ^ mag01[x &amp; 0x1]; |
| 678 | |
| 679 | mt.index = 0; |
| 680 | } |
| 681 | |
| 682 | var x = mt.array[mt.index]; |
| 683 | mt.index += 1; |
| 684 | |
| 685 | x ^= ((x &gt;&gt; u) &amp; d); |
| 686 | x ^= ((x &lt;&lt;% s) &amp; b); |
| 687 | x ^= ((x &lt;&lt;% t) &amp; c); |
| 688 | x ^= (x &gt;&gt; l); |
| 689 | |
| 690 | return x; |
| 691 | } |
| 692 | } |
| 693 | } |
| 694 | |
| 695 | test "float 32" { |
| 696 | var r: Rand = undefined; |
| 697 | r.init(42); |
| 698 | |
| 699 | {var i: usize = 0; while (i &lt; 1000) : (i += 1) { |
| 700 | const val = r.float(f32); |
| 701 | assert(val &gt;= 0.0); |
| 702 | assert(val &lt; 1.0); |
| 703 | }} |
| 704 | } |
| 705 | |
| 706 | test "MT19937_64" { |
| 707 | var rng: MT19937_64 = undefined; |
| 708 | rng.init(rand_test.mt64_seed); |
| 709 | for (rand_test.mt64_data) |value| { |
| 710 | assert(value == rng.get()); |
| 711 | } |
| 712 | } |
| 713 | |
| 714 | test "MT19937_32" { |
| 715 | var rng: MT19937_32 = undefined; |
| 716 | rng.init(rand_test.mt32_seed); |
| 717 | for (rand_test.mt32_data) |value| { |
| 718 | assert(value == rng.get()); |
| 719 | } |
| 720 | }</code></pre> |
| 721 | <script src="highlight/highlight.pack.js"></script> |
| 722 | <script>hljs.initHighlightingOnLoad();</script> |
| 723 | </body> |
| 724 | </html> |