| ... | ... | @@ -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> |