Skip to content

Add std::encoding::ini module#3072

Merged
lerno merged 5 commits intoc3lang:masterfrom
ChristianReifberger:add-ini-encoding
Apr 8, 2026
Merged

Add std::encoding::ini module#3072
lerno merged 5 commits intoc3lang:masterfrom
ChristianReifberger:add-ini-encoding

Conversation

@ChristianReifberger
Copy link
Copy Markdown
Contributor

@ChristianReifberger ChristianReifberger commented Mar 27, 2026

Adds a new INI parsing and encoding module to the stdlib with:

  • parse()/tparse() and encode()/tencode()
  • Mutations: set, remove_key, remove_section, clear, free
  • Iteration: iter(), @each_key, @each (section, key, value)
  • IniOptions: delimiter, padding, inline comments, duplicate-key policy
  • GLOBAL section for pre-header keys
  • tests covering parse, encode, round-trip, edge cases

Types

  • IniMap — the parsed/constructed map; owns all allocated strings
  • IniOptions — parser and encoder configuration; pass by value starting from ini::DEFAULT_OPTIONS
  • DuplicateKeyBehavior — enum: DUPLICATE_OVERWRITE (default) or DUPLICATE_ERROR
  • const String GLOBAL = "" — sentinel for keys before the first section header
  • faultdef INVALID_SECTION, INVALID_KEY, DUPLICATE_KEY

API

Method Description
ini::parse(allocator, src, options) Parse INI text, heap-allocated; returns IniMap?
ini::tparse(src, options) Parse INI text, temp-allocated; returns IniMap?
init(allocator) Create empty map for programmatic construction
tinit() init using temp allocator
set(section, key, value) Insert or overwrite a key; creates section if absent
remove_key(section, key) → bool Remove a single key; returns true if it existed
remove_section(section) → bool Remove a section and all its keys; GLOBAL cannot be removed
get(section, key) → String? Look up a value, or NOT_FOUND
get_section(section) → HashMap*? Return the inner map for a section
has_section(section) → bool True if section exists
has_key(section, key) → bool True if key exists in section
len() → usz Number of sections; empty GLOBAL excluded
section_len(section) → usz Number of keys in a section, or 0 if absent
iter() → String[] Temp-allocated slice of section names; empty GLOBAL excluded
@each_key(section; key, value) Iterate key/value pairs within one section
@each(section, key, value) Iterate every entry; section may be ini::GLOBAL
encode(allocator, options) → String Serialize to INI text; global keys emitted first
tencode(options) → String encode using temp allocator
clear() Remove all entries; GLOBAL section kept empty
free() Free all owned memory; safe to call after a parse error

Implementation notes

  • Single file lib/std/encoding/ini.c3.
  • Section names, keys, and values are all heap-copied into the provided allocator; ini::GLOBAL ("") is the one exception — it is a compile-time constant and never heap-allocated.
  • The outer map is a HashMap{String, HashMap{String, String}}; each section is its own inner HashMap.
  • _ensure_section copies the section name on first insertion only, so re-entering the same section is allocation-free.
  • encode() writes global keys first, then named sections, using a temp-backed ByteWriter and copying the result into the target allocator at the end.
  • @each in clear()/free() yields by-value copies that share backing pointers — safe because HashMap.clear()/free() never dereferences stored value internals.

Tests

test/unit/stdlib/ini.c3t covers all cases: basic parse, global keys, comments (; and #), whitespace trimming, empty values, colon delimiter, duplicate key policies (DUPLICATE_OVERWRITE / DUPLICATE_ERROR), error cases (INVALID_SECTION, INVALID_KEY), programmatic construction via init/set, mutation (remove_key, remove_section, clear), iteration (iter(), @each_key/each including GLOBAL visibility), round-trip parse → encode → parse, inline comments, CRLF line endings, and serialization options (pad_delimiter, custom delimiter).

@ChristianReifberger ChristianReifberger marked this pull request as ready for review March 27, 2026 09:03
@lerno
Copy link
Copy Markdown
Collaborator

lerno commented Apr 5, 2026

The IniMap is duplicating what Object already does. Reuse Object instead. This would remove everything except the parse and encode functionality.

@ChristianReifbergerVUS
Copy link
Copy Markdown

@lerno thanks for the feedback, I've changed it to use Object. I did not look into it but you're right, much of the code could be removed

@lerno
Copy link
Copy Markdown
Collaborator

lerno commented Apr 7, 2026

Also, can you make sure that this submission conforms to the rules for contributing here: https://github.com/c3lang/c3c/blob/master/CONTRIBUTING.md

ChristianReifberger and others added 5 commits April 8, 2026 01:18
Adds a new INI parsing and encoding module to the stdlib with:
- parse()/tparse() and encode()/tencode()
- Full mutation API: set, remove_key, remove_section, clear, free
- Iteration: iter(), @each_key, @each (section, key, value)
- IniOptions: delimiter, padding, inline comments, duplicate-key policy
- GLOBAL section for pre-header keys
- 30 tests covering parse, encode, round-trip, edge cases
@lerno lerno force-pushed the add-ini-encoding branch from d9c4197 to ff812ea Compare April 8, 2026 00:34
@lerno lerno merged commit f63ce8b into c3lang:master Apr 8, 2026
24 checks passed
@lerno
Copy link
Copy Markdown
Collaborator

lerno commented Apr 8, 2026

I ended up rewriting this one significantly because the encodings should really properly be stream first. Unfortunately this is not the case for some of the older code, but new should do so.

ChristianReifberger added a commit to ChristianReifberger/c3c that referenced this pull request Apr 8, 2026
* Add std::encoding::ini module


* Updated API for store/load using streams. Changed API

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants