Skip to content

Implement offset and limit features - #20

Open
Kwanddwo wants to merge 5 commits into
csvtoolkit:mainfrom
Kwanddwo:implement-offset-limit
Open

Implement offset and limit features#20
Kwanddwo wants to merge 5 commits into
csvtoolkit:mainfrom
Kwanddwo:implement-offset-limit

Conversation

@Kwanddwo

@Kwanddwo Kwanddwo commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

This pr implements offset and limit features in csv_reader:

  • added records_returned variable
  • added skip_records function which is run on init and on rewind / seek
  • csv_reader_next_record now returns NULL if limit > 0 and records_returned >= limit
  • this fixes Offset and limit in config is never used #14

Summary by CodeRabbit

  • New Features

    • Added support for skipping a configured number of data records before reading results.
    • Improved record limiting so reads stop after the expected number of rows, including after rewind and seek.
  • Bug Fixes

    • Fixed offset and limit handling when headers are present, ensuring consistent results across repeated reads.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes widen CSVConfig.limit from char to int, add a records_returned field to CSVReader, and introduce an internal skip_records helper used during initialization and rewind to advance past a configured offset. csv_reader_next_record's limit check now uses records_returned. New tests validate offset/limit/rewind/seek behavior.

Changes

Offset and limit handling

Layer / File(s) Summary
Config and struct field changes
csv_config.h, csv_reader.h
CSVConfig.limit type changed from char to int; CSVReader gains a new records_returned (long) field.
skip_records helper and initialization wiring
csv_reader.c
Adds skip_records(reader, count) forward declaration and implementation; initializes records_returned to 0 and invokes skip_records with config->offset in both csv_reader_init_with_config and csv_reader_init_standalone.
Next record limit check and rewind
csv_reader.c
csv_reader_next_record now checks/increments records_returned for limit enforcement; csv_reader_rewind resets records_returned, recomputes line_number after re-reading the header, and re-applies skip_records with the configured offset.
Offset/limit test coverage
tests/test_csv_reader.c
New tests validate offset, limit, combined offset+limit, rewind, and seek behaviors with and without headers; main() invokes the new tests.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant CSVReader
  participant skip_records
  participant Stream

  Caller->>CSVReader: csv_reader_init_standalone(config)
  CSVReader->>CSVReader: records_returned = 0
  CSVReader->>skip_records: skip_records(reader, config->offset)
  loop offset count
    skip_records->>Stream: read full record
    skip_records->>CSVReader: line_number++
  end
  skip_records-->>CSVReader: offset applied
  Caller->>CSVReader: csv_reader_next_record()
  CSVReader->>CSVReader: check records_returned vs limit
  CSVReader->>Stream: read record
  CSVReader->>CSVReader: records_returned++
  CSVReader-->>Caller: return CSVRecord

  Caller->>CSVReader: csv_reader_rewind()
  CSVReader->>CSVReader: records_returned = 0, recompute line_number
  CSVReader->>skip_records: skip_records(reader, config->offset)
  skip_records-->>CSVReader: offset re-applied
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states the main feature added: offset and limit support.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
csv_reader.c (1)

125-132: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider reusing skip_records in csv_reader_seek.

csv_reader_seek's advance loop (elsewhere in this file) duplicates the same arena_reset + read_full_record + line_number++ pattern now encapsulated in skip_records. Calling skip_records(reader, position) there (adjusting for its if (!line) break; vs seek's return 0 on failure) would remove the duplication.

♻️ Sketch
 int csv_reader_seek(CSVReader *reader, long position) {
     if (!reader || !reader->file || position < 0) {
         return 0;
     }

     csv_reader_rewind(reader);

-    for (long i = 0; i < position; i++) {
-        arena_reset(reader->temp_arena);
-        char *line = read_full_record(reader->file, reader->temp_arena);
-        if (!line) {
-            return 0;
-        }
-        reader->line_number++;
-    }
+    long before = reader->line_number;
+    skip_records(reader, (int)position);
+    if (reader->line_number - before < position) {
+        return 0;
+    }

     return 1;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@csv_reader.c` around lines 125 - 132, The seek advance logic duplicates the
same record-skipping pattern already encapsulated in skip_records; update
csv_reader_seek to reuse skip_records instead of reimplementing arena_reset,
read_full_record, and line_number updates. Keep csv_reader_seek’s failure
behavior aligned with its current return-0 semantics by checking whether the
requested position was fully skipped and propagating failure if read_full_record
stops early.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@csv_reader.c`:
- Around line 125-132: The reader navigation paths are not honoring
skipEmptyLines consistently, so empty lines still advance record position in
csv_reader_init_*, csv_reader_rewind, csv_reader_seek, skip_records, and
csv_reader_next_record. Update the record-skipping and iteration logic in these
routines to detect and ignore blank lines when skipEmptyLines is enabled,
matching the behavior already used by csv_reader_get_record_count, so offsets
and seeks land on the correct data row.

---

Nitpick comments:
In `@csv_reader.c`:
- Around line 125-132: The seek advance logic duplicates the same
record-skipping pattern already encapsulated in skip_records; update
csv_reader_seek to reuse skip_records instead of reimplementing arena_reset,
read_full_record, and line_number updates. Keep csv_reader_seek’s failure
behavior aligned with its current return-0 semantics by checking whether the
requested position was fully skipped and propagating failure if read_full_record
stops early.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c1a5fb44-26a6-40bb-90dd-89900c24a1ee

📥 Commits

Reviewing files that changed from the base of the PR and between cc93661 and a49fda0.

📒 Files selected for processing (4)
  • csv_config.h
  • csv_reader.c
  • csv_reader.h
  • tests/test_csv_reader.c

Comment thread csv_reader.c
Comment on lines +125 to +132
static void skip_records(CSVReader *reader, int count) {
for (int i = 0; i < count; i++) {
arena_reset(reader->temp_arena);
char *line = read_full_record(reader->file, reader->temp_arena);
if (!line) break;
reader->line_number++;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how skipEmptyLines is actually implemented relative to raw line reads
rg -nP -C5 '\bskipEmptyLines\b' --type=c
rg -nP -C8 'read_full_record\s*\(' --type=c
rg -nP -C8 'csv_parse_line_inplace\s*\(' --type=c

Repository: csvtoolkit/FastCSV-C

Length of output: 158


🏁 Script executed:

git ls-files

Repository: csvtoolkit/FastCSV-C

Length of output: 602


🏁 Script executed:

ast-grep outline csv_reader.c --view expanded
ast-grep outline csv_parser.c --view expanded
ast-grep outline tests/test_csv_reader.c --view expanded
rg -n "empty|skip|limit|offset|record" csv_reader.c csv_parser.c csv_config.c tests/test_csv_reader.c

Repository: csvtoolkit/FastCSV-C

Length of output: 25378


🏁 Script executed:

sed -n '1,220p' csv_reader.c
printf '\n----\n'
sed -n '220,360p' csv_reader.c
printf '\n----\n'
sed -n '1,220p' csv_config.c
printf '\n----\n'
sed -n '330,390p' tests/test_csv_reader.c
printf '\n----\n'
sed -n '470,650p' tests/test_csv_reader.c

Repository: csvtoolkit/FastCSV-C

Length of output: 22351


🏁 Script executed:

sed -n '1,260p' csv_parser.c
printf '\n----\n'
sed -n '1,220p' csv_reader.h
printf '\n----\n'
sed -n '1,220p' csv_config.h
printf '\n----\n'
sed -n '1,220p' README.md

Repository: csvtoolkit/FastCSV-C

Length of output: 19190


🏁 Script executed:

sed -n '260,340p' csv_parser.c
printf '\n----\n'
python3 - <<'PY'
from pathlib import Path
text = Path('csv_parser.c').read_text()
# print the exact blank-line handling branch if present
for needle in ['record_len == 0', 'line comment', 'EOF']:
    pass
PY

Repository: csvtoolkit/FastCSV-C

Length of output: 1111


Make skipEmptyLines consistent in reader navigation csv_reader_get_record_count skips blank lines, but csv_reader_init_*, csv_reader_rewind, csv_reader_seek, skip_records, and csv_reader_next_record still count them as records. With skipEmptyLines enabled, offset/seek can land on the wrong data row in files that contain empty lines.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@csv_reader.c` around lines 125 - 132, The reader navigation paths are not
honoring skipEmptyLines consistently, so empty lines still advance record
position in csv_reader_init_*, csv_reader_rewind, csv_reader_seek, skip_records,
and csv_reader_next_record. Update the record-skipping and iteration logic in
these routines to detect and ignore blank lines when skipEmptyLines is enabled,
matching the behavior already used by csv_reader_get_record_count, so offsets
and seeks land on the correct data row.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Offset and limit in config is never used

1 participant