From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=pass header.oldest-pass=0 smtp.remote-ip=40.93.201.13 ARC-Seal: i=2; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=pass; t=1793154101; b=dvntswPNtIEAPPN3RinKkAXbYt5bvH8aDF3m1EWiNl3S6wFQACwhpwJiDd0KFdVqKIEI gyE/yBLXy5h2hZmbAf16ISCLzoktYmbSZDU37uAIsSL14BPsrrUGsgN7tg+en9LEJ5TfE OVjx/yOxmRKfJTYHQafVxHWD2XQ3UBOeXEEdv88A1DO59m6/0Ks9yj22FZNXuArykK1Th 3/7Dzxj0j3bNaXC/ohk4/OlHX0l/9UTtmLyRQKDcErAqclf3BTHmE6F1yGcmxDPhimR3A 8Wjezox820bUvmPHTxRccOp4p3EWAlC8OxXKhN7+ZcbKjELQkZGo+V8Q8muUB12m9Ug== ARC-Message-Signature: i=2; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793154101; h=DMARC-Filter:DKIM-Signature:Received:Received:From:Date:Subject: Content-Type:Content-Transfer-Encoding:Message-Id:References: In-Reply-To:To:Cc:X-Mailer:X-ClientProxiedBy:MIME-Version: X-MS-PublicTrafficType:X-MS-TrafficTypeDiagnostic: X-MS-Office365-Filtering-Correlation-Id:X-MS-Exchange-SenderADCheck: X-MS-Exchange-AntiSpam-Relay:X-Microsoft-Antispam: X-Microsoft-Antispam-Message-Info:X-Forefront-Antispam-Report: X-MS-Exchange-AntiSpam-MessageData-ChunkCount: X-MS-Exchange-AntiSpam-MessageData-0: X-MS-Exchange-AntiSpam-MessageData-1:X-OriginatorOrg: X-MS-Exchange-CrossTenant-Network-Message-Id: X-MS-Exchange-CrossTenant-AuthSource:X-MS-Exchange-CrossTenant-AuthAs: X-MS-Exchange-CrossTenant-OriginalArrivalTime: X-MS-Exchange-CrossTenant-FromEntityHeader: X-MS-Exchange-CrossTenant-Id:X-MS-Exchange-CrossTenant-MailboxType: X-MS-Exchange-CrossTenant-UserPrincipalName: X-MS-Exchange-Transport-CrossTenantHeadersStamped; bh=WyWthhsXDV7YMycsyrKHh2mmdeQ2VdQC5sBbQENBsXE=; b=00t3FuNaYe9i/KCHEa4RAnWweAhkGwafBtp7mlu/QXtLNKwDz92s7PjLwzH2yPQua+ys /SbpWBAuJniSFCK3IfFGRcZSqkHBOO0f9k19NYiooPPvKW7svG1oXDqL5aqjOdfMFGjWC HlYu0PvI2dMgjxrzHUzJ9KyYHO3juiAcaqIlCSSaPXQv5n58vno/kE3AQwt6FGBSmxgqE mjbIvJ6a2JMxlr/kxxFMHro5DLJOUmP9B3wkGXMIaOVbMLzd2YhfcKvJTkZt+pbeKo0Ym PQVlOfuOQaMDS7/Ap7cxE7k5RYcgWQaRh8GZN+Yiw8mszqzKI1duxY3ttB99Tv2ANFw== ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass header.d=Nvidia.com header.i=@Nvidia.com header.a=rsa-sha256 header.s=selector2 header.b=Ak6dsWN0; arc=pass header.oldest-pass=0 smtp.remote-ip=40.93.201.13 Received: from CY3PR05CU001.outbound.protection.outlook.com (mail-westcentralusazon11013013.outbound.protection.outlook.com [40.93.201.13]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (secp384r1) server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E99FC606A326; Wed, 28 Oct 2026 02:21:40 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org E99FC606A326 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=Nvidia.com header.i=@Nvidia.com header.a=rsa-sha256 header.s=selector2 header.b=Ak6dsWN0 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=O8TJGJLke7xbYodTz8FNxIWaoZ7V+elfTkQ2fQeyF2RcljpGqmURytv8oIkC/EQhm1Lp6Q5H21INSBM2qwykSkAje4l25mL5FtsNhTR9gfYwUaprC0LrnAnov27hq7tUPwQkZGtUquPU0wAKacH5tlanXCyfWaV2gA2ZitdE3ruJx6XsPkoEZ3VSQItpngHJla6Tj9eHxaICB3AA25e6NXt92FF929GM7BOcXlK8+xMqYPUMbAIznlCrNjp7E+vF+kGVo2ZwLzZbSoB19oQoYnAmuGvi+ja5Du+iMWQ9NLHX40AZuvg7KdFgj1AXb9xLYvStUcudoBBRb4/1cX86UA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=WyWthhsXDV7YMycsyrKHh2mmdeQ2VdQC5sBbQENBsXE=; b=ECBIMXN/er5YKZ0ggaZj9VN8Dpb9gegE3wbDN7RlCvt9/czzUsm2p7wDu+SzRxreubSYdKewxBaSOiHiTDJpz17xl27yU1UPXl8cqgzt6EYMFzrc6f2EXsFOv8Yin9WOJ93NUBsqerLszjuyoO9e9olHRQobE4a/j35j10k2VkatvSNwQeR2xvq0QnE3oEA7TdhWP5IdyFsljc+D69B2SPVlWTUrrsIG682TFV/boP5poQCpJ2U7SCMvLFIXUxD/B0Q8W6wUvHiEJLOsPuqpTgMk3M6vcrkW+p3ZNgVX86789BhTb6xMBWpuA0ysLk+yb7RD2O5mI0Bg0WlWg3G7iA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com; dkim=pass header.d=nvidia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=WyWthhsXDV7YMycsyrKHh2mmdeQ2VdQC5sBbQENBsXE=; b=Ak6dsWN0pqvorelnGrSbxS0SzNglbxJxMX7CTNuoat1+4YwKUTWo4pbG+995GdJkmdocT4jvOvb6/GLOwWMVOsjMiVWWGjwoYLso0Svv0kz+Nh5Zi96k3WIotiNUbuqEcZzKjUEpa2t60NwkJVJDs2xyvOiwtfdeX19z4NLTvqJZ5fC/D2zc1lbrJAM9kfk8wZV2rJtpIOjwdLtJWba2VkLx3KgSdWOhHlD/DZteUqBj+JfNXWeI7/oeOOy2ceajbuap6EirC3H0+rC0L86ndUFV7xDkUse9D6VxiYAFoIZZM6fBV8R77wC9jFw2WjWcqno/0cTLFOAfu8OqbStRWw== Authentication-Results: mx.microsoft.com 1; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from DS0PR12MB6413.namprd12.prod.outlook.com (2603:10b6:8:ce::10) by CH2PR12MB4086.namprd12.prod.outlook.com (2603:10b6:610:7c::19) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.546.22; Wed, 28 Oct 2026 02:21:35 +0000 Received: from DS0PR12MB6413.namprd12.prod.outlook.com ([fe80::e82a:6673:4142:37fa]) by DS0PR12MB6413.namprd12.prod.outlook.com ([fe80::e82a:6673:4142:37fa%5]) with mapi id 15.21.0546.016; Wed, 28 Oct 2026 02:21:35 +0000 From: Eliot Courtney Date: Wed, 28 Oct 2026 11:21:01 +0900 Subject: [PATCH v5 2/7] rust: alloc: add ArrayVec Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20261028-b4-nvkv-v5-2-ef78909b513e@nvidia.com> References: <20261028-b4-nvkv-v5-0-ef78909b513e@nvidia.com> In-Reply-To: <20261028-b4-nvkv-v5-0-ef78909b513e@nvidia.com> To: Danilo Krummrich , Lorenzo Stoakes , Vlastimil Babka , "Liam R. Howlett" , Uladzislau Rezki , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , David Airlie , Simona Vetter Cc: John Hubbard , Alistair Popple , Timur Tabi , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org, Eliot Courtney X-Mailer: b4 0.15.2 X-ClientProxiedBy: TYCP286CA0305.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:38b::16) To DS0PR12MB6413.namprd12.prod.outlook.com (2603:10b6:8:ce::10) X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: DS0PR12MB6413:EE_|CH2PR12MB4086:EE_ X-MS-Office365-Filtering-Correlation-Id: 6f390cdf-2bc5-4aa7-4757-08df349a30a2 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|376014|7416014|10070799003|366016|23010399003|921020|11063799006|56012099006|261009223027099003|261022105027099003|261021135927099003|261026121527099003|261021121427099003|261026140227099003|10067099003|261021123027099003|22082099003|18002099003|6133799003; X-Microsoft-Antispam-Message-Info: JmwHOKcNq3C+TwWX/cKgCtzqbzuJvobe+cbfg6Awwpo85sj4g5P+JUvw1+wSglQJBTIgU3hMo5Oql6AMKsY2ZT4DWJGIAnFriCotad1//D1iGwhHOg+HpezOX3DNjAvMhdJfmUX4gAohb/5aLlpmrpABUaU/RKWJOfF5ZtDrnoetU5NHFCBoj3AWGp0tLV6PXd7HOA/Jw3MVMlI9r52UAgh/ufJ0sIfz9mtm/UoujZZZrUDTeWnUhD1tP3yRpWdOkUbNGsvoGAWRiEYwDZxilIAkwwoL9b3/AXZNXUSp+GsUoGpXsBoMMuSXZeXQFgf22PHaBzrXgpsa7QBPxsPofR+VmerJovrKzb4g9P3vXx3NK0nch2G+LazJ5ttcdTZMniAk85F1eeQGJ6LVrjaGzdC6VQpFjy4jgmItWyCMsCjS4iXJp3I4F96/mTuRhM2f/msA6gurCQNZqeWcJCuzLJKy4FPFR1OFmui/7/dTGKc0+920CAPqHZxIsbcU2Zapctz6JpAK3UwGOsFWj8VeQjhzdGeVpAWukwdRu6VK0KKIlkFtqSb31+rJOH3ZQfnM2lkj8PSJZyQFALFKufHiiAh5qX48IIdMTXR1WgOZxuy3n+pOgpr0wK6fMHiM/yTwx9CNzjPle3USmTFsJP9ygRpMg56jSq7dBt1FsSFU+UEav3x6/Hsmein3FsdiMmjbPElxH1686P4obEqDaoFPbA== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:DS0PR12MB6413.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(376014)(7416014)(10070799003)(366016)(23010399003)(921020)(11063799006)(56012099006)(261009223027099003)(261022105027099003)(261021135927099003)(261026121527099003)(261021121427099003)(261026140227099003)(10067099003)(261021123027099003)(22082099003)(18002099003)(6133799003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?ZGEvRmJlSkNrMDA1SjhZUkpOMzdZSFlnZUNjZFVKcm5iVnJUQlRTTzBuUmNy?= =?utf-8?B?T0RvaGNVeXkxbFE1WjNaZ2R3TlZvRTFlK3lQaWJNMUtaeTFud1J1ZnpJOGJH?= =?utf-8?B?YUlLV2ZoaGtoRGpidEhaaElFRzFkZ2lQeDRlL3MvL1hQb0tVcE5YdmNDOWJT?= =?utf-8?B?enBlRnNQaTlYTFhoWHRINlNScWNoald0TkdoS3JVc0lRdHpmSkRwNTV0OUJS?= =?utf-8?B?MDhRa0JLZUZxa0pHNEdmeWFsVTVjVCt4TGN6NXExbFZIdmNKckJ2QXg0N3d0?= =?utf-8?B?eGN1dm1DbklzbDM3R05idjJwcUt4N2NiUFNiblJ1dFdUaUNhUlRtb0N6YXg0?= =?utf-8?B?ZlA5MG9Yd04wZDJ1c1VhTHA4bEduRjJweSs3Ymw2ZXhva1J1WExpY2ExdVdE?= =?utf-8?B?QXpTRUlrU1FKMmRSVkYzKzJ2TnFVWU8zSWlIOGFWc3V4b1lwNkpTdW1wUnJ6?= =?utf-8?B?clkxTGxrdjV3QUxPM3RtRHdWaGplZDZwVGhmTGtibWppU2xQOU1hc1EyNVNu?= =?utf-8?B?VnZ6a3N1NUlWTkxHdkR0Vi9HanFhNWtrbjdqVksrb1lZd09IU0FDWm1ZbXNU?= =?utf-8?B?MWtDRHJoQXU2K2gyVmREWHRQbHo4VzlnZ2dvRFZLaml6KzRjRFZkSS9aY3hp?= =?utf-8?B?SVhDSk9FNDJ1TkZGTXE3R1ZHbW1Kd1crMWplZ1dxMGlSUjNoVHIrRWtreFZM?= =?utf-8?B?Nm5LTmNqVGduK2VVN3pGdU5CbjN0RnJvY3R2OFdIc1FSdkhGWUxNSjUyRkF5?= =?utf-8?B?blV3QmRvbHhiTmorNVU1YVB6OFlYdm42RnRwZERqOGR0MHFsMUtoWnJodnNk?= =?utf-8?B?Z0FBSlB3Lzd0VUE2S2IweHViRW5Oc3Y4cjZSY2dVTU9ZYzYyRmtsVE1mdEow?= =?utf-8?B?YitLaWd3Q01QcTZTSnFXQnh3V1FQZXprUHpiS3ErSys2NG9VTlRvL053MXN6?= =?utf-8?B?bVVlODEyOElZdFE2VnM0UzhLZmhsZDh3Q1dKVnI1NVBVS1o5SE9sYVUrcVpS?= =?utf-8?B?amV2UC9VKyt2VGtkRjdxSWs0Ym5Ydk1SWUdITG1kUnBVeUIvQjdMSW1HcnVP?= =?utf-8?B?VjVOdVg2REsyRE4yODZEQjNTNzczMFdSYytVbDk1ZzdEeUN0Vk1naVRiTXBN?= =?utf-8?B?Q2dWMWdab2JFSVVoNytCN3dSbDJlNHVXM3g3ZmhFZXcxS3ZRbEVlYk01WVZn?= =?utf-8?B?SWpPUnUva2FHY05ZVGNwR2NlcHd4YWdvQTdUOHdaeC9IK2Nad0NxTUxnUlhp?= =?utf-8?B?eGFNZUxSZnhmNFFUdTN4RkMyRE1oNW1FUTZjeEV4QytIdzFoMmlXaTROWU12?= =?utf-8?B?MXBMZHdZVzhEUDdVaXlMdklHZ2I3YjVsOFhsM0VxZXNmaFAybGoxbUZJMkJj?= =?utf-8?B?WlBsWlFGajNqTlRqTFFRVU54L0I4bEdFeGNkVWtuYWVLUzV1Tmw5Q1ZlZG9F?= =?utf-8?B?c2d5d2gxbDNDLzA0NWIyMndHemg2M2NuUDN1SWpzdjR5R2FjYUlLNmNRS0VU?= =?utf-8?B?bk1iaEtyQ21PcVUwOTBrZDcrMHRpUVZtQXJsTXNkS0d5WnBLWXhKMkxaeWdF?= =?utf-8?B?VnJJRUdiZGdZZzUwWjB1VSsyVE1Ucjd3S0NXKzFNbXBCUFlFRkErYkFEcmk4?= =?utf-8?B?RGZYWXArR0o0SXlXK25Rbk5LYTMyeG1qT0tvUnZUakNvWHUraW0veTRidnJh?= =?utf-8?B?ZnBrZzJkcnRoSitiYkhKS3RvWHBGM21GREtNZ0pGZ211Ym5JSWt4aEtITGky?= =?utf-8?B?Kzl0SnFJZnFpMEI0MWI2OFdIWTVhSXM5OHRjMGlnTUZnNlB6SS9jWEh2TXNV?= =?utf-8?B?SzNhcHJpcXFTZWpRSmE1aUtPMTdBT0NkQklHTjlhY2FxdGNGaDlpT3dETWZU?= =?utf-8?B?NURoanJudm9KMzZFUm5LcWpDam9NOEhMYXZlWFBaOVFmQjFKSkxtaUE5QjNI?= =?utf-8?B?N1VrYWNOcjR6OWU5TGZ5UzBMTE4wRkt2R3BOVUdvN3o4QUQyTnRiMFM5OXNB?= =?utf-8?B?L2FsVTk1bzliMnpQL1dHeTRaWU1ESUIwcEFmdFM2QWpLRWxvMWJ1b0tYV0E1?= =?utf-8?B?dG5ab3dZdkpVVitKSnJKUWNWelFoZitWaGdpMGNVS0pPWFRyQ0tCV0UzUkJP?= =?utf-8?B?U0pScU8vOVU0a09YbzZ2SngzK3c0MU9QUnAzenJ6TW84dW1LcVM2QklwQTB2?= =?utf-8?B?SE1zdFRuUEVJaWZ2WmtiS1h1STZJZzF0MERJYmhPNXFKSENaS2pwWnR6V28y?= =?utf-8?B?bmI4cThmNTlBMmxocStiV1NOV1pUT1hyaTBNTVFIbmwyR0tKN0dhcmNpbXQ2?= =?utf-8?B?SUVlSy9lR0JvS2VHKzFJbEVmdzR4anFQVWY5dDYwWlFEbFlEQUFPQUcrUlhH?= =?utf-8?Q?zZa6FtUWKuAdAakvMRZJTRmhwez4R9WRUKcWVCvydHwJq?= X-MS-Exchange-AntiSpam-MessageData-1: WC4APsqjTWXvLw== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 6f390cdf-2bc5-4aa7-4757-08df349a30a2 X-MS-Exchange-CrossTenant-AuthSource: DS0PR12MB6413.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 28 Oct 2026 02:21:35.7912 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: bnss+IyzrGd36w+o4NcuqJQX++VMsOy3TE91yT9GWJlCQocCvefHoWo9mCw8p33pLpu/ONmCA9KUqabNLWFcbg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: CH2PR12MB4086 Add a fixed capacity vector backed by [MaybeUninit; N]. The ArrayVec is also initializable with a closure, returning an Init instance, to avoid constructing it on the stack. ArrayVec is useful for small but varying size arrays stored on the stack, to avoid a heap allocation, or, for larger varying size arrays initialized into caller provided memory but not wanting to provide an allocator. Reviewed-by: Alexandre Courbot Signed-off-by: Eliot Courtney --- rust/kernel/alloc.rs | 3 + rust/kernel/alloc/arrayvec.rs | 351 +++++++++++++++++++++++++++++++++++++++ rust/kernel/alloc/kvec.rs | 1 + rust/kernel/alloc/kvec/errors.rs | 2 +- 4 files changed, 356 insertions(+), 1 deletion(-) diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs index 21067bde6860..510e2c7f9f72 100644 --- a/rust/kernel/alloc.rs +++ b/rust/kernel/alloc.rs @@ -3,10 +3,13 @@ //! Implementation of the kernel's memory allocation infrastructure. pub mod allocator; +pub mod arrayvec; pub mod kbox; pub mod kvec; pub mod layout; +pub use self::arrayvec::ArrayVec; + pub use self::kbox::Box; pub use self::kbox::KBox; pub use self::kbox::KVBox; diff --git a/rust/kernel/alloc/arrayvec.rs b/rust/kernel/alloc/arrayvec.rs new file mode 100644 index 000000000000..44cc09ee1e51 --- /dev/null +++ b/rust/kernel/alloc/arrayvec.rs @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Implementation of [`ArrayVec`]. + +use crate::{ + alloc::kvec::{ + impl_slice_eq, + PushError, // + }, + const_assert, + error::{ + code::EINVAL, + Error, + Result, // + }, + fmt, // +}; + +use core::{ + borrow::{ + Borrow, + BorrowMut, // + }, + mem::MaybeUninit, + ops::{ + Deref, + DerefMut, // + }, + ptr, + slice, // +}; + +use pin_init::{ + init_from_closure, + Init, // +}; + +/// A fixed capacity vector that holds at most `N` elements. +/// +/// # Invariants +/// +/// - `len` is at most `N`. +/// - The first `len` elements of `data` are initialized. +/// +/// # Examples +/// +/// ``` +/// use kernel::alloc::ArrayVec; +/// +/// let mut v = ArrayVec::::new(); +/// v.extend_from_slice(b"abc")?; +/// assert_eq!(*v, *b"abc"); +/// +/// assert!(v.extend_from_slice(b"ab").is_err()); +/// +/// v.push(4u8)?; +/// assert_eq!(*v, *b"abc\x04"); +/// assert!(v.push(5u8).is_err()); +/// +/// v.clear(); +/// assert!(v.is_empty()); +/// # Ok::<(), Error>(()) +/// ``` +pub struct ArrayVec { + data: [MaybeUninit; N], + len: usize, +} + +/// The maximum size in bytes of an [`ArrayVec`] returned by value from [`ArrayVec::new`]. +const MAX_STACK_SIZE: usize = 512; + +impl ArrayVec { + /// Creates an empty [`ArrayVec`]. + #[inline] + pub const fn new() -> Self { + const_assert!( + size_of::() <= MAX_STACK_SIZE, + "use `init_empty` instead of constructing a large ArrayVec on the stack" + ); + + // INVARIANT: An empty ArrayVec trivially has all its elements initialized. + Self { + data: [const { MaybeUninit::uninit() }; N], + len: 0, + } + } + + /// Creates an initializer for an empty [`ArrayVec`]. + /// + /// # Examples + /// + /// ``` + /// use kernel::alloc::ArrayVec; + /// + /// let v = KBox::init(ArrayVec::::init_empty(), GFP_KERNEL)?; + /// assert!(v.is_empty()); + /// # Ok::<(), Error>(()) + /// ``` + #[inline] + pub fn init_empty() -> impl Init { + Self::init_with(|_| Ok(())) + } + + /// Creates an initializer for an [`ArrayVec`] populated by `f`. + /// + /// `f` gets an empty [`ArrayVec`] and can fill it in place. + /// + /// # Examples + /// + /// ``` + /// use kernel::alloc::ArrayVec; + /// + /// let v = KBox::init( + /// ArrayVec::::init_with(|v| v.extend_from_slice(b"abc")), + /// GFP_KERNEL, + /// )?; + /// assert_eq!(**v, *b"abc"); + /// # Ok::<(), Error>(()) + /// ``` + #[inline] + pub fn init_with(f: impl FnOnce(&mut Self) -> Result<(), E>) -> impl Init { + let init_empty = |slot: *mut Self| { + // SAFETY: By the initializer contract `slot` is valid for writes. Once `len` is zero + // the slot holds a valid empty ArrayVec, since `data` requires no initialization. + // INVARIANT: An empty ArrayVec trivially has all its elements initialized. + unsafe { (&raw mut (*slot).len).write(0) }; + Ok(()) + }; + + // SAFETY: `init_empty` fully initializes the slot and never fails. + unsafe { init_from_closure(init_empty) }.chain(f) + } + + /// Appends an element to the back of the [`ArrayVec`]. + /// + /// Fails when the [`ArrayVec`] is full, handing the element back in [`PushError`]. + #[inline] + pub fn push(&mut self, v: T) -> Result<(), PushError> { + self.push_init(v) + } + + /// Appends an element to the back of the [`ArrayVec`] by initializing it in place. + /// + /// Fails when the [`ArrayVec`] is full, handing the initializer back in [`PushError`]. + #[inline] + pub fn push_init>(&mut self, init: I) -> Result<(), PushError> { + self.try_push_init(init) + .map_err(|TryPushInitError::Full(init)| PushError(init)) + } + + /// Appends an element to the back of the [`ArrayVec`] by initializing it in place. + /// + /// Unlike [`ArrayVec::push_init`], the initializer may be fallible. If the [`ArrayVec`] is + /// full, the original initializer `init` is handed back in [`TryPushInitError::Full`]. If the + /// initializer itself fails, its error is returned in [`TryPushInitError::InitError`]. + pub fn try_push_init(&mut self, init: I) -> Result<(), TryPushInitError> + where + I: Init, + { + let Some(slot) = self.spare_capacity_mut().first_mut() else { + return Err(TryPushInitError::Full(init)); + }; + + // SAFETY: + // - `slot.as_mut_ptr()` points to a spare slot, which is valid, uninitialized memory. + // - If `raw_try_init` fails, this returns without increasing `len` or touching `slot`. + // - `init` implements `Init`, which removes the pinning requirement. + unsafe { pin_init::raw_try_init(slot.as_mut_ptr(), init) } + .map_err(TryPushInitError::InitError)?; + + // INVARIANT: The element at index `len` was just initialized, and the new `len` does not + // exceed `N` because a spare slot existed. + self.len += 1; + + Ok(()) + } + + /// Appends a clone of each element in `slice` to the back of the [`ArrayVec`]. + /// + /// Fails with [`EINVAL`] if `slice` is longer than the remaining capacity. + pub fn extend_from_slice(&mut self, slice: &[T]) -> Result + where + T: Clone, + { + let Some(dst) = self.spare_capacity_mut().get_mut(..slice.len()) else { + return Err(EINVAL); + }; + + for (d, s) in dst.iter_mut().zip(slice) { + d.write(s.clone()); + } + // INVARIANT: The next `slice.len()` elements after `len` were just initialized, and the + // new `len` does not exceed `N` because the spare capacity was enough. + self.len += slice.len(); + + Ok(()) + } + + /// Removes all elements. + #[inline] + pub fn clear(&mut self) { + let elems: *mut [T] = self.as_mut_slice(); + // INVARIANT: An empty ArrayVec trivially has all its elements initialized. + self.len = 0; + // SAFETY: There are no references to the elements since we hold `&mut self`. The elements + // can't be dropped again because `len` is already 0. + unsafe { ptr::drop_in_place(elems) }; + } + + /// Returns the initialized elements as a slice. + #[inline] + pub fn as_slice(&self) -> &[T] { + let ptr = self.data.as_ptr().cast::(); + // SAFETY: `MaybeUninit` has the same layout as `T`, and by the type invariants the first + // `len` elements of `data` are initialized. + unsafe { slice::from_raw_parts(ptr, self.len) } + } + + /// Returns the initialized elements as a mutable slice. + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [T] { + let ptr = self.data.as_mut_ptr().cast::(); + // SAFETY: `MaybeUninit` has the same layout as `T`, and by the type invariants the first + // `len` elements of `data` are initialized. + unsafe { slice::from_raw_parts_mut(ptr, self.len) } + } + + /// Returns a slice of `MaybeUninit` for the remaining spare capacity of the [`ArrayVec`]. + #[inline] + fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { + // PANIC: `len` never exceeds `N` by the type invariants. + &mut self.data[self.len..] + } +} + +/// Error type for [`ArrayVec::try_push_init`]. +pub enum TryPushInitError { + /// The [`ArrayVec`] is full. Hand the initializer back. + Full(I), + /// The initializer failed. + InitError(E), +} + +impl fmt::Debug for TryPushInitError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TryPushInitError::Full(_) => write!(f, "Not enough capacity"), + TryPushInitError::InitError(_) => write!(f, "Initializer failed"), + } + } +} + +impl From> for Error +where + Error: From, +{ + #[inline] + fn from(e: TryPushInitError) -> Error { + match e { + TryPushInitError::Full(_) => EINVAL, + TryPushInitError::InitError(e) => Error::from(e), + } + } +} + +impl Default for ArrayVec { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Drop for ArrayVec { + #[inline] + fn drop(&mut self) { + // SAFETY: The slice holds initialized elements that are never accessed again after this + // point. + unsafe { ptr::drop_in_place(self.as_mut_slice()) }; + } +} + +impl Deref for ArrayVec { + type Target = [T]; + + #[inline] + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +impl DerefMut for ArrayVec { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.as_mut_slice() + } +} + +impl Borrow<[T]> for ArrayVec { + #[inline] + fn borrow(&self) -> &[T] { + self.as_slice() + } +} + +impl BorrowMut<[T]> for ArrayVec { + #[inline] + fn borrow_mut(&mut self) -> &mut [T] { + self.as_mut_slice() + } +} + +impl Eq for ArrayVec {} + +impl_slice_eq! { + [const N: usize, const M: usize] ArrayVec, ArrayVec, + [const N: usize] ArrayVec, &[U], + [const N: usize] ArrayVec, &mut [U], + [const N: usize] &[T], ArrayVec, + [const N: usize] &mut [T], ArrayVec, + [const N: usize] ArrayVec, [U], + [const N: usize] [T], ArrayVec, + [const N: usize, const M: usize] ArrayVec, [U; M], + [const N: usize, const M: usize] ArrayVec, &[U; M], +} + +impl<'a, T, const N: usize> IntoIterator for &'a ArrayVec { + type Item = &'a T; + type IntoIter = slice::Iter<'a, T>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, T, const N: usize> IntoIterator for &'a mut ArrayVec { + type Item = &'a mut T; + type IntoIter = slice::IterMut<'a, T>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +impl fmt::Debug for ArrayVec { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.as_slice(), f) + } +} diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs index c8b312f64657..cd0c57a9ad90 100644 --- a/rust/kernel/alloc/kvec.rs +++ b/rust/kernel/alloc/kvec.rs @@ -1255,6 +1255,7 @@ fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } )* } } +pub(super) use impl_slice_eq; impl_slice_eq! { [A1: Allocator, A2: Allocator] Vec, Vec, diff --git a/rust/kernel/alloc/kvec/errors.rs b/rust/kernel/alloc/kvec/errors.rs index d24dfe22834a..f5c44fdc7016 100644 --- a/rust/kernel/alloc/kvec/errors.rs +++ b/rust/kernel/alloc/kvec/errors.rs @@ -7,7 +7,7 @@ prelude::*, // }; -/// Error type for [`Vec::push_within_capacity`]. +/// Error type for a push that fails because there is no spare capacity. pub struct PushError(pub T); impl fmt::Debug for PushError { -- 2.56.0