Skip to content

Commit

Permalink
Implemented timestamp
Browse files Browse the repository at this point in the history
  • Loading branch information
ludocode committed Oct 4, 2017
1 parent af7dee5 commit 1d5e7ed
Show file tree
Hide file tree
Showing 16 changed files with 555 additions and 93 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,26 @@ Breaking Changes:

- The layout of fields for ext tags in `mpack_tag_t` has changed. An ext tag no longer uses `.v.l` for its length. The compiler will not warn you about this, so be careful when upgrading.

- `mpack_tag_t` is now considered an opaque type (to prevent future breakage when changing its layout.) Compatibility for all types besides `ext` is maintained for this release, but this may change in future releases.

- The mpack configuration `mpack-config.h` file is now optional, and requires `MPACK_HAS_CONFIG` in order to be included. This means you must define `MPACK_HAS_CONFIG` when upgrading or your config file will be ignored! (It is recommended to delete your config file and use the defaults.)

New Features:

- The timestamp type has been implemented. A timestamp is a signed number of nanoseconds since January 1st, 1970 at 12:00 UTC.

- The Node API can now parse multiple messages from a data source. `mpack_tree_parse()` can be called repeatedly to parse each message.

- The Node API can now parse messages indefinitely from a continuous stream. A tree can be initialized with `mpack_tree_init_stream()` to receive a callback for more data.

- The writer now supports a v4 compatibility mode. Call `mpack_writer_set_version(writer, mpack_version_v4);` to encode without using the `raw8` and `bin` types.
- The writer now supports a v4 compatibility mode. Call `mpack_writer_set_version(writer, mpack_version_v4);` to encode without using the `raw8`, `bin`, `ext` and `timestamp` types.

- The stdio helpers now allow reading from a `FILE*`. `_init_file()` functions have been renamed to `_init_filename()`. The old names will continue to work for a few more versions.

Changes:

- Timestamps (exttype -1) are no longer reported as ext types. If you were reading an ext of exttype -1 and parsing the timestamp from it manually, you will need to switch over to the new timestamp functions.

- The reader's skip function is no longer ignored under `MPACK_OPTIMIZE_FOR_SIZE`.

- Fixed an allocation bug when closing a growable writer without having written anything.
Expand Down
39 changes: 38 additions & 1 deletion src/mpack/mpack-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ int mpack_tag_cmp(mpack_tag_t left, mpack_tag_t right) {
}

if (left.type != right.type)
return (int)left.type - (int)right.type;
return ((int)left.type < (int)right.type) ? -1 : 1;

switch (left.type) {
case mpack_type_nil:
Expand Down Expand Up @@ -129,6 +129,15 @@ int mpack_tag_cmp(mpack_tag_t left, mpack_tag_t right) {
}
return (int)left.v.ext.exttype - (int)right.v.ext.exttype;

case mpack_type_timestamp:
if (left.v.timestamp.seconds == right.v.timestamp.seconds) {
if (left.v.timestamp.nanoseconds == right.v.timestamp.nanoseconds) {
return 0;
}
return (left.v.timestamp.nanoseconds < right.v.timestamp.nanoseconds) ? -1 : 1;
}
return (left.v.timestamp.seconds < right.v.timestamp.seconds) ? -1 : 1;

// floats should not normally be compared for equality. we compare
// with memcmp() to silence compiler warnings, but this will return
// equal if both are NaNs with the same representation (though we may
Expand Down Expand Up @@ -177,6 +186,7 @@ void mpack_tag_debug_pseudo_json(mpack_tag_t tag, char* buffer, size_t buffer_si
case mpack_type_double:
mpack_snprintf(buffer, buffer_size, "%f", tag.v.d);
break;

case mpack_type_str:
mpack_snprintf(buffer, buffer_size, "<string of %u bytes>", tag.v.l);
break;
Expand All @@ -187,12 +197,27 @@ void mpack_tag_debug_pseudo_json(mpack_tag_t tag, char* buffer, size_t buffer_si
mpack_snprintf(buffer, buffer_size, "<ext data of type %i and length %u>",
tag.v.ext.exttype, tag.v.ext.length);
break;

case mpack_type_array:
mpack_snprintf(buffer, buffer_size, "<array of %u elements>", tag.v.n);
break;
case mpack_type_map:
mpack_snprintf(buffer, buffer_size, "<map of %u key-value pairs>", tag.v.n);
break;

case mpack_type_timestamp:
{
int64_t seconds = tag.v.timestamp.seconds;
uint32_t nanoseconds = tag.v.timestamp.nanoseconds;
if (nanoseconds == 0) {
mpack_snprintf(buffer, buffer_size, "<timestamp %" PRIi64 ">", seconds);
} else {
mpack_snprintf(buffer, buffer_size, "<timestamp %" PRIi64 ".%09u>",
seconds, nanoseconds);
}
}
break;

default:
mpack_snprintf(buffer, buffer_size, "<unknown!>");
break;
Expand Down Expand Up @@ -241,6 +266,18 @@ void mpack_tag_debug_describe(mpack_tag_t tag, char* buffer, size_t buffer_size)
case mpack_type_map:
mpack_snprintf(buffer, buffer_size, "map of %u key-value pairs", tag.v.n);
break;
case mpack_type_timestamp:
{
int64_t seconds = tag.v.timestamp.seconds;
uint32_t nanoseconds = tag.v.timestamp.nanoseconds;
if (nanoseconds == 0) {
mpack_snprintf(buffer, buffer_size, "timestamp %" PRIi64 , seconds);
} else {
mpack_snprintf(buffer, buffer_size, "timestamp %" PRIi64 ".%09u",
seconds, nanoseconds);
}
}
break;
default:
mpack_snprintf(buffer, buffer_size, "unknown!");
break;
Expand Down
106 changes: 91 additions & 15 deletions src/mpack/mpack-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,18 @@ MPACK_HEADER_START
/**
* @def MPACK_MAXIMUM_TAG_SIZE
*
* The maximum encoded size of a tag in bytes, as of the "new" MessagePack spec.
* The maximum encoded size of a tag in bytes.
*/
#define MPACK_MAXIMUM_TAG_SIZE 9
#define MPACK_MAXIMUM_TAG_SIZE MPACK_TAG_SIZE_TIMESTAMP12 // 15
/** @endcond */

/**
* @def MPACK_TIMESTAMP_NANOSECONDS_MAX
*
* The maximum value of nanoseconds for a timestamp.
*/
#define MPACK_TIMESTAMP_NANOSECONDS_MAX 999999999



#if MPACK_COMPATIBILITY
Expand All @@ -123,7 +130,8 @@ typedef enum mpack_version_t {
mpack_version_v4 = 4,

/**
* Version 2.0/v5, supporting the @c str8, @c bin and @c ext types.
* Version 2.0/v5, supporting the @c str8, @c bin and @c ext types
* (including timestamp.)
*/
mpack_version_v5 = 5,

Expand Down Expand Up @@ -164,17 +172,18 @@ const char* mpack_error_to_string(mpack_error_t error);
* Defines the type of a MessagePack tag.
*/
typedef enum mpack_type_t {
mpack_type_nil = 1, /**< A null value. */
mpack_type_bool, /**< A boolean (true or false.) */
mpack_type_int, /**< A 64-bit signed integer. */
mpack_type_uint, /**< A 64-bit unsigned integer. */
mpack_type_float, /**< A 32-bit IEEE 754 floating point number. */
mpack_type_double, /**< A 64-bit IEEE 754 floating point number. */
mpack_type_str, /**< A string. */
mpack_type_bin, /**< A chunk of binary data. */
mpack_type_ext, /**< A typed MessagePack extension object containing a chunk of binary data. */
mpack_type_array, /**< An array of MessagePack objects. */
mpack_type_map, /**< An ordered map of key/value pairs of MessagePack objects. */
mpack_type_nil = 1, /**< A null value. */
mpack_type_bool, /**< A boolean (true or false.) */
mpack_type_int, /**< A 64-bit signed integer. */
mpack_type_uint, /**< A 64-bit unsigned integer. */
mpack_type_float, /**< A 32-bit IEEE 754 floating point number. */
mpack_type_double, /**< A 64-bit IEEE 754 floating point number. */
mpack_type_str, /**< A string. */
mpack_type_bin, /**< A chunk of binary data. */
mpack_type_ext, /**< A typed MessagePack extension object containing a chunk of binary data. */
mpack_type_array, /**< An array of MessagePack objects. */
mpack_type_map, /**< An ordered map of key/value pairs of MessagePack objects. */
mpack_type_timestamp, /**< A timestamp. */
} mpack_type_t;

/**
Expand All @@ -183,6 +192,14 @@ typedef enum mpack_type_t {
*/
const char* mpack_type_to_string(mpack_type_t type);

/**
* A timestamp.
*/
typedef struct mpack_timestamp_t {
int64_t seconds; /*< The number of seconds (signed) since 1970-01-01T00:00:00Z. */
uint32_t nanoseconds; /*< The number of additional nanoseconds, between 0 and 999,999,999. */
} mpack_timestamp_t;

/**
* An MPack tag is a MessagePack object header. It is a variant type representing
* any kind of object, and includes the value of that object when it is not a
Expand Down Expand Up @@ -222,13 +239,39 @@ struct mpack_tag_t {
int8_t exttype; /*< The extension type. */
uint32_t length; /*< The number of bytes. */
} ext;

/* The value if the type is @ref mpack_type_timestamp. */
mpack_timestamp_t timestamp;
} v;

/* The type of value. */
mpack_type_t type;
};
/** @endcond */

/**
* @name Other tag functions
* @{
*/

/** @cond */
// Currently defined extension types
#define MPACK_TIMESTAMP_EXTTYPE ((int8_t)(-1))
/** @endcond */

MPACK_INLINE void mpack_tag_assert_valid(mpack_tag_t* tag) {
mpack_assert(tag->type != mpack_type_ext || tag->v.ext.exttype != MPACK_TIMESTAMP_EXTTYPE,
"ext tag of exttype %i is reserved for timestamps.", MPACK_TIMESTAMP_EXTTYPE);
mpack_assert(tag->type != mpack_type_timestamp ||
tag->v.timestamp.nanoseconds <= MPACK_TIMESTAMP_NANOSECONDS_MAX,
"timestamp tag is not valid! %u nanoseconds is out of bounds",
tag->v.timestamp.nanoseconds);
}

/**
* @}
*/

/**
* @name Tag Generators
* @{
Expand Down Expand Up @@ -342,6 +385,36 @@ MPACK_INLINE mpack_tag_t mpack_tag_make_ext(int8_t exttype, uint32_t length) {
ret.type = mpack_type_ext;
ret.v.ext.exttype = exttype;
ret.v.ext.length = length;
mpack_tag_assert_valid(&ret);
return ret;
}

/** Generates a timestamp tag from a number of seconds and nanoseconds. */
MPACK_INLINE mpack_tag_t mpack_tag_make_timestamp(int64_t seconds, uint32_t nanoseconds) {
mpack_tag_t ret = MPACK_TAG_ZERO;
ret.type = mpack_type_timestamp;
ret.v.timestamp.seconds = seconds;
ret.v.timestamp.nanoseconds = nanoseconds;
mpack_tag_assert_valid(&ret);
return ret;
}

/** Generates a timestamp tag from a number of seconds (with zero nanoseconds.) */
MPACK_INLINE mpack_tag_t mpack_tag_make_timestamp_seconds(int64_t seconds) {
mpack_tag_t ret = MPACK_TAG_ZERO;
ret.type = mpack_type_timestamp;
ret.v.timestamp.seconds = seconds;
ret.v.timestamp.nanoseconds = 0;
mpack_tag_assert_valid(&ret);
return ret;
}

/** Generates a timestamp tag from an mpack_timestamp_t. */
MPACK_INLINE mpack_tag_t mpack_tag_make_timestamp_struct(mpack_timestamp_t timestamp) {
mpack_tag_t ret = MPACK_TAG_ZERO;
ret.type = mpack_type_timestamp;
ret.v.timestamp = timestamp;
mpack_tag_assert_valid(&ret);
return ret;
}

Expand Down Expand Up @@ -483,7 +556,7 @@ MPACK_INLINE int8_t mpack_tag_ext_exttype(mpack_tag_t* tag) {
*/

/**
* @name other tag functions
* @name Other tag functions
* @{
*/

Expand Down Expand Up @@ -820,6 +893,9 @@ MPACK_INLINE void mpack_store_double(char* p, double value) {
#define MPACK_TAG_SIZE_EXT8 3
#define MPACK_TAG_SIZE_EXT16 4
#define MPACK_TAG_SIZE_EXT32 6
#define MPACK_TAG_SIZE_TIMESTAMP4 (MPACK_TAG_SIZE_FIXEXT4 + 4)
#define MPACK_TAG_SIZE_TIMESTAMP8 (MPACK_TAG_SIZE_FIXEXT8 + 8)
#define MPACK_TAG_SIZE_TIMESTAMP12 (MPACK_TAG_SIZE_EXT8 + 12)

/** @endcond */

Expand Down
17 changes: 17 additions & 0 deletions src/mpack/mpack-expect.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,23 @@ void mpack_expect_false(mpack_reader_t* reader) {
mpack_reader_flag_error(reader, mpack_error_type);
}

mpack_timestamp_t mpack_expect_timestamp(mpack_reader_t* reader) {
mpack_tag_t var = mpack_read_tag(reader);
if (var.type == mpack_type_timestamp)
return var.v.timestamp;
mpack_reader_flag_error(reader, mpack_error_type);
mpack_timestamp_t zero = {0, 0};
return zero;
}

int64_t mpack_expect_timestamp_truncate(mpack_reader_t* reader) {
mpack_tag_t var = mpack_read_tag(reader);
if (var.type == mpack_type_timestamp)
return var.v.timestamp.seconds;
mpack_reader_flag_error(reader, mpack_error_type);
return 0;
}


// Compound Types

Expand Down
9 changes: 9 additions & 0 deletions src/mpack/mpack-expect.h
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,15 @@ void mpack_expect_true(mpack_reader_t* reader);
*/
void mpack_expect_false(mpack_reader_t* reader);

/**
* Reads a timestamp.
*/
mpack_timestamp_t mpack_expect_timestamp(mpack_reader_t* reader);

/**
* Reads a timestamp in seconds, truncating the nanoseconds (if any).
*/
int64_t mpack_expect_timestamp_truncate(mpack_reader_t* reader);

/**
* @}
Expand Down
Loading

0 comments on commit 1d5e7ed

Please sign in to comment.