From nobody Mon Aug 4 19:46:35 2025 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4bwnB42xdNz63QyX; Mon, 04 Aug 2025 19:46:36 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R10" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4bwnB40wHmz4KB3; Mon, 04 Aug 2025 19:46:36 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1754336796; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=osixMdSltFrFfocyeKMTFpjQSbK040sCNghRRTECQhA=; b=I+eVpAtmt+2hS77jYEXofv4hO3tU+Da9TjdEZJgGbO1jV4pxAtT08A1SqQ7RYQnFS3AS31 Lhf6cZtDKQukB/CEBqLvRL/4fUHoSd6ShLLVxYr0u2fYdgwUwHvS7zrASyqiEVb//ZusSJ 5v8E+51vv0ZlIeeqBNdt7cNnPqOGV9mmA2yYxI2HQlxadOH0TWm+gHZE3EYI6kKfoXayNd S7IvNvrMd0MMVrvitqXe7xFi2qOtTrY2oJVCO6fPm232se51PqlSgLOTossIFgfmbZeUoz a+633Qd5lLtWbUoPo+cquY6WMxwdzko8Ds0fO1wL4hnWYvOXdufp0nYLfHph6A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1754336796; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=osixMdSltFrFfocyeKMTFpjQSbK040sCNghRRTECQhA=; b=ooOy0tJ0AiuyWnjGBA/Fzc2v1cN7f0Xw0mdXMzNu3F6mUpmO9JRLmHlzpwZnIHKk3wJILT hbu8xaKE8tgv4v13yKehpXfbEX5bIgFSLr0w2qHH79X27FVREDeEFQhVukChk2Ee5dBarm nfHfSrFKvAWVt0QnKI7tJ91LQFsni4QFy1vKWJPVUNSqI+LGPL+16nc5uUVGp9jexzlKcl hB4VsCr8ZjsKxcc4EX2hJcHwjim+HvXZPdoZJYwlnBZiNWvVjQjHOxMweNuVVWh2EUI0LO evb4hMnOThaaBDiA9TWeEfuYrpSkgBf8GJps9+rNJa6KsgchwYdt866dKDpXsg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1754336796; a=rsa-sha256; cv=none; b=k47JFDQgq76KtTO4cyXdez9jNdBPGwArR27Qx43q3p8nvJJlv6f7B9ZmcBDVg/onc7Itia 5MnlCkITavUNgPqiPnDN4MjzIcX622eqNxTYtrEMb8iLDZgt0aZ++FyaZA98XcrnuAFwc8 Ic1/yMxpt71ptDuSP3tpraPkKIKC82SEQu0EbawL6hDTzRh6TJpgOAt7kNEmXiLqDjY+Zo BZL3r6u6MN7aaPQv8tMGc2PP0nhy67uRyNXC6VRyxgsAxaB6f/q8dsWOjZgQJmTbBWSAef uyz18en9G6kg2cTkvZvdfpSb+edT4TxZG05a5XIWr/rA/2EzEH0hS3h0hdMykA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4bwnB40G3xz12W5; Mon, 04 Aug 2025 19:46:36 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 574JkZ9c098669; Mon, 4 Aug 2025 19:46:35 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 574JkZoB098666; Mon, 4 Aug 2025 19:46:35 GMT (envelope-from git) Date: Mon, 4 Aug 2025 19:46:35 GMT Message-Id: <202508041946.574JkZoB098666@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: John Baldwin Subject: git: b3127a2dc25a - main - libutil++: New library containing C++ utility classes for use in base List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: jhb X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: b3127a2dc25ac63cae8e33e6f3dbd3580644fe52 Auto-Submitted: auto-generated The branch main has been updated by jhb: URL: https://cgit.FreeBSD.org/src/commit/?id=b3127a2dc25ac63cae8e33e6f3dbd3580644fe52 commit b3127a2dc25ac63cae8e33e6f3dbd3580644fe52 Author: John Baldwin AuthorDate: 2025-08-04 19:38:06 +0000 Commit: John Baldwin CommitDate: 2025-08-04 19:38:06 +0000 libutil++: New library containing C++ utility classes for use in base - freebsd::FILE_up is a wrapper class for std::unique_ptr<> for FILE objects which uses a custom deleter that calls fclose(). - freebsd::malloc_up is a wrapper class for std::unique_ptr<> which uses a custom deleter that calls free(). It is useful for pointers allocated by malloc() such as strdup(). - The freebsd::stringf() functions return a std::string constructed using a printf format string. The current implementation of freebsd::stringf() uses fwopen() where the write function appends to a std::string. Sponsored by: Chelsio Communications Pull Request: https://github.com/freebsd/freebsd-src/pull/1794 --- contrib/mandoc/lib.in | 1 + lib/Makefile | 1 + lib/libutil++/Makefile | 16 +++++++++++ lib/libutil++/freebsd::FILE_up.3 | 41 ++++++++++++++++++++++++++ lib/libutil++/freebsd::malloc_up.3 | 50 ++++++++++++++++++++++++++++++++ lib/libutil++/freebsd::stringf.3 | 48 +++++++++++++++++++++++++++++++ lib/libutil++/libutil++.hh | 55 +++++++++++++++++++++++++++++++++++ lib/libutil++/stringf.cc | 57 +++++++++++++++++++++++++++++++++++++ lib/libutil++/tests/Makefile | 9 ++++++ lib/libutil++/tests/stringf_test.cc | 52 +++++++++++++++++++++++++++++++++ lib/libutil++/tests/up_test.cc | 33 +++++++++++++++++++++ share/mk/src.libnames.mk | 4 +++ 12 files changed, 367 insertions(+) diff --git a/contrib/mandoc/lib.in b/contrib/mandoc/lib.in index 6b17aab5b27b..134614aa6478 100644 --- a/contrib/mandoc/lib.in +++ b/contrib/mandoc/lib.in @@ -131,6 +131,7 @@ LINE("libugidfw", "File System Firewall Interface Library (libugidfw, \\-lugidfw LINE("libulog", "User Login Record Library (libulog, \\-lulog)") LINE("libusbhid", "USB Human Interface Devices Library (libusbhid, \\-lusbhid)") LINE("libutil", "System Utilities Library (libutil, \\-lutil)") +LINE("libutil++", "C++ Utilities Library (libutil++, \\-lutil++)") LINE("libvgl", "Video Graphics Library (libvgl, \\-lvgl)") LINE("libx86_64", "x86_64 Architecture Library (libx86_64, \\-lx86_64)") LINE("libxo", "Text, XML, JSON, and HTML Output Emission Library (libxo, \\-lxo)") diff --git a/lib/Makefile b/lib/Makefile index e0aafcad60d4..e5139b312a75 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -106,6 +106,7 @@ SUBDIR= ${SUBDIR_BOOTSTRAP} \ libugidfw \ libulog \ libutil \ + libutil++ \ ${_libvgl} \ libwrap \ libxo \ diff --git a/lib/libutil++/Makefile b/lib/libutil++/Makefile new file mode 100644 index 000000000000..ec83a9ad35f9 --- /dev/null +++ b/lib/libutil++/Makefile @@ -0,0 +1,16 @@ +PACKAGE= lib${LIB} +LIB_CXX= util++ +INTERNALLIB= true +SHLIB_MAJOR= 1 +SRCS= stringf.cc + +MAN+= freebsd::FILE_up.3 \ + freebsd::malloc_up.3 \ + freebsd::stringf.3 + +.include + +HAS_TESTS= +SUBDIR.${MK_TESTS}+= tests + +.include diff --git a/lib/libutil++/freebsd::FILE_up.3 b/lib/libutil++/freebsd::FILE_up.3 new file mode 100644 index 000000000000..ea63b1233b43 --- /dev/null +++ b/lib/libutil++/freebsd::FILE_up.3 @@ -0,0 +1,41 @@ +.\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2025 Chelsio Communications, Inc. +.\" Written by: John Baldwin +.\" +.Dd July 31, 2025 +.Dt FREEBSD::FILE_UP 3 +.Os +.Sh NAME +.Nm freebsd::FILE_up +.Nd std::unique_ptr specialization for stdio FILE objects +.Sh LIBRARY +.Lb libutil++ +.Sh SYNOPSIS +.In libutil++.hh +.Ft using FILE_up = std::unique_ptr; +.Sh DESCRIPTION +This class is a specialization of +.Vt std::unique_ptr +for stdio +.Vt FILE +objects. +When a +.Vt FILE +object managed by an instance of this class is disposed, +.Xr fclose 3 +is invoked to dispose of the +.Vt FILE +object. +.Sh EXAMPLES +.Bd -literal -offset indent +freebsd::FILE_up fp(fopen("foo.txt", "w")); +if (!fp) + err(1, "fopen"); +fprintf(fp.get(), "hello\n"); +// `fp' is implicitly closed on destruction +.Ed +.Sh SEE ALSO +.Xr fclose 3 , +.Xr fopen 3 diff --git a/lib/libutil++/freebsd::malloc_up.3 b/lib/libutil++/freebsd::malloc_up.3 new file mode 100644 index 000000000000..b18e7854213a --- /dev/null +++ b/lib/libutil++/freebsd::malloc_up.3 @@ -0,0 +1,50 @@ +.\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2025 Chelsio Communications, Inc. +.\" Written by: John Baldwin +.\" +.Dd July 31, 2025 +.Dt FREEBSD::MALLOC_UP 3 +.Os +.Sh NAME +.Nm freebsd::malloc_up +.Nd std::unique_ptr specialization for objects allocated via malloc +.Sh LIBRARY +.Lb libutil++ +.Sh SYNOPSIS +.In libutil++.hh +.Ft using malloc_up = std::unique_ptr>; +.Sh DESCRIPTION +This class is a specialization of +.Vt std::unique_ptr +which invokes +.Xr free 3 +instead of +.Fn delete +when an object is disposed. +While explicit calls to +.Xr malloc 3 +should be avoided in C++ code, +this class can be useful to manage an object allocated by an existing API +which uses +.Xr malloc 3 +internally such as +.Xr scandir 3 . +Note that the type of the underlying object must be used as the first +template argument similar to std::unique_ptr. +.Sh EXAMPLES +This example uses +.Xr strdup 3 +for simplicity, +but new C++ code should generally not use +.Xr strdup 3 : +.Bd -literal -offset indent +freebsd::malloc_up my_string(strdup("foo")); +// `mystring' is implicitly freed on destruction +.Ed +.Sh SEE ALSO +.Xr free 3 , +.Xr malloc 3 , +.Xr scandir 3 , +.Xr strdup 3 diff --git a/lib/libutil++/freebsd::stringf.3 b/lib/libutil++/freebsd::stringf.3 new file mode 100644 index 000000000000..341fedef4343 --- /dev/null +++ b/lib/libutil++/freebsd::stringf.3 @@ -0,0 +1,48 @@ +.\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2025 Chelsio Communications, Inc. +.\" Written by: John Baldwin +.\" +.Dd July 31, 2025 +.Dt FREEBSD::STRINGF 3 +.Os +.Sh NAME +.Nm freebsd::stringf +.Nd build a std::string using stdio formatting +.Sh LIBRARY +.Lb libutil++ +.Sh SYNOPSIS +.In libutil++.hh +.Ft std::string +.Fn freebsd::stringf "const char *fmt" "..." +.Ft std::string +.Fn freebsd::stringf "const char *fmt" "va_list ap" +.Sh DESCRIPTION +These functions construct a +.Vt std::string +object containing a formatted string. +The output of the string is dictated by the +.Fa fmt +argument and additional arguments using the same conventions documented in +.Xr printf 3 . +The first form provides functionality similar to +.Xr asprintf 3 +and the second form is similar to +.Xr vasprintf 3 . +.Sh RETURN VALUES +These functions return a std::string object. +.Sh EXCEPTIONS +These functions may throw one of the following exceptions: +.Bl -tag -width Er +.It Bq Er std::bad_alloc +Failed to allocate memory. +.It Bq Er std::length_error +The result would exceeed the maximum possible string size. +.El +.Sh EXAMPLES +.Bd -literal -offset indent +std::string s = freebsd::stringf("hello %s", "world"); +.Ed +.Sh SEE ALSO +.Xr asprintf 3 diff --git a/lib/libutil++/libutil++.hh b/lib/libutil++/libutil++.hh new file mode 100644 index 000000000000..93cc2d9e6650 --- /dev/null +++ b/lib/libutil++/libutil++.hh @@ -0,0 +1,55 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Chelsio Communications, Inc. + * Written by: John Baldwin + */ + +#ifndef __LIBUTILPP_HH__ +#define __LIBUTILPP_HH__ + +#include +#include +#include +#include + +namespace freebsd { + /* + * FILE_up is a std::unique_ptr<> for FILE objects which uses + * fclose() to destroy the wrapped pointer. + */ + struct fclose_deleter { + void operator() (std::FILE *fp) const + { + std::fclose(fp); + } + }; + + using FILE_up = std::unique_ptr; + + /* + * malloc_up is a std::unique_ptr<> which uses free() to + * destroy the wrapped pointer. This can be used to wrap + * pointers allocated implicitly by malloc() such as those + * returned by strdup(). + */ + template + struct free_deleter { + void operator() (T *p) const + { + std::free(p); + } + }; + + template + using malloc_up = std::unique_ptr>; + + /* + * Returns a std::string containing the same output as + * sprintf(). Throws std::bad_alloc if an error occurs. + */ + std::string stringf(const char *fmt, ...) __printflike(1, 2); + std::string stringf(const char *fmt, std::va_list ap); +} + +#endif /* !__LIBUTILPP_HH__ */ diff --git a/lib/libutil++/stringf.cc b/lib/libutil++/stringf.cc new file mode 100644 index 000000000000..8c24167d70ac --- /dev/null +++ b/lib/libutil++/stringf.cc @@ -0,0 +1,57 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Chelsio Communications, Inc. + * Written by: John Baldwin + */ + +#include +#include +#include + +#include "libutil++.hh" + +static int +stringf_write(void *cookie, const char *buf, int len) +{ + std::string *str = reinterpret_cast(cookie); + try { + str->append(buf, len); + } catch (std::bad_alloc) { + errno = ENOMEM; + return (-1); + } catch (std::length_error) { + errno = EFBIG; + return (-1); + } + return (len); +} + +std::string +freebsd::stringf(const char *fmt, va_list ap) +{ + std::string str; + freebsd::FILE_up fp(fwopen(reinterpret_cast(&str), + stringf_write)); + + vfprintf(fp.get(), fmt, ap); + + if (ferror(fp.get())) + throw std::bad_alloc(); + fp.reset(nullptr); + + return str; +} + +std::string +freebsd::stringf(const char *fmt, ...) +{ + std::va_list ap; + std::string str; + + va_start(ap, fmt); + str = freebsd::stringf(fmt, ap); + va_end(ap); + + return str; +} diff --git a/lib/libutil++/tests/Makefile b/lib/libutil++/tests/Makefile new file mode 100644 index 000000000000..81b7be4f5660 --- /dev/null +++ b/lib/libutil++/tests/Makefile @@ -0,0 +1,9 @@ +PACKAGE= tests + +ATF_TESTS_CXX+= stringf_test +ATF_TESTS_CXX+= up_test + +CFLAGS+= -I${SRCTOP}/lib/libutil++ +LIBADD+= util++ + +.include diff --git a/lib/libutil++/tests/stringf_test.cc b/lib/libutil++/tests/stringf_test.cc new file mode 100644 index 000000000000..5b8ef4ad54a9 --- /dev/null +++ b/lib/libutil++/tests/stringf_test.cc @@ -0,0 +1,52 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Chelsio Communications, Inc. + * Written by: John Baldwin + */ + +#include +#include +#include + +#include + +ATF_TEST_CASE_WITHOUT_HEAD(basic); +ATF_TEST_CASE_BODY(basic) +{ + ATF_REQUIRE_EQ("foo", freebsd::stringf("foo")); + ATF_REQUIRE_EQ("bar", freebsd::stringf("%s", "bar")); + ATF_REQUIRE_EQ("42", freebsd::stringf("%u", 42)); + ATF_REQUIRE_EQ("0xdeadbeef", freebsd::stringf("%#x", 0xdeadbeef)); + ATF_REQUIRE_EQ("", freebsd::stringf("")); + ATF_REQUIRE_EQ("this is a test", freebsd::stringf("this %s test", + "is a")); +} + +static std::string +stringv(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + std::string str = freebsd::stringf(fmt, ap); + va_end(ap); + return (str); +} + +ATF_TEST_CASE_WITHOUT_HEAD(va_list); +ATF_TEST_CASE_BODY(va_list) +{ + ATF_REQUIRE_EQ("foo", stringv("foo")); + ATF_REQUIRE_EQ("bar", stringv("%s", "bar")); + ATF_REQUIRE_EQ("42", stringv("%u", 42)); + ATF_REQUIRE_EQ("0xdeadbeef", stringv("%#x", 0xdeadbeef)); + ATF_REQUIRE_EQ("", stringv("")); + ATF_REQUIRE_EQ("this is a test", stringv("this %s test", "is a")); +} + +ATF_INIT_TEST_CASES(tcs) +{ + ATF_ADD_TEST_CASE(tcs, basic); + ATF_ADD_TEST_CASE(tcs, va_list); +} diff --git a/lib/libutil++/tests/up_test.cc b/lib/libutil++/tests/up_test.cc new file mode 100644 index 000000000000..3f344054c334 --- /dev/null +++ b/lib/libutil++/tests/up_test.cc @@ -0,0 +1,33 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Chelsio Communications, Inc. + * Written by: John Baldwin + */ + +#include +#include + +#include + +ATF_TEST_CASE_WITHOUT_HEAD(FILE_up); +ATF_TEST_CASE_BODY(FILE_up) +{ + FILE *fp = fopen("/dev/null", "r"); + ATF_REQUIRE(fp != NULL); + ATF_REQUIRE(fileno(fp) != -1); + + freebsd::FILE_up f(fp); + ATF_REQUIRE_EQ(fileno(fp), fileno(f.get())); + + f.reset(); + ATF_REQUIRE_EQ(f.get(), nullptr); + + ATF_REQUIRE_EQ(-1, fileno(fp)); + ATF_REQUIRE_EQ(EBADF, errno); +} + +ATF_INIT_TEST_CASES(tcs) +{ + ATF_ADD_TEST_CASE(tcs, FILE_up); +} diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk index 0fe352dffaf4..283a99496b9f 100644 --- a/share/mk/src.libnames.mk +++ b/share/mk/src.libnames.mk @@ -78,6 +78,7 @@ _INTERNALLIBS= \ smdb \ smutil \ telnet \ + util++ \ vers \ wpaap \ wpacommon \ @@ -694,6 +695,9 @@ LIBPKGECC?= ${LIBPKGECCDIR}/libpkgecc${PIE_SUFFIX}.a LIBPMCSTATDIR= ${_LIB_OBJTOP}/lib/libpmcstat LIBPMCSTAT?= ${LIBPMCSTATDIR}/libpmcstat${PIE_SUFFIX}.a +LIBUTIL++DIR= ${_LIB_OBJTOP}/lib/libutil++ +LIBUTIL++?= ${LIBUTIL++DIR}/libutil++${PIE_SUFFIX}.a + LIBWPAAPDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/ap LIBWPAAP?= ${LIBWPAAPDIR}/libwpaap${PIE_SUFFIX}.a