git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 00/11] Clarify API for dir.[ch] and unpack-trees.[ch] -- mark relevant fields as internal

From
Jacob Keller <jacob.keller@gmail.com>
Date
Feb 24, 2023, 05:54 UTC
Message-ID
<CA+P7+xo02dGkjb5DwJ1Af_hoQ5HiuxASheZxoFz+r6B-6cQMug@mail.gmail.com>
In-Reply-To
<16ff5069-0408-21cd-995c-8b47afb9810d@github.com>
On Thu, Feb 23, 2023 at 7:29 AM Derrick Stolee <derrickstolee@github.com> wrote:
Show 70 quoted lines
>
> On 2/23/2023 4:14 AM, Elijah Newren via GitGitGadget wrote:
> > This patch is primarily about moving internal-only fields within these two
> > structs into an embedded internal struct. Patch breakdown:
> >
> >  * Patches 1-3: Restructuring dir_struct
> >    * Patch 1: Splitting off internal-use-only fields
> >    * Patch 2: Add important usage note to avoid accidentally using
> >      deprecated API
> >    * Patch 3: Mark output-only fields as such
> >  * Patches 4-11: Restructuring unpack_trees_options
> >    * Patches 4-6: Preparatory cleanup
> >    * Patches 7-10: Splitting off internal-use-only fields
> >    * Patch 11: Mark output-only field as such
>
> > And after the changes:
> >
> > struct dir_struct {
> >     enum [...] flags;
> >     int nr; /* output only */
> >     int ignored_nr; /* output only */
> >     struct dir_entry **entries; /* output only */
> >     struct dir_entry **ignored; /* output only */
> >     struct untracked_cache *untracked;
> >     const char *exclude_per_dir; /* deprecated */
> >     struct dir_struct_internal {
> >         int alloc;
> >         int ignored_alloc;
> > #define EXC_CMDL 0
> > #define EXC_DIRS 1
> > #define EXC_FILE 2
> >         struct exclude_list_group exclude_list_group[3];
> >         struct exclude_stack *exclude_stack;
> >         struct path_pattern *pattern;
> >         struct strbuf basebuf;
> >         struct oid_stat ss_info_exclude;
> >         struct oid_stat ss_excludes_file;
> >         unsigned unmanaged_exclude_files;
> >         unsigned visited_paths;
> >         unsigned visited_directories;
> >     } internal;
> > };
>
> This does present a very clear structure to avoid callers being
> confused when writing these changes. It doesn't, however, present
> any way to guarantee that callers can't mutate this state.
>
> ...here I go on a side track thinking of an alternative...
>
> One way to track this would be to anonymously declare 'struct
> dir_struct_internal' in the header file and let 'struct dir_struct'
> contain a _pointer_ to the internal struct. The dir_struct_internal
> can then be defined inside the .c file, limiting its scope. (It
> must be a pointer in dir_struct or else callers would not be able
> to create a dir_struct without using a pointer and an initializer
> method.
>
> The major downside to this pointer approach is that the internal
> struct needs to be initialized within API calls and somehow cleared
> by all callers. The internal data could be initialized by the common
> initializers read_directory() or fill_directory(). There is a
> dir_clear() that _should_ be called by all callers (but I notice we
> are leaking the struct in at least one place in add-interactive.c,
> and likely others).
>
> This alternative adds some complexity to the structure, but
> provides compiler-level guarantees that these internals are not used
> outside of dir.c. I thought it worth exploring, even if we decide
> that the complexity is not worth those guarantees.
>

Another approach, if you don't mind structure pointer math is to create two structures:

a) the external public one in the public header file
struct dir_entry {
  <public stuff>
};
b) a private structure in the source file:
struct dir_entry_private {
  struct dir_entry entry;
  <private stuff>
};

In the source file you also define a macro/function that can take a pointer to dir_entry and get a pointer to dir_entry_private:

struct dir_entry_private *dir_entry_to_private(struct dir_entry *entry)
{
  return (struct dir_entry_private *)(<calculate the offset that entry
inside dir_entry_private is, then subtract that from entry pointer>))
}

In Linux kernel this is container_of, not sure if git has this already defined and its a common pattern.

Then you can set the code up such that the only way to allocate a dir_entry is to call some function in the dir code. If a new entry needs to be allocated, implement an alloc function that has the full private structure definition.

This way you don't need an extra field in the dir_entry struct at all, but at the cost of requiring special allocations. It works great for code where the only way to get a dir_entry is already some other functions that would ensure the private version is setup correctly.

Show 6 quoted lines
> The best news is that your existing series makes it easier to flip
> to the internal pointer method in the future, since we can shift
> the 'd->internal.member" uses into "d->internal->member" in a
> mechanical way. Thus, the change you are proposing does not lock us
> into this approach if we change our minds later.
>

I think either approach is good. I like container_of because I'm quite used to it in low level kernel code and its a good way to provide private/public split abstractions there. The private pointer variation is also a common approach to this problem and I think it sounds like we already use it in a few places. Its perhaps better for those reasons.

> Thanks,
> -Stolee
Previous: Junio C HamanoNext: Jonathan Tan
Message 27 of 57 in “Clarify API for dir.[ch] and unpack-trees.[ch] -- mark relevant fields as internal”
  1. 00/11 Clarify API for dir.[ch] and unpack-trees.[ch] -- mark relevant fields as internalElijah Newren via GitGitGadget, Feb 23, 2023
  2. 01/11 dir: separate public from internal portion of dir_structElijah Newren via GitGitGadget, Feb 23, 2023
  3. 02/11 dir: add a usage note to exclude_per_dirElijah Newren via GitGitGadget, Feb 23, 2023
  4. Jonathan TanFeb 24, 2023
  5. Elijah NewrenFeb 25, 2023
  6. Jonathan TanFeb 25, 2023
  7. Elijah NewrenFeb 25, 2023
  8. 03/11 dir: mark output only fields of dir_struct as suchElijah Newren via GitGitGadget, Feb 23, 2023
  9. 04/11 unpack-trees: clean up some flow controlElijah Newren via GitGitGadget, Feb 23, 2023
  10. Jonathan TanFeb 24, 2023
  11. 05/11 sparse-checkout: avoid using internal API of unpack-treesElijah Newren via GitGitGadget, Feb 23, 2023
  12. Jonathan TanFeb 24, 2023
  13. Elijah NewrenFeb 25, 2023
  14. 06/11 sparse-checkout: avoid using internal API of unpack-trees, take 2Elijah Newren via GitGitGadget, Feb 23, 2023
  15. Jonathan TanFeb 24, 2023
  16. Elijah NewrenFeb 25, 2023
  17. 07/11 unpack_trees: start splitting internal fields from public APIElijah Newren via GitGitGadget, Feb 23, 2023
  18. 09/11 unpack-trees: rewrap a few overlong lines from previous patchElijah Newren via GitGitGadget, Feb 23, 2023
  19. 10/11 unpack-trees: special case read-tree debugging as internal usageElijah Newren via GitGitGadget, Feb 23, 2023
  20. 08/11 unpack-trees: mark fields only used internally as internalElijah Newren via GitGitGadget, Feb 23, 2023
  21. 11/11 unpack-trees: add usage notices around df_conflict_entryElijah Newren via GitGitGadget, Feb 23, 2023
  22. Derrick StoleeFeb 23, 2023
  23. Derrick StoleeFeb 23, 2023
  24. Elijah NewrenFeb 23, 2023
  25. Elijah NewrenFeb 23, 2023
  26. Junio C HamanoFeb 24, 2023
  27. Jacob KellerFeb 24, 2023
  28. Jonathan TanFeb 24, 2023
  29. 00/11 Clarify API for dir.[ch] and unpack-trees.[ch] -- mark relevant fields as internalElijah Newren via GitGitGadget, Feb 25, 2023
  30. 02/11 dir: add a usage note to exclude_per_dirElijah Newren via GitGitGadget, Feb 25, 2023
  31. 01/11 dir: separate public from internal portion of dir_structElijah Newren via GitGitGadget, Feb 25, 2023
  32. 03/11 dir: mark output only fields of dir_struct as suchElijah Newren via GitGitGadget, Feb 25, 2023
  33. 04/11 unpack-trees: clean up some flow controlElijah Newren via GitGitGadget, Feb 25, 2023
  34. 05/11 sparse-checkout: avoid using internal API of unpack-treesElijah Newren via GitGitGadget, Feb 25, 2023
  35. 06/11 sparse-checkout: avoid using internal API of unpack-trees, take 2Elijah Newren via GitGitGadget, Feb 25, 2023
  36. 09/11 unpack-trees: rewrap a few overlong lines from previous patchElijah Newren via GitGitGadget, Feb 25, 2023
  37. 07/11 unpack_trees: start splitting internal fields from public APIElijah Newren via GitGitGadget, Feb 25, 2023
  38. 11/11 unpack-trees: add usage notices around df_conflict_entryElijah Newren via GitGitGadget, Feb 25, 2023
  39. 10/11 unpack-trees: special case read-tree debugging as internal usageElijah Newren via GitGitGadget, Feb 25, 2023
  40. 08/11 unpack-trees: mark fields only used internally as internalElijah Newren via GitGitGadget, Feb 25, 2023
  41. Junio C HamanoFeb 25, 2023
  42. 00/13 Clarify API for dir.[ch] and unpack-trees.[ch] -- mark relevant fields as internalElijah Newren via GitGitGadget, Feb 27, 2023
  43. 01/13 t2021: fix platform-specific leftover cruftElijah Newren via GitGitGadget, Feb 27, 2023
  44. Derrick StoleeFeb 27, 2023
  45. 02/13 unpack-trees: heed requests to overwrite ignored filesElijah Newren via GitGitGadget, Feb 27, 2023
  46. Jonathan TanFeb 27, 2023
  47. 03/13 dir: separate public from internal portion of dir_structElijah Newren via GitGitGadget, Feb 27, 2023
  48. 05/13 dir: mark output only fields of dir_struct as suchElijah Newren via GitGitGadget, Feb 27, 2023
  49. 04/13 dir: add a usage note to exclude_per_dirElijah Newren via GitGitGadget, Feb 27, 2023
  50. 07/13 sparse-checkout: avoid using internal API of unpack-treesElijah Newren via GitGitGadget, Feb 27, 2023
  51. 06/13 unpack-trees: clean up some flow controlElijah Newren via GitGitGadget, Feb 27, 2023
  52. 08/13 sparse-checkout: avoid using internal API of unpack-trees, take 2Elijah Newren via GitGitGadget, Feb 27, 2023
  53. 10/13 unpack-trees: mark fields only used internally as internalElijah Newren via GitGitGadget, Feb 27, 2023
  54. 09/13 unpack_trees: start splitting internal fields from public APIElijah Newren via GitGitGadget, Feb 27, 2023
  55. 11/13 unpack-trees: rewrap a few overlong lines from previous patchElijah Newren via GitGitGadget, Feb 27, 2023
  56. 12/13 unpack-trees: special case read-tree debugging as internal usageElijah Newren via GitGitGadget, Feb 27, 2023
  57. 13/13 unpack-trees: add usage notices around df_conflict_entryElijah Newren via GitGitGadget, Feb 27, 2023

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.