[libc-commits] [libc] [libc][NFC] Add FieldTokenizer and FlatFileDatabase (PR #214720)
Alexey Samsonov via libc-commits
libc-commits at lists.llvm.org
Fri Aug 7 11:53:07 PDT 2026
================
@@ -0,0 +1,224 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Generic flat-file database template engine.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PWD_FLAT_FILE_DB_H
+#define LLVM_LIBC_SRC_PWD_FLAT_FILE_DB_H
+
+#include "hdr/errno_macros.h"
+#include "hdr/stdio_macros.h"
+#include "hdr/types/size_t.h"
+#include "src/__support/CPP/span.h"
+#include "src/__support/File/file.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace pwd {
+
+struct ReadLineResult {
+ size_t bytes_read;
+ bool truncated;
+};
+
+// Forward declaration of record parser for flat database files.
+template <typename EntryType>
+bool parse_line(cpp::span<char> line, EntryType *entry);
+
+// Generic flat colon-delimited database engine.
+//
+// \tparam EntryType The data structure type representing a record.
+template <typename EntryType> class FlatFileDatabase {
+private:
+ const char *file_path;
+ File *file = nullptr;
+
+ // Reads a single line from the given file into the provided buffer.
+ // Note: POSIX getline/getdelim cannot be used here because user database
+ // lookups (including reentrant _r variants) must operate in-place within a
+ // fixed, bounded buffer without dynamic heap allocations or realloc.
+ LIBC_INLINE static ErrorOr<ReadLineResult> read_line(File *f,
+ cpp::span<char> buf) {
+ if (!f || buf.size() < 2)
+ return Error(EINVAL);
+
+ f->lock();
+ size_t bytes_read = 0;
+ FileIOResult result(0);
+ bool truncated = false;
+
+ for (char &ch : buf.first(buf.size() - 1)) {
+ result = f->read_unlocked(&ch, 1);
+ if (result.has_error()) {
+ f->unlock();
+ return Error(result.error);
+ }
+ if (result.value != 1)
+ break;
+ ++bytes_read;
+ if (ch == '\n')
+ break;
+ }
+
+ auto read_span = buf.first(bytes_read);
+ if (result.value == 1 && !read_span.empty() && read_span.back() != '\n') {
+ truncated = true;
+ char c = '\0';
+ while (true) {
+ result = f->read_unlocked(&c, 1);
+ if (result.has_error()) {
+ f->unlock();
+ return Error(result.error);
+ }
+ if (result.value != 1 || c == '\n')
+ break;
+ }
+ }
+
+ bool has_error = f->error_unlocked();
+ f->unlock();
+
+ if (has_error)
+ return Error(EIO);
+
+ buf[bytes_read] = '\0';
+ return ReadLineResult{bytes_read, truncated};
+ }
+
+public:
+ LIBC_INLINE constexpr explicit FlatFileDatabase(const char *path)
+ : file_path(path) {}
+
+ // Sets or overrides the file path for database operations.
+ LIBC_INLINE void set_path(const char *path) {
+ if (!path)
+ return;
+ if (file) {
+ file->close();
+ file = nullptr;
+ }
+ file_path = path;
+ }
+
+ // Opens or rewinds the database file stream.
+ LIBC_INLINE ErrorOr<int> setdb() {
+ if (!file) {
+ auto result = openfile(file_path, "r");
+ if (!result.has_value())
+ return Error(result.error());
+ file = result.value();
+ return 0;
+ }
+ auto result = file->seek(0, SEEK_SET);
+ if (!result.has_value())
+ return Error(result.error());
+ return 0;
+ }
+
+ // Closes the database file stream.
+ LIBC_INLINE ErrorOr<int> enddb() {
+ if (file) {
+ int result = file->close();
+ file = nullptr;
+ if (result != 0)
+ return Error(result);
+ }
+ return 0;
+ }
+
+ // Reads and parses the next record from the database.
+ LIBC_INLINE ErrorOr<bool> getnext(EntryType *entry, cpp::span<char> buffer) {
+ if (!entry)
+ return Error(EINVAL);
+
+ if (!file) {
+ auto res = setdb();
+ if (!res.has_value())
+ return Error(res.error());
+ }
+
+ auto result = read_line(file, buffer);
+ if (!result.has_value())
+ return Error(result.error());
+
+ ReadLineResult res = result.value();
+ if (res.bytes_read == 0)
+ return false; // EOF
+
+ if (res.truncated)
+ return Error(ERANGE);
+
+ auto line = buffer.first(res.bytes_read);
+ if (!line.empty() && line.back() == '\n')
+ line.back() = '\0';
+
+ size_t valid_len = (!line.empty() && line.back() == '\0')
+ ? res.bytes_read
+ : (res.bytes_read + 1);
+ if (valid_len > buffer.size())
+ valid_len = buffer.size();
+
+ if (parse_line(buffer.subspan(0, valid_len), entry))
+ return true;
+
+ return Error(EINVAL);
+ }
+
+ // Iterates sequentially through records using a callback function.
+ template <typename Func>
----------------
vonosmas wrote:
Why is this a template argument, and not a typed callback, since you know the return type (bool) and arguments (EntryType)? Also, naming could be better - the `func` is basically `skip_callback` which returns true iff the entry should be skipped, and false when we should stop.
https://github.com/llvm/llvm-project/pull/214720
More information about the libc-commits
mailing list