Commit d0c39895aa

Andrew Kelley <andrew@ziglang.org>
2019-02-22 16:56:49
docs for packed structs
closes #1513
1 parent 0c5f897
Changed files (1)
doc/langref.html.in
@@ -8,13 +8,7 @@
       body{
         background-color:#111;
         color: #bbb;
-        font-family: system-ui,
-          /* Fallbacks for browsers that don't support system-ui */
-          /* https://caniuse.com/#search=system-ui */
-          -apple-system, /* iOS and macOS */
-          Roboto,        /* Android */
-          "Segoe UI",    /* Windows */
-          sans-serif;
+        font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif;
       }
       a {
         color: #88f;
@@ -263,7 +257,7 @@ pub fn main() void {
         true and false,
         true or false,
         !true);
-    
+
     // optional
     var optional_value: ?[]const u8 = null;
     assert(optional_value == null);
@@ -282,7 +276,7 @@ pub fn main() void {
 
     warn("\nerror union 1\ntype: {}\nvalue: {}\n",
         @typeName(@typeOf(number_or_error)), number_or_error);
- 
+
     number_or_error = 1234;
 
     warn("\nerror union 2\ntype: {}\nvalue: {}\n",
@@ -707,15 +701,21 @@ fn divide(a: i32, b: i32) i32 {
       {#code_end#}
       <p>
       In this function, values {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#} are known only at runtime,
-      and thus this division operation is vulnerable to both integer overflow and
-      division by zero.
+      and thus this division operation is vulnerable to both {#link|Integer Overflow#} and
+      {#link|Division by Zero#}.
       </p>
       <p>
       Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on
               integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and
                   {#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets.
       </p>
-      {#see_also|Integer Overflow|Division by Zero|Wrapping Operations#}
+      <p>
+      Zig supports arbitrary bit-width integers, referenced by using
+      an identifier of <code>i</code> or </code>u</code> followed by digits. For example, the identifier
+      {#syntax#}i7{#endsyntax#} refers to a signed 7-bit integer. The maximum allowed bit-width of an
+      integer type is {#syntax#}65535{#endsyntax#}.
+      </p>
+      {#see_also|Wrapping Operations#}
       {#header_close#}
       {#header_close#}
       {#header_open|Floats#}
@@ -1652,7 +1652,7 @@ test "pointer slicing" {
     assert(array[3] == 5);
 }
       {#code_end#}
-      <p>Pointers work at compile-time too, as long as the code does not depend on 
+      <p>Pointers work at compile-time too, as long as the code does not depend on
       an undefined memory layout:</p>
       {#code_begin|test#}
 const assert = @import("std").debug.assert;
@@ -2047,13 +2047,203 @@ test "linked list" {
 }
       {#code_end#}
       {#header_open|packed struct#}
-      <p>{#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout.</p>
-      <p>TODO bit fields</p>
-      <p>TODO alignment</p>
-      <p>TODO endianness</p>
-      <p>TODO @bitOffsetOf and @byteOffsetOf</p>
-      <p>TODO mention how volatile loads and stores of bit packed fields could be more efficient when
-      done by hand instead of with packed struct</p>
+      <p>
+      Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout:
+      </p>
+      <ul>
+        <li>Fields remain in the order declared.</li>
+        <li>There is no padding between fields.</li>
+        <li>Zig supports arbitrary width {#link|Integers#} and although normally, integers with fewer
+        than 8 bits will still use 1 byte of memory, in packed structs, they use
+        exactly their bit width.
+        </li>
+        <li>{#syntax#}bool{#endsyntax#} fields use exactly 1 bit.</li>
+        <li>A {#link|packed enum#} field uses exactly the bit width of its integer tag type.</li>
+        <li>A {#link|packed union#} field uses exactly the bit width of the union field with
+        the largest bit width.</li>
+        <li>Non-byte-aligned fields are packed into the smallest possible
+        byte-aligned integers in accordance with the target endianness.
+        </li>
+      </ul>
+      <p>
+      This means that a {#syntax#}packed struct{#endsyntax#} can participate
+      in a {#link|@bitCast#} or a {#link|@ptrCast#} to reinterpret memory.
+      This even works at {#link|comptime#}:
+      </p>
+      {#code_begin|test#}
+const std = @import("std");
+const builtin = @import("builtin");
+const assert = std.debug.assert;
+
+const Full = packed struct {
+    number: u16,
+};
+const Divided = packed struct {
+    half1: u8,
+    quarter3: u4,
+    quarter4: u4,
+};
+
+test "@bitCast between packed structs" {
+    doTheTest();
+    comptime doTheTest();
+}
+
+fn doTheTest() void {
+    assert(@sizeOf(Full) == 2);
+    assert(@sizeOf(Divided) == 2);
+    var full = Full{ .number = 0x1234 };
+    var divided = @bitCast(Divided, full);
+    switch (builtin.endian) {
+        builtin.Endian.Big => {
+            assert(divided.half1 == 0x12);
+            assert(divided.quarter3 == 0x3);
+            assert(divided.quarter4 == 0x4);
+        },
+        builtin.Endian.Little => {
+            assert(divided.half1 == 0x34);
+            assert(divided.quarter3 == 0x2);
+            assert(divided.quarter4 == 0x1);
+        },
+    }
+}
+      {#code_end#}
+      <p>
+      Zig allows the address to be taken of a non-byte-aligned field:
+      </p>
+      {#code_begin|test#}
+const std = @import("std");
+const assert = std.debug.assert;
+
+const BitField = packed struct {
+    a: u3,
+    b: u3,
+    c: u2,
+};
+
+var foo = BitField{
+    .a = 1,
+    .b = 2,
+    .c = 3,
+};
+
+test "pointer to non-byte-aligned field" {
+    const ptr = &foo.b;
+    assert(ptr.* == 2);
+}
+      {#code_end#}
+      <p>
+      However, the pointer to a non-byte-aligned field has special properties and cannot
+      be passed when a normal pointer is expected:
+      </p>
+      {#code_begin|test_err|expected type#}
+const std = @import("std");
+const assert = std.debug.assert;
+
+const BitField = packed struct {
+    a: u3,
+    b: u3,
+    c: u2,
+};
+
+var bit_field = BitField{
+    .a = 1,
+    .b = 2,
+    .c = 3,
+};
+
+test "pointer to non-bit-aligned field" {
+    assert(bar(&bit_field.b) == 2);
+}
+
+fn bar(x: *const u3) u3 {
+    return x.*;
+}
+      {#code_end#}
+      <p>
+      In this case, the function {#syntax#}bar{#endsyntax#} cannot be called becuse the pointer
+      to the non-byte-aligned field mentions the bit offset, but the function expects a byte-aligned pointer.
+      </p>
+      <p>
+      Pointers to non-byte-aligned fields share the same address as the other fields within their host integer:
+      </p>
+      {#code_begin|test#}
+const std = @import("std");
+const assert = std.debug.assert;
+
+const BitField = packed struct {
+    a: u3,
+    b: u3,
+    c: u2,
+};
+
+var bit_field = BitField{
+    .a = 1,
+    .b = 2,
+    .c = 3,
+};
+
+test "pointer to non-bit-aligned field" {
+    assert(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.b));
+    assert(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.c));
+}
+      {#code_end#}
+      <p>
+      This can be observed with {#link|@bitOffsetOf#} and {#link|byteOffsetOf#}:
+      </p>
+      {#code_begin|test#}
+const std = @import("std");
+const assert = std.debug.assert;
+
+const BitField = packed struct {
+    a: u3,
+    b: u3,
+    c: u2,
+};
+
+test "pointer to non-bit-aligned field" {
+    comptime {
+        assert(@bitOffsetOf(BitField, "a") == 0);
+        assert(@bitOffsetOf(BitField, "b") == 3);
+        assert(@bitOffsetOf(BitField, "c") == 6);
+
+        assert(@byteOffsetOf(BitField, "a") == 0);
+        assert(@byteOffsetOf(BitField, "b") == 0);
+        assert(@byteOffsetOf(BitField, "c") == 0);
+    }
+}
+      {#code_end#}
+      <p>
+      Packed structs have 1-byte alignment. However if you have an overaligned pointer to a packed struct,
+      Zig should correctly understand the alignment of fields. However there is
+      <a href="https://github.com/ziglang/zig/issues/1994">a bug</a>:
+      </p>
+      {#code_begin|test_err#}
+const S = packed struct {
+    a: u32,
+    b: u32,
+};
+test "overaligned pointer to packed struct" {
+    var foo: S align(4) = undefined;
+    const ptr: *align(4) S = &foo;
+    const ptr_to_b: *u32 = &ptr.b;
+}
+      {#code_end#}
+      <p>When this bug is fixed, the above test in the documentation will unexpectedly pass, which will
+      cause the test suite to fail, notifying the bug fixer to update these docs.
+      </p>
+      <p>
+      It's also
+      <a href="https://github.com/ziglang/zig/issues/1512">planned to be able to set alignment of struct fields</a>.
+      </p>
+      <p>
+      Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future.
+      For details on this subscribe to
+      <a href="https://github.com/ziglang/zig/issues/1761">this issue</a>.
+      TODO update these docs with a recommendation on how to use packed structs with MMIO
+      (the use case for volatile packed structs) once this issue is resolved.
+      Don't worry, there will be a good solution for this use case in zig.
+      </p>
       {#header_close#}
       {#header_open|struct Naming#}
       <p>Since all structs are anonymous, Zig infers the type name based on a few rules.</p>
@@ -2203,8 +2393,8 @@ export fn entry(foo: Foo) void { }
       {#header_close#}
       {#header_open|packed enum#}
       <p>By default, the size of enums is not guaranteed.</p>
-      <p>{#syntax#}packed enum{#endsyntax#} causes the size of the enum to be the same as the size of the integer tag type
-      of the enum:</p>
+      <p>{#syntax#}packed enum{#endsyntax#} causes the size of the enum to be the same as the size of the
+      integer tag type of the enum:</p>
       {#code_begin|test#}
 const std = @import("std");
 
@@ -2217,6 +2407,7 @@ test "packed enum" {
     std.debug.assert(@sizeOf(Number) == @sizeOf(u8));
 }
       {#code_end#}
+      <p>This makes the enum eligible to be in a {#link|packed struct#}.</p>
       {#header_close#}
       {#see_also|@memberName|@memberCount|@tagName|@sizeOf#}
       {#header_close#}
@@ -2344,7 +2535,12 @@ test "@tagName" {
       Unions with an enum tag are generated as a struct with a tag field and union field. Zig
       sorts the order of the tag and union field by the largest alignment.
       </p>
+      {#header_open|packed union#}
+      <p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible
+      to be in a {#link|packed struct#}.
+      {#header_close#}
       {#header_close#}
+
       {#header_open|blocks#}
       <p>
       Blocks are used to limit the scope of variable declarations:
@@ -3771,7 +3967,7 @@ fn bang2() void {
       Here, the stack trace does not explain how the control
       flow in {#syntax#}bar{#endsyntax#} got to the {#syntax#}hello(){#endsyntax#} call.
       One would have to open a debugger or further instrument the application
-      in order to find out. The error return trace, on the other hand, 
+      in order to find out. The error return trace, on the other hand,
       shows exactly how the error bubbled up.
       </p>
       <p>
@@ -3963,7 +4159,7 @@ test "optional type" {
       cast it to a different type:
       </p>
       {#code_begin|syntax#}
-const optional_value: ?i32 = null; 
+const optional_value: ?i32 = null;
       {#code_end#}
       {#header_close#}
       {#header_open|Optional Pointers#}
@@ -5141,7 +5337,7 @@ async fn testResumeFromSuspend(my_result: *i32) void {
       <p>
       {#syntax#}await{#endsyntax#} is valid only in an {#syntax#}async{#endsyntax#} function, and it takes
       as an operand a promise handle.
-      If the async function associated with the promise handle has already returned, 
+      If the async function associated with the promise handle has already returned,
       then {#syntax#}await{#endsyntax#} destroys the target async function, and gives the return value.
           Otherwise, {#syntax#}await{#endsyntax#} suspends the current async function, registering its
       promise handle with the target coroutine. It becomes the target coroutine's responsibility
@@ -5225,7 +5421,7 @@ fn seq(c: u8) void {
           </li>
       </ul>
       {#header_close#}
-      
+
       {#header_close#}
       {#header_open|Builtin Functions#}
       <p>
@@ -5580,13 +5776,13 @@ const warn = @import("std").debug.warn;
 
 const num1 = blk: {
     var val1: i32 = 99;
-    @compileLog("comptime val1 = ", val1); 
+    @compileLog("comptime val1 = ", val1);
     val1 = val1 + 1;
     break :blk val1;
 };
 
 test "main" {
-    @compileLog("comptime in main"); 
+    @compileLog("comptime in main");
 
     warn("Runtime in main, num1 = {}.\n", num1);
 }
@@ -5596,10 +5792,10 @@ test "main" {
       will ouput:
       </p>
       <p>
-      If all {#syntax#}@compileLog{#endsyntax#} calls are removed or 
+      If all {#syntax#}@compileLog{#endsyntax#} calls are removed or
       not encountered by analysis, the
       program compiles successfully and the generated executable prints:
-      </p>  
+      </p>
       {#code_begin|test#}
 const warn = @import("std").debug.warn;
 
@@ -6425,7 +6621,7 @@ fn List(comptime T: type) type {
       <p>
       When {#syntax#}@This(){#endsyntax#} is used at global scope, it returns a reference to the
       current import. There is a proposal to remove the import type and use an empty struct
-      type instead. See 
+      type instead. See
       <a href="https://github.com/ziglang/zig/issues/1047">#1047</a> for details.
       </p>
       {#header_close#}
@@ -7560,7 +7756,7 @@ const c = @cImport({
             {#link|Undefined Behavior#} occurs if the address is 0.
         </li>
         <li>Allows address 0. On non-freestanding targets, dereferencing address 0 is safety-checked
-            {#link|Undefined Behavior#}. Optional C pointers introduce another bit to keep track of 
+            {#link|Undefined Behavior#}. Optional C pointers introduce another bit to keep track of
             null, just like {#syntax#}?usize{#endsyntax#}. Note that creating an optional C pointer
             is unnecessary as one can use normal {#link|Optional Pointers#}.
         </li>