Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

getCpuInfoFromRegistry

windows.getCpuInfoFromRegistry
fn getCpuInfoFromRegistry(core: usize, args: anytype) !void

File

lib/std/zig/system/windows.zig:55

Code

fn getCpuInfoFromRegistry(core: usize, args: anytype) !void {
    const ArgsType = @TypeOf(args);
    const args_type_info = @typeInfo(ArgsType);

    if (args_type_info != .@"struct") {
        @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType));
    }

    const fields_info = args_type_info.@"struct";

    // Originally, I wanted to issue a single call with a more complex table structure such that we
    // would sequentially visit each CPU#d subkey in the registry and pull the value of interest into
    // a buffer, however, NT seems to be expecting a single buffer per each table meaning we would
    // end up pulling only the last CPU core info, overwriting everything else.
    // If anyone can come up with a solution to this, please do!
    const table_size = 1 + fields_info.field_names.len;
    var table: [table_size + 1]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined;

    const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor");

    const max_cpu_buf = 4;
    var next_cpu_buf: [max_cpu_buf]u8 = undefined;
    const next_cpu = try std.fmt.bufPrint(&next_cpu_buf, "{d}", .{core});

    var subkey: [max_cpu_buf + 1]u16 = undefined;
    const subkey_len = try std.unicode.utf8ToUtf16Le(&subkey, next_cpu);
    subkey[subkey_len] = 0;

    table[0] = .{
        .QueryRoutine = null,
        .Flags = std.os.windows.RTL_QUERY_REGISTRY_SUBKEY | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,
        .Name = subkey[0..subkey_len :0],
        .EntryContext = null,
        .DefaultType = .NONE,
        .DefaultData = null,
        .DefaultLength = 0,
    };

    var tmp_bufs: [fields_info.field_names.len][max_value_len]u8 align(@alignOf(std.os.windows.UNICODE_STRING)) = undefined;

    inline for (fields_info.field_names, 0..) |field_name, i| {
        const ctx: *anyopaque = blk: {
            switch (@field(args, field_name).value_type) {
                .SZ,
                .EXPAND_SZ,
                .MULTI_SZ,
                => {
                    comptime assert(@sizeOf(std.os.windows.UNICODE_STRING) % 2 == 0);
                    const unicode: *std.os.windows.UNICODE_STRING = @ptrCast(&tmp_bufs[i]);
                    unicode.* = .{
                        .Length = 0,
                        .MaximumLength = max_value_len - @sizeOf(std.os.windows.UNICODE_STRING),
                        .Buffer = @ptrCast(tmp_bufs[i][@sizeOf(std.os.windows.UNICODE_STRING)..]),
                    };
                    break :blk unicode;
                },

                .DWORD,
                .DWORD_BIG_ENDIAN,
                .QWORD,
                => break :blk &tmp_bufs[i],

                else => unreachable,
            }
        };

        var key_buf: [max_value_len / 2 + 1]u16 = undefined;
        const key_len = try std.unicode.utf8ToUtf16Le(&key_buf, @field(args, field_name).key);
        key_buf[key_len] = 0;

        table[i + 1] = .{
            .QueryRoutine = null,
            .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,
            .Name = key_buf[0..key_len :0],
            .EntryContext = ctx,
            .DefaultType = .NONE,
            .DefaultData = null,
            .DefaultLength = 0,
        };
    }

    // Table sentinel
    table[table_size] = .{
        .QueryRoutine = null,
        .Flags = 0,
        .Name = null,
        .EntryContext = null,
        .DefaultType = .NONE,
        .DefaultData = null,
        .DefaultLength = 0,
    };

    const res = std.os.windows.ntdll.RtlQueryRegistryValues(
        std.os.windows.RTL_REGISTRY_ABSOLUTE,
        topkey,
        &table,
        null,
        null,
    );
    switch (res) {
        .SUCCESS => {
            inline for (fields_info.field_names, 0..) |field_name, i| switch (@field(args, field_name).value_type) {
                .SZ,
                .EXPAND_SZ,
                .MULTI_SZ,
                => {
                    var buf = @field(args, field_name).value_buf;
                    const entry: *const std.os.windows.UNICODE_STRING = @ptrCast(table[i + 1].EntryContext);
                    const len = try std.unicode.utf16LeToUtf8(buf, entry.slice());
                    buf[len] = 0;
                },

                .DWORD,
                .DWORD_BIG_ENDIAN,
                .QWORD,
                => {
                    const entry: [*]const u8 = @ptrCast(table[i + 1].EntryContext);
                    switch (@field(args, field_name).value_type) {
                        .DWORD, .DWORD_BIG_ENDIAN => {
                            @memcpy(@field(args, field_name).value_buf[0..4], entry[0..4]);
                        },
                        .QWORD => {
                            @memcpy(@field(args, field_name).value_buf[0..8], entry[0..8]);
                        },
                        else => unreachable,
                    }
                },

                else => unreachable,
            };
        },
        else => return error.Unexpected,
    }
}