Skip to content

Commit

Permalink
Support Discord async and fix its ratelimiting, improve flexibility o…
Browse files Browse the repository at this point in the history
…f Orca's core functionalities, and more (#753)

* style(examples): fix weird .clang-format formatting by adding trailing commas

* chore(.clang-format): update for ease of switching between configs

* wip(user-agent): add ua_enqueue() for multiplexing requests

* wip(test): add test-discord-multiplex.c

* fix(user-agent): CURLOPT_POSTFIELDSIZE must be called first than CURLOPT_COPYPOSTFIELDS

* docs(websockets.c): typo

* chore(common/third-party): add pqueue to  and update licenses

* style(websockets): '_ws_' function prefixing for consistency

* wip: generic worker thread implementation

* feat(types.h): add ORCA_GLOBAL_INIT error type

* chore(user-agent): remove unnecessary functions

* chore: change some comments

* feat: check to initialize globals once

* wip: replace discord's threadpool with generic implementation

* fix(test-discord-ws.c): setenv() should be called before threadpool initialization

* chore(work.c): minimum size of 8 slots per threadpool's queue

* feat(user-agent): add ua_set_curl_multi()

* docs(user-agent.h): document all functions and datatypes

* refactor(work.c): remove libpqueue from dependencies and code

* feat: add libuv's queue.h

* wip(user-agent): queue functions for asynchronous IO implementation
* refactor(user-agent): fix premature locking
* docs(user-agent): document leftover fields and a couple
  functionalities
* refactor(user-agent): make ua_conn a public opaque datatype, add a
  field for holding onto user callback and context

* chore(common): move queue.h to common/third-party/

* docs: improve explanation for ua_clone() and discord_clone()

* refactor(user-agent): move internal _ua_conn_setup() logging to outside, queue functions

* feat(discord): add discord_timestamp()

* refactor(websockets): move cleanup logic from ws_perform() to ws_end()

* chore: update to match 4c3eb14

* refactor(examples): replace cee_timestamp_ms() with discord_timestamp()

* refactor(websockets): expose CURLM and CURL handles used internally
* feat(websockets.c): replace pthread_mutex with pthread_rwlock where it makes sense
* refactor(websockets.c): simplify ws_perform() to return a bool on whether the connection is alive
* refactor(websockets): add struct ws_attr for passing optional fields.
* feat(websockets): add ws_timestamp_update() for manually updating internally used timestamp
* refactor(websockets.c): remove noop functions and simply check for NULL

* chore: update to match 656da47

* refactor(user-agent): ua_init() expect a struct ua_attr
* feat(user-agent): add ua_timestamp() for last request performed

* chore: update to match 87d3110

* docs(user-agent.c): fix comment

* fix(discord-ratelimit): don't rely on system time for 'X-Ratelimit-Reset'
* refactor(discord-ratelimit): use a shared variable for global
  ratelimiting
* chore(discord): organize and document code somewhat

* chore: remove ua_block_ms(), there are better solutions for global ratelimiting (see discord)

* fix(user-agent.c): no need to let libcurl create its unique copy for POST fields

* chore(third-party): add libuv's heap-inl.h for min-max heap

* fix(discord-adapter): JSON error callback being ignored for functions without a resp_handle assigned

* wip(discord): use custom CURLM handle for websockets

* chore(user-agent): remove unnecessary queue functionalities

* feat(user-agent): make ua_conn_get() and ua_conn_setup() public

* chore: remove pqueue from codebase

* refactor(user-agent): replace connection pool logic with queue.h

* fix: undefined behavior mentioned at libuv/libuv#565

* chore(user-agent.c): fix typo

* wip(discord): isolate ratelimit logic

* feat(discord-ratelimit): all requests will be assigned to a bucket, this will make request enqueing easier (for asynchronous purposes)

* fix(discord-ratelimit): memory leak

* chore(.clang-format): break before non-assignment operators

* fix(discord-adapter.c): major parameter buckets must be decided on a 'id' basis

* chore(discord-adapter.c): fix minor typo

* chore(discord-adapter.c): correct buffer size

* refactor(discord-adapter.c): simplify major param selector

* chore(discord-ratelimit.c): null bucket tags should be [null] instead of [?]

* refactor(discord-adapter.c): modularize 8a38f37

* refactor(discord-gateway.c): remove repetitive shutdown logging

* refactor(discord-ratelimit.c): unnecessary tmp buffer for holding hash
* docs(discord-ratelimit.c): add ratelimiting rationale

* docs(discord-internal.h): brief explanation for 'struct discord_route'

* refactor: prefix singleton buckets with 'b_'

* refactor(discord-gateway): simplify event scheduling logic
* chore(discord-gateway): remove on_event_raw event because the same can
  be achieved by the discord_set_event_scheduler() callback

* wip(discord-adapter.c): add discord_adapter_enqueue() for asynchronous requests

* fix(discord): move request queues to 'struct discord_adapter'

* wip(discord-adapter): check for IO polling results with discord_adapter_check()

* chore(.clang-format): don't allow single-line for case label

* feat(discord-ratelimit.c): add discord_route_get() and improve docs for discord_bucket_get()

* wip(discord-adapter.c): asynchronous IO request enqueueing logic
* refactor(discord-adapter.c): modularize code that should be used in both
  sync and async scenarios

* fix(discord-adapter.c): wrong info

* feat!(user-agent): replace request timestamp with libcurl provided elapsed time, remove ua_timestamp()
* refactor(user-agent.c): move 'post-request' logic from _ua_conn_send() to _ua_conn_check_status()
* refactor(user-agent.c): rename _ua_conn_reset() to ua_conn_stop() and
  make it public, rename ua_conn_get() to ua_conn_start()

* chore(js_user-agent.c): match fad5f changes

* refactor(discord-ratelimit.c): checking for bucket's update timestamp is unnecessary

* refactor(discord-internal.h): 'struct discord_request_cxt' stores
'struct ua_conn'
* docs(discord-internal.h): more descriptive documentation for 'struct
  discord_request_cxt'
* chore(discord-internal.h): get rid of bucket.update_tstamp

* feat(discord-adapter.c): get and assign 'struct ua_conn' to the request handler (4768962)

* feat(user-agent): add discord_conn_get_results()
* refactor(user-agent.c): simplify code, get rid of compound literals
  and repetition

* chore(js_user-agent.c): update to match 6eb0740

* fix(user-agent.h): declare ua_conn_get_results(), change docs regarding 'info' parameters

* wip(discord-adapter.c): make async layout similar to _discord_adapter_request(), except queues

* wip(discord): discord_set_async() for performing requests asynchronously
* wip(discord-adapter.c): add a couple TODOs
* chore(discord): rename 'discord_request_cxt' to 'discord_request',
  and 'discord_event_cxt' to 'discord_event', shorten a couple fields
* style(discord): run latest .clang-format for discord-gateway.c and
  discord-client.c

* refactor(user-agent): remove redundant callbacks (use ua_info instead)

* chore(discord-gateway.c): rewrite compound literals and move variables declaration to top

* refactor(discord-client.c): discord_timestamp() shall use ws_timestamp() only if there's a live connection

* fix(discord-adapter.c): struct discord_request should hold a bucket field, missing http method, and route field

* wip(test-discord-async.c): test first prototype

* fix: _ws_curl_tls_check() should only trigger ws_close() if the user hasn't done himself

* wip(discord-adapter.c): async trigger user callback, use discord_timestamp()

* refactor(discord): share only whats necessary, shorten a couple fields

* refactor(discord-gateway.c): apply 9220b changes, add close reason, fix reconnect logic

* fix(discord-ratelimit.c): shouldn't skip discord_bucket_build() on unsuccesful requests

* chore(discord-voice-connections.c): apply 9220b changes

* feat(test-discord-ws.c): add reconnect test"

* feat(test-discord-async.c): test asynchronous vs synchronous

* feat(discord-adapter.c): queues cleanup logic

* feat(discord-ratelimit.c): support out-of-order ratelimiting (for multiplexing reasons)

* chore(test): remove test-discord-multiplex.c

* wip(discord): request timeout logic

* refactor(discord-ratelimit.c): split a couple functions

* fix(discord-gateway.c): send CLOSE_REASON_NO_REASON if a reconnect attempt is to follow

* refactor(discord-gateway.c): replace cee_timestamp_ms() with ws_timestamp() to reduce OS calls

* refactor(discord-voice-connections.c): match discord-gateway.c logic

* chore(bot-elitebgs.c): update to latest

* refactor(user-agent.c): add _ua_info_reset() and _ua_info_populate()

* feat(discord): functional ratelimiting for async

* chore(test-discord-async.c): update test

* chore(discord): rename discord-ratelimit.c to discord-adapter-ratelimit.c

* refactor(discord-adapter-request.c): split request handling logic from discord-adapter.c and discord-adapter-ratelimit.c

* refactor(discord-adapter-request.c): consistent naming and descriptive comments

* feat(user-agent): add ua_conn_reset()

* feat(discord-adapter-ratelimit.c): add discord_bucket_get_timeout() and
remove discord_bucket_timeout()
* feat(discord-adapter-request.c): timeout on non-global 429

* fix(discord-adapter-ratelimit.c): in case multiple requests with undefined buckets are enqueued at once, sort them out at once the first one gets a bucket match

* fix(discord): ratelimit per-route, not per-hash

* feat(discord): set priority of async request

* chore(test-discord-async.c): update

* fix(user-agent.c): move HTTP_SEND logging to ua_conn_setup() so that it activates for asynchronous requests

* feat(discord): stop on-going requests with discord_request_stop_all()

* fix(user-agent.c): typo us_conn_perform() -> ua_conn_perform()

* fix(discord-adapter-request.c): req_body recycling logic, memsize should be kept separate from length

* chore(test-discord-async.c): add ordered spamming test

* refactor(discord-adapter-ratelimit.c): alter a couple logging levels

* fix(discord-adapter-request.c): don't force decrease remaining buckets value

* fix(discord-adapter-ratelimit.c): skip when current timestamp is lesser than reset timestamp

* fix(discord-gateway.c): no need to run request checks if connection has been severed

* refactor(discord-internal.h): remove unused 'server' field

* feat: added discord_set_on_commands (#750)

* feat(examples): add simpler slash-commands example

* feat(specs/discord): missing fields for 'Application Command Options'

* chore(specs-code): update to match latest 807c590 changes

* feat(bot-slash-commands.c): update with channel listing example, rename a couple fields

* refactor(bot-slash-commands.c): simplify input read

* feat: added discord_set_on_commands

* docs: added docstring for discord_set_on_commands

Co-authored-by: lcsmuller <[email protected]>

* chore(discord-adapter-ratelimit.c): reduce scope

* refactor(discord-gateway.c): add _discord_gateway_close()

* fix: freeze bucket on any request timed out, unfreeze after it time out has passed

* chore(test-discord-async.c): asynchronous infinite spamming

* style(discord-channel.c): ANSI

* feat(specs/discord/gateway.json): add DISCORD_GATEWAY_CLOSE_REASON_RECONNECT enumerator

* chore(specs-code): update to match 6618c1a

* fix(discord-gateway.c): use opcode 4900 for reconnect

* fix(user-agent.c): curl_mime_free() should be called on conn's stop

* refactor(websockets): ws_start() initialize the multi handle, ws_end()
cleans it up

* chore: update to match 84f0a9a

* feat(discord-adapter-request.c): add discord_request_pause_all() for pausing and resuming async transfers

* wip: support ANSI syntax

* fix(discord-emoji.c): wrong parameter address

* chore: delete stale/redundant/unused files

* chore(test-slack-ws.c): move from 'tmp'

* fix(test-slack-ws.c): incompatible callback signature

* refactor: shorten user-agent.c symbols

* refactor(user-agent): consistency with function naming

* feat(discord-adapter-request.c): if set, async callback receive response body for parsing into object

* refactor: improve naming consistency and get rid of unintuitive NTL_T macros

* refactor(discord-adapter-request.c): change discord_async_cb signature and update to match 5af0a43

* fix(discord): idle queue must be heap-allocated to ensure its shared between original and cloned clients

* docs(discord): move 'Event Scheduling' functions into its own doxygen category

* refactor(discord): move discord_set_async() to discord-adapter.c and rename it to discord_adapter_set_async()
* wip(discord-internal.h): for ANSI compliancy anonymous structures should be replaced

* feat(discord-adapter-request.c): use recycleable buffer for callback return object

* refactor(discord): reorg

* feat(discord): add discord_create_message_async()

* chore(test-discord-async.c): update to match a066ea0

* fix(discord-adapter-ratelimit.c): obtaining length of major parameter

* refactor(user-agent): remove ua_reqheader_del(), make code ANSI C compliant

* style(common): ANSI

* wip: stackful ua_conn

* refactor(common): rename ORCA_NO_RESPONSE to ORCA_CURL_NO_RESPONSE

* fix(discord-adapter-ratelimit.c): UB for types with system-dependent sizes

* fix(discord-adapter-request.c): 3fcdae and reset 'conn' values after each usage, retry on ORCA_CURL_NO_RESPONSE

* fix(websockets.c): don't free multi handle at ws_end(), in case we might be reconnecting

* fix(discord-adapter-request.c): enqueue again on read error, build bucket on success

* chore(test-discord-async.c): update

* refactor(user-agent): rename ua_conn_get_results() to ua_info_extract(), remove struct ua_resp_handle parameter from ua_conn_setup()

* feat(discord): replace struct ua_resp_handle with the more flexible struct discord_request_attr

* chore(test-discord-async.c): match ae9ed

* fix(bot-fetch-messages.c): dereferencing null-pointer

* feat(user-agent): struct ua_info stores the request error code

* refactor(discord-internal): move 'struct discord_adapter' components that makes more hierarchical to 'struct discord_ratelimit'

* refactor(bot-shell.c): simplify and use async

* chore(.clang-format): add comment

* feat(discord): enable sending files over the multiplexer

* chore(bot-shell.c): redundant check

* docs(discord.h): improve discord_set_on_command() and discord_set_on_commands()

* refactor(bot-shell.c): move attachments outside of embed and use discord_set_on_commands()

* style(bot-shell.c): .clang-format

* feat(types.h): add CONTAINEROF() macro

* refactor(websockets.c): remove misleading error

* refactor(discord): rename 'discord_request' to 'discord_context'
* refactor(discord): rename 'discord_ratelimit' to 'discord_request'
* feat(discord-internal.h): add discord_bucket_init() and
  discord_buckets_cleanup()
* refactor(discord-adapter-request): concentrate otherwise scattered 'discord_request' logic over
* feat(discord-internal.h): add generic CLIENT() macro that wraps around
  CONTAINEROF()
* fix(discord-gateway.c): when a transfer is complete, its properly
  checked and acted on if its websockets on REST related, instead of
  just ignoring
* fix(discord-adapter-request): recycleable buffer is assigned to
  cxt->attr.obj when first enqueueing request
  at _discord_context_populate()
* refactor(discord-adapter-request): replace
  discord_request_check_results_async() with
  discord_request_check_action()
* chore(discord): remove all discord_sb_* references
* chore(discord-internal.h): reorganize structures hierarchichally

* refactor(types): add ORCA_EXPECT() macro, remove ORCA_MISSING_PARAMETER in favor of ORCA_BAD_PARAMETER

* refactor: make use of ORCA_EXPECT() to improve consistency and readability

* refactor(discord): replace exposed 'ja_u64**' type with 'u64_snowflake_t**'

* chore(bot-echo.c): simplify

* chore(bot-echo.c): should be discord_create_message_async()

* chore(discord-user.c): remove sb_discord_get_current_user()

* chore(test-discord-async.c): match to most recent

* fix(discord-channel.c): UB when iterating over a list without checking for NULL

* refactor(websockets): rename cws_reqheader_add() -> cws_add_header(), ws_reqheader_add() -> ws_add_header() * refactor(websockets): remove unused logic to keep code minimal

* refactor(discord): keep ws_timestamp_update() value stored to save on system calls

* fix(discord-gateway): 'now' timestamp must be shared between copies * feat(discord): use discord_timestamp() only on MT-Unsafe portions * refactor(discord-gateway): merge reconnect structure to status, rename it to retry

* refactor!(discord): remove bot parameter from callbacks, instead call discord_get_self() * refactor(discord): rename callbacks

* chore(discord): consolidate 2140f changes

* fix(user-agent.c): UB on _ua_conn_respheader_cb(), strings aren't null-terminated, shouldn't use string.h functions

* chore(discord-adapter-request.c): remove leftover asserts

* fix(user-agent.c): skipping extra char

* fix(discord): remove redundant const specifier on callback scalars

* chore(examples): update to match e14bca

* feat(websockets): enable CURLOPT_NOSIGNAL, more descriptive errors, log instead of crashing on error

* fix(websockets): macro typo

* docs(discord-adapter-request.c): add relevant TODO

* refactor!(user-agent): rename ua_run() to ua_easy_run() and add disclaimer

* chore: match e291b

* fix(discord-adapter.c): fallback to read-only blank attr in case of NULL

* refactor(github): make it consistent with discord codebase

* fix(bot-elitebgs.c): use ua_easy_run()

* fix(test-cee.c): use ua_easy_run()

* added functionality for guild_on_(create/update/delete) (#751)

* refactor(discord-adapter): rename discord_bucket_cooldown() to discord_bucket_get_wait() and return sleep time

* fix(discord-adapter-request.c): update 'now' timestamp before populating bucket in a blocking request

* chore(test-discord-ws.c): test ratelimiting on single-threaded blocking request

* refactor(discord): join 'discord_gateway' .status field to .session, use bitmask for gateway status

* refactor(discord): move bot structure (struct discord_gateway -> struct discord), move identify and event timestamps

* chore(common): unnecessary do while (0) wrapping over macros

* refactor!(websockets): add extra field for obtaining timestamp, rename ws_perform() to ws_easy_run() and add disclaimer at its documentation

* chore: match 74543

* fix(discord-gateway.c): wrong bitwise op

* refactor!(websockets): rid of ws_start() last arguments, ws_init() will
expects a user-owned curl_multi handle, rid of compound literals

* chore: match 22aaf

* wip(discord): merge struct discord_request to struct discord_adapter

* refactor(discord): finish transitioning 'discord_request' references to 'discord_adapter'

* chore(bot-voice): include discord-internal.h instead

* wip(discord-gateway.c): disable possibly wrong ratelimiting logic

* refactor(discord-gateway.c): simplify on_dispatch (#752)

Co-authored-by: Lucas Müller <[email protected]>

* fix(discord): discord_adapter_pause_all() is unnecessary after decoupling websockets and requests logic

* chore(discord-gateway.c): rollback from #752, my bad @Anotra

* chore(cee-utils): get latest

* fix(discord): incomplete type

* feat(user-agent): add 'struct ua_conn_attr' for setting connection attributes, to improve flexibility when adding new config

* refactor(user-agent.c): simplify _ua_conn_set_url()

* refactor: match codebase to 9310c

* chore(slack, github, reddit): move request functions to single file

* chore(specs): shorten naming

* chore(specs-code): match latest

* chore: reorg

* chore(specs): generate params for slack

* chore(specs-code): update to f02e74

* fix: undefined retry value

* feat: rename types.[c|h] to common.[c|h], add orca_global_init() and orca_global_cleanup()

* chore(discord): match to d697d

* refactor(js_user-agent): rename jsua_run() to jsua_easy_run()

* fix(discord-gateway.c): scheduler callback shouldn't be skipped

* refactor!(slack): make it consistent with the codebase

* chore: move initialization macro to single-file where it should be used

* chore(discord): merge request functions into a single file

* chore(discord): rename file

* refactor: move macros to discord-restapi.c

* refactor(discord-misc): move discord_disconnect_member() to discord-restapi.c

* chore: .

* feat(discord): make discord_async_next() public

* chore(discord): match to db547

* refactor(examples): replace deprecated discord_global_init() and discord_global_cleanup()

Co-authored-by: antropez <[email protected]>
Co-authored-by: Anotra <[email protected]>
  • Loading branch information
3 people authored Dec 18, 2021
1 parent 9d34a8c commit 96e3674
Show file tree
Hide file tree
Showing 177 changed files with 17,902 additions and 284,814 deletions.
10 changes: 6 additions & 4 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# ORCA-FORMATTING Starts here.
# ORCA-FORMATTING
# Requires clang-format 10 (at least)

Language: Cpp
# BasedOnStyle: Mozilla
AccessModifierOffset: -2
Expand All @@ -10,12 +12,12 @@ AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: false
# AlwaysBreakAfterDefinitionReturnType: TopLevel
AlwaysBreakAfterReturnType: TopLevelDefinitions
# AlwaysBreakAfterReturnType: None # enable for 'main' files
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
Expand All @@ -32,7 +34,7 @@ BraceWrapping:
BeforeCatch: false
BeforeElse: true
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ GITHUB_SRC := $(wildcard github-*.c $(SPECS_WDIR)/github/*.c)
GITHUB_OBJS := $(GITHUB_SRC:%.c=$(OBJDIR)/%.o)
REDDIT_SRC := $(wildcard reddit-*.c $(SPECS_WDIR)/reddit/*.c)
REDDIT_OBJS := $(REDDIT_SRC:%.c=$(OBJDIR)/%.o)
SLACK_SRC := $(wildcard slack-*.c)
SLACK_SRC := $(wildcard slack-*.c $(SPECS_WDIR)/slack/*.c)
SLACK_OBJS := $(SLACK_SRC:%.c=$(OBJDIR)/%.o)

# API libs cflags
Expand Down
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,18 @@ Orca's implementation has minimum external dependencies to make bot deployment d
#include <string.h> // strcmp()
#include <orca/discord.h>

void on_ready(struct discord *client, const struct discord_user *bot)
void on_ready(struct discord *client)
{
const struct discord_user *bot = discord_get_self(client);
log_info("Logged in as %s!", bot->username);
}

void on_message(struct discord *client,
const struct discord_user *bot,
const struct discord_message *msg)
void on_message(struct discord *client, const struct discord_message *msg)
{
// if message content is equal to 'ping', then the bot will respond with 'pong'.
if (0 == strcmp(msg->content, "ping")) {
if (0 == strcmp(msg->content, "ping")) { // if 'ping' received, reply with 'pong'
struct discord_create_message_params params = { .content = "pong" };

discord_async_next(client, NULL); // make next request non-blocking (OPTIONAL)
discord_create_message(client, msg->channel_id, &params, NULL);
}
}
Expand Down Expand Up @@ -164,7 +164,6 @@ $ sudo make install
Included headers must be `orca/` prefixed:
```c
#include <orca/discord.h>
#include <orca/github.h>
```

### Standalone executable
Expand Down
105 changes: 63 additions & 42 deletions cee-utils/cee-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
#include "json-actor.h"
#include "clock.h"


char*
char *
cee_load_whole_file_fp(FILE *fp, size_t *len)
{
fseek(fp, 0, SEEK_END);
Expand All @@ -35,21 +34,20 @@ cee_load_whole_file_fp(FILE *fp, size_t *len)
return str;
}

char*
char *
cee_load_whole_file(const char filename[], size_t *len)
{
FILE *fp = fopen(filename,"rb");
FILE *fp = fopen(filename, "rb");
VASSERT_S(NULL != fp, "%s '%s'\n", strerror(errno), filename);
char *str = cee_load_whole_file_fp(fp, len);
fclose(fp);
return str;
}

int
cee_dati_from_fjson(
char filename[],
void *p_data,
void (from_json_cb)(char *str, size_t len, void *p_data))
cee_dati_from_fjson(char filename[],
void *p_data,
void(from_json_cb)(char *str, size_t len, void *p_data))
{
size_t len;
char *json = cee_load_whole_file(filename, &len);
Expand All @@ -62,13 +60,26 @@ cee_dati_from_fjson(
return 1;
}

void
cee_sized_buffer_from_json(char *str, size_t len, struct sized_buffer *buf)
{
char *dest = NULL;

cee_strndup(str, len, &dest);
buf->start = dest;
buf->size = len;
}

static char *
stat_to_type(const struct stat *st)
{
switch (st->st_mode & S_IFMT) {
case S_IFREG: return "file";
case S_IFDIR: return "folder";
default: return NULL;
case S_IFREG:
return "file";
case S_IFDIR:
return "folder";
default:
return NULL;
}
}

Expand All @@ -84,25 +95,25 @@ cee_iso8601_to_unix_ms(char *str, size_t len, uint64_t *p_value)
char tz_operator = 'Z';
int tz_hour = 0, tz_min = 0;
sscanf(str, "%d-%d-%dT%d:%d:%lf%c%d:%d", /* ISO-8601 complete format */
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, /* Date */
&tm.tm_hour, &tm.tm_min, &seconds, /* Time */
&tz_operator, &tz_hour, &tz_min); /* Timezone */
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, /* Date */
&tm.tm_hour, &tm.tm_min, &seconds, /* Time */
&tz_operator, &tz_hour, &tz_min); /* Timezone */

tm.tm_mon--; /* struct tm takes month from 0 to 11 */
tm.tm_year -= 1900; /* struct tm takes years from 1900 */

uint64_t res = (((uint64_t) mktime(&tm) - timezone) * 1000)
+ (uint64_t) round(seconds * 1000.0);
uint64_t res = (((uint64_t)mktime(&tm) - timezone) * 1000)
+ (uint64_t)round(seconds * 1000.0);
switch (tz_operator) {
case '+': /* Add hours and minutes */
res += (tz_hour * 60 + tz_min) * 60 * 1000;
break;
res += (tz_hour * 60 + tz_min) * 60 * 1000;
break;
case '-': /* Subtract hours and minutes */
res -= (tz_hour * 60 + tz_min) * 60 * 1000;
break;
res -= (tz_hour * 60 + tz_min) * 60 * 1000;
break;
case 'Z': /* UTC, don't do anything */
default: /* @todo should we check for error ? */
break;
break;
}

*p_value = res;
Expand All @@ -120,32 +131,35 @@ cee_unix_ms_to_iso8601(char *str, size_t len, uint64_t *p_value)
struct tm buf;
struct tm *tm = localtime_r(&seconds, &buf);

return snprintf(str, len,
"%d-%.2d-%dT%.2d:%.2d:%.2d.%.3dZ", /* ISO-8601 complete format */
return snprintf(
str, len, "%d-%.2d-%dT%.2d:%.2d:%.2d.%.3dZ", /* ISO-8601 complete format */
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, /* Date */
tm->tm_hour, tm->tm_min, tm->tm_sec, millis); /* Time */
}

int
cee_strtou64(char *str, size_t len, uint64_t *p_value)
cee_strtou64(char *str, size_t len, uint64_t *p_value)
{
char fmt[512];
int ret = snprintf(fmt, sizeof(fmt), "%%%zu"SCNu64, len);
int ret = snprintf(fmt, sizeof(fmt), "%%%zu" SCNu64, len);
if (ret >= sizeof(fmt)) return 0;
return sscanf(str, fmt, p_value) != EOF;
}

int
cee_u64tostr(char *str, size_t len, uint64_t *p_value) {
return snprintf(str, len, "%" PRIu64 , *p_value);
cee_u64tostr(char *str, size_t len, uint64_t *p_value)
{
return snprintf(str, len, "%" PRIu64, *p_value);
}

int
cee_strndup(char *src, size_t len, char **p_dest)
cee_strndup(char *src, size_t len, char **p_dest)
{
*p_dest = malloc(len + 1);

memcpy(*p_dest, src, len);
(*p_dest)[len] = '\0';

return 1;
}

Expand Down Expand Up @@ -173,12 +187,12 @@ cee_timestamp_ms(void)
{
struct PsnipClockTimespec t;
if (0 == psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &t)) {
return (uint64_t)t.seconds*1000 + (uint64_t)t.nanoseconds/1000000;
return (uint64_t)t.seconds * 1000 + (uint64_t)t.nanoseconds / 1000000;
}
return 0ULL;
}

char*
char *
cee_timestamp_str(char *p_str, int len)
{
time_t t = time(NULL);
Expand All @@ -199,31 +213,36 @@ cee_str_bounds_check(const char *str, const size_t threshold_len)
size_t i;
if (!str) return -1; /* Missing string */

for (i=0; i < threshold_len; ++i) {
for (i = 0; i < threshold_len; ++i) {
if ('\0' == str[i]) return i; /* bound check succeeded */
}
return 0; /* bound check failed */
}

char*
cee_join_strings(char** strings, const size_t nmemb, const char delim[], const size_t wordlen, const size_t maxlen)
char *
cee_join_strings(char **strings,
const size_t nmemb,
const char delim[],
const size_t wordlen,
const size_t maxlen)
{
size_t i;
char *buf = malloc(maxlen);
char *cur = buf, * const end = cur + maxlen;
char *cur = buf, *const end = cur + maxlen;

for (i=0; i < nmemb; ++i) {
VASSERT_S(cee_str_bounds_check(strings[i], wordlen) > 0, \
"'%s' exceeds threshold of %zu characters", strings[i], wordlen);
cur += snprintf(cur, end-cur, "%s%s", strings[i], delim);
for (i = 0; i < nmemb; ++i) {
VASSERT_S(cee_str_bounds_check(strings[i], wordlen) > 0,
"'%s' exceeds threshold of %zu characters", strings[i], wordlen);
cur += snprintf(cur, end - cur, "%s%s", strings[i], delim);
ASSERT_S(cur < end, "Out of bounds write attempt");
}
*(cur - strlen(delim)) = '\0';

return buf;
}

void cee_gen_readlink(char *linkbuf, size_t linkbuf_size)
void
cee_gen_readlink(char *linkbuf, size_t linkbuf_size)
{
ssize_t r;
r = readlink("/proc/self/exe", linkbuf, linkbuf_size);
Expand All @@ -236,11 +255,13 @@ void cee_gen_readlink(char *linkbuf, size_t linkbuf_size)
fprintf(stderr, "symlink size is greater than %zu\n", linkbuf_size);
exit(EXIT_FAILURE);
}
linkbuf[r]='\0';
linkbuf[r] = '\0';

return;
}

void cee_gen_dirname(char *linkbuf) {
*strrchr(linkbuf, '/')='\0';
void
cee_gen_dirname(char *linkbuf)
{
*strrchr(linkbuf, '/') = '\0';
}
Loading

0 comments on commit 96e3674

Please sign in to comment.