├── README.md ├── LICENSE ├── std ├── os │ └── windows │ │ ├── gdi32.zig │ │ ├── winmm.zig │ │ ├── psapi.zig │ │ └── user32.zig ├── Ini.zig ├── bloom_filter.zig ├── segmented_list.zig ├── rb.zig ├── http_headers.zig └── serialization.zig └── array_list_sentineled.zig /README.md: -------------------------------------------------------------------------------- 1 | # Zig Standard Library ~Graveyard~ Orphanage 2 | 3 | This is code that used to be in the standard library, but there wasn't any 4 | reason to keep maintaining it there when it could function just fine as 5 | a third party package. 6 | 7 | Feel free to start your own source code repository and take over the duties 8 | of maintaining any of this code! You can send PRs to this README to link to 9 | your project. 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (Expat) 2 | 3 | Copyright (c) 2015-2020 Zig Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /std/os/windows/gdi32.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const windows = std.os.windows; 3 | const BOOL = windows.BOOL; 4 | const DWORD = windows.DWORD; 5 | const WINAPI = windows.WINAPI; 6 | const HDC = windows.HDC; 7 | const HGLRC = windows.HGLRC; 8 | const WORD = windows.WORD; 9 | const BYTE = windows.BYTE; 10 | 11 | pub const PIXELFORMATDESCRIPTOR = extern struct { 12 | nSize: WORD = @sizeOf(PIXELFORMATDESCRIPTOR), 13 | nVersion: WORD, 14 | dwFlags: DWORD, 15 | iPixelType: BYTE, 16 | cColorBits: BYTE, 17 | cRedBits: BYTE, 18 | cRedShift: BYTE, 19 | cGreenBits: BYTE, 20 | cGreenShift: BYTE, 21 | cBlueBits: BYTE, 22 | cBlueShift: BYTE, 23 | cAlphaBits: BYTE, 24 | cAlphaShift: BYTE, 25 | cAccumBits: BYTE, 26 | cAccumRedBits: BYTE, 27 | cAccumGreenBits: BYTE, 28 | cAccumBlueBits: BYTE, 29 | cAccumAlphaBits: BYTE, 30 | cDepthBits: BYTE, 31 | cStencilBits: BYTE, 32 | cAuxBuffers: BYTE, 33 | iLayerType: BYTE, 34 | bReserved: BYTE, 35 | dwLayerMask: DWORD, 36 | dwVisibleMask: DWORD, 37 | dwDamageMask: DWORD, 38 | }; 39 | 40 | pub extern "gdi32" fn SetPixelFormat( 41 | hdc: ?HDC, 42 | format: i32, 43 | ppfd: ?*const PIXELFORMATDESCRIPTOR, 44 | ) callconv(WINAPI) bool; 45 | 46 | pub extern "gdi32" fn ChoosePixelFormat( 47 | hdc: ?HDC, 48 | ppfd: ?*const PIXELFORMATDESCRIPTOR, 49 | ) callconv(WINAPI) i32; 50 | 51 | pub extern "gdi32" fn SwapBuffers(hdc: ?HDC) callconv(WINAPI) bool; 52 | pub extern "gdi32" fn wglCreateContext(hdc: ?HDC) callconv(WINAPI) ?HGLRC; 53 | pub extern "gdi32" fn wglMakeCurrent(hdc: ?HDC, hglrc: ?HGLRC) callconv(WINAPI) bool; 54 | -------------------------------------------------------------------------------- /std/Ini.zig: -------------------------------------------------------------------------------- 1 | bytes: []const u8, 2 | 3 | pub const SectionIterator = struct { 4 | ini: Ini, 5 | next_index: ?usize, 6 | header: []const u8, 7 | 8 | pub fn next(it: *SectionIterator) ?[]const u8 { 9 | const bytes = it.ini.bytes; 10 | const start = it.next_index orelse return null; 11 | const end = mem.indexOfPos(u8, bytes, start, "\n[") orelse bytes.len; 12 | const result = bytes[start..end]; 13 | if (mem.indexOfPos(u8, bytes, start, it.header)) |next_index| { 14 | it.next_index = next_index + it.header.len; 15 | } else { 16 | it.next_index = null; 17 | } 18 | return result; 19 | } 20 | }; 21 | 22 | /// Asserts that `header` includes "\n[" at the beginning and "]\n" at the end. 23 | /// `header` must remain valid for the lifetime of the iterator. 24 | pub fn iterateSection(ini: Ini, header: []const u8) SectionIterator { 25 | assert(mem.startsWith(u8, header, "\n[")); 26 | assert(mem.endsWith(u8, header, "]\n")); 27 | const first_header = header[1..]; 28 | const next_index = if (mem.indexOf(u8, ini.bytes, first_header)) |i| 29 | i + first_header.len 30 | else 31 | null; 32 | return .{ 33 | .ini = ini, 34 | .next_index = next_index, 35 | .header = header, 36 | }; 37 | } 38 | 39 | const std = @import("std.zig"); 40 | const mem = std.mem; 41 | const assert = std.debug.assert; 42 | const Ini = @This(); 43 | const testing = std.testing; 44 | 45 | test iterateSection { 46 | const example = 47 | \\[package] 48 | \\name=libffmpeg 49 | \\version=5.1.2 50 | \\ 51 | \\[dependency] 52 | \\id=libz 53 | \\url=url1 54 | \\ 55 | \\[dependency] 56 | \\id=libmp3lame 57 | \\url=url2 58 | ; 59 | var ini: Ini = .{ .bytes = example }; 60 | var it = ini.iterateSection("\n[dependency]\n"); 61 | const section1 = it.next() orelse return error.TestFailed; 62 | try testing.expectEqualStrings("id=libz\nurl=url1\n", section1); 63 | const section2 = it.next() orelse return error.TestFailed; 64 | try testing.expectEqualStrings("id=libmp3lame\nurl=url2", section2); 65 | try testing.expect(it.next() == null); 66 | } 67 | -------------------------------------------------------------------------------- /std/os/windows/winmm.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const windows = std.os.windows; 3 | const WINAPI = windows.WINAPI; 4 | const UINT = windows.UINT; 5 | const BYTE = windows.BYTE; 6 | const DWORD = windows.DWORD; 7 | 8 | pub const MMRESULT = UINT; 9 | pub const MMSYSERR_BASE = 0; 10 | pub const TIMERR_BASE = 96; 11 | pub const MMSYSERR_ERROR = MMSYSERR_BASE + 1; 12 | pub const MMSYSERR_BADDEVICEID = MMSYSERR_BASE + 2; 13 | pub const MMSYSERR_NOTENABLED = MMSYSERR_BASE + 3; 14 | pub const MMSYSERR_ALLOCATED = MMSYSERR_BASE + 4; 15 | pub const MMSYSERR_INVALHANDLE = MMSYSERR_BASE + 5; 16 | pub const MMSYSERR_NODRIVER = MMSYSERR_BASE + 6; 17 | pub const MMSYSERR_NOMEM = MMSYSERR_BASE + 7; 18 | pub const MMSYSERR_NOTSUPPORTED = MMSYSERR_BASE + 8; 19 | pub const MMSYSERR_BADERRNUM = MMSYSERR_BASE + 9; 20 | pub const MMSYSERR_INVALFLAG = MMSYSERR_BASE + 10; 21 | pub const MMSYSERR_INVALPARAM = MMSYSERR_BASE + 11; 22 | pub const MMSYSERR_HANDLEBUSY = MMSYSERR_BASE + 12; 23 | pub const MMSYSERR_INVALIDALIAS = MMSYSERR_BASE + 13; 24 | pub const MMSYSERR_BADDB = MMSYSERR_BASE + 14; 25 | pub const MMSYSERR_KEYNOTFOUND = MMSYSERR_BASE + 15; 26 | pub const MMSYSERR_READERROR = MMSYSERR_BASE + 16; 27 | pub const MMSYSERR_WRITEERROR = MMSYSERR_BASE + 17; 28 | pub const MMSYSERR_DELETEERROR = MMSYSERR_BASE + 18; 29 | pub const MMSYSERR_VALNOTFOUND = MMSYSERR_BASE + 19; 30 | pub const MMSYSERR_NODRIVERCB = MMSYSERR_BASE + 20; 31 | pub const MMSYSERR_MOREDATA = MMSYSERR_BASE + 21; 32 | pub const MMSYSERR_LASTERROR = MMSYSERR_BASE + 21; 33 | 34 | pub const MMTIME = extern struct { 35 | wType: UINT, 36 | u: extern union { 37 | ms: DWORD, 38 | sample: DWORD, 39 | cb: DWORD, 40 | ticks: DWORD, 41 | smpte: extern struct { 42 | hour: BYTE, 43 | min: BYTE, 44 | sec: BYTE, 45 | frame: BYTE, 46 | fps: BYTE, 47 | dummy: BYTE, 48 | pad: [2]BYTE, 49 | }, 50 | midi: extern struct { 51 | songptrpos: DWORD, 52 | }, 53 | }, 54 | }; 55 | pub const LPMMTIME = *MMTIME; 56 | pub const TIME_MS = 0x0001; 57 | pub const TIME_SAMPLES = 0x0002; 58 | pub const TIME_BYTES = 0x0004; 59 | pub const TIME_SMPTE = 0x0008; 60 | pub const TIME_MIDI = 0x0010; 61 | pub const TIME_TICKS = 0x0020; 62 | 63 | // timeapi.h 64 | pub const TIMECAPS = extern struct { wPeriodMin: UINT, wPeriodMax: UINT }; 65 | pub const LPTIMECAPS = *TIMECAPS; 66 | pub const TIMERR_NOERROR = 0; 67 | pub const TIMERR_NOCANDO = TIMERR_BASE + 1; 68 | pub const TIMERR_STRUCT = TIMERR_BASE + 33; 69 | pub extern "winmm" fn timeBeginPeriod(uPeriod: UINT) callconv(WINAPI) MMRESULT; 70 | pub extern "winmm" fn timeEndPeriod(uPeriod: UINT) callconv(WINAPI) MMRESULT; 71 | pub extern "winmm" fn timeGetDevCaps(ptc: LPTIMECAPS, cbtc: UINT) callconv(WINAPI) MMRESULT; 72 | pub extern "winmm" fn timeGetSystemTime(pmmt: LPMMTIME, cbmmt: UINT) callconv(WINAPI) MMRESULT; 73 | pub extern "winmm" fn timeGetTime() callconv(WINAPI) DWORD; 74 | -------------------------------------------------------------------------------- /std/os/windows/psapi.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const windows = std.os.windows; 3 | const WINAPI = windows.WINAPI; 4 | const DWORD = windows.DWORD; 5 | const HANDLE = windows.HANDLE; 6 | const PENUM_PAGE_FILE_CALLBACKW = windows.PENUM_PAGE_FILE_CALLBACKW; 7 | const HMODULE = windows.HMODULE; 8 | const BOOL = windows.BOOL; 9 | const BOOLEAN = windows.BOOLEAN; 10 | const CONDITION_VARIABLE = windows.CONDITION_VARIABLE; 11 | const CONSOLE_SCREEN_BUFFER_INFO = windows.CONSOLE_SCREEN_BUFFER_INFO; 12 | const COORD = windows.COORD; 13 | const FILE_INFO_BY_HANDLE_CLASS = windows.FILE_INFO_BY_HANDLE_CLASS; 14 | const HRESULT = windows.HRESULT; 15 | const LARGE_INTEGER = windows.LARGE_INTEGER; 16 | const LPCWSTR = windows.LPCWSTR; 17 | const LPTHREAD_START_ROUTINE = windows.LPTHREAD_START_ROUTINE; 18 | const LPVOID = windows.LPVOID; 19 | const LPWSTR = windows.LPWSTR; 20 | const MODULEINFO = windows.MODULEINFO; 21 | const OVERLAPPED = windows.OVERLAPPED; 22 | const PERFORMANCE_INFORMATION = windows.PERFORMANCE_INFORMATION; 23 | const PROCESS_MEMORY_COUNTERS = windows.PROCESS_MEMORY_COUNTERS; 24 | const PSAPI_WS_WATCH_INFORMATION = windows.PSAPI_WS_WATCH_INFORMATION; 25 | const PSAPI_WS_WATCH_INFORMATION_EX = windows.PSAPI_WS_WATCH_INFORMATION_EX; 26 | const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES; 27 | const SIZE_T = windows.SIZE_T; 28 | const SRWLOCK = windows.SRWLOCK; 29 | const UINT = windows.UINT; 30 | const VECTORED_EXCEPTION_HANDLER = windows.VECTORED_EXCEPTION_HANDLER; 31 | const WCHAR = windows.WCHAR; 32 | const WORD = windows.WORD; 33 | const Win32Error = windows.Win32Error; 34 | const va_list = windows.va_list; 35 | const HLOCAL = windows.HLOCAL; 36 | const FILETIME = windows.FILETIME; 37 | const STARTUPINFOW = windows.STARTUPINFOW; 38 | const PROCESS_INFORMATION = windows.PROCESS_INFORMATION; 39 | const OVERLAPPED_ENTRY = windows.OVERLAPPED_ENTRY; 40 | const LPHEAP_SUMMARY = windows.LPHEAP_SUMMARY; 41 | const ULONG_PTR = windows.ULONG_PTR; 42 | const FILE_NOTIFY_INFORMATION = windows.FILE_NOTIFY_INFORMATION; 43 | const HANDLER_ROUTINE = windows.HANDLER_ROUTINE; 44 | const ULONG = windows.ULONG; 45 | const PVOID = windows.PVOID; 46 | const LPSTR = windows.LPSTR; 47 | const PENUM_PAGE_FILE_CALLBACKA = windows.PENUM_PAGE_FILE_CALLBACKA; 48 | 49 | pub extern "psapi" fn EmptyWorkingSet(hProcess: HANDLE) callconv(WINAPI) BOOL; 50 | pub extern "psapi" fn EnumDeviceDrivers(lpImageBase: [*]LPVOID, cb: DWORD, lpcbNeeded: *DWORD) callconv(WINAPI) BOOL; 51 | pub extern "psapi" fn EnumPageFilesA(pCallBackRoutine: PENUM_PAGE_FILE_CALLBACKA, pContext: LPVOID) callconv(WINAPI) BOOL; 52 | pub extern "psapi" fn EnumPageFilesW(pCallBackRoutine: PENUM_PAGE_FILE_CALLBACKW, pContext: LPVOID) callconv(WINAPI) BOOL; 53 | pub extern "psapi" fn EnumProcessModules(hProcess: HANDLE, lphModule: [*]HMODULE, cb: DWORD, lpcbNeeded: *DWORD) callconv(WINAPI) BOOL; 54 | pub extern "psapi" fn EnumProcessModulesEx(hProcess: HANDLE, lphModule: [*]HMODULE, cb: DWORD, lpcbNeeded: *DWORD, dwFilterFlag: DWORD) callconv(WINAPI) BOOL; 55 | pub extern "psapi" fn EnumProcesses(lpidProcess: [*]DWORD, cb: DWORD, cbNeeded: *DWORD) callconv(WINAPI) BOOL; 56 | pub extern "psapi" fn GetDeviceDriverBaseNameA(ImageBase: LPVOID, lpBaseName: LPSTR, nSize: DWORD) callconv(WINAPI) DWORD; 57 | pub extern "psapi" fn GetDeviceDriverBaseNameW(ImageBase: LPVOID, lpBaseName: LPWSTR, nSize: DWORD) callconv(WINAPI) DWORD; 58 | pub extern "psapi" fn GetDeviceDriverFileNameA(ImageBase: LPVOID, lpFilename: LPSTR, nSize: DWORD) callconv(WINAPI) DWORD; 59 | pub extern "psapi" fn GetDeviceDriverFileNameW(ImageBase: LPVOID, lpFilename: LPWSTR, nSize: DWORD) callconv(WINAPI) DWORD; 60 | pub extern "psapi" fn GetMappedFileNameA(hProcess: HANDLE, lpv: ?LPVOID, lpFilename: LPSTR, nSize: DWORD) callconv(WINAPI) DWORD; 61 | pub extern "psapi" fn GetMappedFileNameW(hProcess: HANDLE, lpv: ?LPVOID, lpFilename: LPWSTR, nSize: DWORD) callconv(WINAPI) DWORD; 62 | pub extern "psapi" fn GetModuleBaseNameA(hProcess: HANDLE, hModule: ?HMODULE, lpBaseName: LPSTR, nSize: DWORD) callconv(WINAPI) DWORD; 63 | pub extern "psapi" fn GetModuleBaseNameW(hProcess: HANDLE, hModule: ?HMODULE, lpBaseName: LPWSTR, nSize: DWORD) callconv(WINAPI) DWORD; 64 | pub extern "psapi" fn GetModuleFileNameExA(hProcess: HANDLE, hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) callconv(WINAPI) DWORD; 65 | pub extern "psapi" fn GetModuleFileNameExW(hProcess: HANDLE, hModule: ?HMODULE, lpFilename: LPWSTR, nSize: DWORD) callconv(WINAPI) DWORD; 66 | pub extern "psapi" fn GetModuleInformation(hProcess: HANDLE, hModule: HMODULE, lpmodinfo: *MODULEINFO, cb: DWORD) callconv(WINAPI) BOOL; 67 | pub extern "psapi" fn GetPerformanceInfo(pPerformanceInformation: *PERFORMANCE_INFORMATION, cb: DWORD) callconv(WINAPI) BOOL; 68 | pub extern "psapi" fn GetProcessImageFileNameA(hProcess: HANDLE, lpImageFileName: LPSTR, nSize: DWORD) callconv(WINAPI) DWORD; 69 | pub extern "psapi" fn GetProcessImageFileNameW(hProcess: HANDLE, lpImageFileName: LPWSTR, nSize: DWORD) callconv(WINAPI) DWORD; 70 | pub extern "psapi" fn GetProcessMemoryInfo(Process: HANDLE, ppsmemCounters: *PROCESS_MEMORY_COUNTERS, cb: DWORD) callconv(WINAPI) BOOL; 71 | pub extern "psapi" fn GetWsChanges(hProcess: HANDLE, lpWatchInfo: *PSAPI_WS_WATCH_INFORMATION, cb: DWORD) callconv(WINAPI) BOOL; 72 | pub extern "psapi" fn GetWsChangesEx(hProcess: HANDLE, lpWatchInfoEx: *PSAPI_WS_WATCH_INFORMATION_EX, cb: DWORD) callconv(WINAPI) BOOL; 73 | pub extern "psapi" fn InitializeProcessForWsWatch(hProcess: HANDLE) callconv(WINAPI) BOOL; 74 | pub extern "psapi" fn QueryWorkingSet(hProcess: HANDLE, pv: PVOID, cb: DWORD) callconv(WINAPI) BOOL; 75 | pub extern "psapi" fn QueryWorkingSetEx(hProcess: HANDLE, pv: PVOID, cb: DWORD) callconv(WINAPI) BOOL; 76 | -------------------------------------------------------------------------------- /array_list_sentineled.zig: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2015-2020 Zig Contributors 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. 4 | // The MIT license requires this copyright notice to be included in all copies 5 | // and substantial portions of the software. 6 | const std = @import("std.zig"); 7 | const debug = std.debug; 8 | const mem = std.mem; 9 | const Allocator = mem.Allocator; 10 | const assert = debug.assert; 11 | const testing = std.testing; 12 | const ArrayList = std.ArrayList; 13 | 14 | /// A contiguous, growable list of items in memory, with a sentinel after them. 15 | /// The sentinel is maintained when appending, resizing, etc. 16 | /// If you do not need a sentinel, consider using `ArrayList` instead. 17 | pub fn ArrayListSentineled(comptime T: type, comptime sentinel: T) type { 18 | return struct { 19 | list: ArrayList(T), 20 | 21 | const Self = @This(); 22 | 23 | /// Must deinitialize with deinit. 24 | pub fn init(allocator: *Allocator, m: []const T) !Self { 25 | var self = try initSize(allocator, m.len); 26 | mem.copy(T, self.list.items, m); 27 | return self; 28 | } 29 | 30 | /// Initialize memory to size bytes of undefined values. 31 | /// Must deinitialize with deinit. 32 | pub fn initSize(allocator: *Allocator, size: usize) !Self { 33 | var self = initNull(allocator); 34 | try self.resize(size); 35 | return self; 36 | } 37 | 38 | /// Initialize with capacity to hold at least num bytes. 39 | /// Must deinitialize with deinit. 40 | pub fn initCapacity(allocator: *Allocator, num: usize) !Self { 41 | var self = Self{ .list = try ArrayList(T).initCapacity(allocator, num + 1) }; 42 | self.list.appendAssumeCapacity(sentinel); 43 | return self; 44 | } 45 | 46 | /// Must deinitialize with deinit. 47 | /// None of the other operations are valid until you do one of these: 48 | /// * `replaceContents` 49 | /// * `resize` 50 | pub fn initNull(allocator: *Allocator) Self { 51 | return Self{ .list = ArrayList(T).init(allocator) }; 52 | } 53 | 54 | /// Must deinitialize with deinit. 55 | pub fn initFromBuffer(buffer: Self) !Self { 56 | return Self.init(buffer.list.allocator, buffer.span()); 57 | } 58 | 59 | /// Takes ownership of the passed in slice. The slice must have been 60 | /// allocated with `allocator`. 61 | /// Must deinitialize with deinit. 62 | pub fn fromOwnedSlice(allocator: *Allocator, slice: []T) !Self { 63 | var self = Self{ .list = ArrayList(T).fromOwnedSlice(allocator, slice) }; 64 | try self.list.append(sentinel); 65 | return self; 66 | } 67 | 68 | /// The caller owns the returned memory. The list becomes null and is safe to `deinit`. 69 | pub fn toOwnedSlice(self: *Self) [:sentinel]T { 70 | const allocator = self.list.allocator; 71 | const result = self.list.toOwnedSlice(); 72 | self.* = initNull(allocator); 73 | return result[0 .. result.len - 1 :sentinel]; 74 | } 75 | 76 | /// Only works when `T` is `u8`. 77 | pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: anytype) !Self { 78 | const size = std.math.cast(usize, std.fmt.count(format, args)) catch |err| switch (err) { 79 | error.Overflow => return error.OutOfMemory, 80 | }; 81 | var self = try Self.initSize(allocator, size); 82 | assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size); 83 | return self; 84 | } 85 | 86 | pub fn deinit(self: *Self) void { 87 | self.list.deinit(); 88 | } 89 | 90 | pub fn span(self: anytype) @TypeOf(self.list.items[0..:sentinel]) { 91 | return self.list.items[0..self.len() :sentinel]; 92 | } 93 | 94 | pub fn shrink(self: *Self, new_len: usize) void { 95 | assert(new_len <= self.len()); 96 | self.list.shrink(new_len + 1); 97 | self.list.items[self.len()] = sentinel; 98 | } 99 | 100 | pub fn resize(self: *Self, new_len: usize) !void { 101 | try self.list.resize(new_len + 1); 102 | self.list.items[self.len()] = sentinel; 103 | } 104 | 105 | pub fn isNull(self: Self) bool { 106 | return self.list.items.len == 0; 107 | } 108 | 109 | pub fn len(self: Self) usize { 110 | return self.list.items.len - 1; 111 | } 112 | 113 | pub fn capacity(self: Self) usize { 114 | return if (self.list.capacity > 0) 115 | self.list.capacity - 1 116 | else 117 | 0; 118 | } 119 | 120 | pub fn appendSlice(self: *Self, m: []const T) !void { 121 | const old_len = self.len(); 122 | try self.resize(old_len + m.len); 123 | mem.copy(T, self.list.items[old_len..], m); 124 | } 125 | 126 | pub fn append(self: *Self, byte: T) !void { 127 | const old_len = self.len(); 128 | try self.resize(old_len + 1); 129 | self.list.items[old_len] = byte; 130 | } 131 | 132 | pub fn eql(self: Self, m: []const T) bool { 133 | return mem.eql(T, self.span(), m); 134 | } 135 | 136 | pub fn startsWith(self: Self, m: []const T) bool { 137 | if (self.len() < m.len) return false; 138 | return mem.eql(T, self.list.items[0..m.len], m); 139 | } 140 | 141 | pub fn endsWith(self: Self, m: []const T) bool { 142 | const l = self.len(); 143 | if (l < m.len) return false; 144 | const start = l - m.len; 145 | return mem.eql(T, self.list.items[start..l], m); 146 | } 147 | 148 | pub fn replaceContents(self: *Self, m: []const T) !void { 149 | try self.resize(m.len); 150 | mem.copy(T, self.list.items, m); 151 | } 152 | 153 | /// Initializes an OutStream which will append to the list. 154 | /// This function may be called only when `T` is `u8`. 155 | pub fn outStream(self: *Self) std.io.OutStream(*Self, error{OutOfMemory}, appendWrite) { 156 | return .{ .context = self }; 157 | } 158 | 159 | /// Same as `append` except it returns the number of bytes written, which is always the same 160 | /// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API. 161 | /// This function may be called only when `T` is `u8`. 162 | pub fn appendWrite(self: *Self, m: []const u8) !usize { 163 | try self.appendSlice(m); 164 | return m.len; 165 | } 166 | }; 167 | } 168 | 169 | test "simple" { 170 | var buf = try ArrayListSentineled(u8, 0).init(testing.allocator, ""); 171 | defer buf.deinit(); 172 | 173 | testing.expect(buf.len() == 0); 174 | try buf.appendSlice("hello"); 175 | try buf.appendSlice(" "); 176 | try buf.appendSlice("world"); 177 | testing.expect(buf.eql("hello world")); 178 | testing.expect(mem.eql(u8, mem.spanZ(buf.span().ptr), buf.span())); 179 | 180 | var buf2 = try ArrayListSentineled(u8, 0).initFromBuffer(buf); 181 | defer buf2.deinit(); 182 | testing.expect(buf.eql(buf2.span())); 183 | 184 | testing.expect(buf.startsWith("hell")); 185 | testing.expect(buf.endsWith("orld")); 186 | 187 | try buf2.resize(4); 188 | testing.expect(buf.startsWith(buf2.span())); 189 | } 190 | 191 | test "initSize" { 192 | var buf = try ArrayListSentineled(u8, 0).initSize(testing.allocator, 3); 193 | defer buf.deinit(); 194 | testing.expect(buf.len() == 3); 195 | try buf.appendSlice("hello"); 196 | testing.expect(mem.eql(u8, buf.span()[3..], "hello")); 197 | } 198 | 199 | test "initCapacity" { 200 | var buf = try ArrayListSentineled(u8, 0).initCapacity(testing.allocator, 10); 201 | defer buf.deinit(); 202 | testing.expect(buf.len() == 0); 203 | testing.expect(buf.capacity() >= 10); 204 | const old_cap = buf.capacity(); 205 | try buf.appendSlice("hello"); 206 | testing.expect(buf.len() == 5); 207 | testing.expect(buf.capacity() == old_cap); 208 | testing.expect(mem.eql(u8, buf.span(), "hello")); 209 | } 210 | 211 | test "print" { 212 | var buf = try ArrayListSentineled(u8, 0).init(testing.allocator, ""); 213 | defer buf.deinit(); 214 | 215 | try buf.outStream().print("Hello {} the {}", .{ 2, "world" }); 216 | testing.expect(buf.eql("Hello 2 the world")); 217 | } 218 | 219 | test "outStream" { 220 | var buffer = try ArrayListSentineled(u8, 0).initSize(testing.allocator, 0); 221 | defer buffer.deinit(); 222 | const buf_stream = buffer.outStream(); 223 | 224 | const x: i32 = 42; 225 | const y: i32 = 1234; 226 | try buf_stream.print("x: {}\ny: {}\n", .{ x, y }); 227 | 228 | testing.expect(mem.eql(u8, buffer.span(), "x: 42\ny: 1234\n")); 229 | } 230 | -------------------------------------------------------------------------------- /std/bloom_filter.zig: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2015-2020 Zig Contributors 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. 4 | // The MIT license requires this copyright notice to be included in all copies 5 | // and substantial portions of the software. 6 | const std = @import("std"); 7 | const math = std.math; 8 | const debug = std.debug; 9 | const assert = std.debug.assert; 10 | const testing = std.testing; 11 | 12 | /// There is a trade off of how quickly to fill a bloom filter; 13 | /// the number of items is: 14 | /// n_items / K * ln(2) 15 | /// the rate of false positives is: 16 | /// (1-e^(-K*N/n_items))^K 17 | /// where N is the number of items 18 | pub fn BloomFilter( 19 | /// Size of bloom filter in cells, must be a power of two. 20 | comptime n_items: usize, 21 | /// Number of cells to set per item 22 | comptime K: usize, 23 | /// Cell type, should be: 24 | /// - `bool` for a standard bloom filter 25 | /// - an unsigned integer type for a counting bloom filter 26 | comptime Cell: type, 27 | /// endianess of the Cell 28 | comptime endian: std.builtin.Endian, 29 | /// Hash function to use 30 | comptime hash: fn (out: []u8, Ki: usize, in: []const u8) void, 31 | ) type { 32 | assert(n_items > 0); 33 | assert(math.isPowerOfTwo(n_items)); 34 | assert(K > 0); 35 | const cellEmpty = if (Cell == bool) false else @as(Cell, 0); 36 | const cellMax = if (Cell == bool) true else math.maxInt(Cell); 37 | const n_bytes = (n_items * comptime std.meta.bitCount(Cell)) / 8; 38 | assert(n_bytes > 0); 39 | const Io = std.packed_int_array.PackedIntIo(Cell, endian); 40 | 41 | return struct { 42 | const Self = @This(); 43 | pub const items = n_items; 44 | pub const Index = math.IntFittingRange(0, n_items - 1); 45 | 46 | data: [n_bytes]u8 = [_]u8{0} ** n_bytes, 47 | 48 | pub fn reset(self: *Self) void { 49 | std.mem.set(u8, self.data[0..], 0); 50 | } 51 | 52 | pub fn @"union"(x: Self, y: Self) Self { 53 | var r = Self{ .data = undefined }; 54 | inline for (x.data) |v, i| { 55 | r.data[i] = v | y.data[i]; 56 | } 57 | return r; 58 | } 59 | 60 | pub fn intersection(x: Self, y: Self) Self { 61 | var r = Self{ .data = undefined }; 62 | inline for (x.data) |v, i| { 63 | r.data[i] = v & y.data[i]; 64 | } 65 | return r; 66 | } 67 | 68 | pub fn getCell(self: Self, cell: Index) Cell { 69 | return Io.get(&self.data, cell, 0); 70 | } 71 | 72 | pub fn incrementCell(self: *Self, cell: Index) void { 73 | if (Cell == bool or Cell == u1) { 74 | // skip the 'get' operation 75 | Io.set(&self.data, cell, 0, cellMax); 76 | } else { 77 | const old = Io.get(&self.data, cell, 0); 78 | if (old != cellMax) { 79 | Io.set(&self.data, cell, 0, old + 1); 80 | } 81 | } 82 | } 83 | 84 | pub fn clearCell(self: *Self, cell: Index) void { 85 | Io.set(&self.data, cell, 0, cellEmpty); 86 | } 87 | 88 | pub fn add(self: *Self, item: []const u8) void { 89 | comptime var i = 0; 90 | inline while (i < K) : (i += 1) { 91 | var K_th_bit: packed struct { 92 | x: Index, 93 | } = undefined; 94 | hash(std.mem.asBytes(&K_th_bit), i, item); 95 | incrementCell(self, K_th_bit.x); 96 | } 97 | } 98 | 99 | pub fn contains(self: Self, item: []const u8) bool { 100 | comptime var i = 0; 101 | inline while (i < K) : (i += 1) { 102 | var K_th_bit: packed struct { 103 | x: Index, 104 | } = undefined; 105 | hash(std.mem.asBytes(&K_th_bit), i, item); 106 | if (getCell(self, K_th_bit.x) == cellEmpty) 107 | return false; 108 | } 109 | return true; 110 | } 111 | 112 | pub fn resize(self: Self, comptime newsize: usize) BloomFilter(newsize, K, Cell, endian, hash) { 113 | var r: BloomFilter(newsize, K, Cell, endian, hash) = undefined; 114 | if (newsize < n_items) { 115 | std.mem.copy(u8, r.data[0..], self.data[0..r.data.len]); 116 | var copied: usize = r.data.len; 117 | while (copied < self.data.len) : (copied += r.data.len) { 118 | for (self.data[copied .. copied + r.data.len]) |s, i| { 119 | r.data[i] |= s; 120 | } 121 | } 122 | } else if (newsize == n_items) { 123 | r = self; 124 | } else if (newsize > n_items) { 125 | var copied: usize = 0; 126 | while (copied < r.data.len) : (copied += self.data.len) { 127 | std.mem.copy(u8, r.data[copied .. copied + self.data.len], &self.data); 128 | } 129 | } 130 | return r; 131 | } 132 | 133 | /// Returns number of non-zero cells 134 | pub fn popCount(self: Self) Index { 135 | var n: Index = 0; 136 | if (Cell == bool or Cell == u1) { 137 | for (self.data) |b, i| { 138 | n += @popCount(u8, b); 139 | } 140 | } else { 141 | var i: usize = 0; 142 | while (i < n_items) : (i += 1) { 143 | const cell = self.getCell(@intCast(Index, i)); 144 | n += if (if (Cell == bool) cell else cell > 0) @as(Index, 1) else @as(Index, 0); 145 | } 146 | } 147 | return n; 148 | } 149 | 150 | pub fn estimateItems(self: Self) f64 { 151 | const m = comptime @intToFloat(f64, n_items); 152 | const k = comptime @intToFloat(f64, K); 153 | const X = @intToFloat(f64, self.popCount()); 154 | return (comptime (-m / k)) * math.log1p(X * comptime (-1 / m)); 155 | } 156 | }; 157 | } 158 | 159 | fn hashFunc(out: []u8, Ki: usize, in: []const u8) void { 160 | var st = std.crypto.hash.Gimli.init(.{}); 161 | st.update(std.mem.asBytes(&Ki)); 162 | st.update(in); 163 | st.final(out); 164 | } 165 | 166 | test "std.BloomFilter" { 167 | inline for ([_]type{ bool, u1, u2, u3, u4 }) |Cell| { 168 | const emptyCell = if (Cell == bool) false else @as(Cell, 0); 169 | const BF = BloomFilter(128 * 8, 8, Cell, std.builtin.endian, hashFunc); 170 | var bf = BF{}; 171 | var i: usize = undefined; 172 | // confirm that it is initialised to the empty filter 173 | i = 0; 174 | while (i < BF.items) : (i += 1) { 175 | testing.expectEqual(emptyCell, bf.getCell(@intCast(BF.Index, i))); 176 | } 177 | testing.expectEqual(@as(BF.Index, 0), bf.popCount()); 178 | testing.expectEqual(@as(f64, 0), bf.estimateItems()); 179 | // fill in a few items 180 | bf.incrementCell(42); 181 | bf.incrementCell(255); 182 | bf.incrementCell(256); 183 | bf.incrementCell(257); 184 | // check that they were set 185 | testing.expectEqual(true, bf.getCell(42) != emptyCell); 186 | testing.expectEqual(true, bf.getCell(255) != emptyCell); 187 | testing.expectEqual(true, bf.getCell(256) != emptyCell); 188 | testing.expectEqual(true, bf.getCell(257) != emptyCell); 189 | // clear just one of them; make sure the rest are still set 190 | bf.clearCell(256); 191 | testing.expectEqual(true, bf.getCell(42) != emptyCell); 192 | testing.expectEqual(true, bf.getCell(255) != emptyCell); 193 | testing.expectEqual(false, bf.getCell(256) != emptyCell); 194 | testing.expectEqual(true, bf.getCell(257) != emptyCell); 195 | // reset any of the ones we've set and confirm we're back to the empty filter 196 | bf.clearCell(42); 197 | bf.clearCell(255); 198 | bf.clearCell(257); 199 | i = 0; 200 | while (i < BF.items) : (i += 1) { 201 | testing.expectEqual(emptyCell, bf.getCell(@intCast(BF.Index, i))); 202 | } 203 | testing.expectEqual(@as(BF.Index, 0), bf.popCount()); 204 | testing.expectEqual(@as(f64, 0), bf.estimateItems()); 205 | 206 | // Lets add a string 207 | bf.add("foo"); 208 | testing.expectEqual(true, bf.contains("foo")); 209 | { 210 | // try adding same string again. make sure popcount is the same 211 | const old_popcount = bf.popCount(); 212 | testing.expect(old_popcount > 0); 213 | bf.add("foo"); 214 | testing.expectEqual(true, bf.contains("foo")); 215 | testing.expectEqual(old_popcount, bf.popCount()); 216 | } 217 | 218 | // Get back to empty filter via .reset 219 | bf.reset(); 220 | // Double check that .reset worked 221 | i = 0; 222 | while (i < BF.items) : (i += 1) { 223 | testing.expectEqual(emptyCell, bf.getCell(@intCast(BF.Index, i))); 224 | } 225 | testing.expectEqual(@as(BF.Index, 0), bf.popCount()); 226 | testing.expectEqual(@as(f64, 0), bf.estimateItems()); 227 | 228 | comptime var teststrings = [_][]const u8{ 229 | "foo", 230 | "bar", 231 | "a longer string", 232 | "some more", 233 | "the quick brown fox", 234 | "unique string", 235 | }; 236 | inline for (teststrings) |str| { 237 | bf.add(str); 238 | } 239 | inline for (teststrings) |str| { 240 | testing.expectEqual(true, bf.contains(str)); 241 | } 242 | 243 | { // estimate should be close for low packing 244 | const est = bf.estimateItems(); 245 | testing.expect(est > @intToFloat(f64, teststrings.len) - 1); 246 | testing.expect(est < @intToFloat(f64, teststrings.len) + 1); 247 | } 248 | 249 | const larger_bf = bf.resize(4096); 250 | inline for (teststrings) |str| { 251 | testing.expectEqual(true, larger_bf.contains(str)); 252 | } 253 | testing.expectEqual(@as(u12, bf.popCount()) * (4096 / 1024), larger_bf.popCount()); 254 | 255 | const smaller_bf = bf.resize(64); 256 | inline for (teststrings) |str| { 257 | testing.expectEqual(true, smaller_bf.contains(str)); 258 | } 259 | testing.expect(bf.popCount() <= @as(u10, smaller_bf.popCount()) * (1024 / 64)); 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /std/segmented_list.zig: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2015-2020 Zig Contributors 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. 4 | // The MIT license requires this copyright notice to be included in all copies 5 | // and substantial portions of the software. 6 | const std = @import("std.zig"); 7 | const assert = std.debug.assert; 8 | const testing = std.testing; 9 | const Allocator = std.mem.Allocator; 10 | 11 | // Imagine that `fn at(self: *Self, index: usize) &T` is a customer asking for a box 12 | // from a warehouse, based on a flat array, boxes ordered from 0 to N - 1. 13 | // But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes. 14 | // So when the customer requests a box index, we have to translate it to shelf index 15 | // and box index within that shelf. Illustration: 16 | // 17 | // customer indexes: 18 | // shelf 0: 0 19 | // shelf 1: 1 2 20 | // shelf 2: 3 4 5 6 21 | // shelf 3: 7 8 9 10 11 12 13 14 22 | // shelf 4: 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 23 | // shelf 5: 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 24 | // ... 25 | // 26 | // warehouse indexes: 27 | // shelf 0: 0 28 | // shelf 1: 0 1 29 | // shelf 2: 0 1 2 3 30 | // shelf 3: 0 1 2 3 4 5 6 7 31 | // shelf 4: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 32 | // shelf 5: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 33 | // ... 34 | // 35 | // With this arrangement, here are the equations to get the shelf index and 36 | // box index based on customer box index: 37 | // 38 | // shelf_index = floor(log2(customer_index + 1)) 39 | // shelf_count = ceil(log2(box_count + 1)) 40 | // box_index = customer_index + 1 - 2 ** shelf 41 | // shelf_size = 2 ** shelf_index 42 | // 43 | // Now we complicate it a little bit further by adding a preallocated shelf, which must be 44 | // a power of 2: 45 | // prealloc=4 46 | // 47 | // customer indexes: 48 | // prealloc: 0 1 2 3 49 | // shelf 0: 4 5 6 7 8 9 10 11 50 | // shelf 1: 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 51 | // shelf 2: 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 52 | // ... 53 | // 54 | // warehouse indexes: 55 | // prealloc: 0 1 2 3 56 | // shelf 0: 0 1 2 3 4 5 6 7 57 | // shelf 1: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 58 | // shelf 2: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 59 | // ... 60 | // 61 | // Now the equations are: 62 | // 63 | // shelf_index = floor(log2(customer_index + prealloc)) - log2(prealloc) - 1 64 | // shelf_count = ceil(log2(box_count + prealloc)) - log2(prealloc) - 1 65 | // box_index = customer_index + prealloc - 2 ** (log2(prealloc) + 1 + shelf) 66 | // shelf_size = prealloc * 2 ** (shelf_index + 1) 67 | 68 | /// This is a stack data structure where pointers to indexes have the same lifetime as the data structure 69 | /// itself, unlike ArrayList where push() invalidates all existing element pointers. 70 | /// The tradeoff is that elements are not guaranteed to be contiguous. For that, use ArrayList. 71 | /// Note however that most elements are contiguous, making this data structure cache-friendly. 72 | /// 73 | /// Because it never has to copy elements from an old location to a new location, it does not require 74 | /// its elements to be copyable, and it avoids wasting memory when backed by an ArenaAllocator. 75 | /// Note that the push() and pop() convenience methods perform a copy, but you can instead use 76 | /// addOne(), at(), setCapacity(), and shrinkCapacity() to avoid copying items. 77 | /// 78 | /// This data structure has O(1) push and O(1) pop. 79 | /// 80 | /// It supports preallocated elements, making it especially well suited when the expected maximum 81 | /// size is small. `prealloc_item_count` must be 0, or a power of 2. 82 | pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type { 83 | return struct { 84 | const Self = @This(); 85 | const ShelfIndex = std.math.Log2Int(usize); 86 | 87 | const prealloc_exp: ShelfIndex = blk: { 88 | // we don't use the prealloc_exp constant when prealloc_item_count is 0 89 | // but lazy-init may still be triggered by other code so supply a value 90 | if (prealloc_item_count == 0) { 91 | break :blk 0; 92 | } else { 93 | assert(std.math.isPowerOfTwo(prealloc_item_count)); 94 | const value = std.math.log2_int(usize, prealloc_item_count); 95 | break :blk value; 96 | } 97 | }; 98 | 99 | prealloc_segment: [prealloc_item_count]T, 100 | dynamic_segments: [][*]T, 101 | allocator: *Allocator, 102 | len: usize, 103 | 104 | pub const prealloc_count = prealloc_item_count; 105 | 106 | fn AtType(comptime SelfType: type) type { 107 | if (@typeInfo(SelfType).Pointer.is_const) { 108 | return *const T; 109 | } else { 110 | return *T; 111 | } 112 | } 113 | 114 | /// Deinitialize with `deinit` 115 | pub fn init(allocator: *Allocator) Self { 116 | return Self{ 117 | .allocator = allocator, 118 | .len = 0, 119 | .prealloc_segment = undefined, 120 | .dynamic_segments = &[_][*]T{}, 121 | }; 122 | } 123 | 124 | pub fn deinit(self: *Self) void { 125 | self.freeShelves(@intCast(ShelfIndex, self.dynamic_segments.len), 0); 126 | self.allocator.free(self.dynamic_segments); 127 | self.* = undefined; 128 | } 129 | 130 | pub fn at(self: anytype, i: usize) AtType(@TypeOf(self)) { 131 | assert(i < self.len); 132 | return self.uncheckedAt(i); 133 | } 134 | 135 | pub fn count(self: Self) usize { 136 | return self.len; 137 | } 138 | 139 | pub fn push(self: *Self, item: T) !void { 140 | const new_item_ptr = try self.addOne(); 141 | new_item_ptr.* = item; 142 | } 143 | 144 | pub fn pushMany(self: *Self, items: []const T) !void { 145 | for (items) |item| { 146 | try self.push(item); 147 | } 148 | } 149 | 150 | pub fn pop(self: *Self) ?T { 151 | if (self.len == 0) return null; 152 | 153 | const index = self.len - 1; 154 | const result = uncheckedAt(self, index).*; 155 | self.len = index; 156 | return result; 157 | } 158 | 159 | pub fn addOne(self: *Self) !*T { 160 | const new_length = self.len + 1; 161 | try self.growCapacity(new_length); 162 | const result = uncheckedAt(self, self.len); 163 | self.len = new_length; 164 | return result; 165 | } 166 | 167 | /// Grows or shrinks capacity to match usage. 168 | pub fn setCapacity(self: *Self, new_capacity: usize) !void { 169 | if (prealloc_item_count != 0) { 170 | if (new_capacity <= @as(usize, 1) << (prealloc_exp + @intCast(ShelfIndex, self.dynamic_segments.len))) { 171 | return self.shrinkCapacity(new_capacity); 172 | } 173 | } 174 | return self.growCapacity(new_capacity); 175 | } 176 | 177 | /// Only grows capacity, or retains current capacity 178 | pub fn growCapacity(self: *Self, new_capacity: usize) !void { 179 | const new_cap_shelf_count = shelfCount(new_capacity); 180 | const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); 181 | if (new_cap_shelf_count > old_shelf_count) { 182 | self.dynamic_segments = try self.allocator.realloc(self.dynamic_segments, new_cap_shelf_count); 183 | var i = old_shelf_count; 184 | errdefer { 185 | self.freeShelves(i, old_shelf_count); 186 | self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, old_shelf_count); 187 | } 188 | while (i < new_cap_shelf_count) : (i += 1) { 189 | self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr; 190 | } 191 | } 192 | } 193 | 194 | /// Only shrinks capacity or retains current capacity 195 | pub fn shrinkCapacity(self: *Self, new_capacity: usize) void { 196 | if (new_capacity <= prealloc_item_count) { 197 | const len = @intCast(ShelfIndex, self.dynamic_segments.len); 198 | self.freeShelves(len, 0); 199 | self.allocator.free(self.dynamic_segments); 200 | self.dynamic_segments = &[_][*]T{}; 201 | return; 202 | } 203 | 204 | const new_cap_shelf_count = shelfCount(new_capacity); 205 | const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); 206 | assert(new_cap_shelf_count <= old_shelf_count); 207 | if (new_cap_shelf_count == old_shelf_count) { 208 | return; 209 | } 210 | 211 | self.freeShelves(old_shelf_count, new_cap_shelf_count); 212 | self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, new_cap_shelf_count); 213 | } 214 | 215 | pub fn shrink(self: *Self, new_len: usize) void { 216 | assert(new_len <= self.len); 217 | // TODO take advantage of the new realloc semantics 218 | self.len = new_len; 219 | } 220 | 221 | pub fn writeToSlice(self: *Self, dest: []T, start: usize) void { 222 | const end = start + dest.len; 223 | assert(end <= self.len); 224 | 225 | var i = start; 226 | if (end <= prealloc_item_count) { 227 | std.mem.copy(T, dest[i - start ..], self.prealloc_segment[i..end]); 228 | return; 229 | } else if (i < prealloc_item_count) { 230 | std.mem.copy(T, dest[i - start ..], self.prealloc_segment[i..]); 231 | i = prealloc_item_count; 232 | } 233 | 234 | while (i < end) { 235 | const shelf_index = shelfIndex(i); 236 | const copy_start = boxIndex(i, shelf_index); 237 | const copy_end = std.math.min(shelfSize(shelf_index), copy_start + end - i); 238 | 239 | std.mem.copy( 240 | T, 241 | dest[i - start ..], 242 | self.dynamic_segments[shelf_index][copy_start..copy_end], 243 | ); 244 | 245 | i += (copy_end - copy_start); 246 | } 247 | } 248 | 249 | pub fn uncheckedAt(self: anytype, index: usize) AtType(@TypeOf(self)) { 250 | if (index < prealloc_item_count) { 251 | return &self.prealloc_segment[index]; 252 | } 253 | const shelf_index = shelfIndex(index); 254 | const box_index = boxIndex(index, shelf_index); 255 | return &self.dynamic_segments[shelf_index][box_index]; 256 | } 257 | 258 | fn shelfCount(box_count: usize) ShelfIndex { 259 | if (prealloc_item_count == 0) { 260 | return std.math.log2_int_ceil(usize, box_count + 1); 261 | } 262 | return std.math.log2_int_ceil(usize, box_count + prealloc_item_count) - prealloc_exp - 1; 263 | } 264 | 265 | fn shelfSize(shelf_index: ShelfIndex) usize { 266 | if (prealloc_item_count == 0) { 267 | return @as(usize, 1) << shelf_index; 268 | } 269 | return @as(usize, 1) << (shelf_index + (prealloc_exp + 1)); 270 | } 271 | 272 | fn shelfIndex(list_index: usize) ShelfIndex { 273 | if (prealloc_item_count == 0) { 274 | return std.math.log2_int(usize, list_index + 1); 275 | } 276 | return std.math.log2_int(usize, list_index + prealloc_item_count) - prealloc_exp - 1; 277 | } 278 | 279 | fn boxIndex(list_index: usize, shelf_index: ShelfIndex) usize { 280 | if (prealloc_item_count == 0) { 281 | return (list_index + 1) - (@as(usize, 1) << shelf_index); 282 | } 283 | return list_index + prealloc_item_count - (@as(usize, 1) << ((prealloc_exp + 1) + shelf_index)); 284 | } 285 | 286 | fn freeShelves(self: *Self, from_count: ShelfIndex, to_count: ShelfIndex) void { 287 | var i = from_count; 288 | while (i != to_count) { 289 | i -= 1; 290 | self.allocator.free(self.dynamic_segments[i][0..shelfSize(i)]); 291 | } 292 | } 293 | 294 | pub const Iterator = struct { 295 | list: *Self, 296 | index: usize, 297 | box_index: usize, 298 | shelf_index: ShelfIndex, 299 | shelf_size: usize, 300 | 301 | pub fn next(it: *Iterator) ?*T { 302 | if (it.index >= it.list.len) return null; 303 | if (it.index < prealloc_item_count) { 304 | const ptr = &it.list.prealloc_segment[it.index]; 305 | it.index += 1; 306 | if (it.index == prealloc_item_count) { 307 | it.box_index = 0; 308 | it.shelf_index = 0; 309 | it.shelf_size = prealloc_item_count * 2; 310 | } 311 | return ptr; 312 | } 313 | 314 | const ptr = &it.list.dynamic_segments[it.shelf_index][it.box_index]; 315 | it.index += 1; 316 | it.box_index += 1; 317 | if (it.box_index == it.shelf_size) { 318 | it.shelf_index += 1; 319 | it.box_index = 0; 320 | it.shelf_size *= 2; 321 | } 322 | return ptr; 323 | } 324 | 325 | pub fn prev(it: *Iterator) ?*T { 326 | if (it.index == 0) return null; 327 | 328 | it.index -= 1; 329 | if (it.index < prealloc_item_count) return &it.list.prealloc_segment[it.index]; 330 | 331 | if (it.box_index == 0) { 332 | it.shelf_index -= 1; 333 | it.shelf_size /= 2; 334 | it.box_index = it.shelf_size - 1; 335 | } else { 336 | it.box_index -= 1; 337 | } 338 | 339 | return &it.list.dynamic_segments[it.shelf_index][it.box_index]; 340 | } 341 | 342 | pub fn peek(it: *Iterator) ?*T { 343 | if (it.index >= it.list.len) 344 | return null; 345 | if (it.index < prealloc_item_count) 346 | return &it.list.prealloc_segment[it.index]; 347 | 348 | return &it.list.dynamic_segments[it.shelf_index][it.box_index]; 349 | } 350 | 351 | pub fn set(it: *Iterator, index: usize) void { 352 | it.index = index; 353 | if (index < prealloc_item_count) return; 354 | it.shelf_index = shelfIndex(index); 355 | it.box_index = boxIndex(index, it.shelf_index); 356 | it.shelf_size = shelfSize(it.shelf_index); 357 | } 358 | }; 359 | 360 | pub fn iterator(self: *Self, start_index: usize) Iterator { 361 | var it = Iterator{ 362 | .list = self, 363 | .index = undefined, 364 | .shelf_index = undefined, 365 | .box_index = undefined, 366 | .shelf_size = undefined, 367 | }; 368 | it.set(start_index); 369 | return it; 370 | } 371 | }; 372 | } 373 | 374 | test "std.SegmentedList" { 375 | var a = std.testing.allocator; 376 | 377 | try testSegmentedList(0, a); 378 | try testSegmentedList(1, a); 379 | try testSegmentedList(2, a); 380 | try testSegmentedList(4, a); 381 | try testSegmentedList(8, a); 382 | try testSegmentedList(16, a); 383 | } 384 | 385 | fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { 386 | var list = SegmentedList(i32, prealloc).init(allocator); 387 | defer list.deinit(); 388 | 389 | { 390 | var i: usize = 0; 391 | while (i < 100) : (i += 1) { 392 | try list.push(@intCast(i32, i + 1)); 393 | testing.expect(list.len == i + 1); 394 | } 395 | } 396 | 397 | { 398 | var i: usize = 0; 399 | while (i < 100) : (i += 1) { 400 | testing.expect(list.at(i).* == @intCast(i32, i + 1)); 401 | } 402 | } 403 | 404 | { 405 | var it = list.iterator(0); 406 | var x: i32 = 0; 407 | while (it.next()) |item| { 408 | x += 1; 409 | testing.expect(item.* == x); 410 | } 411 | testing.expect(x == 100); 412 | while (it.prev()) |item| : (x -= 1) { 413 | testing.expect(item.* == x); 414 | } 415 | testing.expect(x == 0); 416 | } 417 | 418 | testing.expect(list.pop().? == 100); 419 | testing.expect(list.len == 99); 420 | 421 | try list.pushMany(&[_]i32{ 1, 2, 3 }); 422 | testing.expect(list.len == 102); 423 | testing.expect(list.pop().? == 3); 424 | testing.expect(list.pop().? == 2); 425 | testing.expect(list.pop().? == 1); 426 | testing.expect(list.len == 99); 427 | 428 | try list.pushMany(&[_]i32{}); 429 | testing.expect(list.len == 99); 430 | 431 | { 432 | var i: i32 = 99; 433 | while (list.pop()) |item| : (i -= 1) { 434 | testing.expect(item == i); 435 | list.shrinkCapacity(list.len); 436 | } 437 | } 438 | 439 | { 440 | var control: [100]i32 = undefined; 441 | var dest: [100]i32 = undefined; 442 | 443 | var i: i32 = 0; 444 | while (i < 100) : (i += 1) { 445 | try list.push(i + 1); 446 | control[@intCast(usize, i)] = i + 1; 447 | } 448 | 449 | std.mem.set(i32, dest[0..], 0); 450 | list.writeToSlice(dest[0..], 0); 451 | testing.expect(std.mem.eql(i32, control[0..], dest[0..])); 452 | 453 | std.mem.set(i32, dest[0..], 0); 454 | list.writeToSlice(dest[50..], 50); 455 | testing.expect(std.mem.eql(i32, control[50..], dest[50..])); 456 | } 457 | 458 | try list.setCapacity(0); 459 | } 460 | -------------------------------------------------------------------------------- /std/rb.zig: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2015-2020 Zig Contributors 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. 4 | // The MIT license requires this copyright notice to be included in all copies 5 | // and substantial portions of the software. 6 | const std = @import("std"); 7 | const assert = std.debug.assert; 8 | const testing = std.testing; 9 | const Order = std.math.Order; 10 | 11 | const Color = enum(u1) { 12 | Black, 13 | Red, 14 | }; 15 | const Red = Color.Red; 16 | const Black = Color.Black; 17 | 18 | const ReplaceError = error{NotEqual}; 19 | const SortError = error{NotUnique}; // The new comparison function results in duplicates. 20 | 21 | /// Insert this into your struct that you want to add to a red-black tree. 22 | /// Do not use a pointer. Turn the *rb.Node results of the functions in rb 23 | /// (after resolving optionals) to your structure using @fieldParentPtr(). Example: 24 | /// 25 | /// const Number = struct { 26 | /// node: rb.Node, 27 | /// value: i32, 28 | /// }; 29 | /// fn number(node: *rb.Node) Number { 30 | /// return @fieldParentPtr(Number, "node", node); 31 | /// } 32 | pub const Node = struct { 33 | left: ?*Node, 34 | right: ?*Node, 35 | 36 | /// parent | color 37 | parent_and_color: usize, 38 | 39 | pub fn next(constnode: *Node) ?*Node { 40 | var node = constnode; 41 | 42 | if (node.right) |right| { 43 | var n = right; 44 | while (n.left) |left| 45 | n = left; 46 | return n; 47 | } 48 | 49 | while (true) { 50 | var parent = node.getParent(); 51 | if (parent) |p| { 52 | if (node != p.right) 53 | return p; 54 | node = p; 55 | } else 56 | return null; 57 | } 58 | } 59 | 60 | pub fn prev(constnode: *Node) ?*Node { 61 | var node = constnode; 62 | 63 | if (node.left) |left| { 64 | var n = left; 65 | while (n.right) |right| 66 | n = right; 67 | return n; 68 | } 69 | 70 | while (true) { 71 | var parent = node.getParent(); 72 | if (parent) |p| { 73 | if (node != p.left) 74 | return p; 75 | node = p; 76 | } else 77 | return null; 78 | } 79 | } 80 | 81 | pub fn isRoot(node: *Node) bool { 82 | return node.getParent() == null; 83 | } 84 | 85 | fn isRed(node: *Node) bool { 86 | return node.getColor() == Red; 87 | } 88 | 89 | fn isBlack(node: *Node) bool { 90 | return node.getColor() == Black; 91 | } 92 | 93 | fn setParent(node: *Node, parent: ?*Node) void { 94 | node.parent_and_color = @ptrToInt(parent) | (node.parent_and_color & 1); 95 | } 96 | 97 | fn getParent(node: *Node) ?*Node { 98 | const mask: usize = 1; 99 | comptime { 100 | assert(@alignOf(*Node) >= 2); 101 | } 102 | const maybe_ptr = node.parent_and_color & ~mask; 103 | return if (maybe_ptr == 0) null else @intToPtr(*Node, maybe_ptr); 104 | } 105 | 106 | fn setColor(node: *Node, color: Color) void { 107 | const mask: usize = 1; 108 | node.parent_and_color = (node.parent_and_color & ~mask) | @enumToInt(color); 109 | } 110 | 111 | fn getColor(node: *Node) Color { 112 | return @intToEnum(Color, @intCast(u1, node.parent_and_color & 1)); 113 | } 114 | 115 | fn setChild(node: *Node, child: ?*Node, is_left: bool) void { 116 | if (is_left) { 117 | node.left = child; 118 | } else { 119 | node.right = child; 120 | } 121 | } 122 | 123 | fn getFirst(nodeconst: *Node) *Node { 124 | var node = nodeconst; 125 | while (node.left) |left| { 126 | node = left; 127 | } 128 | return node; 129 | } 130 | 131 | fn getLast(nodeconst: *Node) *Node { 132 | var node = nodeconst; 133 | while (node.right) |right| { 134 | node = right; 135 | } 136 | return node; 137 | } 138 | }; 139 | 140 | pub const Tree = struct { 141 | root: ?*Node, 142 | compareFn: fn (*Node, *Node, *Tree) Order, 143 | 144 | /// Re-sorts a tree with a new compare function 145 | pub fn sort(tree: *Tree, newCompareFn: fn (*Node, *Node, *Tree) Order) SortError!void { 146 | var newTree = Tree.init(newCompareFn); 147 | var node: *Node = undefined; 148 | while (true) { 149 | node = tree.first() orelse break; 150 | tree.remove(node); 151 | if (newTree.insert(node) != null) { 152 | return error.NotUnique; // EEXISTS 153 | } 154 | } 155 | tree.* = newTree; 156 | } 157 | 158 | /// If you have a need for a version that caches this, please file a bug. 159 | pub fn first(tree: *Tree) ?*Node { 160 | var node: *Node = tree.root orelse return null; 161 | 162 | while (node.left) |left| { 163 | node = left; 164 | } 165 | 166 | return node; 167 | } 168 | 169 | pub fn last(tree: *Tree) ?*Node { 170 | var node: *Node = tree.root orelse return null; 171 | 172 | while (node.right) |right| { 173 | node = right; 174 | } 175 | 176 | return node; 177 | } 178 | 179 | /// Duplicate keys are not allowed. The item with the same key already in the 180 | /// tree will be returned, and the item will not be inserted. 181 | pub fn insert(tree: *Tree, node_const: *Node) ?*Node { 182 | var node = node_const; 183 | var maybe_key: ?*Node = undefined; 184 | var maybe_parent: ?*Node = undefined; 185 | var is_left: bool = undefined; 186 | 187 | maybe_key = doLookup(node, tree, &maybe_parent, &is_left); 188 | if (maybe_key) |key| { 189 | return key; 190 | } 191 | 192 | node.left = null; 193 | node.right = null; 194 | node.setColor(Red); 195 | node.setParent(maybe_parent); 196 | 197 | if (maybe_parent) |parent| { 198 | parent.setChild(node, is_left); 199 | } else { 200 | tree.root = node; 201 | } 202 | 203 | while (node.getParent()) |*parent| { 204 | if (parent.*.isBlack()) 205 | break; 206 | // the root is always black 207 | var grandpa = parent.*.getParent() orelse unreachable; 208 | 209 | if (parent.* == grandpa.left) { 210 | var maybe_uncle = grandpa.right; 211 | 212 | if (maybe_uncle) |uncle| { 213 | if (uncle.isBlack()) 214 | break; 215 | 216 | parent.*.setColor(Black); 217 | uncle.setColor(Black); 218 | grandpa.setColor(Red); 219 | node = grandpa; 220 | } else { 221 | if (node == parent.*.right) { 222 | rotateLeft(parent.*, tree); 223 | node = parent.*; 224 | parent.* = node.getParent().?; // Just rotated 225 | } 226 | parent.*.setColor(Black); 227 | grandpa.setColor(Red); 228 | rotateRight(grandpa, tree); 229 | } 230 | } else { 231 | var maybe_uncle = grandpa.left; 232 | 233 | if (maybe_uncle) |uncle| { 234 | if (uncle.isBlack()) 235 | break; 236 | 237 | parent.*.setColor(Black); 238 | uncle.setColor(Black); 239 | grandpa.setColor(Red); 240 | node = grandpa; 241 | } else { 242 | if (node == parent.*.left) { 243 | rotateRight(parent.*, tree); 244 | node = parent.*; 245 | parent.* = node.getParent().?; // Just rotated 246 | } 247 | parent.*.setColor(Black); 248 | grandpa.setColor(Red); 249 | rotateLeft(grandpa, tree); 250 | } 251 | } 252 | } 253 | // This was an insert, there is at least one node. 254 | tree.root.?.setColor(Black); 255 | return null; 256 | } 257 | 258 | /// lookup searches for the value of key, using binary search. It will 259 | /// return a pointer to the node if it is there, otherwise it will return null. 260 | /// Complexity guaranteed O(log n), where n is the number of nodes book-kept 261 | /// by tree. 262 | pub fn lookup(tree: *Tree, key: *Node) ?*Node { 263 | var parent: ?*Node = undefined; 264 | var is_left: bool = undefined; 265 | return doLookup(key, tree, &parent, &is_left); 266 | } 267 | 268 | /// If node is not part of tree, behavior is undefined. 269 | pub fn remove(tree: *Tree, nodeconst: *Node) void { 270 | var node = nodeconst; 271 | // as this has the same value as node, it is unsafe to access node after newnode 272 | var newnode: ?*Node = nodeconst; 273 | var maybe_parent: ?*Node = node.getParent(); 274 | var color: Color = undefined; 275 | var next: *Node = undefined; 276 | 277 | // This clause is to avoid optionals 278 | if (node.left == null and node.right == null) { 279 | if (maybe_parent) |parent| { 280 | parent.setChild(null, parent.left == node); 281 | } else 282 | tree.root = null; 283 | color = node.getColor(); 284 | newnode = null; 285 | } else { 286 | if (node.left == null) { 287 | next = node.right.?; // Not both null as per above 288 | } else if (node.right == null) { 289 | next = node.left.?; // Not both null as per above 290 | } else 291 | next = node.right.?.getFirst(); // Just checked for null above 292 | 293 | if (maybe_parent) |parent| { 294 | parent.setChild(next, parent.left == node); 295 | } else 296 | tree.root = next; 297 | 298 | if (node.left != null and node.right != null) { 299 | const left = node.left.?; 300 | const right = node.right.?; 301 | 302 | color = next.getColor(); 303 | next.setColor(node.getColor()); 304 | 305 | next.left = left; 306 | left.setParent(next); 307 | 308 | if (next != right) { 309 | var parent = next.getParent().?; // Was traversed via child node (right/left) 310 | next.setParent(node.getParent()); 311 | 312 | newnode = next.right; 313 | parent.left = node; 314 | 315 | next.right = right; 316 | right.setParent(next); 317 | } else { 318 | next.setParent(maybe_parent); 319 | maybe_parent = next; 320 | newnode = next.right; 321 | } 322 | } else { 323 | color = node.getColor(); 324 | newnode = next; 325 | } 326 | } 327 | 328 | if (newnode) |n| 329 | n.setParent(maybe_parent); 330 | 331 | if (color == Red) 332 | return; 333 | if (newnode) |n| { 334 | n.setColor(Black); 335 | return; 336 | } 337 | 338 | while (node == tree.root) { 339 | // If not root, there must be parent 340 | var parent = maybe_parent.?; 341 | if (node == parent.left) { 342 | var sibling = parent.right.?; // Same number of black nodes. 343 | 344 | if (sibling.isRed()) { 345 | sibling.setColor(Black); 346 | parent.setColor(Red); 347 | rotateLeft(parent, tree); 348 | sibling = parent.right.?; // Just rotated 349 | } 350 | if ((if (sibling.left) |n| n.isBlack() else true) and 351 | (if (sibling.right) |n| n.isBlack() else true)) 352 | { 353 | sibling.setColor(Red); 354 | node = parent; 355 | maybe_parent = parent.getParent(); 356 | continue; 357 | } 358 | if (if (sibling.right) |n| n.isBlack() else true) { 359 | sibling.left.?.setColor(Black); // Same number of black nodes. 360 | sibling.setColor(Red); 361 | rotateRight(sibling, tree); 362 | sibling = parent.right.?; // Just rotated 363 | } 364 | sibling.setColor(parent.getColor()); 365 | parent.setColor(Black); 366 | sibling.right.?.setColor(Black); // Same number of black nodes. 367 | rotateLeft(parent, tree); 368 | newnode = tree.root; 369 | break; 370 | } else { 371 | var sibling = parent.left.?; // Same number of black nodes. 372 | 373 | if (sibling.isRed()) { 374 | sibling.setColor(Black); 375 | parent.setColor(Red); 376 | rotateRight(parent, tree); 377 | sibling = parent.left.?; // Just rotated 378 | } 379 | if ((if (sibling.left) |n| n.isBlack() else true) and 380 | (if (sibling.right) |n| n.isBlack() else true)) 381 | { 382 | sibling.setColor(Red); 383 | node = parent; 384 | maybe_parent = parent.getParent(); 385 | continue; 386 | } 387 | if (if (sibling.left) |n| n.isBlack() else true) { 388 | sibling.right.?.setColor(Black); // Same number of black nodes 389 | sibling.setColor(Red); 390 | rotateLeft(sibling, tree); 391 | sibling = parent.left.?; // Just rotated 392 | } 393 | sibling.setColor(parent.getColor()); 394 | parent.setColor(Black); 395 | sibling.left.?.setColor(Black); // Same number of black nodes 396 | rotateRight(parent, tree); 397 | newnode = tree.root; 398 | break; 399 | } 400 | 401 | if (node.isRed()) 402 | break; 403 | } 404 | 405 | if (newnode) |n| 406 | n.setColor(Black); 407 | } 408 | 409 | /// This is a shortcut to avoid removing and re-inserting an item with the same key. 410 | pub fn replace(tree: *Tree, old: *Node, newconst: *Node) !void { 411 | var new = newconst; 412 | 413 | // I assume this can get optimized out if the caller already knows. 414 | if (tree.compareFn(old, new, tree) != .eq) return ReplaceError.NotEqual; 415 | 416 | if (old.getParent()) |parent| { 417 | parent.setChild(new, parent.left == old); 418 | } else 419 | tree.root = new; 420 | 421 | if (old.left) |left| 422 | left.setParent(new); 423 | if (old.right) |right| 424 | right.setParent(new); 425 | 426 | new.* = old.*; 427 | } 428 | 429 | pub fn init(f: fn (*Node, *Node, *Tree) Order) Tree { 430 | return Tree{ 431 | .root = null, 432 | .compareFn = f, 433 | }; 434 | } 435 | }; 436 | 437 | fn rotateLeft(node: *Node, tree: *Tree) void { 438 | var p: *Node = node; 439 | var q: *Node = node.right orelse unreachable; 440 | var parent: *Node = undefined; 441 | 442 | if (!p.isRoot()) { 443 | parent = p.getParent().?; 444 | if (parent.left == p) { 445 | parent.left = q; 446 | } else { 447 | parent.right = q; 448 | } 449 | q.setParent(parent); 450 | } else { 451 | tree.root = q; 452 | q.setParent(null); 453 | } 454 | p.setParent(q); 455 | 456 | p.right = q.left; 457 | if (p.right) |right| { 458 | right.setParent(p); 459 | } 460 | q.left = p; 461 | } 462 | 463 | fn rotateRight(node: *Node, tree: *Tree) void { 464 | var p: *Node = node; 465 | var q: *Node = node.left orelse unreachable; 466 | var parent: *Node = undefined; 467 | 468 | if (!p.isRoot()) { 469 | parent = p.getParent().?; 470 | if (parent.left == p) { 471 | parent.left = q; 472 | } else { 473 | parent.right = q; 474 | } 475 | q.setParent(parent); 476 | } else { 477 | tree.root = q; 478 | q.setParent(null); 479 | } 480 | p.setParent(q); 481 | 482 | p.left = q.right; 483 | if (p.left) |left| { 484 | left.setParent(p); 485 | } 486 | q.right = p; 487 | } 488 | 489 | fn doLookup(key: *Node, tree: *Tree, pparent: *?*Node, is_left: *bool) ?*Node { 490 | var maybe_node: ?*Node = tree.root; 491 | 492 | pparent.* = null; 493 | is_left.* = false; 494 | 495 | while (maybe_node) |node| { 496 | const res = tree.compareFn(node, key, tree); 497 | if (res == .eq) { 498 | return node; 499 | } 500 | pparent.* = node; 501 | switch (res) { 502 | .gt => { 503 | is_left.* = true; 504 | maybe_node = node.left; 505 | }, 506 | .lt => { 507 | is_left.* = false; 508 | maybe_node = node.right; 509 | }, 510 | .eq => unreachable, // handled above 511 | } 512 | } 513 | return null; 514 | } 515 | 516 | const testNumber = struct { 517 | node: Node, 518 | value: usize, 519 | }; 520 | 521 | fn testGetNumber(node: *Node) *testNumber { 522 | return @fieldParentPtr(testNumber, "node", node); 523 | } 524 | 525 | fn testCompare(l: *Node, r: *Node, contextIgnored: *Tree) Order { 526 | var left = testGetNumber(l); 527 | var right = testGetNumber(r); 528 | 529 | if (left.value < right.value) { 530 | return .lt; 531 | } else if (left.value == right.value) { 532 | return .eq; 533 | } else if (left.value > right.value) { 534 | return .gt; 535 | } 536 | unreachable; 537 | } 538 | 539 | fn testCompareReverse(l: *Node, r: *Node, contextIgnored: *Tree) Order { 540 | return testCompare(r, l, contextIgnored); 541 | } 542 | 543 | test "rb" { 544 | if (@import("builtin").arch == .aarch64) { 545 | // TODO https://github.com/ziglang/zig/issues/3288 546 | return error.SkipZigTest; 547 | } 548 | 549 | var tree = Tree.init(testCompare); 550 | var ns: [10]testNumber = undefined; 551 | ns[0].value = 42; 552 | ns[1].value = 41; 553 | ns[2].value = 40; 554 | ns[3].value = 39; 555 | ns[4].value = 38; 556 | ns[5].value = 39; 557 | ns[6].value = 3453; 558 | ns[7].value = 32345; 559 | ns[8].value = 392345; 560 | ns[9].value = 4; 561 | 562 | var dup: testNumber = undefined; 563 | dup.value = 32345; 564 | 565 | _ = tree.insert(&ns[1].node); 566 | _ = tree.insert(&ns[2].node); 567 | _ = tree.insert(&ns[3].node); 568 | _ = tree.insert(&ns[4].node); 569 | _ = tree.insert(&ns[5].node); 570 | _ = tree.insert(&ns[6].node); 571 | _ = tree.insert(&ns[7].node); 572 | _ = tree.insert(&ns[8].node); 573 | _ = tree.insert(&ns[9].node); 574 | tree.remove(&ns[3].node); 575 | testing.expect(tree.insert(&dup.node) == &ns[7].node); 576 | try tree.replace(&ns[7].node, &dup.node); 577 | 578 | var num: *testNumber = undefined; 579 | num = testGetNumber(tree.first().?); 580 | while (num.node.next() != null) { 581 | testing.expect(testGetNumber(num.node.next().?).value > num.value); 582 | num = testGetNumber(num.node.next().?); 583 | } 584 | } 585 | 586 | test "inserting and looking up" { 587 | var tree = Tree.init(testCompare); 588 | var number: testNumber = undefined; 589 | number.value = 1000; 590 | _ = tree.insert(&number.node); 591 | var dup: testNumber = undefined; 592 | //Assert that tuples with identical value fields finds the same pointer 593 | dup.value = 1000; 594 | assert(tree.lookup(&dup.node) == &number.node); 595 | //Assert that tuples with identical values do not clobber when inserted. 596 | _ = tree.insert(&dup.node); 597 | assert(tree.lookup(&dup.node) == &number.node); 598 | assert(tree.lookup(&number.node) != &dup.node); 599 | assert(testGetNumber(tree.lookup(&dup.node).?).value == testGetNumber(&dup.node).value); 600 | //Assert that if looking for a non-existing value, return null. 601 | var non_existing_value: testNumber = undefined; 602 | non_existing_value.value = 1234; 603 | assert(tree.lookup(&non_existing_value.node) == null); 604 | } 605 | 606 | test "multiple inserts, followed by calling first and last" { 607 | if (@import("builtin").arch == .aarch64) { 608 | // TODO https://github.com/ziglang/zig/issues/3288 609 | return error.SkipZigTest; 610 | } 611 | var tree = Tree.init(testCompare); 612 | var zeroth: testNumber = undefined; 613 | zeroth.value = 0; 614 | var first: testNumber = undefined; 615 | first.value = 1; 616 | var second: testNumber = undefined; 617 | second.value = 2; 618 | var third: testNumber = undefined; 619 | third.value = 3; 620 | _ = tree.insert(&zeroth.node); 621 | _ = tree.insert(&first.node); 622 | _ = tree.insert(&second.node); 623 | _ = tree.insert(&third.node); 624 | assert(testGetNumber(tree.first().?).value == 0); 625 | assert(testGetNumber(tree.last().?).value == 3); 626 | var lookupNode: testNumber = undefined; 627 | lookupNode.value = 3; 628 | assert(tree.lookup(&lookupNode.node) == &third.node); 629 | tree.sort(testCompareReverse) catch unreachable; 630 | assert(testGetNumber(tree.first().?).value == 3); 631 | assert(testGetNumber(tree.last().?).value == 0); 632 | assert(tree.lookup(&lookupNode.node) == &third.node); 633 | } 634 | -------------------------------------------------------------------------------- /std/http_headers.zig: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2015-2020 Zig Contributors 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. 4 | // The MIT license requires this copyright notice to be included in all copies 5 | // and substantial portions of the software. 6 | // HTTP Header data structure/type 7 | // Based on lua-http's http.header module 8 | // 9 | // Design criteria: 10 | // - the same header field is allowed more than once 11 | // - must be able to fetch separate occurrences (important for some headers e.g. Set-Cookie) 12 | // - optionally available as comma separated list 13 | // - http2 adds flag to headers that they should never be indexed 14 | // - header order should be recoverable 15 | // 16 | // Headers are implemented as an array of entries. 17 | // An index of field name => array indices is kept. 18 | 19 | const std = @import("std"); 20 | const debug = std.debug; 21 | const assert = debug.assert; 22 | const testing = std.testing; 23 | const mem = std.mem; 24 | const Allocator = mem.Allocator; 25 | 26 | fn never_index_default(name: []const u8) bool { 27 | if (mem.eql(u8, "authorization", name)) return true; 28 | if (mem.eql(u8, "proxy-authorization", name)) return true; 29 | if (mem.eql(u8, "cookie", name)) return true; 30 | if (mem.eql(u8, "set-cookie", name)) return true; 31 | return false; 32 | } 33 | 34 | const HeaderEntry = struct { 35 | name: []const u8, 36 | value: []u8, 37 | never_index: bool, 38 | 39 | const Self = @This(); 40 | 41 | fn init(allocator: *Allocator, name: []const u8, value: []const u8, never_index: ?bool) !Self { 42 | return Self{ 43 | .name = name, // takes reference 44 | .value = try allocator.dupe(u8, value), 45 | .never_index = never_index orelse never_index_default(name), 46 | }; 47 | } 48 | 49 | fn deinit(self: Self, allocator: *Allocator) void { 50 | allocator.free(self.value); 51 | } 52 | 53 | pub fn modify(self: *Self, allocator: *Allocator, value: []const u8, never_index: ?bool) !void { 54 | const old_len = self.value.len; 55 | if (value.len > old_len) { 56 | self.value = try allocator.realloc(self.value, value.len); 57 | } else if (value.len < old_len) { 58 | self.value = allocator.shrink(self.value, value.len); 59 | } 60 | mem.copy(u8, self.value, value); 61 | self.never_index = never_index orelse never_index_default(self.name); 62 | } 63 | 64 | fn compare(context: void, a: HeaderEntry, b: HeaderEntry) bool { 65 | if (a.name.ptr != b.name.ptr and a.name.len != b.name.len) { 66 | // Things beginning with a colon *must* be before others 67 | const a_is_colon = a.name[0] == ':'; 68 | const b_is_colon = b.name[0] == ':'; 69 | if (a_is_colon and !b_is_colon) { 70 | return true; 71 | } else if (!a_is_colon and b_is_colon) { 72 | return false; 73 | } 74 | 75 | // Sort lexicographically on header name 76 | return mem.order(u8, a.name, b.name) == .lt; 77 | } 78 | 79 | // Sort lexicographically on header value 80 | if (!mem.eql(u8, a.value, b.value)) { 81 | return mem.order(u8, a.value, b.value) == .lt; 82 | } 83 | 84 | // Doesn't matter here; need to pick something for sort consistency 85 | return a.never_index; 86 | } 87 | }; 88 | 89 | test "HeaderEntry" { 90 | var e = try HeaderEntry.init(testing.allocator, "foo", "bar", null); 91 | defer e.deinit(testing.allocator); 92 | testing.expectEqualSlices(u8, "foo", e.name); 93 | testing.expectEqualSlices(u8, "bar", e.value); 94 | testing.expectEqual(false, e.never_index); 95 | 96 | try e.modify(testing.allocator, "longer value", null); 97 | testing.expectEqualSlices(u8, "longer value", e.value); 98 | 99 | // shorter value 100 | try e.modify(testing.allocator, "x", null); 101 | testing.expectEqualSlices(u8, "x", e.value); 102 | } 103 | 104 | const HeaderList = std.ArrayListUnmanaged(HeaderEntry); 105 | const HeaderIndexList = std.ArrayListUnmanaged(usize); 106 | const HeaderIndex = std.StringHashMapUnmanaged(HeaderIndexList); 107 | 108 | pub const Headers = struct { 109 | // the owned header field name is stored in the index as part of the key 110 | allocator: *Allocator, 111 | data: HeaderList, 112 | index: HeaderIndex, 113 | 114 | const Self = @This(); 115 | 116 | pub fn init(allocator: *Allocator) Self { 117 | return Self{ 118 | .allocator = allocator, 119 | .data = HeaderList{}, 120 | .index = HeaderIndex{}, 121 | }; 122 | } 123 | 124 | pub fn deinit(self: *Self) void { 125 | { 126 | var it = self.index.iterator(); 127 | while (it.next()) |entry| { 128 | entry.value.deinit(self.allocator); 129 | self.allocator.free(entry.key); 130 | } 131 | self.index.deinit(self.allocator); 132 | } 133 | { 134 | for (self.data.items) |entry| { 135 | entry.deinit(self.allocator); 136 | } 137 | self.data.deinit(self.allocator); 138 | } 139 | self.* = undefined; 140 | } 141 | 142 | pub fn clone(self: Self, allocator: *Allocator) !Self { 143 | var other = Headers.init(allocator); 144 | errdefer other.deinit(); 145 | try other.data.ensureCapacity(allocator, self.data.items.len); 146 | try other.index.initCapacity(allocator, self.index.entries.len); 147 | for (self.data.items) |entry| { 148 | try other.append(entry.name, entry.value, entry.never_index); 149 | } 150 | return other; 151 | } 152 | 153 | pub fn toSlice(self: Self) []const HeaderEntry { 154 | return self.data.items; 155 | } 156 | 157 | pub fn append(self: *Self, name: []const u8, value: []const u8, never_index: ?bool) !void { 158 | const n = self.data.items.len + 1; 159 | try self.data.ensureCapacity(self.allocator, n); 160 | var entry: HeaderEntry = undefined; 161 | if (self.index.getEntry(name)) |kv| { 162 | entry = try HeaderEntry.init(self.allocator, kv.key, value, never_index); 163 | errdefer entry.deinit(self.allocator); 164 | const dex = &kv.value; 165 | try dex.append(self.allocator, n - 1); 166 | } else { 167 | const name_dup = try self.allocator.dupe(u8, name); 168 | errdefer self.allocator.free(name_dup); 169 | entry = try HeaderEntry.init(self.allocator, name_dup, value, never_index); 170 | errdefer entry.deinit(self.allocator); 171 | var dex = HeaderIndexList{}; 172 | try dex.append(self.allocator, n - 1); 173 | errdefer dex.deinit(self.allocator); 174 | _ = try self.index.put(self.allocator, name_dup, dex); 175 | } 176 | self.data.appendAssumeCapacity(entry); 177 | } 178 | 179 | /// If the header already exists, replace the current value, otherwise append it to the list of headers. 180 | /// If the header has multiple entries then returns an error. 181 | pub fn upsert(self: *Self, name: []const u8, value: []const u8, never_index: ?bool) !void { 182 | if (self.index.get(name)) |kv| { 183 | const dex = kv.value; 184 | if (dex.len != 1) 185 | return error.CannotUpsertMultiValuedField; 186 | var e = &self.data.at(dex.at(0)); 187 | try e.modify(value, never_index); 188 | } else { 189 | try self.append(name, value, never_index); 190 | } 191 | } 192 | 193 | /// Returns boolean indicating if the field is present. 194 | pub fn contains(self: Self, name: []const u8) bool { 195 | return self.index.contains(name); 196 | } 197 | 198 | /// Returns boolean indicating if something was deleted. 199 | pub fn delete(self: *Self, name: []const u8) bool { 200 | if (self.index.remove(name)) |*kv| { 201 | const dex = &kv.value; 202 | // iterate backwards 203 | var i = dex.items.len; 204 | while (i > 0) { 205 | i -= 1; 206 | const data_index = dex.items[i]; 207 | const removed = self.data.orderedRemove(data_index); 208 | assert(mem.eql(u8, removed.name, name)); 209 | removed.deinit(self.allocator); 210 | } 211 | dex.deinit(self.allocator); 212 | self.allocator.free(kv.key); 213 | self.rebuildIndex(); 214 | return true; 215 | } else { 216 | return false; 217 | } 218 | } 219 | 220 | /// Removes the element at the specified index. 221 | /// Moves items down to fill the empty space. 222 | /// TODO this implementation can be replaced by adding 223 | /// orderedRemove to the new hash table implementation as an 224 | /// alternative to swapRemove. 225 | pub fn orderedRemove(self: *Self, i: usize) void { 226 | const removed = self.data.orderedRemove(i); 227 | const kv = self.index.getEntry(removed.name).?; 228 | const dex = &kv.value; 229 | if (dex.items.len == 1) { 230 | // was last item; delete the index 231 | dex.deinit(self.allocator); 232 | removed.deinit(self.allocator); 233 | const key = kv.key; 234 | _ = self.index.remove(key); // invalidates `kv` and `dex` 235 | self.allocator.free(key); 236 | } else { 237 | dex.shrinkAndFree(self.allocator, dex.items.len - 1); 238 | removed.deinit(self.allocator); 239 | } 240 | // if it was the last item; no need to rebuild index 241 | if (i != self.data.items.len) { 242 | self.rebuildIndex(); 243 | } 244 | } 245 | 246 | /// Removes the element at the specified index. 247 | /// The empty slot is filled from the end of the list. 248 | /// TODO this implementation can be replaced by simply using the 249 | /// new hash table which does swap removal. 250 | pub fn swapRemove(self: *Self, i: usize) void { 251 | const removed = self.data.swapRemove(i); 252 | const kv = self.index.getEntry(removed.name).?; 253 | const dex = &kv.value; 254 | if (dex.items.len == 1) { 255 | // was last item; delete the index 256 | dex.deinit(self.allocator); 257 | removed.deinit(self.allocator); 258 | const key = kv.key; 259 | _ = self.index.remove(key); // invalidates `kv` and `dex` 260 | self.allocator.free(key); 261 | } else { 262 | dex.shrinkAndFree(self.allocator, dex.items.len - 1); 263 | removed.deinit(self.allocator); 264 | } 265 | // if it was the last item; no need to rebuild index 266 | if (i != self.data.items.len) { 267 | self.rebuildIndex(); 268 | } 269 | } 270 | 271 | /// Access the header at the specified index. 272 | pub fn at(self: Self, i: usize) HeaderEntry { 273 | return self.data.items[i]; 274 | } 275 | 276 | /// Returns a list of indices containing headers with the given name. 277 | /// The returned list should not be modified by the caller. 278 | pub fn getIndices(self: Self, name: []const u8) ?HeaderIndexList { 279 | return self.index.get(name); 280 | } 281 | 282 | /// Returns a slice containing each header with the given name. 283 | pub fn get(self: Self, allocator: *Allocator, name: []const u8) !?[]const HeaderEntry { 284 | const dex = self.getIndices(name) orelse return null; 285 | 286 | const buf = try allocator.alloc(HeaderEntry, dex.items.len); 287 | var n: usize = 0; 288 | for (dex.items) |idx| { 289 | buf[n] = self.data.items[idx]; 290 | n += 1; 291 | } 292 | return buf; 293 | } 294 | 295 | /// Returns all headers with the given name as a comma separated string. 296 | /// 297 | /// Useful for HTTP headers that follow RFC-7230 section 3.2.2: 298 | /// A recipient MAY combine multiple header fields with the same field 299 | /// name into one "field-name: field-value" pair, without changing the 300 | /// semantics of the message, by appending each subsequent field value to 301 | /// the combined field value in order, separated by a comma. The order 302 | /// in which header fields with the same field name are received is 303 | /// therefore significant to the interpretation of the combined field 304 | /// value 305 | pub fn getCommaSeparated(self: Self, allocator: *Allocator, name: []const u8) !?[]u8 { 306 | const dex = self.getIndices(name) orelse return null; 307 | 308 | // adapted from mem.join 309 | const total_len = blk: { 310 | var sum: usize = dex.items.len - 1; // space for separator(s) 311 | for (dex.items) |idx| 312 | sum += self.data.items[idx].value.len; 313 | break :blk sum; 314 | }; 315 | 316 | const buf = try allocator.alloc(u8, total_len); 317 | errdefer allocator.free(buf); 318 | 319 | const first_value = self.data.items[dex.items[0]].value; 320 | mem.copy(u8, buf, first_value); 321 | var buf_index: usize = first_value.len; 322 | for (dex.items[1..]) |idx| { 323 | const value = self.data.items[idx].value; 324 | buf[buf_index] = ','; 325 | buf_index += 1; 326 | mem.copy(u8, buf[buf_index..], value); 327 | buf_index += value.len; 328 | } 329 | 330 | // No need for shrink since buf is exactly the correct size. 331 | return buf; 332 | } 333 | 334 | fn rebuildIndex(self: *Self) void { 335 | // clear out the indexes 336 | var it = self.index.iterator(); 337 | while (it.next()) |entry| { 338 | entry.value.shrinkRetainingCapacity(0); 339 | } 340 | // fill up indexes again; we know capacity is fine from before 341 | for (self.data.items) |entry, i| { 342 | self.index.getEntry(entry.name).?.value.appendAssumeCapacity(i); 343 | } 344 | } 345 | 346 | pub fn sort(self: *Self) void { 347 | std.sort.sort(HeaderEntry, self.data.items, {}, HeaderEntry.compare); 348 | self.rebuildIndex(); 349 | } 350 | 351 | pub fn format( 352 | self: Self, 353 | comptime fmt: []const u8, 354 | options: std.fmt.FormatOptions, 355 | out_stream: anytype, 356 | ) !void { 357 | for (self.toSlice()) |entry| { 358 | try out_stream.writeAll(entry.name); 359 | try out_stream.writeAll(": "); 360 | try out_stream.writeAll(entry.value); 361 | try out_stream.writeAll("\n"); 362 | } 363 | } 364 | }; 365 | 366 | test "Headers.iterator" { 367 | var h = Headers.init(testing.allocator); 368 | defer h.deinit(); 369 | try h.append("foo", "bar", null); 370 | try h.append("cookie", "somevalue", null); 371 | 372 | var count: i32 = 0; 373 | for (h.toSlice()) |e| { 374 | if (count == 0) { 375 | testing.expectEqualSlices(u8, "foo", e.name); 376 | testing.expectEqualSlices(u8, "bar", e.value); 377 | testing.expectEqual(false, e.never_index); 378 | } else if (count == 1) { 379 | testing.expectEqualSlices(u8, "cookie", e.name); 380 | testing.expectEqualSlices(u8, "somevalue", e.value); 381 | testing.expectEqual(true, e.never_index); 382 | } 383 | count += 1; 384 | } 385 | testing.expectEqual(@as(i32, 2), count); 386 | } 387 | 388 | test "Headers.contains" { 389 | var h = Headers.init(testing.allocator); 390 | defer h.deinit(); 391 | try h.append("foo", "bar", null); 392 | try h.append("cookie", "somevalue", null); 393 | 394 | testing.expectEqual(true, h.contains("foo")); 395 | testing.expectEqual(false, h.contains("flooble")); 396 | } 397 | 398 | test "Headers.delete" { 399 | var h = Headers.init(testing.allocator); 400 | defer h.deinit(); 401 | try h.append("foo", "bar", null); 402 | try h.append("baz", "qux", null); 403 | try h.append("cookie", "somevalue", null); 404 | 405 | testing.expectEqual(false, h.delete("not-present")); 406 | testing.expectEqual(@as(usize, 3), h.toSlice().len); 407 | 408 | testing.expectEqual(true, h.delete("foo")); 409 | testing.expectEqual(@as(usize, 2), h.toSlice().len); 410 | { 411 | const e = h.at(0); 412 | testing.expectEqualSlices(u8, "baz", e.name); 413 | testing.expectEqualSlices(u8, "qux", e.value); 414 | testing.expectEqual(false, e.never_index); 415 | } 416 | { 417 | const e = h.at(1); 418 | testing.expectEqualSlices(u8, "cookie", e.name); 419 | testing.expectEqualSlices(u8, "somevalue", e.value); 420 | testing.expectEqual(true, e.never_index); 421 | } 422 | 423 | testing.expectEqual(false, h.delete("foo")); 424 | } 425 | 426 | test "Headers.orderedRemove" { 427 | var h = Headers.init(testing.allocator); 428 | defer h.deinit(); 429 | try h.append("foo", "bar", null); 430 | try h.append("baz", "qux", null); 431 | try h.append("cookie", "somevalue", null); 432 | 433 | h.orderedRemove(0); 434 | testing.expectEqual(@as(usize, 2), h.toSlice().len); 435 | { 436 | const e = h.at(0); 437 | testing.expectEqualSlices(u8, "baz", e.name); 438 | testing.expectEqualSlices(u8, "qux", e.value); 439 | testing.expectEqual(false, e.never_index); 440 | } 441 | { 442 | const e = h.at(1); 443 | testing.expectEqualSlices(u8, "cookie", e.name); 444 | testing.expectEqualSlices(u8, "somevalue", e.value); 445 | testing.expectEqual(true, e.never_index); 446 | } 447 | } 448 | 449 | test "Headers.swapRemove" { 450 | var h = Headers.init(testing.allocator); 451 | defer h.deinit(); 452 | try h.append("foo", "bar", null); 453 | try h.append("baz", "qux", null); 454 | try h.append("cookie", "somevalue", null); 455 | 456 | h.swapRemove(0); 457 | testing.expectEqual(@as(usize, 2), h.toSlice().len); 458 | { 459 | const e = h.at(0); 460 | testing.expectEqualSlices(u8, "cookie", e.name); 461 | testing.expectEqualSlices(u8, "somevalue", e.value); 462 | testing.expectEqual(true, e.never_index); 463 | } 464 | { 465 | const e = h.at(1); 466 | testing.expectEqualSlices(u8, "baz", e.name); 467 | testing.expectEqualSlices(u8, "qux", e.value); 468 | testing.expectEqual(false, e.never_index); 469 | } 470 | } 471 | 472 | test "Headers.at" { 473 | var h = Headers.init(testing.allocator); 474 | defer h.deinit(); 475 | try h.append("foo", "bar", null); 476 | try h.append("cookie", "somevalue", null); 477 | 478 | { 479 | const e = h.at(0); 480 | testing.expectEqualSlices(u8, "foo", e.name); 481 | testing.expectEqualSlices(u8, "bar", e.value); 482 | testing.expectEqual(false, e.never_index); 483 | } 484 | { 485 | const e = h.at(1); 486 | testing.expectEqualSlices(u8, "cookie", e.name); 487 | testing.expectEqualSlices(u8, "somevalue", e.value); 488 | testing.expectEqual(true, e.never_index); 489 | } 490 | } 491 | 492 | test "Headers.getIndices" { 493 | var h = Headers.init(testing.allocator); 494 | defer h.deinit(); 495 | try h.append("foo", "bar", null); 496 | try h.append("set-cookie", "x=1", null); 497 | try h.append("set-cookie", "y=2", null); 498 | 499 | testing.expect(null == h.getIndices("not-present")); 500 | testing.expectEqualSlices(usize, &[_]usize{0}, h.getIndices("foo").?.items); 501 | testing.expectEqualSlices(usize, &[_]usize{ 1, 2 }, h.getIndices("set-cookie").?.items); 502 | } 503 | 504 | test "Headers.get" { 505 | var h = Headers.init(testing.allocator); 506 | defer h.deinit(); 507 | try h.append("foo", "bar", null); 508 | try h.append("set-cookie", "x=1", null); 509 | try h.append("set-cookie", "y=2", null); 510 | 511 | { 512 | const v = try h.get(testing.allocator, "not-present"); 513 | testing.expect(null == v); 514 | } 515 | { 516 | const v = (try h.get(testing.allocator, "foo")).?; 517 | defer testing.allocator.free(v); 518 | const e = v[0]; 519 | testing.expectEqualSlices(u8, "foo", e.name); 520 | testing.expectEqualSlices(u8, "bar", e.value); 521 | testing.expectEqual(false, e.never_index); 522 | } 523 | { 524 | const v = (try h.get(testing.allocator, "set-cookie")).?; 525 | defer testing.allocator.free(v); 526 | { 527 | const e = v[0]; 528 | testing.expectEqualSlices(u8, "set-cookie", e.name); 529 | testing.expectEqualSlices(u8, "x=1", e.value); 530 | testing.expectEqual(true, e.never_index); 531 | } 532 | { 533 | const e = v[1]; 534 | testing.expectEqualSlices(u8, "set-cookie", e.name); 535 | testing.expectEqualSlices(u8, "y=2", e.value); 536 | testing.expectEqual(true, e.never_index); 537 | } 538 | } 539 | } 540 | 541 | test "Headers.getCommaSeparated" { 542 | var h = Headers.init(testing.allocator); 543 | defer h.deinit(); 544 | try h.append("foo", "bar", null); 545 | try h.append("set-cookie", "x=1", null); 546 | try h.append("set-cookie", "y=2", null); 547 | 548 | { 549 | const v = try h.getCommaSeparated(testing.allocator, "not-present"); 550 | testing.expect(null == v); 551 | } 552 | { 553 | const v = (try h.getCommaSeparated(testing.allocator, "foo")).?; 554 | defer testing.allocator.free(v); 555 | testing.expectEqualSlices(u8, "bar", v); 556 | } 557 | { 558 | const v = (try h.getCommaSeparated(testing.allocator, "set-cookie")).?; 559 | defer testing.allocator.free(v); 560 | testing.expectEqualSlices(u8, "x=1,y=2", v); 561 | } 562 | } 563 | 564 | test "Headers.sort" { 565 | var h = Headers.init(testing.allocator); 566 | defer h.deinit(); 567 | try h.append("foo", "bar", null); 568 | try h.append("cookie", "somevalue", null); 569 | 570 | h.sort(); 571 | { 572 | const e = h.at(0); 573 | testing.expectEqualSlices(u8, "cookie", e.name); 574 | testing.expectEqualSlices(u8, "somevalue", e.value); 575 | testing.expectEqual(true, e.never_index); 576 | } 577 | { 578 | const e = h.at(1); 579 | testing.expectEqualSlices(u8, "foo", e.name); 580 | testing.expectEqualSlices(u8, "bar", e.value); 581 | testing.expectEqual(false, e.never_index); 582 | } 583 | } 584 | 585 | test "Headers.format" { 586 | var h = Headers.init(testing.allocator); 587 | defer h.deinit(); 588 | try h.append("foo", "bar", null); 589 | try h.append("cookie", "somevalue", null); 590 | 591 | var buf: [100]u8 = undefined; 592 | testing.expectEqualSlices(u8, 593 | \\foo: bar 594 | \\cookie: somevalue 595 | \\ 596 | , try std.fmt.bufPrint(buf[0..], "{}", .{h})); 597 | } 598 | -------------------------------------------------------------------------------- /std/serialization.zig: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2015-2020 Zig Contributors 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. 4 | // The MIT license requires this copyright notice to be included in all copies 5 | // and substantial portions of the software. 6 | const std = @import("std"); 7 | const builtin = std.builtin; 8 | const io = std.io; 9 | const assert = std.debug.assert; 10 | const math = std.math; 11 | const meta = std.meta; 12 | const trait = meta.trait; 13 | const testing = std.testing; 14 | 15 | pub const Packing = enum { 16 | /// Pack data to byte alignment 17 | Byte, 18 | 19 | /// Pack data to bit alignment 20 | Bit, 21 | }; 22 | 23 | /// Creates a deserializer that deserializes types from any stream. 24 | /// If `is_packed` is true, the data stream is treated as bit-packed, 25 | /// otherwise data is expected to be packed to the smallest byte. 26 | /// Types may implement a custom deserialization routine with a 27 | /// function named `deserialize` in the form of: 28 | /// ``` 29 | /// pub fn deserialize(self: *Self, deserializer: anytype) !void 30 | /// ``` 31 | /// which will be called when the deserializer is used to deserialize 32 | /// that type. It will pass a pointer to the type instance to deserialize 33 | /// into and a pointer to the deserializer struct. 34 | pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime ReaderType: type) type { 35 | return struct { 36 | in_stream: if (packing == .Bit) io.BitReader(endian, ReaderType) else ReaderType, 37 | 38 | const Self = @This(); 39 | 40 | pub fn init(in_stream: ReaderType) Self { 41 | return Self{ 42 | .in_stream = switch (packing) { 43 | .Bit => io.bitReader(endian, in_stream), 44 | .Byte => in_stream, 45 | }, 46 | }; 47 | } 48 | 49 | pub fn alignToByte(self: *Self) void { 50 | if (packing == .Byte) return; 51 | self.in_stream.alignToByte(); 52 | } 53 | 54 | //@BUG: inferred error issue. See: #1386 55 | fn deserializeInt(self: *Self, comptime T: type) (ReaderType.Error || error{EndOfStream})!T { 56 | comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T)); 57 | 58 | const u8_bit_count = 8; 59 | const t_bit_count = comptime meta.bitCount(T); 60 | 61 | const U = std.meta.Int(.unsigned, t_bit_count); 62 | const Log2U = math.Log2Int(U); 63 | const int_size = (t_bit_count + 7) / 8; 64 | 65 | if (packing == .Bit) { 66 | const result = try self.in_stream.readBitsNoEof(U, t_bit_count); 67 | return @bitCast(T, result); 68 | } 69 | 70 | var buffer: [int_size]u8 = undefined; 71 | const read_size = try self.in_stream.read(buffer[0..]); 72 | if (read_size < int_size) return error.EndOfStream; 73 | 74 | if (int_size == 1) { 75 | if (t_bit_count == 8) return @bitCast(T, buffer[0]); 76 | const PossiblySignedByte = std.meta.Int(@typeInfo(T).Int.signedness, 8); 77 | return @truncate(T, @bitCast(PossiblySignedByte, buffer[0])); 78 | } 79 | 80 | var result = @as(U, 0); 81 | for (buffer) |byte, i| { 82 | switch (endian) { 83 | .Big => { 84 | result = (result << u8_bit_count) | byte; 85 | }, 86 | .Little => { 87 | result |= @as(U, byte) << @intCast(Log2U, u8_bit_count * i); 88 | }, 89 | } 90 | } 91 | 92 | return @bitCast(T, result); 93 | } 94 | 95 | /// Deserializes and returns data of the specified type from the stream 96 | pub fn deserialize(self: *Self, comptime T: type) !T { 97 | var value: T = undefined; 98 | try self.deserializeInto(&value); 99 | return value; 100 | } 101 | 102 | /// Deserializes data into the type pointed to by `ptr` 103 | pub fn deserializeInto(self: *Self, ptr: anytype) !void { 104 | const T = @TypeOf(ptr); 105 | comptime assert(trait.is(.Pointer)(T)); 106 | 107 | if (comptime trait.isSlice(T) or comptime trait.isPtrTo(.Array)(T)) { 108 | for (ptr) |*v| 109 | try self.deserializeInto(v); 110 | return; 111 | } 112 | 113 | comptime assert(trait.isSingleItemPtr(T)); 114 | 115 | const C = comptime meta.Child(T); 116 | const child_type_id = @typeInfo(C); 117 | 118 | //custom deserializer: fn(self: *Self, deserializer: anytype) !void 119 | if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self); 120 | 121 | if (comptime trait.isPacked(C) and packing != .Bit) { 122 | var packed_deserializer = deserializer(endian, .Bit, self.in_stream); 123 | return packed_deserializer.deserializeInto(ptr); 124 | } 125 | 126 | switch (child_type_id) { 127 | .Void => return, 128 | .Bool => ptr.* = (try self.deserializeInt(u1)) > 0, 129 | .Float, .Int => ptr.* = try self.deserializeInt(C), 130 | .Struct => { 131 | const info = @typeInfo(C).Struct; 132 | 133 | inline for (info.fields) |*field_info| { 134 | const name = field_info.name; 135 | const FieldType = field_info.field_type; 136 | 137 | if (FieldType == void or FieldType == u0) continue; 138 | 139 | //it doesn't make any sense to read pointers 140 | if (comptime trait.is(.Pointer)(FieldType)) { 141 | @compileError("Will not " ++ "read field " ++ name ++ " of struct " ++ 142 | @typeName(C) ++ " because it " ++ "is of pointer-type " ++ 143 | @typeName(FieldType) ++ "."); 144 | } 145 | 146 | try self.deserializeInto(&@field(ptr, name)); 147 | } 148 | }, 149 | .Union => { 150 | const info = @typeInfo(C).Union; 151 | if (info.tag_type) |TagType| { 152 | //we avoid duplicate iteration over the enum tags 153 | // by getting the int directly and casting it without 154 | // safety. If it is bad, it will be caught anyway. 155 | const TagInt = std.meta.TagType(TagType); 156 | const tag = try self.deserializeInt(TagInt); 157 | 158 | inline for (info.fields) |field_info| { 159 | if (@enumToInt(@field(TagType, field_info.name)) == tag) { 160 | const name = field_info.name; 161 | const FieldType = field_info.field_type; 162 | ptr.* = @unionInit(C, name, undefined); 163 | try self.deserializeInto(&@field(ptr, name)); 164 | return; 165 | } 166 | } 167 | //This is reachable if the enum data is bad 168 | return error.InvalidEnumTag; 169 | } 170 | @compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++ 171 | " because it is an untagged union. Use a custom deserialize()."); 172 | }, 173 | .Optional => { 174 | const OC = comptime meta.Child(C); 175 | const exists = (try self.deserializeInt(u1)) > 0; 176 | if (!exists) { 177 | ptr.* = null; 178 | return; 179 | } 180 | 181 | ptr.* = @as(OC, undefined); //make it non-null so the following .? is guaranteed safe 182 | const val_ptr = &ptr.*.?; 183 | try self.deserializeInto(val_ptr); 184 | }, 185 | .Enum => { 186 | var value = try self.deserializeInt(std.meta.TagType(C)); 187 | ptr.* = try meta.intToEnum(C, value); 188 | }, 189 | else => { 190 | @compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented)."); 191 | }, 192 | } 193 | } 194 | }; 195 | } 196 | 197 | pub fn deserializer( 198 | comptime endian: builtin.Endian, 199 | comptime packing: Packing, 200 | in_stream: anytype, 201 | ) Deserializer(endian, packing, @TypeOf(in_stream)) { 202 | return Deserializer(endian, packing, @TypeOf(in_stream)).init(in_stream); 203 | } 204 | 205 | /// Creates a serializer that serializes types to any stream. 206 | /// If `is_packed` is true, the data will be bit-packed into the stream. 207 | /// Note that the you must call `serializer.flush()` when you are done 208 | /// writing bit-packed data in order ensure any unwritten bits are committed. 209 | /// If `is_packed` is false, data is packed to the smallest byte. In the case 210 | /// of packed structs, the struct will written bit-packed and with the specified 211 | /// endianess, after which data will resume being written at the next byte boundary. 212 | /// Types may implement a custom serialization routine with a 213 | /// function named `serialize` in the form of: 214 | /// ``` 215 | /// pub fn serialize(self: Self, serializer: anytype) !void 216 | /// ``` 217 | /// which will be called when the serializer is used to serialize that type. It will 218 | /// pass a const pointer to the type instance to be serialized and a pointer 219 | /// to the serializer struct. 220 | pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime Writer: type) type { 221 | return struct { 222 | writer: if (packing == .Bit) io.BitWriter(endian, Writer) else Writer, 223 | 224 | const Self = @This(); 225 | pub const Error = Writer.Error; 226 | 227 | pub fn init(writer: Writer) Self { 228 | return Self{ 229 | .writer = switch (packing) { 230 | .Bit => io.bitWriter(endian, writer), 231 | .Byte => writer, 232 | }, 233 | }; 234 | } 235 | 236 | /// Flushes any unwritten bits to the stream 237 | pub fn flush(self: *Self) Error!void { 238 | if (packing == .Bit) return self.writer.flushBits(); 239 | } 240 | 241 | fn serializeInt(self: *Self, value: anytype) Error!void { 242 | const T = @TypeOf(value); 243 | comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T)); 244 | 245 | const t_bit_count = comptime meta.bitCount(T); 246 | const u8_bit_count = comptime meta.bitCount(u8); 247 | 248 | const U = std.meta.Int(.unsigned, t_bit_count); 249 | const Log2U = math.Log2Int(U); 250 | const int_size = (t_bit_count + 7) / 8; 251 | 252 | const u_value = @bitCast(U, value); 253 | 254 | if (packing == .Bit) return self.writer.writeBits(u_value, t_bit_count); 255 | 256 | var buffer: [int_size]u8 = undefined; 257 | if (int_size == 1) buffer[0] = u_value; 258 | 259 | for (buffer) |*byte, i| { 260 | const idx = switch (endian) { 261 | .Big => int_size - i - 1, 262 | .Little => i, 263 | }; 264 | const shift = @intCast(Log2U, idx * u8_bit_count); 265 | const v = u_value >> shift; 266 | byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v); 267 | } 268 | 269 | try self.writer.writeAll(&buffer); 270 | } 271 | 272 | /// Serializes the passed value into the stream 273 | pub fn serialize(self: *Self, value: anytype) Error!void { 274 | const T = comptime @TypeOf(value); 275 | 276 | if (comptime trait.isIndexable(T)) { 277 | for (value) |v| 278 | try self.serialize(v); 279 | return; 280 | } 281 | 282 | //custom serializer: fn(self: Self, serializer: anytype) !void 283 | if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self); 284 | 285 | if (comptime trait.isPacked(T) and packing != .Bit) { 286 | var packed_serializer = Serializer(endian, .Bit, Writer).init(self.writer); 287 | try packed_serializer.serialize(value); 288 | try packed_serializer.flush(); 289 | return; 290 | } 291 | 292 | switch (@typeInfo(T)) { 293 | .Void => return, 294 | .Bool => try self.serializeInt(@as(u1, @boolToInt(value))), 295 | .Float, .Int => try self.serializeInt(value), 296 | .Struct => { 297 | const info = @typeInfo(T); 298 | 299 | inline for (info.Struct.fields) |*field_info| { 300 | const name = field_info.name; 301 | const FieldType = field_info.field_type; 302 | 303 | if (FieldType == void or FieldType == u0) continue; 304 | 305 | //It doesn't make sense to write pointers 306 | if (comptime trait.is(.Pointer)(FieldType)) { 307 | @compileError("Will not " ++ "serialize field " ++ name ++ 308 | " of struct " ++ @typeName(T) ++ " because it " ++ 309 | "is of pointer-type " ++ @typeName(FieldType) ++ "."); 310 | } 311 | try self.serialize(@field(value, name)); 312 | } 313 | }, 314 | .Union => { 315 | const info = @typeInfo(T).Union; 316 | if (info.tag_type) |TagType| { 317 | const active_tag = meta.activeTag(value); 318 | try self.serialize(active_tag); 319 | //This inline loop is necessary because active_tag is a runtime 320 | // value, but @field requires a comptime value. Our alternative 321 | // is to check each field for a match 322 | inline for (info.fields) |field_info| { 323 | if (@field(TagType, field_info.name) == active_tag) { 324 | const name = field_info.name; 325 | const FieldType = field_info.field_type; 326 | try self.serialize(@field(value, name)); 327 | return; 328 | } 329 | } 330 | unreachable; 331 | } 332 | @compileError("Cannot meaningfully serialize " ++ @typeName(T) ++ 333 | " because it is an untagged union. Use a custom serialize()."); 334 | }, 335 | .Optional => { 336 | if (value == null) { 337 | try self.serializeInt(@as(u1, @boolToInt(false))); 338 | return; 339 | } 340 | try self.serializeInt(@as(u1, @boolToInt(true))); 341 | 342 | const OC = comptime meta.Child(T); 343 | const val_ptr = &value.?; 344 | try self.serialize(val_ptr.*); 345 | }, 346 | .Enum => { 347 | try self.serializeInt(@enumToInt(value)); 348 | }, 349 | else => @compileError("Cannot serialize " ++ @tagName(@typeInfo(T)) ++ " types (unimplemented)."), 350 | } 351 | } 352 | }; 353 | } 354 | 355 | pub fn serializer( 356 | comptime endian: builtin.Endian, 357 | comptime packing: Packing, 358 | writer: anytype, 359 | ) Serializer(endian, packing, @TypeOf(writer)) { 360 | return Serializer(endian, packing, @TypeOf(writer)).init(writer); 361 | } 362 | 363 | fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: Packing) !void { 364 | @setEvalBranchQuota(1500); 365 | //@NOTE: if this test is taking too long, reduce the maximum tested bitsize 366 | const max_test_bitsize = 128; 367 | 368 | const total_bytes = comptime blk: { 369 | var bytes = 0; 370 | comptime var i = 0; 371 | while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0); 372 | break :blk bytes * 2; 373 | }; 374 | 375 | var data_mem: [total_bytes]u8 = undefined; 376 | var out = io.fixedBufferStream(&data_mem); 377 | var _serializer = serializer(endian, packing, out.writer()); 378 | 379 | var in = io.fixedBufferStream(&data_mem); 380 | var _deserializer = deserializer(endian, packing, in.reader()); 381 | 382 | comptime var i = 0; 383 | inline while (i <= max_test_bitsize) : (i += 1) { 384 | const U = std.meta.Int(.unsigned, i); 385 | const S = std.meta.Int(.signed, i); 386 | try _serializer.serializeInt(@as(U, i)); 387 | if (i != 0) try _serializer.serializeInt(@as(S, -1)) else try _serializer.serialize(@as(S, 0)); 388 | } 389 | try _serializer.flush(); 390 | 391 | i = 0; 392 | inline while (i <= max_test_bitsize) : (i += 1) { 393 | const U = std.meta.Int(.unsigned, i); 394 | const S = std.meta.Int(.signed, i); 395 | const x = try _deserializer.deserializeInt(U); 396 | const y = try _deserializer.deserializeInt(S); 397 | testing.expect(x == @as(U, i)); 398 | if (i != 0) testing.expect(y == @as(S, -1)) else testing.expect(y == 0); 399 | } 400 | 401 | const u8_bit_count = comptime meta.bitCount(u8); 402 | //0 + 1 + 2 + ... n = (n * (n + 1)) / 2 403 | //and we have each for unsigned and signed, so * 2 404 | const total_bits = (max_test_bitsize * (max_test_bitsize + 1)); 405 | const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0); 406 | const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte; 407 | 408 | testing.expect(in.pos == if (packing == .Bit) total_packed_bytes else total_bytes); 409 | 410 | //Verify that empty error set works with serializer. 411 | //deserializer is covered by FixedBufferStream 412 | var null_serializer = serializer(endian, packing, std.io.null_writer); 413 | try null_serializer.serialize(data_mem[0..]); 414 | try null_serializer.flush(); 415 | } 416 | 417 | test "Serializer/Deserializer Int" { 418 | try testIntSerializerDeserializer(.Big, .Byte); 419 | try testIntSerializerDeserializer(.Little, .Byte); 420 | // TODO these tests are disabled due to tripping an LLVM assertion 421 | // https://github.com/ziglang/zig/issues/2019 422 | //try testIntSerializerDeserializer(builtin.Endian.Big, true); 423 | //try testIntSerializerDeserializer(builtin.Endian.Little, true); 424 | } 425 | 426 | fn testIntSerializerDeserializerInfNaN( 427 | comptime endian: builtin.Endian, 428 | comptime packing: Packing, 429 | ) !void { 430 | const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8); 431 | var data_mem: [mem_size]u8 = undefined; 432 | 433 | var out = io.fixedBufferStream(&data_mem); 434 | var _serializer = serializer(endian, packing, out.writer()); 435 | 436 | var in = io.fixedBufferStream(&data_mem); 437 | var _deserializer = deserializer(endian, packing, in.reader()); 438 | 439 | //@TODO: isInf/isNan not currently implemented for f128. 440 | try _serializer.serialize(std.math.nan(f16)); 441 | try _serializer.serialize(std.math.inf(f16)); 442 | try _serializer.serialize(std.math.nan(f32)); 443 | try _serializer.serialize(std.math.inf(f32)); 444 | try _serializer.serialize(std.math.nan(f64)); 445 | try _serializer.serialize(std.math.inf(f64)); 446 | //try serializer.serialize(std.math.nan(f128)); 447 | //try serializer.serialize(std.math.inf(f128)); 448 | const nan_check_f16 = try _deserializer.deserialize(f16); 449 | const inf_check_f16 = try _deserializer.deserialize(f16); 450 | const nan_check_f32 = try _deserializer.deserialize(f32); 451 | _deserializer.alignToByte(); 452 | const inf_check_f32 = try _deserializer.deserialize(f32); 453 | const nan_check_f64 = try _deserializer.deserialize(f64); 454 | const inf_check_f64 = try _deserializer.deserialize(f64); 455 | //const nan_check_f128 = try deserializer.deserialize(f128); 456 | //const inf_check_f128 = try deserializer.deserialize(f128); 457 | testing.expect(std.math.isNan(nan_check_f16)); 458 | testing.expect(std.math.isInf(inf_check_f16)); 459 | testing.expect(std.math.isNan(nan_check_f32)); 460 | testing.expect(std.math.isInf(inf_check_f32)); 461 | testing.expect(std.math.isNan(nan_check_f64)); 462 | testing.expect(std.math.isInf(inf_check_f64)); 463 | //expect(std.math.isNan(nan_check_f128)); 464 | //expect(std.math.isInf(inf_check_f128)); 465 | } 466 | 467 | test "Serializer/Deserializer Int: Inf/NaN" { 468 | try testIntSerializerDeserializerInfNaN(.Big, .Byte); 469 | try testIntSerializerDeserializerInfNaN(.Little, .Byte); 470 | try testIntSerializerDeserializerInfNaN(.Big, .Bit); 471 | try testIntSerializerDeserializerInfNaN(.Little, .Bit); 472 | } 473 | 474 | fn testAlternateSerializer(self: anytype, _serializer: anytype) !void { 475 | try _serializer.serialize(self.f_f16); 476 | } 477 | 478 | fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: Packing) !void { 479 | const ColorType = enum(u4) { 480 | RGB8 = 1, 481 | RA16 = 2, 482 | R32 = 3, 483 | }; 484 | 485 | const TagAlign = union(enum(u32)) { 486 | A: u8, 487 | B: u8, 488 | C: u8, 489 | }; 490 | 491 | const Color = union(ColorType) { 492 | RGB8: struct { 493 | r: u8, 494 | g: u8, 495 | b: u8, 496 | a: u8, 497 | }, 498 | RA16: struct { 499 | r: u16, 500 | a: u16, 501 | }, 502 | R32: u32, 503 | }; 504 | 505 | const PackedStruct = packed struct { 506 | f_i3: i3, 507 | f_u2: u2, 508 | }; 509 | 510 | //to test custom serialization 511 | const Custom = struct { 512 | f_f16: f16, 513 | f_unused_u32: u32, 514 | 515 | pub fn deserialize(self: *@This(), _deserializer: anytype) !void { 516 | try _deserializer.deserializeInto(&self.f_f16); 517 | self.f_unused_u32 = 47; 518 | } 519 | 520 | pub const serialize = testAlternateSerializer; 521 | }; 522 | 523 | const MyStruct = struct { 524 | f_i3: i3, 525 | f_u8: u8, 526 | f_tag_align: TagAlign, 527 | f_u24: u24, 528 | f_i19: i19, 529 | f_void: void, 530 | f_f32: f32, 531 | f_f128: f128, 532 | f_packed_0: PackedStruct, 533 | f_i7arr: [10]i7, 534 | f_of64n: ?f64, 535 | f_of64v: ?f64, 536 | f_color_type: ColorType, 537 | f_packed_1: PackedStruct, 538 | f_custom: Custom, 539 | f_color: Color, 540 | }; 541 | 542 | const my_inst = MyStruct{ 543 | .f_i3 = -1, 544 | .f_u8 = 8, 545 | .f_tag_align = TagAlign{ .B = 148 }, 546 | .f_u24 = 24, 547 | .f_i19 = 19, 548 | .f_void = {}, 549 | .f_f32 = 32.32, 550 | .f_f128 = 128.128, 551 | .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 }, 552 | .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 553 | .f_of64n = null, 554 | .f_of64v = 64.64, 555 | .f_color_type = ColorType.R32, 556 | .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 }, 557 | .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 }, 558 | .f_color = Color{ .R32 = 123822 }, 559 | }; 560 | 561 | var data_mem: [@sizeOf(MyStruct)]u8 = undefined; 562 | var out = io.fixedBufferStream(&data_mem); 563 | var _serializer = serializer(endian, packing, out.writer()); 564 | 565 | var in = io.fixedBufferStream(&data_mem); 566 | var _deserializer = deserializer(endian, packing, in.reader()); 567 | 568 | try _serializer.serialize(my_inst); 569 | 570 | const my_copy = try _deserializer.deserialize(MyStruct); 571 | testing.expect(meta.eql(my_copy, my_inst)); 572 | } 573 | 574 | test "Serializer/Deserializer generic" { 575 | try testSerializerDeserializer(builtin.Endian.Big, .Byte); 576 | try testSerializerDeserializer(builtin.Endian.Little, .Byte); 577 | try testSerializerDeserializer(builtin.Endian.Big, .Bit); 578 | try testSerializerDeserializer(builtin.Endian.Little, .Bit); 579 | } 580 | 581 | fn testBadData(comptime endian: builtin.Endian, comptime packing: Packing) !void { 582 | const E = enum(u14) { 583 | One = 1, 584 | Two = 2, 585 | }; 586 | 587 | const A = struct { 588 | e: E, 589 | }; 590 | 591 | const C = union(E) { 592 | One: u14, 593 | Two: f16, 594 | }; 595 | 596 | var data_mem: [4]u8 = undefined; 597 | var out = io.fixedBufferStream(&data_mem); 598 | var _serializer = serializer(endian, packing, out.writer()); 599 | 600 | var in = io.fixedBufferStream(&data_mem); 601 | var _deserializer = deserializer(endian, packing, in.reader()); 602 | 603 | try _serializer.serialize(@as(u14, 3)); 604 | testing.expectError(error.InvalidEnumTag, _deserializer.deserialize(A)); 605 | out.pos = 0; 606 | try _serializer.serialize(@as(u14, 3)); 607 | try _serializer.serialize(@as(u14, 88)); 608 | testing.expectError(error.InvalidEnumTag, _deserializer.deserialize(C)); 609 | } 610 | 611 | test "Deserializer bad data" { 612 | try testBadData(.Big, .Byte); 613 | try testBadData(.Little, .Byte); 614 | try testBadData(.Big, .Bit); 615 | try testBadData(.Little, .Bit); 616 | } 617 | -------------------------------------------------------------------------------- /std/os/windows/user32.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const assert = std.debug.assert; 4 | 5 | const windows = std.os.windows; 6 | const GetLastError = windows.kernel32.GetLastError; 7 | const SetLastError = windows.kernel32.SetLastError; 8 | const unexpectedError = windows.unexpectedError; 9 | const HWND = windows.HWND; 10 | const UINT = windows.UINT; 11 | const HDC = windows.HDC; 12 | const LONG = windows.LONG; 13 | const LONG_PTR = windows.LONG_PTR; 14 | const WINAPI = windows.WINAPI; 15 | const RECT = windows.RECT; 16 | const DWORD = windows.DWORD; 17 | const BOOL = windows.BOOL; 18 | const TRUE = windows.TRUE; 19 | const HMENU = windows.HMENU; 20 | const HINSTANCE = windows.HINSTANCE; 21 | const LPVOID = windows.LPVOID; 22 | const ATOM = windows.ATOM; 23 | const WPARAM = windows.WPARAM; 24 | const LRESULT = windows.LRESULT; 25 | const HICON = windows.HICON; 26 | const LPARAM = windows.LPARAM; 27 | const POINT = windows.POINT; 28 | const HCURSOR = windows.HCURSOR; 29 | const HBRUSH = windows.HBRUSH; 30 | 31 | inline fn selectSymbol(comptime function_static: anytype, function_dynamic: *const @TypeOf(function_static), comptime os: std.Target.Os.WindowsVersion) *const @TypeOf(function_static) { 32 | const sym_ok = comptime builtin.os.isAtLeast(.windows, os); 33 | if (sym_ok == true) return function_static; 34 | if (sym_ok == null) return function_dynamic; 35 | if (sym_ok == false) @compileError("Target OS range does not support function, at least " ++ @tagName(os) ++ " is required"); 36 | } 37 | 38 | // === Messages === 39 | 40 | pub const WNDPROC = *const fn (hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM) callconv(WINAPI) LRESULT; 41 | 42 | pub const MSG = extern struct { 43 | hWnd: ?HWND, 44 | message: UINT, 45 | wParam: WPARAM, 46 | lParam: LPARAM, 47 | time: DWORD, 48 | pt: POINT, 49 | lPrivate: DWORD, 50 | }; 51 | 52 | // Compiled by the WINE team @ https://wiki.winehq.org/List_Of_Windows_Messages 53 | pub const WM_NULL = 0x0000; 54 | pub const WM_CREATE = 0x0001; 55 | pub const WM_DESTROY = 0x0002; 56 | pub const WM_MOVE = 0x0003; 57 | pub const WM_SIZE = 0x0005; 58 | pub const WM_ACTIVATE = 0x0006; 59 | pub const WM_SETFOCUS = 0x0007; 60 | pub const WM_KILLFOCUS = 0x0008; 61 | pub const WM_ENABLE = 0x000A; 62 | pub const WM_SETREDRAW = 0x000B; 63 | pub const WM_SETTEXT = 0x000C; 64 | pub const WM_GETTEXT = 0x000D; 65 | pub const WM_GETTEXTLENGTH = 0x000E; 66 | pub const WM_PAINT = 0x000F; 67 | pub const WM_CLOSE = 0x0010; 68 | pub const WM_QUERYENDSESSION = 0x0011; 69 | pub const WM_QUIT = 0x0012; 70 | pub const WM_QUERYOPEN = 0x0013; 71 | pub const WM_ERASEBKGND = 0x0014; 72 | pub const WM_SYSCOLORCHANGE = 0x0015; 73 | pub const WM_ENDSESSION = 0x0016; 74 | pub const WM_SHOWWINDOW = 0x0018; 75 | pub const WM_CTLCOLOR = 0x0019; 76 | pub const WM_WININICHANGE = 0x001A; 77 | pub const WM_DEVMODECHANGE = 0x001B; 78 | pub const WM_ACTIVATEAPP = 0x001C; 79 | pub const WM_FONTCHANGE = 0x001D; 80 | pub const WM_TIMECHANGE = 0x001E; 81 | pub const WM_CANCELMODE = 0x001F; 82 | pub const WM_SETCURSOR = 0x0020; 83 | pub const WM_MOUSEACTIVATE = 0x0021; 84 | pub const WM_CHILDACTIVATE = 0x0022; 85 | pub const WM_QUEUESYNC = 0x0023; 86 | pub const WM_GETMINMAXINFO = 0x0024; 87 | pub const WM_PAINTICON = 0x0026; 88 | pub const WM_ICONERASEBKGND = 0x0027; 89 | pub const WM_NEXTDLGCTL = 0x0028; 90 | pub const WM_SPOOLERSTATUS = 0x002A; 91 | pub const WM_DRAWITEM = 0x002B; 92 | pub const WM_MEASUREITEM = 0x002C; 93 | pub const WM_DELETEITEM = 0x002D; 94 | pub const WM_VKEYTOITEM = 0x002E; 95 | pub const WM_CHARTOITEM = 0x002F; 96 | pub const WM_SETFONT = 0x0030; 97 | pub const WM_GETFONT = 0x0031; 98 | pub const WM_SETHOTKEY = 0x0032; 99 | pub const WM_GETHOTKEY = 0x0033; 100 | pub const WM_QUERYDRAGICON = 0x0037; 101 | pub const WM_COMPAREITEM = 0x0039; 102 | pub const WM_GETOBJECT = 0x003D; 103 | pub const WM_COMPACTING = 0x0041; 104 | pub const WM_COMMNOTIFY = 0x0044; 105 | pub const WM_WINDOWPOSCHANGING = 0x0046; 106 | pub const WM_WINDOWPOSCHANGED = 0x0047; 107 | pub const WM_POWER = 0x0048; 108 | pub const WM_COPYGLOBALDATA = 0x0049; 109 | pub const WM_COPYDATA = 0x004A; 110 | pub const WM_CANCELJOURNAL = 0x004B; 111 | pub const WM_NOTIFY = 0x004E; 112 | pub const WM_INPUTLANGCHANGEREQUEST = 0x0050; 113 | pub const WM_INPUTLANGCHANGE = 0x0051; 114 | pub const WM_TCARD = 0x0052; 115 | pub const WM_HELP = 0x0053; 116 | pub const WM_USERCHANGED = 0x0054; 117 | pub const WM_NOTIFYFORMAT = 0x0055; 118 | pub const WM_CONTEXTMENU = 0x007B; 119 | pub const WM_STYLECHANGING = 0x007C; 120 | pub const WM_STYLECHANGED = 0x007D; 121 | pub const WM_DISPLAYCHANGE = 0x007E; 122 | pub const WM_GETICON = 0x007F; 123 | pub const WM_SETICON = 0x0080; 124 | pub const WM_NCCREATE = 0x0081; 125 | pub const WM_NCDESTROY = 0x0082; 126 | pub const WM_NCCALCSIZE = 0x0083; 127 | pub const WM_NCHITTEST = 0x0084; 128 | pub const WM_NCPAINT = 0x0085; 129 | pub const WM_NCACTIVATE = 0x0086; 130 | pub const WM_GETDLGCODE = 0x0087; 131 | pub const WM_SYNCPAINT = 0x0088; 132 | pub const WM_NCMOUSEMOVE = 0x00A0; 133 | pub const WM_NCLBUTTONDOWN = 0x00A1; 134 | pub const WM_NCLBUTTONUP = 0x00A2; 135 | pub const WM_NCLBUTTONDBLCLK = 0x00A3; 136 | pub const WM_NCRBUTTONDOWN = 0x00A4; 137 | pub const WM_NCRBUTTONUP = 0x00A5; 138 | pub const WM_NCRBUTTONDBLCLK = 0x00A6; 139 | pub const WM_NCMBUTTONDOWN = 0x00A7; 140 | pub const WM_NCMBUTTONUP = 0x00A8; 141 | pub const WM_NCMBUTTONDBLCLK = 0x00A9; 142 | pub const WM_NCXBUTTONDOWN = 0x00AB; 143 | pub const WM_NCXBUTTONUP = 0x00AC; 144 | pub const WM_NCXBUTTONDBLCLK = 0x00AD; 145 | pub const EM_GETSEL = 0x00B0; 146 | pub const EM_SETSEL = 0x00B1; 147 | pub const EM_GETRECT = 0x00B2; 148 | pub const EM_SETRECT = 0x00B3; 149 | pub const EM_SETRECTNP = 0x00B4; 150 | pub const EM_SCROLL = 0x00B5; 151 | pub const EM_LINESCROLL = 0x00B6; 152 | pub const EM_SCROLLCARET = 0x00B7; 153 | pub const EM_GETMODIFY = 0x00B8; 154 | pub const EM_SETMODIFY = 0x00B9; 155 | pub const EM_GETLINECOUNT = 0x00BA; 156 | pub const EM_LINEINDEX = 0x00BB; 157 | pub const EM_SETHANDLE = 0x00BC; 158 | pub const EM_GETHANDLE = 0x00BD; 159 | pub const EM_GETTHUMB = 0x00BE; 160 | pub const EM_LINELENGTH = 0x00C1; 161 | pub const EM_REPLACESEL = 0x00C2; 162 | pub const EM_SETFONT = 0x00C3; 163 | pub const EM_GETLINE = 0x00C4; 164 | pub const EM_LIMITTEXT = 0x00C5; 165 | pub const EM_SETLIMITTEXT = 0x00C5; 166 | pub const EM_CANUNDO = 0x00C6; 167 | pub const EM_UNDO = 0x00C7; 168 | pub const EM_FMTLINES = 0x00C8; 169 | pub const EM_LINEFROMCHAR = 0x00C9; 170 | pub const EM_SETWORDBREAK = 0x00CA; 171 | pub const EM_SETTABSTOPS = 0x00CB; 172 | pub const EM_SETPASSWORDCHAR = 0x00CC; 173 | pub const EM_EMPTYUNDOBUFFER = 0x00CD; 174 | pub const EM_GETFIRSTVISIBLELINE = 0x00CE; 175 | pub const EM_SETREADONLY = 0x00CF; 176 | pub const EM_SETWORDBREAKPROC = 0x00D0; 177 | pub const EM_GETWORDBREAKPROC = 0x00D1; 178 | pub const EM_GETPASSWORDCHAR = 0x00D2; 179 | pub const EM_SETMARGINS = 0x00D3; 180 | pub const EM_GETMARGINS = 0x00D4; 181 | pub const EM_GETLIMITTEXT = 0x00D5; 182 | pub const EM_POSFROMCHAR = 0x00D6; 183 | pub const EM_CHARFROMPOS = 0x00D7; 184 | pub const EM_SETIMESTATUS = 0x00D8; 185 | pub const EM_GETIMESTATUS = 0x00D9; 186 | pub const SBM_SETPOS = 0x00E0; 187 | pub const SBM_GETPOS = 0x00E1; 188 | pub const SBM_SETRANGE = 0x00E2; 189 | pub const SBM_GETRANGE = 0x00E3; 190 | pub const SBM_ENABLE_ARROWS = 0x00E4; 191 | pub const SBM_SETRANGEREDRAW = 0x00E6; 192 | pub const SBM_SETSCROLLINFO = 0x00E9; 193 | pub const SBM_GETSCROLLINFO = 0x00EA; 194 | pub const SBM_GETSCROLLBARINFO = 0x00EB; 195 | pub const BM_GETCHECK = 0x00F0; 196 | pub const BM_SETCHECK = 0x00F1; 197 | pub const BM_GETSTATE = 0x00F2; 198 | pub const BM_SETSTATE = 0x00F3; 199 | pub const BM_SETSTYLE = 0x00F4; 200 | pub const BM_CLICK = 0x00F5; 201 | pub const BM_GETIMAGE = 0x00F6; 202 | pub const BM_SETIMAGE = 0x00F7; 203 | pub const BM_SETDONTCLICK = 0x00F8; 204 | pub const WM_INPUT = 0x00FF; 205 | pub const WM_KEYDOWN = 0x0100; 206 | pub const WM_KEYUP = 0x0101; 207 | pub const WM_CHAR = 0x0102; 208 | pub const WM_DEADCHAR = 0x0103; 209 | pub const WM_SYSKEYDOWN = 0x0104; 210 | pub const WM_SYSKEYUP = 0x0105; 211 | pub const WM_SYSCHAR = 0x0106; 212 | pub const WM_SYSDEADCHAR = 0x0107; 213 | pub const WM_UNICHAR = 0x0109; 214 | pub const WM_WNT_CONVERTREQUESTEX = 0x0109; 215 | pub const WM_CONVERTREQUEST = 0x010A; 216 | pub const WM_CONVERTRESULT = 0x010B; 217 | pub const WM_INTERIM = 0x010C; 218 | pub const WM_IME_STARTCOMPOSITION = 0x010D; 219 | pub const WM_IME_ENDCOMPOSITION = 0x010E; 220 | pub const WM_IME_COMPOSITION = 0x010F; 221 | pub const WM_INITDIALOG = 0x0110; 222 | pub const WM_COMMAND = 0x0111; 223 | pub const WM_SYSCOMMAND = 0x0112; 224 | pub const WM_TIMER = 0x0113; 225 | pub const WM_HSCROLL = 0x0114; 226 | pub const WM_VSCROLL = 0x0115; 227 | pub const WM_INITMENU = 0x0116; 228 | pub const WM_INITMENUPOPUP = 0x0117; 229 | pub const WM_SYSTIMER = 0x0118; 230 | pub const WM_MENUSELECT = 0x011F; 231 | pub const WM_MENUCHAR = 0x0120; 232 | pub const WM_ENTERIDLE = 0x0121; 233 | pub const WM_MENURBUTTONUP = 0x0122; 234 | pub const WM_MENUDRAG = 0x0123; 235 | pub const WM_MENUGETOBJECT = 0x0124; 236 | pub const WM_UNINITMENUPOPUP = 0x0125; 237 | pub const WM_MENUCOMMAND = 0x0126; 238 | pub const WM_CHANGEUISTATE = 0x0127; 239 | pub const WM_UPDATEUISTATE = 0x0128; 240 | pub const WM_QUERYUISTATE = 0x0129; 241 | pub const WM_CTLCOLORMSGBOX = 0x0132; 242 | pub const WM_CTLCOLOREDIT = 0x0133; 243 | pub const WM_CTLCOLORLISTBOX = 0x0134; 244 | pub const WM_CTLCOLORBTN = 0x0135; 245 | pub const WM_CTLCOLORDLG = 0x0136; 246 | pub const WM_CTLCOLORSCROLLBAR = 0x0137; 247 | pub const WM_CTLCOLORSTATIC = 0x0138; 248 | pub const WM_MOUSEMOVE = 0x0200; 249 | pub const WM_LBUTTONDOWN = 0x0201; 250 | pub const WM_LBUTTONUP = 0x0202; 251 | pub const WM_LBUTTONDBLCLK = 0x0203; 252 | pub const WM_RBUTTONDOWN = 0x0204; 253 | pub const WM_RBUTTONUP = 0x0205; 254 | pub const WM_RBUTTONDBLCLK = 0x0206; 255 | pub const WM_MBUTTONDOWN = 0x0207; 256 | pub const WM_MBUTTONUP = 0x0208; 257 | pub const WM_MBUTTONDBLCLK = 0x0209; 258 | pub const WM_MOUSEWHEEL = 0x020A; 259 | pub const WM_XBUTTONDOWN = 0x020B; 260 | pub const WM_XBUTTONUP = 0x020C; 261 | pub const WM_XBUTTONDBLCLK = 0x020D; 262 | pub const WM_MOUSEHWHEEL = 0x020E; 263 | pub const WM_PARENTNOTIFY = 0x0210; 264 | pub const WM_ENTERMENULOOP = 0x0211; 265 | pub const WM_EXITMENULOOP = 0x0212; 266 | pub const WM_NEXTMENU = 0x0213; 267 | pub const WM_SIZING = 0x0214; 268 | pub const WM_CAPTURECHANGED = 0x0215; 269 | pub const WM_MOVING = 0x0216; 270 | pub const WM_POWERBROADCAST = 0x0218; 271 | pub const WM_DEVICECHANGE = 0x0219; 272 | pub const WM_MDICREATE = 0x0220; 273 | pub const WM_MDIDESTROY = 0x0221; 274 | pub const WM_MDIACTIVATE = 0x0222; 275 | pub const WM_MDIRESTORE = 0x0223; 276 | pub const WM_MDINEXT = 0x0224; 277 | pub const WM_MDIMAXIMIZE = 0x0225; 278 | pub const WM_MDITILE = 0x0226; 279 | pub const WM_MDICASCADE = 0x0227; 280 | pub const WM_MDIICONARRANGE = 0x0228; 281 | pub const WM_MDIGETACTIVE = 0x0229; 282 | pub const WM_MDISETMENU = 0x0230; 283 | pub const WM_ENTERSIZEMOVE = 0x0231; 284 | pub const WM_EXITSIZEMOVE = 0x0232; 285 | pub const WM_DROPFILES = 0x0233; 286 | pub const WM_MDIREFRESHMENU = 0x0234; 287 | pub const WM_IME_REPORT = 0x0280; 288 | pub const WM_IME_SETCONTEXT = 0x0281; 289 | pub const WM_IME_NOTIFY = 0x0282; 290 | pub const WM_IME_CONTROL = 0x0283; 291 | pub const WM_IME_COMPOSITIONFULL = 0x0284; 292 | pub const WM_IME_SELECT = 0x0285; 293 | pub const WM_IME_CHAR = 0x0286; 294 | pub const WM_IME_REQUEST = 0x0288; 295 | pub const WM_IMEKEYDOWN = 0x0290; 296 | pub const WM_IME_KEYDOWN = 0x0290; 297 | pub const WM_IMEKEYUP = 0x0291; 298 | pub const WM_IME_KEYUP = 0x0291; 299 | pub const WM_NCMOUSEHOVER = 0x02A0; 300 | pub const WM_MOUSEHOVER = 0x02A1; 301 | pub const WM_NCMOUSELEAVE = 0x02A2; 302 | pub const WM_MOUSELEAVE = 0x02A3; 303 | pub const WM_CUT = 0x0300; 304 | pub const WM_COPY = 0x0301; 305 | pub const WM_PASTE = 0x0302; 306 | pub const WM_CLEAR = 0x0303; 307 | pub const WM_UNDO = 0x0304; 308 | pub const WM_RENDERFORMAT = 0x0305; 309 | pub const WM_RENDERALLFORMATS = 0x0306; 310 | pub const WM_DESTROYCLIPBOARD = 0x0307; 311 | pub const WM_DRAWCLIPBOARD = 0x0308; 312 | pub const WM_PAINTCLIPBOARD = 0x0309; 313 | pub const WM_VSCROLLCLIPBOARD = 0x030A; 314 | pub const WM_SIZECLIPBOARD = 0x030B; 315 | pub const WM_ASKCBFORMATNAME = 0x030C; 316 | pub const WM_CHANGECBCHAIN = 0x030D; 317 | pub const WM_HSCROLLCLIPBOARD = 0x030E; 318 | pub const WM_QUERYNEWPALETTE = 0x030F; 319 | pub const WM_PALETTEISCHANGING = 0x0310; 320 | pub const WM_PALETTECHANGED = 0x0311; 321 | pub const WM_HOTKEY = 0x0312; 322 | pub const WM_PRINT = 0x0317; 323 | pub const WM_PRINTCLIENT = 0x0318; 324 | pub const WM_APPCOMMAND = 0x0319; 325 | pub const WM_RCRESULT = 0x0381; 326 | pub const WM_HOOKRCRESULT = 0x0382; 327 | pub const WM_GLOBALRCCHANGE = 0x0383; 328 | pub const WM_PENMISCINFO = 0x0383; 329 | pub const WM_SKB = 0x0384; 330 | pub const WM_HEDITCTL = 0x0385; 331 | pub const WM_PENCTL = 0x0385; 332 | pub const WM_PENMISC = 0x0386; 333 | pub const WM_CTLINIT = 0x0387; 334 | pub const WM_PENEVENT = 0x0388; 335 | pub const WM_CARET_CREATE = 0x03E0; 336 | pub const WM_CARET_DESTROY = 0x03E1; 337 | pub const WM_CARET_BLINK = 0x03E2; 338 | pub const WM_FDINPUT = 0x03F0; 339 | pub const WM_FDOUTPUT = 0x03F1; 340 | pub const WM_FDEXCEPT = 0x03F2; 341 | pub const DDM_SETFMT = 0x0400; 342 | pub const DM_GETDEFID = 0x0400; 343 | pub const NIN_SELECT = 0x0400; 344 | pub const TBM_GETPOS = 0x0400; 345 | pub const WM_PSD_PAGESETUPDLG = 0x0400; 346 | pub const WM_USER = 0x0400; 347 | pub const CBEM_INSERTITEMA = 0x0401; 348 | pub const DDM_DRAW = 0x0401; 349 | pub const DM_SETDEFID = 0x0401; 350 | pub const HKM_SETHOTKEY = 0x0401; 351 | pub const PBM_SETRANGE = 0x0401; 352 | pub const RB_INSERTBANDA = 0x0401; 353 | pub const SB_SETTEXTA = 0x0401; 354 | pub const TB_ENABLEBUTTON = 0x0401; 355 | pub const TBM_GETRANGEMIN = 0x0401; 356 | pub const TTM_ACTIVATE = 0x0401; 357 | pub const WM_CHOOSEFONT_GETLOGFONT = 0x0401; 358 | pub const WM_PSD_FULLPAGERECT = 0x0401; 359 | pub const CBEM_SETIMAGELIST = 0x0402; 360 | pub const DDM_CLOSE = 0x0402; 361 | pub const DM_REPOSITION = 0x0402; 362 | pub const HKM_GETHOTKEY = 0x0402; 363 | pub const PBM_SETPOS = 0x0402; 364 | pub const RB_DELETEBAND = 0x0402; 365 | pub const SB_GETTEXTA = 0x0402; 366 | pub const TB_CHECKBUTTON = 0x0402; 367 | pub const TBM_GETRANGEMAX = 0x0402; 368 | pub const WM_PSD_MINMARGINRECT = 0x0402; 369 | pub const CBEM_GETIMAGELIST = 0x0403; 370 | pub const DDM_BEGIN = 0x0403; 371 | pub const HKM_SETRULES = 0x0403; 372 | pub const PBM_DELTAPOS = 0x0403; 373 | pub const RB_GETBARINFO = 0x0403; 374 | pub const SB_GETTEXTLENGTHA = 0x0403; 375 | pub const TBM_GETTIC = 0x0403; 376 | pub const TB_PRESSBUTTON = 0x0403; 377 | pub const TTM_SETDELAYTIME = 0x0403; 378 | pub const WM_PSD_MARGINRECT = 0x0403; 379 | pub const CBEM_GETITEMA = 0x0404; 380 | pub const DDM_END = 0x0404; 381 | pub const PBM_SETSTEP = 0x0404; 382 | pub const RB_SETBARINFO = 0x0404; 383 | pub const SB_SETPARTS = 0x0404; 384 | pub const TB_HIDEBUTTON = 0x0404; 385 | pub const TBM_SETTIC = 0x0404; 386 | pub const TTM_ADDTOOLA = 0x0404; 387 | pub const WM_PSD_GREEKTEXTRECT = 0x0404; 388 | pub const CBEM_SETITEMA = 0x0405; 389 | pub const PBM_STEPIT = 0x0405; 390 | pub const TB_INDETERMINATE = 0x0405; 391 | pub const TBM_SETPOS = 0x0405; 392 | pub const TTM_DELTOOLA = 0x0405; 393 | pub const WM_PSD_ENVSTAMPRECT = 0x0405; 394 | pub const CBEM_GETCOMBOCONTROL = 0x0406; 395 | pub const PBM_SETRANGE32 = 0x0406; 396 | pub const RB_SETBANDINFOA = 0x0406; 397 | pub const SB_GETPARTS = 0x0406; 398 | pub const TB_MARKBUTTON = 0x0406; 399 | pub const TBM_SETRANGE = 0x0406; 400 | pub const TTM_NEWTOOLRECTA = 0x0406; 401 | pub const WM_PSD_YAFULLPAGERECT = 0x0406; 402 | pub const CBEM_GETEDITCONTROL = 0x0407; 403 | pub const PBM_GETRANGE = 0x0407; 404 | pub const RB_SETPARENT = 0x0407; 405 | pub const SB_GETBORDERS = 0x0407; 406 | pub const TBM_SETRANGEMIN = 0x0407; 407 | pub const TTM_RELAYEVENT = 0x0407; 408 | pub const CBEM_SETEXSTYLE = 0x0408; 409 | pub const PBM_GETPOS = 0x0408; 410 | pub const RB_HITTEST = 0x0408; 411 | pub const SB_SETMINHEIGHT = 0x0408; 412 | pub const TBM_SETRANGEMAX = 0x0408; 413 | pub const TTM_GETTOOLINFOA = 0x0408; 414 | pub const CBEM_GETEXSTYLE = 0x0409; 415 | pub const CBEM_GETEXTENDEDSTYLE = 0x0409; 416 | pub const PBM_SETBARCOLOR = 0x0409; 417 | pub const RB_GETRECT = 0x0409; 418 | pub const SB_SIMPLE = 0x0409; 419 | pub const TB_ISBUTTONENABLED = 0x0409; 420 | pub const TBM_CLEARTICS = 0x0409; 421 | pub const TTM_SETTOOLINFOA = 0x0409; 422 | pub const CBEM_HASEDITCHANGED = 0x040A; 423 | pub const RB_INSERTBANDW = 0x040A; 424 | pub const SB_GETRECT = 0x040A; 425 | pub const TB_ISBUTTONCHECKED = 0x040A; 426 | pub const TBM_SETSEL = 0x040A; 427 | pub const TTM_HITTESTA = 0x040A; 428 | pub const WIZ_QUERYNUMPAGES = 0x040A; 429 | pub const CBEM_INSERTITEMW = 0x040B; 430 | pub const RB_SETBANDINFOW = 0x040B; 431 | pub const SB_SETTEXTW = 0x040B; 432 | pub const TB_ISBUTTONPRESSED = 0x040B; 433 | pub const TBM_SETSELSTART = 0x040B; 434 | pub const TTM_GETTEXTA = 0x040B; 435 | pub const WIZ_NEXT = 0x040B; 436 | pub const CBEM_SETITEMW = 0x040C; 437 | pub const RB_GETBANDCOUNT = 0x040C; 438 | pub const SB_GETTEXTLENGTHW = 0x040C; 439 | pub const TB_ISBUTTONHIDDEN = 0x040C; 440 | pub const TBM_SETSELEND = 0x040C; 441 | pub const TTM_UPDATETIPTEXTA = 0x040C; 442 | pub const WIZ_PREV = 0x040C; 443 | pub const CBEM_GETITEMW = 0x040D; 444 | pub const RB_GETROWCOUNT = 0x040D; 445 | pub const SB_GETTEXTW = 0x040D; 446 | pub const TB_ISBUTTONINDETERMINATE = 0x040D; 447 | pub const TTM_GETTOOLCOUNT = 0x040D; 448 | pub const CBEM_SETEXTENDEDSTYLE = 0x040E; 449 | pub const RB_GETROWHEIGHT = 0x040E; 450 | pub const SB_ISSIMPLE = 0x040E; 451 | pub const TB_ISBUTTONHIGHLIGHTED = 0x040E; 452 | pub const TBM_GETPTICS = 0x040E; 453 | pub const TTM_ENUMTOOLSA = 0x040E; 454 | pub const SB_SETICON = 0x040F; 455 | pub const TBM_GETTICPOS = 0x040F; 456 | pub const TTM_GETCURRENTTOOLA = 0x040F; 457 | pub const RB_IDTOINDEX = 0x0410; 458 | pub const SB_SETTIPTEXTA = 0x0410; 459 | pub const TBM_GETNUMTICS = 0x0410; 460 | pub const TTM_WINDOWFROMPOINT = 0x0410; 461 | pub const RB_GETTOOLTIPS = 0x0411; 462 | pub const SB_SETTIPTEXTW = 0x0411; 463 | pub const TBM_GETSELSTART = 0x0411; 464 | pub const TB_SETSTATE = 0x0411; 465 | pub const TTM_TRACKACTIVATE = 0x0411; 466 | pub const RB_SETTOOLTIPS = 0x0412; 467 | pub const SB_GETTIPTEXTA = 0x0412; 468 | pub const TB_GETSTATE = 0x0412; 469 | pub const TBM_GETSELEND = 0x0412; 470 | pub const TTM_TRACKPOSITION = 0x0412; 471 | pub const RB_SETBKCOLOR = 0x0413; 472 | pub const SB_GETTIPTEXTW = 0x0413; 473 | pub const TB_ADDBITMAP = 0x0413; 474 | pub const TBM_CLEARSEL = 0x0413; 475 | pub const TTM_SETTIPBKCOLOR = 0x0413; 476 | pub const RB_GETBKCOLOR = 0x0414; 477 | pub const SB_GETICON = 0x0414; 478 | pub const TB_ADDBUTTONSA = 0x0414; 479 | pub const TBM_SETTICFREQ = 0x0414; 480 | pub const TTM_SETTIPTEXTCOLOR = 0x0414; 481 | pub const RB_SETTEXTCOLOR = 0x0415; 482 | pub const TB_INSERTBUTTONA = 0x0415; 483 | pub const TBM_SETPAGESIZE = 0x0415; 484 | pub const TTM_GETDELAYTIME = 0x0415; 485 | pub const RB_GETTEXTCOLOR = 0x0416; 486 | pub const TB_DELETEBUTTON = 0x0416; 487 | pub const TBM_GETPAGESIZE = 0x0416; 488 | pub const TTM_GETTIPBKCOLOR = 0x0416; 489 | pub const RB_SIZETORECT = 0x0417; 490 | pub const TB_GETBUTTON = 0x0417; 491 | pub const TBM_SETLINESIZE = 0x0417; 492 | pub const TTM_GETTIPTEXTCOLOR = 0x0417; 493 | pub const RB_BEGINDRAG = 0x0418; 494 | pub const TB_BUTTONCOUNT = 0x0418; 495 | pub const TBM_GETLINESIZE = 0x0418; 496 | pub const TTM_SETMAXTIPWIDTH = 0x0418; 497 | pub const RB_ENDDRAG = 0x0419; 498 | pub const TB_COMMANDTOINDEX = 0x0419; 499 | pub const TBM_GETTHUMBRECT = 0x0419; 500 | pub const TTM_GETMAXTIPWIDTH = 0x0419; 501 | pub const RB_DRAGMOVE = 0x041A; 502 | pub const TBM_GETCHANNELRECT = 0x041A; 503 | pub const TB_SAVERESTOREA = 0x041A; 504 | pub const TTM_SETMARGIN = 0x041A; 505 | pub const RB_GETBARHEIGHT = 0x041B; 506 | pub const TB_CUSTOMIZE = 0x041B; 507 | pub const TBM_SETTHUMBLENGTH = 0x041B; 508 | pub const TTM_GETMARGIN = 0x041B; 509 | pub const RB_GETBANDINFOW = 0x041C; 510 | pub const TB_ADDSTRINGA = 0x041C; 511 | pub const TBM_GETTHUMBLENGTH = 0x041C; 512 | pub const TTM_POP = 0x041C; 513 | pub const RB_GETBANDINFOA = 0x041D; 514 | pub const TB_GETITEMRECT = 0x041D; 515 | pub const TBM_SETTOOLTIPS = 0x041D; 516 | pub const TTM_UPDATE = 0x041D; 517 | pub const RB_MINIMIZEBAND = 0x041E; 518 | pub const TB_BUTTONSTRUCTSIZE = 0x041E; 519 | pub const TBM_GETTOOLTIPS = 0x041E; 520 | pub const TTM_GETBUBBLESIZE = 0x041E; 521 | pub const RB_MAXIMIZEBAND = 0x041F; 522 | pub const TBM_SETTIPSIDE = 0x041F; 523 | pub const TB_SETBUTTONSIZE = 0x041F; 524 | pub const TTM_ADJUSTRECT = 0x041F; 525 | pub const TBM_SETBUDDY = 0x0420; 526 | pub const TB_SETBITMAPSIZE = 0x0420; 527 | pub const TTM_SETTITLEA = 0x0420; 528 | pub const MSG_FTS_JUMP_VA = 0x0421; 529 | pub const TB_AUTOSIZE = 0x0421; 530 | pub const TBM_GETBUDDY = 0x0421; 531 | pub const TTM_SETTITLEW = 0x0421; 532 | pub const RB_GETBANDBORDERS = 0x0422; 533 | pub const MSG_FTS_JUMP_QWORD = 0x0423; 534 | pub const RB_SHOWBAND = 0x0423; 535 | pub const TB_GETTOOLTIPS = 0x0423; 536 | pub const MSG_REINDEX_REQUEST = 0x0424; 537 | pub const TB_SETTOOLTIPS = 0x0424; 538 | pub const MSG_FTS_WHERE_IS_IT = 0x0425; 539 | pub const RB_SETPALETTE = 0x0425; 540 | pub const TB_SETPARENT = 0x0425; 541 | pub const RB_GETPALETTE = 0x0426; 542 | pub const RB_MOVEBAND = 0x0427; 543 | pub const TB_SETROWS = 0x0427; 544 | pub const TB_GETROWS = 0x0428; 545 | pub const TB_GETBITMAPFLAGS = 0x0429; 546 | pub const TB_SETCMDID = 0x042A; 547 | pub const RB_PUSHCHEVRON = 0x042B; 548 | pub const TB_CHANGEBITMAP = 0x042B; 549 | pub const TB_GETBITMAP = 0x042C; 550 | pub const MSG_GET_DEFFONT = 0x042D; 551 | pub const TB_GETBUTTONTEXTA = 0x042D; 552 | pub const TB_REPLACEBITMAP = 0x042E; 553 | pub const TB_SETINDENT = 0x042F; 554 | pub const TB_SETIMAGELIST = 0x0430; 555 | pub const TB_GETIMAGELIST = 0x0431; 556 | pub const TB_LOADIMAGES = 0x0432; 557 | pub const EM_CANPASTE = 0x0432; 558 | pub const TTM_ADDTOOLW = 0x0432; 559 | pub const EM_DISPLAYBAND = 0x0433; 560 | pub const TB_GETRECT = 0x0433; 561 | pub const TTM_DELTOOLW = 0x0433; 562 | pub const EM_EXGETSEL = 0x0434; 563 | pub const TB_SETHOTIMAGELIST = 0x0434; 564 | pub const TTM_NEWTOOLRECTW = 0x0434; 565 | pub const EM_EXLIMITTEXT = 0x0435; 566 | pub const TB_GETHOTIMAGELIST = 0x0435; 567 | pub const TTM_GETTOOLINFOW = 0x0435; 568 | pub const EM_EXLINEFROMCHAR = 0x0436; 569 | pub const TB_SETDISABLEDIMAGELIST = 0x0436; 570 | pub const TTM_SETTOOLINFOW = 0x0436; 571 | pub const EM_EXSETSEL = 0x0437; 572 | pub const TB_GETDISABLEDIMAGELIST = 0x0437; 573 | pub const TTM_HITTESTW = 0x0437; 574 | pub const EM_FINDTEXT = 0x0438; 575 | pub const TB_SETSTYLE = 0x0438; 576 | pub const TTM_GETTEXTW = 0x0438; 577 | pub const EM_FORMATRANGE = 0x0439; 578 | pub const TB_GETSTYLE = 0x0439; 579 | pub const TTM_UPDATETIPTEXTW = 0x0439; 580 | pub const EM_GETCHARFORMAT = 0x043A; 581 | pub const TB_GETBUTTONSIZE = 0x043A; 582 | pub const TTM_ENUMTOOLSW = 0x043A; 583 | pub const EM_GETEVENTMASK = 0x043B; 584 | pub const TB_SETBUTTONWIDTH = 0x043B; 585 | pub const TTM_GETCURRENTTOOLW = 0x043B; 586 | pub const EM_GETOLEINTERFACE = 0x043C; 587 | pub const TB_SETMAXTEXTROWS = 0x043C; 588 | pub const EM_GETPARAFORMAT = 0x043D; 589 | pub const TB_GETTEXTROWS = 0x043D; 590 | pub const EM_GETSELTEXT = 0x043E; 591 | pub const TB_GETOBJECT = 0x043E; 592 | pub const EM_HIDESELECTION = 0x043F; 593 | pub const TB_GETBUTTONINFOW = 0x043F; 594 | pub const EM_PASTESPECIAL = 0x0440; 595 | pub const TB_SETBUTTONINFOW = 0x0440; 596 | pub const EM_REQUESTRESIZE = 0x0441; 597 | pub const TB_GETBUTTONINFOA = 0x0441; 598 | pub const EM_SELECTIONTYPE = 0x0442; 599 | pub const TB_SETBUTTONINFOA = 0x0442; 600 | pub const EM_SETBKGNDCOLOR = 0x0443; 601 | pub const TB_INSERTBUTTONW = 0x0443; 602 | pub const EM_SETCHARFORMAT = 0x0444; 603 | pub const TB_ADDBUTTONSW = 0x0444; 604 | pub const EM_SETEVENTMASK = 0x0445; 605 | pub const TB_HITTEST = 0x0445; 606 | pub const EM_SETOLECALLBACK = 0x0446; 607 | pub const TB_SETDRAWTEXTFLAGS = 0x0446; 608 | pub const EM_SETPARAFORMAT = 0x0447; 609 | pub const TB_GETHOTITEM = 0x0447; 610 | pub const EM_SETTARGETDEVICE = 0x0448; 611 | pub const TB_SETHOTITEM = 0x0448; 612 | pub const EM_STREAMIN = 0x0449; 613 | pub const TB_SETANCHORHIGHLIGHT = 0x0449; 614 | pub const EM_STREAMOUT = 0x044A; 615 | pub const TB_GETANCHORHIGHLIGHT = 0x044A; 616 | pub const EM_GETTEXTRANGE = 0x044B; 617 | pub const TB_GETBUTTONTEXTW = 0x044B; 618 | pub const EM_FINDWORDBREAK = 0x044C; 619 | pub const TB_SAVERESTOREW = 0x044C; 620 | pub const EM_SETOPTIONS = 0x044D; 621 | pub const TB_ADDSTRINGW = 0x044D; 622 | pub const EM_GETOPTIONS = 0x044E; 623 | pub const TB_MAPACCELERATORA = 0x044E; 624 | pub const EM_FINDTEXTEX = 0x044F; 625 | pub const TB_GETINSERTMARK = 0x044F; 626 | pub const EM_GETWORDBREAKPROCEX = 0x0450; 627 | pub const TB_SETINSERTMARK = 0x0450; 628 | pub const EM_SETWORDBREAKPROCEX = 0x0451; 629 | pub const TB_INSERTMARKHITTEST = 0x0451; 630 | pub const EM_SETUNDOLIMIT = 0x0452; 631 | pub const TB_MOVEBUTTON = 0x0452; 632 | pub const TB_GETMAXSIZE = 0x0453; 633 | pub const EM_REDO = 0x0454; 634 | pub const TB_SETEXTENDEDSTYLE = 0x0454; 635 | pub const EM_CANREDO = 0x0455; 636 | pub const TB_GETEXTENDEDSTYLE = 0x0455; 637 | pub const EM_GETUNDONAME = 0x0456; 638 | pub const TB_GETPADDING = 0x0456; 639 | pub const EM_GETREDONAME = 0x0457; 640 | pub const TB_SETPADDING = 0x0457; 641 | pub const EM_STOPGROUPTYPING = 0x0458; 642 | pub const TB_SETINSERTMARKCOLOR = 0x0458; 643 | pub const EM_SETTEXTMODE = 0x0459; 644 | pub const TB_GETINSERTMARKCOLOR = 0x0459; 645 | pub const EM_GETTEXTMODE = 0x045A; 646 | pub const TB_MAPACCELERATORW = 0x045A; 647 | pub const EM_AUTOURLDETECT = 0x045B; 648 | pub const TB_GETSTRINGW = 0x045B; 649 | pub const EM_GETAUTOURLDETECT = 0x045C; 650 | pub const TB_GETSTRINGA = 0x045C; 651 | pub const EM_SETPALETTE = 0x045D; 652 | pub const EM_GETTEXTEX = 0x045E; 653 | pub const EM_GETTEXTLENGTHEX = 0x045F; 654 | pub const EM_SHOWSCROLLBAR = 0x0460; 655 | pub const EM_SETTEXTEX = 0x0461; 656 | pub const TAPI_REPLY = 0x0463; 657 | pub const ACM_OPENA = 0x0464; 658 | pub const BFFM_SETSTATUSTEXTA = 0x0464; 659 | pub const CDM_GETSPEC = 0x0464; 660 | pub const EM_SETPUNCTUATION = 0x0464; 661 | pub const IPM_CLEARADDRESS = 0x0464; 662 | pub const WM_CAP_UNICODE_START = 0x0464; 663 | pub const ACM_PLAY = 0x0465; 664 | pub const BFFM_ENABLEOK = 0x0465; 665 | pub const CDM_GETFILEPATH = 0x0465; 666 | pub const EM_GETPUNCTUATION = 0x0465; 667 | pub const IPM_SETADDRESS = 0x0465; 668 | pub const PSM_SETCURSEL = 0x0465; 669 | pub const UDM_SETRANGE = 0x0465; 670 | pub const WM_CHOOSEFONT_SETLOGFONT = 0x0465; 671 | pub const ACM_STOP = 0x0466; 672 | pub const BFFM_SETSELECTIONA = 0x0466; 673 | pub const CDM_GETFOLDERPATH = 0x0466; 674 | pub const EM_SETWORDWRAPMODE = 0x0466; 675 | pub const IPM_GETADDRESS = 0x0466; 676 | pub const PSM_REMOVEPAGE = 0x0466; 677 | pub const UDM_GETRANGE = 0x0466; 678 | pub const WM_CAP_SET_CALLBACK_ERRORW = 0x0466; 679 | pub const WM_CHOOSEFONT_SETFLAGS = 0x0466; 680 | pub const ACM_OPENW = 0x0467; 681 | pub const BFFM_SETSELECTIONW = 0x0467; 682 | pub const CDM_GETFOLDERIDLIST = 0x0467; 683 | pub const EM_GETWORDWRAPMODE = 0x0467; 684 | pub const IPM_SETRANGE = 0x0467; 685 | pub const PSM_ADDPAGE = 0x0467; 686 | pub const UDM_SETPOS = 0x0467; 687 | pub const WM_CAP_SET_CALLBACK_STATUSW = 0x0467; 688 | pub const BFFM_SETSTATUSTEXTW = 0x0468; 689 | pub const CDM_SETCONTROLTEXT = 0x0468; 690 | pub const EM_SETIMECOLOR = 0x0468; 691 | pub const IPM_SETFOCUS = 0x0468; 692 | pub const PSM_CHANGED = 0x0468; 693 | pub const UDM_GETPOS = 0x0468; 694 | pub const CDM_HIDECONTROL = 0x0469; 695 | pub const EM_GETIMECOLOR = 0x0469; 696 | pub const IPM_ISBLANK = 0x0469; 697 | pub const PSM_RESTARTWINDOWS = 0x0469; 698 | pub const UDM_SETBUDDY = 0x0469; 699 | pub const CDM_SETDEFEXT = 0x046A; 700 | pub const EM_SETIMEOPTIONS = 0x046A; 701 | pub const PSM_REBOOTSYSTEM = 0x046A; 702 | pub const UDM_GETBUDDY = 0x046A; 703 | pub const EM_GETIMEOPTIONS = 0x046B; 704 | pub const PSM_CANCELTOCLOSE = 0x046B; 705 | pub const UDM_SETACCEL = 0x046B; 706 | pub const EM_CONVPOSITION = 0x046C; 707 | pub const PSM_QUERYSIBLINGS = 0x046C; 708 | pub const UDM_GETACCEL = 0x046C; 709 | pub const MCIWNDM_GETZOOM = 0x046D; 710 | pub const PSM_UNCHANGED = 0x046D; 711 | pub const UDM_SETBASE = 0x046D; 712 | pub const PSM_APPLY = 0x046E; 713 | pub const UDM_GETBASE = 0x046E; 714 | pub const PSM_SETTITLEA = 0x046F; 715 | pub const UDM_SETRANGE32 = 0x046F; 716 | pub const PSM_SETWIZBUTTONS = 0x0470; 717 | pub const UDM_GETRANGE32 = 0x0470; 718 | pub const WM_CAP_DRIVER_GET_NAMEW = 0x0470; 719 | pub const PSM_PRESSBUTTON = 0x0471; 720 | pub const UDM_SETPOS32 = 0x0471; 721 | pub const WM_CAP_DRIVER_GET_VERSIONW = 0x0471; 722 | pub const PSM_SETCURSELID = 0x0472; 723 | pub const UDM_GETPOS32 = 0x0472; 724 | pub const PSM_SETFINISHTEXTA = 0x0473; 725 | pub const PSM_GETTABCONTROL = 0x0474; 726 | pub const PSM_ISDIALOGMESSAGE = 0x0475; 727 | pub const MCIWNDM_REALIZE = 0x0476; 728 | pub const PSM_GETCURRENTPAGEHWND = 0x0476; 729 | pub const MCIWNDM_SETTIMEFORMATA = 0x0477; 730 | pub const PSM_INSERTPAGE = 0x0477; 731 | pub const EM_SETLANGOPTIONS = 0x0478; 732 | pub const MCIWNDM_GETTIMEFORMATA = 0x0478; 733 | pub const PSM_SETTITLEW = 0x0478; 734 | pub const WM_CAP_FILE_SET_CAPTURE_FILEW = 0x0478; 735 | pub const EM_GETLANGOPTIONS = 0x0479; 736 | pub const MCIWNDM_VALIDATEMEDIA = 0x0479; 737 | pub const PSM_SETFINISHTEXTW = 0x0479; 738 | pub const WM_CAP_FILE_GET_CAPTURE_FILEW = 0x0479; 739 | pub const EM_GETIMECOMPMODE = 0x047A; 740 | pub const EM_FINDTEXTW = 0x047B; 741 | pub const MCIWNDM_PLAYTO = 0x047B; 742 | pub const WM_CAP_FILE_SAVEASW = 0x047B; 743 | pub const EM_FINDTEXTEXW = 0x047C; 744 | pub const MCIWNDM_GETFILENAMEA = 0x047C; 745 | pub const EM_RECONVERSION = 0x047D; 746 | pub const MCIWNDM_GETDEVICEA = 0x047D; 747 | pub const PSM_SETHEADERTITLEA = 0x047D; 748 | pub const WM_CAP_FILE_SAVEDIBW = 0x047D; 749 | pub const EM_SETIMEMODEBIAS = 0x047E; 750 | pub const MCIWNDM_GETPALETTE = 0x047E; 751 | pub const PSM_SETHEADERTITLEW = 0x047E; 752 | pub const EM_GETIMEMODEBIAS = 0x047F; 753 | pub const MCIWNDM_SETPALETTE = 0x047F; 754 | pub const PSM_SETHEADERSUBTITLEA = 0x047F; 755 | pub const MCIWNDM_GETERRORA = 0x0480; 756 | pub const PSM_SETHEADERSUBTITLEW = 0x0480; 757 | pub const PSM_HWNDTOINDEX = 0x0481; 758 | pub const PSM_INDEXTOHWND = 0x0482; 759 | pub const MCIWNDM_SETINACTIVETIMER = 0x0483; 760 | pub const PSM_PAGETOINDEX = 0x0483; 761 | pub const PSM_INDEXTOPAGE = 0x0484; 762 | pub const DL_BEGINDRAG = 0x0485; 763 | pub const MCIWNDM_GETINACTIVETIMER = 0x0485; 764 | pub const PSM_IDTOINDEX = 0x0485; 765 | pub const DL_DRAGGING = 0x0486; 766 | pub const PSM_INDEXTOID = 0x0486; 767 | pub const DL_DROPPED = 0x0487; 768 | pub const PSM_GETRESULT = 0x0487; 769 | pub const DL_CANCELDRAG = 0x0488; 770 | pub const PSM_RECALCPAGESIZES = 0x0488; 771 | pub const MCIWNDM_GET_SOURCE = 0x048C; 772 | pub const MCIWNDM_PUT_SOURCE = 0x048D; 773 | pub const MCIWNDM_GET_DEST = 0x048E; 774 | pub const MCIWNDM_PUT_DEST = 0x048F; 775 | pub const MCIWNDM_CAN_PLAY = 0x0490; 776 | pub const MCIWNDM_CAN_WINDOW = 0x0491; 777 | pub const MCIWNDM_CAN_RECORD = 0x0492; 778 | pub const MCIWNDM_CAN_SAVE = 0x0493; 779 | pub const MCIWNDM_CAN_EJECT = 0x0494; 780 | pub const MCIWNDM_CAN_CONFIG = 0x0495; 781 | pub const IE_GETINK = 0x0496; 782 | pub const MCIWNDM_PALETTEKICK = 0x0496; 783 | pub const IE_SETINK = 0x0497; 784 | pub const IE_GETPENTIP = 0x0498; 785 | pub const IE_SETPENTIP = 0x0499; 786 | pub const IE_GETERASERTIP = 0x049A; 787 | pub const IE_SETERASERTIP = 0x049B; 788 | pub const IE_GETBKGND = 0x049C; 789 | pub const IE_SETBKGND = 0x049D; 790 | pub const IE_GETGRIDORIGIN = 0x049E; 791 | pub const IE_SETGRIDORIGIN = 0x049F; 792 | pub const IE_GETGRIDPEN = 0x04A0; 793 | pub const IE_SETGRIDPEN = 0x04A1; 794 | pub const IE_GETGRIDSIZE = 0x04A2; 795 | pub const IE_SETGRIDSIZE = 0x04A3; 796 | pub const IE_GETMODE = 0x04A4; 797 | pub const IE_SETMODE = 0x04A5; 798 | pub const IE_GETINKRECT = 0x04A6; 799 | pub const WM_CAP_SET_MCI_DEVICEW = 0x04A6; 800 | pub const WM_CAP_GET_MCI_DEVICEW = 0x04A7; 801 | pub const WM_CAP_PAL_OPENW = 0x04B4; 802 | pub const WM_CAP_PAL_SAVEW = 0x04B5; 803 | pub const IE_GETAPPDATA = 0x04B8; 804 | pub const IE_SETAPPDATA = 0x04B9; 805 | pub const IE_GETDRAWOPTS = 0x04BA; 806 | pub const IE_SETDRAWOPTS = 0x04BB; 807 | pub const IE_GETFORMAT = 0x04BC; 808 | pub const IE_SETFORMAT = 0x04BD; 809 | pub const IE_GETINKINPUT = 0x04BE; 810 | pub const IE_SETINKINPUT = 0x04BF; 811 | pub const IE_GETNOTIFY = 0x04C0; 812 | pub const IE_SETNOTIFY = 0x04C1; 813 | pub const IE_GETRECOG = 0x04C2; 814 | pub const IE_SETRECOG = 0x04C3; 815 | pub const IE_GETSECURITY = 0x04C4; 816 | pub const IE_SETSECURITY = 0x04C5; 817 | pub const IE_GETSEL = 0x04C6; 818 | pub const IE_SETSEL = 0x04C7; 819 | pub const EM_SETBIDIOPTIONS = 0x04C8; 820 | pub const IE_DOCOMMAND = 0x04C8; 821 | pub const MCIWNDM_NOTIFYMODE = 0x04C8; 822 | pub const EM_GETBIDIOPTIONS = 0x04C9; 823 | pub const IE_GETCOMMAND = 0x04C9; 824 | pub const EM_SETTYPOGRAPHYOPTIONS = 0x04CA; 825 | pub const IE_GETCOUNT = 0x04CA; 826 | pub const EM_GETTYPOGRAPHYOPTIONS = 0x04CB; 827 | pub const IE_GETGESTURE = 0x04CB; 828 | pub const MCIWNDM_NOTIFYMEDIA = 0x04CB; 829 | pub const EM_SETEDITSTYLE = 0x04CC; 830 | pub const IE_GETMENU = 0x04CC; 831 | pub const EM_GETEDITSTYLE = 0x04CD; 832 | pub const IE_GETPAINTDC = 0x04CD; 833 | pub const MCIWNDM_NOTIFYERROR = 0x04CD; 834 | pub const IE_GETPDEVENT = 0x04CE; 835 | pub const IE_GETSELCOUNT = 0x04CF; 836 | pub const IE_GETSELITEMS = 0x04D0; 837 | pub const IE_GETSTYLE = 0x04D1; 838 | pub const MCIWNDM_SETTIMEFORMATW = 0x04DB; 839 | pub const EM_OUTLINE = 0x04DC; 840 | pub const MCIWNDM_GETTIMEFORMATW = 0x04DC; 841 | pub const EM_GETSCROLLPOS = 0x04DD; 842 | pub const EM_SETSCROLLPOS = 0x04DE; 843 | pub const EM_SETFONTSIZE = 0x04DF; 844 | pub const EM_GETZOOM = 0x04E0; 845 | pub const MCIWNDM_GETFILENAMEW = 0x04E0; 846 | pub const EM_SETZOOM = 0x04E1; 847 | pub const MCIWNDM_GETDEVICEW = 0x04E1; 848 | pub const EM_GETVIEWKIND = 0x04E2; 849 | pub const EM_SETVIEWKIND = 0x04E3; 850 | pub const EM_GETPAGE = 0x04E4; 851 | pub const MCIWNDM_GETERRORW = 0x04E4; 852 | pub const EM_SETPAGE = 0x04E5; 853 | pub const EM_GETHYPHENATEINFO = 0x04E6; 854 | pub const EM_SETHYPHENATEINFO = 0x04E7; 855 | pub const EM_GETPAGEROTATE = 0x04EB; 856 | pub const EM_SETPAGEROTATE = 0x04EC; 857 | pub const EM_GETCTFMODEBIAS = 0x04ED; 858 | pub const EM_SETCTFMODEBIAS = 0x04EE; 859 | pub const EM_GETCTFOPENSTATUS = 0x04F0; 860 | pub const EM_SETCTFOPENSTATUS = 0x04F1; 861 | pub const EM_GETIMECOMPTEXT = 0x04F2; 862 | pub const EM_ISIME = 0x04F3; 863 | pub const EM_GETIMEPROPERTY = 0x04F4; 864 | pub const EM_GETQUERYRTFOBJ = 0x050D; 865 | pub const EM_SETQUERYRTFOBJ = 0x050E; 866 | pub const FM_GETFOCUS = 0x0600; 867 | pub const FM_GETDRIVEINFOA = 0x0601; 868 | pub const FM_GETSELCOUNT = 0x0602; 869 | pub const FM_GETSELCOUNTLFN = 0x0603; 870 | pub const FM_GETFILESELA = 0x0604; 871 | pub const FM_GETFILESELLFNA = 0x0605; 872 | pub const FM_REFRESH_WINDOWS = 0x0606; 873 | pub const FM_RELOAD_EXTENSIONS = 0x0607; 874 | pub const FM_GETDRIVEINFOW = 0x0611; 875 | pub const FM_GETFILESELW = 0x0614; 876 | pub const FM_GETFILESELLFNW = 0x0615; 877 | pub const WLX_WM_SAS = 0x0659; 878 | pub const SM_GETSELCOUNT = 0x07E8; 879 | pub const UM_GETSELCOUNT = 0x07E8; 880 | pub const WM_CPL_LAUNCH = 0x07E8; 881 | pub const SM_GETSERVERSELA = 0x07E9; 882 | pub const UM_GETUSERSELA = 0x07E9; 883 | pub const WM_CPL_LAUNCHED = 0x07E9; 884 | pub const SM_GETSERVERSELW = 0x07EA; 885 | pub const UM_GETUSERSELW = 0x07EA; 886 | pub const SM_GETCURFOCUSA = 0x07EB; 887 | pub const UM_GETGROUPSELA = 0x07EB; 888 | pub const SM_GETCURFOCUSW = 0x07EC; 889 | pub const UM_GETGROUPSELW = 0x07EC; 890 | pub const SM_GETOPTIONS = 0x07ED; 891 | pub const UM_GETCURFOCUSA = 0x07ED; 892 | pub const UM_GETCURFOCUSW = 0x07EE; 893 | pub const UM_GETOPTIONS = 0x07EF; 894 | pub const UM_GETOPTIONS2 = 0x07F0; 895 | pub const LVM_GETBKCOLOR = 0x1000; 896 | pub const LVM_SETBKCOLOR = 0x1001; 897 | pub const LVM_GETIMAGELIST = 0x1002; 898 | pub const LVM_SETIMAGELIST = 0x1003; 899 | pub const LVM_GETITEMCOUNT = 0x1004; 900 | pub const LVM_GETITEMA = 0x1005; 901 | pub const LVM_SETITEMA = 0x1006; 902 | pub const LVM_INSERTITEMA = 0x1007; 903 | pub const LVM_DELETEITEM = 0x1008; 904 | pub const LVM_DELETEALLITEMS = 0x1009; 905 | pub const LVM_GETCALLBACKMASK = 0x100A; 906 | pub const LVM_SETCALLBACKMASK = 0x100B; 907 | pub const LVM_GETNEXTITEM = 0x100C; 908 | pub const LVM_FINDITEMA = 0x100D; 909 | pub const LVM_GETITEMRECT = 0x100E; 910 | pub const LVM_SETITEMPOSITION = 0x100F; 911 | pub const LVM_GETITEMPOSITION = 0x1010; 912 | pub const LVM_GETSTRINGWIDTHA = 0x1011; 913 | pub const LVM_HITTEST = 0x1012; 914 | pub const LVM_ENSUREVISIBLE = 0x1013; 915 | pub const LVM_SCROLL = 0x1014; 916 | pub const LVM_REDRAWITEMS = 0x1015; 917 | pub const LVM_ARRANGE = 0x1016; 918 | pub const LVM_EDITLABELA = 0x1017; 919 | pub const LVM_GETEDITCONTROL = 0x1018; 920 | pub const LVM_GETCOLUMNA = 0x1019; 921 | pub const LVM_SETCOLUMNA = 0x101A; 922 | pub const LVM_INSERTCOLUMNA = 0x101B; 923 | pub const LVM_DELETECOLUMN = 0x101C; 924 | pub const LVM_GETCOLUMNWIDTH = 0x101D; 925 | pub const LVM_SETCOLUMNWIDTH = 0x101E; 926 | pub const LVM_GETHEADER = 0x101F; 927 | pub const LVM_CREATEDRAGIMAGE = 0x1021; 928 | pub const LVM_GETVIEWRECT = 0x1022; 929 | pub const LVM_GETTEXTCOLOR = 0x1023; 930 | pub const LVM_SETTEXTCOLOR = 0x1024; 931 | pub const LVM_GETTEXTBKCOLOR = 0x1025; 932 | pub const LVM_SETTEXTBKCOLOR = 0x1026; 933 | pub const LVM_GETTOPINDEX = 0x1027; 934 | pub const LVM_GETCOUNTPERPAGE = 0x1028; 935 | pub const LVM_GETORIGIN = 0x1029; 936 | pub const LVM_UPDATE = 0x102A; 937 | pub const LVM_SETITEMSTATE = 0x102B; 938 | pub const LVM_GETITEMSTATE = 0x102C; 939 | pub const LVM_GETITEMTEXTA = 0x102D; 940 | pub const LVM_SETITEMTEXTA = 0x102E; 941 | pub const LVM_SETITEMCOUNT = 0x102F; 942 | pub const LVM_SORTITEMS = 0x1030; 943 | pub const LVM_SETITEMPOSITION32 = 0x1031; 944 | pub const LVM_GETSELECTEDCOUNT = 0x1032; 945 | pub const LVM_GETITEMSPACING = 0x1033; 946 | pub const LVM_GETISEARCHSTRINGA = 0x1034; 947 | pub const LVM_SETICONSPACING = 0x1035; 948 | pub const LVM_SETEXTENDEDLISTVIEWSTYLE = 0x1036; 949 | pub const LVM_GETEXTENDEDLISTVIEWSTYLE = 0x1037; 950 | pub const LVM_GETSUBITEMRECT = 0x1038; 951 | pub const LVM_SUBITEMHITTEST = 0x1039; 952 | pub const LVM_SETCOLUMNORDERARRAY = 0x103A; 953 | pub const LVM_GETCOLUMNORDERARRAY = 0x103B; 954 | pub const LVM_SETHOTITEM = 0x103C; 955 | pub const LVM_GETHOTITEM = 0x103D; 956 | pub const LVM_SETHOTCURSOR = 0x103E; 957 | pub const LVM_GETHOTCURSOR = 0x103F; 958 | pub const LVM_APPROXIMATEVIEWRECT = 0x1040; 959 | pub const LVM_SETWORKAREAS = 0x1041; 960 | pub const LVM_GETSELECTIONMARK = 0x1042; 961 | pub const LVM_SETSELECTIONMARK = 0x1043; 962 | pub const LVM_SETBKIMAGEA = 0x1044; 963 | pub const LVM_GETBKIMAGEA = 0x1045; 964 | pub const LVM_GETWORKAREAS = 0x1046; 965 | pub const LVM_SETHOVERTIME = 0x1047; 966 | pub const LVM_GETHOVERTIME = 0x1048; 967 | pub const LVM_GETNUMBEROFWORKAREAS = 0x1049; 968 | pub const LVM_SETTOOLTIPS = 0x104A; 969 | pub const LVM_GETITEMW = 0x104B; 970 | pub const LVM_SETITEMW = 0x104C; 971 | pub const LVM_INSERTITEMW = 0x104D; 972 | pub const LVM_GETTOOLTIPS = 0x104E; 973 | pub const LVM_FINDITEMW = 0x1053; 974 | pub const LVM_GETSTRINGWIDTHW = 0x1057; 975 | pub const LVM_GETCOLUMNW = 0x105F; 976 | pub const LVM_SETCOLUMNW = 0x1060; 977 | pub const LVM_INSERTCOLUMNW = 0x1061; 978 | pub const LVM_GETITEMTEXTW = 0x1073; 979 | pub const LVM_SETITEMTEXTW = 0x1074; 980 | pub const LVM_GETISEARCHSTRINGW = 0x1075; 981 | pub const LVM_EDITLABELW = 0x1076; 982 | pub const LVM_GETBKIMAGEW = 0x108B; 983 | pub const LVM_SETSELECTEDCOLUMN = 0x108C; 984 | pub const LVM_SETTILEWIDTH = 0x108D; 985 | pub const LVM_SETVIEW = 0x108E; 986 | pub const LVM_GETVIEW = 0x108F; 987 | pub const LVM_INSERTGROUP = 0x1091; 988 | pub const LVM_SETGROUPINFO = 0x1093; 989 | pub const LVM_GETGROUPINFO = 0x1095; 990 | pub const LVM_REMOVEGROUP = 0x1096; 991 | pub const LVM_MOVEGROUP = 0x1097; 992 | pub const LVM_MOVEITEMTOGROUP = 0x109A; 993 | pub const LVM_SETGROUPMETRICS = 0x109B; 994 | pub const LVM_GETGROUPMETRICS = 0x109C; 995 | pub const LVM_ENABLEGROUPVIEW = 0x109D; 996 | pub const LVM_SORTGROUPS = 0x109E; 997 | pub const LVM_INSERTGROUPSORTED = 0x109F; 998 | pub const LVM_REMOVEALLGROUPS = 0x10A0; 999 | pub const LVM_HASGROUP = 0x10A1; 1000 | pub const LVM_SETTILEVIEWINFO = 0x10A2; 1001 | pub const LVM_GETTILEVIEWINFO = 0x10A3; 1002 | pub const LVM_SETTILEINFO = 0x10A4; 1003 | pub const LVM_GETTILEINFO = 0x10A5; 1004 | pub const LVM_SETINSERTMARK = 0x10A6; 1005 | pub const LVM_GETINSERTMARK = 0x10A7; 1006 | pub const LVM_INSERTMARKHITTEST = 0x10A8; 1007 | pub const LVM_GETINSERTMARKRECT = 0x10A9; 1008 | pub const LVM_SETINSERTMARKCOLOR = 0x10AA; 1009 | pub const LVM_GETINSERTMARKCOLOR = 0x10AB; 1010 | pub const LVM_SETINFOTIP = 0x10AD; 1011 | pub const LVM_GETSELECTEDCOLUMN = 0x10AE; 1012 | pub const LVM_ISGROUPVIEWENABLED = 0x10AF; 1013 | pub const LVM_GETOUTLINECOLOR = 0x10B0; 1014 | pub const LVM_SETOUTLINECOLOR = 0x10B1; 1015 | pub const LVM_CANCELEDITLABEL = 0x10B3; 1016 | pub const LVM_MAPINDEXTOID = 0x10B4; 1017 | pub const LVM_MAPIDTOINDEX = 0x10B5; 1018 | pub const LVM_ISITEMVISIBLE = 0x10B6; 1019 | pub const OCM__BASE = 0x2000; 1020 | pub const LVM_SETUNICODEFORMAT = 0x2005; 1021 | pub const LVM_GETUNICODEFORMAT = 0x2006; 1022 | pub const OCM_CTLCOLOR = 0x2019; 1023 | pub const OCM_DRAWITEM = 0x202B; 1024 | pub const OCM_MEASUREITEM = 0x202C; 1025 | pub const OCM_DELETEITEM = 0x202D; 1026 | pub const OCM_VKEYTOITEM = 0x202E; 1027 | pub const OCM_CHARTOITEM = 0x202F; 1028 | pub const OCM_COMPAREITEM = 0x2039; 1029 | pub const OCM_NOTIFY = 0x204E; 1030 | pub const OCM_COMMAND = 0x2111; 1031 | pub const OCM_HSCROLL = 0x2114; 1032 | pub const OCM_VSCROLL = 0x2115; 1033 | pub const OCM_CTLCOLORMSGBOX = 0x2132; 1034 | pub const OCM_CTLCOLOREDIT = 0x2133; 1035 | pub const OCM_CTLCOLORLISTBOX = 0x2134; 1036 | pub const OCM_CTLCOLORBTN = 0x2135; 1037 | pub const OCM_CTLCOLORDLG = 0x2136; 1038 | pub const OCM_CTLCOLORSCROLLBAR = 0x2137; 1039 | pub const OCM_CTLCOLORSTATIC = 0x2138; 1040 | pub const OCM_PARENTNOTIFY = 0x2210; 1041 | pub const WM_APP = 0x8000; 1042 | pub const WM_RASDIALEVENT = 0xCCCD; 1043 | 1044 | pub extern "user32" fn GetMessageA(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) callconv(WINAPI) BOOL; 1045 | pub fn getMessageA(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: u32, wMsgFilterMax: u32) !void { 1046 | const r = GetMessageA(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax); 1047 | if (r == 0) return error.Quit; 1048 | if (r != -1) return; 1049 | switch (GetLastError()) { 1050 | .INVALID_WINDOW_HANDLE => unreachable, 1051 | .INVALID_PARAMETER => unreachable, 1052 | else => |err| return windows.unexpectedError(err), 1053 | } 1054 | } 1055 | 1056 | pub extern "user32" fn GetMessageW(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) callconv(WINAPI) BOOL; 1057 | pub var pfnGetMessageW: *const @TypeOf(GetMessageW) = undefined; 1058 | pub fn getMessageW(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: u32, wMsgFilterMax: u32) !void { 1059 | const function = selectSymbol(GetMessageW, pfnGetMessageW, .win2k); 1060 | 1061 | const r = function(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax); 1062 | if (r == 0) return error.Quit; 1063 | if (r != -1) return; 1064 | switch (GetLastError()) { 1065 | .INVALID_WINDOW_HANDLE => unreachable, 1066 | .INVALID_PARAMETER => unreachable, 1067 | else => |err| return windows.unexpectedError(err), 1068 | } 1069 | } 1070 | 1071 | pub const PM_NOREMOVE = 0x0000; 1072 | pub const PM_REMOVE = 0x0001; 1073 | pub const PM_NOYIELD = 0x0002; 1074 | 1075 | pub extern "user32" fn PeekMessageA(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) callconv(WINAPI) BOOL; 1076 | pub fn peekMessageA(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: u32, wMsgFilterMax: u32, wRemoveMsg: u32) !bool { 1077 | const r = PeekMessageA(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg); 1078 | if (r == 0) return false; 1079 | if (r != -1) return true; 1080 | switch (GetLastError()) { 1081 | .INVALID_WINDOW_HANDLE => unreachable, 1082 | .INVALID_PARAMETER => unreachable, 1083 | else => |err| return windows.unexpectedError(err), 1084 | } 1085 | } 1086 | 1087 | pub extern "user32" fn PeekMessageW(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) callconv(WINAPI) BOOL; 1088 | pub var pfnPeekMessageW: *const @TypeOf(PeekMessageW) = undefined; 1089 | pub fn peekMessageW(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: u32, wMsgFilterMax: u32, wRemoveMsg: u32) !bool { 1090 | const function = selectSymbol(PeekMessageW, pfnPeekMessageW, .win2k); 1091 | 1092 | const r = function(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg); 1093 | if (r == 0) return false; 1094 | if (r != -1) return true; 1095 | switch (GetLastError()) { 1096 | .INVALID_WINDOW_HANDLE => unreachable, 1097 | .INVALID_PARAMETER => unreachable, 1098 | else => |err| return windows.unexpectedError(err), 1099 | } 1100 | } 1101 | 1102 | pub extern "user32" fn TranslateMessage(lpMsg: *const MSG) callconv(WINAPI) BOOL; 1103 | pub fn translateMessage(lpMsg: *const MSG) bool { 1104 | return if (TranslateMessage(lpMsg) == 0) false else true; 1105 | } 1106 | 1107 | pub extern "user32" fn DispatchMessageA(lpMsg: *const MSG) callconv(WINAPI) LRESULT; 1108 | pub fn dispatchMessageA(lpMsg: *const MSG) LRESULT { 1109 | return DispatchMessageA(lpMsg); 1110 | } 1111 | 1112 | pub extern "user32" fn DispatchMessageW(lpMsg: *const MSG) callconv(WINAPI) LRESULT; 1113 | pub var pfnDispatchMessageW: *const @TypeOf(DispatchMessageW) = undefined; 1114 | pub fn dispatchMessageW(lpMsg: *const MSG) LRESULT { 1115 | const function = selectSymbol(DispatchMessageW, pfnDispatchMessageW, .win2k); 1116 | return function(lpMsg); 1117 | } 1118 | 1119 | pub extern "user32" fn PostQuitMessage(nExitCode: i32) callconv(WINAPI) void; 1120 | pub fn postQuitMessage(nExitCode: i32) void { 1121 | PostQuitMessage(nExitCode); 1122 | } 1123 | 1124 | pub extern "user32" fn DefWindowProcA(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) callconv(WINAPI) LRESULT; 1125 | pub fn defWindowProcA(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) LRESULT { 1126 | return DefWindowProcA(hWnd, Msg, wParam, lParam); 1127 | } 1128 | 1129 | pub extern "user32" fn DefWindowProcW(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) callconv(WINAPI) LRESULT; 1130 | pub var pfnDefWindowProcW: *const @TypeOf(DefWindowProcW) = undefined; 1131 | pub fn defWindowProcW(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) LRESULT { 1132 | const function = selectSymbol(DefWindowProcW, pfnDefWindowProcW, .win2k); 1133 | return function(hWnd, Msg, wParam, lParam); 1134 | } 1135 | 1136 | // === Windows === 1137 | 1138 | pub const CS_VREDRAW = 0x0001; 1139 | pub const CS_HREDRAW = 0x0002; 1140 | pub const CS_DBLCLKS = 0x0008; 1141 | pub const CS_OWNDC = 0x0020; 1142 | pub const CS_CLASSDC = 0x0040; 1143 | pub const CS_PARENTDC = 0x0080; 1144 | pub const CS_NOCLOSE = 0x0200; 1145 | pub const CS_SAVEBITS = 0x0800; 1146 | pub const CS_BYTEALIGNCLIENT = 0x1000; 1147 | pub const CS_BYTEALIGNWINDOW = 0x2000; 1148 | pub const CS_GLOBALCLASS = 0x4000; 1149 | 1150 | pub const WNDCLASSEXA = extern struct { 1151 | cbSize: UINT = @sizeOf(WNDCLASSEXA), 1152 | style: UINT, 1153 | lpfnWndProc: WNDPROC, 1154 | cbClsExtra: i32 = 0, 1155 | cbWndExtra: i32 = 0, 1156 | hInstance: HINSTANCE, 1157 | hIcon: ?HICON, 1158 | hCursor: ?HCURSOR, 1159 | hbrBackground: ?HBRUSH, 1160 | lpszMenuName: ?[*:0]const u8, 1161 | lpszClassName: [*:0]const u8, 1162 | hIconSm: ?HICON, 1163 | }; 1164 | 1165 | pub const WNDCLASSEXW = extern struct { 1166 | cbSize: UINT = @sizeOf(WNDCLASSEXW), 1167 | style: UINT, 1168 | lpfnWndProc: WNDPROC, 1169 | cbClsExtra: i32 = 0, 1170 | cbWndExtra: i32 = 0, 1171 | hInstance: HINSTANCE, 1172 | hIcon: ?HICON, 1173 | hCursor: ?HCURSOR, 1174 | hbrBackground: ?HBRUSH, 1175 | lpszMenuName: ?[*:0]const u16, 1176 | lpszClassName: [*:0]const u16, 1177 | hIconSm: ?HICON, 1178 | }; 1179 | 1180 | pub extern "user32" fn RegisterClassExA(*const WNDCLASSEXA) callconv(WINAPI) ATOM; 1181 | pub fn registerClassExA(window_class: *const WNDCLASSEXA) !ATOM { 1182 | const atom = RegisterClassExA(window_class); 1183 | if (atom != 0) return atom; 1184 | switch (GetLastError()) { 1185 | .CLASS_ALREADY_EXISTS => return error.AlreadyExists, 1186 | .INVALID_PARAMETER => unreachable, 1187 | else => |err| return windows.unexpectedError(err), 1188 | } 1189 | } 1190 | 1191 | pub extern "user32" fn RegisterClassExW(*const WNDCLASSEXW) callconv(WINAPI) ATOM; 1192 | pub var pfnRegisterClassExW: *const @TypeOf(RegisterClassExW) = undefined; 1193 | pub fn registerClassExW(window_class: *const WNDCLASSEXW) !ATOM { 1194 | const function = selectSymbol(RegisterClassExW, pfnRegisterClassExW, .win2k); 1195 | const atom = function(window_class); 1196 | if (atom != 0) return atom; 1197 | switch (GetLastError()) { 1198 | .CLASS_ALREADY_EXISTS => return error.AlreadyExists, 1199 | .CALL_NOT_IMPLEMENTED => unreachable, 1200 | .INVALID_PARAMETER => unreachable, 1201 | else => |err| return windows.unexpectedError(err), 1202 | } 1203 | } 1204 | 1205 | pub extern "user32" fn UnregisterClassA(lpClassName: [*:0]const u8, hInstance: HINSTANCE) callconv(WINAPI) BOOL; 1206 | pub fn unregisterClassA(lpClassName: [*:0]const u8, hInstance: HINSTANCE) !void { 1207 | if (UnregisterClassA(lpClassName, hInstance) == 0) { 1208 | switch (GetLastError()) { 1209 | .CLASS_DOES_NOT_EXIST => return error.ClassDoesNotExist, 1210 | else => |err| return windows.unexpectedError(err), 1211 | } 1212 | } 1213 | } 1214 | 1215 | pub extern "user32" fn UnregisterClassW(lpClassName: [*:0]const u16, hInstance: HINSTANCE) callconv(WINAPI) BOOL; 1216 | pub var pfnUnregisterClassW: *const @TypeOf(UnregisterClassW) = undefined; 1217 | pub fn unregisterClassW(lpClassName: [*:0]const u16, hInstance: HINSTANCE) !void { 1218 | const function = selectSymbol(UnregisterClassW, pfnUnregisterClassW, .win2k); 1219 | if (function(lpClassName, hInstance) == 0) { 1220 | switch (GetLastError()) { 1221 | .CLASS_DOES_NOT_EXIST => return error.ClassDoesNotExist, 1222 | else => |err| return windows.unexpectedError(err), 1223 | } 1224 | } 1225 | } 1226 | 1227 | pub const WS_OVERLAPPED = 0x00000000; 1228 | pub const WS_POPUP = 0x80000000; 1229 | pub const WS_CHILD = 0x40000000; 1230 | pub const WS_MINIMIZE = 0x20000000; 1231 | pub const WS_VISIBLE = 0x10000000; 1232 | pub const WS_DISABLED = 0x08000000; 1233 | pub const WS_CLIPSIBLINGS = 0x04000000; 1234 | pub const WS_CLIPCHILDREN = 0x02000000; 1235 | pub const WS_MAXIMIZE = 0x01000000; 1236 | pub const WS_CAPTION = WS_BORDER | WS_DLGFRAME; 1237 | pub const WS_BORDER = 0x00800000; 1238 | pub const WS_DLGFRAME = 0x00400000; 1239 | pub const WS_VSCROLL = 0x00200000; 1240 | pub const WS_HSCROLL = 0x00100000; 1241 | pub const WS_SYSMENU = 0x00080000; 1242 | pub const WS_THICKFRAME = 0x00040000; 1243 | pub const WS_GROUP = 0x00020000; 1244 | pub const WS_TABSTOP = 0x00010000; 1245 | pub const WS_MINIMIZEBOX = 0x00020000; 1246 | pub const WS_MAXIMIZEBOX = 0x00010000; 1247 | pub const WS_TILED = WS_OVERLAPPED; 1248 | pub const WS_ICONIC = WS_MINIMIZE; 1249 | pub const WS_SIZEBOX = WS_THICKFRAME; 1250 | pub const WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW; 1251 | pub const WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; 1252 | pub const WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU; 1253 | pub const WS_CHILDWINDOW = WS_CHILD; 1254 | 1255 | pub const WS_EX_DLGMODALFRAME = 0x00000001; 1256 | pub const WS_EX_NOPARENTNOTIFY = 0x00000004; 1257 | pub const WS_EX_TOPMOST = 0x00000008; 1258 | pub const WS_EX_ACCEPTFILES = 0x00000010; 1259 | pub const WS_EX_TRANSPARENT = 0x00000020; 1260 | pub const WS_EX_MDICHILD = 0x00000040; 1261 | pub const WS_EX_TOOLWINDOW = 0x00000080; 1262 | pub const WS_EX_WINDOWEDGE = 0x00000100; 1263 | pub const WS_EX_CLIENTEDGE = 0x00000200; 1264 | pub const WS_EX_CONTEXTHELP = 0x00000400; 1265 | pub const WS_EX_RIGHT = 0x00001000; 1266 | pub const WS_EX_LEFT = 0x00000000; 1267 | pub const WS_EX_RTLREADING = 0x00002000; 1268 | pub const WS_EX_LTRREADING = 0x00000000; 1269 | pub const WS_EX_LEFTSCROLLBAR = 0x00004000; 1270 | pub const WS_EX_RIGHTSCROLLBAR = 0x00000000; 1271 | pub const WS_EX_CONTROLPARENT = 0x00010000; 1272 | pub const WS_EX_STATICEDGE = 0x00020000; 1273 | pub const WS_EX_APPWINDOW = 0x00040000; 1274 | pub const WS_EX_LAYERED = 0x00080000; 1275 | pub const WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE; 1276 | pub const WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST; 1277 | 1278 | pub const CW_USEDEFAULT: i32 = @bitCast(@as(u32, 0x80000000)); 1279 | 1280 | pub extern "user32" fn CreateWindowExA(dwExStyle: DWORD, lpClassName: [*:0]const u8, lpWindowName: [*:0]const u8, dwStyle: DWORD, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?LPVOID) callconv(WINAPI) ?HWND; 1281 | pub fn createWindowExA(dwExStyle: u32, lpClassName: [*:0]const u8, lpWindowName: [*:0]const u8, dwStyle: u32, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?*anyopaque) !HWND { 1282 | const window = CreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWindParent, hMenu, hInstance, lpParam); 1283 | if (window) |win| return win; 1284 | 1285 | switch (GetLastError()) { 1286 | .CLASS_DOES_NOT_EXIST => return error.ClassDoesNotExist, 1287 | .INVALID_PARAMETER => unreachable, 1288 | else => |err| return windows.unexpectedError(err), 1289 | } 1290 | } 1291 | 1292 | pub 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; 1293 | pub var pfnCreateWindowExW: *const @TypeOf(CreateWindowExW) = undefined; 1294 | pub 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: ?*anyopaque) !HWND { 1295 | const function = selectSymbol(CreateWindowExW, pfnCreateWindowExW, .win2k); 1296 | const window = function(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWindParent, hMenu, hInstance, lpParam); 1297 | if (window) |win| return win; 1298 | 1299 | switch (GetLastError()) { 1300 | .CLASS_DOES_NOT_EXIST => return error.ClassDoesNotExist, 1301 | .INVALID_PARAMETER => unreachable, 1302 | else => |err| return windows.unexpectedError(err), 1303 | } 1304 | } 1305 | 1306 | pub extern "user32" fn DestroyWindow(hWnd: HWND) callconv(WINAPI) BOOL; 1307 | pub fn destroyWindow(hWnd: HWND) !void { 1308 | if (DestroyWindow(hWnd) == 0) { 1309 | switch (GetLastError()) { 1310 | .INVALID_WINDOW_HANDLE => unreachable, 1311 | .INVALID_PARAMETER => unreachable, 1312 | else => |err| return windows.unexpectedError(err), 1313 | } 1314 | } 1315 | } 1316 | 1317 | pub const SW_HIDE = 0; 1318 | pub const SW_SHOWNORMAL = 1; 1319 | pub const SW_NORMAL = 1; 1320 | pub const SW_SHOWMINIMIZED = 2; 1321 | pub const SW_SHOWMAXIMIZED = 3; 1322 | pub const SW_MAXIMIZE = 3; 1323 | pub const SW_SHOWNOACTIVATE = 4; 1324 | pub const SW_SHOW = 5; 1325 | pub const SW_MINIMIZE = 6; 1326 | pub const SW_SHOWMINNOACTIVE = 7; 1327 | pub const SW_SHOWNA = 8; 1328 | pub const SW_RESTORE = 9; 1329 | pub const SW_SHOWDEFAULT = 10; 1330 | pub const SW_FORCEMINIMIZE = 11; 1331 | pub const SW_MAX = 11; 1332 | 1333 | pub extern "user32" fn ShowWindow(hWnd: HWND, nCmdShow: i32) callconv(WINAPI) BOOL; 1334 | pub fn showWindow(hWnd: HWND, nCmdShow: i32) bool { 1335 | return (ShowWindow(hWnd, nCmdShow) == TRUE); 1336 | } 1337 | 1338 | pub extern "user32" fn UpdateWindow(hWnd: HWND) callconv(WINAPI) BOOL; 1339 | pub fn updateWindow(hWnd: HWND) !void { 1340 | if (UpdateWindow(hWnd) == 0) { 1341 | switch (GetLastError()) { 1342 | .INVALID_WINDOW_HANDLE => unreachable, 1343 | .INVALID_PARAMETER => unreachable, 1344 | else => |err| return windows.unexpectedError(err), 1345 | } 1346 | } 1347 | } 1348 | 1349 | pub extern "user32" fn AdjustWindowRectEx(lpRect: *RECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD) callconv(WINAPI) BOOL; 1350 | pub fn adjustWindowRectEx(lpRect: *RECT, dwStyle: u32, bMenu: bool, dwExStyle: u32) !void { 1351 | assert(dwStyle & WS_OVERLAPPED == 0); 1352 | 1353 | if (AdjustWindowRectEx(lpRect, dwStyle, @intFromBool(bMenu), dwExStyle) == 0) { 1354 | switch (GetLastError()) { 1355 | .INVALID_PARAMETER => unreachable, 1356 | else => |err| return windows.unexpectedError(err), 1357 | } 1358 | } 1359 | } 1360 | 1361 | pub const GWL_WNDPROC = -4; 1362 | pub const GWL_HINSTANCE = -6; 1363 | pub const GWL_HWNDPARENT = -8; 1364 | pub const GWL_STYLE = -16; 1365 | pub const GWL_EXSTYLE = -20; 1366 | pub const GWL_USERDATA = -21; 1367 | pub const GWL_ID = -12; 1368 | 1369 | pub extern "user32" fn GetWindowLongA(hWnd: HWND, nIndex: i32) callconv(WINAPI) LONG; 1370 | pub fn getWindowLongA(hWnd: HWND, nIndex: i32) !i32 { 1371 | const value = GetWindowLongA(hWnd, nIndex); 1372 | if (value != 0) return value; 1373 | 1374 | switch (GetLastError()) { 1375 | .SUCCESS => return 0, 1376 | .INVALID_WINDOW_HANDLE => unreachable, 1377 | .INVALID_PARAMETER => unreachable, 1378 | else => |err| return windows.unexpectedError(err), 1379 | } 1380 | } 1381 | 1382 | pub extern "user32" fn GetWindowLongW(hWnd: HWND, nIndex: i32) callconv(WINAPI) LONG; 1383 | pub var pfnGetWindowLongW: *const @TypeOf(GetWindowLongW) = undefined; 1384 | pub fn getWindowLongW(hWnd: HWND, nIndex: i32) !i32 { 1385 | const function = selectSymbol(GetWindowLongW, pfnGetWindowLongW, .win2k); 1386 | 1387 | const value = function(hWnd, nIndex); 1388 | if (value != 0) return value; 1389 | 1390 | switch (GetLastError()) { 1391 | .SUCCESS => return 0, 1392 | .INVALID_WINDOW_HANDLE => unreachable, 1393 | .INVALID_PARAMETER => unreachable, 1394 | else => |err| return windows.unexpectedError(err), 1395 | } 1396 | } 1397 | 1398 | pub extern "user32" fn GetWindowLongPtrA(hWnd: HWND, nIndex: i32) callconv(WINAPI) LONG_PTR; 1399 | pub fn getWindowLongPtrA(hWnd: HWND, nIndex: i32) !isize { 1400 | // "When compiling for 32-bit Windows, GetWindowLongPtr is defined as a call to the GetWindowLong function." 1401 | // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw 1402 | if (@sizeOf(LONG_PTR) == 4) return getWindowLongA(hWnd, nIndex); 1403 | 1404 | const value = GetWindowLongPtrA(hWnd, nIndex); 1405 | if (value != 0) return value; 1406 | 1407 | switch (GetLastError()) { 1408 | .SUCCESS => return 0, 1409 | .INVALID_WINDOW_HANDLE => unreachable, 1410 | .INVALID_PARAMETER => unreachable, 1411 | else => |err| return windows.unexpectedError(err), 1412 | } 1413 | } 1414 | 1415 | pub extern "user32" fn GetWindowLongPtrW(hWnd: HWND, nIndex: i32) callconv(WINAPI) LONG_PTR; 1416 | pub var pfnGetWindowLongPtrW: *const @TypeOf(GetWindowLongPtrW) = undefined; 1417 | pub fn getWindowLongPtrW(hWnd: HWND, nIndex: i32) !isize { 1418 | if (@sizeOf(LONG_PTR) == 4) return getWindowLongW(hWnd, nIndex); 1419 | const function = selectSymbol(GetWindowLongPtrW, pfnGetWindowLongPtrW, .win2k); 1420 | 1421 | const value = function(hWnd, nIndex); 1422 | if (value != 0) return value; 1423 | 1424 | switch (GetLastError()) { 1425 | .SUCCESS => return 0, 1426 | .INVALID_WINDOW_HANDLE => unreachable, 1427 | .INVALID_PARAMETER => unreachable, 1428 | else => |err| return windows.unexpectedError(err), 1429 | } 1430 | } 1431 | 1432 | pub extern "user32" fn SetWindowLongA(hWnd: HWND, nIndex: i32, dwNewLong: LONG) callconv(WINAPI) LONG; 1433 | pub fn setWindowLongA(hWnd: HWND, nIndex: i32, dwNewLong: i32) !i32 { 1434 | // [...] you should clear the last error information by calling SetLastError with 0 before calling SetWindowLong. 1435 | // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlonga 1436 | SetLastError(.SUCCESS); 1437 | 1438 | const value = SetWindowLongA(hWnd, nIndex, dwNewLong); 1439 | if (value != 0) return value; 1440 | 1441 | switch (GetLastError()) { 1442 | .SUCCESS => return 0, 1443 | .INVALID_WINDOW_HANDLE => unreachable, 1444 | .INVALID_PARAMETER => unreachable, 1445 | else => |err| return windows.unexpectedError(err), 1446 | } 1447 | } 1448 | 1449 | pub extern "user32" fn SetWindowLongW(hWnd: HWND, nIndex: i32, dwNewLong: LONG) callconv(WINAPI) LONG; 1450 | pub var pfnSetWindowLongW: *const @TypeOf(SetWindowLongW) = undefined; 1451 | pub fn setWindowLongW(hWnd: HWND, nIndex: i32, dwNewLong: i32) !i32 { 1452 | const function = selectSymbol(SetWindowLongW, pfnSetWindowLongW, .win2k); 1453 | 1454 | SetLastError(.SUCCESS); 1455 | const value = function(hWnd, nIndex, dwNewLong); 1456 | if (value != 0) return value; 1457 | 1458 | switch (GetLastError()) { 1459 | .SUCCESS => return 0, 1460 | .INVALID_WINDOW_HANDLE => unreachable, 1461 | .INVALID_PARAMETER => unreachable, 1462 | else => |err| return windows.unexpectedError(err), 1463 | } 1464 | } 1465 | 1466 | pub extern "user32" fn SetWindowLongPtrA(hWnd: HWND, nIndex: i32, dwNewLong: LONG_PTR) callconv(WINAPI) LONG_PTR; 1467 | pub fn setWindowLongPtrA(hWnd: HWND, nIndex: i32, dwNewLong: isize) !isize { 1468 | // "When compiling for 32-bit Windows, GetWindowLongPtr is defined as a call to the GetWindowLong function." 1469 | // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw 1470 | if (@sizeOf(LONG_PTR) == 4) return setWindowLongA(hWnd, nIndex, dwNewLong); 1471 | 1472 | SetLastError(.SUCCESS); 1473 | const value = SetWindowLongPtrA(hWnd, nIndex, dwNewLong); 1474 | if (value != 0) return value; 1475 | 1476 | switch (GetLastError()) { 1477 | .SUCCESS => return 0, 1478 | .INVALID_WINDOW_HANDLE => unreachable, 1479 | .INVALID_PARAMETER => unreachable, 1480 | else => |err| return windows.unexpectedError(err), 1481 | } 1482 | } 1483 | 1484 | pub extern "user32" fn SetWindowLongPtrW(hWnd: HWND, nIndex: i32, dwNewLong: LONG_PTR) callconv(WINAPI) LONG_PTR; 1485 | pub var pfnSetWindowLongPtrW: *const @TypeOf(SetWindowLongPtrW) = undefined; 1486 | pub fn setWindowLongPtrW(hWnd: HWND, nIndex: i32, dwNewLong: isize) !isize { 1487 | if (@sizeOf(LONG_PTR) == 4) return setWindowLongW(hWnd, nIndex, dwNewLong); 1488 | const function = selectSymbol(SetWindowLongPtrW, pfnSetWindowLongPtrW, .win2k); 1489 | 1490 | SetLastError(.SUCCESS); 1491 | const value = function(hWnd, nIndex, dwNewLong); 1492 | if (value != 0) return value; 1493 | 1494 | switch (GetLastError()) { 1495 | .SUCCESS => return 0, 1496 | .INVALID_WINDOW_HANDLE => unreachable, 1497 | .INVALID_PARAMETER => unreachable, 1498 | else => |err| return windows.unexpectedError(err), 1499 | } 1500 | } 1501 | 1502 | pub extern "user32" fn GetDC(hWnd: ?HWND) callconv(WINAPI) ?HDC; 1503 | pub fn getDC(hWnd: ?HWND) !HDC { 1504 | const hdc = GetDC(hWnd); 1505 | if (hdc) |h| return h; 1506 | 1507 | switch (GetLastError()) { 1508 | .INVALID_WINDOW_HANDLE => unreachable, 1509 | .INVALID_PARAMETER => unreachable, 1510 | else => |err| return windows.unexpectedError(err), 1511 | } 1512 | } 1513 | 1514 | pub extern "user32" fn ReleaseDC(hWnd: ?HWND, hDC: HDC) callconv(WINAPI) i32; 1515 | pub fn releaseDC(hWnd: ?HWND, hDC: HDC) bool { 1516 | return if (ReleaseDC(hWnd, hDC) == 1) true else false; 1517 | } 1518 | 1519 | // === Modal dialogue boxes === 1520 | 1521 | pub const MB_OK = 0x00000000; 1522 | pub const MB_OKCANCEL = 0x00000001; 1523 | pub const MB_ABORTRETRYIGNORE = 0x00000002; 1524 | pub const MB_YESNOCANCEL = 0x00000003; 1525 | pub const MB_YESNO = 0x00000004; 1526 | pub const MB_RETRYCANCEL = 0x00000005; 1527 | pub const MB_CANCELTRYCONTINUE = 0x00000006; 1528 | pub const MB_ICONHAND = 0x00000010; 1529 | pub const MB_ICONQUESTION = 0x00000020; 1530 | pub const MB_ICONEXCLAMATION = 0x00000030; 1531 | pub const MB_ICONASTERISK = 0x00000040; 1532 | pub const MB_USERICON = 0x00000080; 1533 | pub const MB_ICONWARNING = MB_ICONEXCLAMATION; 1534 | pub const MB_ICONERROR = MB_ICONHAND; 1535 | pub const MB_ICONINFORMATION = MB_ICONASTERISK; 1536 | pub const MB_ICONSTOP = MB_ICONHAND; 1537 | pub const MB_DEFBUTTON1 = 0x00000000; 1538 | pub const MB_DEFBUTTON2 = 0x00000100; 1539 | pub const MB_DEFBUTTON3 = 0x00000200; 1540 | pub const MB_DEFBUTTON4 = 0x00000300; 1541 | pub const MB_APPLMODAL = 0x00000000; 1542 | pub const MB_SYSTEMMODAL = 0x00001000; 1543 | pub const MB_TASKMODAL = 0x00002000; 1544 | pub const MB_HELP = 0x00004000; 1545 | pub const MB_NOFOCUS = 0x00008000; 1546 | pub const MB_SETFOREGROUND = 0x00010000; 1547 | pub const MB_DEFAULT_DESKTOP_ONLY = 0x00020000; 1548 | pub const MB_TOPMOST = 0x00040000; 1549 | pub const MB_RIGHT = 0x00080000; 1550 | pub const MB_RTLREADING = 0x00100000; 1551 | pub const MB_TYPEMASK = 0x0000000F; 1552 | pub const MB_ICONMASK = 0x000000F0; 1553 | pub const MB_DEFMASK = 0x00000F00; 1554 | pub const MB_MODEMASK = 0x00003000; 1555 | pub const MB_MISCMASK = 0x0000C000; 1556 | 1557 | pub const IDOK = 1; 1558 | pub const IDCANCEL = 2; 1559 | pub const IDABORT = 3; 1560 | pub const IDRETRY = 4; 1561 | pub const IDIGNORE = 5; 1562 | pub const IDYES = 6; 1563 | pub const IDNO = 7; 1564 | pub const IDCLOSE = 8; 1565 | pub const IDHELP = 9; 1566 | pub const IDTRYAGAIN = 10; 1567 | pub const IDCONTINUE = 11; 1568 | 1569 | pub extern "user32" fn MessageBoxA(hWnd: ?HWND, lpText: [*:0]const u8, lpCaption: [*:0]const u8, uType: UINT) callconv(WINAPI) i32; 1570 | pub fn messageBoxA(hWnd: ?HWND, lpText: [*:0]const u8, lpCaption: [*:0]const u8, uType: u32) !i32 { 1571 | const value = MessageBoxA(hWnd, lpText, lpCaption, uType); 1572 | if (value != 0) return value; 1573 | switch (GetLastError()) { 1574 | .INVALID_WINDOW_HANDLE => unreachable, 1575 | .INVALID_PARAMETER => unreachable, 1576 | else => |err| return windows.unexpectedError(err), 1577 | } 1578 | } 1579 | 1580 | pub extern "user32" fn MessageBoxW(hWnd: ?HWND, lpText: [*:0]const u16, lpCaption: ?[*:0]const u16, uType: UINT) callconv(WINAPI) i32; 1581 | pub var pfnMessageBoxW: *const @TypeOf(MessageBoxW) = undefined; 1582 | pub fn messageBoxW(hWnd: ?HWND, lpText: [*:0]const u16, lpCaption: [*:0]const u16, uType: u32) !i32 { 1583 | const function = selectSymbol(MessageBoxW, pfnMessageBoxW, .win2k); 1584 | const value = function(hWnd, lpText, lpCaption, uType); 1585 | if (value != 0) return value; 1586 | switch (GetLastError()) { 1587 | .INVALID_WINDOW_HANDLE => unreachable, 1588 | .INVALID_PARAMETER => unreachable, 1589 | else => |err| return windows.unexpectedError(err), 1590 | } 1591 | } 1592 | --------------------------------------------------------------------------------