Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci_meson.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
python-version: '3.x'
- run: python -m pip install meson==${{ matrix.meson_version }} ninja
- run: meson setup builddir/
- run: meson compile -C builddir/
- run: meson test -C builddir/ -v
- uses: actions/upload-artifact@v4
if: failure()
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Build with Meson:

```bash
meson setup builddir
meson compile -C builddir
meson test -C builddir
```

Expand Down
45 changes: 25 additions & 20 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -8,60 +8,51 @@ cc = meson.get_compiler('c')
math_dep = cc.find_library('m', required: false)
unity_dep = dependency('unity', required: true)

# Library configuration

# Sources
epsilon_sources = files(
'src/csv.c',
'src/rng.c',
'src/stats.c',
'src/hash.c',
'src/transform.c'
)


epsilon_headers = files(
'src/csv.h',
'src/rng.h',
'src/stats.h',
'src/hash.h',
'src/transform.h',
'src/pa.h'
)

# Build library
# Library
epsilon_lib = library('epsilon',
sources: epsilon_sources,
dependencies: math_dep,
install: true,
version: meson.project_version()
)

# Declare dependency for downstream use

# Declare dependency for downstream use (export include dirs / link)
epsilon_dep = declare_dependency(
link_with: epsilon_lib,
include_directories: include_directories('src'),
dependencies: math_dep
)

# Install headers

# Install public headers
install_headers(epsilon_headers, subdir: 'epsilon')

# Examples and tests configuration
# Examples & tools
examples = {
'example_hash': 'examples/example_hash.c',
'example_transform': 'examples/example_transform.c',
'example_stats': 'examples/example_stats.c',
'example_rng': 'examples/example_rng.c'
}

tests = {
'hash_test': 'tests/hash_test.c',
'rng_test': 'tests/rng_test.c',
'stats_test': 'tests/stats_test.c',
'transform_test': 'tests/transform_test.c',
}

# Build and register examples
# Build and register examples (not installed)
foreach name, source : examples
exe = executable(name, source,
dependencies: epsilon_dep,
Expand All @@ -70,7 +61,22 @@ foreach name, source : examples
test(name, exe)
endforeach

# Build and register tests
# Tools (intended for installation)
executable('razor-cli', files('tools/razor/cli.c'),
dependencies: epsilon_dep,
install: true,
install_dir: get_option('bindir')
)

# Tests
tests = {
'csv_test': 'tests/csv_test.c',
'hash_test': 'tests/hash_test.c',
'rng_test': 'tests/rng_test.c',
'stats_test': 'tests/stats_test.c',
'transform_test': 'tests/transform_test.c',
}

foreach name, source : tests
exe = executable(name, source,
dependencies: [epsilon_dep, unity_dep],
Expand All @@ -79,11 +85,10 @@ foreach name, source : tests
test(name, exe)
endforeach

# Documentation installation
# Docs & packaging
install_data(
files('README.md', 'LICENSE'),
install_dir: 'share/doc/epsilon'
)

# Distribution metadata
meson.add_dist_script('echo', 'Package contact: boris@cortext.nl')
139 changes: 139 additions & 0 deletions src/csv.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#include "csv.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <assert.h>
#include <stdbool.h>
#include <string.h>

typedef enum { START, IN_FIELD, IN_QUOTE } state_t;

// Parse a single CSV field into buf; returns pointer after field.
const char *csv_parse_field(const char *r, char *buf, size_t buf_size) {
state_t state = START;
size_t w = 0; // write position in buf

while (*r) {
char c = *r++;
switch (state) {
case START:
if (c == '"')
state = IN_QUOTE;
else if (c == ',' || c == '\n' || c == '\r')
goto done; // empty field
else {
if (w < buf_size - 1)
buf[w++] = c;
state = IN_FIELD;
}
break;

case IN_FIELD:
if (c == ',' || c == '\n' || c == '\r')
goto done;
if (w < buf_size - 1)
buf[w++] = c;
break;

case IN_QUOTE:
if (c == '"') {
if (*r == '"') {
if (w < buf_size - 1)
buf[w++] = '"';
} else {
state = IN_FIELD;
continue;
}
} else {
if (w < buf_size - 1)
buf[w++] = c;
}
break;
}
}

done:
if (w < buf_size)
buf[w] = '\0';
else
buf[buf_size - 1] = '\0';
return r;
}

// Parse a CSV row into row->fields; returns number of fields
size_t csv_parse_row(const char *r, csv_row_t *row) {
row->n_fields = 0;
char *w = row->buf; // write pointer into buffer
size_t buf_remaining = CSV_BUF_SIZE;

while (*r && row->n_fields < CSV_MAX_FIELDS) {
row->fields[row->n_fields++] = w;

r = csv_parse_field(r, w, buf_remaining);

// advance write pointer past written field
size_t field_len = strlen(w) + 1;
w += field_len;
if (field_len >= buf_remaining)
buf_remaining = 0;
else
buf_remaining -= field_len;

// move to next field or end of row
if (*r == ',')
r++;
else if (*r == '\r' && r[1] == '\n') {
r += 2;
break;
} else if (*r == '\n' || *r == '\r') {
r++;
break;
} else
break; // end of buffer
}

return row->n_fields;
}

// Initialize CSV reader
int csv_reader_open(csv_reader_t *r, const char *path) {
r->f = fopen(path, "r");
return r->f != NULL ? 0 : -1;
}

// Read next row; returns 1 on success, 0 on EOF
int csv_reader_next(csv_reader_t *r, csv_row_t *row) {
if (!r->f)
return 0;
char *p = r->buf;
size_t len = 0;
int in_quotes = 0;

while (fgets(p, CSV_BUF_SIZE - len, r->f)) {
len += strlen(p);
// Count quotes to see if row is complete
for (char *q = p; *q; q++) {
if (*q == '"')
in_quotes = !in_quotes;
}
if (!in_quotes)
break; // complete row
p = r->buf + len; // append next fgets
if (len >= CSV_BUF_SIZE - 1)
break; // prevent overflow
}

if (len == 0)
return false; // EOF

csv_parse_row(r->buf, row);
return true;
}

// Close CSV reader
void csv_reader_close(csv_reader_t *r) {
if (r->f)
fclose(r->f);
}
26 changes: 26 additions & 0 deletions src/csv.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef CSV_H
#define CSV_H

#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>

#define CSV_MAX_FIELDS 512
#define CSV_BUF_SIZE 4096

typedef struct {
char *fields[CSV_MAX_FIELDS];
char buf[CSV_BUF_SIZE];
size_t n_fields;
} csv_row_t;

typedef struct {
FILE *f;
char buf[CSV_BUF_SIZE];
} csv_reader_t;

int csv_reader_open(csv_reader_t *r, const char *path);
void csv_reader_close(csv_reader_t *r);
int csv_reader_next(csv_reader_t *r, csv_row_t *row);

#endif // CSV_H
Loading
Loading