187 lines
6.2 KiB
C
187 lines
6.2 KiB
C
/** @license 2023 Neil Edelman, distributed under the terms of the
|
|
[MIT License](https://opensource.org/licenses/MIT).
|
|
|
|
A `pair` is `[a, b)` pair of pointers to char. Used in most string parsing.
|
|
|
|
@std C99 */
|
|
|
|
#include "pair.h"
|
|
#include <stdint.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
|
|
/** @return Constructs `a` and `b` as a pair [or else (0,0)?]. */
|
|
struct pair pair(const char *const a, const char *const b) {
|
|
struct pair p;
|
|
assert(a && a <= b);
|
|
p.a = a, p.b = b;
|
|
return p;
|
|
}
|
|
|
|
/** Doesn't check if the number is actually in [0, 9].
|
|
@return Whether it was able to parse unsigned [`a`, `b`] to `n`. */
|
|
int pair_to_natural(const char *a, const char *const b, uint32_t *const n) {
|
|
uint32_t accum = 0;
|
|
while(a < b) {
|
|
unsigned next = accum * 10 + (unsigned)(*a - '0');
|
|
if(accum > next) return errno = ERANGE, 0;
|
|
accum = next;
|
|
a++;
|
|
}
|
|
return *n = accum, 1;
|
|
}
|
|
|
|
static int pair_to_64(const char *a, const char *const b, uint64_t *const n) {
|
|
uint64_t accum = 0;
|
|
while(a < b) {
|
|
uint64_t next = accum * 10 + (uint64_t)(*a - '0');
|
|
if(accum > next) return errno = ERANGE, 0;
|
|
accum = next;
|
|
a++;
|
|
}
|
|
return *n = accum, 1;
|
|
}
|
|
|
|
/** `h0` "1" `h1` ":" `m0` "30" `m1` -> 90 `n` @return Valid. */
|
|
int pair_colon_to_minutes(const char *h0, const char *const h1,
|
|
const char *m0, const char *const m1, uint32_t *const n) {
|
|
uint32_t hours, minutes;
|
|
return pair_to_natural(h0, h1, &hours) && pair_to_natural(m0, m1, &minutes)
|
|
&& minutes < 60 && hours <= UINT32_MAX / 60 - minutes
|
|
? (*n = hours * 60 + minutes, 1) : 0;
|
|
}
|
|
|
|
/** `h0` "1" `h1` "." `m0` "5" `m1` -> 90 `n` @return Valid. */
|
|
int pair_hours_to_minutes(const char *h0, const char *const h1,
|
|
const char *m0, const char *const m1, uint32_t *const n) {
|
|
uint32_t hours, minutes;
|
|
/* fixme: more precision? */
|
|
return pair_to_natural(h0, h1, &hours) && pair_to_natural(m0, m1, &minutes)
|
|
&& minutes <= 9 && hours <= UINT32_MAX / 60 - minutes * 6
|
|
? (*n = hours * 60 + minutes * 6, 1) : 0;
|
|
}
|
|
|
|
/** @return The content of `x` is the same as `y`. */
|
|
int pair_is_equal(struct pair x, struct pair y) {
|
|
assert(x.a <= x.b && y.a <= y.b);
|
|
if(!x.a) return !y.a;
|
|
if(!y.a) return 0;
|
|
if(x.b - x.a != y.b - y.a) return 0;
|
|
while(x.a < x.b) { if(*x.a != *y.a) return 0; x.a++, y.a++; }
|
|
return 1;
|
|
}
|
|
|
|
/** @return Exact match between a pair `x` (start-end pointers) and a string
|
|
`y` (null-terminated). */
|
|
int pair_is_string(struct pair x, const char *y) {
|
|
assert(x.a <= x.b);
|
|
if(!x.a) return !y;
|
|
if(!y) return 0;
|
|
while(x.a < x.b) { if(*x.a != *y || !*y) return 0; x.a++, y++; }
|
|
return !*y;
|
|
}
|
|
/** Is `y` a leap-year? */
|
|
static int leap(int y) {
|
|
assert(y >= 1582);
|
|
if(!(y % 400)) return 1;
|
|
if(!(y % 100)) return 0;
|
|
if(!(y % 4)) return 1;
|
|
return 0;
|
|
}
|
|
/** Convert or narrower type or return zero. */
|
|
static union date32 date_to_32(const uint32_t y, const uint32_t m,
|
|
const uint32_t d) {
|
|
union date32 d32 = { 0 };
|
|
/* Leap year calculations only work at y>=1 and Gregorian Calendar and max
|
|
23 bits. */
|
|
if(y < 1582 || y > 8388607 || m < 1 || m > 12 || d < 1 || d > 31) goto no;
|
|
switch(m) {
|
|
case 1: case 3: case 5: case 7: case 8: case 10: case 12: break;
|
|
case 4: case 6: case 9: case 11: if(d > 30) goto no; break;
|
|
case 2: if(d > (uint32_t)(28 + leap((int)y))) goto no; break;
|
|
default: assert(0); break;
|
|
}
|
|
d32.year = (unsigned)y, d32.month = (unsigned)m, d32.day = (unsigned)d;
|
|
no:
|
|
return d32;
|
|
}
|
|
/** `a` represents the start of a date in y...-mm-dd, which is stored in `d`.
|
|
@return Success.
|
|
@throws[ERANGE] Year is more then 23 bits.
|
|
@throws[EILSEQ] The is not a date? */
|
|
int pair_to_date(const char *a, union date32 *const d) {
|
|
uint32_t year = 0, month, day;
|
|
union date32 temp;
|
|
assert(a && d);
|
|
while(*a >= '0' && *a <= '9') {
|
|
year = year * 10 + (unsigned)(*a - '0');
|
|
if(year > 0x7FFFFF) return errno = ERANGE, 0; /* 23 bits */
|
|
a++;
|
|
}
|
|
if(a[0] != '-' || a[1] == '\0' || a[2] == '\0'
|
|
|| a[3] != '-' || a[4] == '\0' || a[5] == '\0')
|
|
return errno = EILSEQ, 0;
|
|
month = (unsigned)(a[1] - '0') * 10 + (unsigned)(a[2] - '0');
|
|
day = (unsigned)(a[4] - '0') * 10 + (unsigned)(a[5] - '0');
|
|
temp = date_to_32(year, month, day);
|
|
if(!temp.u32) return errno = EILSEQ, 0;
|
|
return *d = temp, 1;
|
|
}
|
|
|
|
int pair_to_cents(const char *a, const char *b, int64_t *const cents) {
|
|
uint64_t d, c;
|
|
int is_negative;
|
|
assert(a && a < b && cents);
|
|
if(a[0] == '-') is_negative = 1, a++, assert(a < b);
|
|
else is_negative = 0;
|
|
if(a + 2 < b && b[-3] == '.') { /* dollars.cents */
|
|
if(!pair_to_64(a, b - 3, &d)) return 0;
|
|
c = (uint64_t)(b[-2] - '0') * 10 + (uint64_t)(b[-1] - '0');
|
|
} else { /* dollars */
|
|
if(!pair_to_64(a, b, &d)) return 0;
|
|
c = 0;
|
|
}
|
|
assert(-INT64_MAX >= INT64_MIN);
|
|
if(INT64_MAX / 100 < d || INT64_MAX - d * 100 < c)
|
|
return errno = ERANGE, 0;
|
|
/* Misses one in 2's. Not a very nuanced conversion. */
|
|
*cents = (is_negative ? -1 : 1) * (int64_t)(d * 100 + c);
|
|
return 1;
|
|
}
|
|
|
|
/** @return A djb2 <http://www.cse.yorku.ca/~oz/hash.html> hash of `p`. */
|
|
uint32_t pair_djb2(struct pair p) {
|
|
uint32_t hash = 5381, c;
|
|
while(p.a < p.b) {
|
|
c = (unsigned char)*p.a++;
|
|
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
#include <stdio.h> /* sprintf */
|
|
/* Maps from substring keywords to indices, default zero. */
|
|
static void pairmap_to_string(const struct pair key, const size_t i,
|
|
char (*const a)[12]) { (void)key; sprintf(*a, "%zu", i); }
|
|
static int pairmap_is_equal(const struct pair a, const struct pair b)
|
|
{ return pair_is_equal(a, b); }
|
|
static uint32_t pairmap_hash(const struct pair p) { return pair_djb2(p); }
|
|
#define TABLE_NAME pairmap
|
|
#define TABLE_KEY struct pair
|
|
#define TABLE_UINT uint32_t
|
|
#define TABLE_VALUE size_t
|
|
#define TABLE_DEFAULT 0 /* Default set at zero. */
|
|
#define TABLE_TO_STRING
|
|
#define TABLE_BODY
|
|
#include "../src/table.h"
|
|
|
|
struct pairmap_table pair_map_table(void) { return pairmap_table(); }
|
|
void pair_map_table_(struct pairmap_table *const t) { pairmap_table_(t); }
|
|
const char *pair_map_table_to_string(const struct pairmap_table *const t)
|
|
{ return pairmap_table_to_string(t); }
|
|
enum table_result pair_map_table_assign(struct pairmap_table *const t,
|
|
const struct pair key, size_t **const content)
|
|
{ return pairmap_table_assign(t, key, content); }
|
|
size_t pair_map_table_get(struct pairmap_table *const t, const struct pair key)
|
|
{ return pairmap_table_get(t, key); }
|