Skip to content

Commit

Permalink
Add c implementation of date decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
bschoenmaeckers committed Jan 2, 2024
1 parent c1aee56 commit e17c4bd
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 6 deletions.
92 changes: 89 additions & 3 deletions source/decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ static PyObject * decode_bytestring(CBORDecoderObject *, uint8_t);
static PyObject * decode_string(CBORDecoderObject *, uint8_t);
static PyObject * CBORDecoder_decode_datetime_string(CBORDecoderObject *);
static PyObject * CBORDecoder_decode_epoch_datetime(CBORDecoderObject *);
static PyObject * CBORDecoder_decode_epoch_date(CBORDecoderObject *);
static PyObject * CBORDecoder_decode_date_string(CBORDecoderObject *);
static PyObject * CBORDecoder_decode_fraction(CBORDecoderObject *);
static PyObject * CBORDecoder_decode_bigfloat(CBORDecoderObject *);
static PyObject * CBORDecoder_decode_rational(CBORDecoderObject *);
Expand Down Expand Up @@ -974,10 +976,12 @@ decode_semantic(CBORDecoderObject *self, uint8_t subtype)
case 35: ret = CBORDecoder_decode_regexp(self); break;
case 36: ret = CBORDecoder_decode_mime(self); break;
case 37: ret = CBORDecoder_decode_uuid(self); break;
case 100: ret = CBORDecoder_decode_epoch_date(self); break;
case 256: ret = CBORDecoder_decode_stringref_ns(self); break;
case 258: ret = CBORDecoder_decode_set(self); break;
case 260: ret = CBORDecoder_decode_ipaddress(self); break;
case 261: ret = CBORDecoder_decode_ipnetwork(self); break;
case 1004: ret = CBORDecoder_decode_date_string(self); break;
case 55799: ret = CBORDecoder_decode_self_describe_cbor(self);
break;

Expand Down Expand Up @@ -1009,7 +1013,7 @@ decode_semantic(CBORDecoderObject *self, uint8_t subtype)


static PyObject *
parse_datestr(CBORDecoderObject *self, PyObject *str)
parse_datetimestr(CBORDecoderObject *self, PyObject *str)
{
const char* buf;
char *p;
Expand Down Expand Up @@ -1082,6 +1086,31 @@ parse_datestr(CBORDecoderObject *self, PyObject *str)
return ret;
}

static PyObject *
parse_datestr(CBORDecoderObject *self, PyObject *str)
{
const char* buf;
Py_ssize_t size;
PyObject *ret = NULL;
unsigned long int Y, m, d;

buf = PyUnicode_AsUTF8AndSize(str, &size);
if (
size < 10 || buf[4] != '-' || buf[7] != '-')
{
PyErr_Format(
_CBOR2_CBORDecodeValueError, "invalid date string %R", str);
return NULL;
}
if (buf) {
Y = strtoul(buf, NULL, 10);
m = strtoul(buf + 5, NULL, 10);
d = strtoul(buf + 8, NULL, 10);
ret = PyDate_FromDate(Y, m, d);
}
return ret;
}


// CBORDecoder.decode_datetime_string(self)
static PyObject *
Expand All @@ -1090,6 +1119,63 @@ CBORDecoder_decode_datetime_string(CBORDecoderObject *self)
// semantic type 0
PyObject *match, *str, *ret = NULL;

if (!_CBOR2_datetimestr_re && _CBOR2_init_re_compile() == -1)
return NULL;
str = decode(self, DECODE_NORMAL);
if (str) {
if (PyUnicode_Check(str)) {
match = PyObject_CallMethodObjArgs(
_CBOR2_datetimestr_re, _CBOR2_str_match, str, NULL);
if (match) {
if (match != Py_None)
ret = parse_datetimestr(self, str);
else
PyErr_Format(
_CBOR2_CBORDecodeValueError,
"invalid datetime string: %R", str);
Py_DECREF(match);
}
} else
PyErr_Format(
_CBOR2_CBORDecodeValueError, "invalid datetime value: %R", str);
Py_DECREF(str);
}
set_shareable(self, ret);
return ret;
}

// CBORDecoder.decode_epoch_date(self)
static PyObject *
CBORDecoder_decode_epoch_date(CBORDecoderObject *self)
{
// semantic type 100
PyObject *num, *tuple, *ret = NULL;

num = decode(self, DECODE_NORMAL);
if (num) {
if (PyNumber_Check(num)) {
tuple = PyTuple_Pack(1, PyNumber_Multiply(num, PyLong_FromLong(24 * 60 * 60)));
if (tuple) {
ret = PyDate_FromTimestamp(tuple);
Py_DECREF(tuple);
}
} else {
PyErr_Format(
_CBOR2_CBORDecodeValueError, "invalid timestamp value %R", num);
}
Py_DECREF(num);
}
set_shareable(self, ret);
return ret;
}

// CBORDecoder.decode_date_string(self)
static PyObject *
CBORDecoder_decode_date_string(CBORDecoderObject *self)
{
// semantic type 0
PyObject *match, *str, *ret = NULL;

if (!_CBOR2_datestr_re && _CBOR2_init_re_compile() == -1)
return NULL;
str = decode(self, DECODE_NORMAL);
Expand All @@ -1103,12 +1189,12 @@ CBORDecoder_decode_datetime_string(CBORDecoderObject *self)
else
PyErr_Format(
_CBOR2_CBORDecodeValueError,
"invalid datetime string: %R", str);
"invalid date string: %R", str);
Py_DECREF(match);
}
} else
PyErr_Format(
_CBOR2_CBORDecodeValueError, "invalid datetime value: %R", str);
_CBOR2_CBORDecodeValueError, "invalid date value: %R", str);
Py_DECREF(str);
}
set_shareable(self, ret);
Expand Down
14 changes: 12 additions & 2 deletions source/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,10 @@ _CBOR2_init_re_compile(void)
Py_DECREF(re);
if (!_CBOR2_re_compile)
goto error;
_CBOR2_datetimestr_re = PyObject_CallFunctionObjArgs(
_CBOR2_re_compile, _CBOR2_str_datetimestr_re, NULL);
if (!_CBOR2_datetimestr_re)
goto error;
_CBOR2_datestr_re = PyObject_CallFunctionObjArgs(
_CBOR2_re_compile, _CBOR2_str_datestr_re, NULL);
if (!_CBOR2_datestr_re)
Expand Down Expand Up @@ -609,6 +613,7 @@ PyObject *_CBOR2_str_BytesIO = NULL;
PyObject *_CBOR2_str_canonical_encoders = NULL;
PyObject *_CBOR2_str_compile = NULL;
PyObject *_CBOR2_str_copy = NULL;
PyObject *_CBOR2_str_datetimestr_re = NULL;
PyObject *_CBOR2_str_datestr_re = NULL;
PyObject *_CBOR2_str_Decimal = NULL;
PyObject *_CBOR2_str_default_encoders = NULL;
Expand Down Expand Up @@ -661,6 +666,7 @@ PyObject *_CBOR2_FrozenDict = NULL;
PyObject *_CBOR2_UUID = NULL;
PyObject *_CBOR2_Parser = NULL;
PyObject *_CBOR2_re_compile = NULL;
PyObject *_CBOR2_datetimestr_re = NULL;
PyObject *_CBOR2_datestr_re = NULL;
PyObject *_CBOR2_ip_address = NULL;
PyObject *_CBOR2_ip_network = NULL;
Expand All @@ -680,6 +686,7 @@ cbor2_free(PyObject *m)
Py_CLEAR(_CBOR2_UUID);
Py_CLEAR(_CBOR2_Parser);
Py_CLEAR(_CBOR2_re_compile);
Py_CLEAR(_CBOR2_datetimestr_re);
Py_CLEAR(_CBOR2_datestr_re);
Py_CLEAR(_CBOR2_ip_address);
Py_CLEAR(_CBOR2_ip_network);
Expand Down Expand Up @@ -977,13 +984,16 @@ PyInit__cbor2(void)
if (!_CBOR2_str_utc_suffix &&
!(_CBOR2_str_utc_suffix = PyUnicode_InternFromString("+00:00")))
goto error;
if (!_CBOR2_str_datestr_re &&
!(_CBOR2_str_datestr_re = PyUnicode_InternFromString(
if (!_CBOR2_str_datetimestr_re &&
!(_CBOR2_str_datetimestr_re = PyUnicode_InternFromString(
"^(\\d{4})-(\\d\\d)-(\\d\\d)T" // Y-m-d
"(\\d\\d):(\\d\\d):(\\d\\d)" // H:M:S
"(?:\\.(\\d{1,6})\\d*)?" // .uS
"(?:Z|([+-]\\d\\d):(\\d\\d))$"))) // +-TZ
goto error;
if (!_CBOR2_str_datestr_re &&
!(_CBOR2_str_datestr_re = PyUnicode_InternFromString("^(\\d{4})-(\\d\\d)-(\\d\\d)"))) // Y-m-d
goto error;
if (!_CBOR2_empty_bytes &&
!(_CBOR2_empty_bytes = PyBytes_FromStringAndSize(NULL, 0)))
goto error;
Expand Down
4 changes: 3 additions & 1 deletion source/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ extern PyObject *_CBOR2_str_BytesIO;
extern PyObject *_CBOR2_str_canonical_encoders;
extern PyObject *_CBOR2_str_compile;
extern PyObject *_CBOR2_str_copy;
extern PyObject *_CBOR2_str_datetimestr_re;
extern PyObject *_CBOR2_str_datestr_re;
extern PyObject *_CBOR2_str_Decimal;
extern PyObject *_CBOR2_str_default_encoders;
Expand Down Expand Up @@ -94,6 +95,7 @@ extern PyObject *_CBOR2_FrozenDict;
extern PyObject *_CBOR2_UUID;
extern PyObject *_CBOR2_Parser;
extern PyObject *_CBOR2_re_compile;
extern PyObject *_CBOR2_datetimestr_re;
extern PyObject *_CBOR2_datestr_re;
extern PyObject *_CBOR2_ip_address;
extern PyObject *_CBOR2_ip_network;
Expand All @@ -107,7 +109,7 @@ int _CBOR2_init_Fraction(void);
int _CBOR2_init_FrozenDict(void);
int _CBOR2_init_UUID(void);
int _CBOR2_init_Parser(void);
int _CBOR2_init_re_compile(void); // also handles datestr_re
int _CBOR2_init_re_compile(void); // also handles datetimestr_re & datestr_re
int _CBOR2_init_ip_address(void);
int _CBOR2_init_thread_locals(void);

Expand Down

0 comments on commit e17c4bd

Please sign in to comment.