Merge branch 'main' into feature/new-object-system
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
#include <string.h>
|
||||
|
||||
static void array_release(struct b_dsref *obj);
|
||||
static void array_to_string(struct b_dsref *obj, struct b_stream *out);
|
||||
static void array_to_string(const struct b_dsref *obj, struct b_stream *out);
|
||||
|
||||
static struct b_dsref_type array_type = {
|
||||
.t_flags = B_DSREF_FUNDAMENTAL,
|
||||
@@ -221,7 +221,7 @@ size_t b_array_capacity(const struct b_array *array)
|
||||
return array->ar_cap;
|
||||
}
|
||||
|
||||
static void array_to_string(struct b_dsref *obj, struct b_stream *out)
|
||||
static void array_to_string(const struct b_dsref *obj, struct b_stream *out)
|
||||
{
|
||||
struct b_array *array = B_ARRAY(obj);
|
||||
|
||||
|
||||
488
ds/datetime.c
Normal file
488
ds/datetime.c
Normal file
@@ -0,0 +1,488 @@
|
||||
#include "datetime.h"
|
||||
|
||||
#include <blue/core/stream.h>
|
||||
#include <blue/ds/datetime.h>
|
||||
#include <blue/ds/string.h>
|
||||
|
||||
static void datetime_to_string(const struct b_dsref *obj, struct b_stream *out);
|
||||
|
||||
static struct b_dsref_type datetime_type = {
|
||||
.t_name = "corelib::datetime",
|
||||
.t_flags = B_DSREF_FUNDAMENTAL,
|
||||
.t_id = B_DSREF_TYPE_DATETIME,
|
||||
.t_instance_size = sizeof(struct b_datetime),
|
||||
.t_to_string = datetime_to_string,
|
||||
};
|
||||
|
||||
struct b_datetime *b_datetime_create(void)
|
||||
{
|
||||
return (struct b_datetime *)b_dsref_type_instantiate(&datetime_type);
|
||||
}
|
||||
|
||||
static bool is_leap_year(const struct b_datetime *dt)
|
||||
{
|
||||
if ((dt->dt_year % 400) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((dt->dt_year % 4) == 0 && (dt->dt_year % 100) != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_year_valid(const struct b_datetime *dt)
|
||||
{
|
||||
return dt->dt_year >= 0;
|
||||
}
|
||||
|
||||
static bool is_month_valid(const struct b_datetime *dt)
|
||||
{
|
||||
return dt->dt_month >= 1 && dt->dt_month <= 12;
|
||||
}
|
||||
|
||||
static bool is_day_valid(const struct b_datetime *dt)
|
||||
{
|
||||
if (dt->dt_day < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (dt->dt_month) {
|
||||
case 2:
|
||||
return dt->dt_day <= (is_leap_year(dt) ? 29 : 28);
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11:
|
||||
return dt->dt_day <= 30;
|
||||
case 1:
|
||||
case 3:
|
||||
case 5:
|
||||
case 7:
|
||||
case 8:
|
||||
case 10:
|
||||
case 12:
|
||||
return dt->dt_day <= 31;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_time_valid(const struct b_datetime *dt)
|
||||
{
|
||||
if (!(dt->dt_hour >= 0 && dt->dt_hour <= 23)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(dt->dt_min >= 0 && dt->dt_min <= 59)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(dt->dt_sec >= 0 && dt->dt_sec <= 60)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_zone_valid(const struct b_datetime *dt)
|
||||
{
|
||||
if (!(dt->dt_zone_offset_hour >= 0 && dt->dt_zone_offset_hour <= 23)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(dt->dt_zone_offset_minute >= 0 && dt->dt_zone_offset_minute <= 59)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool validate(const struct b_datetime *dt)
|
||||
{
|
||||
if (dt->dt_has_date) {
|
||||
if (!is_year_valid(dt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_month_valid(dt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_day_valid(dt)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dt->dt_has_time) {
|
||||
if (!is_time_valid(dt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_zone_valid(dt)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct b_datetime *parse_rfc3339(const char *s)
|
||||
{
|
||||
struct b_datetime *dt = b_datetime_create();
|
||||
if (!dt) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t len = strlen(s);
|
||||
|
||||
size_t i = 0, c = 0;
|
||||
|
||||
bool has_date = false, has_time = false;
|
||||
dt->dt_localtime = true;
|
||||
|
||||
if (len >= 10 && s[4] == '-' && s[7] == '-') {
|
||||
has_date = true;
|
||||
}
|
||||
|
||||
if (len >= 8 && s[2] == ':' && s[5] == ':') {
|
||||
has_time = true;
|
||||
}
|
||||
|
||||
if (len >= 19 && s[4] == '-' && s[7] == '-'
|
||||
&& (s[10] == 'T' || s[10] == 't' || s[10] == ' ') && s[13] == ':'
|
||||
&& s[16] == ':') {
|
||||
has_date = true;
|
||||
has_time = true;
|
||||
}
|
||||
|
||||
if (!has_date && !has_time) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (has_date) {
|
||||
for (c = 0; c < 4; c++, i++) {
|
||||
if (!isdigit(s[i])) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dt->dt_year *= 10;
|
||||
dt->dt_year += (s[i] - '0');
|
||||
}
|
||||
|
||||
if (s[i++] != '-') {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (c = 0; c < 2; c++, i++) {
|
||||
if (!isdigit(s[i])) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dt->dt_month *= 10;
|
||||
dt->dt_month += (s[i] - '0');
|
||||
}
|
||||
|
||||
if (s[i++] != '-' || dt->dt_month > 12) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (c = 0; c < 2; c++, i++) {
|
||||
if (!isdigit(s[i])) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dt->dt_day *= 10;
|
||||
dt->dt_day += (s[i] - '0');
|
||||
}
|
||||
|
||||
if (dt->dt_day > 31) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if ((s[i] == 'T' || s[i] == 't' || s[i] == ' ') && !has_time) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (has_date && has_time) {
|
||||
if (s[i] != 'T' && s[i] != 't' && s[i] != ' ') {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (has_time) {
|
||||
for (c = 0; c < 2; c++, i++) {
|
||||
if (!isdigit(s[i])) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dt->dt_hour *= 10;
|
||||
dt->dt_hour += (s[i] - '0');
|
||||
}
|
||||
|
||||
if (s[i++] != ':') {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (c = 0; c < 2; c++, i++) {
|
||||
if (!isdigit(s[i])) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dt->dt_min *= 10;
|
||||
dt->dt_min += (s[i] - '0');
|
||||
}
|
||||
|
||||
if (s[i++] != ':') {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (c = 0; c < 2; c++, i++) {
|
||||
if (!isdigit(s[i])) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dt->dt_sec *= 10;
|
||||
dt->dt_sec += (s[i] - '0');
|
||||
}
|
||||
|
||||
if (s[i] == '.') {
|
||||
i++;
|
||||
for (c = 0; s[i]; c++, i++) {
|
||||
if (!isdigit(s[i])) {
|
||||
break;
|
||||
}
|
||||
|
||||
dt->dt_msec *= 10;
|
||||
dt->dt_msec += (s[i] - '0');
|
||||
}
|
||||
|
||||
if (c == 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (s[i] == '+' || s[i] == '-') {
|
||||
dt->dt_localtime = false;
|
||||
dt->dt_zone_offset_negative = s[i] == '-';
|
||||
i++;
|
||||
|
||||
for (c = 0; c < 2; c++, i++) {
|
||||
if (!isdigit(s[i])) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dt->dt_zone_offset_hour *= 10;
|
||||
dt->dt_zone_offset_hour += (s[i] - '0');
|
||||
}
|
||||
|
||||
if (s[i++] != ':') {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (c = 0; c < 2; c++, i++) {
|
||||
if (!isdigit(s[i])) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dt->dt_zone_offset_minute *= 10;
|
||||
dt->dt_zone_offset_minute += (s[i] - '0');
|
||||
}
|
||||
} else if (s[i] == 'Z' || s[i] == 'z') {
|
||||
dt->dt_localtime = false;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (s[i] != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dt->dt_has_date = has_date;
|
||||
dt->dt_has_time = has_time;
|
||||
return dt;
|
||||
fail:
|
||||
b_datetime_release(dt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_datetime *b_datetime_parse(enum b_datetime_format format, const char *s)
|
||||
{
|
||||
struct b_datetime *dt = NULL;
|
||||
|
||||
switch (format) {
|
||||
case B_DATETIME_FORMAT_RFC3339:
|
||||
dt = parse_rfc3339(s);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!dt) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!validate(dt)) {
|
||||
b_datetime_release(dt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
enum b_status encode_rfc3339(const struct b_datetime *dt, struct b_stream *out)
|
||||
{
|
||||
if (dt->dt_has_date) {
|
||||
b_stream_write_fmt(
|
||||
out, NULL, "%04ld-%02ld-%02ld", dt->dt_year,
|
||||
dt->dt_month, dt->dt_day);
|
||||
}
|
||||
|
||||
if (dt->dt_has_date && dt->dt_has_time) {
|
||||
b_stream_write_char(out, 'T');
|
||||
}
|
||||
|
||||
if (dt->dt_has_time) {
|
||||
b_stream_write_fmt(
|
||||
out, NULL, "%02ld:%02ld:%02ld", dt->dt_hour, dt->dt_min,
|
||||
dt->dt_sec);
|
||||
|
||||
if (dt->dt_msec > 0) {
|
||||
b_stream_write_fmt(out, NULL, ".%04ld", dt->dt_msec);
|
||||
}
|
||||
|
||||
if (!dt->dt_localtime) {
|
||||
if (dt->dt_zone_offset_hour == 0
|
||||
&& dt->dt_zone_offset_minute == 0) {
|
||||
b_stream_write_char(out, 'Z');
|
||||
} else {
|
||||
b_stream_write_fmt(
|
||||
out, NULL, "%c%02ld:%02ld",
|
||||
dt->dt_zone_offset_negative ? '-' : '+',
|
||||
dt->dt_zone_offset_hour,
|
||||
dt->dt_zone_offset_minute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
void b_datetime_to_string(
|
||||
const b_datetime *dt, b_datetime_format format, struct b_string *dest)
|
||||
{
|
||||
struct b_stream *out;
|
||||
b_string_open_stream(dest, &out);
|
||||
|
||||
switch (format) {
|
||||
case B_DATETIME_FORMAT_RFC3339:
|
||||
encode_rfc3339(dt, out);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
b_stream_close(out);
|
||||
}
|
||||
|
||||
bool b_datetime_is_localtime(const b_datetime *dt)
|
||||
{
|
||||
return dt->dt_localtime;
|
||||
}
|
||||
|
||||
bool b_datetime_has_date(const b_datetime *dt)
|
||||
{
|
||||
return dt->dt_has_date;
|
||||
}
|
||||
|
||||
bool b_datetime_has_time(const b_datetime *dt)
|
||||
{
|
||||
return dt->dt_has_time;
|
||||
}
|
||||
|
||||
long b_datetime_year(const b_datetime *dt)
|
||||
{
|
||||
return dt->dt_year;
|
||||
}
|
||||
|
||||
long b_datetime_month(const b_datetime *dt)
|
||||
{
|
||||
return dt->dt_month;
|
||||
}
|
||||
|
||||
long b_datetime_day(const b_datetime *dt)
|
||||
{
|
||||
return dt->dt_day;
|
||||
}
|
||||
|
||||
long b_datetime_hour(const b_datetime *dt)
|
||||
{
|
||||
return dt->dt_hour;
|
||||
}
|
||||
|
||||
long b_datetime_minute(const b_datetime *dt)
|
||||
{
|
||||
return dt->dt_min;
|
||||
}
|
||||
|
||||
long b_datetime_second(const b_datetime *dt)
|
||||
{
|
||||
return dt->dt_sec;
|
||||
}
|
||||
|
||||
long b_datetime_subsecond(const b_datetime *dt)
|
||||
{
|
||||
return dt->dt_msec;
|
||||
}
|
||||
|
||||
bool b_datetime_zone_offset_is_negative(const b_datetime *dt)
|
||||
{
|
||||
return dt->dt_zone_offset_negative;
|
||||
}
|
||||
|
||||
long b_datetime_zone_offset_hour(const b_datetime *dt)
|
||||
{
|
||||
return dt->dt_zone_offset_hour;
|
||||
}
|
||||
|
||||
long b_datetime_zone_offset_minute(const b_datetime *dt)
|
||||
{
|
||||
return dt->dt_zone_offset_minute;
|
||||
}
|
||||
|
||||
static void datetime_to_string(const struct b_dsref *obj, struct b_stream *out)
|
||||
{
|
||||
struct b_datetime *dt = B_DATETIME(obj);
|
||||
|
||||
if (dt->dt_has_date) {
|
||||
b_stream_write_fmt(
|
||||
out, NULL, "%04ld-%02ld-%02ld", dt->dt_year,
|
||||
dt->dt_month, dt->dt_day);
|
||||
}
|
||||
|
||||
if (dt->dt_has_date && dt->dt_has_time) {
|
||||
b_stream_write_char(out, ' ');
|
||||
}
|
||||
|
||||
if (dt->dt_has_time) {
|
||||
b_stream_write_fmt(
|
||||
out, NULL, "%02ld:%02ld:%02ld", dt->dt_hour, dt->dt_min,
|
||||
dt->dt_sec);
|
||||
|
||||
if (dt->dt_msec > 0) {
|
||||
b_stream_write_fmt(out, NULL, ".%04ld", dt->dt_msec);
|
||||
}
|
||||
|
||||
if (!dt->dt_localtime) {
|
||||
b_stream_write_fmt(
|
||||
out, NULL, " %c%02ld:%02ld",
|
||||
dt->dt_zone_offset_negative ? '-' : '+',
|
||||
dt->dt_zone_offset_hour,
|
||||
dt->dt_zone_offset_minute);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
ds/datetime.h
Normal file
17
ds/datetime.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _BLUELIB_DATETIME_H_
|
||||
#define _BLUELIB_DATETIME_H_
|
||||
|
||||
#include "object.h"
|
||||
|
||||
struct b_datetime {
|
||||
struct b_dsref dt_base;
|
||||
unsigned int dt_year, dt_month, dt_day;
|
||||
unsigned short dt_hour, dt_min, dt_sec;
|
||||
unsigned int dt_msec;
|
||||
|
||||
bool dt_has_date, dt_has_time, dt_localtime;
|
||||
unsigned short dt_zone_offset_hour, dt_zone_offset_minute;
|
||||
bool dt_zone_offset_negative;
|
||||
};
|
||||
|
||||
#endif
|
||||
86
ds/dict.c
86
ds/dict.c
@@ -24,7 +24,7 @@ uint64_t b_cstr_hash(const char *s)
|
||||
}
|
||||
|
||||
static void dict_release(struct b_dsref *obj);
|
||||
static void dict_to_string(struct b_dsref *obj, struct b_stream *out);
|
||||
static void dict_to_string(const struct b_dsref *obj, struct b_stream *out);
|
||||
|
||||
static struct b_dsref_type dict_type = {
|
||||
.t_name = "corelib::dict",
|
||||
@@ -105,7 +105,35 @@ b_status b_dict_put(struct b_dict *dict, const char *key, b_dsref *value)
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
item->bi_str = b_strdup(key);
|
||||
item->bi_str = b_string_create_from_cstr(key);
|
||||
item->bi_value = b_retain(value);
|
||||
|
||||
b_queue_push_back(&bucket->bk_items, &item->bi_entry);
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_dict_put_sk(
|
||||
struct b_dict *dict, const struct b_string *key, b_dsref *value)
|
||||
{
|
||||
uint64_t hash = b_string_hash(key);
|
||||
struct b_dict_bucket *bucket = get_bucket(&dict->d_buckets, hash);
|
||||
if (!bucket) {
|
||||
bucket = create_bucket();
|
||||
if (!bucket) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
bucket->bk_hash = hash;
|
||||
put_bucket(&dict->d_buckets, bucket);
|
||||
}
|
||||
|
||||
struct b_dict_bucket_item *item = create_bucket_item();
|
||||
if (!item) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
item->bi_str = b_string_duplicate(key);
|
||||
item->bi_value = b_retain(value);
|
||||
|
||||
b_queue_push_back(&bucket->bk_items, &item->bi_entry);
|
||||
@@ -126,7 +154,28 @@ b_dsref *b_dict_at(const struct b_dict *dict, const char *key)
|
||||
struct b_dict_bucket_item *item
|
||||
= b_unbox(struct b_dict_bucket_item, it.entry, bi_entry);
|
||||
|
||||
if (!strcmp(item->bi_str, key)) {
|
||||
if (!strcmp(b_string_ptr(item->bi_str), key)) {
|
||||
return item->bi_value;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b_dsref *b_dict_at_sk(const struct b_dict *dict, const struct b_string *key)
|
||||
{
|
||||
uint64_t hash = b_string_hash(key);
|
||||
struct b_dict_bucket *bucket = get_bucket(&dict->d_buckets, hash);
|
||||
if (!bucket) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b_queue_iterator it;
|
||||
b_queue_foreach (&it, &bucket->bk_items) {
|
||||
struct b_dict_bucket_item *item
|
||||
= b_unbox(struct b_dict_bucket_item, it.entry, bi_entry);
|
||||
|
||||
if (b_string_compare(item->bi_str, key)) {
|
||||
return item->bi_value;
|
||||
}
|
||||
}
|
||||
@@ -144,11 +193,26 @@ b_dsref *b_dict_get(struct b_dict *dict, const char *key)
|
||||
return value;
|
||||
}
|
||||
|
||||
b_dsref *b_dict_get_sk(struct b_dict *dict, const struct b_string *key)
|
||||
{
|
||||
b_dsref *value = b_dict_at_sk(dict, key);
|
||||
if (value) {
|
||||
b_retain(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool b_dict_has_key(const struct b_dict *dict, const char *key)
|
||||
{
|
||||
return b_dict_at(dict, key) != NULL;
|
||||
}
|
||||
|
||||
bool b_dict_has_skey(const struct b_dict *dict, const struct b_string *key)
|
||||
{
|
||||
return b_dict_at_sk(dict, key) != NULL;
|
||||
}
|
||||
|
||||
size_t b_dict_get_size(const struct b_dict *dict)
|
||||
{
|
||||
size_t count = 0;
|
||||
@@ -186,7 +250,7 @@ bool b_dict_is_empty(const b_dict *dict)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void dict_to_string(struct b_dsref *obj, struct b_stream *out)
|
||||
static void dict_to_string(const struct b_dsref *obj, struct b_stream *out)
|
||||
{
|
||||
struct b_dict *dict = B_DICT(obj);
|
||||
|
||||
@@ -203,7 +267,8 @@ static void dict_to_string(struct b_dsref *obj, struct b_stream *out)
|
||||
b_dict_iterator it;
|
||||
b_dict_foreach(&it, dict)
|
||||
{
|
||||
b_stream_write_fmt(out, NULL, "%s: ", it.key);
|
||||
b_to_string(B_DSREF(it.key), out);
|
||||
b_stream_write_string(out, ": ", NULL);
|
||||
|
||||
bool is_string = b_typeid(it.value) == B_DSREF_TYPE_STRING;
|
||||
|
||||
@@ -243,11 +308,12 @@ static bool dict_iterator_is_valid(const struct b_iterator *it)
|
||||
return b_dict_iterator_is_valid((struct b_dict_iterator *)it);
|
||||
}
|
||||
|
||||
static struct b_iterator_ops it_ops
|
||||
= {.it_next = dict_iterator_next,
|
||||
.it_close = NULL,
|
||||
.it_erase = dict_iterator_erase,
|
||||
.it_is_valid = dict_iterator_is_valid};
|
||||
static struct b_iterator_ops it_ops = {
|
||||
.it_next = dict_iterator_next,
|
||||
.it_close = NULL,
|
||||
.it_erase = dict_iterator_erase,
|
||||
.it_is_valid = dict_iterator_is_valid,
|
||||
};
|
||||
|
||||
int b_dict_iterator_begin(struct b_dict *dict, b_dict_iterator *it)
|
||||
{
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
#include <blue/core/btree.h>
|
||||
#include <blue/core/queue.h>
|
||||
|
||||
struct b_string;
|
||||
|
||||
struct b_dict_bucket_item {
|
||||
b_queue_entry bi_entry;
|
||||
char *bi_str;
|
||||
struct b_string *bi_str;
|
||||
struct b_dsref *bi_value;
|
||||
};
|
||||
|
||||
|
||||
67
ds/hashmap.c
67
ds/hashmap.c
@@ -40,6 +40,45 @@ static uint64_t hash_data(const void *p, size_t size)
|
||||
|
||||
static void hashmap_release(struct b_dsref *obj);
|
||||
|
||||
static uint64_t hash_key(const struct b_hashmap_key *key)
|
||||
{
|
||||
if (key->key_flags & B_HASHMAP_KEY_F_INTVALUE) {
|
||||
return hash_data(&key->key_data, sizeof key->key_data);
|
||||
} else {
|
||||
return hash_data(key->key_data, key->key_size);
|
||||
}
|
||||
}
|
||||
|
||||
static bool compare_key(
|
||||
const struct b_hashmap_key *a, const struct b_hashmap_key *b)
|
||||
{
|
||||
const void *a_data = NULL, *b_data = NULL;
|
||||
size_t a_len = 0, b_len = 0;
|
||||
|
||||
if (a->key_flags & B_HASHMAP_KEY_F_INTVALUE) {
|
||||
a_data = &a->key_data;
|
||||
a_len = sizeof a->key_data;
|
||||
} else {
|
||||
a_data = a->key_data;
|
||||
a_len = a->key_size;
|
||||
}
|
||||
|
||||
if (b->key_flags & B_HASHMAP_KEY_F_INTVALUE) {
|
||||
b_data = &b->key_data;
|
||||
b_len = sizeof b->key_data;
|
||||
} else {
|
||||
b_data = b->key_data;
|
||||
b_len = b->key_size;
|
||||
}
|
||||
|
||||
if (a_len != b_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t cmp_len = a_len;
|
||||
return memcmp(a_data, b_data, cmp_len) == 0;
|
||||
}
|
||||
|
||||
static struct b_dsref_type hashmap_type = {
|
||||
.t_name = "corelib::hashmap",
|
||||
.t_flags = B_DSREF_FUNDAMENTAL,
|
||||
@@ -101,7 +140,7 @@ b_status b_hashmap_put(
|
||||
struct b_hashmap *hashmap, const b_hashmap_key *key,
|
||||
const b_hashmap_value *value)
|
||||
{
|
||||
uint64_t hash = hash_data(key->key_data, key->key_size);
|
||||
uint64_t hash = hash_key(key);
|
||||
struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash);
|
||||
|
||||
if (!bucket) {
|
||||
@@ -119,12 +158,9 @@ b_status b_hashmap_put(
|
||||
struct b_hashmap_bucket_item *item = b_unbox(
|
||||
struct b_hashmap_bucket_item, it.entry, bi_entry);
|
||||
|
||||
if (item->bi_key.key_size != key->key_size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!memcmp(item->bi_key.key_data, key->key_data, key->key_size)) {
|
||||
return B_ERR_NAME_EXISTS;
|
||||
if (compare_key(&item->bi_key, key)) {
|
||||
memcpy(&item->bi_value, value, sizeof *value);
|
||||
return B_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +180,8 @@ b_status b_hashmap_put(
|
||||
const struct b_hashmap_value *b_hashmap_get(
|
||||
const struct b_hashmap *hashmap, const struct b_hashmap_key *key)
|
||||
{
|
||||
uint64_t hash = hash_data(key->key_data, key->key_size);
|
||||
uint64_t hash = hash_key(key);
|
||||
|
||||
struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash);
|
||||
if (!bucket) {
|
||||
return NULL;
|
||||
@@ -155,11 +192,7 @@ const struct b_hashmap_value *b_hashmap_get(
|
||||
struct b_hashmap_bucket_item *item = b_unbox(
|
||||
struct b_hashmap_bucket_item, it.entry, bi_entry);
|
||||
|
||||
if (item->bi_key.key_size != key->key_size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!memcmp(item->bi_key.key_data, key->key_data, key->key_size)) {
|
||||
if (compare_key(&item->bi_key, key)) {
|
||||
return &item->bi_value;
|
||||
}
|
||||
}
|
||||
@@ -169,7 +202,7 @@ const struct b_hashmap_value *b_hashmap_get(
|
||||
|
||||
bool b_hashmap_has_key(const struct b_hashmap *hashmap, const b_hashmap_key *key)
|
||||
{
|
||||
uint64_t hash = hash_data(key->key_data, key->key_size);
|
||||
uint64_t hash = hash_key(key);
|
||||
struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash);
|
||||
if (!bucket) {
|
||||
return false;
|
||||
@@ -180,11 +213,7 @@ bool b_hashmap_has_key(const struct b_hashmap *hashmap, const b_hashmap_key *key
|
||||
struct b_hashmap_bucket_item *item = b_unbox(
|
||||
struct b_hashmap_bucket_item, it.entry, bi_entry);
|
||||
|
||||
if (item->bi_key.key_size != key->key_size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!memcmp(item->bi_key.key_data, key->key_data, key->key_size)) {
|
||||
if (compare_key(&item->bi_key, key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
49
ds/include/blue/ds/datetime.h
Normal file
49
ds/include/blue/ds/datetime.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef BLUELIB_DATETIME_H_
|
||||
#define BLUELIB_DATETIME_H_
|
||||
|
||||
#include <blue/core/status.h>
|
||||
#include <blue/ds/object.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <ctype.h>
|
||||
|
||||
struct b_string;
|
||||
|
||||
#define B_DATETIME(p) ((b_datetime *)(p))
|
||||
|
||||
typedef struct b_datetime b_datetime;
|
||||
|
||||
typedef enum b_datetime_format {
|
||||
B_DATETIME_FORMAT_RFC3339 = 1,
|
||||
} b_datetime_format;
|
||||
|
||||
BLUE_API b_datetime *b_datetime_create(void);
|
||||
BLUE_API b_datetime *b_datetime_parse(b_datetime_format format, const char *s);
|
||||
BLUE_API void b_datetime_to_string(
|
||||
const b_datetime *dt, b_datetime_format format, struct b_string *dest);
|
||||
|
||||
static inline b_datetime *b_datetime_retain(b_datetime *dt)
|
||||
{
|
||||
return B_DATETIME(b_retain(B_DSREF(dt)));
|
||||
}
|
||||
static inline void b_datetime_release(b_datetime *dt)
|
||||
{
|
||||
b_release(B_DSREF(dt));
|
||||
}
|
||||
|
||||
BLUE_API bool b_datetime_is_localtime(const b_datetime *dt);
|
||||
BLUE_API bool b_datetime_has_date(const b_datetime *dt);
|
||||
BLUE_API bool b_datetime_has_time(const b_datetime *dt);
|
||||
|
||||
BLUE_API long b_datetime_year(const b_datetime *dt);
|
||||
BLUE_API long b_datetime_month(const b_datetime *dt);
|
||||
BLUE_API long b_datetime_day(const b_datetime *dt);
|
||||
BLUE_API long b_datetime_hour(const b_datetime *dt);
|
||||
BLUE_API long b_datetime_minute(const b_datetime *dt);
|
||||
BLUE_API long b_datetime_second(const b_datetime *dt);
|
||||
BLUE_API long b_datetime_subsecond(const b_datetime *dt);
|
||||
|
||||
BLUE_API bool b_datetime_zone_offset_is_negative(const b_datetime *dt);
|
||||
BLUE_API long b_datetime_zone_offset_hour(const b_datetime *dt);
|
||||
BLUE_API long b_datetime_zone_offset_minute(const b_datetime *dt);
|
||||
|
||||
#endif
|
||||
@@ -8,16 +8,12 @@
|
||||
#include <blue/ds/object.h>
|
||||
#include <blue/ds/type.h>
|
||||
|
||||
#define B_DICT(p) ((b_dict *)(p))
|
||||
struct b_string;
|
||||
|
||||
#define B_DICT_ITEM(k, v) \
|
||||
{ \
|
||||
.key = (k), .value = (v) \
|
||||
}
|
||||
#define B_DICT_ITEM_END \
|
||||
{ \
|
||||
.key = NULL, .value = NULL \
|
||||
}
|
||||
#define B_DICT(p) ((b_dict *)(p))
|
||||
|
||||
#define B_DICT_ITEM(k, v) {.key = (k), .value = (v)}
|
||||
#define B_DICT_ITEM_END {.key = NULL, .value = NULL}
|
||||
|
||||
#define b_dict_foreach(it, dict) \
|
||||
for (int z__b_unique_name() = b_dict_iterator_begin(dict, it); \
|
||||
@@ -28,7 +24,7 @@ typedef struct b_dict b_dict;
|
||||
typedef struct b_dict_iterator {
|
||||
b_iterator _base;
|
||||
size_t i;
|
||||
const char *key;
|
||||
const struct b_string *key;
|
||||
b_dsref *value;
|
||||
|
||||
b_dict *_d;
|
||||
@@ -54,10 +50,15 @@ static inline void b_dict_release(b_dict *dict)
|
||||
}
|
||||
|
||||
BLUE_API b_status b_dict_put(b_dict *dict, const char *key, b_dsref *value);
|
||||
BLUE_API b_status b_dict_put_sk(
|
||||
b_dict *dict, const struct b_string *key, b_dsref *value);
|
||||
BLUE_API b_dsref *b_dict_at(const b_dict *dict, const char *key);
|
||||
BLUE_API b_dsref *b_dict_at_sk(const b_dict *dict, const struct b_string *key);
|
||||
BLUE_API b_dsref *b_dict_get(b_dict *dict, const char *key);
|
||||
BLUE_API b_dsref *b_dict_get_sk(b_dict *dict, const struct b_string *key);
|
||||
|
||||
BLUE_API bool b_dict_has_key(const b_dict *dict, const char *key);
|
||||
BLUE_API bool b_dict_has_skey(const b_dict *dict, const struct b_string *key);
|
||||
BLUE_API size_t b_dict_get_size(const b_dict *dict);
|
||||
BLUE_API bool b_dict_is_empty(const b_dict *dict);
|
||||
|
||||
|
||||
@@ -7,27 +7,17 @@
|
||||
#include <blue/core/status.h>
|
||||
#include <blue/ds/object.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define B_HASHMAP(p) ((b_hashmap *)(p))
|
||||
#define B_HASHMAP(p) ((b_hashmap *)(p))
|
||||
|
||||
#define B_HASHMAP_KEY(k, ks) \
|
||||
{ \
|
||||
.key_data = (k), .key_size = (ks) \
|
||||
}
|
||||
#define B_HASHMAP_VALUE(v, vs) \
|
||||
{ \
|
||||
.value_data = (v), .value_size = (vs) \
|
||||
}
|
||||
#define B_HASHMAP_KEY(k, ks) {.key_data = (k), .key_size = (ks)}
|
||||
#define B_HASHMAP_VALUE(v, vs) {.value_data = (v), .value_size = (vs)}
|
||||
|
||||
#define B_HASHMAP_ITEM(k, ks, v, vs) \
|
||||
{ \
|
||||
.key = B_HASHMAP_KEY(k, ks), .value = B_HASHMAP_VALUE(v, vs) \
|
||||
}
|
||||
{.key = B_HASHMAP_KEY(k, ks), .value = B_HASHMAP_VALUE(v, vs)}
|
||||
|
||||
#define B_HASHMAP_ITEM_END \
|
||||
{ \
|
||||
.key = {0}, .value = { 0 } \
|
||||
}
|
||||
#define B_HASHMAP_ITEM_END {.key = {0}, .value = {0}}
|
||||
|
||||
#define b_hashmap_foreach(it, hashmap) \
|
||||
for (int z__b_unique_name() = b_hashmap_iterator_begin(hashmap, it); \
|
||||
@@ -38,7 +28,12 @@ typedef struct b_hashmap b_hashmap;
|
||||
typedef void (*b_hashmap_key_destructor)(void *);
|
||||
typedef void (*b_hashmap_value_destructor)(void *);
|
||||
|
||||
typedef enum b_hashmap_key_flags {
|
||||
B_HASHMAP_KEY_F_INTVALUE = 0x01u,
|
||||
} b_hashmap_key_flags;
|
||||
|
||||
typedef struct b_hashmap_key {
|
||||
b_hashmap_key_flags key_flags;
|
||||
const void *key_data;
|
||||
size_t key_size;
|
||||
} b_hashmap_key;
|
||||
|
||||
@@ -237,6 +237,17 @@ static inline size_t b_number_get_size_t(const b_number *number)
|
||||
|
||||
BLUE_API bool b_number_is_integer(const b_number *number);
|
||||
BLUE_API bool b_number_is_float(const b_number *number);
|
||||
BLUE_API bool b_number_is_inf(const b_number *number);
|
||||
BLUE_API bool b_number_is_inf_positive(const b_number *number);
|
||||
BLUE_API bool b_number_is_inf_negative(const b_number *number);
|
||||
BLUE_API bool b_number_is_nan(const b_number *number);
|
||||
BLUE_API bool b_number_is_nan_positive(const b_number *number);
|
||||
BLUE_API bool b_number_is_nan_negative(const b_number *number);
|
||||
|
||||
BLUE_API void b_number_set_inf_positive(b_number *number, bool v);
|
||||
BLUE_API void b_number_set_inf_negative(b_number *number, bool v);
|
||||
BLUE_API void b_number_set_nan_positive(b_number *number, bool v);
|
||||
BLUE_API void b_number_set_nan_negative(b_number *number, bool v);
|
||||
|
||||
BLUE_API size_t b_number_data_size(const b_number *number);
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
#define B_DSREF(p) ((b_dsref *)(p))
|
||||
|
||||
#define B_TYPEOF(object) ((struct b_dsref *)(object)->ob_type)
|
||||
#define B_TYPEID(object) (b_typeid(B_DSREF(object)))
|
||||
#define B_TYPEOF(object) ((struct b_dsref *)(object)->ob_type)
|
||||
#define B_TYPEID(object) (b_typeid(B_DSREF(object)))
|
||||
|
||||
#define B_RV(p) (b_make_rvalue(B_DSREF(p)))
|
||||
#define B_RVT(t, p) ((t *)(b_make_rvalue(B_DSREF(p))))
|
||||
#define B_RV(p) (b_make_rvalue(B_DSREF(p)))
|
||||
#define B_RVT(t, p) ((t *)(b_make_rvalue(B_DSREF(p))))
|
||||
|
||||
#define B_DSREF_IS(object, type) (B_TYPEID(object) == B_DSREF_TYPE_##type)
|
||||
|
||||
@@ -32,7 +32,7 @@ BLUE_API b_dsref *b_make_rvalue(b_dsref *obj);
|
||||
BLUE_API b_dsref *b_retain(b_dsref *obj);
|
||||
BLUE_API void b_release(b_dsref *obj);
|
||||
|
||||
BLUE_API void b_to_string(b_dsref *obj, struct b_stream *out);
|
||||
BLUE_API void b_to_string(const b_dsref *obj, struct b_stream *out);
|
||||
BLUE_API b_dsref_type_id b_typeid(const b_dsref *obj);
|
||||
|
||||
BLUE_API b_comparison_result_t b_compare(const b_dsref *a, const b_dsref *b);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef BLUELIB_STRING_H_
|
||||
#define BLUELIB_STRING_H_
|
||||
|
||||
#include <blue/core/encoding.h>
|
||||
#include <blue/core/iterator.h>
|
||||
#include <blue/core/status.h>
|
||||
#include <blue/ds/object.h>
|
||||
#include <blue/ds/type.h>
|
||||
@@ -13,16 +15,44 @@ struct b_stream;
|
||||
#define B_CSTR(s) (b_string_create_from_cstr(s))
|
||||
#define B_RV_CSTR(s) (B_RV(b_string_create_from_cstr(s)))
|
||||
|
||||
#define b_string_foreach(it, str) \
|
||||
for (int z__b_unique_name() = b_string_iterator_begin(str, it); \
|
||||
b_string_iterator_is_valid(it); b_string_iterator_next(it))
|
||||
|
||||
typedef struct b_string b_string;
|
||||
|
||||
typedef struct b_string_iterator {
|
||||
b_iterator _base;
|
||||
int _m, _f;
|
||||
b_string *_s, *_tmp;
|
||||
const char **_d;
|
||||
size_t _nd, _ds;
|
||||
|
||||
b_status status;
|
||||
size_t iteration_index;
|
||||
size_t byte_index;
|
||||
size_t codepoint_index;
|
||||
b_wchar char_value;
|
||||
const char *string_value;
|
||||
size_t string_length;
|
||||
size_t string_codepoints;
|
||||
} b_string_iterator;
|
||||
|
||||
typedef enum b_strlen_flags {
|
||||
B_STRLEN_NORMAL = 0,
|
||||
B_STRLEN_IGNORE_ESC = 0x01u,
|
||||
B_STRLEN_IGNORE_MOD = 0x02u,
|
||||
B_STRLEN_CODEPOINTS = 0x04u,
|
||||
} b_strlen_flags;
|
||||
|
||||
typedef enum b_string_tokenise_flags {
|
||||
B_STRING_TOK_F_NORMAL = 0x00u,
|
||||
B_STRING_TOK_F_INCLUDE_EMPTY_TOKENS = 0x01u,
|
||||
} b_string_tokenise_flags;
|
||||
|
||||
BLUE_API b_string *b_string_create(void);
|
||||
BLUE_API b_string *b_string_create_from_cstr(const char *s);
|
||||
BLUE_API b_string *b_string_create_from_wstr(const b_wchar *s);
|
||||
BLUE_API b_string *b_string_create_from_c(char c, size_t count);
|
||||
BLUE_API b_string *b_string_duplicate(const b_string *str);
|
||||
|
||||
@@ -41,6 +71,7 @@ BLUE_API b_status b_string_replace(
|
||||
BLUE_API b_status b_string_replace_all(b_string *str, const char *new_data);
|
||||
BLUE_API b_status b_string_remove(b_string *str, size_t start, size_t length);
|
||||
BLUE_API b_status b_string_transform(b_string *str, int (*transformer)(int));
|
||||
BLUE_API b_status b_string_trim(b_string *str);
|
||||
static inline b_status b_string_toupper(b_string *str)
|
||||
{
|
||||
return b_string_transform(str, toupper);
|
||||
@@ -51,22 +82,42 @@ static inline b_status b_string_tolower(b_string *str)
|
||||
}
|
||||
BLUE_API b_status b_string_open_stream(b_string *str, struct b_stream **out);
|
||||
|
||||
BLUE_API void b_string_append_s(b_string *dest, const b_string *src);
|
||||
BLUE_API void b_string_append_cstr(b_string *dest, const char *src);
|
||||
BLUE_API void b_string_append_cstrf(b_string *dest, const char *format, ...);
|
||||
BLUE_API void b_string_prepend_cstr(b_string *dest, const char *src);
|
||||
BLUE_API void b_string_prepend_cstrf(b_string *dest, const char *format, ...);
|
||||
BLUE_API void b_string_insert_s(b_string *dest, const b_string *src, size_t at);
|
||||
BLUE_API void b_string_insert_cstr(b_string *dest, const char *src, size_t at);
|
||||
BLUE_API void b_string_insert_cstrn(
|
||||
BLUE_API b_status b_string_append_c(b_string *dest, char c);
|
||||
BLUE_API b_status b_string_append_wc(b_string *dest, b_wchar c);
|
||||
BLUE_API b_status b_string_append_s(b_string *dest, const b_string *src);
|
||||
BLUE_API b_status b_string_append_cstr(b_string *dest, const char *src);
|
||||
BLUE_API b_status b_string_append_wstr(b_string *dest, const b_wchar *src);
|
||||
BLUE_API b_status b_string_append_cstrf(b_string *dest, const char *format, ...);
|
||||
|
||||
BLUE_API b_status b_string_prepend_c(b_string *dest, char c);
|
||||
BLUE_API b_status b_string_prepend_wc(b_string *dest, b_wchar c);
|
||||
BLUE_API b_status b_string_prepend_cstr(b_string *dest, const char *src);
|
||||
BLUE_API b_status b_string_prepend_wstr(b_string *dest, const b_wchar *src);
|
||||
BLUE_API b_status b_string_prepend_cstrf(b_string *dest, const char *format, ...);
|
||||
|
||||
BLUE_API b_status b_string_insert_c(b_string *dest, char c, size_t at);
|
||||
BLUE_API b_status b_string_insert_wc(b_string *dest, b_wchar c, size_t at);
|
||||
BLUE_API b_status b_string_insert_s(b_string *dest, const b_string *src, size_t at);
|
||||
BLUE_API b_status b_string_insert_cstr(b_string *dest, const char *src, size_t at);
|
||||
BLUE_API b_status b_string_insert_wstr(
|
||||
b_string *dest, const b_wchar *src, size_t at);
|
||||
BLUE_API b_status b_string_insert_cstrn(
|
||||
b_string *dest, const char *src, size_t len, size_t at);
|
||||
BLUE_API void b_string_insert_cstrf(
|
||||
BLUE_API b_status b_string_insert_wstrn(
|
||||
b_string *dest, const char *src, size_t len, size_t at);
|
||||
BLUE_API b_status b_string_insert_cstrf(
|
||||
b_string *dest, size_t at, const char *format, ...);
|
||||
BLUE_API void b_string_clear(b_string *str);
|
||||
|
||||
BLUE_API b_status b_string_tokenise(
|
||||
b_string *str, const char *delims[], size_t nr_delims,
|
||||
b_string_tokenise_flags flags, b_string_iterator *it);
|
||||
|
||||
BLUE_API size_t b_string_get_size(const b_string *str, b_strlen_flags flags);
|
||||
BLUE_API size_t b_string_get_capacity(const b_string *str);
|
||||
|
||||
BLUE_API bool b_string_compare(const b_string *a, const b_string *b);
|
||||
|
||||
BLUE_API char b_string_front(const b_string *str);
|
||||
BLUE_API char b_string_back(const b_string *str);
|
||||
|
||||
@@ -75,9 +126,16 @@ BLUE_API void b_string_pop_back(b_string *str);
|
||||
BLUE_API const char *b_string_ptr(const b_string *str);
|
||||
BLUE_API b_string *b_string_substr(const b_string *str, size_t start, size_t len);
|
||||
|
||||
BLUE_API int b_string_iterator_begin(const b_string *string, b_string_iterator *it);
|
||||
BLUE_API bool b_string_iterator_next(b_string_iterator *it);
|
||||
// BLUE_API b_status b_string_iterator_erase(b_string_iterator *it);
|
||||
BLUE_API bool b_string_iterator_is_valid(const b_string_iterator *it);
|
||||
|
||||
BLUE_API char *b_strdup(const char *s);
|
||||
BLUE_API size_t b_strlen(const char *s, b_strlen_flags flags);
|
||||
BLUE_API b_wchar *b_wstrdup(const b_wchar *s);
|
||||
BLUE_API size_t b_wstrlen(const b_wchar *s);
|
||||
|
||||
BLUE_API uint64_t b_cstr_hash(const char *s);
|
||||
BLUE_API uint64_t b_string_hash(const b_string *s);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,6 +30,7 @@ typedef enum b_fundamental_type_id {
|
||||
B_DSREF_TYPE_PATH,
|
||||
B_DSREF_TYPE_FILE,
|
||||
B_DSREF_TYPE_DIRECTORY,
|
||||
B_DSREF_TYPE_DATETIME,
|
||||
} b_fundamental_type_id;
|
||||
|
||||
typedef enum b_dsref_type_flags {
|
||||
@@ -44,7 +45,7 @@ typedef struct b_dsref_type {
|
||||
b_queue_entry t_entry;
|
||||
void (*t_init)(struct b_dsref *);
|
||||
void (*t_release)(struct b_dsref *);
|
||||
void (*t_to_string)(struct b_dsref *, struct b_stream *);
|
||||
void (*t_to_string)(const struct b_dsref *, struct b_stream *);
|
||||
} b_dsref_type;
|
||||
|
||||
BLUE_API b_status b_dsref_type_register(b_dsref_type *type);
|
||||
|
||||
527
ds/number.c
527
ds/number.c
@@ -10,7 +10,7 @@ typedef int (*number_converter_t)(const struct b_number *, void *);
|
||||
|
||||
static number_converter_t converters[B_NUMBER_TYPE_COUNT][B_NUMBER_TYPE_COUNT];
|
||||
|
||||
static void number_to_string(struct b_dsref *obj, struct b_stream *out);
|
||||
static void number_to_string(const struct b_dsref *obj, struct b_stream *out);
|
||||
|
||||
static struct b_dsref_type number_type = {
|
||||
.t_name = "corelib::number",
|
||||
@@ -139,6 +139,408 @@ bool b_number_is_float(const struct b_number *number)
|
||||
}
|
||||
}
|
||||
|
||||
bool b_number_is_inf(const b_number *number)
|
||||
{
|
||||
return (number->n_flags & NUMBER_F_INF) != 0;
|
||||
}
|
||||
|
||||
bool b_number_is_inf_positive(const b_number *number)
|
||||
{
|
||||
if (!(number->n_flags & NUMBER_F_INF)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (number->n_type) {
|
||||
case B_NUMBER_INT8:
|
||||
return number->n_value.v_int8 >= 0;
|
||||
case B_NUMBER_INT16:
|
||||
return number->n_value.v_int16 >= 0;
|
||||
case B_NUMBER_INT32:
|
||||
return number->n_value.v_int32 >= 0;
|
||||
case B_NUMBER_INT64:
|
||||
return number->n_value.v_int64 >= 0;
|
||||
case B_NUMBER_FLOAT32:
|
||||
return number->n_value.v_float32 >= 0;
|
||||
case B_NUMBER_FLOAT64:
|
||||
return number->n_value.v_float64 >= 0;
|
||||
case B_NUMBER_CHAR:
|
||||
return number->n_value.v_char >= 0;
|
||||
case B_NUMBER_SHORT:
|
||||
return number->n_value.v_short >= 0;
|
||||
case B_NUMBER_INT:
|
||||
return number->n_value.v_int >= 0;
|
||||
case B_NUMBER_LONG:
|
||||
return number->n_value.v_long >= 0;
|
||||
case B_NUMBER_LONGLONG:
|
||||
return number->n_value.v_longlong >= 0;
|
||||
case B_NUMBER_FLOAT:
|
||||
return number->n_value.v_float >= 0;
|
||||
case B_NUMBER_DOUBLE:
|
||||
return number->n_value.v_double >= 0;
|
||||
case B_NUMBER_SIZE_T:
|
||||
return number->n_value.v_size_t >= 0;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool b_number_is_inf_negative(const b_number *number)
|
||||
{
|
||||
if (!(number->n_flags & NUMBER_F_INF)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (number->n_type) {
|
||||
case B_NUMBER_INT8:
|
||||
return number->n_value.v_int8 < 0;
|
||||
case B_NUMBER_INT16:
|
||||
return number->n_value.v_int16 < 0;
|
||||
case B_NUMBER_INT32:
|
||||
return number->n_value.v_int32 < 0;
|
||||
case B_NUMBER_INT64:
|
||||
return number->n_value.v_int64 < 0;
|
||||
case B_NUMBER_FLOAT32:
|
||||
return number->n_value.v_float32 < 0;
|
||||
case B_NUMBER_FLOAT64:
|
||||
return number->n_value.v_float64 < 0;
|
||||
case B_NUMBER_CHAR:
|
||||
return number->n_value.v_char < 0;
|
||||
case B_NUMBER_SHORT:
|
||||
return number->n_value.v_short < 0;
|
||||
case B_NUMBER_INT:
|
||||
return number->n_value.v_int < 0;
|
||||
case B_NUMBER_LONG:
|
||||
return number->n_value.v_long < 0;
|
||||
case B_NUMBER_LONGLONG:
|
||||
return number->n_value.v_longlong < 0;
|
||||
case B_NUMBER_FLOAT:
|
||||
return number->n_value.v_float < 0;
|
||||
case B_NUMBER_DOUBLE:
|
||||
return number->n_value.v_double < 0;
|
||||
case B_NUMBER_SIZE_T:
|
||||
return number->n_value.v_size_t < 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool b_number_is_nan(const b_number *number)
|
||||
{
|
||||
return (number->n_flags & NUMBER_F_NAN) != 0;
|
||||
}
|
||||
|
||||
bool b_number_is_nan_positive(const b_number *number)
|
||||
{
|
||||
if (!(number->n_flags & NUMBER_F_NAN)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (number->n_type) {
|
||||
case B_NUMBER_INT8:
|
||||
return number->n_value.v_int8 >= 0;
|
||||
case B_NUMBER_INT16:
|
||||
return number->n_value.v_int16 >= 0;
|
||||
case B_NUMBER_INT32:
|
||||
return number->n_value.v_int32 >= 0;
|
||||
case B_NUMBER_INT64:
|
||||
return number->n_value.v_int64 >= 0;
|
||||
case B_NUMBER_FLOAT32:
|
||||
return number->n_value.v_float32 >= 0;
|
||||
case B_NUMBER_FLOAT64:
|
||||
return number->n_value.v_float64 >= 0;
|
||||
case B_NUMBER_CHAR:
|
||||
return number->n_value.v_char >= 0;
|
||||
case B_NUMBER_SHORT:
|
||||
return number->n_value.v_short >= 0;
|
||||
case B_NUMBER_INT:
|
||||
return number->n_value.v_int >= 0;
|
||||
case B_NUMBER_LONG:
|
||||
return number->n_value.v_long >= 0;
|
||||
case B_NUMBER_LONGLONG:
|
||||
return number->n_value.v_longlong >= 0;
|
||||
case B_NUMBER_FLOAT:
|
||||
return number->n_value.v_float >= 0;
|
||||
case B_NUMBER_DOUBLE:
|
||||
return number->n_value.v_double >= 0;
|
||||
case B_NUMBER_SIZE_T:
|
||||
return number->n_value.v_size_t >= 0;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool b_number_is_nan_negative(const b_number *number)
|
||||
{
|
||||
if (!(number->n_flags & NUMBER_F_NAN)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (number->n_type) {
|
||||
case B_NUMBER_INT8:
|
||||
return number->n_value.v_int8 < 0;
|
||||
case B_NUMBER_INT16:
|
||||
return number->n_value.v_int16 < 0;
|
||||
case B_NUMBER_INT32:
|
||||
return number->n_value.v_int32 < 0;
|
||||
case B_NUMBER_INT64:
|
||||
return number->n_value.v_int64 < 0;
|
||||
case B_NUMBER_FLOAT32:
|
||||
return number->n_value.v_float32 < 0;
|
||||
case B_NUMBER_FLOAT64:
|
||||
return number->n_value.v_float64 < 0;
|
||||
case B_NUMBER_CHAR:
|
||||
return number->n_value.v_char < 0;
|
||||
case B_NUMBER_SHORT:
|
||||
return number->n_value.v_short < 0;
|
||||
case B_NUMBER_INT:
|
||||
return number->n_value.v_int < 0;
|
||||
case B_NUMBER_LONG:
|
||||
return number->n_value.v_long < 0;
|
||||
case B_NUMBER_LONGLONG:
|
||||
return number->n_value.v_longlong < 0;
|
||||
case B_NUMBER_FLOAT:
|
||||
return number->n_value.v_float < 0;
|
||||
case B_NUMBER_DOUBLE:
|
||||
return number->n_value.v_double < 0;
|
||||
case B_NUMBER_SIZE_T:
|
||||
return number->n_value.v_size_t < 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void b_number_set_inf_positive(b_number *number, bool v)
|
||||
{
|
||||
if (!v) {
|
||||
number->n_flags &= ~NUMBER_F_INF;
|
||||
return;
|
||||
}
|
||||
|
||||
number->n_flags &= ~NUMBER_F_NAN;
|
||||
number->n_flags |= NUMBER_F_INF;
|
||||
|
||||
switch (number->n_type) {
|
||||
case B_NUMBER_INT8:
|
||||
number->n_value.v_int8 = 0;
|
||||
break;
|
||||
case B_NUMBER_INT16:
|
||||
number->n_value.v_int16 = 0;
|
||||
break;
|
||||
case B_NUMBER_INT32:
|
||||
number->n_value.v_int32 = 0;
|
||||
break;
|
||||
case B_NUMBER_INT64:
|
||||
number->n_value.v_int64 = 0;
|
||||
break;
|
||||
case B_NUMBER_FLOAT32:
|
||||
number->n_value.v_float32 = 0;
|
||||
break;
|
||||
case B_NUMBER_FLOAT64:
|
||||
number->n_value.v_float64 = 0;
|
||||
break;
|
||||
case B_NUMBER_CHAR:
|
||||
number->n_value.v_char = 0;
|
||||
break;
|
||||
case B_NUMBER_SHORT:
|
||||
number->n_value.v_short = 0;
|
||||
break;
|
||||
case B_NUMBER_INT:
|
||||
number->n_value.v_int = 0;
|
||||
break;
|
||||
case B_NUMBER_LONG:
|
||||
number->n_value.v_long = 0;
|
||||
break;
|
||||
case B_NUMBER_LONGLONG:
|
||||
number->n_value.v_longlong = 0;
|
||||
break;
|
||||
case B_NUMBER_FLOAT:
|
||||
number->n_value.v_float = 0;
|
||||
break;
|
||||
case B_NUMBER_DOUBLE:
|
||||
number->n_value.v_double = 0;
|
||||
break;
|
||||
case B_NUMBER_SIZE_T:
|
||||
number->n_value.v_size_t = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void b_number_set_inf_negative(b_number *number, bool v)
|
||||
{
|
||||
if (!v) {
|
||||
number->n_flags &= ~NUMBER_F_INF;
|
||||
return;
|
||||
}
|
||||
|
||||
number->n_flags &= ~NUMBER_F_NAN;
|
||||
number->n_flags |= NUMBER_F_INF;
|
||||
|
||||
switch (number->n_type) {
|
||||
case B_NUMBER_INT8:
|
||||
number->n_value.v_int8 = -1;
|
||||
break;
|
||||
case B_NUMBER_INT16:
|
||||
number->n_value.v_int16 = -1;
|
||||
break;
|
||||
case B_NUMBER_INT32:
|
||||
number->n_value.v_int32 = -1;
|
||||
break;
|
||||
case B_NUMBER_INT64:
|
||||
number->n_value.v_int64 = -1;
|
||||
break;
|
||||
case B_NUMBER_FLOAT32:
|
||||
number->n_value.v_float32 = -1;
|
||||
break;
|
||||
case B_NUMBER_FLOAT64:
|
||||
number->n_value.v_float64 = -1;
|
||||
break;
|
||||
case B_NUMBER_CHAR:
|
||||
number->n_value.v_char = -1;
|
||||
break;
|
||||
case B_NUMBER_SHORT:
|
||||
number->n_value.v_short = -1;
|
||||
break;
|
||||
case B_NUMBER_INT:
|
||||
number->n_value.v_int = -1;
|
||||
break;
|
||||
case B_NUMBER_LONG:
|
||||
number->n_value.v_long = -1;
|
||||
break;
|
||||
case B_NUMBER_LONGLONG:
|
||||
number->n_value.v_longlong = -1;
|
||||
break;
|
||||
case B_NUMBER_FLOAT:
|
||||
number->n_value.v_float = -1;
|
||||
break;
|
||||
case B_NUMBER_DOUBLE:
|
||||
number->n_value.v_double = -1;
|
||||
break;
|
||||
case B_NUMBER_SIZE_T:
|
||||
number->n_value.v_size_t = -1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void b_number_set_nan_positive(b_number *number, bool v)
|
||||
{
|
||||
if (!v) {
|
||||
number->n_flags &= ~NUMBER_F_NAN;
|
||||
return;
|
||||
}
|
||||
|
||||
number->n_flags &= ~NUMBER_F_INF;
|
||||
number->n_flags |= NUMBER_F_NAN;
|
||||
|
||||
switch (number->n_type) {
|
||||
case B_NUMBER_INT8:
|
||||
number->n_value.v_int8 = 0;
|
||||
break;
|
||||
case B_NUMBER_INT16:
|
||||
number->n_value.v_int16 = 0;
|
||||
break;
|
||||
case B_NUMBER_INT32:
|
||||
number->n_value.v_int32 = 0;
|
||||
break;
|
||||
case B_NUMBER_INT64:
|
||||
number->n_value.v_int64 = 0;
|
||||
break;
|
||||
case B_NUMBER_FLOAT32:
|
||||
number->n_value.v_float32 = 0;
|
||||
break;
|
||||
case B_NUMBER_FLOAT64:
|
||||
number->n_value.v_float64 = 0;
|
||||
break;
|
||||
case B_NUMBER_CHAR:
|
||||
number->n_value.v_char = 0;
|
||||
break;
|
||||
case B_NUMBER_SHORT:
|
||||
number->n_value.v_short = 0;
|
||||
break;
|
||||
case B_NUMBER_INT:
|
||||
number->n_value.v_int = 0;
|
||||
break;
|
||||
case B_NUMBER_LONG:
|
||||
number->n_value.v_long = 0;
|
||||
break;
|
||||
case B_NUMBER_LONGLONG:
|
||||
number->n_value.v_longlong = 0;
|
||||
break;
|
||||
case B_NUMBER_FLOAT:
|
||||
number->n_value.v_float = 0;
|
||||
break;
|
||||
case B_NUMBER_DOUBLE:
|
||||
number->n_value.v_double = 0;
|
||||
break;
|
||||
case B_NUMBER_SIZE_T:
|
||||
number->n_value.v_size_t = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void b_number_set_nan_negative(b_number *number, bool v)
|
||||
{
|
||||
if (!v) {
|
||||
number->n_flags &= ~NUMBER_F_NAN;
|
||||
return;
|
||||
}
|
||||
|
||||
number->n_flags &= ~NUMBER_F_INF;
|
||||
number->n_flags |= NUMBER_F_NAN;
|
||||
|
||||
switch (number->n_type) {
|
||||
case B_NUMBER_INT8:
|
||||
number->n_value.v_int8 = -1;
|
||||
break;
|
||||
case B_NUMBER_INT16:
|
||||
number->n_value.v_int16 = -1;
|
||||
break;
|
||||
case B_NUMBER_INT32:
|
||||
number->n_value.v_int32 = -1;
|
||||
break;
|
||||
case B_NUMBER_INT64:
|
||||
number->n_value.v_int64 = -1;
|
||||
break;
|
||||
case B_NUMBER_FLOAT32:
|
||||
number->n_value.v_float32 = -1;
|
||||
break;
|
||||
case B_NUMBER_FLOAT64:
|
||||
number->n_value.v_float64 = -1;
|
||||
break;
|
||||
case B_NUMBER_CHAR:
|
||||
number->n_value.v_char = -1;
|
||||
break;
|
||||
case B_NUMBER_SHORT:
|
||||
number->n_value.v_short = -1;
|
||||
break;
|
||||
case B_NUMBER_INT:
|
||||
number->n_value.v_int = -1;
|
||||
break;
|
||||
case B_NUMBER_LONG:
|
||||
number->n_value.v_long = -1;
|
||||
break;
|
||||
case B_NUMBER_LONGLONG:
|
||||
number->n_value.v_longlong = -1;
|
||||
break;
|
||||
case B_NUMBER_FLOAT:
|
||||
number->n_value.v_float = -1;
|
||||
break;
|
||||
case B_NUMBER_DOUBLE:
|
||||
number->n_value.v_double = -1;
|
||||
break;
|
||||
case B_NUMBER_SIZE_T:
|
||||
number->n_value.v_size_t = -1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t b_number_data_size(const struct b_number *number)
|
||||
{
|
||||
switch (number->n_type) {
|
||||
@@ -175,9 +577,130 @@ size_t b_number_data_size(const struct b_number *number)
|
||||
}
|
||||
}
|
||||
|
||||
static void number_to_string(struct b_dsref *obj, struct b_stream *out)
|
||||
static void print_inf(const struct b_number *n, struct b_stream *out)
|
||||
{
|
||||
switch (n->n_type) {
|
||||
case B_NUMBER_INT8:
|
||||
b_stream_write_string(out, n->n_value.v_int8 < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_INT16:
|
||||
b_stream_write_string(out, n->n_value.v_int16 < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_INT32:
|
||||
b_stream_write_string(out, n->n_value.v_int32 < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_INT64:
|
||||
b_stream_write_string(out, n->n_value.v_int64 < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_FLOAT32:
|
||||
b_stream_write_string(
|
||||
out, n->n_value.v_float32 < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_FLOAT64:
|
||||
b_stream_write_string(
|
||||
out, n->n_value.v_float64 < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_CHAR:
|
||||
b_stream_write_string(out, n->n_value.v_char < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_SHORT:
|
||||
b_stream_write_string(out, n->n_value.v_short < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_INT:
|
||||
b_stream_write_string(out, n->n_value.v_int < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_LONG:
|
||||
b_stream_write_string(out, n->n_value.v_long < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_LONGLONG:
|
||||
b_stream_write_string(
|
||||
out, n->n_value.v_longlong < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_FLOAT:
|
||||
b_stream_write_string(out, n->n_value.v_float < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_DOUBLE:
|
||||
b_stream_write_string(out, n->n_value.v_double < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_SIZE_T:
|
||||
b_stream_write_string(out, n->n_value.v_size_t < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
b_stream_write_string(out, "INF", NULL);
|
||||
}
|
||||
|
||||
static void print_nan(const struct b_number *n, struct b_stream *out)
|
||||
{
|
||||
switch (n->n_type) {
|
||||
case B_NUMBER_INT8:
|
||||
b_stream_write_string(out, n->n_value.v_int8 < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_INT16:
|
||||
b_stream_write_string(out, n->n_value.v_int16 < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_INT32:
|
||||
b_stream_write_string(out, n->n_value.v_int32 < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_INT64:
|
||||
b_stream_write_string(out, n->n_value.v_int64 < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_FLOAT32:
|
||||
b_stream_write_string(
|
||||
out, n->n_value.v_float32 < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_FLOAT64:
|
||||
b_stream_write_string(
|
||||
out, n->n_value.v_float64 < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_CHAR:
|
||||
b_stream_write_string(out, n->n_value.v_char < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_SHORT:
|
||||
b_stream_write_string(out, n->n_value.v_short < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_INT:
|
||||
b_stream_write_string(out, n->n_value.v_int < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_LONG:
|
||||
b_stream_write_string(out, n->n_value.v_long < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_LONGLONG:
|
||||
b_stream_write_string(
|
||||
out, n->n_value.v_longlong < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_FLOAT:
|
||||
b_stream_write_string(out, n->n_value.v_float < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_DOUBLE:
|
||||
b_stream_write_string(out, n->n_value.v_double < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
case B_NUMBER_SIZE_T:
|
||||
b_stream_write_string(out, n->n_value.v_size_t < 0 ? "-" : "", NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
b_stream_write_string(out, "NaN", NULL);
|
||||
}
|
||||
|
||||
static void number_to_string(const struct b_dsref *obj, struct b_stream *out)
|
||||
{
|
||||
struct b_number *number = B_NUMBER(obj);
|
||||
|
||||
if (number->n_flags & NUMBER_F_INF) {
|
||||
print_inf(number, out);
|
||||
return;
|
||||
}
|
||||
|
||||
if (number->n_flags & NUMBER_F_NAN) {
|
||||
print_nan(number, out);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (number->n_type) {
|
||||
case B_NUMBER_INT8:
|
||||
b_stream_write_fmt(out, NULL, "%" PRIu8, number->n_value.v_int8);
|
||||
|
||||
@@ -5,9 +5,15 @@
|
||||
|
||||
#include <blue/ds/number.h>
|
||||
|
||||
enum b_number_flags {
|
||||
NUMBER_F_INF = 0x01u,
|
||||
NUMBER_F_NAN = 0x02u,
|
||||
};
|
||||
|
||||
struct b_number {
|
||||
struct b_dsref n_base;
|
||||
b_number_type n_type;
|
||||
enum b_number_flags n_flags;
|
||||
union {
|
||||
int8_t v_int8;
|
||||
int16_t v_int16;
|
||||
|
||||
@@ -41,7 +41,7 @@ void b_release(struct b_dsref *obj)
|
||||
free(obj);
|
||||
}
|
||||
|
||||
void b_to_string(struct b_dsref *obj, struct b_stream *out)
|
||||
void b_to_string(const struct b_dsref *obj, struct b_stream *out)
|
||||
{
|
||||
if (obj->ob_type->t_to_string) {
|
||||
obj->ob_type->t_to_string(obj, out);
|
||||
|
||||
1368
ds/string.c
1368
ds/string.c
File diff suppressed because it is too large
Load Diff
@@ -8,9 +8,14 @@
|
||||
|
||||
struct b_string {
|
||||
struct b_dsref s_base;
|
||||
/* length of string, not including null-terminator */
|
||||
/* length of string in bytes, not including null-terminator.
|
||||
* a multi-byte utf-8 codepoint will be counted as multiple bytes here */
|
||||
unsigned int s_len;
|
||||
/* maximum length of string storable in the currently-allocated buffer, not including null terminator */
|
||||
/* length of string in codepoints, not including null-terminator.
|
||||
* a multi-byte utf-8 codepoint will be counted as one codepoint here */
|
||||
unsigned int s_codepoints;
|
||||
/* maximum length of string storable in the currently-allocated buffer
|
||||
* in bytes, not including null terminator */
|
||||
unsigned int s_max;
|
||||
union {
|
||||
char d_inline[STRING_INLINE_CAPACITY + 1];
|
||||
|
||||
Reference in New Issue
Block a user